energykit.optimize

Provably-optimal battery and EV dispatch scheduling.

energykit.optimize — DER scheduling: battery, EV, solar dispatch.

class energykit.optimize.BatteryScheduler(capacity_kwh: float, max_power_kw: float, efficiency: float = 0.9, initial_soc: float = 0.5, min_soc: float = 0.1, max_soc: float = 0.95, dt_hours: float = 1.0, index: DatetimeIndex | None = None)[source]

Bases: object

Optimal battery charge/discharge scheduler.

Formulates a linear program to minimize total electricity cost over a planning horizon by choosing when to charge (buy cheap) and discharge (avoid buying expensive).

The LP is:

\[\min_{c_t, d_t} \sum_t p_t (c_t - d_t) \cdot \Delta t\]

subject to:

  • SoC dynamics: \(SoC_{t+1} = SoC_t + \eta_c c_t \Delta t - (1/\eta_d) d_t \Delta t\)

  • Capacity: \(SoC_{\min} \le SoC_t \le SoC_{\max}\)

  • Power limits: \(0 \le c_t, d_t \le P_{\max}\)

  • No simultaneous charge/discharge (relaxed LP — typically satisfied at optimum)

Parameters:
  • capacity_kwh (float) – Usable battery capacity in kWh.

  • max_power_kw (float) – Maximum charge and discharge power in kW.

  • efficiency (float, default 0.90) – Round-trip efficiency (0–1). Applied symmetrically to charge and discharge.

  • initial_soc (float, default 0.50) – Initial state of charge as a fraction of capacity (0–1).

  • min_soc (float, default 0.10) – Minimum allowed SoC (depth-of-discharge protection).

  • max_soc (float, default 0.95) – Maximum allowed SoC (overcharge protection).

  • dt_hours (float, default 1.0) – Timestep duration in hours. Use 0.5 for 30-minute data.

  • index (pd.DatetimeIndex or None) – Optional datetime index for the output schedule.

Examples

>>> batt = BatteryScheduler(capacity_kwh=10, max_power_kw=5)
>>> result = batt.optimize(prices_array)
>>> result.schedule_df[["charge_kw", "discharge_kw", "soc_kwh"]].plot()
optimize(prices: ndarray, load_kw: ndarray | None = None) ScheduleResult[source]

Run the battery optimization.

Parameters:
  • prices (array-like of shape (T,)) – Electricity import price at each timestep ($/kWh or any currency/kWh).

  • load_kw (array-like of shape (T,) or None) – Baseline load at each timestep (kW). Used only for baseline cost calculation. Does not affect the battery dispatch.

Return type:

ScheduleResult

class energykit.optimize.EVScheduler(battery_kwh: float, max_charge_kw: float, efficiency: float = 0.92, dt_hours: float = 1.0, index: DatetimeIndex | None = None)[source]

Bases: object

Smart EV charging scheduler.

Charges an EV battery to a target SoC by a departure time while minimizing electricity cost. Models unidirectional (G1V) charging only.

Parameters:
  • battery_kwh (float) – EV battery capacity (usable, kWh).

  • max_charge_kw (float) – Maximum AC charging power (e.g. 7.4 kW for Level 2 EVSE).

  • efficiency (float, default 0.92) – Charging efficiency (AC-to-DC).

  • dt_hours (float, default 1.0) – Timestep duration in hours.

  • index (pd.DatetimeIndex or None) – Optional datetime index for the output schedule.

Examples

>>> ev = EVScheduler(battery_kwh=75, max_charge_kw=11.0)
>>> prices = np.tile([0.08, 0.10, 0.12, 0.25, 0.30], 5)  # TOU prices
>>> result = ev.optimize(
...     prices=prices,
...     initial_soc=0.20,
...     target_soc=0.80,
...     departure_step=22,    # must be fully charged by step 22
... )
>>> print(result.savings_usd)
optimize(prices: ndarray, initial_soc: float, target_soc: float, departure_step: int | None = None) ScheduleResult[source]

Compute the cost-optimal EV charging schedule.

Parameters:
  • prices (array-like, shape (T,)) – Electricity price over the planning horizon.

  • initial_soc (float) – Current SoC as fraction of capacity (0–1).

  • target_soc (float) – Required SoC at departure (0–1).

  • departure_step (int or None) – Timestep index by which target_soc must be reached. Defaults to the last step.

Return type:

ScheduleResult

DER Scheduler

energykit.optimize.der

Optimal scheduling of Distributed Energy Resources (DER) using Linear Programming via scipy.optimize.linprog (no additional dependencies).

Classes

BatteryScheduler

Minimize electricity cost by optimally scheduling battery charge / discharge given time-varying retail prices and technical constraints (capacity, max power, round-trip efficiency, SoC limits).

EVScheduler

Smart EV charging scheduler. Charges the vehicle to a target state of charge by a departure time while minimizing electricity cost.

Both classes follow a simple optimize(prices) API that returns a ScheduleResult dataclass.

Usage

>>> import numpy as np
>>> from energykit.optimize import BatteryScheduler
>>>
>>> # 24-hour time-of-use prices ($/kWh), hourly resolution
>>> prices = np.array([0.08]*8 + [0.22]*4 + [0.12]*4 + [0.28]*4 + [0.10]*4)
>>>
>>> battery = BatteryScheduler(
...     capacity_kwh=13.5,     # Tesla Powerwall 2
...     max_power_kw=5.0,
...     efficiency=0.90,
...     initial_soc=0.20,
... )
>>> result = battery.optimize(prices)
>>> print(result.savings_usd)
1.84
>>> print(result.schedule_df)
class energykit.optimize.der.BatteryScheduler(capacity_kwh: float, max_power_kw: float, efficiency: float = 0.9, initial_soc: float = 0.5, min_soc: float = 0.1, max_soc: float = 0.95, dt_hours: float = 1.0, index: DatetimeIndex | None = None)[source]

Bases: object

Optimal battery charge/discharge scheduler.

Formulates a linear program to minimize total electricity cost over a planning horizon by choosing when to charge (buy cheap) and discharge (avoid buying expensive).

The LP is:

\[\min_{c_t, d_t} \sum_t p_t (c_t - d_t) \cdot \Delta t\]

subject to:

  • SoC dynamics: \(SoC_{t+1} = SoC_t + \eta_c c_t \Delta t - (1/\eta_d) d_t \Delta t\)

  • Capacity: \(SoC_{\min} \le SoC_t \le SoC_{\max}\)

  • Power limits: \(0 \le c_t, d_t \le P_{\max}\)

  • No simultaneous charge/discharge (relaxed LP — typically satisfied at optimum)

Parameters:
  • capacity_kwh (float) – Usable battery capacity in kWh.

  • max_power_kw (float) – Maximum charge and discharge power in kW.

  • efficiency (float, default 0.90) – Round-trip efficiency (0–1). Applied symmetrically to charge and discharge.

  • initial_soc (float, default 0.50) – Initial state of charge as a fraction of capacity (0–1).

  • min_soc (float, default 0.10) – Minimum allowed SoC (depth-of-discharge protection).

  • max_soc (float, default 0.95) – Maximum allowed SoC (overcharge protection).

  • dt_hours (float, default 1.0) – Timestep duration in hours. Use 0.5 for 30-minute data.

  • index (pd.DatetimeIndex or None) – Optional datetime index for the output schedule.

Examples

>>> batt = BatteryScheduler(capacity_kwh=10, max_power_kw=5)
>>> result = batt.optimize(prices_array)
>>> result.schedule_df[["charge_kw", "discharge_kw", "soc_kwh"]].plot()
optimize(prices: ndarray, load_kw: ndarray | None = None) ScheduleResult[source]

Run the battery optimization.

Parameters:
  • prices (array-like of shape (T,)) – Electricity import price at each timestep ($/kWh or any currency/kWh).

  • load_kw (array-like of shape (T,) or None) – Baseline load at each timestep (kW). Used only for baseline cost calculation. Does not affect the battery dispatch.

Return type:

ScheduleResult

class energykit.optimize.der.EVScheduler(battery_kwh: float, max_charge_kw: float, efficiency: float = 0.92, dt_hours: float = 1.0, index: DatetimeIndex | None = None)[source]

Bases: object

Smart EV charging scheduler.

Charges an EV battery to a target SoC by a departure time while minimizing electricity cost. Models unidirectional (G1V) charging only.

Parameters:
  • battery_kwh (float) – EV battery capacity (usable, kWh).

  • max_charge_kw (float) – Maximum AC charging power (e.g. 7.4 kW for Level 2 EVSE).

  • efficiency (float, default 0.92) – Charging efficiency (AC-to-DC).

  • dt_hours (float, default 1.0) – Timestep duration in hours.

  • index (pd.DatetimeIndex or None) – Optional datetime index for the output schedule.

Examples

>>> ev = EVScheduler(battery_kwh=75, max_charge_kw=11.0)
>>> prices = np.tile([0.08, 0.10, 0.12, 0.25, 0.30], 5)  # TOU prices
>>> result = ev.optimize(
...     prices=prices,
...     initial_soc=0.20,
...     target_soc=0.80,
...     departure_step=22,    # must be fully charged by step 22
... )
>>> print(result.savings_usd)
optimize(prices: ndarray, initial_soc: float, target_soc: float, departure_step: int | None = None) ScheduleResult[source]

Compute the cost-optimal EV charging schedule.

Parameters:
  • prices (array-like, shape (T,)) – Electricity price over the planning horizon.

  • initial_soc (float) – Current SoC as fraction of capacity (0–1).

  • target_soc (float) – Required SoC at departure (0–1).

  • departure_step (int or None) – Timestep index by which target_soc must be reached. Defaults to the last step.

Return type:

ScheduleResult

class energykit.optimize.der.ScheduleResult(schedule_df: DataFrame, total_cost_usd: float, baseline_cost_usd: float, savings_usd: float, status: str)[source]

Bases: object

Optimization result from BatteryScheduler or EVScheduler.

schedule_df

Hourly schedule with columns depending on optimizer type.

Type:

pd.DataFrame

total_cost_usd

Total electricity cost after optimization.

Type:

float

baseline_cost_usd

Electricity cost without any DER dispatch (flat import).

Type:

float

savings_usd

Cost savings vs. baseline.

Type:

float

status

Solver status ("optimal", "infeasible", etc.)

Type:

str

baseline_cost_usd: float
savings_usd: float
schedule_df: DataFrame
status: str
total_cost_usd: float