classdef (Sealed) ElectromagneticMaterialAssignment < handle & matlab.mixin.CustomDisplay
% ElectromagneticMaterialAssignment - Electromagnetic material property assignment over domain or subdomain
%  The ElectromagneticMaterialAssignment object stores the specified
%  material properties on a domain or subdomain and appends the object to
%  the MaterialProperties property of the ElectromagneticModel. In a
%  multidomain geometric model, material properties can be assigned
%  independently for each subdomain or it can be one global assignment.
%
% Instances of this class can only be created by calling the
% electromagneticProperties method of the ElectromagneticModel class.
%
% See also pde.ElectromagneticModel/electromagneticProperties

% Copyright 2020-2022 The MathWorks, Inc.


properties (SetAccess = private)
    %RegionType - Type of geometric region to which the material properties
    %are assigned
    RegionType;
end

properties
    % RegionID - IDs of the geometric regions to which the material
    % properties are assigned
    RegionID;

    %RelativePermittivity - Relative permittivity of the material
    RelativePermittivity;

    %RelativePermeability - Relative permeability of the material
    RelativePermeability;

    %Conductivity - Electric conductivity of the material
    Conductivity;
end

methods
    function self = ElectromagneticMaterialAssignment(emar,mtlPropsInput)
        arguments
            emar pde.ElectromagneticMaterialAssignmentRecords
            mtlPropsInput struct
        end
        
        self.RecordOwner = emar;
        validDomain = emar.ParentModel.validDomain;
        self.RegionType = validDomain;
        self.RegionID = mtlPropsInput.(validDomain);

        
        mtlPropsInput = rmfield(mtlPropsInput,validDomain);
        inputFields = fields(mtlPropsInput);
        for f = 1:numel(inputFields)
            self.(inputFields{f}) = mtlPropsInput.(inputFields{f});
        end
        
        self.PropertiesToValidate = setdiff(properties(self),{'RegionType','RegionID'});
    end
end


methods % Setter methods
    function set.RegionID(self, rids)
        ValidateRegionID(self,rids);
        self.RegionID = rids;
    end
    
    function set.RelativePermittivity(self, coef)
        self.CoefPrecheck(coef,'RelativePermittivity');
        self.RelativePermittivity = coef;
    end
    
    function set.RelativePermeability(self, coef)
        self.CoefPrecheck(coef,'RelativePermeability');
        self.RelativePermeability = coef;
    end

    function set.Conductivity(self,coef)
        self.CoefPrecheck(coef,'Conductivity');
        self.Conductivity = coef;
    end
end


methods(Hidden=true, Access = {?pde.ElectromagneticMaterialAssignmentRecords})
    function tf=numericMaterialProperties(self,varargin)
            tf = true;
            for prop = self.PropertiesToValidate.'
                tf = tf && ~isa(self.(prop{1}),'function_handle');
            end
    end

    function validateTypeSizeComplexity(self, loc, state, complexAllowed)
        for prop = self.PropertiesToValidate.'
            if isempty(self.(prop{1}))
                continue
            end
            coeffName = prop{1};
            coeffSize = 1; % Current support only for homogeneous properies.
            [complexityInput,sizeInput, coefVal] = self.RecordOwner.ParentModel.validateFcnHdl(self.(prop{1}), loc, state);

            if (complexityInput && ~complexAllowed) || any(coefVal < 0,'all') % Although non-physical, zero allowed for modeling purposes
                error(message('pde:materialPropertySpecification:invalidMtlPropValueComplex'));
            end

            if (sizeInput ~= coeffSize)
                error(message('pde:ElectromagneticModel:incorrectSizeCoefFunction', coeffName, coeffSize));
            end
        end
    end
    
    function ok=ValidateRegionID(self,rgnid)
        if isempty(self.RecordOwner)
            return; % Skips validation of rgnid while loading model from file.
        end
        % Must be vector of natural numbers.
        maxDomainID =  self.RecordOwner.ParentModel.maxDomainID;
        if ~isreal(rgnid) || ~all(rgnid(:) > 0) || issparse(rgnid) || any(mod(rgnid(:),1)~=0) || any(rgnid > maxDomainID)
            error(message('pde:materialPropertySpecification:invalidRegionID'));
        end
        ok = true;
    end
end % end of methods that record keeper calls



methods(Hidden, Access = private)
    function ok=CoefPrecheck(self,coef,coefName)
        if isempty(coef) || isempty(self.RecordOwner)
            return;
        end
        validateattributes(coef,{'double','function_handle'},{},'',coefName)
        if contains(self.RecordOwner.ParentModel.AnalysisType,'harmonic','IgnoreCase',true)
            attributes = {'nonnan','nonsparse','finite','scalar'};
            if isequal(coefName,'Conductivity')
                attributes{end+1} = 'real';
                attributes{end+1} = 'nonnegative';
            end
            if isa(coef,'function_handle')
                validateattributes(coef,{'function_handle'},{'scalar'},'',coefName);
            else
                validateattributes(coef,{'double'},attributes,coefName);
            end
            ok = true;
            return;
        end
        if isa(coef,'function_handle')
            validateattributes(coef,{'function_handle'},{'scalar'},'',coefName);

        else
            validateattributes(coef,{'double'},{'nonnan','nonsparse','real','finite','nonnegative','scalar'},coefName);
        end
        ok = true;
    end
end

methods(Hidden=true)
    function delete(self)
        if ~isempty(self.RecordOwner) && isvalid(self.RecordOwner)
            self.RecordOwner.delistMaterialAssignments(self);
        end
    end
end

properties (Hidden = true, Access='private')
    RecordOwner;
    PropertiesToValidate;
end
end