fram-core 0.1.0a1__py3-none-any.whl → 0.1.1__py3-none-any.whl

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 (78) hide show
  1. {fram_core-0.1.0a1.dist-info → fram_core-0.1.1.dist-info}/METADATA +6 -5
  2. fram_core-0.1.1.dist-info/RECORD +100 -0
  3. {fram_core-0.1.0a1.dist-info → fram_core-0.1.1.dist-info}/WHEEL +1 -1
  4. framcore/Base.py +22 -3
  5. framcore/Model.py +26 -9
  6. framcore/__init__.py +2 -1
  7. framcore/aggregators/Aggregator.py +30 -11
  8. framcore/aggregators/HydroAggregator.py +37 -25
  9. framcore/aggregators/NodeAggregator.py +65 -30
  10. framcore/aggregators/WindSolarAggregator.py +22 -30
  11. framcore/attributes/Arrow.py +6 -4
  12. framcore/attributes/ElasticDemand.py +13 -13
  13. framcore/attributes/ReservoirCurve.py +3 -17
  14. framcore/attributes/SoftBound.py +2 -5
  15. framcore/attributes/StartUpCost.py +14 -3
  16. framcore/attributes/Storage.py +17 -5
  17. framcore/attributes/TargetBound.py +2 -4
  18. framcore/attributes/__init__.py +2 -4
  19. framcore/attributes/hydro/HydroBypass.py +9 -2
  20. framcore/attributes/hydro/HydroGenerator.py +24 -7
  21. framcore/attributes/hydro/HydroPump.py +32 -10
  22. framcore/attributes/hydro/HydroReservoir.py +4 -4
  23. framcore/attributes/level_profile_attributes.py +250 -53
  24. framcore/components/Component.py +27 -3
  25. framcore/components/Demand.py +18 -4
  26. framcore/components/Flow.py +26 -4
  27. framcore/components/HydroModule.py +45 -4
  28. framcore/components/Node.py +32 -9
  29. framcore/components/Thermal.py +12 -8
  30. framcore/components/Transmission.py +17 -2
  31. framcore/components/wind_solar.py +25 -10
  32. framcore/curves/LoadedCurve.py +0 -9
  33. framcore/expressions/Expr.py +137 -36
  34. framcore/expressions/__init__.py +3 -1
  35. framcore/expressions/_get_constant_from_expr.py +14 -20
  36. framcore/expressions/queries.py +121 -84
  37. framcore/expressions/units.py +30 -3
  38. framcore/fingerprints/fingerprint.py +0 -1
  39. framcore/juliamodels/JuliaModel.py +13 -3
  40. framcore/loaders/loaders.py +0 -2
  41. framcore/metadata/ExprMeta.py +13 -7
  42. framcore/metadata/LevelExprMeta.py +16 -1
  43. framcore/metadata/Member.py +7 -7
  44. framcore/metadata/__init__.py +1 -1
  45. framcore/querydbs/CacheDB.py +1 -1
  46. framcore/solvers/Solver.py +21 -6
  47. framcore/solvers/SolverConfig.py +4 -4
  48. framcore/timeindexes/AverageYearRange.py +9 -2
  49. framcore/timeindexes/ConstantTimeIndex.py +7 -2
  50. framcore/timeindexes/DailyIndex.py +14 -2
  51. framcore/timeindexes/FixedFrequencyTimeIndex.py +105 -53
  52. framcore/timeindexes/HourlyIndex.py +14 -2
  53. framcore/timeindexes/IsoCalendarDay.py +5 -3
  54. framcore/timeindexes/ListTimeIndex.py +103 -23
  55. framcore/timeindexes/ModelYear.py +8 -2
  56. framcore/timeindexes/ModelYears.py +11 -2
  57. framcore/timeindexes/OneYearProfileTimeIndex.py +10 -2
  58. framcore/timeindexes/ProfileTimeIndex.py +14 -3
  59. framcore/timeindexes/SinglePeriodTimeIndex.py +1 -1
  60. framcore/timeindexes/TimeIndex.py +16 -3
  61. framcore/timeindexes/WeeklyIndex.py +14 -2
  62. framcore/{expressions → timeindexes}/_time_vector_operations.py +76 -2
  63. framcore/timevectors/ConstantTimeVector.py +12 -16
  64. framcore/timevectors/LinearTransformTimeVector.py +20 -3
  65. framcore/timevectors/ListTimeVector.py +18 -14
  66. framcore/timevectors/LoadedTimeVector.py +1 -8
  67. framcore/timevectors/ReferencePeriod.py +13 -3
  68. framcore/timevectors/TimeVector.py +26 -12
  69. framcore/utils/__init__.py +0 -1
  70. framcore/utils/get_regional_volumes.py +21 -3
  71. framcore/utils/get_supported_components.py +1 -1
  72. framcore/utils/global_energy_equivalent.py +22 -5
  73. framcore/utils/isolate_subnodes.py +12 -3
  74. framcore/utils/loaders.py +7 -7
  75. framcore/utils/node_flow_utils.py +4 -4
  76. framcore/utils/storage_subsystems.py +3 -4
  77. fram_core-0.1.0a1.dist-info/RECORD +0 -100
  78. {fram_core-0.1.0a1.dist-info → fram_core-0.1.1.dist-info}/licenses/LICENSE.md +0 -0
@@ -8,7 +8,7 @@ from framcore.timevectors.TimeVector import TimeVector # NB! full import path n
8
8
 
9
9
 
10
10
  class ListTimeVector(TimeVector):
11
- """ListTimeVector class for TimeVectors with a numpy array of values. Subclass of TimeVector."""
11
+ """TimeVector with a numpy array of values paired with a timeindex."""
12
12
 
13
13
  def __init__(
14
14
  self,
@@ -37,18 +37,13 @@ class ListTimeVector(TimeVector):
37
37
  Raises:
38
38
  ValueError: When both is_max_level and is_zero_one_profile is not None. This would mean the TimeVector
39
39
  represents both a level and a profile, which is not allowed.
40
+ ValueError: When the shape of the vector does not match the number of periods in the timeindex.
40
41
 
41
42
  """
42
- if (is_max_level is not None and is_zero_one_profile is not None) or (is_max_level is None and is_zero_one_profile is None):
43
- message = (
44
- f"Input arguments for {self}: Must have exactly one 'non-None'"
45
- "value for is_max_level and is_zero_one_profile. "
46
- "A TimeVector is either a level or a profile."
47
- )
48
- raise ValueError(message)
49
- # assert vector.shape == (timeindex.get_num_periods(),), (
50
- # f"Vector shape {vector.shape} does not match timeindex num_periods {timeindex.get_num_periods()}"
51
- # )
43
+ if vector.shape != (timeindex.get_num_periods(),):
44
+ msg = f"Vector shape {vector.shape} does not match number of periods {timeindex.get_num_periods()} of timeindex ({timeindex})."
45
+ raise ValueError(msg)
46
+
52
47
  self._timeindex = timeindex
53
48
  self._vector = vector
54
49
  self._unit = unit
@@ -56,7 +51,16 @@ class ListTimeVector(TimeVector):
56
51
  self._is_max_level = is_max_level
57
52
  self._is_zero_one_profile = is_zero_one_profile
58
53
 
59
- def __eq__(self, other):
54
+ self._check_type(timeindex, TimeIndex)
55
+ self._check_type(vector, np.ndarray)
56
+ self._check_type(unit, (str, type(None)))
57
+ self._check_type(is_max_level, (bool, type(None)))
58
+ self._check_type(is_zero_one_profile, (bool, type(None)))
59
+ self._check_type(reference_period, (ReferencePeriod, type(None)))
60
+
61
+ self._check_is_level_or_profile()
62
+
63
+ def __eq__(self, other: object) -> None:
60
64
  """Check equality between two ListTimeVector objects."""
61
65
  if not isinstance(other, ListTimeVector):
62
66
  return NotImplemented
@@ -73,9 +77,9 @@ class ListTimeVector(TimeVector):
73
77
  """Return hash of ListTimeVector object."""
74
78
  return hash((self._timeindex, self._vector.tobytes(), self._unit, self._is_max_level, self._is_zero_one_profile, self._reference_period))
75
79
 
76
- def __repr__(self) -> str: # TODO: Also timeindex and reference_period
80
+ def __repr__(self) -> str:
77
81
  """Return the string representation of the ListTimeVector."""
78
- return f"ListTimeVector(timeindex={self._timeindex}, vector={self._vector}, unit={self._unit})"
82
+ return f"ListTimeVector(timeindex={self._timeindex}, vector={self._vector}, unit={self._unit}, reference_period={self._reference_period})"
79
83
 
80
84
  def get_vector(self, is_float32: bool) -> NDArray:
81
85
  """Get the vector of the TimeVector as a numpy array."""
@@ -30,19 +30,12 @@ class LoadedTimeVector(TimeVector):
30
30
  self._loader = loader
31
31
  self._check_type(self._vector_id, str)
32
32
  self._check_type(self._loader, TimeVectorLoader)
33
-
34
33
  self._is_max_level = self._loader.is_max_level(self._vector_id)
35
34
  self._is_zero_one_profile = self._loader.is_zero_one_profile(self._vector_id)
36
35
  self._unit = self._loader.get_unit(self._vector_id)
37
36
  self._reference_period = self._loader.get_reference_period(self._vector_id)
38
37
 
39
- if (self._is_max_level is not None and self._is_zero_one_profile is not None) or (self._is_max_level is None and self._is_zero_one_profile is None):
40
- message = (
41
- f"Values for {self._loader} is_max_level ({self._is_max_level}) and is_zero_one_profile"
42
- f" ({self._is_zero_one_profile}) with time vector {self} Must have exactly one 'non-None' value."
43
- " A TimeVector is either a level or a profile."
44
- )
45
- raise ValueError(message)
38
+ self._check_is_level_or_profile()
46
39
 
47
40
  def __repr__(self) -> str:
48
41
  """Overwrite string representation of LoadedTimeVector objects."""
@@ -5,15 +5,25 @@ class ReferencePeriod(Base):
5
5
  """ReferencePeriod class represents a period of one or more years."""
6
6
 
7
7
  def __init__(self, start_year: int, num_years: int) -> None:
8
- """Initialize a ReferencePeriod with the start year and number of years."""
8
+ """
9
+ Initialize a ReferencePeriod with the start year and number of years.
10
+
11
+ Args:
12
+ start_year (int): The first year in the reference period. Must be a positive integer.
13
+ num_years (int): The number of years in the reference period. Must be a positive non-zero integer.
14
+
15
+ """
9
16
  self._check_type(start_year, int)
10
17
  self._check_type(num_years, int)
18
+
11
19
  if start_year < 0:
12
20
  message = f"start_year must be a positive integer. Got {start_year}."
13
21
  raise ValueError(message)
14
- if num_years < 0:
15
- message = f"num_years must be a positive integer. Got {num_years}."
22
+
23
+ if num_years <= 0:
24
+ message = f"num_years must be a positive non-zero integer. Got {num_years}."
16
25
  raise ValueError(message)
26
+
17
27
  self._start_year = start_year
18
28
  self._num_years = num_years
19
29
 
@@ -16,7 +16,7 @@ if TYPE_CHECKING:
16
16
 
17
17
  # TODO: Floating point precision
18
18
  class TimeVector(Base, ABC):
19
- """TimeVector interface class."""
19
+ """TimeVector interface class for defining timeseries data."""
20
20
 
21
21
  def __init__(self) -> None:
22
22
  """Initialize the TimeVector class."""
@@ -50,12 +50,9 @@ class TimeVector(Base, ABC):
50
50
  @abstractmethod
51
51
  def is_max_level(self) -> bool | None:
52
52
  """
53
- Check if TimeVector is a level representing max Volume/Capacity.
53
+ Whether the TimeVector represents the maximum level, average level given a reference period, or not a level at all.
54
54
 
55
- Returns:
56
- True - vector is a level representing max Volume/Capacity.
57
- False - vector is a level representing average Volume/Capacity over a given reference period.
58
- None - vector is not a level.
55
+ See LevelProfile for a description of Level (max or avg) and Profile (max one or mean one), and their formats.
59
56
 
60
57
  """
61
58
  pass
@@ -63,12 +60,9 @@ class TimeVector(Base, ABC):
63
60
  @abstractmethod
64
61
  def is_zero_one_profile(self) -> bool | None:
65
62
  """
66
- Check if TimeVector is a profile with values between zero and one.
63
+ Whether the TimeVector represents a profile with values between 0 and 1, a profile with average 1 over a given reference period, or is not a profile.
67
64
 
68
- Returns:
69
- True - vector is a profile with values between zero and one.
70
- False - vector is a profile where the mean value is 1 given a reference period.
71
- None - vector is not a profile.
65
+ See LevelProfile for a description of Level (max or avg) and Profile (max one or mean one), and their formats.
72
66
 
73
67
  """
74
68
  pass
@@ -90,5 +84,25 @@ class TimeVector(Base, ABC):
90
84
 
91
85
  @abstractmethod
92
86
  def get_loader(self) -> TimeVectorLoader | None:
93
- """Get the TimeVectorLoader of the TimeVector if self has one."""
87
+ """
88
+ Get the TimeVectorLoader of the TimeVector if self has one.
89
+
90
+ TimeVectors can store timeseries data in Loaders that point to databases. Data is only retrieved and cached when the TimeVector is queried.
91
+ """
94
92
  pass
93
+
94
+ """
95
+ Checks that the TimeVector is either a level or a profile.
96
+
97
+ Raises:
98
+ ValueError: If both is_max_level and is_zero_one_profile are None or both are not None.
99
+ """
100
+
101
+ def _check_is_level_or_profile(self) -> None:
102
+ """Ensure that the TimeVector is either a level or a profile."""
103
+ if (self.is_max_level() is not None and self.is_zero_one_profile() is not None) or (self.is_max_level() is None and self.is_zero_one_profile() is None):
104
+ message = (
105
+ f"Invalid input arguments for {self}: Must have exactly one 'non-None' value for "
106
+ "is_max_level and is_zero_one_profile. A TimeVector is either a level or a profile."
107
+ )
108
+ raise ValueError(message)
@@ -1,6 +1,5 @@
1
1
  # framcore/lib/__init__.py
2
2
 
3
-
4
3
  from framcore.utils.get_supported_components import get_supported_components
5
4
  from framcore.utils.node_flow_utils import (
6
5
  FlowInfo,
@@ -176,11 +176,11 @@ def _get_vector(
176
176
  # TODO: More options: node_category, consumption_category, production_category, with_trade_partners
177
177
 
178
178
 
179
- def _check_category(category, flow_id, flow_info) -> None:
179
+ def _check_category(category: str, flow_id: str, flow_info: FlowInfo) -> None:
180
180
  pass
181
181
 
182
182
 
183
- def get_regional_volumes(
183
+ def get_regional_volumes( # noqa C901
184
184
  db: Model | QueryDB,
185
185
  commodity: str,
186
186
  node_category: str,
@@ -191,7 +191,25 @@ def get_regional_volumes(
191
191
  unit: str,
192
192
  is_float32: bool = True,
193
193
  ) -> RegionalVolumes:
194
- """Calculate aggregated production, consumption, import and export."""
194
+ """
195
+ Calculate aggregated production, consumption, import and export for member in node_category.
196
+
197
+ Decompose the model components into nodes and flows. Analyze the flows to determine their contribution to production, consumption, import, and export if
198
+ they are associated with the specified commodity. Group these contributions based on the provided node_category, production_category, and
199
+ consumption_category metadata.
200
+
201
+ Args:
202
+ db (Model | QueryDB): Model or QueryDB to use
203
+ commodity (str): Commodity to consider
204
+ node_category (str): Meta key for node category to group the results by
205
+ production_category (str): Meta key for production category to group the results by
206
+ consumption_category (str): Meta key for consumption category to group the results by
207
+ data_period (SinglePeriodTimeIndex): Consider results for this data period
208
+ scenario_period (FixedFrequencyTimeIndex): Consider results for this scenario period
209
+ unit (str): Unit to use for the results
210
+ is_float32 (bool): Use float32 for calculations and results if True
211
+
212
+ """
195
213
  db = _load_model_and_create_model_db(db)
196
214
 
197
215
  if not isinstance(is_float32, bool):
@@ -6,7 +6,7 @@ def get_supported_components(
6
6
  supported_types: tuple[type[Component]],
7
7
  forbidden_types: tuple[type[Component]],
8
8
  ) -> dict[str, Component]:
9
- """Return simplified version of components in compliance with specified component types."""
9
+ """Return simplified version of components in compliance with specified component types.See description in Component."""
10
10
  output: dict[str, Component] = {}
11
11
  errors: list[str] = []
12
12
 
@@ -11,23 +11,31 @@ def get_hydro_downstream_energy_equivalent(
11
11
  power_node: str | None = None,
12
12
  ) -> Expr:
13
13
  """
14
- Get the sum downstream energy equivalent for a hydro module.
14
+ Get the expression for the sum downstream energy equivalent for a hydro module.
15
+
16
+ - If power node is given, only count downstream energy equivalents that are connected to the power node.
17
+ - Energy equivalents are collected from hydro generators downstream, and the main topology follows the release_to attribute.
18
+ - Transport pumps are included in the downstream topology, but counted as negative energy equivalents.
19
+
20
+ Args:
21
+ data (dict[str, Component | TimeVector | Curve | Expr]): The dict containing the components.
22
+ module_name (str): The name of the hydro module to start from.
23
+ power_node (str): Optional power node to filter energy equivalents.
15
24
 
16
- Either count all downstream energy equivalents, or only those that are connected to the given power_node.
17
25
  """
18
26
  if data[module_name].get_pump() and data[module_name].get_pump().get_from_module() == module_name: # transport pump
19
27
  pump_power_node = data[module_name].get_pump().get_power_node()
20
28
  pump_to = data[module_name].get_pump().get_to_module()
21
29
  energy_equivalent = get_hydro_downstream_energy_equivalent(data, pump_to, power_node) # continue downstream of pump_to module
22
30
  if power_node in (pump_power_node, None):
23
- return energy_equivalent - data[module_name].get_pump().get_energy_eq().get_level() # pumps has negative energy equivalents
31
+ return energy_equivalent - data[module_name].get_pump().get_energy_equivalent().get_level() # pumps has negative energy equivalents
24
32
  return energy_equivalent
25
33
 
26
34
  energy_equivalent = 0
27
35
  if data[module_name].get_generator(): # hydro generator
28
36
  module_power_node = data[module_name].get_generator().get_power_node()
29
37
  if power_node in (module_power_node, None):
30
- energy_equivalent += data[module_name].get_generator().get_energy_eq().get_level()
38
+ energy_equivalent += data[module_name].get_generator().get_energy_equivalent().get_level()
31
39
  if data[module_name].get_release_to(): # continue from release_to module
32
40
  release_to = data[module_name].get_release_to()
33
41
  energy_equivalent += get_hydro_downstream_energy_equivalent(data, release_to, power_node)
@@ -35,7 +43,16 @@ def get_hydro_downstream_energy_equivalent(
35
43
 
36
44
 
37
45
  def set_global_energy_equivalent(data: dict[str, Component | TimeVector | Curve | Expr], metakey_energy_eq_downstream: str) -> None:
38
- """Set the downstream energy equivalent of all HydroModules. Set to 1 for other types of components?."""
46
+ """
47
+ Loop through data dict and set the downstream energy equivalent for all HydroModules.
48
+
49
+ Send a warning event if a HydroModule has no downstream energy equivalents.
50
+
51
+ Args:
52
+ data (dict[str, Component | TimeVector | Curve | Expr]): The dict containing the components.
53
+ metakey_energy_eq_downstream (str): The meta key to use for storing the downstream energy equivalent.
54
+
55
+ """
39
56
  for module_name, module in data.items():
40
57
  if isinstance(module, HydroModule) and module.get_reservoir():
41
58
  energy_equivalent = get_hydro_downstream_energy_equivalent(data, module_name)
@@ -22,11 +22,20 @@ def _is_member(node: Node, meta_key: str, members: set[str]) -> bool:
22
22
  return value in members
23
23
 
24
24
 
25
- def isolate_subnodes(model: Model, commodity: str, meta_key: str, members: list[str]) -> None:
25
+ def isolate_subnodes(model: Model, commodity: str, meta_key: str, members: list[str]) -> None: # noqa: PLR0915, C901
26
26
  """
27
- Delete nodes of commodity named using meta_key except members and boundary nodes and flows.
27
+ For components in model, delete all nodes of commodity except member nodes, and their flows and boundary nodes.
28
+
29
+ - Keep member nodes and all flows between them.
30
+ - Set boundary nodes exogenous and keep boundary flows into or out from member nodes.
31
+ - Delete all other nodes of commodity and all other flows pointing to them.
32
+
33
+ Args:
34
+ model (Model): Model to modify
35
+ commodity (str): Commodity of nodes to consider
36
+ meta_key (str): Meta key to use to identify members
37
+ members (List[str]): List of meta key values identifying member nodes
28
38
 
29
- Boudary nodes are set exogenous and all flows pointing to them except boundary flows into or out from member nodes.
30
39
  """
31
40
  t = time()
32
41
 
framcore/utils/loaders.py CHANGED
@@ -20,12 +20,12 @@ def add_loaders_if(loaders: set, value: object | None) -> None:
20
20
 
21
21
  def add_loaders(loaders: set[Loader], model: Model) -> None:
22
22
  """Add all loaders stored in Model to loaders set."""
23
- from framcore import Model # noqa: PLC0415
24
- from framcore.components import Component, Flow, Node # noqa: PLC0415
25
- from framcore.curves import Curve # noqa: PLC0415
26
- from framcore.expressions import Expr # noqa: PLC0415
27
- from framcore.timevectors import TimeVector # noqa: PLC0415
28
- from framcore.utils import get_supported_components # noqa: PLC0415
23
+ from framcore import Model
24
+ from framcore.components import Component, Flow, Node
25
+ from framcore.curves import Curve
26
+ from framcore.expressions import Expr
27
+ from framcore.timevectors import TimeVector
28
+ from framcore.utils import get_supported_components
29
29
 
30
30
  _check_type(loaders, "loaders", set)
31
31
  _check_type(model, "model", Model)
@@ -53,7 +53,7 @@ def add_loaders(loaders: set[Loader], model: Model) -> None:
53
53
 
54
54
  def replace_loader_path(loaders: set[Loader], old: Path, new: Path) -> None:
55
55
  """Replace old path with new for all loaders using old path."""
56
- from framcore.loaders import FileLoader # noqa: PLC0415
56
+ from framcore.loaders import FileLoader
57
57
 
58
58
  _check_type(loaders, "loaders", set)
59
59
 
@@ -56,7 +56,7 @@ def get_node_to_commodity(data: dict[str, object]) -> dict[str, str]:
56
56
  return out
57
57
 
58
58
 
59
- def get_flow_infos(flow: Flow, node_to_commodity: dict[str, str]) -> list[FlowInfo]:
59
+ def get_flow_infos(flow: Flow, node_to_commodity: dict[str, str]) -> list[FlowInfo]: # noqa: C901
60
60
  """Get flow infos from analysis of all its arrows."""
61
61
  _check_type(flow, Flow)
62
62
  _check_type(node_to_commodity, dict)
@@ -149,8 +149,8 @@ def get_flow_infos(flow: Flow, node_to_commodity: dict[str, str]) -> list[FlowIn
149
149
 
150
150
 
151
151
  def get_component_to_nodes(data: Model | dict[str, object]) -> dict[str, set[str]]:
152
- """For each str key in data where value is a Comonent find all Node id str in data directly connected to the Component."""
153
- from framcore import Model # noqa: PLC0415
152
+ """For each str key in data where value is a Component find all Node id str in data directly connected to the Component."""
153
+ from framcore import Model
154
154
 
155
155
  _check_type(data, Model | dict)
156
156
 
@@ -184,7 +184,7 @@ def get_component_to_nodes(data: Model | dict[str, object]) -> dict[str, set[str
184
184
 
185
185
  def get_transports_by_commodity(data: Model | dict[str, object], commodity: str) -> dict[str, tuple[str, str]]:
186
186
  """Return dict with key component_id and value (from_node_id, to_node_id) where both nodes belong to given commodity."""
187
- from framcore import Model # noqa: PLC0415
187
+ from framcore import Model
188
188
 
189
189
  _check_type(data, Model | dict)
190
190
  _check_type(commodity, str)
@@ -8,7 +8,7 @@ from framcore.utils import get_supported_components
8
8
 
9
9
 
10
10
  # TODO: Finish implementation, test and demo
11
- def get_storage_subsystems(domain_components: dict[str, Component] | Model) -> dict[str, set[str]]:
11
+ def get_storage_subsystems(domain_components: dict[str, Component] | Model) -> dict[str, set[str]]: # noqa: D103
12
12
  if isinstance(domain_components, Model):
13
13
  domain_components = {k: v for k, v in domain_components.get_data() if isinstance(v, Component)}
14
14
 
@@ -21,12 +21,11 @@ def get_storage_subsystems(domain_components: dict[str, Component] | Model) -> d
21
21
  abstract_subsystems, __ = get_one_commodity_storage_subsystems(graph, include_boundaries=True)
22
22
 
23
23
  # TODO: Use Component.get_top_level to lift abstract_subsystems back to domain_components
24
- domain_subsystems = abstract_subsystems
24
+ return abstract_subsystems
25
25
 
26
- return domain_subsystems
27
26
 
28
27
 
29
- def get_one_commodity_storage_subsystems(
28
+ def get_one_commodity_storage_subsystems( # noqa: C901
30
29
  graph: dict[str, Node | Flow],
31
30
  include_boundaries: bool,
32
31
  ) -> dict[str, tuple[str, set[str], set[str]]]:
@@ -1,100 +0,0 @@
1
- framcore/Base.py,sha256=2MP0T1HAjU3gqkSvdGnbvd3t1JZmdXGCcwueETIhrrs,5326
2
- framcore/Model.py,sha256=TraN6dRQoJgm4veePEVszpdGAMGiTJA1dKSG4s_DtZg,2580
3
- framcore/__init__.py,sha256=5Ryn1oUwtxaPX95dfleE5GVYo9_QSBAwMG8UiUNrg74,128
4
- framcore/aggregators/Aggregator.py,sha256=n9jL4iAGk_N0An80BJNntkYYaXaQzGW1xGD3oFjUYzk,6473
5
- framcore/aggregators/HydroAggregator.py,sha256=ubV16QcJ63Uh8g_HzSexsHDFdUszODpBiEfaUhWZ8ko,47174
6
- framcore/aggregators/NodeAggregator.py,sha256=Zz4D1H42EmDU0jgYJtAR4C0_eRDN6nacLA3guRt_r2M,20716
7
- framcore/aggregators/WindSolarAggregator.py,sha256=3cHIxYZixTIH8kmkuR8GL6eROd8oU7z5txRVCMOvOrg,19620
8
- framcore/aggregators/__init__.py,sha256=ZoBqilfv0XhFkYVROBPQhJHzSY1IcvhJmJ_FOo_Z5Pw,426
9
- framcore/aggregators/_utils.py,sha256=EoRLeGmPC2ds-egPdvDewtaYbRozi6Psyf9DD1PRajk,8337
10
- framcore/attributes/Arrow.py,sha256=7P5xpXD6MdlIuERGQn9f0ia4-tQVYNiSP6CfMbX3K7Q,10863
11
- framcore/attributes/ElasticDemand.py,sha256=sG5wUTw17PQA_8YESaPMnRjUbr7nm6mSmkmuHoGlqJU,3024
12
- framcore/attributes/ReservoirCurve.py,sha256=EVzCMPM5OELB9U9_BAYmDOEFF4Pd6LwwmDOzRpkRVME,813
13
- framcore/attributes/SoftBound.py,sha256=7bW6KlSsKFvh8F6UPjC5PoLMGI7gClNo-dQrwAQHD5k,417
14
- framcore/attributes/StartUpCost.py,sha256=WLZ8mcQWdSyyA0GKIJ2rhMbAU17pHAREBP1Tb-3_4rU,1831
15
- framcore/attributes/Storage.py,sha256=x6njOhmkR3VvFI3Umn1Jjjd4F6yHWx-ZNNOYYv0lcZs,5335
16
- framcore/attributes/TargetBound.py,sha256=DHl8CCPynhrcRqTHPhFo9HddhEt489mGqrqUHQPkPhg,433
17
- framcore/attributes/__init__.py,sha256=SPzgyK4TQSMJ5tWOUmYfUpvWliCbQF8AVQkf6JeEGos,1552
18
- framcore/attributes/hydro/HydroBypass.py,sha256=ZiAfOiukGvlckCRIlccFqjnAwLiGn1SnN1uqtSgN9H4,1342
19
- framcore/attributes/hydro/HydroGenerator.py,sha256=ed0jMP808qchxkc4mXrVdtSO0LAGY6ihrjLN8xvdzN0,3207
20
- framcore/attributes/hydro/HydroPump.py,sha256=Yi54d259CzKpcu44hhWoX3ZxTBqhzlWun1WC5pGfVKw,5915
21
- framcore/attributes/hydro/HydroReservoir.py,sha256=7rYu530BTX9dJmTYrd_02uPddj290E-ziiZzjh_3Q3k,879
22
- framcore/attributes/hydro/__init__.py,sha256=isOSLP2TdHOaPcr16EXIkdtx80LVe8ztFPiuGJPC4ao,381
23
- framcore/attributes/level_profile_attributes.py,sha256=hZEBy_yjTJ4BIDM0qiNsQeO7Kcvr1CNMlv8Oyck7lkM,23340
24
- framcore/components/Component.py,sha256=GT_scqmDwSE8eLB3ZHcDMGfenRbko7xj4cenm_6Io-Q,3820
25
- framcore/components/Demand.py,sha256=vUP3hWobtk_Qe7ZGD2bXkLR_9zhaGrB8tKu3SvcvQp0,5399
26
- framcore/components/Flow.py,sha256=wuqKZ76uIw-OfdEVC-55wok7PmoeRs7KEAR-o2wUC6A,5887
27
- framcore/components/HydroModule.py,sha256=qpu0Fo4bFJs9kgwHtfwlInOX2UvwB1khdvRHWFWgmYQ,11352
28
- framcore/components/Node.py,sha256=Kh8Glu0M0UjmEpJhlATXD9xqc7QZEd5HxFdE2EVT58s,2280
29
- framcore/components/Thermal.py,sha256=MY0iOD870NSptZWpynKUEd8CuLt50s3_265GIiMG4Qc,7666
30
- framcore/components/Transmission.py,sha256=DlVrkKT8rNTUyQXaO3375S1zKfE1OOkl3QYGB7BhhMU,6842
31
- framcore/components/_PowerPlant.py,sha256=zDJ5LtYQN2GLa__BaqCLz54mZJil3dOYjICMPurh-U8,2787
32
- framcore/components/__init__.py,sha256=0N4UMrYi7cYYaZHMIS6HSOli2jEbtct6LsZ0JcwGz5A,568
33
- framcore/components/wind_solar.py,sha256=YsIPMZQEP81tijneFkeMEfprttu7lX7T5loVmSjSM90,2060
34
- framcore/curves/Curve.py,sha256=89CsAJpCa_GkQtsBGclUcK8D3ttCyYhs583-_6zE0is,934
35
- framcore/curves/LoadedCurve.py,sha256=8DiyLeJzydx6Q6MNQonxNGujl3zPma3vINDUN7E8_As,3891
36
- framcore/curves/__init__.py,sha256=BnzdAZVjPwJcjduramRPPX6NJag-g6aKXPho2PwmGco,170
37
- framcore/events/__init__.py,sha256=O3lOZukd_ixwbkoOmf2ei_--lKJLVXvyhSHjUDfaXEE,401
38
- framcore/events/events.py,sha256=mNcHUjWraKyb-gwxeKr5ryOtcN8dCKptRWIYgcxM1p4,1683
39
- framcore/expressions/Expr.py,sha256=I3m8cy-PNomN6p9088QQf1c4Egei5XEj7JoxwqZC0cY,20614
40
- framcore/expressions/__init__.py,sha256=J8bNtWRfIpwIi6yZIjFazQXU9ymJbKsYLqPlMUoH028,628
41
- framcore/expressions/_get_constant_from_expr.py,sha256=bqq-_xRhOrly6GB9Z868icgNqxrrUzobN1zXCq8B6lg,16596
42
- framcore/expressions/_time_vector_operations.py,sha256=kIMmQYR-U2aAxB5KPWx-zaqLvjvlmiwa2yCnmeNvvRY,29031
43
- framcore/expressions/_utils.py,sha256=mtruqnHcwpkIufbTFjJvT3iUfN98gN51SQ9e9hS9cUc,2123
44
- framcore/expressions/queries.py,sha256=PLJVL0CJoJp3okIYyG1QOrR3s9HHfBdZdxuCzrCBHa0,16258
45
- framcore/expressions/units.py,sha256=9X243DutWfkCbRNzy65frQNKN8tlLLzLvWYtiEyhHcg,6863
46
- framcore/fingerprints/__init__.py,sha256=YpIj-t6DRrSOmdidchC4qjLHSoZwsdkf9980nHUsnsw,256
47
- framcore/fingerprints/fingerprint.py,sha256=-e8aweHfPj8VURJqEra14xydVw3E1heB17LWcPFzIZo,9191
48
- framcore/juliamodels/JuliaModel.py,sha256=S-W_t4Pv-xuzz6lrULmxNPu0No6Zs1CPpb9WW3ECZK0,6596
49
- framcore/juliamodels/__init__.py,sha256=2ia9EGUQAZD0_f2o0NWdhD0d2z80K_bweohnGNfnpGA,124
50
- framcore/loaders/__init__.py,sha256=iwlnauyXhH_BAuVIKVmX2vMvU94i_bXI57CE82eM7VA,209
51
- framcore/loaders/loaders.py,sha256=EYqKeItQS5-zI-n-yOJf_7C_Ojo5VBk143f76joINYA,11254
52
- framcore/metadata/Div.py,sha256=fIIB9W9fVJEUNzqIWXOJ9ZN3tWE8dB8ap2BdULT2fJw,2428
53
- framcore/metadata/ExprMeta.py,sha256=zM5u7TXnImEIIVQbUGmcz74LbvNybMIkRYB4913cdiE,1556
54
- framcore/metadata/LevelExprMeta.py,sha256=AcE4DfJLtMFY69A8H7hzKYSR454O6Z9JTpOQpNnqG1I,567
55
- framcore/metadata/Member.py,sha256=7dcjqNWCcoGlOqXtqCBXYeN4z_5cIPBFUngcBVKAeas,1877
56
- framcore/metadata/Meta.py,sha256=P9ESFJlrxxhcY9PNSnnccrsLBulu1v3Kir6agzSrv0I,1173
57
- framcore/metadata/__init__.py,sha256=rf2oJ5SjlmdD4Fk0svPxHbNZlnzUdt-FPyjjhA8wbhc,350
58
- framcore/populators/Populator.py,sha256=SraN4QOTsA3yXEVG4z9ohWfPKokssl3uvDJPGh56lrU,4077
59
- framcore/populators/__init__.py,sha256=jE6tHIvTaPTWcD-yVpBOjDBxI3xFqJh0QASfQexBLCE,119
60
- framcore/querydbs/CacheDB.py,sha256=vzLEJM8SsvQYMXDE_TdjzicHM6yfJJefzp6u-Qyneys,1707
61
- framcore/querydbs/ModelDB.py,sha256=XD4jHlmRQHqF7L2euDIMrPZREhr3SXgv0UnqkADLzRw,1039
62
- framcore/querydbs/QueryDB.py,sha256=IV96mtslktpJMrdxgrwQetVx4R1HAP1GxtKgx9WLPZA,1233
63
- framcore/querydbs/__init__.py,sha256=oPX2sqAMoEMxWL9b_sRg4z4Q_b80iDm_jk-W_qmpEME,231
64
- framcore/solvers/Solver.py,sha256=dtxguoJAYHQCFsDXU9bbCZJGEaFJhS4Bpk-JGvI2g_o,1302
65
- framcore/solvers/SolverConfig.py,sha256=E181Jm9mJvEb2wMN7zE2AUzHwhQp_OLiKfSK6bEGF8s,10585
66
- framcore/solvers/__init__.py,sha256=q9HLYJkRdvJeFgAM1NBGIaFI8APmsZCDTy1AkIKw_Zs,179
67
- framcore/timeindexes/AverageYearRange.py,sha256=nAc-GxtPm9Hm-aEkDCkaKIfB4WC8jUJ_jgbPZjKi838,834
68
- framcore/timeindexes/ConstantTimeIndex.py,sha256=mufAI-lqdKyRw7kOzYZW1RbH9MfMfCNKnJPK7Em_3jk,601
69
- framcore/timeindexes/DailyIndex.py,sha256=SqKfnCisENPlt9gU7FTmjP7D_F3wNcLULqyyEoe5Cas,644
70
- framcore/timeindexes/FixedFrequencyTimeIndex.py,sha256=7DI_TRfaVCr2o402Mz4Wd1oOjluE1Xfr1-ItUrhPGbg,31479
71
- framcore/timeindexes/HourlyIndex.py,sha256=Xvqo41KZHIl1bsh0g1uSmH5TLG80UUt9vm3ZArNWa08,648
72
- framcore/timeindexes/IsoCalendarDay.py,sha256=5zffRr-uIg_mrwraBUWjD1LwhiYNU0pAl6-oGgQgvl8,991
73
- framcore/timeindexes/ListTimeIndex.py,sha256=XsYXkGG-wA6uPo-50zkp_R1_l9SII8dq7S_kGV060vY,8389
74
- framcore/timeindexes/ModelYear.py,sha256=Yh8jKizsRLNJRkkjCGQbxMETkDOMZq2zPEXNSmbAbqc,699
75
- framcore/timeindexes/ModelYears.py,sha256=ACNNNFeM6MycYNAQnYVVdXrjhqCgtdQ7DHyCXPimbzI,718
76
- framcore/timeindexes/OneYearProfileTimeIndex.py,sha256=HzjILknONIbGdw6rN_Ro5_r4ywFDXMoMP1bEgVzGuHo,773
77
- framcore/timeindexes/ProfileTimeIndex.py,sha256=Da_A-4NO8vgu9Z6Uax6rjKL0D03KnDCnroI5L90ZauU,1302
78
- framcore/timeindexes/SinglePeriodTimeIndex.py,sha256=mM-dHDEHdEOgaRLHQ2Q6NbjDoz9wYl3rC46EkLeHQpI,1447
79
- framcore/timeindexes/TimeIndex.py,sha256=kLQMiAi6ddQfzdKxyQbNRxmocwRmiLWz7SxUxCZ8wbw,2552
80
- framcore/timeindexes/WeeklyIndex.py,sha256=fZr0OMDKc5yUlsMa--D7d8FxWg-fkeHH5oclXP_a4mI,648
81
- framcore/timeindexes/__init__.py,sha256=2LmQFuMR1DffqHk5O2zd8o9g8QsazxSSykxOE7WGP4o,1351
82
- framcore/timevectors/ConstantTimeVector.py,sha256=S9fMSna_xOzJwu_Q9B_wNJtm9QYlEC3DmbkutOtMofU,6012
83
- framcore/timevectors/LinearTransformTimeVector.py,sha256=fcXQJ0Sn-m1K56j-5p78p1sFwxrEvgjq00x-THevTF4,4189
84
- framcore/timevectors/ListTimeVector.py,sha256=rQyCY2o7BsutS6kSFnV-CVzgoJCIPccmUugoeo-cwoI,5324
85
- framcore/timevectors/LoadedTimeVector.py,sha256=Igg045J6oFF8ocSUC1khbA98y_niww759FLFa-umzXk,4493
86
- framcore/timevectors/ReferencePeriod.py,sha256=eB7DQYIsyq273D7jQEG_-b-x3S5mpY8pCkATik5DKE8,1456
87
- framcore/timevectors/TimeVector.py,sha256=yH68GReHw19VjScOWBf9u0krRPlHzmOnbvDAyM-pLno,2598
88
- framcore/timevectors/__init__.py,sha256=Wd3gXGatwpbe2yMKSCq7O9My5wbveuK23iYO2ykkUIk,603
89
- framcore/utils/__init__.py,sha256=9M80LjgkJQ53Lbe5jiRro2tHUR4MQp2JPGiKF57wOr4,1222
90
- framcore/utils/get_regional_volumes.py,sha256=QqQ3Ik8VIHFomEIA3krcGJkeyDZFecbaNRtIm7_n8KE,14956
91
- framcore/utils/get_supported_components.py,sha256=F1AxTFuh4pLQTCwg8oYBhus0HOhKjkKYiRf9Ok1ttB4,1856
92
- framcore/utils/global_energy_equivalent.py,sha256=R3I1XVIIaYkagr_BPuKaAj9eS7gNzaff5v1tk7hzQuY,2614
93
- framcore/utils/isolate_subnodes.py,sha256=iRiqgTU_c6W1jLmIBGO7fQoK_abPA3N8beGJbroC2V8,6527
94
- framcore/utils/loaders.py,sha256=8llunmOC6XkbqNXSxL1XVNipGj7OpjIZ-2AyJUj-5v8,3320
95
- framcore/utils/node_flow_utils.py,sha256=4Z6eB4oUqhOWWeHFNYRZY0c0UQlJ8tM9duYvLzkJQ3k,7800
96
- framcore/utils/storage_subsystems.py,sha256=PmkV9rhiVUrmPz1zsyKcEwjJzYUkXQUcGM511tttsXc,4158
97
- fram_core-0.1.0a1.dist-info/METADATA,sha256=3PisZPX4tBGbm57LLFn_qEmV70e9HqsiMwBxO3sms1c,1204
98
- fram_core-0.1.0a1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
99
- fram_core-0.1.0a1.dist-info/licenses/LICENSE.md,sha256=fxh4ZxuR8dM2HDs-pIUitPrJdxQ4fEFh1GvEYZA2m1E,1075
100
- fram_core-0.1.0a1.dist-info/RECORD,,