Skip to content
Eduardo Lenz edited this page Jul 4, 2022 · 19 revisions

Welcome to the LFEM wiki!

How to solve a simple Topology Optimization problem using LFEM

The main idea of the L* packages is to make it easy to implement a topology optimization program. To show a basic example, lets solve the traditional

#
# Min C(x)
# S.t
#      V(x)≤V̄
#
#      0<xmin ≤ x ≤1

where C is the compliance and V the volume. The limit volume V̄ is set as a fraction of the total volume. This problem can be solved by using an Optimal Criteria (OC) as explained by Bensdsoe and Kikuchi in their seminal paper of 1988.

First, load the packages

using BMesh, LMesh, TMeshes, LFEM

The main function is the OC

#
# Min C(x)
# S.t
#      V(x)≤V̄
#
#      0<xmin ≤ x ≤1
#
function OC(mesh::Mesh,vf::Float64,x::Vector,dC::Vector,dV::Vector)
    
    
    # Initial interval
    λ1 = 0.0
    λ2 = 1E5
    
    # Moving limits
    δ = 0.1
    
    # Tolerance (volume constraint)
    tol = 1E-6
    
    # Relaxation
    η = 0.5
    
    # Minimal value
    x_min = 1E-3
    
    # Number of elements
    ne = Get_ne(mesh)

    # Full volume
    full_volume = Volume(mesh,ones(ne))
    
    # Limit volume
    Vlimit = vf*full_volume
    
    
    # Working array
    x_estim = copy(x)
   
    # External loop - OC
    for k=1:1000  
       
       # Middle point 
       λ = (λ1 + λ2)/2
          
       # Loop along the elements
       for j in mesh
              
           # Amplification factor 
           β = -min(0.0,dC[j])/*dV[j]) 
        
           # New value for this variable
           x_e = x[j]*^η)
            
           # Moving limits
           x_dir = min(x[j] + δ, 1.0) 
           x_esq = max(x[j] - δ, x_min) 
           
           # 
           #-------0[----|--x--|----]1---------  
           #           x-δ    x+δ 
           #
            
           # Check moving limits 
           x_estim[j] = max( min( x_e, x_dir), x_esq)
            
            
       end #j
         
       # Current volume
       V = Volume(mesh,x_estim)
 
       # Test constraint
       if abs(λ2 - λ1)<=tol
            break
       end
        
       # Adjust λ to match the volume constraint
       if (V > Vlimit)
             λ1 = λ
       else
             λ2 = λ
       end
 
        
    end # k
 
    # Return the x_estim
    return x_estim
    
end

Next, let's define the functions and derivatives needed to solve the optimization problem

#
# Volume
#
function Volume(mesh::Mesh,x::Vector)
   
    volume = 0.0
    
    # Loop for all elements
    for ele in mesh
        
        # Volume of this element
        vol = Volume_element(mesh,ele)

        # Add to the total volume
        volume = volume + vol*x[ele]
        
    end #ele
    
    return volume
    
end
function dVolume(mesh::Mesh,x::Vector)
    
    # Number of elements
    ne = Get_ne(mesh)

    # Output 
    D = zeros(ne)

    # Loop for all elements
    for ele in mesh
    
        # Local derivative
        D[ele] = Volume_element(mesh,ele)

        
    end #ele
    
    return D
    
end

and

function dCompliance(mesh::Mesh,U::Vector,x::Vector,dkparam::Function)
    
    # Number of elements
    ne = Get_ne(mesh)

    # Output vector
    D = zeros(ne)

    # Loop over elements in the mesh
    for ele in mesh

        # Dofs
        dofs = DOFs(mesh,ele)
   
        # Element displacement in global reference
        ug = U[dofs] 

        # If needed, rotate to local reference
        ul = To_local(ug,mesh,ele)
        
        # Element stiffness in Local reference
        Ke = Local_K(mesh,ele)

        D[ele]  =  -dkparam(x[ele])*dot(ul,Ke,ul)

    end
   
    return D

end

Main program

function main(vf=0.5,output="topo.sol")

    # Load the problem
    mesh = Simply_supported2D(6,6)
    
    # Material parametrization and derivative
    kparam(xe::Float64,p=3.0)=xe^p
    dkparam(xe::Float64,p=3.0)=p*xe^(p-1)

    # Number of elements
    ne = Get_ne(mesh)

    # Initial point
    x = vf*ones(ne)

    # Main loop
    for j=1:100

        # Equilibrium
        U,_ = Solve_linear(mesh,x,kparam)

        # Sensitivities
        dV = dVolume(mesh,x)
        dC = dCompliance(mesh,U,x,dkparam)

        # OC
        xn = OC(mesh,vf,x,dC,dV)

        # Stop criteria
        if norm(xn.-x).<1E-6
            println("Done")
            x .= xn
            break
        end

        # Roll over Beethoven
        x .= xn 

    end

    # Write solution
    Gmsh_init(output,mesh)

    # Write topology
    Gmsh_element_scalar(mesh,x,output,"Topology")

    # As requested by Dr. Olavo, export the displacements
    U,_ = Solve_linear(mesh,x,kparam)
    Gmsh_nodal_vector(mesh,U,output,"Displacement")

end

Putting all togheter

using BMesh, LMesh, TMeshes, LFEM

#
# Min C(x)
# S.t
#      V(x)≤V̄
#
#      0<xmin ≤ x ≤1
#

function main(vf=0.5,output="topo.sol")

    # Load the problem
    mesh = Simply_supported2D(6,6)
    
    # Material parametrization and derivative
    kparam(xe::Float64,p=3.0)=xe^p
    dkparam(xe::Float64,p=3.0)=p*xe^(p-1)

    # Number of elements
    ne = Get_ne(mesh)

    # Initial point
    x = vf*ones(ne)

    # Main loop
    for j=1:100

        # Equilibrium
        U,_ = Solve_linear(mesh,x,kparam)

        # Sensitivities
        dV = dVolume(mesh,x)
        dC = dCompliance(mesh,U,x,dkparam)

        # OC
        xn = OC(mesh,vf,x,dC,dV)

        # Stop criteria
        if norm(xn.-x).<1E-6
            println("Done")
            x .= xn
            break
        end

        # Roll over Beethoven
        x .= xn 

    end

    # Write solution
    Gmsh_init(output,mesh)

    # Write topology
    Gmsh_element_scalar(mesh,x,output,"Topology")

    # As requested by Dr. Olavo, export the displacements
    U,_ = Solve_linear(mesh,x,kparam)
    Gmsh_nodal_vector(mesh,U,output,"Displacement")

end


function OC(mesh::Mesh,vf::Float64,x::Vector,dC::Vector,dV::Vector)
    
    
    # Initial interval
    λ1 = 0.0
    λ2 = 1E5
    
    # Moving limits
    δ = 0.1
    
    # Tolerance (volume constraint)
    tol = 1E-6
    
    # Relaxation
    η = 0.5
    
    # Minimal value
    x_min = 1E-3
    
    # Number of elements
    ne = Get_ne(mesh)

    # Full volume
    full_volume = Volume(mesh,ones(ne))
    
    # Limit volume
    Vlimit = vf*full_volume
    
    
    # Working array
    x_estim = copy(x)
   
    # External loop - OC
    for k=1:1000  
       
       # Middle point 
       λ = (λ1 + λ2)/2
          
       # Loop along the elements
       for j in mesh
              
           # Amplification factor 
           β = -min(0.0,dC[j])/*dV[j]) 
        
           # New value for this variable
           x_e = x[j]*^η)
            
           # Moving limits
           x_dir = min(x[j] + δ, 1.0) 
           x_esq = max(x[j] - δ, x_min) 
           
           # 
           #-------0[----|--x--|----]1---------  
           #           x-δ    x+δ 
           #
            
           # Check moving limits 
           x_estim[j] = max( min( x_e, x_dir), x_esq)
            
            
       end #j
         
       # Current volume
       V = Volume(mesh,x_estim)
 
       # Test constraint
       if abs(λ2 - λ1)<=tol
            break
       end
        
       # Adjust λ to match the volume constraint
       if (V > Vlimit)
             λ1 = λ
       else
             λ2 = λ
       end
 
        
    end # k
 
    # Return the x_estim
    return x_estim
    
end


#
# Volume
#
function Volume(mesh::Mesh,x::Vector)
   
    volume = 0.0
    
    # Loop for all elements
    for ele in mesh
        
        # Volume of this element
        vol = Volume_element(mesh,ele)

        # Add to the total volume
        volume = volume + vol*x[ele]
        
    end #ele
    
    return volume
    
end

function dVolume(mesh::Mesh,x::Vector)
    
    # Number of elements
    ne = Get_ne(mesh)

    # Output 
    D = zeros(ne)

    # Loop for all elements
    for ele in mesh
    
        # Local derivative
        D[ele] = Volume_element(mesh,ele)

        
    end #ele
    
    return D
    
end


function dCompliance(mesh::Mesh,U::Vector,x::Vector,dkparam::Function)
    
    # Number of elements
    ne = Get_ne(mesh)

    # Output vector
    D = zeros(ne)

    # Loop over elements in the mesh
    for ele in mesh

        # Dofs
        dofs = DOFs(mesh,ele)
   
        # Element displacement in global reference
        ug = U[dofs] 

        # If needed, rotate to local reference
        ul = To_local(ug,mesh,ele)
        
        # Element stiffness in Local reference
        Ke = Local_K(mesh,ele)

        D[ele]  =  -dkparam(x[ele])*dot(ul,Ke,ul)

    end
   
    return D

end

Clone this wiki locally