node_network.py 10.8 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
12
from pykat.components import Component
from pykat.detectors import Detector

13
class NodeNetwork(object):
Daniel Brown's avatar
Daniel Brown committed
14
    def __init__(self, kat):
15
        self.__nodes = {}
Daniel Brown's avatar
Daniel Brown committed
16
        self.__kat = kat
17
18
19
20
21
        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
        
22
23
24
        cls = type(self)
        self.__class__ = type(cls.__name__, (cls,), {})
        
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
    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:
41
42
            n = self.createNode(name)
            self.connectNodeToComp(n, comp, do_callback=False)
43
44
45
46
47
48
49
            list.append(n)
        
        self.__componentNodes[comp.id] = tuple(list)
        self.__componentCallback[comp.id] = change_callback
        
        change_callback()
    
50
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
    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]()
            
81
82
83
84
    def connectNodeToComp(self, node, comp, do_callback=True):
        if node.id in self.__nodeComponents:
            comps = self.__nodeComponents[node.id]
        else:
85
            comps = (None,) * 2
86
        
87
        if len(comps) >= 2 and comps[0] != None and comps[1] != None:
88
89
90
            raise pkex.BasePyKatException("Node is already connected to 2 components")
        
        l = list(comps)
91
92
93
94
95
96
97
        
        if l[0] == None:
            l[0] = comp
        elif l[1] == None:
            l[1] = comp
        else:
            raise pkex.BasePyKatException("Connected to two coponents already")
98
99
100
101
        
        self.__nodeComponents[node.id] = tuple(l)
        
        if do_callback: self.__componentCallback[comp.id]()
Daniel Brown's avatar
Daniel Brown committed
102
103
104
105
106
        
    def createNode(self, node_name):
        if node_name == 'dump':
            return DumpNode()
            
107
        if node_name in self.__nodes:
108
            # then this node already exists
109
            return self.__nodes[node_name]
Daniel Brown's avatar
Daniel Brown committed
110
        else:
111
112
113
114
115
            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
116
            self.__nodeComponents[n.id] = (None, None)
Daniel Brown's avatar
Daniel Brown committed
117
            return n
118
119
120
121
122
        
    def removeNode(self, node):
        if not isinstance(node,Node):
            raise exceptions.ValueError("node argument is not of type Node")
        
123
        if node.name not in self.__nodes:
124
125
            raise exceptions.RuntimeError("Trying to remove node {0} when it has not been added".format(node.name))
        
126
        C = self.getNodeComponents(node)
127
        
128
129
        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
130
            
131
        if len(node.getDetectors()) > 0:
132
133
            raise exceptions.RuntimeError("Cannot remove a node which is attached to detectors still")
        
134
135
        self.__remove_node_attr(node)
        del self.__nodes[node.name] 
136
        
Daniel Brown's avatar
Daniel Brown committed
137
    def hasNode(self, name):
138
        return (name in self.__nodes)
139
140
    
    def getNodes(self):
141
        return self.__nodes.copy()
142
    
Daniel Brown's avatar
Daniel Brown committed
143
144
    def dumpInfo(self):
        
145
146
147
        for name in self.__nodes:
            
            n = self.__nodes[name]
Daniel Brown's avatar
Daniel Brown committed
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
            
            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)
173
    
174
175
176
177
178
    def getComponentNodes(self, comp):
        return self.__componentNodes[comp.id]
    
    def getNodeComponents(self, node):
        return self.__nodeComponents[node.id]
179
    
180
    def __add_node_attr(self, node):
181
182
183

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

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