loadpath 0.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- loadpath/README.md +294 -0
- loadpath/SKILL.md +584 -0
- loadpath/__init__.py +17 -0
- loadpath/__main__.py +1202 -0
- loadpath/am.py +316 -0
- loadpath/amgfast.py +326 -0
- loadpath/assembly.py +985 -0
- loadpath/benchmarks/__init__.py +0 -0
- loadpath/benchmarks/beams.py +266 -0
- loadpath/benchmarks/cases/ge-bracket.json +380 -0
- loadpath/benchmarks/evaluate.py +172 -0
- loadpath/benchmarks/mechanisms.py +416 -0
- loadpath/benchmarks/problems.py +140 -0
- loadpath/benchmarks/reference/bridge-3d.top3d.npz +0 -0
- loadpath/benchmarks/reference/cantilever-3d.top3d.npz +0 -0
- loadpath/benchmarks/reference/lbracket-2d.top88-ft2.npz +0 -0
- loadpath/benchmarks/reference/mbb-2d.top88-ft1.npz +0 -0
- loadpath/benchmarks/reference/mbb-2d.top88-ft2.npz +0 -0
- loadpath/benchmarks/reference.py +138 -0
- loadpath/benchmarks/run.py +301 -0
- loadpath/brep.py +1470 -0
- loadpath/bucklingopt.py +300 -0
- loadpath/buildsim.py +1490 -0
- loadpath/cadimport.py +904 -0
- loadpath/calibration.py +885 -0
- loadpath/cavities.py +211 -0
- loadpath/checks/__init__.py +0 -0
- loadpath/checks/dfm.py +354 -0
- loadpath/checks/exchanger.py +7 -0
- loadpath/checks/fea.py +212 -0
- loadpath/checks/fit.py +475 -0
- loadpath/checks/networks.py +260 -0
- loadpath/checks/opening.py +67 -0
- loadpath/checks/sdf_kit.py +7 -0
- loadpath/conjugate.py +267 -0
- loadpath/constrained.py +329 -0
- loadpath/contact.py +691 -0
- loadpath/coopt.py +342 -0
- loadpath/cores.py +163 -0
- loadpath/crease.py +1138 -0
- loadpath/critic.py +212 -0
- loadpath/crosscheck.py +1107 -0
- loadpath/cutcell.py +510 -0
- loadpath/data/lattice_buckling.json +439 -0
- loadpath/data/lattice_tensors.json +27449 -0
- loadpath/data/published_experiments.json +71 -0
- loadpath/decimate.py +177 -0
- loadpath/delivery.py +190 -0
- loadpath/directions.py +262 -0
- loadpath/domain.py +630 -0
- loadpath/drains.py +615 -0
- loadpath/durability.py +413 -0
- loadpath/editor.html +3135 -0
- loadpath/editor.py +610 -0
- loadpath/escape.py +354 -0
- loadpath/evals/__init__.py +0 -0
- loadpath/evals/baselines/coarse.json +1543 -0
- loadpath/evals/baselines/full.json +1497 -0
- loadpath/evals/brief_kinds/__init__.py +1 -0
- loadpath/evals/brief_kinds/heatx.py +387 -0
- loadpath/evals/brief_kinds/lattice.py +571 -0
- loadpath/evals/brief_kinds/multimat.py +593 -0
- loadpath/evals/briefs/assets/bracket.step +1201 -0
- loadpath/evals/briefs/assets/desk_arm.urdf +22 -0
- loadpath/evals/briefs/assets/elbow-gearbox.stl +0 -0
- loadpath/evals/briefs/assets/motor-body.stl +0 -0
- loadpath/evals/briefs/assets/sketch.png +0 -0
- loadpath/evals/briefs/base-adapter-fdm.json +492 -0
- loadpath/evals/briefs/bell-crank-lpbf.json +180 -0
- loadpath/evals/briefs/belt-tensioner-fdm.json +151 -0
- loadpath/evals/briefs/camera-boom-mjf.json +339 -0
- loadpath/evals/briefs/camera-mount-pla.json +203 -0
- loadpath/evals/briefs/coolant-isolator-petg.json +100 -0
- loadpath/evals/briefs/elbow-bearing-bracket-sls.json +300 -0
- loadpath/evals/briefs/elbow-gearbox-forearm-mjf.json +558 -0
- loadpath/evals/briefs/elbow-link-fatigue-fdm.json +251 -0
- loadpath/evals/briefs/elbow-stop-pad-tpu.json +39 -0
- loadpath/evals/briefs/forearm-fdm.json +146 -0
- loadpath/evals/briefs/forearm-servo-link-fdm.json +327 -0
- loadpath/evals/briefs/grip-pad-finger-mm.json +354 -0
- loadpath/evals/briefs/gripper-bracket-creep-fdm.json +340 -0
- loadpath/evals/briefs/gripper-finger-fdm.json +155 -0
- loadpath/evals/briefs/gripper-jaw-fatigue-mjf.json +328 -0
- loadpath/evals/briefs/gripper-palm-fdm.json +715 -0
- loadpath/evals/briefs/hot-stepper-mount-asa-fdm.json +471 -0
- loadpath/evals/briefs/hpu-oil-cooler-lpbf.json +121 -0
- loadpath/evals/briefs/isolated-camera-mount-mm.json +675 -0
- loadpath/evals/briefs/kart-charge-cooler-lpbf.json +93 -0
- loadpath/evals/briefs/lighten-step-bracket.json +220 -0
- loadpath/evals/briefs/nema17-mount-stl.json +429 -0
- loadpath/evals/briefs/pillow-block-sls.json +220 -0
- loadpath/evals/briefs/polyjet-fingertip-mm.json +305 -0
- loadpath/evals/briefs/rod-hook-pla.json +154 -0
- loadpath/evals/briefs/sensor-arm-resin.json +150 -0
- loadpath/evals/briefs/shelf-bracket-pla.json +158 -0
- loadpath/evals/briefs/shoulder-riser-mjf.json +487 -0
- loadpath/evals/briefs/sketch-clevis.json +229 -0
- loadpath/evals/briefs/spring-bracket-pacf.json +294 -0
- loadpath/evals/briefs/tool-flange-ti64.json +280 -0
- loadpath/evals/briefs/tray-arm-lattice-fdm.json +260 -0
- loadpath/evals/briefs/turret-fdm.json +281 -0
- loadpath/evals/briefs/upper-arm-sls.json +179 -0
- loadpath/evals/briefs/urdf-forearm.json +233 -0
- loadpath/evals/briefs/wrist-link-mjf.json +278 -0
- loadpath/evals/briefs/wrist-yoke-mjf.json +245 -0
- loadpath/evals/briefs.py +393 -0
- loadpath/evals/cases/arm-link-cast.json +157 -0
- loadpath/evals/cases/arm-link-driven.json +171 -0
- loadpath/evals/cases/arm-link-sandwich.json +315 -0
- loadpath/evals/cases/arm-link.json +111 -0
- loadpath/evals/cases/bell-crank.json +116 -0
- loadpath/evals/cases/grip-turret.json +479 -0
- loadpath/evals/cases/grip-upper-arm.json +253 -0
- loadpath/evals/cases/grip-wrist-link-mjf.json +929 -0
- loadpath/evals/cases/grip-wrist-yoke.json +831 -0
- loadpath/evals/cases/lpbf-intercooler-core.json +59 -0
- loadpath/evals/cases/mbb-beam.json +61 -0
- loadpath/evals/cases/motor-body.stl +0 -0
- loadpath/evals/cases/motor-mount.json +265 -0
- loadpath/evals/cases/printed-hook.json +77 -0
- loadpath/evals/cases/wall-bracket-fasteners.json +125 -0
- loadpath/evals/cases/wall-bracket.json +126 -0
- loadpath/evals/corpus.py +179 -0
- loadpath/evals/pareto.py +90 -0
- loadpath/evals/run.py +860 -0
- loadpath/examples/arm-link.json +25 -0
- loadpath/examples/bumpered-bracket-compression.json +225 -0
- loadpath/examples/bumpered-bracket-lattice.json +204 -0
- loadpath/examples/bumpered-bracket.json +34 -0
- loadpath/examples/gripper-finger-mm.json +185 -0
- loadpath/examples/heat-exchanger-counterflow.json +21 -0
- loadpath/examples/heat-exchanger-crossflow-tuned.json +48 -0
- loadpath/examples/heat-exchanger-crossflow.json +20 -0
- loadpath/examples/pedal-lattice.json +23 -0
- loadpath/examples/pressure-plenum.json +12 -0
- loadpath/examples/shelf-bracket.json +27 -0
- loadpath/exchanger.py +831 -0
- loadpath/fasteners.py +199 -0
- loadpath/fem.py +668 -0
- loadpath/femops.py +70 -0
- loadpath/fieldjit.py +981 -0
- loadpath/fieldops.py +119 -0
- loadpath/fields.py +3697 -0
- loadpath/fieldtorch.py +464 -0
- loadpath/flow.py +984 -0
- loadpath/flowtopo.py +300 -0
- loadpath/gpu.py +521 -0
- loadpath/heatx.py +1067 -0
- loadpath/heatx_thermal.py +432 -0
- loadpath/homogenized.py +886 -0
- loadpath/ibrep.py +552 -0
- loadpath/impact.py +693 -0
- loadpath/infill.py +376 -0
- loadpath/interface_library.py +340 -0
- loadpath/interlock.py +186 -0
- loadpath/isolator.py +149 -0
- loadpath/knn.py +204 -0
- loadpath/lattice.py +1065 -0
- loadpath/lattice3mf.py +760 -0
- loadpath/lattice_buckling.py +245 -0
- loadpath/lattice_grade.py +325 -0
- loadpath/lattice_orient.py +595 -0
- loadpath/lattice_print.py +159 -0
- loadpath/lattice_tensors.py +558 -0
- loadpath/latticestep.py +1210 -0
- loadpath/manufacture.py +446 -0
- loadpath/material_library.py +489 -0
- loadpath/materials.py +144 -0
- loadpath/mcp_server.py +786 -0
- loadpath/mechanism.py +218 -0
- loadpath/mechanism_nl.py +811 -0
- loadpath/meshops.py +452 -0
- loadpath/mg.py +853 -0
- loadpath/mma.py +214 -0
- loadpath/modal.py +210 -0
- loadpath/multimat.py +1788 -0
- loadpath/narrowband.py +418 -0
- loadpath/nlkernel.py +241 -0
- loadpath/nonlinear.py +1095 -0
- loadpath/orient.py +334 -0
- loadpath/porous.py +994 -0
- loadpath/pressure.py +171 -0
- loadpath/preview.py +335 -0
- loadpath/printcheck.py +263 -0
- loadpath/printsetup.py +207 -0
- loadpath/processcheck.py +376 -0
- loadpath/quick.py +113 -0
- loadpath/recall.py +406 -0
- loadpath/reconstruct.py +797 -0
- loadpath/render.py +436 -0
- loadpath/resolved.py +189 -0
- loadpath/schema.py +1701 -0
- loadpath/sdf_kit.py +843 -0
- loadpath/selfx.py +292 -0
- loadpath/shapes.py +584 -0
- loadpath/sharp.py +717 -0
- loadpath/simp.py +1170 -0
- loadpath/sizing.py +312 -0
- loadpath/slicer.py +321 -0
- loadpath/spec.py +1072 -0
- loadpath/step.py +806 -0
- loadpath/struts.py +639 -0
- loadpath/study.py +1937 -0
- loadpath/tasks.py +822 -0
- loadpath/thermal.py +1691 -0
- loadpath/threemf.py +487 -0
- loadpath/tune.py +437 -0
- loadpath/validation.py +145 -0
- loadpath/verify.py +1215 -0
- loadpath/viewer.html +694 -0
- loadpath/viewer.py +446 -0
- loadpath/viscoelastic.py +106 -0
- loadpath/voxelprint.py +582 -0
- loadpath/voxels.py +402 -0
- loadpath-0.1.1.dist-info/METADATA +319 -0
- loadpath-0.1.1.dist-info/RECORD +220 -0
- loadpath-0.1.1.dist-info/WHEEL +5 -0
- loadpath-0.1.1.dist-info/entry_points.txt +2 -0
- loadpath-0.1.1.dist-info/licenses/LICENSE +21 -0
- loadpath-0.1.1.dist-info/top_level.txt +1 -0
loadpath/README.md
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
# loadpath
|
|
2
|
+
|
|
3
|
+
Generative design for parts you are going to print. Describe what a part must
|
|
4
|
+
keep, what it must avoid, where it is held and how hard it is pushed; loadpath
|
|
5
|
+
removes material until only the load path is left, meshes the result as a
|
|
6
|
+
watertight printable part with exact bores and faces, and then checks that part
|
|
7
|
+
with a fresh finite element analysis in real units.
|
|
8
|
+
|
|
9
|
+
The methods are standard and published, and the code is meant to be read.
|
|
10
|
+
What each model has been checked against is in
|
|
11
|
+
[docs/VALIDATION.md](../docs/VALIDATION.md); nothing has been physically
|
|
12
|
+
tested yet.
|
|
13
|
+
|
|
14
|
+
- **SIMP topology optimization** on a voxel grid: density per cell, penalized
|
|
15
|
+
stiffness, a cone density filter for a minimum member size, Heaviside
|
|
16
|
+
projection with continuation for crisp 0/1 designs, optimality-criteria
|
|
17
|
+
updates, multiple load cases, symmetry planes.
|
|
18
|
+
- **Overhang control** for a chosen build direction (Langelaar's AM filter):
|
|
19
|
+
material that would need support earns nothing, so parts grow their own
|
|
20
|
+
45° supports. Any direction, including tilted ones (`am.TiltedAMFilter`).
|
|
21
|
+
- **Print-aware design** (`process`): layered (transversely isotropic)
|
|
22
|
+
stiffness about the build direction in verification, with the layer bond
|
|
23
|
+
checked; the safety-factor search sizes the part against it, and the
|
|
24
|
+
optimizer can design with the layered stiffness too (`anisotropic`, off by
|
|
25
|
+
default: on the motor mount and hook it did not beat isotropic, see am.py). `"buildDirection": "auto"` compares the six directions on a coarse
|
|
26
|
+
grid by successive halving (all half-way, the best three finished) and keeps the strongest per gram
|
|
27
|
+
(`orient.py`). Every layered result
|
|
28
|
+
is sliced like a slicer would (`printcheck.py`: bridges up to 15 mm are
|
|
29
|
+
fine, mid-air islands, support volume, bed contact and tipping) and written
|
|
30
|
+
as a 3MF standing the way it was designed to print (`threemf.py`).
|
|
31
|
+
- **Durability for printed plastics** (`durability.py`): load cases with
|
|
32
|
+
`cycles` get a fatigue life (Ezeh and Susmel's design curve for printed
|
|
33
|
+
PLA, max stress in the cycle, Miner's rule across cases, the layer bond
|
|
34
|
+
checked in fatigue too), cases with `hours` get creep rupture and crept
|
|
35
|
+
deflection, and fasteners report how much preload relaxes away, all at the
|
|
36
|
+
service temperature through the modulus-retention tables. Optional
|
|
37
|
+
`durability.constrain` sizes the part for them.
|
|
38
|
+
- **Minimum member size** (robust formulation): stiffness is credited to the
|
|
39
|
+
eroded design and the delivered part is the opened one (robust
|
|
40
|
+
formulation), so no sub-minimum skins or webs.
|
|
41
|
+
- **MMA** (Svanberg's Method of Moving Asymptotes, implemented here from the
|
|
42
|
+
published method) for minimum mass subject to stress and deflection
|
|
43
|
+
limits, with relaxed, adaptively normalized P-mean stress aggregation and
|
|
44
|
+
adjoint sensitivities.
|
|
45
|
+
- **Linear elastic FEA** with 8-node hexahedra, solved by conjugate gradient
|
|
46
|
+
with a smoothed-aggregation multigrid preconditioner (pyamg). Checked against
|
|
47
|
+
Euler-Bernoulli beam theory in the tests.
|
|
48
|
+
- **Exact reconstruction**: marching cubes on the smoothed density, then
|
|
49
|
+
manifold3d booleans that add back the preserved geometry and cut the
|
|
50
|
+
keep-outs exactly, so a 22mm bearing seat is 22mm.
|
|
51
|
+
- **Verification of the delivered mesh**, not the density field: mass, peak
|
|
52
|
+
von Mises stress and safety factor per load case, deflection, keep-out
|
|
53
|
+
intrusion and preserve loss (exact booleans), body count, thinnest member.
|
|
54
|
+
|
|
55
|
+
Units are fixed: millimetres, newtons, megapascals, g/cm³.
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install "loadpath[render]"
|
|
61
|
+
python -m loadpath check study.json # validate, show the voxel problem
|
|
62
|
+
python -m loadpath run study.json --out out # optimize, mesh, verify, render
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`out/` gets one STL and one render per outcome, and `report.json` with the
|
|
66
|
+
numbers. Progress streams to stderr as one JSON object per line.
|
|
67
|
+
|
|
68
|
+
### For agents (MCP)
|
|
69
|
+
|
|
70
|
+
`pip install "loadpath[mcp]"`, then add a local server to any MCP client
|
|
71
|
+
(Claude Code, Cursor, Claude Desktop):
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{"mcpServers": {"loadpath": {"command": "loadpath", "args": ["mcp"]}}}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Tools: `loadpath_guide` (the study format), `loadpath_schema`,
|
|
78
|
+
`loadpath_examples`, `loadpath_check`, `loadpath_start`, `loadpath_status`
|
|
79
|
+
(outcomes, STL paths and renders), `loadpath_lattice`,
|
|
80
|
+
`loadpath_heat_exchanger`, `loadpath_size_heat_exchanger`, `loadpath_jobs`,
|
|
81
|
+
`loadpath_cancel`, and checks on any mesh file: `loadpath_check_printability`,
|
|
82
|
+
`loadpath_check_isolation`, `loadpath_check_fit`, and `loadpath_import_cad` (a STEP/STL part as a draft study). Everything runs on the agent's own machine; jobs live in
|
|
83
|
+
`~/.loadpath/jobs` (`LOADPATH_HOME`). See [docs/agents.md](../docs/agents.md).
|
|
84
|
+
|
|
85
|
+
From Python:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from loadpath.spec import study_from_dict
|
|
89
|
+
from loadpath.study import run_study
|
|
90
|
+
|
|
91
|
+
result = run_study(study_from_dict(study))
|
|
92
|
+
best = result.outcomes[result.best]
|
|
93
|
+
best.mesh.export("part.stl")
|
|
94
|
+
print(best.report["mass"], best.report["safetyFactor"])
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### See it in 3D
|
|
98
|
+
|
|
99
|
+
`loadpath.viewer` writes one HTML file a person can open in any browser: orbit
|
|
100
|
+
the part, colour it by von Mises stress, safety factor or displacement for
|
|
101
|
+
each load case (or the worst of them), scrub or animate an exaggerated
|
|
102
|
+
deformation, cut it open with a section plane, hover to read the value under
|
|
103
|
+
the pointer, put two outcomes (or the part it replaces) side by side, and play
|
|
104
|
+
the optimization back iteration by iteration, every run of a safety-factor
|
|
105
|
+
search in order.
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from loadpath.viewer import HistoryRecorder, study_viewer
|
|
109
|
+
|
|
110
|
+
rec = HistoryRecorder() # keeps the density field every 2 iterations
|
|
111
|
+
result = run_study(study, on_progress=rec)
|
|
112
|
+
study_viewer(result, "part.html", history=rec, outcomes="all",
|
|
113
|
+
baseline="old-part.stl") # optional: verified under the same loads
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The page carries its own data (a few MB) and loads three.js from a CDN.
|
|
117
|
+
`heat_exchanger_viewer(mesh, spaces, report, "hx.html")` does the same for
|
|
118
|
+
`design_heat_exchanger(..., fluid_meshes=True)`: each fluid is a layer, and
|
|
119
|
+
the section plane shows the two networks interleave. `Viewer` is the scene
|
|
120
|
+
underneath, for anything else (`add_part` with fields, `add_overlay`,
|
|
121
|
+
`add_arrow`, `add_study_context`).
|
|
122
|
+
|
|
123
|
+
## The study
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"name": "arm-link",
|
|
128
|
+
"designSpace": [{"type": "box", "min": [-16, -18, -12], "max": [156, 18, 12]}],
|
|
129
|
+
"preserve": [
|
|
130
|
+
{"id": "shoulder-boss", "shape": {"type": "cylinder", "center": [0, 0, 0], "axis": "z", "length": 24, "r": 16}},
|
|
131
|
+
{"id": "wrist-boss", "shape": {"type": "cylinder", "center": [140, 0, 0], "axis": "z", "length": 24, "r": 15}}
|
|
132
|
+
],
|
|
133
|
+
"keepOut": [
|
|
134
|
+
{"id": "shoulder-bearing", "shape": {"type": "cylinder", "center": [0, 0, 0], "axis": "z", "length": 30, "r": 11}},
|
|
135
|
+
{"id": "wrist-bearing", "shape": {"type": "cylinder", "center": [140, 0, 0], "axis": "z", "length": 30, "r": 11}},
|
|
136
|
+
{"id": "cable-channel", "shape": {"type": "cylinder", "p0": [30, 0, 0], "p1": [110, 0, 0], "r": 5}}
|
|
137
|
+
],
|
|
138
|
+
"supports": [{"region": "shoulder-bearing"}],
|
|
139
|
+
"loadCases": [
|
|
140
|
+
{"name": "payload", "loads": [{"region": "wrist-bearing", "force": [0, -20, 0]}]},
|
|
141
|
+
{"name": "side", "loads": [{"region": "wrist-bearing", "force": [0, 0, 8]}]}
|
|
142
|
+
],
|
|
143
|
+
"material": "pa12",
|
|
144
|
+
"goal": {"safetyFactor": 3.0},
|
|
145
|
+
"minMember": 3.0,
|
|
146
|
+
"symmetry": ["z"],
|
|
147
|
+
"resolution": {"maxElements": 40000}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
| Field | Meaning |
|
|
152
|
+
| --- | --- |
|
|
153
|
+
| `designSpace` | Shapes material may occupy (their union). |
|
|
154
|
+
| `preserve` | Geometry kept exactly: bosses, mounting faces, tabs. Pinned solid in the optimization and unioned back exactly. |
|
|
155
|
+
| `keepOut` | Volume that must stay empty: bores, bolt access, cable runs, mating parts, a fluid's volume. Cut exactly at the end. `wall: 1.5` keeps 1.5 mm of material round it, pinned solid (a duct's or plenum's skin; see pressure below). |
|
|
156
|
+
| `fasteners` | Optional bolted joints as interfaces: `{id, size: "M5", seat, direction, grip, end: "face"\|"thread"\|"none", headOn: "this"\|"mating"}`. A screw driven from the mating part into this one is `headOn: "mating", end: "thread"`. Adds a seat ring and a face ring or thread sleeve (preserve), the bore and the head and driver path (keep-outs, `<id>-bore`, `<id>-head`), and a self-balanced `preload` case, so the optimizer grows the clamp path instead of keeping a solid boss. Sizes: `hole`, `head`, `wall` (seat ring beyond the head), `ring` (ring thickness), `driver` (head path length, default 60 mm), `threadLength`, `insert`, `sleeveWall`, `preload` (N); `loadpath schema study` describes each. See `fasteners.py`. |
|
|
157
|
+
| `supports` | Where the part is held: a region id or an inline shape, with `fix` = `"all"` (default), axes like `"xz"`, or three booleans. Optional when every load is a pressure on a sealed cavity. |
|
|
158
|
+
| `skin` | Optional skin-and-rib: `1.2` or `{thickness: 1.2, open: ["-z"]}` keeps that many mm of the design space's outer surface as a solid wall (except on the `open` faces) and designs only what is behind it, so the optimizer grows ribs under a skin instead of a free-form part. |
|
|
159
|
+
| `loadCases` | Each a list of forces (N) on regions. The optimizer stiffens for all cases together; verification reports each. Optional `cycles` (times applied over the part's life: fatigue), `hours` (time held: creep) and `temperature` (C). |
|
|
160
|
+
| `durability` | Optional `{temperature, safetyFactor, constrain, preloadHours, fatigueStrength, fatigueSlope, creepRatio, creepExponent}`: the service temperature (default 23 C), the fatigue/creep safety factor target (default 1.5), whether the safety-factor search sizes for it (default false), how long fasteners hold preload (default 8760 h), and overrides for the material's durability data. See `durability.py` for sources. |
|
|
161
|
+
| `material` | A name (`pla`, `petg`, `abs`, `asa`, `tpu`, `pa-cf`, `pa12`, `pa11`, `pa12-gb`, `tpu88a`, `resin`, `resin-tough`, `resin-rigid`, `alsi10mg`, `ti64`, `316l`) or `{E, nu, strength, density}`. A list solves the goal for each and pools the outcomes. |
|
|
162
|
+
| `goal` | `{"volumeFraction": 0.25}` keeps that share of the design space. `{"safetyFactor": 3}` and/or `{"maxDeflection": 0.5}` (mm, at the loads) search for the lightest part that meets them in every case. `outcomes: [0.2, 0.4]` adds a trade-off gallery. `"method": "constrained"` (experimental) minimizes mass with the limits as MMA constraints instead of searching over volume fractions. |
|
|
163
|
+
| `extrusion` | Optional axis (`x`, `y`, `z`): the part is a profile extruded along it, cuttable from plate (waterjet, laser, 2.5-axis milling). |
|
|
164
|
+
| `gravity` | Optional `[gx, gy, gz]` in m/s²: the part's own weight in every load case. |
|
|
165
|
+
| `thermal` | Optional heat in the part, in every load case: `{ambient, stressFree, temperatures: [{region\|shape, t}], heat: [{region\|shape, watts, spread}], convection: h or [{h, region\|shape, fluid}], conductivity, expansion, soften, specificHeat, cycle: {period, on, cycles}}` (C, W, W/(m² K)). The part's own temperature softens it (the card's retention curve) and its expansion, where supports or cooler material stop it, is a load; the optimizer designs for both and verification checks stress with the free expansion taken off and strength derated. With `cycle` the heat is on for `on` seconds of every `period`: the repeating temperature cycle is solved, the part designed for its hot moment, checked hot and cold, and `cycles` count toward thermal-cycling fatigue (`fatigueWeight`, experimental, also designs against it; it does not yet hold up on finer grids) (durability). Examples: `examples/thermal/motor-mount-hot.json`, `examples/thermal/motor-mount-cycle.json`. |
|
|
166
|
+
| `process` | Optional: `"fdm"`, `"sls"`, `"mjf"`, `"sla"`, `"dlp"`, `"volumetric"`, `"lpbf"` (powder-bed metal), or `{type, buildDirection, interlayer, stiffnessAcrossLayers, shearAcrossLayers, anisotropic (false \| true \| "bond"), minFeature, overhangs, escapeHole}`. `buildDirection` is `+x` … `-z`, a vector `[x, y, z]`, or `"auto"`. Sets a minimum member, overhang control (FDM), layered stiffness and the layer-bond check. SLS, MJF, SLA and DLP parts also get `report.process` (processcheck.py): walls, trapped powder or resin, warp-prone panels, resin supports, peel area and suction cups. |
|
|
167
|
+
| `printDirection` | Optional, `+x` … `-z` or a vector `[x, y, z]`: the direction layers stack. Turns on overhang control (Langelaar's AM filter), so the optimizer's own surfaces are self-supporting at 45°. |
|
|
168
|
+
| `minMember` | Thinnest member in mm (sets the filter radius). |
|
|
169
|
+
| `maxMember` | Optional thickest member in mm (at least 2 x `minMember`): a local volume limit, so thick members hollow out or split. |
|
|
170
|
+
| `symmetry` | Planes through the design-space centre, by normal axis. |
|
|
171
|
+
| `pattern` | Optional circular pattern: `{type: "circular", axis: "z", count: 6, center: [x, y, z]}` makes `count` identical sectors round the axis (a hub, a turret). |
|
|
172
|
+
| `milling` | Optional, the part is machined: `{axes: 3, directions: ["+z"], toolDiameter: 6}`. Every void must be reachable by a cutter coming straight in from one of the `directions` (default `+z`; `axes: 5` is 3+2 positional from all six sides; `axes: 2.5` also puts pocket floors on `stepDown` levels). Slots and inside corners narrower than `toolDiameter` are filled. |
|
|
173
|
+
| `casting` | Optional, the part is cast or molded: `"+z"` or `{draw: "+z", sides: 2}`. No undercuts along the draw: with 2 sides the mold halves pull both ways (every line along the draw crosses the part once); with 1, one half pulls off a flat parting plane. |
|
|
174
|
+
| `resolution` | `maxElements` (default 60,000) or an explicit `pitch` in mm. |
|
|
175
|
+
| `lattice` | Optional: also fill the best outcome with a stress-graded TPMS lattice, `{type: "gyroid"\|"diamond"\|"schwarz-p", cell, skin, minWall, maxWall, solidAbove, safetyFactor}`. `skin: 0` leaves it open so powder or resin drains. |
|
|
176
|
+
|
|
177
|
+
Shapes: `box` (`min`/`max` or `center`/`size`), `cylinder` (`p0`/`p1` or
|
|
178
|
+
`center`/`axis`/`length`, with `r` and optional `rInner` for a tube), `sphere`,
|
|
179
|
+
and `mesh` (a closed STL by `path`, relative to the study file, or inline as
|
|
180
|
+
`stlBase64`).
|
|
181
|
+
|
|
182
|
+
A load or support on a keep-out means "through the material lining it", which
|
|
183
|
+
is how a shaft pushes on a bore or a bolt holds a hole. Loads take a `force`
|
|
184
|
+
(N) and/or a `moment` (N·mm, about the region's centroid: a motor's torque);
|
|
185
|
+
a carried `mass` (kg, with `dynamic`, `gravity` and optional `at`, its centre
|
|
186
|
+
in mm) becomes its weight along gravity and that weight's moment about the
|
|
187
|
+
region; `"distribution": "bearing"` spreads a force over the half of a bore it pushes
|
|
188
|
+
on, as a shaft does. Supports take `"fix": "pin"` on a cylinder for a joint
|
|
189
|
+
that turns: held radially and axially, free to rotate.
|
|
190
|
+
|
|
191
|
+
A **pressure** acts in a keep-out, the fluid's volume:
|
|
192
|
+
`{"region": "plenum", "pressure": 0.25}` is 2.5 bar gauge (MPa; negative is a
|
|
193
|
+
vacuum) on every wall lining it, normal to the wall. The keep-out keeps a
|
|
194
|
+
`wall` (default `minMember`) that is pinned solid, so the pressure boundary
|
|
195
|
+
cannot be optimized open and the load does not depend on the design; the
|
|
196
|
+
optimizer then puts material behind the wall where it stiffens it: ribs.
|
|
197
|
+
A sealed cavity's pressure balances on itself, so a study whose only loads
|
|
198
|
+
are pressures needs no `supports` (it is held by a statically determinate
|
|
199
|
+
3-2-1 support that takes no load); an open duct's or a flanged housing's
|
|
200
|
+
needs them where the part is held, because the thrust on its open ends goes
|
|
201
|
+
there. The report's `deflection` for a pressure-only case is the most any
|
|
202
|
+
wetted wall bulges. Checked against Lamé (tests/test_pressure.py): a thick
|
|
203
|
+
sphere reads 5-7% over the exact bore stress and 6-9% over its bulge; a
|
|
204
|
+
closed thin cylinder (3.7 cells through the wall) 8-11% over the hoop
|
|
205
|
+
stress. Verification resolves the thinnest wall or skin with four cells (up
|
|
206
|
+
to 400,000 cells): a wall voxelizes to a whole number of cells, and at two
|
|
207
|
+
or three a 2 mm plenum wall read 2.4 mm and 30% under its bending stress; at
|
|
208
|
+
four, the flat wall of a 90 x 60 mm plenum reads the clamped-plate centre
|
|
209
|
+
stress (50 MPa at 2.5 bar). Walls under two optimization cells are warned
|
|
210
|
+
about. The tetrahedral cross-check does not apply pressure yet.
|
|
211
|
+
|
|
212
|
+
## Reading the report
|
|
213
|
+
|
|
214
|
+
- `safetyFactor` is the material strength over the peak von Mises stress,
|
|
215
|
+
worst case. Stress within one optimization cell of where a load or support
|
|
216
|
+
is applied is left out of the peak (`contactBand`): an idealized load
|
|
217
|
+
produces stress there that belongs to the idealization, not the part. The
|
|
218
|
+
analysis is linear and the material numbers are typical datasheet values;
|
|
219
|
+
printed parts are weaker across layers. Treat it as an engineering estimate
|
|
220
|
+
for comparing designs and catching an undersized part, not a certification.
|
|
221
|
+
No safety factor from loadpath has yet been compared with a load test.
|
|
222
|
+
- `keepOutIntrusion` and `preserveMissing` are mm³ from exact booleans and
|
|
223
|
+
should be zero to rounding.
|
|
224
|
+
- `p5Member` is the 5th-percentile member thickness on the medial ridge.
|
|
225
|
+
- `print` (layered processes): `supportVolume`/`supportArea` a slicer would
|
|
226
|
+
add, `bridges` and `maxBridgeSpan`, `islands` (starts in mid-air),
|
|
227
|
+
`bed` (`contactArea`, `feet`, `smallestFoot`, `comMargin` = how far the
|
|
228
|
+
centre of mass sits inside the contact, `aspect`), and `warnings`.
|
|
229
|
+
- `durability` (when a case has `cycles` or `hours`, or the study has a
|
|
230
|
+
`durability` block): per case the fatigue `life` and Miner `damage`
|
|
231
|
+
(and `interlayerLife` across layers), or the `ruptureStrength`,
|
|
232
|
+
`creepRatio` and `creepDeflection` of a held load; `fatigueSafetyFactor`
|
|
233
|
+
(the factor on the cyclic loads that uses up the life), `creepSafetyFactor`,
|
|
234
|
+
their minimum `safetyFactor`, `fatigueLimitedBy`, `joints` (each fastener's
|
|
235
|
+
`preloadRetention` and head `bearingSafetyFactor`), the material data used
|
|
236
|
+
with its source and quality, and `warnings`.
|
|
237
|
+
- `python -m loadpath orient part.stl [--study s.json] [--3mf part.3mf]`
|
|
238
|
+
ranks build orientations of any mesh (the axes and its own flat faces) by
|
|
239
|
+
layer-bond safety under the study's loads, support and bed risk.
|
|
240
|
+
|
|
241
|
+
## Evals
|
|
242
|
+
|
|
243
|
+
`python -m loadpath.evals.run --out evals-out/` runs 15 parts with hard
|
|
244
|
+
requirements (robot-arm links, brackets, a motor mount, a bell crank, a
|
|
245
|
+
hook printed without support, the MBB beam, a metal heat exchanger) and
|
|
246
|
+
scores the delivered meshes. Exit code is the number of failing cases. Run
|
|
247
|
+
it after any change to the solver. Results and the case list are in
|
|
248
|
+
[docs/case-studies/evals.md](../docs/case-studies/evals.md).
|
|
249
|
+
|
|
250
|
+
- `--tier coarse` runs every case on a coarse grid with a short search and no
|
|
251
|
+
fine-grid confirm: a smoke run for each push, compared against a coarse
|
|
252
|
+
baseline (`--baseline old/scorecard.json --gate regressions`).
|
|
253
|
+
- Every case also gets a Pareto score (`pareto.py`): `massAtTarget`, the
|
|
254
|
+
mass the outcomes' mass/SF front needs at the target SF, which moves when
|
|
255
|
+
a part gets lighter at the same SF and pass/fail does not.
|
|
256
|
+
- `--corpus 8` (or `100:108`) adds generated cases (`corpus.py`): links,
|
|
257
|
+
brackets and three-point mounts from a seed, so a fix tuned on the
|
|
258
|
+
hand-written cases is also tried on parts nobody tuned on.
|
|
259
|
+
- `--critic anthropic|agent` renders interface close-ups of each pick and
|
|
260
|
+
has a vision model file findings (creases, sawn faces, blocked seats,
|
|
261
|
+
knife edges) as metrics (`loadpath/critic.py`). `agent` writes a request
|
|
262
|
+
for the agent running the evals to answer by looking at the images;
|
|
263
|
+
`python -m loadpath.critic score evals-out/` folds the answers in.
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
MIT.
|
|
268
|
+
|
|
269
|
+
## Limits
|
|
270
|
+
|
|
271
|
+
See [docs/VALIDATION.md](../docs/VALIDATION.md) for the evidence behind each
|
|
272
|
+
model.
|
|
273
|
+
|
|
274
|
+
- Linear elastic, small deflection. Printed layers are modelled as
|
|
275
|
+
transversely isotropic stiffness plus a tension-across-layers strength
|
|
276
|
+
check; interlayer shear is not checked separately. Buckling is in
|
|
277
|
+
nonlinear.py; no contact.
|
|
278
|
+
- Fatigue and creep are estimates: one published design curve for printed
|
|
279
|
+
PLA, scaled by strength for PETG, ABS and ASA; rough values for TPU,
|
|
280
|
+
resins and metals; creep from typical creep-modulus data, warmed through
|
|
281
|
+
the modulus-retention tables. No frequency (self-heating), moisture or
|
|
282
|
+
variable-amplitude counting beyond Miner's rule.
|
|
283
|
+
- Overhang control is the 45° stencil on the voxel grid; off-axis directions
|
|
284
|
+
use the 26-neighbour cells within 45° of straight down, which is coarser.
|
|
285
|
+
Bridges are recognized by the print check, not designed for.
|
|
286
|
+
- Peak stress from a voxel model is refined at hotspots and carries a 1.15
|
|
287
|
+
allowance in the safety factor; it lands within about ±13% of the
|
|
288
|
+
textbook plate-with-hole value in the tests.
|
|
289
|
+
- Material cards are datasheet values until calibrated: `loadpath calibrate
|
|
290
|
+
kit` prints coupons, `loadpath calibrate fit` turns their measurements into
|
|
291
|
+
a card for your printer (calibration.py, docs/CALIBRATION.md); a saved card
|
|
292
|
+
is usable by name as the study's material.
|
|
293
|
+
- Pure Python and SciPy. A 40,000-cell study with two load cases takes a few
|
|
294
|
+
minutes on a laptop core; a safety-factor search runs several of those.
|