irma 1.0.0__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.
- irma/__init__.py +66 -0
- irma/__main__.py +7 -0
- irma/cli.py +198 -0
- irma/core/__init__.py +1 -0
- irma/core/constants.py +88 -0
- irma/core/crystal.py +915 -0
- irma/core/crystal_cards.py +745 -0
- irma/core/crystal_input.py +137 -0
- irma/core/deck.py +583 -0
- irma/core/driver.py +761 -0
- irma/core/elastic_dw.py +251 -0
- irma/core/elastic_extinction.py +369 -0
- irma/core/endf_writer.py +1536 -0
- irma/core/engine.py +102 -0
- irma/core/extinction.py +456 -0
- irma/core/grids.py +410 -0
- irma/core/incoherent_dw.py +252 -0
- irma/core/kernels.py +1624 -0
- irma/core/noncubic_engine.py +2149 -0
- irma/core/noncubic_helpers.py +265 -0
- irma/core/noncubic_inelastic.py +70 -0
- irma/core/noncubic_inelastic_context.py +648 -0
- irma/core/noncubic_numerics.py +329 -0
- irma/core/noncubic_workers.py +1542 -0
- irma/core/nuclear_data.py +760 -0
- irma/core/phonopy_io.py +1082 -0
- irma/core/sab_grids.py +98 -0
- irma/core/standalone_sab.py +458 -0
- irma/gui/__init__.py +1 -0
- irma/gui/app.py +235 -0
- irma/gui/deck_text.py +540 -0
- irma/gui/element_table.py +540 -0
- irma/gui/endf_form.py +3404 -0
- irma/gui/grid_form.py +357 -0
- irma/gui/mlip_panel.py +1166 -0
- irma/gui/ncrystal_panel.py +627 -0
- irma/gui/ns_panel.py +1952 -0
- irma/gui/runner.py +318 -0
- irma/gui/widgets.py +415 -0
- irma/mlip/__init__.py +6 -0
- irma/mlip/bundle.py +609 -0
- irma/mlip/calculators.py +856 -0
- irma/mlip/cli.py +626 -0
- irma/mlip/emit.py +935 -0
- irma/mlip/envs.py +614 -0
- irma/mlip/force_server.py +173 -0
- irma/mlip/phonons.py +348 -0
- irma/mlip/relax.py +257 -0
- irma/ncrystal/__init__.py +55 -0
- irma/ncrystal/__main__.py +90 -0
- irma/ncrystal/build.py +670 -0
- irma/ncrystal/config.py +449 -0
- irma/ncrystal/convert.py +161 -0
- irma/ncrystal/ncmat.py +132 -0
- irma/ncrystal/pack.py +404 -0
- irma/ncrystal/provenance.py +123 -0
- irma/spectra/__init__.py +67 -0
- irma/spectra/__main__.py +7 -0
- irma/spectra/chopper_resolution.py +646 -0
- irma/spectra/cli.py +751 -0
- irma/spectra/config.py +1105 -0
- irma/spectra/dos_from_phonopy.py +83 -0
- irma/spectra/dos_io.py +117 -0
- irma/spectra/dos_mode0.py +412 -0
- irma/spectra/elastic.py +937 -0
- irma/spectra/forward.py +1091 -0
- irma/spectra/instruments.py +281 -0
- irma/spectra/sqe.py +544 -0
- irma-1.0.0.dist-info/METADATA +584 -0
- irma-1.0.0.dist-info/RECORD +76 -0
- irma-1.0.0.dist-info/WHEEL +5 -0
- irma-1.0.0.dist-info/entry_points.txt +5 -0
- irma-1.0.0.dist-info/licenses/LICENSE +30 -0
- irma-1.0.0.dist-info/licenses/LICENSES/Apache-2.0.txt +202 -0
- irma-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +101 -0
- irma-1.0.0.dist-info/top_level.txt +1 -0
irma/__init__.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""IRMA — (In)elastic Representation of Materials As S(alpha,beta) evaluations.
|
|
2
|
+
|
|
3
|
+
A standalone tool for generating thermal neutron scattering law S(alpha, beta)
|
|
4
|
+
libraries in ENDF-6 format using the phonon expansion method.
|
|
5
|
+
|
|
6
|
+
Supports:
|
|
7
|
+
- Continuous phonon frequency distributions
|
|
8
|
+
- Generalized coherent elastic (Bragg edges) for arbitrary crystals
|
|
9
|
+
- Phonopy-backed hybrid inelastic_mode paths for noncubic MT4 generation
|
|
10
|
+
- Free-gas and diffusion translational modes
|
|
11
|
+
- Discrete oscillators
|
|
12
|
+
- Cold hydrogen/deuterium (ortho/para)
|
|
13
|
+
- Automatic alpha/beta grid generation
|
|
14
|
+
- Neutron-scattering forward model (irma.spectra): powder S(Q,E) ->
|
|
15
|
+
instrument-resolved 1-D spectrum for VISION / indirect / direct geometries,
|
|
16
|
+
with an auto chopper-resolution model for the 8 PyChop direct instruments
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
__version__ = "1.0.0"
|
|
20
|
+
__author__ = "IRMA developers"
|
|
21
|
+
|
|
22
|
+
# Pin native BLAS/OMP thread pools to 1 at package import, before numpy can be
|
|
23
|
+
# loaded through irma. The mode-1/2 engine parallelizes across SPAWNED worker
|
|
24
|
+
# processes (get_context("spawn")); a multithreaded BLAS in the parent or the
|
|
25
|
+
# workers oversubscribes the cores under the process pool (measured >10x
|
|
26
|
+
# slower). Doing it here covers the common `import irma` / CLI entry;
|
|
27
|
+
# setdefault preserves a deliberate caller override (a host that imports numpy
|
|
28
|
+
# before irma should set these itself, or accept threaded BLAS).
|
|
29
|
+
import os as _os
|
|
30
|
+
|
|
31
|
+
for _v in ("OMP_NUM_THREADS", "OPENBLAS_NUM_THREADS", "MKL_NUM_THREADS",
|
|
32
|
+
"NUMEXPR_NUM_THREADS", "VECLIB_MAXIMUM_THREADS", "BLIS_NUM_THREADS"):
|
|
33
|
+
_os.environ.setdefault(_v, "1")
|
|
34
|
+
|
|
35
|
+
# Stable top-level public API. Importing these from `irma` directly
|
|
36
|
+
# (`from irma import run_leapr, DeckError`) decouples callers from the internal
|
|
37
|
+
# module layout, so the core modules can be refactored without breaking them.
|
|
38
|
+
# Exposed LAZILY (PEP 562 __getattr__) so `import irma` stays light -- the heavy
|
|
39
|
+
# engine is only imported when one of these names is actually accessed, and the
|
|
40
|
+
# light core (numpy + endf-parserpy) install never pulls an optional extra.
|
|
41
|
+
__all__ = ["run_leapr", "LeaprResult", "parse_leapr_input", "DeckError",
|
|
42
|
+
"__version__", "__author__"]
|
|
43
|
+
|
|
44
|
+
# public name -> "module:attr" it is loaded from on first access
|
|
45
|
+
_PUBLIC_API = {
|
|
46
|
+
"run_leapr": "irma.core.engine:run_leapr",
|
|
47
|
+
"LeaprResult": "irma.core.driver:LeaprResult",
|
|
48
|
+
"parse_leapr_input": "irma.core.deck:parse_leapr_input",
|
|
49
|
+
"DeckError": "irma.core.deck:DeckError",
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def __getattr__(name):
|
|
54
|
+
"""Lazily resolve the public API symbols (PEP 562)."""
|
|
55
|
+
target = _PUBLIC_API.get(name)
|
|
56
|
+
if target is None:
|
|
57
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
58
|
+
import importlib
|
|
59
|
+
module_name, attr = target.split(":")
|
|
60
|
+
value = getattr(importlib.import_module(module_name), attr)
|
|
61
|
+
globals()[name] = value # cache so later access skips __getattr__
|
|
62
|
+
return value
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def __dir__():
|
|
66
|
+
return sorted(set(globals()) | set(__all__))
|
irma/__main__.py
ADDED
irma/cli.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""Command-line interface for IRMA.
|
|
2
|
+
|
|
3
|
+
Four capabilities behind one ``irma`` entry point:
|
|
4
|
+
|
|
5
|
+
irma <deck> <out.endf> ENDF/TSL evaluation (legacy, unchanged)
|
|
6
|
+
irma evaluate <deck> <out.endf> same, explicit (alias: run)
|
|
7
|
+
irma spectra vision|indirect|direct ... neutron-scattering forward spectra
|
|
8
|
+
irma spectra run <config> -o out.csv
|
|
9
|
+
irma ncrystal -o <outdir> config.yaml NCrystal .irmapack export
|
|
10
|
+
irma --gui | --version
|
|
11
|
+
|
|
12
|
+
Backward compatibility is mandatory: if the first argument is not a known
|
|
13
|
+
subcommand keyword, it is treated as the legacy 2-positional deck path, so
|
|
14
|
+
``irma deck out.endf``, ``python -m irma deck out.endf`` and
|
|
15
|
+
``python -m irma.core.engine deck out.endf`` keep working byte-for-byte.
|
|
16
|
+
|
|
17
|
+
``main(argv=None)`` returns a process exit code; the console-script and
|
|
18
|
+
``python -m irma`` wrappers both ``sys.exit(main())``.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _print_help():
|
|
26
|
+
"""Print the top-level CLI usage text."""
|
|
27
|
+
print("IRMA — (In)elastic Representation of Materials "
|
|
28
|
+
"As S(alpha,beta) evaluations")
|
|
29
|
+
print()
|
|
30
|
+
print("Usage:")
|
|
31
|
+
print(" irma <input_file> <output_file> "
|
|
32
|
+
"Run ENDF/TSL evaluation from an input deck")
|
|
33
|
+
print(" irma evaluate <input_file> <output_file> "
|
|
34
|
+
"Same, explicit (alias: run)")
|
|
35
|
+
print(" irma spectra vision|indirect|direct ... "
|
|
36
|
+
"Neutron-scattering forward spectrum")
|
|
37
|
+
print(" irma spectra run <config> -o out.csv "
|
|
38
|
+
"Run a spectra config file")
|
|
39
|
+
print(" irma ncrystal -o <outdir> config.yaml "
|
|
40
|
+
"Bake NCrystal .irmapack set (alias: python -m irma.ncrystal)")
|
|
41
|
+
print(" irma mlip build <structure> -o <outdir> "
|
|
42
|
+
"MLIP phonon model bundle (no DFT needed)")
|
|
43
|
+
print(" irma mlip emit <bundle> --to endf,... "
|
|
44
|
+
"Generate IRMA inputs from a bundle")
|
|
45
|
+
print(" irma mlip validate <bundle> "
|
|
46
|
+
"Check a bundle end to end")
|
|
47
|
+
print(" irma --gui "
|
|
48
|
+
"Launch graphical interface")
|
|
49
|
+
print(" irma --version "
|
|
50
|
+
"Show version")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _launch_gui():
|
|
54
|
+
"""Start the Tk GUI, with a clear message if tkinter is unavailable."""
|
|
55
|
+
try:
|
|
56
|
+
from irma.gui.app import main as gui_main
|
|
57
|
+
except ImportError as exc:
|
|
58
|
+
print(f"\nThe GUI could not be started: {exc}", file=sys.stderr)
|
|
59
|
+
print("The graphical interface requires tkinter, which is not "
|
|
60
|
+
"installed for this Python.", file=sys.stderr)
|
|
61
|
+
print("See the Tkinter section of INSTALL.md for how to install "
|
|
62
|
+
"it on your system.", file=sys.stderr)
|
|
63
|
+
return 4
|
|
64
|
+
gui_main()
|
|
65
|
+
return 0
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def validate_output_path(path):
|
|
69
|
+
"""Pre-flight an output file path BEFORE a long compute (shared by the deck
|
|
70
|
+
and spectra CLIs). Catches the common fail-late cases: the target is an
|
|
71
|
+
existing directory, the parent directory is missing, or the parent is not
|
|
72
|
+
writable. Returns an error message on failure, else None (each caller picks
|
|
73
|
+
its own exit code / stream)."""
|
|
74
|
+
p = os.path.abspath(str(path))
|
|
75
|
+
if os.path.isdir(p):
|
|
76
|
+
return f"output path is a directory, not a file: {p}"
|
|
77
|
+
if os.path.exists(p) and not os.access(p, os.W_OK):
|
|
78
|
+
# An existing read-only target otherwise fails only at the
|
|
79
|
+
# post-compute open (review S10).
|
|
80
|
+
return f"output file exists and is not writable: {p}"
|
|
81
|
+
parent = os.path.dirname(p)
|
|
82
|
+
if not os.path.isdir(parent):
|
|
83
|
+
return f"output directory does not exist: {parent}"
|
|
84
|
+
if not os.access(parent, os.W_OK):
|
|
85
|
+
return f"output directory is not writable: {parent}"
|
|
86
|
+
return None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _run_deck(args):
|
|
90
|
+
"""Legacy 2-positional ENDF/TSL evaluation (run_leapr), unchanged."""
|
|
91
|
+
# `irma evaluate --help` / `irma run --help` is the natural discovery
|
|
92
|
+
# command; asking for help must never be answered with an error.
|
|
93
|
+
if args and args[0] in ("-h", "--help"):
|
|
94
|
+
_print_help()
|
|
95
|
+
return 0
|
|
96
|
+
if len(args) < 2:
|
|
97
|
+
print("Error: both input_file and output_file are required.", file=sys.stderr)
|
|
98
|
+
print("Usage: irma <input_file> <output_file>", file=sys.stderr)
|
|
99
|
+
return 1
|
|
100
|
+
input_file, output_file = args[0], args[1]
|
|
101
|
+
# Mirror the output-side preflight: a directory passed as the input (a
|
|
102
|
+
# one-keystroke tab-completion slip) must fail with a clear message, not
|
|
103
|
+
# leak an IsADirectoryError traceback out of the deck parser's open().
|
|
104
|
+
if os.path.isdir(input_file):
|
|
105
|
+
print(f"Error: input path is a directory, not a file: {input_file}",
|
|
106
|
+
file=sys.stderr)
|
|
107
|
+
print("Pass the LEAPR-style input deck itself "
|
|
108
|
+
"(e.g. graphite.input).", file=sys.stderr)
|
|
109
|
+
return 1
|
|
110
|
+
if not os.path.isfile(input_file):
|
|
111
|
+
print(f"Error: input file not found: {input_file}", file=sys.stderr)
|
|
112
|
+
return 1
|
|
113
|
+
# Pre-validate the output path BEFORE the (multi-minute) compute, so a typo'd
|
|
114
|
+
# output path / directory target / read-only parent fails fast instead of only
|
|
115
|
+
# after the whole run finishes.
|
|
116
|
+
err = validate_output_path(output_file)
|
|
117
|
+
if err:
|
|
118
|
+
print(f"Error: {err}", file=sys.stderr)
|
|
119
|
+
return 1
|
|
120
|
+
from irma.core.engine import run_leapr, DeckError
|
|
121
|
+
try:
|
|
122
|
+
run_leapr(input_file, output_file)
|
|
123
|
+
except DeckError as exc:
|
|
124
|
+
print(f"\nInput deck error:\n {exc}", file=sys.stderr)
|
|
125
|
+
print("Fix the input deck and rerun (see the README card-by-card "
|
|
126
|
+
"input reference).", file=sys.stderr)
|
|
127
|
+
return 2
|
|
128
|
+
except (RuntimeError, OSError, ValueError) as exc:
|
|
129
|
+
# User-meaningful failures (phonopy model loading, missing/unreadable
|
|
130
|
+
# files, semantic problems) get a clean message; a traceback is
|
|
131
|
+
# reserved for genuinely unexpected errors. OSError covers the
|
|
132
|
+
# filesystem backstops (FileNotFoundError, IsADirectoryError,
|
|
133
|
+
# PermissionError) for anything the preflights above did not catch.
|
|
134
|
+
print(f"\nIRMA failed: {exc}", file=sys.stderr)
|
|
135
|
+
return 3
|
|
136
|
+
return 0
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def main(argv=None):
|
|
140
|
+
"""Console entry point: dispatch to help, GUI, spectra, ncrystal, or a
|
|
141
|
+
deck evaluation depending on the arguments."""
|
|
142
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
143
|
+
|
|
144
|
+
if not argv:
|
|
145
|
+
_print_help()
|
|
146
|
+
return 0
|
|
147
|
+
if argv[0] == "--version":
|
|
148
|
+
from irma import __version__
|
|
149
|
+
print(f"IRMA v{__version__}")
|
|
150
|
+
return 0
|
|
151
|
+
if argv[0] in ("--help", "-h"):
|
|
152
|
+
_print_help()
|
|
153
|
+
return 0
|
|
154
|
+
if argv[0] == "--gui":
|
|
155
|
+
return _launch_gui()
|
|
156
|
+
if argv[0] == "spectra":
|
|
157
|
+
from irma.spectra.cli import main as spectra_main
|
|
158
|
+
return spectra_main(argv[1:])
|
|
159
|
+
if argv[0] == "mlip" and (
|
|
160
|
+
len(argv) == 1
|
|
161
|
+
or argv[1] in ("build", "emit", "validate", "env", "-h",
|
|
162
|
+
"--help")
|
|
163
|
+
or not os.path.exists("mlip")):
|
|
164
|
+
from irma.mlip.cli import main as mlip_main
|
|
165
|
+
return mlip_main(argv[1:])
|
|
166
|
+
if argv[0] == "ncrystal":
|
|
167
|
+
# Same code path as `python -m irma.ncrystal` (which keeps working);
|
|
168
|
+
# surfaced here so all three capabilities live behind one command.
|
|
169
|
+
from irma.ncrystal.__main__ import main as ncrystal_main
|
|
170
|
+
return ncrystal_main(argv[1:])
|
|
171
|
+
if argv[0] in ("evaluate", "run"):
|
|
172
|
+
return _run_deck(argv[1:])
|
|
173
|
+
|
|
174
|
+
# Fall-through: argv[0] is not a known subcommand. Before treating it as a
|
|
175
|
+
# legacy deck path, catch an obvious mistyped subcommand or unknown flag so
|
|
176
|
+
# the user gets a clear hint instead of a misleading "input file not found".
|
|
177
|
+
tok = argv[0]
|
|
178
|
+
if not os.path.exists(tok):
|
|
179
|
+
if tok.startswith("-"):
|
|
180
|
+
print(f"Error: unknown option {tok!r}", file=sys.stderr)
|
|
181
|
+
_print_help()
|
|
182
|
+
return 1
|
|
183
|
+
# only second-guess COMMAND-LIKE tokens (a bare word); a path-like token
|
|
184
|
+
# with a "." or "/" is a (missing) deck path -> let _run_deck report that.
|
|
185
|
+
if "." not in tok and "/" not in tok and os.sep not in tok:
|
|
186
|
+
import difflib
|
|
187
|
+
known = ["evaluate", "run", "spectra", "ncrystal", "mlip",
|
|
188
|
+
"--gui", "--version", "--help"]
|
|
189
|
+
close = difflib.get_close_matches(tok, known, n=1, cutoff=0.7)
|
|
190
|
+
if close:
|
|
191
|
+
print(f"Error: unknown command {tok!r}. Did you mean {close[0]!r}?",
|
|
192
|
+
file=sys.stderr)
|
|
193
|
+
return 1
|
|
194
|
+
return _run_deck(argv)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
if __name__ == "__main__":
|
|
198
|
+
sys.exit(main())
|
irma/core/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Core computation engine for IRMA."""
|
irma/core/constants.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Physical constants and element data shared across IRMA core modules.
|
|
2
|
+
|
|
3
|
+
The numeric values match NJOY2016's physics module exactly (bit-compatible
|
|
4
|
+
LEAPR reproduction depends on it).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# ============================================================================
|
|
8
|
+
# Physical constants (matching NJOY2016 physics module exactly)
|
|
9
|
+
# ============================================================================
|
|
10
|
+
BK = 8.617333262e-5 # Boltzmann constant, eV/K
|
|
11
|
+
EV = 1.602176634e-12 # erg/eV
|
|
12
|
+
CLIGHT = 2.99792458e10 # cm/s
|
|
13
|
+
AMU = 931.49410242e6 * EV / (CLIGHT * CLIGHT) # g/amu
|
|
14
|
+
HBAR = 6.582119569e-16 * EV # Planck/2pi, erg*s
|
|
15
|
+
AMASSN = 1.00866491595 # neutron mass in amu
|
|
16
|
+
|
|
17
|
+
THERM = 0.0253 # thermal energy in eV (for lat=1 scaling)
|
|
18
|
+
|
|
19
|
+
# WL2EKIN = h²/(2 m_n) [eV·Å²] (de Broglie E = h²/(2 m_n λ²); CODATA 2018, matches NCrystal wl2ekin from NCDefs.hh)
|
|
20
|
+
WL2EKIN: float = 0.081804209605330899
|
|
21
|
+
|
|
22
|
+
# ============================================================================
|
|
23
|
+
# CODATA 2018 / SI 2019 constants (phonopy-backed noncubic paths)
|
|
24
|
+
# ============================================================================
|
|
25
|
+
# References:
|
|
26
|
+
# E. Tiesinga, P. J. Mohr, D. B. Newell, and B. N. Taylor, "CODATA
|
|
27
|
+
# recommended values of the fundamental physical constants: 2018",
|
|
28
|
+
# Rev. Mod. Phys. 93, 025010 (2021); https://physics.nist.gov/constants
|
|
29
|
+
# h, e, and k_B are exact by the 2019 SI redefinition (BIPM, 9th SI
|
|
30
|
+
# brochure, 2019); BK above is that exact k_B/e value to 10 significant
|
|
31
|
+
# figures and is shared by the classic (NJOY-reproducing) path.
|
|
32
|
+
# Derived values are computed from the defining constants rather than
|
|
33
|
+
# hardcoded, so they cannot drift out of sync.
|
|
34
|
+
from math import pi as _pi
|
|
35
|
+
|
|
36
|
+
PLANCK_J_S = 6.62607015e-34 # h [J s] (exact, SI 2019)
|
|
37
|
+
ECHARGE_C = 1.602176634e-19 # e [C] (exact, SI 2019)
|
|
38
|
+
KB_J_PER_K = 1.380649e-23 # k_B [J/K] (exact, SI 2019)
|
|
39
|
+
HBAR_J_S = PLANCK_J_S / (2.0 * _pi) # ħ [J s]
|
|
40
|
+
HBAR_EV_S = HBAR_J_S / ECHARGE_C # ħ [eV s] = 6.582119569...e-16
|
|
41
|
+
NEUTRON_MASS_KG = 1.67492749804e-27 # m_n [kg] (CODATA 2018)
|
|
42
|
+
AMU_KG = 1.66053906660e-27 # m_u [kg] (CODATA 2018)
|
|
43
|
+
|
|
44
|
+
# 1 THz in eV: h * 1e12 / e = 4.135667696...e-3 eV
|
|
45
|
+
THZ_TO_EV = PLANCK_J_S * 1.0e12 / ECHARGE_C
|
|
46
|
+
|
|
47
|
+
# ħ²/(2 m_n) in meV·Å² = 2.0721248551... (the free-neutron kinematic
|
|
48
|
+
# constant linking alpha to Q²: E[meV] = HBAR2_OVER_2MN_MEV_A2 · Q[1/Å]²)
|
|
49
|
+
HBAR2_OVER_2MN_MEV_A2 = (
|
|
50
|
+
HBAR_J_S ** 2 / (2.0 * NEUTRON_MASS_KG) / (ECHARGE_C * 1.0e-3) * 1.0e20
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# ============================================================================
|
|
54
|
+
# Phonon-mode floor policy (mesh-sum numerical guards, modes 1/2)
|
|
55
|
+
# ============================================================================
|
|
56
|
+
# Per-mode one-phonon amplitudes and thermal displacements diverge as
|
|
57
|
+
# kT/ω² for ω → 0. In the exact BZ integral the acoustic divergence is
|
|
58
|
+
# integrable, but on a DISCRETE mesh the three Goldstone modes at Γ carry
|
|
59
|
+
# finite weight with a numerically meaningless ω (acoustic-sum-rule
|
|
60
|
+
# noise, commonly up to ~0.05 meV), so they must be excluded outright.
|
|
61
|
+
# Away from Γ every mode is physical and only an overflow guard is
|
|
62
|
+
# needed. Hence a two-tier rule:
|
|
63
|
+
# - at Γ q-points: drop modes below GAMMA_ACOUSTIC_FLOOR_MEV
|
|
64
|
+
# (optical Γ modes pass; Goldstone noise cannot);
|
|
65
|
+
# - elsewhere: drop modes below MODE_ENERGY_FLOOR_MEV (1 μeV), far
|
|
66
|
+
# under any acoustic energy at a nonzero mesh q-point, so genuinely
|
|
67
|
+
# ultra-soft physics is kept.
|
|
68
|
+
# Shifted Monkhorst-Pack production meshes contain no Γ point and no
|
|
69
|
+
# sub-μeV modes, so both guards are inert there.
|
|
70
|
+
MODE_ENERGY_FLOOR_MEV = 1.0e-3
|
|
71
|
+
GAMMA_ACOUSTIC_FLOOR_MEV = 0.1
|
|
72
|
+
|
|
73
|
+
_Z_TO_SYMBOL = {
|
|
74
|
+
1: 'H', 2: 'He', 3: 'Li', 4: 'Be', 5: 'B', 6: 'C', 7: 'N', 8: 'O',
|
|
75
|
+
9: 'F', 10: 'Ne',11: 'Na',12: 'Mg',13: 'Al',14: 'Si',15: 'P', 16: 'S',
|
|
76
|
+
17: 'Cl',18: 'Ar',19: 'K', 20: 'Ca',21: 'Sc',22: 'Ti',23: 'V', 24: 'Cr',
|
|
77
|
+
25: 'Mn',26: 'Fe',27: 'Co',28: 'Ni',29: 'Cu',30: 'Zn',31: 'Ga',32: 'Ge',
|
|
78
|
+
33: 'As',34: 'Se',35: 'Br',36: 'Kr',37: 'Rb',38: 'Sr',39: 'Y', 40: 'Zr',
|
|
79
|
+
41: 'Nb',42: 'Mo',43: 'Tc',44: 'Ru',45: 'Rh',46: 'Pd',47: 'Ag',48: 'Cd',
|
|
80
|
+
49: 'In',50: 'Sn',51: 'Sb',52: 'Te',53: 'I', 54: 'Xe',55: 'Cs',56: 'Ba',
|
|
81
|
+
57: 'La',58: 'Ce',59: 'Pr',60: 'Nd',61: 'Pm',62: 'Sm',63: 'Eu',64: 'Gd',
|
|
82
|
+
65: 'Tb',66: 'Dy',67: 'Ho',68: 'Er',69: 'Tm',70: 'Yb',71: 'Lu',72: 'Hf',
|
|
83
|
+
73: 'Ta',74: 'W', 75: 'Re',76: 'Os',77: 'Ir',78: 'Pt',79: 'Au',80: 'Hg',
|
|
84
|
+
81: 'Tl',82: 'Pb',83: 'Bi',84: 'Po',85: 'At',86: 'Rn',87: 'Fr',88: 'Ra',
|
|
85
|
+
89: 'Ac',90: 'Th',91: 'Pa',92: 'U', 93: 'Np',94: 'Pu',95: 'Am',96: 'Cm',
|
|
86
|
+
97: 'Bk',98: 'Cf',99: 'Es',100:'Fm',101:'Md',102:'No',103:'Lr',
|
|
87
|
+
}
|
|
88
|
+
|