function ParetoOptimalTracing(nelx,nely,desVolFrac,problem,volDecrMax,filterRadius)
% Generate pareto-optimal topologies via fixed point iteration
% Author: Krishnan Suresh; [email protected]
% Reference: "A 199-line Matlab Code for Pareto-Optimal Tracing in Topology Optimization",
% K. Suresh, Structural and Multidisciplinary Optimization, DOI:10.1007/s00158-010-0534-6
% Acknowledgements: "A 99 line topology optimization code written in Matlab"
% by Ole Sigmund (2001), Structural and Multidisciplinary Optimization,
% Vol 21, pp. 120--127.
if (nargin == 0) % default values
nelx = 60;nely = 30; % The grid size for topology optimization
desVolFrac = 0.5; % The final volume fraction desired
problem = 1; % 1 or 2 for cantilevered beam problems
volDecrMax = 0.05; % step-size for pareto-tracing
JIncMax = 5; % For steep change in pareto-curve, use additional constraint
filterRadius = 0.6; % Use for smoothening the topological sensitivity field
end
voidEps = 1e-6; % Relative Young's Modulus of void region
filter = fspecial('gaussian', [3 3],filterRadius); % smoothen topological sensitivity field
totalIter = 0;
elemsIn = ones(nely,nelx); % intialize the domain
U = FE(nelx,nely,elemsIn,voidEps,problem); % Solve FEA problem
T = ComputeT(U,elemsIn,voidEps); % Compute topological sensitivity
T = filter2(filter,T); % smoothen the field
J(1) = computeCompliance(nelx,nely,elemsIn,voidEps,U); % compute & store compliance
volIndex = 1;volFractions(1) = 1; volfrac = 1; % initialization
volDecrement = volDecrMax; % current decrement of volume fraction
JOld = J(1);
while (volfrac > desVolFrac)
volfrac = max(desVolFrac,volfrac-volDecrement); % move to the next volume fraction
volIndex = volIndex+1;
volFractions(volIndex) = volfrac; % store the volume fraction
iter = 0;
while (iter < 10) % to avoid cycles; typically 20 iterations is sufficient
totalIter= totalIter+1;
[isValid,isParetoOptimal] = analyzeTopology(T,elemsIn);
if ((iter > 5)&&(isValid)&&(isParetoOptimal)) % done with current vol
break
end
% Find the level-set value such that the contour has given vol fraction
value = findContourValueWithVolumeFraction(T,volfrac);
[index] = find(T < value); % eliminate all elements less than this value
elemsIn = ones(nely,nelx); % start with the full domain
elemsIn(ind2sub(size(T),index)) = 0; % remove elements
U = FE(nelx,nely,elemsIn,voidEps,problem); % FEA
T = ComputeT(U,elemsIn,voidEps); % Topological Sensitivity
T = filter2(filter,T); % Smoothen the field
JTemp = computeCompliance(nelx,nely,elemsIn,voidEps,U);
if (abs(JTemp-JOld)/JTemp < 0.001) break; end
JOld = JTemp;
iter= iter+1;
end
J(volIndex) = JTemp;
value = findContourValueWithVolumeFraction(T,volfrac); % as above
plotContour(T,value,figure(1));
title(['v=' num2str(volfrac) '; J = ' num2str(J(volIndex)) '; #FEA = ' num2str(totalIter)]);
pause(0.01);
dJ = J(volIndex)- J(volIndex-1);
volDecrement = max(volDecrMax/10,min(volDecrement,JIncMax*volDecrement/dJ));
end
figure(2); plot(volFractions,J,volFractions,J,'*');
xlabel('Volume'); ylabel('Compliance'); grid on;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [U]=FE(nelx,nely,elemsIn,voidEps,problem)
if (problem == 1) % Cantilevered beam;
fixeddofs = 1:2*(nely+1); % left edge
forcedDof = 2*(nelx+1)*(nely+1)-nely; % y force
elseif (problem == 2) % MBB beam
fixeddofs = 1:2*(nely+1); % left edge
forcedDof = 2*(nelx+1)*(nely+1); % y force
end
K = sparse(2*(nelx+1)*(nely+1), 2*(nelx+1)*(nely+1));
F = sparse(2*(nely+1)*(nelx+1),1); U = zeros(2*(nely+1)*(nelx+1),1);
[KE] = lk;
for elx = 1:nelx
for ely = 1:nely
n1 = (nely+1)*(elx-1)+ely;
n2 = (nely+1)* elx +ely;
edof = [2*n1-1 2*n1 2*n2-1 2*n2 2*n2+1 2*n2+2 2*n1+1 2*n1+2]';
alpha = (1-elemsIn(ely,elx))*voidEps + elemsIn(ely,elx);
K(edof,edof) = K(edof,edof) + alpha*KE; % not the most efficient method
end
end
F(forcedDof,1) = -1;
alldofs = 1:2*(nely+1)*(nelx+1);
freedofs = setdiff(alldofs,fixeddofs);
U(freedofs,:) = K(freedofs,freedofs) \ F(freedofs,:);
U(fixeddofs,:)= 0;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [KE]=lk % element stiffness
E = 1.; nu = 0.3;
k=[ 1/2-nu/6 1/8+nu/8 -1/4-nu/12 -1/8+3*nu/8 ...
-1/4+nu/12 -1/8-nu/8 nu/6 1/8-3*nu/8];
KE = E/(1-nu^2)*[ k(1) k(2) k(3) k(4) k(5) k(6) k(7) k(8)
k(2) k(1) k(8) k(7) k(6) k(5) k(4) k(3)
k(3) k(8) k(1) k(6) k(7) k(4) k(5) k(2)
k(4) k(7) k(6) k(1) k(8) k(3) k(2) k(5)
k(5) k(6) k(7) k(8) k(1) k(2) k(3) k(4)
k(6) k(5) k(4) k(3) k(2) k(1) k(8) k(7)
k(7) k(4) k(5) k(2) k(3) k(8) k(1) k(6)
k(8) k(3) k(2) k(5) k(4) k(7) k(6) k(1)];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [T] = ComputeT(U,elemsIn,voidEps)
% Compute the topological sensitivity at the center of each element
[nely,nelx] = size(elemsIn);
gradN =0.5*[-1 1 1 -1;-1 -1 1 1]; % at center
E0 = 1;nu = 0.3;
D0 = 1/(1-nu^2)*[1 nu 0; nu 1 0;0 0 (1-nu)/2]; % plane stress
T(1:nely,1:nelx) = 0; % initialize to 0
for elx = 1:nelx
for ely = 1:nely
n1 = (nely+1)*(elx-1)+ely;
n2 = (nely+1)* elx +ely;
edof = [2*n1-1 2*n1 2*n2-1 2*n2 2*n2+1 2*n2+2 2*n1+1 2*n1+2]';
uGrad = gradN*U(edof(1:2:end));
vGrad = gradN*U(edof(2:2:end));
strains = [uGrad(1); vGrad(2); (uGrad(2)+vGrad(1)) ];
alpha = (1-elemsIn(ely,elx))*voidEps + elemsIn(ely,elx);
E = E0*alpha;
stresses = D0*E*strains;
stressTensor = [stresses(1) stresses(3); stresses(3) stresses(2)];
strainTensor = [strains(1) strains(3)/2; strains(3)/2 strains(2)];
if (elemsIn(ely,elx))
T(ely,elx) = 4/(1+nu)*sum(sum(stressTensor.*strainTensor))- ...
(1-3*nu)/(1-nu^2)*trace(stressTensor)*trace(strainTensor);
else
T(ely,elx) = -4/(3-nu)*sum(sum(stressTensor.*strainTensor))-...
(1-3*nu)/((1+nu)*(3-nu))*trace(stressTensor)*trace(strainTensor);
end
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [J]=computeCompliance(nelx,nely,elemsIn,voidEps,U)
% Compute the compliance of the entire mesh
[KE] = lk;J = 0;
for elx = 1:nelx
for ely = 1:nely
n1 = (nely+1)*(elx-1)+ely;
n2 = (nely+1)* elx +ely;
edof = [2*n1-1 2*n1 2*n2-1 2*n2 2*n2+1 2*n2+2 2*n1+1 2*n1+2]';
alpha = (1-elemsIn(ely,elx))*voidEps + elemsIn(ely,elx);
Ue = U(edof);
J = J + alpha*Ue'*KE*Ue;
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function value = findContourValueWithVolumeFraction(field,volfrac)
% Find the level-set value such that the contour has given vol fraction
% The code computes the level-set value with desired external volume
[nely,nelx] = size(field);
externalVolumeDesired = nelx*nely*(1-volfrac);
field = -field; % reverse the sign to compute external volume
valMax = 0; valMin = min(field(:));
bufferedField = valMin*ones(nely+2,nelx+2);% Add buffer to get closed contours
bufferedField(2:end-1,2:end-1) = field;
iterMax = 50;iter = 1;
while (1) % A binary search is used to find the optimal level-set value
valMid = (valMax+valMin)/2;
extVol = computeAreaInContour(bufferedField,valMid);
err = abs(extVol-externalVolumeDesired)/(extVol);
if (err < 0.001) || (iter > iterMax)
value = -valMid; % change the sign before return
return;
end
if (extVol > externalVolumeDesired)
valMin = valMid;
else
valMax = valMid;
end
iter = iter+1;
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function area = computeAreaInContour(bufferedField,value)
% For a given level-set value, compute the area enclosed
% It is assumed that the field has been buffered; see code above
[cntr,h] = contours(bufferedField,[value value]);
indices = find(cntr(1,:) == value);area = 0;
for i = 1:numel(indices)
startCol = indices(i)+1;
endCol = startCol+ cntr(2,indices(i))-1;
xPoly = cntr(1,startCol:endCol);
yPoly = cntr(2,startCol:endCol);
area = area + polyarea(xPoly,yPoly);
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [isValid,isParetoOptimal] = analyzeTopology(T,elemsIn)
% Check if the Topological field is valid and/or pareto-optimal
T_SMin = min(T(elemsIn==1)); % Min of topological field inside the domain
T_AMax = max(T(elemsIn==0)); % Max of topological field outside the domain
T_AMin = min(T(elemsIn==0)); % Min of topological field outside the domain
isValid = 0; isParetoOptimal = 0;
if (T_SMin > 0.8*T_AMax), isValid = 1; end
if (T_AMin+T_SMin >= 0), isParetoOptimal = 1; end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function plotContour(T,value,fig)
%Use Matlab's built-in contour command to draw the optimal topology.
[nely,nelx] = size(T);
figure(fig);clf;fill([1 nelx nelx 1],[1 1 nely nely],'b'); hold on;
[cntr,h] =contourf(-T(end:-1:1,:),[-value -value]); % the second argument is essential
axis('equal'); axis tight;axis off;
