diff --git a/examples/lkat_optimisation.py b/examples/lkat_optimisation.py index 3ad08c991276efc11410614e719d44c361be330a..089002c03fe4efdb8a8ebf292e43904c98c98de4 100644 --- a/examples/lkat_optimisation.py +++ b/examples/lkat_optimisation.py @@ -35,7 +35,7 @@ def callback(lkat, maxphi): inter = pylibkat.interferometer.in_dll(lkat, "inter") m1 = inter.mirror_list[0] - m2 = inter.mirror_list[1] + m2 = inter.mirror_list[1] circ = inter.output_data_list[0] @@ -54,7 +54,6 @@ def callback(lkat, maxphi): print "Process: Maximum power =", circ.re print "Process: Mirror tuning =", m1.phi - cmd = """ l l1 1 0 n1 s s1 1 n1 n2 @@ -75,7 +74,7 @@ kat = pykat.finesse.kat() kat.parseCommands(cmd) -maxphi = Value('d', 100) +maxphi = Value('d', 0) p = kat.getProcess(callback, maxphi=maxphi) p.start() diff --git a/pykat/commands.py b/pykat/commands.py index ebe0db0ab574bb9c544406508cfea3627e45231b..66410e87b8d22e117f0476a40b636cb0570b3b8e 100644 --- a/pykat/commands.py +++ b/pykat/commands.py @@ -15,9 +15,10 @@ from collections import namedtuple from pykat.utilities.optics.gaussian_beams import gauss_param class Command(object): - def __init__(self): - self.tag = None + tag = None + __removed = False + def getFinesseText(self): """ Base class for individual finesse optical components """ raise NotImplementedError("This function is not implemented") @@ -32,8 +33,17 @@ class Command(object): """ self._kat = kat + def remove(self): + self._kat.remove(self) + self.__removed = True + + @property + def removed(self): return self.__removed + class cavity(Command): def __init__(self, name, c1, n1, c2, n2): + super(Command, self).__init__() + self.__name = name self.__c1 = c1 self.__c2 = c2 @@ -84,6 +94,7 @@ class tf(Command): fQ = namedtuple('fQ', ['f', 'Q']) def __init__(self, name, poles, zeros): + super(Command, self).__init__() pass class xaxis(Command): @@ -92,6 +103,9 @@ class xaxis(Command): and interface to the xaxis command in FINESSE. """ + @property + def name(self): return self._axis_type + def __init__(self, scale, limits, param, steps, comp=None, axis_type="xaxis"): """ Typical usage: @@ -102,6 +116,8 @@ class xaxis(Command): steps is the number of points to compute between upper and lower limits. """ + super(Command, self).__init__() + self._axis_type = axis_type self.x = putter("x1") @@ -140,7 +156,7 @@ class xaxis(Command): raise pkex.BasePyKatException("param argument is not of type Param") else: self.__param = param - self.__comp = param._owner.name + self.__comp = param._owner().name @property def param(self): return self.__param @@ -150,7 +166,7 @@ class xaxis(Command): raise pkex.BasePyKatException("param argument is not of type Param") else: self.__param = value - self.__comp = value._owner.name + self.__comp = value._owner().name @staticmethod def parseFinesseText(text): diff --git a/pykat/components.py b/pykat/components.py index faac85779ea310defe1f7d9a5b28883ddfd9a0b0..d5e1f17a32dac5f830b440720587c6a074e93b1d 100644 --- a/pykat/components.py +++ b/pykat/components.py @@ -16,41 +16,41 @@ import pykat.gui.graphics from pykat.gui.graphics import * from pykat.SIfloat import * from pykat.param import Param, AttrParam - +import weakref import pykat.exceptions as pkex next_component_id = 1 class NodeGaussSetter(object): def __init__(self, component, node): - self.__comp = component - self.__node = node + self.__comp = weakref.ref(component) + self.__node = weakref.ref(node) @property def node(self): - return self.__node + return self.__node() @property def q(self): - return self.__node.qx + return self.__node().qx @q.setter def q(self, value): - self.__node.setGauss(self.__comp, complex(value)) + self.__node().setGauss(self.__comp(), complex(value)) @property def qx(self): - return self.__node.qx + return self.__node().qx @qx.setter def qx(self, value): - self.__node.setGauss(self.__comp, complex(value)) + self.__node().setGauss(self.__comp(), complex(value)) @property def qy(self): - return self.__node.qy + return self.__node().qy @qy.setter def qy(self, value): - self.__node.setGauss(self.__comp, self.qx, complex(value)) + self.__node().setGauss(self.__comp(), self.qx, complex(value)) class Component(object): __metaclass__ = abc.ABCMeta @@ -62,6 +62,7 @@ class Component(object): self._kat = None self.tag = None self._params = [] + self.__removed = False # store a unique ID for this component global next_component_id @@ -106,7 +107,7 @@ class Component(object): 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) @@ -141,6 +142,9 @@ class Component(object): def getQGraphicsItem(self): return None + @property + def removed(self): return self.__removed + @property def nodes(self): return self._kat.nodes.getComponentNodes(self) @@ -152,6 +156,13 @@ class Component(object): def __str__(self): return self.name + def remove(self): + self._kat.remove(self) + + del self._params[:] + + self.__removed = True + class AbstractMirrorComponent(Component): __metaclass__ = abc.ABCMeta diff --git a/pykat/detectors.py b/pykat/detectors.py index 3f30920e648e3fd47eb265ef9785a80daa6a242c..ae8bde8c7b729491c53d64802b79416af3683c39 100644 --- a/pykat/detectors.py +++ b/pykat/detectors.py @@ -27,7 +27,8 @@ class Detector(object) : self._params = [] self._mask = {} self.__scale = None - + self.__removed = False + if node != None: if node[-1]=='*': self._alternate_beam = True @@ -39,9 +40,19 @@ class Detector(object) : self._params.append(param) def _on_kat_add(self, kat): + self._kat = kat + if self.__requested_node != None: self.__node = kat.nodes.createNode(self.__requested_node) + def remove(self): + if self.__removed: + raise pkex.BasePyKatException("{0} has already been marked as removed".format(self.name)) + else: + self._kat.remove(self) + + self.__removed = True + @staticmethod def parseFinesseText(text): raise NotImplementedError("This function is not implemented") @@ -53,6 +64,10 @@ class Detector(object) : def getQGraphicsItem(self): return None + + @property + def removed(self): return self.__removed + @property def scale(self): return self.__scale @scale.setter diff --git a/pykat/finesse.py b/pykat/finesse.py index fd4f0b4872e7046e6bfb641f76250bc095660c99..c4f97dc03d35a4a744993abcccd562ab040f78a9 100644 --- a/pykat/finesse.py +++ b/pykat/finesse.py @@ -164,18 +164,27 @@ class Signals(object): self.__name = name self.__amplitude = Param("amp", self, SIfloat(amplitude)) self.__phase = Param("phase", self, SIfloat(phase)) + self.__removed = False # 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 # and then based on the parameter specfied get the name if not param.canFsig: - raise pkex.BasePyKatException("Cannot fsig parameter {1} on component {0}".format(str(param._owner), param.name)) - + raise pkex.BasePyKatException("Cannot fsig parameter {1} on component {0}".format(str(param._owner().name), param.name)) def _register_param(self, param): self._params.append(param) + + @property + def removed(self): return self.__removed + def remove(self): + if self.__removed: + raise pkex.BasePyKatException("Signal {0} has already been marked as removed".format(self.name)) + else: + self._kat.remove(self) + @property def name(self): return self.__name @@ -194,7 +203,7 @@ class Signals(object): def target(self): return self.__target.fsig_name @property - def owner(self): return self.__target._owner.name + def owner(self): return self.__target._owner().name def getFinesseText(self): rtn = [] @@ -211,10 +220,18 @@ class Signals(object): # so need to get the name of at least one of them # as if you tune one you tune them all if len(self.targets) == 0: - return "signal" + return "fsignal" else: return self.targets[0].name + + @property + def removed(self): return False # we can never remove the Signal object altogethr just the + # individual fsig targets + def remove(self): + for t in self.targets: + self._kat.remove(self) + @property def f(self): return self.__f @f.setter @@ -236,11 +253,10 @@ class Signals(object): raise pkex.BasePyKatException("No target was specified for signal to be applied") if name == None: - name = "sig_" +target._owner.name + "_" + target.name + name = "sig_" + target._owner().name + "_" + target.name self.targets.append(Signals.fsig(target, name, amplitude, phase)) - def getFinesseText(self): rtn = [] @@ -794,6 +810,30 @@ class kat(object): if self.verbose: print "" if self.verbose: print "Finished in " + str(datetime.datetime.now()-start) + def remove(self, obj): + if not (obj.name in self.__components or obj.name in self.__detectors or obj.name in self.__commands): + raise pkex.BasePyKatException("{0} is not currently in the simulation".format(obj.name)) + + if obj.removed: + raise pkex.BasePyKatException("{0} has already been removed".format(obj.name)) + + if isinstance(obj, Component): + del self.__components[obj.name] + self.__del_component(obj) + self.nodes.removeComponent(obj) + elif isinstance(obj, Command): + del self.__commands[obj.name] + self.__del_command(obj) + elif isinstance(obj, Detector): + del self.__detectors[obj.name] + self.__del_detector(obj) + + for b in self.__blocks: + if obj in self.__blocks[b].contents: + self.__blocks[b].contents.remove(obj) + + import gc + print gc.get_referrers(obj) def add(self, obj): try: @@ -1060,8 +1100,18 @@ class kat(object): fget = lambda self: self.__get_detector(name) setattr(self.__class__, name, property(fget)) - setattr(self, '__det_' + name, det) + setattr(self, '__det_' + name, det) + + def __del_detector(self, det): + if not isinstance(det, Detector): + raise exceptions.ValueError("Argument is not of type Detector") + + name = det.name + + delattr(self.__class__, name) + delattr(self, '__det_' + name) + def __get_detector(self, name): return getattr(self, '__det_' + name) @@ -1076,6 +1126,15 @@ class kat(object): setattr(self.__class__, name, property(fget)) setattr(self, '__com_' + name, com) + def __del_command(self, com): + + if not isinstance(com, Command): + raise exceptions.ValueError("Argument is not of type Command") + + name = com.__class__.__name__ + delattr(self.__class__, name) + delattr(self, '__com_' + name) + def __get_command(self, name): return getattr(self, '__com_' + name) @@ -1088,7 +1147,15 @@ class kat(object): setattr(self.__class__, comp.name, property(fget)) setattr(self, '__comp_' + comp.name, comp) + + def __del_component(self, comp): + if not isinstance(comp, Component): + raise exceptions.ValueError("Argument is not of type Component") + + delattr(self.__class__, comp.name) + delattr(self, '__comp_' + comp.name) + def __get_component(self, name): return getattr(self, '__comp_' + name) diff --git a/pykat/node_network.py b/pykat/node_network.py index c882dc26b7a99fadd48acbc4dc1851aae557762f..64b875a63e3a83aa7f71ca65c0e16e4d2865de8a 100644 --- a/pykat/node_network.py +++ b/pykat/node_network.py @@ -63,15 +63,18 @@ class NodeNetwork(object): 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) + del 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) + del old_node_comps comp_nodes = list(comp.nodes) comp_nodes[comp_nodes.index(node_old)] = node_new self.__componentNodes[comp.id] = tuple(comp_nodes) + del comp_nodes # if old node is no longer connected to anything then delete it if node_old.components.count(None) == 2: @@ -116,6 +119,23 @@ class NodeNetwork(object): self.__nodes[node_name] = n self.__nodeComponents[n.id] = (None, None) return n + + def removeComponent(self, comp): + C = self.__componentNodes[comp.id] + + for n in C: + if comp in self.__nodeComponents[n.id]: + l = list(self.__nodeComponents[n.id]) + l[l.index(comp)] = None + self.__nodeComponents[n.id] = tuple(l) + + if l.count(None) == 2: + self.removeNode(n) + + del l + + del self.__componentCallback[comp.id] + del self.__componentNodes[comp.id] def removeNode(self, node): @@ -128,13 +148,14 @@ class NodeNetwork(object): 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") + raise exceptions.RuntimeError("Cannot remove a node which is attached to components still") 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] + del self.__nodeComponents[node.id] def hasNode(self, name): return (name in self.__nodes) diff --git a/pykat/param.py b/pykat/param.py index c6e0ba0a381ae5c5e05462d9424577123adcac24..2376114f0d36995a467c372ddbb247eb5f260d8b 100644 --- a/pykat/param.py +++ b/pykat/param.py @@ -1,6 +1,7 @@ import abc import pykat.exceptions as pkex - +import weakref + class putable(object): """ Objects that inherit this should be able to have something `put` to it. @@ -59,11 +60,11 @@ class Param(putable, putter): def __init__(self, name, owner, value, canFsig=False, fsig_name=None, isPutable=True, isPutter=True, isTunable=True, var_name=None): self._name = name - self._owner = owner + self._owner = weakref.ref(owner) self._value = value self._isPutter = isPutter self._isTunable = isTunable - self._owner._register_param(self) + self._owner()._register_param(self) self._canFsig = False if canFsig: @@ -96,15 +97,35 @@ class Param(putable, putter): def isTuneable(self): return self._isTunable @property - def value(self): return self._value + def value(self): + if self._owner().removed: + raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name)) + else: + 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 + if self._owner().removed: + raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name)) + else: + self._value = value + + def __str__(self): + if self._owner().removed: + raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name)) + else: + return str(self.value) + + def __float__(self): + if self._owner().removed: + raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name)) + else: + return self.value def getFinesseText(self): + if self._owner().removed: + raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name)) + rtn = [] if self.isPutable: rtn.extend(self._getPutFinesseText()) @@ -112,7 +133,7 @@ class Param(putable, putter): # 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)) + rtn.append("set {put_name} {comp} {param}".format(put_name=self.put_name(), comp=self._owner().name, param=self.name)) return rtn @@ -172,10 +193,13 @@ class AttrParam(Param): If the value pf the parameter is not 0 the attr command will be printed. """ def getFinesseText(self): + if self._owner().removed: + raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name)) + rtn = [] if self.value != None: - rtn.append("attr {0} {1} {2}".format(self._owner.name, self.name, self.value)) + rtn.append("attr {0} {1} {2}".format(self._owner().name, self.name, self.value)) rtn.extend(super(AttrParam, self).getFinesseText())