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.
- diffpes/__init__.py +65 -0
- diffpes/inout/__init__.py +88 -0
- diffpes/inout/chgcar.py +459 -0
- diffpes/inout/doscar.py +248 -0
- diffpes/inout/eigenval.py +258 -0
- diffpes/inout/hdf5.py +818 -0
- diffpes/inout/helpers.py +295 -0
- diffpes/inout/kpoints.py +433 -0
- diffpes/inout/plotting.py +757 -0
- diffpes/inout/poscar.py +174 -0
- diffpes/inout/procar.py +331 -0
- diffpes/inout/py.typed +0 -0
- diffpes/maths/__init__.py +68 -0
- diffpes/maths/dipole.py +368 -0
- diffpes/maths/gaunt.py +496 -0
- diffpes/maths/spherical_harmonics.py +336 -0
- diffpes/py.typed +0 -0
- diffpes/radial/__init__.py +47 -0
- diffpes/radial/bessel.py +198 -0
- diffpes/radial/integrate.py +128 -0
- diffpes/radial/wavefunctions.py +335 -0
- diffpes/simul/__init__.py +146 -0
- diffpes/simul/broadening.py +258 -0
- diffpes/simul/crosssections.py +196 -0
- diffpes/simul/expanded.py +1021 -0
- diffpes/simul/forward.py +586 -0
- diffpes/simul/oam.py +112 -0
- diffpes/simul/polarization.py +443 -0
- diffpes/simul/py.typed +0 -0
- diffpes/simul/resolution.py +122 -0
- diffpes/simul/self_energy.py +142 -0
- diffpes/simul/spectrum.py +1116 -0
- diffpes/simul/workflow.py +424 -0
- diffpes/tightb/__init__.py +68 -0
- diffpes/tightb/diagonalize.py +340 -0
- diffpes/tightb/hamiltonian.py +286 -0
- diffpes/tightb/projections.py +134 -0
- diffpes/types/__init__.py +162 -0
- diffpes/types/aliases.py +52 -0
- diffpes/types/bands.py +1064 -0
- diffpes/types/dos.py +510 -0
- diffpes/types/geometry.py +306 -0
- diffpes/types/kpath.py +365 -0
- diffpes/types/params.py +496 -0
- diffpes/types/py.typed +0 -0
- diffpes/types/radial_params.py +482 -0
- diffpes/types/self_energy.py +271 -0
- diffpes/types/tb_model.py +531 -0
- diffpes/types/volumetric.py +608 -0
- diffpes/utils/__init__.py +30 -0
- diffpes/utils/math.py +255 -0
- diffpes/utils/py.typed +0 -0
- diffpes-2026.3.1.dist-info/METADATA +176 -0
- diffpes-2026.3.1.dist-info/RECORD +55 -0
- diffpes-2026.3.1.dist-info/WHEEL +4 -0
diffpes/inout/helpers.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"""Parser-adjacent workflow helpers for assembling simulation-ready arrays.
|
|
2
|
+
|
|
3
|
+
Provides utilities for atom-subset aggregation, orbital channel
|
|
4
|
+
reductions, and cross-file consistency checks between EIGENVAL,
|
|
5
|
+
PROCAR, and KPOINTS parsed data.
|
|
6
|
+
|
|
7
|
+
Routine Listings
|
|
8
|
+
----------------
|
|
9
|
+
:func:`select_atoms`
|
|
10
|
+
Extract orbital projections for a subset of atoms.
|
|
11
|
+
:func:`aggregate_atoms`
|
|
12
|
+
Sum orbital projections over a set of atoms.
|
|
13
|
+
:func:`reduce_orbitals`
|
|
14
|
+
Reduce 9 orbital channels to s/p/d totals.
|
|
15
|
+
:func:`check_consistency`
|
|
16
|
+
Check dimension agreement across parsed VASP files.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import jax.numpy as jnp
|
|
20
|
+
from beartype.typing import Optional, Union
|
|
21
|
+
from jaxtyping import Array, Float, Int
|
|
22
|
+
|
|
23
|
+
from diffpes.types import (
|
|
24
|
+
BandStructure,
|
|
25
|
+
KPathInfo,
|
|
26
|
+
OrbitalProjection,
|
|
27
|
+
SpinOrbitalProjection,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
_S_IDX: int = 0
|
|
31
|
+
_P_SLICE: slice = slice(1, 4)
|
|
32
|
+
_D_SLICE: slice = slice(4, 9)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def select_atoms(
|
|
36
|
+
orb: Union[OrbitalProjection, SpinOrbitalProjection],
|
|
37
|
+
atom_indices: list[int],
|
|
38
|
+
) -> Union[OrbitalProjection, SpinOrbitalProjection]:
|
|
39
|
+
"""Extract orbital projections for a subset of atoms.
|
|
40
|
+
|
|
41
|
+
Extended Summary
|
|
42
|
+
----------------
|
|
43
|
+
Creates a new projection object containing only the atoms at the
|
|
44
|
+
requested indices. This is the primary mechanism for isolating the
|
|
45
|
+
contribution of specific atomic sites (e.g. surface atoms, a
|
|
46
|
+
particular element) to the simulated ARPES spectrum.
|
|
47
|
+
|
|
48
|
+
Implementation Logic
|
|
49
|
+
--------------------
|
|
50
|
+
1. Convert ``atom_indices`` to a JAX ``int32`` index array.
|
|
51
|
+
2. Fancy-index the ``projections`` array along axis 2 (atom axis)
|
|
52
|
+
to extract the requested atoms.
|
|
53
|
+
3. If ``spin`` data is present (non-``None``), apply the same
|
|
54
|
+
fancy-indexing to the spin array.
|
|
55
|
+
4. If ``oam`` (orbital angular momentum) data is present, apply
|
|
56
|
+
the same fancy-indexing.
|
|
57
|
+
5. Construct and return a new object of the same type as the input
|
|
58
|
+
(``SpinOrbitalProjection`` or ``OrbitalProjection``) so that
|
|
59
|
+
downstream code can treat the result identically to the
|
|
60
|
+
original.
|
|
61
|
+
|
|
62
|
+
Parameters
|
|
63
|
+
----------
|
|
64
|
+
orb : OrbitalProjection or SpinOrbitalProjection
|
|
65
|
+
Full orbital projections with shape ``(K, B, A, 9)``.
|
|
66
|
+
atom_indices : list[int]
|
|
67
|
+
0-based indices of atoms to select.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
OrbitalProjection or SpinOrbitalProjection
|
|
72
|
+
Projections restricted to the specified atoms.
|
|
73
|
+
Shape ``(K, B, len(atom_indices), 9)``.
|
|
74
|
+
Preserves the input type.
|
|
75
|
+
|
|
76
|
+
Notes
|
|
77
|
+
-----
|
|
78
|
+
The returned object shares no memory with the original because
|
|
79
|
+
JAX fancy-indexing always produces a copy. The function is
|
|
80
|
+
pure and can be safely used inside ``jax.jit``-compiled code.
|
|
81
|
+
"""
|
|
82
|
+
idx: Int[Array, " N"] = jnp.asarray(atom_indices, dtype=jnp.int32)
|
|
83
|
+
proj_sub: Float[Array, "K B N 9"] = orb.projections[:, :, idx, :]
|
|
84
|
+
spin_sub: Optional[Float[Array, "K B N 9"]] = None
|
|
85
|
+
if orb.spin is not None:
|
|
86
|
+
spin_sub = orb.spin[:, :, idx, :]
|
|
87
|
+
oam_sub: Optional[Float[Array, "K B N 9"]] = None
|
|
88
|
+
if orb.oam is not None:
|
|
89
|
+
oam_sub = orb.oam[:, :, idx, :]
|
|
90
|
+
if isinstance(orb, SpinOrbitalProjection):
|
|
91
|
+
result_soc: SpinOrbitalProjection = SpinOrbitalProjection(
|
|
92
|
+
projections=proj_sub,
|
|
93
|
+
spin=spin_sub, # type: ignore[arg-type]
|
|
94
|
+
oam=oam_sub,
|
|
95
|
+
)
|
|
96
|
+
return result_soc
|
|
97
|
+
result: OrbitalProjection = OrbitalProjection(
|
|
98
|
+
projections=proj_sub,
|
|
99
|
+
spin=spin_sub,
|
|
100
|
+
oam=oam_sub,
|
|
101
|
+
)
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def aggregate_atoms(
|
|
106
|
+
orb: OrbitalProjection,
|
|
107
|
+
atom_indices: Optional[list[int]] = None,
|
|
108
|
+
) -> Float[Array, "K B 9"]:
|
|
109
|
+
"""Sum orbital projections over a set of atoms.
|
|
110
|
+
|
|
111
|
+
Extended Summary
|
|
112
|
+
----------------
|
|
113
|
+
Produces a ``(K, B, 9)`` array where the atom axis has been
|
|
114
|
+
summed out, giving the total orbital weight at each (k-point,
|
|
115
|
+
band) pair. This is the standard reduction used before computing
|
|
116
|
+
cross-section-weighted ARPES intensities, because the simulation
|
|
117
|
+
only needs the aggregate orbital character rather than per-atom
|
|
118
|
+
contributions.
|
|
119
|
+
|
|
120
|
+
Implementation Logic
|
|
121
|
+
--------------------
|
|
122
|
+
1. If ``atom_indices`` is not ``None``, fancy-index the
|
|
123
|
+
``projections`` array along axis 2 to restrict to the
|
|
124
|
+
requested atoms before summing.
|
|
125
|
+
2. If ``atom_indices`` is ``None``, use the full projections
|
|
126
|
+
array (all atoms).
|
|
127
|
+
3. Sum along axis 2 (the atom axis) using ``jnp.sum`` and
|
|
128
|
+
return the resulting ``(K, B, 9)`` array.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
orb : OrbitalProjection
|
|
133
|
+
Full orbital projections with shape ``(K, B, A, 9)``.
|
|
134
|
+
atom_indices : list[int] or None, optional
|
|
135
|
+
0-based indices of atoms to sum over. If None, sums over
|
|
136
|
+
all atoms.
|
|
137
|
+
|
|
138
|
+
Returns
|
|
139
|
+
-------
|
|
140
|
+
Float[Array, "K B 9"]
|
|
141
|
+
Atom-summed orbital projections.
|
|
142
|
+
|
|
143
|
+
Notes
|
|
144
|
+
-----
|
|
145
|
+
This function operates only on the ``projections`` field and
|
|
146
|
+
ignores ``spin`` and ``oam`` fields. For spin-resolved
|
|
147
|
+
aggregation, use :func:`select_atoms` first and then perform
|
|
148
|
+
the reduction manually.
|
|
149
|
+
"""
|
|
150
|
+
if atom_indices is not None:
|
|
151
|
+
idx: Int[Array, " N"] = jnp.asarray(atom_indices, dtype=jnp.int32)
|
|
152
|
+
proj: Float[Array, "K B N 9"] = orb.projections[:, :, idx, :]
|
|
153
|
+
else:
|
|
154
|
+
proj = orb.projections
|
|
155
|
+
result: Float[Array, "K B 9"] = jnp.sum(proj, axis=2)
|
|
156
|
+
return result
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def reduce_orbitals(
|
|
160
|
+
projections: Float[Array, "K B A 9"],
|
|
161
|
+
) -> Float[Array, "K B A 3"]:
|
|
162
|
+
"""Reduce 9 orbital channels to s/p/d totals.
|
|
163
|
+
|
|
164
|
+
Extended Summary
|
|
165
|
+
----------------
|
|
166
|
+
Collapses the 9-channel VASP orbital decomposition
|
|
167
|
+
(``s, py, pz, px, dxy, dyz, dz2, dxz, dx2-y2``) into three
|
|
168
|
+
angular-momentum shell totals. This is useful for coarse-grained
|
|
169
|
+
orbital-character analysis (e.g. fat-band coloring by s/p/d
|
|
170
|
+
weight).
|
|
171
|
+
|
|
172
|
+
Implementation Logic
|
|
173
|
+
--------------------
|
|
174
|
+
1. Extract the ``s`` channel (index 0) as-is -- shape ``(K, B, A)``.
|
|
175
|
+
2. Sum the three ``p`` channels (indices 1:4) along the last axis.
|
|
176
|
+
3. Sum the five ``d`` channels (indices 4:9) along the last axis.
|
|
177
|
+
4. Stack ``[s_total, p_total, d_total]`` along a new trailing axis
|
|
178
|
+
to produce shape ``(K, B, A, 3)``.
|
|
179
|
+
|
|
180
|
+
Parameters
|
|
181
|
+
----------
|
|
182
|
+
projections : Float[Array, "K B A 9"]
|
|
183
|
+
Full 9-channel orbital projections.
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
Float[Array, "K B A 3"]
|
|
188
|
+
Reduced projections: ``[s_total, p_total, d_total]``.
|
|
189
|
+
|
|
190
|
+
Notes
|
|
191
|
+
-----
|
|
192
|
+
The VASP orbital ordering assumed here is:
|
|
193
|
+
``[s, py, pz, px, dxy, dyz, dz2, dxz, dx2-y2]`` (indices 0-8).
|
|
194
|
+
This matches the standard PROCAR output when ``LORBIT=11`` or
|
|
195
|
+
``LORBIT=12``.
|
|
196
|
+
"""
|
|
197
|
+
s_total: Float[Array, "K B A"] = projections[..., _S_IDX]
|
|
198
|
+
p_total: Float[Array, "K B A"] = jnp.sum(
|
|
199
|
+
projections[..., _P_SLICE], axis=-1
|
|
200
|
+
)
|
|
201
|
+
d_total: Float[Array, "K B A"] = jnp.sum(
|
|
202
|
+
projections[..., _D_SLICE], axis=-1
|
|
203
|
+
)
|
|
204
|
+
reduced: Float[Array, "K B A 3"] = jnp.stack(
|
|
205
|
+
[s_total, p_total, d_total], axis=-1
|
|
206
|
+
)
|
|
207
|
+
return reduced
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def check_consistency(
|
|
211
|
+
bands: BandStructure,
|
|
212
|
+
orb: OrbitalProjection,
|
|
213
|
+
kpath: Optional[KPathInfo] = None,
|
|
214
|
+
) -> None:
|
|
215
|
+
"""Check dimension agreement across parsed VASP files.
|
|
216
|
+
|
|
217
|
+
Extended Summary
|
|
218
|
+
----------------
|
|
219
|
+
Validates that the k-point and band dimensions are consistent
|
|
220
|
+
between the EIGENVAL-derived band structure, the PROCAR-derived
|
|
221
|
+
orbital projections, and (optionally) the KPOINTS-derived path
|
|
222
|
+
metadata. This is a defensive check intended to catch mismatches
|
|
223
|
+
caused by mixing output files from different VASP runs.
|
|
224
|
+
|
|
225
|
+
Implementation Logic
|
|
226
|
+
--------------------
|
|
227
|
+
1. Extract ``nkpoints`` and ``nbands`` from ``bands.eigenvalues``
|
|
228
|
+
(shape ``(K, B)``) and ``orb.projections`` (shape
|
|
229
|
+
``(K, B, A, 9)``).
|
|
230
|
+
2. Compare the k-point counts between EIGENVAL and PROCAR. Raise
|
|
231
|
+
``ValueError`` with a descriptive message if they disagree.
|
|
232
|
+
3. Compare the band counts between EIGENVAL and PROCAR. Raise
|
|
233
|
+
``ValueError`` if they disagree.
|
|
234
|
+
4. If ``kpath`` is provided and its mode is ``"Line-mode"``,
|
|
235
|
+
compare the total k-point count from KPOINTS
|
|
236
|
+
(``kpath.num_kpoints``) against the EIGENVAL k-point count.
|
|
237
|
+
Only check when ``num_kpoints > 0`` (i.e. the KPOINTS file
|
|
238
|
+
provides a concrete count).
|
|
239
|
+
|
|
240
|
+
Parameters
|
|
241
|
+
----------
|
|
242
|
+
bands : BandStructure
|
|
243
|
+
Parsed EIGENVAL data.
|
|
244
|
+
orb : OrbitalProjection
|
|
245
|
+
Parsed PROCAR data.
|
|
246
|
+
kpath : KPathInfo or None, optional
|
|
247
|
+
Parsed KPOINTS data.
|
|
248
|
+
|
|
249
|
+
Raises
|
|
250
|
+
------
|
|
251
|
+
ValueError
|
|
252
|
+
If k-point or band counts disagree between files.
|
|
253
|
+
|
|
254
|
+
Notes
|
|
255
|
+
-----
|
|
256
|
+
This function does not verify atom counts (PROCAR vs POSCAR)
|
|
257
|
+
because ``BandStructure`` does not carry atom information.
|
|
258
|
+
For atom-count checks, compare ``orb.projections.shape[2]``
|
|
259
|
+
against ``geometry.atom_counts.sum()`` manually.
|
|
260
|
+
"""
|
|
261
|
+
nk_bands: int = int(bands.eigenvalues.shape[0])
|
|
262
|
+
nb_bands: int = int(bands.eigenvalues.shape[1])
|
|
263
|
+
nk_procar: int = int(orb.projections.shape[0])
|
|
264
|
+
nb_procar: int = int(orb.projections.shape[1])
|
|
265
|
+
|
|
266
|
+
if nk_bands != nk_procar:
|
|
267
|
+
msg = (
|
|
268
|
+
f"K-point count mismatch: EIGENVAL has {nk_bands}, "
|
|
269
|
+
f"PROCAR has {nk_procar}."
|
|
270
|
+
)
|
|
271
|
+
raise ValueError(msg)
|
|
272
|
+
|
|
273
|
+
if nb_bands != nb_procar:
|
|
274
|
+
msg = (
|
|
275
|
+
f"Band count mismatch: EIGENVAL has {nb_bands}, "
|
|
276
|
+
f"PROCAR has {nb_procar}."
|
|
277
|
+
)
|
|
278
|
+
raise ValueError(msg)
|
|
279
|
+
|
|
280
|
+
if kpath is not None and kpath.mode == "Line-mode":
|
|
281
|
+
nk_kpath: int = int(kpath.num_kpoints)
|
|
282
|
+
if nk_kpath > 0 and nk_bands != nk_kpath:
|
|
283
|
+
msg = (
|
|
284
|
+
f"K-point count mismatch: EIGENVAL has {nk_bands}, "
|
|
285
|
+
f"KPOINTS has {nk_kpath}."
|
|
286
|
+
)
|
|
287
|
+
raise ValueError(msg)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
__all__: list[str] = [
|
|
291
|
+
"aggregate_atoms",
|
|
292
|
+
"check_consistency",
|
|
293
|
+
"reduce_orbitals",
|
|
294
|
+
"select_atoms",
|
|
295
|
+
]
|