Skip to content

Commit d486bea

Browse files
authored
Add utils directory with script for fixing molden files with incorrect orbital normalization (#5)
1 parent b73df45 commit d486bea

2 files changed

Lines changed: 173 additions & 0 deletions

File tree

utils/README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Utilities
2+
3+
## fix_molden_normalization.py
4+
5+
`orca_2mkl` seems to have a bug where certain molecules get written with incorrect orbital normalization.
6+
7+
When you hit an error like:
8+
9+
```
10+
Error loading wavefunction file: Could not correct the data read from orca.molden.input. The molden or mkl file you are trying to load contains errors. Please report this problem to Toon.Verstraelen@UGent.be, so he can fix it.
11+
```
12+
13+
Use this script to fix the normalization:
14+
15+
```bash
16+
python utils/fix_molden_normalization.py orca.molden.input
17+
```
18+
19+
This creates `fixed_orca.molden.input` with properly normalized orbitals that PyXDM can process.
20+
21+
### Technical details
22+
23+
HORTON tries 4 automatic fixes (ORCA-specific, PSI4, Turbomole, general renormalization), but all fail for this bug. The issue requires both fixing the basis set contractions (ORCA fix) AND renormalizing the MO coefficients. HORTON only attempts basis fixes but checks the final orbital normalization, which still fails. This script applies HORTON's ORCA basis fix first, then renormalizes each orbital coefficient against the corrected overlap matrix.

utils/fix_molden_normalization.py

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
"""
2+
Fix molden files with incorrect orbital normalization.
3+
4+
orca_2mkl seems to have a bug where certain molecules get written with
5+
incorrect basis set normalization AND orbital coefficient scaling.
6+
This causes HORTON to fail with "Could not correct the data read from..." errors.
7+
8+
This script applies ORCA basis fix then renormalizes MO coefficients.
9+
"""
10+
11+
import sys
12+
from pathlib import Path
13+
14+
import numpy as np
15+
16+
try:
17+
import horton.io.molden as molden_module
18+
from horton import IOData
19+
except ImportError:
20+
print("Error: HORTON not installed")
21+
sys.exit(1)
22+
23+
24+
def fix_molden_file(input_file, output_file=None):
25+
"""
26+
Load molden file, apply ORCA basis fix, renormalize orbitals, and save.
27+
28+
Parameters
29+
----------
30+
input_file : str
31+
Path to input molden file
32+
output_file : str, optional
33+
Path to output molden file. Defaults to fixed_input_file
34+
35+
Returns
36+
-------
37+
str
38+
Path to the output file
39+
"""
40+
if output_file is None:
41+
output_file = "fixed_" + input_file
42+
43+
print(f"Loading {input_file}...")
44+
45+
# Temporarily patch HORTON to not raise errors on bad normalization
46+
original_fix = molden_module._fix_molden_from_buggy_codes
47+
48+
def patched_fix(result, filename):
49+
try:
50+
original_fix(result, filename)
51+
except IOError:
52+
print(" [Note: HORTON's automatic fixes failed, proceeding to manual fix...]")
53+
# Apply ORCA basis fix manually
54+
from horton import GOBasis
55+
56+
obasis = result["obasis"]
57+
orca_con_coeffs = molden_module._get_fixed_con_coeffs(obasis, "orca")
58+
if orca_con_coeffs is not None:
59+
orca_obasis = GOBasis(obasis.centers, obasis.shell_map, obasis.nprims, obasis.shell_types, obasis.alphas, orca_con_coeffs)
60+
result["obasis"] = orca_obasis
61+
print(" Applied ORCA basis set fix")
62+
63+
molden_module._fix_molden_from_buggy_codes = patched_fix
64+
65+
try:
66+
result = molden_module.load_molden(input_file)
67+
finally:
68+
molden_module._fix_molden_from_buggy_codes = original_fix
69+
70+
obasis = result["obasis"]
71+
orb_alpha = result["orb_alpha"]
72+
orb_beta = result.get("orb_beta")
73+
74+
print(f" {obasis.nbasis} basis functions, {orb_alpha.nfn} alpha orbitals")
75+
76+
# Compute overlap matrix
77+
olp = obasis.compute_overlap()
78+
79+
# Renormalize alpha orbitals
80+
print(" Renormalizing alpha orbitals...")
81+
max_norm = 0.0
82+
for i in range(orb_alpha.nfn):
83+
c = orb_alpha._coeffs[:, i]
84+
norm_sq = np.dot(c, np.dot(olp, c))
85+
norm = np.sqrt(norm_sq)
86+
max_norm = max(max_norm, norm)
87+
orb_alpha._coeffs[:, i] /= norm
88+
89+
print(f" Max initial norm: {max_norm:.6f} (should be ~1.0 for correct files)")
90+
91+
# Renormalize beta orbitals if present
92+
if orb_beta is not None:
93+
print(" Renormalizing beta orbitals...")
94+
for i in range(orb_beta.nfn):
95+
c = orb_beta._coeffs[:, i]
96+
norm_sq = np.dot(c, np.dot(olp, c))
97+
norm = np.sqrt(norm_sq)
98+
orb_beta._coeffs[:, i] /= norm
99+
100+
# Verify correction
101+
print(" Verifying normalization...")
102+
max_error = 0.0
103+
for i in range(orb_alpha.nfn):
104+
c = orb_alpha._coeffs[:, i]
105+
norm = np.dot(c, np.dot(olp, c))
106+
error = abs(norm - 1.0)
107+
max_error = max(max_error, error)
108+
109+
print(f" Max error: {max_error:.2e} (target: < 1e-4)")
110+
111+
# Save corrected molden file
112+
print(f"\nSaving to {output_file}...")
113+
114+
kwargs = {
115+
"coordinates": result["coordinates"],
116+
"numbers": result["numbers"],
117+
"obasis": result["obasis"],
118+
"orb_alpha": orb_alpha,
119+
}
120+
121+
if orb_beta is not None:
122+
kwargs["orb_beta"] = orb_beta
123+
124+
iodata = IOData(**kwargs)
125+
126+
# Add optional fields
127+
if "energy" in result:
128+
iodata.energy = result["energy"]
129+
if "permutation" in result:
130+
iodata.permutation = result["permutation"]
131+
132+
iodata.to_file(output_file)
133+
134+
print("Done. Use the fixed file:")
135+
print(f" pyxdm {output_file} --scheme mbis")
136+
137+
return output_file
138+
139+
140+
if __name__ == "__main__":
141+
if len(sys.argv) < 2:
142+
print("Usage: python fix_molden_normalization.py <input> [output]")
143+
print("\nExample:")
144+
print(" python fix_molden_normalization.py orca.molden.input")
145+
sys.exit(1)
146+
147+
input_file = sys.argv[1]
148+
output_file = sys.argv[2] if len(sys.argv) > 2 else None
149+
150+
fix_molden_file(input_file, output_file)

0 commit comments

Comments
 (0)