grid-data-models 2.3.2__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.2 → grid_data_models-2.3.3}/PKG-INFO +5 -5
  2. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/pyproject.toml +4 -4
  3. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/enums.py +1 -0
  4. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/market/tariff.py +33 -6
  5. grid_data_models-2.3.3/src/gdm/distribution/upgrade_handler/from__2_3_2__to__2_3_3.py +77 -0
  6. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/upgrade_handler.py +6 -0
  7. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/version.py +1 -1
  8. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/.gitignore +0 -0
  9. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/LICENSE.txt +0 -0
  10. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/README.md +0 -0
  11. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/__init__.py +0 -0
  12. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/cli/cli.py +0 -0
  13. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/cli/reducer.py +0 -0
  14. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/constants.py +0 -0
  15. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/dataset/__init__.py +0 -0
  16. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/dataset/cost_model.py +0 -0
  17. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/dataset/dataset_system.py +0 -0
  18. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/__init__.py +0 -0
  19. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/catalog_system.py +0 -0
  20. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/common/__init__.py +0 -0
  21. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/common/curve.py +0 -0
  22. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/common/limitset.py +0 -0
  23. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/common/sequence_pair.py +0 -0
  24. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/__init__.py +0 -0
  25. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/__init__.py +0 -0
  26. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/distribution_branch_base.py +0 -0
  27. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/distribution_component_base.py +0 -0
  28. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/distribution_switch_base.py +0 -0
  29. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/base/distribution_transformer_base.py +0 -0
  30. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_battery.py +0 -0
  31. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_bus.py +0 -0
  32. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_capacitor.py +0 -0
  33. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_feeder.py +0 -0
  34. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_load.py +0 -0
  35. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_regulator.py +0 -0
  36. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_solar.py +0 -0
  37. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_substation.py +0 -0
  38. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_transformer.py +0 -0
  39. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/distribution_vsource.py +0 -0
  40. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/geometry_branch.py +0 -0
  41. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/matrix_impedance_branch.py +0 -0
  42. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/matrix_impedance_fuse.py +0 -0
  43. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/matrix_impedance_recloser.py +0 -0
  44. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/matrix_impedance_switch.py +0 -0
  45. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/components/sequence_impedance_branch.py +0 -0
  46. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/__init__.py +0 -0
  47. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/base/__init__.py +0 -0
  48. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/base/capacitor_controller_base.py +0 -0
  49. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/base/inverter_controller_base.py +0 -0
  50. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_capacitor_controller.py +0 -0
  51. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_inverter_controller.py +0 -0
  52. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_recloser_controller.py +0 -0
  53. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_regulator_controller.py +0 -0
  54. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/controllers/distribution_switch_controller.py +0 -0
  55. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/distribution_graph.py +0 -0
  56. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/distribution_system.py +0 -0
  57. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/__init__.py +0 -0
  58. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/bare_conductor_equipment.py +0 -0
  59. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/base/__init__.py +0 -0
  60. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/base/matrix_impedance_branch_equipment_base.py +0 -0
  61. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/battery_equipment.py +0 -0
  62. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/capacitor_equipment.py +0 -0
  63. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/concentric_cable_equipment.py +0 -0
  64. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/distribution_transformer_equipment.py +0 -0
  65. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/geometry_branch_equipment.py +0 -0
  66. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/inverter_equipment.py +0 -0
  67. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/load_equipment.py +0 -0
  68. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/matrix_impedance_branch_equipment.py +0 -0
  69. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/matrix_impedance_fuse_equipment.py +0 -0
  70. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/matrix_impedance_recloser_equipment.py +0 -0
  71. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/matrix_impedance_switch_equipment.py +0 -0
  72. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/phase_capacitor_equipment.py +0 -0
  73. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/phase_load_equipment.py +0 -0
  74. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/phase_voltagesource_equipment.py +0 -0
  75. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/recloser_controller_equipment.py +0 -0
  76. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/sequence_impedance_branch_equipment.py +0 -0
  77. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/solar_equipment.py +0 -0
  78. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/equipment/voltagesource_equipment.py +0 -0
  79. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/market/__init__.py +0 -0
  80. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/model_reduction/__init__.py +0 -0
  81. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/model_reduction/reducer.py +0 -0
  82. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/sys_functools.py +0 -0
  83. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/__init__.py +0 -0
  84. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_0_1__to__2_1_2.py +0 -0
  85. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_1_2__to__2_1_3.py +0 -0
  86. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_1_3__to__2_1_4.py +0 -0
  87. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_1_4__to__2_1_5.py +0 -0
  88. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_1_5__to__2_2_0.py +0 -0
  89. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_2_0__to__2_2_1.py +0 -0
  90. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_2_1__to__2_3_0.py +0 -0
  91. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_3_0__to__2_3_1.py +0 -0
  92. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/distribution/upgrade_handler/from__2_3_1__to__2_3_2.py +0 -0
  93. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/exceptions.py +0 -0
  94. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/hashing_utils.py +0 -0
  95. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/__init__.py +0 -0
  96. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/exceptions.py +0 -0
  97. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/inspection/__init__.py +0 -0
  98. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/inspection/inspector.py +0 -0
  99. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/inspection/relationships.py +0 -0
  100. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/inspection/topology.py +0 -0
  101. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/knowledge/__init__.py +0 -0
  102. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/knowledge/documentation.py +0 -0
  103. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/operations/__init__.py +0 -0
  104. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/operations/merger.py +0 -0
  105. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/operations/splitter.py +0 -0
  106. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/schemas.py +0 -0
  107. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/server.py +0 -0
  108. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/utilities/__init__.py +0 -0
  109. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/utilities/subsystem.py +0 -0
  110. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/utilities/timeseries.py +0 -0
  111. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/validation/__init__.py +0 -0
  112. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/validation/auto_fixer.py +0 -0
  113. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/validation/diagnostics.py +0 -0
  114. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/validation/suggestions.py +0 -0
  115. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/mcp/version.py +0 -0
  116. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/quantities.py +0 -0
  117. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/__init__.py +0 -0
  118. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/__init__.py +0 -0
  119. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/base.py +0 -0
  120. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/building.py +0 -0
  121. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/overhead_line_segment.py +0 -0
  122. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/pole.py +0 -0
  123. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/pvsystem.py +0 -0
  124. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/transformer.py +0 -0
  125. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/underground_cable.py +0 -0
  126. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/components/underground_junction.py +0 -0
  127. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/structural/structural_system.py +0 -0
  128. {grid_data_models-2.3.2 → grid_data_models-2.3.3}/src/gdm/tracked_changes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: grid-data-models
3
- Version: 2.3.2
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,13 +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; extra == 'doc'
33
+ Requires-Dist: jupyter-book<3,>=2.1.4; extra == 'doc'
34
34
  Requires-Dist: myst-parser; extra == 'doc'
35
35
  Requires-Dist: pydata-sphinx-theme; extra == 'doc'
36
36
  Requires-Dist: sphinx; extra == 'doc'
37
37
  Requires-Dist: sphinxcontrib-mermaid; extra == 'doc'
38
38
  Provides-Extra: mcp
39
- Requires-Dist: mcp>=1.0.0; extra == 'mcp'
39
+ Requires-Dist: mcp>=1.27.0; extra == 'mcp'
40
40
  Description-Content-Type: text/markdown
41
41
 
42
42
 
@@ -24,19 +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,<3",
39
+ "jupyter-book>=2.1.4,<3",
40
40
  "sphinx",
41
41
  "pydata-sphinx-theme",
42
42
  "myst-parser",
@@ -143,6 +143,7 @@ class CustomerClass(str, Enum):
143
143
  RESIDENTIAL = "residential"
144
144
  COMMERCIAL = "commercial"
145
145
  INDUSTRIAL = "industrial"
146
+ UTILITY = "utility"
146
147
 
147
148
 
148
149
  class BillingDemandBasis(str, Enum):
@@ -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: Month = Field(..., description="Month for the TOU rates")
54
- tou_periods: List[TOURatePeriod] = Field(..., description="List of TOU periods for the month")
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=Month.JULY,
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=Month.JANUARY,
144
+ months=[Month.DECEMBER, Month.JANUARY, Month.FEBRUARY],
118
145
  tou_periods=[
119
146
  TOURatePeriod.example(),
120
147
  TOURatePeriod(
@@ -0,0 +1,77 @@
1
+ from loguru import logger
2
+ from infrasys.migrations.metadata_migration import migrate_component_metadata
3
+
4
+
5
+ SEASON_TO_MONTHS = {
6
+ "summer": ["june", "july", "august"],
7
+ "winter": ["december", "january", "february"],
8
+ "shoulder": ["march", "april", "may", "september", "october", "november"],
9
+ }
10
+
11
+
12
+ def _get_component_type(component: dict) -> str | None:
13
+ return component.get("__metadata__", {}).get("fields", {}).get("type")
14
+
15
+
16
+ def _migrate_seasonal_tou_rates(component: dict) -> None:
17
+ """Migrate SeasonalTOURates: rename 'season' -> 'months' (Season -> List[Month])."""
18
+ if "season" in component:
19
+ season_value = component.pop("season")
20
+ component["months"] = SEASON_TO_MONTHS.get(season_value, [season_value])
21
+ logger.debug(
22
+ f"Migrated SeasonalTOURates season={season_value} -> months={component['months']}"
23
+ )
24
+
25
+
26
+ def _migrate_demand_charge(component: dict) -> None:
27
+ """Migrate DemandCharge: add 'months' field if missing (default to all 12 months)."""
28
+ if "months" not in component:
29
+ component["months"] = [
30
+ "january",
31
+ "february",
32
+ "march",
33
+ "april",
34
+ "may",
35
+ "june",
36
+ "july",
37
+ "august",
38
+ "september",
39
+ "october",
40
+ "november",
41
+ "december",
42
+ ]
43
+ logger.debug("Migrated DemandCharge: added default months (all 12 months)")
44
+
45
+
46
+ def _migrate_tariff_components(obj: dict) -> None:
47
+ """Recursively walk the component dict and apply tariff migrations."""
48
+ component_type = _get_component_type(obj)
49
+ if component_type == "SeasonalTOURates":
50
+ _migrate_seasonal_tou_rates(obj)
51
+ elif component_type == "DemandCharge":
52
+ _migrate_demand_charge(obj)
53
+
54
+ for value in obj.values():
55
+ if isinstance(value, dict):
56
+ _migrate_tariff_components(value)
57
+ elif isinstance(value, list):
58
+ for item in value:
59
+ if isinstance(item, dict):
60
+ _migrate_tariff_components(item)
61
+
62
+
63
+ def from__2_3_2__to__2_3_3(data: dict, from_version: str, to_version: str) -> dict:
64
+ logger.info(f"Upgrading DistributionSystem from verion {from_version} to {to_version}")
65
+ data["data_format_version"] = str(to_version)
66
+ number_of_components_before = len(data["components"])
67
+
68
+ for component in data["components"]:
69
+ _migrate_tariff_components(component)
70
+
71
+ data["components"] = migrate_component_metadata(data["components"])
72
+ number_of_components_after = len(data["components"])
73
+ assert (
74
+ number_of_components_before == number_of_components_after
75
+ ), "Number of components should be the same before and after model upgrade"
76
+
77
+ return data
@@ -20,6 +20,7 @@ from gdm.distribution.upgrade_handler.from__2_2_0__to__2_2_1 import from__2_2_0_
20
20
  from gdm.distribution.upgrade_handler.from__2_2_1__to__2_3_0 import from__2_2_1__to__2_3_0
21
21
  from gdm.distribution.upgrade_handler.from__2_3_0__to__2_3_1 import from__2_3_0__to__2_3_1
22
22
  from gdm.distribution.upgrade_handler.from__2_3_1__to__2_3_2 import from__2_3_1__to__2_3_2
23
+ from gdm.distribution.upgrade_handler.from__2_3_2__to__2_3_3 import from__2_3_2__to__2_3_3
23
24
 
24
25
 
25
26
  def fix_version(version):
@@ -112,6 +113,11 @@ class UpgradeHandler(BaseModel):
112
113
  from_version="2.3.1",
113
114
  to_version="2.3.2",
114
115
  ),
116
+ UpgradeSchema(
117
+ method=from__2_3_2__to__2_3_3,
118
+ from_version="2.3.2",
119
+ to_version="2.3.3",
120
+ ),
115
121
  ]
116
122
 
117
123
  @model_validator(mode="after")
@@ -3,7 +3,7 @@ import subprocess
3
3
  import platform
4
4
  import sys
5
5
 
6
- VERSION = "2.3.2"
6
+ VERSION = "2.3.3"
7
7
  SUPPORTED_VERSIONS = [VERSION]
8
8
 
9
9