fram-core 0.0.0__py3-none-any.whl → 0.1.0__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 (103) hide show
  1. fram_core-0.1.0.dist-info/METADATA +42 -0
  2. fram_core-0.1.0.dist-info/RECORD +100 -0
  3. {fram_core-0.0.0.dist-info → fram_core-0.1.0.dist-info}/WHEEL +1 -2
  4. fram_core-0.1.0.dist-info/licenses/LICENSE.md +8 -0
  5. framcore/Base.py +161 -0
  6. framcore/Model.py +90 -0
  7. framcore/__init__.py +10 -0
  8. framcore/aggregators/Aggregator.py +172 -0
  9. framcore/aggregators/HydroAggregator.py +849 -0
  10. framcore/aggregators/NodeAggregator.py +530 -0
  11. framcore/aggregators/WindSolarAggregator.py +315 -0
  12. framcore/aggregators/__init__.py +13 -0
  13. framcore/aggregators/_utils.py +184 -0
  14. framcore/attributes/Arrow.py +307 -0
  15. framcore/attributes/ElasticDemand.py +90 -0
  16. framcore/attributes/ReservoirCurve.py +23 -0
  17. framcore/attributes/SoftBound.py +16 -0
  18. framcore/attributes/StartUpCost.py +65 -0
  19. framcore/attributes/Storage.py +158 -0
  20. framcore/attributes/TargetBound.py +16 -0
  21. framcore/attributes/__init__.py +63 -0
  22. framcore/attributes/hydro/HydroBypass.py +49 -0
  23. framcore/attributes/hydro/HydroGenerator.py +100 -0
  24. framcore/attributes/hydro/HydroPump.py +178 -0
  25. framcore/attributes/hydro/HydroReservoir.py +27 -0
  26. framcore/attributes/hydro/__init__.py +13 -0
  27. framcore/attributes/level_profile_attributes.py +911 -0
  28. framcore/components/Component.py +136 -0
  29. framcore/components/Demand.py +144 -0
  30. framcore/components/Flow.py +189 -0
  31. framcore/components/HydroModule.py +371 -0
  32. framcore/components/Node.py +99 -0
  33. framcore/components/Thermal.py +208 -0
  34. framcore/components/Transmission.py +198 -0
  35. framcore/components/_PowerPlant.py +81 -0
  36. framcore/components/__init__.py +22 -0
  37. framcore/components/wind_solar.py +82 -0
  38. framcore/curves/Curve.py +44 -0
  39. framcore/curves/LoadedCurve.py +146 -0
  40. framcore/curves/__init__.py +9 -0
  41. framcore/events/__init__.py +21 -0
  42. framcore/events/events.py +51 -0
  43. framcore/expressions/Expr.py +591 -0
  44. framcore/expressions/__init__.py +30 -0
  45. framcore/expressions/_get_constant_from_expr.py +477 -0
  46. framcore/expressions/_utils.py +73 -0
  47. framcore/expressions/queries.py +416 -0
  48. framcore/expressions/units.py +227 -0
  49. framcore/fingerprints/__init__.py +11 -0
  50. framcore/fingerprints/fingerprint.py +292 -0
  51. framcore/juliamodels/JuliaModel.py +171 -0
  52. framcore/juliamodels/__init__.py +7 -0
  53. framcore/loaders/__init__.py +10 -0
  54. framcore/loaders/loaders.py +405 -0
  55. framcore/metadata/Div.py +73 -0
  56. framcore/metadata/ExprMeta.py +56 -0
  57. framcore/metadata/LevelExprMeta.py +32 -0
  58. framcore/metadata/Member.py +55 -0
  59. framcore/metadata/Meta.py +44 -0
  60. framcore/metadata/__init__.py +15 -0
  61. framcore/populators/Populator.py +108 -0
  62. framcore/populators/__init__.py +7 -0
  63. framcore/querydbs/CacheDB.py +50 -0
  64. framcore/querydbs/ModelDB.py +34 -0
  65. framcore/querydbs/QueryDB.py +45 -0
  66. framcore/querydbs/__init__.py +11 -0
  67. framcore/solvers/Solver.py +63 -0
  68. framcore/solvers/SolverConfig.py +272 -0
  69. framcore/solvers/__init__.py +9 -0
  70. framcore/timeindexes/AverageYearRange.py +27 -0
  71. framcore/timeindexes/ConstantTimeIndex.py +22 -0
  72. framcore/timeindexes/DailyIndex.py +33 -0
  73. framcore/timeindexes/FixedFrequencyTimeIndex.py +814 -0
  74. framcore/timeindexes/HourlyIndex.py +33 -0
  75. framcore/timeindexes/IsoCalendarDay.py +33 -0
  76. framcore/timeindexes/ListTimeIndex.py +277 -0
  77. framcore/timeindexes/ModelYear.py +23 -0
  78. framcore/timeindexes/ModelYears.py +27 -0
  79. framcore/timeindexes/OneYearProfileTimeIndex.py +29 -0
  80. framcore/timeindexes/ProfileTimeIndex.py +43 -0
  81. framcore/timeindexes/SinglePeriodTimeIndex.py +37 -0
  82. framcore/timeindexes/TimeIndex.py +103 -0
  83. framcore/timeindexes/WeeklyIndex.py +33 -0
  84. framcore/timeindexes/__init__.py +36 -0
  85. framcore/timeindexes/_time_vector_operations.py +689 -0
  86. framcore/timevectors/ConstantTimeVector.py +131 -0
  87. framcore/timevectors/LinearTransformTimeVector.py +131 -0
  88. framcore/timevectors/ListTimeVector.py +127 -0
  89. framcore/timevectors/LoadedTimeVector.py +97 -0
  90. framcore/timevectors/ReferencePeriod.py +51 -0
  91. framcore/timevectors/TimeVector.py +108 -0
  92. framcore/timevectors/__init__.py +17 -0
  93. framcore/utils/__init__.py +35 -0
  94. framcore/utils/get_regional_volumes.py +387 -0
  95. framcore/utils/get_supported_components.py +60 -0
  96. framcore/utils/global_energy_equivalent.py +63 -0
  97. framcore/utils/isolate_subnodes.py +172 -0
  98. framcore/utils/loaders.py +97 -0
  99. framcore/utils/node_flow_utils.py +236 -0
  100. framcore/utils/storage_subsystems.py +106 -0
  101. fram_core-0.0.0.dist-info/METADATA +0 -5
  102. fram_core-0.0.0.dist-info/RECORD +0 -4
  103. fram_core-0.0.0.dist-info/top_level.txt +0 -1
@@ -0,0 +1,198 @@
1
+ """Contain class describing transmission of Power commodity between nodes."""
2
+
3
+ from framcore.attributes import Arrow, AvgFlowVolume, Conversion, Cost, FlowVolume, Loss, Proportion
4
+ from framcore.components import Component, Flow
5
+
6
+
7
+ class Transmission(Component):
8
+ """
9
+ Transmission component representing a one directional transmission line. Subclass of Component.
10
+
11
+ An object of this class represents one transmission line where power flows one direction (the other direction is
12
+ represented by another Transmission object). However, the actual measured power being sent can be higher than the
13
+ amount being recieved because of loss. One Transmission object therefore represents the viewpoints of both the
14
+ sender and the reciever of power on this specific line.
15
+
16
+ """
17
+
18
+ def __init__(
19
+ self,
20
+ from_node: str,
21
+ to_node: str,
22
+ max_capacity: FlowVolume = None,
23
+ min_capacity: FlowVolume | None = None,
24
+ loss: Loss | None = None,
25
+ tariff: Cost | None = None,
26
+ ramp_up: Proportion | None = None,
27
+ ramp_down: Proportion | None = None,
28
+ ingoing_volume: AvgFlowVolume | None = None,
29
+ outgoing_volume: AvgFlowVolume | None = None,
30
+ ) -> None:
31
+ """
32
+ Initialize object of the Transmission class. Perform type checks and convert arguments to expressions.
33
+
34
+ Args:
35
+ from_node (str): Node which power is transported from.
36
+ to_node (str): Destination Node.
37
+ max_capacity (FlowVolume, optional): Maximum transmission capacity. Defaults to None.
38
+ min_capacity (FlowVolume | None, optional): Minimum transmission capacity. Defaults to None.
39
+ loss (Loss | None, optional): Amount of power lost while transmitting. Defaults to None.
40
+ tariff (Cost | None, optional): Costs associated with operating this transmission line. Defaults to None.
41
+ ramp_up (Proportion | None, optional): Max upwards change in transmission per time. Defaults to None.
42
+ ramp_down (Proportion | None, optional): Max downwards change in transmission per time. Defaults to None.
43
+ ingoing_volume (AvgFlowVolume | None, optional): Volume of power recieved by to_node. Defaults to None.
44
+ outgoing_volume (AvgFlowVolume | None, optional): Volume of power sent by from_node. Defaults to None.
45
+
46
+ """
47
+ super().__init__()
48
+
49
+ self._check_type(from_node, str)
50
+ self._check_type(to_node, str)
51
+ self._check_type(max_capacity, FlowVolume)
52
+ self._check_type(min_capacity, (FlowVolume, type(None)))
53
+ self._check_type(loss, (Loss, type(None)))
54
+ self._check_type(tariff, (Cost, type(None)))
55
+ self._check_type(ramp_up, (Proportion, type(None)))
56
+ self._check_type(ramp_down, (Proportion, type(None)))
57
+ self._check_type(ingoing_volume, (AvgFlowVolume, type(None)))
58
+ self._check_type(outgoing_volume, (AvgFlowVolume, type(None)))
59
+
60
+ self._from_node = from_node
61
+ self._to_node = to_node
62
+ self._max_capacity = max_capacity
63
+ self._min_capacity = min_capacity
64
+ self._loss = loss
65
+ self._tariff = tariff
66
+ self._ramp_up = ramp_up
67
+ self._ramp_down = ramp_down
68
+
69
+ if outgoing_volume is None:
70
+ outgoing_volume = AvgFlowVolume()
71
+
72
+ if ingoing_volume is None:
73
+ ingoing_volume = AvgFlowVolume()
74
+
75
+ self._outgoing_volume: AvgFlowVolume = outgoing_volume
76
+ self._ingoing_volume: AvgFlowVolume = ingoing_volume
77
+
78
+ def get_from_node(self) -> str:
79
+ """Get the from node of the transmission line."""
80
+ return self._from_node
81
+
82
+ def set_from_node(self, node: str) -> None:
83
+ """Set the from node of the transmission line."""
84
+ self._check_type(node, str)
85
+ self._from_node = node
86
+
87
+ def get_to_node(self) -> str:
88
+ """Get the to node of the transmission line."""
89
+ return self._to_node
90
+
91
+ def set_to_node(self, node: str) -> None:
92
+ """Set the to node of the transmission line."""
93
+ self._check_type(node, str)
94
+ self._to_node = node
95
+
96
+ def get_max_capacity(self) -> FlowVolume:
97
+ """Get the maximum capacity (before losses) of the transmission line."""
98
+ return self._max_capacity
99
+
100
+ def get_min_capacity(self) -> FlowVolume:
101
+ """Get the minimum capacity (before losses) of the transmission line."""
102
+ return self._min_capacity
103
+
104
+ def set_min_capacity(self, value: FlowVolume | None) -> None:
105
+ """Set the minimum capacity (before losses) of the transmission line."""
106
+ self._check_type(value, (FlowVolume, type(None)))
107
+ self._min_capacity = value
108
+
109
+ def get_outgoing_volume(self) -> AvgFlowVolume:
110
+ """Get the outgoing (before losses) flow volume of the transmission line."""
111
+ return self._outgoing_volume
112
+
113
+ def get_ingoing_volume(self) -> AvgFlowVolume:
114
+ """Get the ingoing (after losses) flow volume of the transmission line."""
115
+ return self._ingoing_volume
116
+
117
+ def get_loss(self) -> Loss | None:
118
+ """Get the loss of the transmission line."""
119
+ return self._loss
120
+
121
+ def set_loss(self, loss: Loss | None) -> None:
122
+ """Set the loss of the transmission line."""
123
+ self._check_type(loss, (Loss, type(None)))
124
+ self._loss = loss
125
+
126
+ def get_tariff(self) -> Cost | None:
127
+ """Get the tariff of the transmission line."""
128
+ return self._tariff
129
+
130
+ def set_tariff(self, tariff: Cost | None) -> None:
131
+ """Set the tariff of the transmission line."""
132
+ self._check_type(tariff, (Cost, type(None)))
133
+ self._tariff = tariff
134
+
135
+ def get_ramp_up(self) -> Proportion | None:
136
+ """Get the ramp up profile level of the transmission line."""
137
+ return self._ramp_up
138
+
139
+ def set_ramp_up(self, value: Proportion | None) -> None:
140
+ """Set the ramp up of the transmission line."""
141
+ self._check_type(value, (Proportion, type(None)))
142
+ self._ramp_up = value
143
+
144
+ def get_ramp_down(self) -> Proportion | None:
145
+ """Get the ramp down of the transmission line."""
146
+ return self._ramp_down
147
+
148
+ def set_ramp_down(self, value: Proportion | None) -> None:
149
+ """Set the ramp down of the transmission line."""
150
+ self._check_type(value, (Proportion, type(None)))
151
+ self._ramp_down = value
152
+
153
+ """Implementation of Component interface"""
154
+
155
+ def _get_simpler_components(self, base_name: str) -> dict[str, Component]:
156
+ return {base_name + "_Flow": self._create_flow()}
157
+
158
+ def _replace_node(self, old: str, new: str) -> None:
159
+ if old == self._from_node:
160
+ self._from_node = new
161
+ if old == self._to_node:
162
+ self._to_node = new
163
+
164
+ def _create_flow(self) -> Flow:
165
+ arrow_volumes: dict[Arrow, FlowVolume] = dict()
166
+
167
+ flow = Flow(
168
+ main_node=self._from_node,
169
+ max_capacity=self._max_capacity,
170
+ volume=self._outgoing_volume,
171
+ arrow_volumes=arrow_volumes,
172
+ # ramp_up=self._ramp_up, # TODO
173
+ # ramp_down=self._ramp_up, # TODO
174
+ )
175
+
176
+ outgoing_arrow = Arrow(
177
+ node=self._from_node,
178
+ is_ingoing=False,
179
+ conversion=Conversion(value=1),
180
+ )
181
+ flow.add_arrow(outgoing_arrow)
182
+ arrow_volumes[outgoing_arrow] = self._outgoing_volume
183
+
184
+ # TODO: Extend Loss to support more fetures, such as quadratic losses? Needs loss param in Arrow to do this
185
+
186
+ ingoing_arrow = Arrow(
187
+ node=self._to_node,
188
+ is_ingoing=True,
189
+ conversion=Conversion(value=1),
190
+ loss=self._loss,
191
+ )
192
+ flow.add_arrow(ingoing_arrow)
193
+ arrow_volumes[ingoing_arrow] = self._ingoing_volume
194
+
195
+ if self._tariff is not None:
196
+ flow.add_cost_term("tariff", self._tariff)
197
+
198
+ return flow
@@ -0,0 +1,81 @@
1
+ from framcore.attributes import AvgFlowVolume, Cost, FlowVolume
2
+ from framcore.components import Component
3
+
4
+
5
+ class _PowerPlant(Component):
6
+ """
7
+ PowerPlant parent class component representing a power plant. Subclass of Component.
8
+
9
+ This class models a powerplant with various attributes such as power node,
10
+ capacity, VOC levels and profile, and production.
11
+
12
+ Functions that are dependent on commodity nodes other than power are defined in the spesific power plant components.
13
+ """
14
+
15
+ def __init__(
16
+ self,
17
+ power_node: str,
18
+ max_capacity: FlowVolume,
19
+ min_capacity: FlowVolume | None,
20
+ voc: Cost | None = None,
21
+ production: AvgFlowVolume | None = None,
22
+ ) -> None:
23
+ """Initialize the class."""
24
+ super().__init__()
25
+
26
+ self._check_type(power_node, str)
27
+ self._check_type(max_capacity, FlowVolume)
28
+ self._check_type(min_capacity, (FlowVolume, type(None)))
29
+ self._check_type(voc, (Cost, type(None)))
30
+ self._check_type(production, (AvgFlowVolume, type(None)))
31
+
32
+ self._power_node = power_node
33
+ self._max_capacity = max_capacity
34
+ self._min_capacity = min_capacity
35
+ self._voc = voc
36
+
37
+ if not production:
38
+ production = AvgFlowVolume()
39
+
40
+ self._production = production
41
+
42
+ def get_max_capacity(self) -> FlowVolume:
43
+ """Get the capacity of the power unit."""
44
+ return self._max_capacity
45
+
46
+ def get_production(self) -> AvgFlowVolume:
47
+ """Get the production volume of the power unit."""
48
+ return self._production
49
+
50
+ def get_min_capacity(self) -> FlowVolume | None:
51
+ """Get the minimum capacity of the power unit."""
52
+ return self._min_capacity
53
+
54
+ def set_min_capacity(self, value: FlowVolume | None) -> None:
55
+ """Set the minimum capacity of the power unit."""
56
+ self._check_type(value, (FlowVolume, type(None)))
57
+ self._min_capacity = value
58
+
59
+ def get_power_node(self) -> str:
60
+ """Get the power node of the power unit."""
61
+ return self._power_node
62
+
63
+ def set_power_node(self, power_node: str) -> None:
64
+ """Set the power node of the power unit."""
65
+ self._check_type(power_node, str)
66
+ self._power_node = power_node
67
+
68
+ def get_voc(self) -> Cost | None:
69
+ """Get the variable operating cost (VOC) level of the power unit."""
70
+ return self._voc
71
+
72
+ def set_voc(self, voc: Cost | None) -> None:
73
+ """Set the variable operating cost (VOC) level of the power unit."""
74
+ self._check_type(voc, (Cost, type(None)))
75
+ self._voc = voc
76
+
77
+ """Implementation of Component interface"""
78
+
79
+ def _replace_node(self, old: str, new: str) -> None:
80
+ if old == self._power_node:
81
+ self._power_node = new
@@ -0,0 +1,22 @@
1
+ # ruff: noqa: I001
2
+
3
+ from framcore.components.Component import Component
4
+ from framcore.components.Node import Node
5
+ from framcore.components.Flow import Flow
6
+ from framcore.components.Demand import Demand
7
+ from framcore.components.HydroModule import HydroModule
8
+ from framcore.components.Thermal import Thermal
9
+ from framcore.components.Transmission import Transmission
10
+ from framcore.components.wind_solar import Solar, Wind
11
+
12
+ __all__ = [
13
+ "Component",
14
+ "Demand",
15
+ "Flow",
16
+ "HydroModule",
17
+ "Node",
18
+ "Solar",
19
+ "Thermal",
20
+ "Transmission",
21
+ "Wind",
22
+ ]
@@ -0,0 +1,82 @@
1
+ from framcore.attributes import Arrow, AvgFlowVolume, Conversion, Cost, FlowVolume
2
+ from framcore.components import Flow
3
+ from framcore.components._PowerPlant import _PowerPlant
4
+
5
+
6
+ class _WindSolar(_PowerPlant):
7
+ """
8
+ Wind and Solar class component representing a wind and solar power plant. Subclass of PowerPlant.
9
+
10
+ This class models a wind or solar power plant with various attributes inherited from the parent class PowerPlant.
11
+ """
12
+
13
+ def __init__(
14
+ self,
15
+ power_node: str,
16
+ max_capacity: FlowVolume,
17
+ voc: Cost | None = None,
18
+ production: AvgFlowVolume | None = None,
19
+ ) -> None:
20
+ """
21
+ Initialize the Wind and Solar class.
22
+
23
+ Args:
24
+ power_node (str): Reference to a power Node to produce to.
25
+ max_capacity (FlowVolume): Maximum capacity.
26
+ voc (Cost | None, optional): Variable operational costs. Defaults to None.
27
+ production (AvgFlowVolume | None, optional): Actual production. Defaults to None.
28
+
29
+ """
30
+ super().__init__(
31
+ power_node=power_node,
32
+ max_capacity=max_capacity,
33
+ min_capacity=None,
34
+ voc=voc,
35
+ production=production,
36
+ )
37
+
38
+ """Implementation of Component interface"""
39
+
40
+ def _get_simpler_components(self, base_name: str) -> dict[str, Flow]:
41
+ return {base_name + "_Flow": self._create_flow()}
42
+
43
+ def _create_flow(self) -> Flow:
44
+ flow = Flow(
45
+ main_node=self._power_node,
46
+ max_capacity=self._max_capacity,
47
+ volume=self._production,
48
+ )
49
+
50
+ flow.add_arrow(Arrow(node=self._power_node, is_ingoing=True, conversion=Conversion(value=1)))
51
+
52
+ if self._voc:
53
+ flow.add_cost_term("VOC", self._voc)
54
+ else:
55
+ flow.set_min_capacity(self._max_capacity)
56
+ flow.set_exogenous()
57
+
58
+ return flow
59
+
60
+
61
+ class Wind(_WindSolar):
62
+ """
63
+ Wind power component.
64
+
65
+ Has attributes for power node, capacity, variable operation cost, and production.
66
+
67
+ Compatible with WindSolarAggregator.
68
+ """
69
+
70
+ pass
71
+
72
+
73
+ class Solar(_WindSolar):
74
+ """
75
+ Solar power component.
76
+
77
+ Has attributes for power node, capacity, variable operation cost, and production.
78
+
79
+ Compatible with WindSolarAggregator.
80
+ """
81
+
82
+ pass
@@ -0,0 +1,44 @@
1
+ """Curve interface."""
2
+
3
+ from abc import ABC, abstractmethod
4
+
5
+ from numpy.typing import NDArray
6
+
7
+ from framcore import Base
8
+
9
+
10
+ class Curve(Base, ABC):
11
+ """Curve interface class."""
12
+
13
+ @abstractmethod
14
+ def get_unique_name(self) -> str | None:
15
+ """Return unique name of curve."""
16
+ pass
17
+
18
+ @abstractmethod
19
+ def get_x_axis(self, is_float32: bool) -> NDArray:
20
+ """
21
+ Get array of x axis values.
22
+
23
+ Args:
24
+ is_float32 (bool): Flag for converting the array of values to numpy float32.
25
+
26
+ Returns:
27
+ NDArray: Numpy array of values.
28
+
29
+ """
30
+ pass
31
+
32
+ @abstractmethod
33
+ def get_y_axis(self, is_float32: bool) -> NDArray:
34
+ """
35
+ Get array of y axis values.
36
+
37
+ Args:
38
+ is_float32 (bool): Flag for converting the array of values to numpy float32.
39
+
40
+ Returns:
41
+ NDArray: Numpy array of values.
42
+
43
+ """
44
+ pass
@@ -0,0 +1,146 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ import numpy as np
6
+ from numpy.typing import NDArray
7
+
8
+ from framcore.curves import Curve
9
+ from framcore.fingerprints import Fingerprint
10
+
11
+ if TYPE_CHECKING:
12
+ from framcore.loaders import CurveLoader
13
+
14
+
15
+ class LoadedCurve(Curve):
16
+ """
17
+ Represents a curve loaded from a CurveLoader.
18
+
19
+ Methods
20
+ -------
21
+ get_unique_name()
22
+ Returns the unique name of the curve.
23
+ get_x_axis(precision=None)
24
+ Returns the x-axis data.
25
+ get_y_axis(precision=None)
26
+ Returns the y-axis data.
27
+ get_x_unit()
28
+ Returns the unit for the x-axis.
29
+ get_y_unit()
30
+ Returns the unit for the y-axis.
31
+ get_loader()
32
+ Returns the loader instance.
33
+ get_fingerprint()
34
+ Returns the fingerprint of the curve.
35
+
36
+ """
37
+
38
+ def __init__(self, curve_id: str, loader: CurveLoader) -> None:
39
+ """
40
+ Initialize a LoadedCurve instance.
41
+
42
+ Parameters
43
+ ----------
44
+ curve_id : str
45
+ Identifier for the curve.
46
+ loader : CurveLoader
47
+ Loader instance used to retrieve curve data.
48
+
49
+ """
50
+ self._curve_id = curve_id
51
+ self._loader = loader
52
+
53
+ # TODO: get from loader
54
+ self._reference_period = None
55
+
56
+ def __repr__(self) -> str:
57
+ """Return a string representation of the LoadedCurve instance."""
58
+ return f"{type(self).__name__}(curve_id={self._curve_id},loader={self._loader},x_unit={self.get_x_unit()}),y_unit={self.get_y_unit()}),"
59
+
60
+ def get_unique_name(self) -> str:
61
+ """
62
+ Return the unique name of the curve.
63
+
64
+ Returns
65
+ -------
66
+ str
67
+ The unique name for the curve.
68
+
69
+ """
70
+ return self._curve_id
71
+
72
+ def get_x_axis(self, is_float32: bool) -> NDArray:
73
+ """
74
+ Get x axis values of the curve as a numpy array.
75
+
76
+ Args:
77
+ is_float32 (bool): Flag for converting the array of values to numpy float32.
78
+
79
+ Returns:
80
+ NDArray: Numpy array of x axis values.
81
+
82
+ """
83
+ x_axis = self._loader.get_x_axis(self._curve_id)
84
+ if is_float32:
85
+ x_axis = x_axis.astype(np.float32)
86
+ return x_axis
87
+
88
+ def get_y_axis(self, is_float32: bool) -> NDArray:
89
+ """
90
+ Get y axis values of the curve as a numpy array.
91
+
92
+ Args:
93
+ is_float32 (bool): Flag for converting the array of values to numpy float32.
94
+
95
+ Returns:
96
+ NDArray: Numpy array of y axis values.
97
+
98
+ """
99
+ y_axis = self._loader.get_y_axis(self._curve_id)
100
+ if is_float32:
101
+ y_axis = y_axis.astype(np.float32)
102
+ return y_axis
103
+
104
+ def get_x_unit(self) -> str:
105
+ """
106
+ Return the unit for the x-axis.
107
+
108
+ Returns
109
+ -------
110
+ str
111
+ The unit for the x-axis.
112
+
113
+ """
114
+ return self._loader.get_x_unit(self._curve_id)
115
+
116
+ def get_y_unit(self) -> str:
117
+ """
118
+ Return the unit for the y-axis.
119
+
120
+ Returns
121
+ -------
122
+ str
123
+ The unit for the y-axis.
124
+
125
+ """
126
+ return self._loader.get_y_unit(self._curve_id)
127
+
128
+ def get_loader(self) -> CurveLoader:
129
+ """
130
+ Return the loader instance used to retrieve curve data.
131
+
132
+ Returns
133
+ -------
134
+ CurveLoader
135
+ The loader instance associated with this curve.
136
+
137
+ """
138
+ return self._loader
139
+
140
+ def get_fingerprint(self) -> Fingerprint:
141
+ """
142
+ Return the fingerprint of the curve.
143
+
144
+ The method is not implemented yet.
145
+ """
146
+ raise NotImplementedError("Not implemented yet.")
@@ -0,0 +1,9 @@
1
+ # framcore/curves/__init__.py
2
+
3
+ from framcore.curves.Curve import Curve
4
+ from framcore.curves.LoadedCurve import LoadedCurve
5
+
6
+ __all__ = [
7
+ "Curve",
8
+ "LoadedCurve",
9
+ ]
@@ -0,0 +1,21 @@
1
+ # framcore/events/__init__.py
2
+
3
+ from framcore.events.events import (
4
+ get_event_handler,
5
+ set_event_handler,
6
+ send_debug_event,
7
+ send_error_event,
8
+ send_event,
9
+ send_info_event,
10
+ send_warning_event,
11
+ )
12
+
13
+ __all__ = [
14
+ "get_event_handler",
15
+ "send_debug_event",
16
+ "send_error_event",
17
+ "send_event",
18
+ "send_info_event",
19
+ "send_warning_event",
20
+ "set_event_handler",
21
+ ]
@@ -0,0 +1,51 @@
1
+ """
2
+ Event system.
3
+
4
+ All code in the core use these functions to communicate events.
5
+
6
+ Calling systems (e.g. workflow codes) can get events by hooking into SEND_EVENT_CHANNEL.
7
+ """
8
+
9
+ _EVENT_HANDLER = None
10
+
11
+
12
+ def set_event_handler(handler: object | None) -> None:
13
+ """Set event handler if any."""
14
+ if handler is not None and (not hasattr(handler, "handle_event") or not callable(handler.handle_event)):
15
+ message = "Given handler does not implement handle_event."
16
+ raise ValueError(message)
17
+ global _EVENT_HANDLER # noqa: PLW0603 # TODO: unsafe?
18
+ _EVENT_HANDLER = handler
19
+
20
+
21
+ def get_event_handler() -> object | None:
22
+ """Get event handler if any."""
23
+ return _EVENT_HANDLER
24
+
25
+
26
+ def send_event(sender: object, event_type: str, **kwargs: dict[str, object]) -> None:
27
+ """All events in core should use this."""
28
+ if _EVENT_HANDLER is None:
29
+ print(event_type, kwargs)
30
+ else:
31
+ _EVENT_HANDLER.handle_event(sender, event_type, **kwargs)
32
+
33
+
34
+ def send_warning_event(sender: object, message: str) -> None:
35
+ """Use this to send warning event."""
36
+ send_event(sender, "warning", message=message)
37
+
38
+
39
+ def send_error_event(sender: object, message: str, exception_type_name: str, traceback: str) -> None:
40
+ """Use this to send error event."""
41
+ send_event(sender, "error", message=message, exception_type_name=exception_type_name, traceback=traceback)
42
+
43
+
44
+ def send_info_event(sender: object, message: str) -> None:
45
+ """Use this to send info event."""
46
+ send_event(sender, "info", message=message)
47
+
48
+
49
+ def send_debug_event(sender: object, message: str) -> None:
50
+ """Use this to send debug event."""
51
+ send_event(sender, "debug", message=message)