timeseries — multi-scenario load flow, no dynamics

This is feature 2.1 from the migration plan: new relative to the MATLAB tool, which had no equivalent. Each Snapshot is a set of P/Q overrides (dispatch and/or load) applied to a base Network; run_time_series() solves each one with the existing static power-flow adapter in a plain loop — no dynamics, no coupling between snapshots.

        sequenceDiagram
    participant U as caller
    participant S as scale_loads() / dispatch_sweep()
    participant A as apply_snapshot()
    participant PF as run_power_flow()
    participant R as TimeSeriesResult

    U->>S: base Network, {label: factor, ...}
    S-->>U: list[Snapshot]
    loop each Snapshot
        U->>A: Network, Snapshot
        A-->>U: new Network (der_units/loads overridden,<br/>everything else unchanged)
        U->>PF: run_power_flow(overridden Network)
        PF-->>R: bus_table() or empty if not converged
    end
    R-->>U: bus_tables[label], converged[label]
    

This is a deliberate simplicity choice over pandapower’s native timeseries/controller batch API: it reuses the already-tested run_power_flow() path exactly, at the cost of not getting pandapower’s vectorized C-level speedup. Worth revisiting if sweep size ever makes that difference matter — swap the inner loop for pandapower’s batch update_data/OutputWriter machinery without changing the Snapshot/TimeSeriesResult interface.

Network is a frozen pydantic model, so apply_snapshot() never mutates the base network — model_copy(update=...) produces an independent copy per snapshot, which is what makes running snapshots in any order (or in parallel) safe.

Reference

Time-series load flow: multiple dispatch/load snapshots, no dynamics.

This is feature 2.1 from the migration plan — new relative to the MATLAB tool. Each Snapshot is a set of P/Q overrides (dispatch and/or load) applied to a base Network, and run_time_series() solves each one with the existing static power-flow adapter (g2elin_core.powerflow) in a plain loop.

That’s a deliberate simplicity choice over pandapower’s native timeseries/controller batch API: it reuses the already-tested run_power_flow() path exactly, at the cost of not getting pandapower’s vectorized C-level speedup. Worth revisiting if sweep size ever makes that speed difference matter (see the migration plan’s “pandapower solve speed at scale” risk note) — swap run_time_series()’s inner loop for pandapower’s batch update_data/OutputWriter machinery without changing the Snapshot/TimeSeriesResult interface.

class g2elin_core.timeseries.scenario.Snapshot(label, der_p_mw=<factory>, der_q_mvar=<factory>, load_p_mw=<factory>, load_q_mvar=<factory>)[source]

Bases: object

P/Q overrides for one time step. Unlisted DER units/loads keep the base network’s values. DER units are keyed by DerUnit.id, loads by their index in Network.loads (loads have no id field).

Parameters:
label: str
der_p_mw: dict[int, float]
der_q_mvar: dict[int, float]
load_p_mw: dict[int, float]
load_q_mvar: dict[int, float]
g2elin_core.timeseries.scenario.apply_snapshot(network, snapshot)[source]

Returns a new Network with the snapshot’s overrides applied.

Parameters:
Return type:

Network

g2elin_core.timeseries.scenario.scale_loads(network, factors)[source]

One snapshot per (label, factor) pair, scaling every load’s P and Q by factor relative to the base network — e.g. for a simple daily load curve: scale_loads(net, {"00:00": 0.6, "12:00": 1.0, "18:00": 1.15}).

Parameters:
Return type:

list[Snapshot]

g2elin_core.timeseries.scenario.dispatch_sweep(network, der_id, p_mw_values)[source]

One snapshot per (label, P) pair, overriding a single DER unit’s dispatch — e.g. a generator ramp/PV-curve sweep.

Parameters:
Return type:

list[Snapshot]

class g2elin_core.timeseries.scenario.TimeSeriesResult(network: 'Network', snapshots: 'list[Snapshot]', bus_tables: 'dict[str, pd.DataFrame]', converged: 'dict[str, bool]', results: 'dict[str, PowerFlowResult]'=<factory>)[source]

Bases: object

Parameters:
network: Network
snapshots: list[Snapshot]
bus_tables: dict[str, DataFrame]
converged: dict[str, bool]
results: dict[str, PowerFlowResult]
combined_bus_table()[source]

All snapshots’ bus tables stacked, with a snapshot column.

Return type:

DataFrame

all_converged()[source]
Return type:

bool

g2elin_core.timeseries.scenario.run_time_series(network, snapshots)[source]
Parameters:
Return type:

TimeSeriesResult