Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions source/source_lcao/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ if(ENABLE_LCAO)
module_operator_lcao/td_pot_hybrid.cpp
module_operator_lcao/dspin_lcao.cpp
module_operator_lcao/dftu_lcao.cpp
module_operator_lcao/operator_force_stress_utils.cpp
pulay_fs_center2.cpp
FORCE_STRESS.cpp
FORCE_gamma.cpp
Expand Down
153 changes: 125 additions & 28 deletions source/source_lcao/FORCE_STRESS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
#include "source_lcao/module_operator_lcao/dftu_lcao.h"
#include "source_lcao/module_operator_lcao/dspin_lcao.h"
#include "source_lcao/module_operator_lcao/nonlocal_new.h"
#include "source_lcao/module_operator_lcao/ekinetic_new.h"
#include "source_lcao/module_operator_lcao/overlap_new.h"
#include "source_lcao/pulay_fs.h"


// mohan add 2025-11-04
Expand Down Expand Up @@ -94,7 +97,8 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell,

const int nat = ucell.nat;

ForceStressArrays fsr; // mohan add 2024-06-15
// NOTE: ForceStressArrays is no longer needed as we use operator-based force calculation
// ForceStressArrays fsr; // removed - no longer needed

// total force : ModuleBase::matrix fcs;

Expand Down Expand Up @@ -161,46 +165,139 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell,
sigmaxc, pelec->f_en.etxc, pelec->charge, rhopw, locpp, sf);
}

//! atomic forces from integration (4 terms)
this->integral_part(PARAM.globalv.gamma_only_local, isforce, isstress,
ucell, gd, fsr, pelec, dmat.dm, psi, foverlap, ftvnl_dphi, // add dmat.dm, mohan 20251104
fvnl_dbeta, fvl_dphi, soverlap, stvnl_dphi, svnl_dbeta,
svl_dphi, fvnl_dalpha, svnl_dalpha, deepks,
two_center_bundle, orb, pv, kv);
// Calculate forces and stresses using new operator-based methods
// Step 1: Calculate Energy Density Matrix (EDM) for overlap force
// EDM = Σ_k w_k * ε_k * |ψ_k><ψ_k|
elecstate::DensityMatrix<T, double> edm = flk.cal_edm(pelec, *psi, *dmat.dm, kv, pv,
PARAM.inp.nspin, PARAM.inp.nbands, ucell, *this->RA);

// calculate force and stress for Nonlocal part
// Step 2: Handle different spin cases
if (PARAM.inp.nspin == 1 || PARAM.inp.nspin == 2)
{
hamilt::NonlocalNew<hamilt::OperatorLCAO<T, double>> tmp_nonlocal(nullptr,
kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd, two_center_bundle.overlap_orb_beta.get());

// For nspin=1 or nspin=2, use double precision
// Switch to spin channel 1 for DMR access
if (PARAM.inp.nspin == 2)
{
dmat.dm->switch_dmr(1);
edm.switch_dmr(1);
}

const hamilt::HContainer<double>* dmr = dmat.dm->get_DMR_pointer(1);
tmp_nonlocal.cal_force_stress(isforce, isstress, dmr, fvnl_dbeta, svnl_dbeta);
const hamilt::HContainer<double>* dmR = dmat.dm->get_DMR_pointer(1);
const hamilt::HContainer<double>* edmR = edm.get_DMR_pointer(1);

// Calculate kinetic force/stress (uses DM)
if (PARAM.inp.t_in_h)
{
hamilt::EkineticNew<hamilt::OperatorLCAO<T, double>> tmp_ekinetic(
nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd,
two_center_bundle.kinetic_orb.get());
tmp_ekinetic.cal_force_stress(isforce, isstress, dmR, ftvnl_dphi, stvnl_dphi);
}

// Calculate overlap force/stress (uses EDM)
hamilt::OverlapNew<hamilt::OperatorLCAO<T, double>> tmp_overlap(
nullptr, kv.kvec_d, nullptr, nullptr, &ucell, orb.cutoffs(), &gd,
two_center_bundle.overlap_orb.get());
tmp_overlap.cal_force_stress(isforce, isstress, edmR, foverlap, soverlap);

// Calculate nonlocal force/stress (uses DM)
hamilt::NonlocalNew<hamilt::OperatorLCAO<T, double>> tmp_nonlocal(
nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd,
two_center_bundle.overlap_orb_beta.get());
tmp_nonlocal.cal_force_stress(isforce, isstress, dmR, fvnl_dbeta, svnl_dbeta);

// Switch back to spin channel 0
if (PARAM.inp.nspin == 2)
{
dmat.dm->switch_dmr(0);
edm.switch_dmr(0);
}

// Calculate local potential force/stress (vl_dphi)
// This uses grid integration, not operator-based method
flk.ParaV = dmat.dm->get_paraV_pointer();
PulayForceStress::cal_pulay_fs(fvl_dphi, svl_dphi, *dmat.dm, ucell, pelec->pot,
isforce, isstress, false /*reset dm to gint*/);
}
else if (PARAM.inp.nspin == 4)
{
hamilt::NonlocalNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>> tmp_nonlocal(
nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd,
two_center_bundle.overlap_orb_beta.get());

// calculate temporary complex DMR for nonlocal force&stress
// In fact, only SOC part need the imaginary part of DMR for correct force&stress
// Calculate kinetic force/stress (uses DM)
if (PARAM.inp.t_in_h)
{
hamilt::EkineticNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>> tmp_ekinetic(
nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd,
two_center_bundle.kinetic_orb.get());
tmp_ekinetic.cal_force_stress(isforce, isstress, dmat.dm->get_DMR_pointer(1), ftvnl_dphi, stvnl_dphi);
}

// Calculate overlap force/stress (uses EDM)
hamilt::OverlapNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>> tmp_overlap(
nullptr, kv.kvec_d, nullptr, nullptr, &ucell, orb.cutoffs(), &gd,
two_center_bundle.overlap_orb.get());
tmp_overlap.cal_force_stress(isforce, isstress, edm.get_DMR_pointer(1), foverlap, soverlap);

// For nspin=4 (non-collinear), need complex DMR
// Create temporary complex DMR for DM
hamilt::HContainer<std::complex<double>> tmp_dmr(dmat.dm->get_DMR_pointer(1)->get_paraV());
std::vector<int> ijrs = dmat.dm->get_DMR_pointer(1)->get_ijr_info();
tmp_dmr.insert_ijrs(&ijrs);
tmp_dmr.allocate();
dmat.dm->cal_DMR_full(&tmp_dmr);
// Calculate nonlocal force/stress (uses DM)
hamilt::NonlocalNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>> tmp_nonlocal(
nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd,
two_center_bundle.overlap_orb_beta.get());
tmp_nonlocal.cal_force_stress(isforce, isstress, &tmp_dmr, fvnl_dbeta, svnl_dbeta);

// Calculate local potential force/stress (vl_dphi)
flk.ParaV = dmat.dm->get_paraV_pointer();
PulayForceStress::cal_pulay_fs(fvl_dphi, svl_dphi, *dmat.dm, ucell, pelec->pot,
isforce, isstress, false /*reset dm to gint*/);
}

// MPI reduction for forces
if (isforce)
{
Parallel_Reduce::reduce_pool(fvl_dphi.c, fvl_dphi.nr * fvl_dphi.nc);
}

// MPI reduction for stresses
if (isstress)
{
Parallel_Reduce::reduce_pool(svl_dphi.c, svl_dphi.nr * svl_dphi.nc);
}

// Handle DeePKS forces if enabled
#ifdef __MLALGO
if (PARAM.inp.deepks_scf)
{
const int nks = (PARAM.inp.nspin == 1 || PARAM.inp.nspin == 2) ? 1 : kv.get_nks();
if (PARAM.globalv.gamma_only_local)
{
DeePKS_domain::cal_f_delta<double>(deepks.ld.dm_r, ucell, orb, gd,
*flk.ParaV, nks, deepks.ld.deepks_param,
kv.kvec_d, deepks.ld.phialpha, deepks.ld.gedm,
fvnl_dalpha, isstress, svnl_dalpha);
}
else
{
DeePKS_domain::cal_f_delta<std::complex<double>>(deepks.ld.dm_r, ucell, orb, gd,
*flk.ParaV, nks, deepks.ld.deepks_param,
kv.kvec_d, deepks.ld.phialpha, deepks.ld.gedm,
fvnl_dalpha, isstress, svnl_dalpha);
}

if (isforce)
{
Parallel_Reduce::reduce_pool(fvnl_dalpha.c, fvnl_dalpha.nr * fvnl_dalpha.nc);
}
if (isstress)
{
Parallel_Reduce::reduce_pool(svnl_dalpha.c, svnl_dalpha.nr * svnl_dalpha.nc);
}
}
#endif

//! forces and stress from vdw
// Peize Lin add 2014-04-04, update 2021-03-09
Expand Down Expand Up @@ -275,13 +372,12 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell,
}
if (PARAM.inp.dft_plus_u == 2)
{
// dftu.force_stress(ucell, gd, pelec, pv, fsr, force_u, stress_u, kv);
// mohan modify 2025-11-03
// Old DFT+U implementation (dft_plus_u==2) still needs ForceStressArrays
ForceStressArrays fsr_dftu;
std::vector<std::vector<double>>* dmk_d = nullptr;
std::vector<std::vector<std::complex<double>>>* dmk_c = nullptr;
// add a new template function
assign_dmk_ptr<T>(dmat.dm, dmk_d, dmk_c, PARAM.globalv.gamma_only_local);
dftu.force_stress(ucell, gd, dmk_d, dmk_c, pv, fsr, force_u, stress_u, kv);
dftu.force_stress(ucell, gd, dmk_d, dmk_c, pv, fsr_dftu, force_u, stress_u, kv);
}
else
{
Expand Down Expand Up @@ -332,10 +428,11 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell,
}
}

if (!PARAM.globalv.gamma_only_local)
{
this->flk.finish_ftable(fsr);
}
// NOTE: finish_ftable is no longer needed as we don't use ForceStressArrays for overlap/kinetic
// if (!PARAM.globalv.gamma_only_local)
// {
// this->flk.finish_ftable(fsr);
// }

#ifdef __EXX
// Force and Stress contribution from exx
Expand Down Expand Up @@ -492,8 +589,8 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell,
//-----------------------------
// this->print_force("OVERLAP FORCE",foverlap,1,ry);
ModuleIO::print_force(GlobalV::ofs_running, ucell, "OVERLAP FORCE", foverlap, false);
// this->print_force("TVNL_DPHI force",ftvnl_dphi,PARAM.inp.test_force);
// this->print_force("VNL_DBETA force",fvnl_dbeta,PARAM.inp.test_force);
ModuleIO::print_force(GlobalV::ofs_running, ucell, "TVNL_DPHI force",ftvnl_dphi,false);
ModuleIO::print_force(GlobalV::ofs_running, ucell, "VNL_DBETA force",fvnl_dbeta,false);
// this->print_force("T_VNL FORCE",ftvnl,1,ry);
ModuleIO::print_force(GlobalV::ofs_running, ucell, "T_VNL FORCE", ftvnl, false);
ModuleIO::print_force(GlobalV::ofs_running, ucell, "VL_dPHI FORCE", fvl_dphi, false);
Expand Down
2 changes: 2 additions & 0 deletions source/source_lcao/edm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ elecstate::DensityMatrix<double, double> Force_LCAO<double>::cal_edm(const elecs
{
elecstate::cal_dm_psi(edm.get_paraV_pointer(), wg_ekb, psi, edm);
}
edm.init_DMR(ra, &ucell);
edm.cal_DMR();
return edm;
}

Expand Down
1 change: 1 addition & 0 deletions source/source_lcao/module_operator_lcao/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ add_library(
td_pot_hybrid.cpp
dspin_lcao.cpp
dftu_lcao.cpp
operator_force_stress_utils.cpp
)

if(ENABLE_COVERAGE)
Expand Down
67 changes: 67 additions & 0 deletions source/source_lcao/module_operator_lcao/ekinetic_force_stress.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once
#include "ekinetic_new.h"
#include "operator_force_stress_utils.hpp"
#include "source_base/timer.h"

namespace hamilt
{

template <typename TK, typename TR>
void EkineticNew<OperatorLCAO<TK, TR>>::cal_force_stress(const bool cal_force,
const bool cal_stress,
const HContainer<double>* dmR,
ModuleBase::matrix& force,
ModuleBase::matrix& stress)
{
ModuleBase::TITLE("EkineticNew", "cal_force_stress");
ModuleBase::timer::tick("EkineticNew", "cal_force_stress");

// Lambda function to calculate kinetic integral and its gradient
auto integral_calc = [this](int T1, int L1, int N1, int M1,
int T2, int L2, int N2, int M2,
const ModuleBase::Vector3<double>& dtau,
double* olm) {
this->intor_->calculate(T1, L1, N1, M1, T2, L2, N2, M2,
dtau * this->ucell->lat0, &olm[0], &olm[1]);
};

// Use unified template with ForceSign=+1, StressSign=-1 for kinetic operator
OperatorForceStress::cal_force_stress_2center<TK, TR, decltype(integral_calc), +1, -1>(
cal_force, cal_stress, dmR, this->ucell, this->gridD,
this->orb_cutoff_, dmR->get_paraV(), integral_calc, force, stress);

ModuleBase::timer::tick("EkineticNew", "cal_force_stress");
}

// Dummy implementations for cal_force_IJR and cal_stress_IJR
// These are not used in the simplified approach above
template <typename TK, typename TR>
void EkineticNew<OperatorLCAO<TK, TR>>::cal_force_IJR(
const int& iat1,
const int& iat2,
const Parallel_Orbitals* paraV,
const std::unordered_map<int, std::vector<double>>& nlm1_all,
const std::unordered_map<int, std::vector<double>>& nlm2_all,
const hamilt::BaseMatrix<TR>* dmR_pointer,
double* force1,
double* force2)
{
// Not used in current implementation
}

template <typename TK, typename TR>
void EkineticNew<OperatorLCAO<TK, TR>>::cal_stress_IJR(
const int& iat1,
const int& iat2,
const Parallel_Orbitals* paraV,
const std::unordered_map<int, std::vector<double>>& nlm1_all,
const std::unordered_map<int, std::vector<double>>& nlm2_all,
const hamilt::BaseMatrix<TR>* dmR_pointer,
const ModuleBase::Vector3<double>& dis1,
const ModuleBase::Vector3<double>& dis2,
double* stress)
{
// Not used in current implementation
}

} // namespace hamilt
18 changes: 14 additions & 4 deletions source/source_lcao/module_operator_lcao/ekinetic_new.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ hamilt::EkineticNew<hamilt::OperatorLCAO<TK, TR>>::EkineticNew(
const std::vector<double>& orb_cutoff,
const Grid_Driver* GridD_in,
const TwoCenterIntegrator* intor)
: hamilt::OperatorLCAO<TK, TR>(hsk_in, kvec_d_in, hR_in), orb_cutoff_(orb_cutoff), intor_(intor)
: hamilt::OperatorLCAO<TK, TR>(hsk_in, kvec_d_in, hR_in), orb_cutoff_(orb_cutoff), intor_(intor), gridD(GridD_in)
{
this->cal_type = calculation_type::lcao_fixed;
this->ucell = ucell_in;
Expand All @@ -25,7 +25,11 @@ hamilt::EkineticNew<hamilt::OperatorLCAO<TK, TR>>::EkineticNew(
assert(this->hsk != nullptr);
#endif
// initialize HR to allocate sparse Ekinetic matrix memory
this->initialize_HR(GridD_in);
// Only initialize if hR_in is not nullptr (for force calculation, hR_in can be nullptr)
if (hR_in != nullptr)
{
this->initialize_HR(GridD_in);
}
}

// destructor
Expand Down Expand Up @@ -101,7 +105,9 @@ void hamilt::EkineticNew<hamilt::OperatorLCAO<TK, TR>>::calculate_HR()
ModuleBase::TITLE("EkineticNew", "calculate_HR");
if (this->HR_fixed == nullptr || this->HR_fixed->size_atom_pairs() <= 0)
{
ModuleBase::WARNING_QUIT("hamilt::EkineticNew::calculate_HR", "HR_fixed is nullptr or empty");
// Skip calculation if HR_fixed is empty (e.g., zero cutoff case)
// This is not an error, just means there are no atom pairs to calculate
return;
}
ModuleBase::timer::tick("EkineticNew", "calculate_HR");

Expand Down Expand Up @@ -242,7 +248,8 @@ void hamilt::EkineticNew<hamilt::OperatorLCAO<TK, TR>>::contributeHR()
this->HR_fixed_done = true;
}
// last node of sub-chain, add HR_fixed into HR
if (this->next_sub_op == nullptr)
// skip if HR_fixed is nullptr or empty
if (this->next_sub_op == nullptr && this->HR_fixed != nullptr && this->HR_fixed->size_atom_pairs() > 0)
{
this->hR->add(*(this->HR_fixed));
}
Expand All @@ -251,6 +258,9 @@ void hamilt::EkineticNew<hamilt::OperatorLCAO<TK, TR>>::contributeHR()
return;
}

// Include force/stress implementation
#include "ekinetic_force_stress.hpp"

template class hamilt::EkineticNew<hamilt::OperatorLCAO<double, double>>;
template class hamilt::EkineticNew<hamilt::OperatorLCAO<std::complex<double>, double>>;
template class hamilt::EkineticNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>>;
Loading
Loading