node_network.py 11.2 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
        return getattr(self, '__node_' + name)        
        
203
204
205
206
207
208
209
    def __getitem__(self, value):
        return self.__nodes[str(value)]
        
    def __contains__(self, value):
        return value in self.__nodes
        
    
210
class Node(object):
Daniel Brown's avatar
Daniel Brown committed
211

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