Skip to content

Commit 46e04b9

Browse files
authored
Merge pull request #98 from Grid2op/bd-dev
Ready for version 0.10.0
2 parents 919994d + 4376087 commit 46e04b9

File tree

12 files changed

+513
-35
lines changed

12 files changed

+513
-35
lines changed

CHANGELOG.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,23 @@ TODO: in `main.cpp` check the returned policy of pybind11 and also the `py::call
2626
TODO: a cpp class that is able to compute (DC powerflow) ContingencyAnalysis and TimeSeries using PTDF and LODF
2727
TODO: integration test with pandapower (see `pandapower/contingency/contingency.py` and import `lightsim2grid_installed` and check it's True)
2828

29+
[0.10.0] 2024-12-17
30+
-------------------
31+
- [BREAKING] disconnected storage now raises errors if some power is produced / absorbed, when using legacy grid2op version,
32+
you can retrieve the previous behaviour by initializing the `LightSimBackend` with
33+
`backend = LightSimBackend(..., stop_if_storage_disco=False)`
34+
- [BREAKING] with the new `detachment_is_allowed` feature in grid2op, the kwargs `stop_if_load_disco`,
35+
`stop_if_gen_disco` (and `stop_if_storage_disco`) are now optional. They are set up from the
36+
call to `grid2op.make(...)` and are erased by the `allow_detachment` kwargs. In other words,
37+
you don't need to set `stop_if_load_disco`, `stop_if_gen_disco` or `stop_if_storage_disco`. It is
38+
automatically set by `grid2op.make(..., allow_detachment=XXX)` to have the correct bahaviour.
39+
- [FIXED] an issue with the storage units (when asking it to produce / consume
40+
but deactivating them with the same action the grid did not diverge)
41+
- [IMPROVED] add the grid2op "detachement" support (loads and generators are allowed
42+
to be disconnected from the grid)
43+
- [ADDED] a kwargs `stop_if_storage_disco` to control (in legacy grid2op version) the behaviour
44+
of the backend when a storage unit is disconnected.
45+
2946
[0.9.2.post2] 2024-11-29
3047
--------------------------
3148
- [FIXED] The attribute `can_output_theta` (of base `Backend` class)

basic_perf.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
import warnings
2+
import pandapower as pp
3+
import numpy as np
4+
from grid2op import make, Parameters
5+
from grid2op.Chronics import FromNPY
6+
from lightsim2grid import LightSimBackend
7+
import tempfile
8+
import os
9+
10+
try:
11+
from tabulate import tabulate
12+
TABULATE_AVAIL = True
13+
except ImportError:
14+
print("The tabulate package is not installed. Some output might not work properly")
15+
TABULATE_AVAIL = False
16+
17+
18+
case_names = [
19+
# "case14.json",
20+
"case118.json",
21+
# "case_illinois200.json",
22+
# "case300.json",
23+
# "case1354pegase.json",
24+
"case1888rte.json",
25+
# "GBnetwork.json", # 2224 buses
26+
"case2848rte.json",
27+
# "case2869pegase.json",
28+
# "case3120sp.json",
29+
"case6495rte.json",
30+
"case6515rte.json",
31+
"case9241pegase.json"
32+
]
33+
34+
case_name = "case6495rte.json"
35+
case_name = "case14.json"
36+
37+
def make_grid2op_env(pp_case, case_name, load_p, load_q, gen_p, sgen_p):
38+
param = Parameters.Parameters()
39+
param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True})
40+
41+
with warnings.catch_warnings():
42+
warnings.filterwarnings("ignore")
43+
env_lightsim = make("blank",
44+
param=param,
45+
test=True,
46+
backend=LightSimBackend(),
47+
chronics_class=FromNPY,
48+
data_feeding_kwargs={"load_p": load_p,
49+
"load_q": load_q,
50+
"prod_p": gen_p
51+
},
52+
grid_path=case_name,
53+
_add_to_name=f"{case_name}",
54+
)
55+
return env_lightsim
56+
57+
if __name__ == "__main__":
58+
59+
import pandapower.networks as pn
60+
for case_name in case_names:
61+
tmp_nm = os.path.splitext(case_name)[0]
62+
print(f"====================== {tmp_nm} ======================")
63+
case = getattr(pn,tmp_nm)()
64+
pp.runpp(case) # for slack
65+
66+
load_p_init = 1.0 * case.load["p_mw"].values
67+
load_q_init = 1.0 * case.load["q_mvar"].values
68+
gen_p_init = 1.0 * case.gen["p_mw"].values
69+
sgen_p_init = 1.0 * case.sgen["p_mw"].values
70+
71+
nb_ts = 1
72+
# add slack !
73+
slack_gens = np.zeros((nb_ts, case.ext_grid.shape[0]))
74+
if "res_ext_grid" in case:
75+
slack_gens += np.tile(case.res_ext_grid["p_mw"].values.reshape(1,-1), (nb_ts, 1))
76+
gen_p_g2op = np.concatenate((gen_p_init.reshape(1,-1), slack_gens), axis=1)
77+
78+
with tempfile.TemporaryDirectory() as tmpdirname:
79+
pp.to_json(case, os.path.join(tmpdirname, case_name))
80+
with open(os.path.join(tmpdirname, "config.py"), "w") as f:
81+
f.write("config = {}")
82+
83+
env = make_grid2op_env(None,
84+
os.path.join(tmpdirname, case_name),
85+
load_p=load_p_init.reshape(1,-1),
86+
load_q=load_q_init.reshape(1,-1),
87+
gen_p=gen_p_g2op.reshape(1,-1),
88+
sgen_p=None)
89+
90+
env.backend._grid.tell_solver_need_reset()
91+
_ = env.step(env.action_space())
92+
ls_solver = env.backend._grid.get_solver()
93+
nb_iter_solver = ls_solver.get_nb_iter()
94+
timers = ls_solver.get_timers_jacobian()
95+
(timer_Fx, timer_solve, timer_init, timer_check,
96+
timer_compute_dS, timer_fillJ, timer_compVa_Vm, timer_preproc, timer_total) = timers
97+
print(f"Total time for the powerflow (=pre proc + NR + post proc): {env.backend._grid.timer_last_ac_pf:.2e}s")
98+
print(f"Total time spent in the Newton Raphson: {timer_total:.2e}s")
99+
print(f"Time to pre process input data: {timer_preproc:.2e}s")
100+
print(f"Time to intialize linear solver: {timer_init:.2e}s")
101+
print(f"Then for all iterations (cumulated time over all {nb_iter_solver} iterations)")
102+
print(f"\ttotal time to compute dS/dVm and dS/dVa: {timer_compute_dS:.2e}s")
103+
print(f"\ttotal time fill jacobian matrix (from dS/dVm and dS/dVa): {timer_fillJ:.2e}s")
104+
print(f"\ttotal time to solve J.x = b: {timer_solve:.2e}s")
105+
print(f"\ttotal time to compute V, Va and Vm: {timer_compVa_Vm:.2e}s")
106+
print(f"\ttotal time to compute p, q mismatch at buses: {timer_Fx:.2e}s")
107+
print(f"\ttotal time to check if p,q mismatch at buses are within tolerance: {timer_check:.2e}s")
108+
print(f"====================== {' '*len(tmp_nm)} ======================")
109+
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Copyright (c) 2024, RTE (https://www.rte-france.com)
2+
# See AUTHORS.txt
3+
# This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0.
4+
# If a copy of the Mozilla Public License, version 2.0 was not distributed with this file,
5+
# you can obtain one at http://mozilla.org/MPL/2.0/.
6+
# SPDX-License-Identifier: MPL-2.0
7+
# This file is part of LightSim2grid, LightSim2grid a implements a c++ backend targeting the Grid2Op platform.
8+
9+
import warnings
10+
import copy
11+
import pandapower as pp
12+
import numpy as np
13+
import hashlib
14+
from scipy.interpolate import interp1d
15+
import matplotlib.pyplot as plt
16+
from grid2op import make, Parameters
17+
from grid2op.Chronics import FromNPY, ChangeNothing
18+
from grid2op.Backend import PandaPowerBackend
19+
from grid2op.Exceptions import Grid2OpException
20+
import lightsim2grid
21+
from lightsim2grid import LightSimBackend
22+
from benchmark_grid_size import (get_loads_gens,
23+
make_grid2op_env_pp,
24+
run_grid2op_env,
25+
make_grid2op_env)
26+
from benchmark_solvers import solver_gs, solver_names, order_solver_print
27+
28+
from tqdm import tqdm
29+
import os
30+
from utils_benchmark import print_configuration, get_env_name_displayed
31+
from benchmark_solvers import solver_names
32+
33+
try:
34+
from tabulate import tabulate
35+
TABULATE_AVAIL = True
36+
except ImportError:
37+
print("The tabulate package is not installed. Some output might not work properly")
38+
TABULATE_AVAIL = False
39+
40+
VERBOSE = False
41+
MAKE_PLOT = False
42+
WITH_PP = False
43+
DEBUG = False
44+
45+
case_names = [
46+
"case14.json",
47+
"case118.json",
48+
"case_illinois200.json",
49+
"case300.json",
50+
"case1354pegase.json",
51+
"case1888rte.json",
52+
# # "GBnetwork.json", # 2224 buses
53+
# "case2848rte.json",
54+
# "case2869pegase.json",
55+
# "case3120sp.json",
56+
# "case6495rte.json",
57+
# "case6515rte.json",
58+
# "case9241pegase.json"
59+
]
60+
61+
62+
if __name__ == "__main__":
63+
prng = np.random.default_rng(42)
64+
case_names_displayed = [get_env_name_displayed(el) for el in case_names]
65+
nb_iters = []
66+
ts_sizes = []
67+
errors = {}
68+
for case_name in tqdm(case_names):
69+
70+
if not os.path.exists(case_name):
71+
import pandapower.networks as pn
72+
case = getattr(pn, os.path.splitext(case_name)[0])()
73+
pp.to_json(case, case_name)
74+
75+
# load the case file
76+
case = pp.from_json(case_name)
77+
ts_sizes.append(case.bus.shape[0])
78+
pp.runpp(case) # for slack
79+
80+
# create the env
81+
param = Parameters.Parameters()
82+
param.init_from_dict({"NO_OVERFLOW_DISCONNECTION": True})
83+
84+
with warnings.catch_warnings():
85+
warnings.filterwarnings("ignore")
86+
env_pp = make("blank",
87+
param=param, test=True,
88+
backend=PandaPowerBackend(lightsim2grid=False),
89+
chronics_class=ChangeNothing,
90+
grid_path=case_name,
91+
_add_to_name=f"{case_name}",
92+
)
93+
env_ls = make("blank",
94+
param=param, test=True,
95+
backend=LightSimBackend(),
96+
chronics_class=ChangeNothing,
97+
grid_path=case_name,
98+
_add_to_name=f"{case_name}",
99+
)
100+
env_ls.backend.set_solver_type(lightsim2grid.SolverType.GaussSeidel)
101+
all_iters = [1, 3, 10, 30, 100, 300, 1_000, 3_000,
102+
10_000, 30_000,
103+
100_000, 300_000
104+
]
105+
iters = []
106+
errors_p = []
107+
errors_q = []
108+
for max_iter in all_iters:
109+
env_ls.backend.set_solver_max_iter(max_iter)
110+
env_ls.backend._grid.tell_solver_need_reset()
111+
conv = True
112+
try:
113+
obs = env_ls.reset()
114+
except Grid2OpException as exc_:
115+
conv = False
116+
iters.append(env_ls.backend._grid.get_solver().get_nb_iter())
117+
v_tmp = env_ls.backend._grid.get_solver().get_V()
118+
res_tmp = env_ls.backend._grid.check_solution(v_tmp, False)
119+
error_p = 1. * np.abs(res_tmp.real).max()
120+
error_q = 1. * np.abs(res_tmp.imag).max()
121+
errors_p.append(error_p)
122+
errors_q.append(error_q)
123+
if conv:
124+
break
125+
if conv:
126+
nb_iters.append(iters[-1])
127+
else:
128+
nb_iters.append(None)
129+
130+
errors[case.bus.shape[0]] = (errors_p, errors_q)
131+
132+
print("Configuration:")
133+
print_configuration()
134+
print(f"Solver used for linear algebra: {lightsim2grid.SolverType.GaussSeidel}")
135+
print()
136+
hds = ["grid size (nb bus)", "gauss seidel max iter"]
137+
tab = []
138+
for sz, nb_it in zip(ts_sizes, nb_iters):
139+
tab.append([sz, nb_it])
140+
141+
if TABULATE_AVAIL:
142+
res_use_with_grid2op_2 = tabulate(tab, headers=hds, tablefmt="rst")
143+
print(res_use_with_grid2op_2)
144+
else:
145+
print(tab)
146+
147+
print(errors[118][0])
148+
print(errors[118][1])
149+
import pickle
150+
with open("res_gauss_seidel.pickle", "wb") as f:
151+
pickle.dump(errors, file=f)
152+
with open("res_gauss_seidel_nb_iters.pickle", "wb") as f:
153+
pickle.dump(nb_iters, file=f)
154+
print()
155+
print()
156+
157+
158+
# total computation time : 1h27min16s
159+
# Configuration:
160+
161+
# - date: 2024-12-02 18:46 CET
162+
# - system: Linux 5.15.0-56-generic
163+
# - OS: ubuntu 20.04
164+
# - processor: Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz
165+
# - python version: 3.8.10.final.0 (64 bit)
166+
# - numpy version: 1.24.3
167+
# - pandas version: 2.0.3
168+
# - pandapower version: 2.14.0
169+
# - grid2op version: 1.11.0.dev2
170+
# - lightsim2grid version: 0.9.2.post2
171+
# - lightsim2grid extra information:
172+
173+
# - klu_solver_available: True
174+
# - nicslu_solver_available: False
175+
# - cktso_solver_available: False
176+
# - compiled_march_native: False
177+
# - compiled_o3_optim: False
178+
179+
# Solver used for linear algebra: SolverType.GaussSeidel
180+
181+
# ==================== =======================
182+
# grid size (nb bus) gauss seidel max iter
183+
# ==================== =======================
184+
# 14 278
185+
# 118 3274
186+
# 200 8360
187+
# 300 40783
188+
# 1354 122169
189+
# 1888
190+
# ==================== =======================
191+
# [31.858705410410803, 13.801689961508492, 7.912199121114395, 6.387621207822959, 4.5494311573542525, 1.3539274305627065, 0.01652457790687702, 5.5928201247405206e-08, 9.957519963773673e-09]
192+
# [111.7637849724719, 52.1105433668106, 6.3902552555152345, 1.1851759157023143, 0.8457897295792693, 0.25197455746676584, 0.0030761171444685202, 1.0415372012959338e-08, 1.8561325626140559e-09]

benchmarks/benchmark_grid_size.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,9 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
190190
g2op_step_time,
191191
ls_solver_time,
192192
ls_gridmodel_time,
193-
g2op_sizes
193+
g2op_sizes,
194+
sgen_p,
195+
nb_ts
194196
):
195197
_ = env_lightsim.reset()
196198
done = False
@@ -330,7 +332,7 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
330332
g2op_step_time_reset,
331333
ls_solver_time_reset,
332334
ls_gridmodel_time_reset,
333-
g2op_sizes_reset
335+
g2op_sizes_reset, sgen_p, nb_ts
334336
)
335337

336338
reset_solver = False # default
@@ -340,7 +342,7 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
340342
g2op_step_time,
341343
ls_solver_time,
342344
ls_gridmodel_time,
343-
g2op_sizes
345+
g2op_sizes, sgen_p, nb_ts
344346
)
345347

346348
# Perform the computation using TimeSerie

docs/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222
author = 'Benjamin DONNOT'
2323

2424
# The full version, including alpha/beta/rc tags
25-
release = "0.9.2.post2"
26-
version = '0.9'
25+
release = "0.10.0"
26+
version = '0.10'
2727

2828
# -- General configuration ---------------------------------------------------
2929

lightsim2grid/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# SPDX-License-Identifier: MPL-2.0
77
# This file is part of LightSim2grid, LightSim2grid implements a c++ backend targeting the Grid2Op platform.
88

9-
__version__ = "0.9.2.post2"
9+
__version__ = "0.10.0"
1010

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

0 commit comments

Comments
 (0)