Skip to content

Commit 072f7ba

Browse files
authored
Ml feature joule law (#123)
* A function to verify Joule law added * Joule law added to factory * configuration files for powergrid updated to include Joule law
1 parent 29e27a4 commit 072f7ba

File tree

4 files changed

+161
-10
lines changed

4 files changed

+161
-10
lines changed

configurations/powergrid/benchmarks/l2rpn_case14_sandbox.ini

+3-8
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ eval_dict = {
3737
"Physics": [],
3838
"IndRed": [],
3939
"OOD": []}
40-
# utils_lib = "scen1_utils"
4140

4241
[Benchmark1]
4342
attr_x = ("prod_p", "prod_v", "load_p", "load_q")
@@ -91,8 +90,6 @@ eval_params = {
9190
"KCL_tolerance": 1e-2,
9291
"ACTIVE_FLOW": True}
9392

94-
#utils_lib = "scen1_utils"
95-
9693
[Benchmark2]
9794
attr_x = ("prod_p", "prod_v", "load_p", "load_q")
9895
attr_tau = ("line_status", "topo_vect")
@@ -146,7 +143,7 @@ dataset_create_params = {
146143
}
147144
eval_dict = {
148145
"ML": ["MSE_avg", "MAE_avg", "mape_avg", "mape_90_avg", "TIME_INF"],
149-
"Physics": ["CURRENT_POS", "VOLTAGE_POS", "LOSS_POS", "DISC_LINES", "CHECK_LOSS", "CHECK_GC", "CHECK_LC", "CHECK_VOLTAGE_EQ"],
146+
"Physics": ["CURRENT_POS", "VOLTAGE_POS", "LOSS_POS", "DISC_LINES", "CHECK_LOSS", "CHECK_GC", "CHECK_LC", "CHECK_VOLTAGE_EQ", "CHECK_JOULE_LAW"],
150147
"IndRed": ["TIME_INF"],
151148
"OOD": ["MSE_avg", "MAE_avg", "mape_avg", "mape_90_avg", "TIME_INF"]}
152149
eval_params = {
@@ -157,9 +154,9 @@ eval_params = {
157154
"KCL": {"tolerance": 1e-2,
158155
"ACTIVE_FLOW": True},
159156
"VOLTAGE_EQ": {"tolerance": 1e-4,
160-
"verify_theta": False}
157+
"verify_theta": False},
158+
"JOULE_tolerance": 1e-2,
161159
}
162-
# utils_lib = "scen2_utils"
163160

164161
[Benchmark3]
165162
attr_x = ("prod_p", "prod_v", "load_p", "load_q")
@@ -200,5 +197,3 @@ eval_params = {
200197
"VOLTAGE_EQ": {"tolerance": 1e-4,
201198
"verify_theta": True}
202199
}
203-
#utils_lib = "scen1_utils"
204-

configurations/powergrid/benchmarks/l2rpn_neurips_2020_track1_small.ini

+3-2
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ dataset_create_params = {
204204
}
205205
eval_dict = {
206206
"ML": ["MSE_avg", "MAE_avg", "mape_avg", "mape_90_avg", "TIME_INF"],
207-
"Physics": ["CURRENT_POS", "VOLTAGE_POS", "LOSS_POS", "DISC_LINES", "CHECK_LOSS", "CHECK_GC", "CHECK_LC", "CHECK_VOLTAGE_EQ"],
207+
"Physics": ["CURRENT_POS", "VOLTAGE_POS", "LOSS_POS", "DISC_LINES", "CHECK_LOSS", "CHECK_GC", "CHECK_LC", "CHECK_VOLTAGE_EQ", "CHECK_JOULE_LAW"],
208208
"IndRed": ["TIME_INF"],
209209
"OOD": ["MSE_avg", "MAE_avg", "mape_avg", "mape_90_avg", "TIME_INF"]}
210210
eval_params = {
@@ -215,7 +215,8 @@ eval_params = {
215215
"KCL": {"tolerance": 1e-2,
216216
"ACTIVE_FLOW": True},
217217
"VOLTAGE_EQ": {"tolerance": 1e-4,
218-
"verify_theta": False}
218+
"verify_theta": False},
219+
"JOULE_tolerance": 1e-2,
219220
}
220221

221222
[Benchmark3]

lips/metrics/power_grid/joule_law.py

+153
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
"""
2+
Function to compute the Joule law for power grid use case
3+
"""
4+
from typing import Union
5+
import numpy as np
6+
7+
from lightsim2grid import LightSimBackend
8+
from grid2op.Backend import PandaPowerBackend
9+
10+
from ...logger import CustomLogger
11+
12+
def verify_joule_law(predictions: dict,
13+
log_path: Union[str, None]=None,
14+
result_level: int=0,
15+
**kwargs):
16+
"""
17+
This function compute Joule's law which is ``$PL = R \times I^2$``
18+
19+
To compute the PowerLoss(PL), we use : ``$p_ex + p_or$``
20+
The resistance values are deduced from backends.
21+
The currents are computed as follows : ``$\frac{a_or + a_ex}{2}$``
22+
23+
TODO : consider the line disconnections
24+
25+
Parameters
26+
----------
27+
predictions: ``dict``
28+
Predictions made by an augmented simulator
29+
log_path: ``str``, optional
30+
the path where the logs should be saved, by default None
31+
**kwargs: ``dict``
32+
It should contain `observations` and `config` to load the tolerance
33+
34+
Returns
35+
-------
36+
``dict``
37+
a dictionary with useful information about the verification
38+
39+
These informations are:
40+
- mae_per_obs: `array`
41+
The Absolute error (difference) between two sides of Joule equation per observation
42+
43+
- mae_per_obj: `array`
44+
The Absolute Error (difference) between two sides of Joule equation per line for each observation
45+
46+
- mae: `scalar`
47+
the Mean Absolute Error (difference) between two sides of the Joule equation
48+
49+
- wmape: `scalar`
50+
weighted mean absolute percentage error between two sides of the Joule equation
51+
52+
- violation_proportion: `scalar`
53+
The percentage of violation of Joule law over all the observations
54+
"""
55+
# logger
56+
logger = CustomLogger("PhysicsCompliances(Joule_law)", log_path).logger
57+
58+
try:
59+
env = kwargs["env"]
60+
except KeyError:
61+
logger.error("The requirements were not satisiftied to verify_joule_law function")
62+
raise
63+
64+
try:
65+
config = kwargs["config"]
66+
except KeyError:
67+
try:
68+
tolerance = kwargs["tolerance"]
69+
except KeyError:
70+
logger.error("The tolerance could not be found for verify_joule_law function")
71+
raise
72+
else:
73+
tolerance = float(tolerance)
74+
else:
75+
tolerance = float(config.get_option("eval_params")["JOULE_tolerance"])
76+
77+
if isinstance(env.backend, LightSimBackend):
78+
# extract the impedance values which are in p.u
79+
z_pu = [el.r_pu for el in env.backend._grid.get_lines()]
80+
z_pu_trafo = [el.r_pu for el in env.backend._grid.get_trafos()]
81+
# enumerate lines and trafos
82+
n_lines = len(z_pu)
83+
n_trafo = len(z_pu_trafo)
84+
# transform pu to Ohm and keep only lines
85+
z_base = np.power(env.backend.lines_or_pu_to_kv, 2) / env.backend._grid.get_sn_mva()
86+
r_ohm = z_pu * z_base[:n_lines]
87+
88+
elif isinstance(env.backend, PandaPowerBackend):
89+
# n_lines
90+
n_lines = len(env.backend._grid.line)
91+
# n_transformers
92+
n_transformers = len(env.backend._grid.trafo)
93+
# get the resistance
94+
r_ohm = np.array(env.backend._grid.line["r_ohm_per_km"]).reshape(1,-1)
95+
96+
verifications = dict()
97+
mean_current = _get_average_currents(predictions)
98+
mean_current_squared = np.power(mean_current, 2)
99+
pl_mw = _get_power_loss(predictions)
100+
# MAE between PL and R.I^2
101+
left_array = pl_mw[:, :n_lines]/3
102+
right_array = mean_current_squared[:, :n_lines] * r_ohm
103+
mae_per_obj = np.abs(left_array - right_array)
104+
mae_per_obs = mae_per_obj.mean(axis=1)
105+
mae = np.mean(mae_per_obs)
106+
wmape = np.mean(np.abs(left_array - right_array)) / np.mean(np.abs(left_array))
107+
violation_prop = np.sum(mae_per_obj > tolerance) / mae_per_obj.size
108+
109+
logger.info("Joule law violation proportion: %.3f", violation_prop)
110+
logger.info("MAE for Joule law: %.3f", mae)
111+
logger.info("WMAPE for Joule law: %.3f", wmape)
112+
113+
if result_level > 0:
114+
verifications["mae_per_obs"] = mae_per_obs
115+
verifications["mae_per_obj"] = mae_per_obj
116+
verifications["violation_proportion"] = violation_prop
117+
verifications["mae"] = mae
118+
verifications["wmape"] = wmape
119+
120+
return verifications
121+
122+
def _get_average_currents(data: dict) -> np.ndarray:
123+
"""Compute the average current using current from both extremities of a power line
124+
125+
Parameters
126+
----------
127+
data : ``dict``
128+
A python dictionary including currents as keys
129+
130+
Returns
131+
-------
132+
np.array
133+
average matrix of currents
134+
"""
135+
current_avg = np.abs(data.get("a_or") + data.get("a_ex"))/2
136+
current_avg_kA = current_avg / 1000
137+
return current_avg_kA
138+
139+
def _get_power_loss(data: dict) -> np.ndarray:
140+
"""Compute the power loss from powers at two extremities of power lines
141+
142+
Parameters
143+
----------
144+
data : ``dict``
145+
A python dictionary including powers as keys
146+
147+
Returns
148+
-------
149+
``np.ndarray``
150+
Power losses array at the line level for all the observations
151+
"""
152+
pl_mw = np.abs(data.get("p_or") + data.get("p_ex"))
153+
return pl_mw

lips/metrics/power_grid/physics_compliances.py

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from ...metrics.power_grid.global_conservation import global_conservation
1919
from ...metrics.power_grid.local_conservation import local_conservation
2020
from ...metrics.power_grid.verify_voltage_equality import verify_voltage_at_bus
21+
from ...metrics.power_grid.joule_law import verify_joule_law
2122
from ...logger import CustomLogger
2223

2324
def verify_current_pos(predictions: dict,
@@ -496,3 +497,4 @@ def verify_kcl(env,
496497
metric_factory.register_metric("CHECK_GC", global_conservation)
497498
metric_factory.register_metric("CHECK_LC", local_conservation)
498499
metric_factory.register_metric("CHECK_VOLTAGE_EQ", verify_voltage_at_bus)
500+
metric_factory.register_metric("CHECK_JOULE_LAW", verify_joule_law)

0 commit comments

Comments
 (0)