Time Series

Goal

This class aims to make faster (and easier) the computations of the current flows (measured in Amps) at a certain side of a powerline / transformer when the topology is not modified.

It can be used as:

from lightsim2grid import TimeSerie
import grid2op
from lightsim2grid.lightSimBackend import LightSimBackend

env_name = ...
env = grid2op.make(env_name, backend=LightSimBackend())

time_series = TimeSerie(env)
res_p, res_a, res_v = time_series.get_flows(scenario_id=..., seed=...)

# we have:
# res_p[row_id] will be the active power flows (origin side), on all powerlines corresponding to step "row_id"
# res_a[row_id] will be the current flows, on all powerlines corresponding to step "row_id"
# res_v[row_id] will be the complex voltage, on all bus of the grid at step "row_id"

For now this relies on grid2op, but we could imagine a version of this class that can read to / from other data sources (for now please use the more basic lightsim2grid.timeSerie.TimeSeriesCPP for such purpose)

Importantly, this method is around 11x faster than simulating “do nothing” (or “one change then nothing”) with grid2op (and lightsim2grid, see section Benchmarks (Time Series) )

Note

A more detailed example is given in the examples\time_serie.py file from the lightsim2grid package.

Warning

Topology and injections

The topology is taken from the initial provided grid and cannot be changed when evaluating a given “time serie”.

Then, the call to time_series.compute_V(scenario_id=…, seed=…) will only read the injections (productions and loads) from grid2op to compute the voltages.

Note

As this class calls a long c++ function, it is possible to use the python Threading module to achieve high efficient parrallelism. An example is provided in the examples\timeseries_with_grid2op_multithreading.py file.

Note

Set time_series.init_from_n_powerflow = True before calling compute_V (setting it afterwards has no effect) to initialize the first step of the batch with the voltage solution of the grid’s current (“n”) state instead of a flat start – this is usually faster. See lightsim2grid.timeSerie.TimeSerie.init_from_n_powerflow().

Independent scenarios: InjectionSweep

TimeSerie initializes each step with the solution of the step before it. That is the right thing to do for a time series – two consecutive instants are close to one another, so the previous solution is an excellent starting point – but it makes the steps a chain: step i cannot be computed before step i-1, and its result depends on it.

If your “steps” are unrelated scenarios rather than consecutive instants (a sample of load / generation patterns, a set of “what if” injections, a Monte-Carlo draw…), use lightsim2grid.injectionSweep.InjectionSweep instead. It computes exactly the same thing, with exactly the same interface, but starts every step from the same voltage – the one you provide, or the “n” powerflow result if init_from_n_powerflow is set – just like lightsim2grid.contingencyAnalysis.ContingencyAnalysis does for each contingency.

Two things follow:

  • the results do not depend on the order the steps are given in;

  • the batch can be spread over several OS threads, in c++, with sweep.nb_thread = … (the results do not depend on that either). TimeSerie cannot: splitting a chain into per-thread ranges would break it, so setting nb_thread to anything but 1 raises there.

import grid2op
from lightsim2grid import InjectionSweep, LightSimBackend

env_name = ...
env = grid2op.make(env_name, backend=LightSimBackend())

sweep = InjectionSweep(env)
sweep.nb_thread = 4
res_p, res_a, res_v = sweep.get_flows(scenario_id=..., seed=...)

Note

On a genuine time series TimeSerie usually needs fewer solver iterations, precisely because each step starts from its neighbour’s solution. InjectionSweep trades that away for independence – and buys back much more than it costs as soon as you use several threads.

Note

TimeSerie / InjectionSweep also expose a newer, setter-based API (modify_gen_p / modify_sgen_p / modify_load_p / modify_load_q / modify_gen_v + compute()) as an alternative to the single bundled compute_V_from_inj call – see Scenario Sweep (which uses that same API, plus a per-step contingency) for details on how it behaves when an axis is never set. modify_gen_v (per-step generator target voltage magnitude, in pu – vm_pu, NOT kV) is different from the other four: it does not feed the injection (Sbus) at all, it only re-seeds |V| at each voltage-regulating generator’s regulated bus before that step’s solve.

Benchmarks (Time Series)

Here are some benchmarks made with:

  • date: 2026-08-28 16:57 CEST

  • system: Linux 6.8.0-60-generic

  • OS: ubuntu 22.04

  • processor: 13th Gen Intel(R) Core(TM) i7-13700H

  • python version: 3.12.8.final.0 (64 bit)

  • numpy version: 2.3.5

  • pandas version: 2.3.3

  • pandapower version: 3.4.0

  • grid2op version: 1.12.5.dev0

  • lightsim2grid version: 1.0.0

  • lightsim2grid extra information:

    • klu_solver_available: True

    • nicslu_solver_available: True

    • cktso_solver_available: True

    • compiled_march_native: True

    • compiled_o3_optim: True

This benchmark is available by running, from the root of the lightsim2grid repository:

cd benchmarks
python3 time_serie.py

For this setting the outputs are:

For environment: l2rpn_neurips_2020_track2
Total time spent in "computer" to solve everything: 0.03s (20834 pf / s), 0.05 ms / pf)
    - time to pre process the injections: 0.00s
    - time to perform powerflows: 0.03s (22437 pf / s, 0.04 ms / pf)
In addition, it took 0.00 s to retrieve the current from the complex voltages (in total 19934.2 pf /s, 0.05 ms / pf)

Comparison with raw grid2op timings
It took grid2op (with lightsim2grid): 0.32s to perform the same computation
    This is a 11.0 speed up from TimeSerie over raw grid2op (lightsim2grid)
It took grid2op (with pandapower): 6.56s to perform the same computation
    This is a 227.1 speed up from TimeSerie over raw grid2op (pandapower)

In this case then, the `TimeSerie` module is 11 times faster than raw grid2op (lightsim2grid) and 227 times faster than raw grid2op (pandapower)
All results match !

Detailed usage

Classes:

Computers

alias of TimeSeriesCPP

TimeSerie(grid2op_env)

This helper class, that only works with grid2op when using a LightSimBackend allows to compute the flows (at the origin side of the powerline / transformers).

TimeSeriesCPP

Allows the computation of time series, that is, the same grid topology is used while the active / reactive power injected at each buse vary.

lightsim2grid.timeSerie.Computers

alias of TimeSeriesCPP

class lightsim2grid.timeSerie.TimeSerie(grid2op_env)[source]

This helper class, that only works with grid2op when using a LightSimBackend allows to compute the flows (at the origin side of the powerline / transformers). It is roughly equivalent to the grid2op code:

import grid2op
import numpy as np
from grid2op.Parameters import Parameters
from lightsim2grid import LightSimBackend

env_name = ...
param = Parameters()
param.NO_OVERFLOW_DISCONNECTION = True

env = grid2op.make(env_name, param=param, backend=LightSimBackend())

done = False
obs = env.reset()
nb_step = obs.max_step
Vs = np.zeros((nb_step, 2 * env.n_sub), dtype=complex)
As = np.zeros((nb_step, env.n_line), dtype=float)
while not done:
    obs, reward, done, info = env.step(env.action_space())
    Vs[i, :env.n_sub] = env.backend.V
    As[i] = obs.a_or

Compare to the previous code, it avoid all grid2op code and can be more than 15 times faster (on the case 118).

It also allows to use python threading module, as the c++ computation can be done in different python threads (the GIL is not locked during the c++ computation).

Examples

It can be used as:

from lightsim2grid import TimeSerie
import grid2op
from lightsim2grid import LightSimBackend

env_name = ...
env = grid2op.make(env_name, param=param, backend=LightSimBackend())

time_series = TimeSerie(env)
res_p, res_a, res_v = time_series.get_flows(scenario_id=..., seed=...)

Methods:

clear()

Clear everything, as if nothing has been computed

close()

permanently close the object

compute([v_init, max_iter, tol, ignore_errors])

Run the batch using whatever was set by modify_gen_p() / modify_sgen_p() / modify_load_p() / modify_load_q() (the new setter-based API -- an alternative to the single bundled compute_V_from_inj() call).

compute_A()

This function returns the current flows (in Amps, A) at the origin (for powerline) / high voltage (for transformer) side

compute_P()

This function returns the active power flows (in MW) at the origin (for powerline) / high voltage (for transformer) side

compute_V([scenario_id, seed, v_init, ...])

This function allows to retrieve the complex voltage at each bus of the grid for each step.

compute_V_from_inj(prod_p, load_p, load_q[, ...])

This function allows to compute the voltages, at each bus given a list of productions and loads.

get_flows([scenario_id, seed, v_init, ...])

Retrieve the flows for each step simulated.

get_injections([scenario_id, seed])

This function allows to retrieve the injection of the given scenario, for the given seed from the grid2op internal environment.

modify_gen_p(gen_p)

Per-step active generator setpoints, shape (n_simul, n_gen).

modify_gen_v(gen_v)

Per-step generator target voltage magnitude, shape (n_simul, n_gen), in pu (vm_pu), NOT kV.

modify_load_p(load_p)

Per-step active load setpoints, shape (n_simul, n_load).

modify_load_q(load_q)

Per-step reactive load setpoints, shape (n_simul, n_load).

modify_sgen_p(sgen_p)

Per-step active static generator setpoints, shape (n_simul, n_sgen).

Attributes:

init_from_n_powerflow

Whether to initialize the complex voltages of the first step of the batch with the results of a "n" powerflow (a powerflow at the current state of the grid) instead of a flat start.

clear()[source]

Clear everything, as if nothing has been computed

close()[source]

permanently close the object

compute(v_init=None, max_iter=None, tol=None, ignore_errors=False)[source]

Run the batch using whatever was set by modify_gen_p() / modify_sgen_p() / modify_load_p() / modify_load_q() (the new setter-based API – an alternative to the single bundled compute_V_from_inj() call). max_iter / tol default to the backend’s own values when not given.

compute_A()[source]

This function returns the current flows (in Amps, A) at the origin (for powerline) / high voltage (for transformer) side

It does not recompute the voltages at each buses, it uses the information get from compute_V and This is why you must call compute_V(…) first !

compute_P()[source]

This function returns the active power flows (in MW) at the origin (for powerline) / high voltage (for transformer) side

It does not recompute the voltages at each buses, it uses the information get from compute_V and This is why you must call compute_V(…) first !

compute_V(scenario_id=None, seed=None, v_init=None, ignore_errors=False)[source]

This function allows to retrieve the complex voltage at each bus of the grid for each step.

Warning

Topology fixed = no maintenance, no attacks, etc.

As the topology is fixed, this class does not allow to simulate the effect of maintenance or attacks !

compute_V_from_inj(prod_p, load_p, load_q, v_init=None, ignore_errors=False)[source]

This function allows to compute the voltages, at each bus given a list of productions and loads.

We do not recommend to use it directly, as the order of the load or generators might vary !

get_flows(scenario_id=None, seed=None, v_init=None, ignore_errors=False)[source]

Retrieve the flows for each step simulated.

Each row of the resulting flow matrix will correspond to a step.

Examples

import grid2op
from lightsim2grid import TimeSerie
from lightsim2grid import LightSimBackend
env_name = ...
env = grid2op.make(env_name, backend=LightSimBackend())

timeserie = TimeSerie(env)
res_p, res_a, res_v = timeserie.get_flows(scenario_id, seed, v_init, ignore_errors)

# in this results, then
# res_a[row_id] will be the flows, on all powerline corresponding to the `row_id` contingency.
# you can retrieve it with `security_analysis.contingency_order[row_id]`
get_injections(scenario_id=None, seed=None)[source]

This function allows to retrieve the injection of the given scenario, for the given seed from the grid2op internal environment.

property init_from_n_powerflow

Whether to initialize the complex voltages of the first step of the batch with the results of a “n” powerflow (a powerflow at the current state of the grid) instead of a flat start. Default: False. Must be set before the computation actually runs (eg before compute_V is called); it has no effect on a powerflow that has already been solved.

modify_gen_p(gen_p)[source]

Per-step active generator setpoints, shape (n_simul, n_gen). Part of the new setter-based API (see compute()); an alternative to compute_V_from_inj(), not required if you use that call instead.

modify_gen_v(gen_v)[source]

Per-step generator target voltage magnitude, shape (n_simul, n_gen), in pu (vm_pu), NOT kV. Unlike modify_gen_p()/modify_load_p()/ modify_load_q(), this does NOT feed the injection (Sbus) – it only re-seeds |V| at each voltage-regulating generator’s regulated bus before that step’s solve. See modify_gen_p().

modify_load_p(load_p)[source]

Per-step active load setpoints, shape (n_simul, n_load). See modify_gen_p().

modify_load_q(load_q)[source]

Per-step reactive load setpoints, shape (n_simul, n_load). See modify_gen_p().

modify_sgen_p(sgen_p)[source]

Per-step active static generator setpoints, shape (n_simul, n_sgen). See modify_gen_p().

class lightsim2grid.timeSerie.TimeSeriesCPP

Allows the computation of time series, that is, the same grid topology is used while the active / reactive power injected at each buse vary. The grid topology is fixed, the injections vary.

This is a “raw” c++ class, for an easier to use interface, please refer to the python documentation of the lightsim2grid.timeSerie.TimeSerie class.

Methods:

amps_computation_time(self)

Time spent in computing the flows (in amps) after the voltages have been computed at each nodes

available_algorithm_names(self)

Returns the names of all registered algorithms, including any loaded plugins, as a list of string.

available_default_algorithms(self)

Return the list of the names of the algorithm available on the current lightsim2grid installation.

change_algorithm(*args, **kwargs)

Overloaded function.

change_solver(*args, **kwargs)

Overloaded function.

clear(self)

Clear the solver and to as if the class never performed any powerflow.

close(self)

Clear the solver and to as if the class never performed any powerflow.

compute(self, Vinit, max_iter, tol)

Run the batch: one powerflow per simulation, using whatever was set by modify_* (and, on ScenarioSweep, set_contingency_lines / set_contingency_trafos).

compute_Vs(self, arg0, arg1, arg2, arg3, ...)

Compute the voltages (at each bus of the grid model) for some time series of injections (productions, loads, storage units, etc.)

compute_flows(self)

Retrieve the flows (in amps, at the origin of each powerlines / high voltage size of each transformers.

compute_power_flows(self)

Retrieve the active flows (in MW, at the origin of each powerlines / high voltage size of each transformers.

converged_mask(self)

Per-row convergence, as a list[bool] of length nb_steps() (row order matches the row order of whatever was solved: my_defaults() for ContingencyAnalysis, the injection / contingency matrices otherwise).

get_algo_config(self)

Config (eg ScalingPolicyType / damping parameters) of the internal solver used for every step.

get_algo_name(self)

Registry name of the currently selected algorithm.

get_algo_type(self)

Return the type of the solver currently used.

get_flows(self)

Get the current flows (in kA) at the origin side / high voltage side of each transformers / powerlines.

get_power_flows(self)

Get the active flows (in MW) at the origin side / high voltage side of each transformers / powerlines.

get_sbuses(self)

Get the complex power injected at each (solver id) bus of the powergrid.

get_status(self)

Status of the solvers (1: success, 0: failure).

get_voltages(self)

Get the complex voltage angles at each bus of the powergrid.

modify_gen_p(self, gen_p)

Per-step active generator setpoints, shape (n_simul, n_gen).

modify_gen_v(self, gen_v)

Per-step generator target voltage magnitude, shape (n_simul, n_gen), in pu (vm_pu), NOT kV.

modify_load_p(self, load_p)

Per-step active load setpoints, shape (n_simul, n_load).

modify_load_q(self, load_q)

Per-step reactive load setpoints, shape (n_simul, n_load).

modify_sgen_p(self, sgen_p)

Per-step active static generator setpoints, shape (n_simul, n_sgen).

nb_converged(self)

Number of powerflows, among those nb_solved() attempted, that actually converged.

nb_solved(self)

Total number of powerflows solved.

preprocessing_time(self)

Time spent in pre processing the data (this involves, but is not limited to the computation of the Sbus)

set_algo_config(self, config)

See get_algo_config().

solver_time(self)

Total time spent only in solving the powerflows (excluding pre processing the data, post processing them, initializing everything etc.)

thread_init_time(self)

Time (in seconds, float) spent building the per thread solvers.

total_time(self)

Total time spent in solving the powerflows, pre processing the data, post processing them, initializing everything etc.

Attributes:

init_from_n_powerflow

Whether to initialize the complex voltages of the first step with the results of a "n" powerflow (ie a powerflow run at the start of the computation, on the injections of the grid model) or not.

nb_thread

Number of OS threads used to compute the steps (default 1).

amps_computation_time(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) float

Time spent in computing the flows (in amps) after the voltages have been computed at each nodes

It is given in seconds (float).

available_algorithm_names(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) list[str]

Returns the names of all registered algorithms, including any loaded plugins, as a list of string.

available_default_algorithms(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) list[lightsim2grid.lightsim2grid_cpp.AlgorithmType]

Return the list of the names of the algorithm available on the current lightsim2grid installation.

This is a list of lightsim2grid.algorithm.AlgorithmType.

change_algorithm(*args, **kwargs)

Overloaded function.

  1. change_algorithm(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, arg0: lightsim2grid.lightsim2grid_cpp.AlgorithmType) -> None

    This function allows to control which solver is used during the powerflow. See the section Available powerflow algorithms for more information about them.

    See also

    lightsim2grid.algorithm.AlgorithmType for a list of the available algorithms (NB: some algorithms might not be available on all platform)

    Note

    If the algorithm type entered is a DC algorithm (eg from lightsim2grid.algorithm.AlgorithmType, DC_SparseLU, DC_KLU or DC_NICSLU), it will change the _dc_solver otherwise the regular _solver is modified.

    Examples

    from lightsim2grid.algorithm import AlgorithmType
    # init the grid model
    from lightsim2grid.network import init_from_pandapower
    pp_net = ...  # any pandapower grid
    lightsim_grid_model = init_from_pandapower(pp_net)  # some warnings might be issued as well as some warnings
    
    # change the algorithm used for the powerflow
    # to use internally a Newton Raphson algorithm with the Eigen sparse LU linear solver
    lightsim_grid_model.change_algorithm(AlgorithmType.NR_SparseLU)
    
  2. change_algorithm(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, arg0: str) -> None

    Change the AC (or DC) algorithm by registry name. Accepts built-in names and plugin names registered via load_solver_plugin().

    See also

    change_algorithm() to change it by lightsim2grid.algorithm.AlgorithmType instead.

change_solver(*args, **kwargs)

Overloaded function.

  1. change_solver(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, arg0: lightsim2grid.lightsim2grid_cpp.AlgorithmType) -> None

DEPRECATED: use ‘change_algorithm’ instead

  1. change_solver(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, arg0: str) -> None

DEPRECATED: use ‘change_algorithm’ instead

clear(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) None

Clear the solver and to as if the class never performed any powerflow.

close(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) None

Clear the solver and to as if the class never performed any powerflow.

compute(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, Vinit: Annotated[numpy.typing.NDArray[numpy.complex128], '[m, 1]'], max_iter: SupportsInt | SupportsIndex, tol: SupportsFloat | SupportsIndex) None

Run the batch: one powerflow per simulation, using whatever was set by modify_* (and, on ScenarioSweep, set_contingency_lines / set_contingency_trafos). Raises if nothing was ever set.

compute_Vs(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, arg0: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous'], arg1: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous'], arg2: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous'], arg3: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous'], arg4: Annotated[numpy.typing.NDArray[numpy.complex128], '[m, 1]'], arg5: SupportsInt | SupportsIndex, arg6: SupportsFloat | SupportsIndex) int

Compute the voltages (at each bus of the grid model) for some time series of injections (productions, loads, storage units, etc.)

Note

During this computation, the GIL is released, allowing easier parrallel computation

gen_p: numy.ndarray, float

Active generation for each generators. Its counts as many column as the number of generators on the grid and as many rows as the number of steps to compute.

sgen_p: numy.ndarray, float

Active generation for each static generator. Its counts as many column as the number of static generators on the grid and as many rows as the number of steps to compute.

load_p:numy.ndarray, float

Active consumption for each loads. Its counts as many column as the number of loads on the grid and as many rows as the number of steps to compute.

load_q: numy.ndarray, float

Reactive consumption for each loads. Its counts as many column as the number of loads on the grid and as many rows as the number of steps to compute.

Vinit: numy.ndarray, complex

First voltage at each bus of the grid model (including the disconnected buses)

max_iter: int

Total number of iteration (>0 integer)

tol: float

Solver tolerance (> 0. float)

status: int

The status of the computation. 1 means “success”: all powerflows were computed sucessfully, 0 means there were some errors and that the computation stopped after a certain number of steps.

DEPRECATED: prefer modify_gen_p / modify_sgen_p / modify_load_p / modify_load_q + compute() instead.

compute_flows(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.writeable', 'flags.c_contiguous']

Retrieve the flows (in amps, at the origin of each powerlines / high voltage size of each transformers.

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs() has been called.

Note

This function must be called before lightsim2grid.timeSerie.TimeSeriesCPP.get_flows()

Note

During this computation, the GIL is released, allowing easier parrallel computation

compute_power_flows(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.writeable', 'flags.c_contiguous']

Retrieve the active flows (in MW, at the origin of each powerlines / high voltage size of each transformers.

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs() has been called.

Note

This function must be called before lightsim2grid.timeSerie.TimeSeriesCPP.get_flows()

Note

During this computation, the GIL is released, allowing easier parrallel computation

converged_mask(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) list[bool]

Per-row convergence, as a list[bool] of length nb_steps() (row order matches the row order of whatever was solved: my_defaults() for ContingencyAnalysis, the injection / contingency matrices otherwise). Unlike converged() (ContingencyAnalysis-only, and only usable if compute_limit_violations=True was set), this is available on every batch class – TimeSeries, InjectionSweep, ContingencyAnalysis and ScenarioSweep alike – unconditionally, no setup required.

Row i is True iff that row was both invertible and reported convergence by the solver. A row that was never attempted at all (eg every row after the first failure in a TimeSeries, whose steps are chained and so stop there) is False too – indistinguishable from an outright divergence, the same “never attempted” == “did not converge” convention nb_solved() / nb_converged() already use.

get_algo_config(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) lightsim2grid.lightsim2grid_cpp.AlgoConfig

Config (eg ScalingPolicyType / damping parameters) of the internal solver used for every step. Copied once from the grid model’s own get_ac_algo_config() at construction time, then independent of it; re-apply with set_algo_config() if you change the grid model’s config afterwards, or after change_algorithm().

get_algo_name(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) str

Registry name of the currently selected algorithm. Unlike get_algo_type(), stays meaningful for plugin solvers (and built-ins with no dedicated AlgorithmType member).

get_algo_type(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) lightsim2grid.lightsim2grid_cpp.AlgorithmType

Return the type of the solver currently used.

This is equivalent to the get_type of the lightsim2grid.algorithm.AlgorithmSelector.get_type() of the solver used.

get_flows(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]']

Get the current flows (in kA) at the origin side / high voltage side of each transformers / powerlines.

Each rows correspond to a time step, each column to a powerline / transformer

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_flows() has been called. (compute_flows also requires that lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs() has been caleed)

Returns:

As – The flows (in kA) at the origin side / high voltage side of each transformers / powerlines.

Return type:

numpy.ndarry (matrix)

get_power_flows(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]']

Get the active flows (in MW) at the origin side / high voltage side of each transformers / powerlines.

Each rows correspond to a time step, each column to a powerline / transformer

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_power_flows() has been called. (compute_power_flows also requires that lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs() has been caleed)

Returns:

Ps – The active flows (in MW) at the origin side / high voltage side of each transformers / powerlines.

Return type:

numpy.ndarry (matrix)

get_sbuses(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) Annotated[numpy.typing.NDArray[numpy.complex128], '[m, n]', 'flags.c_contiguous']

Get the complex power injected at each (solver id) bus of the powergrid. Results are given in pair unit. We do not recommend to use it as it uses the solver id and NOT the powergrid bus id (you can refer to lightsim2grid.network.LSGrid.id_me_to_ac_solver() and lightsim2grid.network.LSGrid.id_ac_solver_to_me() for more information)

Each rows correspond to a time step, each column to a bus (bus are identified by their solver id !)

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs().

Returns:

Sbuses – The complex power injected at each bus (pair unit, load sign convention)

Return type:

numpy.ndarry (matrix)

get_status(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) int

Status of the solvers (1: success, 0: failure).

Note

Even if the solver failed at some point, some results might still be available (but not totally).

get_voltages(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) Annotated[numpy.typing.NDArray[numpy.complex128], '[m, n]']

Get the complex voltage angles at each bus of the powergrid.

Each rows correspond to a time step, each column to a bus.

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs().

Returns:

Vs – The complex voltage angles at each bus of the powergrid.

Return type:

numpy.ndarry (matrix)

property init_from_n_powerflow

Whether to initialize the complex voltages of the first step with the results of a “n” powerflow (ie a powerflow run at the start of the computation, on the injections of the grid model) or not.

Default: False, meaning the first step is initialized with the input vector given to lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs(). Every other step is initialized with the result of the step before it either way.

modify_gen_p(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, gen_p: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step active generator setpoints, shape (n_simul, n_gen). See the class docstring: locks / checks the number of simulations against any other modify_* / set_contingency_* call already made on this object.

modify_gen_v(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, gen_v: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step generator target voltage magnitude, shape (n_simul, n_gen), in pu (vm_pu), NOT kV. Unlike modify_gen_p/modify_sgen_p/modify_load_p/modify_load_q, this does NOT feed the injection (Sbus) – it only re-seeds |V| at each voltage-regulating generator’s regulated bus before that step’s solve. See modify_gen_p() for the shared row-count-lock behavior.

modify_load_p(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, load_p: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step active load setpoints, shape (n_simul, n_load). See modify_gen_p().

modify_load_q(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, load_q: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step reactive load setpoints, shape (n_simul, n_load). See modify_gen_p().

modify_sgen_p(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, sgen_p: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step active static generator setpoints, shape (n_simul, n_sgen). See modify_gen_p().

nb_converged(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) int

Number of powerflows, among those nb_solved() attempted, that actually converged. Always <= nb_solved(): a row skipped outright (eg a non-invertible / islanding admittance matrix, on ContingencyAnalysis / ScenarioSweep) never reaches the solver at all, so it counts towards neither.

nb_solved(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) int

Total number of powerflows solved.

property nb_thread

Number of OS threads used to compute the steps (default 1).

With nb_thread == 1 the behaviour is the legacy sequential one. With nb_thread > 1 the steps are split into contiguous ranges, each solved by its own thread (each with its own solver), writing to disjoint rows of the result matrix: the results do NOT depend on the number of threads. Values < 1 are clamped to 1.

Warning

This is only available on lightsim2grid.injectionSweep.InjectionSweepCPP (and on lightsim2grid.contingencyAnalysis.ContingencyAnalysisCPP), whose computations are independent of one another.

On lightsim2grid.timeSerie.TimeSeriesCPP each step is initialized with the result of the previous one, so the steps cannot be split over threads without making the results depend on how they were split: setting it to anything but 1 raises a RuntimeError. Use InjectionSweepCPP if you want the very same injections computed in parallel.

preprocessing_time(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) float

Time spent in pre processing the data (this involves, but is not limited to the computation of the Sbus)

It is given in seconds (float).

set_algo_config(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP, config: lightsim2grid.lightsim2grid_cpp.AlgoConfig) None

See get_algo_config().

solver_time(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) float

Total time spent only in solving the powerflows (excluding pre processing the data, post processing them, initializing everything etc.)

It is given in seconds (float).

thread_init_time(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) float

Time (in seconds, float) spent building the per thread solvers. It is 0. when nb_thread is 1, in which case the (already initialized) internal solver is reused.

total_time(self: lightsim2grid.lightsim2grid_cpp.TimeSeriesCPP) float

Total time spent in solving the powerflows, pre processing the data, post processing them, initializing everything etc.

It is given in seconds (float).

Classes:

InjectionSweep(grid2op_env)

Same computation as lightsim2grid.timeSerie.TimeSerie -- a fixed grid topology, one powerflow per set of injections -- but every powerflow starts from the same voltage instead of from the result of the previous one.

InjectionSweepCPP

Allows the computation of many powerflows on the same grid topology while the active / reactive power injected at each bus vary: exactly the same inputs, the same results and the same interface as lightsim2grid.timeSerie.TimeSeriesCPP.

class lightsim2grid.injectionSweep.InjectionSweep(grid2op_env)[source]

Same computation as lightsim2grid.timeSerie.TimeSerie – a fixed grid topology, one powerflow per set of injections – but every powerflow starts from the same voltage instead of from the result of the previous one.

That makes the steps independent of one another: the result of a step does not depend on the steps computed before it, nor on the order in which they were given. Use this class when the “steps” are unrelated scenarios rather than consecutive instants of a time series. Two practical consequences:

  • the computation can be spread over several OS threads, see nb_thread below (lightsim2grid.timeSerie.TimeSerie cannot: splitting a chained computation would make its results depend on how it was split);

  • a step that is far from its neighbours does not inherit a bad starting point from them – but a step that IS close to its neighbours no longer benefits from their solution, so a genuine time series usually converges in fewer iterations with lightsim2grid.timeSerie.TimeSerie.

Examples

It is used exactly like lightsim2grid.timeSerie.TimeSerie:

import grid2op
from lightsim2grid import InjectionSweep
from lightsim2grid import LightSimBackend

env_name = ...
env = grid2op.make(env_name, backend=LightSimBackend())

sweep = InjectionSweep(env)
sweep.nb_thread = 4  # optional, the results do not depend on it
res_p, res_a, res_v = sweep.get_flows(scenario_id=..., seed=...)

Attributes:

init_from_n_powerflow

Whether to initialize the complex voltages of each step of the batch with the results of a "n" powerflow (a powerflow at the current state of the grid) instead of the vector given to compute_V.

nb_thread

1).

property init_from_n_powerflow

Whether to initialize the complex voltages of each step of the batch with the results of a “n” powerflow (a powerflow at the current state of the grid) instead of the vector given to compute_V. Default: False.

Unlike lightsim2grid.timeSerie.TimeSerie.init_from_n_powerflow, this applies to every step and not only the first one – here every step starts from that same voltage. Must be set before the computation actually runs.

property nb_thread

1).

The steps are split into contiguous ranges, each solved by its own thread with its own solver, writing to disjoint rows of the result matrix: the results do not depend on the number of threads. Values < 1 are clamped to 1.

Must be set before the computation actually runs (eg before compute_V is called); it has no effect on a batch that has already been computed.

Type:

Number of OS threads used to compute the steps (default

class lightsim2grid.injectionSweep.InjectionSweepCPP

Allows the computation of many powerflows on the same grid topology while the active / reactive power injected at each bus vary: exactly the same inputs, the same results and the same interface as lightsim2grid.timeSerie.TimeSeriesCPP.

The difference is how each computation is initialized. TimeSeriesCPP initializes a step with the solution of the step before it (they are consecutive instants of a time series, so they are expected to be close to one another). InjectionSweepCPP initializes every step with the very same voltage - the one you provide, or the result of the “n” powerflow if init_from_n_powerflow is set - exactly like lightsim2grid.contingencyAnalysis.ContingencyAnalysisCPP does for each of its contingencies.

Use it when the “steps” are independent scenarios rather than consecutive instants. The result of a step then does not depend on the steps computed before it, nor on the order in which they are given, and the computation can be spread over several threads (see nb_thread, which TimeSeriesCPP cannot support).

This is a “raw” c++ class, for an easier to use interface, please refer to the python documentation of the lightsim2grid.injectionSweep.InjectionSweep class.

Methods:

amps_computation_time(self)

Time spent in computing the flows (in amps) after the voltages have been computed at each nodes

available_algorithm_names(self)

Returns the names of all registered algorithms, including any loaded plugins, as a list of string.

available_default_algorithms(self)

Return the list of the names of the algorithm available on the current lightsim2grid installation.

change_algorithm(*args, **kwargs)

Overloaded function.

change_solver(*args, **kwargs)

Overloaded function.

clear(self)

Clear the solver and to as if the class never performed any powerflow.

close(self)

Clear the solver and to as if the class never performed any powerflow.

compute(self, Vinit, max_iter, tol)

Run the batch: one powerflow per simulation, using whatever was set by modify_* (and, on ScenarioSweep, set_contingency_lines / set_contingency_trafos).

compute_Vs(self, arg0, arg1, arg2, arg3, ...)

Compute the voltages (at each bus of the grid model) for some time series of injections (productions, loads, storage units, etc.)

compute_flows(self)

Retrieve the flows (in amps, at the origin of each powerlines / high voltage size of each transformers.

compute_power_flows(self)

Retrieve the active flows (in MW, at the origin of each powerlines / high voltage size of each transformers.

converged_mask(self)

Per-row convergence, as a list[bool] of length nb_steps() (row order matches the row order of whatever was solved: my_defaults() for ContingencyAnalysis, the injection / contingency matrices otherwise).

get_algo_config(self)

Config (eg ScalingPolicyType / damping parameters) of the internal solver used for every step.

get_algo_name(self)

Registry name of the currently selected algorithm.

get_algo_type(self)

Return the type of the solver currently used.

get_flows(self)

Get the current flows (in kA) at the origin side / high voltage side of each transformers / powerlines.

get_power_flows(self)

Get the active flows (in MW) at the origin side / high voltage side of each transformers / powerlines.

get_sbuses(self)

Get the complex power injected at each (solver id) bus of the powergrid.

get_status(self)

Status of the solvers (1: success, 0: failure).

get_voltages(self)

Get the complex voltage angles at each bus of the powergrid.

modify_gen_p(self, gen_p)

Per-step active generator setpoints, shape (n_simul, n_gen).

modify_gen_v(self, gen_v)

Per-step generator target voltage magnitude, shape (n_simul, n_gen), in pu (vm_pu), NOT kV.

modify_load_p(self, load_p)

Per-step active load setpoints, shape (n_simul, n_load).

modify_load_q(self, load_q)

Per-step reactive load setpoints, shape (n_simul, n_load).

modify_sgen_p(self, sgen_p)

Per-step active static generator setpoints, shape (n_simul, n_sgen).

nb_converged(self)

Number of powerflows, among those nb_solved() attempted, that actually converged.

nb_solved(self)

Total number of powerflows solved.

preprocessing_time(self)

Time spent in pre processing the data (this involves, but is not limited to the computation of the Sbus)

set_algo_config(self, config)

See get_algo_config().

solver_time(self)

Total time spent only in solving the powerflows (excluding pre processing the data, post processing them, initializing everything etc.)

thread_init_time(self)

Time (in seconds, float) spent building the per thread solvers.

total_time(self)

Total time spent in solving the powerflows, pre processing the data, post processing them, initializing everything etc.

Attributes:

init_from_n_powerflow

Whether to initialize the complex voltages of each step with the results of a "n" powerflow (ie a powerflow run at the start of the computation, on the injections of the grid model) or not.

nb_thread

Number of OS threads used to compute the steps (default 1).

amps_computation_time(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) float

Time spent in computing the flows (in amps) after the voltages have been computed at each nodes

It is given in seconds (float).

available_algorithm_names(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) list[str]

Returns the names of all registered algorithms, including any loaded plugins, as a list of string.

available_default_algorithms(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) list[lightsim2grid.lightsim2grid_cpp.AlgorithmType]

Return the list of the names of the algorithm available on the current lightsim2grid installation.

This is a list of lightsim2grid.algorithm.AlgorithmType.

change_algorithm(*args, **kwargs)

Overloaded function.

  1. change_algorithm(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, arg0: lightsim2grid.lightsim2grid_cpp.AlgorithmType) -> None

    This function allows to control which solver is used during the powerflow. See the section Available powerflow algorithms for more information about them.

    See also

    lightsim2grid.algorithm.AlgorithmType for a list of the available algorithms (NB: some algorithms might not be available on all platform)

    Note

    If the algorithm type entered is a DC algorithm (eg from lightsim2grid.algorithm.AlgorithmType, DC_SparseLU, DC_KLU or DC_NICSLU), it will change the _dc_solver otherwise the regular _solver is modified.

    Examples

    from lightsim2grid.algorithm import AlgorithmType
    # init the grid model
    from lightsim2grid.network import init_from_pandapower
    pp_net = ...  # any pandapower grid
    lightsim_grid_model = init_from_pandapower(pp_net)  # some warnings might be issued as well as some warnings
    
    # change the algorithm used for the powerflow
    # to use internally a Newton Raphson algorithm with the Eigen sparse LU linear solver
    lightsim_grid_model.change_algorithm(AlgorithmType.NR_SparseLU)
    
  2. change_algorithm(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, arg0: str) -> None

    Change the AC (or DC) algorithm by registry name. Accepts built-in names and plugin names registered via load_solver_plugin().

    See also

    change_algorithm() to change it by lightsim2grid.algorithm.AlgorithmType instead.

change_solver(*args, **kwargs)

Overloaded function.

  1. change_solver(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, arg0: lightsim2grid.lightsim2grid_cpp.AlgorithmType) -> None

DEPRECATED: use ‘change_algorithm’ instead

  1. change_solver(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, arg0: str) -> None

DEPRECATED: use ‘change_algorithm’ instead

clear(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) None

Clear the solver and to as if the class never performed any powerflow.

close(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) None

Clear the solver and to as if the class never performed any powerflow.

compute(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, Vinit: Annotated[numpy.typing.NDArray[numpy.complex128], '[m, 1]'], max_iter: SupportsInt | SupportsIndex, tol: SupportsFloat | SupportsIndex) None

Run the batch: one powerflow per simulation, using whatever was set by modify_* (and, on ScenarioSweep, set_contingency_lines / set_contingency_trafos). Raises if nothing was ever set.

compute_Vs(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, arg0: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous'], arg1: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous'], arg2: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous'], arg3: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous'], arg4: Annotated[numpy.typing.NDArray[numpy.complex128], '[m, 1]'], arg5: SupportsInt | SupportsIndex, arg6: SupportsFloat | SupportsIndex) int

Compute the voltages (at each bus of the grid model) for some time series of injections (productions, loads, storage units, etc.)

Note

During this computation, the GIL is released, allowing easier parrallel computation

gen_p: numy.ndarray, float

Active generation for each generators. Its counts as many column as the number of generators on the grid and as many rows as the number of steps to compute.

sgen_p: numy.ndarray, float

Active generation for each static generator. Its counts as many column as the number of static generators on the grid and as many rows as the number of steps to compute.

load_p:numy.ndarray, float

Active consumption for each loads. Its counts as many column as the number of loads on the grid and as many rows as the number of steps to compute.

load_q: numy.ndarray, float

Reactive consumption for each loads. Its counts as many column as the number of loads on the grid and as many rows as the number of steps to compute.

Vinit: numy.ndarray, complex

First voltage at each bus of the grid model (including the disconnected buses)

max_iter: int

Total number of iteration (>0 integer)

tol: float

Solver tolerance (> 0. float)

status: int

The status of the computation. 1 means “success”: all powerflows were computed sucessfully, 0 means there were some errors and that the computation stopped after a certain number of steps.

DEPRECATED: prefer modify_gen_p / modify_sgen_p / modify_load_p / modify_load_q + compute() instead.

compute_flows(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.writeable', 'flags.c_contiguous']

Retrieve the flows (in amps, at the origin of each powerlines / high voltage size of each transformers.

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs() has been called.

Note

This function must be called before lightsim2grid.timeSerie.TimeSeriesCPP.get_flows()

Note

During this computation, the GIL is released, allowing easier parrallel computation

compute_power_flows(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.writeable', 'flags.c_contiguous']

Retrieve the active flows (in MW, at the origin of each powerlines / high voltage size of each transformers.

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs() has been called.

Note

This function must be called before lightsim2grid.timeSerie.TimeSeriesCPP.get_flows()

Note

During this computation, the GIL is released, allowing easier parrallel computation

converged_mask(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) list[bool]

Per-row convergence, as a list[bool] of length nb_steps() (row order matches the row order of whatever was solved: my_defaults() for ContingencyAnalysis, the injection / contingency matrices otherwise). Unlike converged() (ContingencyAnalysis-only, and only usable if compute_limit_violations=True was set), this is available on every batch class – TimeSeries, InjectionSweep, ContingencyAnalysis and ScenarioSweep alike – unconditionally, no setup required.

Row i is True iff that row was both invertible and reported convergence by the solver. A row that was never attempted at all (eg every row after the first failure in a TimeSeries, whose steps are chained and so stop there) is False too – indistinguishable from an outright divergence, the same “never attempted” == “did not converge” convention nb_solved() / nb_converged() already use.

get_algo_config(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) lightsim2grid.lightsim2grid_cpp.AlgoConfig

Config (eg ScalingPolicyType / damping parameters) of the internal solver used for every step. Copied once from the grid model’s own get_ac_algo_config() at construction time, then independent of it; re-apply with set_algo_config() if you change the grid model’s config afterwards, or after change_algorithm().

get_algo_name(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) str

Registry name of the currently selected algorithm. Unlike get_algo_type(), stays meaningful for plugin solvers (and built-ins with no dedicated AlgorithmType member).

get_algo_type(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) lightsim2grid.lightsim2grid_cpp.AlgorithmType

Return the type of the solver currently used.

This is equivalent to the get_type of the lightsim2grid.algorithm.AlgorithmSelector.get_type() of the solver used.

get_flows(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]']

Get the current flows (in kA) at the origin side / high voltage side of each transformers / powerlines.

Each rows correspond to a time step, each column to a powerline / transformer

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_flows() has been called. (compute_flows also requires that lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs() has been caleed)

Returns:

As – The flows (in kA) at the origin side / high voltage side of each transformers / powerlines.

Return type:

numpy.ndarry (matrix)

get_power_flows(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]']

Get the active flows (in MW) at the origin side / high voltage side of each transformers / powerlines.

Each rows correspond to a time step, each column to a powerline / transformer

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_power_flows() has been called. (compute_power_flows also requires that lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs() has been caleed)

Returns:

Ps – The active flows (in MW) at the origin side / high voltage side of each transformers / powerlines.

Return type:

numpy.ndarry (matrix)

get_sbuses(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) Annotated[numpy.typing.NDArray[numpy.complex128], '[m, n]', 'flags.c_contiguous']

Get the complex power injected at each (solver id) bus of the powergrid. Results are given in pair unit. We do not recommend to use it as it uses the solver id and NOT the powergrid bus id (you can refer to lightsim2grid.network.LSGrid.id_me_to_ac_solver() and lightsim2grid.network.LSGrid.id_ac_solver_to_me() for more information)

Each rows correspond to a time step, each column to a bus (bus are identified by their solver id !)

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs().

Returns:

Sbuses – The complex power injected at each bus (pair unit, load sign convention)

Return type:

numpy.ndarry (matrix)

get_status(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) int

Status of the solvers (1: success, 0: failure).

Note

Even if the solver failed at some point, some results might still be available (but not totally).

get_voltages(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) Annotated[numpy.typing.NDArray[numpy.complex128], '[m, n]']

Get the complex voltage angles at each bus of the powergrid.

Each rows correspond to a time step, each column to a bus.

Warning

This function must be called after lightsim2grid.timeSerie.TimeSeriesCPP.compute_Vs().

Returns:

Vs – The complex voltage angles at each bus of the powergrid.

Return type:

numpy.ndarry (matrix)

property init_from_n_powerflow

Whether to initialize the complex voltages of each step with the results of a “n” powerflow (ie a powerflow run at the start of the computation, on the injections of the grid model) or not.

Default: False, meaning every step is initialized with the input vector given to lightsim2grid.injectionSweep.InjectionSweepCPP.compute_Vs().

modify_gen_p(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, gen_p: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step active generator setpoints, shape (n_simul, n_gen). See the class docstring: locks / checks the number of simulations against any other modify_* / set_contingency_* call already made on this object.

modify_gen_v(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, gen_v: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step generator target voltage magnitude, shape (n_simul, n_gen), in pu (vm_pu), NOT kV. Unlike modify_gen_p/modify_sgen_p/modify_load_p/modify_load_q, this does NOT feed the injection (Sbus) – it only re-seeds |V| at each voltage-regulating generator’s regulated bus before that step’s solve. See modify_gen_p() for the shared row-count-lock behavior.

modify_load_p(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, load_p: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step active load setpoints, shape (n_simul, n_load). See modify_gen_p().

modify_load_q(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, load_q: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step reactive load setpoints, shape (n_simul, n_load). See modify_gen_p().

modify_sgen_p(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, sgen_p: Annotated[numpy.typing.NDArray[numpy.float64], '[m, n]', 'flags.c_contiguous']) None

Per-step active static generator setpoints, shape (n_simul, n_sgen). See modify_gen_p().

nb_converged(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) int

Number of powerflows, among those nb_solved() attempted, that actually converged. Always <= nb_solved(): a row skipped outright (eg a non-invertible / islanding admittance matrix, on ContingencyAnalysis / ScenarioSweep) never reaches the solver at all, so it counts towards neither.

nb_solved(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) int

Total number of powerflows solved.

property nb_thread

Number of OS threads used to compute the steps (default 1).

With nb_thread == 1 the behaviour is the legacy sequential one. With nb_thread > 1 the steps are split into contiguous ranges, each solved by its own thread (each with its own solver), writing to disjoint rows of the result matrix: the results do NOT depend on the number of threads. Values < 1 are clamped to 1.

Warning

This is only available on lightsim2grid.injectionSweep.InjectionSweepCPP (and on lightsim2grid.contingencyAnalysis.ContingencyAnalysisCPP), whose computations are independent of one another.

On lightsim2grid.timeSerie.TimeSeriesCPP each step is initialized with the result of the previous one, so the steps cannot be split over threads without making the results depend on how they were split: setting it to anything but 1 raises a RuntimeError. Use InjectionSweepCPP if you want the very same injections computed in parallel.

preprocessing_time(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) float

Time spent in pre processing the data (this involves, but is not limited to the computation of the Sbus)

It is given in seconds (float).

set_algo_config(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP, config: lightsim2grid.lightsim2grid_cpp.AlgoConfig) None

See get_algo_config().

solver_time(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) float

Total time spent only in solving the powerflows (excluding pre processing the data, post processing them, initializing everything etc.)

It is given in seconds (float).

thread_init_time(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) float

Time (in seconds, float) spent building the per thread solvers. It is 0. when nb_thread is 1, in which case the (already initialized) internal solver is reused.

total_time(self: lightsim2grid.lightsim2grid_cpp.InjectionSweepCPP) float

Total time spent in solving the powerflows, pre processing the data, post processing them, initializing everything etc.

It is given in seconds (float).