| name | hierarchical-topopt |
| description | Run fast 2D SIMP topology optimization with flexible boundary conditions, lightweight STL exports, and optional STL turntable renderings. Use when asked to optimize cantilevers, beams, bridges, plates, tension/shear strips, custom support/load layouts, density fields, compliance minimization, profile/flat/multimaterial STL meshes, polished STL previews, or rotating GIFs of optimized geometry. Produces density plots/data, boundary-condition previews and resolved node/DOF JSON, optimization history, summary files, optional STL meshes, and optional STL render artifacts by running bundled Python scripts. |
| license | 3-clause BSD license |
| metadata | {"version":"1.0","skill-author":"Markus J. Buehler, Massachusetts Institute of Technology (MIT), Laboratory for Atomistic and Molecular Mechanics (LAMM)"} |
Hierarchical TopOpt
Run a headless 2D minimum-compliance topology optimization workflow with flexible
supports and loads. Always use the bundled script; do not rewrite the optimizer
from scratch.
This skill intentionally does not generate gyroid, hole, voxel microstructure,
marching-cubes, or PyVista boolean meshes. Keep it fast and robust for local
agent execution.
Required Workflow
- Create or use an output directory, usually
skill_output_<timestamp>_hierarchical-topopt.
- Select a boundary condition preset or write
boundary_conditions.json.
- Run
scripts/run_hierarchical_topopt.py.
- Verify artifacts with
find.
- Final answer must list exact artifact paths and report compliance, final volume fraction, fixed DOFs, loaded DOFs, and total force from
summary.json.
Default command:
python3 skills/hierarchical-topopt/scripts/run_hierarchical_topopt.py \
--out skill_output_<timestamp>_hierarchical-topopt \
--title "Hierarchical Topology Optimization" \
--nelx 90 \
--nely 30 \
--volfrac 0.55 \
--penal 4.0 \
--rmin 4.5 \
--filter density \
--maxiter 200 \
--bc-preset cantilever-mid-down \
--mesh-mode profile-stl \
--dens-cut 0.30 \
--max-height 10 \
--profile-origin bottom
Core Parameters
--nelx, --nely: number of finite elements in x/y.
--volfrac: target volume fraction.
--penal: SIMP penalization.
--rmin: density/sensitivity filter radius.
--filter: density or sensitivity.
--maxiter: maximum optimizer iterations.
--bc-preset: named support/load preset.
--bc-json: file containing custom boundary conditions.
--bc-json-string: inline custom boundary-condition JSON.
--mesh-mode: density-only, profile-stl, flat-stl, multimaterial-stl, or all-basic.
--resize-scale: scale density field before STL export.
--dens-cut: densities at or below this value are void for STL export.
--max-height: STL z-height.
--profile-origin: bottom creates a one-sided profile from z=0 to
density * max-height; center creates a symmetric profile from
-0.5 * density * max-height to +0.5 * density * max-height. Applies to
profile-stl; flat and multimaterial STL exports remain bottom-anchored.
--material-cuts: comma-separated density bands for multimaterial export, e.g. 0.2,0.55,0.8,1.0.
Boundary Condition Model
Use normalized geometry selectors whenever the user describes locations in words.
The script resolves them to exact node IDs and DOFs and writes
boundary_conditions_resolved.json.
Coordinate convention:
elements: nelx by nely
nodes: (nelx + 1) by (nely + 1)
i: 0..nelx, left to right
j: 0..nely, bottom to top
node_id(i,j) = i * (nely + 1) + j
ux_dof = 2 * node_id
uy_dof = 2 * node_id + 1
Boundary condition JSON:
{
"supports": [
{"selector": "left_edge", "ux": true, "uy": true}
],
"loads": [
{"selector": "right_mid", "fx": 0.0, "fy": -1.0, "distribution": "total"}
]
}
Always prefer distribution: "total" for loads unless the user explicitly says
force per node. A total load is split across selected nodes.
Critical load selector rules:
top_mid means one top-center node, not a distributed line load.
top_edge means the entire top edge.
- "middle 20 percent of the top edge" must use
{"type": "edge_fraction", "edge": "top", "start": 0.4, "end": 0.6}.
- For "middle P percent" of an edge, use
edge_fraction with
start = (1 - P/100) / 2 and end = (1 + P/100) / 2.
- For small local models, prefer a matching preset over custom JSON when one
exists.
Selector Types
String aliases:
left_edge, right_edge, top_edge, bottom_edge
left_mid, right_mid, top_mid, bottom_mid, center
left_bottom, right_bottom, left_top, right_top
bottom_left, bottom_right, top_left, top_right
node:i,j
Structured selectors:
{"type": "node", "i": 90, "j": 15}
{"type": "node_fraction", "x": 1.0, "y": 0.5}
{"type": "edge", "edge": "left"}
{"type": "edge_fraction", "edge": "top", "start": 0.4, "end": 0.6}
{"type": "x_range_fraction", "x0": 0.7, "x1": 1.0}
{"type": "y_range_fraction", "y0": 0.0, "y1": 0.1}
{"type": "box_fraction", "x0": 0.4, "x1": 0.6, "y0": 0.95, "y1": 1.0}
{"type": "line_fraction", "x0": 0.0, "y0": 0.5, "x1": 1.0, "y1": 0.5, "tol": 0.02}
{"type": "circle_fraction", "x": 0.5, "y": 0.5, "r": 0.05}
Translation Examples
Use these examples to convert user text into boundary_conditions.json.
Fix the left edge:
{"supports": [{"selector": "left_edge", "ux": true, "uy": true}], "loads": []}
Pin lower left and roller lower right:
{
"supports": [
{"selector": "left_bottom", "ux": true, "uy": true},
{"selector": "right_bottom", "ux": false, "uy": true}
],
"loads": []
}
Fix all nodes 70 percent to the right:
{
"supports": [
{"selector": {"type": "x_range_fraction", "x0": 0.7, "x1": 1.0}, "ux": true, "uy": true}
],
"loads": []
}
Fix the bottom 10 percent strip:
{
"supports": [
{"selector": {"type": "y_range_fraction", "y0": 0.0, "y1": 0.1}, "ux": true, "uy": true}
],
"loads": []
}
Load the middle 20 percent of the top edge downward:
{
"supports": [{"selector": "left_edge", "ux": true, "uy": true}],
"loads": [
{"selector": {"type": "edge_fraction", "edge": "top", "start": 0.4, "end": 0.6}, "fx": 0.0, "fy": -1.0, "distribution": "total"}
]
}
Bridge with pin/roller supports and a distributed top load:
{
"supports": [
{"selector": {"type": "node_fraction", "x": 0.0, "y": 0.0}, "ux": true, "uy": true},
{"selector": {"type": "node_fraction", "x": 1.0, "y": 0.0}, "ux": false, "uy": true}
],
"loads": [
{"selector": {"type": "edge_fraction", "edge": "top", "start": 0.4, "end": 0.6}, "fx": 0.0, "fy": -1.0, "distribution": "total"}
]
}
Shear the right edge:
{
"supports": [{"selector": "left_edge", "ux": true, "uy": true}],
"loads": [{"selector": "right_edge", "fx": 1.0, "fy": 0.0, "distribution": "total"}]
}
Push a square patch near the upper-right corner:
{
"supports": [{"selector": "left_edge", "ux": true, "uy": true}],
"loads": [
{"selector": {"type": "box_fraction", "x0": 0.8, "x1": 1.0, "y0": 0.8, "y1": 1.0}, "fx": 1.0, "fy": 0.0, "distribution": "total"}
]
}
Load the center point downward:
{
"supports": [{"selector": "bottom_edge", "ux": true, "uy": true}],
"loads": [{"selector": {"type": "node_fraction", "x": 0.5, "y": 0.5}, "fx": 0.0, "fy": -1.0, "distribution": "total"}]
}
Presets
Use presets for common cases:
cantilever-tip-down
cantilever-mid-down
left-fixed-top-mid-20-down
bridge-center-load
bridge-top-mid-20-down
simply-supported-center-load
fixed-fixed-center-load
fixed-fixed-top-mid-20-down
tension-strip
shear-strip
Use --bc-json boundary_conditions.json when the user describes a custom setup.
When using custom boundary conditions, write the JSON file first, then pass it to
the script.
Outputs
Always written:
density.png
density_resized.png
density.npy
density_resized.npy
density.csv
optimization_history.csv
boundary_conditions.json
boundary_conditions_resolved.json
bc_preview.png
convergence.png
summary.json
parameters.json
README.md
Mesh outputs depend on --mesh-mode:
result_profile.stl
result_flat.stl
MAT1.stl, MAT2.stl, ...
STL Turntable Rendering
When the user asks for a 3D render, preview image, social-post visual, or movie
of an STL, use scripts/render_stl_turntable.py after generating or receiving
the STL. The renderer is standalone and accepts any ASCII or binary STL file,
including result_profile.stl, result_flat.stl, and existing user-provided
STLs.
Example:
python3 skills/hierarchical-topopt/scripts/render_stl_turntable.py \
skill_output_<timestamp>_hierarchical-topopt/result_profile.stl \
--out skill_output_<timestamp>_hierarchical-topopt/stl_turntable \
--title "Optimized Bridge Geometry" \
--material titanium \
--background dark \
--frames 48 \
--fps 18 \
--dpi 140
For a standalone STL outside this skill:
python3 skills/hierarchical-topopt/scripts/render_stl_turntable.py \
/path/to/existing_model.stl \
--out stl_turntable_render \
--title "Existing STL" \
--material blue-steel \
--background studio
Renderer options:
--material: blue-steel, ceramic, gold, graphite, polymer, or titanium.
--background: dark, light, or studio.
--frames, --fps, --dpi, and --figsize: control animation quality.
--max-faces: deterministic face downsampling limit for large STLs.
--edge-alpha: triangle edge visibility; keep low for polished renders.
Renderer outputs:
turntable.gif
preview.png
frames/frame_*.png
render_manifest.json
README.md
Do not claim success unless find lists the density image, BC preview, resolved
BC JSON, optimization history, summary, parameters, README, and requested mesh
files.
If an STL render is requested, also verify the render output directory and list
turntable.gif, preview.png, frames/, render_manifest.json, and
README.md.