Guide
STL simplification
The plant sums force on a short list of flat plates. A CAD mesh is the wrong list: it is tens or millions of triangles, often with interior faces, and a generic decimator opens cracks between sides. geometry.py finds the spacecraft’s sides, fits a plane to each, and writes a sealed plate file.
Command
python main.py sim geometry --in craft.stl --quality 1pct
python main.py sim geometry --in part.step --quality 1pct
python main.py sim geometry --in craft.obj --units m
Output is outputs/results/geometry/<name>_<quality>.geom. Each data line is seven numbers: unit normal, area in m², centroid in metres.
| Input | Length unit |
|---|---|
.stl, .obj | Millimetres, unless you pass --units m, cm, or in |
.step, .stp | The file’s own unit. trimesh and cascadio return metres. --units must stay at metres |
What the algorithm does
The implementation is simplify_sides in python/arlamx_v2/geometry.py. It is a side finder, in the sense of a variational shape approximation that groups triangles into planar proxies, followed by an ear clip of each sealed outline.
- Load and clean. Vertices are scaled into metres. STEP meshes have their vertices welded. Any triangle with area under 10⁻¹⁵ m² is dropped.
- Neighbours. Two triangles are adjacent when they share an edge.
- Grow a side. A triangle joins its neighbour’s side when the angle between their normals is inside a cone. The cone depends on the quality name.
- Fit a plane. One area-weighted plane per side. The normal is flipped so it points the same way as the average of the member normals (outward on a consistent mesh).
- Seal the corners. An edge that belongs to exactly two sides is moved onto the line where those two planes intersect. Vertices of that edge are projected onto the line. Plane fitting would otherwise open a gap, and a gap is a missing strip of area.
- Emit plates. If the side is flat enough, the boundary is ear-clipped into triangles and kept only when the new area matches the original area inside the outline tolerance. Otherwise the original triangles are kept. Nearly coplanar triangles then share the fitted normal. Each output triangle becomes one plate: normal, area, centroid.
- Report. You get the number of sides, the number of plates, the area in and out, and the length of boundary edges that should have been shared and are not. A closed shell should report an unmatched length of zero.
Quality
| Name | Normal cone | Area budget | When |
|---|---|---|---|
1pct | 8° | 1% | The one to use |
5pct | 12° | 5% | A coarser look |
10pct | 22° | 10% | A crude smoke test |
The outline replaces a side only when its area is already close to the source triangles (outline_ok is 0.5%, 3%, and 8% for the three names). Outside that, the source triangles stay, so a curved membrane is not silently replaced by a smaller polygon. On SolarCat, python main.py sim validate_attitudes --quality 1pct also checks wetted area and ram area at six attitudes against the STL.
python main.py sim plot_simplify
# cube (12 triangles → 6 sides) and a hex prism, under outputs/results/geometry/
Limits worth knowing before you trust the area
- Sides are merged by normal, not by CAD part. Two coplanar faces of different materials become one side. Split the STEP bodies and simplify each one if you need to keep that distinction. The
.geomfile has no material column. - There is no flow shadowing and no sun shadowing downstream. Interior triangles that survive into the plate file still produce force. A mesh of a solid should be the exterior surface.
- On
SolarCat_v3.stl,simplify_sideshas produced sliver plates whose centroids sit far from the vehicle. The 176-plate model indata/SolarCat_v3_1pct_plates.geomwas built by a separate clustering pass for that reason (CHANGELOG_v2.7.md). Check centroids after a new mesh. - A unit cube should come back as 6 sides, area 6, sealed. That case is
tests/geometry/test_simplify.py.