pandahub 0.3.8__zip → 0.3.9__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {pandahub-0.3.8 → pandahub-0.3.9}/CHANGELOG.md +6 -0
  2. {pandahub-0.3.8 → pandahub-0.3.9}/PKG-INFO +1 -1
  3. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/routers/net.py +3 -2
  4. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/routers/variants.py +3 -3
  5. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/PandaHub.py +198 -106
  6. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub.egg-info/PKG-INFO +1 -1
  7. {pandahub-0.3.8 → pandahub-0.3.9}/pyproject.toml +1 -1
  8. {pandahub-0.3.8 → pandahub-0.3.9}/test/performance_test.py +8 -8
  9. {pandahub-0.3.8 → pandahub-0.3.9}/test/test_networks.py +12 -12
  10. {pandahub-0.3.8 → pandahub-0.3.9}/test/test_projects.py +2 -2
  11. {pandahub-0.3.8 → pandahub-0.3.9}/CONTRIBUTING.rst +0 -0
  12. {pandahub-0.3.8 → pandahub-0.3.9}/LICENSE +0 -0
  13. {pandahub-0.3.8 → pandahub-0.3.9}/MANIFEST.in +0 -0
  14. {pandahub-0.3.8 → pandahub-0.3.9}/README.md +0 -0
  15. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/__init__.py +0 -0
  16. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/__init__.py +0 -0
  17. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/dependencies.py +0 -0
  18. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/internal/__init__.py +0 -0
  19. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/internal/db.py +0 -0
  20. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/internal/schemas.py +0 -0
  21. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/internal/settings.py +0 -0
  22. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/internal/toolbox.py +0 -0
  23. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/internal/users.py +0 -0
  24. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/main.py +0 -0
  25. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/routers/__init__.py +0 -0
  26. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/routers/auth.py +0 -0
  27. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/routers/projects.py +0 -0
  28. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/routers/timeseries.py +0 -0
  29. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/api/routers/users.py +0 -0
  30. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/__init__.py +0 -0
  31. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/database_toolbox.py +0 -0
  32. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/datatypes.py +0 -0
  33. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/mongodb_indexes.py +0 -0
  34. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/timeseries/__init__.py +0 -0
  35. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/timeseries/data_sources/__init__.py +0 -0
  36. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/timeseries/data_sources/mongo_data.py +0 -0
  37. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub/lib/timeseries/output_writer_mongodb.py +0 -0
  38. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub.egg-info/SOURCES.txt +0 -0
  39. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub.egg-info/dependency_links.txt +0 -0
  40. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub.egg-info/requires.txt +0 -0
  41. {pandahub-0.3.8 → pandahub-0.3.9}/pandahub.egg-info/top_level.txt +0 -0
  42. {pandahub-0.3.8 → pandahub-0.3.9}/requirements.txt +0 -0
  43. {pandahub-0.3.8 → pandahub-0.3.9}/setup.cfg +0 -0
  44. {pandahub-0.3.8 → pandahub-0.3.9}/setup.py +0 -0
  45. {pandahub-0.3.8 → pandahub-0.3.9}/test/__init__.py +0 -0
  46. {pandahub-0.3.8 → pandahub-0.3.9}/test/conftest.py +0 -0
  47. {pandahub-0.3.8 → pandahub-0.3.9}/test/test_client.py +0 -0
  48. {pandahub-0.3.8 → pandahub-0.3.9}/test/test_timeseries.py +0 -0
  49. {pandahub-0.3.8 → pandahub-0.3.9}/tutorials/config.py +0 -0
@@ -1,5 +1,11 @@
1
1
  # Change Log
2
2
 
3
+ ## [0.3.9]
4
+
5
+ - BREAKING: inserting multiple networks with the same name does not represent an error anymore, networks are only unique by their net_id (_id field of the collection)
6
+ - BREAKING: passing net_id as str will not remap to the project name internally but look up on the _id field
7
+ - IMPROVED: fix race condition when calling write_network_to_db without net_id
8
+
3
9
  ## [0.3.8]
4
10
 
5
11
  - BREAKING: specifying variant as list will raise an error instead of a deprecation warning
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pandahub
3
- Version: 0.3.8
3
+ Version: 0.3.9
4
4
  Summary: Data hub for pandapower and pandapipes networks based on MongoDB
5
5
  Author-email: Jan Ulffers <jan.ulffers@iee.fraunhofer.de>, Leon Thurner <leon.thurner@retoflow.de>, Jannis Kupka <jannis.kupka@retoflow.de>, Mike Vogt <mike.vogt@iee.fraunhofer.de>, Joschka Thurner <joschka.thurner@retoflow.de>, Alexander Scheidler <alexander.scheidler@iee.fraunhofer.de>
6
6
  License: Copyright (c) 2022 by University of Kassel, Fraunhofer Institute for Energy Economics
@@ -1,6 +1,7 @@
1
1
  from typing import Optional, Any
2
2
 
3
3
  import pandapower as pp
4
+ import pandapipes as pps
4
5
  from fastapi import APIRouter, Depends
5
6
  from pydantic import BaseModel
6
7
 
@@ -25,7 +26,7 @@ class GetNetFromDB(BaseModel):
25
26
 
26
27
  @router.post("/get_net_from_db")
27
28
  def get_net_from_db(data: GetNetFromDB, ph=Depends(pandahub)):
28
- net = ph.get_net_from_db(**data.model_dump())
29
+ net = ph.get_network_by_name(**data.model_dump())
29
30
  return pp.to_json(net)
30
31
 
31
32
 
@@ -49,7 +50,7 @@ def write_network_to_db(data: WriteNetwork, ph=Depends(pandahub)):
49
50
 
50
51
  class BaseCRUDModel(BaseModel):
51
52
  project_id: str
52
- net: str
53
+ net_id: int | str
53
54
  element_type: str
54
55
 
55
56
  class GetNetValueModel(BaseCRUDModel):
@@ -17,7 +17,7 @@ router = APIRouter(
17
17
 
18
18
  class GetVariantsModel(BaseModel):
19
19
  project_id: str
20
- net_id: int
20
+ net_id: int | str
21
21
 
22
22
  @router.post("/get_variants")
23
23
  def get_variants(data: GetVariantsModel, ph=Depends(pandahub)):
@@ -40,7 +40,7 @@ def create_variant(data: CreateVariantModel, ph=Depends(pandahub)):
40
40
 
41
41
  class DeleteVariantModel(BaseModel):
42
42
  project_id: str
43
- net_id: int
43
+ net_id: int | str
44
44
  index: int
45
45
 
46
46
  @router.post("/delete_variant")
@@ -51,7 +51,7 @@ def delete_variant(data: DeleteVariantModel, ph=Depends(pandahub)):
51
51
 
52
52
  class UpdateVariantModel(BaseModel):
53
53
  project_id: str
54
- net_id: int
54
+ net_id: int | str
55
55
  index: int
56
56
  data: dict
57
57
 
@@ -16,7 +16,7 @@ from functools import reduce
16
16
  from operator import getitem
17
17
  from uuid import UUID
18
18
  from pymongo import MongoClient, ReplaceOne
19
- from pymongo.errors import ServerSelectionTimeoutError
19
+ from pymongo.errors import ServerSelectionTimeoutError, DuplicateKeyError
20
20
 
21
21
  import pandapipes as pps
22
22
  from pandapipes import from_json_string as from_json_pps, FromSerializableRegistryPpipe
@@ -707,7 +707,8 @@ class PandaHub:
707
707
  db = self._get_project_database()
708
708
  return list(db["_networks"].find())
709
709
 
710
- def get_net_from_db(
710
+
711
+ def get_network_by_name(
711
712
  self,
712
713
  name,
713
714
  include_results=True,
@@ -721,16 +722,16 @@ class PandaHub:
721
722
  self.set_active_project_by_id(project_id)
722
723
  self.check_permission("read")
723
724
  db = self._get_project_database()
724
- _id = self._get_id_from_name(name, db)
725
- if _id is None:
725
+ net_id = self._get_net_id_from_name(name, db)
726
+ if net_id is None:
726
727
  return None
727
- return self.get_net_from_db_by_id(
728
- _id, include_results, only_tables, geo_mode=geo_mode, variant=variant, convert=convert
728
+ return self.get_network(
729
+ net_id, include_results, only_tables, geo_mode=geo_mode, variant=variant, convert=convert
729
730
  )
730
731
 
731
- def get_net_from_db_by_id(
732
+ def get_network(
732
733
  self,
733
- id,
734
+ net_id,
734
735
  include_results=True,
735
736
  only_tables=None,
736
737
  convert=True,
@@ -739,7 +740,7 @@ class PandaHub:
739
740
  ):
740
741
  self.check_permission("read")
741
742
  return self._get_net_from_db_by_id(
742
- id,
743
+ net_id,
743
744
  include_results,
744
745
  only_tables,
745
746
  convert=convert,
@@ -749,7 +750,7 @@ class PandaHub:
749
750
 
750
751
  def _get_net_from_db_by_id(
751
752
  self,
752
- id_,
753
+ net_id,
753
754
  include_results=True,
754
755
  only_tables=None,
755
756
  convert=True,
@@ -757,20 +758,20 @@ class PandaHub:
757
758
  variant=None,
758
759
  ):
759
760
  db = self._get_project_database()
760
- meta = self._get_network_metadata(db, id_)
761
+ meta = self._get_network_metadata(db, net_id)
761
762
 
762
763
  package = pp if meta.get("sector", "power") == "power" else pps
763
764
  net = package.create_empty_network()
764
765
 
765
766
  # add all elements that are stored as dataframes
766
- collection_names = self._get_net_collections(db)
767
+ collection_names = self.get_net_collections(db)
767
768
  for collection_name in collection_names:
768
769
  el = self._element_name_of_collection(collection_name)
769
770
  self._add_element_from_collection(
770
771
  net,
771
772
  db,
772
773
  el,
773
- id_,
774
+ net_id,
774
775
  include_results=include_results,
775
776
  only_tables=only_tables,
776
777
  geo_mode=geo_mode,
@@ -797,7 +798,8 @@ class PandaHub:
797
798
  value = json.loads(value[11:], cls=io_pp.PPJSONDecoder, registry_class=registry)
798
799
  net[key] = value
799
800
 
800
- def get_subnet_from_db(
801
+
802
+ def get_subnet_by_name(
801
803
  self,
802
804
  name,
803
805
  bus_filter=None,
@@ -811,11 +813,11 @@ class PandaHub:
811
813
  ):
812
814
  self.check_permission("read")
813
815
  db = self._get_project_database()
814
- _id = self._get_id_from_name(name, db)
815
- if _id is None:
816
+ net_id = self._get_net_id_from_name(name, db)
817
+ if net_id is None:
816
818
  return None
817
- return self.get_subnet_from_db_by_id(
818
- _id,
819
+ return self.get_subnet(
820
+ net_id,
819
821
  bus_filter=bus_filter,
820
822
  include_results=include_results,
821
823
  add_edge_branches=add_edge_branches,
@@ -824,7 +826,7 @@ class PandaHub:
824
826
  additional_filters=additional_filters,
825
827
  )
826
828
 
827
- def get_subnet_from_db_by_id(
829
+ def get_subnet(
828
830
  self,
829
831
  net_id,
830
832
  bus_filter=None,
@@ -1034,7 +1036,7 @@ class PandaHub:
1034
1036
  )
1035
1037
 
1036
1038
  # add all other collections
1037
- collection_names = self._get_net_collections(db)
1039
+ collection_names = self.get_net_collections(db)
1038
1040
  for collection in collection_names:
1039
1041
  table_name = self._element_name_of_collection(collection)
1040
1042
  # skip all element tables that we have already added
@@ -1072,33 +1074,67 @@ class PandaHub:
1072
1074
 
1073
1075
  def write_network_to_db(
1074
1076
  self,
1075
- net,
1076
- name,
1077
+ net: pp.pandapowerNet | pps.pandapipesNet,
1078
+ name: str,
1077
1079
  sector="power",
1078
- overwrite=True,
1079
- project_id=None,
1080
- metadata=None,
1081
- skip_results=False,
1082
- net_id=None,
1083
- ):
1080
+ overwrite: bool = True,
1081
+ project_id: str = None,
1082
+ metadata: dict = None,
1083
+ skip_results: bool = False,
1084
+ net_id: int | str = None,
1085
+ ) -> dict:
1086
+ """
1087
+ Write a pandapower or pandapipes network to the database.
1088
+
1089
+ A network is uniquely identified only by its net_id (multiple networks with the same name are allowed).
1090
+ If no net_id is passed, an incrementing integer id is generated.
1091
+ Raises an error if a passed net_id already exists in the database unless overwrite is set to True.
1092
+
1093
+
1094
+ Parameters
1095
+ ----------
1096
+ net:
1097
+ pandapower or pandapipes network
1098
+ name:
1099
+ name of the network
1100
+ sector:
1101
+ sector of the network
1102
+ overwrite:
1103
+ if True, an existing network with the same net_id will be overwritten
1104
+ project_id:
1105
+ id of the project (defaults to active project)
1106
+ metadata:
1107
+ additional metadata to be stored with the network
1108
+ skip_results:
1109
+ if True, results tables are not stored in the database
1110
+ net_id:
1111
+ id of the network. If None, a new integer id is generated
1112
+
1113
+ Returns
1114
+ -------
1115
+ dict
1116
+ metadata of the created network
1117
+ """
1084
1118
  if project_id:
1085
1119
  self.set_active_project_by_id(project_id)
1086
1120
  self.check_permission("write")
1087
1121
  db = self._get_project_database()
1088
-
1089
- # if not isinstance(net, pp.pandapowerNet) and not isinstance(net, pps.pandapipesNet):
1090
- # raise PandaHubError("net must be a pandapower or pandapipes object")
1091
-
1092
- if self._network_with_name_exists(name, db):
1093
- if overwrite:
1094
- self.delete_net_from_db(name)
1095
- else:
1096
- raise PandaHubError("Network name already exists")
1122
+ networks_coll = db["_networks"]
1097
1123
 
1098
1124
  if net_id is None:
1099
- max_id_network = db["_networks"].find_one(sort=[("_id", -1)])
1100
- net_id = 0 if max_id_network is None else max_id_network["_id"] + 1
1101
- db["_networks"].insert_one({"_id": net_id})
1125
+ net_id = self._get_int_index("_networks")
1126
+ else:
1127
+ try:
1128
+ networks_coll.insert_one({"_id": net_id})
1129
+ except DuplicateKeyError:
1130
+ if overwrite:
1131
+ self.delete_network(net_id)
1132
+ networks_coll.insert_one({"_id": net_id})
1133
+ else:
1134
+ msg = f"Network with net_id {net_id} already exists"
1135
+ raise PandaHubError(msg)
1136
+
1137
+ networks_coll.update_one({"_id": net_id}, {"$set": {"name": name, "sector": sector}})
1102
1138
 
1103
1139
  data = {}
1104
1140
  dtypes = {}
@@ -1128,14 +1164,12 @@ class PandaHub:
1128
1164
 
1129
1165
  # write network metadata
1130
1166
  network_data = {
1131
- "name": name,
1132
- "sector": sector,
1133
1167
  "dtypes": dtypes,
1134
1168
  "data": data,
1135
1169
  }
1136
1170
  if metadata is not None:
1137
1171
  network_data.update(metadata)
1138
- db["_networks"].update_one({"_id": net_id}, {"$set": network_data})
1172
+ networks_coll.update_one({"_id": net_id}, {"$set": network_data})
1139
1173
  return network_data | {"_id": net_id}
1140
1174
 
1141
1175
  def _write_net_collections_to_db(self, db, collections):
@@ -1151,16 +1185,22 @@ class PandaHub:
1151
1185
  db[collection_name].insert_many(element_data, ordered=False)
1152
1186
  # print(f"\nFAILED TO WRITE TABLE '{element_type}' TO DATABASE! (details above)")
1153
1187
 
1154
- def delete_net_from_db(self, name):
1155
- self.check_permission("write")
1188
+ def delete_network(self, net_id):
1189
+ if net_id is None:
1190
+ raise PandaHubError(f"No net_id was passed", 404)
1156
1191
  db = self._get_project_database()
1157
- _id = self._get_id_from_name(name, db)
1158
- if _id is None:
1159
- raise PandaHubError("Network does not exist", 404)
1160
- collection_names = self._get_net_collections(db) # TODO
1192
+ collection_names = self.get_net_collections(db)
1161
1193
  for collection_name in collection_names:
1162
- db[collection_name].delete_many({"net_id": _id})
1163
- db["_networks"].delete_one({"_id": _id})
1194
+ db[collection_name].delete_many({"net_id": net_id})
1195
+ db["_networks"].delete_one({"_id": net_id})
1196
+
1197
+ def delete_network_by_name(self, name):
1198
+ self.check_permission("write")
1199
+ db = self._get_project_database()
1200
+ net_id = self._get_net_id_from_name(name, db)
1201
+ if net_id is None:
1202
+ raise PandaHubError(f"Network with name {name} does not exist", 404)
1203
+ self.delete_network(net_id)
1164
1204
 
1165
1205
  def network_with_name_exists(self, name):
1166
1206
  self.check_permission("read")
@@ -1177,19 +1217,20 @@ class PandaHub:
1177
1217
  nets = pd.DataFrame(list(db.find(fi, projection=proj)))
1178
1218
  return nets
1179
1219
 
1180
- def _get_metadata_from_name(self, name, db):
1181
- return list(db["_networks"].find({"name": name}))
1182
-
1183
- def _get_id_from_name(self, name, db):
1184
- metadata = self._get_metadata_from_name(name, db)
1220
+ def _get_net_id_from_name(self, name, db):
1221
+ metadata = list(db["_networks"].find({"name": name}))
1185
1222
  if len(metadata) > 1:
1186
- raise PandaHubError("Duplicate Network!")
1223
+ msg = (f"Multiple networks with the name {metadata[0]['name']} found in the database. "
1224
+ f"Use the corresponding function which selects the network by net_id instead.")
1225
+ raise PandaHubError(msg)
1187
1226
  return None if len(metadata) == 0 else metadata[0]["_id"]
1188
1227
 
1189
1228
  def _network_with_name_exists(self, name, db):
1190
- return self._get_id_from_name(name, db) is not None
1229
+ return self._get_net_id_from_name(name, db) is not None
1191
1230
 
1192
- def _get_net_collections(self, db, with_areas=True):
1231
+ def get_net_collections(self, db=None, with_areas=True):
1232
+ if db is None:
1233
+ db = self._get_project_database()
1193
1234
  if with_areas:
1194
1235
  collection_filter = {"name": {"$regex": "^net_"}}
1195
1236
  else:
@@ -1268,16 +1309,12 @@ class PandaHub:
1268
1309
  # -------------------------
1269
1310
 
1270
1311
  def get_net_value_from_db(
1271
- self, net, element_type, element_index, parameter, variant=None, project_id=None
1312
+ self, net_id, element_type, element_index, parameter, variant=None, project_id=None
1272
1313
  ):
1273
1314
  if project_id:
1274
1315
  self.set_active_project_by_id(project_id)
1275
1316
  self.check_permission("write")
1276
1317
  db = self._get_project_database()
1277
- if isinstance(net, str):
1278
- net_id = self._get_id_from_name(net, db)
1279
- else:
1280
- net_id = net
1281
1318
 
1282
1319
  collection = self._collection_name_of_element(element_type)
1283
1320
  dtypes = self._datatypes.get(element_type)
@@ -1303,14 +1340,14 @@ class PandaHub:
1303
1340
  return document[parameter]
1304
1341
 
1305
1342
  def delete_element(
1306
- self, net, element_type, element_index, variant=None, project_id=None, **kwargs
1343
+ self, net_id, element_type, element_index, variant=None, project_id=None, **kwargs
1307
1344
  ) -> dict:
1308
1345
  """
1309
1346
  Delete an element from the database.
1310
1347
 
1311
1348
  Parameters
1312
1349
  ----------
1313
- net: str or int
1350
+ net_id: str or int
1314
1351
  Network to add elements to, either a name or numeric id
1315
1352
  element_type: str
1316
1353
  Name of the element type (e.g. bus, line)
@@ -1326,7 +1363,7 @@ class PandaHub:
1326
1363
  The deleted element as dict with all fields
1327
1364
  """
1328
1365
  return self.delete_elements(
1329
- net=net,
1366
+ net_id=net_id,
1330
1367
  element_type=element_type,
1331
1368
  element_indexes=[element_index],
1332
1369
  variant=variant,
@@ -1336,7 +1373,7 @@ class PandaHub:
1336
1373
 
1337
1374
  def delete_elements(
1338
1375
  self,
1339
- net: Union[int, str],
1376
+ net_id: Union[int, str],
1340
1377
  element_type: str,
1341
1378
  element_indexes: list[int],
1342
1379
  variant: Union[int, None] = None,
@@ -1348,7 +1385,7 @@ class PandaHub:
1348
1385
 
1349
1386
  Parameters
1350
1387
  ----------
1351
- net: str or int
1388
+ net_id: str or int
1352
1389
  Network to add elements to, either a name or numeric id
1353
1390
  element_type: str
1354
1391
  Name of the element type (e.g. bus, line)
@@ -1373,11 +1410,6 @@ class PandaHub:
1373
1410
  db = self._get_project_database()
1374
1411
  collection = self._collection_name_of_element(element_type)
1375
1412
 
1376
- if isinstance(net, str):
1377
- net_id = self._get_id_from_name(net, db)
1378
- else:
1379
- net_id = net
1380
-
1381
1413
  element_filter = {
1382
1414
  "index": {"$in": element_indexes},
1383
1415
  "net_id": int(net_id),
@@ -1405,7 +1437,7 @@ class PandaHub:
1405
1437
 
1406
1438
  def set_net_value_in_db(
1407
1439
  self,
1408
- net,
1440
+ net_id,
1409
1441
  element_type,
1410
1442
  element_index,
1411
1443
  parameter,
@@ -1423,10 +1455,6 @@ class PandaHub:
1423
1455
  if value is not None and dtypes is not None and parameter in dtypes:
1424
1456
  value = dtypes[parameter](value)
1425
1457
  collection = self._collection_name_of_element(element_type)
1426
- if isinstance(net, str):
1427
- net_id = self._get_id_from_name(net, db)
1428
- else:
1429
- net_id = net
1430
1458
  element_filter = {
1431
1459
  "index": element_index,
1432
1460
  "net_id": int(net_id),
@@ -1478,7 +1506,7 @@ class PandaHub:
1478
1506
 
1479
1507
  def set_object_attribute(
1480
1508
  self,
1481
- net,
1509
+ net_id,
1482
1510
  element_type,
1483
1511
  element_index,
1484
1512
  parameter,
@@ -1495,10 +1523,6 @@ class PandaHub:
1495
1523
  if dtypes is not None and parameter in dtypes:
1496
1524
  value = dtypes[parameter](value)
1497
1525
  collection = self._collection_name_of_element(element_type)
1498
- if isinstance(net, str):
1499
- net_id = self._get_id_from_name(net, db)
1500
- else:
1501
- net_id = net
1502
1526
 
1503
1527
  js = list(db[collection].find({"index": element_index, "net_id": net_id}))[0]
1504
1528
  obj = json_to_object(js["object"])
@@ -1544,7 +1568,7 @@ class PandaHub:
1544
1568
 
1545
1569
  def create_element(
1546
1570
  self,
1547
- net: Union[int, str],
1571
+ net_id: Union[int, str],
1548
1572
  element_type: str,
1549
1573
  element_index: int,
1550
1574
  element_data: dict,
@@ -1557,7 +1581,7 @@ class PandaHub:
1557
1581
 
1558
1582
  Parameters
1559
1583
  ----------
1560
- net: str or int
1584
+ net_id: str or int
1561
1585
  Network to add elements to, either as name or numeric id
1562
1586
  element_type: str
1563
1587
  Name of the element type (e.g. bus, line)
@@ -1575,7 +1599,7 @@ class PandaHub:
1575
1599
  The created element (element_data with added _id field)
1576
1600
  """
1577
1601
  return self.create_elements(
1578
- net=net,
1602
+ net_id=net_id,
1579
1603
  element_type=element_type,
1580
1604
  elements_data=[{"index": element_index, **element_data}],
1581
1605
  variant=variant,
@@ -1585,7 +1609,7 @@ class PandaHub:
1585
1609
 
1586
1610
  def create_elements(
1587
1611
  self,
1588
- net: Union[int, str],
1612
+ net_id: Union[int, str],
1589
1613
  element_type: str,
1590
1614
  elements_data: list[dict],
1591
1615
  variant: int | None = None,
@@ -1597,7 +1621,7 @@ class PandaHub:
1597
1621
 
1598
1622
  Parameters
1599
1623
  ----------
1600
- net: str or int
1624
+ net_id: str or int
1601
1625
  Network to add elements to, either a name or numeric id
1602
1626
  element_type: str
1603
1627
  Name of the element type (e.g. bus, line)
@@ -1621,10 +1645,6 @@ class PandaHub:
1621
1645
  var_data = {"var_type": "base", "not_in_var": [], "variant": None}
1622
1646
  else:
1623
1647
  var_data = {"var_type": "addition", "not_in_var": [], "variant": variant}
1624
- if isinstance(net, str):
1625
- net_id = self._get_id_from_name(net, db)
1626
- else:
1627
- net_id = net
1628
1648
 
1629
1649
  data = []
1630
1650
  for elm_data in elements_data:
@@ -1707,6 +1727,46 @@ class PandaHub:
1707
1727
  logger.info(f"creating indexes in {collection} collection")
1708
1728
  project_db[collection].create_indexes(indexes)
1709
1729
 
1730
+ def _get_int_index(self, collection: str, index_field: str = "_id", query_filter: dict | None = None,
1731
+ retries: int = 10) -> int:
1732
+ """Create a document in a collection with an incrementing integer value for the index_field key.
1733
+
1734
+ ! This function expects a unique index set in the database on collection.index_field, compound with the keys in query_filter (if not None) !
1735
+
1736
+ Parameters
1737
+ ----------
1738
+ collection:
1739
+ The collection to create the document in.
1740
+ index_field:
1741
+ The field holding the incrementing integer index.
1742
+ query_filter:
1743
+ A query filter to apply when searching for the highest existing index.
1744
+ retries:
1745
+ Number of retries to create the index in case of a race condition.
1746
+
1747
+ Returns
1748
+ -------
1749
+ The value of the created index.
1750
+ """
1751
+ query_filter = query_filter if query_filter is not None else {}
1752
+ coll = self.get_project_database(collection)
1753
+ for retry in range(retries):
1754
+ try:
1755
+ max_index = next(coll.find(query_filter, projection={index_field: 1})
1756
+ .sort(index_field, -1)
1757
+ .limit(1),
1758
+ None)
1759
+ index = 0 if max_index is None else int(max_index[0][index_field]) + 1
1760
+ coll.insert_one({index_field: index})
1761
+ break
1762
+ except DuplicateKeyError:
1763
+ continue
1764
+ else:
1765
+ msg = f"Failed to create integer index for field {index_field} in {collection}!"
1766
+ raise PandaHubError(msg)
1767
+ return index
1768
+
1769
+
1710
1770
  # -------------------------
1711
1771
  # Variants
1712
1772
  # -------------------------
@@ -1715,14 +1775,7 @@ class PandaHub:
1715
1775
  db = self._get_project_database()
1716
1776
  net_id = int(data["net_id"])
1717
1777
  if index is None:
1718
- max_index = list(
1719
- db["variant"]
1720
- .find({"net_id": net_id}, projection={"_id": 0, "index": 1})
1721
- .sort("index", -1)
1722
- .limit(1)
1723
- )
1724
- index = int(max_index[0]["index"]) + 1 if max_index else 1
1725
-
1778
+ index = self._get_int_index("variant", index_field="index", query_filter={"net_id": net_id})
1726
1779
  data["index"] = index
1727
1780
  if data.get("default_name") is not None and data.get("name") is None:
1728
1781
  data["name"] = data.pop("default_name") + " " + str(index)
@@ -1730,7 +1783,7 @@ class PandaHub:
1730
1783
  del data["_id"]
1731
1784
 
1732
1785
  if index == 1:
1733
- for coll in self._get_net_collections(db):
1786
+ for coll in self.get_net_collections(db):
1734
1787
  db[coll].update_many(
1735
1788
  {"$or": [{"var_type": None}, {"var_type": np.nan}]},
1736
1789
  {"$set": {"var_type": "base", "not_in_var": [], "variant": None}},
@@ -1739,7 +1792,7 @@ class PandaHub:
1739
1792
 
1740
1793
  def delete_variant(self, net_id, index):
1741
1794
  db = self._get_project_database()
1742
- collection_names = self._get_net_collections(db)
1795
+ collection_names = self.get_net_collections(db)
1743
1796
  for coll in collection_names:
1744
1797
  # remove references to deleted objects
1745
1798
  db[coll].update_many(
@@ -2976,7 +3029,7 @@ class PandaHub:
2976
3029
  "ph.create_element_in_db was renamed - use ph.create_element instead!"
2977
3030
  )
2978
3031
  return self.create_element(
2979
- net=net,
3032
+ net_id=net,
2980
3033
  element_type=element,
2981
3034
  element_index=element_index,
2982
3035
  element_data=data,
@@ -2997,7 +3050,7 @@ class PandaHub:
2997
3050
  "Watch out for changed order of project_id and variant args"
2998
3051
  )
2999
3052
  return self.create_elements(
3000
- net=net,
3053
+ net_id=net,
3001
3054
  element_type=element_type,
3002
3055
  elements_data=elements_data,
3003
3056
  variant=variant,
@@ -3011,13 +3064,52 @@ class PandaHub:
3011
3064
  "ph.delete_net_element was renamed - use ph.delete_element instead!"
3012
3065
  )
3013
3066
  return self.delete_element(
3014
- net=net,
3067
+ net_id=net,
3015
3068
  element_type=element,
3016
3069
  element_index=element_index,
3017
3070
  variant=variant,
3018
3071
  project_id=project_id,
3019
3072
  )
3020
3073
 
3074
+ def get_net_from_db(self, *args, **kwargs):
3075
+ msg = ("Getting a network by name can be ambiguous and will throw an error if more than one Network with the "
3076
+ "given name exists. Preferably, switch to get_network() and pass the network id. "
3077
+ "If you want to continue to retrieve a network by its name, switch to get_network_by_name(). "
3078
+ "get_net_from_db() will be removed in future versions.")
3079
+ warnings.warn(msg, DeprecationWarning)
3080
+ return self.get_network_by_name(*args, **kwargs)
3081
+
3082
+ def get_net_from_db_by_id(self, *args, **kwargs):
3083
+ msg = "get_net_from_db_by_id() has been renamed - use get_network() as drop-in replacement. This function will be removed in future versions."
3084
+ warnings.warn(msg, DeprecationWarning)
3085
+ self.get_network(*args, **kwargs)
3086
+
3087
+ def get_subnet_from_db(self, *args, **kwargs):
3088
+ msg = ("Getting a network by name can be ambiguous and will throw an error if more than one Network with the "
3089
+ "given name exists. Preferably, switch to get_subnet() and pass the network id. "
3090
+ "If you want to continue to retrieve a subnet by its name, switch to get_subnet_by_name(). "
3091
+ "get_subnet_from_db() will be removed in future versions.")
3092
+ warnings.warn(msg, DeprecationWarning)
3093
+ return self.get_subnet_by_name(*args, **kwargs)
3094
+
3095
+ def get_subnet_from_db_by_id(self,*args, **kwargs):
3096
+ msg = "get_subnet_from_db_by_id() has been renamed - use get_subnet() as drop-in replacement. This function will be removed in future versions."
3097
+ warnings.warn(msg, DeprecationWarning)
3098
+ return self.get_subnet(*args, **kwargs)
3099
+
3100
+ def delete_net_from_db(self, name):
3101
+ msg = ("Deleting a network by name can be ambiguous and will throw an error if more than one Network with the "
3102
+ "given name exists. Preferably, switch to delete_network() and pass the network id. "
3103
+ "If you want to continue to delete a net by its name, switch to delete_network_by_name(). "
3104
+ "delete_net_from_db() will be removed in future versions.")
3105
+ warnings.warn(msg, DeprecationWarning)
3106
+ return self.delete_network_by_name(name)
3107
+
3108
+ def _get_net_collections(self, db, with_areas=True):
3109
+ msg = "_get_net_collections() has been made public - use get_net_collections() as drop-in replacement. This function will be removed in future versions."
3110
+ warnings.warn(msg, DeprecationWarning)
3111
+ return self.get_net_collections(db, with_areas)
3112
+
3021
3113
 
3022
3114
  if __name__ == "__main__":
3023
3115
  self = PandaHub()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pandahub
3
- Version: 0.3.8
3
+ Version: 0.3.9
4
4
  Summary: Data hub for pandapower and pandapipes networks based on MongoDB
5
5
  Author-email: Jan Ulffers <jan.ulffers@iee.fraunhofer.de>, Leon Thurner <leon.thurner@retoflow.de>, Jannis Kupka <jannis.kupka@retoflow.de>, Mike Vogt <mike.vogt@iee.fraunhofer.de>, Joschka Thurner <joschka.thurner@retoflow.de>, Alexander Scheidler <alexander.scheidler@iee.fraunhofer.de>
6
6
  License: Copyright (c) 2022 by University of Kassel, Fraunhofer Institute for Energy Economics
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pandahub"
7
- version = "0.3.8" # File format version '__format_version__' is tracked in __init__.py
7
+ version = "0.3.9" # File format version '__format_version__' is tracked in __init__.py
8
8
  authors=[
9
9
  { name = "Jan Ulffers", email = "jan.ulffers@iee.fraunhofer.de" },
10
10
  { name = "Leon Thurner", email = "leon.thurner@retoflow.de" },
@@ -17,13 +17,13 @@ def get_test_net(n_buses):
17
17
  pp.create_sgens(net, net.bus.index[::50], p_mw=0, q_mvar=0)
18
18
  pp.create_gens(net, net.bus.index[::1000], p_mw=0, q_mvar=0)
19
19
  pp.create_lines(net, net.bus.index[::2], net.bus.index[1::2], 1.0, "NAYY 4x50 SE")
20
- pp.create_transformers_from_parameters(net, net.bus.index[::1000],
20
+ pp.create_transformers_from_parameters(net, net.bus.index[::1000],
21
21
  net.bus.index[1::1000],
22
22
  vn_hv_kv=20.,
23
23
  vn_lv_kv=0.4,
24
24
  sn_mva=0.4,
25
25
  vkr_percent=5.,
26
- vk_percent=0.5,
26
+ vk_percent=0.5,
27
27
  pfe_kw=0,
28
28
  i0_percent=0,
29
29
  )
@@ -31,7 +31,7 @@ def get_test_net(n_buses):
31
31
  line_indices = list(net.line.index) + list(net.line.index)
32
32
  pp.create_switches(net, line_buses, line_indices, "l")
33
33
  return net
34
-
34
+
35
35
  def write_test_net_to_mongodb(net, project_name):
36
36
  ph = PandaHub()
37
37
  if ph.project_exists(project_name):
@@ -39,14 +39,14 @@ def write_test_net_to_mongodb(net, project_name):
39
39
  ph.delete_project(True)
40
40
  ph.create_project(project_name)
41
41
  ph.write_network_to_db(net, "test_net")
42
-
42
+
43
43
  def load_test_subnet_from_mongodb(project_name):
44
44
  ph = PandaHub()
45
45
  ph.set_active_project(project_name)
46
46
  buses = list(range(1000))
47
47
  t0 = time.time()
48
- subnet = ph.get_subnet_from_db("test_net",
49
- bus_filter={"index": {"$in": buses}})
48
+ subnet = ph.get_subnet_by_name("test_net",
49
+ bus_filter={"index": {"$in": buses}})
50
50
  t1 = time.time()
51
51
  print("LOADED NET IN %.2fs"%(t1-t0))
52
52
  return subnet
@@ -54,11 +54,11 @@ def load_test_subnet_from_mongodb(project_name):
54
54
  def profile_load_test(project_name):
55
55
  lp = LineProfiler()
56
56
  lp_wrapper = lp(load_test_subnet_from_mongodb)
57
- lp.add_function(PandaHub.get_subnet_from_db_by_id)
57
+ lp.add_function(PandaHub.get_subnet)
58
58
  lp.add_function(PandaHub._add_element_from_collection)
59
59
  lp_wrapper(project_name)
60
60
  lp.print_stats()
61
-
61
+
62
62
  if __name__ == '__main__':
63
63
  n_buses = 3e6 #3 Million buses
64
64
  project_name = "test_%u"%n_buses
@@ -21,7 +21,7 @@ def test_additional_res_tables(ph):
21
21
  net1 = pp.create_empty_network()
22
22
  net1['res_test'] = pd.DataFrame(data={'col1': [1, 2], 'col2': [3, 4]})
23
23
  ph.write_network_to_db(net1, 'test')
24
- net2 = ph.get_net_from_db('test')
24
+ net2 = ph.get_network_by_name('test')
25
25
 
26
26
  assert('res_test' in net2)
27
27
  assert(net1.res_test.shape == (2,2))
@@ -50,14 +50,14 @@ def test_network_io(ph):
50
50
  # we check storing two different networks consecutively to ensure the nets are properly separated
51
51
  for net, name in [(net1, name1), (net2, name2)]:
52
52
  if ph.network_with_name_exists(name):
53
- ph.delete_net_from_db(name)
53
+ ph.delete_network_by_name(name)
54
54
 
55
55
  assert ph.network_with_name_exists(name) == False
56
56
 
57
57
  ph.write_network_to_db(net, name)
58
58
  assert ph.network_with_name_exists(name) == True
59
59
 
60
- net_loaded = ph.get_net_from_db(name)
60
+ net_loaded = ph.get_network_by_name(name)
61
61
 
62
62
  pp.runpp(net)
63
63
  pp.runpp(net_loaded)
@@ -66,18 +66,18 @@ def test_network_io(ph):
66
66
  # doesn't compare to itself
67
67
  # assert pp.nets_equal(net, net_loaded, check_dtype=False)
68
68
 
69
- net3 = ph.get_net_from_db(name, only_tables=["bus"])
69
+ net3 = ph.get_network_by_name(name, only_tables=["bus"])
70
70
  assert len(net3.bus) == len(net.bus)
71
71
  assert len(net3.line) == 0
72
72
  assert len(net3.load) == 0
73
73
 
74
74
  # delete first network
75
- ph.delete_net_from_db(name1)
75
+ ph.delete_network_by_name(name1)
76
76
  assert ph.network_with_name_exists(name1) == False
77
77
 
78
78
  # check that second network is still in database
79
79
  assert ph.network_with_name_exists(name2) == True
80
- net2_loaded = ph.get_net_from_db(name2)
80
+ net2_loaded = ph.get_network_by_name(name2)
81
81
  pp.runpp(net2_loaded)
82
82
  assert pp.nets_equal(net2, net2_loaded, check_only_results=True)
83
83
 
@@ -90,20 +90,20 @@ def test_load_subnetwork(ph):
90
90
  net = nw_pp.mv_oberrhein()
91
91
  ph.write_network_to_db(net, name)
92
92
 
93
- subnet = ph.get_subnet_from_db(name, bus_filter={"vn_kv": 110})
93
+ subnet = ph.get_subnet_by_name(name, bus_filter={"vn_kv": 110})
94
94
  expected_sizes = [("bus", 4), ("line", 0), ("trafo", 2), ("ext_grid", 2)]
95
95
 
96
96
  for element, size in expected_sizes:
97
97
  assert len(subnet[element]) == size
98
98
  assert len(subnet["res_" + element]) == size
99
99
 
100
- subnet = ph.get_subnet_from_db(name, bus_filter={"vn_kv": 110},
100
+ subnet = ph.get_subnet_by_name(name, bus_filter={"vn_kv": 110},
101
101
  include_results=False)
102
102
  for element, size in expected_sizes:
103
103
  assert len(subnet[element]) == size
104
104
  assert len(subnet["res_" + element]) == 0
105
105
 
106
- subnet = ph.get_subnet_from_db(name, bus_filter={"vn_kv": 110},
106
+ subnet = ph.get_subnet_by_name(name, bus_filter={"vn_kv": 110},
107
107
  add_edge_branches=False)
108
108
  expected_sizes = [("bus", 2), ("line", 0), ("trafo", 0), ("ext_grid", 2)]
109
109
 
@@ -135,7 +135,7 @@ def test_access_and_set_single_values(ph):
135
135
  ph.delete_element(name, element, index)
136
136
  with pytest.raises(PandaHubError):
137
137
  ph.get_net_value_from_db(name, element, index, parameter)
138
- net = ph.get_net_from_db(name)
138
+ net = ph.get_network_by_name(name)
139
139
  assert index not in net[element].index
140
140
 
141
141
 
@@ -143,7 +143,7 @@ def test_pandapipes(ph):
143
143
  ph.set_active_project('pytest')
144
144
  net = nw_pps.gas_versatility()
145
145
  ph.write_network_to_db(net, 'versatility')
146
- net2 = ph.get_net_from_db('versatility', convert=False)
146
+ net2 = ph.get_network_by_name('versatility', convert=False)
147
147
  pps.pipeflow(net)
148
148
  pps.pipeflow(net2)
149
149
  assert nets_equal(net, net2, check_only_results=True)
@@ -167,7 +167,7 @@ if __name__ == '__main__':
167
167
  ph.create_project('pytest')
168
168
  net = nw_pps.gas_versatility()
169
169
  ph.write_network_to_db(net, 'versatility')
170
- net2 = ph.get_net_from_db('versatility')
170
+ net2 = ph.get_network_by_name('versatility')
171
171
  pps.pipeflow(net)
172
172
  pps.pipeflow(net2)
173
173
  # test_network_io(ph)
@@ -53,7 +53,7 @@ def test_upgrade_project():
53
53
  raise pandahub.PandaHubError("net must be a pandapower or pandapipes object")
54
54
  if self._network_with_name_exists(name, db):
55
55
  if overwrite:
56
- self.delete_net_from_db(name)
56
+ self.delete_network_by_name(name)
57
57
  else:
58
58
  raise pandahub.PandaHubError("Network name already exists")
59
59
  max_id_network = db["_networks"].find_one(sort=[("_id", -1)])
@@ -92,7 +92,7 @@ def test_upgrade_project():
92
92
  ph.set_active_project("pytest")
93
93
  ph.upgrade_project_to_latest_version()
94
94
  # and test if everything went fine
95
- net2 = ph.get_net_from_db("simple_network")
95
+ net2 = ph.get_network_by_name("simple_network")
96
96
  assert pp.nets_equal(net, net2, check_dtype=False)
97
97
 
98
98
 
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes