Skip to content

Commit 10ba137

Browse files
dyzhengdyzhengclaude
authored
fix: nspin = 2 for init_chg=dm (#6975)
* fix: nspin = 2 for init_chg=dm * Fix: findmkl.cmake error * Fix: init_chg hr support nspin=2 * Fix: refactor for short esolver_ks_lcao.cpp * Fix: rewrite init_dm_from_file_test to match new CSR format, add init_chg=hr tests The CSR file format was updated (commit 9c1a061) with spin info, ucell data, and CSR comment block headers, but the unit test still wrote the old format, causing parse failures. Rewrote write_test_csr to use write_dmr_csr so the test always produces the current format. Added HR read tests for nspin=1 and nspin=2 covering the init_chg=hr code path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix: replace system() calls with POSIX mkdir in init_dm_from_file test Use POSIX mkdir() from <sys/stat.h> instead of system("mkdir -p") for C++11 compatibility, and remove system("rm -rf") cleanup calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: dyzheng <zhengdy@bjaisi.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bd462b4 commit 10ba137

File tree

12 files changed

+692
-39
lines changed

12 files changed

+692
-39
lines changed

cmake/FindMKL.cmake

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ find_path(MKL_INCLUDE mkl_service.h HINTS ${MKLROOT}/include)
1212
find_library(MKL_INTEL NAMES mkl_intel_lp64 HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
1313
find_library(MKL_INTEL_THREAD NAMES mkl_intel_thread HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
1414
find_library(MKL_CORE NAMES mkl_core HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
15+
find_library(MKL_IOMP5 NAMES iomp5
16+
HINTS ENV CMPLR_ROOT
17+
PATH_SUFFIXES lib lib/intel64 linux/compiler/lib/intel64_lin
18+
)
1519
if(ENABLE_MPI)
1620
find_library(MKL_SCALAPACK NAMES mkl_scalapack_lp64 HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
1721
find_library(MKL_BLACS_INTELMPI NAMES mkl_blacs_intelmpi_lp64 HINTS ${MKLROOT}/lib ${MKLROOT}/lib/intel64)
@@ -58,6 +62,11 @@ if(MKL_FOUND)
5862
IMPORTED_LOCATION "${MKL_BLACS_INTELMPI}"
5963
INTERFACE_INCLUDE_DIRECTORIES "${MKL_INCLUDE}")
6064
endif()
65+
if(MKL_IOMP5 AND NOT TARGET MKL::IOMP5)
66+
add_library(MKL::IOMP5 UNKNOWN IMPORTED)
67+
set_target_properties(MKL::IOMP5 PROPERTIES
68+
IMPORTED_LOCATION "${MKL_IOMP5}")
69+
endif()
6170
add_library(MKL::MKL INTERFACE IMPORTED)
6271
if (ENABLE_MPI)
6372
set_property(TARGET MKL::MKL PROPERTY
@@ -74,6 +83,10 @@ if(MKL_FOUND)
7483
"-Wl,--end-group"
7584
)
7685
endif()
86+
if(TARGET MKL::IOMP5)
87+
set_property(TARGET MKL::MKL APPEND PROPERTY
88+
INTERFACE_LINK_LIBRARIES MKL::IOMP5)
89+
endif()
7790
endif()
7891

7992
if(ENABLE_MPI)

docs/advanced/elec_properties/density_matrix.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,15 @@ The following line is dimension of the density matrix, and the rest lines are th
3838
The examples can be found in [examples/density_matrix](https://github.com/deepmodeling/abacus-develop/tree/develop/examples/density_matrix)
3939

4040
- Note: now this function is valid only for LCAO gamma only calcualtion.
41+
42+
## Real-space Density Matrix (CSR format)
43+
44+
ABACUS can also output the real-space density matrix DM(R) in CSR (Compressed Sparse Row) format by setting:
45+
```
46+
out_dmr 1
47+
```
48+
After the calculation, the density matrix files are written to `OUT.${suffix}/`:
49+
- `nspin=1`: `dmrs1_nao.csr`
50+
- `nspin=2` (spin-polarized): `dmrs1_nao.csr` (spin-up) and `dmrs2_nao.csr` (spin-down)
51+
52+
These files can be used to restart calculations by setting `init_chg dm` in the INPUT file together with `read_file_dir` pointing to the directory containing the CSR files. This is supported for both `nspin=1` and `nspin=2`.

docs/advanced/scf/initialization.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
# Initializing SCF
22
Good initializing would abate the number of iteration steps in SCF.
3-
Charge density should be initialed for constructing the initial hamiltonian operator.
3+
Charge density should be initialed for constructing the initial hamiltonian operator.
44

55
In PW basis, wavefunction should be initialized for iterate diagonalization method.
66
In LCAO basis, wavefunction can be read to calculate initial charge density. The wavefunction itself does not have to be initialized.
77

88
## Charge Density
99
`init_chg` is used for choosing the method of charge density initialization.
10-
- `atomic` : initial charge density by atomic charge density from pseudopotential file under keyword `PP_RHOATOM`
10+
- `atomic` : initial charge density by atomic charge density from pseudopotential file under keyword `PP_RHOATOM`
1111
- `file` : initial charge density from files produced by previous calculations with [`out_chg 1`](../elec_properties/charge.md).
1212
- `auto`: Abacus first attempts to read the density from a file; if not found, it defaults to using atomic density.
13+
- `dm` (LCAO only): initial charge density from density matrix files in CSR format. For `nspin=1`, reads `dmrs1_nao.csr`. For `nspin=2` (spin-polarized), reads both `dmrs1_nao.csr` (spin-up) and `dmrs2_nao.csr` (spin-down). These files are generated by previous calculations with [`out_dmr 1`](../elec_properties/density_matrix.md). This method is particularly useful for restarting spin-polarized calculations.
14+
- `hr` (LCAO only): initial charge density from Hamiltonian matrix files in CSR format. The Hamiltonian is read from file, then diagonalized to obtain wavefunctions and charge density. For `nspin=1`, reads `hrs1_nao.csr`. For `nspin=2` (spin-polarized), reads both `hrs1_nao.csr` (spin-up) and `hrs2_nao.csr` (spin-down). These files are generated by previous calculations with [`out_mat_hs2 1`](../input_files/input-main.md).
1315

1416
## Wave function
1517
`init_wfc` is used for choosing the method of wavefunction coefficient initialization.

source/source_esolver/esolver_ks_lcao.cpp

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -170,31 +170,21 @@ void ESolver_KS_LCAO<TK, TR>::before_scf(UnitCell& ucell, const int istep)
170170
this->dmat.dm->init_DMR(*hamilt_lcao->getHR());
171171

172172
// 13.1) decide the strategy for initializing DMR and HR
173-
if(istep == 0)//if the first scf step, readin DMR from file,
173+
if(istep == 0)//if the first scf step, readin DMR from file,
174174
{
175175
//calculate or readin the density matrix DMR
176176
if(PARAM.inp.init_chg == "dm")
177177
{
178-
//! 13.1.1) init density matrix from file
179-
std::string dmfile = PARAM.globalv.global_readin_dir + "dmrs1_nao.csr";
180-
LCAO_domain::init_dm_from_file<TK>(dmfile, this->dmat, ucell, &(this->pv));
181-
GlobalV::ofs_running << " Read density matrix (real space) from file: "
182-
<< dmfile << std::endl;
178+
//! 13.1.1) init charge density from density matrix file
179+
LCAO_domain::init_chg_dm<TK>(PARAM.globalv.global_readin_dir, PARAM.inp.nspin,
180+
this->dmat, ucell, &(this->pv), this->pelec->charge);
183181
}
184182
if(PARAM.inp.init_chg == "hr")
185183
{
186-
//! 13.1.2) init HR from file
187-
std::string hrfile = PARAM.globalv.global_readin_dir + "hrs1_nao.csr";
188-
LCAO_domain::init_hr_from_file<TR>(
189-
hrfile,
190-
dynamic_cast<hamilt::HamiltLCAO<TK, TR>*>(this->p_hamilt)->getHR(),
191-
ucell,
192-
&(this->pv)
193-
);
194-
this->p_hamilt->refresh(false);
195-
hsolver::HSolverLCAO<TK> hsolver_lcao_obj(&(this->pv), PARAM.inp.ks_solver);
196-
hsolver_lcao_obj.solve(this->p_hamilt, this->psi[0], this->pelec, *this->dmat.dm,
197-
this->chr, PARAM.inp.nspin, 0);
184+
//! 13.1.2) init charge density from Hamiltonian matrix file
185+
LCAO_domain::init_chg_hr<TK, TR>(PARAM.globalv.global_readin_dir, PARAM.inp.nspin,
186+
this->p_hamilt, ucell, &(this->pv), this->psi[0], this->pelec, *this->dmat.dm,
187+
this->chr, PARAM.inp.ks_solver);
198188
}
199189
}
200190
else //if not, use the DMR calculated from last step
@@ -204,11 +194,6 @@ void ESolver_KS_LCAO<TK, TR>::before_scf(UnitCell& ucell, const int istep)
204194
// 2. DMK in DensityMatrix is empty (istep == 0), then DMR is initialized by zeros
205195
this->dmat.dm->cal_DMR();
206196
}
207-
// 13.2 if init_chg = "dm", then calculate rho from readin DMR before init_scf
208-
if(PARAM.inp.init_chg == "dm")
209-
{
210-
LCAO_domain::dm2rho(this->dmat.dm->get_DMR_vector(), PARAM.inp.nspin, this->pelec->charge, true);
211-
}
212197
// 13.2) init_scf, should be before_scf? mohan add 2025-03-10
213198
this->pelec->init_scf(ucell, this->Pgrid, this->sf.strucFac, this->locpp.numeric, ucell.symm);
214199

source/source_lcao/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ if(ENABLE_LCAO)
6464
add_coverage(hamilt_lcao)
6565
endif()
6666

67+
IF (BUILD_TESTING)
68+
add_subdirectory(test)
69+
endif()
70+
6771
endif()
6872

6973

source/source_lcao/LCAO_set.cpp

Lines changed: 142 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include "source_io/module_wf/read_wfc_nao.h" // use read_wfc_nao
55
#include "source_estate/elecstate_tools.h" // use fixed_weights
66
#include "source_lcao/module_hcontainer/read_hcontainer.h"
7+
#include "source_lcao/rho_tau_lcao.h" // use dm2rho
8+
#include "source_lcao/hamilt_lcao.h" // use HamiltLCAO for init_chg_hr
9+
#include "source_hsolver/hsolver_lcao.h" // use HSolverLCAO for init_chg_hr
710

811
template <typename TK>
912
void LCAO_domain::set_psi_occ_dm_chg(
@@ -94,20 +97,46 @@ void LCAO_domain::set_pot(
9497

9598
template <typename TK>
9699
void LCAO_domain::init_dm_from_file(
97-
const std::string dmfile,
100+
const std::string& readin_dir,
101+
const int nspin,
98102
LCAO_domain::Setup_DM<TK>& dmat,
99103
const UnitCell& ucell,
100104
const Parallel_Orbitals* pv)
101105
{
102106
ModuleBase::TITLE("LCAO_domain", "init_dm_from_file");
103-
hamilt::HContainer<double>* dm_container = dmat.dm->get_DMR_vector()[0];
104-
hamilt::Read_HContainer<double> reader_dm(
105-
dm_container,
106-
dmfile,
107-
PARAM.globalv.nlocal,
108-
&ucell
109-
);
110-
reader_dm.read();
107+
const int nspin_dm = (nspin == 2) ? 2 : 1;
108+
for (int is = 0; is < nspin_dm; ++is)
109+
{
110+
const std::string dmfile = readin_dir + "/dmrs" + std::to_string(is + 1) + "_nao.csr";
111+
hamilt::HContainer<double>* dm_container = dmat.dm->get_DMR_vector()[is];
112+
hamilt::Read_HContainer<double> reader_dm(
113+
dm_container,
114+
dmfile,
115+
PARAM.globalv.nlocal,
116+
&ucell
117+
);
118+
reader_dm.read();
119+
}
120+
return;
121+
}
122+
123+
template <typename TK>
124+
void LCAO_domain::init_chg_dm(
125+
const std::string& readin_dir,
126+
const int nspin,
127+
LCAO_domain::Setup_DM<TK>& dmat,
128+
const UnitCell& ucell,
129+
const Parallel_Orbitals* pv,
130+
Charge* chr)
131+
{
132+
ModuleBase::TITLE("LCAO_domain", "init_chg_dm");
133+
134+
// Step 1: Read density matrix from file
135+
LCAO_domain::init_dm_from_file<TK>(readin_dir, nspin, dmat, ucell, pv);
136+
137+
// Step 2: Convert density matrix to charge density
138+
LCAO_domain::dm2rho(dmat.dm->get_DMR_vector(), nspin, chr, true);
139+
111140
return;
112141
}
113142

@@ -130,6 +159,57 @@ void LCAO_domain::init_hr_from_file(
130159
return;
131160
}
132161

162+
template <typename TK, typename TR>
163+
void LCAO_domain::init_chg_hr(
164+
const std::string& readin_dir,
165+
const int nspin,
166+
hamilt::Hamilt<TK>* p_hamilt,
167+
const UnitCell& ucell,
168+
const Parallel_Orbitals* pv,
169+
psi::Psi<TK>& psi,
170+
elecstate::ElecState* pelec,
171+
elecstate::DensityMatrix<TK, double>& dm,
172+
Charge& chr,
173+
const std::string& ks_solver)
174+
{
175+
ModuleBase::TITLE("LCAO_domain", "init_chg_hr");
176+
177+
auto* hamilt_lcao = dynamic_cast<hamilt::HamiltLCAO<TK, TR>*>(p_hamilt);
178+
if (!hamilt_lcao)
179+
{
180+
ModuleBase::WARNING_QUIT("LCAO_domain::init_chg_hr", "p_hamilt is not HamiltLCAO");
181+
}
182+
183+
// Step 1: Read HR from file(s)
184+
if (nspin == 2)
185+
{
186+
// nspin=2: load spin-up into first half of hRS2, spin-down into second half
187+
const std::string hrfile_up = readin_dir + "/hrs1_nao.csr";
188+
LCAO_domain::init_hr_from_file<TR>(hrfile_up, hamilt_lcao->getHR(), ucell, pv);
189+
190+
// switch hR data pointer to spin-down half, then read hrs2
191+
auto& hRS2 = hamilt_lcao->getHRS2();
192+
hamilt_lcao->getHR()->allocate(hRS2.data() + hRS2.size() / 2, 0);
193+
const std::string hrfile_down = readin_dir + "/hrs2_nao.csr";
194+
LCAO_domain::init_hr_from_file<TR>(hrfile_down, hamilt_lcao->getHR(), ucell, pv);
195+
196+
// restore hR to spin-up half
197+
hamilt_lcao->getHR()->allocate(hRS2.data(), 0);
198+
}
199+
else
200+
{
201+
const std::string hrfile = readin_dir + "/hrs1_nao.csr";
202+
LCAO_domain::init_hr_from_file<TR>(hrfile, hamilt_lcao->getHR(), ucell, pv);
203+
}
204+
205+
// Step 2: Mark HR as loaded from file (skip operator recalculation)
206+
p_hamilt->refresh(false);
207+
208+
// Step 3: Diagonalize to get wavefunctions and charge density
209+
hsolver::HSolverLCAO<TK> hsolver_lcao_obj(pv, ks_solver);
210+
hsolver_lcao_obj.solve(p_hamilt, psi, pelec, dm, chr, nspin, 0);
211+
}
212+
133213

134214

135215
template void LCAO_domain::set_psi_occ_dm_chg<double>(
@@ -183,16 +263,33 @@ template void LCAO_domain::set_pot<std::complex<double>>(
183263
const Input_para &inp);
184264

185265
template void LCAO_domain::init_dm_from_file<double>(
186-
const std::string dmfile,
266+
const std::string& readin_dir,
267+
const int nspin,
187268
LCAO_domain::Setup_DM<double>& dmat,
188269
const UnitCell& ucell,
189270
const Parallel_Orbitals* pv);
190271
template void LCAO_domain::init_dm_from_file<std::complex<double>>(
191-
const std::string dmfile,
272+
const std::string& readin_dir,
273+
const int nspin,
192274
LCAO_domain::Setup_DM<std::complex<double>>& dmat,
193275
const UnitCell& ucell,
194276
const Parallel_Orbitals* pv);
195277

278+
template void LCAO_domain::init_chg_dm<double>(
279+
const std::string& readin_dir,
280+
const int nspin,
281+
LCAO_domain::Setup_DM<double>& dmat,
282+
const UnitCell& ucell,
283+
const Parallel_Orbitals* pv,
284+
Charge* chr);
285+
template void LCAO_domain::init_chg_dm<std::complex<double>>(
286+
const std::string& readin_dir,
287+
const int nspin,
288+
LCAO_domain::Setup_DM<std::complex<double>>& dmat,
289+
const UnitCell& ucell,
290+
const Parallel_Orbitals* pv,
291+
Charge* chr);
292+
196293
template void LCAO_domain::init_hr_from_file<double>(
197294
const std::string hrfile,
198295
hamilt::HContainer<double>* hmat,
@@ -203,3 +300,37 @@ template void LCAO_domain::init_hr_from_file<std::complex<double>>(
203300
hamilt::HContainer<std::complex<double>>* hmat,
204301
const UnitCell& ucell,
205302
const Parallel_Orbitals* pv);
303+
304+
template void LCAO_domain::init_chg_hr<double, double>(
305+
const std::string& readin_dir,
306+
const int nspin,
307+
hamilt::Hamilt<double>* p_hamilt,
308+
const UnitCell& ucell,
309+
const Parallel_Orbitals* pv,
310+
psi::Psi<double>& psi,
311+
elecstate::ElecState* pelec,
312+
elecstate::DensityMatrix<double, double>& dm,
313+
Charge& chr,
314+
const std::string& ks_solver);
315+
template void LCAO_domain::init_chg_hr<std::complex<double>, double>(
316+
const std::string& readin_dir,
317+
const int nspin,
318+
hamilt::Hamilt<std::complex<double>>* p_hamilt,
319+
const UnitCell& ucell,
320+
const Parallel_Orbitals* pv,
321+
psi::Psi<std::complex<double>>& psi,
322+
elecstate::ElecState* pelec,
323+
elecstate::DensityMatrix<std::complex<double>, double>& dm,
324+
Charge& chr,
325+
const std::string& ks_solver);
326+
template void LCAO_domain::init_chg_hr<std::complex<double>, std::complex<double>>(
327+
const std::string& readin_dir,
328+
const int nspin,
329+
hamilt::Hamilt<std::complex<double>>* p_hamilt,
330+
const UnitCell& ucell,
331+
const Parallel_Orbitals* pv,
332+
psi::Psi<std::complex<double>>& psi,
333+
elecstate::ElecState* pelec,
334+
elecstate::DensityMatrix<std::complex<double>, double>& dm,
335+
Charge& chr,
336+
const std::string& ks_solver);

0 commit comments

Comments
 (0)