Source code for pyscf.pbc.scf.uhf

#!/usr/bin/env python
# Copyright 2014-2019 The PySCF Developers. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Author: Qiming Sun <osirpt.sun@gmail.com>
#

'''
Unrestricted Hartree-Fock for periodic systems at a single k-point

See Also:
    pyscf/pbc/scf/khf.py : Hartree-Fock for periodic systems with k-point sampling
'''

import numpy as np
from pyscf import lib
from pyscf.lib import logger
from pyscf.scf import uhf as mol_uhf
from pyscf.pbc.scf import hf as pbchf
from pyscf.pbc.scf import addons
from pyscf.pbc.scf import chkfile
from pyscf import __config__


[docs] def init_guess_by_chkfile(cell, chkfile_name, project=None, kpt=None): '''Read the HF results from checkpoint file and make the density matrix for UHF initial guess. Returns: Density matrix, (nao,nao) ndarray ''' from pyscf import gto chk_cell, scf_rec = chkfile.load_scf(chkfile_name) if project is None: project = not gto.same_basis_set(chk_cell, cell) mo = scf_rec['mo_coeff'] mo_occ = scf_rec['mo_occ'] if kpt is None: kpt = np.zeros(3) if 'kpt' in scf_rec: chk_kpt = scf_rec['kpt'] elif 'kpts' in scf_rec: kpts = scf_rec['kpts'] # the closest kpt from KRHF results where = np.argmin(lib.norm(kpts-kpt, axis=1)) chk_kpt = kpts[where] if getattr(mo[0], 'ndim', None) == 2: # KRHF mo = mo[where] mo_occ = mo_occ[where] else: # KUHF mo = [mo[0][where], mo[1][where]] mo_occ = [mo_occ[0][where], mo_occ[1][where]] else: # from molecular code chk_kpt = np.zeros(3) if project: s = cell.pbc_intor('int1e_ovlp', kpt=kpt) def fproj(mo): if project: mo = addons.project_mo_nr2nr(chk_cell, mo, cell, chk_kpt-kpt) norm = np.einsum('pi,pi->i', mo.conj(), s.dot(mo)) mo /= np.sqrt(norm) return mo if getattr(mo, 'ndim', None) == 2: mo = fproj(mo) mo_occa = (mo_occ>1e-8).astype(np.double) mo_occb = mo_occ - mo_occa dm = mol_uhf.make_rdm1([mo,mo], [mo_occa,mo_occb]) else: # UHF dm = mol_uhf.make_rdm1([fproj(mo[0]),fproj(mo[1])], mo_occ) # Real DM for gamma point if kpt is None or np.allclose(kpt, 0): dm = dm.real return dm
[docs] def dip_moment(cell, dm, unit='Debye', verbose=logger.NOTE, grids=None, rho=None, kpt=np.zeros(3)): ''' Dipole moment in the cell. Args: cell : an instance of :class:`Cell` dm_kpts (a list of ndarrays) : density matrices of k-points Return: A list: the dipole moment on x, y and z components ''' dm = dm[0] + dm[1] return pbchf.dip_moment(cell, dm, unit, verbose, grids, rho, kpt)
get_rho = pbchf.get_rho
[docs] class UHF(pbchf.SCF): '''UHF class for PBCs. ''' init_guess_breaksym = getattr(__config__, 'scf_uhf_init_guess_breaksym', 1) _keys = {"init_guess_breaksym"} init_guess_by_minao = mol_uhf.UHF.init_guess_by_minao init_guess_by_atom = mol_uhf.UHF.init_guess_by_atom init_guess_by_huckel = mol_uhf.UHF.init_guess_by_huckel init_guess_by_mod_huckel = mol_uhf.UHF.init_guess_by_mod_huckel eig = mol_uhf.UHF.eig get_fock = mol_uhf.UHF.get_fock get_grad = mol_uhf.UHF.get_grad get_occ = mol_uhf.UHF.get_occ make_rdm1 = mol_uhf.UHF.make_rdm1 make_rdm2 = mol_uhf.UHF.make_rdm2 energy_elec = mol_uhf.UHF.energy_elec _finalize = mol_uhf.UHF._finalize get_rho = get_rho analyze = mol_uhf.UHF.analyze mulliken_pop = mol_uhf.UHF.mulliken_pop mulliken_meta = mol_uhf.UHF.mulliken_meta mulliken_meta_spin = mol_uhf.UHF.mulliken_meta_spin canonicalize = mol_uhf.UHF.canonicalize spin_square = mol_uhf.UHF.spin_square stability = mol_uhf.UHF.stability to_gpu = lib.to_gpu def __init__(self, cell, kpt=np.zeros(3), exxdiv=getattr(__config__, 'pbc_scf_SCF_exxdiv', 'ewald')): pbchf.SCF.__init__(self, cell, kpt, exxdiv) self.nelec = None @property def nelec(self): if self._nelec is not None: return self._nelec else: cell = self.cell ne = cell.nelectron nalpha = (ne + cell.spin) // 2 nbeta = nalpha - cell.spin if nalpha + nbeta != ne: raise RuntimeError('Electron number %d and spin %d are not consistent\n' 'Note cell.spin = 2S = Nalpha - Nbeta, not 2S+1' % (ne, cell.spin)) return nalpha, nbeta @nelec.setter def nelec(self, x): self._nelec = x
[docs] def dump_flags(self, verbose=None): pbchf.SCF.dump_flags(self, verbose) logger.info(self, 'number of electrons per cell ' 'alpha = %d beta = %d', *self.nelec) return self
[docs] def get_veff(self, cell=None, dm=None, dm_last=0, vhf_last=0, hermi=1, kpt=None, kpts_band=None): if cell is None: cell = self.cell if dm is None: dm = self.make_rdm1() if kpt is None: kpt = self.kpt if isinstance(dm, np.ndarray) and dm.ndim == 2: dm = np.asarray((dm*.5,dm*.5)) vj, vk = self.get_jk(cell, dm, hermi, kpt, kpts_band) vhf = vj[0] + vj[1] - vk return vhf
[docs] def get_bands(self, kpts_band, cell=None, dm=None, kpt=None): '''Get energy bands at the given (arbitrary) 'band' k-points. Returns: mo_energy : (nmo,) ndarray or a list of (nmo,) ndarray Bands energies E_n(k) mo_coeff : (nao, nmo) ndarray or a list of (nao,nmo) ndarray Band orbitals psi_n(k) ''' if cell is None: cell = self.cell if dm is None: dm = self.make_rdm1() if kpt is None: kpt = self.kpt kpts_band = np.asarray(kpts_band) single_kpt_band = (getattr(kpts_band, 'ndim', None) == 1) kpts_band = kpts_band.reshape(-1,3) fock = self.get_hcore(cell, kpts_band) focka, fockb = fock + self.get_veff(cell, dm, kpt=kpt, kpts_band=kpts_band) s1e = self.get_ovlp(cell, kpts_band) nkpts = len(kpts_band) e_a = [] e_b = [] c_a = [] c_b = [] for k in range(nkpts): e, c = self.eig((focka[k], fockb[k]), s1e[k]) e_a.append(e[0]) e_b.append(e[1]) c_a.append(c[0]) c_b.append(c[1]) mo_energy = (e_a, e_b) mo_coeff = (c_a, c_b) if single_kpt_band: mo_energy = (mo_energy[0][0], mo_energy[1][0]) mo_coeff = (mo_coeff[0][0], mo_coeff[1][0]) return mo_energy, mo_coeff
[docs] @lib.with_doc(dip_moment.__doc__) def dip_moment(self, cell=None, dm=None, unit='Debye', verbose=logger.NOTE, **kwargs): if cell is None: cell = self.cell if dm is None: dm = self.make_rdm1() rho = kwargs.pop('rho', None) if rho is None: rho = self.get_rho(dm) return dip_moment(cell, dm, unit, verbose, rho=rho, kpt=self.kpt, **kwargs)
[docs] def get_init_guess(self, cell=None, key='minao', s1e=None): if cell is None: cell = self.cell if s1e is None: s1e = self.get_ovlp(cell) dm = mol_uhf.UHF.get_init_guess(self, cell, key) ne = np.einsum('xij,ji->x', dm, s1e).real nelec = self.nelec if np.any(abs(ne - nelec) > 0.01): logger.debug(self, 'Big error detected in the electron number ' 'of initial guess density matrix (Ne/cell = %s)!\n' ' This can cause huge error in Fock matrix and ' 'lead to instability in SCF for low-dimensional ' 'systems.\n DM is normalized wrt the number ' 'of electrons %s', ne, nelec) dm *= (nelec / ne).reshape(2,1,1) return dm
[docs] def init_guess_by_1e(self, cell=None): if cell is None: cell = self.cell if cell.dimension < 3: logger.warn(self, 'Hcore initial guess is not recommended in ' 'the SCF of low-dimensional systems.') return mol_uhf.UHF.init_guess_by_1e(self, cell)
[docs] def init_guess_by_chkfile(self, chk=None, project=True, kpt=None): if chk is None: chk = self.chkfile if kpt is None: kpt = self.kpt return init_guess_by_chkfile(self.cell, chk, project, kpt)
[docs] def to_ks(self, xc='HF'): '''Convert to RKS object. ''' from pyscf.pbc import dft return self._transfer_attrs_(dft.UKS(self.cell, self.kpt, xc=xc))
[docs] def convert_from_(self, mf): '''Convert given mean-field object to UHF''' addons.convert_to_uhf(mf, self) return self