diff --git a/bin/parse.kat b/bin/parse.kat
deleted file mode 100644
index 235dd32b622715b527b0bf7a0977149d7fb4225e..0000000000000000000000000000000000000000
--- a/bin/parse.kat
+++ /dev/null
@@ -1,10 +0,0 @@
-l l1 1 0 0 n1
-s s1 10 1 n1 n2
-m m1 0.5 0.5 0 n2 n3
-s s2 10 1 n3 n4
-m m2 0.5 0.5 0 n4 dump
-
-pd PD1 n2
-
-xaxis m1 phi lin 0 360 1000
-
diff --git a/bin/test_plot.py b/bin/test_plot.py
index f21aa3ce748d4c9d1aae6b015d869050af9d1e1c..0fc55def1e3ec2fc893396a57953c55e4039d4b1 100644
--- a/bin/test_plot.py
+++ b/bin/test_plot.py
@@ -1,46 +1,46 @@
-from pykat import finesse
-from pykat.detectors import *
-from pykat.components import *
-from pykat.commands import *
-from pykat.structs import *
-
-import numpy as np
-import pylab as pl
-
-code = """
-l l1 1 0 0 n1
-s s1 10 1 n1 n2
-m m1 0.5 0.5 0 n2 n3
-s s2 10 1 n3 n4
-m m2 0.5 0.5 0 n4 n5
-s s3 10 1 n5 n6
-"""
-
-#kat = finesse.kat(katexe='/Users/adf/work/bin/kat')
-kat = finesse.kat()
-
-kat.parseCommands(code)
-
-kat.add(cavity('cav1', 'm1', 'n3', 'm2', 'n4'))
-
-kat.add(photodiode('pd_cav','n4'))
-kat.add(photodiode('pd_ref','n2'))
-kat.add(photodiode('pd_trs','n5'))
-
-kat.add(xaxis("lin", [0, 360], kat.m2, kat.m2.phi, 100))
-
-kat.m1.Rcx = -1000.0
-kat.m1.Rcy = -1000.0
-kat.m2.Rcx =  1000.0
-kat.m2.Rcy =  1000.0
-
-kat.maxtem = 0
-
-out = kat.run(printout=0,printerr=0)
-
-pl.figure()
-pl.plot(out.x, out["pd_cav"])
-pl.xlabel(out.xlabel)
-pl.ylabel("Intensity [W]")
-pl.legend(out.ylabels)
-pl.show()
+from pykat import finesse
+from pykat.detectors import *
+from pykat.components import *
+from pykat.commands import *
+from pykat.structs import *
+
+import numpy as np
+import pylab as pl
+
+code = """
+l l1 1 0 0 n1
+s s1 10 1 n1 n2
+m m1 0.5 0.5 0 n2 n3
+s s2 10 1 n3 n4
+m m2 0.5 0.5 0 n4 n5
+s s3 10 1 n5 n6
+"""
+
+#kat = finesse.kat(katexe='/Users/adf/work/bin/kat')
+kat = finesse.kat()
+
+kat.parseCommands(code)
+
+kat.add(cavity('cav1', 'm1', 'n3', 'm2', 'n4'))
+
+kat.add(photodiode('pd_cav','n4'))
+kat.add(photodiode('pd_ref','n2'))
+kat.add(photodiode('pd_trs','n5'))
+
+kat.add(xaxis("lin", [0, 360], kat.m2, kat.m2.phi, 100))
+
+kat.m1.Rcx = -1000.0
+kat.m1.Rcy = -1000.0
+kat.m2.Rcx =  1000.0
+kat.m2.Rcy =  1000.0
+
+kat.maxtem = 0
+
+out = kat.run(printout=0,printerr=0)
+
+pl.figure()
+pl.plot(out.x, out["pd_cav"])
+pl.xlabel(out.xlabel)
+pl.ylabel("Intensity [W]")
+pl.legend(out.ylabels)
+pl.show()
diff --git a/pykat/colours.py b/pykat/colours.py
index f485487593fe50d78b4108f6724ebea985e91094..cc65b2f77c41b757c5de407ecd8b7f8ca6315b86 100644
--- a/pykat/colours.py
+++ b/pykat/colours.py
@@ -1,22 +1,22 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Mon Jan 28 10:43:18 2013
-
-@author: Daniel
-"""
-
-class colours:
-    HEADER = '\033[95m'
-    OKBLUE = '\033[94m'
-    OKGREEN = '\033[92m'
-    WARNING = '\033[93m'
-    FAIL = '\033[91m'
-    ENDC = '\033[0m'
-
-    def disable(self):
-        self.HEADER = ''
-        self.OKBLUE = ''
-        self.OKGREEN = ''
-        self.WARNING = ''
-        self.FAIL = ''
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Jan 28 10:43:18 2013
+
+@author: Daniel
+"""
+
+class colours:
+    HEADER = '\033[95m'
+    OKBLUE = '\033[94m'
+    OKGREEN = '\033[92m'
+    WARNING = '\033[93m'
+    FAIL = '\033[91m'
+    ENDC = '\033[0m'
+
+    def disable(self):
+        self.HEADER = ''
+        self.OKBLUE = ''
+        self.OKGREEN = ''
+        self.WARNING = ''
+        self.FAIL = ''
         self.ENDC = ''
\ No newline at end of file
diff --git a/pykat/commands.py b/pykat/commands.py
index 19fe6606425802bd21c89450a825b5c9e2b48602..9f85839f517a58ce9db9d4ed437242b756903932 100644
--- a/pykat/commands.py
+++ b/pykat/commands.py
@@ -1,142 +1,142 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Mon Jan 28 11:58:09 2013
-
-@author: Daniel
-"""
-import numpy
-from numpy import min,max
-import exceptions
-from components import *
-from structs import *
-from pykat.param import Param, putter
-
-class Command(object):
-    def __init__(self):
-        self.tag = None
-        
-    def getFinesseText(self):
-        """ Base class for individual finesse optical components """    
-        raise NotImplementedError("This function is not implemented")
-    
-    @staticmethod
-    def parseFinesseText(text):    
-        raise NotImplementedError("This function is not implemented")
-    
-    def _on_kat_add(self, kat):
-        """
-        Called when this component has been added to a kat object
-        """
-        self._kat = kat
-        
-class cavity(Command):
-    def __init__(self, name, c1, n1, c2, n2):
-        self.__name = name
-        self.__c1 = c1
-        self.__c2 = c2
-        self.__n1 = n1
-        self.__n2 = n2
-        
-    def getFinesseText(self):
-        return 'cav {0} {1} {2} {3} {4}'.format(self.__name, self.__c1, self.__n1, self.__c2, self.__n2);
-
-class gauss(object):
-    @staticmethod
-    def parseFinesseText(text, kat):  
-        values = text.split(" ")
-        
-        if not values[0].startswith("gauss"):
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse gauss command".format(text))
-       
-class xaxis(Command):
-    
-    def __init__(self, scale, limits, comp, param, steps, axis_type="xaxis"):
-        self._axis_type = axis_type
-        
-        self.x = putter("x1")
-        self.mx = putter("mx1")
-        
-        if scale == "lin":
-            scale = Scale.linear
-        elif scale == "log":
-            scale = Scale.logarithmic
-        elif isinstance(scale, str): 
-            # else we have a string but not a recognisable one
-            raise exceptions.ValueError("scale argument '{0}' is not valid, must be 'lin' or 'log'".format(scale))
-         
-        if scale != Scale.linear and scale != Scale.logarithmic:
-            raise exceptions.ValueError("scale is not Scale.linear or Scale.logarithmic")            
-            
-        self.scale = scale
-        
-        if numpy.size(limits) != 2 :
-            raise exceptions.ValueError("limits input should be a 2x1 vector of limits for the xaxis")
-            
-        self.limits = numpy.array(SIfloat(limits)).astype(float)
-        
-        if steps <= 0 :
-            raise exceptions.ValueError("steps value should be > 0")            
-            
-        self.steps = int(steps)
-        
-        # if entered component is a string then just store and use that
-        if isinstance(comp, str):
-            self.__comp = comp
-        elif not isinstance(comp, Component):
-            raise exceptions.ValueError("{0} has not been added".format(comp))
-        else:
-            self.__comp = comp
-        
-        if isinstance(param, str):
-            self.__param = param
-        elif not isinstance(param, Param) :
-            raise exceptions.ValueError("param argument is not of type Param")
-        else:
-            self.__param = param
-    
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-        
-        if values[0] != "xaxis" and values[0] != "xaxis*":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text))
-        
-        axis_type = values[0]
-        
-        values.pop(0) # remove initial value
-        
-        if len(values) != 6:
-            raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text))
-
-        return xaxis(values[2], [values[3], values[4]], values[0], values[1], values[5], axis_type=axis_type)
-        
-    def getFinesseText(self):
-        # store either the component name of the string provided
-        comp_name = self.__comp.name if isinstance(self.__comp, Component) else self.__comp
-        param_name = self.__param.name if isinstance(self.__param, Param) else self.__param
-        
-        return '{axis_type} {0} {1} {2} {3} {4} {5}'.format(
-                comp_name, param_name, self.scale,
-                min(self.limits), max(self.limits), self.steps, axis_type=self._axis_type);
-                
-class x2axis(xaxis):
-    def __init__(self, scale, limits, comp, param, steps):
-        xaxis.__init__(self, scale, limits, comp, param, steps, axis_type="x2axis")        
-        self.x = putter("x2")
-        self.mx = putter("mx2")
-
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-        
-        if values[0] != "x2axis" and values[0] != "x2axis*":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text))
-        
-        axis_type = values[0]
-        
-        values.pop(0) # remove initial value
-        
-        if len(values) != 6:
-            raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text))
-
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Jan 28 11:58:09 2013
+
+@author: Daniel
+"""
+import numpy
+from numpy import min,max
+import exceptions
+from components import *
+from structs import *
+from pykat.param import Param, putter
+
+class Command(object):
+    def __init__(self):
+        self.tag = None
+
+    def getFinesseText(self):
+        """ Base class for individual finesse optical components """
+        raise NotImplementedError("This function is not implemented")
+
+    @staticmethod
+    def parseFinesseText(text):
+        raise NotImplementedError("This function is not implemented")
+
+    def _on_kat_add(self, kat):
+        """
+        Called when this component has been added to a kat object
+        """
+        self._kat = kat
+
+class cavity(Command):
+    def __init__(self, name, c1, n1, c2, n2):
+        self.__name = name
+        self.__c1 = c1
+        self.__c2 = c2
+        self.__n1 = n1
+        self.__n2 = n2
+
+    def getFinesseText(self):
+        return 'cav {0} {1} {2} {3} {4}'.format(self.__name, self.__c1, self.__n1, self.__c2, self.__n2);
+
+class gauss(object):
+    @staticmethod
+    def parseFinesseText(text, kat):
+        values = text.split(" ")
+
+        if not values[0].startswith("gauss"):
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse gauss command".format(text))
+
+class xaxis(Command):
+
+    def __init__(self, scale, limits, comp, param, steps, axis_type="xaxis"):
+        self._axis_type = axis_type
+
+        self.x = putter("x1")
+        self.mx = putter("mx1")
+
+        if scale == "lin":
+            scale = Scale.linear
+        elif scale == "log":
+            scale = Scale.logarithmic
+        elif isinstance(scale, str):
+            # else we have a string but not a recognisable one
+            raise exceptions.ValueError("scale argument '{0}' is not valid, must be 'lin' or 'log'".format(scale))
+
+        if scale != Scale.linear and scale != Scale.logarithmic:
+            raise exceptions.ValueError("scale is not Scale.linear or Scale.logarithmic")
+
+        self.scale = scale
+
+        if numpy.size(limits) != 2 :
+            raise exceptions.ValueError("limits input should be a 2x1 vector of limits for the xaxis")
+
+        self.limits = numpy.array(SIfloat(limits)).astype(float)
+
+        if steps <= 0 :
+            raise exceptions.ValueError("steps value should be > 0")
+
+        self.steps = int(steps)
+
+        # if entered component is a string then just store and use that
+        if isinstance(comp, str):
+            self.__comp = comp
+        elif not isinstance(comp, Component):
+            raise exceptions.ValueError("{0} has not been added".format(comp))
+        else:
+            self.__comp = comp
+
+        if isinstance(param, str):
+            self.__param = param
+        elif not isinstance(param, Param) :
+            raise exceptions.ValueError("param argument is not of type Param")
+        else:
+            self.__param = param
+
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0] != "xaxis" and values[0] != "xaxis*":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text))
+
+        axis_type = values[0]
+
+        values.pop(0) # remove initial value
+
+        if len(values) != 6:
+            raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text))
+
+        return xaxis(values[2], [values[3], values[4]], values[0], values[1], values[5], axis_type=axis_type)
+
+    def getFinesseText(self):
+        # store either the component name of the string provided
+        comp_name = self.__comp.name if isinstance(self.__comp, Component) else self.__comp
+        param_name = self.__param.name if isinstance(self.__param, Param) else self.__param
+
+        return '{axis_type} {0} {1} {2} {3} {4} {5}'.format(
+                comp_name, param_name, self.scale,
+                min(self.limits), max(self.limits), self.steps, axis_type=self._axis_type);
+
+class x2axis(xaxis):
+    def __init__(self, scale, limits, comp, param, steps):
+        xaxis.__init__(self, scale, limits, comp, param, steps, axis_type="x2axis")
+        self.x = putter("x2")
+        self.mx = putter("mx2")
+
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0] != "x2axis" and values[0] != "x2axis*":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse xaxis command".format(text))
+
+        axis_type = values[0]
+
+        values.pop(0) # remove initial value
+
+        if len(values) != 6:
+            raise exceptions.RuntimeError("xaxis Finesse code format incorrect '{0}'".format(text))
+
         return x2axis(values[2], [values[3], values[4]], values[0], values[1], values[5])
\ No newline at end of file
diff --git a/pykat/components.py b/pykat/components.py
index 73948105755f4e25f8fd966e765d5f5d08fb62ed..8aac82e60ce14f2ab2a19a7109aa05872b119765 100644
--- a/pykat/components.py
+++ b/pykat/components.py
@@ -1,729 +1,729 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Mon Jan 28 11:10:01 2013
-
-@author: Daniel
-"""
-import exceptions
-import pykat.exceptions as pkex
-import pykat
-from pykat.node_network import *
-from pykat.exceptions import *
-import abc
-
-import pykat.gui.resources
-import pykat.gui.graphics
-from pykat.gui.graphics import *
-from pykat.SIfloat import *
-from pykat.param import Param, AttrParam
-
-next_component_id = 1
-
-class NodeGaussSetter(object):
-    def __init__(self, component, node):                
-        self.__comp = component
-        self.__node = node
-    
-    @property
-    def node(self):
-        return self.__node
-    
-    @property
-    def q(self):
-        return self.__node.qx
-        
-    @q.setter
-    def q(self, value):
-        self.__node.setGauss(self.__comp, complex(value))
-        
-    @property
-    def qx(self):
-        return self.__node.qx
-    @qx.setter
-    def qx(self, value):
-        self.__node.setGauss(self.__comp, complex(value))
-    
-    @property
-    def qy(self):
-        return self.__node.qy
-    @qy.setter
-    def qy(self, value):
-        self.__node.setGauss(self.__comp, self.qx, complex(value))
-        
-class Component(object):
-    __metaclass__ = abc.ABCMeta
-    
-    def __init__(self, name):
-        self.__name = name
-        self._svgItem = None
-        self._requested_node_names = []
-        self._kat = None
-        self.tag = None
-        self._params = []
-        
-        # 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 _register_param(self, param):
-        self._params.append(param)
-        
-    def _on_kat_add(self, kat):
-        """
-        Called when this component has been added to a kat object.
-        kat is the finesse.kat object which it now belongs to and
-        node_array is an array specific to this object which contains
-        references to the nodes that are attached to it.
-        """
-        if self._kat != None:
-            raise pkex.BasePyKatException("Component has already been added to a finesse.kat object")
-            
-        self._kat = kat
-        
-        kat.nodes.registerComponentNodes(self, self._requested_node_names, self.__on_node_change)
-        
-    def __on_node_change(self):
-        # need to update the node gauss parameter setter members 
-        self.__update_node_setters()
-        
-    def __update_node_setters(self):
-        # check if any node setters have already been added. If so we
-        # need to remove them. This function should get called if the nodes
-        # are updated, either by some function call or the GUI
-        key_rm = [k for k in self.__dict__ if k.startswith("__nodesetter_", 0, 13)]
-        
-        # now we have a list of which to remove
-        for key in key_rm:
-            ns = self.__dict__[key]
-            delattr(self, '__nodesetter_' + ns.node.name)
-            delattr(self.__class__, ns.node.name)
-        
-        for node in self.nodes:
-            if type(node) != pykat.node_network.DumpNode:
-                ns = NodeGaussSetter(self, node)
-                self.__add_node_setter(ns)
-        
-    def __add_node_setter(self, ns):
-
-        if not isinstance(ns, NodeGaussSetter):
-            raise exceptions.ValueError("Argument is not of type NodeGaussSetter")
-        
-        name = ns.node.name
-        fget = lambda self: self.__get_node_setter(name)
-        
-        setattr(self.__class__, name, property(fget))
-        setattr(self, '__nodesetter_' + name, ns)                   
-
-    def __get_node_setter(self, name):
-        return getattr(self, '__nodesetter_' + name)   
-        
-    @staticmethod
-    @abc.abstractmethod
-    def parseFinesseText(text):
-        """Parses Finesse syntax"""
-        raise NotImplementedError("This function is not implemented")
-
-    @abc.abstractmethod
-    def getFinesseText(self):
-        """ Base class for individual Finesse optical components """    
-        raise NotImplementedError("This function is not implemented")
-
-    @abc.abstractmethod
-    def getQGraphicsItem(self):    
-        return None      
-    
-    @property
-    def nodes(self): return self._kat.nodes.getComponentNodes(self) 
-    
-    @property    
-    def name(self): return self.__name      
-    
-    @property
-    def id(self): return self.__id
-    
-    def __str__(self): return self.name
-    
-class AbstractMirrorComponent(Component):
-    __metaclass__ = abc.ABCMeta
-    
-    def __init__(self, name, R=0, T=0, phi=0, Rcx=0, Rcy=0, xbeta=0, ybeta=0, mass=0, r_ap=0):
-        super(AbstractMirrorComponent, self).__init__(name)
-
-        self.__R = Param("R", self, SIfloat(R))
-        self.__T = Param("T", self, SIfloat(T))
-        self.__phi = Param("phi", self, SIfloat(phi))
-        self.__Rcx = AttrParam("Rcx", self, SIfloat(Rcx))
-        self.__Rcy = AttrParam("Rcy", self, SIfloat(Rcy))
-        self.__xbeta = AttrParam("xbeta", self, SIfloat(xbeta))
-        self.__ybeta = AttrParam("ybeta", self, SIfloat(ybeta))
-        self.__mass = AttrParam("mass", self, SIfloat(mass))
-        self.__r_ap = AttrParam("r_ap", self, SIfloat(r_ap))
-        
-    @property
-    def r_ap(self): return self.__r_ap
-    @r_ap.setter
-    def r_ap(self,value): self.__r_ap.value = SIfloat(value)
-
-    @property
-    def mass(self): return self.__mass
-    @mass.setter
-    def mass(self,value): self.__mass.value = SIfloat(value)
-    
-    @property
-    def R(self): return self.__R
-    @R.setter
-    def R(self,value): self.__R.value = SIfloat(value)
-    
-    @property
-    def T(self): return self.__T
-    @T.setter
-    def T(self,value): self.__T.value = SIfloat(value)
-        
-    @property
-    def phi(self): return self.__phi
-    @phi.setter
-    def phi(self,value): self.__phi.value = SIfloat(value)
-    
-    @property
-    def Rcx(self): return self.__Rcx
-    @Rcx.setter
-    def Rcx(self,value): self.__Rcx.value = SIfloat(value)
-    
-    @property
-    def Rcy(self): return self.__Rcy
-    @Rcy.setter
-    def Rcy(self,value): self.__Rcy.value = SIfloat(value)
-    
-    @property
-    def xbeta(self): return self.__xbeta
-    @xbeta.setter
-    def xbeta(self,value): self.__xbeta.value = SIfloat(value)
-    
-    @property
-    def ybeta(self): return self.__ybeta
-    @ybeta.setter
-    def ybeta(self,value): self.__ybeta.value = SIfloat(value)
-    
-    @property
-    def Rc(self):
-        if self.Rcx == self.Rcy:
-            return self.Rcx
-        else:
-            return [self.Rcx, self.Rcy]
-    
-    @Rc.setter
-    def Rc(self,value):
-        self.Rcx.value = SIfloat(value)
-        self.Rcy.value = SIfloat(value)
-
-class mirror(AbstractMirrorComponent):
-    def __init__(self,name,node1,node2,R=0,T=0,phi=0,Rcx=0,Rcy=0,xbeta=0,ybeta=0,mass=0, r_ap=0):
-        super(mirror, self).__init__(name, R, T, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap)
-        
-        self._requested_node_names.append(node1)
-        self._requested_node_names.append(node2)
-    
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-
-        if values[0] != "m" and values[0] != "m1" and values[0] != "m2":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse mirror command".format(text))
-        
-        if len(values) != 7:
-            raise exceptions.RuntimeError("Mirror Finesse code format incorrect '{0}'".format(text))
-
-        if len(values[0])==1:
-            values.pop(0) # remove initial value
-            return mirror(values[0], values[4], values[5], R=values[1], T=values[2], phi=values[3])
-        else:
-            if values[0][1]=="1":
-                values.pop(0) # remove initial value
-                return mirror(values[0], values[4], values[5], R=1.0-SIfloat(values[1])-SIfloat(values[2]), T=values[1], phi=values[3])
-            else:
-                values.pop(0) # remove initial value
-                return mirror(values[0], values[4], values[5], R=values[1], T=1.0-SIfloat(values[1])-SIfloat(values[2]), phi=values[3])
-
-    def getFinesseText(self):        
-        rtn = []
-            
-        rtn.append('m {0} {1} {2} {3} {4} {5}'.format(
-                self.name, self.R.value, self.T.value, self.phi.value,
-                self.nodes[0].name, self.nodes[1].name))
-
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-                    
-        return rtn
-        
-    def getQGraphicsItem(self):
-        if self._svgItem == None:
-            self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])])
-            
-        return self._svgItem
-
-class beamSplitter(AbstractMirrorComponent):
-    def __init__(self, name, node1, node2, node3, node4, R = 0, T = 0, phi = 0, alpha = 0, Rcx = 0, Rcy = 0, xbeta = 0, ybeta = 0, mass = 0, r_ap = 0):
-        super(beamSplitter, self).__init__(name, R, T, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap)
-        
-        self._requested_node_names.append(node1)
-        self._requested_node_names.append(node2)
-        self._requested_node_names.append(node3)
-        self._requested_node_names.append(node4)
-             
-        self.__alpha = AttrParam("alpha", self, SIfloat(alpha))
-    
-    @property
-    def alpha(self): return self.__alpha
-    @alpha.setter
-    def alpha(self,value): self.__alpha.value = SIfloat(value)
-    
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-
-        if values[0] != "bs" and values[0] != "bs1" and values[0] != "bs2":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse beam splitter command".format(text))
-        
-        if len(values) != 10:
-            raise exceptions.RuntimeError("Beam splitter Finesse code format incorrect '{0}'".format(text))
-
-        if len(values[0])==1:
-            values.pop(0) # remove initial value
-            return beamSplitter(values[0], values[5], values[6], values[7], values[8], values[1], values[2], values[3], values[4])
-        else:
-            if values[0][1]=="1":
-                values.pop(0) # remove initial value
-                return beamSplitter(values[0], values[5], values[6], values[7], values[8], 1.0 - SIfloat(values[1]) - SIfloat(values[2]), values[1], values[3], values[4])
-            else:
-                values.pop(0) # remove initial value
-                return beamSplitter(values[0], values[5], values[6], values[7], values[8], values[1], 1.0 - SIfloat(values[1]) - SIfloat(values[2]), values[3], values[4])
-            
-    def getFinesseText(self):
-        rtn = []
-            
-        rtn.append('bs {0} {1} {2} {3} {4} {5} {6} {7} {8}'.format(
-                self.name, self.R.value, self.T.value, self.phi.value,
-                self.alpha.value, self.nodes[0].name,
-                self.nodes[1].name, self.nodes[2].name,
-                self.nodes[3].name))
-
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-            
-        return rtn
-        
-    def getQGraphicsItem(self):
-        if self._svgItem == None:
-            # FIXME: make proper SVG component for beam splitter
-            self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])])
-            
-        return self._svgItem
-   
-class space(Component):
-    def __init__(self, name, node1, node2, L=0, n=1):
-        Component.__init__(self, name)
-        
-        self._requested_node_names.append(node1)
-        self._requested_node_names.append(node2)
-        self._QItem = None
-        self.__L = Param("L", self, SIfloat(L))
-        self.__n = Param("n", self, SIfloat(n))
-        
-    @property
-    def L(self): return self.__L
-    @L.setter
-    def L(self,value): self.__L,value = SIfloat(value)
-    @property
-    def n(self): return self.__n
-    @n.setter
-    def n(self,value): self.__n.value = SIfloat(value)
-    
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-
-        if values[0] != "s":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse space command".format(text))
-
-        values.pop(0) # remove initial value
-        
-        if len(values) == 5:
-            return space(values[0], values[3], values[4], values[1], values[2])
-        elif len(values) == 4:
-            return space(values[0], values[2], values[3], values[1])
-        else:
-            raise exceptions.RuntimeError("Space Finesse code format incorrect '{0}'".format(text))
-        
-    def getFinesseText(self):
-        rtn = []
-        
-        if self.__n == 1:
-            rtn.append('s {0} {1} {2} {3}'.format(self.name, self.__L.value, self.nodes[0].name, self.nodes[1].name))
-        else:
-            rtn.append('s {0} {1} {2} {3} {4}'.format(self.name, self.__L.value, self.__n.value, self.nodes[0].name, self.nodes[1].name))
-       
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-            
-        return rtn
-        
-    def getQGraphicsItem(self):
-        if self._QItem == None:
-            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self)
-        
-        return self._QItem
-
-class grating(Component):
-    def __init__(self, name, node1, node2, node3 = None, node4 = None, n = 2, d = 0, eta_0 = 0, eta_1 = 0, eta_2 = 0, eta_3 = 0, rho_0 = 0, alpha = 0): # TODO: implement Rcx, Rcy and Rc
-        Component.__init__(self, name)
-        
-        self._requested_node_names.append(node1)
-        self._requested_node_names.append(node2)
-
-        if n > 2:
-            if node3 != None:
-                self._requested_node_names.append(node3)
-            else:
-                raise exceptions.RuntimeError("Grating node 3 not specified")
-
-        if n > 3:
-            if node4 != None:
-                self._requested_node_names.append(node4)
-            else:
-                raise exceptions.RuntimeError("Grating node 4 not specified")
-
-        if n > 4 or n < 2:
-            raise exceptions.RuntimeError("Grating must have between 2 and 4 ports")
-        
-        self.__n = n
-        self.__d = Param("d", self, SIfloat(d))
-        self.__eta_0 = AttrParam("eta_0", self, SIfloat(eta_0))
-        self.__eta_1 = AttrParam("eta_1", self, SIfloat(eta_1))
-        self.__eta_2 = AttrParam("eta_2", self, SIfloat(eta_2))
-        self.__eta_3 = AttrParam("eta_3", self, SIfloat(eta_3))
-        self.__rho_0 = AttrParam("rho_0", self, SIfloat(rho_0))
-        self.__alpha = AttrParam("alpha", self, SIfloat(alpha))
-    
-    @property
-    def n(self): return Param('n', self.__n)
-    @n.setter
-    def n(self, value):
-        if value < 2 or value > 4:
-            raise exceptions.RuntimeError("Grating must have between 2 and 4 ports")
-        else:
-            self.__n = value
-    
-    @property
-    def d(self): return self.__d
-    @d.setter
-    def d(self, value): self.__d.value = SIfloat(value)
-    
-    @property
-    def eta_0(self): return self.__eta_0
-    @eta_0.setter
-    def eta_0(self, value): self.__eta_0.value = SIfloat(value)
-    
-    @property
-    def eta_1(self): return self.__eta_1
-    @eta_1.setter
-    def eta_1(self, value): self.__eta_1.value = SIfloat(value)
-    
-    @property
-    def eta_2(self): return self.__eta_2
-    @eta_2.setter
-    def eta_2(self, value): self.__eta_2.value = SIfloat(value)
-    
-    @property
-    def eta_3(self): return self.__eta_3
-    @eta_3.setter
-    def eta_3(self, value): self.__eta_3.value = SIfloat(value)
-    
-    @property
-    def rho_0(self): return self.__rho_0
-    @rho_0.setter
-    def rho_0(self, value): self.__rho_0.value = SIfloat(value)
-    
-    @property
-    def alpha(self): return self.__alpha
-    @alpha.setter
-    def alpha(self, value): self.__alpha.value = SIfloat(value)
-    
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-
-        if values[0][0 : 2] != "gr":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse grating command".format(text))
-
-        if len(values[0]) > 2:
-            if int(values[0][2]) > 4 or int(values[0][2]) < 2:
-                raise exceptions.RuntimeError("Grating must have between 2 and 4 ports")
-            else:
-                n = int(values[0][2])
-        else:
-            n = 2
-
-        values.pop(0) # remove initial value
-        
-        if n == 2:
-            if len(values) != 4:
-                raise exceptions.RuntimeError("Two port grating must have 2 nodes defined")
-
-            return grating(values[0], values[2], values[3], None, None, n, values[1])
-        elif n == 3:
-            if len(values) != 5:
-                raise exceptions.RuntimeError("Three port grating must have 3 nodes defined")
-            
-            return grating(values[0], values[2], values[3], values[4], None, n, values[1])
-        else:
-            if len(values) != 6:
-                raise exceptions.RuntimeError("Four port grating must have 4 nodes defined")
-            
-            return grating(values[0], values[2], values[3], values[4], values[5], n, values[1])
-        
-    def getFinesseText(self):
-        rtn = []
-        
-        if self.__n == 2:
-            rtn.append('gr{0} {1} {2} {3} {4}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name))
-        elif self.__n == 3:
-            rtn.append('gr{0} {1} {2} {3} {4} {5}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name))
-        else:
-            rtn.append('gr{0} {1} {2} {3} {4} {5} {6}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name, self.nodes[3].name))
-        
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-        
-        return rtn
-       
-    def getQGraphicsItem(self):
-        if self._QItem == None:
-            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make SVG graphic for grating
-        
-        return self._QItem
-
-class isolator(Component):
-    def __init__(self, name, node1, node2, S = 0):
-        Component.__init__(self, name)
-        
-        self._requested_node_names.append(node1)
-        self._requested_node_names.append(node2)
-        
-        self.__S = Param("S",self,SIfloat(S))
-        
-    @property
-    def S(self): return self.__S
-    @S.setter
-    def S(self, value): self.__S.value = SIfloat(value)
-    
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-
-        if values[0] != "isol":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse isolator command".format(text))
-
-        values.pop(0) # remove initial value
-        
-        if len(values) == 4:
-            return isolator(values[0], values[2], values[3], values[1])
-        else:
-            raise exceptions.RuntimeError("Isolator Finesse code format incorrect '{0}'".format(text))
-        
-    def getFinesseText(self):
-        rtn = ['isol {0} {1} {2} {3}'.format(self.name, self.S.value, self.nodes[0].name, self.nodes[1].name)]
-        
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-            
-        return rtn
-
-    def getQGraphicsItem(self):
-        if self._QItem == None:
-            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make isolator graphic
-        
-        return self._QItem
-
-class lens(Component):
-    def __init__(self, name, node1, node2, f):
-        Component.__init__(self, name)
-        
-        self._requested_node_names.append(node1)
-        self._requested_node_names.append(node2)
-        
-        self.__f = Param("f", self, SIfloat(f))
-        
-    @property
-    def f(self): return self.__f
-    @f.setter
-    def f(self, value): self.__f.value = SIfloat(value)
-    
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-
-        if values[0] != "lens":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse lens command".format(text))
-
-        values.pop(0) # remove initial value
-        
-        if len(values) == 4:
-            return lens(values[0], values[2], values[3], values[1])
-        else:
-            raise exceptions.RuntimeError("Lens Finesse code format incorrect '{0}'".format(text))
-        
-    def getFinesseText(self):
-        rtn = ['lens {0} {1} {2} {3}'.format(self.name, self.f.value, self.nodes[0].name, self.nodes[1].name)]
-        
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-            
-        return rtn
-        
-    def getQGraphicsItem(self):
-        if self._QItem == None:
-            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make lens graphic
-        
-        return self._QItem
-        
-class modulator(Component):
-    def __init__(self, name, f, midx, order, modulation_type, node1, node2, phase=0):
-        Component.__init__(self, name)
-        
-        self._requested_node_names.append(node1)
-        self._requested_node_names.append(node2)
-        
-        self.__f = Param("f", self, SIfloat(f))
-        self.__midx = Param("midx", self, SIfloat(midx))
-        self.__phase = Param("phase", self, SIfloat(phase))
-        self.__order = order
-        self.type = modulation_type
-        
-            
-    @property 
-    def f(self): return self.__f
-    @f.setter
-    def f(self, value): self.__f.value = SIfloat(value)
-    
-    @property 
-    def midx(self): return self.__midx
-    @midx.setter
-    def midx(self, value): self.__midx.value = SIfloat(value)
-    
-    @property 
-    def phase(self): return self.__phase
-    @phase.setter
-    def phase(self, value): self.__phase.value = SIfloat(value)
-    
-    @property 
-    def order(self): return self.__order
-    @order.setter
-    def order(self, value):
-        if order <= 1 and order > 6:
-            raise pkex.BasePyKatException("modulator order must be between 1 and 6")
-            
-        self.__order = int(value)
-    
-    @property 
-    def type(self): return self.__type
-    @type.setter
-    def type(self, value):
-        if value != "am" and value != "pm":
-            raise pkex.BasePyKatException("Modulator type must be am (amplitude modulation) or pm (phase modulation)")
-            
-        self.__type = str(value)
-    
-    @staticmethod
-    def parseFinesseText(text):
-        v = text.split(" ")
-
-        if v[0] != "mod":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse modulator command".format(text))
-
-        v.pop(0) # remove initial value
-        
-        if len(v) == 7:
-            return modulator(v[0], v[1], v[2], v[3], v[4], v[5], v[6])
-        if len(v) == 8:
-            return modulator(v[0], v[1], v[2], v[3], v[4], v[6], v[7], phase=v[5])
-        else:
-            raise pkex.BasePyKatException("Modulator Finesse code format incorrect '{0}'".format(text))
-        
-    def getFinesseText(self):
-        rtn = ['mod {0} {1} {2} {3} {4} {5} {6} {7}'.format(self.name, self.f, self.midx, self.order, self.type, self.phase, self.nodes[0].name, self.nodes[1].name)]
-        
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-            
-        return rtn
-        
-    def getQGraphicsItem(self):
-        if self._QItem == None:
-            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make lens graphic
-        
-        return self._QItem
-
-class laser(Component):
-    def __init__(self,name,node,P=1,f_offset=0,phase=0):
-        Component.__init__(self,name)
-        
-        self._requested_node_names.append(node)
-        
-        self.__power = Param("P", self, SIfloat(P))
-        self.__f_offset = Param("f", self, SIfloat(f_offset))
-        self.__phase = Param("phase", self, SIfloat(phase))
-        self.__noise = AttrParam("noise", self, 0)
-        
-    @property
-    def power(self): return self.__power
-    @power.setter
-    def power(self,value): self.__power.value = float(value)
-    
-    @property
-    def f(self): return self.__f_offset
-    @f.setter
-    def f(self,value): self.__f_offset.value = float(value)
-    
-    @property
-    def phase(self): return self.__phase
-    @phase.setter
-    def phase(self,value): self.__phase.value = float(value)
-    
-    @staticmethod
-    def parseFinesseText(text):
-        values = text.split(" ")
-
-        if values[0] != "l":
-            raise exceptions.RuntimeError("'{0}' not a valid Finesse laser command".format(text))
-
-        values.pop(0) # remove initial value
-        
-        if len(values) == 5:
-            return laser(values[0],values[4],P=values[1],f_offset=values[2],phase=values[3])
-        elif len(values) == 4:
-            return laser(values[0],values[3],P=values[1],f_offset=values[2], phase=0)
-        else:
-            raise exceptions.FinesseParse("Laser Finesse code format incorrect '{0}'".format(text))
-    
-    def getFinesseText(self):
-        rtn = ['l {0} {1} {2} {3} {4}'.format(self.name, self.__power.value, self.__f_offset.value, self.__phase.value, self.nodes[0].name)]
-        
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-        
-        return rtn
-         
-    def getQGraphicsItem(self):
-        if self._svgItem == None:
-            self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/laser.svg", self, [(65,25,self.nodes[0])])
-            
-        return self._svgItem
-            
+# -*- coding: utf-8 -*-
+"""
+Created on Mon Jan 28 11:10:01 2013
+
+@author: Daniel
+"""
+import exceptions
+import pykat.exceptions as pkex
+import pykat
+from pykat.node_network import *
+from pykat.exceptions import *
+import abc
+
+import pykat.gui.resources
+import pykat.gui.graphics
+from pykat.gui.graphics import *
+from pykat.SIfloat import *
+from pykat.param import Param, AttrParam
+
+next_component_id = 1
+
+class NodeGaussSetter(object):
+    def __init__(self, component, node):                
+        self.__comp = component
+        self.__node = node
+    
+    @property
+    def node(self):
+        return self.__node
+    
+    @property
+    def q(self):
+        return self.__node.qx
+        
+    @q.setter
+    def q(self, value):
+        self.__node.setGauss(self.__comp, complex(value))
+        
+    @property
+    def qx(self):
+        return self.__node.qx
+    @qx.setter
+    def qx(self, value):
+        self.__node.setGauss(self.__comp, complex(value))
+    
+    @property
+    def qy(self):
+        return self.__node.qy
+    @qy.setter
+    def qy(self, value):
+        self.__node.setGauss(self.__comp, self.qx, complex(value))
+        
+class Component(object):
+    __metaclass__ = abc.ABCMeta
+    
+    def __init__(self, name):
+        self.__name = name
+        self._svgItem = None
+        self._requested_node_names = []
+        self._kat = None
+        self.tag = None
+        self._params = []
+        
+        # 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 _register_param(self, param):
+        self._params.append(param)
+        
+    def _on_kat_add(self, kat):
+        """
+        Called when this component has been added to a kat object.
+        kat is the finesse.kat object which it now belongs to and
+        node_array is an array specific to this object which contains
+        references to the nodes that are attached to it.
+        """
+        if self._kat != None:
+            raise pkex.BasePyKatException("Component has already been added to a finesse.kat object")
+            
+        self._kat = kat
+        
+        kat.nodes.registerComponentNodes(self, self._requested_node_names, self.__on_node_change)
+        
+    def __on_node_change(self):
+        # need to update the node gauss parameter setter members 
+        self.__update_node_setters()
+        
+    def __update_node_setters(self):
+        # check if any node setters have already been added. If so we
+        # need to remove them. This function should get called if the nodes
+        # are updated, either by some function call or the GUI
+        key_rm = [k for k in self.__dict__ if k.startswith("__nodesetter_", 0, 13)]
+        
+        # now we have a list of which to remove
+        for key in key_rm:
+            ns = self.__dict__[key]
+            delattr(self, '__nodesetter_' + ns.node.name)
+            delattr(self.__class__, ns.node.name)
+        
+        for node in self.nodes:
+            if type(node) != pykat.node_network.DumpNode:
+                ns = NodeGaussSetter(self, node)
+                self.__add_node_setter(ns)
+        
+    def __add_node_setter(self, ns):
+
+        if not isinstance(ns, NodeGaussSetter):
+            raise exceptions.ValueError("Argument is not of type NodeGaussSetter")
+        
+        name = ns.node.name
+        fget = lambda self: self.__get_node_setter(name)
+        
+        setattr(self.__class__, name, property(fget))
+        setattr(self, '__nodesetter_' + name, ns)                   
+
+    def __get_node_setter(self, name):
+        return getattr(self, '__nodesetter_' + name)   
+        
+    @staticmethod
+    @abc.abstractmethod
+    def parseFinesseText(text):
+        """Parses Finesse syntax"""
+        raise NotImplementedError("This function is not implemented")
+
+    @abc.abstractmethod
+    def getFinesseText(self):
+        """ Base class for individual Finesse optical components """    
+        raise NotImplementedError("This function is not implemented")
+
+    @abc.abstractmethod
+    def getQGraphicsItem(self):    
+        return None      
+    
+    @property
+    def nodes(self): return self._kat.nodes.getComponentNodes(self) 
+    
+    @property    
+    def name(self): return self.__name      
+    
+    @property
+    def id(self): return self.__id
+    
+    def __str__(self): return self.name
+    
+class AbstractMirrorComponent(Component):
+    __metaclass__ = abc.ABCMeta
+    
+    def __init__(self, name, R=0, T=0, phi=0, Rcx=0, Rcy=0, xbeta=0, ybeta=0, mass=0, r_ap=0):
+        super(AbstractMirrorComponent, self).__init__(name)
+
+        self.__R = Param("R", self, SIfloat(R))
+        self.__T = Param("T", self, SIfloat(T))
+        self.__phi = Param("phi", self, SIfloat(phi))
+        self.__Rcx = AttrParam("Rcx", self, SIfloat(Rcx))
+        self.__Rcy = AttrParam("Rcy", self, SIfloat(Rcy))
+        self.__xbeta = AttrParam("xbeta", self, SIfloat(xbeta))
+        self.__ybeta = AttrParam("ybeta", self, SIfloat(ybeta))
+        self.__mass = AttrParam("mass", self, SIfloat(mass))
+        self.__r_ap = AttrParam("r_ap", self, SIfloat(r_ap))
+        
+    @property
+    def r_ap(self): return self.__r_ap
+    @r_ap.setter
+    def r_ap(self,value): self.__r_ap.value = SIfloat(value)
+
+    @property
+    def mass(self): return self.__mass
+    @mass.setter
+    def mass(self,value): self.__mass.value = SIfloat(value)
+    
+    @property
+    def R(self): return self.__R
+    @R.setter
+    def R(self,value): self.__R.value = SIfloat(value)
+    
+    @property
+    def T(self): return self.__T
+    @T.setter
+    def T(self,value): self.__T.value = SIfloat(value)
+        
+    @property
+    def phi(self): return self.__phi
+    @phi.setter
+    def phi(self,value): self.__phi.value = SIfloat(value)
+    
+    @property
+    def Rcx(self): return self.__Rcx
+    @Rcx.setter
+    def Rcx(self,value): self.__Rcx.value = SIfloat(value)
+    
+    @property
+    def Rcy(self): return self.__Rcy
+    @Rcy.setter
+    def Rcy(self,value): self.__Rcy.value = SIfloat(value)
+    
+    @property
+    def xbeta(self): return self.__xbeta
+    @xbeta.setter
+    def xbeta(self,value): self.__xbeta.value = SIfloat(value)
+    
+    @property
+    def ybeta(self): return self.__ybeta
+    @ybeta.setter
+    def ybeta(self,value): self.__ybeta.value = SIfloat(value)
+    
+    @property
+    def Rc(self):
+        if self.Rcx == self.Rcy:
+            return self.Rcx
+        else:
+            return [self.Rcx, self.Rcy]
+    
+    @Rc.setter
+    def Rc(self,value):
+        self.Rcx.value = SIfloat(value)
+        self.Rcy.value = SIfloat(value)
+
+class mirror(AbstractMirrorComponent):
+    def __init__(self,name,node1,node2,R=0,T=0,phi=0,Rcx=0,Rcy=0,xbeta=0,ybeta=0,mass=0, r_ap=0):
+        super(mirror, self).__init__(name, R, T, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap)
+        
+        self._requested_node_names.append(node1)
+        self._requested_node_names.append(node2)
+    
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0] != "m" and values[0] != "m1" and values[0] != "m2":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse mirror command".format(text))
+        
+        if len(values) != 7:
+            raise exceptions.RuntimeError("Mirror Finesse code format incorrect '{0}'".format(text))
+
+        if len(values[0])==1:
+            values.pop(0) # remove initial value
+            return mirror(values[0], values[4], values[5], R=values[1], T=values[2], phi=values[3])
+        else:
+            if values[0][1]=="1":
+                values.pop(0) # remove initial value
+                return mirror(values[0], values[4], values[5], R=1.0-SIfloat(values[1])-SIfloat(values[2]), T=values[1], phi=values[3])
+            else:
+                values.pop(0) # remove initial value
+                return mirror(values[0], values[4], values[5], R=values[1], T=1.0-SIfloat(values[1])-SIfloat(values[2]), phi=values[3])
+
+    def getFinesseText(self):        
+        rtn = []
+            
+        rtn.append('m {0} {1} {2} {3} {4} {5}'.format(
+                self.name, self.R.value, self.T.value, self.phi.value,
+                self.nodes[0].name, self.nodes[1].name))
+
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+                    
+        return rtn
+        
+    def getQGraphicsItem(self):
+        if self._svgItem == None:
+            self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])])
+            
+        return self._svgItem
+
+class beamSplitter(AbstractMirrorComponent):
+    def __init__(self, name, node1, node2, node3, node4, R = 0, T = 0, phi = 0, alpha = 0, Rcx = 0, Rcy = 0, xbeta = 0, ybeta = 0, mass = 0, r_ap = 0):
+        super(beamSplitter, self).__init__(name, R, T, phi, Rcx, Rcy, xbeta, ybeta, mass, r_ap)
+        
+        self._requested_node_names.append(node1)
+        self._requested_node_names.append(node2)
+        self._requested_node_names.append(node3)
+        self._requested_node_names.append(node4)
+             
+        self.__alpha = AttrParam("alpha", self, SIfloat(alpha))
+    
+    @property
+    def alpha(self): return self.__alpha
+    @alpha.setter
+    def alpha(self,value): self.__alpha.value = SIfloat(value)
+    
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0] != "bs" and values[0] != "bs1" and values[0] != "bs2":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse beam splitter command".format(text))
+        
+        if len(values) != 10:
+            raise exceptions.RuntimeError("Beam splitter Finesse code format incorrect '{0}'".format(text))
+
+        if len(values[0])==1:
+            values.pop(0) # remove initial value
+            return beamSplitter(values[0], values[5], values[6], values[7], values[8], values[1], values[2], values[3], values[4])
+        else:
+            if values[0][1]=="1":
+                values.pop(0) # remove initial value
+                return beamSplitter(values[0], values[5], values[6], values[7], values[8], 1.0 - SIfloat(values[1]) - SIfloat(values[2]), values[1], values[3], values[4])
+            else:
+                values.pop(0) # remove initial value
+                return beamSplitter(values[0], values[5], values[6], values[7], values[8], values[1], 1.0 - SIfloat(values[1]) - SIfloat(values[2]), values[3], values[4])
+            
+    def getFinesseText(self):
+        rtn = []
+            
+        rtn.append('bs {0} {1} {2} {3} {4} {5} {6} {7} {8}'.format(
+                self.name, self.R.value, self.T.value, self.phi.value,
+                self.alpha.value, self.nodes[0].name,
+                self.nodes[1].name, self.nodes[2].name,
+                self.nodes[3].name))
+
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+            
+        return rtn
+        
+    def getQGraphicsItem(self):
+        if self._svgItem == None:
+            # FIXME: make proper SVG component for beam splitter
+            self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/mirror_flat.svg", self ,[(-4,15,self.nodes[0]), (14,15,self.nodes[1])])
+            
+        return self._svgItem
+   
+class space(Component):
+    def __init__(self, name, node1, node2, L=0, n=1):
+        Component.__init__(self, name)
+        
+        self._requested_node_names.append(node1)
+        self._requested_node_names.append(node2)
+        self._QItem = None
+        self.__L = Param("L", self, SIfloat(L))
+        self.__n = Param("n", self, SIfloat(n))
+        
+    @property
+    def L(self): return self.__L
+    @L.setter
+    def L(self,value): self.__L,value = SIfloat(value)
+    @property
+    def n(self): return self.__n
+    @n.setter
+    def n(self,value): self.__n.value = SIfloat(value)
+    
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0] != "s":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse space command".format(text))
+
+        values.pop(0) # remove initial value
+        
+        if len(values) == 5:
+            return space(values[0], values[3], values[4], values[1], values[2])
+        elif len(values) == 4:
+            return space(values[0], values[2], values[3], values[1])
+        else:
+            raise exceptions.RuntimeError("Space Finesse code format incorrect '{0}'".format(text))
+        
+    def getFinesseText(self):
+        rtn = []
+        
+        if self.__n == 1:
+            rtn.append('s {0} {1} {2} {3}'.format(self.name, self.__L.value, self.nodes[0].name, self.nodes[1].name))
+        else:
+            rtn.append('s {0} {1} {2} {3} {4}'.format(self.name, self.__L.value, self.__n.value, self.nodes[0].name, self.nodes[1].name))
+       
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+            
+        return rtn
+        
+    def getQGraphicsItem(self):
+        if self._QItem == None:
+            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self)
+        
+        return self._QItem
+
+class grating(Component):
+    def __init__(self, name, node1, node2, node3 = None, node4 = None, n = 2, d = 0, eta_0 = 0, eta_1 = 0, eta_2 = 0, eta_3 = 0, rho_0 = 0, alpha = 0): # TODO: implement Rcx, Rcy and Rc
+        Component.__init__(self, name)
+        
+        self._requested_node_names.append(node1)
+        self._requested_node_names.append(node2)
+
+        if n > 2:
+            if node3 != None:
+                self._requested_node_names.append(node3)
+            else:
+                raise exceptions.RuntimeError("Grating node 3 not specified")
+
+        if n > 3:
+            if node4 != None:
+                self._requested_node_names.append(node4)
+            else:
+                raise exceptions.RuntimeError("Grating node 4 not specified")
+
+        if n > 4 or n < 2:
+            raise exceptions.RuntimeError("Grating must have between 2 and 4 ports")
+        
+        self.__n = n
+        self.__d = Param("d", self, SIfloat(d))
+        self.__eta_0 = AttrParam("eta_0", self, SIfloat(eta_0))
+        self.__eta_1 = AttrParam("eta_1", self, SIfloat(eta_1))
+        self.__eta_2 = AttrParam("eta_2", self, SIfloat(eta_2))
+        self.__eta_3 = AttrParam("eta_3", self, SIfloat(eta_3))
+        self.__rho_0 = AttrParam("rho_0", self, SIfloat(rho_0))
+        self.__alpha = AttrParam("alpha", self, SIfloat(alpha))
+    
+    @property
+    def n(self): return Param('n', self.__n)
+    @n.setter
+    def n(self, value):
+        if value < 2 or value > 4:
+            raise exceptions.RuntimeError("Grating must have between 2 and 4 ports")
+        else:
+            self.__n = value
+    
+    @property
+    def d(self): return self.__d
+    @d.setter
+    def d(self, value): self.__d.value = SIfloat(value)
+    
+    @property
+    def eta_0(self): return self.__eta_0
+    @eta_0.setter
+    def eta_0(self, value): self.__eta_0.value = SIfloat(value)
+    
+    @property
+    def eta_1(self): return self.__eta_1
+    @eta_1.setter
+    def eta_1(self, value): self.__eta_1.value = SIfloat(value)
+    
+    @property
+    def eta_2(self): return self.__eta_2
+    @eta_2.setter
+    def eta_2(self, value): self.__eta_2.value = SIfloat(value)
+    
+    @property
+    def eta_3(self): return self.__eta_3
+    @eta_3.setter
+    def eta_3(self, value): self.__eta_3.value = SIfloat(value)
+    
+    @property
+    def rho_0(self): return self.__rho_0
+    @rho_0.setter
+    def rho_0(self, value): self.__rho_0.value = SIfloat(value)
+    
+    @property
+    def alpha(self): return self.__alpha
+    @alpha.setter
+    def alpha(self, value): self.__alpha.value = SIfloat(value)
+    
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0][0 : 2] != "gr":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse grating command".format(text))
+
+        if len(values[0]) > 2:
+            if int(values[0][2]) > 4 or int(values[0][2]) < 2:
+                raise exceptions.RuntimeError("Grating must have between 2 and 4 ports")
+            else:
+                n = int(values[0][2])
+        else:
+            n = 2
+
+        values.pop(0) # remove initial value
+        
+        if n == 2:
+            if len(values) != 4:
+                raise exceptions.RuntimeError("Two port grating must have 2 nodes defined")
+
+            return grating(values[0], values[2], values[3], None, None, n, values[1])
+        elif n == 3:
+            if len(values) != 5:
+                raise exceptions.RuntimeError("Three port grating must have 3 nodes defined")
+            
+            return grating(values[0], values[2], values[3], values[4], None, n, values[1])
+        else:
+            if len(values) != 6:
+                raise exceptions.RuntimeError("Four port grating must have 4 nodes defined")
+            
+            return grating(values[0], values[2], values[3], values[4], values[5], n, values[1])
+        
+    def getFinesseText(self):
+        rtn = []
+        
+        if self.__n == 2:
+            rtn.append('gr{0} {1} {2} {3} {4}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name))
+        elif self.__n == 3:
+            rtn.append('gr{0} {1} {2} {3} {4} {5}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name))
+        else:
+            rtn.append('gr{0} {1} {2} {3} {4} {5} {6}'.format(self.__n, self.name, self.__d.value, self.nodes[0].name, self.nodes[1].name, self.nodes[2].name, self.nodes[3].name))
+        
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+        
+        return rtn
+       
+    def getQGraphicsItem(self):
+        if self._QItem == None:
+            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make SVG graphic for grating
+        
+        return self._QItem
+
+class isolator(Component):
+    def __init__(self, name, node1, node2, S = 0):
+        Component.__init__(self, name)
+        
+        self._requested_node_names.append(node1)
+        self._requested_node_names.append(node2)
+        
+        self.__S = Param("S",self,SIfloat(S))
+        
+    @property
+    def S(self): return self.__S
+    @S.setter
+    def S(self, value): self.__S.value = SIfloat(value)
+    
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0] != "isol":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse isolator command".format(text))
+
+        values.pop(0) # remove initial value
+        
+        if len(values) == 4:
+            return isolator(values[0], values[2], values[3], values[1])
+        else:
+            raise exceptions.RuntimeError("Isolator Finesse code format incorrect '{0}'".format(text))
+        
+    def getFinesseText(self):
+        rtn = ['isol {0} {1} {2} {3}'.format(self.name, self.S.value, self.nodes[0].name, self.nodes[1].name)]
+        
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+            
+        return rtn
+
+    def getQGraphicsItem(self):
+        if self._QItem == None:
+            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make isolator graphic
+        
+        return self._QItem
+
+class lens(Component):
+    def __init__(self, name, node1, node2, f):
+        Component.__init__(self, name)
+        
+        self._requested_node_names.append(node1)
+        self._requested_node_names.append(node2)
+        
+        self.__f = Param("f", self, SIfloat(f))
+        
+    @property
+    def f(self): return self.__f
+    @f.setter
+    def f(self, value): self.__f.value = SIfloat(value)
+    
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0] != "lens":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse lens command".format(text))
+
+        values.pop(0) # remove initial value
+        
+        if len(values) == 4:
+            return lens(values[0], values[2], values[3], values[1])
+        else:
+            raise exceptions.RuntimeError("Lens Finesse code format incorrect '{0}'".format(text))
+        
+    def getFinesseText(self):
+        rtn = ['lens {0} {1} {2} {3}'.format(self.name, self.f.value, self.nodes[0].name, self.nodes[1].name)]
+        
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+            
+        return rtn
+        
+    def getQGraphicsItem(self):
+        if self._QItem == None:
+            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make lens graphic
+        
+        return self._QItem
+        
+class modulator(Component):
+    def __init__(self, name, f, midx, order, modulation_type, node1, node2, phase=0):
+        Component.__init__(self, name)
+        
+        self._requested_node_names.append(node1)
+        self._requested_node_names.append(node2)
+        
+        self.__f = Param("f", self, SIfloat(f))
+        self.__midx = Param("midx", self, SIfloat(midx))
+        self.__phase = Param("phase", self, SIfloat(phase))
+        self.__order = order
+        self.type = modulation_type
+        
+            
+    @property 
+    def f(self): return self.__f
+    @f.setter
+    def f(self, value): self.__f.value = SIfloat(value)
+    
+    @property 
+    def midx(self): return self.__midx
+    @midx.setter
+    def midx(self, value): self.__midx.value = SIfloat(value)
+    
+    @property 
+    def phase(self): return self.__phase
+    @phase.setter
+    def phase(self, value): self.__phase.value = SIfloat(value)
+    
+    @property 
+    def order(self): return self.__order
+    @order.setter
+    def order(self, value):
+        if order <= 1 and order > 6:
+            raise pkex.BasePyKatException("modulator order must be between 1 and 6")
+            
+        self.__order = int(value)
+    
+    @property 
+    def type(self): return self.__type
+    @type.setter
+    def type(self, value):
+        if value != "am" and value != "pm":
+            raise pkex.BasePyKatException("Modulator type must be am (amplitude modulation) or pm (phase modulation)")
+            
+        self.__type = str(value)
+    
+    @staticmethod
+    def parseFinesseText(text):
+        v = text.split(" ")
+
+        if v[0] != "mod":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse modulator command".format(text))
+
+        v.pop(0) # remove initial value
+        
+        if len(v) == 7:
+            return modulator(v[0], v[1], v[2], v[3], v[4], v[5], v[6])
+        if len(v) == 8:
+            return modulator(v[0], v[1], v[2], v[3], v[4], v[6], v[7], phase=v[5])
+        else:
+            raise pkex.BasePyKatException("Modulator Finesse code format incorrect '{0}'".format(text))
+        
+    def getFinesseText(self):
+        rtn = ['mod {0} {1} {2} {3} {4} {5} {6} {7}'.format(self.name, self.f, self.midx, self.order, self.type, self.phase, self.nodes[0].name, self.nodes[1].name)]
+        
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+            
+        return rtn
+        
+    def getQGraphicsItem(self):
+        if self._QItem == None:
+            self._QItem = pykat.gui.graphics.SpaceQGraphicsItem(self) # TODO: make lens graphic
+        
+        return self._QItem
+
+class laser(Component):
+    def __init__(self,name,node,P=1,f_offset=0,phase=0):
+        Component.__init__(self,name)
+        
+        self._requested_node_names.append(node)
+        
+        self.__power = Param("P", self, SIfloat(P))
+        self.__f_offset = Param("f", self, SIfloat(f_offset))
+        self.__phase = Param("phase", self, SIfloat(phase))
+        self.__noise = AttrParam("noise", self, 0)
+        
+    @property
+    def power(self): return self.__power
+    @power.setter
+    def power(self,value): self.__power.value = float(value)
+    
+    @property
+    def f(self): return self.__f_offset
+    @f.setter
+    def f(self,value): self.__f_offset.value = float(value)
+    
+    @property
+    def phase(self): return self.__phase
+    @phase.setter
+    def phase(self,value): self.__phase.value = float(value)
+    
+    @staticmethod
+    def parseFinesseText(text):
+        values = text.split(" ")
+
+        if values[0] != "l":
+            raise exceptions.RuntimeError("'{0}' not a valid Finesse laser command".format(text))
+
+        values.pop(0) # remove initial value
+        
+        if len(values) == 5:
+            return laser(values[0],values[4],P=values[1],f_offset=values[2],phase=values[3])
+        elif len(values) == 4:
+            return laser(values[0],values[3],P=values[1],f_offset=values[2], phase=0)
+        else:
+            raise exceptions.FinesseParse("Laser Finesse code format incorrect '{0}'".format(text))
+    
+    def getFinesseText(self):
+        rtn = ['l {0} {1} {2} {3} {4}'.format(self.name, self.__power.value, self.__f_offset.value, self.__phase.value, self.nodes[0].name)]
+        
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+        
+        return rtn
+         
+    def getQGraphicsItem(self):
+        if self._svgItem == None:
+            self._svgItem = pykat.gui.graphics.ComponentQGraphicsItem(":/resources/laser.svg", self, [(65,25,self.nodes[0])])
+            
+        return self._svgItem
+            
diff --git a/pykat/detectors.py b/pykat/detectors.py
index 30e46004ead75389d95c476d32db7ee9d524200b..67434f87f3b98f988f6de5e57479c5230a720380 100644
--- a/pykat/detectors.py
+++ b/pykat/detectors.py
@@ -1,297 +1,297 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 01 09:09:10 2013
-
-@author: Daniel
-"""
-import exceptions
-import pykat.gui.resources
-
-from pykat.utils import *
-from pykat.gui.graphics import *
-from pykat.node_network import *
-
-class Detector(object) :
-    def __init__(self, name,node):
-        self.__name = name
-        self._svgItem = None
-        self._kat = None
-        self.noplot = False
-        self.enabled = True
-        self.tag = None
-        self.__node = None
-        self._params = []
-        
-        if node.find('*'):
-            self._alternate_beam = True
-            node.replace('*','')
-        
-        self.__requested_node = node
-    
-    def _register_param(self, param):
-        self._params.append(param)
-        
-    def _on_kat_add(self, kat):
-        self.__node = kat.nodes.createNode(self.__requested_node)
-    
-    @staticmethod
-    def parseFinesseText(text):    
-        raise NotImplementedError("This function is not implemented")
-        
-    def getFinesseText(self):
-        """ Base class for individual finesse optical components """    
-        raise NotImplementedError("This function is not implemented")
-        
-    def getQGraphicsItem(self):    
-        return None
-    
-    @property 
-    def node(self): return self.__node
-    @node.setter
-    def node(self, value):
-        if value in self._kat.nodes:
-            self.__node = self._kat.nodes[value]
-        else:
-            raise pkex.BasePyKatException("There is no node called " + value)
-    
-    @property
-    def name(self): return self.__name        
-
-    def __str__(self): return self.name
-
-class pd(Detector):
-    
-    def __init__(self, name, num_demods, node_name, senstype=None, alternate_beam=False, **kwargs):
-        Detector.__init__(self, name, node_name)
-        
-        self.__num_demods = num_demods
-        self.__senstype = senstype
-        self.__alternate_beam = alternate_beam
-        # create the parameters for all 5 demodulations regardless
-        # of how many the user specifies. Later we add properties to
-        # those which correspond to the number of demodulations
-        
-        self.__f1 = Param("f1", self, 0)
-        self.__f2 = Param("f2", self, 0)
-        self.__f3 = Param("f3", self, 0)
-        self.__f4 = Param("f4", self, 0)
-        self.__f5 = Param("f5", self, 0)
-        
-        self.__phi1 = Param("phi1", self, None)
-        self.__phi2 = Param("phi2", self, None)
-        self.__phi3 = Param("phi3", self, None)
-        self.__phi4 = Param("phi4", self, None)
-        self.__phi5 = Param("phi5", self, None)
-        
-        # define new class for assigning new attributes
-        cls = type(self)
-        self.__class__ = type(cls.__name__, (cls,), {})
-    
-        self.__set_demod_attrs()
-        
-    @property
-    def senstype(self): return self.__senstype
-    @senstype.setter
-    def senstype(self,value):
-        if value == "": value = None
-        
-        if value != "S" and value != "N" and value != None: 
-            raise pkex.BasePyKatException("Photodiode sensitivity type can either be 'N', 'S' or None.")
-            
-        self.__senstype = value
-        
-    @property
-    def num_demods(self): return self.__num_demods
-    @num_demods.setter
-    def num_demods(self, value): 
-        if value < 0 or value > 5:
-            raise pkex.BasePyKatException("Number of demodulations must be between 0 and 5")
-        
-        self.__num_demods = value
-        self.__set_demod_attrs()
-    
-    def __get_fphi(self, name):
-        return getattr(self, '_'+ self.__class__.__name__ +'__' + name)
-    
-    def __set_f(self, num, value):
-        setattr(self, '_'+ self.__class__.__name__ +'__f' + name, float(value))
-    
-    def __set_phi(self, num, value):
-        if value == None and num != self.num_demods:
-            # check if we are setting no phase that this is only on the last
-            # demodulation phase.
-            raise pkex.BasePyKatException("Only last demodulation phase can be set to None")
-        elif isinstance(value, str) and not isinstance(value,float) and value.lower() != "max":
-            raise pkex.BasePyKatException("Demodulation phase can only be set to a 'max' or a number (or None if the last demodulation phase)")
-            
-        setattr(self, '_'+ self.__class__.__name__ +'__phi' + name, value)
-        
-    def __set_demod_attrs(self):
-        """
-        For the set number of demodulations the correct number of 
-        Parameters are created.
-        """
-        
-        # if there are demodulations present then we want to add
-        # the various parameters so they are available for users
-        # to play with.
-        if self.__num_demods > 0:
-            for i in range(1,6):
-                name = str(i)
-                if i <= self.num_demods:
-                    if not hasattr(self, "f"+name):
-                        setattr(self.__class__, "f"+name, property(fget=lambda self, i=i: self.__get_fphi('f'+str(i)), fset=lambda self, value, i=i: self.__set_f(str(i), value)))
-                    
-                    if not hasattr(self, "phi"+name):
-                        setattr(self.__class__, "phi"+name, property(fget=lambda self, i=i: self.__get_fphi('phi'+str(i)), fset=lambda self, value, i=i: self.__set_phi(str(i), value)))
-                else:
-                    if hasattr(self, "f"+name):
-                        delattr(self.__class__, "f"+name)
-                    if hasattr(self, "phi"+name):
-                        delattr(self.__class__, "phi"+name)
-        else:
-            return
-    
-    def getFinesseText(self) :
-        rtn = []
-        
-        if self.enabled:
-            alt_str = ""
-            fphi_str = ""
-            
-            if self.__alternate_beam:
-                alt_str = "*"
-                
-            for n in range(1, 1+self.num_demods):
-                fphi_str += str(self.__getattribute__("f"+str(n)))
-                phi_val = self.__getattribute__("phi"+str(n))
-                
-                if phi_val != None:
-                    fphi_str += " " + str(phi_val)
-            
-            senstype = self.senstype
-            
-            if senstype == None:
-                senstype = ""
-                
-            rtn.append("pd{0}{1} {2} {3} {4}{5}".format(senstype, self.num_demods, self.name, fphi_str, self.node.name, alt_str))
-        
-        for p in self._params:
-            rtn.extend(p.getFinesseText())
-            
-        return rtn
-            
-class photodiode(Detector):
-
-    class __F(list):
-        def __init__(self, values=None):
-            if values==None:
-                values = []
-            list.__init__(self,[SIfloat(value) for value in values])
-            
-    class __Phi(list):
-        def __convertValue(self, value):
-            if value=="max":
-                return value                
-            else:
-                return SIfloat(value)
-                
-        def __init__(self, values=None):
-            if values==None:
-                values = []
-            list.__init__(self,[self.__convertValue(value) for value in values])
-
-        def __getitem__(self, key): # probably not needed
-            if list.__getitem__(self,key)=="max":
-                return list.__getitem__(self,key)
-            else:
-                return float(list.__getitem__(self,key))
-            
-    @property
-    def f(self): return self.__f
-
-    @property
-    def phi(self): return self.__phi
-        
-    def __init__(self, name, node, senstype="", num_demods=0, demods=[]):        
-        Detector.__init__(self, name, node)
-        
-        if num_demods>2:
-            raise NotImplementedError("pd with more than two demodulations not implemented yet")   
-            
-        self.num_demods = num_demods
-        self.senstype = senstype
-
-        # every second element into f (starting at 1)
-        self.__f = self.__F(demods[::2])
-        
-        # Every second element into phi (starting at 2)
-        self.__phi = self.__Phi(demods[1::2])
-        
-    @staticmethod
-    def parseFinesseText(text): 
-        values = text.split(" ")
-
-        if values[0][0:2] != "pd":
-            raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
-        if len(values[0])==2:
-            __num_demods=0
-            __senstype=""
-        elif len(values[0])==3 or len(values[0])==4:
-            if values[0][2]=="S":
-                __senstype="S"
-            elif values[0][2]=="N":
-                __senstype="N"
-            else:
-                try:
-                    __num_demods=int(values[0][2])
-                    __senstype=""
-                except ValueError:
-                    raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
-            if len(values[0])==4:
-                try:
-                    __num_demods=int(values[0][3])
-                except ValueError:
-                    raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))                
-        else:
-            raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
-
-        if __num_demods<0 or __num_demods>5:
-            raise exceptions.FinesseParse("'{0}' number of demodulations must be >0 and <5".format(text))
-
-        values.pop(0) # remove initial value
-
-        if len(values) == 2 * __num_demods + 1 or len(values) == 2 * __num_demods + 2:
-            return photodiode(values[0], values[-1], __senstype, __num_demods, values[1:len(values)-1])
-        else:
-            raise exceptions.FinesseParse("Photodiode code format incorrect '{0}'".format(text))
-
-        #return photodiode("name", "node", demods)   
-        #raise NotImplementedError("This function is not implemented")   
-        
-    def getFinesseText(self) :
-        if self.enabled:
-            rtn = []
-            __f_phi=range(len(self.f)+len(self.phi))
-            __f_phi[::2]=self.f
-            __f_phi[1::2]=self.phi
-            __f_phi_str = " ".join(map(str, __f_phi))
-            
-            if self._alternate_beam:
-                rtn.append("pd{0}{1} {2} {3} {4}".format(self.senstype, self.num_demods, self.name, __f_phi_str,  self.node.name))
-            else:
-                rtn.append("pd{0}{1} {2} {3} {4}*".format(self.senstype, self.num_demods, self.name, __f_phi_str,  self.node.name))
-            
-            if self.noplot:
-                rtn.append("noplot {0}".format(self.name))
-            
-            return rtn
-        else:
-            return None
-    
-    def getQGraphicsItem(self):
-        if self._svgItem == None:
-            self._svgItem = ComponentQGraphicsItem(":/resources/photodiode_red.svg",self,[(-5,11,self.node)])
-        
-        return self._svgItem    
-        
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Feb 01 09:09:10 2013
+
+@author: Daniel
+"""
+import exceptions
+import pykat.gui.resources
+
+from pykat.utils import *
+from pykat.gui.graphics import *
+from pykat.node_network import *
+
+class Detector(object) :
+    def __init__(self, name,node):
+        self.__name = name
+        self._svgItem = None
+        self._kat = None
+        self.noplot = False
+        self.enabled = True
+        self.tag = None
+        self.__node = None
+        self._params = []
+        
+        if node.find('*'):
+            self._alternate_beam = True
+            node.replace('*','')
+        
+        self.__requested_node = node
+    
+    def _register_param(self, param):
+        self._params.append(param)
+        
+    def _on_kat_add(self, kat):
+        self.__node = kat.nodes.createNode(self.__requested_node)
+    
+    @staticmethod
+    def parseFinesseText(text):    
+        raise NotImplementedError("This function is not implemented")
+        
+    def getFinesseText(self):
+        """ Base class for individual finesse optical components """    
+        raise NotImplementedError("This function is not implemented")
+        
+    def getQGraphicsItem(self):    
+        return None
+    
+    @property 
+    def node(self): return self.__node
+    @node.setter
+    def node(self, value):
+        if value in self._kat.nodes:
+            self.__node = self._kat.nodes[value]
+        else:
+            raise pkex.BasePyKatException("There is no node called " + value)
+    
+    @property
+    def name(self): return self.__name        
+
+    def __str__(self): return self.name
+
+class pd(Detector):
+    
+    def __init__(self, name, num_demods, node_name, senstype=None, alternate_beam=False, **kwargs):
+        Detector.__init__(self, name, node_name)
+        
+        self.__num_demods = num_demods
+        self.__senstype = senstype
+        self.__alternate_beam = alternate_beam
+        # create the parameters for all 5 demodulations regardless
+        # of how many the user specifies. Later we add properties to
+        # those which correspond to the number of demodulations
+        
+        self.__f1 = Param("f1", self, 0)
+        self.__f2 = Param("f2", self, 0)
+        self.__f3 = Param("f3", self, 0)
+        self.__f4 = Param("f4", self, 0)
+        self.__f5 = Param("f5", self, 0)
+        
+        self.__phi1 = Param("phi1", self, None)
+        self.__phi2 = Param("phi2", self, None)
+        self.__phi3 = Param("phi3", self, None)
+        self.__phi4 = Param("phi4", self, None)
+        self.__phi5 = Param("phi5", self, None)
+        
+        # define new class for assigning new attributes
+        cls = type(self)
+        self.__class__ = type(cls.__name__, (cls,), {})
+    
+        self.__set_demod_attrs()
+        
+    @property
+    def senstype(self): return self.__senstype
+    @senstype.setter
+    def senstype(self,value):
+        if value == "": value = None
+        
+        if value != "S" and value != "N" and value != None: 
+            raise pkex.BasePyKatException("Photodiode sensitivity type can either be 'N', 'S' or None.")
+            
+        self.__senstype = value
+        
+    @property
+    def num_demods(self): return self.__num_demods
+    @num_demods.setter
+    def num_demods(self, value): 
+        if value < 0 or value > 5:
+            raise pkex.BasePyKatException("Number of demodulations must be between 0 and 5")
+        
+        self.__num_demods = value
+        self.__set_demod_attrs()
+    
+    def __get_fphi(self, name):
+        return getattr(self, '_'+ self.__class__.__name__ +'__' + name)
+    
+    def __set_f(self, num, value):
+        setattr(self, '_'+ self.__class__.__name__ +'__f' + name, float(value))
+    
+    def __set_phi(self, num, value):
+        if value == None and num != self.num_demods:
+            # check if we are setting no phase that this is only on the last
+            # demodulation phase.
+            raise pkex.BasePyKatException("Only last demodulation phase can be set to None")
+        elif isinstance(value, str) and not isinstance(value,float) and value.lower() != "max":
+            raise pkex.BasePyKatException("Demodulation phase can only be set to a 'max' or a number (or None if the last demodulation phase)")
+            
+        setattr(self, '_'+ self.__class__.__name__ +'__phi' + name, value)
+        
+    def __set_demod_attrs(self):
+        """
+        For the set number of demodulations the correct number of 
+        Parameters are created.
+        """
+        
+        # if there are demodulations present then we want to add
+        # the various parameters so they are available for users
+        # to play with.
+        if self.__num_demods > 0:
+            for i in range(1,6):
+                name = str(i)
+                if i <= self.num_demods:
+                    if not hasattr(self, "f"+name):
+                        setattr(self.__class__, "f"+name, property(fget=lambda self, i=i: self.__get_fphi('f'+str(i)), fset=lambda self, value, i=i: self.__set_f(str(i), value)))
+                    
+                    if not hasattr(self, "phi"+name):
+                        setattr(self.__class__, "phi"+name, property(fget=lambda self, i=i: self.__get_fphi('phi'+str(i)), fset=lambda self, value, i=i: self.__set_phi(str(i), value)))
+                else:
+                    if hasattr(self, "f"+name):
+                        delattr(self.__class__, "f"+name)
+                    if hasattr(self, "phi"+name):
+                        delattr(self.__class__, "phi"+name)
+        else:
+            return
+    
+    def getFinesseText(self) :
+        rtn = []
+        
+        if self.enabled:
+            alt_str = ""
+            fphi_str = ""
+            
+            if self.__alternate_beam:
+                alt_str = "*"
+                
+            for n in range(1, 1+self.num_demods):
+                fphi_str += str(self.__getattribute__("f"+str(n)))
+                phi_val = self.__getattribute__("phi"+str(n))
+                
+                if phi_val != None:
+                    fphi_str += " " + str(phi_val)
+            
+            senstype = self.senstype
+            
+            if senstype == None:
+                senstype = ""
+                
+            rtn.append("pd{0}{1} {2} {3} {4}{5}".format(senstype, self.num_demods, self.name, fphi_str, self.node.name, alt_str))
+        
+        for p in self._params:
+            rtn.extend(p.getFinesseText())
+            
+        return rtn
+            
+class photodiode(Detector):
+
+    class __F(list):
+        def __init__(self, values=None):
+            if values==None:
+                values = []
+            list.__init__(self,[SIfloat(value) for value in values])
+            
+    class __Phi(list):
+        def __convertValue(self, value):
+            if value=="max":
+                return value                
+            else:
+                return SIfloat(value)
+                
+        def __init__(self, values=None):
+            if values==None:
+                values = []
+            list.__init__(self,[self.__convertValue(value) for value in values])
+
+        def __getitem__(self, key): # probably not needed
+            if list.__getitem__(self,key)=="max":
+                return list.__getitem__(self,key)
+            else:
+                return float(list.__getitem__(self,key))
+            
+    @property
+    def f(self): return self.__f
+
+    @property
+    def phi(self): return self.__phi
+        
+    def __init__(self, name, node, senstype="", num_demods=0, demods=[]):        
+        Detector.__init__(self, name, node)
+        
+        if num_demods>2:
+            raise NotImplementedError("pd with more than two demodulations not implemented yet")   
+            
+        self.num_demods = num_demods
+        self.senstype = senstype
+
+        # every second element into f (starting at 1)
+        self.__f = self.__F(demods[::2])
+        
+        # Every second element into phi (starting at 2)
+        self.__phi = self.__Phi(demods[1::2])
+        
+    @staticmethod
+    def parseFinesseText(text): 
+        values = text.split(" ")
+
+        if values[0][0:2] != "pd":
+            raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
+        if len(values[0])==2:
+            __num_demods=0
+            __senstype=""
+        elif len(values[0])==3 or len(values[0])==4:
+            if values[0][2]=="S":
+                __senstype="S"
+            elif values[0][2]=="N":
+                __senstype="N"
+            else:
+                try:
+                    __num_demods=int(values[0][2])
+                    __senstype=""
+                except ValueError:
+                    raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
+            if len(values[0])==4:
+                try:
+                    __num_demods=int(values[0][3])
+                except ValueError:
+                    raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))                
+        else:
+            raise exceptions.FinesseParse("'{0}' not a valid photodiode command".format(text))
+
+        if __num_demods<0 or __num_demods>5:
+            raise exceptions.FinesseParse("'{0}' number of demodulations must be >0 and <5".format(text))
+
+        values.pop(0) # remove initial value
+
+        if len(values) == 2 * __num_demods + 1 or len(values) == 2 * __num_demods + 2:
+            return photodiode(values[0], values[-1], __senstype, __num_demods, values[1:len(values)-1])
+        else:
+            raise exceptions.FinesseParse("Photodiode code format incorrect '{0}'".format(text))
+
+        #return photodiode("name", "node", demods)   
+        #raise NotImplementedError("This function is not implemented")   
+        
+    def getFinesseText(self) :
+        if self.enabled:
+            rtn = []
+            __f_phi=range(len(self.f)+len(self.phi))
+            __f_phi[::2]=self.f
+            __f_phi[1::2]=self.phi
+            __f_phi_str = " ".join(map(str, __f_phi))
+            
+            if self._alternate_beam:
+                rtn.append("pd{0}{1} {2} {3} {4}".format(self.senstype, self.num_demods, self.name, __f_phi_str,  self.node.name))
+            else:
+                rtn.append("pd{0}{1} {2} {3} {4}*".format(self.senstype, self.num_demods, self.name, __f_phi_str,  self.node.name))
+            
+            if self.noplot:
+                rtn.append("noplot {0}".format(self.name))
+            
+            return rtn
+        else:
+            return None
+    
+    def getQGraphicsItem(self):
+        if self._svgItem == None:
+            self._svgItem = ComponentQGraphicsItem(":/resources/photodiode_red.svg",self,[(-5,11,self.node)])
+        
+        return self._svgItem    
+        
diff --git a/pykat/exceptions.py b/pykat/exceptions.py
index 2e3d0168e5687ba489f91316cb0124af6fee9e12..e268327a669ddbc43dbde8d72046f3dcd6f1c967 100644
--- a/pykat/exceptions.py
+++ b/pykat/exceptions.py
@@ -1,30 +1,30 @@
-import exceptions
-
-class BasePyKatException(Exception):
-    def __init__(self, msg):
-        self.__msg = msg
-        
-    def __str__(self):
-        return self.__msg
-
-class FinesseParse(BasePyKatException) :    
-    def __init__(self, msg):
-        BasePyKatException.__init__(self, "Error parsing Finesse input\n{0}".format(msg))
-    
-class MissingFinesseEnvVar(BasePyKatException) :    
-    def __init__(self):
-        BasePyKatException.__init__(self, "The environment variable FINESSE_DIR was not defined")
-
-class MissingFinesse(BasePyKatException) :    
-    def __init__(self):
-        BasePyKatException.__init__(self, "Could not find the finesse executable 'kat' in '{0}'," \
-                                     "or you do not have the permissions to run it." \
-                                      .format(os.environ.get('FINESSE_DIR')))
-    
-class FinesseRunError(BasePyKatException) :
-    def __init__(self, err, kat):
-        self.__err = err
-        self.__kat = kat
-        
-        BasePyKatException.__init__(self, "Finesse returned an error running {1}: {0}".format(self.__err, self.__kat))
-        
+import exceptions
+
+class BasePyKatException(Exception):
+    def __init__(self, msg):
+        self.__msg = msg
+        
+    def __str__(self):
+        return self.__msg
+
+class FinesseParse(BasePyKatException) :    
+    def __init__(self, msg):
+        BasePyKatException.__init__(self, "Error parsing Finesse input\n{0}".format(msg))
+    
+class MissingFinesseEnvVar(BasePyKatException) :    
+    def __init__(self):
+        BasePyKatException.__init__(self, "The environment variable FINESSE_DIR was not defined")
+
+class MissingFinesse(BasePyKatException) :    
+    def __init__(self):
+        BasePyKatException.__init__(self, "Could not find the finesse executable 'kat' in '{0}'," \
+                                     "or you do not have the permissions to run it." \
+                                      .format(os.environ.get('FINESSE_DIR')))
+    
+class FinesseRunError(BasePyKatException) :
+    def __init__(self, err, kat):
+        self.__err = err
+        self.__kat = kat
+        
+        BasePyKatException.__init__(self, "Finesse returned an error running {1}: {0}".format(self.__err, self.__kat))
+        
diff --git a/pykat/finesse.py b/pykat/finesse.py
index 3930e681795583f3ce3f2eaf72912ca3e9ec56ed..6fac5751abae974f5b7102debfc30907de331709 100644
--- a/pykat/finesse.py
+++ b/pykat/finesse.py
@@ -1,725 +1,725 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Sun Jan 27 09:56:53 2013
-
-PyKat - Python interface and wrapper for FINESSE
-Copyright (C) 2013 Daniel David Brown
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Contact at ddb@star.sr.bham.ac.uk
-
-@author: Daniel Brown
-"""
-import sys
-import os
-import subprocess
-import tempfile
-import numpy as np
-import datetime
-import pickle
-import pykat
-import warnings
-import re
-
-from pykat.node_network import NodeNetwork
-from pykat.detectors import Detector
-from pykat.components import Component
-from pykat.commands import Command, xaxis
-from pykat.gui.gui import pyKatGUI
-
-import pykat.exceptions as pkex
-
-from PyQt4.QtCore import QCoreApplication
-from PyQt4.QtGui import QApplication
-
-NO_GUI = False
-NO_BLOCK = "NO_BLOCK"
-
-class katRun(object):
-    def __init__(self):
-        self.runDateTime = datetime.datetime.now()
-        self.x = None
-        self.y = None
-        self.xlabel = None
-        self.ylabels = None
-        self.katScript = None
-        self.katVersion = None
-        
-    def saveKatRun(self, filename):
-        with open(filename,'w') as outfile:
-            pickle.dump(self, outfile)
-    
-    @staticmethod
-    def loadKatRun(filename):
-        with open(filename,'r') as infile:
-            return pickle.load(infile)
-    
-    def get(self, value): return self[value]
-    
-    def __getitem__(self, value):
-        if value in self.ylabels:
-            idx = self.ylabels.index(value)
-            if len(self.y.shape) == 1:
-                return self.y
-            else:
-                return self.y[:, idx]
-        else:
-            raise  pkex.BasePyKatException("No output by the name {0} found".format(value))
-      
-class katRun2D(object):
-    def __init__(self):
-        self.runDateTime = datetime.datetime.now()
-        self.x = None
-        self.y = None
-        self.z = None
-        self.xlabel = None
-        self.ylabel = None
-        self.zlabels = None
-        self.katScript = None
-        self.katVersion = None
-        
-    def saveKatRun(self, filename):
-        with open(filename,'w') as outfile:
-            pickle.dump(self, outfile)
-    
-    @staticmethod
-    def loadKatRun(filename):
-        with open(filename,'r') as infile:
-            return pickle.load(infile)
-    
-    def get(self, value): return self[value]
-    
-    def __getitem__(self, value):
-        idx = [i for i in range(len(self.zlabels)) if self.zlabels[i].split(" ")[0] == str(value)]
-        
-        if len(idx) > 0:
-            return self.z[idx].squeeze()
-        else:
-            raise  pkex.BasePyKatException("No output by the name {0} found".format(str(value)))
-      
-class Block:
-    def __init__(self, name):
-        self.__name = name
-        self.contents = [] # List of objects and strings of finesse code
-        self.enabled = True 
-        
-    @property
-    def name(self): return self.__name
-    
-class kat(object):                    
-        
-    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
-        self.__blocks = {} # dictionary of blocks that are used
-        self.__components = {}  # dictionary of optical components      
-        self.__detectors = {}   # dictionary of detectors
-        self.__commands = {}    # dictionary of commands
-        self.__extra_lines = [] # an array of strings which are just normal finesse code to include when running
-        self.__gui = None
-        self.nodes = NodeNetwork(self)  
-        self.__katdir = katdir
-        self.__katname = katname
-        self.__tempdir = tempdir
-        self.__tempname = tempname
-        self.pykatgui = None
-        
-        # Various options for running finesse, typicaly the commands with just 1 input
-        # and have no name attached to them.
-        self.__phase = None
-        self.__maxtem = None
-        self.__noxaxis = None
-        self.__time_code = None
-        
-        if kat_code != None and kat_file != None:
-            raise pkex.BasePyKatException("Specify either a Kat file or some Kat code, not both.")
-        
-        if kat_code != None:
-            self.parseCommands(kat_code)
-        
-        if kat_file != None:
-            self.loadKatFile(kat_file)
-        
-        cls = type(self)
-        self.__class__ = type(cls.__name__, (cls,), {})
-        
-    @property
-    def maxtem(self): return self.__maxtem
-    @maxtem.setter
-    def maxtem(self,value): self.__maxtem = int(value)
-    
-    @property
-    def phase(self): return self.__phase
-    @phase.setter
-    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)
-    
-    @property
-    def noxaxis(self): return self.__noxaxis
-    @noxaxis.setter
-    def noxaxis(self,value): self.__noxaxis = bool(value)
-
-    def logo(self):
-        print """                                              ..-
-    PyKat                 _                  '(
-                          \\`.|\\.__...-\"\"""-_." )
-       ..+-----.._        /  ' `            .-'
-   . '            `:      7/* _/._\\    \\   (
-  (        '::;;+;;:      `-"' =" /,`"" `) /
-  L.        \\`:::a:f            c_/     n_'
-  ..`--...___`.  .    ,  
-   `^-....____:   +."""
-    
-    def loadKatFile(self, katfile):
-        commands=open(katfile).read()
-        self.parseCommands(commands)
-    
-    def parseKatCode(self, code):
-        #commands = code.split("\n")
-        self.parseCommands(code)
-        
-    def parseCommands(self, commands):
-        blockComment = False
-
-        self.__currentTag= NO_BLOCK
-        
-        if not (NO_BLOCK in self.__blocks):
-            self.__blocks[NO_BLOCK] = Block(NO_BLOCK)
-        
-        commands=self.remove_comments(commands)
-        
-        after_process = [] # list of commands that should be processed after 
-                           # objects have been set and created
-        
-        for line in commands.split("\n"):
-            #for line in commands:
-            if len(line.strip()) >= 2:
-                line = line.strip()
-
-                # Looking for block start or end
-                values = line.split(" ")
-                if values[0] == "%%%":
-                    if values[1] == "FTblock":
-                        newTag = values[2]
-                        
-                        if self.__currentTag != None and newTag != self.__currentTag: 
-                            warnings.warn("found block {0} before block {1} ended".format(newTag, self.__currentTag))    
-                            
-                        if newTag in self.__blocks:
-                            raise pkex.BasePyKatException("Block `{0}` has already been read")
-                            
-                        self.__blocks[newTag] = Block(newTag) # create new list to store all references to components in block
-                        self.__currentTag = newTag                            
-                        
-                    if values[1] == "FTend":
-                        self.__currentTag = NO_BLOCK
-                        
-                    continue
-                #warnings.warn("current tag {0}".format(self.__currentTag))    
-
-                # don't read comment lines
-                if line[0] == "#" or line[0] == "%":
-                    continue
-                
-                # check if block comment is being used
-                if not blockComment and line[0:2] == "/*":
-                    blockComment = True
-                    continue
-                elif blockComment and line[0:2] == "*/":
-                    blockComment = False
-                    continue
-                
-                first = line.split(" ",1)[0]
-                obj = None
-                
-                if(first == "m" or first == "m1" or first == "m2"):
-                    obj = pykat.components.mirror.parseFinesseText(line)
-                elif(first == "s"):
-                    obj = pykat.components.space.parseFinesseText(line)
-                elif(first == "l"):
-                    obj = pykat.components.laser.parseFinesseText(line)
-                elif(first[0:2] == "bs"):
-                    obj = pykat.components.beamSplitter.parseFinesseText(line)
-                elif(first[0:2] == "gr"):
-                    obj = pykat.components.grating.parseFinesseText(line)
-                elif(first[0:4] == "isol"):
-                    obj = pykat.components.isolator.parseFinesseText(line)
-                elif(first[0:4] == "lens"):
-                    obj = pykat.components.lens.parseFinesseText(line)
-                elif(first[0:3] == "mod"):
-                    obj = pykat.components.modulator.parseFinesseText(line)
-                elif(first[0:2] == "pd"):
-                    obj = pykat.detectors.photodiode.parseFinesseText(line)
-                elif(first == "xaxis" or first == "xaxis*"):
-                    obj = pykat.commands.xaxis.parseFinesseText(line)
-                elif(first == "x2axis" or first == "x2axis*"):
-                    obj = pykat.commands.x2axis.parseFinesseText(line)
-                elif(first == "gauss" or first == "gauss*" or first == "gauss**"):
-                    after_process.append(line)
-                else:
-                    if self.verbose:
-                        print "Parsing `{0}` into pykat object not implemented yet, added as extra line.".format(line)
-                    obj = line
-                    # manually add the line to the block contents
-                    self.__blocks[self.__currentTag].contents.append(line) 
-                
-                if obj != None and not isinstance(obj, str):
-                    self.add(obj)
-                    
-        # now process all the varous gauss/attr etc. commands which require
-        # components to exist first before they can be processed
-        for line in after_process:
-            first = line.split(" ",1)[0]
-            
-            if first == "gauss" or first == "gauss*" or first == "gauss**":
-                pykat.commands.gauss.parseFinesseText(line)
-            
-        self.__currentTag = NO_BLOCK 
-
-    def saveScript(self, filename=None):
-        """
-        Saves the current kat object to a Finesse input file
-        """
-        try:
-            katScript = "".join(self.generateKatScript())       
-            katfile = open(filename,'w')
-            katfile.writelines(katScript)
-            katfile.flush()
-            katfile.close()
-
-        except pkex.BasePyKatException as ex:
-            print ex
-            
-    def run(self, printout=0, printerr=0, save_output=False, save_kat=False,kat_name=None) :
-        """ 
-        Runs the current simulation setup that has been built thus far.
-        It returns a katRun or katRun2D object which is populated with the various
-        data from the simulation run.
-        """
-        start = datetime.datetime.now()
-
-        
-        try:        
-            if not hasattr(self, "xaxis") and self.noxaxis != None and self.noxaxis == False:
-                raise pkex.BasePyKatException("No xaxis was defined")
-            
-            if len(self.__katdir) == 0:
-                # Get the environment variable for where Finesse is stored
-                self.__finesse_dir = os.environ.get('FINESSE_DIR')
-                
-                if self.__finesse_dir == None :
-                    raise MissingFinesseEnvVar()
-            else:
-                self.__finesse_dir = self.__katdir
-                
-            if len(self.__katname) == 0:
-                katexe = "kat"
-                
-                if os.sys.platform == "win32":
-                    katexe += ".exe"
-            else:
-                katexe = self.__katname
-            
-            kat_exec = os.path.join(self.__finesse_dir, katexe) 
-            
-            # check if kat file exists and it is executable by user        
-            if not (os.path.isfile(kat_exec) and os.access(kat_exec, os.X_OK)):
-                raise MissingFinesse()
-                
-            if self.verbose: print "--------------------------------------------------------------"
-            if self.verbose: print "Running kat - Started at " + str(start)
-            
-            if hasattr(self, "x2axis"):
-                r = katRun2D()
-            else:
-                r = katRun()
-                
-            r.katScript = "".join(self.generateKatScript())   
-            
-            # create a kat file which we will write the script into
-            if self.__tempname == None:
-                katfile = tempfile.NamedTemporaryFile(suffix=".kat", dir=self.__tempdir)
-            else:
-                filepath =os.path.join(self.__tempdir, self.__tempname+".kat" )
-                katfile = open( filepath, 'w' ) 
-                
-            katfile.writelines(r.katScript)
-            katfile.flush()
-            
-            cmd=[kat_exec, '--perl1']
-            
-            if self.__time_code:
-                cmd.append('--perf-timing')
-
-            cmd.append('--no-backspace')
-            # set default format so that less repeated numbers are printed to the
-            # output file, should speed up running and parsing of output files
-            cmd.append('-format=%.15g')
-
-            cmd.append(katfile.name)
-            
-            #if self.verbose:
-                #print cmd
-                
-            p=subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-            err = ""
-            
-            if self.verbose: print "Finesse binary output:"
-            
-            for line in iter(p.stderr.readline, ""):
-                #err += line 
-                
-                if len(line) > 0:
-                    if line.rstrip().endswith('%'):
-                        vals = line.split("-")
-                        action = vals[0].strip()
-                        prc = vals[1].strip()[:-1]
-                        sys.stdout.write("\r{0} {1}%".format(action, prc))
-                    elif line[0] == '*':
-                        sys.stdout.write(line)
-                        
-            [out,errpipe] = p.communicate()
-            
-            # get the version number
-            ix = out.find('build ') + 6
-            ix2 = out.find(')',ix)
-            r.katVersion = out[ix:ix2]
-            
-            r.runDateTime = datetime.datetime.now()
-            
-            if p.returncode != 0:
-                raise pkex.FinesseRunError(err, katfile.name)
-            
-            if printout == 1: print out
-            if printerr == 1: print err
-
-            root = os.path.splitext(katfile.name)
-            base = os.path.basename(root[0])            
-            outfile = root[0] + ".out"
-            
-            if save_output:        
-                newoutfile = "{0}.out".format(base)
-                
-                cwd = os.path.os.getcwd()
-                newoutfile = os.path.join(cwd,newoutfile)
-                
-                if os.path.isfile(newoutfile):
-                    os.remove(newoutfile)
-                    
-                os.rename(outfile, newoutfile)
-
-                if self.verbose: print "\nOutput data saved to '{0}'".format(newoutfile)
-            
-            if hasattr(self, "x2axis"):
-                [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:])
-            else:
-                [r.x,r.y,hdr] = self.readOutFile(outfile)
-            
-                r.xlabel = hdr[0]
-                r.ylabels = map(str.strip, hdr[1:])
-                            
-            if save_kat:
-                if kat_name == None:
-                    kat_name = "pykat_output"                
-                
-                cwd = os.path.os.getcwd()
-                newkatfile = os.path.join(cwd, kat_name + ".kat")
-                
-                if os.path.isfile(newkatfile):
-                    os.remove(newkatfile)
-                  
-                os.rename(katfile.name, newkatfile)         
-                
-                if self.verbose: print "Kat file saved to '{0}'".format(newkatfile)
-                
-
-            katfile.close()
-            perfData = []
-            
-            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])))
-                    
-                return [r, perfData]
-            else:
-                return r
+# -*- coding: utf-8 -*-
+"""
+Created on Sun Jan 27 09:56:53 2013
+
+PyKat - Python interface and wrapper for FINESSE
+Copyright (C) 2013 Daniel David Brown
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Contact at ddb@star.sr.bham.ac.uk
+
+@author: Daniel Brown
+"""
+import sys
+import os
+import subprocess
+import tempfile
+import numpy as np
+import datetime
+import pickle
+import pykat
+import warnings
+import re
+
+from pykat.node_network import NodeNetwork
+from pykat.detectors import Detector
+from pykat.components import Component
+from pykat.commands import Command, xaxis
+from pykat.gui.gui import pyKatGUI
+
+import pykat.exceptions as pkex
+
+from PyQt4.QtCore import QCoreApplication
+from PyQt4.QtGui import QApplication
+
+NO_GUI = False
+NO_BLOCK = "NO_BLOCK"
+
+class katRun(object):
+    def __init__(self):
+        self.runDateTime = datetime.datetime.now()
+        self.x = None
+        self.y = None
+        self.xlabel = None
+        self.ylabels = None
+        self.katScript = None
+        self.katVersion = None
+        
+    def saveKatRun(self, filename):
+        with open(filename,'w') as outfile:
+            pickle.dump(self, outfile)
+    
+    @staticmethod
+    def loadKatRun(filename):
+        with open(filename,'r') as infile:
+            return pickle.load(infile)
+    
+    def get(self, value): return self[value]
+    
+    def __getitem__(self, value):
+        if value in self.ylabels:
+            idx = self.ylabels.index(value)
+            if len(self.y.shape) == 1:
+                return self.y
+            else:
+                return self.y[:, idx]
+        else:
+            raise  pkex.BasePyKatException("No output by the name {0} found".format(value))
+      
+class katRun2D(object):
+    def __init__(self):
+        self.runDateTime = datetime.datetime.now()
+        self.x = None
+        self.y = None
+        self.z = None
+        self.xlabel = None
+        self.ylabel = None
+        self.zlabels = None
+        self.katScript = None
+        self.katVersion = None
+        
+    def saveKatRun(self, filename):
+        with open(filename,'w') as outfile:
+            pickle.dump(self, outfile)
+    
+    @staticmethod
+    def loadKatRun(filename):
+        with open(filename,'r') as infile:
+            return pickle.load(infile)
+    
+    def get(self, value): return self[value]
+    
+    def __getitem__(self, value):
+        idx = [i for i in range(len(self.zlabels)) if self.zlabels[i].split(" ")[0] == str(value)]
+        
+        if len(idx) > 0:
+            return self.z[idx].squeeze()
+        else:
+            raise  pkex.BasePyKatException("No output by the name {0} found".format(str(value)))
+      
+class Block:
+    def __init__(self, name):
+        self.__name = name
+        self.contents = [] # List of objects and strings of finesse code
+        self.enabled = True 
+        
+    @property
+    def name(self): return self.__name
+    
+class kat(object):                    
+        
+    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
+        self.__blocks = {} # dictionary of blocks that are used
+        self.__components = {}  # dictionary of optical components      
+        self.__detectors = {}   # dictionary of detectors
+        self.__commands = {}    # dictionary of commands
+        self.__extra_lines = [] # an array of strings which are just normal finesse code to include when running
+        self.__gui = None
+        self.nodes = NodeNetwork(self)  
+        self.__katdir = katdir
+        self.__katname = katname
+        self.__tempdir = tempdir
+        self.__tempname = tempname
+        self.pykatgui = None
+        
+        # Various options for running finesse, typicaly the commands with just 1 input
+        # and have no name attached to them.
+        self.__phase = None
+        self.__maxtem = None
+        self.__noxaxis = None
+        self.__time_code = None
+        
+        if kat_code != None and kat_file != None:
+            raise pkex.BasePyKatException("Specify either a Kat file or some Kat code, not both.")
+        
+        if kat_code != None:
+            self.parseCommands(kat_code)
+        
+        if kat_file != None:
+            self.loadKatFile(kat_file)
+        
+        cls = type(self)
+        self.__class__ = type(cls.__name__, (cls,), {})
+        
+    @property
+    def maxtem(self): return self.__maxtem
+    @maxtem.setter
+    def maxtem(self,value): self.__maxtem = int(value)
+    
+    @property
+    def phase(self): return self.__phase
+    @phase.setter
+    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)
+    
+    @property
+    def noxaxis(self): return self.__noxaxis
+    @noxaxis.setter
+    def noxaxis(self,value): self.__noxaxis = bool(value)
+
+    def logo(self):
+        print """                                              ..-
+    PyKat                 _                  '(
+                          \\`.|\\.__...-\"\"""-_." )
+       ..+-----.._        /  ' `            .-'
+   . '            `:      7/* _/._\\    \\   (
+  (        '::;;+;;:      `-"' =" /,`"" `) /
+  L.        \\`:::a:f            c_/     n_'
+  ..`--...___`.  .    ,  
+   `^-....____:   +."""
+    
+    def loadKatFile(self, katfile):
+        commands=open(katfile).read()
+        self.parseCommands(commands)
+    
+    def parseKatCode(self, code):
+        #commands = code.split("\n")
+        self.parseCommands(code)
+        
+    def parseCommands(self, commands):
+        blockComment = False
+
+        self.__currentTag= NO_BLOCK
+        
+        if not (NO_BLOCK in self.__blocks):
+            self.__blocks[NO_BLOCK] = Block(NO_BLOCK)
+        
+        commands=self.remove_comments(commands)
+        
+        after_process = [] # list of commands that should be processed after 
+                           # objects have been set and created
+        
+        for line in commands.split("\n"):
+            #for line in commands:
+            if len(line.strip()) >= 2:
+                line = line.strip()
+
+                # Looking for block start or end
+                values = line.split(" ")
+                if values[0] == "%%%":
+                    if values[1] == "FTblock":
+                        newTag = values[2]
+                        
+                        if self.__currentTag != None and newTag != self.__currentTag: 
+                            warnings.warn("found block {0} before block {1} ended".format(newTag, self.__currentTag))    
+                            
+                        if newTag in self.__blocks:
+                            raise pkex.BasePyKatException("Block `{0}` has already been read")
+                            
+                        self.__blocks[newTag] = Block(newTag) # create new list to store all references to components in block
+                        self.__currentTag = newTag                            
+                        
+                    if values[1] == "FTend":
+                        self.__currentTag = NO_BLOCK
+                        
+                    continue
+                #warnings.warn("current tag {0}".format(self.__currentTag))    
+
+                # don't read comment lines
+                if line[0] == "#" or line[0] == "%":
+                    continue
+                
+                # check if block comment is being used
+                if not blockComment and line[0:2] == "/*":
+                    blockComment = True
+                    continue
+                elif blockComment and line[0:2] == "*/":
+                    blockComment = False
+                    continue
+                
+                first = line.split(" ",1)[0]
+                obj = None
+                
+                if(first == "m" or first == "m1" or first == "m2"):
+                    obj = pykat.components.mirror.parseFinesseText(line)
+                elif(first == "s"):
+                    obj = pykat.components.space.parseFinesseText(line)
+                elif(first == "l"):
+                    obj = pykat.components.laser.parseFinesseText(line)
+                elif(first[0:2] == "bs"):
+                    obj = pykat.components.beamSplitter.parseFinesseText(line)
+                elif(first[0:2] == "gr"):
+                    obj = pykat.components.grating.parseFinesseText(line)
+                elif(first[0:4] == "isol"):
+                    obj = pykat.components.isolator.parseFinesseText(line)
+                elif(first[0:4] == "lens"):
+                    obj = pykat.components.lens.parseFinesseText(line)
+                elif(first[0:3] == "mod"):
+                    obj = pykat.components.modulator.parseFinesseText(line)
+                elif(first[0:2] == "pd"):
+                    obj = pykat.detectors.photodiode.parseFinesseText(line)
+                elif(first == "xaxis" or first == "xaxis*"):
+                    obj = pykat.commands.xaxis.parseFinesseText(line)
+                elif(first == "x2axis" or first == "x2axis*"):
+                    obj = pykat.commands.x2axis.parseFinesseText(line)
+                elif(first == "gauss" or first == "gauss*" or first == "gauss**"):
+                    after_process.append(line)
+                else:
+                    if self.verbose:
+                        print "Parsing `{0}` into pykat object not implemented yet, added as extra line.".format(line)
+                    obj = line
+                    # manually add the line to the block contents
+                    self.__blocks[self.__currentTag].contents.append(line) 
+                
+                if obj != None and not isinstance(obj, str):
+                    self.add(obj)
+                    
+        # now process all the varous gauss/attr etc. commands which require
+        # components to exist first before they can be processed
+        for line in after_process:
+            first = line.split(" ",1)[0]
+            
+            if first == "gauss" or first == "gauss*" or first == "gauss**":
+                pykat.commands.gauss.parseFinesseText(line)
+            
+        self.__currentTag = NO_BLOCK 
+
+    def saveScript(self, filename=None):
+        """
+        Saves the current kat object to a Finesse input file
+        """
+        try:
+            katScript = "".join(self.generateKatScript())       
+            katfile = open(filename,'w')
+            katfile.writelines(katScript)
+            katfile.flush()
+            katfile.close()
+
+        except pkex.BasePyKatException as ex:
+            print ex
+            
+    def run(self, printout=0, printerr=0, save_output=False, save_kat=False,kat_name=None) :
+        """ 
+        Runs the current simulation setup that has been built thus far.
+        It returns a katRun or katRun2D object which is populated with the various
+        data from the simulation run.
+        """
+        start = datetime.datetime.now()
+
+        
+        try:        
+            if not hasattr(self, "xaxis") and self.noxaxis != None and self.noxaxis == False:
+                raise pkex.BasePyKatException("No xaxis was defined")
+            
+            if len(self.__katdir) == 0:
+                # Get the environment variable for where Finesse is stored
+                self.__finesse_dir = os.environ.get('FINESSE_DIR')
+                
+                if self.__finesse_dir == None :
+                    raise MissingFinesseEnvVar()
+            else:
+                self.__finesse_dir = self.__katdir
+                
+            if len(self.__katname) == 0:
+                katexe = "kat"
+                
+                if os.sys.platform == "win32":
+                    katexe += ".exe"
+            else:
+                katexe = self.__katname
+            
+            kat_exec = os.path.join(self.__finesse_dir, katexe) 
+            
+            # check if kat file exists and it is executable by user        
+            if not (os.path.isfile(kat_exec) and os.access(kat_exec, os.X_OK)):
+                raise MissingFinesse()
+                
+            if self.verbose: print "--------------------------------------------------------------"
+            if self.verbose: print "Running kat - Started at " + str(start)
+            
+            if hasattr(self, "x2axis"):
+                r = katRun2D()
+            else:
+                r = katRun()
+                
+            r.katScript = "".join(self.generateKatScript())   
+            
+            # create a kat file which we will write the script into
+            if self.__tempname == None:
+                katfile = tempfile.NamedTemporaryFile(suffix=".kat", dir=self.__tempdir)
+            else:
+                filepath =os.path.join(self.__tempdir, self.__tempname+".kat" )
+                katfile = open( filepath, 'w' ) 
+                
+            katfile.writelines(r.katScript)
+            katfile.flush()
+            
+            cmd=[kat_exec, '--perl1']
+            
+            if self.__time_code:
+                cmd.append('--perf-timing')
+
+            cmd.append('--no-backspace')
+            # set default format so that less repeated numbers are printed to the
+            # output file, should speed up running and parsing of output files
+            cmd.append('-format=%.15g')
+
+            cmd.append(katfile.name)
+            
+            #if self.verbose:
+                #print cmd
+                
+            p=subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+            err = ""
+            
+            if self.verbose: print "Finesse binary output:"
+            
+            for line in iter(p.stderr.readline, ""):
+                #err += line 
+                
+                if len(line) > 0:
+                    if line.rstrip().endswith('%'):
+                        vals = line.split("-")
+                        action = vals[0].strip()
+                        prc = vals[1].strip()[:-1]
+                        sys.stdout.write("\r{0} {1}%".format(action, prc))
+                    elif line[0] == '*':
+                        sys.stdout.write(line)
+                        
+            [out,errpipe] = p.communicate()
+            
+            # get the version number
+            ix = out.find('build ') + 6
+            ix2 = out.find(')',ix)
+            r.katVersion = out[ix:ix2]
+            
+            r.runDateTime = datetime.datetime.now()
+            
+            if p.returncode != 0:
+                raise pkex.FinesseRunError(err, katfile.name)
+            
+            if printout == 1: print out
+            if printerr == 1: print err
+
+            root = os.path.splitext(katfile.name)
+            base = os.path.basename(root[0])            
+            outfile = root[0] + ".out"
+            
+            if save_output:        
+                newoutfile = "{0}.out".format(base)
+                
+                cwd = os.path.os.getcwd()
+                newoutfile = os.path.join(cwd,newoutfile)
+                
+                if os.path.isfile(newoutfile):
+                    os.remove(newoutfile)
+                    
+                os.rename(outfile, newoutfile)
+
+                if self.verbose: print "\nOutput data saved to '{0}'".format(newoutfile)
+            
+            if hasattr(self, "x2axis"):
+                [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:])
+            else:
+                [r.x,r.y,hdr] = self.readOutFile(outfile)
+            
+                r.xlabel = hdr[0]
+                r.ylabels = map(str.strip, hdr[1:])
+                            
+            if save_kat:
+                if kat_name == None:
+                    kat_name = "pykat_output"                
+                
+                cwd = os.path.os.getcwd()
+                newkatfile = os.path.join(cwd, kat_name + ".kat")
+                
+                if os.path.isfile(newkatfile):
+                    os.remove(newkatfile)
+                  
+                os.rename(katfile.name, newkatfile)         
+                
+                if self.verbose: print "Kat file saved to '{0}'".format(newkatfile)
+                
+
+            katfile.close()
+            perfData = []
+            
+            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])))
+                    
+                return [r, perfData]
+            else:
+                return r
             
         except pkex.FinesseRunError as fe:
-            print fe
-        finally:
-            if self.verbose: print ""
-            if self.verbose: print "Finished in " + str(datetime.datetime.now()-start)
-            
-        
-    def add(self, obj):
-        try:
-            obj.tag = self.__currentTag
-            self.__blocks[self.__currentTag].contents.append(obj)
-            
-            if isinstance(obj, Component):
-                
-                if obj.name in self.__components :
-                    raise pkex.BasePyKatException("A component with name '{0}' has already been added".format([obj.name]))            
-                            
-                self.__components[obj.name] = obj
-                self.__add_component(obj)
-                
-            elif isinstance(obj, Detector):
-                
-                if obj.name in self.__detectors :
-                        raise pkex.BasePyKatException("A detector '{0}' has already been added".format(obj.name))
-                        
-                self.__detectors[obj.name] = obj
-                self.__add_detector(obj)
-                
-            elif isinstance(obj, Command):
-                
-                self.__commands[obj.__class__.__name__] = obj
-                self.__add_command(obj)
-                
-            else :
-                raise pkex.BasePyKatException("Object {0} could not be added".format(obj))
-                
-            obj._on_kat_add(self)
-            
-        except pkex.BasePyKatException as ex:
-            print ex
-
-    def readOutFile(self, filename):
-        
-        with open(filename,'r') as outfile:
-            # read first to lines to get to header line
-            outfile.readline()
-            outfile.readline()
-            
-            hdr = outfile.readline().replace('%','').replace('\n','').split(',')
-        
-        data = np.loadtxt(filename,comments='%',skiprows=4)
-        
-        if hasattr(self, "x2axis"):
-            # 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]
-            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)
-            # once you do this the data for y and x axes need swapping
-            z = z.swapaxes(1,2)
-            return [x, y, z, hdr]
-        else:
-            shape_len = len(data.shape)
-            
-            if shape_len > 1:
-                rows,cols = data.shape
-                x = data[:,0]
-                y = data[:,1:cols].squeeze()
-            else:
-                rows = 1
-                cols = data.shape[0]
-                
-                x = data[0]
-                y = data[1:cols].squeeze()
-            
-            return [x, y, hdr]
-            
-    def generateKatScript(self) :
-        """ Generates the kat file which can then be run """
-        
-        out = []    
-        
-        for key in self.__blocks:
-            objs = self.__blocks[key].contents
-            
-            out.append("%%% FTblock " + key + "\n")
-            
-            for obj in objs:
-                if isinstance(obj, str):
-                    out.append(obj + '\n')
-                    
-                elif isinstance(obj, Component) or isinstance(obj, Detector) or isinstance(obj, Command):
-                    txt = obj.getFinesseText() 
-                    
-                    if txt != None:
-                        if isinstance(txt,list):
-                            for t in txt:
-                                out.append(t + "\n")
-                        else:
-                            out.append(txt + "\n")
-                            
-            out.append("%%% FTend " + key + "\n")
-        
-        if self.noxaxis != None and self.noxaxis == True:
-            out.append("noxaxis\n")
-            
-        # now loop through all the nodes and get any gauss commands
-        for key in self.nodes.getNodes():
-            txt = self.nodes.getNodes()[key].getFinesseText()
-            
-            if txt != None:
-                if isinstance(txt,list):
-                    for t in txt: out.append(t+ "\n")
-                else:
-                    out.append(txt + "\n")
-        
-        if self.phase != None: out.append("phase {0}\n".format(self.phase))
-        if self.maxtem != None: out.append("maxtem {0}\n".format(self.maxtem))            
-
-        # ensure we don't do any plotting. That should be handled
-        # by user themselves
-        out.append("gnuterm no\n")
-        out.append("pyterm no\n")
-        
-        return out
-        
-    def openGUI(self):
-        if NO_GUI:
-            print  "No PyQt4 module was installed so cannot open a GUI"
-        else:
-            self.app = QCoreApplication.instance() 
-            created = False
-            
-            if self.app == None:
-                created = True
-                self.app = QApplication([""])
-                
-            if self.pykatgui == None:
-                self.pykatgui = pyKatGUI(self)
-                self.pykatgui.main()
-            else:
-                self.pykatgui.show()
-                
-            if created: self.app.exec_()
-    
-    def getComponents(self):
-        return self.__components.values()
-    
-    def hasComponent(self, name):
-        return (name in self.__components)
-    
-    def _newName(self, container, prefix):
-        n = 1
-        name = "{0}{1}".format(prefix, n)
-        
-        while name in container:
-            n += 1
-            name = "{0}{1}".format(prefix,n)
-        
-        return name
-    
-    def getNewComponentName(self,prefix):
-        '''
-        Returns a name for a component which hasn't already been added.
-        Returns [prefix] + number, where number is greater than 1. e.g.
-        if m1 exists getNewName('m') will return 'm2'
-        '''
-        return self._newName(self.__components, prefix)
-    
-    def getNewDetectorName(self,prefix):
-        '''
-        Returns a name for a component which hasn't already been added.
-        Returns [prefix] + number, where number is greater than 1. e.g.
-        if m1 exists getNewName('m') will return 'm2'
-        '''
-        return self._newName(self.__detectors, prefix)
-        
-    def getNewNodeNames(self,prefix,N=1):
-        '''
-        Returns a list of names for N number of nodes which haven't already been added.
-        Returns [prefix] + number, where number is greater than 1. e.g.
-        if m1 exists getNewName('m') will return 'm2'
-        '''
-        rtn = []
-        n = 1
-        
-        for M in range(1,N+1):
-            name = "{0}{1}".format(prefix, n)
-            
-            while name in self.nodes.getNodes() or (name in rtn):
-                n += 1
-                name = "{0}{1}".format(prefix,n)
-        
-            rtn.append(name)
-            
-        return rtn
-        
-    
-    def __add_detector(self, det):
-
-        if not isinstance(det, Detector):
-            raise exceptions.ValueError("Argument is not of type Detector")
-        
-        name = det.name
-        fget = lambda self: self.__get_detector(name)
-        
-        setattr(self.__class__, name, property(fget))
-        setattr(self, '__det_' + name, det)                   
-
-    def __get_detector(self, name):
-        return getattr(self, '__det_' + name) 
-        
-    def __add_command(self, com):
-
-        if not isinstance(com, Command):
-            raise exceptions.ValueError("Argument is not of type Command")
-        
-        name = com.__class__.__name__
-        fget = lambda self: self.__get_command(name)
-        
-        setattr(self.__class__, name, property(fget))
-        setattr(self, '__com_' + name, com)                   
-
-    def __get_command(self, name):
-        return getattr(self, '__com_' + name)            
-    
-    def __add_component(self, comp):
-
-        if not isinstance(comp, Component):
-            raise exceptions.ValueError("Argument is not of type Component")
-            
-        fget = lambda self: self.__get_component(comp.name)
-        
-        setattr(self.__class__, comp.name, property(fget))
-        setattr(self, '__comp_' + comp.name, comp)                   
-
-    def __get_component(self, name):
-        return getattr(self, '__comp_' + name)        
-
-    def remove_comments(self, string):
-        pattern = r"(\".*?\"|\'.*?\'|%{3}[^\r\n]*$)|(/\*.*?\*/|%[^\r\n]*$|#[^\r\n]*$|//[^\r\n]*$)"
-        # first group captures quoted strings (double or single)
-        # second group captures comments (//single-line or /* multi-line */)
-        regex = re.compile(pattern, re.MULTILINE|re.DOTALL)
-        def _replacer(match):
-            # if the 2nd group (capturing comments) is not None,
-            # it means we have captured a non-quoted (real) comment string.
-            if match.group(2) is not None:
-                return "" # so we will return empty to remove the comment
-            else: # otherwise, we will return the 1st group
-                return match.group(1) # captured quoted-string
-        return regex.sub(_replacer, string)
+            print fe
+        finally:
+            if self.verbose: print ""
+            if self.verbose: print "Finished in " + str(datetime.datetime.now()-start)
+            
+        
+    def add(self, obj):
+        try:
+            obj.tag = self.__currentTag
+            self.__blocks[self.__currentTag].contents.append(obj)
+            
+            if isinstance(obj, Component):
+                
+                if obj.name in self.__components :
+                    raise pkex.BasePyKatException("A component with name '{0}' has already been added".format([obj.name]))            
+                            
+                self.__components[obj.name] = obj
+                self.__add_component(obj)
+                
+            elif isinstance(obj, Detector):
+                
+                if obj.name in self.__detectors :
+                        raise pkex.BasePyKatException("A detector '{0}' has already been added".format(obj.name))
+                        
+                self.__detectors[obj.name] = obj
+                self.__add_detector(obj)
+                
+            elif isinstance(obj, Command):
+                
+                self.__commands[obj.__class__.__name__] = obj
+                self.__add_command(obj)
+                
+            else :
+                raise pkex.BasePyKatException("Object {0} could not be added".format(obj))
+                
+            obj._on_kat_add(self)
+            
+        except pkex.BasePyKatException as ex:
+            print ex
+
+    def readOutFile(self, filename):
+        
+        with open(filename,'r') as outfile:
+            # read first to lines to get to header line
+            outfile.readline()
+            outfile.readline()
+            
+            hdr = outfile.readline().replace('%','').replace('\n','').split(',')
+        
+        data = np.loadtxt(filename,comments='%',skiprows=4)
+        
+        if hasattr(self, "x2axis"):
+            # 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]
+            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)
+            # once you do this the data for y and x axes need swapping
+            z = z.swapaxes(1,2)
+            return [x, y, z, hdr]
+        else:
+            shape_len = len(data.shape)
+            
+            if shape_len > 1:
+                rows,cols = data.shape
+                x = data[:,0]
+                y = data[:,1:cols].squeeze()
+            else:
+                rows = 1
+                cols = data.shape[0]
+                
+                x = data[0]
+                y = data[1:cols].squeeze()
+            
+            return [x, y, hdr]
+            
+    def generateKatScript(self) :
+        """ Generates the kat file which can then be run """
+        
+        out = []    
+        
+        for key in self.__blocks:
+            objs = self.__blocks[key].contents
+            
+            out.append("%%% FTblock " + key + "\n")
+            
+            for obj in objs:
+                if isinstance(obj, str):
+                    out.append(obj + '\n')
+                    
+                elif isinstance(obj, Component) or isinstance(obj, Detector) or isinstance(obj, Command):
+                    txt = obj.getFinesseText() 
+                    
+                    if txt != None:
+                        if isinstance(txt,list):
+                            for t in txt:
+                                out.append(t + "\n")
+                        else:
+                            out.append(txt + "\n")
+                            
+            out.append("%%% FTend " + key + "\n")
+        
+        if self.noxaxis != None and self.noxaxis == True:
+            out.append("noxaxis\n")
+            
+        # now loop through all the nodes and get any gauss commands
+        for key in self.nodes.getNodes():
+            txt = self.nodes.getNodes()[key].getFinesseText()
+            
+            if txt != None:
+                if isinstance(txt,list):
+                    for t in txt: out.append(t+ "\n")
+                else:
+                    out.append(txt + "\n")
+        
+        if self.phase != None: out.append("phase {0}\n".format(self.phase))
+        if self.maxtem != None: out.append("maxtem {0}\n".format(self.maxtem))            
+
+        # ensure we don't do any plotting. That should be handled
+        # by user themselves
+        out.append("gnuterm no\n")
+        out.append("pyterm no\n")
+        
+        return out
+        
+    def openGUI(self):
+        if NO_GUI:
+            print  "No PyQt4 module was installed so cannot open a GUI"
+        else:
+            self.app = QCoreApplication.instance() 
+            created = False
+            
+            if self.app == None:
+                created = True
+                self.app = QApplication([""])
+                
+            if self.pykatgui == None:
+                self.pykatgui = pyKatGUI(self)
+                self.pykatgui.main()
+            else:
+                self.pykatgui.show()
+                
+            if created: self.app.exec_()
+    
+    def getComponents(self):
+        return self.__components.values()
+    
+    def hasComponent(self, name):
+        return (name in self.__components)
+    
+    def _newName(self, container, prefix):
+        n = 1
+        name = "{0}{1}".format(prefix, n)
+        
+        while name in container:
+            n += 1
+            name = "{0}{1}".format(prefix,n)
+        
+        return name
+    
+    def getNewComponentName(self,prefix):
+        '''
+        Returns a name for a component which hasn't already been added.
+        Returns [prefix] + number, where number is greater than 1. e.g.
+        if m1 exists getNewName('m') will return 'm2'
+        '''
+        return self._newName(self.__components, prefix)
+    
+    def getNewDetectorName(self,prefix):
+        '''
+        Returns a name for a component which hasn't already been added.
+        Returns [prefix] + number, where number is greater than 1. e.g.
+        if m1 exists getNewName('m') will return 'm2'
+        '''
+        return self._newName(self.__detectors, prefix)
+        
+    def getNewNodeNames(self,prefix,N=1):
+        '''
+        Returns a list of names for N number of nodes which haven't already been added.
+        Returns [prefix] + number, where number is greater than 1. e.g.
+        if m1 exists getNewName('m') will return 'm2'
+        '''
+        rtn = []
+        n = 1
+        
+        for M in range(1,N+1):
+            name = "{0}{1}".format(prefix, n)
+            
+            while name in self.nodes.getNodes() or (name in rtn):
+                n += 1
+                name = "{0}{1}".format(prefix,n)
+        
+            rtn.append(name)
+            
+        return rtn
+        
+    
+    def __add_detector(self, det):
+
+        if not isinstance(det, Detector):
+            raise exceptions.ValueError("Argument is not of type Detector")
+        
+        name = det.name
+        fget = lambda self: self.__get_detector(name)
+        
+        setattr(self.__class__, name, property(fget))
+        setattr(self, '__det_' + name, det)                   
+
+    def __get_detector(self, name):
+        return getattr(self, '__det_' + name) 
+        
+    def __add_command(self, com):
+
+        if not isinstance(com, Command):
+            raise exceptions.ValueError("Argument is not of type Command")
+        
+        name = com.__class__.__name__
+        fget = lambda self: self.__get_command(name)
+        
+        setattr(self.__class__, name, property(fget))
+        setattr(self, '__com_' + name, com)                   
+
+    def __get_command(self, name):
+        return getattr(self, '__com_' + name)            
+    
+    def __add_component(self, comp):
+
+        if not isinstance(comp, Component):
+            raise exceptions.ValueError("Argument is not of type Component")
+            
+        fget = lambda self: self.__get_component(comp.name)
+        
+        setattr(self.__class__, comp.name, property(fget))
+        setattr(self, '__comp_' + comp.name, comp)                   
+
+    def __get_component(self, name):
+        return getattr(self, '__comp_' + name)        
+
+    def remove_comments(self, string):
+        pattern = r"(\".*?\"|\'.*?\'|%{3}[^\r\n]*$)|(/\*.*?\*/|%[^\r\n]*$|#[^\r\n]*$|//[^\r\n]*$)"
+        # first group captures quoted strings (double or single)
+        # second group captures comments (//single-line or /* multi-line */)
+        regex = re.compile(pattern, re.MULTILINE|re.DOTALL)
+        def _replacer(match):
+            # if the 2nd group (capturing comments) is not None,
+            # it means we have captured a non-quoted (real) comment string.
+            if match.group(2) is not None:
+                return "" # so we will return empty to remove the comment
+            else: # otherwise, we will return the 1st group
+                return match.group(1) # captured quoted-string
+        return regex.sub(_replacer, string)
diff --git a/pykat/gui/graphics.py b/pykat/gui/graphics.py
index 8be038975c82d2cff493768b67aa3ac8c933f385..6b445defea3f529c42d8aa2d303878bf1aa0d499 100644
--- a/pykat/gui/graphics.py
+++ b/pykat/gui/graphics.py
@@ -1,191 +1,191 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Fri Feb 01 09:13:03 2013
-
-@author: Daniel
-"""
-
-from PyQt4.QtGui import *
-from PyQt4.Qt import *
-from PyQt4 import QtSvg
-from PyQt4.QtSvg import QGraphicsSvgItem
-import pykat.components
-import exceptions
-
-nsize = 10
-    
-class NodeQGraphicItem(QGraphicsRectItem):
-    
-    def __init__(self, node, x,y, *args, **kwargs):
-        QGraphicsRectItem.__init__(self, *args, **kwargs)
-        self.__node = node
-        self.setPos(x,y)
-        
-        item = QGraphicsTextItem(node.name, self)
-        rect = item.boundingRect()       
-        item.setPos(-0.5*rect.width(), 0)
-        
-        self.setAcceptHoverEvents(True)
-        
-        self.marked = False
-        
-    @property
-    def node(self): return self.__node
-        
-    def refresh(self):
-        if not self.marked:
-            if self.__node.isConnected():
-                self.setBrush(QBrush(Qt.red))
-            else:
-                self.setBrush(QBrush(Qt.green))
-        else:
-            self.setBrush(QBrush(Qt.yellow))
-            
-class SpaceQGraphicsItem(QGraphicsLineItem):
-    def __init__(self, spaceComponent):
-        QGraphicsLineItem.__init__(self)
-        self.__n1 = None
-        self.__n2 = None
-        self.__space = spaceComponent
-    
-        item = QGraphicsTextItem(self.__space.name, self)
-        rect = item.boundingRect()       
-        item.setPos(-0.5*rect.width(),0*rect.height())
-    
-        self.refresh()
-        
-    @property
-    def space(self): return self.__space
-    
-    def refresh(self):
-        nodes = self.__space.nodes
-                
-        conn = nodes[0].amIConnected(self.__space)
-        
-        x1 = 0
-        y1 = 0
-        x2 = 0
-        y2 = 0
-                
-        if conn[0]:
-            if conn[1] != None:
-                if self.__n1 is not None:
-                    # i.e. we have a node graphic item but now it is connected to something
-                    self.__n1.scene().removeItem(self.__n1)
-                    self.__n1 = None
-                    
-                # now check if a connected component was returned too
-                if conn[1] != None:
-                    # so this node should be attached to something
-                    # in this case we get the position of their node 
-                    # and draw the the line from their
-                    itm=conn[1].getQGraphicsItem()
-                    x1 = itm.x() + itm.nodedx[conn[2]][0]
-                    y1 = itm.y() + itm.nodedx[conn[2]][1]
-            else:
-                if self.__n1 == None:
-                    self.__n1 = NodeQGraphicItem(nodes[0],0,0,-nsize/2,-nsize/2,nsize,nsize,self)
-                    self.__n1.setPen(QPen(Qt.black,1))
-                    
-                self.__n1.setVisible(True)
-                self.__n1.setBrush(QBrush(Qt.green))
-                p = self.__n1.pos()
-                x1 = self.x()+p.x()
-                y1 = self.y()+p.y()
-                
-        conn = nodes[1].amIConnected(self.__space)
-        
-        if conn[0]:
-            if conn[1] != None:
-                
-                if self.__n2 is not None:
-                    # i.e. we have a node graphic item but now it is connected to something
-                    self.__n2.scene().removeItem(self.__n2)
-                    self.__n2 = None
-                    
-                # now check if a connected component was returned too
-                if conn[1] != None:
-                    # so this node should be attached to something
-                    # in this case we get the position of their node 
-                    # and draw the the line from their
-                    itm=conn[1].getQGraphicsItem()
-                    x2 = itm.x() + itm.nodedx[conn[2]][0]
-                    y2 = itm.y() + itm.nodedx[conn[2]][1]
-            else:
-                if self.__n2 == None:
-                    self.__n2 = NodeQGraphicItem(nodes[1],0,0,-nsize/2,-nsize/2,nsize,nsize,self)
-                    self.__n2.setPen(QPen(Qt.black,1))
-                    
-                self.__n2.setVisible(True)
-                self.__n2.setBrush(QBrush(Qt.green))
-                p = self.__n2.pos()
-                x2 = self.x()+p.x()
-                y2 = self.y()+p.y()
-        
-        # convert x1,y1,x2 and y2 into the local coordinates of the 
-        # space object
-        p = QPointF((x1-x2)*0.5,(y1-y2)*0.5)
-        self.setPos(x1 - p.x(), y1 - p.y())
-        
-        # if the nodes are visible then reposition them in the 
-        # component reference frame
-        if self.__n1 is not None and self.__n1.isVisible():
-            self.__n1.setPos(QPointF(p.x(),p.y()))
-            self.__n1.refresh()
-        
-        if self.__n2 is not None and self.__n2.isVisible():
-            self.__n2.setPos(QPointF(p.x()+x2-x1, p.y()+y2-y1))
-            self.__n2.refresh()
-            
-        self.setLine(p.x(), p.y(), p.x()+x2-x1, p.y()+y2-y1)
-        self.setPen(QPen(Qt.red, 3))
-        
-    
-class ComponentQGraphicsItem(QtSvg.QGraphicsSvgItem):
-    
-    def __init__(self, svgfile, component, nodes):
-        QGraphicsSvgItem.__init__(self,svgfile)
-        self.__nodeGraphics = []
-        self.__component = component
-        # this signals the itemChange() method when this item is moved
-        # used for refreshing the spaces between components
-        self.setFlags(QGraphicsItem.ItemSendsGeometryChanges)
-        self.nodedx = [] # stores the node square offsets
-                
-        item = QGraphicsTextItem(component.name,self)
-        rect = item.boundingRect()       
-        item.setPos(-0.5*rect.width(),40-0.5*rect.height())
-        
-        self.setAcceptsHoverEvents(True)
-        
-        for n in nodes:
-            self.nodedx.append([n[0],n[1]])
-            node = n[2].getQGraphicsItem(n[0],n[1],nsize,self)
-            node.setPen(QPen(Qt.black))
-            node.refresh()
-            self.__nodeGraphics.append(node)
-            
-        self.refresh()
-        self.installEventFilter(self)
-        self.setHandlesChildEvents(True)
-             
-    @property
-    def component(self): return self.__component
-    
-    def refresh(self):
-        for n in self.__nodeGraphics:
-            n.refresh()
-        
-    def itemChange(self, change, value):
-        # if the item is moved then update any spaces attached to it
-        if change == QGraphicsItem.ItemPositionHasChanged:
-            nodes = self.__component.nodes
-            
-            for n in nodes:
-                conn = n.amIConnected(self.__component)
-                
-                if conn[0] and isinstance(conn[1],  pykat.components.space):
-                    conn[1].getQGraphicsItem().refresh()
-                   
-        return QGraphicsSvgItem.itemChange(self, change, value)
-            
+# -*- coding: utf-8 -*-
+"""
+Created on Fri Feb 01 09:13:03 2013
+
+@author: Daniel
+"""
+
+from PyQt4.QtGui import *
+from PyQt4.Qt import *
+from PyQt4 import QtSvg
+from PyQt4.QtSvg import QGraphicsSvgItem
+import pykat.components
+import exceptions
+
+nsize = 10
+    
+class NodeQGraphicItem(QGraphicsRectItem):
+    
+    def __init__(self, node, x,y, *args, **kwargs):
+        QGraphicsRectItem.__init__(self, *args, **kwargs)
+        self.__node = node
+        self.setPos(x,y)
+        
+        item = QGraphicsTextItem(node.name, self)
+        rect = item.boundingRect()       
+        item.setPos(-0.5*rect.width(), 0)
+        
+        self.setAcceptHoverEvents(True)
+        
+        self.marked = False
+        
+    @property
+    def node(self): return self.__node
+        
+    def refresh(self):
+        if not self.marked:
+            if self.__node.isConnected():
+                self.setBrush(QBrush(Qt.red))
+            else:
+                self.setBrush(QBrush(Qt.green))
+        else:
+            self.setBrush(QBrush(Qt.yellow))
+            
+class SpaceQGraphicsItem(QGraphicsLineItem):
+    def __init__(self, spaceComponent):
+        QGraphicsLineItem.__init__(self)
+        self.__n1 = None
+        self.__n2 = None
+        self.__space = spaceComponent
+    
+        item = QGraphicsTextItem(self.__space.name, self)
+        rect = item.boundingRect()       
+        item.setPos(-0.5*rect.width(),0*rect.height())
+    
+        self.refresh()
+        
+    @property
+    def space(self): return self.__space
+    
+    def refresh(self):
+        nodes = self.__space.nodes
+                
+        conn = nodes[0].amIConnected(self.__space)
+        
+        x1 = 0
+        y1 = 0
+        x2 = 0
+        y2 = 0
+                
+        if conn[0]:
+            if conn[1] != None:
+                if self.__n1 is not None:
+                    # i.e. we have a node graphic item but now it is connected to something
+                    self.__n1.scene().removeItem(self.__n1)
+                    self.__n1 = None
+                    
+                # now check if a connected component was returned too
+                if conn[1] != None:
+                    # so this node should be attached to something
+                    # in this case we get the position of their node 
+                    # and draw the the line from their
+                    itm=conn[1].getQGraphicsItem()
+                    x1 = itm.x() + itm.nodedx[conn[2]][0]
+                    y1 = itm.y() + itm.nodedx[conn[2]][1]
+            else:
+                if self.__n1 == None:
+                    self.__n1 = NodeQGraphicItem(nodes[0],0,0,-nsize/2,-nsize/2,nsize,nsize,self)
+                    self.__n1.setPen(QPen(Qt.black,1))
+                    
+                self.__n1.setVisible(True)
+                self.__n1.setBrush(QBrush(Qt.green))
+                p = self.__n1.pos()
+                x1 = self.x()+p.x()
+                y1 = self.y()+p.y()
+                
+        conn = nodes[1].amIConnected(self.__space)
+        
+        if conn[0]:
+            if conn[1] != None:
+                
+                if self.__n2 is not None:
+                    # i.e. we have a node graphic item but now it is connected to something
+                    self.__n2.scene().removeItem(self.__n2)
+                    self.__n2 = None
+                    
+                # now check if a connected component was returned too
+                if conn[1] != None:
+                    # so this node should be attached to something
+                    # in this case we get the position of their node 
+                    # and draw the the line from their
+                    itm=conn[1].getQGraphicsItem()
+                    x2 = itm.x() + itm.nodedx[conn[2]][0]
+                    y2 = itm.y() + itm.nodedx[conn[2]][1]
+            else:
+                if self.__n2 == None:
+                    self.__n2 = NodeQGraphicItem(nodes[1],0,0,-nsize/2,-nsize/2,nsize,nsize,self)
+                    self.__n2.setPen(QPen(Qt.black,1))
+                    
+                self.__n2.setVisible(True)
+                self.__n2.setBrush(QBrush(Qt.green))
+                p = self.__n2.pos()
+                x2 = self.x()+p.x()
+                y2 = self.y()+p.y()
+        
+        # convert x1,y1,x2 and y2 into the local coordinates of the 
+        # space object
+        p = QPointF((x1-x2)*0.5,(y1-y2)*0.5)
+        self.setPos(x1 - p.x(), y1 - p.y())
+        
+        # if the nodes are visible then reposition them in the 
+        # component reference frame
+        if self.__n1 is not None and self.__n1.isVisible():
+            self.__n1.setPos(QPointF(p.x(),p.y()))
+            self.__n1.refresh()
+        
+        if self.__n2 is not None and self.__n2.isVisible():
+            self.__n2.setPos(QPointF(p.x()+x2-x1, p.y()+y2-y1))
+            self.__n2.refresh()
+            
+        self.setLine(p.x(), p.y(), p.x()+x2-x1, p.y()+y2-y1)
+        self.setPen(QPen(Qt.red, 3))
+        
+    
+class ComponentQGraphicsItem(QtSvg.QGraphicsSvgItem):
+    
+    def __init__(self, svgfile, component, nodes):
+        QGraphicsSvgItem.__init__(self,svgfile)
+        self.__nodeGraphics = []
+        self.__component = component
+        # this signals the itemChange() method when this item is moved
+        # used for refreshing the spaces between components
+        self.setFlags(QGraphicsItem.ItemSendsGeometryChanges)
+        self.nodedx = [] # stores the node square offsets
+                
+        item = QGraphicsTextItem(component.name,self)
+        rect = item.boundingRect()       
+        item.setPos(-0.5*rect.width(),40-0.5*rect.height())
+        
+        self.setAcceptsHoverEvents(True)
+        
+        for n in nodes:
+            self.nodedx.append([n[0],n[1]])
+            node = n[2].getQGraphicsItem(n[0],n[1],nsize,self)
+            node.setPen(QPen(Qt.black))
+            node.refresh()
+            self.__nodeGraphics.append(node)
+            
+        self.refresh()
+        self.installEventFilter(self)
+        self.setHandlesChildEvents(True)
+             
+    @property
+    def component(self): return self.__component
+    
+    def refresh(self):
+        for n in self.__nodeGraphics:
+            n.refresh()
+        
+    def itemChange(self, change, value):
+        # if the item is moved then update any spaces attached to it
+        if change == QGraphicsItem.ItemPositionHasChanged:
+            nodes = self.__component.nodes
+            
+            for n in nodes:
+                conn = n.amIConnected(self.__component)
+                
+                if conn[0] and isinstance(conn[1],  pykat.components.space):
+                    conn[1].getQGraphicsItem().refresh()
+                   
+        return QGraphicsSvgItem.itemChange(self, change, value)
+            
diff --git a/pykat/gui/qt_gui.py b/pykat/gui/qt_gui.py
index deeecfa02dbb8912b7d2bdb2092c549dc2210805..68dd68ef67ecbfebd67f061f374a52c484025189 100644
--- a/pykat/gui/qt_gui.py
+++ b/pykat/gui/qt_gui.py
@@ -1,78 +1,78 @@
-# -*- coding: utf-8 -*-
-
-# Form implementation generated from reading ui file 'qt_gui.ui'
-#
-# Created: Sat Feb 09 17:34:29 2013
-#      by: PyQt4 UI code generator 4.9.5
-#
-# WARNING! All changes made in this file will be lost!
-
-from PyQt4 import QtCore, QtGui
-#from pykat.gui.gui import pyKatGraphicsView
-
-try:
-    _fromUtf8 = QtCore.QString.fromUtf8
-except AttributeError:
-    _fromUtf8 = lambda s: s
-
-class Ui_MainWindow(object):
-    def setupUi(self, MainWindow):
-        MainWindow.setObjectName(_fromUtf8("MainWindow"))
-        MainWindow.resize(833, 614)
-        self.centralwidget = QtGui.QWidget(MainWindow)
-        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred)
-        sizePolicy.setHorizontalStretch(0)
-        sizePolicy.setVerticalStretch(0)
-        sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
-        self.centralwidget.setSizePolicy(sizePolicy)
-        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
-        self.gridLayout = QtGui.QGridLayout(self.centralwidget)
-        self.gridLayout.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint)
-        self.gridLayout.setMargin(2)
-        self.gridLayout.setSpacing(5)
-        self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
-        MainWindow.setCentralWidget(self.centralwidget)
-        self.menubar = QtGui.QMenuBar(MainWindow)
-        self.menubar.setGeometry(QtCore.QRect(0, 0, 833, 26))
-        self.menubar.setObjectName(_fromUtf8("menubar"))
-        self.menuFile = QtGui.QMenu(self.menubar)
-        self.menuFile.setObjectName(_fromUtf8("menuFile"))
-        self.menuAbout = QtGui.QMenu(self.menubar)
-        self.menuAbout.setObjectName(_fromUtf8("menuAbout"))
-        MainWindow.setMenuBar(self.menubar)
-        self.statusbar = QtGui.QStatusBar(MainWindow)
-        self.statusbar.setObjectName(_fromUtf8("statusbar"))
-        MainWindow.setStatusBar(self.statusbar)
-        self.actionClose = QtGui.QAction(MainWindow)
-        self.actionClose.setObjectName(_fromUtf8("actionClose"))
-        self.actionSave = QtGui.QAction(MainWindow)
-        self.actionSave.setObjectName(_fromUtf8("actionSave"))
-        self.actionOpen = QtGui.QAction(MainWindow)
-        self.actionOpen.setObjectName(_fromUtf8("actionOpen"))
-        self.actionHelp = QtGui.QAction(MainWindow)
-        self.actionHelp.setObjectName(_fromUtf8("actionHelp"))
-        self.actionExport_to_SVG = QtGui.QAction(MainWindow)
-        self.actionExport_to_SVG.setObjectName(_fromUtf8("actionExport_to_SVG"))
-        self.menuFile.addAction(self.actionSave)
-        self.menuFile.addAction(self.actionOpen)
-        self.menuFile.addSeparator()
-        self.menuFile.addAction(self.actionExport_to_SVG)
-        self.menuFile.addSeparator()
-        self.menuFile.addAction(self.actionClose)
-        self.menuAbout.addAction(self.actionHelp)
-        self.menubar.addAction(self.menuFile.menuAction())
-        self.menubar.addAction(self.menuAbout.menuAction())
-
-        self.retranslateUi(MainWindow)
-        QtCore.QMetaObject.connectSlotsByName(MainWindow)
-
-    def retranslateUi(self, MainWindow):
-        MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
-        self.menuFile.setTitle(QtGui.QApplication.translate("MainWindow", "File", None, QtGui.QApplication.UnicodeUTF8))
-        self.menuAbout.setTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
-        self.actionClose.setText(QtGui.QApplication.translate("MainWindow", "Close", None, QtGui.QApplication.UnicodeUTF8))
-        self.actionSave.setText(QtGui.QApplication.translate("MainWindow", "Save", None, QtGui.QApplication.UnicodeUTF8))
-        self.actionOpen.setText(QtGui.QApplication.translate("MainWindow", "Open", None, QtGui.QApplication.UnicodeUTF8))
-        self.actionHelp.setText(QtGui.QApplication.translate("MainWindow", "Help", None, QtGui.QApplication.UnicodeUTF8))
-        self.actionExport_to_SVG.setText(QtGui.QApplication.translate("MainWindow", "Export to SVG...", None, QtGui.QApplication.UnicodeUTF8))
-
+# -*- coding: utf-8 -*-
+
+# Form implementation generated from reading ui file 'qt_gui.ui'
+#
+# Created: Sat Feb 09 17:34:29 2013
+#      by: PyQt4 UI code generator 4.9.5
+#
+# WARNING! All changes made in this file will be lost!
+
+from PyQt4 import QtCore, QtGui
+#from pykat.gui.gui import pyKatGraphicsView
+
+try:
+    _fromUtf8 = QtCore.QString.fromUtf8
+except AttributeError:
+    _fromUtf8 = lambda s: s
+
+class Ui_MainWindow(object):
+    def setupUi(self, MainWindow):
+        MainWindow.setObjectName(_fromUtf8("MainWindow"))
+        MainWindow.resize(833, 614)
+        self.centralwidget = QtGui.QWidget(MainWindow)
+        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Preferred)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(self.centralwidget.sizePolicy().hasHeightForWidth())
+        self.centralwidget.setSizePolicy(sizePolicy)
+        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
+        self.gridLayout = QtGui.QGridLayout(self.centralwidget)
+        self.gridLayout.setSizeConstraint(QtGui.QLayout.SetDefaultConstraint)
+        self.gridLayout.setMargin(2)
+        self.gridLayout.setSpacing(5)
+        self.gridLayout.setObjectName(_fromUtf8("gridLayout"))
+        MainWindow.setCentralWidget(self.centralwidget)
+        self.menubar = QtGui.QMenuBar(MainWindow)
+        self.menubar.setGeometry(QtCore.QRect(0, 0, 833, 26))
+        self.menubar.setObjectName(_fromUtf8("menubar"))
+        self.menuFile = QtGui.QMenu(self.menubar)
+        self.menuFile.setObjectName(_fromUtf8("menuFile"))
+        self.menuAbout = QtGui.QMenu(self.menubar)
+        self.menuAbout.setObjectName(_fromUtf8("menuAbout"))
+        MainWindow.setMenuBar(self.menubar)
+        self.statusbar = QtGui.QStatusBar(MainWindow)
+        self.statusbar.setObjectName(_fromUtf8("statusbar"))
+        MainWindow.setStatusBar(self.statusbar)
+        self.actionClose = QtGui.QAction(MainWindow)
+        self.actionClose.setObjectName(_fromUtf8("actionClose"))
+        self.actionSave = QtGui.QAction(MainWindow)
+        self.actionSave.setObjectName(_fromUtf8("actionSave"))
+        self.actionOpen = QtGui.QAction(MainWindow)
+        self.actionOpen.setObjectName(_fromUtf8("actionOpen"))
+        self.actionHelp = QtGui.QAction(MainWindow)
+        self.actionHelp.setObjectName(_fromUtf8("actionHelp"))
+        self.actionExport_to_SVG = QtGui.QAction(MainWindow)
+        self.actionExport_to_SVG.setObjectName(_fromUtf8("actionExport_to_SVG"))
+        self.menuFile.addAction(self.actionSave)
+        self.menuFile.addAction(self.actionOpen)
+        self.menuFile.addSeparator()
+        self.menuFile.addAction(self.actionExport_to_SVG)
+        self.menuFile.addSeparator()
+        self.menuFile.addAction(self.actionClose)
+        self.menuAbout.addAction(self.actionHelp)
+        self.menubar.addAction(self.menuFile.menuAction())
+        self.menubar.addAction(self.menuAbout.menuAction())
+
+        self.retranslateUi(MainWindow)
+        QtCore.QMetaObject.connectSlotsByName(MainWindow)
+
+    def retranslateUi(self, MainWindow):
+        MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuFile.setTitle(QtGui.QApplication.translate("MainWindow", "File", None, QtGui.QApplication.UnicodeUTF8))
+        self.menuAbout.setTitle(QtGui.QApplication.translate("MainWindow", "About", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionClose.setText(QtGui.QApplication.translate("MainWindow", "Close", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionSave.setText(QtGui.QApplication.translate("MainWindow", "Save", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionOpen.setText(QtGui.QApplication.translate("MainWindow", "Open", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionHelp.setText(QtGui.QApplication.translate("MainWindow", "Help", None, QtGui.QApplication.UnicodeUTF8))
+        self.actionExport_to_SVG.setText(QtGui.QApplication.translate("MainWindow", "Export to SVG...", None, QtGui.QApplication.UnicodeUTF8))
+
diff --git a/pykat/node_network.py b/pykat/node_network.py
index 2ad607f0a2a8ea30120a176c8340a5b4ead071fd..8d5be82a2761e7dff4f8b81b9d6acbc3f4e8c00e 100644
--- a/pykat/node_network.py
+++ b/pykat/node_network.py
@@ -1,332 +1,332 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Sun Jan 27 10:02:41 2013
-
-@author: Daniel
-"""
-import exceptions
-import pykat.gui.graphics
-import pykat.exceptions as pkex
-from pykat.components import Component
-from pykat.detectors import Detector
-from pykat.utilities.optics.gaussian_beams import gauss_param
-
-class NodeNetwork(object):
-    def __init__(self, kat):
-        self.__nodes = {}
-        self.__kat = kat
-        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
-        
-        cls = type(self)
-        self.__class__ = type(cls.__name__, (cls,), {})
-        
-    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:
-            n = self.createNode(name)
-            self.connectNodeToComp(n, comp, do_callback=False)
-            list.append(n)
-        
-        self.__componentNodes[comp.id] = tuple(list)
-        self.__componentCallback[comp.id] = change_callback
-        
-        change_callback()
-    
-    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]()
-            
-    def connectNodeToComp(self, node, comp, do_callback=True):
-        if node.id in self.__nodeComponents:
-            comps = self.__nodeComponents[node.id]
-        else:
-            comps = (None,) * 2
-        
-        if len(comps) >= 2 and comps[0] != None and comps[1] != None:
-            raise pkex.BasePyKatException("Node is already connected to 2 components")
-        
-        l = list(comps)
-        
-        if l[0] == None:
-            l[0] = comp
-        elif l[1] == None:
-            l[1] = comp
-        else:
-            raise pkex.BasePyKatException("Connected to two coponents already")
-        
-        self.__nodeComponents[node.id] = tuple(l)
-        
-        if do_callback: self.__componentCallback[comp.id]()
-        
-    def createNode(self, node_name):
-        if node_name == 'dump':
-            return DumpNode()
-            
-        if node_name in self.__nodes:
-            # then this node already exists
-            return self.__nodes[node_name]
-        else:
-            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
-            self.__nodeComponents[n.id] = (None, None)
-            return n
-        
-    def removeNode(self, node):
-        if not isinstance(node,Node):
-            raise exceptions.ValueError("node argument is not of type Node")
-        
-        if node.name not in self.__nodes:
-            raise exceptions.RuntimeError("Trying to remove node {0} when it has not been added".format(node.name))
-        
-        C = self.getNodeComponents(node)
-        
-        if C[0] is not None or C[1] is not None:
-            raise exceptions.RuntimeError("Cannot remove a node which is attached to components")
-            
-        if len(node.getDetectors()) > 0:
-            raise exceptions.RuntimeError("Cannot remove a node which is attached to detectors still")
-        
-        self.__remove_node_attr(node)
-        del self.__nodes[node.name] 
-        
-    def hasNode(self, name):
-        return (name in self.__nodes)
-    
-    def getNodes(self):
-        return self.__nodes.copy()
-    
-    def dumpInfo(self):
-        
-        for name in self.__nodes:
-            
-            n = self.__nodes[name]
-            
-            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)
-    
-    def getComponentNodes(self, comp):
-        return self.__componentNodes[comp.id]
-    
-    def getNodeComponents(self, node):
-        return self.__nodeComponents[node.id]
-    
-    def __add_node_attr(self, node):
-
-        if not isinstance(node, Node):
-            raise exceptions.ValueError("Argument is not of type Node")
-        
-        name = node.name
-        fget = lambda self: self.__get_node_attr(name)
-        
-        setattr(self.__class__, name, property(fget))
-        setattr(self, '__node_' + name, node)                   
-    
-    def __remove_node_attr(self, node):
-        if not isinstance(node, Node):
-            raise exceptions.ValueError("Argument is not of type Node")
-        
-        name = node.name
-        delattr(self, '__node_' + name)
-        delattr(self.__class__, name)
-        
-    def __get_node_attr(self, name):
-        return getattr(self, '__node_' + name)        
-        
-    def __getitem__(self, value):
-        return self.__nodes[str(value)]
-        
-    def __contains__(self, value):
-        return value in self.__nodes
-        
-    
-class Node(object):
-
-    def __init__(self, name, network, id):
-        self._detectors = []
-        self.__name = name
-        self._item = None
-        self._network = network
-        self.__q_x = None
-        self.__q_y = None
-        self.__q_comp = None
-        self.__id = id
-    
-    def __str__(self): return self.__name
-    
-    @property
-    def id(self): return self.__id
-    
-    @property
-    def network(self): return self._network
-    
-    @property
-    def components(self): return self._network.getNodeComponents(self)
-    
-    @property
-    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
-    
-    def removeGauss():
-        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 = gauss_param(q=args[0])
-            self.__q_y = gauss_param(q=args[0])
-        elif len(args) == 2:
-            self.__q_x = gauss_param(q=args[0])
-            self.__q_y = gauss_param(q=args[1])
-        else:
-            raise pkex.BasePyKatException("Must specify either 1 Gaussian beam parameter or 2 for astigmatic beams")
-        
-    def getFinesseText(self):    
-        if self.__q_x is None or self.__q_y is None or self.__q_comp is None:
-            return []
-            
-        rtn = []
-        
-        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
-        
-    def isConnected(self):
-        if (self.components[0] is not None) and (self.components[1] is not None):
-            return True
-        else:
-            return False
-      
-    def remove(self):
-        self._network.removeNode(self)
-        
-        if self._item != None:
-            self._item.scene().removeItem(self._item)
-    
-    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,
-                                                             nsize, nsize, parent)
-            
-        return self._item
-    
-    def getDetectors(self):
-        return self._detectors[:]
-        
-    def amIConnected(self, obj):
-        """
-        Checks if obj is connected to the node. Returns true or false in tuple
-        with None or the other object and the node index which it is attached to
-        """ 
-        comps = self.components
-        
-        if obj == comps[0]:
-            if comps[1] == None:
-                ix = -1
-            else:
-                ix = comps[1].nodes.index(self)
-                
-            return [True, comps[1], ix]
-            
-        elif obj == comps[1]:
-            if comps[0] == None:
-                ix = -1
-            else:
-                ix = comps[0].nodes.index(self)
-                
-            return [True, comps[0], ix]
-        else:
-            return [False, None]
-        
-    @property
-    def name(self): return self.__name      
-    
-    
-class DumpNode(Node):
-    def __init__(self):
-        Node.__init__(self, 'dump', None, -1)
-        
+# -*- coding: utf-8 -*-
+"""
+Created on Sun Jan 27 10:02:41 2013
+
+@author: Daniel
+"""
+import exceptions
+import pykat.gui.graphics
+import pykat.exceptions as pkex
+from pykat.components import Component
+from pykat.detectors import Detector
+from pykat.utilities.optics.gaussian_beams import gauss_param
+
+class NodeNetwork(object):
+    def __init__(self, kat):
+        self.__nodes = {}
+        self.__kat = kat
+        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
+        
+        cls = type(self)
+        self.__class__ = type(cls.__name__, (cls,), {})
+        
+    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:
+            n = self.createNode(name)
+            self.connectNodeToComp(n, comp, do_callback=False)
+            list.append(n)
+        
+        self.__componentNodes[comp.id] = tuple(list)
+        self.__componentCallback[comp.id] = change_callback
+        
+        change_callback()
+    
+    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]()
+            
+    def connectNodeToComp(self, node, comp, do_callback=True):
+        if node.id in self.__nodeComponents:
+            comps = self.__nodeComponents[node.id]
+        else:
+            comps = (None,) * 2
+        
+        if len(comps) >= 2 and comps[0] != None and comps[1] != None:
+            raise pkex.BasePyKatException("Node is already connected to 2 components")
+        
+        l = list(comps)
+        
+        if l[0] == None:
+            l[0] = comp
+        elif l[1] == None:
+            l[1] = comp
+        else:
+            raise pkex.BasePyKatException("Connected to two coponents already")
+        
+        self.__nodeComponents[node.id] = tuple(l)
+        
+        if do_callback: self.__componentCallback[comp.id]()
+        
+    def createNode(self, node_name):
+        if node_name == 'dump':
+            return DumpNode()
+            
+        if node_name in self.__nodes:
+            # then this node already exists
+            return self.__nodes[node_name]
+        else:
+            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
+            self.__nodeComponents[n.id] = (None, None)
+            return n
+        
+    def removeNode(self, node):
+        if not isinstance(node,Node):
+            raise exceptions.ValueError("node argument is not of type Node")
+        
+        if node.name not in self.__nodes:
+            raise exceptions.RuntimeError("Trying to remove node {0} when it has not been added".format(node.name))
+        
+        C = self.getNodeComponents(node)
+        
+        if C[0] is not None or C[1] is not None:
+            raise exceptions.RuntimeError("Cannot remove a node which is attached to components")
+            
+        if len(node.getDetectors()) > 0:
+            raise exceptions.RuntimeError("Cannot remove a node which is attached to detectors still")
+        
+        self.__remove_node_attr(node)
+        del self.__nodes[node.name] 
+        
+    def hasNode(self, name):
+        return (name in self.__nodes)
+    
+    def getNodes(self):
+        return self.__nodes.copy()
+    
+    def dumpInfo(self):
+        
+        for name in self.__nodes:
+            
+            n = self.__nodes[name]
+            
+            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)
+    
+    def getComponentNodes(self, comp):
+        return self.__componentNodes[comp.id]
+    
+    def getNodeComponents(self, node):
+        return self.__nodeComponents[node.id]
+    
+    def __add_node_attr(self, node):
+
+        if not isinstance(node, Node):
+            raise exceptions.ValueError("Argument is not of type Node")
+        
+        name = node.name
+        fget = lambda self: self.__get_node_attr(name)
+        
+        setattr(self.__class__, name, property(fget))
+        setattr(self, '__node_' + name, node)                   
+    
+    def __remove_node_attr(self, node):
+        if not isinstance(node, Node):
+            raise exceptions.ValueError("Argument is not of type Node")
+        
+        name = node.name
+        delattr(self, '__node_' + name)
+        delattr(self.__class__, name)
+        
+    def __get_node_attr(self, name):
+        return getattr(self, '__node_' + name)        
+        
+    def __getitem__(self, value):
+        return self.__nodes[str(value)]
+        
+    def __contains__(self, value):
+        return value in self.__nodes
+        
+    
+class Node(object):
+
+    def __init__(self, name, network, id):
+        self._detectors = []
+        self.__name = name
+        self._item = None
+        self._network = network
+        self.__q_x = None
+        self.__q_y = None
+        self.__q_comp = None
+        self.__id = id
+    
+    def __str__(self): return self.__name
+    
+    @property
+    def id(self): return self.__id
+    
+    @property
+    def network(self): return self._network
+    
+    @property
+    def components(self): return self._network.getNodeComponents(self)
+    
+    @property
+    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
+    
+    def removeGauss():
+        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 = gauss_param(q=args[0])
+            self.__q_y = gauss_param(q=args[0])
+        elif len(args) == 2:
+            self.__q_x = gauss_param(q=args[0])
+            self.__q_y = gauss_param(q=args[1])
+        else:
+            raise pkex.BasePyKatException("Must specify either 1 Gaussian beam parameter or 2 for astigmatic beams")
+        
+    def getFinesseText(self):    
+        if self.__q_x is None or self.__q_y is None or self.__q_comp is None:
+            return []
+            
+        rtn = []
+        
+        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
+        
+    def isConnected(self):
+        if (self.components[0] is not None) and (self.components[1] is not None):
+            return True
+        else:
+            return False
+      
+    def remove(self):
+        self._network.removeNode(self)
+        
+        if self._item != None:
+            self._item.scene().removeItem(self._item)
+    
+    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,
+                                                             nsize, nsize, parent)
+            
+        return self._item
+    
+    def getDetectors(self):
+        return self._detectors[:]
+        
+    def amIConnected(self, obj):
+        """
+        Checks if obj is connected to the node. Returns true or false in tuple
+        with None or the other object and the node index which it is attached to
+        """ 
+        comps = self.components
+        
+        if obj == comps[0]:
+            if comps[1] == None:
+                ix = -1
+            else:
+                ix = comps[1].nodes.index(self)
+                
+            return [True, comps[1], ix]
+            
+        elif obj == comps[1]:
+            if comps[0] == None:
+                ix = -1
+            else:
+                ix = comps[0].nodes.index(self)
+                
+            return [True, comps[0], ix]
+        else:
+            return [False, None]
+        
+    @property
+    def name(self): return self.__name      
+    
+    
+class DumpNode(Node):
+    def __init__(self):
+        Node.__init__(self, 'dump', None, -1)
+        
         
\ No newline at end of file
diff --git a/pykat/param.py b/pykat/param.py
index 6ae1f9a9b29c52d565ec0b6054329c31fa6a072e..9a13f0b458619c810dfd3ae9931f01a2c3447cbf 100644
--- a/pykat/param.py
+++ b/pykat/param.py
@@ -1,167 +1,167 @@
-from pykat.SIfloat import SIfloat
-import abc
-import pykat.exceptions as pkex
-
-class putable(object):
-    """
-    Objects that inherit this should be able to have something `put` to it.
-    Essentially this means you could write Finesse commands like
-    
-    put this parameter value
-    """
-    __metaclass__ = abc.ABCMeta
-    
-    def __init__(self, component_name, parameter_name, isPutable=True):
-        self._parameter_name = parameter_name
-        self._component_name = component_name
-        self._putter = None
-        self._isPutable  = isPutable
-    
-    @property
-    def isPutable(self): return self._isPutable
-    
-    def put(self, var):
-    
-        if not isinstance(var, putter):
-            raise pkex.BasePyKatException("var was not something that can be `put` as a value")
-        
-        if self._putter != None:
-            self._putter.put_count -= 1
-        
-        self._putter = var
-        self._putter.put_count += 1
-        
-    def _getPutFinesseText(self):
-        rtn = []
-        # if something is being put to this 
-        if self._putter != None:
-            rtn.append("put {comp} {param} ${value}".format(comp=self._component_name, param=self._parameter_name, value=self._putter.put_name()))
-        
-        return rtn
-            
-class putter(object):
-    """
-    If an object can be put to something that is putable it should inherit this
-    object.
-    """
-    
-    def __init__(self, put_name, isPutter=True):
-        self._put_name = put_name
-        self.put_count = 0
-        self._isPutter = isPutter
-    
-    @property
-    def isPutter(self): return self._isPutter
-    
-    def put_name(self): return self._put_name
-    
-        
-class Param(putable, putter):
-
-    def __init__(self, name, owner, value, isPutable=True, isPutter=True, isTunable=True, var_name=None):
-        self._name = name
-        self._owner = owner
-        self._value = value
-        self._isPutter = isPutter
-        self._isTunable = isTunable
-        self._owner._register_param(self)
-        
-        if isPutter:
-            if var_name == None:
-                var_name = "var_{0}_{1}".format(owner.name, name)
-                
-            putter.__init__(self, var_name, isPutter)
-            
-        if isPutable:
-            putable.__init__(self, owner.name, name, isPutable)
-        
-    @property
-    def name(self): return self._name
-    
-    @property
-    def isTuneable(self): return self._isTunable
-    
-    @property
-    def value(self): return self._value
-    @value.setter
-    def value(self, value):
-        self._value = value
-    
-    def __str__(self): return str(self.value)
-    def __float__(self): return self.value
-        
-    def getFinesseText(self):
-        rtn = []
-        
-        if self.isPutable: rtn.extend(self._getPutFinesseText())
-        
-        # if this parameter is being put somewhere then we need to
-        # set it as a variable
-        if self.isPutter and self.put_count > 0:
-            rtn.append("set {put_name} {comp} {param}".format(put_name=self.put_name(), comp=self._owner.name, param=self.name))
-        
-        return rtn
-        
-    def __mul__(self, a):
-        return self.value * a
-    
-    def __imul__(self, a):
-        return self.value * (a)
-        
-    __rmul__ = __mul__
-    
-    def __add__(self, a):
-        return self.value + (a)
-    
-    def __iadd__(self, a):
-        return self.value + (a)
-        
-    __radd__ = __add__
-    
-    def __sub__(self, a):
-        return self.value - (a)
-    
-    def __isub__(self, a):
-        return self.value - (a)
-        
-    __rsub__ = __sub__
-    
-    def __div__(self, a):
-        return self.value / (a)
-    
-    def __idiv__(self, a):
-        return self.value / complex(a)
-        
-    def __pow__(self, q):
-        return  self.value**q
-
-    def __neg__(self):
-        return -self.value
-        
-    def __eq__(self, q):
-        return (q) == self.value
-    def __ne__(self, q):
-        return (q) != self.value
-    def __lt__(self, q):
-        return (q) > self.value
-    def __gt__(self, q):
-        return (q) < self.value        
-        
-class AttrParam(Param):
-    """
-    Certain parameters of a component are set using the Finesse `attr` command.
-    
-    This inherits directly from a Param object so can be set whether this attribute
-    is putable or a putter.
-    
-    If the value pf the parameter is not 0 the attr command will be printed.
-    """
-    def getFinesseText(self):
-        rtn = []
-        
-        if self.value != 0:
-            rtn.append("attr {0} {1} {2}".format(self._owner.name, self.name, self.value))
-            
-        rtn.extend(super(AttrParam, self).getFinesseText())
-        
+from pykat.SIfloat import SIfloat
+import abc
+import pykat.exceptions as pkex
+
+class putable(object):
+    """
+    Objects that inherit this should be able to have something `put` to it.
+    Essentially this means you could write Finesse commands like
+    
+    put this parameter value
+    """
+    __metaclass__ = abc.ABCMeta
+    
+    def __init__(self, component_name, parameter_name, isPutable=True):
+        self._parameter_name = parameter_name
+        self._component_name = component_name
+        self._putter = None
+        self._isPutable  = isPutable
+    
+    @property
+    def isPutable(self): return self._isPutable
+    
+    def put(self, var):
+    
+        if not isinstance(var, putter):
+            raise pkex.BasePyKatException("var was not something that can be `put` as a value")
+        
+        if self._putter != None:
+            self._putter.put_count -= 1
+        
+        self._putter = var
+        self._putter.put_count += 1
+        
+    def _getPutFinesseText(self):
+        rtn = []
+        # if something is being put to this 
+        if self._putter != None:
+            rtn.append("put {comp} {param} ${value}".format(comp=self._component_name, param=self._parameter_name, value=self._putter.put_name()))
+        
+        return rtn
+            
+class putter(object):
+    """
+    If an object can be put to something that is putable it should inherit this
+    object.
+    """
+    
+    def __init__(self, put_name, isPutter=True):
+        self._put_name = put_name
+        self.put_count = 0
+        self._isPutter = isPutter
+    
+    @property
+    def isPutter(self): return self._isPutter
+    
+    def put_name(self): return self._put_name
+    
+        
+class Param(putable, putter):
+
+    def __init__(self, name, owner, value, isPutable=True, isPutter=True, isTunable=True, var_name=None):
+        self._name = name
+        self._owner = owner
+        self._value = value
+        self._isPutter = isPutter
+        self._isTunable = isTunable
+        self._owner._register_param(self)
+        
+        if isPutter:
+            if var_name == None:
+                var_name = "var_{0}_{1}".format(owner.name, name)
+                
+            putter.__init__(self, var_name, isPutter)
+            
+        if isPutable:
+            putable.__init__(self, owner.name, name, isPutable)
+        
+    @property
+    def name(self): return self._name
+    
+    @property
+    def isTuneable(self): return self._isTunable
+    
+    @property
+    def value(self): return self._value
+    @value.setter
+    def value(self, value):
+        self._value = value
+    
+    def __str__(self): return str(self.value)
+    def __float__(self): return self.value
+        
+    def getFinesseText(self):
+        rtn = []
+        
+        if self.isPutable: rtn.extend(self._getPutFinesseText())
+        
+        # if this parameter is being put somewhere then we need to
+        # set it as a variable
+        if self.isPutter and self.put_count > 0:
+            rtn.append("set {put_name} {comp} {param}".format(put_name=self.put_name(), comp=self._owner.name, param=self.name))
+        
+        return rtn
+        
+    def __mul__(self, a):
+        return self.value * a
+    
+    def __imul__(self, a):
+        return self.value * (a)
+        
+    __rmul__ = __mul__
+    
+    def __add__(self, a):
+        return self.value + (a)
+    
+    def __iadd__(self, a):
+        return self.value + (a)
+        
+    __radd__ = __add__
+    
+    def __sub__(self, a):
+        return self.value - (a)
+    
+    def __isub__(self, a):
+        return self.value - (a)
+        
+    __rsub__ = __sub__
+    
+    def __div__(self, a):
+        return self.value / (a)
+    
+    def __idiv__(self, a):
+        return self.value / complex(a)
+        
+    def __pow__(self, q):
+        return  self.value**q
+
+    def __neg__(self):
+        return -self.value
+        
+    def __eq__(self, q):
+        return (q) == self.value
+    def __ne__(self, q):
+        return (q) != self.value
+    def __lt__(self, q):
+        return (q) > self.value
+    def __gt__(self, q):
+        return (q) < self.value        
+        
+class AttrParam(Param):
+    """
+    Certain parameters of a component are set using the Finesse `attr` command.
+    
+    This inherits directly from a Param object so can be set whether this attribute
+    is putable or a putter.
+    
+    If the value pf the parameter is not 0 the attr command will be printed.
+    """
+    def getFinesseText(self):
+        rtn = []
+        
+        if self.value != 0:
+            rtn.append("attr {0} {1} {2}".format(self._owner.name, self.name, self.value))
+            
+        rtn.extend(super(AttrParam, self).getFinesseText())
+        
         return rtn
\ No newline at end of file
diff --git a/pykat/plotting.py b/pykat/plotting.py
index 8deec541fc4ffb73675e7a4fae8c0657a20b0a9c..10e6cc1a7ba9a1662c2743962778d1fe06e56bcf 100644
--- a/pykat/plotting.py
+++ b/pykat/plotting.py
@@ -1,83 +1,83 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Sat Feb 02 10:35:04 2013
-
-@author: Daniel
-"""
-
-import numpy as np
-import matplotlib
-
-BACKEND = 'Qt4Agg'
-matplotlib.use(BACKEND)
-
-from matplotlib import rc
-import matplotlib.pyplot as plt
-
-mainpid = -1
-
-def plot1D(run, title=""):
-    
-    rc('font', **pp.font)
-    rc('xtick',labelsize=pp.TICK_SIZE)
-    rc('ytick',labelsize=pp.TICK_SIZE)
-    rc('text', usetex=pp.USETEX)
-    rc('axes', labelsize = pp.LABEL_SIZE)
-    
-    fig=plt.figure()
-    fig.set_size_inches(pp.fig_size)
-    fig.set_dpi(pp.FIG_DPI)
-        
-    ax1 = fig.add_subplot(111)
-    ax1.set_xlim(np.min(run.x),np.max(run.x))
-    traces = ax1.plot(run.x,run.y)
-    ax1.grid(pp.GRID)
-    
-    ax1.set_xlabel(run.xlabel)
-    legends = run.ylabels
-    ax1.legend(traces, legends, loc=0, shadow=pp.SHADOW,prop={'size':pp.LEGEND_SIZE})
-
-    if pp.PRINT_TITLE:
-        plt.title(title)
-        
-    if pp.SCREEN_TITLE:
-        fig.canvas.manager.set_window_title(title)
-    else:
-        fig.canvas.manager.set_window_title('')
-        
-    #plt.ion()
-    plt.show()
-    
-class pp():
-    # set some gobal settings first
-    BACKEND = 'Qt4Agg' # matplotlib backend
-    FIG_DPI=90 # DPI of on sceen plot
-    # Some help in calculating good figure size for Latex
-    # documents. Starting with plot size in pt,
-    # get this from LaTeX using \showthe\columnwidth
-    fig_width_pt = 484.0
-    inches_per_pt = 1.0/72.27  # Convert TeX pt to inches
-    golden_mean = (np.sqrt(5)-1.0)/2.0   # Aesthetic ratio
-    fig_width = fig_width_pt*inches_per_pt  # width in inches
-    fig_height = fig_width*golden_mean      # height in inches
-    fig_size = [fig_width,fig_height]
-    # some plot options:
-    LINEWIDTH = 1 # linewidths of traces in plot
-    AA = True # antialiasing of traces
-    USETEX = False # use Latex encoding in text
-    SHADOW = False # shadow of legend box
-    GRID = True # grid on or off
-    # font sizes for normal text, tick labels and legend
-    FONT_SIZE = 10 # size of normal text
-    TICK_SIZE = 10 # size of tick labels
-    LABEL_SIZE = 10 # size of axes labels
-    LEGEND_SIZE = 10 # size of legend
-    # font family and type
-    font = {'family':'sans-serif','sans-serif':['Helvetica'],'size':FONT_SIZE}
-    DPI=300 # DPI for saving via savefig
-    # print options given to savefig command:
-    print_options = {'dpi':DPI, 'transparent':True, 'bbox_inches':'tight', 'pad_inches':0.1}
-    # for Palatino and other serif fonts use:
-    #font = {'family':'serif','serif':['Palatino']}
-    SCREEN_TITLE = True # show title on screen?
+# -*- coding: utf-8 -*-
+"""
+Created on Sat Feb 02 10:35:04 2013
+
+@author: Daniel
+"""
+
+import numpy as np
+import matplotlib
+
+BACKEND = 'Qt4Agg'
+matplotlib.use(BACKEND)
+
+from matplotlib import rc
+import matplotlib.pyplot as plt
+
+mainpid = -1
+
+def plot1D(run, title=""):
+    
+    rc('font', **pp.font)
+    rc('xtick',labelsize=pp.TICK_SIZE)
+    rc('ytick',labelsize=pp.TICK_SIZE)
+    rc('text', usetex=pp.USETEX)
+    rc('axes', labelsize = pp.LABEL_SIZE)
+    
+    fig=plt.figure()
+    fig.set_size_inches(pp.fig_size)
+    fig.set_dpi(pp.FIG_DPI)
+        
+    ax1 = fig.add_subplot(111)
+    ax1.set_xlim(np.min(run.x),np.max(run.x))
+    traces = ax1.plot(run.x,run.y)
+    ax1.grid(pp.GRID)
+    
+    ax1.set_xlabel(run.xlabel)
+    legends = run.ylabels
+    ax1.legend(traces, legends, loc=0, shadow=pp.SHADOW,prop={'size':pp.LEGEND_SIZE})
+
+    if pp.PRINT_TITLE:
+        plt.title(title)
+        
+    if pp.SCREEN_TITLE:
+        fig.canvas.manager.set_window_title(title)
+    else:
+        fig.canvas.manager.set_window_title('')
+        
+    #plt.ion()
+    plt.show()
+    
+class pp():
+    # set some gobal settings first
+    BACKEND = 'Qt4Agg' # matplotlib backend
+    FIG_DPI=90 # DPI of on sceen plot
+    # Some help in calculating good figure size for Latex
+    # documents. Starting with plot size in pt,
+    # get this from LaTeX using \showthe\columnwidth
+    fig_width_pt = 484.0
+    inches_per_pt = 1.0/72.27  # Convert TeX pt to inches
+    golden_mean = (np.sqrt(5)-1.0)/2.0   # Aesthetic ratio
+    fig_width = fig_width_pt*inches_per_pt  # width in inches
+    fig_height = fig_width*golden_mean      # height in inches
+    fig_size = [fig_width,fig_height]
+    # some plot options:
+    LINEWIDTH = 1 # linewidths of traces in plot
+    AA = True # antialiasing of traces
+    USETEX = False # use Latex encoding in text
+    SHADOW = False # shadow of legend box
+    GRID = True # grid on or off
+    # font sizes for normal text, tick labels and legend
+    FONT_SIZE = 10 # size of normal text
+    TICK_SIZE = 10 # size of tick labels
+    LABEL_SIZE = 10 # size of axes labels
+    LEGEND_SIZE = 10 # size of legend
+    # font family and type
+    font = {'family':'sans-serif','sans-serif':['Helvetica'],'size':FONT_SIZE}
+    DPI=300 # DPI for saving via savefig
+    # print options given to savefig command:
+    print_options = {'dpi':DPI, 'transparent':True, 'bbox_inches':'tight', 'pad_inches':0.1}
+    # for Palatino and other serif fonts use:
+    #font = {'family':'serif','serif':['Palatino']}
+    SCREEN_TITLE = True # show title on screen?
     PRINT_TITLE = False # show title in saved file?
\ No newline at end of file
diff --git a/pykat/utils.py b/pykat/utils.py
index 328fb2933d34ba8191a7ca0b65608525675e7d1c..116ab67b832f8a4258e4c4b9a2a33bc5c848af4a 100644
--- a/pykat/utils.py
+++ b/pykat/utils.py
@@ -1,13 +1,13 @@
-# -*- coding: utf-8 -*-
-"""
-Created on Sat Feb 02 12:17:50 2013
-
-@author: Daniel
-"""
-from pykat.components import *
-
-def isSpace(obj):
-    if obj == None:
-        return False
-    else:
+# -*- coding: utf-8 -*-
+"""
+Created on Sat Feb 02 12:17:50 2013
+
+@author: Daniel
+"""
+from pykat.components import *
+
+def isSpace(obj):
+    if obj == None:
+        return False
+    else:
         return isinstance(obj,space)
\ No newline at end of file