classdef Oven < Simulator.MOTCaptureProcess & matlab.mixin.Copyable

    properties (Access = private)
        
        OvenDefaults         = struct('NozzleLength',                                          60e-3, ...
                                      'NozzleRadius',                                        2.60e-3, ...
                                      'OvenTemperature',                                        1000);
    end
    
    properties (Access = public)
        NozzleLength;
        NozzleRadius;
        OvenTemperature;
        
        VelocityCutoff;
        ClausingFactor;
        ReducedClausingFactor;
        ReducedFlux;
    end
    
    properties (Dependent)
        ExitDivergence;
        Beta;
        OvenDistance;
        OvenTemperatureinKelvin;
        AverageVelocity;
        AtomicBeamDensity;
        MeanFreePath;
        CollisionTime;
    end
    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %- Methods
    
    methods
        function this  = Oven(varargin)
            this@Simulator.MOTCaptureProcess('SimulationMode', '2D', varargin{:});
            this.NozzleLength               = this.OvenDefaults.NozzleLength;
            this.NozzleRadius               = this.OvenDefaults.NozzleRadius; 
            this.OvenTemperature            = this.OvenDefaults.OvenTemperature;
            this.ClausingFactor             = this.calculateClausingFactor();
            [this.ReducedClausingFactor, ~] = this.calculateReducedClausingFactor();
        end
        
        function restoreDefaults(this)
            this.NozzleLength               = this.OvenDefaults.NozzleLength;
            this.NozzleRadius               = this.OvenDefaults.NozzleRadius; 
            this.OvenTemperature            = this.OvenDefaults.OvenTemperature;
            this.ClausingFactor             = this.calculateClausingFactor();
            [this.ReducedClausingFactor, ~] = this.calculateReducedClausingFactor();
        end
        
    end % - lifecycle    

    methods
        function set.NozzleLength(this,val)
            this.NozzleLength = val;
        end
        function ret = get.NozzleLength(this)
            ret = this.NozzleLength;
        end
        function set.NozzleRadius(this,val)
            this.NozzleRadius = val;
        end
        function ret = get.NozzleRadius(this)
            ret = this.NozzleRadius;
        end
        function set.OvenTemperature(this,val)
            this.OvenTemperature = val;
        end
        function ret = get.OvenTemperature(this)
            ret = this.OvenTemperature;
        end
        function set.VelocityCutoff(this,val)
            this.VelocityCutoff = val;
        end
        function ret = get.VelocityCutoff(this)
            ret = this.VelocityCutoff;
        end
        function set.ClausingFactor(this,val)
            this.ClausingFactor = val;
        end
        function ret = get.ClausingFactor(this)
            ret = this.ClausingFactor;
        end
        function set.ReducedClausingFactor(this,val)
            this.ReducedClausingFactor = val;
        end
        function ret = get.ReducedClausingFactor(this)
            ret = this.ReducedClausingFactor;
        end
        function set.ReducedFlux(this,val)
            this.ReducedFlux = val;
        end
        function ret = get.ReducedFlux(this)
            ret = this.ReducedFlux;
        end
    end % - setters and getters
    
    methods
        function ret       = get.Beta(this)
            ret = 2 * (this.NozzleRadius/this.NozzleLength);
        end 
        
        function ret       = get.OvenDistance(this)
            ApertureCut    = max(2.5e-3,this.NozzleRadius);
            ret            = (25+12.5)*1e-3 + (this.NozzleRadius + ApertureCut) / tan(15/360 * 2 * pi);
        end                               
        
        function ret       = get.ExitDivergence(this)
           Theta_Nozzle    = atan((this.NozzleRadius + 0.035/sqrt(2))/this.OvenDistance); % The angle of capture region towards the oven nozzle
           Theta_Aperture  = 15/360*2*pi; % The limitation angle of the second aperture in the oven
           ret             = min(Theta_Nozzle,Theta_Aperture);  
        end 
        
        function ret       = get.OvenTemperatureinKelvin(this)
            ret = this.OvenTemperature + Helper.PhysicsConstants.ZeroKelvin;
        end
        
        function ret       = get.AverageVelocity(this)
            %See Background collision probability section in Barbiero
            ret = sqrt((8 * pi * Helper.PhysicsConstants.BoltzmannConstant*this.OvenTemperatureinKelvin)/ (9 * Helper.PhysicsConstants.Dy164Mass));
        end
        
        function ret       = get.AtomicBeamDensity(this)
           %See Background collision probability section in Barbiero 
           ret  = this.calculateFreeMolecularRegimeFlux / (this.AverageVelocity * pi * (this.NozzleRadius)^2);
        end
            
        function ret       = get.MeanFreePath(this)
            % Cross section  = pi ( 2 * Van-der-waals radius of Dy)^2;
            % Van-der-waals radius of Dy = 281e-12 
            %See Expected atomic flux section and Background collision probability section in Barbiero
            ret = 1/(sqrt(2) * (pi * (2*281e-12)^2) * this.AtomicBeamDensity);  
        end
        
        function ret       = get.CollisionTime(this)
            ret = 3 * this.MeanFreePath/this.AverageVelocity; %See Background collision probability section in Barbiero 
        end
        
    end % - getters for dependent properties    
        
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %- Methods
    
    methods(Access = protected)
        function cp = copyElement(this)
            % Shallow copy object
            cp = copyElement@matlab.mixin.Copyable(this);
            
            % Forces the setter to redefine the function handles to the new copied object
            
            pl = properties(this);
            for k = 1:length(pl)
                sc = superclasses(this.(pl{k}));
                if any(contains(sc,{'matlab.mixin.Copyable'}))
                    cp.(pl{k}) = this.(pl{k}).copy();
                end
            end
        end
    end
    
    methods (Static)
        
        % Creates an Instance of Class, ensures singleton behaviour (that there
        % can only be one Instance of this class
        function singleObj = getInstance(varargin)
            % Creates an Instance of Class, ensures singleton behaviour
            persistent localObj;
            if isempty(localObj) || ~isvalid(localObj)
                localObj =  Simulator.Oven();
            end
            singleObj = localObj;
        end
    end    
    
end
