Skip to content

Commit b71a499

Browse files
authored
Add analyze method for PBC-DFT methods (#559)
* Add analyze method for PBC-DFT methods * improve logging msg
1 parent 3368db5 commit b71a499

File tree

12 files changed

+178
-32
lines changed

12 files changed

+178
-32
lines changed

gpu4pyscf/pbc/scf/hf.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,31 @@ def to_cpu(self):
336336
utils.to_cpu(self, out=mf)
337337
return mf
338338

339+
def analyze(self, verbose=logger.DEBUG, with_meta_lowdin=True, **kwargs):
340+
'''Analyze the given SCF object: print orbital energies, occupancies;
341+
print orbital coefficients; Mulliken population analysis; Diople moment.
342+
'''
343+
from pyscf.scf.hf import mulliken_meta, mulliken_pop, MO_BASE
344+
log = logger.new_logger(self, verbose)
345+
cell = self.cell
346+
mo_energy = self.mo_energy.get()
347+
mo_occ = self.mo_occ.get()
348+
349+
if log.verbose >= logger.NOTE:
350+
self.dump_scf_summary(log)
351+
log.note('**** MO energy ****')
352+
for i, c in enumerate(mo_occ):
353+
log.note('MO #%-3d energy= %-18.15g occ= %g', i+MO_BASE, mo_energy[i], c)
354+
355+
s = self.get_ovlp().get()
356+
dm = self.make_rdm1().get()
357+
if with_meta_lowdin:
358+
pop = mulliken_meta(cell, dm, s=s, verbose=log)
359+
else:
360+
pop = mulliken_pop(cell, dm, s=s, verbose=log)
361+
dip = None
362+
return pop, dip
363+
339364
def normalize_dm_(mf, dm, s1e=None):
340365
'''
341366
Force density matrices integrated to the correct number of electrons.

gpu4pyscf/pbc/scf/khf.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,10 +411,11 @@ class KRHF(KSCF):
411411
check_sanity = pbchf.SCF.check_sanity
412412

413413
def get_init_guess(self, cell=None, key='minao', s1e=None):
414+
kpts = self.kpts
414415
if s1e is None:
415-
s1e = self.get_ovlp(cell)
416+
s1e = self.get_ovlp(cell, kpts)
416417
dm = mol_hf.SCF.get_init_guess(self, cell, key)
417-
nkpts = len(self.kpts)
418+
nkpts = len(kpts)
418419
if dm.ndim == 2:
419420
# dm[nao,nao] at gamma point -> dm_kpts[nkpts,nao,nao]
420421
dm = cp.repeat(dm[None,:,:], nkpts, axis=0)
@@ -446,3 +447,33 @@ def to_cpu(self):
446447
mf = khf_cpu.KRHF(self.cell)
447448
utils.to_cpu(self, out=mf)
448449
return mf
450+
451+
def analyze(self, verbose=None, **kwargs):
452+
'''Analyze the given SCF object: print orbital energies, occupancies;
453+
print orbital coefficients; Mulliken population analysis; Dipole moment
454+
'''
455+
from pyscf.pbc.scf.khf import mulliken_meta
456+
if verbose is None:
457+
verbose = self.verbose
458+
log = logger.new_logger(self, verbose)
459+
mo_energy = self.mo_energy.get()
460+
mo_occ = self.mo_occ.get()
461+
cell = self.cell
462+
kpts = self.kpts
463+
if log.verbose >= logger.NOTE:
464+
self.dump_scf_summary(log)
465+
log.note('**** MO energy ****')
466+
log.note('k-point nocc HOMO/AU LUMO/AU')
467+
for k, kpt in enumerate(cell.get_scaled_kpts(kpts)):
468+
nocc = np.count_nonzero(mo_occ[k])
469+
homo = mo_energy[k,nocc-1]
470+
lumo = mo_energy[k,nocc ]
471+
log.note('%2d (%6.3f %6.3f %6.3f) %2d %15.9f %15.9f',
472+
k, kpt[0], kpt[1], kpt[2], nocc, homo, lumo)
473+
474+
log.note('**** Population analysis for atoms in the reference cell ****')
475+
s = self.get_ovlp(kpts=kpts).get()
476+
dm = self.make_rdm1().get()
477+
pop, chg = mulliken_meta(cell, dm, kpts=kpts, s=s, verbose=verbose)
478+
dip = None
479+
return (pop, chg), dip

gpu4pyscf/pbc/scf/kuhf.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,6 @@ def __init__(self, cell, kpts=None, exxdiv='ewald'):
223223
get_occ = get_occ
224224
energy_elec = energy_elec
225225
get_rho = khf.get_rho
226-
analyze = NotImplemented
227226
canonicalize = canonicalize
228227

229228
def get_init_guess(self, cell=None, key='minao', s1e=None):
@@ -323,3 +322,37 @@ def to_cpu(self):
323322
mf = kuhf_cpu.KUHF(self.cell)
324323
utils.to_cpu(self, out=mf)
325324
return mf
325+
326+
def analyze(self, verbose=None, **kwargs):
327+
'''Analyze the given SCF object: print orbital energies, occupancies;
328+
print orbital coefficients; Mulliken population analysis; Dipole moment
329+
'''
330+
from pyscf.pbc.scf.kuhf import mulliken_meta
331+
if verbose is None:
332+
verbose = self.verbose
333+
log = logger.new_logger(self, verbose)
334+
mo_energy = self.mo_energy.get()
335+
mo_occ = self.mo_occ.get()
336+
cell = self.cell
337+
kpts = self.kpts
338+
if log.verbose >= logger.NOTE:
339+
self.dump_scf_summary(log)
340+
log.note('**** MO energy ****')
341+
log.note(' alpha | beta')
342+
log.note('k-point nocc HOMO/AU LUMO/AU | nocc HOMO/AU LUMO/AU')
343+
for k, kpt in enumerate(cell.get_scaled_kpts(kpts)):
344+
nocca = np.count_nonzero(mo_occ[0,k])
345+
noccb = np.count_nonzero(mo_occ[1,k])
346+
homoa = mo_energy[0,k,nocca-1]
347+
homob = mo_energy[1,k,noccb-1]
348+
lumoa = mo_energy[0,k,nocca ]
349+
lumob = mo_energy[1,k,noccb ]
350+
log.note('%2d (%6.3f %6.3f %6.3f) %2d %15.9f %15.9f |%2d %15.9f %15.9f',
351+
k, kpt[0], kpt[1], kpt[2], nocca, homoa, lumoa, noccb, homob, lumob)
352+
353+
log.note('**** Population analysis for atoms in the reference cell ****')
354+
s = self.get_ovlp(kpts=kpts).get()
355+
dm = self.make_rdm1().get()
356+
pop, chg = mulliken_meta(cell, dm, kpts=kpts, s=s, verbose=verbose)
357+
dip = None
358+
return (pop, chg), dip

gpu4pyscf/pbc/scf/tests/test_pbc_scf_hf.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@ class KnownValues(unittest.TestCase):
2727
def setUpClass(cls):
2828
L = 4
2929
n = 21
30-
cell = pbcgto.Cell()
31-
cell.build(unit = 'B',
32-
verbose = 7,
33-
output = '/dev/null',
34-
a = ((L,0,0),(0,L,0),(0,0,L)),
35-
mesh = [n,n,n],
36-
atom = [['He', (L/2.-.5,L/2.,L/2.-.5)],
37-
['He', (L/2. ,L/2.,L/2.+.5)]],
38-
basis = { 'He': [[0, (0.8, 1.0)],
39-
[0, (1.0, 1.0)],
40-
[0, (1.2, 1.0)]]})
30+
cell = pbcgto.M(
31+
unit = 'B',
32+
verbose = 7,
33+
output = '/dev/null',
34+
a = ((L,0,0),(0,L,0),(0,0,L)),
35+
mesh = [n,n,n],
36+
atom = [['He', (L/2.-.5,L/2.,L/2.-.5)],
37+
['He', (L/2. ,L/2.,L/2.+.5)]],
38+
basis = { 'He': [[0, (0.8, 1.0)],
39+
[0, (1.0, 1.0)],
40+
[0, (1.2, 1.0)]]})
4141
cls.cell = cell
4242

4343
@classmethod
@@ -49,8 +49,13 @@ def test_rhf_exx_ewald(self):
4949
mf = scf.RHF(cell, exxdiv='ewald').run()
5050
self.assertAlmostEqual(mf.e_tot, -4.3511582284698633, 7)
5151
self.assertTrue(mf.mo_coeff.dtype == np.double)
52+
pop = mf.analyze()[0][0]
53+
self.assertAlmostEqual(lib.fp(pop), 0.011047586674983092, 5)
54+
5255
kmf = scf.KRHF(cell, [[0,0,0]], exxdiv='ewald').run()
5356
self.assertAlmostEqual(mf.e_tot, kmf.e_tot, 8)
57+
pop = kmf.analyze()[0][0]
58+
self.assertAlmostEqual(lib.fp(pop), 0.011047586674983092, 5)
5459

5560
# test bands
5661
np.random.seed(1)

gpu4pyscf/pbc/scf/tests/test_pbc_scf_uhf.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ def test_kuhf_bands(self):
5252
kmf_cpu = kmf.to_cpu().run()
5353
self.assertAlmostEqual(kmf.e_tot, kmf_cpu.e_tot, 8)
5454
self.assertAlmostEqual(kmf.e_tot, -4.021029656152094, 8)
55+
pop = kmf.analyze()[0][0]
56+
self.assertAlmostEqual(lib.fp(pop), 0.02897067698093582, 5)
5557

5658
np.random.seed(1)
5759
kpts_bands = np.random.random((1,3))
@@ -64,6 +66,8 @@ def test_uhf_bands(self):
6466
mf_cpu = mf.to_cpu().run()
6567
self.assertAlmostEqual(mf.e_tot, mf_cpu.e_tot, 8)
6668
self.assertAlmostEqual(mf.e_tot, -3.9546467710639632, 7)
69+
pop = mf.analyze()[0][0]
70+
print(lib.fp(pop), -0.04691820429296646)
6771

6872
np.random.seed(1)
6973
kpts_bands = np.random.random((1,3))

gpu4pyscf/pbc/scf/uhf.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ def get_init_guess(self, cell=None, key='minao', s1e=None):
113113
energy_elec = mol_uhf.UHF.energy_elec
114114
_finalize = mol_uhf.UHF._finalize
115115
get_rho = pbchf.get_rho
116-
analyze = NotImplemented
117116
mulliken_pop = NotImplemented
118117
mulliken_meta = NotImplemented
119118
mulliken_meta_spin = NotImplemented
@@ -135,3 +134,33 @@ def to_cpu(self):
135134
mf = uhf_cpu.UHF(self.cell)
136135
utils.to_cpu(self, out=mf)
137136
return mf
137+
138+
def analyze(self, verbose=logger.DEBUG, with_meta_lowdin=True, **kwargs):
139+
'''Analyze the given SCF object: print orbital energies, occupancies;
140+
print orbital coefficients; Mulliken population analysis; Diople moment.
141+
'''
142+
from pyscf.scf.hf import mulliken_meta, mulliken_pop, MO_BASE
143+
log = logger.new_logger(self, verbose)
144+
cell = self.cell
145+
mo_energy = self.mo_energy.get()
146+
mo_occ = self.mo_occ.get()
147+
148+
if log.verbose >= logger.NOTE:
149+
self.dump_scf_summary(log)
150+
mo_e_a, mo_e_b = mo_energy
151+
mo_occ_a, mo_occ_b = mo_occ
152+
nmo = len(mo_occ_a)
153+
log.note('**** MO energy ****')
154+
log.note(' alpha | beta alpha | beta')
155+
for i in range(nmo):
156+
log.note('MO #%-3d energy= %-18.15g | %-18.15g occ= %g | %g',
157+
i+MO_BASE, mo_e_a[i], mo_e_b[i], mo_occ_a[i], mo_occ_b[i])
158+
159+
s = self.get_ovlp().get()
160+
dm = self.make_rdm1().get()
161+
if with_meta_lowdin:
162+
pop = mulliken_meta(cell, dm, s=s, verbose=log)
163+
else:
164+
pop = mulliken_pop(cell, dm, s=s, verbose=log)
165+
dip = None
166+
return pop, dip

gpu4pyscf/scf/hf.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -699,8 +699,8 @@ def eig(self, fock, s):
699699
init_direct_scf = NotImplemented
700700
get_jk = _get_jk
701701
get_veff = NotImplemented
702-
mulliken_meta = hf_cpu.SCF.mulliken_meta
703-
pop = hf_cpu.SCF.pop
702+
mulliken_meta = pop = NotImplemented
703+
mulliken_pop = NotImplemented
704704
_is_mem_enough = NotImplemented
705705
density_fit = NotImplemented
706706
newton = NotImplemented
@@ -717,8 +717,7 @@ def eig(self, fock, s):
717717
to_gks = NotImplemented
718718
to_ks = NotImplemented
719719
canonicalize = NotImplemented
720-
mulliken_pop = NotImplemented
721-
mulliken_meta = NotImplemented
720+
dump_scf_summary = hf_cpu.dump_scf_summary
722721

723722
smearing = smearing
724723

@@ -744,13 +743,13 @@ def dip_moment(self, mol=None, dm=None, unit='Debye', origin=None,
744743
verbose=logger.NOTE):
745744
if mol is None: mol = self.mol
746745
if dm is None: dm = self.make_rdm1()
747-
return hf_cpu.dip_moment(mol, dm.get(), unit, origin, verbose)
746+
return hf_cpu.dip_moment(mol, cupy.asnumpy(dm), unit, origin, verbose)
748747

749748
def quad_moment(self, mol=None, dm=None, unit='DebyeAngstrom', origin=None,
750749
verbose=logger.NOTE):
751750
if mol is None: mol = self.mol
752751
if dm is None: dm = self.make_rdm1()
753-
return hf_cpu.quad_moment(mol, dm.get(), unit, origin, verbose)
752+
return hf_cpu.quad_moment(mol, cupy.asnumpy(dm), unit, origin, verbose)
754753

755754
def remove_soscf(self):
756755
lib.logger.warn('remove_soscf has no effect in current version')

gpu4pyscf/scf/rohf.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ class ROHF(hf.RHF):
9797
to_uks = NotImplemented
9898
to_gks = NotImplemented
9999
to_ks = NotImplemented
100-
analyze = NotImplemented
101100
stability = NotImplemented
102101
mulliken_pop = NotImplemented
103102
mulliken_meta = NotImplemented

gpu4pyscf/scf/tests/test_rhf.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
from pyscf import lib
2121
from gpu4pyscf import scf
2222

23-
mol = pyscf.M(
23+
def setUpModule():
24+
global mol, mol1
25+
mol = pyscf.M(
2426
atom='''
2527
C -0.65830719, 0.61123287, -0.00800148
2628
C 0.73685281, 0.61123287, -0.00800148
@@ -32,7 +34,7 @@
3234
output = '/dev/null'
3335
)
3436

35-
mol1 = pyscf.M(
37+
mol1 = pyscf.M(
3638
atom='''
3739
C -1.20806619, -0.34108413, -0.00755148
3840
C 1.28636081, -0.34128013, -0.00668648
@@ -249,8 +251,17 @@ def test_rhf_d3(self):
249251
mf = scf.RHF(mol)
250252
mf.disp = 'd3bj'
251253
e_tot = mf.kernel()
254+
255+
#mf_ref = mol.RHF()
256+
#mf_ref.disp = 'd3bj'
257+
#e_ref = mf_ref.kernel()
258+
#chg_ref = mf_ref.analyze()[0][1]
252259
e_ref = -151.1150439066
253-
assert np.abs(e_tot - e_ref) < 1e-5
260+
assert abs(e_tot - e_ref) < 1e-8
261+
262+
chg = mf.analyze()[0][1]
263+
#assert abs(chg - chg_ref).max() < 1e-5
264+
self.assertAlmostEqual(lib.fp(chg), -0.003225958206417059, 5)
254265

255266
def test_rhf_d4(self):
256267
mf = scf.RHF(mol)
@@ -299,12 +310,15 @@ def test_rohf(self):
299310
O 0.00000000 0.00000000 0.60539399
300311
H 0.00000000 0.93467313 -1.18217476
301312
H 0.00000000 -0.93467313 -1.18217476''',
302-
charge=1, spin=1, unit='B')
313+
charge=1, spin=1, unit='B', verbose=5, output='/dev/null')
303314
mf = mol.ROHF().to_gpu().run()
304315
self.assertAlmostEqual(mf.e_tot, -107.61304925181142, 8)
305316
ref = mf.to_cpu().run()
306317
self.assertAlmostEqual(mf.e_tot, ref.e_tot, 8)
307318

319+
chg = mf.analyze()[0][1]
320+
self.assertAlmostEqual(lib.fp(chg), -0.0705568646397904, 5)
321+
308322
# TODO:
309323
#test analyze
310324
#test mulliken_pop

gpu4pyscf/scf/tests/test_uhf.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@
2222
from gpu4pyscf.lib.multi_gpu import num_devices
2323
import pytest
2424

25-
mol = pyscf.M(
25+
def setUpModule():
26+
global mol, mol1
27+
mol = pyscf.M(
2628
atom='''
2729
C -0.65830719, 0.61123287, -0.00800148
2830
C 0.73685281, 0.61123287, -0.00800148
@@ -35,7 +37,7 @@
3537
output = '/dev/null'
3638
)
3739

38-
mol1 = pyscf.M(
40+
mol1 = pyscf.M(
3941
atom='''
4042
C -1.20806619, -0.34108413, -0.00755148
4143
C 1.28636081, -0.34128013, -0.00668648
@@ -258,9 +260,12 @@ def test_uhf_scf_fast(self):
258260
mol1 = mol.copy()
259261
mol1.basis = 'sto3g'
260262
mol1.build(False, False)
261-
e_tot = mol1.UHF().to_gpu().kernel()
263+
mf = mol1.UHF().to_gpu()
264+
e_tot = mf.kernel()
262265
e_ref = -148.8650361770461
263266
assert np.abs(e_tot - e_ref) < 1e-5
267+
chg = mf.analyze()[0][1]
268+
self.assertAlmostEqual(lib.fp(chg), 0.022191785654920748, 5)
264269

265270
@pytest.mark.slow
266271
def test_uhf_d3bj(self):

0 commit comments

Comments
 (0)