%%%% QP-BASED (TWO VOLUME CONSTRAINTS) SELF-WEIGHT TOPOLOGY OPTIMIZATION CODE Oct, 2023 %%%%
function topqp2vsw(nelx,nely,volfrac_l,volfrac_u,penal,rmin,ec)
%% MATERIAL PROPERTIES
E0 = 1;
Emin = 1e-9;
nu = 0.3;
%% PREPARE FINITE ELEMENT ANALYSIS
A11 = [12  3 -6 -3;  3 12  3  0; -6  3 12 -3; -3  0 -3 12];
A12 = [-6 -3  0  3; -3 -6 -3 -6;  0 -3 -6  3;  3 -6  3 -6];
B11 = [-4  3 -2  9;  3 -4 -9  4; -2 -9 -4 -3;  9  4 -3 -4];
B12 = [ 2 -3  4 -9; -3  2  9 -2;  4  9  2  3; -9 -2  3  2];
KE = 1/(1-nu^2)/24*([A11 A12;A12' A11]+nu*[B11 B12;B12' B11]);
nodenrs = reshape(1:(1+nelx)*(1+nely),1+nely,1+nelx);
edofVec = reshape(2*nodenrs(1:end-1,1:end-1)+1,nelx*nely,1);
edofMat = repmat(edofVec,1,8)+repmat([0 1 2*nely+[2 3 0 1] -2 -1],nelx*nely,1);
iK = reshape(kron(edofMat,ones(8,1))',64*nelx*nely,1);
jK = reshape(kron(edofMat,ones(1,8))',64*nelx*nely,1);
U = zeros(2*(nely+1)*(nelx+1),1);
%% DEFINE KINEMATIC BOUNDARY CONDITIONS
fixeddofs = union([1:2:2*(nely+1)],[2*(nelx+1)*(nely+1)]);
alldofs = [1:2*(nely+1)*(nelx+1)];
freedofs = setdiff(alldofs,fixeddofs);
%% PREPARE FILTER
iH = ones(nelx*nely*(2*(ceil(rmin)-1)+1)^2,1);
jH = ones(size(iH));
sH = zeros(size(iH));
k = 0;
for i1 = 1:nelx
  for j1 = 1:nely
    e1 = (i1-1)*nely+j1;
    for i2 = max(i1-(ceil(rmin)-1),1):min(i1+(ceil(rmin)-1),nelx)
      for j2 = max(j1-(ceil(rmin)-1),1):min(j1+(ceil(rmin)-1),nely)
        e2 = (i2-1)*nely+j2;
        k = k+1;
        iH(k) = e1;
        jH(k) = e2;
        sH(k) = max(0,rmin-sqrt((i1-i2)^2+(j1-j2)^2));
      end
    end
  end
end
H = sparse(iH,jH,sH);
Hs = sum(H,2);
%% INITIALIZE ITERATION
x = repmat(volfrac_l,nely,nelx);
xPhys = x;
loop=0; dchange=1.; fchange=1.; alph=1.; cacc=1.e9; ddc=0.; 
%% START ITERATION
while dchange > 1e-2 || abs(fchange) > 1e-6 || alph > 1.
  loop = loop + 1;
  %% FE-ANALYSIS
  gv = -1./nelx/nely;
  F = zeros(2*(nely+1)*(nelx+1),1);
  for e = 1:nelx*nely
    F(edofMat(e,2:2:end)) = F(edofMat(e,2:2:end)) + xPhys(e)*gv/4.; % vectorise
  end
  sK = reshape(KE(:)*(Emin+xPhys(:)'.^penal*(E0-Emin)),64*nelx*nely,1);
  K = sparse(iK,jK,sK); K = (K+K')/2;
  U(freedofs) = K(freedofs,freedofs)\F(freedofs);
  %% FUNCTION AND SENSITIVITY DERIVATIVES
  ce = reshape(sum((U(edofMat)*KE).*U(edofMat),2),nely,nelx);
  c = sum(sum((Emin+xPhys.^penal*(E0-Emin)).*ce));
  dc = -penal*(E0-Emin)*xPhys.^(penal-1).*ce;
  dc(:)=dc(:)+2.*gv*sum(U(edofMat(:,2:2:end)),2)*1./4.;
  dv_l = -ones(nely,nelx)/nelx/nely/volfrac_l; dv_u = ones(nely,nelx)/nelx/nely/volfrac_u;
  g_l = -sum(xPhys,"all")/nelx/nely/volfrac_l+1.; g_u = sum(xPhys,"all")/nelx/nely/volfrac_u-1.;
  %% OBJECTIVE SCALING
  if loop == 1
    cscl=c;
  end
  c=c/cscl; dc=dc/cscl;
  fchange = c-cacc;
  %% FILTERING/MODIFICATION OF SENSITIVITIES
  dc(:) = H*(dc(:)./Hs);
  dv_l(:) = H*(dv_l(:)./Hs);
  dv_u(:) = H*(dv_u(:)./Hs);
  %% CHECK IF DESCENT STEP
  if loop > 2 && fchange > 0. && ec == 1
    %% IF NOT RETRIEVE THE LAST ACCEPTABLE ITERATE
    x=sub.x_k; c=sub.f_k;
    %% AND INCREASE THE CURVATURE
    alph=alph*2.;
  else
    %% CURVATURE APPROXIMATION
    if loop > 1
        ddc = 2.*(cold-c-sum(dc.*(xold-x),"all"))/max(sum((xold-x).*(xold-x),"all"),1e-9);
    end
    ddc = max(ddc,1e-9);
    %% STORE THIS ACCEPTED ITERATE
    sub.ddf_k=ddc; sub.df_k=dc; sub.dg_k={dv_l,dv_u}; sub.g_k={g_l,g_u}; sub.x_k=x; sub.f_k=c;
    xold=x; cacc=c; alph=1.; 
  end
  %% QP UPDATE OF DESIGN VARIABLES
  l1 = 0e0; l2 = 1e9; move = 0.1;
  while (l2-l1)/(l1+l2) > 1e-9
    lmid = 0.5*(l2+l1);
    %% UPDATE WITH MINIMUM VOLUME CONSTRAINT
    xdel = min(max((sub.df_k+lmid*sub.dg_k{1})/sub.ddf_k/alph,-move),move);
    xnew = min(max(x-xdel,0.),1.);
    gt = sub.g_k{1}+sum(sub.dg_k{1}.*(xnew-sub.x_k),"all");
    if gt>0; l1=lmid;
    else; l2=lmid;
    end
  end
  %% IF MINIMUM VOLUME CONSTRAINT IS NOT ACTIVE
  if lmid < 1e-9
    l1 = 0e0; l2 = 1e9; move = 0.1;
    while (l2-l1)/(l1+l2) > 1e-9
      lmid = 0.5*(l2+l1);
      %% UPDATE WITH MAXIMUM VOLUME CONSTRAINT
      xdel = min(max((sub.df_k+lmid*sub.dg_k{2})/sub.ddf_k/alph,-move),move);
      xnew = min(max(x-xdel,0.),1.);
      gt = sub.g_k{2}+sum((sub.dg_k{2}).*(xnew-sub.x_k),"all");
      if gt>0; l1=lmid;
      else; l2=lmid;
      end
    end
  end
  dchange = max(abs(xnew(:)-x(:)));
  x=xnew; cold=c; baw=(sum(x>1-1e-3,"all")+sum(x<1e-3,"all"))/nelx/nely; 
  %% FILTER DESIGN VARIABLES
  xPhys(:) = (H*x(:))./Hs;
  %% PRINT RESULTS
  fprintf(' It.:%5i Obj.:%10.3e Vol.:%10.3e B&W.:%2.2f ch.:%10.3e %10.3e\n',loop,c*cscl, ...
    mean(xPhys(:)),baw,dchange,fchange);
  %% PLOT DENSITIES
  colormap(gray); imagesc(1-xPhys); caxis([0 1]); axis equal; axis off; drawnow;
end
tmp = 1-xPhys;
tmp2 = flip(tmp,2);
colormap(gray); imagesc([tmp2 tmp]); caxis([0 1]); axis equal; axis off; drawnow;
saveas(gcf,'topo.eps');
quit();
end
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% This Matlab code was written by D.P. Munro, while in the employment of   %
% the Department of Precision and Microsystems Engineering, Faculty of     %
% Mechanical Engineering, Delft University of Technology, at Mekelweg 5,   %
% 2328 CD Delft, The Netherlands.                                          %
% Please send your comments to: d.p.munro-1@tudelft.nl                     %
%                                                                          %
% The code is intended for educational purposes and theoretical details    %
% are discussed in the paper                                               %
% "A simple QP modification of the OC update to permit treatment of the    %
% topology design problem of self-weight", D.P. Munro , Struct Multidisc   %
% Optim, 2023                                                              %
% This version is based on earlier 88-line code                            %
% by Erik Andreassen and co-workers (2011), Struct Multidisc Optim, Vol    %
% 43, pp. 1--16.                                                           %
%                                                                          %
% Disclaimer:                                                              %
% The author reserves all rights but do not guaranty that the code is      %
% free from errors. Furthermore, we shall not be liable in any event       %
% caused by the use of the program.                                        %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
