function outSt=mcbGenerateTables(motor,inverter,purpose,varargin)
%  mcbGenerateTables Returns the lookup-tables related to motors.
%
%  When the purpose is defined as 'Star2Star' or 'Star2Delta', it returns LUTs
% related to PMSM which can be used in FEM Parameterized PMSM plant models.
% When the purpose is defined as 'idiqLUTs' or 'idiqLUTsinit', it returns
% LUTs related to PMSM which can be used in PMSM reference current current
% generation.
%
%  mcbGenerateTables(pmsm,[],'Star2Delta',[]) reads the star-equivalent
%  motor parameters (Rs, Ld, Lq, permanent flux linkage) and generates the
%  LUTs of delta-equivalent motor parameters (Rs, fluxD(Id, Iq, theta),
% fluxQ(Id, Iq, theta), Torq(Id,Iq,theta)) for FEM parameterized motor model.
%
%  mcbGenerateTables(pmsm,[],'Star2Star',[]) reads the star-equivalent
%  motor parameters (Rs, Ld, Lq, permanent flux linkage) and generates the
%  LUTs of star-equivalent motor parameters (Rs, fluxD(Id, Iq, theta),
% fluxQ(Id, Iq, theta), Torq(Id,Iq,theta)) for FEM parameterized motor model.
%
% The LUT parameters in star and delta winding are adjusted
% in the script to give the same motor characteristics.
%  mcbGenerateTables requires star-equilvalent parameters of pmsm, and the
% winding type that is being used in the PMSM plant model.
%  The LUTs fluxD(id, iq, angle), fluxQ(id, iq, angle) and Torq(id, iq, angle)
% will be generated using 5 values of id, 5 values of iq and 60/polepairs+1
% values of theta.
%  Id, Iq arrays will be selected from -Irated to +Irated. The generated
% lookup tables are created assuming a linear motor model.
%
%  mcbGenerateTables(pmsm,inverter,'idiqLUTs',seed) generates the LUTs
%  id(T,w), and iq(T,w) based on the pmsm, inverter and the seed
%  structures.
%  mcbGenerateTables(pmsm,inverter,'idiqLUTsinit',seed) generates the dummy LUTs
%  id(T,w), and iq(T,w) based on the pmsm, inverter and the seed
%  structures.
%   The motor.PMSMLUT structure comprises of pmsm parameter input method using
%  motor.PMSMLUT.method field. The measured torque may be provided through
%  motor.PMSMLUT.TorqueTable using the same id and iq grid as the other
%  non-linear motor parameters such as flux linkage tables or inductance tables.
%
% The optional argument useTorquePercent may be used to compute the id iq
% LUTs with torque percentage grid, as defined in optional field of
% pmsm.PMSMLUT.trefPctVec. When the torque percentages are in use, the
% envelope of speed-torque characteristics may be provided through
% pmsm.PMSMLUT.Tenvelope, corresponding to the speed vector defined at
% pmsm.PMSMLUT.wrpmVec.
%
%  Example for Delta-wound FEM parameterized PMSM:
%        pmsm=mcb_SetPMSMMotorParameters('Teknic2310P');
%        PMSMLUT=mcbGenerateTables(pmsm,[],'Star2Delta',[]);
%        % use the PMSMLUT in the mask inputs of the PMSM plant model
%
%  Example for PMSM Current Reference Generator with LUTs:
%        inverter= mcb_SetInverterParameters("BoostXL-DRV8305");
%        pmsm=mcb_SetPMSMMotorParameters('Teknic2310P');
%        pmsm.PMSMLUT.method='Ldq'; % 'lumped' or 'Ldq' or 'FluxDQ'
%        pmsm.PMSMLUT.idVec=[-4, -2, 0, 2]*pmsm.I_rated;
%        pmsm.PMSMLUT.iqVec=[-4, -2, 0, 2, 4]*pmsm.I_rated;
%        pmsm.PMSMLUT.LdTable=pmsm.Ld * ones(4, 5);
%        pmsm.PMSMLUT.LqTable=pmsm.Lq * ones(4, 5);
%        pmsm.PMSMLUT.FluxPMTable=pmsm.FluxPM * ones(4, 5);
%        seed.drawLUTonConstraintCurves=0; % 0 or 1
%        % optionally provide the following to specify the custom grid
%        %%seed.trefVec=[0 0.15 0.3];
%        %%seed.wrpmVec=[0 1000 2000 6000];
%        %%seed.TBreakPointsCount=21;
%        %%seed.wBreakPointsCount=64;
%        % for a variable voltage system, to create the 3D LUTs, use
%        %%seed.vBreakPointsCount=2;
%        PMSMLUT=mcbGenerateTables(pmsm,inverter,'idiqLUTs',seed);
%        % use the PMSMLUT in the mask inputs of the PMSM Iref generator
%
%  Example for Flux Tables for Torque estimator and Feedforward controller:
%        pmsm=mcb_SetPMSMMotorParameters('Teknic2310P');
%        seed.idVec=[-4,-2,0,2]*pmsm.I_rated;
%        seed.iqVec=[-4,-2,0,2,4]*pmsm.I_rated;
%        PMSMLUT=mcbGenerateTables(pmsm,[],'idealFluxTables',seed);
%        % use the PMSMLUT in the mask inputs of the PMSM plant model

%   Copyright 2022-2023 The MathWorks, Inc.

%%

defaultSeed.drawLUTonConstraintCurves=0;
p = inputParser;
addRequired(p,'motor',@(x) (isstruct(x)&&min(isfield(x,{'p','Rs','Ld','Lq','FluxPM','B','I_rated'}))) || (all(cellfun(@(y) isprop(x,y),{'p','Rs','Ld','Lq','FluxPM','B','I_rated'}))));
addRequired(p,'inverter',@(x) ((isstruct(x)&&min(isfield(x,{'V_dc'}))) || isempty(x)));
addRequired(p,'purpose',@(x) (ischar(x) || isstring(x))&&any(ismember(lower(x) ...
    ,{'star2delta','star2star','idiqlutsinit','idiqluts', ...
    'idiq3dlutsinit','idiq3dluts', ...
    'idealfluxtables','jmagfluxa2fluxdq', ...
    'jmagfluxfiles2fluxdq','jmagindfiles2inddq'})));
addOptional(p,'thisSeed',defaultSeed,@(x) ((isstruct(x)) || isempty(x)));
addParameter(p,'useTorquePercent',0,@(x) any(ismember(x ,[1,0])));
addParameter(p,'drawLUT',0,@(x) any(ismember(x ,[6,2,1,0])));
addParameter(p,'verbose',0,@(x) any(ismember(x ,[1,0])));
parse(p,motor,inverter,purpose,varargin{:});
warning('off','MATLAB:structOnObject');
motor=struct(p.Results.motor);
warning('on','MATLAB:structOnObject');
inverter=p.Results.inverter;
if ~isfield(inverter,'R_board')
    inverter.R_board=0;
end
useTorquePercent=p.Results.useTorquePercent;
drawLUT=p.Results.drawLUT;
verbose=p.Results.verbose;
purpose=p.Results.purpose;
thisSeed=p.Results.thisSeed;


if isfield(motor,'PMSMLUT') && isfield(motor.PMSMLUT,'FWCMethod')
    FWCMethod=motor.PMSMLUT.FWCMethod;
elseif isfield(thisSeed,'FWCMethod')
    FWCMethod=thisSeed.FWCMethod;
else
    FWCMethod='vclmt';
end
if strcmpi(purpose,'idiqluts')
    if ~isfield(motor,'PMSMLUT') ||  ~min(isfield(motor.PMSMLUT,{'idVec', 'iqVec'}))
        motor.PMSMLUT=thisSeed;
    end
    [motor.T_rated,PMSMspeeds]=mcbPMSMRatedTorque(motor,inverter,'FWCMethod',FWCMethod);
    motor.N_max=PMSMspeeds(end);
    motor.N_base=PMSMspeeds(1);
end


switch lower(purpose)
    case {'star2delta','star2star'}
        pmsm=motor;
        % using 5 values of id, iq in the range [-Irated,Irated]
        id_len=5;iq_len=5;
        %using 60/polepairs+1 values of angles in degrees in the range [0,120/polepairs]
        angle_len=180/pmsm.p/3+1;
        FEMParam.idVec=linspace(-pmsm.I_rated,pmsm.I_rated,id_len);
        FEMParam.iqVec=linspace(-pmsm.I_rated,pmsm.I_rated,iq_len);
        FEMParam.degVec=linspace(0,120/pmsm.p,angle_len);
        % pick the type of winding.
        isDelta=strcmpi(purpose,'star2delta');
        if isDelta
            FEMParam.windingType='delta';
        else
            FEMParam.windingType='star';
        end
        % initialize the matrices
        nid = length(FEMParam.idVec);
        niq = length(FEMParam.iqVec);
        nx = length(FEMParam.degVec);
        FEMParam.fluxD = zeros(nid,niq,nx);
        FEMParam.fluxQ = zeros(nid,niq,nx);
        FEMParam.Torq = zeros(nid,niq,nx);
        % the resistance is multuplied by sqrt(3) when using delta winding to match
        % the base speed of the star equivalent model
        FEMParam.Rs=pmsm.Rs*sqrt(2*isDelta+1);
        for i=1:nid
            for j=1:niq
                for m=1:nx
                    % the id, iq values are rotated by -30 degrees electrical, and
                    % multiplied by sqrt(3) when delta winding is used, to match
                    % the star equivalent model
                    i_d   = ( FEMParam.idVec(i)*cosd(isDelta*30) ...
                        +FEMParam.iqVec(j)*sind(isDelta*30))*sqrt(2*isDelta+1);
                    i_q   = (-FEMParam.idVec(i)*sind(isDelta*30) ...
                        +FEMParam.iqVec(j)*cosd(isDelta*30))*sqrt(2*isDelta+1);
                    % flux linkages on d, q axes is calculated.
                    psi_d = i_d*pmsm.Ld + pmsm.FluxPM;
                    psi_q = i_q*pmsm.Lq;
                    FEMParam.fluxD(i,j,m) = psi_d;
                    FEMParam.fluxQ(i,j,m) = psi_q;
                    % torque based on currents and flux linkages is calculated
                    FEMParam.Torq(i,j,m)  = 3/2*pmsm.p*(i_q*psi_d - i_d*psi_q);
                end
            end
        end
        outSt=FEMParam;

    case {'idiqlutsinit'}
        PMSMLUT=thisSeed;
        PMSMLUT.trefVec=[0 100];
        PMSMLUT.trefPctVec=[0 100];
        PMSMLUT.Tenvelope=[0 100];
        PMSMLUT.wrpmVec=[0 10000];
        PMSMLUT.idTable=[0 0;0 0];
        PMSMLUT.iqTable=[0 0;0 0];
        outSt=PMSMLUT;

    case {'idiqluts'}
        outSt=0;
        pmsm=motor;
        % if the idTable, iqTable, trefVec, wrpmVec are to be created
        %either from Ld, Lq, and FluxPM lumped parametrs or LUTs
        seedLUT=thisSeed;
        if isfield(pmsm,'PMSMLUT') && min(isfield(pmsm.PMSMLUT,{'idVec', 'iqVec'}))
            PMSMLUT=pmsm.PMSMLUT;
            seedLUT=PMSMLUT;
            % else
            %     PMSMLUT=seedLUT;
            if verbose>0
                disp('#### using the PMSMLUT from pmsm.PMSMLUT');
            end
        else
            if verbose>0
                disp('#### using the PMSMLUT from seed');
            end
        end

        if isfield(seedLUT,'method')
            if strcmpi(seedLUT.method,'lumped')
                seedLUT.goLUT=0;
            elseif strcmpi(seedLUT.method,'Ldq')
                seedLUT.goLUT=1;
            elseif strcmpi(seedLUT.method,'FluxDQ')
                seedLUT.goLUT=2;
            else
                seedLUT.goLUT=0;
            end
        else
            seedLUT.goLUT=0;
        end
        if ~isfield(seedLUT,'FWCMethod')
            seedLUT.FWCMethod='vclmt';
        end

        if seedLUT.goLUT==1
            if ~(isfield(seedLUT,'idVec') && isfield(seedLUT,'iqVec') ...
                    && isfield(seedLUT,'LdTable') ...
                    && isfield(seedLUT,'LqTable') ...
                    && isfield(seedLUT,'FluxPMTable'))
                seedLUT.goLUT=0;
                if verbose>0
                    disp('#### One or more of idvec, iqvec, LqTable, LqTable, and FluxPMTable are missing. Proceeding with lumped parameters.');
                end
            end
        elseif seedLUT.goLUT==2
            if ~(isfield(seedLUT,'idVec') && isfield(seedLUT,'iqVec') ...
                    && isfield(seedLUT,'FluxDTable') ...
                    && isfield(seedLUT,'FluxQTable'))
                seedLUT.goLUT=0;
                if verbose>0
                    disp('#### FluxD or FluxQ LUT is missing. Proceeding with lumped parameters.');
                end
            end
        end

        if ~isfield(seedLUT,'motorType')
            if seedLUT.goLUT==0
                if pmsm.Lq>pmsm.Ld
                    seedLUT.motorType='ipmsm';
                else
                    seedLUT.motorType='spmsm';
                end
            elseif seedLUT.goLUT==1
                if min(min(seedLUT.LqTable>(seedLUT.LdTable)))==1%#ok
                    seedLUT.motorType='ipmsm';
                else
                    seedLUT.motorType='spmsm';
                end
            end
        else
            if strcmpi(seedLUT.motorType,'ipmsm') || strcmpi(seedLUT.motorType,'synrm') || strcmpi(seedLUT.motorType,'pmasynrm')
                if seedLUT.goLUT==0
                    if pmsm.Lq<=pmsm.Ld
                        error(message('mcb:blocks:MoreThanTest','Lq','Ld'));
                    end
                elseif seedLUT.goLUT==1
                    if min(min(seedLUT.LqTable>seedLUT.LdTable))==0%#ok
                        error(message('mcb:blocks:MoreThanTest','Lq','Ld'));
                    end
                end
            end
        end
        if isfield(seedLUT,'useDynamicCornerRPMspmsm')
            useDynamicCornerRPMspmsm=seedLUT.useDynamicCornerRPMspmsm;
        else
            useDynamicCornerRPMspmsm=0;
        end
        if bitget(drawLUT,1)==0
            if isfield(seedLUT,'drawLUTonConstraintCurves')
                drawLUTonConstraintCurves=seedLUT.drawLUTonConstraintCurves;
            else
                drawLUTonConstraintCurves=0;
            end
        else
            drawLUTonConstraintCurves=1;
        end
        if bitget(drawLUT,2)==0
            if isfield(seedLUT,'drawContours')
                drawtheContours=seedLUT.drawContours;
            else
                drawtheContours=0;
            end
        else
            drawtheContours=1;
        end

        changeDQAxes=bitget(drawLUT,3);
        vmax=inverter.V_dc/sqrt(3);%assuming SVPWM, and actual equations

        if useTorquePercent==1

            pmsm.PMSMLUT=seedLUT;
            driveCharTables=mcbPMSMCharacteristics(pmsm,inverter, ...
                "driveCharacteristics",0,"constraintCurves",0,"FWCMethod",seedLUT.FWCMethod);
            % milestone_speeds=mcbPMSMSpeeds(pmsm,inverter,"FWCMethod",seedLUT.FWCMethod);
            % milestone_speeds=unique(milestone_speeds);
            % wcorner=milestone_speeds(1);
            % TArray=driveCharTables.TArray;
            %The following additional element is to make sure that at max speed,
            %the torque vector's last value is used, resulting in actual iq at max
            %speed.
            % TArray(end+1:end+5)=TArray(end);%TArray(end+1)=0;
            envelop_w=driveCharTables.wArray;
            envelop_t=driveCharTables.TArray;

        end
        if isfield(seedLUT,'wBreakPointsCount')
            wBreakPoints=seedLUT.wBreakPointsCount;
        else
            wBreakPoints=64;
        end
        if isfield(seedLUT,'wrpmVec')
            seedLUT.wrpmVec=unique(round(seedLUT.wrpmVec),'stable');
        else
            if useTorquePercent==1
                pmsm.T_rated=envelop_t(1);
                speeds=driveCharTables.speed_milestone;
            else
                [pmsm.T_rated,speeds]=mcbPMSMRatedTorque(pmsm,inverter);
            end
            pmsm.N_max=speeds(end);
            pmsm.N_base=speeds(1);
            milestone_speeds=[pmsm.N_base pmsm.N_max];
            wcorner=milestone_speeds(1);
            % if useTorquePercent==1
            %     seedLUT.wrpmVec=envelop_w;
            % else
            seedLUT.wrpmVec=unique(round(linspace(1,milestone_speeds(end),wBreakPoints)));
            % end
        end
        if isfield(seedLUT,'TBreakPointsCount')
            TbreakCount=seedLUT.TBreakPointsCount;
        else
            TbreakCount=21;
        end
        if useTorquePercent==1
            if isfield(seedLUT,'Tenvelope') && isfield(seedLUT,'wrpmVec') && length(seedLUT.Tenvelope)==length(seedLUT.wrpmVec)
                envelop_w=seedLUT.wrpmVec;
                envelop_t=seedLUT.Tenvelope;
            end
            seedLUT.Tenvelope=interp1(envelop_w,envelop_t,seedLUT.wrpmVec);
            seedLUT.wrpmVec=seedLUT.wrpmVec(~isnan(seedLUT.Tenvelope));
            seedLUT.Tenvelope=seedLUT.Tenvelope(~isnan(seedLUT.Tenvelope));
            if isfield(seedLUT,'trefPctVec')
                TorqueGrid=seedLUT.trefPctVec;
            else
                TorqueGrid=[0 linspace(4,100,TbreakCount-1)];
            end
            seedLUT.trefPctVec=TorqueGrid;
        else
            if ~isfield(seedLUT,'trefVec')
                if isfield(pmsm,'T_rated')
                    TArray=pmsm.T_rated;
                else
                    TArray=mcbPMSMRatedTorque(pmsm,inverter);
                end
                seedLUT.trefVec=linspace(-TArray(1),TArray(1),TbreakCount);
            end
            TorqueGrid=seedLUT.trefVec;
        end


        if useTorquePercent==0
            envelop_w=seedLUT.wrpmVec;%#ok
            envelop_t=1;%#ok
        end
        % if PMSMLUT.wrpmVec(end)>numel(TArray)
        %     TArray(end+1:PMSMLUT.wrpmVec(end))=TArray(end); %#ok
        % end
        seedLUT.idTable=zeros(length(TorqueGrid),length(seedLUT.wrpmVec));
        seedLUT.iqTable=zeros(length(TorqueGrid),length(seedLUT.wrpmVec));
        if seedLUT.goLUT==2
            [Ld_derived,Lq_derived,fluxPM_derived]=mcb.internal.PMSMFluxDQ2Ldq(seedLUT);
            goLUT=2;
            CriticalCurrent=(mean(fluxPM_derived,'all')/mean(Ld_derived,'all'));
        elseif seedLUT.goLUT==1
            goLUT=1;
            CriticalCurrent=(mean(seedLUT.FluxPMTable,'all')/mean(seedLUT.LdTable,'all'));
        elseif seedLUT.goLUT==0
            goLUT=0;%if lumped parameters are selected, ignore the LUT options
            CriticalCurrent=pmsm.FluxPM/pmsm.Ld;
        end
        if ~isfield(seedLUT,'motorType')
            if seedLUT.goLUT==2
                if min(min(Lq_derived>Ld_derived))==1%#ok
                    seedLUT.motorType='ipmsm';
                else
                    seedLUT.motorType='spmsm';
                end
            end
        else
            if strcmpi(seedLUT.motorType,'ipmsm') || strcmpi(seedLUT.motorType,'synrm') || strcmpi(seedLUT.motorType,'pmasynrm')
                if seedLUT.goLUT==2
                    if min(min(Lq_derived>Ld_derived))==0%#ok
                        %error(message('mcb:blocks:MoreThanTest','Lq','Ld'));
                        % commented here to avoid error cases where the
                        % derived Ld Lq may not match the expected saliency
                        % requirements.
                    end
                end
            end
        end
        if strcmpi(seedLUT.motorType,'spmsm') && goLUT==2
            seedLUT.LdTable=Ld_derived;
            seedLUT.LqTable=Lq_derived;
            seedLUT.FluxPMTable=fluxPM_derived;
            seedLUT.method='Ldq';
            goLUT=1;
        end
        seedLUT.goLUT=goLUT;
        if goLUT==0 || strcmpi(seedLUT.motorType,'spmsm')% for SPMSM, wcorner is needed
            milestone_speeds=mcbPMSMSpeeds(pmsm,inverter,"FWCMethod",seedLUT.FWCMethod);
            milestone_speeds=unique(milestone_speeds);
            wcorner=milestone_speeds(1);
        end
        try
            if seedLUT.goLUT==0 || seedLUT.goLUT==1 || seedLUT.goLUT==2
                if strcmpi(seedLUT.motorType,'IPMSM') || strcmpi(seedLUT.motorType,'synrm') || strcmpi(seedLUT.motorType,'pmasynrm')
                    if ~strcmpi(seedLUT.FWCMethod,'vclmt')
                        warning(message('mcb:blocks:APIForceSetVCLMT').getString());
                    end
                    F = findall(0,'type','figure','tag','LUTIPMSMWaitbar');delete(F);
                    thisMotorType='IPMSM';
                    if isfield(pmsm,'oldmotorType')
                        thisMotorType=pmsm.oldmotorType;
                    end
                    h=waitbar(0,"Calculating the LUTs for "+thisMotorType,'CreateCancelBtn','setappdata(gcbf,''cancelthis'',1)','tag','LUTIPMSMWaitbar');
                    setappdata(h,'cancelthis',0);
                    h.CloseRequestFcn = '';
                    loopercount=0;
                    for iter1=1:length(TorqueGrid)

                        thisT=TorqueGrid(iter1);
                        for iter2=1:length(seedLUT.wrpmVec)
                            if useTorquePercent==1
                                thisT=TorqueGrid(iter1)*seedLUT.Tenvelope(iter2)/100;
                            end
                            loopercount=loopercount+1;
                            waitbar(loopercount/(length(TorqueGrid)*length(seedLUT.wrpmVec)),h, ...
                                "Calculating the LUTs for "+thisMotorType+" (step "+string(loopercount)+"/"+string((length(TorqueGrid)*length(seedLUT.wrpmVec)))+")");
                            if getappdata(h,'cancelthis')
                                delete(h);
                                return;
                            end
                            thiswe=seedLUT.wrpmVec(iter2)*pmsm.p*pi/30;

                            %uncommenting the following commands limits the
                            %possible negative torque to only the magnitude on
                            %the positive torque side for the given speed.

                            % thisrpm=PMSMLUT.wrpmVec(iter2);
                            % if thisrpm<=0
                            %     thisrpm=1;
                            %     thiswe=pmsm.p*pi/30;
                            % end
                            %thisT=sign(thisT)*min(abs(thisT),TArray(thisrpm));

                            %Assuming VCLMT as default
                            %finding the MTPA points till the voltage constraint is
                            %reached
                            seed.w=thiswe;seed.t=thisT;seed.id=-0.1;seed.iq=0.1;
                            if seedLUT.goLUT==1
                                seed.ParamTableData.idVec=seedLUT.idVec;seed.ParamTableData.iqVec=seedLUT.iqVec;
                                seed.ParamTableData.FluxPMTable=seedLUT.FluxPMTable;
                                seed.ParamTableData.LdTable=seedLUT.LdTable;
                                seed.ParamTableData.LqTable=seedLUT.LqTable;
                                try
                                    seed.ParamTableData.TorqueTable=seedLUT.TorqueTable;
                                catch
                                end
                            elseif seedLUT.goLUT==2
                                seed.ParamTableData.idVec=seedLUT.idVec;seed.ParamTableData.iqVec=seedLUT.iqVec;
                                seed.ParamTableData.FluxDTable=seedLUT.FluxDTable;
                                seed.ParamTableData.FluxQTable=seedLUT.FluxQTable;
                                try
                                    seed.ParamTableData.TorqueTable=seedLUT.TorqueTable;
                                catch
                                end
                            else
                                seed.ParamTableData.idVec=0;seed.ParamTableData.iqVec=0;
                                seed.ParamTableData.FluxPMTable=0;
                                seed.ParamTableData.LdTable=0;
                                seed.ParamTableData.LqTable=0;
                            end
                            seed.goLUT=goLUT;
                            %Finding the id, iq for IPMSM MTPA for a given w, T

                            outputSt=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,34,'outputAll',1);

                            tmp_id=outputSt(1);
                            tmp_iq=outputSt(2);
                            if goLUT==0
                                [~,~,vs]=mcbPMSMCalcVdVq(pmsm,seed.ParamTableData,tmp_id,tmp_iq,goLUT,thiswe);
                            else
                                [~,~,vs]=mcbPMSMCalcVdVq(pmsm,seed.ParamTableData,tmp_id,abs(tmp_iq),goLUT,thiswe);
                            end
                            if vs <=vmax*1.001
                                %mtpa path is available.
                                seedLUT.idTable(iter1,iter2)=outputSt(1);
                                seedLUT.iqTable(iter1,iter2)=outputSt(2);
                            else
                                %look for FW with VCLMT
                                %Finding the id, iq for IPMSM VCLMT FW for a given w, T
                                seed1=seed;
                                if iter2>2
                                    seed1.id=seedLUT.idTable(iter1,iter2-1);
                                    seed1.iq=seedLUT.iqTable(iter1,iter2-1);
                                end
                                outputSt=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed1,35,'outputAll',1);
                                tmp_id=outputSt(1);
                                %Check for possibility of mtpv too
                                %Finding the id, iq for IPMSM MTPV for a given w, T
                                if CriticalCurrent/2>abs(tmp_id)
                                    tmp_idmtpv=0;
                                else
                                    outputStmtpv=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,36,'outputAll',1);
                                    tmp_idmtpv=outputStmtpv(1);
                                end
                                if (tmp_id>tmp_idmtpv) || (tmp_idmtpv>=0)
                                    %FW path is available.
                                    seedLUT.idTable(iter1,iter2)=outputSt(1);
                                    seedLUT.iqTable(iter1,iter2)=outputSt(2);
                                else
                                    %MTPV path is available
                                    seedLUT.idTable(iter1,iter2)=outputStmtpv(1);
                                    seedLUT.iqTable(iter1,iter2)=outputStmtpv(2);
                                end
                            end

                            %plot( seedLUT.idTable(iter1,iter2), seedLUT.iqTable(iter1,iter2),'r*');
                        end
                    end
                    delete(h);
                else % if it is Surface PMSM
                    F = findall(0,'type','figure','tag','LUTSPMSMWaitbar');delete(F);
                    h=waitbar(0,'Calculating the LUTs for SPMSM','CreateCancelBtn','setappdata(gcbf,''cancelthis'',1)','tag','LUTSPMSMWaitbar');
                    setappdata(h,'cancelthis',0);
                    h.CloseRequestFcn = '';
                    loopercount=0;
                    pmsm.Lq=pmsm.Ld;
                    for iter1=1:length(TorqueGrid)
                        %waitbar((iter1-1)/length(TorqueGrid),h,'Calculating the LUTs for SPMSM');
                        thisT=TorqueGrid(iter1);
                        %a variable to store the dynamically changing corner speed
                        %for the given torque, the speed at which the voltage of
                        %the stator reaches the system voltage
                        wcornertmp=-1;
                        for iter2=1:length(seedLUT.wrpmVec)

                            if useTorquePercent==1
                                thisT=TorqueGrid(iter1)*seedLUT.Tenvelope(iter2)/100;
                            end
                            loopercount=loopercount+1;
                            waitbar(loopercount/(length(TorqueGrid)*length(seedLUT.wrpmVec)),h, ...
                                'Calculating the LUTs for SPMSM (step '+string(loopercount)+'/'+string((length(TorqueGrid)*length(seedLUT.wrpmVec)))+')');
                            if getappdata(h,'cancelthis')
                                delete(h);
                                return;
                            end
                            thiswe=seedLUT.wrpmVec(iter2)*pmsm.p*pi/30;
                            thisrpm=seedLUT.wrpmVec(iter2);
                            if thisrpm<=0
                                thisrpm=1;
                                thiswe=pmsm.p*pi/30;
                            end
                            %uncommenting the following command limits the
                            %possible negative torque to only the magnitude on
                            %the positive torque side for the given speed.

                            % thisT=sign(thisT)*min(abs(thisT),TArray(thisrpm));

                            %finding the MTPA points till the voltage constraint is
                            %reached
                            seed.w=thiswe;seed.t=thisT;seed.id=0.1;seed.iq=0.1;
                            if seedLUT.goLUT==1
                                seed.ParamTableData.idVec=seedLUT.idVec;seed.ParamTableData.iqVec=seedLUT.iqVec;
                                seed.ParamTableData.FluxPMTable=seedLUT.FluxPMTable;
                                seed.ParamTableData.LdTable=seedLUT.LdTable;
                                seed.ParamTableData.LqTable=seedLUT.LdTable;
                            elseif seedLUT.goLUT==2
                                seed.ParamTableData.idVec=seedLUT.idVec;seed.ParamTableData.iqVec=seedLUT.iqVec;
                                seed.ParamTableData.FluxDTable=seedLUT.FluxDTable;
                                seed.ParamTableData.FluxQTable=seedLUT.FluxQTable;
                            else
                                seed.ParamTableData.idVec=0;seed.ParamTableData.iqVec=0;
                                seed.ParamTableData.FluxPMTable=0;
                                seed.ParamTableData.LdTable=0;
                                seed.ParamTableData.LqTable=0;
                            end
                            % seed.ParamTableData.idVec=seedLUT.idVec;seed.ParamTableData.iqVec=seedLUT.iqVec;
                            % seed.ParamTableData.FluxPMTable=seedLUT.FluxPMTable;
                            % seed.ParamTableData.LdTable=0*seedLUT.LdTable;
                            % seed.ParamTableData.LqTable=0*seedLUT.LqTable;
                            seed.goLUT=goLUT;
                            %Finding the id, iq for SPMSM MTPA for a given w, T
                            outputSt=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,37,'outputAll',1);
                            tmp_id=outputSt(1);
                            tmp_iq=outputSt(2);
                            [~,~,vs]=mcbPMSMCalcVdVq(pmsm,seed.ParamTableData,tmp_id,tmp_iq,goLUT,thiswe);
                            %if the dynamic corner rpm is in use, check for vs,
                            %else, check for corner rpm for the max case. This is
                            %to have back-ward compatibility with the earlier spmsm
                            %mtpa current ref generator block
                            if ((vs <=vmax) && (useDynamicCornerRPMspmsm || strcmpi(seedLUT.FWCMethod,'vclmt'))) || ((thisrpm<wcorner)&&(useDynamicCornerRPMspmsm==0))
                                %mtpa path is available.
                                seedLUT.idTable(iter1,iter2)=outputSt(1);
                                seedLUT.iqTable(iter1,iter2)=outputSt(2);
                            else
                                if wcornertmp==-1
                                    wcornertmp=seedLUT.wrpmVec(iter2-1);
                                end
                                if strcmpi(seedLUT.FWCMethod,'cvcp')
                                    %look for FW with CVCP
                                    %wcornertmp can be used too
                                    %literature assumes constant wcorner, it can
                                    %also be implemented.
                                    seed.w=thiswe;seed.t=thisT;
                                    %seed.wcorner needs the speed in electrical
                                    %radps
                                    if useDynamicCornerRPMspmsm
                                        seed.wcorner=wcornertmp*pmsm.p*pi/30;
                                    else
                                        seed.wcorner=wcorner*pmsm.p*pi/30;
                                    end
                                    seed.id=0.1;seed.iq=0.1;
                                    seed.goLUT=goLUT;
                                    %Finding the id, iq for SPMSM CVCP FW for a given w, T
                                    outputSt=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,38,'outputAll',1);
                                    % vs is not checked against vmax rest of the points
                                    % either fall on cvcp line, or on the circle, as
                                    % per the implementation

                                    seedLUT.idTable(iter1,iter2)=outputSt(1);
                                    seedLUT.iqTable(iter1,iter2)=outputSt(2);
                                elseif strcmpi(seedLUT.FWCMethod,'cccp')
                                    %look for FW with CCCP
                                    seed.w=thiswe;seed.t=thisT;
                                    if useDynamicCornerRPMspmsm
                                        seed.wcorner=wcornertmp*pmsm.p*pi/30;
                                    else
                                        seed.wcorner=wcorner*pmsm.p*pi/30;
                                    end
                                    seed.id=0.1;seed.iq=0.1;
                                    seed.goLUT=goLUT;
                                    %Finding the id, iq for SPMSM CCCP FW for a given w, T
                                    outputSt=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,39,'outputAll',1);
                                    % vs is not checked against vmax rest of the points
                                    %                             PMSMLUT.idTable(iter1,iter2)=outputSt(1);
                                    %                             PMSMLUT.iqTable(iter1,iter2)=outputSt(2);

                                    tmp_id=outputSt(1);
                                    %Check for possibility of mtpv too
                                    %Finding the id, iq for SPMSM VCLMT MTPV for a given w, T
                                    outputStmtpv=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,42,'outputAll',1);
                                    tmp_idmtpv=outputStmtpv(1);
                                    if tmp_id>tmp_idmtpv
                                        %FW path is available.
                                        seedLUT.idTable(iter1,iter2)=outputSt(1);
                                        seedLUT.iqTable(iter1,iter2)=outputSt(2);
                                    else
                                        %MTPV path is available
                                        seedLUT.idTable(iter1,iter2)=outputStmtpv(1);
                                        seedLUT.iqTable(iter1,iter2)=outputStmtpv(2);
                                    end
                                elseif strcmpi(seedLUT.FWCMethod,'none')
                                    %noFW case
                                    seed.w=thiswe;seed.t=thisT;seed.id=0.1;seed.iq=0.1;
                                    seed.goLUT=goLUT;
                                    %Finding the id, iq for SPMSM noFW for a given w, T
                                    outputSt=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,40,'outputAll',1);
                                    % vs is not checked against vmax rest of the points
                                    seedLUT.idTable(iter1,iter2)=outputSt(1);
                                    seedLUT.iqTable(iter1,iter2)=outputSt(2);
                                else %vclmt
                                    %VCLMT case
                                    seed.w=thiswe;seed.t=thisT;seed.id=0.1;seed.iq=0.1;
                                    seed.goLUT=goLUT;
                                    %Finding the id, iq for SPMSM VCLMT FW for a given w, T
                                    outputSt=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,41,'outputAll',1);
                                    tmp_id=outputSt(1);
                                    %Check for possibility of mtpv too
                                    %Finding the id, iq for SPMSM VCLMT MTPV for a given w, T
                                    outputStmtpv=mcb.internal.PMSMSpeedCurrentsFcn(pmsm,inverter,seed,42,'outputAll',1);
                                    tmp_idmtpv=outputStmtpv(1);
                                    if tmp_id>tmp_idmtpv
                                        %FW path is available.
                                        seedLUT.idTable(iter1,iter2)=outputSt(1);
                                        seedLUT.iqTable(iter1,iter2)=outputSt(2);
                                    else
                                        %MTPV path is available
                                        seedLUT.idTable(iter1,iter2)=outputStmtpv(1);
                                        seedLUT.iqTable(iter1,iter2)=outputStmtpv(2);
                                    end
                                end
                            end
                        end
                    end
                    delete(h);
                end
            end
        catch ME
            F = findall(0,'type','figure','tag','LUTIPMSMWaitbar');delete(F);
            F = findall(0,'type','figure','tag','LUTSPMSMWaitbar');delete(F);
            rethrow(ME);
        end
        if drawLUTonConstraintCurves==1 || drawtheContours==1
            pmsm.PMSMLUT=seedLUT;
            if goLUT==1
                pmsm.Ld=mean(mean(pmsm.PMSMLUT.LdTable));
                pmsm.Lq=mean(mean(pmsm.PMSMLUT.LqTable));
                pmsm.FluxPM=mean(mean(pmsm.PMSMLUT.FluxPMTable));
            elseif goLUT==2
                [Ld_derived,Lq_derived,fluxPM_derived]=mcb.internal.PMSMFluxDQ2Ldq(pmsm.PMSMLUT);
                pmsm.Ld=mean(mean(Ld_derived));
                pmsm.Lq=mean(mean(Lq_derived));
                pmsm.FluxPM=mean(mean(fluxPM_derived));
            end
            mcbPMSMCharacteristics(pmsm,inverter,FWCMethod=seedLUT.FWCMethod,drawContours=drawtheContours,constraintCurves=drawLUTonConstraintCurves,changeDQAxes=changeDQAxes);
            legend off;

            if changeDQAxes==1
                tmp=seedLUT.idTable;
                seedLUT.idTable=seedLUT.iqTable;
                seedLUT.iqTable=-tmp;
            end
            if useTorquePercent==0
                for i=1:numel(TorqueGrid)
                    lh=plot(seedLUT.idTable(i,:),seedLUT.iqTable(i,:),'Color',[1 0 0 0.05]);
                    lh.DataTipTemplate.DataTipRows(1).Label='id';
                    lh.DataTipTemplate.DataTipRows(2).Label='iq';
                    %lh.DataTipTemplate.DataTipRows(end+1)='Constant torque curve (red)';
                    ddt1=dataTipTextRow("RPM",seedLUT.wrpmVec(:));
                    ddt=dataTipTextRow("Nm",TorqueGrid(i)*ones(1,length(seedLUT.wrpmVec)));
                    lh.DataTipTemplate.DataTipRows(end+1)=ddt;
                    lh.DataTipTemplate.DataTipRows(end+1)=ddt1;
                end
                for j=1:numel(seedLUT.wrpmVec)
                    lh=plot(seedLUT.idTable(:,j),seedLUT.iqTable(:,j));
                    lh.LineStyle = "-";
                    lh.Color = [0 0 1 0.05];
                    lh.Marker = ".";
                    lh.MarkerEdgeColor="black";
                    lh.DataTipTemplate.DataTipRows(1).Label='id';
                    lh.DataTipTemplate.DataTipRows(2).Label='iq';
                    ddt1=dataTipTextRow("RPM",seedLUT.wrpmVec(j)*ones(1,length(TorqueGrid)));
                    ddt=dataTipTextRow("Nm",TorqueGrid(:));
                    %lh.DataTipTemplate.DataTipRows(end+1)='Constant speed curve (blue)';
                    lh.DataTipTemplate.DataTipRows(end+1)=ddt1;
                    lh.DataTipTemplate.DataTipRows(end+1)=ddt;
                end
            else
                for j=1:numel(seedLUT.wrpmVec)
                    lh=plot(seedLUT.idTable(:,j),seedLUT.iqTable(:,j));
                    lh.LineStyle = "-";
                    lh.Color = [0 0 1 0.05];
                    lh.Marker = ".";
                    lh.MarkerEdgeColor="black";
                    lh.DataTipTemplate.DataTipRows(1).Label='id';
                    lh.DataTipTemplate.DataTipRows(2).Label='iq';
                    ddt=dataTipTextRow("Nm",TorqueGrid(:)*seedLUT.Tenvelope(j)/100);
                    ddt1=dataTipTextRow("RPM",seedLUT.wrpmVec(j)*ones(1,length(TorqueGrid)));
                    %lh.DataTipTemplate.DataTipRows(end+1)='Constant speed curve (blue)';
                    lh.DataTipTemplate.DataTipRows(end+1)=ddt1;
                    lh.DataTipTemplate.DataTipRows(end+1)=ddt;
                end
            end
            xmax=pmsm.I_rated;
            if changeDQAxes==1
                xlim([0 xmax]);
                ylim([0 xmax]);
                set(gca,'YAxisLocation','left');
            else
                xlim([-xmax 0]);
                ylim([0 xmax]);
                set(gca,'YAxisLocation','right');
            end
        end
        outSt=seedLUT;

    case {'idiq3dlutsinit'}
        seedLUT=thisSeed;
        PMSMLUT=seedLUT;
        PMSMLUT.trefVec=[0 100];
        PMSMLUT.wrpmVec=[0 10000];
        PMSMLUT.Tenvelope=[0 100];
        PMSMLUT.trefPctVec=[0 100];
        PMSMLUT.vdcVec=[18 30];
        PMSMLUT.idTable=zeros(2,2,2);
        PMSMLUT.iqTable=zeros(2,2,2);
        outSt=PMSMLUT;
    case {'idiq3dluts'}
        pmsm=motor;

        if isfield(pmsm,'PMSMLUT') && min(isfield(pmsm.PMSMLUT,{'idVec', 'iqVec','vdcVec'}))
            thisSeed=pmsm.PMSMLUT;
        end
        if isfield(thisSeed,'vBreakPointsCount')
            vBreakPoints=thisSeed.vBreakPointsCount;
        else
            vBreakPoints=2;
        end
        if ~isfield(thisSeed,'vdcVec')
            thisSeed.vdcVec=linspace(inverter.V_dc*0.75,inverter.V_dc*1.25,vBreakPoints);
        else
            vBreakPoints=numel(thisSeed.vdcVec);
        end
        if isfield(thisSeed,'wBreakPointsCount')
            wBreakPoints=thisSeed.wBreakPointsCount;
        else
            wBreakPoints=64;
        end
        vmax=max(thisSeed.vdcVec);
        inv1.V_dc=vmax;
        wmax=mcbPMSMMaxSpeed(pmsm,inv1,FWCMethod=thisSeed.FWCMethod);
        if isfield(thisSeed,'wrpmVec')
            thisSeed.wrpmVec=unique(round(thisSeed.wrpmVec),'stable');
        else
            thisSeed.wrpmVec=unique(round(linspace(1,wmax,wBreakPoints)));
        end
        for i=1:vBreakPoints
            inverter.V_dc=thisSeed.vdcVec(i);
            pmsm.PMSMLUT=thisSeed;
            PMSMLUT=mcbGenerateTables(pmsm,inverter,'idiqLUTs',useTorquePercent=useTorquePercent);
            idTable(:,:,i)=PMSMLUT.idTable;%#ok
            iqTable(:,:,i)=PMSMLUT.iqTable;%#ok
            % if i==1
            %     thisSeed.wrpmVec=PMSMLUT.wrpmVec;
            % end
        end
        PMSMLUT.vdcVec=thisSeed.vdcVec;
        PMSMLUT.idTable=idTable;
        PMSMLUT.iqTable=iqTable;
        outSt=PMSMLUT;
    case 'idealfluxtables'
        % This script creates the ideal flux tables using the standard
        % equations. These FluxD, FluxQ tables can be used as templates and
        % canbe provided as inputs for the LUT based blocks
        pmsm=motor;
        if isempty(thisSeed)
            createidiqVec=1;
        elseif ~isstruct(thisSeed)
            createidiqVec=1;
        elseif ~isfield(thisSeed,'idVec')
            createidiqVec=1;
        elseif ~isfield(thisSeed,'iqVec')
            createidiqVec=1;
        else
            createidiqVec=0;
        end
        if createidiqVec==1
            thisSeed.idVec=[-4,-2,0,2]*pmsm.I_rated;
            thisSeed.iqVec=[-4,-2,0,2,4]*pmsm.I_rated;
        end

        FluxDTable=ones(numel(thisSeed.idVec),numel(thisSeed.iqVec));
        for i=1:numel(thisSeed.idVec)
            for j=1:numel(thisSeed.iqVec)
                FluxDTable(i,j)=pmsm.Ld*thisSeed.idVec(i)+pmsm.FluxPM;
            end
        end
        FluxQTable=ones(numel(thisSeed.idVec),numel(thisSeed.iqVec));
        for i=1:numel(thisSeed.idVec)
            for j=1:numel(thisSeed.iqVec)
                FluxQTable(i,j)=pmsm.Lq*thisSeed.iqVec(j);
            end
        end
        PMSMLUT.FluxDTable=FluxDTable;
        PMSMLUT.FluxQTable=FluxQTable;
        outSt=PMSMLUT;

    case 'jmagfluxa2fluxdq'
        %ee_declaration_functions.actuators.rotational.fem_motor.polarAflux2DQflux
        thetaRadVec  = [0 5 10 15 20 25 30]*2*pi/360; % Rotor angle vector, theta
        [FluxDTable,FluxQTable]=ee_declaration_functions.actuators.rotational.fem_motor.polarAflux2DQflux(thisSeed.FluxA,thetaRadVec,1);
        PMSMLUT.FluxDTable=FluxDTable;
        PMSMLUT.FluxQTable=FluxQTable;
        outSt=PMSMLUT;

    case 'jmagfluxfiles2fluxdq'
        tmp=readmatrix(thisSeed.fluxDfilepath);
        idVec=tmp(2:end,1);
        iqVec=tmp(1,2:end);
        tmp=tmp(2:end,2:end);
        FluxDTable=fillmissing(tmp,'linear',2,'EndValues','nearest');
        FluxDTable=fillmissing(FluxDTable,'linear',1,'EndValues','nearest');
        tmp=readmatrix(thisSeed.fluxQfilepath);
        tmp=tmp(2:end,2:end);
        FluxQTable=fillmissing(tmp,'linear',2,'EndValues','nearest');
        FluxQTable=fillmissing(FluxQTable,'linear',1,'EndValues','nearest');
        PMSMLUT.idVec=idVec;
        PMSMLUT.iqVec=iqVec;
        PMSMLUT.FluxDTable=FluxDTable;
        PMSMLUT.FluxQTable=FluxQTable;
        outSt=PMSMLUT;

    case 'jmagindfiles2inddq'
        tmp=readmatrix(thisSeed.Ldfilepath);
        idVec=tmp(2:end,1);
        iqVec=tmp(1,2:end);
        tmp=tmp(2:end,2:end);
        LdTable=fillmissing(tmp,'linear',2,'EndValues','nearest');
        LdTable=fillmissing(LdTable,'linear',1,'EndValues','nearest');
        tmp=readmatrix(thisSeed.Lqfilepath);
        tmp=tmp(2:end,2:end);
        LqTable=fillmissing(tmp,'linear',2,'EndValues','nearest');
        LqTable=fillmissing(LqTable,'linear',1,'EndValues','nearest');
        PMSMLUT.idVec=idVec;
        PMSMLUT.iqVec=iqVec;
        PMSMLUT.LdTable=LdTable;
        PMSMLUT.LqTable=LqTable;
        outSt=PMSMLUT;

    otherwise
        outSt=0;
end
end

