Skip to content

refactor: add rotating equipment, drive train and power supply to energy domain#1486

Closed
frodehk wants to merge 15 commits intomainfrom
refactor/drive-train-energy-domain
Closed

refactor: add rotating equipment, drive train and power supply to energy domain#1486
frodehk wants to merge 15 commits intomainfrom
refactor/drive-train-energy-domain

Conversation

@frodehk
Copy link
Copy Markdown
Contributor

@frodehk frodehk commented Apr 15, 2026

Summary

Introduces an energy domain model for drive trains, rotating equipment, and power supplies — decoupled from the process domain.

New modules

Rotating equipment (domain/energy/rotating_equipment/)

  • RotatingEquipment — abstract base for anything drawing mechanical power from a shaft
  • ShaftPowerConsumer — implementation, computes shaft power from enthalpy rise (P = ṁ × Δh)

Drive trains (domain/energy/drive_train/)

  • DriveTrain — abstract base: sums shaft power demand, accounts for mechanical losses
  • TurbineDriveTrain — gas turbine driver: evaluates fuel via Turbine (knows about fuel)
  • ElectricDriveTrain — electric motor: computes electrical power demand only (fuel-agnostic) - needs a power supply

Power supplies (domain/energy/power_supply/)

  • PowerSupply — abstract base: power demand [MW] → fuel rate + capacity margin
  • GeneratorSupply — fuel-burning generator (delegates to GeneratorSetModel)
  • ShoreSupply — subsea cable from shore (zero fuel, cable loss)
  • WindSupply — offshore wind (zero fuel, variable capacity per timestep)

Design

Process domain:    Shaft → speed → Compressor → propagate_stream → enthalpy in/out
Orchestration:     Process result → creates ShaftPowerConsumer(h_in, h_out, mass_rate)
Energy domain:     ShaftPowerConsumer → shaft power
                   DriveTrain([ShaftPowerConsumer, ...]) → power demand
                   PowerSupply(power_demand) → fuel / capacity margin

The energy domain depends on infrastructure models (Turbine, GeneratorSetModel) only through thin protocols in domain/energy/infrastructure_contracts.py — no direct imports.

DriveTrain does not own the physical shaft — it only sees resulting power demand. Speed control and chart lookups remain in the process domain.

TurbineDriveTrain is a special case where the turbine converts mechanical power directly to fuel. ElectricDriveTrain only computes electrical power demand — fuel conversion is handled by a separate PowerSupply, composed at a higher level.


Simplified diagram:

flowchart LR
    PR["Process Result\n(h_in, h_out, ṁ)"]
    SPC["ShaftPowerConsumer\nP = ṁ × Δh"]
    TDT["TurbineDriveTrain"]
    EDT["ElectricDriveTrain"]
    T["Turbine"]
    FUEL["Fuel\n(Sm³/day)"]

    subgraph PowerSupply["Power Supply"]
        GSS["GeneratorSupply"]
        SCS["ShoreSupply"]
        WS["WindSupply"]
    end

    PR -- "creates" --> SPC
    SPC -- "shaft power MW" --> TDT
    SPC -- "shaft power MW" --> EDT
    TDT -- "mechanical power MW\n(÷ mech. efficiency)" --> T
    T -- "fuel rate" --> FUEL
    EDT -- "power demand MW\n(÷ mech. efficiency)" --> PowerSupply
    GSS -- "fuel rate" --> FUEL

    style T fill:#f9f,stroke:#333
    style GSS fill:#bbf,stroke:#333
    style SCS fill:#bbf,stroke:#333
    style WS fill:#bbf,stroke:#333
    style TDT fill:#fdb,stroke:#333
    style EDT fill:#fdb,stroke:#333
    style FUEL fill:#faa,stroke:#333
Loading

Type of Work

  • Patch: X.Y.Z+1. NEGLIGIBLE visible changes, does not change input or output - OR changes behaviour. Use chore:, refactor: etc
  • Minor: X.Y+1.Z. Minor changes, might ADD new input (YAML), or other backwards-compatible changes. Use feat:, fix:
  • Major: X+1.Y.Z. Major and most likely BREAKING changes, wo. backwards compatibility, or removing temporary backwards compatibility functionality. Use ! or BREAKING:.

See here (internal): https://github.com/equinor/ecalc-internal/discussions/1044

Have you remembered and considered?

  • IF FEAT: I have remembered to update documentation
  • IF FIX OR FEAT: I have remembered to update manual changelog (docs/drafts/next.draft.md)
  • IF BREAKING: I have remembered to update migration guide (docs/docs/migration_guides/)
  • IF BREAKING: I have committed with BREAKING: in footer or ! in header
  • I have added tests (if not, comment why)
  • I have used conventional commits syntax (if you squash, make sure that conventional commit is used)
  • I have included the Github issue nr in the footer!

What is this PR all about?

What else did you consider?

Between the lines?

@frodehk frodehk self-assigned this Apr 15, 2026
@frodehk frodehk requested a review from a team as a code owner April 15, 2026 10:46
@frodehk frodehk changed the title Refactor/drive train energy domain refactor/drive train energy domain Apr 15, 2026
@frodehk frodehk changed the title refactor/drive train energy domain refactor: Add DriveTrain and RotatingEquipment to energy domain Apr 15, 2026
@olelod
Copy link
Copy Markdown
Contributor

olelod commented Apr 15, 2026

should the generator set drive train be an electric drive train which has power supply either from a generator set or from offshore wind or power from shore? we get electricity from multiple sources.

@frodehk
Copy link
Copy Markdown
Contributor Author

frodehk commented Apr 15, 2026

should the generator set drive train be an electric drive train which has power supply either from a generator set or from offshore wind or power from shore? we get electricity from multiple sources.

Yes, definitely - good comment.

@frodehk frodehk changed the title refactor: Add DriveTrain and RotatingEquipment to energy domain refactor: add rotating equipment, drive train and power supply to energy domain Apr 15, 2026
Copy link
Copy Markdown
Contributor

@kjbrak kjbrak left a comment

Choose a reason for hiding this comment

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

Nice work - clean layered architecture with good test coverage. The composability of ElectricDriveTrain + PowerSupply is really well done. A few suggestions below, mostly around input validation and coupling direction.

Comment thread src/libecalc/domain/energy/drive_train/drive_train.py
Comment thread src/libecalc/domain/energy/drive_train/drive_train.py Outdated
Comment thread src/libecalc/domain/energy/drive_train/drive_train.py Outdated
Comment thread src/libecalc/domain/energy/power_supply/wind.py Outdated
Comment thread src/libecalc/domain/energy/drive_train/turbine_drive_train.py Outdated
Comment thread src/libecalc/domain/energy/drive_train/electric_drive_train.py Outdated
@kjbrak
Copy link
Copy Markdown
Contributor

kjbrak commented Apr 17, 2026

ref. Pump integration work

We're working on bringing pumps into the new process domain (branch feature/pump-process-unit-v2). The pump computes
head and efficiency from its chart, but unlike compressors, there's no EoS so we don't have h_in / h_out from a
flash.

However, the pump's enthalpy rise is computable analytically: Δh = head / η = ΔP / (ρ × η) [J/kg]. This is physically
the same concept - specific energy input to the fluid.

Question: Should we expand ShaftPowerConsumer to accept this directly? E.g.:

@classmethod
def from_enthalpy_rise(cls, delta_h_joule_per_kg: float, mass_rate_kg_per_h: float):
...

Or should we make compressor also own its "fluid power" or "delta enthalpy" so we get the same pattern?

Also: for pumps, does DriveTrain.mechanical_efficiency apply the same way as for compressors, or is mechanical loss
already baked into the pump chart efficiency?

Would be good to discuss before merging so the interface works for both compressors and pumps.

@frodehk frodehk closed this Apr 18, 2026
@frodehk
Copy link
Copy Markdown
Contributor Author

frodehk commented Apr 18, 2026

Superseded by #1492

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