# Hierarchical Topopt

> 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.

- Skill: `lamm-mit/hierarchical-topopt` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add lamm-mit/hierarchical-topopt`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lamm-mit/hierarchical-topopt/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: 3-clause BSD license
- Author: lamm-mit (https://skillmd.com/u/lamm-mit)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lamm-mit/hierarchical-topopt

---


# 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

1. Create or use an output directory, usually `skill_output_<timestamp>_hierarchical-topopt`.
2. Select a boundary condition preset or write `boundary_conditions.json`.
3. Run `scripts/run_hierarchical_topopt.py`.
4. Verify artifacts with `find`.
5. 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:

```bash
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:

```text
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:

```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:

```json
{"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:

```json
{"supports": [{"selector": "left_edge", "ux": true, "uy": true}], "loads": []}
```

Pin lower left and roller lower right:

```json
{
  "supports": [
    {"selector": "left_bottom", "ux": true, "uy": true},
    {"selector": "right_bottom", "ux": false, "uy": true}
  ],
  "loads": []
}
```

Fix all nodes 70 percent to the right:

```json
{
  "supports": [
    {"selector": {"type": "x_range_fraction", "x0": 0.7, "x1": 1.0}, "ux": true, "uy": true}
  ],
  "loads": []
}
```

Fix the bottom 10 percent strip:

```json
{
  "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:

```json
{
  "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:

```json
{
  "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:

```json
{
  "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:

```json
{
  "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:

```json
{
  "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:

```bash
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:

```bash
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`.

