diff --git a/.github/workflows/tests_cmake_t8code_api.yml b/.github/workflows/tests_cmake_t8code_api.yml new file mode 100644 index 0000000000..d96b5d0b33 --- /dev/null +++ b/.github/workflows/tests_cmake_t8code_api.yml @@ -0,0 +1,111 @@ +name: CMake tests t8code api + + +# This file is part of t8code. +# t8code is a C library to manage a collection (a forest) of multiple +# connected adaptive space-trees of general element types in parallel. +# +# Copyright (C) 2024 the developers +# +# t8code is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# t8code is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with t8code; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +on: + workflow_call: + inputs: + MAKEFLAGS: + required: true + type: string + description: 'Make flags to use for compilation (like -j4)' + MPI: + required: true + type: string + description: 'Use MPI for compilation (ON/OFF)' + BUILD_TYPE: + required: true + type: string + description: 'Build type (Release/Debug)' + LESS_TESTS: + required: true + type: boolean + description: 'Enable less tests option for configuring' + +jobs: + t8code_cmake_tests: + timeout-minutes: 30 + runs-on: ubuntu-latest + container: dlramr/t8code-ubuntu:t8-dependencies + steps: +# +# Setup +# + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: SC_P4EST_MPI_${{ inputs.MPI }} + - name: untar artifact + run: tar -xf artifact.tar && rm artifact.tar + - name: Update packages + run: apt-get update && apt-get upgrade -y + # This seems to be necessary because of the docker container + - name: disable ownership checks + run: git config --global --add safe.directory '*' + - name: Get input vars + run: export MAKEFLAGS="${{ inputs.MAKEFLAGS }}" + && export MPI="${{ inputs.MPI }}" + && export BUILD_TYPE="${{ inputs.BUILD_TYPE }}" + && export SC_PATH=$PWD/sc/build/$BUILD_TYPE + && export P4EST_PATH=$PWD/p4est/build/$BUILD_TYPE + && echo MAKEFLAGS="$MAKEFLAGS" >> $GITHUB_ENV + && echo MPI="$MPI" >> $GITHUB_ENV + && echo BUILD_TYPE="$BUILD_TYPE" >> $GITHUB_ENV + && echo SC_PATH="$SC_PATH" >> $GITHUB_ENV + && echo P4EST_PATH="$P4EST_PATH" >> $GITHUB_ENV +# +# T8CODE +# +# + # build config vars + - name: less-test option + if: ${{ inputs.LESS_TESTS }} + run: export LESS_TEST_OPTION="-DT8CODE_ENABLE_LESS_TESTS=ON" + && echo LESS_TEST_OPTION="$LESS_TEST_OPTION" >> $GITHUB_ENV + - name: build config variables + run: export CONFIG_OPTIONS="${LESS_TEST_OPTION} -GNinja -DT8CODE_USE_SYSTEM_SC=ON -DT8CODE_USE_SYSTEM_P4EST=ON -DT8CODE_BUILD_PEDANTIC=ON -DT8CODE_ENABLE_MPI=$MPI -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSC_DIR=$SC_PATH/install/cmake -DP4EST_DIR=$P4EST_PATH/install/cmake" + && echo CONFIG_OPTIONS="$CONFIG_OPTIONS" >> $GITHUB_ENV + # cmake and test with fortran + - name: check fortran + run: echo "Checking fortran" + - name: echo cmake line + run: echo cmake ../ $CONFIG_OPTIONS -DT8CODE_BUILD_FORTRAN_INTERFACE=ON + - name: cmake MPI fortran debug + run: mkdir build_fortran && cd build_fortran && cmake ../ $CONFIG_OPTIONS -DT8CODE_ENABLE_FORTRAN=ON + - name: OnFailUploadLog + if: failure() + uses: actions/upload-artifact@v4 + with: + name: cmake_${{ inputs.BUILD_TYPE }}_MPI_${{ inputs.MPI }}_fortran.log + path: build_fortran/CMakeFiles/CMakeOutput.log + - name: make + run: cd build_fortran && ninja $MAKEFLAGS + - name: ninja install + run: cd build_fortran && ninja install $MAKEFLAGS + - name: ninja test + run: cd build_fortran && ninja test + - name: OnFailUploadLog + if: failure() + uses: actions/upload-artifact@v4 + with: + name: test-suite_${{ inputs.BUILD_TYPE }}_MPI_${{ inputs.MPI }}_fortran.log + path: build_fortran/Testing/Temporary/LastTest.log diff --git a/.github/workflows/tests_cmake_testsuite.yml b/.github/workflows/tests_cmake_testsuite.yml index f98c46db63..ac20231bc5 100644 --- a/.github/workflows/tests_cmake_testsuite.yml +++ b/.github/workflows/tests_cmake_testsuite.yml @@ -113,6 +113,24 @@ jobs: MPI: ${{ matrix.MPI }} BUILD_TYPE: ${{ matrix.BUILD_TYPE }} LESS_TESTS: ${{ github.event_name == 'pull_request' }} + + # Run t8code linkage tests with and without MPI and in serial and debug mode + t8code_api_tests: + if: (github.event_name == 'schedule' && github.repository == 'DLR-AMR/t8code') || (github.event_name != 'schedule') + uses: ./.github/workflows/tests_cmake_t8code_api.yml + strategy: + fail-fast: false + matrix: + MPI: [ON] # For now the fortran API only supports building with MPI + BUILD_TYPE: [Debug, Release] + include: + - MAKEFLAGS: -j4 + needs: preparation + with: + MAKEFLAGS: ${{ matrix.MAKEFLAGS }} + MPI: ${{ matrix.MPI }} + BUILD_TYPE: ${{ matrix.BUILD_TYPE }} + LESS_TESTS: ${{ github.event_name == 'pull_request' }} # Run t8code tests with shipped submodules. This test is only for the build system, so only one config is tested. t8code_w_shipped_submodules_tests: @@ -123,4 +141,3 @@ jobs: MPI: ON BUILD_TYPE: Debug LESS_TESTS: ${{ github.event_name == 'pull_request' }} - diff --git a/.gitignore b/.gitignore index 03984a386e..da972a4f2d 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,9 @@ config.status Doxyfile doxygen/ +CMakeCache.txt +CMakeFiles/ + src/stamp-h1 src/t8_config.h src/pre_config.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ee2e6f03f6..d04c785c09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ include(cmake/GitProjectVersion.cmake) project( T8CODE DESCRIPTION "Parallel algorithms and data structures for tree-based AMR with arbitrary element shapes." - LANGUAGES C CXX + LANGUAGES C CXX Fortran VERSION "${T8CODE_VERSION_MAJOR}.${T8CODE_VERSION_MINOR}.${T8CODE_VERSION_PATCH}" ) include( CTest ) @@ -15,6 +15,7 @@ option( T8CODE_BUILD_TESTS "Build t8code's automated tests" ON ) option( T8CODE_BUILD_TUTORIALS "Build t8code's tutorials" ON ) option( T8CODE_BUILD_EXAMPLES "Build t8code's examples" ON ) option( T8CODE_BUILD_BENCHMARKS "Build t8code's benchmarks" ON ) +option( T8CODE_BUILD_FORTRAN_INTERFACE "Build t8code's Fortran interface" OFF ) option( T8CODE_ENABLE_LESS_TESTS "Tests not as thoroughly to speed up the test suite. Tests the same functionality. (WARNING: Use with care.)" OFF ) option( T8CODE_ENABLE_MPI "Enable t8code's features which rely on MPI" ON ) @@ -56,7 +57,12 @@ set( CMAKE_CXX_EXTENSIONS OFF ) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) if( T8CODE_ENABLE_MPI ) - find_package( MPI COMPONENTS C REQUIRED ) + if( T8CODE_BUILD_FORTRAN_INTERFACE ) + find_package( MPI COMPONENTS C Fortran REQUIRED ) + else() + find_package( MPI COMPONENTS C REQUIRED ) + endif() + if( NOT MPIEXEC_EXECUTABLE ) message( FATAL_ERROR "MPIEXEC was not found" ) endif() @@ -137,4 +143,13 @@ if ( T8CODE_BUILD_DOCUMENTATION ) add_subdirectory( ${CMAKE_CURRENT_LIST_DIR}/doc ) endif() -include (cmake/CPackConfig.cmake) \ No newline at end of file +if( T8CODE_BUILD_FORTRAN_INTERFACE ) + enable_language( Fortran ) + add_subdirectory( ${CMAKE_CURRENT_LIST_DIR}/api/t8_fortran_interface ) + + if( NOT T8CODE_ENABLE_MPI ) + message( FATAL_ERROR "Fortran API only available when MPI is enabled." ) + endif() +endif() + +include (cmake/CPackConfig.cmake) diff --git a/Makefile.am b/Makefile.am index f0872791e7..e4c53198bb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -47,6 +47,7 @@ dist_t8aclocal_DATA = config/t8_include.m4 \ config/t8_netcdf.m4 \ config/t8_vtk.m4 \ config/t8_occ.m4 \ + config/t8_fortran.m4 \ config/t8_mpi.m4 diff --git a/api/Makefile.am b/api/Makefile.am index 974f210370..b27d462ba9 100644 --- a/api/Makefile.am +++ b/api/Makefile.am @@ -10,8 +10,24 @@ libt8_installed_headers_fortran_interface = \ dist_fortraninterfaceinclude_HEADERS = $(libt8_installed_headers_fortran_interface) +# Save the module sources in a different variable for later use +t8_fortran_module_sources = api/t8_fortran_interface/t8_fortran_interface_mod.f90 + +# Add the Fortran sources to the lib +libt8_compiled_sources += $(t8_fortran_module_sources) + +AM_FCFLAGS = libt8_compiled_sources += \ api/t8_fortran_interface/t8_fortran_interface.c -AM_CPPFLAGS += -I@top_srcdir@/api +AM_CPPFLAGS += -I@top_srcdir@/api/t8_fortran_interface +MODSOURCES = $(t8_fortran_module_sources) + +src_libt8_la_FCFLAGS = $(AM_FCFLAGS) +src_libt8_la_FFLAGS = $(FFLAGS) + +# Include the Fortran specific variables and rules +include api/t8_fortran_interface/t8_fortran_specific.mk + +# T8_ENABLE_FORTRAN endif diff --git a/api/t8_fortran_interface/CMakeLists.txt b/api/t8_fortran_interface/CMakeLists.txt new file mode 100644 index 0000000000..3072277cd3 --- /dev/null +++ b/api/t8_fortran_interface/CMakeLists.txt @@ -0,0 +1,18 @@ +# Link in C-Fortran interface file into libt8. +target_sources( T8 PRIVATE t8_fortran_interface.c ) +target_sources( T8 PRIVATE t8_fortran_interface_mod.f90 ) + +# Add this directory to header search path. +target_include_directories( T8 PRIVATE ${CMAKE_CURRENT_LIST_DIR} ) + +# Install header files. +install( + FILES ${CMAKE_CURRENT_LIST_DIR}/t8_fortran_interface.h + DESTINATION ${CMAKE_INSTALL_PREFIX}/include/t8_fortran_interface +) + +# Install module files. +install( + FILES ${CMAKE_BINARY_DIR}/src/t8_fortran_interface_mod.mod + DESTINATION ${CMAKE_INSTALL_PREFIX}/include/t8_fortran_interface +) diff --git a/api/t8_fortran_interface/t8_fortran_interface.c b/api/t8_fortran_interface/t8_fortran_interface.c index 92d0ab5a8e..487ca829fe 100644 --- a/api/t8_fortran_interface/t8_fortran_interface.c +++ b/api/t8_fortran_interface/t8_fortran_interface.c @@ -20,7 +20,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include +#include #include #include #include diff --git a/api/t8_fortran_interface/t8_fortran_interface.h b/api/t8_fortran_interface/t8_fortran_interface.h index 3424a34d61..195205f280 100644 --- a/api/t8_fortran_interface/t8_fortran_interface.h +++ b/api/t8_fortran_interface/t8_fortran_interface.h @@ -35,6 +35,7 @@ #include #include #include +#include typedef int (*t8_fortran_adapt_coordinate_callback) (double x, double y, double z, int is_family); diff --git a/api/t8_fortran_interface/t8_fortran_interface_mod.f90 b/api/t8_fortran_interface/t8_fortran_interface_mod.f90 new file mode 100644 index 0000000000..5001bc70cf --- /dev/null +++ b/api/t8_fortran_interface/t8_fortran_interface_mod.f90 @@ -0,0 +1,327 @@ +!! This file is part of t8code. +!! t8code is a C library to manage a collection (a forest) of multiple +!! connected adaptive space-trees of general element classes in parallel. +!! +!! Copyright (C) 2024 the developers +!! +!! t8code is free software; you can redistribute it and/or modify +!! it under the terms of the GNU General Public License as published by +!! the Free Software Foundation; either version 2 of the License, or +!! (at your option) any later version. +!! +!! t8code is distributed in the hope that it will be useful, +!! but WITHOUT ANY WARRANTY; without even the implied warranty of +!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +!! GNU General Public License for more details. +!! +!! You should have received a copy of the GNU General Public License +!! along with t8code; if not, write to the Free Software Foundation, Inc., +!! 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module t8_fortran_interface_mod + + use, intrinsic :: ISO_C_BINDING + + + !!! Interface for t8_fortran_MPI_Comm_new + !!! Given a fortran MPI Communicator, converts it into C and + !!! returns a pointer to the C MPI communicator. + !!! This function allocates memory that needs to be freed with + !!! t8_fortran_mpi_comm_delete_f + !!! + !!! Code modified from: https://stackoverflow.com/questions/42530620/how-to-pass-mpi-communicator-handle-from-fortran-to-c-using-iso-c-binding + INTERFACE + type (C_PTR) FUNCTION t8_fortran_mpi_comm_new_f (FCOMM) & + BIND(C, NAME='t8_fortran_MPI_Comm_new') + use, intrinsic :: ISO_C_BINDING, only: c_int, c_ptr + IMPLICIT NONE + INTEGER (C_INT), VALUE :: Fcomm + END FUNCTION t8_fortran_mpi_comm_new_f + END INTERFACE + + !!! Free memory of a C MPI communicator pointer that was + !!! allocated using t8_fortran_mpi_comm_new_f + INTERFACE + subroutine t8_fortran_mpi_comm_delete_f (Ccomm) & + BIND(C, NAME='t8_fortran_MPI_Comm_delete') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr), value :: Ccomm + END subroutine t8_fortran_mpi_comm_delete_f + END INTERFACE + + !!! Initialize sc and t8code with a given C MPI Communicator + Interface + subroutine t8_fortran_init_all_f (Ccomm) & + BIND(C, NAME='t8_fortran_init_all') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr), value :: Ccomm + END subroutine t8_fortran_init_all_f + end Interface + + !!! Initialize sc and t8code with a given C MPI Communicator + Interface + subroutine t8_fortran_init_all_noMPI_f () & + BIND(C, NAME='t8_fortran_init_all_noMPI') + END subroutine t8_fortran_init_all_noMPI_f + end Interface + + Interface + type (c_ptr) function t8_cmesh_new_periodic_tri_f (Ccomm) & + bind (c, name = 't8_cmesh_new_periodic_tri_wrap') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr), value :: Ccomm + end function t8_cmesh_new_periodic_tri_f + end Interface + + Interface + integer (c_int) function t8_cmesh_vtk_write_file_f (cmesh, fileprefix, scale) & + bind (c, name = 't8_cmesh_vtk_write_file') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int, c_char, c_double + IMPLICIT NONE + type (c_ptr), value :: cmesh + character (c_char) :: fileprefix + real (c_double), value :: scale + end function t8_cmesh_vtk_write_file_f + end Interface + + Interface + subroutine t8_cmesh_destroy_f (cmesh) & + bind (c, name = 't8_cmesh_destroy') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr) :: cmesh + end subroutine t8_cmesh_destroy_f + end Interface + + Interface + subroutine t8_fortran_cmesh_init_f (cmesh) & + bind (c, name = 't8_cmesh_init') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr) :: cmesh + end subroutine t8_fortran_cmesh_init_f + end Interface + + Interface + type (c_ptr) function t8_fortran_geometry_linear_new_f (dimension) & + bind (c, name = 't8_geometry_linear_new') + use, intrinsic :: ISO_C_BINDING, only: c_int, c_ptr + IMPLICIT NONE + integer (c_int), value :: dimension + end function t8_fortran_geometry_linear_new_f + end Interface + + Interface + subroutine t8_fortran_cmesh_register_geometry_f (cmesh, geometry) & + bind (c, name = 't8_cmesh_register_geometry') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr), value :: cmesh + type (c_ptr), value :: geometry + end subroutine t8_fortran_cmesh_register_geometry_f + end Interface + + Interface + subroutine t8_fortran_cmesh_set_tree_class_f (cmesh, gtree_id, tree_class) & + bind (c, name = 't8_cmesh_set_tree_class') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int64_t, c_int + IMPLICIT NONE + type (c_ptr), value :: cmesh + integer (c_int64_t), value :: gtree_id + integer (c_int), value :: tree_class + end subroutine t8_fortran_cmesh_set_tree_class_f + end Interface + + Interface + subroutine t8_fortran_cmesh_set_tree_vertices_f (cmesh, ltree_id, vertices, num_vertices) & + bind (c, name = 't8_cmesh_set_tree_vertices') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int, c_int64_t + IMPLICIT NONE + type (c_ptr), value :: cmesh + integer (c_int64_t), value :: ltree_id + type(c_ptr),value :: vertices + integer (c_int), value :: num_vertices + end subroutine t8_fortran_cmesh_set_tree_vertices_f + end Interface + + Interface + subroutine t8_fortran_cmesh_set_join_f (cmesh, gtree1, gtree2, face1, face2, orientation) & + bind (c, name = 't8_cmesh_set_join') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int, c_int64_t + IMPLICIT NONE + type (c_ptr), value :: cmesh + integer (c_int64_t), value :: gtree1 + integer (c_int64_t), value :: gtree2 + integer (c_int), value :: face1 + integer (c_int), value :: face2 + integer (c_int), value :: orientation + end subroutine t8_fortran_cmesh_set_join_f + end Interface + + Interface + subroutine t8_fortran_cmesh_set_join_by_vertices_noConn_f (cmesh, ntrees, eclasses, vertices, do_both_directions) & + bind (c, name = 't8_fortran_cmesh_set_join_by_vertices_noConn') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: cmesh + integer (c_int), value :: ntrees + type (c_ptr), value :: eclasses + type (c_ptr), value :: vertices + integer (c_int), value :: do_both_directions + end subroutine t8_fortran_cmesh_set_join_by_vertices_noConn_f + end Interface + + Interface + subroutine t8_fortran_cmesh_commit_f (cmesh, Ccom) & + bind (c, name = 't8_fortran_cmesh_commit') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr), value :: cmesh + type (c_ptr), value :: Ccom + end subroutine t8_fortran_cmesh_commit_f + end Interface + + Interface + type (c_ptr) function t8_forest_new_uniform_default_f (cmesh, level, do_face_ghost, Ccomm) & + bind (c, name = 't8_forest_new_uniform_default') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: cmesh + integer (c_int), value :: level + integer (c_int), value :: do_face_ghost + type (c_ptr), value :: Ccomm + end function t8_forest_new_uniform_default_f + end Interface + + + Interface + subroutine t8_forest_unref_f (forest) & + bind (c, name = 't8_forest_unref') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr) :: forest + end subroutine t8_forest_unref_f + end Interface + + + Interface + integer (c_int) function t8_forest_write_vtk_f (forest, fileprefix) & + bind (c, name = 't8_forest_write_vtk') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int, c_char, c_double + IMPLICIT NONE + type (c_ptr), value :: forest + character (c_char) :: fileprefix + end function t8_forest_write_vtk_f + end Interface + + Interface + subroutine t8_forest_iterate_replace_f (forest_new, forest_old, replace_fn) & + bind (c, name = 't8_forest_iterate_replace') + use, intrinsic :: ISO_C_BINDING, only: c_ptr + IMPLICIT NONE + type (c_ptr), value :: forest_new + type (c_ptr), value :: forest_old + type (c_ptr), value :: replace_fn + end subroutine t8_forest_iterate_replace_f + end Interface + + Interface + integer (c_int) function t8_forest_get_local_num_elements (forest) & + bind (c, name = 't8_forest_get_local_num_elements') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: forest + end function t8_forest_get_local_num_elements + end Interface + + Interface + integer (c_int) function t8_forest_get_global_num_elements (forest) & + bind (c, name = 't8_forest_get_global_num_elements') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: forest + end function t8_forest_get_global_num_elements + end Interface + + Interface + integer (c_int) function t8_forest_get_num_local_trees (forest) & + bind (c, name = 't8_forest_get_num_local_trees') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: forest + end function t8_forest_get_num_local_trees + end Interface + + Interface + integer (c_int) function t8_forest_get_tree_num_elements (forest, ltreeid) & + bind (c, name = 't8_forest_get_tree_num_elements') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: forest + integer (c_int), value :: ltreeid + end function t8_forest_get_tree_num_elements + end Interface + + Interface + type (c_ptr) function t8_forest_get_element_in_tree (forest, ltreeid, leid_in_tree) & + bind (c, name = 't8_forest_get_element_in_tree') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: forest + integer (c_int), value :: ltreeid, leid_in_tree + end function t8_forest_get_element_in_tree + end Interface + + Interface + subroutine t8_forest_element_from_ref_coords (forest, ltreeid, element, ref_coords, num_coords, coords_out) & + bind (c, name = 't8_forest_element_from_ref_coords') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int, c_double + IMPLICIT NONE + type (c_ptr), value :: forest, element + integer (c_int), value :: ltreeid, num_coords + real (c_double), dimension(3) :: ref_coords, coords_out + end subroutine t8_forest_element_from_ref_coords + end Interface + + Interface + subroutine t8_global_productionf_noargs_f (string) & + bind (c, name = 't8_global_productionf_noargs') + use, intrinsic :: ISO_C_BINDING, only: c_char + IMPLICIT NONE + character (c_char) :: string + end subroutine t8_global_productionf_noargs_f + end Interface + + Interface + subroutine t8_fortran_finalize_f () & + bind (c, name = 't8_fortran_finalize') + IMPLICIT NONE + end subroutine t8_fortran_finalize_f + end Interface + + Interface + type (c_ptr) function t8_fortran_adapt_by_coordinates_f (forest, recursive, callback) & + bind (c, name = 't8_fortran_adapt_by_coordinates') + use, intrinsic :: ISO_C_BINDING, only : c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: forest + integer (c_int), value :: recursive + type (c_ptr), value :: callback + end function t8_fortran_adapt_by_coordinates_f + end Interface + + Interface + subroutine t8_fortran_element_volume_f (forest, ltreeid, element) & + bind (c, name = 't8_forest_element_volume') + use, intrinsic :: ISO_C_BINDING, only: c_ptr, c_int + IMPLICIT NONE + type (c_ptr), value :: forest + integer (c_int), value :: ltreeid + type (c_ptr), value :: element + end subroutine t8_fortran_element_volume_f + end Interface + +End module t8_fortran_interface_mod diff --git a/api/t8_fortran_interface/t8_fortran_specific.mk b/api/t8_fortran_interface/t8_fortran_specific.mk new file mode 100644 index 0000000000..3bde84337f --- /dev/null +++ b/api/t8_fortran_interface/t8_fortran_specific.mk @@ -0,0 +1,47 @@ +if T8_ENABLE_FORTRAN +# Clean up modules files in the root directory (if no module directory has been specified) +CLEANFILES += *.$(FC_MODEXT) + +# Get the supplied FCFLAGS +AM_FCFLAGS += @FCFLAGS@ + +# Define a variable holding the module directory (for a rule below) +t8_current_moddir = + +if T8_WITH_MODDIR +# Updates for the module output and include path (if a separate module directory has been specified) +AM_FCFLAGS += $(FC_MODOUT)@T8_FORTRAN_MODULE_DIR@ $(FC_MODINC)@T8_FORTRAN_MODULE_DIR@ +AM_CPPFLAGS += -I@T8_FORTRAN_MODULE_DIR@ + +# Clean the module files in this directory +CLEANFILES += @T8_FORTRAN_MODULE_DIR@/*.$(FC_MODEXT) + +# Add the creation of the module directory as an order only prerequisite to the Fortran module files +$(MODSOURCES): %.f90 : | create-moddir + +# Rule to create the module directory +create-moddir: + @$(MKDIR_P) @T8_FORTRAN_MODULE_DIR@ + +# Save the module directory +t8_current_moddir += @T8_FORTRAN_MODULE_DIR@/ + +# End if T8_WITH_MODDIR +endif + +# If the install target is made, we will copy the module files into the include directory (after the installation of the header files) +install-data-hook: + @cp -fp $(t8_current_moddir)*.$(FC_MODEXT) $(includedir)/t8_fortran_interface + +# Define dependencies of the Fortran modules (in case they depend on other modules) +# This needs to be done in order to ensure the correct build process in any case + +# Define dependencies for all Fortran programs of the Fortran modules +# This needs to be done in order to ensure the correct build process in any case +# ... + +# TODO: Implement t8_fortran_test depends on the modules: t8_fortran_interface +#example/Fortran/t8_fortran_test.o : api/t8_fortran_interface/t8_fortran_interface.o + +# end if T8_ENABLE_FORTRAN +endif diff --git a/cmake/FindOpenCASCADE.cmake b/cmake/FindOpenCASCADE.cmake index 828990bd45..6eb5c10c22 100644 --- a/cmake/FindOpenCASCADE.cmake +++ b/cmake/FindOpenCASCADE.cmake @@ -1,4 +1,4 @@ -# taken and adapted from the heekscad project: https://github.com/Heeks +# Taken and adapted from the heekscad project: https://github.com/Heeks # HeeksCAD is covered by the new BSD license diff --git a/config/t8_fortran.m4 b/config/t8_fortran.m4 new file mode 100644 index 0000000000..b1e452376e --- /dev/null +++ b/config/t8_fortran.m4 @@ -0,0 +1,34 @@ +dnl T8_CHECK_FORTRAN +dnl This functions checks some properties of Fortran modules and +dnl whether specific directory for the Fortran module files has been specified or not. +dnl +dnl A directory may be specified by the option --with-moddir= +dnl This option is only of relevance if the option --enable-fortran has been chosen, +dnl since only in this case Fortran codes will be compiled +dnl +AC_DEFUN([T8_CHECK_FORTRAN], [ + +dnl Check if a directory has been specified which will hold the module files +T8_ARG_WITH([moddir], + [if Fortran modules will be built, this option specifies an explicit directory which should hold the module files (use --with-moddir=)], + [MODDIR]) + +dnl If Fortran is enabled +if test "x$T8_ENABLE_FORTRAN" != xno ; then + +dnl Check the properties of Fortran modules (after the Fortran Compiler has been found by MPI_ENGAGE) +AC_FC_MODULE_EXTENSION +AC_FC_MODULE_FLAG +AC_FC_MODULE_OUTPUT_FLAG + +if test "x$T8_WITH_MODDIR" = xyes ; then + dnl The option is given without a directory + AC_MSG_ERROR([missing directory path for the module directory]) +elif test "x$T8_WITH_MODDIR" != xno ; then + AC_MSG_NOTICE([we have set a module dir var]) + dnl Substitute the variable in the makefile + AC_SUBST(T8_FORTRAN_MODULE_DIR, $T8_WITH_MODDIR) +fi + +fi +]) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a6cd1f77af..ef9f9ba71a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,7 +20,15 @@ function( add_t8_test ) set(TEST_BUILD_DIR "${CMAKE_BINARY_DIR}/${TEST_RELATIVE_DIR}") add_executable( ${ADD_T8_TEST_NAME} ${ADD_T8_TEST_SOURCES} ) - target_link_libraries( ${ADD_T8_TEST_NAME} PRIVATE T8 gtest pthread) + + # Check if test is a Fortran file and if MPI is enabled. + string ( FIND ${ADD_T8_TEST_NAME} "fortran" is_fortran_file ) + if ( (${is_fortran_file} GREATER_EQUAL 0) AND T8CODE_ENABLE_MPI ) + target_include_directories( ${ADD_T8_TEST_NAME} PRIVATE ${CMAKE_BINARY_DIR}/src ) + target_link_libraries( ${ADD_T8_TEST_NAME} PRIVATE T8 gtest pthread MPI::MPI_Fortran ) + else() + target_link_libraries( ${ADD_T8_TEST_NAME} PRIVATE T8 gtest pthread ) + endif () set_target_properties(${ADD_T8_TEST_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TEST_BUILD_DIR}" @@ -131,6 +139,10 @@ add_t8_test( NAME t8_gtest_pack_unpack_serial SOURCES t8_gtest_main.cx add_t8_test( NAME t8_gtest_root_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_root.cxx ) add_t8_test( NAME t8_gtest_scheme_consistency_serial SOURCES t8_gtest_main.cxx t8_schemes/t8_gtest_scheme_consistency.cxx ) +if( T8CODE_BUILD_FORTRAN_INTERFACE AND T8CODE_ENABLE_MPI ) + add_t8_test( NAME t8_test_fortran_mpi_interface_init_parallel SOURCES api/t8_fortran_interface/t8_test_mpi_init.f90 ) +endif() + copy_test_file( test_cube_unstructured_1.inp ) copy_test_file( test_cube_unstructured_2.inp ) copy_test_file( test_vtk_tri.vtu ) diff --git a/test/api/t8_fortran_interface/t8_test_mpi_init.f90 b/test/api/t8_fortran_interface/t8_test_mpi_init.f90 new file mode 100644 index 0000000000..665a6d0ec7 --- /dev/null +++ b/test/api/t8_fortran_interface/t8_test_mpi_init.f90 @@ -0,0 +1,51 @@ +!! This file is part of t8code. +!! t8code is a C library to manage a collection (a forest) of multiple +!! connected adaptive space-trees of general element classes in parallel. +!! +!! Copyright (C) 2024 the developers +!! +!! t8code is free software; you can redistribute it and/or modify +!! it under the terms of the GNU General Public License as published by +!! the Free Software Foundation; either version 2 of the License, or +!! (at your option) any later version. +!! +!! t8code is distributed in the hope that it will be useful, +!! but WITHOUT ANY WARRANTY; without even the implied warranty of +!! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +!! GNU General Public License for more details. +!! +!! You should have received a copy of the GNU General Public License +!! along with t8code; if not, write to the Free Software Foundation, Inc., +!! 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +!! Description: +!! +!! This program tests if t8code can be initialized from Fortran +!! with given MPI communicator. Works only when MPI is enabled. + +program t8_test_mpi_init + use mpi + use iso_c_binding, only: c_ptr, c_int + use t8_fortran_interface_mod + + implicit none + + integer :: ierror, fcomm + type(c_ptr) :: ccomm + + call MPI_Init (ierror) + + if (ierror /= 0) then + print *, 'MPI initialization failed.' + stop 1 + endif + + fcomm = MPI_COMM_WORLD + ccomm = t8_fortran_mpi_comm_new_f (fcomm) + + call t8_fortran_init_all_f (ccomm) + call t8_fortran_finalize_f () + + print *, 'All good!' + stop 0 +end program