%% Example of a MATLAB code for automatic design of 
% a surface-mounted permanent magnet synchronous motor with inner rotor 
% See section 6.4 of User Manual - Part II for more details.

%% Step 1: Specify initial design mxp-file and mat-file name to save the results
cd ../
initMXPscriptingAPI;   % MOTORXP-MATLAB API initialization
initdesignfile = 'C:\FEMmotor\MotorXP_PM_v1.2_MATLAB(forWorkshop)\SimFiles\Training_inrunner\inrunner1.mxp';
save2filename = 'Optimization_PMSM_inrunner_training.mat';             % save optimization algorithm results to this mat-file

%% Step 2: Specify geometry constraints and initial design parameters 

% Inrunner or outrunner? Uncomment one of the following:
motortype='InnerRotor';                 % inrunner
% motortype='OuterRotor';                % outrunner

% Coil fill factor
coilFillFactor=0.35;                         % coil fill factor in the range 0... 1

% Motor or generator? Uncomment one of the following:
machineMode='motor';                    % motor
% machineMode='generator';              % generator

% Operating conditions the machine is optimized for
targetSpeed=1600;                       % speed [RPM] the optimization is run for
targetPower=50;                         % output power [kW] the optimization is run for

% Outer diameter in millimeters (for fixed outer diameter specify outerDiameter_min = outerDiameter_max)
outerDiameter_min = 350;
outerDiameter_max = 350;

% Air gap length in millimeters (for fixed air gap specify airGap_min = airGap_max)
airGap_min = 1.5;
airGap_max = 2.5;

% Lamination length in millimeters (for fixed lamination length specify laminationLength_min = laminationLength_max)
laminationLength_min = 40;
laminationLength_max = 60;

% Inner diameter in millimeters (for fixed inner diameter specify innerDiameter_min = innerDiameter_max)
innerDiameter_min = 140;
innerDiameter_max = 220;
% Inner diameter can be defined either in millimeters or as a ratio of the
% outer diameter (uncomment one of the followings):
innerDiameter_unit = 'mm';
% innerDiameter_unit = 'ratio2outerDiameter';
% Not sure regarding inner diameter? Just uncomment the following 3 lines:
% innerDiameter_unit = 'ratio2outerDiameter';
% innerDiameter_min = 0.1;          % minimum inner diameter is 10% of the outer diameter
% innerDiameter_max = 0.9;          % maximum inner diameter is 90% of the outer diameter

% Approximate slot opening depth and width in millimeters 
% - for open slot stator specify: slotopeningDepth=0, slotopeningWidth=0 (default)
% - for semi closed slot stator specify: slotopeningDepth>0, slotopeningWidth>0
% - for closed slot stator specify: slotopeningDepth>0, slotopeningWidth=0
slotopeningDepth = 1.5;
slotopeningWidth = 3;

% Fixed magnet weight? Uncomment the following line and specify the desired magnet weight, otherwise the magnet weight will be a variable
% magnetWeight = 743;     % grams

% Reference machine design weight (grams) and losses (W); reference design will be displayed as a red x on Pareto plot
refWeight = 25829;    % grams
refLoss = 2419;     % W
% If there is no reference design, uncomment the following 2 lines:
% refWeight = [];
% refLoss = [];

%% Step 3: Specify number of designs generated by the optimization algorithm

maxNumberOfProcesses = feature('numcores');                      % maximum number of processes working in parallel (equal to the number of physical cpu cores)
maxNumberOfProcesses = 7;                                      % or can be defined manually
init_dataset_size = 50;                                          % number of initial designs generated using latin hypercube sampling
NoOfBachSequential = 14;                                         % number of designs per one iteration of optimization algorithm 
niter = 30;                                                      % number of optimization algorithm iterations
maxeval = init_dataset_size+niter*NoOfBachSequential;            % total number of designs generated (50+50*14=750 designs by default)

%% Step 4: Specify optimization problem and run the optimization

designFunction = @evalDesigns_SMPMSM;                            % funtion to process the designs generated by the optimization algorithm
no_outputs = 2;                                                  % number of optimization objectives
labelObjective = {'Weight, grams', 'Losses, W'};                 % optimization objectives

% Define bounds on design variables
bounds = [ % lower   upper  
             0.35      0.75;                       % 1  Tooth Width angle ratio to slot pitch angle at the air gap
             0.3        0.9;                       % 2  Stator back iron depth coefficient
             0.8      1.2;                       % 3  Rotor back iron depth to stator back iron depth ratio
             120       175];                     % 4  Magnet Angle   
         
index = size(bounds,1)+1;   % design variable bounds index in "bounds"             
if exist('magnetWeight','var') && length(magnetWeight)==1 && isnumeric(magnetWeight) && magnetWeight>0
    % fixed magnet weight
    [magnetVolume0, Br, coilSpan] = getMagnetVolume(initdesignfile,targetPower,targetSpeed,magnetWeight);   
    Userdata.index_coef_magnetVolume = 0;
else
    % variable magnet weight (volume)
    [magnetVolume0, Br, coilSpan] = getMagnetVolume(initdesignfile,targetPower,targetSpeed);   
    Userdata.index_coef_magnetVolume = index;
    bounds = [bounds;
              [1.5   3]];   % add bounds for Magnet volume coefficient 
    index = index+1;
end

if outerDiameter_min==outerDiameter_max
    % fixed outer diameter
    Userdata.index_outerDiameter = 0;
    Userdata.outerDiameter = outerDiameter_min;
else
    % variable outer diameter
    Userdata.index_outerDiameter = index;
    Userdata.outerDiameter = [];
    bounds = [bounds;
              [outerDiameter_min outerDiameter_max]];   % add bounds for outer diameter
    index = index+1;
end

if airGap_min==airGap_max
    % fixed air gap length
    Userdata.index_airGap = 0;
    Userdata.airGap = airGap_min;
else
    % variable air gap length
    Userdata.index_airGap = index;
    Userdata.airGap = [];
    bounds = [bounds;
              [airGap_min airGap_max]];   % add bounds for air gap length
    index = index+1;
end

if laminationLength_min==laminationLength_max
    % fixed lamination length
    Userdata.index_laminationLength = 0;
    Userdata.laminationLength = laminationLength_min;
else
    % variable lamination length
    Userdata.index_laminationLength = index;
    Userdata.laminationLength = [];
    bounds = [bounds;
              [laminationLength_min laminationLength_max]];   % add bounds for lamination length
    index = index+1;
end

Userdata.innerDiameter_unit = innerDiameter_unit;
if innerDiameter_min==innerDiameter_max
    % fixed inner diameter
    Userdata.index_innerDiameter = 0;
    Userdata.innerDiameter = innerDiameter_min;
else
    % variable inner diameter
    Userdata.index_innerDiameter = index;
    Userdata.innerDiameter = [];
    bounds = [bounds;
              [innerDiameter_min innerDiameter_max]];   % add bounds for inner diameter
end

% Extreme case bounds:
% Maximum magnet depth ratio to (outerDiameter-innerDiameter)/2
if Br>1  % for stronger magnets
    ratio_magnetDepth_max = 0.7;
else     % for weaker magnets with Br<1
    ratio_magnetDepth_max = 0.85;
end    
% Minimum slot depth ratio to total stator depth (slotDepth/(slotDepth+statorBackIronDepth))
ratio_slotDepth_min = 0.1;
% Default weight and losses for designs with failed assembling
errWeight = 11*pi*outerDiameter_max^2/4*laminationLength_max/100;
errLoss = 10*targetPower*1000;

Userdata.slotopeningDepth=slotopeningDepth;
Userdata.slotopeningWidth=slotopeningWidth;
Userdata.motortype=motortype;                               % either 'InnerRotor' or 'OuterRotor'
Userdata.coilFillFactor=coilFillFactor;                     % coil fill factor
Userdata.targetSpeed=targetSpeed;                           % speed [RPM] the optimization is run for
Userdata.targetPower=targetPower;                           % output power [kW] the optimization is run for
Userdata.machineMode=machineMode;                           % either 'motor' or 'generator'
Userdata.initCurrent=[];                                    % expected stator current for 1 turn, no parallel paths, to speed up the calculation
Userdata.magnetVolume0 = magnetVolume0;                     % initial magnet volume
Userdata.ratio_magnetDepth_max = ratio_magnetDepth_max;     % Maximum magnet depth ratio to (outerDiameter-innerDiameter)/2
Userdata.ratio_slotDepth_min = ratio_slotDepth_min;         % Minimum slot depth ratio to total stator depth (slotDepth/(slotDepth+statorBackIronDepth))
Userdata.coilSpan = coilSpan;                               % winding coil span
Userdata.errWeight = errWeight;
Userdata.errLoss = errLoss;

lb = bounds(:,1)';                               % lower bounds [lb1,lb2,...]
ub = bounds(:,2)';                               % upper bounds [ub1,ub2,...]

y_refdesign=[refWeight refLoss];    % reference design motor weight (grams) and losses (W) @(targetPower, targetSpeed) - reference design red x on Pareto plot

runTSEMOptimizationpm(designFunction,initdesignfile,ub,lb,no_outputs,init_dataset_size,maxeval,niter,maxNumberOfProcesses,save2filename,y_refdesign,labelObjective,Userdata);


