Skip to content
10 changes: 4 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,11 @@ set(ABACUS_BIN_PATH ${CMAKE_CURRENT_BINARY_DIR}/${ABACUS_BIN_NAME})
include_directories(${ABACUS_SOURCE_DIR})
include_directories(${ABACUS_SOURCE_DIR}/source_base/module_container)

set(ABACUS_MIN_CXX_STANDARD 14)
if(NOT DEFINED CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD ${ABACUS_MIN_CXX_STANDARD})
elseif(CMAKE_CXX_STANDARD LESS ABACUS_MIN_CXX_STANDARD)
message(FATAL_ERROR "ABACUS requires C++${ABACUS_MIN_CXX_STANDARD} or newer.")
endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)

Expand Down Expand Up @@ -331,7 +334,6 @@ if(ENABLE_LCAO)
target_link_libraries(${ABACUS_BIN_NAME} ${PEXSI_LIBRARY} ${SuperLU_DIST_LIBRARY} ${ParMETIS_LIBRARY} ${METIS_LIBRARY} pexsi)
include_directories(${PEXSI_INCLUDE_DIR} ${ParMETIS_INCLUDE_DIR})
add_compile_definitions(__PEXSI)
set(CMAKE_CXX_STANDARD 14)
endif()
else()
set(ENABLE_MLALGO OFF)
Expand Down Expand Up @@ -452,7 +454,6 @@ if(USE_CUDA)
# Always find CUDAToolkit to get CUDAToolkit_VERSION
find_package(CUDAToolkit REQUIRED)

set_if_higher(CMAKE_CXX_STANDARD 14)
if(CUDAToolkit_VERSION VERSION_GREATER_EQUAL "13.0")
message(STATUS "CUDA ${CUDAToolkit_VERSION} detected. Setting CMAKE_CUDA_STANDARD to 17.")
set_if_higher(CMAKE_CXX_STANDARD 17)
Expand Down Expand Up @@ -687,8 +688,6 @@ if(ENABLE_MLALGO OR DEFINED Torch_DIR)
find_package(Torch REQUIRED)
if(NOT Torch_VERSION VERSION_LESS "2.1.0")
set_if_higher(CMAKE_CXX_STANDARD 17)
elseif(NOT Torch_VERSION VERSION_LESS "1.5.0")
set_if_higher(CMAKE_CXX_STANDARD 14)
endif()
include_directories(${TORCH_INCLUDE_DIRS})
if(MKL_FOUND)
Expand Down Expand Up @@ -750,7 +749,6 @@ if(DEFINED LIBRI_DIR)
set(ENABLE_LIBRI ON)
endif()
if(ENABLE_LIBRI)
set_if_higher(CMAKE_CXX_STANDARD 14)
if(LIBRI_DIR)
else()
find_package(LibRI REQUIRED)
Expand Down
16 changes: 14 additions & 2 deletions source/source_esolver/esolver_fp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,20 @@ void ESolver_FP::before_scf(UnitCell& ucell, const int istep)
if (ucell.ionic_position_updated)
{
this->CE.update_all_dis(ucell);
this->CE.extrapolate_charge(&this->Pgrid, ucell, &this->chr, &this->sf,
GlobalV::ofs_running, GlobalV::ofs_warning);

const bool skip_charge_extrap_for_wfc = PARAM.inp.basis_type == "lcao"
&& PARAM.inp.wfc_extrap != "none"
&& istep > 0;
if (skip_charge_extrap_for_wfc)
{
GlobalV::ofs_running << " charge density extrapolation is skipped because wfc_extrap = "
<< PARAM.inp.wfc_extrap << "." << std::endl;
}
else
{
this->CE.extrapolate_charge(&this->Pgrid, ucell, &this->chr, &this->sf,
GlobalV::ofs_running, GlobalV::ofs_warning);
}
}

//! calculate D2 or D3 vdW
Expand Down
119 changes: 114 additions & 5 deletions source/source_esolver/esolver_ks_lcao.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#include "esolver_ks_lcao.h"
#include "source_base/global_function.h"
#include "source_base/module_external/blacs_connector.h"
#include "source_cell/module_neighbor/sltk_atom_arrange.h"
#include "source_estate/elecstate_tools.h"
Expand All @@ -16,8 +17,11 @@
#include "../source_lcao/module_ri/exx_opt_orb.h"
#endif
#include "source_lcao/module_rdmft/rdmft.h"
#include "source_lcao/module_extrap/wf_history_lcao.h"
#include "source_lcao/module_operator_lcao/operator_lcao.h" // build overlap SR before WFN reorthonormalization
#include "source_estate/module_charge/chgmixing.h" // use charge mixing, mohan add 20251006
#include "source_estate/module_dm/init_dm.h" // init dm from electronic wave functions
#include "source_estate/module_dm/cal_dm_psi.h" // rebuild DMK from extrapolated WFN
#include "source_io/module_ctrl/ctrl_runner_lcao.h" // use ctrl_runner_lcao()
#include "source_io/module_ctrl/ctrl_iter_lcao.h" // use ctrl_iter_lcao()
#include "source_io/module_ctrl/ctrl_scf_lcao.h" // use ctrl_scf_lcao()
Expand All @@ -26,6 +30,11 @@
#include "source_lcao/LCAO_set.h" // mohan add 20251111
#include "source_psi/setup_psi.h" // use Setup_Psi for deallocate_psi

#include <iomanip>
#include <sstream>
#include <string>
#include <type_traits>

namespace ModuleESolver
{

Expand Down Expand Up @@ -82,6 +91,14 @@ void ESolver_KS_LCAO<TK, TR>::before_all_runners(UnitCell& ucell, const Input_pa
LCAO_domain::set_psi_occ_dm_chg<TK>(this->kv, this->psi, this->pv, this->pelec,
this->dmat, this->chr, inp);

const auto wfc_extrap_method = ModuleExtrap::wfc_extrap_method_from_string(inp.wfc_extrap);
if (wfc_extrap_method != ModuleExtrap::WfcExtrapMethod::None)
{
this->wf_history_lcao_ = std::make_unique<ModuleExtrap::WfHistoryLCAO<TK>>(wfc_extrap_method);
GlobalV::ofs_running << " WFN extrapolation method: "
<< ModuleExtrap::to_string(wfc_extrap_method) << std::endl;
}

LCAO_domain::set_pot<TK>(ucell, this->kv, this->sf, *this->pw_rho, *this->pw_rhod,
this->pelec, this->orb_, this->pv, this->locpp, this->dftu,
this->solvent, this->exx_nao, this->deepks, inp);
Expand Down Expand Up @@ -175,6 +192,8 @@ void ESolver_KS_LCAO<TK, TR>::before_scf(UnitCell& ucell, const int istep)
}
this->dmat.dm->init_DMR(*hamilt_lcao->getHR());

bool initialized_by_wfc_extrap = false;

// 13.1) decide the strategy for initializing DMR and HR
if(istep == 0)//if the first scf step, readin DMR from file,
{
Expand All @@ -193,12 +212,97 @@ void ESolver_KS_LCAO<TK, TR>::before_scf(UnitCell& ucell, const int istep)
this->chr, PARAM.inp.ks_solver);
}
}
else if(PARAM.inp.esolver_type!="tddft")//if not, use the DMR calculated from last step
else if(PARAM.inp.esolver_type!="tddft")//initialize DMR from WFN history if required
{
// 13.1.2) two cases are considered:
// 1. DMK in DensityMatrix is not empty (istep > 0), then DMR is initialized by DMK
// 2. DMK in DensityMatrix is empty (istep == 0), then DMR is initialized by zeros
this->dmat.dm->cal_DMR();
if constexpr (std::is_same<TK, double>::value)
{
if (this->wf_history_lcao_ != nullptr)
{
// The newly-created HamiltLCAO has allocated S(R), but the actual
// overlap values are normally filled later by updateHk() inside the
// eigensolver. WFN extrapolation needs S before entering SCF, so build
// only the overlap real-space matrix here and then fold it to S(Gamma).
auto* lcao_op = dynamic_cast<hamilt::OperatorLCAO<TK, TR>*>(hamilt_lcao->getOperator());
if (lcao_op == nullptr)
{
ModuleBase::WARNING_QUIT("ESolver_KS_LCAO::before_scf",
"Failed to access the LCAO operator chain for WFN extrapolation.");
}
ModuleBase::timer::start("WFN_Extrap", "prepare_overlap");
lcao_op->contributeHR();
// Refresh the current Gamma-only overlap matrix before reusing the previous WFN.
// The layout must follow the active LCAO solver because the WFN orthonormalizer
// assembles distributed local blocks using the same column/row-major convention.
const int sk_layout =
ModuleBase::GlobalFunc::IS_COLUMN_MAJOR_KS_SOLVER(PARAM.inp.ks_solver) ? 1 : 0;
hamilt_lcao->updateSk(0, sk_layout);
ModuleBase::timer::end("WFN_Extrap", "prepare_overlap");

ModuleBase::timer::start("WFN_Extrap", "apply");
const ModuleExtrap::WfExtrapApplyResult wfc_result
= this->wf_history_lcao_->try_use_prev_wf_gamma(hamilt_lcao->getSk(),
this->pv,
*(this->psi),
this->pelec->wg);
ModuleBase::timer::end("WFN_Extrap", "apply");
if (wfc_result.ok())
{
ModuleBase::timer::start("WFN_Extrap", "rebuild_density");
elecstate::cal_dm_psi(this->dmat.dm->get_paraV_pointer(),
this->pelec->wg,
*(this->psi),
*(this->dmat.dm));
this->dmat.dm->cal_DMR();
LCAO_domain::dm2rho(this->dmat.dm->get_DMR_vector(), PARAM.inp.nspin, &this->chr);
ModuleBase::timer::end("WFN_Extrap", "rebuild_density");
initialized_by_wfc_extrap = true;

GlobalV::ofs_running << " WFN extrapolation: use_prev_wf from ionic step "
<< wfc_result.snapshot_istep
<< ", active bands = " << wfc_result.nactive_bands
<< ", max |C^T S C - I| = "
<< wfc_result.max_orthonormality_deviation << std::endl;
}
else
{
std::ostringstream oss;
oss << std::scientific << std::setprecision(16);
oss << "WFN extrapolation failed with status: "
<< ModuleExtrap::to_string(wfc_result.status) << "\n\n"
<< " snapshot_istep=" << wfc_result.snapshot_istep << "\n"
<< " failed_state=" << wfc_result.failed_state << "\n"
<< " failed_pivot_index=" << wfc_result.failed_pivot_index << "\n"
<< " failed_pivot=" << wfc_result.failed_pivot << "\n"
<< " nstate=" << wfc_result.nstate << "\n"
<< " nbands=" << wfc_result.nbands << "\n"
<< " nbasis=" << wfc_result.nbasis << "\n"
<< " min_metric_diag=" << wfc_result.min_metric_diag << "\n"
<< " max_metric_diag=" << wfc_result.max_metric_diag << "\n"
<< " max_metric_abs=" << wfc_result.max_metric_abs << "\n"
<< " max_metric_asymmetry=" << wfc_result.max_metric_asymmetry << "\n"
<< " max_orthonormality_deviation="
<< wfc_result.max_orthonormality_deviation << "\n";
const std::string message = oss.str();
ModuleBase::WARNING_QUIT("ESolver_KS_LCAO::before_scf", message);
}
}
}
else
{
if (this->wf_history_lcao_ != nullptr)
{
ModuleBase::WARNING_QUIT("ESolver_KS_LCAO::before_scf",
"WFN extrapolation is currently supported only for the real Gamma-only NAO path.");
}
}

if (!initialized_by_wfc_extrap)
{
// 13.1.2) two cases are considered:
// 1. DMK in DensityMatrix is not empty (istep > 0), then DMR is initialized by DMK
// 2. DMK in DensityMatrix is empty (istep == 0), then DMR is initialized by zeros
this->dmat.dm->cal_DMR();
}
}
// 13.2) init_scf, should be before_scf? mohan add 2025-03-10
elecstate::init_scf(ucell, this->Pgrid, this->sf.strucFac, this->locpp.numeric,
Expand Down Expand Up @@ -521,6 +625,11 @@ void ESolver_KS_LCAO<TK, TR>::after_scf(UnitCell& ucell, const int istep, const
this->rdmft_solver, this->deepks, this->exx_nao,
this->conv_esolver, this->scf_nmax_flag, istep);

if (conv_esolver && this->wf_history_lcao_ != nullptr && this->psi != nullptr)
{
this->wf_history_lcao_->update_after_scf(istep, *(this->psi), this->pelec->wg);
}

//! 3) Clean up RA, which is used to serach for adjacent atoms
if (!PARAM.inp.cal_force && !PARAM.inp.cal_stress)
{
Expand Down
11 changes: 11 additions & 0 deletions source/source_esolver/esolver_ks_lcao.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ template <typename T, typename TR>
class ESolver_LR;
}

namespace ModuleExtrap
{
template <typename TK>
class WfHistoryLCAO;
}

//-----------------------------------
// ESolver for LCAO
//-----------------------------------
Expand Down Expand Up @@ -82,6 +88,11 @@ class ESolver_KS_LCAO : public ESolver_KS
//! Add density matrix class, mohan add 2025-10-30
LCAO_domain::Setup_DM<TK> dmat;

//! Optional NAO wavefunction-history extrapolation state.
//! The object owns only copied wavefunction snapshots; transient objects such as
//! Psi, HamiltLCAO, DensityMatrix, and Charge are passed in only when needed.
std::unique_ptr<ModuleExtrap::WfHistoryLCAO<TK>> wf_history_lcao_;


// For deepks method, mohan add 2025-10-08
Setup_DeePKS<TK> deepks;
Expand Down
1 change: 1 addition & 0 deletions source/source_io/module_parameter/input_parameter.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ struct Input_para
std::string init_chg = "atomic"; ///< "file","atomic"
bool dm_to_rho = false; ///< read density matrix from npz format and calculate charge density
std::string chg_extrap = "default"; ///< xiaohui modify 2015-02-01
std::string wfc_extrap = "none"; ///< wavefunction extrapolation method for LCAO calculations
bool init_vel = false; ///< read velocity from STRU or not liuyu 2021-07-14

std::string input_file = "INPUT"; ///< input file name
Expand Down
41 changes: 41 additions & 0 deletions source/source_io/module_parameter/read_input_item_system.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,47 @@ Available options are:
};
this->add_item(item);
}
{
Input_Item item("wfc_extrap");
item.annotation = "none; use_prev_wf";
item.category = "System variables";
item.type = "String";
item.description = R"(Wavefunction extrapolation method for LCAO calculations.

* none: Disable wavefunction-based extrapolation.
* use_prev_wf: Use the previous ionic step wavefunctions as the initial guess.

This option is currently limited to Gamma-only LCAO calculations.
The k-point, ASPC, and GExt_PROJ paths will be enabled by later updates.)";
item.default_value = "none";
read_sync_string(input.wfc_extrap);
item.check_value = [](const Input_Item& item, const Parameter& para) {
if (para.input.wfc_extrap != "none" && para.input.wfc_extrap != "use_prev_wf")
{
ModuleBase::WARNING_QUIT("ReadInput", "wfc_extrap should be none or use_prev_wf");
}
if (para.input.wfc_extrap == "use_prev_wf")
{
if (para.input.basis_type != "lcao")
{
ModuleBase::WARNING_QUIT("ReadInput", "WFN-based extrapolation is available only for LCAO");
}
if (!para.input.gamma_only)
{
ModuleBase::WARNING_QUIT("ReadInput", "WFN-based extrapolation is not implemented for k-points");
}
if (para.input.esolver_type == "tddft")
{
ModuleBase::WARNING_QUIT("ReadInput", "WFN-based extrapolation is not implemented for tddft");
}
if (para.input.nspin == 4)
{
ModuleBase::WARNING_QUIT("ReadInput", "WFN-based extrapolation is not implemented for nspin = 4");
}
}
};
this->add_item(item);
}
{
Input_Item item("ecutwfc");
item.annotation = "energy cutoff for wave functions";
Expand Down
3 changes: 3 additions & 0 deletions source/source_lcao/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ add_subdirectory(module_deltaspin)

if(ENABLE_LCAO)
add_subdirectory(module_operator_lcao)
add_subdirectory(module_extrap)
list(APPEND objects
hamilt_lcao.cpp
module_operator_lcao/operator_lcao.cpp
Comment on lines +9 to 12
Copy link
Copy Markdown
Author

@Growl1234 Growl1234 Jun 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like module_operator_lcao also acts like this, so I'm hesitating here...

Expand All @@ -23,6 +24,8 @@ if(ENABLE_LCAO)
module_operator_lcao/dspin_lcao.cpp
module_operator_lcao/dftu_lcao.cpp
module_operator_lcao/operator_force_stress_utils.cpp
module_extrap/wf_history_lcao.cpp
module_extrap/wf_orthonormalize_lcao.cpp
Comment on lines +27 to +28
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above.

dftu_lcao.cpp
pulay_fs_center2.cpp
FORCE_STRESS.cpp
Expand Down
14 changes: 14 additions & 0 deletions source/source_lcao/module_extrap/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
set(objects
wf_history_lcao.cpp
wf_orthonormalize_lcao.cpp
)

add_library(
lcao_extrap
OBJECT
${objects}
)

if(ENABLE_COVERAGE)
add_coverage(lcao_extrap)
endif()
Loading
Loading