From 3d38c45ca099fb871bf55ccceff25c737deada2a Mon Sep 17 00:00:00 2001 From: Ben Hourahine Date: Sun, 25 May 2025 05:21:23 +0100 Subject: [PATCH 1/8] Test version using fortuno WIP Note: The ctester intentionally has a fail, but the status does not propagate to ctest correctly. --- CMakeLists.txt | 213 +++++++++++++++++++++------- cmake/scalapackfx.pc.in | 6 + cmake/scalapackfxConfig.cmake.in | 6 + cmake/scalapackfxHelpers.cmake | 77 ++++++++++ example/CMakeLists.txt | 12 ++ example/common_module.f90 | 103 ++++++++++++++ example/scalapackfx_example.f90 | 28 ++++ example/transpose.f90 | 90 ++++++++++++ {lib => include}/scalapackfx.fypp | 0 src/CMakeLists.txt | 68 +++++++++ {lib => src}/blacs.fpp | 0 {lib => src}/blacsfx.fpp | 0 {lib => src}/blacsgrid.fpp | 0 {lib => src}/libscalapackfx.fpp | 0 {lib => src}/linecomm.fpp | 0 {lib => src}/module.fpp | 0 {lib => src}/pblas.fpp | 0 {lib => src}/pblasfx.fpp | 0 {lib => src}/scalapack.fpp | 0 {lib => src}/scalapackfx.fpp | 0 {lib => src}/scalapackfx_common.fpp | 0 {lib => src}/scalapackfx_tools.fpp | 0 subprojects/Fortuno.cmake | 26 ++++ subprojects/fypp.cmake | 20 +++ test/CMakeLists.txt | 54 +++---- test/test_desc.f90 | 87 +++++++++--- 26 files changed, 687 insertions(+), 103 deletions(-) create mode 100644 cmake/scalapackfx.pc.in create mode 100644 cmake/scalapackfxConfig.cmake.in create mode 100644 cmake/scalapackfxHelpers.cmake create mode 100644 example/CMakeLists.txt create mode 100644 example/common_module.f90 create mode 100644 example/scalapackfx_example.f90 create mode 100644 example/transpose.f90 rename {lib => include}/scalapackfx.fypp (100%) create mode 100644 src/CMakeLists.txt rename {lib => src}/blacs.fpp (100%) rename {lib => src}/blacsfx.fpp (100%) rename {lib => src}/blacsgrid.fpp (100%) rename {lib => src}/libscalapackfx.fpp (100%) rename {lib => src}/linecomm.fpp (100%) rename {lib => src}/module.fpp (100%) rename {lib => src}/pblas.fpp (100%) rename {lib => src}/pblasfx.fpp (100%) rename {lib => src}/scalapack.fpp (100%) rename {lib => src}/scalapackfx.fpp (100%) rename {lib => src}/scalapackfx_common.fpp (100%) rename {lib => src}/scalapackfx_tools.fpp (100%) create mode 100644 subprojects/Fortuno.cmake create mode 100644 subprojects/fypp.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index ecfec12..fdf3753 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,72 +1,175 @@ -cmake_minimum_required(VERSION 3.16) +#[=============================================================================[ +# Basic project definition # +]=============================================================================] +cmake_minimum_required(VERSION 3.22...3.29) + +list(APPEND CMAKE_MESSAGE_CONTEXT scalapackfx) + +project( + scalapackfx + VERSION 1.3.0 + DESCRIPTION "Modern Fortran Interface for ScaLAPACK" + LANGUAGES Fortran +) + +#[=============================================================================[ +# Options # +]=============================================================================] + +option( + SCALAPACKFX_BUILD_SHARED_LIBS + "scalapackfx: Build as shared library" + OFF +) + +option( + SCALAPACKFX_BUILD_EXAMPLES + "scalapackfx: Whether to build the examples" + ${PROJECT_IS_TOP_LEVEL} +) +option( + SCALAPACKFX_BUILD_TESTS + "scalapackfx: Whether to build the tests" + ${PROJECT_IS_TOP_LEVEL} +) +option( + SCALAPACKFX_INSTALL + "scalapackfx: Install project" + ${PROJECT_IS_TOP_LEVEL} +) +set( + SCALAPACKFX_INSTALL_MODULEDIR + "modules" + CACHE STRING + "scalapackfx: Sub-directory for installed Fortran module files (relative to CMAKE_INSTALL_LIBDIR)" +) + +option( + SCALAPACKFX_SUBPROJECT_REQUIRE_FIND + "scalapackfx: Require find_package for all subprojects" + OFF +) +option( + SCALAPACKFX_SUBPROJECT_DISABLE_FIND + "scalapackfx: Disable find_package for all subprojects" + OFF +) + +#[=============================================================================[ +# Project configuration # +]=============================================================================] + +include(FetchContent) include(CMakePackageConfigHelpers) +include(GNUInstallDirs) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake +list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) +include(scalapackfxHelpers) include(ScalapackFxUtils) -include(${CMAKE_CURRENT_SOURCE_DIR}/config.cmake) +set(BUILD_SHARED_LIBS ${SCALAPACKFX_BUILD_SHARED_LIBS}) +scalapackfx_setup_build_type("RelWithDebInfo") -project(ScalapackFx VERSION 1.2.0 LANGUAGES Fortran) - -setup_build_type() +find_package(MPI REQUIRED) # # Prerequisites # find_package(CustomScalapack REQUIRED QUIET) find_package(CustomLapack REQUIRED) -find_program(FYPP fypp) -if(NOT FYPP) - message(FATAL_ERROR "Preprocessor fypp could not be found") -endif() -# -# Build instructions -# -include(GNUInstallDirs) +include(subprojects/fypp.cmake) + +# Extra flags from cmake options and compiler capabilities +scalapackfx_add_fypp_defines(FYPP_FLAGS) -add_subdirectory(lib) -if(NOT BUILD_EXPORTED_TARGETS_ONLY) +set(FYPP_FLAGS "${FYPP_FLAGS}") + +set(FYPP_CONFIG_FLAGS "${FYPP_FLAGS}") +# Make sure, the line-marker option is not set +list(REMOVE_ITEM FYPP_CONFIG_FLAGS "-n") +set(FYPP_BUILD_FLAGS "${FYPP_FLAGS}" "--file-var-root=${CMAKE_SOURCE_DIR}" + "$,-DDEBUG=1,-DDEBUG=0>") + +set(PYTHON_INTERPRETER "python3" CACHE STRING + "Python interpreter to use for preprocessor") + +set(fypp_flags ${FYPP_BUILD_FLAGS}) +list(APPEND + fypp_flags -I${CMAKE_CURRENT_SOURCE_DIR}/include -DRELEASE="'${VERSION}'" +) + +#[=============================================================================[ +# Main definition # +]=============================================================================] + +add_subdirectory(src) + +if (SCALAPACKFX_BUILD_EXAMPLES) + add_subdirectory(example) +endif () +if (SCALAPACKFX_BUILD_TESTS) + include(subprojects/Fortuno.cmake) + enable_testing() add_subdirectory(test) -endif() +endif () -# -# Installation -# -add_library(ScalapackFx INTERFACE) -target_link_libraries(ScalapackFx INTERFACE scalapackfx) -install(TARGETS ScalapackFx EXPORT scalapackfx-targets) - -install(EXPORT scalapackfx-targets - FILE scalapackfx-targets.cmake - NAMESPACE ScalapackFx:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/scalapackfx" - EXPORT_LINK_INTERFACE_LIBRARIES) - -configure_package_config_file( - ${CMAKE_CURRENT_SOURCE_DIR}/utils/export/scalapackfx-config.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/cmake/scalapackfx-config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/scalapackfx) - -write_basic_package_version_file( - ${CMAKE_CURRENT_BINARY_DIR}/cmake/scalapackfx-config-version.cmake - VERSION ${PROJECT_VERSION} - COMPATIBILITY SameMajorVersion) - -install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/cmake/scalapackfx-config.cmake - ${CMAKE_CURRENT_BINARY_DIR}/cmake/scalapackfx-config-version.cmake - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/scalapackfx) - -install( - DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules - DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/scalapackfx) - -get_pkgconfig_params(PKGCONFIG_REQUIRES PKGCONFIG_LIBS PKGCONFIG_LIBS_PRIVATE PKGCONFIG_C_FLAGS) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/utils/export/scalapackfx.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/scalapackfx.pc @ONLY) -install( - FILES "${CMAKE_CURRENT_BINARY_DIR}/scalapackfx.pc" - DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + +#[=============================================================================[ +# Install or Export # +]=============================================================================] + +if (SCALAPACKFX_INSTALL) + + # pkg-config files + configure_file(cmake/scalapackfx.pc.in scalapackfx.pc @ONLY) + install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/scalapackfx.pc + DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig + COMPONENT scalapackfx_development + ) + + # cmake export files + write_basic_package_version_file( + ${CMAKE_CURRENT_BINARY_DIR}/scalapackfxConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion + ) + configure_package_config_file( + cmake/scalapackfxConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/scalapackfxConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/scalapackfx + ) + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/scalapackfxConfigVersion.cmake + ${CMAKE_CURRENT_BINARY_DIR}/scalapackfxConfig.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/scalapackfx + COMPONENT scalapackfx_development + ) + + export( + EXPORT scalapackfxTargets + FILE scalapackfxTargets.cmake + NAMESPACE scalapackfx:: + ) + install( + EXPORT scalapackfxTargets + FILE scalapackfxTargets.cmake + NAMESPACE scalapackfx:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/scalapackfx + COMPONENT scalapackfx_development + ) +endif () + +# Make project available for FetchContent +if (NOT PROJECT_IS_TOP_LEVEL) + # Propagate variables + set(scalapackfx_VERSION ${scalapackfx_VERSION} PARENT_SCOPE) + set(scalapackfx_VERSION_MAJOR ${scalapackfx_VERSION_MAJOR} PARENT_SCOPE) + set(scalapackfx_VERSION_MINOR ${scalapackfx_VERSION_MINOR} PARENT_SCOPE) + set(scalapackfx_VERSION_PATCH ${scalapackfx_VERSION_PATCH} PARENT_SCOPE) + set(scalapackfx_VERSION_TWEAK ${scalapackfx_VERSION_TWEAK} PARENT_SCOPE) +endif () diff --git a/cmake/scalapackfx.pc.in b/cmake/scalapackfx.pc.in new file mode 100644 index 0000000..9c1fd82 --- /dev/null +++ b/cmake/scalapackfx.pc.in @@ -0,0 +1,6 @@ +Name: @PROJECT_NAME@ +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ + +Cflags: -I@CMAKE_INSTALL_FULL_LIBDIR@/@SCALAPACKFX_INSTALL_MODULEDIR@ +Libs: -L@CMAKE_INSTALL_FULL_LIBDIR@ -lscalapackfx diff --git a/cmake/scalapackfxConfig.cmake.in b/cmake/scalapackfxConfig.cmake.in new file mode 100644 index 0000000..2d29e7e --- /dev/null +++ b/cmake/scalapackfxConfig.cmake.in @@ -0,0 +1,6 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(MPI) + +include(${CMAKE_CURRENT_LIST_DIR}/scalapackfxTargets.cmake) diff --git a/cmake/scalapackfxHelpers.cmake b/cmake/scalapackfxHelpers.cmake new file mode 100644 index 0000000..dccf12e --- /dev/null +++ b/cmake/scalapackfxHelpers.cmake @@ -0,0 +1,77 @@ +# Sets up the build type. +function (scalapackfx_setup_build_type default_build_type) + + get_property(_multiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if (_multiConfig) + message(STATUS "Build type multi-config (build type selected at the build step)") + else () + if (NOT CMAKE_BUILD_TYPE) + message(STATUS "Build type ${default_build_type} (default single-config)") + set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE STRING "Build type" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build") + set_property( + CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo" + ) + else () + message(STATUS "scalapackfx: build type: ${CMAKE_BUILD_TYPE} (manually selected single-config)") + endif () + endif () + +endfunction () + +# Replaces the extension of a given file +# +# Args: +# oldext [in]: Old extension +# newext [in]: New extension +# fname [in]: File name in which extension should be replaced. +# newfname [out]: File name after extension replacement. +# +function(scalapackfx_replace_extension oldext newext fname newfname) + + string(REGEX REPLACE "\\.${oldext}$" ".${newext}" _newfname ${fname}) + set(${newfname} ${_newfname} PARENT_SCOPE) + +endfunction() + + +# Registers files for preprocessing +# +# Args: +# preproc [in]: Preprocessor to use +# preprocopts [in]: Preprocessor command line arguments (but not in/out file) +# oldext [in]: Extension of the unpreprocessed files. +# newext [in]: Extension of the preprocessed files. +# oldfiles [in]: List of unpreprocessed file names. +# newfiles [out]: List of preprocessed file names. +# +function(scalapackfx_preprocess preproc preprocopts oldext newext oldfiles newfiles) + + set(_newfiles) + foreach(oldfile IN LISTS oldfiles) + # Start with an absolulte path, so that the correct relative path is calculated thereafter + get_filename_component(oldfile ${oldfile} ABSOLUTE ${CMAKE_CURRENT_SOURCE_DIR}) + file(RELATIVE_PATH oldfile ${CMAKE_CURRENT_SOURCE_DIR} ${oldfile}) + scalapackfx_replace_extension(${oldext} ${newext} ${oldfile} newfile) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${newfile} + COMMAND ${preproc} ${preprocopts} ${CMAKE_CURRENT_SOURCE_DIR}/${oldfile} ${CMAKE_CURRENT_BINARY_DIR}/${newfile} + MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${oldfile}) + list(APPEND _newfiles ${CMAKE_CURRENT_BINARY_DIR}/${newfile}) + endforeach() + set(${newfiles} ${_newfiles} PARENT_SCOPE) + +endfunction() + + +# Build -D command line arguments for Fypp preprocessor based on current configuration +# +# Args: +# fyppflags [inout]: Current Fypp flags on enter, with -D options extended flags on exit. +# +function (scalapackfx_add_fypp_defines fyppflags) + + set(_fyppflags "${${fyppflags}}") + set(${fyppflags} ${_fyppflags} PARENT_SCOPE) + +endfunction() diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt new file mode 100644 index 0000000..4e6ecdb --- /dev/null +++ b/example/CMakeLists.txt @@ -0,0 +1,12 @@ +# Folder for generated mod-files +set(moduledir "${CMAKE_CURRENT_BINARY_DIR}/modules") + +add_executable(transpose) + +target_sources(transpose PRIVATE transpose.f90 common_module.f90) +set_target_properties(transpose + PROPERTIES Fortran_MODULE_DIRECTORY "${moduledir}" +) +target_link_libraries(transpose PRIVATE scalapackfx) +target_link_libraries(transpose PRIVATE MPI::MPI_Fortran) +target_link_libraries(transpose PRIVATE Scalapack::Scalapack) diff --git a/example/common_module.f90 b/example/common_module.f90 new file mode 100644 index 0000000..64c9710 --- /dev/null +++ b/example/common_module.f90 @@ -0,0 +1,103 @@ +module test_common_module + use blacsfx_module + use scalapackfx_module + use scalapackfx_tools_module + implicit none + private + + public :: dp + public :: readfromfile, writetofile + + integer, parameter :: sp = kind(1.0) + integer, parameter :: dp = kind(1.0d0) + + interface writetofile + module procedure writetofile_real + module procedure writetofile_cmplx + end interface writetofile + +contains + + + !> Read distributed matrix from file. + !! \param mygrid BLACS descriptor + !! \param fname Name of the file to read the matrix from. + !! \param mb Row block size of the matrix. + !! \param nb Column block size of the matrix. + !! \param mtxloc Allocated local matrix on exit. + !! \param desc Matrix descriptor + subroutine readfromfile(mygrid, fname, mb, nb, mtxloc, desc) + type(blacsgrid), intent(inout) :: mygrid + character(*), intent(in) :: fname + integer, intent(in) :: mb, nb + real(dp), allocatable, intent(out) :: mtxloc(:,:) + integer, intent(out) :: desc(DLEN_) + + integer :: mm, nn + + if (mygrid%lead) then + open(12, file=fname, action="read", status="old") + read(12, *) mm, nn + call blacsfx_gebs(mygrid, mm) + call blacsfx_gebs(mygrid, nn) + else + call blacsfx_gebr(mygrid, mm) + call blacsfx_gebr(mygrid, nn) + end if + call scalafx_creatematrix(mygrid, mm, nn, mb, nb, mtxloc, desc) + if (mygrid%lead) then + call readarray_lead(mygrid, 12, desc, mtxloc, formatted=.true.) + close(12) + else + call readarray_follow(mygrid, desc, mtxloc) + end if + + end subroutine readfromfile + + + !> Write distributed matrix to file. + !! \param mygrid BlACS descriptor + !! \param fname Name of the file to write the matrix to. + !! \param mtxloc Local part of the matrix. + !! \param desc Matrix descriptor + subroutine writetofile_real(mygrid, fname, mtxloc, desc) + type(blacsgrid), intent(in) :: mygrid + character(*), intent(in) :: fname + real(dp), intent(in) :: mtxloc(:,:) + integer, intent(in) :: desc(DLEN_) + + if (mygrid%lead) then + open(12, file=fname, form="formatted", status="replace") + write(12, "(I0,1X,I0)") desc(M_), desc(N_) + call writearray_lead(mygrid, 12, desc, mtxloc, elemformat="(ES23.15)") + close(12) + else + call writearray_follow(mygrid, desc, mtxloc) + end if + + end subroutine writetofile_real + + !> Write distributed matrix to file. + !! \param mygrid BlACS descriptor + !! \param fname Name of the file to write the matrix to. + !! \param mtxloc Local part of the matrix. + !! \param desc Matrix descriptor + subroutine writetofile_cmplx(mygrid, fname, mtxloc, desc) + type(blacsgrid), intent(in) :: mygrid + character(*), intent(in) :: fname + complex(dp), intent(in) :: mtxloc(:,:) + integer, intent(in) :: desc(DLEN_) + + if (mygrid%lead) then + open(12, file=fname, form="formatted", status="replace") + write(12, "(I0,1X,I0)") desc(M_), desc(N_) + call writearray_lead(mygrid, 12, desc, mtxloc, elemformat="(ES23.15)") + close(12) + else + call writearray_follow(mygrid, desc, mtxloc) + end if + + end subroutine writetofile_cmplx + + +end module test_common_module diff --git a/example/scalapackfx_example.f90 b/example/scalapackfx_example.f90 new file mode 100644 index 0000000..3a8bf64 --- /dev/null +++ b/example/scalapackfx_example.f90 @@ -0,0 +1,28 @@ +!> Example +!! +!! Program demonstrating capabilities of the scalapackfx library. +!! By default built only, if the project is the top-level project, and never installed. +!! +program scalapackfx_example + use mpi_f08, only : MPI_Comm, MPI_Comm_rank, MPI_COMM_WORLD, MPI_Finalize, MPI_Init + use scalapackfx, only: broadcast + implicit none + + type(MPI_Comm) :: comm + integer :: rank + integer :: buffer + + call MPI_Init() + comm = MPI_COMM_WORLD + call MPI_Comm_rank(comm, rank) + if (rank == 0) then + buffer = 1 + else + buffer = -1 + end if + call broadcast(comm, buffer, 0) + print "(a, i2.2, a, i0)", 'Rank: ', rank, '| buffer = ', buffer + call MPI_Finalize() + +end program scalapackfx_example + diff --git a/example/transpose.f90 b/example/transpose.f90 new file mode 100644 index 0000000..8700e99 --- /dev/null +++ b/example/transpose.f90 @@ -0,0 +1,90 @@ +!> Testing transposition +program test_ptran + use, intrinsic :: iso_fortran_env, stdout => output_unit + use test_common_module + use libscalapackfx_module + implicit none + + + ! Block size (using an extremely small value for test purposes) + integer, parameter :: bsize = 2 + + call main() + +contains + + subroutine main() + type(blacsgrid) :: mygrid + real(dp), allocatable :: xx(:,:), res(:,:) + complex(dp), allocatable :: xxc(:,:), resc(:,:) + integer :: descx(DLEN_), descres(DLEN_) + integer :: nprow, npcol, mm, nn, iproc, nproc + + ! Initialize blas and create a square processor grid + call blacsfx_pinfo(iproc, nproc) + do nprow = int(sqrt(real(nproc, dp))), nproc + if (mod(nproc, nprow) == 0) then + exit + end if + end do + npcol = nproc / nprow + call mygrid%initgrid(nprow, npcol) + if (mygrid%lead) then + write(stdout, "(A,2(1X,I0))") "# processor grid:", nprow, npcol + write(stdout, "(A,1X,I0)") "# block size:", bsize + end if + + ! Set up matrix + if (mygrid%lead) then + write(stdout, "(A)") "Matrix read from file 'hamsqr1.dat'." + end if + call readfromfile(mygrid, "hamsqr1.dat", bsize, bsize, xx, descx) + mm = descx(M_) + nn = descx(N_) + if (mygrid%lead) then + write(stdout, "(A,2(1X,I0))") "# global matrix size:", mm, nn + write(stdout, "(A,2(1X,I0))") "# local matrix size on leader:",& + & size(xx, dim=1), size(xx, dim=2) + end if + + call scalafx_creatematrix(mygrid, nn, mm, bsize, bsize, res, descres) + res = 0.0_dp + + call pblasfx_ptran(xx,descx,res,descres) + + ! Write results to disc. + call writetofile(mygrid, "ptran_realresult.dat", res, descres) + if (mygrid%lead) then + write(stdout, "(A)") "Result written to file 'ptran_realresult.dat'." + end if + + allocate(xxc(size(xx,dim=1),size(xx,dim=2))) + allocate(resc(size(res,dim=1),size(res,dim=2))) + + xxc = cmplx(0,1,dp) * xx + xx + + call pblasfx_ptranu(xxc,descx,resc,descres) + + ! Write results to disc. + call writetofile(mygrid, "ptran_cmplxresult.dat", resc, descres) + if (mygrid%lead) then + write(stdout, "(A)") "Result written to file 'ptran_cmplxresult.dat'." + end if + + call pblasfx_ptranc(xxc,descx,resc,descres) + + ! Write results to disc. + call writetofile(mygrid, "ptran_cmplxHresult.dat", resc, descres) + if (mygrid%lead) then + write(stdout, "(A)") "Result written to file 'ptran_cmplxHresult.dat'." + end if + + deallocate(resc) + deallocate(xxc) + + ! Finish blacs. + call blacsfx_exit() + + end subroutine main + +end program test_ptran diff --git a/lib/scalapackfx.fypp b/include/scalapackfx.fypp similarity index 100% rename from lib/scalapackfx.fypp rename to include/scalapackfx.fypp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..0a89acc --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,68 @@ +# Folder for generated mod-files +set(moduledir "${CMAKE_CURRENT_BINARY_DIR}/modules") + +add_library(scalapackfx) +add_library(scalapackfx::scalapackfx ALIAS scalapackfx) + +set(curdir ${CMAKE_CURRENT_SOURCE_DIR}) + +set(sources-fpp + ${curdir}/blacs.fpp + ${curdir}/blacsfx.fpp + ${curdir}/blacsgrid.fpp + ${curdir}/libscalapackfx.fpp + ${curdir}/linecomm.fpp + ${curdir}/pblas.fpp + ${curdir}/pblasfx.fpp + ${curdir}/scalapack.fpp + ${curdir}/scalapackfx.fpp + ${curdir}/scalapackfx_common.fpp + ${curdir}/scalapackfx_tools.fpp +) + +set(ALL-SOURCES-FPP ${ALL-SOURCES-FPP} ${sources-fpp}) + +scalapackfx_preprocess("${FYPP}" "${fypp_flags}" "fpp" "f90" "${ALL-SOURCES-FPP}" all-sources-f90-preproc) + +target_sources( + scalapackfx + PRIVATE + ${all-sources-f90-preproc} +) + +target_include_directories( + scalapackfx PUBLIC + $ + $ +) + +target_link_libraries(scalapackfx PRIVATE MPI::MPI_Fortran) + +set_target_properties( + scalapackfx + PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} + EXPORT_NAME scalapackfx + OUTPUT_NAME scalapackfx + Fortran_MODULE_DIRECTORY "${moduledir}" +) + +if (SCALAPACKFX_INSTALL) + install( + TARGETS scalapackfx + EXPORT scalapackfxTargets + LIBRARY + DESTINATION "${CMAKE_INSTALL_LIBDIR}" + COMPONENT scalapackfx_runtime + NAMELINK_COMPONENT scalapackfx_development + PUBLIC_HEADER + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" + COMPONENT scalapackfx_development + ) + install( + DIRECTORY "${moduledir}/" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SCALAPACKFX_INSTALL_MODULEDIR}" + COMPONENT scalapackfx_development + ) +endif () diff --git a/lib/blacs.fpp b/src/blacs.fpp similarity index 100% rename from lib/blacs.fpp rename to src/blacs.fpp diff --git a/lib/blacsfx.fpp b/src/blacsfx.fpp similarity index 100% rename from lib/blacsfx.fpp rename to src/blacsfx.fpp diff --git a/lib/blacsgrid.fpp b/src/blacsgrid.fpp similarity index 100% rename from lib/blacsgrid.fpp rename to src/blacsgrid.fpp diff --git a/lib/libscalapackfx.fpp b/src/libscalapackfx.fpp similarity index 100% rename from lib/libscalapackfx.fpp rename to src/libscalapackfx.fpp diff --git a/lib/linecomm.fpp b/src/linecomm.fpp similarity index 100% rename from lib/linecomm.fpp rename to src/linecomm.fpp diff --git a/lib/module.fpp b/src/module.fpp similarity index 100% rename from lib/module.fpp rename to src/module.fpp diff --git a/lib/pblas.fpp b/src/pblas.fpp similarity index 100% rename from lib/pblas.fpp rename to src/pblas.fpp diff --git a/lib/pblasfx.fpp b/src/pblasfx.fpp similarity index 100% rename from lib/pblasfx.fpp rename to src/pblasfx.fpp diff --git a/lib/scalapack.fpp b/src/scalapack.fpp similarity index 100% rename from lib/scalapack.fpp rename to src/scalapack.fpp diff --git a/lib/scalapackfx.fpp b/src/scalapackfx.fpp similarity index 100% rename from lib/scalapackfx.fpp rename to src/scalapackfx.fpp diff --git a/lib/scalapackfx_common.fpp b/src/scalapackfx_common.fpp similarity index 100% rename from lib/scalapackfx_common.fpp rename to src/scalapackfx_common.fpp diff --git a/lib/scalapackfx_tools.fpp b/src/scalapackfx_tools.fpp similarity index 100% rename from lib/scalapackfx_tools.fpp rename to src/scalapackfx_tools.fpp diff --git a/subprojects/Fortuno.cmake b/subprojects/Fortuno.cmake new file mode 100644 index 0000000..1877a2c --- /dev/null +++ b/subprojects/Fortuno.cmake @@ -0,0 +1,26 @@ +# Variables influencing how subproject is obtained +set(CMAKE_REQUIRE_FIND_PACKAGE_Fortuno ${SCALAPACKFX_SUBPROJECT_REQUIRE_FIND}) +set(CMAKE_DISABLE_FIND_PACKAGE_Fortuno ${SCALAPACKFX_SUBPROJECT_DISABLE_FIND}) +# set FETCHCONTENT_SOURCE_DIR_FORTUNO to use a local source of the subproject + +# Subproject related variables +option( + FORTUNO_BUILD_SHARED_LIBS "Fortuno: Build as shared library" ${SCALAPACKFX_BUILD_SHARED_LIBS} +) + +option(FORTUNO_WITH_MPI "Fortuno: whether to build the MPI interface" ON) + +# Make subproject available +FetchContent_Declare( + Fortuno + GIT_REPOSITORY "https://github.com/fortuno-repos/fortuno.git" + GIT_TAG "main" + FIND_PACKAGE_ARGS +) +FetchContent_MakeAvailable(Fortuno) + +if (Fortuno_FOUND) + message(STATUS "Subproject Fortuno: using installed version") +else () + message(STATUS "Subproject Fortuno: building from source in ${fortuno_SOURCE_DIR}") +endif () diff --git a/subprojects/fypp.cmake b/subprojects/fypp.cmake new file mode 100644 index 0000000..6d3ac4b --- /dev/null +++ b/subprojects/fypp.cmake @@ -0,0 +1,20 @@ +# Variables influencing how subproject is obtained +set(CMAKE_REQUIRE_FIND_PACKAGE_Fypp ${DNAOAD_SUBPROJECT_REQUIRE_FIND}) +set(CMAKE_DISABLE_FIND_PACKAGE_Fypp ${DNAOAD_SUBPROJECT_DISABLE_FIND}) +# set FETCHCONTENT_SOURCE_DIR_Fypp to use a local copy of the subproject + +# Make subproject available +FetchContent_Declare( + Fypp + GIT_REPOSITORY "https://github.com/aradi/fypp.git" + GIT_TAG "main" +) +FetchContent_MakeAvailable(Fypp) + +if (Fypp_FOUND) + message(STATUS "Subproject Fypp: using installed version") + set(FYPP "fypp") +else () + message(STATUS "Subproject Fypp: using local copy in ${fypp_BINARY_DIR}") + set(FYPP "${fypp_SOURCE_DIR}/bin/fypp") +endif () diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4a37c61..0c87b2b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,29 +1,33 @@ -set(targets - test_desc - test_subgrids) - -set(common-dep-targets - test_cpg2l - test_det - test_diag - test_gemr2d - test_linecomm - test_matinv - test_pposv - test_psyr_pher - test_remoteelements - test_svd - test_tran) +# Folder for generated mod-files +set(moduledir "${CMAKE_CURRENT_BINARY_DIR}/modules") -add_library(common OBJECT test_common.f90) -target_link_libraries(common PRIVATE ScalapackFx) +list(APPEND tests + test_desc +# test_cpg2l +# test_det +# test_diag +# test_gemr2d +# test_linecomm +# test_matinv +# test_pposv +# test_psyr_pher +# test_remoteelements +# test_svd +# test_tran +) -foreach(target IN LISTS targets) - add_executable(${target} ${target}.f90) - target_link_libraries(${target} ScalapackFx) -endforeach() +foreach(test IN LISTS tests) + add_executable(${test}) + target_sources(${test} PRIVATE ${test}.f90) + set_target_properties( + ${test} PROPERTIES Fortran_MODULE_DIRECTORY "${moduledir}" + ) + target_link_libraries(${test} PRIVATE scalapackfx) + target_link_libraries(${test} PRIVATE Fortuno::fortuno_mpi MPI::MPI_Fortran) + target_link_libraries(${test} PRIVATE Scalapack::Scalapack) -foreach(target IN LISTS common-dep-targets) - add_executable(${target} ${target}.f90 $) - target_link_libraries(${target} ScalapackFx) + add_test( + NAME ${test} + COMMAND ${test} + ) endforeach() diff --git a/test/test_desc.f90 b/test/test_desc.f90 index aa546b5..ee8aabe 100644 --- a/test/test_desc.f90 +++ b/test/test_desc.f90 @@ -1,28 +1,69 @@ -program test_descriptors +!> Test app driving Fortuno unit tests. +module test_scalapackfx use libscalapackfx_module + use fortuno_mpi, only : global_comm, is_equal, test => mpi_case_item, check => mpi_check,& + & test_list, this_rank implicit none - type(blacsgrid) :: mygrid - type(blocklist) :: blist - integer :: desc(DLEN_) - integer :: ii, iglob, iloc, nelem - integer :: iproc, nproc +contains + + function tests() + type(test_list) :: tests + + tests = test_list([& + test("desc", test_desc)& + &]) + + end function tests + + + subroutine test_desc() + type(blacsgrid) :: myGrid + type(blocklist) :: block_List + integer :: desc(DLEN_) + integer :: ii, iGlob, iLoc, nElem + integer :: iProc, nProc, nGrid + + call blacsfx_pinfo(iProc, nProc) + + nGrid = floor(sqrt(real(nProc))) + + call myGrid%initgrid(2, 2) + + call check(.false.) + + if (myGrid%mycol == -1) then + + print *, "IDLE Node:", iProc, " Exiting..." + + else + + call scalafx_getdescriptor(myGrid, 5, 5, 2, 2, desc) + call block_List%init(myGrid, desc, "c") + do ii = 1, size(block_List) + call block_List%getblock(ii, iGlob, iLoc, nElem) + print "(6(A,I3,2X))", "PROW:", myGrid%myrow, "PCOL:", myGrid%mycol,& + & "II:", ii, "GLOB:", iGlob, "LOC:", iLoc, "NEL:", nElem + end do + + end if + + ! THEN each rank must contain source rank's value + !call check(is_equal(buffer, sourceval)) + call check(.false.) - call blacsfx_pinfo(iproc, nproc) - call mygrid%initgrid(2, 2) - if (mygrid%mycol == -1) then - print *, "IDLE Node:", iproc, " Exiting..." call blacsfx_exit() - stop - end if - call scalafx_getdescriptor(mygrid, 5, 5, 2, 2, desc) - call blist%init(mygrid, desc, "c") - do ii = 1, size(blist) - call blist%getblock(ii, iglob, iloc, nelem) - print "(6(A,I3,2X))", "PROW:", mygrid%myrow, "PCOL:", mygrid%mycol,& - & "II:", ii, "GLOB:", iglob, "LOC:", iloc, "NEL:", nelem - end do - call blacsfx_exit() - - -end program test_descriptors + + end subroutine test_desc + +end module test_scalapackfx + + +program testapp + use fortuno_mpi, only : execute_mpi_cmd_app + use test_scalapackfx, only : tests + implicit none + + call execute_mpi_cmd_app(tests()) + +end program testapp From 6d85438f4a76670955d1f38c7bcd762694c39ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A1lint=20Aradi?= Date: Fri, 11 Jul 2025 14:04:57 +0200 Subject: [PATCH 2/8] Fix MPI testing issues * Execute tests using mpiexec * Fix double MPI-finalization * Add helper routines for convenient BLACS testing --- CMakeLists.txt | 28 +++++++ test/CMakeLists.txt | 13 +++- test/blacstestutils.f90 | 157 ++++++++++++++++++++++++++++++++++++++++ test/test_desc.f90 | 49 ++++++------- 4 files changed, 217 insertions(+), 30 deletions(-) create mode 100644 test/blacstestutils.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index fdf3753..031f8fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,27 @@ option( OFF ) +set( + SCALAPACKFX_TEST_OMP_THREADS + "1" + CACHE STRING + "Nr. of OpenMP-threads used for testing" +) + +set(SCALAPACKFX_TEST_MPI_PROCS + "1" + CACHE STRING + "Nr. of MPI processes used for testing" +) + +set( + SCALAPACKFX_MPIEXEC_POSTFLAGS + "" + CACHE STRING + "Additional flags for the mpiexec command (to be added after the mpiexec command name)" +) + + #[=============================================================================[ # Project configuration # ]=============================================================================] @@ -74,6 +95,13 @@ scalapackfx_setup_build_type("RelWithDebInfo") find_package(MPI REQUIRED) +set( + MPI_TEST_RUNNER + ${MPIEXEC_PREFLAGS} ${MPIEXEC_EXECUTABLE} ${MPIEXEC_NUMPROC_FLAG} ${SCALAPACKFX_TEST_MPI_PROCS} + ${MPIEXEC_POSTFLAGS} ${SCALAPACKFX_MPIEXEC_POSTFLAGS} +) + + # # Prerequisites # diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0c87b2b..9d8cba1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,6 +1,10 @@ # Folder for generated mod-files set(moduledir "${CMAKE_CURRENT_BINARY_DIR}/modules") +add_library(blacstestutils blacstestutils.f90) +target_link_libraries(blacstestutils PRIVATE scalapackfx) +target_link_libraries(blacstestutils PRIVATE Fortuno::fortuno_mpi) + list(APPEND tests test_desc # test_cpg2l @@ -16,18 +20,23 @@ list(APPEND tests # test_tran ) + foreach(test IN LISTS tests) add_executable(${test}) target_sources(${test} PRIVATE ${test}.f90) set_target_properties( ${test} PROPERTIES Fortran_MODULE_DIRECTORY "${moduledir}" ) + target_link_libraries(${test} PRIVATE blacstestutils) target_link_libraries(${test} PRIVATE scalapackfx) target_link_libraries(${test} PRIVATE Fortuno::fortuno_mpi MPI::MPI_Fortran) target_link_libraries(${test} PRIVATE Scalapack::Scalapack) - add_test( NAME ${test} - COMMAND ${test} + COMMAND ${MPI_TEST_RUNNER} ./${test} + ) + set_tests_properties( + ${test} + PROPERTIES ENVIRONMENT OMP_NUM_THREADS=${SCALAPACKFX_TEST_OMP_THREADS} ) endforeach() diff --git a/test/blacstestutils.f90 b/test/blacstestutils.f90 new file mode 100644 index 0000000..cdd6bb8 --- /dev/null +++ b/test/blacstestutils.f90 @@ -0,0 +1,157 @@ +module blacstestutils + use libscalapackfx_module, only : blacsgrid, blacsfx_exit, blacsfx_pinfo + use fortuno_mpi, only : test_item, mpi_case, check => mpi_check, failed => mpi_failed, num_ranks + implicit none + + private + public :: this_proc, num_procs + public :: blacs_test + public :: blacs_grid_env, get_grid_or_fail + + + !> Implements a test class with BLACS initialization and destruction + type, extends(mpi_case) :: blacs_case + contains + procedure :: run => blacs_case_run + end type blacs_case + + + abstract interface + !> Interface of the test procedure + subroutine blacs_test_procedure() + end subroutine blacs_test_procedure + end interface + + + !> Implements a BLACS grid wrapper enforcing grid finalization + type, extends(blacsgrid) :: blacs_grid_env + + !> Whether the grid contains all BLACS processes + logical :: has_all_procs = .false. + contains + final :: final_blacs_grid_env + end type blacs_grid_env + + + ! Number of processes available in the BLACS framework + integer :: num_procs_ = -1 + + ! The id of the current process in the BLACS framework + integer :: this_proc_ = -1 + +contains + + + !> Returns the id of the current process in the BLACS framework + function this_proc() + integer :: this_proc + this_proc = this_proc_ + end function this_proc + + + !> Returns the number for processes available in the BLACS framework + function num_procs() + integer :: num_procs + num_procs = num_procs_ + end function num_procs + + + !> Wraps a blacs_case instance as test_item suitable for array constructors. + function blacs_test(name, proc) result(testitem) + character(*), intent(in) :: name + procedure(blacs_test_procedure) :: proc + + type(test_item) :: testitem + + testitem = test_item(blacs_case(name=name, proc=proc)) + + end function blacs_test + + + !> Run procedure of the tempfile_case type. + subroutine blacs_case_run(this) + class(blacs_case), intent(in) :: this + + call blacsfx_pinfo(this_proc_, num_procs_) + call check(num_procs_ == num_ranks(),& + & "Number of BLACS processes differ from number of MPI ranks") + if (failed()) return + call this%proc() + call blacsfx_exit(keepmpi=.true.) + this_proc_ = -1 + num_procs_ = -1 + + end subroutine blacs_case_run + + + !> Returns a grid environment or sets the calling test to failed if not possible + !! + !! Note: This routine must be called from within fortuno MPI test procedures. + !! + subroutine get_grid_or_fail(this, nrow, ncol, includeall) + + !> Instance + type(blacs_grid_env), intent(out) :: this + + !> Number of process rows + integer, optional, intent(in) :: nrow + + !> Number of process columns + integer, optional, intent(in) :: ncol + + !> Whether it should be ensured that all processes are included in the grid (default: .true.) + logical, optional, intent(in) :: includeall + + integer :: nprocs + integer :: nrow_, ncol_ + logical :: includeall_, hasall + type(blacsgrid) :: grid + + includeall_ = .true. + if (present(includeall)) includeall_ = includeall + + nprocs = num_procs() + if (present(nrow) .and. present(ncol)) then + nrow_ = nrow + ncol_ = ncol + else if (present(nrow)) then + nrow_ = nrow + ncol_ = nprocs / nrow_ + else if (present(ncol)) then + ncol_ = ncol + nrow_ = nprocs / ncol_ + else if (includeall_) then + do nrow_ = floor(sqrt(real(nprocs))), 1, -1 + ncol_ = nprocs / nrow_ + if (ncol_ * nrow_ == nprocs) exit + end do + else + nrow_ = floor(sqrt(real(nprocs))) + ncol_ = nprocs / nrow_ + end if + hasall = ncol_ * nrow_ == nprocs + + call check(nrow_ * ncol_ <= nprocs, msg="Required grid needs more processes than available") + if (failed()) return + call check(nrow_ > 0, msg="Could not set up grid with at least one process row") + if (failed()) return + call check(ncol_ > 0, msg="Could not set up grid with at least one process column") + if (failed()) return + call check(.not. includeall_ .or. hasall,& + & msg="Could not include all processes in the required grid") + if (failed()) return + + call this%blacsgrid%initgrid(nrow_, ncol_) + this%has_all_procs = hasall + + end subroutine get_grid_or_fail + + + subroutine final_blacs_grid_env(this) + type(blacs_grid_env), intent(inout) :: this + + call this%blacsgrid%destruct() + + end subroutine final_blacs_grid_env + +end module blacstestutils diff --git a/test/test_desc.f90 b/test/test_desc.f90 index ee8aabe..2aa20e9 100644 --- a/test/test_desc.f90 +++ b/test/test_desc.f90 @@ -1,12 +1,14 @@ !> Test app driving Fortuno unit tests. module test_scalapackfx use libscalapackfx_module - use fortuno_mpi, only : global_comm, is_equal, test => mpi_case_item, check => mpi_check,& - & test_list, this_rank + use fortuno_mpi, only : check => mpi_check, failed => mpi_failed, skip => mpi_skip, test_list + use blacstestutils, only : blacs_grid_env, get_grid_or_fail, test => blacs_test,& + & num_procs implicit none contains + function tests() type(test_list) :: tests @@ -18,42 +20,33 @@ end function tests subroutine test_desc() - type(blacsgrid) :: myGrid - type(blocklist) :: block_List + type(blacs_grid_env) :: env + type(blocklist) :: blist integer :: desc(DLEN_) - integer :: ii, iGlob, iLoc, nElem - integer :: iProc, nProc, nGrid - - call blacsfx_pinfo(iProc, nProc) - - nGrid = floor(sqrt(real(nProc))) - - call myGrid%initgrid(2, 2) - - call check(.false.) + integer :: ii, iglob, iloc, nelem - if (myGrid%mycol == -1) then + if (num_procs() /= 4) then + call skip() + return + end if - print *, "IDLE Node:", iProc, " Exiting..." + call get_grid_or_fail(env, nrow=2, ncol=2) + if (failed()) return + if (env%mycol == -1) then + print *, "IDLE Node:", env%iproc, " Exiting..." else - - call scalafx_getdescriptor(myGrid, 5, 5, 2, 2, desc) - call block_List%init(myGrid, desc, "c") - do ii = 1, size(block_List) - call block_List%getblock(ii, iGlob, iLoc, nElem) - print "(6(A,I3,2X))", "PROW:", myGrid%myrow, "PCOL:", myGrid%mycol,& - & "II:", ii, "GLOB:", iGlob, "LOC:", iLoc, "NEL:", nElem + call scalafx_getdescriptor(env%blacsgrid, 5, 5, 2, 2, desc) + call blist%init(env%blacsgrid, desc, "c") + do ii = 1, size(blist) + call blist%getblock(ii, iglob, iloc, nelem) + print "(6(A,I3,2X))", "PROW:", env%myrow, "PCOL:", env%mycol,& + & "II:", ii, "GLOB:", iglob, "LOC:", iloc, "NEL:", nelem end do - end if - ! THEN each rank must contain source rank's value - !call check(is_equal(buffer, sourceval)) call check(.false.) - call blacsfx_exit() - end subroutine test_desc end module test_scalapackfx From e5f030b792af89f3bd413918ccb7b73944228fdb Mon Sep 17 00:00:00 2001 From: Ben Hourahine Date: Sun, 10 Aug 2025 12:17:17 +0100 Subject: [PATCH 3/8] Test cases for BLACS grid initialisation --- src/scalapackfx.fpp | 3 +- test/blacstestutils.f90 | 9 +++- test/test_desc.f90 | 101 +++++++++++++++++++++++++++++++++------- 3 files changed, 93 insertions(+), 20 deletions(-) diff --git a/src/scalapackfx.fpp b/src/scalapackfx.fpp index 517617b..23ccb9c 100644 --- a/src/scalapackfx.fpp +++ b/src/scalapackfx.fpp @@ -2056,8 +2056,7 @@ contains !! (default: lead column). !! \param info Info flag. !! - subroutine scalafx_getdescriptor(mygrid, mm, nn, mb, nb, desc, rsrc, csrc, & - & info) + subroutine scalafx_getdescriptor(mygrid, mm, nn, mb, nb, desc, rsrc, csrc, info) type(blacsgrid), intent(in) :: mygrid integer, intent(in) :: mm, nn, mb, nb integer, intent(out) :: desc(DLEN_) diff --git a/test/blacstestutils.f90 b/test/blacstestutils.f90 index cdd6bb8..bfac39a 100644 --- a/test/blacstestutils.f90 +++ b/test/blacstestutils.f90 @@ -9,7 +9,7 @@ module blacstestutils public :: blacs_grid_env, get_grid_or_fail - !> Implements a test class with BLACS initialization and destruction + !> Implements a test class with BLACS initialization and then destruction type, extends(mpi_case) :: blacs_case contains procedure :: run => blacs_case_run @@ -57,6 +57,10 @@ end function num_procs !> Wraps a blacs_case instance as test_item suitable for array constructors. + !! + !! Note: This routine must be called from within fortuno MPI test procedures and can invoke test + !! failue internally + !! function blacs_test(name, proc) result(testitem) character(*), intent(in) :: name procedure(blacs_test_procedure) :: proc @@ -86,7 +90,8 @@ end subroutine blacs_case_run !> Returns a grid environment or sets the calling test to failed if not possible !! - !! Note: This routine must be called from within fortuno MPI test procedures. + !! Note: This routine must be called from within fortuno MPI test procedures and can invoke test + !! failue internally !! subroutine get_grid_or_fail(this, nrow, ncol, includeall) diff --git a/test/test_desc.f90 b/test/test_desc.f90 index 2aa20e9..bf86985 100644 --- a/test/test_desc.f90 +++ b/test/test_desc.f90 @@ -1,53 +1,122 @@ !> Test app driving Fortuno unit tests. module test_scalapackfx use libscalapackfx_module - use fortuno_mpi, only : check => mpi_check, failed => mpi_failed, skip => mpi_skip, test_list + use fortuno_mpi, only : fortuno_check => mpi_check, fortuno_failed => mpi_failed,& + & fortuno_skip => mpi_skip, test_list use blacstestutils, only : blacs_grid_env, get_grid_or_fail, test => blacs_test,& & num_procs implicit none contains - + !> Tests to run function tests() type(test_list) :: tests tests = test_list([& - test("desc", test_desc)& + test("desc_square", test_desc_square),& + test("desc_nonsquare", test_desc_nonsquare)& &]) end function tests - subroutine test_desc() + !> Test cases where all processors fit onto a square BLACS grid + subroutine test_desc_square() type(blacs_grid_env) :: env type(blocklist) :: blist integer :: desc(DLEN_) - integer :: ii, iglob, iloc, nelem + integer :: ii, iGlob, iLoc, nElem, sqrtProcs, info - if (num_procs() /= 4) then - call skip() + sqrtProcs = gridSide(num_procs()) + if (num_procs() /= sqrtProcs**2) then + call fortuno_skip() return end if - call get_grid_or_fail(env, nrow=2, ncol=2) - if (failed()) return + print *, "Testing square grid of processors" + + call get_grid_or_fail(env, nrow=sqrtProcs, ncol=sqrtProcs) + if (fortuno_failed()) return if (env%mycol == -1) then - print *, "IDLE Node:", env%iproc, " Exiting..." + print *, "IDLE Node:", env%iproc, " Should be a square grid, so should not get here..." + call fortuno_check(.false.) else - call scalafx_getdescriptor(env%blacsgrid, 5, 5, 2, 2, desc) + print *, "WORKING Node:", env%iproc + call scalafx_getdescriptor(env%blacsgrid, num_procs()+1, num_procs()+1, sqrtProcs, sqrtProcs,& + & desc, info=info) + if (info == 0) then + call fortuno_check(.true.) + else + print *, "Node:", env%iproc, " ScaLAPACK descriptor returned info=", info + call fortuno_check(.false.) + end if call blist%init(env%blacsgrid, desc, "c") do ii = 1, size(blist) - call blist%getblock(ii, iglob, iloc, nelem) - print "(6(A,I3,2X))", "PROW:", env%myrow, "PCOL:", env%mycol,& - & "II:", ii, "GLOB:", iglob, "LOC:", iloc, "NEL:", nelem + ! Loop over local blocks of the matrix of this proc. + call blist%getblock(ii, iGlob, iLoc, nElem) + print "(6(A,1X,I0,2X))", "PROW: ", env%myrow, "PCOL:", env%mycol,& + & "II:", ii, "GLOB:", iGlob, "LOC:", iLoc, "NEL:", nElem end do + end if - call check(.false.) + end subroutine test_desc_square + + + !> Test cases where not all processors neccessarily fit onto a square BLACS grid + subroutine test_desc_nonsquare() + type(blacs_grid_env) :: env + type(blocklist) :: blist + integer :: desc(DLEN_) + integer :: ii, iGlob, iLoc, nElem, sqrtProcs, info + + print *, "Testing non-square grid of processors" + + sqrtProcs = gridSide(num_procs()) + + call get_grid_or_fail(env, nrow=sqrtProcs, ncol=sqrtProcs, includeall=.false.) + if (fortuno_failed()) return + + if (env%mycol == -1) then + print *, "IDLE Node:", env%iproc, " Idle so exiting successfully..." + call fortuno_check(.true.) + else + print *, "WORKING Node:", env%iproc + call scalafx_getdescriptor(env%blacsgrid, num_procs()+1, num_procs()+1, sqrtProcs, sqrtProcs,& + & desc, info=info) + if (info == 0) then + call fortuno_check(.true.) + else + print *, "Node:", env%iproc, " ScaLAPACK descriptor returned info=", info + call fortuno_check(.false.) + end if + call blist%init(env%blacsgrid, desc, "c") + do ii = 1, size(blist) + ! Loop over local blocks of the matrix of this proc. + call blist%getblock(ii, iGlob, iLoc, nElem) + print "(6(A,1X,I0,2X))", "PROW: ", env%myrow, "PCOL:", env%mycol,& + & "II:", ii, "GLOB:", iGlob, "LOC:", iLoc, "NEL:", nElem + end do + + end if + + end subroutine test_desc_nonsquare + + + !> Calculate side of a square grid of nProc processors + function gridSide(nProc) + + !> Number of processors + integer, intent(in) :: nProc + + !> sqrt(nProc) + integer :: gridSide + + gridSide = floor(sqrt(real(nProc) + epsilon(0.0))) - end subroutine test_desc + end function gridSide end module test_scalapackfx From 3fd1da20b40d70dd4e0b2a7b37a946f0ba7b4572 Mon Sep 17 00:00:00 2001 From: Ben Hourahine Date: Sun, 10 Aug 2025 16:30:38 +0100 Subject: [PATCH 4/8] Workflow for github --- .github/workflows/build.yml | 121 ++++++++++++++++++++++++++++++++++++ test/test_desc.f90 | 10 +-- 2 files changed, 126 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..2f6edbe --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,121 @@ +name: CI +on: + push: + paths: + - '.github/workflows/**' + - 'cmake/**' + - 'example/**' + - 'include/**' + - 'src/**' + - 'subprojects/**' + - 'test/**' + - 'CMakeLists.txt' + - 'config.cmake' + pull_request: + paths: + - '.github/workflows/**' + - 'cmake/**' + - 'example/**' + - 'include/**' + - 'src/**' + - 'subprojects/**' + - 'test/**' + - 'CMakeLists.txt' + - 'config.cmake' + +env: + CI: "ON" + HOMEBREW_NO_ANALYTICS: "ON" + HOMEBREW_NO_AUTO_UPDATE: "ON" + HOMEBREW_NO_BOTTLE_SOURCE_FALLBACK: "ON" + HOMEBREW_NO_GITHUB_API: "ON" + HOMEBREW_NO_INSTALL_CLEANUP: "ON" + BUILD_DIR: _build + CMAKE_OPTIONS: >- + -DWITH_UNIT_TESTS=true + +jobs: + + gfortran-build: + + runs-on: ${{ matrix.os }} + + env: + FC: gfortran + + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + mpi: [openmpi] # mpich + config: [Debug] + version: [13] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get the number of available processes + run: | + NPROC=$(nproc) + echo "NPROC=$NPROC" >> $GITHUB_ENV + + - name: Install gfortran (Linux) + run: | + sudo add-apt-repository ppa:ubuntu-toolchain-r/test + sudo apt-get update + sudo apt-get install -y gfortran-${{ matrix.version }} + sudo update-alternatives \ + --install /usr/bin/gfortran gfortran /usr/bin/gfortran-${{ matrix.version }} 100 + + - name: Enable MPI build + if: contains(matrix.mpi, 'openmpi') || contains(matrix.mpi, 'mpich') + run: echo "WITH_MPI=true" >> $GITHUB_ENV + + - name: Set Compiler + run: | + echo "FC=${FC}" >> ${GITHUB_ENV} + + - name: Install LAPACK/BLAS + run: | + sudo apt-get install liblapack-dev libblas-dev + + - name: Install OpenMPI + if: contains(matrix.mpi, 'openmpi') + run: | + sudo apt-get update + sudo apt-get install libopenmpi-dev libscalapack-openmpi-dev + echo "CMAKE_OPTIONS=${CMAKE_OPTIONS} -DSCALAPACK_LIBRARY='scalapack-openmpi'" >> $GITHUB_ENV + echo "CMAKE_DEP_OPTIONS=-DSCALAPACK_LIBRARY='scalapack-openmpi'" >> $GITHUB_ENV + +# - name: Install MPICH +# if: contains(matrix.mpi, 'mpich') +# run: | +# sudo apt-get update +# sudo apt-get install mpich libscalapack-mpich-dev +# echo "CMAKE_OPTIONS=${CMAKE_OPTIONS}" >> $GITHUB_ENV +# echo "CMAKE_DEP_OPTIONS=-DSCALAPACK_LIBRARY='scalapack-mpich'" >> $GITHUB_ENV + + - name: Set extra CMake flags + run: | + echo "CMAKE_OPTIONS=${CMAKE_OPTIONS} -DENABLE_DYNAMIC_LOADING=true" >> $GITHUB_ENV + if [[ "${{ matrix.mpi }}" == "openmpi" || "${{ matrix.mpi }}" == "mpich" ]]; then + echo "CMAKE_OPTIONS=${CMAKE_OPTIONS} -DTEST_MPI_PROCS=${NPROC}" >> $GITHUB_ENV + fi + + - name: Configure build + run: >- + cmake -B ${BUILD_DIR} -G Ninja + -DCMAKE_INSTALL_PREFIX=${PWD}/${INSTALL_DIR} + -DCMAKE_BUILD_TYPE=${{ matrix.config }} + ${CMAKE_OPTIONS} + -DWITH_MPI=${WITH_MPI} + + - name: Build project + run: cmake --build ${BUILD_DIR} -j + + - name: Run regression tests + run: | + echo "SCALAPACKFX_TEST_OMP_THREADS=1" >> $GITHUB_ENV + echo "TEST_UUID=$(uuidgen)" >> ${GITHUB_ENV} + ctest -j ${SCALAPACKFX_TEST_OMP_THREADS} --output-on-failure --test-dir ${BUILD_DIR} diff --git a/test/test_desc.f90 b/test/test_desc.f90 index bf86985..913cc52 100644 --- a/test/test_desc.f90 +++ b/test/test_desc.f90 @@ -56,8 +56,8 @@ subroutine test_desc_square() do ii = 1, size(blist) ! Loop over local blocks of the matrix of this proc. call blist%getblock(ii, iGlob, iLoc, nElem) - print "(6(A,1X,I0,2X))", "PROW: ", env%myrow, "PCOL:", env%mycol,& - & "II:", ii, "GLOB:", iGlob, "LOC:", iLoc, "NEL:", nElem + print "(6(A,1X,I0,2X))", "PROW: ", env%myrow, "PCOL:", env%mycol, "II:", ii,& + & "GLOB:", iGlob, "LOC:", iLoc, "NEL:", nElem end do end if @@ -96,8 +96,8 @@ subroutine test_desc_nonsquare() do ii = 1, size(blist) ! Loop over local blocks of the matrix of this proc. call blist%getblock(ii, iGlob, iLoc, nElem) - print "(6(A,1X,I0,2X))", "PROW: ", env%myrow, "PCOL:", env%mycol,& - & "II:", ii, "GLOB:", iGlob, "LOC:", iLoc, "NEL:", nElem + print "(6(A,1X,I0,2X))", "PROW: ", env%myrow, "PCOL:", env%mycol, "II:", ii,& + & "GLOB:", iGlob, "LOC:", iLoc, "NEL:", nElem end do end if @@ -105,7 +105,7 @@ subroutine test_desc_nonsquare() end subroutine test_desc_nonsquare - !> Calculate side of a square grid of nProc processors + !> Calculate side of a square grid containing nProc processors function gridSide(nProc) !> Number of processors From 056905342391677ee14bdc281423553b25a0c3f5 Mon Sep 17 00:00:00 2001 From: Ben Hourahine Date: Sun, 10 Aug 2025 20:26:32 +0100 Subject: [PATCH 5/8] Test cmake change --- test/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9d8cba1..dc545a3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,9 +24,9 @@ list(APPEND tests foreach(test IN LISTS tests) add_executable(${test}) target_sources(${test} PRIVATE ${test}.f90) - set_target_properties( - ${test} PROPERTIES Fortran_MODULE_DIRECTORY "${moduledir}" - ) + #set_target_properties( + # ${test} PROPERTIES Fortran_MODULE_DIRECTORY "${moduledir}" + #) target_link_libraries(${test} PRIVATE blacstestutils) target_link_libraries(${test} PRIVATE scalapackfx) target_link_libraries(${test} PRIVATE Fortuno::fortuno_mpi MPI::MPI_Fortran) From 557dd1d39020f9aef448bfd606b536b1d95e2d57 Mon Sep 17 00:00:00 2001 From: Ben Hourahine Date: Sun, 10 Aug 2025 20:30:49 +0100 Subject: [PATCH 6/8] Remove commented lines --- test/CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dc545a3..7e017cf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -24,9 +24,6 @@ list(APPEND tests foreach(test IN LISTS tests) add_executable(${test}) target_sources(${test} PRIVATE ${test}.f90) - #set_target_properties( - # ${test} PROPERTIES Fortran_MODULE_DIRECTORY "${moduledir}" - #) target_link_libraries(${test} PRIVATE blacstestutils) target_link_libraries(${test} PRIVATE scalapackfx) target_link_libraries(${test} PRIVATE Fortuno::fortuno_mpi MPI::MPI_Fortran) From b22cab0fd15bb1986033a0f4ce009a293a923965 Mon Sep 17 00:00:00 2001 From: Ben Hourahine Date: Sun, 10 Aug 2025 22:34:07 +0100 Subject: [PATCH 7/8] Clarifying comment --- src/blacsfx.fpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/blacsfx.fpp b/src/blacsfx.fpp index f0701ef..58099e4 100644 --- a/src/blacsfx.fpp +++ b/src/blacsfx.fpp @@ -557,7 +557,8 @@ contains !> Holds up execution of all processes within given scope. !! !! \param self BLACS group descriptor - !! \param scope Scope of the barrier (default: "A") + !! \param scope Scope of the barrier, indicating row ('R'), column ('C'), or entire grid ('A') + !! (default: "A") !! subroutine blacsfx_barrier(mygrid, scope) type(blacsgrid), intent(in) :: mygrid From d344baefc803550269bbb3367373f1e0748a2df0 Mon Sep 17 00:00:00 2001 From: Ben Hourahine Date: Sun, 10 Aug 2025 22:34:35 +0100 Subject: [PATCH 8/8] Subgrid tester, not working yet --- test/CMakeLists.txt | 1 + test/test_desc.f90 | 2 +- test/test_subgrids.f90 | 130 ++++++++++++++++++++++++++++++----------- 3 files changed, 98 insertions(+), 35 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 7e017cf..1a38396 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -17,6 +17,7 @@ list(APPEND tests # test_psyr_pher # test_remoteelements # test_svd + test_subgrids # test_tran ) diff --git a/test/test_desc.f90 b/test/test_desc.f90 index 913cc52..297d802 100644 --- a/test/test_desc.f90 +++ b/test/test_desc.f90 @@ -1,4 +1,4 @@ -!> Test app driving Fortuno unit tests. +!> Test BLACS descriptor setup module test_scalapackfx use libscalapackfx_module use fortuno_mpi, only : fortuno_check => mpi_check, fortuno_failed => mpi_failed,& diff --git a/test/test_subgrids.f90 b/test/test_subgrids.f90 index 18d6477..a0bb595 100644 --- a/test/test_subgrids.f90 +++ b/test/test_subgrids.f90 @@ -1,40 +1,102 @@ -program test_subgrid +!> Test BLACS subgrids +module test_scalapackfx use libscalapackfx_module + use fortuno_mpi, only : fortuno_check => mpi_check, fortuno_failed => mpi_failed,& + & fortuno_skip => mpi_skip, test_list + use blacstestutils, only : blacs_grid_env, get_grid_or_fail, test => blacs_test,& + & num_procs implicit none - type(blacsgrid) :: grpproc, allproc, grpleaders - integer :: iproc, nproc - - call blacsfx_pinfo(iproc, nproc) - call allproc%initgrid(2, 4) - call grpproc%initsplitgrids(2, 2, 2, leadgrid=grpleaders) - print "(A,4(A,1X,I4,5X))", "ROW-MAJOR| ", "GLOBALID:", iproc, & - & "GROUPID:", grpproc%iproc, "ROW:", grpproc%myrow,& - & "COL:", grpproc%mycol - if (grpproc%iproc == -1) then - print *, "IDLE:", iproc - else - if (grpleaders%iproc /= -1) then - print *, "GRIDLEAD:", iproc, grpproc%iproc, grpleaders%iproc - call blacsfx_barrier(grpleaders) +contains + + !> Tests to run + function tests() + type(test_list) :: tests + + tests = test_list([& + test("desc_subgrids", test_subgrids)& + &]) + + end function tests + + + !> Test splitting up a BLACS grid + subroutine test_subgrids() + + type(blacs_grid_env) :: allProc, grpLeaders, grpProc + integer :: iProc, nProc, sqrtProcs + + sqrtProcs = gridSide(num_procs()) + + call get_grid_or_fail(allProc, nrow=sqrtProcs, ncol=sqrtProcs, includeall=.false.) + if (fortuno_failed()) return + + call blacsfx_pinfo(iProc, nProc) + + call fortuno_check(iProc == allProc%iProc) + call fortuno_check(nProc == allProc%nProc) + print *, "iProc=", iProc, " nProc=", nProc + + call grpproc%initsplitgrids(sqrtProcs, sqrtProcs, sqrtProcs, leadgrid=grpLeaders) + + print "(A,4(A,1X,I4,5X))", "ROW-MAJOR| ", "GLOBAL ID:", iProc,& + & "GROUP ID:", grpProc%iProc, "ROW:", grpProc%myrow,& + & "COL:", grpProc%mycol + + if (grpproc%iproc == -1) then + print *, "IDLE:", iproc else - print *, "NORMAL:", iproc, grpproc%iproc + if (grpLeaders%iproc /= -1) then + print *, "GRIDLEAD:", iproc, grpproc%iproc, grpLeaders%iproc + !call blacsfx_barrier(grpLeaders) + call grpLeaders%destruct() + else + print *, "NORMAL:", iproc, grpproc%iproc + end if + + !call blacsfx_barrier(grpproc) + call grpproc%destruct() + end if - call blacsfx_barrier(grpproc) - call grpproc%destruct() - if (grpleaders%iproc /= -1) then - call grpleaders%destruct() + + call grpproc%initsplitgrids(sqrtProcs, sqrtProcs, sqrtProcs, colmajor=.true.) + print "(A,4(A,1X,I4,5X))", "COL-MAJOR| ", "GLOBALID:", iproc, & + & "GROUPID:", grpproc%iproc, "ROW:", grpproc%myrow,& + & "COL:", grpproc%mycol + if (grpproc%iproc == -1) then + print *, "IDLE:", iproc + else + !call blacsfx_barrier(grpproc) end if - end if - call grpproc%initsplitgrids(2, 2, 2, colmajor=.true.) - print "(A,4(A,1X,I4,5X))", "COL-MAJOR| ", "GLOBALID:", iproc, & - & "GROUPID:", grpproc%iproc, "ROW:", grpproc%myrow,& - & "COL:", grpproc%mycol - if (grpproc%iproc == -1) then - print *, "IDLE:", iproc - else - call blacsfx_barrier(grpproc) - end if - call blacsfx_exit() - -end program test_subgrid + + ! call blacsfx_exit() + + call fortuno_check(.false.) + + end subroutine test_subgrids + + + !> Calculate side of a square grid containing nProc processors + function gridSide(nProc) + + !> Number of processors + integer, intent(in) :: nProc + + !> sqrt(nProc) + integer :: gridSide + + gridSide = floor(sqrt(real(nProc) + epsilon(0.0))) + + end function gridSide + +end module test_scalapackfx + + +program testapp + use fortuno_mpi, only : execute_mpi_cmd_app + use test_scalapackfx, only : tests + implicit none + + call execute_mpi_cmd_app(tests()) + +end program testapp