Files
EOS/docs/_generated/configoptimization.md
T
560370281f perf(prediction): optional SciPy-free evaluation of the self-consumption tables (#1378)
* perf(prediction): evaluate self-consumption tables without SciPy

The GENETIC inverter (and the GENETIC0 one) evaluates a pickled
scipy.interpolate.RegularGridInterpolator for the self-consumption
probabilities. SciPy is used nowhere else in EOS, yet loading it costs
about 35 MB RSS (21 MB anonymous) on Linux arm64, which every
optimization run pays.

Add utils/gridinterpolator.py:

- LinearGridInterpolator: 2-D linear interpolation with the same steps
  as SciPy's linear 2-D path (interval search, normalised distances,
  corner weights summed in the same order, fill value outside the grid,
  NaN for NaN input).
- load_grid_interpolator(): reads the shipped pickles with a restricted
  unpickler that maps the SciPy class to a stand-in and accepts only
  the NumPy globals needed for the arrays. SciPy is not imported, and
  the data files no longer depend on SciPy's private module paths.

Results are bit-identical to SciPy on Linux (aarch64, checked on 3.6M
points per table including the query pattern of the inverter). The
macOS arm64 SciPy wheel fuses multiply-add, so there SciPy itself
differs by <= 1.1e-16; GENETIC plans for fixed seeds are unchanged on
both platforms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(prediction): make the SciPy-free table evaluation opt-in

Add optimization.self_consumption_interpolator (scipy | numpy, default scipy).
The default keeps evaluating the self-consumption tables with SciPy's
RegularGridInterpolator, built from the same grid, values and fill value the
pickle holds, so results are unchanged. With numpy the tables are evaluated by
LinearGridInterpolator and SciPy is never imported. The backend is read on
every evaluation, so a config change applies to the next run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Christin <christin@BigBrother.bikini.bottom.zone>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Andreas <drbacke@gmx.de>
2026-10-11 09:38:14 +02:00

10 KiB

General Optimization Configuration

:::{table} optimization :widths: 10 20 10 5 5 30 :align: left

Name Environment Variable Type Read-Only Default Description
algorithm EOS_OPTIMIZATION__ALGORITHM <enum 'OptimizationAlgorithm'> rw required Optimization algorithm [GENETIC
algorithms list[str] ro N/A Available optimization algorithms.
genetic EOS_OPTIMIZATION__GENETIC GeneticCommonSettings rw required GENETIC optimization algorithm configuration.
genetic0 EOS_OPTIMIZATION__GENETIC0 Genetic0CommonSettings rw required GENETIC0 optimization algorithm configuration.
keys list[str] ro N/A The keys of the solution.
self_consumption_interpolator EOS_OPTIMIZATION__SELF_CONSUMPTION_INTERPOLATOR <enum 'GridInterpolatorBackend'> rw scipy How the inverter's self-consumption probability table is evaluated [scipy
:::

Example Input

   {
       "optimization": {
           "algorithm": "GENETIC",
           "genetic": {
               "interval_sec": 3600,
               "horizon_hours": 24,
               "individuals": 400,
               "generations": 400,
               "seed": null,
               "fitness_cache_max_entries": null,
               "measurement_max_age_seconds": 300,
               "tail_horizon_hours": 48,
               "terminal_value_mode": "AUTO",
               "terminal_value_euro_per_kwh": 0.0,
               "terminal_value_window_hours": 24,
               "penalties": {
                   "ev_soc_miss": 10
               }
           },
           "genetic0": {
               "horizon_hours": 24,
               "individuals": 400,
               "generations": 400,
               "seed": null,
               "penalties": {
                   "ev_soc_miss": 10
               }
           },
           "self_consumption_interpolator": "scipy"
       }
   }

Example Output

   {
       "optimization": {
           "algorithm": "GENETIC",
           "genetic": {
               "interval_sec": 3600,
               "horizon_hours": 24,
               "individuals": 400,
               "generations": 400,
               "seed": null,
               "fitness_cache_max_entries": null,
               "measurement_max_age_seconds": 300,
               "tail_horizon_hours": 48,
               "terminal_value_mode": "AUTO",
               "terminal_value_euro_per_kwh": 0.0,
               "terminal_value_window_hours": 24,
               "penalties": {
                   "ev_soc_miss": 10
               },
               "horizon": 24
           },
           "genetic0": {
               "horizon_hours": 24,
               "individuals": 400,
               "generations": 400,
               "seed": null,
               "penalties": {
                   "ev_soc_miss": 10
               },
               "interval_sec": 3600,
               "horizon": 24
           },
           "self_consumption_interpolator": "scipy",
           "algorithms": [
               "GENETIC",
               "GENETIC0"
           ],
           "keys": []
       }
   }

GENETIC0 Optimization Algorithm Configuration

:::{table} optimization::genetic0 :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
generations Optional[int] rw 400 Number of generations to evolve [>= 10]. Defaults to 400.
horizon int ro N/A Number of optimization steps.
horizon_hours int rw 24 The general time window within which the energy optimization goal shall be achieved [h]. Defaults to 24 hours.
individuals Optional[int] rw 300 Number of individuals (solutions) in the population [>= 10]. Defaults to 300.
interval_sec int ro N/A The optimization interval [sec]. Fixed to 1 hour (3600 seconds).
penalties dict[str, Union[float, int, str]] rw required Penalty parameters used in fitness evaluation.
seed Optional[int] rw None Random seed for reproducibility. None = random.
:::

Example Input

   {
       "optimization": {
           "genetic0": {
               "horizon_hours": 24,
               "individuals": 300,
               "generations": 400,
               "seed": null,
               "penalties": {
                   "ev_soc_miss": 10
               }
           }
       }
   }

Example Output

   {
       "optimization": {
           "genetic0": {
               "horizon_hours": 24,
               "individuals": 300,
               "generations": 400,
               "seed": null,
               "penalties": {
                   "ev_soc_miss": 10
               },
               "interval_sec": 3600,
               "horizon": 24
           }
       }
   }

GENETIC Optimization Algorithm Configuration

:::{table} optimization::genetic :widths: 10 10 5 5 30 :align: left

Name Type Read-Only Default Description
fitness_cache_max_entries Optional[int] rw None Maximum number of keys in the per-run fitness cache. None = unbounded (default), 0 = cache disabled, N > 0 = keep at most N keys and evict the oldest first. Every cached evaluation takes one or two keys. The cache only saves repeated evaluations; the result for a fixed seed is the same with any value. Limit it to cap memory on small devices.
generations Optional[int] rw 400 Number of generations to evolve [>= 10]. Defaults to 400.
horizon int ro N/A Number of optimization steps.
horizon_hours int rw 24 The general time window within which the energy optimization goal shall be achieved [h]. Defaults to 24 hours.
individuals Optional[int] rw 300 Number of individuals (solutions) in the population [>= 10]. Defaults to 300.
interval_sec Literal[900, 3600] rw 3600 The optimization interval [sec]. Defaults to 3600 seconds (1 hour)
measurement_max_age_seconds int rw 300 Maximum age of SoC measurements for configuration-based optimization [s].
penalties dict[str, Union[float, int, str]] rw required Penalty parameters used in fitness evaluation.
seed Optional[int] rw None Random seed for reproducibility. None = random.
tail_horizon_hours int rw 48 Forecast lookahead after the control horizon [h]. No tail commands are issued. Set 0 to disable.
terminal_value_euro_per_kwh float rw 0.0 Value assigned to usable battery energy remaining at the end of the optimization horizon [EUR/kWh]. This terminal value is independent of the battery LCOS. Only used with terminal_value_mode = FIXED. Defaults to 0 EUR/kWh.
terminal_value_mode <enum 'TerminalValueMode'> rw AUTO How to value the energy left in the battery at the end of the control horizon. AUTO solves the forecast tail with an AUTO continuation proxy at its end (or only the proxy if tail is zero); FIXED uses 'terminal_value_euro_per_kwh'. Defaults to AUTO.
terminal_value_window_hours int rw 24 Length of the trailing window at the effective tail end the AUTO continuation curve is derived from [h]. One day covers a full load and PV cycle. Defaults to 24 hours.
:::

Example Input

   {
       "optimization": {
           "genetic": {
               "interval_sec": 3600,
               "horizon_hours": 24,
               "individuals": 300,
               "generations": 400,
               "seed": null,
               "fitness_cache_max_entries": null,
               "measurement_max_age_seconds": 300,
               "tail_horizon_hours": 48,
               "terminal_value_mode": "AUTO",
               "terminal_value_euro_per_kwh": 0.0,
               "terminal_value_window_hours": 24,
               "penalties": {
                   "ev_soc_miss": 10
               }
           }
       }
   }

Example Output

   {
       "optimization": {
           "genetic": {
               "interval_sec": 3600,
               "horizon_hours": 24,
               "individuals": 300,
               "generations": 400,
               "seed": null,
               "fitness_cache_max_entries": null,
               "measurement_max_age_seconds": 300,
               "tail_horizon_hours": 48,
               "terminal_value_mode": "AUTO",
               "terminal_value_euro_per_kwh": 0.0,
               "terminal_value_window_hours": 24,
               "penalties": {
                   "ev_soc_miss": 10
               },
               "horizon": 24
           }
       }
   }