classdef FEStruct < matlab.mixin.CustomDisplay
    %FEStruct class for results data
    %
    % This undocumented function may be changed or removed in a future release.

    % Copyright 2019-2023 The MathWorks, Inc.

    properties
        % Tensor components
        xx, yy, zz; hoop
        yz, xz, xy;

        % Vector components and magnitude
        x,y,z;
        Magnitude;
    end

    properties (Dependent,Hidden = true)
        % Displacement
        ux,uy,uz;

        % Velocity
        vx,vy,vz;

        % Acceleration
        ax,ay,az;

        % Axisymmetric radial components
        ur,vr,ar;

        % Stress components
        sxx, syy, szz; sh
        syz, sxz, sxy;

        %Strain components
        exx, eyy, ezz; eh
        eyz, exz, exy;

        %Axisymmetric stress comonents
        srr, srz;

        %Axisymmetric strain comonents
        err, erz;

        %Symmetric components
        szy, szx, syx; szr;

        %Symmetric components
        ezy, ezx, eyx; ezr;

        %Electric field intensity
        Ex, Ey;

        %Axisymmetric electric field intensity
        Er, Ez;

        %Electric flux density
        Dx, Dy

        %Axisymmetric electric field density
        Dr, Dz

        %3-D Magnetic potential
        Ax, Ay, Az;

        %Magnetic field intensity
        Hx, Hy;

        %Axisymmetric magnetic field intensity
        Hr, Hz;

        %Magnetic flux density
        Bx, By;

        %Axisymmetric magnetic field density
        Br, Bz;

        %Current density 
        Jx, Jy, Jz;

    end

    properties (Hidden = true,SetAccess=private)
        elasticityType;
        tensorID
    end

    methods
        function self = FEStruct(tensorID,elasticity,varargin)
            self.tensorID = tensorID;
            self.elasticityType = elasticity;
            if contains(tensorID,["Stress", "Strain"])
                cgardX = varargin{1};
                cgardY = varargin{2};
                cgardZ = varargin{3};
                if contains(tensorID,"Stress")
                    self.xx = squeeze(cgardX(:,1,:));
                    self.yy = squeeze(cgardY(:,2,:));
                    self.xy = squeeze(cgardX(:,2,:));
                    if ~isempty(cgardZ)
                        self.zz = squeeze(cgardZ(:,3,:));
                        self.yz = squeeze(cgardY(:,3,:));
                        self.xz = squeeze(cgardX(:,3,:));
                        self.hoop = squeeze(cgardZ(:,3,:));
                    end
                elseif contains(tensorID,"Strain")
                    self.xx = squeeze(cgardX(:,1,:));
                    self.yy = squeeze(cgardY(:,2,:));
                    self.xy = squeeze(cgardX(:,2,:)+cgardY(:,1,:));
                    if ~isempty(cgardZ)
                        self.zz = squeeze(cgardZ(:,3,:));
                        self.hoop = squeeze(cgardZ(:,3,:));
                        self.yz = squeeze(cgardY(:,3,:)+cgardZ(:,2,:));
                        self.xz = squeeze(cgardX(:,3,:)+cgardZ(:,1,:));
                    end
                end
            end

            if contains(tensorID,["Displacement", "Velocity","Acceleration"])
                umatrix = varargin{1};
                self.x = squeeze(umatrix(:,1,:));
                self.y = squeeze(umatrix(:,2,:));
                mag = squeeze(sqrt(umatrix(:,1,:).^2+umatrix(:,2,:).^2));
                if contains(elasticity,'3D')
                    self.z = squeeze(umatrix(:,3,:));
                    mag = squeeze(sqrt(umatrix(:,1,:).^2+umatrix(:,2,:).^2+umatrix(:,3,:).^2));
                end
                self.Magnitude = mag;
            end

            if contains(tensorID,"MagneticPotential")
                umatrix = varargin{1};
                self.x = squeeze(umatrix(:,1,:));
                self.y = squeeze(umatrix(:,2,:));
                self.z = squeeze(umatrix(:,3,:));
            end

            if contains(tensorID,["ElectricField","ElectricFlux","MagneticField","MagneticFlux","CurrentDensity"])
                if contains(elasticity,'emHar')
                    self.x = varargin{1};
                    self.y = varargin{2};
                    if contains(elasticity,'3D')
                        self.z = varargin{3};
                    end
                    return;
                end
                gradX = varargin{1};
                gradY = varargin{2};
                self.x = gradX(:,1);
                self.y = gradY(:,1);
                if contains(elasticity,'em3D')
                    gradZ = varargin{3};
                    self.z = gradZ(:,1);
                end
            end
        end

        %%%% Displacement, Velocity, and Acceleration components.
        function ur  = get.ur(self)
            validateAccess(self.tensorID,'Displacement','ur')
            ur = self.ux;
        end

        function vr  = get.vr(self)
            validateAccess(self.tensorID,'Velocity','vr')
            vr = self.vx;
        end

        function ar  = get.ar(self)
            validateAccess(self.tensorID,'Acceleration','ar')
            ar = self.ax;
        end

        function ux  = get.ux(self)
            validateAccess(self.tensorID,'Displacement','ux')
            ux = self.x;
        end

        function uy  = get.uy(self)
            validateAccess(self.tensorID,'Displacement','uy')
            uy = self.y;
        end

        function uz  = get.uz(self)
            validateAccess(self.tensorID,'Displacement','uz')
            if contains(self.elasticityType,'as')
                uz = self.y;
            else
                uz = self.z;
            end
        end

        function vx  = get.vx(self)
            validateAccess(self.tensorID,'Velocity','vx')
            vx = self.x;
        end

        function vy  = get.vy(self)
            validateAccess(self.tensorID,'Velocity','vy')
            vy = self.y;
        end

        function vz  = get.vz(self)
            validateAccess(self.tensorID,'Velocity','vz')
            if contains(self.elasticityType,'as')
                vz = self.y;
            else
                vz = self.z;
            end
        end


        function ax  = get.ax(self)
            validateAccess(self.tensorID,'Acceleration','ax')
            ax = self.x;
        end

        function ay  = get.ay(self)
            validateAccess(self.tensorID,'Acceleration','ay')
            ay = self.y;
        end

        function az  = get.az(self)
            validateAccess(self.tensorID,'Acceleration','az')
            if contains(self.elasticityType,'as')
                az = self.y;
            else
                az = self.z;
            end
        end

        %%%%%%%%%%%%%%%%%%%%% Stress components %%%%%%%%%%%%%%%%
        function sxx  = get.sxx(self)
            validateAccess(self.tensorID,'Stress','sxx')
            sxx = self.xx;
        end

        function syy  = get.syy(self)
            validateAccess(self.tensorID,'Stress','syy')
            syy = self.yy;
        end

        function szz  = get.szz(self)
            validateAccess(self.tensorID,'Stress','szz')
            if contains(self.elasticityType,'as')
                szz = self.yy;
            else
                szz = self.zz;
            end
        end

        function syz  = get.syz(self)
            validateAccess(self.tensorID,'Stress','syz')
            syz = self.yz;
        end

        function sxz  = get.sxz(self)
            validateAccess(self.tensorID,'Stress','sxz')
            sxz = self.xz;
        end

        function sxy  = get.sxy(self)
            validateAccess(self.tensorID,'Stress','sxy')
            sxy = self.xy;
        end

        function szy  = get.szy(self)
            validateAccess(self.tensorID,'Stress','szy')
            szy = self.yz;
        end

        function szx  = get.szx(self)
            validateAccess(self.tensorID,'Stress','szx')
            szx = self.xz;
        end

        function syx  = get.syx(self)
            validateAccess(self.tensorID,'Stress','syx')
            syx = self.xy;
        end

        function srr  = get.srr(self)
            validateAccess(self.tensorID,'Stress','srr')
            srr = self.sxx;
        end

        function srz  = get.srz(self)
            validateAccess(self.tensorID,'Stress','srz')
            srz = self.sxy;
        end

        function szr  = get.szr(self)
            validateAccess(self.tensorID,'Stress','szr')
            szr = self.sxy;
        end

        function sh  = get.sh(self)
            validateAccess(self.tensorID,'Stress','sh')
            sh = self.hoop;
        end

        %%%%%%%%%%%%%%%%%%%%% Strain components %%%%%%%%%%%%%%%%
        function exx  = get.exx(self)
            validateAccess(self.tensorID,'Strain','exx')
            exx = self.xx;
        end

        function eyy  = get.eyy(self)
            validateAccess(self.tensorID,'Strain','eyy')
            eyy = self.yy;
        end

        function ezz  = get.ezz(self)
            validateAccess(self.tensorID,'Strain','ezz')
            if contains(self.elasticityType,'as')
                ezz = self.yy;
            else
                ezz = self.zz;
            end
        end

        function eyz  = get.eyz(self)
            validateAccess(self.tensorID,'Strain','eyz')
            eyz = self.yz;
        end

        function exz  = get.exz(self)
            validateAccess(self.tensorID,'Strain','exz')
            exz = self.xz;
        end

        function exy  = get.exy(self)
            validateAccess(self.tensorID,'Strain','exy')
            exy = self.xy;
        end

        function ezy  = get.ezy(self)
            validateAccess(self.tensorID,'Strain','ezy')
            ezy = self.yz;
        end

        function ezx  = get.ezx(self)
            validateAccess(self.tensorID,'Strain','ezx')
            ezx = self.xz;
        end

        function eyx  = get.eyx(self)
            validateAccess(self.tensorID,'Strain','eyx')
            eyx = self.xy;
        end

        function err  = get.err(self)
            validateAccess(self.tensorID,'Strain','err')
            err = self.exx;
        end

        function erz  = get.erz(self)
            validateAccess(self.tensorID,'Strain','erz')
            erz = self.xy;
        end

        function ezr  = get.ezr(self)
            validateAccess(self.tensorID,'Strain','ezr')
            ezr = self.xy;
        end

        function eh  = get.eh(self)
            validateAccess(self.tensorID,'Strain','eh')
            eh = self.hoop;
        end

        %%%%%%%%%%%%%% Electric field variables %%%%
        function Ex  = get.Ex(self)
            validateAccess(self.tensorID,'ElectricField','Ex')
            Ex = self.x;
        end

        function Ey  = get.Ey(self)
            validateAccess(self.tensorID,'ElectricField','Ey')
            Ey = self.y;
        end

        function Er  = get.Er(self)
            validateAccess(self.tensorID,'ElectricField','Er')
            Er = self.x;
        end

        function Ez  = get.Ez(self)
            validateAccess(self.tensorID,'ElectricField','Ez')
            if contains(self.elasticityType,'em2Das') ...
                    || contains(self.elasticityType,'emHar2Das')
                Ez = self.y;
            else
                Ez = self.z;
            end

        end

        function Dx  = get.Dx(self)
            validateAccess(self.tensorID,'ElectricFlux','Dx')
            Dx = self.x;
        end

        function Dy  = get.Dy(self)
            validateAccess(self.tensorID,'ElectricFlux','Dy')
            Dy = self.y;
        end

        function Dr  = get.Dr(self)
            validateAccess(self.tensorID,'ElectricFlux','Dr')
            Dr = self.x;
        end

        function Dz  = get.Dz(self)
            validateAccess(self.tensorID,'ElectricFlux','Dz')
            if contains(self.elasticityType,'em2Das')
                Dz = self.y;
            else
                Dz = self.z;
            end
        end

        %%%%%%%%%%%%%% Current Density %%%%%%%%%%%%%

        function Jx  = get.Jx(self)
            validateAccess(self.tensorID,'CurrentDensity','Jx')
            Jx = self.x;
        end

        function Jy  = get.Jy(self)
            validateAccess(self.tensorID,'CurrentDensity','Jy')
            Jy = self.y;
        end

        function Jz  = get.Jz(self)
            validateAccess(self.tensorID,'CurrentDensity','Jz')
            Jz = self.z;

        end

        %%%%%%%%%%%%%% Magnetic field variables %%%%
        function Ax  = get.Ax(self)
            validateAccess(self.tensorID,'MagneticPotential','Ax')
            Ax = self.x;
        end

        function Ay  = get.Ay(self)
            validateAccess(self.tensorID,'MagneticPotential','Ay')
            Ay = self.y;
        end

        function Az  = get.Az(self)
            validateAccess(self.tensorID,'MagneticPotential','Az')
            Az = self.z;
        end
        


        function Hx  = get.Hx(self)
            validateAccess(self.tensorID,'MagneticField','Hx')
            Hx = self.x;
        end

        function Hy  = get.Hy(self)
            validateAccess(self.tensorID,'MagneticField','Hy')
            Hy = self.y;
        end

        function Hr  = get.Hr(self)
            validateAccess(self.tensorID,'MagneticField','Hr')
            Hr = self.x;
        end

        function Hz  = get.Hz(self)
            validateAccess(self.tensorID,'MagneticField','Hz')
            if contains(self.elasticityType,'em2Das') ...
                    || contains(self.elasticityType,'emHar2Das')
                Hz = self.y;
            else
                Hz = self.z;
            end
        end

        function Bx  = get.Bx(self)
            validateAccess(self.tensorID,'MagneticFlux','Bx')
            Bx = self.x;
        end

        function By  = get.By(self)
            validateAccess(self.tensorID,'MagneticFlux','By')
            By = self.y;
        end

        function Br  = get.Br(self)
            validateAccess(self.tensorID,'MagneticFlux','Br')
            Br = self.x;
        end

        function Bz  = get.Bz(self)
            validateAccess(self.tensorID,'MagneticFlux','Bz')
            if contains(self.elasticityType,'em2Das')
                Bz = self.y;
            else
                Bz = self.z;
            end
        end

    end

    methods (Access = protected,Hidden = true)

        function group = getGroupForTensorID(self)
            % user accessbile names for a given TensorID and ElasticityType
            switch self.tensorID
                case 'Displacement'
                    if contains(self.elasticityType,'as')
                        group = {'ur','uz','Magnitude'};
                    elseif contains(self.elasticityType,'pn') || contains(self.elasticityType,'ps')
                        group = {'ux','uy','Magnitude'};
                    else
                        group = {'ux','uy','uz','Magnitude'};
                    end
                case 'Velocity'
                    if contains(self.elasticityType,'as')
                        group = {'vr','vz','Magnitude'};
                    elseif contains(self.elasticityType,'pn') || contains(self.elasticityType,'ps')
                        group = {'vx','vy','Magnitude'};
                    else
                        group = {'vx','vy','vz','Magnitude'};
                    end
                case 'Acceleration'
                    if contains(self.elasticityType,'as')
                        group = {'ar','az','Magnitude'};
                    elseif contains(self.elasticityType,'pn') || contains(self.elasticityType,'ps')
                        group = {'ax','ay','Magnitude'};
                    else
                        group = {'ax','ay','az','Magnitude'};
                    end
                case 'Stress'
                    if contains(self.elasticityType,'ps')
                        group = {'sxx','syy','sxy'};
                    elseif contains(self.elasticityType,'pn')
                        group = {'sxx','syy','sxy','szz'};
                    elseif contains(self.elasticityType,'as')
                        group = {'srr','szz','srz','sh'};
                    else
                        group = {'sxx','syy','szz','syz','sxz','sxy'};
                    end
                case 'Strain'
                    if contains(self.elasticityType,'ps')
                        group = {'exx','eyy','exy','ezz'};
                    elseif contains(self.elasticityType,'pn')
                        group = {'exx','eyy','exy'};
                    elseif contains(self.elasticityType,'as')
                        group = {'err','ezz','erz','eh'};
                    else
                        group = {'exx','eyy','ezz','eyz','exz','exy'};
                    end
                case 'ElectricField'
                    if contains(self.elasticityType,'em2D')
                        group = {'Ex','Ey'};
                    end

                    if contains(self.elasticityType,'em3D')
                        group = {'Ex','Ey','Ez'};
                    end

                    if contains(self.elasticityType,'em2Das')
                        group = {'Er','Ez'};
                    end

                    if contains(self.elasticityType,'emHar2D')
                        group = {'Ex', 'Ey'};
                    end

                    if contains(self.elasticityType,'emHar3D')
                        group = {'Ex', 'Ey', 'Ez'};
                    end

                    if contains(self.elasticityType,'emHar2Das')
                        group = {'Er', 'Ez'};
                    end
                case 'ElectricFlux'
                    if contains(self.elasticityType,'em2D')
                        group = {'Dx','Dy'};
                    end

                    if contains(self.elasticityType,'em3D')
                        group = {'Dx','Dy','Dz'};
                    end

                    if contains(self.elasticityType,'em2Das')
                        group = {'Dr','Dz'};
                    end

                case 'CurrentDensity'
                    if contains(self.elasticityType,'em2D')
                        group = {'Jx','Jy'};
                    end

                    if contains(self.elasticityType,'em3D')
                        group = {'Jx','Jy','Jz'};
                    end

                case 'MagneticPotential'
                    group = {'Ax','Ay','Az'};
                case 'MagneticField'
                    if contains(self.elasticityType,'em2D')
                        group = {'Hx','Hy'};
                    end
                    if contains(self.elasticityType,'em3D')
                        group = {'Hx','Hy','Hz'};
                    end
                    if contains(self.elasticityType,'em2Das')
                        group = {'Hr','Hz'};
                    end
                    if contains(self.elasticityType,'emHar2D')
                        group = {'Hx','Hy'};
                    end
                    if contains(self.elasticityType,'emHar3D')
                        group = {'Hx','Hy','Hz'};
                    end
                    if contains(self.elasticityType,'emHar2Das')
                        group = {'Hr', 'Hz'};
                    end
                case 'MagneticFlux'
                    if contains(self.elasticityType,'em2D')
                        group = {'Bx','By'};
                    end
                    if contains(self.elasticityType,'em3D')
                        group = {'Bx','By','Bz'};
                    end
                    if contains(self.elasticityType,'em2Das')
                        group = {'Br','Bz'};
                    end

            end
        end

        function group = getPropertyGroups(self)
            % custom display property groups
            group = getGroupForTensorID(self);
            group = matlab.mixin.util.PropertyGroup(group);
        end
    end

    methods(Hidden)
        function names = properties(self)
            % overload so that tab completion matches custom display while
            % preserving regular behavior otherwise. 
            if nargout == 0
                builtin('properties',self);
            else
                names = getGroupForTensorID(self).';
            end
        end
    end
end

% Helper function to valiate access to only right set of fields for a given
% tensor, e.g., Displacement can only access ux, uy, uz, ur.
function validateAccess(objTensorID, accessTensorID, fieldName)
if ~contains(objTensorID,accessTensorID)
    error(message('pde:StructuralResults:accessIncorrectField',fieldName,objTensorID))
end
end
