Source code for gpaw.forces

import numpy as np

from ase.units import Hartree, Bohr

from gpaw.directmin.tools import get_n_occ
from gpaw.utilities import unpack_hermitian
from gpaw.xc.hybrid import HybridXCBase


[docs]def calculate_forces(wfs, dens, ham, log=None): """Return the atomic forces.""" assert not isinstance(ham.xc, HybridXCBase) assert not ham.xc.name.startswith('GLLB') func_name = None if hasattr(wfs.eigensolver, 'dm_helper'): func_name = getattr(wfs.eigensolver.dm_helper.func, 'name', None) elif hasattr(wfs.eigensolver, 'odd'): func_name = getattr(wfs.eigensolver.odd, 'name', None) if func_name == 'PZ-SIC': if wfs.mode == 'fd' or wfs.mode == 'pw': return calculate_forces_using_non_diag_lagr_matrix( wfs, dens, ham, log) elif wfs.mode == 'lcao': for kpt in wfs.kpt_u: kpt.rho_MM = \ wfs.calculate_density_matrix(kpt.f_n, kpt.C_nM) natoms = len(wfs.setups) # Force from projector functions (and basis set): F_wfs_av = np.zeros((natoms, 3)) wfs.calculate_forces(ham, F_wfs_av) wfs.gd.comm.sum(F_wfs_av, 0) F_ham_av = np.zeros((natoms, 3)) try: # ODD functionals need force corrections for each spin correction = ham.xc.setup_force_corrections except AttributeError: pass else: correction(F_ham_av) ham.calculate_forces(dens, F_ham_av) F_av = F_ham_av + F_wfs_av wfs.world.broadcast(F_av, 0) if func_name == 'PZ-SIC' and wfs.mode == 'lcao': F_av += wfs.eigensolver.dm_helper.func.get_odd_corrections_to_forces( wfs, dens) for kpt in wfs.kpt_u: # need to re-set rho_MM otherwise it will be used # it's probably better to in wfs.reset, but # when position changes wfs.reset is not called kpt.rho_MM = None F_av = wfs.kd.symmetry.symmetrize_forces(F_av) if log: log('\nForces in eV/Ang:') c = Hartree / Bohr for a, setup in enumerate(wfs.setups): log('%3d %-2s %10.5f %10.5f %10.5f' % ((a, setup.symbol) + tuple(F_av[a] * c))) log() return F_av
def calculate_forces_using_non_diag_lagr_matrix(wfs, dens, ham, log=None): natoms = len(wfs.setups) F_wfs_av = np.zeros((natoms, 3)) esolv = wfs.eigensolver grad_knG = esolv.get_gradients_2(ham, wfs) if 'SIC' in esolv.odd.name: for kpt in wfs.kpt_u: esolv.odd.get_energy_and_gradients_kpt( wfs, kpt, grad_knG, esolv.iloop.U_k, add_grad=True) for kpt in wfs.kpt_u: k = esolv.n_kps * kpt.s + kpt.q n_occ = get_n_occ(kpt)[0] lamb = wfs.integrate( kpt.psit_nG[:n_occ], grad_knG[k][:n_occ], True) P_ani = kpt.P_ani dP_aniv = wfs.pt.dict(n_occ, derivative=True) wfs.pt.derivative(kpt.psit_nG[:n_occ], dP_aniv, kpt.q) dH_asp = ham.dH_asp for a, dP_niv in dP_aniv.items(): dP_niv = dP_niv.conj() dO_ii = wfs.setups[a].dO_ii P_ni = P_ani[a][:n_occ] dS_nkv = np.einsum('niv,ij,kj->nkv', dP_niv, dO_ii, P_ni) dS_nkv = \ (dS_nkv + np.transpose(dS_nkv, (1, 0, 2)).conj()) # '-' becuase there is extra minus in 'pt derivative' F_wfs_av[a] -= np.einsum('kn,nkv->v', lamb, dS_nkv).real dH_ii = unpack_hermitian(dH_asp[a][kpt.s]) dh_nv = np.einsum('niv,ij,nj->nv', dP_niv, dH_ii, P_ni) # '+' becuase there is extra minus in 'pt derivative' F_wfs_av[a] += np.einsum('n,nv->v', kpt.f_n[:n_occ], 2.0 * dh_nv.real) if 'SIC' in esolv.odd.name: esolv.odd.get_odd_corrections_to_forces(F_wfs_av, wfs, kpt) wfs.kd.comm.sum(F_wfs_av, 0) wfs.gd.comm.sum(F_wfs_av, 0) F_ham_av = np.zeros((natoms, 3)) ham.calculate_forces(dens, F_ham_av) F_av = F_ham_av + F_wfs_av wfs.world.broadcast(F_av, 0) F_av = wfs.kd.symmetry.symmetrize_forces(F_av) if log: log('\nForces in eV/Ang:') c = Hartree / Bohr for a, setup in enumerate(wfs.setups): log('%3d %-2s %10.5f %10.5f %10.5f' % ((a, setup.symbol) + tuple(F_av[a] * c))) log() return F_av