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:densityorsensitivity.--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, orall-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:bottomcreates a one-sided profile fromz=0todensity * max-height;centercreates a symmetric profile from-0.5 * density * max-heightto+0.5 * density * max-height. Applies toprofile-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_midmeans one top-center node, not a distributed line load.top_edgemeans 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_fractionwithstart = (1 - P/100) / 2andend = (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_edgeleft_mid,right_mid,top_mid,bottom_mid,centerleft_bottom,right_bottom,left_top,right_topbottom_left,bottom_right,top_left,top_rightnode: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-downcantilever-mid-downleft-fixed-top-mid-20-downbridge-center-loadbridge-top-mid-20-downsimply-supported-center-loadfixed-fixed-center-loadfixed-fixed-top-mid-20-downtension-stripshear-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.pngdensity_resized.pngdensity.npydensity_resized.npydensity.csvoptimization_history.csvboundary_conditions.jsonboundary_conditions_resolved.jsonbc_preview.pngconvergence.pngsummary.jsonparameters.jsonREADME.md
Mesh outputs depend on --mesh-mode:
result_profile.stlresult_flat.stlMAT1.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, ortitanium.--background:dark,light, orstudio.--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.gifpreview.pngframes/frame_*.pngrender_manifest.jsonREADME.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.