Skip to main content

MILP library for modeling energy supply systems of industrial production sites.

Project description

ETA Components

pipeline license

This is a component library for the mathematical modeling of industrial production sites. Currently it provides models for energy converters, such as CHPs, absorption chillers and energy storages, networks to connect these components to and basic models to mock energy sources and sinks.

The energy converter models are largely based on Baumgärtner (2020): "Optimization of low-carbon energy systems from industrial to national scale" p. 128 ff.

Modeling backend (Linopy-only)

eta-components constructs linear / mixed-integer linear models using Linopy + xarray (vectorized, labelled-array model construction). The modeling backend is Linopy-only (no Pyomo fallback).

Components

  • A large variety of energy converters:
    • AbsorptionChiller,
    • GasBoiler and ElectrodeBoiler,
    • Chp,
    • AirWaterCompressionChiller and WaterWaterCompressionChiller,
    • DryCooler,
    • ParallelHX and CounterCurrentHX,
    • AirWaterHeatPump and WaterWaterHeatPump and
    • HeatStorage and ColdStorage.
  • Networks to enforce energy balance across all connected components:
    • Gas, Hydrogen
    • Electrical (AC/DC variants)
    • HeatingFluid, CoolingFluid
  • Environments to provide access to shared parameters:
    • AmbientEnvironment.
  • Traders to buy and sell energy across the system's boundaries:
    • SimpleBuyer and SimpleSeller.
  • Mock models for fixed energy sources and sinks, such as waste heat sources or cooling demands:
    • SimpleHeatingSource / SimpleHeatingSink
    • SimpleCoolingSource / SimpleCoolingSink
    • SimpleElectricitySource / SimpleElectricitySink
  • System orchestration:
    • BasicSystem (Linopy-only)

Features

  • On/off operating decisions for all converters.
  • Part-load efficiency for all converters.
  • Buying decisions for all converters.
  • Objective construction via an objective-term registry (e.g. StaticCost, Emissions).
  • Plotting of each network's energy production and consumption.
  • Indices not only for time steps but also for typical periods and stochastic samples.

Overview

flowchart LR
  userCode[UserCode]
  basicSystem[BasicSystem]
  components[Units_Networks_Objectives]
  backendProtocol[BackendProtocol]
  linopyBackend[LinopyBackend]
  linopyModel[linopy.Model]
  results[ResultsBundle]

  userCode --> basicSystem
  userCode --> components
  components --> basicSystem
  basicSystem --> backendProtocol
  backendProtocol --> linopyBackend
  linopyBackend --> linopyModel
  linopyModel --> results
  basicSystem --> results

See docs/architecture_overview.md for module-level details.

What's not implemented yet

  • Investment and maintenance cost for all converters.
  • Continuous sizing of all converters.
  • Sellers and buyers for cooling energy.
    • At the moment these must be implemented with buyers or sellers, respectively (because of negative heat flow).
  • Variable temperatures for the networks à la Thermal Dependencies paper.
  • Framework for bilevel problems.
  • Starting cost, ramp limits and maximum starts per time for relevant equipments.
  • Measures for increasing efficiency.
  • Material data models for production processes.

Requirements

  • Python 3.11 or higher

See Solver and third-party requirements for MILP solver setup.

Installation

To install the project along with its development dependencies, execute the following command:

poetry install

Optionally install the git hooks:

poetry run pre-commit install

Run tests with:

poetry run pytest

Usage

The entire system is modeled as energy flows, thus no volume flows are explicitly implemented. Each converter, trader or energy source or sink must be connected to its corresponding network(s). The networks then enforce an energy balance across all connected components so that energy production and consumption match for each time step. The energy flows are implemented in a thermodynamic contex with each network as the system's boundary. Thus, inlet energy is positive and outlet energy is negative.

E.g. a gas boiler has a negative gas consumption (because it flows out of the gas network) and a positive heat production (because it flows into the heating network). The same goes for electrical energy. Attention is required when using cooling producers or consumers, as cooling producers move heat out of the cooling network (negative production) and cooling consumers move heat into the network (positive consumption).

Quick start

import eta_components.milp_component_library as mcl
from eta_components.milp_component_library.units.base_unit import BaseStandaloneUnit


class DispatchGenerator(BaseStandaloneUnit):
    """Toy generator with linear marginal cost."""

    def __init__(
        self,
        name: str,
        data: dict,
        system: mcl.systems.BasicSystem,
        *,
        electrical_network: mcl.networks.Electrical,
    ):
        self._net = electrical_network
        super().__init__(name, data, system)

    def build(self) -> None:
        idx = self.system.index
        p = self.system.add_variable(
            f"{self.name}__p",
            dims=("year", "period", "time"),
            coords={d: idx.coords[d].values for d in ("year", "period", "time")},
            lower=0,
        )
        self.vars["p"] = p
        self._net.register_unit(self, p, mcl.networks.PowerSign.POSITIVE)
        self.system.add_objective_term(
            f"{self.name}__opex",
            p * float(self.data.get("marginal_cost", 1.0)),
            kind="opex",
        )

    def unregister(self) -> None:
        self._net.unregister_unit(self)
        self.system.unregister_unit(self)

    @property
    def time_step_cost(self):
        return 0

    @property
    def annual_cost(self):
        return 0

    @property
    def onetime_cost(self):
        return 0

    @property
    def emissions(self):
        return 0


sys = mcl.systems.BasicSystem(n_years=1, n_periods=2, n_time_steps=3, step_length=5 * 60)
el = mcl.networks.Electrical("electrical", {}, sys)

# Fixed demand profile: consumption is negative by convention.
demand = {
    (1, 1, 1): -5,
    (1, 1, 2): -4,
    (1, 1, 3): -6,
    (1, 2, 1): -3,
    (1, 2, 2): -2,
    (1, 2, 3): -5,
}
_ = mcl.sources_sinks.SimpleElectricitySink("demand", {"P_in": demand}, sys, electrical_network=el)
_ = DispatchGenerator("gen", {"marginal_cost": 2.0}, sys, electrical_network=el)

sys.set_objective(mcl.objectives.StaticCost("cost", {"sense": "minimize", "weights": 1}, sys))
results = sys.solve(solver="highs", options={"time_limit_s": 30})
print(results.solve_info)
print(results.vars)

Key concepts in this Linopy-first workflow:

  • Indexing: system.index defines canonical coordinates for ("year", "period", "time"). Parameters and variables are represented as xarray.DataArray-like objects aligned to these dims. See docs/indexing_and_parameters.md.
  • Extension points: build your own variables/constraints using system.add_variable(...) and system.add_constraints(...). For objectives, register terms with system.add_objective_term(...) and use an objective like StaticCost.
  • Networks: networks enforce a vectorized power balance (sum of registered power expressions equals zero).

For a longer quickstart, see docs/quickstart_linopy.md. For solver configuration, see docs/solver_options.md.

Example

There are examples in the folder examples/.

Documentation

Hosted documentation (GitLab Pages): eta-components documentation

Sphinx documentation is in docs/. Build locally:

poetry install --with docs
cd docs && make html

Development

See CONTRIBUTING.md for setup, tests, and merge request workflow.

Run tests with:

poetry run pytest

Before submitting a merge request:

poetry run pre-commit run --all-files

Adding dependencies

Adding dependencies to the project can be done via

poetry add <package-name>@latest

Related software

  • eta-incerto — primary consumer for investment and uncertainty optimization workflows

Solver and third-party requirements

Models are built with Linopy and require a separate MILP/LP solver:

  • HiGHS (highspy) — recommended open-source default for examples and development
  • Gurobi (gurobipy) — supported where Linopy solver interface allows; commercial license required
  • CPLEX — optional alternative where Linopy solver interface allows

This package is licensed under BSD-2-Clause. Solver products are third-party software with their own licenses and terms.

Citation

For academic use, cite this repository using CITATION.cff. See AUTHORS.rst for further contributors.

License

BSD-2-Clause — see LICENSE. See also CHANGELOG.md and SECURITY.md.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

eta_components-1.2.2.tar.gz (47.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

eta_components-1.2.2-py3-none-any.whl (71.5 kB view details)

Uploaded Python 3

File details

Details for the file eta_components-1.2.2.tar.gz.

File metadata

  • Download URL: eta_components-1.2.2.tar.gz
  • Upload date:
  • Size: 47.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.13.2 Windows/11

File hashes

Hashes for eta_components-1.2.2.tar.gz
Algorithm Hash digest
SHA256 6979206389bc9e2ae2012e6aa7fba3861909c424a7f56139a561f9720430482e
MD5 cbda37d9f72518559b644bf839dd312e
BLAKE2b-256 eb72adc675f4d7deb95a79dab0e585428538dd91c9eb8b33bf205af641b64081

See more details on using hashes here.

File details

Details for the file eta_components-1.2.2-py3-none-any.whl.

File metadata

  • Download URL: eta_components-1.2.2-py3-none-any.whl
  • Upload date:
  • Size: 71.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.1.3 CPython/3.13.2 Windows/11

File hashes

Hashes for eta_components-1.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 40982a9e5653d6da13d59a28e9ce4bd477fef52a00751e64afb2ff11dc24a086
MD5 cab1bdae9e4541c3932e18b4eb4a2b0f
BLAKE2b-256 03c017948cbc4b4879b7b79c97a77127523dc01469ede98911ce59afc95a51d3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page