DeepSDFStruct.optimization#
Structural Optimization Utilities#
This module provides tools for gradient-based optimization of SDF-based geometries, with a focus on structural design problems. It integrates with TorchFEM for finite element analysis and provides optimization algorithms suitable for constrained design problems.
Key Features#
- MMA Optimizer
Implementation of the Method of Moving Asymptotes (MMA), a gradient-based algorithm well-suited for structural optimization with nonlinear constraints. MMA is particularly effective for: - Topology optimization - Shape optimization with constraints - Problems with expensive objective evaluations - Highly nonlinear design spaces
- Finite Element Integration
Conversion between TorchFEM and PyVista mesh formats
Support for tetrahedral and hexahedral elements
Linear and quadratic element types
Integration with gradient computation
- Mesh Quality Utilities
Signed volume computation for tetrahedra
Mesh quality metrics
Degeneracy detection
The module is designed to work seamlessly with differentiable SDF representations, enabling gradient-based optimization of complex 3D structures.
Functions
|
Convert a TorchFEM Solid mesh to PyVista UnstructuredGrid. |
|
Compute signed volumes of tetrahedral elements. |
Classes
|
Method of Moving Asymptotes (MMA) optimizer for constrained problems. |
- class DeepSDFStruct.optimization.MMA(parameters, bounds, max_step=0.1, n_constraints=1)#
Bases:
objectMethod of Moving Asymptotes (MMA) optimizer for constrained problems.
MMA is a gradient-based optimization algorithm designed for nonlinear constrained problems. It constructs convex subproblems using moving asymptotes and is particularly effective for structural optimization.
The optimizer handles a single objective function and a single constraint, with box bounds on design variables. It automatically normalizes the objective by its initial value for better numerical behavior.
- Parameters:
parameters (torch.Tensor) – Initial design variables (will be optimized in-place).
bounds (array-like of shape (n, 2)) – Box constraints [[lower_1, upper_1], …, [lower_n, upper_n]] for each design variable.
max_step (float, default 0.1) – Maximum allowed change in design variables per iteration, as a fraction of the bound range.
- parameters#
Current design variables (updated in-place each iteration).
- Type:
torch.Tensor
- loop#
Current iteration number.
- Type:
int
- x#
Current design variables in numpy format.
- Type:
ndarray
- xold1, xold2
Design variables from previous two iterations (for MMA history).
- Type:
ndarray
- step(F, dF, G, dG)#
Perform one MMA optimization step given objective, constraint, and their gradients.
Notes
MMA was developed by Krister Svanberg and is widely used in topology optimization. It is particularly effective for problems where: - The objective and constraints are expensive to evaluate - Gradients are available (via automatic differentiation) - The design space is high-dimensional - Strong nonlinearity is present
The implementation uses the mmapy package for the core MMA algorithm.
Examples
>>> import torch >>> from DeepSDFStruct.optimization import MMA >>> >>> # Define design variables >>> params = torch.ones(10, requires_grad=True) >>> bounds = [[0.0, 2.0]] * 10 >>> >>> # Create optimizer >>> optimizer = MMA(params, bounds, max_step=0.1) >>> >>> # Optimization loop >>> for i in range(100): ... # Compute objective and constraint ... objective = (params ** 2).sum() ... constraint = params.sum() - 5.0 ... ... # Compute gradients ... dF = torch.autograd.grad(objective, params, create_graph=True)[0] ... dG = torch.autograd.grad(constraint, params, create_graph=True)[0] ... ... # MMA step ... optimizer.step(objective, dF, constraint, dG) ... ... if optimizer.ch < 1e-3: ... break
References
- step(F, dF, G, dG, geom_eval=None, geom_rows=None, max_inner=1, feas_tol=0.05, restore_eval=None, restore_tol=0.005, restore_max_steps=8, restore_step_limit=None)#
Perform one MMA optimization step.
Updates design variables by solving a convex subproblem constructed from the objective, constraint, and their gradients.
- Parameters:
F (torch.Tensor or float) – Objective function value at current design.
dF (torch.Tensor) – Gradient of objective w.r.t. design variables, shape (n,).
G (torch.Tensor or float) – Constraint values at current design (≤ 0 is feasible), reshaped to (m, 1) for the
m = n_constraintsrows this instance was built with. A scalar is accepted whenm == 1.dG (torch.Tensor) – Gradient of the constraints w.r.t. design variables, reshaped to (m, n). For
m == 1a flat (n,) tensor is accepted.geom_eval (callable, optional) – Cheap geometry-only re-evaluation
x_np -> np.ndarray. Given a candidate design vector it returns the true (nonlinear) constraint valuesg = value - targetfor the rows listed ingeom_rows, without running the expensive (CFD) objective/constraints. Enables the hybrid-GCMMA conservativeness loop; whenNonethis is a plain MMA step.geom_rows (sequence of int, optional) – Row indices into
Gfor the geometry-only constraints thatgeom_evalreturns, in the same order. Required withgeom_eval.max_inner (int, optional) – Maximum GCMMA inner (conservativeness) iterations per step when
geom_evalis supplied. Each rejected candidate raises the offending rows’ curvature parameter rho (Svanberg 2002raaupdate) and re-solves; the move limit stays FIXED. Default 1 (no inner loop).feas_tol (float, optional) – TRUE-violation level below which a candidate is accepted outright, in the units of the constraint rows (pass normalized, “fraction over budget” rows and the default 0.05 reads “5% over budget is tolerated transiently”). Must be well above 0: at an ACTIVE constraint the true value always reads slightly above the approximation (residual curvature, evaluation noise) – with a ~0 tolerance the inner loop fires on every boundary-riding step and burns max_inner solves per iteration for nothing.
restore_eval (callable, optional) – Enables POST-STEP FEASIBILITY RESTORATION on the cheap geometry rows:
x_np -> (g, J)returning the true values (k,) AND their gradients (k, n) – masked for locked variables, in the same normalized units as the constraint rows. After the step is accepted (through whichever gate), the candidate is projected back onto the geometry-feasible set with minimum-norm Gauss-Newton passes, so geometry violations beyondrestore_tolcannot survive an iteration. Independent of the GCMMA inner loop (works with or withoutgeom_eval). No CFD is invoked.restore_tol (float, optional) – Restoration target/trigger: rows with
g <= restore_tolare left alone. Keep it above the geometry-evaluation noise floor (default 5e-3).restore_max_steps (int, optional) – Maximum Gauss-Newton passes per outer iteration (default 8; typically 1-2 are used). Each pass costs one geometry evaluation plus up to 4 backtracks.
restore_step_limit (float, optional) – Per-pass infinity-norm cap on the correction; defaults to
max_step.
Notes
The method automatically: - Normalizes the objective by its initial value - Enforces move limits based on max_step - Updates MMA history (xold1, xold2) - Computes and logs convergence metric (ch) - Updates self.parameters in-place
The convergence metric ch is the relative change in design variables.
- DeepSDFStruct.optimization.get_mesh_from_torchfem(Solid)#
Convert a TorchFEM Solid mesh to PyVista UnstructuredGrid.
This function enables visualization and export of TorchFEM finite element meshes using PyVista. It supports both tetrahedral and hexahedral elements with linear and quadratic shape functions.
- Parameters:
Solid (torchfem.Solid) – TorchFEM solid mesh object containing nodes, elements, and element type.
- Returns:
PyVista mesh representation suitable for visualization and I/O.
- Return type:
pyvista.UnstructuredGrid
- Raises:
NotImplementedError – If input is not a torchfem.Solid object.
Notes
Supported element types: - Tetra1: 4-node linear tetrahedron - Tetra2: 10-node quadratic tetrahedron - Hexa1: 8-node linear hexahedron - Hexa2: 20-node quadratic hexahedron
Examples
>>> from DeepSDFStruct.optimization import get_mesh_from_torchfem >>> import torchfem >>> >>> # Assume we have a TorchFEM solid mesh >>> # solid = torchfem.Solid(...) >>> >>> # Convert to PyVista for visualization >>> pv_mesh = get_mesh_from_torchfem(solid) >>> pv_mesh.plot()
- DeepSDFStruct.optimization.tet_signed_vol(vertices, tets)#
Compute signed volumes of tetrahedral elements.
Calculates the signed volume of each tetrahedron, which is positive for correctly oriented elements and negative for inverted elements. This is useful for detecting mesh degeneracies and enforcing mesh quality constraints.
- Parameters:
vertices (torch.Tensor) – Vertex coordinates of shape (N, 3).
tets (torch.Tensor) – Tetrahedral connectivity of shape (M, 4), where each row contains vertex indices [v0, v1, v2, v3].
- Returns:
Signed volumes of shape (M,), one per tetrahedron. Positive volumes indicate correctly oriented elements.
- Return type:
torch.Tensor
Notes
- The signed volume is computed as:
V = (1/6) * ((v1-v0) × (v2-v0)) · (v3-v0)
Examples
>>> import torch >>> from DeepSDFStruct.optimization import tet_signed_vol >>> >>> # Define a simple tetrahedron >>> vertices = torch.tensor([ ... [0.0, 0.0, 0.0], ... [1.0, 0.0, 0.0], ... [0.0, 1.0, 0.0], ... [0.0, 0.0, 1.0] ... ]) >>> tets = torch.tensor([[0, 1, 2, 3]]) >>> volumes = tet_signed_vol(vertices, tets) >>> print(f"Volume: {volumes[0]:.3f}") # Should be 1/6 ≈ 0.167