components.py 61.9 KB
Newer Older
Daniel Brown's avatar
Daniel Brown committed
1 2 3 4 5 6
# -*- coding: utf-8 -*-
"""
Created on Mon Jan 28 11:10:01 2013

@author: Daniel
"""
7 8 9 10 11
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

12
from pykat import USE_GUI, HAS_OPTIVIS, NoGUIException
13 14 15 16 17 18

import pykat.external.six as six

if six.PY2:
	import exceptions

19
import math
20
import warnings
Daniel Brown's avatar
Daniel Brown committed
21 22 23 24 25
import pykat.exceptions as pkex
import pykat
from pykat.node_network import *
from pykat.exceptions import *
import abc
26 27
import copy
from collections import OrderedDict
28
from .optics import ABCD
29 30 31 32 33 34 35

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
Daniel Brown's avatar
Daniel Brown committed
36

Daniel Brown's avatar
Daniel Brown committed
37 38
from pykat.SIfloat import *
from pykat.param import Param, AttrParam
39
import weakref
40
import pykat.exceptions as pkex
41
from copy import deepcopy
42
from pykat.freeze import canFreeze
Daniel Brown's avatar
Daniel Brown committed
43

44
next_component_id = 1
45

46 47 48 49 50
if USE_GUI:
    import pykat.gui.resources
    import pykat.gui.graphics
    from pykat.gui.graphics import *

51
@canFreeze
Daniel Brown's avatar
Daniel Brown committed
52 53
class NodeGaussSetter(object):
    def __init__(self, component, node):                
54 55
        self.__comp = weakref.ref(component)
        self.__node = weakref.ref(node)
Daniel Brown's avatar
Daniel Brown committed
56
        self.__name = None
57 58
        self._freeze()
     
59 60 61
    def __str__(self):
        return self.__node().name
        
62 63 64
    @property
    def enabled(self):
        return self.__node().enabled
Daniel Brown's avatar
Daniel Brown committed
65
        
66 67 68 69
    @enabled.setter
    def enabled(self, value):
        self.__node().enabled = value
           
Daniel Brown's avatar
Daniel Brown committed
70 71
    @property
    def name(self):
72
        return self.node.name
73 74 75 76 77 78 79 80 81 82 83
    
    @property
    def gauss_name(self):
        if self.__name is None:
            return "g_%s" % self.node.name
        else:
            return self.__name
        
    @gauss_name.setter
    def gauss_name(self, value):
        self.__name = value
84
            
Daniel Brown's avatar
Daniel Brown committed
85 86
    @property
    def node(self):
87
        return self.__node()
Daniel Brown's avatar
Daniel Brown committed
88 89 90
    
    @property
    def q(self):
91
        return self.__node().qx
Daniel Brown's avatar
Daniel Brown committed
92 93 94
        
    @q.setter
    def q(self, value):
95
        self.__node().setGauss(self.__comp(), complex(value))
Daniel Brown's avatar
Daniel Brown committed
96 97 98
        
    @property
    def qx(self):
99
        return self.__node().qx
Daniel Brown's avatar
Daniel Brown committed
100 101
    @qx.setter
    def qx(self, value):
102
        self.__node().setGauss(self.__comp(), complex(value))
Daniel Brown's avatar
Daniel Brown committed
103 104 105
    
    @property
    def qy(self):
106
        return self.__node().qy
Daniel Brown's avatar
Daniel Brown committed
107 108
    @qy.setter
    def qy(self, value):
109
        self.__node().setGauss(self.__comp(), self.qx, complex(value))
110 111 112 113 114
     
    @property 
    def n(self):
        return self.__node().n
    
Daniel Brown's avatar
Daniel Brown committed
115
        
116
id_____pykat_class = 0
117 118
 
@canFreeze
Daniel Brown's avatar
Daniel Brown committed
119 120
class Component(object):
    __metaclass__ = abc.ABCMeta
121 122 123 124 125

    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
126 127 128
        global id_____pykat_class
        id_____pykat_class += 1
        cnew_name = str("%s.%s_%i" % (cls.__module__, cls.__name__, id_____pykat_class))
129 130 131
        
        cnew = type(cnew_name, (cls,), {})
        
132 133
        o = object.__new__(cnew)
        return o
134 135
        
    def __init__(self, name=None):
136
        self._unfreeze()
137 138
        
        self._optivis_component = None
Daniel Brown's avatar
Daniel Brown committed
139 140 141 142 143 144
        self.__name = name
        self._svgItem = None
        self._requested_node_names = []
        self._kat = None
        self.tag = None
        self._params = []
145
        self.__removed = False
Daniel Brown's avatar
Daniel Brown committed
146
        self._default_fsig_param = None
147
        self.optivisLabelContent = None
Daniel Brown's avatar
Daniel Brown committed
148 149 150 151
        
        # store a unique ID for this component
        global next_component_id
        self.__id = next_component_id
152
        next_component_id += 1    
153
         
154 155 156 157 158
    def __deepcopy__(self, memo):
        """
        When deep copying a kat object we need to take into account
        the instance specific properties.
        """
159 160 161
        # Here we create a copy of this object based of the base class
        # of this one, otherwise we're making a copy of a copy of a copy...
        result = self.__class__.__new__(self.__class__.__base__)
162
        result._unfreeze()
163
        memo[id(self)] = result
164
        result.__dict__ = copy.deepcopy(self.__dict__, memo)
165
        
166 167
        for _ in result._params:
            _._updateOwner(result)
168 169
    
        result._freeze()
170 171
        return result
        
Daniel Brown's avatar
Daniel Brown committed
172 173
    def _register_param(self, param):
        self._params.append(param)
Daniel Brown's avatar
Daniel Brown committed
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
    
    def _default_fsig(self):
        """
        Returns what Finesse internally determines as the default 
        fsig parameter. This is used mainly for parsing fsig command
        lines where no target parameter is stated.
        """
        if self._default_fsig_param != None:
            if not self._default_fsig_param.canFsig:
                raise pkex.BasePyKatException("Default fsig parameter %s is not possible to fsig" % (self.__default_fsig_param.name))
            else:
                return self._default_fsig_param
        else:
            return None
    
Daniel Brown's avatar
Daniel Brown committed
189 190 191 192 193 194 195 196 197 198 199 200 201
    def _on_kat_add(self, kat):
        """
        Called when this component has been added to a kat object.
        kat is the finesse.kat object which it now belongs to and
        node_array is an array specific to this object which contains
        references to the nodes that are attached to it.
        """
        if self._kat != None:
            raise pkex.BasePyKatException("Component has already been added to a finesse.kat object")
            
        self._kat = kat
        
        kat.nodes.registerComponentNodes(self, self._requested_node_names, self.__on_node_change)
202 203 204 205
    
    def __repr__(self):
        return "<%s (%s) at %s>" % (self.__class__.__name__, self.__name, hex(id(self)))
        
206 207 208 209 210
    def _on_kat_remove(self):
        # inform all parameters that we have removed its owner
        # so that it can then warn about any puts/vars/xaxis
        for p in self._params:
            p._onOwnerRemoved()
Daniel Brown's avatar
Daniel Brown committed
211
        
212 213 214 215
        del self._params[:]

        self.__removed = True
          
Daniel Brown's avatar
Daniel Brown committed
216 217 218 219 220 221 222 223 224
    def __on_node_change(self):
        # need to update the node gauss parameter setter members 
        self.__update_node_setters()
        
    def __update_node_setters(self):
        # check if any node setters have already been added. If so we
        # need to remove them. This function should get called if the nodes
        # are updated, either by some function call or the GUI
        key_rm = [k for k in self.__dict__ if k.startswith("__nodesetter_", 0, 13)]
225

Daniel Brown's avatar
Daniel Brown committed
226 227 228
        # now we have a list of which to remove
        for key in key_rm:
            ns = self.__dict__[key]
229 230 231 232
            name = str(ns.node.name)
            
            if '__nodesetter_' + name in self.__dict__:
                delattr(self, '__nodesetter_' + name)
233
            
234 235 236 237
            if name in self.__class__.__dict__:
                delattr(self.__class__, name)
        
        # Now re-add them pointing to the recent nodes
Daniel Brown's avatar
Daniel Brown committed
238 239 240 241 242 243
        for node in self.nodes:
            if type(node) != pykat.node_network.DumpNode:
                ns = NodeGaussSetter(self, node)
                self.__add_node_setter(ns)
        
    def __add_node_setter(self, ns):
244 245
        self._unfreeze()
        
Daniel Brown's avatar
Daniel Brown committed
246
        if not isinstance(ns, NodeGaussSetter):
247
            raise pkex.ValueError("Argument is not of type NodeGaussSetter")
Daniel Brown's avatar
Daniel Brown committed
248
        
249
        name = str(ns.node.name)
Daniel Brown's avatar
Daniel Brown committed
250
        fget = lambda self: self.__get_node_setter(name)
251
            
Daniel Brown's avatar
Daniel Brown committed
252 253 254
        setattr(self.__class__, name, property(fget))
        setattr(self, '__nodesetter_' + name, ns)                   

255 256
        self._freeze()
        
Daniel Brown's avatar
Daniel Brown committed
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
    def __get_node_setter(self, name):
        return getattr(self, '__nodesetter_' + name)   
        
    @staticmethod
    @abc.abstractmethod
    def parseFinesseText(text):
        """Parses Finesse syntax"""
        raise NotImplementedError("This function is not implemented")

    @abc.abstractmethod
    def getFinesseText(self):
        """ Base class for individual Finesse optical components """    
        raise NotImplementedError("This function is not implemented")

    @abc.abstractmethod
    def getQGraphicsItem(self):    
273 274 275
        if not USE_GUI:
            raise NoGUIException
            
Daniel Brown's avatar
Daniel Brown committed
276 277
        return None      
    
278 279 280 281 282 283 284 285 286
    def ABCD(self, from_node, to_node, direction="x"):
        """
        Returns a 2x2 complex ABCD matrix for this component.
        Actual matrix depends from which to which node you go.
        
        from_node: node object or node name as string
        to_node:   node object or node name as string
        direction: 'x' horizontal beam shape, 'y' vertical beam shape
        """
287 288 289 290 291 292 293
        assert(self._kat.nodes[from_node] in self.nodes)
        assert(self._kat.nodes[to_node] in self.nodes)

        if from_node == to_node:
            return None
        else:
            return np.eye(2)
294
    
295 296 297
    @property
    def removed(self): return self.__removed
    
Daniel Brown's avatar
Daniel Brown committed
298 299 300 301 302 303 304 305 306 307 308
    @property
    def nodes(self): return self._kat.nodes.getComponentNodes(self) 
    
    @property    
    def name(self): return self.__name      
    
    @property
    def id(self): return self.__id
    
    def __str__(self): return self.name
    
309
    def remove(self):
310 311 312 313
        if self.__removed:
            raise pkex.BasePyKatException("{0} has already been marked as removed".format(self.name))
        else:
            self._kat.remove(self)
314 315 316 317 318 319
    
    def getOptivisParameterDict(self):
        if len(self._params) == 0:
            return None
            
        d = OrderedDict()
320
        
321 322
        for p in self._params:
            d[p.name] = OptivisCanvasItemDataType.TEXTBOX
323
        
324
        return d
325
        
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
    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


Daniel Brown's avatar
Daniel Brown committed
346 347
class AbstractMirrorComponent(Component):
    __metaclass__ = abc.ABCMeta
348
        
Daniel Brown's avatar
Daniel Brown committed
349
    def __init__(self, name, R=None, T=None, L=None, phi=0, Rcx=None, Rcy=None, xbeta=None, ybeta=None, mass=None, r_ap=None, Ix=None, Iy=None, zmech=None, rxmech=None, rymech=None):
Daniel Brown's avatar
Daniel Brown committed
350
        super(AbstractMirrorComponent, self).__init__(name)
351
 
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
        # This checking is no longer done on creating the object. Pykat will only complain about 
        # R,T,L values when generating the actual finesse command. Up to that point you can set what
        # ever you like. This is because we can now set parameters to be `constants` values, which 
        # can be changed and are only final when we write it to the a finesse file.
        
        # if (L != None and R != None and T != None) and SIfloat(R)+SIfloat(T)+SIfloat(L) != 1:
        #     raise pkex.BasePyKatException('L+R+T must equal 1 if all are specified at {0}'.format(self.name))
        # elif (R != None and L is None and T != None):
        #     L = 1- (SIfloat(R)+SIfloat(T))
        # elif (R is None and L != None and T != None):
        #     R = 1 - (SIfloat(L)+SIfloat(T))
        # elif (R != None and L != None and T is None):
        #     T = 1 - (SIfloat(L)+SIfloat(R))
        # elif (L is None and R is None and T is None):
        #     raise pkex.BasePyKatException('Must specify at least two of L, R or T')
367
        
Daniel Brown's avatar
Daniel Brown committed
368 369
        self.__R = Param("R", self, SIfloat(R))
        self.__T = Param("T", self, SIfloat(T))
370 371
        self.__L = Param("L", self, SIfloat(L))
        
372
        self.__phi = Param("phi", self, SIfloat(phi), canFsig=True, fsig_name="phase")
Daniel Brown's avatar
Daniel Brown committed
373 374
        self.__Rcx = AttrParam("Rcx", self, SIfloat(Rcx))
        self.__Rcy = AttrParam("Rcy", self, SIfloat(Rcy))
375 376
        self.__xbeta = AttrParam("xbeta", self, SIfloat(xbeta), canFsig=True, fsig_name="xbeta", fsig_name_options=["yaw", "rx"])
        self.__ybeta = AttrParam("ybeta", self, SIfloat(ybeta), canFsig=True, fsig_name="ybeta", fsig_name_options=["pitch", "ry"])
Daniel Brown's avatar
Daniel Brown committed
377
        self.__mass = AttrParam("mass", self, SIfloat(mass))
Daniel Brown's avatar
Daniel Brown committed
378 379
        self.__Ix = AttrParam("Ix", self, SIfloat(Ix))
        self.__Iy = AttrParam("Iy", self, SIfloat(Iy))
Daniel Brown's avatar
Daniel Brown committed
380
        self.__r_ap = AttrParam("r_ap", self, SIfloat(r_ap))
381
    
Daniel Brown's avatar
Daniel Brown committed
382 383 384 385
        self.__zmech = AttrParam("zmech", self, zmech)
        self.__rxmech = AttrParam("rxmech", self, rxmech)
        self.__rymech = AttrParam("rymech", self, rymech)
        
386 387 388 389 390 391
        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")
392
        
393 394 395 396 397
        self.__Fs0 = Param("Fs0", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Fs0")
        self.__Fs1 = Param("Fs1", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Fs1")
        self.__Fs0 = Param("s0", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="s0")
        self.__Fs1 = Param("s1", self, None, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="s1")
            
Daniel Brown's avatar
Daniel Brown committed
398
        self._default_fsig_param = self.__phi
399 400
    
    def setRTL(self, R=None, T=None, L=None):
Daniel Brown's avatar
Daniel Brown committed
401 402 403 404 405 406 407 408 409
        """
        Sets the R, T, and/or L properties. At least two values should be specfified
        as the the third can be assumed as:
        
            R+T+L = 1
        
        By setting one of the properties to None will set whether an m/m1/m2 (bs/bs1/bs2)
        command is used with Finesse.
        """
410 411 412
        if R is not None: self.R = R
        if T is not None: self.T = T
        if L is not None: self.L = L
Andreas Freise's avatar
Andreas Freise committed
413 414

    def completeRTL(self, R=None, T=None, L=None):
Daniel Brown's avatar
Daniel Brown committed
415
        
Andreas Freise's avatar
Andreas Freise committed
416 417 418 419 420 421 422
        setValues = sum(x is not None for x in [R,T,L])
        if setValues == 3:
            self.setRTL(R,T,L)
        elif setValues < 2:
            raise pkex.BasePyKatException("must set at least two out of three parameters (R, T, L)")            
        else:
            if R is not None:
423
                self.R = R
Andreas Freise's avatar
Andreas Freise committed
424
            else:
425
                self.R = 1-T-L            
Andreas Freise's avatar
Andreas Freise committed
426
            if T is not None:
427
                self.T = T
Andreas Freise's avatar
Andreas Freise committed
428
            else:
429
                self.T = 1-R-L            
Andreas Freise's avatar
Andreas Freise committed
430
            if L is not None:
431
                self.L = L
Andreas Freise's avatar
Andreas Freise committed
432
            else:
433
                self.L = 1-R-T            
Andreas Freise's avatar
Andreas Freise committed
434

435 436 437 438 439 440 441 442 443 444 445 446 447
    @property
    def z(self): return self.__z
    @property
    def Fz(self): return self.__Fz
    @property
    def rx(self): return self.__rx
    @property
    def Frx(self): return self.__Frx
    @property
    def ry(self): return self.__ry
    @property
    def Fry(self): return self.__Fry
    
448 449 450
    @property
    def L(self): return self.__L
    @L.setter
451
    def L(self,value): self.__L.value = SIfloat(value)       
Daniel Brown's avatar
Daniel Brown committed
452 453 454 455 456 457 458 459 460 461 462
        
    @property
    def r_ap(self): return self.__r_ap
    @r_ap.setter
    def r_ap(self,value): self.__r_ap.value = SIfloat(value)

    @property
    def mass(self): return self.__mass
    @mass.setter
    def mass(self,value): self.__mass.value = SIfloat(value)
    
Daniel Brown's avatar
Daniel Brown committed
463 464 465 466 467 468 469 470 471 472
    @property
    def Ix(self): return self.__Ix
    @Ix.setter
    def Ix(self,value): self.__Ix.value = SIfloat(value)
    
    @property
    def Iy(self): return self.__Iy
    @Iy.setter
    def Iy(self,value): self.__Iy.value = SIfloat(value)
    
Daniel Brown's avatar
Daniel Brown committed
473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507
    @property
    def R(self): return self.__R
    @R.setter
    def R(self,value): self.__R.value = SIfloat(value)
    
    @property
    def T(self): return self.__T
    @T.setter
    def T(self,value): self.__T.value = SIfloat(value)
        
    @property
    def phi(self): return self.__phi
    @phi.setter
    def phi(self,value): self.__phi.value = SIfloat(value)
    
    @property
    def Rcx(self): return self.__Rcx
    @Rcx.setter
    def Rcx(self,value): self.__Rcx.value = SIfloat(value)
    
    @property
    def Rcy(self): return self.__Rcy
    @Rcy.setter
    def Rcy(self,value): self.__Rcy.value = SIfloat(value)
    
    @property
    def xbeta(self): return self.__xbeta
    @xbeta.setter
    def xbeta(self,value): self.__xbeta.value = SIfloat(value)
    
    @property
    def ybeta(self): return self.__ybeta
    @ybeta.setter
    def ybeta(self,value): self.__ybeta.value = SIfloat(value)
    
Daniel Brown's avatar
Daniel Brown committed
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
    @property
    def zmech(self): return self.__zmech
    @zmech.setter
    def zmech(self,value): self.__zmech.value = value
    
    @property
    def rxmech(self): return self.__rxmech
    @rxmech.setter
    def rxmech(self,value): self.__rxmech.value = value
    
    @property
    def rymech(self): return self.__rymech
    @rymech.setter
    def rymech(self,value): self.__rymech.value = value
    
Daniel Brown's avatar
Daniel Brown committed
523 524 525 526 527 528 529 530 531
    @property
    def Rc(self):
        if self.Rcx == self.Rcy:
            return self.Rcx
        else:
            return [self.Rcx, self.Rcy]
    
    @Rc.setter
    def Rc(self,value):
532 533
        self.Rcx = value
        self.Rcy = value
Daniel Brown's avatar
Daniel Brown committed
534

Daniel Brown's avatar
Daniel Brown committed
535 536 537 538 539 540 541
    def parseAttribute(self, key, value):
        if key in ["Rcx", "Rx", "ROCx", "rx", "rcx", "rocx"]:
            self.Rcx = value
        elif key in ["Rcy", "Ry", "ROCy", "ry", "rcy", "rocy"]:
            self.Rcy = value
        elif key in ["Rc", "ROC", "r", "rc", "roc"]:
            self.Rc = value
Daniel Brown's avatar
Daniel Brown committed
542
        elif key in ["M","m", "Mass", "mass"]:
Daniel Brown's avatar
Daniel Brown committed
543
            self.mass = value
544
        elif key in ["xbeta", "xBeta", "yaw"]:
Daniel Brown's avatar
Daniel Brown committed
545
            self.xbeta = value
546
        elif key in ["ybeta", "yBeta", "pitch"]:
Daniel Brown's avatar
Daniel Brown committed
547 548 549 550 551 552 553
            self.ybeta = value
        elif key in ["x_off"]:
            self.x_offset = value
        elif key in ["y_off"]:
            self.y_offset = value
        elif key in ["r_ap"]:
            self.r_ap = value
Daniel Brown's avatar
Daniel Brown committed
554 555 556 557
        elif key in ["Ix","ix"]:
            self.Ix = value
        elif key in ["Iy","iy"]:
            self.Iy = value
Daniel Brown's avatar
Daniel Brown committed
558 559 560 561 562 563
        elif key in ["zmech", "mech"]:
            self.zmech = value
        elif key in ["rxmech"]:
            self.rxmech = value
        elif key in ["rymech"]:
            self.rymech = value
Daniel Brown's avatar
Daniel Brown committed
564 565 566 567 568
        else:
            return False
            
        return True
        
Daniel Brown's avatar
Daniel Brown committed
569
class mirror(AbstractMirrorComponent):
570 571
    def __init__(self, name, node1, node2, R=None, T=None, L=None,
                 phi=0, Rcx=None, Rcy=None, xbeta=None, ybeta=None, mass=None, r_ap=None):
Daniel Brown's avatar
Daniel Brown committed
572
        super(mirror, self).__init__(name, R, T, L, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap)
Daniel Brown's avatar
Daniel Brown committed
573 574 575
        
        self._requested_node_names.append(node1)
        self._requested_node_names.append(node2)
576 577
        self._freeze()
        
Daniel Brown's avatar
Daniel Brown committed
578 579 580 581 582 583
    def parseAttributes(self, values):
        
        for key in values.keys():
            if not self.parseAttribute(key, values[key]):
                raise pkex.BasePyKatException("No attribute {0} for mirrors".format(key))
        
Daniel Brown's avatar
Daniel Brown committed
584 585
    @staticmethod
    def parseFinesseText(text):
586
        values = text.split()
Daniel Brown's avatar
Daniel Brown committed
587 588

        if values[0] != "m" and values[0] != "m1" and values[0] != "m2":
589
            raise pkex.BasePyKatException("'{0}' not a valid Finesse mirror command".format(text))
Daniel Brown's avatar
Daniel Brown committed
590 591
        
        if len(values) != 7:
592
            raise pkex.BasePyKatException("Mirror Finesse code format incorrect '{0}'".format(text))
Daniel Brown's avatar
Daniel Brown committed
593 594 595

        if len(values[0])==1:
            values.pop(0) # remove initial value
Daniel Brown's avatar
Daniel Brown committed
596
            return mirror(values[0], values[4], values[5], L=None, R=values[1], T=values[2], phi=values[3])
Daniel Brown's avatar
Daniel Brown committed
597 598 599
        else:
            if values[0][1]=="1":
                values.pop(0) # remove initial value
Daniel Brown's avatar
Daniel Brown committed
600
                return mirror(values[0], values[4], values[5], R=None, L=values[2], T=values[1], phi=values[3])
Daniel Brown's avatar
Daniel Brown committed
601 602
            else:
                values.pop(0) # remove initial value
Daniel Brown's avatar
Daniel Brown committed
603
                return mirror(values[0], values[4], values[5], T=None, R=values[1], L=values[2], phi=values[3])
Daniel Brown's avatar
Daniel Brown committed
604

605
    def getFinesseText(self):
606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
        mode = 'm'
        opt1 = None
        opt2 = None
        
        if self.R.value is not None and self.T.value is not None and self.L.value is None:
            mode = 'm'
            opt1 = self.R
            opt2 = self.T
            msg = "R+T"
        elif self.R.value is None and self.T.value is not None and self.L.value is not None:
            mode = 'm1'
            opt1 = self.T
            opt2 = self.L
            msg = "T+L"
        elif self.R.value is not None and self.T.value is None and self.L.value is not None:
            mode = 'm2'
            opt1 = self.R
            opt2 = self.L
            msg = "R+L"
        elif self.R.value is not None and self.T.value is not None and self.L.value is not None:
            mode = 'm'
            opt1 = self.R
            opt2 = self.T
            v = opt1.value + opt2.value + self.L.value
            
            if v > 1+1e-14 or v < 0:
                raise pkex.BasePyKatException("Mirror {0} has R+T+L = {1}, must be, 0 < R+T+L <= 1".format(self.name, v))
        else:
            raise pkex.BasePyKatException("Mirror {0} has R={1}, T={2} and L={3}. You must define at least a pair of values".format(self.name, self.R, self.T, self.L))
635
        
636 637 638
        if opt1.value + opt2.value > 1+1e-14:
            raise pkex.BasePyKatException("Mirror {0} has {1} = {2}, must be <= 1".format(self.name, msg, opt1+opt2))
            
Daniel Brown's avatar
Daniel Brown committed
639 640
        rtn = []
            
641 642 643
        rtn.append('{mode} {0} {1} {2} {3} {4} {5}'.format(
                self.name, opt1, opt2, self.phi,
                self.nodes[0].name, self.nodes[1].name, mode=mode))
Daniel Brown's avatar
Daniel Brown committed
644 645 646 647 648

        for p in self._params:
            rtn.extend(p.getFinesseText())
                    
        return rtn
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
    
    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")
          
Daniel Brown's avatar
Daniel Brown committed
680 681
        
    def getQGraphicsItem(self):
682 683 684
        if not USE_GUI:
            raise NoGUIException
            
685
        if self._svgItem is None:
Daniel Brown's avatar
Daniel Brown committed
686 687 688
            self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])])
            
        return self._svgItem
689 690
        
    def ABCD(self, from_node, to_node, direction="x"):
691 692 693
        assert(self._kat.nodes[from_node] in self.nodes)
        assert(self._kat.nodes[to_node] in self.nodes)
        
694 695 696 697 698 699 700 701 702
        direction = direction.lower()
        from_node = str(from_node)
        to_node = str(to_node)
        
        if direction == "x" and self.Rcx.value is not None:
            Rc = self.Rcx.value
        elif direction == "y" and self.Rcy.value is not None:
            Rc = self.Rcy.value
        else:
Daniel Brown's avatar
Daniel Brown committed
703
            Rc = np.inf
704 705 706 707 708 709 710 711 712
        
        n1 = self.nodes[0].n
        n2 = self.nodes[1].n
        
        if from_node == str(self.nodes[0]) and to_node == str(self.nodes[0]):
            return ABCD.mirror_refl(n1, Rc)
        elif from_node == str(self.nodes[0]) and to_node == str(self.nodes[1]):
            return ABCD.mirror_trans(n1, n2, Rc)
        elif from_node == str(self.nodes[1]) and to_node == str(self.nodes[0]):
713
            return ABCD.mirror_trans(n2, n1, -Rc)
714 715 716
        elif from_node == str(self.nodes[1]) and to_node == str(self.nodes[1]):
            return ABCD.mirror_refl(n2, -Rc)
        else:
717
            return None
718 719 720 721 722
    
    def nodeConnections(self):
        return (
                   (self.nodes[0], self.nodes[1]),
               )
Daniel Brown's avatar
Daniel Brown committed
723 724

class beamSplitter(AbstractMirrorComponent):
725 726
    def __init__(self, name, node1, node2, node3, node4, R = None, T = None, L=None, phi = 0, alpha = 0, Rcx = None, Rcy = None, xbeta = None, ybeta = None, mass = None, r_ap = None):
        super(beamSplitter, self).__init__(name, R, T, L, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap)
Daniel Brown's avatar
Daniel Brown committed
727 728 729 730 731
        
        self._requested_node_names.append(node1)
        self._requested_node_names.append(node2)
        self._requested_node_names.append(node3)
        self._requested_node_names.append(node4)
Daniel Brown's avatar
Daniel Brown committed
732
        self.__alpha = Param("alpha", self, SIfloat(alpha))
733 734

        self._freeze()
735
        
Daniel Brown's avatar
Daniel Brown committed
736 737 738 739 740
    @property
    def alpha(self): return self.__alpha
    @alpha.setter
    def alpha(self,value): self.__alpha.value = SIfloat(value)
    
Daniel Brown's avatar
Daniel Brown committed
741 742 743 744 745 746 747 748
    def parseAttributes(self, values):
        
        for key in values.keys():
            if not self.parseAttribute(key, values[key]):
                if key == "alpha":
                    self.alpha = values[key]
                else:
                    raise pkex.BasePyKatException("No attribute {0} for mirrors".format(key))
749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
    
    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")
                     
Daniel Brown's avatar
Daniel Brown committed
783 784
    @staticmethod
    def parseFinesseText(text):
785
        values = text.split()
Daniel Brown's avatar
Daniel Brown committed
786 787

        if values[0] != "bs" and values[0] != "bs1" and values[0] != "bs2":
788
            raise pkex.BasePyKatException("'{0}' not a valid Finesse beam splitter command".format(text))
Daniel Brown's avatar
Daniel Brown committed
789 790
        
        if len(values) != 10:
791
            raise pkex.BasePyKatException("Beam splitter Finesse code format incorrect '{0}'".format(text))
Daniel Brown's avatar
Daniel Brown committed
792

793
        if len(values[0])==2:
Daniel Brown's avatar
Daniel Brown committed
794
            values.pop(0) # remove initial value
Daniel Brown's avatar
Daniel Brown committed
795 796
            return beamSplitter(values[0], values[5], values[6], values[7], values[8],
                                values[1], values[2], None, values[3], values[4])
797 798
        elif values[0][2]=="1":
            values.pop(0) # remove initial value
Daniel Brown's avatar
Daniel Brown committed
799 800
            return beamSplitter(values[0], values[5], values[6], values[7], values[8],
                                None, values[1], values[2], values[3], values[4])
Daniel Brown's avatar
Daniel Brown committed
801
        else:
802
            values.pop(0) # remove initial value
Daniel Brown's avatar
Daniel Brown committed
803 804
            return beamSplitter(values[0], values[5], values[6], values[7], values[8],
                                values[1], None, values[2], values[3], values[4])
805
        
Daniel Brown's avatar
Daniel Brown committed
806
    def getFinesseText(self):
807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839
        mode = 'bs'
        opt1 = None
        opt2 = None
        
        if self.R.value is not None and self.T.value is not None and self.L.value is None:
            mode = 'bs'
            opt1 = self.R
            opt2 = self.T
            msg = "R+T"
        elif self.R.value is None and self.T.value is not None and self.L.value is not None:
            mode = 'bs1'
            opt1 = self.T
            opt2 = self.L
            msg = "T+L"
        elif self.R.value is not None and self.T.value is None and self.L.value is not None:
            mode = 'bs2'
            opt1 = self.R
            opt2 = self.L
            msg = "R+L"
        elif self.R.value is not None and self.T.value is not None and self.L.value is not None:
            mode = 'bs'
            opt1 = self.R
            opt2 = self.T
            v = opt1.value + opt2.value + self.L.value
            
            if v > 1+1e-14 or v < 0:
                raise pkex.BasePyKatException("Beamsplitter {0} has R+T+L = {1}, must be, 0 < R+T+L <= 1".format(self.name, v))
        else:
            raise pkex.BasePyKatException("Beamsplitter {0} has R={1}, T={2} and L={3}. You must define at least a pair of values".format(self.name, self.R.value,self.T.value,self.L.value))
        
        if opt1.value + opt2.value > 1+1e-14:
            raise pkex.BasePyKatException("Beamsplitter {0} has {1} = {2}, must be <= 1".format(self.name, msg, opt1+opt2))
            
Daniel Brown's avatar
Daniel Brown committed
840 841
        rtn = []
            
842 843 844
        rtn.append('{mode} {0} {1} {2} {3} {4} {5} {6} {7} {8}'.format(
                self.name, opt1, opt2, self.phi,
                self.alpha, self.nodes[0].name,
Daniel Brown's avatar
Daniel Brown committed
845
                self.nodes[1].name, self.nodes[2].name,
846
                self.nodes[3].name, mode=mode))
Daniel Brown's avatar
Daniel Brown committed
847 848 849 850 851 852 853

        for p in self._params:
            rtn.extend(p.getFinesseText())
            
        return rtn
        
    def getQGraphicsItem(self):
854 855 856
        if not USE_GUI:
            raise NoGUIException
            
857
        if self._svgItem is None:
Daniel Brown's avatar
Daniel Brown committed
858
            # FIXME: make proper SVG component for beam splitter
859
            self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,24,self.nodes[0]), (-4,6,self.nodes[1]), (14,6,self.nodes[2]), (14,24,self.nodes[3])])
Daniel Brown's avatar
Daniel Brown committed
860 861
            
        return self._svgItem
862 863
        
    def ABCD(self, from_node, to_node, direction="x"):
864 865 866
        assert(self._kat.nodes[from_node] in self.nodes)
        assert(self._kat.nodes[to_node] in self.nodes)
        
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
        direction = direction.lower()
        from_node = str(from_node)
        to_node = str(to_node)
   
        if direction == "x":
            refl = ABCD.bs_refl_x
            trans = ABCD.bs_trans_x
        else:
            refl = ABCD.bs_refl_y
            trans = ABCD.bs_trans_y
       
        if direction == "x" and self.Rcx.value is not None:
            Rc = self.Rcx.value
        elif direction == "y" and self.Rcy.value is not None:
            Rc = self.Rcy.value
        else:
Daniel Brown's avatar
Daniel Brown committed
883
            Rc = np.inf
Daniel Brown's avatar
Daniel Brown committed
884
   
885 886 887 888 889
        n1 = float(self.nodes[0].n)
        n2 = float(self.nodes[3].n)
        
        alpha1 = self.alpha.value
        alpha2 = np.rad2deg( np.arcsin( np.sin(np.deg2rad(self.alpha.value) ) * n1/n2) )
890 891 892 893
   
        if (from_node == str(self.nodes[0]) and to_node == str(self.nodes[1])) or \
            (from_node == str(self.nodes[1]) and to_node == str(self.nodes[0])):
            return refl(n1, Rc, self.alpha.value)
894
            
895 896 897
        elif (from_node == str(self.nodes[2]) and to_node == str(self.nodes[3])) or \
            (from_node == str(self.nodes[3]) and to_node == str(self.nodes[2])):
            return refl(n2, -Rc, self.alpha.value)
898
            
899
        elif from_node == str(self.nodes[0]) and to_node == str(self.nodes[2]):
900 901
            return trans(n1, n2, Rc, alpha1)
            
902
        elif from_node == str(self.nodes[2]) and to_node == str(self.nodes[0]):
903 904
            return trans(n2, n1, -Rc, alpha2)
            
905
        elif from_node == str(self.nodes[1]) and to_node == str(self.nodes[3]):
906 907
            return trans(n1, n2, Rc, alpha1)
            
908
        elif from_node == str(self.nodes[3]) and to_node == str(self.nodes[1]):
909
            return trans(n2, n1, -Rc, alpha2)
910
        else:
911
            return None
912 913 914 915 916 917 918 919 920
            
    def nodeConnections(self):
        return (
                   (self.nodes[0], self.nodes[1]), # node 1 <-R> node 2
                   (self.nodes[0], self.nodes[2]), # node 1 <-T> node 3
                   (self.nodes[1], self.nodes[3]), # node 2 <-T> node 4
                   (self.nodes[2], self.nodes[3]), # node 3 <-R> node 4
               )
        
Daniel Brown's avatar
Daniel Brown committed
921
class space(Component):
922
    def __init__(self, name, node1, node2, L = 0, n = 1, gx = None, gy = None):
Daniel Brown's avatar
Daniel Brown committed
923 924 925 926 927
        Component.__init__(self, name)
        
        self._requested_node_names.append(node1)
        self._requested_node_names.append(node2)
        self._QItem = None
Daniel Brown's avatar
Daniel Brown committed
928
        self.__L = Param("L", self, SIfloat(L), canFsig=True, fsig_name="phase")
Daniel Brown's avatar
Daniel Brown committed
929
        self.__n = Param("n", self, SIfloat(n))
930

931 932
        self.__gx = AttrParam("gx", self, gx)
        self.__gy = AttrParam("gy", self, gy)
Daniel Brown's avatar
Daniel Brown committed
933
        
Daniel Brown's avatar
Daniel Brown committed
934
        self._default_fsig_param = self.__L
935 936

        self._freeze()
Daniel Brown's avatar
Daniel Brown committed
937
        
Daniel Brown's avatar
Daniel Brown committed
938 939 940
    @property
    def L(self): return self.__L
    @L.setter
Andreas Freise's avatar
Andreas Freise committed
941
    def L(self,value): self.__L.value = SIfloat(value)
Daniel Brown's avatar
Daniel Brown committed
942 943 944 945
    @property
    def n(self): return self.__n
    @n.setter
    def n(self,value): self.__n.value = SIfloat(value)
946 947

    @property
948
    def gouy(self):
Daniel Brown's avatar
Daniel Brown committed
949
        if self.__gx.value == self.__gy.value: 
Daniel Brown's avatar
Daniel Brown committed
950
            return self.__gx.value 
Daniel Brown's avatar
Daniel Brown committed
951 952 953
        else:
            raise pkex.BasePyKatException("Gouy phase in x and y directions are different, use gx and gy properties instead")
            
954 955
    @gouy.setter
    def gouy(self,value):
Daniel Brown's avatar
Daniel Brown committed
956 957 958
        self.__gx.value = SIfloat(value)
        self.__gy.value = SIfloat(value)
        
959
    @property
960
    def gouyx(self): return self.gx
961
    @gouyx.setter
962
    def gouyx(self,value): self.gx = SIfloat(value)
963 964

    @property
965
    def gouyy(self): return self.gy
966
    @gouyy.setter
967 968 969 970 971 972 973 974 975 976 977
    def gouyy(self,value): self.gy = SIfloat(value)

    @property
    def gx(self): return self.__gx
    @gx.setter
    def gx(self,value): self.__gx.value = SIfloat(value)
    
    @property
    def gy(self): return self.__gy
    @gy.setter
    def gy(self,value): self.__gy.value = SIfloat(value)
Daniel Brown's avatar
Daniel Brown committed
978
    
979 980 981 982 983 984 985 986 987 988 989 990
    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
        
Daniel Brown's avatar
Daniel Brown committed
991 992 993 994 995 996 997
    def parseAttributes(self, values):
        
        for key in values.keys():
            if key in ["gx","gouyx"]:
                self.__gx.value = values[key]
            elif key in ["gy", "gouyy"]:
                self.__gy.value = values[key]
998
            elif key in ["g","gouy"]:
Daniel Brown's avatar
Daniel Brown committed
999 1000 1001 1002 1003
                self.__gx.value = values[key]
                self.__gy.value = values[key]
            else:
                raise pkex.BasePyKatException("No attribute {0} for spaces".format(key))