pm3-rs-python 0.2.3__tar.gz → 0.2.5__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.
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/CHANGELOG.md +283 -4
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/Cargo.lock +1 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/Cargo.toml +1 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/PKG-INFO +10 -1
- pm3_rs_python-0.2.5/THIRD_PARTY_NOTICES.md +115 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/docs/pbc.md +74 -4
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/docs/python-api.md +78 -5
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/docs/rust-api.md +63 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/docs/scope.md +84 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/pyproject.toml +27 -3
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/python/pm3_rs/__init__.py +22 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/python/pm3_rs/_native.pyi +33 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/python/pm3_rs/ase.py +102 -4
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/python/pm3_rs/native.py +238 -14
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/cli.rs +848 -29
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/constants.rs +43 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/corrections/h4.rs +2 -2
- pm3_rs_python-0.2.5/src/corrections/mmok.rs +224 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/corrections/mod.rs +33 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/dc/derivatives.rs +6 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/dc/mod.rs +1 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/dc/scf.rs +1 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/densitydiis.rs +5 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/error.rs +22 -4
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/gradient.rs +8 -5
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/hessian.rs +108 -28
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/integrals_d.rs +1 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/ir.rs +22 -76
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/lib.rs +5 -2
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/molden.rs +356 -17
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/optimizer.rs +147 -31
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/overlap.rs +1 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/born.rs +2 -2
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/dfpt.rs +343 -43
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/dielectric.rs +38 -13
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/ewald.rs +257 -56
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/ewald_hessian.rs +95 -28
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/finite_field.rs +1 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/gamma.rs +111 -4
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/hessian.rs +232 -58
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/kpoints.rs +1 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/kscf.rs +558 -9
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/mod.rs +15 -15
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/multipole.rs +2 -2
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/phased.rs +5 -5
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/phonon.rs +27 -11
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/python.rs +431 -126
- pm3_rs_python-0.2.5/src/rigid.rs +543 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/scf.rs +428 -16
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/api_surface.rs +73 -6
- pm3_rs_python-0.2.5/tests/attribution.rs +426 -0
- pm3_rs_python-0.2.5/tests/data/mopac_oracle.tsv +204 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/molecules.rs +104 -0
- pm3_rs_python-0.2.5/tests/mopac_oracle.rs +443 -0
- pm3_rs_python-0.2.5/tests/pbc_cubic_identities.rs +280 -0
- pm3_rs_python-0.2.5/tests/pbc_scf_diagnosis.rs +379 -0
- pm3_rs_python-0.2.5/tests/test_api_key_contract.py +478 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/test_periodic_python_api.py +7 -1
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/test_python_api.py +256 -5
- pm3_rs_python-0.2.5/third_party/README.md +60 -0
- pm3_rs_python-0.2.5/third_party/mopac/NOTICE +78 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/third_party/pyseqm/NOTICE +12 -3
- pm3_rs_python-0.2.5/third_party/rust-crates/LICENSES.txt +10114 -0
- pm3_rs_python-0.2.5/third_party/rust-crates/NOTICE +119 -0
- pm3_rs_python-0.2.3/THIRD_PARTY_NOTICES.md +0 -63
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/LICENSE +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/README.md +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/docs/divide-and-conquer.md +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/python/pm3_rs/cli.py +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/python/pm3_rs/py.typed +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/basis.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/bin/pm3_rs.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/cell.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/cmatrix.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/corrections/d3.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/corrections/hx.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/corrections/periodic.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data/d3_c6_reference.csv +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data/d3_r0ab.csv +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data/d3_radii.csv +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data/element_data.csv +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data/pm3_global.csv +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data/pm3_pair_parameters.csv +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data/pm3_parameters.csv +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data/pm3_sparkles.csv +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/data_tables.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/dc/partition.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/dc/pattern.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/dipole.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/dual.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/dual2.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/fock.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/frame.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/hamiltonian.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/integrals.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/linalg.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/math.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/neighbor.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/onecenter.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/overlap_numeric.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/params.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/berry.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/ewald_reference.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/gradient.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/kernel.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/lo_to.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/optimize.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/pbc/screen.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/repulsion.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/rotations.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/special.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/src/system.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/crystals.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/pbc_berry.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/pbc_born_charges.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/pbc_dfpt_options.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/pbc_dielectric.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/pbc_finite_field.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/pbc_lo_to.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/pbc_phonon.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/pbc_uhf_response.rs +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/test_ase_npt.py +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/tests/test_python.py +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/third_party/dftd3/NOTICE +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/third_party/h_bonds4/NOTICE +0 -0
- {pm3_rs_python-0.2.3 → pm3_rs_python-0.2.5}/third_party/mopac/LICENSE +0 -0
|
@@ -1,6 +1,285 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.2.
|
|
3
|
+
## 0.2.5
|
|
4
|
+
|
|
5
|
+
A broad oracle, and the three defects it found.
|
|
6
|
+
|
|
7
|
+
0.2.4 was validated against a dozen hand-picked molecules. This release compares
|
|
8
|
+
against **189, spanning every element PM3 is parameterized for**, and the answer
|
|
9
|
+
to "does pm3-rs reproduce MOPAC" is now a measurement rather than a belief: all
|
|
10
|
+
189 agree on heat of formation, dipole, net atomic charges and ionization
|
|
11
|
+
potential. Getting there took fixing two real bugs and correcting one assumption
|
|
12
|
+
about what the oracle was.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **`tests/data/mopac_oracle.tsv`** — MOPAC v23.2.5's own answers for 189
|
|
17
|
+
molecules, generated by `tools/oracle/build_oracle_set.py` and read by
|
|
18
|
+
`tests/mopac_oracle.rs`. Geometries are MOPAC-optimized (so each fixture is a
|
|
19
|
+
PM3 stationary point) except where there is nothing to optimize — a noble-gas
|
|
20
|
+
pair has no bound minimum, a Sparkle no valence orbitals — and those rows say so
|
|
21
|
+
in a `relaxed` column. Coverage is the whole parameter set: main group, the
|
|
22
|
+
noble gases, the heavy s-block, Zn/Cd/Hg, the fifteen La–Lu Sparkles and MOPAC's
|
|
23
|
+
`Cb`/`+`/`-`. Francium is the one absentee, because neither code has parameters
|
|
24
|
+
for it and both refuse it.
|
|
25
|
+
|
|
26
|
+
**Agreement: 189 of 189**, worst heat of formation `7.2e-4 kcal/mol`, worst
|
|
27
|
+
dipole `1.1e-3 D`, worst charge `1.6e-4 e`.
|
|
28
|
+
|
|
29
|
+
- **`Pm3Options::stability`** ([`ScfStability`]) — an SCF stability search. A
|
|
30
|
+
converged SCF is one fixed point of an operator that has several, and nothing in
|
|
31
|
+
a convergence test says it is the ground state. When the frontier gap is small
|
|
32
|
+
enough that the orbital ordering was in doubt, the molecule is re-solved from
|
|
33
|
+
deliberately different starting points and the lowest solution kept.
|
|
34
|
+
|
|
35
|
+
- **`Pm3Options::guess`** ([`ScfGuess`]) — the starting density is now selectable
|
|
36
|
+
(`Sad`, `Core`, `SymmetryBroken`), because it decides *which* solution is
|
|
37
|
+
reached and not merely how fast.
|
|
38
|
+
|
|
39
|
+
- **`Pm3Options::mmok`** — MOPAC's `MMOK` molecular-mechanics amide correction,
|
|
40
|
+
`K·sin²(O=C–N–H)` per amide hydrogen with `K = 7.1853 kcal/mol`. **Off by
|
|
41
|
+
default**, unlike MOPAC. Available from Python and the CLI as a `+mmok` suffix
|
|
42
|
+
on the method (`method="pm3+mmok"`, `--method pm3-d3h4+mmok`). Written over the
|
|
43
|
+
crate's dual numbers, so its gradient and Hessian contributions come from the
|
|
44
|
+
same expression and an optimization with it on minimizes what it reports.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- **The SCF converged to excited solutions on seven molecules**, by up to 279
|
|
49
|
+
kcal/mol, and reported them as converged. `2-butyne` (+279), `ketene` (+204),
|
|
50
|
+
`C₂F₄` (+166), `C₂Cl₄` (+157), the `O₂` triplet (+135), `NH₂•` (+37) and the
|
|
51
|
+
methyl radical (+111) each reached a self-consistent state that obeyed the
|
|
52
|
+
aufbau principle among its own eigenvalues and was not the ground state. Every
|
|
53
|
+
one of them now agrees with MOPAC.
|
|
54
|
+
|
|
55
|
+
Three separate causes, which is why no single change fixed them: DIIS stepping
|
|
56
|
+
over the fixed point (`2-butyne`, `ketene`), the SAD guess being in the wrong
|
|
57
|
+
basin (`C₂Cl₄`), and the restricted iteration having no path to the solution at
|
|
58
|
+
all (`C₂F₄` — reachable only through the unrestricted loop, which converges
|
|
59
|
+
spin-pure there and so returns a restricted state). The search covers all three
|
|
60
|
+
by trying them and keeping the lowest.
|
|
61
|
+
|
|
62
|
+
It fires on 6 of the 189 and costs nothing on the rest: the frontier gap
|
|
63
|
+
separates the two populations cleanly (every wrong solution below 4 eV, every
|
|
64
|
+
first-try-correct one above 5.7), so the trigger is a gap threshold sitting in
|
|
65
|
+
the empty band between. The whole 189-molecule set solves in under a second.
|
|
66
|
+
|
|
67
|
+
- **The unrestricted path started from core-Hamiltonian orbitals**, which is the
|
|
68
|
+
wrong guess. From SAD, the `O₂` triplet and the amino radical land on MOPAC's
|
|
69
|
+
answer instead of 135 and 37 kcal/mol above it, and the methoxy radical takes a
|
|
70
|
+
few dozen iterations instead of about 1600.
|
|
71
|
+
|
|
72
|
+
- **`Pm3Result` gained `scf_paths_tried` and `scf_improvement_ev`**, so a caller
|
|
73
|
+
can see when the search changed the answer rather than having to infer it.
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
|
|
77
|
+
- **MOPAC's default is not PM3, and the oracle now says so.** MOPAC applies `MMOK`
|
|
78
|
+
unless given `NOMM`, adding a classical amide torsion term after the SCF. It
|
|
79
|
+
changes no orbital and no total energy, which is what makes it easy to mistake
|
|
80
|
+
for a discrepancy: acetamide's density, dipole and every eigenvalue agreed with
|
|
81
|
+
ours to six figures while its heat of formation was 0.66 kcal/mol away. Every
|
|
82
|
+
row of the oracle is now generated with `NOMM`, so the file is plain PM3.
|
|
83
|
+
|
|
84
|
+
Both the form and the constant of the correction were measured against MOPAC
|
|
85
|
+
rather than copied, by scanning formamide's torsion with `MMOK` and `NOMM` and
|
|
86
|
+
subtracting; the fit is exact to the digits MOPAC prints.
|
|
87
|
+
|
|
88
|
+
## 0.2.4
|
|
89
|
+
|
|
90
|
+
Most of what was broken in 0.2.3 was not the physics but the **surface**: things
|
|
91
|
+
the library could do that no caller could ask for, and one assumption that was
|
|
92
|
+
true at Γ and quietly false everywhere else.
|
|
93
|
+
|
|
94
|
+
### Fixed
|
|
95
|
+
|
|
96
|
+
- **A meshed `D(q)` was wrong at `q = 0`** — by 300%, anisotropic on a cubic
|
|
97
|
+
crystal, and with the *sign* reversed, so a stable structure reported an
|
|
98
|
+
imaginary optical mode. One substitution made in two places: `P(T) = P(0)`,
|
|
99
|
+
which **is** Γ sampling (one k-point cannot tell images apart) and is false on a
|
|
100
|
+
mesh, where `P(0)` is the Brillouin-zone average while `P(T)` decays with `T`.
|
|
101
|
+
`phased_skeleton` asserted it for the skeleton — the dominant half — and
|
|
102
|
+
`bare_blocks` asserted it again for the response's exchange term. NaCl `3×3×3`
|
|
103
|
+
now gives an isotropic `D(0)` agreeing with a central difference of the analytic
|
|
104
|
+
forces to `1e-4`. Γ sampling is unchanged bit for bit: there the two expressions
|
|
105
|
+
are the same number.
|
|
106
|
+
|
|
107
|
+
The clue had been recorded a release earlier and not used — the error converged
|
|
108
|
+
smoothly with mesh size, **0% at `1³`** rising to 77% at `7³`. Zero at `1³` is
|
|
109
|
+
the tell: a `1×1×1` mesh *is* Γ.
|
|
110
|
+
|
|
111
|
+
- **`native.optimize()` returned no `energy_ev`, and `relax()` no `iterations`
|
|
112
|
+
or heat of formation.** Reading the docs for one and applying them to the other
|
|
113
|
+
ended a structure optimization with a `KeyError` from the call that had done the
|
|
114
|
+
most work. All three optimizers now answer to the same names, held by a
|
|
115
|
+
table-driven contract test over every native function.
|
|
116
|
+
|
|
117
|
+
- **`pm3-rs optimize big.xyz --dc 4.8` optimized nothing** and exited 0: the
|
|
118
|
+
divide-and-conquer branch returned before the command was read. The same held
|
|
119
|
+
for `frequencies`, `hessian`, `gradient`, `molden`, `ir` and `charges`.
|
|
120
|
+
|
|
121
|
+
- **Translations and rotations are removed by projection**, not by being small.
|
|
122
|
+
The rigid-body subspace is discovered from the geometry — 2 rotations for a
|
|
123
|
+
linear molecule, 0 for an atom — and those modes are **exactly** `0.00 cm⁻¹`.
|
|
124
|
+
Three different thresholds (`< 50`, `< 100`, `< 300 cm⁻¹`) for the same quantity
|
|
125
|
+
are gone, as is `SOFT_MODE_FLOOR`.
|
|
126
|
+
|
|
127
|
+
- **The Γ acoustic sum rule was applied row-wise**, which is not symmetric, and
|
|
128
|
+
the eigensolver reads one triangle — so the rule that had just been imposed was
|
|
129
|
+
partly undone and `PeriodicPhonons::hessian` came back non-symmetric. It is a
|
|
130
|
+
symmetric projection now, and `acoustic_residual_cm` reports the **pre**-projection
|
|
131
|
+
residual, which is the number worth seeing rather than the one the correction
|
|
132
|
+
just flattened.
|
|
133
|
+
|
|
134
|
+
- **`force_tol` was converted the wrong way** in the new CLI and partitioned
|
|
135
|
+
paths — a gradient scales the opposite way to a length, so the factor is
|
|
136
|
+
`BOHR_TO_ANGSTROM`. The tolerance was 3.57× looser than asked for, the optimizer
|
|
137
|
+
stopped early, and it still reported `converged: true`. Now
|
|
138
|
+
`constants::force_tol_to_au` and `stress_tol_to_au`, with a test that pins the
|
|
139
|
+
direction.
|
|
140
|
+
|
|
141
|
+
- **A stalled periodic SCF had no recourse on the Γ path and one rung on the
|
|
142
|
+
k-point path.** Damping now goes ahead of smearing on both, because its guarantee
|
|
143
|
+
is stronger: it changes the path and not the equations, and both loops measure
|
|
144
|
+
convergence against the *undamped* step, so a converged damped run solves what was
|
|
145
|
+
asked. Silicon on a 3×3×3 mesh is rescued (−145.536995 eV); diamond still fails,
|
|
146
|
+
because its residual alternates rather than decays and no mixing fraction reaches
|
|
147
|
+
a symmetry-breaking limit cycle. There is deliberately no third rung: a level
|
|
148
|
+
shift converges diamond to a *different solution*, which is worse than not
|
|
149
|
+
converging, so it stays in the diagnosis for the caller to choose.
|
|
150
|
+
|
|
151
|
+
- **A periodic response returned an incomplete answer for a partially occupied
|
|
152
|
+
cell, silently.** The occupation factors are the metallic
|
|
153
|
+
`(f_n − f_m)/(ε_n − ε_m)` form, which makes it look handled; what is missing is
|
|
154
|
+
the Fermi-level shift, and the sum skips the near-degenerate pairs that carry the
|
|
155
|
+
Fermi-surface term. It is refused now — keyed on whether the occupations came out
|
|
156
|
+
**integral**, not on whether smearing was used, so a gapped cell under a small
|
|
157
|
+
smearing still runs. Same `T·S` certificate the SCF rescue uses.
|
|
158
|
+
|
|
159
|
+
- Seventeen lines across three files had been committed after a PowerShell cp932
|
|
160
|
+
round-trip. In `pbc/mod.rs` the casualties were the electrostatics derivation:
|
|
161
|
+
`√(r² + a)` had lost its root and bracket and `[ W_KO(r) − W_point(r) ]` its
|
|
162
|
+
minus sign, so a formula read as noise rather than as wrong.
|
|
163
|
+
|
|
164
|
+
### Added
|
|
165
|
+
|
|
166
|
+
- **Orbital energies, coefficients and occupations** (`native.orbitals`,
|
|
167
|
+
`pm3-rs orbitals`, `--coefficients`). Coefficients come with `ao_labels`, and
|
|
168
|
+
the frontier is taken across both spin channels — a radical's β LUMO sits below
|
|
169
|
+
its α one.
|
|
170
|
+
- **Phonon eigenvectors.** They were computed and discarded. `modes` (real) at Γ,
|
|
171
|
+
`modes_real`/`modes_imag` at a wavevector, through `native.phonons` and
|
|
172
|
+
`PM3.get_phonons`.
|
|
173
|
+
- **`Pm3Options::cphf_max_iter`** and a `cphf_max_iter=` argument on the six
|
|
174
|
+
Python entry points that run a coupled-perturbed solve. It was two hard-coded
|
|
175
|
+
`400`s, so a stiff response could only be rescued by editing the crate.
|
|
176
|
+
- **Divide-and-conquer geometry optimization** from the CLI (`--dc` with
|
|
177
|
+
`optimize`), `native.divide_and_conquer_optimize` and `PM3`.
|
|
178
|
+
- **Molden `[STO]`** (`--sto`, `basis="sto"`) beside the default `[GTO]`, and
|
|
179
|
+
`--output` to say where the file goes.
|
|
180
|
+
- **`--pbc` in the spelling the reader has in mind**: `1,0,1`, `101`, `xz`,
|
|
181
|
+
`x,z`, `true,false,true`, `none`.
|
|
182
|
+
- **The periodic optimizer's controls**: `--relax-cell`, `--fixed-cell`,
|
|
183
|
+
`--max-steps`, `--force-tol`, `--stress-tol`, `--pressure`. `write_xyz` now
|
|
184
|
+
carries the cell as extended-XYZ `Lattice="..."`, which was silent data loss the
|
|
185
|
+
moment variable-cell relaxation existed.
|
|
186
|
+
- `--kpts` and `--q` on the CLI; `native.divide_and_conquer_forces`; periodic
|
|
187
|
+
`hessian` from Python.
|
|
188
|
+
|
|
189
|
+
### Performance
|
|
190
|
+
|
|
191
|
+
- **Periodic runs are about 10× faster.** A 48-atom Γ single point went from
|
|
192
|
+
17.7 s to 1.82 s; the Python test suite from 396 s to 72 s. The numbers are
|
|
193
|
+
unchanged.
|
|
194
|
+
|
|
195
|
+
None of the three optimizations that had been planned were the problem. Measured
|
|
196
|
+
first, as `examples/born_profile.rs` established: the serial k-point loop was
|
|
197
|
+
31–45% of a *meshed* run and nothing of a Γ one, the serial Fock build was
|
|
198
|
+
**0.4%** of an iteration at every size from 6 to 72 atoms, and an optimization
|
|
199
|
+
step cost 1.6 gradients rather than the up-to-30 the line search was feared to be
|
|
200
|
+
paying. It was the Ewald sum, at 89–98% of every iteration — and inside that,
|
|
201
|
+
90.5 s of real space against 2.5 s of reciprocal, so the guess *within* the guess
|
|
202
|
+
was wrong too.
|
|
203
|
+
|
|
204
|
+
The real-space sum evaluated `erfc` and `exp` for every pair inside the cutoff,
|
|
205
|
+
**twice per iteration**, at distances that do not move while the charges do. The
|
|
206
|
+
kernel is geometry and now sits in `EwaldContext` beside the phase table. Real
|
|
207
|
+
space fell 32×. `examples/periodic_profile.rs` and `PM3_GAMMA_PROFILE=1` are the
|
|
208
|
+
measurement.
|
|
209
|
+
|
|
210
|
+
- **A Γ-point Hessian is 2.3× faster, and its Ewald phase 210×.** 48 atoms: 51.3 s
|
|
211
|
+
to 22.8 s, with the Ewald second derivative going from 31.60 s to 0.15 s. Numbers
|
|
212
|
+
unchanged.
|
|
213
|
+
|
|
214
|
+
Profiled first, again, and again it was not the assumption: the coupled-perturbed
|
|
215
|
+
response — the phase everyone expects to dominate — was 38%, the skeleton rounded
|
|
216
|
+
to zero, and the Ewald term was 62%. Its inner sum turned out to be a **structure
|
|
217
|
+
factor written out longhand**: what the site-pair loop accumulates for an atom
|
|
218
|
+
pair is `Re[S_A S_B*]`, so `O(N_G · N_sites²)` becomes `N_sites` to build the
|
|
219
|
+
factors plus `N_atoms²` to combine them — and PM3 puts four to five multipole
|
|
220
|
+
sites on every heavy atom. `examples/hessian_profile.rs` and
|
|
221
|
+
`PM3_HESSIAN_PROFILE=1` are the measurement. The Hessian is now 96% response,
|
|
222
|
+
which is where the next look starts.
|
|
223
|
+
|
|
224
|
+
### Licensing
|
|
225
|
+
|
|
226
|
+
- **The wheel shipped `LICENSE` alone.** `_native.pyd` has the MOPAC-derived PM3
|
|
227
|
+
parameter tables compiled into it, so `pip install` distributed Apache-2.0
|
|
228
|
+
material without the licence text or the attribution. It now carries
|
|
229
|
+
`THIRD_PARTY_NOTICES.md`, the Apache text and every `NOTICE`.
|
|
230
|
+
- **Nothing recorded the ~60 Rust crates statically linked into the binaries.**
|
|
231
|
+
`faer`, `rayon`, `pyo3` and their transitive closure are compiled into
|
|
232
|
+
everything shipped, and MIT asks for the copyright notice to travel with a
|
|
233
|
+
substantial portion. `third_party/rust-crates/` now carries the index and 524 KB
|
|
234
|
+
of verbatim texts, generated by `tools/collect_rust_notices.py`.
|
|
235
|
+
|
|
236
|
+
Two things the graph turned up: `faer` declares MIT and ships **no MIT text**
|
|
237
|
+
(what it ships is its own upstream Eigen/LAPACK/SuiteSparse attributions), and
|
|
238
|
+
`unicode-ident` is `(MIT OR Apache-2.0) **AND** Unicode-3.0` — the only entry
|
|
239
|
+
where choosing a licence does not settle the question.
|
|
240
|
+
- `tests/attribution.rs` holds all of it to the tree: every linked crate is in the
|
|
241
|
+
notice, the two distributions agree, no upstream source is tracked, and no file
|
|
242
|
+
has been through a codepage round-trip.
|
|
243
|
+
|
|
244
|
+
### Testing
|
|
245
|
+
|
|
246
|
+
- **Every CLI command against every flag**, mechanically — the matrix that found
|
|
247
|
+
`--dc` swallowing seven commands. Plus a second layer running each command
|
|
248
|
+
through the installed console script, where only `energy` had ever been
|
|
249
|
+
exercised across the Python↔Rust boundary.
|
|
250
|
+
- **All fifteen La–Lu Sparkles against MOPAC** at `1e-5 kcal/mol`, re-run through
|
|
251
|
+
the executable rather than compared to a stored JSON.
|
|
252
|
+
- `tests/pbc_cubic_identities.rs` has no `#[ignore]`s left, and neither does the
|
|
253
|
+
suite.
|
|
254
|
+
|
|
255
|
+
### Known limits
|
|
256
|
+
|
|
257
|
+
- PM3's transition metals are **Zn, Cd and Hg** and nothing else. `TiO₂`, `SrTiO₃`,
|
|
258
|
+
`LiCoO₂` and ferrocene are outside the model, not unimplemented. Those three are
|
|
259
|
+
enough for real organometallic chemistry: `examples/phonon_structures.rs` and
|
|
260
|
+
`examples/organometallic_bands.rs` cover `Zn(CH₃)₂` and `Hg(CH₃)₂` — linear
|
|
261
|
+
dialkyls with genuine `M–C` σ bonds — and the `Cd(CN)₂` framework.
|
|
262
|
+
|
|
263
|
+
- **Phonons must be evaluated at PM3's own geometry, not at the experimental one**,
|
|
264
|
+
and for ionic solids the two are far apart. A Hessian off a stationary point is
|
|
265
|
+
not a phonon spectrum, so the experimental lattice constant is the wrong place to
|
|
266
|
+
ask. At the lattice constant PM3 actually prefers, MgO's spectrum is clean — but
|
|
267
|
+
that equilibrium sits 12.7% *above* the measured value, and the highest mode comes
|
|
268
|
+
out at 745 cm⁻¹ against 401 measured.
|
|
269
|
+
|
|
270
|
+
For NaCl the energy is still falling at the compressive edge of the range Γ
|
|
271
|
+
sampling can hold, so PM3 has no bound rocksalt minimum there at all. Neither is
|
|
272
|
+
a defect in the second-derivative code; PM3 was fitted to molecular heats of
|
|
273
|
+
formation and never saw a Madelung lattice.
|
|
274
|
+
|
|
275
|
+
- Where PM3 *is* on firm ground is the molecular chemistry it was fitted to.
|
|
276
|
+
Dimethylzinc relaxes to a Zn–C bond of 1.924 Å against 1.930 Å measured, and its
|
|
277
|
+
symmetric Zn–C stretch comes out at 599 cm⁻¹ against ≈615.
|
|
278
|
+
|
|
279
|
+
- A periodic SCF on some cubic cells with an even Monkhorst–Pack mesh still
|
|
280
|
+
converges to a mesh-dependent solution; see `docs/pbc.md`.
|
|
281
|
+
|
|
282
|
+
## 0.2.3
|
|
4
283
|
|
|
5
284
|
One performance fix, found by running real crystals rather than by reading code,
|
|
6
285
|
and two reporting defects it turned up on the way.
|
|
@@ -45,7 +324,7 @@ and two reporting defects it turned up on the way.
|
|
|
45
324
|
- `cargo fmt` across the tree: 0.2.2 shipped 26 files that rustfmt disagreed
|
|
46
325
|
with, all of them new or edited in that release.
|
|
47
326
|
|
|
48
|
-
## 0.2.2
|
|
327
|
+
## 0.2.2
|
|
49
328
|
|
|
50
329
|
The response properties a dynamical matrix was already most of the way to, and a
|
|
51
330
|
Berry phase to check them from outside. The largest fix is none of those: a
|
|
@@ -189,7 +468,7 @@ removing the intra-atomic `dd` moment the phase omits (`PM3_BORN_NO_DD=1`)
|
|
|
189
468
|
collapses that to `1.9e-4`. The two formalisms agree on everything except one
|
|
190
469
|
identified physical term.
|
|
191
470
|
|
|
192
|
-
## 0.2.1
|
|
471
|
+
## 0.2.1
|
|
193
472
|
|
|
194
473
|
Phonons everywhere the Γ point already worked, an external electric field, and
|
|
195
474
|
the wavefunction outputs that go with it. Three defects that had shipped in
|
|
@@ -377,7 +656,7 @@ test that could have caught them ran.
|
|
|
377
656
|
existed. A test now compares every stub signature against the extension, and
|
|
378
657
|
another asserts `pm3_rs.native` can pass every argument the extension accepts.
|
|
379
658
|
|
|
380
|
-
## 0.2.0
|
|
659
|
+
## 0.2.0
|
|
381
660
|
|
|
382
661
|
Periodic boundary conditions. Molecular results are unchanged except for one
|
|
383
662
|
bug fix, noted below.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pm3-rs-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.5
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Science/Research
|
|
6
6
|
Classifier: Programming Language :: Rust
|
|
@@ -19,6 +19,15 @@ Requires-Dist: numpy>=1.21
|
|
|
19
19
|
Requires-Dist: ase>=3.22 ; extra == 'ase'
|
|
20
20
|
Provides-Extra: ase
|
|
21
21
|
License-File: LICENSE
|
|
22
|
+
License-File: THIRD_PARTY_NOTICES.md
|
|
23
|
+
License-File: third_party/README.md
|
|
24
|
+
License-File: third_party/mopac/LICENSE
|
|
25
|
+
License-File: third_party/dftd3/NOTICE
|
|
26
|
+
License-File: third_party/h_bonds4/NOTICE
|
|
27
|
+
License-File: third_party/mopac/NOTICE
|
|
28
|
+
License-File: third_party/pyseqm/NOTICE
|
|
29
|
+
License-File: third_party/rust-crates/NOTICE
|
|
30
|
+
License-File: third_party/rust-crates/LICENSES.txt
|
|
22
31
|
Summary: PM3 semiempirical method with periodic boundary conditions, divide-and-conquer, a Python-native API and an ASE calculator
|
|
23
32
|
Keywords: chemistry,computational-chemistry,quantum-chemistry,semiempirical,pm3,nddo,mopac,ase,periodic-boundary-conditions,dft
|
|
24
33
|
Author: ss0832
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
`pm3-rs` is GPL-3.0-or-later. This document records the provenance of its
|
|
4
|
+
scientific parameters and reference algorithms, with the citations.
|
|
5
|
+
|
|
6
|
+
**[`third_party/README.md`](third_party/README.md) is the index**: one table
|
|
7
|
+
saying, for each upstream project, what `pm3-rs` takes from it, under which
|
|
8
|
+
license, and which files here hold the terms. Each `third_party/<name>/NOTICE`
|
|
9
|
+
records the same for that project in detail. Start there if the question is
|
|
10
|
+
"what is owed to whom"; this file is the bibliography.
|
|
11
|
+
|
|
12
|
+
No third-party source code is vendored in this repository. What crosses the
|
|
13
|
+
boundary is published numeric parameters and published working equations.
|
|
14
|
+
|
|
15
|
+
That is true of the *repository*. It is not true of the **binaries**: the Rust
|
|
16
|
+
dependency graph is statically linked into `_native.pyd` and the `pm3-rs`
|
|
17
|
+
command, so around sixty crates are compiled into everything this project ships.
|
|
18
|
+
They are recorded separately in
|
|
19
|
+
[`third_party/rust-crates/NOTICE`](third_party/rust-crates/NOTICE), with the
|
|
20
|
+
verbatim texts in `LICENSES.txt` beside it, because the obligation they carry is
|
|
21
|
+
a different one: MIT asks for the copyright notice to travel with the binary,
|
|
22
|
+
not for a citation in a bibliography. See "Rust dependencies" below.
|
|
23
|
+
|
|
24
|
+
## PM3
|
|
25
|
+
|
|
26
|
+
The PM3 model is described by:
|
|
27
|
+
|
|
28
|
+
- J. J. P. Stewart, "Optimization of parameters for semiempirical methods I.
|
|
29
|
+
Method", *J. Comput. Chem.* **10**, 209-220 (1989),
|
|
30
|
+
DOI 10.1002/jcc.540100208.
|
|
31
|
+
- J. J. P. Stewart, "Optimization of parameters for semiempirical methods II.
|
|
32
|
+
Applications", *J. Comput. Chem.* **10**, 221-264 (1989),
|
|
33
|
+
DOI 10.1002/jcc.540100209.
|
|
34
|
+
|
|
35
|
+
The machine-readable PM3 tables under `src/data/` are generated by
|
|
36
|
+
`tools/extract_pm3_params.py` from open-source MOPAC v23.2.5:
|
|
37
|
+
|
|
38
|
+
- repository: https://github.com/openmopac/mopac, tag `v23.2.5`
|
|
39
|
+
- license: Apache-2.0, retained at `third_party/mopac/LICENSE`
|
|
40
|
+
- input files: `parameters_for_PM3_C.F90`,
|
|
41
|
+
`parameters_for_PM3_Sparkles_C.F90`, `alpb_and_xfac_pm3.F90`, and
|
|
42
|
+
`parameters_C.F90`
|
|
43
|
+
|
|
44
|
+
The extracted data include element parameters, per-element Gaussian terms,
|
|
45
|
+
PM3 pair factors, shell/reference-atom data, La-Lu Sparkles, and MOPAC special
|
|
46
|
+
atom codes. MOPAC is also used as the executable oracle for plain PM3.
|
|
47
|
+
|
|
48
|
+
The Rust implementation follows the MOPAC reference equations for derived
|
|
49
|
+
multipoles, one- and two-center NDDO integrals, PM3 core-core repulsion, SCF
|
|
50
|
+
bookkeeping, and special zero-orbital cores. The implementation is original
|
|
51
|
+
Rust code; no MOPAC Fortran source is redistributed.
|
|
52
|
+
|
|
53
|
+
MOPAC contributes in three distinct roles — redistributed parameter data,
|
|
54
|
+
followed-but-not-copied equations, and a build-time oracle that never ships —
|
|
55
|
+
which carry different obligations. [`third_party/mopac/NOTICE`](third_party/mopac/NOTICE)
|
|
56
|
+
separates them and names the upstream file behind each.
|
|
57
|
+
|
|
58
|
+
## D3, H4, and X corrections
|
|
59
|
+
|
|
60
|
+
- D3: S. Grimme, J. Antony, S. Ehrlich, and H. Krieg, *J. Chem. Phys.*
|
|
61
|
+
**132**, 154104 (2010). Embedded C6/radius tables were extracted from the
|
|
62
|
+
Apache-2.0 MOPAC implementation; see `third_party/dftd3/NOTICE`.
|
|
63
|
+
- PM3-D3: the zero-damping constants `s6=1`, `s8=0.612`, `rs6=1.345`,
|
|
64
|
+
`rs8=1`, `alpha6=14`, and `alpha8=16` are verified against the public MOPAC
|
|
65
|
+
5.022mn `anad3.f` implementation distributed by the University of Minnesota.
|
|
66
|
+
- D3H4: J. Rezac and P. Hobza, *J. Chem. Theory Comput.* **8**, 141-151
|
|
67
|
+
(2012), DOI 10.1021/ct200751e. PM3-specific D3/H4/H-H coefficients follow
|
|
68
|
+
the authors' public implementation and Cuby interface parameter table; see
|
|
69
|
+
`third_party/h_bonds4/NOTICE`.
|
|
70
|
+
- X: J. Rezac and P. Hobza, *Chem. Phys. Lett.* **506**, 286-289 (2011),
|
|
71
|
+
DOI 10.1016/j.cplett.2011.03.009. Parameters follow MOPAC
|
|
72
|
+
`corrections/disp_DnX.F90`.
|
|
73
|
+
|
|
74
|
+
## PySEQM
|
|
75
|
+
|
|
76
|
+
The closed-form s/p two-center integral and rotation kernels in
|
|
77
|
+
`src/integrals.rs` and `src/overlap.rs` follow the reference equations
|
|
78
|
+
implemented by PySEQM:
|
|
79
|
+
|
|
80
|
+
- https://github.com/lanl/PYSEQM
|
|
81
|
+
- G. Zhou et al., *J. Chem. Theory Comput.* **16**, 4951 (2020)
|
|
82
|
+
- BSD-3-Clause; see `third_party/pyseqm/NOTICE`
|
|
83
|
+
|
|
84
|
+
## Rust dependencies
|
|
85
|
+
|
|
86
|
+
Everything above concerns *scientific* provenance — parameters and equations —
|
|
87
|
+
and none of it is code that ships. The Rust crates are the opposite case: no
|
|
88
|
+
science crosses from them, and all of their code does.
|
|
89
|
+
|
|
90
|
+
`faer` (linear algebra), `rayon` (parallelism) and `pyo3` (the Python bindings)
|
|
91
|
+
are the three direct dependencies; with their transitive closure that is around
|
|
92
|
+
sixty crates, statically linked into `_native.pyd` and the `pm3-rs` executable.
|
|
93
|
+
Most are MIT, which requires the copyright notice and the permission notice to
|
|
94
|
+
be included in "all copies or substantial portions of the Software" — which a
|
|
95
|
+
static binary is. A handful carry other terms: `atomic-wait` is BSD-2-Clause,
|
|
96
|
+
`bytemuck` offers Zlib, `target-lexicon` is Apache-2.0 with the LLVM exception,
|
|
97
|
+
and `unicode-ident` is `(MIT OR Apache-2.0) AND Unicode-3.0`, where the `AND`
|
|
98
|
+
means the Unicode terms apply on top rather than as an alternative.
|
|
99
|
+
|
|
100
|
+
- index, with the SPDX expression and files found per crate:
|
|
101
|
+
[`third_party/rust-crates/NOTICE`](third_party/rust-crates/NOTICE)
|
|
102
|
+
- the verbatim texts, with their copyright lines:
|
|
103
|
+
`third_party/rust-crates/LICENSES.txt`
|
|
104
|
+
- regenerate with `python tools/collect_rust_notices.py` after changing a
|
|
105
|
+
dependency; `tests/attribution.rs` fails if the graph and the index disagree
|
|
106
|
+
|
|
107
|
+
`faer` additionally carries upstream attributions of its own — Eigen (MPL-2.0),
|
|
108
|
+
LAPACK and SuiteSparse (BSD) — which are reproduced in `LICENSES.txt` along with
|
|
109
|
+
everything else, rather than being left one level up the chain.
|
|
110
|
+
|
|
111
|
+
Apache-2.0 and BSD-3-Clause are compatible with GPL-3.0-or-later for this use,
|
|
112
|
+
as are MIT, BSD-2-Clause, Zlib, MPL-2.0 and Apache-2.0-WITH-LLVM-exception; the
|
|
113
|
+
compatibility is one-way, and this project is the GPL end of it. Numeric
|
|
114
|
+
scientific parameter values are retained with provenance regardless of their
|
|
115
|
+
copyright status.
|
|
@@ -461,6 +461,46 @@ each image's displacement scaled by its own `e^{iq·T}`, validated by folding a
|
|
|
461
461
|
hydrogen-bonded chain onto its own doubled cell. The Γ-point analytic Hessian
|
|
462
462
|
reaches 1D and 2D the same way, its Ewald block being the phased sum at `q = 0`.
|
|
463
463
|
|
|
464
|
+
## A meshed `D(q)` was wrong at `q = 0`, and is fixed in 0.2.4
|
|
465
|
+
|
|
466
|
+
Through 0.2.3, `dynamical_matrix_on_mesh` / `phonons(..., kpts=...)` / `--kpts`
|
|
467
|
+
with `--q` returned a badly wrong answer at `q = 0`. Measured on rocksalt NaCl at
|
|
468
|
+
its experimental lattice constant, `3×3×3` mesh, in eV/Ų:
|
|
469
|
+
|
|
470
|
+
| | `D_xx` | `D_yy` | `D_zz` |
|
|
471
|
+
|---|---:|---:|---:|
|
|
472
|
+
| central difference of the analytic forces | +2.16 | +2.16 | +2.16 |
|
|
473
|
+
| `dynamical_matrix_on_mesh`, 0.2.3 | **−4.61** | **−1.28** | **−1.28** |
|
|
474
|
+
| `dynamical_matrix_on_mesh`, 0.2.4 | +2.16 | +2.16 | +2.16 |
|
|
475
|
+
|
|
476
|
+
Three things had been wrong at once — the magnitude, the isotropy a cubic crystal
|
|
477
|
+
must have, and the *sign*, which reported an imaginary optical mode for a
|
|
478
|
+
structure that is a minimum.
|
|
479
|
+
|
|
480
|
+
**The cause was one substitution made in two places.** `P(T) = P(0)` — one density
|
|
481
|
+
matrix serving every lattice image — *is* Γ sampling: a single k-point cannot tell
|
|
482
|
+
images apart, so it holds there by construction. On a mesh it is false, because
|
|
483
|
+
`P(0)` becomes the Brillouin-zone average while `P(T)` decays with `T`.
|
|
484
|
+
|
|
485
|
+
- `phased_skeleton` passed one density matrix to `pair_block` for every image. The
|
|
486
|
+
skeleton is the larger half of `D(q)`, so a meshed run built its dominant term
|
|
487
|
+
from the wrong density.
|
|
488
|
+
- `bare_blocks` read `exchange_scale · P^σ(0)` for the exchange part of each bare
|
|
489
|
+
perturbation, where the response needs `P^σ(T)`. `SpinChannel::p_images` already
|
|
490
|
+
carried the right thing under both samplings; nothing was reading it.
|
|
491
|
+
|
|
492
|
+
Both now read the per-image density. Γ sampling is unchanged, bit for bit: at Γ
|
|
493
|
+
the two expressions are the same number.
|
|
494
|
+
|
|
495
|
+
The clue that identified it had been recorded for a release and not used — the
|
|
496
|
+
error converged smoothly with mesh size, 0% at `1³` rising to 77% at `7³`. Zero at
|
|
497
|
+
`1³` is the tell: a `1×1×1` mesh *is* Γ, where the substitution is exactly true.
|
|
498
|
+
|
|
499
|
+
`tests/pbc_cubic_identities.rs` holds both identities on a mesh, and
|
|
500
|
+
`pbc::dfpt::tests::the_meshed_skeleton_is_where_the_cubic_symmetry_breaks` runs
|
|
501
|
+
the skeleton without the response under both samplings — a separation no public
|
|
502
|
+
entry point offers, and what made the second occurrence findable.
|
|
503
|
+
|
|
464
504
|
## Response properties
|
|
465
505
|
|
|
466
506
|
Everything in this section is a contraction of the same first-order density the
|
|
@@ -597,10 +637,40 @@ so it is known rather than discovered:
|
|
|
597
637
|
level shifts to 5 eV, and at a thousand iterations. Two eV of smearing does converge — to a
|
|
598
638
|
*different* solution 200 eV away.
|
|
599
639
|
|
|
600
|
-
The chemical potential is what gives it away: it swings by an electronvolt every iteration
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
640
|
+
The chemical potential is what gives it away: it swings by an electronvolt every iteration. An
|
|
641
|
+
even Γ-centred mesh on a cubic cell puts every one of its points on a zone-boundary symmetry
|
|
642
|
+
point, where bands meet.
|
|
643
|
+
|
|
644
|
+
> **The mechanism above is now measured, and the "occupations flip" half of it is wrong.**
|
|
645
|
+
> `PM3_KSCF_TRACE=1` reports the occupation-flip count and the per-atom populations alongside
|
|
646
|
+
> the residual. On the **primitive** two-atom cell at 5.64 Å with a `2×2×2` mesh, the flip count
|
|
647
|
+
> is **zero at every iteration** — the occupations never change sides — while a full **1.1
|
|
648
|
+
> electrons** move between Na and Cl each pass and `μ` follows across twelve electronvolts. It
|
|
649
|
+
> is charge sloshing, not a Fermi level trapped in a degenerate manifold; the charge moves first
|
|
650
|
+
> and `μ` follows it. That cell also *recovers*, converging unaided to `−341.786 eV`.
|
|
651
|
+
>
|
|
652
|
+
> **And converging is not the point.** The primitive cell on a `2×2×2` mesh converges, cleanly,
|
|
653
|
+
> to a state with **−2.10 electrons of charge on the sodium**, where every mesh from `3³` to
|
|
654
|
+
> `7³` puts `+0.16` to `+0.24` there and agrees on the energy to 0.06 eV. A sodium that has
|
|
655
|
+
> gained two electrons is not a chemical result, and nothing in the energy, the residual or the
|
|
656
|
+
> `converged` flag says so. `KpointResult::charge_swing` does: the even mesh swings **1.44
|
|
657
|
+
> electrons** on its way there against the odd mesh's 0.29. **Check it, and check the charges.**
|
|
658
|
+
>
|
|
659
|
+
> **It is not a mixing problem, and three standard remedies say so.** A deeper Pulay history
|
|
660
|
+
> (8 → 24) moves diamond's residual from `3.72e-5` to `3.41e-5`; Kerker preconditioning of the
|
|
661
|
+
> atomic-charge channel makes NaCl's sloshing *worse* (1.44 → 2.56 electrons of swing); handing
|
|
662
|
+
> damping over to the accelerator, as the Γ path does, converges diamond and silicon and breaks
|
|
663
|
+
> four k-point identities. What the Kerker experiment showed instead is the actual shape of the
|
|
664
|
+
> problem: **three different mixers reach three different converged solutions on the same cell
|
|
665
|
+
> and mesh** — `−341.79`, `−366.34` and `−379.22 eV`, with `−2.10`, `−4.35` and `−4.94`
|
|
666
|
+
> electrons on the sodium — while every odd mesh agrees on `−327.45` and `+0.17`. The
|
|
667
|
+
> fixed-point map has several attractors at that sampling and the mixer picks among them. No
|
|
668
|
+
> preconditioner fixes that. Use an odd mesh, and check `charge_swing`.
|
|
669
|
+
>
|
|
670
|
+
> The conventional-cell failure `tests/crystals.rs` pins is a different system and has not been
|
|
671
|
+
> re-measured with the trace. Run it before repeating the explanation above.
|
|
672
|
+
> `examples/scf_hardening.rs` has the ladder, and a failed SCF now carries its own diagnosis in
|
|
673
|
+
> the error rather than only a residual.
|
|
604
674
|
|
|
605
675
|
Odd meshes do not sit on those points. `3×3×3` converges in eighteen iterations, and the answer
|
|
606
676
|
is right: energies agree to 0.02 eV and charges to 0.02 electrons across `3×3×3`, `4×4×4` and
|