Skip to content

Commit 32b27e0

Browse files
Merge branch 'main' into df/#351-nbval
# Conflicts: # CHANGELOG.md
2 parents d92e566 + f334f6f commit 32b27e0

File tree

12 files changed

+320
-42
lines changed

12 files changed

+320
-42
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.
66

77
### Added
88
- Add project-level `CLAUDE.md` file [#329](https://github.com/ie3-institute/pypsdm/issues/329)
9+
- Adding congestion result handling [#198](https://github.com/ie3-institute/pypsdm/issues/198)
910
- Using `NBVAL` as validation for jupyter notebooks [#351](https://github.com/ie3-institute/pypsdm/issues/351)
1011

1112
### Changed

docs/nbs/result_models.ipynb

Lines changed: 161 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,23 @@
1515
},
1616
{
1717
"cell_type": "code",
18-
"execution_count": 2,
19-
"metadata": {},
18+
"execution_count": 1,
19+
"metadata": {
20+
"ExecuteTime": {
21+
"end_time": "2025-08-11T10:35:43.558159Z",
22+
"start_time": "2025-08-11T10:35:39.074640Z"
23+
}
24+
},
2025
"outputs": [
2126
{
2227
"name": "stderr",
2328
"output_type": "stream",
2429
"text": [
25-
"\u001B[32m2024-04-30 11:20:03.169\u001B[0m | \u001B[1mINFO \u001B[0m | \u001B[36mpypsdm.models.gwr\u001B[0m:\u001B[36mfrom_csv\u001B[0m:\u001B[36m275\u001B[0m - \u001B[1mReading grid from /Users/thomas/coding/python/pypsdm/pypsdm-feature/tests/resources/simbench/input\u001B[0m\n",
26-
"\u001B[32m2024-04-30 11:20:03.344\u001B[0m | \u001B[34m\u001B[1mDEBUG \u001B[0m | \u001B[36mpypsdm.models.primary_data\u001B[0m:\u001B[36mfrom_csv\u001B[0m:\u001B[36m266\u001B[0m - \u001B[34m\u001B[1mNo primary data in path /Users/thomas/coding/python/pypsdm/pypsdm-feature/tests/resources/simbench/input\u001B[0m\n",
27-
"\u001B[32m2024-04-30 11:20:03.344\u001B[0m | \u001B[1mINFO \u001B[0m | \u001B[36mpypsdm.models.gwr\u001B[0m:\u001B[36mfrom_csv\u001B[0m:\u001B[36m287\u001B[0m - \u001B[1mReading results from /Users/thomas/coding/python/pypsdm/pypsdm-feature/tests/resources/simbench/results\u001B[0m\n"
30+
"\u001b[32m2025-08-11 12:35:40.609\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mpypsdm.models.gwr\u001b[0m:\u001b[36mfrom_csv\u001b[0m:\u001b[36m293\u001b[0m - \u001b[1mReading grid from /home/smmsstau@ie3.e-technik.tu-dortmund.de/PycharmProjects/pypsdm/tests/resources/simbench/input\u001b[0m\n",
31+
"\u001b[32m2025-08-11 12:35:40.921\u001b[0m | \u001b[34m\u001b[1mDEBUG \u001b[0m | \u001b[36mpypsdm.models.primary_data\u001b[0m:\u001b[36mfrom_csv\u001b[0m:\u001b[36m273\u001b[0m - \u001b[34m\u001b[1mNo primary data in path /home/smmsstau@ie3.e-technik.tu-dortmund.de/PycharmProjects/pypsdm/tests/resources/simbench/input\u001b[0m\n",
32+
"\u001b[32m2025-08-11 12:35:40.922\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mpypsdm.models.gwr\u001b[0m:\u001b[36mfrom_csv\u001b[0m:\u001b[36m305\u001b[0m - \u001b[1mReading results from /home/smmsstau@ie3.e-technik.tu-dortmund.de/PycharmProjects/pypsdm/tests/resources/simbench/results\u001b[0m\n",
33+
"\u001b[32m2025-08-11 12:35:41.784\u001b[0m | \u001b[33m\u001b[1mWARNING \u001b[0m | \u001b[36mpypsdm.models.result.participant.dict\u001b[0m:\u001b[36mfrom_csv\u001b[0m:\u001b[36m76\u001b[0m - \u001b[33m\u001b[1mEntity 557b9f51-d83c-476c-a84c-d240530c203d not in input entities\u001b[0m\n",
34+
"\u001b[32m2025-08-11 12:35:41.789\u001b[0m | \u001b[33m\u001b[1mWARNING \u001b[0m | \u001b[36mpypsdm.models.result.participant.dict\u001b[0m:\u001b[36mfrom_csv\u001b[0m:\u001b[36m76\u001b[0m - \u001b[33m\u001b[1mEntity 5d50a881-c383-463e-8355-41b3dd57422d not in input entities\u001b[0m\n"
2835
]
2936
}
3037
],
@@ -7737,51 +7744,16 @@
77377744
},
77387745
{
77397746
"cell_type": "code",
7747+
"execution_count": 41,
77407748
"metadata": {
77417749
"ExecuteTime": {
77427750
"end_time": "2025-08-05T17:12:52.846724Z",
77437751
"start_time": "2025-08-05T17:12:52.809506Z"
77447752
}
77457753
},
7746-
"source": [
7747-
"# NBVAL_IGNORE_OUTPUT\n",
7748-
"# Let's say you want to make sure that all the load is actually connected to the\n",
7749-
"# node that we have filtered for\n",
7750-
"load_uuids = list(nodal_gwr.loads.keys())\n",
7751-
"\n",
7752-
"# You can get a subset of the input data model via a list of uuids\n",
7753-
"# Note that the `node` attribute confirms that indeed the load is connected\n",
7754-
"# to the node we have filtered for\n",
7755-
"gwr.loads.subset(load_uuids[0]).data"
7756-
],
77577754
"outputs": [
77587755
{
77597756
"data": {
7760-
"text/plain": [
7761-
" cos_phi_rated e_cons_annual \\\n",
7762-
"uuid \n",
7763-
"a0330517-9705-4d0a-bcaf-71f203cd6187 0.93 0.0 \n",
7764-
"\n",
7765-
" id \\\n",
7766-
"uuid \n",
7767-
"a0330517-9705-4d0a-bcaf-71f203cd6187 MV3.101 Load 61 \n",
7768-
"\n",
7769-
" load_profile \\\n",
7770-
"uuid \n",
7771-
"a0330517-9705-4d0a-bcaf-71f203cd6187 No load profile assigned \n",
7772-
"\n",
7773-
" node \\\n",
7774-
"uuid \n",
7775-
"a0330517-9705-4d0a-bcaf-71f203cd6187 090d13e8-3cce-4793-816f-4c50f23f3f7f \n",
7776-
"\n",
7777-
" operates_from operates_until operator \\\n",
7778-
"uuid \n",
7779-
"a0330517-9705-4d0a-bcaf-71f203cd6187 NaN NaN NaN \n",
7780-
"\n",
7781-
" q_characteristics s_rated \n",
7782-
"uuid \n",
7783-
"a0330517-9705-4d0a-bcaf-71f203cd6187 cosPhiFixed:{(0.0,0.93)} 668.0 "
7784-
],
77857757
"text/html": [
77867758
"<div>\n",
77877759
"<style scoped>\n",
@@ -7843,14 +7815,49 @@
78437815
" </tbody>\n",
78447816
"</table>\n",
78457817
"</div>"
7818+
],
7819+
"text/plain": [
7820+
" cos_phi_rated e_cons_annual \\\n",
7821+
"uuid \n",
7822+
"a0330517-9705-4d0a-bcaf-71f203cd6187 0.93 0.0 \n",
7823+
"\n",
7824+
" id \\\n",
7825+
"uuid \n",
7826+
"a0330517-9705-4d0a-bcaf-71f203cd6187 MV3.101 Load 61 \n",
7827+
"\n",
7828+
" load_profile \\\n",
7829+
"uuid \n",
7830+
"a0330517-9705-4d0a-bcaf-71f203cd6187 No load profile assigned \n",
7831+
"\n",
7832+
" node \\\n",
7833+
"uuid \n",
7834+
"a0330517-9705-4d0a-bcaf-71f203cd6187 090d13e8-3cce-4793-816f-4c50f23f3f7f \n",
7835+
"\n",
7836+
" operates_from operates_until operator \\\n",
7837+
"uuid \n",
7838+
"a0330517-9705-4d0a-bcaf-71f203cd6187 NaN NaN NaN \n",
7839+
"\n",
7840+
" q_characteristics s_rated \n",
7841+
"uuid \n",
7842+
"a0330517-9705-4d0a-bcaf-71f203cd6187 cosPhiFixed:{(0.0,0.93)} 668.0 "
78467843
]
78477844
},
78487845
"execution_count": 41,
78497846
"metadata": {},
78507847
"output_type": "execute_result"
78517848
}
78527849
],
7853-
"execution_count": 41
7850+
"source": [
7851+
"# NBVAL_IGNORE_OUTPUT\n",
7852+
"# Let's say you want to make sure that all the load is actually connected to the\n",
7853+
"# node that we have filtered for\n",
7854+
"load_uuids = list(nodal_gwr.loads.keys())\n",
7855+
"\n",
7856+
"# You can get a subset of the input data model via a list of uuids\n",
7857+
"# Note that the `node` attribute confirms that indeed the load is connected\n",
7858+
"# to the node we have filtered for\n",
7859+
"gwr.loads.subset(load_uuids[0]).data"
7860+
]
78547861
},
78557862
{
78567863
"cell_type": "markdown",
@@ -7860,6 +7867,118 @@
78607867
"\n",
78617868
"Check out the `docs/nbs/plots.ipynb` notebook for some examples of the included plotting utilities\n"
78627869
]
7870+
},
7871+
{
7872+
"cell_type": "markdown",
7873+
"metadata": {},
7874+
"source": [
7875+
"## Congestion Results\n",
7876+
"\n",
7877+
"If we want to analyze congestions in the grid, we can run SIMONA with congestion detection. All congestion results are mapped uuid of the asset for which the congestion was detected. The type information (e.g.: node, line, ect.) specifies type of the asset that has a congestion.\n",
7878+
"\n",
7879+
"Each congestion contains the value that occurred and the limits for the asset (e.g.: voltage band, line current limit, etc.)."
7880+
]
7881+
},
7882+
{
7883+
"cell_type": "code",
7884+
"execution_count": 2,
7885+
"metadata": {
7886+
"ExecuteTime": {
7887+
"end_time": "2025-08-11T10:35:47.649212Z",
7888+
"start_time": "2025-08-11T10:35:47.631440Z"
7889+
}
7890+
},
7891+
"outputs": [
7892+
{
7893+
"data": {
7894+
"text/html": [
7895+
"<div>\n",
7896+
"<style scoped>\n",
7897+
" .dataframe tbody tr th:only-of-type {\n",
7898+
" vertical-align: middle;\n",
7899+
" }\n",
7900+
"\n",
7901+
" .dataframe tbody tr th {\n",
7902+
" vertical-align: top;\n",
7903+
" }\n",
7904+
"\n",
7905+
" .dataframe thead th {\n",
7906+
" text-align: right;\n",
7907+
" }\n",
7908+
"</style>\n",
7909+
"<table border=\"1\" class=\"dataframe\">\n",
7910+
" <thead>\n",
7911+
" <tr style=\"text-align: right;\">\n",
7912+
" <th></th>\n",
7913+
" <th>max</th>\n",
7914+
" <th>min</th>\n",
7915+
" <th>subgrid</th>\n",
7916+
" <th>type</th>\n",
7917+
" <th>value</th>\n",
7918+
" </tr>\n",
7919+
" <tr>\n",
7920+
" <th>time</th>\n",
7921+
" <th></th>\n",
7922+
" <th></th>\n",
7923+
" <th></th>\n",
7924+
" <th></th>\n",
7925+
" <th></th>\n",
7926+
" </tr>\n",
7927+
" </thead>\n",
7928+
" <tbody>\n",
7929+
" <tr>\n",
7930+
" <th>2016-01-02 02:00:00</th>\n",
7931+
" <td>103</td>\n",
7932+
" <td>97</td>\n",
7933+
" <td>135</td>\n",
7934+
" <td>node</td>\n",
7935+
" <td>103.222590</td>\n",
7936+
" </tr>\n",
7937+
" <tr>\n",
7938+
" <th>2016-01-04 10:00:00</th>\n",
7939+
" <td>103</td>\n",
7940+
" <td>97</td>\n",
7941+
" <td>135</td>\n",
7942+
" <td>node</td>\n",
7943+
" <td>103.257236</td>\n",
7944+
" </tr>\n",
7945+
" <tr>\n",
7946+
" <th>2016-01-04 11:00:00</th>\n",
7947+
" <td>103</td>\n",
7948+
" <td>97</td>\n",
7949+
" <td>135</td>\n",
7950+
" <td>node</td>\n",
7951+
" <td>103.308051</td>\n",
7952+
" </tr>\n",
7953+
" </tbody>\n",
7954+
"</table>\n",
7955+
"</div>"
7956+
],
7957+
"text/plain": [
7958+
" max min subgrid type value\n",
7959+
"time \n",
7960+
"2016-01-02 02:00:00 103 97 135 node 103.222590\n",
7961+
"2016-01-04 10:00:00 103 97 135 node 103.257236\n",
7962+
"2016-01-04 11:00:00 103 97 135 node 103.308051"
7963+
]
7964+
},
7965+
"execution_count": 2,
7966+
"metadata": {},
7967+
"output_type": "execute_result"
7968+
}
7969+
],
7970+
"source": [
7971+
"# Congestion results are only provided for two nodes\n",
7972+
"node_with_congestions = [\n",
7973+
" \"5d50a881-c383-463e-8355-41b3dd57422d\",\n",
7974+
" \"557b9f51-d83c-476c-a84c-d240530c203d\",\n",
7975+
"]\n",
7976+
"\n",
7977+
"congestion_res_1 = gwr.congestions_res[node_with_congestions[0]]\n",
7978+
"congestion_res_2 = gwr.congestions_res[node_with_congestions[1]]\n",
7979+
"\n",
7980+
"congestion_res_1.data"
7981+
]
78637982
}
78647983
],
78657984
"metadata": {

pypsdm/models/enums.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def get_plot_name(self):
4040
def get_result_type(self) -> type[TimeSeries]:
4141
# locally to avoid circular imports
4242
from pypsdm import FlexOption
43+
from pypsdm.models.result.grid.congestions import CongestionResult
4344
from pypsdm.models.result.grid.connector import ConnectorCurrent
4445
from pypsdm.models.result.grid.switch import SwitchResult
4546
from pypsdm.models.result.grid.transformer import Transformer2WResult
@@ -66,6 +67,8 @@ def get_result_type(self) -> type[TimeSeries]:
6667
return ConnectorCurrent
6768
case RawGridElementsEnum.SWITCH:
6869
return SwitchResult
70+
case RawGridElementsEnum.CONGESTION:
71+
return CongestionResult
6972
case _:
7073
raise NotImplementedError(
7174
f"Result type {self} not implemented yet!"
@@ -74,6 +77,7 @@ def get_result_type(self) -> type[TimeSeries]:
7477
raise ValueError(f"Entity type {self} not supported!")
7578

7679
def get_result_dict_type(self) -> Type["EntitiesResultDictMixin"]:
80+
from pypsdm.models.result.grid.congestions import CongestionsResult
7781
from pypsdm.models.result.grid.line import LinesResult
7882
from pypsdm.models.result.grid.node import NodesResult
7983
from pypsdm.models.result.grid.switch import SwitchesResult
@@ -100,6 +104,8 @@ def get_result_dict_type(self) -> Type["EntitiesResultDictMixin"]:
100104
return Transformers2WResult
101105
case RawGridElementsEnum.SWITCH:
102106
return SwitchesResult
107+
case RawGridElementsEnum.CONGESTION:
108+
return CongestionsResult
103109
case SystemParticipantsEnum.ELECTRIC_VEHICLE:
104110
return EvsResult
105111
case SystemParticipantsEnum.EV_CHARGING_STATION:
@@ -161,6 +167,7 @@ class RawGridElementsEnum(EntitiesEnum):
161167
TRANSFROMER_3_W = "transformer_3_w"
162168
SWITCH = "switch"
163169
MEASUREMENT_UNIT = "measurement_unit"
170+
CONGESTION = "congestion"
164171

165172

166173
class ThermalGridElementsEnum(EntitiesEnum):

pypsdm/models/gwr.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ def transformers_2_w_res(self):
112112
def switches_res(self):
113113
return self.raw_grid_res.switches
114114

115+
@property
116+
def congestions_res(self):
117+
return self.results.raw_grid.congestions
118+
115119
@property
116120
def participants_res(self):
117121
return self.results.participants

pypsdm/models/input/container/raw_grid.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ def get_with_enum(self, enum: RawGridElementsEnum):
8282
return self.transformers_2_w
8383
case RawGridElementsEnum.SWITCH:
8484
return self.switches
85+
case RawGridElementsEnum.CONGESTION:
86+
return (self.nodes, self.nodes, self.transformers_2_w)
8587
case _:
8688
raise ValueError(f"Unknown enum {enum}")
8789

pypsdm/models/result/container/grid.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,10 @@ def transformers_2w(self):
4141
def switches(self):
4242
return self.raw_grid.switches
4343

44+
@property
45+
def congestions(self):
46+
return self.raw_grid.congestions
47+
4448
@property
4549
def controlling_ems(self):
4650
return self.participants.controlling_ems

pypsdm/models/result/container/raw_grid.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pypsdm.models.result.grid.node import NodesResult
1111
from pypsdm.models.result.grid.switch import SwitchesResult
1212
from pypsdm.models.result.grid.transformer import Transformers2WResult
13+
from pypsdm.models.result.grid.congestions import CongestionsResult
1314
from pypsdm.models.ts.base import EntityKey
1415

1516

@@ -19,6 +20,7 @@ class RawGridResultContainer(ResultContainerMixin):
1920
lines: LinesResult
2021
transformers_2w: Transformers2WResult
2122
switches: SwitchesResult
23+
congestions: CongestionsResult
2224

2325
def __init__(self, dct):
2426
def get_or_empty(key: RawGridElementsEnum, dict_type):
@@ -37,6 +39,9 @@ def get_or_empty(key: RawGridElementsEnum, dict_type):
3739
RawGridElementsEnum.TRANSFORMER_2_W, Transformers2WResult
3840
)
3941
self.switches = get_or_empty(RawGridElementsEnum.SWITCH, SwitchesResult)
42+
self.congestions = get_or_empty(
43+
RawGridElementsEnum.CONGESTION, CongestionsResult
44+
)
4045

4146
def __len__(self):
4247
return sum(len(v) for v in self.to_dict().values())
@@ -55,6 +60,7 @@ def to_dict(self, include_empty: bool = False) -> dict:
5560
RawGridElementsEnum.LINE: self.lines,
5661
RawGridElementsEnum.TRANSFORMER_2_W: self.transformers_2w,
5762
RawGridElementsEnum.SWITCH: self.switches,
63+
RawGridElementsEnum.CONGESTION: self.congestions,
5864
}
5965
if not include_empty:
6066
res = {k: v for k, v in res.items() if v}

pypsdm/models/result/grid/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
from .congestions import CongestionResult
12
from .connector import ConnectorCurrent, ConnectorCurrentDict
23
from .line import LinesResult
34
from .node import NodesResult
45
from .switch import SwitchesResult, SwitchResult
56
from .transformer import Transformer2WResult, Transformers2WResult
67

78
__all__ = [
9+
"CongestionResult",
810
"ConnectorCurrent",
911
"ConnectorCurrentDict",
1012
"NodesResult",

0 commit comments

Comments
 (0)