physpyx 2.0.1__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.
@@ -1,11 +1,11 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: physpyx
3
- Version: 2.0.1
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.0.1"
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.10.9,<0.11.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=[P * i / n])
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 += "/".join(channel.label for channel in self.radioactivity)
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
- active = dict(re.findall(r"{(.*)} = {(.*)}", self._fname.read_text()))
44
+ current = dict(re.findall(r"{(.*)} = {(.*)}", self._fname.read_text()))
45
45
  else:
46
- active = {}
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
- if name == "mu0":
54
- qty = r"\qty[input-digits = 0123456789\pi]{4\pi e-7}{\tesla\meter\per\ampere}"
55
- try:
56
- outfile.write(f"\t{{{name}}} = {{{str(qty)}}},\n")
57
- if old := active.pop(name, None):
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}: {qty}")
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, qty in active.items():
71
- logger.info(f"remove {name}: {qty}")
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 "Pint" documentation "returns a
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, unit, fmt=fmt, base=base, compact=compact, preferred=preferred
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",
@@ -62,6 +72,7 @@ class SiunitxQuantity(Quantity):
62
72
  base: bool = False,
63
73
  compact: bool = False,
64
74
  preferred: bool = True,
75
+ alt: AltDict | bool = False,
65
76
  ) -> "SiunitxQuantity":
66
77
  """
67
78
  Return an instance of SiunitxQuantity
@@ -70,30 +81,41 @@ class SiunitxQuantity(Quantity):
70
81
  :param unit: Assigns the unit if any.
71
82
  :param fmt: Sets the amplitude "f-string" format.
72
83
  :param base: If true, converts the unit into SI units.
73
- :param compact: As described in the "Pint" documentation "returns a
84
+ :param compact: As described in the `Pint` documentation "returns a
74
85
  rescaled to compact, human-readable units".
75
86
  :param preferred: If true, attempts to convert units into preferred
76
87
  ones. For instance (kg.m/s2 will be converted to N). Note that, this
77
88
  defaults to "True" which means that all SiunitxQuantity resulting from
78
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.
79
92
  """
80
93
  quantity = cast(SiunitxQuantity, super().__new__(cls, value, units))
81
94
  quantity._fmt = fmt
82
95
  quantity._base = base
83
96
  quantity._compact = compact
84
97
  quantity._preferred = preferred
98
+ quantity._alt = cls._cast_alt_dict(alt)
85
99
  return quantity
86
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
+
87
108
  def __str__(self) -> str:
88
109
  quantity = self
89
110
  if self._base:
90
111
  quantity = cast(SiunitxQuantity, self.to_base_units())
91
112
  elif self._preferred:
92
- if sum(1 for _ in self.unit_items()) != 1:
93
- if to := self._preferred_units.get(self.dimensionality, None):
94
- quantity = cast(SiunitxQuantity, self.to(to))
95
- else:
96
- quantity = cast(SiunitxQuantity, self.to_reduced_units())
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
+ )
97
119
  if self._compact:
98
120
  quantity = cast(SiunitxQuantity, self.to_compact())
99
121
 
@@ -144,8 +166,8 @@ class SiunitxQuantity(Quantity):
144
166
  return rf"\num{{{out}}}"
145
167
 
146
168
  def __neg__(self) -> "SiunitxQuantity":
147
- # NOTE: only useful to fix typing warning Cannot access attribute "???"
148
- # for class "PlainQuantity[Unknown]"
169
+ # NOTE: to fix typing warning 'Cannot access attribute "???" for class
170
+ # "PlainQuantity[Unknown]"'
149
171
  return cast(SiunitxQuantity, super().__neg__())
150
172
 
151
173
  def _format_unit(self) -> str:
@@ -172,12 +194,33 @@ class SiunitxQuantity(Quantity):
172
194
  tmp += r"\cubed"
173
195
  else:
174
196
  tmp += rf"\tothe{{{p:f}}}"
175
- if power >= 0:
197
+ if power >= 0.0:
176
198
  num += tmp
177
199
  else:
178
200
  den += r"\per" + tmp
179
201
  return num + den
180
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
+
181
224
  def fmtex(
182
225
  self,
183
226
  *,
@@ -186,15 +229,24 @@ class SiunitxQuantity(Quantity):
186
229
  base: bool | None = None,
187
230
  compact: bool | None = None,
188
231
  preferred: bool | None = None,
232
+ alt: AltDict | bool = False,
189
233
  ) -> "SiunitxQuantity":
190
234
  fmt = self._fmt if fmt is None else fmt
191
235
  compact = self._compact if compact is None else compact
192
236
  base = self._base if base is None else base
193
237
  preferred = self._preferred if preferred is None else preferred
238
+ alt = self._cast_alt_dict(alt)
239
+
194
240
  if to == "":
195
241
  m = self.m if self.units != "percent" else self.m / 100
196
242
  return self.__class__(
197
- m, "", fmt=fmt, base=False, compact=compact, preferred=preferred
243
+ m,
244
+ "",
245
+ fmt=fmt,
246
+ base=False,
247
+ compact=compact,
248
+ preferred=preferred,
249
+ alt=alt,
198
250
  )
199
251
 
200
252
  if to:
@@ -203,10 +255,17 @@ class SiunitxQuantity(Quantity):
203
255
  q._base = False
204
256
  q._compact = compact
205
257
  q._preferred = False
258
+ q._alt = alt
206
259
  return q
207
260
 
208
261
  return self.__class__(
209
- self.m, self.units, fmt=fmt, base=base, compact=compact, preferred=preferred
262
+ self.m,
263
+ self.units,
264
+ fmt=fmt,
265
+ base=base,
266
+ compact=compact,
267
+ preferred=preferred,
268
+ alt=alt,
210
269
  )
211
270
 
212
271
  def norm(self) -> "SiunitxQuantity":
File without changes
File without changes
File without changes
File without changes
File without changes