Skip to content

Critical: [SimExec] Stream handoff routes nonlinear flowsheets to the next execution item #19

Description

@bernalde

Maintainer triage (June 29, 2026)

  • Status: Verified bug against the current master source in this fork.
  • Severity: Critical.
  • Area labels: area:simexec, area:flowsheet.
  • Tackle batch: P1 - critical blockers and severe unit errors.

The source review confirmed that the cited code path and failure mode are still present. The original audit details are preserved below for reproduction notes and suggested fixes.


Original audit details

Part of the model-correctness audit — see #17.

Severity: critical | Category: logic | Location: PharmaPy/SimExec.py lines 129-139, 149-153

What's wrong

After solving a unit name, the destination of the Connection is taken as self.execution_names[ind + 1] (the next unit in the topological execution order), while the actual successor(s) of name are in neighbors = self.graph[name]. neighbors is only used as a boolean gate (len(neighbors) > 0); its contents are ignored when choosing the destination. So connection = Connection(source_uo=getattr(self, name), destination_uo=getattr(self, uo_next)) with uo_next = execution_names[ind+1] routes the outlet to whatever unit happens to be next in the sorted order, not to the true downstream unit.

Why it's incorrect

Topological order is not unique and does not equal the physical edge list except for a single linear chain. Example: graph A->C, B->C, C->D (Mixer C fed by A and B). A valid topological order from topological_bfs is [A, B, C, D]. Processing A (ind=0): neighbors=[C] so the gate passes, but execution_names[1]=B, so transfer_data() copies A.Outlet into B's Inlet/Phases. A's material/energy is delivered to the wrong unit and B's true feed is overwritten. This violates mass and energy conservation across the flowsheet (a stream is delivered to a unit that is not connected to its source). It only coincides with correct behavior when the flowsheet is a single linear chain where topological order equals the edge sequence.

Suggested regression test

Build a Y-shaped flowsheet {'A':['C'], 'B':['C'], 'C':['D']} where A, B carry distinct compositions. After SolveFlowsheet, assert that C.Inlets (Mixer) received A.Outlet and B.Outlet, and that no Connection has destination == B. Then assert total mass into C equals mass out of A plus mass out of B. The current code will instead create a Connection A->B.


Verification: 1/1 adversarial reviewers confirmed (refuted: 0).

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:flowsheetFlowsheet sequencing, stream handoff, and simulation executionarea:simexecSimulation execution, flowsheet orchestration, and handoff bookkeepingbugSomething isn't workingcorrectnessModel/numerical correctness defectseverity:criticalConfirmed bug with severe crash, wrong routing, or major physical-unit impactstatus:verifiedSource-reviewed against the current codebase

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions