Graded-Lattice (Infill) Topology Optimization
println("Julia ", VERSION)Julia 1.12.6
Overview
A graded lattice is a structure whose micro-architecture — a periodic lattice of struts — varies smoothly from fully solid to open where material is not needed. Instead of interpolating stiffness with the ad-hoc SIMP power law ρᵖ, homogenization-based infill (Groen & Sigmund 2018; Wu, Sigmund & Groen 2021) first computes the true effective stiffness C_H(ρ) of a lattice unit cell at a range of lattice densities ρ, and then interpolates that table through the optimization.
In Rasmah this is a GradedLatticePenalty <: AbstractPenalty: the design variable is the local lattice density, and its effective stiffness is the monotone-cubic (PCHIP) interpolation of the homogenized C_H(ρ) table. Because it implements the same value/derivative interface as SIMP, the whole AffineStateMap + adjoint machinery drives it unchanged.
using Rasmah
using LinearAlgebra
part = box(2.0, 0.5, 0.5)
b = brep(part)
left = faces_in_box(b, [-1.1, -1.0, -1.0], [-0.9, 1.0, 1.0])
right = faces_in_box(b, [ 0.9, -1.0, -1.0], [ 1.1, 1.0, 1.0])
mesh = tetrahedralize(b, 8)TetMesh{Matrix{Float64}, Matrix{Int64}, Rasmah.BRepFaceLabels}([-1.0 -0.75 … 0.75 1.0; -0.25 -0.25 … 0.25 0.25; -0.25 -0.25 … 0.25 0.25], [1 1 … 44 44; 2 11 … 71 72; 11 10 … 72 45; 38 38 … 81 81], Rasmah.BRepFaceLabels(FaceSet[FaceSet([16 3 … 15 4; 25 4 … 24 13; 26 13 … 25 14]), FaceSet([61 61 … 65 70; 62 70 … 74 79; 71 71 … 75 80]), FaceSet([29 30 … 4 32; 30 31 … 5 33; 57 58 … 32 60]), FaceSet([52 24 … 51 19; 79 25 … 78 46; 80 52 … 79 47]), FaceSet([28 10 … 37 1; 37 19 … 46 10; 64 46 … 73 37]), FaceSet([45 36 … 18 9; 54 45 … 45 18; 81 72 … 54 45])]))
solve(GradedLatticeTopOpt(), …) builds a cross-lattice unit cell at resolution cell_n, tabulates its homogenized stiffness, and optimizes the macro density so each element’s effective material follows that graded-lattice response.
φ, hist = solve(GradedLatticeTopOpt(), mesh, steel;
fixed = left => [0, 0, 0], loads = right => [1e6, 0, 0],
volfrac = 0.4, cell_n = 6, max_iter = 40)
println("compliance: ", round(hist[1]; digits = 3), " → ", round(hist[end]; digits = 3))
println("final volume frac = ", round(sum(φ) / length(φ); digits = 3))
vtk(mesh; field = φ, fieldname = "lattice density", edges = true)compliance: 38.428 → 6.974
final volume frac = 0.4
Verification
The homogenization table is anchored at the solid end: a fully-dense lattice (ρ = 1) reproduces the solid material exactly, so the penalty interpolant must evaluate to 1 there — and its derivative must match finite differences:
cell = tetrahedralize_box(1.0, 1.0, 1.0, 6, 6, 6)
C = Matrix(elasticity_matrix(1e3, 0.3))
ρs, sc = lattice_stiffness_table(cell, C, range(0.01, 1.0; length = 17); ρmin = 0.01, t = 0.5, p = 3.0)
pen = GradedLatticePenalty(ρs, sc)
println("value at ρ = 1 = ", value(pen, 1.0), " (must be 1)")
h = 1e-6
println("dvalue at ρ = 0.5: analytic ", round(derivative(pen, 0.5); digits = 6),
", finite-diff ", round((value(pen, 0.5 + h) - value(pen, 0.5 - h)) / (2h); digits = 6))value at ρ = 1 = 1.0 (must be 1)
dvalue at ρ = 0.5: analytic 0.745784, finite-diff 0.745784
Next steps
- Harmonic, thermal-CTE & two-scale topology optimization — concurrent micro/macro and homogenization-driven design.
- Density-based topology optimization — the SIMP baseline this penalty generalizes.
