diff --git a/pykat/__init__.py b/pykat/__init__.py
index 4dda77b69059678d5f39a95c805adca1d778d013..f91c9789644c76b1be75846bf1fe1a8e4f6f2e51 100644
--- a/pykat/__init__.py
+++ b/pykat/__init__.py
@@ -1,7 +1,18 @@
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
 __version__ = "0.6.2"
 
 # This flag is used to switch on the gui features in pkat at import time
 USE_GUI = False
+HAS_OPTIVIS = False
+import imp
+try:
+	imp.find_module('optivis')
+	HAS_OPTIVIS = True
+except ImportError:
+	HAS_OPTIVIS = False
 
 import pykat.exceptions as pkex
 
diff --git a/pykat/components.py b/pykat/components.py
index f21bb74135a79552a5af42d92bf3990479ab6907..42d63af071e8b5f903150b3b3581f442089c0b63 100644
--- a/pykat/components.py
+++ b/pykat/components.py
@@ -9,7 +9,7 @@ from __future__ import division
 from __future__ import print_function
 from __future__ import unicode_literals
 
-from pykat import USE_GUI, NoGUIException
+from pykat import USE_GUI, HAS_OPTIVIS, NoGUIException
 
 import pykat.external.six as six
 
@@ -21,15 +21,22 @@ 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 optivis.bench.labels import Label as optivis_label
+    from optivis.geometry import Coordinates as optivis_coord
+    import PyQt4
 
 from pykat.SIfloat import *
 from pykat.param import Param, AttrParam
 import weakref
 import pykat.exceptions as pkex
-from copy import deepcopy
-next_component_id = 1
 
-from pykat import USE_GUI, NoGUIException
+next_component_id = 1
 
 if USE_GUI:
     import pykat.gui.resources
@@ -78,8 +85,16 @@ class NodeGaussSetter(object):
         
 class Component(object):
     __metaclass__ = abc.ABCMeta
-    
-    def __init__(self, name):
+
+    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
+        return object.__new__(type(cls.__name__, (cls,), {}), *args, **kwargs)
+        
+    def __init__(self, name=None):
+        
+        self._optivis_component = None
         self.__name = name
         self._svgItem = None
         self._requested_node_names = []
@@ -88,18 +103,26 @@ 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
         self.__id = next_component_id
         next_component_id += 1
         
-        # This creates an instance specific class for the component
-        # this enables us to add properties to instances rather than
-        # all classes
-        cls = type(self)
-        self.__class__ = type(cls.__name__, (cls,), {})
-    
+       
+    def __deepcopy__(self, memo):
+        """
+        When deep copying a kat object we need to take into account
+        the instance specific properties.
+        """
+        result = self.__class__.__new__(self.__class__)
+        result.__dict__ = copy.deepcopy(self.__dict__, memo)
+        
+        result.__update_node_setters             
+        
+        return result
+        
     def _register_param(self, param):
         self._params.append(param)
     
@@ -209,20 +232,38 @@ class Component(object):
         del self._params[:]
 
         self.__removed = True
-
-    def __deepcopy__(self, memo):
-        cls = self.__class__
-        result = cls.__new__(cls)
-        memo[id(self)] = result
+    
+    def getOptivisParameterDict(self):
+        if len(self._params) == 0:
+            return None
+            
+        d = OrderedDict()
         
-        for k, v in self.__dict__.items():
-            setattr(result, k, deepcopy(v, memo))
+        for p in self._params:
+            d[p.name] = OptivisCanvasItemDataType.TEXTBOX
         
-        for p in result._params:
-            p._updateOwner(result)
+        return d
         
-        return result
-            
+    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 +299,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
         
@@ -449,6 +490,37 @@ class mirror(AbstractMirrorComponent):
             rtn.extend(p.getFinesseText())
                     
         return rtn
+    
+    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")
+          
         
     def getQGraphicsItem(self):
         if not USE_GUI:
@@ -482,7 +554,40 @@ class beamSplitter(AbstractMirrorComponent):
                     self.alpha = values[key]
                 else:
                     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, 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")
+                     
     @staticmethod
     def parseFinesseText(text):
         values = text.split()
@@ -579,6 +684,18 @@ class space(Component):
     @gy.setter
     def gy(self,value): self.__gy.value = SIfloat(value)
     
+    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
+        
     def parseAttributes(self, values):
         
         for key in values.keys():
@@ -802,7 +919,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
@@ -847,7 +990,32 @@ class lens(Component):
             rtn.extend(p.getFinesseText())
             
         return rtn
+    
+    def getOptivisComponent(self):
+        self.setOptivisLabelContent()
         
+        if self._optivis_component is None:
+            self._optivis_component = optivis_components.ConvexLens(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
@@ -929,6 +1097,31 @@ class modulator(Component):
             
         return rtn
         
+    def getOptivisComponent(self):
+        self.setOptivisLabelContent()
+        
+        if self._optivis_component is None:
+            self._optivis_component = optivis_components.ElectroopticModulator(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
@@ -998,7 +1191,29 @@ class laser(Component):
             rtn.extend(p.getFinesseText())
         
         return rtn
-         
+
+    def getOptivisComponent(self):
+        self.setOptivisLabelContent()
+        
+        if self._optivis_component is None:
+            self._optivis_component = optivis_components.Laser(name=self.name, 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")
+        
+        if mode == "input":
+            return None
+        elif mode == "output":
+            return self._optivis_component.getOutputNode("out")
+                
     def getQGraphicsItem(self):
         if not USE_GUI:
             raise NoGUIException
diff --git a/pykat/detectors.py b/pykat/detectors.py
index acca83ca2b6f3231bd78e0f4d9129e207c4f3c79..a14bfbc1618cc384829b03bda7a56cf7edbfed50 100644
--- a/pykat/detectors.py
+++ b/pykat/detectors.py
@@ -11,11 +11,11 @@ from __future__ import unicode_literals
 
 import pykat.external.six as six
 if six.PY2:
-	import exceptions
+    import exceptions
 
 import abc
 from pykat.node_network import *
-from pykat.param import Param
+from pykat.param import Param, AttrParam
 from pykat.SIfloat import SIfloat
 
 import pykat.external.six as six
@@ -356,7 +356,13 @@ class bp(Detector1):
 
 class pd(Detector1):
 
-    def __init__(self, name, num_demods, node_name, senstype=None, alternate_beam=False, pdtype=None, **kwargs):
+    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
+        return object.__new__(type(cls.__name__, (cls,), {}), *args, **kwargs)
+
+    def __init__(self, name=None, num_demods=1, node_name=None, senstype=None, alternate_beam=False, pdtype=None, **kwargs):
         BaseDetector.__init__(self, name, node_name)
         
         self.__num_demods = num_demods
@@ -402,12 +408,25 @@ class pd(Detector1):
             elif i<num_demods-1:
                 raise pkex.BasePyKatException("Missing demodulation phase {0} (phi{0})".format(i+1))
         
-        # define new class for assigning new attributes
-        cls = type(self)
-        self.__class__ = type(cls.__name__, (cls,), {})
-    
+   
         self.__set_demod_attrs()
-        
+
+
+    def __deepcopy__(self, memo):
+        """
+        When deep copying a kat object we need to take into account
+        the instance specific properties.
+        """
+        result = pd(self.name, self.num_demods, self.node.name)
+        memo[id(self)] = result
+        result.__dict__ = copy.deepcopy(self.__dict__, memo)
+        # Find all properties in class we are copying
+        # and deep copy these to the new class instance
+        for x in self.__class__.__dict__.items():
+            if isinstance(x[1], property):
+                setattr(result.__class__, x[0], x[1])
+        return result       
+                
     @property
     def senstype(self): return self.__senstype
     @senstype.setter
@@ -666,8 +685,8 @@ class qnoised(pd):
 class qshot(pd):
     
     def __init__(self, name, num_demods, node_name, alternate_beam=False, **kwargs):
-        super(qnoised, self).__init__(name, num_demods, node_name, alternate_beam=alternate_beam, pdtype=None, senstype=None, **kwargs)
-    
+        super(qshot, self).__init__(name, num_demods, node_name, alternate_beam=alternate_beam, pdtype=None, senstype=None, **kwargs)     
+
     @pd.pdtype.setter
     def pdtype(self, value):
         raise pkex.BasePyKatException("Setting pdtype is not possible with qshot detectors")
diff --git a/pykat/finesse.py b/pykat/finesse.py
index 610d64bcd6e4c20f883354caf6cad3d55f846853..0a614967295049cded3ee0c352d6fa7bcbfb89bd 100644
--- a/pykat/finesse.py
+++ b/pykat/finesse.py
@@ -34,6 +34,7 @@ import subprocess
 import tempfile
 import numpy as np
 import datetime
+import time
 import pickle
 import pykat
 import warnings
@@ -63,6 +64,8 @@ except ImportError: # not 2.6+ or is 3.x
         print("boom")
         pass
 """
+
+from math import erfc, pi
 from collections import namedtuple, OrderedDict
 
 from pykat.node_network import NodeNetwork
@@ -76,7 +79,13 @@ import pykat.external.six as six
 
 import pykat.exceptions as pkex
 
-from pykat import USE_GUI, NoGUIException
+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
@@ -174,9 +183,16 @@ def f__lkat_trace_callback(lkat, trace_info, getCavities, getNodes, getSpaces):
                                                  gouyy = space.gouy_y)
                      
                                              
+def GUILength(L):
+    """
+    Should scale the lengths in some way to handle km and mm for time being
+    """
+    return L # * ( 40 * erfc(L/400.0) + 0.01)
+            
 class katRun(object):
     def __init__(self):
-        self.runDateTime = datetime.datetime.now()
+        self.runtime = None
+        self.StartDateTime = datetime.datetime.now()
         self.x = None
         self.y = None
         self.xlabel = None
@@ -185,7 +201,7 @@ class katRun(object):
         self.katVersion = None
         self.yaxis = None
         
-    def plot(self):
+    def plot(self, logy=False):
         import pylab
         
         pylab.plot(self.x, self.y)
@@ -206,9 +222,10 @@ class katRun(object):
     
     def __getitem__(self, value):
         idx = [i for i in range(len(self.ylabels)) if self.ylabels[i].split()[0] == str(value)]
-
+        out = None
+        
         if len(idx) > 0:
-            out = self.y[:, idx]
+            #out = self.y[:, idx]
             
             if len(idx) == 1:
                 if self.yaxis == "abs:deg":
@@ -220,7 +237,10 @@ class katRun(object):
                     out = self.y[:, idx[0]] * np.exp(1j*math.pi*self.y[:, idx[1]]/180.0)
                 elif self.yaxis == "re:im":
                     out = self.y[:, idx[0]] + 1j*self.y[:, idx[1]]
-            
+
+            if out == None:
+                out = self.y[:, idx]
+
             if out.size == 1:
                 return out[0].squeeze()
             else:
@@ -230,7 +250,8 @@ class katRun(object):
       
 class katRun2D(object):
     def __init__(self):
-        self.runDateTime = datetime.datetime.now()
+        self.runtime
+        self.startDateTime = datetime.datetime.now()
         self.x = None
         self.y = None
         self.z = None
@@ -270,7 +291,7 @@ class Signals(object):
             self.__phase = Param("phase", self, SIfloat(phase))
             self.__removed = False
             self.__signal = signal
-			
+            
             # unfortunatenly the target names for fsig are not the same as the
             # various parameter names of the components, e.g. mirror xbeta is x 
             # for fsig. So we need to check here what type of component we are targetting
@@ -397,11 +418,19 @@ class Block:
 Constant = namedtuple('Constant', 'name, value, usedBy')
     
 class kat(object):  
-        
-    def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None):
 
-        cls = type(self)
-        self.__class__ = type(cls.__name__, (cls,), {})
+    def __new__(cls, *args, **kwargs):
+        # This may seem like an arbitrary step but here we are creating a
+        # new class that is a base class of itself. This is because when
+        # the kat object adds new components it also adds properties for
+        # each of these. There properties are unique to each kat object,
+        # but properties are part of the class definition. Thus if two
+        # kat objects share the same class definition they also have the
+        # same properties regardless of whether they have the actual
+        # object added to it. So we create an instance specific class.
+        return object.__new__(type(pykat.finesse.kat.__name__, (pykat.finesse.
+                                                                kat,), {}), *args, **kwargs)
+    def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None):
         
         self.scene = None # scene object for GUI
         self.verbose = True
@@ -512,9 +541,9 @@ class kat(object):
     def phase(self,value): self.__phase = int(value)
         
     @property
-    def getPerformanceData(self): return self.__time_code
-    @getPerformanceData.setter
-    def getPerformanceData(self,value): self.__time_code = bool(value)
+    def timeCode(self): return self.__time_code
+    @timeCode.setter
+    def timeCode(self,value): self.__time_code = bool(value)
     
     @property
     def components(self):
@@ -730,9 +759,9 @@ class kat(object):
                             raise pkex.BasePyKatException("maxtem command `{0}` is incorrect.".format(line))
                         else:
                             if v[1] == "off":
-                            	self.maxtem = -1
+                                self.maxtem = -1
                             else:
-    	                        self.maxtem = int(v[1])
+                                self.maxtem = int(v[1])
                     elif(first == "trace"):
                         v = line.split()
                         if len(v) > 2:
@@ -877,7 +906,8 @@ class kat(object):
                         amp = float(v[6])
                     else:
                         raise pkex.BasePyKatException("'{0}' isnot a valid fsig command".format(line))
-                
+                    
+                    self.signals.f = freq
                     self.signals.apply(comp._default_fsig(), amp, phase, name)
                 
                 else:
@@ -912,8 +942,8 @@ class kat(object):
         cmd = "\n".join(self.generateKatScript())
         
         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
@@ -925,6 +955,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()
         
@@ -966,7 +1001,8 @@ class kat(object):
             r.yaxis = self.yaxis
             
             r.katScript = "".join(self.generateKatScript())
-            
+            r.katScript += "time\n"
+
             if (plot==None):
                 # ensure we don't do any plotting. That should be handled
                 # by user themselves
@@ -996,6 +1032,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
@@ -1060,7 +1099,13 @@ class kat(object):
 
             
             [out,errpipe] = p.communicate()
-            
+
+            _out = out.split("\n")
+
+            for line in _out[::-1]:
+                if line.lstrip().startswith('computation time:'):
+                    r.runtime = float(line.split(":")[1].replace("s",""))
+
             if printout == 1: 
                 print (out)
             else:
@@ -1080,8 +1125,52 @@ class kat(object):
             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)
@@ -1095,20 +1184,21 @@ class kat(object):
                 os.rename(outfile, newoutfile)
 
                 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 = [s.strip() for s in hdr[2:]]
-                #r.zlabels = map(str.strip, hdr[2:])
-            else:
-                [r.x,r.y,hdr] = self.readOutFile(outfile)
+                    r.xlabel = hdr[0]
+                    r.ylabel = hdr[1]
+                    r.zlabels = [s.strip() for s in hdr[2:]]
+                    #r.zlabels = map(str.strip, hdr[2:])
+                else:
+                    [r.x,r.y,hdr] = self.readOutFile(outfile)
             
-                r.xlabel = hdr[0]
-                r.ylabels = [s.strip() for s in hdr[1:]]
-                #r.ylabels = map(str.strip, hdr[1:]) // replaced 090415 adf 
+                    r.xlabel = hdr[0]
+                    r.ylabels = [s.strip() for s in hdr[1:]]
+                    #r.ylabels = map(str.strip, hdr[1:]) // replaced 090415 adf 
                     
             if save_kat:
                 if kat_name == None:
@@ -1136,18 +1226,27 @@ class kat(object):
 
             katfile.close()
             perfData = []
+
+            rtn = [r]
             
             if self.__time_code:
                 perffile = open(root[0] + ".perf",'r')
                 
                 for l in perffile.readlines():
                     vals = l.strip().split()
-                    perfData.append((vals[0], float(vals[1]), float(vals[2]), float(vals[3])))
+                    perfData.append((vals[0], long(vals[1]), long(vals[2])))
+                    #perfData.append((vals[0], float(vals[1]), float(vals[2]), float(vals[3])))
                     
-                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 ex:
             pkex.PrintError("Error from Finesse:", ex)
         except pkex.BasePyKatException as ex:
@@ -1301,11 +1400,11 @@ class kat(object):
         if hasattr(self, "x2axis") and self.noxaxis == False:
             # need to parse 2D outputs slightly different as they are effectively 2D matrices
             # written in linear form
-            x = data[0::(1+self.x2axis.steps),0]
+            x = data[0::(1+self.x2axis.steps),0].squeeze()
             y = data[0:(1+self.x2axis.steps),1]
             # get rows and columns lined up so that we can reshape a single column of all x/y data
             # into a matrix
-            z = data[:,2:].transpose().reshape(data.shape[1]-2, 1+self.xaxis.steps, 1+self.x2axis.steps)
+            z = data[:,2:].transpose().reshape(data.shape[1]-2, 1+self.xaxis.steps, 1+self.x2axis.steps).squeeze()
             # once you do this the data for y and x axes need swapping
             z = z.swapaxes(1,2)
             return [x, y, z, hdr]
@@ -1314,19 +1413,22 @@ class kat(object):
             
             rows,cols = data.shape
             
-            x = data[:,0]
+            x = data[:,0].squeeze()
             y = data[:,1:cols]
         
             return [x, y, hdr]
 
     def removeLine(self, fragment) :
         """
-        This will search all blocks by default and search for the string
-        fragment specified and remove it. This will only remove non-parsed
-        commands, it will not remove commands that have already been parsed
-        into the pykat structure, such as mirrors and beamsplitters, use the
-        kat.remove(...) function for that purpose.
+        This will search all blocks and search for the string
+        fragment specified and remove it.
+        WARNING: This will only remove non-parsed commands, it will not
+        remove commands that have already been parsed
+        into a pykat object, such as mirrors and beamsplitters, use
+        kat.remove or kat.component.remove() to delete parsed objects.
         """
+        found = False
+
         for key in self.__blocks:
             objs = self.__blocks[key].contents
             for obj in objs:
@@ -1334,6 +1436,11 @@ class kat(object):
                     if fragment in obj:
                         print ("  ** removing line '{0}'".format(obj))
                         objs.remove(obj)
+                        found = True
+
+        if not found:
+            pkex.BasePyKatException("The command fragment '%s' is not an extra line added to this kat object. Please check that the item you are trying to remove has not been parsed as a pykat object." % fragment)
+
 
     def addLine(self, line, block=NO_BLOCK) :
         """
@@ -1474,7 +1581,169 @@ class kat(object):
         #out.append("pyterm no\n")
         
         return out
+
+    def optivis(self):
+        if not HAS_OPTIVIS:
+            print("Optivis is not installed")
+            return None
         
+        import optivis.scene as scene
+        import optivis.bench.links as links
+        import optivis.view.canvas as canvas
+        
+        scene = scene.Scene(title="My pykat layout")
+        
+        # Run through again to add links
+        for c in self.getComponents():
+            if not isinstance(c, pykat.components.space):
+                continue
+            
+            a = c.connectingComponents()
+            
+            # Need to handle where spaces don't connect two components but there is a loose
+            # node, which may or may not have detectors attached
+            if a[0] is None or a[1] is None:
+                continue
+                
+            c1 = a[0].getOptivisComponent()
+            c2 = a[1].getOptivisComponent()
+            
+            no = a[0].getOptivisNode("Output", c.nodes[0])
+            ni = a[1].getOptivisNode("Input", c.nodes[1])
+            
+            if no is None or ni is None:
+                raise pkex.BasePyKatException("Optivis node is None")
+            
+            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)
+            label_name = optivis_label(text="", position=optivis_coord(0, -0.5), item=c._optivis_component)
+            label_name.content["Name"] = c.name
+            
+            c._optivis_component.labels.append(c.label_node1)
+            c._optivis_component.labels.append(c.label_node2)
+            c._optivis_component.labels.append(label_name)
+            scene.addLink(c._optivis_component)
+        
+        gui = canvas.Full(scene=scene)
+        
+        ### get menu bar from Optivis
+        menubar = gui.qMainWindow.menuBar()
+        
+        ### add new Pykat menu and menu items
+        pykatMenu = menubar.addMenu('&Pykat')
+    
+        # save
+        save = PyQt4.QtGui.QAction('Save', gui.qMainWindow)
+        save.setShortcut('Ctrl+S')
+        save.triggered.connect(lambda: self._optivis_doSave(gui))
+        pykatMenu.addAction(save)
+    
+        # trace
+        trace = PyQt4.QtGui.QAction('Trace', gui.qMainWindow)
+        trace.setShortcut('Ctrl+T')
+        trace.triggered.connect(lambda: self._optivis_doTrace(gui))
+        pykatMenu.addAction(trace)
+    
+        return gui
+    
+    def _optivis_doTrace(self, gui, **kwargs):
+        """
+        Change at some point to use a stored GUI reference
+        """
+        if not HAS_OPTIVIS:
+            print("Optivis is not installed")
+            return None
+        
+        prev = self.noxaxis
+        
+        self.noxaxis = True
+        out, tdata = self.run(getTraceData=True, **kwargs)
+        self.noxaxis = prev
+        
+        # For now just select the first trace computed
+        # Later we could add some gui list to show the different ones
+        tdata = tdata[0]
+        
+        for c in self.getComponents():
+            if not isinstance(c, pykat.components.space):
+                continue
+                
+            if not (hasattr(c, "label_node1") and hasattr(c, "label_node2")):
+                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
+            
+            c.label_node2.content["w0_x"] = tdata[c.nodes[1].name][0].w0
+            c.label_node2.content["w_x"] = tdata[c.nodes[1].name][0].w
+            c.label_node2.content["z_x"] = tdata[c.nodes[1].name][0].z
+            c.label_node2.content["Rc_x"] = tdata[c.nodes[1].name][0].Rc
+            c.label_node2.content["Zr_x"] = tdata[c.nodes[1].name][0].zr
+            
+            c.label_node2.content["w0_y"] = tdata[c.nodes[1].name][1].w0
+            c.label_node2.content["w_y"] = tdata[c.nodes[1].name][1].w
+            c.label_node2.content["z_y"] = tdata[c.nodes[1].name][1].z
+            c.label_node2.content["Rc_y"] = tdata[c.nodes[1].name][1].Rc
+            c.label_node2.content["Zr_y"] = tdata[c.nodes[1].name][1].zr
+       
+        gui.redraw()
+        
+    def _optivis_doSave(self, gui, **kwargs):
+        """
+        Save kat script from Optivis
+        """
+        if not HAS_OPTIVIS:
+            print("Optivis is not installed")
+            return None
+    
+        # generate file path
+        directory = os.path.join(os.path.expanduser('~'), '{0}.kat'.format(gui.scene.title))
+
+        # desired save path
+        path = None
+    
+        # get path to file to export to
+        while True:    
+            dialog = PyQt4.Qt.QFileDialog(parent=gui.qMainWindow, caption='Save Finesse Script', directory=directory)
+            dialog.setAcceptMode(PyQt4.Qt.QFileDialog.AcceptSave)
+            dialog.setFileMode(PyQt4.Qt.QFileDialog.AnyFile)
+
+            # show dialog
+            dialog.exec_()
+      
+            if len(dialog.selectedFiles()) is 0:
+                # no filename specified
+                return
+
+            # get file path and format
+            path = str(dialog.selectedFiles()[0])
+      
+            try:
+                # check if we can write to the path
+                open(path, 'w').close()
+                os.unlink(path)
+        
+                # if we get this far, all is good so we can break out the infinite loop
+                break
+            except OSError:
+                PyQt4.Qt.QMessageBox.critical(gui.qMainWindow, 'Filename invalid', 'The specified filename is invalid')
+            except IOError:
+                PyQt4.Qt.QMessageBox.critical(gui.qMainWindow, 'Permission denied', 'You do not have permission to save the file to the specified location.')
+    
+        # save kat file
+        self.saveScript(filename=path)
+    
     def openGUI(self):
         if not USE_GUI:
             raise NoGUIException
@@ -1615,7 +1884,7 @@ class kat(object):
         
         name = com.__class__.__name__
         
-        print (getattr(self.__class__, name))
+        #print (getattr(self.__class__, name))
         
         delattr(self.__class__, name)
         delattr(self, '__com_' + name)
diff --git a/pykat/node_network.py b/pykat/node_network.py
index 550290e662dfcedb4378ead9002e538facb0a544..f75c055e2871700a8ff864d72c0b90c328f5481b 100644
--- a/pykat/node_network.py
+++ b/pykat/node_network.py
@@ -23,6 +23,13 @@ from pykat.optics.gaussian_beams import beam_param
 from copy import deepcopy
 
 class NodeNetwork(object):
+
+    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
+        return object.__new__(type(cls.__name__, (cls,), {}), *args, **kwargs)
+
     def __init__(self, kat):
         self.__nodes = {}
         self.__kat = kat
@@ -30,10 +37,7 @@ class NodeNetwork(object):
         self.__componentNodes = {} # dictionary of tuples containing which nodes are connected to a given component
         self.__componentCallback = {}
         self.__node_id = 1
-        
-        cls = type(self)
-        self.__class__ = type(cls.__name__, (cls,), {})
-    
+            
     @property
     def kat(self): return self.__kat