fluxopt.elements
¶
Classes:
| Name | Description |
|---|---|
Element |
Base for user-facing element models (shared pydantic config). |
Carrier |
Physical energy medium (electricity, heat, gas, …). |
Sizing |
Capacity optimization parameters. |
Investment |
Singular discrete build-timing optimization. |
Status |
Binary on/off behavior parameters. |
Flow |
A single energy flow on a carrier. |
Effect |
A tracked quantity across the optimization horizon (cost, CO₂, …). |
Storage |
Energy storage with level dynamics. |
PiecewiseConversion |
Piecewise-linear conversion linking N flows. |
Functions:
| Name | Description |
|---|---|
qualified_id |
Format a qualified flow id: |
node_id |
Format a carrier-node id: |
Element
pydantic-model
¶
Carrier
pydantic-model
¶
Bases: Element
Physical energy medium (electricity, heat, gas, …).
Show JSON schema:
{
"description": "Physical energy medium (electricity, heat, gas, \u2026).",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"nodes": {
"items": {
"type": "string"
},
"title": "Nodes",
"type": "array"
},
"unit": {
"default": "MWh",
"title": "Unit",
"type": "string"
},
"color": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Color"
},
"description": {
"default": "",
"title": "Description",
"type": "string"
}
},
"required": [
"id"
],
"title": "Carrier",
"type": "object"
}
Fields:
nodes
pydantic-field
¶
Sub-nodes for multi-node balancing. Empty means single-node.
Sizing
pydantic-model
¶
Bases: Element
Capacity optimization parameters.
The solver decides the optimal size within [size_min, size_max].
mandatory=True: continuous, size in [min, max], no binary.mandatory=False: binary indicator gates size: 0 or [min, max].size_min == size_maxwithmandatory=False: binary invest at exact size (yes/no).
See: docs/math/sizing.md
Show JSON schema:
{
"$defs": {
"ProfileRef": {
"description": "Reference to a time-series stored outside the model definition.\n\nA serializable stand-in for an inline ``Variate`` array: the profile lives\nin a data file / dataset and is named here, so structural definitions\nround-trip to YAML/JSON without inlining 8760-point series. Resolve it to a\n:class:`xr.DataArray` with :meth:`resolve` before building the model.",
"properties": {
"dataset": {
"title": "Dataset",
"type": "string"
},
"variable": {
"title": "Variable",
"type": "string"
}
},
"required": [
"dataset",
"variable"
],
"title": "ProfileRef",
"type": "object"
},
"Variate": {
"anyOf": [
{
"type": "number"
},
{
"type": "integer"
},
{
"items": {
"type": "number"
},
"type": "array"
},
{
"$ref": "#/$defs/ProfileRef"
}
]
}
},
"description": "Capacity optimization parameters.\n\nThe solver decides the optimal size within [size_min, size_max].\n\n- ``mandatory=True``: continuous, size in [min, max], no binary.\n- ``mandatory=False``: binary indicator gates size: 0 or [min, max].\n- ``size_min == size_max`` with ``mandatory=False``: binary invest\n at exact size (yes/no).\n\nSee: docs/math/sizing.md",
"properties": {
"size_min": {
"minimum": 0,
"title": "Size Min",
"type": "number"
},
"size_max": {
"minimum": 0,
"title": "Size Max",
"type": "number"
},
"mandatory": {
"default": true,
"title": "Mandatory",
"type": "boolean"
},
"effects_per_size": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size",
"type": "object"
},
"effects_fixed": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed",
"type": "object"
}
},
"required": [
"size_min",
"size_max"
],
"title": "Sizing",
"type": "object"
}
Fields:
-
size_min(float) -
size_max(float) -
mandatory(bool) -
effects_per_size(dict[str, Variate]) -
effects_fixed(dict[str, Variate])
Validators:
-
_bounds_are_ordered
effects_per_size
pydantic-field
¶
Effect cost per unit size (e.g. €/MW).
Investment
pydantic-model
¶
Bases: Element
Singular discrete build-timing optimization.
The solver decides WHEN to build (which period) and at what size.
Once built, capacity is available for lifetime periods.
Size is decided once — no growth or partial retirement.
Show JSON schema:
{
"$defs": {
"ProfileRef": {
"description": "Reference to a time-series stored outside the model definition.\n\nA serializable stand-in for an inline ``Variate`` array: the profile lives\nin a data file / dataset and is named here, so structural definitions\nround-trip to YAML/JSON without inlining 8760-point series. Resolve it to a\n:class:`xr.DataArray` with :meth:`resolve` before building the model.",
"properties": {
"dataset": {
"title": "Dataset",
"type": "string"
},
"variable": {
"title": "Variable",
"type": "string"
}
},
"required": [
"dataset",
"variable"
],
"title": "ProfileRef",
"type": "object"
},
"Variate": {
"anyOf": [
{
"type": "number"
},
{
"type": "integer"
},
{
"items": {
"type": "number"
},
"type": "array"
},
{
"$ref": "#/$defs/ProfileRef"
}
]
}
},
"description": "Singular discrete build-timing optimization.\n\nThe solver decides WHEN to build (which period) and at what size.\nOnce built, capacity is available for ``lifetime`` periods.\nSize is decided once \u2014 no growth or partial retirement.",
"properties": {
"size_min": {
"minimum": 0,
"title": "Size Min",
"type": "number"
},
"size_max": {
"minimum": 0,
"title": "Size Max",
"type": "number"
},
"mandatory": {
"default": true,
"title": "Mandatory",
"type": "boolean"
},
"lifetime": {
"anyOf": [
{
"exclusiveMinimum": 0,
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Lifetime"
},
"prior_size": {
"default": 0.0,
"minimum": 0,
"title": "Prior Size",
"type": "number"
},
"effects_per_size_at_build": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size At Build",
"type": "object"
},
"effects_fixed_at_build": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed At Build",
"type": "object"
},
"effects_per_size_recurring": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size Recurring",
"type": "object"
},
"effects_fixed_recurring": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed Recurring",
"type": "object"
}
},
"required": [
"size_min",
"size_max"
],
"title": "Investment",
"type": "object"
}
Fields:
-
size_min(float) -
size_max(float) -
mandatory(bool) -
lifetime(int | None) -
prior_size(float) -
effects_per_size_at_build(dict[str, Variate]) -
effects_fixed_at_build(dict[str, Variate]) -
effects_per_size_recurring(dict[str, Variate]) -
effects_fixed_recurring(dict[str, Variate])
Validators:
-
_bounds_are_ordered
mandatory
pydantic-field
¶
mandatory: bool = True
If True, must build exactly once; if False, may build at most once.
effects_per_size_at_build
pydantic-field
¶
One-time per-MW costs charged in the build period.
effects_fixed_at_build
pydantic-field
¶
One-time fixed costs charged in the build period.
effects_per_size_recurring
pydantic-field
¶
Recurring per-MW costs charged every active period.
Status
pydantic-model
¶
Bases: Element
Binary on/off behavior parameters.
Together with relative bounds, gives semi-continuous behavior:
{0} U [min, max] * size.
See: docs/math/status.md
Show JSON schema:
{
"$defs": {
"ProfileRef": {
"description": "Reference to a time-series stored outside the model definition.\n\nA serializable stand-in for an inline ``Variate`` array: the profile lives\nin a data file / dataset and is named here, so structural definitions\nround-trip to YAML/JSON without inlining 8760-point series. Resolve it to a\n:class:`xr.DataArray` with :meth:`resolve` before building the model.",
"properties": {
"dataset": {
"title": "Dataset",
"type": "string"
},
"variable": {
"title": "Variable",
"type": "string"
}
},
"required": [
"dataset",
"variable"
],
"title": "ProfileRef",
"type": "object"
},
"Variate": {
"anyOf": [
{
"type": "number"
},
{
"type": "integer"
},
{
"items": {
"type": "number"
},
"type": "array"
},
{
"$ref": "#/$defs/ProfileRef"
}
]
}
},
"description": "Binary on/off behavior parameters.\n\nTogether with relative bounds, gives semi-continuous behavior:\n``{0} U [min, max] * size``.\n\nSee: docs/math/status.md",
"properties": {
"uptime_min": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Uptime Min"
},
"uptime_max": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Uptime Max"
},
"downtime_min": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Downtime Min"
},
"downtime_max": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Downtime Max"
},
"effects_per_running_hour": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Running Hour",
"type": "object"
},
"effects_per_startup": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Startup",
"type": "object"
}
},
"title": "Status",
"type": "object"
}
Fields:
-
uptime_min(float | None) -
uptime_max(float | None) -
downtime_min(float | None) -
downtime_max(float | None) -
effects_per_running_hour(dict[str, Variate]) -
effects_per_startup(dict[str, Variate])
Validators:
-
_durations_are_ordered
effects_per_running_hour
pydantic-field
¶
Effect cost per running hour.
Flow
pydantic-model
¶
Bases: Element
A single energy flow on a carrier.
short_id defaults to carrier (or carrier:node when node
is set). Set explicitly to disambiguate multiple flows on the same
carrier::
Flow(carrier='elec') # short_id='elec'
Flow(carrier='heat', node='A') # short_id='heat:A'
Flow(carrier='elec', short_id='base') # short_id='base'
short_id must be unique within a component. The system-global
qualified id component(short_id) is derived at build time and
appears as the flow coordinate in ModelData and results; the
flow itself is never modified. Storage resolves colliding short_ids
to charge / discharge at that point.
See: docs/math/flows.md
Show JSON schema:
{
"$defs": {
"Investment": {
"description": "Singular discrete build-timing optimization.\n\nThe solver decides WHEN to build (which period) and at what size.\nOnce built, capacity is available for ``lifetime`` periods.\nSize is decided once \u2014 no growth or partial retirement.",
"properties": {
"size_min": {
"minimum": 0,
"title": "Size Min",
"type": "number"
},
"size_max": {
"minimum": 0,
"title": "Size Max",
"type": "number"
},
"mandatory": {
"default": true,
"title": "Mandatory",
"type": "boolean"
},
"lifetime": {
"anyOf": [
{
"exclusiveMinimum": 0,
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Lifetime"
},
"prior_size": {
"default": 0.0,
"minimum": 0,
"title": "Prior Size",
"type": "number"
},
"effects_per_size_at_build": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size At Build",
"type": "object"
},
"effects_fixed_at_build": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed At Build",
"type": "object"
},
"effects_per_size_recurring": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size Recurring",
"type": "object"
},
"effects_fixed_recurring": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed Recurring",
"type": "object"
}
},
"required": [
"size_min",
"size_max"
],
"title": "Investment",
"type": "object"
},
"ProfileRef": {
"description": "Reference to a time-series stored outside the model definition.\n\nA serializable stand-in for an inline ``Variate`` array: the profile lives\nin a data file / dataset and is named here, so structural definitions\nround-trip to YAML/JSON without inlining 8760-point series. Resolve it to a\n:class:`xr.DataArray` with :meth:`resolve` before building the model.",
"properties": {
"dataset": {
"title": "Dataset",
"type": "string"
},
"variable": {
"title": "Variable",
"type": "string"
}
},
"required": [
"dataset",
"variable"
],
"title": "ProfileRef",
"type": "object"
},
"Sizing": {
"description": "Capacity optimization parameters.\n\nThe solver decides the optimal size within [size_min, size_max].\n\n- ``mandatory=True``: continuous, size in [min, max], no binary.\n- ``mandatory=False``: binary indicator gates size: 0 or [min, max].\n- ``size_min == size_max`` with ``mandatory=False``: binary invest\n at exact size (yes/no).\n\nSee: docs/math/sizing.md",
"properties": {
"size_min": {
"minimum": 0,
"title": "Size Min",
"type": "number"
},
"size_max": {
"minimum": 0,
"title": "Size Max",
"type": "number"
},
"mandatory": {
"default": true,
"title": "Mandatory",
"type": "boolean"
},
"effects_per_size": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size",
"type": "object"
},
"effects_fixed": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed",
"type": "object"
}
},
"required": [
"size_min",
"size_max"
],
"title": "Sizing",
"type": "object"
},
"Status": {
"description": "Binary on/off behavior parameters.\n\nTogether with relative bounds, gives semi-continuous behavior:\n``{0} U [min, max] * size``.\n\nSee: docs/math/status.md",
"properties": {
"uptime_min": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Uptime Min"
},
"uptime_max": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Uptime Max"
},
"downtime_min": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Downtime Min"
},
"downtime_max": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Downtime Max"
},
"effects_per_running_hour": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Running Hour",
"type": "object"
},
"effects_per_startup": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Startup",
"type": "object"
}
},
"title": "Status",
"type": "object"
},
"Variate": {
"anyOf": [
{
"type": "number"
},
{
"type": "integer"
},
{
"items": {
"type": "number"
},
"type": "array"
},
{
"$ref": "#/$defs/ProfileRef"
}
]
}
},
"description": "A single energy flow on a carrier.\n\n``short_id`` defaults to ``carrier`` (or ``carrier:node`` when ``node``\nis set). Set explicitly to disambiguate multiple flows on the same\ncarrier::\n\n Flow(carrier='elec') # short_id='elec'\n Flow(carrier='heat', node='A') # short_id='heat:A'\n Flow(carrier='elec', short_id='base') # short_id='base'\n\n``short_id`` must be unique within a component. The system-global\nqualified id ``component(short_id)`` is derived at build time and\nappears as the ``flow`` coordinate in ``ModelData`` and results; the\nflow itself is never modified. Storage resolves colliding short_ids\nto ``charge`` / ``discharge`` at that point.\n\nSee: docs/math/flows.md",
"properties": {
"carrier": {
"title": "Carrier",
"type": "string"
},
"short_id": {
"default": "",
"title": "Short Id",
"type": "string"
},
"node": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Node"
},
"size": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/$defs/Sizing"
},
{
"$ref": "#/$defs/Investment"
},
{
"type": "null"
}
],
"default": null,
"title": "Size"
},
"relative_rate_min": {
"$ref": "#/$defs/Variate",
"default": 0.0
},
"relative_rate_max": {
"$ref": "#/$defs/Variate",
"default": 1.0
},
"fixed_relative_profile": {
"anyOf": [
{
"$ref": "#/$defs/Variate"
},
{
"type": "null"
}
],
"default": null
},
"effects_per_flow_hour": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Flow Hour",
"type": "object"
},
"flow_hours_min": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Flow Hours Min"
},
"flow_hours_max": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Flow Hours Max"
},
"load_factor_min": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Load Factor Min"
},
"load_factor_max": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Load Factor Max"
},
"ramp_up_per_hour": {
"anyOf": [
{
"$ref": "#/$defs/Variate"
},
{
"type": "null"
}
],
"default": null
},
"ramp_down_per_hour": {
"anyOf": [
{
"$ref": "#/$defs/Variate"
},
{
"type": "null"
}
],
"default": null
},
"status": {
"anyOf": [
{
"$ref": "#/$defs/Status"
},
{
"type": "null"
}
],
"default": null
},
"prior_rates": {
"anyOf": [
{
"items": {
"type": "number"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Prior Rates"
}
},
"required": [
"carrier"
],
"title": "Flow",
"type": "object"
}
Fields:
-
carrier(str) -
short_id(str) -
node(str | None) -
size(float | Sizing | Investment | None) -
relative_rate_min(Variate) -
relative_rate_max(Variate) -
fixed_relative_profile(Variate | None) -
effects_per_flow_hour(dict[str, Variate]) -
flow_hours_min(float | None) -
flow_hours_max(float | None) -
load_factor_min(float | None) -
load_factor_max(float | None) -
ramp_up_per_hour(Variate | None) -
ramp_down_per_hour(Variate | None) -
status(Status | None) -
prior_rates(list[float] | None)
short_id
pydantic-field
¶
short_id: str = ''
Component-local identifier; defaults to carrier
(or carrier:node). The qualified form component(short_id)
is derived at build time.
size
pydantic-field
¶
size: float | Sizing | Investment | None = None
Nominal capacity [MW], Sizing for investment optimization,
or None (unsized / unbounded).
relative_rate_min
pydantic-field
¶
relative_rate_min: Variate = 0.0
Lower bound as fraction of size.
relative_rate_max
pydantic-field
¶
relative_rate_max: Variate = 1.0
Upper bound as fraction of size.
fixed_relative_profile
pydantic-field
¶
fixed_relative_profile: Variate | None = None
Fixed profile as fraction of size; sets both lower and upper bounds equal to the profile value.
effects_per_flow_hour
pydantic-field
¶
Effect coefficients per flow-hour (e.g. €/MWh).
flow_hours_min
pydantic-field
¶
flow_hours_min: float | None = None
Lower bound on total flow-hours Σ P·Δt [MWh],
applied to each period independently.
flow_hours_max
pydantic-field
¶
flow_hours_max: float | None = None
Upper bound on total flow-hours Σ P·Δt [MWh],
applied to each period independently.
load_factor_min
pydantic-field
¶
load_factor_min: float | None = None
Lower bound on utilization Σ P·Δt / (size·T),
applied to each period independently. Requires size.
load_factor_max
pydantic-field
¶
load_factor_max: float | None = None
Upper bound on utilization Σ P·Δt / (size·T),
applied to each period independently. Requires size.
ramp_up_per_hour
pydantic-field
¶
ramp_up_per_hour: Variate | None = None
Maximum rate increase between consecutive
timesteps, as fraction of size per hour. Requires size.
ramp_down_per_hour
pydantic-field
¶
ramp_down_per_hour: Variate | None = None
Maximum rate decrease between consecutive
timesteps, as fraction of size per hour. Requires size.
status
pydantic-field
¶
status: Status | None = None
On/off behavior (semi-continuous, startup costs, durations).
prior_rates
pydantic-field
¶
Flow rates [MW] before the horizon, used for status initial conditions.
model_post_init
¶
model_post_init(__context: Any) -> None
Default short_id from carrier/node and validate field combinations.
Source code in src/fluxopt/elements.py
Effect
pydantic-model
¶
Bases: Element
A tracked quantity across the optimization horizon (cost, CO₂, …).
One effect is designated as the objective to minimize via the
objective argument of optimize(). Others can be bounded
to enforce budgets (e.g. emission caps).
Effects accumulate contributions from two domains:
- Temporal — per-timestep flow costs, running costs, startup costs.
- Lump — one-time sizing costs and fixed costs.
Cross-effect chains (e.g. CO₂ → cost) are supported via
contribution_from.
See: docs/math/effects.md
Show JSON schema:
{
"$defs": {
"ProfileRef": {
"description": "Reference to a time-series stored outside the model definition.\n\nA serializable stand-in for an inline ``Variate`` array: the profile lives\nin a data file / dataset and is named here, so structural definitions\nround-trip to YAML/JSON without inlining 8760-point series. Resolve it to a\n:class:`xr.DataArray` with :meth:`resolve` before building the model.",
"properties": {
"dataset": {
"title": "Dataset",
"type": "string"
},
"variable": {
"title": "Variable",
"type": "string"
}
},
"required": [
"dataset",
"variable"
],
"title": "ProfileRef",
"type": "object"
},
"Variate": {
"anyOf": [
{
"type": "number"
},
{
"type": "integer"
},
{
"items": {
"type": "number"
},
"type": "array"
},
{
"$ref": "#/$defs/ProfileRef"
}
]
}
},
"description": "A tracked quantity across the optimization horizon (cost, CO\u2082, \u2026).\n\nOne effect is designated as the objective to minimize via the\n``objective`` argument of ``optimize()``. Others can be bounded\nto enforce budgets (e.g. emission caps).\n\nEffects accumulate contributions from two domains:\n\n- **Temporal** \u2014 per-timestep flow costs, running costs, startup costs.\n- **Lump** \u2014 one-time sizing costs and fixed costs.\n\nCross-effect chains (e.g. CO\u2082 \u2192 cost) are supported via\n``contribution_from``.\n\nSee: docs/math/effects.md",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"unit": {
"default": "",
"title": "Unit",
"type": "string"
},
"total_max": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Total Max"
},
"total_min": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Total Min"
},
"periodic_max": {
"anyOf": [
{
"$ref": "#/$defs/Variate"
},
{
"type": "null"
}
],
"default": null
},
"periodic_min": {
"anyOf": [
{
"$ref": "#/$defs/Variate"
},
{
"type": "null"
}
],
"default": null
},
"contribution_from": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Contribution From",
"type": "object"
},
"period_weights": {
"anyOf": [
{
"items": {
"type": "number"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Period Weights"
}
},
"required": [
"id"
],
"title": "Effect",
"type": "object"
}
Fields:
-
id(str) -
unit(str) -
total_max(float | None) -
total_min(float | None) -
periodic_max(Variate | None) -
periodic_min(Variate | None) -
contribution_from(dict[str, Variate]) -
period_weights(list[float] | None)
total_max
pydantic-field
¶
total_max: float | None = None
Upper bound on weighted total across all periods.
total_min
pydantic-field
¶
total_min: float | None = None
Lower bound on weighted total across all periods.
periodic_max
pydantic-field
¶
periodic_max: Variate | None = None
Upper bound applied to each period independently. Scalar or per-period values (multi-period only).
periodic_min
pydantic-field
¶
periodic_min: Variate | None = None
Lower bound applied to each period independently. Scalar or per-period values (multi-period only).
contribution_from
pydantic-field
¶
Cross-effect factors {source_effect: factor}.
Scalar factors apply identically to both domains. Time-varying factors
apply per-timestep in the temporal domain and are rejected at build time
when the source effect carries lump (sizing/fixed) contributions.
Storage
pydantic-model
¶
Bases: Element
Energy storage with level dynamics.
Flow ids are qualified as storage(flow) at build time. When both
flows share a short_id, the qualified ids use charge /
discharge instead (the flows themselves are never modified)::
Storage(
id='bat', charging=Flow(carrier='elec'), discharging=Flow(carrier='elec')
) # bat(charge), bat(discharge)
Storage(
id='bat',
charging=Flow(carrier='elec', short_id='in'),
discharging=Flow(carrier='elec', short_id='out'),
) # bat(in), bat(out)
Level balance::
E_{s,t+1} = E_{s,t} (1 - δ)^Δt + P^c η^c Δt - P^d / η^d Δt
See: docs/math/storage.md
Show JSON schema:
{
"$defs": {
"Flow": {
"description": "A single energy flow on a carrier.\n\n``short_id`` defaults to ``carrier`` (or ``carrier:node`` when ``node``\nis set). Set explicitly to disambiguate multiple flows on the same\ncarrier::\n\n Flow(carrier='elec') # short_id='elec'\n Flow(carrier='heat', node='A') # short_id='heat:A'\n Flow(carrier='elec', short_id='base') # short_id='base'\n\n``short_id`` must be unique within a component. The system-global\nqualified id ``component(short_id)`` is derived at build time and\nappears as the ``flow`` coordinate in ``ModelData`` and results; the\nflow itself is never modified. Storage resolves colliding short_ids\nto ``charge`` / ``discharge`` at that point.\n\nSee: docs/math/flows.md",
"properties": {
"carrier": {
"title": "Carrier",
"type": "string"
},
"short_id": {
"default": "",
"title": "Short Id",
"type": "string"
},
"node": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Node"
},
"size": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/$defs/Sizing"
},
{
"$ref": "#/$defs/Investment"
},
{
"type": "null"
}
],
"default": null,
"title": "Size"
},
"relative_rate_min": {
"$ref": "#/$defs/Variate",
"default": 0.0
},
"relative_rate_max": {
"$ref": "#/$defs/Variate",
"default": 1.0
},
"fixed_relative_profile": {
"anyOf": [
{
"$ref": "#/$defs/Variate"
},
{
"type": "null"
}
],
"default": null
},
"effects_per_flow_hour": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Flow Hour",
"type": "object"
},
"flow_hours_min": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Flow Hours Min"
},
"flow_hours_max": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Flow Hours Max"
},
"load_factor_min": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Load Factor Min"
},
"load_factor_max": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Load Factor Max"
},
"ramp_up_per_hour": {
"anyOf": [
{
"$ref": "#/$defs/Variate"
},
{
"type": "null"
}
],
"default": null
},
"ramp_down_per_hour": {
"anyOf": [
{
"$ref": "#/$defs/Variate"
},
{
"type": "null"
}
],
"default": null
},
"status": {
"anyOf": [
{
"$ref": "#/$defs/Status"
},
{
"type": "null"
}
],
"default": null
},
"prior_rates": {
"anyOf": [
{
"items": {
"type": "number"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Prior Rates"
}
},
"required": [
"carrier"
],
"title": "Flow",
"type": "object"
},
"Investment": {
"description": "Singular discrete build-timing optimization.\n\nThe solver decides WHEN to build (which period) and at what size.\nOnce built, capacity is available for ``lifetime`` periods.\nSize is decided once \u2014 no growth or partial retirement.",
"properties": {
"size_min": {
"minimum": 0,
"title": "Size Min",
"type": "number"
},
"size_max": {
"minimum": 0,
"title": "Size Max",
"type": "number"
},
"mandatory": {
"default": true,
"title": "Mandatory",
"type": "boolean"
},
"lifetime": {
"anyOf": [
{
"exclusiveMinimum": 0,
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Lifetime"
},
"prior_size": {
"default": 0.0,
"minimum": 0,
"title": "Prior Size",
"type": "number"
},
"effects_per_size_at_build": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size At Build",
"type": "object"
},
"effects_fixed_at_build": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed At Build",
"type": "object"
},
"effects_per_size_recurring": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size Recurring",
"type": "object"
},
"effects_fixed_recurring": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed Recurring",
"type": "object"
}
},
"required": [
"size_min",
"size_max"
],
"title": "Investment",
"type": "object"
},
"ProfileRef": {
"description": "Reference to a time-series stored outside the model definition.\n\nA serializable stand-in for an inline ``Variate`` array: the profile lives\nin a data file / dataset and is named here, so structural definitions\nround-trip to YAML/JSON without inlining 8760-point series. Resolve it to a\n:class:`xr.DataArray` with :meth:`resolve` before building the model.",
"properties": {
"dataset": {
"title": "Dataset",
"type": "string"
},
"variable": {
"title": "Variable",
"type": "string"
}
},
"required": [
"dataset",
"variable"
],
"title": "ProfileRef",
"type": "object"
},
"Sizing": {
"description": "Capacity optimization parameters.\n\nThe solver decides the optimal size within [size_min, size_max].\n\n- ``mandatory=True``: continuous, size in [min, max], no binary.\n- ``mandatory=False``: binary indicator gates size: 0 or [min, max].\n- ``size_min == size_max`` with ``mandatory=False``: binary invest\n at exact size (yes/no).\n\nSee: docs/math/sizing.md",
"properties": {
"size_min": {
"minimum": 0,
"title": "Size Min",
"type": "number"
},
"size_max": {
"minimum": 0,
"title": "Size Max",
"type": "number"
},
"mandatory": {
"default": true,
"title": "Mandatory",
"type": "boolean"
},
"effects_per_size": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Size",
"type": "object"
},
"effects_fixed": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Fixed",
"type": "object"
}
},
"required": [
"size_min",
"size_max"
],
"title": "Sizing",
"type": "object"
},
"Status": {
"description": "Binary on/off behavior parameters.\n\nTogether with relative bounds, gives semi-continuous behavior:\n``{0} U [min, max] * size``.\n\nSee: docs/math/status.md",
"properties": {
"uptime_min": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Uptime Min"
},
"uptime_max": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Uptime Max"
},
"downtime_min": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Downtime Min"
},
"downtime_max": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Downtime Max"
},
"effects_per_running_hour": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Running Hour",
"type": "object"
},
"effects_per_startup": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Startup",
"type": "object"
}
},
"title": "Status",
"type": "object"
},
"Variate": {
"anyOf": [
{
"type": "number"
},
{
"type": "integer"
},
{
"items": {
"type": "number"
},
"type": "array"
},
{
"$ref": "#/$defs/ProfileRef"
}
]
}
},
"description": "Energy storage with level dynamics.\n\nFlow ids are qualified as ``storage(flow)`` at build time. When both\nflows share a short_id, the qualified ids use ``charge`` /\n``discharge`` instead (the flows themselves are never modified)::\n\n Storage(\n id='bat', charging=Flow(carrier='elec'), discharging=Flow(carrier='elec')\n ) # bat(charge), bat(discharge)\n Storage(\n id='bat',\n charging=Flow(carrier='elec', short_id='in'),\n discharging=Flow(carrier='elec', short_id='out'),\n ) # bat(in), bat(out)\n\nLevel balance::\n\n E_{s,t+1} = E_{s,t} (1 - \u03b4)^\u0394t + P^c \u03b7^c \u0394t - P^d / \u03b7^d \u0394t\n\nSee: docs/math/storage.md",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"charging": {
"$ref": "#/$defs/Flow"
},
"discharging": {
"$ref": "#/$defs/Flow"
},
"capacity": {
"anyOf": [
{
"type": "number"
},
{
"$ref": "#/$defs/Sizing"
},
{
"$ref": "#/$defs/Investment"
},
{
"type": "null"
}
],
"default": null,
"title": "Capacity"
},
"eta_charge": {
"$ref": "#/$defs/Variate",
"default": 1.0
},
"eta_discharge": {
"$ref": "#/$defs/Variate",
"default": 1.0
},
"relative_loss_per_hour": {
"$ref": "#/$defs/Variate",
"default": 0.0
},
"prior_level": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Prior Level"
},
"cyclic": {
"default": true,
"title": "Cyclic",
"type": "boolean"
},
"relative_level_min": {
"$ref": "#/$defs/Variate",
"default": 0.0
},
"relative_level_max": {
"$ref": "#/$defs/Variate",
"default": 1.0
},
"final_level_min": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Final Level Min"
},
"final_level_max": {
"anyOf": [
{
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Final Level Max"
},
"prevent_simultaneous": {
"default": false,
"title": "Prevent Simultaneous",
"type": "boolean"
},
"status": {
"anyOf": [
{
"$ref": "#/$defs/Status"
},
{
"type": "null"
}
],
"default": null
}
},
"required": [
"id",
"charging",
"discharging"
],
"title": "Storage",
"type": "object"
}
Fields:
-
id(str) -
charging(Flow) -
discharging(Flow) -
capacity(float | Sizing | Investment | None) -
eta_charge(Variate) -
eta_discharge(Variate) -
relative_loss_per_hour(Variate) -
prior_level(float | None) -
cyclic(bool) -
relative_level_min(Variate) -
relative_level_max(Variate) -
final_level_min(float | None) -
final_level_max(float | None) -
prevent_simultaneous(bool) -
status(Status | None)
capacity
pydantic-field
¶
capacity: float | Sizing | Investment | None = None
Maximum stored energy [MWh], Sizing for investment
optimization, or None.
relative_loss_per_hour
pydantic-field
¶
relative_loss_per_hour: Variate = 0.0
Self-discharge rate [1/h].
prior_level
pydantic-field
¶
prior_level: float | None = None
Initial energy level [MWh]; None = unconstrained.
relative_level_min
pydantic-field
¶
relative_level_min: Variate = 0.0
Min SOC as fraction of capacity.
relative_level_max
pydantic-field
¶
relative_level_max: Variate = 1.0
Max SOC as fraction of capacity.
final_level_min
pydantic-field
¶
final_level_min: float | None = None
Lower bound on the level at the last timestep [MWh], per period; None = unconstrained.
final_level_max
pydantic-field
¶
final_level_max: float | None = None
Upper bound on the level at the last timestep [MWh], per period; None = unconstrained.
prevent_simultaneous
pydantic-field
¶
prevent_simultaneous: bool = False
If True, a binary per timestep excludes charging and discharging at once. Requires sized flows (fixed or Sizing/Investment) for the big-M bound.
status
pydantic-field
¶
status: Status | None = None
Component-level on/off behavior gating both charging and
discharging. Forbids flow-level status on the child flows
(the two switches would have no defined precedence).
model_post_init
¶
model_post_init(__context: Any) -> None
Validate physical ranges, carrier match, and status/size requirements.
Source code in src/fluxopt/elements.py
PiecewiseConversion
pydantic-model
¶
Bases: Element
Piecewise-linear conversion linking N flows.
Wraps :func:linopy.piecewise.add_piecewise_formulation. All flows
share interpolation weights — every operating point lies on the same
piece of the curve.
Two input forms:
-
Dict — equality-only, terse for the common case::
PiecewiseConversion(points={'fuel': [0, 50, 100], 'Heat': [0, 45, 70]})
-
List of tuples — supports per-flow inequality bounds::
PiecewiseConversion( points=[ ('fuel', [0, 50, 100]), ('Heat', [0, 45, 70], '>='), ] )
See: docs/math/converters.md
Show JSON schema:
{
"$defs": {
"PiecewiseMethod": {
"enum": [
"auto",
"sos2",
"incremental",
"lp"
],
"type": "string"
},
"ProfileRef": {
"description": "Reference to a time-series stored outside the model definition.\n\nA serializable stand-in for an inline ``Variate`` array: the profile lives\nin a data file / dataset and is named here, so structural definitions\nround-trip to YAML/JSON without inlining 8760-point series. Resolve it to a\n:class:`xr.DataArray` with :meth:`resolve` before building the model.",
"properties": {
"dataset": {
"title": "Dataset",
"type": "string"
},
"variable": {
"title": "Variable",
"type": "string"
}
},
"required": [
"dataset",
"variable"
],
"title": "ProfileRef",
"type": "object"
},
"Status": {
"description": "Binary on/off behavior parameters.\n\nTogether with relative bounds, gives semi-continuous behavior:\n``{0} U [min, max] * size``.\n\nSee: docs/math/status.md",
"properties": {
"uptime_min": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Uptime Min"
},
"uptime_max": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Uptime Max"
},
"downtime_min": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Downtime Min"
},
"downtime_max": {
"anyOf": [
{
"minimum": 0,
"type": "number"
},
{
"type": "null"
}
],
"default": null,
"title": "Downtime Max"
},
"effects_per_running_hour": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Running Hour",
"type": "object"
},
"effects_per_startup": {
"additionalProperties": {
"$ref": "#/$defs/Variate"
},
"title": "Effects Per Startup",
"type": "object"
}
},
"title": "Status",
"type": "object"
},
"Variate": {
"anyOf": [
{
"type": "number"
},
{
"type": "integer"
},
{
"items": {
"type": "number"
},
"type": "array"
},
{
"$ref": "#/$defs/ProfileRef"
}
]
}
},
"description": "Piecewise-linear conversion linking N flows.\n\nWraps :func:`linopy.piecewise.add_piecewise_formulation`. All flows\nshare interpolation weights \u2014 every operating point lies on the same\npiece of the curve.\n\nTwo input forms:\n\n- **Dict** \u2014 equality-only, terse for the common case::\n\n PiecewiseConversion(points={'fuel': [0, 50, 100], 'Heat': [0, 45, 70]})\n\n- **List of tuples** \u2014 supports per-flow inequality bounds::\n\n PiecewiseConversion(\n points=[\n ('fuel', [0, 50, 100]),\n ('Heat', [0, 45, 70], '>='),\n ]\n )\n\nSee: docs/math/converters.md",
"properties": {
"points": {
"anyOf": [
{
"additionalProperties": {
"items": {
"$ref": "#/$defs/Variate"
},
"type": "array"
},
"type": "object"
},
{
"items": {
"anyOf": [
{
"maxItems": 2,
"minItems": 2,
"prefixItems": [
{
"type": "string"
},
{
"items": {
"$ref": "#/$defs/Variate"
},
"type": "array"
}
],
"type": "array"
},
{
"maxItems": 3,
"minItems": 3,
"prefixItems": [
{
"type": "string"
},
{
"items": {
"$ref": "#/$defs/Variate"
},
"type": "array"
},
{
"enum": [
"==",
"<=",
">="
],
"type": "string"
}
],
"type": "array"
}
]
},
"type": "array"
}
],
"title": "Points"
},
"method": {
"$ref": "#/$defs/PiecewiseMethod",
"default": "auto"
},
"status": {
"anyOf": [
{
"$ref": "#/$defs/Status"
},
{
"type": "null"
}
],
"default": null
},
"availability": {
"$ref": "#/$defs/Variate",
"default": 1.0
}
},
"required": [
"points"
],
"title": "PiecewiseConversion",
"type": "object"
}
Fields:
-
points(dict[str, list[Variate]] | list[_CurveTuple]) -
method(PiecewiseMethod) -
status(Status | None) -
availability(Variate)
points
pydantic-field
¶
Per-flow breakpoints. Either {flow: [bp...]} (equality
only) or a list of (flow, [bp...]) / (flow, [bp...], '<='|'>=')
tuples. Need >=2 flows; all breakpoint lists must share the same
length (>=2). At most one tuple may carry a non-equality bound,
and only when exactly two flows are present.
method
pydantic-field
¶
Formulation. "auto" picks LP (2 flows + bounded +
matching convexity), else incremental (monotonic) or sos2.
Override with "sos2" / "incremental" / "lp".
status
pydantic-field
¶
status: Status | None = None
Component-level on/off behavior gating the curve.
availability
pydantic-field
¶
availability: Variate = 1.0
Time-varying scaling of the upper breakpoint.
model_post_init
¶
model_post_init(__context: Any) -> None
Validate normalized breakpoints and bound combinations.