physpyx 2.0.0__tar.gz → 2.1.0__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.
- {physpyx-2.0.0 → physpyx-2.1.0}/PKG-INFO +3 -3
- {physpyx-2.0.0 → physpyx-2.1.0}/pyproject.toml +4 -3
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/astronomy.py +1 -1
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/nuclide.py +2 -2
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/qty.py +22 -22
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/quantity.py +76 -16
- {physpyx-2.0.0 → physpyx-2.1.0}/README.md +0 -0
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/__init__.py +0 -0
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/data/all-nuclides.csv +0 -0
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/helper.py +0 -0
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/manager.py +0 -0
- {physpyx-2.0.0 → physpyx-2.1.0}/src/physpyx/physpyx.py +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: physpyx
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Provides a way to use python code in LaTeX
|
|
5
5
|
Author: Jérôme Dufour
|
|
6
6
|
Author-email: Jérôme Dufour <jerome.dufour@eduvaud.ch>
|
|
7
|
+
License-Expression: GPL-3.0-or-later
|
|
7
8
|
Classifier: Programming Language :: Python :: 3
|
|
8
|
-
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
9
9
|
Classifier: Operating System :: Unix
|
|
10
10
|
Classifier: Topic :: Education
|
|
11
11
|
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "physpyx"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.1.0"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="Jérôme Dufour", email="jerome.dufour@eduvaud.ch" },
|
|
6
6
|
]
|
|
7
7
|
description = "Provides a way to use python code in LaTeX"
|
|
8
8
|
classifiers = [
|
|
9
9
|
"Programming Language :: Python :: 3",
|
|
10
|
-
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
|
11
10
|
"Operating System :: Unix",
|
|
12
11
|
"Topic :: Education",
|
|
13
12
|
"Topic :: Scientific/Engineering :: Physics"
|
|
14
13
|
]
|
|
14
|
+
license = "GPL-3.0-or-later"
|
|
15
15
|
readme = "README.md"
|
|
16
16
|
requires-python = ">=3.13"
|
|
17
17
|
dependencies = [
|
|
@@ -24,6 +24,7 @@ dependencies = [
|
|
|
24
24
|
dev = [
|
|
25
25
|
"mypy>=1.18.1",
|
|
26
26
|
"ipdb>=0.13.13",
|
|
27
|
+
"scipy-stubs>=1.17.1.5",
|
|
27
28
|
]
|
|
28
29
|
docs = [
|
|
29
30
|
"sphinx>=7.1.2",
|
|
@@ -36,7 +37,7 @@ docs = [
|
|
|
36
37
|
]
|
|
37
38
|
|
|
38
39
|
[build-system]
|
|
39
|
-
requires = ["uv_build>=0.
|
|
40
|
+
requires = ["uv_build>=0.11.0,<0.12.0"]
|
|
40
41
|
build-backend = "uv_build"
|
|
41
42
|
|
|
42
43
|
[project.urls]
|
|
@@ -90,7 +90,7 @@ class Orbit:
|
|
|
90
90
|
return self.a * ellipkinc(phi, m) - arc_length
|
|
91
91
|
|
|
92
92
|
for i in range(n + 1):
|
|
93
|
-
phi = np.pi / 2 - newton(func, x0=np.pi / 2 * i / n, args=
|
|
93
|
+
phi = np.pi / 2 - newton(func, x0=np.pi / 2 * i / n, args=(P * i / n))
|
|
94
94
|
r = self.b / np.sqrt(1 - (self.e * np.cos(phi)) ** 2)
|
|
95
95
|
yield np.array([-self.F + r * np.cos(phi), r * np.sin(phi)])
|
|
96
96
|
|
|
@@ -224,7 +224,7 @@ class Nuclide:
|
|
|
224
224
|
if with_mass:
|
|
225
225
|
label += rf"\num{{{self.atomic_mass:.6f}}}"
|
|
226
226
|
if with_decay:
|
|
227
|
-
label +=
|
|
227
|
+
label += str(self.radioactivity.decays.channels)
|
|
228
228
|
if label:
|
|
229
229
|
yield rf"\node[anchor=south] at ({self.N + 0.5}, {self.Z}) {{\tiny{{{label}}}}};"
|
|
230
230
|
|
|
@@ -337,7 +337,7 @@ class NuclideTable:
|
|
|
337
337
|
:param y: y-position of the label (top of table if x=Zmax+1, bottom of table if x=Zmin-1)
|
|
338
338
|
:param vertical: orientation of the label
|
|
339
339
|
"""
|
|
340
|
-
decays = {c.decay for n in nuclides for c in n.radioactivity.decays}
|
|
340
|
+
decays = {c.decay for n in nuclides for c in n.radioactivity.decays.channels}
|
|
341
341
|
for i, decay in enumerate(sorted(decays, key=lambda x: x.name)):
|
|
342
342
|
pos = f"({x}, {y - i})" if vertical else f"({x + i}, {y})"
|
|
343
343
|
yield rf"\fill[color={decay.color}!60] {pos} rectangle ++(1, 1) node[midway, black]{{{decay.label}}};"
|
|
@@ -4,7 +4,7 @@ import sys
|
|
|
4
4
|
from logging import getLogger
|
|
5
5
|
from pint._typing import Scalar
|
|
6
6
|
from physpyx.manager import PTManager
|
|
7
|
-
from physpyx.quantity import Constants, SiunitxQuantity, SiunitxUnitRegistry
|
|
7
|
+
from physpyx.quantity import AltDict, Constants, SiunitxQuantity, SiunitxUnitRegistry
|
|
8
8
|
|
|
9
9
|
logger = getLogger(__name__)
|
|
10
10
|
|
|
@@ -41,34 +41,25 @@ class Qty(PTManager):
|
|
|
41
41
|
|
|
42
42
|
def _write_successfile(self) -> None:
|
|
43
43
|
if self._fname.exists():
|
|
44
|
-
|
|
44
|
+
current = dict(re.findall(r"{(.*)} = {(.*)}", self._fname.read_text()))
|
|
45
45
|
else:
|
|
46
|
-
|
|
46
|
+
current = {}
|
|
47
47
|
with self._fname.open("w") as outfile:
|
|
48
48
|
outfile.write(
|
|
49
49
|
"\\ExplSyntaxOn\n\\prop_gset_from_keyval:Nn \\g_physpyx_pty_prop {\n"
|
|
50
50
|
)
|
|
51
51
|
for name, qty in sys._getframe(2).f_locals.items():
|
|
52
52
|
if isinstance(qty, SiunitxQuantity):
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if old != str(qty):
|
|
59
|
-
logger.info(f"update {name}: {old} -> {qty}")
|
|
53
|
+
for name, new in qty.export(name):
|
|
54
|
+
outfile.write(f"\t{{{name}}} = {{{new}}},\n")
|
|
55
|
+
if old := current.pop(name, None):
|
|
56
|
+
if old != new:
|
|
57
|
+
logger.info(f"update {name}: {old} -> {new}")
|
|
60
58
|
else:
|
|
61
|
-
logger.info(f"add {name}
|
|
62
|
-
except ValueError as e:
|
|
63
|
-
# TODO: report the correct error in
|
|
64
|
-
# PTManager.write_errorfile, not the one raised in
|
|
65
|
-
# PTManager.__exit__
|
|
66
|
-
logger.error(f"exporting {name} failed, {qty._fmt}")
|
|
67
|
-
e.add_note(f"exporting {name} failed")
|
|
68
|
-
raise e
|
|
59
|
+
logger.info(f"add {name}={new}")
|
|
69
60
|
|
|
70
|
-
for name,
|
|
71
|
-
logger.info(f"remove {name}: {
|
|
61
|
+
for name, old in current.items():
|
|
62
|
+
logger.info(f"remove {name}: {old}")
|
|
72
63
|
outfile.write("}\n\\ExplSyntaxOff\n")
|
|
73
64
|
|
|
74
65
|
def __call__(
|
|
@@ -80,6 +71,7 @@ class Qty(PTManager):
|
|
|
80
71
|
base: bool = False,
|
|
81
72
|
compact: bool = False,
|
|
82
73
|
preferred: bool = False,
|
|
74
|
+
alt: AltDict | bool = False,
|
|
83
75
|
) -> SiunitxQuantity:
|
|
84
76
|
"""
|
|
85
77
|
Return an instance of SiunitxQuantity
|
|
@@ -94,11 +86,13 @@ class Qty(PTManager):
|
|
|
94
86
|
:param to: Immediately converts unit.
|
|
95
87
|
:param fmt: Sets the "f-string" format of the magnitude.
|
|
96
88
|
:param base: If true, converts the unit into SI units.
|
|
97
|
-
:param compact: As described in the
|
|
89
|
+
:param compact: As described in the `Pint` documentation "returns a
|
|
98
90
|
rescaled, human-readable unit".
|
|
99
91
|
:param preferred: If true, attempts to convert unit into simpler one.
|
|
100
92
|
For instance (kg.m/s2 will be converted to N). Note that, this defaults
|
|
101
93
|
to "False" only here.
|
|
94
|
+
:param alt: If provided, writes an additional alternative output with
|
|
95
|
+
different formatting. The string '_alt' is added to the base name.
|
|
102
96
|
"""
|
|
103
97
|
if isinstance(value, str):
|
|
104
98
|
if unit is not None:
|
|
@@ -106,6 +100,12 @@ class Qty(PTManager):
|
|
|
106
100
|
value, unit, fmt = self._constants(value, fmt)
|
|
107
101
|
|
|
108
102
|
q = self._ureg.Quantity(
|
|
109
|
-
value,
|
|
103
|
+
value,
|
|
104
|
+
unit,
|
|
105
|
+
fmt=fmt,
|
|
106
|
+
base=base,
|
|
107
|
+
compact=compact,
|
|
108
|
+
preferred=preferred,
|
|
109
|
+
alt=alt,
|
|
110
110
|
)
|
|
111
111
|
return q if to is None else q.fmtex(to=to)
|
|
@@ -4,18 +4,28 @@ from numpy import cross, ndarray, linalg
|
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from pint._typing import Magnitude, UnitLike
|
|
6
6
|
from pint.registry import GenericUnitRegistry, Quantity, Unit
|
|
7
|
-
from typing import cast
|
|
7
|
+
from typing import Generator, TypedDict, cast
|
|
8
8
|
|
|
9
9
|
from physpyx.nuclide import NuclideTable
|
|
10
10
|
|
|
11
11
|
logger = getLogger(__name__)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
class AltDict(TypedDict, total=False):
|
|
15
|
+
fmt: str
|
|
16
|
+
base: bool
|
|
17
|
+
preferred: bool
|
|
18
|
+
compact: bool
|
|
19
|
+
to: str
|
|
20
|
+
alt: "AltDict"
|
|
21
|
+
|
|
22
|
+
|
|
14
23
|
class SiunitxQuantity(Quantity):
|
|
15
24
|
_fmt: str
|
|
16
25
|
_base: bool
|
|
17
26
|
_compact: bool
|
|
18
27
|
_preferred: bool
|
|
28
|
+
_alt: AltDict
|
|
19
29
|
|
|
20
30
|
_sinunitx_map = {
|
|
21
31
|
"astronomical_unit": "astronomicalunit",
|
|
@@ -50,6 +60,7 @@ class SiunitxQuantity(Quantity):
|
|
|
50
60
|
"tesla",
|
|
51
61
|
"henry",
|
|
52
62
|
"farad",
|
|
63
|
+
"coulomb",
|
|
53
64
|
]
|
|
54
65
|
}
|
|
55
66
|
|
|
@@ -61,6 +72,7 @@ class SiunitxQuantity(Quantity):
|
|
|
61
72
|
base: bool = False,
|
|
62
73
|
compact: bool = False,
|
|
63
74
|
preferred: bool = True,
|
|
75
|
+
alt: AltDict | bool = False,
|
|
64
76
|
) -> "SiunitxQuantity":
|
|
65
77
|
"""
|
|
66
78
|
Return an instance of SiunitxQuantity
|
|
@@ -69,30 +81,41 @@ class SiunitxQuantity(Quantity):
|
|
|
69
81
|
:param unit: Assigns the unit if any.
|
|
70
82
|
:param fmt: Sets the amplitude "f-string" format.
|
|
71
83
|
:param base: If true, converts the unit into SI units.
|
|
72
|
-
:param compact: As described in the
|
|
84
|
+
:param compact: As described in the `Pint` documentation "returns a
|
|
73
85
|
rescaled to compact, human-readable units".
|
|
74
|
-
:param preferred: If true, attempts to convert
|
|
75
|
-
For instance (kg.m/s2 will be converted to N). Note that, this
|
|
76
|
-
to "True" which means that all SiunitxQuantity resulting from
|
|
86
|
+
:param preferred: If true, attempts to convert units into preferred
|
|
87
|
+
ones. For instance (kg.m/s2 will be converted to N). Note that, this
|
|
88
|
+
defaults to "True" which means that all SiunitxQuantity resulting from
|
|
77
89
|
mathematical operations, will be converted.
|
|
90
|
+
:param alt: If provided, writes an additional alternative output with
|
|
91
|
+
different formatting. The string '_alt' is added to the base name.
|
|
78
92
|
"""
|
|
79
93
|
quantity = cast(SiunitxQuantity, super().__new__(cls, value, units))
|
|
80
94
|
quantity._fmt = fmt
|
|
81
95
|
quantity._base = base
|
|
82
96
|
quantity._compact = compact
|
|
83
97
|
quantity._preferred = preferred
|
|
98
|
+
quantity._alt = cls._cast_alt_dict(alt)
|
|
84
99
|
return quantity
|
|
85
100
|
|
|
101
|
+
@classmethod
|
|
102
|
+
def _cast_alt_dict(cls, alt: AltDict | bool) -> AltDict:
|
|
103
|
+
if isinstance(alt, bool):
|
|
104
|
+
return {"preferred": True} if alt else AltDict()
|
|
105
|
+
else:
|
|
106
|
+
return alt
|
|
107
|
+
|
|
86
108
|
def __str__(self) -> str:
|
|
87
109
|
quantity = self
|
|
88
110
|
if self._base:
|
|
89
111
|
quantity = cast(SiunitxQuantity, self.to_base_units())
|
|
90
112
|
elif self._preferred:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
113
|
+
quantity = cast(
|
|
114
|
+
SiunitxQuantity,
|
|
115
|
+
self.to(to)
|
|
116
|
+
if (to := self._preferred_units.get(self.dimensionality, None))
|
|
117
|
+
else self.to_reduced_units(),
|
|
118
|
+
)
|
|
96
119
|
if self._compact:
|
|
97
120
|
quantity = cast(SiunitxQuantity, self.to_compact())
|
|
98
121
|
|
|
@@ -143,8 +166,8 @@ class SiunitxQuantity(Quantity):
|
|
|
143
166
|
return rf"\num{{{out}}}"
|
|
144
167
|
|
|
145
168
|
def __neg__(self) -> "SiunitxQuantity":
|
|
146
|
-
# NOTE:
|
|
147
|
-
#
|
|
169
|
+
# NOTE: to fix typing warning 'Cannot access attribute "???" for class
|
|
170
|
+
# "PlainQuantity[Unknown]"'
|
|
148
171
|
return cast(SiunitxQuantity, super().__neg__())
|
|
149
172
|
|
|
150
173
|
def _format_unit(self) -> str:
|
|
@@ -171,12 +194,33 @@ class SiunitxQuantity(Quantity):
|
|
|
171
194
|
tmp += r"\cubed"
|
|
172
195
|
else:
|
|
173
196
|
tmp += rf"\tothe{{{p:f}}}"
|
|
174
|
-
if power >= 0:
|
|
197
|
+
if power >= 0.0:
|
|
175
198
|
num += tmp
|
|
176
199
|
else:
|
|
177
200
|
den += r"\per" + tmp
|
|
178
201
|
return num + den
|
|
179
202
|
|
|
203
|
+
def export(self, name: str) -> Generator[tuple[str, str], None, None]:
|
|
204
|
+
try:
|
|
205
|
+
yield (
|
|
206
|
+
name,
|
|
207
|
+
r"\qty[input-digits = 0123456789\pi]{4\pi e-7}{\tesla\meter\per\ampere}"
|
|
208
|
+
if name == "mu0"
|
|
209
|
+
else str(self),
|
|
210
|
+
)
|
|
211
|
+
except ValueError as e:
|
|
212
|
+
# TODO: report the correct error in
|
|
213
|
+
# PTManager.write_errorfile, not the one raised in
|
|
214
|
+
# PTManager.__exit__
|
|
215
|
+
logger.error(f"exporting {name} failed, {self._fmt}")
|
|
216
|
+
e.add_note(f"exporting {name} failed")
|
|
217
|
+
raise e
|
|
218
|
+
|
|
219
|
+
if self._alt:
|
|
220
|
+
alt = self.fmtex(**self._alt)
|
|
221
|
+
alt._alt = AltDict()
|
|
222
|
+
yield from alt.export(name + "_alt")
|
|
223
|
+
|
|
180
224
|
def fmtex(
|
|
181
225
|
self,
|
|
182
226
|
*,
|
|
@@ -185,15 +229,24 @@ class SiunitxQuantity(Quantity):
|
|
|
185
229
|
base: bool | None = None,
|
|
186
230
|
compact: bool | None = None,
|
|
187
231
|
preferred: bool | None = None,
|
|
232
|
+
alt: AltDict | bool = False,
|
|
188
233
|
) -> "SiunitxQuantity":
|
|
189
234
|
fmt = self._fmt if fmt is None else fmt
|
|
190
235
|
compact = self._compact if compact is None else compact
|
|
191
236
|
base = self._base if base is None else base
|
|
192
237
|
preferred = self._preferred if preferred is None else preferred
|
|
238
|
+
alt = self._cast_alt_dict(alt)
|
|
239
|
+
|
|
193
240
|
if to == "":
|
|
194
241
|
m = self.m if self.units != "percent" else self.m / 100
|
|
195
242
|
return self.__class__(
|
|
196
|
-
m,
|
|
243
|
+
m,
|
|
244
|
+
"",
|
|
245
|
+
fmt=fmt,
|
|
246
|
+
base=False,
|
|
247
|
+
compact=compact,
|
|
248
|
+
preferred=preferred,
|
|
249
|
+
alt=alt,
|
|
197
250
|
)
|
|
198
251
|
|
|
199
252
|
if to:
|
|
@@ -202,10 +255,17 @@ class SiunitxQuantity(Quantity):
|
|
|
202
255
|
q._base = False
|
|
203
256
|
q._compact = compact
|
|
204
257
|
q._preferred = False
|
|
258
|
+
q._alt = alt
|
|
205
259
|
return q
|
|
206
260
|
|
|
207
261
|
return self.__class__(
|
|
208
|
-
self.m,
|
|
262
|
+
self.m,
|
|
263
|
+
self.units,
|
|
264
|
+
fmt=fmt,
|
|
265
|
+
base=base,
|
|
266
|
+
compact=compact,
|
|
267
|
+
preferred=preferred,
|
|
268
|
+
alt=alt,
|
|
209
269
|
)
|
|
210
270
|
|
|
211
271
|
def norm(self) -> "SiunitxQuantity":
|
|
@@ -241,7 +301,7 @@ class Constants:
|
|
|
241
301
|
"k": (8.987551e9, "newton meter squared per coulomb squared", ".0e"),
|
|
242
302
|
"epsilon0": (
|
|
243
303
|
8.8541878188e-12,
|
|
244
|
-
"coulomb squared per newton meter
|
|
304
|
+
"coulomb squared per newton meter squared",
|
|
245
305
|
".3e",
|
|
246
306
|
),
|
|
247
307
|
"mu0": (1.2566370612696005e-06, "tesla meter per ampere", ".3e"),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|