Skip to content

Commit

Permalink
Merge pull request #98 from Grid2op/bd-dev
Browse files Browse the repository at this point in the history
Ready for version 0.10.0
BDonnot authored Dec 17, 2024
2 parents 919994d + 4376087 commit 46e04b9
Showing 12 changed files with 513 additions and 35 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -26,6 +26,23 @@ TODO: in `main.cpp` check the returned policy of pybind11 and also the `py::call
TODO: a cpp class that is able to compute (DC powerflow) ContingencyAnalysis and TimeSeries using PTDF and LODF
TODO: integration test with pandapower (see `pandapower/contingency/contingency.py` and import `lightsim2grid_installed` and check it's True)

[0.10.0] 2024-12-17
-------------------
- [BREAKING] disconnected storage now raises errors if some power is produced / absorbed, when using legacy grid2op version,
you can retrieve the previous behaviour by initializing the `LightSimBackend` with
`backend = LightSimBackend(..., stop_if_storage_disco=False)`
- [BREAKING] with the new `detachment_is_allowed` feature in grid2op, the kwargs `stop_if_load_disco`,
`stop_if_gen_disco` (and `stop_if_storage_disco`) are now optional. They are set up from the
call to `grid2op.make(...)` and are erased by the `allow_detachment` kwargs. In other words,
you don't need to set `stop_if_load_disco`, `stop_if_gen_disco` or `stop_if_storage_disco`. It is
automatically set by `grid2op.make(..., allow_detachment=XXX)` to have the correct bahaviour.
- [FIXED] an issue with the storage units (when asking it to produce / consume
but deactivating them with the same action the grid did not diverge)
- [IMPROVED] add the grid2op "detachement" support (loads and generators are allowed
to be disconnected from the grid)
- [ADDED] a kwargs `stop_if_storage_disco` to control (in legacy grid2op version) the behaviour
of the backend when a storage unit is disconnected.

[0.9.2.post2] 2024-11-29
--------------------------
- [FIXED] The attribute `can_output_theta` (of base `Backend` class)
109 changes: 109 additions & 0 deletions basic_perf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import warnings
import pandapower as pp
import numpy as np
from grid2op import make, Parameters
from grid2op.Chronics import FromNPY
from lightsim2grid import LightSimBackend
import tempfile
import os

try:
from tabulate import tabulate
TABULATE_AVAIL = True
except ImportError:
print("The tabulate package is not installed. Some output might not work properly")
TABULATE_AVAIL = False


case_names = [
# "case14.json",
"case118.json",
# "case_illinois200.json",
# "case300.json",
# "case1354pegase.json",
"case1888rte.json",
# "GBnetwork.json", # 2224 buses
"case2848rte.json",
# "case2869pegase.json",
# "case3120sp.json",
"case6495rte.json",
"case6515rte.json",
"case9241pegase.json"
]

case_name = "case6495rte.json"
case_name = "case14.json"

def make_grid2op_env(pp_case, case_name, load_p, load_q, gen_p, sgen_p):
param = Parameters.Parameters()
param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True})

with warnings.catch_warnings():
warnings.filterwarnings("ignore")
env_lightsim = make("blank",
param=param,
test=True,
backend=LightSimBackend(),
chronics_class=FromNPY,
data_feeding_kwargs={"load_p": load_p,
"load_q": load_q,
"prod_p": gen_p
},
grid_path=case_name,
_add_to_name=f"{case_name}",
)
return env_lightsim

if __name__ == "__main__":

import pandapower.networks as pn
for case_name in case_names:
tmp_nm = os.path.splitext(case_name)[0]
print(f"====================== {tmp_nm} ======================")
case = getattr(pn,tmp_nm)()
pp.runpp(case) # for slack

load_p_init = 1.0 * case.load["p_mw"].values
load_q_init = 1.0 * case.load["q_mvar"].values
gen_p_init = 1.0 * case.gen["p_mw"].values
sgen_p_init = 1.0 * case.sgen["p_mw"].values

nb_ts = 1
# add slack !
slack_gens = np.zeros((nb_ts, case.ext_grid.shape[0]))
if "res_ext_grid" in case:
slack_gens += np.tile(case.res_ext_grid["p_mw"].values.reshape(1,-1), (nb_ts, 1))
gen_p_g2op = np.concatenate((gen_p_init.reshape(1,-1), slack_gens), axis=1)

with tempfile.TemporaryDirectory() as tmpdirname:
pp.to_json(case, os.path.join(tmpdirname, case_name))
with open(os.path.join(tmpdirname, "config.py"), "w") as f:
f.write("config = {}")

env = make_grid2op_env(None,
os.path.join(tmpdirname, case_name),
load_p=load_p_init.reshape(1,-1),
load_q=load_q_init.reshape(1,-1),
gen_p=gen_p_g2op.reshape(1,-1),
sgen_p=None)

env.backend._grid.tell_solver_need_reset()
_ = env.step(env.action_space())
ls_solver = env.backend._grid.get_solver()
nb_iter_solver = ls_solver.get_nb_iter()
timers = ls_solver.get_timers_jacobian()
(timer_Fx, timer_solve, timer_init, timer_check,
timer_compute_dS, timer_fillJ, timer_compVa_Vm, timer_preproc, timer_total) = timers
print(f"Total time for the powerflow (=pre proc + NR + post proc): {env.backend._grid.timer_last_ac_pf:.2e}s")
print(f"Total time spent in the Newton Raphson: {timer_total:.2e}s")
print(f"Time to pre process input data: {timer_preproc:.2e}s")
print(f"Time to intialize linear solver: {timer_init:.2e}s")
print(f"Then for all iterations (cumulated time over all {nb_iter_solver} iterations)")
print(f"\ttotal time to compute dS/dVm and dS/dVa: {timer_compute_dS:.2e}s")
print(f"\ttotal time fill jacobian matrix (from dS/dVm and dS/dVa): {timer_fillJ:.2e}s")
print(f"\ttotal time to solve J.x = b: {timer_solve:.2e}s")
print(f"\ttotal time to compute V, Va and Vm: {timer_compVa_Vm:.2e}s")
print(f"\ttotal time to compute p, q mismatch at buses: {timer_Fx:.2e}s")
print(f"\ttotal time to check if p,q mismatch at buses are within tolerance: {timer_check:.2e}s")
print(f"====================== {' '*len(tmp_nm)} ======================")

192 changes: 192 additions & 0 deletions benchmarks/benchmark_gauss_seidel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# Copyright (c) 2024, RTE (https://www.rte-france.com)
# See AUTHORS.txt
# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
# you can obtain one at http://mozilla.org/MPL/2.0/.
# SPDX-License-Identifier: MPL-2.0
# This file is part of LightSim2grid, LightSim2grid a implements a c++ backend targeting the Grid2Op platform.

import warnings
import copy
import pandapower as pp
import numpy as np
import hashlib
from scipy.interpolate import interp1d
import matplotlib.pyplot as plt
from grid2op import make, Parameters
from grid2op.Chronics import FromNPY, ChangeNothing
from grid2op.Backend import PandaPowerBackend
from grid2op.Exceptions import Grid2OpException
import lightsim2grid
from lightsim2grid import LightSimBackend
from benchmark_grid_size import (get_loads_gens,
make_grid2op_env_pp,
run_grid2op_env,
make_grid2op_env)
from benchmark_solvers import solver_gs, solver_names, order_solver_print

from tqdm import tqdm
import os
from utils_benchmark import print_configuration, get_env_name_displayed
from benchmark_solvers import solver_names

try:
from tabulate import tabulate
TABULATE_AVAIL = True
except ImportError:
print("The tabulate package is not installed. Some output might not work properly")
TABULATE_AVAIL = False

VERBOSE = False
MAKE_PLOT = False
WITH_PP = False
DEBUG = False

case_names = [
"case14.json",
"case118.json",
"case_illinois200.json",
"case300.json",
"case1354pegase.json",
"case1888rte.json",
# # "GBnetwork.json", # 2224 buses
# "case2848rte.json",
# "case2869pegase.json",
# "case3120sp.json",
# "case6495rte.json",
# "case6515rte.json",
# "case9241pegase.json"
]


if __name__ == "__main__":
prng = np.random.default_rng(42)
case_names_displayed = [get_env_name_displayed(el) for el in case_names]
nb_iters = []
ts_sizes = []
errors = {}
for case_name in tqdm(case_names):

if not os.path.exists(case_name):
import pandapower.networks as pn
case = getattr(pn, os.path.splitext(case_name)[0])()
pp.to_json(case, case_name)

# load the case file
case = pp.from_json(case_name)
ts_sizes.append(case.bus.shape[0])
pp.runpp(case) # for slack

# create the env
param = Parameters.Parameters()
param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True})

with warnings.catch_warnings():
warnings.filterwarnings("ignore")
env_pp = make("blank",
param=param, test=True,
backend=PandaPowerBackend(lightsim2grid=False),
chronics_class=ChangeNothing,
grid_path=case_name,
_add_to_name=f"{case_name}",
)
env_ls = make("blank",
param=param, test=True,
backend=LightSimBackend(),
chronics_class=ChangeNothing,
grid_path=case_name,
_add_to_name=f"{case_name}",
)
env_ls.backend.set_solver_type(lightsim2grid.SolverType.GaussSeidel)
all_iters = [1, 3, 10, 30, 100, 300, 1_000, 3_000,
10_000, 30_000,
100_000, 300_000
]
iters = []
errors_p = []
errors_q = []
for max_iter in all_iters:
env_ls.backend.set_solver_max_iter(max_iter)
env_ls.backend._grid.tell_solver_need_reset()
conv = True
try:
obs = env_ls.reset()
except Grid2OpException as exc_:
conv = False
iters.append(env_ls.backend._grid.get_solver().get_nb_iter())
v_tmp = env_ls.backend._grid.get_solver().get_V()
res_tmp = env_ls.backend._grid.check_solution(v_tmp, False)
error_p = 1. * np.abs(res_tmp.real).max()
error_q = 1. * np.abs(res_tmp.imag).max()
errors_p.append(error_p)
errors_q.append(error_q)
if conv:
break
if conv:
nb_iters.append(iters[-1])
else:
nb_iters.append(None)

errors[case.bus.shape[0]] = (errors_p, errors_q)

print("Configuration:")
print_configuration()
print(f"Solver used for linear algebra: {lightsim2grid.SolverType.GaussSeidel}")
print()
hds = ["grid size (nb bus)", "gauss seidel max iter"]
tab = []
for sz, nb_it in zip(ts_sizes, nb_iters):
tab.append([sz, nb_it])

if TABULATE_AVAIL:
res_use_with_grid2op_2 = tabulate(tab, headers=hds, tablefmt="rst")
print(res_use_with_grid2op_2)
else:
print(tab)

print(errors[118][0])
print(errors[118][1])
import pickle
with open("res_gauss_seidel.pickle", "wb") as f:
pickle.dump(errors, file=f)
with open("res_gauss_seidel_nb_iters.pickle", "wb") as f:
pickle.dump(nb_iters, file=f)
print()
print()


# total computation time : 1h27min16s
# Configuration:

# - date: 2024-12-02 18:46 CET
# - system: Linux 5.15.0-56-generic
# - OS: ubuntu 20.04
# - processor: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
# - python version: 3.8.10.final.0 (64 bit)
# - numpy version: 1.24.3
# - pandas version: 2.0.3
# - pandapower version: 2.14.0
# - grid2op version: 1.11.0.dev2
# - lightsim2grid version: 0.9.2.post2
# - lightsim2grid extra information:

# - klu_solver_available: True
# - nicslu_solver_available: False
# - cktso_solver_available: False
# - compiled_march_native: False
# - compiled_o3_optim: False

# Solver used for linear algebra: SolverType.GaussSeidel

# ==================== =======================
# grid size (nb bus) gauss seidel max iter
# ==================== =======================
# 14 278
# 118 3274
# 200 8360
# 300 40783
# 1354 122169
# 1888
# ==================== =======================
# [31.858705410410803, 13.801689961508492, 7.912199121114395, 6.387621207822959, 4.5494311573542525, 1.3539274305627065, 0.01652457790687702, 5.5928201247405206e-08, 9.957519963773673e-09]
# [111.7637849724719, 52.1105433668106, 6.3902552555152345, 1.1851759157023143, 0.8457897295792693, 0.25197455746676584, 0.0030761171444685202, 1.0415372012959338e-08, 1.8561325626140559e-09]
8 changes: 5 additions & 3 deletions benchmarks/benchmark_grid_size.py
Original file line number Diff line number Diff line change
@@ -190,7 +190,9 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
g2op_step_time,
ls_solver_time,
ls_gridmodel_time,
g2op_sizes
g2op_sizes,
sgen_p,
nb_ts
):
_ = env_lightsim.reset()
done = False
@@ -330,7 +332,7 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
g2op_step_time_reset,
ls_solver_time_reset,
ls_gridmodel_time_reset,
g2op_sizes_reset
g2op_sizes_reset, sgen_p, nb_ts
)

reset_solver = False # default
@@ -340,7 +342,7 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
g2op_step_time,
ls_solver_time,
ls_gridmodel_time,
g2op_sizes
g2op_sizes, sgen_p, nb_ts
)

# Perform the computation using TimeSerie
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -22,8 +22,8 @@
author = 'Benjamin DONNOT'

# The full version, including alpha/beta/rc tags
release = "0.9.2.post2"
version = '0.9'
release = "0.10.0"
version = '0.10'

# -- General configuration ---------------------------------------------------

2 changes: 1 addition & 1 deletion lightsim2grid/__init__.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,7 @@
# SPDX-License-Identifier: MPL-2.0
# This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform.

__version__ = "0.9.2.post2"
__version__ = "0.10.0"

__all__ = ["newtonpf", "SolverType", "ErrorType", "solver", "compilation_options"]

Loading

0 comments on commit 46e04b9

Please sign in to comment.