Skip to content

Commit

Permalink
Merge pull request #100 from Grid2op/issue_96
Browse files Browse the repository at this point in the history
Adressing issue #96
  • Loading branch information
BDonnot authored Feb 4, 2025
2 parents 4594c3d + f72f568 commit e7ccf69
Show file tree
Hide file tree
Showing 22 changed files with 1,250 additions and 218 deletions.
20 changes: 20 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ 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.2] 2025-01-xx
----------------------
- [FIXED] an error when changing of bus one of the slack (did not trigger the
recompute of pv bus ids)
- [FIXED] an issue when turning off a generator: it was still declared as "slack"
if it was one.
- [FIXED] could not disconnect a generator when it was a slack bus
- [ADDED] packaging package as a dependency
- [IMPROVED] refactoring of the c++ side container element to reduce
code (for "one end" elements such as loads, generators, static generators and shunts)

[0.10.1] 2025-01-04
----------------------------
- [FIXED] some timings on the benchmarks were not measured at the right time
- [ADDED] more benchmarks especially for DC powerflow
- [ADDED] a `dcpf` function that can replace the pandapower `dcpf` interal function
- [IMPROVED] benchmark on the documentation
(clarity of what is done)
- [IMPROVED] consistency of the names and measured times accross the different benchmarks

[0.10.0] 2024-12-17
-------------------
- [BREAKING] disconnected storage now raises errors if some power is produced / absorbed, when using legacy grid2op version,
Expand Down
347 changes: 347 additions & 0 deletions benchmarks/benchmark_dc_solvers.py

Large diffs are not rendered by default.

39 changes: 32 additions & 7 deletions benchmarks/benchmark_grid_size.py
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
env_lightsim.backend.tol)
time_serie._TimeSerie__computed = True
a_or = time_serie.compute_A()
assert status, f"some powerflow diverge for Time Series for {case_name}: {computer.nb_solved()} "
assert status or computer.nb_solver() == nb_step_pp, f"some powerflow diverge for Time Series for {case_name}: {computer.nb_solved()} "

if VERBOSE:
# print detailed results if needed
Expand Down Expand Up @@ -403,7 +403,32 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
print_configuration()
print(f"Solver used for linear algebra: {linear_solver_used_str}")
print()


print("TL;DR")
tab_tldr = []
for i, nm_ in enumerate(case_names_displayed):
tab_tldr.append((nm_,
ts_sizes[i],
1000. * ls_gridmodel_time[i] / nb_step if ls_gridmodel_time[i] else None,
1000. * ls_gridmodel_time_reset[i] / nb_step_reset if ls_gridmodel_time_reset[i] else None,
1000. / ts_speeds[i] if ts_speeds[i] else None,
1000. / sa_speeds[i] if sa_speeds[i] else None,
))
if TABULATE_AVAIL:
res_use_with_grid2op_2 = tabulate(tab_tldr,
headers=["grid",
"size (nb bus)",
"time (recycling)",
"time (no recycling)",
"time (`TimeSerie`)",
"time (`ContingencyAnalysis`)",
],
tablefmt="rst")
print(res_use_with_grid2op_2)
else:
print(tab_tldr)
print()

print("Results using grid2op.steps (288 consecutive steps, only measuring 'dc pf [init] + ac pf') (no recycling allowed, non default)")
tab_g2op = []
for i, nm_ in enumerate(case_names_displayed):
Expand All @@ -417,13 +442,13 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
))
if TABULATE_AVAIL:
res_use_with_grid2op_2 = tabulate(tab_g2op,
headers=["grid",
headers=["grid name",
"size (nb bus)",
"avg step duration (ms)",
"time [DC + AC] (ms / pf)",
"speed (pf / s)",
"time in 'gridmodel' (ms / pf)",
"time in 'pf algo' (ms / pf)",
"time in 'solver' (ms / pf)",
"time in 'algo' (ms / pf)",
],
tablefmt="rst")
print(res_use_with_grid2op_2)
Expand All @@ -450,8 +475,8 @@ def run_grid2op_env(env_lightsim, case, reset_solver,
"avg step duration (ms)",
"time [DC + AC] (ms / pf)",
"speed (pf / s)",
"time in 'gridmodel' (ms / pf)",
"time in 'pf algo' (ms / pf)",
"time in 'solver' (ms / pf)",
"time in 'algo' (ms / pf)",
],
tablefmt="rst")
print(res_use_with_grid2op_2)
Expand Down
88 changes: 82 additions & 6 deletions benchmarks/benchmark_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import warnings
import pandas as pd
from grid2op import make
from grid2op.Backend import PandaPowerBackend
from grid2op.Agent import DoNothingAgent
from grid2op.Chronics import ChangeNothing
import re
Expand All @@ -22,6 +23,13 @@
print("Be carefull: there might be maintenance")
from grid2op.Chronics import GridStateFromFile

try:
from pypowsybl2grid import PyPowSyBlBackend
pypow_error = None
except ImportError as exc_:
pypow_error = exc_
print("Backend based on pypowsybl will not be benchmarked")

from grid2op.Parameters import Parameters
import lightsim2grid
from lightsim2grid.lightSimBackend import LightSimBackend
Expand Down Expand Up @@ -105,15 +113,42 @@ def main(max_ts,
if re.match("^.*\\.json$", env_name_input) is None:
# i provided an environment name
env_pp = make(env_name_input, param=param, test=test,
backend=PandaPowerBackend(lightsim2grid=False, with_numba=True),
data_feeding_kwargs={"gridvalueClass": GridStateFromFile})
env_pp_no_numba = make(env_name_input, param=param, test=test,
backend=PandaPowerBackend(lightsim2grid=False, with_numba=False),
data_feeding_kwargs={"gridvalueClass": GridStateFromFile})
env_pp_ls_numba = make(env_name_input, param=param, test=test,
backend=PandaPowerBackend(lightsim2grid=True, with_numba=True),
data_feeding_kwargs={"gridvalueClass": GridStateFromFile})
env_lightsim = make(env_name_input, backend=LightSimBackend(), param=param, test=test,
data_feeding_kwargs={"gridvalueClass": GridStateFromFile})
if pypow_error is None:
env_pypow = make(env_name_input, param=param, test=test,
backend=PyPowSyBlBackend(),
data_feeding_kwargs={"gridvalueClass": GridStateFromFile})
else:
# I provided an environment path
env_pp = make("blank", param=param, test=True,
data_feeding_kwargs={"gridvalueClass": ChangeNothing},
grid_path=env_name_input
grid_path=env_name_input,
backend=PandaPowerBackend(lightsim2grid=False, with_numba=True)
)
env_pp_no_numba = make("blank", param=param, test=True,
data_feeding_kwargs={"gridvalueClass": ChangeNothing},
grid_path=env_name_input,
backend=PandaPowerBackend(lightsim2grid=False, with_numba=False)
)
env_pp_ls_numba = make("blank", param=param, test=True,
data_feeding_kwargs={"gridvalueClass": ChangeNothing},
grid_path=env_name_input,
backend=PandaPowerBackend(lightsim2grid=True, with_numba=True)
)
if pypow_error is None:
env_pypow = make("blank", param=param, test=True,
data_feeding_kwargs={"gridvalueClass": ChangeNothing},
grid_path=env_name_input,
backend=PyPowSyBlBackend())
env_lightsim = make("blank", param=param, test=True,
backend=LightSimBackend(),
data_feeding_kwargs={"gridvalueClass": ChangeNothing},
Expand All @@ -126,7 +161,35 @@ def main(max_ts,
nb_ts_pp, time_pp, aor_pp, gen_p_pp, gen_q_pp = run_env(env_pp, max_ts, agent, chron_id=0, env_seed=0)
pp_comp_time = env_pp.backend.comp_time
pp_time_pf = env_pp._time_powerflow
if hasattr(env_pp, "_time_step"):
# for oldest grid2op version where this was not stored
time_pp = env_pp._time_step

tmp_no_numba = run_env(env_pp_no_numba, max_ts, agent, chron_id=0, env_seed=0)
nb_ts_pp_no_numba, time_pp_no_numba, aor_pp_no_numba, gen_p_pp_no_numba, gen_q_pp_no_numba = tmp_no_numba
pp_no_numba_comp_time = env_pp_no_numba.backend.comp_time
pp_no_numba_time_pf = env_pp_no_numba._time_powerflow
if hasattr(env_pp_no_numba, "_time_step"):
# for oldest grid2op version where this was not stored
time_pp_no_numba = env_pp_no_numba._time_step

tmp_ls_numba = run_env(env_pp_ls_numba, max_ts, agent, chron_id=0, env_seed=0)
nb_ts_pp_ls_numba, time_pp_ls_numba, aor_pp_ls_numba, gen_p_ls_numba, gen_q_ls_numba = tmp_ls_numba
pp_ls_numba_comp_time = env_pp_ls_numba.backend.comp_time
pp_ls_numba_time_pf = env_pp_ls_numba._time_powerflow
if hasattr(env_pp_ls_numba, "_time_step"):
# for oldest grid2op version where this was not stored
time_pp_ls_numba = env_pp_ls_numba._time_step

if pypow_error is None:
# also benchmark pypowsybl backend
nb_ts_pypow, time_pypow, aor_pypow, gen_p_pypow, gen_q_pypow = run_env(env_pypow, max_ts, agent, chron_id=0, env_seed=0)
pypow_comp_time = env_pypow.backend.comp_time
pypow_time_pf = env_pypow._time_powerflow
if hasattr(env_pypow, "_time_step"):
# for oldest grid2op version where this was not stored
time_pypow = env_pypow._time_step

wst = True # print extra info in the run_env function
solver_types = env_lightsim.backend.available_solvers

Expand Down Expand Up @@ -154,27 +217,40 @@ def main(max_ts,
with_type_solver=wst, env_seed=0)
gs_comp_time = env_lightsim.backend.comp_time
gs_time_pf = env_lightsim._time_powerflow
if hasattr(env_lightsim, "_time_step"):
# for oldest grid2op version where this was not stored
time_gs = env_lightsim._time_step
res_times[solver_type] = (solver_names[solver_type],
nb_ts_gs, time_gs, aor_gs, gen_p_gs,
gen_q_gs, gs_comp_time, gs_time_pf)

# NOW PRINT THE RESULTS
print("Configuration:")
config_str = print_configuration()
config_str = print_configuration(pypow_error)
if save_results != DONT_SAVE:
with open(save_results+"config_info.txt", "w", encoding="utf-8") as f:
f.write(config_str)
# order on which the solvers will be
this_order = [el for el in res_times.keys() if el not in order_solver_print] + order_solver_print

env_name = get_env_name_displayed(env_name_input)
hds = [f"{env_name}", f"grid2op speed (it/s)", f"grid2op 'backend.runpf' time (ms)", f"solver powerflow time (ms)"]
hds = [f"{env_name}", f"grid2op speed (it/s)", f"grid2op 'backend.runpf' time (ms)", f"time in 'algo' (ms / pf)"]
tab = []
if no_pp is False:
tab.append(["PP", f"{nb_ts_pp/time_pp:.2e}",
f"{1000.*pp_time_pf/nb_ts_pp:.2e}",
f"{1000.*pp_comp_time/nb_ts_pp:.2e}"])

tab.append(["PP (no numba)", f"{nb_ts_pp_no_numba/time_pp_no_numba:.2e}",
f"{1000.*pp_no_numba_time_pf/nb_ts_pp_no_numba:.2e}",
f"{1000.*pp_no_numba_comp_time/nb_ts_pp_no_numba:.2e}"])
tab.append(["PP (with lightsim)", f"{nb_ts_pp_ls_numba/time_pp_ls_numba:.2e}",
f"{1000.*pp_ls_numba_time_pf/nb_ts_pp_ls_numba:.2e}",
f"{1000.*pp_ls_numba_comp_time/nb_ts_pp_ls_numba:.2e}"])
if pypow_error is None:
tab.append(["pypowsybl", f"{nb_ts_pypow/time_pypow:.2e}",
f"{1000.*pypow_time_pf/nb_ts_pypow:.2e}",
f"{1000.*pypow_comp_time/nb_ts_pypow:.2e}"])

for key in this_order:
if key not in res_times:
continue
Expand Down Expand Up @@ -202,10 +278,10 @@ def main(max_ts,
print(tab)
print()

hds = [f"{env_name} ({nb_ts_pp} iter)", f"Δ aor (amps)", f"Δ gen_p (MW)", f"Δ gen_q (MVAr)"]
if no_pp is False:
hds = [f"{env_name} ({nb_ts_pp} iter)", f"Δ aor (amps)", f"Δ gen_p (MW)", f"Δ gen_q (MVAr)"]
tab = [["PP (ref)", "0.00", "0.00", "0.00"]]

for key in this_order:
if key not in res_times:
continue
Expand Down
6 changes: 6 additions & 0 deletions benchmarks/req_benchmarks.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
numba
tabulate
py-cpuinfo
grid2op
distro
matplotlib
13 changes: 11 additions & 2 deletions benchmarks/utils_benchmark.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from tqdm import tqdm
import argparse
import datetime
import importlib
from grid2op.Environment import MultiMixEnvironment
import pdb

Expand Down Expand Up @@ -81,7 +82,7 @@ def run_env(env, max_ts, agent, chron_id=None, keep_forecast=False, with_type_so
if not keep_forecast:
env.deactivate_forecast()
need_reset = True

if need_reset:
obs = env.reset()
else:
Expand Down Expand Up @@ -127,7 +128,7 @@ def str2bool(v):
raise argparse.ArgumentTypeError('Boolean value expected.')


def print_configuration():
def print_configuration(pypow_error=True):
res = []
print()
tmp = f"- date: {datetime.datetime.now():%Y-%m-%d %H:%M %z} {time.localtime().tm_zone}"
Expand Down Expand Up @@ -185,6 +186,14 @@ def print_configuration():
tmp = (f"- pandapower version: {pp.__version__}")
res.append(tmp)
print(tmp)
if pypow_error is None:
tmp = (f"- pypowsybl version: {importlib.metadata.version('pypowsybl')}")
res.append(tmp)
print(tmp)
tmp = (f"- pypowsybl2grid version: {importlib.metadata.version('pypowsybl2grid')}")
res.append(tmp)
print(tmp)

tmp = (f"- grid2op version: {grid2op.__version__}")
res.append(tmp)
print(tmp)
Expand Down
Loading

0 comments on commit e7ccf69

Please sign in to comment.