Skip to content

Debug export: input_data.initial_soe is stale (day-start SOC) after the first optimization run of the day #292

Description

@johanzander

What happened

While investigating #289, I found that input_data["initial_soe"] in the debug export/schedule JSON does not represent the SOE the DP optimizer actually started from for a given run, once more than one optimization has happened that day.

Root cause

core/bess/battery_system_manager.py:2094-2104:

total_cap = self.battery_settings.total_capacity
if self._initial_soc_pct is not None:
    result.input_data["initial_soe"] = (
        self._initial_soc_pct / 100.0 * total_cap
    )
elif not prepare_next_day:
    current_soc = self._get_current_battery_soc()
    if current_soc is not None:
        result.input_data["initial_soe"] = current_soc / 100.0 * total_cap

self._initial_soc_pct is documented at battery_system_manager.py:171 as "SOC at midnight (%), set at period 0", and is set once per day at battery_system_manager.py:1277. Because the if self._initial_soc_pct is not None: branch always wins once it's set, input_data["initial_soe"] is overwritten with the day-start (midnight) SOE on every re-optimization for the rest of the day — not the SOE the DP actually used as its starting state for that specific run.

Evidence

In a user-submitted debug bundle for #289 (BESS v9.8.1, optimization triggered at 08:00, optimization_period=32):

  • input_data.initial_soe = 9.6 kWh
  • The same run's own period-32 decision row shows battery_soe_start = 6.2 kWh

The DP's real starting SOE for that run was 6.2 kWh (confirmed by reproducing the run with the real dp_battery_algorithm.py code); 9.6 was the midnight SOC, carried over from _initial_soc_pct.

Why this matters

This field is one of the primary things a maintainer (or an AI agent, per docs/agents/) reads first when debugging a reported schedule from a user's debug bundle. Because it silently means "day-start SOE" instead of "this run's starting SOE" after the first run of the day, it actively misleads root-cause analysis — it did exactly that during the #289 investigation before we caught it by cross-checking against the period data.

Suggested fix

Split the concepts instead of overloading one field, e.g.:

  • Keep initial_soe meaning "the SOE this optimization run actually started from" (i.e. always populate it from the current run's real starting state, not _initial_soc_pct).
  • Add a separate, distinctly-named field such as day_start_soe for the midnight value DailyViewBuilder needs for chart anchoring.

Happy to have an agent pick this up via the usual @claude-bot analyze / @claude-bot fix flow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions