Skip to content

Commit 4b8a763

Browse files
committed
fix nspin=1
1 parent 0f118c4 commit 4b8a763

5 files changed

Lines changed: 67 additions & 34 deletions

File tree

source/source_esolver/esolver_lr_lcao_tddft.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -845,7 +845,7 @@ void ModuleESolver::ESolver_LR<T, TR>::init_pot(const Charge& chg_gs)
845845
// while the S1 integrand indexes them as if there were 1 -- it does not even read a
846846
// consistent spin combination. Use `ST::S2_gs` there, which is exactly half of S2_singlet,
847847
// matching the `K_Hxc(singlet) = 2 * pot_hxc_gs` convention of the gradient operators.
848-
const ST st_gs = (nspin == 1) ? ST::S1 : (oshell ? ST::S2_updown : ST::S2_gs);
848+
const ST st_gs = (nspin == 1) ? ST::S1_gs : (oshell ? ST::S2_updown : ST::S2_gs);
849849
// `pot_hxc_gs` supplies the $g^{xc}$ of both $W^c$ and the force term, for either spin.
850850
// Those two call sites are guarded by `has_local_xc(xc_kernel)` -- the *LR* kernel name --
851851
// so an `xc_kernel rpa` run never touches them however local `dft_functional` is.
@@ -918,7 +918,8 @@ void ModuleESolver::ESolver_LR<T, TR>::read_ks_chg(Charge& chg_gs)
918918
for (int is = 0; is < this->nspin; ++is)
919919
{
920920
std::stringstream ssc;
921-
ssc << this->in_dir << "chgs" << is + 1 << ".cube";
921+
if (this->nspin == 1) { ssc << this->in_dir << "chg.cube"; }
922+
else { ssc << this->in_dir << "chgs" << is + 1 << ".cube"; }
922923
GlobalV::ofs_running << ssc.str() << std::endl;
923924
if (ModuleIO::read_vdata_palgrid(Pgrid,
924925
GlobalV::MY_RANK,

source/source_lcao/module_lr/Grad/force/lr_force.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
// #include "source_lcao/module_lr/utils/lr_util_hcontainer.h"
88
namespace LR
99
{
10+
/// `dm_gs` carries the ground-state occupations, at nspin=1 they are 2 for fully occupied bands.
11+
inline double gs_dm_channel_factor() { return (PARAM.inp.nspin == 1) ? 0.5 : 1.0; }
12+
1013
template<typename TK>
1114
Charge LR_Force<TK>::dm_to_charge(const elecstate::DensityMatrix<TK, double>& dm)
1215
{
@@ -128,7 +131,8 @@ namespace LR
128131
std::vector<const double*> vr_eff = { v_lin.c };
129132
ModuleGint::cal_gint_fvl(1, vr_eff, dm_gs.get_DMR_vector(), true, false, &fhxc_dvhxc, &stress_tmp);
130133
}
131-
if(!reproduce_gs) {fhxc_dvhxc *= 2;} // for the two channels of the ground-state dm.
134+
if(!reproduce_gs) {fhxc_dvhxc *= 2;} // for the two channels of the ground-state dm.
135+
fhxc_dvhxc *= gs_dm_channel_factor();
132136

133137
// 4. kinetic (Pulay)
134138
std::vector<hamilt::HContainer<double>> dT = cal_hs_grad('T', this->ucell_, this->pv_, this->gd_, this->two_center_bundle_);
@@ -193,6 +197,7 @@ namespace LR
193197
ModuleBase::matrix stress_tmp;
194198
std::vector<const double*> vr_eff = { v2.c };
195199
ModuleGint::cal_gint_fvl(1, vr_eff, dm_gs.get_DMR_vector(), true, false, &f, &stress_tmp);
200+
f *= gs_dm_channel_factor();
196201
return f;
197202
}
198203

source/source_lcao/module_lr/potentials/pot_hxc_lrtd.cpp

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ namespace LR
5959
// Hartree
6060
switch (this->spin_type_)
6161
{
62-
case SpinType::S1: case SpinType::S2_updown: case SpinType::S2_gs:
62+
case SpinType::S1_gs: case SpinType::S2_updown: case SpinType::S2_gs:
6363
v_eff += elecstate::H_Hartree_pw::v_hartree(ucell, const_cast<ModulePW::PW_Basis*>(&this->rho_basis_), 1, rho);
6464
break;
65-
case SpinType::S2_singlet:
65+
case SpinType::S1: case SpinType::S2_singlet:
6666
v_eff += 2 * elecstate::H_Hartree_pw::v_hartree(ucell, const_cast<ModulePW::PW_Basis*>(&this->rho_basis_), 1, rho);
6767
break;
6868
default:
@@ -90,11 +90,16 @@ namespace LR
9090
switch (s)
9191
{
9292
case SpinType::S1:
93-
funcs[s] = [this, &fxc](FXC_PARA_TYPE)->void
93+
case SpinType::S1_gs:
94+
{
95+
// S1_gs is exactly half of S1 (see the SpinType doc in the header).
96+
const double prefac = (s == SpinType::S1_gs) ? 1.0 : 2.0;
97+
funcs[s] = [this, &fxc, prefac](FXC_PARA_TYPE)->void
9498
{
95-
for (int ir = 0;ir < nrxx;++ir) { v_eff(0, ir) += ModuleBase::e2 * fxc.v2rho2.at(ir) * rho[ir]; }
99+
for (int ir = 0;ir < nrxx;++ir) { v_eff(0, ir) += ModuleBase::e2 * prefac * fxc.v2rho2.at(ir) * rho[ir]; }
96100
};
97101
break;
102+
}
98103
case SpinType::S2_singlet:
99104
case SpinType::S2_gs:
100105
{
@@ -140,7 +145,10 @@ namespace LR
140145
switch (s)
141146
{
142147
case SpinType::S1:
143-
funcs[s] = [this, &fxc](FXC_PARA_TYPE)->void
148+
case SpinType::S1_gs:
149+
{
150+
const double prefac = (s == SpinType::S1_gs) ? 1.0 : 2.0; // S1_gs is exactly half of S1.
151+
funcs[s] = [this, &fxc, prefac](FXC_PARA_TYPE)->void
144152
{
145153
// test: output drho
146154
// double thr = 1e-1;
@@ -176,9 +184,10 @@ namespace LR
176184
vxc_tmp[ir] += (fxc.v2rho2.at(ir) * rho[ir]
177185
+ fxc.v2rhosigma_2drho.at(ir) * drho.at(ir));
178186
}
179-
BlasConnector::axpy(nrxx, ModuleBase::e2, vxc_tmp.data(), 1, v_eff.c, 1);
187+
BlasConnector::axpy(nrxx, ModuleBase::e2 * prefac, vxc_tmp.data(), 1, v_eff.c, 1);
180188
};
181189
break;
190+
}
182191
case SpinType::S2_singlet:
183192
case SpinType::S2_gs:
184193
{

source/source_lcao/module_lr/potentials/pot_hxc_lrtd.h

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ namespace LR
1010
class PotHxcLR : public PotLRBase
1111
{
1212
public:
13-
/// S1: K^Hartree + K^xc
13+
/// S1: the nspin=1 singlet LR kernel, 2*K^Hartree + 2*K^xc.
14+
/// The factor 2 on *both* terms is what makes it equal to S2_singlet.
15+
/// S1_gs: the nspin=1 counterpart of S2_gs, i.e. the *ground-state* Hxc kernel
16+
/// K^Hartree + K^xc = S1 / 2. This is what `pot_hxc_gs` needs at nspin=1.
1417
/// S2_singlet: 2*K^Hartree + K^xc_{upup} + K^xc_{updown}
1518
/// S2_triplet: K^xc_{upup} - K^xc_{updown}
1619
/// S2_updown: K^Hartree + (K^xc_{upup}, K^xc_{updown}, K^xc_{downup} or K^xc_{downdown}), according to `ispin_op` (for spin-polarized systems)
17-
/// S2_gs: the nspin=2 counterpart of S1, i.e. the *ground-state* Hxc kernel
18-
/// K^Hartree + (K^xc_{upup} + K^xc_{updown})/2 = S2_singlet / 2.
19-
/// Used for `pot_hxc_gs` in LR gradients, where the convention is
20-
/// `K_Hxc(singlet) = 2 * pot_hxc_gs` (see `cal_multiplier_w_from_z.h`).
20+
/// S2_gs: the nspin=2 *ground-state* Hxc kernel K^Hartree + (K^xc_{upup} + K^xc_{updown})/2 = S2_singlet / 2.
21+
/// Used for `pot_hxc_gs` in LR gradients, where the convention is `K_Hxc(singlet) = 2 * pot_hxc_gs`.
2122
/// The 1/2 on the xc part is not a convention but the chain rule: the derivative is
2223
/// taken w.r.t. the *total* density matrix, and $\partial v_u/\partial\rho =
23-
/// (f_{uu}+f_{ud})/2$ because $\rho_u=\rho_d=\rho/2$. The Hartree part needs no
24-
/// halving, which is exactly why S1 and S2_gs share the same Hartree weight.
24+
/// (f_{uu}+f_{ud})/2$ because $\rho_u=\rho_d=\rho/2$. The Hartree part needs no halving.
2525
/// Do NOT use S1 here when nspin=2: `KernelXC` is built with `PARAM.inp.nspin`, so the
2626
/// kernel arrays carry 3 spin components per grid point while the S1 integrand indexes
2727
/// them as if there were 1.
28-
enum SpinType { S1 = 0, S2_singlet = 1, S2_triplet = 2, S2_updown = 3, S2_gs = 4 };
28+
enum SpinType { S1 = 0, S2_singlet = 1, S2_triplet = 2, S2_updown = 3, S2_gs = 4, S1_gs = 5 };
2929
/// XCType here is to determin the method of integration from kernel to potential, not the way calculating the kernel
3030
enum XCType { None = 0, LDA = 1, GGA = 2, HYB_GGA = 4 };
3131
/// constructor building exchange-correlation kernel

source/source_lcao/module_lr/potentials/xc_kernel.cpp

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -223,12 +223,17 @@ void LR::KernelXC::f_xc_libxc(const int& nspin, const double& omega, const doubl
223223
xc_gga_fxc(&func, nrxx, rho.data(), sigma.data(), v2rho2_tmp.data(), v2rhosigma_tmp.data(), v2sigma2_tmp.data());
224224
// std::cout << "max element of v2sigma2_tmp: " << *std::max_element(v2sigma2_tmp.begin(), v2sigma2_tmp.end()) << std::endl;
225225
// std::cout << "rho corresponding to max element of v2sigma2_tmp: " << rho[(std::max_element(v2sigma2_tmp.begin(), v2sigma2_tmp.end()) - v2sigma2_tmp.begin()) / 6] << std::endl;
226-
// cut off by sgn
227-
cutoff_grid_data_spin2(vrho_tmp, sgn);
228-
cutoff_grid_data_spin2(vsigma_tmp, sgn);
229-
cutoff_grid_data_spin2(v2rho2_tmp, sgn);
230-
cutoff_grid_data_spin2(v2rhosigma_tmp, sgn);
231-
cutoff_grid_data_spin2(v2sigma2_tmp, sgn);
226+
// cut off by sgn. nspin=2 only: `cutoff_grid_data_spin2` assumes >1 component per
227+
// grid point (it asserts on it), and at nspin=1 there is exactly one, for which both
228+
// of its `for_each` ranges are empty -- the cutoff is a no-op anyway.
229+
if (nspin == 2)
230+
{
231+
cutoff_grid_data_spin2(vrho_tmp, sgn);
232+
cutoff_grid_data_spin2(vsigma_tmp, sgn);
233+
cutoff_grid_data_spin2(v2rho2_tmp, sgn);
234+
cutoff_grid_data_spin2(v2rhosigma_tmp, sgn);
235+
cutoff_grid_data_spin2(v2sigma2_tmp, sgn);
236+
}
232237
if (need_kxc)
233238
{
234239
xc_gga_kxc(&func, nrxx, rho.data(), sigma.data(),
@@ -503,23 +508,36 @@ void LR::KernelXC::build_gxc_coef(GxcCoef& dst, const bool triplet, const int& n
503508

504509
if (nspin == 1)
505510
{
506-
// Single component everywhere; all the weight sums collapse to 1 (section 3).
511+
// Single component everywhere; all the weight sums collapse to 1.
512+
//
513+
// ... and then the whole set is scaled by 4 to reach the *singlet* normalization, the same
514+
// one `nspin=2` produces below and the only one the rest of the gradient code knows about.
515+
// libxc's unpolarized derivatives are taken w.r.t. the TOTAL density, so for a closed shell
516+
// (rho_u = rho_d = rho/2) an n-th derivative is 2^(n-1) smaller than the singlet spin
517+
// combination: d^2E/drho^2 = (f_uu+f_ud)/2 -- which is why `SpinType::S1` carries a 2 --
518+
// and d^3E/drho^3 = (g_uuu + 3g_uud)/4, while the nspin=2 branch below builds
519+
// a_s2 = g_uuu + 2g_uud + g_udd = g_uuu + 3g_uud. Hence 4 here, 2 there.
520+
//
521+
// Measured on H2/SZ/LDA: without it the GXC DMTRANS force was 0.08549 eV/Ang against the
522+
// nspin=2 singlet's 0.17097 -- a factor 2, being 1/4 from this and 2 from the `dm_gs`
523+
// channel convention (`gs_dm_channel_factor` in `lr_force.cpp`).
524+
constexpr double to_singlet = 4.;
507525
#ifdef _OPENMP
508526
#pragma omp parallel for schedule(static, 4096)
509527
#endif
510528
for (int i = 0;i < nrxx;++i)
511529
{
512-
dst.a_s2[i] = v3r3[i];
530+
dst.a_s2[i] = to_singlet * v3r3[i];
513531
if (!is_gga) { continue; }
514-
dst.a_st[i] = v3r2s[i] * 4.;
515-
dst.a_t2[i] = v3rs2[i] * 4.;
516-
dst.a_q[i] = v2rs[i] * 2.;
517-
dst.c_s[i] = v2rs[i] * 4.;
518-
dst.c_t[i] = v2s2[i] * 8.;
519-
dst.e_s2[i] = v3r2s[i] * 2.;
520-
dst.e_st[i] = v3rs2[i] * 8.;
521-
dst.e_t2[i] = v3s3[i] * 8.;
522-
dst.e_q[i] = v2s2[i] * 4.;
532+
dst.a_st[i] = to_singlet * v3r2s[i] * 4.;
533+
dst.a_t2[i] = to_singlet * v3rs2[i] * 4.;
534+
dst.a_q[i] = to_singlet * v2rs[i] * 2.;
535+
dst.c_s[i] = to_singlet * v2rs[i] * 4.;
536+
dst.c_t[i] = to_singlet * v2s2[i] * 8.;
537+
dst.e_s2[i] = to_singlet * v3r2s[i] * 2.;
538+
dst.e_st[i] = to_singlet * v3rs2[i] * 8.;
539+
dst.e_t2[i] = to_singlet * v3s3[i] * 8.;
540+
dst.e_q[i] = to_singlet * v2s2[i] * 4.;
523541
}
524542
return;
525543
}

0 commit comments

Comments
 (0)