Skip to content

Commit 2b0b14d

Browse files
committed
Fix and optimize get_S sparse output
1 parent d320d16 commit 2b0b14d

3 files changed

Lines changed: 293 additions & 23 deletions

File tree

source/module_esolver/esolver_gets.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,8 @@ void ESolver_GetS::before_all_runners(UnitCell& ucell, const Input_para& inp)
7272
two_center_bundle_,
7373
orb_);
7474

75-
// 4) initialize the density matrix
76-
// DensityMatrix is allocated here, DMK is also initialized here
77-
// DMR is not initialized here, it will be constructed in each before_scf
78-
dynamic_cast<elecstate::ElecStateLCAO<std::complex<double>>*>(this->pelec)
79-
->init_DM(&this->kv, &(this->pv), inp.nspin);
75+
// get_S only builds and writes overlap matrices. Allocating DMK here is
76+
// unused and can dominate memory for large LCAO/SOC systems.
8077

8178
ModuleBase::timer::tick("ESolver_GetS", "before_all_runners");
8279
}

source/module_io/write_HS_R.cpp

Lines changed: 283 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "write_HS_R.h"
22

3+
#include "module_base/parallel_reduce.h"
34
#include "module_parameter/parameter.h"
45
#include "module_base/timer.h"
56
#include "module_hamilt_lcao/hamilt_lcaodft/LCAO_HS_arrays.hpp"
@@ -8,6 +9,248 @@
89
#include "module_hamilt_lcao/hamilt_lcaodft/spar_st.h"
910
#include "write_HS_sparse.h"
1011

12+
#ifdef __MPI
13+
#include <mpi.h>
14+
#endif
15+
16+
#include <algorithm>
17+
#include <cstdio>
18+
#include <fstream>
19+
#include <iomanip>
20+
#include <numeric>
21+
#include <sstream>
22+
#include <vector>
23+
24+
namespace
25+
{
26+
template <typename T>
27+
struct GetSEntry
28+
{
29+
size_t row;
30+
size_t col;
31+
T value;
32+
};
33+
34+
void write_gets_data(std::ofstream& ofs, const double& data)
35+
{
36+
ofs << " " << std::fixed << std::scientific << std::setprecision(8) << data;
37+
}
38+
39+
void write_gets_data(std::ofstream& ofs, const std::complex<double>& data)
40+
{
41+
ofs << " (" << std::fixed << std::scientific << std::setprecision(8) << data.real() << ","
42+
<< std::fixed << std::scientific << std::setprecision(8) << data.imag() << ")";
43+
}
44+
45+
template <typename Tdata>
46+
void save_gets_sparse_fast(
47+
const std::map<Abfs::Vector3_Order<int>, std::map<size_t, std::map<size_t, Tdata>>>& smat,
48+
const std::set<Abfs::Vector3_Order<int>>& all_R_coor,
49+
const double& sparse_thr,
50+
const std::string& filename,
51+
const std::string& label,
52+
const int& istep)
53+
{
54+
const int nlocal = PARAM.globalv.nlocal;
55+
const int total_R_num = all_R_coor.size();
56+
57+
std::vector<int> nonzero_num(total_R_num, 0);
58+
int count = 0;
59+
for (const auto& R_coor : all_R_coor)
60+
{
61+
auto iter = smat.find(R_coor);
62+
if (iter != smat.end())
63+
{
64+
for (const auto& row_loop : iter->second)
65+
{
66+
for (const auto& value : row_loop.second)
67+
{
68+
if (std::abs(value.second) > sparse_thr)
69+
{
70+
++nonzero_num[count];
71+
}
72+
}
73+
}
74+
}
75+
++count;
76+
}
77+
#ifdef __MPI
78+
Parallel_Reduce::reduce_all(nonzero_num.data(), total_R_num);
79+
#endif
80+
81+
int output_R_number = 0;
82+
for (const int nnz : nonzero_num)
83+
{
84+
if (nnz != 0)
85+
{
86+
++output_R_number;
87+
}
88+
}
89+
90+
int myrank = 0;
91+
int nprocs = 1;
92+
#ifdef __MPI
93+
MPI_Comm_rank(MPI_COMM_WORLD, &myrank);
94+
MPI_Comm_size(MPI_COMM_WORLD, &nprocs);
95+
#endif
96+
97+
std::ofstream ofs;
98+
if (myrank == 0)
99+
{
100+
ofs.open(filename.c_str());
101+
ofs << "STEP: " << std::max(istep, 0) << std::endl;
102+
ofs << "Matrix Dimension of " + label + "(R): " << nlocal << std::endl;
103+
ofs << "Matrix number of " + label + "(R): " << output_R_number << std::endl;
104+
}
105+
106+
count = 0;
107+
for (const auto& R_coor : all_R_coor)
108+
{
109+
if (nonzero_num[count] == 0)
110+
{
111+
++count;
112+
continue;
113+
}
114+
115+
std::ofstream ofs_indices;
116+
std::ifstream ifs_indices;
117+
std::stringstream indices_filename;
118+
indices_filename << PARAM.globalv.global_out_dir << std::to_string(myrank) << "temp_gets_sparse_indices.dat";
119+
120+
std::vector<int> indptr;
121+
if (myrank == 0)
122+
{
123+
ofs << R_coor.x << " " << R_coor.y << " " << R_coor.z << " "
124+
<< nonzero_num[count] << std::endl;
125+
ofs_indices.open(indices_filename.str().c_str());
126+
indptr.reserve(nlocal + 1);
127+
indptr.push_back(0);
128+
}
129+
130+
const int row_block_size = 4096;
131+
for (int row_begin = 0; row_begin < nlocal; row_begin += row_block_size)
132+
{
133+
const int row_end = std::min(row_begin + row_block_size, nlocal);
134+
135+
std::vector<GetSEntry<Tdata>> local_entries;
136+
auto iter = smat.find(R_coor);
137+
if (iter != smat.end())
138+
{
139+
auto row_iter = iter->second.lower_bound(static_cast<size_t>(row_begin));
140+
while (row_iter != iter->second.end() && row_iter->first < static_cast<size_t>(row_end))
141+
{
142+
for (const auto& value : row_iter->second)
143+
{
144+
if (std::abs(value.second) > sparse_thr)
145+
{
146+
local_entries.push_back({row_iter->first, value.first, value.second});
147+
}
148+
}
149+
++row_iter;
150+
}
151+
}
152+
153+
std::vector<GetSEntry<Tdata>> entries;
154+
#ifdef __MPI
155+
const int local_bytes = static_cast<int>(local_entries.size() * sizeof(GetSEntry<Tdata>));
156+
std::vector<int> recv_counts(nprocs, 0);
157+
MPI_Gather(&local_bytes, 1, MPI_INT, recv_counts.data(), 1, MPI_INT, 0, MPI_COMM_WORLD);
158+
159+
std::vector<int> displs;
160+
int total_bytes = 0;
161+
if (myrank == 0)
162+
{
163+
displs.resize(nprocs, 0);
164+
for (int ip = 1; ip < nprocs; ++ip)
165+
{
166+
displs[ip] = displs[ip - 1] + recv_counts[ip - 1];
167+
}
168+
total_bytes = std::accumulate(recv_counts.begin(), recv_counts.end(), 0);
169+
entries.resize(total_bytes / sizeof(GetSEntry<Tdata>));
170+
}
171+
172+
MPI_Gatherv(local_entries.empty() ? nullptr : local_entries.data(),
173+
local_bytes,
174+
MPI_BYTE,
175+
entries.empty() ? nullptr : entries.data(),
176+
recv_counts.empty() ? nullptr : recv_counts.data(),
177+
displs.empty() ? nullptr : displs.data(),
178+
MPI_BYTE,
179+
0,
180+
MPI_COMM_WORLD);
181+
#else
182+
entries.swap(local_entries);
183+
#endif
184+
185+
if (myrank == 0)
186+
{
187+
std::sort(entries.begin(), entries.end(), [](const auto& lhs, const auto& rhs) {
188+
return lhs.row == rhs.row ? lhs.col < rhs.col : lhs.row < rhs.row;
189+
});
190+
191+
std::vector<GetSEntry<Tdata>> merged_entries;
192+
merged_entries.reserve(entries.size());
193+
for (size_t i = 0; i < entries.size();)
194+
{
195+
GetSEntry<Tdata> merged = entries[i];
196+
++i;
197+
while (i < entries.size() && entries[i].row == merged.row && entries[i].col == merged.col)
198+
{
199+
merged.value += entries[i].value;
200+
++i;
201+
}
202+
if (std::abs(merged.value) > sparse_thr)
203+
{
204+
merged_entries.push_back(merged);
205+
}
206+
}
207+
208+
size_t entry_index = 0;
209+
for (int row = row_begin; row < row_end; ++row)
210+
{
211+
int row_nnz = 0;
212+
while (entry_index < merged_entries.size()
213+
&& merged_entries[entry_index].row == static_cast<size_t>(row))
214+
{
215+
write_gets_data(ofs, merged_entries[entry_index].value);
216+
ofs_indices << " " << merged_entries[entry_index].col;
217+
++row_nnz;
218+
++entry_index;
219+
}
220+
indptr.push_back(indptr.back() + row_nnz);
221+
}
222+
}
223+
}
224+
225+
if (myrank == 0)
226+
{
227+
ofs << std::endl;
228+
229+
ofs_indices << std::endl;
230+
ofs_indices.close();
231+
ifs_indices.open(indices_filename.str().c_str());
232+
ofs << ifs_indices.rdbuf();
233+
ifs_indices.close();
234+
235+
for (const auto& pointer : indptr)
236+
{
237+
ofs << " " << pointer;
238+
}
239+
ofs << std::endl;
240+
241+
std::remove(indices_filename.str().c_str());
242+
}
243+
244+
++count;
245+
}
246+
247+
if (myrank == 0)
248+
{
249+
ofs.close();
250+
}
251+
}
252+
} // namespace
253+
11254
// if 'binary=true', output binary file.
12255
// The 'sparse_thr' is the accuracy of the sparse matrix.
13256
// If the absolute value of the matrix element is less than or equal to the
@@ -178,25 +421,49 @@ void ModuleIO::output_SR(Parallel_Orbitals& pv,
178421

179422
if (PARAM.inp.nspin == 4)
180423
{
181-
ModuleIO::save_sparse(HS_Arrays.SR_soc_sparse,
182-
HS_Arrays.all_R_coor,
183-
sparse_thr,
184-
binary,
185-
SR_filename,
186-
pv,
187-
"S",
188-
istep);
424+
if (PARAM.inp.calculation == "get_S" && !binary)
425+
{
426+
save_gets_sparse_fast(HS_Arrays.SR_soc_sparse,
427+
HS_Arrays.all_R_coor,
428+
sparse_thr,
429+
SR_filename,
430+
"S",
431+
istep);
432+
}
433+
else
434+
{
435+
ModuleIO::save_sparse(HS_Arrays.SR_soc_sparse,
436+
HS_Arrays.all_R_coor,
437+
sparse_thr,
438+
binary,
439+
SR_filename,
440+
pv,
441+
"S",
442+
istep);
443+
}
189444
}
190445
else
191446
{
192-
ModuleIO::save_sparse(HS_Arrays.SR_sparse,
193-
HS_Arrays.all_R_coor,
194-
sparse_thr,
195-
binary,
196-
SR_filename,
197-
pv,
198-
"S",
199-
istep);
447+
if (PARAM.inp.calculation == "get_S" && !binary)
448+
{
449+
save_gets_sparse_fast(HS_Arrays.SR_sparse,
450+
HS_Arrays.all_R_coor,
451+
sparse_thr,
452+
SR_filename,
453+
"S",
454+
istep);
455+
}
456+
else
457+
{
458+
ModuleIO::save_sparse(HS_Arrays.SR_sparse,
459+
HS_Arrays.all_R_coor,
460+
sparse_thr,
461+
binary,
462+
SR_filename,
463+
pv,
464+
"S",
465+
istep);
466+
}
200467
}
201468

202469
sparse_format::destroy_HS_R_sparse(HS_Arrays);

source/module_io/write_HS_sparse.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,7 +765,13 @@ void ModuleIO::save_sparse(
765765
}
766766
}
767767

768-
output_single_R(ofs, smat.at(R_coor), sparse_thr, binary, pv, reduce);
768+
// nonzero_num is reduced across all MPI ranks, but a given rank may
769+
// not own local sparse data for this R block. It still has to enter
770+
// output_single_R so the row-wise reductions remain matched.
771+
const std::map<size_t, std::map<size_t, Tdata>> empty_smat;
772+
auto iter = smat.find(R_coor);
773+
const auto& local_smat = (iter == smat.end()) ? empty_smat : iter->second;
774+
output_single_R(ofs, local_smat, sparse_thr, binary, pv, reduce);
769775
++count;
770776
}
771777
if (!reduce || GlobalV::DRANK == 0) {
@@ -797,4 +803,4 @@ template void ModuleIO::save_sparse<std::complex<double>>(
797803
const Parallel_Orbitals&,
798804
const std::string&,
799805
const int&,
800-
const bool&);
806+
const bool&);

0 commit comments

Comments
 (0)