diff --git a/pykat/__init__.py b/pykat/__init__.py index 4dda77b69059678d5f39a95c805adca1d778d013..f91c9789644c76b1be75846bf1fe1a8e4f6f2e51 100644 --- a/pykat/__init__.py +++ b/pykat/__init__.py @@ -1,7 +1,18 @@ +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function +from __future__ import unicode_literals __version__ = "0.6.2" # This flag is used to switch on the gui features in pkat at import time USE_GUI = False +HAS_OPTIVIS = False +import imp +try: + imp.find_module('optivis') + HAS_OPTIVIS = True +except ImportError: + HAS_OPTIVIS = False import pykat.exceptions as pkex diff --git a/pykat/components.py b/pykat/components.py index f21bb74135a79552a5af42d92bf3990479ab6907..42d63af071e8b5f903150b3b3581f442089c0b63 100644 --- a/pykat/components.py +++ b/pykat/components.py @@ -9,7 +9,7 @@ from __future__ import division from __future__ import print_function from __future__ import unicode_literals -from pykat import USE_GUI, NoGUIException +from pykat import USE_GUI, HAS_OPTIVIS, NoGUIException import pykat.external.six as six @@ -21,15 +21,22 @@ import pykat from pykat.node_network import * from pykat.exceptions import * import abc +import copy +from collections import OrderedDict + +if HAS_OPTIVIS: + import optivis.bench.components as optivis_components + from optivis.view.canvas import OptivisCanvasItemDataType + from optivis.bench.labels import Label as optivis_label + from optivis.geometry import Coordinates as optivis_coord + import PyQt4 from pykat.SIfloat import * from pykat.param import Param, AttrParam import weakref import pykat.exceptions as pkex -from copy import deepcopy -next_component_id = 1 -from pykat import USE_GUI, NoGUIException +next_component_id = 1 if USE_GUI: import pykat.gui.resources @@ -78,8 +85,16 @@ class NodeGaussSetter(object): class Component(object): __metaclass__ = abc.ABCMeta - - def __init__(self, name): + + def __new__(cls, *args, **kwargs): + # This creates an instance specific class for the component + # this enables us to add properties to instances rather than + # all classes + return object.__new__(type(cls.__name__, (cls,), {}), *args, **kwargs) + + def __init__(self, name=None): + + self._optivis_component = None self.__name = name self._svgItem = None self._requested_node_names = [] @@ -88,18 +103,26 @@ class Component(object): self._params = [] self.__removed = False self._default_fsig_param = None + self.optivisLabelContent = None # 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 __deepcopy__(self, memo): + """ + When deep copying a kat object we need to take into account + the instance specific properties. + """ + result = self.__class__.__new__(self.__class__) + result.__dict__ = copy.deepcopy(self.__dict__, memo) + + result.__update_node_setters + + return result + def _register_param(self, param): self._params.append(param) @@ -209,20 +232,38 @@ class Component(object): del self._params[:] self.__removed = True - - def __deepcopy__(self, memo): - cls = self.__class__ - result = cls.__new__(cls) - memo[id(self)] = result + + def getOptivisParameterDict(self): + if len(self._params) == 0: + return None + + d = OrderedDict() - for k, v in self.__dict__.items(): - setattr(result, k, deepcopy(v, memo)) + for p in self._params: + d[p.name] = OptivisCanvasItemDataType.TEXTBOX - for p in result._params: - p._updateOwner(result) + return d - return result - + def getOptivisTooltip(self): + tooltip = "Name: %s" % self.name + + for p in self._params: + if p.value is not None: + tooltip += "\n%s = %s" %(p.name, str(p.value)) + + return tooltip + + def setOptivisLabelContent(self): + """ + Sets default Optivis label contents + """ + + if self.optivisLabelContent is None: + self.optivisLabelContent = {} + + self.optivisLabelContent["Name"] = self.name + + class AbstractMirrorComponent(Component): __metaclass__ = abc.ABCMeta @@ -258,12 +299,12 @@ class AbstractMirrorComponent(Component): self.__rxmech = AttrParam("rxmech", self, rxmech) self.__rymech = AttrParam("rymech", self, rymech) - self.__z = Param("z", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="z") - self.__rx = Param("rx", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="rx") - self.__ry = Param("ry", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="ry") - self.__Fz = Param("Fz", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Fz") - self.__Frx = Param("Frx", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Frx") - self.__Fry = Param("Fry", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Fry") + self.__z = Param("z", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="z") + self.__rx = Param("rx", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="rx") + self.__ry = Param("ry", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="ry") + self.__Fz = Param("Fz", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Fz") + self.__Frx = Param("Frx", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Frx") + self.__Fry = Param("Fry", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Fry") self._default_fsig_param = self.__phi @@ -449,6 +490,37 @@ class mirror(AbstractMirrorComponent): rtn.extend(p.getFinesseText()) return rtn + + def getOptivisComponent(self): + self.setOptivisLabelContent() + + if self._optivis_component is None: + self._optivis_component = optivis_components.CavityMirror(name=self.name, aoi=0, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self)) + + lbl = optivis_label(text="", position=optivis_coord(0, -1), item=self._optivis_component) + lbl.content["Name"] = self.name + self._optivis_component.labels.append(lbl) + + return self._optivis_component + + def getOptivisNode(self, mode, kat_node): + mode = mode.lower() + + if mode != "input" and mode.lower() != "output": + raise pkex.BasePyKatException("Mode must be either input or output not %s" % mode) + + if mode == "input": + if kat_node is self.nodes[0]: + return self._optivis_component.getInputNode("fr") + else: + return self._optivis_component.getInputNode("bk") + + elif mode == "output": + if kat_node is self.nodes[0]: + return self._optivis_component.getOutputNode("fr") + else: + return self._optivis_component.getOutputNode("bk") + def getQGraphicsItem(self): if not USE_GUI: @@ -482,7 +554,40 @@ class beamSplitter(AbstractMirrorComponent): self.alpha = values[key] else: raise pkex.BasePyKatException("No attribute {0} for mirrors".format(key)) - + + def getOptivisComponent(self): + self.setOptivisLabelContent() + + if self._optivis_component is None: + self._optivis_component = optivis_components.BeamSplitter(name=self.name, aoi=-self.alpha, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self)) + + return self._optivis_component + + def getOptivisNode(self, mode, kat_node): + mode = mode.lower() + + if mode != "input" and mode.lower() != "output": + raise pkex.BasePyKatException("Mode must be either input or output") + + if mode == "input": + if kat_node is self.nodes[0]: + return self._optivis_component.getInputNode("frA") + elif kat_node is self.nodes[1]: + return self._optivis_component.getInputNode("frB") + elif kat_node is self.nodes[2]: + return self._optivis_component.getInputNode("bkB") + elif kat_node is self.nodes[3]: + return self._optivis_component.getInputNode("bkA") + elif mode == "output": + if kat_node is self.nodes[0]: + return self._optivis_component.getOutputNode("frB") + elif kat_node is self.nodes[1]: + return self._optivis_component.getOutputNode("frA") + elif kat_node is self.nodes[2]: + return self._optivis_component.getOutputNode("bkA") + elif kat_node is self.nodes[3]: + return self._optivis_component.getOutputNode("bkB") + @staticmethod def parseFinesseText(text): values = text.split() @@ -579,6 +684,18 @@ class space(Component): @gy.setter def gy(self,value): self.__gy.value = SIfloat(value) + def connectingComponents(self): + """ + Returns the two components that this space connects. + """ + a = list(self.nodes[0].components + self.nodes[1].components) + a = [value for value in a if value != self] + + if len(a) != 2: + raise pkex.BasePyKatException("Space should only connect 2 components") + + return a + def parseAttributes(self, values): for key in values.keys(): @@ -802,7 +919,33 @@ class isolator(Component): rtn.extend(p.getFinesseText()) return rtn - + + def getOptivisComponent(self): + self.setOptivisLabelContent() + + if self._optivis_component is None: + self._optivis_component = optivis_components.FaradayIsolator(name=self.name, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self)) + + return self._optivis_component + + def getOptivisNode(self, mode, kat_node): + mode = mode.lower() + + if mode != "input" and mode.lower() != "output": + raise pkex.BasePyKatException("Mode must be either input or output") + + if mode == "input": + if kat_node is self.nodes[0]: + return self._optivis_component.getInputNode("fr") + elif kat_node is self.nodes[1]: + return self._optivis_component.getInputNode("bk") + elif mode == "output": + if kat_node is self.nodes[0]: + return self._optivis_component.getnOutputNode("fr") + elif kat_node is self.nodes[1]: + return self._optivis_component.getOutputNode("bk") + + def getQGraphicsItem(self): if not USE_GUI: raise NoGUIException @@ -847,7 +990,32 @@ class lens(Component): rtn.extend(p.getFinesseText()) return rtn + + def getOptivisComponent(self): + self.setOptivisLabelContent() + if self._optivis_component is None: + self._optivis_component = optivis_components.ConvexLens(name=self.name, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self)) + + return self._optivis_component + + def getOptivisNode(self, mode, kat_node): + mode = mode.lower() + + if mode != "input" and mode.lower() != "output": + raise pkex.BasePyKatException("Mode must be either input or output") + + if mode == "input": + if kat_node is self.nodes[0]: + return self._optivis_component.getInputNode("fr") + elif kat_node is self.nodes[1]: + return self._optivis_component.getInputNode("bk") + elif mode == "output": + if kat_node is self.nodes[0]: + return self._optivis_component.getnOutputNode("fr") + elif kat_node is self.nodes[1]: + return self._optivis_component.getOutputNode("bk") + def getQGraphicsItem(self): if not USE_GUI: raise NoGUIException @@ -929,6 +1097,31 @@ class modulator(Component): return rtn + def getOptivisComponent(self): + self.setOptivisLabelContent() + + if self._optivis_component is None: + self._optivis_component = optivis_components.ElectroopticModulator(name=self.name, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self)) + + return self._optivis_component + + def getOptivisNode(self, mode, kat_node): + mode = mode.lower() + + if mode != "input" and mode.lower() != "output": + raise pkex.BasePyKatException("Mode must be either input or output") + + if mode == "input": + if kat_node is self.nodes[0]: + return self._optivis_component.getInputNode("fr") + elif kat_node is self.nodes[1]: + return self._optivis_component.getInputNode("bk") + elif mode == "output": + if kat_node is self.nodes[0]: + return self._optivis_component.getnOutputNode("fr") + elif kat_node is self.nodes[1]: + return self._optivis_component.getOutputNode("bk") + def getQGraphicsItem(self): if not USE_GUI: raise NoGUIException @@ -998,7 +1191,29 @@ class laser(Component): rtn.extend(p.getFinesseText()) return rtn - + + def getOptivisComponent(self): + self.setOptivisLabelContent() + + if self._optivis_component is None: + self._optivis_component = optivis_components.Laser(name=self.name, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self)) + lbl = optivis_label(text="", position=optivis_coord(0, -1), item=self._optivis_component) + lbl.content["Name"] = self.name + self._optivis_component.labels.append(lbl) + + return self._optivis_component + + def getOptivisNode(self, mode, kat_node): + mode = mode.lower() + + if mode != "input" and mode.lower() != "output": + raise pkex.BasePyKatException("Mode must be either input or output") + + if mode == "input": + return None + elif mode == "output": + return self._optivis_component.getOutputNode("out") + def getQGraphicsItem(self): if not USE_GUI: raise NoGUIException diff --git a/pykat/detectors.py b/pykat/detectors.py index acca83ca2b6f3231bd78e0f4d9129e207c4f3c79..a14bfbc1618cc384829b03bda7a56cf7edbfed50 100644 --- a/pykat/detectors.py +++ b/pykat/detectors.py @@ -11,11 +11,11 @@ from __future__ import unicode_literals import pykat.external.six as six if six.PY2: - import exceptions + import exceptions import abc from pykat.node_network import * -from pykat.param import Param +from pykat.param import Param, AttrParam from pykat.SIfloat import SIfloat import pykat.external.six as six @@ -356,7 +356,13 @@ class bp(Detector1): class pd(Detector1): - def __init__(self, name, num_demods, node_name, senstype=None, alternate_beam=False, pdtype=None, **kwargs): + def __new__(cls, *args, **kwargs): + # This creates an instance specific class for the component + # this enables us to add properties to instances rather than + # all classes + return object.__new__(type(cls.__name__, (cls,), {}), *args, **kwargs) + + def __init__(self, name=None, num_demods=1, node_name=None, senstype=None, alternate_beam=False, pdtype=None, **kwargs): BaseDetector.__init__(self, name, node_name) self.__num_demods = num_demods @@ -402,12 +408,25 @@ class pd(Detector1): elif i<num_demods-1: raise pkex.BasePyKatException("Missing demodulation phase {0} (phi{0})".format(i+1)) - # define new class for assigning new attributes - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) - + self.__set_demod_attrs() - + + + def __deepcopy__(self, memo): + """ + When deep copying a kat object we need to take into account + the instance specific properties. + """ + result = pd(self.name, self.num_demods, self.node.name) + memo[id(self)] = result + result.__dict__ = copy.deepcopy(self.__dict__, memo) + # Find all properties in class we are copying + # and deep copy these to the new class instance + for x in self.__class__.__dict__.items(): + if isinstance(x[1], property): + setattr(result.__class__, x[0], x[1]) + return result + @property def senstype(self): return self.__senstype @senstype.setter @@ -666,8 +685,8 @@ class qnoised(pd): class qshot(pd): def __init__(self, name, num_demods, node_name, alternate_beam=False, **kwargs): - super(qnoised, self).__init__(name, num_demods, node_name, alternate_beam=alternate_beam, pdtype=None, senstype=None, **kwargs) - + super(qshot, self).__init__(name, num_demods, node_name, alternate_beam=alternate_beam, pdtype=None, senstype=None, **kwargs) + @pd.pdtype.setter def pdtype(self, value): raise pkex.BasePyKatException("Setting pdtype is not possible with qshot detectors") diff --git a/pykat/finesse.py b/pykat/finesse.py index 610d64bcd6e4c20f883354caf6cad3d55f846853..0a614967295049cded3ee0c352d6fa7bcbfb89bd 100644 --- a/pykat/finesse.py +++ b/pykat/finesse.py @@ -34,6 +34,7 @@ import subprocess import tempfile import numpy as np import datetime +import time import pickle import pykat import warnings @@ -63,6 +64,8 @@ except ImportError: # not 2.6+ or is 3.x print("boom") pass """ + +from math import erfc, pi from collections import namedtuple, OrderedDict from pykat.node_network import NodeNetwork @@ -76,7 +79,13 @@ import pykat.external.six as six import pykat.exceptions as pkex -from pykat import USE_GUI, NoGUIException +from pykat import USE_GUI, HAS_OPTIVIS, NoGUIException + + +if HAS_OPTIVIS: + from optivis.bench.labels import Label as optivis_label + from optivis.geometry import Coordinates as optivis_coord + import PyQt4 if USE_GUI: from pykat.gui.gui import pyKatGUI @@ -174,9 +183,16 @@ def f__lkat_trace_callback(lkat, trace_info, getCavities, getNodes, getSpaces): gouyy = space.gouy_y) +def GUILength(L): + """ + Should scale the lengths in some way to handle km and mm for time being + """ + return L # * ( 40 * erfc(L/400.0) + 0.01) + class katRun(object): def __init__(self): - self.runDateTime = datetime.datetime.now() + self.runtime = None + self.StartDateTime = datetime.datetime.now() self.x = None self.y = None self.xlabel = None @@ -185,7 +201,7 @@ class katRun(object): self.katVersion = None self.yaxis = None - def plot(self): + def plot(self, logy=False): import pylab pylab.plot(self.x, self.y) @@ -206,9 +222,10 @@ class katRun(object): def __getitem__(self, value): idx = [i for i in range(len(self.ylabels)) if self.ylabels[i].split()[0] == str(value)] - + out = None + if len(idx) > 0: - out = self.y[:, idx] + #out = self.y[:, idx] if len(idx) == 1: if self.yaxis == "abs:deg": @@ -220,7 +237,10 @@ class katRun(object): out = self.y[:, idx[0]] * np.exp(1j*math.pi*self.y[:, idx[1]]/180.0) elif self.yaxis == "re:im": out = self.y[:, idx[0]] + 1j*self.y[:, idx[1]] - + + if out == None: + out = self.y[:, idx] + if out.size == 1: return out[0].squeeze() else: @@ -230,7 +250,8 @@ class katRun(object): class katRun2D(object): def __init__(self): - self.runDateTime = datetime.datetime.now() + self.runtime + self.startDateTime = datetime.datetime.now() self.x = None self.y = None self.z = None @@ -270,7 +291,7 @@ class Signals(object): self.__phase = Param("phase", self, SIfloat(phase)) self.__removed = False self.__signal = signal - + # unfortunatenly the target names for fsig are not the same as the # various parameter names of the components, e.g. mirror xbeta is x # for fsig. So we need to check here what type of component we are targetting @@ -397,11 +418,19 @@ class Block: Constant = namedtuple('Constant', 'name, value, usedBy') class kat(object): - - def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None): - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) + def __new__(cls, *args, **kwargs): + # This may seem like an arbitrary step but here we are creating a + # new class that is a base class of itself. This is because when + # the kat object adds new components it also adds properties for + # each of these. There properties are unique to each kat object, + # but properties are part of the class definition. Thus if two + # kat objects share the same class definition they also have the + # same properties regardless of whether they have the actual + # object added to it. So we create an instance specific class. + return object.__new__(type(pykat.finesse.kat.__name__, (pykat.finesse. + kat,), {}), *args, **kwargs) + 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 @@ -512,9 +541,9 @@ class kat(object): 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) + def timeCode(self): return self.__time_code + @timeCode.setter + def timeCode(self,value): self.__time_code = bool(value) @property def components(self): @@ -730,9 +759,9 @@ class kat(object): raise pkex.BasePyKatException("maxtem command `{0}` is incorrect.".format(line)) else: if v[1] == "off": - self.maxtem = -1 + self.maxtem = -1 else: - self.maxtem = int(v[1]) + self.maxtem = int(v[1]) elif(first == "trace"): v = line.split() if len(v) > 2: @@ -877,7 +906,8 @@ class kat(object): amp = float(v[6]) else: raise pkex.BasePyKatException("'{0}' isnot a valid fsig command".format(line)) - + + self.signals.f = freq self.signals.apply(comp._default_fsig(), amp, phase, name) else: @@ -912,8 +942,8 @@ class kat(object): cmd = "\n".join(self.generateKatScript()) return Process(target=f__lkat_process, args=(callback, cmd, kwargs)) - - def run(self, printout=0, printerr=0, plot=None, save_output=False, save_kat=False, kat_name=None, cmd_args=None) : + + def run(self, printout=0, printerr=0, plot=None, save_output=False, save_kat=False, kat_name=None, cmd_args=None, getTraceData=False): """ Runs the current simulation setup that has been built thus far. It returns a katRun or katRun2D object which is populated with the various @@ -925,6 +955,11 @@ class kat(object): save_kat (bool) - if true does not delete kat file kat_name (string) - name of kat file if needed, will be randomly generated otherwise cmd_args (list of strings) - command line flags to pass to FINESSE + getTraceData (bool) - If true a list of dictionaries is returned along with the + output file. Each dictionary is the result of the beam tracing + that Finesse performs, the keys are the node names and the values + are the x and y beam parameters. If no tracing is done a None + is returned. """ start = datetime.datetime.now() @@ -966,7 +1001,8 @@ class kat(object): r.yaxis = self.yaxis r.katScript = "".join(self.generateKatScript()) - + r.katScript += "time\n" + if (plot==None): # ensure we don't do any plotting. That should be handled # by user themselves @@ -996,6 +1032,9 @@ class kat(object): if cmd_args != None: cmd.extend(cmd_args) + + if getTraceData: + cmd.append('--trace') cmd.append('--no-backspace') # set default format so that less repeated numbers are printed to the @@ -1060,7 +1099,13 @@ class kat(object): [out,errpipe] = p.communicate() - + + _out = out.split("\n") + + for line in _out[::-1]: + if line.lstrip().startswith('computation time:'): + r.runtime = float(line.split(":")[1].replace("s","")) + if printout == 1: print (out) else: @@ -1080,8 +1125,52 @@ class kat(object): self.__prevrunfilename = katfile.name root = os.path.splitext(katfile.name) - base = os.path.basename(root[0]) + base = os.path.basename(root[0]) + path = os.path.split(katfile.name)[0] outfile = root[0] + ".out" + + traceData = None + + if getTraceData: + # First see if we have any trace files + + traceFiles = [file for file in os.listdir(path) if file.endswith(".trace") and file.startswith(base)] + + print("Found %i trace files" % len(traceFiles)) + print(path) + print(traceFiles) + + if len(traceFiles) > 0: + import fileinput + traceData = [] + + for file in traceFiles: + traceData.append({}) + ifile = fileinput.input(os.path.join(path, file)) + + for line in ifile: + line = line.strip() + + if len(line) > 0: + a = line.split(':', 1) + + if a[0].isdigit(): + print("Found %s" % a[0]) + + values = a[1].split() + + node_name = values[1].split("(")[0] + + line1x = ifile.readline() + line2x = ifile.readline() + line1y = ifile.readline() + line2y = ifile.readline() + + qx = line2x.strip().split()[0].split("=")[1] + qy = line2y.strip().split()[0].split("=")[1] + + traceData[-1][node_name] = (pykat.beam_param(q=complex(qx)), pykat.beam_param(q=complex(qy))) + if save_output: newoutfile = "{0}.out".format(base) @@ -1095,20 +1184,21 @@ class kat(object): os.rename(outfile, newoutfile) if self.verbose: print ("\nOutput data saved to '{0}'".format(newoutfile)) - - if hasattr(self, "x2axis") and self.noxaxis == False: - [r.x,r.y,r.z,hdr] = self.readOutFile(outfile) + + if len(self.detectors.keys()) > 0: + if hasattr(self, "x2axis") and self.noxaxis == False: + [r.x,r.y,r.z,hdr] = self.readOutFile(outfile) - r.xlabel = hdr[0] - r.ylabel = hdr[1] - r.zlabels = [s.strip() for s in hdr[2:]] - #r.zlabels = map(str.strip, hdr[2:]) - else: - [r.x,r.y,hdr] = self.readOutFile(outfile) + r.xlabel = hdr[0] + r.ylabel = hdr[1] + r.zlabels = [s.strip() for s in hdr[2:]] + #r.zlabels = map(str.strip, hdr[2:]) + else: + [r.x,r.y,hdr] = self.readOutFile(outfile) - r.xlabel = hdr[0] - r.ylabels = [s.strip() for s in hdr[1:]] - #r.ylabels = map(str.strip, hdr[1:]) // replaced 090415 adf + r.xlabel = hdr[0] + r.ylabels = [s.strip() for s in hdr[1:]] + #r.ylabels = map(str.strip, hdr[1:]) // replaced 090415 adf if save_kat: if kat_name == None: @@ -1136,18 +1226,27 @@ class kat(object): katfile.close() perfData = [] + + rtn = [r] 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]))) + perfData.append((vals[0], long(vals[1]), long(vals[2]))) + #perfData.append((vals[0], float(vals[1]), float(vals[2]), float(vals[3]))) - return [r, perfData] + rtn.append(perfData) + + if getTraceData: + rtn.append(traceData) + + if len(rtn) == 1: + return rtn[0] else: - return r - + return rtn + except pkex.FinesseRunError as ex: pkex.PrintError("Error from Finesse:", ex) except pkex.BasePyKatException as ex: @@ -1301,11 +1400,11 @@ class kat(object): if hasattr(self, "x2axis") and self.noxaxis == False: # 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] + x = data[0::(1+self.x2axis.steps),0].squeeze() 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) + z = data[:,2:].transpose().reshape(data.shape[1]-2, 1+self.xaxis.steps, 1+self.x2axis.steps).squeeze() # once you do this the data for y and x axes need swapping z = z.swapaxes(1,2) return [x, y, z, hdr] @@ -1314,19 +1413,22 @@ class kat(object): rows,cols = data.shape - x = data[:,0] + x = data[:,0].squeeze() y = data[:,1:cols] return [x, y, hdr] def removeLine(self, fragment) : """ - This will search all blocks by default and search for the string - fragment specified and remove it. This will only remove non-parsed - commands, it will not remove commands that have already been parsed - into the pykat structure, such as mirrors and beamsplitters, use the - kat.remove(...) function for that purpose. + This will search all blocks and search for the string + fragment specified and remove it. + WARNING: This will only remove non-parsed commands, it will not + remove commands that have already been parsed + into a pykat object, such as mirrors and beamsplitters, use + kat.remove or kat.component.remove() to delete parsed objects. """ + found = False + for key in self.__blocks: objs = self.__blocks[key].contents for obj in objs: @@ -1334,6 +1436,11 @@ class kat(object): if fragment in obj: print (" ** removing line '{0}'".format(obj)) objs.remove(obj) + found = True + + if not found: + pkex.BasePyKatException("The command fragment '%s' is not an extra line added to this kat object. Please check that the item you are trying to remove has not been parsed as a pykat object." % fragment) + def addLine(self, line, block=NO_BLOCK) : """ @@ -1474,7 +1581,169 @@ class kat(object): #out.append("pyterm no\n") return out + + def optivis(self): + if not HAS_OPTIVIS: + print("Optivis is not installed") + return None + import optivis.scene as scene + import optivis.bench.links as links + import optivis.view.canvas as canvas + + scene = scene.Scene(title="My pykat layout") + + # Run through again to add links + for c in self.getComponents(): + if not isinstance(c, pykat.components.space): + continue + + a = c.connectingComponents() + + # Need to handle where spaces don't connect two components but there is a loose + # node, which may or may not have detectors attached + if a[0] is None or a[1] is None: + continue + + c1 = a[0].getOptivisComponent() + c2 = a[1].getOptivisComponent() + + no = a[0].getOptivisNode("Output", c.nodes[0]) + ni = a[1].getOptivisNode("Input", c.nodes[1]) + + if no is None or ni is None: + raise pkex.BasePyKatException("Optivis node is None") + + c._optivis_component = links.Link(outputNode=no, inputNode=ni, length=c.L.value) + + c.label_node1 = optivis_label(text="", position=optivis_coord(-0.5, 0), item=c._optivis_component) + c.label_node2 = optivis_label(text="", position=optivis_coord( 0.5, 0), item=c._optivis_component) + label_name = optivis_label(text="", position=optivis_coord(0, -0.5), item=c._optivis_component) + label_name.content["Name"] = c.name + + c._optivis_component.labels.append(c.label_node1) + c._optivis_component.labels.append(c.label_node2) + c._optivis_component.labels.append(label_name) + scene.addLink(c._optivis_component) + + gui = canvas.Full(scene=scene) + + ### get menu bar from Optivis + menubar = gui.qMainWindow.menuBar() + + ### add new Pykat menu and menu items + pykatMenu = menubar.addMenu('&Pykat') + + # save + save = PyQt4.QtGui.QAction('Save', gui.qMainWindow) + save.setShortcut('Ctrl+S') + save.triggered.connect(lambda: self._optivis_doSave(gui)) + pykatMenu.addAction(save) + + # trace + trace = PyQt4.QtGui.QAction('Trace', gui.qMainWindow) + trace.setShortcut('Ctrl+T') + trace.triggered.connect(lambda: self._optivis_doTrace(gui)) + pykatMenu.addAction(trace) + + return gui + + def _optivis_doTrace(self, gui, **kwargs): + """ + Change at some point to use a stored GUI reference + """ + if not HAS_OPTIVIS: + print("Optivis is not installed") + return None + + prev = self.noxaxis + + self.noxaxis = True + out, tdata = self.run(getTraceData=True, **kwargs) + self.noxaxis = prev + + # For now just select the first trace computed + # Later we could add some gui list to show the different ones + tdata = tdata[0] + + for c in self.getComponents(): + if not isinstance(c, pykat.components.space): + continue + + if not (hasattr(c, "label_node1") and hasattr(c, "label_node2")): + continue + + c.label_node1.content["w0_x"] = tdata[c.nodes[0].name][0].w0 + c.label_node1.content["w_x"] = tdata[c.nodes[0].name][0].w + c.label_node1.content["z_x"] = tdata[c.nodes[0].name][0].z + c.label_node1.content["Rc_x"] = tdata[c.nodes[0].name][0].Rc + c.label_node1.content["Zr_x"] = tdata[c.nodes[0].name][0].zr + + c.label_node1.content["w0_y"] = tdata[c.nodes[0].name][1].w0 + c.label_node1.content["w_y"] = tdata[c.nodes[0].name][1].w + c.label_node1.content["z_y"] = tdata[c.nodes[0].name][1].z + c.label_node1.content["Rc_y"] = tdata[c.nodes[0].name][1].Rc + c.label_node1.content["Zr_y"] = tdata[c.nodes[0].name][1].zr + + c.label_node2.content["w0_x"] = tdata[c.nodes[1].name][0].w0 + c.label_node2.content["w_x"] = tdata[c.nodes[1].name][0].w + c.label_node2.content["z_x"] = tdata[c.nodes[1].name][0].z + c.label_node2.content["Rc_x"] = tdata[c.nodes[1].name][0].Rc + c.label_node2.content["Zr_x"] = tdata[c.nodes[1].name][0].zr + + c.label_node2.content["w0_y"] = tdata[c.nodes[1].name][1].w0 + c.label_node2.content["w_y"] = tdata[c.nodes[1].name][1].w + c.label_node2.content["z_y"] = tdata[c.nodes[1].name][1].z + c.label_node2.content["Rc_y"] = tdata[c.nodes[1].name][1].Rc + c.label_node2.content["Zr_y"] = tdata[c.nodes[1].name][1].zr + + gui.redraw() + + def _optivis_doSave(self, gui, **kwargs): + """ + Save kat script from Optivis + """ + if not HAS_OPTIVIS: + print("Optivis is not installed") + return None + + # generate file path + directory = os.path.join(os.path.expanduser('~'), '{0}.kat'.format(gui.scene.title)) + + # desired save path + path = None + + # get path to file to export to + while True: + dialog = PyQt4.Qt.QFileDialog(parent=gui.qMainWindow, caption='Save Finesse Script', directory=directory) + dialog.setAcceptMode(PyQt4.Qt.QFileDialog.AcceptSave) + dialog.setFileMode(PyQt4.Qt.QFileDialog.AnyFile) + + # show dialog + dialog.exec_() + + if len(dialog.selectedFiles()) is 0: + # no filename specified + return + + # get file path and format + path = str(dialog.selectedFiles()[0]) + + try: + # check if we can write to the path + open(path, 'w').close() + os.unlink(path) + + # if we get this far, all is good so we can break out the infinite loop + break + except OSError: + PyQt4.Qt.QMessageBox.critical(gui.qMainWindow, 'Filename invalid', 'The specified filename is invalid') + except IOError: + PyQt4.Qt.QMessageBox.critical(gui.qMainWindow, 'Permission denied', 'You do not have permission to save the file to the specified location.') + + # save kat file + self.saveScript(filename=path) + def openGUI(self): if not USE_GUI: raise NoGUIException @@ -1615,7 +1884,7 @@ class kat(object): name = com.__class__.__name__ - print (getattr(self.__class__, name)) + #print (getattr(self.__class__, name)) delattr(self.__class__, name) delattr(self, '__com_' + name) diff --git a/pykat/node_network.py b/pykat/node_network.py index 550290e662dfcedb4378ead9002e538facb0a544..f75c055e2871700a8ff864d72c0b90c328f5481b 100644 --- a/pykat/node_network.py +++ b/pykat/node_network.py @@ -23,6 +23,13 @@ from pykat.optics.gaussian_beams import beam_param from copy import deepcopy class NodeNetwork(object): + + def __new__(cls, *args, **kwargs): + # This creates an instance specific class for the component + # this enables us to add properties to instances rather than + # all classes + return object.__new__(type(cls.__name__, (cls,), {}), *args, **kwargs) + def __init__(self, kat): self.__nodes = {} self.__kat = kat @@ -30,10 +37,7 @@ class NodeNetwork(object): 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,), {}) - + @property def kat(self): return self.__kat