Skip to content

Commit 9ae17b7

Browse files
committed
feat(deltaspin): add constraint SCF annealing for direction_only mode
Add direction_only guidance mechanism that: - Skips inner BFGS lambda loop when sc_direction_only=1 - Phase 1 (first 10 SCF iters): apply strong lambda (500 eV/uB) + tiny mixing_beta (0.02) to guide spin direction toward target - Phase 2: decay lambda once aligned, linearly restore mixing_beta - Print per-atom Mi/Target comparison and lambda evolution Add set_mixing_beta() and set_lambda()/get_Mi() accessors to support the guidance mechanism.
1 parent e2b66ef commit 9ae17b7

3 files changed

Lines changed: 91 additions & 0 deletions

File tree

source/source_esolver/esolver_ks_lcao.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,11 @@ void ESolver_KS_LCAO<TK, TR>::hamilt2rho_single(UnitCell& ucell, int istep, int
407407
sc.run_lambda_linear_scan(iter - 1);
408408
skip_solve = true;
409409
}
410+
else if (PARAM.inp.sc_direction_only)
411+
{
412+
// direction_only mode: skip the inner BFGS lambda loop.
413+
// Lambda projection is handled by the DeltaSpin operator.
414+
}
410415
else if (!sc.mag_converged() && this->drho > 0 && this->drho < PARAM.inp.sc_scf_thr)
411416
{
412417
sc.run_lambda_loop(iter - 1);
@@ -480,6 +485,87 @@ void ESolver_KS_LCAO<TK, TR>::iter_finish(UnitCell& ucell, const int istep, int&
480485
// 3) for delta spin
481486
cal_mi_lcao_wrapper<TK>(iter, PARAM.inp);
482487

488+
// 3b) direction_only: constraint SCF annealing
489+
// Phase 1 (iter 1-5): strong lambda + tiny mixing_beta to lock spin direction
490+
// Phase 2 (iter 6+): decay lambda once aligned, restore mixing_beta
491+
if (PARAM.inp.sc_direction_only && PARAM.inp.sc_mag_switch)
492+
{
493+
spinconstrain::SpinConstrain<TK>& sc = spinconstrain::SpinConstrain<TK>::getScInstance();
494+
const int nat = sc.get_nat();
495+
const auto& Mi = sc.get_Mi();
496+
const auto& target = sc.get_target_mag();
497+
const auto& constrain = sc.get_constrain();
498+
auto lambda = sc.get_sc_lambda();
499+
500+
const double LAMBDA_STRONG_EV = 500.0; // strong constraint in eV/uB
501+
const double BETA_MIN = 0.02; // tiny mixing during guidance
502+
const int GUIDANCE_ITERS = 10; // guidance phase length
503+
const double DECAY = 0.5; // per-step decay after guidance
504+
505+
// Check if all atoms have correct spin direction
506+
bool all_aligned = true;
507+
for (int ia = 0; ia < nat; ++ia)
508+
for (int ic = 0; ic < 3; ++ic)
509+
if (constrain[ia][ic] != 0 && Mi[ia][ic] * target[ia][ic] < 0)
510+
all_aligned = false;
511+
512+
if (iter <= GUIDANCE_ITERS)
513+
{
514+
// Phase 1: strong constraint, minimal mixing
515+
for (int ia = 0; ia < nat; ++ia)
516+
for (int ic = 0; ic < 3; ++ic)
517+
if (constrain[ia][ic] != 0)
518+
lambda[ia][ic] = (target[ia][ic] > 0 ? LAMBDA_STRONG_EV : -LAMBDA_STRONG_EV) / ModuleBase::Ry_to_eV;
519+
this->p_chgmix->set_mixing_beta(BETA_MIN);
520+
GlobalV::ofs_running << " [DS-dir] iter " << iter << " PHASE 1: guidance (lambda=" << LAMBDA_STRONG_EV
521+
<< " eV/uB, beta=" << BETA_MIN << ")" << std::endl;
522+
std::cerr << " [DS-dir] iter " << iter << " PHASE 1: guidance (lambda=" << LAMBDA_STRONG_EV
523+
<< " eV/uB, beta=" << BETA_MIN << ")" << std::endl;
524+
}
525+
else
526+
{
527+
// Phase 2: decay lambda, restore mixing_beta
528+
if (all_aligned)
529+
{
530+
for (int ia = 0; ia < nat; ++ia)
531+
for (int ic = 0; ic < 3; ++ic)
532+
if (constrain[ia][ic] != 0)
533+
lambda[ia][ic] *= DECAY;
534+
GlobalV::ofs_running << " [DS-dir] iter " << iter << " PHASE 2: decay (aligned)" << std::endl;
535+
std::cerr << " [DS-dir] iter " << iter << " PHASE 2: decay (aligned)" << std::endl;
536+
}
537+
else
538+
{
539+
GlobalV::ofs_running << " [DS-dir] iter " << iter << " PHASE 1: still guiding (MISMATCH)" << std::endl;
540+
std::cerr << " [DS-dir] iter " << iter << " PHASE 1: still guiding (MISMATCH)" << std::endl;
541+
// Keep strong constraint if not aligned yet
542+
for (int ia = 0; ia < nat; ++ia)
543+
for (int ic = 0; ic < 3; ++ic)
544+
if (constrain[ia][ic] != 0)
545+
lambda[ia][ic] = (target[ia][ic] > 0 ? LAMBDA_STRONG_EV : -LAMBDA_STRONG_EV) / ModuleBase::Ry_to_eV;
546+
}
547+
// Linearly restore mixing_beta to input value
548+
double beta_restore = std::min(1.0, (iter - GUIDANCE_ITERS) / (double)GUIDANCE_ITERS);
549+
this->p_chgmix->set_mixing_beta(BETA_MIN + (PARAM.inp.mixing_beta - BETA_MIN) * beta_restore);
550+
}
551+
sc.set_lambda(lambda);
552+
// Print per-atom Mi/Target
553+
for (int ia = 0; ia < nat; ++ia)
554+
for (int ic = 0; ic < 3; ++ic)
555+
if (constrain[ia][ic] != 0)
556+
{
557+
std::string status = (Mi[ia][ic] * target[ia][ic] < 0) ? "MISMATCH" : "OK";
558+
GlobalV::ofs_running << " Atom " << ia << " comp " << ic << ": Mi=" << Mi[ia][ic]
559+
<< " T=" << target[ia][ic] << " [" << status
560+
<< "] lambda=" << lambda[ia][ic] * ModuleBase::Ry_to_eV << " eV/uB"
561+
<< " beta=" << this->p_chgmix->get_mixing_beta() << std::endl;
562+
std::cerr << " Atom " << ia << " comp " << ic << ": Mi=" << Mi[ia][ic]
563+
<< " T=" << target[ia][ic] << " [" << status
564+
<< "] lambda=" << lambda[ia][ic] * ModuleBase::Ry_to_eV << " eV/uB"
565+
<< " beta=" << this->p_chgmix->get_mixing_beta() << std::endl;
566+
}
567+
}
568+
483569
// call iter_finish() of ESolver_KS, where band gap is printed,
484570
// eig and occ are printed, magnetization is calculated,
485571
// charge mixing is performed, potential is updated,

source/source_estate/module_charge/charge_mixing.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class Charge_Mixing
5151

5252
void close_kerker_gg0() { mixing_gg0 = 0.0; mixing_gg0_mag = 0.0; }
5353
void conserve_setting() { mixing_beta = 0.01; mixing_beta_mag = 0.04; }
54+
void set_mixing_beta(double beta) { mixing_beta = beta; }
5455
/**
5556
* @brief initialize mixing, including constructing mixing and allocating memory for mixing data
5657
* @brief this function should be called at eachiterinit()

source/source_lcao/module_deltaspin/spin_constrain.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,10 +439,14 @@ class SpinConstrain
439439
void set_constrain(const ModuleBase::Vector3<int>* constrain_in, int nat_in);
440440
/// get sc_lambda
441441
const std::vector<ModuleBase::Vector3<double>>& get_sc_lambda() const;
442+
/// get Mi (current magnetic moments)
443+
const std::vector<ModuleBase::Vector3<double>>& get_Mi() const { return Mi_; }
442444
/// get target_mag
443445
const std::vector<ModuleBase::Vector3<double>>& get_target_mag() const;
444446
/// get constrain
445447
const std::vector<ModuleBase::Vector3<int>>& get_constrain() const;
448+
/// set lambda directly
449+
void set_lambda(const std::vector<ModuleBase::Vector3<double>>& v) { lambda_ = v; }
446450
/// get nat
447451
int get_nat();
448452
/// get ntype

0 commit comments

Comments
 (0)