Skip to content

Commit 25ebd05

Browse files
author
abacus_fixer
committed
Refactor: extract accumulate_Mi_from_becp as free function in mi_tools
Why: - SpinConstrain<TK>::accumulate_Mi_from_becp does not depend on TK or any singleton state beyond Mi_ and get_spin_sign(ik); keeping it as a member unnecessarily couples it to the class template and singleton lifecycle. - Factoring it out as a free function lets both LCAO and PW DeltaSpin paths call it without going through the SpinConstrain<TK> instantiation, and makes it independently unit-testable. What changed: - New files source/source_lcao/module_deltaspin/mi_tools.{h,cpp}: - pauli_to_moment moved here as inline free function (was inline in header) - accumulate_Mi_from_becp reimplemented as free function; Mi_ becomes an in/out reference parameter 'mi', ik replaced by caller-precomputed spin_sign (npol==2 callers pass 1), TK template parameter dropped. - Entry validates becp/wg_ik/nh_iat non-null, nkb/nbands positive, npol in {1,2}, spin_sign in {-1,1} via ModuleBase::WARNING_QUIT. - ModuleBase::ZERO replaced with local const std::complex<double> zero(0.0, 0.0), removing the source_base/constants.h include. - spin_constrain.h: removed pauli_to_moment inline definition and the accumulate_Mi_from_becp member declaration. - spin_constrain.cpp: removed the member function definition; get_spin_sign kept as member (it still reads npol_ and isk). - cal_mw_from_lambda.cpp, deltaspin_pw_impl.cpp (3 call sites total): updated to precompute spin_sign and call the free function with mi. - CMakeLists.txt: added mi_tools.cpp to the deltaspin OBJECT library. Verification: - cmake --build build_std_para -j4 : deltaspin OBJECT lib + abacus_std_para executable built successfully. - Functional/ctest runs not executed (compile-only verification).
1 parent 512a3a7 commit 25ebd05

7 files changed

Lines changed: 185 additions & 139 deletions

File tree

source/source_lcao/module_deltaspin/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ list(APPEND objects
1010
deltaspin_lcao.cpp
1111
sc_parse_json.cpp
1212
cal_mw_helper.cpp
13+
mi_tools.cpp
1314
)
1415

1516
add_library(

source/source_lcao/module_deltaspin/cal_mw_from_lambda.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "source_hsolver/diago_iter_assist.h"
55
#include "source_io/module_parameter/parameter.h"
66
#include "spin_constrain.h"
7+
#include "mi_tools.h"
78
#include "source_pw/module_pwdft/onsite_proj.h"
89
#include "source_base/parallel_reduce.h"
910
#include "source_hsolver/hsolver_lcao.h"
@@ -279,8 +280,9 @@ void spinconstrain::SpinConstrain<std::complex<double>>::cal_mw_from_lambda(
279280
for (int ik = 0; ik < nk; ik++)
280281
{
281282
const std::complex<double>* becp = &becp_tmp[ik * size_becp];
282-
this->accumulate_Mi_from_becp(becp, nkb, nbands, this->npol_, ik,
283-
&this->pelec->wg(ik, 0), nh_iat);
283+
const int spin_sign = (this->npol_ == 2) ? 1 : this->get_spin_sign(ik);
284+
accumulate_Mi_from_becp(becp, nkb, nbands, this->npol_, spin_sign,
285+
&this->pelec->wg(ik, 0), nh_iat, this->Mi_);
284286
}
285287
// MPI reduction: sum Mi across all k-pool ranks
286288
Parallel_Reduce::reduce_double_allpool(PARAM.inp.kpar,
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#include "mi_tools.h"
2+
3+
#include "source_base/tool_quit.h"
4+
5+
namespace spinconstrain
6+
{
7+
8+
void accumulate_Mi_from_becp(const std::complex<double>* becp,
9+
int nkb,
10+
int nbands,
11+
int npol,
12+
int spin_sign,
13+
const double* wg_ik,
14+
const int* nh_iat,
15+
std::vector<ModuleBase::Vector3<double>>& mi)
16+
{
17+
if (becp == nullptr)
18+
{
19+
ModuleBase::WARNING_QUIT("accumulate_Mi_from_becp", "becp is nullptr");
20+
}
21+
if (wg_ik == nullptr)
22+
{
23+
ModuleBase::WARNING_QUIT("accumulate_Mi_from_becp", "wg_ik is nullptr");
24+
}
25+
if (nh_iat == nullptr)
26+
{
27+
ModuleBase::WARNING_QUIT("accumulate_Mi_from_becp", "nh_iat is nullptr");
28+
}
29+
if (nkb <= 0)
30+
{
31+
ModuleBase::WARNING_QUIT("accumulate_Mi_from_becp", "nkb must be positive");
32+
}
33+
if (nbands <= 0)
34+
{
35+
ModuleBase::WARNING_QUIT("accumulate_Mi_from_becp", "nbands must be positive");
36+
}
37+
if (npol != 1 && npol != 2)
38+
{
39+
ModuleBase::WARNING_QUIT("accumulate_Mi_from_becp", "npol must be 1 or 2");
40+
}
41+
if (spin_sign != -1 && spin_sign != 1)
42+
{
43+
ModuleBase::WARNING_QUIT("accumulate_Mi_from_becp", "spin_sign must be -1 or 1");
44+
}
45+
46+
const std::complex<double> zero(0.0, 0.0);
47+
if (npol == 2)
48+
{
49+
for (int ib = 0; ib < nbands; ib++)
50+
{
51+
const double weight = wg_ik[ib];
52+
int begin_ih = 0;
53+
for (int iat = 0; iat < static_cast<int>(mi.size()); iat++)
54+
{
55+
std::complex<double> occ[4] = {zero, zero, zero, zero};
56+
const int nh = nh_iat[iat];
57+
for (int ih = 0; ih < nh; ih++)
58+
{
59+
const int index = ib * 2 * nkb + begin_ih + ih;
60+
occ[0] += conj(becp[index]) * becp[index];
61+
occ[1] += conj(becp[index]) * becp[index + nkb];
62+
occ[2] += conj(becp[index + nkb]) * becp[index];
63+
occ[3] += conj(becp[index + nkb]) * becp[index + nkb];
64+
}
65+
mi[iat] += pauli_to_moment(occ, weight);
66+
begin_ih += nh;
67+
}
68+
}
69+
}
70+
else // npol == 1
71+
{
72+
for (int ib = 0; ib < nbands; ib++)
73+
{
74+
const double weight = wg_ik[ib];
75+
int begin_ih = 0;
76+
for (int iat = 0; iat < static_cast<int>(mi.size()); iat++)
77+
{
78+
double occ = 0.0;
79+
const int nh = nh_iat[iat];
80+
for (int ih = 0; ih < nh; ih++)
81+
{
82+
const int index = ib * nkb + begin_ih + ih;
83+
occ += (conj(becp[index]) * becp[index]).real();
84+
}
85+
mi[iat].z += weight * occ * spin_sign;
86+
begin_ih += nh;
87+
}
88+
}
89+
}
90+
}
91+
92+
} // namespace spinconstrain
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
#ifndef MI_TOOLS_H
2+
#define MI_TOOLS_H
3+
4+
#include <complex>
5+
#include <vector>
6+
7+
#include "source_base/vector3.h"
8+
9+
/**
10+
* @file mi_tools.h
11+
* @brief Free-function utilities for computing atomic magnetic moments (Mi)
12+
* from intermediate quantities (e.g. becp projector coefficients)
13+
* in the DeltaSpin module.
14+
*
15+
* @par Rationale
16+
* These functions do not depend on the SpinConstrain<TK> template state and
17+
* are factored out as free functions to:
18+
* - Reduce coupling to the singleton class members
19+
* - Allow reuse by both LCAO and PW basis paths
20+
* - Be independently unit-testable
21+
*/
22+
23+
namespace spinconstrain
24+
{
25+
26+
/**
27+
* @brief Convert spinor occupation matrix to magnetic moment vector using Pauli matrices.
28+
*
29+
* @details For a two-component spinor wavefunction, the spin density matrix is:
30+
* rho = |a|^2 a*b | = | (1+Mz)/2 (Mx-iMy)/2 |
31+
* |b*a |b|^2 | | (Mx+iMy)/2 (1-Mz)/2 |
32+
* The magnetic moment components are extracted via Pauli matrix traces:
33+
* Mx = Tr(rho * sigma_x) = occ[1] + occ[2] (real part)
34+
* My = Tr(rho * sigma_y) = -Im(occ[1] - occ[2]) (from sigma_y = [[0,-i],[i,0]])
35+
* Mz = Tr(rho * sigma_z) = occ[0] - occ[3] (real part)
36+
* where occ = {|a|^2, a*b, b*a, |b|^2} from becp coefficients.
37+
*
38+
* @param occ 4-element array of occupation matrix elements (complex)
39+
* @param weight k-point weight for integration
40+
* @return 3D magnetic moment vector (Mx, My, Mz) in Bohr magnetons
41+
*/
42+
inline ModuleBase::Vector3<double> pauli_to_moment(const std::complex<double> occ[4], double weight)
43+
{
44+
return ModuleBase::Vector3<double>(
45+
weight * (occ[1] + occ[2]).real(),
46+
weight * (occ[1] - occ[2]).imag(),
47+
weight * (occ[0] - occ[3]).real()
48+
);
49+
}
50+
51+
/**
52+
* @brief Accumulate atomic magnetic moments from becp coefficients for one k-point.
53+
*
54+
* @details For npol=2 (nspin=4), computes full Pauli decomposition:
55+
* occ[0] = sum(becp_up^* * becp_up), occ[1] = sum(becp_up^* * becp_dn),
56+
* occ[2] = sum(becp_dn^* * becp_up), occ[3] = sum(becp_dn^* * becp_dn)
57+
* Mi = pauli_to_moment(occ, weight)
58+
* For npol=1 (nspin=2), only z-component:
59+
* occ = sum(|becp|^2), Mi.z += weight * occ * spin_sign
60+
*
61+
* @param becp Projector coefficients <alpha_{l,m}|psi_{k,i}>
62+
* @param nkb Total number of projectors
63+
* @param nbands Number of bands
64+
* @param npol Number of spinor components (1 for collinear, 2 for non-collinear)
65+
* @param spin_sign +1 for spin-up, -1 for spin-down (nspin=2 only); unused for npol=2
66+
* @param wg_ik Band occupation weights for this k-point (from Fermi-Dirac)
67+
* @param nh_iat Array of projector counts per atom: nh_iat[iat] = nproj for atom iat
68+
* @param mi [in,out] Magnetic moments vector to accumulate into (size = nat)
69+
*/
70+
void accumulate_Mi_from_becp(const std::complex<double>* becp,
71+
int nkb,
72+
int nbands,
73+
int npol,
74+
int spin_sign,
75+
const double* wg_ik,
76+
const int* nh_iat,
77+
std::vector<ModuleBase::Vector3<double>>& mi);
78+
79+
} // namespace spinconstrain
80+
81+
#endif // MI_TOOLS_H

source/source_lcao/module_deltaspin/spin_constrain.cpp

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -130,89 +130,6 @@ int SpinConstrain<TK>::get_spin_sign(int ik) const
130130
return (this->pelec->klist->isk[ik] == 0) ? 1 : -1;
131131
}
132132

133-
/**
134-
* @brief Accumulate magnetic moments from projector coefficients (becp) for one k-point.
135-
*
136-
* @par Algorithm (npol=2, non-collinear):
137-
* For each atom, compute the 2x2 occupation matrix from becp coefficients:
138-
* occ[0] = sum_ih becp_up^*(ih) * becp_up(ih) = <psi_up|P_at|psi_up>
139-
* occ[1] = sum_ih becp_up^*(ih) * becp_dn(ih) = <psi_up|P_at|psi_dn>
140-
* occ[2] = sum_ih becp_dn^*(ih) * becp_up(ih) = <psi_dn|P_at|psi_up>
141-
* occ[3] = sum_ih becp_dn^*(ih) * becp_dn(ih) = <psi_dn|P_at|psi_dn>
142-
* where P_at = sum_{l,m} |alpha_{l,m}><alpha_{l,m}| is the atomic projector.
143-
*
144-
* The magnetic moment is extracted via Pauli matrix traces:
145-
* Mx = Re(occ[1] + occ[2]), My = -Im(occ[1] - occ[2]), Mz = Re(occ[0] - occ[3])
146-
*
147-
* @par Algorithm (npol=1, collinear):
148-
* Only the z-component (spin projection) is computed:
149-
* occ = sum_ih |becp(ih)|^2 = <psi|P_at|psi>
150-
* Mz += weight * occ * spin_sign
151-
* where spin_sign = +1 for spin-up, -1 for spin-down.
152-
*
153-
* @param becp Projector coefficients, layout: [ib * npol * nkb + spin * nkb + ih]
154-
* @param nkb Total number of projectors across all atoms
155-
* @param nbands Number of bands (occupied + unoccupied in the subspace)
156-
* @param npol Number of spinor components (1 for collinear, 2 for non-collinear)
157-
* @param ik K-point index (used for spin_sign lookup in collinear mode)
158-
* @param wg_ik Band occupation weights for this k-point (from Fermi-Dirac)
159-
* @param nh_iat Array of projector counts per atom: nh_iat[iat] = nproj for atom iat
160-
*/
161-
template <typename TK>
162-
void SpinConstrain<TK>::accumulate_Mi_from_becp(const std::complex<double>* becp,
163-
int nkb,
164-
int nbands,
165-
int npol,
166-
int ik,
167-
const double* wg_ik,
168-
const int* nh_iat)
169-
{
170-
if (npol == 2)
171-
{
172-
for (int ib = 0; ib < nbands; ib++)
173-
{
174-
const double weight = wg_ik[ib];
175-
int begin_ih = 0;
176-
for (int iat = 0; iat < static_cast<int>(this->Mi_.size()); iat++)
177-
{
178-
std::complex<double> occ[4] = {ModuleBase::ZERO, ModuleBase::ZERO, ModuleBase::ZERO, ModuleBase::ZERO};
179-
const int nh = nh_iat[iat];
180-
for (int ih = 0; ih < nh; ih++)
181-
{
182-
const int index = ib * 2 * nkb + begin_ih + ih;
183-
occ[0] += conj(becp[index]) * becp[index];
184-
occ[1] += conj(becp[index]) * becp[index + nkb];
185-
occ[2] += conj(becp[index + nkb]) * becp[index];
186-
occ[3] += conj(becp[index + nkb]) * becp[index + nkb];
187-
}
188-
this->Mi_[iat] += pauli_to_moment(occ, weight);
189-
begin_ih += nh;
190-
}
191-
}
192-
}
193-
else // npol == 1
194-
{
195-
const int sign = this->get_spin_sign(ik);
196-
for (int ib = 0; ib < nbands; ib++)
197-
{
198-
const double weight = wg_ik[ib];
199-
int begin_ih = 0;
200-
for (int iat = 0; iat < static_cast<int>(this->Mi_.size()); iat++)
201-
{
202-
double occ = 0.0;
203-
const int nh = nh_iat[iat];
204-
for (int ih = 0; ih < nh; ih++)
205-
{
206-
const int index = ib * nkb + begin_ih + ih;
207-
occ += (conj(becp[index]) * becp[index]).real();
208-
}
209-
this->Mi_[iat].z += weight * occ * sign;
210-
begin_ih += nh;
211-
}
212-
}
213-
}
214-
}
215-
216133
template <typename TK>
217134
int SpinConstrain<TK>::get_nw() const
218135
{

source/source_lcao/module_deltaspin/spin_constrain.h

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -60,31 +60,6 @@
6060
namespace spinconstrain
6161
{
6262

63-
/**
64-
* @brief Convert spinor occupation matrix to magnetic moment vector using Pauli matrices.
65-
*
66-
* @details For a two-component spinor wavefunction, the spin density matrix is:
67-
* rho = |a|^2 a*b | = | (1+Mz)/2 (Mx-iMy)/2 |
68-
* |b*a |b|^2 | | (Mx+iMy)/2 (1-Mz)/2 |
69-
* The magnetic moment components are extracted via Pauli matrix traces:
70-
* Mx = Tr(rho * sigma_x) = occ[1] + occ[2] (real part)
71-
* My = Tr(rho * sigma_y) = -Im(occ[1] - occ[2]) (from sigma_y = [[0,-i],[i,0]])
72-
* Mz = Tr(rho * sigma_z) = occ[0] - occ[3] (real part)
73-
* where occ = {|a|^2, a*b, b*a, |b|^2} from becp coefficients.
74-
*
75-
* @param occ 4-element array of occupation matrix elements (complex)
76-
* @param weight k-point weight for integration
77-
* @return 3D magnetic moment vector (Mx, My, Mz) in Bohr magnetons
78-
*/
79-
inline ModuleBase::Vector3<double> pauli_to_moment(const std::complex<double> occ[4], double weight)
80-
{
81-
return ModuleBase::Vector3<double>(
82-
weight * (occ[1] + occ[2]).real(),
83-
weight * (occ[1] - occ[2]).imag(),
84-
weight * (occ[0] - occ[3]).real()
85-
);
86-
}
87-
8863
struct ScAtomData;
8964

9065
/**
@@ -549,31 +524,6 @@ class SpinConstrain
549524
int get_iwt(int itype, int iat, int orbital_index) const; ///< Convert (itype, iat, iw) to global orbital index
550525
/// @brief Get spin sign for k-point ik: +1 for spin-up, -1 for spin-down (nspin=2 only)
551526
int get_spin_sign(int ik) const;
552-
/**
553-
* @brief Accumulate magnetic moments from becp coefficients for a single k-point.
554-
*
555-
* @details For npol=2 (nspin=4), computes full Pauli decomposition:
556-
* occ[0] = sum(becp_up^* * becp_up), occ[1] = sum(becp_up^* * becp_dn),
557-
* occ[2] = sum(becp_dn^* * becp_up), occ[3] = sum(becp_dn^* * becp_dn)
558-
* Mi = pauli_to_moment(occ, weight)
559-
* For npol=1 (nspin=2), only z-component:
560-
* occ = sum(|becp|^2), Mi.z += weight * occ * spin_sign
561-
*
562-
* @param becp Projector coefficients <alpha_{l,m}|psi_{k,i}>
563-
* @param nkb Total number of projectors
564-
* @param nbands Number of bands
565-
* @param npol Number of spinor components
566-
* @param ik K-point index (for spin_sign lookup in nspin=2)
567-
* @param wg_ik Band weights for this k-point
568-
* @param nh_iat Number of projectors per atom
569-
*/
570-
void accumulate_Mi_from_becp(const std::complex<double>* becp,
571-
int nkb,
572-
int nbands,
573-
int npol,
574-
int ik,
575-
const double* wg_ik,
576-
const int* nh_iat);
577527
private:
578528
/// DeltaSpin operator pointer for LCAO magnetic moment calculation
579529
hamilt::Operator<TK>* p_operator = nullptr;

source/source_pw/module_pwdft/deltaspin_pw_impl.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "source_base/kernels/math_kernel_op.h"
66
#include "source_pw/module_pwdft/onsite_proj.h"
77
#include "source_lcao/module_deltaspin/spin_constrain.h"
8+
#include "source_lcao/module_deltaspin/mi_tools.h"
89
#include "source_io/module_parameter/parameter.h"
910
#include "source_hsolver/diago_iter_assist.h"
1011
#include "source_hsolver/hsolver_pw.h"
@@ -52,8 +53,9 @@ void SpinConstrain<std::complex<double>>::cal_mi_pw()
5253
onsite_p->overlap_proj_psi(nbands * npol, psi_pointer); // Compute becp = <alpha|psi>
5354
const std::complex<double>* becp = onsite_p->get_h_becp();
5455
int nkb = onsite_p->get_tot_nproj();
55-
this->accumulate_Mi_from_becp(becp, nkb, nbands, npol, ik,
56-
&this->pelec->wg(ik, 0), &onsite_p->get_nh(0));
56+
const int spin_sign = (npol == 2) ? 1 : this->get_spin_sign(ik);
57+
accumulate_Mi_from_becp(becp, nkb, nbands, npol, spin_sign,
58+
&this->pelec->wg(ik, 0), &onsite_p->get_nh(0), this->Mi_);
5759
}
5860
}
5961
#if ((defined __CUDA) || (defined __ROCM))
@@ -73,8 +75,9 @@ void SpinConstrain<std::complex<double>>::cal_mi_pw()
7375
onsite_p->overlap_proj_psi(nbands * npol, psi_pointer);
7476
const std::complex<double>* becp = onsite_p->get_h_becp();
7577
int nkb = onsite_p->get_size_becp() / nbands / npol;
76-
this->accumulate_Mi_from_becp(becp, nkb, nbands, npol, ik,
77-
&this->pelec->wg(ik, 0), &onsite_p->get_nh(0));
78+
const int spin_sign = (npol == 2) ? 1 : this->get_spin_sign(ik);
79+
accumulate_Mi_from_becp(becp, nkb, nbands, npol, spin_sign,
80+
&this->pelec->wg(ik, 0), &onsite_p->get_nh(0), this->Mi_);
7881
}
7982
}
8083
#endif

0 commit comments

Comments
 (0)