Skip to content

Commit 494ee60

Browse files
committed
fix the asymmetry of exx force with 2 different DM
1 parent 0138b26 commit 494ee60

9 files changed

Lines changed: 130 additions & 46 deletions

File tree

source/module_base/matrix.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@ void matrix::create( const int nrow, const int ncol, const bool flag_zero )
154154
}
155155
}
156156

157+
/* Unary minus*/
158+
matrix operator-(const matrix& m1)
159+
{
160+
matrix tm(m1);
161+
const int size = m1.nr * m1.nc;
162+
for (int i = 0; i < size; i++)
163+
tm.c[i] = -tm.c[i];
164+
return tm;
165+
}
166+
157167
/* Adding matrices, as a friend */
158168
matrix operator+(const matrix &m1, const matrix &m2)
159169
{

source/module_base/matrix.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class matrix
8383
using type=double; // Peiae Lin add 2022.08.08 for template
8484
};
8585

86-
86+
matrix operator-(const matrix& m1); // unary minus
8787
matrix operator+(const matrix &m1, const matrix &m2);
8888
matrix operator-(const matrix &m1, const matrix &m2);
8989
matrix operator*(const matrix &m1, const matrix &m2);

source/module_lr/Grad/esolver_lr_grad.cpp

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -158,20 +158,23 @@ std::vector<ModuleBase::matrix> LR::ESolver_LR<T, TR>::cal_force(const int ispin
158158
// LR_Util::print_DMR(dm_trans, "dm_trans of istate " + std::to_string(istate));
159159
// difference density matrix
160160
std::vector<ct::Tensor> dm_diff_k = cal_dm_diff_pblas(this->X[ispin].template data<T>() + offset, this->paraX_[ispin], c, this->paraC_, this->nbasis, this->nocc[ispin], this->nvirt[ispin], this->paraMat_);
161-
// std::cout << "dm_diff_k T(k) before symmetrization, istate " + std::to_string(istate) << std::endl;
162-
// LR_Util::print_value(dm_diff_k[0].data<T>(), this->paraMat_.get_col_size(), this->paraMat_.get_row_size());
163-
for (auto& d : dm_diff_k) { LR_Util::matsym(d.data<T>(), this->nbasis, this->paraMat_); } // symmetrize
161+
std::cout << "dm_diff_k T(k) before symmetrization, istate " + std::to_string(istate) << std::endl;
162+
LR_Util::print_value(dm_diff_k[0].data<T>(), this->paraMat_.get_col_size(), this->paraMat_.get_row_size());
163+
// for (auto& d : dm_diff_k) { LR_Util::matsym(d.data<T>(), this->nbasis, this->paraMat_); } // symmetrize
164164
// std::cout << "dm_diff_k T(k) after symmetrization, istate " + std::to_string(istate) << std::endl;
165165
// LR_Util::print_value(dm_diff_k[0].data<T>(), this->paraMat_.get_col_size(), this->paraMat_.get_row_size());
166166

167167
const std::vector<ct::Tensor>& dm_relaxed_k = cal_dm_trans_pblas(Z.template data<T>() + offset, this->paraX_[ispin], c, this->paraC_, this->nbasis, this->nocc[ispin], this->nvirt[ispin], this->paraMat_);
168-
// std::cout << "dm_relaxed_k Z(k) before symmetrization, istate " + std::to_string(istate) << std::endl;
169-
// LR_Util::print_value(dm_relaxed_k[0].data<T>(), this->paraMat_.get_col_size(), this->paraMat_.get_row_size());
168+
std::cout << "dm_relaxed_k Z(k) before symmetrization, istate " + std::to_string(istate) << std::endl;
169+
LR_Util::print_value(dm_relaxed_k[0].data<T>(), this->paraMat_.get_col_size(), this->paraMat_.get_row_size());
170170
for (auto& d : dm_relaxed_k) { LR_Util::matsym(d.data<T>(), this->nbasis, this->paraMat_); } // symmetrize
171-
// std::cout << "dm_relaxed_k Z(k) after symmetrization, istate " + std::to_string(istate) << std::endl;
172-
// LR_Util::print_value(dm_relaxed_k[0].data<T>(), this->paraMat_.get_col_size(), this->paraMat_.get_row_size());
171+
std::cout << "dm_relaxed_k Z(k) after symmetrization, istate " + std::to_string(istate) << std::endl;
172+
LR_Util::print_value(dm_relaxed_k[0].data<T>(), this->paraMat_.get_col_size(), this->paraMat_.get_row_size());
173173
// relaxed difference density matrix
174174
const std::vector<ct::Tensor>& relaxed_diff_dm_k = dm_diff_k + dm_relaxed_k;
175+
const elecstate::DensityMatrix<T, T>& diff_dm =
176+
LR_Util::build_dm_from_dmk<T, T>(dm_diff_k,
177+
this->paraMat_, this->nk, this->kv.kvec_d, this->ucell, this->gd, this->orb_cutoff_);
175178
const elecstate::DensityMatrix<T, T>& relaxed_diff_dm =
176179
LR_Util::build_dm_from_dmk<T, T>(relaxed_diff_dm_k,
177180
this->paraMat_, this->nk, this->kv.kvec_d, this->ucell, this->gd, this->orb_cutoff_);
@@ -241,6 +244,20 @@ std::vector<ModuleBase::matrix> LR::ESolver_LR<T, TR>::cal_force(const int ispin
241244
if (PARAM.inp.test_force)
242245
ModuleIO::print_force(GlobalV::ofs_running, this->ucell, "OVERLAP-EDM FORCE (eV/Angstrom)", force_overlap_edm, false);
243246

247+
if (PARAM.inp.test_force)
248+
{
249+
// test H[T] force (Z=0), non-EXX part
250+
elecstate::DensityMatrix<T, double> diff_dm_real(&this->paraMat_, 1, this->kv.kvec_d, this->nk);
251+
LR_Util::initialize_DMR(diff_dm_real, this->paraMat_, this->ucell, this->gd, this->orb_cutoff_);
252+
LR_Util::get_DMR_real_imag_part(diff_dm, diff_dm_real, 'R');
253+
254+
GlobalV::ofs_running << "========== [TEST H_GS-(T) force (Z=0), non-EXX part] ===========" << std::endl;
255+
ModuleBase::matrix force_hamiltgs_diff = lr_force.cal_force_hamilt_gs_dm_relaxed_diff(diff_dm_real, dm_gs, /*with_ewald=*/false);
256+
ModuleIO::print_force(GlobalV::ofs_running, this->ucell, "H_GS-T FORCE (without EXX) (eV/Angstrom)", force_hamiltgs_diff, false);
257+
GlobalV::ofs_running << "========== [\\TEST H_GS-(T) force (Z=0), non-EXX part] ===========" << std::endl;
258+
}
259+
260+
244261
#ifdef __EXX
245262
const double& alpha = this->exx_info.info_global.hybrid_alpha;
246263

@@ -253,15 +270,26 @@ std::vector<ModuleBase::matrix> LR::ESolver_LR<T, TR>::cal_force(const int ispin
253270
force_hxc_dmtrans += force_exx_dmtrans;
254271

255272
}
273+
256274
if (LR::exx_kernel_list().count(PARAM.inp.dft_functional))
257275
{
258276
const auto& Ds_gs = LR_Util::get_exx_Ds_spin1(dm_gs, this->ucell, this->kv, this->paraMat_); // returns 0.5*D[0]
259277
const auto& Ds_relaxed_diff = LR_Util::get_exx_Ds_spin1(relaxed_diff_dm, this->ucell, this->kv, this->paraMat_); // returns 0.5*D[0]
260278
// LR_Util::print_CV(Ds_relaxed_diff, "Ds_relaxed_diff for EXX force");
261-
ModuleBase::matrix force_exx_gs_diff = lr_force.cal_force_exx_gs_dm_relaxed_diff(Ds_gs, Ds_relaxed_diff, alpha * 4.0); // cancel the two 0.5s in Ds
279+
ModuleBase::matrix force_exx_gs_relaxed_diff = lr_force.cal_force_exx_gs_dm_relaxed_diff(Ds_gs, Ds_relaxed_diff, alpha * 4.0); // cancel the two 0.5s in Ds
280+
if (PARAM.inp.test_force)
281+
ModuleIO::print_force(GlobalV::ofs_running, this->ucell, "EXX GS-(T+Z) FORCE (eV/Angstrom)", force_exx_gs_relaxed_diff, false);
282+
force_hamiltgs_relaxed_diff += force_exx_gs_relaxed_diff;
283+
262284
if (PARAM.inp.test_force)
263-
ModuleIO::print_force(GlobalV::ofs_running, this->ucell, "EXX GS-(T+Z) FORCE (eV/Angstrom)", force_exx_gs_diff, false);
264-
force_hamiltgs_relaxed_diff += force_exx_gs_diff;
285+
{
286+
// test H[T] force (Z=0), EXX part
287+
const auto& Ds_diff = LR_Util::get_exx_Ds_spin1(diff_dm, this->ucell, this->kv, this->paraMat_); // returns 0.5*D[0]
288+
GlobalV::ofs_running << "========== [TEST H_GS-(T) force (Z=0), EXX part] ===========" << std::endl;
289+
ModuleBase::matrix force_exx_gs_diff = lr_force.cal_force_exx_gs_dm_relaxed_diff(Ds_gs, Ds_diff, alpha * 4.0); // cancel the two 0.5s in Ds
290+
ModuleIO::print_force(GlobalV::ofs_running, this->ucell, "H_GS-T EXX FORCE (Z=0) (eV/Angstrom)", force_exx_gs_diff, false);
291+
GlobalV::ofs_running << "========== [\\TEST H_GS-(T) force (Z=0), EXX part] ===========" << std::endl;
292+
}
265293
}
266294
#endif
267295
forces[istate] = force_hxc_dmtrans + force_hamiltgs_relaxed_diff + force_overlap_edm;
@@ -319,7 +347,7 @@ void LR::ESolver_LR<T, TR>::test_force()
319347
if (this->nbasis == 2 && ucell.nat == 2)
320348
{
321349
// lr_force.cal_H2_sz_center2_deriv(orb_cutoff_, kv); // for gradient
322-
// lr_force.cal_H2_sz_center4(orb_cutoff_, kv, /*is_grad=*/false); // for Coulomb energy
350+
// lr_force.cal_H2_sz_center4(orb_cutoff_, kv, /*is_grad=*/false); // for 4-center integrals
323351
// lr_force.cal_H2_sz_center4(orb_cutoff_, kv, /*is_grad=*/true); // for gradient
324352
// exit(0);
325353
}

source/module_lr/Grad/force/lr_force.cpp

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,9 @@ namespace LR
138138
for (std::size_t idim = 0; idim < 3; ++idim)
139139
for (const auto& force_item : exx_lri_kernel.force[idim])
140140
f_exx_dmtrans(force_item.first, idim) = std::real(force_item.second);
141-
const double fac = -2.0 * alpha; //-2 is the same as post_process_Hexx (which didn't act on Hs)
142-
return f_exx_dmtrans * fac;
141+
const double fac = -2.0 * alpha; //-2 is the same as post_process_Hexx, Hartree to Ry (which didn't act on Hs)
142+
return f_exx_dmtrans * fac; // dm_trans (DX) already contain the spin channel (sqrt(2) times of up/down channel DX)
143+
// return f_exx_dmtrans * fac * 2; // 2 is the same in post_process_Eexx at nspin=1 ( up->up + down->down, 2 spin-conserving transitions)
143144
}
144145

145146
template<typename TK>
@@ -151,19 +152,55 @@ namespace LR
151152
{
152153
ModuleBase::matrix f_exx_gs_diff(this->ucell_.nat, 3);
153154
auto& exx_lri_kernel = this->exx_lri_.lock()->get();
154-
exx_lri_kernel.set_Ds(dm_gs, this->exx_lri_.lock()->get_info().dm_threshold, spin_suffix);
155-
exx_lri_kernel.cal_Hs({ "", "", spin_suffix }); // using dm_gs
156-
// // test: print Hs here
157-
// LR_Util::print_CV(exx_lri_kernel.Hs, "EXX Hs from gs dm in cal_force_exx_gs_dm_relaxed_diff");
158-
// auto* lr_ptr = dynamic_cast<RI::LR<int, int, 3, TK>*>(&exx_lri_kernel); // wrong:
159-
// assert(lr_ptr != nullptr);
160155
RI::LR<int, int, 3, TK> lr_exx_kernel(std::move(exx_lri_kernel));
161-
lr_exx_kernel.cal_force(relaxed_diff_dm, { "", "", spin_suffix , "", "" }); // using relaxed_diff_dm
162-
exx_lri_kernel = std::move(lr_exx_kernel); // move back
163-
for (std::size_t idim = 0; idim < 3; ++idim)
164-
for (const auto& force_item : exx_lri_kernel.force[idim])
165-
f_exx_gs_diff(force_item.first, idim) = std::real(force_item.second);
166-
const double fac = -2.0 * alpha; //-2 is the same as post_process_Hexx (a.u. to Ry, which didn't act on Hs)
156+
157+
auto add_force_from_kernel = [&]() {
158+
for (std::size_t idim = 0; idim < 3; ++idim)
159+
for (const auto& force_item : lr_exx_kernel.force[idim])
160+
f_exx_gs_diff(force_item.first, idim) += std::real(force_item.second);
161+
};
162+
163+
auto transpose_dm = [](const std::map<int, std::map<TAC, RI::Tensor<TK>>>& dm)
164+
-> std::map<int, std::map<TAC, RI::Tensor<TK>>>
165+
{
166+
std::map<int, std::map<TAC, RI::Tensor<TK>>> dm_transpose;
167+
for (const auto& pair0 : dm)
168+
for (const auto& pair1 : pair0.second)
169+
{
170+
const int& iat0 = pair0.first;
171+
const int& iat1 = pair1.first.first;
172+
const auto& R = pair1.first.second;
173+
dm_transpose[iat1][{iat0, { -R[0], -R[1], -R[2] }}] = pair1.second.transpose();
174+
}
175+
return dm_transpose;
176+
};
177+
178+
// `cal_force` calculates Pulay term.
179+
// If D_IJ = D_KL(H - F = Pulay), it caluclates 0.5 * d(ik | jl).
180+
// Multiply spin factor (outside) on it gives the final result.
181+
// The spin factor is not hard-coded in this function.
182+
183+
// 1. Pulay term
184+
lr_exx_kernel.set_Ds(dm_gs, this->exx_lri_.lock()->get_info().dm_threshold, spin_suffix);
185+
lr_exx_kernel.cal_Hs({ "", "", spin_suffix }); // using dm_gs as D_KL
186+
lr_exx_kernel.cal_force(relaxed_diff_dm, { "", "", spin_suffix , "", "" }); // using relaxed_diff_dm as D_IJ
187+
add_force_from_kernel();
188+
189+
// 2. Hellmann-Feynman term
190+
const auto& dm_gs_transpose = transpose_dm(dm_gs);
191+
const auto& relaxed_diff_dm_transpose = transpose_dm(relaxed_diff_dm);
192+
lr_exx_kernel.set_Ds(relaxed_diff_dm_transpose, this->exx_lri_.lock()->get_info().dm_threshold, spin_suffix);
193+
lr_exx_kernel.cal_Hs({ "", "", spin_suffix }); // using relaxed_diff_dm as D_KL
194+
lr_exx_kernel.cal_force(dm_gs_transpose, { "", "", spin_suffix , "", "" }); // using dm_gs as D_IJ
195+
add_force_from_kernel();
196+
197+
// move back
198+
exx_lri_kernel = std::move(lr_exx_kernel);
199+
200+
// -2 * 0.5 * alpha
201+
// -2 is the same as post_process_Hexx (a.u. to Ry, which didn't act on Hs)
202+
// 0.5 is the 2-electron integral prefactor
203+
const double fac = -alpha;
167204
return f_exx_gs_diff * fac;
168205
}
169206
#endif

0 commit comments

Comments
 (0)