Skip to content

Commit 70c3060

Browse files
Merge pull request #422 from ManexAlkorta/master
Push get_free_energy_hessian_dev() and environment
2 parents 25a0452 + 4012943 commit 70c3060

22 files changed

Lines changed: 5992 additions & 6 deletions

Modules/Classify.py

Lines changed: 784 additions & 0 deletions
Large diffs are not rendered by default.

Modules/Ensemble.py

Lines changed: 270 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import numpy as np
66
import time
77
#from scipy.special import tanh, sinh, cosh
8+
9+
import sscha.Classify as Classify
10+
import sscha.qClassify as qClassify
811
try:
912
from typing import Union
1013
from flare.bffs.gp import GaussianProcess
@@ -3817,7 +3820,274 @@ def get_free_energy_hessian(self, include_v4 = False, get_full_hessian = True, v
38173820
return dyn_hessian, d3* 2.0 # Ha to Ry
38183821
return dyn_hessian
38193822

3823+
def get_free_energy_hessian_dev(self, include_v4 = False, do_scf = True, eps = 1e-6, alpha_mix=0.3, get_full_hessian = True, verbose = False):
3824+
"""
3825+
Dev function.
3826+
3827+
GET THE FREE ENERGY ODD CORRECTION DEV
3828+
======================================
3829+
3830+
This function computes the third and fourth order corrections classifying the
3831+
polarization vector by wave-vector q. We rewrote the hessian from Bianco's paper
3832+
into smaller objects. The expression will be soon uploaded.
3833+
3834+
To reduce the required RAM, only the symmetry independent third and fourth order
3835+
force constants are saved in memory, computing the rest on the fly.
3836+
3837+
Parameters
3838+
----------
3839+
include_v4 : bool
3840+
If True we include the fourth order force constant matrix.
3841+
This requires a lot of memory
3842+
do_scf : bool
3843+
If True, the W matrix is self-consitently converged. Otherwise, just the first
3844+
correction of the fourth-order is considered (False often enough in practice).
3845+
eps : float
3846+
Precision to reach for the max value of |W|. It is later weighted by alpha-mix
3847+
in practice to ensure convergence. Defaults to 1e-6.
3848+
alpha_mix : float
3849+
Mixing parameter used in the scf loop of W, where
3850+
W(n+1)=W(n)(1-alpha_mix)+alpha_mix*W(n+1). Default to 0.3.
3851+
get_full_hessian : bool
3852+
If True the full hessian matrix is returned, if false, only the correction to
3853+
the SSCHA dynamical matrix is returned.
3854+
verbose : bool
3855+
If true, the third order force constant tensor is written in output [Ha/bohr^3 units].
3856+
This can be used to interpolate the result on a bigger mesh with cellconstructor.
3857+
3858+
Returns
3859+
-------
3860+
phi_sc : Phonons()
3861+
The dynamical matrix of the free energy hessian in (Ry/bohr^2)
3862+
"""
3863+
3864+
self.convert_units(UNITS_HARTREE)
3865+
super_structure = self.current_dyn.structure.generate_supercell(self.supercell)
3866+
dyn_supercell = self.current_dyn.GenerateSupercellDyn(self.supercell)
3867+
nr = np.prod(self.supercell)
3868+
3869+
nat_sc = dyn_supercell.structure.N_atoms
3870+
n_modes = nat_sc*3
3871+
3872+
mapping, rot_cart, map_uc, map_tr, T_list, T_list_frac = Classify.map_singlet(self.current_dyn, verbose = verbose)
3873+
3874+
orbit2a, orbit2s, norbit, indep_elem, n_indep_elem, tensor = Classify.recognize_doublet(self.current_dyn, mapping, map_uc, verbose = verbose)
3875+
3876+
orbit3a, orbit3s, norbit3, indep_3fc_elem, n_indep_3fc_elem, kernel_3fc, rot_3fc, mapping_triplet = Classify.recognize_triplet(self.current_dyn, mapping, map_uc, verbose=verbose)
3877+
3878+
nref2 = orbit2a.shape[0]
3879+
indep_fc = np.zeros((nref2, max(n_indep_elem)), dtype=np.float64)
3880+
3881+
super_structure = self.current_dyn.structure.generate_supercell(self.supercell)
3882+
w, pols = self.current_dyn.DiagonalizeSupercell()
3883+
3884+
n_modes = len(w)
3885+
nat_sc = int(np.shape(pols)[0] / 3)
3886+
nat = self.dyn_0.structure.N_atoms
3887+
3888+
a = np.zeros( (nat_sc * 3), dtype = np.double)
3889+
new_pol = np.zeros( (nat_sc, nat_sc * 3, 3), dtype = np.double)
3890+
# If the element is sym:
3891+
ur = np.zeros( (self.N, nat_sc * 3))
3892+
upsilon = np.zeros( (nat_sc*3, nat_sc * 3))
3893+
# Symmetry data for sym 3FC:
3894+
s_cart = np.zeros( (3, 3, 48) , dtype = np.float64, order = "F")
3895+
s_inv_cart = np.zeros( (3, 3, 48) , dtype = np.float64, order = "F")
3896+
irt = np.zeros( (48, nat_sc), dtype = np.intc, order = "F")
3897+
translations_irt = np.zeros( (nat_sc, np.prod(self.supercell)), dtype = np.intc, order = "F")
3898+
3899+
# Get the translational modes
3900+
if not self.ignore_small_w:
3901+
trans = CC.Methods.get_translations(pols, super_structure.get_masses_array())
3902+
else:
3903+
trans = np.abs(w) < CC.Phonons.__EPSILON_W__
3904+
38203905

3906+
# Get the atomic types
3907+
ityp = super_structure.get_ityp() + 1 #Py to Fortran indexing
3908+
n_typ = len(self.current_dyn.structure.masses)
3909+
3910+
amass = np.zeros(n_typ, dtype = np.double)
3911+
3912+
for at_type in self.current_dyn.structure.masses:
3913+
index = ityp[self.current_dyn.structure.atoms.index(at_type)] - 1
3914+
amass[index] = self.current_dyn.structure.masses[at_type]
3915+
3916+
# Get the forces and conver in the correct units
3917+
f = (self.forces - self.sscha_forces)# * Bohr
3918+
u = self.u_disps.reshape((self.N, nat_sc, 3), order = "C") #/ Bohr
3919+
3920+
log_err = "err_yesrho"
3921+
a = SCHAModules.thermodynamic.w_to_a(w, self.current_T)
3922+
# Get the polarization vectors in the correct format
3923+
for i in range(nat_sc):
3924+
for j in range(n_modes):
3925+
new_pol[i, j, :] = pols[3*i : 3*(i+1), j]
3926+
3927+
#Calculating rotated displacements and upsilon matrix"
3928+
ur, upsilon = SCHAModules.get_ur_upsilon_matrices(a, new_pol, trans, amass, ityp, u)
3929+
#Obtaining symmetry data
3930+
qe_sym = CC.symmetries.QE_Symmetry(super_structure)
3931+
qe_sym.SetupFromSPGLIB()
3932+
3933+
# SpaceGroup symmetries data
3934+
s_cart, s_inv_cart, irt = qe_sym.QE_s_cart, qe_sym.QE_s_inv_cart, qe_sym.QE_irt
3935+
s_cart = np.asfortranarray(s_cart)
3936+
s_inv_cart = np.asfortranarray(s_inv_cart)
3937+
3938+
#Translations
3939+
translations_irt = qe_sym.QE_translations_irt
3940+
#self.prepare_sym_third_fc = False
3941+
3942+
wq = np.empty(nat*3, dtype=np.float64)
3943+
tmp_pol_vecs = np.empty([nat*3, nat*3], dtype=np.complex128)
3944+
norm_pol_vecs = np.empty([nat*3, nat*3], dtype=np.complex128)
3945+
pol_vecs = np.empty([self.supercell[0]*self.supercell[1]*self.supercell[2], nat*3, nat_sc*3], dtype=np.complex128)
3946+
l = np.empty([self.supercell[0]*self.supercell[1]*self.supercell[2], nat*3, nat_sc*3], dtype=np.complex128)
3947+
wq = np.empty([l.shape[0], l.shape[1]], dtype=np.float64)
3948+
aq = np.empty([l.shape[0], l.shape[1]], dtype=np.float64)
3949+
3950+
rcell = CC.Methods.get_reciprocal_vectors(self.current_dyn.structure.unit_cell)
3951+
q_list = np.empty([l.shape[0], 3], dtype=np.float64)
3952+
q_list_cart = np.empty([l.shape[0], 3], dtype=np.float64)
3953+
qi=0
3954+
for qstar in self.current_dyn.q_stars:
3955+
for q in qstar:
3956+
q_cryst = np.round(CC.Methods.cart_to_cryst(rcell, q)/__A_TO_BOHR__,6)
3957+
wq[qi], tmp_pol_vecs = self.current_dyn.DyagDinQ(iq=qi)
3958+
tmp_pol_vecs = np.transpose(tmp_pol_vecs) # First index mode. Second incex atom, alpha.
3959+
aq[qi] = SCHAModules.thermodynamic.w_to_a(wq[qi], self.current_T)
3960+
for mode in range(nat*3):
3961+
for atom in range(nat):
3962+
norm_pol_vecs[mode,atom*3:(atom+1)*3] = tmp_pol_vecs[mode,atom*3:(atom+1)*3]*aq[qi, mode]/np.sqrt(amass[ityp[atom]-1])
3963+
for mode in range(nat*3):
3964+
for Tx in range(self.supercell[0]):
3965+
for Ty in range(self.supercell[1]):
3966+
for Tz in range(self.supercell[2]):
3967+
T = np.array([Tx, Ty, Tz])
3968+
index = Tx*self.supercell[2]*self.supercell[1]+Ty*self.supercell[2]+Tz
3969+
l[qi,mode,index*nat*3:(index+1)*nat*3] = norm_pol_vecs[mode]*np.exp(2j*np.pi*np.matmul(q_cryst,T))/np.sqrt(self.supercell[0]*self.supercell[1]*self.supercell[2], dtype=np.complex128)
3970+
pol_vecs[qi,mode,index*nat*3:(index+1)*nat*3] = tmp_pol_vecs[mode]*np.exp(2j*np.pi*np.matmul(q_cryst,T))/np.sqrt(self.supercell[0]*self.supercell[1]*self.supercell[2], dtype=np.complex128)
3971+
q_list[qi] = q_cryst
3972+
q_list_cart[qi] = q
3973+
qi+=1
3974+
3975+
Nq = l.shape[0]
3976+
# Get translational modes
3977+
transq = np.zeros(wq.shape, dtype=bool)
3978+
for qi in range(Nq):
3979+
if not self.ignore_small_w:
3980+
with warnings.catch_warnings():
3981+
warnings.simplefilter("ignore")
3982+
transq[qi,:] = CC.Methods.get_translations(np.transpose(pol_vecs[qi,:,:nat*3]), super_structure.get_masses_array()[:nat])
3983+
else:
3984+
transq[qi,:] = np.abs(w) < CC.Phonons.__EPSILON_W__
3985+
for mode in range(nat*3):
3986+
if transq[qi, mode]:
3987+
l[qi,mode] = np.zeros(nat_sc*3, dtype=np.complex128)
3988+
pol_vecs[qi,mode] = np.zeros(nat_sc*3, dtype=np.complex128)
3989+
3990+
# Imposing eps(q)=[eps(-q)]* time-reversal criteria")
3991+
3992+
############################## Start impose TRS ##############################
3993+
mappingq, orbitq1a, orbitq1s, its_zb = qClassify.map_singlet(q_list_cart, q_list, rcell*__A_TO_BOHR__, rot_cart)
3994+
k = 0
3995+
trs_qlist = np.empty(q_list.shape, dtype=np.float64)
3996+
trs_qlist_cart = np.empty(q_list.shape, dtype=np.float64)
3997+
trs_polvecs = np.empty(l.shape, dtype=np.complex128)
3998+
trs_l = np.empty(l.shape, dtype=np.complex128)
3999+
trs_aq = np.empty(aq.shape, dtype=np.float64)
4000+
trs_wq = np.empty(wq.shape, dtype=np.float64)
4001+
4002+
for qi, q in enumerate(q_list):
4003+
if its_zb[qi] == 0:
4004+
trs_qlist[k] = q
4005+
trs_qlist_cart[k] = CC.Methods.cryst_to_cart(rcell*__A_TO_BOHR__, q)
4006+
trs_polvecs[k] = pol_vecs[qi]
4007+
trs_l[k] = l[qi]
4008+
trs_aq[k] = aq[qi]
4009+
trs_wq[k] = wq[qi]
4010+
k+=1
4011+
elif its_zb[qi] == 1:
4012+
trs_qlist[k] = q
4013+
trs_qlist_cart[k] = CC.Methods.cryst_to_cart(rcell*__A_TO_BOHR__, q)
4014+
trs_polvecs[k] = pol_vecs[qi]
4015+
trs_l[k] = l[qi]
4016+
trs_aq[k] = aq[qi]
4017+
trs_wq[k] = wq[qi]
4018+
k+=1
4019+
trs_qlist[k] = (-1)*q
4020+
trs_qlist_cart[k] = CC.Methods.cryst_to_cart(rcell*__A_TO_BOHR__, (-1)*q)
4021+
trs_polvecs[k] = np.conjugate(pol_vecs[qi])
4022+
trs_l[k] = np.conjugate(l[qi])
4023+
trs_aq[k] = aq[qi]
4024+
trs_wq[k] = wq[qi]
4025+
k+=1
4026+
############################## End impose TRS ##############################
4027+
4028+
# Redo the classification imposing the TRS criteria.
4029+
mappingq, orbitq1a, orbitq1s, its_zb = qClassify.map_singlet(trs_qlist_cart, trs_qlist, rcell*__A_TO_BOHR__, rot_cart)
4030+
refq2, refq2o, norbitq2, nrefq2 = qClassify.recognize_doublet(trs_qlist, mappingq)
4031+
4032+
mod = self.supercell
4033+
nat = self.current_dyn.structure.N_atoms
4034+
n_modes = self.current_dyn.structure.N_atoms*mod[0]*mod[1]*mod[2]*3
4035+
4036+
ref_3fc = SCHAModules.module_hess.get_ref3fc(nat, orbit3a, indep_3fc_elem, n_indep_3fc_elem, kernel_3fc, rot_3fc, ur, upsilon, f, self.rho, log_err, s_inv_cart, irt, translations_irt, True)
4037+
4038+
vs_red = np.empty([nrefq2,nat*3,nat*3,nat*3], dtype=np.complex128)
4039+
vs_red = SCHAModules.module_hess.get_ref_vsq(refq2,trs_l,rot_3fc,ref_3fc,mapping_triplet,True)
4040+
trs_gq, daq = SCHAModules.get_gq(trs_aq, trs_wq, transq, self.current_T)
4041+
indep_fc = SCHAModules.module_hess.get_indep2fc(vs_red, refq2, refq2o, norbitq2, orbit2a, n_indep_elem, indep_elem, rot_cart, mapping, map_uc, map_tr, T_list, trs_qlist, trs_gq, verbose)
4042+
4043+
if include_v4:
4044+
refq4, refq4o, norbitq4, nrefq4 = qClassify.recognize_quadruplet(trs_qlist,mappingq,verbose)
4045+
4046+
orbit4t, orbit4o, norbit_4, indep_4fc_elem, n_indep_4fc_elem, kernel_4fc, rot_4fc, mapping_quadruplet = Classify.recognize_quadruplet(self.current_dyn, mapping, map_uc, verbose)
4047+
4048+
ref_4fc = SCHAModules.module_hess.get_ref4fc(orbit4t, indep_4fc_elem, n_indep_4fc_elem, kernel_4fc, rot_4fc, ur, upsilon, f, self.rho, log_err, s_inv_cart, irt, translations_irt, True)
4049+
4050+
ws_red = np.zeros([nrefq4,nat*3,nat*3,nat*3,nat*3], dtype=np.complex128)
4051+
ws_red = SCHAModules.module_hess.get_ref_wsq(refq4,trs_l,rot_4fc,ref_4fc,mapping_quadruplet,True)
4052+
4053+
degs = qClassify.find_degeneracies(trs_wq)
4054+
Pmn = qClassify.construct_Pmn(mapping, orbitq1a, orbitq1s, trs_polvecs, rot_cart)
4055+
4056+
v_red, ref_3fc = SCHAModules.module_hess.get_v3_red(nat, norbit3, orbit3a, orbit3s, indep_3fc_elem, n_indep_3fc_elem, kernel_3fc, rot_3fc, ur, upsilon, f, self.rho, log_err, s_inv_cart, irt, translations_irt)
4057+
vs = SCHAModules.module_hess.get_all_vsq(trs_l, v_red, map_uc)
4058+
4059+
if do_scf:
4060+
ws_red_scf = SCHAModules.module_hess.get_scf_wsq(ws_red, trs_gq, refq4, refq4o, norbitq4, Pmn, degs, True, eps, alpha_mix)
4061+
indep_fc4 = SCHAModules.module_hess.get_indep2fc_v4(vs, ws_red_scf, refq4, refq4o, norbitq4, orbit2a, n_indep_elem, indep_elem, trs_gq, Pmn, degs, mapping, rot_cart, verbose)
4062+
else:
4063+
indep_fc4 = SCHAModules.module_hess.get_indep2fc_v4(vs, ws_red, refq4, refq4o, norbitq4, orbit2a, n_indep_elem, indep_elem, trs_gq, Pmn, degs, mapping, rot_cart, verbose)
4064+
indep_fc += indep_fc4
4065+
phi_sc_odd = np.zeros((n_modes, n_modes), dtype = np.double)
4066+
for ref2 in range(nref2):
4067+
for i in range(norbit[ref2]):
4068+
nat1, nat2 = orbit2a[ref2,i,:]
4069+
fc9 = np.dot(tensor[ref2,i,:, :n_indep_elem[ref2]], indep_fc[ref2, :n_indep_elem[ref2]])
4070+
for alpha in range(3):
4071+
for beta in range(3):
4072+
index = 3*alpha+beta
4073+
#Apply translation sym:
4074+
for r in range(nr):
4075+
# Translated Single atomic-cartesian index
4076+
# Fortran to Py: -1
4077+
phi_sc_odd[3*(translations_irt[nat1,r]-1)+alpha, 3*(translations_irt[nat2,r]-1)+beta] = fc9[index]
4078+
dynq_odd = CC.Phonons.GetDynQFromFCSupercell(phi_sc_odd, np.array(self.current_dyn.q_tot),
4079+
self.current_dyn.structure, super_structure)
4080+
self.convert_units(UNITS_DEFAULT)
4081+
dynq_odd *= 2 # Ha/bohr^2 -> Ry/bohr^2
4082+
4083+
# Generate the Phonon structure by including the odd correction
4084+
dyn_hessian = self.current_dyn.Copy()
4085+
for iq in range(len(self.current_dyn.q_tot)):
4086+
if get_full_hessian:
4087+
dyn_hessian.dynmats[iq] = self.current_dyn.dynmats[iq] + dynq_odd[iq, :, :]
4088+
else:
4089+
dyn_hessian.dynmats[iq] = dynq_odd[iq, :, :]
4090+
return dyn_hessian
38214091

38224092
def compute_ensemble(self, calculator, compute_stress = True, stress_numerical = False,
38234093
cluster = None, verbose = True, timer=None):

0 commit comments

Comments
 (0)