Mega Voxel Topology Optimization Example

Solving Massive Mega-Voxel Problems On The GPU

Here we demonstrate how you can use the multi-grid solver in FANTOM to solve mega-voxel TO problems in 3D. We will use the bridge problem again and use a structured mesh with 8M elements!

You can run this with 16GB of GPU VRAM since FANTOM’s multi-grid is matrix free at the highest level!

Importing The Necessary Tools

First we import the tools from pyFANTOM we will be using in this example.

from pyFANTOM.CUDA import(
    StructuredMesh3D, # Mesh Object for 3D Structured Meshes
    StructuredFilter3D, # Filter kernel for 3D Structured Meshes
    StructuredStiffnessKernel, # Stiffness Kernel for 3D Structured Meshes
    FiniteElement, # Finite Element Object to Setup Boundary Conditions and RHS
    CG, GMRES, MultiGrid, SPSOLVE, # Sparse Solvers
    MinimumCompliance, # TO Problem Definition
    PGD, MMA, OC # Nonlinear Optimizers
)

from pyFANTOM.Physics import LinearElasticity # Physics Model For FEA

# Other Imports
import numpy as np
import matplotlib.pyplot as plt
from tqdm import trange

Setting Up Physics and Geometry

The first step in using pyFANTOM is defining the domain of the problem and setting up the physics for FEA.

length = 1.0
width = 0.25
height = 0.25

# Create a physics model
physics = LinearElasticity(E=1.0, nu=1/3, thickness=1.0, type='PlaneStress')

# Create a mesh with 8M elements (512 x 128 x 128)
mesh = StructuredMesh3D(nx=512, ny=128, nz=128, lx=length, ly=height, lz=width, physics=physics)

Setting Up The Finite Element Solver

The next step in using pyFANTOM is setting up a stiffness kernel and finite-element boundary conditions. For mega-voxel problems, we use the MultiGrid solver with multiple levels to efficiently solve the large linear systems.

# Create a stiffness kernel
stiffness_kernel = StructuredStiffnessKernel(mesh = mesh)

# Create a solver
# For mega-voxel problems, MultiGrid with multiple levels is essential
solver = MultiGrid(
    mesh=mesh,
    kernel=stiffness_kernel,
    maxiter=100,
    tol=1e-4,
    n_level=6,  # 6 levels for the multi-grid hierarchy
    cycle='W',  # W-cycle for better convergence
    w_level=[2,3],  # Number of W-cycles at each level
    omega_boost=1.05  # Relaxation parameter boost
)

# Create a finite-element object
fe = FiniteElement(mesh=mesh, kernel=stiffness_kernel, solver=solver)

left_edge_nodes = np.where(mesh.nodes[:, 0] < 1e-12)[0]
right_edge_nodes = np.where(mesh.nodes[:, 0] > length - 1e-12)[0]

bottom_edge_nodes = np.where(mesh.nodes[:, 1] < 1e-12)[0]

fe.reset_dirichlet_boundary_conditions()
fe.reset_forces()
# Apply force to the bottom edge
fe.add_point_forces(
    node_ids=bottom_edge_nodes,
    positions=None,
    forces=np.array([[0.0, -1.0/len(bottom_edge_nodes), 0.0]])
)

# Apply zero displacement to the left edge and the right edge
fe.add_dirichlet_boundary_condition(
    node_ids=np.concatenate([left_edge_nodes, right_edge_nodes]), # Node IDs to apply the boundary condition to
    positions=None, # Alternative way to specify the boundary condition is to pass positions in space
    dofs=np.array([[1, 1, 1]]), # Degrees of freedom to apply the boundary condition to
    rhs=0.0 # Value to apply the boundary condition to
)

Setting Up TO Problem And Optimizer

Once the finite element is setup we can define the problem and optimizer to perform TO.

# Define the filter kernel for TO
filter_kernel = StructuredFilter3D(mesh=mesh, r_min=1.5)

# Define the TO problem
to_problem = MinimumCompliance(
    FE=fe,
    filter=filter_kernel,
    E_mul=[1.0], # You can pass a list of values to perform multi-material TO
    volume_fraction=[0.05], # You can pass a list of values to perform volume fraction control for each material
    void=1e-9, # You can pass a value to set void modulus
    penalty=3, # You can pass a value to set penalty factor
    # penalty_schedule = lambda p, i: (p-2)*np.round(3 * min(50, i) / 50)/3 + 2, # You can pass a function to set penalty schedule here
    heavyside= True, # You can pass a boolean to use heavyside or not
    eta=0.5, # You can pass a value to set eta - only used for heavyside
    beta=2.0, # You can pass a value to set beta, or a function of iteration to set beta schedule - only used for heavyside
)

# Define the optimizer
optimizer = PGD( # You can use any of the optimizers in pyFANTOM: OC, MMA, PGD
    problem=to_problem,
    change_tol=np.inf, # No change tolerance
    fun_tol=1e-4, # Function tolerance (convergence criterion for the optimizer)
)

Running The Optimization Loop

Now we can run the optimization loop and perform TO on the mega-voxel problem.

maximum_iterations = 300

Progressbar = trange(maximum_iterations, desc='Optimizer Iterations', leave=True)
objective_history = []
for i in Progressbar:
    optimizer.iter()

    Progressbar.set_postfix(
        optimizer.logs()
    )

    objective_history.append(optimizer.logs()['objective'])

    if optimizer.converged():
        print(f'Converged in {i} iterations')
        break

The mega-voxel optimization converged in 73 iterations with the following output:

Optimizer Iterations:  24%|██▍       | 73/300 [23:49<1:14:04, 19.58s/it, objective=244, variable change=5.01, function change=6.24e-6, iteration=75, residual=0.0135] Converged in 73 iterations

Now we can visualize the resulting topology:

to_problem.visualize_solution()

We can also verify the volume fraction:

(to_problem.desvars>0.5).sum()/to_problem.desvars.size

This returns approximately 0.05 (5%), confirming that the volume fraction constraint is satisfied.

Notes on Mega-Voxel Problems

  • Memory Efficiency: FANTOM’s multi-grid solver is matrix-free at the highest level, allowing you to solve problems with 8M+ elements on GPUs with 16GB VRAM.

  • Performance: The multi-grid solver with W-cycles provides excellent convergence rates even for very large problems, making it practical to solve mega-voxel topology optimization problems.

  • Scalability: The solver scales well with the number of grid levels. For problems with 8M elements, using 6 levels provides a good balance between memory usage and convergence speed.