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/kpoints.py
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
"""VASP KPOINTS file parser.
|
|
2
|
+
|
|
3
|
+
Extended Summary
|
|
4
|
+
----------------
|
|
5
|
+
Reads VASP KPOINTS files and returns a
|
|
6
|
+
:class:`~diffpes.types.KPathInfo` PyTree containing plotting labels and
|
|
7
|
+
mode-specific metadata (automatic grid/shift, explicit weights, and
|
|
8
|
+
line-mode segment endpoints).
|
|
9
|
+
|
|
10
|
+
Routine Listings
|
|
11
|
+
----------------
|
|
12
|
+
:func:`read_kpoints`
|
|
13
|
+
Parse a VASP KPOINTS file into KPathInfo.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
import jax.numpy as jnp
|
|
20
|
+
from beartype.typing import Optional
|
|
21
|
+
|
|
22
|
+
from diffpes.types import KPathInfo, make_kpath_info
|
|
23
|
+
|
|
24
|
+
_FLOAT_TOKEN_RE: re.Pattern[str] = re.compile(
|
|
25
|
+
r"[-+]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][-+]?\d+)?"
|
|
26
|
+
)
|
|
27
|
+
_XYZ_COMPONENTS: int = 3
|
|
28
|
+
_WEIGHT_COMPONENT_INDEX: int = 3
|
|
29
|
+
_WEIGHT_COMPONENT_COUNT: int = 4
|
|
30
|
+
_COORDINATE_MODE_TOKENS: set[str] = {
|
|
31
|
+
"cartesian",
|
|
32
|
+
"reciprocal",
|
|
33
|
+
"direct",
|
|
34
|
+
"fractional",
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def read_kpoints( # noqa: PLR0915
|
|
39
|
+
filename: str = "KPOINTS",
|
|
40
|
+
) -> KPathInfo:
|
|
41
|
+
"""Parse a VASP KPOINTS file.
|
|
42
|
+
|
|
43
|
+
Reads a VASP KPOINTS file that specifies Brillouin-zone sampling.
|
|
44
|
+
Supports the three standard modes:
|
|
45
|
+
Line-mode (path segments), Automatic (Monkhorst-Pack/Gamma grids),
|
|
46
|
+
and Explicit (listed k-points with optional weights).
|
|
47
|
+
|
|
48
|
+
Implementation Logic
|
|
49
|
+
--------------------
|
|
50
|
+
1. Parse comment, line-2 integer, and line-3 mode/scheme.
|
|
51
|
+
2. Dispatch by mode:
|
|
52
|
+
- Line-mode: parse paired endpoint lines, derive segment count,
|
|
53
|
+
endpoint k-points, labels, and label indices.
|
|
54
|
+
- Automatic: parse grid and shift vectors.
|
|
55
|
+
- Explicit: parse listed k-points and weights.
|
|
56
|
+
3. Construct ``KPathInfo`` with both legacy plotting fields and
|
|
57
|
+
richer mode-specific metadata.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
filename : str, optional
|
|
62
|
+
Path to KPOINTS file. Default is ``"KPOINTS"``.
|
|
63
|
+
|
|
64
|
+
Returns
|
|
65
|
+
-------
|
|
66
|
+
kpath : KPathInfo
|
|
67
|
+
K-point metadata including labels/indices and mode-specific
|
|
68
|
+
parsed fields.
|
|
69
|
+
|
|
70
|
+
Notes
|
|
71
|
+
-----
|
|
72
|
+
In Line-mode, line 2 is points-per-segment. ``num_kpoints`` in the
|
|
73
|
+
returned object is the total count ``segments * points_per_segment``,
|
|
74
|
+
preserving existing plotting behavior.
|
|
75
|
+
"""
|
|
76
|
+
path: Path = Path(filename)
|
|
77
|
+
with path.open("r") as fid:
|
|
78
|
+
comment: str = fid.readline().strip()
|
|
79
|
+
num_line: str = fid.readline().strip()
|
|
80
|
+
points_per_segment: int = int(num_line.split(maxsplit=1)[0])
|
|
81
|
+
scheme_or_mode: str = fid.readline().strip()
|
|
82
|
+
mode_line: str = scheme_or_mode.lower()
|
|
83
|
+
|
|
84
|
+
labels: list[str] = []
|
|
85
|
+
label_indices: list[int] = []
|
|
86
|
+
line_endpoints: list[list[float]] = []
|
|
87
|
+
explicit_kpoints: list[list[float]] = []
|
|
88
|
+
explicit_weights: list[float] = []
|
|
89
|
+
grid: Optional[list[int]] = None
|
|
90
|
+
shift: Optional[list[float]] = None
|
|
91
|
+
coord_mode: str = ""
|
|
92
|
+
segments: int = 0
|
|
93
|
+
total_kpts: int
|
|
94
|
+
|
|
95
|
+
if "line" in mode_line:
|
|
96
|
+
mode: str = "Line-mode"
|
|
97
|
+
coord_mode = fid.readline().strip()
|
|
98
|
+
raw_lines: list[str] = [
|
|
99
|
+
line.strip() for line in fid if line.strip()
|
|
100
|
+
]
|
|
101
|
+
segments = len(raw_lines) // 2
|
|
102
|
+
|
|
103
|
+
if segments > 0:
|
|
104
|
+
line_endpoints.append(_extract_coords(raw_lines[0]))
|
|
105
|
+
labels.append(_extract_label(raw_lines[0]))
|
|
106
|
+
label_indices.append(0)
|
|
107
|
+
|
|
108
|
+
idx: int = 0
|
|
109
|
+
for seg in range(segments):
|
|
110
|
+
end_line: str = raw_lines[2 * seg + 1]
|
|
111
|
+
line_endpoints.append(_extract_coords(end_line))
|
|
112
|
+
labels.append(_extract_label(end_line))
|
|
113
|
+
idx += points_per_segment
|
|
114
|
+
label_indices.append(idx - 1)
|
|
115
|
+
|
|
116
|
+
total_kpts = segments * points_per_segment
|
|
117
|
+
elif points_per_segment == 0:
|
|
118
|
+
mode = "Automatic"
|
|
119
|
+
coord_mode = scheme_or_mode
|
|
120
|
+
grid = _parse_grid(fid.readline())
|
|
121
|
+
shift = _parse_shift(fid.readline())
|
|
122
|
+
total_kpts = 0
|
|
123
|
+
else:
|
|
124
|
+
mode = "Explicit"
|
|
125
|
+
remaining_lines: list[str] = [
|
|
126
|
+
line.strip() for line in fid if line.strip()
|
|
127
|
+
]
|
|
128
|
+
coord_mode = scheme_or_mode
|
|
129
|
+
if (
|
|
130
|
+
mode_line not in _COORDINATE_MODE_TOKENS
|
|
131
|
+
and remaining_lines
|
|
132
|
+
and not _looks_like_kpoint_line(remaining_lines[0])
|
|
133
|
+
):
|
|
134
|
+
coord_mode = remaining_lines.pop(0)
|
|
135
|
+
explicit_kpoints, explicit_weights = _parse_explicit_kpoints(
|
|
136
|
+
remaining_lines, points_per_segment
|
|
137
|
+
)
|
|
138
|
+
total_kpts = points_per_segment
|
|
139
|
+
|
|
140
|
+
line_endpoints_arr: Optional[jnp.ndarray] = None
|
|
141
|
+
if line_endpoints:
|
|
142
|
+
line_endpoints_arr = jnp.asarray(line_endpoints, dtype=jnp.float64)
|
|
143
|
+
explicit_kpoints_arr: Optional[jnp.ndarray] = None
|
|
144
|
+
if explicit_kpoints:
|
|
145
|
+
explicit_kpoints_arr = jnp.asarray(explicit_kpoints, dtype=jnp.float64)
|
|
146
|
+
explicit_weights_arr: Optional[jnp.ndarray] = None
|
|
147
|
+
if explicit_weights:
|
|
148
|
+
explicit_weights_arr = jnp.asarray(explicit_weights, dtype=jnp.float64)
|
|
149
|
+
grid_arr: Optional[jnp.ndarray] = None
|
|
150
|
+
if grid is not None:
|
|
151
|
+
grid_arr = jnp.asarray(grid, dtype=jnp.int32)
|
|
152
|
+
shift_arr: Optional[jnp.ndarray] = None
|
|
153
|
+
if shift is not None:
|
|
154
|
+
shift_arr = jnp.asarray(shift, dtype=jnp.float64)
|
|
155
|
+
|
|
156
|
+
parsed_kpoints: Optional[jnp.ndarray] = line_endpoints_arr
|
|
157
|
+
parsed_weights: Optional[jnp.ndarray] = None
|
|
158
|
+
if mode == "Explicit":
|
|
159
|
+
parsed_kpoints = explicit_kpoints_arr
|
|
160
|
+
parsed_weights = explicit_weights_arr
|
|
161
|
+
|
|
162
|
+
kpath: KPathInfo = make_kpath_info(
|
|
163
|
+
num_kpoints=total_kpts,
|
|
164
|
+
label_indices=label_indices if label_indices else [0],
|
|
165
|
+
points_per_segment=points_per_segment,
|
|
166
|
+
segments=segments,
|
|
167
|
+
kpoints=parsed_kpoints,
|
|
168
|
+
weights=parsed_weights,
|
|
169
|
+
grid=grid_arr,
|
|
170
|
+
shift=shift_arr,
|
|
171
|
+
mode=mode,
|
|
172
|
+
labels=tuple(labels),
|
|
173
|
+
comment=comment,
|
|
174
|
+
coordinate_mode=coord_mode,
|
|
175
|
+
)
|
|
176
|
+
return kpath
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _parse_explicit_kpoints(
|
|
180
|
+
lines: list[str],
|
|
181
|
+
num_kpoints: int,
|
|
182
|
+
) -> tuple[list[list[float]], list[float]]:
|
|
183
|
+
"""Parse explicit-mode k-point coordinates and optional weights.
|
|
184
|
+
|
|
185
|
+
Extended Summary
|
|
186
|
+
----------------
|
|
187
|
+
In VASP explicit KPOINTS mode, each k-point is listed on its own
|
|
188
|
+
line with at least three fractional/Cartesian coordinates and an
|
|
189
|
+
optional fourth column for the integration weight.
|
|
190
|
+
|
|
191
|
+
Implementation Logic
|
|
192
|
+
--------------------
|
|
193
|
+
1. Iterate over ``lines``, stopping once ``num_kpoints`` k-points
|
|
194
|
+
have been collected.
|
|
195
|
+
2. Split each line and parse all tokens as floats. Raise
|
|
196
|
+
``ValueError`` if any token is not numeric or if fewer than 3
|
|
197
|
+
values are present.
|
|
198
|
+
3. Store the first 3 values as coordinates.
|
|
199
|
+
4. If a 4th value is present, use it as the weight; otherwise
|
|
200
|
+
default to 1.0.
|
|
201
|
+
|
|
202
|
+
Parameters
|
|
203
|
+
----------
|
|
204
|
+
lines : list[str]
|
|
205
|
+
Remaining lines from the KPOINTS file after the mode line.
|
|
206
|
+
num_kpoints : int
|
|
207
|
+
Expected number of k-points to parse.
|
|
208
|
+
|
|
209
|
+
Returns
|
|
210
|
+
-------
|
|
211
|
+
points : list[list[float]]
|
|
212
|
+
Parsed k-point coordinates, each a 3-element list.
|
|
213
|
+
weights : list[float]
|
|
214
|
+
Corresponding k-point weights.
|
|
215
|
+
|
|
216
|
+
Raises
|
|
217
|
+
------
|
|
218
|
+
ValueError
|
|
219
|
+
If a coordinate line cannot be parsed or has fewer than 3
|
|
220
|
+
numeric tokens.
|
|
221
|
+
"""
|
|
222
|
+
points: list[list[float]] = []
|
|
223
|
+
weights: list[float] = []
|
|
224
|
+
for stripped in lines:
|
|
225
|
+
if len(points) >= num_kpoints:
|
|
226
|
+
break
|
|
227
|
+
parts: list[float]
|
|
228
|
+
try:
|
|
229
|
+
parts = [float(x) for x in stripped.split()]
|
|
230
|
+
except ValueError as exc:
|
|
231
|
+
msg = f"Invalid explicit KPOINTS coordinate line: {stripped!r}"
|
|
232
|
+
raise ValueError(msg) from exc
|
|
233
|
+
if len(parts) < _XYZ_COMPONENTS:
|
|
234
|
+
msg = "Explicit KPOINTS line must contain at least 3 coordinates."
|
|
235
|
+
raise ValueError(msg)
|
|
236
|
+
points.append(parts[:_XYZ_COMPONENTS])
|
|
237
|
+
if len(parts) >= _WEIGHT_COMPONENT_COUNT:
|
|
238
|
+
weights.append(parts[_WEIGHT_COMPONENT_INDEX])
|
|
239
|
+
else:
|
|
240
|
+
weights.append(1.0)
|
|
241
|
+
return points, weights
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _looks_like_kpoint_line(line: str) -> bool:
|
|
245
|
+
"""Return ``True`` when the first three tokens are parseable floats.
|
|
246
|
+
|
|
247
|
+
Extended Summary
|
|
248
|
+
----------------
|
|
249
|
+
Heuristic used to distinguish a k-point coordinate line from a
|
|
250
|
+
coordinate-mode descriptor line (e.g. ``"Reciprocal"``) when the
|
|
251
|
+
KPOINTS file uses explicit mode and the mode keyword also happens
|
|
252
|
+
to appear on line 3. Lines with fewer than 3 tokens or with
|
|
253
|
+
non-numeric leading tokens return ``False``.
|
|
254
|
+
|
|
255
|
+
Parameters
|
|
256
|
+
----------
|
|
257
|
+
line : str
|
|
258
|
+
A single line from the KPOINTS file.
|
|
259
|
+
|
|
260
|
+
Returns
|
|
261
|
+
-------
|
|
262
|
+
bool
|
|
263
|
+
``True`` if the first three whitespace-separated tokens can
|
|
264
|
+
all be converted to ``float``; ``False`` otherwise.
|
|
265
|
+
"""
|
|
266
|
+
parts: list[str] = line.split()
|
|
267
|
+
if len(parts) < _XYZ_COMPONENTS:
|
|
268
|
+
return False
|
|
269
|
+
try:
|
|
270
|
+
float(parts[0])
|
|
271
|
+
float(parts[1])
|
|
272
|
+
float(parts[2])
|
|
273
|
+
except ValueError:
|
|
274
|
+
return False
|
|
275
|
+
return True
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _parse_grid(line: str) -> list[int]:
|
|
279
|
+
"""Parse automatic-mode grid line into three integers.
|
|
280
|
+
|
|
281
|
+
Extended Summary
|
|
282
|
+
----------------
|
|
283
|
+
In VASP automatic KPOINTS mode, the line immediately after the
|
|
284
|
+
scheme keyword contains the three Monkhorst-Pack grid subdivisions
|
|
285
|
+
``N1 N2 N3``. Tokens are parsed as floats first and then rounded
|
|
286
|
+
to the nearest integer to tolerate files written with decimal
|
|
287
|
+
points (e.g. ``"4.0 4.0 4.0"``).
|
|
288
|
+
|
|
289
|
+
Parameters
|
|
290
|
+
----------
|
|
291
|
+
line : str
|
|
292
|
+
The grid-dimension line from the KPOINTS file.
|
|
293
|
+
|
|
294
|
+
Returns
|
|
295
|
+
-------
|
|
296
|
+
list[int]
|
|
297
|
+
Three-element list ``[N1, N2, N3]``.
|
|
298
|
+
|
|
299
|
+
Raises
|
|
300
|
+
------
|
|
301
|
+
ValueError
|
|
302
|
+
If the line contains fewer than 3 whitespace-separated tokens.
|
|
303
|
+
"""
|
|
304
|
+
vals: list[str] = line.split()
|
|
305
|
+
if len(vals) < _XYZ_COMPONENTS:
|
|
306
|
+
msg = "Automatic KPOINTS grid line must have 3 values."
|
|
307
|
+
raise ValueError(msg)
|
|
308
|
+
return [
|
|
309
|
+
int(round(float(vals[0]))),
|
|
310
|
+
int(round(float(vals[1]))),
|
|
311
|
+
int(round(float(vals[2]))),
|
|
312
|
+
]
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _parse_shift(line: str) -> list[float]:
|
|
316
|
+
"""Parse automatic-mode shift line into three floats.
|
|
317
|
+
|
|
318
|
+
Extended Summary
|
|
319
|
+
----------------
|
|
320
|
+
In VASP automatic KPOINTS mode, the line after the grid line
|
|
321
|
+
contains the shift vector applied to the Monkhorst-Pack mesh.
|
|
322
|
+
Values of ``0 0 0`` indicate a Gamma-centred grid; non-zero
|
|
323
|
+
values shift the mesh in fractional reciprocal-space coordinates.
|
|
324
|
+
|
|
325
|
+
Parameters
|
|
326
|
+
----------
|
|
327
|
+
line : str
|
|
328
|
+
The shift line from the KPOINTS file.
|
|
329
|
+
|
|
330
|
+
Returns
|
|
331
|
+
-------
|
|
332
|
+
list[float]
|
|
333
|
+
Three-element list ``[s1, s2, s3]``.
|
|
334
|
+
|
|
335
|
+
Raises
|
|
336
|
+
------
|
|
337
|
+
ValueError
|
|
338
|
+
If the line contains fewer than 3 whitespace-separated tokens.
|
|
339
|
+
"""
|
|
340
|
+
vals: list[str] = line.split()
|
|
341
|
+
if len(vals) < _XYZ_COMPONENTS:
|
|
342
|
+
msg = "Automatic KPOINTS shift line must have 3 values."
|
|
343
|
+
raise ValueError(msg)
|
|
344
|
+
return [float(vals[0]), float(vals[1]), float(vals[2])]
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _extract_coords(line: str) -> list[float]:
|
|
348
|
+
"""Extract first three float tokens from a KPOINTS coordinate line.
|
|
349
|
+
|
|
350
|
+
Extended Summary
|
|
351
|
+
----------------
|
|
352
|
+
Uses a regex (``_FLOAT_TOKEN_RE``) to find all floating-point
|
|
353
|
+
tokens in the line, regardless of surrounding non-numeric text
|
|
354
|
+
(such as symmetry labels or comment markers). This makes the
|
|
355
|
+
extraction robust to various KPOINTS formatting styles.
|
|
356
|
+
|
|
357
|
+
Parameters
|
|
358
|
+
----------
|
|
359
|
+
line : str
|
|
360
|
+
A single k-point coordinate line.
|
|
361
|
+
|
|
362
|
+
Returns
|
|
363
|
+
-------
|
|
364
|
+
list[float]
|
|
365
|
+
Three-element list of the first three float values found.
|
|
366
|
+
|
|
367
|
+
Raises
|
|
368
|
+
------
|
|
369
|
+
ValueError
|
|
370
|
+
If fewer than 3 float tokens are found on the line.
|
|
371
|
+
"""
|
|
372
|
+
tokens: list[str] = _FLOAT_TOKEN_RE.findall(line)
|
|
373
|
+
if len(tokens) < _XYZ_COMPONENTS:
|
|
374
|
+
msg = f"Could not parse k-point coordinates from line: {line!r}"
|
|
375
|
+
raise ValueError(msg)
|
|
376
|
+
return [float(tokens[0]), float(tokens[1]), float(tokens[2])]
|
|
377
|
+
|
|
378
|
+
|
|
379
|
+
def _extract_label(line: str) -> str:
|
|
380
|
+
r"""Extract symmetry label from a KPOINTS line.
|
|
381
|
+
|
|
382
|
+
Parses a single coordinate line from a VASP KPOINTS file and
|
|
383
|
+
attempts to recover the human-readable symmetry label (e.g.
|
|
384
|
+
``"G"``, ``"X"``, ``"M"``) that is conventionally appended after
|
|
385
|
+
the three fractional coordinates.
|
|
386
|
+
|
|
387
|
+
Implementation Logic
|
|
388
|
+
--------------------
|
|
389
|
+
1. **Regex match** -- search for the pattern ``! <label>`` using
|
|
390
|
+
``re.search(r"!\\s*(\\S+)", line)``. The ``!`` delimiter is the
|
|
391
|
+
standard VASP comment marker used by AFLOW, SeeK-path, and most
|
|
392
|
+
KPOINTS generators. If a match is found, return the first
|
|
393
|
+
captured non-whitespace group.
|
|
394
|
+
|
|
395
|
+
2. **Fallback heuristic** -- if no ``!`` marker is present, split
|
|
396
|
+
the line on whitespace. If there are more than 4 tokens (three
|
|
397
|
+
coordinates plus at least a weight and a label), return the last
|
|
398
|
+
token as the label.
|
|
399
|
+
|
|
400
|
+
3. **Default** -- if neither strategy yields a label, return an
|
|
401
|
+
empty string.
|
|
402
|
+
|
|
403
|
+
Parameters
|
|
404
|
+
----------
|
|
405
|
+
line : str
|
|
406
|
+
A single k-point line from the KPOINTS file, typically of the
|
|
407
|
+
form ``"0.0 0.0 0.0 ! G"`` or ``"0.0 0.0 0.0 1 G"``.
|
|
408
|
+
|
|
409
|
+
Returns
|
|
410
|
+
-------
|
|
411
|
+
label : str
|
|
412
|
+
Extracted symmetry label, or ``""`` if none is found.
|
|
413
|
+
|
|
414
|
+
Notes
|
|
415
|
+
-----
|
|
416
|
+
The ``! LABEL`` convention is the most reliable. The fallback
|
|
417
|
+
heuristic can misidentify a numeric weight as a label when the
|
|
418
|
+
line has exactly 5 whitespace-separated tokens, but this situation
|
|
419
|
+
is rare in practice.
|
|
420
|
+
"""
|
|
421
|
+
_min_parts_with_label: int = 4
|
|
422
|
+
match: Optional[re.Match[str]] = re.search(r"!\s*(\S+)", line)
|
|
423
|
+
if match:
|
|
424
|
+
return match.group(1)
|
|
425
|
+
parts: list[str] = line.split()
|
|
426
|
+
if len(parts) > _min_parts_with_label:
|
|
427
|
+
return parts[-1]
|
|
428
|
+
return ""
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
__all__: list[str] = [
|
|
432
|
+
"read_kpoints",
|
|
433
|
+
]
|