Skip to content

Release 1.1.0

Choose a tag to compare

@BDonnot BDonnot released this 22 Sep 15:07
· 191 commits to master since this release
dec6adc

lightsim2grid 1.1.0

This release adds a continuation powerflow and differentiable batch powerflows (pytorch), and
models storage units and active power limits more fully. It also detects results that are
physically unreachable and makes the solvers faster across the board. Many correctness fixes
come with it, mostly around voltage control, the distributed slack and the solver cache.

Highlights

  • Continuation powerflow: ContinuationPowerFlow / run_cpf traces the PV curve from the
    current operating point to a target state and stops at the voltage-collapse nose. Its options
    follow MATPOWER's cpf.*. load_steering / gen_steering choose which elements move, and
    ContinuationSweepCPP computes the whole curve for a single symbolic factorization.
  • Differentiable powerflows: lightsim2grid.differentiable.BatchCPUPowerFlow turns a batch
    of powerflows into a pytorch operation. It is differentiable with respect to injections and
    generator voltage setpoints, through the adjoint of the batch (keep_jacobian / solve_JT,
    with solve_transpose on the linear solvers). Install it with
    pip install lightsim2grid[torch]; torch remains optional.
  • Physical violations: compute_physical_violations on every batch algorithm and on its
    python wrapper reports solutions that no real grid can reach. These are a bus needing more
    reactive power than its machines can provide, an hvdc line past its maximum power, or a
    generator or battery pushed past its P limits by the distributed slack. Every
    LimitViolation now carries a ViolationCategory: OPERATIONAL, PHYSICAL or SOLVER.
  • Storage units: batteries can take part in the distributed slack and regulate their bus
    voltage, and they have optional active power limits. init_from_pypowsybl reads all three
    the way OpenLoadFlow does.
  • Generators: optional active power limits (set_gen_p_limits), read by every loader that
    provides them, and a reactive sharing key (set_gen_reactive_key) for generators that
    regulate the same bus.
  • MATPOWER in grid2op: with LightSimBackend(loader_method="matpower"), a grid2op
    environment can ship a MATPOWER case as its powergrid.
  • Generator contingencies: ScenarioSweep.set_contingency_gens(mask) drops a generator for
    a given step. It re-shares the distributed slack and switches the bus PV -> PQ when needed,
    without any extra symbolic factorization.

Breaking changes

  • BINARY_FORMAT_VERSION goes from 4 to 10. Bus connectivity is no longer stored (it is
    recounted from the elements), and generators and storage units carry new state. Files saved
    with an earlier version no longer load.
  • A grid where several elements regulate the same bus with different voltage setpoints is
    now refused. Before, the last setpoint written silently won.
  • LSGrid.deactivate_bus / reactivate_bus are now deprecated no-ops. A bus is in the
    powerflow if and only if an active element sits on it, so to remove a bus, disconne
    elements.
  • LSGrid.get_bus_status() returns a new list (a copy) instead of a reference to an
    vector.
  • (C++) Everything a solver family caches now lives in one SolverSideCache. This ch
    memory layout of LSGrid, so code that uses an LSGrid across a module boundary (
    example gpusim2grid) must be rebuilt.

Fixes

Correctness of the powerflow

  • The fast-decoupled algorithms kept the distributed slack at its initial guess. It i
    re-solved at every iteration, and FDPF lands on the Newton-Raphson answer.
  • Fast-decoupled and Gauss-Seidel solved grids with remote or shared voltage control,
    voltage-mode SVCs, to a wrong answer. ac_pf now refuses such grids for these algorithms.
  • The active power of generators sharing the distributed slack with batteries was ove
  • The reactive power published for elements sharing a bus could be NaN or counted twice.
  • The Jacobian of hvdc angle droop now includes the derivative of the dc-line losses.
    solution was already right; the batch gradients were not.
  • A converged Newton-Raphson now always reports angles in [-pi, pi].
  • A slack bus held by a voltage-regulating storage unit is no longer given a free voltage
    magnitude.

Solver cache and grid state

  • A non-converged powerflow keeps its last iterate readable (max_iter=0 used to ret
    nothing).
  • A powerflow that throws part-way no longer leaves a cache that claims to be up to d
  • unset_changes() called with a modification that was never solved no longer makes the next
    powerflow solve the old grid.
  • consider_only_main_component() and update_topo() now report every bus leaving t
    This fixes grid2op's automatically_disconnect=True.
  • Bus connectivity no longer drifts from the elements: after a refused change_bus,
    holding only an SVC, and when read before the first powerflow.
  • Opening one end of a phase-shifting transformer now invalidates the DC injections.

Batch algorithms

  • ContingencyAnalysisCPP gains, in python, everything the other batch classes gained since
    1.0.0.
  • In handle_disconnected_grid mode, a contingency that strands a lone remote voltage
    regulator is no longer reported as diverged.
  • Batch modify_gen_v now applies to generators that regulate a remote bus.
  • Stale results after registering a new contingency no longer make get_flows() rais
  • The batch algorithms no longer write into the grid's own cache or reset its solver.

OpenLoadFlow comparison (bake_outer_loops / remove_outer_loops)

  • Closer agreement with OpenLoadFlow on voltage control, reactive limits (clamping an
    capability curve extrapolation), shared controls, and the default distributed slack.
  • remove_outer_loops no longer depends on the process's hash seed.

Misc

  • Selecting a fast-decoupled algorithm by name no longer fails on the first powerflow
  • The KLU / NICSLU / CKTSO headers can be included twice in a translation unit.

Performance

Answers are identical to 1.0.0 (bit for bit in most cases), and a large share of the
powerflow has been removed:

  • Batch algorithms: the base case (grid reading, connectivity, the "n" powerflow and the
    Jacobian analysis) is kept between compute() calls, per-thread algorithms are kep
    a single graph search settles every N-1 contingency. The row loop is also leaner.
  • Newton-Raphson: cheaper Jacobian assembly and sparsity build, fewer allocations
    iteration, and a better initial distributed slack.
  • Other algorithms: fast-decoupled is noticeably faster, Gauss-Seidel is much fas
    building the DC matrix is faster.
  • Results: branch flows, currents and shunts are computed faster.
  • Grid side: the voltage-control plan is built once per powerflow, bus connectivi
    tracked incrementally, and the checks on ids the library produced itself are debug-only.
    Every check on user input is kept.
  • -march=native builds now also compile KLU and SuiteSparse with it.

Other additions

  • Three cache levels on the batch algorithms: clear_grid_results(), clear_batch_in and clear_batch_outputs()`.
  • get_linear_solver_stats() on the batch classes, to check that a sweep really reuses its
    factorization. set_refactor_fallback on every linear solver.
  • Every loader now records which substation each element belongs to. A MATPOWER case
    BASE_KV is 0 everywhere is now accepted.
  • (C++) VoltageControlPlan, BaseAlgo::get_bus_mismatch(), new AlgoControl flags
    shared interface for the element containers (BranchContainer, `VoltageSourceConta
    ...).
  • New benchmarks (benchmarks/cache_profiling/, make_exotic_grid.cpp) and new C++

The full list is in CHANGELOG.rst.