Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adressing issue #96 #100

Open
wants to merge 12 commits into
base: bd-dev
Choose a base branch
from
11 changes: 9 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,20 @@ 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.1.dev0] 2025-xx-yy
----------------------------
[0.10.1] 2025-01-xx
----------------------
- [FIXED] some timings on the benchmarks were not measured at the right time
- [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] more benchmarks especially for DC powerflow
- [ADDED] a `dfpc` function that can replace the pandapower `dcpf` interal function
- [ADDED] packaging package as a dependency
- [IMPROVED] benchmark on the documentation
(clarity of what is done)
- [IMPROVED] consistency of the names and measured times accross the different benchmarks
- [IMPROVED] refactoring of the c++ side container element to reduce
code (for "one end" elements such as loads, generators, static generators and shunts)

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
Loading