diff --git a/CMakeLists.txt b/CMakeLists.txt index 568574b0d5..29ee3669f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,6 +164,8 @@ cmake_dependent_option(CP2K_USE_SIRIUS "Enable SIRIUS support" ${CP2K_USE_EVERYTHING} "CP2K_USE_MPI" OFF) cmake_dependent_option(CP2K_USE_SPLA "Enable SpLA support" ${CP2K_USE_EVERYTHING} "CP2K_USE_MPI" OFF) +cmake_dependent_option(CP2K_USE_MIMIC "Enable MIMIC support" + ${CP2K_USE_EVERYTHING} "CP2K_USE_MPI" OFF) cmake_dependent_option( CP2K_USE_LIBXSMM "Enable libxsmm support" ${CP2K_USE_EVERYTHING} "NOT CP2K_USE_ACCEL MATCHES \"OPENCL\"" ON) @@ -816,6 +818,10 @@ if(CP2K_USE_LIBTORCH) endif() endif() +if(CP2K_USE_MIMIC) + find_package(MiMiC REQUIRED) +endif() + if(CP2K_USE_MPI_F08 AND NOT MPI_Fortran_HAVE_F08_MODULE) message( FATAL_ERROR @@ -1008,6 +1014,11 @@ if(CP2K_USE_SPLA_GEMM_OFFLOADING) message(" - SpLA GEMM offloading\n\n") endif() +if(CP2K_USE_MIMIC) + message(" - MiMiC\n" " - include directories: ${CP2K_MIMIC_INCLUDE_DIRS}\n" + " - libraries: ${CP2K_MIMIC_LINK_LIBRARIES}\n\n") +endif() + if(CP2K_USE_DFTD4) message(" - DFTD4\n" " - include directories : ${dftd4_INCLUDE_DIRS}\n" " - libraries : ${dftd4_LINK_LIBRARIES}\n\n") @@ -1140,6 +1151,10 @@ if(NOT CP2K_USE_SPLA) message(" - SpLA") endif() +if(NOT CP2K_USE_MIMIC) + message(" - MiMiC") +endif() + if(NOT CP2K_USE_HDF5) message(" - HDF5") endif() diff --git a/cmake/modules/FindMiMiC.cmake b/cmake/modules/FindMiMiC.cmake new file mode 100644 index 0000000000..41d9aa704c --- /dev/null +++ b/cmake/modules/FindMiMiC.cmake @@ -0,0 +1,71 @@ +#!-------------------------------------------------------------------------------------------------! +#! CP2K: A general program to perform molecular dynamics simulations ! +#! Copyright 2000-2025 CP2K developers group ! +#! ! +#! SPDX-License-Identifier: GPL-2.0-or-later ! +#!-------------------------------------------------------------------------------------------------! + +include(FindPackageHandleStandardArgs) +include(cp2k_utils) + +# ! Use PkgConfig to look for modules named mclf and mcl, LOOK for .pc files +find_package(PkgConfig REQUIRED) + +if(PKG_CONFIG_FOUND) + pkg_check_modules(CP2K_MIMIC IMPORTED_TARGET GLOBAL mclf mcl) +endif() + +# ! Using CP2K_UTILS +if(NOT CP2K_MIMIC_FOUND) + cp2k_set_default_paths(MIMIC "MiMiC") + cp2k_find_libraries(MIMIC mclf) + cp2k_find_libraries(MIMICc mcl) +endif() + +if(NOT CP2K_MIMIC_FOUND) + find_library(CP2K_MIMIC_LIBRARIES mclf PATH_SUFFIXES MiMiC) + find_library(CP2K_MIMICc_LIBRARIES mcl PATH_SUFFIXES MiMiC) + set(CP2K_MIMIC_FOUND True) +endif() + +if(CP2K_MIMIC_FOUND) + set(CP2K_MIMIC_LINK_LIBRARIES + "${CP2K_MIMIC_LIBRARIES};${CP2K_MIMICc_LIBRARIES}") +endif() + +if(NOT CP2K_MIMIC_INCLUDE_DIRS) + cp2k_include_dirs(MIMIC "mcl.mod") +endif() + +if(NOT CP2K_MIMIC_INCLUDE_DIRS) + find_path(CP2K_MIMIC_INCLUDE_DIRS "mcl.mod" PATH_SUFFIXES MiMiC) +endif() + +if(CP2K_MIMIC_INCLUDE_DIRS) + find_package_handle_standard_args( + MiMiC DEFAULT_MSG CP2K_MIMIC_FOUND CP2K_MIMIC_LINK_LIBRARIES + CP2K_MIMIC_INCLUDE_DIRS) +else() + find_package_handle_standard_args(MiMiC DEFAULT_MSG CP2K_MIMIC_FOUND + CP2K_MIMIC_LINK_LIBRARIES) +endif() + +if(CP2K_MIMIC_FOUND) + if(NOT TARGET CP2K::MIMIC::mclf) + add_library(CP2K::MIMIC::mclf INTERFACE IMPORTED) + add_library(CP2K::MIMIC::mcl INTERFACE IMPORTED) + endif() + + if(CP2K_MIMIC_INCLUDE_DIRS) + set_target_properties( + CP2K::MIMIC::mclf CP2K::MIMIC::mcl + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CP2K_MIMIC_INCLUDE_DIRS}") + endif() + target_link_libraries(CP2K::MIMIC::mclf + INTERFACE "${CP2K_MIMIC_LINK_LIBRARIES}") + target_link_libraries(CP2K::MIMIC::mcl + INTERFACE "${CP2K_MIMIC_LINK_LIBRARIES}") +endif() + +mark_as_advanced(CP2K_MIMIC_FOUND CP2K_MIMIC_LINK_LIBRARIES + CP2K_MIMIC_INCLUDE_DIRS) diff --git a/docs/technologies/libraries.md b/docs/technologies/libraries.md index fa8099a487..9450182c92 100644 --- a/docs/technologies/libraries.md +++ b/docs/technologies/libraries.md @@ -236,3 +236,12 @@ greenX - Open-source file format and library. Support for greenX can be enabled - Pass `-DCP2K_USE_HDF5=ON` to CMake to enable HDF5 support. - HDF5 is a hard dependency for SIRIUS and TREXIO, but can also be used by itself to allow read/write functionalities of QCSchema files in the active space module. + +## MIMIC (multiscale simulations) + +MiMiC - Multiscale simulation framework + +- Interface realized through MCL library, which can be downloaded from + +- For more information about the framework and supported programs see +- Pass `-DCP2K_USE_MIMIC=ON` to CMake diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b0f3abc8fd..2feb0a9b33 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1081,6 +1081,9 @@ list( input/input_section_types.F input/input_val_types.F) +list(APPEND CP2K_SRCS_F mimic/mimic_communicator.F mimic/mimic_loop.F + mimic/mcl_wrapper/mcl_api.F mimic/mcl_wrapper/mcl_requests.F) + list( APPEND CP2K_SRCS_F @@ -1733,6 +1736,8 @@ target_link_libraries( $<$,$>:cp2k::FFTW3::fftw3_threads> $<$,$>:cp2k::FFTW3::fftw3_omp> $<$:SPLA::spla> + $<$:CP2K::MIMIC::mclf> + $<$:CP2K::MIMIC::mcl> $<$:cp2k::Libint2::int2> $<$:cp2k::cosma> $<$:DLAF::Fortran> @@ -1794,6 +1799,7 @@ target_compile_definitions( $<$:__SIRIUS_DFTD4> $<$:__SIRIUS_NLCG> $<$:__SIRIUS_VCSQNM> + $<$:__MIMIC> $<$:__MKL> $<$:__ACCELERATE> $<$:__CUSOLVERMP> diff --git a/src/cp2k_info.F b/src/cp2k_info.F index 26598699c3..6d31cf9288 100644 --- a/src/cp2k_info.F +++ b/src/cp2k_info.F @@ -222,6 +222,9 @@ CONTAINS #if defined(__LIBTORCH) flags = TRIM(flags)//" libtorch" #endif +#if defined(__MIMIC) + flags = TRIM(flags)//" mimic" +#endif #if defined(__OFFLOAD_CUDA) flags = TRIM(flags)//" offload_cuda" #endif diff --git a/src/input_constants.F b/src/input_constants.F index bbcbb47dc0..d976d0d4c5 100644 --- a/src/input_constants.F +++ b/src/input_constants.F @@ -137,7 +137,8 @@ MODULE input_constants do_tamc = 17, & tree_mc_run = 18, & driver_run = 19, & - negf_run = 20 + negf_run = 20, & + mimic_run = 21 ! Run Types of Atom Code INTEGER, PARAMETER, PUBLIC :: atom_no_run = 1, & diff --git a/src/input_cp2k_check.F b/src/input_cp2k_check.F index 8103ca1305..6ce6aafc5d 100644 --- a/src/input_cp2k_check.F +++ b/src/input_cp2k_check.F @@ -20,11 +20,11 @@ MODULE input_cp2k_check cp_unit_set_release,& cp_unit_set_type USE input_constants, ONLY: & - do_region_global, do_thermo_al, do_thermo_csvr, do_thermo_gle, do_thermo_nose, & - do_thermo_same_as_part, negf_run, npt_f_ensemble, npt_i_ensemble, npt_ia_ensemble, & - vdw_nl_LMKLL, xc_funct_b3lyp, xc_funct_beefvdw, xc_funct_blyp, xc_funct_bp, & - xc_funct_hcth120, xc_funct_no_shortcut, xc_funct_olyp, xc_funct_pade, xc_funct_pbe, & - xc_funct_pbe0, xc_funct_tpss, xc_funct_xwpbe, xc_none, xc_vdw_fun_nonloc + do_qs, do_region_global, do_thermo_al, do_thermo_csvr, do_thermo_gle, do_thermo_nose, & + do_thermo_same_as_part, mimic_run, negf_run, npt_f_ensemble, npt_i_ensemble, & + npt_ia_ensemble, vdw_nl_LMKLL, xc_funct_b3lyp, xc_funct_beefvdw, xc_funct_blyp, & + xc_funct_bp, xc_funct_hcth120, xc_funct_no_shortcut, xc_funct_olyp, xc_funct_pade, & + xc_funct_pbe, xc_funct_pbe0, xc_funct_tpss, xc_funct_xwpbe, xc_none, xc_vdw_fun_nonloc USE input_keyword_types, ONLY: keyword_type USE input_parsing, ONLY: section_vals_parse USE input_section_types, ONLY: & @@ -69,9 +69,10 @@ CONTAINS CHARACTER(len=*), PARAMETER :: routineN = 'check_cp2k_input' - INTEGER :: handle, iforce_eval, nforce_eval, & - run_type - LOGICAL :: explicit, explicit_embed, explicit_mix + INTEGER :: force_eval_method, handle, iforce_eval, & + nforce_eval, run_type + LOGICAL :: apply_ext_potential, do_center, & + explicit, explicit_embed, explicit_mix TYPE(section_vals_type), POINTER :: section, section1, section2, section3, & section4, sections @@ -130,6 +131,30 @@ CONTAINS CALL xc_functionals_expand(section2, section1) END DO + ! additional checks for a MiMiC run + IF (run_type == mimic_run) THEN + ! disable CENTER_COORDINATES + CALL section_vals_val_get(sections, "SUBSYS%TOPOLOGY%CENTER_COORDINATES%_SECTION_PARAMETERS_", & + l_val=do_center) + IF (do_center) THEN + CALL section_vals_val_set(sections, & + "SUBSYS%TOPOLOGY%CENTER_COORDINATES%_SECTION_PARAMETERS_", & + l_val=.FALSE.) + CPWARN("Turning off CENTER_COORDINATES for a MiMiC run.") + END IF + + ! do not allow the use of external potential + section => section_vals_get_subs_vals(sections, "DFT%EXTERNAL_POTENTIAL") + CALL section_vals_get(section, explicit=apply_ext_potential) + IF (apply_ext_potential) & + CPABORT("The EXTERNAL_POTENTIAL section is not allowed for the MiMiC runtype.") + + ! force eval methods supported with MiMiC + CALL section_vals_val_get(sections, "METHOD", i_val=force_eval_method) + IF (force_eval_method /= do_qs) & + CPABORT("At the moment, only Quickstep method is supported with MiMiC.") + END IF + CALL timestop(handle) END SUBROUTINE check_cp2k_input diff --git a/src/input_cp2k_global.F b/src/input_cp2k_global.F index 24c632df0d..7e3342b0bd 100644 --- a/src/input_cp2k_global.F +++ b/src/input_cp2k_global.F @@ -53,8 +53,8 @@ MODULE input_cp2k_global do_fft_fftw3, do_fft_sg, do_opt_basis, do_optimize_input, do_scalapack, do_swarm, do_tamc, & do_test, do_tree_mc, do_tree_mc_ana, driver_run, ehrenfest, energy_force_run, energy_run, & fftw_plan_estimate, fftw_plan_exhaustive, fftw_plan_measure, fftw_plan_patient, gaussian, & - geo_opt_run, linear_response_run, mol_dyn_run, mon_car_run, negf_run, none_run, pint_run, & - real_time_propagation, tree_mc_run, vib_anal + geo_opt_run, linear_response_run, mimic_run, mol_dyn_run, mon_car_run, negf_run, none_run, & + pint_run, real_time_propagation, tree_mc_run, vib_anal USE input_keyword_types, ONLY: keyword_create,& keyword_release,& keyword_type @@ -396,13 +396,14 @@ CONTAINS "BAND", "CELL_OPT", "WFN_OPT", "WAVEFUNCTION_OPTIMIZATION", & "MOLECULAR_DYNAMICS", "GEOMETRY_OPTIMIZATION", "MONTECARLO", & "LINEAR_RESPONSE", "NORMAL_MODES", "RT_PROPAGATION", & - "EHRENFEST_DYN", "TAMC", "TMC", "DRIVER", "NEGF"), & + "EHRENFEST_DYN", "TAMC", "TMC", "DRIVER", "NEGF", "MIMIC"), & enum_i_vals=[none_run, energy_run, energy_force_run, mol_dyn_run, & geo_opt_run, mon_car_run, debug_run, & bsse_run, linear_response_run, pint_run, vib_anal, do_band, & cell_opt_run, energy_run, energy_run, mol_dyn_run, geo_opt_run, & mon_car_run, linear_response_run, & - vib_anal, real_time_propagation, ehrenfest, do_tamc, tree_mc_run, driver_run, negf_run], & + vib_anal, real_time_propagation, ehrenfest, do_tamc, tree_mc_run, & + driver_run, negf_run, mimic_run], & enum_desc=s2a("Perform no tasks", "Computes energy", "Computes energy and forces", & "Molecular Dynamics", "Geometry Optimization", "Monte Carlo", & "Performs a Debug analysis", "Basis set superposition error", "Linear Response", & @@ -415,7 +416,8 @@ CONTAINS "Temperature Accelerated Monte Carlo (TAMC)", & "Tree Monte Carlo (TMC), a pre-sampling MC algorithm", & "i-PI driver mode", & - "Non-equilibrium Green's function method")) + "Non-equilibrium Green's function method", & + "Run as a client in a simulation through the MiMiC framework")) CALL section_add_keyword(section, keyword) CALL keyword_release(keyword) diff --git a/src/mimic/PACKAGE b/src/mimic/PACKAGE new file mode 100644 index 0000000000..c98584b5bc --- /dev/null +++ b/src/mimic/PACKAGE @@ -0,0 +1,14 @@ +{ + "description": "Wrapper for the MiMiC routines", + "requires": [ + "mcl_wrapper", + "../", + "../base", + "../common", + "../input", + "../mpiwrap", + "../subsys", + "../pw", + "../pw_env", + ], +} diff --git a/src/mimic/mcl_wrapper/PACKAGE b/src/mimic/mcl_wrapper/PACKAGE new file mode 100644 index 0000000000..35c5f46b5f --- /dev/null +++ b/src/mimic/mcl_wrapper/PACKAGE @@ -0,0 +1,6 @@ +{ + "description": "Wrapper for the MCL routines", + "requires": [ + "../../base", + ], +} diff --git a/src/mimic/mcl_wrapper/mcl_api.F b/src/mimic/mcl_wrapper/mcl_api.F new file mode 100644 index 0000000000..8c287edcf2 --- /dev/null +++ b/src/mimic/mcl_wrapper/mcl_api.F @@ -0,0 +1,98 @@ +!--------------------------------------------------------------------------------------------------! +! CP2K: A general program to perform molecular dynamics simulations ! +! Copyright 2000-2025 CP2K developers group ! +! ! +! SPDX-License-Identifier: GPL-2.0-or-later ! +!--------------------------------------------------------------------------------------------------! + +! ************************************************************************************************** +!> \brief Wrapper module for MiMiC Communication Library (MCL) routines +!> \par History +!> 05.2025 Created [AA] +!> \author Andrej Antalik +! ************************************************************************************************** + +MODULE mcl_api + + USE kinds, ONLY: int_4, int_8, sp, dp +#if defined(__MIMIC) + USE mcl, ONLY: mcl_finalize, mcl_get_api_version, mcl_get_program_id, mcl_receive, mcl_send +#endif + +#include "../../base/base_uses.f90" + + IMPLICIT NONE + + #:include 'mcl_wrapper.fypp' + + PRIVATE + + PUBLIC :: mcl_finalize, mcl_get_api_version, mcl_get_program_id, mcl_receive, mcl_send + +#if !defined(__MIMIC) + + #:for direction in mcl_direction + INTERFACE mcl_${direction}$ + MODULE PROCEDURE mcl_${direction}$_char + #:for kind_l in mcl_kind_labels + #:for dim_l in mcl_dimension_labels + MODULE PROCEDURE mcl_${direction}$_${kind_l}$_${dim_l}$ + #:endfor + #:endfor + END INTERFACE + #:endfor + +CONTAINS + + SUBROUTINE mcl_finalize() + CPABORT("This CP2K executable has not been linked against MiMiC.") + END SUBROUTINE mcl_finalize + + SUBROUTINE mcl_get_api_version(version) + INTEGER, DIMENSION(3) :: version, dummy_version + CPABORT("This CP2K executable has not been linked against MiMiC.") + dummy_version = version + END SUBROUTINE mcl_get_api_version + + SUBROUTINE mcl_get_program_id(id) + INTEGER :: id, dummy_id + CPABORT("This CP2K executable has not been linked against MiMiC.") + dummy_id = id + END SUBROUTINE mcl_get_program_id + + #:for direction in mcl_direction + SUBROUTINE mcl_${direction}$_char(buffer, length, tag, remote_id, err) + CHARACTER(LEN=*), TARGET :: buffer + INTEGER :: length, tag, remote_id, dummy_integer + INTEGER, OPTIONAL, INTENT(OUT) :: err + CHARACTER(LEN=:), ALLOCATABLE :: dummy_char + CPABORT("This CP2K executable has not been linked against MiMiC.") + dummy_integer = length + dummy_integer = tag + dummy_integer = remote_id + err = dummy_integer + dummy_char = TRIM(buffer) + END SUBROUTINE mcl_${direction}$_char + + #:for kind_v, kind_l in zip(mcl_kinds, mcl_kind_labels) + #:for dim_v, dim_l in zip(mcl_dimensions, mcl_dimension_labels) + SUBROUTINE mcl_${direction}$_${kind_l}$_${dim_l}$ (buffer, length, tag, remote_id, err) + ${kind_v}$${dim_v}$, TARGET :: buffer + INTEGER :: length, tag, remote_id, dummy_integer + INTEGER, OPTIONAL, INTENT(OUT) :: err + ${kind_v}$${dim_v}$, POINTER :: dummy_buffer + CPABORT("This CP2K executable has not been linked against MiMiC.") + dummy_integer = length + dummy_integer = tag + dummy_integer = remote_id + err = dummy_integer + dummy_buffer => buffer + END SUBROUTINE mcl_${direction}$_${kind_l}$_${dim_l}$ + #:endfor + #:endfor + #:endfor + +#endif + +END MODULE mcl_api + diff --git a/src/mimic/mcl_wrapper/mcl_requests.F b/src/mimic/mcl_wrapper/mcl_requests.F new file mode 100644 index 0000000000..7634fefb8c --- /dev/null +++ b/src/mimic/mcl_wrapper/mcl_requests.F @@ -0,0 +1,42 @@ +!--------------------------------------------------------------------------------------------------! +! CP2K: A general program to perform molecular dynamics simulations ! +! Copyright 2000-2025 CP2K developers group ! +! ! +! SPDX-License-Identifier: GPL-2.0-or-later ! +!--------------------------------------------------------------------------------------------------! + +! ************************************************************************************************** +!> \brief Wrapper module for MiMiC Communication Library (MCL) request labels +!> \par History +!> 05.2025 Created [AA] +!> \author Andrej Antalik +! ************************************************************************************************** + +MODULE mcl_requests + + #:include 'mcl_wrapper.fypp' + +#if defined(__MIMIC) + #:for label in mcl_implemented_labels + USE mcl, ONLY: ${label}$ + #:endfor +#endif + + IMPLICIT NONE + + PRIVATE + + #:for label in mcl_implemented_labels + PUBLIC :: ${label}$ + #:endfor + +#if !defined(__MIMIC) + #:set req_value = -1 + #:for label in mcl_implemented_labels + #:set req_value = req_value - 1 + INTEGER, PARAMETER :: ${label}$ = ${req_value}$ + #:endfor +#endif + +END MODULE mcl_requests + diff --git a/src/mimic/mcl_wrapper/mcl_wrapper.fypp b/src/mimic/mcl_wrapper/mcl_wrapper.fypp new file mode 100644 index 0000000000..af89f86edc --- /dev/null +++ b/src/mimic/mcl_wrapper/mcl_wrapper.fypp @@ -0,0 +1,50 @@ +#!-------------------------------------------------------------------------------------------------! +#! CP2K: A general program to perform molecular dynamics simulations ! +#! Copyright 2000-2025 CP2K developers group ! +#! ! +#! SPDX-License-Identifier: GPL-2.0-or-later ! +#!-------------------------------------------------------------------------------------------------! +#:mute + #:set mcl_implemented_labels = [ & + "MCL_DATA", & + "MCL_LENGTH", & + "MCL_REQUEST", & + "MCL_EXIT", & + "MCL_SEND_CLIENT_ID", & + "MCL_SEND_CLIENT_NAME", & + "MCL_SEND_CLIENT_RUNTYPE", & + "MCL_SEND_CLIENT_APIVER", & + "MCL_RUNTYPE_QM_RS_GRID", & + "MCL_SEND_NUM_PARTICLES", & + "MCL_SEND_NUM_PARTICLE_SPECIES", & + "MCL_SEND_PARTICLE_SPECIES_IDS", & + "MCL_SEND_NUM_FRAGMENTS", & + "MCL_SEND_NUM_PARTICLES_IN_FRAGMENTS", & + "MCL_SEND_PARTICLE_IDS_IN_FRAGMENTS", & + "MCL_SEND_NUM_CONSTR_BONDS", & + "MCL_SEND_NUM_CONSTR_ANGLES", & + "MCL_SEND_PARTICLE_MULTIPOLES", & + "MCL_SEND_NUCLEAR_CHARGES", & + "MCL_SEND_SPECIES_MASSES", & + "MCL_SEND_SPECIES_ELEMENTS", & + "MCL_SEND_SPECIES_LABELS", & + "MCL_SEND_PARTICLE_POSITIONS", & + "MCL_RECV_PARTICLE_POSITIONS", & + "MCL_SEND_PARTICLE_FORCES", & + "MCL_SEND_ENERGY", & + "MCL_SEND_BOX_ORIGIN", & + "MCL_SEND_BOX_VECTORS", & + "MCL_SEND_BOX_NUM_GRIDPOINTS", & + "MCL_SEND_BOX_GRIDPOINT_COORDS", & + "MCL_SEND_DENSITY", & + "MCL_RECV_POTENTIAL_ON_GRIDPOINTS", & + "MCL_SEND_REFERENCE_CHARGES", & + "MCL_COMPUTE_FORCES"] + + #:set mcl_direction = ["send", "receive"] + #:set mcl_kinds = ["INTEGER(KIND=int_4)", "INTEGER(KIND=int_8)", "REAL(KIND=sp)", "REAL(KIND=dp)"] + #:set mcl_kind_labels = ["i4", "i8", "sp", "dp"] + #:set mcl_dimensions = ["", ", DIMENSION(:)", ", DIMENSION(:,:)", ", DIMENSION(:,:,:)"] + #:set mcl_dimension_labels = ["0d", "1d", "2d", "3d"] +#:endmute + diff --git a/src/mimic/mimic_communicator.F b/src/mimic/mimic_communicator.F new file mode 100644 index 0000000000..2d96ca85f6 --- /dev/null +++ b/src/mimic/mimic_communicator.F @@ -0,0 +1,711 @@ +!--------------------------------------------------------------------------------------------------! +! CP2K: A general program to perform molecular dynamics simulations ! +! Copyright 2000-2025 CP2K developers group ! +! ! +! SPDX-License-Identifier: GPL-2.0-or-later ! +!--------------------------------------------------------------------------------------------------! + +! ************************************************************************************************** +!> \brief Module containing a MiMiC communicator class +!> \par History +!> 05.2025 Created [AA] +!> \author Andrej Antalik +! ************************************************************************************************** + +MODULE mimic_communicator + + USE atomic_kind_list_types, ONLY: atomic_kind_list_type + USE atomic_kind_types, ONLY: get_atomic_kind + USE cp_control_types, ONLY: dft_control_type + USE cp_result_methods, ONLY: get_results + USE cp_result_types, ONLY: cp_result_type + USE cp_subsys_types, ONLY: cp_subsys_get,& + cp_subsys_type + USE cp_units, ONLY: cp_unit_from_cp2k + USE force_env_types, ONLY: force_env_get,& + force_env_type + USE kinds, ONLY: default_string_length,& + dp + USE mcl_api, ONLY: mcl_finalize,& + mcl_get_api_version,& + mcl_get_program_id,& + mcl_receive,& + mcl_send + USE mcl_requests, ONLY: MCL_DATA,& + MCL_LENGTH,& + MCL_REQUEST,& + MCL_RUNTYPE_QM_RS_GRID + USE message_passing, ONLY: mp_para_env_type + USE particle_list_types, ONLY: particle_list_type + USE pw_env_types, ONLY: pw_env_get,& + pw_env_type + USE pw_pool_types, ONLY: pw_pool_type + USE pw_types, ONLY: pw_r3d_rs_type + USE qs_energy_types, ONLY: qs_energy_type + USE qs_environment_types, ONLY: get_qs_env,& + qs_environment_type,& + set_qs_env + USE qs_kind_types, ONLY: get_qs_kind,& + qs_kind_type + USE qs_ks_types, ONLY: qs_ks_env_type,& + set_ks_env + USE qs_rho_types, ONLY: qs_rho_get,& + qs_rho_type +#include "../base/base_uses.f90" + + IMPLICIT NONE + + PRIVATE + + CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'mimic_communicator' + +! ************************************************************************************************** +!> \brief MiMiC communicator class that facilitates MiMiC client-server data exchange +!> \par History +!> 05.2025 Created [AA] +! ************************************************************************************************** + TYPE, PUBLIC :: mimic_communicator_type + PRIVATE + !> communication + TYPE(mp_para_env_type), POINTER :: para_env => Null() + LOGICAL :: is_ionode + INTEGER :: mcl_server = 0, & + client_id = -1 + !> CP2K data + TYPE(force_env_type), POINTER :: force_env => Null() + TYPE(pw_pool_type), POINTER :: pw_info => Null() + TYPE(particle_list_type), POINTER :: atoms => Null() + TYPE(atomic_kind_list_type), POINTER :: kinds => Null() + TYPE(qs_energy_type), POINTER :: energy => Null() + TYPE(pw_r3d_rs_type), POINTER :: potential => Null() + TYPE(qs_rho_type), POINTER :: density => Null() + INTEGER :: n_atoms = -1, & + n_kinds = -1, & + n_spins = -1 + INTEGER, DIMENSION(:, :), ALLOCATABLE :: npts_pproc + !> beginning index of the local buffer in the global buffer diminished by 1 + INTEGER, DIMENSION(:), ALLOCATABLE :: lb_pproc + + CONTAINS + + PROCEDURE :: initialize + PROCEDURE :: finalize + PROCEDURE :: receive_request + PROCEDURE :: send_value + PROCEDURE :: send_client_info + PROCEDURE :: send_atom_info + PROCEDURE :: send_kind_info + PROCEDURE :: send_box_info + PROCEDURE :: send_result + PROCEDURE :: send_grid_coordinates + PROCEDURE :: send_density + PROCEDURE :: send_forces + PROCEDURE :: send_positions + PROCEDURE :: receive_positions + PROCEDURE :: receive_potential + + END TYPE mimic_communicator_type + +CONTAINS + +! ************************************************************************************************** +!> \brief Initialize the communicator by loading data and saving pointers to relevant data +!> \param this ... +!> \param force_env ... +!> \par History +!> 05.2025 Created [AA] +! ************************************************************************************************** + SUBROUTINE initialize(this, force_env) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + TYPE(force_env_type), TARGET :: force_env + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':initialize' + + TYPE(cp_subsys_type), POINTER :: subsys + TYPE(dft_control_type), POINTER :: dft_control + TYPE(pw_env_type), POINTER :: pw_env + TYPE(qs_environment_type), POINTER :: qs_env + TYPE(qs_ks_env_type), POINTER :: ks_env + INTEGER :: handle + + CALL timeset(routineN, handle) + + CALL mcl_get_program_id(this%client_id) + + NULLIFY (subsys, qs_env, ks_env, pw_env) + this%force_env => force_env + CALL force_env_get(this%force_env, subsys=subsys, para_env=this%para_env, qs_env=qs_env) + CALL cp_subsys_get(subsys, natom=this%n_atoms, particles=this%atoms, & + nkind=this%n_kinds, atomic_kinds=this%kinds) + CALL get_qs_env(qs_env, energy=this%energy, vee=this%potential, rho=this%density, & + dft_control=dft_control, ks_env=ks_env, pw_env=pw_env) + CALL pw_env_get(pw_env, auxbas_pw_pool=this%pw_info) + + this%is_ionode = this%para_env%is_source() + + ALLOCATE (this%npts_pproc(3, 0:this%para_env%num_pe - 1), source=0) + this%npts_pproc(:, this%para_env%mepos) = this%pw_info%pw_grid%npts_local + CALL this%para_env%sum(this%npts_pproc) + + ALLOCATE (this%lb_pproc(0:this%para_env%num_pe - 1), source=0) + this%lb_pproc(this%para_env%mepos) = this%pw_info%pw_grid%bounds_local(1, 1) & + - this%pw_info%pw_grid%bounds(1, 1) + CALL this%para_env%sum(this%lb_pproc) + + this%n_spins = dft_control%nspins + + CALL set_qs_env(qs_env, mimic=.TRUE.) + dft_control%apply_external_potential = .TRUE. + dft_control%eval_external_potential = .FALSE. + + ! allocate external electrostatic potential + IF (ASSOCIATED(this%potential)) THEN + CALL this%potential%release() + DEALLOCATE (this%potential) + END IF + ALLOCATE (this%potential) + CALL this%pw_info%create_pw(this%potential) + CALL set_ks_env(ks_env, vee=this%potential) + + CALL timestop(handle) + + END SUBROUTINE initialize + +! ************************************************************************************************** +!> \brief Finalize the simulation by deallocating memory +!> \param this ... +! ************************************************************************************************** + SUBROUTINE finalize(this) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':finalize' + + INTEGER :: handle + + CALL timeset(routineN, handle) + + CALL this%para_env%sync() + + CALL mcl_finalize() + + CALL timestop(handle) + + END SUBROUTINE finalize + +! ************************************************************************************************** +!> \brief Receive a request from the server +!> \param this ... +!> \return ... +! ************************************************************************************************** + FUNCTION receive_request(this) RESULT(request) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + INTEGER :: request + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':receive_request' + + INTEGER :: handle + + CALL timeset(routineN, handle) + + request = -1 + CALL mcl_receive(request, 1, MCL_REQUEST, this%mcl_server) + CALL this%para_env%bcast(request) + + CALL timestop(handle) + + END FUNCTION receive_request + +! ************************************************************************************************** +!> \brief Send the specified single value data to the server +!> \param this ... +!> \param option word corresponding to available options +!> \note Several values hardcoded for now +! ************************************************************************************************** + SUBROUTINE send_value(this, option) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + CHARACTER(LEN=*) :: option + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_value' + + REAL(dp) :: energy + INTEGER :: handle + + CALL timeset(routineN, handle) + + SELECT CASE (option) + CASE ("num_atoms", "num_atoms_in_fragments") + CALL mcl_send(this%n_atoms, 1, MCL_DATA, this%mcl_server) + CASE ("num_kinds") + CALL mcl_send(this%n_kinds, 1, MCL_DATA, this%mcl_server) + CASE ("num_fragments") + CALL mcl_send(1, 1, MCL_DATA, this%mcl_server) + CASE ("num_bonds") ! later use to communicate constraints + CALL mcl_send(0, 1, MCL_DATA, this%mcl_server) + CASE ("num_angles") ! later use to communicate constraints + CALL mcl_send(0, 1, MCL_DATA, this%mcl_server) + CASE ("energy") + energy = this%energy%total - this%energy%ee + CALL mcl_send(energy, 1, MCL_DATA, this%mcl_server) + CASE DEFAULT + CPABORT("The value chosen in "//routineN//" is not implemented.") + END SELECT + + CALL timestop(handle) + + END SUBROUTINE send_value + +! ************************************************************************************************** +!> \brief Send the specified information about the client to the server +!> \param this ... +!> \param option word corresponding to available options +! ************************************************************************************************** + SUBROUTINE send_client_info(this, option) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + CHARACTER(LEN=*) :: option + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_client_info' + + CHARACTER(LEN=*), PARAMETER :: client_name = "CP2K" + INTEGER, DIMENSION(3) :: api_version + INTEGER :: handle, length + + CALL timeset(routineN, handle) + + SELECT CASE (option) + CASE ("id") + CALL mcl_send(this%client_id, 1, MCL_DATA, this%mcl_server) + CASE ("name") + length = LEN(client_name) + CALL mcl_send(length, 1, MCL_LENGTH, this%mcl_server) + CALL mcl_send(client_name, length, MCL_DATA, this%mcl_server) + CASE ("run_type") + CALL mcl_send(MCL_RUNTYPE_QM_RS_GRID, 1, MCL_DATA, this%mcl_server) + CASE ("api_version") + CALL mcl_get_api_version(api_version) + CALL mcl_send(api_version, 3, MCL_DATA, this%mcl_server) + CASE DEFAULT + CPABORT("The value chosen in "//routineN//" is not implemented.") + END SELECT + + CALL timestop(handle) + + END SUBROUTINE send_client_info + +! ************************************************************************************************** +!> \brief Send the specified data for each atom to the server +!> \param this ... +!> \param option word corresponding to available options +! ************************************************************************************************** + SUBROUTINE send_atom_info(this, option) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + CHARACTER(LEN=*) :: option + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_atom_info' + + INTEGER, DIMENSION(:), ALLOCATABLE :: buffer + INTEGER :: handle, i + + CALL timeset(routineN, handle) + + ALLOCATE (buffer(this%n_atoms)) + SELECT CASE (option) + CASE ("kinds") + DO i = 1, this%n_atoms + buffer(i) = this%atoms%els(i)%atomic_kind%kind_number + END DO + CASE ("ids") + DO i = 1, this%n_atoms + buffer(i) = this%atoms%els(i)%atom_index + END DO + CASE DEFAULT + CPABORT("The value chosen in "//routineN//" is not implemented.") + END SELECT + CALL mcl_send(buffer, SIZE(buffer), MCL_DATA, this%mcl_server) + + CALL timestop(handle) + + END SUBROUTINE send_atom_info + +! ************************************************************************************************** +!> \brief Send the specified data for each kind to the server +!> \param this ... +!> \param option word corresponding to available options +! ************************************************************************************************** + SUBROUTINE send_kind_info(this, option) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + CHARACTER(LEN=*) :: option + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_kind_info' + + TYPE(qs_environment_type), POINTER :: qs_env + TYPE(qs_kind_type), DIMENSION(:), POINTER :: qs_kinds + REAL(dp), DIMENSION(:), ALLOCATABLE :: buffer_dp + INTEGER, DIMENSION(:), ALLOCATABLE :: buffer_i + CHARACTER(LEN=:), ALLOCATABLE :: labels + CHARACTER(LEN=default_string_length) :: label + INTEGER :: handle, length, i + + CALL timeset(routineN, handle) + + SELECT CASE (option) + CASE ("labels") + ALLOCATE (CHARACTER(30*this%n_kinds) :: labels) + labels = "" + DO i = 1, this%n_kinds + CALL get_atomic_kind(this%kinds%els(i), name=label) + labels = TRIM(labels)//TRIM(label)//"," + END DO + length = LEN(TRIM(labels)) - 1 + CALL mcl_send(length, 1, MCL_LENGTH, this%mcl_server) + CALL mcl_send(labels, length, MCL_DATA, this%mcl_server) + CASE ("elements") + ALLOCATE (buffer_i(this%n_kinds)) + DO i = 1, this%n_kinds + CALL get_atomic_kind(this%kinds%els(i), z=buffer_i(i)) + END DO + CALL mcl_send(buffer_i, SIZE(buffer_i), MCL_DATA, this%mcl_server) + CASE ("masses") + ALLOCATE (buffer_dp(this%n_kinds)) + DO i = 1, this%n_kinds + buffer_dp(i) = cp_unit_from_cp2k(this%kinds%els(i)%mass, "AMU") + END DO + CALL mcl_send(buffer_dp, SIZE(buffer_dp), MCL_DATA, this%mcl_server) + CASE ("nuclear_charges") + NULLIFY (qs_env, qs_kinds) + CALL force_env_get(this%force_env, qs_env=qs_env) + CALL get_qs_env(qs_env, qs_kind_set=qs_kinds) + ALLOCATE (buffer_dp(this%n_kinds)) + DO i = 1, this%n_kinds + CALL get_qs_kind(qs_kinds(i), zeff=buffer_dp(i)) + END DO + CALL mcl_send(buffer_dp, SIZE(buffer_dp), MCL_DATA, this%mcl_server) + CASE DEFAULT + CPABORT("The value chosen in "//routineN//" is not implemented.") + END SELECT + + CALL timestop(handle) + + END SUBROUTINE send_kind_info + +! ************************************************************************************************** +!> \brief Send the specified box information to the server +!> \param this ... +!> \param option word corresponding to available options +! ************************************************************************************************** + SUBROUTINE send_box_info(this, option) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + CHARACTER(LEN=*) :: option + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_box_info' + + INTEGER, DIMENSION(3) :: npts_glob + REAL(dp), DIMENSION(3) :: origin + REAL(dp), DIMENSION(9) :: box_vectors + INTEGER :: handle, i + + CALL timeset(routineN, handle) + + npts_glob = this%pw_info%pw_grid%npts + + SELECT CASE (option) + CASE ("num_gridpoints") + CALL mcl_send(npts_glob, 3, MCL_DATA, this%mcl_server) + CASE ("origin") + origin = 0.0_dp + CALL mcl_send(origin, 3, MCL_DATA, this%mcl_server) + CASE ("box_vectors") + box_vectors = [(this%pw_info%pw_grid%dh(:, i)*REAL(npts_glob(i), dp), i=1, 3)] + CALL mcl_send(box_vectors, 9, MCL_DATA, this%mcl_server) + CASE DEFAULT + CPABORT("The value chosen in "//routineN//" is not implemented.") + END SELECT + + CALL timestop(handle) + + END SUBROUTINE send_box_info + +! ************************************************************************************************** +!> \brief Send the specified result to the server +!> \param this ... +!> \param option word corresponding to available options +! ************************************************************************************************** + SUBROUTINE send_result(this, option) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + CHARACTER(LEN=*) :: option + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_result' + + TYPE(qs_environment_type), POINTER :: qs_env + TYPE(cp_result_type), POINTER :: results + CHARACTER(LEN=default_string_length) :: description + REAL(KIND=dp), DIMENSION(:), ALLOCATABLE :: buffer + INTEGER :: handle + + CALL timeset(routineN, handle) + + NULLIFY (qs_env, results) + CALL force_env_get(this%force_env, qs_env=qs_env) + CALL get_qs_env(qs_env, results=results) + + SELECT CASE (option) + CASE ("hirshfeld_charges") + description = "[HIRSHFELD-CHARGES]" + ALLOCATE (buffer(this%n_atoms), source=0.0_dp) + CALL get_results(results, description, buffer) + CALL mcl_send(buffer, SIZE(buffer), MCL_DATA, this%mcl_server) + CASE DEFAULT + CPABORT("The value chosen in "//routineN//" is not implemented.") + END SELECT + + CALL timestop(handle) + + END SUBROUTINE send_result + +! ************************************************************************************************** +!> \brief Send grid point coordinates to the server +!> \param this ... +! ************************************************************************************************** + SUBROUTINE send_grid_coordinates(this) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_grid_coordinates' + + INTEGER, DIMENSION(3) :: npts_glob, npts, lb_glob, lb, ub + REAL(dp), DIMENSION(3) :: origin + REAL(dp), DIMENSION(3, 3) :: box_vectors + REAL(dp), DIMENSION(:, :), ALLOCATABLE :: coords + INTEGER :: handle, i, j, k, offset + + CALL timeset(routineN, handle) + + origin = 0.0_dp + box_vectors = this%pw_info%pw_grid%dh + ! number of grid points + npts_glob = this%pw_info%pw_grid%npts + npts = this%pw_info%pw_grid%npts_local + ! bounds + lb_glob = this%pw_info%pw_grid%bounds(1, :) + lb = this%pw_info%pw_grid%bounds_local(1, :) + ub = this%pw_info%pw_grid%bounds_local(2, :) + + ALLOCATE (coords(3, PRODUCT(npts_glob)), source=0.0_dp) + offset = (lb(1) - lb_glob(1))*PRODUCT(npts(2:)) + DO k = lb(3), ub(3) + DO j = lb(2), ub(2) + DO i = lb(1), ub(1) + offset = offset + 1 + coords(:, offset) = origin + box_vectors(:, 1)*REAL(i - lb_glob(1), dp) & + + box_vectors(:, 2)*REAL(j - lb_glob(2), dp) & + + box_vectors(:, 3)*REAL(k - lb_glob(3), dp) + END DO + END DO + END DO + CALL this%para_env%sum(coords) + + CALL mcl_send(coords, SIZE(coords), MCL_DATA, this%mcl_server) + + CALL timestop(handle) + + END SUBROUTINE send_grid_coordinates + +! ************************************************************************************************** +!> \brief Receive external potential from the server +!> \param this ... +! ************************************************************************************************** + SUBROUTINE receive_potential(this) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':receive_potential' + + INTEGER, DIMENSION(3) :: npts, lb, ub + REAL(dp), DIMENSION(:), ALLOCATABLE, TARGET :: buffer + REAL(dp), DIMENSION(:), ALLOCATABLE :: buffer_loc + REAL(dp), DIMENSION(:), POINTER :: buffer_p + INTEGER :: i, j, k, i_proc, offset + INTEGER :: handle, length, tag + + CALL timeset(routineN, handle) + + NULLIFY (buffer_p) + npts = this%pw_info%pw_grid%npts_local + lb = this%pw_info%pw_grid%bounds_local(1, :) + ub = this%pw_info%pw_grid%bounds_local(2, :) + ALLOCATE (buffer_loc(PRODUCT(npts))) + + tag = 1 + + IF (this%is_ionode) THEN + ALLOCATE (buffer(PRODUCT(this%pw_info%pw_grid%npts))) + ! receive potential at the IO process + CALL mcl_receive(buffer, SIZE(buffer), MCL_DATA, this%mcl_server) + ! distribute across processes + DO i_proc = 0, this%para_env%num_pe - 1 + length = PRODUCT(this%npts_pproc(:, i_proc)) + offset = this%lb_pproc(i_proc)*PRODUCT(npts(2:)) + 1 + buffer_p => buffer(offset:offset + length - 1) + IF (i_proc /= this%para_env%source) THEN + i = i_proc + CALL this%para_env%send(buffer_p, i, tag) + ELSE + buffer_loc(:) = buffer_p + END IF + END DO + ELSE + CALL this%para_env%recv(buffer_loc, this%para_env%source, tag) + END IF + + ! set the potential + offset = 0 + DO k = lb(3), ub(3) + DO j = lb(2), ub(2) + DO i = lb(1), ub(1) + offset = offset + 1 + this%potential%array(i, j, k) = -buffer_loc(offset) + END DO + END DO + END DO + + CALL timestop(handle) + + END SUBROUTINE receive_potential + +! ************************************************************************************************** +!> \brief Send electron density to the server +!> \param this ... +! ************************************************************************************************** + SUBROUTINE send_density(this) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_density' + + INTEGER, DIMENSION(3) :: npts, lb, ub + TYPE(pw_r3d_rs_type), DIMENSION(:), POINTER :: rho + REAL(dp), DIMENSION(:), ALLOCATABLE, TARGET :: buffer + REAL(dp), DIMENSION(:), ALLOCATABLE :: buffer_loc + REAL(dp), DIMENSION(:), POINTER :: buffer_p + INTEGER :: i_spin, i_proc, i, j, k, offset + INTEGER :: handle, length, tag + + CALL timeset(routineN, handle) + + NULLIFY (rho, buffer_p) + CALL qs_rho_get(this%density, rho_r=rho) + npts = this%pw_info%pw_grid%npts_local + lb = this%pw_info%pw_grid%bounds_local(1, :) + ub = this%pw_info%pw_grid%bounds_local(2, :) + ALLOCATE (buffer_loc(PRODUCT(npts))) + + ! gather density values + buffer_loc = 0.0_dp + DO i_spin = 1, this%n_spins + offset = 0 + DO k = lb(3), ub(3) + DO j = lb(2), ub(2) + DO i = lb(1), ub(1) + offset = offset + 1 + buffer_loc(offset) = buffer_loc(offset) + rho(i_spin)%array(i, j, k) + END DO + END DO + END DO + END DO + + tag = 1 + + IF (.NOT. this%is_ionode) THEN + CALL this%para_env%send(buffer_loc, this%para_env%source, tag) + ELSE + ALLOCATE (buffer(PRODUCT(this%pw_info%pw_grid%npts))) + ! collect from the processes at the IO process + DO i_proc = 0, this%para_env%num_pe - 1 + length = PRODUCT(this%npts_pproc(:, i_proc)) + offset = this%lb_pproc(i_proc)*PRODUCT(npts(2:)) + 1 + buffer_p => buffer(offset:offset + length - 1) + IF (i_proc /= this%para_env%source) THEN + i = i_proc + CALL this%para_env%recv(buffer_p, i, tag) + ELSE + buffer_p = buffer_loc + END IF + END DO + ! send the density + CALL mcl_send(buffer, SIZE(buffer), MCL_DATA, this%mcl_server) + END IF + + CALL timestop(handle) + + END SUBROUTINE send_density + +! ************************************************************************************************** +!> \brief Send positions of all atoms to the server +!> \param this ... +! ************************************************************************************************** + SUBROUTINE send_positions(this) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_positions' + + REAL(dp), DIMENSION(:, :), ALLOCATABLE :: buffer + INTEGER :: handle, i_atom + + CALL timeset(routineN, handle) + + ALLOCATE (buffer(3, this%n_atoms)) + DO i_atom = 1, this%n_atoms + buffer(:, i_atom) = this%atoms%els(i_atom)%r + END DO + CALL mcl_send(buffer, SIZE(buffer), MCL_DATA, this%mcl_server) + + CALL timestop(handle) + + END SUBROUTINE send_positions + +! ************************************************************************************************** +!> \brief Receive positions of all atoms from the server +!> \param this ... +! ************************************************************************************************** + SUBROUTINE receive_positions(this) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':receive_positions' + + REAL(dp), DIMENSION(:, :), ALLOCATABLE :: buffer + INTEGER :: handle, i_atom + + CALL timeset(routineN, handle) + + ALLOCATE (buffer(3, this%n_atoms)) + CALL mcl_receive(buffer, SIZE(buffer), MCL_DATA, this%mcl_server) + CALL this%para_env%bcast(buffer) + DO i_atom = 1, this%n_atoms + this%atoms%els(i_atom)%r = buffer(:, i_atom) + END DO + + CALL timestop(handle) + + END SUBROUTINE receive_positions + +! ************************************************************************************************** +!> \brief Send QM forces of all atoms to the server +!> \param this ... +! ************************************************************************************************** + SUBROUTINE send_forces(this) + CLASS(mimic_communicator_type), INTENT(INOUT) :: this + + CHARACTER(LEN=*), PARAMETER :: routineN = moduleN//':send_forces' + + REAL(dp), DIMENSION(:, :), ALLOCATABLE :: buffer + INTEGER :: handle, i_atom + + CALL timeset(routineN, handle) + + ALLOCATE (buffer(3, this%n_atoms)) + DO i_atom = 1, this%n_atoms + buffer(:, i_atom) = this%atoms%els(i_atom)%f + END DO + CALL mcl_send(buffer, SIZE(buffer), MCL_DATA, this%mcl_server) + + CALL timestop(handle) + + END SUBROUTINE send_forces + +END MODULE mimic_communicator diff --git a/src/mimic/mimic_loop.F b/src/mimic/mimic_loop.F new file mode 100644 index 0000000000..cb50950265 --- /dev/null +++ b/src/mimic/mimic_loop.F @@ -0,0 +1,139 @@ +!--------------------------------------------------------------------------------------------------! +! CP2K: A general program to perform molecular dynamics simulations ! +! Copyright 2000-2025 CP2K developers group ! +! ! +! SPDX-License-Identifier: GPL-2.0-or-later ! +!--------------------------------------------------------------------------------------------------! + +! ************************************************************************************************** +!> \brief Module containing the main loop for simulations with the MiMiC framework +!> \par History +!> 05.2025 Created [AA] +!> \author Andrej Antalik +! ************************************************************************************************** + +MODULE mimic_loop + + USE force_env_methods, ONLY: force_env_calc_energy_force + USE force_env_types, ONLY: force_env_type + USE mcl_requests, ONLY: & + MCL_COMPUTE_FORCES, MCL_EXIT, MCL_RECV_PARTICLE_POSITIONS, & + MCL_RECV_POTENTIAL_ON_GRIDPOINTS, MCL_SEND_BOX_GRIDPOINT_COORDS, & + MCL_SEND_BOX_NUM_GRIDPOINTS, MCL_SEND_BOX_ORIGIN, MCL_SEND_BOX_VECTORS, & + MCL_SEND_CLIENT_APIVER, MCL_SEND_CLIENT_ID, MCL_SEND_CLIENT_NAME, MCL_SEND_CLIENT_RUNTYPE, & + MCL_SEND_DENSITY, MCL_SEND_ENERGY, MCL_SEND_NUCLEAR_CHARGES, MCL_SEND_NUM_CONSTR_ANGLES, & + MCL_SEND_NUM_CONSTR_BONDS, MCL_SEND_NUM_FRAGMENTS, MCL_SEND_NUM_PARTICLES, & + MCL_SEND_NUM_PARTICLES_IN_FRAGMENTS, MCL_SEND_NUM_PARTICLE_SPECIES, & + MCL_SEND_PARTICLE_FORCES, MCL_SEND_PARTICLE_IDS_IN_FRAGMENTS, MCL_SEND_PARTICLE_POSITIONS, & + MCL_SEND_PARTICLE_SPECIES_IDS, MCL_SEND_REFERENCE_CHARGES, MCL_SEND_SPECIES_ELEMENTS, & + MCL_SEND_SPECIES_LABELS, MCL_SEND_SPECIES_MASSES + USE mimic_communicator, ONLY: mimic_communicator_type +#include "../base/base_uses.f90" + + IMPLICIT NONE + + PRIVATE + + PUBLIC :: do_mimic_loop + +CONTAINS + +! ************************************************************************************************** +!> \brief The main loop for a MiMiC run +!> \param force_env ... +!> \par History +!> 5.2025 created [AA] +! ************************************************************************************************** + SUBROUTINE do_mimic_loop(force_env) + TYPE(force_env_type), POINTER :: force_env + + CHARACTER(LEN=*), PARAMETER :: routineN = 'do_mimic_loop' + + CHARACTER(LEN=32) :: request_str + INTEGER :: handle, request + LOGICAL :: is_last_step + TYPE(mimic_communicator_type) :: mimic_comm + + CALL timeset(routineN, handle) + + CPASSERT(ASSOCIATED(force_env)) + + CALL mimic_comm%initialize(force_env) + + is_last_step = .FALSE. + DO WHILE (.NOT. is_last_step) + request = mimic_comm%receive_request() + + SELECT CASE (request) + CASE (MCL_SEND_CLIENT_ID) + CALL mimic_comm%send_client_info("id") + CASE (MCL_SEND_CLIENT_NAME) + CALL mimic_comm%send_client_info("name") + CASE (MCL_SEND_CLIENT_RUNTYPE) + CALL mimic_comm%send_client_info("run_type") + CASE (MCL_SEND_CLIENT_APIVER) + CALL mimic_comm%send_client_info("api_version") + CASE (MCL_SEND_NUM_PARTICLES) + CALL mimic_comm%send_value("num_atoms") + CASE (MCL_SEND_NUM_PARTICLE_SPECIES) + CALL mimic_comm%send_value("num_kinds") + CASE (MCL_SEND_PARTICLE_SPECIES_IDS) + CALL mimic_comm%send_atom_info("kinds") + CASE (MCL_SEND_NUM_FRAGMENTS) + CALL mimic_comm%send_value("num_fragments") + CASE (MCL_SEND_NUM_PARTICLES_IN_FRAGMENTS) + CALL mimic_comm%send_value("num_atoms_in_fragments") + CASE (MCL_SEND_NUM_CONSTR_BONDS) + CALL mimic_comm%send_value("num_bonds") + CASE (MCL_SEND_NUM_CONSTR_ANGLES) + CALL mimic_comm%send_value("num_angles") + CASE (MCL_SEND_SPECIES_MASSES) + CALL mimic_comm%send_kind_info("masses") + CASE (MCL_SEND_PARTICLE_IDS_IN_FRAGMENTS) + CALL mimic_comm%send_atom_info("ids") + CASE (MCL_SEND_SPECIES_ELEMENTS) + CALL mimic_comm%send_kind_info("elements") + CASE (MCL_SEND_SPECIES_LABELS) + CALL mimic_comm%send_kind_info("labels") + CASE (MCL_SEND_PARTICLE_POSITIONS) + CALL mimic_comm%send_positions() + CASE (MCL_RECV_PARTICLE_POSITIONS) + CALL mimic_comm%receive_positions() + CASE (MCL_SEND_PARTICLE_FORCES) + CALL mimic_comm%send_forces() + CASE (MCL_SEND_ENERGY) + CALL mimic_comm%send_value("energy") + CASE (MCL_SEND_NUCLEAR_CHARGES) + CALL mimic_comm%send_kind_info("nuclear_charges") + CASE (MCL_SEND_BOX_NUM_GRIDPOINTS) + CALL mimic_comm%send_box_info("num_gridpoints") + CASE (MCL_SEND_BOX_ORIGIN) + CALL mimic_comm%send_box_info("origin") + CASE (MCL_SEND_BOX_VECTORS) + CALL mimic_comm%send_box_info("box_vectors") + CASE (MCL_SEND_BOX_GRIDPOINT_COORDS) + CALL mimic_comm%send_grid_coordinates() + CASE (MCL_SEND_DENSITY) + CALL mimic_comm%send_density() + CASE (MCL_RECV_POTENTIAL_ON_GRIDPOINTS) + CALL mimic_comm%receive_potential() + CASE (MCL_SEND_REFERENCE_CHARGES) + CALL mimic_comm%send_result("hirshfeld_charges") + CASE (MCL_COMPUTE_FORCES) + CALL force_env_calc_energy_force(force_env, calc_force=.TRUE.) + CASE (MCL_EXIT) + is_last_step = .TRUE. + CASE DEFAULT + WRITE (request_str, "(I0)") request + CPABORT("Unrecognized MiMiC request: "//TRIM(request_str)) + END SELECT + END DO + + CALL mimic_comm%finalize() + + CALL timestop(handle) + + END SUBROUTINE do_mimic_loop + +END MODULE mimic_loop + diff --git a/src/mpiwrap/message_passing.F b/src/mpiwrap/message_passing.F index 23f87b0c5b..17adf514d3 100644 --- a/src/mpiwrap/message_passing.F +++ b/src/mpiwrap/message_passing.F @@ -27,6 +27,9 @@ MODULE message_passing real_8_size, default_string_length USE machine, ONLY: m_abort USE mp_perf_env, ONLY: add_perf, add_mp_perf_env, rm_mp_perf_env +#if defined(__MIMIC) + USE mcl, ONLY: mcl_initialize, mcl_is_initialized, mcl_abort +#endif #include "../base/base_uses.f90" @@ -144,6 +147,11 @@ MODULE message_passing PUBLIC :: mp_para_env_create, mp_para_env_release, & mp_para_cart_create, mp_para_cart_release +#if defined(__MIMIC) + ! Stores the split world communicator to finalize a MiMiC run + MPI_COMM_TYPE, PRIVATE, SAVE :: mimic_comm_world +#endif + TYPE mp_comm_type PRIVATE MPI_COMM_TYPE :: handle = mp_comm_null_handle @@ -1076,6 +1084,9 @@ CONTAINS CLASS(mp_comm_type), INTENT(OUT) :: mp_comm #if defined(__parallel) INTEGER :: ierr, provided_tsl +#if defined(__MIMIC) + INTEGER :: mimic_handle +#endif !$OMP MASTER #if defined(__DLAF) @@ -1099,6 +1110,16 @@ CONTAINS #endif debug_comm_count = 1 mp_comm = mp_comm_world +#if defined(__MIMIC) + mimic_handle = mp_comm%get_handle() + CALL mcl_initialize(mimic_handle) + CALL mp_comm%set_handle(mimic_handle) +#if defined(__MPI_F08) + mimic_comm_world%mpi_val = mimic_handle +#else + mimic_comm_world = mimic_handle +#endif +#endif CALL mp_comm%init() CALL add_mp_perf_env() END SUBROUTINE mp_world_init @@ -1164,7 +1185,11 @@ CONTAINS CHARACTER(LEN=default_string_length) :: debug_comm_count_char #if defined(__parallel) INTEGER :: ierr +#if defined(__MIMIC) + CALL mpi_barrier(mimic_comm_world, ierr) +#else CALL mpi_barrier(MPI_COMM_WORLD, ierr) ! call mpi directly to avoid 0 stack pointer +#endif #endif CALL rm_mp_perf_env() @@ -1194,11 +1219,18 @@ CONTAINS ! ************************************************************************************************** SUBROUTINE mp_abort() INTEGER :: ierr +#if defined(__MIMIC) + LOGICAL :: mcl_initialized +#endif ierr = 0 #if !defined(__NO_ABORT) #if defined(__parallel) +#if defined(__MIMIC) + CALL mcl_is_initialized(mcl_initialized) + IF (mcl_initialized) CALL mcl_abort(1, ierr) +#endif CALL mpi_abort(MPI_COMM_WORLD, 1, ierr) #else CALL m_abort() diff --git a/src/qs_environment_methods.F b/src/qs_environment_methods.F index 03c5d6e0d6..ff570d9ed7 100644 --- a/src/qs_environment_methods.F +++ b/src/qs_environment_methods.F @@ -321,7 +321,7 @@ CONTAINS END IF ! reallocate vee: external electrostatic potential - IF (dft_control%apply_external_potential) THEN + IF (dft_control%apply_external_potential .AND. .NOT. qs_env%mimic) THEN NULLIFY (vee) CALL get_qs_env(qs_env, pw_env=new_pw_env, vee=vee) IF (ASSOCIATED(vee)) THEN diff --git a/src/qs_environment_types.F b/src/qs_environment_types.F index e129102c55..aaa5614549 100644 --- a/src/qs_environment_types.F +++ b/src/qs_environment_types.F @@ -219,6 +219,7 @@ MODULE qs_environment_types TYPE qs_environment_type LOGICAL :: qmmm = .FALSE., qmmm_periodic = .FALSE. + LOGICAL :: mimic = .FALSE. LOGICAL :: requires_mo_derivs = .FALSE. LOGICAL :: requires_matrix_vxc = .FALSE. LOGICAL :: has_unit_metric = .FALSE. @@ -341,6 +342,7 @@ CONTAINS !> \param sab_all ... !> \param qmmm ... !> \param qmmm_periodic ... +!> \param mimic ... !> \param sac_ae ... !> \param sac_ppl ... !> \param sac_lri ... @@ -496,7 +498,7 @@ CONTAINS !> \version 1.0 ! ************************************************************************************************** SUBROUTINE get_qs_env(qs_env, atomic_kind_set, qs_kind_set, cell, super_cell, cell_ref, use_ref_cell, kpoints, & - dft_control, mos, sab_orb, sab_all, qmmm, qmmm_periodic, sac_ae, sac_ppl, sac_lri, & + dft_control, mos, sab_orb, sab_all, qmmm, qmmm_periodic, mimic, sac_ae, sac_ppl, sac_lri, & sap_ppnl, sab_vdw, sab_scp, sap_oce, sab_lrc, sab_se, sab_xtbe, sab_tbe, sab_core, & sab_xb, sab_xtb_pp, sab_xtb_nonbond, sab_almo, & sab_kp, sab_kp_nosym, sab_cneo, particle_set, energy, force, & @@ -536,7 +538,7 @@ CONTAINS TYPE(mo_set_type), DIMENSION(:), OPTIONAL, POINTER :: mos TYPE(neighbor_list_set_p_type), DIMENSION(:), & OPTIONAL, POINTER :: sab_orb, sab_all - LOGICAL, OPTIONAL :: qmmm, qmmm_periodic + LOGICAL, OPTIONAL :: qmmm, qmmm_periodic, mimic TYPE(neighbor_list_set_p_type), DIMENSION(:), OPTIONAL, POINTER :: sac_ae, sac_ppl, sac_lri, & sap_ppnl, sab_vdw, sab_scp, sap_oce, sab_lrc, sab_se, sab_xtbe, sab_tbe, sab_core, & sab_xb, sab_xtb_pp, sab_xtb_nonbond, sab_almo, sab_kp, sab_kp_nosym, sab_cneo @@ -679,6 +681,7 @@ CONTAINS IF (PRESENT(super_cell)) super_cell => qs_env%super_cell IF (PRESENT(qmmm)) qmmm = qs_env%qmmm IF (PRESENT(qmmm_periodic)) qmmm_periodic = qs_env%qmmm_periodic + IF (PRESENT(mimic)) mimic = qs_env%mimic IF (PRESENT(mos)) mos => qs_env%mos IF (PRESENT(mos_last_converged)) mos_last_converged => qs_env%mos_last_converged IF (PRESENT(ewald_env)) ewald_env => qs_env%ewald_env @@ -963,6 +966,7 @@ CONTAINS qs_env%single_point_run = .FALSE. qs_env%qmmm = .FALSE. qs_env%qmmm_periodic = .FALSE. + qs_env%mimic = .FALSE. qs_env%requires_mo_derivs = .FALSE. qs_env%requires_matrix_vxc = .FALSE. qs_env%has_unit_metric = .FALSE. @@ -1004,6 +1008,7 @@ CONTAINS !> \param mos ... !> \param qmmm ... !> \param qmmm_periodic ... +!> \param mimic ... !> \param ewald_env ... !> \param ewald_pw ... !> \param mpools ... @@ -1080,7 +1085,7 @@ CONTAINS !> \version 1.0 ! ************************************************************************************************** SUBROUTINE set_qs_env(qs_env, super_cell, & - mos, qmmm, qmmm_periodic, & + mos, qmmm, qmmm_periodic, mimic, & ewald_env, ewald_pw, mpools, & rho_external, external_vxc, mask, & scf_control, rel_control, qs_charges, ks_env, & @@ -1099,7 +1104,7 @@ CONTAINS TYPE(qs_environment_type), INTENT(INOUT) :: qs_env TYPE(cell_type), OPTIONAL, POINTER :: super_cell TYPE(mo_set_type), DIMENSION(:), OPTIONAL, POINTER :: mos - LOGICAL, OPTIONAL :: qmmm, qmmm_periodic + LOGICAL, OPTIONAL :: qmmm, qmmm_periodic, mimic TYPE(ewald_environment_type), OPTIONAL, POINTER :: ewald_env TYPE(ewald_pw_type), OPTIONAL, POINTER :: ewald_pw TYPE(qs_matrix_pools_type), OPTIONAL, POINTER :: mpools @@ -1189,6 +1194,7 @@ CONTAINS ! IF (PRESENT(qmmm)) qs_env%qmmm = qmmm IF (PRESENT(qmmm_periodic)) qs_env%qmmm_periodic = qmmm_periodic + IF (PRESENT(mimic)) qs_env%mimic = mimic IF (PRESENT(mos)) qs_env%mos => mos IF (PRESENT(mos_last_converged)) qs_env%mos_last_converged => mos_last_converged IF (PRESENT(ls_scf_env)) qs_env%ls_scf_env => ls_scf_env diff --git a/src/qs_external_potential.F b/src/qs_external_potential.F index 66581ce135..44add42b4b 100644 --- a/src/qs_external_potential.F +++ b/src/qs_external_potential.F @@ -203,7 +203,7 @@ CONTAINS cell=cell, & dft_control=dft_control) - IF (dft_control%apply_external_potential) THEN + IF (dft_control%apply_external_potential .AND. .NOT. qs_env%mimic) THEN !ensure that external potential is loaded to grid IF (dft_control%eval_external_potential) THEN CALL external_e_potential(qs_env) diff --git a/src/start/PACKAGE b/src/start/PACKAGE index d1907e7c10..0f80f65605 100644 --- a/src/start/PACKAGE +++ b/src/start/PACKAGE @@ -17,5 +17,6 @@ "../motion", "../motion/mc", "../emd", + "../mimic", ], } diff --git a/src/start/cp2k_runs.F b/src/start/cp2k_runs.F index 7cee49cbb2..8f8b355124 100644 --- a/src/start/cp2k_runs.F +++ b/src/start/cp2k_runs.F @@ -75,8 +75,9 @@ MODULE cp2k_runs bsse_run, cell_opt_run, debug_run, do_atom, do_band, do_cp2k, do_embed, do_farming, & do_fist, do_ipi, do_mixed, do_nnp, do_opt_basis, do_optimize_input, do_qmmm, do_qs, & do_sirius, do_swarm, do_tamc, do_test, do_tree_mc, do_tree_mc_ana, driver_run, ehrenfest, & - energy_force_run, energy_run, geo_opt_run, linear_response_run, mol_dyn_run, mon_car_run, & - negf_run, none_run, pint_run, real_time_propagation, rtp_method_bse, tree_mc_run, vib_anal + energy_force_run, energy_run, geo_opt_run, linear_response_run, mimic_run, mol_dyn_run, & + mon_car_run, negf_run, none_run, pint_run, real_time_propagation, rtp_method_bse, & + tree_mc_run, vib_anal USE input_cp2k, ONLY: create_cp2k_root_section USE input_cp2k_check, ONLY: check_cp2k_input USE input_cp2k_global, ONLY: create_global_section @@ -106,6 +107,7 @@ MODULE cp2k_runs mp_comm_type,& mp_para_env_release,& mp_para_env_type + USE mimic_loop, ONLY: do_mimic_loop USE mscfg_methods, ONLY: do_mol_loop,& loop_over_molecules USE neb_methods, ONLY: neb @@ -383,6 +385,8 @@ CONTAINS CALL neb(root_section, input_declaration, para_env, globenv) CASE (negf_run) CALL do_negf(force_env) + CASE (mimic_run) + CALL do_mimic_loop(force_env) CASE default CPABORT("") END SELECT diff --git a/tests/MIMIC/regtest-qmmm/MCL_LOG_1 b/tests/MIMIC/regtest-qmmm/MCL_LOG_1 new file mode 100644 index 0000000000..20cbd28438 Binary files /dev/null and b/tests/MIMIC/regtest-qmmm/MCL_LOG_1 differ diff --git a/tests/MIMIC/regtest-qmmm/TEST_FILES.toml b/tests/MIMIC/regtest-qmmm/TEST_FILES.toml new file mode 100644 index 0000000000..0378e634ae --- /dev/null +++ b/tests/MIMIC/regtest-qmmm/TEST_FILES.toml @@ -0,0 +1,7 @@ +# runs are executed in the same order as in this file +# the second field tells which test should be run in order to compare with the last available output +# e.g. 0 means do not compare anything, running is enough +# 1 compares the last total energy in the file +# for details see cp2k/tools/do_regtest + +"client.inp" = [{matcher="E_total", tol=1e-9, ref=-1.17639095290768}] diff --git a/tests/MIMIC/regtest-qmmm/client.inp b/tests/MIMIC/regtest-qmmm/client.inp new file mode 100644 index 0000000000..f4540e63bc --- /dev/null +++ b/tests/MIMIC/regtest-qmmm/client.inp @@ -0,0 +1,61 @@ +&GLOBAL + PREFERRED_FFT_LIBRARY FFTW3 + PRINT_LEVEL Low + PROJECT h2 + RUN_TYPE MIMIC +&END GLOBAL + +&FORCE_EVAL + METHOD Quickstep + &DFT + BASIS_SET_FILE_NAME GTH_BASIS_SETS + POTENTIAL_FILE_NAME GTH_POTENTIALS + &MGRID + CUTOFF 100 + NGRIDS 4 + REL_CUTOFF 60 + &END MGRID + &POISSON + PERIODIC NONE + POISSON_SOLVER MT + &END POISSON + &QS + EPS_DEFAULT 1.0E-10 + EXTRAPOLATION PS + EXTRAPOLATION_ORDER 5 + &END QS + &SCF + EPS_SCF 5.0E-7 + MAX_SCF 1000 + SCF_GUESS Atomic + &OT + MINIMIZER DIIS + PRECONDITIONER Full_Single_Inverse + &END OT + &END SCF + &XC + &XC_FUNCTIONAL + &GGA_C_LYP T + &END GGA_C_LYP + &GGA_X_B88 T + &END GGA_X_B88 + &END XC_FUNCTIONAL + &END XC + &END DFT + &SUBSYS + &CELL + ABC [Bohr] 10.00 10.00 10.00 + ALPHA_BETA_GAMMA 90.00 90.00 90.00 + PERIODIC NONE + &END CELL + &COORD + Unit Bohr + H 23.09245158 21.44838997 24.18849265 + H 22.33656119 21.54287627 25.34122551 + &END COORD + &KIND H + BASIS_SET DZVP-GTH + POTENTIAL GTH-BLYP-q1 + &END KIND + &END SUBSYS +&END FORCE_EVAL diff --git a/tests/TEST_DIRS b/tests/TEST_DIRS index e7e5b75e1d..0a12ab482c 100644 --- a/tests/TEST_DIRS +++ b/tests/TEST_DIRS @@ -4,6 +4,7 @@ # in case a new directory is added just add it at the top of the list.. # the order will be regularly checked and modified... QS/regtest-moments-kpoints +MIMIC/regtest-qmmm mimic mpiranks==4 QS/regtest-monovalent QS/regtest-tddfpt-sf-force libint !ifx QS/regtest-tddfpt-sf libint !ifx diff --git a/tests/do_regtest.py b/tests/do_regtest.py index 4a3ff2c1be..b5a3088ed0 100755 --- a/tests/do_regtest.py +++ b/tests/do_regtest.py @@ -317,6 +317,10 @@ class Config: env["PIKA_COMMANDLINE_OPTIONS"] = ( f"--pika:bind=none --pika:threads={self.ompthreads}" ) + if cwd is not None and "MIMIC" == cwd.parent.name: + env["MCL_COMM_MODE"] = "TEST_STUB" + env["MCL_PROGRAM"] = "1" + env["MCL_TEST_DATA"] = "MCL_LOG_1" exe_name = f"{exe_stem}.{self.version}" cmd = [str(self.binary_dir / exe_name)] if self.valgrind: diff --git a/tools/toolchain/install_cp2k_toolchain.sh b/tools/toolchain/install_cp2k_toolchain.sh index 1ab896d920..1d04cbd49c 100755 --- a/tools/toolchain/install_cp2k_toolchain.sh +++ b/tools/toolchain/install_cp2k_toolchain.sh @@ -246,6 +246,8 @@ The --with-PKG options follow the rules: Default = no --with-ace Enable interface to ML-pace Default = no + --with-mcl Install MCL library for MiMiC with toolchain. + Default = no FURTHER INSTRUCTIONS @@ -281,7 +283,8 @@ mpi_list="mpich openmpi intelmpi" math_list="mkl acml openblas" lib_list="fftw libint libxc libgrpp libxsmm cosma scalapack elpa dbcsr cusolvermp plumed spfft spla gsl spglib hdf5 libvdwxc sirius - libvori libtorch deepmd ace dftd4 tblite pugixml libsmeagol trexio greenx gmp" + libvori libtorch deepmd ace dftd4 tblite pugixml libsmeagol + trexio greenx gmp mcl" package_list="${tool_list} ${mpi_list} ${math_list} ${lib_list}" # ------------------------------------------------------------------------ @@ -336,6 +339,7 @@ with_ninja="__DONTUSE__" with_dftd4="__DONTUSE__" with_tblite="__DONTUSE__" with_libsmeagol="__DONTUSE__" +with_mcl="__DONTUSE__" # for MPI, we try to detect system MPI variant if (command -v mpiexec > /dev/null 2>&1); then @@ -697,6 +701,9 @@ while [ $# -ge 1 ]; do --with-dbcsr*) with_dbcsr=$(read_with $1) ;; + --with-mcl*) + with_mcl=$(read_with ${1}) + ;; --help*) show_help exit 0 @@ -770,6 +777,10 @@ if [ "${MPI_MODE}" = "no" ]; then echo "Not using MPI, so COSMA is disabled" with_cosma="__DONTUSE__" fi + if [ "${with_mcl}" != "__DONTUSE__" ]; then + echo "Not using MPI, so MCL is disabled" + with_mcl="__DONTUSE__" + fi else # if gcc is installed, then mpi needs to be installed too if [ "${with_gcc}" = "__INSTALL__" ]; then @@ -844,6 +855,7 @@ if [ "${with_spglib}" = "__INSTALL__" ] || [ "${with_ninja}" = "__INSTALL__" ] || [ "${with_greenx}" = "__INSTALL__" ] || [ "${with_dftd4}" = "__INSTALL__" ] || + [ "${with_mcl}" = "__INSTALL__" ] || [ "${with_tblite}" = "__INSTALL__" ]; then [ "${with_cmake}" = "__DONTUSE__" ] && with_cmake="__INSTALL__" fi diff --git a/tools/toolchain/scripts/stage8/install_mcl.sh b/tools/toolchain/scripts/stage8/install_mcl.sh new file mode 100755 index 0000000000..31a16fba01 --- /dev/null +++ b/tools/toolchain/scripts/stage8/install_mcl.sh @@ -0,0 +1,112 @@ +#!/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)" + +mcl_ver="3.0.0" +mcl_sha256="3e740582836fe90e04a693cfc5a219826bcac03217f70ea5570bad6aeafda685" + +# shellcheck source=/dev/null +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_mcl" ] && rm "${BUILDDIR}/setup_mcl" + +! [ -d "${BUILDDIR}" ] && mkdir -p "${BUILDDIR}" +cd "${BUILDDIR}" + +case "${with_mcl:=__INSTALL__}" in + __INSTALL__) + echo "==================== Installing MCL ====================" + pkg_install_dir="${INSTALLDIR}/mcl-${mcl_ver}" + install_lock_file="${pkg_install_dir}/install_successful" + if verify_checksums "${install_lock_file}"; then + echo "libmcl-${mcl_ver} is already installed, skipping it." + else + if [ -f mcl-${mcl_ver}.tar.gz ]; then + echo "mcl-${mcl_ver}.tar.gz is found" + else + download_pkg_from_urlpath "${mcl_sha256}" mcl-${mcl_ver}.tar.gz \ + https://gitlab.com/mimic-project/mcl/-/archive/${mcl_ver} + + fi + + echo "Installing from scratch into ${pkg_install_dir}" + [ -d mcl-${mcl_ver} ] && rm -rf mcl-${mcl_ver} + tar -xzf mcl-${mcl_ver}.tar.gz + + mkdir "mcl-${mcl_ver}/build" + cd "mcl-${mcl_ver}/build" + + cmake \ + -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_SHARED_LIBS=YES \ + -DBUILD_FORTRAN_API=YES \ + -DCMAKE_INSTALL_PREFIX="${pkg_install_dir}" \ + -DCMAKE_VERBOSE_MAKEFILE=ON \ + .. > cmake.log 2>&1 || tail -n ${LOG_LINES} cmake.log + CMAKE_BUILD_PARALLEL_LEVEL="$(get_nprocs)" cmake --build . > build.log 2>&1 || tail -n ${LOG_LINES} build.log + CMAKE_BUILD_PARALLEL_LEVEL="$(get_nprocs)" cmake --build . --target install > install.log 2>&1 || tail -n ${LOG_LINES} install.log + + write_checksums "${install_lock_file}" "${SCRIPT_DIR}/stage8/$(basename "${SCRIPT_NAME}")" + fi + MCL_LDFLAGS="-L'${pkg_install_dir}/lib/MiMiC' -Wl,-rpath,'${pkg_install_dir}/lib/MiMiC'" + ;; + __SYSTEM__) + echo "==================== Finding MCL from system paths ====================" + check_lib -lmclf "mcl" + add_lib_from_paths MCL_LDFLAGS "mclf.*" "$LIB_PATHS" + add_lib_from_paths MCL_LDFLAGS "mcl.*" "$LIB_PATHS" + ;; + __DONTUSE__) ;; + + *) + echo "==================== Linking MCL to user paths ====================" + pkg_install_dir="${with_mcl}" + + # use the lib64 directory if present (multi-abi distros may link lib/ to lib32/ instead) + MCL_LIBDIR="${pkg_install_dir}/lib/MiMiC" + [ -d "${pkg_install_dir}/lib64" ] && MCL_LIBDIR="${pkg_install_dir}/lib64/MiMiC" + + check_dir "${MCL_LIBDIR}" + MCL_LDFLAGS="-L'${MCL_LIBDIR}' -Wl,-rpath,'$MCL_LIBDIR}'" + ;; +esac + +if [ "$with_mcl" != "__DONTUSE__" ]; then + MCL_LIBS="-lmcl -lmclf -lstdc++" + if [ "$with_mcl" != "__SYSTEM__" ]; then + cat << EOF > "${BUILDDIR}/setup_mcl" +prepend_path LD_LIBRARY_PATH "${pkg_install_dir}/lib/MiMiC" +prepend_path LD_RUN_PATH "${pkg_install_dir}/lib/MiMiC" +prepend_path LIBRARY_PATH "${pkg_install_dir}/lib/MiMiC" +export MCL_LIBS="${MCL_LIBS}" +export MCL_ROOT="${pkg_install_dir}" +prepend_path PKG_CONFIG_PATH "$pkg_install_dir/lib/pkgconfig" +prepend_path CMAKE_PREFIX_PATH "$pkg_install_dir" +EOF + fi + cat << EOF >> "${BUILDDIR}/setup_mcl" +export MCL_VER="${mcl_ver}" +export MCL_ROOT="${pkg_install_dir}" +export MCL_LDFLAGS="${MCL_LDFLAGS}" +export MCL_LIBS="${MCL_LIBS}" +export CP_DFLAGS="\${CP_DFLAGS} -D__MIMIC" +export CP_LDFLAGS="\${CP_LDFLAGS} ${MCL_LDFLAGS}" +export CP_LIBS="\${CP_LIBS} ${MCL_LIBS}" +EOF + cat "${BUILDDIR}/setup_mcl" >> "${SETUPFILE}" +fi + +load "${BUILDDIR}/setup_mcl" +write_toolchain_env "${INSTALLDIR}" + +cd "${ROOTDIR}" +report_timing "MCL" diff --git a/tools/toolchain/scripts/stage8/install_stage8.sh b/tools/toolchain/scripts/stage8/install_stage8.sh index 42dcc1d2c6..bd705ab220 100755 --- a/tools/toolchain/scripts/stage8/install_stage8.sh +++ b/tools/toolchain/scripts/stage8/install_stage8.sh @@ -10,4 +10,5 @@ ./scripts/stage8/install_dftd4.sh ./scripts/stage8/install_trexio.sh ./scripts/stage8/install_tblite.sh +./scripts/stage8/install_mcl.sh #EOF