Skip to content

Define and test the contract for calling a solver's run more than once #252

Description

@camUrban

Problem Statement

None of the solvers guard against a second call to run. Each solver's run method sets self.ran = True only at the end and never checks it on entry, so calling run again on an already-solved solver silently re-executes the entire solve pipeline. The ran flag is consumed only by downstream code: output.py gates visualization and results on it, and _serialization.py gates whether solved state is saved. It is not a re-entry guard. From reading the code, the second pass appears to overwrite the solver's result arrays in place rather than accumulate (the steady stacks and the unsteady per-step wake lists are indexed and overwritten, and calculate_streamlines rebuilds its grid from the seed points each call), but this behavior is neither specified nor tested, and the unsteady wake path in particular has not been verified to reproduce the first run's results. We do not yet know what the intended contract should be: reject a second run with a clear error, guarantee an idempotent re-solve, or document re-running as unsupported and require constructing a fresh solver from the problem.

Location(s): pterasoftware/steady_horseshoe_vortex_lattice_method.py, pterasoftware/steady_ring_vortex_lattice_method.py, pterasoftware/unsteady_ring_vortex_lattice_method.py, pterasoftware/aeroelastic_unsteady_ring_vortex_lattice_method.py, pterasoftware/free_flight_unsteady_ring_vortex_lattice_method.py, pterasoftware/output.py, pterasoftware/_serialization.py

Proposed Solution

  1. Decide the intended contract for a repeated run call across all solvers, including the aeroelastic and free-flight subclasses that inherit the unsteady run. The main options are raising on a second call, guaranteeing an idempotent re-solve, or documenting re-running as unsupported.
  2. Clarify the role of self.ran in light of that decision: keep it purely as the "has been solved at least once" marker that output.py and _serialization.py consume, or additionally use it as the re-entry guard.
  3. Implement whatever the decision requires, then add tests that cement the contract so it cannot regress silently.

Additional Context

This is related to #240 (pause and resume unsteady simulations). That feature wants a saved mid-run solver to resume stepping and produce results identical to an uninterrupted run, which is a deliberate, stateful form of continuing a solve. Settling the plain re-run contract here clarifies the baseline that #240 builds on: whether continuing a solve is something the solver objects support at all, and if so, through which entry point.

Metadata

Metadata

Assignees

No one assigned

    Labels

    maintenanceImprovements or additions to documentation, testing, robustness, or toolingquestionFurther information is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions