Skip to content

Commit be9c52a

Browse files
committed
examples: add N2 GASSCF energy scanner
1 parent 12f534c commit be9c52a

2 files changed

Lines changed: 131 additions & 0 deletions

File tree

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/usr/bin/env python
2+
#
3+
# Author: Yi Deng <yideng@uchicago.edu>
4+
#
5+
6+
"""Singlet N2 GASSCF energy scanner near the equilibrium bond length."""
7+
8+
from pathlib import Path
9+
10+
import numpy
11+
12+
from pyscf import gto, lib, scf
13+
from pyscf.mcscf import gasscf
14+
15+
16+
# OpenMolcas reference input:
17+
#
18+
# &GATEWAY
19+
# ... (Geometry and basis are stored in the .npz file)
20+
# noCD
21+
#
22+
# &SEWARD
23+
#
24+
# &RASSCF
25+
#
26+
# LUMORB
27+
# FILEORB = n2_guessorb.INPORB
28+
#
29+
# Spin = 1
30+
# Symmetry = 1
31+
#
32+
# Nactel = 10 0 0
33+
# Inactive = 2
34+
#
35+
# GASSCF
36+
# 3
37+
# 2
38+
# 2 4
39+
# 4
40+
# 7 9
41+
# 2
42+
# 10 10
43+
#
44+
# CIRoot = 1 1 1
45+
# ITERations = 200 100
46+
# CIMX = 200
47+
#
48+
# OpenMolcas output:
49+
#
50+
# :: RASSCF root number 1 Total energy: -109.13534823
51+
52+
53+
openmolcas_energy = -109.13534823
54+
55+
here = Path(__file__).resolve().parent
56+
with numpy.load(here / "data/n2_scanner_mo.npz", allow_pickle=False) as data:
57+
mol = gto.loads(data["mol"].item())
58+
mo_coeff = data["mo_coeff"]
59+
60+
mol.max_memory = 8000 # MB
61+
mol.verbose = 4
62+
63+
# One-cycle RHF for mf generation.
64+
mf = scf.RHF(mol)
65+
mf.max_cycle = 1
66+
mf.kernel()
67+
# Allow the RHF reference to converge at each scanner geometry.
68+
mf.max_cycle = 50
69+
mf.mo_coeff = mo_coeff
70+
71+
mc = gasscf.GASSCF(
72+
mf,
73+
ncas=8,
74+
nelecas=(5,5),
75+
ncore=2,
76+
gas_orbs=(2,4,2),
77+
gas_restr=((2,4),(7,9),(10,10)),
78+
gas_restr_type="cumulative-occ",
79+
)
80+
81+
mc.verbose = 4
82+
mc.max_cycle_macro = 80
83+
mc.conv_tol = 1e-9
84+
mc.conv_tol_grad = 1e-5
85+
mc.fcisolver.max_cycle = 200
86+
mc.fcisolver.max_space = 30
87+
mc.fcisolver.conv_tol = 1e-11
88+
mc.fcisolver.conv_tol_residual = 1e-7
89+
90+
mc.kernel(mo_coeff)
91+
92+
ss, _ = mc.spin_square()
93+
energy = float(mc.e_tot)
94+
95+
coords = mol.atom_coords(unit="Bohr")
96+
bond = coords[1] - coords[0]
97+
r0 = numpy.linalg.norm(bond) * lib.param.BOHR
98+
direction = bond / numpy.linalg.norm(bond)
99+
rows = [(r0, energy, ss, bool(mc.converged))]
100+
101+
print()
102+
print("OpenMolcas/PySCF GASSCF comparison at the starting geometry")
103+
print(f"R(N-N) / Angstrom : {r0:.7f}")
104+
print(f"E(OpenMolcas) / Eh : {openmolcas_energy:.12f}")
105+
print(f"E(PySCF, physical) / Eh: {energy:.12f}")
106+
print(f"Difference / Eh : {energy - openmolcas_energy:+.3e}")
107+
108+
# The scanner projects the previous orbitals and reuses the previous CI guess.
109+
# Keep the atom order, basis, electron counts, GAS definition and spin fixed.
110+
scanner = mc.as_scanner()
111+
for distance in (r0 - 0.001, r0 + 0.001):
112+
new_coords = coords.copy()
113+
new_coords[1] = coords[0] + (distance / lib.param.BOHR) * direction
114+
new_mol = mol.set_geom_(new_coords, unit="Bohr", inplace=False)
115+
scanner(new_mol)
116+
energy = float(scanner.e_tot)
117+
ss, _ = scanner.spin_square()
118+
rows.append((distance, energy, ss, bool(scanner.converged)))
119+
120+
print()
121+
print("N2 singlet GASSCF energy scan")
122+
print("R / Angstrom E(physical) / Eh <S^2> converged")
123+
for distance, energy, ss, converged in sorted(rows):
124+
print(f"{distance:12.7f} {energy:22.12f} {ss:12.8f} {converged}")
125+
126+
# Example output (last digits may depend on platform):
127+
# R / Angstrom E(physical) / Eh <S^2> converged
128+
# 1.0967000 -109.135311779472 -0.00000000 True
129+
# 1.0977000 -109.135348228725 -0.00000000 True
130+
# 1.0987000 -109.135379223650 -0.00000000 True
131+
29.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)