22#define GET_PCHG_PW_H
33
44#include " source_base/module_container/ATen/core/tensor.h"
5+ #include " source_base/parallel_comm.h"
56#include " source_estate/module_charge/symm_rho.h"
7+ #include " source_io/module_output/band_parallel_output.h"
68#include " source_io/module_output/cube_io.h"
79
810namespace ModuleIO
@@ -18,6 +20,7 @@ namespace ModuleIO
1820template <typename Device>
1921void get_pchg_pw (const std::vector<int >& out_pchg,
2022 const int nspin,
23+ const int global_nbands,
2124 UnitCell* ucell,
2225 const psi::Psi<std::complex <double >, Device>* kspw_psi,
2326 const ModulePW::PW_Basis* pw_rho,
@@ -31,7 +34,9 @@ void get_pchg_pw(const std::vector<int>& out_pchg,
3134{
3235 const int nks = kv.get_nks (); // current process pool k-point count
3336 const int nkstot = kv.get_nkstot (); // total k-point count
34- const int nbands = kspw_psi->get_nbands ();
37+ // INPUT selectors and file labels use global bands, while BPCG Psi storage uses a
38+ // contiguous local shard. Constructing the layout collectively reconciles both views.
39+ const BandParallelLayout band_layout (kspw_psi->get_nbands (), global_nbands);
3540
3641 const int nks_without_spin = nspin == 2 ? nkstot / 2 : nkstot;
3742 const int smooth_nrxx = pw_wfc->nrxx ;
@@ -40,8 +45,8 @@ void get_pchg_pw(const std::vector<int>& out_pchg,
4045 const bool needs_interpolation = pw_rhod != pw_rho;
4146
4247 // Expand the INPUT selection into a fixed-size mask indexed directly by band.
43- std::vector<int > bands_picked (nbands , 0 );
44- if (static_cast <int >(out_pchg.size ()) > nbands )
48+ std::vector<int > bands_picked (global_nbands , 0 );
49+ if (static_cast <int >(out_pchg.size ()) > global_nbands )
4550 {
4651 ModuleBase::WARNING_QUIT (" ModuleIO::get_pchg_pw" ,
4752 " The number of bands specified by `out_pchg` in the "
@@ -56,7 +61,7 @@ void get_pchg_pw(const std::vector<int>& out_pchg,
5661 " Invalid values found!" );
5762 }
5863 }
59- const int length = std::min (static_cast <int >(out_pchg.size ()), nbands );
64+ const int length = std::min (static_cast <int >(out_pchg.size ()), global_nbands );
6065 for (int i = 0 ; i < length; ++i)
6166 {
6267 bands_picked[i] = static_cast <int >(out_pchg[i]);
@@ -117,6 +122,35 @@ void get_pchg_pw(const std::vector<int>& out_pchg,
117122 return dense_host.data <std::complex <double >>();
118123 };
119124
125+ // Each buffer stores one rank-local dense-grid slab. It is global with respect to
126+ // band ownership, not spatial decomposition: POOL_WORLD still distributes the grid.
127+ std::vector<std::complex <double >> wfcr_up_global (dense_nrxx);
128+ std::vector<std::complex <double >> wfcr_down_global (is_spinor ? dense_nrxx : 0 );
129+ // Only the owning band group may index the local Psi shard. After its FFT, BP_WORLD
130+ // broadcasts the slab to the corresponding plane-wave rank in every band group.
131+ // Every group must call this lambda in the same band/k-point/component order.
132+ auto transform_global_band = [&](const int global_band,
133+ const int basis_offset,
134+ const int ik,
135+ ct::Tensor& smooth,
136+ ct::Tensor& smooth_host,
137+ ct::Tensor& dense_host,
138+ std::vector<std::complex <double >>& global_wfcr) -> const std::complex <double >* {
139+ const int owner = band_layout.owner_group (global_band);
140+ if (band_layout.band_group () == owner)
141+ {
142+ const int local_band = band_layout.local_index (global_band);
143+ kspw_psi->fix_k (ik);
144+ const std::complex <double >* owner_wfcr
145+ = transform_wfc (&kspw_psi[0 ](local_band, basis_offset), ik, smooth, smooth_host, dense_host);
146+ std::copy (owner_wfcr, owner_wfcr + dense_nrxx, global_wfcr.begin ());
147+ }
148+ #ifdef __MPI
149+ MPI_Bcast (global_wfcr.data (), dense_nrxx, MPI_DOUBLE_COMPLEX , owner, BP_WORLD );
150+ #endif
151+ return global_wfcr.data ();
152+ };
153+
120154 std::vector<std::vector<double >> rho_band (nspin, std::vector<double >(dense_nrxx));
121155 // Convert a two-component spinor into the Pauli-basis fields (rho, m_x, m_y, m_z).
122156 // Per-k output overwrites the fields, whereas k-summed output accumulates weighted fields.
@@ -147,7 +181,9 @@ void get_pchg_pw(const std::vector<int>& out_pchg,
147181 }
148182 };
149183
150- for (int ib = 0 ; ib < nbands; ++ib)
184+ // Traverse global bands on every rank so that owner broadcasts remain collective-safe
185+ // even when the selected band belongs to a nonzero band group.
186+ for (int ib = 0 ; ib < global_nbands; ++ib)
151187 {
152188 if (!bands_picked[ib])
153189 {
@@ -162,23 +198,26 @@ void get_pchg_pw(const std::vector<int>& out_pchg,
162198 if (if_separate_k)
163199 {
164200 // Preserve each Bloch state's contribution; no Brillouin-zone weight is applied here.
201+ // Each KPAR pool writes only the global k-points it owns.
165202 for (int ik = 0 ; ik < nks; ++ik)
166203 {
167204 const int ikstot = kv.ik2iktot [ik];
168205 const int spin_index = kv.isk [ik];
169206 // In collinear calculations the two spin channels share the same k-point numbering.
170207 const int k_number = ikstot % nks_without_spin + 1 ;
171208
172- kspw_psi->fix_k (ik);
173- const std::complex <double >* wfcr_up
174- = transform_wfc (&kspw_psi[0 ](ib, 0 ), ik, wfcr_up_smooth, wfcr_up_smooth_host, wfcr_up_dense_host);
175- const std::complex <double >* wfcr_up_host_data = wfcr_up;
209+ const std::complex <double >* wfcr_up_host_data
210+ = transform_global_band (ib, 0 , ik, wfcr_up_smooth, wfcr_up_smooth_host, wfcr_up_dense_host, wfcr_up_global);
176211 const std::complex <double >* wfcr_down_host_data = nullptr ;
177212 if (is_spinor)
178213 {
179- const std::complex <double >* wfcr_down
180- = transform_wfc (&kspw_psi[0 ](ib, npwx), ik, wfcr_down_smooth, wfcr_down_smooth_host, wfcr_down_dense_host);
181- wfcr_down_host_data = wfcr_down;
214+ wfcr_down_host_data = transform_global_band (ib,
215+ npwx,
216+ ik,
217+ wfcr_down_smooth,
218+ wfcr_down_smooth_host,
219+ wfcr_down_dense_host,
220+ wfcr_down_global);
182221 }
183222
184223 const double spin_degeneracy = nspin == 1 ? 2.0 : 1.0 ;
@@ -218,21 +257,24 @@ void get_pchg_pw(const std::vector<int>& out_pchg,
218257 }
219258 else
220259 {
221- // Form a Brillouin-zone weighted partial density for the selected band.
260+ // Form the pool-local part of the Brillouin-zone weighted density. Owner
261+ // broadcasts make this contribution identical across band groups.
222262 for (int ik = 0 ; ik < nks; ++ik)
223263 {
224264 const int spin_index = kv.isk [ik];
225265
226- kspw_psi->fix_k (ik);
227- const std::complex <double >* wfcr_up
228- = transform_wfc (&kspw_psi[0 ](ib, 0 ), ik, wfcr_up_smooth, wfcr_up_smooth_host, wfcr_up_dense_host);
229- const std::complex <double >* wfcr_up_host_data = wfcr_up;
266+ const std::complex <double >* wfcr_up_host_data
267+ = transform_global_band (ib, 0 , ik, wfcr_up_smooth, wfcr_up_smooth_host, wfcr_up_dense_host, wfcr_up_global);
230268 const std::complex <double >* wfcr_down_host_data = nullptr ;
231269 if (is_spinor)
232270 {
233- const std::complex <double >* wfcr_down
234- = transform_wfc (&kspw_psi[0 ](ib, npwx), ik, wfcr_down_smooth, wfcr_down_smooth_host, wfcr_down_dense_host);
235- wfcr_down_host_data = wfcr_down;
271+ wfcr_down_host_data = transform_global_band (ib,
272+ npwx,
273+ ik,
274+ wfcr_down_smooth,
275+ wfcr_down_smooth_host,
276+ wfcr_down_dense_host,
277+ wfcr_down_global);
236278 }
237279
238280 const double weight = static_cast <double >(kv.wk [ik] / ucell->omega );
0 commit comments