pymdkit 1.7.3__tar.gz → 1.7.4__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.
- {pymdkit-1.7.3 → pymdkit-1.7.4}/CHANGELOG.md +9 -0
- {pymdkit-1.7.3/src/pymdkit.egg-info → pymdkit-1.7.4}/PKG-INFO +42 -21
- {pymdkit-1.7.3 → pymdkit-1.7.4}/README.md +41 -20
- {pymdkit-1.7.3 → pymdkit-1.7.4}/pyproject.toml +1 -1
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/__init__.py +1 -1
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/msd.py +28 -13
- {pymdkit-1.7.3 → pymdkit-1.7.4/src/pymdkit.egg-info}/PKG-INFO +42 -21
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_msd_large.py +1 -1
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_msd_phase3.py +132 -6
- {pymdkit-1.7.3 → pymdkit-1.7.4}/LICENSE +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/MANIFEST.in +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/setup.cfg +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/_config.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/_logging.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/__init__.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_cifio.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_extxyz.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_fileio.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_geometry.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_gpumd.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_gpumd_final_structure.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_gpumd_msd.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_gpumd_run.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_gpumd_status.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_gpumd_thermo.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_lattice.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_auto.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_conductivity.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_coordinates.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_fit.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_io.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_provenance.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_range.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_thermo.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_msd_uncertainty.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_oxidation.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_parallel.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_precision.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_pred_msd_plan.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_pred_msd_reference.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_reduction.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_submit.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_submit_vasp.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_supercell.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_symmetry.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_vasp_ehull.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_vasp_final_structure.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_vasp_nep.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_vasp_output.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/_vaspset.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/chemsys_entry.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/compute_rmsd.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/config.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/convert.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/dope.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/ewald.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/gpumd_job.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/gpumd_out.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/nep_rmse.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/perturb.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/pred_msd_time.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/rd.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/realloc_occ.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/submit.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/supercell.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/trjconv.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/vasp_job.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/commands/vasp_out.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit/pymdkit_main.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit.egg-info/SOURCES.txt +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit.egg-info/dependency_links.txt +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit.egg-info/entry_points.txt +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit.egg-info/requires.txt +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/src/pymdkit.egg-info/top_level.txt +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/data/88-Li2YCl3-63-d1.cif +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_config_logging.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_convert.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_ehull.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_gpumd_out.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_lattice_symmetry.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_msd_auto.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_msd_fit.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_msd_range.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_msd_uncertainty.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_parallel.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_pred_msd_time.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_progress.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_realloc_occ.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_rmsd_gpu.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_rmsd_max.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_rmsd_two_folder.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_submit.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_v165.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_v170_job_infrastructure.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_v170_vasp_consolidation.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_v172_vasp_writing.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_vasp_out.py +0 -0
- {pymdkit-1.7.3 → pymdkit-1.7.4}/tests/test_vaspset.py +0 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.7.4
|
|
4
|
+
|
|
5
|
+
- `pmk msd -ne` retains finite, positive Einstein diffusion coefficients and
|
|
6
|
+
Nernst–Einstein conductivity when the fit-quality diagnostics would reject the
|
|
7
|
+
window. The result is reported and aggregated as WARNING, with the original
|
|
8
|
+
diagnostic details preserved. This applies to automatic and manual windows.
|
|
9
|
+
- Missing valid regression, failed requested estimators, and invalid NE inputs
|
|
10
|
+
still fail the calculation and preserve existing aggregate summaries.
|
|
11
|
+
|
|
3
12
|
## 1.7.3
|
|
4
13
|
|
|
5
14
|
- `pmk msd` analyzes fixed-cell GPUMD NVT trajectories, selects or accepts a
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymdkit
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.4
|
|
4
4
|
Summary: A Python command-line toolkit for high-throughput atomistic workflows in AI-driven materials design.
|
|
5
5
|
Author-email: Yueda Wang <ydwang0608@ustc.edu.cn>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -36,6 +36,15 @@ materials design. Install `pymdkit` once and call its tools from anywhere as
|
|
|
36
36
|
Commands expose explicit, documented CLI interfaces designed for reproducible shell,
|
|
37
37
|
HPC, and AI-agent workflows, and each underlying script is still runnable on its own.
|
|
38
38
|
|
|
39
|
+
## 1.7.4 release notes
|
|
40
|
+
|
|
41
|
+
- `pmk msd -ne` retains valid Einstein diffusion coefficients and conductivity
|
|
42
|
+
as WARNING when fit quality would otherwise be REJECT. This applies to
|
|
43
|
+
automatic and manual windows and preserves the individual diagnostic results.
|
|
44
|
+
- Missing valid regression, failed requested estimators, and invalid NE inputs
|
|
45
|
+
still cause calculation errors. Without `-ne`, fit-quality rejection still
|
|
46
|
+
withholds diffusion coefficients.
|
|
47
|
+
|
|
39
48
|
## 1.7.3 release notes
|
|
40
49
|
|
|
41
50
|
- `pmk msd` analyzes GPUMD trajectories from fixed-cell NVT production. OLS is
|
|
@@ -97,7 +106,7 @@ installed.
|
|
|
97
106
|
On a machine with Internet access, run:
|
|
98
107
|
|
|
99
108
|
```bash
|
|
100
|
-
python -m pip download pymdkit==1.7.
|
|
109
|
+
python -m pip download pymdkit==1.7.4 -d pymdkit-offline
|
|
101
110
|
```
|
|
102
111
|
|
|
103
112
|
This downloads `pymdkit` and the dependencies resolved by pip into the
|
|
@@ -105,14 +114,14 @@ This downloads `pymdkit` and the dependencies resolved by pip into the
|
|
|
105
114
|
system. Then, inside the target pymdkit Python or conda environment, run:
|
|
106
115
|
|
|
107
116
|
```bash
|
|
108
|
-
python -m pip install --no-index --find-links=pymdkit-offline pymdkit==1.7.
|
|
117
|
+
python -m pip install --no-index --find-links=pymdkit-offline pymdkit==1.7.4
|
|
109
118
|
```
|
|
110
119
|
|
|
111
120
|
For optional GPU use, prepare the bundle on a networked machine compatible with
|
|
112
121
|
the target HPC platform and Python environment:
|
|
113
122
|
|
|
114
123
|
```bash
|
|
115
|
-
python -m pip download "pymdkit[gpu-cuda12]==1.7.
|
|
124
|
+
python -m pip download "pymdkit[gpu-cuda12]==1.7.4" -d pymdkit-offline
|
|
116
125
|
```
|
|
117
126
|
|
|
118
127
|
Transfer the whole `pymdkit-offline` directory, then install from it on the
|
|
@@ -120,7 +129,7 @@ target system:
|
|
|
120
129
|
|
|
121
130
|
```bash
|
|
122
131
|
python -m pip install --no-index --find-links=pymdkit-offline \
|
|
123
|
-
"pymdkit[gpu-cuda12]==1.7.
|
|
132
|
+
"pymdkit[gpu-cuda12]==1.7.4"
|
|
124
133
|
```
|
|
125
134
|
|
|
126
135
|
This GPU bundle includes the optional `cupy-cuda12x` package. Its wheels must also
|
|
@@ -877,22 +886,33 @@ diffusion in cm²/s, `%.2e`) and, only with `-ne`, `msd/sigma.txt` (x/y/z/total
|
|
|
877
886
|
Nernst–Einstein conductivity in mS/cm, `%.2f`). Headers are comments. Total
|
|
878
887
|
diffusion and conductivity are directional arithmetic means before rounding;
|
|
879
888
|
display rounding may cause last-digit differences. PASS and WARNING write these
|
|
880
|
-
coefficients with status in their comment headers.
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
889
|
+
coefficients with the quality status in their comment headers. `provenance.json`
|
|
890
|
+
records the final quality and full-precision total coefficients.
|
|
891
|
+
|
|
892
|
+
With `-ne`, an automatic or manual window with finite, positive x/y/z/total
|
|
893
|
+
Einstein diffusion coefficients retains D and NE conductivity even when the fit
|
|
894
|
+
quality would otherwise be REJECT. The overall status becomes WARNING. The
|
|
895
|
+
original reason and the beta, intercept-consistency, and three-segment slope
|
|
896
|
+
diagnostics remain in the fit output. A WARNING can indicate that the physical
|
|
897
|
+
diffusion regime failed quality criteria. Without `-ne`, REJECT returns nonzero
|
|
898
|
+
and retains only raw MSD and fit diagnostics. PASS and WARNING retain D.
|
|
899
|
+
|
|
900
|
+
`fit-windows.txt` records evaluated candidates, rejection reasons, beta,
|
|
901
|
+
directional D, R², intercept, RSS, points, upper-lag origin count, intercept
|
|
902
|
+
diagnostics, and selected-window segment D values. `fit-diagnostic.txt` records
|
|
903
|
+
settings, selection, and sensitivity. Beta-qualified window sensitivity,
|
|
904
|
+
all-valid-candidate sensitivity, and three-segment slope spread are reported
|
|
905
|
+
separately. Segments and MSD time origins are correlated. Window-to-window spread
|
|
906
|
+
measures sensitivity. Formal statistical uncertainty requires an explicit
|
|
907
|
+
request. Move an existing `msd` directory aside before repeating a calculation.
|
|
908
|
+
Temporary coordinate storage is removed when the calculation ends.
|
|
890
909
|
|
|
891
910
|
The five-column raw MSD is published immediately after FFT succeeds, before
|
|
892
911
|
window search, optional uncertainty, or thermodynamic/NE processing. Later errors
|
|
893
|
-
retain this curve and any completed fit diagnostics
|
|
894
|
-
|
|
895
|
-
|
|
912
|
+
retain this curve and any completed fit diagnostics. A missing valid regression,
|
|
913
|
+
a failed requested WLS/GLS estimator, or invalid required thermodynamic inputs
|
|
914
|
+
returns nonzero and prevents publication of new coefficients and aggregates.
|
|
915
|
+
Parsing and FFT failures occur before the raw curve is published.
|
|
896
916
|
FFT uses float64 atom chunks sized from an internal 128 MiB workspace budget,
|
|
897
917
|
with bounded remapping of time slabs to avoid retaining the entire coordinate
|
|
898
918
|
memmap in RAM. For 100000 frames and 784 selected atoms the temporary coordinate
|
|
@@ -962,9 +982,10 @@ missing NVT targets produce an explicit error). Genuine calculation failures ret
|
|
|
962
982
|
nonzero and preserve previous summaries; skipped templates are not failures.
|
|
963
983
|
A successful run without `-ne` removes any stale root `sigma-total.txt`.
|
|
964
984
|
The companion `msd-quality-total.txt` retains each directory's PASS/WARNING
|
|
965
|
-
status
|
|
966
|
-
|
|
967
|
-
|
|
985
|
+
status. In NE mode, a directory whose valid coefficients were retained despite
|
|
986
|
+
fit-quality rejection appears as WARNING and contributes to both numeric
|
|
987
|
+
summaries. Without `-ne`, a directory with overall REJECT prevents publication
|
|
988
|
+
of new aggregates.
|
|
968
989
|
See [the automatic-fit report](devtools/MSD_AUTO_FIT_REPORT.md) for validation,
|
|
969
990
|
and [the intercept/stability update](devtools/MSD_AUTO_FIT_PHASE4A1_REPORT.md)
|
|
970
991
|
for the current policy, 500 K MSD comparison and limitations.
|
|
@@ -7,6 +7,15 @@ materials design. Install `pymdkit` once and call its tools from anywhere as
|
|
|
7
7
|
Commands expose explicit, documented CLI interfaces designed for reproducible shell,
|
|
8
8
|
HPC, and AI-agent workflows, and each underlying script is still runnable on its own.
|
|
9
9
|
|
|
10
|
+
## 1.7.4 release notes
|
|
11
|
+
|
|
12
|
+
- `pmk msd -ne` retains valid Einstein diffusion coefficients and conductivity
|
|
13
|
+
as WARNING when fit quality would otherwise be REJECT. This applies to
|
|
14
|
+
automatic and manual windows and preserves the individual diagnostic results.
|
|
15
|
+
- Missing valid regression, failed requested estimators, and invalid NE inputs
|
|
16
|
+
still cause calculation errors. Without `-ne`, fit-quality rejection still
|
|
17
|
+
withholds diffusion coefficients.
|
|
18
|
+
|
|
10
19
|
## 1.7.3 release notes
|
|
11
20
|
|
|
12
21
|
- `pmk msd` analyzes GPUMD trajectories from fixed-cell NVT production. OLS is
|
|
@@ -68,7 +77,7 @@ installed.
|
|
|
68
77
|
On a machine with Internet access, run:
|
|
69
78
|
|
|
70
79
|
```bash
|
|
71
|
-
python -m pip download pymdkit==1.7.
|
|
80
|
+
python -m pip download pymdkit==1.7.4 -d pymdkit-offline
|
|
72
81
|
```
|
|
73
82
|
|
|
74
83
|
This downloads `pymdkit` and the dependencies resolved by pip into the
|
|
@@ -76,14 +85,14 @@ This downloads `pymdkit` and the dependencies resolved by pip into the
|
|
|
76
85
|
system. Then, inside the target pymdkit Python or conda environment, run:
|
|
77
86
|
|
|
78
87
|
```bash
|
|
79
|
-
python -m pip install --no-index --find-links=pymdkit-offline pymdkit==1.7.
|
|
88
|
+
python -m pip install --no-index --find-links=pymdkit-offline pymdkit==1.7.4
|
|
80
89
|
```
|
|
81
90
|
|
|
82
91
|
For optional GPU use, prepare the bundle on a networked machine compatible with
|
|
83
92
|
the target HPC platform and Python environment:
|
|
84
93
|
|
|
85
94
|
```bash
|
|
86
|
-
python -m pip download "pymdkit[gpu-cuda12]==1.7.
|
|
95
|
+
python -m pip download "pymdkit[gpu-cuda12]==1.7.4" -d pymdkit-offline
|
|
87
96
|
```
|
|
88
97
|
|
|
89
98
|
Transfer the whole `pymdkit-offline` directory, then install from it on the
|
|
@@ -91,7 +100,7 @@ target system:
|
|
|
91
100
|
|
|
92
101
|
```bash
|
|
93
102
|
python -m pip install --no-index --find-links=pymdkit-offline \
|
|
94
|
-
"pymdkit[gpu-cuda12]==1.7.
|
|
103
|
+
"pymdkit[gpu-cuda12]==1.7.4"
|
|
95
104
|
```
|
|
96
105
|
|
|
97
106
|
This GPU bundle includes the optional `cupy-cuda12x` package. Its wheels must also
|
|
@@ -848,22 +857,33 @@ diffusion in cm²/s, `%.2e`) and, only with `-ne`, `msd/sigma.txt` (x/y/z/total
|
|
|
848
857
|
Nernst–Einstein conductivity in mS/cm, `%.2f`). Headers are comments. Total
|
|
849
858
|
diffusion and conductivity are directional arithmetic means before rounding;
|
|
850
859
|
display rounding may cause last-digit differences. PASS and WARNING write these
|
|
851
|
-
coefficients with status in their comment headers.
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
860
|
+
coefficients with the quality status in their comment headers. `provenance.json`
|
|
861
|
+
records the final quality and full-precision total coefficients.
|
|
862
|
+
|
|
863
|
+
With `-ne`, an automatic or manual window with finite, positive x/y/z/total
|
|
864
|
+
Einstein diffusion coefficients retains D and NE conductivity even when the fit
|
|
865
|
+
quality would otherwise be REJECT. The overall status becomes WARNING. The
|
|
866
|
+
original reason and the beta, intercept-consistency, and three-segment slope
|
|
867
|
+
diagnostics remain in the fit output. A WARNING can indicate that the physical
|
|
868
|
+
diffusion regime failed quality criteria. Without `-ne`, REJECT returns nonzero
|
|
869
|
+
and retains only raw MSD and fit diagnostics. PASS and WARNING retain D.
|
|
870
|
+
|
|
871
|
+
`fit-windows.txt` records evaluated candidates, rejection reasons, beta,
|
|
872
|
+
directional D, R², intercept, RSS, points, upper-lag origin count, intercept
|
|
873
|
+
diagnostics, and selected-window segment D values. `fit-diagnostic.txt` records
|
|
874
|
+
settings, selection, and sensitivity. Beta-qualified window sensitivity,
|
|
875
|
+
all-valid-candidate sensitivity, and three-segment slope spread are reported
|
|
876
|
+
separately. Segments and MSD time origins are correlated. Window-to-window spread
|
|
877
|
+
measures sensitivity. Formal statistical uncertainty requires an explicit
|
|
878
|
+
request. Move an existing `msd` directory aside before repeating a calculation.
|
|
879
|
+
Temporary coordinate storage is removed when the calculation ends.
|
|
861
880
|
|
|
862
881
|
The five-column raw MSD is published immediately after FFT succeeds, before
|
|
863
882
|
window search, optional uncertainty, or thermodynamic/NE processing. Later errors
|
|
864
|
-
retain this curve and any completed fit diagnostics
|
|
865
|
-
|
|
866
|
-
|
|
883
|
+
retain this curve and any completed fit diagnostics. A missing valid regression,
|
|
884
|
+
a failed requested WLS/GLS estimator, or invalid required thermodynamic inputs
|
|
885
|
+
returns nonzero and prevents publication of new coefficients and aggregates.
|
|
886
|
+
Parsing and FFT failures occur before the raw curve is published.
|
|
867
887
|
FFT uses float64 atom chunks sized from an internal 128 MiB workspace budget,
|
|
868
888
|
with bounded remapping of time slabs to avoid retaining the entire coordinate
|
|
869
889
|
memmap in RAM. For 100000 frames and 784 selected atoms the temporary coordinate
|
|
@@ -933,9 +953,10 @@ missing NVT targets produce an explicit error). Genuine calculation failures ret
|
|
|
933
953
|
nonzero and preserve previous summaries; skipped templates are not failures.
|
|
934
954
|
A successful run without `-ne` removes any stale root `sigma-total.txt`.
|
|
935
955
|
The companion `msd-quality-total.txt` retains each directory's PASS/WARNING
|
|
936
|
-
status
|
|
937
|
-
|
|
938
|
-
|
|
956
|
+
status. In NE mode, a directory whose valid coefficients were retained despite
|
|
957
|
+
fit-quality rejection appears as WARNING and contributes to both numeric
|
|
958
|
+
summaries. Without `-ne`, a directory with overall REJECT prevents publication
|
|
959
|
+
of new aggregates.
|
|
939
960
|
See [the automatic-fit report](devtools/MSD_AUTO_FIT_REPORT.md) for validation,
|
|
940
961
|
and [the intercept/stability update](devtools/MSD_AUTO_FIT_PHASE4A1_REPORT.md)
|
|
941
962
|
for the current policy, 500 K MSD comparison and limitations.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "pymdkit"
|
|
7
|
-
version = "1.7.
|
|
7
|
+
version = "1.7.4"
|
|
8
8
|
description = "A Python command-line toolkit for high-throughput atomistic workflows in AI-driven materials design."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
@@ -34,12 +34,12 @@ def production_temperature(production):
|
|
|
34
34
|
|
|
35
35
|
|
|
36
36
|
def add_arguments(parser):
|
|
37
|
-
parser.epilog = ('Automatic fits use positive MSD lags below 0.7T
|
|
38
|
-
'and three-segment Einstein slopes diagnose the regime. '
|
|
39
|
-
'Einstein regression
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
37
|
+
parser.epilog = ('Automatic fits use positive MSD lags below 0.7T. Beta, intercept consistency '
|
|
38
|
+
'and three-segment Einstein slopes diagnose the diffusion regime. '
|
|
39
|
+
'Einstein regression calculates D. With -ne, valid Einstein coefficients and '
|
|
40
|
+
'conductivity are retained as WARNING if fit quality would otherwise be REJECT. '
|
|
41
|
+
'Without -ne, PASS/WARNING retain D and REJECT retains only raw MSD and fit diagnostics. '
|
|
42
|
+
'Missing valid regression or invalid NE thermodynamic inputs cause calculation errors. '
|
|
43
43
|
'Window spread is sensitivity, not a confidence interval. '
|
|
44
44
|
'Optional --uncertainty covariance reports conditional Bayesian uncertainty separately.')
|
|
45
45
|
parser.add_argument('--uncertainty', choices=['covariance'])
|
|
@@ -196,11 +196,15 @@ def analyze(folder, args):
|
|
|
196
196
|
(result/'diagnostics.json').write_text(json.dumps(dict(selection=segment_metadata, status='ERROR', reason=str(exc)), indent=2), encoding='utf-8')
|
|
197
197
|
raise ValueError(f'fitting-window search failed after MSD calculation; raw MSD retained: {exc}') from exc
|
|
198
198
|
selected = diagnostic['selected']
|
|
199
|
-
|
|
199
|
+
diffusion = None
|
|
200
|
+
fit_error = None
|
|
201
|
+
if selected is not None and (args.ne or diagnostic['status'] != 'REJECT'):
|
|
200
202
|
interval = (selected['start'], selected['end'])
|
|
201
203
|
beta = selected['beta']
|
|
202
|
-
diffusion = np.array([selected
|
|
203
|
-
if args.
|
|
204
|
+
diffusion = np.array([selected.get('D_'+k, np.nan) for k in ('x', 'y', 'z', 'total')], dtype=float)
|
|
205
|
+
if args.ne and (not np.isfinite(diffusion).all() or np.any(diffusion <= 0)):
|
|
206
|
+
diffusion = None
|
|
207
|
+
if diffusion is not None and args.fit_method != 'ols':
|
|
204
208
|
try:
|
|
205
209
|
fit = fit_directions(mapped, dt, interval, args.fit_method)
|
|
206
210
|
diffusion = np.array([r['diffusion'] for r in fit['axes'].values()] + [fit['diffusion']])*1e-4
|
|
@@ -210,9 +214,19 @@ def analyze(folder, args):
|
|
|
210
214
|
diagnostic['method_intercepts_A2'] = {
|
|
211
215
|
axis: estimate['intercept'] for axis, estimate in fit['axes'].items()
|
|
212
216
|
}
|
|
213
|
-
except (ValueError, RuntimeError, ImportError) as exc:
|
|
214
|
-
|
|
215
|
-
|
|
217
|
+
except (ValueError, RuntimeError, ImportError, np.linalg.LinAlgError) as exc:
|
|
218
|
+
fit_error = f'{args.fit_method.upper()} Einstein regression failed: {exc}' if args.ne else str(exc)
|
|
219
|
+
if args.ne and diffusion is None:
|
|
220
|
+
fit_error = f"No valid Einstein regression available. {diagnostic['reason']}"
|
|
221
|
+
if fit_error is not None:
|
|
222
|
+
diagnostic.update(status='REJECT', overall_status='REJECT', reason=fit_error)
|
|
223
|
+
if selected is not None:
|
|
224
|
+
selected['overall_status'] = 'REJECT'
|
|
225
|
+
elif args.ne and diagnostic['status'] == 'REJECT':
|
|
226
|
+
diagnostic.update(status='WARNING', overall_status='WARNING', reason=(
|
|
227
|
+
'NE mode retained valid Einstein coefficients despite the original fit-quality REJECT. '
|
|
228
|
+
+ diagnostic['reason']))
|
|
229
|
+
selected['overall_status'] = 'WARNING'
|
|
216
230
|
write_diagnostics(result, diagnostic, duration, dt, len(times), len(indices), args.fit_method, mode,
|
|
217
231
|
getattr(args, 'mobile_site_distance', None))
|
|
218
232
|
if selection is not None:
|
|
@@ -223,7 +237,8 @@ def analyze(folder, args):
|
|
|
223
237
|
if diagnostic['status'] == 'REJECT':
|
|
224
238
|
print(' No self-diffusion or NE conductivity output generated.')
|
|
225
239
|
saved = 'msd/' if selection is None else str(result)
|
|
226
|
-
|
|
240
|
+
failure = 'MSD calculation failed' if args.ne else 'REJECT'
|
|
241
|
+
raise ValueError(f"{failure}: {diagnostic['reason']}; MSD and diagnostics saved in {saved}")
|
|
227
242
|
uncertainty = None
|
|
228
243
|
if getattr(args, 'uncertainty', None):
|
|
229
244
|
from ._msd_uncertainty import estimate, write_outputs
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pymdkit
|
|
3
|
-
Version: 1.7.
|
|
3
|
+
Version: 1.7.4
|
|
4
4
|
Summary: A Python command-line toolkit for high-throughput atomistic workflows in AI-driven materials design.
|
|
5
5
|
Author-email: Yueda Wang <ydwang0608@ustc.edu.cn>
|
|
6
6
|
License-Expression: GPL-3.0-or-later
|
|
@@ -36,6 +36,15 @@ materials design. Install `pymdkit` once and call its tools from anywhere as
|
|
|
36
36
|
Commands expose explicit, documented CLI interfaces designed for reproducible shell,
|
|
37
37
|
HPC, and AI-agent workflows, and each underlying script is still runnable on its own.
|
|
38
38
|
|
|
39
|
+
## 1.7.4 release notes
|
|
40
|
+
|
|
41
|
+
- `pmk msd -ne` retains valid Einstein diffusion coefficients and conductivity
|
|
42
|
+
as WARNING when fit quality would otherwise be REJECT. This applies to
|
|
43
|
+
automatic and manual windows and preserves the individual diagnostic results.
|
|
44
|
+
- Missing valid regression, failed requested estimators, and invalid NE inputs
|
|
45
|
+
still cause calculation errors. Without `-ne`, fit-quality rejection still
|
|
46
|
+
withholds diffusion coefficients.
|
|
47
|
+
|
|
39
48
|
## 1.7.3 release notes
|
|
40
49
|
|
|
41
50
|
- `pmk msd` analyzes GPUMD trajectories from fixed-cell NVT production. OLS is
|
|
@@ -97,7 +106,7 @@ installed.
|
|
|
97
106
|
On a machine with Internet access, run:
|
|
98
107
|
|
|
99
108
|
```bash
|
|
100
|
-
python -m pip download pymdkit==1.7.
|
|
109
|
+
python -m pip download pymdkit==1.7.4 -d pymdkit-offline
|
|
101
110
|
```
|
|
102
111
|
|
|
103
112
|
This downloads `pymdkit` and the dependencies resolved by pip into the
|
|
@@ -105,14 +114,14 @@ This downloads `pymdkit` and the dependencies resolved by pip into the
|
|
|
105
114
|
system. Then, inside the target pymdkit Python or conda environment, run:
|
|
106
115
|
|
|
107
116
|
```bash
|
|
108
|
-
python -m pip install --no-index --find-links=pymdkit-offline pymdkit==1.7.
|
|
117
|
+
python -m pip install --no-index --find-links=pymdkit-offline pymdkit==1.7.4
|
|
109
118
|
```
|
|
110
119
|
|
|
111
120
|
For optional GPU use, prepare the bundle on a networked machine compatible with
|
|
112
121
|
the target HPC platform and Python environment:
|
|
113
122
|
|
|
114
123
|
```bash
|
|
115
|
-
python -m pip download "pymdkit[gpu-cuda12]==1.7.
|
|
124
|
+
python -m pip download "pymdkit[gpu-cuda12]==1.7.4" -d pymdkit-offline
|
|
116
125
|
```
|
|
117
126
|
|
|
118
127
|
Transfer the whole `pymdkit-offline` directory, then install from it on the
|
|
@@ -120,7 +129,7 @@ target system:
|
|
|
120
129
|
|
|
121
130
|
```bash
|
|
122
131
|
python -m pip install --no-index --find-links=pymdkit-offline \
|
|
123
|
-
"pymdkit[gpu-cuda12]==1.7.
|
|
132
|
+
"pymdkit[gpu-cuda12]==1.7.4"
|
|
124
133
|
```
|
|
125
134
|
|
|
126
135
|
This GPU bundle includes the optional `cupy-cuda12x` package. Its wheels must also
|
|
@@ -877,22 +886,33 @@ diffusion in cm²/s, `%.2e`) and, only with `-ne`, `msd/sigma.txt` (x/y/z/total
|
|
|
877
886
|
Nernst–Einstein conductivity in mS/cm, `%.2f`). Headers are comments. Total
|
|
878
887
|
diffusion and conductivity are directional arithmetic means before rounding;
|
|
879
888
|
display rounding may cause last-digit differences. PASS and WARNING write these
|
|
880
|
-
coefficients with status in their comment headers.
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
889
|
+
coefficients with the quality status in their comment headers. `provenance.json`
|
|
890
|
+
records the final quality and full-precision total coefficients.
|
|
891
|
+
|
|
892
|
+
With `-ne`, an automatic or manual window with finite, positive x/y/z/total
|
|
893
|
+
Einstein diffusion coefficients retains D and NE conductivity even when the fit
|
|
894
|
+
quality would otherwise be REJECT. The overall status becomes WARNING. The
|
|
895
|
+
original reason and the beta, intercept-consistency, and three-segment slope
|
|
896
|
+
diagnostics remain in the fit output. A WARNING can indicate that the physical
|
|
897
|
+
diffusion regime failed quality criteria. Without `-ne`, REJECT returns nonzero
|
|
898
|
+
and retains only raw MSD and fit diagnostics. PASS and WARNING retain D.
|
|
899
|
+
|
|
900
|
+
`fit-windows.txt` records evaluated candidates, rejection reasons, beta,
|
|
901
|
+
directional D, R², intercept, RSS, points, upper-lag origin count, intercept
|
|
902
|
+
diagnostics, and selected-window segment D values. `fit-diagnostic.txt` records
|
|
903
|
+
settings, selection, and sensitivity. Beta-qualified window sensitivity,
|
|
904
|
+
all-valid-candidate sensitivity, and three-segment slope spread are reported
|
|
905
|
+
separately. Segments and MSD time origins are correlated. Window-to-window spread
|
|
906
|
+
measures sensitivity. Formal statistical uncertainty requires an explicit
|
|
907
|
+
request. Move an existing `msd` directory aside before repeating a calculation.
|
|
908
|
+
Temporary coordinate storage is removed when the calculation ends.
|
|
890
909
|
|
|
891
910
|
The five-column raw MSD is published immediately after FFT succeeds, before
|
|
892
911
|
window search, optional uncertainty, or thermodynamic/NE processing. Later errors
|
|
893
|
-
retain this curve and any completed fit diagnostics
|
|
894
|
-
|
|
895
|
-
|
|
912
|
+
retain this curve and any completed fit diagnostics. A missing valid regression,
|
|
913
|
+
a failed requested WLS/GLS estimator, or invalid required thermodynamic inputs
|
|
914
|
+
returns nonzero and prevents publication of new coefficients and aggregates.
|
|
915
|
+
Parsing and FFT failures occur before the raw curve is published.
|
|
896
916
|
FFT uses float64 atom chunks sized from an internal 128 MiB workspace budget,
|
|
897
917
|
with bounded remapping of time slabs to avoid retaining the entire coordinate
|
|
898
918
|
memmap in RAM. For 100000 frames and 784 selected atoms the temporary coordinate
|
|
@@ -962,9 +982,10 @@ missing NVT targets produce an explicit error). Genuine calculation failures ret
|
|
|
962
982
|
nonzero and preserve previous summaries; skipped templates are not failures.
|
|
963
983
|
A successful run without `-ne` removes any stale root `sigma-total.txt`.
|
|
964
984
|
The companion `msd-quality-total.txt` retains each directory's PASS/WARNING
|
|
965
|
-
status
|
|
966
|
-
|
|
967
|
-
|
|
985
|
+
status. In NE mode, a directory whose valid coefficients were retained despite
|
|
986
|
+
fit-quality rejection appears as WARNING and contributes to both numeric
|
|
987
|
+
summaries. Without `-ne`, a directory with overall REJECT prevents publication
|
|
988
|
+
of new aggregates.
|
|
968
989
|
See [the automatic-fit report](devtools/MSD_AUTO_FIT_REPORT.md) for validation,
|
|
969
990
|
and [the intercept/stability update](devtools/MSD_AUTO_FIT_PHASE4A1_REPORT.md)
|
|
970
991
|
for the current policy, 500 K MSD comparison and limitations.
|
|
@@ -64,7 +64,7 @@ class LargeMSDTests(unittest.TestCase):
|
|
|
64
64
|
args.uncertainty = 'covariance'
|
|
65
65
|
args.min_fit_window = 1.
|
|
66
66
|
with patch('pymdkit.commands._msd_uncertainty.estimate', side_effect=RuntimeError('unavailable')) as estimate:
|
|
67
|
-
with self.assertRaisesRegex(ValueError, '
|
|
67
|
+
with self.assertRaisesRegex(ValueError, 'No valid Einstein regression'):
|
|
68
68
|
msd.analyze(self.root, args)
|
|
69
69
|
estimate.assert_not_called()
|
|
70
70
|
for name in ('msd.txt', 'fit-windows.txt', 'fit-diagnostic.txt'):
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
1
3
|
import os
|
|
2
4
|
from pathlib import Path
|
|
3
5
|
import subprocess
|
|
@@ -13,7 +15,8 @@ from pymdkit.commands._gpumd_run import parse_run, trajectory_schedule
|
|
|
13
15
|
from pymdkit.commands._msd_thermo import load_thermo
|
|
14
16
|
from pymdkit.commands._msd_conductivity import conductivity, ELEMENTARY_CHARGE, BOLTZMANN
|
|
15
17
|
from pymdkit.commands._msd_coordinates import Coordinates
|
|
16
|
-
from pymdkit.commands import msd
|
|
18
|
+
from pymdkit.commands import _msd_auto, _msd_fit, msd
|
|
19
|
+
from pymdkit.commands._pred_msd_plan import read_plan
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
HPC_RUN = '''time_step 1.0
|
|
@@ -258,24 +261,24 @@ class Phase3Tests(unittest.TestCase):
|
|
|
258
261
|
with self.assertRaisesRegex(ValueError, 'ordering'):
|
|
259
262
|
p.update(f)
|
|
260
263
|
|
|
261
|
-
def fixture(self, folder):
|
|
264
|
+
def fixture(self, folder, *, retention=1., step_scale=.05, temperature=300):
|
|
262
265
|
folder.mkdir(exist_ok=True)
|
|
263
266
|
n, count = 129, 64
|
|
264
267
|
model = Atoms('Li'*count+'O'*count, positions=np.zeros((count*2, 3)), cell=[100]*3, pbc=True)
|
|
265
268
|
model.arrays['group'] = np.r_[np.zeros(count, dtype=int), np.ones(count, dtype=int)]
|
|
266
269
|
write(folder/'model.xyz', model)
|
|
267
|
-
(folder/'run.in').write_text(f'time_step 1\nensemble nvt_nhc
|
|
270
|
+
(folder/'run.in').write_text(f'time_step 1\nensemble nvt_nhc {temperature} {temperature} 100\ndump_thermo 1\ndump_exyz 1 1 1\nrun {n}\n')
|
|
268
271
|
rng = np.random.default_rng(17)
|
|
269
272
|
raw = np.zeros((count*2, 3))
|
|
270
273
|
for i in range(n):
|
|
271
|
-
raw[:count]
|
|
274
|
+
raw[:count] = retention*raw[:count] + rng.normal(0, step_scale, (count, 3))
|
|
272
275
|
f = model.copy()
|
|
273
276
|
f.positions = raw % 100
|
|
274
277
|
f.arrays['unwrapped_position'] = raw.copy()
|
|
275
278
|
f.info['Time'] = i+1
|
|
276
279
|
write(folder/'dump.xyz', f, append=i>0)
|
|
277
280
|
data = np.zeros((n, 18))
|
|
278
|
-
data[:, 0] =
|
|
281
|
+
data[:, 0] = temperature
|
|
279
282
|
data[:, 9:] = np.tile(np.diag([100]*3).ravel(), (n, 1))
|
|
280
283
|
np.savetxt(folder/'thermo.out', data)
|
|
281
284
|
|
|
@@ -310,7 +313,7 @@ class Phase3Tests(unittest.TestCase):
|
|
|
310
313
|
args = type('Args', (), dict(group=0, ne=True, charge=1., fit_range=None,
|
|
311
314
|
fit_method='ols', min_fit_window=1.))()
|
|
312
315
|
with patch.object(_msd_fit, 'fft_msd', wraps=_msd_fit.fft_msd) as fft:
|
|
313
|
-
with self.assertRaisesRegex(ValueError, '
|
|
316
|
+
with self.assertRaisesRegex(ValueError, 'No valid Einstein regression'):
|
|
314
317
|
msd.analyze(self.root, args)
|
|
315
318
|
self.assertEqual(fft.call_count, 1)
|
|
316
319
|
for name in ('msd.txt', 'fit-windows.txt', 'fit-diagnostic.txt'):
|
|
@@ -347,6 +350,129 @@ class Phase3Tests(unittest.TestCase):
|
|
|
347
350
|
for name in ('self-diff.txt', 'sigma.txt'):
|
|
348
351
|
self.assertIn('quality=WARNING', (self.root/'msd'/name).read_text())
|
|
349
352
|
|
|
353
|
+
def test_quality_rejection_with_valid_regression(self):
|
|
354
|
+
t = np.arange(129)*.001
|
|
355
|
+
values = t[:, None]**.6*np.array([.5, .6, .7])
|
|
356
|
+
parser = argparse.ArgumentParser()
|
|
357
|
+
msd.add_arguments(parser)
|
|
358
|
+
for name, flags in (
|
|
359
|
+
('non-ne', []),
|
|
360
|
+
('auto-ne', ['-ne']),
|
|
361
|
+
('manual-ne', ['-ne', '-fit-range', '.01', '.075', '-trajectory-range', '0', '.000129']),
|
|
362
|
+
):
|
|
363
|
+
with self.subTest(mode=name):
|
|
364
|
+
folder = self.root/name
|
|
365
|
+
self.fixture(folder)
|
|
366
|
+
args = parser.parse_args(['-group', '0', *flags])
|
|
367
|
+
original = _msd_auto.search(t, values, .129, interval=args.fit_range)
|
|
368
|
+
self.assertEqual(original['status'], 'REJECT')
|
|
369
|
+
self.assertIsNotNone(original['selected'])
|
|
370
|
+
directions = ('x', 'y', 'z', 'total')
|
|
371
|
+
expected = np.array([original['selected']['D_'+axis] for axis in directions])
|
|
372
|
+
self.assertTrue(np.all(np.isfinite(expected) & (expected > 0)))
|
|
373
|
+
with patch.object(Path, 'cwd', return_value=self.root), patch.object(
|
|
374
|
+
_msd_fit, 'fft_msd', return_value=values):
|
|
375
|
+
if args.ne:
|
|
376
|
+
result = msd.analyze(folder, args)
|
|
377
|
+
else:
|
|
378
|
+
with self.assertRaisesRegex(ValueError, '^REJECT: Raw beta'):
|
|
379
|
+
msd.analyze(folder, args)
|
|
380
|
+
|
|
381
|
+
out = folder/'msd'
|
|
382
|
+
if args.trajectory_range:
|
|
383
|
+
out = out/'segments/0-0.000129ns'
|
|
384
|
+
fit = json.loads((out/'diagnostics.json').read_text())['fit']
|
|
385
|
+
self.assertEqual(fit['status'], 'WARNING')
|
|
386
|
+
self.assertEqual(fit['overall_status'], 'WARNING')
|
|
387
|
+
self.assertEqual(fit['selected']['overall_status'], 'WARNING')
|
|
388
|
+
self.assertEqual(fit['segments'], original['segments'])
|
|
389
|
+
self.assertTrue((out/'msd.txt').is_file())
|
|
390
|
+
windows = (out/'fit-windows.txt').read_text()
|
|
391
|
+
diagnostic = (out/'fit-diagnostic.txt').read_text()
|
|
392
|
+
for key in ('beta_status', 'intercept_status', 'einstein_slope_status'):
|
|
393
|
+
self.assertIn(f'\n{key}: {original[key]}\n', diagnostic)
|
|
394
|
+
self.assertIn(original['reason'], diagnostic)
|
|
395
|
+
if not args.ne:
|
|
396
|
+
self.assertIn('\nstatus: REJECT\n', diagnostic)
|
|
397
|
+
for output in ('self-diff.txt', 'sigma.txt', 'provenance.json'):
|
|
398
|
+
self.assertFalse((out/output).exists())
|
|
399
|
+
continue
|
|
400
|
+
self.assertEqual(result.quality, 'WARNING')
|
|
401
|
+
self.assertIn('\nstatus: WARNING\n', diagnostic)
|
|
402
|
+
self.assertIn('\noverall_status: WARNING\n', diagnostic)
|
|
403
|
+
self.assertIn('NE mode retained valid Einstein coefficients', diagnostic)
|
|
404
|
+
self.assertTrue(any(row.endswith('\tWARNING') for row in windows.splitlines()))
|
|
405
|
+
expected_sigma = conductivity(expected, 64, 1, 1e6, 300, ['Li'])
|
|
406
|
+
self.assertAlmostEqual(result.diffusion, expected[-1])
|
|
407
|
+
self.assertAlmostEqual(result.conductivity, expected_sigma[-1])
|
|
408
|
+
for output, prefix, coefficients, fmt in (
|
|
409
|
+
('self-diff.txt', 'D', expected, '.2e'),
|
|
410
|
+
('sigma.txt', 'sigma', expected_sigma, '.2f'),
|
|
411
|
+
):
|
|
412
|
+
header, *rows = (out/output).read_text().splitlines()
|
|
413
|
+
self.assertIn('quality=WARNING', header)
|
|
414
|
+
self.assertEqual([row.split() for row in rows], [
|
|
415
|
+
[f'{prefix}_{axis}', format(value, fmt)] for axis, value in zip(directions, coefficients)])
|
|
416
|
+
provenance = json.loads((out/'provenance.json').read_text())
|
|
417
|
+
self.assertEqual(provenance['quality'], 'WARNING')
|
|
418
|
+
self.assertEqual(provenance['diffusion_cm2_s'], result.diffusion)
|
|
419
|
+
self.assertEqual(provenance['conductivity_mS_cm'], result.conductivity)
|
|
420
|
+
|
|
421
|
+
def test_ne_rejected_quality_does_not_hide_weighted_estimator_failure(self):
|
|
422
|
+
t = np.arange(129)*.001
|
|
423
|
+
values = t[:, None]**.6*np.array([.5, .6, .7])
|
|
424
|
+
for method in ('wls', 'gls'):
|
|
425
|
+
with self.subTest(method=method):
|
|
426
|
+
folder = self.root/method
|
|
427
|
+
self.fixture(folder)
|
|
428
|
+
args = argparse.Namespace(group=0, ne=True, charge=1., fit_range=[.013, .064], fit_method=method)
|
|
429
|
+
self.assertEqual(_msd_auto.search(t, values, .129, interval=args.fit_range)['status'], 'REJECT')
|
|
430
|
+
with patch.object(_msd_fit, 'fft_msd', return_value=values):
|
|
431
|
+
with self.assertRaisesRegex(ValueError, f'{method.upper()} Einstein regression failed: .*binary points'):
|
|
432
|
+
msd.analyze(folder, args)
|
|
433
|
+
for output in ('msd.txt', 'fit-windows.txt', 'fit-diagnostic.txt'):
|
|
434
|
+
self.assertTrue((folder/'msd'/output).is_file())
|
|
435
|
+
for output in ('self-diff.txt', 'sigma.txt', 'provenance.json'):
|
|
436
|
+
self.assertFalse((folder/'msd'/output).exists())
|
|
437
|
+
|
|
438
|
+
def test_cli_ne_aggregates_rejected_quality_and_predicts_from_warning(self):
|
|
439
|
+
confined, brownian = self.root/'confined', self.root/'brownian'
|
|
440
|
+
self.fixture(confined, retention=.96)
|
|
441
|
+
self.fixture(brownian, temperature=500)
|
|
442
|
+
result = self.cli('msd', '-group', '0', '-ne')
|
|
443
|
+
self.assertEqual(result.returncode, 0, result.stdout+result.stderr)
|
|
444
|
+
for folder in (confined, brownian):
|
|
445
|
+
for output in ('self-diff.txt', 'sigma.txt', 'provenance.json'):
|
|
446
|
+
self.assertTrue((folder/'msd'/output).is_file())
|
|
447
|
+
for output in ('self-diff-total.txt', 'sigma-total.txt'):
|
|
448
|
+
_, *rows = (self.root/output).read_text().splitlines()
|
|
449
|
+
self.assertEqual([row.split()[0] for row in rows], ['confined', 'brownian'])
|
|
450
|
+
self.assertIn('confined WARNING', (self.root/'msd-quality-total.txt').read_text())
|
|
451
|
+
diagnostic = (confined/'msd/fit-diagnostic.txt').read_text()
|
|
452
|
+
self.assertIn('NE mode retained valid Einstein coefficients', diagnostic)
|
|
453
|
+
result = self.cli('pred-msd-time', '-ea', '.2', '-temp-interval', '50', '-max-temp', '350')
|
|
454
|
+
self.assertEqual(result.returncode, 0, result.stdout+result.stderr)
|
|
455
|
+
plan = read_plan(self.root/'pred-msd-time.log')
|
|
456
|
+
self.assertEqual(plan['reference']['temperature_K'], 300)
|
|
457
|
+
self.assertEqual(plan['reference']['quality'], 'WARNING')
|
|
458
|
+
self.assertTrue(any('BASELINE WARNING' in warning for warning in plan['warnings']))
|
|
459
|
+
|
|
460
|
+
def test_cli_ne_zero_msd_preserves_diagnostics_and_aggregate_summaries(self):
|
|
461
|
+
self.fixture(self.root, step_scale=0.)
|
|
462
|
+
msd.write_summaries(self.root, [msd.AnalysisResult('prior', 300., 1.e-5, 1.)], ne=True)
|
|
463
|
+
summaries = {p: p.read_bytes() for p in self.root.glob('*-total.txt')}
|
|
464
|
+
result = self.cli('msd', '-group', '0', '-ne')
|
|
465
|
+
self.assertNotEqual(result.returncode, 0, result.stdout)
|
|
466
|
+
self.assertIn('No valid Einstein regression', result.stdout)
|
|
467
|
+
self.assertIn('existing summaries preserved', result.stdout)
|
|
468
|
+
out = self.root/'msd'
|
|
469
|
+
np.testing.assert_array_equal(np.loadtxt(out/'msd.txt')[:, 1:], 0.)
|
|
470
|
+
for output in ('fit-windows.txt', 'fit-diagnostic.txt'):
|
|
471
|
+
self.assertTrue((out/output).is_file())
|
|
472
|
+
for output in ('self-diff.txt', 'sigma.txt', 'provenance.json'):
|
|
473
|
+
self.assertFalse((out/output).exists())
|
|
474
|
+
self.assertEqual({p: p.read_bytes() for p in summaries}, summaries)
|
|
475
|
+
|
|
350
476
|
def test_cli_multi_temperature_templates_and_failure_preserves_summary(self):
|
|
351
477
|
for name, target, measured in [('hot', 1000, 1001), ('cold-b', 300, 301), ('cold-a', 300, 301)]:
|
|
352
478
|
folder = self.root/name
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|