11#include " charge_mixing.h"
22
3+ #include < functional>
4+
35#include " source_io/module_parameter/parameter.h"
46#include " source_base/module_mixing/broyden_mixing.h"
57#include " source_base/module_mixing/pulay_mixing.h"
68#include " source_base/parallel_common.h"
9+ #include " source_base/parallel_reduce.h"
710#include " source_base/timer.h"
811#include " source_hamilt/module_xc/xc_functional.h"
912
1013Charge_Mixing::Charge_Mixing ()
1114{
1215 this ->mixing = nullptr ;
16+ this ->mixing_uom = nullptr ;
1317 this ->mixing_highf = nullptr ;
1418}
1519
@@ -26,6 +30,12 @@ Charge_Mixing::~Charge_Mixing()
2630 delete this ->mixing_highf ;
2731 this ->mixing_highf = nullptr ;
2832 }
33+
34+ if (this ->mixing_uom != nullptr )
35+ {
36+ delete this ->mixing_uom ;
37+ this ->mixing_uom = nullptr ;
38+ }
2939}
3040
3141void Charge_Mixing::set_mixing (const std::string& mixing_mode_in,
@@ -112,7 +122,9 @@ void Charge_Mixing::init_mixing()
112122 ModuleBase::TITLE (" Charge_Mixing" , " init_mixing" );
113123 ModuleBase::timer::start (" Charge_Mixing" , " init_mixing" );
114124
115- // (re)construct mixing object
125+ // (re)construct the charge mixer. The independent UOM mixer is
126+ // initialized explicitly by the DFT+U call path.
127+
116128 if (this ->mixing_mode == " broyden" )
117129 {
118130 delete this ->mixing ;
@@ -133,6 +145,9 @@ void Charge_Mixing::init_mixing()
133145 ModuleBase::WARNING_QUIT (" Charge_Mixing" , " This Mixing mode is not implemended yet,coming soon." );
134146 }
135147
148+ delete this ->mixing_uom ;
149+ this ->mixing_uom = nullptr ;
150+
136151 if ( PARAM .globalv .double_grid )
137152 {
138153 // ONLY smooth part of charge density is mixed by specific mixing method
@@ -190,6 +205,20 @@ void Charge_Mixing::init_mixing()
190205 return ;
191206}
192207
208+ void Charge_Mixing::init_mixing_uom ()
209+ {
210+ ModuleBase::TITLE (" Charge_Mixing" , " init_mixing_uom" );
211+ ModuleBase::timer::start (" Charge_Mixing" , " init_mixing_uom" );
212+
213+ delete this ->mixing_uom ;
214+ // The occupation matrix is a small, strongly coupled nonlinear variable.
215+ // A conservative plain step avoids binding its history to the much
216+ // larger charge-density mixer.
217+ this ->mixing_uom = new Base_Mixing::Plain_Mixing (0.5 * this ->mixing_beta );
218+
219+ ModuleBase::timer::end (" Charge_Mixing" , " init_mixing_uom" );
220+ }
221+
193222void Charge_Mixing::set_rhopw (ModulePW::PW_Basis* rhopw_in, ModulePW::PW_Basis* rhodpw_in)
194223{
195224 this ->rhopw = rhopw_in;
@@ -205,6 +234,11 @@ void Charge_Mixing::mix_reset()
205234 {
206235 this ->tau_mdata .reset ();
207236 }
237+ if (this ->mixing_uom != nullptr )
238+ {
239+ this ->mixing_uom ->reset ();
240+ this ->uom_mdata .reset ();
241+ }
208242}
209243
210244bool Charge_Mixing::if_scf_oscillate (const int iteration, const double drho, const int iternum_used, const double threshold)
@@ -265,8 +299,12 @@ void Charge_Mixing::allocate_mixing_uom(int uom_size)
265299 ModuleBase::timer::start (" Charge_Mixing" , " allocate_mixing_uom" );
266300 // For nspin=2, uom_size already includes both spin channels
267301 // (pot_uterm_pw.size() = pot_index * 2 for nspin=2)
268- // So uom_fold should always be 1
269- this ->mixing ->init_mixing_data (this ->uom_mdata , uom_size, sizeof (double ));
302+ // So uom_fold should always be 1. UOM has an independent history.
303+ if (this ->mixing_uom == nullptr )
304+ {
305+ ModuleBase::WARNING_QUIT (" Charge_Mixing" , " UOM mixing object is not initialized." );
306+ }
307+ this ->mixing_uom ->init_mixing_data (this ->uom_mdata , uom_size, sizeof (double ));
270308 this ->uom_mdata .reset ();
271309 ModuleBase::timer::end (" Charge_Mixing" , " allocate_mixing_uom" );
272310 return ;
@@ -276,16 +314,53 @@ void Charge_Mixing::mix_uom(std::vector<double>& uom_in, std::vector<double>& uo
276314{
277315 ModuleBase::TITLE (" Charge_Mixing" , " mix_uom" );
278316 ModuleBase::timer::start (" Charge_Mixing" , " mix_uom" );
317+
318+ if (uom_in.empty ())
319+ {
320+ ModuleBase::timer::end (" Charge_Mixing" , " mix_uom" );
321+ return ;
322+ }
323+ if (this ->mixing_uom == nullptr || this ->uom_mdata .length != uom_in.size ()
324+ || uom_save_in.size () != uom_in.size ())
325+ {
326+ ModuleBase::WARNING_QUIT (" Charge_Mixing" , " UOM mixing data is not initialized consistently." );
327+ }
328+
279329 double * uom_value_out = uom_in.data ();
280330 double * uom_value_in = uom_save_in.data ();
281- // For all nspin cases, uom_array layout is already fully sized
282- // and mixing operates on the entire array
283- this ->mixing ->push_data (this ->uom_mdata , uom_value_in, uom_value_out, nullptr , false );
284- this ->mixing ->mix_data (this ->uom_mdata , uom_value_out);
331+ const std::size_t length = this ->uom_mdata .length ;
332+
333+ // The UOM vector has its own history and its own contractive step. It
334+ // must not reuse the charge-density mixer: the two vectors have different
335+ // lengths and represent different nonlinear variables.
336+ this ->mixing_uom ->push_data (this ->uom_mdata ,
337+ uom_value_in,
338+ uom_value_out,
339+ nullptr ,
340+ true );
341+
342+ auto inner_product_uom = [length](double * first, double * second) {
343+ double value = 0.0 ;
344+ #ifdef _OPENMP
345+ #pragma omp parallel for reduction(+ : value)
346+ #endif
347+ for (std::size_t i = 0 ; i < length; ++i)
348+ {
349+ value += first[i] * second[i];
350+ }
351+ #ifdef __MPI
352+ Parallel_Reduce::reduce_pool (value);
353+ #endif
354+ return value;
355+ };
356+
357+ this ->mixing_uom ->cal_coef (this ->uom_mdata , inner_product_uom);
358+ this ->mixing_uom ->mix_data (this ->uom_mdata , uom_value_out);
359+
285360 ModuleBase::timer::end (" Charge_Mixing" , " mix_uom" );
286361#ifdef __MPI
287362 // Synchronize mixed uom across all ranks to prevent divergence
288- // after multiple Pulay steps (same pattern as mix_dmr)
363+ // after multiple mixing steps.
289364 Parallel_Common::bcast_double (uom_in.data (), uom_in.size ());
290365#endif
291366 return ;
0 commit comments