param.py 12.4 KB
Newer Older
1 2 3 4 5
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

Daniel Brown's avatar
Daniel Brown committed
6 7
import abc
import pykat.exceptions as pkex
8
import weakref
9 10 11

from pykat.freeze import canFreeze

Daniel Brown's avatar
Daniel Brown committed
12 13 14 15 16
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
    
17 18
    param.put(kat.xaxis.x)
    
Daniel Brown's avatar
Daniel Brown committed
19 20 21 22 23 24 25
    """
    __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
26
        self._alt = False
Daniel Brown's avatar
Daniel Brown committed
27 28 29 30 31
        self._isPutable  = isPutable
    
    @property
    def isPutable(self): return self._isPutable
    
32
    def put(self, var, alt=False):
33 34 35
        if not self._isPutable:
            raise pkex.BasePyKatException("Can't put to this object")
            
36
        if var is not None and not isinstance(var, putter):
37
            raise pkex.BasePyKatException("`%s` was not something that can be `put` to a parameter" % str(var))
Daniel Brown's avatar
Daniel Brown committed
38
        
39
        # Remove existing puts
40 41
        if self._putter is not None:
            self._putter.unregister(self)
Daniel Brown's avatar
Daniel Brown committed
42
        
43
        self._putter = var
44
            
45
        self._alt = alt
46
        
47 48
        if var is not None:
            self._putter.register(self)
Daniel Brown's avatar
Daniel Brown committed
49
        
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
    # def _getPutFinesseText(self):
    #     rtn = []
    #
    #     if self._isPutable and self._putter is not None:
    #         putter_enabled = True
    #
    #         if hasattr(self._putter.owner, 'enabled'):
    #             putter_enabled = self._putter.owner.enabled
    #
    #         if putter_enabled:
    #             if self._alt:
    #                 alt = '*'
    #             else:
    #                 alt = ''
    #
    #             # if something is being put to this
    #             rtn.append("put{alt} {comp} {param} ${value}".format(alt=alt, comp=self._component_name, param=self._parameter_name, value=self._putter.put_name()))
    #
    #     return rtn
69 70
        
        
Daniel Brown's avatar
Daniel Brown committed
71 72 73 74 75 76
class putter(object):
    """
    If an object can be put to something that is putable it should inherit this
    object.
    """
    
77
    def __init__(self, put_name, owner, isPutter=True):
Daniel Brown's avatar
Daniel Brown committed
78 79 80
        self._put_name = put_name
        self.put_count = 0
        self._isPutter = isPutter
81
        self.putees = [] # list of params that this puts to
82
        
83
        assert(owner is not None)
84
        self.__owner = weakref.ref(owner)
85
    
86 87 88 89
    def _updateOwner(self, newOwner):
        del self.__owner
        self.__owner = weakref.ref(newOwner)
        
90
    def clearPuts(self):
91 92
        import copy
        for _ in copy.copy(self.putees):
93 94 95
            _.put(None)
    
    def register(self, toput):
96 97 98
        if not self._isPutter:
            raise pkex.BasePyKatException("This object can't put")
            
99 100 101 102
        self.put_count += 1
        self.putees.append(toput)
    
    def unregister(self, item):
103 104 105
        if not self._isPutter:
            raise pkex.BasePyKatException("This object can't put")
            
106 107 108
        self.put_count -= 1
        self.putees.remove(item)
        
109
    @property
110
    def owner(self): return self.__owner()
111 112 113 114 115 116
    
    @property
    def name(self): return self._put_name
    
    @property
    def putCount(self): return self.put_count
Daniel Brown's avatar
Daniel Brown committed
117 118 119 120 121 122
    
    @property
    def isPutter(self): return self._isPutter
    
    def put_name(self): return self._put_name
    
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
    def _getPutFinesseText(self):    
        rtn = []
        has = hasattr(self.owner, 'enabled')
        
        if (has and not self.owner.enabled):
            return rtn
            
        if self.isPutter and len(self.putees) > 0:
            for _ in self.putees:

                has = hasattr(_.owner, 'enabled')
                if (has and _.owner.enabled) or not has:
                    if _._alt:
                        alt = '*'
                    else:
                        alt = ''
    
                    # if something is being put to this 
                    rtn.append("put{alt} {comp} {param} ${value}".format(alt=alt, comp=_._component_name, param=_._parameter_name, value=self.put_name()))
        
        return rtn
        
145
@canFreeze
Daniel Brown's avatar
Daniel Brown committed
146 147
class Param(putable, putter):

148
    def __init__(self, name, owner, value, canFsig=False, fsig_name=None, fsig_name_options=[], isPutable=True, isPutter=True, isTunable=True, var_name=None, register=True):
149
        self._unfreeze()
Daniel Brown's avatar
Daniel Brown committed
150
        self._name = name
151
        self._registered = register
152
        self._owner = weakref.ref(owner)
Daniel Brown's avatar
Daniel Brown committed
153 154
        self._isPutter = isPutter
        self._isTunable = isTunable
155
        self._canFsig = False
156 157
        self._isConst = False
        self._constName = None
158
        
159 160 161
        if self._registered:
            self._owner()._register_param(self)
        
162 163
        if canFsig:
            self._canFsig = True
164
            
165 166 167
            if not pykat.isContainer(fsig_name_options):
                raise pkex.BasePyKatException("fsig name options should be a list of names")
            
168
            if fsig_name is None:
169
                raise pkex.BasePyKatException("If parameter is a possible fsig target the fsig_name argument must be set")
170
                
171
            self.__fsig_name = fsig_name
Daniel Brown's avatar
Daniel Brown committed
172
        
173 174 175 176 177 178
            self.__fsig_name_options = list(fsig_name_options)
            
            if fsig_name not in self.__fsig_name_options:
                self.__fsig_name_options.append(fsig_name)
            
        
Daniel Brown's avatar
Daniel Brown committed
179
        if isPutter:
180
            if var_name is None:
Daniel Brown's avatar
Daniel Brown committed
181 182
                var_name = "var_{0}_{1}".format(owner.name, name)
                
183
        putter.__init__(self, var_name, owner, isPutter)
Daniel Brown's avatar
Daniel Brown committed
184
            
185
        putable.__init__(self, owner.name, name, isPutable)
186 187 188 189

        self.value = value
        
        self._freeze()
190 191 192 193
    
    def __repr__(self):
        return "<%s (%s.%s=%s) at %s>" % (self.__class__.__name__, self.owner.name, self._name, self.value, hex(id(self)))
           
194 195 196
    @property
    def canFsig(self): return self._canFsig
    
197 198 199
    @property
    def owner(self): return self._owner()
    
200 201 202
    @property
    def fsig_name(self): return self.__fsig_name
    
203 204 205
    @property
    def fsigName(self): return self.__fsig_name
    
206 207 208
    @property
    def fsigNameOptions(self): return self.__fsig_name_options
    
Daniel Brown's avatar
Daniel Brown committed
209 210 211 212 213 214
    @property
    def name(self): return self._name
    
    @property
    def isTuneable(self): return self._isTunable
    
215 216 217 218 219 220 221 222 223 224 225 226 227 228
    @property
    def isConstant(self):
        """
        True if the value of this parameter is set by a constant
        """
        return self._isConst
    
    @property
    def constantName(self):
        """
        Name of the constant that the value of this parameter comes from
        """
        return self._constName
        
Daniel Brown's avatar
Daniel Brown committed
229
    @property
230 231 232 233
    def value(self):
        if self._owner().removed:
            raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name))
        else:
234
            if self._isConst:
235 236
                if self._constName[1:] not in self.owner._kat.constants:
                    raise pkex.BasePyKatException("Parameter {}.{} could not find a Finesse constant called `{}`".format(self.owner.name, self.name, self._constName))
237 238 239
                return self.owner._kat.constants[self._constName[1:]].value
            else:
                return self._value
240
    
Daniel Brown's avatar
Daniel Brown committed
241 242
    @value.setter
    def value(self, value):
243 244 245
        if self._owner().removed:
            raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name))
        else:
246 247 248 249
            v = str(value)
            
            # signal frequency constant is internal to finesse so handled like a usual value
            if v.startswith('$') and v not in ("$fs", "$mfs"):
250 251 252 253 254 255 256
                self._isConst = True
                self._constName = value
                self._value = None
            else:
                self._isConst = False
                self._constName = None
                self._value = value
257 258 259 260
    
    def __str__(self):
        if self._owner().removed:
            raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name))
261 262
        elif self._isConst:
            return self._constName
263 264
        elif type(self.value) == float:
            return repr(self.value)
265 266 267 268 269 270 271
        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:
272
            return float(self.value)
Daniel Brown's avatar
Daniel Brown committed
273 274
        
    def getFinesseText(self):
275 276 277
        if self._owner() is None:
            raise pkex.BasePyKatException("Owner has been removed but parameter (%s) is still referenced" % self.name)
            
278 279 280
        if self._owner().removed:
            raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name))
            
Daniel Brown's avatar
Daniel Brown committed
281
        rtn = []
282 283
        #if self.isPutable: rtn.extend(self._getPutFinesseText())
        if self.isPutter: rtn.extend(self._getPutFinesseText())
Daniel Brown's avatar
Daniel Brown committed
284 285 286 287
        
        # if this parameter is being put somewhere then we need to
        # set it as a variable
        if self.isPutter and self.put_count > 0:
288
            rtn.append("set {put_name} {comp} {param}".format(put_name=self.put_name(), comp=self._owner().name, param=self.name))
Daniel Brown's avatar
Daniel Brown committed
289 290
        
        return rtn
291 292 293 294 295 296 297
        
    def _updateOwner(self, newOwner):
        """
        This updates the internal weak reference to link a parameter to who owns it.
        Should only be called by the __deepcopy__ component method to ensure things
        are kept up to date.
        """
298
        self._unfreeze()
299 300
        del self._owner
        self._owner = weakref.ref(newOwner)
301
        self._freeze()
302
        
303 304 305 306
    def _onOwnerRemoved(self):
        #if this param can be put somewhere we need to check if it is
        if self.isPutable:
            for a in self.putees:
307
                print ("Removing put from {0} {1} to {2} {3}".format(self.owner.name, self.name, a.owner.name, a.name))
308 309 310 311 312 313 314 315 316
                a._putter = None
                self.put_count -= 1
                
            # delete any references left over
            del self.putees[:]
        
        # check if we have anything being put to us
        if self.isPutter:
            if self._putter != None:
317
                print ("Removing put from {0} {1} to {2} {3}".format(self._putter.owner.name, self._putter.name, self.owner.name, self.name))
318 319 320 321 322
                self._putter.put_count -= 1
                self._putter.putees.remove(self)
                self._putter = None
       
       
Daniel Brown's avatar
Daniel Brown committed
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
    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)
        
345 346
    def __rsub__(self, a):
        return (a) - self.value
Daniel Brown's avatar
Daniel Brown committed
347
    
348 349 350 351 352 353
    def __truediv__(self, a):
        return self.value / (a)
    
    def __itruediv__(self, a):
        return self.value / complex(a)
    
Daniel Brown's avatar
Daniel Brown committed
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
    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):
385 386 387
        if self._owner().removed:
            raise pkex.BasePyKatException("{0} has been removed from the simulation".format(self._owner().name))

Daniel Brown's avatar
Daniel Brown committed
388 389
        rtn = []
        
390
        if self.value != None:
391
            rtn.append("attr {0} {1} {2}".format(self._owner().name, self.name, self))
Daniel Brown's avatar
Daniel Brown committed
392 393 394
            
        rtn.extend(super(AttrParam, self).getFinesseText())
        
395
        return rtn
396 397

    
398
import pykat