|
6 | 6 | import time |
7 | 7 | #from scipy.special import tanh, sinh, cosh |
8 | 8 |
|
| 9 | +import sscha.Classify as Classify |
| 10 | +import sscha.qClassify as qClassify |
9 | 11 |
|
10 | 12 | """ |
11 | 13 | This is part of the program python-sscha |
@@ -250,6 +252,19 @@ def __init__(self, dyn0, T0, supercell = None, **kwargs): |
250 | 252 | # A flag that memorize if the ensemble has also the stresses |
251 | 253 | self.has_stress = True |
252 | 254 |
|
| 255 | + # Store data for calculations of 3FC elements: |
| 256 | + self.a = np.zeros( (Nsc * 3), dtype = np.double) |
| 257 | + self.new_pol = np.zeros( (Nsc, Nsc * 3, 3), dtype = np.double) |
| 258 | + # If the element is sym: |
| 259 | + self.ur = np.zeros( (self.N, Nsc * 3)) |
| 260 | + self.upsilon = np.zeros( (Nsc*3, Nsc * 3)) |
| 261 | + # Symmetry data for sym 3FC: |
| 262 | + self.nsym = 0 |
| 263 | + self.s_cart = np.zeros( (3, 3, 48) , dtype = np.float64, order = "F") |
| 264 | + self.s_inv_cart = np.zeros( (3, 3, 48) , dtype = np.float64, order = "F") |
| 265 | + self.irt = np.zeros( (48, Nsc), dtype = np.intc, order = "F") |
| 266 | + self.translations_irt = np.zeros( (Nsc, np.prod(self.supercell)), dtype = np.intc, order = "F") |
| 267 | + |
253 | 268 | # A flag for each configuration that check if it possess a force and a stress |
254 | 269 | self.force_computed = None |
255 | 270 | self.stress_computed = None |
@@ -3831,7 +3846,253 @@ def get_free_energy_hessian(self, include_v4 = False, get_full_hessian = True, v |
3831 | 3846 | return dyn_hessian, d3* 2.0 # Ha to Ry |
3832 | 3847 | return dyn_hessian |
3833 | 3848 |
|
| 3849 | + def get_free_energy_hessian_dev(self, include_v4 = False, get_full_hessian = True, verbose = False): |
| 3850 | + """ |
| 3851 | + Dev function. With the meson installation, the OpenMP parallelization is NOT WORKING! |
| 3852 | + With the old installation everything is fine (old_setup.py) |
| 3853 | +
|
| 3854 | + GET THE FREE ENERGY ODD CORRECTION DEV |
| 3855 | + ====================================== |
| 3856 | +
|
| 3857 | + This function computes the third and fourth order corrections classifying the |
| 3858 | + polarization vector by wave-vector q. We rewrote the hessian from Bianco's paper |
| 3859 | + into smaller objects. The expression will be soon uploaded. |
| 3860 | +
|
| 3861 | + To reduce the required RAM, only the symmetry independent third and fourth order |
| 3862 | + force constants are saved in memory, computing the rest on the fly. |
| 3863 | +
|
| 3864 | + Parameters |
| 3865 | + ---------- |
| 3866 | + include_v4 : bool |
| 3867 | + If True we include the fourth order force constant matrix. |
| 3868 | + This requires a lot of memory |
| 3869 | + get_full_hessian : bool |
| 3870 | + If True the full hessian matrix is returned, if false, only the correction to |
| 3871 | + the SSCHA dynamical matrix is returned. |
| 3872 | + verbose : bool |
| 3873 | + If true, the third order force constant tensor is written in output [Ha/bohr^3 units]. |
| 3874 | + This can be used to interpolate the result on a bigger mesh with cellconstructor. |
| 3875 | +
|
| 3876 | + Returns |
| 3877 | + ------- |
| 3878 | + phi_sc : Phonons() |
| 3879 | + The dynamical matrix of the free energy hessian in (Ry/bohr^2) |
| 3880 | + """ |
| 3881 | + |
| 3882 | + self.convert_units(UNITS_HARTREE) |
| 3883 | + super_structure = self.current_dyn.structure.generate_supercell(self.supercell) |
| 3884 | + dyn_supercell = self.current_dyn.GenerateSupercellDyn(self.supercell) |
| 3885 | + nr = np.prod(self.supercell) |
| 3886 | + |
| 3887 | + nat_sc = dyn_supercell.structure.N_atoms |
| 3888 | + n_modes = nat_sc*3 |
| 3889 | + |
| 3890 | + mapping, rot_cart, map_uc, map_tr, T_list, T_list_frac = Classify.map_singlet(self.current_dyn, verbose = verbose) |
| 3891 | + |
| 3892 | + orbit2a, orbit2s, norbit, indep_elem, n_indep_elem, tensor = Classify.recognize_doublet(self.current_dyn, mapping, map_uc, verbose = verbose) |
| 3893 | + |
| 3894 | + 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) |
| 3895 | + |
| 3896 | + nref2 = orbit2a.shape[0] |
| 3897 | + indep_fc = np.zeros((nref2, max(n_indep_elem)), dtype=np.float64) |
| 3898 | + |
| 3899 | + super_structure = self.current_dyn.structure.generate_supercell(self.supercell) |
| 3900 | + w, pols = self.current_dyn.DiagonalizeSupercell() |
| 3901 | + |
| 3902 | + n_modes = len(w) |
| 3903 | + nat_sc = int(np.shape(pols)[0] / 3) |
| 3904 | + nat = self.dyn_0.structure.N_atoms |
| 3905 | + |
| 3906 | + # Get the translational modes |
| 3907 | + if not self.ignore_small_w: |
| 3908 | + trans = CC.Methods.get_translations(pols, super_structure.get_masses_array()) |
| 3909 | + else: |
| 3910 | + trans = np.abs(w) < CC.Phonons.__EPSILON_W__ |
| 3911 | + |
| 3912 | + |
| 3913 | + # Get the atomic types |
| 3914 | + ityp = super_structure.get_ityp() + 1 #Py to Fortran indexing |
| 3915 | + n_typ = len(self.current_dyn.structure.masses) |
| 3916 | + |
| 3917 | + amass = np.zeros(n_typ, dtype = np.double) |
| 3918 | + |
| 3919 | + for at_type in self.current_dyn.structure.masses: |
| 3920 | + index = ityp[self.current_dyn.structure.atoms.index(at_type)] - 1 |
| 3921 | + amass[index] = self.current_dyn.structure.masses[at_type] |
| 3922 | + |
| 3923 | + # Get the forces and conver in the correct units |
| 3924 | + f = (self.forces - self.sscha_forces)# * Bohr |
| 3925 | + u = self.u_disps.reshape((self.N, nat_sc, 3), order = "C") #/ Bohr |
| 3926 | + |
| 3927 | + log_err = "err_yesrho" |
| 3928 | + self.a = SCHAModules.thermodynamic.w_to_a(w, self.current_T) |
| 3929 | + # Get the polarization vectors in the correct format |
| 3930 | + for i in range(nat_sc): |
| 3931 | + for j in range(n_modes): |
| 3932 | + self.new_pol[i, j, :] = pols[3*i : 3*(i+1), j] |
| 3933 | + |
| 3934 | + #Calculating rotated displacements and upsilon matrix" |
| 3935 | + self.ur, self.upsilon = SCHAModules.get_ur_upsilon_matrices(self.a, self.new_pol, trans, amass, ityp, u) |
| 3936 | + #Obtaining symmetry data |
| 3937 | + qe_sym = CC.symmetries.QE_Symmetry(super_structure) |
| 3938 | + qe_sym.SetupFromSPGLIB() |
| 3939 | + |
| 3940 | + # SpaceGroup symmetries data |
| 3941 | + self.nsym, self.s_cart, self.s_inv_cart, self.irt = qe_sym.QE_nsym, qe_sym.QE_s_cart, qe_sym.QE_s_inv_cart, qe_sym.QE_irt |
| 3942 | + self.s_cart = np.asfortranarray(self.s_cart) |
| 3943 | + self.s_inv_cart = np.asfortranarray(self.s_inv_cart) |
| 3944 | + |
| 3945 | + #Translations |
| 3946 | + self.translations_irt = qe_sym.QE_translations_irt |
| 3947 | + #self.prepare_sym_third_fc = False |
| 3948 | + |
| 3949 | + wq = np.empty(nat*3, dtype=np.float64) |
| 3950 | + tmp_pol_vecs = np.empty([nat*3, nat*3], dtype=np.complex128) |
| 3951 | + norm_pol_vecs = np.empty([nat*3, nat*3], dtype=np.complex128) |
| 3952 | + pol_vecs = np.empty([self.supercell[0]*self.supercell[1]*self.supercell[2], nat*3, nat_sc*3], dtype=np.complex128) |
| 3953 | + l = np.empty([self.supercell[0]*self.supercell[1]*self.supercell[2], nat*3, nat_sc*3], dtype=np.complex128) |
| 3954 | + wq = np.empty([l.shape[0], l.shape[1]], dtype=np.float64) |
| 3955 | + aq = np.empty([l.shape[0], l.shape[1]], dtype=np.float64) |
| 3956 | + |
| 3957 | + rcell = CC.Methods.get_reciprocal_vectors(self.current_dyn.structure.unit_cell) |
| 3958 | + q_list = np.empty([l.shape[0], 3], dtype=np.float64) |
| 3959 | + q_list_cart = np.empty([l.shape[0], 3], dtype=np.float64) |
| 3960 | + qi=0 |
| 3961 | + for qstar in self.current_dyn.q_stars: |
| 3962 | + for q in qstar: |
| 3963 | + q_cryst = np.round(CC.Methods.cart_to_cryst(rcell, q)/__A_TO_BOHR__,6) |
| 3964 | + wq[qi], tmp_pol_vecs = self.current_dyn.DyagDinQ(iq=qi) |
| 3965 | + tmp_pol_vecs = np.transpose(tmp_pol_vecs) # First index mode. Second incex atom, alpha. |
| 3966 | + aq[qi] = SCHAModules.thermodynamic.w_to_a(wq[qi], self.current_T) |
| 3967 | + for mode in range(nat*3): |
| 3968 | + for atom in range(nat): |
| 3969 | + 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]) |
| 3970 | + for mode in range(nat*3): |
| 3971 | + for Tx in range(self.supercell[0]): |
| 3972 | + for Ty in range(self.supercell[1]): |
| 3973 | + for Tz in range(self.supercell[2]): |
| 3974 | + T = np.array([Tx, Ty, Tz]) |
| 3975 | + index = Tx*self.supercell[2]*self.supercell[1]+Ty*self.supercell[2]+Tz |
| 3976 | + 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) |
| 3977 | + 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) |
| 3978 | + q_list[qi] = q_cryst |
| 3979 | + q_list_cart[qi] = q |
| 3980 | + qi+=1 |
| 3981 | + |
| 3982 | + Nq = l.shape[0] |
| 3983 | + # Get translational modes |
| 3984 | + transq = np.zeros(wq.shape, dtype=bool) |
| 3985 | + for qi in range(Nq): |
| 3986 | + if not self.ignore_small_w: |
| 3987 | + with warnings.catch_warnings(): |
| 3988 | + warnings.simplefilter("ignore") |
| 3989 | + transq[qi,:] = CC.Methods.get_translations(np.transpose(pol_vecs[qi,:,:nat*3]), super_structure.get_masses_array()[:nat]) |
| 3990 | + else: |
| 3991 | + transq[qi,:] = np.abs(w) < CC.Phonons.__EPSILON_W__ |
| 3992 | + for mode in range(nat*3): |
| 3993 | + if transq[qi, mode]: |
| 3994 | + l[qi,mode] = np.zeros(nat_sc*3, dtype=np.complex128) |
| 3995 | + pol_vecs[qi,mode] = np.zeros(nat_sc*3, dtype=np.complex128) |
| 3996 | + |
| 3997 | + # Imposing eps(q)=[eps(-q)]* time-reversal criteria") |
| 3998 | + |
| 3999 | + ############################## Start impose TRS ############################## |
| 4000 | + mappingq, orbitq1a, orbitq1s, its_zb = qClassify.map_singlet(q_list_cart, q_list, rcell*__A_TO_BOHR__, rot_cart) |
| 4001 | + k = 0 |
| 4002 | + trs_qlist = np.empty(q_list.shape, dtype=np.float64) |
| 4003 | + trs_qlist_cart = np.empty(q_list.shape, dtype=np.float64) |
| 4004 | + trs_polvecs = np.empty(l.shape, dtype=np.complex128) |
| 4005 | + trs_l = np.empty(l.shape, dtype=np.complex128) |
| 4006 | + trs_aq = np.empty(aq.shape, dtype=np.float64) |
| 4007 | + trs_wq = np.empty(wq.shape, dtype=np.float64) |
| 4008 | + |
| 4009 | + for qi, q in enumerate(q_list): |
| 4010 | + if its_zb[qi] == 0: |
| 4011 | + trs_qlist[k] = q |
| 4012 | + trs_qlist_cart[k] = CC.Methods.cryst_to_cart(rcell*__A_TO_BOHR__, q) |
| 4013 | + trs_polvecs[k] = pol_vecs[qi] |
| 4014 | + trs_l[k] = l[qi] |
| 4015 | + trs_aq[k] = aq[qi] |
| 4016 | + trs_wq[k] = wq[qi] |
| 4017 | + k+=1 |
| 4018 | + elif its_zb[qi] == 1: |
| 4019 | + trs_qlist[k] = q |
| 4020 | + trs_qlist_cart[k] = CC.Methods.cryst_to_cart(rcell*__A_TO_BOHR__, q) |
| 4021 | + trs_polvecs[k] = pol_vecs[qi] |
| 4022 | + trs_l[k] = l[qi] |
| 4023 | + trs_aq[k] = aq[qi] |
| 4024 | + trs_wq[k] = wq[qi] |
| 4025 | + k+=1 |
| 4026 | + trs_qlist[k] = (-1)*q |
| 4027 | + trs_qlist_cart[k] = CC.Methods.cryst_to_cart(rcell*__A_TO_BOHR__, (-1)*q) |
| 4028 | + trs_polvecs[k] = np.conjugate(pol_vecs[qi]) |
| 4029 | + trs_l[k] = np.conjugate(l[qi]) |
| 4030 | + trs_aq[k] = aq[qi] |
| 4031 | + trs_wq[k] = wq[qi] |
| 4032 | + k+=1 |
| 4033 | + ############################## End impose TRS ############################## |
| 4034 | + |
| 4035 | + # Redo the classification imposing the TRS criteria. |
| 4036 | + mappingq, orbitq1a, orbitq1s, its_zb = qClassify.map_singlet(trs_qlist_cart, trs_qlist, rcell*__A_TO_BOHR__, rot_cart) |
| 4037 | + refq2, refq2o, norbitq2, nrefq2 = qClassify.recognize_doublet(trs_qlist, mappingq) |
| 4038 | + |
| 4039 | + mod = self.supercell |
| 4040 | + nat = self.current_dyn.structure.N_atoms |
| 4041 | + n_modes = self.current_dyn.structure.N_atoms*mod[0]*mod[1]*mod[2]*3 |
| 4042 | + |
| 4043 | + ref_3fc = SCHAModules.module_hess.get_ref3fc(nat, orbit3a, indep_3fc_elem, n_indep_3fc_elem, kernel_3fc, rot_3fc, self.ur, self.upsilon, f, self.rho, log_err, self.s_inv_cart, self.irt, self.translations_irt, verbose) |
3834 | 4044 |
|
| 4045 | + vs_red = np.empty([nrefq2,nat*3,nat*3,nat*3], dtype=np.complex128) |
| 4046 | + vs_red = SCHAModules.module_hess.get_ref_vsq(refq2,trs_l,rot_3fc,ref_3fc,mapping_triplet,verbose) |
| 4047 | + trs_gq, daq = SCHAModules.get_gq(trs_aq, trs_wq, transq, self.current_T) |
| 4048 | + 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) |
| 4049 | + |
| 4050 | + if include_v4: |
| 4051 | + refq4, refq4o, norbitq4, nrefq4 = qClassify.recognize_quadruplet(trs_qlist,mappingq,verbose) |
| 4052 | + |
| 4053 | + 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) |
| 4054 | + |
| 4055 | + ref_4fc = SCHAModules.module_hess.get_ref4fc(orbit4t, indep_4fc_elem, n_indep_4fc_elem, kernel_4fc, rot_4fc, self.ur, self.upsilon, f, self.rho, log_err, self.s_inv_cart, self.irt, self.translations_irt, verbose) |
| 4056 | + |
| 4057 | + ws_red = np.zeros([nrefq4,nat*3,nat*3,nat*3,nat*3], dtype=np.complex128) |
| 4058 | + ws_red = SCHAModules.module_hess.get_ref_wsq(refq4,trs_l,rot_4fc,ref_4fc,mapping_quadruplet,verbose) |
| 4059 | + |
| 4060 | + degs = qClassify.find_degeneracies(trs_wq) |
| 4061 | + Pmn = qClassify.construct_Pmn(mapping, orbitq1a, orbitq1s, trs_polvecs, rot_cart) |
| 4062 | + |
| 4063 | + ws_red_scf = SCHAModules.module_hess.get_scf_wsq(ws_red, trs_gq, refq4, refq4o, norbitq4, Pmn, degs, verbose) |
| 4064 | + |
| 4065 | + 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, self.ur, self.upsilon, f, self.rho, log_err, self.s_inv_cart, self.irt, self.translations_irt) |
| 4066 | + vs = SCHAModules.module_hess.get_all_vsq(trs_l, v_red, map_uc) |
| 4067 | + |
| 4068 | + 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) |
| 4069 | + indep_fc += indep_fc4 |
| 4070 | + phi_sc_odd = np.zeros((n_modes, n_modes), dtype = np.double) |
| 4071 | + for ref2 in range(nref2): |
| 4072 | + for i in range(norbit[ref2]): |
| 4073 | + nat1, nat2 = orbit2a[ref2,i,:] |
| 4074 | + fc9 = np.dot(tensor[ref2,i,:, :n_indep_elem[ref2]], indep_fc[ref2, :n_indep_elem[ref2]]) |
| 4075 | + for alpha in range(3): |
| 4076 | + for beta in range(3): |
| 4077 | + index = 3*alpha+beta |
| 4078 | + #Apply translation sym: |
| 4079 | + for r in range(nr): |
| 4080 | + # Translated Single atomic-cartesian index |
| 4081 | + # Fortran to Py: -1 |
| 4082 | + phi_sc_odd[3*(self.translations_irt[nat1,r]-1)+alpha, 3*(self.translations_irt[nat2,r]-1)+beta] = fc9[index] |
| 4083 | + dynq_odd = CC.Phonons.GetDynQFromFCSupercell(phi_sc_odd, np.array(self.current_dyn.q_tot), |
| 4084 | + self.current_dyn.structure, super_structure) |
| 4085 | + self.convert_units(UNITS_DEFAULT) |
| 4086 | + dynq_odd *= 2 # Ha/bohr^2 -> Ry/bohr^2 |
| 4087 | + |
| 4088 | + # Generate the Phonon structure by including the odd correction |
| 4089 | + dyn_hessian = self.current_dyn.Copy() |
| 4090 | + for iq in range(len(self.current_dyn.q_tot)): |
| 4091 | + if get_full_hessian: |
| 4092 | + dyn_hessian.dynmats[iq] = self.current_dyn.dynmats[iq] + dynq_odd[iq, :, :] |
| 4093 | + else: |
| 4094 | + dyn_hessian.dynmats[iq] = dynq_odd[iq, :, :] |
| 4095 | + return dyn_hessian |
3835 | 4096 |
|
3836 | 4097 | def compute_ensemble(self, calculator, compute_stress = True, stress_numerical = False, |
3837 | 4098 | cluster = None, verbose = True, timer=None): |
|
0 commit comments