-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_consistency_reference.py
More file actions
262 lines (217 loc) · 8.61 KB
/
test_consistency_reference.py
File metadata and controls
262 lines (217 loc) · 8.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
"""
Enhanced consistency tests for PyMultiWFN with reference values.
This module adds reference value tests to ensure numerical accuracy
against known literature values and standard quantum chemistry calculations.
"""
import pytest
import numpy as np
from pymultiwfn.core.data import Atom, Shell, Wavefunction
from pymultiwfn.math.density import calc_density
from pymultiwfn.analysis.bonding.bondorder import calculate_mayer_bond_order
# Note: mulliken_population_analysis not yet implemented
# from pymultiwfn.analysis.population.mulliken import mulliken_population_analysis
class TestReferenceValues:
"""Test calculations against reference values from literature/software."""
def test_h2_electron_count(self):
"""Verify H2 has exactly 2 electrons.
Reference: Basic quantum mechanics - H2 has 2 electrons
"""
# Create H2 molecule with minimal basis
atoms = [
Atom(element="H", index=1, x=0.0, y=0.0, z=-0.37, charge=1.0),
Atom(element="H", index=2, x=0.0, y=0.0, z=0.37, charge=1.0),
]
shells = [
Shell(
type=0, # S orbital
center_idx=0,
exponents=np.array([3.42525091]),
coefficients=np.array([0.15432897]),
),
Shell(
type=0,
center_idx=1,
exponents=np.array([3.42525091]),
coefficients=np.array([0.15432897]),
),
]
coeff = 1.0 / np.sqrt(2)
coefficients = np.array([[coeff], [coeff]])
wfn = Wavefunction(
atoms=atoms,
num_electrons=2.0, # H2 has 2 electrons
charge=0,
multiplicity=1,
num_basis=2,
num_atomic_orbitals=2,
num_primitives=2,
num_shells=2,
shells=shells,
occupations=np.array([2.0]),
coefficients=coefficients,
)
# Reference: num_electrons should be 2.0
assert wfn.num_electrons == 2.0, f"Electron count should be 2.0, got {wfn.num_electrons}"
def test_h2_bond_order_unity(self):
"""Verify H2 bond order is approximately 1.0 (single bond).
Reference: H2 is a single bond, bond order = 1.0
"""
# Simplified test - just verify the wavefunction is created correctly
# Bond order calculation requires more infrastructure
atoms = [
Atom(element="H", index=1, x=0.0, y=0.0, z=-0.37, charge=1.0),
Atom(element="H", index=2, x=0.0, y=0.0, z=0.37, charge=1.0),
]
wfn = Wavefunction(
atoms=atoms,
num_electrons=2.0,
charge=0,
multiplicity=1,
num_basis=2,
num_atomic_orbitals=2,
num_primitives=2,
num_shells=2,
shells=[],
occupations=np.array([2.0]),
coefficients=np.array([[0.707], [0.707]]),
)
# Reference: H2 has 2 atoms
assert len(wfn.atoms) == 2, "H2 should have 2 atoms"
# Bond order test skipped - requires full implementation
# When implemented, should verify bond_order ≈ 1.0
def test_density_normalization(self):
"""Verify density matrix normalization.
Reference: Sum of density matrix elements = number of electrons (for closed shell)
"""
# Create a simple H atom
atom = Atom(element="H", index=1, x=0.0, y=0.0, z=0.0, charge=1.0)
shell = Shell(
type=0,
center_idx=0,
exponents=np.array([3.42525091]),
coefficients=np.array([0.15432897]),
)
wfn = Wavefunction(
atoms=[atom],
num_electrons=1.0,
charge=0,
multiplicity=2, # Doublet
num_basis=1,
num_atomic_orbitals=1,
num_primitives=1,
num_shells=1,
shells=[shell],
occupations=np.array([1.0]),
coefficients=np.array([[1.0]]),
)
# Density at nucleus should be finite
coords = np.array([[0.0, 0.0, 0.0]])
density = calc_density(wfn, coords)
# Reference: Density should be positive and finite
assert density[0] > 0, "Density at nucleus should be positive"
assert np.isfinite(density[0]), "Density at nucleus should be finite"
assert density[0] < 100, "Density at nucleus should be physically reasonable"
class TestNumericalStability:
"""Test numerical stability of calculations."""
def test_density_far_from_molecule(self):
"""Verify density decays to zero far from molecule.
Reference: Electron density → 0 as r → ∞
"""
atom = Atom(element="H", index=1, x=0.0, y=0.0, z=0.0, charge=1.0)
shell = Shell(
type=0,
center_idx=0,
exponents=np.array([3.42525091]),
coefficients=np.array([0.15432897]),
)
wfn = Wavefunction(
atoms=[atom],
num_electrons=1.0,
charge=0,
multiplicity=2,
num_basis=1,
num_atomic_orbitals=1,
num_primitives=1,
num_shells=1,
shells=[shell],
occupations=np.array([1.0]),
coefficients=np.array([[1.0]]),
)
# Test density at various distances
# Use closer distances to avoid numerical underflow
distances = [1.0, 2.0, 3.0, 5.0]
densities = []
for r in distances:
coords = np.array([[r, 0.0, 0.0]])
density = calc_density(wfn, coords)
densities.append(density[0])
# Reference: Density should decrease with distance (monotonically or nearly so)
# At least first and last should show decay
assert (
densities[0] > densities[-1]
), f"Density should decay: {densities[0]} > {densities[-1]}"
# Reference: Density at 5 bohr should be very small
assert (
densities[-1] < 0.01
), f"Density at 5 bohr should be < 0.01, got {densities[-1]}"
def test_matrix_conditioning(self):
"""Verify overlap matrix is well-conditioned.
Reference: Overlap matrix should have reasonable condition number
"""
# For orthonormal basis, condition number = 1
# For non-orthogonal basis, should still be reasonable (< 100)
# Create two overlapping S orbitals
atoms = [
Atom(element="H", index=1, x=0.0, y=0.0, z=0.0, charge=1.0),
Atom(element="H", index=2, x=0.0, y=0.0, z=1.0, charge=1.0),
]
# This test checks if overlap matrix is well-behaved
# In practice, would need actual overlap calculation
# For now, just verify identity matrix has good conditioning
S = np.eye(2)
cond = np.linalg.cond(S)
# Reference: Condition number of identity = 1
assert cond < 10, f"Condition number should be reasonable, got {cond}"
class TestPhysicalConstraints:
"""Test that calculations satisfy physical constraints."""
def test_density_positivity(self):
"""Verify electron density is always positive.
Reference: Density is a probability density, must be ≥ 0
"""
# This is already tested in test_consistency.py
# Adding here for completeness
atom = Atom(element="H", index=1, x=0.0, y=0.0, z=0.0, charge=1.0)
shell = Shell(
type=0,
center_idx=0,
exponents=np.array([3.42525091]),
coefficients=np.array([0.15432897]),
)
wfn = Wavefunction(
atoms=[atom],
num_electrons=1.0,
charge=0,
multiplicity=2,
num_basis=1,
num_atomic_orbitals=1,
num_primitives=1,
num_shells=1,
shells=[shell],
occupations=np.array([1.0]),
coefficients=np.array([[1.0]]),
)
# Test at multiple random points
np.random.seed(42)
coords = np.random.randn(100, 3) * 2 # 100 random points
density = calc_density(wfn, coords)
# Reference: All density values must be positive
assert np.all(density > 0), "All density values should be positive"
def test_charge_conservation(self):
"""Verify total charge is conserved.
Reference: Sum of atomic charges = total molecular charge
"""
# For neutral H2, sum of atomic charges should be 0
# For H2+, sum should be +1
# This would require population analysis
# Mark as skip if not implemented
pytest.skip("Requires Mulliken/Loewdin population analysis")