Compare named vehicles across the standard sims with opt-trade - #63
Merged
Merged
Conversation
raorjun
added this pull request to stack #65
September 21, 2026 03:25
raorjun
marked this pull request as ready for review
September 21, 2026 03:31
raorjun
force-pushed
the
feat/optsim-trade-study
branch
from
September 23, 2026 06:26
4e10dc5 to
ac575af
Compare
raorjun
force-pushed
the
feat/optsim-trade-study
branch
from
September 24, 2026 01:27
ac575af to
f3a445b
Compare
OptSim could sample a design space and invert for a setup. It could not answer the question that a design review asks: what does this specific change give, and what does it cost, across more than one study. opt-trade reads a YAML of named candidates and the metrics to compare. It compiles each candidate once and runs each requested standard against that one executable. Then it writes a comparison table. SteadyStateEval, RampSteerEval and TransientEval all run the same compiled model, and _3_StandardSim already builds it once for all three. They also share one interface. So pipeline/standards.py is a registry of three entries and a generic runner. The sweep's batch step now uses the registry. Before this change, a second standard in compiler_config.yaml would have run SteadyStateEval's report against the wrong executable. opt-trade compiles each candidate and does not use an override. opt-solve makes the opposite choice. A trade study must be able to change mass, CG, toe and camber, and an override of those silently does nothing. The solver's evaluator had a private cache of compiled vehicles. That cache is now pipeline/variants.py, and both tools share it. The cache key is the content, so two studies build and simulate a shared vehicle only once. The report has no score and no ranking. It makes sure that each difference is worth reading: - It shows a delta below the metric's stated resolution, but marks it. - It does not compare a run that lost simulation cases. - If one candidate is exactly two others combined, it reports the interaction. So "we'll do both" is not assumed to be the sum. TransientEval reports some metrics once per group under one name. You must ask for those by group. A metric that repeats with different values is an error. Real run on the example study, four vehicles by two standards: 1144 s cold, 3 s cached.
The trade study refused to compare a run that lost simulation cases, but the solver did not. A star point that lost a case still returns finite gradients, fitted through fewer points. So it bent the surrogate, and no number looked wrong. Evaluations are cached, so it would also have bent every later solve. Both tools now share one definition, standards.case_loss. The solver stops and reports the variant, the counts and what to change. This commit also replaces the error for tooling that changes during a run. The error came from the sweep's compiler and said to run make clean-opt. That advice is wrong here, because the store discards a stale cache by itself on the next run. A real run hit this error when another process edited hashed files between the star and the first verification compile. The first end-to-end solve over compile-only knobs found that problem. The solve now passes. Front toe, rear toe and the rear bar together built five executables in one batch, and the bar's star points shared the baseline executable. The verification compiled exactly one more. The solve converged on the first try at 0.3578 / 0.8359, against 0.35 / 0.85, in 617 s.
raorjun
force-pushed
the
feat/optsim-trade-study
branch
from
September 24, 2026 01:30
f3a445b to
a36fbf7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
This PR adds
make opt-trade. It compares vehicles that you name, on metrics that you choose, across more than one standard sim. Stacked on #61.OptSim could sample a design space (
opt-standard) and invert for a setup (opt-solve). It could not answer the question that a design review asks: what does this specific change give, and what does it cost? This PR replaces nothing. The docs now start with a table that shows how the three tools relate.Support for the other standard sims needed only a small change. SteadyStateEval, RampSteerEval and TransientEval all run the same compiled model.
_3_StandardSimalready builds it once for all three. The three also share one interface:python -m <module> <config>, the samesimulationandreportkeys, and<stem>_metrics.csv. Sopipeline/standards.pyis a registry of three entries and a generic runner. One compile for each vehicle serves every standard. FourPostEval is not included, on purpose. It runsFourPostSim, which is a different model.The sweep's
batch.pynow sends each standard through that registry. Before this change, a second standard incompiler_config.yamlwould have run SteadyStateEval's report against the wrong executable.The trade study compiles each candidate. It never uses an override.
opt-solvemakes the opposite choice, and both choices are on purpose. A trade study must be able to change mass, CG, toe and camber. #61 showed that an override of those parameters silently fails. The solver's evaluator had a private cache of compiled vehicles. That cache is nowpipeline/variants.py, and both tools share it. The cache key is the content. So two studies build and simulate a shared vehicle only once, for example the baseline.The report has no score and no ranking, on purpose. How much understeer is worth how much settling time is an engineering decision. A weighted sum would hide that decision in a number. The report does make sure that each difference is worth reading:
~. The resolution is the smallest change worth acting on.n/c, and the report does not compare it. Its fits use fewer points, so its delta would mix the design change with missing data. A baseline that lost cases makes that whole standard invalid. Exit 2.yaw_gain_dconce for the step and again for the frequency sweep. You can ask for it only with its group (step.yaw_gain_dc). The bare name is an error that lists the options. A metric that repeats with different values is an error. A metric that repeats with the same value is one metric. TransientEval really does writeyaw_overshoot_pcttwice.How to check
After the rebase on
main: 566 passed, 18 skipped, andruffis clean. Before the rebase,mypywas clean inside the container, which has CI'sscipy-stubs.I ran the example study for real: 4 vehicles × 2 standards, OpenModelica 1.26.3, 12-CPU container.
_doe_config.yamlwas byte-identical after the runs.The numbers are credible:
I verified
opt-solveagain end to end after its evaluator moved to the shared store. It converged on the same setup and metrics as before the move: 231.5 / 748.5 / 22578.7 / 52139.2 gives 0.3130 / 0.8502.Review time is best spent on two items:
pipeline/trade.py, which decides when a delta is worth reading.resolutionsemantics for the stacking verdict are what you want.Notes
A decision that needs a second opinion. In the real run,
bothchanged understeer by -0.032. Its parts predicted -0.018. So the interaction is -0.014, almost as large as the two parts together. That was enough to putbothover the 0.02 resolution, although neither part crossed it. The interaction itself is below resolution. So the label reads additive within resolution, with the number next to it. My first label was "stacks", and I changed it. The correct claim is "cannot be told apart from zero", which is weaker than "adds". If you prefer to flag an interaction that is large relative to its parts, that is a one-line change.Compare results from the same tool. Each metric here comes from its standard's own test matrix. So an
opt-tradegradient matchesmake standard-eval-steady-state. It does not matchopt-solve, which fits through its own denser isoline.A candidate can change only a declared variable (
sweep.variablesinvehicle_architecture.yaml). Each variable needs its Modelica record mapping. To trade on a new variable, such as wheelbase, declare it there first. The study accepts a value outside a variable's sweep range and adds a note.This PR also stops the solver from fitting through an evaluation that lost cases. The trade study refused to compare such a run, but the solver did not. So a star point that lost cases would have bent the surrogate silently. Because evaluations are cached, it would also have bent every later solve. Both tools now share
standards.case_loss.The compile-only knob path is now verified end to end. #61 listed it as not verified. The run used front toe, rear toe and the rear bar together:
The run also shows what the bars and springs could not do. Front toe changed understeer from 0.325 to 0.358. So on this car, toe has the authority over balance that roll stiffness does not have.
The first try of this run crashed, and the crash was useful. Another process edited hashed tooling files between the star and the verification compile. The staleness check correctly refused to continue. But its message came from the sweep's compiler and said to run
make clean-opt. That advice is wrong for a store that discards a stale cache by itself. This PR replaces the message.Not verified: RampSteerEval through
opt-tradeend to end. It is registered, and a test checks its interface against its real config. I did not run it. There is noregression-baselinerun. This PR touches no physics.Follow-ups, not in this PR:
VariantStorebuilds one model for each vehicle today.limit_*metric is a fallback until the SteadyStateEval test matrix reaches a limit. A trade study reports those columns correctly. That does not make them mean what their names say.