node_network.py 11 KB
Newer Older
Daniel Brown's avatar
Daniel Brown committed
1
2
3
4
5
6
7
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 27 10:02:41 2013

@author: Daniel
"""
import exceptions
8
import pykat.gui.graphics
9
import pykat.exceptions as pkex
Daniel Brown's avatar
Daniel Brown committed
10
11
from pykat.components import Component
from pykat.detectors import Detector
12
from pykat.utilities.optics.gaussian_beams import gauss_param
Daniel Brown's avatar
Daniel Brown committed
13

14
class NodeNetwork(object):
Daniel Brown's avatar
Daniel Brown committed
15
    def __init__(self, kat):
16
        self.__nodes = {}
Daniel Brown's avatar
Daniel Brown committed
17
        self.__kat = kat
18
19
20
21
22
        self.__nodeComponents = {} # dictionary of tuples containing which components are connected to a node
        self.__componentNodes = {} # dictionary of tuples containing which nodes are connected to a given component
        self.__componentCallback = {}
        self.__node_id = 1
        
23
24
25
        cls = type(self)
        self.__class__ = type(cls.__name__, (cls,), {})
        
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
    def registerComponentNodes(self, comp, node_names, change_callback):
        """
        For a given component we create some nodes or get existing ones and 
        attach them to this component. Also specify a callback function that
        is called whenever the nodes attached to this component are changed
        , e.g. connected, disconnected, name change, etc.
        """
        if not isinstance(comp, Component):
            raise exceptions.ValueError("comp argument is not of type Component")
        
        if comp.id in self.__componentNodes:
            raise pkex.BasePyKatException("Component has already been registered")
        
        list = []
        
        for name in node_names:
42
43
            n = self.createNode(name)
            self.connectNodeToComp(n, comp, do_callback=False)
44
45
46
47
48
49
50
            list.append(n)
        
        self.__componentNodes[comp.id] = tuple(list)
        self.__componentCallback[comp.id] = change_callback
        
        change_callback()
    
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
    def replaceNode(self, comp, node_old, node_new):
        
        if node_new.components.count(None) == 0:
            raise pkex.BasePyKatException("New node already connected to two components")
            
        if comp not in node_old.components:
            raise pkex.BasePyKatException("Old node not attached to component")
        
        if comp in node_new.components:
            raise pkex.BasePyKatException("New node already attached to component")
        
        # add component to new node component list
        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)
        
        # 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)
        
        comp_nodes = list(comp.nodes)
        comp_nodes[comp_nodes.index(node_old)] = node_new
        self.__componentNodes[comp.id] = tuple(comp_nodes)
        
        # if old node is no longer connected to anything then delete it
        if node_old.components.count(None) == 2:
            self.removeNode(node_old)
            
        self.__componentCallback[comp.id]()
            
82
83
84
85
    def connectNodeToComp(self, node, comp, do_callback=True):
        if node.id in self.__nodeComponents:
            comps = self.__nodeComponents[node.id]
        else:
86
            comps = (None,) * 2
87
        
88
        if len(comps) >= 2 and comps[0] != None and comps[1] != None:
89
90
91
            raise pkex.BasePyKatException("Node is already connected to 2 components")
        
        l = list(comps)
92
93
94
95
96
97
98
        
        if l[0] == None:
            l[0] = comp
        elif l[1] == None:
            l[1] = comp
        else:
            raise pkex.BasePyKatException("Connected to two coponents already")
99
100
101
102
        
        self.__nodeComponents[node.id] = tuple(l)
        
        if do_callback: self.__componentCallback[comp.id]()
Daniel Brown's avatar
Daniel Brown committed
103
104
105
106
107
        
    def createNode(self, node_name):
        if node_name == 'dump':
            return DumpNode()
            
108
        if node_name in self.__nodes:
109
            # then this node already exists
110
            return self.__nodes[node_name]
Daniel Brown's avatar
Daniel Brown committed
111
        else:
112
113
114
115
116
            n = Node(node_name, self, self.__node_id)
            
            self.__node_id += 1
            self.__add_node_attr(n) # add node as a member of this object, e.g. kat.nodes.n
            self.__nodes[node_name] = n
117
            self.__nodeComponents[n.id] = (None, None)
Daniel Brown's avatar
Daniel Brown committed
118
            return n
119
120
121
122
123
        
    def removeNode(self, node):
        if not isinstance(node,Node):
            raise exceptions.ValueError("node argument is not of type Node")
        
124
        if node.name not in self.__nodes:
125
126
            raise exceptions.RuntimeError("Trying to remove node {0} when it has not been added".format(node.name))
        
127
        C = self.getNodeComponents(node)
128
        
129
130
        if C[0] is not None or C[1] is not None:
            raise exceptions.RuntimeError("Cannot remove a node which is attached to components")
Daniel Brown's avatar
Daniel Brown committed
131
            
132
        if len(node.getDetectors()) > 0:
133
134
            raise exceptions.RuntimeError("Cannot remove a node which is attached to detectors still")
        
135
136
        self.__remove_node_attr(node)
        del self.__nodes[node.name] 
137
        
Daniel Brown's avatar
Daniel Brown committed
138
    def hasNode(self, name):
139
        return (name in self.__nodes)
140
141
    
    def getNodes(self):
142
        return self.__nodes.copy()
143
    
Daniel Brown's avatar
Daniel Brown committed
144
145
    def dumpInfo(self):
        
146
147
148
        for name in self.__nodes:
            
            n = self.__nodes[name]
Daniel Brown's avatar
Daniel Brown committed
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
            
            items = n.getComponents()
            comp = items[0][:]
            det = items[1]
            
            if comp[0] == None:
                comp1 = 'dump'
            else:
                comp1 = comp[0].name
            
            if comp[1] == None:
                comp2 = 'dump'
            else:
                comp2 = comp[1].name    
            
            detectors = ""
            
            if len(det) > 0:
                detectors = "Detectors: "
                
                for d in det:
                    detectors = detectors + d.name + " "
                
            print "node: {0} connected:{1} {2}->{3} {4}".format(
                    n.name,n.isConnected(),comp1, comp2, detectors)
174
    
175
176
177
178
179
    def getComponentNodes(self, comp):
        return self.__componentNodes[comp.id]
    
    def getNodeComponents(self, node):
        return self.__nodeComponents[node.id]
180
    
181
    def __add_node_attr(self, node):
182
183
184

        if not isinstance(node, Node):
            raise exceptions.ValueError("Argument is not of type Node")
Daniel Brown's avatar
Daniel Brown committed
185
        
186
        name = node.name
187
        fget = lambda self: self.__get_node_attr(name)
188
        
189
        setattr(self.__class__, name, property(fget))
190
        setattr(self, '__node_' + name, node)                   
Daniel Brown's avatar
Daniel Brown committed
191
    
192
    def __remove_node_attr(self, node):
193
194
195
196
        if not isinstance(node, Node):
            raise exceptions.ValueError("Argument is not of type Node")
        
        name = node.name
197
198
        delattr(self, '__node_' + name)
        delattr(self.__class__, name)
199
        
200
    def __get_node_attr(self, name):
201
202
203
        return getattr(self, '__node_' + name)        
        
class Node(object):
Daniel Brown's avatar
Daniel Brown committed
204

205
    def __init__(self, name, network, id):
Daniel Brown's avatar
Daniel Brown committed
206
207
        self._detectors = []
        self.__name = name
208
        self._item = None
Daniel Brown's avatar
Daniel Brown committed
209
        self._network = network
Daniel Brown's avatar
Daniel Brown committed
210
211
212
        self.__q_x = None
        self.__q_y = None
        self.__q_comp = None
213
        self.__id = id
214
        
215
216
217
    @property
    def id(self): return self.__id
    
218
219
    @property
    def network(self): return self._network
Daniel Brown's avatar
Daniel Brown committed
220
    
221
222
223
    @property
    def components(self): return self._network.getNodeComponents(self)
    
224
    @property
Daniel Brown's avatar
Daniel Brown committed
225
226
227
228
229
230
231
232
233
234
    def q(self):
        if self.__q_x == self.__q_y:
            return self.__q_x
        else:
            return (self.__q_x, self.__q_y)
            
    @property
    def qx(self): return self.__q_x
    @property
    def qy(self): return self.__q_y
235
236
    
    def removeGauss():
Daniel Brown's avatar
Daniel Brown committed
237
238
239
240
241
242
243
244
        self.__q_x = None
        self.__q_y = None
        self.__q_comp = None
    
    def setGauss(self, component, *args):
        self.__q_comp = component
        
        if len(args) == 1:
245
246
            self.__q_x = gauss_param(q=args[0])
            self.__q_y = gauss_param(q=args[0])
Daniel Brown's avatar
Daniel Brown committed
247
        elif len(args) == 2:
248
249
            self.__q_x = gauss_param(q=args[0])
            self.__q_y = gauss_param(q=args[1])
Daniel Brown's avatar
Daniel Brown committed
250
251
        else:
            raise pkex.BasePyKatException("Must specify either 1 Gaussian beam parameter or 2 for astigmatic beams")
252
        
Daniel Brown's avatar
Daniel Brown committed
253
254
255
    def getFinesseText(self):    
        if self.__q_x is None or self.__q_y is None or self.__q_comp is None:
            return []
256
            
Daniel Brown's avatar
Daniel Brown committed
257
        rtn = []
258
        
Daniel Brown's avatar
Daniel Brown committed
259
260
261
262
263
264
        if self.__q_x == self.__q_y:
            rtn.append("gauss* g_{node} {comp} {node} {z} {zr}".format(node=self.name, comp=self.__q_comp.name, z=self.__q_x.real, zr=self.__q_x.imag))
        else:
            rtn.append("gauss* g_{node} {comp} {node} {zx} {zrx} {zy} {zry}".format(node=self.name, comp=self.__q_comp.name, zx=self.__q_x.real, zrx=self.__q_x.imag, zy=self.__q_y.real, zry=self.__q_y.imag))
            
        return rtn
265
        
Daniel Brown's avatar
Daniel Brown committed
266
    def isConnected(self):
267
        if (self.components[0] is not None) and (self.components[1] is not None):
268
269
270
271
272
273
            return True
        else:
            return False
      
    def remove(self):
        self._network.removeNode(self)
Daniel Brown's avatar
Daniel Brown committed
274
275
276
277
        
        if self._item != None:
            self._item.scene().removeItem(self._item)
    
278
279
280
281
282
    def getQGraphicsItem(self,dx=0,dy=0,nsize=8,parent=None):
        if self._item == None:
            self._item = pykat.gui.graphics.NodeQGraphicItem(self,
                                                             dx,dy,
                                                             -nsize/2,-nsize/2,
283
                                                             nsize, nsize, parent)
284
285
286
            
        return self._item
    
287
288
    def getDetectors(self):
        return self._detectors[:]
Daniel Brown's avatar
Daniel Brown committed
289
        
290
291
    def amIConnected(self, obj):
        """
Daniel Brown's avatar
Daniel Brown committed
292
        Checks if obj is connected to the node. Returns true or false in tuple
293
294
        with None or the other object and the node index which it is attached to
        """ 
295
296
297
298
        comps = self.components
        
        if obj == comps[0]:
            if comps[1] == None:
299
300
                ix = -1
            else:
301
                ix = comps[1].nodes.index(self)
302
                
303
304
305
306
            return [True, comps[1], ix]
            
        elif obj == comps[1]:
            if comps[0] == None:
307
308
                ix = -1
            else:
309
                ix = comps[0].nodes.index(self)
310
                
311
            return [True, comps[0], ix]
312
313
314
        else:
            return [False, None]
        
315
316
317
    @property
    def name(self): return self.__name      
    
Daniel Brown's avatar
Daniel Brown committed
318
319
320
    
class DumpNode(Node):
    def __init__(self):
Daniel Brown's avatar
Daniel Brown committed
321
        Node.__init__(self, 'dump', None, -1)
Daniel Brown's avatar
Daniel Brown committed
322
323