@@ -114,7 +114,7 @@ ct::Tensor ModuleESolver::ESolver_LR<T, TR>::solve_zvector_eqation(const int isp
114114 std::weak_ptr<Exx_LRI<T>>(this ->exx_lri ), this ->exx_info .info_global .hybrid_alpha ,
115115#endif
116116 std::weak_ptr<PotHxcLR>(this ->pot [ispin]), std::weak_ptr<PotHxcLR>(this ->pot_hxc_gs ),
117- this ->kv , this ->paraX_ , this ->paraC_ , this ->paraMat_ , this ->spin_types [ispin]);
117+ this ->kv , this ->paraX_ , this ->paraC_ , this ->paraMat_ , this ->spin_types [ispin], this -> openshell );
118118 ModuleBase::timer::end (" ESolver_LR" , " solve_zvector_eqation" );
119119 return Z;
120120}
@@ -123,6 +123,7 @@ template<typename T, typename TR>
123123std::vector<ModuleBase::matrix> ModuleESolver::ESolver_LR<T, TR >::cal_force(const int ispin)
124124{
125125 if (PARAM .inp .test_force && ispin == 0 ) { this ->test_force (); }
126+ if (this ->openshell ) { return this ->cal_force_openshell (); }
126127
127128 const ct::Tensor& Z = this ->solve_zvector_eqation (ispin);
128129
@@ -302,7 +303,10 @@ std::vector<ModuleBase::matrix> ModuleESolver::ESolver_LR<T, TR>::cal_force(cons
302303 const auto & Ds_gs = LR_Util::get_exx_Ds_spin1 (dm_gs, (*this ->ucell_ ), this ->kv , this ->paraMat_ ); // returns 0.5*D[0]
303304 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]
304305 // LR_Util::print_CV(Ds_relaxed_diff, "Ds_relaxed_diff for EXX force");
305- 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
306+ // `get_exx_Ds_spin1` feeds `split_m2D_ktoR(..., nspin=1)`, which reads only channel 0
307+ // with a 0.5 prefactor. For `dm_gs` that channel is $D^\text{gs}_\uparrow$ at nspin=2
308+ // but the spin-summed $D^\text{gs}$ at nspin=1, i.e. twice as large.
309+ ModuleBase::matrix force_exx_gs_relaxed_diff = lr_force.cal_force_exx_gs_dm_relaxed_diff (Ds_gs, Ds_relaxed_diff, alpha * 4.0 ) * gs_dm_channel_factor (); // cancel the two 0.5s in Ds
306310 if (PARAM .inp .test_force )
307311 ModuleIO::print_force (GlobalV::ofs_running, (*this ->ucell_ ), " EXX GS-(T+Z) FORCE (eV/Angstrom)" , force_exx_gs_relaxed_diff, false );
308312 force_hamiltgs_relaxed_diff += force_exx_gs_relaxed_diff;
@@ -312,7 +316,7 @@ std::vector<ModuleBase::matrix> ModuleESolver::ESolver_LR<T, TR>::cal_force(cons
312316 // test H[T] force (Z=0), EXX part
313317 const auto & Ds_diff = LR_Util::get_exx_Ds_spin1 (diff_dm, (*this ->ucell_ ), this ->kv , this ->paraMat_ ); // returns 0.5*D[0]
314318 GlobalV::ofs_running << " ========== [TEST H_GS-(T) force (Z=0), EXX part] ===========" << std::endl;
315- 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
319+ ModuleBase::matrix force_exx_gs_diff = lr_force.cal_force_exx_gs_dm_relaxed_diff (Ds_gs, Ds_diff, alpha * 4.0 ) * gs_dm_channel_factor () ; // cancel the two 0.5s in Ds
316320 ModuleIO::print_force (GlobalV::ofs_running, (*this ->ucell_ ), " H_GS-T EXX FORCE (Z=0) (eV/Angstrom)" , force_exx_gs_diff, false );
317321 GlobalV::ofs_running << " ========== [\\ TEST H_GS-(T) force (Z=0), EXX part] ===========" << std::endl;
318322 }
@@ -327,6 +331,171 @@ std::vector<ModuleBase::matrix> ModuleESolver::ESolver_LR<T, TR>::cal_force(cons
327331 return forces;
328332}
329333
334+ template <typename T, typename TR >
335+ std::vector<ModuleBase::matrix> ModuleESolver::ESolver_LR<T, TR >::cal_force_openshell()
336+ {
337+ ModuleBase::TITLE (" ESolver_LR" , " cal_force_openshell" );
338+ ModuleBase::timer::start (" ESolver_LR" , " cal_force" );
339+
340+ // Open shell: there is a single eigenproblem whose vector is the concatenation
341+ // [up-block | down-block], and every density matrix has two independent channels.
342+ // The spin-orbital formulas apply verbatim -- unlike the closed-shell singlet/triplet
343+ // algorithm, X here is normalized over BOTH channels, so it carries no implicit sqrt(2)
344+ // and none of the collapsed 2/4 factors are needed.
345+ const ct::Tensor& Z = this ->solve_zvector_eqation (0 );
346+
347+ const std::vector<int > ld_x = { this ->nk * this ->paraX_ [0 ].get_local_size (),
348+ this ->nk * this ->paraX_ [1 ].get_local_size () };
349+ const std::vector<int > off_x = { 0 , ld_x[0 ] };
350+ std::vector<psi::Psi<T>> c_spin;
351+ for (int is : {0 , 1 }) { c_spin.push_back (LR_Util::get_psi_spin (*this ->psi_ks , is, this ->nk )); }
352+
353+ LR_Force<T> lr_force ((*this ->ucell_ ), this ->kv .kvec_d , this ->paraMat_ ,
354+ *this ->pw_rhod , *this ->pw_rho , this ->locpp , this ->sf , this ->gd , this ->two_center_bundle_
355+ #ifdef __EXX
356+ , std::weak_ptr<Exx_LRI<T>>(this ->exx_lri ), this ->exx_info .info_global .hybrid_alpha
357+ #endif
358+ );
359+ GlobalV::ofs_running << " Start to calculate excited-state force of updown (open shell)" << std::endl;
360+
361+ std::vector<ModuleBase::matrix> forces (this ->nstates );
362+ for (int istate = 0 ;istate < this ->nstates ;++istate)
363+ {
364+ const int offset = istate * this ->nloc_per_state ;
365+ const T* const X_istate = this ->X [0 ].template data <T>() + offset;
366+ const T* const Z_istate = Z.template data <T>() + offset;
367+
368+ // 1. the k-space blocks of each spin channel
369+ std::vector<std::vector<ct::Tensor>> dmx_k (2 ), dmdiff_k (2 ), relaxed_k (2 );
370+ for (int is : {0 , 1 })
371+ {
372+ dmx_k[is] = cal_dm_trans_pblas (X_istate + off_x[is], this ->paraX_ [is], c_spin[is], this ->paraC_ ,
373+ this ->nbasis , this ->nocc [is], this ->nvirt [is], this ->paraMat_ );
374+ dmdiff_k[is] = cal_dm_diff_pblas (X_istate + off_x[is], this ->paraX_ [is], c_spin[is], this ->paraC_ ,
375+ this ->nbasis , this ->nocc [is], this ->nvirt [is], this ->paraMat_ );
376+ std::vector<ct::Tensor> dmz_k = cal_dm_trans_pblas (Z_istate + off_x[is], this ->paraX_ [is], c_spin[is],
377+ this ->paraC_ , this ->nbasis , this ->nocc [is], this ->nvirt [is], this ->paraMat_ );
378+ for (auto & d : dmz_k) { LR_Util::matsym (d.template data <T>(), this ->nbasis , this ->paraMat_ ); }
379+ relaxed_k[is] = dmdiff_k[is] + dmz_k;
380+ }
381+
382+ // 2. $D^X$. Complex and UN-symmetrized first (the EXX kernel needs the full
383+ // non-symmetric $D^X$), then the real symmetrized copy for the grid Hxc force --
384+ // `build_dm_from_dmk_spin` symmetrizes IN PLACE, hence the ordering.
385+ auto dm_trans = LR_Util::build_dm_from_dmk_spin<T, T>(dmx_k,
386+ this ->paraMat_ , this ->nk , this ->kv .kvec_d , (*this ->ucell_ ), this ->gd , this ->orb_cutoff_ );
387+ LR_Util::transpose_DMR (dm_trans, (*this ->ucell_ ).nat );
388+ auto dm_trans_real = LR_Util::build_dm_from_dmk_spin<T, double >(dmx_k,
389+ this ->paraMat_ , this ->nk , this ->kv .kvec_d , (*this ->ucell_ ), this ->gd , this ->orb_cutoff_ ,
390+ /* symmetrize=*/ true );
391+ LR_Util::transpose_DMR (dm_trans_real, (*this ->ucell_ ).nat );
392+
393+ // 3. the relaxed difference density matrix $T+D^Z$
394+ const elecstate::DensityMatrix<T, T>& relaxed_diff_dm =
395+ LR_Util::build_dm_from_dmk_spin<T, T>(relaxed_k,
396+ this ->paraMat_ , this ->nk , this ->kv .kvec_d , (*this ->ucell_ ), this ->gd , this ->orb_cutoff_ );
397+ elecstate::DensityMatrix<T, double > relaxed_diff_dm_real (&this ->paraMat_ , 2 , this ->kv .kvec_d , this ->nk );
398+ LR_Util::initialize_DMR (relaxed_diff_dm_real, this ->paraMat_ , (*this ->ucell_ ), this ->gd , this ->orb_cutoff_ );
399+ LR_Util::get_DMR_real_imag_part (relaxed_diff_dm, relaxed_diff_dm_real, ' R' );
400+
401+ // 4. the energy-weighted density matrix
402+ std::weak_ptr<PotHxcLR> pot_weak = this ->pot [0 ];
403+ std::weak_ptr<PotHxcLR> pot_hxc_gs_weak = this ->pot_hxc_gs ;
404+ #ifdef __EXX
405+ std::weak_ptr<Exx_LRI<T>> exx_lri_weak = this ->exx_lri ;
406+ #endif
407+ const std::vector<std::vector<ct::Tensor>>& edm_k =
408+ cal_edm_from_XZ_istate_openshell (X_istate, Z_istate,
409+ this ->pelec ->ekb .c [istate], this ->eig_ks .c , dm_trans,
410+ *this ->psi_ks , this ->nspin , this ->nbasis , this ->nocc , this ->nvirt ,
411+ (*this ->ucell_ ), this ->orb_cutoff_ ,
412+ #ifdef __EXX
413+ exx_lri_weak, this ->exx_info .info_global .hybrid_alpha ,
414+ #endif
415+ pot_weak, pot_hxc_gs_weak,
416+ this ->kv , this ->gd , this ->paraX_ , this ->paraC_ , this ->paraMat_ , this ->xc_kernel );
417+ elecstate::DensityMatrix<T, double > edm_real = LR_Util::build_dm_from_dmk_spin<T, double >(edm_k,
418+ this ->paraMat_ , this ->nk , this ->kv .kvec_d , (*this ->ucell_ ), this ->gd , this ->orb_cutoff_ ,
419+ /* symmetrize=*/ true );
420+
421+ // 5. the force terms
422+ ModuleBase::matrix force_hxc_dmtrans = lr_force.cal_force_hxc_dmtrans (dm_trans_real, *this ->pot [0 ]);
423+ if (PARAM .inp .test_force )
424+ ModuleIO::print_force (GlobalV::ofs_running, (*this ->ucell_ ), " HXC DMTRANS FORCE (eV/Angstrom)" , force_hxc_dmtrans, false );
425+
426+ const elecstate::DensityMatrix<T, double >& dm_gs = this ->cal_dm_gs ();
427+
428+ // the $g^{xc}$ half of $\partial_x K[D^X]D^X$, i.e. the derivative of the xc kernel
429+ // through the ground-state density. Only for local kernels.
430+ if (LR_Util::has_local_xc (this ->xc_kernel ))
431+ {
432+ PotGradXCLR pot_grad (this ->pot_hxc_gs ->xc_kernel_components (), this ->pot_hxc_gs ->get_rho_basis (),
433+ (*this ->ucell_ ), this ->pot_hxc_gs ->nrxx , /* triplet=*/ false );
434+ ModuleBase::matrix force_gxc_dmtrans =
435+ lr_force.cal_force_gxc_dmtrans_openshell (dm_trans_real, dm_gs, pot_grad);
436+ if (PARAM .inp .test_force )
437+ ModuleIO::print_force (GlobalV::ofs_running, (*this ->ucell_ ), " GXC DMTRANS FORCE (eV/Angstrom)" , force_gxc_dmtrans, false );
438+ force_hxc_dmtrans += force_gxc_dmtrans;
439+ }
440+
441+ ModuleBase::matrix force_hamiltgs_relaxed_diff = lr_force.cal_force_hamilt_gs_dm_relaxed_diff (
442+ relaxed_diff_dm_real, dm_gs, false , this ->pot_hxc_gs .get ());
443+ if (PARAM .inp .test_force )
444+ ModuleIO::print_force (GlobalV::ofs_running, (*this ->ucell_ ), " H_GS-(T+Z) FORCE (without EXX) (eV/Angstrom)" , force_hamiltgs_relaxed_diff, false );
445+
446+ ModuleBase::matrix force_overlap_edm = lr_force.cal_force_overlap_edm (edm_real);
447+ if (PARAM .inp .test_force )
448+ ModuleIO::print_force (GlobalV::ofs_running, (*this ->ucell_ ), " OVERLAP-EDM FORCE (eV/Angstrom)" , force_overlap_edm, false );
449+
450+ #ifdef __EXX
451+ const double & alpha = this ->exx_info .info_global .hybrid_alpha ;
452+ // Exchange is spin-diagonal, so each channel is done independently and summed.
453+ // `get_exx_Ds_gs` returns the channels unscaled (SPIN_multiple = 1 at nspin=2), unlike
454+ // the closed-shell `get_exx_Ds_spin1` which returns 0.5*D. Counting the closed-shell
455+ // `alpha*4.0` back down for both terms:
456+ // $D^XD^X$: each slot goes 0.5*D^X_tot -> D^X_is, i.e. x2 each, and the explicit
457+ // sum over is adds another x2 -- but $D^X_\text{tot}=\sqrt2 D^X_\sigma$ eats one,
458+ // so 4/(2*2) * ... = `alpha`.
459+ // $D^\text{gs}(T{+}D^Z)$: the left slot goes 0.5*D_up -> D_is (x2); the right slot
460+ // goes 0.5*(T+Z)_tot = (T+Z)_up -> (T+Z)_is (x1, no sqrt2 here); the explicit sum
461+ // over is adds x2. So 4/(2*1*2) = `alpha` as well -- NOT `2*alpha`: the earlier
462+ // comment forgot that the closed-shell right slot is already the spin SUM, which is
463+ // exactly what the `for (is)` loop below now supplies.
464+ if (LR::exx_kernel_list ().count (this ->xc_kernel ))
465+ {
466+ const auto & Ds_trans = LR_Util::get_exx_Ds_gs (dm_trans, (*this ->ucell_ ), this ->kv , this ->paraMat_ );
467+ ModuleBase::matrix force_exx_dmtrans (this ->ucell_ ->nat , 3 );
468+ for (int is : {0 , 1 })
469+ {
470+ force_exx_dmtrans += lr_force.cal_force_exx_dm_trans (Ds_trans[is], alpha, std::to_string (is));
471+ }
472+ if (PARAM .inp .test_force )
473+ ModuleIO::print_force (GlobalV::ofs_running, (*this ->ucell_ ), " EXX DMTRANS FORCE (eV/Angstrom)" , force_exx_dmtrans, false );
474+ force_hxc_dmtrans += force_exx_dmtrans;
475+ }
476+ if (LR::exx_kernel_list ().count (PARAM .inp .dft_functional ))
477+ {
478+ const auto & Ds_gs = LR_Util::get_exx_Ds_gs (dm_gs, (*this ->ucell_ ), this ->kv , this ->paraMat_ );
479+ const auto & Ds_relaxed_diff = LR_Util::get_exx_Ds_gs (relaxed_diff_dm, (*this ->ucell_ ), this ->kv , this ->paraMat_ );
480+ ModuleBase::matrix force_exx_gs_relaxed_diff (this ->ucell_ ->nat , 3 );
481+ for (int is : {0 , 1 })
482+ {
483+ force_exx_gs_relaxed_diff += lr_force.cal_force_exx_gs_dm_relaxed_diff (
484+ Ds_gs[is], Ds_relaxed_diff[is], alpha, std::to_string (is));
485+ }
486+ if (PARAM .inp .test_force )
487+ ModuleIO::print_force (GlobalV::ofs_running, (*this ->ucell_ ), " EXX GS-(T+Z) FORCE (eV/Angstrom)" , force_exx_gs_relaxed_diff, false );
488+ force_hamiltgs_relaxed_diff += force_exx_gs_relaxed_diff;
489+ }
490+ #endif
491+ forces[istate] = force_hxc_dmtrans + force_hamiltgs_relaxed_diff + force_overlap_edm;
492+ }
493+ ModuleBase::timer::end (" ESolver_LR" , " cal_force" );
494+ print_force (forces, std::cout);
495+ print_force (forces, GlobalV::ofs_running);
496+ return forces;
497+ }
498+
330499template <typename T, typename TR >
331500elecstate::DensityMatrix<T, double > ModuleESolver::ESolver_LR<T, TR >::cal_dm_gs()
332501{
0 commit comments