Skip to content

Let the other simulators run when the first one fails - #311

Merged
adrpo merged 1 commit into
masterfrom
fix-first-fmi-simulator-privileged
Aug 12, 2026
Merged

Let the other simulators run when the first one fails#311
adrpo merged 1 commit into
masterfrom
fix-first-fmi-simulator-privileged

Conversation

@adrpo

@adrpo adrpo commented Aug 12, 2026

Copy link
Copy Markdown
Member

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 is an ordinary failure and not just a slow one.

What it cost

In the first combined master-fmi run (2026-08-12 11:04 UTC), 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 combined run they are phase 5 in both tables, and FMPy's simulation time is
recorded as exactly 0: OMSimulator failed in under a second for 344 of them, and FMPy
never started.

master-fmi-fmpy regressions, 08-11 06:50 → 08-12 11:04 models
genuinely found by #309 (PowerGrids_dev, PowerGrids_symb_jac, ClaRa_dev, ScalableTestSuite_noopt) 213
first simulator's veto 433
other ~7
total 653

Of the 440 regressions outside the #309 libraries, 436 report exactly OMSimulator's
finalphase
, and their combined FMPy simulation time is 0.0 minutes against
OMSimulator's 78.2.

It is also why the comparison page reports OMSimulator's version — and its JSON parse
warning — where FMPy version 0.3.29 used to be:

to 1.2.0-master (7fd853cf...) warning: Failed to load settings: [json.exception.parse_error.101]
... OMSimulator v3.0.0.post162-g77dcf25-linux info: 1 warnings info: 0 errors

FMPy never ran to say what it was. Both sides of that comparison are the same omc commit
7fd853cf, so none of this is a compiler change.

The change

The first simulator now records its failure and falls through to the others — which is
what every simulator after it already did:

except TimeoutError as e:
  execstat["sim"] = monotonic()-start
  if len(fmisimulators) > 1:
    firstSimulatorFailed = True
    ...
  else:
    writeResultAndExit(0, True, omc, omc_new)

Nothing changes when only one simulator is configured: that case still ends the model
where it always has, so the non-FMI code paths and the single-simulator FMI jobs are
untouched.

Testing

On MyLibrary.Blocks.Examples.PID_Controller, with the first simulator replaced by
/bin/false:

phase simulators
before 5 {}
after 5 {"fmpy": {"sim": 0.64, "diff": {...}, "phase": 7}}

and unchanged in both:

case result
one failing simulator phase 5, os._exit as before
both simulators working OMSimulator phase 7, fmpy phase 7

Note

Same shape as #308/#310: a failure path that leaves before the accounting is done. Worth
watching for others.


generated by Claude Code

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

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MYvMaAotMy425H7nvKzWX7
@adrpo
adrpo enabled auto-merge (squash) August 12, 2026 20:01
@adrpo
adrpo merged commit dc682c8 into master Aug 12, 2026
10 of 11 checks passed
@adrpo
adrpo deleted the fix-first-fmi-simulator-privileged branch August 12, 2026 22:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant