Skip to content

Battery & Degradation

Battery Model

The BatteryModel tracks the physical state of the battery system throughout the simulation.

Initialization

python
BatteryModel(
    capacity_mwh=10.0,           # Total energy capacity
    power_mw=5.0,                # Max charge/discharge power
    min_soc_percent=10.0,        # Minimum SoC (%)
    max_soc_percent=90.0,        # Maximum SoC (%)
    round_trip_efficiency=0.9,   # RTE as decimal (0-1)
    initial_soc_percent=50.0     # Starting SoC (%)
)

Efficiency Model

Round-trip efficiency (RTE) is split symmetrically into charge and discharge efficiency:

charge_efficiency = √RTE
discharge_efficiency = √RTE

Use the interactive calculator below to see how RTE affects energy flow:

Grid Input
10 MWh
94.87% eff.
Stored
9.49 MWh
94.87% eff.
Delivered
9.00 MWh
Energy lost: 1.00 MWh (10.0%)

SoC Bounds

min_soc_mwh = capacity_mwh × min_soc_percent / 100
max_soc_mwh = capacity_mwh × max_soc_percent / 100
usable_capacity = max_soc_mwh − min_soc_mwh

Charge/Discharge Limits

Maximum energy per 15-minute period:

max_charge = min(power_mw × 0.25, available_capacity / charge_efficiency)
max_discharge = min(power_mw × 0.25, stored_energy × discharge_efficiency)

Where:

  • available_capacity = max_soc_mwh − current_soc_mwh
  • stored_energy = current_soc_mwh − min_soc_mwh

Dynamic Updates

During simulation, the battery model is updated as degradation occurs:

  • update_degraded_capacity(new_capacity) — recalculates SoC bounds
  • update_degraded_rte(new_rte) — recalculates one-way efficiencies

Degradation Model

The DegradationModel tracks capacity and RTE fade from two independent sources: cycling and calendar aging.

Interactive Degradation Curves

Adjust the parameters to see how different degradation rates affect battery health over a 25-year project lifetime:

Initialization

python
DegradationModel(
    initial_capacity_mwh=10.0,
    initial_rte=0.9,
    degradation_per_cycle=0.00003,      # Capacity fade per EFC (decimal)
    degradation_per_year=0.01,           # Calendar fade per year (1%)
    lifetime_cycles=8000,                # Cycles before replacement
    rte_degradation_per_cycle=0.000004   # RTE fade per EFC (optional)
)

Note

degradation_per_cycle is entered as a percentage in the API (e.g. 0.003) and divided by 100 internally.

Cycle-Based Degradation

Each cycle contributes to capacity fade, weighted by depth of discharge:

equivalent_full_cycles = energy_throughput_mwh / initial_capacity_mwh
adjusted_cycles = equivalent_full_cycles × DoD^1.5
capacity_fade = adjusted_cycles × degradation_per_cycle

The DoD exponent of 1.5 means deeper cycles degrade the battery faster:

Depth of DischargeDegradation Factor
100%1.00×
80%0.72×
50%0.35×
20%0.09×

Calendar-Based Degradation

Time-dependent fade regardless of usage:

capacity_calendar_fade = years × degradation_per_year
rte_calendar_fade = years × degradation_per_year × 0.25

Calendar aging affects RTE at 25% the rate of capacity.

RTE Degradation

If rte_degradation_per_cycle is specified:

rte_cycle_fade = total_cycles × rte_degradation_per_cycle

If not specified, RTE fades at 40% the rate of capacity:

rte_cycle_fade = capacity_cycle_fade × 0.4

State of Health

SoH is tracked separately for cycle and calendar sources. The worse of the two determines overall health:

soh_capacity = 1.0 − max(capacity_cycle_fade, capacity_calendar_fade)
soh_rte = 1.0 − max(rte_cycle_fade, rte_calendar_fade)

current_capacity = initial_capacity × soh_capacity
current_rte = initial_rte × soh_rte

Replacement Logic

When soh_capacity drops below 70%:

  1. Replacement counter increments
  2. Current cycle/year counters saved as new baseline
  3. All SoH values reset to 100%
  4. Future degradation calculated from the replacement point

This models a full battery swap-out when capacity drops below acceptable levels.

Degradation Tracker

SimpleDegradationTracker integrates degradation into the simulation loop:

PropertyValue
Update intervalEvery 672 periods (~7 days at 15-min resolution)
Per updateConverts accumulated throughput to cycles, elapsed periods to years
ActionCalls add_cycle() and add_time(), then updates battery model
FinalizeForces a final update at simulation end

Typical Values

ParameterTypical RangeDescription
degradation_per_year0.005 - 0.020.5-2% calendar fade per year
degradation_per_cycle0.001 - 0.01Per EFC capacity fade (API input)
lifetime_cycles4,000 - 15,000Cycles before replacement
Replacement threshold70% SoHFixed

Solar Model

Calculates actual solar generation from a production profile:

actual_generation = profile_mw × degradation_factor × availability / 100
actual_generation = min(actual_generation, peak_power_mw)

Annual degradation: degradation_factor = 1.0 − degradation_rate × years_operated

Wind Model

Same calculation as solar:

actual_generation = profile_mw × degradation_factor × availability / 100
actual_generation = min(actual_generation, peak_power_mw)

Both models cap output at the configured peak power (AC inverter limit).