From 51cbe342779bdc4f043a1480c011075740f80635 Mon Sep 17 00:00:00 2001 From: Tammo van der Heide Date: Thu, 15 Aug 2024 13:50:57 +0200 Subject: [PATCH] Parallelize (MPI, optional) two-center integration --- .github/workflows/build.yml | 48 ++++- CMakeLists.txt | 23 ++ README.rst | 27 ++- cmake/SkProgsUtils.cmake | 129 +++++++++++ common/include/error.fypp | 21 ++ common/lib/CMakeLists.txt | 16 +- common/lib/environment.F90 | 121 +++++++++++ common/lib/globalenv.F90 | 194 +++++++++++++++++ common/lib/mpienv.F90 | 203 ++++++++++++++++++ common/lib/mpifx.F90 | 18 ++ common/lib/schedule.F90 | 65 ++++++ config.cmake | 15 +- external/mpifx/CMakeLists.txt | 8 + sktools/src/sktools/calculators/sktwocnt.py | 2 +- sktwocnt/lib/CMakeLists.txt | 25 ++- sktwocnt/lib/gridorbital.f90 | 2 +- sktwocnt/lib/{twocnt.f90 => twocnt.F90} | 168 +++++++++++++-- sktwocnt/prog/CMakeLists.txt | 16 +- sktwocnt/prog/cmdargs.f90 | 6 +- sktwocnt/prog/input.f90 | 23 +- sktwocnt/prog/{main.f90 => main.F90} | 24 ++- sktwocnt/prog/output.f90 | 16 +- test/prog/sktable/CMakeLists.txt | 4 +- .../Non-Relativistic/Dynamic-conv/_C-C.skf | 42 ++++ .../Non-Relativistic/Dynamic-conv/config | 1 + .../Non-Relativistic/Dynamic-conv/skdef.hsd | 72 +++++++ .../Non-Relativistic/Dynamic-noconv/_C-C.skf | 48 +++++ .../Non-Relativistic/Dynamic-noconv/config | 1 + .../Non-Relativistic/Dynamic-noconv/skdef.hsd | 72 +++++++ test/prog/sktable/bin/testwithworkdir | 15 +- test/prog/sktable/tests | 2 + utils/export/skprogs-config.cmake.in | 11 + utils/test/check_submodule_commits | 112 ++++++++++ 33 files changed, 1486 insertions(+), 64 deletions(-) create mode 100644 common/lib/environment.F90 create mode 100644 common/lib/globalenv.F90 create mode 100644 common/lib/mpienv.F90 create mode 100644 common/lib/mpifx.F90 create mode 100644 common/lib/schedule.F90 create mode 100644 external/mpifx/CMakeLists.txt rename sktwocnt/lib/{twocnt.f90 => twocnt.F90} (89%) rename sktwocnt/prog/{main.f90 => main.F90} (53%) create mode 100644 test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/_C-C.skf create mode 100644 test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/config create mode 100644 test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/skdef.hsd create mode 100644 test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/_C-C.skf create mode 100644 test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/config create mode 100644 test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/skdef.hsd create mode 100755 utils/test/check_submodule_commits diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4b0d6f77..501fbe06 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest] + mpi: [nompi, openmpi] config: [Debug] version: [13] @@ -38,11 +39,31 @@ jobs: --slave /usr/bin/gfortran gfortran /usr/bin/gfortran-${{ matrix.version }} \ --slave /usr/bin/gcov gcov /usr/bin/gcov-${{ matrix.version }} + - name: Enable MPI build + if: contains(matrix.mpi, 'openmpi') || contains(matrix.mpi, 'mpich') + run: echo "WITH_MPI=true" >> $GITHUB_ENV + + - name: Disable MPI build + if: contains(matrix.mpi, 'nompi') + run: echo "WITH_MPI=false" >> $GITHUB_ENV + - name: Set Compiler run: | echo "FC=gfortran" >> $GITHUB_ENV echo "CC=gcc" >> $GITHUB_ENV + - name: Install OpenMPI + if: contains(matrix.mpi, 'openmpi') + run: | + sudo apt-get update + sudo apt-get install libopenmpi-dev + + - name: Install MPICH + if: contains(matrix.mpi, 'mpich') + run: | + sudo apt-get update + sudo apt-get install mpich + - name: Compile and Install libXC run: | git clone https://gitlab.com/libxc/libxc.git @@ -50,7 +71,7 @@ jobs: git checkout 6.2.2 cmake -DCMAKE_INSTALL_PREFIX=${PWD}/${BUILD_DIR}/${INSTALL_DIR} -DENABLE_FORTRAN=True -B ${BUILD_DIR} . cd ${BUILD_DIR} - make -j 2 + make -j2 make install cd ../../ @@ -65,8 +86,10 @@ jobs: pip3 install cmake fypp numpy scipy - name: Configure build - run: | - cmake -B ${BUILD_DIR} -DCMAKE_INSTALL_PREFIX=${PWD}/${BUILD_DIR}/${INSTALL_DIR} -DCMAKE_BUILD_TYPE=Debug . + run: >- + cmake -B ${BUILD_DIR} -DWITH_MPI=${WITH_MPI} + -DCMAKE_INSTALL_PREFIX=${PWD}/${BUILD_DIR}/${INSTALL_DIR} + -DCMAKE_BUILD_TYPE=${{ matrix.config }} . - name: Build project run: cmake --build ${BUILD_DIR} @@ -74,7 +97,19 @@ jobs: - name: Run regression tests run: | pushd ${BUILD_DIR} - ctest -j 2 --output-on-failure + ctest -j2 --output-on-failure + popd + + - name: Configure build (TEST_MPI_PROCS=2) + if: contains(matrix.mpi, 'openmpi') || contains(matrix.mpi, 'mpich') + run: | + cmake -B ${BUILD_DIR} -DTEST_MPI_PROCS=2 . + + - name: Run regression tests (TEST_MPI_PROCS=2) + if: contains(matrix.mpi, 'openmpi') || contains(matrix.mpi, 'mpich') + run: | + pushd ${BUILD_DIR} + ctest -j1 --output-on-failure popd - name: Install project @@ -96,6 +131,7 @@ jobs: CMAKE_OPTIONS: >- -DCMAKE_BUILD_TYPE=${{ matrix.config }} -DFYPP_FLAGS='-DTRAVIS' + -DWITH_MPI=false steps: - name: Checkout code @@ -128,7 +164,7 @@ jobs: git checkout 6.2.2 cmake -DCMAKE_INSTALL_PREFIX=${PWD}/${BUILD_DIR}/${INSTALL_DIR} -DENABLE_FORTRAN=True -B ${BUILD_DIR} . cd ${BUILD_DIR} - make -j 2 + make -j2 make install cd ../../ @@ -156,7 +192,7 @@ jobs: - name: Run regression tests run: | pushd ${BUILD_DIR} - ctest -j 2 --output-on-failure + ctest -j2 --output-on-failure popd - name: Install project diff --git a/CMakeLists.txt b/CMakeLists.txt index 11181e34..3cc6f9e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,16 @@ endif() find_package(Libxc QUIET) +if(WITH_MPI) + find_package(MPI REQUIRED) + if(NOT MPI_FORTRAN_FOUND) + message(FATAL_ERROR "Compiler ${CMAKE_Fortran_COMPILER} is not MPI capable but is specified " + "for a WITH_MPI=TRUE build") + endif() + list(FILTER MPI_Fortran_COMPILE_OPTIONS EXCLUDE REGEX "-fallow-argument-mismatch") + message(STATUS "MPI_Fortran_COMPILE_OPTIONS: ${MPI_Fortran_COMPILE_OPTIONS}") +endif() + if (Libxc_FOUND AND Libxc_VERSION VERSION_LESS "6.0.0") message(FATAL_ERROR "SkProgs requires libXC version >=6.0.0 to work properly.") endif() @@ -72,6 +82,19 @@ set(FYPP_BUILD_FLAGS "${FYPP_FLAGS}" "--file-var-root=${CMAKE_SOURCE_DIR}" set(PYTHON_INTERPRETER "python3" CACHE STRING "Python interpreter to use for installing and test python components") +############## External components (optional) ################# + +# Note 1 : GIT_TAG hashes below must be updated with the utils/test/check_submodule_commits script! +# Note 2 : GIT_REPOSITORY URLs should be to https// addresses for the CI pipelines to see them + +if(WITH_MPI) + set(MPIFX_GIT_REPOSITORY "https://github.com/dftbplus/mpifx.git") + set(MPIFX_GIT_TAG "aca7a212f68778aacb33ed33925d45f83dab91ca") # do not change manually! + skprogs_config_hybrid_dependency(MpiFx MpiFx::MpiFx "${HYBRID_CONFIG_METHODS}" "QUIET" + external/mpifx "${exclude}" "${MPIFX_GIT_REPOSITORY}" "${MPIFX_GIT_TAG}") + list(APPEND PKG_CONFIG_REQUIRES mpifx) +endif() + #################### Add source components #################### add_subdirectory(common) diff --git a/README.rst b/README.rst index 2ce0e3e5..c5ac64eb 100644 --- a/README.rst +++ b/README.rst @@ -27,7 +27,9 @@ Prerequisites * LAPACK/BLAS libraries (or compatible equivalents) -* libXC library with f03 interface (version >=6.0.0) +* libXC library with f03 interface (>=6.0.0) + +* MpiFx (>=1.5, MPI-enabled build only) Obtaining via Conda @@ -65,7 +67,11 @@ Follow the usual CMake build workflow: the install location (i.e. path stored in ``YOUR_SKPROGS_INSTALL_FOLDER``, e.g. ``$HOME/opt/skprogs``) and the build directory (e.g. ``_build``):: - FC=gfortran cmake -DCMAKE_INSTALL_PREFIX=YOUR_SKPROGS_INSTALL_FOLDER -B _build . + FC=gfortran cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=YOUR_SKPROGS_INSTALL_FOLDER -B _build . + + An MPI enabled build is obtained by additionally setting ``-DWITH_MPI=1`` + (default: ``-DWITH_MPI=0``). At the moment only the two-center integration + code ``sktwocnt`` is MPI parallelized and benefits from multiple processors. If libXC is installed in a non-standard location, you may need to specify either the ``CMAKE_PREFIX_PATH`` environment variable (if libXC was built with @@ -86,6 +92,18 @@ Follow the usual CMake build workflow: ctest -j popd +* If you want to test the MPI enabled binary with more than one MPI-process, you + should set the ``TEST_MPI_PROCS`` variable in ``config.cmake`` accordingly, + e.g.:: + + set(TEST_MPI_PROCS "2" CACHE STRING "Nr. of processes used for testing") + + The ``TEST_MPI_PROCS`` cache variable can be updated or changed also after the + compilation by invoking CMake with the appropriate ``-D`` option, e.g.:: + + cmake -B _build -DTEST_MPI_PROCS=2 . + pushd _build; ctest; popd + * If the tests were successful, install the package via :: cmake --install _build @@ -177,6 +195,11 @@ follows: skgen -o slateratom -t sktwocnt sktable -d C,H,O C,H,O + For an MPI enabled binary, make sure to prepend any required information to + the two-center binary, e.g.:: + + skgen -o slateratom -t "mpirun -np 2 sktwocnt" sktable -d C C |& tee output + The SK-files will be created in the current folder. See the help (e.g. ``skgen -h``) for additional options. diff --git a/cmake/SkProgsUtils.cmake b/cmake/SkProgsUtils.cmake index d5f85909..d9f5dfdf 100644 --- a/cmake/SkProgsUtils.cmake +++ b/cmake/SkProgsUtils.cmake @@ -50,6 +50,11 @@ endfunction() function (skprogs_add_fypp_defines fyppflags) set(_fyppflags "${${fyppflags}}") + + if(WITH_MPI) + list(APPEND _fyppflags -DWITH_MPI) + endif() + set(${fyppflags} ${_fyppflags} PARENT_SCOPE) endfunction() @@ -195,6 +200,130 @@ macro(skprogs_setup_global_compiler_flags) endmacro() +# Handles a hybrid dependency. +# +# Depending on the list items in the config_methods variable, it will try to: +# +# - checkout the source as a submodule within the origin sub-folder ("Submodule") +# - find the package as external dependency ("Find") +# - fetch the source from a git repository ("Fetch") into the build folder +# +# The methods are tried in the order of their appearance until success the first eligible one. +# +# The methods "Submodule" and "Fetch" would call the passed sub-directory with add_subdirectory() +# passing two variables with the source and binary directory. +# +# Args: +# package [in]: Name of the dependency to look for. +# config_methods [in]: Config methods to try +# target [in]: Name of the target, which must be exported after the configuration. +# findpkgopts [in]: Options to pass to find_package() +# subdir [in]: Subdirectory with CMakeFiles.txt for integrating package source. +# subdiropts [in]: Options to pass to the add_subdir() command. +# git_repository [in]: Git repository to fetch the package from. +# git_tag [in]: Git tag to use when fetching the source. +# +# Variables: +# _SOURCE_DIR, _BINARY_DIR: +# Source and binary directories for the build (to pass to add_subdirectory()) +# +macro(skprogs_config_hybrid_dependency package target config_methods findpkgopts subdir subdiropts + git_repository git_tag) + + set(_allowed_methods "submodule;find;fetch;pkgconf") + string(TOLOWER "${package}" _package_lower) + string(TOUPPER "${package}" _package_upper) + + foreach(_config_method IN ITEMS ${config_methods}) + + string(TOLOWER "${_config_method}" _config_lower) + if(NOT ${_config_lower} IN_LIST _allowed_methods) + message(FATAL_ERROR "${package}: Unknown configuration method '${_config_method}'") + endif() + + if("${_config_lower}" STREQUAL "find") + + message(STATUS "${package}: Trying to find installed package") + find_package(${package} ${findpkgopts}) + if(${package}_FOUND) + message(STATUS "${package}: Installed package found") + break() + else() + message(STATUS "${package}: Installed package could not be found") + endif() + + elseif("${_config_lower}" STREQUAL "pkgconf") + message(STATUS "${package}: Trying to find installed package (pkg-config)") + + find_package(PkgConfig QUIET) + if(PkgConfig_FOUND) + pkg_check_modules("${_package_upper}" QUIET "${package}") + if("${${_package_upper}_FOUND}") + message(STATUS "${package}: Installed package found (pkg-config)") + add_library("${target}" INTERFACE IMPORTED) + target_link_libraries( + "${target}" + INTERFACE + "${${_package_upper}_LINK_LIBRARIES}" + ) + target_include_directories( + "${target}" + INTERFACE + "${${_package_upper}_INCLUDE_DIRS}" + ) + break() + else() + message(STATUS "${package}: Installed package could not be found (pkg-config)") + endif() + endif() + + elseif("${_config_lower}" STREQUAL "submodule") + + if (NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${subdir}/origin/CMakeLists.txt + AND GIT_WORKING_COPY) + message(STATUS "${package}: Downloading via git submodule update") + execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init ${subdir}/origin + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) + endif() + + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${subdir}/origin/CMakeLists.txt) + message(STATUS "${package}: Using source in ${subdir}/origin") + set(${_package_upper}_SOURCE_DIR "origin") + set(${_package_upper}_BINARY_DIR) + add_subdirectory(${subdir} ${subdiropts}) + break() + endif() + + elseif("${_config_lower}" STREQUAL "fetch") + + message(STATUS "${package}: Fetching from repository ${git_repository}@${git_tag}") + FetchContent_Declare(${_package_lower} GIT_REPOSITORY ${git_repository} GIT_TAG ${git_tag}) + FetchContent_GetProperties(${_package_lower}) + if(NOT ${_package_lower}_POPULATED) + FetchContent_Populate(${_package_lower}) + endif() + set(${_package_upper}_SOURCE_DIR "${${_package_lower}_SOURCE_DIR}") + set(${_package_upper}_BINARY_DIR "${${_package_lower}_BINARY_DIR}") + add_subdirectory(${subdir} ${subdiropts}) + break() + + endif() + + endforeach() + + if(NOT TARGET ${target}) + message(FATAL_ERROR "Could not configure ${package} to export target '${target}'") + endif() + + unset(_allowed_methods) + unset(_package_lower) + unset(_package_upper) + unset(_config_method) + unset(_config_lower) + +endmacro() + + # Checks whether the current compiler fullfills minimal version requirements. # # diff --git a/common/include/error.fypp b/common/include/error.fypp index cbd9d419..b474c16c 100644 --- a/common/include/error.fypp +++ b/common/include/error.fypp @@ -15,6 +15,27 @@ #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +#! Macro to return an error flag if return variable available or throw +#! an error and shut down otherwise +#:def ERROR_HANDLING(errVar, errNumber, msg) + block + use common_accuracy, only : lc + use common_message + !> Error handling string + character(lc) :: stringTmp + + write(stringTmp,"(A)")${msg}$ + if (present(${errVar}$)) then + ${errVar}$ = ${errNumber}$ + call warning(stringTmp) + return + else + call error(stringTmp) + end if + end block +#:enddef ERROR_HANDLING + + #! Propagation of error handling, for now it just returns when in error #:def HANDLE_ERROR(errVar) if (present(${errVar}$)) then diff --git a/common/lib/CMakeLists.txt b/common/lib/CMakeLists.txt index 7a3a653a..a245b89d 100644 --- a/common/lib/CMakeLists.txt +++ b/common/lib/CMakeLists.txt @@ -31,12 +31,26 @@ set(sources-f90 utils.F90) set(sources-fpp - assert.F90) + assert.F90 + environment.F90 + globalenv.F90 + mpifx.F90 + schedule.F90) + +if(WITH_MPI) + list(APPEND sources-fpp + mpienv.F90) +endif() skprogs_preprocess("${FYPP}" "${fypp_flags}" "F90" "f90" "${sources-fpp}" sources-f90-preproc) add_library(skprogs-common ${sources-f90} ${sources-f90-preproc}) +if(WITH_MPI) + target_link_libraries(skprogs-common MPI::MPI_Fortran) + target_link_libraries(skprogs-common MpiFx::MpiFx) +endif() + set(moddir ${CMAKE_CURRENT_BINARY_DIR}/modfiles) set_target_properties(skprogs-common PROPERTIES Fortran_MODULE_DIRECTORY ${moddir}) target_include_directories(skprogs-common PUBLIC diff --git a/common/lib/environment.F90 b/common/lib/environment.F90 new file mode 100644 index 00000000..6d2c7d56 --- /dev/null +++ b/common/lib/environment.F90 @@ -0,0 +1,121 @@ +!--------------------------------------------------------------------------------------------------! +! DFTB+: general package for performing fast atomistic simulations ! +! Copyright (C) 2006 - 2023 DFTB+ developers group ! +! ! +! See the LICENSE file for terms of usage and distribution. ! +!--------------------------------------------------------------------------------------------------! + +#:include 'common.fypp' +#:include 'error.fypp' + +!> Contains computer environment settings +module common_environment + use common_globalenv, only : shutdown, stdOut +#:if WITH_MPI + use common_globalenv, only : globalMpiComm + use common_mpienv, only : TMpiEnv, TMpiEnv_init, TMpiEnv_final +#:endif + implicit none + + private + public :: TEnvironment, TEnvironment_init + + + !> Contains environment settings. + type :: TEnvironment + private + + !> Whether this process is the lead? + logical, public :: tGlobalLead = .true. + + !> Nr. of groups in the system + integer, public :: nGroup = 1 + + !> Id of current group (starts with 0) + integer, public :: myGroup = 0 + + #:if WITH_MPI + !> Global mpi settings + type(TMpiEnv), public :: mpi + + !> Whether MPI environment had been initialised + logical :: mpiInitialised = .false. + #:endif + + contains + procedure :: destruct => TEnvironment_destruct + procedure :: shutdown => TEnvironment_shutdown + + #:if WITH_MPI + procedure :: initMpi => TEnvironment_initMpi + #:endif + + end type TEnvironment + + +contains + + !> Returns an initialized instance. + subroutine TEnvironment_init(this) + + !> Instance + type(TEnvironment), intent(out) :: this + + continue + + end subroutine TEnvironment_init + + + !> Finalizes the environment. + subroutine TEnvironment_destruct(this) + + !> Instance + class(TEnvironment), intent(inout) :: this + + #:if WITH_MPI + if (this%mpiInitialised) then + call TMpiEnv_final(this%mpi) + this%mpiInitialised = .false. + end if + #:endif + + flush(stdOut) + + end subroutine TEnvironment_destruct + + + !> Gracefully cleans up and shuts down. + !! + !! Note: This routine must be collectively called by all processes. + subroutine TEnvironment_shutdown(this) + + !> Instance + class(TEnvironment), intent(inout) :: this + + call this%destruct() + call shutdown() + + end subroutine TEnvironment_shutdown + + +#:if WITH_MPI + !> Initializes MPI environment. + subroutine TEnvironment_initMpi(this, nGroup) + + !> Instance + class(TEnvironment), intent(inout) :: this + + !> Number of process groups to create + integer, intent(in) :: nGroup + + ! MPI settings + call TMpiEnv_init(this%mpi, globalMpiComm=globalMpiComm, nGroup=nGroup) + this%tGlobalLead = this%mpi%tGlobalLead + this%nGroup = this%mpi%nGroup + this%myGroup = this%mpi%myGroup + this%mpiInitialised = .true. + + end subroutine TEnvironment_initMpi +#:endif + +end module common_environment diff --git a/common/lib/globalenv.F90 b/common/lib/globalenv.F90 new file mode 100644 index 00000000..e4aad0a0 --- /dev/null +++ b/common/lib/globalenv.F90 @@ -0,0 +1,194 @@ +!--------------------------------------------------------------------------------------------------! +! DFTB+: general package for performing fast atomistic simulations ! +! Copyright (C) 2006 - 2023 DFTB+ developers group ! +! ! +! See the LICENSE file for terms of usage and distribution. ! +!--------------------------------------------------------------------------------------------------! + +#:include 'common.fypp' + +!> Contains fundamental global computing environment settings. +!! +!! It contains global settings and routines, which can be used already before the parsing of the +!! input has taken place and the details of the user settings for the running-environment are +!! known. Also, it can be used by routines which are not MPI-aware but wish to make I/O or abort the +!! code. +module common_globalenv + use, intrinsic :: iso_fortran_env, only : output_unit, error_unit +#:if WITH_MPI + use mpi, only : MPI_COMM_WORLD + use extlibs_mpifx, only : mpifx_comm, MPI_THREAD_FUNNELED, mpifx_init_thread, mpifx_abort,& + & mpifx_barrier, mpifx_finalize +#:endif + implicit none + + private + public :: initGlobalEnv, destructGlobalEnv + public :: abortProgram, shutdown, synchronizeAll + public :: stdOut, stdOut0, stdErr, stdErr0, tIoProc + public :: withMpi + #:if WITH_MPI + public :: globalMpiComm + #:endif + + + !> Unredirected standard out + integer, parameter :: stdOut0 = output_unit + + !> Unredirected standard error + integer, parameter :: stdErr0 = error_unit + + !> Standard out file handler + integer, protected :: stdOut = stdOut0 + + !> Standard error file handler + integer, protected :: stdErr = stdErr0 + + !> Whether current process is the global lead process + logical, protected :: tIoProc = .true. + +#:if WITH_MPI + !> Global MPI communicator (used for aborts) + type(mpifx_comm), protected :: globalMpiComm +#:endif + + !> Whether code was compiled with MPI support + logical, parameter :: withMpi = ${FORTRAN_LOGICAL(WITH_MPI)}$ + +#:if WITH_MPI + !> Whether MPI finalization should be performed at the end + logical :: doMpiFinalization = .true. +#:endif + + +contains + + !> Initializes global environment (must be the first statement of a program). + subroutine initGlobalEnv(outputUnit, mpiComm, errorUnit, devNull) + + !> Customised global standard output + integer, intent(in), optional :: outputUnit + + !> Customised global MPI communicator + integer, intent(in), optional :: mpiComm + + !> Customised global standard error + integer, intent(in), optional :: errorUnit + + !> Unit of the null device (needed for follow processes to suppress their output) + integer, intent(in), optional :: devNull + + integer :: outputUnit0, errorUnit0, devNull0 + + #:if WITH_MPI + integer :: mpiComm0 + #:endif + + if (present(outputUnit)) then + outputUnit0 = outputUnit + else + outputUnit0 = stdOut0 + end if + + if (present(errorUnit)) then + errorUnit0 = errorUnit + else + errorUnit0 = stdErr0 + end if + + #:if WITH_MPI + if (present(mpiComm)) then + mpiComm0 = mpiComm + doMpiFinalization = .false. + else + mpiComm0 = MPI_COMM_WORLD + call mpifx_init_thread(requiredThreading=MPI_THREAD_FUNNELED) + end if + + call globalMpiComm%init(commid=mpiComm0) + if (globalMpiComm%lead) then + stdOut = outputUnit0 + stdErr = errorUnit0 + else + if (present(devNull)) then + devNull0 = devNull + else + open(newunit=devNull0, file="/dev/null", action="write") + end if + stdOut = devNull0 + stdErr = devNull0 + end if + tIoProc = globalMpiComm%lead + #:else + stdOut = outputUnit0 + stdErr = errorUnit0 + #:endif + + end subroutine initGlobalEnv + + + !> Finalizes global environment (must be the last statement of a program). + subroutine destructGlobalEnv() + + #:if WITH_MPI + if (doMpiFinalization) then + call mpifx_finalize() + end if + #:endif + + end subroutine destructGlobalEnv + + + !> Gracefully shuts down environment and stops execution. + !! + !! Note: this routine must be called collectively by all processes. + subroutine shutdown() + + call synchronizeAll() + call destructGlobalEnv() + stop + + end subroutine shutdown + + + !> Aborts program execution immediately. + !! + !! Note: if this routine is called by any the processes, execution immediately stops + !! without waiting for any other processes. + subroutine abortProgram(errorCode) + + !> Error code to emit (default: 1) + integer, intent(in), optional :: errorCode + + integer :: errorCode0 + #:if WITH_MPI + integer :: error + #:endif + + if (.not. present(errorCode)) then + errorCode0 = 1 + else + errorCode0 = errorCode + end if + + #:if WITH_MPI + call mpifx_abort(globalMpiComm, errorCode0, error) + if (error /= 0) then + write(stdErr0, "(A,I0,A)") "Process ", globalMpiComm%rank, " could not be aborted." + end if + #:endif + error stop + + end subroutine abortProgram + + + !> Waits until all processes reach this point. + subroutine synchronizeAll() + + #:if WITH_MPI + call mpifx_barrier(globalMpiComm) + #:endif + + end subroutine synchronizeAll + +end module common_globalenv diff --git a/common/lib/mpienv.F90 b/common/lib/mpienv.F90 new file mode 100644 index 00000000..ddab0589 --- /dev/null +++ b/common/lib/mpienv.F90 @@ -0,0 +1,203 @@ +!--------------------------------------------------------------------------------------------------! +! DFTB+: general package for performing fast atomistic simulations ! +! Copyright (C) 2006 - 2023 DFTB+ developers group ! +! ! +! See the LICENSE file for terms of usage and distribution. ! +!--------------------------------------------------------------------------------------------------! + +#:include "common.fypp" +#:include "error.fypp" + + +!> Contains MPI related environment settings +module common_mpienv + use common_accuracy, only : lc + use extlibs_mpifx, only : mpifx_comm, mpifx_allgather, MPI_COMM_TYPE_SHARED + use common_message, only : error + implicit none + + private + public :: TMpiEnv, TMpiEnv_init, TMpiEnv_final + + + !> Contains MPI related environment settings + type :: TMpiEnv + + !> Global MPI communicator + type(mpifx_comm) :: globalComm + + !> Communicator to access processes within current group + type(mpifx_comm) :: groupComm + + !> Communicator to access equivalent processes in other groups + type(mpifx_comm) :: interGroupComm + + !> Communicator within the current node + type(mpifx_comm) :: nodeComm + + !> Size of the process groups + integer :: groupSize + + !> Number of processor groups + integer :: nGroup + + !> Group index of the current process (starts with 0) + integer :: myGroup + + !> Rank of the processes in the given group (with respect of globalComm) + integer, allocatable :: groupMembersGlobal(:) + + !> Rank of the processes in the given group (with respect of MPI_COMM_WORLD) + integer, allocatable :: groupMembersWorld(:) + + !> Whether current process is the global lead + logical :: tGlobalLead + + !> Whether current process is the group lead + logical :: tGroupLead + + contains + + procedure :: mpiSerialEnv + + end type TMpiEnv + + +contains + + !> Initializes MPI environment. + ! --------------------------------------------------------------- + ! Initializes global communicator and group communicators + ! Example: + ! globalSize = 10 + ! nGroup = 2 + ! groupSize = 5 + ! rank + ! globalComm: 0 1 2 3 4 5 6 7 8 9 + ! groupComm: 0 1 2 3 4 0 1 2 3 4 + ! interGroupComm: 0 0 0 0 0 1 1 1 1 1 + ! --------------------------------------------------------------- + ! SCALAPACK + ! Different groups handle different kpoints/spin (iKS) + ! All procs within a group know eigenval(:,iKS) + ! These are distributed to all other nodes using interGroupComm + ! eigenvec(:,:,iKS) are used to build the density matrix, DM(:,:,iKS) + ! DM(:,:,iKS) contains kWeight(iK) and occupation(iKS) + ! total DM(:,:) is obtained by mpiallreduce with MPI_SUM + ! --------------------------------------------------------------- + ! LIBNEGF + ! Different groups handle different kpoints/spin (iKS) + ! All procs within a group know densMat(:,:,iKS) + ! DM(:,:,iKS) contains kWeight(iK) and occupation(iKS) + ! total DM(:,:) is obtained by mpiallreduce with MPI_SUM + ! --------------------------------------------------------------- + subroutine TMpiEnv_init(this, globalMpiComm, nGroup) + + !> Initialised instance on exit + type(TMpiEnv), intent(out) :: this + + !> The global MPI communicator (assumed to be MPI_COMM_WORLD if not specified) + type(mpifx_comm), optional, intent(in) :: globalMpiComm + + !> Number of process groups to create + integer, intent(in), optional :: nGroup + + if (present(globalMpiComm)) then + this%globalComm = globalMpiComm + else + call this%globalComm%init() + end if + if (present(nGroup)) then + this%nGroup = nGroup + else + this%nGroup = 1 + end if + + call setup_inter_node_communicator(this) + call setup_subgrids_common(this) + + this%tGlobalLead = this%globalComm%lead + this%tGroupLead = this%groupComm%lead + + if (this%tGlobalLead .and. .not. this%tGroupLead) then + call error("Internal error: Global lead process is not a group leading process") + end if + + end subroutine TMpiEnv_init + + + !> Finalises the communicators in the structure supplied here + subroutine TMpiEnv_final(this) + + !> Initialised instance + type(TMpiEnv), intent(inout) :: this + + call this%interGroupComm%free() + call this%groupComm%free() + + end subroutine TMpiEnv_final + + + !> Routine to check this is a single processor instance, stopping otherwise (useful to call in + !! purely serial codes to avid multiple copies being invoked with mpirun) + subroutine mpiSerialEnv(this, iErr) + + !> Instance + class(TMpiEnv), intent(in) :: this + + !> Optional error flag + integer, intent(out), optional :: iErr + + if (this%globalComm%size > 1) then + @:ERROR_HANDLING(iErr, -1, 'This is serial code, but invoked on multiple processors') + end if + + end subroutine mpiSerialEnv + + + !> Sets up subgrids and group communicators used with common (non-NEGF) solvers. + subroutine setup_subgrids_common(this) + + !> Environment instance + type(TMpiEnv), intent(inout) :: this + + integer :: myRank, myGroup + character(lc) :: tmpStr + type(mpifx_comm) :: mpiCommWorld + + this%groupSize = this%globalComm%size / this%nGroup + if (this%nGroup * this%groupSize /= this%globalComm%size) then + write(tmpStr, "(A,I0,A,I0,A)") "Number of groups (", this%nGroup,& + & ") not compatible with number of processes (", this%globalComm%size, ")" + call error(tmpStr) + end if + + this%myGroup = this%globalComm%rank / this%groupSize + myRank = mod(this%globalComm%rank, this%groupSize) + call this%globalComm%split(this%myGroup, myRank, this%groupComm) + allocate(this%groupMembersGlobal(this%groupSize)) + call mpifx_allgather(this%groupComm, this%globalComm%rank, this%groupMembersGlobal) + + ! Make a wrapper around MPI_COMM_WORLD and get group member ids within that descriptor + call mpiCommWorld%init() + allocate(this%groupMembersWorld(this%groupSize)) + call mpifx_allgather(this%groupComm, mpiCommWorld%rank, this%groupMembersWorld) + + myGroup = myRank + myRank = this%myGroup + call this%globalComm%split(myGroup, myRank, this%interGroupComm) + + end subroutine setup_subgrids_common + + + !> Sets up a communicator within the node to use MPI shared memory. + subroutine setup_inter_node_communicator(this) + + !> Environment instance + type(TMpiEnv), intent(inout) :: this + + call this%globalComm%split_type(MPI_COMM_TYPE_SHARED, this%globalComm%rank, this%nodeComm) + + end subroutine setup_inter_node_communicator + +end module common_mpienv diff --git a/common/lib/mpifx.F90 b/common/lib/mpifx.F90 new file mode 100644 index 00000000..78b7dda9 --- /dev/null +++ b/common/lib/mpifx.F90 @@ -0,0 +1,18 @@ +!--------------------------------------------------------------------------------------------------! +! DFTB+: general package for performing fast atomistic simulations ! +! Copyright (C) 2006 - 2023 DFTB+ developers group ! +! ! +! See the LICENSE file for terms of usage and distribution. ! +!--------------------------------------------------------------------------------------------------! + +#:include 'common.fypp' + +!> Exporting mpifx functionality if compiled with mpi support, otherwise empty. +module extlibs_mpifx +#:if WITH_MPI + use libmpifx_module +#:endif + implicit none + public + +end module extlibs_mpifx diff --git a/common/lib/schedule.F90 b/common/lib/schedule.F90 new file mode 100644 index 00000000..b1d08f4a --- /dev/null +++ b/common/lib/schedule.F90 @@ -0,0 +1,65 @@ +!--------------------------------------------------------------------------------------------------! +! DFTB+: general package for performing fast atomistic simulations ! +! Copyright (C) 2006 - 2023 DFTB+ developers group ! +! ! +! See the LICENSE file for terms of usage and distribution. ! +!--------------------------------------------------------------------------------------------------! + +#:include 'common.fypp' + +!> Contains routines helpful for MPI-parallelisation. +module common_schedule + use common_environment, only : TEnvironment + implicit none + + private + public :: getStartAndEndIndex + + +contains + + !> Returns the start and end index of an MPI process that calculates parts of a loop. + subroutine getStartAndEndIndex(env, nElements, iStart, iEnd) + + !> Environment settings + type(TEnvironment), intent(in) :: env + + !> Array size to split + integer, intent(in) :: nElements + + !> Start and end index of current element range + integer, intent(out) :: iStart, iEnd + + #:if WITH_MPI + !! Size of split index regions + integer :: splitSize + + !! Number of elements that exceed integer times nProcs + integer :: offset + #:endif + + @:ASSERT(nElements >= 0) + + #:if WITH_MPI + @:ASSERT(env%mpi%globalComm%rank < env%mpi%globalComm%size) + + splitSize = nElements / env%mpi%globalComm%size + + ! start and end indices assuming equal split sizes + iStart = env%mpi%globalComm%rank * splitSize + 1 + iEnd = iStart + splitSize - 1 + + ! distribute possible remainder to the ranges at the end + offset = env%mpi%globalComm%size - mod(nElements, env%mpi%globalComm%size) + if (env%mpi%globalComm%rank + 1 > offset) then + iStart = iStart + env%mpi%globalComm%rank - offset + iEnd = iEnd + env%mpi%globalComm%rank - offset + 1 + end if + #:else + iStart = 1 + iEnd = nElements + #:endif + + end subroutine getStartAndEndIndex + +end module common_schedule diff --git a/config.cmake b/config.cmake index a34d68b9..b78abcde 100644 --- a/config.cmake +++ b/config.cmake @@ -7,10 +7,20 @@ # automatically default to RelWithDebInfo if used in a single configuration build. Uncomment or # override it only if you want a non-default single configuration build. +option(WITH_MPI "Whether SkProgs should support MPI-parallelism" FALSE) + # # Test environment settings # -set(TEST_RUNNER_TEMPLATE " " CACHE STRING "How to run the tests") +set(TEST_MPI_PROCS "1" CACHE STRING "Nr. of MPI processes used for testing") + +# Command line used to launch the test code. +# The escaped variables (\${VARIABLE}) will be substituted by the corresponding CMake variables. +if(WITH_MPI) + set(TEST_RUNNER_TEMPLATE "mpiexec -n \${TEST_MPI_PROCS}" CACHE STRING "How to run the tests") +else() + set(TEST_RUNNER_TEMPLATE " " CACHE STRING "How to run the tests") +endif() # # Installation options @@ -32,3 +42,6 @@ set(INSTALL_MODULEDIR "${INSTALL_INCLUDEDIR}/modfiles" CACHE PATH #set(TOOLCHAIN "gnu" CACHE STRING "Prefix of the toolchain file to be read from the sys/ folder") # Uncomment and set it if you want to override the automatic, compiler based toolchain file # selection. + +set(HYBRID_CONFIG_METHODS "Submodule;Find;Fetch" CACHE STRING + "Configuration methods to try in order to satisfy hybrid dependencies") diff --git a/external/mpifx/CMakeLists.txt b/external/mpifx/CMakeLists.txt new file mode 100644 index 00000000..f06f5b47 --- /dev/null +++ b/external/mpifx/CMakeLists.txt @@ -0,0 +1,8 @@ +# This CMakeLists.txt should be invoked via skprogs_config_hybrid_dependency()! + +set(BUILD_EXPORTED_TARGETS_ONLY TRUE) + +add_subdirectory(${MPIFX_SOURCE_DIR} ${MPIFX_BINARY_DIR}) + +add_library(MpiFx::MpiFx INTERFACE IMPORTED GLOBAL) +target_link_libraries(MpiFx::MpiFx INTERFACE MpiFx) diff --git a/sktools/src/sktools/calculators/sktwocnt.py b/sktools/src/sktools/calculators/sktwocnt.py index c58a56aa..7ee5eeb7 100644 --- a/sktools/src/sktools/calculators/sktwocnt.py +++ b/sktools/src/sktools/calculators/sktwocnt.py @@ -278,7 +278,7 @@ def __init__(self, binary, workdir): def run(self): fpin = open(os.path.join(self._workdir, INPUT_FILE), "r") fpout = open(os.path.join(self._workdir, STDOUT_FILE), "w") - proc = subproc.Popen([self._binary], cwd=self._workdir, + proc = subproc.Popen(self._binary.split(), cwd=self._workdir, stdin=fpin, stdout=fpout, stderr=subproc.STDOUT) proc.wait() fpin.close() diff --git a/sktwocnt/lib/CMakeLists.txt b/sktwocnt/lib/CMakeLists.txt index 779ef7c0..8b13ef03 100644 --- a/sktwocnt/lib/CMakeLists.txt +++ b/sktwocnt/lib/CMakeLists.txt @@ -1,16 +1,33 @@ +set(projectdir ${PROJECT_SOURCE_DIR}) + +# +# General options for all targets +# +set(fypp_flags ${FYPP_BUILD_FLAGS} ${FYPP_CONFIG_FLAGS}) +list(APPEND fypp_flags -I${projectdir}/common/include -DRELEASE="'${RELEASE}'") + set(sources-f90 bisection.f90 gridorbital.f90 quadrature.f90 - twocnt.f90 xcfunctionals.f90) -add_library(skprogs-sktwocnt ${sources-f90}) +set(sources-fpp + twocnt.F90) + +skprogs_preprocess("${FYPP}" "${fypp_flags}" "F90" "f90" "${sources-fpp}" sources-f90-preproc) + +add_library(skprogs-sktwocnt ${sources-f90} ${sources-f90-preproc}) target_link_libraries(skprogs-sktwocnt skprogs-common) -target_link_libraries(skprogs-sktwocnt skprogs-common Libxc::xcf03 Libxc::xc) -target_link_libraries(skprogs-sktwocnt skprogs-common LAPACK::LAPACK) +target_link_libraries(skprogs-sktwocnt Libxc::xcf03 Libxc::xc) +target_link_libraries(skprogs-sktwocnt LAPACK::LAPACK) + +if(WITH_MPI) + target_link_libraries(skprogs-sktwocnt MPI::MPI_Fortran) + target_link_libraries(skprogs-sktwocnt MpiFx::MpiFx) +endif() set(moddir ${CMAKE_CURRENT_BINARY_DIR}/modfiles) set_target_properties(skprogs-sktwocnt PROPERTIES Fortran_MODULE_DIRECTORY ${moddir}) diff --git a/sktwocnt/lib/gridorbital.f90 b/sktwocnt/lib/gridorbital.f90 index 06cb594c..717f5c14 100644 --- a/sktwocnt/lib/gridorbital.f90 +++ b/sktwocnt/lib/gridorbital.f90 @@ -128,7 +128,7 @@ elemental function TGridorb1_getValue(this, rr) result(rad) ! sanity check ! if (this%nGrid < ninter + 1) then - ! write(*,*) "Not enough points in the orbital grid!" + ! write(stdOut,*) "Not enough points in the orbital grid!" ! stop ! end if diff --git a/sktwocnt/lib/twocnt.f90 b/sktwocnt/lib/twocnt.F90 similarity index 89% rename from sktwocnt/lib/twocnt.f90 rename to sktwocnt/lib/twocnt.F90 index 9c63bf24..61119113 100644 --- a/sktwocnt/lib/twocnt.f90 +++ b/sktwocnt/lib/twocnt.F90 @@ -1,8 +1,12 @@ +#:include 'common.fypp' + !> Module that contains the two-center integrator routines for tabulating Hamiltonian and overlap. module twocnt use common_accuracy, only : dp use common_constants, only : pi, rec4pi + use common_globalenv, only : stdOut + use common_environment, only : TEnvironment use common_anglib, only : initGaunt, freeGaunt, realGaunt use common_coordtrans, only : coordtrans_becke_12, coordtrans_radial_becke2 use common_sphericalharmonics, only : TRealTessY, TRealTessY_init @@ -11,11 +15,16 @@ module twocnt use common_partition, only : partition_becke_homo use common_splines, only : spline3ders use common_fifo, only : TFiFoReal2 + use common_schedule, only : getStartAndEndIndex use common_interpolation, only : get2ndNaturalSplineDerivs, get_cubic_spline use common_poisson, only : solvePoisson, TBeckeGridParams, TBeckeIntegrator,& & TBeckeIntegrator_init, TBeckeIntegrator_setKernelParam, TBeckeIntegrator_precompFdMatrix,& & TBeckeIntegrator_buildLU, TBeckeIntegrator_getCoords, TBeckeIntegrator_solveHelmholz +#:if WITH_MPI + use extlibs_mpifx, only : MPI_SUM, MPI_MAX, mpifx_allreduceip +#:endif + use gridorbital, only : TGridorb2 use xcfunctionals, only : xcFunctional @@ -152,7 +161,10 @@ module twocnt contains !> Calculates Hamiltonian and overlap matrix elements for different dimer distances. - subroutine get_twocenter_integrals(inp, imap, skham, skover) + subroutine get_twocenter_integrals(env, inp, imap, skham, skover) + + !> Environment settings + type(TEnvironment), intent(in) :: env !> parsed twocnt input instance type(TTwocntInp), intent(in), target :: inp @@ -229,6 +241,18 @@ subroutine get_twocenter_integrals(inp, imap, skham, skover) !! number of radial and angular integration abscissas integer :: nRad, nAng + !! start and end index for MPI parallelization, if applicable + integer :: iParallelStart, iParallelEnd + + !! number of distances that fit inside a batch of length 1 Bohr + integer :: nDistPer1Bohr + + !! number of MPI ranks SkTwocnt was executed with and rank of this instance + integer :: nProcs, rank + + !! true, if the number of MPI ranks exceeds the number of distances of a 1 Bohr batch + logical :: tCoresExceedBatch + select case (inp%iXC) case(xcFunctional%LDA_PW91) call xc_f03_func_init(xcfunc_x, XC_LDA_X, XC_UNPOLARIZED) @@ -320,27 +344,48 @@ subroutine get_twocenter_integrals(inp, imap, skham, skover) call TIntegMap_init(imap, atom1, atom2) - ! calculate lines for 1 Bohr in one batch. + #:if WITH_MPI + nProcs = env%mpi%globalComm%size + rank = env%mpi%globalComm%rank + #:else + nProcs = 1 + rank = 0 + #:endif + dist = 0.0_dp tDynlen = (inp%maxdist > 0.0_dp) + ! calculate lines for 1 Bohr in one batch + nDistPer1Bohr = ceiling(1.0_dp / inp%dr) + tCoresExceedBatch = (nDistPer1Bohr < nProcs) + if (tDynlen) then - nBatchline = ceiling(1.0_dp / inp%dr) - maxdist = inp%maxdist + real(nBatchline, dp) * inp%dr + if (tCoresExceedBatch) then + ! We do not want cores to idle around, therefore increase the batch size if the number of + ! MPI ranks exceeds the number of distances of the default batch length of 1 Bohr. An + ! increased batch size later requires the Hamiltonian and overlap data to be cropped to the + ! same size one would have obtained with the default batch length of 1 Bohr. + nBatchline = nProcs + else + nBatchline = nDistPer1Bohr + end if + maxdist = inp%maxdist + real(nDistPer1Bohr, dp) * inp%dr else maxdist = abs(inp%maxdist) nBatchline = ceiling((maxdist - inp%r0) / inp%dr) end if nBatch = 0 denserrmax = 0.0_dp - allocate(denserr(nBatchline)) + allocate(denserr(nBatchline), source=0.0_dp) + call getStartAndEndIndex(env, nBatchline, iParallelStart, iParallelEnd) lpBatch: do - allocate(skhambuffer(imap%ninteg, nBatchline)) - allocate(skoverbuffer(imap%ninteg, nBatchline)) - write(*, "(A,I0,A,F6.3,A,F6.3)") "Calculating ", nBatchline, " lines: r0 = ",& + allocate(skhambuffer(imap%ninteg, nBatchline), source=0.0_dp) + allocate(skoverbuffer(imap%ninteg, nBatchline), source=0.0_dp) + write(stdOut, "(A,I0,A,F6.3,A,F6.3)") "Calculating ", nBatchline, " lines: r0 = ",& & inp%r0 + inp%dr * real(nBatch * nBatchline, dp), " dr = ", inp%dr - lpDist: do ir = 1, nBatchline + denserr(:) = 0.0_dp + lpDist: do ir = iParallelStart, iParallelEnd dist = inp%r0 + inp%dr * real(nBatch * nBatchline + ir - 1, dp) - write(*, "(A,F6.2,A)") 'Calculating dimer distance: ', dist, ' Bohr' + write(*, "(A,F6.2,A,1I0)") 'Calculating dimer distance: ', dist, ' Bohr, on rank ', rank call gengrid2_2(quads, coordtrans_becke_12, partition_becke_homo, beckepars, dist, grid1,& & grid2, dots, weights) nRad = size(quads(1)%xx) @@ -350,33 +395,51 @@ subroutine get_twocenter_integrals(inp, imap, skham, skover) & inp%camBeta, inp%tGlobalHybrid, inp%tLC, inp%tCam, imap, xcfunc_xc, xcfunc_x,& & xcfunc_xsr, xcfunc_c, skhambuffer(:, ir), skoverbuffer(:, ir), denserr(ir)) end do lpDist + #:if WITH_MPI + call mpifx_allreduceip(env%mpi%globalComm, dist, MPI_MAX) + call mpifx_allreduceip(env%mpi%globalComm, denserr, MPI_SUM) + call mpifx_allreduceip(env%mpi%globalComm, skhambuffer, MPI_SUM) + call mpifx_allreduceip(env%mpi%globalComm, skoverbuffer, MPI_SUM) + #:endif denserrmax = max(denserrmax, maxval(denserr)) maxabs = max(maxval(abs(skhambuffer)), maxval(abs(skoverbuffer))) if (tDynlen) then tConverged = (maxabs < inp%epsilon) ! if new batch gave no contributions above tolerance: omit it and exit - if (tConverged .or. dist > maxdist) exit - nBatch = nBatch + 1 + if (tConverged .or. dist > maxdist) then + if (tCoresExceedBatch) then + ! If the number of cores lead to an increased batch size, we have to store the last + ! batch to be later able to crop the data to agree with the default batch size. + call hamfifo%push_alloc(skhambuffer) + call overfifo%push_alloc(skoverbuffer) + end if + exit lpBatch + end if call hamfifo%push_alloc(skhambuffer) call overfifo%push_alloc(skoverbuffer) + nBatch = nBatch + 1 else tConverged = .true. call hamfifo%push_alloc(skhambuffer) call overfifo%push_alloc(skoverbuffer) - exit + exit lpBatch end if end do lpBatch - if (.not. tConverged) then - write(*, "(A,F6.2,A,ES10.3)") "Warning, maximal distance ", inp%maxdist,& - & " reached! Max integral value:", maxabs - end if - write(*, "(A,ES10.3)") "Maximal integration error: ", denserrmax - ! hand over Hamiltonian and overlap call hamfifo%popall_concat(skham) call overfifo%popall_concat(skover) + if (tDynlen .and. tCoresExceedBatch) then + call cropData(inp, tConverged, nDistPer1Bohr, maxdist, skham, skover) + end if + + if (.not. tConverged) then + write(stdOut, "(A,F6.2,A,ES10.3)") "Warning, maximal distance ", inp%maxdist,& + & " reached! Max integral value:", maxabs + end if + write(stdOut, "(A,ES10.3)") "Maximal integration error: ", denserrmax + ! finalize libxc objects if (inp%tGlobalHybrid .or. inp%tCam) then if (inp%iXC == xcFunctional%CAMY_PBEh) then @@ -697,6 +760,70 @@ subroutine getskintegrals(beckeInt, radialHFQuadrature, nRad, nAng, atom1, atom2 end subroutine getskintegrals + !> Crops the Hamiltonian (H) and overlap (S) matrices to the number of distances one would have + !! obtained with the default 1 Bohr batch length. + subroutine cropData(inp, tConverged, nDistPer1Bohr, maxdist, skham, skover) + + !> twocnt input instance + type(TTwocntInp), intent(in) :: inp + + !> true, if the Hamiltonian and overlap matrix elements are converged + logical, intent(in) :: tConverged + + !> maximum dimer distance to tabulate + real(dp), intent(in) :: maxdist + + !> Hamiltonian and overlap matrices to crop + real(dp), intent(inout), allocatable :: skham(:,:), skover(:,:) + + !! number of distances that fit inside a batch of length 1 Bohr + integer :: nDistPer1Bohr + + !! number of full 1 Bohr batches that H and S have been tabulated for + integer :: nBatchInTot + + !! iterates over 1 Bohr batches + integer :: iSmallBatch + + !! start and end index of the current 1 Bohr batch + integer :: iSmallBatchStart, iSmallBatchEnd + + !! maximum absolute of H and S matrix elements in current 1 Bohr batch + integer :: maxabs + + !! end index when cropping H and S + integer :: iEnd + + !! number of 1 Bohr batches within the maximum distance + integer :: nBatch + + nDistPer1Bohr = ceiling(1.0_dp / inp%dr) + + if (tConverged) then + ! crop to full 1 Bohr batches (converged version) + nBatchInTot = floor(real(size(skham, dim=2), dp) / real(nDistPer1Bohr, dp)) + do iSmallBatch = 1, nBatchInTot + iSmallBatchStart = (iSmallBatch - 1) * nDistPer1Bohr + 1 + iSmallBatchEnd = iSmallBatch * nDistPer1Bohr + maxabs = max(maxval(abs(skham(:, iSmallBatchStart:iSmallBatchEnd))),& + & maxval(abs(skover(:, iSmallBatchStart:iSmallBatchEnd)))) + if (maxabs < inp%epsilon) then + iEnd = max(iSmallBatch - 1, 1) * nDistPer1Bohr + skham = skham(:, 1:iEnd) + skover = skover(:, 1:iEnd) + exit + end if + end do + else + ! crop to full 1 Bohr batches (max. distance reached version) + nBatch = ceiling(((maxdist - inp%r0) / inp%dr + 1.0_dp) / real(nDistPer1Bohr, dp) - 1.0_dp) + skham = skham(:, 1:nBatch * nDistPer1Bohr) + skover = skover(:, 1:nBatch * nDistPer1Bohr) + end if + + end subroutine cropData + + !> Calculates libXC renormalized density superposition of dimer. pure function getLibxcRho(rho) result(rhor) @@ -797,7 +924,8 @@ subroutine getDivergence(nRad, nAng, drho1, drho2, r1, r2, theta1, theta2, vsigm tval = aa(1, :) if ((rval(2) < rval(1)) .or. (tval(2) > tval(1))) then - write(*,*) 'getDivergence: Expected ascending order in radii and descending order in theta!' + write(stdOut,*) 'getDivergence: Expected ascending order in radii and descending order in& + & theta!' stop end if diff --git a/sktwocnt/prog/CMakeLists.txt b/sktwocnt/prog/CMakeLists.txt index 624afac7..395d62e2 100644 --- a/sktwocnt/prog/CMakeLists.txt +++ b/sktwocnt/prog/CMakeLists.txt @@ -1,10 +1,22 @@ +set(projectdir ${PROJECT_SOURCE_DIR}) + +# +# General options for all targets +# +set(fypp_flags ${FYPP_BUILD_FLAGS} ${FYPP_CONFIG_FLAGS}) +list(APPEND fypp_flags -I${projectdir}/common/include -DRELEASE="'${RELEASE}'") + set(sources-f90 cmdargs.f90 input.f90 - main.f90 output.f90) -add_executable(sktwocnt ${sources-f90}) +set(sources-fpp + main.F90) + +skprogs_preprocess("${FYPP}" "${fypp_flags}" "F90" "f90" "${sources-fpp}" sources-f90-preproc) + +add_executable(sktwocnt ${sources-f90} ${sources-f90-preproc}) target_link_libraries(skprogs-sktwocnt skprogs-common Libxc::xcf03 Libxc::xc) target_link_libraries(sktwocnt skprogs-sktwocnt) diff --git a/sktwocnt/prog/cmdargs.f90 b/sktwocnt/prog/cmdargs.f90 index 689a41b0..2cee0df5 100644 --- a/sktwocnt/prog/cmdargs.f90 +++ b/sktwocnt/prog/cmdargs.f90 @@ -1,6 +1,8 @@ !> Module that handles command line argument parsing. module cmdargs + use common_globalenv, only : stdOut + implicit none private @@ -28,10 +30,10 @@ subroutine parse_command_arguments() call get_command_argument(1, arg) select case (arg) case ('--version') - write(*, '(A,1X,A)') programName, programVersion + write(stdOut, '(A,1X,A)') programName, programVersion stop case default - write(*, '(A,A,A)') "Invalid command line argument '", arg, "'" + write(stdOut, '(A,A,A)') "Invalid command line argument '", arg, "'" error stop end select end if diff --git a/sktwocnt/prog/input.f90 b/sktwocnt/prog/input.f90 index 85515dde..b1a9cf1f 100644 --- a/sktwocnt/prog/input.f90 +++ b/sktwocnt/prog/input.f90 @@ -2,6 +2,7 @@ module input use common_accuracy, only : dp + use common_globalenv, only : stdOut use gridorbital, only : TGridorb2_init use twocnt, only : TTwocntInp, TAtomdata use xcfunctionals, only : xcFunctional @@ -133,7 +134,7 @@ subroutine readInput(inp, fname) call nextline_(fp, iLine, line) read(line, *, iostat=iErr) inp%omega if (inp%omega < 1.0e-08_dp) then - write(*,'(a)') 'Chosen omega too small!' + write(stdOut,'(a)') 'Chosen omega too small!' stop end if call checkerror_(fname, line, iLine, iErr) @@ -144,7 +145,7 @@ subroutine readInput(inp, fname) call nextline_(fp, iLine, line) read(line, *, iostat=iErr) inp%omega, inp%camAlpha, inp%camBeta if (inp%omega < 1.0e-08_dp) then - write(*,'(a)') 'Chosen omega too small!' + write(stdOut,'(a)') 'Chosen omega too small!' stop end if call checkerror_(fname, line, iLine, iErr) @@ -257,10 +258,10 @@ subroutine readatom_(fname, fp, iLine, potcomps, tDensitySuperpos, tReadRadDeriv ! (positive where abs(r * R(r)) has its maximum) imax = maxloc(abs(data(:, 1) * data(:, 2)), dim=1) if (data(imax, 2) < 0.0_dp) then - write(*, "(A,F5.2,A)") "Wave function negative at the maximum of radial probability& + write(stdOut, "(A,F5.2,A)") "Wave function negative at the maximum of radial probability& & (r =", data(imax, 1), " Bohr)" - write(*, "(A)") "Please change the sign of the wave function (and of its derivatives)!" - write(*, "(A,A,A)") "File: '", trim(buffer), "'" + write(stdOut, "(A)") "Please change the sign of the wave function (and of its derivatives)!" + write(stdOut, "(A,A,A)") "File: '", trim(buffer), "'" stop end if end do @@ -307,7 +308,7 @@ subroutine readatom_(fname, fp, iLine, potcomps, tDensitySuperpos, tReadRadDeriv call TGridorb2_init(atom%ddrho, data(:, 1), data(:, 4)) else if (trim(line) /= "noread") then - write(*, "(A,I0,A)") "Line ", iLine, " ignored since density is not needed." + write(stdOut, "(A,I0,A)") "Line ", iLine, " ignored since density is not needed." end if end if @@ -410,7 +411,7 @@ subroutine checkangmoms_(angmoms) integer, intent(in) :: angmoms(:) if (maxval(angmoms) > 4) then - write(*,*) "Only angular momentum up to 'f' is allowed." + write(stdOut,*) "Only angular momentum up to 'f' is allowed." stop end if @@ -454,10 +455,10 @@ subroutine error_(txt, fname, line, iLine) !> index of erroneous line integer, intent(in) :: iLine - write(*, "(A,A)") "!!! Parsing error: ", txt - write(*, "(2X,A,A)") "File: ", trim(fname) - write(*, "(2X,A,I0)") "Line number: ", iLine - write(*, "(2X,A,A,A)") "Line: '", trim(line), "'" + write(stdOut, "(A,A)") "!!! Parsing error: ", txt + write(stdOut, "(2X,A,A)") "File: ", trim(fname) + write(stdOut, "(2X,A,I0)") "Line number: ", iLine + write(stdOut, "(2X,A,A,A)") "Line: '", trim(line), "'" stop diff --git a/sktwocnt/prog/main.f90 b/sktwocnt/prog/main.F90 similarity index 53% rename from sktwocnt/prog/main.f90 rename to sktwocnt/prog/main.F90 index 182c423d..50acef8e 100644 --- a/sktwocnt/prog/main.f90 +++ b/sktwocnt/prog/main.F90 @@ -1,7 +1,11 @@ +#:include 'common.fypp' + !> Program to calculate two-center integrals of Slater-Koster tables. program main use common_accuracy, only : dp + use common_environment, only : TEnvironment, TEnvironment_init + use common_globalenv, only : initGlobalEnv, destructGlobalEnv, stdOut use input, only : readInput use twocnt, only : TTwocntInp, TIntegMap, get_twocenter_integrals use output, only : write_sktables @@ -18,14 +22,26 @@ program main !> resulting Hamiltonian and overlap matrices real(dp), allocatable :: skham(:,:), skover(:,:) + type(TEnvironment) :: env + + call initGlobalEnv() + call TEnvironment_init(env) +#:if WITH_MPI + call env%initMpi(1) +#:endif call parse_command_arguments() call readInput(inp, "sktwocnt.in") - write(*, "(A)") "Input done." + write(stdOut, "(A)") "Reading input done." + + call get_twocenter_integrals(env, inp, imap, skham, skover) + write(stdOut, "(A)") "Two-center integration done." - call get_twocenter_integrals(inp, imap, skham, skover) - write(*, "(A)") "Twocnt done." + if (env%tGlobalLead) then + call write_sktables(skham, skover) + end if - call write_sktables(skham, skover) + call env%destruct() + call destructGlobalEnv() end program main diff --git a/sktwocnt/prog/output.f90 b/sktwocnt/prog/output.f90 index 43d941e8..aa5a58e6 100644 --- a/sktwocnt/prog/output.f90 +++ b/sktwocnt/prog/output.f90 @@ -2,6 +2,7 @@ module output use common_accuracy, only : dp + use common_globalenv, only : stdOut implicit none private @@ -17,8 +18,19 @@ subroutine write_sktables(skham, skover) !> Hamiltonian and overlap matrix real(dp), intent(in) :: skham(:,:), skover(:,:) - call write_sktable_("at1-at2.ham.dat", skham) - call write_sktable_("at1-at2.over.dat", skover) + if (size(skham, dim=2) > 0) then + call write_sktable_("at1-at2.ham.dat", skham) + write(stdOut, "(A)") "SK-table (Hamiltonian) written." + else + write(stdOut, "(A)") "Nothing to write (Hamiltonian)." + end if + + if (size(skover, dim=2) > 0) then + call write_sktable_("at1-at2.over.dat", skover) + write(stdOut, "(A)") "SK-table (overlap) written." + else + write(stdOut, "(A)") "Nothing to write (overlap)." + end if end subroutine write_sktables diff --git a/test/prog/sktable/CMakeLists.txt b/test/prog/sktable/CMakeLists.txt index 936d4a15..03880d6d 100644 --- a/test/prog/sktable/CMakeLists.txt +++ b/test/prog/sktable/CMakeLists.txt @@ -15,7 +15,7 @@ set(fypp_flags ${FYPP_CONFIG_FLAGS}) list(APPEND fypp_flags -I${projectdir}/common/include -DRELEASE="'${releasename}'") execute_process( - COMMAND ${FYPP} ${fypp_flags} + COMMAND ${FYPP} ${fypp_flags} -DMPI_PROCS=${TEST_MPI_PROCS} INPUT_FILE ${builddir}/_sktable_tests.fypp OUTPUT_FILE ${builddir}/_sktable_tests) @@ -31,5 +31,5 @@ foreach(test IN LISTS tests) add_test( NAME sktable_${test} COMMAND ${PYTHON_INTERPRETER} ${srcdir}/bin/testwithworkdir ${test} ${builddir} - -r ${srcdir}) + -r ${srcdir} -m ${TEST_RUNNER}) endforeach() diff --git a/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/_C-C.skf b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/_C-C.skf new file mode 100644 index 00000000..2bee28f4 --- /dev/null +++ b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/_C-C.skf @@ -0,0 +1,42 @@ +0.500000 19 + 0.000000000000E+00 -1.991451862433E-01 -5.008031450092E-01 -4.388315590227E-02 0.000000000000E+00 3.643020862183E-01 3.643020862183E-01 0.000000000000E+00 2.000000000000E+00 2.000000000000E+00 + 1.201000000000E+01 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 3.219086673683E-04 -1.955462093865E-05 0.000000000000E+00 1.800903426339E-04 -8.155727862988E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -1.974277096770E-04 1.209670522897E-05 0.000000000000E+00 -1.342770484994E-04 2.744297023381E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.228469937433E-04 -6.266286348893E-06 0.000000000000E+00 6.767859252726E-05 -1.453381467879E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -1.009520160444E-04 3.006612318733E-06 0.000000000000E+00 -5.703809671614E-05 -2.777821745454E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 4.909066157092E-05 -1.475531792103E-06 0.000000000000E+00 2.236043875471E-05 1.199590708793E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -5.297820724564E-05 -1.320577661839E-06 0.000000000000E+00 -9.753202995221E-06 -5.221413384600E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.047400797332E-05 3.252581690915E-07 0.000000000000E+00 1.745201506921E-06 2.062966143228E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -2.154277831327E-05 -3.164061077605E-06 0.000000000000E+00 1.955180355808E-05 -5.571035481656E-05 + + +Spline +12 0.0553585 +112.9353346817185 2.801373701455403 -0.1119994835253462 +0.035 0.0375 0.204206 -35.71077211012958 2016.504000000031 24177.93762071238 +0.0375 0.04 0.12791 -25.17491577974109 2197.838532155373 -120889.6881035729 +0.04 0.0425 0.07682029999999999 -16.45240477090621 1291.165871378576 -57585.58520643491 +0.0425 0.045 0.0428593 -11.07630513663398 859.2739823303137 16659.22892930921 +0.045 0.04533 0.0207993 -6.467574682557872 984.2181993001326 -2167173.572075024 +0.04533 0.045334 0.0186943 -6.526006277016704 -1161.283637054166 353213222.4907721 +0.045334 0.046259 0.0186682 -6.518342311433599 3077.275032831984 -1324559.571220061 +0.046259 0.047184 0.0142234 -4.225362350069925 -598.3777773036936 561811.1110751317 +0.047184 0.0493131 0.0102476 -3.890262342340788 960.6480559297889 -100763.5210502349 +0.0493131 0.0503195 0.00534702 -1.169934109375229 317.0412179256228 -143026.9144497911 +0.0503195 0.0513259 0.00434492 -0.9663840979460291 -114.7856421811885 10348.58893883691 +0.0513259 0.0553585 0.00326664 -1.165980214261954 -83.5411824570522 -5782.515169399558 27636944.82683195 -3877959552.095367 + +This SPLINE is just a DUMMY-SPLINE!!!!!!!!!!!!!!! + diff --git a/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/config b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/config new file mode 100644 index 00000000..3aab270d --- /dev/null +++ b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/config @@ -0,0 +1 @@ +C C diff --git a/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/skdef.hsd b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/skdef.hsd new file mode 100644 index 00000000..6ced8eab --- /dev/null +++ b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-conv/skdef.hsd @@ -0,0 +1,72 @@ +SkdefVersion = 1 + +Globals { + Superposition = density + XCFunctional = LDA {} +} + +AtomParameters { + + C { + AtomConfig { + AtomicNumber = 6 + Mass = 12.01 + Occupations { + 1S = 1.0 1.0 + 2S = 1.0 1.0 + 2P = 2.0 0.0 + } + ValenceShells = 2s 2p + Relativistics = None + } + DftbAtom { + ShellResolved = No + DensityCompression = PowerCompression { Power = 2; Radius = 7.0 } + WaveCompressions = SingleAtomCompressions { + S = PowerCompression { Power = 2; Radius = 2.7 } + P = PowerCompression { Power = 2; Radius = 2.7 } + } + } + } + +} + + +OnecenterParameters { + + $StandardDeltaFilling { + DeltaFilling = 0.01 + } + + C { + $StandardDeltaFilling + Calculator = SlaterAtom { + Exponents { + S = 0.5 1.14 2.62 6.0 + P = 0.5 1.14 2.62 6.0 + } + MaxPowers { + S = 3 + P = 3 + } + } + } + +} + +TwoCenterParameters { + + $EqGrid = EquidistantGrid { + GridStart = 8.0 + GridSeparation = 0.5 + Tolerance = 5e-5 + MaxDistance = 12.0 + } + + $SkTwocnt_300_150 = Sktwocnt { + IntegrationPoints = 300 150 + } + + C-C { Grid = $EqGrid; Calculator = $SkTwocnt_300_150 } + +} diff --git a/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/_C-C.skf b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/_C-C.skf new file mode 100644 index 00000000..7c6e45ff --- /dev/null +++ b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/_C-C.skf @@ -0,0 +1,48 @@ +0.500000 25 + 0.000000000000E+00 -1.991451862433E-01 -5.008031450092E-01 -4.388315590227E-02 0.000000000000E+00 3.643020862183E-01 3.643020862183E-01 0.000000000000E+00 2.000000000000E+00 2.000000000000E+00 + 1.201000000000E+01 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 3.219086673683E-04 -1.955462093865E-05 0.000000000000E+00 1.800903426339E-04 -8.155727862988E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -1.974277096770E-04 1.209670522897E-05 0.000000000000E+00 -1.342770484994E-04 2.744297023381E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.228469937433E-04 -6.266286348893E-06 0.000000000000E+00 6.767859252726E-05 -1.453381467879E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -1.009520160444E-04 3.006612318733E-06 0.000000000000E+00 -5.703809671614E-05 -2.777821745454E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 4.909066157092E-05 -1.475531792103E-06 0.000000000000E+00 2.236043875471E-05 1.199590708793E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -5.297820724564E-05 -1.320577661839E-06 0.000000000000E+00 -9.753202995221E-06 -5.221413384600E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.047400797332E-05 3.252581690915E-07 0.000000000000E+00 1.745201506921E-06 2.062966143228E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -2.154277831327E-05 -3.164061077605E-06 0.000000000000E+00 1.955180355808E-05 -5.571035481656E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 7.063622131663E-06 9.625515572233E-07 0.000000000000E+00 -8.526308665088E-06 1.996835788813E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 3.430861462875E-07 -3.499518052138E-06 0.000000000000E+00 3.427845533508E-05 -4.541637226559E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -6.486186365540E-07 1.054240409289E-06 0.000000000000E+00 -1.296048334703E-05 1.418623554365E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.383793345544E-05 -2.963448902885E-06 0.000000000000E+00 3.737537198328E-05 -2.791987934497E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -5.183000983418E-06 8.635250595737E-07 0.000000000000E+00 -1.336257931418E-05 6.113292508301E-06 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.999995439150E-05 -2.034913172819E-06 0.000000000000E+00 3.251616618959E-05 -8.763086604784E-06 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -7.274730120167E-06 5.472458444468E-07 0.000000000000E+00 -1.106775078140E-05 -2.103818266608E-06 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 2.061203707744E-05 -1.047185407574E-06 0.000000000000E+00 2.341202817384E-05 8.155530513834E-06 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -7.468690382990E-06 2.117453765767E-07 0.000000000000E+00 -7.331090226859E-06 -8.988969613913E-06 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.771796717585E-05 -1.962395798075E-07 0.000000000000E+00 1.310077315003E-05 2.069459751665E-05 + 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 -6.386677784830E-06 -7.702235117823E-08 0.000000000000E+00 -3.222548200783E-06 -1.374764544683E-05 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 0.000000000000E+00 1.312792135917E-05 4.316364845240E-07 0.000000000000E+00 3.641528826244E-06 2.818443964454E-05 + + +Spline +12 0.0553585 +112.9353346817185 2.801373701455403 -0.1119994835253462 +0.035 0.0375 0.204206 -35.71077211012958 2016.504000000031 24177.93762071238 +0.0375 0.04 0.12791 -25.17491577974109 2197.838532155373 -120889.6881035729 +0.04 0.0425 0.07682029999999999 -16.45240477090621 1291.165871378576 -57585.58520643491 +0.0425 0.045 0.0428593 -11.07630513663398 859.2739823303137 16659.22892930921 +0.045 0.04533 0.0207993 -6.467574682557872 984.2181993001326 -2167173.572075024 +0.04533 0.045334 0.0186943 -6.526006277016704 -1161.283637054166 353213222.4907721 +0.045334 0.046259 0.0186682 -6.518342311433599 3077.275032831984 -1324559.571220061 +0.046259 0.047184 0.0142234 -4.225362350069925 -598.3777773036936 561811.1110751317 +0.047184 0.0493131 0.0102476 -3.890262342340788 960.6480559297889 -100763.5210502349 +0.0493131 0.0503195 0.00534702 -1.169934109375229 317.0412179256228 -143026.9144497911 +0.0503195 0.0513259 0.00434492 -0.9663840979460291 -114.7856421811885 10348.58893883691 +0.0513259 0.0553585 0.00326664 -1.165980214261954 -83.5411824570522 -5782.515169399558 27636944.82683195 -3877959552.095367 + +This SPLINE is just a DUMMY-SPLINE!!!!!!!!!!!!!!! + diff --git a/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/config b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/config new file mode 100644 index 00000000..3aab270d --- /dev/null +++ b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/config @@ -0,0 +1 @@ +C C diff --git a/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/skdef.hsd b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/skdef.hsd new file mode 100644 index 00000000..901a17cd --- /dev/null +++ b/test/prog/sktable/LDA-PW91/Non-Relativistic/Dynamic-noconv/skdef.hsd @@ -0,0 +1,72 @@ +SkdefVersion = 1 + +Globals { + Superposition = density + XCFunctional = LDA {} +} + +AtomParameters { + + C { + AtomConfig { + AtomicNumber = 6 + Mass = 12.01 + Occupations { + 1S = 1.0 1.0 + 2S = 1.0 1.0 + 2P = 2.0 0.0 + } + ValenceShells = 2s 2p + Relativistics = None + } + DftbAtom { + ShellResolved = No + DensityCompression = PowerCompression { Power = 2; Radius = 7.0 } + WaveCompressions = SingleAtomCompressions { + S = PowerCompression { Power = 2; Radius = 2.7 } + P = PowerCompression { Power = 2; Radius = 2.7 } + } + } + } + +} + + +OnecenterParameters { + + $StandardDeltaFilling { + DeltaFilling = 0.01 + } + + C { + $StandardDeltaFilling + Calculator = SlaterAtom { + Exponents { + S = 0.5 1.14 2.62 6.0 + P = 0.5 1.14 2.62 6.0 + } + MaxPowers { + S = 3 + P = 3 + } + } + } + +} + +TwoCenterParameters { + + $EqGrid = EquidistantGrid { + GridStart = 8.0 + GridSeparation = 0.5 + Tolerance = 5e-6 + MaxDistance = 12.0 + } + + $SkTwocnt_300_150 = Sktwocnt { + IntegrationPoints = 300 150 + } + + C-C { Grid = $EqGrid; Calculator = $SkTwocnt_300_150 } + +} diff --git a/test/prog/sktable/bin/testwithworkdir b/test/prog/sktable/bin/testwithworkdir index e0f669d5..fbd034dd 100644 --- a/test/prog/sktable/bin/testwithworkdir +++ b/test/prog/sktable/bin/testwithworkdir @@ -51,15 +51,16 @@ def main(cmdlineargs=None): with open(os.path.join(cwd, 'config'), 'r') as fd: config = fd.readline() + slateratom_bin = os.path.abspath('../../../slateratom/prog/slateratom') + sktwocnt_bin = ' '.join( + [args.mpi_prefix, os.path.abspath('../../../sktwocnt/prog/sktwocnt')]) + cmd = 'python3 ' \ + os.path.abspath('../../../../sktools/src/sktools/scripts/skgen.py') \ + ' ' + '-c {} '.format( os.path.join(cwd, 'skdef.hsd')) \ - + '-o {} '.format(os.path.abspath( - '../../../slateratom/prog/slateratom')) \ - + '-t {} '.format( - os.path.abspath('../../../sktwocnt/prog/sktwocnt')) \ - + 'sktable ' + config + + '-o "{}" '.format(slateratom_bin) \ + + '-t "{}" '.format(sktwocnt_bin) + 'sktable ' + config # remove _build folder and old files if present shutil.rmtree(os.path.join(cwd, '_build'), ignore_errors=True) @@ -110,6 +111,10 @@ def parse_cmdline_args(cmdlineargs=None): dest='templateroot', default=os.getcwd(), type=str, help=msg) + msg = 'prefix for calling MPI-enabled binaries' + parser.add_argument('-m', '--mpi-prefix', action='store', dest='mpi_prefix', + default='', type=str, help=msg) + args = parser.parse_args(cmdlineargs) return args diff --git a/test/prog/sktable/tests b/test/prog/sktable/tests index af0e0790..69c729ce 100644 --- a/test/prog/sktable/tests +++ b/test/prog/sktable/tests @@ -3,6 +3,8 @@ #:include 'common.fypp' +LDA-PW91/Non-Relativistic/Dynamic-conv +LDA-PW91/Non-Relativistic/Dynamic-noconv LDA-PW91/Non-Relativistic/Power LDA-PW91/Non-Relativistic/WS GGA-PBE96/Non-Relativistic diff --git a/utils/export/skprogs-config.cmake.in b/utils/export/skprogs-config.cmake.in index 156bfc57..e53ab017 100644 --- a/utils/export/skprogs-config.cmake.in +++ b/utils/export/skprogs-config.cmake.in @@ -1,10 +1,21 @@ @PACKAGE_INIT@ +# Global DFTB+ config options +set(SkProgs_WITH_MPI @WITH_MPI@) + include(CMakeFindDependencyMacro) if(NOT TARGET SkProgs::sktwocnt) + + if(SkProgs_WITH_MPI) + if(NOT TARGET MpiFx::MpiFx) + find_dependency(MpiFx) + endif() + endif() + if (NOT TARGET Libxc::xc) find_dependency(Libxc) endif() include(${CMAKE_CURRENT_LIST_DIR}/skprogs-targets.cmake) + endif() diff --git a/utils/test/check_submodule_commits b/utils/test/check_submodule_commits new file mode 100755 index 00000000..d305dc4a --- /dev/null +++ b/utils/test/check_submodule_commits @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 + +"""Checks the submodule commit IDs in integration CMake files.""" + +import argparse +import configparser +import re +import subprocess +import sys + +_DESCRIPTION = """ +Checks (and optionally updates) the submodule commit IDs in the integration +CMake files. It loops over all git submodules, reads their commit hashes and +checks, whether it matches the commit ID in the CMakeLists.txt in the parent +folder. The script must be run from the projects root folder and the git command +should work. +""" + +_PAT_SUBMOD_COMMIT = re.compile( + r"""^[-+ ]*(?P[0-9a-f]+) + \s+ + external/(?P[0-9a-zA-Z_-]+)/origin + \s+.*$""", + re.MULTILINE | re.VERBOSE) + +_PAT_SPECIAL_CHAR = re.compile(r"[-_]") + +_SPECIAL_CHAR_REPLACEMENT = "_" + +def main(): + """Main script""" + args = _parse_arguments() + result = subprocess.run(["git", "submodule", "status"], + stdout=subprocess.PIPE, check=True) + output = result.stdout.decode('ascii') + if args.check: + gitconfig = configparser.ConfigParser() + gitconfig.read('.git/config') + allsync = True + with open("CMakeLists.txt", "r") as fp: + origcontent = fp.read() + content = origcontent + for match in _PAT_SUBMOD_COMMIT.finditer(output): + commit, submodule = match.groups() + if args.check: + locale = ('submodule "external/%s/origin"' % submodule) + active = gitconfig.get(locale, 'active') + if active == 'true': + url = gitconfig.get(locale, 'url') + _check_submodule_url(content, submodule, url) + newcontent, nn = _replace_submodule_commit(content, submodule, commit) + if nn < 1: + print("! Commit for submodule {} was NOT FOUND".format(submodule)) + allsync = False + continue + sync = (newcontent == content) + if sync: + print("+ Commit ID for submodule {} matches.".format(submodule)) + elif args.update: + content = newcontent + print("* Commit for submodule {} has been updated."\ + .format(submodule)) + else: + print("! Commit for submodule {} DOES NOT MATCH!".format(submodule)) + allsync = False + if content != origcontent: + with open("CMakeLists.txt", "w") as fp: + fp.write(content) + if not allsync: + sys.exit(1) + + +def _parse_arguments(): + """Returns command line arguments""" + parser = argparse.ArgumentParser(description=_DESCRIPTION) + msg = "Whether the commits should be updated in the CMakeLists.txt files" + parser.add_argument("-u", "--update", action='store_true', help=msg) + msg = "Check consistency between CMakeLists.txt and submodules" + parser.add_argument("-c", "--check", action='store_true', help=msg) + return parser.parse_args() + + +def _replace_submodule_commit(content, submodule, commit): + """Replaces the commit id in the CMakeLists.txt file content""" + submodule_normed = submodule.upper() + submodule_normed = re.sub( + _PAT_SPECIAL_CHAR, _SPECIAL_CHAR_REPLACEMENT, submodule_normed) + + newcontent, nsubs = re.subn( + r"set\({}_GIT_TAG (['\"]).+\1\)".format(submodule_normed), + 'set({}_GIT_TAG "{}")'.format(submodule_normed, commit), + content) + return newcontent, nsubs + + +def _check_submodule_url(content, submodule, url): + """Checks consistency of the submodule url in the CMakeLists.txt file""" + submodule_normed = submodule.upper() + submodule_normed = re.sub( + _PAT_SPECIAL_CHAR, _SPECIAL_CHAR_REPLACEMENT, submodule_normed) + match = re.findall( + r"set\({}_GIT_REPOSITORY \"({})(\.git)?\"\)".format(submodule_normed, url), + content) + if (len(match) == 0): + print('** {} : mismatch between .git/config and CMakeLists.txt'.format(submodule)) + else: + if (match[0][1] == '.git'): + print("* {} : bare vs non-bare repository address in CMakeLists.txt and .git/config".format(submodule)) + + +if __name__ == '__main__': + main()