diff --git a/docs/advanced/elec_properties/wfc.md b/docs/advanced/elec_properties/wfc.md index dc96eb88844..ef43e834c7a 100644 --- a/docs/advanced/elec_properties/wfc.md +++ b/docs/advanced/elec_properties/wfc.md @@ -7,12 +7,14 @@ ABACUS is able to output electron wave functions in both PW and LCAO basis calcu To output wave functions in G-space, add one of the following keywords to the `INPUT` file while performing SCF calculation: - **PW basis**: Set [`out_wfc_pw`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#out-wfc-pw) to `1`. Output file format: `wfs[spin]k[kpoint]_pw.txt`, where `[spin]` is the spin channel index, and `[kpoint]` the k-point index. -- **LCAO basis**: Set [`out_wfc_lcao`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#out-wfc-lcao) to `1`. - - **Multi-k calculations**: Generates multiple files `wfs[spin]k[kpoint]_nao.txt`. - - **Gamma-only calculations**: `wfs[spin]_nao.txt` instead. +- **LCAO basis**: Set [`out_wfc_lcao`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#out-wfc-lcao) to `1` for text or `2` for binary output. + - **Multi-k calculations**: Generates multiple files `wfs[spin]k[kpoint]_nao.txt` or `.dat`. + - **Gamma-only calculations**: Generates `wfs[spin]_nao.txt` or `.dat` instead. + +To reuse LCAO coefficients, set `init_wfc file`, point `read_file_dir` to the coefficient files, and set `read_wfc_lcao` to `1` for text or `2` for binary input. The selected extension is required; there is no automatic format fallback. ## Wave Function in Real Space One can also choose to output real-space wave functions with the keyword [`out_wfc_norm`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#out-wfc-norm) or [`out_wfc_re_im`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#out-wfc-re-im). -Notice: When the [`basis_type`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#basis-type) is `lcao`, only `get_wf` [`calculation`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#calculation) is effective. An example is [examples/11_wfc/lcao_ienvelope_Si2](https://github.com/deepmodeling/abacus-develop/tree/develop/examples/11_wfc/lcao_ienvelope_Si2). \ No newline at end of file +Notice: When the [`basis_type`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#basis-type) is `lcao`, only `get_wf` [`calculation`](https://abacus-rtd.readthedocs.io/en/latest/advanced/input_files/input-main.html#calculation) is effective. An example is [examples/11_wfc/lcao_ienvelope_Si2](https://github.com/deepmodeling/abacus-develop/tree/develop/examples/11_wfc/lcao_ienvelope_Si2). diff --git a/docs/advanced/input_files/input-main.md b/docs/advanced/input_files/input-main.md index e4b44876ca4..9cd943b7cff 100644 --- a/docs/advanced/input_files/input-main.md +++ b/docs/advanced/input_files/input-main.md @@ -19,6 +19,7 @@ - [latname](#latname) - [assume\_isolated](#assume_isolated) - [init\_wfc](#init_wfc) + - [read\_wfc\_lcao](#read_wfc_lcao) - [init\_chg](#init_chg) - [init\_vel](#init_vel) - [mem\_saver](#mem_saver) @@ -737,7 +738,7 @@ - atomic: from atomic pseudo wave functions. If they are not enough, other wave functions are initialized with random numbers. - atomic+random: add small random numbers on atomic pseudo-wavefunctions - - file: from binary files wf*.dat, which are output by setting out_wfc_pw to 2. + - file: from wave function coefficient files. For PW calculations, binary `wf*.dat` files are generated by setting `out_wfc_pw` to 2. For LCAO calculations, the text or binary format is selected by `read_wfc_lcao` and generated by setting `out_wfc_lcao` to the corresponding value. - random: random numbers - nao: from numerical atomic orbitals. If they are not enough, other wave functions are initialized with random numbers. - nao+random: add small random numbers on numerical atomic orbitals @@ -745,6 +746,20 @@ > Note: Only the file option is useful for the lcao basis set, which is mostly used when calculation is set to get_wf and get_pchg. - **Default**: atomic +### read_wfc_lcao + +- **Type**: Integer +- **Availability**: *[`basis_type`](#basis_type)==lcao* +- **Description**: The file format used when reading LCAO wave function coefficients. + + Available options are: + + - 1: text files (`.txt`) generated by setting `out_wfc_lcao` to 1. + - 2: binary files (`.dat`) generated by setting `out_wfc_lcao` to 2. + + The selected format is used directly; ABACUS does not automatically detect or fall back to the other format. Only independent wave function files are supported, not files accumulated with `out_app_flag`. +- **Default**: 1 + ### init_chg - **Type**: String diff --git a/docs/advanced/scf/initialization.md b/docs/advanced/scf/initialization.md index c01d1f2bb8a..bead5975127 100644 --- a/docs/advanced/scf/initialization.md +++ b/docs/advanced/scf/initialization.md @@ -21,4 +21,4 @@ Atomic wave function is read from pseudopotential file under keyword `PP_PSWFC`, When `basis_type=lcao`, we further support reading of initial wavefunction by setting `init_wfc` to `file`. In LCAO code, wave function is used to initialize density matrix and real-space charge density. -For such purpose, a file containing wavefunction must be prepared. Such files can be generated from previous calculations with [`out_wfc_lcao 1`](../elec_properties/wfc.md). +For such purpose, files containing wavefunction coefficients must be prepared. They can be generated by a previous calculation with [`out_wfc_lcao 1`](../elec_properties/wfc.md) for text files or `out_wfc_lcao 2` for binary files. Set `read_wfc_lcao` to the same value when reading them; ABACUS does not automatically switch between formats. diff --git a/docs/parameters.yaml b/docs/parameters.yaml index 2df3f8fc18d..b792e87458c 100644 --- a/docs/parameters.yaml +++ b/docs/parameters.yaml @@ -168,7 +168,7 @@ parameters: Available options are: * atomic: from atomic pseudo wave functions. If they are not enough, other wave functions are initialized with random numbers. * atomic+random: add small random numbers on atomic pseudo-wavefunctions - * file: from binary files wf*.dat, which are output by setting out_wfc_pw to 2. + * file: from wave function coefficient files. For PW calculations, binary `wf*.dat` files are generated by setting `out_wfc_pw` to 2. For LCAO calculations, the text or binary format is selected by `read_wfc_lcao` and generated by setting `out_wfc_lcao` to the corresponding value. * random: random numbers * nao: from numerical atomic orbitals. If they are not enough, other wave functions are initialized with random numbers. * nao+random: add small random numbers on numerical atomic orbitals @@ -177,6 +177,20 @@ parameters: default_value: atomic unit: "" availability: "" + - name: read_wfc_lcao + category: System variables + type: Integer + description: | + The file format used when reading LCAO wave function coefficients. + + Available options are: + * 1: text files (`.txt`) generated by setting `out_wfc_lcao` to 1. + * 2: binary files (`.dat`) generated by setting `out_wfc_lcao` to 2. + + The selected format is used directly; ABACUS does not automatically detect or fall back to the other format. Only independent wave function files are supported, not files accumulated with `out_app_flag`. + default_value: "1" + unit: "" + availability: basis_type==lcao - name: init_chg category: System variables type: String diff --git a/source/source_esolver/esolver_ks_lcao_tddft.cpp b/source/source_esolver/esolver_ks_lcao_tddft.cpp index f767c4b2107..f7f9ca0f560 100644 --- a/source/source_esolver/esolver_ks_lcao_tddft.cpp +++ b/source/source_esolver/esolver_ks_lcao_tddft.cpp @@ -114,6 +114,7 @@ void ESolver_KS_LCAO_TDDFT::before_all_runners(BaseCell& basecell, c this->kv.ik2iktot, this->kv.get_nkstot(), this->inp_->nspin, + this->inp_->read_wfc_lcao, 0, TD_info::estep_shift)) { diff --git a/source/source_esolver/esolver_lr_lcao_tddft.cpp b/source/source_esolver/esolver_lr_lcao_tddft.cpp index 52bd6ad0e89..fbfd735ace3 100644 --- a/source/source_esolver/esolver_lr_lcao_tddft.cpp +++ b/source/source_esolver/esolver_lr_lcao_tddft.cpp @@ -805,6 +805,7 @@ void ModuleESolver::ESolver_LR::read_ks_wfc() this->kv.ik2iktot, this->kv.get_nkstot(), this->inp_->nspin, + this->inp_->read_wfc_lcao, /*skip_bands=*/this->nocc_max - this->nocc_in)) { ModuleBase::WARNING_QUIT("ESolver_LR", "read ground-state wavefunction failed."); } diff --git a/source/source_io/module_parameter/input_parameter.h b/source/source_io/module_parameter/input_parameter.h index 196b11fad9f..7b0ed8cf0e2 100644 --- a/source/source_io/module_parameter/input_parameter.h +++ b/source/source_io/module_parameter/input_parameter.h @@ -46,6 +46,7 @@ struct Input_para double erf_sigma = 0.1; ///< the width of the energy step for reciprocal vectors int fft_mode = 0; ///< fftw mode 0: estimate, 1: measure, 2: patient, 3: exhaustive std::string init_wfc = "atomic"; ///< "file","atomic","random" + int read_wfc_lcao = 1; ///< LCAO wavefunction input format: 1 text, 2 binary int pw_seed = 0; ///< random seed for initializing wave functions std::string init_chg = "atomic"; ///< "file","atomic" bool dm_to_rho = false; ///< read density matrix from npz format and calculate charge density diff --git a/source/source_io/module_parameter/read_inp_sys.cpp b/source/source_io/module_parameter/read_inp_sys.cpp index 3c9bff3935a..03037d0fb12 100644 --- a/source/source_io/module_parameter/read_inp_sys.cpp +++ b/source/source_io/module_parameter/read_inp_sys.cpp @@ -448,7 +448,7 @@ Theory: G. Makov and M. C. Payne, Phys. Rev. B 51, 4014 (1995).)"; Available options are: * atomic: from atomic pseudo wave functions. If they are not enough, other wave functions are initialized with random numbers. * atomic+random: add small random numbers on atomic pseudo-wavefunctions -* file: from binary files wf*.dat, which are output by setting out_wfc_pw to 2. +* file: from wave function coefficient files. For PW calculations, binary `wf*.dat` files are generated by setting `out_wfc_pw` to 2. For LCAO calculations, the text or binary format is selected by `read_wfc_lcao` and generated by setting `out_wfc_lcao` to the corresponding value. * random: random numbers * nao: from numerical atomic orbitals. If they are not enough, other wave functions are initialized with random numbers. * nao+random: add small random numbers on numerical atomic orbitals @@ -474,6 +474,30 @@ Available options are: read_sync_string(input.init_wfc); this->add_item(item); } + { + Input_Item item("read_wfc_lcao"); + item.annotation = "LCAO wave function input format: 1 text, 2 binary"; + item.category = "System variables"; + item.type = "Integer"; + item.description = R"(The file format used when reading LCAO wave function coefficients. + +Available options are: +* 1: text files (`.txt`) generated by setting `out_wfc_lcao` to 1. +* 2: binary files (`.dat`) generated by setting `out_wfc_lcao` to 2. + +The selected format is used directly; ABACUS does not automatically detect or fall back to the other format. Only independent wave function files are supported, not files accumulated with `out_app_flag`.)"; + item.default_value = "1"; + item.unit = ""; + item.set_availability("basis_type==lcao"); + read_sync_int(input.read_wfc_lcao); + item.check_value = [](const Input_Item& item, const Parameter& para) { + if (para.input.read_wfc_lcao != 1 && para.input.read_wfc_lcao != 2) + { + ModuleBase::WARNING_QUIT("ReadInput", "read_wfc_lcao should be 1 or 2"); + } + }; + this->add_item(item); + } { Input_Item item("init_chg"); item.annotation = "start charge is from 'atomic' or file"; diff --git a/source/source_io/module_wf/read_wfc_nao.cpp b/source/source_io/module_wf/read_wfc_nao.cpp index 662b39778d2..1e318bb2922 100644 --- a/source/source_io/module_wf/read_wfc_nao.cpp +++ b/source/source_io/module_wf/read_wfc_nao.cpp @@ -2,14 +2,76 @@ #include "source_base/parallel_common.h" #include "source_base/timer.h" -#include "source_io/module_wf/write_wfc_nao.h" - -#include "source_io/module_wf/write_wfc_nao.h" #include "source_base/module_external/scalapack_connector.h" #include "source_base/module_out/filename.h" #include "source_base/tool_title.h" // use title #include "source_base/global_function.h" // use READ_VALUE +#include + +namespace +{ + +template +bool read_binary_value(std::ifstream& ifs, T& value) +{ + ifs.read(reinterpret_cast(&value), sizeof(T)); + return static_cast(ifs); +} + +template +bool read_record_value(std::ifstream& ifs, T& value, const bool binary) +{ + if (binary) + { + return read_binary_value(ifs, value); + } + ModuleBase::GlobalFunc::READ_VALUE(ifs, value); + return static_cast(ifs); +} + +bool read_binary_wfc_data(std::ifstream& ifs, double& data) +{ + return read_binary_value(ifs, data); +} + +bool read_binary_wfc_data(std::ifstream& ifs, float& data) +{ + double value = 0.0; + if (!read_binary_value(ifs, value)) + { + return false; + } + data = static_cast(value); + return true; +} + +bool read_binary_wfc_data(std::ifstream& ifs, std::complex& data) +{ + double real = 0.0; + double imag = 0.0; + if (!read_binary_value(ifs, real) || !read_binary_value(ifs, imag)) + { + return false; + } + data = std::complex(real, imag); + return true; +} + +bool read_binary_wfc_data(std::ifstream& ifs, std::complex& data) +{ + double real = 0.0; + double imag = 0.0; + if (!read_binary_value(ifs, real) || !read_binary_value(ifs, imag)) + { + return false; + } + data = std::complex(static_cast(real), static_cast(imag)); + return true; +} + +} // namespace + // mohan add 2025-10-19 void ModuleIO::read_wfc_nao_one_data(std::ifstream& ifs, float& data) { @@ -47,6 +109,7 @@ bool ModuleIO::read_wfc_nao( const std::vector &ik2iktot, const int nkstot, const int nspin, + const int read_type, const int skip_band, const int istep) { @@ -55,10 +118,34 @@ bool ModuleIO::read_wfc_nao( const int nk = ekb.nr; - const bool gamma_only = std::is_same::value; - const int out_type = 1; // only support .txt file now + const bool gamma_only = std::is_same::value || std::is_same::value; + const bool binary = (read_type == 2); bool read_success = true; int myrank = 0; +#ifdef __MPI + MPI_Comm_rank(ParaV.comm(), &myrank); +#endif + if (read_type != 1 && read_type != 2) + { + if (myrank == 0) + { + std::cout << " Error in reading wave function files!\n" + << " read_wfc_lcao should be 1 or 2, but got " << read_type << std::endl; + } + ModuleBase::timer::end("ModuleIO", "read_wfc_nao"); + return false; + } + if (skip_band < 0) + { + if (myrank == 0) + { + std::cout << " Error in reading wave function files!\n" + << " skip_band should not be negative, but got " << skip_band << std::endl; + } + ModuleBase::timer::end("ModuleIO", "read_wfc_nao"); + return false; + } + int nbands = ParaV.get_wfc_global_nbands(); // the global number of bands int nlocal = ParaV.get_wfc_global_nbasis(); // the global number of basis functions int nbands_local = ParaV.ncol_bands; // the number of bands in the local process @@ -72,10 +159,6 @@ bool ModuleIO::read_wfc_nao( } psid.resize(nk, nbands_local, nlocal_local); -#ifdef __MPI - MPI_Comm_rank(ParaV.comm(), &myrank); -#endif - // lambda function to read one file auto read_one_file = [&](const std::string& ss, std::stringstream& error_message, @@ -83,7 +166,9 @@ bool ModuleIO::read_wfc_nao( std::vector& ctot) { std::ifstream ifs; - ifs.open(ss.c_str()); + const std::ios_base::openmode mode + = binary ? (std::ios::in | std::ios::binary) : std::ios::in; + ifs.open(ss.c_str(), mode); if (!ifs) { error_message << " Can't open file:" << ss << std::endl; @@ -94,14 +179,39 @@ bool ModuleIO::read_wfc_nao( std::cout << " Read NAO wave functions from " << ss << std::endl; } + const auto incomplete_file = [&](const std::string& field) { + error_message << "The wave function file is incomplete or corrupted while reading " + << field << ": " << ss << std::endl; + ifs.close(); + return false; + }; + if (!gamma_only) { int ik_file = 0; double kx = 0.0; double ky = 0.0; double kz = 0.0; - ModuleBase::GlobalFunc::READ_VALUE(ifs, ik_file); - ifs >> kx >> ky >> kz; + if (!read_record_value(ifs, ik_file, binary)) + { + return incomplete_file("the k-point index"); + } + if (binary) + { + if (!read_binary_value(ifs, kx) || !read_binary_value(ifs, ky) + || !read_binary_value(ifs, kz)) + { + return incomplete_file("the k-point vector"); + } + } + else + { + ifs >> kx >> ky >> kz; + if (!ifs) + { + return incomplete_file("the k-point vector"); + } + } if (ik_file != ik + 1) { error_message << "The k index read in from file do not match the k index generated by ABACUS!\n"; @@ -112,12 +222,16 @@ bool ModuleIO::read_wfc_nao( } } int nbands_file = 0, nlocal_file = 0; - ModuleBase::GlobalFunc::READ_VALUE(ifs, nbands_file); - ModuleBase::GlobalFunc::READ_VALUE(ifs, nlocal_file); - if (nbands > nbands_file) + if (!read_record_value(ifs, nbands_file, binary) + || !read_record_value(ifs, nlocal_file, binary)) + { + return incomplete_file("the dimensions"); + } + if (nbands_file < 0 || skip_band > nbands_file || nbands > nbands_file - skip_band) { error_message << "The number of bands to be read exceeds the number of bands in the file generated by ABACUS!\n"; error_message << " nbands in the existing file=" << nbands_file; + error_message << " skip_band=" << skip_band; error_message << " nbands to be read into ABACUS=" << nbands << std::endl; ifs.close(); return false; @@ -135,9 +249,12 @@ bool ModuleIO::read_wfc_nao( // the first skip_bands useless bands are read into 0th band to be overwritten const int ib_read = std::max(i - skip_band, 0); int ib = 0; - ModuleBase::GlobalFunc::READ_VALUE(ifs, ib); - ModuleBase::GlobalFunc::READ_VALUE(ifs, ekb(ik, ib_read)); - ModuleBase::GlobalFunc::READ_VALUE(ifs, wg(ik, ib_read)); + if (!read_record_value(ifs, ib, binary) + || !read_record_value(ifs, ekb(ik, ib_read), binary) + || !read_record_value(ifs, wg(ik, ib_read), binary)) + { + return incomplete_file("band " + std::to_string(i + 1) + " metadata"); + } if (i+1 != ib) { error_message << "The band index read in from file do not match the global parameter band index!\n"; @@ -148,7 +265,20 @@ bool ModuleIO::read_wfc_nao( } for (int j = 0; j < nlocal; j++) { - read_wfc_nao_one_data(ifs, ctot[ib_read * nlocal + j]); + bool data_read = false; + if (binary) + { + data_read = read_binary_wfc_data(ifs, ctot[ib_read * nlocal + j]); + } + else + { + read_wfc_nao_one_data(ifs, ctot[ib_read * nlocal + j]); + data_read = static_cast(ifs); + } + if (!data_read) + { + return incomplete_file("band " + std::to_string(i + 1) + " coefficients"); + } } } ifs.close(); @@ -180,7 +310,7 @@ bool ModuleIO::read_wfc_nao( readin_dir = readin_dir + "WFC/"; } std::string ss = ModuleIO::filename_output(readin_dir,"wf","nao", - ik,ik2iktot,nspin,nkstot,out_type,out_app_flag,gamma_only,istep); + ik,ik2iktot,nspin,nkstot,read_type,out_app_flag,gamma_only,istep); read_success = read_one_file(ss, error_message, ik, ctot); errors = error_message.str(); @@ -231,8 +361,9 @@ template bool ModuleIO::read_wfc_nao(const std::string& global_readin_di const std::vector &ik2iktot, const int nkstot, const int nspin, - const int istep, - const int skip_band); + const int read_type, + const int skip_band, + const int istep); // mohan add 2025-10-19 template bool ModuleIO::read_wfc_nao(const std::string& global_readin_dir, @@ -243,8 +374,9 @@ template bool ModuleIO::read_wfc_nao(const std::string& global_readin_dir const std::vector &ik2iktot, const int nkstot, const int nspin, - const int istep, - const int skip_band); + const int read_type, + const int skip_band, + const int istep); template bool ModuleIO::read_wfc_nao>(const std::string& global_readin_dir, const Parallel_Orbitals& ParaV, @@ -254,8 +386,9 @@ template bool ModuleIO::read_wfc_nao>(const std::string& gl const std::vector &ik2iktot, const int nkstot, const int nspin, - const int istep, - const int skip_band); + const int read_type, + const int skip_band, + const int istep); // mohan add 2025-10-19 template bool ModuleIO::read_wfc_nao>(const std::string& global_readin_dir, @@ -266,5 +399,6 @@ template bool ModuleIO::read_wfc_nao>(const std::string& glo const std::vector &ik2iktot, const int nkstot, const int nspin, - const int istep, - const int skip_band); + const int read_type, + const int skip_band, + const int istep); diff --git a/source/source_io/module_wf/read_wfc_nao.h b/source/source_io/module_wf/read_wfc_nao.h index 4abf06ae007..80c27e33ef4 100644 --- a/source/source_io/module_wf/read_wfc_nao.h +++ b/source/source_io/module_wf/read_wfc_nao.h @@ -34,6 +34,7 @@ void read_wfc_nao_one_data(std::ifstream& ifs, std::complex& data); * @param ParaV The parallel orbitals object. * @param psid The Psi object to store the wavefunction coefficients. * @param pelec Pointer to the ElecState object. + * @param read_type The input file type: 1 for text and 2 for binary. * @param skip_band From which band to start reading. * @return True if the wavefunction coefficients are successfully read, false otherwise. */ @@ -47,6 +48,7 @@ bool read_wfc_nao( const std::vector &ik2iktot, const int nkstot, const int nspin, + const int read_type, const int skip_band = 0, const int istep = -1); diff --git a/source/source_io/test/CMakeLists.txt b/source/source_io/test/CMakeLists.txt index 4849f0a6621..0e31ae783ce 100644 --- a/source/source_io/test/CMakeLists.txt +++ b/source/source_io/test/CMakeLists.txt @@ -242,7 +242,7 @@ add_test( AddTest( TARGET MODULE_IO_read_wfc_nao_test LIBS parameter base device - SOURCES read_wfc_nao_test.cpp ../module_wf/read_wfc_nao.cpp ../../source_psi/psi.cpp ../../source_basis/module_ao/parallel_orbitals.cpp + SOURCES read_wfc_nao_test.cpp ../module_wf/read_wfc_nao.cpp ../module_wf/write_wfc_nao.cpp ../../source_psi/psi.cpp ../../source_basis/module_ao/parallel_orbitals.cpp ) add_test( diff --git a/source/source_io/test/read_wfc_nao_test.cpp b/source/source_io/test/read_wfc_nao_test.cpp index bf3c31adec1..71165948e4c 100644 --- a/source/source_io/test/read_wfc_nao_test.cpp +++ b/source/source_io/test/read_wfc_nao_test.cpp @@ -7,6 +7,9 @@ #include "source_basis/module_ao/parallel_orbitals.h" #include "source_io/module_wf/write_wfc_nao.h" #include "source_base/module_out/filename.h" +#include "source_base/global_function.h" + +#include /************************************************ * unit test of functions in read_wfc_nao.cpp @@ -23,6 +26,53 @@ class ReadWfcNaoTest : public ::testing::Test { protected: + int my_rank = 0; + int nproc = 1; + std::string binary_test_dir; + + void SetUp() override + { +#ifdef __MPI + MPI_Comm_rank(MPI_COMM_WORLD, &my_rank); + MPI_Comm_size(MPI_COMM_WORLD, &nproc); +#endif + binary_test_dir = "./read_wfc_nao_binary_np" + std::to_string(nproc) + "/"; + ModuleBase::GlobalFunc::MAKE_DIR(binary_test_dir); +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + } + + void TearDown() override + { +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + if (my_rank == 0) + { + std::remove((binary_test_dir + "wf_nao.dat").c_str()); + std::remove((binary_test_dir + "wf_nao.txt").c_str()); + std::remove((binary_test_dir + "wfk1_nao.dat").c_str()); + std::remove(binary_test_dir.substr(0, binary_test_dir.size() - 1).c_str()); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + } + + void initialize_parallel_orbitals(Parallel_Orbitals& para, const int nlocal, const int nbands) + { +#ifdef __MPI + std::ofstream ofs_running, ofs_warning; + para.init(nlocal, nlocal, 1, MPI_COMM_WORLD); + para.set_nloc_wfc_Eij(nbands, ofs_running, ofs_warning); + para.set_desc_wfc_Eij(nlocal, nbands, para.nrow); +#else + para.set_serial(nlocal, nlocal); + para.nrow_bands = nlocal; + para.ncol_bands = nbands; +#endif + } }; @@ -60,7 +110,7 @@ TEST_F(ReadWfcNaoTest,ReadWfcNao) // Act ModuleIO::read_wfc_nao(PARAM.sys.global_readin_dir, ParaV, psid, - ekb, wg, ik2iktot, nkstot, nspin); + ekb, wg, ik2iktot, nkstot, nspin, 1); // Assert EXPECT_NEAR(ekb(0,1),0.31482195194888534794941393,1e-5); EXPECT_NEAR(wg(0,1),0.0,1e-5); @@ -106,7 +156,7 @@ TEST_F(ReadWfcNaoTest, ReadWfcNaoPart) // Act ModuleIO::read_wfc_nao(PARAM.sys.global_readin_dir, ParaV, psid, - ekb, wg, ik2iktot, nkstot, nspin, skip_band, nstep); + ekb, wg, ik2iktot, nkstot, nspin, 1, skip_band, nstep); // Assert EXPECT_NEAR(ekb(0, 1), 7.4141254894954844445464914e-01, 1e-5); @@ -116,6 +166,294 @@ TEST_F(ReadWfcNaoTest, ReadWfcNaoPart) } } +TEST_F(ReadWfcNaoTest, ReadBinaryGamma) +{ + const int nbands = 2; + const int nlocal = 3; + Parallel_Orbitals para; + initialize_parallel_orbitals(para, nlocal, nbands); + + const std::vector coefficients = {0.1, 0.2, 0.3, 1.1, 1.2, 1.3}; + ModuleBase::matrix ekb_source(1, nbands); + ModuleBase::matrix wg_source(1, nbands); + ekb_source(0, 0) = -0.5; + ekb_source(0, 1) = 0.7; + wg_source(0, 0) = 2.0; + wg_source(0, 1) = 0.0; + if (my_rank == 0) + { + ModuleIO::wfc_nao_write2file(binary_test_dir + "wf_nao.dat", + coefficients.data(), + nlocal, + 0, + ekb_source, + wg_source, + true, + false); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + psi::Psi psi_read; + ModuleBase::matrix ekb(1, nbands); + ModuleBase::matrix wg(1, nbands); + const std::vector ik2iktot = {0}; + EXPECT_TRUE(ModuleIO::read_wfc_nao(binary_test_dir, + para, + psi_read, + ekb, + wg, + ik2iktot, + 1, + 1, + 2)); + EXPECT_DOUBLE_EQ(ekb(0, 0), ekb_source(0, 0)); + EXPECT_DOUBLE_EQ(ekb(0, 1), ekb_source(0, 1)); + EXPECT_DOUBLE_EQ(wg(0, 0), wg_source(0, 0)); + EXPECT_DOUBLE_EQ(wg(0, 1), wg_source(0, 1)); + if (my_rank == 0) + { + EXPECT_DOUBLE_EQ(psi_read(0, 0, 0), coefficients[0]); + } +} + +TEST_F(ReadWfcNaoTest, ReadBinaryGammaFloat) +{ + const int nbands = 2; + const int nlocal = 3; + Parallel_Orbitals para; + initialize_parallel_orbitals(para, nlocal, nbands); + + const std::vector coefficients = {0.1, 0.2, 0.3, 1.1, 1.2, 1.3}; + ModuleBase::matrix ekb_source(1, nbands); + ModuleBase::matrix wg_source(1, nbands); + ekb_source(0, 0) = -0.5; + ekb_source(0, 1) = 0.7; + wg_source(0, 0) = 2.0; + wg_source(0, 1) = 0.0; + if (my_rank == 0) + { + ModuleIO::wfc_nao_write2file(binary_test_dir + "wf_nao.dat", + coefficients.data(), + nlocal, + 0, + ekb_source, + wg_source, + true, + false); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + psi::Psi psi_read; + ModuleBase::matrix ekb(1, nbands); + ModuleBase::matrix wg(1, nbands); + const std::vector ik2iktot = {0}; + EXPECT_TRUE(ModuleIO::read_wfc_nao(binary_test_dir, + para, + psi_read, + ekb, + wg, + ik2iktot, + 1, + 1, + 2)); + EXPECT_DOUBLE_EQ(ekb(0, 0), ekb_source(0, 0)); + EXPECT_DOUBLE_EQ(ekb(0, 1), ekb_source(0, 1)); + EXPECT_DOUBLE_EQ(wg(0, 0), wg_source(0, 0)); + EXPECT_DOUBLE_EQ(wg(0, 1), wg_source(0, 1)); + if (my_rank == 0) + { + EXPECT_FLOAT_EQ(psi_read(0, 0, 0), static_cast(coefficients[0])); + } +} + +TEST_F(ReadWfcNaoTest, ReadBinaryComplex) +{ + const int nbands = 2; + const int nlocal = 3; + Parallel_Orbitals para; + initialize_parallel_orbitals(para, nlocal, nbands); + + const std::vector> coefficients + = {{0.1, -0.1}, {0.2, -0.2}, {0.3, -0.3}, {1.1, 0.4}, {1.2, 0.5}, {1.3, 0.6}}; + ModuleBase::matrix ekb_source(1, nbands); + ModuleBase::matrix wg_source(1, nbands); + ekb_source(0, 0) = -0.4; + ekb_source(0, 1) = 0.8; + wg_source(0, 0) = 1.0; + wg_source(0, 1) = 0.0; + if (my_rank == 0) + { + ModuleIO::wfc_nao_write2file_complex(binary_test_dir + "wfk1_nao.dat", + coefficients.data(), + nlocal, + 0, + ModuleBase::Vector3(0.25, 0.0, 0.0), + ekb_source, + wg_source, + true, + false); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + psi::Psi> psi_read; + ModuleBase::matrix ekb(1, nbands); + ModuleBase::matrix wg(1, nbands); + const std::vector ik2iktot = {0}; + EXPECT_TRUE(ModuleIO::read_wfc_nao(binary_test_dir, + para, + psi_read, + ekb, + wg, + ik2iktot, + 1, + 1, + 2)); + EXPECT_DOUBLE_EQ(ekb(0, 0), ekb_source(0, 0)); + EXPECT_DOUBLE_EQ(ekb(0, 1), ekb_source(0, 1)); + EXPECT_DOUBLE_EQ(wg(0, 0), wg_source(0, 0)); + EXPECT_DOUBLE_EQ(wg(0, 1), wg_source(0, 1)); + if (my_rank == 0) + { + EXPECT_DOUBLE_EQ(psi_read(0, 0, 0).real(), coefficients[0].real()); + EXPECT_DOUBLE_EQ(psi_read(0, 0, 0).imag(), coefficients[0].imag()); + } +} + +TEST_F(ReadWfcNaoTest, ReadBinaryPart) +{ + const int nbands_file = 3; + const int nbands = 2; + const int skip_band = 1; + const int nlocal = 3; + Parallel_Orbitals para; + initialize_parallel_orbitals(para, nlocal, nbands); + + const std::vector coefficients + = {0.1, 0.2, 0.3, 1.1, 1.2, 1.3, 2.1, 2.2, 2.3}; + ModuleBase::matrix ekb_source(1, nbands_file); + ModuleBase::matrix wg_source(1, nbands_file); + for (int ib = 0; ib < nbands_file; ++ib) + { + ekb_source(0, ib) = -0.5 + ib; + wg_source(0, ib) = 2.0 - ib; + } + if (my_rank == 0) + { + ModuleIO::wfc_nao_write2file(binary_test_dir + "wf_nao.dat", + coefficients.data(), + nlocal, + 0, + ekb_source, + wg_source, + true, + false); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + psi::Psi psi_read; + ModuleBase::matrix ekb(1, nbands); + ModuleBase::matrix wg(1, nbands); + const std::vector ik2iktot = {0}; + EXPECT_TRUE(ModuleIO::read_wfc_nao(binary_test_dir, + para, + psi_read, + ekb, + wg, + ik2iktot, + 1, + 1, + 2, + skip_band)); + EXPECT_DOUBLE_EQ(ekb(0, 0), ekb_source(0, 1)); + EXPECT_DOUBLE_EQ(ekb(0, 1), ekb_source(0, 2)); + EXPECT_DOUBLE_EQ(wg(0, 0), wg_source(0, 1)); + EXPECT_DOUBLE_EQ(wg(0, 1), wg_source(0, 2)); + if (my_rank == 0) + { + EXPECT_DOUBLE_EQ(psi_read(0, 0, 0), coefficients[nlocal]); + } +} + +TEST_F(ReadWfcNaoTest, BinaryDoesNotFallBackToText) +{ + const int nbands = 2; + const int nlocal = 2; + Parallel_Orbitals para; + initialize_parallel_orbitals(para, nlocal, nbands); + + const std::vector coefficients = {0.25, 0.5, 0.75, 1.0}; + ModuleBase::matrix ekb_source(1, nbands); + ModuleBase::matrix wg_source(1, nbands); + if (my_rank == 0) + { + ModuleIO::wfc_nao_write2file(binary_test_dir + "wf_nao.txt", + coefficients.data(), + nlocal, + 0, + ekb_source, + wg_source, + false, + false); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + psi::Psi psi_read; + ModuleBase::matrix ekb(1, nbands); + ModuleBase::matrix wg(1, nbands); + const std::vector ik2iktot = {0}; + EXPECT_FALSE(ModuleIO::read_wfc_nao(binary_test_dir, + para, + psi_read, + ekb, + wg, + ik2iktot, + 1, + 1, + 2)); +} + +TEST_F(ReadWfcNaoTest, RejectTruncatedBinary) +{ + const int nbands = 2; + const int nlocal = 2; + Parallel_Orbitals para; + initialize_parallel_orbitals(para, nlocal, nbands); + + if (my_rank == 0) + { + std::ofstream ofs(binary_test_dir + "wf_nao.dat", std::ios::binary); + ofs.write(reinterpret_cast(&nbands), sizeof(nbands)); + ofs.write(reinterpret_cast(&nlocal), sizeof(nlocal)); + } +#ifdef __MPI + MPI_Barrier(MPI_COMM_WORLD); +#endif + + psi::Psi psi_read; + ModuleBase::matrix ekb(1, nbands); + ModuleBase::matrix wg(1, nbands); + const std::vector ik2iktot = {0}; + EXPECT_FALSE(ModuleIO::read_wfc_nao(binary_test_dir, + para, + psi_read, + ekb, + wg, + ik2iktot, + 1, + 1, + 2)); +} + #ifdef __MPI diff --git a/source/source_io/test_serial/read_input_item_test.cpp b/source/source_io/test_serial/read_input_item_test.cpp index c5815f858ae..5e3d74a6500 100644 --- a/source/source_io/test_serial/read_input_item_test.cpp +++ b/source/source_io/test_serial/read_input_item_test.cpp @@ -373,6 +373,28 @@ TEST_F(InputTest, Item_test) it->second.reset_value(it->second, param); EXPECT_EQ(param.input.init_wfc, "nao"); } + { // read_wfc_lcao + auto it = find_label("read_wfc_lcao", readinput.input_lists); + EXPECT_NE(it, readinput.input_lists.end()); + EXPECT_EQ(param.input.read_wfc_lcao, 1); + EXPECT_EQ(it->second.get_availability(), "basis_type==lcao"); + + it->second.str_values = {"2"}; + it->second.read_value(it->second, param); + EXPECT_EQ(param.input.read_wfc_lcao, 2); + + param.input.read_wfc_lcao = 0; + testing::internal::CaptureStdout(); + EXPECT_EXIT(it->second.check_value(it->second, param), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, testing::HasSubstr("NOTICE")); + + param.input.read_wfc_lcao = 3; + testing::internal::CaptureStdout(); + EXPECT_EXIT(it->second.check_value(it->second, param), ::testing::ExitedWithCode(1), ""); + output = testing::internal::GetCapturedStdout(); + EXPECT_THAT(output, testing::HasSubstr("NOTICE")); + } { // init_chg auto it = find_label("init_chg", readinput.input_lists); param.input.init_chg = "get_pchg"; diff --git a/source/source_lcao/lcao_set.cpp b/source/source_lcao/lcao_set.cpp index dda20f80580..206409b0d8f 100644 --- a/source/source_lcao/lcao_set.cpp +++ b/source/source_lcao/lcao_set.cpp @@ -27,7 +27,7 @@ void LCAO_domain::set_psi_occ_dm_chg( { if (!ModuleIO::read_wfc_nao(PARAM.globalv.global_readin_dir, pv, *psi, pelec->ekb, pelec->wg, kv.ik2iktot, - kv.get_nkstot(), inp.nspin)) + kv.get_nkstot(), inp.nspin, inp.read_wfc_lcao)) { ModuleBase::WARNING_QUIT("set_psi_occ_dm_chg", "read electronic wave functions failed"); }