├── ML_MSTO_Optimizer_v5_1_release ├── Approx_SA_ANN.m ├── Approx_SA_CFD.m ├── Augment_TOMs.m ├── Cap_ANN.m ├── Check_CH.m ├── DeHomogenize_Macro.m ├── DeHomogenize_TOM.m ├── FEA.m ├── Hom_FEA.m ├── Hom_Macro_Fn.m ├── Indexing.m ├── Initialize_Program.m ├── Interp_CH.m ├── MAIN_v5_1_Generate_ANN_Models.m ├── MAIN_v5_1_ML_MSTO_Optimizer.m ├── ML_Models │ └── Phys001_Mecha │ │ ├── Homog2D_001_Mecha_N1000000_nels50.mat │ │ └── Homog3D_001_Mecha_N1000000_nels10.mat ├── Micro_TO.m ├── N_dN_calc.m ├── Normalize_TOM_BC.m ├── Num_Hom.m ├── Obj_Fns.m ├── PATCH_3Darray.m ├── PostProcess_Skel.m ├── Prepare_BCMacro.m ├── Prepare_BCMicro.m ├── Prepare_C.m ├── Prepare_Filter.m ├── Prepare_HS_bounds.m ├── Prepare_K.m ├── Prepare_KE.m ├── Prepare_Mesh.m ├── Prepare_Micro_Model.m ├── Prepare_RVE.m ├── Prepare_Sample_Plan.m ├── SS_FEA.m ├── TOM_scaler.m ├── Train_ANNs.m ├── Tri_Infill_Method.m ├── alg_GOCM.m ├── alg_OCM.m ├── blendedPolymask.m ├── density_grad.m ├── display_macro.m ├── display_micro.m ├── display_top.m ├── ini_design.m ├── isPoint.m ├── mesh_info.m ├── opt_proj.m ├── output_plot.m ├── parfor_waitbar.m ├── patchJobStorageLocation.m ├── save_results.m └── setup_parpool.m └── README.md /ML_MSTO_Optimizer_v5_1_release/Approx_SA_ANN.m: -------------------------------------------------------------------------------- 1 | % SENSITIVITY COEFFICIENT APPROXIMATION - ANALYTICAL DERIVATIVE THROUGH ANN 2 | function [dc, satime] = Approx_SA_ANN(Xin,macro,micro,Ue_int_Macro,Fe_int_Macro,ML_model,U_Macro) 3 | %% INITIALIZE TOM DATA 4 | if all(macro.phys == [1 2]) 5 | xMacro = Xin(1:length(Xin)/2); 6 | xWeight = Xin(length(Xin)/2+1:end); 7 | num_dv = 2; 8 | macro.h_fd = [(macro.x_ub(1) - macro.x_lb(1))*macro.h_fd(1), (macro.x_ub(2) - macro.x_lb(2))*macro.h_fd(2)]; 9 | else 10 | xMacro = Xin; 11 | xWeight = ones(size(xMacro)); 12 | num_dv = 1; 13 | macro.h_fd(1) = (macro.x_ub(1) - macro.x_lb(1))*macro.h_fd(1); 14 | end 15 | x_lb = macro.x_lb; x_ub = macro.x_ub; 16 | if micro.dim == 3 17 | micro.ortho = 1; 18 | end 19 | [~, ch_index, phys_index] = Indexing('full',micro,micro.ortho); 20 | [ml_index, ~, ~] = Indexing('part',micro,micro.ortho); 21 | TOM_e = [xMacro, xWeight]; 22 | for p = 1:length(micro.phys) 23 | if micro.bc_type == 1 24 | TOM_e = [TOM_e, Normalize_TOM_BC(Fe_int_Macro{micro.phys(p)},micro)]; % TOM features 25 | elseif micro.bc_type == 2 26 | TOM_e = [TOM_e, Normalize_TOM_BC(Ue_int_Macro{micro.phys(p)},micro)]; % TOM features 27 | end 28 | end 29 | Cminvec = micro.Cminvec; Cmaxvec = micro.Cmaxvec; 30 | 31 | %% ANN TRANSFER FUNCTIONS 32 | tansig_n = @(yn_1,wn,bn) 2./(1+exp(-2*(yn_1*wn+bn))) - 1; % inputs are: y_(n-1), w_n, b_n; output: F_n for Fn = tansig 33 | purelin_n = @(yn_1,wn,bn) yn_1*wn+bn; % inputs are: y_(n-1), w_n, b_n; output: F_n for Fn = purelin 34 | 35 | %% DERIVATIVES OF ANN TRANSFER FUNCTIONS 36 | dtansig_n = @(yn_1,wn,bn) (4*wn.*exp(-2*(yn_1*wn+bn)))./(1+exp(-2*(yn_1*wn+bn))).^2; % inputs are: y_(n-1), w_n, b_n; output: dF_n/dy_(n-1) for Fn = tansig 37 | dpurelin_n = @(yn_1,wn,bn) wn; % inputs are: y_(n-1), w_n, b_n; output: dF_n/dy_(n-1) for Fn = purelin 38 | 39 | %% INITIALIZE ANN FOR DERIVATIVE CALCULATION 40 | % Initialize ANN parameters (ANN derivative code adapted from mathworks.com/matlabcentral/answers/398531-how-to-manually-calculate-a-neural-network-output) 41 | w = cellfun(@transpose,[ML_model.IW{1},ML_model.LW(2:size(ML_model.LW,1)+1:end)],'UniformOutput',false); % weight matrices 42 | b = cellfun(@transpose,ML_model.b','UniformOutput',false); % bias vectors 43 | tf = cellfun(@(x)x.transferFcn,ML_model.layers','UniformOutput',false); % transfer functions 44 | dtf = cell(size(tf)); % derivative of transfer functions 45 | for f = 1:numel(dtf) 46 | if strcmp(tf{f},'tansig') 47 | tf{f} = 'tansig_n'; dtf{f} = 'dtansig_n'; 48 | elseif strcmp(tf{f},'purelin') 49 | tf{f} = 'purelin_n'; dtf{f} = 'dpurelin_n'; 50 | end 51 | end 52 | % Prepare mapminmax on TOM_in (pre-process) 53 | if any(cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Inputs{1}.processFcns)) 54 | gain_in = ML_model.Inputs{1}.processSettings{cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Inputs{1}.processFcns)}.gain; % gain of input mapminmax processSetting 55 | xmin_in = ML_model.Inputs{1}.processSettings{cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Inputs{1}.processFcns)}.xmin; % xmin of input mapminmax processSetting 56 | ymin_in = ML_model.Inputs{1}.processSettings{cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Inputs{1}.processFcns)}.ymin; % ymin of input mapminmax processSetting 57 | else 58 | gain_in = []; xmin_in = []; ymin_in = []; 59 | end 60 | % Prepare inverse mapminmax on CH_out (post-process) 61 | if any(cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Outputs{end}.processFcns)) 62 | gain_in = ML_model.Inputs{1}.processSettings{cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Inputs{1}.processFcns)}.gain; % gain of input mapminmax processSetting 63 | gain_out = ML_model.outputs{end}.processSettings{cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Outputs{end}.processFcns)}.gain; % gain of output mapminmax processSetting 64 | ymin_out = ML_model.outputs{end}.processSettings{cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Outputs{end}.processFcns)}.ymin; % ymin of input mapminmax processSetting 65 | xmin_out = ML_model.outputs{end}.processSettings{cellfun(@(c) strcmp(c,'mapminmax'),ML_model.Outputs{end}.processFcns)}.xmin; % xmin of input mapminmax processSetting 66 | else 67 | gain_out = []; xmin_out = []; ymin_out = []; 68 | end 69 | 70 | %% APPROXIMATE SENSITIVITY COEFFICIENTS 71 | dKE_Hom = cell(numel(xMacro),1); 72 | WaitMessage = parfor_waitbar(numel(xMacro)); %Add ('Waitbar', true) as arguments to turn on a waitbar popup 73 | fprintf(' | Approximate Sensitivity Coefficients |\n'); tic 74 | % fprintf(' |0---10---20---30---40---50---60---70---80---90-100%%|\n'); tic 75 | parfor e = 1:numel(xMacro) 76 | %% DEFINE TOM INPUT PARAMETERS 77 | if all(micro.phys == [1 2]) 78 | TOM_in = TOM_e(e,:)'; % includes VF, obj weight, and TOM BCs 79 | else 80 | TOM_in = [TOM_e(e,1)'; TOM_e(e,3:end)']; % includes only VF and TOM BCs 81 | end 82 | 83 | TOM_in0 = TOM_in; 84 | if micro.vf_cutoff > 0 85 | TOM_in(TOM_in0(1)<=micro.vf_cutoff,1) = micro.vf_cutoff; 86 | end 87 | 88 | %% DERIVATIVE OF HOMOGENIZED CONSTITUTIVE TENSORS WITH RESPECT TO VF 89 | % Perform mapminmax on TOM_in (pre-process) 90 | if isempty(gain_in) 91 | y0 = TOM_in; % input to 1st hidden layer, no pre-processing 92 | else 93 | y0 = bsxfun(@plus,bsxfun(@times,bsxfun(@minus,TOM_in,xmin_in),gain_in),ymin_in); % input to 1st hidden layer 94 | end 95 | 96 | % Loop through hidden layers + output layer 97 | y = cell(1,length(w)); % output of layer n 98 | dy = y; % derivative of y_n with respect to y_(n-1) 99 | y{1} = tansig_n(transpose(y0),w{1},b{1}); % 1st hidden layer output 100 | dy{1} = dtansig_n(transpose(y0),w{1},b{1}); dy_product = dy{1}; % 1st hidden layer output's derivative 101 | for n=2:length(w) 102 | if strcmp(tf{f},'tansig_n') 103 | y{n} = tansig_n(y{n-1},w{n},b{n}); % hidden layer n output 104 | dy{n} = dtansig_n(y{n-1},w{n},b{n}); % derivative of hidden layer n output 105 | elseif strcmp(tf{f},'purelin_n') 106 | y{n} = purelin_n(y{n-1},w{n},b{n}); % hidden layer n output 107 | dy{n} = dpurelin_n(y{n-1},w{n},b{n}); % derivative of hidden layer n output 108 | end 109 | dy_product = dy_product*dy{n}; % product ANN derivative via chain rule 110 | end 111 | 112 | % Perform inverse mapminmax on CH_out (post-process) 113 | if isempty(gain_out) 114 | CH_out = y{end}; % output of output layer, no post-processing 115 | dCH_out = dy_product; % derivative of output layer with respect to y0 116 | else 117 | CH_out = transpose((y{end} - ymin_out)./transpose(gain_out) + transpose(xmin_out)); % output of output layer 118 | dCH_out = transpose(bsxfun(@rdivide,bsxfun(@times,dy_product,gain_in),transpose(gain_out))); % derivative of output layer with respect to y0 119 | end 120 | 121 | dC_Hom = cell(3,3); 122 | dC_Homvec = dCH_out(:,1)'; % no capping of dc 123 | if TOM_in0(1) <= micro.vf_cutoff 124 | dC_Homvec = (CH_out' - Cminvec).*(1/micro.vf_cutoff).^1; % vf_cutoff derivative 125 | end 126 | for p = 1:size(phys_index,1) 127 | dC_Hom{phys_index(p,1),phys_index(p,2)} = zeros(max(ch_index{phys_index(p,1),phys_index(p,2)}(:,1)),max(ch_index{phys_index(p,1),phys_index(p,2)}(:,2))); 128 | for ind = 1:size(ch_index{phys_index(p,1),phys_index(p,2)},1) 129 | % Derivative of homogenized constitutive tensor with respect to VF 130 | dC_Hom{phys_index(p,1),phys_index(p,2)}(ch_index{phys_index(p,1),phys_index(p,2)}(ind,1),ch_index{phys_index(p,1),phys_index(p,2)}(ind,2)) = dC_Homvec(all(sort(ch_index{1,1}(ind,:),'descend') == ml_index{1,1},2)); 131 | end 132 | end 133 | 134 | %% DERIVATIVE OF HOMOGENIZED ELEMENTAL STIFFNESS MATRIX WITH RESPECT TO VF 135 | dKE_Hom{e} = cell(3,3); % # of rows/cols is the number of unique physics modes || KE_ij: coupling stiffness matrices of physics i to physics j 136 | if any(micro.phys == 1) 137 | [dKE_Hom{e}{1,1}, ~, ~] = Prepare_KE(dC_Hom{1,1},1,micro); % Mechanical 138 | dKE_Hom{e}{1,1} = reshape(dKE_Hom{e}{1,1},[micro.nen*micro.dim,micro.nen*micro.dim]); 139 | end 140 | if any(micro.phys == 2) 141 | [dKE_Hom{e}{2,2}, ~, ~] = Prepare_KE(dC_Hom{2,2},2,micro); % Thermal 142 | dKE_Hom{e}{2,2} = reshape(dKE_Hom{e}{2,2},[micro.nen,micro.nen]); 143 | end 144 | if all(micro.phys == [1 2]) 145 | [dKE_Hom{e}{2,1}, ~, ~] = Prepare_KE(dC_Hom{1,1},[1,2],micro); % Thermomechanical 146 | dKE_Hom{e}{2,1} = reshape(dKE_Hom{e}{2,1},[micro.nen*micro.dim,micro.nen]); 147 | end 148 | 149 | WaitMessage.Send; 150 | end 151 | WaitMessage.Destroy; 152 | fprintf(' | Done. Elapsed Time: %4.2f seconds. |\n',toc); satime = toc; 153 | 154 | % Calculate analytical derivative of homogenized macroscale using dKE_Hom 155 | [~, dc, ~] = Obj_Fns([],macro,U_Macro,[],[],[],[],dKE_Hom,0.5); % Case 3: Calling Obj_Fns([],macro,U_Macro,[],[],[],[],dKE_Hom,weight_e) is dc for Homogenized stiffness matrix 156 | dc(dc>0) = -dc(dc>0); 157 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Approx_SA_CFD.m: -------------------------------------------------------------------------------- 1 | % SENSITIVITY COEFFICIENT APPROXIMATION - CENTRAL FINITE DIFFERENCES 2 | function [dc, satime] = Approx_SA_CFD(Xin,macro,micro,mat,Ue_int_Macro,Fe_int_Macro,ML_model,U_Macro) 3 | %% INITIALIZE TOM DATA 4 | if all(macro.phys == [1 2]) 5 | xMacro = Xin(1:length(Xin)/2); 6 | xWeight = Xin(length(Xin)/2+1:end); 7 | num_dv = 2; 8 | macro.h_fd = [(macro.x_ub(1) - macro.x_lb(1))*macro.h_fd(1), (macro.x_ub(2) - macro.x_lb(2))*macro.h_fd(2)]; 9 | else 10 | xMacro = Xin; 11 | xWeight = ones(size(xMacro)); 12 | num_dv = 1; 13 | macro.h_fd(1) = (macro.x_ub(1) - macro.x_lb(1))*macro.h_fd(1); 14 | end 15 | x_lb = macro.x_lb; x_ub = macro.x_ub; 16 | [~, ch_index, phys_index] = Indexing('full',micro,1); 17 | [ml_index, ~, ~] = Indexing('part',micro,1); 18 | TOM_e = [xMacro, xWeight]; 19 | for p = 1:length(micro.phys) 20 | if micro.bc_type == 1 21 | TOM_e = [TOM_e, Normalize_TOM_BC(Fe_int_Macro{micro.phys(p)},micro)]; % TOM features 22 | elseif micro.bc_type == 2 23 | TOM_e = [TOM_e, Normalize_TOM_BC(Ue_int_Macro{micro.phys(p)},micro)]; % TOM features 24 | end 25 | end 26 | 27 | %% PERTURB VOLUME FRACTION 28 | TOM_e_ph = []; TOM_e_mh = []; 29 | for v = 1:num_dv 30 | TOM_temp = TOM_e; TOM_temp(:,v) = TOM_temp(:,v) + macro.h_fd(v); % plus H pertubation 31 | TOM_temp(:,v) = min(TOM_temp(:,v),macro.x_ub(v)); % if perturbed volume fraction is outside of macroscale design variable bounds then set volume fraction to bounds 32 | TOM_e_ph = [TOM_e_ph; TOM_temp]; 33 | TOM_temp = TOM_e; TOM_temp(:,v) = TOM_temp(:,v) - macro.h_fd(v); % minus H pertubation 34 | TOM_temp(:,v) = max(TOM_temp(:,v),macro.x_lb(v)); % if perturbed volume fraction is outside of macroscale design variable bounds then set volume fraction to bounds 35 | TOM_e_mh = [TOM_e_mh; TOM_temp]; 36 | end 37 | if micro.ml 38 | [C_lower_HS, C_upper_HS] = Prepare_HS_bounds(micro.mat,micro.dim,0); 39 | C_Homvec_ph = Cap_ANN(Interp_CH(ML_model, TOM_e_ph, micro),C_lower_HS(TOM_e_ph(:,1)'),C_upper_HS(TOM_e_ph(:,1)'))'; 40 | C_Homvec_mh = Cap_ANN(Interp_CH(ML_model, TOM_e_mh, micro),C_lower_HS(TOM_e_mh(:,1)'),C_upper_HS(TOM_e_mh(:,1)'))'; 41 | end 42 | 43 | %% CONTINUATION INITIALIZATION FOR SUBPROBLEMS 44 | if micro.ml == 0 && micro.cont == 1 && macro.cont == 0 % if full TO is used and TOM continutation is on and Macro continuation is off 45 | penal_k = 1; % p0 46 | del_penal = 0.3; % del_p 47 | penal_k = penal_k:del_penal:micro.penal; % penalization values 48 | if penal_k(end) ~= micro.penal; penal_k(end+1) = micro.penal; end % add last penalization value is max 49 | tolx_k = 1e-2; % w0 50 | tolx_mult = (micro.tolx/tolx_k)^(1/(length(penal_k)-1)); % w multiplier 51 | for i = 1:(length(penal_k)-1) 52 | tolx_k = [tolx_k min(tolx_k)*tolx_mult]; %#ok 53 | end 54 | loop_k = ones(length(tolx_k))*micro.maxloop/10; loop_k(end) = micro.maxloop; 55 | else 56 | penal_k = micro.penal; 57 | tolx_k = micro.tolx; 58 | loop_k = micro.maxloop; 59 | end 60 | 61 | %% APPROXIMATE SENSITIVITY COEFFICIENTS 62 | dKE_Hom = cell(numel(xMacro),1); 63 | WaitMessage = parfor_waitbar(numel(xMacro)*num_dv); %Add ('Waitbar', true) as arguments to turn on a waitbar popup 64 | fprintf(' | Approximate Sensitivity Coefficients |\n'); tic 65 | % fprintf(' |0---10---20---30---40---50---60---70---80---90-100%%|\n'); tic 66 | parfor e = 1:numel(xMacro) 67 | if mod(e,numel(xMacro)) == 0 68 | eind = numel(xMacro); % element index for multiple design variables num_dv>=2 69 | else 70 | eind = mod(e,numel(xMacro)); % element index for multiple design variables num_dv>=2 71 | end 72 | 73 | %% DERIVATIVE OF HOMOGENIZED STIFFNESS TENSORS WITH RESPECT TO VF 74 | dC_Hom = cell(3,3); 75 | if xMacro(eind) < micro.vf_cutoff 76 | if any(micro.phys == 1) 77 | dC_Hom{1,1} = (mat.Emin + 3*(mat.Emax - mat.Emin)*xMacro(e)^2) * Prepare_C(mat.nu,1,micro.dim); 78 | end 79 | if any(micro.phys == 2) 80 | dC_Hom{2,2} = (mat.kmin + 3*(mat.kmax - mat.kmin)*xMacro(e)^2) * Prepare_C(mat.nu,2,micro.dim); 81 | end 82 | else 83 | dC_Homvec = (C_Homvec_ph(:,e) - C_Homvec_mh(:,e))/(2*macro.h_fd(ceil(e/numel(xMacro)))); 84 | for p = 1:size(phys_index,1) 85 | dC_Hom{phys_index(p,1),phys_index(p,2)} = zeros(max(ch_index{phys_index(p,1),phys_index(p,2)}(:,1)),max(ch_index{phys_index(p,1),phys_index(p,2)}(:,2))); 86 | for ind = 1:size(ch_index{phys_index(p,1),phys_index(p,2)},1) 87 | % Derivative of homogenized constitutive tensor with respect to VF 88 | dC_Hom{phys_index(p,1),phys_index(p,2)}(ch_index{phys_index(p,1),phys_index(p,2)}(ind,1),ch_index{phys_index(p,1),phys_index(p,2)}(ind,2)) = dC_Homvec(all(sort(ch_index{1,1}(ind,:),'descend') == ml_index{1,1},2)); 89 | end 90 | end 91 | end 92 | 93 | %% DERIVATIVE OF HOMOGENIZED ELEMENTAL STIFFNESS MATRIX WITH RESPECT TO VF 94 | dKE_Hom{e} = cell(3,3); % # of rows/cols is the number of unique physics modes || KE_ij: coupling stiffness matrices of physics i to physics j 95 | if any(micro.phys == 1) 96 | [dKE_Hom{e}{1,1}, ~, ~] = Prepare_KE(dC_Hom{1,1},1,micro); % Mechanical 97 | dKE_Hom{e}{1,1} = reshape(dKE_Hom{e}{1,1},[micro.nen*micro.dim,micro.nen*micro.dim]); 98 | end 99 | if any(micro.phys == 2) 100 | [dKE_Hom{e}{2,2}, ~, ~] = Prepare_KE(dC_Hom{2,2},2,micro); % Thermal 101 | dKE_Hom{e}{2,2} = reshape(dKE_Hom{e}{2,2},[micro.nen,micro.nen]); 102 | end 103 | if all(micro.phys == [1 2]) 104 | [dKE_Hom{e}{2,1}, ~, ~] = Prepare_KE(dC_Hom{1,1},[1,2],micro); % Thermomechanical 105 | dKE_Hom{e}{2,1} = reshape(dKE_Hom{e}{2,1},[micro.nen*micro.dim,micro.nen]); 106 | end 107 | 108 | WaitMessage.Send; 109 | end 110 | WaitMessage.Destroy; 111 | fprintf(' | Done. Elapsed Time: %4.2f seconds. |\n\n',toc); satime = toc; 112 | 113 | % Calculate analytical derivative of homogenized macroscale using dKE_Hom 114 | [~, dc, ~] = Obj_Fns([],macro,U_Macro,[],[],[],[],dKE_Hom,0.5); % Case 3: Calling Obj_Fns([],macro,U_Macro,[],[],[],[],dKE_Hom,weight_e) is dc for Homogenized stiffness matrix 115 | dc(dc>0) = -dc(dc>0); 116 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Augment_TOMs.m: -------------------------------------------------------------------------------- 1 | % AUGMENT TOPOLOGY-OPTIMIZED MICROSTRUCTURES 2 | function [X,Y_C] = Augment_TOMs(scale,X0,Y_C0) 3 | %% CREATE COORDINATE DATA FOR OPERATIONS: X0 4 | X_data = zeros(scale.nen,7); %[Fx/Ux_e, Fy/Uy_e, Fz/Uz_e, Q/T_e, x_e, y_e, z_e] 5 | if scale.dim == 2 6 | max_aug = 8; 7 | if any(scale.phys == 1) 8 | X_data(:,1) = X0((scale.spdim_nums(1,1)+1):scale.dim:scale.spdim_nums(2,1)); % Fx/Ux of X 9 | X_data(:,2) = X0((scale.spdim_nums(1,1)+2):scale.dim:scale.spdim_nums(2,1)); % Fy/Uy of X 10 | X_data(:,3) = 0; 11 | end 12 | if any(scale.phys == 2) 13 | X_data(:,4) = X0((scale.spdim_nums(1,2)+1):scale.spdim_nums(2,2)); % Q/T of X 14 | end 15 | X_data(:,5:7) = [-1 -1 0; % node 1 16 | 1 -1 0; % node 2 17 | 1 1 0; % node 4 18 | -1 1 0]*scale.nelx/2; %node 3 19 | elseif scale.dim == 3 20 | max_aug = 48; 21 | if any(scale.phys == 1) 22 | X_data(:,1) = X0((scale.spdim_nums(1,1)+1):scale.dim:scale.spdim_nums(2,1)); % Fx/Ux of X 23 | X_data(:,2) = X0((scale.spdim_nums(1,1)+2):scale.dim:scale.spdim_nums(2,1)); % Fy/Uy of X 24 | X_data(:,3) = X0((scale.spdim_nums(1,1)+3):scale.dim:scale.spdim_nums(2,1)); % Fz/Uz of Z 25 | end 26 | if any(scale.phys == 2) 27 | X_data(:,4) = X0((scale.spdim_nums(1,2)+1):scale.spdim_nums(2,2)); % Q/T of X 28 | end 29 | X_data(:,5:7) = [-1 -1 -1; % node 1 30 | 1 -1 -1; % node 2 31 | 1 1 -1; % node 4 32 | -1 1 -1; % node 3 33 | -1 -1 1; % node 5 34 | 1 -1 1; % node 6 35 | 1 1 1; % node 8 36 | -1 1 1]*scale.nelx/2; % node 7 37 | end 38 | 39 | %% INITIALIZE OUTPUTS 40 | X = zeros(max_aug,scale.spdim); 41 | X(:,1:min(scale.spdim_nums(1,:))) = X0(1:min(scale.spdim_nums(1,:))); 42 | Y_C = cell(max_aug,1); % Homogenized Tensor Output 43 | 44 | %% DEFINE 2D/3D OPERATIONS 45 | ops = cell(max_aug,1); 46 | if scale.dim == 2 47 | ops{2} = {'Rz', 90}; 48 | ops{3} = {'Rz', 180}; 49 | ops{4} = {'Rz', 270}; 50 | ops{5} = {'Fx', []}; 51 | ops{6} = {'Rz', 90; 'Fx', []}; 52 | ops{7} = {'Fx', []; 'Rz', 90}; 53 | ops{8} = {'Rz', 180; 'Fx', []}; 54 | elseif scale.dim == 3 55 | ops{2} = {'Rx', 90}; 56 | ops{3} = {'Rx', 180}; 57 | ops{4} = {'Rx', 270}; 58 | ops{5} = {'Ry', 90}; 59 | ops{6} = {'Rx', 90; 'Ry', 90}; 60 | ops{7} = {'Ry', 90; 'Rx', 90}; 61 | ops{8} = {'Rx', 180; 'Ry', 90}; 62 | ops{9} = {'Ry', 90; 'Rx', 180}; 63 | ops{10} = {'Rx', 270; 'Ry', 90}; 64 | ops{11} = {'Ry', 90; 'Rx', 270}; 65 | ops{12} = {'Ry', 180}; 66 | ops{13} = {'Rx', 90; 'Ry', 180}; 67 | ops{14} = {'Ry', 180; 'Rx', 90}; 68 | ops{15} = {'Rx', 180; 'Ry', 180}; 69 | ops{16} = {'Ry', 270}; 70 | ops{17} = {'Rx', 90; 'Ry', 270}; 71 | ops{18} = {'Ry', 270; 'Rx', 90}; 72 | ops{19} = {'Rx', 270; 'Ry', 270}; 73 | ops{20} = {'Ry', 270; 'Rx', 270}; 74 | ops{21} = {'Rz', 90}; 75 | ops{22} = {'Rx', 180; 'Rz', 90}; 76 | ops{23} = {'Rz', 90; 'Rx', 180}; 77 | ops{24} = {'Rx', 180; 'Ry', 180; 'Rz', 90}; 78 | ops{25} = {'Fx', []}; 79 | ops{26} = {'Rx', 90; 'Fx', []}; 80 | ops{27} = {'Rx', 180; 'Fx', []}; 81 | ops{28} = {'Rx', 270; 'Fx', []}; 82 | ops{29} = {'Ry', 90; 'Fx', []}; 83 | ops{30} = {'Fx', []; 'Ry', 90}; 84 | ops{31} = {'Rx', 90; 'Ry', 90; 'Fx', []}; 85 | ops{32} = {'Rx', 90; 'Fx', []; 'Ry', 90}; 86 | ops{33} = {'Ry', 90; 'Rx', 90; 'Fx', []}; 87 | ops{34} = {'Fx', []; 'Ry', 90; 'Rx', 90}; 88 | ops{35} = {'Rx', 180; 'Ry', 90; 'Fx', []}; 89 | ops{36} = {'Rx', 180; 'Fx', []; 'Ry', 90}; 90 | ops{37} = {'Rx', 270; 'Ry', 90; 'Fx', []}; 91 | ops{38} = {'Rx', 270; 'Fx', []; 'Ry', 90}; 92 | ops{39} = {'Ry', 90; 'Rx', 270; 'Fx', []}; 93 | ops{40} = {'Fx', []; 'Ry', 90; 'Rx', 270}; 94 | ops{41} = {'Ry', 180; 'Fx', []}; 95 | ops{42} = {'Rx', 90; 'Ry', 180; 'Fx', []}; 96 | ops{43} = {'Ry', 180; 'Rx', 90; 'Fx', []}; 97 | ops{44} = {'Rx', 180; 'Ry', 180; 'Fx', []}; 98 | ops{45} = {'Rz', 90; 'Fx', []}; 99 | ops{46} = {'Fx', []; 'Rz', 90}; 100 | ops{47} = {'Rx', 180; 'Rz', 90; 'Fx', []}; 101 | ops{48} = {'Rx', 180; 'Fx', []; 'Rz', 90}; 102 | end 103 | 104 | %% PERFORM AUGMENTATIONS 105 | X(1,:) = X0(1:scale.spdim); 106 | Y_C{1} = Y_C0; 107 | for c = 2:max_aug 108 | X_data(:,:,c) = X_data(:,:,1); 109 | [X_data(:,:,c), A] = compute_A(X_data(:,:,c),ops{c}); 110 | X = transform_X(X,X_data,c,scale.spdim_nums,scale); 111 | Y_C = transform_C(Y_C,A,c,scale); 112 | end 113 | end 114 | 115 | %% COMPUTE THE TRANSFORMATION MATRIX A FOR THE GIVEN OPERATIONS 116 | function [X_data_c, A] = compute_A(X_data_c,ops) 117 | A = eye(3); 118 | for op = 1:size(ops,1) 119 | if strcmp(ops{op,1},'Rx') 120 | [X_data_c, A] = rot_x(X_data_c,ops{op,2},A); 121 | elseif strcmp(ops{op,1},'Ry') 122 | [X_data_c, A] = rot_y(X_data_c,ops{op,2},A); 123 | elseif strcmp(ops{op,1},'Rz') 124 | [X_data_c, A] = rot_z(X_data_c,ops{op,2},A); 125 | elseif strcmp(ops{op,1},'Fx') 126 | [X_data_c, A] = flip_x(X_data_c,A); 127 | elseif strcmp(ops{op,1},'Fy') 128 | [X_data_c, A] = flip_y(X_data_c,A); 129 | elseif strcmp(ops{op,1},'Fz') 130 | [X_data_c, A] = flip_z(X_data_c,A); 131 | end 132 | end 133 | end 134 | 135 | %% TRANSFORM COORDINATE AND BOUNDARY CONDITION DATA 136 | function X = transform_X(X,X_data,c,spdim_nums,scale) 137 | for e1 = 1:scale.nen %original coord 138 | for e2 = 1:scale.nen %transformed coord 139 | if any(X_data(e1,5,1) == X_data(e2,5,c)) && any(X_data(e1,6,1) == X_data(e2,6,c)) && any(X_data(e1,7,1) == X_data(e2,7,c)) 140 | if any(scale.phys == 1) 141 | X(c,spdim_nums(1,1)+e1*scale.dim-(scale.dim-1)) = X_data(e2,1,c)'; % Fx/Ux of X 142 | X(c,spdim_nums(1,1)+e1*scale.dim-(scale.dim-2)) = X_data(e2,2,c)'; % Fy/Uy of X 143 | if scale.dim == 3; X(c,spdim_nums(1,1)+e1*scale.dim) = X_data(e2,3,c)'; end % Fz/Uz of X 144 | end 145 | if any(scale.phys == 2) 146 | X(c,spdim_nums(1,2)+e1) = X_data(e2,4,c)'; % Q/T of X 147 | end 148 | end 149 | end 150 | end 151 | X(c,1:min(spdim_nums(spdim_nums>0))) = X(1,1:min(spdim_nums(spdim_nums>0))); 152 | end 153 | 154 | %% TRANSFORM HOMOGENIZED CONSTITUTIVE TENSORS 155 | function Y_C = transform_C(Y_C,A,c,scale) 156 | if scale.dim == 2 157 | Abar = [A(1,1)*A(1,1), A(1,2)*A(1,2), 2*A(1,1)*A(1,2); 158 | A(2,1)*A(2,1), A(2,2)*A(2,2), 2*A(2,1)*A(2,2); 159 | A(1,1)*A(2,1), A(1,2)*A(2,2), A(1,1)*A(2,2)+A(1,2)*A(2,1)]; 160 | F = eye(3); F(3,3) = 2; 161 | elseif scale.dim == 3 162 | Abar = [A(1,1)*A(1,1), A(1,2)*A(1,2), A(1,3)*A(1,3), 2*A(1,2)*A(1,3), 2*A(1,1)*A(1,3), 2*A(1,1)*A(1,2); 163 | A(2,1)*A(2,1), A(2,2)*A(2,2), A(2,3)*A(2,3), 2*A(2,2)*A(2,3), 2*A(2,1)*A(2,3), 2*A(2,1)*A(2,2); 164 | A(3,1)*A(3,1), A(3,2)*A(3,2), A(3,3)*A(3,3), 2*A(3,2)*A(3,3), 2*A(3,1)*A(3,3), 2*A(3,1)*A(3,2); 165 | A(2,1)*A(3,1), A(2,2)*A(3,2), A(2,3)*A(3,3), A(2,2)*A(3,3) + A(2,3)*A(3,2), A(2,1)*A(3,3) + A(2,3)*A(3,1), A(2,1)*A(3,2) + A(2,2)*A(3,1); 166 | A(1,1)*A(3,1), A(1,2)*A(3,2), A(1,3)*A(3,3), A(1,2)*A(3,3) + A(1,3)*A(3,2), A(1,1)*A(3,3) + A(1,3)*A(3,1), A(1,1)*A(3,2) + A(1,2)*A(3,1); 167 | A(1,1)*A(2,1), A(1,2)*A(2,2), A(1,3)*A(2,3), A(1,2)*A(2,3) + A(1,3)*A(2,2), A(1,1)*A(2,3) + A(1,3)*A(2,1), A(1,1)*A(2,2) + A(1,2)*A(2,1)]; 168 | F = eye(6); F(4,4) = 2; F(5,5) = 2; F(6,6) = 2; 169 | end 170 | Ma = F*(Abar/F); 171 | Y_C{c} = cell(3,3); % # of rows/cols is the number of unique physics modes 172 | if any(scale.phys == 1) 173 | Y_C{c}{1,1} = Ma'*Y_C{1}{1,1}*Ma; % Mechanical 174 | end 175 | if any(scale.phys == 2) 176 | Y_C{c}{2,2} = A(1:scale.dim,1:scale.dim)'*Y_C{1}{2,2}*A(1:scale.dim,1:scale.dim); % Thermal 177 | end 178 | if all(scale.phys == [1 2]) 179 | Y_C{c}{2,1} = Ma*Y_C{1}{2,1}; % Thermomechanical 180 | end 181 | end 182 | 183 | %% ROTATE ABOUT THE X, Y, OR Z AXIS 184 | function [cd, R] = rot_x(cd0,a,A) 185 | R = [1 0 0; 0 cosd(a) -sind(a); 0 sind(a) cosd(a)]; %Rx 186 | RR = [R zeros(3,4); 0 0 0 1 0 0 0; zeros(3,4) R]; % expanded rotation matrix for operation on the 1,2,3,5,6,7 columns of X_data (the columns with data that is orientation-relevant) 187 | 188 | if size(cd0,2) == 3 % if coordinates only 189 | cd = cd0*R'; 190 | else % operate on the expanded cd 191 | cd = cd0*RR'; 192 | end 193 | R = R*A; 194 | end 195 | function [cd, R] = rot_y(cd0,a,A) 196 | R = [cosd(a) 0 sind(a); 0 1 0; -sind(a) 0 cosd(a)]; %Ry 197 | RR = [R zeros(3,4); 0 0 0 1 0 0 0; zeros(3,4) R]; % expanded rotation matrix for operation on the 1,2,3,5,6,7 columns of X_data (the columns with data that is orientation-relevant) 198 | 199 | if size(cd0,2) == 3 % if coordinates only 200 | cd = cd0*R'; 201 | else % operate on the expanded cd 202 | cd = cd0*RR'; 203 | end 204 | R = R*A; 205 | end 206 | function [cd, R] = rot_z(cd0,a,A) 207 | R = [cosd(a) -sind(a) 0; sind(a) cosd(a) 0; 0 0 1]; %Rz 208 | RR = [R zeros(3,4); 0 0 0 1 0 0 0; zeros(3,4) R]; % expanded rotation matrix for operation on the 1,2,3,5,6,7 columns of X_data (the columns with data that is orientation-relevant) 209 | 210 | if size(cd0,2) == 3 % if coordinates only 211 | cd = cd0*R'; 212 | else % operate on the expanded cd 213 | cd = cd0*RR'; 214 | end 215 | R = R*A; 216 | end 217 | 218 | %% FLIP COORDINATES ALONG THE X, Y, OR Z DIRECTION 219 | function [cd, R] = flip_x(cd0,A) 220 | R = eye(3); 221 | R(1,1) = -R(1,1); 222 | RR = [R zeros(3,4); 0 0 0 1 0 0 0; zeros(3,4) R]; % expanded rotation matrix for operation on the 1,2,3,5,6,7 columns of X_data (the columns with data that is orientation-relevant) 223 | 224 | if size(cd0,2) == 3 % if coordinates only 225 | cd = cd0*R'; 226 | else 227 | cd = cd0*RR'; 228 | end 229 | R = R*A; 230 | end 231 | function [cd, R] = flip_y(cd0,A) 232 | R = eye(3); 233 | R(2,2) = -R(2,2); 234 | RR = [R zeros(3,4); 0 0 0 1 0 0 0; zeros(3,4) R]; % expanded rotation matrix for operation on the 1,2,3,5,6,7 columns of X_data (the columns with data that is orientation-relevant) 235 | 236 | if size(cd0,2) == 3 % if coordinates only 237 | cd = cd0*R'; 238 | else 239 | cd = cd0*RR'; 240 | end 241 | R = R*A; 242 | end 243 | function [cd, R] = flip_z(cd0,A) 244 | R = eye(3); 245 | R(3,3) = -R(3,3); 246 | RR = [R zeros(3,4); 0 0 0 1 0 0 0; zeros(3,4) R]; % expanded rotation matrix for operation on the 1,2,3,5,6,7 columns of X_data (the columns with data that is orientation-relevant) 247 | 248 | if size(cd0,2) == 3 % if coordinates only 249 | cd = cd0*R'; 250 | else 251 | cd = cd0*RR'; 252 | end 253 | R = R*A; 254 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Cap_ANN.m: -------------------------------------------------------------------------------- 1 | % CAP ANN PREDICTIONS WITH H-S BOUNDS 2 | function pred_out = Cap_ANN(pred_in,Cminvec,Cmaxvec) 3 | % pred_in [rows = number of predictions, cols = number of C properties] 4 | % for 1 prediction, pred_in is a row vector 5 | if size(Cminvec,1)==1 && size(Cmaxvec,1)==1 6 | pred_out = arrayfun(@min,arrayfun(@max,pred_in,repmat(Cminvec,[size(pred_in,1),1])),repmat(Cmaxvec,[size(pred_in,1),1])); 7 | else 8 | pred_out = arrayfun(@min,arrayfun(@max,pred_in,Cminvec),Cmaxvec); 9 | end 10 | 11 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Check_CH.m: -------------------------------------------------------------------------------- 1 | % CHECK TO MAKE SURE HOMOGENIZED TENSORS ARE VALID 2 | function [valid, validCH] = Check_CH(C,scale,mat,VF) 3 | tol_ch = eps^0.5; % add/subtract a tolerance to the Cmin and Cmax thresholds 4 | valid = 0; 5 | validCH = cell(size(C)); 6 | for C_row = 1:size(C,1) 7 | for C_col = 1:size(C,2) 8 | if ~isempty(C{C_row,C_col}) 9 | if C_row == 1 && C_col == 1 % mechanical CH checking 10 | Cmin = mat.Emin * Prepare_C(mat.nu,1,scale.dim) - tol_ch; 11 | Cmax = mat.Emax * Prepare_C(mat.nu,1,scale.dim) + tol_ch; 12 | for i = 1:scale.dim 13 | for j = 1:scale.dim 14 | if Cmin(i,j) <= C{C_row,C_col}(i,j) && C{C_row,C_col}(i,j) <= Cmax(i,j) % check top left [dim x dim] values of CH 15 | valid = 1; 16 | validCH = C; 17 | else 18 | valid = 0; 19 | if VF > 0.5 20 | validCH{C_row,C_col}(i,j) = Cmax(i,j); 21 | else 22 | validCH{C_row,C_col}(i,j) = Cmin(i,j); 23 | end 24 | return 25 | end 26 | end 27 | end 28 | for k = (scale.dim+1):size(C{C_row,C_col},2) 29 | if Cmin(k,k) <= C{C_row,C_col}(k,k) && C{C_row,C_col}(k,k) <= Cmax(k,k) % check bottom right diagonal values of CH 30 | valid = 1; 31 | validCH = C; 32 | else 33 | valid = 0; 34 | if VF > 0.5 35 | validCH{C_row,C_col}(k,k) = Cmax(k,k); 36 | else 37 | validCH{C_row,C_col}(k,k) = Cmin(k,k); 38 | end 39 | return 40 | end 41 | end 42 | end 43 | if C_row == 2 && C_col == 2 % thermal CH checking 44 | Cmin = mat.kmin * Prepare_C(mat.nu,2,scale.dim) - tol_ch; 45 | Cmax = mat.kmax * Prepare_C(mat.nu,2,scale.dim) + tol_ch; 46 | for k = 1:scale.dim 47 | if Cmin(k,k) <= C{C_row,C_col}(k,k) && C{C_row,C_col}(k,k) <= Cmax(k,k) % check diagonal values of CH 48 | valid = 1; 49 | validCH = C; 50 | else 51 | valid = 0; 52 | if VF > 0.5 53 | validCH{C_row,C_col}(k,k) = Cmax(k,k); 54 | else 55 | validCH{C_row,C_col}(k,k) = Cmin(k,k); 56 | end 57 | return 58 | end 59 | end 60 | end 61 | if C_row == 2 && C_col == 1 62 | valid = 1; % do not check homogenized thermal strain for feasibility 63 | 64 | % alphaH = C{1,1}/C{C_row,C_col}'; % Homogenized thermal strain vector 65 | % Amin = 1/(scale.dim)/mat.Amin; 66 | % Amax = 1/(scale.dim)/mat.Amax; 67 | % for k = 1:scale.dim 68 | % if Amax <= alphaH(k) && alphaH(k) <= Amin 69 | % valid = 1; 70 | % else 71 | % valid = 0; 72 | % return 73 | % end 74 | % end 75 | end 76 | end 77 | end 78 | end 79 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/DeHomogenize_Macro.m: -------------------------------------------------------------------------------- 1 | % DE-HOMOGENIZE FINAL MACROSCALE DESIGN 2 | function [dehom_history, final_history] = DeHomogenize_Macro(Xin,macro,micro,mat,KE,opt_history,final_history,foutput) 3 | if all(macro.phys == [1 2]) 4 | xMacro = Xin(1:length(Xin)/2); 5 | xWeight = Xin(length(Xin)/2+1:end); 6 | else 7 | xMacro = Xin; 8 | xWeight = ones(size(xMacro)); 9 | end 10 | 11 | %% INITIALIZE DEHOMOGENIZATION HISTORY 12 | dehom_history = cell(1,7); dehom_history{1,1} = 'Description'; dehom_history{1,2} = 'xMacro'; dehom_history{1,3} = 'xMicro'; dehom_history{1,4} = 'xMulti'; dehom_history{1,5} = 'xWeight'; dehom_history{1,6} = 'Time'; dehom_history{1,7} = 'c_vec'; 13 | dehom_history{end+1,1} = ['Initial, Perform DH_' num2str(macro.dehom)]; dehom_history{end,2} = xMacro; dehom_history{end,5} = xWeight; dhtime = tic; 14 | 15 | %% DE-HOMOGENIZE FINAL MACROSCALE DESIGN 16 | fprintf(' De-homogenize Macroscale Design:\n'); 17 | fprintf('==========================================================\n'); 18 | if macro.dehom == 0 19 | fprintf(' | Skip De-homogenization |\n'); 20 | xMicro = xMacro; 21 | xMulti = xMacro; 22 | else 23 | if macro.dehom == 1 % Local topology optimization (TOM) 24 | 25 | % 1.a De-homogenize TOMs 26 | subtime = tic; 27 | xMicro = DeHomogenize_TOM(xMacro,xWeight,macro,micro,mat,KE,opt_history); % Solve local TOMs 28 | dehom_history{end+1,1} = 'DH_1a_TOM_Multi'; dehom_history{end,2} = xMacro; dehom_history{end,3} = xMicro; dehom_history{end,4} = cell2mat(xMicro); dehom_history{end,5} = xWeight; dehom_history{end,6} = toc(subtime); 29 | 30 | % 1.b Connect TOMs 31 | subtime = tic; 32 | if macro.PP_tri_infill 33 | xMicro = Tri_Infill_Method(macro,micro,xMicro); 34 | end 35 | dehom_history{end+1,1} = 'DH_1b_Connected_Multi'; dehom_history{end,2} = xMacro; dehom_history{end,3} = xMicro; dehom_history{end,4} = cell2mat(xMicro); dehom_history{end,5} = xWeight; dehom_history{end,6} = toc(subtime); 36 | 37 | % 1.c Scale TOMs 38 | subtime = tic; 39 | xMicro = TOM_scaler(macro,micro,xMicro); 40 | dehom_history{end+1,1} = 'DH_1c_Scaled_Multi'; dehom_history{end,2} = xMacro; dehom_history{end,3} = xMicro; dehom_history{end,4} = cell2mat(xMicro); dehom_history{end,5} = xWeight; dehom_history{end,6} = toc(subtime); 41 | 42 | % Define Multi 43 | xMulti = cell2mat(xMicro); 44 | multi = macro; 45 | multi.nelx = macro.nelx*micro.nelx; multi.nely = macro.nely*micro.nely; multi.nelz = macro.nelz*micro.nelz; 46 | multi.nele = multi.nelx*multi.nely*max(multi.nelz,1); % number of elements 47 | multi.DDx = multi.nelx; multi.DDy = multi.nely; if multi.nelz == 0; multi.DDz = 1; else; macro.DDz = macro.nelz; end % Design Domain size. DDz is thickness in 2D 48 | 49 | end 50 | save_results(foutput,dehom_history,[],[],[]); 51 | final_history{end+1,1} = 'multiscale_ini'; final_history{end,2} = ones(size(xMulti))*macro.volfrac; 52 | final_history{end+1,1} = 'multiscale_final'; final_history{end,2} = xMulti; 53 | 54 | % Post-process final de-homogenized topology 55 | if (macro.final_comp || macro.PP_rm_low_SE > 0 || macro.PP_rm_skel) 56 | if macro.final_comp 57 | subtime = tic; 58 | [c0, ~] = SS_FEA(final_history{end-1,2},multi,mat); % add command line output eventually for final_comp operations 59 | final_history{end-1,4} = c0; final_history{end-1,5} = toc(subtime); final_history{end-1,6} = c0/c0; 60 | end 61 | 62 | % Skeletonization post processing 63 | if macro.PP_rm_skel 64 | subtime = tic; 65 | xMulti = PostProcess_Skel(xMulti>macro.den_threshold); 66 | dehom_history{end+1,1} = 'DH_PP0_Skel_Multi'; dehom_history{end,2} = xMacro; dehom_history{end,3} = xMicro; dehom_history{end,4} = xMulti; dehom_history{end,5} = xWeight; dehom_history{end,6} = toc(subtime); 67 | end 68 | 69 | % xMulti FEA analysis after PPskel, needed for PP_rm_low_SE or final_comp 70 | if (macro.final_comp || macro.PP_rm_low_SE > 0) 71 | subtime = tic; 72 | [c, c_vec] = SS_FEA(xMulti,multi,mat); % initial FEA analysis needed for PP_rm_low_SE or final_comp 73 | else 74 | c = []; 75 | end 76 | 77 | % Remove low strain energy solids 78 | if macro.PP_rm_low_SE > 0 79 | filter_neighborhood = [0 1 0; 1 1 1; 0 1 0]; 80 | xMulti(reshape(c_vec(:,1),size(xMulti)) < mean(c_vec(:,1))*0.001) = 0; 81 | xMulti = imopen(xMulti,filter_neighborhood); % open 82 | xMulti = imclose(xMulti,filter_neighborhood); % close 83 | dehom_history{end+1,1} = 'DH_PP1_Post_Multi'; dehom_history{end,2} = xMacro; dehom_history{end,3} = xMicro; dehom_history{end,4} = xMulti; dehom_history{end,5} = xWeight; dehom_history{end,6} = toc(subtime); dehom_history{end,7} = c_vec; 84 | 85 | for f = 2:macro.PP_rm_low_SE % repeat PP_rm_low_SE iterations 86 | subtime = tic; 87 | [c, c_vec] = SS_FEA(xMulti,multi,mat); 88 | xMulti(reshape(c_vec(:,1),size(xMulti)) < mean(c_vec(:,1))*0.001) = 0; 89 | xMulti = imopen(xMulti,filter_neighborhood); % open 90 | xMulti = imclose(xMulti,filter_neighborhood); % close 91 | dehom_history{end+1,1} = ['DH_PP' num2str(f) '_Post_Multi']; dehom_history{end,2} = xMacro; dehom_history{end,3} = xMicro; dehom_history{end,4} = xMulti; dehom_history{end,5} = xWeight; dehom_history{end,6} = toc(subtime); dehom_history{end,7} = c_vec; 92 | end 93 | end 94 | final_history{end,4} = c; % save final multiscale topology's compliance 95 | end 96 | final_history{end,2} = xMulti; % save final multiscale topology 97 | 98 | % Calculate Compatibility 99 | final_history{end,5} = toc(dhtime); 100 | if macro.final_comp 101 | final_history{end,6} = c/c0; % Multiscale Compliance Ratio 102 | final_history{end,7} = c/opt_history{end,4}; % DH Compatibility 103 | end 104 | end 105 | dehom_history{end+1,1} = 'DH_Final_Multi'; dehom_history{end,2} = xMacro; dehom_history{end,3} = xMicro; dehom_history{end,4} = xMulti; dehom_history{end,5} = xWeight; dehom_history{end,6} = toc(dhtime); 106 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/DeHomogenize_TOM.m: -------------------------------------------------------------------------------- 1 | % DE-HOMOGENIZE MACROSCALE WITH FINAL SET OF TOMs 2 | function xMicro = DeHomogenize_TOM(xMacro,xWeight,macro,micro,mat,KE,opt_history) 3 | %% INITIALIZE TOM DATA 4 | Ue_int_Macro = opt_history{end,9}; 5 | Fe_int_Macro = opt_history{end,10}; 6 | TOM_e = [xMacro, xWeight]; 7 | for p = 1:length(micro.phys) 8 | if micro.bc_type == 1 9 | TOM_e = [TOM_e, Normalize_TOM_BC(Fe_int_Macro{micro.phys(p)},micro)]; % TOM features 10 | elseif micro.bc_type == 2 11 | TOM_e = [TOM_e, Normalize_TOM_BC(Ue_int_Macro{micro.phys(p)},micro)]; % TOM features 12 | end 13 | end 14 | 15 | %% CONTINUATION INITIALIZATION FOR SUBPROBLEMS 16 | if micro.cont == 1 && macro.cont == 0 % if TOM continutation is on and Macro continuation is off 17 | penal_k = 1; % p0 18 | del_penal = 0.3; % del_p 19 | penal_k = penal_k:del_penal:micro.penal; % penalization values 20 | if penal_k(end) ~= micro.penal; penal_k(end+1) = micro.penal; end % add last penalization value is max 21 | tolx_k = 1e-2; % w0 22 | tolx_mult = (micro.tolx/tolx_k)^(1/(length(penal_k)-1)); % w multiplier 23 | for i = 1:(length(penal_k)-1) 24 | tolx_k = [tolx_k min(tolx_k)*tolx_mult]; %#ok 25 | end 26 | loop_k = ones(size(tolx_k))*micro.maxloop/10; loop_k(end) = micro.maxloop; 27 | else 28 | penal_k = micro.penal; 29 | tolx_k = micro.tolx; 30 | loop_k = micro.maxloop; 31 | end 32 | 33 | %% MICROSCALE DE-HOMOGENIZATION 34 | xMicro = cell(numel(xMacro),1); cMicro = cell(numel(xMacro),1); 35 | WaitMessage = parfor_waitbar(numel(xMacro)); %Add ('Waitbar', true) as arguments to turn on a waitbar popup 36 | fprintf(' | Solve Final Inner Optimization Problems (TOMs) |\n'); tic 37 | parfor e = 1:numel(xMacro) 38 | if xMacro(e) < 0.02 39 | xMicro{e} = zeros(micro.nelx,micro.nely,max(1,micro.nelz)); 40 | else 41 | xMicro{e} = ini_design(micro,xMacro(e),macro.x_lb(1),macro.x_ub(1)); 42 | for pk = 1:numel(penal_k) 43 | [xMicro{e}, cMicro{e}] = Micro_TO(micro,TOM_e(e,:),xMicro{e},penal_k(pk),loop_k(pk),tolx_k(pk),mat,KE); 44 | end 45 | if ~isreal(sum(sum(sum(xMicro{e})))) 46 | xMicro{e} = zeros(micro.nelx,micro.nely,max(1,micro.nelz)); 47 | end 48 | end 49 | WaitMessage.Send; 50 | end 51 | WaitMessage.Destroy; 52 | fprintf(' | Done. Elapsed Time: %4.2f seconds. |\n',toc); 53 | xMicro = reshape(xMicro,[macro.nelx,macro.nely,max(macro.nelz,1)]); 54 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/FEA.m: -------------------------------------------------------------------------------- 1 | % FINITE ELEMENT ANALYSIS FOR ANY SCALE 2 | function [U,Ue_int,Fe_int] = FEA(xPhys,scale,penal,mat,KE,F,U,N) 3 | %% ELEMENT CONNECTIVITY MATRICES 4 | edof = cell(1,3); 5 | if any(scale.phys == 1) % 1 = Mechanical 6 | edof{1} = zeros(scale.nele,scale.nen*scale.dim); 7 | end 8 | if any(scale.phys == 2) % 2 = Thermal 9 | edof{2} = zeros(scale.nele,scale.nen); 10 | end 11 | 12 | for i = 1:scale.nele 13 | for j = 1:scale.nen 14 | for k = 1:scale.dim 15 | if any(scale.phys == 1) % 1 = Mechanical 16 | edof{1}(i,(j-1)*scale.dim + k) = scale.dim * scale.necon((i-1)*scale.nen + j) + k; 17 | end 18 | end 19 | if any(scale.phys == 2) % 2 = Thermal 20 | edof{2}(i,j) = scale.necon((i-1)*scale.nen + j) + 1; 21 | end 22 | end 23 | end 24 | 25 | %% ASSEMBLE STIFFNESS MATRICES 26 | [K, K_nele, ~] = Prepare_K(xPhys,scale,penal,mat,KE,[],[],edof,[]); % Case 1: Calling Prepare_K(xPhys,scale,penal,mat,KE,[],[],edof,[]) is SIMP stiffness matrix only 27 | 28 | %% SOLVE FEA 29 | if any(scale.phys == 2) % 2 = Thermal 30 | F{2}(N{2}==1) = F{2}(N{2}==1) - K{2,2}(N{2}==1,N{2}==0)*U{2}(N{2}==0); 31 | for LC = 1:length(U{2}(1,:)) 32 | try 33 | R = chol(K{2,2}(N{2}(:,LC)==1,N{2}(:,LC)==1)); 34 | U{2}(N{2}(:,LC)==1,LC) = R\(R'\F{2}(N{2}(:,LC)==1,LC)); % solve Cholesky thermal problem 35 | catch 36 | U{2}(N{2}(:,LC)==1,LC) = K{2,2}(N{2}(:,LC)==1,N{2}(:,LC)==1)\F{2}(N{2}(:,LC)==1,LC); % solve standard thermal problem 37 | end 38 | end 39 | end 40 | 41 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 42 | Ft = zeros(size(F{1})); 43 | Ft(N{1}==1) = K{2,1}(N{1}==1,N{2}==1)*U{2}(N{2}==1); % solve thermomechanical problem 44 | F{1} = F{1} + Ft; % add thermally-induced forces to mechanical load 45 | end 46 | if any(scale.phys == 1) % 1 = Mechanical 47 | F{1}(N{1}==1) = F{1}(N{1}==1) - K{1,1}(N{1}==1,N{1}==0)*U{1}(N{1}==0); 48 | for LC = 1:length(U{1}(1,:)) 49 | try 50 | R = chol(K{1,1}(N{1}(:,LC)==1,N{1}(:,LC)==1)); 51 | U{1}(N{1}(:,LC)==1,LC) = R\(R'\F{1}(N{1}(:,LC)==1,LC)); % solve Cholesky mechanical problem 52 | catch 53 | U{1}(N{1}(:,LC)==1,LC) = K{1,1}(N{1}(:,LC)==1,N{1}(:,LC)==1)\F{1}(N{1}(:,LC)==1,LC); % solve standard mechanical problem 54 | end 55 | end 56 | end 57 | 58 | Ue_int = cell(1,3); 59 | Fe_int = cell(1,3); 60 | if any(scale.phys == 1) % 1 = Mechanical 61 | Ue_int{1} = zeros(scale.nele,scale.nen*scale.dim,length(U{1}(1,:))); 62 | Fe_int{1} = zeros(scale.nele,scale.nen*scale.dim,length(U{1}(1,:))); 63 | if length(F{1}(1,:)) == 1 64 | for i = 1:scale.nele 65 | Ue_int{1}(i,:) = U{1}(edof{1}(i,:)); % local elemental displacements 66 | Fe_int{1}(i,:) = reshape(K_nele{1,1}(i,:),[scale.nen*scale.dim,scale.nen*scale.dim])*Ue_int{1}(i,:)'; % solve for internal forces 67 | end 68 | end 69 | end 70 | if any(scale.phys == 2) % 2 = Thermal 71 | Ue_int{2} = zeros(scale.nele,scale.nen,length(U{2}(1,:))); 72 | Fe_int{2} = zeros(scale.nele,scale.nen,length(U{2}(1,:))); 73 | if length(F{2}(1,:)) == 1 74 | for i = 1:scale.nele 75 | Ue_int{2}(i,:) = U{2}(edof{2}(i,:)); % local elemental temperatures 76 | Fe_int{2}(i,:) = reshape(K_nele{2,2}(i,:),[scale.nen,scale.nen])*Ue_int{2}(i,:)'; % solve for internal fluxes 77 | end 78 | end 79 | end 80 | if any(scale.phys == 3) % 3 = Fluid 81 | Ue_int{3} = zeros(scale.nele,scale.nen*scale.dim,length(U{3}(1,:))); 82 | Fe_int{3} = zeros(scale.nele,scale.nen*scale.dim,length(U{3}(1,:))); 83 | end 84 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Hom_FEA.m: -------------------------------------------------------------------------------- 1 | % HOMOGENIZED FINITE ELEMENT ANALYSIS FOR ANY SCALE 2 | function [U,Ue_int,Fe_int] = Hom_FEA(KE_Hom,scale,F,U,N) 3 | %% ELEMENT CONNECTIVITY MATRICES 4 | edof = cell(1,3); 5 | if any(scale.phys == 1) % 1 = Mechanical 6 | edof{1} = zeros(scale.nele,scale.nen*scale.dim); 7 | end 8 | if any(scale.phys == 2) % 2 = Thermal 9 | edof{2} = zeros(scale.nele,scale.nen); 10 | end 11 | 12 | for i = 1:scale.nele 13 | for j = 1:scale.nen 14 | for k = 1:scale.dim 15 | if any(scale.phys == 1) % 1 = Mechanical 16 | edof{1}(i,(j-1)*scale.dim + k) = scale.dim * scale.necon((i-1)*scale.nen + j) + k; 17 | end 18 | end 19 | if any(scale.phys == 2) % 2 = Thermal 20 | edof{2}(i,j) = scale.necon((i-1)*scale.nen + j) + 1; 21 | end 22 | end 23 | end 24 | 25 | %% ASSEMBLE STIFFNESS MATRICES 26 | [K_Hom, K_Hom_nele, ~] = Prepare_K([],scale,[],[],[],KE_Hom,[],edof,[]); % Case 3: Calling Prepare_K([],scale,[],[],[],KE_Hom,[],edof,[]) is Homogenized stiffness matrix only 27 | 28 | %% SOLVE FEA 29 | if any(scale.phys == 2) % 2 = Thermal 30 | F{2}(N{2}==1) = F{2}(N{2}==1) - K_Hom{2,2}(N{2}==1,N{2}==0)*U{2}(N{2}==0); 31 | for LC = 1:length(U{2}(1,:)) 32 | try 33 | R = chol(K_Hom{2,2}(N{2}(:,LC)==1,N{2}(:,LC)==1)); 34 | U{2}(N{2}(:,LC)==1,LC) = R\(R'\F{2}(N{2}(:,LC)==1,LC)); % solve Cholesky thermal problem 35 | catch 36 | U{2}(N{2}(:,LC)==1,LC) = K_Hom{2,2}(N{2}(:,LC)==1,N{2}(:,LC)==1)\F{2}(N{2}(:,LC)==1,LC); % solve standard thermal problem 37 | end 38 | end 39 | end 40 | 41 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 42 | Ft = zeros(size(F{1})); 43 | Ft(N{1}==1) = K_Hom{2,1}(N{1}==1,N{2}==1)*U{2}(N{2}==1); % solve thermomechanical problem 44 | F{1} = F{1} + Ft; % add thermally-induced forces to mechanical load 45 | end 46 | if any(scale.phys == 1) % 1 = Mechanical 47 | F{1}(N{1}==1) = F{1}(N{1}==1) - K_Hom{1,1}(N{1}==1,N{1}==0)*U{1}(N{1}==0); 48 | for LC = 1:length(U{1}(1,:)) 49 | try 50 | R = chol(K_Hom{1,1}(N{1}(:,LC)==1,N{1}(:,LC)==1)); 51 | U{1}(N{1}(:,LC)==1,LC) = R\(R'\F{1}(N{1}(:,LC)==1,LC)); % solve Cholesky mechanical problem 52 | catch 53 | U{1}(N{1}(:,LC)==1,LC) = K_Hom{1,1}(N{1}(:,LC)==1,N{1}(:,LC)==1)\F{1}(N{1}(:,LC)==1,LC); % solve standard mechanical problem 54 | end 55 | end 56 | end 57 | 58 | Ue_int = cell(1,3); 59 | Fe_int = cell(1,3); 60 | if any(scale.phys == 1) % 1 = Mechanical 61 | Ue_int{1} = zeros(scale.nele,scale.nen*scale.dim,length(U{1}(1,:))); 62 | Fe_int{1} = zeros(scale.nele,scale.nen*scale.dim,length(U{1}(1,:))); 63 | if length(F{1}(1,:)) == 1 64 | for i = 1:scale.nele 65 | Ue_int{1}(i,:) = U{1}(edof{1}(i,:)); % local elemental displacements 66 | Fe_int{1}(i,:) = reshape(K_Hom_nele{1,1}(i,:),[scale.nen*scale.dim,scale.nen*scale.dim])*Ue_int{1}(i,:)'; % solve for internal forces 67 | end 68 | end 69 | end 70 | if any(scale.phys == 2) % 2 = Thermal 71 | Ue_int{2} = zeros(scale.nele,scale.nen,length(U{2}(1,:))); 72 | Fe_int{2} = zeros(scale.nele,scale.nen,length(U{2}(1,:))); 73 | if length(F{2}(1,:)) == 1 74 | for i = 1:scale.nele 75 | Ue_int{2}(i,:) = U{2}(edof{2}(i,:)); % local elemental temperatures 76 | Fe_int{2}(i,:) = reshape(K_Hom_nele{2,2}(i,:),[scale.nen,scale.nen])*Ue_int{2}(i,:)'; % solve for internal fluxes 77 | end 78 | end 79 | end 80 | if any(scale.phys == 3) % 3 = Fluid 81 | Ue_int{3} = zeros(scale.nele,scale.nen*scale.dim,length(U{3}(1,:))); 82 | Fe_int{3} = zeros(scale.nele,scale.nen*scale.dim,length(U{3}(1,:))); 83 | end 84 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Hom_Macro_Fn.m: -------------------------------------------------------------------------------- 1 | % HOMOGENIZED MACROSCALE OBJECTIVE FUNCTION 2 | function [c, dc] = Hom_Macro_Fn(Xin,macro,micro,mat,KE,FE,F0_Macro,U0_Macro,N_Macro,ML_model) 3 | %% LOAD TEMP FILES AND ASSEMBLE OPT_HISTORY 4 | temp_files = dir(['temp_files/temp_' num2str(macro.nelx) 'x' num2str(macro.nely) 'x' num2str(macro.nelz) '_' num2str(macro.rand_id) '_*']); 5 | load(['temp_files/' temp_files(end).name],'opt_history') 6 | 7 | %% INITIALIZE DESIGN VECTORS 8 | if all(macro.phys == [1 2]) 9 | xMacro = Xin(1:length(Xin)/2); 10 | xWeight = Xin(length(Xin)/2+1:end); 11 | xMacro(:) = (macro.den_H*xMacro(:))./macro.den_Hs; % apply density filter 12 | Xin = [xMacro(:); xWeight(:)]; 13 | else 14 | xMacro = Xin; 15 | xWeight = ones(size(xMacro)); 16 | xMacro(:) = (macro.den_H*xMacro(:))./macro.den_Hs; % apply density filter 17 | Xin = xMacro(:); 18 | end 19 | 20 | %% SAVE ITERATION INFORMATION 21 | opt_history{end+1,2} = reshape(xMacro,[macro.nelx, macro.nely, max(macro.nelz,1)]); 22 | opt_history{end,3} = reshape(xWeight,[macro.nelx, macro.nely, max(macro.nelz,1)]); 23 | 24 | %% INITIALIZE TOM DATA 25 | [~, ch_index, phys_index] = Indexing('full',micro,micro.ortho); 26 | [ml_index, ~, ~] = Indexing('part',micro,micro.ortho); 27 | Ue_int_Macro = opt_history{end-1,9}; 28 | Fe_int_Macro = opt_history{end-1,10}; 29 | U_Macro = opt_history{end-1,11}; 30 | TOM_e = [xMacro, xWeight]; 31 | for p = 1:length(micro.phys) 32 | if micro.bc_type == 1 33 | TOM_e = [TOM_e, Normalize_TOM_BC(Fe_int_Macro{micro.phys(p)},micro)]; % TOM features 34 | elseif micro.bc_type == 2 35 | TOM_e = [TOM_e, Normalize_TOM_BC(Ue_int_Macro{micro.phys(p)},micro)]; % TOM features 36 | end 37 | end 38 | if micro.ml 39 | [C_lower_HS, C_upper_HS] = Prepare_HS_bounds(micro.mat,micro.dim,micro.ortho); 40 | C_Homvec = Cap_ANN(Interp_CH(ML_model, TOM_e, micro),C_lower_HS(TOM_e(:,1)'),C_upper_HS(TOM_e(:,1)'))'; % interpolate and cap ANN predictions 41 | else 42 | C_Homvec = []; 43 | end 44 | 45 | %% CONTINUATION INITIALIZATION FOR SUBPROBLEMS 46 | if micro.ml == 0 && micro.cont == 1 && macro.cont == 0 % if full TO is used and TOM continutation is on and Macro continuation is off 47 | penal_k = 1; % p0 48 | del_penal = 0.3; % del_p 49 | penal_k = penal_k:del_penal:micro.penal; % penalization values 50 | if penal_k(end) ~= micro.penal; penal_k(end+1) = micro.penal; end % add last penalization value is max 51 | tolx_k = 1e-2; % w0 52 | tolx_mult = (micro.tolx/tolx_k)^(1/(length(penal_k)-1)); % w multiplier 53 | for i = 1:(length(penal_k)-1) 54 | tolx_k = [tolx_k min(tolx_k)*tolx_mult]; %#ok 55 | end 56 | loop_k = ones(size(tolx_k))*micro.maxloop/10; loop_k(end) = micro.maxloop; 57 | else 58 | penal_k = micro.penal; 59 | tolx_k = micro.tolx; 60 | loop_k = micro.maxloop; 61 | end 62 | 63 | %% GENERATE SUBPROBLEMS 64 | xMicro = cell(numel(xMacro),1); 65 | C_Hom = cell(numel(xMacro),1); 66 | KE_Hom = cell(numel(xMacro),1); 67 | WaitMessage = parfor_waitbar(numel(xMacro)); %Add ('Waitbar', true) as arguments to turn on a waitbar popup 68 | fprintf(' |It.:%5i |\n',opt_history{end-1,1}+1); 69 | fprintf(' | Approximate Inner Optimization Problems (TOMs) |\n'); tic 70 | parfor e = 1:numel(xMacro) 71 | %% HOMOGENIZED CONSTITUTIVE TENSORS 72 | C_Hom{e} = cell(3,3); 73 | if micro.ml % ANN Prediction 74 | for p = 1:size(phys_index,1) 75 | C_Hom{e}{phys_index(p,1),phys_index(p,2)} = zeros(max(ch_index{phys_index(p,1),phys_index(p,2)}(:,1)),max(ch_index{phys_index(p,1),phys_index(p,2)}(:,2))); 76 | for ind = 1:size(ch_index{phys_index(p,1),phys_index(p,2)},1) 77 | C_Hom{e}{phys_index(p,1),phys_index(p,2)}(ch_index{phys_index(p,1),phys_index(p,2)}(ind,1),ch_index{phys_index(p,1),phys_index(p,2)}(ind,2)) = C_Homvec(all(sort(ch_index{1,1}(ind,:),'descend') == ml_index{1,1},2),e); 78 | end 79 | end 80 | else % No ML 81 | if xMacro(e) < micro.nelx/micro.nele 82 | if micro.ml == 0; xMicro{e} = zeros(micro.nelx,micro.nely,max(1,micro.nelz)); end 83 | if any(micro.phys == 1) 84 | C_Hom{e}{1,1} = mat.Emin * Prepare_C(mat.nu,1,micro.dim); 85 | end 86 | if any(micro.phys == 2) 87 | C_Hom{e}{2,2} = mat.kmin * Prepare_C(mat.nu,2,micro.dim); 88 | end 89 | else 90 | xMicro{e} = ini_design(micro,xMacro(e),macro.x_lb(1),macro.x_ub(1)); 91 | for pk = 1:numel(penal_k) 92 | xMicro{e} = Micro_TO(micro,TOM_e(e,:),xMicro{e},penal_k(pk),loop_k(pk),tolx_k(pk),mat,KE); 93 | end 94 | C_Hom{e} = Num_Hom(xMicro{e},micro,mat,KE,FE); % returns C_Hom{2,1} but this is not needed to build any stiffness matrix for Hom_FEA 95 | end 96 | end 97 | [~, C_Hom{e}] = Check_CH(C_Hom{e},micro,mat,xMacro(e)); % returns 1 then CHi is valid, returns 0 then CHi is not valid 98 | 99 | %% HOMOGENIZED ELEMENTAL STIFFNESS MATRIX 100 | KE_Hom{e} = cell(3,3); % # of rows/cols is the number of unique physics modes || KE_ij: coupling stiffness matrices of physics i to physics j 101 | if any(micro.phys == 1) 102 | [KE_Hom{e}{1,1}, ~, ~] = Prepare_KE(C_Hom{e}{1,1},1,micro); % Mechanical 103 | KE_Hom{e}{1,1} = reshape(KE_Hom{e}{1,1},[micro.nen*micro.dim,micro.nen*micro.dim]); 104 | end 105 | if any(micro.phys == 2) 106 | [KE_Hom{e}{2,2}, ~, ~] = Prepare_KE(C_Hom{e}{2,2},2,micro); % Thermal 107 | KE_Hom{e}{2,2} = reshape(KE_Hom{e}{2,2},[micro.nen,micro.nen]); 108 | end 109 | if all(micro.phys == [1 2]) 110 | [~ , ~, KE_Hom{e}{2,1}] = Prepare_KE(C_Hom{e}{1,1},[1,2],micro); % Thermomechanical 111 | KE_Hom{e}{2,1} = reshape(KE_Hom{e}{2,1},[micro.nen*micro.dim,micro.nen]); 112 | end 113 | WaitMessage.Send; 114 | end 115 | WaitMessage.Destroy; 116 | fprintf(' | Done. Elapsed Time: %4.2f seconds. |\n',toc); ptomtime = toc; 117 | opt_history{end,7} = C_Hom; opt_history{end,8} = KE_Hom; 118 | 119 | %% SENSITIVITY ANALYSIS 120 | if macro.h_fd == 0 121 | [dc, satime] = Approx_SA_ANN(Xin,macro,micro,Ue_int_Macro,Fe_int_Macro,ML_model,U_Macro); 122 | else 123 | [dc, satime] = Approx_SA_CFD(Xin,macro,micro,mat,Ue_int_Macro,Fe_int_Macro,ML_model,U_Macro); 124 | end 125 | if all(macro.phys == [1 2]) 126 | dc(1:length(Xin)/2) = (macro.den_H*dc(1:length(Xin)/2))./macro.den_Hs; % apply density filter 127 | dc(1:length(Xin)/2) = macro.sen_H*(xMacro(:).*dc(1:length(Xin)/2))./macro.sen_Hs./max(1e-3,xMacro(:)); % filter Macroscale sensitivities 128 | else 129 | dc(:) = (macro.den_H*dc(:))./macro.den_Hs; % apply density filter 130 | dc(:) = macro.sen_H*(xMacro(:).*dc(:))./macro.sen_Hs./max(1e-3,xMacro(:)); % filter Macroscale sensitivities 131 | end 132 | opt_history{end,5} = dc; opt_history{end,6} = [ptomtime, satime]; 133 | 134 | %% HOMOGENIZED FINITE ELEMENT ANALYSIS 135 | [U_Macro,Ue_int_Macro,Fe_int_Macro] = Hom_FEA(KE_Hom,macro,F0_Macro,U0_Macro,N_Macro); 136 | opt_history{end,9} = Ue_int_Macro; opt_history{end,10} = Fe_int_Macro; opt_history{end,11} = U_Macro; 137 | 138 | %% OBJECTIVE FUNCTION EVALUATION 139 | [c, ~, ~] = Obj_Fns([],macro,U_Macro,[],[],[],KE_Hom,[],0.5); % Case 2: Calling Obj_Fns([],macro,U_Macro,[],[],[],KE_Hom,[],weight_e) is c for Homogenized stiffness matrix 140 | opt_history{end,4} = c; 141 | 142 | %% PLOT MACROSCALE ITERATION 143 | opt_history{end,1} = opt_history{end-1,1} + 1; % Macroscale iteration number 144 | output_plot(xMacro,xWeight,macro,xMicro,c,opt_history{end,1},max(abs(opt_history{end,2}(:) - opt_history{end-1,2}(:)))); 145 | 146 | %% SAVE LAST TEMP FILE AND OPT_HISTORY PART 147 | save_size = 10; % number of iterations to save per temp file (keeps file size low) 148 | if size(opt_history,1) > save_size 149 | fnum = str2double(temp_files(end).name(end-6:end-4))+1; 150 | if length(int2str(fnum)) == 1 151 | zeros_num = '00'; 152 | elseif length(int2str(fnum)) == 2 153 | zeros_num = '0'; 154 | elseif length(int2str(fnum)) == 3 155 | zeros_num = []; 156 | end 157 | temp_files(end + 1).name = [temp_files(end).name(1:end-7) zeros_num num2str(fnum) '.mat']; 158 | opt_history(1:save_size,:) = []; 159 | end 160 | save(['temp_files/' temp_files(end).name],'opt_history') 161 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Indexing.m: -------------------------------------------------------------------------------- 1 | % GENERATE INDEXING NUMBERS FOR CH TENSORS AND PHYSICS MODELS 2 | function [ml_index, ch_index, phys_index] = Indexing(indtype,scale,ortho) 3 | 4 | %% indtype = 'part' 5 | % 2D Ortho Mecha: C11, C21, C22, C33 6 | % 2D Ortho Therm: k11, k22 7 | % 2D Ortho ThMec: A11, A21 8 | % 2D Aniso Mecha: C11, C21, C22, C31, C32, C33 9 | % 2D Aniso Therm: k11, k21, k22 10 | % 2D Aniso ThMec: A11, A21, A31 11 | 12 | % 3D Ortho Mecha: C11, C21, C22, C31, C32, C33, C44, C55, C66 13 | % 3D Ortho Therm: k11, k22, k33 14 | % 3D Ortho ThMec: A11, A21, A31 15 | % 3D Aniso Mecha: C11, C21, C22, C31, C32, C33, C41, C42, C43, C44, C51, C52, C53, C54, C55, C61, C62, C63, C64, C65, C66 16 | % 3D Aniso Therm: k11, k21, k22, k31, k32, k33 17 | % 3D Aniso ThMec: A11, A21, A31, A41, A51, A61 18 | 19 | %% indtype = 'full' 20 | % 2D Ortho Mecha: C11, C12, C21, C22, C33 21 | % 2D Ortho Therm: k11, k22 22 | % 2D Ortho ThMec: A11, A21 23 | % 2D Aniso Mecha: C11, C12, C13, C21, C22, C23, C31, C32, C33 24 | % 2D Aniso Therm: k11, k21, k21, k22 25 | % 2D Aniso ThMec: A11, A21, A31 26 | 27 | % 3D Ortho Mecha: C11, C12, C13, C21, C22, C33, C31, C32, C33, C44, C55, C66 28 | % 3D Ortho Therm: k11, k22, k33 29 | % 3D Ortho ThMec: A11, A21, A31 30 | % 3D Aniso Mecha: C11, C12, C13, C14, C15, C16, C21, C22, C23, C24, C25, C26, C31, C32, C33, C34, C35, C36, C41, C42, C43, C44, C45, C46, C51, C52, C53, C54, C55, C56, C61, C62, C63, C64, C65, C66 31 | % 3D Aniso Therm: k11, k12, k13, k21, k22, k23, k31, k32, k33 32 | % 3D Aniso ThMec: A11, A21, A31, A41, A51, A61 33 | 34 | %% CH Tensor Indexing 35 | ml_index_part = cell(3,3); % for ML model indexing 36 | ml_index_full = cell(3,3); % for CH indexing 37 | ch_index_full = cell(3,3); % for CH indexing 38 | 39 | num_LC(:,:,1) = [3, 1; 1, 2]; num_LC(:,:,2) = [6, 1; 1, 3]; % number of load cases for FE. Rows/Cols are physics modes. Pages are scale.dimension, 2D/3D 40 | 41 | ml_index_part{1,1} = []; ml_index_full{1,1} = []; ch_index_full{1,1} = []; 42 | for y = 1:num_LC(1,1,scale.dim-1) 43 | for x = 1:num_LC(1,1,scale.dim-1) 44 | if ortho 45 | if (x <= scale.dim && y <= scale.dim) || y == x 46 | ch_index_full{1,1}(size(ch_index_full{1,1},1)+1,1) = y; 47 | ch_index_full{1,1}(size(ch_index_full{1,1},1),2) = x; 48 | 49 | if x <= y 50 | ml_index_part{1,1}(size(ml_index_part{1,1},1)+1,1) = y; 51 | ml_index_part{1,1}(size(ml_index_part{1,1},1),2) = x; 52 | ml_index_full{1,1}(size(ml_index_full{1,1},1)+1,1) = y; 53 | ml_index_full{1,1}(size(ml_index_full{1,1},1),2) = x; 54 | else 55 | ml_index_full{1,1}(size(ml_index_full{1,1},1)+1,1) = x; 56 | ml_index_full{1,1}(size(ml_index_full{1,1},1),2) = y; 57 | end 58 | end 59 | else 60 | ch_index_full{1,1}(size(ch_index_full{1,1},1)+1,1) = y; 61 | ch_index_full{1,1}(size(ch_index_full{1,1},1),2) = x; 62 | 63 | if x <= y 64 | ml_index_part{1,1}(size(ml_index_part{1,1},1)+1,1) = y; 65 | ml_index_part{1,1}(size(ml_index_part{1,1},1),2) = x; 66 | ml_index_full{1,1}(size(ml_index_full{1,1},1)+1,1) = y; 67 | ml_index_full{1,1}(size(ml_index_full{1,1},1),2) = x; 68 | else 69 | ml_index_full{1,1}(size(ml_index_full{1,1},1)+1,1) = x; 70 | ml_index_full{1,1}(size(ml_index_full{1,1},1),2) = y; 71 | end 72 | end 73 | end 74 | end 75 | 76 | ml_index_part{2,2} = []; ml_index_full{2,2} = []; ch_index_full{2,2} = []; 77 | for y = 1:num_LC(2,2,scale.dim-1) 78 | for x = 1:num_LC(2,2,scale.dim-1) 79 | if ortho 80 | if y == x 81 | ch_index_full{2,2}(size(ch_index_full{2,2},1)+1,1) = y; 82 | ch_index_full{2,2}(size(ch_index_full{2,2},1),2) = x; 83 | 84 | if x <= y 85 | ml_index_part{2,2}(size(ml_index_part{2,2},1)+1,1) = y; 86 | ml_index_part{2,2}(size(ml_index_part{2,2},1),2) = x; 87 | ml_index_full{2,2}(size(ml_index_full{2,2},1)+1,1) = y; 88 | ml_index_full{2,2}(size(ml_index_full{2,2},1),2) = x; 89 | else 90 | ml_index_full{2,2}(size(ml_index_full{2,2},1)+1,1) = x; 91 | ml_index_full{2,2}(size(ml_index_full{2,2},1),2) = y; 92 | end 93 | end 94 | else 95 | ch_index_full{2,2}(size(ch_index_full{2,2},1)+1,1) = y; 96 | ch_index_full{2,2}(size(ch_index_full{2,2},1),2) = x; 97 | 98 | if x <= y 99 | ml_index_part{2,2}(size(ml_index_part{2,2},1)+1,1) = y; 100 | ml_index_part{2,2}(size(ml_index_part{2,2},1),2) = x; 101 | ml_index_full{2,2}(size(ml_index_full{2,2},1)+1,1) = y; 102 | ml_index_full{2,2}(size(ml_index_full{2,2},1),2) = x; 103 | else 104 | ml_index_full{2,2}(size(ml_index_full{2,2},1)+1,1) = x; 105 | ml_index_full{2,2}(size(ml_index_full{2,2},1),2) = y; 106 | end 107 | end 108 | end 109 | end 110 | 111 | ml_index_part{2,1} = []; ml_index_full{2,1} = []; ch_index_full{2,1} = []; 112 | for y = 1:num_LC(1,1,scale.dim-1) 113 | if ortho 114 | if y <= scale.dim 115 | ml_index_part{2,1}(size(ml_index_part{2,1},1)+1,1) = y; 116 | ml_index_part{2,1}(size(ml_index_part{2,1},1),2) = 1; 117 | ml_index_full{2,1}(size(ml_index_full{2,1},1)+1,1) = y; 118 | ml_index_full{2,1}(size(ml_index_full{2,1},1),2) = 1; 119 | ch_index_full{2,1}(size(ch_index_full{2,1},1)+1,1) = y; 120 | ch_index_full{2,1}(size(ch_index_full{2,1},1),2) = 1; 121 | end 122 | else 123 | ml_index_part{2,1}(size(ml_index_part{2,1},1)+1,1) = y; 124 | ml_index_part{2,1}(size(ml_index_part{2,1},1),2) = 1; 125 | ml_index_full{2,1}(size(ml_index_full{2,1},1)+1,1) = y; 126 | ml_index_full{2,1}(size(ml_index_full{2,1},1),2) = 1; 127 | ch_index_full{2,1}(size(ch_index_full{2,1},1)+1,1) = y; 128 | ch_index_full{2,1}(size(ch_index_full{2,1},1),2) = 1; 129 | end 130 | end 131 | 132 | if strcmp(indtype,'part') 133 | ml_index = ml_index_part; 134 | ch_index = ml_index_part; 135 | elseif strcmp(indtype,'full') 136 | ml_index = ml_index_full; 137 | ch_index = ch_index_full; 138 | end 139 | 140 | %% Physics Model Indexing 141 | phys_index = []; 142 | if any(scale.phys == 1) 143 | phys_index(size(phys_index,1)+1,:) = [1, 1]; 144 | end 145 | if any(scale.phys == 2) 146 | phys_index(size(phys_index,1)+1,:) = [2, 2]; 147 | end 148 | % if all(scale.phys == [1 2]) 149 | % phys_index(size(phys_index,1)+1,:) = [2, 1]; 150 | % end 151 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Initialize_Program.m: -------------------------------------------------------------------------------- 1 | % INITIALIZE ML-MSTO PROGRAM 2 | function t_start = Initialize_Program() 3 | t_start = tic; rng shuffle 4 | 5 | %% ADD PROGRAM PATHS 6 | curr_path = pwd; 7 | addpath(genpath(curr_path)); 8 | 9 | %% SETUP PARALLEL COMPUTING POOL (SUPERCOMPUTERS ONLY) 10 | patchJobStorageLocation(); 11 | setup_parpool(); 12 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Interp_CH.m: -------------------------------------------------------------------------------- 1 | % INTERPOLATE LOW VOLUME FRACTION CH 2 | function CH = Interp_CH(ML_model, PTOMs, micro) %without material properties (E, k, etc) 3 | PTOMs0 = PTOMs; 4 | if micro.vf_cutoff > 0 5 | PTOMs(PTOMs0(:,1)<=micro.vf_cutoff,1) = micro.vf_cutoff; 6 | end 7 | CH = ML_model(PTOMs')'; 8 | CH(PTOMs0(:,1)<=micro.vf_cutoff,:) = micro.Cminvec + (CH(PTOMs0(:,1)<=micro.vf_cutoff,:) - micro.Cminvec).*(PTOMs0(PTOMs0(:,1)<=micro.vf_cutoff,1)/micro.vf_cutoff).^1; % have not added this to piecewise ann 9 | % 3D Ortho Mecha: C11, C21, C22, C31, C32, C33, C44, C55, C66 10 | % to 11 | % 3D Aniso Mecha: C11, C21, C22, C31, C32, C33, C41, C42, C43, C44, C51, C52, C53, C54, C55, C61, C62, C63, C64, C65, C66 12 | if micro.dim == 3 13 | CH = [CH(:,1:6), zeros(size(CH,1), 3), CH(:,7), zeros(size(CH,1), 4), CH(:,8), zeros(size(CH,1), 5), CH(:,9)]; 14 | end 15 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/MAIN_v5_1_Generate_ANN_Models.m: -------------------------------------------------------------------------------- 1 | %% GENERATE SAMPLE PLAN AND TRAIN ANN MODELS 2 | % By Joel Najmon Ph.D. 3 | % Last date updated: December, 2023 4 | clear; clc; close all; 5 | 6 | %% SELECT SAMPLE PLAN SIZE 7 | micro.N = 1000; %[Number of Sample Points] 8 | micro.rr = 1.05; %[N Multiplier for Redundant Samples] 9 | 10 | %% SELECT PHYSICAL DIMENSION 11 | micro.dim = 2; % 2D or 3D 12 | 13 | %% SURROGATE MODEL SETTINGS 14 | n_uni_train = micro.N; % number of unique samples for training, N = max 15 | if micro.dim == 2 16 | micro.n_aug = 8; 17 | elseif micro.dim == 3 18 | micro.n_aug = 48; % Number of augmentations to use in ANN training. Integer between 1-8 (2D) or 1-48 (3D). 19 | end 20 | NL = 1; % number of hidden layers 21 | NN = 2; % number of neurons per hidden layer 22 | reps = 8; % number of times to generate an ANN to get the best one 23 | 24 | %% SELECT PHYSICS MODELS 25 | % 1 = Mechanical 26 | % 2 = Thermal 27 | % 3 = Fluid 28 | micro.phys = [1]; 29 | 30 | %% MICROSCALE OPTIMIZATION PARAMETERS 31 | micro.nelx = 100; if rem(micro.nelx,2) ~= 0; micro.nelx = micro.nelx + 1; end % nels should be an even number 32 | micro.nely = micro.nelx; if micro.dim == 2; micro.nelz = 0; elseif micro.dim == 3; micro.nelz = micro.nelx; end 33 | micro.penal = 3; micro.cont = 1; % Continuation: 1 = Yes, = 0 No % CURRENTLY IT IS ASSUMED THAT ALL PHYS MODES USE THE SAME PENALIZATION VALUE 34 | micro.flt_den_min = micro.nelx/20; % density filter minimum radius (flt_den_min <= 1 is off) 35 | micro.flt_sen_min = 1.0; % sensitivity filter minimum radius (flt_sen_min <= 1 is off) 36 | micro.x_lb(1) = 0; micro.x_ub(1) = 1; % bounds for the initial design (final designs are always binary bounds) 37 | micro.x_lb(2) = 0; micro.x_ub(2) = 1; % bounds for the weight design 38 | micro.ortho = 0; % ortho = 1: orthotropic, ortho = 0: anisotropic 39 | 40 | micro.maxloop = 50; % Maximum number of iterations 41 | micro.tolx = 1e-4; % Termination criterion 42 | micro.displayflag = 0; % Display structure flag 43 | 44 | % SELECT MICROSCALE BOUNDARY CONDITIONS 45 | micro.bc_type = 2; % TOM Type of Boundary Conditions: 1 = Internal Force BC, 2 = Internal Displacement BC 46 | micro.load_bc = []; % TOM Load Boundary Conditions: 1 = Corners, 2 = Midpoints of Edges, 3 = Edges, 4 = Face, 5 = Midpoints of Faces 47 | micro.supp_bc = [1]; % TOM Support Boundary Conditions: 1 = Corners, 2 = Midpoints of Edges, 3 = Edges, 4 = Face, 5 = Midpoints of Faces 48 | micro.design_ini = 1; % TOM Initial Design: 1 = Uniform, 2 = Normal, 3 = Random Distributions 49 | if micro.bc_type == 2; micro.load_bc = []; end 50 | 51 | %% TYPE OF PRE-DEFINED PARAMETERIZED MICROSTRUCTURE 52 | % 1 = Unit cell with rectangular void (2 - parameters + rotation) 53 | % 2 = Unit cell with cross (2 - parameters + rotation) 54 | % 3 = Unit cell with anisotruss box/cross (6 - parameters + rotation) 55 | micro.unit_cell_type = 1; 56 | if micro.unit_cell_type == 1 || micro.unit_cell_type == 2; micro.para_num = 3; elseif micro.unit_cell_type == 3; micro.para_num = 7; end 57 | 58 | %% MATERIAL PROPERTIES 59 | mat.Emax = 1.0; 60 | mat.Emin = 1e-9; 61 | mat.nu = 0.3; 62 | mat.kmax = 0.5; % Good thermal conductivity 63 | mat.kmin = 1e-9; % Poor thermal conductivity 64 | mat.Amax = 1e-3; 65 | mat.Amin = 1e-9; 66 | micro.mat = mat; % save mat into micro 67 | 68 | %% PREPARE MESH AND FILTER 69 | [micro.necon, micro.necon_per] = Prepare_Mesh(micro); % Micro mesh connectivity 70 | micro = mesh_info(micro); 71 | micro.DDx = 1; micro.DDy = 1; micro.DDz = 1; % Design Domain size. DDz is thickness in 2D 72 | [micro.den_H, micro.den_Hs] = Prepare_Filter(micro,micro.flt_den_min); % Micro density filter 73 | [micro.sen_H, micro.sen_Hs] = Prepare_Filter(micro,micro.flt_sen_min); % Micro sensitivity filter 74 | 75 | %% PREPARE FEA 76 | KE = cell(3,3); FE = cell(3,3); Cmin = cell(3,3); Cmax = cell(3,3); % # of rows/cols is the number of unique physics modes || KE_ij: coupling stiffness matrices of physics i to physics j 77 | if any(micro.phys == 1) 78 | [KE{1,1}, FE{1,1}, ~] = Prepare_KE(Prepare_C(mat.nu,1,micro.dim),1,micro); % Mechanical 79 | Cmin{1,1} = micro.mat.Emin * Prepare_C(micro.mat.nu,1,micro.dim); 80 | Cmax{1,1} = micro.mat.Emax * Prepare_C(micro.mat.nu,1,micro.dim); 81 | end 82 | if any(micro.phys == 2) 83 | [KE{2,2}, FE{2,2}, ~] = Prepare_KE(Prepare_C(mat.nu,2,micro.dim),2,micro); % Thermal 84 | Cmin{2,2} = micro.mat.kmin * Prepare_C(micro.mat.nu,2,micro.dim); 85 | Cmax{2,2} = micro.mat.kmax * Prepare_C(micro.mat.nu,2,micro.dim); 86 | end 87 | if all(micro.phys == [1 2]) 88 | [ ~, FE{2,1}, KE{2,1}] = Prepare_KE(Prepare_C(mat.nu,1,micro.dim),[1,2],micro); % Thermomechanical 89 | Cmin{2,1} = micro.mat.Amin * Prepare_C(micro.mat.nu,1,micro.dim); % might not be finished yet 90 | Cmax{2,1} = micro.mat.Amax * Prepare_C(micro.mat.nu,1,micro.dim); % might not be finished yet 91 | end 92 | 93 | %% RECORD C RANGE 94 | [ml_index, ~, phys_index] = Indexing('part',micro,micro.ortho); 95 | outnum = 0; for p = 1:size(phys_index,1); for ind = 1:size(ml_index{phys_index(p,1),phys_index(p,2)},1); outnum = outnum + 1; end; end 96 | an = 1; Cminvec = zeros(1,outnum); Cmaxvec = zeros(1,outnum); 97 | for p = 1:size(phys_index,1) 98 | for ind = 1:size(ml_index{phys_index(p,1),phys_index(p,2)},1) 99 | Cminvec(1,an) = Cmin{phys_index(p,1),phys_index(p,2)}(ml_index{phys_index(p,1),phys_index(p,2)}(ind,1),ml_index{phys_index(p,1),phys_index(p,2)}(ind,2)); 100 | Cmaxvec(1,an) = Cmax{phys_index(p,1),phys_index(p,2)}(ml_index{phys_index(p,1),phys_index(p,2)}(ind,1),ml_index{phys_index(p,1),phys_index(p,2)}(ind,2)); 101 | an = an + 1; 102 | end 103 | end 104 | micro.Cminvec = Cminvec; micro.Cmaxvec = Cmaxvec; 105 | 106 | %% GENERATE SAMPLE PLAN INPUTS 107 | [SP_para, micro] = Prepare_Sample_Plan(micro); 108 | 109 | %% CONTINUATION INITIALIZATION 110 | if micro.cont 111 | penal_k = 1; % p0 112 | del_penal = 0.3; % del_p 113 | penal_k = penal_k:del_penal:micro.penal; % penalization values 114 | if penal_k(end) ~= micro.penal; penal_k(end+1) = micro.penal; end % add last penalization value is max 115 | tolx_k = 1e-2; % w0 116 | tolx_mult = (micro.tolx/tolx_k)^(1/(length(penal_k)-1)); % w multiplier 117 | for i = 1:(length(penal_k)-1) 118 | tolx_k = [tolx_k min(tolx_k)*tolx_mult]; %#ok 119 | end 120 | loop_k = ones(size(tolx_k))*micro.maxloop/10; loop_k(end) = micro.maxloop; 121 | else 122 | penal_k = micro.penal; 123 | tolx_k = micro.tolx; 124 | loop_k = micro.maxloop; 125 | end 126 | 127 | %% PRINT GENERATE SAMPLE PLAN INFORMATION 128 | fprintf('==========================================================\n'); 129 | fprintf(' Generate Sample Plan and Train ANNs\n'); 130 | fprintf('==========================================================\n\n'); 131 | fprintf(' ANN Settings:\n'); 132 | fprintf('==========================================================\n'); 133 | fprintf(' Number of Sample Points: N = %1.2e\n',micro.N); 134 | fprintf(' Number of Augmentations per TOM: %2d\n',micro.n_aug); 135 | fprintf(' Number of Hidden Layers: %3d\n',NL); 136 | fprintf(' Number of Nodes per Hidden Layer: %3d\n',NN); 137 | fprintf(' Number of Training Repetitions: %3d\n\n',reps); 138 | display_micro(micro) 139 | Initialize_Program(); 140 | 141 | %% GENERATE SAMPLE PLAN OUTPUTS 142 | fprintf(' Generate Sample Plan, Augment TOMs, and Train ANNs:\n'); 143 | fprintf('==========================================================\n'); 144 | SP_theta = cell(ceil(micro.N*micro.rr),1); % Microstructure Output 145 | SP_C = cell(ceil(micro.N*micro.rr),1); % Homogenized Tensor Output 146 | SP_solve_time = zeros(ceil(micro.N*micro.rr),1); % Average TOM Solution Time Output 147 | SP_C_check = cell(ceil(micro.N*micro.rr),1); % Check if sample is valid 148 | WaitMessage = parfor_waitbar(ceil(micro.N*micro.rr)); % Add ('Waitbar', true) as arguments to turn on a waitbar popup 149 | fprintf(' | Solve Inner Optimization Problems (TOMs) |\n'); tic 150 | for i = 1:ceil(micro.N*micro.rr) 151 | TOM_time = tic; 152 | if SP_para(i,1) < micro.nelx/micro.nele 153 | % Design 154 | SP_theta{i} = zeros(micro.nelx,micro.nely,max(1,micro.nelz)); 155 | % Homogenized Stiffness Tensors 156 | SP_C{i} = cell(3,3); 157 | if any(micro.phys == 1) 158 | SP_C{i}{1,1} = mat.Emin * Prepare_C(mat.nu,1,micro.dim); 159 | end 160 | if any(micro.phys == 2) 161 | SP_C{i}{2,2} = mat.kmin * Prepare_C(mat.nu,2,micro.dim); 162 | end 163 | if 2 == sum(micro.phys == [1 2]) 164 | SP_C{i}{2,1} = mat.Amin * Prepare_C(mat.nu,1,micro.dim); % might not be finished yet 165 | end 166 | SP_C_check{i} = 1; 167 | else 168 | % Design 169 | SP_para(i,1) = 0.5; 170 | micro.design_ini = 3; 171 | SP_theta{i} = ini_design(micro,SP_para(i,1),micro.x_lb(1),micro.x_ub(1)); 172 | for pk = 1:numel(penal_k) 173 | SP_theta{i} = Micro_TO(micro,SP_para(i,:),SP_theta{i},penal_k(pk),loop_k(pk),tolx_k(pk),mat,KE); 174 | end 175 | % Homogenized Stiffness Tensors 176 | SP_C{i} = Num_Hom(SP_theta{i}>0.48,micro,mat,KE,FE); 177 | SP_C_check{i} = Check_CH(SP_C{i},micro,mat,SP_para(i,1)); % returns 1 then CHi is valid, returns 0 then CHi is not valid 178 | end 179 | SP_solve_time(i) = toc(TOM_time); 180 | WaitMessage.Send; 181 | end 182 | WaitMessage.Destroy; 183 | fprintf(' | Done. Elapsed Time: %4.2f seconds. |\n\n',toc/micro.rr); 184 | sptime = toc/micro.rr; % Total time to solve all TOMs 185 | avgTOMtime = mean(SP_solve_time); % Average time to solve one TOM 186 | 187 | %% REMOVE INVALID SAMPLES 188 | SP_C_check = cell2mat(SP_C_check); 189 | % fprintf('\n NUMBER OF FAILED SAMPLES: %d\n',ceil(micro.N*micro.rr)-sum(SP_C_check)); 190 | SP_para(SP_C_check==0,:) = []; 191 | SP_theta(SP_C_check==0) = []; 192 | SP_C(SP_C_check==0) = []; 193 | 194 | if sum(SP_C_check) < micro.N 195 | error('\n TOO MANY INVALID SAMPLES. INCREASE rr VALUE.') 196 | else 197 | del_nums = sum(SP_C_check) - micro.N; 198 | del_nums = randperm(sum(SP_C_check),del_nums); 199 | end 200 | SP_para(del_nums,:) = []; 201 | SP_theta(del_nums) = []; 202 | SP_C(del_nums) = []; 203 | 204 | %% SAVE SAMPLE PLAN 205 | curr_path = pwd; 206 | model_name = 'Homog'; 207 | if all(micro.phys == 1) 208 | physname = 'Phys001_Mecha'; 209 | physshort = 'Mecha'; 210 | elseif all(micro.phys == 2) 211 | physname = 'Phys002_Therm'; 212 | physshort = 'Therm'; 213 | elseif all(micro.phys == 3) 214 | physname = 'Phys003_Fluid'; 215 | physshort = 'Fluid'; 216 | elseif all(micro.phys == [1 2]) 217 | physname = 'Phys012_ThMec'; 218 | physshort = 'ThMec'; 219 | elseif all(micro.phys == [2 3]) 220 | physname = 'Phys023_ThFlu'; 221 | physshort = 'ThFlu'; 222 | end 223 | % Output File Name 224 | existing_file = dir([curr_path '/ML_Models/' physname '/' model_name num2str(micro.dim) 'D_*']); 225 | if numel(existing_file) == 0 226 | fnum = 1; zeros_num = '00'; 227 | else 228 | file_num = cell(numel(existing_file),1); 229 | for i = 1:numel(existing_file) 230 | file_num{i} = existing_file(i).name; 231 | file_num{i} = str2double(file_num{i}(9:11)); 232 | end 233 | fnum = max(cell2mat(file_num))+1; 234 | if length(int2str(fnum)) == 1 235 | zeros_num = '00'; 236 | elseif length(int2str(fnum)) == 2 237 | zeros_num = '0'; 238 | elseif length(int2str(fnum)) == 3 239 | zeros_num = []; 240 | end 241 | end 242 | fname = [model_name num2str(micro.dim) 'D_' zeros_num num2str(fnum) '_' physshort '_N' num2str(micro.N) '_nels' num2str(micro.nelx) '.mat']; 243 | % Save Sample Plan 244 | save([curr_path '/ML_Models/' physname '/' fname],'SP_para','SP_C','sptime','avgTOMtime','micro','-v7.3'); 245 | 246 | %% GENERATE SURROGATE MODEL 247 | Train_ANNs([curr_path '/ML_Models/' physname '/' fname],SP_para,SP_C,micro,n_uni_train,NN,NL,reps); 248 | 249 | fprintf('==========================================================\n'); 250 | fprintf(' End of Generate Sample Plans and Train ANNs Program\n'); 251 | fprintf('==========================================================\n'); -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/ML_Models/Phys001_Mecha/Homog2D_001_Mecha_N1000000_nels50.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edrl-purdue/ml-msto/52b487b536a2d967a09722ac079593d185faf90e/ML_MSTO_Optimizer_v5_1_release/ML_Models/Phys001_Mecha/Homog2D_001_Mecha_N1000000_nels50.mat -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/ML_Models/Phys001_Mecha/Homog3D_001_Mecha_N1000000_nels10.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/edrl-purdue/ml-msto/52b487b536a2d967a09722ac079593d185faf90e/ML_MSTO_Optimizer_v5_1_release/ML_Models/Phys001_Mecha/Homog3D_001_Mecha_N1000000_nels10.mat -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Micro_TO.m: -------------------------------------------------------------------------------- 1 | % TOM TOPOLOGY OPTIMIZATION 2 | function [xMicro, c_vec] = Micro_TO(scale,TOM_e,xMicro0,penal,maxloop,tolx,mat,KE) 3 | %% PREPARE MICROSCALE BOUNDARY CONDITIONS 4 | vf_e = TOM_e(1); 5 | weight_e = TOM_e(2); 6 | F0_Micro = cell(1,3); % KU = F for 3 physics 7 | U0_Micro = cell(1,3); 8 | N_Micro = cell(1,3); 9 | for p = 1:length(scale.phys) 10 | [F0_Micro{scale.phys(p)},U0_Micro{scale.phys(p)},N_Micro{scale.phys(p)}] = Prepare_BCMicro(scale,TOM_e,scale.phys(p)); 11 | end 12 | 13 | %% PREPARE OC 14 | x = xMicro0; 15 | xMicro = x; 16 | change = 1; 17 | loop = 0; 18 | 19 | % SELECT OBJECTIVE FUNCTION 20 | % 1 = Compliance 21 | % 2 = Force-Displacement Error 22 | % 3 = Mutual Potential Energy 23 | micro_TO_obj = 1; % still need to sort out/organize the code for other objective functions 24 | 25 | %% MICROSCALE TOPOLOGY OPTIMIZATION LOOP 26 | while change > tolx && loop < maxloop 27 | loop = loop+1; 28 | 29 | %% MICROSCALE FEA 30 | [U_Micro,~,~] = FEA(xMicro,scale,penal,mat,KE,F0_Micro,U0_Micro,N_Micro); 31 | 32 | %% OBJECTIVE FUNCTION AND SENSITIVITY ANALYSIS 33 | [c, dc, c_vec] = Obj_Fns(xMicro,scale,U_Micro,penal,mat,KE,[],[],weight_e); % Case 1: Calling Obj_Fns(xMicro,scale,U_Micro,penal,mat,KE,[],[],weight_e) is c and dc for SIMP stiffness matrix 34 | dv = ones(size(xMicro)); 35 | dc = reshape(dc,size(xMicro)); 36 | 37 | %% DENSITY FILTER 38 | dc(:) = scale.den_H*(xMicro(:).*dc(:))./scale.den_Hs./max(1e-3,xMicro(:)); 39 | dc(:) = scale.den_H*(dc(:)./scale.den_Hs); 40 | dv(:) = scale.den_H*(dv(:)./scale.den_Hs); 41 | 42 | %% OPTIMALITY CRITERIA UPDATE 43 | l1 = 0; l2 = 1e9; move = 0.2; 44 | while (l2-l1)/(l1+l2) > 1e-3 45 | lmid = 0.5*(l2+l1); 46 | min_1 = min(x+move,x.*sqrt(-dc./dv/lmid)); 47 | min_2 = min(1,min_1); 48 | max_1 = max(x-move,min_2); 49 | xnew = max(0,max_1); 50 | xMicro(:) = (scale.den_H*xnew(:))./scale.den_Hs; 51 | if sum(xMicro(:)) > vf_e*scale.nele 52 | l1 = lmid; 53 | else 54 | l2 = lmid; 55 | end 56 | end 57 | change = max(abs(xnew(:)-x(:))); 58 | x = xnew; 59 | 60 | %% PRINT RESULTS AND PLOT DENSITIES 61 | if scale.displayflag 62 | fprintf(' It.:%5i Obj.:%11.4f Vol.:%7.3f TargetVol.:%7.3f ch.:%7.3f \n',loop,c,mean(xMicro(:)),vf_e,change); 63 | figure(3) 64 | clf; 65 | display_top(xMicro); 66 | end 67 | end 68 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/N_dN_calc.m: -------------------------------------------------------------------------------- 1 | % CALCULATE SHAPE FUNCTION AND ITS DERIVATIVE 2 | % ncoord = [xi, eta, zeta] vector of local/natural coordinates 3 | function [N, dN] = N_dN_calc(ncoord) 4 | if length(ncoord) == 2 5 | xi = ncoord(1); eta = ncoord(2); 6 | % Shape function 7 | N(1) = 0.25*(1.0 - xi)*(1.0 - eta); 8 | N(2) = 0.25*(1.0 + xi)*(1.0 - eta); 9 | N(3) = 0.25*(1.0 + xi)*(1.0 + eta); 10 | N(4) = 0.25*(1.0 - xi)*(1.0 + eta); 11 | % With respect to xi: 12 | dN(1,1) = -0.25 * (1.0 - eta); 13 | dN(2,1) = 0.25 * (1.0 - eta); 14 | dN(3,1) = 0.25 * (1.0 + eta); 15 | dN(4,1) = -0.25 * (1.0 + eta); 16 | % With respect to eta: 17 | dN(1,2) = -0.25 * (1.0 - xi); 18 | dN(2,2) = -0.25 * (1.0 + xi); 19 | dN(3,2) = 0.25 * (1.0 + xi); 20 | dN(4,2) = 0.25 * (1.0 - xi); 21 | elseif length(ncoord) == 3 22 | xi = ncoord(1); eta = ncoord(2); zeta = ncoord(3); 23 | % Shape function 24 | N(1) = 0.125*(1.0 - xi)*(1.0 - eta)*(1.0 - zeta); 25 | N(2) = 0.125*(1.0 + xi)*(1.0 - eta)*(1.0 - zeta); 26 | N(3) = 0.125*(1.0 + xi)*(1.0 + eta)*(1.0 - zeta); 27 | N(4) = 0.125*(1.0 - xi)*(1.0 + eta)*(1.0 - zeta); 28 | N(5) = 0.125*(1.0 - xi)*(1.0 - eta)*(1.0 + zeta); 29 | N(6) = 0.125*(1.0 + xi)*(1.0 - eta)*(1.0 + zeta); 30 | N(7) = 0.125*(1.0 + xi)*(1.0 + eta)*(1.0 + zeta); 31 | N(8) = 0.125*(1.0 - xi)*(1.0 + eta)*(1.0 + zeta); 32 | % With respect to xi: 33 | dN(1,1) = -0.125 * (1.0 - eta) * (1.0 - zeta); 34 | dN(2,1) = 0.125 * (1.0 - eta) * (1.0 - zeta); 35 | dN(3,1) = 0.125 * (1.0 + eta) * (1.0 - zeta); 36 | dN(4,1) = -0.125 * (1.0 + eta) * (1.0 - zeta); 37 | dN(5,1) = -0.125 * (1.0 - eta) * (1.0 + zeta); 38 | dN(6,1) = 0.125 * (1.0 - eta) * (1.0 + zeta); 39 | dN(7,1) = 0.125 * (1.0 + eta) * (1.0 + zeta); 40 | dN(8,1) = -0.125 * (1.0 + eta) * (1.0 + zeta); 41 | % With respect to eta: 42 | dN(1,2) = -0.125 * (1.0 - xi) * (1.0 - zeta); 43 | dN(2,2) = -0.125 * (1.0 + xi) * (1.0 - zeta); 44 | dN(3,2) = 0.125 * (1.0 + xi) * (1.0 - zeta); 45 | dN(4,2) = 0.125 * (1.0 - xi) * (1.0 - zeta); 46 | dN(5,2) = -0.125 * (1.0 - xi) * (1.0 + zeta); 47 | dN(6,2) = -0.125 * (1.0 + xi) * (1.0 + zeta); 48 | dN(7,2) = 0.125 * (1.0 + xi) * (1.0 + zeta); 49 | dN(8,2) = 0.125 * (1.0 - xi) * (1.0 + zeta); 50 | % With respect to zeta: 51 | dN(1,3) = -0.125 * (1.0 - xi) * (1.0 - eta); 52 | dN(2,3) = -0.125 * (1.0 + xi) * (1.0 - eta); 53 | dN(3,3) = -0.125 * (1.0 + xi) * (1.0 + eta); 54 | dN(4,3) = -0.125 * (1.0 - xi) * (1.0 + eta); 55 | dN(5,3) = 0.125 * (1.0 - xi) * (1.0 - eta); 56 | dN(6,3) = 0.125 * (1.0 + xi) * (1.0 - eta); 57 | dN(7,3) = 0.125 * (1.0 + xi) * (1.0 + eta); 58 | dN(8,3) = 0.125 * (1.0 - xi) * (1.0 + eta); 59 | end 60 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Normalize_TOM_BC.m: -------------------------------------------------------------------------------- 1 | % NORMALIZE TOM BOUNDARY CONDITION VALUES 2 | function Xe_int_Macro = Normalize_TOM_BC(Xe_int_Macro,scale) 3 | % Xe_int_Macro is an input vector of unnormalized macroscale elemental nodal field variables for the field variable X (displacements, forces, etc). These values are supplied as boundary conditions inputs to a subproblem 4 | 5 | for e = 1:size(Xe_int_Macro,1) 6 | ndofn = size(Xe_int_Macro,2)/scale.nen; 7 | if ndofn > scale.dim 8 | ndofn = scale.dim; 9 | end 10 | ssum = 0; 11 | for d = 1:ndofn 12 | ssum = ssum + Xe_int_Macro(e,d:ndofn:end).^2; 13 | end 14 | Xmag = max(ssum.^0.5); 15 | Xe_int_Macro(e,:) = Xe_int_Macro(e,:)/Xmag; 16 | end 17 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Num_Hom.m: -------------------------------------------------------------------------------- 1 | % NUMERICAL HOMOGENIZATION - MULTIPHYSICS 2 | function CH = Num_Hom(xPhys,scale,mat,KE,FE) 3 | % This code was adapted from numerical homogenization code from "How to 4 | % determine composite material properties using numerical 5 | % homogenization." by Erik Andreassen and Casper Schousboe Andreasen, 6 | % published in Computational Materials Science, 2014. 7 | % https://doi.org/10.1016/j.commatsci.2013.09.006 8 | 9 | %% ELEMENT CONNECTIVITY MATRICES 10 | edof_per = cell(1,3); 11 | if any(scale.phys == 1) % 1 = Mechanical 12 | edof_per{1} = zeros(scale.nele,scale.nen*scale.dim); 13 | end 14 | if any(scale.phys == 2) % 2 = Thermal 15 | edof_per{2} = zeros(scale.nele,scale.nen); 16 | end 17 | 18 | for i = 1:scale.nele 19 | for j = 1:scale.nen 20 | for k = 1:scale.dim 21 | if any(scale.phys == 1) % 1 = Mechanical 22 | edof_per{1}(i,(j-1)*scale.dim + k) = scale.dim * scale.necon_per((i-1)*scale.nen + j) + k; 23 | end 24 | end 25 | if any(scale.phys == 2) % 2 = Thermal 26 | edof_per{2}(i,j) = scale.necon_per((i-1)*scale.nen + j) + 1; 27 | end 28 | end 29 | end 30 | num_LC(:,:,1) = [3, 1; 1, 2]; num_LC(:,:,2) = [6, 1; 1, 3]; % number of load cases for FE. Rows/Cols are physics modes. Pages are dimension, 2D/3D 31 | 32 | %% ASSEMBLE STIFFNESS MATRICES 33 | if scale.dim == 3 34 | KE{1,1} = KE{1,1}./scale.nelx; % make KE mesh dependent to be compatible with FE 35 | KE{2,2} = KE{2,2}./scale.nelx; % make KE mesh dependent to be compatible with FE 36 | end 37 | [K, ~, F] = Prepare_K(xPhys,scale,scale.penal,mat,KE,[],FE,edof_per,num_LC); % Case 2: Calling Prepare_K(xPhys,scale,scale.penal,mat,KE,[],FE,edof_per,num_LC) is SIMP stiffness and loading matrices 38 | 39 | x_E = mat.Emin + (xPhys.^scale.penal)*(mat.Emax-mat.Emin); % assumed the same penalization parameter is used for all 3 material properties 40 | x_k = mat.kmin + (xPhys.^scale.penal)*(mat.kmax-mat.kmin); 41 | x_A = mat.Amin + (xPhys.^scale.penal)*(mat.Amax-mat.Amin); 42 | 43 | %% COMPUTE FIELD VARIABLES RESULTING FROM PRESCRIBED UNIT STRAINS 44 | chi = cell(3,3); 45 | dof = cell(3,3); 46 | if any(scale.phys == 1) % 1 = Mechanical 47 | chi{1,1} = zeros(scale.dim*scale.nele,num_LC(1,1,scale.dim-1)); %ndof number of rows 48 | dof{1,1} = (scale.dim + 1):scale.dim*scale.nele; 49 | try 50 | R = chol(K{1,1}(dof{1,1},dof{1,1})); 51 | chi{1,1}(dof{1,1},:) = R\(R'\F{1,1}(dof{1,1},:)); %full calculations % Solve Cholesky (remember to constrain one node) 52 | catch 53 | chi{1,1}(dof{1,1},:) = K{1,1}(dof{1,1},dof{1,1})\F{1,1}(dof{1,1},:); %full calculations % Solve standard (remember to constrain one node) 54 | end 55 | end 56 | if any(scale.phys == 2) % 2 = Thermal 57 | chi{2,2} = zeros(scale.nele,num_LC(2,2,scale.dim-1)); 58 | dof{2,2} = 2:scale.nele; 59 | try 60 | R = chol(K{2,2}(dof{2,2},dof{2,2})); 61 | chi{2,2}(dof{2,2},:) = R\(R'\F{2,2}(dof{2,2},:)); %full calculations % Solve Cholesky (remember to constrain one node) 62 | catch 63 | chi{2,2}(dof{2,2},:) = K{2,2}(dof{2,2},dof{2,2})\F{2,2}(dof{2,2},:); %full calculations % Solve standard (remember to constrain one node) 64 | end 65 | end 66 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 67 | chi{2,1} = zeros(scale.dim*scale.nele,num_LC(2,1,scale.dim-1)); 68 | dof{2,1} = (scale.dim + 1):scale.dim*scale.nele; 69 | try 70 | R = chol(K{1,1}(dof{2,1},dof{2,1})); 71 | chi{2,1}(dof{2,1},1) = R\(R'\F{2,1}(dof{2,1},1)); %full calculations % Solve Cholesky (remember to constrain one node) 72 | catch 73 | chi{2,1}(dof{2,1},1) = K{1,1}(dof{2,1},dof{2,1})\F{2,1}(dof{2,1},1); %full calculations % Solve standard (remember to constrain one node) 74 | end 75 | end 76 | 77 | %% COMPUTE PRESCRIBED FIELD VARIABLES 78 | chi0 = cell(3,3); chi0e = cell(3,3); 79 | dofe = cell(3,3); 80 | if any(scale.phys == 1) % 1 = Mechanical 81 | chi0{1,1} = zeros(scale.nele, scale.dim*scale.nen, num_LC(1,1,scale.dim-1)); % The displacement vectors corresponding to the unit strain cases 82 | chi0e{1,1} = zeros(scale.dim*scale.nen, num_LC(1,1,scale.dim-1)); % The element displacements for the three unit strains 83 | if scale.dim == 2; dofe{1,1} = [3, 5:8]; elseif scale.dim == 3; dofe{1,1} = [4, 6:8, 10:12, 14:24]; end 84 | KE{1,1} = reshape(KE{1,1}, [scale.dim*scale.nen,scale.dim*scale.nen]); % Here the exact ratio does not matter, because 85 | FE{1,1} = reshape(FE{1,1}, [scale.dim*scale.nen,num_LC(1,1,scale.dim-1)]); % it is reflected in the load vector 86 | try 87 | R = chol(KE{1,1}(dofe{1,1},dofe{1,1})); 88 | chi0e{1,1}(dofe{1,1},:) = R\(R'\FE{1,1}(dofe{1,1},:)); % solve Cholesky 89 | catch 90 | chi0e{1,1}(dofe{1,1},:) = KE{1,1}(dofe{1,1},dofe{1,1})\FE{1,1}(dofe{1,1},:); % solve standard 91 | end 92 | for lc = 1:num_LC(1,1,scale.dim-1) 93 | chi0{1,1}(:,:,lc) = kron(chi0e{1,1}(:,lc)', ones(scale.nele,1)); % epsilon0_lclc = (0, ..., 1, ..., 0) 94 | end 95 | end 96 | if any(scale.phys == 2) % 2 = Thermal 97 | chi0{2,2} = zeros(scale.nele, scale.nen, num_LC(2,2,scale.dim-1)); % The displacement vectors corresponding to the unit strain cases 98 | chi0e{2,2} = zeros(scale.nen, num_LC(2,2,scale.dim-1)); % The element displacements for the three unit strains 99 | dofe{2,2} = 2:scale.nen; 100 | KE{2,2} = reshape(KE{2,2}, [scale.nen,scale.nen]); % Here the exact ratio does not matter, because 101 | FE{2,2} = reshape(FE{2,2}, [scale.nen,num_LC(2,2,scale.dim-1)]); % it is reflected in the load vector 102 | try 103 | R = chol(KE{2,2}(dofe{2,2},dofe{2,2})); 104 | chi0e{2,2}(dofe{2,2},:) = R\(R'\FE{2,2}(dofe{2,2},:)); % solve Cholesky 105 | catch 106 | chi0e{2,2}(dofe{2,2},:) = KE{2,2}(dofe{2,2},dofe{2,2})\FE{2,2}(dofe{2,2},:); % solve standard 107 | end 108 | for lc = 1:num_LC(2,2,scale.dim-1) 109 | chi0{2,2}(:,:,lc) = kron(chi0e{2,2}(:,lc)', ones(scale.nele,1)); % epsilon0_lclc = (0, ..., 1, ..., 0) 110 | end 111 | end 112 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 113 | chi0{2,1} = zeros(scale.nele, scale.dim*scale.nen, num_LC(2,1,scale.dim-1)); % The displacement vectors corresponding to the unit strain cases 114 | chi0e{2,1} = zeros(scale.dim*scale.nen, num_LC(2,1,scale.dim-1)); % The element displacements for the three unit strains 115 | if scale.dim == 2; dofe{2,1} = [3, 5:8]; elseif scale.dim == 3; dofe{2,1} = [4, 6:8, 10:12, 14:24]; end 116 | FE{2,1} = reshape(FE{2,1}, [scale.dim*scale.nen,num_LC(2,1,scale.dim-1)]); % it is reflected in the load vector 117 | try 118 | R = chol(KE{1,1}(dofe{2,1},dofe{2,1})); 119 | chi0e{2,1}(dofe{2,1},1) = R\(R'\FE{2,1}(dofe{2,1},1)); % solve Cholesky 120 | catch 121 | chi0e{2,1}(dofe{2,1},1) = KE{1,1}(dofe{2,1},dofe{2,1})\FE{2,1}(dofe{2,1},1); % solve standard 122 | end 123 | for lc = 1:num_LC(2,1,scale.dim-1) 124 | chi0{2,1}(:,:,lc) = x_A(:)*chi0e{2,1}(:,lc)'; 125 | end 126 | end 127 | 128 | %% HOMOGENIZATION 129 | CH = cell(3,3); % # of rows/cols is the number of unique physics modes 130 | cellVolume = 1*1*1; % lx, ly, lz = 1: Unit cell length in x-direction, y-direction, and z-direction. 131 | if any(scale.phys == 1) % 1 = Mechanical 132 | for i = 1:num_LC(1,1,scale.dim-1) 133 | for j = 1:num_LC(1,1,scale.dim-1) 134 | sum_KE = ((chi0{1,1}(:,:,i) - chi{1,1}(edof_per{1}+(i-1)*max(dof{1,1})))*KE{1,1}).*(chi0{1,1}(:,:,j) - chi{1,1}(edof_per{1}+(j-1)*max(dof{1,1}))); 135 | sum_KE = reshape(sum(sum_KE,2), size(xPhys)); 136 | CH{1,1}(i,j) = 1/cellVolume*sum(sum(sum(x_E.*sum_KE))); % Homogenized elasticity tensor 137 | end 138 | end 139 | end 140 | if any(scale.phys == 2) % 2 = Thermal 141 | for i = 1:num_LC(2,2,scale.dim-1) 142 | for j = 1:num_LC(2,2,scale.dim-1) 143 | sum_KE = ((chi0{2,2}(:,:,i) - chi{2,2}(edof_per{2}+(i-1)*max(dof{2,2})))*KE{2,2}).*(chi0{2,2}(:,:,j) - chi{2,2}(edof_per{2}+(j-1)*max(dof{2,2}))); 144 | sum_KE = reshape(sum(sum_KE,2), size(xPhys)); 145 | CH{2,2}(i,j) = 1/cellVolume*sum(sum(sum(x_k.*sum_KE))); % Homogenized conductivity tensor 146 | end 147 | end 148 | end 149 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 150 | for i = 1:num_LC(1,1,scale.dim-1) 151 | sum_KE = ((chi0{2,1}(:,:) - chi{2,1}(edof_per{1}))*KE{1,1}).*(chi0{1,1}(:,:,i) - chi{1,1}(edof_per{1}+(i-1)*max(dof{1,1}))); 152 | sum_KE = reshape(sum(sum_KE,2), size(xPhys)); 153 | CH{2,1}(i,1) = 1/cellVolume*sum(sum(sum(x_E.*sum_KE))); % Homogenized thermal stress vector 154 | end 155 | % CH{2,1} = CH{1,1}/CH{2,1}'; % Homogenized thermal strain vector 156 | end 157 | CH{1,1} = real(0.5*(CH{1,1}+CH{1,1}')); 158 | CH{2,2} = real(0.5*(CH{2,2}+CH{2,2}')); 159 | CH{2,1} = real(CH{2,1}); 160 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Obj_Fns.m: -------------------------------------------------------------------------------- 1 | % OBJECTIVE FUNCTION AND ANALYTICAL DERIVATIVE 2 | function [c, dc, c_vec] = Obj_Fns(xTheta,scale,U,penal,mat,KE,KE_Hom,dKE_Hom,weight) 3 | % Case 1: Calling [c, dc, c_vec] = Obj_Fns(xMicro,scale,U_Micro,penal,mat,KE,[],[],weight_e) is c and dc for SIMP stiffness matrix 4 | % Case 2: Calling [c, ~, c_vec] = Obj_Fns([],macro,U_Macro,[],[],[],KE_Hom,[],weight_e) is c for Homogenized stiffness matrix 5 | % Case 3: Calling [~, dc, ~] = Obj_Fns([],macro,U_Macro,[],[],[],[],dKE_Hom,weight_e) is dc for Homogenized stiffness matrix 6 | 7 | %% OBJECTIVE FUNCTION WEIGHTS 8 | if scale.phys == 1 9 | w1 = 1.0; %mechanical 10 | w2 = 0.0; %thermal 11 | elseif scale.phys == 2 12 | w1 = 0.0; %mechanical 13 | w2 = 1.0; %thermal 14 | elseif all(scale.phys == [1 2]) 15 | w1 = weight; %mechanical 16 | w2 = (1-weight); %thermal 17 | end 18 | 19 | %% ELEMENT CONNECTIVITY MATRICES 20 | edof = cell(1,3); 21 | if any(scale.phys == 1) % 1 = Mechanical 22 | edof{1} = zeros(scale.nele,scale.nen*scale.dim); 23 | end 24 | if any(scale.phys == 2) % 2 = Thermal 25 | edof{2} = zeros(scale.nele,scale.nen); 26 | end 27 | 28 | for i = 1:scale.nele 29 | for j = 1:scale.nen 30 | for k = 1:scale.dim 31 | if any(scale.phys == 1) % 1 = Mechanical 32 | edof{1}(i,(j-1)*scale.dim + k) = scale.dim * scale.necon((i-1)*scale.nen + j) + k; 33 | end 34 | end 35 | if any(scale.phys == 2) % 2 = Thermal 36 | edof{2}(i,j) = scale.necon((i-1)*scale.nen + j) + 1; 37 | end 38 | end 39 | end 40 | 41 | %% COMPUTE OBJECTIVE FUNCTION AND ANALYTICAL SENSITIVITIES 42 | if ~isempty(KE) % Case 1 43 | c = zeros(scale.nele,2); 44 | dc = zeros(scale.nele,3); 45 | % xTheta(:) = (scale.H*xTheta(:))./scale.Hs; 46 | for i = 1:scale.nele 47 | uKu_m = 0; 48 | uKu_t = 0; 49 | uKu_mt = 0; 50 | if any(scale.phys == 1) 51 | for k = 1:scale.nen*scale.dim 52 | for h = 1:scale.nen*scale.dim 53 | uKu_m = uKu_m + U{1}(edof{1}(i,k))*KE{1,1}((k-1)*scale.nen*scale.dim+h)*U{1}(edof{1}(i,h)); % multiplied by [simp E] or [derivative of simp E] 54 | end 55 | end 56 | end 57 | if any(scale.phys == 2) 58 | for k = 1:scale.nen 59 | for h = 1:scale.nen 60 | uKu_t = uKu_t + U{2}(edof{2}(i,k))*KE{2,2}((k-1)*scale.nen+h)*U{2}(edof{2}(i,h)); % multiplied by [simp k] or [derivative of simp k] 61 | end 62 | end 63 | end 64 | if all(scale.phys == [1 2]) 65 | for k = 1:scale.nen*scale.dim 66 | for h = 1:scale.nen 67 | uKu_mt = uKu_mt + U{1}(edof{1}(i,k))*KE{2,1}((k-1)*scale.nen+h)*U{2}(edof{2}(i,h)); % multiplied by [derivative of simp alpha - (simp alpha/simp k)*derivative of simp k] 68 | end 69 | end 70 | end 71 | 72 | % objective function 73 | c(i,1) = w1*(mat.Emin + (xTheta(i)^penal)*(mat.Emax-mat.Emin))*uKu_m; 74 | c(i,2) = w2*(mat.kmin + (xTheta(i)^penal)*(mat.kmax-mat.kmin))*uKu_t; 75 | if all(scale.phys == [1 2]) 76 | dc(i,1) = -w1*penal*(xTheta(i)^(penal-1))*(mat.Emax-mat.Emin)*uKu_m; 77 | dc(i,2) = -w2*penal*(xTheta(i)^(penal-1))*(mat.kmax-mat.kmin)*uKu_t; 78 | dc(i,3) = w1*(penal*(xTheta(i)^(penal-1))*(mat.Amax-mat.Amin) - ((mat.Amin + (xTheta(i)^penal)*(mat.Amax-mat.Amin))/(mat.kmin + (xTheta(i)^penal)*(mat.kmax-mat.kmin)))*(penal*(xTheta(i)^(penal-1))*(mat.kmax-mat.kmin)))*uKu_mt; 79 | else 80 | dc(i,1) = -w1*penal*(xTheta(i)^(penal-1))*(mat.Emax-mat.Emin)*uKu_m; % no dKmt/dTheta derivative term 81 | dc(i,2) = -w2*penal*(xTheta(i)^(penal-1))*(mat.kmax-mat.kmin)*uKu_t; 82 | end 83 | end 84 | c_vec = sum(c,2); 85 | c = sum(c(:)); 86 | dc = sum(dc,2); 87 | elseif ~isempty(KE_Hom) % Case 2 88 | c = zeros(numel(KE_Hom),2); 89 | for i = 1:numel(KE_Hom) 90 | uKu_m = 0; 91 | uKu_t = 0; 92 | uKu_mt = 0; 93 | if any(scale.phys == 1) 94 | for k = 1:scale.nen*scale.dim 95 | for h = 1:scale.nen*scale.dim 96 | uKu_m = uKu_m + U{1}(edof{1}(i,k))*KE_Hom{i}{1,1}((k-1)*scale.nen*scale.dim+h)*U{1}(edof{1}(i,h)); % multiplied by [simp E] or [derivative of simp E] 97 | end 98 | end 99 | end 100 | if any(scale.phys == 2) 101 | for k = 1:scale.nen 102 | for h = 1:scale.nen 103 | uKu_t = uKu_t + U{2}(edof{2}(i,k))*KE_Hom{i}{2,2}((k-1)*scale.nen+h)*U{2}(edof{2}(i,h)); % multiplied by [simp k] or [derivative of simp k] 104 | end 105 | end 106 | end 107 | if all(scale.phys == [1 2]) 108 | for k = 1:scale.nen*scale.dim 109 | for h = 1:scale.nen 110 | uKu_mt = uKu_mt + U{1}(edof{1}(i,k))*KE_Hom{i}{2,1}((k-1)*scale.nen+h)*U{2}(edof{2}(i,h)); % multiplied by [derivative of simp alpha - (simp alpha/simp k)*derivative of simp k] 111 | end 112 | end 113 | end 114 | 115 | % objective function 116 | c(i,1) = w1*uKu_m; 117 | c(i,2) = w2*uKu_t; 118 | end 119 | c_vec = sum(c,2); 120 | c = sum(c(:)); 121 | dc = []; 122 | elseif ~isempty(dKE_Hom) % Case 3 NOT EXTENDED TO THERMOMECHANICAL YET 123 | dc = zeros(numel(dKE_Hom),3); 124 | for i = 1:numel(dKE_Hom) 125 | udKu_m = 0; 126 | udKu_t = 0; 127 | if any(scale.phys == 1) 128 | for k = 1:scale.nen*scale.dim 129 | for h = 1:scale.nen*scale.dim 130 | udKu_m = udKu_m + U{1}(edof{1}(i,k))*dKE_Hom{i}{1,1}((k-1)*scale.nen*scale.dim+h)*U{1}(edof{1}(i,h)); % multiplied by [simp E] or [derivative of simp E] 131 | end 132 | end 133 | end 134 | if any(scale.phys == 2) 135 | for k = 1:scale.nen 136 | for h = 1:scale.nen 137 | udKu_t = udKu_t + U{2}(edof{2}(i,k))*dKE_Hom{i}{2,2}((k-1)*scale.nen+h)*U{2}(edof{2}(i,h)); % multiplied by [simp E] or [derivative of simp E] 138 | end 139 | end 140 | end 141 | if all(scale.phys == [1 2]) 142 | for k = 1:scale.nen*scale.dim 143 | for h = 1:scale.nen 144 | % NOT EXTENDED TO THERMOMECHANICAL YET 145 | % udKu_mt = udKu_mt + U{1}(edof{1}(i,k))*KE_Hom{i}{2,1}((k-1)*scale.nen+h)*U{2}(edof{2}(i,h)); % multiplied by [derivative of simp alpha - (simp alpha/simp k)*derivative of simp k] 146 | end 147 | end 148 | end 149 | 150 | if all(scale.phys == [1 2]) % NOT EXTENDED TO THERMOMECHANICAL YET 151 | dc(i,1) = w1*udKu_m; 152 | dc(i,2) = w2*udKu_t; 153 | % dc(i,3) = ??? 154 | % dc(i,3) = w1*(penal*(xTheta(i)^(penal-1))*(mat.Amax-mat.Amin) - ((mat.Amin + (xTheta(i)^penal)*(mat.Amax-mat.Amin))/(mat.kmin + (xTheta(i)^penal)*(mat.kmax-mat.kmin)))*(penal*(xTheta(i)^(penal-1))*(mat.kmax-mat.kmin)))*uKu_mt; 155 | else 156 | dc(i,1) = w1*udKu_m; % no udKu_mt 157 | dc(i,2) = w2*udKu_t; 158 | end 159 | end 160 | c_vec = []; 161 | c = []; 162 | dc = sum(dc,2); 163 | end 164 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/PostProcess_Skel.m: -------------------------------------------------------------------------------- 1 | % SKELETONIZATION POST PROCESSING 2 | function xMulti = PostProcess_Skel(xMulti) 3 | xMulti0 = zeros(size(xMulti)); 4 | 5 | %% ITERATE THROUGH THE SKELETONIZATION POST PROCESS METHOD 6 | fprintf(' | Apply Skeletonization Post Processing |\n'); tic 7 | k = 1; 8 | while any(xMulti(:) ~= xMulti0(:)) && k <= 10 9 | % Save past iteration 10 | xMulti0 = xMulti; 11 | 12 | % Remove Islands 13 | xMulti_rm = remove_islands(xMulti); 14 | 15 | % Calculate Distance Transform and Stencils 16 | [D, Dlist, stencils, stencil_size] = calc_D_stencils(xMulti_rm); 17 | 18 | % Generate Skeletons 19 | [~, xSkel_good, xSkel_bad] = gen_skel(xMulti_rm); 20 | 21 | % Remove Skeletons 22 | [xMulti_er, xSkel_rebuild] = rm_skel(xMulti_rm,xSkel_good,xSkel_bad,D,Dlist,stencils,stencil_size); 23 | 24 | % Rebuild Skeletons 25 | xMulti = rb_skel(xMulti_er,xSkel_rebuild,D,Dlist,stencils,stencil_size); 26 | 27 | k = k + 1; 28 | clear xMulti_er xMulti_rm xSkel_bad xSkel_good xSkel_rebuild stencils stencil_size D Dlist 29 | end 30 | fprintf(' | Done. Elapsed Time: %4.2f seconds. |\n',toc); ptomtime = toc; 31 | 32 | %% REMOVE ISLANDS 33 | function xMulti_rm = remove_islands(xMulti) 34 | if size(xMulti,3) == 1 % 2D 35 | filter_neighborhood = 4; % 2d conn 36 | else % 3D 37 | filter_neighborhood = 6; % 3d conn 38 | end 39 | stats = regionprops(bwconncomp(xMulti,filter_neighborhood),'Area','Image','Centroid','BoundingBox','PixelIdxList'); 40 | area = zeros(numel(stats),1); 41 | for s = 1:numel(stats) 42 | area(s) = stats(s).Area; 43 | end 44 | [~, ind] = max(area); 45 | xMulti_rm = zeros(size(xMulti)); 46 | xMulti_rm(stats(ind).PixelIdxList) = 1; 47 | xMulti_rm = logical(xMulti_rm); 48 | 49 | %%%%%%% POSSIBLE IMPROVEMENT 50 | % could make this part more robust by checking loads/supports of 51 | % each mass. Keep masses (even if distinct) that are statically 52 | % determinate with loads and supports. By removing islands based 53 | % off this criteria and not just connectivity this subroutine is 54 | % more robust to a variety of topologies and problems (e.g. two 55 | % distinct columns in a 2D top-distributed load and bottom-distributed support problem) 56 | end 57 | 58 | %% CALCULATE DISTANCE TRANSFORM AND STENCILS ON XMULTI_RM 59 | function [D, Dlist, stencils, stencil_size] = calc_D_stencils(xMulti) 60 | D = bwdist(~xMulti,'euclidean'); % calculate distance transform on input top with islands removed 61 | Dlist = unique(D(:)); % sort list of all the unique element distances 62 | template = zeros(2*ceil(max(Dlist)) + 1); % create a square template to calculate the discrete set of circle stencils on. Use the max D value to set the size of the template 63 | template(ceil(max(Dlist)) + 1,ceil(max(Dlist)) + 1) = 1; % Set center element to 1 64 | template = bwdist(template); % calculate distance transform to every point on the template 65 | 66 | if size(xMulti,3) == 1 % 2D 67 | filter_neighborhood = 4; % 2d conn 68 | else % 3D 69 | filter_neighborhood = 6; % 3d conn 70 | end 71 | 72 | stencil_size = cell(numel(Dlist),1); 73 | parfor s = 1:numel(Dlist) 74 | stats = regionprops(bwconncomp(template <= Dlist(s),filter_neighborhood),'BoundingBox'); 75 | stencil_size{s} = stats.BoundingBox(3:4); 76 | end 77 | 78 | uni_sizes = (unique(cell2mat(stencil_size))-1)/2; 79 | stencils = cell(numel(uni_sizes),1); 80 | parfor s = 1:numel(uni_sizes) 81 | stencils{s} = double(template((ceil(max(Dlist)) + 1 - uni_sizes(s)):(ceil(max(Dlist)) + 1 + uni_sizes(s)),(ceil(max(Dlist)) + 1 - uni_sizes(s)):(ceil(max(Dlist)) + 1 + uni_sizes(s)))); 82 | end 83 | end 84 | 85 | %% GENERATE SKELETONS 86 | function [xSkel_full, xSkel_good, xSkel_bad] = gen_skel(xin) 87 | xSkel_full = bwskel(xin); % Create full skeleton 88 | xSkel_good = bwskel(bwskel(bwskel(bwskel(bwskel(bwskel(bwskel(bwskel(bwskel(xin,'MinBranchLength',1/eps),'MinBranchLength',1/eps),'MinBranchLength',1/eps),'MinBranchLength',1/eps),'MinBranchLength',1/eps),'MinBranchLength',1/eps),'MinBranchLength',1/eps),'MinBranchLength',1/eps),'MinBranchLength',1/eps); % Prune full skeleton to arrive at the good skeleton to keep 89 | xSkel_bad = xSkel_full - xSkel_good; % Subtract skeletons to fine the bad skeleton to remove 90 | end 91 | 92 | %% REMOVE SKELETONS 93 | function [xMulti_er, xSkel_rebuild] = rm_skel(xMulti_er,xSkel_good,xSkel_bad,D,Dlist,stencils,stencil_size) 94 | % Eroded topology (xMulti_er) starts as the full topology with removed islands 95 | xSkel_good_er = xSkel_good; 96 | xnums = reshape(1:numel(xMulti_er),size(xMulti_er)); 97 | 98 | data = [xnums(:), D(:), xSkel_bad(:), xMulti_er(:)]; 99 | % data = sortrows(data,[3,2,1],{'descend','descend','ascend'}); 100 | data = sortrows(data,[3,1,2],{'descend','ascend','descend'}); 101 | comp = cell(sum(xSkel_bad(:)),1); % distance comparison cell array 102 | uni_sizes = unique(cell2mat(stencil_size)); 103 | for L = 1:sum(xSkel_bad(:)) 104 | % Form Indexing Arrays for translate D stencils for distance comparison 105 | [row,col,page] = ind2sub(size(xMulti_er),data(L,1)); % array index for element L of xSkel_bad 106 | r_size = (stencil_size{Dlist==data(L,2)} - 1)/2; % get the radius size for element L according to the D stencil 107 | rows = round((row - r_size(1)):(row + r_size(1)),0); % stencil rows 108 | row_ind = and(rows >= 1, rows <= size(xMulti_er,1)); % stencil rows that are within bounds 109 | cols = round((col - r_size(2)):(col + r_size(2)),0); % stencil cols 110 | col_ind = and(cols >= 1, cols <= size(xMulti_er,2)); % stencil cols that are within bounds 111 | 112 | % Compare element distance array to D 113 | try 114 | ind = combvec(rows(row_ind),cols(col_ind))'; 115 | comp{L} = sparse(ind(:,1), ind(:,2), stencils{mean(stencil_size{Dlist==data(L,2)})==uni_sizes}(row_ind,col_ind), size(xMulti_er,1), size(xMulti_er,2)); % located elements that are within stencil size of D_L and less than the distance function D at element L. 116 | catch 117 | C = cols(col_ind); 118 | comp{L} = sparse(size(xMulti_er,1), size(xMulti_er,2)); 119 | sten = stencils{mean(stencil_size{Dlist==data(L,2)})==uni_sizes}(row_ind,col_ind); 120 | for idx=1:sum(col_ind) % loop to avoid large memory usage from combvec 121 | comp{L} = comp{L} + sparse(rows(row_ind)', repmat(C(:,idx),1,length(rows(row_ind)))', sten(:,idx), size(xMulti_er,1), size(xMulti_er,2)); 122 | end 123 | end 124 | end 125 | for L = 1:sum(xSkel_bad(:)) 126 | xMulti_er(logical(comp{L})) = 0; 127 | xSkel_good_er(logical(comp{L})) = 0; 128 | end 129 | xSkel_rebuild = xSkel_good - xSkel_good_er; 130 | 131 | %%%%%%% POSSIBLE IMPROVEMENT 132 | % could also check removals in location to external loads and 133 | % supports. Structure material should not be eroded if it is 134 | % connected to a boundary load or support. 135 | end 136 | 137 | %% REBUILD SKELETONS 138 | function xMulti_rb = rb_skel(xMulti_rb,xSkel_rebuild,D,Dlist,stencils,stencil_size) 139 | % Projected topology starts as the full topology 140 | xnums = reshape(1:numel(xMulti_rb),size(xMulti_rb)); 141 | 142 | data = [xnums(:), D(:), xSkel_rebuild(:), xMulti_rb(:)]; 143 | % data = sortrows(data,[3,2,1],{'descend','descend','ascend'}); 144 | data = sortrows(data,[3,1,2],{'descend','ascend','descend'}); 145 | comp = cell(sum(xSkel_rebuild(:)),1); % distance comparison cell array 146 | uni_sizes = unique(cell2mat(stencil_size)); 147 | for L = 1:sum(xSkel_rebuild(:)) 148 | % Form Indexing Arrays for translate D stencils for distance comparison 149 | [row,col,page] = ind2sub(size(xMulti_rb),data(L,1)); % array index for element L of xSkel_bad 150 | r_size = (stencil_size{Dlist==data(L,2)} - 1)/2; % get the radius size for element L according to the D stencil 151 | rows = round((row - r_size(1)):(row + r_size(1)),0); % stencil rows 152 | row_ind = and(rows >= 1, rows <= size(xMulti_rb,1)); % stencil rows that are within bounds 153 | cols = round((col - r_size(2)):(col + r_size(2)),0); % stencil cols 154 | col_ind = and(cols >= 1, cols <= size(xMulti_rb,2)); % stencil cols that are within bounds 155 | 156 | % Compare element distance array to D 157 | ind = combvec(rows(row_ind),cols(col_ind))'; 158 | comp{L} = sparse(ind(:,1), ind(:,2), stencils{mean(stencil_size{Dlist==data(L,2)})==uni_sizes}(row_ind,col_ind), size(xMulti_rb,1), size(xMulti_rb,2)); % located elements that are within stencil size of D_L and less than the distance function D at element L. 159 | 160 | try 161 | ind = combvec(rows(row_ind),cols(col_ind))'; 162 | comp{L} = sparse(ind(:,1), ind(:,2), stencils{mean(stencil_size{Dlist==data(L,2)})==uni_sizes}(row_ind,col_ind), size(xMulti_rb,1), size(xMulti_rb,2)); % located elements that are within stencil size of D_L and less than the distance function D at element L. 163 | catch 164 | C = cols(col_ind); 165 | comp{L} = sparse(size(xMulti_rb,1), size(xMulti_rb,2)); 166 | sten = stencils{mean(stencil_size{Dlist==data(L,2)})==uni_sizes}(row_ind,col_ind); 167 | for idx=1:sum(col_ind) % loop to avoid large memory usage from combvec 168 | comp{L} = comp{L} + sparse(rows(row_ind)', repmat(C(:,idx),1,length(rows(row_ind)))', sten(:,idx), size(xMulti_rb,1), size(xMulti_rb,2)); 169 | end 170 | end 171 | end 172 | for L = 1:sum(xSkel_rebuild(:)) 173 | xMulti_rb(logical(comp{L})) = 1; 174 | end 175 | end 176 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Prepare_C.m: -------------------------------------------------------------------------------- 1 | % GENERATE STIFFNESS TENSOR WITHOUT E, k, etc 2 | function [C] = Prepare_C(nu,phys,dim) %without material properties (E, k, etc) 3 | if phys == 1 % Mechanical 4 | L1 = nu*1/((1+nu)*(1 - 2*nu)); 5 | L2 = 1/(2*(1+nu)); 6 | if dim == 2 % 2D 7 | C_L1 = zeros(3,3); C_L1(1:2,1:2) = L1; 8 | C_L2 = diag([2 2 1])*L2; 9 | C = C_L1 + C_L2; % plane strain assumption (usually go with this, better for 2d extruded designs) 10 | % C = (1/(1-nu*nu))*[1 nu 0; nu 1 0; 0 0 (1-nu)/2]; % plane stress assumption (better for 2d thin plate designs) 11 | elseif dim == 3 % 3D 12 | C_L1 = zeros(6,6); C_L1(1:3,1:3) = L1; 13 | C_L2 = diag([2 2 2 1 1 1])*L2; 14 | C = C_L1 + C_L2; 15 | end 16 | elseif phys == 2 % Thermal 17 | if dim == 2 % 2D 18 | C = eye(2); 19 | elseif dim == 3 % 3D 20 | C = eye(3); 21 | end 22 | elseif phys == 3 % Fluid 23 | if dim == 2 % 2D 24 | C = 0; 25 | elseif dim == 3 % 3D 26 | C = 0; 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Prepare_Filter.m: -------------------------------------------------------------------------------- 1 | % PREPARE FILTER 2 | function [H,Hs] = Prepare_Filter(scale,rmin) 3 | if scale.dim == 2 % 2D 4 | iH = ones(scale.nely*scale.nelx*((ceil(rmin)-1)+1)^2,1); jH = ones(size(iH)); sH = zeros(size(iH)); k = 0; 5 | for i1 = 1:scale.nely 6 | for j1 = 1:scale.nelx 7 | e1 = (i1-1)*scale.nelx+j1; 8 | for i2 = max(i1-(ceil(rmin)-1),1):min(i1+(ceil(rmin)-1),scale.nely) 9 | for j2 = max(j1-(ceil(rmin)-1),1):min(j1+(ceil(rmin)-1),scale.nelx) 10 | e2 = (i2-1)*scale.nelx+j2; 11 | k = k+1; 12 | iH(k) = e1; 13 | jH(k) = e2; 14 | sH(k) = max(0,rmin-sqrt((i1-i2)^2+(j1-j2)^2)); 15 | end 16 | end 17 | end 18 | end 19 | elseif scale.dim == 3 % 3D 20 | iH = ones(scale.nelx*scale.nely*scale.nelz*(2*(ceil(rmin)-1)+1)^2,1); jH = ones(size(iH)); sH = zeros(size(iH)); k = 0; 21 | for k1 = 1:scale.nelz 22 | for i1 = 1:scale.nelx 23 | for j1 = 1:scale.nely 24 | e1 = (k1-1)*scale.nelx*scale.nely + (i1-1)*scale.nely+j1; 25 | for k2 = max(k1-(ceil(rmin)-1),1):min(k1+(ceil(rmin)-1),scale.nelz) 26 | for i2 = max(i1-(ceil(rmin)-1),1):min(i1+(ceil(rmin)-1),scale.nelx) 27 | for j2 = max(j1-(ceil(rmin)-1),1):min(j1+(ceil(rmin)-1),scale.nely) 28 | e2 = (k2-1)*scale.nelx*scale.nely + (i2-1)*scale.nely+j2; 29 | k = k+1; 30 | iH(k) = e1; 31 | jH(k) = e2; 32 | sH(k) = max(0,rmin-sqrt((i1-i2)^2+(j1-j2)^2+(k1-k2)^2)); 33 | end 34 | end 35 | end 36 | end 37 | end 38 | end 39 | end 40 | H = sparse(iH,jH,sH); 41 | Hs = sum(H,2); 42 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Prepare_HS_bounds.m: -------------------------------------------------------------------------------- 1 | % PREPARE HASHIN-SHTRIKMAN BOUNDING FUNCTIONS 2 | function [C_lower_HS, C_upper_HS] = Prepare_HS_bounds(mat, dim, ortho) %without material properties (E, k, etc) 3 | K1 = mat.Emin/(3*(1-2*mat.nu)); 4 | K2 = mat.Emax/(3*(1-2*mat.nu)); 5 | mu1 = mat.Emin/(2*(1+mat.nu)); 6 | mu2 = mat.Emax/(2*(1+mat.nu)); 7 | 8 | if dim == 2 % 2D Plain Strain 9 | if ortho == 1 10 | C_lower_HS = @(Q) [K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C11 11 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) - (2/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C21 12 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C22 13 | mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))]'; % C33 14 | C_upper_HS = @(Q) [K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C11 15 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) - (2/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C21 16 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C22 17 | mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))]'; % C33 18 | elseif ortho == 0 19 | C_lower_HS = @(Q) [K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C11 20 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) - (2/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C21 21 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C22 22 | zeros(size(Q)) % C31 23 | zeros(size(Q)) % C32 24 | mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))]'; % C33 25 | C_upper_HS = @(Q) [K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C11 26 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) - (2/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C21 27 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C22 28 | zeros(size(Q)) % C31 29 | zeros(size(Q)) % C32 30 | mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))]'; % C33 31 | end 32 | elseif dim == 3 33 | if ortho == 1 34 | C_lower_HS = @(Q) [K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C11 35 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) - (2/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C21 36 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C22 37 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) - (2/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C31 38 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) - (2/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C32 39 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C33 40 | mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1))) % C44 41 | mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1))) % C55 42 | mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))]'; % C66 43 | C_upper_HS = @(Q) [K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C11 44 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) - (2/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C21 45 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C22 46 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) - (2/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C31 47 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) - (2/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C32 48 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C33 49 | mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2))) % C44 50 | mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2))) % C55 51 | mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))]'; % C66 52 | elseif ortho == 0 53 | C_lower_HS = @(Q) [K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C11 54 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) - (2/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C21 55 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C22 56 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) - (2/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C31 57 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) - (2/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C32 58 | K1 + Q./((K2-K1)^-1 + (3*(1-Q))/(3*K1 + 4*mu1)) + (4/3)*(mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))) % C33 59 | zeros(size(Q)) % C41 60 | zeros(size(Q)) % C42 61 | zeros(size(Q)) % C43 62 | mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1))) % C44 63 | zeros(size(Q)) % C51 64 | zeros(size(Q)) % C52 65 | zeros(size(Q)) % C53 66 | zeros(size(Q)) % C54 67 | mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1))) % C55 68 | zeros(size(Q)) % C61 69 | zeros(size(Q)) % C62 70 | zeros(size(Q)) % C63 71 | zeros(size(Q)) % C64 72 | zeros(size(Q)) % C65 73 | mu1 + Q./((mu2-mu1)^-1 + (6*(K1+2*mu1)*(1-Q))/(5*mu1*(3*K1+4*mu1)))]'; % C66 74 | C_upper_HS = @(Q) [K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C11 75 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) - (2/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C21 76 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C22 77 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) - (2/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C31 78 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) - (2/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C32 79 | K2 + (1-Q)./((K1-K2)^-1 + (3*Q)/(3*K2 + 4*mu2)) + (4/3)*(mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))) % C33 80 | zeros(size(Q)) % C41 81 | zeros(size(Q)) % C42 82 | zeros(size(Q)) % C43 83 | mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2))) % C44 84 | zeros(size(Q)) % C51 85 | zeros(size(Q)) % C52 86 | zeros(size(Q)) % C53 87 | zeros(size(Q)) % C54 88 | mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2))) % C55 89 | zeros(size(Q)) % C61 90 | zeros(size(Q)) % C62 91 | zeros(size(Q)) % C63 92 | zeros(size(Q)) % C64 93 | zeros(size(Q)) % C65 94 | mu2 + (1-Q)./((mu1-mu2)^-1 + (6*(K2+2*mu2)*Q)/(5*mu2*(3*K2+4*mu2)))]'; % C66 95 | end 96 | end 97 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Prepare_K.m: -------------------------------------------------------------------------------- 1 | % ASSEMBLE STIFFNESS MATRIX - MULTIPHYSICS 2 | function [K, K_nele, F] = Prepare_K(xPhys,scale,penal,mat,KE,KE_Hom,FE,edof,num_LC) 3 | % Case 1: Calling Prepare_K(xPhys,scale,penal,mat,KE,[],[],edof,[]) is SIMP stiffness matrix only 4 | % Case 2: Calling Prepare_K(xPhys,scale,scale.penal,mat,KE,[],FE,edof_per,num_LC) is SIMP stiffness and loading matrices 5 | % Case 3: Calling Prepare_K([],scale,[],[],[],KE_Hom,[],edof,[]) is Homogenized stiffness matrix only 6 | 7 | K = cell(3,3); 8 | K_nele = cell(3,3); 9 | K_i = cell(3,3); 10 | edof_k_i = cell(3,3); edof_k_j = cell(3,3); 11 | if ~isempty(FE) % if FE is not empty 12 | F = cell(3,3); 13 | F_nele = cell(3,3); 14 | F_i = cell(3,3); 15 | edof_f_i = cell(3,3); edof_f_j = cell(3,3); % input edof is edof_per for FE case 16 | else 17 | F = []; 18 | F_nele = []; 19 | end 20 | 21 | %% INITIALIZE STIFFNESS MATRICES 22 | if any(scale.phys == 1) % 1 = Mechanical 23 | K_nele{1,1} = zeros(scale.nele, (scale.nen*scale.dim)*(scale.nen*scale.dim)); 24 | edof_k_i{1,1} = zeros(scale.nele*(scale.nen*scale.dim)*(scale.nen*scale.dim),1); edof_k_j{1,1} = zeros(scale.nele*(scale.nen*scale.dim)*(scale.nen*scale.dim),1); 25 | K_i{1,1} = zeros(scale.nele*(scale.nen*scale.dim)*(scale.nen*scale.dim), 1); 26 | if ~isempty(FE) % if FE is not empty 27 | F_nele{1,1} = zeros(scale.nele, (scale.nen*scale.dim)*num_LC(1,1,scale.dim-1)); 28 | edof_f_i{1,1} = zeros(scale.nele*(scale.nen*scale.dim)*num_LC(1,1,scale.dim-1),1); edof_f_j{1,1} = zeros(scale.nele*(scale.nen*scale.dim)*num_LC(1,1,scale.dim-1),1); 29 | F_i{1,1} = zeros(scale.nele*(scale.nen*scale.dim)*num_LC(1,1,scale.dim-1), 1); 30 | end 31 | end 32 | if any(scale.phys == 2) % 2 = Thermal 33 | K_nele{2,2} = zeros(scale.nele, scale.nen*scale.nen); 34 | edof_k_i{2,2} = zeros(scale.nele*scale.nen*scale.nen, 1); edof_k_j{2,2} = zeros(scale.nele*scale.nen*scale.nen, 1); 35 | K_i{2,2} = zeros(scale.nele*scale.nen*scale.nen, 1); 36 | if ~isempty(FE) % if FE is not empty 37 | F_nele{2,2} = zeros(scale.nele, scale.nen*num_LC(2,2,scale.dim-1)); 38 | edof_f_i{2,2} = zeros(scale.nele*scale.nen*num_LC(2,2,scale.dim-1),1); edof_f_j{2,2} = zeros(scale.nele*scale.nen*num_LC(2,2,scale.dim-1),1); 39 | F_i{2,2} = zeros(scale.nele*scale.nen*num_LC(2,2,scale.dim-1), 1); 40 | end 41 | end 42 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 43 | K_nele{2,1} = zeros(scale.nele, (scale.nen*scale.dim)*scale.nen); 44 | edof_k_i{2,1} = zeros(scale.nele*(scale.nen*scale.dim)*scale.nen, 1); edof_k_j{2,1} = zeros(scale.nele*(scale.nen*scale.dim)*scale.nen, 1); 45 | K_i{2,1} = zeros(scale.nele*(scale.nen*scale.dim)*scale.nen, 1); 46 | if ~isempty(FE) % if FE is not empty 47 | F_nele{2,1} = zeros(scale.nele, (scale.nen*scale.dim)*num_LC(2,1,scale.dim-1)); 48 | edof_f_i{2,1} = zeros(scale.nele*(scale.nen*scale.dim)*num_LC(2,1,scale.dim-1),1); edof_f_j{2,1} = zeros(scale.nele*(scale.nen*scale.dim)*num_LC(2,1,scale.dim-1),1); 49 | F_i{2,1} = zeros(scale.nele*(scale.nen*scale.dim)*num_LC(2,1,scale.dim-1), 1); 50 | end 51 | end 52 | 53 | %% INTERPOLATE MATERIAL PROPERTIES 54 | for i = 1:scale.nele 55 | if any(scale.phys == 1) % 1 = Mechanical 56 | if ~isempty(KE) 57 | dens = mat.Emin + (xPhys(i)^penal)*(mat.Emax-mat.Emin); 58 | K_nele{1,1}(i,:) = KE{1,1}*dens; 59 | if ~isempty(FE) % if FE is not empty 60 | F_nele{1,1}(i,:) = FE{1,1}*dens; 61 | end 62 | elseif ~isempty(KE_Hom) 63 | K_nele{1,1}(i,:) = KE_Hom{i}{1,1}(:); 64 | end 65 | end 66 | if any(scale.phys == 2) % 2 = Thermal 67 | if ~isempty(KE) 68 | dens = mat.kmin + (xPhys(i)^penal)*(mat.kmax-mat.kmin); 69 | K_nele{2,2}(i,:) = KE{2,2}*dens; 70 | if ~isempty(FE) % if FE is not empty 71 | F_nele{2,2}(i,:) = FE{2,2}*dens; 72 | end 73 | elseif ~isempty(KE_Hom) 74 | K_nele{2,2}(i,:) = KE_Hom{i}{2,2}(:); 75 | end 76 | end 77 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 78 | if ~isempty(KE) 79 | dens = mat.Amin + (xPhys(i)^penal)*(mat.Amax-mat.Amin); 80 | K_nele{2,1}(i,:) = KE{2,1}*dens; 81 | if ~isempty(FE) % if FE is not empty 82 | F_nele{2,1}(i,:) = FE{2,1}*dens; 83 | end 84 | elseif ~isempty(KE_Hom) 85 | K_nele{2,1}(i,:) = KE_Hom{i}{2,1}(:); 86 | end 87 | end 88 | end 89 | 90 | %% ASSEMBLE STIFFNESS MATRICES 91 | for i = 1:scale.nele 92 | if any(scale.phys == 1) % 1 = Mechanical 93 | L1 = 1; L2 = 1; 94 | for j = 1:(scale.nen*scale.dim) 95 | for k = 1:(scale.nen*scale.dim) 96 | edof_k_i{1,1}((i-1)*(scale.nen*scale.dim)*(scale.nen*scale.dim) + L1) = edof{1}(i,k); 97 | edof_k_j{1,1}((i-1)*(scale.nen*scale.dim)*(scale.nen*scale.dim) + L1) = edof{1}(i,j); 98 | L1 = L1 + 1; 99 | if ~isempty(FE) % if FE is not empty 100 | if j <= num_LC(1,1,scale.dim-1) 101 | edof_f_i{1,1}((i-1)*(scale.nen*scale.dim)*num_LC(1,1,scale.dim-1) + L2) = edof{1}(i,k); 102 | edof_f_j{1,1}((i-1)*(scale.nen*scale.dim)*num_LC(1,1,scale.dim-1) + L2) = j; 103 | L2 = L2 + 1; 104 | end 105 | end 106 | end 107 | end 108 | K_i{1,1}(((i-1)*(scale.nen*scale.dim)*(scale.nen*scale.dim):(i*(scale.nen*scale.dim)*(scale.nen*scale.dim) - 1)) + 1) = K_nele{1,1}(i,:); 109 | if ~isempty(FE) % if FE is not empty 110 | F_i{1,1}(((i-1)*(scale.nen*scale.dim)*num_LC(1,1,scale.dim-1):(i*(scale.nen*scale.dim)*num_LC(1,1,scale.dim-1) - 1)) + 1) = F_nele{1,1}(i,:); 111 | end 112 | end 113 | if any(scale.phys == 2) % 2 = Thermal 114 | L1 = 1; L2 = 1; 115 | for j = 1:scale.nen 116 | for k = 1:scale.nen 117 | edof_k_i{2,2}((i-1)*scale.nen*scale.nen + L1) = edof{2}(i,k); 118 | edof_k_j{2,2}((i-1)*scale.nen*scale.nen + L1) = edof{2}(i,j); 119 | L1 = L1 + 1; 120 | if ~isempty(FE) % if FE is not empty 121 | if j <= num_LC(2,2,scale.dim-1) 122 | edof_f_i{2,2}((i-1)*scale.nen*num_LC(2,2,scale.dim-1) + L2) = edof{2}(i,k); 123 | edof_f_j{2,2}((i-1)*scale.nen*num_LC(2,2,scale.dim-1) + L2) = j; 124 | L2 = L2 + 1; 125 | end 126 | end 127 | end 128 | end 129 | K_i{2,2}(((i-1)*scale.nen*scale.nen:(i*scale.nen*scale.nen - 1)) + 1) = K_nele{2,2}(i,:); 130 | if ~isempty(FE) % if FE is not empty 131 | F_i{2,2}(((i-1)*scale.nen*num_LC(2,2,scale.dim-1):(i*scale.nen*num_LC(2,2,scale.dim-1) - 1)) + 1) = F_nele{2,2}(i,:); 132 | end 133 | end 134 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 135 | L1 = 1; 136 | for j = 1:scale.nen 137 | for k = 1:(scale.nen*scale.dim) 138 | edof_k_i{2,1}((i-1)*(scale.nen*scale.dim)*scale.nen + L1) = edof{1}(i,k); 139 | edof_k_j{2,1}((i-1)*(scale.nen*scale.dim)*scale.nen + L1) = edof{2}(i,j); 140 | L1 = L1 + 1; 141 | end 142 | end 143 | K_i{2,1}(((i-1)*(scale.nen*scale.dim)*scale.nen:(i*(scale.nen*scale.dim)*scale.nen - 1)) + 1) = K_nele{2,1}(i,:); 144 | if ~isempty(FE) % if FE is not empty 145 | L2 = 1; 146 | for j = 1:num_LC(2,1,scale.dim-1) 147 | for k = 1:(scale.nen*scale.dim) 148 | edof_f_i{2,1}((i-1)*(scale.nen*scale.dim)*num_LC(2,1,scale.dim-1) + L2) = edof{1}(i,k); 149 | edof_f_j{2,1}((i-1)*(scale.nen*scale.dim)*num_LC(2,1,scale.dim-1) + L2) = j; 150 | L2 = L2 + 1; 151 | end 152 | end 153 | F_i{2,1}(((i-1)*(scale.nen*scale.dim)*num_LC(2,1,scale.dim-1):(i*(scale.nen*scale.dim)*num_LC(2,1,scale.dim-1) - 1)) + 1) = F_nele{2,1}(i,:); 154 | end 155 | end 156 | end 157 | 158 | if any(scale.phys == 1) % 1 = Mechanical 159 | if ~isempty(FE) % if FE is not empty 160 | K{1,1} = sparse(edof_k_i{1,1},edof_k_j{1,1},K_i{1,1},scale.nele*scale.dim,scale.nele*scale.dim); K{1,1} = 0.5*(K{1,1}+K{1,1}'); % perodic sizing 161 | F{1,1} = sparse(edof_f_i{1,1},edof_f_j{1,1},F_i{1,1},scale.nele*scale.dim,num_LC(1,1,scale.dim-1)); %three load cases for the three strain cases 162 | else 163 | K{1,1} = sparse(edof_k_i{1,1},edof_k_j{1,1},K_i{1,1},(scale.nelx+1)*(scale.nely+1)*(scale.nelz+1)*scale.dim,(scale.nelx+1)*(scale.nely+1)*(scale.nelz+1)*scale.dim); K{1,1} = 0.5*(K{1,1}+K{1,1}'); % nonperiodic sizing 164 | end 165 | end 166 | if any(scale.phys == 2) % 2 = Thermal 167 | if ~isempty(FE) % if FE is not empty 168 | K{2,2} = sparse(edof_k_i{2,2},edof_k_j{2,2},K_i{2,2},scale.nele,scale.nele); K{2,2} = 0.5*(K{2,2}+K{2,2}'); % periodic sizing 169 | F{2,2} = sparse(edof_f_i{2,2},edof_f_j{2,2},F_i{2,2},scale.nele,num_LC(2,2,scale.dim-1)); %two load cases 170 | else 171 | K{2,2} = sparse(edof_k_i{2,2},edof_k_j{2,2},K_i{2,2},(scale.nelx+1)*(scale.nely+1)*(scale.nelz+1),(scale.nelx+1)*(scale.nely+1)*(scale.nelz+1)); K{2,2} = 0.5*(K{2,2}+K{2,2}'); % nonperiodic sizing 172 | end 173 | end 174 | if all(scale.phys == [1 2]) % 1,2 = Thermomechanical 175 | if ~isempty(FE) % if FE is not empty 176 | K{2,1} = sparse(edof_k_i{2,1},edof_k_j{2,1},K_i{2,1},scale.nele*scale.dim,scale.nele); % periodic sizing 177 | F{2,1} = sparse(edof_f_i{2,1},edof_f_j{2,1},F_i{2,1},scale.nele*scale.dim,num_LC(2,1,scale.dim-1)); %one load cases 178 | else 179 | K{2,1} = sparse(edof_k_i{2,1},edof_k_j{2,1},K_i{2,1},(scale.nelx+1)*(scale.nely+1)*(scale.nelz+1)*scale.dim,(scale.nelx+1)*(scale.nely+1)*(scale.nelz+1)); % nonperiodic sizing 180 | end 181 | end 182 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Prepare_KE.m: -------------------------------------------------------------------------------- 1 | % GENERATE ELEMENT STIFFNESS MATRIX AND HOMOGENIZATION LOAD VECTOR - MULTIPHYSICS 2 | function [KE, FE, GE] = Prepare_KE(C,phys,scale) 3 | % input C is stiffness/conductivity tensor for KE and FE generation (thermal, mechanical) 4 | % input C is the thermal stress tensor for FE generation (thermomechanical) 5 | % no input required for coupling tensors GE 6 | 7 | %nels is the number of side elements for a microscale that is homogenized 8 | nels = scale.nelx; % assumes that nelx = nely = nelz 9 | 10 | if scale.dim == 2 11 | % a = (1/nels)/2; b = (1/nels)/2; 12 | a = (scale.DDx/scale.nelx)/2; b = (scale.DDy/scale.nely)/2; 13 | X = [-a a a -a]; % a and b matter for FE. They do not affect 2D KE, but affect 3D KE. This is adjusted for below (line 137). 14 | Y = [-b -b b b]; 15 | if phys == 1 % Mechanical 16 | ndofn = 2; 17 | epsilon = diag([1 1 1]); 18 | alpha = zeros(size(epsilon,1),ndofn,scale.dim); 19 | alpha(1,1,1) = 1; alpha(3,2,1) = 1; 20 | alpha(2,2,2) = 1; alpha(3,1,2) = 1; 21 | elseif phys == 2 % Thermal 22 | ndofn = 1; 23 | epsilon = diag([1 1]); 24 | alpha = zeros(size(epsilon,1),ndofn,scale.dim); 25 | alpha(1,1,1) = 1; 26 | alpha(2,1,2) = 1; 27 | elseif all(phys == [1 2]) % Thermomechanical 28 | ndofn = 2; 29 | epsilon = [1; 1; 0]; 30 | alpha = zeros(size(epsilon,1),ndofn,scale.dim); 31 | alpha(1,1,1) = 1; alpha(3,2,1) = 1; 32 | alpha(2,2,2) = 1; alpha(3,1,2) = 1; 33 | elseif phys == 3 % Fluid 34 | ndofn = 3; 35 | end 36 | elseif scale.dim == 3 37 | % a = (1/nels)/2; b = (1/nels)/2; c = (1/nels)/2; 38 | a = (scale.DDx/scale.nelx)/2; b = (scale.DDy/scale.nely)/2; c = (scale.DDz/scale.nelz)/2; 39 | X = [-a a a -a -a a a -a]; 40 | Y = [-b -b b b -b -b b b]; 41 | Z = [-c -c -c -c c c c c]; 42 | if phys == 1 % Mechanical 43 | ndofn = 3; 44 | epsilon = diag([1 1 1 1 1 1]); 45 | alpha = zeros(size(epsilon,1),ndofn,scale.dim); 46 | alpha(1,1,1) = 1; alpha(4,2,1) = 1; alpha(6,3,1) = 1; 47 | alpha(2,2,2) = 1; alpha(4,1,2) = 1; alpha(5,3,2) = 1; 48 | alpha(3,3,3) = 1; alpha(5,2,3) = 1; alpha(6,1,3) = 1; 49 | elseif phys == 2 % Thermal 50 | ndofn = 1; 51 | epsilon = diag([1 1 1]); 52 | alpha = zeros(size(epsilon,1),ndofn,scale.dim); 53 | alpha(1,1,1) = 1; 54 | alpha(2,1,2) = 1; 55 | alpha(3,1,3) = 1; 56 | elseif all(phys == [1 2]) % Thermomechanical 57 | ndofn = 3; 58 | epsilon = [1; 1; 1; 0; 0; 0]; 59 | alpha = zeros(size(epsilon,1),ndofn,scale.dim); 60 | alpha(1,1,1) = 1; alpha(4,2,1) = 1; alpha(6,3,1) = 1; 61 | alpha(2,2,2) = 1; alpha(4,1,2) = 1; alpha(5,3,2) = 1; 62 | alpha(3,3,3) = 1; alpha(5,2,3) = 1; alpha(6,1,3) = 1; 63 | elseif phys == 3 % Fluid 64 | ndofn = 4; 65 | end 66 | end 67 | 68 | GP = [-1/sqrt(3), 1/sqrt(3)]; 69 | W = [1 1]; 70 | 71 | KE = zeros(scale.nen*ndofn,scale.nen*ndofn); 72 | FE = zeros(scale.nen*ndofn,size(epsilon,2)); 73 | GE = zeros(scale.nen*ndofn,scale.nen); 74 | beta = zeros(size(epsilon,1),ndofn); 75 | for ii = 1:2 76 | for jj = 1:2 77 | if scale.dim == 2 78 | [N, dN] = N_dN_calc([GP(ii), GP(jj)]); 79 | J(1,1) = dot(dN(:,1),X); 80 | J(1,2) = dot(dN(:,1),Y); 81 | J(2,1) = dot(dN(:,2),X); 82 | J(2,2) = dot(dN(:,2),Y); 83 | invJ = inv(J); 84 | weight = W(ii)*W(jj)*det(J)*scale.DDz; 85 | B = zeros(size(epsilon,1),scale.nen*ndofn); 86 | 87 | for ll = 1:scale.dim 88 | for i = 1:size(epsilon,1) 89 | for j = 1:ndofn 90 | beta(i,j) = invJ(ll,ll)*alpha(i,j,ll); 91 | end 92 | for j = 1:(scale.nen*ndofn) 93 | B(i,j) = B(i,j) + beta(i,mod(j-1,ndofn)+1)*dN(floor((j-1)/ndofn)+1,ll); 94 | end 95 | end 96 | end 97 | 98 | if all(phys == 1) || all(phys == 2) 99 | KE = KE + weight*B'*C*B; % this 2D KE calculation is already mesh-independent 100 | FE = FE + weight*B'*C*epsilon; % FE is inherently mesh-dependent 101 | GE = []; 102 | elseif all(phys == [1 2]) 103 | KE = []; 104 | FE = FE + weight*B'*C*epsilon; 105 | GE = GE + weight*B'*epsilon*N; 106 | end 107 | elseif scale.dim == 3 108 | for kk = 1:2 109 | [N, dN] = N_dN_calc([GP(ii), GP(jj), GP(kk)]); 110 | J(1,1) = dot(dN(:,1),X); 111 | J(1,2) = dot(dN(:,1),Y); 112 | J(1,3) = dot(dN(:,1),Z); 113 | J(2,1) = dot(dN(:,2),X); 114 | J(2,2) = dot(dN(:,2),Y); 115 | J(2,3) = dot(dN(:,2),Z); 116 | J(3,1) = dot(dN(:,3),X); 117 | J(3,2) = dot(dN(:,3),Y); 118 | J(3,3) = dot(dN(:,3),Z); 119 | invJ = inv(J); 120 | weight = W(ii)*W(jj)*W(kk)*det(J); 121 | B = zeros(size(epsilon,1),scale.nen*ndofn); 122 | 123 | for ll = 1:scale.dim 124 | for i = 1:size(epsilon,1) 125 | for j = 1:ndofn 126 | beta(i,j) = invJ(ll,ll)*alpha(i,j,ll); 127 | end 128 | end 129 | for i = 1:size(epsilon,1) 130 | for j = 1:(scale.nen*ndofn) 131 | B(i,j) = B(i,j) + beta(i,mod(j-1,ndofn)+1)*dN(floor((j-1)/ndofn)+1,ll); 132 | end 133 | end 134 | end 135 | 136 | if all(phys == 1) || all(phys == 2) 137 | KE = KE + weight*B'*C*B*nels; % nels factor added to make 3D KE mesh-independent (for fea, but not for homogenization) 138 | FE = FE + weight*B'*C*epsilon; % FE is inherently mesh-dependent 139 | GE = []; 140 | elseif all(phys == [1 2]) 141 | KE = []; 142 | FE = FE + weight*B'*C*epsilon; 143 | GE = GE + weight*B'*epsilon*N; 144 | end 145 | end 146 | end 147 | end 148 | end 149 | KE = 0.5*(KE+KE'); KE = KE(:); 150 | FE = FE(:); 151 | GE = GE(:); 152 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Prepare_Mesh.m: -------------------------------------------------------------------------------- 1 | % PREPARE MESH CONNECTIVITY MATRICES 2 | function [necon, necon_per] = Prepare_Mesh(scale) 3 | % Node numbers for non-periodic mesh 4 | [Y, X, Z] = meshgrid(0:scale.nely,0:scale.nelx,0:scale.nelz); % nonperiodic mesh 5 | nnums = X + Y*(scale.nelx+1) + Z*(scale.nely+1)*(scale.nelx+1); 6 | 7 | % Node numbers for periodic mesh 8 | nelx_1 = scale.nelx-1; nely_1 = scale.nely-1; nelz_1 = max(scale.nelz-1,0); 9 | [Y_per, X_per, Z_per] = meshgrid(0:nely_1,0:nelx_1,0:nelz_1); % sub periodic mesh 10 | nnums_per = X_per + Y_per*(nelx_1+1) + Z_per*(nely_1+1)*(nelx_1+1); % nnums created with nel-1 (sub periodic mesh) 11 | nnums_per(end+1,:,:) = nnums_per(1,:,:); nnums_per(:,end+1,:) = nnums_per(:,1,:); nnums_per(:,:,end+1) = nnums_per(:,:,1); % periodic mesh 12 | 13 | % Create connectivity matrices for non-periodic and periodic meshes 14 | if scale.dim == 2 % 2D 15 | nnums_per = nnums_per(:,:,1); 16 | nnums1 = nnums(1:end-1,1:end-1); nnums1 = nnums1(:); 17 | nnums2 = nnums(2:end,1:end-1); nnums2 = nnums2(:); 18 | nnums3 = nnums(2:end,2:end); nnums3 = nnums3(:); 19 | nnums4 = nnums(1:end-1,2:end); nnums4 = nnums4(:); 20 | necon = [nnums1, nnums2, nnums3, nnums4]'; necon = necon(:); 21 | nnums_per1 = nnums_per(1:end-1,1:end-1); nnums_per1 = nnums_per1(:); 22 | nnums_per2 = nnums_per(2:end,1:end-1); nnums_per2 = nnums_per2(:); 23 | nnums_per3 = nnums_per(2:end,2:end); nnums_per3 = nnums_per3(:); 24 | nnums_per4 = nnums_per(1:end-1,2:end); nnums_per4 = nnums_per4(:); 25 | necon_per = [nnums_per1, nnums_per2, nnums_per3, nnums_per4]'; necon_per = necon_per(:); 26 | elseif scale.dim == 3 % 3D 27 | nnums1 = nnums(1:end-1,1:end-1,1:end-1); nnums1 = nnums1(:); 28 | nnums2 = nnums(2:end,1:end-1,1:end-1); nnums2 = nnums2(:); 29 | nnums3 = nnums(2:end,2:end,1:end-1); nnums3 = nnums3(:); 30 | nnums4 = nnums(1:end-1,2:end,1:end-1); nnums4 = nnums4(:); 31 | nnums5 = nnums(1:end-1,1:end-1,2:end); nnums5 = nnums5(:); 32 | nnums6 = nnums(2:end,1:end-1,2:end); nnums6 = nnums6(:); 33 | nnums7 = nnums(2:end,2:end,2:end); nnums7 = nnums7(:); 34 | nnums8 = nnums(1:end-1,2:end,2:end); nnums8 = nnums8(:); 35 | necon = [nnums1, nnums2, nnums3, nnums4, nnums5, nnums6, nnums7, nnums8]'; necon = necon(:); 36 | nnums_per1 = nnums_per(1:end-1,1:end-1,1:end-1); nnums_per1 = nnums_per1(:); 37 | nnums_per2 = nnums_per(2:end,1:end-1,1:end-1); nnums_per2 = nnums_per2(:); 38 | nnums_per3 = nnums_per(2:end,2:end,1:end-1); nnums_per3 = nnums_per3(:); 39 | nnums_per4 = nnums_per(1:end-1,2:end,1:end-1); nnums_per4 = nnums_per4(:); 40 | nnums_per5 = nnums_per(1:end-1,1:end-1,2:end); nnums_per5 = nnums_per5(:); 41 | nnums_per6 = nnums_per(2:end,1:end-1,2:end); nnums_per6 = nnums_per6(:); 42 | nnums_per7 = nnums_per(2:end,2:end,2:end); nnums_per7 = nnums_per7(:); 43 | nnums_per8 = nnums_per(1:end-1,2:end,2:end); nnums_per8 = nnums_per8(:); 44 | necon_per = [nnums_per1, nnums_per2, nnums_per3, nnums_per4, nnums_per5, nnums_per6, nnums_per7, nnums_per8]'; necon_per = necon_per(:); 45 | end 46 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Prepare_Micro_Model.m: -------------------------------------------------------------------------------- 1 | % LOAD MICROSCALE DATA STRUCTURE AND ANN MODEL 2 | function [micro, ML_model] = Prepare_Micro_Model(useANN,dim,phys) 3 | if useANN % LOAD MICROSCALE SURROGATE MODEL 4 | if all(phys == [1]) 5 | if dim == 2 6 | ann_name = 'Homog2D_001_Mecha_N1000000_nels50'; 7 | elseif dim == 3 8 | ann_name = 'Homog3D_001_Mecha_N1000000_nels10'; 9 | end 10 | load(['ML_Models/Phys001_Mecha/' ann_name '.mat'],'ML_model','micro'); % Load Machine Learning Model 11 | elseif all(phys == [2]) 12 | if dim == 2 13 | ann_name = 'Homog2D_011_Therm_N1000000_nels50'; 14 | elseif dim == 3 15 | ann_name = 'Homog3D_011_Therm_N1000000_nels50'; 16 | end 17 | load(['ML_Models/Phys002_Therm/' ann_name '.mat'],'ML_model','micro'); % Load Machine Learning Model 18 | elseif all(phys == [1 2]) 19 | if dim == 2 20 | ann_name = 'Homog2D_011_ThMec_N1000000_nels50'; 21 | elseif dim == 3 22 | ann_name = 'Homog3D_011_ThMec_N1000000_nels50'; 23 | end 24 | load(['ML_Models/Phys012_ThMec/' ann_name '.mat'],'ML_model','micro'); % Load Machine Learning Model 25 | end 26 | micro.ann_name = ann_name; 27 | else % DEFINE MICROSCALE OPTIMIZATION PARAMETERS 28 | ML_model = []; micro.ann_name = []; % Machine Learning Model 29 | if dim == 2 30 | micro.nelx = 50; 31 | elseif dim == 3 32 | micro.nelx = 10; 33 | end 34 | if rem(micro.nelx,2) ~= 0; micro.nelx = micro.nelx + 1; end % nels should be an even number 35 | micro.dim = dim; micro.phys = phys; micro.nely = micro.nelx; if micro.dim == 2; micro.nelz = 0; elseif micro.dim == 3; micro.nelz = micro.nelx; end 36 | micro.penal = 3; micro.cont = 1; % Continuation: 1 = Yes, = 0 No % CURRENTLY IT IS ASSUMED THAT ALL PHYS MODES USE THE SAME PENALIZATION SCHEME 37 | micro.flt_den_min = micro.nelx/20; % density filter minimum radius (flt_den_min <= 1 is off) 38 | micro.flt_sen_min = 1.0; % sensitivity filter minimum radius (flt_sen_min <= 1 is off) 39 | micro.xMicro_lb = 0; micro.xMicro_ub = 1; % bounds for the initial design (final designs are always binary bounds) 40 | 41 | micro.maxloop = 50; % Maximum number of iterations 42 | micro.tolx = 1e-4; % Termination criterion 43 | micro.displayflag = 0; % Display structure flag 44 | 45 | micro.bc_type = 2; % TOM Type of Boundary Conditions: 1 = Internal Force BC, 2 = Internal Displacement BC 46 | micro.load_bc = []; % TOM Load Boundary Conditions: 1 = Corners, 2 = Midpoints of Edges, 3 = Edges, 4 = Face, 5 = Midpoints of Faces 47 | micro.supp_bc = [1]; % TOM Support Boundary Conditions: 1 = Corners, 2 = Midpoints of Edges, 3 = Edges, 4 = Face, 5 = Midpoints of Faces 48 | micro.design_ini = 1; % TOM Initial Design: 1 = Uniform, 2 = Normal, 3 = Random Distributions 49 | micro.ML_model_type = 1; 50 | if all(phys == [1]) 51 | if dim == 2 52 | micro.spdim_nums = [2, 0, 0; 10, 0, 0]; % TOM Dimension Indexing 53 | micro.spdim = 10; % Number of TOM Dimensions 54 | elseif dim == 3 55 | micro.spdim_nums = [2, 0, 0; 26, 0, 0]; % TOM Dimension Indexing 56 | micro.spdim = 26; % Number of TOM Dimensions 57 | end 58 | elseif all(phys == [2]) 59 | if dim == 2 60 | micro.spdim_nums = [0, 2, 0; 0, 6, 0]; % TOM Dimension Indexing 61 | micro.spdim = 6; % Number of TOM Dimensions 62 | elseif dim == 3 63 | 64 | end 65 | elseif all(phys == [1 2]) 66 | if dim == 2 67 | micro.spdim_nums = [2, 10, 0; 10, 14, 0]; % TOM Dimension Indexing 68 | micro.spdim = 14; % Number of TOM Dimensions 69 | elseif dim == 3 70 | 71 | end 72 | end 73 | if micro.bc_type == 2; micro.load_bc = []; end 74 | 75 | [micro.necon, micro.necon_per] = Prepare_Mesh(micro); % Micro mesh connectivity 76 | micro = mesh_info(micro); 77 | micro.DDx = 1; micro.DDy = 1; micro.DDz = 1; % Design Domain size. DDz is thickness in 2D 78 | [micro.den_H, micro.den_Hs] = Prepare_Filter(micro,micro.flt_den_min); % Micro density filter 79 | [micro.sen_H, micro.sen_Hs] = Prepare_Filter(micro,micro.flt_sen_min); % Micro sensitivity filter 80 | end 81 | micro.ml = useANN; % record if an ANN was loaded 82 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Prepare_Sample_Plan.m: -------------------------------------------------------------------------------- 1 | % GENERATE FEATURES AND SAMPLE PLAN 2 | function [X,scale] = Prepare_Sample_Plan(scale) 3 | %% GENERATE FEATURES AND SAMPLE PLAN FOR HOMOGENIZATION 4 | % Dimension Counting 5 | scale.spdim = 0; %[Dimension] 6 | scale.spdim = scale.spdim + 1; % volume fraction input 7 | scale.spdim = scale.spdim + 1; % objective function weight input 8 | % scale.spdim = scale.spdim + 1; % penalization parameter input 9 | scale.spdim_nums = zeros(2,3); % rows = start dim num, end dim num for Fx/Ux_e. cols = number of phyics models 10 | if any(scale.phys == 1) 11 | scale.spdim_nums(1,1) = scale.spdim; 12 | scale.spdim = scale.spdim + scale.nen*scale.dim; 13 | scale.spdim_nums(2,1) = scale.spdim; 14 | end 15 | if any(scale.phys == 2) 16 | scale.spdim_nums(1,2) = scale.spdim; 17 | scale.spdim = scale.spdim + scale.nen; 18 | scale.spdim_nums(2,2) = scale.spdim; 19 | end 20 | 21 | % Feature Bounds 22 | lb = ones(1,scale.spdim)*-1; ub = ones(1,scale.spdim); % boundary condition input range 23 | lb(1) = scale.x_lb(1); ub(1) = scale.x_ub(1); % volume fraction input range 24 | if length(scale.phys) == 1; lb(2) = 1; ub(2) = 1; else; lb(2) = scale.x_lb(2); ub(2) = scale.x_ub(2); end % objective function weight input range 25 | % if scale.cont == 1; lb(3) = 1; ub(3) = scale.penal; else; lb(3) = scale.penal; ub(3) = scale.penal; end % penalization parameter input range 26 | 27 | % Latin Hypercube Sampling 28 | X_unnorm = lhsdesign(ceil(scale.N*scale.rr),scale.spdim-2,'iterations',10); %Volume fraction and Nodal force/disp Input 29 | X_unnorm = [X_unnorm(:,1) lhsdesign(ceil(scale.N*scale.rr),2,'iterations',10) X_unnorm(:,2:end)]; %Objective function Weight and penalization parameter Input 30 | for i = 1:ceil(scale.N*scale.rr) 31 | X_unnorm(i,:) = X_unnorm(i,:).*(ub-lb) + lb; 32 | end 33 | 34 | % Normalize 35 | X = X_unnorm; 36 | for i = 1:ceil(scale.N*scale.rr) 37 | if any(scale.phys == 1) 38 | X(i,(scale.spdim_nums(1,1)+1):scale.spdim_nums(2,1)) = Normalize_TOM_BC(X_unnorm(i,(scale.spdim_nums(1,1)+1):scale.spdim_nums(2,1)),scale); % normalize subproblem boundary condition values 39 | end 40 | if any(scale.phys == 2) 41 | X(i,(scale.spdim_nums(1,2)+1):scale.spdim_nums(2,2)) = Normalize_TOM_BC(X_unnorm(i,(scale.spdim_nums(1,2)+1):scale.spdim_nums(2,2)),scale); % normalize subproblem boundary condition values 42 | end 43 | end 44 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/SS_FEA.m: -------------------------------------------------------------------------------- 1 | % SINGLE-SCALE FINITE ELEMENT ANALYSIS 2 | function [c, c_vec] = SS_FEA(proj_rho,scale,mat) 3 | try 4 | % PREPARE MESH AND FILTER 5 | scale.DDx = scale.nelx; scale.DDy = scale.nely; if scale.nelz == 0; scale.DDz = 1; else; scale.DDz = scale.nelz; end % Design Domain size. DDz is thickness in 2D 6 | [scale.H,scale.Hs] = Prepare_Filter(scale,0.1); % No filter needed. placer holder H, Hs 7 | [scale.necon, scale.necon_per] = Prepare_Mesh(scale); % Micro mesh connectivity 8 | 9 | % PREPARE FEA 10 | KE = cell(3,3); % # of rows/cols is the number of unique physics modes || KE_ij: coupling stiffness matrices of physics i to physics j 11 | if any(scale.phys == 1) 12 | [KE{1,1}, ~, ~] = Prepare_KE(Prepare_C(mat.nu,1,scale.dim),1,scale); % Mechanical 13 | end 14 | if any(scale.phys == 2) 15 | [KE{2,2}, ~, ~] = Prepare_KE(Prepare_C(mat.nu,2,scale.dim),2,scale); % Thermal 16 | end 17 | if all(scale.phys == [1 2]) 18 | [ ~, ~, KE{2,1}] = Prepare_KE(Prepare_C(mat.nu,1,scale.dim),[1,2],scale); % Thermomechanical 19 | end 20 | 21 | % PREPARE MACROSCALE BOUNDARY CONDITIONS 22 | scale.bc = cell(3,2); % rows = physics modes, columns = [loads, supports] 23 | F0_Macro = cell(1,3); % KU = F for 3 physics 24 | U0_Macro = cell(1,3); 25 | N_Macro = cell(1,3); 26 | for p = 1:length(scale.phys) 27 | [scale,F0_Macro{scale.phys(p)},U0_Macro{scale.phys(p)},N_Macro{scale.phys(p)}] = Prepare_BCMacro(scale,scale.phys(p)); 28 | end 29 | 30 | % FINITE ELEMENT ANALYSIS 31 | disp('Solving single-scale FEA '); tic 32 | [U_Macro,~,~] = FEA(proj_rho,scale,3,mat,KE,F0_Macro,U0_Macro,N_Macro); 33 | [c, ~, c_vec] = Obj_Fns(proj_rho,scale,U_Macro,3,mat,KE,[],[],0.5); % 0.5 argument is weight between multiphysics if on 34 | disp(['Finished Solving single-scale FEA. Time: ' num2str(toc) ' seconds.']); 35 | catch ME 36 | c_vec = NaN; c = []; 37 | disp(['Skipping single-scale FEA due to error:' ME.message]); % try-catch is used here in case the system runs-out-of-memory 38 | end 39 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/TOM_scaler.m: -------------------------------------------------------------------------------- 1 | % SCALE MICROSCALE MESH RESOLUTION (NOT COMPLETED YET, PLACE-HOLDER CODE) 2 | function xMicro_scaled = TOM_scaler(macro,micro,xMicro) 3 | %% SCALING TO SPECIFIED EPI SIZE 4 | [Ye_c, Xe_c, Ze_c] = meshgrid((-macro.nely/2 + 1/2):1:(macro.nely/2 - 1/2),(-macro.nelx/2 + 1/2):1:(macro.nelx/2 - 1/2),min(-macro.nelz/2 + 1/2,0):1:max(macro.nelz/2 - 1/2,0)); % middle of coarse, unscaled element coordinates on point-wise coordinates 5 | [ye, xe, ze] = meshgrid((-0.5 + 0.5/micro.nely):(1/micro.nely):(0.5 - 0.5/micro.nely),(-0.5 + 0.5/micro.nelx):(1/micro.nelx):(0.5 - 0.5/micro.nelx),min(-0.5 + 0.5/micro.nelz,0):(1/micro.nelz):max(0.5 - 0.5/micro.nelz,0)); % middle of resolution-scaled microscale element coordinates on global reference 6 | 7 | xMicro_data = cell(size(xMicro)); 8 | for e = 1:macro.nele 9 | xMicro_data{e} = zeros(micro.nele,4); 10 | xMicro_data{e}(:,1) = Xe_c(e) + xe(:); 11 | xMicro_data{e}(:,2) = Ye_c(e) + ye(:); 12 | xMicro_data{e}(:,3) = Ze_c(e) + ze(:); 13 | xMicro_data{e}(:,4) = xMicro{e}(:); 14 | end 15 | 16 | macro_scaled = macro; 17 | [Ye_f, Xe_f, Ze_f] = meshgrid((-macro.nely/2 + macro.epi/2):macro.epi:(macro.nely/2 - macro.epi/2),(-macro.nelx/2 + macro.epi/2):macro.epi:(macro.nelx/2 - macro.epi/2),min(-macro.nelz/2 + macro.epi/2,0):macro.epi:max(macro.nelz/2 - macro.epi/2,0)); % middle of coarse, unscaled element coordinates on point-wise coordinates 18 | macro_scaled.nelx = macro.nelx*(1/macro.epi); macro_scaled.nely = macro.nely*(1/macro.epi); macro_scaled.nelz = macro.nelz*(1/macro.epi); 19 | [macro_scaled.epi_necon, ~] = Prepare_Mesh(macro_scaled); % Macro mesh connectivity 20 | macro_scaled = mesh_info(macro_scaled); 21 | 22 | [ye, xe, ze] = meshgrid((-0.5 + 0.5/micro.nely)*macro.epi:(1/micro.nely)*macro.epi:(0.5 - 0.5/micro.nely)*macro.epi,(-0.5 + 0.5/micro.nelx)*macro.epi:(1/micro.nelx)*macro.epi:(0.5 - 0.5/micro.nelx)*macro.epi,min(-0.5 + 0.5/micro.nelz,0)*macro.epi:(1/micro.nelz)*macro.epi:max(0.5 - 0.5/micro.nelz,0)*macro.epi); % middle of resolution-scaled microscale element coordinates on global reference 23 | 24 | xMicro_scaled = cell(size(xMicro)*(1/macro.epi)); 25 | xMicro_scaled_data = cell(size(xMicro)*(1/macro.epi)); 26 | for e = 1:macro_scaled.nele 27 | xMicro_scaled_data{e} = zeros(micro.nele,3); 28 | xMicro_scaled_data{e}(:,1) = Xe_f(e) + xe(:); 29 | xMicro_scaled_data{e}(:,2) = Ye_f(e) + ye(:); 30 | xMicro_scaled_data{e}(:,3) = Ze_f(e) + ze(:); 31 | end 32 | 33 | xMicro_data_x = cell(micro.nele,1); xMicro_data_y = cell(micro.nele,1); xMicro_data_z = cell(micro.nele,1); xMicro_data_s = cell(micro.nele,1); 34 | xMicro_scaled_data_x = cell(micro.nele,1); xMicro_scaled_data_y = cell(micro.nele,1); xMicro_scaled_data_z = cell(micro.nele,1); xMicro_scaled_data_s = cell(micro.nele,1); 35 | parfor s = 1:micro.nele 36 | xMicro_data_x{s} = zeros(size(xMicro_data)); xMicro_data_y{s} = zeros(size(xMicro_data)); xMicro_data_z{s} = zeros(size(xMicro_data)); xMicro_data_s{s} = zeros(size(xMicro_data)); 37 | for e = 1:macro.nele 38 | xMicro_data_x{s}(e) = xMicro_data{e}(s,1); 39 | xMicro_data_y{s}(e) = xMicro_data{e}(s,2); 40 | xMicro_data_z{s}(e) = xMicro_data{e}(s,3); 41 | xMicro_data_s{s}(e) = xMicro_data{e}(s,4); 42 | end 43 | xMicro_scaled_data_x{s} = zeros(size(xMicro_scaled_data)); xMicro_scaled_data_y{s} = zeros(size(xMicro_scaled_data)); xMicro_scaled_data_z{s} = zeros(size(xMicro_scaled_data)); xMicro_scaled_data_s{s} = zeros(size(xMicro_scaled_data)); 44 | for e = 1:macro_scaled.nele 45 | xMicro_scaled_data_x{s}(e) = xMicro_scaled_data{e}(s,1); 46 | xMicro_scaled_data_y{s}(e) = xMicro_scaled_data{e}(s,2); 47 | xMicro_scaled_data_z{s}(e) = xMicro_scaled_data{e}(s,3); 48 | end 49 | if macro.dim == 2 50 | xMicro_scaled_data_s{s} = interp2(xMicro_data_y{s},xMicro_data_x{s},xMicro_data_s{s},max(min(xMicro_scaled_data_y{s},max(xMicro_data_y{s}(:))),min(xMicro_data_y{s}(:))),max(min(xMicro_scaled_data_x{s},max(xMicro_data_x{s}(:))),min(xMicro_data_x{s}(:))),'linear'); % X_scaled and Y_scaled coordinates are rounded to be on boundary inside this interp function call so that a nearest neighbor boundary condition is effectively applied. 51 | elseif macro.dim == 3 52 | xMicro_scaled_data_s{s} = interp3(xMicro_data_y{s},xMicro_data_x{s},xMicro_data_z{s},xMicro_data_s{s},max(min(xMicro_scaled_data_y{s},max(xMicro_data_y{s}(:))),min(xMicro_data_y{s}(:))),max(min(xMicro_scaled_data_x{s},max(xMicro_data_x{s}(:))),min(xMicro_data_x{s}(:))),max(min(xMicro_scaled_data_z{s},max(xMicro_data_z{s}(:))),min(xMicro_data_z{s}(:))),'linear'); % X_scaled, Y_scaled, and Z_scaled coordinates are rounded to be on boundary inside this interp function call so that a nearest neighbor boundary condition is effectively applied. 53 | end 54 | end 55 | 56 | 57 | parfor e = 1:macro_scaled.nele 58 | xMicro_scaled{e} = ini_design(micro,0,micro.x_lb(1),micro.x_ub(1)); 59 | for s = 1:micro.nele 60 | xMicro_scaled{e}(s) = xMicro_scaled_data_s{s}(e); 61 | end 62 | end 63 | 64 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/Train_ANNs.m: -------------------------------------------------------------------------------- 1 | % TRAIN ANN MODELS 2 | function [ML_model,TR] = Train_ANNs(target_path,SP_para,SP_C,micro,n_uni_train,NN,NL,reps) 3 | %% PREPARE FEATURES AND TARGETS 4 | % Features and Targets 5 | X_0 = SP_para; 6 | Y_0 = SP_C; 7 | % Augmentations 8 | X = cell(micro.N,1); 9 | Y = cell(micro.N,1); %Homogenized Tensor Output 10 | WaitMessage = parfor_waitbar(micro.N); %Add ('Waitbar', true) as arguments to turn on a waitbar popup 11 | fprintf(' | Augment TOMs |\n'); tic 12 | parfor i = 1:micro.N 13 | [X{i},Y{i}] = Augment_TOMs(micro,X_0(i,:),Y_0{i}); 14 | WaitMessage.Send; 15 | end 16 | WaitMessage.Destroy; 17 | fprintf(' | Done. Elapsed Time: %4.2f seconds. |\n\n',toc); augtime = toc; 18 | X = cell2mat(X); 19 | Y = vertcat(Y{:}); 20 | if micro.dim == 2; aug_total = 8; elseif micro.dim == 3; aug_total = 48; end % number of total augmentations 21 | 22 | %% NUMBER INDEX TESTING AND TRAINING DATA SETS 23 | n_uni_total = size(X,1)/aug_total; % Total number of samples without augmentations 24 | if n_uni_train > n_uni_total*0.7 % if n_uni_train is larger than 70% of n_uni_total 25 | n_uni_train = floor(n_uni_total*0.7); % n_uni_train should not be bigger than 70% of n_uni_total if testing IS planned 26 | % fprintf('WARNING: n_uni_train IS TOO LARGE. SETTING n_uni_train TO: %d\n',n_uni_train) 27 | end 28 | n_uni_test = round(n_uni_train*0.3/0.7); % Total data is 70% training data and 30% testing/validation data 29 | n_uni_model = n_uni_train + n_uni_test; 30 | total_indexes = randperm(n_uni_total,n_uni_model); 31 | n_ind_block_training = total_indexes(1:n_uni_train); 32 | n_ind_block_testing = total_indexes(n_uni_train+1:end); 33 | 34 | %% INDEX TRAINING 35 | index_training = []; 36 | ind1 = 1; 37 | for i1 = 1:n_uni_train 38 | if micro.n_aug == 1 39 | rand_samp = 1; 40 | elseif micro.n_aug > 1 41 | rand_samp = sort([1 randperm(aug_total-1,micro.n_aug-1)+1])'; % always include the first augmentation of a sample's augmentation set 42 | end 43 | index_training(ind1:ind1 + micro.n_aug-1,1) = aug_total*(n_ind_block_training(i1)-1) + rand_samp; 44 | ind1 = ind1 + micro.n_aug; 45 | end 46 | 47 | %% INDEX TESTING 48 | index_testing = []; 49 | ind1 = 1; 50 | for i1 = 1:n_uni_test 51 | if micro.n_aug == 1 52 | rand_samp = 1; 53 | elseif micro.n_aug > 1 54 | rand_samp = sort([1 randperm(aug_total-1,micro.n_aug-1)+1])'; % always include the first augmentation of a sample's augmentation set 55 | end 56 | index_testing(ind1:(ind1 + micro.n_aug-1),1) = aug_total*(n_ind_block_testing(i1)-1) + rand_samp; 57 | ind1 = ind1 + micro.n_aug; 58 | end 59 | 60 | %% TRAIN SURROGATE MODELS 61 | [ML_model,TR] = ANN_model(X,Y,index_training,index_testing,micro,NN,NL,reps); 62 | 63 | %% SAVE SURROGATE MODELS 64 | save(target_path,'-append','ML_model','TR','augtime'); % does not appended augmented X and Y; to save space 65 | end 66 | 67 | %% GENERATE ANN MODELS 68 | function [ML_model,TR] = ANN_model(X,Y,index_training,index_testing,micro,NN,NL,reps) 69 | % Calculate min and max constitutive tensor values 70 | Cmin = cell(3,3); Cmax = cell(3,3); 71 | if any(micro.phys == 1) 72 | Cmin{1,1} = micro.mat.Emin * Prepare_C(micro.mat.nu,1,micro.dim); 73 | Cmax{1,1} = micro.mat.Emax * Prepare_C(micro.mat.nu,1,micro.dim); 74 | end 75 | if any(micro.phys == 2) 76 | Cmin{2,2} = micro.mat.kmin * Prepare_C(micro.mat.nu,2,micro.dim); 77 | Cmax{2,2} = micro.mat.kmax * Prepare_C(micro.mat.nu,2,micro.dim); 78 | end 79 | if 2 == sum(micro.phys == [1 2]) 80 | Cmin{2,1} = micro.mat.Amin * Prepare_C(micro.mat.nu,1,micro.dim); % might not be finished yet 81 | Cmax{2,1} = micro.mat.Amax * Prepare_C(micro.mat.nu,1,micro.dim); % might not be finished yet 82 | end 83 | [C_lower_HS, C_upper_HS] = Prepare_HS_bounds(micro.mat,micro.dim,micro.ortho); 84 | 85 | % Initalize ANN 86 | % https://stats.stackexchange.com/questions/181/how-to-choose-the-number-of-hidden-layers-and-nodes-in-a-feedforward-neural-netw 87 | % Rules of thumb for NN selection 88 | % NN is the average between the sizes of the input and output neurons 89 | % NN is between the size of input neurons and size of output neurons 90 | % NN is 2/3 the size of the input neurons plus the size of the output neurons 91 | % NN should be less than twice the size of the input neurons 92 | % NN should be sqrt(input neurons * output neurons) for a 3 layer NN 93 | hiddensizes = ones(1,NL)*NN; 94 | net = fitnet(hiddensizes,'trainscg'); %'trainlm' (faster for a smaller number of samples),'trainscg' (faster for a larger number of samples) 95 | % net.trainParam.epochs = 1e6; % max training iterations 96 | % https://stats.stackexchange.com/questions/181/how-to-choose-the-number-of-hidden-layers-and-nodes-in-a-feedforward-neural-netw 97 | % https://www.mathworks.com/help/deeplearning/ug/choose-a-multilayer-neural-network-training-function.html 98 | % http://www.faqs.org/faqs/ai-faq/neural-nets/part1/preamble.html 99 | net.trainParam.showWindow = 0; 100 | net.divideFcn = 'divideind'; 101 | net.divideParam.trainInd = index_training; %70 102 | net.divideParam.valInd = index_testing(1:ceil(numel(index_testing)/2)); %15 103 | net.divideParam.testInd = index_testing(ceil(numel(index_testing)/2)+1:end); %15 104 | % https://www.mathworks.com/help/deeplearning/ug/divide-data-for-optimal-neural-network-training.html 105 | net.layers{1:end}.transferFcn = 'tansig'; 106 | net.layers{end}.transferFcn = 'purelin'; % https://media.licdn.com/dms/image/C4D12AQGTOQOnaOcXgA/article-inline_image-shrink_400_744/0/1520042651987?e=1683158400&v=beta&t=4_Eh2uBhYFrDLR__esDKCbQ5ji6DETS-ZFKZTYcKAO4 107 | 108 | % Train a ANN for every output 109 | fprintf(' | Train ANNs |\n'); mlstart = tic; 110 | reps_ML_model = cell(reps,1); 111 | reps_TR = cell(reps,1); 112 | repsCorr = zeros(reps,1); 113 | repsMSE = zeros(reps,1); 114 | WaitMessage = parfor_waitbar(reps); %Add ('Waitbar', true) as arguments to turn on a waitbar popup 115 | [ml_index, ~, phys_index] = Indexing('part',micro,micro.ortho); 116 | outnum = 0; for p = 1:size(phys_index,1); for ind = 1:size(ml_index{phys_index(p,1),phys_index(p,2)},1); outnum = outnum + 1; end; end 117 | Yind = zeros(size(Y,1),outnum); an = 1; 118 | Cminvec = zeros(1,outnum); Cmaxvec = zeros(1,outnum); 119 | for p = 1:size(phys_index,1) 120 | for ind = 1:size(ml_index{phys_index(p,1),phys_index(p,2)},1) 121 | % Parse SP_C (Y) Data 122 | for y = 1:size(Y,1) 123 | Yind(y,an) = Y{y}{phys_index(p,1),phys_index(p,2)}(ml_index{phys_index(p,1),phys_index(p,2)}(ind,1),ml_index{phys_index(p,1),phys_index(p,2)}(ind,2)); 124 | end 125 | Cminvec(1,an) = Cmin{phys_index(p,1),phys_index(p,2)}(ml_index{phys_index(p,1),phys_index(p,2)}(ind,1),ml_index{phys_index(p,1),phys_index(p,2)}(ind,2)); 126 | Cmaxvec(1,an) = Cmax{phys_index(p,1),phys_index(p,2)}(ml_index{phys_index(p,1),phys_index(p,2)}(ind,1),ml_index{phys_index(p,1),phys_index(p,2)}(ind,2)); 127 | an = an + 1; 128 | end 129 | end 130 | for n = 1:reps 131 | rng shuffle 132 | % Fit ANN model 133 | tStart = tic; 134 | [ML_model, TR] = train(net,X',Yind','useParallel','yes'); 135 | TR.total_time = toc(tStart); 136 | TR.Cminvec = Cminvec; 137 | TR.Cmaxvec = Cmaxvec; 138 | 139 | % Correlation 140 | TR.train_corr = corrcoef(ML_model(X(TR.trainInd,:)')',Yind(TR.trainInd,:)); 141 | TR.val_corr = corrcoef(ML_model(X(TR.valInd,:)')',Yind(TR.valInd,:)); 142 | TR.test_corr = corrcoef(Cap_ANN(ML_model(X(TR.testInd,:)')',C_lower_HS(X(TR.testInd,1)'),C_upper_HS(X(TR.testInd,1)')),Yind(TR.testInd,:)); 143 | % TR.test_corr = corrcoef(Cap_ANN(ML_model(X(TR.testInd,:)')',Cminvec,Cmaxvec),Yind(TR.testInd,:)); 144 | repsCorr(n) = TR.test_corr(2,1); 145 | 146 | % MSE 147 | TR.train_MSE = TR.best_perf; 148 | TR.val_MSE = TR.best_vperf; 149 | TR.test_MSE = immse(Cap_ANN(ML_model(X(TR.testInd,:)')',C_lower_HS(X(TR.testInd,1)'),C_upper_HS(X(TR.testInd,1)')),Yind(TR.testInd,:)); 150 | % TR.test_MSE = immse(Cap_ANN(ML_model(X(TR.testInd,:)')',Cminvec,Cmaxvec),Yind(TR.testInd,:)); 151 | repsMSE(n) = TR.test_MSE; 152 | 153 | reps_ML_model{n} = ML_model; 154 | reps_TR{n} = TR; 155 | WaitMessage.Send; 156 | end 157 | corr_vecs = [1-repsCorr, transpose(1:numel(repsCorr))]; 158 | corr_vecs = [sortrows(corr_vecs,1), transpose(1:numel(repsCorr))]; % [corr, model #, corr rank] 159 | corr_vecs = sortrows(corr_vecs,2); 160 | 161 | MSE_vecs = [repsMSE, transpose(1:numel(repsMSE))]; 162 | MSE_vecs = [sortrows(MSE_vecs,1), transpose(1:numel(repsMSE))]; % [MSE, model #, MSE rank] 163 | MSE_vecs = sortrows(MSE_vecs,2); 164 | 165 | rank_vecs = [corr_vecs(:,3) + MSE_vecs(:,3), repsMSE, 1-repsCorr, transpose(1:numel(repsMSE))]; % [total rank, MSE, corr, model #] 166 | bestmodel = rank_vecs(rank_vecs(:,1)==min(rank_vecs(:,1)),:); 167 | bestmodel = sortrows(bestmodel,[2,3]); 168 | 169 | ML_model = reps_ML_model{bestmodel(1,4)}; 170 | TR = reps_TR{bestmodel(1,4)}; 171 | WaitMessage.Destroy; 172 | fprintf(' | Done. Elapsed Time: %4.2f seconds. |\n\n',toc(mlstart)); 173 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/alg_GOCM.m: -------------------------------------------------------------------------------- 1 | % GENERALIZED OPTIMALITY CRITERION ALGORITHM 2 | function [Xin, c] = alg_GOCM(FUN,Xin,A,B,Aeq,Beq,LB,UB,nonlcon,options,macro) 3 | % A more general OC implementation. Can handle objective functions and 4 | % sensitivities with positive or negative values. Can handle multiple 5 | % inequality constraints (A, B). Equality constraints (Aeq, Beq) are 6 | % converted to inequality constraints: Aeq*Xin <= Beq 7 | % nonlcon not considered 8 | 9 | % Prepare GOCM 10 | lmid = zeros(options.maxloop+1,size([Aeq; A],1)); 11 | lmid(1,:) = [1, options.lmin*ones(1,size(A,1))]; % initial lagrange multiplier value (eq 11) 12 | A = [Aeq; A]; B = [Beq; B]; % converty equality constraints to inequality constraints 13 | dg = transpose(A./B); % sensitivity of linear, normalized inequality constraints 14 | dg = (macro.den_H*dg)./macro.den_Hs; % apply density filter 15 | change = 1; 16 | loop = 0; 17 | 18 | % Run GOCM 19 | while change > options.tolx && loop < options.maxloop 20 | loop = loop+1; 21 | 22 | % homogenized macroscale objective and derivative 23 | [c, dc] = FUN(Xin); 24 | if loop == 1; c0 = c; end % note initial compliance 25 | dc = dc/c0; % normalize sensitivies 26 | 27 | % GOC update 28 | g = transpose(A*Xin./B - 1); % normalized inequality constraints 29 | if loop == 1; g_change = zeros(size(g)); else; g_change = g-g_old; end % change in inequality constraints 30 | g_old = g; 31 | for i = 1:numel(g) 32 | if (g(i) > 0 && g_change(i) > 0) || (g(i) < 0 && g_change(i) < 0) % when the sign of g and g_change are the same then p0 is positive 33 | p0 = 1.0; 34 | elseif (g(i) > 0 && g_change(i) > -options.tolp) || (g(i) < 0 && g_change(i) < options.tolp) 35 | p0 = 0.5; 36 | else 37 | p0 = 0; 38 | end 39 | lmid(loop+1,i) = lmid(loop,i)*(1 + p0*(g(i) + g_change(i))); 40 | end 41 | lmid(loop+1,:) = min(max(lmid(loop+1,:),options.lmin),options.lmax); 42 | D = Xin.*(-(min(dc,0) + sum(min(lmid(loop+1,:).*dg,0),2))./(max(dc,0) + sum(max(lmid(loop+1,:).*dg,0),2))).^options.eta; 43 | min_1 = min(Xin+options.move, D); % minimum operation 1 44 | min_2 = min([UB, min_1], [], 2); % minimum operation 2 45 | max_1 = max(Xin-options.move, min_2); % maximum operation 1 46 | xnew = max([LB, max_1], [], 2); % maximum operation 2 47 | change = max(abs(xnew-Xin)); 48 | Xin = xnew; 49 | end 50 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/alg_OCM.m: -------------------------------------------------------------------------------- 1 | % OPTIMALITY CRITERION ALGORITHM 2 | function [Xin, c] = alg_OCM(FUN,Xin,A,B,Aeq,Beq,LB,UB,nonlcon,options) 3 | % Not a general OC implementation. Only valid for a positive objective 4 | % function, always negative sensitivities, and one equality constraint. 5 | % The inequality constraints (A, B) are not considered 6 | % nonlcon not considered 7 | 8 | % Prepare OCM 9 | change = 1; 10 | loop = 0; 11 | 12 | % Run OCM 13 | while change > options.tolx && loop < options.maxloop 14 | loop = loop+1; 15 | 16 | % homogenized macroscale objective and derivative 17 | [c, dc] = FUN(Xin); 18 | 19 | % OC update 20 | l1 = options.lmin; l2 = options.lmax; 21 | while (l2-l1)/(l1+l2) > 1e-3 22 | lmid = 0.5*(l2+l1); 23 | D = Xin.*(-dc/lmid).^options.eta; 24 | min_1 = min(Xin+options.move, D); % minimum operation 1 25 | min_2 = min([UB, min_1], [], 2); % minimum operation 2 26 | max_1 = max(Xin-options.move, min_2); % maximum operation 1 27 | xnew = max([LB, max_1], [], 2); % maximum operation 2 28 | if 0 > Beq - Aeq*xnew 29 | l1 = lmid; 30 | else 31 | l2 = lmid; 32 | end 33 | end 34 | change = max(abs(xnew-Xin)); 35 | Xin = xnew; 36 | end 37 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/blendedPolymask.m: -------------------------------------------------------------------------------- 1 | function maskSet = blendedPolymask(curves, xVec, yVec, zVec) 2 | %BLENDEDPOLYMASK Creates a 3D mask "lofted" from a set of polygons 3 | % BW = BLENDEDPOLYMASK(C,X,Y,Z) returns a logical volume of size equal to 4 | % [length(Y) length(X) length(Z)]. C is a cell array of N-by-3 XYZ 5 | % coordinates specifying individual (planar) XY polygons. The first 6 | % Z-coordinate of each polygon in C specifies its planar location. X, Y 7 | % and Z are vectors specifying pixel locations in the 3D output volume. 8 | % The output BW is a "loft" from one polygon to the next. Interpolation 9 | % between polygons is done via the distance function of the masks at each 10 | % neighbouring polygon. The IP toolbox BWDIST function is required. 11 | % 12 | % Example: 13 | % [circXY(:,1),circXY(:,2)] = pol2cart(linspace(0,2*pi,50)', 1); 14 | % sqXY = [-1 -1;1 -1;1 1;-1 1; -1 -1]; 15 | % C = {[sqXY*5 ones(5,1)] % Start with a small square 16 | % [circXY*40 ones(50,1)*30] % Blend to a large circle 17 | % [sqXY*20 ones(5,1)*65] % Blend to a large square 18 | % [circXY*10 ones(50,1)*99]}; % Blend to a small circle 19 | % X = linspace(-40, 40, 200); 20 | % Y = linspace(-40, 40, 200); 21 | % Z = linspace(0, 100, 400); 22 | % BW = blendedPolymask(C,X,Y,Z); 23 | % figure, patch(isosurface(X,Y,Z,BW,0.5),'FaceColor','g','EdgeColor','none','FaceAlpha',0.5) 24 | % view(3), camlight, hold on, axis image 25 | % cellfun(@(x)patch(x(:,1),x(:,2),x(:,3),'b'),C) 26 | % 27 | % See also poly2mask 28 | 29 | % Written by Sven Holcombe 30 | 31 | % Initialise maskSet to the size of the input xVec, yVec, zVecs 32 | lenX = length(xVec); lenY = length(yVec); lenZ = length(zVec); 33 | maskSet = false([lenY lenX lenZ]); 34 | 35 | % For each zVec location, exactly where along "curves" is it placed? (partial indices used) 36 | numCurves = length(curves); 37 | curveZlocations = cellfun(@(xyz)xyz(1,3),curves); 38 | idxsIntoCurves = interp1(curveZlocations, 1:numCurves, zVec(:), 'linear'); 39 | 40 | % For any zSamps outside the range of curves, bring them back to the first/last curve 41 | validSheetNos = find(~isnan(idxsIntoCurves)); 42 | 43 | % For each zVec, get its which are its nearest inferior/superior curve indices? 44 | idxsLowHigh = [floor(idxsIntoCurves) ceil(idxsIntoCurves)]; 45 | [usedIdxs,~,validUsedGrpNo] = unique(idxsLowHigh(validSheetNos,:)); 46 | 47 | % Build BW masks and mask dist maps for each non-empty zLevel 48 | usedBWmasks = arrayfun(@(i)poly2mask(... 49 | interp1(xVec, 1:lenX, curves{i}(:,1),'linear','extrap'),... 50 | interp1(yVec, 1:lenY, curves{i}(:,2),'linear','extrap'),... 51 | lenY, lenX) , usedIdxs, 'UniformOutput',false); 52 | 53 | % For any curveZlocations exactly hitting a usedBWmask, use it directly 54 | directCopySlices = find(diff(idxsLowHigh,[],2)==0); % Idxs into "usedIdxs" 55 | for i = 1:length(directCopySlices) 56 | maskSet(:,:,directCopySlices(i)) = usedBWmasks{validUsedGrpNo(validSheetNos==directCopySlices(i))}; 57 | end 58 | 59 | % Loop through remaining levels. If a pixel is closer to an "on" 60 | % neighbour than an "off" neighbour, it gets turned on. 61 | for i = setdiff(validSheetNos(:)', directCopySlices) 62 | twoDists = abs(idxsLowHigh(i,:) - idxsIntoCurves(i)); 63 | twoInds = [find(usedIdxs==idxsLowHigh(i,1)) find(usedIdxs==idxsLowHigh(i,2))]; 64 | BW1 = usedBWmasks{twoInds(1)}; BW2 = usedBWmasks{twoInds(2)}; 65 | scaledDist1 = (bwdist(BW1) - bwdist(~BW1)) * twoDists(2); 66 | scaledDist2 = (bwdist(BW2) - bwdist(~BW2)) * twoDists(1); 67 | maskSet(:,:,i) = scaledDist1+scaledDist2 <= 0; 68 | % BELOW SHOWS COMPARISONS BETWEEN BW1, BW2, and interpolated result 69 | % bb1 = bwboundaries(BW1,'noholes'); bb2 = bwboundaries(BW2,'noholes'); bb = bwboundaries(maskSet(:,:,i),'noholes'); 70 | % figure, plot(bb1{1}(:,2),bb1{1}(:,1),'r',bb2{1}(:,2),bb2{1}(:,1),'b',bb{1}(:,2),bb{1}(:,1),':g','LineWidth',2) 71 | % title(sprintf('Rdist=%f, Bdist=%f',twoDists)), axis image; uiwait 72 | end 73 | -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/density_grad.m: -------------------------------------------------------------------------------- 1 | % MACROSCALE DENSITY GRADING CONSTRAINT 2 | function [A, b] = density_grad(macro) 3 | if macro.delta == 1 4 | A = []; 5 | b = []; 6 | else 7 | Dmax = 1/macro.delta; % constraint range is greater than 1 8 | Dmax = 1; % constraint range is only immediately adjacent elemnts (e.g., NSEW in 2D) 9 | xNums = reshape(1:macro.nele,[macro.nelx, macro.nely, max(macro.nelz,1)]); % element nums at point a 10 | 11 | theta1 = []; theta2 = []; 12 | for e = 1:macro.nele 13 | dists = bwdist(xNums==e); 14 | theta1 = [theta1; e*ones(size(xNums(dists<=Dmax)))]; 15 | theta2 = [theta2; xNums(dists<=Dmax)]; 16 | end 17 | A = sparse(transpose(1:numel(theta1(:))), theta1(:), ones(size(theta1(:))), numel(theta1(:)), macro.nele) +... % Theta_j 18 | sparse(transpose(1:numel(theta2(:))), theta2(:), -ones(size(theta2(:))), numel(theta2(:)), macro.nele); % -Theta_k 19 | 20 | A = A(any(A,2),:); % Theta_j - Theta_k 21 | b = ones(size(A,1),1)*macro.delta; 22 | end 23 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/display_macro.m: -------------------------------------------------------------------------------- 1 | % DISPLAY MACROSCALE OPTIMIZATION SETTINGS 2 | function display_macro(macro) 3 | fprintf(' Macroscale Optimization Settings:\n'); 4 | fprintf('==========================================================\n'); 5 | fprintf([' Problem name: ' macro.macro_bc_name '\n']); 6 | if macro.dim == 2 7 | fprintf(' Mesh Resolution: nelx = %3d, nely = %3d\n',macro.nelx,macro.nely); 8 | elseif macro.dim == 3 9 | fprintf(' Mesh Resolution: nelx = %3d, nely = %3d, nelz = %3d\n',macro.nelx,macro.nely,macro.nelz); 10 | end 11 | if macro.phys == 1 12 | fprintf(' Physics mode: Mechanical\n'); 13 | end 14 | fprintf(' Volume Fraction: vf = %1.2f\n',macro.volfrac); 15 | fprintf(' Density Filter Radius: rmin = %1.2f\n',macro.flt_den_min); 16 | fprintf(' Sensitivity Filter Radius: rmin = %1.2f\n',macro.flt_sen_min); 17 | fprintf(' Density Grading limit: delta = %1.2f\n',macro.delta); 18 | fprintf(' Theta bounds: [%1.2f %1.2f]\n',macro.x_lb(1),macro.x_ub(1)); 19 | fprintf(' Volume Fraction Cutoff for Interpolation: vf = %1.2f\n',macro.vf_cutoff); 20 | if macro.alg == 1 21 | fprintf(' Algorithm: MATLAB''s fmincon\n'); 22 | elseif macro.alg == 2 23 | fprintf(' Algorithm: Optimality Criterion\n'); 24 | elseif macro.alg == 3 25 | fprintf(' Algorithm: Generalized Optimality Criterion\n'); 26 | end 27 | fprintf(' Loops: %3d, Tolerance: %1.1e\n\n',macro.maxloop,macro.tolx); 28 | 29 | fprintf(' Macroscale De-homogenization Settings:\n'); 30 | fprintf('==========================================================\n'); 31 | if macro.dehom == 0 32 | fprintf(' No de-homogenization. \n'); 33 | elseif macro.dehom == 1 34 | fprintf(' TOM de-homogenization. \n'); 35 | fprintf(' TOM mesh resolution scale factor: %1.2f\n',macro.Micro_scaler); 36 | fprintf(' Unit cell size: epi = %1.2f\n',macro.epi); 37 | fprintf(' Density threshold for post-processing: %1.2f\n',macro.den_threshold); 38 | if macro.PP_tri_infill 39 | fprintf(' Apply triangular infill connectivity post process: True\n'); 40 | else 41 | fprintf(' Apply triangular infill connectivity post process: False\n'); 42 | end 43 | if macro.PP_rm_skel 44 | fprintf(' Apply skeletonization post process: True\n'); 45 | else 46 | fprintf(' Apply skeletonization post process: False\n'); 47 | end 48 | end 49 | 50 | if macro.dehom ~= 0 51 | fprintf(' Number of remove low strain energy solid iterations: %1d\n',macro.PP_rm_low_SE); 52 | if macro.PP_rm_skel 53 | fprintf(' Calculate final compliance and compatibility: True\n'); 54 | else 55 | fprintf(' Calculate final compliance and compatibility: False\n'); 56 | end 57 | end 58 | fprintf('\n'); 59 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/display_micro.m: -------------------------------------------------------------------------------- 1 | % DISPLAY MICROSCALE OPTIMIZATION SETTINGS 2 | function display_micro(micro) 3 | fprintf(' Microscale Optimization Settings:\n'); 4 | fprintf('==========================================================\n'); 5 | if micro.bc_type == 1 6 | fprintf(' Boundary Condition: Force-driven\n'); 7 | elseif micro.bc_type == 2 8 | fprintf(' Boundary Condition: Displacement-driven\n'); 9 | end 10 | if micro.dim == 2 11 | fprintf(' Mesh Resolution: nelx = %3d, nely = %3d\n',micro.nelx,micro.nely); 12 | elseif micro.dim == 3 13 | fprintf(' Mesh Resolution: nelx = %3d, nely = %3d, nelz = %3d\n',micro.nelx,micro.nely,micro.nelz); 14 | end 15 | if micro.phys == 1 16 | fprintf(' Physics mode: Mechanical\n'); 17 | elseif micro.phys == 2 18 | fprintf(' Physics mode: Thermal\n'); 19 | elseif all(micro.phys == [1 2]) 20 | fprintf(' Physics mode: Thermomechanical\n'); 21 | end 22 | if micro.cont 23 | fprintf(' Penalization: p = %1d, Continuation: On\n',micro.penal); 24 | else 25 | fprintf(' Penalization: p = %1d, Continuation: Off\n',micro.penal); 26 | end 27 | fprintf(' Density Filter Radius: rmin = %1.2f\n',micro.flt_den_min); 28 | fprintf(' Sensitivity Filter Radius: rmin = %1.2f\n',micro.flt_sen_min); 29 | fprintf(' Theta bounds: [%1.2f %1.2f]\n',micro.x_lb(1),micro.x_ub(1)); 30 | fprintf(' Weight bounds: [%1.2f %1.2f]\n',micro.x_lb(2),micro.x_ub(2)); 31 | fprintf(' Algorithm: Optimality Criterion\n'); 32 | fprintf(' Loops: %3d, Tolerance: %1.1e\n\n',micro.maxloop,micro.tolx); 33 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/display_top.m: -------------------------------------------------------------------------------- 1 | % DISPLAY 2D/3D TOPOLOGIES 2 | function display_top(rho,threshold,view_name) 3 | % rho: input 2D/3D array for topologies [0, 1] 4 | % threshold: plotting threshold for rho elements (always used for 3D plots, has to be user-defined for 2D plots) 5 | % view_name: camera view for 3D only (iso, top, bottom, front, back, left, right) 6 | 7 | %% MANAGE INPUT ARGUMENTS 8 | if nargin == 1 9 | threshold = 0.5; % default 50% plotting threshold 10 | view_name = 'iso'; 11 | elseif nargin == 2 12 | if isstring(threshold) || ischar(threshold) % 2nd input is view_name, threshold not defined 13 | view_name = threshold; 14 | if size(rho,3) == 1 15 | threshold = 0.5; % default 50% plotting threshold for 2D 16 | else 17 | threshold = mean(rho(:)); % default 50% plotting threshold for 3D 18 | end 19 | else % 2nd input is not view_name and assumed to be the threshold 20 | view_name = 'iso'; 21 | end 22 | end 23 | 24 | %% PLOT 2D AND 3D TOPOLOGIES 25 | if size(rho,3) == 1 % 2D plot 26 | if nargin > 1 % threshold is only used for 2D plots that specify it. Otherwise no threshold is used for 2D plots 27 | rho = rho > threshold; 28 | end 29 | colormap(gray); imagesc(1-rho,[0, 1]); caxis([0 1]); 30 | axis equal; axis off; axis tight; box on; view([-90,90]); drawnow; 31 | elseif sum(sum(sum(rho > threshold))) ~= 0 % 3D plot (rho > threshold cannot be all zeros) 32 | patch = PATCH_3Darray(rho > threshold); % from https://www.mathworks.com/matlabcentral/fileexchange/28497-plot-a-3d-array-using-patch?s_tid=ta_fx_results\ 33 | patch.FaceColor = ones(1,3)*0.85; % gray faces 34 | patch.EdgeColor = ones(1,3)*0.1; % black edges 35 | camlight('headlight') 36 | axis equal; axis off; axis tight; box on; 37 | if strcmp(view_name ,'top') 38 | view([0,90]); 39 | elseif strcmp(view_name ,'bottom') 40 | view([0,-90]); 41 | elseif strcmp(view_name ,'front') 42 | view([0,0]); 43 | elseif strcmp(view_name ,'back') 44 | view([180,0]); 45 | elseif strcmp(view_name ,'right') 46 | view([90,0]); 47 | elseif strcmp(view_name ,'left') 48 | view([-90,0]); 49 | else % default to iso view 50 | view([20,25]); 51 | end 52 | drawnow; 53 | end 54 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/ini_design.m: -------------------------------------------------------------------------------- 1 | % PREPARE INITIAL DESIGN 2 | function x0 = ini_design(scale,vf,lb,ub) 3 | beta = ub - lb; % range of distribution [0,1] 4 | 5 | if scale.design_ini == 1 % Uniformly Distributed Volume Fraction Design 6 | x0 = ones(scale.nelx,scale.nely,max(1,scale.nelz))*vf; 7 | elseif scale.design_ini == 2 % Normally Distributed Volume Fraction Design 8 | if vf < 0.5 9 | a = 0.0; b = 2*(vf - 0); 10 | elseif vf == 0.5 11 | a = 0.0; b = 1.0; 12 | elseif vf > 0.5 13 | a = 1 - 2*(1 - vf); b = 1.0; 14 | end 15 | range = ((b - a)/2)*(1 - beta); a = a + range; b = b - range; 16 | x0 = normrnd(vf,((b-a)/2)/4,scale.nelx,scale.nely,max(1,scale.nelz)); % Set to standard deviation of 4 (i.e., 99.994% of cells are within range) 17 | x0(x0b) = b; 19 | elseif scale.design_ini == 3 % Randomly Distributed Volume Fraction Design 20 | if vf < 0.5 21 | a = 0.0; b = 2*(vf - 0); 22 | elseif vf == 0.5 23 | a = 0.0; b = 1.0; 24 | elseif vf > 0.5 25 | a = 1 - 2*(1 - vf); b = 1.0; 26 | end 27 | range = ((b - a)/2)*(1 - beta); a = a + range; b = b - range; 28 | x0 = a + rand(scale.nelx,scale.nely,max(1,scale.nelz)).*(b-a); 29 | end 30 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/mesh_info.m: -------------------------------------------------------------------------------- 1 | % ADD MESH INFORMATION TO A SCALE'S STRUCTURE 2 | function scale = mesh_info(scale) 3 | scale.nele = max(scale.nelx*scale.nely,scale.nelx*scale.nely*scale.nelz); % number of elements 4 | if scale.dim == 2 5 | scale.nen = 4; % number of nodes per element 6 | elseif scale.dim == 3 7 | scale.nen = 8; % number of nodes per element 8 | end 9 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/opt_proj.m: -------------------------------------------------------------------------------- 1 | function abs_error = opt_proj(scaler, vf, scale, Mesh_line_data) 2 | Mesh_line_data(:,9:10) = Mesh_line_data(:,9:10)*scaler; 3 | Mesh_line_data(Mesh_line_data(:,9) < scale.thick_tol,9) = 0; Mesh_line_data(Mesh_line_data(:,10) < scale.thick_tol,10) = 0; % identify thin lattices 4 | Mesh_line_data(any(Mesh_line_data(:,9:10) < scale.thick_tol,2),:) = []; % remove thin lattices 5 | [Mesh_uni,~,ii] = unique(Mesh_line_data(:,1:2)); % identify disconnected lattices 6 | Mesh_line_data(any(Mesh_line_data(:,1) == Mesh_uni(accumarray(ii,1).' == 1)',2),:) = []; % remove disconnected lattices 7 | Mesh_line_data(any(Mesh_line_data(:,2) == Mesh_uni(accumarray(ii,1).' == 1)',2),:) = []; % remove disconnected lattices 8 | xProj = Project_SDF(scale, Mesh_line_data); 9 | abs_error = abs(mean(xProj(:)) - vf); 10 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/output_plot.m: -------------------------------------------------------------------------------- 1 | % OUTPUT ITERATION INFO AND PLOTS 2 | function output_plot(xMacro,xWeight,macro,xMicro,c,loop,change) 3 | 4 | %% PRINT RESULTS 5 | fprintf(' | Obj.:%.4e Vol.:%7.3f ch.:%7.3f |\n\n',c,mean(xMacro(:)),change); 6 | 7 | %% PLOT RESULTS 8 | if macro.displayflag 9 | xMacro = reshape(xMacro,[macro.nelx, macro.nely, max(macro.nelz,1)]); 10 | figure(1) 11 | clf; 12 | display_top(real(xMacro)); drawnow; 13 | 14 | if macro.alg ~= 1 15 | figure(2) 16 | h = findobj(gca,'Type','line'); 17 | if ~isempty(h) 18 | plot([h.XData, loop], [h.YData, c],'LineStyle','none','MarkerEdgeColor','k','MarkerFaceColor','m','Marker','diamond'); xlabel('Iterations'); ylabel('Function value'); drawnow; 19 | else 20 | plot(loop, c,'LineStyle','none','MarkerEdgeColor','k','MarkerFaceColor','m','Marker','diamond'); xlabel('Iterations'); ylabel('Function value'); drawnow; 21 | end 22 | end 23 | 24 | if all(macro.phys == [1 2]) 25 | xWeight = reshape(xWeight,[macro.nelx, macro.nely, max(macro.nelz,1)]); 26 | figure(4) 27 | clf; 28 | display_obj(xWeight); drawnow; 29 | end 30 | 31 | if ~isempty(cell2mat(xMicro)) 32 | figure(3) 33 | clf; 34 | display_top(cell2mat(reshape(xMicro,[macro.nelx, macro.nely, max(macro.nelz,1)]))); drawnow; 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/parfor_waitbar.m: -------------------------------------------------------------------------------- 1 | classdef parfor_waitbar < handle 2 | %This class creates a waitbar or message when using for or parfor. 3 | %Required Input: 4 | %TotalMessage: N in "i = 1: N". 5 | %Optional Inputs: 6 | %'Waitbar': true of false (default). If true, this class creates a 7 | % waitbar. 8 | %'FileName': 'screen' or a char array. If 'screen', print the message 9 | % on screen; otherwise, save the message in the file 10 | % named 'FileName'. 11 | %'ReportInterval': 1x1. Report at every i is costly. This number 12 | % defines the interval for reporting. 13 | %%To use this class, one needs to call the class right before the loop: 14 | %N = 1000; 15 | %WaitMessage = parfor_waitbar(N); 16 | %%Call "Send" method in the loop. 17 | %for i = 1: N 18 | % WaitMessage.Send; 19 | % pause(0.5); 20 | %end 21 | %%Delete the obj after the loop. 22 | %WaitMessage.Destroy; 23 | %Copyright (c) 2019, Yun Pu 24 | properties (SetAccess = private) 25 | NumMessage; %Number of messages received from the workers. 26 | TotalMessage; %Number of total messages. 27 | Waitbar; %If waitbar is true, create a waitbar; otherwise, save the message in a file. 28 | FileName; %If FileName = 'screen', the current message does not save in a file. 29 | StartTime 30 | UsedTime_1; %Time at last step. 31 | WaitbarHandle; 32 | ReportInterval; 33 | FileID; 34 | DataQueueHandle; 35 | bar0; 36 | bar10; 37 | bar20; 38 | bar30; 39 | bar40; 40 | bar50; 41 | bar60; 42 | bar70; 43 | bar80; 44 | bar90; 45 | bar100; 46 | end 47 | 48 | methods 49 | function Obj = parfor_waitbar(TotalMessage, varargin) 50 | Obj.DataQueueHandle = parallel.pool.DataQueue; 51 | Obj.StartTime = tic; 52 | Obj.NumMessage = 0; 53 | Obj.UsedTime_1 = Obj.StartTime; 54 | Obj.TotalMessage = TotalMessage; 55 | Obj.bar0 = 0; 56 | Obj.bar10 = 0; 57 | Obj.bar20 = 0; 58 | Obj.bar30 = 0; 59 | Obj.bar40 = 0; 60 | Obj.bar50 = 0; 61 | Obj.bar60 = 0; 62 | Obj.bar70 = 0; 63 | Obj.bar80 = 0; 64 | Obj.bar90 = 0; 65 | Obj.bar100 = 0; 66 | InParser = inputParser; 67 | addParameter(InParser,'Waitbar',false,@islogical); 68 | addParameter(InParser,'FileName', 'screen', @ischar); 69 | addParameter(InParser,'ReportInterval', ceil(TotalMessage/100), @isnumeric); 70 | parse(InParser, varargin{:}) 71 | Obj.Waitbar = InParser.Results.Waitbar; 72 | Obj.FileName = InParser.Results.FileName; 73 | Obj.ReportInterval = InParser.Results.ReportInterval; 74 | if Obj.Waitbar 75 | Obj.WaitbarHandle = waitbar(0, [num2str(0), '%'], 'Resize', true); 76 | end 77 | switch Obj.FileName 78 | case 'screen' 79 | otherwise 80 | Obj.FileID = fopen(Obj.FileName, 'w'); 81 | end 82 | afterEach(Obj.DataQueueHandle, @Obj.Update); 83 | end 84 | function Send(Obj) 85 | send(Obj.DataQueueHandle, 0); 86 | end 87 | function Destroy(Obj) 88 | if Obj.Waitbar 89 | delete(Obj.WaitbarHandle); 90 | end 91 | delete(Obj.DataQueueHandle); 92 | delete(Obj); 93 | end 94 | end 95 | 96 | methods (Access = private) 97 | function Obj = Update(Obj, ~) 98 | Obj.AddOne; 99 | if mod(Obj.NumMessage, Obj.ReportInterval) 100 | return 101 | end 102 | if Obj.Waitbar 103 | Obj.WaitbarUpdate; 104 | else 105 | Obj.FileUpdate; 106 | end 107 | end 108 | 109 | function WaitbarUpdate(Obj) 110 | UsedTime_now = toc(Obj.StartTime); 111 | EstimatedTimeNeeded = (UsedTime_now-Obj.UsedTime_1)/Obj.ReportInterval*(Obj.TotalMessage-Obj.NumMessage); 112 | waitbar(Obj.NumMessage/Obj.TotalMessage, Obj.WaitbarHandle, [num2str(Obj.NumMessage/Obj.TotalMessage*100, '%.2f'), '%; ', num2str(UsedTime_now, '%.2f'), 's used and ', num2str(EstimatedTimeNeeded, '%.2f'), 's needed.']); 113 | Obj.UsedTime_1 = UsedTime_now; 114 | end 115 | 116 | function FileUpdate(Obj) 117 | UsedTime_now = toc(Obj.StartTime); 118 | EstimatedTimeNeeded = (UsedTime_now-Obj.UsedTime_1)/Obj.ReportInterval*(Obj.TotalMessage-Obj.NumMessage); 119 | switch Obj.FileName 120 | case 'screen' 121 | % fprintf('%.2f%%; %.2fs used and %.2fs needed...\n', Obj.NumMessage/Obj.TotalMessage*100, UsedTime_now, EstimatedTimeNeeded); 122 | if Obj.NumMessage/Obj.TotalMessage*100 > 0 && Obj.bar0 == 0 123 | fprintf(' |='); 124 | Obj.bar0 = 1; 125 | end 126 | if Obj.NumMessage/Obj.TotalMessage*100 > 10 && Obj.bar10 == 0 127 | fprintf('====='); 128 | Obj.bar10 = 1; 129 | end 130 | if Obj.NumMessage/Obj.TotalMessage*100 > 20 && Obj.bar20 == 0 131 | fprintf('====='); 132 | Obj.bar20 = 1; 133 | end 134 | if Obj.NumMessage/Obj.TotalMessage*100 > 30 && Obj.bar30 == 0 135 | fprintf('====='); 136 | Obj.bar30 = 1; 137 | end 138 | if Obj.NumMessage/Obj.TotalMessage*100 > 40 && Obj.bar40 == 0 139 | fprintf('====='); 140 | Obj.bar40 = 1; 141 | end 142 | if Obj.NumMessage/Obj.TotalMessage*100 > 50 && Obj.bar50 == 0 143 | fprintf('====='); 144 | Obj.bar50 = 1; 145 | end 146 | if Obj.NumMessage/Obj.TotalMessage*100 > 60 && Obj.bar60 == 0 147 | fprintf('====='); 148 | Obj.bar60 = 1; 149 | end 150 | if Obj.NumMessage/Obj.TotalMessage*100 > 70 && Obj.bar70 == 0 151 | fprintf('====='); 152 | Obj.bar70 = 1; 153 | end 154 | if Obj.NumMessage/Obj.TotalMessage*100 > 80 && Obj.bar80 == 0 155 | fprintf('====='); 156 | Obj.bar80 = 1; 157 | end 158 | if Obj.NumMessage/Obj.TotalMessage*100 > 90 && Obj.bar90 == 0 159 | fprintf('====='); 160 | Obj.bar90 = 1; 161 | end 162 | if Obj.NumMessage/Obj.TotalMessage*100 == 100 && Obj.bar100 == 0 163 | fprintf('=====|\n'); 164 | Obj.bar100 = 1; 165 | end 166 | otherwise 167 | fprintf(Obj.FileID, '%.2f%%; %.2fs used and %.2fs needed...\n', Obj.NumMessage/Obj.TotalMessage*100, UsedTime_now, EstimatedTimeNeeded); 168 | end 169 | Obj.UsedTime_1 = UsedTime_now; 170 | end 171 | function AddOne(Obj) 172 | Obj.NumMessage = Obj.NumMessage + 1; 173 | end 174 | end 175 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/patchJobStorageLocation.m: -------------------------------------------------------------------------------- 1 | function patchJobStorageLocation 2 | % This function should be called before any PCT code is executed in a MATLAB session 3 | % (perhaps by a suitable startup.m file at MATLAB startup?). It will iterate over all 4 | % known profiles for the current user, and if they have a JobStorageLocation property 5 | % it will append the current process PID. This ensures that on a single machine all 6 | % JobStorageLocation's for the same scheduler will end up using different folders, which 7 | % allows them to all submit jobs at exactly the same time, without potential file name 8 | % clashes that would otherwise arise. NOTE that you will need to set the JobStorageLocation 9 | % of any subsequent MATLAB by hand if you want to retrieve the results of a job in about 10 | % different MATLAB session. Finally, if you want to make JobStorageLocation's unique 11 | % across multiple computers (rather than just within one computer) then it would be best 12 | % to change the uniqueEndingStr below to call 'tempname' and extract the final part. 13 | % That will ensure that even with PID reuse across multiple machines you still get about 14 | % unique string for each MATLAB. 15 | 16 | % Copyright 2016 The MathWorks, Inc. 17 | 18 | % https://www.mathworks.com/matlabcentral/answers/545174-how-can-i-work-around-a-race-condition-on-a-parallel-computing-job-storage-location 19 | 20 | try 21 | % Check to see if PCT is installed. If not simply return early as there 22 | % is nothing to do. 23 | if ~exist('parpool','file') 24 | return ; 25 | end 26 | 27 | % Make sure that this can run in normal MATLAB as well as deployed MCR's. 28 | % Some of the code below checks that we are in a deployed (or overriden) 29 | % MATLAB, so do this first. 30 | if ~(isdeployed || parallel.internal.settings.qeDeployedOverride) 31 | parallel.internal.settings.qeDeployedOverride(true); 32 | end 33 | 34 | % Using parallel.Settings find the scheduler component for each profile 35 | S = parallel.Settings; 36 | profiles = S.findProfile; 37 | 38 | % Add the PID of this process to each of the JobStorageLocations. Obviously 39 | % if there are multiple machines which end up with the same PID there might 40 | % be problems. Alternatively use 'tempname' and strip off the beginning. 41 | uniqueEndingStr = num2str(feature('getpid')); 42 | 43 | for index = 1:numel(profiles) 44 | 45 | sc = profiles(index).getSchedulerComponent; 46 | 47 | % Check if the scheduler component has a JobStorageLocation property that 48 | % we need to append to. If not loop to the next scheduler component. 49 | if ~isprop(sc, 'JobStorageLocation') 50 | continue 51 | end 52 | 53 | % Get the value at the user level (in case you've already called this 54 | % function once and set the value at the session level) 55 | baseDir = get(sc, 'JobStorageLocation', 'user'); 56 | 57 | % If it isn't a string we will create a specific local cluster which will 58 | % have the correct default location and append to that instead 59 | if ~ischar( baseDir ) 60 | l = parallel.cluster.Local; 61 | baseDir = l.JobStorageLocation; 62 | end 63 | 64 | dataLoc = fullfile(baseDir, uniqueEndingStr); 65 | 66 | % Directory must exist - NOTE we should probably think about getting 67 | % rid of this folder when this MATLAB exits? But only if there isn't 68 | % work still running. 69 | if ~exist(dataLoc, 'dir') 70 | mkdir (dataLoc); 71 | end 72 | 73 | % Importantly, we are only making the change to the settings at a session 74 | % level so that when we restart the system 75 | set(sc, 'JobStorageLocation', dataLoc, 'session') 76 | end 77 | 78 | catch 79 | 80 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/save_results.m: -------------------------------------------------------------------------------- 1 | % SAVE ML-MSTO RESULTS 2 | function foutput = save_results(foutput,var,fig_name,macro,micro) 3 | % Initialize Call: foutput = save_results([],[],[],macro,micro) % Creates output folder and initial save file 4 | % Save Variable Call: save_results(foutput,var,[],[],[]) 5 | % Save Figure Call: save_results(foutput,[],fig_name,[],[]) 6 | % Save Variable and Figure Call: save_results(foutput,var,fig_name,[],[]) 7 | 8 | %% CREATE OUTPUT FOLDER 9 | if isempty(foutput) && isempty(micro) % ssto only (no micro) 10 | if all(macro.phys == 1) 11 | physshort = 'Mecha'; 12 | elseif all(macro.phys == 2) 13 | physshort = 'Therm'; 14 | elseif all(macro.phys == 3) 15 | physshort = 'Fluid'; 16 | elseif all(macro.phys == [1 2]) 17 | physshort = 'ThMec'; 18 | elseif all(macro.phys == [2 3]) 19 | physshort = 'ThFlu'; 20 | end 21 | % Output Folder Name 22 | existing_folder = dir(['Results/' 'SSTO_' physshort '_' num2str(macro.dim) 'D_*']); 23 | if numel(existing_folder) == 0 24 | fnum = 1; zeros_num = '00'; 25 | else 26 | folder_num = cell(numel(existing_folder),1); 27 | for i = 1:numel(existing_folder) 28 | folder_num{i} = existing_folder(i).name; 29 | folder_num{i} = str2double(folder_num{i}(15:17)); 30 | end 31 | fnum = max(cell2mat(folder_num))+1; 32 | if length(int2str(fnum)) == 1 33 | zeros_num = '00'; 34 | elseif length(int2str(fnum)) == 2 35 | zeros_num = '0'; 36 | elseif length(int2str(fnum)) == 3 37 | zeros_num = []; 38 | end 39 | end 40 | foutput = ['SSTO_' physshort '_' num2str(macro.dim) 'D_' zeros_num num2str(fnum) '_(nelx' num2str(macro.nelx) '_nely' num2str(macro.nely) '_nelz' num2str(macro.nelz) '_VF' num2str(macro.volfrac) '_denmin' num2str(macro.flt_den_min) '_senmin' num2str(macro.flt_sen_min) '_del' num2str(macro.delta)]; 41 | if ~exist([cd '/Results/' foutput], 'dir') 42 | mkdir([cd '/Results/' foutput]); 43 | end 44 | % Save Initial Output File 45 | save(['Results/' foutput '/MSTO_Output.mat'],'macro','micro','-v7.3'); 46 | elseif isempty(foutput) 47 | if all(micro.phys == 1) 48 | physshort = 'Mecha'; 49 | elseif all(micro.phys == 2) 50 | physshort = 'Therm'; 51 | elseif all(micro.phys == 3) 52 | physshort = 'Fluid'; 53 | elseif all(micro.phys == [1 2]) 54 | physshort = 'ThMec'; 55 | elseif all(micro.phys == [2 3]) 56 | physshort = 'ThFlu'; 57 | end 58 | % Output Folder Name 59 | existing_folder = dir(['Results/' 'MSTO_' physshort '_' num2str(micro.dim) 'D_*']); 60 | if numel(existing_folder) == 0 61 | fnum = 1; zeros_num = '00'; 62 | else 63 | folder_num = cell(numel(existing_folder),1); 64 | for i = 1:numel(existing_folder) 65 | folder_num{i} = existing_folder(i).name; 66 | folder_num{i} = str2double(folder_num{i}(15:17)); 67 | end 68 | fnum = max(cell2mat(folder_num))+1; 69 | if length(int2str(fnum)) == 1 70 | zeros_num = '00'; 71 | elseif length(int2str(fnum)) == 2 72 | zeros_num = '0'; 73 | elseif length(int2str(fnum)) == 3 74 | zeros_num = []; 75 | end 76 | end 77 | foutput = ['MSTO_' physshort '_' num2str(macro.dim) 'D_' zeros_num num2str(fnum) '_(nelx' num2str(macro.nelx) '_nely' num2str(macro.nely) '_nelz' num2str(macro.nelz) '_VF' num2str(macro.volfrac) '_denmin' num2str(macro.flt_den_min) '_senmin' num2str(macro.flt_sen_min) '_del' num2str(macro.delta)]; 78 | if ~exist([cd '/Results/' foutput], 'dir') 79 | mkdir([cd '/Results/' foutput]); 80 | end 81 | % Save Initial Output File 82 | save(['Results/' foutput '/MSTO_Output.mat'],'macro','micro','-v7.3'); 83 | end 84 | 85 | %% SAVE MSTO VARIABLES 86 | if ~isempty(var) 87 | S.(inputname(2)) = var; 88 | save(['Results/' foutput '/MSTO_Output.mat'],'-append','-struct','S'); 89 | end 90 | 91 | %% SAVE MATLAB FIGURE 92 | if ~isempty(fig_name) 93 | % print(['Results/' foutput '/' fig_name '.png'], '-dpng', '-r900'); 94 | end 95 | end -------------------------------------------------------------------------------- /ML_MSTO_Optimizer_v5_1_release/setup_parpool.m: -------------------------------------------------------------------------------- 1 | % SETUP PARALLEL POOL 2 | function setup_parpool() 3 | pc = parcluster('local'); %process based 4 | dpc = pc.JobStorageLocation; 5 | if ~isempty(getenv('SLURM_CPUS_ON_NODE')) 6 | rmdir(dpc,'s') 7 | delete(gcp('nocreate')) 8 | parpool(pc,str2double(getenv('SLURM_CPUS_ON_NODE')),'IdleTimeout',480); 9 | else 10 | if ~ispc 11 | rmdir(dpc,'s') 12 | delete(gcp('nocreate')) 13 | parpool(pc,feature('numcores'),'IdleTimeout',480); 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ml-msto 2 | Machine learning-based multiscale topology optimization 3 | 4 | By: Joel Najmon and Andres Tovar 5 | 6 | This is a MATLAB code repository for the machine learning-based multiscale topology optimization (ML-MSTO) method proposed in the 2023 journal paper: "Multiscale Topology Optimization via Artificial Neural Networks and Displacement-driven Toplogy-optimized Microstructures" submitted to Computers & Structures in November 2023. The paper is currently under review. 7 | 8 | Only lines 7-23 of the MAIN_v5_1_ML_MSTO_Optimizer.m file need to be modified to run the examples. If you wish to skip the computationally expensive de-homogenization step, change line 30 from 'macro.dehom = 1;' to 'macro.dehom = 0;'. Results from the program are found in the 'Results' folder. 9 | 10 | Note that there is a typo in the manuscript regarding the delta value for the 3D density-graded and infilled design cases. The delta value for these 3D design cases is delta = 0.9 (i.e., it is not the same delta value of delta = 0.5 used in the 2D density-graded and infilled design cases). This correction is reflected in the 3D example instructions below. 11 | 12 | Below are instructions on how to run reduced fidelity versions of the 2D and 3D examples found in the submitted manuscript (simply copy and paste the following lines over lines 7-23). 13 | \ 14 | \ 15 | 2D Cantilever Beam (fixed design case): %\ 16 | macro.nelx = 60; %\ 17 | macro.nely = 30; %\ 18 | macro.nelz = 0; if macro.nelz == 0; macro.dim = 2; else; macro.dim = 3; end % set to 0 if 2D %\ 19 | macro.volfrac = 0.50; %\ 20 | macro.cont = 0; % Continuation: 1 = Yes, 0 = No (Untested) %\ 21 | macro.flt_den_min = 1.0; % density filter minimum radius (flt_den_min <= 1 is off) %\ 22 | macro.flt_sen_min = 3.0; % sensitivity filter minimum radius (flt_sen_min <= 1 is off) %\ 23 | macro.delta = 0; % Maximum allowable limit in density between adjacent macroscale elements. [0 1] (1 = no constraint), (0 = fixed design) %\ 24 | macro.x_lb(1) = 0; macro.x_ub(1) = 1; % bounds for the density design %\ 25 | macro.x_lb(2) = 0; macro.x_ub(2) = 1; % bounds for the weight design %\ 26 | macro.h_fd = 0; % SA via ANN: h_fd = 0; SA via CFD: h_fd = [0.001, 0.001] (perturbation size for density and weight design variables) %\ 27 | macro.vf_cutoff = 0.1; % cutoff volume fraction for CH interpolation %\ 28 | 29 | macro.alg = 3; % alg=1: fmincon, alg=2: OC, alg=3: GOC, alg=4: MMA %\ 30 | macro.maxloop = 100; % Maximum number of iterations %\ 31 | macro.tolx = 1e-3; % Termination criterion %\ 32 | macro.displayflag = 1; % Display structure actively flag %\ 33 | \ 34 | \ 35 | 2D Cantilever Beam (density-graded design case): %\ 36 | macro.nelx = 60; %\ 37 | macro.nely = 30; %\ 38 | macro.nelz = 0; if macro.nelz == 0; macro.dim = 2; else; macro.dim = 3; end % set to 0 if 2D %\ 39 | macro.volfrac = 0.50; %\ 40 | macro.cont = 0; % Continuation: 1 = Yes, 0 = No (Untested) %\ 41 | macro.flt_den_min = 1.0; % density filter minimum radius (flt_den_min <= 1 is off) %\ 42 | macro.flt_sen_min = 3.0; % sensitivity filter minimum radius (flt_sen_min <= 1 is off) %\ 43 | macro.delta = 0.5; % Maximum allowable limit in density between adjacent macroscale elements. [0 1] (1 = no constraint), (0 = fixed design) %\ 44 | macro.x_lb(1) = 0; macro.x_ub(1) = 1; % bounds for the density design %\ 45 | macro.x_lb(2) = 0; macro.x_ub(2) = 1; % bounds for the weight design %\ 46 | macro.h_fd = 0; % SA via ANN: h_fd = 0; SA via CFD: h_fd = [0.001, 0.001] (perturbation size for density and weight design variables) %\ 47 | macro.vf_cutoff = 0.1; % cutoff volume fraction for CH interpolation %\ 48 | 49 | macro.alg = 3; % alg=1: fmincon, alg=2: OC, alg=3: GOC, alg=4: MMA %\ 50 | macro.maxloop = 100; % Maximum number of iterations %\ 51 | macro.tolx = 1e-3; % Termination criterion %\ 52 | macro.displayflag = 1; % Display structure actively flag %\ 53 | \ 54 | \ 55 | 2D Cantilever Beam (infilled design case): %\ 56 | macro.nelx = 60; %\ 57 | macro.nely = 30; %\ 58 | macro.nelz = 0; if macro.nelz == 0; macro.dim = 2; else; macro.dim = 3; end % set to 0 if 2D %\ 59 | macro.volfrac = 0.50; %\ 60 | macro.cont = 0; % Continuation: 1 = Yes, 0 = No (Untested) %\ 61 | macro.flt_den_min = 1.0; % density filter minimum radius (flt_den_min <= 1 is off) %\ 62 | macro.flt_sen_min = 3.0; % sensitivity filter minimum radius (flt_sen_min <= 1 is off) %\ 63 | macro.delta = 0.5; % Maximum allowable limit in density between adjacent macroscale elements. [0 1] (1 = no constraint), (0 = fixed design) %\ 64 | macro.x_lb(1) = 0; macro.x_ub(1) = 0.8; % bounds for the density design %\ 65 | macro.x_lb(2) = 0; macro.x_ub(2) = 1; % bounds for the weight design %\ 66 | macro.h_fd = 0; % SA via ANN: h_fd = 0; SA via CFD: h_fd = [0.001, 0.001] (perturbation size for density and weight design variables) %\ 67 | macro.vf_cutoff = 0.1; % cutoff volume fraction for CH interpolation %\ 68 | 69 | macro.alg = 3; % alg=1: fmincon, alg=2: OC, alg=3: GOC, alg=4: MMA %\ 70 | macro.maxloop = 100; % Maximum number of iterations %\ 71 | macro.tolx = 1e-3; % Termination criterion %\ 72 | macro.displayflag = 1; % Display structure actively flag %\ 73 | \ 74 | \ 75 | 3D Five-point Bending Plate (infilled design case): %\ 76 | macro.nelx = 24; %\ 77 | macro.nely = 24; %\ 78 | macro.nelz = 4; if macro.nelz == 0; macro.dim = 2; else; macro.dim = 3; end % set to 0 if 2D %\ 79 | macro.volfrac = 0.25; %\ 80 | macro.cont = 0; % Continuation: 1 = Yes, 0 = No (Untested) %\ 81 | macro.flt_den_min = 1.0; % density filter minimum radius (flt_den_min <= 1 is off) %\ 82 | macro.flt_sen_min = 1.2; % sensitivity filter minimum radius (flt_sen_min <= 1 is off) %\ 83 | macro.delta = 0; % Maximum allowable limit in density between adjacent macroscale elements. [0 1] (1 = no constraint), (0 = fixed design) %\ 84 | macro.x_lb(1) = 0; macro.x_ub(1) = 1; % bounds for the density design %\ 85 | macro.x_lb(2) = 0; macro.x_ub(2) = 1; % bounds for the weight design %\ 86 | macro.h_fd = 0; % SA via ANN: h_fd = 0; SA via CFD: h_fd = [0.001, 0.001] (perturbation size for density and weight design variables) %\ 87 | macro.vf_cutoff = 0.1; % cutoff volume fraction for CH interpolation %\ 88 | 89 | macro.alg = 3; % alg=1: fmincon, alg=2: OC, alg=3: GOC, alg=4: MMA %\ 90 | macro.maxloop = 50; % Maximum number of iterations %\ 91 | macro.tolx = 1e-3; % Termination criterion %\ 92 | macro.displayflag = 1; % Display structure actively flag %\ 93 | \ 94 | \ 95 | 3D Five-point Bending Plate (density-graded design case): %\ 96 | macro.nelx = 24; %\ 97 | macro.nely = 24; %\ 98 | macro.nelz = 4; if macro.nelz == 0; macro.dim = 2; else; macro.dim = 3; end % set to 0 if 2D %\ 99 | macro.volfrac = 0.25; %\ 100 | macro.cont = 0; % Continuation: 1 = Yes, 0 = No (Untested) %\ 101 | macro.flt_den_min = 1.0; % density filter minimum radius (flt_den_min <= 1 is off) %\ 102 | macro.flt_sen_min = 1.2; % sensitivity filter minimum radius (flt_sen_min <= 1 is off) %\ 103 | macro.delta = 0.9; % Maximum allowable limit in density between adjacent macroscale elements. [0 1] (1 = no constraint), (0 = fixed design) %\ 104 | macro.x_lb(1) = 0; macro.x_ub(1) = 1; % bounds for the density design %\ 105 | macro.x_lb(2) = 0; macro.x_ub(2) = 1; % bounds for the weight design %\ 106 | macro.h_fd = 0; % SA via ANN: h_fd = 0; SA via CFD: h_fd = [0.001, 0.001] (perturbation size for density and weight design variables) %\ 107 | macro.vf_cutoff = 0.1; % cutoff volume fraction for CH interpolation %\ 108 | 109 | macro.alg = 3; % alg=1: fmincon, alg=2: OC, alg=3: GOC, alg=4: MMA %\ 110 | macro.maxloop = 50; % Maximum number of iterations %\ 111 | macro.tolx = 1e-3; % Termination criterion %\ 112 | macro.displayflag = 1; % Display structure actively flag %\ 113 | \ 114 | \ 115 | 3D Five-point Bending Plate (infilled design case): %\ 116 | macro.nelx = 24; %\ 117 | macro.nely = 24; %\ 118 | macro.nelz = 4; if macro.nelz == 0; macro.dim = 2; else; macro.dim = 3; end % set to 0 if 2D %\ 119 | macro.volfrac = 0.25; %\ 120 | macro.cont = 0; % Continuation: 1 = Yes, 0 = No (Untested) %\ 121 | macro.flt_den_min = 1.0; % density filter minimum radius (flt_den_min <= 1 is off) %\ 122 | macro.flt_sen_min = 1.2; % sensitivity filter minimum radius (flt_sen_min <= 1 is off) %\ 123 | macro.delta = 0.9; % Maximum allowable limit in density between adjacent macroscale elements. [0 1] (1 = no constraint), (0 = fixed design) %\ 124 | macro.x_lb(1) = 0; macro.x_ub(1) = 0.6; % bounds for the density design %\ 125 | macro.x_lb(2) = 0; macro.x_ub(2) = 1; % bounds for the weight design %\ 126 | macro.h_fd = 0; % SA via ANN: h_fd = 0; SA via CFD: h_fd = [0.001, 0.001] (perturbation size for density and weight design variables) %\ 127 | macro.vf_cutoff = 0.1; % cutoff volume fraction for CH interpolation %\ 128 | 129 | macro.alg = 3; % alg=1: fmincon, alg=2: OC, alg=3: GOC, alg=4: MMA %\ 130 | macro.maxloop = 50; % Maximum number of iterations %\ 131 | macro.tolx = 1e-3; % Termination criterion %\ 132 | macro.displayflag = 1; % Display structure actively flag %\ 133 | --------------------------------------------------------------------------------