Test every library of an FMI job with FMI - #309
Merged
Conversation
A library whose reference files are already prepared by another library took a
`continue` that skipped the rest of the configuration loop - and the only thing
after it is the line that turns FMI on:
if allTestsFmi:
c["fmi"] = "2.0"
So four libraries of configs/conf.json have been running as ordinary native
models inside the FMI jobs: PowerGrids_symb_jac, PowerGrids_dev, ClaRa_dev and
ScalableTestSuite_noopt, each of them the second library to want a reference
directory that PowerGrids, ClaRa and ScalableTestSuite had already cloned. They
verify identically in master and in master-fmi - 66, 19, 72 and 244 models -
while every library that prepares its own directory drops as an FMI job should
(242 to 176 for ScalableTestSuite, 65 to 31 for PowerGrids).
The reuse also looked the directory up under a key it had not computed yet, so
it took whatever `destination` the previous library left behind. Both are fixed
by computing the normalised destination first and putting the preparation in an
else branch, which leaves nothing after the loop's body unreachable.
Since #297 this crashed the run rather than quietly mistesting it. Such a model
has no per-simulator results, so the second simulator's branch reported the
phase the *first* simulator reached - 7, verified - with no comparison to go
with it, and building the report died on it after seven hours:
("%s (%d verified)" % (timeSeconds(diff.get("time")), ...)) if s[3]["phase"]>=7
AttributeError: 'NoneType' object has no attribute 'get'
A simulator that never ran a model now reports the phase the shared build
reached and never a phase another simulator went on to reach with the same FMU,
in the report and in the database row alike; and a model with nothing to compare
renders as an empty cell whatever phase it claims, so a mismatch can never again
throw away a finished run.
Fixes the master-fmi failure of build 11369.
adrpo
added a commit
that referenced
this pull request
Aug 12, 2026
An FMI job that runs several simulators over one FMU (#297, #309) gives the first of them a veto over the rest. When it fails, the TimeoutError handler calls writeResultAndExit, the process is gone, and the loop that runs fmisimulators[1:] never starts -- but a row is still written for every simulator, recording the phase the first one stopped at. The other tools are marked as having failed at a model they were never given. checkOutputTimeout raises TimeoutError for a command that exits non-zero as well as for one that runs out of time, so this covers an ordinary failure and not just a slow one. In the master-fmi run of 2026-08-12 this cost 433 models. They had never simulated under OMSimulator -- phase 5 in every master-fmi run back to 2026-08-08 -- but FMPy ran them, and the previous master-fmi-fmpy run has them at phase 6 or 7. In the first combined run they are phase 5 in both tables, with FMPy's simulation time recorded as exactly 0: OMSimulator failed in under a second for 344 of them, and FMPy never started. They show up as 433 of the 653 regressions on the comparison page, next to the 213 that PR #309 really did find. It is also why that page reports OMSimulator's version, and its JSON parse warning, in place of "FMPy version 0.3.29": FMPy never ran to say what it was. The first simulator now records its failure and falls through to the others, which is what every simulator after it already did. Nothing changes when only one simulator is configured: that case still ends the model where it always has, so the non-FMI paths and the single-simulator FMI jobs are untouched. Verified on MyLibrary.Blocks.Examples.PID_Controller with the first simulator replaced by /bin/false: before phase 5, "simulators": {} after phase 5, "simulators": {"fmpy": {"sim": 0.64, "phase": 7}} and, unchanged in both: one failing simulator phase 5, os._exit as before both simulators working OMSimulator phase 7, fmpy phase 7 Claude-Session: https://claude.ai/code/session_01MYvMaAotMy425H7nvKzWX7 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
The
master-fmijob of build 11369 ran for seven hours and twenty minutes and then threw all of it away:A model reported that it had been verified while having nothing to show for the comparison. Chasing that turned up an older bug underneath it.
Four libraries have not been testing FMI at all
The configuration loop prepares a reference-file directory once and lets the libraries that share it reuse the result:
That
continueskips the rest of the loop body — and the only thing after it is the line that turns FMI on:So in
configs/conf.jsonfour libraries have been running as ordinary native models inside the FMI jobs, each of them the second library to want a directory another had already cloned:PowerGrids_symb_jacPowerGridsPowerGrids_devPowerGridsClaRa_devClaRaScalableTestSuite_nooptScalableTestSuiteThe results say so plainly. Each of the four verifies exactly the same number of models in
masterand inmaster-fmi, while every library that prepares its own directory drops as an FMI job should:PowerGrids_symb_jacPowerGrids_devClaRa_devScalableTestSuite_nooptPowerGridsScalableTestSuiteClaRaThe same three lines also looked the directory up under
destination— a variable assigned after the lookup, so it held whatever the previous library left behind. It has been reading the right entry only because everydestinationin the configuration happens to be spelled in normalised form.Both are fixed by computing the normalised destination first and putting the preparation in an
elsebranch, so nothing in the loop body is unreachable any more.Why it started crashing
Before #297 this only mistested those libraries quietly. Now a job can run several simulators against one FMU, and a model that was never built as an FMU has no per-simulator results at all — so the second simulator's branch fell back to the phase the first simulator reached. That is 7, verified, with no comparison to go with it, and the report generator trusts
phase >= 7to mean there is one.Two changes:
Testing
The report cell was evaluated directly, before and after, by extracting the real expression from
test.py:The reference-file loop was executed as it stands, with git and omc stubbed, over three libraries sharing one directory. Before, the third lost
fmi; after, all three keep it, and each still ends up with the same prepared reference files.resultValuesanddataForSimulatorwere exercised over the shapes that matter: a simulator with no results of its own (phase capped at the build, no diff, no simulation time, and the row records the same), a build that failed (keeps its own lower phase), a simulator with its own results (untouched), one that ran and failed (reports its own failure, not the other's success), and the primary branch (passed through unchanged).Related to #297, which turned this from a silent mistest into a crash.
Generated by Claude Code.