diff --git a/bin/parse.kat b/bin/parse.kat deleted file mode 100644 index 235dd32b622715b527b0bf7a0977149d7fb4225e..0000000000000000000000000000000000000000 --- a/bin/parse.kat +++ /dev/null @@ -1,10 +0,0 @@ -l l1 1 0 0 n1 -s s1 10 1 n1 n2 -m m1 0.5 0.5 0 n2 n3 -s s2 10 1 n3 n4 -m m2 0.5 0.5 0 n4 dump - -pd PD1 n2 - -xaxis m1 phi lin 0 360 1000 - diff --git a/bin/test_plot.py b/bin/test_plot.py index f21aa3ce748d4c9d1aae6b015d869050af9d1e1c..0fc55def1e3ec2fc893396a57953c55e4039d4b1 100644 --- a/bin/test_plot.py +++ b/bin/test_plot.py @@ -1,46 +1,46 @@ -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 1 0 0 n1 -s s1 10 1 n1 n2 -m m1 0.5 0.5 0 n2 n3 -s s2 10 1 n3 n4 -m m2 0.5 0.5 0 n4 n5 -s s3 10 1 n5 n6 -""" - -#kat = finesse.kat(katexe='/Users/adf/work/bin/kat') -kat = finesse.kat() - -kat.parseCommands(code) - -kat.add(cavity('cav1', 'm1', 'n3', 'm2', 'n4')) - -kat.add(photodiode('pd_cav','n4')) -kat.add(photodiode('pd_ref','n2')) -kat.add(photodiode('pd_trs','n5')) - -kat.add(xaxis("lin", [0, 360], kat.m2, kat.m2.phi, 100)) - -kat.m1.Rcx = -1000.0 -kat.m1.Rcy = -1000.0 -kat.m2.Rcx = 1000.0 -kat.m2.Rcy = 1000.0 - -kat.maxtem = 0 - -out = kat.run(printout=0,printerr=0) - -pl.figure() -pl.plot(out.x, out["pd_cav"]) -pl.xlabel(out.xlabel) -pl.ylabel("Intensity [W]") -pl.legend(out.ylabels) -pl.show() +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 1 0 0 n1 +s s1 10 1 n1 n2 +m m1 0.5 0.5 0 n2 n3 +s s2 10 1 n3 n4 +m m2 0.5 0.5 0 n4 n5 +s s3 10 1 n5 n6 +""" + +#kat = finesse.kat(katexe='/Users/adf/work/bin/kat') +kat = finesse.kat() + +kat.parseCommands(code) + +kat.add(cavity('cav1', 'm1', 'n3', 'm2', 'n4')) + +kat.add(photodiode('pd_cav','n4')) +kat.add(photodiode('pd_ref','n2')) +kat.add(photodiode('pd_trs','n5')) + +kat.add(xaxis("lin", [0, 360], kat.m2, kat.m2.phi, 100)) + +kat.m1.Rcx = -1000.0 +kat.m1.Rcy = -1000.0 +kat.m2.Rcx = 1000.0 +kat.m2.Rcy = 1000.0 + +kat.maxtem = 0 + +out = kat.run(printout=0,printerr=0) + +pl.figure() +pl.plot(out.x, out["pd_cav"]) +pl.xlabel(out.xlabel) +pl.ylabel("Intensity [W]") +pl.legend(out.ylabels) +pl.show() diff --git a/pykat/colours.py b/pykat/colours.py index f485487593fe50d78b4108f6724ebea985e91094..cc65b2f77c41b757c5de407ecd8b7f8ca6315b86 100644 --- a/pykat/colours.py +++ b/pykat/colours.py @@ -1,22 +1,22 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Jan 28 10:43:18 2013 - -@author: Daniel -""" - -class colours: - HEADER = '\033[95m' - OKBLUE = '\033[94m' - OKGREEN = '\033[92m' - WARNING = '\033[93m' - FAIL = '\033[91m' - ENDC = '\033[0m' - - def disable(self): - self.HEADER = '' - self.OKBLUE = '' - self.OKGREEN = '' - self.WARNING = '' - self.FAIL = '' +# -*- coding: utf-8 -*- +""" +Created on Mon Jan 28 10:43:18 2013 + +@author: Daniel +""" + +class colours: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + + def disable(self): + self.HEADER = '' + self.OKBLUE = '' + self.OKGREEN = '' + self.WARNING = '' + self.FAIL = '' self.ENDC = '' \ No newline at end of file diff --git a/pykat/commands.py b/pykat/commands.py index 19fe6606425802bd21c89450a825b5c9e2b48602..9f85839f517a58ce9db9d4ed437242b756903932 100644 --- a/pykat/commands.py +++ b/pykat/commands.py @@ -1,142 +1,142 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Jan 28 11:58:09 2013 - -@author: Daniel -""" -import numpy -from numpy import min,max -import exceptions -from components import * -from structs import * -from pykat.param import Param, putter - -class Command(object): - def __init__(self): - self.tag = None - - def getFinesseText(self): - """ Base class for individual finesse optical components """ - raise NotImplementedError("This function is not implemented") - - @staticmethod - def parseFinesseText(text): - raise NotImplementedError("This function is not implemented") - - def _on_kat_add(self, kat): - """ - Called when this component has been added to a kat object - """ - self._kat = kat - -class cavity(Command): - def __init__(self, name, c1, n1, c2, n2): - self.__name = name - self.__c1 = c1 - self.__c2 = c2 - self.__n1 = n1 - self.__n2 = n2 - - def getFinesseText(self): - return 'cav {0} {1} {2} {3} {4}'.format(self.__name, self.__c1, self.__n1, self.__c2, self.__n2); - -class gauss(object): - @staticmethod - def parseFinesseText(text, kat): - values = text.split(" ") - - if not values[0].startswith("gauss"): - raise exceptions.RuntimeError("'{0}' not a valid Finesse gauss command".format(text)) - -class xaxis(Command): - - def __init__(self, scale, limits, comp, param, steps, axis_type="xaxis"): - self._axis_type = axis_type - - self.x = putter("x1") - self.mx = putter("mx1") - - if scale == "lin": - scale = Scale.linear - elif scale == "log": - scale = Scale.logarithmic - elif isinstance(scale, str): - # else we have a string but not a recognisable one - raise exceptions.ValueError("scale argument '{0}' is not valid, must be 'lin' or 'log'".format(scale)) - - if scale != Scale.linear and scale != Scale.logarithmic: - raise exceptions.ValueError("scale is not Scale.linear or Scale.logarithmic") - - self.scale = scale - - if numpy.size(limits) != 2 : - raise exceptions.ValueError("limits input should be a 2x1 vector of limits for the xaxis") - - self.limits = numpy.array(SIfloat(limits)).astype(float) - - if steps <= 0 : - raise exceptions.ValueError("steps value should be > 0") - - self.steps = int(steps) - - # if entered component is a string then just store and use that - if isinstance(comp, str): - self.__comp = comp - elif not isinstance(comp, Component): - raise exceptions.ValueError("{0} has not been added".format(comp)) - else: - self.__comp = comp - - if isinstance(param, str): - self.__param = param - elif not isinstance(param, Param) : - raise exceptions.ValueError("param argument is not of type Param") - else: - self.__param = param - - @staticmethod - def parseFinesseText(text): - values = text.split(" ") - - if values[0] != "xaxis" and values[0] != "xaxis*": - raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text)) - - axis_type = values[0] - - values.pop(0) # remove initial value - - if len(values) != 6: - raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text)) - - return xaxis(values[2], [values[3], values[4]], values[0], values[1], values[5], axis_type=axis_type) - - def getFinesseText(self): - # store either the component name of the string provided - comp_name = self.__comp.name if isinstance(self.__comp, Component) else self.__comp - param_name = self.__param.name if isinstance(self.__param, Param) else self.__param - - return '{axis_type} {0} {1} {2} {3} {4} {5}'.format( - comp_name, param_name, self.scale, - min(self.limits), max(self.limits), self.steps, axis_type=self._axis_type); - -class x2axis(xaxis): - def __init__(self, scale, limits, comp, param, steps): - xaxis.__init__(self, scale, limits, comp, param, steps, axis_type="x2axis") - self.x = putter("x2") - self.mx = putter("mx2") - - @staticmethod - def parseFinesseText(text): - values = text.split(" ") - - if values[0] != "x2axis" and values[0] != "x2axis*": - raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text)) - - axis_type = values[0] - - values.pop(0) # remove initial value - - if len(values) != 6: - raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text)) - +# -*- coding: utf-8 -*- +""" +Created on Mon Jan 28 11:58:09 2013 + +@author: Daniel +""" +import numpy +from numpy import min,max +import exceptions +from components import * +from structs import * +from pykat.param import Param, putter + +class Command(object): + def __init__(self): + self.tag = None + + def getFinesseText(self): + """ Base class for individual finesse optical components """ + raise NotImplementedError("This function is not implemented") + + @staticmethod + def parseFinesseText(text): + raise NotImplementedError("This function is not implemented") + + def _on_kat_add(self, kat): + """ + Called when this component has been added to a kat object + """ + self._kat = kat + +class cavity(Command): + def __init__(self, name, c1, n1, c2, n2): + self.__name = name + self.__c1 = c1 + self.__c2 = c2 + self.__n1 = n1 + self.__n2 = n2 + + def getFinesseText(self): + return 'cav {0} {1} {2} {3} {4}'.format(self.__name, self.__c1, self.__n1, self.__c2, self.__n2); + +class gauss(object): + @staticmethod + def parseFinesseText(text, kat): + values = text.split(" ") + + if not values[0].startswith("gauss"): + raise exceptions.RuntimeError("'{0}' not a valid Finesse gauss command".format(text)) + +class xaxis(Command): + + def __init__(self, scale, limits, comp, param, steps, axis_type="xaxis"): + self._axis_type = axis_type + + self.x = putter("x1") + self.mx = putter("mx1") + + if scale == "lin": + scale = Scale.linear + elif scale == "log": + scale = Scale.logarithmic + elif isinstance(scale, str): + # else we have a string but not a recognisable one + raise exceptions.ValueError("scale argument '{0}' is not valid, must be 'lin' or 'log'".format(scale)) + + if scale != Scale.linear and scale != Scale.logarithmic: + raise exceptions.ValueError("scale is not Scale.linear or Scale.logarithmic") + + self.scale = scale + + if numpy.size(limits) != 2 : + raise exceptions.ValueError("limits input should be a 2x1 vector of limits for the xaxis") + + self.limits = numpy.array(SIfloat(limits)).astype(float) + + if steps <= 0 : + raise exceptions.ValueError("steps value should be > 0") + + self.steps = int(steps) + + # if entered component is a string then just store and use that + if isinstance(comp, str): + self.__comp = comp + elif not isinstance(comp, Component): + raise exceptions.ValueError("{0} has not been added".format(comp)) + else: + self.__comp = comp + + if isinstance(param, str): + self.__param = param + elif not isinstance(param, Param) : + raise exceptions.ValueError("param argument is not of type Param") + else: + self.__param = param + + @staticmethod + def parseFinesseText(text): + values = text.split(" ") + + if values[0] != "xaxis" and values[0] != "xaxis*": + raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text)) + + axis_type = values[0] + + values.pop(0) # remove initial value + + if len(values) != 6: + raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text)) + + return xaxis(values[2], [values[3], values[4]], values[0], values[1], values[5], axis_type=axis_type) + + def getFinesseText(self): + # store either the component name of the string provided + comp_name = self.__comp.name if isinstance(self.__comp, Component) else self.__comp + param_name = self.__param.name if isinstance(self.__param, Param) else self.__param + + return '{axis_type} {0} {1} {2} {3} {4} {5}'.format( + comp_name, param_name, self.scale, + min(self.limits), max(self.limits), self.steps, axis_type=self._axis_type); + +class x2axis(xaxis): + def __init__(self, scale, limits, comp, param, steps): + xaxis.__init__(self, scale, limits, comp, param, steps, axis_type="x2axis") + self.x = putter("x2") + self.mx = putter("mx2") + + @staticmethod + def parseFinesseText(text): + values = text.split(" ") + + if values[0] != "x2axis" and values[0] != "x2axis*": + raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text)) + + axis_type = values[0] + + values.pop(0) # remove initial value + + if len(values) != 6: + raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text)) + return x2axis(values[2], [values[3], values[4]], values[0], values[1], values[5]) \ No newline at end of file diff --git a/pykat/components.py b/pykat/components.py index 73948105755f4e25f8fd966e765d5f5d08fb62ed..8aac82e60ce14f2ab2a19a7109aa05872b119765 100644 --- a/pykat/components.py +++ b/pykat/components.py @@ -1,729 +1,729 @@ -# -*- coding: utf-8 -*- -""" -Created on Mon Jan 28 11:10:01 2013 - -@author: Daniel -""" -import exceptions -import pykat.exceptions as pkex -import pykat -from pykat.node_network import * -from pykat.exceptions import * -import abc - -import pykat.gui.resources -import pykat.gui.graphics -from pykat.gui.graphics import * -from pykat.SIfloat import * -from pykat.param import Param, AttrParam - -next_component_id = 1 - -class NodeGaussSetter(object): - def __init__(self, component, node): - self.__comp = component - self.__node = node - - @property - def node(self): - return self.__node - - @property - def q(self): - return self.__node.qx - - @q.setter - def q(self, value): - self.__node.setGauss(self.__comp, complex(value)) - - @property - def qx(self): - return self.__node.qx - @qx.setter - def qx(self, value): - self.__node.setGauss(self.__comp, complex(value)) - - @property - def qy(self): - return self.__node.qy - @qy.setter - def qy(self, value): - self.__node.setGauss(self.__comp, self.qx, complex(value)) - -class Component(object): - __metaclass__ = abc.ABCMeta - - def __init__(self, name): - self.__name = name - self._svgItem = None - self._requested_node_names = [] - self._kat = None - self.tag = None - self._params = [] - - # store a unique ID for this component - global next_component_id - self.__id = next_component_id - next_component_id += 1 - - # This creates an instance specific class for the component - # this enables us to add properties to instances rather than - # all classes - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) - - def _register_param(self, param): - self._params.append(param) - - 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 - node_array is an array specific to this object which contains - references to the nodes that are attached to it. - """ - if self._kat != None: - raise pkex.BasePyKatException("Component has already been added to a finesse.kat object") - - self._kat = kat - - kat.nodes.registerComponentNodes(self, self._requested_node_names, self.__on_node_change) - - def __on_node_change(self): - # need to update the node gauss parameter setter members - self.__update_node_setters() - - def __update_node_setters(self): - # check if any node setters have already been added. If so we - # need to remove them. This function should get called if the nodes - # are updated, either by some function call or the GUI - key_rm = [k for k in self.__dict__ if k.startswith("__nodesetter_", 0, 13)] - - # now we have a list of which to remove - for key in key_rm: - ns = self.__dict__[key] - delattr(self, '__nodesetter_' + ns.node.name) - delattr(self.__class__, ns.node.name) - - for node in self.nodes: - if type(node) != pykat.node_network.DumpNode: - ns = NodeGaussSetter(self, node) - self.__add_node_setter(ns) - - def __add_node_setter(self, ns): - - if not isinstance(ns, NodeGaussSetter): - raise exceptions.ValueError("Argument is not of type NodeGaussSetter") - - name = ns.node.name - fget = lambda self: self.__get_node_setter(name) - - setattr(self.__class__, name, property(fget)) - setattr(self, '__nodesetter_' + name, ns) - - def __get_node_setter(self, name): - return getattr(self, '__nodesetter_' + name) - - @staticmethod - @abc.abstractmethod - def parseFinesseText(text): - """Parses Finesse syntax""" - raise NotImplementedError("This function is not implemented") - - @abc.abstractmethod - def getFinesseText(self): - """ Base class for individual Finesse optical components """ - raise NotImplementedError("This function is not implemented") - - @abc.abstractmethod - def getQGraphicsItem(self): - return None - - @property - def nodes(self): return self._kat.nodes.getComponentNodes(self) - - @property - def name(self): return self.__name - - @property - def id(self): return self.__id - - def __str__(self): return self.name - -class AbstractMirrorComponent(Component): - __metaclass__ = abc.ABCMeta - - def __init__(self, name, R=0, T=0, phi=0, Rcx=0, Rcy=0, xbeta=0, ybeta=0, mass=0, r_ap=0): - super(AbstractMirrorComponent, self).__init__(name) - - self.__R = Param("R", self, SIfloat(R)) - self.__T = Param("T", self, SIfloat(T)) - self.__phi = Param("phi", self, SIfloat(phi)) - self.__Rcx = AttrParam("Rcx", self, SIfloat(Rcx)) - self.__Rcy = AttrParam("Rcy", self, SIfloat(Rcy)) - self.__xbeta = AttrParam("xbeta", self, SIfloat(xbeta)) - self.__ybeta = AttrParam("ybeta", self, SIfloat(ybeta)) - self.__mass = AttrParam("mass", self, SIfloat(mass)) - self.__r_ap = AttrParam("r_ap", self, SIfloat(r_ap)) - - @property - def r_ap(self): return self.__r_ap - @r_ap.setter - def r_ap(self,value): self.__r_ap.value = SIfloat(value) - - @property - def mass(self): return self.__mass - @mass.setter - def mass(self,value): self.__mass.value = SIfloat(value) - - @property - def R(self): return self.__R - @R.setter - def R(self,value): self.__R.value = SIfloat(value) - - @property - def T(self): return self.__T - @T.setter - def T(self,value): self.__T.value = SIfloat(value) - - @property - def phi(self): return self.__phi - @phi.setter - def phi(self,value): self.__phi.value = SIfloat(value) - - @property - def Rcx(self): return self.__Rcx - @Rcx.setter - def Rcx(self,value): self.__Rcx.value = SIfloat(value) - - @property - def Rcy(self): return self.__Rcy - @Rcy.setter - def Rcy(self,value): self.__Rcy.value = SIfloat(value) - - @property - def xbeta(self): return self.__xbeta - @xbeta.setter - def xbeta(self,value): self.__xbeta.value = SIfloat(value) - - @property - def ybeta(self): return self.__ybeta - @ybeta.setter - def ybeta(self,value): self.__ybeta.value = SIfloat(value) - - @property - def Rc(self): - if self.Rcx == self.Rcy: - return self.Rcx - else: - return [self.Rcx, self.Rcy] - - @Rc.setter - def Rc(self,value): - self.Rcx.value = SIfloat(value) - self.Rcy.value = SIfloat(value) - -class mirror(AbstractMirrorComponent): - def __init__(self,name,node1,node2,R=0,T=0,phi=0,Rcx=0,Rcy=0,xbeta=0,ybeta=0,mass=0, r_ap=0): - super(mirror, self).__init__(name, R, T, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap) - - self._requested_node_names.append(node1) - self._requested_node_names.append(node2) - - @staticmethod - def parseFinesseText(text): - 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)) - - if len(values) != 7: - raise exceptions.RuntimeError("Mirror Finesse code format incorrect '{0}'".format(text)) - - if len(values[0])==1: - values.pop(0) # remove initial value - return mirror(values[0], values[4], values[5], R=values[1], T=values[2], phi=values[3]) - else: - if values[0][1]=="1": - values.pop(0) # remove initial value - return mirror(values[0], values[4], values[5], R=1.0-SIfloat(values[1])-SIfloat(values[2]), T=values[1], phi=values[3]) - else: - values.pop(0) # remove initial value - return mirror(values[0], values[4], values[5], R=values[1], T=1.0-SIfloat(values[1])-SIfloat(values[2]), phi=values[3]) - - def getFinesseText(self): - rtn = [] - - rtn.append('m {0} {1} {2} {3} {4} {5}'.format( - self.name, self.R.value, self.T.value, self.phi.value, - self.nodes[0].name, self.nodes[1].name)) - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - - def getQGraphicsItem(self): - if self._svgItem == None: - self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])]) - - return self._svgItem - -class beamSplitter(AbstractMirrorComponent): - def __init__(self, name, node1, node2, node3, node4, R = 0, T = 0, phi = 0, alpha = 0, Rcx = 0, Rcy = 0, xbeta = 0, ybeta = 0, mass = 0, r_ap = 0): - super(beamSplitter, self).__init__(name, R, T, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap) - - self._requested_node_names.append(node1) - self._requested_node_names.append(node2) - self._requested_node_names.append(node3) - self._requested_node_names.append(node4) - - self.__alpha = AttrParam("alpha", self, SIfloat(alpha)) - - @property - def alpha(self): return self.__alpha - @alpha.setter - def alpha(self,value): self.__alpha.value = SIfloat(value) - - @staticmethod - def parseFinesseText(text): - 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)) - - if len(values) != 10: - raise exceptions.RuntimeError("Beam splitter Finesse code format incorrect '{0}'".format(text)) - - if len(values[0])==1: - values.pop(0) # remove initial value - return beamSplitter(values[0], values[5], values[6], values[7], values[8], values[1], values[2], values[3], values[4]) - else: - if values[0][1]=="1": - values.pop(0) # remove initial value - return beamSplitter(values[0], values[5], values[6], values[7], values[8], 1.0 - SIfloat(values[1]) - SIfloat(values[2]), values[1], values[3], values[4]) - else: - values.pop(0) # remove initial value - return beamSplitter(values[0], values[5], values[6], values[7], values[8], values[1], 1.0 - SIfloat(values[1]) - SIfloat(values[2]), values[3], values[4]) - - def getFinesseText(self): - rtn = [] - - rtn.append('bs {0} {1} {2} {3} {4} {5} {6} {7} {8}'.format( - self.name, self.R.value, self.T.value, self.phi.value, - self.alpha.value, self.nodes[0].name, - self.nodes[1].name, self.nodes[2].name, - self.nodes[3].name)) - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - - def getQGraphicsItem(self): - if self._svgItem == None: - # FIXME: make proper SVG component for beam splitter - self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])]) - - return self._svgItem - -class space(Component): - def __init__(self, name, node1, node2, L=0, n=1): - Component.__init__(self, name) - - self._requested_node_names.append(node1) - self._requested_node_names.append(node2) - self._QItem = None - self.__L = Param("L", self, SIfloat(L)) - self.__n = Param("n", self, SIfloat(n)) - - @property - def L(self): return self.__L - @L.setter - def L(self,value): self.__L,value = SIfloat(value) - @property - def n(self): return self.__n - @n.setter - def n(self,value): self.__n.value = SIfloat(value) - - @staticmethod - def parseFinesseText(text): - values = text.split(" ") - - if values[0] != "s": - raise exceptions.RuntimeError("'{0}' not a valid Finesse space command".format(text)) - - values.pop(0) # remove initial value - - if len(values) == 5: - return space(values[0], values[3], values[4], values[1], values[2]) - 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)) - - def getFinesseText(self): - rtn = [] - - if self.__n == 1: - rtn.append('s {0} {1} {2} {3}'.format(self.name, self.__L.value, self.nodes[0].name, self.nodes[1].name)) - else: - rtn.append('s {0} {1} {2} {3} {4}'.format(self.name, self.__L.value, self.__n.value, self.nodes[0].name, self.nodes[1].name)) - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - - def getQGraphicsItem(self): - if self._QItem == None: - self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) - - return self._QItem - -class grating(Component): - def __init__(self, name, node1, node2, node3 = None, node4 = None, n = 2, d = 0, eta_0 = 0, eta_1 = 0, eta_2 = 0, eta_3 = 0, rho_0 = 0, alpha = 0): # TODO: implement Rcx, Rcy and Rc - Component.__init__(self, name) - - self._requested_node_names.append(node1) - self._requested_node_names.append(node2) - - if n > 2: - if node3 != None: - self._requested_node_names.append(node3) - else: - raise exceptions.RuntimeError("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") - - if n > 4 or n < 2: - raise exceptions.RuntimeError("Grating must have between 2 and 4 ports") - - self.__n = n - self.__d = Param("d", self, SIfloat(d)) - self.__eta_0 = AttrParam("eta_0", self, SIfloat(eta_0)) - self.__eta_1 = AttrParam("eta_1", self, SIfloat(eta_1)) - self.__eta_2 = AttrParam("eta_2", self, SIfloat(eta_2)) - self.__eta_3 = AttrParam("eta_3", self, SIfloat(eta_3)) - self.__rho_0 = AttrParam("rho_0", self, SIfloat(rho_0)) - self.__alpha = AttrParam("alpha", self, SIfloat(alpha)) - - @property - def n(self): return Param('n', self.__n) - @n.setter - def n(self, value): - if value < 2 or value > 4: - raise exceptions.RuntimeError("Grating must have between 2 and 4 ports") - else: - self.__n = value - - @property - def d(self): return self.__d - @d.setter - def d(self, value): self.__d.value = SIfloat(value) - - @property - def eta_0(self): return self.__eta_0 - @eta_0.setter - def eta_0(self, value): self.__eta_0.value = SIfloat(value) - - @property - def eta_1(self): return self.__eta_1 - @eta_1.setter - def eta_1(self, value): self.__eta_1.value = SIfloat(value) - - @property - def eta_2(self): return self.__eta_2 - @eta_2.setter - def eta_2(self, value): self.__eta_2.value = SIfloat(value) - - @property - def eta_3(self): return self.__eta_3 - @eta_3.setter - def eta_3(self, value): self.__eta_3.value = SIfloat(value) - - @property - def rho_0(self): return self.__rho_0 - @rho_0.setter - def rho_0(self, value): self.__rho_0.value = SIfloat(value) - - @property - def alpha(self): return self.__alpha - @alpha.setter - def alpha(self, value): self.__alpha.value = SIfloat(value) - - @staticmethod - def parseFinesseText(text): - values = text.split(" ") - - if values[0][0 : 2] != "gr": - raise exceptions.RuntimeError("'{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") - else: - n = int(values[0][2]) - else: - n = 2 - - values.pop(0) # remove initial value - - if n == 2: - if len(values) != 4: - raise exceptions.RuntimeError("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") - - 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") - - return grating(values[0], values[2], values[3], values[4], values[5], n, values[1]) - - def getFinesseText(self): - rtn = [] - - if self.__n == 2: - rtn.append('gr{0} {1} {2} {3} {4}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name)) - elif self.__n == 3: - rtn.append('gr{0} {1} {2} {3} {4} {5}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name)) - else: - rtn.append('gr{0} {1} {2} {3} {4} {5} {6}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name, self.nodes[3].name)) - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - - def getQGraphicsItem(self): - if self._QItem == None: - self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make SVG graphic for grating - - return self._QItem - -class isolator(Component): - def __init__(self, name, node1, node2, S = 0): - Component.__init__(self, name) - - self._requested_node_names.append(node1) - self._requested_node_names.append(node2) - - self.__S = Param("S",self,SIfloat(S)) - - @property - def S(self): return self.__S - @S.setter - def S(self, value): self.__S.value = SIfloat(value) - - @staticmethod - def parseFinesseText(text): - values = text.split(" ") - - if values[0] != "isol": - raise exceptions.RuntimeError("'{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)) - - def getFinesseText(self): - rtn = ['isol {0} {1} {2} {3}'.format(self.name, self.S.value, self.nodes[0].name, self.nodes[1].name)] - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - - def getQGraphicsItem(self): - if self._QItem == None: - self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make isolator graphic - - return self._QItem - -class lens(Component): - def __init__(self, name, node1, node2, f): - Component.__init__(self, name) - - self._requested_node_names.append(node1) - self._requested_node_names.append(node2) - - self.__f = Param("f", self, SIfloat(f)) - - @property - def f(self): return self.__f - @f.setter - def f(self, value): self.__f.value = SIfloat(value) - - @staticmethod - def parseFinesseText(text): - values = text.split(" ") - - if values[0] != "lens": - raise exceptions.RuntimeError("'{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)) - - def getFinesseText(self): - rtn = ['lens {0} {1} {2} {3}'.format(self.name, self.f.value, self.nodes[0].name, self.nodes[1].name)] - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - - def getQGraphicsItem(self): - if self._QItem == None: - self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make lens graphic - - return self._QItem - -class modulator(Component): - def __init__(self, name, f, midx, order, modulation_type, node1, node2, phase=0): - Component.__init__(self, name) - - self._requested_node_names.append(node1) - self._requested_node_names.append(node2) - - self.__f = Param("f", self, SIfloat(f)) - self.__midx = Param("midx", self, SIfloat(midx)) - self.__phase = Param("phase", self, SIfloat(phase)) - self.__order = order - self.type = modulation_type - - - @property - def f(self): return self.__f - @f.setter - def f(self, value): self.__f.value = SIfloat(value) - - @property - def midx(self): return self.__midx - @midx.setter - def midx(self, value): self.__midx.value = SIfloat(value) - - @property - def phase(self): return self.__phase - @phase.setter - def phase(self, value): self.__phase.value = SIfloat(value) - - @property - def order(self): return self.__order - @order.setter - def order(self, value): - if order <= 1 and order > 6: - raise pkex.BasePyKatException("modulator order must be between 1 and 6") - - self.__order = int(value) - - @property - def type(self): return self.__type - @type.setter - def type(self, value): - if value != "am" and value != "pm": - raise pkex.BasePyKatException("Modulator type must be am (amplitude modulation) or pm (phase modulation)") - - self.__type = str(value) - - @staticmethod - def parseFinesseText(text): - v = text.split(" ") - - if v[0] != "mod": - raise exceptions.RuntimeError("'{0}' not a valid Finesse modulator command".format(text)) - - v.pop(0) # remove initial value - - if len(v) == 7: - return modulator(v[0], v[1], v[2], v[3], v[4], v[5], v[6]) - if len(v) == 8: - return modulator(v[0], v[1], v[2], v[3], v[4], v[6], v[7], phase=v[5]) - else: - raise pkex.BasePyKatException("Modulator Finesse code format incorrect '{0}'".format(text)) - - def getFinesseText(self): - rtn = ['mod {0} {1} {2} {3} {4} {5} {6} {7}'.format(self.name, self.f, self.midx, self.order, self.type, self.phase, self.nodes[0].name, self.nodes[1].name)] - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - - def getQGraphicsItem(self): - if self._QItem == None: - self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make lens graphic - - return self._QItem - -class laser(Component): - def __init__(self,name,node,P=1,f_offset=0,phase=0): - Component.__init__(self,name) - - self._requested_node_names.append(node) - - self.__power = Param("P", self, SIfloat(P)) - self.__f_offset = Param("f", self, SIfloat(f_offset)) - self.__phase = Param("phase", self, SIfloat(phase)) - self.__noise = AttrParam("noise", self, 0) - - @property - def power(self): return self.__power - @power.setter - def power(self,value): self.__power.value = float(value) - - @property - def f(self): return self.__f_offset - @f.setter - def f(self,value): self.__f_offset.value = float(value) - - @property - def phase(self): return self.__phase - @phase.setter - def phase(self,value): self.__phase.value = float(value) - - @staticmethod - def parseFinesseText(text): - values = text.split(" ") - - if values[0] != "l": - raise exceptions.RuntimeError("'{0}' not a valid Finesse laser command".format(text)) - - values.pop(0) # remove initial value - - if len(values) == 5: - return laser(values[0],values[4],P=values[1],f_offset=values[2],phase=values[3]) - elif len(values) == 4: - return laser(values[0],values[3],P=values[1],f_offset=values[2], phase=0) - else: - raise exceptions.FinesseParse("Laser Finesse code format incorrect '{0}'".format(text)) - - def getFinesseText(self): - rtn = ['l {0} {1} {2} {3} {4}'.format(self.name, self.__power.value, self.__f_offset.value, self.__phase.value, self.nodes[0].name)] - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - - def getQGraphicsItem(self): - if self._svgItem == None: - self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/laser.svg", self, [(65,25,self.nodes[0])]) - - return self._svgItem - +# -*- coding: utf-8 -*- +""" +Created on Mon Jan 28 11:10:01 2013 + +@author: Daniel +""" +import exceptions +import pykat.exceptions as pkex +import pykat +from pykat.node_network import * +from pykat.exceptions import * +import abc + +import pykat.gui.resources +import pykat.gui.graphics +from pykat.gui.graphics import * +from pykat.SIfloat import * +from pykat.param import Param, AttrParam + +next_component_id = 1 + +class NodeGaussSetter(object): + def __init__(self, component, node): + self.__comp = component + self.__node = node + + @property + def node(self): + return self.__node + + @property + def q(self): + return self.__node.qx + + @q.setter + def q(self, value): + self.__node.setGauss(self.__comp, complex(value)) + + @property + def qx(self): + return self.__node.qx + @qx.setter + def qx(self, value): + self.__node.setGauss(self.__comp, complex(value)) + + @property + def qy(self): + return self.__node.qy + @qy.setter + def qy(self, value): + self.__node.setGauss(self.__comp, self.qx, complex(value)) + +class Component(object): + __metaclass__ = abc.ABCMeta + + def __init__(self, name): + self.__name = name + self._svgItem = None + self._requested_node_names = [] + self._kat = None + self.tag = None + self._params = [] + + # store a unique ID for this component + global next_component_id + self.__id = next_component_id + next_component_id += 1 + + # This creates an instance specific class for the component + # this enables us to add properties to instances rather than + # all classes + cls = type(self) + self.__class__ = type(cls.__name__, (cls,), {}) + + def _register_param(self, param): + self._params.append(param) + + 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 + node_array is an array specific to this object which contains + references to the nodes that are attached to it. + """ + if self._kat != None: + raise pkex.BasePyKatException("Component has already been added to a finesse.kat object") + + self._kat = kat + + kat.nodes.registerComponentNodes(self, self._requested_node_names, self.__on_node_change) + + def __on_node_change(self): + # need to update the node gauss parameter setter members + self.__update_node_setters() + + def __update_node_setters(self): + # check if any node setters have already been added. If so we + # need to remove them. This function should get called if the nodes + # are updated, either by some function call or the GUI + key_rm = [k for k in self.__dict__ if k.startswith("__nodesetter_", 0, 13)] + + # now we have a list of which to remove + for key in key_rm: + ns = self.__dict__[key] + delattr(self, '__nodesetter_' + ns.node.name) + delattr(self.__class__, ns.node.name) + + for node in self.nodes: + if type(node) != pykat.node_network.DumpNode: + ns = NodeGaussSetter(self, node) + self.__add_node_setter(ns) + + def __add_node_setter(self, ns): + + if not isinstance(ns, NodeGaussSetter): + raise exceptions.ValueError("Argument is not of type NodeGaussSetter") + + name = ns.node.name + fget = lambda self: self.__get_node_setter(name) + + setattr(self.__class__, name, property(fget)) + setattr(self, '__nodesetter_' + name, ns) + + def __get_node_setter(self, name): + return getattr(self, '__nodesetter_' + name) + + @staticmethod + @abc.abstractmethod + def parseFinesseText(text): + """Parses Finesse syntax""" + raise NotImplementedError("This function is not implemented") + + @abc.abstractmethod + def getFinesseText(self): + """ Base class for individual Finesse optical components """ + raise NotImplementedError("This function is not implemented") + + @abc.abstractmethod + def getQGraphicsItem(self): + return None + + @property + def nodes(self): return self._kat.nodes.getComponentNodes(self) + + @property + def name(self): return self.__name + + @property + def id(self): return self.__id + + def __str__(self): return self.name + +class AbstractMirrorComponent(Component): + __metaclass__ = abc.ABCMeta + + def __init__(self, name, R=0, T=0, phi=0, Rcx=0, Rcy=0, xbeta=0, ybeta=0, mass=0, r_ap=0): + super(AbstractMirrorComponent, self).__init__(name) + + self.__R = Param("R", self, SIfloat(R)) + self.__T = Param("T", self, SIfloat(T)) + self.__phi = Param("phi", self, SIfloat(phi)) + self.__Rcx = AttrParam("Rcx", self, SIfloat(Rcx)) + self.__Rcy = AttrParam("Rcy", self, SIfloat(Rcy)) + self.__xbeta = AttrParam("xbeta", self, SIfloat(xbeta)) + self.__ybeta = AttrParam("ybeta", self, SIfloat(ybeta)) + self.__mass = AttrParam("mass", self, SIfloat(mass)) + self.__r_ap = AttrParam("r_ap", self, SIfloat(r_ap)) + + @property + def r_ap(self): return self.__r_ap + @r_ap.setter + def r_ap(self,value): self.__r_ap.value = SIfloat(value) + + @property + def mass(self): return self.__mass + @mass.setter + def mass(self,value): self.__mass.value = SIfloat(value) + + @property + def R(self): return self.__R + @R.setter + def R(self,value): self.__R.value = SIfloat(value) + + @property + def T(self): return self.__T + @T.setter + def T(self,value): self.__T.value = SIfloat(value) + + @property + def phi(self): return self.__phi + @phi.setter + def phi(self,value): self.__phi.value = SIfloat(value) + + @property + def Rcx(self): return self.__Rcx + @Rcx.setter + def Rcx(self,value): self.__Rcx.value = SIfloat(value) + + @property + def Rcy(self): return self.__Rcy + @Rcy.setter + def Rcy(self,value): self.__Rcy.value = SIfloat(value) + + @property + def xbeta(self): return self.__xbeta + @xbeta.setter + def xbeta(self,value): self.__xbeta.value = SIfloat(value) + + @property + def ybeta(self): return self.__ybeta + @ybeta.setter + def ybeta(self,value): self.__ybeta.value = SIfloat(value) + + @property + def Rc(self): + if self.Rcx == self.Rcy: + return self.Rcx + else: + return [self.Rcx, self.Rcy] + + @Rc.setter + def Rc(self,value): + self.Rcx.value = SIfloat(value) + self.Rcy.value = SIfloat(value) + +class mirror(AbstractMirrorComponent): + def __init__(self,name,node1,node2,R=0,T=0,phi=0,Rcx=0,Rcy=0,xbeta=0,ybeta=0,mass=0, r_ap=0): + super(mirror, self).__init__(name, R, T, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap) + + self._requested_node_names.append(node1) + self._requested_node_names.append(node2) + + @staticmethod + def parseFinesseText(text): + 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)) + + if len(values) != 7: + raise exceptions.RuntimeError("Mirror Finesse code format incorrect '{0}'".format(text)) + + if len(values[0])==1: + values.pop(0) # remove initial value + return mirror(values[0], values[4], values[5], R=values[1], T=values[2], phi=values[3]) + else: + if values[0][1]=="1": + values.pop(0) # remove initial value + return mirror(values[0], values[4], values[5], R=1.0-SIfloat(values[1])-SIfloat(values[2]), T=values[1], phi=values[3]) + else: + values.pop(0) # remove initial value + return mirror(values[0], values[4], values[5], R=values[1], T=1.0-SIfloat(values[1])-SIfloat(values[2]), phi=values[3]) + + def getFinesseText(self): + rtn = [] + + rtn.append('m {0} {1} {2} {3} {4} {5}'.format( + self.name, self.R.value, self.T.value, self.phi.value, + self.nodes[0].name, self.nodes[1].name)) + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + def getQGraphicsItem(self): + if self._svgItem == None: + self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])]) + + return self._svgItem + +class beamSplitter(AbstractMirrorComponent): + def __init__(self, name, node1, node2, node3, node4, R = 0, T = 0, phi = 0, alpha = 0, Rcx = 0, Rcy = 0, xbeta = 0, ybeta = 0, mass = 0, r_ap = 0): + super(beamSplitter, self).__init__(name, R, T, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap) + + self._requested_node_names.append(node1) + self._requested_node_names.append(node2) + self._requested_node_names.append(node3) + self._requested_node_names.append(node4) + + self.__alpha = AttrParam("alpha", self, SIfloat(alpha)) + + @property + def alpha(self): return self.__alpha + @alpha.setter + def alpha(self,value): self.__alpha.value = SIfloat(value) + + @staticmethod + def parseFinesseText(text): + 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)) + + if len(values) != 10: + raise exceptions.RuntimeError("Beam splitter Finesse code format incorrect '{0}'".format(text)) + + if len(values[0])==1: + values.pop(0) # remove initial value + return beamSplitter(values[0], values[5], values[6], values[7], values[8], values[1], values[2], values[3], values[4]) + else: + if values[0][1]=="1": + values.pop(0) # remove initial value + return beamSplitter(values[0], values[5], values[6], values[7], values[8], 1.0 - SIfloat(values[1]) - SIfloat(values[2]), values[1], values[3], values[4]) + else: + values.pop(0) # remove initial value + return beamSplitter(values[0], values[5], values[6], values[7], values[8], values[1], 1.0 - SIfloat(values[1]) - SIfloat(values[2]), values[3], values[4]) + + def getFinesseText(self): + rtn = [] + + rtn.append('bs {0} {1} {2} {3} {4} {5} {6} {7} {8}'.format( + self.name, self.R.value, self.T.value, self.phi.value, + self.alpha.value, self.nodes[0].name, + self.nodes[1].name, self.nodes[2].name, + self.nodes[3].name)) + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + def getQGraphicsItem(self): + if self._svgItem == None: + # FIXME: make proper SVG component for beam splitter + self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])]) + + return self._svgItem + +class space(Component): + def __init__(self, name, node1, node2, L=0, n=1): + Component.__init__(self, name) + + self._requested_node_names.append(node1) + self._requested_node_names.append(node2) + self._QItem = None + self.__L = Param("L", self, SIfloat(L)) + self.__n = Param("n", self, SIfloat(n)) + + @property + def L(self): return self.__L + @L.setter + def L(self,value): self.__L,value = SIfloat(value) + @property + def n(self): return self.__n + @n.setter + def n(self,value): self.__n.value = SIfloat(value) + + @staticmethod + def parseFinesseText(text): + values = text.split(" ") + + if values[0] != "s": + raise exceptions.RuntimeError("'{0}' not a valid Finesse space command".format(text)) + + values.pop(0) # remove initial value + + if len(values) == 5: + return space(values[0], values[3], values[4], values[1], values[2]) + 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)) + + def getFinesseText(self): + rtn = [] + + if self.__n == 1: + rtn.append('s {0} {1} {2} {3}'.format(self.name, self.__L.value, self.nodes[0].name, self.nodes[1].name)) + else: + rtn.append('s {0} {1} {2} {3} {4}'.format(self.name, self.__L.value, self.__n.value, self.nodes[0].name, self.nodes[1].name)) + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + def getQGraphicsItem(self): + if self._QItem == None: + self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) + + return self._QItem + +class grating(Component): + def __init__(self, name, node1, node2, node3 = None, node4 = None, n = 2, d = 0, eta_0 = 0, eta_1 = 0, eta_2 = 0, eta_3 = 0, rho_0 = 0, alpha = 0): # TODO: implement Rcx, Rcy and Rc + Component.__init__(self, name) + + self._requested_node_names.append(node1) + self._requested_node_names.append(node2) + + if n > 2: + if node3 != None: + self._requested_node_names.append(node3) + else: + raise exceptions.RuntimeError("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") + + if n > 4 or n < 2: + raise exceptions.RuntimeError("Grating must have between 2 and 4 ports") + + self.__n = n + self.__d = Param("d", self, SIfloat(d)) + self.__eta_0 = AttrParam("eta_0", self, SIfloat(eta_0)) + self.__eta_1 = AttrParam("eta_1", self, SIfloat(eta_1)) + self.__eta_2 = AttrParam("eta_2", self, SIfloat(eta_2)) + self.__eta_3 = AttrParam("eta_3", self, SIfloat(eta_3)) + self.__rho_0 = AttrParam("rho_0", self, SIfloat(rho_0)) + self.__alpha = AttrParam("alpha", self, SIfloat(alpha)) + + @property + def n(self): return Param('n', self.__n) + @n.setter + def n(self, value): + if value < 2 or value > 4: + raise exceptions.RuntimeError("Grating must have between 2 and 4 ports") + else: + self.__n = value + + @property + def d(self): return self.__d + @d.setter + def d(self, value): self.__d.value = SIfloat(value) + + @property + def eta_0(self): return self.__eta_0 + @eta_0.setter + def eta_0(self, value): self.__eta_0.value = SIfloat(value) + + @property + def eta_1(self): return self.__eta_1 + @eta_1.setter + def eta_1(self, value): self.__eta_1.value = SIfloat(value) + + @property + def eta_2(self): return self.__eta_2 + @eta_2.setter + def eta_2(self, value): self.__eta_2.value = SIfloat(value) + + @property + def eta_3(self): return self.__eta_3 + @eta_3.setter + def eta_3(self, value): self.__eta_3.value = SIfloat(value) + + @property + def rho_0(self): return self.__rho_0 + @rho_0.setter + def rho_0(self, value): self.__rho_0.value = SIfloat(value) + + @property + def alpha(self): return self.__alpha + @alpha.setter + def alpha(self, value): self.__alpha.value = SIfloat(value) + + @staticmethod + def parseFinesseText(text): + values = text.split(" ") + + if values[0][0 : 2] != "gr": + raise exceptions.RuntimeError("'{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") + else: + n = int(values[0][2]) + else: + n = 2 + + values.pop(0) # remove initial value + + if n == 2: + if len(values) != 4: + raise exceptions.RuntimeError("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") + + 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") + + return grating(values[0], values[2], values[3], values[4], values[5], n, values[1]) + + def getFinesseText(self): + rtn = [] + + if self.__n == 2: + rtn.append('gr{0} {1} {2} {3} {4}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name)) + elif self.__n == 3: + rtn.append('gr{0} {1} {2} {3} {4} {5}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name)) + else: + rtn.append('gr{0} {1} {2} {3} {4} {5} {6}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name, self.nodes[3].name)) + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + def getQGraphicsItem(self): + if self._QItem == None: + self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make SVG graphic for grating + + return self._QItem + +class isolator(Component): + def __init__(self, name, node1, node2, S = 0): + Component.__init__(self, name) + + self._requested_node_names.append(node1) + self._requested_node_names.append(node2) + + self.__S = Param("S",self,SIfloat(S)) + + @property + def S(self): return self.__S + @S.setter + def S(self, value): self.__S.value = SIfloat(value) + + @staticmethod + def parseFinesseText(text): + values = text.split(" ") + + if values[0] != "isol": + raise exceptions.RuntimeError("'{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)) + + def getFinesseText(self): + rtn = ['isol {0} {1} {2} {3}'.format(self.name, self.S.value, self.nodes[0].name, self.nodes[1].name)] + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + def getQGraphicsItem(self): + if self._QItem == None: + self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make isolator graphic + + return self._QItem + +class lens(Component): + def __init__(self, name, node1, node2, f): + Component.__init__(self, name) + + self._requested_node_names.append(node1) + self._requested_node_names.append(node2) + + self.__f = Param("f", self, SIfloat(f)) + + @property + def f(self): return self.__f + @f.setter + def f(self, value): self.__f.value = SIfloat(value) + + @staticmethod + def parseFinesseText(text): + values = text.split(" ") + + if values[0] != "lens": + raise exceptions.RuntimeError("'{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)) + + def getFinesseText(self): + rtn = ['lens {0} {1} {2} {3}'.format(self.name, self.f.value, self.nodes[0].name, self.nodes[1].name)] + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + def getQGraphicsItem(self): + if self._QItem == None: + self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make lens graphic + + return self._QItem + +class modulator(Component): + def __init__(self, name, f, midx, order, modulation_type, node1, node2, phase=0): + Component.__init__(self, name) + + self._requested_node_names.append(node1) + self._requested_node_names.append(node2) + + self.__f = Param("f", self, SIfloat(f)) + self.__midx = Param("midx", self, SIfloat(midx)) + self.__phase = Param("phase", self, SIfloat(phase)) + self.__order = order + self.type = modulation_type + + + @property + def f(self): return self.__f + @f.setter + def f(self, value): self.__f.value = SIfloat(value) + + @property + def midx(self): return self.__midx + @midx.setter + def midx(self, value): self.__midx.value = SIfloat(value) + + @property + def phase(self): return self.__phase + @phase.setter + def phase(self, value): self.__phase.value = SIfloat(value) + + @property + def order(self): return self.__order + @order.setter + def order(self, value): + if order <= 1 and order > 6: + raise pkex.BasePyKatException("modulator order must be between 1 and 6") + + self.__order = int(value) + + @property + def type(self): return self.__type + @type.setter + def type(self, value): + if value != "am" and value != "pm": + raise pkex.BasePyKatException("Modulator type must be am (amplitude modulation) or pm (phase modulation)") + + self.__type = str(value) + + @staticmethod + def parseFinesseText(text): + v = text.split(" ") + + if v[0] != "mod": + raise exceptions.RuntimeError("'{0}' not a valid Finesse modulator command".format(text)) + + v.pop(0) # remove initial value + + if len(v) == 7: + return modulator(v[0], v[1], v[2], v[3], v[4], v[5], v[6]) + if len(v) == 8: + return modulator(v[0], v[1], v[2], v[3], v[4], v[6], v[7], phase=v[5]) + else: + raise pkex.BasePyKatException("Modulator Finesse code format incorrect '{0}'".format(text)) + + def getFinesseText(self): + rtn = ['mod {0} {1} {2} {3} {4} {5} {6} {7}'.format(self.name, self.f, self.midx, self.order, self.type, self.phase, self.nodes[0].name, self.nodes[1].name)] + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + def getQGraphicsItem(self): + if self._QItem == None: + self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make lens graphic + + return self._QItem + +class laser(Component): + def __init__(self,name,node,P=1,f_offset=0,phase=0): + Component.__init__(self,name) + + self._requested_node_names.append(node) + + self.__power = Param("P", self, SIfloat(P)) + self.__f_offset = Param("f", self, SIfloat(f_offset)) + self.__phase = Param("phase", self, SIfloat(phase)) + self.__noise = AttrParam("noise", self, 0) + + @property + def power(self): return self.__power + @power.setter + def power(self,value): self.__power.value = float(value) + + @property + def f(self): return self.__f_offset + @f.setter + def f(self,value): self.__f_offset.value = float(value) + + @property + def phase(self): return self.__phase + @phase.setter + def phase(self,value): self.__phase.value = float(value) + + @staticmethod + def parseFinesseText(text): + values = text.split(" ") + + if values[0] != "l": + raise exceptions.RuntimeError("'{0}' not a valid Finesse laser command".format(text)) + + values.pop(0) # remove initial value + + if len(values) == 5: + return laser(values[0],values[4],P=values[1],f_offset=values[2],phase=values[3]) + elif len(values) == 4: + return laser(values[0],values[3],P=values[1],f_offset=values[2], phase=0) + else: + raise exceptions.FinesseParse("Laser Finesse code format incorrect '{0}'".format(text)) + + def getFinesseText(self): + rtn = ['l {0} {1} {2} {3} {4}'.format(self.name, self.__power.value, self.__f_offset.value, self.__phase.value, self.nodes[0].name)] + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + + def getQGraphicsItem(self): + if self._svgItem == None: + 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 30e46004ead75389d95c476d32db7ee9d524200b..67434f87f3b98f988f6de5e57479c5230a720380 100644 --- a/pykat/detectors.py +++ b/pykat/detectors.py @@ -1,297 +1,297 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Feb 01 09:09:10 2013 - -@author: Daniel -""" -import exceptions -import pykat.gui.resources - -from pykat.utils import * -from pykat.gui.graphics import * -from pykat.node_network import * - -class Detector(object) : - def __init__(self, name,node): - self.__name = name - self._svgItem = None - self._kat = None - self.noplot = False - self.enabled = True - self.tag = None - self.__node = None - self._params = [] - - if node.find('*'): - self._alternate_beam = True - node.replace('*','') - - self.__requested_node = node - - def _register_param(self, param): - self._params.append(param) - - def _on_kat_add(self, kat): - self.__node = kat.nodes.createNode(self.__requested_node) - - @staticmethod - def parseFinesseText(text): - 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 - - @property - def node(self): return self.__node - @node.setter - def node(self, value): - if value in self._kat.nodes: - self.__node = self._kat.nodes[value] - else: - raise pkex.BasePyKatException("There is no node called " + value) - - @property - def name(self): return self.__name - - def __str__(self): return self.name - -class pd(Detector): - - def __init__(self, name, num_demods, node_name, senstype=None, alternate_beam=False, **kwargs): - Detector.__init__(self, name, node_name) - - self.__num_demods = num_demods - self.__senstype = senstype - self.__alternate_beam = alternate_beam - # create the parameters for all 5 demodulations regardless - # of how many the user specifies. Later we add properties to - # those which correspond to the number of demodulations - - self.__f1 = Param("f1", self, 0) - self.__f2 = Param("f2", self, 0) - self.__f3 = Param("f3", self, 0) - self.__f4 = Param("f4", self, 0) - self.__f5 = Param("f5", self, 0) - - self.__phi1 = Param("phi1", self, None) - self.__phi2 = Param("phi2", self, None) - self.__phi3 = Param("phi3", self, None) - self.__phi4 = Param("phi4", self, None) - self.__phi5 = Param("phi5", self, None) - - # define new class for assigning new attributes - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) - - self.__set_demod_attrs() - - @property - def senstype(self): return self.__senstype - @senstype.setter - def senstype(self,value): - if value == "": value = None - - if value != "S" and value != "N" and value != None: - raise pkex.BasePyKatException("Photodiode sensitivity type can either be 'N', 'S' or None.") - - self.__senstype = value - - @property - def num_demods(self): return self.__num_demods - @num_demods.setter - def num_demods(self, value): - if value < 0 or value > 5: - raise pkex.BasePyKatException("Number of demodulations must be between 0 and 5") - - self.__num_demods = value - self.__set_demod_attrs() - - def __get_fphi(self, name): - return getattr(self, '_'+ self.__class__.__name__ +'__' + name) - - def __set_f(self, num, value): - setattr(self, '_'+ self.__class__.__name__ +'__f' + name, float(value)) - - def __set_phi(self, num, value): - if value == None and num != self.num_demods: - # check if we are setting no phase that this is only on the last - # demodulation phase. - raise pkex.BasePyKatException("Only last demodulation phase can be set to None") - elif isinstance(value, str) and not isinstance(value,float) and value.lower() != "max": - raise pkex.BasePyKatException("Demodulation phase can only be set to a 'max' or a number (or None if the last demodulation phase)") - - setattr(self, '_'+ self.__class__.__name__ +'__phi' + name, value) - - def __set_demod_attrs(self): - """ - For the set number of demodulations the correct number of - Parameters are created. - """ - - # if there are demodulations present then we want to add - # the various parameters so they are available for users - # to play with. - if self.__num_demods > 0: - for i in range(1,6): - name = str(i) - if i <= self.num_demods: - if not hasattr(self, "f"+name): - setattr(self.__class__, "f"+name, property(fget=lambda self, i=i: self.__get_fphi('f'+str(i)), fset=lambda self, value, i=i: self.__set_f(str(i), value))) - - if not hasattr(self, "phi"+name): - setattr(self.__class__, "phi"+name, property(fget=lambda self, i=i: self.__get_fphi('phi'+str(i)), fset=lambda self, value, i=i: self.__set_phi(str(i), value))) - else: - if hasattr(self, "f"+name): - delattr(self.__class__, "f"+name) - if hasattr(self, "phi"+name): - delattr(self.__class__, "phi"+name) - else: - return - - def getFinesseText(self) : - rtn = [] - - if self.enabled: - alt_str = "" - fphi_str = "" - - if self.__alternate_beam: - alt_str = "*" - - for n in range(1, 1+self.num_demods): - fphi_str += str(self.__getattribute__("f"+str(n))) - phi_val = self.__getattribute__("phi"+str(n)) - - if phi_val != None: - fphi_str += " " + str(phi_val) - - senstype = self.senstype - - if senstype == None: - senstype = "" - - rtn.append("pd{0}{1} {2} {3} {4}{5}".format(senstype, self.num_demods, self.name, fphi_str, self.node.name, alt_str)) - - for p in self._params: - rtn.extend(p.getFinesseText()) - - return rtn - -class photodiode(Detector): - - class __F(list): - def __init__(self, values=None): - if values==None: - values = [] - list.__init__(self,[SIfloat(value) for value in values]) - - class __Phi(list): - def __convertValue(self, value): - if value=="max": - return value - else: - return SIfloat(value) - - def __init__(self, values=None): - if values==None: - values = [] - list.__init__(self,[self.__convertValue(value) for value in values]) - - def __getitem__(self, key): # probably not needed - if list.__getitem__(self,key)=="max": - return list.__getitem__(self,key) - else: - return float(list.__getitem__(self,key)) - - @property - def f(self): return self.__f - - @property - def phi(self): return self.__phi - - def __init__(self, name, node, senstype="", num_demods=0, demods=[]): - Detector.__init__(self, name, node) - - if num_demods>2: - raise NotImplementedError("pd with more than two demodulations not implemented yet") - - self.num_demods = num_demods - self.senstype = senstype - - # every second element into f (starting at 1) - self.__f = self.__F(demods[::2]) - - # Every second element into phi (starting at 2) - self.__phi = self.__Phi(demods[1::2]) - - @staticmethod - 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(values[0])==2: - __num_demods=0 - __senstype="" - elif len(values[0])==3 or len(values[0])==4: - if values[0][2]=="S": - __senstype="S" - elif values[0][2]=="N": - __senstype="N" - else: - try: - __num_demods=int(values[0][2]) - __senstype="" - except ValueError: - raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text)) - if len(values[0])==4: - try: - __num_demods=int(values[0][3]) - 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(values[0], values[-1], __senstype, __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: - rtn = [] - __f_phi=range(len(self.f)+len(self.phi)) - __f_phi[::2]=self.f - __f_phi[1::2]=self.phi - __f_phi_str = " ".join(map(str, __f_phi)) - - if self._alternate_beam: - rtn.append("pd{0}{1} {2} {3} {4}".format(self.senstype, self.num_demods, self.name, __f_phi_str, self.node.name)) - else: - rtn.append("pd{0}{1} {2} {3} {4}*".format(self.senstype, self.num_demods, self.name, __f_phi_str, self.node.name)) - - if self.noplot: - rtn.append("noplot {0}".format(self.name)) - - return rtn - else: - return None - - def getQGraphicsItem(self): - if self._svgItem == None: - self._svgItem = ComponentQGraphicsItem(":/resources/photodiode_red.svg",self,[(-5,11,self.node)]) - - return self._svgItem - +# -*- coding: utf-8 -*- +""" +Created on Fri Feb 01 09:09:10 2013 + +@author: Daniel +""" +import exceptions +import pykat.gui.resources + +from pykat.utils import * +from pykat.gui.graphics import * +from pykat.node_network import * + +class Detector(object) : + def __init__(self, name,node): + self.__name = name + self._svgItem = None + self._kat = None + self.noplot = False + self.enabled = True + self.tag = None + self.__node = None + self._params = [] + + if node.find('*'): + self._alternate_beam = True + node.replace('*','') + + self.__requested_node = node + + def _register_param(self, param): + self._params.append(param) + + def _on_kat_add(self, kat): + self.__node = kat.nodes.createNode(self.__requested_node) + + @staticmethod + def parseFinesseText(text): + 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 + + @property + def node(self): return self.__node + @node.setter + def node(self, value): + if value in self._kat.nodes: + self.__node = self._kat.nodes[value] + else: + raise pkex.BasePyKatException("There is no node called " + value) + + @property + def name(self): return self.__name + + def __str__(self): return self.name + +class pd(Detector): + + def __init__(self, name, num_demods, node_name, senstype=None, alternate_beam=False, **kwargs): + Detector.__init__(self, name, node_name) + + self.__num_demods = num_demods + self.__senstype = senstype + self.__alternate_beam = alternate_beam + # create the parameters for all 5 demodulations regardless + # of how many the user specifies. Later we add properties to + # those which correspond to the number of demodulations + + self.__f1 = Param("f1", self, 0) + self.__f2 = Param("f2", self, 0) + self.__f3 = Param("f3", self, 0) + self.__f4 = Param("f4", self, 0) + self.__f5 = Param("f5", self, 0) + + self.__phi1 = Param("phi1", self, None) + self.__phi2 = Param("phi2", self, None) + self.__phi3 = Param("phi3", self, None) + self.__phi4 = Param("phi4", self, None) + self.__phi5 = Param("phi5", self, None) + + # define new class for assigning new attributes + cls = type(self) + self.__class__ = type(cls.__name__, (cls,), {}) + + self.__set_demod_attrs() + + @property + def senstype(self): return self.__senstype + @senstype.setter + def senstype(self,value): + if value == "": value = None + + if value != "S" and value != "N" and value != None: + raise pkex.BasePyKatException("Photodiode sensitivity type can either be 'N', 'S' or None.") + + self.__senstype = value + + @property + def num_demods(self): return self.__num_demods + @num_demods.setter + def num_demods(self, value): + if value < 0 or value > 5: + raise pkex.BasePyKatException("Number of demodulations must be between 0 and 5") + + self.__num_demods = value + self.__set_demod_attrs() + + def __get_fphi(self, name): + return getattr(self, '_'+ self.__class__.__name__ +'__' + name) + + def __set_f(self, num, value): + setattr(self, '_'+ self.__class__.__name__ +'__f' + name, float(value)) + + def __set_phi(self, num, value): + if value == None and num != self.num_demods: + # check if we are setting no phase that this is only on the last + # demodulation phase. + raise pkex.BasePyKatException("Only last demodulation phase can be set to None") + elif isinstance(value, str) and not isinstance(value,float) and value.lower() != "max": + raise pkex.BasePyKatException("Demodulation phase can only be set to a 'max' or a number (or None if the last demodulation phase)") + + setattr(self, '_'+ self.__class__.__name__ +'__phi' + name, value) + + def __set_demod_attrs(self): + """ + For the set number of demodulations the correct number of + Parameters are created. + """ + + # if there are demodulations present then we want to add + # the various parameters so they are available for users + # to play with. + if self.__num_demods > 0: + for i in range(1,6): + name = str(i) + if i <= self.num_demods: + if not hasattr(self, "f"+name): + setattr(self.__class__, "f"+name, property(fget=lambda self, i=i: self.__get_fphi('f'+str(i)), fset=lambda self, value, i=i: self.__set_f(str(i), value))) + + if not hasattr(self, "phi"+name): + setattr(self.__class__, "phi"+name, property(fget=lambda self, i=i: self.__get_fphi('phi'+str(i)), fset=lambda self, value, i=i: self.__set_phi(str(i), value))) + else: + if hasattr(self, "f"+name): + delattr(self.__class__, "f"+name) + if hasattr(self, "phi"+name): + delattr(self.__class__, "phi"+name) + else: + return + + def getFinesseText(self) : + rtn = [] + + if self.enabled: + alt_str = "" + fphi_str = "" + + if self.__alternate_beam: + alt_str = "*" + + for n in range(1, 1+self.num_demods): + fphi_str += str(self.__getattribute__("f"+str(n))) + phi_val = self.__getattribute__("phi"+str(n)) + + if phi_val != None: + fphi_str += " " + str(phi_val) + + senstype = self.senstype + + if senstype == None: + senstype = "" + + rtn.append("pd{0}{1} {2} {3} {4}{5}".format(senstype, self.num_demods, self.name, fphi_str, self.node.name, alt_str)) + + for p in self._params: + rtn.extend(p.getFinesseText()) + + return rtn + +class photodiode(Detector): + + class __F(list): + def __init__(self, values=None): + if values==None: + values = [] + list.__init__(self,[SIfloat(value) for value in values]) + + class __Phi(list): + def __convertValue(self, value): + if value=="max": + return value + else: + return SIfloat(value) + + def __init__(self, values=None): + if values==None: + values = [] + list.__init__(self,[self.__convertValue(value) for value in values]) + + def __getitem__(self, key): # probably not needed + if list.__getitem__(self,key)=="max": + return list.__getitem__(self,key) + else: + return float(list.__getitem__(self,key)) + + @property + def f(self): return self.__f + + @property + def phi(self): return self.__phi + + def __init__(self, name, node, senstype="", num_demods=0, demods=[]): + Detector.__init__(self, name, node) + + if num_demods>2: + raise NotImplementedError("pd with more than two demodulations not implemented yet") + + self.num_demods = num_demods + self.senstype = senstype + + # every second element into f (starting at 1) + self.__f = self.__F(demods[::2]) + + # Every second element into phi (starting at 2) + self.__phi = self.__Phi(demods[1::2]) + + @staticmethod + 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(values[0])==2: + __num_demods=0 + __senstype="" + elif len(values[0])==3 or len(values[0])==4: + if values[0][2]=="S": + __senstype="S" + elif values[0][2]=="N": + __senstype="N" + else: + try: + __num_demods=int(values[0][2]) + __senstype="" + except ValueError: + raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text)) + if len(values[0])==4: + try: + __num_demods=int(values[0][3]) + 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(values[0], values[-1], __senstype, __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: + rtn = [] + __f_phi=range(len(self.f)+len(self.phi)) + __f_phi[::2]=self.f + __f_phi[1::2]=self.phi + __f_phi_str = " ".join(map(str, __f_phi)) + + if self._alternate_beam: + rtn.append("pd{0}{1} {2} {3} {4}".format(self.senstype, self.num_demods, self.name, __f_phi_str, self.node.name)) + else: + rtn.append("pd{0}{1} {2} {3} {4}*".format(self.senstype, self.num_demods, self.name, __f_phi_str, self.node.name)) + + if self.noplot: + rtn.append("noplot {0}".format(self.name)) + + return rtn + else: + return None + + def getQGraphicsItem(self): + if self._svgItem == None: + self._svgItem = ComponentQGraphicsItem(":/resources/photodiode_red.svg",self,[(-5,11,self.node)]) + + return self._svgItem + diff --git a/pykat/exceptions.py b/pykat/exceptions.py index 2e3d0168e5687ba489f91316cb0124af6fee9e12..e268327a669ddbc43dbde8d72046f3dcd6f1c967 100644 --- a/pykat/exceptions.py +++ b/pykat/exceptions.py @@ -1,30 +1,30 @@ -import exceptions - -class BasePyKatException(Exception): - def __init__(self, msg): - self.__msg = msg - - def __str__(self): - return self.__msg - -class FinesseParse(BasePyKatException) : - def __init__(self, msg): - BasePyKatException.__init__(self, "Error parsing Finesse input\n{0}".format(msg)) - -class MissingFinesseEnvVar(BasePyKatException) : - def __init__(self): - BasePyKatException.__init__(self, "The environment variable FINESSE_DIR was not defined") - -class MissingFinesse(BasePyKatException) : - def __init__(self): - BasePyKatException.__init__(self, "Could not find the finesse executable 'kat' in '{0}'," \ - "or you do not have the permissions to run it." \ - .format(os.environ.get('FINESSE_DIR'))) - -class FinesseRunError(BasePyKatException) : - def __init__(self, err, kat): - self.__err = err - self.__kat = kat - - BasePyKatException.__init__(self, "Finesse returned an error running {1}: {0}".format(self.__err, self.__kat)) - +import exceptions + +class BasePyKatException(Exception): + def __init__(self, msg): + self.__msg = msg + + def __str__(self): + return self.__msg + +class FinesseParse(BasePyKatException) : + def __init__(self, msg): + BasePyKatException.__init__(self, "Error parsing Finesse input\n{0}".format(msg)) + +class MissingFinesseEnvVar(BasePyKatException) : + def __init__(self): + BasePyKatException.__init__(self, "The environment variable FINESSE_DIR was not defined") + +class MissingFinesse(BasePyKatException) : + def __init__(self): + BasePyKatException.__init__(self, "Could not find the finesse executable 'kat' in '{0}'," \ + "or you do not have the permissions to run it." \ + .format(os.environ.get('FINESSE_DIR'))) + +class FinesseRunError(BasePyKatException) : + def __init__(self, err, kat): + self.__err = err + self.__kat = kat + + BasePyKatException.__init__(self, "Finesse returned an error running {1}: {0}".format(self.__err, self.__kat)) + diff --git a/pykat/finesse.py b/pykat/finesse.py index 3930e681795583f3ce3f2eaf72912ca3e9ec56ed..6fac5751abae974f5b7102debfc30907de331709 100644 --- a/pykat/finesse.py +++ b/pykat/finesse.py @@ -1,725 +1,725 @@ -# -*- coding: utf-8 -*- -""" -Created on Sun Jan 27 09:56:53 2013 - -PyKat - Python interface and wrapper for FINESSE -Copyright (C) 2013 Daniel David Brown - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Contact at ddb@star.sr.bham.ac.uk - -@author: Daniel Brown -""" -import sys -import os -import subprocess -import tempfile -import numpy as np -import datetime -import pickle -import pykat -import warnings -import re - -from pykat.node_network import NodeNetwork -from pykat.detectors import Detector -from pykat.components import Component -from pykat.commands import Command, xaxis -from pykat.gui.gui import pyKatGUI - -import pykat.exceptions as pkex - -from PyQt4.QtCore import QCoreApplication -from PyQt4.QtGui import QApplication - -NO_GUI = False -NO_BLOCK = "NO_BLOCK" - -class katRun(object): - def __init__(self): - self.runDateTime = datetime.datetime.now() - self.x = None - self.y = None - self.xlabel = None - self.ylabels = None - self.katScript = None - self.katVersion = None - - def saveKatRun(self, filename): - with open(filename,'w') as outfile: - pickle.dump(self, outfile) - - @staticmethod - def loadKatRun(filename): - with open(filename,'r') as infile: - return pickle.load(infile) - - def get(self, value): return self[value] - - def __getitem__(self, value): - if value in self.ylabels: - idx = self.ylabels.index(value) - if len(self.y.shape) == 1: - return self.y - else: - return self.y[:, idx] - else: - raise pkex.BasePyKatException("No output by the name {0} found".format(value)) - -class katRun2D(object): - def __init__(self): - self.runDateTime = datetime.datetime.now() - self.x = None - self.y = None - self.z = None - self.xlabel = None - self.ylabel = None - self.zlabels = None - self.katScript = None - self.katVersion = None - - def saveKatRun(self, filename): - with open(filename,'w') as outfile: - pickle.dump(self, outfile) - - @staticmethod - def loadKatRun(filename): - with open(filename,'r') as infile: - return pickle.load(infile) - - 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)] - - if len(idx) > 0: - return self.z[idx].squeeze() - else: - raise pkex.BasePyKatException("No output by the name {0} found".format(str(value))) - -class Block: - def __init__(self, name): - self.__name = name - self.contents = [] # List of objects and strings of finesse code - self.enabled = True - - @property - def name(self): return self.__name - -class kat(object): - - def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None): - - self.scene = None # scene object for GUI - self.verbose = True - self.__blocks = {} # dictionary of blocks that are used - self.__components = {} # dictionary of optical components - self.__detectors = {} # dictionary of detectors - self.__commands = {} # dictionary of commands - self.__extra_lines = [] # an array of strings which are just normal finesse code to include when running - self.__gui = None - 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 - # and have no name attached to them. - self.__phase = None - self.__maxtem = None - self.__noxaxis = None - self.__time_code = None - - if kat_code != None and kat_file != None: - raise pkex.BasePyKatException("Specify either a Kat file or some Kat code, not both.") - - if kat_code != None: - self.parseCommands(kat_code) - - if kat_file != None: - self.loadKatFile(kat_file) - - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) - - @property - def maxtem(self): return self.__maxtem - @maxtem.setter - def maxtem(self,value): self.__maxtem = int(value) - - @property - def phase(self): return self.__phase - @phase.setter - def phase(self,value): self.__phase = int(value) - - @property - def getPerformanceData(self): return self.__time_code - @getPerformanceData.setter - def getPerformanceData(self,value): self.__time_code = bool(value) - - @property - def noxaxis(self): return self.__noxaxis - @noxaxis.setter - def noxaxis(self,value): self.__noxaxis = bool(value) - - def logo(self): - print """ ..- - PyKat _ '( - \\`.|\\.__...-\"\"""-_." ) - ..+-----.._ / ' ` .-' - . ' `: 7/* _/._\\ \\ ( - ( '::;;+;;: `-"' =" /,`"" `) / - L. \\`:::a:f c_/ n_' - ..`--...___`. . , - `^-....____: +.""" - - def loadKatFile(self, katfile): - commands=open(katfile).read() - self.parseCommands(commands) - - def parseKatCode(self, code): - #commands = code.split("\n") - self.parseCommands(code) - - def parseCommands(self, commands): - blockComment = False - - self.__currentTag= NO_BLOCK - - if not (NO_BLOCK in self.__blocks): - self.__blocks[NO_BLOCK] = Block(NO_BLOCK) - - commands=self.remove_comments(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: - if len(line.strip()) >= 2: - line = line.strip() - - # Looking for block start or end - values = line.split(" ") - if values[0] == "%%%": - if values[1] == "FTblock": - newTag = values[2] - - if self.__currentTag != None and newTag != self.__currentTag: - warnings.warn("found block {0} before block {1} ended".format(newTag, self.__currentTag)) - - if newTag in self.__blocks: - raise pkex.BasePyKatException("Block `{0}` has already been read") - - self.__blocks[newTag] = Block(newTag) # create new list to store all references to components in block - self.__currentTag = newTag - - if values[1] == "FTend": - self.__currentTag = NO_BLOCK - - continue - #warnings.warn("current tag {0}".format(self.__currentTag)) - - # don't read comment lines - if line[0] == "#" or line[0] == "%": - continue - - # check if block comment is being used - if not blockComment and line[0:2] == "/*": - blockComment = True - continue - elif blockComment and line[0:2] == "*/": - blockComment = False - continue - - first = line.split(" ",1)[0] - obj = None - - if(first == "m" or first == "m1" or first == "m2"): - obj = pykat.components.mirror.parseFinesseText(line) - elif(first == "s"): - obj = pykat.components.space.parseFinesseText(line) - elif(first == "l"): - obj = pykat.components.laser.parseFinesseText(line) - elif(first[0:2] == "bs"): - obj = pykat.components.beamSplitter.parseFinesseText(line) - elif(first[0:2] == "gr"): - obj = pykat.components.grating.parseFinesseText(line) - elif(first[0:4] == "isol"): - obj = pykat.components.isolator.parseFinesseText(line) - elif(first[0:4] == "lens"): - obj = pykat.components.lens.parseFinesseText(line) - elif(first[0:3] == "mod"): - obj = pykat.components.modulator.parseFinesseText(line) - elif(first[0:2] == "pd"): - obj = pykat.detectors.photodiode.parseFinesseText(line) - elif(first == "xaxis" or first == "xaxis*"): - obj = pykat.commands.xaxis.parseFinesseText(line) - elif(first == "x2axis" or first == "x2axis*"): - obj = pykat.commands.x2axis.parseFinesseText(line) - elif(first == "gauss" or first == "gauss*" or first == "gauss**"): - after_process.append(line) - else: - if self.verbose: - print "Parsing `{0}` into pykat object not implemented yet, added as extra line.".format(line) - obj = line - # manually add the line to the block contents - self.__blocks[self.__currentTag].contents.append(line) - - if obj != None and not isinstance(obj, str): - self.add(obj) - - # now process all the varous gauss/attr etc. commands which require - # components to exist first before they can be processed - for line in after_process: - first = line.split(" ",1)[0] - - if first == "gauss" or first == "gauss*" or first == "gauss**": - pykat.commands.gauss.parseFinesseText(line) - - self.__currentTag = NO_BLOCK - - def saveScript(self, filename=None): - """ - Saves the current kat object to a Finesse input file - """ - try: - katScript = "".join(self.generateKatScript()) - katfile = open(filename,'w') - katfile.writelines(katScript) - katfile.flush() - katfile.close() - - except pkex.BasePyKatException as ex: - print ex - - def run(self, printout=0, printerr=0, save_output=False, save_kat=False,kat_name=None) : - """ - Runs the current simulation setup that has been built thus far. - It returns a katRun or katRun2D object which is populated with the various - data from the simulation run. - """ - start = datetime.datetime.now() - - - try: - if not hasattr(self, "xaxis") and self.noxaxis != None and self.noxaxis == False: - raise pkex.BasePyKatException("No xaxis was defined") - - if len(self.__katdir) == 0: - # Get the environment variable for where Finesse is stored - self.__finesse_dir = os.environ.get('FINESSE_DIR') - - if self.__finesse_dir == None : - raise MissingFinesseEnvVar() - else: - self.__finesse_dir = self.__katdir - - if len(self.__katname) == 0: - katexe = "kat" - - if os.sys.platform == "win32": - katexe += ".exe" - else: - katexe = self.__katname - - kat_exec = os.path.join(self.__finesse_dir, katexe) - - # check if kat file exists and it is executable by user - if not (os.path.isfile(kat_exec) and os.access(kat_exec, os.X_OK)): - raise MissingFinesse() - - if self.verbose: print "--------------------------------------------------------------" - if self.verbose: print "Running kat - Started at " + str(start) - - if hasattr(self, "x2axis"): - r = katRun2D() - else: - r = katRun() - - r.katScript = "".join(self.generateKatScript()) - - # create a kat file which we will write the script into - 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() - - cmd=[kat_exec, '--perl1'] - - if self.__time_code: - cmd.append('--perf-timing') - - cmd.append('--no-backspace') - # set default format so that less repeated numbers are printed to the - # output file, should speed up running and parsing of output files - cmd.append('-format=%.15g') - - cmd.append(katfile.name) - - #if self.verbose: - #print cmd - - p=subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - err = "" - - if self.verbose: print "Finesse binary output:" - - for line in iter(p.stderr.readline, ""): - #err += line - - if len(line) > 0: - if line.rstrip().endswith('%'): - vals = line.split("-") - action = vals[0].strip() - prc = vals[1].strip()[:-1] - sys.stdout.write("\r{0} {1}%".format(action, prc)) - elif line[0] == '*': - sys.stdout.write(line) - - [out,errpipe] = p.communicate() - - # get the version number - ix = out.find('build ') + 6 - ix2 = out.find(')',ix) - r.katVersion = out[ix:ix2] - - r.runDateTime = datetime.datetime.now() - - if p.returncode != 0: - raise pkex.FinesseRunError(err, katfile.name) - - if printout == 1: print out - if printerr == 1: print err - - root = os.path.splitext(katfile.name) - base = os.path.basename(root[0]) - outfile = root[0] + ".out" - - if save_output: - newoutfile = "{0}.out".format(base) - - cwd = os.path.os.getcwd() - newoutfile = os.path.join(cwd,newoutfile) - - if os.path.isfile(newoutfile): - os.remove(newoutfile) - - os.rename(outfile, newoutfile) - - if self.verbose: print "\nOutput data saved to '{0}'".format(newoutfile) - - if hasattr(self, "x2axis"): - [r.x,r.y,r.z,hdr] = self.readOutFile(outfile) - - r.xlabel = hdr[0] - r.ylabel = hdr[1] - r.zlabels = map(str.strip, hdr[2:]) - else: - [r.x,r.y,hdr] = self.readOutFile(outfile) - - r.xlabel = hdr[0] - r.ylabels = map(str.strip, hdr[1:]) - - if save_kat: - if kat_name == None: - kat_name = "pykat_output" - - cwd = os.path.os.getcwd() - newkatfile = os.path.join(cwd, kat_name + ".kat") - - if os.path.isfile(newkatfile): - os.remove(newkatfile) - - os.rename(katfile.name, newkatfile) - - if self.verbose: print "Kat file saved to '{0}'".format(newkatfile) - - - katfile.close() - perfData = [] - - if self.__time_code: - perffile = open(root[0] + ".perf",'r') - - for l in perffile.readlines(): - vals = l.strip().split(' ') - perfData.append((vals[0], float(vals[1]), float(vals[2]), float(vals[3]))) - - return [r, perfData] - else: - return r +# -*- coding: utf-8 -*- +""" +Created on Sun Jan 27 09:56:53 2013 + +PyKat - Python interface and wrapper for FINESSE +Copyright (C) 2013 Daniel David Brown + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Contact at ddb@star.sr.bham.ac.uk + +@author: Daniel Brown +""" +import sys +import os +import subprocess +import tempfile +import numpy as np +import datetime +import pickle +import pykat +import warnings +import re + +from pykat.node_network import NodeNetwork +from pykat.detectors import Detector +from pykat.components import Component +from pykat.commands import Command, xaxis +from pykat.gui.gui import pyKatGUI + +import pykat.exceptions as pkex + +from PyQt4.QtCore import QCoreApplication +from PyQt4.QtGui import QApplication + +NO_GUI = False +NO_BLOCK = "NO_BLOCK" + +class katRun(object): + def __init__(self): + self.runDateTime = datetime.datetime.now() + self.x = None + self.y = None + self.xlabel = None + self.ylabels = None + self.katScript = None + self.katVersion = None + + def saveKatRun(self, filename): + with open(filename,'w') as outfile: + pickle.dump(self, outfile) + + @staticmethod + def loadKatRun(filename): + with open(filename,'r') as infile: + return pickle.load(infile) + + def get(self, value): return self[value] + + def __getitem__(self, value): + if value in self.ylabels: + idx = self.ylabels.index(value) + if len(self.y.shape) == 1: + return self.y + else: + return self.y[:, idx] + else: + raise pkex.BasePyKatException("No output by the name {0} found".format(value)) + +class katRun2D(object): + def __init__(self): + self.runDateTime = datetime.datetime.now() + self.x = None + self.y = None + self.z = None + self.xlabel = None + self.ylabel = None + self.zlabels = None + self.katScript = None + self.katVersion = None + + def saveKatRun(self, filename): + with open(filename,'w') as outfile: + pickle.dump(self, outfile) + + @staticmethod + def loadKatRun(filename): + with open(filename,'r') as infile: + return pickle.load(infile) + + 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)] + + if len(idx) > 0: + return self.z[idx].squeeze() + else: + raise pkex.BasePyKatException("No output by the name {0} found".format(str(value))) + +class Block: + def __init__(self, name): + self.__name = name + self.contents = [] # List of objects and strings of finesse code + self.enabled = True + + @property + def name(self): return self.__name + +class kat(object): + + def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None): + + self.scene = None # scene object for GUI + self.verbose = True + self.__blocks = {} # dictionary of blocks that are used + self.__components = {} # dictionary of optical components + self.__detectors = {} # dictionary of detectors + self.__commands = {} # dictionary of commands + self.__extra_lines = [] # an array of strings which are just normal finesse code to include when running + self.__gui = None + 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 + # and have no name attached to them. + self.__phase = None + self.__maxtem = None + self.__noxaxis = None + self.__time_code = None + + if kat_code != None and kat_file != None: + raise pkex.BasePyKatException("Specify either a Kat file or some Kat code, not both.") + + if kat_code != None: + self.parseCommands(kat_code) + + if kat_file != None: + self.loadKatFile(kat_file) + + cls = type(self) + self.__class__ = type(cls.__name__, (cls,), {}) + + @property + def maxtem(self): return self.__maxtem + @maxtem.setter + def maxtem(self,value): self.__maxtem = int(value) + + @property + def phase(self): return self.__phase + @phase.setter + def phase(self,value): self.__phase = int(value) + + @property + def getPerformanceData(self): return self.__time_code + @getPerformanceData.setter + def getPerformanceData(self,value): self.__time_code = bool(value) + + @property + def noxaxis(self): return self.__noxaxis + @noxaxis.setter + def noxaxis(self,value): self.__noxaxis = bool(value) + + def logo(self): + print """ ..- + PyKat _ '( + \\`.|\\.__...-\"\"""-_." ) + ..+-----.._ / ' ` .-' + . ' `: 7/* _/._\\ \\ ( + ( '::;;+;;: `-"' =" /,`"" `) / + L. \\`:::a:f c_/ n_' + ..`--...___`. . , + `^-....____: +.""" + + def loadKatFile(self, katfile): + commands=open(katfile).read() + self.parseCommands(commands) + + def parseKatCode(self, code): + #commands = code.split("\n") + self.parseCommands(code) + + def parseCommands(self, commands): + blockComment = False + + self.__currentTag= NO_BLOCK + + if not (NO_BLOCK in self.__blocks): + self.__blocks[NO_BLOCK] = Block(NO_BLOCK) + + commands=self.remove_comments(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: + if len(line.strip()) >= 2: + line = line.strip() + + # Looking for block start or end + values = line.split(" ") + if values[0] == "%%%": + if values[1] == "FTblock": + newTag = values[2] + + if self.__currentTag != None and newTag != self.__currentTag: + warnings.warn("found block {0} before block {1} ended".format(newTag, self.__currentTag)) + + if newTag in self.__blocks: + raise pkex.BasePyKatException("Block `{0}` has already been read") + + self.__blocks[newTag] = Block(newTag) # create new list to store all references to components in block + self.__currentTag = newTag + + if values[1] == "FTend": + self.__currentTag = NO_BLOCK + + continue + #warnings.warn("current tag {0}".format(self.__currentTag)) + + # don't read comment lines + if line[0] == "#" or line[0] == "%": + continue + + # check if block comment is being used + if not blockComment and line[0:2] == "/*": + blockComment = True + continue + elif blockComment and line[0:2] == "*/": + blockComment = False + continue + + first = line.split(" ",1)[0] + obj = None + + if(first == "m" or first == "m1" or first == "m2"): + obj = pykat.components.mirror.parseFinesseText(line) + elif(first == "s"): + obj = pykat.components.space.parseFinesseText(line) + elif(first == "l"): + obj = pykat.components.laser.parseFinesseText(line) + elif(first[0:2] == "bs"): + obj = pykat.components.beamSplitter.parseFinesseText(line) + elif(first[0:2] == "gr"): + obj = pykat.components.grating.parseFinesseText(line) + elif(first[0:4] == "isol"): + obj = pykat.components.isolator.parseFinesseText(line) + elif(first[0:4] == "lens"): + obj = pykat.components.lens.parseFinesseText(line) + elif(first[0:3] == "mod"): + obj = pykat.components.modulator.parseFinesseText(line) + elif(first[0:2] == "pd"): + obj = pykat.detectors.photodiode.parseFinesseText(line) + elif(first == "xaxis" or first == "xaxis*"): + obj = pykat.commands.xaxis.parseFinesseText(line) + elif(first == "x2axis" or first == "x2axis*"): + obj = pykat.commands.x2axis.parseFinesseText(line) + elif(first == "gauss" or first == "gauss*" or first == "gauss**"): + after_process.append(line) + else: + if self.verbose: + print "Parsing `{0}` into pykat object not implemented yet, added as extra line.".format(line) + obj = line + # manually add the line to the block contents + self.__blocks[self.__currentTag].contents.append(line) + + if obj != None and not isinstance(obj, str): + self.add(obj) + + # now process all the varous gauss/attr etc. commands which require + # components to exist first before they can be processed + for line in after_process: + first = line.split(" ",1)[0] + + if first == "gauss" or first == "gauss*" or first == "gauss**": + pykat.commands.gauss.parseFinesseText(line) + + self.__currentTag = NO_BLOCK + + def saveScript(self, filename=None): + """ + Saves the current kat object to a Finesse input file + """ + try: + katScript = "".join(self.generateKatScript()) + katfile = open(filename,'w') + katfile.writelines(katScript) + katfile.flush() + katfile.close() + + except pkex.BasePyKatException as ex: + print ex + + def run(self, printout=0, printerr=0, save_output=False, save_kat=False,kat_name=None) : + """ + Runs the current simulation setup that has been built thus far. + It returns a katRun or katRun2D object which is populated with the various + data from the simulation run. + """ + start = datetime.datetime.now() + + + try: + if not hasattr(self, "xaxis") and self.noxaxis != None and self.noxaxis == False: + raise pkex.BasePyKatException("No xaxis was defined") + + if len(self.__katdir) == 0: + # Get the environment variable for where Finesse is stored + self.__finesse_dir = os.environ.get('FINESSE_DIR') + + if self.__finesse_dir == None : + raise MissingFinesseEnvVar() + else: + self.__finesse_dir = self.__katdir + + if len(self.__katname) == 0: + katexe = "kat" + + if os.sys.platform == "win32": + katexe += ".exe" + else: + katexe = self.__katname + + kat_exec = os.path.join(self.__finesse_dir, katexe) + + # check if kat file exists and it is executable by user + if not (os.path.isfile(kat_exec) and os.access(kat_exec, os.X_OK)): + raise MissingFinesse() + + if self.verbose: print "--------------------------------------------------------------" + if self.verbose: print "Running kat - Started at " + str(start) + + if hasattr(self, "x2axis"): + r = katRun2D() + else: + r = katRun() + + r.katScript = "".join(self.generateKatScript()) + + # create a kat file which we will write the script into + 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() + + cmd=[kat_exec, '--perl1'] + + if self.__time_code: + cmd.append('--perf-timing') + + cmd.append('--no-backspace') + # set default format so that less repeated numbers are printed to the + # output file, should speed up running and parsing of output files + cmd.append('-format=%.15g') + + cmd.append(katfile.name) + + #if self.verbose: + #print cmd + + p=subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + err = "" + + if self.verbose: print "Finesse binary output:" + + for line in iter(p.stderr.readline, ""): + #err += line + + if len(line) > 0: + if line.rstrip().endswith('%'): + vals = line.split("-") + action = vals[0].strip() + prc = vals[1].strip()[:-1] + sys.stdout.write("\r{0} {1}%".format(action, prc)) + elif line[0] == '*': + sys.stdout.write(line) + + [out,errpipe] = p.communicate() + + # get the version number + ix = out.find('build ') + 6 + ix2 = out.find(')',ix) + r.katVersion = out[ix:ix2] + + r.runDateTime = datetime.datetime.now() + + if p.returncode != 0: + raise pkex.FinesseRunError(err, katfile.name) + + if printout == 1: print out + if printerr == 1: print err + + root = os.path.splitext(katfile.name) + base = os.path.basename(root[0]) + outfile = root[0] + ".out" + + if save_output: + newoutfile = "{0}.out".format(base) + + cwd = os.path.os.getcwd() + newoutfile = os.path.join(cwd,newoutfile) + + if os.path.isfile(newoutfile): + os.remove(newoutfile) + + os.rename(outfile, newoutfile) + + if self.verbose: print "\nOutput data saved to '{0}'".format(newoutfile) + + if hasattr(self, "x2axis"): + [r.x,r.y,r.z,hdr] = self.readOutFile(outfile) + + r.xlabel = hdr[0] + r.ylabel = hdr[1] + r.zlabels = map(str.strip, hdr[2:]) + else: + [r.x,r.y,hdr] = self.readOutFile(outfile) + + r.xlabel = hdr[0] + r.ylabels = map(str.strip, hdr[1:]) + + if save_kat: + if kat_name == None: + kat_name = "pykat_output" + + cwd = os.path.os.getcwd() + newkatfile = os.path.join(cwd, kat_name + ".kat") + + if os.path.isfile(newkatfile): + os.remove(newkatfile) + + os.rename(katfile.name, newkatfile) + + if self.verbose: print "Kat file saved to '{0}'".format(newkatfile) + + + katfile.close() + perfData = [] + + if self.__time_code: + perffile = open(root[0] + ".perf",'r') + + for l in perffile.readlines(): + vals = l.strip().split(' ') + perfData.append((vals[0], float(vals[1]), float(vals[2]), float(vals[3]))) + + return [r, perfData] + else: + return r except pkex.FinesseRunError as fe: - print fe - finally: - if self.verbose: print "" - if self.verbose: print "Finished in " + str(datetime.datetime.now()-start) - - - def add(self, obj): - try: - obj.tag = self.__currentTag - self.__blocks[self.__currentTag].contents.append(obj) - - if isinstance(obj, Component): - - if obj.name in self.__components : - raise pkex.BasePyKatException("A component with name '{0}' has already been added".format([obj.name])) - - self.__components[obj.name] = obj - self.__add_component(obj) - - elif isinstance(obj, Detector): - - if obj.name in self.__detectors : - raise pkex.BasePyKatException("A detector '{0}' has already been added".format(obj.name)) - - self.__detectors[obj.name] = obj - self.__add_detector(obj) - - elif isinstance(obj, Command): - - self.__commands[obj.__class__.__name__] = obj - self.__add_command(obj) - - else : - raise pkex.BasePyKatException("Object {0} could not be added".format(obj)) - - obj._on_kat_add(self) - - except pkex.BasePyKatException as ex: - print ex - - def readOutFile(self, filename): - - with open(filename,'r') as outfile: - # read first to lines to get to header line - outfile.readline() - outfile.readline() - - hdr = outfile.readline().replace('%','').replace('\n','').split(',') - - data = np.loadtxt(filename,comments='%',skiprows=4) - - if hasattr(self, "x2axis"): - # need to parse 2D outputs slightly different as they are effectively 2D matrices - # written in linear form - x = data[0::(1+self.x2axis.steps),0] - y = data[0:(1+self.x2axis.steps),1] - # get rows and columns lined up so that we can reshape a single column of all x/y data - # into a matrix - z = data[:,2:].transpose().reshape(data.shape[1]-2, 1+self.xaxis.steps, 1+self.x2axis.steps) - # once you do this the data for y and x axes need swapping - z = z.swapaxes(1,2) - return [x, y, z, hdr] - else: - shape_len = len(data.shape) - - if shape_len > 1: - rows,cols = data.shape - x = data[:,0] - y = data[:,1:cols].squeeze() - else: - rows = 1 - cols = data.shape[0] - - x = data[0] - y = data[1:cols].squeeze() - - return [x, y, hdr] - - def generateKatScript(self) : - """ Generates the kat file which can then be run """ - - out = [] - - for key in self.__blocks: - objs = self.__blocks[key].contents - - out.append("%%% FTblock " + key + "\n") - - for obj in objs: - if isinstance(obj, str): - out.append(obj + '\n') - - elif isinstance(obj, Component) or isinstance(obj, Detector) or isinstance(obj, Command): - txt = obj.getFinesseText() - - if txt != None: - if isinstance(txt,list): - for t in txt: - out.append(t + "\n") - else: - out.append(txt + "\n") - - out.append("%%% FTend " + key + "\n") - - if self.noxaxis != None and self.noxaxis == True: - out.append("noxaxis\n") - - # now loop through all the nodes and get any gauss commands - for key in self.nodes.getNodes(): - txt = self.nodes.getNodes()[key].getFinesseText() - - if txt != None: - if isinstance(txt,list): - for t in txt: out.append(t+ "\n") - else: - out.append(txt + "\n") - - if self.phase != None: out.append("phase {0}\n".format(self.phase)) - if self.maxtem != None: out.append("maxtem {0}\n".format(self.maxtem)) - - # ensure we don't do any plotting. That should be handled - # by user themselves - out.append("gnuterm no\n") - out.append("pyterm no\n") - - return out - - def openGUI(self): - if NO_GUI: - print "No PyQt4 module was installed so cannot open a GUI" - else: - self.app = QCoreApplication.instance() - created = False - - if self.app == None: - created = True - self.app = QApplication([""]) - - if self.pykatgui == None: - self.pykatgui = pyKatGUI(self) - self.pykatgui.main() - else: - self.pykatgui.show() - - if created: self.app.exec_() - - def getComponents(self): - return self.__components.values() - - def hasComponent(self, name): - return (name in self.__components) - - def _newName(self, container, prefix): - n = 1 - name = "{0}{1}".format(prefix, n) - - while name in container: - n += 1 - name = "{0}{1}".format(prefix,n) - - return name - - def getNewComponentName(self,prefix): - ''' - Returns a name for a component which hasn't already been added. - Returns [prefix] + number, where number is greater than 1. e.g. - if m1 exists getNewName('m') will return 'm2' - ''' - return self._newName(self.__components, prefix) - - def getNewDetectorName(self,prefix): - ''' - Returns a name for a component which hasn't already been added. - Returns [prefix] + number, where number is greater than 1. e.g. - if m1 exists getNewName('m') will return 'm2' - ''' - return self._newName(self.__detectors, prefix) - - def getNewNodeNames(self,prefix,N=1): - ''' - Returns a list of names for N number of nodes which haven't already been added. - Returns [prefix] + number, where number is greater than 1. e.g. - if m1 exists getNewName('m') will return 'm2' - ''' - rtn = [] - n = 1 - - for M in range(1,N+1): - name = "{0}{1}".format(prefix, n) - - while name in self.nodes.getNodes() or (name in rtn): - n += 1 - name = "{0}{1}".format(prefix,n) - - rtn.append(name) - - return rtn - - - def __add_detector(self, det): - - if not isinstance(det, Detector): - raise exceptions.ValueError("Argument is not of type Detector") - - name = det.name - fget = lambda self: self.__get_detector(name) - - setattr(self.__class__, name, property(fget)) - setattr(self, '__det_' + name, det) - - def __get_detector(self, name): - return getattr(self, '__det_' + name) - - def __add_command(self, com): - - if not isinstance(com, Command): - raise exceptions.ValueError("Argument is not of type Command") - - name = com.__class__.__name__ - fget = lambda self: self.__get_command(name) - - setattr(self.__class__, name, property(fget)) - setattr(self, '__com_' + name, com) - - def __get_command(self, name): - return getattr(self, '__com_' + name) - - def __add_component(self, comp): - - if not isinstance(comp, Component): - raise exceptions.ValueError("Argument is not of type Component") - - fget = lambda self: self.__get_component(comp.name) - - setattr(self.__class__, comp.name, property(fget)) - setattr(self, '__comp_' + comp.name, comp) - - def __get_component(self, name): - return getattr(self, '__comp_' + name) - - def remove_comments(self, string): - 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 */) - regex = re.compile(pattern, re.MULTILINE|re.DOTALL) - def _replacer(match): - # if the 2nd group (capturing comments) is not None, - # it means we have captured a non-quoted (real) comment string. - if match.group(2) is not None: - 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) + print fe + finally: + if self.verbose: print "" + if self.verbose: print "Finished in " + str(datetime.datetime.now()-start) + + + def add(self, obj): + try: + obj.tag = self.__currentTag + self.__blocks[self.__currentTag].contents.append(obj) + + if isinstance(obj, Component): + + if obj.name in self.__components : + raise pkex.BasePyKatException("A component with name '{0}' has already been added".format([obj.name])) + + self.__components[obj.name] = obj + self.__add_component(obj) + + elif isinstance(obj, Detector): + + if obj.name in self.__detectors : + raise pkex.BasePyKatException("A detector '{0}' has already been added".format(obj.name)) + + self.__detectors[obj.name] = obj + self.__add_detector(obj) + + elif isinstance(obj, Command): + + self.__commands[obj.__class__.__name__] = obj + self.__add_command(obj) + + else : + raise pkex.BasePyKatException("Object {0} could not be added".format(obj)) + + obj._on_kat_add(self) + + except pkex.BasePyKatException as ex: + print ex + + def readOutFile(self, filename): + + with open(filename,'r') as outfile: + # read first to lines to get to header line + outfile.readline() + outfile.readline() + + hdr = outfile.readline().replace('%','').replace('\n','').split(',') + + data = np.loadtxt(filename,comments='%',skiprows=4) + + if hasattr(self, "x2axis"): + # need to parse 2D outputs slightly different as they are effectively 2D matrices + # written in linear form + x = data[0::(1+self.x2axis.steps),0] + y = data[0:(1+self.x2axis.steps),1] + # get rows and columns lined up so that we can reshape a single column of all x/y data + # into a matrix + z = data[:,2:].transpose().reshape(data.shape[1]-2, 1+self.xaxis.steps, 1+self.x2axis.steps) + # once you do this the data for y and x axes need swapping + z = z.swapaxes(1,2) + return [x, y, z, hdr] + else: + shape_len = len(data.shape) + + if shape_len > 1: + rows,cols = data.shape + x = data[:,0] + y = data[:,1:cols].squeeze() + else: + rows = 1 + cols = data.shape[0] + + x = data[0] + y = data[1:cols].squeeze() + + return [x, y, hdr] + + def generateKatScript(self) : + """ Generates the kat file which can then be run """ + + out = [] + + for key in self.__blocks: + objs = self.__blocks[key].contents + + out.append("%%% FTblock " + key + "\n") + + for obj in objs: + if isinstance(obj, str): + out.append(obj + '\n') + + elif isinstance(obj, Component) or isinstance(obj, Detector) or isinstance(obj, Command): + txt = obj.getFinesseText() + + if txt != None: + if isinstance(txt,list): + for t in txt: + out.append(t + "\n") + else: + out.append(txt + "\n") + + out.append("%%% FTend " + key + "\n") + + if self.noxaxis != None and self.noxaxis == True: + out.append("noxaxis\n") + + # now loop through all the nodes and get any gauss commands + for key in self.nodes.getNodes(): + txt = self.nodes.getNodes()[key].getFinesseText() + + if txt != None: + if isinstance(txt,list): + for t in txt: out.append(t+ "\n") + else: + out.append(txt + "\n") + + if self.phase != None: out.append("phase {0}\n".format(self.phase)) + if self.maxtem != None: out.append("maxtem {0}\n".format(self.maxtem)) + + # ensure we don't do any plotting. That should be handled + # by user themselves + out.append("gnuterm no\n") + out.append("pyterm no\n") + + return out + + def openGUI(self): + if NO_GUI: + print "No PyQt4 module was installed so cannot open a GUI" + else: + self.app = QCoreApplication.instance() + created = False + + if self.app == None: + created = True + self.app = QApplication([""]) + + if self.pykatgui == None: + self.pykatgui = pyKatGUI(self) + self.pykatgui.main() + else: + self.pykatgui.show() + + if created: self.app.exec_() + + def getComponents(self): + return self.__components.values() + + def hasComponent(self, name): + return (name in self.__components) + + def _newName(self, container, prefix): + n = 1 + name = "{0}{1}".format(prefix, n) + + while name in container: + n += 1 + name = "{0}{1}".format(prefix,n) + + return name + + def getNewComponentName(self,prefix): + ''' + Returns a name for a component which hasn't already been added. + Returns [prefix] + number, where number is greater than 1. e.g. + if m1 exists getNewName('m') will return 'm2' + ''' + return self._newName(self.__components, prefix) + + def getNewDetectorName(self,prefix): + ''' + Returns a name for a component which hasn't already been added. + Returns [prefix] + number, where number is greater than 1. e.g. + if m1 exists getNewName('m') will return 'm2' + ''' + return self._newName(self.__detectors, prefix) + + def getNewNodeNames(self,prefix,N=1): + ''' + Returns a list of names for N number of nodes which haven't already been added. + Returns [prefix] + number, where number is greater than 1. e.g. + if m1 exists getNewName('m') will return 'm2' + ''' + rtn = [] + n = 1 + + for M in range(1,N+1): + name = "{0}{1}".format(prefix, n) + + while name in self.nodes.getNodes() or (name in rtn): + n += 1 + name = "{0}{1}".format(prefix,n) + + rtn.append(name) + + return rtn + + + def __add_detector(self, det): + + if not isinstance(det, Detector): + raise exceptions.ValueError("Argument is not of type Detector") + + name = det.name + fget = lambda self: self.__get_detector(name) + + setattr(self.__class__, name, property(fget)) + setattr(self, '__det_' + name, det) + + def __get_detector(self, name): + return getattr(self, '__det_' + name) + + def __add_command(self, com): + + if not isinstance(com, Command): + raise exceptions.ValueError("Argument is not of type Command") + + name = com.__class__.__name__ + fget = lambda self: self.__get_command(name) + + setattr(self.__class__, name, property(fget)) + setattr(self, '__com_' + name, com) + + def __get_command(self, name): + return getattr(self, '__com_' + name) + + def __add_component(self, comp): + + if not isinstance(comp, Component): + raise exceptions.ValueError("Argument is not of type Component") + + fget = lambda self: self.__get_component(comp.name) + + setattr(self.__class__, comp.name, property(fget)) + setattr(self, '__comp_' + comp.name, comp) + + def __get_component(self, name): + return getattr(self, '__comp_' + name) + + def remove_comments(self, string): + 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 */) + regex = re.compile(pattern, re.MULTILINE|re.DOTALL) + def _replacer(match): + # if the 2nd group (capturing comments) is not None, + # it means we have captured a non-quoted (real) comment string. + if match.group(2) is not None: + 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) diff --git a/pykat/gui/graphics.py b/pykat/gui/graphics.py index 8be038975c82d2cff493768b67aa3ac8c933f385..6b445defea3f529c42d8aa2d303878bf1aa0d499 100644 --- a/pykat/gui/graphics.py +++ b/pykat/gui/graphics.py @@ -1,191 +1,191 @@ -# -*- coding: utf-8 -*- -""" -Created on Fri Feb 01 09:13:03 2013 - -@author: Daniel -""" - -from PyQt4.QtGui import * -from PyQt4.Qt import * -from PyQt4 import QtSvg -from PyQt4.QtSvg import QGraphicsSvgItem -import pykat.components -import exceptions - -nsize = 10 - -class NodeQGraphicItem(QGraphicsRectItem): - - def __init__(self, node, x,y, *args, **kwargs): - QGraphicsRectItem.__init__(self, *args, **kwargs) - self.__node = node - self.setPos(x,y) - - item = QGraphicsTextItem(node.name, self) - rect = item.boundingRect() - item.setPos(-0.5*rect.width(), 0) - - self.setAcceptHoverEvents(True) - - self.marked = False - - @property - def node(self): return self.__node - - def refresh(self): - if not self.marked: - if self.__node.isConnected(): - self.setBrush(QBrush(Qt.red)) - else: - self.setBrush(QBrush(Qt.green)) - else: - self.setBrush(QBrush(Qt.yellow)) - -class SpaceQGraphicsItem(QGraphicsLineItem): - def __init__(self, spaceComponent): - QGraphicsLineItem.__init__(self) - self.__n1 = None - self.__n2 = None - self.__space = spaceComponent - - item = QGraphicsTextItem(self.__space.name, self) - rect = item.boundingRect() - item.setPos(-0.5*rect.width(),0*rect.height()) - - self.refresh() - - @property - def space(self): return self.__space - - def refresh(self): - nodes = self.__space.nodes - - conn = nodes[0].amIConnected(self.__space) - - x1 = 0 - y1 = 0 - x2 = 0 - y2 = 0 - - if conn[0]: - if conn[1] != None: - if self.__n1 is not None: - # i.e. we have a node graphic item but now it is connected to something - self.__n1.scene().removeItem(self.__n1) - self.__n1 = None - - # now check if a connected component was returned too - if conn[1] != None: - # so this node should be attached to something - # in this case we get the position of their node - # and draw the the line from their - itm=conn[1].getQGraphicsItem() - x1 = itm.x() + itm.nodedx[conn[2]][0] - y1 = itm.y() + itm.nodedx[conn[2]][1] - else: - if self.__n1 == None: - self.__n1 = NodeQGraphicItem(nodes[0],0,0,-nsize/2,-nsize/2,nsize,nsize,self) - self.__n1.setPen(QPen(Qt.black,1)) - - self.__n1.setVisible(True) - self.__n1.setBrush(QBrush(Qt.green)) - p = self.__n1.pos() - x1 = self.x()+p.x() - y1 = self.y()+p.y() - - conn = nodes[1].amIConnected(self.__space) - - if conn[0]: - if conn[1] != None: - - if self.__n2 is not None: - # i.e. we have a node graphic item but now it is connected to something - self.__n2.scene().removeItem(self.__n2) - self.__n2 = None - - # now check if a connected component was returned too - if conn[1] != None: - # so this node should be attached to something - # in this case we get the position of their node - # and draw the the line from their - itm=conn[1].getQGraphicsItem() - x2 = itm.x() + itm.nodedx[conn[2]][0] - y2 = itm.y() + itm.nodedx[conn[2]][1] - else: - if self.__n2 == None: - self.__n2 = NodeQGraphicItem(nodes[1],0,0,-nsize/2,-nsize/2,nsize,nsize,self) - self.__n2.setPen(QPen(Qt.black,1)) - - self.__n2.setVisible(True) - self.__n2.setBrush(QBrush(Qt.green)) - p = self.__n2.pos() - x2 = self.x()+p.x() - y2 = self.y()+p.y() - - # convert x1,y1,x2 and y2 into the local coordinates of the - # space object - p = QPointF((x1-x2)*0.5,(y1-y2)*0.5) - self.setPos(x1 - p.x(), y1 - p.y()) - - # if the nodes are visible then reposition them in the - # component reference frame - if self.__n1 is not None and self.__n1.isVisible(): - self.__n1.setPos(QPointF(p.x(),p.y())) - self.__n1.refresh() - - if self.__n2 is not None and self.__n2.isVisible(): - self.__n2.setPos(QPointF(p.x()+x2-x1, p.y()+y2-y1)) - self.__n2.refresh() - - self.setLine(p.x(), p.y(), p.x()+x2-x1, p.y()+y2-y1) - self.setPen(QPen(Qt.red, 3)) - - -class ComponentQGraphicsItem(QtSvg.QGraphicsSvgItem): - - def __init__(self, svgfile, component, nodes): - QGraphicsSvgItem.__init__(self,svgfile) - self.__nodeGraphics = [] - self.__component = component - # this signals the itemChange() method when this item is moved - # used for refreshing the spaces between components - self.setFlags(QGraphicsItem.ItemSendsGeometryChanges) - self.nodedx = [] # stores the node square offsets - - item = QGraphicsTextItem(component.name,self) - rect = item.boundingRect() - item.setPos(-0.5*rect.width(),40-0.5*rect.height()) - - self.setAcceptsHoverEvents(True) - - for n in nodes: - self.nodedx.append([n[0],n[1]]) - node = n[2].getQGraphicsItem(n[0],n[1],nsize,self) - node.setPen(QPen(Qt.black)) - node.refresh() - self.__nodeGraphics.append(node) - - self.refresh() - self.installEventFilter(self) - self.setHandlesChildEvents(True) - - @property - def component(self): return self.__component - - def refresh(self): - for n in self.__nodeGraphics: - n.refresh() - - def itemChange(self, change, value): - # if the item is moved then update any spaces attached to it - if change == QGraphicsItem.ItemPositionHasChanged: - nodes = self.__component.nodes - - for n in nodes: - conn = n.amIConnected(self.__component) - - if conn[0] and isinstance(conn[1], pykat.components.space): - conn[1].getQGraphicsItem().refresh() - - return QGraphicsSvgItem.itemChange(self, change, value) - +# -*- coding: utf-8 -*- +""" +Created on Fri Feb 01 09:13:03 2013 + +@author: Daniel +""" + +from PyQt4.QtGui import * +from PyQt4.Qt import * +from PyQt4 import QtSvg +from PyQt4.QtSvg import QGraphicsSvgItem +import pykat.components +import exceptions + +nsize = 10 + +class NodeQGraphicItem(QGraphicsRectItem): + + def __init__(self, node, x,y, *args, **kwargs): + QGraphicsRectItem.__init__(self, *args, **kwargs) + self.__node = node + self.setPos(x,y) + + item = QGraphicsTextItem(node.name, self) + rect = item.boundingRect() + item.setPos(-0.5*rect.width(), 0) + + self.setAcceptHoverEvents(True) + + self.marked = False + + @property + def node(self): return self.__node + + def refresh(self): + if not self.marked: + if self.__node.isConnected(): + self.setBrush(QBrush(Qt.red)) + else: + self.setBrush(QBrush(Qt.green)) + else: + self.setBrush(QBrush(Qt.yellow)) + +class SpaceQGraphicsItem(QGraphicsLineItem): + def __init__(self, spaceComponent): + QGraphicsLineItem.__init__(self) + self.__n1 = None + self.__n2 = None + self.__space = spaceComponent + + item = QGraphicsTextItem(self.__space.name, self) + rect = item.boundingRect() + item.setPos(-0.5*rect.width(),0*rect.height()) + + self.refresh() + + @property + def space(self): return self.__space + + def refresh(self): + nodes = self.__space.nodes + + conn = nodes[0].amIConnected(self.__space) + + x1 = 0 + y1 = 0 + x2 = 0 + y2 = 0 + + if conn[0]: + if conn[1] != None: + if self.__n1 is not None: + # i.e. we have a node graphic item but now it is connected to something + self.__n1.scene().removeItem(self.__n1) + self.__n1 = None + + # now check if a connected component was returned too + if conn[1] != None: + # so this node should be attached to something + # in this case we get the position of their node + # and draw the the line from their + itm=conn[1].getQGraphicsItem() + x1 = itm.x() + itm.nodedx[conn[2]][0] + y1 = itm.y() + itm.nodedx[conn[2]][1] + else: + if self.__n1 == None: + self.__n1 = NodeQGraphicItem(nodes[0],0,0,-nsize/2,-nsize/2,nsize,nsize,self) + self.__n1.setPen(QPen(Qt.black,1)) + + self.__n1.setVisible(True) + self.__n1.setBrush(QBrush(Qt.green)) + p = self.__n1.pos() + x1 = self.x()+p.x() + y1 = self.y()+p.y() + + conn = nodes[1].amIConnected(self.__space) + + if conn[0]: + if conn[1] != None: + + if self.__n2 is not None: + # i.e. we have a node graphic item but now it is connected to something + self.__n2.scene().removeItem(self.__n2) + self.__n2 = None + + # now check if a connected component was returned too + if conn[1] != None: + # so this node should be attached to something + # in this case we get the position of their node + # and draw the the line from their + itm=conn[1].getQGraphicsItem() + x2 = itm.x() + itm.nodedx[conn[2]][0] + y2 = itm.y() + itm.nodedx[conn[2]][1] + else: + if self.__n2 == None: + self.__n2 = NodeQGraphicItem(nodes[1],0,0,-nsize/2,-nsize/2,nsize,nsize,self) + self.__n2.setPen(QPen(Qt.black,1)) + + self.__n2.setVisible(True) + self.__n2.setBrush(QBrush(Qt.green)) + p = self.__n2.pos() + x2 = self.x()+p.x() + y2 = self.y()+p.y() + + # convert x1,y1,x2 and y2 into the local coordinates of the + # space object + p = QPointF((x1-x2)*0.5,(y1-y2)*0.5) + self.setPos(x1 - p.x(), y1 - p.y()) + + # if the nodes are visible then reposition them in the + # component reference frame + if self.__n1 is not None and self.__n1.isVisible(): + self.__n1.setPos(QPointF(p.x(),p.y())) + self.__n1.refresh() + + if self.__n2 is not None and self.__n2.isVisible(): + self.__n2.setPos(QPointF(p.x()+x2-x1, p.y()+y2-y1)) + self.__n2.refresh() + + self.setLine(p.x(), p.y(), p.x()+x2-x1, p.y()+y2-y1) + self.setPen(QPen(Qt.red, 3)) + + +class ComponentQGraphicsItem(QtSvg.QGraphicsSvgItem): + + def __init__(self, svgfile, component, nodes): + QGraphicsSvgItem.__init__(self,svgfile) + self.__nodeGraphics = [] + self.__component = component + # this signals the itemChange() method when this item is moved + # used for refreshing the spaces between components + self.setFlags(QGraphicsItem.ItemSendsGeometryChanges) + self.nodedx = [] # stores the node square offsets + + item = QGraphicsTextItem(component.name,self) + rect = item.boundingRect() + item.setPos(-0.5*rect.width(),40-0.5*rect.height()) + + self.setAcceptsHoverEvents(True) + + for n in nodes: + self.nodedx.append([n[0],n[1]]) + node = n[2].getQGraphicsItem(n[0],n[1],nsize,self) + node.setPen(QPen(Qt.black)) + node.refresh() + self.__nodeGraphics.append(node) + + self.refresh() + self.installEventFilter(self) + self.setHandlesChildEvents(True) + + @property + def component(self): return self.__component + + def refresh(self): + for n in self.__nodeGraphics: + n.refresh() + + def itemChange(self, change, value): + # if the item is moved then update any spaces attached to it + if change == QGraphicsItem.ItemPositionHasChanged: + nodes = self.__component.nodes + + for n in nodes: + conn = n.amIConnected(self.__component) + + if conn[0] and isinstance(conn[1], pykat.components.space): + conn[1].getQGraphicsItem().refresh() + + return QGraphicsSvgItem.itemChange(self, change, value) + diff --git a/pykat/gui/qt_gui.py b/pykat/gui/qt_gui.py index deeecfa02dbb8912b7d2bdb2092c549dc2210805..68dd68ef67ecbfebd67f061f374a52c484025189 100644 --- a/pykat/gui/qt_gui.py +++ b/pykat/gui/qt_gui.py @@ -1,78 +1,78 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'qt_gui.ui' -# -# Created: Sat Feb 09 17:34:29 2013 -# by: PyQt4 UI code generator 4.9.5 -# -# WARNING! All changes made in this file will be lost! - -from PyQt4 import QtCore, QtGui -#from pykat.gui.gui import pyKatGraphicsView - -try: - _fromUtf8 = QtCore.QString.fromUtf8 -except AttributeError: - _fromUtf8 = lambda s: s - -class Ui_MainWindow(object): - def setupUi(self, MainWindow): - MainWindow.setObjectName(_fromUtf8("MainWindow")) - MainWindow.resize(833, 614) - self.centralwidget = QtGui.QWidget(MainWindow) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) - self.centralwidget.setSizePolicy(sizePolicy) - self.centralwidget.setObjectName(_fromUtf8("centralwidget")) - self.gridLayout = QtGui.QGridLayout(self.centralwidget) - self.gridLayout.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) - self.gridLayout.setMargin(2) - self.gridLayout.setSpacing(5) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - MainWindow.setCentralWidget(self.centralwidget) - self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 833, 26)) - self.menubar.setObjectName(_fromUtf8("menubar")) - self.menuFile = QtGui.QMenu(self.menubar) - self.menuFile.setObjectName(_fromUtf8("menuFile")) - self.menuAbout = QtGui.QMenu(self.menubar) - self.menuAbout.setObjectName(_fromUtf8("menuAbout")) - MainWindow.setMenuBar(self.menubar) - self.statusbar = QtGui.QStatusBar(MainWindow) - self.statusbar.setObjectName(_fromUtf8("statusbar")) - MainWindow.setStatusBar(self.statusbar) - self.actionClose = QtGui.QAction(MainWindow) - self.actionClose.setObjectName(_fromUtf8("actionClose")) - self.actionSave = QtGui.QAction(MainWindow) - self.actionSave.setObjectName(_fromUtf8("actionSave")) - self.actionOpen = QtGui.QAction(MainWindow) - self.actionOpen.setObjectName(_fromUtf8("actionOpen")) - self.actionHelp = QtGui.QAction(MainWindow) - self.actionHelp.setObjectName(_fromUtf8("actionHelp")) - self.actionExport_to_SVG = QtGui.QAction(MainWindow) - self.actionExport_to_SVG.setObjectName(_fromUtf8("actionExport_to_SVG")) - self.menuFile.addAction(self.actionSave) - self.menuFile.addAction(self.actionOpen) - self.menuFile.addSeparator() - self.menuFile.addAction(self.actionExport_to_SVG) - self.menuFile.addSeparator() - self.menuFile.addAction(self.actionClose) - self.menuAbout.addAction(self.actionHelp) - self.menubar.addAction(self.menuFile.menuAction()) - self.menubar.addAction(self.menuAbout.menuAction()) - - self.retranslateUi(MainWindow) - QtCore.QMetaObject.connectSlotsByName(MainWindow) - - def retranslateUi(self, MainWindow): - MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) - self.menuFile.setTitle(QtGui.QApplication.translate("MainWindow", "File", None, QtGui.QApplication.UnicodeUTF8)) - self.menuAbout.setTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8)) - self.actionClose.setText(QtGui.QApplication.translate("MainWindow", "Close", None, QtGui.QApplication.UnicodeUTF8)) - self.actionSave.setText(QtGui.QApplication.translate("MainWindow", "Save", None, QtGui.QApplication.UnicodeUTF8)) - self.actionOpen.setText(QtGui.QApplication.translate("MainWindow", "Open", None, QtGui.QApplication.UnicodeUTF8)) - self.actionHelp.setText(QtGui.QApplication.translate("MainWindow", "Help", None, QtGui.QApplication.UnicodeUTF8)) - self.actionExport_to_SVG.setText(QtGui.QApplication.translate("MainWindow", "Export to SVG...", None, QtGui.QApplication.UnicodeUTF8)) - +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'qt_gui.ui' +# +# Created: Sat Feb 09 17:34:29 2013 +# by: PyQt4 UI code generator 4.9.5 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui +#from pykat.gui.gui import pyKatGraphicsView + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + _fromUtf8 = lambda s: s + +class Ui_MainWindow(object): + def setupUi(self, MainWindow): + MainWindow.setObjectName(_fromUtf8("MainWindow")) + MainWindow.resize(833, 614) + self.centralwidget = QtGui.QWidget(MainWindow) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth()) + self.centralwidget.setSizePolicy(sizePolicy) + self.centralwidget.setObjectName(_fromUtf8("centralwidget")) + self.gridLayout = QtGui.QGridLayout(self.centralwidget) + self.gridLayout.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint) + self.gridLayout.setMargin(2) + self.gridLayout.setSpacing(5) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + MainWindow.setCentralWidget(self.centralwidget) + self.menubar = QtGui.QMenuBar(MainWindow) + self.menubar.setGeometry(QtCore.QRect(0, 0, 833, 26)) + self.menubar.setObjectName(_fromUtf8("menubar")) + self.menuFile = QtGui.QMenu(self.menubar) + self.menuFile.setObjectName(_fromUtf8("menuFile")) + self.menuAbout = QtGui.QMenu(self.menubar) + self.menuAbout.setObjectName(_fromUtf8("menuAbout")) + MainWindow.setMenuBar(self.menubar) + self.statusbar = QtGui.QStatusBar(MainWindow) + self.statusbar.setObjectName(_fromUtf8("statusbar")) + MainWindow.setStatusBar(self.statusbar) + self.actionClose = QtGui.QAction(MainWindow) + self.actionClose.setObjectName(_fromUtf8("actionClose")) + self.actionSave = QtGui.QAction(MainWindow) + self.actionSave.setObjectName(_fromUtf8("actionSave")) + self.actionOpen = QtGui.QAction(MainWindow) + self.actionOpen.setObjectName(_fromUtf8("actionOpen")) + self.actionHelp = QtGui.QAction(MainWindow) + self.actionHelp.setObjectName(_fromUtf8("actionHelp")) + self.actionExport_to_SVG = QtGui.QAction(MainWindow) + self.actionExport_to_SVG.setObjectName(_fromUtf8("actionExport_to_SVG")) + self.menuFile.addAction(self.actionSave) + self.menuFile.addAction(self.actionOpen) + self.menuFile.addSeparator() + self.menuFile.addAction(self.actionExport_to_SVG) + self.menuFile.addSeparator() + self.menuFile.addAction(self.actionClose) + self.menuAbout.addAction(self.actionHelp) + self.menubar.addAction(self.menuFile.menuAction()) + self.menubar.addAction(self.menuAbout.menuAction()) + + self.retranslateUi(MainWindow) + QtCore.QMetaObject.connectSlotsByName(MainWindow) + + def retranslateUi(self, MainWindow): + MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) + self.menuFile.setTitle(QtGui.QApplication.translate("MainWindow", "File", None, QtGui.QApplication.UnicodeUTF8)) + self.menuAbout.setTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8)) + self.actionClose.setText(QtGui.QApplication.translate("MainWindow", "Close", None, QtGui.QApplication.UnicodeUTF8)) + self.actionSave.setText(QtGui.QApplication.translate("MainWindow", "Save", None, QtGui.QApplication.UnicodeUTF8)) + self.actionOpen.setText(QtGui.QApplication.translate("MainWindow", "Open", None, QtGui.QApplication.UnicodeUTF8)) + self.actionHelp.setText(QtGui.QApplication.translate("MainWindow", "Help", None, QtGui.QApplication.UnicodeUTF8)) + self.actionExport_to_SVG.setText(QtGui.QApplication.translate("MainWindow", "Export to SVG...", None, QtGui.QApplication.UnicodeUTF8)) + diff --git a/pykat/node_network.py b/pykat/node_network.py index 2ad607f0a2a8ea30120a176c8340a5b4ead071fd..8d5be82a2761e7dff4f8b81b9d6acbc3f4e8c00e 100644 --- a/pykat/node_network.py +++ b/pykat/node_network.py @@ -1,332 +1,332 @@ -# -*- coding: utf-8 -*- -""" -Created on Sun Jan 27 10:02:41 2013 - -@author: Daniel -""" -import exceptions -import pykat.gui.graphics -import pykat.exceptions as pkex -from pykat.components import Component -from pykat.detectors import Detector -from pykat.utilities.optics.gaussian_beams import gauss_param - -class NodeNetwork(object): - def __init__(self, kat): - self.__nodes = {} - self.__kat = kat - 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 - - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) - - 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 = self.createNode(name) - self.connectNodeToComp(n, comp, do_callback=False) - list.append(n) - - self.__componentNodes[comp.id] = tuple(list) - self.__componentCallback[comp.id] = change_callback - - change_callback() - - def replaceNode(self, comp, node_old, node_new): - - if node_new.components.count(None) == 0: - raise pkex.BasePyKatException("New node already connected to two components") - - if comp not in node_old.components: - raise pkex.BasePyKatException("Old node not attached to component") - - if comp in node_new.components: - raise pkex.BasePyKatException("New node already attached to component") - - # add component to new node component list - new_node_comps = list(node_new.components) - new_node_comps[new_node_comps.index(None)] = comp - self.__nodeComponents[node_new.id] = tuple(new_node_comps) - - # remove component from old node list - old_node_comps = list(node_old.components) - old_node_comps[old_node_comps.index(comp)] = None - self.__nodeComponents[node_old.id] = tuple(old_node_comps) - - comp_nodes = list(comp.nodes) - comp_nodes[comp_nodes.index(node_old)] = node_new - self.__componentNodes[comp.id] = tuple(comp_nodes) - - # if old node is no longer connected to anything then delete it - if node_old.components.count(None) == 2: - self.removeNode(node_old) - - self.__componentCallback[comp.id]() - - def connectNodeToComp(self, node, comp, do_callback=True): - if node.id in self.__nodeComponents: - comps = self.__nodeComponents[node.id] - else: - 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") - - l = list(comps) - - if l[0] == None: - l[0] = comp - elif l[1] == None: - l[1] = comp - else: - raise pkex.BasePyKatException("Connected to two coponents already") - - 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: - # then this node already exists - return self.__nodes[node_name] - else: - 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 - self.__nodeComponents[n.id] = (None, None) - 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: - raise exceptions.RuntimeError("Trying to remove node {0} when it has not been added".format(node.name)) - - C = self.getNodeComponents(node) - - 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(node.getDetectors()) > 0: - raise exceptions.RuntimeError("Cannot remove a node which is attached to detectors still") - - self.__remove_node_attr(node) - del self.__nodes[node.name] - - def hasNode(self, name): - return (name in self.__nodes) - - def getNodes(self): - return self.__nodes.copy() - - def dumpInfo(self): - - for name in self.__nodes: - - n = self.__nodes[name] - - items = n.getComponents() - comp = items[0][:] - det = items[1] - - if comp[0] == None: - comp1 = 'dump' - else: - comp1 = comp[0].name - - if comp[1] == None: - comp2 = 'dump' - else: - comp2 = comp[1].name - - detectors = "" - - if len(det) > 0: - detectors = "Detectors: " - - for d in det: - detectors = detectors + d.name + " " - - print "node: {0} connected:{1} {2}->{3} {4}".format( - n.name,n.isConnected(),comp1, comp2, detectors) - - def getComponentNodes(self, comp): - return self.__componentNodes[comp.id] - - def getNodeComponents(self, node): - return self.__nodeComponents[node.id] - - 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_attr(name) - - setattr(self.__class__, name, property(fget)) - setattr(self, '__node_' + name, node) - - def __remove_node_attr(self, node): - if not isinstance(node, Node): - raise exceptions.ValueError("Argument is not of type Node") - - name = node.name - delattr(self, '__node_' + name) - delattr(self.__class__, name) - - def __get_node_attr(self, name): - return getattr(self, '__node_' + name) - - def __getitem__(self, value): - return self.__nodes[str(value)] - - def __contains__(self, value): - return value in self.__nodes - - -class Node(object): - - def __init__(self, name, network, id): - self._detectors = [] - self.__name = name - self._item = None - self._network = network - self.__q_x = None - self.__q_y = None - self.__q_comp = None - self.__id = id - - def __str__(self): return self.__name - - @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 q(self): - if self.__q_x == self.__q_y: - return self.__q_x - else: - return (self.__q_x, self.__q_y) - - @property - def qx(self): return self.__q_x - @property - def qy(self): return self.__q_y - - def removeGauss(): - self.__q_x = None - self.__q_y = None - self.__q_comp = None - - def setGauss(self, component, *args): - self.__q_comp = component - - if len(args) == 1: - self.__q_x = gauss_param(q=args[0]) - self.__q_y = gauss_param(q=args[0]) - elif len(args) == 2: - self.__q_x = gauss_param(q=args[0]) - self.__q_y = gauss_param(q=args[1]) - else: - raise pkex.BasePyKatException("Must specify either 1 Gaussian beam parameter or 2 for astigmatic beams") - - def getFinesseText(self): - if self.__q_x is None or self.__q_y is None or self.__q_comp is None: - return [] - - rtn = [] - - if self.__q_x == self.__q_y: - rtn.append("gauss* g_{node} {comp} {node} {z} {zr}".format(node=self.name, comp=self.__q_comp.name, z=self.__q_x.real, zr=self.__q_x.imag)) - else: - rtn.append("gauss* g_{node} {comp} {node} {zx} {zrx} {zy} {zry}".format(node=self.name, comp=self.__q_comp.name, zx=self.__q_x.real, zrx=self.__q_x.imag, zy=self.__q_y.real, zry=self.__q_y.imag)) - - return rtn - - def isConnected(self): - if (self.components[0] is not None) and (self.components[1] is not None): - return True - else: - return False - - def remove(self): - self._network.removeNode(self) - - if self._item != None: - self._item.scene().removeItem(self._item) - - 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) - - return self._item - - 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 - """ - comps = self.components - - if obj == comps[0]: - if comps[1] == None: - ix = -1 - else: - ix = comps[1].nodes.index(self) - - return [True, comps[1], ix] - - elif obj == comps[1]: - if comps[0] == None: - ix = -1 - else: - ix = comps[0].nodes.index(self) - - return [True, comps[0], ix] - else: - return [False, None] - - @property - def name(self): return self.__name - - -class DumpNode(Node): - def __init__(self): - Node.__init__(self, 'dump', None, -1) - +# -*- coding: utf-8 -*- +""" +Created on Sun Jan 27 10:02:41 2013 + +@author: Daniel +""" +import exceptions +import pykat.gui.graphics +import pykat.exceptions as pkex +from pykat.components import Component +from pykat.detectors import Detector +from pykat.utilities.optics.gaussian_beams import gauss_param + +class NodeNetwork(object): + def __init__(self, kat): + self.__nodes = {} + self.__kat = kat + 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 + + cls = type(self) + self.__class__ = type(cls.__name__, (cls,), {}) + + 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 = self.createNode(name) + self.connectNodeToComp(n, comp, do_callback=False) + list.append(n) + + self.__componentNodes[comp.id] = tuple(list) + self.__componentCallback[comp.id] = change_callback + + change_callback() + + def replaceNode(self, comp, node_old, node_new): + + if node_new.components.count(None) == 0: + raise pkex.BasePyKatException("New node already connected to two components") + + if comp not in node_old.components: + raise pkex.BasePyKatException("Old node not attached to component") + + if comp in node_new.components: + raise pkex.BasePyKatException("New node already attached to component") + + # add component to new node component list + new_node_comps = list(node_new.components) + new_node_comps[new_node_comps.index(None)] = comp + self.__nodeComponents[node_new.id] = tuple(new_node_comps) + + # remove component from old node list + old_node_comps = list(node_old.components) + old_node_comps[old_node_comps.index(comp)] = None + self.__nodeComponents[node_old.id] = tuple(old_node_comps) + + comp_nodes = list(comp.nodes) + comp_nodes[comp_nodes.index(node_old)] = node_new + self.__componentNodes[comp.id] = tuple(comp_nodes) + + # if old node is no longer connected to anything then delete it + if node_old.components.count(None) == 2: + self.removeNode(node_old) + + self.__componentCallback[comp.id]() + + def connectNodeToComp(self, node, comp, do_callback=True): + if node.id in self.__nodeComponents: + comps = self.__nodeComponents[node.id] + else: + 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") + + l = list(comps) + + if l[0] == None: + l[0] = comp + elif l[1] == None: + l[1] = comp + else: + raise pkex.BasePyKatException("Connected to two coponents already") + + 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: + # then this node already exists + return self.__nodes[node_name] + else: + 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 + self.__nodeComponents[n.id] = (None, None) + 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: + raise exceptions.RuntimeError("Trying to remove node {0} when it has not been added".format(node.name)) + + C = self.getNodeComponents(node) + + 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(node.getDetectors()) > 0: + raise exceptions.RuntimeError("Cannot remove a node which is attached to detectors still") + + self.__remove_node_attr(node) + del self.__nodes[node.name] + + def hasNode(self, name): + return (name in self.__nodes) + + def getNodes(self): + return self.__nodes.copy() + + def dumpInfo(self): + + for name in self.__nodes: + + n = self.__nodes[name] + + items = n.getComponents() + comp = items[0][:] + det = items[1] + + if comp[0] == None: + comp1 = 'dump' + else: + comp1 = comp[0].name + + if comp[1] == None: + comp2 = 'dump' + else: + comp2 = comp[1].name + + detectors = "" + + if len(det) > 0: + detectors = "Detectors: " + + for d in det: + detectors = detectors + d.name + " " + + print "node: {0} connected:{1} {2}->{3} {4}".format( + n.name,n.isConnected(),comp1, comp2, detectors) + + def getComponentNodes(self, comp): + return self.__componentNodes[comp.id] + + def getNodeComponents(self, node): + return self.__nodeComponents[node.id] + + 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_attr(name) + + setattr(self.__class__, name, property(fget)) + setattr(self, '__node_' + name, node) + + def __remove_node_attr(self, node): + if not isinstance(node, Node): + raise exceptions.ValueError("Argument is not of type Node") + + name = node.name + delattr(self, '__node_' + name) + delattr(self.__class__, name) + + def __get_node_attr(self, name): + return getattr(self, '__node_' + name) + + def __getitem__(self, value): + return self.__nodes[str(value)] + + def __contains__(self, value): + return value in self.__nodes + + +class Node(object): + + def __init__(self, name, network, id): + self._detectors = [] + self.__name = name + self._item = None + self._network = network + self.__q_x = None + self.__q_y = None + self.__q_comp = None + self.__id = id + + def __str__(self): return self.__name + + @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 q(self): + if self.__q_x == self.__q_y: + return self.__q_x + else: + return (self.__q_x, self.__q_y) + + @property + def qx(self): return self.__q_x + @property + def qy(self): return self.__q_y + + def removeGauss(): + self.__q_x = None + self.__q_y = None + self.__q_comp = None + + def setGauss(self, component, *args): + self.__q_comp = component + + if len(args) == 1: + self.__q_x = gauss_param(q=args[0]) + self.__q_y = gauss_param(q=args[0]) + elif len(args) == 2: + self.__q_x = gauss_param(q=args[0]) + self.__q_y = gauss_param(q=args[1]) + else: + raise pkex.BasePyKatException("Must specify either 1 Gaussian beam parameter or 2 for astigmatic beams") + + def getFinesseText(self): + if self.__q_x is None or self.__q_y is None or self.__q_comp is None: + return [] + + rtn = [] + + if self.__q_x == self.__q_y: + rtn.append("gauss* g_{node} {comp} {node} {z} {zr}".format(node=self.name, comp=self.__q_comp.name, z=self.__q_x.real, zr=self.__q_x.imag)) + else: + rtn.append("gauss* g_{node} {comp} {node} {zx} {zrx} {zy} {zry}".format(node=self.name, comp=self.__q_comp.name, zx=self.__q_x.real, zrx=self.__q_x.imag, zy=self.__q_y.real, zry=self.__q_y.imag)) + + return rtn + + def isConnected(self): + if (self.components[0] is not None) and (self.components[1] is not None): + return True + else: + return False + + def remove(self): + self._network.removeNode(self) + + if self._item != None: + self._item.scene().removeItem(self._item) + + 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) + + return self._item + + 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 + """ + comps = self.components + + if obj == comps[0]: + if comps[1] == None: + ix = -1 + else: + ix = comps[1].nodes.index(self) + + return [True, comps[1], ix] + + elif obj == comps[1]: + if comps[0] == None: + ix = -1 + else: + ix = comps[0].nodes.index(self) + + return [True, comps[0], ix] + else: + return [False, None] + + @property + def name(self): return self.__name + + +class DumpNode(Node): + def __init__(self): + Node.__init__(self, 'dump', None, -1) + \ No newline at end of file diff --git a/pykat/param.py b/pykat/param.py index 6ae1f9a9b29c52d565ec0b6054329c31fa6a072e..9a13f0b458619c810dfd3ae9931f01a2c3447cbf 100644 --- a/pykat/param.py +++ b/pykat/param.py @@ -1,167 +1,167 @@ -from pykat.SIfloat import SIfloat -import abc -import pykat.exceptions as pkex - -class putable(object): - """ - Objects that inherit this should be able to have something `put` to it. - Essentially this means you could write Finesse commands like - - put this parameter value - """ - __metaclass__ = abc.ABCMeta - - def __init__(self, component_name, parameter_name, isPutable=True): - self._parameter_name = parameter_name - self._component_name = component_name - self._putter = None - self._isPutable = isPutable - - @property - def isPutable(self): return self._isPutable - - def put(self, var): - - if not isinstance(var, putter): - raise pkex.BasePyKatException("var was not something that can be `put` as a value") - - if self._putter != None: - self._putter.put_count -= 1 - - self._putter = var - self._putter.put_count += 1 - - def _getPutFinesseText(self): - rtn = [] - # if something is being put to this - if self._putter != None: - rtn.append("put {comp} {param} ${value}".format(comp=self._component_name, param=self._parameter_name, value=self._putter.put_name())) - - return rtn - -class putter(object): - """ - If an object can be put to something that is putable it should inherit this - object. - """ - - def __init__(self, put_name, isPutter=True): - self._put_name = put_name - self.put_count = 0 - self._isPutter = isPutter - - @property - def isPutter(self): return self._isPutter - - def put_name(self): return self._put_name - - -class Param(putable, putter): - - def __init__(self, name, owner, value, isPutable=True, isPutter=True, isTunable=True, var_name=None): - self._name = name - self._owner = owner - self._value = value - self._isPutter = isPutter - self._isTunable = isTunable - self._owner._register_param(self) - - if isPutter: - if var_name == None: - var_name = "var_{0}_{1}".format(owner.name, name) - - putter.__init__(self, var_name, isPutter) - - if isPutable: - putable.__init__(self, owner.name, name, isPutable) - - @property - def name(self): return self._name - - @property - def isTuneable(self): return self._isTunable - - @property - def value(self): return self._value - @value.setter - def value(self, value): - self._value = value - - def __str__(self): return str(self.value) - def __float__(self): return self.value - - def getFinesseText(self): - rtn = [] - - if self.isPutable: rtn.extend(self._getPutFinesseText()) - - # if this parameter is being put somewhere then we need to - # set it as a variable - if self.isPutter and self.put_count > 0: - rtn.append("set {put_name} {comp} {param}".format(put_name=self.put_name(), comp=self._owner.name, param=self.name)) - - return rtn - - def __mul__(self, a): - return self.value * a - - def __imul__(self, a): - return self.value * (a) - - __rmul__ = __mul__ - - def __add__(self, a): - return self.value + (a) - - def __iadd__(self, a): - return self.value + (a) - - __radd__ = __add__ - - def __sub__(self, a): - return self.value - (a) - - def __isub__(self, a): - return self.value - (a) - - __rsub__ = __sub__ - - def __div__(self, a): - return self.value / (a) - - def __idiv__(self, a): - return self.value / complex(a) - - def __pow__(self, q): - return self.value**q - - def __neg__(self): - return -self.value - - def __eq__(self, q): - return (q) == self.value - def __ne__(self, q): - return (q) != self.value - def __lt__(self, q): - return (q) > self.value - def __gt__(self, q): - return (q) < self.value - -class AttrParam(Param): - """ - Certain parameters of a component are set using the Finesse `attr` command. - - This inherits directly from a Param object so can be set whether this attribute - is putable or a putter. - - If the value pf the parameter is not 0 the attr command will be printed. - """ - def getFinesseText(self): - rtn = [] - - if self.value != 0: - rtn.append("attr {0} {1} {2}".format(self._owner.name, self.name, self.value)) - - rtn.extend(super(AttrParam, self).getFinesseText()) - +from pykat.SIfloat import SIfloat +import abc +import pykat.exceptions as pkex + +class putable(object): + """ + Objects that inherit this should be able to have something `put` to it. + Essentially this means you could write Finesse commands like + + put this parameter value + """ + __metaclass__ = abc.ABCMeta + + def __init__(self, component_name, parameter_name, isPutable=True): + self._parameter_name = parameter_name + self._component_name = component_name + self._putter = None + self._isPutable = isPutable + + @property + def isPutable(self): return self._isPutable + + def put(self, var): + + if not isinstance(var, putter): + raise pkex.BasePyKatException("var was not something that can be `put` as a value") + + if self._putter != None: + self._putter.put_count -= 1 + + self._putter = var + self._putter.put_count += 1 + + def _getPutFinesseText(self): + rtn = [] + # if something is being put to this + if self._putter != None: + rtn.append("put {comp} {param} ${value}".format(comp=self._component_name, param=self._parameter_name, value=self._putter.put_name())) + + return rtn + +class putter(object): + """ + If an object can be put to something that is putable it should inherit this + object. + """ + + def __init__(self, put_name, isPutter=True): + self._put_name = put_name + self.put_count = 0 + self._isPutter = isPutter + + @property + def isPutter(self): return self._isPutter + + def put_name(self): return self._put_name + + +class Param(putable, putter): + + def __init__(self, name, owner, value, isPutable=True, isPutter=True, isTunable=True, var_name=None): + self._name = name + self._owner = owner + self._value = value + self._isPutter = isPutter + self._isTunable = isTunable + self._owner._register_param(self) + + if isPutter: + if var_name == None: + var_name = "var_{0}_{1}".format(owner.name, name) + + putter.__init__(self, var_name, isPutter) + + if isPutable: + putable.__init__(self, owner.name, name, isPutable) + + @property + def name(self): return self._name + + @property + def isTuneable(self): return self._isTunable + + @property + def value(self): return self._value + @value.setter + def value(self, value): + self._value = value + + def __str__(self): return str(self.value) + def __float__(self): return self.value + + def getFinesseText(self): + rtn = [] + + if self.isPutable: rtn.extend(self._getPutFinesseText()) + + # if this parameter is being put somewhere then we need to + # set it as a variable + if self.isPutter and self.put_count > 0: + rtn.append("set {put_name} {comp} {param}".format(put_name=self.put_name(), comp=self._owner.name, param=self.name)) + + return rtn + + def __mul__(self, a): + return self.value * a + + def __imul__(self, a): + return self.value * (a) + + __rmul__ = __mul__ + + def __add__(self, a): + return self.value + (a) + + def __iadd__(self, a): + return self.value + (a) + + __radd__ = __add__ + + def __sub__(self, a): + return self.value - (a) + + def __isub__(self, a): + return self.value - (a) + + __rsub__ = __sub__ + + def __div__(self, a): + return self.value / (a) + + def __idiv__(self, a): + return self.value / complex(a) + + def __pow__(self, q): + return self.value**q + + def __neg__(self): + return -self.value + + def __eq__(self, q): + return (q) == self.value + def __ne__(self, q): + return (q) != self.value + def __lt__(self, q): + return (q) > self.value + def __gt__(self, q): + return (q) < self.value + +class AttrParam(Param): + """ + Certain parameters of a component are set using the Finesse `attr` command. + + This inherits directly from a Param object so can be set whether this attribute + is putable or a putter. + + If the value pf the parameter is not 0 the attr command will be printed. + """ + def getFinesseText(self): + rtn = [] + + if self.value != 0: + rtn.append("attr {0} {1} {2}".format(self._owner.name, self.name, self.value)) + + rtn.extend(super(AttrParam, self).getFinesseText()) + return rtn \ No newline at end of file diff --git a/pykat/plotting.py b/pykat/plotting.py index 8deec541fc4ffb73675e7a4fae8c0657a20b0a9c..10e6cc1a7ba9a1662c2743962778d1fe06e56bcf 100644 --- a/pykat/plotting.py +++ b/pykat/plotting.py @@ -1,83 +1,83 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Feb 02 10:35:04 2013 - -@author: Daniel -""" - -import numpy as np -import matplotlib - -BACKEND = 'Qt4Agg' -matplotlib.use(BACKEND) - -from matplotlib import rc -import matplotlib.pyplot as plt - -mainpid = -1 - -def plot1D(run, title=""): - - rc('font', **pp.font) - rc('xtick',labelsize=pp.TICK_SIZE) - rc('ytick',labelsize=pp.TICK_SIZE) - rc('text', usetex=pp.USETEX) - rc('axes', labelsize = pp.LABEL_SIZE) - - fig=plt.figure() - fig.set_size_inches(pp.fig_size) - fig.set_dpi(pp.FIG_DPI) - - ax1 = fig.add_subplot(111) - ax1.set_xlim(np.min(run.x),np.max(run.x)) - traces = ax1.plot(run.x,run.y) - ax1.grid(pp.GRID) - - ax1.set_xlabel(run.xlabel) - legends = run.ylabels - ax1.legend(traces, legends, loc=0, shadow=pp.SHADOW,prop={'size':pp.LEGEND_SIZE}) - - if pp.PRINT_TITLE: - plt.title(title) - - if pp.SCREEN_TITLE: - fig.canvas.manager.set_window_title(title) - else: - fig.canvas.manager.set_window_title('') - - #plt.ion() - plt.show() - -class pp(): - # set some gobal settings first - BACKEND = 'Qt4Agg' # matplotlib backend - FIG_DPI=90 # DPI of on sceen plot - # Some help in calculating good figure size for Latex - # documents. Starting with plot size in pt, - # get this from LaTeX using \showthe\columnwidth - fig_width_pt = 484.0 - inches_per_pt = 1.0/72.27 # Convert TeX pt to inches - golden_mean = (np.sqrt(5)-1.0)/2.0 # Aesthetic ratio - fig_width = fig_width_pt*inches_per_pt # width in inches - fig_height = fig_width*golden_mean # height in inches - fig_size = [fig_width,fig_height] - # some plot options: - LINEWIDTH = 1 # linewidths of traces in plot - AA = True # antialiasing of traces - USETEX = False # use Latex encoding in text - SHADOW = False # shadow of legend box - GRID = True # grid on or off - # font sizes for normal text, tick labels and legend - FONT_SIZE = 10 # size of normal text - TICK_SIZE = 10 # size of tick labels - LABEL_SIZE = 10 # size of axes labels - LEGEND_SIZE = 10 # size of legend - # font family and type - font = {'family':'sans-serif','sans-serif':['Helvetica'],'size':FONT_SIZE} - DPI=300 # DPI for saving via savefig - # print options given to savefig command: - print_options = {'dpi':DPI, 'transparent':True, 'bbox_inches':'tight', 'pad_inches':0.1} - # for Palatino and other serif fonts use: - #font = {'family':'serif','serif':['Palatino']} - SCREEN_TITLE = True # show title on screen? +# -*- coding: utf-8 -*- +""" +Created on Sat Feb 02 10:35:04 2013 + +@author: Daniel +""" + +import numpy as np +import matplotlib + +BACKEND = 'Qt4Agg' +matplotlib.use(BACKEND) + +from matplotlib import rc +import matplotlib.pyplot as plt + +mainpid = -1 + +def plot1D(run, title=""): + + rc('font', **pp.font) + rc('xtick',labelsize=pp.TICK_SIZE) + rc('ytick',labelsize=pp.TICK_SIZE) + rc('text', usetex=pp.USETEX) + rc('axes', labelsize = pp.LABEL_SIZE) + + fig=plt.figure() + fig.set_size_inches(pp.fig_size) + fig.set_dpi(pp.FIG_DPI) + + ax1 = fig.add_subplot(111) + ax1.set_xlim(np.min(run.x),np.max(run.x)) + traces = ax1.plot(run.x,run.y) + ax1.grid(pp.GRID) + + ax1.set_xlabel(run.xlabel) + legends = run.ylabels + ax1.legend(traces, legends, loc=0, shadow=pp.SHADOW,prop={'size':pp.LEGEND_SIZE}) + + if pp.PRINT_TITLE: + plt.title(title) + + if pp.SCREEN_TITLE: + fig.canvas.manager.set_window_title(title) + else: + fig.canvas.manager.set_window_title('') + + #plt.ion() + plt.show() + +class pp(): + # set some gobal settings first + BACKEND = 'Qt4Agg' # matplotlib backend + FIG_DPI=90 # DPI of on sceen plot + # Some help in calculating good figure size for Latex + # documents. Starting with plot size in pt, + # get this from LaTeX using \showthe\columnwidth + fig_width_pt = 484.0 + inches_per_pt = 1.0/72.27 # Convert TeX pt to inches + golden_mean = (np.sqrt(5)-1.0)/2.0 # Aesthetic ratio + fig_width = fig_width_pt*inches_per_pt # width in inches + fig_height = fig_width*golden_mean # height in inches + fig_size = [fig_width,fig_height] + # some plot options: + LINEWIDTH = 1 # linewidths of traces in plot + AA = True # antialiasing of traces + USETEX = False # use Latex encoding in text + SHADOW = False # shadow of legend box + GRID = True # grid on or off + # font sizes for normal text, tick labels and legend + FONT_SIZE = 10 # size of normal text + TICK_SIZE = 10 # size of tick labels + LABEL_SIZE = 10 # size of axes labels + LEGEND_SIZE = 10 # size of legend + # font family and type + font = {'family':'sans-serif','sans-serif':['Helvetica'],'size':FONT_SIZE} + DPI=300 # DPI for saving via savefig + # print options given to savefig command: + print_options = {'dpi':DPI, 'transparent':True, 'bbox_inches':'tight', 'pad_inches':0.1} + # for Palatino and other serif fonts use: + #font = {'family':'serif','serif':['Palatino']} + SCREEN_TITLE = True # show title on screen? PRINT_TITLE = False # show title in saved file? \ No newline at end of file diff --git a/pykat/utils.py b/pykat/utils.py index 328fb2933d34ba8191a7ca0b65608525675e7d1c..116ab67b832f8a4258e4c4b9a2a33bc5c848af4a 100644 --- a/pykat/utils.py +++ b/pykat/utils.py @@ -1,13 +1,13 @@ -# -*- coding: utf-8 -*- -""" -Created on Sat Feb 02 12:17:50 2013 - -@author: Daniel -""" -from pykat.components import * - -def isSpace(obj): - if obj == None: - return False - else: +# -*- coding: utf-8 -*- +""" +Created on Sat Feb 02 12:17:50 2013 + +@author: Daniel +""" +from pykat.components import * + +def isSpace(obj): + if obj == None: + return False + else: return isinstance(obj,space) \ No newline at end of file