From c523002d91b35f451b9a77ec964ac52548d920ef Mon Sep 17 00:00:00 2001 From: Dynamics of Condensed Matter <30792324+DCM-Uni-Paderborn@users.noreply.github.com> Date: Tue, 12 May 2026 19:36:10 +0200 Subject: [PATCH] Add LibFCI active-space solver (#5167) Co-authored-by: Thomas D. Kuehne --- CMakeLists.txt | 16 ++ cmake/cp2kConfig.cmake.in | 5 + cmake/modules/FindLibFCI.cmake | 55 ++++++ docs/technologies/libraries.md | 7 + src/CMakeLists.txt | 3 + src/cp2k_info.F | 3 + src/input_constants.F | 3 +- src/input_cp2k_as.F | 15 +- src/qs_active_space_fci.F | 179 ++++++++++++++++++ src/qs_active_space_methods.F | 159 +++++++++++++++- tests/QS/regtest-as-fci/TEST_FILES.toml | 3 + tests/QS/regtest-as-fci/h2_as_fci.inp | 86 +++++++++ tests/QS/regtest-as-fci/h2_as_fci_uks.inp | 87 +++++++++ tests/TEST_DIRS | 1 + tools/precommit/precommit.py | 2 +- tools/toolchain/install_cp2k_toolchain.sh | 9 +- .../scripts/stage8/install_libfci.sh | 107 +++++++++++ .../scripts/stage8/install_stage8.sh | 1 + 18 files changed, 729 insertions(+), 12 deletions(-) create mode 100644 cmake/modules/FindLibFCI.cmake create mode 100644 src/qs_active_space_fci.F create mode 100644 tests/QS/regtest-as-fci/TEST_FILES.toml create mode 100644 tests/QS/regtest-as-fci/h2_as_fci.inp create mode 100644 tests/QS/regtest-as-fci/h2_as_fci_uks.inp create mode 100755 tools/toolchain/scripts/stage8/install_libfci.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 19c5ddbf97..d098b2ebee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,6 +129,8 @@ option(CP2K_USE_TBLITE "Enable TBLITE support" ${CP2K_USE_EVERYTHING}) option(CP2K_USE_FFTW3 "Enable FFTW3 support" ${CP2K_USE_EVERYTHING}) option(CP2K_USE_GREENX "Enable GreenX support" ${CP2K_USE_EVERYTHING}) option(CP2K_USE_HDF5 "Enable HDF5 support" ${CP2K_USE_EVERYTHING}) +option(CP2K_USE_LIBFCI "Enable LibFCI active-space solver support" + ${CP2K_USE_EVERYTHING}) option(CP2K_USE_LIBINT2 "Enable Libint2 support" ${CP2K_USE_EVERYTHING}) option(CP2K_USE_LIBTORCH "Enable LibTorch support" ${CP2K_USE_EVERYTHING}) option(CP2K_USE_OPENPMD "Enable support for I/O via openPMD" OFF) @@ -801,6 +803,10 @@ if(CP2K_USE_LIBINT2) include_directories(BEFORE ${CP2K_LIBINT2_INCLUDE_DIRS}) endif() +if(CP2K_USE_LIBFCI) + find_package(LibFCI REQUIRED) +endif() + # Spglib if(CP2K_USE_SPGLIB) find_package(Spglib REQUIRED CONFIG) @@ -1179,6 +1185,12 @@ if(CP2K_USE_LIBINT2) " - libraries: ${CP2K_LIBINT2_LINK_LIBRARIES}\n\n") endif() +if(CP2K_USE_LIBFCI) + message(" - LibFCI\n" + " - include directories: ${CP2K_LIBFCI_INCLUDE_DIRS}\n" + " - libraries: ${CP2K_LIBFCI_LINK_LIBRARIES}\n\n") +endif() + if(CP2K_USE_VORI) message(" - Libvori\n" " - libraries: ${CP2K_LIBVORI_LINK_LIBRARIES}\n\n") endif() @@ -1294,6 +1306,10 @@ if(NOT CP2K_USE_LIBINT2) message(" - Libint2") endif() +if(NOT CP2K_USE_LIBFCI) + message(" - LibFCI") +endif() + if(NOT CP2K_USE_LIBXC) message(" - LibXC") endif() diff --git a/cmake/cp2kConfig.cmake.in b/cmake/cp2kConfig.cmake.in index c0e33f5260..f82c0a865b 100644 --- a/cmake/cp2kConfig.cmake.in +++ b/cmake/cp2kConfig.cmake.in @@ -89,6 +89,11 @@ if(NOT TARGET cp2k::cp2k) find_dependency(Libint2 REQUIRED) endif() + if(@CP2K_USE_LIBFCI@) + set(CP2K_USE_LIBFCI @CP2K_USE_LIBFCI@) + find_dependency(LibFCI REQUIRED) + endif() + if(@CP2K_USE_SPGLIB@) set(CP2K_USE_SPGLIB @CP2K_USE_SPGLIB@) find_dependency(LibSPG REQUIRED) diff --git a/cmake/modules/FindLibFCI.cmake b/cmake/modules/FindLibFCI.cmake new file mode 100644 index 0000000000..e420da3111 --- /dev/null +++ b/cmake/modules/FindLibFCI.cmake @@ -0,0 +1,55 @@ +#!-------------------------------------------------------------------------------------------------! +#! CP2K: A general program to perform molecular dynamics simulations ! +#! Copyright 2000-2026 CP2K developers group ! +#! ! +#! SPDX-License-Identifier: GPL-2.0-or-later ! +#!-------------------------------------------------------------------------------------------------! + +include(FindPackageHandleStandardArgs) +include(cp2k_utils) + +find_package(libfci CONFIG QUIET) + +if(libfci_FOUND AND TARGET libfci::fci) + set(LibFCI_FOUND TRUE) + set(CP2K_LIBFCI_FOUND TRUE) + set(CP2K_LIBFCI_LINK_LIBRARIES libfci::fci) + get_target_property(CP2K_LIBFCI_INCLUDE_DIRS libfci::fci + INTERFACE_INCLUDE_DIRECTORIES) +else() + find_package(PkgConfig QUIET) + cp2k_set_default_paths(LIBFCI "libfci") + + if(PKG_CONFIG_FOUND) + pkg_check_modules(CP2K_LIBFCI IMPORTED_TARGET GLOBAL libfci) + endif() + + if(NOT CP2K_LIBFCI_FOUND) + cp2k_find_libraries(LIBFCI "fci") + cp2k_include_dirs(LIBFCI "libfci.h") + endif() + + find_package_handle_standard_args(LibFCI DEFAULT_MSG CP2K_LIBFCI_INCLUDE_DIRS + CP2K_LIBFCI_LINK_LIBRARIES) +endif() + +if(CP2K_LIBFCI_FOUND) + if(NOT TARGET cp2k::libfci::fci) + add_library(cp2k::libfci::fci INTERFACE IMPORTED) + endif() + + if(TARGET libfci::fci) + target_link_libraries(cp2k::libfci::fci INTERFACE libfci::fci) + elseif(TARGET PkgConfig::CP2K_LIBFCI) + target_link_libraries(cp2k::libfci::fci INTERFACE PkgConfig::CP2K_LIBFCI) + else() + set_target_properties( + cp2k::libfci::fci PROPERTIES INTERFACE_INCLUDE_DIRECTORIES + "${CP2K_LIBFCI_INCLUDE_DIRS}") + target_link_libraries(cp2k::libfci::fci + INTERFACE ${CP2K_LIBFCI_LINK_LIBRARIES}) + endif() +endif() + +mark_as_advanced(CP2K_LIBFCI_FOUND CP2K_LIBFCI_INCLUDE_DIRS + CP2K_LIBFCI_LINK_LIBRARIES) diff --git a/docs/technologies/libraries.md b/docs/technologies/libraries.md index 352bd36c91..db8a0d08cf 100644 --- a/docs/technologies/libraries.md +++ b/docs/technologies/libraries.md @@ -219,6 +219,13 @@ TREXIO - Open-source file format and library. Support for TREXIO can be enabled - TREXIO library can be downloaded from - For more information see . +## LibFCI (full-CI active-space solver) + +LibFCI is an external library providing a full-CI solver for CP2K active-space calculations. Support +for LibFCI can be enabled by passing `-DCP2K_USE_LIBFCI=ON` to CMake. + +- LibFCI can be downloaded from + ## GREENX (basically functionality for GreenX methods (RPA, GW, Laplace-MP2 etc.) greenX - Open-source file format and library. Support for greenX can be enabled by passing diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 09f40654b1..964efa49bf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -506,6 +506,7 @@ list( qmmmx_update.F qmmmx_util.F qs_2nd_kernel_ao.F + qs_active_space_fci.F qs_active_space_methods.F qs_active_space_mixing.F qs_active_space_types.F @@ -1758,6 +1759,7 @@ target_link_libraries( $<$:CP2K::MIMIC::mclf> $<$:CP2K::MIMIC::mcl> $<$:cp2k::Libint2::int2> + $<$:cp2k::libfci::fci> $<$:cp2k::cosma> $<$:${CP2K_DLAF_FORTRAN_TARGET}> $<$:cp2k::greenx> @@ -1816,6 +1818,7 @@ target_compile_definitions( $<$:__GREENX> $<$,$>:__FFTW3> $<$:__LIBINT> + $<$:__LIBFCI> $<$:__LIBTORCH> $<$:__COSMA> $<$:__LIBXSMM> diff --git a/src/cp2k_info.F b/src/cp2k_info.F index d60a9201da..fa8ffd84d9 100644 --- a/src/cp2k_info.F +++ b/src/cp2k_info.F @@ -282,6 +282,9 @@ CONTAINS #if defined(__TREXIO) flags = TRIM(flags)//" trexio" #endif +#if defined(__LIBFCI) + flags = TRIM(flags)//" libfci" +#endif #if defined(__OFFLOAD_UNIFIED_MEMORY) flags = TRIM(flags)//" offload_unified_memory" #endif diff --git a/src/input_constants.F b/src/input_constants.F index 7bedb3fb8c..b67f55672d 100644 --- a/src/input_constants.F +++ b/src/input_constants.F @@ -1146,7 +1146,8 @@ MODULE input_constants ! Active space external solvers INTEGER, PARAMETER, PUBLIC :: no_solver = 200, & - qiskit_solver = 201 + qiskit_solver = 201, & + fci_solver = 202 ! callgraph parameters INTEGER, PARAMETER, PUBLIC :: callgraph_none = 0, & diff --git a/src/input_cp2k_as.F b/src/input_cp2k_as.F index bc7ea2db81..b2493e924e 100644 --- a/src/input_cp2k_as.F +++ b/src/input_cp2k_as.F @@ -22,8 +22,8 @@ MODULE input_cp2k_as USE input_constants, ONLY: & casci_canonical, eri_method_full_gpw, eri_method_gpw_ht, eri_operator_coulomb, & eri_operator_erf, eri_operator_erfc, eri_operator_gaussian, eri_operator_lr_trunc, & - eri_operator_trunc, eri_operator_yukawa, gaussian, manual_selection, mao_projection, & - no_solver, qiskit_solver, wannier_projection + eri_operator_trunc, eri_operator_yukawa, fci_solver, gaussian, manual_selection, & + mao_projection, no_solver, qiskit_solver, wannier_projection USE input_cp2k_loc, ONLY: create_localize_section USE input_cp2k_xc, ONLY: create_xc_section USE input_keyword_types, ONLY: keyword_create,& @@ -154,13 +154,14 @@ CONTAINS CALL keyword_release(keyword) CALL keyword_create(keyword, __LOCATION__, name="AS_SOLVER", & - description="The external active space solver for the embedding approach", & - usage="AS_SOLVER QISKIT", & + description="The active space solver for the embedding approach", & + usage="AS_SOLVER FCI", & default_i_val=no_solver, & - enum_c_vals=s2a("NONE", "QISKIT"), & - enum_i_vals=[no_solver, qiskit_solver], & + enum_c_vals=s2a("NONE", "QISKIT", "FCI"), & + enum_i_vals=[no_solver, qiskit_solver, fci_solver], & enum_desc=s2a("NO solver, used to produce FCIDUMP/QCSchema files", & - "QISKIT active space solver")) + "QISKIT active space solver", & + "LibFCI full-CI active space solver")) CALL section_add_keyword(section, keyword) CALL keyword_release(keyword) diff --git a/src/qs_active_space_fci.F b/src/qs_active_space_fci.F new file mode 100644 index 0000000000..998ad6014c --- /dev/null +++ b/src/qs_active_space_fci.F @@ -0,0 +1,179 @@ +!--------------------------------------------------------------------------------------------------! +! CP2K: A general program to perform molecular dynamics simulations ! +! Copyright 2000-2026 CP2K developers group ! +! ! +! SPDX-License-Identifier: GPL-2.0-or-later ! +!--------------------------------------------------------------------------------------------------! + +! ************************************************************************************************** +!> \brief Local FCI solver interface for active-space embedding. +! ************************************************************************************************** +MODULE qs_active_space_fci +#if defined(__LIBFCI) + USE ISO_C_BINDING, ONLY: C_CHAR, & + C_DOUBLE, & + C_INT, & + C_NULL_CHAR +#endif + USE kinds, ONLY: dp + USE message_passing, ONLY: mp_para_env_type + USE qs_active_space_types, ONLY: active_space_type +#if defined(__LIBFCI) + USE qs_active_space_utils, ONLY: eri_to_array, & + subspace_matrix_to_array +#endif +#include "./base/base_uses.f90" + + IMPLICIT NONE + PRIVATE + + CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'qs_active_space_fci' + + PUBLIC :: solve_active_space_fci + +#if defined(__LIBFCI) + INTERFACE + FUNCTION libfci_solve(nspins, norb, nelec, ms2, h1_a, h1_b, eri_aa, eri_ab, eri_bb, & + max_iter, threshold, pspace, project_spin, verbose, energy, & + rdm1_a, rdm1_b, err_msg, err_msg_len) RESULT(status) & + BIND(C, name="libfci_solve") + IMPORT :: C_CHAR, C_DOUBLE, C_INT + INTEGER(C_INT), VALUE :: nspins, norb, nelec, ms2 + REAL(C_DOUBLE), DIMENSION(*), INTENT(IN) :: h1_a, h1_b, eri_aa, eri_ab, eri_bb + INTEGER(C_INT), VALUE :: max_iter, pspace, project_spin, verbose + REAL(C_DOUBLE), VALUE :: threshold + REAL(C_DOUBLE), INTENT(OUT) :: energy + REAL(C_DOUBLE), DIMENSION(*), INTENT(OUT) :: rdm1_a, rdm1_b + CHARACTER(KIND=C_CHAR), DIMENSION(*), INTENT(OUT) :: err_msg + INTEGER(C_INT), VALUE :: err_msg_len + INTEGER(C_INT) :: status + END FUNCTION libfci_solve + END INTERFACE +#endif + +CONTAINS + +! ************************************************************************************************** +!> \brief Solve the current active-space Hamiltonian with the local FCI kernel. +!> \param active_space_env active-space environment +!> \param para_env parallel environment +!> \param p_act_mo_a alpha or spin-summed active-space 1-RDM +!> \param p_act_mo_b beta active-space 1-RDM for unrestricted calculations +! ************************************************************************************************** + SUBROUTINE solve_active_space_fci(active_space_env, para_env, p_act_mo_a, p_act_mo_b) + TYPE(active_space_type), INTENT(INOUT), POINTER :: active_space_env + TYPE(mp_para_env_type), INTENT(IN), POINTER :: para_env + REAL(KIND=dp), ALLOCATABLE, DIMENSION(:), & + INTENT(OUT) :: p_act_mo_a + REAL(KIND=dp), ALLOCATABLE, DIMENSION(:), & + INTENT(OUT), OPTIONAL :: p_act_mo_b + +#if defined(__LIBFCI) + CHARACTER(KIND=C_CHAR), DIMENSION(512) :: c_error + CHARACTER(LEN=512) :: error_text + INTEGER :: max_iter, ms2, n2, n4, nmo_active, & + nspins, status + LOGICAL :: ionode + REAL(KIND=dp) :: energy_active, threshold + REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: eri_aa, eri_ab, eri_bb, fock_a, fock_b, & + p_beta + + nmo_active = active_space_env%nmo_active + nspins = active_space_env%nspins + n2 = nmo_active*nmo_active + n4 = n2*n2 + ionode = para_env%is_source() + + ALLOCATE (fock_a(n2), eri_aa(n4), p_act_mo_a(n2)) + ALLOCATE (fock_b(MAX(1, n2)), eri_ab(MAX(1, n4)), eri_bb(MAX(1, n4)), p_beta(MAX(1, n2))) + fock_b(:) = 0.0_dp + eri_ab(:) = 0.0_dp + eri_bb(:) = 0.0_dp + p_beta(:) = 0.0_dp + + ASSOCIATE (act_indices => active_space_env%active_orbitals(:, 1)) + CALL subspace_matrix_to_array(active_space_env%fock_sub(1), fock_a, act_indices, act_indices) + END ASSOCIATE + CALL eri_to_array(active_space_env%eri, eri_aa, active_space_env%active_orbitals, 1, 1) + + IF (nspins == 2) THEN + IF (.NOT. PRESENT(p_act_mo_b)) THEN + CPABORT("Missing beta output buffer for local active-space FCI solver.") + END IF + ALLOCATE (p_act_mo_b(n2)) + ASSOCIATE (act_indices => active_space_env%active_orbitals(:, 2)) + CALL subspace_matrix_to_array(active_space_env%fock_sub(2), fock_b(1:n2), act_indices, act_indices) + END ASSOCIATE + CALL eri_to_array(active_space_env%eri, eri_ab(1:n4), active_space_env%active_orbitals, 1, 2) + CALL eri_to_array(active_space_env%eri, eri_bb(1:n4), active_space_env%active_orbitals, 2, 2) + END IF + + status = 0 + energy_active = 0.0_dp + error_text = "" + c_error(:) = C_NULL_CHAR + ms2 = active_space_env%multiplicity - 1 + max_iter = 2048 + threshold = 1.0E-8_dp + + IF (ionode) THEN + status = libfci_solve(INT(nspins, C_INT), INT(nmo_active, C_INT), & + INT(active_space_env%nelec_active, C_INT), INT(ms2, C_INT), & + fock_a, fock_b, eri_aa, eri_ab, eri_bb, INT(max_iter, C_INT), & + REAL(threshold, C_DOUBLE), INT(200, C_INT), INT(1, C_INT), INT(0, C_INT), & + energy_active, p_act_mo_a, p_beta, c_error, INT(SIZE(c_error), C_INT)) + error_text = c_string(c_error) + END IF + + CALL para_env%bcast(status, para_env%source) + CALL para_env%bcast(error_text, para_env%source) + IF (status /= 0) THEN + IF (LEN_TRIM(error_text) > 0) THEN + CPABORT("Local active-space FCI solver failed: "//TRIM(error_text)) + ELSE + CPABORT("Local active-space FCI solver failed.") + END IF + END IF + + CALL para_env%bcast(energy_active, para_env%source) + CALL para_env%bcast(p_act_mo_a, para_env%source) + active_space_env%energy_active = energy_active + IF (nspins == 2) THEN + p_act_mo_b(:) = p_beta(1:n2) + CALL para_env%bcast(p_act_mo_b, para_env%source) + END IF + + DEALLOCATE (fock_a, fock_b, eri_aa, eri_ab, eri_bb, p_beta) +#else + MARK_USED(active_space_env) + MARK_USED(para_env) + MARK_USED(p_act_mo_a) + IF (PRESENT(p_act_mo_b)) THEN + MARK_USED(p_act_mo_b) + END IF + CPABORT("AS_SOLVER FCI requires CP2K to be built with LibFCI support.") +#endif + + END SUBROUTINE solve_active_space_fci + +#if defined(__LIBFCI) +! ************************************************************************************************** +!> \brief Convert a C null-terminated string buffer to a Fortran string. +!> \param buffer ... +!> \return ... +! ************************************************************************************************** + FUNCTION c_string(buffer) RESULT(text) + CHARACTER(KIND=C_CHAR), DIMENSION(:), INTENT(IN) :: buffer + CHARACTER(LEN=512) :: text + + INTEGER :: i + + text = "" + DO i = 1, MIN(SIZE(buffer), LEN(text)) + IF (buffer(i) == C_NULL_CHAR) EXIT + text(i:i) = buffer(i) + END DO + END FUNCTION c_string +#endif + +END MODULE qs_active_space_fci diff --git a/src/qs_active_space_methods.F b/src/qs_active_space_methods.F index aae69b8717..2b26aba609 100644 --- a/src/qs_active_space_methods.F +++ b/src/qs_active_space_methods.F @@ -62,7 +62,7 @@ MODULE qs_active_space_methods casci_canonical, eri_method_full_gpw, eri_method_gpw_ht, eri_operator_coulomb, & eri_operator_erf, eri_operator_erfc, eri_operator_gaussian, eri_operator_yukawa, & eri_operator_trunc, eri_operator_lr_trunc, & - manual_selection, mao_projection, no_solver, qiskit_solver, wannier_projection, & + fci_solver, manual_selection, mao_projection, no_solver, qiskit_solver, wannier_projection, & eri_poisson_analytic, eri_poisson_periodic, eri_poisson_mt, high_spin_roks USE kpoint_types, ONLY: get_kpoint_info, kpoint_type USE input_section_types, ONLY: section_vals_get, section_vals_get_subs_vals, & @@ -118,6 +118,7 @@ MODULE qs_active_space_methods qcschema_env_release, & qcschema_to_hdf5, & qcschema_type + USE qs_active_space_fci, ONLY: solve_active_space_fci USE qs_active_space_types, ONLY: active_space_type, & create_active_space_type, & csr_idx_from_combined, & @@ -1146,7 +1147,7 @@ CONTAINS ! associate the active space environment with the qs environment CALL set_qs_env(qs_env, active_space=active_space_env) - ! Perform the embedding calculation only if qiskit is specified + ! Perform the embedding calculation when an active-space solver is specified CALL section_vals_val_get(as_input, "AS_SOLVER", i_val=as_solver) SELECT CASE (as_solver) CASE (no_solver) @@ -1158,6 +1159,9 @@ CONTAINS CASE (qiskit_solver) CALL rsdft_embedding(qs_env, active_space_env, as_input) CALL qs_scf_compute_properties(qs_env, wf_type="MC-DFT", do_mp2=.FALSE.) + CASE (fci_solver) + CALL local_fci_embedding(qs_env, active_space_env, as_input) + CALL qs_scf_compute_properties(qs_env, wf_type="MC-DFT", do_mp2=.FALSE.) CASE DEFAULT CPABORT("Unknown active space solver") END SELECT @@ -3016,6 +3020,157 @@ CONTAINS END SUBROUTINE print_pmat_noon +! ************************************************************************************************** +!> \brief Run range-separated DFT embedding with the local FCI active-space solver. +!> \param qs_env Quickstep environment +!> \param active_space_env active-space environment +!> \param as_input ACTIVE_SPACE input section +! ************************************************************************************************** + SUBROUTINE local_fci_embedding(qs_env, active_space_env, as_input) + TYPE(qs_environment_type), POINTER :: qs_env + TYPE(active_space_type), POINTER :: active_space_env + TYPE(section_vals_type), POINTER :: as_input + + CHARACTER(len=*), PARAMETER :: routineN = 'local_fci_embedding' + + INTEGER :: handle, iter, iw, max_iter + LOGICAL :: converged, do_scf_embedding + REAL(KIND=dp) :: delta_E, energy_corr, energy_new, & + energy_old, energy_scf, eps_iter, t1, & + t2 + REAL(KIND=dp), ALLOCATABLE, DIMENSION(:) :: p_act_mo_a, p_act_mo_b + TYPE(cp_logger_type), POINTER :: logger + TYPE(dbcsr_p_type), DIMENSION(:), POINTER :: rho_ao + TYPE(dft_control_type), POINTER :: dft_control + TYPE(mo_set_type), DIMENSION(:), POINTER :: mos_active + TYPE(mp_para_env_type), POINTER :: para_env + TYPE(qs_energy_type), POINTER :: energy + TYPE(qs_ks_env_type), POINTER :: ks_env + TYPE(qs_rho_type), POINTER :: rho + + CALL timeset(routineN, handle) + + t1 = m_walltime() + logger => cp_get_default_logger() + iw = cp_logger_get_default_io_unit(logger) + + CALL get_qs_env(qs_env, para_env=para_env, dft_control=dft_control) + IF (dft_control%roks) CPABORT("AS_SOLVER FCI is not supported for ROKS calculations.") + + CALL section_vals_val_get(as_input, "SCF_EMBEDDING", l_val=do_scf_embedding) + active_space_env%do_scf_embedding = do_scf_embedding + CALL section_vals_val_get(as_input, "MAX_ITER", i_val=max_iter) + IF (max_iter < 0) CPABORT("Specify a non-negative number of max iterations.") + CALL section_vals_val_get(as_input, "EPS_ITER", r_val=eps_iter) + IF (eps_iter < 0.0) CPABORT("Specify a non-negative convergence threshold.") + + CALL get_qs_env(qs_env, rho=rho, energy=energy, ks_env=ks_env) + CALL qs_rho_get(rho, rho_ao=rho_ao) + + IF (iw > 0) THEN + WRITE (UNIT=iw, FMT="(/,T2,A,/)") & + "RANGE-SEPARATED DFT EMBEDDING WITH LIBFCI SOLVER" + WRITE (iw, '(T3,A,T68,I12)') "Max. iterations", max_iter + WRITE (iw, '(T3,A,T68,E12.4)') "Conv. threshold", eps_iter + WRITE (iw, '(T3,A,T68,A)') "Density mixer", TRIM(active_space_mixing_label(active_space_env)) + WRITE (iw, '(T3,A,T66,F14.2)') "Mixing alpha", active_space_env%alpha + WRITE (UNIT=iw, FMT="(/,T3,A,T11,A,T21,A,T34,A,T55,A,T75,A,/,T3,A)") & + "Iter", "Update", "Time", "Corr. energy", "Total energy", "Change", REPEAT("-", 78) + END IF + + iter = 0 + converged = .FALSE. + energy_scf = active_space_env%energy_ref + energy_new = energy_scf + mos_active => active_space_env%mos_active + + DO WHILE (iter < max_iter) + iter = iter + 1 + + IF (active_space_env%nspins == 2) THEN + CALL solve_active_space_fci(active_space_env, para_env, p_act_mo_a, p_act_mo_b) + active_space_env%energy_total = active_space_env%energy_inactive + active_space_env%energy_active + CALL update_active_density(p_act_mo_a, active_space_env, p_act_mo_b) + DEALLOCATE (p_act_mo_a, p_act_mo_b) + ELSE + CALL solve_active_space_fci(active_space_env, para_env, p_act_mo_a) + active_space_env%energy_total = active_space_env%energy_inactive + active_space_env%energy_active + CALL update_active_density(p_act_mo_a, active_space_env) + DEALLOCATE (p_act_mo_a) + END IF + + energy_old = energy_new + energy_new = active_space_env%energy_total + energy_corr = energy_new - energy_scf + delta_E = energy_new - energy_old + + t2 = t1 + t1 = m_walltime() + IF (iw > 0) THEN + WRITE (UNIT=iw, & + FMT="(T3,I4,T11,A,T19,F6.1,T28,F18.10,T49,F18.10,T70,ES11.2)") & + iter, TRIM(active_space_mixing_label(active_space_env)), & + t1 - t2, energy_corr, energy_new, delta_E + CALL m_flush(iw) + END IF + + CALL update_density_ao(active_space_env, rho_ao) + CALL qs_rho_update_rho(rho, qs_env=qs_env) + CALL qs_ks_did_change(qs_env%ks_env, rho_changed=.TRUE.) + CALL evaluate_core_matrix_traces(qs_env) + CALL qs_ks_build_kohn_sham_matrix(qs_env, calculate_forces=.FALSE., & + just_energy=.FALSE., & + ext_xc_section=active_space_env%xc_section) + + active_space_env%energy_ref = energy%total + CALL calculate_operators(mos_active, qs_env, active_space_env) + CALL subspace_fock_matrix(active_space_env, dft_control%roks) + + IF (.NOT. active_space_env%do_scf_embedding) THEN + IF (iw > 0) THEN + WRITE (UNIT=iw, FMT="(/,T3,A,I5,A)") & + "*** one-shot embedding correction finished ***" + END IF + converged = .TRUE. + EXIT + ELSEIF (ABS(delta_E) <= eps_iter) THEN + IF (iw > 0) THEN + WRITE (UNIT=iw, FMT="(/,T3,A,I5,A)") & + "*** rs-DFT embedding run converged in ", iter, " iteration(s) ***" + END IF + converged = .TRUE. + EXIT + END IF + END DO + + IF (.NOT. converged) THEN + IF (iw > 0) THEN + WRITE (UNIT=iw, FMT="(/,T3,A,I5,A)") & + "*** rs-DFT embedding did not converged after ", iter, " iteration(s) ***" + END IF + END IF + + energy%total = active_space_env%energy_total + + IF (iw > 0) THEN + WRITE (UNIT=iw, FMT="(/,T3,A)") & + "Final energy contributions:" + WRITE (UNIT=iw, FMT="(T6,A,T56,F20.10)") & + "Inactive energy:", active_space_env%energy_inactive + WRITE (UNIT=iw, FMT="(T6,A,T56,F20.10)") & + "Active energy:", active_space_env%energy_active + WRITE (UNIT=iw, FMT="(T6,A,T56,F20.10)") & + "Correlation energy:", energy_corr + WRITE (UNIT=iw, FMT="(T6,A,T56,F20.10)") & + "Total rs-DFT energy:", active_space_env%energy_total + END IF + + CALL print_pmat_noon(active_space_env, iw) + CALL para_env%sync() + CALL timestop(handle) + + END SUBROUTINE local_fci_embedding + ! ************************************************************************************************** !> \brief ... !> \param qs_env ... diff --git a/tests/QS/regtest-as-fci/TEST_FILES.toml b/tests/QS/regtest-as-fci/TEST_FILES.toml new file mode 100644 index 0000000000..7524b40076 --- /dev/null +++ b/tests/QS/regtest-as-fci/TEST_FILES.toml @@ -0,0 +1,3 @@ +"h2_as_fci.inp" = [{matcher="M011", tol=1e-12, ref=-1.157903329414417}] +"h2_as_fci_uks.inp" = [{matcher="M011", tol=1e-10, ref=-1.157902959723317}] +#EOF diff --git a/tests/QS/regtest-as-fci/h2_as_fci.inp b/tests/QS/regtest-as-fci/h2_as_fci.inp new file mode 100644 index 0000000000..03d51cd448 --- /dev/null +++ b/tests/QS/regtest-as-fci/h2_as_fci.inp @@ -0,0 +1,86 @@ +&GLOBAL + PRINT_LEVEL LOW + PROJECT h2_as_fci + RUN_TYPE ENERGY +&END GLOBAL + +&FORCE_EVAL + METHOD QS + &DFT + &ACTIVE_SPACE + ACTIVE_ELECTRONS 2 + ACTIVE_ORBITALS 2 + ACTIVE_ORBITAL_INDICES 1 2 + AS_SOLVER FCI + MAX_ITER 1 + ORBITAL_SELECTION MANUAL + SCF_EMBEDDING F + &ERI + EPS_INTEGRAL 1E-10 + METHOD FULL_GPW + OMEGA 1.0 + OPERATOR LONGRANGE + PERIODICITY 0 0 0 + &END ERI + &ERI_GPW + CUTOFF 250 + &END ERI_GPW + &END ACTIVE_SPACE + &MGRID + CUTOFF 300 + &END MGRID + &POISSON + PERIODIC NONE + POISSON_SOLVER ANALYTIC + &END POISSON + &QS + METHOD GAPW + &END QS + &SCF + ADDED_MOS 3 + EPS_DIIS 1E-003 + EPS_EIGVAL 1E-009 + EPS_SCF 1E-009 + MAX_SCF 20 + &PRINT + &RESTART OFF + &END RESTART + &END PRINT + &END SCF + &XC + &HF + FRACTION 1.0 + &INTERACTION_POTENTIAL + OMEGA 1.0 + POTENTIAL_TYPE LONGRANGE + &END INTERACTION_POTENTIAL + &END HF + &XC_FUNCTIONAL + &LDA_C_PMGB06 + SCALE -1.0 + _OMEGA 1.0 + &END LDA_C_PMGB06 + &LDA_C_PW + &END LDA_C_PW + &LDA_X_ERF + _OMEGA 1.0 + SCALE 1.0 + &END LDA_X_ERF + &END XC_FUNCTIONAL + &END XC + &END DFT + &SUBSYS + &CELL + ABC 6.0 6.0 6.0 + PERIODIC NONE + &END CELL + &COORD + H 0.0 0.0 0.356 + H 0.0 0.0 -0.356 + &END COORD + &KIND H + BASIS_SET 6-31G* + POTENTIAL ALL + &END KIND + &END SUBSYS +&END FORCE_EVAL diff --git a/tests/QS/regtest-as-fci/h2_as_fci_uks.inp b/tests/QS/regtest-as-fci/h2_as_fci_uks.inp new file mode 100644 index 0000000000..bfdae14e86 --- /dev/null +++ b/tests/QS/regtest-as-fci/h2_as_fci_uks.inp @@ -0,0 +1,87 @@ +&GLOBAL + PRINT_LEVEL LOW + PROJECT h2_as_fci_uks + RUN_TYPE ENERGY +&END GLOBAL + +&FORCE_EVAL + METHOD QS + &DFT + UKS TRUE + &ACTIVE_SPACE + ACTIVE_ELECTRONS 2 + ACTIVE_ORBITALS 2 + ACTIVE_ORBITAL_INDICES 1 2 1 2 + AS_SOLVER FCI + MAX_ITER 3 + ORBITAL_SELECTION MANUAL + SCF_EMBEDDING T + &ERI + EPS_INTEGRAL 1E-10 + METHOD FULL_GPW + OMEGA 1.0 + OPERATOR LONGRANGE + PERIODICITY 0 0 0 + &END ERI + &ERI_GPW + CUTOFF 250 + &END ERI_GPW + &END ACTIVE_SPACE + &MGRID + CUTOFF 300 + &END MGRID + &POISSON + PERIODIC NONE + POISSON_SOLVER ANALYTIC + &END POISSON + &QS + METHOD GAPW + &END QS + &SCF + ADDED_MOS 3 + EPS_DIIS 1E-003 + EPS_EIGVAL 1E-009 + EPS_SCF 1E-009 + MAX_SCF 20 + &PRINT + &RESTART OFF + &END RESTART + &END PRINT + &END SCF + &XC + &HF + FRACTION 1.0 + &INTERACTION_POTENTIAL + OMEGA 1.0 + POTENTIAL_TYPE LONGRANGE + &END INTERACTION_POTENTIAL + &END HF + &XC_FUNCTIONAL + &LDA_C_PMGB06 + SCALE -1.0 + _OMEGA 1.0 + &END LDA_C_PMGB06 + &LDA_C_PW + &END LDA_C_PW + &LDA_X_ERF + _OMEGA 1.0 + SCALE 1.0 + &END LDA_X_ERF + &END XC_FUNCTIONAL + &END XC + &END DFT + &SUBSYS + &CELL + ABC 6.0 6.0 6.0 + PERIODIC NONE + &END CELL + &COORD + H 0.0 0.0 0.356 + H 0.0 0.0 -0.356 + &END COORD + &KIND H + BASIS_SET 6-31G* + POTENTIAL ALL + &END KIND + &END SUBSYS +&END FORCE_EVAL diff --git a/tests/TEST_DIRS b/tests/TEST_DIRS index 9faad82014..2fddba2e1d 100644 --- a/tests/TEST_DIRS +++ b/tests/TEST_DIRS @@ -234,6 +234,7 @@ QS/regtest-stress QS/regtest-mom-2 libint !ifx QS/regtest-admm-2 libint !ifx QS/regtest-as-dft libint libxc !ifx +QS/regtest-as-fci libfci libint libxc !ifx QS/regtest-mp2-admm-grad libint !ifx QS/regtest-admm-dm libint !ifx NNP/regtest-1 diff --git a/tools/precommit/precommit.py b/tools/precommit/precommit.py index acedfdad5e..173bfcb0c8 100755 --- a/tools/precommit/precommit.py +++ b/tools/precommit/precommit.py @@ -235,7 +235,7 @@ def process_file(fn: str, allow_modifications: bool) -> None: if re.match(r".*\.(cc|cpp|cxx|hcc|hpp|hxx)$", fn): if basename in ["torch_c_api.cpp", "ace_c_api.cpp", "openpmd_c_api.cpp"]: - # Begrudgingly tolerated because some libraries have no C API. + # Begrudgingly tolerated for C++ code with a narrow C ABI. run_remote_tool("clangformat", fn) else: raise Exception(f"C++ is not supported.") diff --git a/tools/toolchain/install_cp2k_toolchain.sh b/tools/toolchain/install_cp2k_toolchain.sh index ce2b6188de..bc116beca9 100755 --- a/tools/toolchain/install_cp2k_toolchain.sh +++ b/tools/toolchain/install_cp2k_toolchain.sh @@ -337,6 +337,8 @@ Specific options of --with-PKG: Default = no --with-trexio Enable the trexio library for TREXIO file format. Default = no + --with-libfci Enable the libfci active-space solver library. + Default = no --with-mcl Install MCL library for MiMiC with toolchain. Default = no --with-dbcsr Install DBCSR library with toolchain. @@ -462,7 +464,7 @@ math_list="mkl acml openblas" lib_list="fftw libint libxc libxsmm cosma scalapack elpa dbcsr cusolvermp plumed spfft spla gsl spglib hdf5 libvdwxc sirius libvori libtorch deepmd ace dftd4 tblite pugixml libsmeagol - fmt trexio greenx gmp mcl" + fmt trexio libfci greenx gmp mcl" package_list="${tool_list} ${mpi_list} ${math_list} ${lib_list}" # ------------------------------------------------------------------------ @@ -491,6 +493,7 @@ with_fmt="__DONTUSE__" with_spglib="__INSTALL__" with_hdf5="__DONTUSE__" with_trexio="__DONTUSE__" +with_libfci="__DONTUSE__" with_elpa="__INSTALL__" with_cusolvermp="__DONTUSE__" with_libvdwxc="__DONTUSE__" @@ -905,6 +908,9 @@ Otherwise use option no." --with-trexio*) with_trexio=$(read_with "${1}") ;; + --with-libfci*) + with_libfci=$(read_with "${1}") + ;; --with-greenx*) with_greenx=$(read_with "${1}") ;; @@ -1091,6 +1097,7 @@ if [ "${with_spglib}" = "__INSTALL__" ] || [ "${with_spla}" = "__INSTALL__" ] || [ "${with_ninja}" = "__INSTALL__" ] || [ "${with_greenx}" = "__INSTALL__" ] || + [ "${with_libfci}" = "__INSTALL__" ] || [ "${with_dftd4}" = "__INSTALL__" ] || [ "${with_mcl}" = "__INSTALL__" ] || [ "${with_tblite}" = "__INSTALL__" ]; then diff --git a/tools/toolchain/scripts/stage8/install_libfci.sh b/tools/toolchain/scripts/stage8/install_libfci.sh new file mode 100755 index 0000000000..5f1a608b5a --- /dev/null +++ b/tools/toolchain/scripts/stage8/install_libfci.sh @@ -0,0 +1,107 @@ +#!/bin/bash -e + +# TODO: Review and if possible fix shellcheck errors. +# shellcheck disable=all + +[ "${BASH_SOURCE[0]}" ] && SCRIPT_NAME="${BASH_SOURCE[0]}" || SCRIPT_NAME=$0 +SCRIPT_DIR="$(cd "$(dirname "$SCRIPT_NAME")/.." && pwd -P)" + +libfci_ver="0.1.0" +libfci_sha256="63e8bd632e55f4b99dbaa3e905cdcd3c5dbc17aefdca1391b855f34a792bb29a" + +source "${SCRIPT_DIR}"/common_vars.sh +source "${SCRIPT_DIR}"/tool_kit.sh +source "${SCRIPT_DIR}"/signal_trap.sh +source "${INSTALLDIR}"/toolchain.conf +source "${INSTALLDIR}"/toolchain.env + +[ -f "${BUILDDIR}/setup_libfci" ] && rm "${BUILDDIR}/setup_libfci" + +! [ -d "${BUILDDIR}" ] && mkdir -p "${BUILDDIR}" +cd "${BUILDDIR}" + +case "$with_libfci" in + __DONTUSE__) ;; + + __INSTALL__) + echo "==================== Installing libfci ====================" + require_env MATH_LIBS + + pkg_install_dir="${INSTALLDIR}/libfci-${libfci_ver}" + install_lock_file="${pkg_install_dir}/install_successful" + if verify_checksums "${install_lock_file}"; then + echo "libfci-${libfci_ver} is already installed, skipping it." + else + retrieve_package "${libfci_sha256}" "libfci-${libfci_ver}.tar.gz" + echo "Installing from scratch into ${pkg_install_dir}" + [ -d "libfci-${libfci_ver}" ] && rm -rf "libfci-${libfci_ver}" + mkdir "libfci-${libfci_ver}" + tar -xzf "libfci-${libfci_ver}.tar.gz" -C "libfci-${libfci_ver}" --strip-components=1 + + mkdir "libfci-${libfci_ver}/build" + cd "libfci-${libfci_ver}/build" + cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_CXX_COMPILER="${CXX}" \ + -DCMAKE_INSTALL_PREFIX="${pkg_install_dir}" \ + -DCMAKE_INSTALL_LIBDIR=lib \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + .. > cmake.log 2>&1 || tail_excerpt cmake.log + CMAKE_BUILD_PARALLEL_LEVEL="$(get_nprocs)" cmake --build . > build.log 2>&1 || tail_excerpt build.log + CMAKE_BUILD_PARALLEL_LEVEL="$(get_nprocs)" cmake --build . --target install > install.log 2>&1 || tail_excerpt install.log + write_checksums "${install_lock_file}" "${SCRIPT_DIR}/stage8/$(basename "${SCRIPT_NAME}")" + cd ../.. + fi + LIBFCI_CFLAGS="-I'${pkg_install_dir}/include'" + LIBFCI_LDFLAGS="-L'${pkg_install_dir}/lib' -Wl,-rpath,'${pkg_install_dir}/lib'" + ;; + __SYSTEM__) + echo "==================== Finding libfci from system paths ====================" + check_lib -lfci "libfci" + add_include_from_paths LIBFCI_CFLAGS "libfci.h" $INCLUDE_PATHS + add_lib_from_paths LIBFCI_LDFLAGS "libfci.*" $LIB_PATHS + ;; + *) + echo "==================== Linking libfci to user paths ====================" + pkg_install_dir="$with_libfci" + LIBFCI_LIBDIR="${pkg_install_dir}/lib" + [ -d "${pkg_install_dir}/lib64" ] && LIBFCI_LIBDIR="${pkg_install_dir}/lib64" + check_dir "${LIBFCI_LIBDIR}" + check_dir "${pkg_install_dir}/include" + LIBFCI_CFLAGS="-I'${pkg_install_dir}/include'" + LIBFCI_LDFLAGS="-L'${LIBFCI_LIBDIR}' -Wl,-rpath,'${LIBFCI_LIBDIR}'" + ;; +esac +if [ "$with_libfci" != "__DONTUSE__" ]; then + LIBFCI_LIBS="-lfci -lstdc++" + cat << EOF > "${BUILDDIR}/setup_libfci" +export LIBFCI_VER="${libfci_ver}" +EOF + if [ "$with_libfci" != "__SYSTEM__" ]; then + cat << EOF >> "${BUILDDIR}/setup_libfci" +prepend_path LD_LIBRARY_PATH "${pkg_install_dir}/lib" +prepend_path LD_RUN_PATH "${pkg_install_dir}/lib" +prepend_path LIBRARY_PATH "${pkg_install_dir}/lib" +prepend_path CPATH "${pkg_install_dir}/include" +prepend_path PKG_CONFIG_PATH "${pkg_install_dir}/lib/pkgconfig" +prepend_path CMAKE_PREFIX_PATH "${pkg_install_dir}" +EOF + fi + cat << EOF >> "${BUILDDIR}/setup_libfci" +export LIBFCI_CFLAGS="${LIBFCI_CFLAGS}" +export LIBFCI_LDFLAGS="${LIBFCI_LDFLAGS}" +export LIBFCI_LIBS="${LIBFCI_LIBS}" +export CP_DFLAGS="\${CP_DFLAGS} -D__LIBFCI" +export CP_CFLAGS="\${CP_CFLAGS} ${LIBFCI_CFLAGS}" +export CP_LDFLAGS="\${CP_LDFLAGS} ${LIBFCI_LDFLAGS}" +export CP_LIBS="${LIBFCI_LIBS} \${CP_LIBS}" +export LIBFCI_ROOT="${pkg_install_dir}" +EOF + filter_setup "${BUILDDIR}/setup_libfci" "${SETUPFILE}" +fi + +load "${BUILDDIR}/setup_libfci" +write_toolchain_env "${INSTALLDIR}" + +cd "${ROOTDIR}" +report_timing "libfci" diff --git a/tools/toolchain/scripts/stage8/install_stage8.sh b/tools/toolchain/scripts/stage8/install_stage8.sh index 8ac738f4ed..2b256c8eb1 100755 --- a/tools/toolchain/scripts/stage8/install_stage8.sh +++ b/tools/toolchain/scripts/stage8/install_stage8.sh @@ -9,6 +9,7 @@ ./scripts/stage8/install_spfft.sh ./scripts/stage8/install_spla.sh ./scripts/stage8/install_sirius.sh +./scripts/stage8/install_libfci.sh ./scripts/stage8/install_trexio.sh ./scripts/stage8/install_mcl.sh #EOF