diffpes 2026.3.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 (55) hide show
  1. diffpes/__init__.py +65 -0
  2. diffpes/inout/__init__.py +88 -0
  3. diffpes/inout/chgcar.py +459 -0
  4. diffpes/inout/doscar.py +248 -0
  5. diffpes/inout/eigenval.py +258 -0
  6. diffpes/inout/hdf5.py +818 -0
  7. diffpes/inout/helpers.py +295 -0
  8. diffpes/inout/kpoints.py +433 -0
  9. diffpes/inout/plotting.py +757 -0
  10. diffpes/inout/poscar.py +174 -0
  11. diffpes/inout/procar.py +331 -0
  12. diffpes/inout/py.typed +0 -0
  13. diffpes/maths/__init__.py +68 -0
  14. diffpes/maths/dipole.py +368 -0
  15. diffpes/maths/gaunt.py +496 -0
  16. diffpes/maths/spherical_harmonics.py +336 -0
  17. diffpes/py.typed +0 -0
  18. diffpes/radial/__init__.py +47 -0
  19. diffpes/radial/bessel.py +198 -0
  20. diffpes/radial/integrate.py +128 -0
  21. diffpes/radial/wavefunctions.py +335 -0
  22. diffpes/simul/__init__.py +146 -0
  23. diffpes/simul/broadening.py +258 -0
  24. diffpes/simul/crosssections.py +196 -0
  25. diffpes/simul/expanded.py +1021 -0
  26. diffpes/simul/forward.py +586 -0
  27. diffpes/simul/oam.py +112 -0
  28. diffpes/simul/polarization.py +443 -0
  29. diffpes/simul/py.typed +0 -0
  30. diffpes/simul/resolution.py +122 -0
  31. diffpes/simul/self_energy.py +142 -0
  32. diffpes/simul/spectrum.py +1116 -0
  33. diffpes/simul/workflow.py +424 -0
  34. diffpes/tightb/__init__.py +68 -0
  35. diffpes/tightb/diagonalize.py +340 -0
  36. diffpes/tightb/hamiltonian.py +286 -0
  37. diffpes/tightb/projections.py +134 -0
  38. diffpes/types/__init__.py +162 -0
  39. diffpes/types/aliases.py +52 -0
  40. diffpes/types/bands.py +1064 -0
  41. diffpes/types/dos.py +510 -0
  42. diffpes/types/geometry.py +306 -0
  43. diffpes/types/kpath.py +365 -0
  44. diffpes/types/params.py +496 -0
  45. diffpes/types/py.typed +0 -0
  46. diffpes/types/radial_params.py +482 -0
  47. diffpes/types/self_energy.py +271 -0
  48. diffpes/types/tb_model.py +531 -0
  49. diffpes/types/volumetric.py +608 -0
  50. diffpes/utils/__init__.py +30 -0
  51. diffpes/utils/math.py +255 -0
  52. diffpes/utils/py.typed +0 -0
  53. diffpes-2026.3.1.dist-info/METADATA +176 -0
  54. diffpes-2026.3.1.dist-info/RECORD +55 -0
  55. diffpes-2026.3.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,306 @@
1
+ """Crystal geometry data structure for VASP crystal structures.
2
+
3
+ Extended Summary
4
+ ----------------
5
+ Defines the :class:`CrystalGeometry` PyTree for representing
6
+ crystal structures parsed from VASP POSCAR files. Includes real-space
7
+ lattice vectors, reciprocal lattice, atomic coordinates, element
8
+ symbols, and atom counts per species.
9
+
10
+ Routine Listings
11
+ ----------------
12
+ :class:`CrystalGeometry`
13
+ PyTree NamedTuple for crystal geometry data.
14
+ :func:`make_crystal_geometry`
15
+ Factory function with validation and reciprocal lattice computation.
16
+
17
+ Notes
18
+ -----
19
+ The ``symbols`` field is stored as auxiliary data in the PyTree
20
+ since JAX cannot trace Python strings. All numeric fields are
21
+ stored as JAX arrays for compatibility with JAX transformations.
22
+ """
23
+
24
+ import jax.numpy as jnp
25
+ from beartype import beartype
26
+ from beartype.typing import NamedTuple, Tuple, Union
27
+ from jax.tree_util import register_pytree_node_class
28
+ from jaxtyping import Array, Float, Int, jaxtyped
29
+
30
+ from .aliases import ScalarNumeric
31
+
32
+
33
+ @register_pytree_node_class
34
+ class CrystalGeometry(NamedTuple):
35
+ """PyTree for crystal geometry from VASP POSCAR.
36
+
37
+ Encapsulates the full crystal structure information parsed from a
38
+ VASP POSCAR file: real-space lattice vectors, the corresponding
39
+ reciprocal lattice, fractional atomic coordinates, per-species
40
+ element symbols, and per-species atom counts. Together these fields
41
+ fully describe the periodic crystal needed for ARPES simulation.
42
+
43
+ This class is registered as a JAX PyTree via
44
+ ``@register_pytree_node_class``. Numeric fields (lattice,
45
+ reciprocal_lattice, coords, atom_counts) are stored as children
46
+ visible to JAX tracing, while the ``symbols`` tuple of Python
47
+ strings is stored as auxiliary data because JAX cannot trace
48
+ Python strings.
49
+
50
+ Attributes
51
+ ----------
52
+ lattice : Float[Array, "3 3"]
53
+ Real-space lattice vectors as rows (angstroms).
54
+ reciprocal_lattice : Float[Array, "3 3"]
55
+ Reciprocal lattice vectors as rows (1/angstroms).
56
+ coords : Float[Array, "N 3"]
57
+ Fractional atomic coordinates.
58
+ symbols : tuple[str, ...]
59
+ Element symbols for each species.
60
+ atom_counts : Int[Array, " S"]
61
+ Number of atoms per species.
62
+
63
+ Notes
64
+ -----
65
+ Registered as a JAX PyTree with ``@register_pytree_node_class``.
66
+ The ``symbols`` field is a tuple of Python strings and therefore
67
+ must be stored as PyTree auxiliary data (not children). JAX treats
68
+ auxiliary data as compile-time constants: changing ``symbols``
69
+ triggers recompilation of any ``jit``-compiled function that
70
+ receives this PyTree.
71
+
72
+ See Also
73
+ --------
74
+ make_crystal_geometry : Factory function with validation, float64
75
+ casting, and automatic reciprocal lattice computation.
76
+ """
77
+
78
+ lattice: Float[Array, "3 3"]
79
+ reciprocal_lattice: Float[Array, "3 3"]
80
+ coords: Float[Array, "N 3"]
81
+ symbols: tuple[str, ...]
82
+ atom_counts: Int[Array, " S"]
83
+
84
+ def tree_flatten(
85
+ self,
86
+ ) -> Tuple[
87
+ Tuple[
88
+ Float[Array, "3 3"],
89
+ Float[Array, "3 3"],
90
+ Float[Array, "N 3"],
91
+ Int[Array, " S"],
92
+ ],
93
+ tuple[str, ...],
94
+ ]:
95
+ """Flatten into JAX children and auxiliary data.
96
+
97
+ Separates the PyTree into children (JAX-traced arrays) and
98
+ auxiliary data (static Python values). For CrystalGeometry,
99
+ the four numeric fields are children and the ``symbols`` tuple
100
+ of strings is auxiliary data.
101
+
102
+ Implementation Logic
103
+ --------------------
104
+ 1. **Children** (JAX arrays, participate in autodiff):
105
+ ``(lattice, reciprocal_lattice, coords, atom_counts)``
106
+ 2. **Auxiliary data** (static, not traced by JAX):
107
+ ``symbols`` -- a tuple of Python strings. JAX treats this
108
+ as a compile-time constant; any change triggers JIT
109
+ recompilation.
110
+
111
+ Returns
112
+ -------
113
+ children : tuple of Array
114
+ Tuple of ``(lattice, reciprocal_lattice, coords,
115
+ atom_counts)`` JAX arrays.
116
+ aux_data : tuple[str, ...]
117
+ Element symbol strings, stored outside JAX tracing.
118
+ """
119
+ return (
120
+ (
121
+ self.lattice,
122
+ self.reciprocal_lattice,
123
+ self.coords,
124
+ self.atom_counts,
125
+ ),
126
+ self.symbols,
127
+ )
128
+
129
+ @classmethod
130
+ def tree_unflatten(
131
+ cls,
132
+ aux_data: tuple[str, ...],
133
+ children: Tuple[
134
+ Float[Array, "3 3"],
135
+ Float[Array, "3 3"],
136
+ Float[Array, "N 3"],
137
+ Int[Array, " S"],
138
+ ],
139
+ ) -> "CrystalGeometry":
140
+ """Reconstruct a CrystalGeometry from flattened components.
141
+
142
+ Inverse of :meth:`tree_flatten`. JAX calls this method
143
+ automatically when unflattening a PyTree after a
144
+ transformation (e.g., inside ``jax.jit`` or ``jax.grad``).
145
+
146
+ Implementation Logic
147
+ --------------------
148
+ 1. Unpack ``children`` into four JAX arrays:
149
+ ``(lattice, reciprocal_lattice, coords, atom_counts)``.
150
+ 2. Receive ``aux_data`` as the ``symbols`` tuple of strings.
151
+ 3. Pass all five fields to the constructor, re-interleaving
152
+ ``symbols`` from ``aux_data`` into its correct position
153
+ between ``coords`` and ``atom_counts``.
154
+
155
+ Parameters
156
+ ----------
157
+ aux_data : tuple[str, ...]
158
+ Element symbol strings recovered from auxiliary data.
159
+ children : tuple of Array
160
+ Tuple of ``(lattice, reciprocal_lattice, coords,
161
+ atom_counts)`` JAX arrays.
162
+
163
+ Returns
164
+ -------
165
+ geometry : CrystalGeometry
166
+ Reconstructed instance with identical data.
167
+ """
168
+ lattice: Float[Array, "3 3"]
169
+ reciprocal_lattice: Float[Array, "3 3"]
170
+ coords: Float[Array, "N 3"]
171
+ atom_counts: Int[Array, " S"]
172
+ lattice, reciprocal_lattice, coords, atom_counts = children
173
+ geometry: CrystalGeometry = cls(
174
+ lattice=lattice,
175
+ reciprocal_lattice=reciprocal_lattice,
176
+ coords=coords,
177
+ symbols=aux_data,
178
+ atom_counts=atom_counts,
179
+ )
180
+ return geometry
181
+
182
+
183
+ def _compute_reciprocal_lattice(
184
+ lattice: Float[Array, "3 3"],
185
+ ) -> Float[Array, "3 3"]:
186
+ r"""Compute reciprocal lattice vectors from real-space lattice.
187
+
188
+ Derives the reciprocal lattice from the real-space lattice using
189
+ the standard cross-product formula. The reciprocal lattice vectors
190
+ ``b_i`` satisfy ``a_i . b_j = 2 pi delta_{ij}``.
191
+
192
+ Implementation Logic
193
+ --------------------
194
+ Given real-space lattice vectors ``a1, a2, a3`` (rows of
195
+ ``lattice``):
196
+
197
+ 1. **Extract rows**: ``a1 = lattice[0]``, ``a2 = lattice[1]``,
198
+ ``a3 = lattice[2]``.
199
+ 2. **Compute unit-cell volume**:
200
+ ``V = a1 . (a2 x a3)`` (scalar triple product).
201
+ 3. **Compute reciprocal vectors** via the cross-product formula:
202
+
203
+ .. math::
204
+
205
+ b_1 = 2\\pi \\, (a_2 \\times a_3) / V
206
+
207
+ b_2 = 2\\pi \\, (a_3 \\times a_1) / V
208
+
209
+ b_3 = 2\\pi \\, (a_1 \\times a_2) / V
210
+
211
+ 4. **Stack** ``[b1, b2, b3]`` into a (3, 3) array with
212
+ reciprocal vectors as rows.
213
+
214
+ Parameters
215
+ ----------
216
+ lattice : Float[Array, "3 3"]
217
+ Real-space lattice vectors as rows.
218
+
219
+ Returns
220
+ -------
221
+ reciprocal : Float[Array, "3 3"]
222
+ Reciprocal lattice vectors as rows (units of 1/angstrom,
223
+ with the 2 pi prefactor included).
224
+ """
225
+ a1: Float[Array, " 3"] = lattice[0]
226
+ a2: Float[Array, " 3"] = lattice[1]
227
+ a3: Float[Array, " 3"] = lattice[2]
228
+ volume: Float[Array, " "] = jnp.dot(a1, jnp.cross(a2, a3))
229
+ b1: Float[Array, " 3"] = 2.0 * jnp.pi * jnp.cross(a2, a3) / volume
230
+ b2: Float[Array, " 3"] = 2.0 * jnp.pi * jnp.cross(a3, a1) / volume
231
+ b3: Float[Array, " 3"] = 2.0 * jnp.pi * jnp.cross(a1, a2) / volume
232
+ reciprocal: Float[Array, "3 3"] = jnp.stack([b1, b2, b3])
233
+ return reciprocal
234
+
235
+
236
+ @jaxtyped(typechecker=beartype)
237
+ def make_crystal_geometry(
238
+ lattice: Union[Float[Array, "3 3"], "list[list[ScalarNumeric]]"],
239
+ coords: Float[Array, "N 3"],
240
+ symbols: tuple[str, ...],
241
+ atom_counts: Union[Int[Array, " S"], "list[int]"],
242
+ ) -> CrystalGeometry:
243
+ """Create a validated CrystalGeometry instance.
244
+
245
+ Validates and normalises the inputs, then automatically computes
246
+ the reciprocal lattice from the real-space lattice so the caller
247
+ does not need to supply it. Numeric arrays are cast to the
248
+ appropriate JAX dtypes (float64 for real-valued fields, int32
249
+ for atom counts).
250
+
251
+ Implementation Logic
252
+ --------------------
253
+ 1. **Cast lattice** to ``jnp.float64`` via ``jnp.asarray``.
254
+ Accepts both JAX arrays and nested Python lists.
255
+ 2. **Cast coords** to ``jnp.float64`` via ``jnp.asarray``.
256
+ 3. **Cast atom_counts** to ``jnp.int32`` via ``jnp.asarray``.
257
+ Accepts both JAX integer arrays and Python ``list[int]``.
258
+ 4. **Auto-compute reciprocal lattice** by calling
259
+ ``_compute_reciprocal_lattice(lattice_arr)``. This derives
260
+ ``b_i = 2 pi (a_j x a_k) / V`` from the validated
261
+ real-space lattice.
262
+ 5. **Construct** the ``CrystalGeometry`` NamedTuple from all
263
+ five fields (including the computed reciprocal lattice) and
264
+ return it.
265
+
266
+ Parameters
267
+ ----------
268
+ lattice : Union[Float[Array, "3 3"], list]
269
+ Real-space lattice vectors as rows (angstroms).
270
+ coords : Float[Array, "N 3"]
271
+ Fractional atomic coordinates.
272
+ symbols : tuple[str, ...]
273
+ Element symbols for each species.
274
+ atom_counts : Union[Int[Array, " S"], list[int]]
275
+ Number of atoms per species.
276
+
277
+ Returns
278
+ -------
279
+ geometry : CrystalGeometry
280
+ Validated crystal geometry instance with the reciprocal
281
+ lattice pre-computed.
282
+
283
+ See Also
284
+ --------
285
+ CrystalGeometry : The PyTree class constructed by this factory.
286
+ _compute_reciprocal_lattice : Cross-product formula used to
287
+ derive the reciprocal lattice.
288
+ """
289
+ lattice_arr: Float[Array, "3 3"] = jnp.asarray(lattice, dtype=jnp.float64)
290
+ coords_arr: Float[Array, "N 3"] = jnp.asarray(coords, dtype=jnp.float64)
291
+ counts_arr: Int[Array, " S"] = jnp.asarray(atom_counts, dtype=jnp.int32)
292
+ reciprocal: Float[Array, "3 3"] = _compute_reciprocal_lattice(lattice_arr)
293
+ geometry: CrystalGeometry = CrystalGeometry(
294
+ lattice=lattice_arr,
295
+ reciprocal_lattice=reciprocal,
296
+ coords=coords_arr,
297
+ symbols=symbols,
298
+ atom_counts=counts_arr,
299
+ )
300
+ return geometry
301
+
302
+
303
+ __all__: list[str] = [
304
+ "CrystalGeometry",
305
+ "make_crystal_geometry",
306
+ ]
diffpes/types/kpath.py ADDED
@@ -0,0 +1,365 @@
1
+ """K-point path information data structure.
2
+
3
+ Extended Summary
4
+ ----------------
5
+ Defines the :class:`KPathInfo` PyTree for storing Brillouin-zone
6
+ metadata parsed from VASP KPOINTS files, including both plotting
7
+ labels and mode-specific metadata (automatic grids, explicit weights,
8
+ line-mode segments/endpoints).
9
+
10
+ Routine Listings
11
+ ----------------
12
+ :class:`KPathInfo`
13
+ PyTree for k-point path metadata.
14
+ :func:`make_kpath_info`
15
+ Create a validated KPathInfo instance.
16
+ """
17
+
18
+ import jax.numpy as jnp
19
+ from beartype import beartype
20
+ from beartype.typing import NamedTuple, Optional, Tuple, Union
21
+ from jax.tree_util import register_pytree_node_class
22
+ from jaxtyping import Array, Float, Int, jaxtyped
23
+
24
+
25
+ @register_pytree_node_class
26
+ class KPathInfo(NamedTuple):
27
+ """PyTree for k-point path metadata.
28
+
29
+ Extended Summary
30
+ ----------------
31
+ Stores Brillouin-zone path information parsed from VASP KPOINTS
32
+ files. Includes plotting fields (labels + label indices) and
33
+ mode-specific metadata needed for full parser completeness:
34
+ automatic-mode grid/shift, explicit-mode k-points/weights, and
35
+ line-mode segment endpoints.
36
+
37
+ This class is registered as a JAX PyTree via
38
+ ``@register_pytree_node_class``. Array-valued metadata is stored
39
+ as children. String metadata is stored as auxiliary data because
40
+ JAX cannot trace Python strings.
41
+
42
+ Attributes
43
+ ----------
44
+ num_kpoints : Int[Array, " "]
45
+ Total number of k-points in the path (line mode) or header
46
+ count (explicit mode).
47
+ label_indices : Int[Array, " L"]
48
+ Indices of symmetry points along the path.
49
+ points_per_segment : Int[Array, " "]
50
+ Raw integer from line 2 of KPOINTS (line mode: points per
51
+ segment).
52
+ segments : Int[Array, " "]
53
+ Number of line segments in line mode.
54
+ kpoints : Optional[Float[Array, "K 3"]]
55
+ Mode-specific k-points:
56
+ line mode -> segment endpoints (segments + 1),
57
+ explicit mode -> listed k-points,
58
+ automatic mode -> None.
59
+ weights : Optional[Float[Array, " K"]]
60
+ Explicit-mode per-k-point weights (None otherwise).
61
+ grid : Optional[Int[Array, " 3"]]
62
+ Automatic-mode Monkhorst-Pack/Gamma grid (None otherwise).
63
+ shift : Optional[Float[Array, " 3"]]
64
+ Automatic-mode grid shift (None otherwise).
65
+ mode : str
66
+ KPOINTS file mode (Automatic, Line-mode, Explicit).
67
+ labels : tuple[str, ...]
68
+ Symmetry point labels (e.g., Gamma, M, K).
69
+ comment : str
70
+ Raw comment from KPOINTS line 1.
71
+ coordinate_mode : str
72
+ Coordinate/scheme line metadata:
73
+ line/explicit -> Reciprocal/Cartesian line,
74
+ automatic -> scheme line (e.g., Monkhorst-Pack).
75
+
76
+ Notes
77
+ -----
78
+ Registered as a JAX PyTree with ``@register_pytree_node_class``.
79
+ The ``mode`` string and ``labels`` tuple of strings are stored as
80
+ auxiliary data (not children). JAX treats auxiliary data as
81
+ compile-time constants: changing either value triggers
82
+ recompilation of any ``jit``-compiled function that receives
83
+ this PyTree.
84
+
85
+ See Also
86
+ --------
87
+ make_kpath_info : Factory function with validation and int32
88
+ casting.
89
+ """
90
+
91
+ num_kpoints: Int[Array, " "]
92
+ label_indices: Int[Array, " L"]
93
+ points_per_segment: Int[Array, " "]
94
+ segments: Int[Array, " "]
95
+ kpoints: Optional[Float[Array, "K 3"]]
96
+ weights: Optional[Float[Array, " K"]]
97
+ grid: Optional[Int[Array, " 3"]]
98
+ shift: Optional[Float[Array, " 3"]]
99
+ mode: str
100
+ labels: tuple[str, ...]
101
+ comment: str
102
+ coordinate_mode: str
103
+
104
+ def tree_flatten(
105
+ self,
106
+ ) -> Tuple[
107
+ Tuple[
108
+ Int[Array, " "],
109
+ Int[Array, " L"],
110
+ Int[Array, " "],
111
+ Int[Array, " "],
112
+ Optional[Float[Array, "K 3"]],
113
+ Optional[Float[Array, " K"]],
114
+ Optional[Int[Array, " 3"]],
115
+ Optional[Float[Array, " 3"]],
116
+ ],
117
+ Tuple[str, tuple[str, ...], str, str],
118
+ ]:
119
+ """Flatten into JAX children and auxiliary data.
120
+
121
+ Separates the PyTree into children (JAX-traced arrays) and
122
+ auxiliary data (static Python values). For KPathInfo, all
123
+ array-valued metadata is stored in children and string metadata
124
+ in aux_data.
125
+
126
+ Implementation Logic
127
+ --------------------
128
+ 1. **Children** (JAX arrays, participate in tracing):
129
+ ``(num_kpoints, label_indices, points_per_segment,
130
+ segments, kpoints, weights, grid, shift)``.
131
+ 2. **Auxiliary data** (static, not traced by JAX):
132
+ ``(mode, labels, comment, coordinate_mode)``.
133
+ JAX treats these as compile-time constants; any change
134
+ triggers JIT recompilation.
135
+
136
+ Returns
137
+ -------
138
+ children : tuple of Array
139
+ Tuple of array-valued metadata fields.
140
+ aux_data : tuple[str, tuple[str, ...], str, str]
141
+ String metadata stored outside JAX tracing.
142
+ """
143
+ return (
144
+ (
145
+ self.num_kpoints,
146
+ self.label_indices,
147
+ self.points_per_segment,
148
+ self.segments,
149
+ self.kpoints,
150
+ self.weights,
151
+ self.grid,
152
+ self.shift,
153
+ ),
154
+ (
155
+ self.mode,
156
+ self.labels,
157
+ self.comment,
158
+ self.coordinate_mode,
159
+ ),
160
+ )
161
+
162
+ @classmethod
163
+ def tree_unflatten(
164
+ cls,
165
+ aux_data: Tuple[str, tuple[str, ...], str, str],
166
+ children: Tuple[
167
+ Int[Array, " "],
168
+ Int[Array, " L"],
169
+ Int[Array, " "],
170
+ Int[Array, " "],
171
+ Optional[Float[Array, "K 3"]],
172
+ Optional[Float[Array, " K"]],
173
+ Optional[Int[Array, " 3"]],
174
+ Optional[Float[Array, " 3"]],
175
+ ],
176
+ ) -> "KPathInfo":
177
+ """Reconstruct a KPathInfo from flattened components.
178
+
179
+ Inverse of :meth:`tree_flatten`. JAX calls this method
180
+ automatically when unflattening a PyTree after a
181
+ transformation (e.g., inside ``jax.jit`` or ``jax.grad``).
182
+
183
+ Implementation Logic
184
+ --------------------
185
+ 1. Unpack ``children`` into the eight array-valued fields.
186
+ 2. Unpack ``aux_data`` into the four string fields.
187
+ 3. Pass all fields to the constructor.
188
+
189
+ Parameters
190
+ ----------
191
+ aux_data : tuple[str, tuple[str, ...], str, str]
192
+ String metadata recovered from auxiliary data.
193
+ children : tuple of Array
194
+ Array-valued metadata tuple.
195
+
196
+ Returns
197
+ -------
198
+ kpath : KPathInfo
199
+ Reconstructed instance with identical data.
200
+ """
201
+ num_kpoints: Int[Array, " "]
202
+ label_indices: Int[Array, " L"]
203
+ points_per_segment: Int[Array, " "]
204
+ segments: Int[Array, " "]
205
+ kpoints: Optional[Float[Array, "K 3"]]
206
+ weights: Optional[Float[Array, " K"]]
207
+ grid: Optional[Int[Array, " 3"]]
208
+ shift: Optional[Float[Array, " 3"]]
209
+ (
210
+ num_kpoints,
211
+ label_indices,
212
+ points_per_segment,
213
+ segments,
214
+ kpoints,
215
+ weights,
216
+ grid,
217
+ shift,
218
+ ) = children
219
+ mode: str
220
+ labels: tuple[str, ...]
221
+ comment: str
222
+ coordinate_mode: str
223
+ mode, labels, comment, coordinate_mode = aux_data
224
+ kpath: KPathInfo = cls(
225
+ num_kpoints=num_kpoints,
226
+ label_indices=label_indices,
227
+ points_per_segment=points_per_segment,
228
+ segments=segments,
229
+ kpoints=kpoints,
230
+ weights=weights,
231
+ grid=grid,
232
+ shift=shift,
233
+ mode=mode,
234
+ labels=labels,
235
+ comment=comment,
236
+ coordinate_mode=coordinate_mode,
237
+ )
238
+ return kpath
239
+
240
+
241
+ @jaxtyped(typechecker=beartype)
242
+ def make_kpath_info( # noqa: PLR0913
243
+ num_kpoints: Union[int, Int[Array, " "]],
244
+ label_indices: Union[Int[Array, " L"], "list[int]"],
245
+ points_per_segment: Union[int, Int[Array, " "]] = 0,
246
+ segments: Union[int, Int[Array, " "]] = 0,
247
+ kpoints: Optional[Float[Array, "K 3"]] = None,
248
+ weights: Optional[Float[Array, " K"]] = None,
249
+ grid: Optional[Union[Int[Array, " 3"], "list[int]"]] = None,
250
+ shift: Optional[Float[Array, " 3"]] = None,
251
+ mode: str = "Line-mode",
252
+ labels: tuple[str, ...] = (),
253
+ comment: str = "",
254
+ coordinate_mode: str = "",
255
+ ) -> KPathInfo:
256
+ """Create a validated KPathInfo instance.
257
+
258
+ Extended Summary
259
+ ----------------
260
+ Factory function that validates and normalises raw k-path
261
+ metadata before constructing a ``KPathInfo`` PyTree. Integer
262
+ scalars and arrays are cast to ``int32``; float arrays are cast
263
+ to ``float64``. Optional fields (``kpoints``, ``weights``,
264
+ ``grid``, ``shift``) are cast only when present, preserving
265
+ ``None`` for modes that do not use them.
266
+
267
+ The function is decorated with ``@jaxtyped(typechecker=beartype)``
268
+ so that shape constraints are checked at call time. String
269
+ metadata (``mode``, ``labels``, ``comment``, ``coordinate_mode``)
270
+ is passed through unchanged and stored as PyTree auxiliary data.
271
+
272
+ Use this factory when constructing ``KPathInfo`` from parsed
273
+ KPOINTS data or when building synthetic k-paths for testing. The
274
+ factory ensures consistent dtypes and handles the three KPOINTS
275
+ modes (Line-mode, Explicit, Automatic) through the optional
276
+ fields.
277
+
278
+ Implementation Logic
279
+ --------------------
280
+ 1. **Cast integer fields** (``num_kpoints``, ``label_indices``,
281
+ ``points_per_segment``, ``segments``) to ``jnp.int32`` via
282
+ ``jnp.asarray``. Accepts both Python ints and JAX arrays.
283
+ 2. **Cast optional float fields** (``kpoints``, ``weights``,
284
+ ``shift``) to ``jnp.float64`` when not ``None``.
285
+ 3. **Cast optional int field** (``grid``) to ``jnp.int32`` when
286
+ not ``None``.
287
+ 4. **Pass string metadata** (``mode``, ``labels``, ``comment``,
288
+ ``coordinate_mode``) through unchanged -- stored as auxiliary
289
+ data in the PyTree.
290
+ 5. **Construct** the ``KPathInfo`` NamedTuple from all validated
291
+ fields and return it.
292
+
293
+ Parameters
294
+ ----------
295
+ num_kpoints : Union[int, Int[Array, " "]]
296
+ Total number of k-points along the path.
297
+ label_indices : Union[Int[Array, " L"], list[int]]
298
+ Indices of symmetry points along the path.
299
+ points_per_segment : Union[int, Int[Array, " "]], optional
300
+ Raw value from line 2 of KPOINTS. Default is 0.
301
+ segments : Union[int, Int[Array, " "]], optional
302
+ Number of path segments in line mode. Default is 0.
303
+ kpoints : Optional[Float[Array, "K 3"]], optional
304
+ Mode-specific k-point coordinates. Default is None.
305
+ weights : Optional[Float[Array, " K"]], optional
306
+ Explicit-mode weights. Default is None.
307
+ grid : Optional[Union[Int[Array, " 3"], list[int]]], optional
308
+ Automatic-mode MP/Gamma grid. Default is None.
309
+ shift : Optional[Float[Array, " 3"]], optional
310
+ Automatic-mode grid shift. Default is None.
311
+ mode : str, optional
312
+ KPOINTS file mode. Default is ``"Line-mode"``.
313
+ labels : tuple[str, ...], optional
314
+ Symmetry point labels. Default is empty tuple.
315
+ comment : str, optional
316
+ KPOINTS comment line. Default is empty string.
317
+ coordinate_mode : str, optional
318
+ Coordinate/scheme line metadata. Default is empty string.
319
+
320
+ Returns
321
+ -------
322
+ kpath : KPathInfo
323
+ Validated k-path info instance.
324
+
325
+ See Also
326
+ --------
327
+ KPathInfo : The PyTree class constructed by this factory.
328
+ """
329
+ nkpts_arr: Int[Array, " "] = jnp.asarray(num_kpoints, dtype=jnp.int32)
330
+ indices_arr: Int[Array, " L"] = jnp.asarray(label_indices, dtype=jnp.int32)
331
+ pps_arr: Int[Array, " "] = jnp.asarray(points_per_segment, dtype=jnp.int32)
332
+ segments_arr: Int[Array, " "] = jnp.asarray(segments, dtype=jnp.int32)
333
+ kpoints_arr: Optional[Float[Array, "K 3"]] = None
334
+ if kpoints is not None:
335
+ kpoints_arr = jnp.asarray(kpoints, dtype=jnp.float64)
336
+ weights_arr: Optional[Float[Array, " K"]] = None
337
+ if weights is not None:
338
+ weights_arr = jnp.asarray(weights, dtype=jnp.float64)
339
+ grid_arr: Optional[Int[Array, " 3"]] = None
340
+ if grid is not None:
341
+ grid_arr = jnp.asarray(grid, dtype=jnp.int32)
342
+ shift_arr: Optional[Float[Array, " 3"]] = None
343
+ if shift is not None:
344
+ shift_arr = jnp.asarray(shift, dtype=jnp.float64)
345
+ kpath: KPathInfo = KPathInfo(
346
+ num_kpoints=nkpts_arr,
347
+ label_indices=indices_arr,
348
+ points_per_segment=pps_arr,
349
+ segments=segments_arr,
350
+ kpoints=kpoints_arr,
351
+ weights=weights_arr,
352
+ grid=grid_arr,
353
+ shift=shift_arr,
354
+ mode=mode,
355
+ labels=labels,
356
+ comment=comment,
357
+ coordinate_mode=coordinate_mode,
358
+ )
359
+ return kpath
360
+
361
+
362
+ __all__: list[str] = [
363
+ "KPathInfo",
364
+ "make_kpath_info",
365
+ ]