Add runnable tensegrity simulation demos (MuJoCo, PyBullet, PyChrono, Newton, DiffPD, PolyFEM+IPC) + 3D animated renders + Edison survey + regime-aware sweeps + PLA/TPU 85A printable-design model + BO integration bridge + PR #35 Sobol T3-prism sweep ac... - #33
Add runnable tensegrity simulation demos (MuJoCo, PyBullet, PyChrono, Newton, DiffPD, PolyFEM+IPC) + 3D animated renders + Edison survey + regime-aware sweeps + PLA/TPU 85A printable-design model + BO integration bridge + PR #35 Sobol T3-prism sweep ac...#33sgbaird-alt with Copilot wants to merge 53 commits into
Conversation
… + Edison query Agent-Logs-Url: https://github.com/vertical-cloud-lab/tensegrity-optimization/sessions/2649cfd6-d22c-4009-8914-ceb38bbaace5 Co-authored-by: sgbaird-alt <71569943+sgbaird-alt@users.noreply.github.com>
|
@copilot fetch edison results. Extensively consider #28 and #18 in terms of the choices of simulations and parameters (e.g., forces/loads/timescales/etc.). This doesn't preclude other applications like the ones mentioned in #14 and #16 ; provide recommendations and try things out from both regimes. Re-run additional simulations based on this. Embed visualizations from those simulations directly in your comment reply. |
|
@copilot consider also the fact that we might be using PETG struts + TPU "strings" as a tensegrity-like structure, and how we might best design and model that in a way that helps us mimic a true tensegrity structure if were to make one manually. |
…) and commit Edison survey Agent-Logs-Url: https://github.com/vertical-cloud-lab/tensegrity-optimization/sessions/bfc9f985-512f-4973-b7a2-c04715c29984 Co-authored-by: sgbaird-alt <71569943+sgbaird-alt@users.noreply.github.com>
Edison I encoded the two application regimes in
Crutch-tip regime (#18) — payload accel / vertical velocity / tendon strain energy vs. time, then a stiffness sweep: NASA lander / CubeSat regime (#14, #16) — same plots:
NASA-lander 100 g / 8 ms comfortably clears the GEVS 1500 g target; crutch 16 g is ~2× the 8 g HAVS target. The most actionable finding from the sweeps is empirical confirmation of Edison's Rec C limitation: peak g is flat (±2 %) across three decades of cable stiffness because in a rigid-strut model the impulse is set by floor contact, not the cable network — but SEA varies ~10×. That motivates moving to Edison Rec B (DiffPD) before doing real crutch-design BO; I've left that as a follow-up checklist item rather than dragging this PR into a second engine. (commit |
…ty check + sweep Agent-Logs-Url: https://github.com/vertical-cloud-lab/tensegrity-optimization/sessions/5fd99ac2-a7b5-4c68-a270-04d6729513ec Co-authored-by: sgbaird-alt <71569943+sgbaird-alt@users.noreply.github.com>
Agent-Logs-Url: https://github.com/vertical-cloud-lab/tensegrity-optimization/sessions/5fd99ac2-a7b5-4c68-a270-04d6729513ec Co-authored-by: sgbaird-alt <71569943+sgbaird-alt@users.noreply.github.com>
Added a PETG strut + TPU 95A printable-design module that replaces the abstract cable stiffness
Pareto clouds (peak vs SEA, marker colour = tendon Ø, marker size ∝ prestrain, red dashed = regime peak target):
The CSV outputs include the class-1 flag column so a future BO loop can filter out non-tensegrity designs without re-running the geometry check. Two earlier comments from the code reviewer were minor nits; I addressed the meaningful one ( |
|
@copilot btw it's TPU 85A that we're using. Attempt running DiffPD and PolyFEM+IPC |
…n load path Agent-Logs-Url: https://github.com/vertical-cloud-lab/tensegrity-optimization/sessions/53efdd39-435f-4f15-aafe-42c11eeb89f4 Co-authored-by: sgbaird-yolo <235173974+sgbaird-yolo@users.noreply.github.com>
Done both. TPU 85A propagated through DiffPD / PolyFEM+IPC attempts:
Versus the rigid-strut MuJoCo/PyBullet/PyChrono runs where peak g was floor-pinned flat ±2 % across three decades of cable stiffness, here peak g responds monotonically to TPU-85A tendon Ø — the BO objective is no longer degenerate. Absolute values are still high (the soft 12 MPa tendons let the payload free-fall deep before the suspension snaps taut, giving a rope-snap profile); two follow-ups left in the README are (i) add prestrain so suspension is taut at rest, (ii) replace the all-particle struts with rigid bodies via |
You're right that the current tasks aren't fair. Wrote up the thinking in The core problem (quantified): every corner of the PR #35 box is a different-sized object. Sweeping it gives 6.2× cell mass (9.5→59 g), 4.7× envelope volume (118→553 cm³), and 4.0× strut-tip footprint (85→339 mm²). So "fat/short/large-radius wins on SEA" is partly a tautology — those cells just have more material/volume/contact area. Of the three objectives only Real scaled-up lander constraints: hard absorber mass budget (~2–5 % of landed mass → ~100–250 g for the 5 kg regime), stowed/deployed envelope volume, footprint/ground-pressure (= base reaction ÷ footprint, a cap and a floor for stability/regolith), and crush stroke (physics floor Two routes (recommend the hybrid):
Edison's verdict (task This is scoped as the analysis + mock review you asked for; the constant-mass-manifold re-parameterization and constrained-qNEHVI wiring are listed as the prioritized follow-on changes. |
|
@copilot by hybrid, do you mean implement two separate campaigns or do you mean incorporate both into the same campaign? |
Same campaign — not two separate ones. The "hybrid" combines Route A and Route B inside one
So the structure stays one-campaign-per-regime, exactly like
Correction: the commit is |
|
@copilot update the BO script(s) based on completing the hybrid approach. Refer to honegumi docs as needed (via context7 if you prefer) |
|
@claude I need you to match the experimental campaign in #102 as best you can as described there, except using simulation methods described here in this extensive thread for the objectives. Run repeat campaigns with different initial seeds (you might need to make a parallelized GitHub actions workflow file for me to add and run). Also compare simulation methods against the actual Sobol batch that was experimentally tested to see which of the many different simulation outcomes in this thread might correlate with the two objectives of interest mentioned in that thread. See if you can account for the sub-100% infill of the PLA, as well.. when you're doing the experiments. |
|
Claude encountered an error after 9m 43s —— View job Matching the PR #102 experimental campaign with simulation objectives
|
|
@claude try again #33 (comment) |
|
Claude finished @sgbaird's task in 42m 41s —— View job The PR #102 campaign, matched in simulationDone, committed in What "matching PR #102" meant here. 1. Sub-100 % PLA infill
Two things had to come with it before the masses were right:
2. The drop-tower analogue
The mat is calibrated to the measured input pulse only: 208.4 G vs measured 208.2 G, 4.08 ms vs the 4.08 ms implied by the measured peak and Δv. Deliberately not to the measured restitution — a mat lossy enough to return 2 % of impact velocity in this model peaks near 300 G, well above the tower, because the rig loses energy through paths the model doesn't carry (rails, anvil, mount). So simulated 3. Which simulation outcomes correlate with the two measured objectives
Measured
Measured Three readings:
Infill matters most in the mass channel that 4. Repeat-seed simulation-only campaign
The nine printed articles score 0.584–0.827 in simulation, so the loop improves ~17 % on the best of them and the seeds agree to under 2 %. All three walk to the same corner: 5. The parallel workflow (needs you to move it)I can't write to git mv simulations/workflows-staged/sim-bo-pr102-matrix.yml .github/workflows/
Caveats
|
…er analogue, sim-vs-measured correlation study, repeat-seed BO campaign - print_infill.py: sub-100% PLA infill (0.565 solid fraction, refit 0.556 from the committed CSVs), effective strut density/modulus (Gibson-Ashby bracket), CAD-vs-model geometry factors, and the PR #35 constant-mass projection. Predicted as-printed mass lands within 0.7 g of the scale readings. - drop_tower_sim.py: MuJoCo analogue of the 60 in / PU-mat drop in the bench's own objectives (CFC-180 transmissibility t180, rebound energy e_reb_mJ), with the mat calibrated to the measured input pulse (208.4 G / 4.08 ms). - pr102_correlation.py: every simulated observable in this directory vs the seven mapped tested articles. Volumetric SEA tracks measured t180 at rho = -0.93; the purpose-built t180 analogue only manages +0.46; nothing predicts e_reb_mJ yet. - pr102_sim_campaign.py: closed-loop simulation-only analogue of PR #102's SAASBO campaign, one run per seed, with cross-seed mean +/- 1 sd figures. - workflows-staged/sim-bo-pr102-matrix.yml: parallel seed matrix for Actions (staged outside .github/workflows, which this app cannot write to). Co-authored-by: Sterling G. Baird <45469701+sgbaird@users.noreply.github.com>
…th check All three seeds walk to the same corner (R max, H min, twist min, cable_d min) and agree on best t180 to under 2 percent, improving ~17 percent on the best of the nine printed articles as scored in simulation. Co-authored-by: Sterling G. Baird <45469701+sgbaird@users.noreply.github.com>
|
@claude you should have been starting from scratch, changing the seed for Sobol each time. See how many repeats you can do within your single session. After you correct things, I can go back in and get the workflows set up, but first see how many repeats you can get on your own (10 for example). Noting you might be able to parallelize a bit on your end with your CPUs. |
|
Claude finished @sgbaird's task in 34m 47s —— View job Ten independent repeats, each drawing its own Sobol batchYou were right, and it was worse than a missing seed argument. Every repeat attached the same nine printed articles as round 0, so the seed reached nothing but the surrogate's own randomness. The "seeds agree to under 2 %" line I reported was arithmetic, not evidence. Committed in What changed
One thing had to move with it. The hypervolume reference point was derived from the seed's own round 0, which is harmless when every seed shares round 0 and wrong the moment they do not: a repeat that drew a bad initial batch would be handed a generous reference point and score a larger hypervolume for it. It is now computed once from the nine printed articles, so it is the same number for every seed.
Ten repeats, 36 designs each
The third panel is there so the old failure mode stays visible: those are ten different round-0 clouds. Under The repeats now say something. They start far apart and finish close. After round 0 alone the hypervolume spans 7.71 to 11.07 (13.8 % of its mean) and the best All ten walk to the same corner: For the workflow you wanted to set up
git mv simulations/workflows-staged/sim-bo-pr102-matrix.yml .github/workflows/What I did not do
|
…ol seed The repeats shared round 0: every seed attached the nine physically printed articles as completed trials, so the seed reached nothing but the surrogate's own randomness and the seeds agreed to under 2 percent. --init sobol (now the default) draws each repeat's own nine-point round 0 from that repeat's seed; --init printed keeps the PR #102-exact behaviour. The hypervolume reference point moves off the seed's own round 0 onto a fixed one derived from the printed articles, so a repeat that draws a bad initial batch is not handed a generous reference point for it. --jobs runs repeats concurrently, one process per seed, with the numeric libraries pinned to one thread each. Co-authored-by: Sterling G. Baird <45469701+sgbaird@users.noreply.github.com>
Four batches of 9 per repeat, the same per-seed budget as the earlier three-seed run, ten repeats in 1457 s on four cores. The independent draws start far apart and finish close: hypervolume after round 0 spans 13.8 percent of its mean, and 17.50 +/- 0.26 (1.51 percent) after four batches. All ten corner R, H, twist and cable_d identically and leave strut_d loose across the whole box. Co-authored-by: Sterling G. Baird <45469701+sgbaird@users.noreply.github.com>
|
@claude I also need a comparison to random search, a "true optimum" (based on running a whole bunch of the simulations and taking the best one, or some other way that you determine with a much less restricted budget), and whatever other baseline you might want to add (LHS? Something heuristic?). Make sure that you're always saving the results (can commit raw results here), in case we need to look back and change plots or do other analysis. Also, why is there no plot of "running best" for e reb? |
|
Claude finished @sgbaird's task in 51m 51s —— View job Baselines, a reference optimum, and the missing
|
| baseline | what it is |
|---|---|
random |
uniform i.i.d. draws; the floor |
sobol |
scrambled Sobol over the whole budget, i.e. the campaign's round 0 extended to fill it, so the gap to the BO is exactly what the surrogate contributes |
lhs |
scrambled Latin hypercube |
heuristic |
compass (pattern) search with a halving step on a normalized weighted sum, budget split over three weightings (0.15 / 0.5 / 0.85) so it yields a spread of trade-offs rather than one point. The seed sets the start and the axis order. This is "turn one knob at a time", formalized |
| method | final HV (mean ± sd) | fraction of reference | best t180 |
best e_reb_mJ |
p vs BO |
|---|---|---|---|---|---|
| BO (qNEHVI) | 17.50 ± 0.25 | 97.1 % | 0.4972 | 169.81 | — |
| compass search | 14.49 ± 1.78 | 80.4 % | 0.5408 | 172.59 | 9.1e-5 |
| Sobol | 11.82 ± 1.11 | 65.6 % | 0.5790 | 172.17 | 9.1e-5 |
| Latin hypercube | 11.66 ± 1.54 | 64.7 % | 0.5864 | 172.00 | 9.1e-5 |
| random search | 10.86 ± 1.09 | 60.3 % | 0.5972 | 172.30 | 9.1e-5 |
p is a one-sided Mann-Whitney U on the ten final hypervolumes. 9.1e-5 is the smallest value that test can return at n = 10 against n = 10, so every baseline is completely separated from the BO, no overlap between the two sets of ten.
Three readings:
- The surrogate is doing the work, not the space-filling design. The BO's first nine designs are a Sobol batch (Ax's own generator, a different scramble from the
sobolbaseline's, hence the small offset at design 9: 9.25 against 9.81, with the baseline slightly ahead). The moment the model takes over the traces separate and never re-cross: one model-driven batch takes the BO from 51 % of the ceiling to 83 %, 96 % by design 18, 97 % by design 27. Sobol run out to the full 36 finishes at 66 %. - Sobol and LHS are indistinguishable from each other and barely beat random (65.6 / 64.7 / 60.3 %). At 36 points in 5 dimensions, quasi-random stratification buys very little.
- Compass search is the strongest baseline and still loses by a wide margin, and it has the largest seed-to-seed spread of any method (sd 1.78 on hypervolume, 4.04 mJ on best
e_reb_mJ). That is the expected signature of a local method on a front whose extremes are box corners: whether a run lands near one depends on where it started.
The BO's spread is also the smallest of the five, sd 0.25 = 1.4 % of its mean against 9 to 12 % for the baselines. Better and more repeatable across ten independent repeats, which is the property that matters when a real evaluation is a print plus a drop session.
Pooled over ten seeds, the BO's points lie along the front and every baseline's are a cloud in the interior. Note the BO does not reach the extreme low-e_reb_mJ tail (R 25 / H 110 / strut_d 12): qNEHVI spends its budget on the knee, where the hypervolume is, and the tail is worth about 0.8 mJ.
Everything is saved
Every evaluation from every run is on disk, so plots and further analysis never need a re-run:
outputs/pr102_reference_cloud.csv.gz— all 68,944 reference evaluations (2.4 MB gzipped,pd.read_csvreads it directly)outputs/pr102_reference_front.csv— the 247 non-dominated designs;outputs/pr102_reference_summary.csvoutputs/pr102_baseline_<strategy>_seed<k>.csv— 40 runs, each row carrying parameters, both objectives, printed mass, the constant-mass print scale, feasibility flag, hypervolume and both running bestsoutputs/pr102_baselines_summary.csv— the table above- refreshed
outputs_bundle.zip(32 MB)
Reproduce: python simulations/pr102_baselines.py --reference --jobs 4 (~21 min) then python simulations/pr102_baselines.py --strategies random sobol lhs heuristic --seeds 0 1 2 3 4 5 6 7 8 9 --jobs 4 (27 s).
Caveat
This compares optimizers on a simulated objective, so it measures search efficiency on this response surface, not accuracy against the bench. Section 3's caveats about the objective itself all still stand (simulated t180 cannot exceed ~1 because rigid struts cannot resonate; simulated e_rebound is a rank proxy at ~20x the measured value). What does transfer is the shape of the problem: 5 continuous axes, 2 objectives, smooth deterministic response, front on the box boundary. On that shape, 36 model-driven evaluations reach 97 % of a 68,944-evaluation ceiling and 36 space-filling ones reach 66 %.
Also: these ten BO repeats are the qNEHVI ones, not the SAASBO step that matches PR #102 exactly. A single SAASBO round of 3 designs took 570 s, so a like-for-like SAASBO baseline comparison needs the staged Actions matrix rather than one session.
· branch copilot/explore-simulations-for-tensegrity
…nce front The PR #102 simulation-only campaign reported a hypervolume trace with nothing to compare it against. Adds pr102_baselines.py: four baselines at the campaign's own 36-design budget over the same ten seeds, plus a reference optimum from a 65,536-design Sobol sweep and a Nelder-Mead polish of 21 weightings. BO reaches 97.1 % of the reference hypervolume, compass search 80.4 %, Sobol 65.6 %, LHS 64.7 %, random 60.3 %; every baseline is completely separated from the BO over ten seeds (Mann-Whitney p = 9.1e-5, the floor at n = 10). The front sits on box bounds, with cable_d and twist pinned low along its whole length. Also fixes the missing running-best e_reb_mJ panel: the column was computed and saved but never plotted, in either the per-seed or the aggregate figure. Every evaluation from every run is written to CSV, including the full reference cloud, so plots can be redone without re-running anything. Co-authored-by: Sterling G. Baird <45469701+sgbaird@users.noreply.github.com>
|
@claude did you apply the right normalization in the objective for mass? I forgot |
No. The mass normalization is wrong, and it is worse than a scale factor
|
| fit | residual sd, 12 weighed articles |
|---|---|
| wall + infill, strut-diameter dependent | 0.378 g |
| two flat densities (what I had) | 0.927 g |
| print-to-print scatter, spec-08 triplicate | 0.457 g |
So the ported model is as accurate as the process is repeatable and mine was twice as coarse. Independent check: re-projected onto 20.23 g, S0 comes out at scale 1.1335 against the 1.1538 it was printed at. The mat recalibrates on the new manifold to an exact match on the input pulse (208.2 G, 4.08 ms).
simulate() can now pin the article's total mass so the simulated inertia agrees with the scale, and the correlation study scores each article at its own weighed mass instead of a batch target.
What changed, on a 256-point Sobol set
| constant solid mass (old) | constant printed mass (new) | |
|---|---|---|
e_reb_mJ relative span |
0.316 | 0.047 |
mass_g relative span |
0.316 | 0.045 |
ρ(e_reb_mJ, mass_g) |
0.9999 | 0.997 |
ρ(e_reb_mJ, e_rebound) |
0.505 | 0.026 |
The leak is bounded to the declared print scatter now. But it makes a second problem unmissable, and I would rather say it than leave it in a CSV: with mass controlled, this simulation has only one live objective. ρ(e_reb_mJ, e_rebound) = 0.026 means simulated restitution does not respond to the design at all. That follows from the mat calibration decision I documented in §2 last time (calibrated to the measured input pulse, not to the measured restitution, so simulated e_rebound ≈ 0.61 against a measured 0.02 to 0.05). The bench's e_rebound spans 2.5× across the articles, so the measured objective is real; the simulated stand-in is not. Read anything the sim says about e_reb_mJ as mass bookkeeping.
Ten repeats, re-run on the corrected manifold
All ten now land on the identical box vertex: R 40 / H 60 / twist 40 / strut_d 6.0 / cable_d 3.0 / mass at the slab's low edge, t180 = 0.5686 every time. Final hypervolume 10.058 ± 0.0002, a 0.002 % spread against 1.51 % before. Round 0 still starts them 5.59 to 6.81 apart, so the loop is converging, not degenerate at the start.
That collapse is the honest consequence of the fix, not a better result. With mass held constant the problem is effectively single-objective in t180 plus "sit at the light edge of the slab", and the optimum is a corner. strut_d was the one loose axis before and is now pinned, because with mass fixed the strut diameter sets the scale.
The correlation study also moved, and one prior lead did not survive
Re-scored at each article's weighed mass. lander_SEA_J_per_cm3 holds at ρ = −0.93 (p = 0.003, span 231 %) and remains the observable I would attach to the campaign GP. But lander_eta, which led that table at −0.96 last time, fell to −0.71 and switched which objective it leads. I flagged it then as "a ranking of numerical structure until a perturbation study says otherwise"; re-projecting each article onto its own mass was that perturbation, and it failed. A new instance of the same failure appeared: sim_in_180_g, the null control, now reads ρ = −0.89 at p = 0.007 on a 0.09 % span. Any leader whose rel_span is a fraction of a percent is noise with a good p-value.
- Quantify the confound
- Port PR Add T-3_01 Sobol batch print key (ID-to-spec mapping + as-printed .3mf) #102's calibrated printed-mass model and constant-printed-mass projection
- Make mass the sixth parameter in the campaign and the baselines
- Re-run the ten repeats and the correlation study; commit
- Re-run the reference front and the baselines against it
- Final write-up sync
Reference sweep running now.
View job run · branch copilot/explore-simulations-for-tensegrity
|
@claude For future session, why would you make mass a variable rather than simply applying some normalization to the objective itself like dividing the rebound energy by the mass? Mass, which is predetermined by the other variables, is more difficult to represent within the optimization framework, because you need to use fixed features which uses some of the API tooling from featurization. See honegumi for this and you'll also need to look at the ax GitHub documentation extensively Note that there is an earlier session running concurrently |
|
Claude finished @sgbaird's task in 3m 55s —— View job Mass as a parameter vs. normalizing the objectiveYou are right about the simulated campaign, and the numbers say something sharper than I did three hours ago: normalizing would have been the correct move there, and it also shows that the objective definition was never the bug. Nothing committed this session, since the earlier session is mid-edit on these same files. The distinction that decides it: is mass free or determined?That is the whole question, and the two campaigns answer it differently. In PR #102 mass is free. Its five base coordinates fix the article's shape only, because the constant-mass projection uniformly rescales every dimension including the joint. The overall scale is a genuine sixth degree of freedom, and In my sim it is determined. Why it is not just expensive in Ax, it is inexpressibleWorth spelling out, because it is stronger than "more difficult":
Also worth noting from PR #102's own experience with the slab: at the full 0.457 g print scatter, qNEHVI put all nine suggestions on the light edge, so shapes were chosen at 19.77 g and reported at 20.23 g. That is the mass axis being exploited as a gradient rather than modelled, which is another argument for removing it from the search space where you can. What normalizing actually reveals, on both sidesMeasured batch, the 7 articles with a mass (
On the bench, Simulated reference cloud, all 68,944 evaluations in
So the correction to my last comment: the mass normalization was never the real fault. Dividing by mass, which costs one line, removes the confound completely and exposes what is underneath, which is that my simulated restitution varies by 0.34 % across the entire box. That is the mat calibration decision from section 2 of the write-up coming home: the mat was fit to the input pulse and explicitly not to the measured restitution, so the model has no design-dependent loss mechanism. Making mass a parameter would have frozen that confound at one mass rather than fixing it, and imported the fixed-features machinery for nothing. One more thing normalization surfaces that the raw objective hides: the normalized rebound is 0.79 rank-correlated with
What I would do instead, with no fixed features anywhere
Route 1 is what I would pick for the sim, since it also makes the sim and the bench search the same manifold, which is a precondition for the correlation study in section 3 meaning anything. HandoffNot committing, to stay clear of the concurrent session. If it has already landed the six-parameter port, the cheap unwind is to drop the mass parameter, switch the objective to Separately: fixing the simulated restitution matters more than any of this. As it stands the sim's second objective is 0.34 % wide and mostly redundant with the first, so |
…inted mass e_reb_mJ = e_rebound * m * g * h is an absolute energy, so it is proportional to the article's printed mass. evaluate_pr102 was projecting onto PR #35's constant-*solid*-mass manifold (30.95 g), which leaves printed mass free: over the committed 68,944-design reference sweep, printed mass spans 32 %, simulated e_rebound spans 0.34 %, and rho(e_reb_mJ, mass_g) = 0.99993. The objective was the mass. PR #102 fixed the same hole on the bench side in 2f1ca2e by projecting onto constant *printed* mass and carrying mass_printed_g as a sixth BO parameter in a narrow slab (target +/- 0.457 g, the spec-08 triplicate scatter). This ports that projection (pr102_mass_model.py, recalibrated from the CSVs already committed here: residual sd 0.378 g over 12 weighed articles against 0.927 g for the flat two-density fit) and wires it in. Also: simulate() can now pin the article's total mass, so the simulated inertia agrees with the scale; the correlation study scores each article at its own weighed mass; the mat is recalibrated on the new manifold (S0 input peak 208.2 G, pulse 4.08 ms, both exact). Co-authored-by: Sterling G. Baird <45469701+sgbaird@users.noreply.github.com>



















simulations/tprism_mesh.py): 3 PLA struts + 9 TPU 85A tendons fused via gmsh OCC fragmentsimulations/polyfem_drop.py --geometry tprismJSON builder + CLI dispatchsolref="0.002 1",solimp="0.98 0.999 0.0001")printable_design.pyprimaryPLAMaterial withPETGkept as a backward-compat alias;tprism_mesh.pyphysical-volume group renamedPETG_strut→PLA_strut;polyfem_drop.pyNeoHookean kwargs renamed*_petg*→*_pla*with PLA defaults E=3.5 GPa, ν=0.36, ρ=1240; updates innewton_drop.py,printable_sweep.py,render_regimes.py,regimes.py,simulations/README.md)LITERATURE_HIGHtask78fb09a2modeling-feedback contacts brief committed atedison-trajectories/modeling-feedback-contacts/(6dae17f)simulations/validation_experiments.md: 10-row bench-test table mapping each Instron/drop-tower experiment to a specific simulation output, plus a 3-test minimum-viable validation budget for the T3 prints we already have (Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35)simulations/bo_integration.md+simulations/bo_evaluator.py: drop-in replacement for PR Scaffold Bayesian-optimization script with honegumi + customized tensegrity campaign #30simulate_specimenthat maps Ax parameterization →PrintableDesign→ overriddenRegime→run_regimes.simulate(...)→{F_peak_N, SEA_J_per_g, eta}; forward-compatiblefidelity={"C","B","A"}signature for the three-tier ladder (MuJoCo / Newton / PolyFEM+IPC) from Edison sim survey 782657e0bo_evaluator._t3_seed_designs()returns the three PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 T3-prism prints (scale 1.0× / 1.5× / soft-tendon variant) as Ax-compatible dicts so the BO loop's first batch reflects parts already on the bench; other PR Scaffold Bayesian-optimization script with honegumi + customized tensegrity campaign #30 topologies (truncated-octa, simplex-4-strut, stacked-prism) emit a warning and fall back to T3 physics until the generic mesher lands.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 BO integration, and Edison analysis querysimulations/benchmark_costs.py: empirically measure tier-C CPU cost — ~0.1 s raw / ~0.2 s with CFC-180 filtering per design on an AMD EPYC 7763 (1 core, MuJoCo 3.9); cost table for all three tiers documented inbo_integration.mdbo_evaluator.pynow reads the PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 schema directly (R_mm,H_mm,twist_deg,strut_d_mm,cable_d_mm) vianormalize_parameterization(), withevaluate_batch_csv()+ CLI to score a wholet3-prism-bo-batch.csvso those trials can be attached to Ax instead of discarded;_t3_seed_designs()re-expressed in the geometrically-accurate PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 mm schema.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 equilibrium twist (60°,B_i→T_i) maps to the sim (tprism_geometry150°,STRUTS=[(0,4),(1,5),(2,3)]) viasim_twist = scad_twist + 120°; without it every PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 design was falsely flagged class-2 and penalized_cfc_filter, pure-NumPy 4-pole phaseless Butterworth) applied to the simulated axial acceleration before extractingF_peak/eta, so the tier-C objective lives in the same processed space as the drop-tower accelerometer pipeline (PR Analyze drop-tower accelerometer "tuning" data (issue #71) #74) and simulated + measured rows can attach to one Ax/BoTorch model; opt-out viacfc180=False/--raw-peakbo_integration.md: cost table, PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 axis mapping, twist-convention note, CFC-180 parity, and what the sims give the BO that the printer/drop-tower cannot cheaplyANALYSIStask4e74f66c(statussuccess) submitted viascripts/edison/submit_simulation_bo_value.pywith the PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 BO script, the sim→BO bridge, the tier-C simulator, and the manuscript draft uploaded; full answer committed atedison-trajectories/simulation-bo-value/(recommends CFC-180 parity, closing the loop on PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35, multi-fidelity co-kriging GP, and qNEHVI one-campaign-per-regime with HAVS/GEVS outcome constraints)## Multi-task treatment of the regimessection insimulations/bo_integration.mdrecommends a multi-task GP (MTBO) that carries the regime as a task and shares information across the crutch/lander campaigns (replacing the prior one-campaign-per-regime suggestion), with the AxSpecified_Task_ST_MTGP_trans/BOTORCH_MODULARwiring from the Honegumi multitask tutorial, the mean-collapse caveat from the Honegumi concept page, a note that fidelity (C/B/A) generalizes as a second task axis, and updated open-questionssimulations/outputs/mujoco_drop(37.2 g),mujoco_sweep,run_regimes(crutch k*=5798/16.3 g, lander k*=400/103.9 g),printable_sweep,benchmark_costs(~107 ms/design on this runner's AMD EPYC 9V74)render_spotchecks,render_mujoco_drop,render_regimessimulations/outputs_bundle.zip(11 MB, < 100 MB so no split needed) archiving the fullsimulations/outputs/set.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 T3-prism Sobol variations through the simulators and analyse the resultssimulations/sobol_t3_campaign.py: draws an N-point Sobol set over the exact PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 design box (R_mm∈[25,40],H_mm∈[60,110],twist_deg∈[40,80],strut_d_mm∈[6,12],cable_d_mm∈[3.0,5.5]) viascipy.stats.qmc.Sobol(Ax is not installed in the sim env) and scores itoutputs/sobol_t3_tierC.csvoutputs/sobol_t3_tierB.csvoutputs/sobol_t3_*.pngsimulations/sobol_t3_analysis.mdinterpretation: F_peak is near-invariant at Tier-C (SEA/eta are the discriminating objectives);strut_d_mmandH_mmdominate the Tier-C sensitivity whiletwist_degreads ≈0 because the Tier-C regime override does not consume the twist axis (it surfaces only at Tier-B/A); the whole PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 box is printable; CSVs are ready toattach_trialto Ax as a cheap simulated priorsimulations/README.mdscipy.stats.qmc.Sobol(Ax not required, per the comment)sobol_t3_campaign.py(run_tier_a+_tier_a_onemultiprocessing worker, 2 concurrent processes) — welded PLA/TPU T-prism drops on an 8-design subset; mesh resolution scaled with cross-section (lc_strut/lc_tendon) to hold tet count ~6k; meshes written outsiderun_drop'swork_dir(which it wipes on entry)--param-jsonbridge); added parameterizedrun_param()topybullet_drop.pyandpychrono_drop.py(+--param-jsonstdin/stdout CLI for the conda interpreter)analyse()with an engine-ladder Spearman comparison (sobol_t3_engine_ladder.png) and a Tier-A figure (sobol_t3_tierA.png); rewrote the cross-fidelity report section as a C→B→A ladder; new CLI args--n-tiera,--n-pybullet,--n-pychrono,--tiera-workerssobol_t3_tierA.csv,sobol_t3_pybullet.csv,sobol_t3_pychrono.csv,sobol_t3_tierA.png,sobol_t3_engine_ladder.png), refreshedsobol_t3_analysis.md, README, andoutputs_bundle.zip(11.8 MB)simulations/sobol_t3_violins.py: reads the existingoutputs/sobol_t3_*.csvcampaign measurements and rendersplotly.express.violin(..., points="all", box=True)with non-zerojitterso every raw measurement is drawn as a jittered swarm beside each violin (PNG via kaleido + interactive HTML)outputs/sobol_t3_violin_objectives.{png,html}— Tier-C MuJoCo objectives (F_peak,SEA,eta), one violin per regime, faceted with independent y-axes (512 feasible designs)outputs/sobol_t3_violin_engines.{png,html}— per-engine peak deceleration (g) across the C→B→A ladder (PyBullet/PyChrono/Newton/PolyFEM) on a log y-axissimulations/README.md,sobol_t3_analysis.md, and the campaign Files list; refreshedoutputs_bundle.zipscripts/edison/submit_sobol_t3_results.py+scripts/edison/fetch_sobol_t3_results.py: bundle the campaign scripts (sobol_t3_campaign.py,sobol_t3_violins.py,bo_evaluator.py,run_regimes.py), the measured CSVs (sobol_t3_tierC/tierB/tierA/pybullet/pychrono.csv), the figures (Pareto / sensitivity / Tier-C-vs-Tier-B / engine-ladder / Tier-A / both violin plots), thesobol_t3_analysis.md+bo_integration.mdwrite-ups, and a project-context note as a single Edison data collection, submitANALYSIS, poll, and commit the answerANALYSIStaskff8faab3(statussuccess) committed atedison-trajectories/sobol-t3-results/— Edison re-analysed the CSVs directly and confirmed our reproducible numbers (Tier-CF_peaknear-invariant; SEA/eta discriminating; engine-ladder ρ PyChrono +0.70 / Newton +0.60 / PyBullet −0.02 / PolyFEM +0.43) while sharpening several claims we should revise: crutch Tier-CF_peakis essentially the static payload weight (ratio 1.002), Tier-CSEAis a conservative elastic-energy proxy (not true dissipation), PolyFEM peak-g is ~1 g flat (so Tier-A does not back the Tier-CF_peakladder), andstrut_d_mm's apparent leverage is largely a rigid-body mass/contact-geometry artifact (ρ=−0.976 vs anL·d²strut-mass proxy, sign flips by regime)F_peak, filtered/unfiltered peaks, constant-mass strut-diameter sweep, twist-injection plumbing audit at Tier-C/Tier-B) and a full multi-fidelity BO citation listff8faab3review recommended, in newsimulations/sobol_t3_diagnostics.py(run on 48 feasible PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 designs)mj_contactForce, the sensorized-platen observable): genuine ~104× static-weight transient for the lander while the crutch's large soft cell barely loads the floor in the 25 ms window, confirming the payload-accelF_peakis a support-load proxy not an impact peak →outputs/sobol_t3_diag_base_reaction.csvF_peakspan (~10%) is ~3× the filtered span (~3%), so the filter suppresses part of the design transient (the fix is the observable, not removing the filter) →outputs/sobol_t3_diag_cfc.csvstrut_deffect ~17×, confirming Edison'sL·d²inertia/contact confound; reported as effect-size (peak-g range) since Spearman stays ≈±1 on a monotonic sweep →outputs/sobol_t3_diag_constmass.csvregimes.Regimehas no twist field andrun_regimes.build_xmlnever passes it, so the twist≈0 Tier-C result is un-consumed plumbing not physical irrelevanceoutputs/sobol_t3_diagnostics.png+ write-upsimulations/sobol_t3_diagnostics.md; narrowed the matching over-claims insobol_t3_analysis.md(payload-accelF_peakrelabeled support-load proxy;strut_ddemoted from "dominant lever" to inertia confound; twist framed as plumbing); documented the script insimulations/README.md; refreshedoutputs_bundle.zip.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 but with simulations instead of real experimentssimulations/sim_bo_campaign.py: the simulation-only analogue of PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35's single Sobol hardware batch — AxAxClient(defaultSobol → BOTORCH_MODULARqNEHVI) proposes designs over the exact PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 box (R_mm∈[25,40],H_mm∈[60,110],twist_deg∈[40,80],strut_d_mm∈[6,12],cable_d_mm∈[3.0,5.5]); the Tier-C MuJoCo regime sim (bo_evaluator.evaluate_design, CFC-180 filtered) scores each on the three objectives (minimizeF_peak_N, maximizeSEA_J_per_g, maximizeeta); results feed straight back to the surrogate.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 T3 cells (bo_evaluator._t3_seed_designs()) and runs independent per-(regime) campaigns; running-bestSEAclimbs then plateaus (classic BO convergence), withF_peaknear-invariant (it is a Tier-C support-load proxy) soSEAis the live discriminator(tier, regime)pair so crutch and lander never share an axis (the prior shared plots were unreadable given their ~6× objective-scale gap)outputs/sim_bo_<tier>_<regime>_seed<k>_{convergence,pareto,cv}.pngoutputs/sim_bo_<tier>_<regime>_convergence.pngplots the mean running-best with a ±1σ band across seeds (faint per-seed traces behind it)simulations/sim_bo_campaign.pyinto a tier/seed-parameterized closed-loop driver (--tiers C B,--seeds,--n-iter,--regime,--raw-peak,--outdir): Tier-C MuJoCo runs the 3-objective qNEHVI loop while Tier-B Newton/Warp XPBD runs a single-objectiveF_peakloop (Newton only exposes the payload-accel trace). Headline cross-tier result: Tier-CF_peakis near-flat (support-load proxy) but Tier-BF_peakspans ~2.5× and the BO genuinely descends because the elastic tendons sit in the dynamic load pathoutputs/sim_bo_<tier>_<regime>_seed<k>_cv.pngvia Ax's built-incross_validate(refitGenerators.BOTORCH_MODULARadapter), showing strong predictive signal where it matters (Tier-C crutchSEAR²≈0.97/ρ≈0.96, Tier-BF_peakR²≈0.99); near-flat CV outcomes (e.g. Tier-C landereta) are pinned observables, not model failuressim_bo_{crutch,lander}.csv,sim_bo_{pareto,convergence}.png) with tier-named CSVs (sim_bo_<tier>_<regime>.csv+_pareto.csv) and the per-seed/aggregate figures; rewrotesimulations/sim_bo_campaign.md, updatedsimulations/README.md, and refreshedoutputs_bundle.zipscripts/edison/submit_sim_bo_review.py+scripts/edison/fetch_sim_bo_review.py: bundle the campaign driver (sim_bo_campaign.py), the sim→BO bridge (bo_evaluator.py), the per-(tier,regime) trial + Pareto CSVs, and the per-seed/aggregate convergence, Pareto, and LOO-CV figures, plussim_bo_campaign.md/bo_integration.mdand a project-context note, as a single Edison collection; submitANALYSIS, poll, and commit the answerANALYSIStask491f90ae(statussuccess) committed atedison-trajectories/sim-bo-review/— confirms real predictive signal on the discriminating outcomes (Tier-C crutchSEA, Tier-BF_peak) and that weak CV on near-constant outcomes (Tier-C landereta) is intrinsic, not model failure; flags Tier-B was regime-blind (matched crutch/landerF_peakratio 0.998), that the per-fidelity constraints don't align (Tier-C 100% feasible / Tier-B 0%), Tier-C lander BO proposal collapse, and recommends a contextual, cost-aware, discrepancy-aware multi-fidelity BO (regime + fidelity as task/context, physics features, Kennedy–O'Hagan / co-kriging fusion, peak-g as the constraint variable, constrained qNEHVI)simulations/sim_bo_campaign.py:_eval_tier_bnow seeds each Newton drop with the regime's own impact velocity (crutch 1.4 m/s, lander 9.8 m/s) vianewton_drop.build_model(impact_velocity_mps=…)and reads a robust start-up-gated peak (newton_drop.peak_decel_g), so crutch and lander are now physically distinct (e.g. ~1463 N vs ~564 N for the same seed design instead of the prior 0.998 ratio)nrmse = RMSE/(max−min)and a constant-meannull_skillbaseline) to every per-(tier,regime)outputs/sim_bo_<tier>_<regime>_cv_summary.csv, per Edison rec. B, so a highR²on a near-constant outcome is not mistaken for decision-useful signalsimulations/outputs/(CSVs + figures) and syncedsimulations/sim_bo_campaign.md(Tier-B narrative + numbers, LOO-CV table, Files list) to the new regime-distinct results, which the output refresh had made stale.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 box and render the best/worst/mediocre designs as callouts (sims are cheap enough that no cost-aware/multi-fidelity loop is needed)simulations/pareto_render_campaign.py: densely Sobol-maps 2048 designs per regime over the exact PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 box and scores each at Tier-C MuJoCo (bo_evaluator.evaluate_design, CFC-180 filtered) on the three objectives (minimizeF_peak_N, maximizeSEA_J_per_g, maximizeeta) at ~0.15 s/eval; takes the 3-objective non-dominated set as the empirical Pareto frontRegime), plus headline best/worst drop GIF/MP4 animations per regimeoutputs/pareto_<regime>_annotated.png: aSEA↔etaPareto scatter (colour = near-invariantF_peak) with the render stills dropped on as callout thumbnails + leader lines, and a clean 2-DSEA↔etafrontier staircase rather than the zig-zagging 3-objective setpareto_<regime>.csv(2048 rows +feasible/paretoflags), per-pick stillspareto_<regime>_render_<tag>.png,pareto_<regime>_{best,worst}_drop.{gif,mp4}, andpareto_summary.md; both regimes report the whole box printable,F_peakspan ~3–4 % (crutch 712–739 N, lander 4628–4790 N),SEA↔etathe live trade-off (best cells short/fat-strut/large-radius, worst tall/slender)F_peakis a support-load proxy (shown as colour),SEAis an elastic-energy proxy, andtwist_deg≈0 at Tier-C is un-consumed plumbing; write-upsimulations/pareto_render_campaign.md+README.mdentry; refreshedoutputs_bundle.zip(~25 MB)simulations/fair_evaluation_analysis.md: quantifies the size confound — across the PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 box the cell mass varies 6.2× (9.5→59 g), envelope volume 4.7× (118→553 cm³), and strut-tip footprint 4.0× (85→339 mm²), so designs are compared at very different sizes; onlySEA_J_per_gcontrols for mass, none control for volume/footprint, and the landerF_peakis a real ~94–98 g transient (only the crutchF_peakis a support-load proxy)s ≥ v²/2a_max ≈ 3.3 mm) and lays out two routes to fairness — (A) re-parameterize onto a constant-mass / scale-free shape-ratio manifold so budgets hold by construction, vs (B) keep the box but score intensive objectives (SEA_J_per_g+SEA_J_per_cm³, base-reaction peak-g, ground pressure) with mass/volume/footprint + peak-g as Ax outcome constraints under constrained qNEHVI — plus a recommended hybrid for the landerscripts/edison/submit_fair_evaluation.py+scripts/edison/fetch_fair_evaluation.py; README entryANALYSIStaske43abed6(statussuccess) committed atedison-trajectories/fair-evaluation/— endorses the diagnosis and the hybrid, independently reproduces the confound (~6.6× mass / 4.69× volume / 4.0× footprint), and sharpens the write-up: re-parameterization (constant-mass equality manifold + dimensionless Buckingham-π shape groups) is sounder than outcome constraints for the binding mass budget; don't hard-constrain a biased cheap observable (use Tier-C base reaction only for coarse screening, reserve peak-g acceptance for Tier-B/A/bench); report fronts at 2–3 fixed mass/envelope budgets; corrected the over-claim that landerF_peak≈ static load and reframed GEVS/HAVS as requirement proxies — all folded back intofair_evaluation_analysis.mdAxClientcampaign per regime (not two separate campaigns); added a clarifying paragraph under "Recommended hybrid" insimulations/fair_evaluation_analysis.md(cbcf27b)simulations/bo_evaluator.pywith the hybrid geometry/manifold machinery:cell_geometry_metrics()(cell mass / envelope volume / strut-tip footprint, all derived fromPrintableDesign.nodes— also fixes a pre-existing strut-length bug by usingdesign.strut_length_m);design_from_shape_ratios()— the Route A constant-mass manifold that builds a cell from four dimensionless ratios (H/R,H/strut_d,cable_d/strut_d,twist) and solves the single overall scale by closed-form cube root so cell mass equalsm*exactly; the inversedesign_to_shape_ratios(); andbase_reaction_peak_N()— the Route B base floor-reaction observable (sums vertical strut↔floor contact forces, optionally CFC-180 filtered)evaluate_printable_design(design, *, regime, fidelity, cfc180, base_reaction)(called byevaluate_design) and extended the return dict withcell_mass_g,envelope_cm3,footprint_mm2,SEA_J_per_cm3, and optionalF_base_peak_N; all existing keys (F_peak_N,SEA_J_per_g,eta) and behaviour preserved for backward compatibilitysimulations/sim_bo_hybrid_campaign.py: the fair closed-loop analogue ofsim_bo_campaign.py— oneAxClientconstrained-qNEHVI campaign per regime over the four scale-free shape ratios at a fixed cell massm*(Route A), scoring intensive objectives (impact F — base floor-reaction for the lander, payload-accelF_peakfor the crutch's large soft cell —,SEA_J_per_g,SEA_J_per_cm³,eta) under envelope-volume + strut-tip-footprint Ax outcome constraints (Route B; mass is structurally fixed so it is not a constraint); seeded with the three printed PR Add T3-prism (3-strut tensegrity) parametric CAD with Bambu PETG.gcode.3mfslice + re-importable project.3mf(H2D-only, supports enabled, scale 1.5× / cable_d 4.5 mm) + PLA-cables and PLA-struts/TPU-cables MM variants (with modeled-in PLA scaffo... #35 T3 cells projected onto the manifold; per-seed + mean±σ convergence, intensive-trade Pareto, constraint-feasibility, and LOO-CV figures; CLI--regime/--seeds/--n-iter/--mass-g/--envelope-max-cm3/--footprint-min-mm2/--footprint-max-mm2/--raw-peak/--outdirfair_evaluation_analysis.md§4 (steps 1–4 marked implemented, pointing at the new helpers/script) andsimulations/README.mdoutputs/artifact set and a dedicatedsimulations/sim_bo_hybrid_campaign.mdwrite-up (the 4-objective constrained-qNEHVI run is slow; the module docstring documents usage and the output files in the meantime), and re-run the dense Pareto render campaign on the constant-mass manifold (fair_evaluation_analysis.md§4 step 5)