Skip to content

Commit

Permalink
Docs Improvements August 2024 (#74)
Browse files Browse the repository at this point in the history
  • Loading branch information
ADGEfficiency authored Dec 22, 2024
1 parent 8a21d11 commit 85af22b
Show file tree
Hide file tree
Showing 9 changed files with 192 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
timeout-minutes: 30
strategy:
matrix:
python-version: [3.11.0, 3.12.0]
python-version: [3.11.9, 3.12.8]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
Expand Down
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ clean:

.PHONY: setup-pip-poetry setup-test setup-static setup-check setup-docs
QUIET := -q
PIP_CMD=pip

setup-pip-poetry:
pip install --upgrade pip $(QUIET)
pip install poetry==1.7.0 $(QUIET)
$(PIP_CMD) install --upgrade pip $(QUIET)
$(PIP_CMD) install poetry==1.7.0 $(QUIET)

setup: setup-pip-poetry
poetry install --with main $(QUIET)
Expand All @@ -34,7 +35,7 @@ setup-check: setup-pip-poetry
# TODO could change this now we use mike
# as we don't run a build on netlify anymore
setup-docs:
pip install -r ./docs/requirements.txt $(QUIET)
$(PIP_CMD) install -r ./docs/requirements.txt $(QUIET)


# ----- TEST -----
Expand Down
33 changes: 18 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ A Python library for optimizing energy assets with mixed-integer linear programm
- heat pumps,
- renewable (wind & solar) generators.

Assets & sites can be optimized to either maximize profit or minimize carbon emissions, or a user defined custom objective function.
Assets can be optimized to either maximize profit or minimize carbon emissions, or for user defined custom objective functions. Custom constraints can be used to further constrain asset behaviour.

A site is a collection of assets that can be optimized together. Sites can use custom objectives and constraints.

Energy balances are performed on electricity, high, and low temperature heat.

Expand All @@ -42,8 +44,11 @@ asset = epl.Battery(
power_mw=2,
capacity_mwh=4,
efficiency_pct=0.9,
electricity_prices=[100.0, 50, 200, -100, 0, 200, 100, -100],
export_electricity_prices=40
# different electricity prices for each interval
# length of electricity_prices is the length of the simulation
electricity_prices=[100.0, 50, 200, -100, 0, 200, 100, -100],
# a constant value for each interval
export_electricity_prices=40,
)

simulation = asset.optimize()
Expand All @@ -58,15 +63,10 @@ import energypylinear as epl

assets = [
# 2.0 MW, 4.0 MWh battery
epl.Battery(
power_mw=2.0,
capacity_mwh=4.0
),
epl.Battery(power_mw=2.0, capacity_mwh=4.0),
# 30 MW open cycle generator
epl.CHP(
electric_power_max_mw=100,
electric_power_min_mw=30,
electric_efficiency_pct=0.4
electric_power_max_mw=100, electric_power_min_mw=30, electric_efficiency_pct=0.4
),
# 2 EV chargers & 4 charge events
epl.EVs(
Expand All @@ -82,14 +82,17 @@ assets = [
# natural gas boiler to generate high temperature heat
epl.Boiler(),
# valve to generate low temperature heat from high temperature heat
epl.Valve()
epl.Valve(),
]

site = epl.Site(
assets=assets,
electricity_prices=[100, 50, 200, -100, 0],
high_temperature_load_mwh=[105, 110, 120, 110, 105],
low_temperature_load_mwh=[105, 110, 120, 110, 105]
assets=assets,
# length of energy prices is the length of the simulation
electricity_prices=[100, 50, 200, -100, 0],
# these should match the length of the export_electricity_prices
# if they don't, they will be repeated or cut to match the length of electricity_prices
high_temperature_load_mwh=[105, 110, 120, 110, 105],
low_temperature_load_mwh=[105, 110, 120, 110, 105],
)

simulation = site.optimize()
Expand Down
8 changes: 5 additions & 3 deletions docs/docs/assets/evs.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
The `epl.EVs` asset is suitable for modelling electric vehicle charging, with multiple chargers and charge events.
The `epl.EVs` asset is suitable for modelling electric vehicle charging. One asset can operate many electric vehicle chargers that supply electricity to many electric vehicle charge events.

The electric vehicle asset will optimize the dispatch of the chargers to supply electricity to the charge events. The asset can do both grid to vehicle and vehicle to grid electricity flow with a `allows_evs_discharge` flag.

## Assumptions

Chargers are configured by their size given in `charger_mws`.
Electric vehicle chargers are configured by the charger power output, given as `charger_mws`.

A `charge_event` is a time interval where an EV can be charged. This is given as a boolean 2D array, with one binary digit for each charge event, interval pairs.
A `charge_event` is a time interval where an electric vehicle can be charged. This is given as a boolean 2D array, with one binary digit for each charge event, interval pairs.

Each charge event has a required amount of electricity `charge_event_mwh`, that can be delivered when the `charge_event` is 1. The model is constrained so that each charge event receives all of it's `charge_event_mwh`.

Expand Down
34 changes: 34 additions & 0 deletions docs/docs/how-to/price-carbon.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,37 @@ print(f"{-variance.cost / variance.emissions:.2f} $/tC")
variance=<Account profit=903.33 emissions=0.6156>
1467.51 $/tC
```

## Full Example

```python
import energypylinear as epl

electricity_prices = [100, 50, 200, -100, 0, 200, 100, -100]
electricity_carbon_intensities = [0.1, 0.2, 0.1, 0.15, 0.01, 0.7, 0.5, 0.01]
asset = epl.Battery(
power_mw=2,
capacity_mwh=4,
efficiency_pct=0.9,
electricity_prices=electricity_prices,
electricity_carbon_intensities=electricity_carbon_intensities,
)

# optimize for carbon
carbon = asset.optimize(objective="carbon", verbose=3)
carbon_account = epl.get_accounts(carbon.results, verbose=3)
print(f"{carbon_account=}")

# optimize for money
price = asset.optimize(
objective="price",
verbose=3
)
price_account = epl.get_accounts(price.results, verbose=3)
print(f"{price_account=}")

# calculate variance (difference) between accounts
variance = price_account - carbon_account
print(f"{variance=}")
print(f"{-variance.cost / variance.emissions:.2f} $/tC")
```
2 changes: 1 addition & 1 deletion docs/docs/index.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# energy-py-linear

{!../../README.md!lines=11-97}
{!../../README.md!lines=11-99}
2 changes: 2 additions & 0 deletions docs/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,9 @@ nav:
- Multiple Assets: how-to/dispatch-site.md
- Carbon: how-to/price-carbon.md
- Forecast: how-to/dispatch-forecast.md
- Battery Cycles: how-to/battery-cycles.md
- Network Charges: how-to/network-charges.md
>>>>>>> 8a21d117db6b142efd9f1d9f0dd4e87d5a7dae35
- Customization:
- Constraints: how-to/custom-constraints.md
- Objective Functions: how-to/custom-objectives.md
Expand Down
61 changes: 58 additions & 3 deletions energypylinear/constraints.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,22 @@ class ConstraintTerm:
Examples:
```python
# a constraint term for site import power electricity cost
# site import power electricity cost
ConstraintTerm(
variable="import_power_mwh",
asset_type="site",
interval_data="electricity_prices"
)
# a constraint term for site export power electricity revenue
# site export power electricity revenue
ConstraintTerm(
variable="import_power_mwh",
asset_type="site",
interval_data="electricity_prices",
coefficient=-1
)
# a constraint term for battery cycle cost
# battery cycle cost
ConstraintTerm(
variable="electric_charge_mwh",
asset_type="battery",
Expand Down Expand Up @@ -77,6 +77,61 @@ class Constraint(pydantic.BaseModel):
interval_aggregation: How to aggregate terms across intervals.
None will result in one constraint per interval.
"sum" will result in one constraint per simulation.
Example of a custom constraint with the `energypylinear` Pydantic models to represent:
- the constraint,
- the constraint terms on the left and right sides,
- the sense of the constraint,
- whether to aggregate across the interval (time) dimension.
```python
import energypylinear as epl
epl.Battery(
power_mw=1,
capacity_mwh=2,
efficiency_pct=0.98,
electricity_prices=np.random.normal(0.0, 1000, 48 * 7),
constraints=[
epl.Constraint(
lhs=[
epl.ConstraintTerm(
asset_type="battery", variable="electric_charge_mwh"
),
epl.ConstraintTerm(
asset_type="battery", variable="electric_discharge_mwh"
),
],
rhs=cycle_limit_mwh,
sense="le",
interval_aggregation="sum",
)
],
)
```
Constraints can also be set with dictionaries:
```python
import energypylinear as epl
epl.Battery(
power_mw=1,
capacity_mwh=2,
efficiency_pct=0.98,
electricity_prices=np.random.normal(0.0, 1000, 48 * 7),
constraints=[
{
"lhs": [
{"asset_type": "battery", "variable": "electric_charge_mwh"},
{"asset_type": "battery", "variable": "electric_discharge_mwh"},
],
"rhs": cycle_limit_mwh,
"sense": "le",
"interval_aggregation": "sum",
},
],
)
"""

lhs: float | ConstraintTerm | dict | list[float | ConstraintTerm | dict]
Expand Down
70 changes: 69 additions & 1 deletion energypylinear/objectives.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,75 @@ class FunctionTermManyVariables:

@dataclasses.dataclass
class CustomObjectiveFunction:
"""A custom objective function - a sum of `OneTerm` objects."""
"""A custom objective function - a sum of `OneTerm` objects.
Example of a custom objective function with the `energypylinear` Pydantic models to represent:
- site import power cost,
- site export power cost.
```python
import energypylinear as epl
asset = epl.Battery(
power_mw=1,
capacity_mwh=2,
efficiency_pct=0.98,
electricity_prices=np.random.normal(0.0, 1000, 48 * 7)
)
asset.optimize(
objective={"terms": [
{
"asset_type": "site",
"variable": "import_power_mwh",
"interval_data": "electricity_prices",
},
{
"asset_type": "site",
"variable": "export_power_mwh",
"interval_data": "electricity_prices",
"coefficient": -1,
}
]}
)
```
Objectives can also be set with dictionaries:
```python
import energypylinear as epl
epl.Battery(
power_mw=1,
capacity_mwh=2,
efficiency_pct=0.98,
electricity_prices=np.random.normal(0.0, 1000, 48 * 7),
)
site.optimize(
objective=epl.CustomObjectiveFunction(
terms=[
epl.Term(
asset_type="site",
variable="import_power_mwh",
interval_data="electricity_prices",
),
epl.Term(
asset_type="site",
variable="export_power_mwh",
interval_data="electricity_prices",
coefficient=-1,
),
epl.Term(
asset_name="solar",
variable="electric_generation_mwh",
coefficient=-25,
),
]
)
)
```
"""

terms: list[OneTerm]

Expand Down

0 comments on commit 85af22b

Please sign in to comment.