diff --git a/examples/length_tuning.py b/examples/length_tuning.py index 8a1c524195a93ea01bcf36eb0eead4b646a0e250..2729774f1129920388738b78e15822ed6a30e763 100644 --- a/examples/length_tuning.py +++ b/examples/length_tuning.py @@ -38,7 +38,7 @@ xaxis b1 phi lin 0 180 100 yaxis deg % plotting the phase of the results """ -kat = finesse.kat(tempdir = "/home/sleavey/Desktop/") +kat = finesse.kat() kat.parseCommands(code) maxtem = np.arange(0, 2, 2) diff --git a/pykat/components.py b/pykat/components.py index 99c8cdbd4ea36b3955afcb99b2e76631effc1b30..7193049e4a4ec5df00b4b3f0fb7b35ceb1eca3bb 100644 --- a/pykat/components.py +++ b/pykat/components.py @@ -13,6 +13,7 @@ import pykat from pykat.node_network import * from pykat.exceptions import * import abc +import copy from pykat.SIfloat import * from pykat.param import Param, AttrParam @@ -70,8 +71,15 @@ 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.__name = name self._svgItem = None self._requested_node_names = [] @@ -86,12 +94,20 @@ class Component(object): 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) diff --git a/pykat/detectors.py b/pykat/detectors.py index 612b01c48f2765e976e894c6f8d83fba76633dc7..e6c5e93385170e704b7451636499853f3026cf1b 100644 --- a/pykat/detectors.py +++ b/pykat/detectors.py @@ -75,6 +75,8 @@ class BaseDetector(object) : else: raise pkex.BasePyKatException("Nodes should be a list or tuple of node names or a singular node name as a string.") + + def _register_param(self, param): self._params.append(param) @@ -349,7 +351,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 @@ -394,11 +402,28 @@ 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 diff --git a/pykat/finesse.py b/pykat/finesse.py index e32e7859670cbad7b743d758769c768396740583..bc335e23355a34c0135ee2a3c8e817d00496b46c 100644 --- a/pykat/finesse.py +++ b/pykat/finesse.py @@ -183,10 +183,9 @@ 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] - if len(idx) == 1: if self.yaxis == "abs:deg": out = self.y[:, idx[0]] @@ -198,6 +197,9 @@ class katRun(object): 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: @@ -375,10 +377,18 @@ Constant = namedtuple('Constant', 'name, value, usedBy') class kat(object): + 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): - - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) self.scene = None # scene object for GUI self.verbose = True @@ -424,7 +434,30 @@ class kat(object): if kat_file != None: self.loadKatFile(kat_file) - + + def __deepcopy__(self, memo): + """ + When deep copying a kat object we need to take into account + the instance specific properties. 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. + """ + result = self.__class__.__new__(self.__class__) + 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 signals(self): return self.__signals @@ -1229,7 +1262,7 @@ class kat(object): # 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].squeeze() - y = data[0:(1+self.x2axis.steps),1].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).squeeze() @@ -1242,7 +1275,7 @@ class kat(object): rows,cols = data.shape x = data[:,0].squeeze() - y = data[:,1:cols].squeeze() + y = data[:,1:cols] return [x, y, hdr] diff --git a/pykat/node_network.py b/pykat/node_network.py index 973d42c05c4e88d474ceab180ca4f0a60aeea625..e5db68304399b711c3844de435c9c2c3c09dc013 100644 --- a/pykat/node_network.py +++ b/pykat/node_network.py @@ -15,9 +15,18 @@ import pykat.exceptions as pkex from pykat.components import Component, NodeGaussSetter from pykat.detectors import BaseDetector as Detector 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 self.__nodeComponents = {} # dictionary of tuples containing which components are connected to a node @@ -25,9 +34,8 @@ class NodeNetwork(object): self.__componentCallback = {} self.__node_id = 1 - cls = type(self) - self.__class__ = type(cls.__name__, (cls,), {}) - + + @property def kat(self): return self.__kat