|
21 | 21 | #include "source_lcao/module_operator_lcao/dftu_lcao.h" |
22 | 22 | #include "source_lcao/module_operator_lcao/dspin_lcao.h" |
23 | 23 | #include "source_lcao/module_operator_lcao/nonlocal_new.h" |
| 24 | +#include "source_lcao/module_operator_lcao/ekinetic_new.h" |
| 25 | +#include "source_lcao/module_operator_lcao/overlap_new.h" |
| 26 | +#include "source_lcao/pulay_fs.h" |
24 | 27 |
|
25 | 28 |
|
26 | 29 | // mohan add 2025-11-04 |
@@ -94,7 +97,8 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell, |
94 | 97 |
|
95 | 98 | const int nat = ucell.nat; |
96 | 99 |
|
97 | | - ForceStressArrays fsr; // mohan add 2024-06-15 |
| 100 | + // NOTE: ForceStressArrays is no longer needed as we use operator-based force calculation |
| 101 | + // ForceStressArrays fsr; // removed - no longer needed |
98 | 102 |
|
99 | 103 | // total force : ModuleBase::matrix fcs; |
100 | 104 |
|
@@ -161,46 +165,149 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell, |
161 | 165 | sigmaxc, pelec->f_en.etxc, pelec->charge, rhopw, locpp, sf); |
162 | 166 | } |
163 | 167 |
|
164 | | - //! atomic forces from integration (4 terms) |
165 | | - this->integral_part(PARAM.globalv.gamma_only_local, isforce, isstress, |
166 | | - ucell, gd, fsr, pelec, dmat.dm, psi, foverlap, ftvnl_dphi, // add dmat.dm, mohan 20251104 |
167 | | - fvnl_dbeta, fvl_dphi, soverlap, stvnl_dphi, svnl_dbeta, |
168 | | - svl_dphi, fvnl_dalpha, svnl_dalpha, deepks, |
169 | | - two_center_bundle, orb, pv, kv); |
| 168 | + // Calculate forces and stresses using new operator-based methods |
| 169 | + // Step 1: Calculate Energy Density Matrix (EDM) for overlap force |
| 170 | + // EDM = Σ_k w_k * ε_k * |ψ_k><ψ_k| |
| 171 | + elecstate::DensityMatrix<T, double> edm = flk.cal_edm(pelec, *psi, *dmat.dm, kv, pv, |
| 172 | + PARAM.inp.nspin, PARAM.inp.nbands, ucell, *this->RA); |
170 | 173 |
|
171 | | - // calculate force and stress for Nonlocal part |
| 174 | + // Step 2: Handle different spin cases |
172 | 175 | if (PARAM.inp.nspin == 1 || PARAM.inp.nspin == 2) |
173 | 176 | { |
174 | | - hamilt::NonlocalNew<hamilt::OperatorLCAO<T, double>> tmp_nonlocal(nullptr, |
175 | | - kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd, two_center_bundle.overlap_orb_beta.get()); |
176 | | - |
| 177 | + // For nspin=1 or nspin=2, use double precision |
| 178 | + // Switch to spin channel 1 for DMR access |
177 | 179 | if (PARAM.inp.nspin == 2) |
178 | 180 | { |
179 | 181 | dmat.dm->switch_dmr(1); |
180 | 182 | } |
181 | 183 |
|
182 | | - const hamilt::HContainer<double>* dmr = dmat.dm->get_DMR_pointer(1); |
183 | | - tmp_nonlocal.cal_force_stress(isforce, isstress, dmr, fvnl_dbeta, svnl_dbeta); |
| 184 | + const hamilt::HContainer<double>* dmR = dmat.dm->get_DMR_pointer(1); |
| 185 | + const hamilt::HContainer<double>* edmR = edm.get_DMR_pointer(1); |
| 186 | + |
| 187 | + // Calculate kinetic force/stress (uses DM) |
| 188 | + if (PARAM.inp.t_in_h) |
| 189 | + { |
| 190 | + hamilt::EkineticNew<hamilt::OperatorLCAO<T, double>> tmp_ekinetic( |
| 191 | + nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd, |
| 192 | + two_center_bundle.kinetic_orb.get()); |
| 193 | + tmp_ekinetic.cal_force_stress(isforce, isstress, dmR, ftvnl_dphi, stvnl_dphi); |
| 194 | + } |
| 195 | + |
| 196 | + // Calculate overlap force/stress (uses EDM) |
| 197 | + hamilt::OverlapNew<hamilt::OperatorLCAO<T, double>> tmp_overlap( |
| 198 | + nullptr, kv.kvec_d, nullptr, nullptr, &ucell, orb.cutoffs(), &gd, |
| 199 | + two_center_bundle.overlap_orb.get()); |
| 200 | + tmp_overlap.cal_force_stress(isforce, isstress, edmR, foverlap, soverlap); |
| 201 | + |
| 202 | + // Calculate nonlocal force/stress (uses DM) |
| 203 | + hamilt::NonlocalNew<hamilt::OperatorLCAO<T, double>> tmp_nonlocal( |
| 204 | + nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd, |
| 205 | + two_center_bundle.overlap_orb_beta.get()); |
| 206 | + tmp_nonlocal.cal_force_stress(isforce, isstress, dmR, fvnl_dbeta, svnl_dbeta); |
| 207 | + |
| 208 | + // Calculate local potential force/stress (vl_dphi) |
| 209 | + // This uses grid integration, not operator-based method |
| 210 | + flk.ParaV = dmat.dm->get_paraV_pointer(); |
| 211 | + PulayForceStress::cal_pulay_fs(fvl_dphi, svl_dphi, *dmat.dm, ucell, pelec->pot, |
| 212 | + isforce, isstress, false /*reset dm to gint*/); |
| 213 | + |
| 214 | + // Switch back to spin channel 0 |
184 | 215 | if (PARAM.inp.nspin == 2) |
185 | 216 | { |
186 | 217 | dmat.dm->switch_dmr(0); |
187 | 218 | } |
188 | 219 | } |
189 | 220 | else if (PARAM.inp.nspin == 4) |
190 | 221 | { |
191 | | - hamilt::NonlocalNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>> tmp_nonlocal( |
192 | | - nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd, |
193 | | - two_center_bundle.overlap_orb_beta.get()); |
194 | | - |
195 | | - // calculate temporary complex DMR for nonlocal force&stress |
196 | | - // In fact, only SOC part need the imaginary part of DMR for correct force&stress |
| 222 | + // For nspin=4 (non-collinear), need complex DMR |
| 223 | + // Create temporary complex DMR for DM |
197 | 224 | hamilt::HContainer<std::complex<double>> tmp_dmr(dmat.dm->get_DMR_pointer(1)->get_paraV()); |
198 | 225 | std::vector<int> ijrs = dmat.dm->get_DMR_pointer(1)->get_ijr_info(); |
199 | 226 | tmp_dmr.insert_ijrs(&ijrs); |
200 | 227 | tmp_dmr.allocate(); |
201 | 228 | dmat.dm->cal_DMR_full(&tmp_dmr); |
| 229 | + |
| 230 | + // Create temporary complex DMR for EDM |
| 231 | + hamilt::HContainer<std::complex<double>> tmp_edmr(edm.get_DMR_pointer(1)->get_paraV()); |
| 232 | + tmp_edmr.insert_ijrs(&ijrs); |
| 233 | + tmp_edmr.allocate(); |
| 234 | + edm.cal_DMR_full(&tmp_edmr); |
| 235 | + |
| 236 | + // Calculate kinetic force/stress (uses DM) |
| 237 | + if (PARAM.inp.t_in_h) |
| 238 | + { |
| 239 | + hamilt::EkineticNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>> tmp_ekinetic( |
| 240 | + nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd, |
| 241 | + two_center_bundle.kinetic_orb.get()); |
| 242 | + tmp_ekinetic.cal_force_stress(isforce, isstress, &tmp_dmr, ftvnl_dphi, stvnl_dphi); |
| 243 | + } |
| 244 | + |
| 245 | + // Calculate overlap force/stress (uses EDM) |
| 246 | + hamilt::OverlapNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>> tmp_overlap( |
| 247 | + nullptr, kv.kvec_d, nullptr, nullptr, &ucell, orb.cutoffs(), &gd, |
| 248 | + two_center_bundle.overlap_orb.get()); |
| 249 | + tmp_overlap.cal_force_stress(isforce, isstress, &tmp_edmr, foverlap, soverlap); |
| 250 | + |
| 251 | + // Calculate nonlocal force/stress (uses DM) |
| 252 | + hamilt::NonlocalNew<hamilt::OperatorLCAO<std::complex<double>, std::complex<double>>> tmp_nonlocal( |
| 253 | + nullptr, kv.kvec_d, nullptr, &ucell, orb.cutoffs(), &gd, |
| 254 | + two_center_bundle.overlap_orb_beta.get()); |
202 | 255 | tmp_nonlocal.cal_force_stress(isforce, isstress, &tmp_dmr, fvnl_dbeta, svnl_dbeta); |
| 256 | + |
| 257 | + // Calculate local potential force/stress (vl_dphi) |
| 258 | + flk.ParaV = dmat.dm->get_paraV_pointer(); |
| 259 | + PulayForceStress::cal_pulay_fs(fvl_dphi, svl_dphi, *dmat.dm, ucell, pelec->pot, |
| 260 | + isforce, isstress, false /*reset dm to gint*/); |
| 261 | + } |
| 262 | + |
| 263 | + // MPI reduction for forces |
| 264 | + if (isforce) |
| 265 | + { |
| 266 | + Parallel_Reduce::reduce_pool(foverlap.c, foverlap.nr * foverlap.nc); |
| 267 | + Parallel_Reduce::reduce_pool(ftvnl_dphi.c, ftvnl_dphi.nr * ftvnl_dphi.nc); |
| 268 | + Parallel_Reduce::reduce_pool(fvnl_dbeta.c, fvnl_dbeta.nr * fvnl_dbeta.nc); |
| 269 | + Parallel_Reduce::reduce_pool(fvl_dphi.c, fvl_dphi.nr * fvl_dphi.nc); |
| 270 | + } |
| 271 | + |
| 272 | + // MPI reduction for stresses |
| 273 | + if (isstress) |
| 274 | + { |
| 275 | + Parallel_Reduce::reduce_pool(soverlap.c, soverlap.nr * soverlap.nc); |
| 276 | + Parallel_Reduce::reduce_pool(stvnl_dphi.c, stvnl_dphi.nr * stvnl_dphi.nc); |
| 277 | + Parallel_Reduce::reduce_pool(svnl_dbeta.c, svnl_dbeta.nr * svnl_dbeta.nc); |
| 278 | + Parallel_Reduce::reduce_pool(svl_dphi.c, svl_dphi.nr * svl_dphi.nc); |
| 279 | + } |
| 280 | + |
| 281 | + // Handle DeePKS forces if enabled |
| 282 | +#ifdef __MLALGO |
| 283 | + if (PARAM.inp.deepks_scf) |
| 284 | + { |
| 285 | + const int nks = (PARAM.inp.nspin == 1 || PARAM.inp.nspin == 2) ? 1 : kv.get_nks(); |
| 286 | + if (PARAM.globalv.gamma_only_local) |
| 287 | + { |
| 288 | + DeePKS_domain::cal_f_delta<double>(deepks.ld.dm_r, ucell, orb, gd, |
| 289 | + *flk.ParaV, nks, deepks.ld.deepks_param, |
| 290 | + kv.kvec_d, deepks.ld.phialpha, deepks.ld.gedm, |
| 291 | + fvnl_dalpha, isstress, svnl_dalpha); |
| 292 | + } |
| 293 | + else |
| 294 | + { |
| 295 | + DeePKS_domain::cal_f_delta<std::complex<double>>(deepks.ld.dm_r, ucell, orb, gd, |
| 296 | + *flk.ParaV, nks, deepks.ld.deepks_param, |
| 297 | + kv.kvec_d, deepks.ld.phialpha, deepks.ld.gedm, |
| 298 | + fvnl_dalpha, isstress, svnl_dalpha); |
| 299 | + } |
| 300 | + |
| 301 | + if (isforce) |
| 302 | + { |
| 303 | + Parallel_Reduce::reduce_pool(fvnl_dalpha.c, fvnl_dalpha.nr * fvnl_dalpha.nc); |
| 304 | + } |
| 305 | + if (isstress) |
| 306 | + { |
| 307 | + Parallel_Reduce::reduce_pool(svnl_dalpha.c, svnl_dalpha.nr * svnl_dalpha.nc); |
| 308 | + } |
203 | 309 | } |
| 310 | +#endif |
204 | 311 |
|
205 | 312 | //! forces and stress from vdw |
206 | 313 | // Peize Lin add 2014-04-04, update 2021-03-09 |
@@ -275,13 +382,12 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell, |
275 | 382 | } |
276 | 383 | if (PARAM.inp.dft_plus_u == 2) |
277 | 384 | { |
278 | | - // dftu.force_stress(ucell, gd, pelec, pv, fsr, force_u, stress_u, kv); |
279 | | - // mohan modify 2025-11-03 |
| 385 | + // Old DFT+U implementation (dft_plus_u==2) still needs ForceStressArrays |
| 386 | + ForceStressArrays fsr_dftu; |
280 | 387 | std::vector<std::vector<double>>* dmk_d = nullptr; |
281 | 388 | std::vector<std::vector<std::complex<double>>>* dmk_c = nullptr; |
282 | | - // add a new template function |
283 | 389 | assign_dmk_ptr<T>(dmat.dm, dmk_d, dmk_c, PARAM.globalv.gamma_only_local); |
284 | | - dftu.force_stress(ucell, gd, dmk_d, dmk_c, pv, fsr, force_u, stress_u, kv); |
| 390 | + dftu.force_stress(ucell, gd, dmk_d, dmk_c, pv, fsr_dftu, force_u, stress_u, kv); |
285 | 391 | } |
286 | 392 | else |
287 | 393 | { |
@@ -332,10 +438,11 @@ void Force_Stress_LCAO<T>::getForceStress(UnitCell& ucell, |
332 | 438 | } |
333 | 439 | } |
334 | 440 |
|
335 | | - if (!PARAM.globalv.gamma_only_local) |
336 | | - { |
337 | | - this->flk.finish_ftable(fsr); |
338 | | - } |
| 441 | + // NOTE: finish_ftable is no longer needed as we don't use ForceStressArrays for overlap/kinetic |
| 442 | + // if (!PARAM.globalv.gamma_only_local) |
| 443 | + // { |
| 444 | + // this->flk.finish_ftable(fsr); |
| 445 | + // } |
339 | 446 |
|
340 | 447 | #ifdef __EXX |
341 | 448 | // Force and Stress contribution from exx |
|
0 commit comments