Skip to content

Commit c88ddb6

Browse files
committed
feat(exx-symmetry): SSG spin-flip coset support for H(R) rotation (nspin=2)
1 parent 7625b2f commit c88ddb6

5 files changed

Lines changed: 82 additions & 9 deletions

File tree

source/source_io/module_parameter/read_inp_sys.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ void ReadInput::item_system()
278278
"* 0: disabled (default)\n"
279279
"* 1: enabled";
280280
item.default_value = "0";
281-
item.availability = "symmetry==1";
281+
item.set_availability("symmetry==1");
282282
read_sync_int(input.symmetry_ssg);
283283
this->add_item(item);
284284
}

source/source_lcao/module_ri/exx_lri.hpp

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -803,14 +803,20 @@ void Exx_LRI<Tdata>::cal_exx_elec(const std::vector<std::map<TA, std::map<TAC, R
803803
return;
804804
}
805805

806+
// (nspin=2 SSG) the up-sublattice-to-down-sublattice spin-flip coset makes the down channel's
807+
// H(R) a single flip-op rotation of the up channel's, so we compute cal_Hs for is=0 only and
808+
// derive is=1 from the stored up-channel full H(R); see restore_HR_flip_nspin2.
809+
const bool ssg_flip = (p_symrot && PARAM.inp.nspin == 2 && ucell.symm.spin_flip_nspin2);
810+
std::map<TA, std::map<TAC, RI::Tensor<Tdata>>> Hs_full_up;
811+
806812
this->Hexxs.resize(PARAM.inp.nspin);
807813
this->Eexx = 0;
808814
for(int is=0; is<PARAM.inp.nspin; ++is)
809815
{
810816
const std::string suffix = ((PARAM.inp.cal_force || PARAM.inp.cal_stress) ? std::to_string(is) : "");
811817

812818
this->exx_lri.set_Ds(Ds[is], this->info.dm_threshold, suffix);
813-
this->exx_lri.cal_Hs({ "","",suffix });
819+
if (!(ssg_flip && is == 1)) { this->exx_lri.cal_Hs({ "","",suffix }); }
814820

815821
if (!p_symrot)
816822
{
@@ -819,10 +825,20 @@ void Exx_LRI<Tdata>::cal_exx_elec(const std::vector<std::map<TA, std::map<TAC, R
819825
}
820826
else
821827
{
822-
// reduce but not repeat
823-
auto Hs_a2D = this->exx_lri.post_2D.set_tensors_map2(this->exx_lri.Hs);
824-
// rotate locally without repeat
825-
Hs_a2D = p_symrot->restore_HR(ucell.symm, ucell.atoms, ucell.st, 'H', Hs_a2D);
828+
std::map<TA, std::map<TAC, RI::Tensor<Tdata>>> Hs_a2D;
829+
if (ssg_flip && is == 1)
830+
{
831+
// down channel: derive the full H(R) from the stored up channel by one spin-flip op
832+
Hs_a2D = p_symrot->restore_HR_flip_nspin2(ucell.symm, ucell.atoms, ucell.st, 'H', Hs_full_up);
833+
}
834+
else
835+
{
836+
// reduce but not repeat
837+
Hs_a2D = this->exx_lri.post_2D.set_tensors_map2(this->exx_lri.Hs);
838+
// rotate locally without repeat
839+
Hs_a2D = p_symrot->restore_HR(ucell.symm, ucell.atoms, ucell.st, 'H', Hs_a2D);
840+
if (ssg_flip && is == 0) { Hs_full_up = Hs_a2D; } // keep up channel for the flip derivation
841+
}
826842
// cal energy using full Hs without repeat
827843
this->exx_lri.energy = this->exx_lri.post_2D.cal_energy(
828844
this->exx_lri.post_2D.saves["Ds_" + suffix],

source/source_lcao/module_ri/module_exx_symmetry/irreducible_sector.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ namespace ModuleSymmetry
99
// leaves R and the atom pair untouched, so the sector bookkeeping is identical for both kinds.
1010
static inline const ModuleBase::Matrix3& sector_gmatrix(const Symmetry& symm, const int isym)
1111
{
12-
return (isym < symm.nrotk) ? symm.gmatrix[isym] : symm.gmatrix_anti[isym - symm.nrotk];
12+
if (isym < symm.nrotk) { return symm.gmatrix[isym]; }
13+
// (nspin=2 SSG) the spin-flip coset reuses the [nrotk, nrotk+nrotk_flip) index range
14+
// (nrotk_anti==0 in that case); its spatial part is gmatrix_flip[isym-nrotk].
15+
return symm.spin_flip_nspin2 ? symm.gmatrix_flip[isym - symm.nrotk]
16+
: symm.gmatrix_anti[isym - symm.nrotk];
1317
}
1418
static inline int sector_rotated_atom(const Symmetry& symm, const int isym, const int iat)
1519
{
16-
return (isym < symm.nrotk) ? symm.get_rotated_atom(isym, iat)
17-
: symm.get_rotated_atom_anti(isym - symm.nrotk, iat);
20+
if (isym < symm.nrotk) { return symm.get_rotated_atom(isym, iat); }
21+
return symm.spin_flip_nspin2 ? symm.get_rotated_atom_flip(isym - symm.nrotk, iat)
22+
: symm.get_rotated_atom_anti(isym - symm.nrotk, iat);
1823
}
1924

2025
TC Irreducible_Sector::rotate_R(const Symmetry& symm,

source/source_lcao/module_ri/module_exx_symmetry/symm_rotation.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ namespace ModuleSymmetry
127127
std::array<std::map<int, std::map<std::pair<int, TC>, RI::Tensor<Tdata>>>, 4> restore_HR_nspin4(
128128
const Symmetry& symm, const Atom* atoms, const Statistics& st, const char mode,
129129
const std::array<std::map<int, std::map<std::pair<int, TC>, RI::Tensor<Tdata>>>, 4>& HR_irreducible_soc)const;
130+
/// (nspin=2 SSG) derive the FULL H(R) of the OPPOSITE spin channel from a full H(R), using a
131+
/// single spin-flip coset operation f=[C2_perp||g]. With SOC off the spin channels are
132+
/// independent real matrices and f's spatial/orbital rotation is identical to a unitary op,
133+
/// so H_down(f.apR)=rotate_f(H_up(apR)); f is a bijection on the full atom-pair set, so one
134+
/// forward pass over HR_full_this yields the whole opposite channel. This lets cal_exx_elec
135+
/// skip the second spin's cal_Hs entirely. Uses flip op j=0 (raw isym = nrotk).
136+
template<typename Tdata> // RI::Tensor type
137+
std::map<int, std::map<std::pair<int, TC>, RI::Tensor<Tdata>>> restore_HR_flip_nspin2(
138+
const Symmetry& symm, const Atom* atoms, const Statistics& st, const char mode,
139+
const std::map<int, std::map<std::pair<int, TC>, RI::Tensor<Tdata>>>& HR_full_this)const;
130140

131141
//--------------------------------------------------------------------------------
132142
/// test functions

source/source_lcao/module_ri/module_exx_symmetry/symm_rotation_r.hpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,48 @@ namespace ModuleSymmetry
248248
return HR_full;
249249
}
250250

251+
// (nspin=2 SSG) derive the opposite spin channel's FULL H(R) from a full H(R) by one spin-flip
252+
// coset op f=[C2_perp||g] (raw isym = nrotk, i.e. gmatrix_flip[0]). With SOC off the two spin
253+
// channels are independent real matrices, and f's spatial/orbital rotation is identical to a
254+
// unitary op (only the target spin channel differs), so the covariance is exactly the one used
255+
// by restore_HR: H_other[Z] = rotate_f( H_this[ f.Z ] ), with f.Z = rotate_apR_by_formula(f, Z).
256+
// f is a bijection on the full atom-pair set, so a single forward pass fills the whole channel.
257+
template<typename Tdata>
258+
std::map<int, std::map<std::pair<int, TC>, RI::Tensor<Tdata>>> Symmetry_rotation::restore_HR_flip_nspin2(
259+
const Symmetry& symm, const Atom* atoms, const Statistics& st, const char mode,
260+
const std::map<int, std::map<std::pair<int, TC>, RI::Tensor<Tdata>>>& HR_full_this) const
261+
{
262+
ModuleBase::TITLE("Symmetry_rotation", "restore_HR_flip_nspin2");
263+
ModuleBase::timer::start("Symmetry_rotation", "restore_HR_flip_nspin2");
264+
assert(symm.spin_flip_nspin2);
265+
assert(symm.nrotk_flip > 0);
266+
const int isym_flip = symm.nrotk; // flip op j=0, raw sector index nrotk (nrotk_anti==0 here)
267+
std::map<int, std::map<std::pair<int, TC>, RI::Tensor<Tdata>>> HR_other;
268+
for (auto& tmp1 : HR_full_this)
269+
{
270+
const int& z1 = tmp1.first;
271+
for (auto& tmp2 : tmp1.second)
272+
{
273+
const int& z2 = tmp2.first.first;
274+
const TC& Rz = tmp2.first.second;
275+
const TapR& src = this->irs_.rotate_apR_by_formula(symm, isym_flip, { { z1, z2 }, Rz });
276+
const int& s1 = src.first.first;
277+
const int& s2 = src.first.second;
278+
const TC& Rs = src.second;
279+
// f is a bijection on the full apR set, so H_this[src] should exist; a missing entry
280+
// can only be a below-threshold drop, treated as zero (skip).
281+
auto it1 = HR_full_this.find(s1);
282+
if (it1 == HR_full_this.end()) { continue; }
283+
auto it2 = it1->second.find({ s2, Rs });
284+
if (it2 == it1->second.end()) { continue; }
285+
HR_other[z1][{z2, Rz}] = rotate_atompair_serial(it2->second, isym_flip,
286+
atoms[st.iat2it[s1]], atoms[st.iat2it[s2]], mode);
287+
}
288+
}
289+
ModuleBase::timer::end("Symmetry_rotation", "restore_HR_flip_nspin2");
290+
return HR_other;
291+
}
292+
251293
template<typename Tdata>
252294
inline void set_block(const int starti, const int startj, const RI::Tensor<std::complex<double>>& block,
253295
RI::Tensor<Tdata>& obj_tensor)

0 commit comments

Comments
 (0)