From 9ca9dcbae9a655561c30c6656d0f97b7982d56c8 Mon Sep 17 00:00:00 2001
From: Daniel Brown <ddb@star.sr.bham.ac.uk>
Date: Thu, 19 Feb 2015 12:54:17 +0000
Subject: [PATCH] adding in Optivis functionality

---
 examples/LLO_matched.kat     |  44 +---------
 examples/optivis_aligo_ex.py |   6 +-
 examples/optivis_ex.py       |  16 +++-
 pykat/components.py          |  98 ++++++++++++++++++----
 pykat/finesse.py             | 152 +++++++++++++++++++++++++++++------
 5 files changed, 232 insertions(+), 84 deletions(-)

diff --git a/examples/LLO_matched.kat b/examples/LLO_matched.kat
index ab66558..b9c165c 100644
--- a/examples/LLO_matched.kat
+++ b/examples/LLO_matched.kat
@@ -379,47 +379,9 @@ noplot AS_f1_P
 noplot AS_f1_Q
 noplot AS_f2_P
 noplot AS_f2_Q
-noplot P_DC
+#noplot P_DC
 ###########################################################################
 %%% FTend errsigs
 
-
-%%% FTblock locks
-###########################################################################
-set PRCL_err POP_f1_P  re
-set MICH_err POP_f2_P  re
-set CARM_err REFL_f1_P re
-set SRCL_err REFL_f2_P re
-set DARMerr AS_f2_P re
-
-func DARMerr10pm = $DARMerr+0.02142517601
-lock DARM_lock $DARMerr10pm 0.081 1n
-
-lock PRCL_lock $PRCL_err 300  10u
-lock MICH_lock $MICH_err 400  0.1m
-lock CARM_lock $CARM_err -3m  0.1m
-lock SRCL_lock $SRCL_err 100  10u
-
-func mMICH_lock = 0 - $MICH_lock
-func ETMX_lock = $CARM_lock + $MICH_lock + $DARM_lock
-func ETMY_lock = $CARM_lock - $MICH_lock - $DARM_lock
-
-put* PRM   phi     $PRCL_lock
-put* ITMX  phi     $MICH_lock
-put* ETMX  phi     $ETMX_lock
-put* ITMY  phi     $mMICH_lock
-put* ETMY  phi     $ETMY_lock
-put* SRMHR phi     $SRCL_lock
-
-noplot PRCL_lock
-noplot SRCL_lock
-noplot MICH_lock
-noplot DARM_lock
-noplot CARM_lock
-noplot mMICH_lock
-noplot ETMX_lock
-noplot ETMY_lock
-noplot DARMerr10pm
-###########################################################################
-%%% FTend locks
-
+time
+noxaxis
\ No newline at end of file
diff --git a/examples/optivis_aligo_ex.py b/examples/optivis_aligo_ex.py
index 95764e2..0feb58f 100644
--- a/examples/optivis_aligo_ex.py
+++ b/examples/optivis_aligo_ex.py
@@ -1,6 +1,8 @@
 import pykat
 
-kat = pykat.finesse.kat(kat_file="LLO_matched.kat")
+kat = pykat.finesse.kat(kat_file="/Users/ddb/git/geo-simulation/geo-files/geoHF_v48.kat")
 
-kat.optivis().show()
+gui, scene = kat.optivis()
+
+gui.show()
 
diff --git a/examples/optivis_ex.py b/examples/optivis_ex.py
index cfa7b85..eba9810 100644
--- a/examples/optivis_ex.py
+++ b/examples/optivis_ex.py
@@ -9,7 +9,7 @@ s s1 100 n0 n3
 bs bs1 1 0 0 45 n3 n4 n5 n6
 
 s s3 50 n4 n4a
-m m1 1 0 0 n4a n4b
+m m1 0.9 0.1 0 n4a n4b
 s s3a 200 n4b n7a
 m m2 1 0 0 n7a n7b
 
@@ -17,7 +17,19 @@ s s4 50 n5 n5a
 m m3 1 0 0 n5a n5b
 #s s4a 200 n5b n8a
 #m m4 1 0 0 n8a n8b
+
+attr m1 Rc -500
+attr m2 Rc 500
+cav c1 m1 n4b m2 n7a
+
+maxtem 0
+noxaxis
 """)
 
-kat.optivis().show()
+out, tdata = kat.run(getTraceData=True)
+
+gui = kat.optivis()
+
+kat.optivis_updateTraceData(gui, tdata[0])
 
+gui.show()
diff --git a/pykat/components.py b/pykat/components.py
index 7593f1e..4aedf93 100644
--- a/pykat/components.py
+++ b/pykat/components.py
@@ -9,17 +9,21 @@ from __future__ import print_function
 from pykat import USE_GUI, HAS_OPTIVIS, NoGUIException
 
 import pykat.external.six as six
+
 if six.PY2:
 	import exceptions
+
 import pykat.exceptions as pkex
 import pykat
 from pykat.node_network import *
 from pykat.exceptions import *
 import abc
 import copy
+from collections import OrderedDict
 
 if HAS_OPTIVIS:
     import optivis.bench.components as optivis_components
+    from optivis.view.canvas import OptivisCanvasItemDataType
 
 from pykat.SIfloat import *
 from pykat.param import Param, AttrParam
@@ -93,6 +97,7 @@ class Component(object):
         self._params = []
         self.__removed = False
         self._default_fsig_param = None
+        self.optivisLabelContent = None
         
         # store a unique ID for this component
         global next_component_id
@@ -100,7 +105,6 @@ class Component(object):
         next_component_id += 1
         
        
-        
     def __deepcopy__(self, memo):
         """
         When deep copying a kat object we need to take into account
@@ -222,7 +226,38 @@ class Component(object):
         del self._params[:]
 
         self.__removed = True
+    
+    def getOptivisParameterDict(self):
+        if len(self._params) == 0:
+            return None
+            
+        d = OrderedDict()
+        
+        for p in self._params:
+            d[p.name] = OptivisCanvasItemDataType.TEXTBOX
+        
+        return d
+        
+    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
+
+
 class AbstractMirrorComponent(Component):
     __metaclass__ = abc.ABCMeta
         
@@ -258,12 +293,12 @@ class AbstractMirrorComponent(Component):
         self.__rxmech = AttrParam("rxmech", self, rxmech)
         self.__rymech = AttrParam("rymech", self, rymech)
         
-        self.__z = Param("z", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="z")
-        self.__rx = Param("rx", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="rx")
-        self.__ry = Param("ry", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="ry")
-        self.__Fz = Param("Fz", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Fz")
-        self.__Frx = Param("Frx", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Frx")
-        self.__Fry = Param("Fry", self, 0, canFsig=True, isPutable=False, isPutter=False, isTunable=False, fsig_name="Fry")
+        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")
         
         self._default_fsig_param = self.__phi
         
@@ -451,8 +486,10 @@ class mirror(AbstractMirrorComponent):
         return rtn
     
     def getOptivisComponent(self):
+        self.setOptivisLabelContent()
+        
         if self._optivis_component is None:
-            self._optivis_component = optivis_components.CavityMirror(name=self.name, aoi=0)
+            self._optivis_component = optivis_components.CavityMirror(name=self.name, aoi=0, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self))
         
         return self._optivis_component
     
@@ -509,8 +546,10 @@ class beamSplitter(AbstractMirrorComponent):
                     raise pkex.BasePyKatException("No attribute {0} for mirrors".format(key))
     
     def getOptivisComponent(self):
+        self.setOptivisLabelContent()
+        
         if self._optivis_component is None:
-            self._optivis_component = optivis_components.BeamSplitter(name=self.name, aoi=-self.alpha)
+            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
     
@@ -870,7 +909,33 @@ class isolator(Component):
             rtn.extend(p.getFinesseText())
             
         return rtn
-
+    
+    def getOptivisComponent(self):
+        self.setOptivisLabelContent()
+        
+        if self._optivis_component is None:
+            self._optivis_component = optivis_components.FaradayIsolator(name=self.name, 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("fr")
+            elif kat_node is self.nodes[1]:
+                return self._optivis_component.getInputNode("bk")
+        elif mode == "output":
+            if kat_node is self.nodes[0]:
+                return self._optivis_component.getnOutputNode("fr")
+            elif kat_node is self.nodes[1]:
+                return self._optivis_component.getOutputNode("bk")
+     
+     
     def getQGraphicsItem(self):
         if not USE_GUI:
             raise NoGUIException
@@ -917,8 +982,10 @@ class lens(Component):
         return rtn
     
     def getOptivisComponent(self):
+        self.setOptivisLabelContent()
+        
         if self._optivis_component is None:
-            self._optivis_component = optivis_components.ConvexLens(name=self.name)
+            self._optivis_component = optivis_components.ConvexLens(name=self.name, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self))
         
         return self._optivis_component
     
@@ -1021,9 +1088,10 @@ class modulator(Component):
         return rtn
         
     def getOptivisComponent(self):
+        self.setOptivisLabelContent()
+        
         if self._optivis_component is None:
-            #self._optivis_component = optivis_components.Modulator(name=self.name)
-            self._optivis_component = optivis_components.ConvexLens(name=self.name)
+            self._optivis_component = optivis_components.ElectroopticModulator(name=self.name, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self))
             
         return self._optivis_component
     
@@ -1115,8 +1183,10 @@ class laser(Component):
         return rtn
 
     def getOptivisComponent(self):
+        self.setOptivisLabelContent()
+        
         if self._optivis_component is None:
-            self._optivis_component = optivis_components.Laser(name=self.name)
+            self._optivis_component = optivis_components.Laser(name=self.name, tooltip=self.getOptivisTooltip, paramList=self.getOptivisParameterDict(), pykatObject=weakref.ref(self))
         
         return self._optivis_component
     
diff --git a/pykat/finesse.py b/pykat/finesse.py
index c379195..50a65b7 100644
--- a/pykat/finesse.py
+++ b/pykat/finesse.py
@@ -56,6 +56,11 @@ import pykat.exceptions as pkex
 
 from pykat import USE_GUI, HAS_OPTIVIS, NoGUIException
 
+if HAS_OPTIVIS:
+    from optivis.bench.labels import Label as optivis_label
+    from optivis.geometry import Coordinates as optivis_coord
+    import PyQt4
+    
 if USE_GUI:
     from pykat.gui.gui import pyKatGUI
     from PyQt4.QtCore import QCoreApplication
@@ -156,7 +161,7 @@ def GUILength(L):
     """
     Should scale the lengths in some way to handle km and mm for time being
     """                                  
-    return L * ( 20 * erfc(L/2e3) + 0.01)
+    return L # * ( 40 * erfc(L/400.0) + 0.01)
     
 class katRun(object):
     def __init__(self):
@@ -914,7 +919,7 @@ class kat(object):
         
         return Process(target=f__lkat_process, args=(callback, cmd, kwargs))
            
-    def run(self, printout=0, printerr=0, plot=None, save_output=False, save_kat=False, kat_name=None, cmd_args=None) :
+    def run(self, printout=0, printerr=0, plot=None, save_output=False, save_kat=False, kat_name=None, cmd_args=None, getTraceData=False):
         """ 
         Runs the current simulation setup that has been built thus far.
         It returns a katRun or katRun2D object which is populated with the various
@@ -926,6 +931,11 @@ class kat(object):
         save_kat (bool) - if true does not delete kat file
         kat_name (string) - name of kat file if needed, will be randomly generated otherwise
         cmd_args (list of strings) - command line flags to pass to FINESSE
+        getTraceData (bool) - If true a list of dictionaries is returned along with the
+                              output file. Each dictionary is the result of the beam tracing
+                              that Finesse performs, the keys are the node names and the values
+                              are the x and y beam parameters. If no tracing is done a None
+                              is returned.
         """
         start = datetime.datetime.now()
         
@@ -996,6 +1006,9 @@ class kat(object):
             
             if cmd_args != None:
                 cmd.extend(cmd_args)
+            
+            if getTraceData:
+                cmd.append('--trace')
                 
             cmd.append('--no-backspace')
             # set default format so that less repeated numbers are printed to the
@@ -1059,13 +1072,57 @@ class kat(object):
             if p.returncode != 0:
                 print(err)
                 sys.exit(1) 
-            
+                
             self.__prevrunfilename = katfile.name
             
             root = os.path.splitext(katfile.name)
-            base = os.path.basename(root[0])            
+            base = os.path.basename(root[0])
+            path = os.path.split(katfile.name)[0]
+            
             outfile = root[0] + ".out"
             
+            traceData = None
+            
+            if getTraceData:
+                # First see if we have any trace files
+                
+                traceFiles = [file for file in os.listdir(path) if file.endswith(".trace") and file.startswith(base)]
+                
+                print("Found %i trace files" % len(traceFiles))
+                print(path)
+                print(traceFiles)
+                
+                if len(traceFiles) > 0:
+                    import fileinput
+                    traceData = []
+                    
+                    for file in traceFiles:
+                        traceData.append({})
+                        ifile = fileinput.input(os.path.join(path, file))
+                    
+                        for line in ifile:
+                            line = line.strip()
+                        
+                            if len(line) > 0:
+                                a = line.split(':', 1)
+                        
+                                if a[0].isdigit():
+                                    print("Found %s" % a[0])
+                                
+                                    values = a[1].split()
+                                
+                                    node_name = values[1].split("(")[0]
+                                
+                                    line1x = ifile.readline()
+                                    line2x = ifile.readline()
+                                    line1y = ifile.readline()
+                                    line2y = ifile.readline()
+
+                                    qx = line2x.strip().split()[0].split("=")[1]
+                                    qy = line2y.strip().split()[0].split("=")[1]
+                                
+                                    traceData[-1][node_name] = (pykat.beam_param(q=complex(qx)), pykat.beam_param(q=complex(qy)))
+                                                
             if save_output:        
                 newoutfile = "{0}.out".format(base)
                 
@@ -1079,18 +1136,19 @@ class kat(object):
 
                 if self.verbose: print("\nOutput data saved to '{0}'".format(newoutfile))
             
-            if hasattr(self, "x2axis") and self.noxaxis == False:
-                [r.x,r.y,r.z,hdr] = self.readOutFile(outfile)
+            if len(self.detectors.keys()) > 0:
+                if hasattr(self, "x2axis") and self.noxaxis == False:
+                    [r.x,r.y,r.z,hdr] = self.readOutFile(outfile)
                 
-                r.xlabel = hdr[0]
-                r.ylabel = hdr[1]
-                r.zlabels = map(str.strip, hdr[2:])
+                    r.xlabel = hdr[0]
+                    r.ylabel = hdr[1]
+                    r.zlabels = map(str.strip, hdr[2:])
                          
-            else:
-                [r.x,r.y,hdr] = self.readOutFile(outfile)
+                else:
+                    [r.x,r.y,hdr] = self.readOutFile(outfile)
             
-                r.xlabel = hdr[0]
-                r.ylabels = map(str.strip, hdr[1:])
+                    r.xlabel = hdr[0]
+                    r.ylabels = map(str.strip, hdr[1:])
                     
             if save_kat:
                 if kat_name == None:
@@ -1119,6 +1177,8 @@ class kat(object):
             katfile.close()
             perfData = []
             
+            rtn = [r]
+            
             if self.__time_code:
                 perffile = open(root[0] + ".perf",'r')
                 
@@ -1126,9 +1186,15 @@ class kat(object):
                     vals = l.strip().split()
                     perfData.append((vals[0], long(vals[1]), long(vals[2])))
                     
-                return [r, perfData]
+                rtn.append(perfData)
+            
+            if getTraceData:
+                rtn.append(traceData)
+                
+            if len(rtn) == 1:
+                return rtn[0]
             else:
-                return r
+                return rtn
             
         except pkex.FinesseRunError as fe:
             print(fe)
@@ -1470,12 +1536,6 @@ class kat(object):
         
         scene = scene.Scene(title="My pykat layout")
         
-        # Run through once to add components, ignoring spaces
-        for c in self.getComponents():
-            if isinstance(c, pykat.components.space): continue
-            
-            optivis_op = getattr(c, "getOptivisComponent", None)
-            
         # Run through again to add links
         for c in self.getComponents():
             if not isinstance(c, pykat.components.space):
@@ -1497,12 +1557,54 @@ class kat(object):
             if no is None or ni is None:
                 raise pkex.BasePyKatException("Optivis node is None")
             
-            scene.addLink(links.Link(no, ni, GUILength(c.L.value) ))
-                
-        gui = canvas.Simple(scene=scene)
+            c._optivis_component = links.Link(outputNode=no, inputNode=ni, length=c.L.value)
+            
+            c.label_node1 = optivis_label(text="", position=optivis_coord(-0.5, 0), item=c._optivis_component)
+            c.label_node2 = optivis_label(text="", position=optivis_coord( 0.5, 0), item=c._optivis_component)
+            
+            c._optivis_component.labels.append(c.label_node1)
+            c._optivis_component.labels.append(c.label_node2)
+            
+            scene.addLink(c._optivis_component)
+        
+        gui = canvas.Full(scene=scene)
         
+        ### add menu and menu items
+        menubar = gui.qMainWindow.menuBar()
+        fileMenu = menubar.addMenu('&Pykat')
+    
+        trace = PyQt4.QtGui.QAction('Trace', gui.qMainWindow)
+        trace.triggered.connect(self.optivis_updateTraceData)
+        fileMenu.addAction(trace)
+    
         return gui
+    
+    def optivis_updateTraceData(self, gui, tdata):
+        """
+        Change at some point to use a stored GUI reference
+        """
+        if not HAS_OPTIVIS:
+            print("Optivis is not installed")
+            return None
+        
+        for c in self.getComponents():
+            if not isinstance(c, pykat.components.space):
+                continue
         
+            c.label_node1.content["w0_x"] = tdata[c.nodes[0].name][0].w0
+            c.label_node1.content["w_x"] = tdata[c.nodes[0].name][0].w
+            c.label_node1.content["z_x"] = tdata[c.nodes[0].name][0].z
+            c.label_node1.content["Rc_x"] = tdata[c.nodes[0].name][0].Rc
+            c.label_node1.content["Zr_x"] = tdata[c.nodes[0].name][0].zr
+            
+            c.label_node1.content["w0_y"] = tdata[c.nodes[0].name][1].w0
+            c.label_node1.content["w_y"] = tdata[c.nodes[0].name][1].w
+            c.label_node1.content["z_y"] = tdata[c.nodes[0].name][1].z
+            c.label_node1.content["Rc_y"] = tdata[c.nodes[0].name][1].Rc
+            c.label_node1.content["Zr_y"] = tdata[c.nodes[0].name][1].zr
+            
+        gui.draw()
+    
     def openGUI(self):
         if not USE_GUI:
             raise NoGUIException
@@ -1521,7 +1623,7 @@ class kat(object):
                 self.pykatgui.show()
             
             if created: self.app.exec_()
-    
+        
     def getComponents(self):
         return self.__components.values()
     
-- 
GitLab