Skip to content

feat: add PressureRatioCompressor and CommonPressureRatioSolver#1483

Closed
olelod wants to merge 3 commits intomainfrom
feature/pressure-ratio-domain
Closed

feat: add PressureRatioCompressor and CommonPressureRatioSolver#1483
olelod wants to merge 3 commits intomainfrom
feature/pressure-ratio-domain

Conversation

@olelod
Copy link
Copy Markdown
Contributor

@olelod olelod commented Apr 14, 2026

What

Introduces a ratio-based solving path for compressor trains that operate at a fixed pressure ratio — no shaft speed modelling. This is the domain foundation for the upcoming SIMPLIFIED_SERIAL YAML type (follow-up PR).

Why

The existing solver (CommonSpeedWithPressureControlSolver, formerly OutletPressureSolver) searches for a shaft speed to meet a target outlet pressure. Simplified compressor trains (using GENERIC_FROM_INPUT or GENERIC_FROM_DESIGN_POINT charts) don't model shaft speed — they distribute the total pressure ratio equally
across stages. This PR adds domain support for that approach.

Changes

New domain entities:

  • PressureRatioCompressor — a ProcessUnit that evaluates at a given pressure ratio via propagate_stream_at_pressure_ratio(). Only accepts generic chart types.
  • CommonPressureRatioSolver — distributes equal pressure ratio (geometric mean) across all PressureRatioCompressor stages, applies minimum recirculation per stage (individual anti-surge). Exposes get_max_standard_rate() for stonewall-based feasibility.

Solver hierarchy refactored:

  • New ProcessSystemSolver ABC with find_solution(pressure_constraint, inlet_stream) → Solution
  • OutletPressureSolver → renamed to CommonSpeedWithPressureControlSolver (speed-based path)
  • CommonPressureRatioSolver (ratio-based path, no speed search)

FeasibilitySolver split:

  • RunnerBasedFeasibility — uses runner + stonewall limits (speed-based path)
  • RatioBasedFeasibility — uses per-stage stonewall limits from chart data directly

Cross-validation guards:

  • CommonPressureRatioSolver rejects speed-based Compressor units
  • ProcessSystemRunner rejects PressureRatioCompressor units

Both raise DomainValidationException at construction time.

Tests

  • test_pressure_ratio_compressor.py — unit tests for the new process unit
  • test_common_pressure_ratio_solver.py — solver tests with 2-stage systems
  • Existing solver tests updated for the rename

@olelod olelod force-pushed the feature/pressure-ratio-domain branch 2 times, most recently from 865110e to fb0b0b0 Compare April 15, 2026 08:16
@tj098895
Copy link
Copy Markdown
Contributor

tj098895 commented Apr 15, 2026

Can this be rather solved by:

  • Not use a shaft (and hence no shaft / speed strategy)
  • Use a "PressureRatioStrategy" across compressors instead?

Can we then reuse the existing compressor etc and separate solving from simulation better?

But at the same time, this compressor is a "synthetic generic compressor", so might be relevant to keep that as a separate thing anyways somehow? Not sure how this aligns with reality and what is the use of something "unreal"?

@olelod
Copy link
Copy Markdown
Contributor Author

olelod commented Apr 15, 2026

Can this be rather solved by:

  • Not use a shaft (and hence no shaft / speed strategy)
  • Use a "PressureRatioStrategy" across compressors instead?

Can we then reuse the existing compressor etc and separate solving from simulation better?

But at the same time, this compressor is a "synthetic generic compressor", so might be relevant to keep that as a separate thing anyways somehow? Not sure how this aligns with reality and what is the use of something "unreal"?

We of course could. But would not that mean having to deal with shaft: Shaft | None, speed: float | None etc?

The generic chart is fundamentally not a real compressor — it's a synthetic stand-in used when you don't have actual vendor curves. You give it an efficiency and it tells you how much work it does for a given inlet stream and pressure ratio. There's no shaft speed involved because the machine isn't modelled at that level of detail. We don't
take the work performed and the rate put in, to impose a speed (interpolate it in the compressor chart) - and find three different speeds for three generic compressors in a train. We simply don't care about speed at all - only the work performed by the generic compressor.

The typical use case is early-phase studies or concept screening — you want a rough energy estimate for a compression stage before you have a vendor quote or a real performance curve. You're not pretending to know the machine; you're explicitly saying you don't.

inner_inlet = current_inlet

outlet_with_recirc = unit.propagate_stream_at_pressure_ratio(
inner_inlet, pressure_ratio=pressure_ratio_per_stage
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can pressure ratio be a config that is being set instead, calculated by the compressor pressure ratio solver, in order to always use the propagate_stream method? I see now however, that the compressor is a bit different wrt. behaviour, so probably a good idea to separate it from a "normal compressor process impl"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe PressureRatioCompressor should not inherit from ProcessUnit at all? Make it something separate?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will such a system use ASV/recirc loop and other chokes, or not relevant?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is of course individual recirculation to surge line (if the rate is too low), but this is just using min/max_rate_given_head on the compressor chart - no need for recirc ProcessUnits.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and upstream/downstream choke is not relevant. multiple streams and pressures should not be relevant for simplified trains.

max=max(0.0, max_sm3_per_day - inlet_sm3_per_day),
)

def propagate_stream_at_pressure_ratio(self, inlet_stream: FluidStream, pressure_ratio: float) -> FluidStream:
Copy link
Copy Markdown
Contributor

@frodehk frodehk Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Have a setter for pressure_ratio, and use propagate_stream() to fulfill the ProcessUnit contract. Other process units are already doing this, e.g. Choke (set_pressure_change).

Alternatively, split the ProcessUnit interface? e.g. RatioBasedUnit

Comment thread src/libecalc/domain/process/process_solver/common_pressure_ratio_solver.py Outdated
@olelod olelod force-pushed the feature/pressure-ratio-domain branch from fb0b0b0 to 4573d90 Compare April 17, 2026 11:48
olelod added 3 commits April 22, 2026 09:19
Introduce a ratio-based solving path for compressor trains that operate
at a fixed pressure ratio (no shaft speed, the legacy simplified train).
The total pressure ratio is distributed equally across stages (geometric
mean), with individual anti-surge recirculation computed analytically.

New domain entities:
- PressureRatioCompressor: ProcessUnit that evaluates at a given pressure
  ratio via propagate_stream_at_pressure_ratio(). Validates that only
  generic chart types are used (GENERIC_FROM_INPUT or GENERIC_FROM_DESIGN_POINT).
- CommonPressureRatioSolver: distributes equal pressure ratio across all
  PressureRatioCompressor stages, applies minimum recirculation per stage.
  Implements ProcessSystemSolver ABC. Exposes get_max_standard_rate()
  for stonewall-based feasibility.

Solver hierarchy refactored:
- ProcessSystemSolver ABC: find_solution(pressure_constraint, inlet_stream)
- CommonSpeedWithPressureControlSolver (renamed from OutletPressureSolver):
  speed-based path with choke/ASV pressure control
- CommonPressureRatioSolver: ratio-based path, no speed search

FeasibilitySolver split into two implementations:
- RunnerBasedFeasibility: uses runner + stonewall limits (speed-based path)
- RatioBasedFeasibility: uses per-stage stonewall limits from chart data
PressureRatioCompressor no longer inherits ProcessUnit (StreamPropagator).
It now implements the new PressureRatioUnit ABC which only contracts the
ratio-based evaluation methods.

- Add PressureRatioUnit ABC to process_system/
- PressureRatioCompressor: swap base class, remove NotImplementedError propagate_stream()
- CommonPressureRatioSolver: isinstance checks on PressureRatioUnit, drop Compressor guard
- ProcessSystemRunner: drop PressureRatioCompressor guard (now enforced by type system)
- Remove test_propagate_stream_raises_not_implemented (behaviour is gone by design)
…o, CommonSpeedWithPressureControlSolver → OutletPressureSolverSpeed
@olelod
Copy link
Copy Markdown
Contributor Author

olelod commented May 3, 2026

replaced by #1527

@olelod olelod closed this May 3, 2026
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.

3 participants