Skip to content

fluxopt.flow_system

FlowSystem — the declarative top of the element layer.

A FlowSystem is an inert, validated description of a flow system: the same lists you would pass to :func:fluxopt.optimize, gathered into one object that round-trips to dict/YAML. It carries structure (components, effects, config) and ProfileRef references to time-series; the actual series are supplied at solve time via profiles (system.optimize(profiles=...)) and resolved into arrays just before the model is built.

The FlowSystem has no modeling behavior of its own — .optimize() runs the existing pipeline (:meth:ModelData.build → the math program). Declaration (the system) and use (building/solving) stay separate.

Classes:

Name Description
FlowSystem

A declarative flow-system description (see module docstring).

FlowSystem pydantic-model

Bases: BaseModel

A declarative flow-system description (see module docstring).

Show JSON schema:
{
  "$defs": {
    "Carrier": {
      "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"
    },
    "Converter": {
      "description": "Conversion between input and output flows.\n\nTwo mutually exclusive modes:\n\n- **Linear** \u2014 ``conversion_factors=[{flow_short_id: a_f}, ...]``,\n  one dict per equation; constraint ``sum_f(a_f * P_{f,t}) = 0``.\n- **Piecewise** \u2014 ``conversion=PiecewiseConversion(...)``; the solver\n  interpolates between breakpoints, optionally with on/off via\n  ``PiecewiseConversion.status``.",
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "inputs": {
          "items": {
            "$ref": "#/$defs/Flow"
          },
          "title": "Inputs",
          "type": "array"
        },
        "outputs": {
          "items": {
            "$ref": "#/$defs/Flow"
          },
          "title": "Outputs",
          "type": "array"
        },
        "conversion_factors": {
          "items": {
            "additionalProperties": {
              "$ref": "#/$defs/Variate"
            },
            "type": "object"
          },
          "title": "Conversion Factors",
          "type": "array"
        },
        "conversion": {
          "anyOf": [
            {
              "$ref": "#/$defs/PiecewiseConversion"
            },
            {
              "type": "null"
            }
          ],
          "default": null
        }
      },
      "required": [
        "id",
        "inputs",
        "outputs"
      ],
      "title": "Converter",
      "type": "object"
    },
    "Effect": {
      "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"
    },
    "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"
    },
    "PiecewiseConversion": {
      "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"
    },
    "PiecewiseMethod": {
      "enum": [
        "auto",
        "sos2",
        "incremental",
        "lp"
      ],
      "type": "string"
    },
    "Port": {
      "description": "System boundary importing and exporting carriers.\n\n``imports`` bring flow into the system (sources); ``exports`` send it out\nof the system (sinks).",
      "properties": {
        "id": {
          "title": "Id",
          "type": "string"
        },
        "imports": {
          "items": {
            "$ref": "#/$defs/Flow"
          },
          "title": "Imports",
          "type": "array"
        },
        "exports": {
          "items": {
            "$ref": "#/$defs/Flow"
          },
          "title": "Exports",
          "type": "array"
        }
      },
      "required": [
        "id"
      ],
      "title": "Port",
      "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"
    },
    "Storage": {
      "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"
    },
    "Timesteps": {
      "anyOf": [
        {
          "items": {
            "format": "date-time",
            "type": "string"
          },
          "type": "array"
        },
        {
          "items": {
            "type": "integer"
          },
          "type": "array"
        }
      ]
    },
    "Variate": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "integer"
        },
        {
          "items": {
            "type": "number"
          },
          "type": "array"
        },
        {
          "$ref": "#/$defs/ProfileRef"
        }
      ]
    }
  },
  "description": "A declarative flow-system description (see module docstring).",
  "properties": {
    "timesteps": {
      "$ref": "#/$defs/Timesteps"
    },
    "carriers": {
      "items": {
        "$ref": "#/$defs/Carrier"
      },
      "title": "Carriers",
      "type": "array"
    },
    "effects": {
      "items": {
        "$ref": "#/$defs/Effect"
      },
      "title": "Effects",
      "type": "array"
    },
    "ports": {
      "items": {
        "$ref": "#/$defs/Port"
      },
      "title": "Ports",
      "type": "array"
    },
    "objective": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "additionalProperties": {
            "type": "number"
          },
          "type": "object"
        }
      ],
      "title": "Objective"
    },
    "converters": {
      "items": {
        "$ref": "#/$defs/Converter"
      },
      "title": "Converters",
      "type": "array"
    },
    "storages": {
      "items": {
        "$ref": "#/$defs/Storage"
      },
      "title": "Storages",
      "type": "array"
    },
    "dt": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "items": {
            "type": "number"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Dt"
    },
    "periods": {
      "anyOf": [
        {
          "items": {
            "type": "integer"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Periods"
    },
    "period_weights": {
      "anyOf": [
        {
          "items": {
            "type": "number"
          },
          "type": "array"
        },
        {
          "type": "null"
        }
      ],
      "default": null,
      "title": "Period Weights"
    }
  },
  "required": [
    "timesteps",
    "carriers",
    "effects",
    "ports",
    "objective"
  ],
  "title": "FlowSystem",
  "type": "object"
}

Config:

  • default: _PYDANTIC_CFG

Fields:

Validators:

  • _validate_references

timesteps pydantic-field

timesteps: Timesteps

Time index for the optimization horizon.

carriers pydantic-field

carriers: list[Carrier]

Carrier declarations.

effects pydantic-field

effects: list[Effect]

Effects to track (costs, emissions, …).

ports pydantic-field

ports: list[Port]

System boundary ports with imports/exports.

objective pydantic-field

objective: str | dict[str, float]

Effect(s) to minimize — a name or {effect: weight}. Must name at least one non-penalty effect.

converters pydantic-field

converters: list[Converter]

Linear/piecewise converters between carriers.

storages pydantic-field

storages: list[Storage]

Energy storages.

dt pydantic-field

dt: float | list[float] | None = None

Timestep duration in hours. Auto-derived if None.

periods pydantic-field

periods: list[int] | None = None

Integer period labels for multi-period optimization.

period_weights pydantic-field

period_weights: list[float] | None = None

Explicit weights per period. Inferred from gaps if None.

from_dict classmethod

from_dict(data: Mapping[str, Any]) -> FlowSystem

Build a system from a mapping (e.g. parsed YAML/JSON).

Source code in src/fluxopt/flow_system.py
@classmethod
def from_dict(cls, data: Mapping[str, Any]) -> FlowSystem:
    """Build a system from a mapping (e.g. parsed YAML/JSON)."""
    return from_dict(cls, data)

from_yaml classmethod

from_yaml(path: str | Path) -> FlowSystem

Load a system from a YAML file.

Parameters:

Name Type Description Default

path

str | Path

Path to a YAML document describing the system.

required
Source code in src/fluxopt/flow_system.py
@classmethod
def from_yaml(cls, path: str | Path) -> FlowSystem:
    """Load a system from a YAML file.

    Args:
        path: Path to a YAML document describing the system.
    """
    import yaml

    with open(path) as fh:
        return cls.from_dict(yaml.safe_load(fh))

to_dict

to_dict() -> dict[str, Any]

Serialize the system to a JSON-safe dict (profiles as ProfileRef).

Source code in src/fluxopt/flow_system.py
def to_dict(self) -> dict[str, Any]:
    """Serialize the system to a JSON-safe dict (profiles as ``ProfileRef``)."""
    return to_dict(self)

to_yaml

to_yaml(path: str | Path) -> None

Write the system to a YAML file.

Parameters:

Name Type Description Default

path

str | Path

Destination path.

required
Source code in src/fluxopt/flow_system.py
def to_yaml(self, path: str | Path) -> None:
    """Write the system to a YAML file.

    Args:
        path: Destination path.
    """
    import yaml

    with open(path, 'w') as fh:
        yaml.safe_dump(self.to_dict(), fh, sort_keys=False)

required_profiles

required_profiles() -> dict[str, set[str]]

Enumerate the external data this system needs, as {dataset: variables}.

The contract a profiles supply must cover before :meth:build_model / :meth:optimize can run. Empty when every value is inline.

Source code in src/fluxopt/flow_system.py
def required_profiles(self) -> dict[str, set[str]]:
    """Enumerate the external data this system needs, as ``{dataset: variables}``.

    The contract a ``profiles`` supply must cover before
    :meth:`build_model` / :meth:`optimize` can run. Empty when every value
    is inline.
    """
    refs: list[tuple[str, ProfileRef]] = []
    for group in (self.carriers, self.effects, self.ports, self.converters, self.storages):
        _collect_profile_refs(group, '', refs)
    out: dict[str, set[str]] = {}
    for _, ref in refs:
        out.setdefault(ref.dataset, set()).add(ref.variable)
    return out

math

math() -> Any

The equations this system will be solved as, as data.

An :class:lpspec.Model — the whole program, before any of this system's numbers are bound to it. Read it (to_yaml()), typeset it (mathspec.to_latex(...)), or edit it and hand it back to :meth:optimize as math=: adding a named quantity to expressions: or a row to constraints: is how a caller extends the math now, in the same language and with the same load-time checks the shipped program gets.

Source code in src/fluxopt/flow_system.py
def math(self) -> Any:
    """The equations this system will be solved as, as data.

    An :class:`lpspec.Model` — the whole program, before any of this
    system's numbers are bound to it. Read it (``to_yaml()``), typeset it
    (``mathspec.to_latex(...)``), or edit it and hand it back to
    :meth:`optimize` as ``math=``: adding a named quantity to
    ``expressions:`` or a row to ``constraints:`` is how a caller extends
    the math now, in the same language and with the same load-time checks
    the shipped program gets.
    """
    from fluxopt.math import program

    return program()

parameters

parameters(profiles: Mapping[str, Any] | None = None) -> Parameters

The numbers this system binds to its math, as data.

The other half of :meth:math. That returns the equations before any of this system's numbers reach them; this returns the numbers, keyed as the program declares them — so (math, parameters/) is the whole problem, hashable and diffable, and a caller adding a constraint through math= can see what they are adding to.

Derived, never authored: the fold and the pre-scaling have already turned what the user declared into what the solver adds up, so the set is worth keeping and not worth editing. Change the elements or change the program.

Parameters:

Name Type Description Default

profiles

Mapping[str, Any] | None

Mapping from ProfileRef.dataset to a dataset (or mapping) holding the referenced variables, as :meth:build_data takes.

None
Source code in src/fluxopt/flow_system.py
def parameters(self, profiles: Mapping[str, Any] | None = None) -> Parameters:
    """The numbers this system binds to its math, as data.

    The other half of :meth:`math`. That returns the equations before any
    of this system's numbers reach them; this returns the numbers, keyed
    as the program declares them — so `(math, parameters/)` is the
    whole problem, hashable and diffable, and a caller adding a constraint
    through ``math=`` can see what they are adding to.

    Derived, never authored: the fold and the pre-scaling have already
    turned what the user declared into what the solver adds up, so the
    set is worth keeping and not worth editing. Change the elements or
    change the program.

    Args:
        profiles: Mapping from ``ProfileRef.dataset`` to a dataset (or
            mapping) holding the referenced variables, as
            :meth:`build_data` takes.
    """
    return Parameters.of(self.build_data(profiles), self.objective)

build_data

build_data(profiles: Mapping[str, Any] | None = None) -> ModelData

Materialize this declaration's data, resolving profile references.

Resolves ProfileRef references (on a copy — the system stays reusable across different profiles) and builds the ModelData both lanes read.

Parameters:

Name Type Description Default

profiles

Mapping[str, Any] | None

Mapping from ProfileRef.dataset to a dataset (or mapping) holding the referenced variables. Required if the system uses any ProfileRef — see :meth:required_profiles.

None

Raises:

Type Description
KeyError

If any ProfileRef cannot be resolved from profiles; lists every unresolvable ref with its element/field path.

Source code in src/fluxopt/flow_system.py
def build_data(self, profiles: Mapping[str, Any] | None = None) -> ModelData:
    """Materialize this declaration's data, resolving profile references.

    Resolves ``ProfileRef`` references (on a copy — the system stays
    reusable across different ``profiles``) and builds the ``ModelData``
    both lanes read.

    Args:
        profiles: Mapping from ``ProfileRef.dataset`` to a dataset (or mapping)
            holding the referenced variables. Required if the system uses
            any ``ProfileRef`` — see :meth:`required_profiles`.

    Raises:
        KeyError: If any ``ProfileRef`` cannot be resolved from *profiles*;
            lists every unresolvable ref with its element/field path.
    """
    refs: list[tuple[str, ProfileRef]] = []
    for group in (self.carriers, self.effects, self.ports, self.converters, self.storages):
        _collect_profile_refs(group, '', refs)
    _check_profiles_cover(refs, profiles or {})

    carriers, effects, ports, converters, storages = (
        # No refs → nothing to substitute, so no copy or walk needed.
        (self.carriers, self.effects, self.ports, self.converters, self.storages)
        if not refs
        else copy.deepcopy((self.carriers, self.effects, self.ports, self.converters, self.storages))
    )
    if refs:
        for group in (carriers, effects, ports, converters, storages):
            _resolve_refs(group, profiles or {})

    data = ModelData.build(
        self.timesteps,
        carriers,
        effects,
        ports,
        converters,
        storages,
        self.dt,
        periods=self.periods,
        period_weights=self.period_weights,
    )
    return data

optimize

optimize(
    profiles: Mapping[str, Any] | None = None,
    *,
    solver: str = 'highs',
    math: Any = None,
    dimensions: Mapping[str, Any] | None = None,
    lookups: Mapping[str, Any] | None = None,
    parameters: Mapping[str, Any] | None = None,
    **kwargs: Any,
) -> Result

Resolve profile references, build the data, and solve.

Parameters:

Name Type Description Default

profiles

Mapping[str, Any] | None

Mapping from ProfileRef.dataset to a dataset (or mapping) holding the referenced variables. Required if the system uses any ProfileRef.

None

solver

str

Solver name — highs, or gurobi with lpspec's extra.

'highs'

math

Any

An edited :meth:math to solve instead of the shipped program. Whatever it declares is checked and lowered exactly as the shipped one is.

None

dimensions

Mapping[str, Any] | None

Labels for dimensions math adds.

None

lookups

Mapping[str, Any] | None

Data for lookups math adds, as {name: frame}.

None

parameters

Mapping[str, Any] | None

Data for parameters math adds.

The three are the language's own declaration blocks, and :meth:parameters returns them under the same three names. All are ignored by the shipped program, which declares nothing a caller has to fill.

None

**kwargs

Any

Passed to the solver verbatim, in its own vocabulary.

{}
Source code in src/fluxopt/flow_system.py
def optimize(
    self,
    profiles: Mapping[str, Any] | None = None,
    *,
    solver: str = 'highs',
    math: Any = None,
    dimensions: Mapping[str, Any] | None = None,
    lookups: Mapping[str, Any] | None = None,
    parameters: Mapping[str, Any] | None = None,
    **kwargs: Any,
) -> Result:
    """Resolve profile references, build the data, and solve.

    Args:
        profiles: Mapping from ``ProfileRef.dataset`` to a dataset (or mapping)
            holding the referenced variables. Required if the system uses
            any ``ProfileRef``.
        solver: Solver name — ``highs``, or ``gurobi`` with lpspec's extra.
        math: An edited :meth:`math` to solve instead of the shipped
            program. Whatever it declares is checked and lowered exactly
            as the shipped one is.
        dimensions: Labels for dimensions *math* adds.
        lookups: Data for lookups *math* adds, as ``{name: frame}``.
        parameters: Data for parameters *math* adds.

            The three are the language's own declaration blocks, and
            :meth:`parameters` returns them under the same three names.
            All are ignored by the shipped program, which declares
            nothing a caller has to fill.
        **kwargs: Passed to the solver verbatim, in its own vocabulary.
    """
    from fluxopt.math import solve

    return solve(
        self.build_data(profiles),
        self.objective,
        solver_name=solver,
        solver_options=kwargs or None,
        math=math,
        dimensions=dimensions,
        lookups=lookups,
        parameters=parameters,
    )