classdef SsmParameters
    % $Id: SsmParameters.m 1916 2017-01-26 13:16:27Z DanielaP $
    % SsmParameters Class holding fundamental and derived parameters of the
    %   Glasgow Sagnac Speed Meter experiment
    %
    % written by Andreas Glaefke, Christian Graef and Sean Leavey
    % for the Glasgow Speed Meter group
    %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%% Change Log %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%% 1105: 9 March 2015, AG
    % Assessment of all parameters
    % Added To-dos and missing sources
    % Added GetDistance method
    % Added some comments on positions
    % Cavity Bade Width and Length are now calculated, not hard-coded
    
    %%%% 1110: 10 March 2015, AG
    % Distance method is now also able to calculate distances to
    % beamsplitters and laser
    
    %%%% 1114: 10 March 2015, AG
    % Use of distance method simplified for two distance between two
    % mirrors
    % Some mistakes corrected that I noticed when I used the class:
        % - Reflectivity of M11 corrected
        % - Position of MainLaser added
        % - Detectors added
        
    %%%% 1120: 18 March 2015, AG
    % GetDistance method simplified by using iskey method such that no
    % ElementType input is required
        
    %%%% 1178: 31 March 2015, AG
    % The parameter class now has the parameters from the Optocad model AND
    % "ideal" parameters (same as in the Finesse model). For ideal
    % parameters, use SsmParameters('ideal'). If the input is empty, the
    % class will return the Optocad parameters such that no existing models
    % are affected.

    %%%% 1226: 28 April 2015, AG
    % When the getDistance method is used in the ideal case, the ideal
    % parameters (or an error message, if the elements are not
    % neighbouring) is returned. 
    
    %%%% 1245: 30 April 2015, AG
    % The class can now be initialized using either 'ideal' or 'optocad'.
    % Any other input will yield an error message. 
    
    %%%% 1259: 01 May 2015, SL
    % Squashed potential issue where SsmParameters() did not have the same
    % behaviour as SsmParameters('optocad') (obj.SetOfParameters was then
    % case-specific)
    
    %%%% 1261: 12 May 2015, AG
    % Changed the laser power to 3.4 as it is now known that the factor of
    % 2 was _not_ a Finesse error but physical. Corrected some of the
    % descriptions in the definition of reflectivities. 
    
    %%%% 1272: 18 May 2015, AG
    % Added a warning regarding the laser power
    % Added the EOM. This was required to enable hassle-free changes of the
    % modulation depth and frequency in Finesse. 
    % Note that the EOMs do not have any properties other than m and f yet.
    % This probably has to be changed later on. 
    
    %%%% 1282: 26 May 2015, AG
    % Added a container "Distances". This is no new information but makes
    % it much easier to implement e.g. asymmetries in the Finesse model.
    % The result depends on the parameter set and in case the optocad
    % one is used it is still calculated from the positions. 

    %%%% 1361: 21 July 2015, AG
    % Added a function "override" to override parameters of certain objects
    % more easily. No error handling has been implemented yet.

    %%%% 1378: 31 July 2015, AG
    % Added a function "FinesseAngleRange" to change the angles from
    % Optocad's [-180 180] to Finesse's [-90 90] range

    %%%% 1388: 04 August 2015, AG
    % Added losses as additional parameter

    %%%% 1388: 04 August 2015, AG
    % Added losses as additional parameter
    
    %%%% 1520: 05 November 2015, AG
    % added homodyne angle
    % added function to override homodyne angle
    % added function to convert real angles to finesse angles
    
    %%%% 1521: 05 November 2015, AG
    % getDistances now works properly after overriding distances    
    
    %%%% 1534: 12 November 2015, SL
    % changed homodyne angle functionality slightly
    % moved functions to convert homodyne angles into individual simulation
    %    frameworks
    
    %%%% 1554: 20 November 2015, SL
    % added material constants to each optic, and removed the "global"
    % fused silica constants in use already
    %
    % also added 'g' to represent gravitational acceleration
    
    %%%% 1560: 23 November 2015, SL
    % added fibre geometries for test masses
    
    %%%% 1585: 22 December 2015, SL
    % updated ITM diameter and thickness as per the Coastline quote

    %%%% 1612: 10 Februar 2016, JSH
    % Changed fibre diameter of 100g suspension to 20um

    %%%% 1686: 23 June 2016, SS
    % Changed homodyne angle to 45 deg, as discussed in
    %  https://arran.physics.gla.ac.uk/wp/speedmeter/?p=5648
    
    %%%% 1724: 28 July 2016, DP
    % Updated ITMs' RoC to 8m 
    % Updated mirrors positions and M1-M2-M3 distances for the ideal mode
    % with round trip length 2.86m (Optocad output v1.3)
    
    %%%% 1729: 05 August 2016, DP
    % Changed ETMs diameter and thickness (from specification document) 
    % and (after the last update of Optocad v1.3):
    % - mirrors positions
    % - central BS diameter and thickness
    % - M9 RoC and angle of incidence
    
    %%%% 1908: 26 January 2017, DP
    % Changed ITM real reflectivity according to LMA measurements
    
    %%%% 1916: 26 January 2017, DP
    % Changed ITM ideal reflectivity according to LMA measurements
    % Changed ETM ideal and real reflectivity according to LMA measurements
    % Changed ETM thickness
    
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%    
   % To do
        % Reflectivity of steering mirrors 
        % Losses of BeamSplitters
        % Positions and possibly further properties of EOMs
        % Error handling in override function
   % No sources for
        % all of the material parameters
        
    properties (Constant=true, Hidden=true)
        %% Physical constants
        clight    = 299792458;          % Speed of light in vacuum [m/s]
        lambda0   = 1064E-9;            % Fundamental laser wavelength [m]
        hBar      = 1.054571726E-34;    % Modified Planck constant [Js]
        kB        = 1.380648813E-23;    % Boltzmann constant [J/K]
        u         = 1.660538921E-27;    % Atomic mass unit [kg]
        g         = 9.81;               % Gravitational acceleration [m/s^2]

        %% General thermal
        roomTemp  = 293.45;             % Room temperature [K]. Measurement C. Mullen, 02 Mar 2014: T~20.3C
        ETcryo = 120;
        
        %% General vacuum
        residualGasPressure = 1e-6; % Residual gas pressure of dominant contributor H20 [mbar]
        residualGasMass = 18.01528; % Residual gas mass of dominant contributor H20 [u]

        %% Materials

        %  Fused silica (bulk)
        bulkSilica = struct( ...
            'rho',          2.201/10^3*10^6, ...   % kg/m^3
            'C',            772, ...               % J/kg/K
            'k',            1.38, ...              % W/m/K, thermal conductivity
            'alpha',        3.9e-7, ...            % 1/K
            'beta',         1.52e-4, ...           % (1/K), dY/dT
            'phi',          4.1e-10, ...           % loss angle [dimensionless]
            'Y',            7.2e10, ...            % Young's modulus
            'dissdepth',    1.5e-2, ...            % 
            'sigma',        0.167, ...             % poisson ratio
            'n',            1.4496309898590634 ... % refractive index
        );
        %  Fused silica (bulk)
        bulkSilicon = struct( ...
            'rho',          2.330/10^3*10^6, ...   % kg/m^3
            'C',            328, ...               % J/kg/K
            'k',            694, ...              % W/m/K, thermal conductivity
            'alpha',        -2.8e-8, ...            % 1/K
            'beta',         -2.6e-5, ...           % (1/K), dY/dT
            'phi',          4.5e-9, ...           % loss angle [dimensionless]
            'phi_su',        5e-13, ...
            'Y',            1.67e11, ...            % Young's modulus
            'dissdepth',    1.5e-2, ...            % 
            'sigma',        0.167, ...             % poisson ratio
            'n',            1.4496309898590634 ... % refractive index
        );
        %% Fibre geometries

        % small test masses (M1a, M1b)
        fibreGeomSmallTm = struct(...
            'nFibres',              2, ...       number of fibres
            'fibreLength',          0.1, ...     fibre length [m]
            'fibreDiameter',        10e-6, ...   fibre diameter [m]
            'attVertDist',          0.0025, ...  vertical attachment point [m]
            'attHorDist',           0, ...       horizontal attachment point [m]
            'attTransDist',         0 ...        transverse attachment point [m]
        );

        % large test masses (M2a, M2b, M3a, M3b)
        fibreGeomLargeTm = struct(...
            'nFibres',              4, ...       number of fibres
            'fibreLength',          0.2, ...     fibre length [m]
            'fibreDiameter',        20e-6, ...   fibre diameter [m]
            'attVertDist',          0.0058, ...  vertical attachment point [m]
            'attHorDist',           0.00225, ... horizontal attachment point [m]
            'attTransDist',         0.02597 ...  transverse attachment point [m]
        );
    
        % ET like masses
        fibreGeomETTm = struct(...
            'nFibres',              4, ...       number of fibres
            'fibreLength',          2.0, ...     fibre length [m]
            'fibreDiameter',        3000e-6, ...   fibre diameter [m]
            'attVertDist',          0.010, ...  vertical attachment point [m]
            'attHorDist',           0.015, ... horizontal attachment point [m]
            'attTransDist',         0.225 ...  transverse attachment point [m]
        );
    end

    properties (Hidden=true)
        %% Positions
        % The positions are mostly taken from the txt-file with
        % Optocad-Results, indicating the first hit of the beam. 
        % For the beamsplitters there is some ambiguity about which position to
        % use. Currently, the HR coating position is used for the cavity input
        % mirrors. 
        % For now only one position is used to avoid interal resonance.

        posM1a       =  [ 199.984E-3 -75.1847E-3];
        posM3a       =  [ 1.52795E+0 -74.7993E-3];
        posM2a       =  [ 1.51230E+0 -278.295E-3];
        posM1b       =  [ 300.000E-3  75.000E-3];
        posM2b       =  [ 1.62814E+0  75.000E-3];
        posM3b       =  [ 1.61251E+0  278.127E-3];
        posM4        =  [-350.000E-3         0.0];
        posM5        =  [-350.000E-3  145.981E-3];
        posM6        =  [-100.000E-3  70.9807E-3];
        posM7        =  [-100.000E-3 -28.8791E-3];
        posM8        =  [-249.490E-3 -10.0965E-3];
        posM9        =  [-274.399E-3  250.000E-3];
        posM10       =  [-305.610E-3 -75.0992E-3];
        posM11       =  [-96.1000E-3  145.981E-3];
        posM12       =  [-94.5125E-3  197.058E-3];
        posM13       =  [522.763E-6   197.047E-3];
        posM14       =  [-222.981E-3  70.8997E-3];
        posM15       =  [-222.960E-3 -249.940E-3];
        posM16       =  [ 362.849E-6 -248.083E-3];
        pPhD3        =  [ 50.0000E-3 -248.086E-3];
        pPhD4        =  [-1.50593E-3 -300.000E-3];
        posLaserStab =  [ 50.0000E-3  147.853E-3];
        posMainLaser =  [-600.000E-3         0.0]; %Beam start in OptoCad
        posPhD3      =  [.05,         -248.086E-3]; 
        posPhD4      =  [-1.50593E-3,    -.30]; 

        ElementType1;
        ElementType2;

        %% EOM properties
        fRF1 = 15e6;                    % modulation frequency 1st EOM
        fRF2 = 100e6;                   % modulation frequency 2nd EOM
        midx1 = 0.3;                    % modulation index 1st EOM
        midx2 = 0;                      % modulation index 2nd EOM

        %% Substrate diameters
        % 2*rap in Optocad

        smallTM_diameter   = 0.010;   % [m] %M1a, M1b, from Coastline quote (https://arran.physics.gla.ac.uk/wp/speedmeter/wp-content/uploads/sites/3/2015/07/Q15-3060-U-of-Glasgow-4.pdf)
        largeTM_diameter   = 2*0.0244;    % [m] %M2a, M2b, M3a, M3b
        ETTM_diameter      = 0.60;
        steering_diameter  = 0.03;        % [m]
        centralBS_diameter = 0.050;       % [m]

        %% Substrate thicknesses
        % dx in Optocad

        smallTM_thickness   = 0.005;   % [m] %M1a, M1b, from Coastline quote (https://arran.physics.gla.ac.uk/wp/speedmeter/wp-content/uploads/sites/3/2015/07/Q15-3060-U-of-Glasgow-4.pdf) 
        largeTM_thickness   = 0.0254;  % [m] %M2a, M2b, M3a, M3b
        ETTM_thickness      = 0.30;
        steering_thickness  = 0.006;    % [m]
        centralBS_thickness = 0.0125;  % [m]

        %% Substrate weight
        % calculated based on thickness, diameter and mass density

        smallTM_mass;
        largeTM_mass;
        ETTM_mass;
        steering_mass;
        centralBS_mass;    

        %% Reflectivity
        % r in Optocad

        smallTM_reflectivity   = 0.999368;   %M1a, M1b
        largeTM_reflectivity   = 0.9999966;   %M2a, M2b, M3a, M3b
        steering_reflectivity  = 1;         % Must be replaced by more realistic value
        M9_reflectivity        = 0.99;      % In contrast to the other mirrors R=1 cannot be used in the MIST model as otherwise the Michelson measurement at M9 is not possible. 
        centralBS_reflectivity = 0.5;       % Symmetric BS (M6, M11)
        BHDBS_reflectivity     = 0.5;       % Output BS for BHD

        %% Loss
        smallTM_Loss   = 0;       % M1a, M1b
        largeTM_Loss   = 0;       % M2a, M2b, M3a, M3b
        steering_Loss  = 0;         
        M9_Loss        = 0;      
        centralBS_Loss = 0;       % symmetric BS (M6, M11)
        BHDBS_Loss     = 0;       % output BS for BHD

        %% Radii of Curvature
        % rc in Optocad

        smallTM_ROC   = -8; % concave radius of curvature of mirrors M1a, M1b
        largeTM_ROC   = inf;   % most mirrors are flat
        steering_ROC  = inf;
        M9_ROC        = -5.12; % concave radius of curvature of mirror M9
        centralBS_ROC = inf;

        %% Cavity Properties
        % Do not hard-code, but calculate from mirror positions

        cavityMedianLength;% = 1.3; % cl in Optocad
        cavityBaseWidth;% = 0.1;    % cb in Optocad
        cavityOpeningAngle;         % ca in optocad

        %% angle of the normal
        % specifying the angle, in degrees, of the normal on the surface at its
        % apex with respect to the positive x-axis.

        angleITM; % M1a/b
        angleETM; % M2a/b, M3a/b
        angleM4           = -45;
        angleM5           = 45;
        angleM6           = -45;
        angleM7;
        angleM8;
        angleM9           = -5.48; % from OptoCad, v1.3
        angleM10;
        angleM11          = 45;
        angleM12          = 45;
        angleM13          = 45;
        angleM14          = -45;
        angleM15          = -45;
        angleM16          = 45;

    %% Laser Properties
        LaserPower = 3.4; % input laser power
    end

    properties
        % Homodyne angle
        HomodyneAngle = 45;

        Mirror = containers.Map()
        BS = containers.Map()
        Laser = containers.Map()
        Detector = containers.Map()
        EOM = containers.Map()   
        Distances = containers.Map()
        constants = struct();
        thermal = struct()
        vacuum = struct();
        idealDistances = struct();
       % HomodyneAngleStruct = struct();
        SetOfParameters;
    end

    methods 
        function obj = SsmParameters(varargin)
            % This is the class constructor: use to initialise properties
            if isempty(varargin) % If input empty, return Optocad parameters
                obj.SetOfParameters = 'optocad';
                disp('You are now using the Optocad parameter class. Write SsmParameters("ideal") for the ideal parameter class');
            else
                if ~strcmp(varargin,'ideal') && ~strcmp(varargin,'optocad')
                    error('Choose either ideal or optocad');
                else
                    obj.SetOfParameters = varargin{1};
                end
            end

            warning('P_in is set to %sW for technical reasons. Note that the SSM design assumes P_in=1.7W.', obj.LaserPower)
            warning('Some angles of incidence are changed by 180 degrees to be in the range that Finesse uses');

            obj.constants = struct(...
                'c',        obj.clight, ...
                'lambda0',  obj.lambda0, ...
                'hBar',     obj.hBar, ...
                'kB',       obj.kB, ...
                'u',        obj.u, ...
                'g',        obj.g ...
            );

            obj.constants.meta = struct(...
                'c', 'Speed of light in vacuum [m/s]', ...
                'lambda0', 'Fundamental laser wavelength [m]', ...
                'hBar',    'Modified Planck constant [Js]', ...
                'kB',      'Boltzmann constant [J/K]', ...
                'u',       'Atomic mass unit [kg]', ...
                'g',       'Gravitational acceleration [m/s^2]' ...
            );

            obj.thermal = struct(...
                'roomTemp', obj.roomTemp, ...
                'ETcryo', obj.ETcryo ...
            );

            obj.thermal.meta = struct(...
                'roomTemp', 'Room temperature [K]', ...
                'ETcryo', 'Cryo temperature [K]' ...
            );

            obj.vacuum = struct(...
                'residualGasPressure', obj.residualGasPressure, ...
                'residualGasMass', obj.residualGasMass...
            );

            obj.vacuum.meta = struct(...
                'residualGasPressure', 'Residual gas pressure of dominant contributor H20 [mbar]', ...
                'residualGasMass', 'Residual gas mass of dominant contributor H20 [u]'...
            );

            obj.cavityBaseWidth    = pdist([obj.posM2a;obj.posM3a]);

            obj.cavityMedianLength = sqrt((pdist([obj.posM1a;obj.posM2a]))^2-...
                                        (obj.cavityBaseWidth/2)^2);

            obj.cavityOpeningAngle = radtodeg(atan(obj.cavityBaseWidth/...
                obj.cavityMedianLength));

            obj.angleETM = (180.0-obj.cavityOpeningAngle)/4.0;
            obj.angleITM = 0.5 * obj.cavityOpeningAngle;
            obj.angleM10 = -45.0 - 0.5*obj.angleM9;
            obj.angleM8 = 45.0 - 0.5*obj.angleM9 - obj.angleITM;
            obj.angleM7 = -(45.0 + obj.angleITM);

            materialSmallTestMass = obj.bulkSilica;
            materialLargeTestMass = obj.bulkSilica;
            materialETMass = obj.bulkSilicon;
            materialSteeringMass = obj.bulkSilica;
            materialCentralBeamSplitter = obj.bulkSilica;

            obj.smallTM_mass = (obj.smallTM_diameter/2)^2*pi*...
                obj.smallTM_thickness*materialSmallTestMass.rho;
            
            obj.ETTM_mass = (obj.ETTM_diameter/2)^2*pi*...
                obj.ETTM_thickness*materialETMass.rho;

            obj.largeTM_mass = (obj.largeTM_diameter/2)^2*pi*...
                obj.largeTM_thickness*materialLargeTestMass.rho;

            obj.steering_mass = (obj.steering_diameter/2)^2*pi*...
                obj.steering_thickness*materialSteeringMass.rho;        

            obj.centralBS_mass = (obj.centralBS_diameter/2)^2*pi*...
                obj.centralBS_thickness*materialCentralBeamSplitter.rho;

            ETTm = struct(...
                'position',      obj.posM1a, ...
                'diameter',      obj.ETTM_diameter, ...
                'thickness',     obj.ETTM_thickness, ...
                'R',             obj.smallTM_reflectivity, ...
                'Loss',          obj.smallTM_Loss, ...
                'ROC',           obj.smallTM_ROC, ...
                'angle',         obj.angleITM, ...
                'mass',          obj.ETTM_mass, ...
                'bulkmaterial',  materialETMass, ...
                'fibregeometry', obj.fibreGeomETTm ...
            );
        
            M1a = struct(...
                'position',      obj.posM1a, ...
                'diameter',      obj.smallTM_diameter, ...
                'thickness',     obj.smallTM_thickness, ...
                'R',             obj.smallTM_reflectivity, ...
                'Loss',          obj.smallTM_Loss, ...
                'ROC',           obj.smallTM_ROC, ...
                'angle',         obj.angleITM, ...
                'mass',          obj.smallTM_mass, ...
                'bulkmaterial',  materialSmallTestMass, ...
                'fibregeometry', obj.fibreGeomSmallTm ...
            );
            M1a = obj.FinesseAngleRange(M1a);

            M1b = struct(...
                'position',      obj.posM1b, ...
                'diameter',      obj.smallTM_diameter, ...
                'thickness',     obj.smallTM_thickness, ...
                'R',             obj.smallTM_reflectivity, ...
                'Loss',          obj.smallTM_Loss, ...                     
                'ROC',           obj.smallTM_ROC, ...
                'angle',         obj.angleITM, ...
                'mass',          obj.smallTM_mass, ...
                'bulkmaterial',  materialSmallTestMass, ...
                'fibregeometry', obj.fibreGeomSmallTm ...
            );
            M1b = obj.FinesseAngleRange(M1b);                   

            M2a = struct(...
                'position',      obj.posM2a, ...
                'diameter',      obj.largeTM_diameter, ...
                'thickness',     obj.largeTM_thickness, ...
                'R',             obj.largeTM_reflectivity, ...
                'Loss',          obj.largeTM_Loss, ...                     
                'ROC',           obj.largeTM_ROC, ...
                'angle',         obj.angleETM, ...
                'mass',          obj.largeTM_mass, ...
                'bulkmaterial',  materialLargeTestMass, ...
                'fibregeometry', obj.fibreGeomLargeTm ...
            );
            M2a = obj.FinesseAngleRange(M2a);

            M2b = struct(...
                'position',      obj.posM2b, ...
                'diameter',      obj.largeTM_diameter, ...
                'thickness',     obj.largeTM_thickness, ...
                'R',             obj.largeTM_reflectivity, ...
                'Loss',          obj.largeTM_Loss, ...                     
                'ROC',           obj.largeTM_ROC, ...
                'angle',         obj.angleETM, ...
                'mass',          obj.largeTM_mass, ...
                'bulkmaterial',  materialLargeTestMass, ...
                'fibregeometry', obj.fibreGeomLargeTm ...
            );
            M2b = obj.FinesseAngleRange(M2b);             

            M3a = struct(...
                'position',      obj.posM3a, ...
                'diameter',      obj.largeTM_diameter, ...
                'thickness',     obj.largeTM_thickness, ...
                'R',             obj.largeTM_reflectivity, ...
                'Loss',          obj.largeTM_Loss, ...                     
                'ROC',           obj.largeTM_ROC, ...
                'angle',         obj.angleETM, ...
                'mass',          obj.largeTM_mass, ...
                'bulkmaterial',  materialLargeTestMass, ...
                'fibregeometry', obj.fibreGeomLargeTm ...
            );
            M3a = obj.FinesseAngleRange(M3a);

            M3b = struct(...
                'position',      obj.posM3b, ...
                'diameter',      obj.largeTM_diameter, ...
                'thickness',     obj.largeTM_thickness, ...
                'R',             obj.largeTM_reflectivity, ...
                'Loss',          obj.largeTM_Loss, ...                     
                'ROC',           obj.largeTM_ROC, ...
                'angle',         obj.angleETM, ...
                'mass',          obj.largeTM_mass, ...
                'bulkmaterial',  materialLargeTestMass, ...
                'fibregeometry', obj.fibreGeomLargeTm ...
            );
            M3b = obj.FinesseAngleRange(M3b);

            M4 = struct(...
                'position',     obj.posM4, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...
                'Loss',         obj.steering_Loss, ...                      
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM4, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );
            M4 = obj.FinesseAngleRange(M4);                 

            M5 = struct(...
                'position',     obj.posM5, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...   
                'Loss',         obj.steering_Loss, ...                           
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM5, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );
            M5 = obj.FinesseAngleRange(M5);

            M6 = struct(...
                'position',     obj.posM6, ...
                'diameter',     obj.centralBS_diameter, ...
                'thickness',    obj.centralBS_thickness, ...
                'R',            obj.centralBS_reflectivity, ...     
                'Loss',         obj.centralBS_Loss, ...                          
                'ROC',          obj.centralBS_ROC, ...
                'angle',        obj.angleM6, ...
                'mass',         obj.centralBS_mass, ...
                'bulkmaterial', materialCentralBeamSplitter ...
            );
            M6 = obj.FinesseAngleRange(M6);                 

            M7 = struct(...
                'position',     obj.posM7, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...   
                'Loss',         obj.steering_Loss, ...                        
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM7, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );
            M7 = obj.FinesseAngleRange(M7);                 

            M8 = struct(...
                'position',     obj.posM8, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...    
                'Loss',         obj.steering_Loss, ...                          
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM8, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );
            M8 = obj.FinesseAngleRange(M8);                 

            M9 = struct(...
                'position',     obj.posM9, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...   
                'Loss',         obj.steering_Loss, ...                        
                'ROC',          obj.M9_ROC, ...
                'angle',        obj.angleM9, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );       
            M9 = obj.FinesseAngleRange(M9);                 

            M10 = struct(...
                'position',     obj.posM10, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...  
                'Loss',         obj.steering_Loss, ...                        
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM10, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );
            M10 = obj.FinesseAngleRange(M10);                

            M11 = struct(...
                'position',     obj.posM11, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.centralBS_reflectivity, ... 
                'Loss',         obj.centralBS_Loss, ...                       
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM11, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );   
            M11 = obj.FinesseAngleRange(M11);                 

            M12 = struct(...
                'position',     obj.posM12, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...     
                'Loss',         obj.steering_Loss, ...                          
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM12, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );    
            M12 = obj.FinesseAngleRange(M12);                 

            M13 = struct(...
                'position',     obj.posM13, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...  
                'Loss',         obj.steering_Loss, ...                       
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM13, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );   
            M13 = obj.FinesseAngleRange(M13);                 

            M14 = struct(...
                'position',     obj.posM14, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ...   
                'Loss',         obj.steering_Loss, ...                        
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM14, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );
            M14 = obj.FinesseAngleRange(M14);                 

            M15 = struct(...
                'position',     obj.posM15, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.steering_reflectivity, ... 
                'Loss',         obj.steering_Loss, ...                       
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM15, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
            );  
            M15 = obj.FinesseAngleRange(M15);                 

            M16 = struct(...
                'position',     obj.posM16, ...
                'diameter',     obj.steering_diameter, ...
                'thickness',    obj.steering_thickness, ...
                'R',            obj.BHDBS_reflectivity, ...   
                'Loss',         obj.BHDBS_Loss, ...                        
                'ROC',          obj.steering_ROC, ...
                'angle',        obj.angleM16, ...
                'mass',         obj.steering_mass, ...
                'bulkmaterial', materialSteeringMass ...
                );
            M16 = obj.FinesseAngleRange(M16);                 

            PhD3 = struct(...
                'position',  obj.posPhD3 ...
            );

            PhD4 = struct(...
                'position',  obj.posPhD4 ...
            );

            MainLaser = struct(...
                'P', obj.LaserPower, ...
                'position', obj.posMainLaser...
            );

            LaserStab = struct(...
                'position', obj.posLaserStab, ...
                'P', obj.LaserPower ...
            );

            EOM1 = struct(...
                'midx',  obj.midx1, ...
                'fRF',  obj.fRF1 ...
            );

            EOM2 = struct(...
                'midx',  obj.midx2, ...
                'fRF',  obj.fRF2 ...
            );

            if strcmp(obj.SetOfParameters, 'ideal')
                % Create structure with all the ideal distances
                obj.idealDistances = struct(...
                  'lrs2', 250.E-3       ,...  % input -> M4
                  'lrs3', 166.92366E-3, ...   % M4 -> M5
                  'lrs4', 258.0114E-3  , ...  % M5 -> M11
                  'lrs5', 72.09037552E-3 , ...% M11 -> M6
                  'lrs7', 95.75258782E-3 , ...% M6 -> M7 
                  'lrs8', 297.0895521E-3 , ...% M7 -> M1a 
                  'lrs10', 1.3242         , ...% M1a -> M2a
                  'lrs11', 0.2037           ,... % M2a -> M3a
                  'lrs12', 1.3242         ,... % M3a -> M1a
                  'lrs17', 365.5666524E-3, ...% M1b -> M6
                  'lrs19', 1.3242         , ...% M1b -> M2b
                  'lrs20', 0.2037           , ...% M2b -> M3b
                  'lrs21', 1.3242         , ...% M3b -> M1b
                  'lrs23', 499.2783443E-3, ...% M1a -> M10
                  'lrs24', 326.5506374E-3, ...% M10 -> M9
                  'lrs25', 261.2520758E-3, ...% M9 -> M8
                  'lrs26', 549.7129644E-3);   % M8 -> M1b

                % Use same reflectivities as in the Finesse model
                M1a.R = 0.999368;
                M2a.R = 0.9999966;
                M3a.R = 0.9999966;
                M1b.R = 0.999368;
                M2b.R = 0.9999966;
                M3b.R = 0.9999966;
            end
            
            obj.Mirror('ETTm') = ETTm;
            obj.Mirror('M1a') = M1a;
            obj.Mirror('M1b') = M1b;
            obj.Mirror('M2a') = M2a;
            obj.Mirror('M2b') = M2b;
            obj.Mirror('M3a') = M3a;
            obj.Mirror('M3b') = M3b;
            obj.Mirror('M4') = M4;
            obj.Mirror('M5') = M5;
            obj.Mirror('M7') = M7;
            obj.Mirror('M8') = M8;
            obj.Mirror('M9') = M9;
            obj.Mirror('M10') = M10;
            obj.Mirror('M12') = M12;
            obj.Mirror('M13') = M13;
            obj.Mirror('M14') = M14;
            obj.Mirror('M15') = M15;

            obj.BS('M6') = M6;
            obj.BS('M11') = M11;
            obj.BS('M16') = M16;

            obj.Laser('MainLaser') = MainLaser;
            obj.Laser('LaserStab') = LaserStab;

            obj.Detector('PhD3') = PhD3;
            obj.Detector('PhD4') = PhD4;

            obj.EOM('EOM1') = EOM1;
            obj.EOM('EOM2') = EOM2;
            
            % Ray segments
            rs2 = struct(...
                'l', obj.getDistance('MainLaser','M4'), ...
                'description', 'Input -> M4' ...
            );
            rs3 = struct(...
                'l', obj.getDistance('M4','M5'), ...
                'description', '% M4 -> M5' ...
            );
            rs4 = struct(...
                'l', obj.getDistance('M11','M5'), ...
                'description', 'M5 -> M11' ...
            );
            rs5 = struct(...
                'l', obj.getDistance('M11','M6'), ...
                'description', 'M11 -> M6' ...
            );
            rs7 = struct(...
                'l', obj.getDistance('M6','M7'), ...
                'description', 'M6 -> M7' ...
            );
            rs8 = struct(...
                'l', obj.getDistance('M1a','M7'), ...
                'description', 'M7 -> M1a' ...
            );
            rs10 = struct(...
                'l', obj.getDistance('M1a','M2a'), ...
                'description', 'M1a -> M2a' ...
            );
            rs11 = struct(...
                'l', obj.getDistance('M2a','M3a'), ...
                'description', 'M2a -> M3a' ...
            );
            rs12 = struct(...
                'l', obj.getDistance('M3a','M1a'), ...
                'description', 'M3a -> M1a' ...
            );
            rs17 = struct(...
                'l', obj.getDistance('M1b','M6'), ...
                'description', 'M1b -> M6' ...
            );
            rs19 = struct(...
                'l', obj.getDistance('M1b','M2b'), ...
                'description', 'M1b -> M2b' ...
            );
            rs20 = struct(...
                'l', obj.getDistance('M3b','M2b'), ...
                'description', 'M2b -> M3b' ...
            );
            rs21 = struct(...
                'l', obj.getDistance('M1b','M3b'), ...
                'description', 'M3b -> M1b' ...
            );
            rs23 = struct(...
                'l', obj.getDistance('M1a','M10'), ...
                'description', 'M1a -> M10' ...
            );
            rs24 = struct(...
                'l', obj.getDistance('M9','M10'), ...
                'description', 'M10 -> M9' ...
            );
            rs25 = struct(...
                'l', obj.getDistance('M9','M8'), ...
                'description', 'M9 -> M8' ...
            );
            rs26 = struct(...
                'l', obj.getDistance('M1b','M8'), ...
                'description', 'M8 -> M1b' ...
            );
        
            % Set distances using calculated ray segments
            obj.Distances('rs2') = rs2;
            obj.Distances('rs3') = rs3;
            obj.Distances('rs4') = rs4;
            obj.Distances('rs5') = rs5;
            obj.Distances('rs7') = rs7;
            obj.Distances('rs8') = rs8;
            obj.Distances('rs10') = rs10;
            obj.Distances('rs11') = rs11;
            obj.Distances('rs12') = rs12;
            obj.Distances('rs17') = rs17;
            obj.Distances('rs19') = rs19;
            obj.Distances('rs20') = rs20;
            obj.Distances('rs21') = rs21;
            obj.Distances('rs23') = rs23;
            obj.Distances('rs24') = rs24;
            obj.Distances('rs25') = rs25;
            obj.Distances('rs26') = rs26;
        end

        %% Function to check AOI range
        function StructWithCorrectedAOI = FinesseAngleRange(obj,OpticalElement)
                if OpticalElement.angle<-90
                    OpticalElement.angle = OpticalElement.angle+180;
                else
                    if OpticalElement.angle>90
                        OpticalElement.angle = OpticalElement.angle-180;
                    end
                end

                StructWithCorrectedAOI = OpticalElement;
        end

        %% Function to override parameters
        function NewPFile = override(obj, object, property, NewValue)
           if obj.Mirror.isKey(object)            
              a = obj.Mirror(object);
              switch property
                  case 'position' 
                      a.position = NewValue;
                      obj.Mirror(object) = a;
                      NewPFile = obj;
                  case 'diameter' 
                      a.diameter = NewValue;
                      obj.Mirror(object) = a;
                      NewPFile = obj;
                  case 'thickness' 
                      a.thickness = NewValue;
                      obj.Mirror(object) = a;
                      NewPFile = obj;
                  case 'R' 
                      a.R = NewValue;
                      obj.Mirror(object) = a;
                      NewPFile = obj;
                  case 'ROC' 
                      a.ROC = NewValue;
                      obj.Mirror(object) = a;
                      NewPFile = obj;
                  case 'angle' 
                      a.angle = NewValue;
                      obj.Mirror(object) = a;
                      NewPFile = obj;
                  case 'mass' 
                      a.mass = NewValue;
                      obj.Mirror(object) = a;
                      NewPFile = obj;                
              end
           elseif obj.BS.isKey(object)            
              a = obj.BS(object);
              switch property
                  case 'position' 
                      a.position = NewValue;
                      obj.BS(object) = a;
                      NewPFile = obj;
                  case 'diameter' 
                      a.diameter = NewValue;
                      obj.BS(object) = a;
                      NewPFile = obj;
                  case 'thickness' 
                      a.thickness = NewValue;
                      obj.BS(object) = a;
                      NewPFile = obj;
                  case 'R' 
                      a.R = NewValue;
                      obj.BS(object) = a;
                      NewPFile = obj;
                  case 'ROC' 
                      a.ROC = NewValue;
                      obj.BS(object) = a;
                      NewPFile = obj;
                  case 'angle' 
                      a.angle = NewValue;
                      obj.BS(object) = a;
                      NewPFile = obj;
                  case 'mass' 
                      a.mass = NewValue;
                      obj.BS(object) = a;
                      NewPFile = obj;              
              end
           elseif obj.Laser.isKey(object)            
              a = obj.Laser(object);
              switch property
                  case 'position' 
                      a.position = NewValue;
                      obj.Laser(object) = a;
                      NewPFile = obj;
                  case 'P' 
                      a.P = NewValue;
                      obj.Laser(object) = a;
                      NewPFile = obj;
              end
           elseif obj.Detector.isKey(object)   
              a = obj.Detector(object);           
              switch property
                  case 'position' 
                      a.position = NewValue;
                      obj.Detector(object) = a;
                      NewPFile = obj;
              end
           elseif obj.EOM.isKey(object)    
              a = obj.EOM(object);           
              switch property
                  case 'midx' 
                      a.midx = NewValue;
                      obj.EOM(object) = a;
                      NewPFile = obj;
                  case 'fRF' 
                      a.fRF = NewValue;
                      obj.EOM(object) = a;
                      NewPFile = obj;                  
              end
    %        
    %        if obj.Distances.isKey(m1)   
    %           a = obj.Distances(m1);          
    %           switch property
    %               case 'l' 
    %                   a.l = NewValue;
    %                   obj.Distances(m1) = a;
    %                   NewPFile = obj;
    %           end
    %        end       

           elseif obj.Distances.isKey(object)   
              a = obj.Distances(object);          
              switch property
                  case 'l' 
                      a.l = NewValue;
                      obj.Distances(object) = a;
                      switch object
                          case 'rs2'
                              obj.idealDistances.lrs2 = NewValue;
                          case 'rs3'
                              obj.idealDistances.lrs3 = NewValue;
                          case 'rs4'
                              obj.idealDistances.lrs4 = NewValue;
                          case 'rs5'
                              obj.idealDistances.lrs5 = NewValue;
                          case 'rs7'
                              obj.idealDistances.lrs7 = NewValue;
                          case 'rs8'
                              obj.idealDistances.lrs8 = NewValue;
                          case 'rs10'
                              obj.idealDistances.lrs10 = NewValue;
                          case 'rs11'
                              obj.idealDistances.lrs11 = NewValue;
                          case 'rs12'
                              obj.idealDistances.lrs12 = NewValue;
                          case 'rs17'
                              obj.idealDistances.lrs17 = NewValue;
                          case 'rs19'
                              obj.idealDistances.lrs19 = NewValue;
                          case 'rs20'
                              obj.idealDistances.lrs20 = NewValue;
                          case 'rs21'
                              obj.idealDistances.lrs21 = NewValue;
                          case 'rs23'
                              obj.idealDistances.lrs23 = NewValue;
                          case 'rs24'
                              obj.idealDistances.lrs24 = NewValue;
                          case 'rs25'
                              obj.idealDistances.lrs25 = NewValue;
                          case 'rs26'
                              obj.idealDistances.lrs26 = NewValue;
                          otherwise
                               warning('Changing the length does not change the affect results of getDistance! Something went wrong')
                      end
                      NewPFile = obj;
              end
           elseif strcmp(object, 'HomodyneAngle')
                obj.HomodyneAngle = NewValue;
                NewPFile = obj;
           else
                error('Specified object to be overridden not identified');
           end
        end

        %% Function getDistance 
        function d = getDistance(obj,m1, m2)
            % Calculates the distance between two elements of the SSM
            % Input: m1&m2 - Name of the optical element
            % Output: d - euclidean distance        
            if strcmp(obj.SetOfParameters,'ideal')
               switch m1
                   case 'MainLaser'
                       switch m2
                           case 'M4'
                               d = obj.idealDistances.lrs2;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end
                   case 'M4'
                       switch m2
                           case 'MainLaser'
                               d = obj.idealDistances.lrs2;
                           case 'M5'
                               d = obj.idealDistances.lrs3;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end
                   case 'M5'
                       switch m2
                           case 'M4'
                               d = obj.idealDistances.lrs3;
                           case 'M11'
                               d = obj.idealDistances.lrs4;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end
                   case 'M11'
                       switch m2
                           case 'M5'
                               d = obj.idealDistances.lrs4;
                           case 'M6'
                               d = obj.idealDistances.lrs5;
                           case 'M16'
                               d = 1e-9; % as in Finesse
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end
                   case 'M6'
                       switch m2
                           case 'M7'
                               d = obj.idealDistances.lrs7;
                           case 'M11'
                               d = obj.idealDistances.lrs5;
                           case 'M1b'
                               d = obj.idealDistances.lrs17;
                           case 'M16'
                               d = 1; % as in Finesse!
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end
                   case 'M7'
                       switch m2
                           case 'M6'
                               d = obj.idealDistances.lrs7;
                           case 'M1a'
                               d = obj.idealDistances.lrs8;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end
                   case 'M1a'
                       switch m2
                           case 'M2a'
                               d = obj.idealDistances.lrs10;
                           case 'M3a'
                               d = obj.idealDistances.lrs12;
                           case 'M7'
                               d = obj.idealDistances.lrs8;
                           case 'M10'
                               d = obj.idealDistances.lrs23;                           
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end
                   case 'M2a'
                       switch m2
                           case 'M1a'
                               d = obj.idealDistances.lrs10;
                           case 'M3a'
                               d = obj.idealDistances.lrs11;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end      
                   case 'M3a'
                       switch m2
                           case 'M1a'
                               d = obj.idealDistances.lrs12;
                           case 'M2a'
                               d = obj.idealDistances.lrs11;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end 
                   case 'M1b'
                       switch m2
                           case 'M6'
                               d = obj.idealDistances.lrs17;
                           case 'M2b'
                               d = obj.idealDistances.lrs19;
                           case 'M3b'
                               d = obj.idealDistances.lrs21;
                           case 'M8'
                               d = obj.idealDistances.lrs26;                                                   
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end 
                   case 'M2b'
                       switch m2
                           case 'M1b'
                               d = obj.idealDistances.lrs19;
                           case 'M3b'
                               d = obj.idealDistances.lrs20;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end       
                   case 'M3b'
                       switch m2
                           case 'M1b'
                               d = obj.idealDistances.lrs21;
                           case 'M2b'
                               d = obj.idealDistances.lrs20;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end                    
                   case 'M10'
                       switch m2
                           case 'M1a'
                               d = obj.idealDistances.lrs23;
                           case 'M9'
                               d = obj.idealDistances.lrs24;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end                    
                   case 'M9'
                       switch m2
                           case 'M10'
                               d = obj.idealDistances.lrs24;
                           case 'M8'
                               d = obj.idealDistances.lrs25;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end                    
                   case 'M8'
                       switch m2
                           case 'M1b'
                               d = obj.idealDistances.lrs26;
                           case 'M9'
                               d = obj.idealDistances.lrs25;
                           otherwise
                               error('%s is not connected to %s', m1, m2);
                       end      
                   case 'M16' % aka dHD
                       switch m2
                           case 'M6'
                               d = 1; % as in Finesse
                           case 'M11'
                               d = 1e-9; % as in Finesse
                           otherwise
                               error('Please use neighbouring objects when using the ideal set of parameter');
                       end                                                          
               end
            else
                if obj.Mirror.isKey(m1)                                     
                    if obj.Mirror.isKey(m2)            
                        d = pdist([obj.Mirror(m1).position; obj.Mirror(m2).position]);
                    elseif obj.BS.isKey(m2)            
                        d = pdist([obj.Mirror(m1).position; obj.BS(m2).position]);                        
                    elseif obj.Laser.isKey(m2)            
                        d = pdist([obj.Mirror(m1).position; obj.Laser(m2).position]); 
                    elseif obj.Detector.isKey(m2)            
                        d = pdist([obj.Mirror(m1).position; obj.Detector(m2).position]); 
                    else
                        error('%s is not connected to %s', m1, m2);
                    end
                elseif obj.BS.isKey(m1)         
                    if obj.Mirror.isKey(m2)
                        d = pdist([obj.BS(m1).position; obj.Mirror(m2).position]);
                    elseif obj.BS.isKey(m2)            
                        d = pdist([obj.BS(m1).position; obj.BS(m2).position]);                        
                    elseif obj.Laser.isKey(m2)            
                        d = pdist([obj.BS(m1).position; obj.Laser(m2).position]); 
                    elseif obj.Detector.isKey(m2)
                        d = pdist([obj.BS(m1).position; obj.Detector(m2).position]);
                    else
                        error('%s is not connected to %s', m1, m2);
                    end
                elseif obj.Laser.isKey(m1)
                    if obj.Mirror.isKey(m2)
                        d = pdist([obj.Laser(m1).position; obj.Mirror(m2).position]);
                    elseif obj.BS.isKey(m2)
                        d = pdist([obj.Laser(m1).position; obj.BS(m2).position]);                        
                    elseif obj.Laser.isKey(m2)            
                        d = pdist([obj.Laser(m1).position; obj.Laser(m2).position]); 
                    elseif obj.Detector.isKey(m2)            
                        d = pdist([obj.Laser(m1).position; obj.Detector(m2).position]); 
                    else
                        error('%s is not connected to %s', m1, m2);
                    end
                elseif obj.Detector.isKey(m1)
                    if obj.Mirror.isKey(m2)
                        d = pdist([obj.Detector(m1).position; obj.Mirror(m2).position]);
                    elseif obj.BS.isKey(m2)
                        d = pdist([obj.Detector(m1).position; obj.BS(m2).position]);
                    elseif obj.Laser.isKey(m2)
                        d = pdist([obj.Detector(m1).position; obj.Laser(m2).position]); 
                    elseif obj.Detector.isKey(m2)
                        d = pdist([obj.Detector(m1).position; obj.Detector(m2).position]);
                    else
                        error('%s is not connected to %s', m1, m2);
                    end
                else
                    error('Specified first key, %s, does not match any available component', m1);
                end
            end
        end
    end
end
