Your first model¶
Every odys model is built from two objects. An AssetPortfolio holds what you own and operate. An EnergySystem takes that portfolio and adds the rest: the markets you trade on, the futures you plan for, the horizon and where each asset starts. Calling .optimize() on it returns the plan.
Basic usage¶
Let's build a first system, a small industrial site: a 100 MW CCGT serving a factory over four hours.
from datetime import timedelta
from odys import AssetPortfolio, EnergySystem, FixedLoad, Generator, LoadProfile, Scenario
ccgt = Generator(name="ccgt", nominal_power=100.0, variable_cost=50.0)
factory = FixedLoad(name="factory")
energy_system = EnergySystem(
portfolio=AssetPortfolio([ccgt, factory]),
scenarios=Scenario(profiles=(LoadProfile(load=factory, values=[60, 90, 40, 70]),)),
timestep=timedelta(hours=1),
number_of_steps=4,
)
result = energy_system.optimize()
print(result.generators.power)
time generator
0 ccgt 60.0
1 ccgt 90.0
2 ccgt 40.0
3 ccgt 70.0
Name: power, dtype: float64
The CCGT is the only supply, so it follows the factory's demand exactly. The optimizer maximizes profit, and here the profit is only the fuel cost: 260 MWh at 50 $/MWh is 13,000, so the objective is negative:
print(result.objective_value)
-13000.0
The rest of the User Guide adds to this site one piece at a time.
Units¶
odys doesn't convert units. Powers are in MW and held for the whole step, energies are in MWh, and prices and variable costs are per MWh, in whatever currency your prices are in. For a small asset, convert to MW yourself: a 22 kW charger is 0.022.
A few fields depend on the step length, such as ramp limits and self-discharge. Time and units lists how each one reads it.
The portfolio¶
An AssetPortfolio takes the assets you own and operate: generators, fixed and flexible loads, battery storage, electric vehicles and chargers. Building it checks two rules, and raises an OdysValidationError when one fails:
- Asset names are unique within the portfolio.
- Chargers and electric vehicles come together: a portfolio with vehicles but no charger, or chargers but no vehicle, is rejected.
Markets aren't assets, so they don't go in the portfolio: pass them to the EnergySystem with markets=... (see EnergyMarket).
When you build portfolios in a script, you can read the assets back by name or by type:
portfolio = AssetPortfolio([ccgt, factory])
portfolio.get_asset("ccgt") # the Generator named "ccgt"
portfolio.assets_of(Generator) # (ccgt,), in the order you added them
portfolio.assets # every asset, keyed by name, read-only
EnergySystem parameters¶
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
portfolio |
AssetPortfolio |
Yes | - | The assets you own and operate |
scenarios |
Scenario or list[Scenario] |
Yes | - | The futures you plan for: demand, available capacity and prices |
timestep |
timedelta |
Yes | - | Length of each step |
number_of_steps |
int |
Yes | - | How many steps the horizon has |
markets |
EnergyMarket or list[EnergyMarket] or None |
No | None |
The markets you buy from and sell to |
objective |
Objective |
No | Objective() |
What to maximize: expected profit by default |
initial_conditions |
sequence of initial states | No | () |
Where each asset starts, just before the first step |
final_conditions |
sequence of final states | No | () |
Where an asset has to be at the last step |
Each one is covered on its own page:
scenarios: a single Scenario holds the time series of one future. A list of them plans against several futures.timestepandnumber_of_steps: see Time and units.markets: see EnergyMarket.objective: see Risk (CVaR), to weigh profit against risk.initial_conditionsandfinal_conditions: see below.
Creating the EnergySystem checks the whole configuration before anything is solved, and raises an OdysValidationError on the first problem. See Validation and errors.
Initial and final conditions¶
Some assets carry state from one step to the next: a generator's output, a battery's charge. An initial condition says where such an asset starts, and a final condition where it has to end. Say you're planning tomorrow's dispatch: the CCGT running tonight and the battery's charge at midnight are where tomorrow starts.
Conditions go to the EnergySystem, not to a Scenario, because they're the same in every future: you know the starting state when you plan, and a final state is a target you choose. Each asset has at most one of each.
| Asset | Initial condition | Final condition | Initial condition required |
|---|---|---|---|
| Generator | GeneratorInitialState |
GeneratorFinalState |
When its start matters: ramp limits, minimum up or down times, start-up or shut-down costs |
| BatteryStorage | StorageInitialState |
StorageFinalState |
Always |
| ElectricVehicle | ElectricVehicleInitialState |
ElectricVehicleFinalState |
Always |
Final conditions are always optional. Loads, chargers and markets carry no state, so they take none. Each asset page shows its conditions in code.
One bus¶
odys connects every asset and every market to a single bus. At every step and in every scenario, the power fed in equals the power taken out:
| Feeds the bus | Draws from the bus |
|---|---|
| Generator output | FixedLoad demand |
| Battery discharge, from battery storage and vehicles with V2G | FlexibleLoad consumption, within its range |
| EnergyMarket purchases | Battery charging |
| EnergyMarket sales |
There's no network: no lines, no congestion and no losses between assets, so power fed in anywhere serves demand anywhere. Chargers aren't on the bus themselves: the vehicles plugged into them are.
There's no unserved energy either. All demand has to be met, so a system whose supply can't cover it has no solution. odys catches the obvious cases when you create the EnergySystem (see Validation and errors). See PB-1.
Optimizing¶
.optimize() builds the model as a mixed-integer linear program and solves it with HiGHS, unless you choose another solver with a SolverConfig (see Solvers). It returns the results, one container per asset type; see Results. The exact model is written out in the Mathematical Formulation.
Next steps¶
The factory's demand was a list of four numbers in a Scenario. Next, see what a Scenario describes: the availability, demand and prices your system faces at each step.