frequenz-quantities 1.0.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.
- frequenz/quantities/__init__.py +112 -0
- frequenz/quantities/_apparent_power.py +234 -0
- frequenz/quantities/_current.py +131 -0
- frequenz/quantities/_energy.py +188 -0
- frequenz/quantities/_frequency.py +115 -0
- frequenz/quantities/_percentage.py +66 -0
- frequenz/quantities/_power.py +244 -0
- frequenz/quantities/_quantity.py +541 -0
- frequenz/quantities/_reactive_power.py +235 -0
- frequenz/quantities/_temperature.py +39 -0
- frequenz/quantities/_voltage.py +148 -0
- frequenz/quantities/conftest.py +13 -0
- frequenz/quantities/experimental/__init__.py +11 -0
- frequenz/quantities/experimental/marshmallow.py +271 -0
- frequenz/quantities/py.typed +0 -0
- frequenz_quantities-1.0.0.dist-info/LICENSE +21 -0
- frequenz_quantities-1.0.0.dist-info/METADATA +103 -0
- frequenz_quantities-1.0.0.dist-info/RECORD +20 -0
- frequenz_quantities-1.0.0.dist-info/WHEEL +5 -0
- frequenz_quantities-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Types for holding reactive power quantities with units."""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Self, overload
|
|
10
|
+
|
|
11
|
+
from ._quantity import NoDefaultConstructible, Quantity
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ._current import Current
|
|
15
|
+
from ._percentage import Percentage
|
|
16
|
+
from ._voltage import Voltage
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ReactivePower(
|
|
20
|
+
Quantity,
|
|
21
|
+
metaclass=NoDefaultConstructible,
|
|
22
|
+
exponent_unit_map={
|
|
23
|
+
-3: "mVAR",
|
|
24
|
+
0: "VAR",
|
|
25
|
+
3: "kVAR",
|
|
26
|
+
6: "MVAR",
|
|
27
|
+
},
|
|
28
|
+
):
|
|
29
|
+
"""A reactive power quantity.
|
|
30
|
+
|
|
31
|
+
Objects of this type are wrappers around `float` values and are immutable.
|
|
32
|
+
|
|
33
|
+
The constructors accept a single `float` value, the `as_*()` methods return a
|
|
34
|
+
`float` value, and each of the arithmetic operators supported by this type are
|
|
35
|
+
actually implemented using floating-point arithmetic.
|
|
36
|
+
|
|
37
|
+
So all considerations about floating-point arithmetic apply to this type as well.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def from_volt_amperes_reactive(cls, value: float) -> Self:
|
|
42
|
+
"""Initialize a new reactive power quantity.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
value: The reactive power in volt-amperes reactive (VAR).
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A new reactive power quantity.
|
|
49
|
+
"""
|
|
50
|
+
return cls._new(value)
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def from_milli_volt_amperes_reactive(cls, mvars: float) -> Self:
|
|
54
|
+
"""Initialize a new reactive power quantity.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
mvars: The reactive power in millivolt-amperes reactive (mVAR).
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
A new reactive power quantity.
|
|
61
|
+
"""
|
|
62
|
+
return cls._new(mvars, exponent=-3)
|
|
63
|
+
|
|
64
|
+
@classmethod
|
|
65
|
+
def from_kilo_volt_amperes_reactive(cls, kvars: float) -> Self:
|
|
66
|
+
"""Initialize a new reactive power quantity.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
kvars: The reactive power in kilovolt-amperes reactive (kVAR).
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
A new reactive power quantity.
|
|
73
|
+
"""
|
|
74
|
+
return cls._new(kvars, exponent=3)
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def from_mega_volt_amperes_reactive(cls, mvars: float) -> Self:
|
|
78
|
+
"""Initialize a new reactive power quantity.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
mvars: The reactive power in megavolt-amperes reactive (MVAR).
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
A new reactive power quantity.
|
|
85
|
+
"""
|
|
86
|
+
return cls._new(mvars, exponent=6)
|
|
87
|
+
|
|
88
|
+
def as_volt_amperes_reactive(self) -> float:
|
|
89
|
+
"""Return the reactive power in volt-amperes reactive (VAR).
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
The reactive power in volt-amperes reactive (VAR).
|
|
93
|
+
"""
|
|
94
|
+
return self._base_value
|
|
95
|
+
|
|
96
|
+
def as_milli_volt_amperes_reactive(self) -> float:
|
|
97
|
+
"""Return the reactive power in millivolt-amperes reactive (mVAR).
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
The reactive power in millivolt-amperes reactive (mVAR).
|
|
101
|
+
"""
|
|
102
|
+
return self._base_value * 1e3
|
|
103
|
+
|
|
104
|
+
def as_kilo_volt_amperes_reactive(self) -> float:
|
|
105
|
+
"""Return the reactive power in kilovolt-amperes reactive (kVAR).
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
The reactive power in kilovolt-amperes reactive (kVAR).
|
|
109
|
+
"""
|
|
110
|
+
return self._base_value / 1e3
|
|
111
|
+
|
|
112
|
+
def as_mega_volt_amperes_reactive(self) -> float:
|
|
113
|
+
"""Return the reactive power in megavolt-amperes reactive (MVAR).
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
The reactive power in megavolt-amperes reactive (MVAR).
|
|
117
|
+
"""
|
|
118
|
+
return self._base_value / 1e6
|
|
119
|
+
|
|
120
|
+
@overload
|
|
121
|
+
def __mul__(self, scalar: float, /) -> Self:
|
|
122
|
+
"""Scale this power by a scalar.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
scalar: The scalar by which to scale this power.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
The scaled power.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
@overload
|
|
132
|
+
def __mul__(self, percent: Percentage, /) -> Self:
|
|
133
|
+
"""Scale this power by a percentage.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
percent: The percentage by which to scale this power.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
The scaled power.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
def __mul__(self, other: float | Percentage, /) -> Self:
|
|
143
|
+
"""Return a power or energy from multiplying this power by the given value.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
other: The scalar, percentage or duration to multiply by.
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
A power or energy.
|
|
150
|
+
"""
|
|
151
|
+
from ._percentage import Percentage # pylint: disable=import-outside-toplevel
|
|
152
|
+
|
|
153
|
+
match other:
|
|
154
|
+
case float() | Percentage():
|
|
155
|
+
return super().__mul__(other)
|
|
156
|
+
case _:
|
|
157
|
+
return NotImplemented
|
|
158
|
+
|
|
159
|
+
# We need the ignore here because otherwise mypy will give this error:
|
|
160
|
+
# > Overloaded operator methods can't have wider argument types in overrides
|
|
161
|
+
# The problem seems to be when the other type implements an **incompatible**
|
|
162
|
+
# __rmul__ method, which is not the case here, so we should be safe.
|
|
163
|
+
# Please see this example:
|
|
164
|
+
# https://github.com/python/mypy/blob/c26f1297d4f19d2d1124a30efc97caebb8c28616/test-data/unit/check-overloading.test#L4738C1-L4769C55
|
|
165
|
+
# And a discussion in a mypy issue here:
|
|
166
|
+
# https://github.com/python/mypy/issues/4985#issuecomment-389692396
|
|
167
|
+
@overload # type: ignore[override]
|
|
168
|
+
def __truediv__(self, other: float, /) -> Self:
|
|
169
|
+
"""Divide this power by a scalar.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
other: The scalar to divide this power by.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
The divided power.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
@overload
|
|
179
|
+
def __truediv__(self, other: Self, /) -> float:
|
|
180
|
+
"""Return the ratio of this power to another.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
other: The other power.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
The ratio of this power to another.
|
|
187
|
+
"""
|
|
188
|
+
|
|
189
|
+
@overload
|
|
190
|
+
def __truediv__(self, current: Current, /) -> Voltage:
|
|
191
|
+
"""Return a voltage from dividing this power by the given current.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
current: The current to divide by.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
A voltage from dividing this power by the a current.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
@overload
|
|
201
|
+
def __truediv__(self, voltage: Voltage, /) -> Current:
|
|
202
|
+
"""Return a current from dividing this power by the given voltage.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
voltage: The voltage to divide by.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
A current from dividing this power by a voltage.
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
def __truediv__(
|
|
212
|
+
self, other: float | Self | Current | Voltage, /
|
|
213
|
+
) -> Self | float | Voltage | Current:
|
|
214
|
+
"""Return a current or voltage from dividing this power by the given value.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
other: The scalar, power, current or voltage to divide by.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
A current or voltage from dividing this power by the given value.
|
|
221
|
+
"""
|
|
222
|
+
from ._current import Current # pylint: disable=import-outside-toplevel
|
|
223
|
+
from ._voltage import Voltage # pylint: disable=import-outside-toplevel
|
|
224
|
+
|
|
225
|
+
match other:
|
|
226
|
+
case float():
|
|
227
|
+
return super().__truediv__(other)
|
|
228
|
+
case ReactivePower():
|
|
229
|
+
return self._base_value / other._base_value
|
|
230
|
+
case Current():
|
|
231
|
+
return Voltage._new(self._base_value / other._base_value)
|
|
232
|
+
case Voltage():
|
|
233
|
+
return Current._new(self._base_value / other._base_value)
|
|
234
|
+
case _:
|
|
235
|
+
return NotImplemented
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2022 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Types for holding quantities with units."""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from typing import Self
|
|
8
|
+
|
|
9
|
+
from ._quantity import NoDefaultConstructible, Quantity
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Temperature(
|
|
13
|
+
Quantity,
|
|
14
|
+
metaclass=NoDefaultConstructible,
|
|
15
|
+
exponent_unit_map={
|
|
16
|
+
0: "°C",
|
|
17
|
+
},
|
|
18
|
+
):
|
|
19
|
+
"""A temperature quantity (in degrees Celsius)."""
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_celsius(cls, value: float) -> Self:
|
|
23
|
+
"""Initialize a new temperature quantity.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
value: The temperature in degrees Celsius.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
A new temperature quantity.
|
|
30
|
+
"""
|
|
31
|
+
return cls._new(value)
|
|
32
|
+
|
|
33
|
+
def as_celsius(self) -> float:
|
|
34
|
+
"""Return the temperature in degrees Celsius.
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
The temperature in degrees Celsius.
|
|
38
|
+
"""
|
|
39
|
+
return self._base_value
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2022 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Types for holding quantities with units."""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Self, overload
|
|
10
|
+
|
|
11
|
+
from ._quantity import NoDefaultConstructible, Quantity
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from ._current import Current
|
|
15
|
+
from ._percentage import Percentage
|
|
16
|
+
from ._power import Power
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Voltage(
|
|
20
|
+
Quantity,
|
|
21
|
+
metaclass=NoDefaultConstructible,
|
|
22
|
+
exponent_unit_map={0: "V", -3: "mV", 3: "kV"},
|
|
23
|
+
):
|
|
24
|
+
"""A voltage quantity.
|
|
25
|
+
|
|
26
|
+
Objects of this type are wrappers around `float` values and are immutable.
|
|
27
|
+
|
|
28
|
+
The constructors accept a single `float` value, the `as_*()` methods return a
|
|
29
|
+
`float` value, and each of the arithmetic operators supported by this type are
|
|
30
|
+
actually implemented using floating-point arithmetic.
|
|
31
|
+
|
|
32
|
+
So all considerations about floating-point arithmetic apply to this type as well.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@classmethod
|
|
36
|
+
def from_volts(cls, volts: float) -> Self:
|
|
37
|
+
"""Initialize a new voltage quantity.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
volts: The voltage in volts.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
A new voltage quantity.
|
|
44
|
+
"""
|
|
45
|
+
return cls._new(volts)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_millivolts(cls, millivolts: float) -> Self:
|
|
49
|
+
"""Initialize a new voltage quantity.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
millivolts: The voltage in millivolts.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
A new voltage quantity.
|
|
56
|
+
"""
|
|
57
|
+
return cls._new(millivolts, exponent=-3)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def from_kilovolts(cls, kilovolts: float) -> Self:
|
|
61
|
+
"""Initialize a new voltage quantity.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
kilovolts: The voltage in kilovolts.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
A new voltage quantity.
|
|
68
|
+
"""
|
|
69
|
+
return cls._new(kilovolts, exponent=3)
|
|
70
|
+
|
|
71
|
+
def as_volts(self) -> float:
|
|
72
|
+
"""Return the voltage in volts.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
The voltage in volts.
|
|
76
|
+
"""
|
|
77
|
+
return self._base_value
|
|
78
|
+
|
|
79
|
+
def as_millivolts(self) -> float:
|
|
80
|
+
"""Return the voltage in millivolts.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
The voltage in millivolts.
|
|
84
|
+
"""
|
|
85
|
+
return self._base_value * 1e3
|
|
86
|
+
|
|
87
|
+
def as_kilovolts(self) -> float:
|
|
88
|
+
"""Return the voltage in kilovolts.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
The voltage in kilovolts.
|
|
92
|
+
"""
|
|
93
|
+
return self._base_value / 1e3
|
|
94
|
+
|
|
95
|
+
# See comment for Power.__mul__ for why we need the ignore here.
|
|
96
|
+
@overload # type: ignore[override]
|
|
97
|
+
def __mul__(self, scalar: float, /) -> Self:
|
|
98
|
+
"""Scale this voltage by a scalar.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
scalar: The scalar by which to scale this voltage.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
The scaled voltage.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
@overload
|
|
108
|
+
def __mul__(self, percent: Percentage, /) -> Self:
|
|
109
|
+
"""Scale this voltage by a percentage.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
percent: The percentage by which to scale this voltage.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
The scaled voltage.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
@overload
|
|
119
|
+
def __mul__(self, other: Current, /) -> Power:
|
|
120
|
+
"""Multiply the voltage by the current to get the power.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
other: The current to multiply the voltage with.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
The calculated power.
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
def __mul__(self, other: float | Percentage | Current, /) -> Self | Power:
|
|
130
|
+
"""Return a voltage or power from multiplying this voltage by the given value.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
other: The scalar, percentage or current to multiply by.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
The calculated voltage or power.
|
|
137
|
+
"""
|
|
138
|
+
from ._current import Current # pylint: disable=import-outside-toplevel
|
|
139
|
+
from ._percentage import Percentage # pylint: disable=import-outside-toplevel
|
|
140
|
+
from ._power import Power # pylint: disable=import-outside-toplevel
|
|
141
|
+
|
|
142
|
+
match other:
|
|
143
|
+
case float() | Percentage():
|
|
144
|
+
return super().__mul__(other)
|
|
145
|
+
case Current():
|
|
146
|
+
return Power._new(self._base_value * other._base_value)
|
|
147
|
+
case _:
|
|
148
|
+
return NotImplemented
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# License: MIT
|
|
2
|
+
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Validate docstring code examples.
|
|
5
|
+
|
|
6
|
+
Code examples are often wrapped in triple backticks (```) within docstrings.
|
|
7
|
+
This plugin extracts these code examples and validates them using pylint.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from frequenz.repo.config.pytest import examples
|
|
11
|
+
from sybil import Sybil
|
|
12
|
+
|
|
13
|
+
pytest_collect_file = Sybil(**examples.get_sybil_arguments()).pytest()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# License: All rights reserved
|
|
2
|
+
# Copyright © 2024 Frequenz Energy-as-a-Service GmbH
|
|
3
|
+
|
|
4
|
+
"""Experimental features for quantities.
|
|
5
|
+
|
|
6
|
+
Danger:
|
|
7
|
+
This package contains experimental features for which the API is not yet stable.
|
|
8
|
+
|
|
9
|
+
Any module or class in this package may be removed or changed in a future release,
|
|
10
|
+
even in minor or patch releases.
|
|
11
|
+
"""
|