Skip to content

Commit 0be2efa

Browse files
author
abacus_fixer
committed
Merge remote-tracking branch 'upstream/develop' into 2026-08-10-b
2 parents d3b5bb8 + bdb8678 commit 0be2efa

23 files changed

Lines changed: 1463 additions & 30 deletions

File tree

docs/advanced/elec_properties/hs_matrix.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,12 @@ The output of $H(R)$ and $S(R)$ matrices is controlled by [out_hsr](../input_fil
6565
| --- | --- |
6666
| `0` | Disabled |
6767
| `1` | Text CSR; an optional second value controls precision, for example `out_hsr 1 12` |
68-
| `2` | Reserved for future binary output; not implemented |
68+
| `2` | Native binary CSR using `.dat` files |
6969
| `3` | NPZ: `hrs1_nao.npz`, `hrs2_nao.npz` when needed, and `sr_nao.npz` |
7070

7171
The legacy keywords `out_mat_hs2 1 [precision]` and `out_hsr_npz 1` remain supported as aliases for text and NPZ output respectively. If `out_hsr` is present together with either legacy keyword, `out_hsr` takes precedence.
7272

73-
For a multi-k calculation, the files contain the individual real-space blocks stored for the Bravais lattice vectors $R$. For a gamma-only calculation, ABACUS stores the real-space contributions in a folded representation. Both text CSR and NPZ output write this internal representation directly: all stored $R$-space contributions are summed into a single block labelled `R = (0, 0, 0)`.
73+
For a multi-k calculation, the files contain the individual real-space blocks stored for the Bravais lattice vectors $R$. For a gamma-only calculation, ABACUS stores the real-space contributions in a folded representation. Text CSR, native binary, and NPZ output write this internal representation directly: all stored $R$-space contributions are summed into a single block labelled `R = (0, 0, 0)`.
7474

7575
The folded gamma-only output is sufficient to inspect the matrix used by the gamma-only real-space container, but it does not retain the original lattice-vector resolution and cannot be used to interpolate matrices at arbitrary k points. Terms that are added only while constructing $H(k)$, rather than stored in the internal $H(R)$ container, are not guaranteed to be present. Use [out_hsk](../input_files/input-main.md#out_hsk) when the final $H(\Gamma)$ and $S(\Gamma)$ matrices are required.
7676

@@ -89,6 +89,22 @@ In gamma-only mode, every generated file reports one Bravais lattice vector and
8989
# representation: gamma-only folded matrix; stored R-space contributions are summed into R = (0, 0, 0)
9090
```
9191

92+
### Native Binary CSR Format
93+
94+
Set `out_hsr 2` to write the same H(R) and S(R) matrix sets with a `.dat` suffix. For example, an `nspin = 2` calculation writes `hrs1_nao.dat`, `hrs2_nao.dat`, and `sr_nao.dat`. When `out_app_flag` is false, the one-based ionic step is included before `_nao`, for example `hrs1g1_nao.dat` and `srg1_nao.dat`.
95+
96+
Each ionic step is a complete record with no padding or self-describing header:
97+
98+
1. Native `int`: zero-based ionic step, matrix dimension, number of R blocks.
99+
2. For every R block in lexicographic `(Rx, Ry, Rz)` order, four native `int` values: `Rx`, `Ry`, `Rz`, and `nnz`.
100+
3. `nnz` matrix values in CSR order. Real matrices use one native `double`; complex matrices use consecutive real and imaginary `double` values.
101+
4. `nnz` native `int` column indices.
102+
5. `dimension + 1` native `long long` row pointers.
103+
104+
All R blocks stored by the internal HContainer are present, including blocks with zero nonzero values. The sparse threshold is `1e-10`, matching text CSR output. The format uses the host integer representation and byte order and therefore requires a compatible ABI. It does not contain unit-cell, spin, or matrix-label metadata; those are determined by the calculation input and filename.
105+
106+
When `out_app_flag` is true, the first ionic step truncates the shared file and later ionic steps append complete records. Otherwise every ionic step is written to its own file.
107+
92108
### NPZ Format
93109

94110
Set `out_hsr 3` to write `hrs1_nao.npz`, `hrs2_nao.npz` when a second spin channel is present, and `sr_nao.npz`. Matrix entry names include the atom-pair indices and the three components of $R$. Multi-k calculations retain the stored $R$ blocks, while gamma-only calculations contain only matrix entry names ending in `_0_0_0`.
@@ -125,7 +141,7 @@ The CSR format stores a sparse m × n matrix M in row form using three arrays (v
125141

126142
### Precision Control
127143

128-
Use `out_hsr 1 12` to output text CSR files with 12-digit precision (default is 8). Precision is ignored for NPZ output.
144+
Use `out_hsr 1 12` to output text CSR files with 12-digit precision (default is 8). Precision is ignored for native binary and NPZ output.
129145

130146
For calculations involving ionic movements, the output frequency of the matrix is controlled by [out_freq_ion](../input_files/input-main.md#out_freq_ion) and [out_app_flag](../input_files/input-main.md#out_app_flag).
131147

docs/advanced/input_files/input-main.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2037,11 +2037,13 @@
20372037
- **Description**: Output Hamiltonian and overlap matrices in real space, indexed by the Bravais lattice vector R, in the directory OUT.${suffix}. The first integer selects the format:
20382038
- 0: disabled;
20392039
- 1: text CSR output; the optional second integer controls precision and defaults to 8;
2040-
- 2: reserved for binary output, which is not implemented yet;
2040+
- 2: native binary CSR output using .dat files;
20412041
- 3: NPZ output using hrs1_nao.npz, hrs2_nao.npz when needed, and sr_nao.npz.
20422042

20432043
For multi-k calculations, the output contains the individual real-space blocks stored for the Bravais lattice vectors R. For gamma-only calculations, the internal real-space contributions are folded into a single R = (0, 0, 0) block. This folded result cannot recover the original R-resolved contributions or interpolate arbitrary k points. Terms added only while constructing H(k) are not guaranteed to be present.
20442044

2045+
For binary output, each file uses the same basename as text output with a .dat suffix. Every native record contains the zero-based ionic step, matrix dimension, and number of R blocks as ints. Each R block contains three int coordinates, an int nonzero count, native double values (real/imaginary double pairs for complex matrices), int column indices, and long long row pointers. Native integer representation and byte order are used. When out_app_flag is true, the first ionic step truncates the file and later steps append complete records.
2046+
20452047
> Note: In the 3.10-LTS version, the file names are data-HR-sparse_SPIN0.csr and data-SR-sparse_SPIN0.csr, etc.
20462048
- **Default**: 0 8
20472049
- **Unit**: Ry

docs/parameters.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3145,11 +3145,13 @@ parameters:
31453145
Output Hamiltonian and overlap matrices in real space, indexed by the Bravais lattice vector R, in the directory OUT.${suffix}. The first integer selects the format:
31463146
* 0: disabled;
31473147
* 1: text CSR output; the optional second integer controls precision and defaults to 8;
3148-
* 2: reserved for binary output, which is not implemented yet;
3148+
* 2: native binary CSR output using .dat files;
31493149
* 3: NPZ output using hrs1_nao.npz, hrs2_nao.npz when needed, and sr_nao.npz.
31503150
31513151
For multi-k calculations, the output contains the individual real-space blocks stored for the Bravais lattice vectors R. For gamma-only calculations, the internal real-space contributions are folded into a single R = (0, 0, 0) block. This folded result cannot recover the original R-resolved contributions or interpolate arbitrary k points. Terms added only while constructing H(k) are not guaranteed to be present.
31523152
3153+
For binary output, each file uses the same basename as text output with a .dat suffix. Every native record contains the zero-based ionic step, matrix dimension, and number of R blocks as ints. Each R block contains three int coordinates, an int nonzero count, native double values (real/imaginary double pairs for complex matrices), int column indices, and long long row pointers. Native integer representation and byte order are used. When out_app_flag is true, the first ionic step truncates the file and later steps append complete records.
3154+
31533155
[NOTE] In the 3.10-LTS version, the file names are data-HR-sparse_SPIN0.csr and data-SR-sparse_SPIN0.csr, etc.
31543156
default_value: 0 8
31553157
unit: Ry

source/source_io/module_ctrl/ctrl_scf_lcao.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,13 @@ void ModuleIO::ctrl_scf_lcao(UnitCell& ucell,
267267
//------------------------------------------------------------------
268268
//! 7a) Output H(R) and S(R) matrices in CSR format
269269
//------------------------------------------------------------------
270-
if (inp.out_hsr[0] == 1)
270+
if (inp.out_hsr[0] == 1 || inp.out_hsr[0] == 2)
271271
{
272272
const int precision = inp.out_hsr[1];
273273
std::vector<hamilt::HContainer<TR>*> hr_vec = p_hamilt->getHR_vector();
274274
const hamilt::HContainer<TR>* sr = p_hamilt->getSR();
275275

276-
ModuleIO::write_hsr(hr_vec, sr, &ucell, precision, pv,
276+
ModuleIO::write_hsr(hr_vec, sr, &ucell, inp.out_hsr[0], precision, pv,
277277
out_app_flag, gamma_only, ucell.get_iat2iwt(), ucell.nat, istep);
278278
}
279279

source/source_io/module_hs/write_hs_r.cpp

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

3+
#include "source_base/module_out/sparse_matrix.h"
34
#include "source_base/timer.h"
45
#include "source_base/tool_quit.h"
56
#include "source_io/module_parameter/parameter.h"
@@ -9,6 +10,13 @@
910
#include "source_lcao/spar_st.h"
1011
#include "write_hs_sparse.h"
1112

13+
#include <algorithm>
14+
#include <complex>
15+
#include <fstream>
16+
#include <limits>
17+
#include <tuple>
18+
#include <vector>
19+
1220
// if 'binary=true', output binary file.
1321
// The 'sparse_thr' is the accuracy of the sparse matrix.
1422
// If the absolute value of the matrix element is less than or equal to the
@@ -229,23 +237,39 @@ std::string ModuleIO::hsr_gen_fname(const std::string& prefix,
229237
const bool append,
230238
const int istep)
231239
{
240+
return hsr_gen_fname(prefix, ispin, append, istep, 1);
241+
}
242+
243+
std::string ModuleIO::hsr_gen_fname(const std::string& prefix,
244+
const int ispin,
245+
const bool append,
246+
const int istep,
247+
const int out_type)
248+
{
249+
const std::string extension = out_type == 2 ? ".dat" : ".csr";
232250
if (!append && istep >= 0)
233251
{
234-
return prefix + std::to_string(ispin + 1) + "g" + std::to_string(istep + 1) + "_nao.csr";
252+
return prefix + std::to_string(ispin + 1) + "g" + std::to_string(istep + 1) + "_nao" + extension;
235253
}
236254
else
237255
{
238-
return prefix + std::to_string(ispin + 1) + "_nao.csr";
256+
return prefix + std::to_string(ispin + 1) + "_nao" + extension;
239257
}
240258
}
241259

242260
std::string ModuleIO::sr_gen_fname(const bool append, const int istep)
243261
{
262+
return sr_gen_fname(append, istep, 1);
263+
}
264+
265+
std::string ModuleIO::sr_gen_fname(const bool append, const int istep, const int out_type)
266+
{
267+
const std::string extension = out_type == 2 ? ".dat" : ".csr";
244268
if (!append && istep >= 0)
245269
{
246-
return "srg" + std::to_string(istep + 1) + "_nao.csr";
270+
return "srg" + std::to_string(istep + 1) + "_nao" + extension;
247271
}
248-
return "sr_nao.csr";
272+
return "sr_nao" + extension;
249273
}
250274

251275
std::string ModuleIO::dhr_gen_fname(const std::string& prefix,
@@ -309,10 +333,159 @@ void ModuleIO::write_hcontainer_csr(const std::string& fname,
309333
ofs.close();
310334
}
311335

336+
namespace
337+
{
338+
template <typename T>
339+
void write_native_value(std::ofstream& ofs, const T& value)
340+
{
341+
ofs.write(reinterpret_cast<const char*>(&value), sizeof(T));
342+
}
343+
344+
void write_native_value(std::ofstream& ofs, const std::complex<double>& value)
345+
{
346+
const double real = value.real();
347+
const double imag = value.imag();
348+
write_native_value(ofs, real);
349+
write_native_value(ofs, imag);
350+
}
351+
352+
template <typename TR>
353+
ModuleIO::SparseMatrix<TR> make_sparse_R_block(hamilt::HContainer<TR>* mat_serial,
354+
const int rx,
355+
const int ry,
356+
const int rz,
357+
const double sparse_threshold)
358+
{
359+
const int nbasis = mat_serial->get_nbasis();
360+
ModuleIO::SparseMatrix<TR> sparse_matrix(nbasis, nbasis);
361+
sparse_matrix.setSparseThreshold(sparse_threshold);
362+
mat_serial->fix_R(rx, ry, rz);
363+
364+
for (int iap = 0; iap < mat_serial->size_atom_pairs(); ++iap)
365+
{
366+
const auto atom_pair = mat_serial->get_atom_pair(iap);
367+
const int r_index = atom_pair.find_R(rx, ry, rz);
368+
if (r_index < 0)
369+
{
370+
continue;
371+
}
372+
const auto matrix_info = atom_pair.get_matrix_values(r_index);
373+
const int* index = std::get<0>(matrix_info).data();
374+
TR* data = std::get<1>(matrix_info);
375+
for (int irow = index[0]; irow < index[0] + index[1]; ++irow)
376+
{
377+
for (int icol = index[2]; icol < index[2] + index[3]; ++icol)
378+
{
379+
sparse_matrix.insert(irow, icol, *data);
380+
++data;
381+
}
382+
}
383+
}
384+
385+
mat_serial->unfix_R();
386+
return sparse_matrix;
387+
}
388+
} // namespace
389+
390+
template <typename TR>
391+
void ModuleIO::write_hcontainer_csr_binary(const std::string& fname,
392+
hamilt::HContainer<TR>* mat_serial,
393+
const int istep,
394+
const bool append)
395+
{
396+
std::ios_base::openmode mode = std::ios::out | std::ios::binary;
397+
if (append && istep > 0)
398+
{
399+
mode |= std::ios::app;
400+
}
401+
std::ofstream ofs(fname.c_str(), mode);
402+
if (!ofs.is_open())
403+
{
404+
ModuleBase::WARNING_QUIT("ModuleIO::write_hcontainer_csr_binary",
405+
"Cannot open HContainer binary CSR file: " + fname);
406+
}
407+
408+
const size_t nR_size = mat_serial->size_R_loop();
409+
if (nR_size > static_cast<size_t>(std::numeric_limits<int>::max()))
410+
{
411+
ModuleBase::WARNING_QUIT("ModuleIO::write_hcontainer_csr_binary",
412+
"Too many R blocks for native binary CSR output: " + fname);
413+
}
414+
415+
std::vector<std::tuple<int, int, int>> R_coordinates;
416+
R_coordinates.reserve(nR_size);
417+
for (size_t iR = 0; iR < nR_size; ++iR)
418+
{
419+
int rx = 0;
420+
int ry = 0;
421+
int rz = 0;
422+
mat_serial->loop_R(iR, rx, ry, rz);
423+
R_coordinates.push_back(std::make_tuple(rx, ry, rz));
424+
}
425+
std::sort(R_coordinates.begin(), R_coordinates.end());
426+
427+
const int step = std::max(istep, 0);
428+
const int nbasis = mat_serial->get_nbasis();
429+
const int nR = static_cast<int>(R_coordinates.size());
430+
write_native_value(ofs, step);
431+
write_native_value(ofs, nbasis);
432+
write_native_value(ofs, nR);
433+
434+
const double sparse_threshold = 1e-10;
435+
for (const auto& R_coordinate: R_coordinates)
436+
{
437+
const int rx = std::get<0>(R_coordinate);
438+
const int ry = std::get<1>(R_coordinate);
439+
const int rz = std::get<2>(R_coordinate);
440+
const SparseMatrix<TR> sparse_matrix
441+
= make_sparse_R_block(mat_serial, rx, ry, rz, sparse_threshold);
442+
const auto& elements = sparse_matrix.getElements();
443+
if (elements.size() > static_cast<size_t>(std::numeric_limits<int>::max()))
444+
{
445+
ModuleBase::WARNING_QUIT("ModuleIO::write_hcontainer_csr_binary",
446+
"Too many nonzero values for native binary CSR output: " + fname);
447+
}
448+
const int nnz = static_cast<int>(elements.size());
449+
450+
write_native_value(ofs, rx);
451+
write_native_value(ofs, ry);
452+
write_native_value(ofs, rz);
453+
write_native_value(ofs, nnz);
454+
455+
for (const auto& element: elements)
456+
{
457+
write_native_value(ofs, element.second);
458+
}
459+
460+
std::vector<long long> row_ptr(nbasis + 1, 0);
461+
for (const auto& element: elements)
462+
{
463+
write_native_value(ofs, element.first.second);
464+
++row_ptr[element.first.first + 1];
465+
}
466+
for (int irow = 1; irow <= nbasis; ++irow)
467+
{
468+
row_ptr[irow] += row_ptr[irow - 1];
469+
}
470+
for (const long long pointer: row_ptr)
471+
{
472+
write_native_value(ofs, pointer);
473+
}
474+
}
475+
476+
ofs.close();
477+
if (!ofs)
478+
{
479+
ModuleBase::WARNING_QUIT("ModuleIO::write_hcontainer_csr_binary",
480+
"Failed to write HContainer binary CSR file: " + fname);
481+
}
482+
}
483+
312484
template <typename TR>
313485
void ModuleIO::write_hsr(const std::vector<hamilt::HContainer<TR>*>& hr_vec,
314486
const hamilt::HContainer<TR>* sr,
315487
const UnitCell* ucell,
488+
const int out_type,
316489
const int precision,
317490
const Parallel_2D& paraV,
318491
const bool append,
@@ -321,6 +494,10 @@ void ModuleIO::write_hsr(const std::vector<hamilt::HContainer<TR>*>& hr_vec,
321494
const int nat,
322495
const int istep)
323496
{
497+
if (out_type != 1 && out_type != 2)
498+
{
499+
ModuleBase::WARNING_QUIT("ModuleIO::write_hsr", "out_type must be 1 or 2");
500+
}
324501
const int nspin = hr_vec.size();
325502
assert(nspin > 0);
326503
const std::string representation_note
@@ -346,9 +523,16 @@ void ModuleIO::write_hsr(const std::vector<hamilt::HContainer<TR>*>& hr_vec,
346523
if (GlobalV::MY_RANK == 0)
347524
{
348525
std::string fname = PARAM.globalv.global_out_dir
349-
+ hsr_gen_fname("hrs", ispin, append, istep);
350-
write_hcontainer_csr(
351-
fname, ucell, precision, &hr_serial, istep, ispin, nspin, "H", representation_note);
526+
+ hsr_gen_fname("hrs", ispin, append, istep, out_type);
527+
if (out_type == 2)
528+
{
529+
write_hcontainer_csr_binary(fname, &hr_serial, istep, append);
530+
}
531+
else
532+
{
533+
write_hcontainer_csr(
534+
fname, ucell, precision, &hr_serial, istep, ispin, nspin, "H", representation_note);
535+
}
352536
}
353537
}
354538

@@ -369,9 +553,16 @@ void ModuleIO::write_hsr(const std::vector<hamilt::HContainer<TR>*>& hr_vec,
369553
if (GlobalV::MY_RANK == 0)
370554
{
371555
std::string fname = PARAM.globalv.global_out_dir
372-
+ sr_gen_fname(append, istep);
373-
write_hcontainer_csr(
374-
fname, ucell, precision, &sr_serial, istep, 0, 1, "S", representation_note);
556+
+ sr_gen_fname(append, istep, out_type);
557+
if (out_type == 2)
558+
{
559+
write_hcontainer_csr_binary(fname, &sr_serial, istep, append);
560+
}
561+
else
562+
{
563+
write_hcontainer_csr(
564+
fname, ucell, precision, &sr_serial, istep, 0, 1, "S", representation_note);
565+
}
375566
}
376567
}
377568
}
@@ -384,15 +575,20 @@ template void ModuleIO::write_hcontainer_csr<std::complex<double>>(
384575
const std::string&, const UnitCell*, const int,
385576
hamilt::HContainer<std::complex<double>>*, const int, const int, const int, const std::string&, const std::string&);
386577

578+
template void ModuleIO::write_hcontainer_csr_binary<double>(
579+
const std::string&, hamilt::HContainer<double>*, const int, const bool);
580+
template void ModuleIO::write_hcontainer_csr_binary<std::complex<double>>(
581+
const std::string&, hamilt::HContainer<std::complex<double>>*, const int, const bool);
582+
387583
template void ModuleIO::write_hsr<double>(
388584
const std::vector<hamilt::HContainer<double>*>&,
389585
const hamilt::HContainer<double>*,
390-
const UnitCell*, const int, const Parallel_2D&,
586+
const UnitCell*, const int, const int, const Parallel_2D&,
391587
const bool, const bool, const int*, const int, const int);
392588
template void ModuleIO::write_hsr<std::complex<double>>(
393589
const std::vector<hamilt::HContainer<std::complex<double>>*>&,
394590
const hamilt::HContainer<std::complex<double>>*,
395-
const UnitCell*, const int, const Parallel_2D&,
591+
const UnitCell*, const int, const int, const Parallel_2D&,
396592
const bool, const bool, const int*, const int, const int);
397593

398594

0 commit comments

Comments
 (0)