grid-data-models 2.3.1__tar.gz → 2.3.3__tar.gz

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 (128) hide show
  1. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/.gitignore +3 -0
  2. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/PKG-INFO +6 -5
  3. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/README.md +1 -1
  4. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/pyproject.toml +4 -3
  5. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/cli/reducer.py +5 -3
  6. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/distribution_branch_base.py +0 -9
  7. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/distribution_system.py +29 -9
  8. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/enums.py +16 -0
  9. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/phase_capacitor_equipment.py +13 -4
  10. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/market/tariff.py +34 -7
  11. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/model_reduction/reducer.py +41 -17
  12. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/sys_functools.py +61 -46
  13. grid_data_models-2.3.3/src/gdm/distribution/upgrade_handler/from__2_3_1__to__2_3_2.py +15 -0
  14. grid_data_models-2.3.3/src/gdm/distribution/upgrade_handler/from__2_3_2__to__2_3_3.py +77 -0
  15. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/upgrade_handler.py +12 -0
  16. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/exceptions.py +9 -4
  17. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/server.py +436 -82
  18. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/version.py +1 -1
  19. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/LICENSE.txt +0 -0
  20. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/__init__.py +0 -0
  21. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/cli/cli.py +0 -0
  22. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/constants.py +0 -0
  23. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/dataset/__init__.py +0 -0
  24. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/dataset/cost_model.py +0 -0
  25. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/dataset/dataset_system.py +0 -0
  26. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/__init__.py +0 -0
  27. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/catalog_system.py +0 -0
  28. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/common/__init__.py +0 -0
  29. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/common/curve.py +0 -0
  30. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/common/limitset.py +0 -0
  31. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/common/sequence_pair.py +0 -0
  32. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/__init__.py +0 -0
  33. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/__init__.py +0 -0
  34. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/distribution_component_base.py +0 -0
  35. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/distribution_switch_base.py +0 -0
  36. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/distribution_transformer_base.py +0 -0
  37. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_battery.py +0 -0
  38. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_bus.py +0 -0
  39. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_capacitor.py +0 -0
  40. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_feeder.py +0 -0
  41. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_load.py +0 -0
  42. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_regulator.py +0 -0
  43. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_solar.py +0 -0
  44. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_substation.py +0 -0
  45. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_transformer.py +0 -0
  46. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_vsource.py +0 -0
  47. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/geometry_branch.py +0 -0
  48. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/matrix_impedance_branch.py +0 -0
  49. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/matrix_impedance_fuse.py +0 -0
  50. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/matrix_impedance_recloser.py +0 -0
  51. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/matrix_impedance_switch.py +0 -0
  52. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/components/sequence_impedance_branch.py +0 -0
  53. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/__init__.py +0 -0
  54. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/base/__init__.py +0 -0
  55. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/base/capacitor_controller_base.py +0 -0
  56. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/base/inverter_controller_base.py +0 -0
  57. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_capacitor_controller.py +0 -0
  58. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_inverter_controller.py +0 -0
  59. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_recloser_controller.py +0 -0
  60. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_regulator_controller.py +0 -0
  61. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_switch_controller.py +0 -0
  62. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/distribution_graph.py +0 -0
  63. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/__init__.py +0 -0
  64. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/bare_conductor_equipment.py +0 -0
  65. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/base/__init__.py +0 -0
  66. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/base/matrix_impedance_branch_equipment_base.py +0 -0
  67. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/battery_equipment.py +0 -0
  68. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/capacitor_equipment.py +0 -0
  69. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/concentric_cable_equipment.py +0 -0
  70. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/distribution_transformer_equipment.py +0 -0
  71. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/geometry_branch_equipment.py +0 -0
  72. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/inverter_equipment.py +0 -0
  73. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/load_equipment.py +0 -0
  74. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/matrix_impedance_branch_equipment.py +0 -0
  75. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/matrix_impedance_fuse_equipment.py +0 -0
  76. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/matrix_impedance_recloser_equipment.py +0 -0
  77. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/matrix_impedance_switch_equipment.py +0 -0
  78. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/phase_load_equipment.py +0 -0
  79. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/phase_voltagesource_equipment.py +0 -0
  80. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/recloser_controller_equipment.py +0 -0
  81. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/sequence_impedance_branch_equipment.py +0 -0
  82. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/solar_equipment.py +0 -0
  83. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/voltagesource_equipment.py +0 -0
  84. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/market/__init__.py +0 -0
  85. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/model_reduction/__init__.py +0 -0
  86. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/__init__.py +0 -0
  87. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_0_1__to__2_1_2.py +0 -0
  88. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_1_2__to__2_1_3.py +0 -0
  89. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_1_3__to__2_1_4.py +0 -0
  90. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_1_4__to__2_1_5.py +0 -0
  91. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_1_5__to__2_2_0.py +0 -0
  92. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_2_0__to__2_2_1.py +0 -0
  93. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_2_1__to__2_3_0.py +0 -0
  94. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_3_0__to__2_3_1.py +0 -0
  95. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/hashing_utils.py +0 -0
  96. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/__init__.py +0 -0
  97. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/exceptions.py +0 -0
  98. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/inspection/__init__.py +0 -0
  99. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/inspection/inspector.py +0 -0
  100. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/inspection/relationships.py +0 -0
  101. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/inspection/topology.py +0 -0
  102. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/knowledge/__init__.py +0 -0
  103. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/knowledge/documentation.py +0 -0
  104. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/operations/__init__.py +0 -0
  105. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/operations/merger.py +0 -0
  106. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/operations/splitter.py +0 -0
  107. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/schemas.py +0 -0
  108. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/utilities/__init__.py +0 -0
  109. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/utilities/subsystem.py +0 -0
  110. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/utilities/timeseries.py +0 -0
  111. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/validation/__init__.py +0 -0
  112. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/validation/auto_fixer.py +0 -0
  113. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/validation/diagnostics.py +0 -0
  114. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/validation/suggestions.py +0 -0
  115. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/mcp/version.py +0 -0
  116. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/quantities.py +0 -0
  117. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/__init__.py +0 -0
  118. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/__init__.py +0 -0
  119. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/base.py +0 -0
  120. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/building.py +0 -0
  121. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/overhead_line_segment.py +0 -0
  122. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/pole.py +0 -0
  123. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/pvsystem.py +0 -0
  124. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/transformer.py +0 -0
  125. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/underground_cable.py +0 -0
  126. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/components/underground_junction.py +0 -0
  127. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/structural/structural_system.py +0 -0
  128. {grid_data_models-2.3.1 → grid_data_models-2.3.3}/src/gdm/tracked_changes.py +0 -0
@@ -7,6 +7,8 @@ __pycache__/
7
7
  *.so
8
8
  *.code-workspace
9
9
  *.sqlite
10
+ *.db
11
+ *.sql
10
12
  *.ruff_cache/
11
13
 
12
14
  # Distribution / packaging
@@ -164,3 +166,4 @@ cython_debug/
164
166
  .qodo
165
167
  .DS_Store
166
168
  *.dump
169
+ uv.lock
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grid-data-models
3
- Version: 2.3.1
3
+ Version: 2.3.3
4
4
  Project-URL: Documentation, https://github.com/NREL-Distribution-Suites/grid-data-models#readme
5
5
  Project-URL: Issues, https://github.com/NREL-Distribution-Suites/grid-data-models/issues
6
6
  Project-URL: Source, https://github.com/NREL-Distribution-Suites/grid-data-models
@@ -13,9 +13,9 @@ Classifier: Programming Language :: Python :: 3.11
13
13
  Requires-Python: >=3.11
14
14
  Requires-Dist: geopandas
15
15
  Requires-Dist: importlib-metadata
16
- Requires-Dist: infrasys~=1.0
16
+ Requires-Dist: infrasys~=1.1
17
17
  Requires-Dist: networkx
18
- Requires-Dist: pandas~=2.2.3
18
+ Requires-Dist: pandas~=3.0.2
19
19
  Requires-Dist: plotly
20
20
  Requires-Dist: pydantic
21
21
  Requires-Dist: semver
@@ -30,12 +30,13 @@ Requires-Dist: pytest-doctestplus; extra == 'dev'
30
30
  Requires-Dist: ruff; extra == 'dev'
31
31
  Provides-Extra: doc
32
32
  Requires-Dist: autodoc-pydantic[erdantic]; extra == 'doc'
33
+ Requires-Dist: jupyter-book<3,>=2.1.4; extra == 'doc'
33
34
  Requires-Dist: myst-parser; extra == 'doc'
34
35
  Requires-Dist: pydata-sphinx-theme; extra == 'doc'
35
36
  Requires-Dist: sphinx; extra == 'doc'
36
37
  Requires-Dist: sphinxcontrib-mermaid; extra == 'doc'
37
38
  Provides-Extra: mcp
38
- Requires-Dist: mcp>=1.0.0; extra == 'mcp'
39
+ Requires-Dist: mcp>=1.27.0; extra == 'mcp'
39
40
  Description-Content-Type: text/markdown
40
41
 
41
42
 
@@ -61,7 +62,7 @@ In an effort to reduce code duplication and provide client packages a standard i
61
62
  - **Built-in validation layer:** Use of [pydantic](https://docs.pydantic.dev/latest/) allows us to validate model fields.
62
63
  - **Time series data management:** GDM uses [infrasys](https://github.nrel.gov/CADET/infrastructure_systems) package which enables [efficient time series data management](https://nrel.github.io/infrasys/explanation/time_series.html) by sharing arrays across components and offloading system memory. For example, we can attach time series power consumption data to a load profile.
63
64
  - **Built-in unit conversion:** GDM leverages [pint](https://pint.readthedocs.io/en/stable/) for unit conversion for power system quantities. For example, power, voltage, time, etc.
64
- - **JSON serialization/deserialization:** GDM uses [infrasys](https://github.com/NREL/infrasys) to serialize and deserialize distribution system components to/from JSON.
65
+ - **JSON serialization/deserialization:** GDM uses [infrasys](https://github.com/NatLabRockies/infrasys) to serialize and deserialize distribution system components to/from JSON.
65
66
  - **Track System Changes:** Supports tracking changes within a distribution model (both temporal and scenario-based static updates), enabling powerful scenario management capabilities.
66
67
  - **Graph-Based Analysis:** Exposes a connectivity graph using NetworkX, allowing advanced graph-based algorithms and visualizations.
67
68
  - **Interoperability:** Easily integrates with existing tools.
@@ -21,7 +21,7 @@ In an effort to reduce code duplication and provide client packages a standard i
21
21
  - **Built-in validation layer:** Use of [pydantic](https://docs.pydantic.dev/latest/) allows us to validate model fields.
22
22
  - **Time series data management:** GDM uses [infrasys](https://github.nrel.gov/CADET/infrastructure_systems) package which enables [efficient time series data management](https://nrel.github.io/infrasys/explanation/time_series.html) by sharing arrays across components and offloading system memory. For example, we can attach time series power consumption data to a load profile.
23
23
  - **Built-in unit conversion:** GDM leverages [pint](https://pint.readthedocs.io/en/stable/) for unit conversion for power system quantities. For example, power, voltage, time, etc.
24
- - **JSON serialization/deserialization:** GDM uses [infrasys](https://github.com/NREL/infrasys) to serialize and deserialize distribution system components to/from JSON.
24
+ - **JSON serialization/deserialization:** GDM uses [infrasys](https://github.com/NatLabRockies/infrasys) to serialize and deserialize distribution system components to/from JSON.
25
25
  - **Track System Changes:** Supports tracking changes within a distribution model (both temporal and scenario-based static updates), enabling powerful scenario management capabilities.
26
26
  - **Graph-Based Analysis:** Exposes a connectivity graph using NetworkX, allowing advanced graph-based algorithms and visualizations.
27
27
  - **Interoperability:** Easily integrates with existing tools.
@@ -24,18 +24,19 @@ dependencies = [
24
24
  "semver",
25
25
  "networkx",
26
26
  "pydantic",
27
- "infrasys~=1.0",
27
+ "infrasys~=1.1",
28
28
  "importlib_metadata",
29
29
  "typer",
30
- "pandas~=2.2.3",
30
+ "pandas~=3.0.2",
31
31
  "geopandas",
32
32
  "plotly",
33
33
  ]
34
34
 
35
35
  [project.optional-dependencies]
36
- mcp = ["mcp>=1.0.0"]
36
+ mcp = ["mcp>=1.27.0"]
37
37
  dev = ["pre-commit", "pytest", "pytest-cov", "pytest-doctestplus", "pytest-asyncio", "ruff", "docutils"]
38
38
  doc = [
39
+ "jupyter-book>=2.1.4,<3",
39
40
  "sphinx",
40
41
  "pydata-sphinx-theme",
41
42
  "myst-parser",
@@ -28,9 +28,11 @@ def reduce(
28
28
  reducer: Annotated[
29
29
  ReducerType, typer.Option("-r", "--reducer", help="Reducer type to apply.")
30
30
  ] = ReducerType.three_phase,
31
- timeseries: Annotated[
31
+ time_series: Annotated[
32
32
  bool,
33
- typer.Option("-ts", "--timeseries", help="Include timeseries data in the reduced system."),
33
+ typer.Option(
34
+ "-ts", "--time-series", help="Include time series data in the reduced system."
35
+ ),
34
36
  ] = False,
35
37
  ):
36
38
  """Reduce a GDM distribution system."""
@@ -46,6 +48,6 @@ def reduce(
46
48
  sys = DistributionSystem.from_json(gdm_file)
47
49
  reducer_func = {"three_phase": reduce_to_three_phase_system}
48
50
  new_sys_name = sys.name + "_reduced" if sys.name else None
49
- new_sys = reducer_func[reducer.value](sys, new_sys_name, timeseries)
51
+ new_sys = reducer_func[reducer.value](sys, new_sys_name, time_series)
50
52
  new_sys.to_json(target_path)
51
53
  typer.echo(str(target_path))
@@ -41,15 +41,6 @@ class DistributionBranchBase(InServiceDistributionComponentBase, ABC):
41
41
  msg = f"Conductor phase ({phase=}) does not match bus phases ({bus.phases=})"
42
42
  raise ValueError(msg)
43
43
 
44
- num_branch_phases = len(self.phases)
45
- if not any(num_branch_phases == len(bus.phases) for bus in self.buses):
46
- bus_phase_counts = {bus.name: len(bus.phases) for bus in self.buses}
47
- msg = (
48
- f"Number of branch phases ({num_branch_phases}) must match the number of phases "
49
- f"of at least one connected bus. Connected bus phase counts: {bus_phase_counts}"
50
- )
51
- raise ValueError(msg)
52
-
53
44
  if self.buses[0].name == self.buses[1].name:
54
45
  msg = (
55
46
  f"From bus {self.buses[0].name=} and to bus"
@@ -276,8 +276,10 @@ class DistributionSystem(System):
276
276
  self,
277
277
  bus_names: list[str],
278
278
  name: str,
279
- keep_timeseries: bool = False,
279
+ keep_time_series: bool = False,
280
+ keep_timeseries: bool | None = None,
280
281
  time_series_type: Type[TimeSeriesData] = SingleTimeSeries,
282
+ directed_graph: nx.DiGraph | None = None,
281
283
  ) -> "DistributionSystem":
282
284
  """Method to get subsystem from list of buses.
283
285
 
@@ -287,15 +289,23 @@ class DistributionSystem(System):
287
289
  List of bus names
288
290
  name: str
289
291
  Name of the subsystem.
290
- keep_timeseries: bool
291
- Set this flag to retain timeseries data associated with the component.
292
+ keep_time_series: bool
293
+ Set this flag to retain time series data associated with the component.
294
+ keep_timeseries: bool | None
295
+ Deprecated alias for keep_time_series.
292
296
  time_series_type: Type[TimeSeriesData]
293
297
  Type of time series data. Defaults to: SingleTimeSeries
298
+ directed_graph: nx.DiGraph | None
299
+ Optional precomputed directed graph for this system. When provided,
300
+ it is reused instead of rebuilding the directed graph.
294
301
  Returns
295
302
  -------
296
303
  DistributionSystem
297
304
  """
298
- tree = self.get_directed_graph()
305
+ if keep_timeseries is not None:
306
+ keep_time_series = keep_timeseries
307
+
308
+ tree = directed_graph if directed_graph is not None else self.get_directed_graph()
299
309
  subtree = tree.subgraph(bus_names)
300
310
  subtree_system = DistributionSystem(auto_add_composed_components=True, name=name)
301
311
  for u, v, _ in subtree.edges(data=True):
@@ -308,7 +318,7 @@ class DistributionSystem(System):
308
318
  parent_components = self.list_parent_components(self.get_component(DistributionBus, u))
309
319
  self._add_to_subsystem(subtree_system, parent_components, bus_names)
310
320
 
311
- if keep_timeseries:
321
+ if keep_time_series:
312
322
  for comp in subtree_system.get_components(
313
323
  Component,
314
324
  filter_func=lambda x: self.has_time_series(x, time_series_type=time_series_type),
@@ -470,7 +480,6 @@ class DistributionSystem(System):
470
480
  bus_1 = cycle[i]
471
481
  bus_2 = cycle[(i + 1) % len(cycle)]
472
482
  edge_data = self.get_undirected_graph().get_edge_data(bus_1, bus_2)
473
- print(bus_1, bus_2, edge_data)
474
483
  if edge_data:
475
484
  for key, data in edge_data.items():
476
485
  if issubclass(data.get("type"), MatrixImpedanceSwitch):
@@ -480,7 +489,10 @@ class DistributionSystem(System):
480
489
  break
481
490
  return list(set(switch_buses))
482
491
 
483
- def get_split_phase_mapping(self) -> dict[str, set[Phase]]:
492
+ def get_split_phase_mapping(
493
+ self,
494
+ directed_graph: nx.DiGraph | None = None,
495
+ ) -> dict[str, set[Phase]]:
484
496
  """Generates a mapping of components to their split-phase configurations.
485
497
 
486
498
  This method identifies distribution transformers with center-tapped windings and
@@ -494,6 +506,12 @@ class DistributionSystem(System):
494
506
  A dictionary where keys are component names and values are sets of phases
495
507
  associated with the high-voltage bus.
496
508
 
509
+ Parameters
510
+ ----------
511
+ directed_graph: nx.DiGraph | None
512
+ Optional precomputed directed graph for this system. When provided,
513
+ it is reused instead of rebuilding the directed graph.
514
+
497
515
  Notes
498
516
  -----
499
517
  - The method uses the directed graph representation of the distribution system to
@@ -505,7 +523,7 @@ class DistributionSystem(System):
505
523
  - Logs the process of identifying and mapping split-phase transformers.
506
524
  """
507
525
  split_phase_map = {}
508
- original_tree = self.get_directed_graph()
526
+ original_tree = directed_graph if directed_graph is not None else self.get_directed_graph()
509
527
  split_phase_trs: list[DistributionTransformer] = list(
510
528
  self.get_components(
511
529
  DistributionTransformer,
@@ -518,7 +536,9 @@ class DistributionSystem(System):
518
536
  }.pop()
519
537
  hv_bus = (set([bus.name for bus in tr.buses]) - set([lv_bus])).pop()
520
538
  lv_system = self.get_subsystem(
521
- list(nx.descendants(original_tree, lv_bus)) + [lv_bus], name=""
539
+ list(nx.descendants(original_tree, lv_bus)) + [lv_bus],
540
+ name="",
541
+ directed_graph=original_tree,
522
542
  )
523
543
  bus_model_types = self.get_model_types_with_field_type(DistributionBus)
524
544
  for model_type in bus_model_types:
@@ -118,6 +118,21 @@ class TOUPeriodType(str, Enum):
118
118
  MID_PEAK = "mid_peak"
119
119
 
120
120
 
121
+ class Month(str, Enum):
122
+ JANUARY = "january"
123
+ FEBRUARY = "february"
124
+ MARCH = "march"
125
+ APRIL = "april"
126
+ MAY = "may"
127
+ JUNE = "june"
128
+ JULY = "july"
129
+ AUGUST = "august"
130
+ SEPTEMBER = "september"
131
+ OCTOBER = "october"
132
+ NOVEMBER = "november"
133
+ DECEMBER = "december"
134
+
135
+
121
136
  class Season(str, Enum):
122
137
  SUMMER = "summer"
123
138
  WINTER = "winter"
@@ -128,6 +143,7 @@ class CustomerClass(str, Enum):
128
143
  RESIDENTIAL = "residential"
129
144
  COMMERCIAL = "commercial"
130
145
  INDUSTRIAL = "industrial"
146
+ UTILITY = "utility"
131
147
 
132
148
 
133
149
  class BillingDemandBasis(str, Enum):
@@ -54,13 +54,22 @@ class PhaseCapacitorEquipment(Component):
54
54
  def aggregate(
55
55
  cls, instances: list["PhaseCapacitorEquipment"], name: str
56
56
  ) -> "PhaseCapacitorEquipment":
57
+ resistance_inverse_sum = sum(
58
+ 1 / inst.resistance for inst in instances if inst.resistance.magnitude
59
+ )
60
+ reactance_inverse_sum = sum(
61
+ 1 / inst.reactance for inst in instances if inst.reactance.magnitude
62
+ )
63
+
57
64
  return PhaseCapacitorEquipment(
58
65
  name=name,
59
66
  rated_reactive_power=sum(inst.rated_reactive_power for inst in instances),
60
- resistance=1
61
- / sum(1 / inst.resistance if inst.resistance.magnitude else 0 for inst in instances),
62
- reactance=1
63
- / sum(1 / inst.reactance if inst.reactance.magnitude else 0 for inst in instances),
67
+ resistance=(
68
+ 1 / resistance_inverse_sum if resistance_inverse_sum else Resistance(0, "ohm")
69
+ ),
70
+ reactance=(
71
+ 1 / reactance_inverse_sum if reactance_inverse_sum else Reactance(0, "ohm")
72
+ ),
64
73
  num_banks=sum(inst.num_banks for inst in instances),
65
74
  num_banks_on=sum(inst.num_banks_on for inst in instances),
66
75
  )
@@ -4,7 +4,7 @@ from pydantic import Field, model_validator
4
4
  from typing import List, Optional
5
5
  from infrasys import Component
6
6
 
7
- from gdm.distribution.enums import BillingDemandBasis, CustomerClass, TOUPeriodType, Season
7
+ from gdm.distribution.enums import BillingDemandBasis, CustomerClass, Month, TOUPeriodType
8
8
 
9
9
 
10
10
  class TOURatePeriod(Component):
@@ -31,6 +31,9 @@ class TOURatePeriod(Component):
31
31
 
32
32
  class DemandCharge(Component):
33
33
  name: str = ""
34
+ months: List[Month] = Field(
35
+ ..., min_length=1, description="Months for which this demand charge applies"
36
+ )
34
37
  rate: float = Field(..., gt=0, description="Rate for demand charge in $/kW")
35
38
  billing_demand_basis: BillingDemandBasis = Field(
36
39
  ..., description="Basis for billing demand calculation"
@@ -39,9 +42,16 @@ class DemandCharge(Component):
39
42
  ..., description="Time periods when the demand charge applies"
40
43
  )
41
44
 
45
+ @model_validator(mode="after")
46
+ def check_no_duplicate_months(self) -> "DemandCharge":
47
+ if len(self.months) != len(set(self.months)):
48
+ raise ValueError("Duplicate months are not allowed within a single DemandCharge")
49
+ return self
50
+
42
51
  @classmethod
43
52
  def example(cls) -> "DemandCharge":
44
53
  return DemandCharge(
54
+ months=[Month.JUNE, Month.JULY, Month.AUGUST],
45
55
  rate=12.50,
46
56
  billing_demand_basis=BillingDemandBasis.PEAK_15MIN,
47
57
  time_applicability=[TOURatePeriod.example()],
@@ -50,13 +60,23 @@ class DemandCharge(Component):
50
60
 
51
61
  class SeasonalTOURates(Component):
52
62
  name: str = ""
53
- season: Season = Field(..., description="Season for the TOU rates")
54
- tou_periods: List[TOURatePeriod] = Field(..., description="List of TOU periods for the season")
63
+ months: List[Month] = Field(
64
+ ..., min_length=1, description="Months for which these TOU rates apply"
65
+ )
66
+ tou_periods: List[TOURatePeriod] = Field(
67
+ ..., description="List of TOU periods for the specified months"
68
+ )
69
+
70
+ @model_validator(mode="after")
71
+ def check_no_duplicate_months(self) -> "SeasonalTOURates":
72
+ if len(self.months) != len(set(self.months)):
73
+ raise ValueError("Duplicate months are not allowed within a single SeasonalTOURates")
74
+ return self
55
75
 
56
76
  @classmethod
57
77
  def example(cls) -> "SeasonalTOURates":
58
78
  return SeasonalTOURates(
59
- season=Season.SUMMER,
79
+ months=[Month.JUNE, Month.JULY, Month.AUGUST],
60
80
  tou_periods=[
61
81
  TOURatePeriod.example(),
62
82
  TOURatePeriod(
@@ -81,7 +101,7 @@ class TieredRate(Component):
81
101
 
82
102
  class FixedCharge(Component):
83
103
  name: str = ""
84
- amount: float = Field(..., gt=0, description="Amount of the fixed charge in $/month")
104
+ amount: float = Field(..., ge=0, description="Amount of the fixed charge in $/month")
85
105
  description: Optional[str] = Field(None, description="Description of the fixed charge")
86
106
 
87
107
  @classmethod
@@ -93,7 +113,7 @@ class DistributionTariff(Component):
93
113
  name: str = Field(..., description="Name of the tariff")
94
114
  utility: str = Field(..., description="Name of the utility company")
95
115
  customer_class: CustomerClass = Field(..., description="Customer class for the tariff")
96
- fixed_charge: FixedCharge = Field(..., description="Fixed charge for the tariff")
116
+ fixed_charge: Optional[FixedCharge] = Field(None, description="Fixed charge for the tariff")
97
117
  seasonal_tou: List[SeasonalTOURates] = Field(
98
118
  ..., description="Seasonal TOU rates for the tariff"
99
119
  )
@@ -104,6 +124,13 @@ class DistributionTariff(Component):
104
124
  None, description="List of tiered energy charges for the tariff"
105
125
  )
106
126
 
127
+ @model_validator(mode="after")
128
+ def check_no_overlapping_months(self) -> "DistributionTariff":
129
+ all_months = [m for entry in self.seasonal_tou for m in entry.months]
130
+ if len(all_months) != len(set(all_months)):
131
+ raise ValueError("A month must not appear in more than one SeasonalTOURates entry")
132
+ return self
133
+
107
134
  @classmethod
108
135
  def example(cls) -> "DistributionTariff":
109
136
  return DistributionTariff(
@@ -114,7 +141,7 @@ class DistributionTariff(Component):
114
141
  seasonal_tou=[
115
142
  SeasonalTOURates.example(),
116
143
  SeasonalTOURates(
117
- season=Season.WINTER,
144
+ months=[Month.DECEMBER, Month.JANUARY, Month.FEBRUARY],
118
145
  tou_periods=[
119
146
  TOURatePeriod.example(),
120
147
  TOURatePeriod(
@@ -14,9 +14,9 @@ from gdm.distribution.distribution_system import (
14
14
  )
15
15
  from gdm.distribution.enums import Phase
16
16
  from gdm.distribution.sys_functools import (
17
- get_aggregated_load_timeseries,
18
- get_aggregated_solar_timeseries,
19
- get_aggregated_battery_timeseries,
17
+ get_aggregated_load_time_series,
18
+ get_aggregated_solar_time_series,
19
+ get_aggregated_battery_time_series,
20
20
  )
21
21
 
22
22
 
@@ -74,23 +74,28 @@ def _reduce_system(
74
74
  dist_system: DistributionSystem,
75
75
  bus_subset: list[DistributionBus],
76
76
  name: str,
77
- agg_timeseries: bool = False,
77
+ agg_time_series: bool = False,
78
+ agg_timeseries: bool | None = None,
78
79
  time_series_type: Type[TimeSeriesData] = SingleTimeSeries,
79
80
  ) -> DistributionSystem:
81
+ if agg_timeseries is not None:
82
+ agg_time_series = agg_timeseries
83
+
84
+ original_tree = dist_system.get_directed_graph()
80
85
  reduced_system = dist_system.get_subsystem(
81
86
  bus_subset,
82
87
  name,
83
- keep_timeseries=agg_timeseries,
88
+ keep_time_series=agg_time_series,
84
89
  time_series_type=time_series_type,
90
+ directed_graph=original_tree,
85
91
  )
86
92
 
87
- split_phase_mapping = dist_system.get_split_phase_mapping()
88
- original_tree = dist_system.get_directed_graph()
93
+ split_phase_mapping = dist_system.get_split_phase_mapping(directed_graph=original_tree)
89
94
  reduced_network_tree = original_tree.subgraph(bus_subset)
90
95
  ts_agg_func_mapper: dict[Union[Type[DistributionLoad], Type[DistributionSolar]], Callable] = {
91
- DistributionLoad: get_aggregated_load_timeseries,
92
- DistributionSolar: get_aggregated_solar_timeseries,
93
- DistributionBattery: get_aggregated_battery_timeseries,
96
+ DistributionLoad: get_aggregated_load_time_series,
97
+ DistributionSolar: get_aggregated_solar_time_series,
98
+ DistributionBattery: get_aggregated_battery_time_series,
94
99
  }
95
100
  for node in reduced_network_tree.nodes():
96
101
  if reduced_network_tree.out_degree(node) < original_tree.out_degree(node):
@@ -103,7 +108,11 @@ def _reduce_system(
103
108
  for snode in nx.descendants(original_tree, successor)
104
109
  ] + list(sucessors_diff)
105
110
  subtree = original_tree.subgraph(successors_descendants)
106
- subtree_system = dist_system.get_subsystem(subtree.nodes, "")
111
+ subtree_system = dist_system.get_subsystem(
112
+ list(subtree.nodes),
113
+ "",
114
+ directed_graph=original_tree,
115
+ )
107
116
  model_types = subtree_system.get_model_types_with_field_type(DistributionBus)
108
117
  for model_type in model_types:
109
118
  agg_component = _get_aggregated_bus_component(
@@ -112,10 +121,9 @@ def _reduce_system(
112
121
  model_type=model_type,
113
122
  split_phase_mapping=split_phase_mapping,
114
123
  )
115
- # print(model_type.__name__, agg_component)
116
124
  reduced_system.add_component(agg_component)
117
125
  agg_comp = reduced_system.get_component(model_type, agg_component.name)
118
- if agg_timeseries:
126
+ if agg_time_series:
119
127
  comps = list(subtree_system.get_components(model_type))
120
128
  ts_metadata = dist_system.list_time_series_metadata(
121
129
  comps[0], time_series_type=time_series_type
@@ -135,18 +143,34 @@ def _reduce_system(
135
143
  def reduce_to_three_phase_system(
136
144
  dist_system: DistributionSystem,
137
145
  name: str,
138
- agg_timeseries: bool = False,
146
+ agg_time_series: bool = False,
147
+ agg_timeseries: bool | None = None,
139
148
  time_series_type: Type[TimeSeriesData] = SingleTimeSeries,
140
149
  ) -> DistributionSystem:
141
150
  three_phase_buses = _get_three_phase_buses(dist_system)
142
- return _reduce_system(dist_system, three_phase_buses, name, agg_timeseries, time_series_type)
151
+ return _reduce_system(
152
+ dist_system,
153
+ three_phase_buses,
154
+ name,
155
+ agg_time_series,
156
+ agg_timeseries,
157
+ time_series_type,
158
+ )
143
159
 
144
160
 
145
161
  def reduce_to_primary_system(
146
162
  dist_system: DistributionSystem,
147
163
  name: str,
148
- agg_timeseries: bool = False,
164
+ agg_time_series: bool = False,
165
+ agg_timeseries: bool | None = None,
149
166
  time_series_type: Type[TimeSeriesData] = SingleTimeSeries,
150
167
  ) -> DistributionSystem:
151
168
  primary_buses = _get_primary_buses(dist_system)
152
- return _reduce_system(dist_system, primary_buses, name, agg_timeseries, time_series_type)
169
+ return _reduce_system(
170
+ dist_system,
171
+ primary_buses,
172
+ name,
173
+ agg_time_series,
174
+ agg_timeseries,
175
+ time_series_type,
176
+ )