Commit a817860f authored by Daniel Brown's avatar Daniel Brown
Browse files

completly changing how node connections are stored: node_network (kat.nodes)...

completly changing how node connections are stored: node_network (kat.nodes) now stores all the information regarding which node is connected to which component and vice-versa. If any of these connections change it is handled in node_network. All the compoents and nodes no longer store the connection information locally, they must reference directly from node_network otherwise weird clashes will occur. Not fully tested yet.
parents abbe7a78 ab37de12
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)
......@@ -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
......@@ -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
......@@ -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
......@@ -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:
......
......@@ -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):