diff --git a/README.rst b/README.rst
index 676b1e18a25d1777ab9f2d513d06baa899e754ca..157bc7a7c271d96f5fd0e952353acb55291995c6 100644
--- a/README.rst
+++ b/README.rst
@@ -6,6 +6,8 @@ It aims to provide a Python toolset for automating more complex tasks
 as well as providing a GUI for manipulating and viewing simulation
 setups.
 
+Source code is hosted at https://git.ligo.org/finesse/pykat
+
 Installation
 -------------
 
diff --git a/pykat/__init__.py b/pykat/__init__.py
index b256447fffe90bf87756efbd575e25d033fa4dad..daf240044eb61ebfdf08d216553f99a0a664659f 100644
--- a/pykat/__init__.py
+++ b/pykat/__init__.py
@@ -3,7 +3,7 @@ from __future__ import division
 from __future__ import print_function
 from __future__ import unicode_literals
 
-__version__ = "1.0.14"
+__version__ = "1.0.17"
 
 # This flag is used to switch on the gui features in pkat at import time
 USE_GUI = False
@@ -20,7 +20,7 @@ except ImportError:
 import pykat.exceptions as pkex
 
 NoGUIException = pkex.BasePyKatException("No PyQt4 module was found so cannot open a GUI")
-
+    
 import pykat.finesse as finesse
 import pykat.components as components
 import pykat.detectors as detectors
@@ -30,4 +30,3 @@ from pykat.optics.gaussian_beams import BeamParam
 
 from pykat.plotting import init_pykat_plotting
 
-
diff --git a/pykat/commands.py b/pykat/commands.py
index 76d803f71b0de709662925f48cc944e5de0dcced..aefa0930816b8ece6294afb91307ae92492a7b8c 100644
--- a/pykat/commands.py
+++ b/pykat/commands.py
@@ -10,6 +10,8 @@ from __future__ import print_function
 from __future__ import unicode_literals
 
 import numpy
+import warnings
+import pykat
 import pykat.external.six as six
 import pykat.exceptions as pkex
 
@@ -22,35 +24,38 @@ from numpy import min, max
 from pykat.param import Param, putter
 from collections import namedtuple
 from pykat.optics.gaussian_beams import BeamParam
+from pykat.freeze import canFreeze
 
-
-
-
+@canFreeze
 class Command(object):
     __metaclass__ = abc.ABCMeta
     
     def __init__(self, name, unique):
+        self.__dict__["____FROZEN____"] = False
+        self._kat = None
         self.__unique = unique
         self.tag = None
         self.__removed = False
         self.__name = name.strip("*")
         self._putters = []
     
+        
     def __deepcopy__(self, memo):
         """
         When deep copying a kat object we need to take into account
         the instance specific properties.
         """
-
         cls = self.__class__
         result = cls.__new__(cls)
+        result._unfreeze()
         result.__dict__ = copy.deepcopy(self.__dict__, memo)
         
         for _ in result._putters:
             _._updateOwner(result)
-        
+
+        result._freeze()
         return result
-            
+    
     def getFinesseText(self):
         """ Base class for individual finesse optical components """
         raise NotImplementedError("This function is not implemented")
@@ -103,7 +108,8 @@ class variable(Command):
     def __init__(self, name, value):
         Command.__init__(self, name, False)
         self.__value = value
-    
+        self._freeze()
+        
     def getFinesseText(self):
         return "variable {name} {value}".format(name=self.name, value=self.value)
     
@@ -135,6 +141,8 @@ class func(Command):
         
         self.output = putter(name, self)
         self._putters.append(self.output)
+
+        self._freeze()
         
     def getFinesseText(self):
         rtn = []
@@ -175,7 +183,8 @@ class lock(Command):
         self.output = putter(name, self)
         self._putters.append(self.output)
 
-
+        self._freeze()
+        
     @staticmethod
     def parseFinesseText(line, kat):
         v = line.split()
@@ -228,6 +237,8 @@ class cavity(Command):
         
         self.enabled = True
 
+        self._freeze()
+        
     def getFinesseText(self):
         if self.enabled:
             return 'cav {0} {1} {2} {3} {4}'.format(self.name, self.__c1.name, self.__n1.name, self.__c2.name, self.__n2.name);
@@ -405,6 +416,8 @@ class tf(Command):
         self.poles = []
         self.gain = 1
         self.phase = 0
+        
+        self._freeze()
     
     def addPole(self,f, Q):
         self.poles.append(tf.fQ(SIfloat(f), SIfloat(Q)))
@@ -504,6 +517,8 @@ class xaxis(Command):
             self.__param = param
             self.__comp = param._owner()
 
+        self._freeze()
+        
     def _set_variables(self):
         self.x = putter("x1", self)
         self.mx = putter("mx1", self)
diff --git a/pykat/components.py b/pykat/components.py
index ac1126ab99766b86225f844773b51ca7bc97c0fd..fb78a5ed368dc5a31bc8b0d2102a417488b6ad81 100644
--- a/pykat/components.py
+++ b/pykat/components.py
@@ -16,6 +16,7 @@ import pykat.external.six as six
 if six.PY2:
 	import exceptions
 
+import warnings
 import pykat.exceptions as pkex
 import pykat
 from pykat.node_network import *
@@ -36,6 +37,7 @@ from pykat.param import Param, AttrParam
 import weakref
 import pykat.exceptions as pkex
 from copy import deepcopy
+from pykat.freeze import canFreeze
 
 next_component_id = 1
 
@@ -85,7 +87,8 @@ class NodeGaussSetter(object):
         self.__node().setGauss(self.__comp(), self.qx, complex(value))
         
 id_____pykat_class = 0
-  
+ 
+@canFreeze
 class Component(object):
     __metaclass__ = abc.ABCMeta
 
@@ -99,9 +102,11 @@ class Component(object):
         
         cnew = type(cnew_name, (cls,), {})
         
-        return object.__new__(cnew)
+        o = object.__new__(cnew)
+        return o
         
     def __init__(self, name=None):
+        self._unfreeze()
         
         self._optivis_component = None
         self.__name = name
@@ -118,21 +123,22 @@ class Component(object):
         global next_component_id
         self.__id = next_component_id
         next_component_id += 1    
-       
+         
     def __deepcopy__(self, memo):
         """
         When deep copying a kat object we need to take into account
         the instance specific properties.
         """
-        
         # Here we create a copy of this object based of the base class
         # of this one, otherwise we're making a copy of a copy of a copy...
         result = self.__class__.__new__(self.__class__.__base__)
+        result._unfreeze()
         result.__dict__ = copy.deepcopy(self.__dict__, memo)
         
         for _ in result._params:
             _._updateOwner(result)
-        
+    
+        result._freeze()
         return result
         
     def _register_param(self, param):
@@ -204,7 +210,8 @@ class Component(object):
                 self.__add_node_setter(ns)
         
     def __add_node_setter(self, ns):
-
+        self._unfreeze()
+        
         if not isinstance(ns, NodeGaussSetter):
             raise exceptions.ValueError("Argument is not of type NodeGaussSetter")
         
@@ -214,6 +221,8 @@ class Component(object):
         setattr(self.__class__, name, property(fget))
         setattr(self, '__nodesetter_' + name, ns)                   
 
+        self._freeze()
+        
     def __get_node_setter(self, name):
         return getattr(self, '__nodesetter_' + name)   
         
@@ -500,7 +509,8 @@ class mirror(AbstractMirrorComponent):
         
         self._requested_node_names.append(node1)
         self._requested_node_names.append(node2)
-
+        self._freeze()
+        
     def parseAttributes(self, values):
         
         for key in values.keys():
@@ -592,6 +602,8 @@ class beamSplitter(AbstractMirrorComponent):
         self._requested_node_names.append(node3)
         self._requested_node_names.append(node4)
         self.__alpha = Param("alpha", self, SIfloat(alpha))
+
+        self._freeze()
         
     @property
     def alpha(self): return self.__alpha
@@ -704,6 +716,8 @@ class space(Component):
         self.__gy = AttrParam("gy", self, gy)
         
         self._default_fsig_param = self.__L
+
+        self._freeze()
         
     @property
     def L(self): return self.__L
@@ -830,6 +844,8 @@ class grating(Component):
         self.__rho_0 = AttrParam("rho_0", self, SIfloat(rho_0))
         self.__alpha = AttrParam("alpha", self, SIfloat(alpha))
         self._svgItem = None
+
+        self._freeze()
         
     @property
     def n(self): return Param('n', self.__n)
@@ -959,6 +975,7 @@ class isolator(Component):
         self.__S = Param("S",self,SIfloat(S))
         self.__L = Param("L",self,SIfloat(L))
 
+        self._freeze()
         
     @property
     def S(self): return self.__S
@@ -1064,6 +1081,8 @@ class isolator1(Component):
         self._requested_node_names.append(node4)
         self._svgItem = None
 
+        self._freeze()
+        
     @staticmethod
     def parseFinesseText(text):
         values = text.split()
@@ -1104,6 +1123,8 @@ class lens(Component):
         self._svgItem = None
         self.__f = Param("f", self, SIfloat(f))
         self.__p = Param("p", self, SIfloat(p))
+
+        self._freeze()
         
     @property
     def f(self): return self.__f
@@ -1200,6 +1221,8 @@ class modulator(Component):
         self.type = modulation_type
         
         self._default_fsig_param = self.__phase
+        
+        self._freeze()
             
     @property 
     def f(self): return self.__f
@@ -1316,6 +1339,8 @@ class laser(Component):
         self._svgItem = None
         
         self._default_fsig_param = self.__f_offset
+
+        self._freeze()
         
     @property
     def P(self): return self.__power
@@ -1413,6 +1438,9 @@ class squeezer(Component):
         self.__angle = Param("angle", self, SIfloat(angle), canFsig=False, fsig_name="angle")
         self._svgItem = None
         self.entangled_carrier = entangled_carrier
+
+        self._freeze()
+        
         
     @property
     def db(self): return self.__db
diff --git a/pykat/detectors.py b/pykat/detectors.py
index f3195d08a753f2eb8fd448da9a4192c4b96786ea..62e6938f3b7215cf4ddd7d65141c743013f0f084 100644
--- a/pykat/detectors.py
+++ b/pykat/detectors.py
@@ -26,6 +26,7 @@ import warnings
 import copy
 
 from pykat import USE_GUI, NoGUIException
+from pykat.freeze import canFreeze
 
 if USE_GUI:
     import pykat.gui.resources
@@ -33,6 +34,7 @@ if USE_GUI:
 
 id_____pykat_class = 0
 
+@canFreeze
 class BaseDetector(object) :
     """
     This is a base class for all detectors. Classes Detector1 and Detector2 should be used directly.
@@ -51,9 +53,12 @@ class BaseDetector(object) :
         
         cnew = type(cnew_name, (cls,), {})
         
-        return object.__new__(cnew)
+        o = object.__new__(cnew)
+        return o
         
     def __init__(self, name, nodes=None, max_nodes=1):
+
+        self._unfreeze()
         
         self.__name = name
         self._svgItem = None
@@ -106,8 +111,10 @@ class BaseDetector(object) :
         # Here we create a copy of this object based of the base class
         # of this one, otherwise we're making a copy of a copy of a copy...
         result = self.__class__.__new__(self.__class__.__base__)
+        result._unfreeze()
         result.__dict__ = copy.deepcopy(self.__dict__, memo)
         
+        result._freeze()
         return result
                 
     def _register_param(self, param):
@@ -243,6 +250,8 @@ class beam(Detector1):
         self.alternate_beam = alternate_beam
         self.__f = Param("f", self, frequency)        
     
+        self._freeze()
+        
     @property
     def f(self): return self.__f
     
@@ -291,6 +300,8 @@ class cp(Detector0):
         self.cavity = str(cavity)
         self.direction = direction
         self.parameter = parameter
+        
+        self._freeze()
 
     @property
     def direction(self): return self.__direction
@@ -345,6 +356,8 @@ class xd(Detector0):
         
         self.component = component
         self.motion = motion
+    
+        self._freeze()
 
     @staticmethod
     def parseFinesseText(text): 
@@ -373,6 +386,8 @@ class ad(Detector1):
         self.alternate_beam = alternate_beam
         self.__f = Param("f", self, frequency)
     
+        self._freeze()
+    
     @property
     def mode(self): return self.__mode
     @mode.setter
@@ -426,6 +441,8 @@ class gouy(Detector1):
         self.spaces = copy.copy(spaces)
         self.direction = direction
         self.alternate_beam = False
+    
+        self._freeze()
         
     @property
     def direction(self): return self.__dir
@@ -475,6 +492,8 @@ class bp(Detector1):
         self.parameter = parameter
         self.direction = direction
         self.alternate_beam = alternate_beam
+    
+        self._freeze()
         
     @property
     def direction(self): return self.__dir
@@ -598,9 +617,10 @@ class pd(Detector1):
                 ps[i].value = kwargs[p]
             elif i<num_demods-1:
                 raise pkex.BasePyKatException("Missing demodulation phase {0} (phase{0})".format(i+1))
-        
    
         self.__set_demod_attrs()
+    
+        self._freeze()
                 
     @property
     def senstype(self): return self.__senstype
@@ -655,6 +675,7 @@ class pd(Detector1):
         For the set number of demodulations the correct number of 
         Parameters are created.
         """
+        self._unfreeze()
         
         # if there are demodulations present then we want to add
         # the various parameters so they are available for users
@@ -678,8 +699,8 @@ class pd(Detector1):
                         
                     if hasattr(self, "phase"+name):
                         delattr(self.__class__, "phase"+name)
-        else:
-            return
+        
+        self._freeze()
     
     @staticmethod
     def parseFinesseText(text): 
@@ -776,7 +797,11 @@ class qnoised(pd):
     def __init__(self, name, num_demods, node_name, alternate_beam=False, pdtype=None, **kwargs):
         super(qnoised, self).__init__(name, num_demods, node_name, alternate_beam=alternate_beam, pdtype=pdtype, **kwargs)
     
+        self._unfreeze()
+        
         self.__homangle = AttrParam("homangle", self, None)
+        
+        self._freeze()
     
     @property
     def homangle(self): return self.__homangle
@@ -985,7 +1010,9 @@ class hd(Detector2):
         BaseDetector.__init__(self, name, (node1_name, node2_name), max_nodes=2)
     
         self.__phase = Param("phase", self, phase)
-    
+
+        self._freeze()
+        
     @property
     def phase(self): return self.__phase
     @phase.setter
@@ -1026,6 +1053,8 @@ class qhd(Detector2):
     
         self.__phase = Param("phase", self, phase)
         self.sensitivity = sensitivity
+    
+        self._freeze()
         
     @property
     def phase(self): return self.__phase
diff --git a/pykat/finesse.py b/pykat/finesse.py
index f442a719cb8d644dfe9b7f2b11f312921359cb3a..f25155f43cc4a3bf5795eea590134d10a6392a4f 100644
--- a/pykat/finesse.py
+++ b/pykat/finesse.py
@@ -87,14 +87,13 @@ from pykat.commands import Command, xaxis
 from pykat.SIfloat import *
 from pykat.param import Param, AttrParam
 from pykat.external import progressbar
-
+from pykat.freeze import canFreeze
 import pykat.external.six as six
 
 import pykat.exceptions as pkex
 
 from pykat import USE_GUI, HAS_OPTIVIS, NoGUIException
 
-    
 if HAS_OPTIVIS:
     from optivis.bench.labels import Label as optivis_label
     from optivis.geometry import Coordinates as optivis_coord
@@ -210,6 +209,16 @@ class BlockedKatFile(object):
         bkf.write("mytest.kat")
     """
     
+    def __str__(self):
+         rtn = ""
+         
+         for block in self.ordering:
+             rtn += "\n%%% FTblock " + block + "\n"
+             rtn += self.blocks[block]
+             rtn += "%%% FTend " + block + "\n"
+         
+         return rtn
+         
     def __init__(self, NO_BLOCK="NO_BLOCK"):
         self.__NO_BLOCK = NO_BLOCK
         self.ordering = [self.__NO_BLOCK]
@@ -265,12 +274,14 @@ class BlockedKatFile(object):
             if len(values) >= 3 and values[0] == "%%%":
                 if values[1] == "FTblock":
                     newTag = values[2]
-
+                    
                     if self.__currentBlock != None and self.__currentBlock != self.__NO_BLOCK: 
                         warnings.warn("found block {0} before block {1} ended".format(newTag, self.__currentBlock))    
     
                     if newTag in self.blocks:
-                        raise pkex.BasePyKatException("Block `{0}` has already been read".format(newTag))
+                        #raise pkex.BasePyKatException("Block `{0}` has already been read".format(newTag))
+                        self.__currentBlock = newTag
+                        continue
     
                     self.blocks[newTag] = ""
                     self.__currentBlock = newTag
@@ -330,18 +341,24 @@ def GUILength(L):
     Should scale the lengths in some way to handle km and mm for time being
     """
     return L # * ( 40 * erfc(L/400.0) + 0.01)
-            
+
+@canFreeze
 class KatRun(object):
     def __init__(self):
+        self._unfreeze()
         self.runtime = None
         self.StartDateTime = datetime.datetime.now()
         self.x = None
+        self.stdout = None
+        self.stderr = None
+        self.runDateTime = None
         self.y = None
         self.xlabel = None
         self.ylabels = None
         self.katScript = None
         self.katVersion = None
         self.yaxis = None
+        self._freeze()
         
     def info(self):
         
@@ -623,9 +640,11 @@ class KatRun(object):
                 return out.squeeze()
         else:
             raise  pkex.BasePyKatException("No output by the name '{0}' found in the output".format(str(value)))
-      
+            
+@canFreeze 
 class KatRun2D(object):
     def __init__(self):
+        self._unfreeze()
         self.runtime = None
         self.startDateTime = datetime.datetime.now()
         self.x = None
@@ -638,6 +657,7 @@ class KatRun2D(object):
         self.katVersion = None
         self.stderr = None
         self.stdout = None
+        self._freeze()
         
     def saveKatRun(self, filename):
         with open(filename,'w') as outfile:
@@ -658,10 +678,13 @@ class KatRun2D(object):
         else:
             raise  pkex.BasePyKatException("No output by the name {0} found".format(str(value)))
     
-        
+@canFreeze    
 class Signals(object):
+    
+    @canFreeze 
     class fsig(object):
         def __init__(self, param, name, amplitude, phase, signal):
+            self._unfreeze()
             self._params = []
             self.__target = param
             self.__name = name
@@ -669,6 +692,7 @@ class Signals(object):
             self.__phase = Param("phase", self, SIfloat(phase))
             self.__removed = False
             self.__signal = signal
+            self._freeze()
             
             # unfortunatenly the target names for fsig are not the same as the
             # various parameter names of the components, e.g. mirror xbeta is x 
@@ -691,7 +715,7 @@ class Signals(object):
                 raise pkex.BasePyKatException("Signal {0} has already been marked as removed".format(self.name))
             else:
                 self.__signal.targets.remove(self)
-                self.__remove = True
+                self.__removed = True
         
         @property
         def name(self): return self.__name
@@ -742,29 +766,36 @@ class Signals(object):
         
         del self.targets[:]
         
+        self.f = None
+        
     @property
     def f(self): return self.__f
     @f.setter
     def f(self,value):
+        if value is None:
+            self.__f.value = None
+            return
+            
         v = SIfloat(value)
         
         if v <= 0:
             raise pkex.BasePyKatException("Signal frequency must be greater than 0.")
             
         self.__f.value = SIfloat(value)
-    
+        
     def __init__(self, kat):
+        self._unfreeze()
         self._default_name = "fsignal"
         self.targets = []
         self._params = []
         self.__f = Param("f", self, None)
         self._kat = kat
+        self._freeze()
         
     def _register_param(self, param):
         self._params.append(param)
         
     def apply(self, target, amplitude, phase, name=None):
-        
         if target is None:
             raise  pkex.BasePyKatException("No target was specified for signal to be applied")
         
@@ -811,6 +842,7 @@ Constant = namedtuple('Constant', 'name, value, usedBy')
 
 id___ = 0
 
+@canFreeze
 class kat(object):  
 
     def __new__(cls, *args, **kwargs):
@@ -828,7 +860,7 @@ class kat(object):
         return object.__new__(cnew)
     
     def __init__(self, kat_file=None, kat_code=None, katdir="", katname="", tempdir=None, tempname=None):
-
+        self._unfreeze()
         self.__looking = False
         self.scene = None # scene object for GUI
         self.verbose = True
@@ -866,6 +898,7 @@ class kat(object):
         self.__time_code = None
         self.__yaxis = "abs" # default yaxis
         self.__lambda0 = 1064e-9
+        self.__finesse_dir = None
         
         if kat_code != None and kat_file != None:
             raise pkex.BasePyKatException("Specify either a Kat file or some Kat code, not both.")
@@ -875,7 +908,9 @@ class kat(object):
         
         if kat_file != None:
             self.loadKatFile(kat_file)
-
+    
+        self._freeze()
+        
     def deepcopy(self):
         return copy.deepcopy(self)
     
@@ -1137,6 +1172,9 @@ class kat(object):
             print("$" + key, "::::", "owner =", self.__variables[key].owner.name, ", use count =", self.__variables[key].putCount)
     
     def parseCommands(self, commands, blocks=None, addToBlock=None, preserve=False):
+        
+        commands = str(commands)
+        
         try:
             if addToBlock is not None and blocks is not None:
                 raise pkex.BasePyKatException("When parsing commands you cannot set both blocks and addToBlock arguments")
@@ -1172,7 +1210,9 @@ class kat(object):
                                     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".format(newTag))
+                                    #raise pkex.BasePyKatException("Block `{0}` has already been read".format(newTag))
+                                    self.__currentTag = newTag
+                                    continue # Just add to existing block data
                         
                                 self.__blocks[newTag] = Block(newTag) # create new list to store all references to components in block
                                 self.__currentTag = newTag
@@ -1791,6 +1831,10 @@ class kat(object):
                                         traceData[-1][node_name] = (pykat.BeamParam(q=complex(qx), wavelength=self.lambda0),
                                                                     pykat.BeamParam(q=complex(qy), wavelength=self.lambda0),
                                                                     component_name)
+                                        direc = a[1].split(";")[-1].strip().split(maxsplit=1)[-1]
+                                        
+                                        traceData[-1][node_name][0].direction = direc
+                                        traceData[-1][node_name][1].direction = direc
                             
                         finally:
                             ifile.close()
@@ -2016,6 +2060,7 @@ class kat(object):
         
     def add(self, obj, block=NO_BLOCK):
         try:
+            self._unfreeze()
             obj.tag = block
             self.__blocks[block].contents.append(obj)
             
@@ -2048,9 +2093,11 @@ class kat(object):
                 raise pkex.BasePyKatException("Object {0} could not be added".format(obj))
                 
             obj._on_kat_add(self)
-            
+        
         except pkex.BasePyKatException as ex:
             pkex.PrintError("Error on adding object:", ex)
+        finally:
+            self._freeze()
 
     def readOutFile(self, filename):
         
diff --git a/pykat/freeze.py b/pykat/freeze.py
new file mode 100644
index 0000000000000000000000000000000000000000..20a24a7c4152f1143ed5888220240afa04bda72a
--- /dev/null
+++ b/pykat/freeze.py
@@ -0,0 +1,25 @@
+import warnings
+
+def canFreeze(cls):
+    
+    def _freeze(self): self.__dict__["____FROZEN____"] = True
+    def _unfreeze(self): self.__dict__["____FROZEN____"] = False
+    
+    def frozensetattr(self, name, value):
+        if "____FROZEN____" in self.__dict__ and self.__dict__["____FROZEN____"] and not hasattr(self, name):
+            if hasattr(self, "name"):
+                n = self.name
+            elif hasattr(self, "__name"):
+                n = self.__name
+            else:
+                n = self.__class__.__name__
+                
+            warnings.warn("'%s' does not have attribute called '%s'" % (n, name), stacklevel=2)
+            
+        super(cls, self).__setattr__(name, value)
+
+    cls.__setattr__ = frozensetattr
+    cls._freeze = _freeze
+    cls._unfreeze = _unfreeze
+    
+    return cls
\ No newline at end of file
diff --git a/pykat/gw_detectors/finesse_files/aLIGO.kat b/pykat/gw_detectors/finesse_files/aLIGO.kat
index 601c6f7c641263d94fa9928755e1c8665bb996f4..93cd05395f6e998d14bad7320d6a2eddaa6ab3df 100644
--- a/pykat/gw_detectors/finesse_files/aLIGO.kat
+++ b/pykat/gw_detectors/finesse_files/aLIGO.kat
@@ -12,7 +12,7 @@
 #   http://iopscience.iop.org/article/10.1088/0264-9381/32/7/074001/meta#cqg507871s4-8
 # - G. Billingsley. LIGO core optics reference page. https://galaxy.ligo. caltech.edu/optics/
 # 
-# Anna Green, 28th November 2016
+# Anna Green, Andreas Freise, 28th November 2016
 #--------------------------------------------------------------------------
 #
 # References used for aLIGO parameters
@@ -49,7 +49,7 @@
 #   Changes in file structure
 #   - removed length calculation fucntions for PRC and SRC sub-length
 #     and added length values into space components
-#   -
+#   - removed maxtem 4 command from HOMs block
 #   
 #   Naming changes
 #   - changed naming for thermal lens components
@@ -57,6 +57,8 @@
 #   - changed naming of SRMHR to SRM and node names in SRM substrate
 #   - changed name of ITMHR to ITM and ETMHR to ETM
 #   - changed names of arm spaces from LXarm, LYarm to LX, LY
+#		- changed name of HOMs block to cavities
+#		- changed PR2 node4 name from dump11 to nAPOP (auxilliary POP)
 #
 #
 #--------------------------------------------------------------------------
@@ -95,7 +97,7 @@ attr PRM Rc 11.009
 # Distance between PRM and PR2
 s lp1 16.6107 nPRM2 nPR2a
 # PR2
-bs1 PR2 250u $Mloss 0 -0.79 nPR2a nPR2b nPOP dump11
+bs1 PR2 250u $Mloss 0 -0.79 nPR2a nPR2b nPOP nAPOP
 attr PR2 Rc -4.545
 # Distance from PR2 to PR3
 s lp2 16.1647 nPR2b nPR3a
@@ -231,9 +233,8 @@ attr SRM Rc -5.6938
 ###########################################################################
 %%% FTend SRC
 
-%%% FTblock HOMs
+%%% FTblock cavities
 ###########################################################################
-maxtem 4
 #cav cavIMC MC2 nMC2in MC2 nMC2refl 
 cav cavXARM ITMX nITMX2 ETMX nETMX1
 cav cavYARM ITMY nITMY2 ETMY nETMY1
@@ -244,7 +245,7 @@ cav cavSRY SRM nSRM1 ITMY nITMYs2
 # removed OMC
 
 ###########################################################################
-%%% FTend HOMs
+%%% FTend cavities
 
 %%% FTblock constants
 ###########################################################################
diff --git a/pykat/gw_detectors/ifo.py b/pykat/gw_detectors/ifo.py
index 6cd77428decd7076a12ce73ea4c6d025976ba4c3..b2e5c5d1e3db0ac0f6a8178e921b6c26f1ce6d2b 100644
--- a/pykat/gw_detectors/ifo.py
+++ b/pykat/gw_detectors/ifo.py
@@ -8,13 +8,18 @@ import math
 import copy
 import warnings
 import cmath
+import inspect
+import six 
+
 from pykat import finesse
 from pykat.finesse import BlockedKatFile
+
 import pykat.components
 import pykat.exceptions as pkex
 import pykat.external.peakdetect as peak
 import matplotlib.pyplot as plt
 import pkg_resources
+
 from scipy.optimize import fmin
 
 global nsilica, clight
@@ -116,7 +121,16 @@ class aLIGO(object):
         self.CARM =  DOF("CARM", self.REFL_f1, "I", ["ETMX", "ETMY"], [1, 1], 1.5)
         self.DARM =  DOF("DARM", self.AS_DC,   "",  ["ETMX", "ETMY"], [1,-1], 1.0)
         self.SRCL =  DOF("SRCL", self.REFL_f2, "I", "SRM", 1, 1e2)
-            
+        
+        self.__DOFs = {}
+        
+        for _ in inspect.getmembers(self, lambda x: isinstance(x, DOF)):
+            self.__DOFs[_[0]] = _[1]
+    
+    @property
+    def DOFs(self):
+        return copy.copy(self.__DOFs)
+    
     def adjust_PRC_length(self, kat, verbose=False):
         """
         Adjust PRC length so that it fulfils the requirement
@@ -159,7 +173,7 @@ put f1m f $mx1
         ax.set_xlim([np.min(out.x-self.f1), np.max(out.x-self.f1)])
         ax.set_xlabel("delta_f1 [Hz]")
         ax.set_ylabel('sqrt(W) ')
-        ax.grid()
+        ax.grid(True)
         ax.legend()
         plt.tight_layout()
         plt.show(block=0)
@@ -395,28 +409,127 @@ put f1m f $mx1
             ax.set_xlim([np.min(out.x), np.max(out.x)])
             ax.set_xlabel("phi [deg] {}".format(d.optics[0]))
             ax.set_ylabel('{} [W] '.format(d.signal_name(kat)))
-            ax.grid()
+            ax.grid(True)
         plt.tight_layout()
         plt.show(block=0)
 
-    def plot_error_signals(self, _kat, xlimits=[-1,1]):
+    def _strToDOFs(self, DOFs):
+        dofs = []
+        
+        for _ in DOFs:
+            if isinstance(_, six.string_types):
+                if _ in self.__DOFs:
+                    dofs.append(self.__DOFs[_])
+                else:
+                    raise pkex.BasePyKatException("Could not find DOF called `%s`. Possible DOF options: %s" % (_, str(list(self.__DOFs.keys()))))
+            else:
+                raise pkex.BasePyKatException("'%s' not possible DOF options: %s" % (_, str(list(self.__DOFs.keys()))))
+        
+        return dofs
+
+    def plot_error_signals(self, _kat, xlimits=[-1,1], DOFs=None, plotDOFs=None,
+                                replaceDOFSignals=False, block=True, fig=None, legend=None):
+        """
+        Displays error signals for a given kat file. Can also be used to plot multiple
+        DOF's error signals against each other for visualising any cross coupling.
+        
+        _kat: LIGO based kat object.
+        xlimits: Range of DOF to plot in degrees
+        DOFs: list, DOF names to compute. Default: DARM, CARM, PRCL, SRCL, MICH
+        plotDOFs: list, DOF names to plot against each DOF. If None the same DOF as in DOFs is plotted.
+        block: Boolean, for plot blocking terminal or not if being shown
+        replaceDOFSignals: Bool, replaces already present signals for any DOF if already defined in kat. Regardless of this value, it will add default signals if none found.
+        fig: figure, uses predefined figure, when defined it won't be shown automatically
+        legend: string, if no plotDOFs is defined this legend is shown
+        
+        Example:
+            import pykat
+            from pykat.gw_detectors import ifo
+
+            ligo = ifo.aLIGO()
+            
+            # Plot default
+            ligo.plot_error_signals(ligo.kat, block=True)
+            # plot MICH and CARM against themselves
+            ligo.plot_error_signals(ligo.kat, DOFs=["MICH", "CARM"], block=True)
+            # plot DARM and CARM against MICH
+            ligo.plot_error_signals(ligo.kat, DOFs=["MICH"], plotDOFs=["DARM", "CARM"], block=True)
+        """
+        
         kat = _kat.deepcopy()
         kat.verbose = False
         kat.noxaxis = True
-        dofs = [self.DARM, self.CARM, self.PRCL, self.SRCL,  self.MICH]
-        idx=1
-        fig = plt.figure()
-        for d in dofs:
-            ax = fig.add_subplot(2,3,idx)
-            idx+=1
-            out = scan_DOF(kat, d, xlimits = np.multiply(d.scale,xlimits), relative = True)
-            ax.plot(out.x,out[d.signal_name(kat)])
+        
+        if DOFs is None:
+            dofs = [self.DARM, self.CARM, self.PRCL, self.SRCL, self.MICH]
+        else:
+            dofs = self._strToDOFs(DOFs)
+        
+        # add in signals for those DOF to plot
+        for _ in dofs:
+            if not (not replaceDOFSignals and hasattr(kat, _.signal_name(kat))):
+                kat.parseCommands(_.signal(kat))
+                
+        toShow = None
+        
+        if plotDOFs is not None:
+            toShow = self._strToDOFs(plotDOFs)
+        
+            # Check if other DOF signals we need to include for plotting
+            for _ in toShow:
+                if not (not replaceDOFSignals and hasattr(kat, _.signal_name(kat))):
+                    kat.parseCommands(_.signal(kat))
+                    
+        if fig is not None:
+            _fig = fig
+        else:
+            _fig = plt.figure()
+        
+        nrows = 2
+        ncols = 3
+        
+        if DOFs is not None:
+            n = len(DOFs)
+            
+            if n < 3:
+                nrows = 1
+                ncols = n
+        
+        for d, idx in zip(dofs, range(1, len(dofs)+1)):
+            ax = _fig.add_subplot(nrows, ncols, idx)
+            
+            scan_cmd = scan_optics_string(d.optics, d.factors, "scan", linlog="lin",
+                                            xlimits=np.multiply(d.scale, xlimits), steps=200,
+                                            axis=1, relative=True)
+            kat.parseCommands(scan_cmd)
+            out = kat.run()
+            
+            if toShow is None:
+                ax.plot(out.x, out[d.signal_name(kat)], label=legend)
+            else:
+                for _ in toShow:
+                    if legend is None:
+                        legend = _.name
+                        
+                    ax.plot(out.x, out[_.signal_name(kat)], label=legend)
+                
             ax.set_xlim([np.min(out.x), np.max(out.x)])
             ax.set_xlabel("{} [deg]".format(d.name))
-            ax.set_ylabel('{} [W] '.format(d.port.name))
-            ax.grid()
+            
+            if plotDOFs is None:
+                ax.set_ylabel('{} [W] '.format(d.port.name))
+            else:
+                ax.set_ylabel('Error signal [W]')
+            
+            ax.grid(True)
+        
+        if toShow is not None or legend is not None:
+            plt.legend(loc=0)
+           
         plt.tight_layout()
-        plt.show(block=0)
+        
+        if fig is None:
+            plt.show(block=block)
 
     def set_DC_offset(self, _kat, DCoffset=None, verbose=False):
         if DCoffset:
@@ -799,19 +912,24 @@ class port(object):
 def scan_optics_string(_optics, _factors, _varName, linlog="lin", xlimits=[-100, 100], steps=200, axis=1,relative=False):
     optics=make_list_copy(_optics)
     factors=make_list_copy(_factors)
+    
     if len(optics) != len(factors):
         raise pkex.BasePyKatException("you must provide a factor for each optics")
 
     if linlog not in ["lin", "log"]: 
         raise pkex.BasePyKatException("linlog must be 'lin' or 'log'")
+        
     _tuneStr  = "var {} 0\n".format(_varName)
+    
     if axis==1:
         _tuneStr += "xaxis {} phi {} {} {} {}".format(_varName, linlog, xlimits[0], xlimits[1], steps)
     elif (axis==2 or axis==3): 
         _tuneStr += "x{}axis {} phi {} {} {} {}".format(axis, _varName, linlog, xlimits[0], xlimits[1], steps)
     else:
         raise pkex.BasePyKatException("axis must be 1, 2 or 3")
+        
     _putStr = ""
+    
     for idx, o in enumerate(optics):
         if factors[idx] == 1:
             _xStr="$x"
@@ -826,6 +944,7 @@ def scan_optics_string(_optics, _factors, _varName, linlog="lin", xlimits=[-100,
                 
         _putStr = "\n".join([_putStr, "{} {} phi {}{}".format(_putCmd, o, _xStr, axis)])            
         _tuneStr += _putStr
+        
     return _tuneStr
         
 def make_transparent(kat, _components):
@@ -915,12 +1034,14 @@ def BS_optical_path(thickness, n=nsilica, angle=45.0):
 
 def scan_DOF(_kat, DOF, xlimits=[-100, 100], steps=200, relative=False): 
     kat = _kat.deepcopy()
-    scan_string = scan_optics_string(DOF.optics, DOF.factors, "scan", linlog="lin", xlimits=xlimits, steps=steps, axis=1,relative=relative)
+    
+    scan_string = scan_optics_string(DOF.optics, DOF.factors, "scan", linlog="lin",
+                                     xlimits=xlimits, steps=steps, axis=1, relative=relative)
+    
     kat.parseCommands(scan_string)
-    sigStr = DOF.signal(kat)
-    kat.parseCommands(sigStr)
-    out = kat.run()
-    return out
+    kat.parseCommands(DOF.signal(kat))
+    
+    return kat.run()
 
 def scan_optics(_kat, _optics, _factors, xlimits=[-100, 100], steps=200,relative=False): 
     """
@@ -935,12 +1056,16 @@ def scan_optics(_kat, _optics, _factors, xlimits=[-100, 100], steps=200,relative
     scan_optis(kat, ["ETMX", "ETMY", [1, -1])
     """
     kat = _kat.deepcopy()
+    
     optics=make_list_copy(_optics)
     factors=make_list_copy(_factors)
-    scan_string = scan_optics_string(optics, factors, "scan", linlog="lin", xlimits=xlimits, steps=steps, axis=1,relative=relative)
+    
+    scan_string = scan_optics_string(optics, factors, "scan", linlog="lin", xlimits=xlimits,
+                                      steps=steps, axis=1,relative=relative)
+    
     kat.parseCommands(scan_string)
-    out = kat.run()
-    return out
+    
+    return kat.run()
 
 def optical_gain(_kat, DOF_sig, DOF_det, f=10.0):
     kat = _kat.deepcopy()
diff --git a/test/run_tests.py b/test/run_tests.py
index ffa3705f552f43f2027e051d2acbafeadabed2fc..f89a89f26981f9c10ec3319f70bc413b77410d1a 100644
--- a/test/run_tests.py
+++ b/test/run_tests.py
@@ -2,6 +2,9 @@ import pykat
 import traceback
 import os
 import sys
+import warnings
+
+warnings.filterwarnings('error')
 
 class bcolors:
     HEADER = '\033[95m'
diff --git a/test/test_scripts/structural/test_deepcopying_references.py b/test/test_scripts/structural/test_deepcopying_references.py
index 2d3fc7f41ed55a947966d56388d9dbe9c3a8ccf4..99342fff049eadd6a48033b3b43169d0122ec2ba 100644
--- a/test/test_scripts/structural/test_deepcopying_references.py
+++ b/test/test_scripts/structural/test_deepcopying_references.py
@@ -44,7 +44,7 @@ kat1.o1.num_demods = 2
 assert(hasattr(kat1.o1, "f2"))
 assert(not hasattr(kat0.o1, "f2"))
 
-kat1.num_demods = 1
+kat1.o1.num_demods = 1
 
 assert(hasattr(kat1.o1, "f1"))
 
diff --git a/test/test_scripts/structural/test_lambda0_beam_param.py b/test/test_scripts/structural/test_lambda0_beam_param.py
index ac78e97028cfb9309901452a88c2fa5f906d0de0..83010b824736b4ccc364879d32122b40fd745cc7 100644
--- a/test/test_scripts/structural/test_lambda0_beam_param.py
+++ b/test/test_scripts/structural/test_lambda0_beam_param.py
@@ -26,7 +26,7 @@ yaxis abs:deg                                 # move detector frequency with xax
 kat = finesse.kat()
 kat.verbose = False
 kat.lambda0 = 1550e-9
-kat.parseKatCode(kat_code)
+kat.parseCommands(kat_code)
 kat.maxtem = 0
 kat.trace = 2
 out, T = kat.run(getTraceData=True)