diff --git a/pykat/SIfloat.py b/pykat/SIfloat.py index e18a1750f6083d32529774b2bdd74149d0c9438b..e93166719a30d21bfb80ba68c8776cd670d79cc3 100644 --- a/pykat/SIfloat.py +++ b/pykat/SIfloat.py @@ -1,16 +1,14 @@ import os import re -""" -class SIfloat(value): - def __init__(self, value): - self.__value = value -""" - #staticmethod def SIfloat(value): - value=str(value) - + if type(value)==list: + return [convertToFloat(s) for s in value] + else: + return convertToFloat(value) + +def convertToFloat(value): __prefix = {'y': 1e-24, # yocto 'z': 1e-21, # zepto 'a': 1e-18, # atto @@ -30,7 +28,7 @@ def SIfloat(value): 'Z': 1e21, # zetta 'Y': 1e24, # yotta } + value = str(value) for i, j in __prefix.iteritems(): value=value.replace(i, str(j)) return float(value) - diff --git a/pykat/commands.py b/pykat/commands.py index 9a2ac779ec67587771116d196925bf8405af5de4..45ab2ca9a5f6307e5d04745170266b44f0866406 100644 --- a/pykat/commands.py +++ b/pykat/commands.py @@ -86,7 +86,7 @@ class xaxis(Command): if numpy.size(limits) != 2 : raise exceptions.ValueError("limits input should be a 2x1 vector of limits for the xaxis") - self.limits = numpy.array(limits).astype(float) + self.limits = numpy.array(SIfloat(limits)).astype(float) if steps <= 0 : raise exceptions.ValueError("steps value should be > 0") @@ -131,4 +131,4 @@ class xaxis(Command): comp_name, param_name, self.scale, min(self.limits), max(self.limits), self.steps); - \ No newline at end of file + diff --git a/pykat/components.py b/pykat/components.py index fe3bb36ecad940ead430abe5f9e7814d97b0165a..ab3b40325b9348928ab5167762e9d91c5cab7033 100644 --- a/pykat/components.py +++ b/pykat/components.py @@ -43,7 +43,7 @@ class Component(object) : self.__id = next_component_id next_component_id += 1 - def _on_kat_add(self, kat, node_array): + def _on_kat_add(self, kat): """ Called when this component has been added to a kat object. kat is the finesse.kat object which it now belongs to and @@ -55,16 +55,12 @@ class Component(object) : self._kat = kat - for node_name in self._requested_node_names: - node = self.__addNode(node_name) + kat.nodes.registerComponentNodes(self, self._requested_node_names, self.__on_node_change) - # now that we have changed what our nodes are we need to - # update stuff... - self._on_node_change() - - def _on_node_change(): + def __on_node_change(self): # need to update the node gauss parameter setter members - self.__update_node_setters() + #self.__update_node_setters() + return def __update_node_setters(self): # check if any node setters have already been added. If so we @@ -77,7 +73,7 @@ class Component(object) : detattr(self, '__nodesetter_' + ns._node.name) delattr(self.__class__, ns._node.name) - for node_name in self.getNodes(): + for node in self.nodes: self.__add_node_setter(NodeGaussSetter(self, node)) def __add_node_setter(self, ns): @@ -97,30 +93,16 @@ class Component(object) : @staticmethod def parseFinesseText(text): raise NotImplementedError("This function is not implemented") - - def setAttr(name, value): - raise NotImplementedError("This function is not implemented") - + def getFinesseText(self): """ Base class for individual finesse optical components """ raise NotImplementedError("This function is not implemented") def getQGraphicsItem(self): return None - - def __addNode(self, name): - n = self._kat.nodes.createNode(name) - - if n == None: - raise exceptions.RuntimeError("createNode did not return a node for '{0}'".format(name)) - else: - n.connect(self) - - return n - - def getNodes(self): - """ Returns a copy of the nodes the component has """ - return self._kat.nodes.getComponentNodes(self) + + @property + def nodes(self): return self._kat.nodes.getComponentNodes(self) @property def name(self): return self.__name @@ -132,7 +114,7 @@ class Component(object) : class Param(float): def __new__(self,name,value): - return float.__new__(self,value) + return float.__new__(self,SIfloat(value)) def __init__(self,name,value): self.__name = name @@ -230,14 +212,10 @@ class mirror(Component): def getFinesseText(self): rtn = [] - nodes = self.getNodes() - - if len(nodes) != 2: - raise exceptions.RuntimeError("Not enough nodes for mirror") rtn.append('m {0} {1} {2} {3} {4} {5}'.format( self.name, self.__R, self.__T, self.__phi, - nodes[0].name, nodes[1].name)) + self.nodes[0].name, self.nodes[1].name)) if self.r_ap != 0: rtn.append("attr {0} r_ap {1}".format(self.name,self.__r_ap)) if self.mass != 0: rtn.append("attr {0} mass {1}".format(self.name,self.__mass)) @@ -250,8 +228,7 @@ class mirror(Component): def getQGraphicsItem(self): if self._svgItem == None: - nodes = self.getNodes() - self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,nodes[0]), (14,15,nodes[1])]) + self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])]) return self._svgItem @@ -293,12 +270,10 @@ class space(Component): raise exceptions.RuntimeError("Space Finesse code format incorrect '{0}'".format(text)) def getFinesseText(self): - nodes = self.getNodes() - if self.__n == 1: - return 's {0} {1} {2} {3}'.format(self.name, self.__L, nodes[0].name, nodes[1].name) + return 's {0} {1} {2} {3}'.format(self.name, self.__L, self.nodes[0].name, self.nodes[1].name) else: - return 's {0} {1} {2} {3} {4}'.format(self.name, self.__L, self.__n, nodes[0].name, nodes[1].name) + return 's {0} {1} {2} {3} {4}'.format(self.name, self.__L, self.__n, self.nodes[0].name, self.nodes[1].name) def getQGraphicsItem(self): if self._QItem == None: @@ -306,19 +281,19 @@ class space(Component): return self._QItem - def changeNode(self, node_old, node_new): - ''' - Called when a space's node has been connected - to another components node - ''' - node_new.connect(self) - node_old.disconnect(self) - - if self._nodes[0] == node_old: - self._nodes[0] = node_new - - if self._nodes[1] == node_old: - self._nodes[1] = node_new + # def changeNode(self, node_old, node_new): + # ''' + # Called when a space's node has been connected + # to another components node + # ''' + # node_new.connect(self) + # node_old.disconnect(self) + + # if self._nodes[0] == node_old: + # self._nodes[0] = node_new + + # if self._nodes[1] == node_old: + # self._nodes[1] = node_newf class laser(Component): @@ -363,15 +338,11 @@ class laser(Component): raise exceptions.FinesseParse("Laser Finesse code format incorrect '{0}'".format(text)) def getFinesseText(self): - nodes = self.getNodes() - - return 'l {0} {1} {2} {3} {4}'.format(self.name, self.__power, self.__f_offset, self.__phase, nodes[0].name) + return 'l {0} {1} {2} {3} {4}'.format(self.name, self.__power, self.__f_offset, self.__phase, self.nodes[0].name) def getQGraphicsItem(self): if self._svgItem == None: - nodes = self.getNodes() - self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/laser.svg", - self,[(65,25,nodes[0])]) + self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/laser.svg", self, [(65,25,self.nodes[0])]) return self._svgItem diff --git a/pykat/detectors.py b/pykat/detectors.py index ebbaed8d1be528584ece7d838f58b7d0a6f769e6..6417c0707db2e358f3d43b2f53014065e842bac5 100644 --- a/pykat/detectors.py +++ b/pykat/detectors.py @@ -49,25 +49,73 @@ class Detector(object) : name = property(__getname) class photodiode(Detector): - class demodulation: - def __init__(self, f, phase): - self.frequency = f - self.phase = phase + + def __init__(self, name, node, type, num_demods, demods): - def __init__(self, name, node, demods): Detector.__init__(self, name, node) - - self._num_demods = len(demods) - - for d in demods: - if not isinstance(d, photodiode.demodulation): - raise ValueError("demods array has something other than a demodulation in it") - - self._demods.append(d) + if num_demods>2: + raise NotImplementedError("pd with more than two demodulations not implemented yet") + self.num_demods = num_demods + self.type = type + self + + @property + def num_demods(self): return Param('num_demods', self.__num_demods) + @num_demods.setter + def num_demods(self,value): self.__num_demods = int(value) + @property + def type(self): return Param('type', self.__type) + @type.setter + def type(self,value): self.__type = value + @property + def f1(self): return Param('f1', self.__f1) + @f1.setter + def f1(self,value): self.__f1 = SIfloat(value) + @property + def phi1(self): return Param('phi1', self.__phi1) + @phi1.setter + def phi1(self,value): self.__phi1 = SIfloat(value) @staticmethod - def parseFinesseText(text): - raise NotImplementedError("This function is not implemented") + def parseFinesseText(text): + values = text.split(" ") + + if values[0][0:2] != "pd": + raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text)) + if len(value[0])==2: + __num_demods=0 + __type="" + elif len(value[0])==3 or len(value[0])==4: + if value[0][3]=="S": + __type="S" + elif value[0][3]=="N": + __type="N" + else: + try: + __num_demods=int(values[0][3]) + __type="" + except ValueError: + raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text)) + if len(value[0])==4: + try: + __num_demods=int(values[0][4]) + except ValueError: + raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text)) + else: + raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text)) + + if __num_demods<0 or __num_demods>5: + raise exceptions.FinesseParse("'{0}' number of demodulations must be >0 and <5".format(text)) + + values.pop(0) # remove initial value + + if len(values) == 2 * __num_demods + 1 or len(values) == 2 * __num_demods + 2: + return photodiode(value[0], values[-1], __type, __num_demods, values[1:len(values-1)]) + else: + raise exceptions.FinesseParse("Photodiode code format incorrect '{0}'".format(text)) + + #return photodiode("name", "node", demods) + #raise NotImplementedError("This function is not implemented") def getFinesseText(self) : if self.enabled: @@ -90,4 +138,4 @@ class photodiode(Detector): self._svgItem = ComponentQGraphicsItem(":/resources/photodiode_red.svg",self,[(-5,11,self._node)]) return self._svgItem - \ No newline at end of file + diff --git a/pykat/finesse.py b/pykat/finesse.py index d7b7d3c37da1c3951c10a9d0b3b19f66d187ae22..fe7393e5408df8be16d4d14cfd9bce2bd4c31387 100644 --- a/pykat/finesse.py +++ b/pykat/finesse.py @@ -76,7 +76,7 @@ class Block: class kat(object): - def __init__(self, kat_file=None, kat_code=None, katdir="", katname=""): + def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None): self.scene = None # scene object for GUI self.__blocks = {} # dictionary of blocks that are used @@ -88,6 +88,8 @@ class kat(object): self.nodes = NodeNetwork(self) self.__katdir = katdir self.__katname = katname + self.__tempdir = tempdir + self.__tempname = tempname self.pykatgui = None # Various options for running finesse, typicaly the commands with just 1 input @@ -132,7 +134,7 @@ class kat(object): def parseKatCode(self, code): #commands = code.split("\n") - self.parseCommands(commands) + self.parseCommands(code) def parseCommands(self, commands): blockComment = False @@ -191,16 +193,16 @@ class kat(object): obj = pykat.components.space.parseFinesseText(line) elif(first == "l"): obj = pykat.components.laser.parseFinesseText(line) - elif(first == "xaxis" or first == "x2axis" or first == "xaxis*" or first == "x2axis*"): - obj = pykat.commands.xaxis.parseFinesseText(line) + elif(first == "xaxis" or first == "x2axis" or first == "xaxis*" or first == "x2axis*"): + obj = pykat.commands.xaxis.parseFinesseText(line) else: print "Parsing `{0}` into pykat object not implemented yet, added as extra line.".format(line) obj = line - self.__block[self.__currentTag].contents.append(obj) + self.__blocks[self.__currentTag].contents.append(obj) - if not isinstance(line, str): - self.__block[self.__currentTag].contents.append(obj) + if not isinstance(obj, str): + self.__blocks[self.__currentTag].contents.append(obj) self.add(obj) self.__currentTag = NO_BLOCK @@ -239,7 +241,12 @@ class kat(object): raise MissingFinesse() # create a kat file which we will write the script into - katfile = tempfile.NamedTemporaryFile(suffix=".kat") + if self.__tempname == None: + katfile = tempfile.NamedTemporaryFile(suffix=".kat", dir=self.__tempdir) + else: + filepath =os.path.join(self.__tempdir, self.__tempname+".kat" ) + katfile = open( filepath, 'w' ) + katfile.writelines(r.katScript) katfile.flush() @@ -249,6 +256,7 @@ class kat(object): cmd.append('--no-backspace') cmd.append(katfile.name) + print cmd p=subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) err = "" @@ -399,7 +407,8 @@ class kat(object): out = [] for key in self.__blocks: - objs = self.__blocks[key] + objs = self.__blocks[key].contents + print key, objs out.append("%%% FTblock " + key) for obj in objs: diff --git a/pykat/node_network.py b/pykat/node_network.py index 850e5fcf428cc5905c148f5bfe8ac9f58dfb21e4..de4221dd77cef26a5b7a25203a0b54d446ab30dd 100644 --- a/pykat/node_network.py +++ b/pykat/node_network.py @@ -12,52 +12,99 @@ from pykat.detectors import Detector class NodeNetwork(object): def __init__(self, kat): - self._nodes = {} + self.__nodes = {} self.__kat = kat - self.__componentNodes = {} + self.__nodeComponents = {} # dictionary of tuples containing which components are connected to a node + self.__componentNodes = {} # dictionary of tuples containing which nodes are connected to a given component + self.__componentCallback = {} + self.__node_id = 1 + + def registerComponentNodes(self, comp, node_names, change_callback): + """ + For a given component we create some nodes or get existing ones and + attach them to this component. Also specify a callback function that + is called whenever the nodes attached to this component are changed + , e.g. connected, disconnected, name change, etc. + """ + if not isinstance(comp, Component): + raise exceptions.ValueError("comp argument is not of type Component") + + if comp.id in self.__componentNodes: + raise pkex.BasePyKatException("Component has already been registered") + + list = [] + + for name in node_names: + n = createNode(name) + self.connectNodeToComp(node, comp, do_callback=False) + list.append(n) + + self.__componentNodes[comp.id] = tuple(list) + self.__componentCallback[comp.id] = change_callback + + change_callback() + + def connectNodeToComp(self, node, comp, do_callback=True): + if node.id in self.__nodeComponents: + comps = self.__nodeComponents[node.id] + else: + comps = () + + if len(comps) >= 2: + raise pkex.BasePyKatException("Node is already connected to 2 components") + + l = list(comps) + l.append(comp) + + self.__nodeComponents[node.id] = tuple(l) + + if do_callback: self.__componentCallback[comp.id]() def createNode(self, node_name): if node_name == 'dump': return DumpNode() - if node_name in self._nodes: + if node_name in self.__nodes: # then this node already exists - return self._nodes[node_name] + return self.__nodes[node_name] else: - n = Node(node_name, self) - self.__add_node(n) # add node as a member of this object, e.g. kat.nodes.n - self._nodes[node_name] = n + n = Node(node_name, self, self.__node_id) + + self.__node_id += 1 + self.__add_node_attr(n) # add node as a member of this object, e.g. kat.nodes.n + self.__nodes[node_name] = n return n def removeNode(self, node): if not isinstance(node,Node): raise exceptions.ValueError("node argument is not of type Node") - if node.name not in self._nodes: + if node.name not in self.__nodes: raise exceptions.RuntimeError("Trying to remove node {0} when it has not been added".format(node.name)) - C = node.getComponents() + C = self.getNodeComponents(node) - if C[0][0] is not None or C[0][1] is not None: - raise exceptions.RuntimeError("Cannot remove a node which is attached to components still") + if C[0] is not None or C[1] is not None: + raise exceptions.RuntimeError("Cannot remove a node which is attached to components") - if len(C[1]) > 0: + if len(node.getDetectors()) > 0: raise exceptions.RuntimeError("Cannot remove a node which is attached to detectors still") - self.__remove_node(node) - del self._nodes[node.name] + self.__remove_node_attr(node) + del self.__nodes[node.name] def hasNode(self, name): - return (name in self._nodes) + return (name in self.__nodes) def getNodes(self): - return self._nodes.copy() + return self.__nodes.copy() def dumpInfo(self): - for name in self._nodes: + for name in self.__nodes: + + n = self.__nodes[name] - n = self._nodes[name] items = n.getComponents() comp = items[0][:] det = items[1] @@ -83,25 +130,21 @@ class NodeNetwork(object): print "node: {0} connected:{1} {2}->{3} {4}".format( n.name,n.isConnected(),comp1, comp2, detectors) - def getComponentNodes(self, comp): + def getComponentNodes(self, comp): return self.__componentNodes[comp.id] + def getNodeComponents(self, node): return self.__nodeComponents[node.id] - if not isinstance(comp, Component): - raise pkex.BasePyKatException("Passed object was not a component") - - return self.__componentNodes[comp.id] - - def __add_node(self, node): + def __add_node_attr(self, node): if not isinstance(node, Node): raise exceptions.ValueError("Argument is not of type Node") name = node.name - fget = lambda self: self.__get_node(name) + fget = lambda self: self.__get_node_attr(name) setattr(self.__class__, name, property(fget)) setattr(self, '__node_' + name, node) - def __remove_node(self, node): + def __remove_node_attr(self, node): if not isinstance(node, Node): raise exceptions.ValueError("Argument is not of type Node") @@ -109,7 +152,7 @@ class NodeNetwork(object): detattr(self, '__node_' + name) delattr(self.__class__, name) - def __get_node(self, name): + def __get_node_attr(self, name): return getattr(self, '__node_' + name) class Node(object): @@ -117,19 +160,24 @@ class Node(object): w0_z = 1 z_zR = 2 - def __init__(self, name, network): - self._comp1 = None - self._comp2 = None + def __init__(self, name, network, id): self._detectors = [] self.__name = name self._item = None self._network = network self.__gauss = None self.__gauss_version = None + self.__id = id + @property + def id(self): return self.__id + @property def network(self): return self._network + @property + def components(self): return self._network.getNodeComponents(self) + @property def gauss(self): return self.__gauss @@ -148,36 +196,11 @@ class Node(object): self.__gauss_version = Node.gauss_version.w0_z - def getFinesseText(self): - - if not self.isConnected() or self.__gauss == None or self.__gauss_version == None: - return None - - - comp = "" - - if self._comp2 == None: - comp = self._comp1.name - else: - comp = self._comp2.name - - rtn = [] - - if self.__gauss_version == Node.gauss_version.w0_z: - - if len(self.__gauss) == 2: - rtn.append("gauss gauss_{node} {comp} {node} {w0} {z}".format(node=self.name, comp=comp, w0=self.__gauss[0], z=self.__gauss[1])) - elif len(self.__gauss) == 4: - rtn.append("gauss gauss_{node} {comp} {node} {w0x} {zx} {w0y} {zy}".format(node=self.name, comp=comp, w0x=self.__gauss[0], zx=self.__gauss[1], w0y=self.__gauss[2], zy=self.__gauss[3])) - else: - raise pkex.BasePyKatException("Unexpected number of gaussian parameters") - else: - raise pkex.BasePyKatException("Unexpected gauss version") - - return rtn + def getFinesseText(self): + return [] def isConnected(self): - if (self._comp1 is not None) and (self._comp2 is not None): + if (self.components[0] is not None) and (self.self.components[1] is not None): return True else: return False @@ -188,94 +211,46 @@ class Node(object): if self._item != None: self._item.scene().removeItem(self._item) - def disconnect(self, obj): - - if not (isinstance(obj,Component) or isinstance(obj,Detector)): - raise exceptions.ValueError("Object is not a component or detector") - - if isinstance(obj, Component): - - if self._comp1 == obj: - self._comp1 = None - elif self._comp2 == obj: - self._comp2 = None - else: - raise exceptions.RuntimeError("Cannot dettach {0} from node {1}".format( - obj.name, self.__name)) - - else: - # we must have a detector as we check above - self._detectors.remove(obj) - - if self._item is not None: - self._item.refresh() - - - def connect(self, obj): - - if not (isinstance(obj,Component) or isinstance(obj,Detector)): - raise exceptions.ValueError("Object is not a component or detector") - - if isinstance(obj, Component): - - if self._comp1 == None: - self._comp1 = obj - elif self._comp2 == None: - self._comp2 = obj - else: - raise exceptions.RuntimeError("Cannot attach {0} to node {1} as it is already connected: {2} and {3}".format( - obj.name, self.__name,self._comp1.name,self._comp2.name)) - - if self._comp1 == self._comp2: - raise exceptions.RuntimeError("Cannot connect {0} to both sides of node".format(obj.name)) - else: - # we must have a detector as we check above - self._detectors.append(obj) - - if self._item is not None: - self._item.refresh() - def getQGraphicsItem(self,dx=0,dy=0,nsize=8,parent=None): if self._item == None: self._item = pykat.gui.graphics.NodeQGraphicItem(self, dx,dy, -nsize/2,-nsize/2, - nsize,nsize,parent) + nsize, nsize, parent) return self._item - def getComponents(self): - ''' Returns a tuple with the first being the 2 components that - connect to the node. The second item is a list of the detectors at the - node''' - return [(self._comp1, self._comp2),self._detectors] + def getDetectors(self): + return self._detectors[:] def amIConnected(self, obj): """ Checks if obj is connected to the node. Returns true or false in tuple with None or the other object and the node index which it is attached to """ - if obj == self._comp1: - if self._comp2 == None: + comps = self.components + + if obj == comps[0]: + if comps[1] == None: ix = -1 else: - ix = self._comp2.getNodes().index(self) + ix = comps[1].getNodes().index(self) - return [True, self._comp2, ix] - elif obj == self._comp2: - if self._comp1 == None: + return [True, comps[1], ix] + + elif obj == comps[1]: + if comps[0] == None: ix = -1 else: - ix = self._comp1.getNodes().index(self) + ix = comps[0].getNodes().index(self) - return [True, self._comp1, ix] + return [True, comps[0], ix] else: return [False, None] - def __getname(self): - return self.__name - - name = property(__getname) + @property + def name(self): return self.__name + class DumpNode(Node): def __init__(self):