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

Attempt at fixing referencing issue when deep copying the various pykat...

Attempt at fixing referencing issue when deep copying the various pykat objects. It was an issue because they create dynamic properties for classes on the fly depending on which components are added, e.g. so that you can do kat.m1. However on copying the class definitions also need to be deep-copied by creating a new class and copying over the properties.
parent 9ccb67d3
......@@ -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)
......
......@@ -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)
......
......@@ -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
......
......@@ -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]
......
......@@ -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
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment