diff --git a/bin/test_const.py b/bin/test_const.py new file mode 100755 index 0000000000000000000000000000000000000000..9722cb38e6aafcf860023bd6068a75a38534df1d --- /dev/null +++ b/bin/test_const.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from pykat import finesse +from pykat.detectors import * +from pykat.components import * +from pykat.commands import * +from pykat.structs import * + +import numpy as np +import pylab as pl + +code = """ +l l1 2 0 n1 +m m1 0.99 0.01 0 n1 n2 +s cav1 $test n2 n3 +m m2 0.99 0.01 -0.1 n3 n4 + +attr m2 m 1 # mech sus1 + +const test 1200 +ad up_refl 0 n1 +ad low_refl 0 n1 + +qd refl_A 0 0 n1 +qd refl_Q 0 90 n1 +qd tran_A 0 0 n4 +qd tran_Q 0 90 n4 + +put up_refl f $x1 +put low_refl f $mx1 + +yaxis log re:im +""" + +kat = finesse.kat(kat_code=code) + +kat.signals.apply(kat.l1.power, 1, 0) +kat.signals.apply(kat.m1.phi, 1, 90) + +kat.add(xaxis('log', [1, 1000], kat.signals.f, 100)) + +out = kat.run(printout=0, printerr=0) diff --git a/bin/test_plot.py b/bin/test_plot.py index cc54c7cb781272bd45698c1f6f40eb41a4d07138..230d6eb3854b283baa413bd2ef04d3f5a7dac2b5 100644 --- a/bin/test_plot.py +++ b/bin/test_plot.py @@ -8,7 +8,7 @@ import numpy as np import pylab as pl code = """ -l l1 1 0 0 n1 +l l1 1 0 0 n1 ### test s s1 10 1 n1 n2 m m1 0.5 0.5 0 n2 n3 s s2 10 1 n3 n4 @@ -29,7 +29,7 @@ kat.add(photodiode('pd_trs','n5')) kat.add(photodiode('pd_cav','n4', num_demods=1, demods=[1])) -kat.add(xaxis("lin", [0, 360], kat.m2, kat.m2.phi, 100)) +kat.add(xaxis("lin", [0, 360], kat.m2.phi, 100)) kat.m1.Rcx = -1000.0 kat.m1.Rcy = -1000.0 diff --git a/examples/asc_test/master.py b/examples/asc_test/master.py index 1f61f146670d928c877c62b2b5d752d0b6bddab6..af85fcabb992bfd89788bb6e18f30b86b02faa16 100644 --- a/examples/asc_test/master.py +++ b/examples/asc_test/master.py @@ -60,8 +60,8 @@ def main(): scale 2 PDrefl_q """ kat.parseKatCode(code_det) - kat.PDrefl_p.phi[0]=result['p_phase'] - kat.PDrefl_q.phi[0]=result['q_phase'] + kat.PDrefl_p.phi1=result['p_phase'] + kat.PDrefl_q.phi1=result['q_phase'] print "--------------------------------------------------------" print " 4. adding a 0.1nm offset to ETM and compute PDH signal" @@ -117,7 +117,7 @@ def pd_phase(tmpkat): # function for root finding def PD_q_test(x): - kat.PDrefl_q.phi[0]=x + kat.PDrefl_q.phi1=x out = kat.run() print '\r root finding: function value %g ' % out.y, sys.stdout.flush() diff --git a/examples/asc_test/master2.py b/examples/asc_test/master2.py index 036a839ac230bb528c196b18df3f558fe3b2dae9..d2dfe433bb3b9baac9c6ea3488a04ca7a79e48c5 100644 --- a/examples/asc_test/master2.py +++ b/examples/asc_test/master2.py @@ -96,10 +96,10 @@ def main(): kat.parseKatCode(code_WFS2) (WFS1_phase, WFS2_phase) = asc_phases(kat) - kat.WFS1_I.phi[0]=WFS1_phase - kat.WFS1_Q.phi[0]=WFS1_phase+90.0 - kat.WFS2_I.phi[0]=WFS2_phase - kat.WFS2_Q.phi[0]=WFS2_phase+90.0 + kat.WFS1_I.phi1=WFS1_phase + kat.WFS1_Q.phi1=WFS1_phase+90.0 + kat.WFS2_I.phi1=WFS2_phase + kat.WFS2_Q.phi1=WFS2_phase+90.0 result['WFS1_phase']=WFS1_phase result['WFS2_phase']=WFS2_phase @@ -178,7 +178,7 @@ def asc_phases(tmpkat): kat.maxtem=1 def demod_phase1(x): - kat.WFS1_I.phi[0]=x[0] + kat.WFS1_I.phi1=x[0] out = kat.run() signal = out["WFS1_I"] print '\r minimising: function value %g ' % signal , @@ -186,7 +186,7 @@ def asc_phases(tmpkat): return -1*abs(signal) def demod_phase2(x): - kat.WFS2_I.phi[0]=x[0] + kat.WFS2_I.phi1=x[0] out = kat.run() signal = out["WFS2_I"] print '\r minimising: function value %g ' % signal , diff --git a/pykat/SIfloat.py b/pykat/SIfloat.py index 43fc3236adf10547965fb9639462f165b0e25b2b..4276cc42ad16bd5a33b5e4f607398d38485ac098 100644 --- a/pykat/SIfloat.py +++ b/pykat/SIfloat.py @@ -9,7 +9,7 @@ def SIfloat(value): return convertToFloat(value) def convertToFloat(value): - __prefix = {'y': 'e-24', # yocto + __suffix = {'y': 'e-24', # yocto 'z': 'e-21', # zepto 'a': 'e-18', # atto 'f': 'e-15', # femto @@ -23,12 +23,12 @@ def convertToFloat(value): 'M': 'e6', # mega 'G': 'e9', # giga 'T': 'e12', # tera - 'P': 'e15', # peta - 'E': 'e18', # exa - 'Z': 'e21', # zetta - 'Y': 'e24', # yotta + 'P': 'e15' # peta } + value = str(value) - for i, j in __prefix.iteritems(): + + for i, j in __suffix.iteritems(): value=value.replace(i, str(j)) + return float(value) diff --git a/pykat/commands.py b/pykat/commands.py index a47b0dd0a51e6bb3573a579a933af2bf0b39cca7..2b70e256ad4023f51ad1bb787f5d93567a43110b 100644 --- a/pykat/commands.py +++ b/pykat/commands.py @@ -44,11 +44,34 @@ class cavity(Command): class gauss(object): @staticmethod def parseFinesseText(text, kat): - values = text.split(" ") + values = text.split() - if not values[0].startswith("gauss"): + if not values[0].startswith("gauss") or (len(values) != 6 and len(values != 8)): raise exceptions.RuntimeError("'{0}' not a valid Finesse gauss command".format(text)) + name = values[1] + component = values[2] + node = values[3] + + + + if values[0].endswith("**"): + if len(values) == 6: + print "" + elif len(values) == 8: + print "" + elif values[0].endswith("*"): + if len(values) == 6: + print "" + elif len(values) == 8: + print "" + else: + if len(values) == 6: + print "" + elif len(values) == 8: + print "" + + class xaxis(Command): def __init__(self, scale, limits, param, steps, comp=None, axis_type="xaxis"): @@ -94,7 +117,7 @@ class xaxis(Command): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0] != "xaxis" and values[0] != "xaxis*": raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text)) @@ -125,7 +148,7 @@ class x2axis(xaxis): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0] != "x2axis" and values[0] != "x2axis*": raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text)) diff --git a/pykat/components.py b/pykat/components.py index 3affe6f8bbfb4b36a8cdebf5aeb815718fe21803..3bb5dd50d101d5448f21cfe9453f7361b3643fd9 100644 --- a/pykat/components.py +++ b/pykat/components.py @@ -17,6 +17,8 @@ from pykat.gui.graphics import * from pykat.SIfloat import * from pykat.param import Param, AttrParam +import pykat.exceptions as pkex + next_component_id = 1 class NodeGaussSetter(object): @@ -232,13 +234,13 @@ class mirror(AbstractMirrorComponent): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0] != "m" and values[0] != "m1" and values[0] != "m2": - raise exceptions.RuntimeError("'{0}' not a valid Finesse mirror command".format(text)) + raise pkex.BasePyKatException("'{0}' not a valid Finesse mirror command".format(text)) if len(values) != 7: - raise exceptions.RuntimeError("Mirror Finesse code format incorrect '{0}'".format(text)) + raise pkex.BasePyKatException("Mirror Finesse code format incorrect '{0}'".format(text)) if len(values[0])==1: values.pop(0) # remove initial value @@ -287,13 +289,13 @@ class beamSplitter(AbstractMirrorComponent): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0] != "bs" and values[0] != "bs1" and values[0] != "bs2": - raise exceptions.RuntimeError("'{0}' not a valid Finesse beam splitter command".format(text)) + raise pkex.BasePyKatException("'{0}' not a valid Finesse beam splitter command".format(text)) if len(values) != 10: - raise exceptions.RuntimeError("Beam splitter Finesse code format incorrect '{0}'".format(text)) + raise pkex.BasePyKatException("Beam splitter Finesse code format incorrect '{0}'".format(text)) if len(values[0])==2: values.pop(0) # remove initial value @@ -372,10 +374,10 @@ class space(Component): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0] != "s": - raise exceptions.RuntimeError("'{0}' not a valid Finesse space command".format(text)) + raise pkex.BasePyKatException("'{0}' not a valid Finesse space command".format(text)) values.pop(0) # remove initial value @@ -384,7 +386,7 @@ class space(Component): elif len(values) == 4: return space(values[0], values[2], values[3], values[1]) else: - raise exceptions.RuntimeError("Space Finesse code format incorrect '{0}'".format(text)) + raise pkex.BasePyKatException("Space Finesse code format incorrect '{0}'".format(text)) def getFinesseText(self): rtn = [] @@ -416,16 +418,16 @@ class grating(Component): if node3 != None: self._requested_node_names.append(node3) else: - raise exceptions.RuntimeError("Grating node 3 not specified") + raise pkex.BasePyKatException("Grating node 3 not specified") if n > 3: if node4 != None: self._requested_node_names.append(node4) else: - raise exceptions.RuntimeError("Grating node 4 not specified") + raise pkex.BasePyKatException("Grating node 4 not specified") if n > 4 or n < 2: - raise exceptions.RuntimeError("Grating must have between 2 and 4 ports") + raise pkex.BasePyKatException("Grating must have between 2 and 4 ports") self.__n = n self.__d = Param("d", self, SIfloat(d)) @@ -441,7 +443,7 @@ class grating(Component): @n.setter def n(self, value): if value < 2 or value > 4: - raise exceptions.RuntimeError("Grating must have between 2 and 4 ports") + raise pkex.BasePyKatException("Grating must have between 2 and 4 ports") else: self.__n = value @@ -482,14 +484,14 @@ class grating(Component): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0][0 : 2] != "gr": - raise exceptions.RuntimeError("'{0}' not a valid Finesse grating command".format(text)) + raise pkex.BasePyKatException("'{0}' not a valid Finesse grating command".format(text)) if len(values[0]) > 2: if int(values[0][2]) > 4 or int(values[0][2]) < 2: - raise exceptions.RuntimeError("Grating must have between 2 and 4 ports") + raise pkex.BasePyKatException("Grating must have between 2 and 4 ports") else: n = int(values[0][2]) else: @@ -499,17 +501,17 @@ class grating(Component): if n == 2: if len(values) != 4: - raise exceptions.RuntimeError("Two port grating must have 2 nodes defined") + raise pkex.BasePyKatException("Two port grating must have 2 nodes defined") return grating(values[0], values[2], values[3], None, None, n, values[1]) elif n == 3: if len(values) != 5: - raise exceptions.RuntimeError("Three port grating must have 3 nodes defined") + raise pkex.BasePyKatException("Three port grating must have 3 nodes defined") return grating(values[0], values[2], values[3], values[4], None, n, values[1]) else: if len(values) != 6: - raise exceptions.RuntimeError("Four port grating must have 4 nodes defined") + raise pkex.BasePyKatException("Four port grating must have 4 nodes defined") return grating(values[0], values[2], values[3], values[4], values[5], n, values[1]) @@ -550,17 +552,17 @@ class isolator(Component): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0] != "isol": - raise exceptions.RuntimeError("'{0}' not a valid Finesse isolator command".format(text)) + raise pkex.BasePyKatException("'{0}' not a valid Finesse isolator command".format(text)) values.pop(0) # remove initial value if len(values) == 4: return isolator(values[0], values[2], values[3], values[1]) else: - raise exceptions.RuntimeError("Isolator Finesse code format incorrect '{0}'".format(text)) + raise pkex.BasePyKatException("Isolator Finesse code format incorrect '{0}'".format(text)) def getFinesseText(self): rtn = ['isol {0} {1} {2} {3}'.format(self.name, self.S.value, self.nodes[0].name, self.nodes[1].name)] @@ -592,17 +594,17 @@ class lens(Component): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0] != "lens": - raise exceptions.RuntimeError("'{0}' not a valid Finesse lens command".format(text)) + raise pkex.BasePyKatException("'{0}' not a valid Finesse lens command".format(text)) values.pop(0) # remove initial value if len(values) == 4: return lens(values[0], values[2], values[3], values[1]) else: - raise exceptions.RuntimeError("Lens Finesse code format incorrect '{0}'".format(text)) + raise pkex.BasePyKatException("Lens Finesse code format incorrect '{0}'".format(text)) def getFinesseText(self): rtn = ['lens {0} {1} {2} {3}'.format(self.name, self.f.value, self.nodes[0].name, self.nodes[1].name)] @@ -667,10 +669,10 @@ class modulator(Component): @staticmethod def parseFinesseText(text): - v = text.split(" ") + v = text.split() if v[0] != "mod": - raise exceptions.RuntimeError("'{0}' not a valid Finesse modulator command".format(text)) + raise pkex.BasePyKatException("'{0}' not a valid Finesse modulator command".format(text)) v.pop(0) # remove initial value @@ -723,10 +725,10 @@ class laser(Component): @staticmethod def parseFinesseText(text): - values = text.split(" ") + values = text.split() if values[0] != "l": - raise exceptions.RuntimeError("'{0}' not a valid Finesse laser command".format(text)) + raise pkex.BasePyKatException("'{0}' not a valid Finesse laser command".format(text)) values.pop(0) # remove initial value diff --git a/pykat/finesse.py b/pykat/finesse.py index 1a7d3af7c4ccf523be9fedafe427303b767066d6..8217878351538e33292e05d77c9e4c65feb08e05 100644 --- a/pykat/finesse.py +++ b/pykat/finesse.py @@ -34,6 +34,8 @@ import pykat import warnings import re +from collections import namedtuple + from pykat.node_network import NodeNetwork from pykat.detectors import Detector from pykat.components import Component @@ -49,7 +51,6 @@ from PyQt4.QtGui import QApplication NO_GUI = False NO_BLOCK = "NO_BLOCK" -pykat_version = "0.1" pykat_web = "www.gwoptics.org/pykat" @@ -75,7 +76,7 @@ class katRun(object): def get(self, value): return self[value] def __getitem__(self, value): - idx = [i for i in range(len(self.ylabels)) if self.ylabels[i].split(" ")[0] == str(value)] + idx = [i for i in range(len(self.ylabels)) if self.ylabels[i].split()[0] == str(value)] if len(idx) > 0 and self.y.shape == (): # In the case we have a noxaxis and just one output... @@ -122,7 +123,7 @@ class katRun2D(object): def get(self, value): return self[value] def __getitem__(self, value): - idx = [i for i in range(len(self.zlabels)) if self.zlabels[i].split(" ")[0] == str(value)] + idx = [i for i in range(len(self.zlabels)) if self.zlabels[i].split()[0] == str(value)] if len(idx) > 0: return self.z[idx].squeeze() @@ -236,7 +237,9 @@ class Block: @property def name(self): return self.__name -class kat(object): +Constant = namedtuple('Constant', 'name, value, usedBy') + +class kat(object): def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None): @@ -254,6 +257,7 @@ class kat(object): self.__tempname = tempname self.pykatgui = None self.__signals = Signals() + self.constants = {} # initialise default block self.__currentTag= NO_BLOCK @@ -327,22 +331,64 @@ class kat(object): def parseKatCode(self, code): #commands = code.split("\n") self.parseCommands(code) + + def processConstants(self, commands): + """ + Before fully parsing a bunch of commands firstly any constants or variables + to be recorded and replaced. + """ + constants = self.constants + + for line in commands: + values = line.split() + + if len(values)>0 and values[0] == 'const': + + if len(values) >= 3: + if values[1] in constants: + raise pkex.BasePyKatException('const command with the name "{0}" already used'.format(values[1])) + else: + constants[str(values[1])] = Constant(values[1], values[2], []) + else: + raise pkex.BasePyKatException('const command "{0}" was not the correct format'.format(line)) + + commands_new = [] + + for line in commands: + values = line.split() + + if len(values) > 0 and values[0] != 'const': + # check if we have a var/constant in this line + if line.find('$') >= 0: + for key in constants.keys(): + if line.find('$'+key) > -1: + constants[key].usedBy.append(line) + line = line.replace('$'+key, str(constants[key].value)) + + + commands_new.append(line) + + self.constants = constants + + return commands_new def parseCommands(self, commands): blockComment = False commands=self.remove_comments(commands) + commands=self.processConstants(commands) + after_process = [] # list of commands that should be processed after # objects have been set and created - for line in commands.split("\n"): + for line in commands: #for line in commands: if len(line.strip()) >= 2: line = line.strip() # Looking for block start or end - values = line.split(" ") + values = line.split() if values[0] == "%%%": if values[1] == "FTblock": newTag = values[2] @@ -407,13 +453,13 @@ class kat(object): elif(first == "noxaxis"): self.noxaxis = True elif(first == "phase"): - v = line.split(" ") + v = line.split() if len(v) != 2: raise pkex.BasePyKatException("phase command `{0}` is incorrect.".format(line)) else: self.phase = int(v[1]) elif(first == "maxtem"): - v = line.split(" ") + v = line.split() if len(v) != 2: raise pkex.BasePyKatException("maxtem command `{0}` is incorrect.".format(line)) else: @@ -422,13 +468,13 @@ class kat(object): else: self.maxtem = int(v[1]) elif(first == "retrace"): - v = line.split(" ") + v = line.split() if len(v) > 2: raise pkex.BasePyKatException("Retrace command `{0}` is incorrect.".format(line)) elif len(v) == 2: self.retrace = v[1] elif(first == "deriv_h"): - v = line.split(" ") + v = line.split() if len(v) != 2: raise pkex.BasePyKatException("deriv_h command `{0}` is incorrect.".format(line)) else: @@ -451,9 +497,9 @@ class kat(object): for line in after_process: first = line.split(" ",1)[0] if first == "gauss" or first == "gauss*" or first == "gauss**": - pykat.commands.gauss.parseFinesseText(line) + pykat.commands.gauss.parseFinesseText(line, self) elif (first == "scale"): - v = line.split(" ") + v = line.split() if len(v) == 3: component_name = v[2] if component_name in self.__detectors : @@ -465,7 +511,7 @@ class kat(object): else: raise pkex.BasePyKatException("scale command `{0}` is incorrect.".format(text)) elif (first == "pdtype"): - v = line.split(" ") + v = line.split() if len(v) == 3: component_name = v[1] if component_name in self.__detectors : @@ -662,7 +708,7 @@ class kat(object): perffile = open(root[0] + ".perf",'r') for l in perffile.readlines(): - vals = l.strip().split(' ') + vals = l.strip().split() perfData.append((vals[0], float(vals[1]), float(vals[2]), float(vals[3]))) return [r, perfData] @@ -941,6 +987,11 @@ class kat(object): return getattr(self, '__comp_' + name) def remove_comments(self, string): + """ + This takes a raw Finesse code string and removes any comments + It returns a list of lines however, not a multiline string. + Also removes any extrawhite space in command lines. + """ pattern = r"(\".*?\"|\'.*?\'|%{3}[^\r\n]*$)|(/\*.*?\*/|%[^\r\n]*$|#[^\r\n]*$|//[^\r\n]*$)" # first group captures quoted strings (double or single) # second group captures comments (//single-line or /* multi-line */) @@ -952,7 +1003,30 @@ class kat(object): return "" # so we will return empty to remove the comment else: # otherwise, we will return the 1st group return match.group(1) # captured quoted-string - return regex.sub(_replacer, string) + + # remove any inline comments + string = regex.sub(_replacer, string) + + commands = [] + + for line in string.split('\n'): + line = line.replace('\r','') + if len(line) > 0: + # remove any mutliple whitespace + line = " ".join(line.split()) + # add to a list all the positions of any inline comment markers + i = [line.find('#'), line.find('\\')] + i = filter(lambda a: a != -1, i) + + if len(i) == 0: + commands.append(line) + else: + line = line[0:min(i)] + if len(line): + commands.append(line) + + + return commands # printing pykat logo on first input kat.logo() diff --git a/pykat/node_network.py b/pykat/node_network.py index d5e006f1ce7c022499efd978c722c57e7e52869b..c882dc26b7a99fadd48acbc4dc1851aae557762f 100644 --- a/pykat/node_network.py +++ b/pykat/node_network.py @@ -86,7 +86,7 @@ class NodeNetwork(object): comps = (None,) * 2 if len(comps) >= 2 and comps[0] != None and comps[1] != None: - raise pkex.BasePyKatException("Node is already connected to 2 components") + raise pkex.BasePyKatException("Node '{0}' is already connected to 2 components ({1}, {2})".format(node.name, comps[0], comps[1])) l = list(comps) @@ -118,6 +118,7 @@ class NodeNetwork(object): return n def removeNode(self, node): + if not isinstance(node,Node): raise exceptions.ValueError("node argument is not of type Node") @@ -326,9 +327,12 @@ class Node(object): @property def name(self): return self.__name - + class DumpNode(Node): + __total_dump_node_id = 0 + def __init__(self): - Node.__init__(self, 'dump', None, -1) + DumpNode.__total_dump_node_id -= 1 + Node.__init__(self, 'dump', None, DumpNode.__total_dump_node_id)