From 7d7c380d6b43274b67301c60f64a4cd0b15c6d24 Mon Sep 17 00:00:00 2001 From: Matt Dawson Date: Wed, 15 May 2024 07:58:24 -0700 Subject: [PATCH] Update build scripts (#106) * update build scripts * update packaging * add fortran to project by default * add fortran compiler to python image * move fortran mod files to include dir * separate shared fortran utility module * fix fortran wrapper build for optional MICM and TUVX inclusion * update integration builds * make MICM and TUV-x not dependent on CXX interface * add more output to fortran packaging script * only build micm with c++ interface * update buidl scripts * specify TUV-x install include paths * disable TUV-x for some tests * include fortran compiler in docs build * update tag for TUV-x * don't build with TUV-x for doc gen --- .../workflows/fetch_content_integration.yml | 2 - .github/workflows/gh_pages.yml | 5 + .github/workflows/mac.yml | 4 +- .github/workflows/ubuntu.yml | 2 +- .github/workflows/windows.yml | 10 +- CMakeLists.txt | 8 +- cmake/dependencies.cmake | 8 +- docker/Dockerfile | 2 + docker/Dockerfile.docs | 1 + docker/Dockerfile.fortran-gcc.integration | 3 + docker/Dockerfile.fortran-intel | 16 +- docker/Dockerfile.fortran-nvhpc | 17 +- docker/Dockerfile.memcheck | 2 + docker/Dockerfile.mpi | 2 + docker/Dockerfile.mpi_openmp | 2 + docker/Dockerfile.openmp | 2 + docker/Dockerfile.python | 4 + fortran/CMakeLists.txt | 20 +- .../fetch_content_integration/CMakeLists.txt | 89 ++-- fortran/test/unit/CMakeLists.txt | 5 +- {src => fortran}/util.F90 | 0 src/CMakeLists.txt | 10 +- src/packaging/CMakeLists.txt | 52 ++- src/tuvx/CMakeLists.txt | 1 + src/tuvx/interface.F90 | 14 +- src/tuvx/tuvx_util.F90 | 384 ++++++++++++++++++ 26 files changed, 553 insertions(+), 112 deletions(-) rename {src => fortran}/util.F90 (100%) create mode 100644 src/tuvx/tuvx_util.F90 diff --git a/.github/workflows/fetch_content_integration.yml b/.github/workflows/fetch_content_integration.yml index e447f6e4..6c3c86d2 100644 --- a/.github/workflows/fetch_content_integration.yml +++ b/.github/workflows/fetch_content_integration.yml @@ -2,8 +2,6 @@ name: FetchContentIntegration on: push: - branches: - - main pull_request: workflow_dispatch: diff --git a/.github/workflows/gh_pages.yml b/.github/workflows/gh_pages.yml index f9a57b93..2e6e4858 100644 --- a/.github/workflows/gh_pages.yml +++ b/.github/workflows/gh_pages.yml @@ -10,6 +10,11 @@ jobs: build-and-deploy: name: Build and deploy to gh-pages runs-on: ubuntu-latest + strategy: + matrix: + gcc-version: [13] + env: + FC: gfortran-${{ matrix.gcc-version }} steps: - uses: actions/checkout@v3 with: diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 94f4226e..40522850 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -35,7 +35,7 @@ jobs: - name: Run Cmake run: | PYTHON_PATH=$(which python) - cmake -S . -B build -D CMAKE_CXX_FLAGS=-Wl,-ld_classic -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_PYTHON_LIBRARY=ON -D Python3_EXECUTABLE=$PYTHON_PATH + cmake -S . -B build -D CMAKE_CXX_FLAGS=-Wl,-ld_classic -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_TUVX=OFF -D MUSICA_ENABLE_PYTHON_LIBRARY=ON -D Python3_EXECUTABLE=$PYTHON_PATH - name: Build run: cmake --build build --verbose --parallel 10 @@ -60,7 +60,7 @@ jobs: run: brew install netcdf netcdf-fortran - name: Run Cmake - run: cmake -S . -B build -D CMAKE_CXX_FLAGS=-Wl,-ld_classic -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_BUILD_FORTRAN_INTERFACE=ON + run: cmake -S . -B build -D CMAKE_CXX_FLAGS=-Wl,-ld_classic -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_TUVX=OFF -D MUSICA_BUILD_FORTRAN_INTERFACE=ON - name: Build run: cmake --build build --verbose --parallel 10 diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 974aa07c..a04ceba2 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -38,7 +38,7 @@ jobs: - run: pip install -r python/requirements.txt - name: Run Cmake - run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_PYTHON_LIBRARY=ON + run: cmake -S . -B build -D CMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_TUVX=OFF -D MUSICA_ENABLE_PYTHON_LIBRARY=ON - name: Build run: cmake --build build --verbose --parallel 10 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index f321930c..834628b6 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -30,7 +30,7 @@ jobs: - run: pip install -r python/requirements.txt - name: Run Cmake - run: cmake -S . -B build -G "MinGW Makefiles" -D MUSICA_ENABLE_PYTHON_LIBRARY=ON + run: cmake -S . -B build -G "MinGW Makefiles" -D MUSICA_ENABLE_TUVX=OFF -D MUSICA_ENABLE_PYTHON_LIBRARY=ON - name: Build run: cmake --build build --parallel @@ -59,7 +59,7 @@ jobs: - run: pip install -r python/requirements.txt - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_PYTHON_LIBRARY=ON + run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_TUVX=OFF -D MUSICA_ENABLE_PYTHON_LIBRARY=ON - name: Build run: cmake --build build --config ${{ matrix.build_type }} --parallel @@ -87,7 +87,7 @@ jobs: - run: pip install -r python/requirements.txt - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_PYTHON_LIBRARY=ON + run: cmake -S . -B build -G "Visual Studio 17 2022" -A ${{ matrix.architecture }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -D MUSICA_ENABLE_TUVX=OFF -D MUSICA_ENABLE_PYTHON_LIBRARY=ON - name: Build run: cmake --build build --config ${{ matrix.build_type }} --parallel @@ -111,7 +111,7 @@ jobs: - run: pip install -r python/requirements.txt - name: Run CMake - run: cmake -S . -B build -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" -DCMAKE_C_COMPILER="C:/Program Files/LLVM/bin/clang.exe" -G"MinGW Makefiles" -D MUSICA_ENABLE_PYTHON_LIBRARY=ON + run: cmake -S . -B build -DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" -DCMAKE_C_COMPILER="C:/Program Files/LLVM/bin/clang.exe" -G"MinGW Makefiles" -D MUSICA_ENABLE_TUVX=OFF -D MUSICA_ENABLE_PYTHON_LIBRARY=ON - name: Build run: cmake --build build --parallel @@ -139,7 +139,7 @@ jobs: - run: pip install -r python/requirements.txt - name: Run CMake - run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -T ClangCL -D MUSICA_ENABLE_PYTHON_LIBRARY=ON + run: cmake -S . -B build -G "Visual Studio 16 2019" -A ${{ matrix.architecture }} -T ClangCL -D MUSICA_ENABLE_TUVX=OFF -D MUSICA_ENABLE_PYTHON_LIBRARY=ON - name: Build run: cmake --build build --config Debug --parallel diff --git a/CMakeLists.txt b/CMakeLists.txt index 48651b5d..348d8420 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,12 +29,8 @@ option(MUSICA_ENABLE_MPI "Enable MPI parallel support" OFF) option(MUSICA_ENABLE_OPENMP "Enable OpemMP support" OFF) option(MUSICA_ENABLE_MEMCHECK "Enable memory checking" OFF) option(MUSICA_BUILD_DOCS "Build the documentation" OFF) - -cmake_dependent_option( - MUSICA_ENABLE_TUVX "Builds TUV-x, a photolysis calculator library" ON "MUSICA_BUILD_FORTRAN_INTERFACE" OFF) - -cmake_dependent_option( - MUSICA_ENABLE_MICM "Adds MICM, a model independent chemical mechanism solver" ON "MUSICA_BUILD_C_CXX_INTERFACE" OFF) +option(MUSICA_ENABLE_MICM "Enable MICM" ON) +option(MUSICA_ENABLE_TUVX "Enable TUV-x" ON) cmake_dependent_option( MUSICA_ENABLE_PYTHON_LIBRARY "Adds pybind11, a lightweight header-only library that exposes C++ types in Python and vice versa" OFF "MUSICA_BUILD_C_CXX_INTERFACE" OFF) diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index 52585e99..36449193 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -30,7 +30,7 @@ endif() ################################################################################ # MICM -if (MUSICA_ENABLE_MICM) +if (MUSICA_ENABLE_MICM AND MUSICA_BUILD_C_CXX_INTERFACE) FetchContent_Declare(micm GIT_REPOSITORY https://github.com/NCAR/micm.git GIT_TAG 2a5cd4e11a6973974f3c584dfa9841d70e0a42d5 @@ -41,13 +41,15 @@ endif() ################################################################################ # TUV-x -if (MUSICA_ENABLE_TUVX) +if (MUSICA_ENABLE_TUVX AND MUSICA_BUILD_C_CXX_INTERFACE) set(TUVX_ENABLE_TESTS OFF CACHE BOOL "" FORCE) set(TUVX_MOD_DIR ${MUSICA_MOD_DIR} CACHE STRING "" FORCE) + set(TUVX_INSTALL_MOD_DIR ${MUSICA_INSTALL_INCLUDE_DIR} CACHE STRING "" FORCE) + set(TUVX_INSTALL_INCLUDE_DIR ${MUSICA_INSTALL_INCLUDE_DIR} CACHE STRING "" FORCE) FetchContent_Declare(tuvx GIT_REPOSITORY https://github.com/NCAR/tuv-x.git - GIT_TAG main + GIT_TAG 6ff27992da1485392329208b736d2ec1522dafa3 ) FetchContent_MakeAvailable(tuvx) diff --git a/docker/Dockerfile b/docker/Dockerfile index 42880400..53255b7e 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -4,8 +4,10 @@ RUN dnf -y update \ && dnf -y install \ cmake \ gcc-c++ \ + gfortran \ gdb \ git \ + lapack-devel \ lcov \ make \ netcdf-fortran-devel \ diff --git a/docker/Dockerfile.docs b/docker/Dockerfile.docs index b3cc510e..b073c621 100644 --- a/docker/Dockerfile.docs +++ b/docker/Dockerfile.docs @@ -27,6 +27,7 @@ RUN echo "The suffix is '$SWITCHER_SUFFIX'" RUN mkdir /build \ && cd /build \ && cmake -D MUSICA_BUILD_DOCS=ON \ + -D MUSICA_ENABLE_TUVX=OFF \ /musica \ && make docs diff --git a/docker/Dockerfile.fortran-gcc.integration b/docker/Dockerfile.fortran-gcc.integration index 76d70945..162b5377 100644 --- a/docker/Dockerfile.fortran-gcc.integration +++ b/docker/Dockerfile.fortran-gcc.integration @@ -40,6 +40,7 @@ RUN cd musica \ -D CMAKE_BUILD_TYPE=Release \ -D MUSICA_ENABLE_TESTS=ON \ -D MUSICA_ENABLE_MICM=ON \ + -D MUSICA_ENABLE_TUVX=OFF \ && cd build \ && make install @@ -49,6 +50,8 @@ RUN cd musica/fortran/test/fetch_content_integration \ && cmake .. \ -D CMAKE_BUILD_TYPE=Release \ -D MUSICA_GIT_TAG=${MUSICA_GIT_TAG} \ + -D MUSICA_ENABLE_MICM=ON \ + -D MUSICA_ENABLE_TUVX=OFF \ && make WORKDIR musica/fortran/test/fetch_content_integration/build \ No newline at end of file diff --git a/docker/Dockerfile.fortran-intel b/docker/Dockerfile.fortran-intel index 05f45e3d..a28bcf18 100644 --- a/docker/Dockerfile.fortran-intel +++ b/docker/Dockerfile.fortran-intel @@ -22,11 +22,13 @@ RUN apt update \ zlib1g-dev \ && apt clean -# Build netcdf-fortran -ENV CXX=icpx -ENV CC=icx -ENV FC=ifx +# Set environment variables to install MUSICA using gcc +ENV CXX=g++ +ENV CC=gcc +ENV FC=gfortran +ENV FFLAGS="-I/usr/include/" +# Build netcdf-fortran RUN git clone https://github.com/Unidata/netcdf-fortran.git \ && cd netcdf-fortran \ && git checkout v4.6.0 \ @@ -35,10 +37,6 @@ RUN git clone https://github.com/Unidata/netcdf-fortran.git \ && make -j 8 \ && make install -# Set environment variables to install MUSICA using gcc -ENV FC=gfortran -ENV FFLAGS="-I/usr/include/" - # Copy the musica code COPY . musica @@ -47,7 +45,6 @@ RUN cd musica \ && cmake -S . \ -B build \ -D MUSICA_ENABLE_TESTS=ON \ - -D MUSICA_ENABLE_TUVX=OFF \ -D CMAKE_BUILD_TYPE=Release \ && cd build \ && make install @@ -61,6 +58,7 @@ RUN cd musica/fortran/test/fetch_content_integration \ && mkdir build && cd build \ && cmake .. \ -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_EXE_LINKER_FLAGS="-Wl,--copy-dt-needed-entries" \ -D MUSICA_GIT_TAG=${MUSICA_GIT_TAG} \ && make diff --git a/docker/Dockerfile.fortran-nvhpc b/docker/Dockerfile.fortran-nvhpc index 650bbe10..d93e9b4c 100644 --- a/docker/Dockerfile.fortran-nvhpc +++ b/docker/Dockerfile.fortran-nvhpc @@ -15,6 +15,7 @@ RUN apt update \ gcc \ gfortran \ git \ + liblapack-dev \ lcov \ libcurl4-openssl-dev \ libhdf5-dev \ @@ -27,11 +28,13 @@ RUN apt update \ zlib1g-dev \ && apt clean -# Build netcdf-fortran -ENV CXX=nvc++ -ENV CC=nvc -ENV FC=nvfortran +# Set environment variables to install MUSICA using gcc +ENV CXX=g++ +ENV CC=gcc +ENV FC=gfortran +ENV FFLAGS="-I/usr/include/" +# Build netcdf-fortran RUN git clone https://github.com/Unidata/netcdf-fortran.git \ && cd netcdf-fortran \ && git checkout v4.6.0 \ @@ -40,10 +43,6 @@ RUN git clone https://github.com/Unidata/netcdf-fortran.git \ && make -j 8 \ && make install -# Set environment variables to install MUSICA using gcc -ENV FC=gfortran -ENV FFLAGS="-I/usr/include/" - # Copy the musica code COPY . musica @@ -52,7 +51,6 @@ RUN cd musica \ && cmake -S . \ -B build \ -D MUSICA_ENABLE_TESTS=ON \ - -D MUSICA_ENABLE_TUVX=OFF \ -D CMAKE_BUILD_TYPE=Release \ && cd build \ && make install @@ -66,6 +64,7 @@ RUN cd musica/fortran/test/fetch_content_integration \ && mkdir build && cd build \ && cmake .. \ -D CMAKE_BUILD_TYPE=Release \ + -D CMAKE_EXE_LINKER_FLAGS="-Wl,--copy-dt-needed-entries" \ -D MUSICA_GIT_TAG=${MUSICA_GIT_TAG} \ && make diff --git a/docker/Dockerfile.memcheck b/docker/Dockerfile.memcheck index 369e94d9..25c61637 100644 --- a/docker/Dockerfile.memcheck +++ b/docker/Dockerfile.memcheck @@ -4,8 +4,10 @@ RUN dnf -y update \ && dnf -y install \ cmake \ gcc-c++ \ + gfortran \ gdb \ git \ + lapack-devel \ lcov \ make \ netcdf-fortran-devel \ diff --git a/docker/Dockerfile.mpi b/docker/Dockerfile.mpi index 27ab222d..91acf76e 100644 --- a/docker/Dockerfile.mpi +++ b/docker/Dockerfile.mpi @@ -12,7 +12,9 @@ WORKDIR /home/test_user RUN sudo dnf -y install \ cmake \ gcc-c++ \ + gfortran \ git \ + lapack-devel \ lcov \ make \ netcdf-fortran-devel \ diff --git a/docker/Dockerfile.mpi_openmp b/docker/Dockerfile.mpi_openmp index 03d26053..a9147545 100644 --- a/docker/Dockerfile.mpi_openmp +++ b/docker/Dockerfile.mpi_openmp @@ -12,7 +12,9 @@ WORKDIR /home/test_user RUN sudo dnf -y install \ cmake \ gcc-c++ \ + gfortran \ git \ + lapack-devel \ lcov \ make \ netcdf-fortran-devel \ diff --git a/docker/Dockerfile.openmp b/docker/Dockerfile.openmp index 81647771..7ad2c7af 100644 --- a/docker/Dockerfile.openmp +++ b/docker/Dockerfile.openmp @@ -12,7 +12,9 @@ WORKDIR /home/test_user RUN sudo dnf -y install \ cmake \ gcc-c++ \ + gfortran \ git \ + lapack-devel \ lcov \ make \ netcdf-fortran-devel \ diff --git a/docker/Dockerfile.python b/docker/Dockerfile.python index 3600c98a..1648d612 100644 --- a/docker/Dockerfile.python +++ b/docker/Dockerfile.python @@ -4,8 +4,11 @@ RUN dnf -y update \ && dnf -y install \ cmake \ gcc-c++ \ + gcc-fortran \ git \ + lapack-devel \ make \ + netcdf-fortran-devel \ python-devel \ pip \ && dnf clean all @@ -22,6 +25,7 @@ RUN cd musica \ -B build \ -D CMAKE_BUILD_TYPE=Release \ -D MUSICA_ENABLE_PYTHON_LIBRARY=ON \ + -D MUSICA_ENABLE_TUVX=OFF \ && cd build \ && make install -j 8 diff --git a/fortran/CMakeLists.txt b/fortran/CMakeLists.txt index ddd5e7f9..1470a355 100644 --- a/fortran/CMakeLists.txt +++ b/fortran/CMakeLists.txt @@ -7,6 +7,11 @@ project( ) message (STATUS "CMake build configuration for ${PROJECT_NAME} (${CMAKE_BUILD_TYPE}) ${PROJECT_VERSION}") +message (STATUS " MICM: ${MUSICA_ENABLE_MICM}") +message (STATUS " TUV-x: ${MUSICA_ENABLE_TUVX}") +message (STATUS " Tests: ${MUSICA_ENABLE_TESTS}") +message (STATUS " Install: ${MUSICA_ENABLE_INSTALL}") +message (STATUS " C/C++ Interface: ${MUSICA_BUILD_C_CXX_INTERFACE}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) @@ -41,9 +46,20 @@ target_link_libraries(musica-fortran target_sources(musica-fortran PRIVATE - micm.F90 - tuvx.F90 + util.F90 ) +if (MUSICA_ENABLE_MICM) + target_sources(musica-fortran + PRIVATE + micm.F90 + ) +endif() +if (MUSICA_ENABLE_TUVX) + target_sources(musica-fortran + PRIVATE + tuvx.F90 + ) +endif() # Add flags for gfortran if(${CMAKE_Fortran_COMPILER_ID} MATCHES "GNU") diff --git a/fortran/test/fetch_content_integration/CMakeLists.txt b/fortran/test/fetch_content_integration/CMakeLists.txt index 4d08257e..94a14fb6 100644 --- a/fortran/test/fetch_content_integration/CMakeLists.txt +++ b/fortran/test/fetch_content_integration/CMakeLists.txt @@ -16,10 +16,8 @@ FetchContent_Declare(musica_fortran GIT_TAG ${MUSICA_GIT_TAG} ) -set(MUSICA_BUILD_C_CXX_INTERFACE ON) +set(MUSICA_BUILD_C_CXX_INTERFACE OFF) set(MUSICA_BUILD_FORTRAN_INTERFACE ON) -set(MUSICA_ENABLE_MICM ON) -set(MUSICA_ENABLE_TUVX ON) set(MUSICA_ENABLE_TESTS OFF) set(MUSICA_ENABLE_INSTALL OFF) @@ -29,55 +27,46 @@ FetchContent_MakeAvailable(musica_fortran) add_custom_target(copy_unit_test_configs ALL ${CMAKE_COMMAND} -E copy_directory ${musica_fortran_SOURCE_DIR}/configs ${CMAKE_BINARY_DIR}/configs) -# copy config data from tuvx -# we know it'll be there, but we can't use tuvx_SOURCE_DIR because fetch content -# for tuvx is defined inside of the musica-fortran project so it's not avaialble at this level -add_custom_target(copy_tuvx_test_configs ALL ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_BINARY_DIR}/_deps/tuvx-src/examples ${CMAKE_BINARY_DIR}/examples) - -add_custom_target(copy_tuvx_data ALL ${CMAKE_COMMAND} -E copy_directory - ${CMAKE_BINARY_DIR}/_deps/tuvx-src/data ${CMAKE_BINARY_DIR}/data) - - -find_package(PkgConfig REQUIRED) -pkg_check_modules(netcdff IMPORTED_TARGET REQUIRED netcdf-fortran) - enable_testing() # API Test -add_executable(test_micm_fortran_api test_micm_api.F90) - -target_link_libraries(test_micm_fortran_api - PRIVATE - musica::musica-fortran -) - -set_target_properties(test_micm_fortran_api - PROPERTIES - LINKER_LANGUAGE Fortran -) - -add_test( - NAME test_micm_fortran_api - COMMAND $ - WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} -) +if (MUSICA_ENABLE_MICM) + add_executable(test_micm_fortran_api test_micm_api.F90) + + target_link_libraries(test_micm_fortran_api + PRIVATE + musica::musica-fortran + ) + + set_target_properties(test_micm_fortran_api + PROPERTIES + LINKER_LANGUAGE Fortran + ) + + add_test( + NAME test_micm_fortran_api + COMMAND $ + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) +endif() # API Test -add_executable(test_tuvx_fortran_api test_tuvx_api.F90) - -target_link_libraries(test_tuvx_fortran_api - PRIVATE - musica::musica-fortran -) - -set_target_properties(test_tuvx_fortran_api - PROPERTIES - LINKER_LANGUAGE Fortran -) - -add_test( - NAME test_tuvx_fortran_api - COMMAND $ - WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} -) \ No newline at end of file +if (MUSICA_ENABLE_TUVX) + add_executable(test_tuvx_fortran_api test_tuvx_api.F90) + + target_link_libraries(test_tuvx_fortran_api + PRIVATE + musica::musica-fortran + ) + + set_target_properties(test_tuvx_fortran_api + PROPERTIES + LINKER_LANGUAGE Fortran + ) + + add_test( + NAME test_tuvx_fortran_api + COMMAND $ + WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} + ) +endif() \ No newline at end of file diff --git a/fortran/test/unit/CMakeLists.txt b/fortran/test/unit/CMakeLists.txt index 22c4c1d1..69e15730 100644 --- a/fortran/test/unit/CMakeLists.txt +++ b/fortran/test/unit/CMakeLists.txt @@ -1,7 +1,10 @@ include(test_util) create_standard_test_fortran(NAME fortran_util SOURCES util.F90) -create_standard_test_fortran(NAME micm_fortran_api SOURCES ../fetch_content_integration/test_micm_api.F90) + +if (MUSICA_ENABLE_MICM) + create_standard_test_fortran(NAME micm_fortran_api SOURCES ../fetch_content_integration/test_micm_api.F90) +endif() if (MUSICA_ENABLE_TUVX) create_standard_test_fortran(NAME connect_to_tuvx SOURCES tuvx.F90) diff --git a/src/util.F90 b/fortran/util.F90 similarity index 100% rename from src/util.F90 rename to fortran/util.F90 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 46e33dda..7ebbe2cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,7 +4,7 @@ project( musica VERSION ${PROJECT_VERSION} - LANGUAGES C CXX + LANGUAGES C CXX ) message (STATUS "CMake build configuration for ${PROJECT_NAME} (${CMAKE_BUILD_TYPE}) ${PROJECT_VERSION}") @@ -38,7 +38,6 @@ configure_file(version.c.in ${CMAKE_BINARY_DIR}/version.c @ONLY) target_sources(musica PRIVATE util.cpp - util.F90 component_versions.c ${CMAKE_BINARY_DIR}/version.c ) @@ -52,10 +51,10 @@ set_target_properties(musica PROPERTIES SOVERSION ${PROJECT_VERSION_MAJOR} ) -message(STATUS "seting include directory to ${CMAKE_CURRENT_SOURCE_DIR}/../include") +message(STATUS "seting include directory to ${MUSICA_PROJECT_SRC_DIR}/include") target_include_directories(musica PUBLIC - $ + $ $ ) @@ -71,7 +70,7 @@ endif() # TUVX if(MUSICA_ENABLE_TUVX) enable_language(Fortran) - + # include the sources directly into musica target_sources(musica PRIVATE @@ -87,6 +86,7 @@ if(MUSICA_ENABLE_TUVX) target_include_directories(tuvx PUBLIC $ + $ ) add_subdirectory(tuvx) diff --git a/src/packaging/CMakeLists.txt b/src/packaging/CMakeLists.txt index 46751354..2df3a57c 100644 --- a/src/packaging/CMakeLists.txt +++ b/src/packaging/CMakeLists.txt @@ -3,8 +3,6 @@ include(CMakePackageConfigHelpers) install( TARGETS musica - nlohmann_json - micm EXPORT musica_Exports LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} @@ -19,36 +17,64 @@ install( ${MUSICA_INSTALL_INCLUDE_DIR} ) +# install fortran source files install( FILES - ${MUSICA_FORTRAN_SRC_DIR}/micm.F90 + ${MUSICA_FORTRAN_SRC_DIR}/util.F90 DESTINATION ${MUSICA_INSTALL_INCLUDE_DIR}/musica/fortran ) -if (MUSICA_ENABLE_TUVX) +#include the fortran mod files +install( + DIRECTORY + ${PROJECT_BINARY_DIR}/../include/ + DESTINATION + ${MUSICA_INSTALL_INCLUDE_DIR} + FILES_MATCHING PATTERN "*.mod" +) + +# install MICM +if(MUSICA_ENABLE_MICM) install( TARGETS - tuvx - tuvx_object - yaml-cpp + nlohmann_json + micm EXPORT musica_Exports + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + ) + + install( + DIRECTORY + ${micm_SOURCE_DIR}/include/ + DESTINATION + ${MUSICA_INSTALL_INCLUDE_DIR} ) + install( FILES - ${MUSICA_FORTRAN_SRC_DIR}/tuvx.F90 + ${MUSICA_FORTRAN_SRC_DIR}/micm.F90 DESTINATION ${MUSICA_INSTALL_INCLUDE_DIR}/musica/fortran ) endif() -if (MUSICA_ENABLE_MICM) +# install TUV-x +if (MUSICA_ENABLE_TUVX) install( - DIRECTORY - ${micm_SOURCE_DIR}/include/ + TARGETS + tuvx_object + yaml-cpp + EXPORT + musica_Exports + ) + install( + FILES + ${MUSICA_FORTRAN_SRC_DIR}/tuvx.F90 DESTINATION - ${MUSICA_INSTALL_INCLUDE_DIR}/musica + ${MUSICA_INSTALL_INCLUDE_DIR}/musica/fortran ) endif() @@ -112,7 +138,7 @@ if (MUSICA_CREATE_ENVIRONMENT_MODULE) message(STATUS "MODULE PATH ${MODULE_FILE_PATH}") # Generate the module file using the template - configure_file(${CMAKE_CURRENT_SOURCE_DIR}/modulefile.lua.in + configure_file(${MUSICA_PROJECT_SRC_DIR}/modulefile.lua.in ${MODULE_FILE_PATH} @ONLY) diff --git a/src/tuvx/CMakeLists.txt b/src/tuvx/CMakeLists.txt index 8f16a57e..9df87adb 100644 --- a/src/tuvx/CMakeLists.txt +++ b/src/tuvx/CMakeLists.txt @@ -2,4 +2,5 @@ target_sources(musica PRIVATE interface.F90 tuvx.cpp + tuvx_util.F90 ) \ No newline at end of file diff --git a/src/tuvx/interface.F90 b/src/tuvx/interface.F90 index 35a949e9..a19b20a8 100644 --- a/src/tuvx/interface.F90 +++ b/src/tuvx/interface.F90 @@ -1,15 +1,18 @@ module tuvx_interface - use iso_c_binding, only : c_ptr, c_loc, c_int - use tuvx_core, only : core_t - use musica_util, only : to_f_string, string_t_c - use musica_string, only : string_t + use iso_c_binding, only : c_ptr, c_loc, c_int + use tuvx_core, only : core_t + use musica_tuvx_util, only : to_f_string, string_t_c + use musica_string, only : string_t + implicit none private contains +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + function internal_create_tuvx(config_path, error_code) bind(C, name="internal_create_tuvx") type(string_t_c), value, intent(in) :: config_path integer(kind=c_int), intent(out) :: error_code @@ -28,6 +31,7 @@ function internal_create_tuvx(config_path, error_code) bind(C, name="internal_cr end function internal_create_tuvx +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! subroutine internal_delete_tuvx(tuvx) bind(C, name="internal_delete_tuvx") use iso_c_binding, only: c_ptr, c_f_pointer @@ -40,4 +44,6 @@ subroutine internal_delete_tuvx(tuvx) bind(C, name="internal_delete_tuvx") end if end subroutine internal_delete_tuvx +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + end module tuvx_interface diff --git a/src/tuvx/tuvx_util.F90 b/src/tuvx/tuvx_util.F90 new file mode 100644 index 00000000..79a1fbb0 --- /dev/null +++ b/src/tuvx/tuvx_util.F90 @@ -0,0 +1,384 @@ +! Copyright (C) 2023-2024 National Center for Atmospheric Research +! SPDX-License-Identifier: Apache-2.0 +module musica_tuvx_util + + use iso_c_binding, only: c_char, c_int, c_ptr, c_size_t, & + c_null_ptr, c_f_pointer + + implicit none + private + + public :: string_t_c, string_t, error_t_c, error_t, mapping_t_c, mapping_t, & + to_c_string, to_f_string, assert, copy_mappings + + !> Wrapper for a c string + type, bind(c) :: string_t_c + type(c_ptr) :: ptr_ = c_null_ptr + integer(c_size_t) :: size_ = 0_c_size_t + end type string_t_c + + !> String type + type :: string_t + private + character(len=:), allocatable :: value_ + contains + procedure, private, pass(from) :: char_assign_string + procedure, private, pass(to) :: string_assign_char + procedure, private, pass(to) :: string_assign_string + procedure, private, pass(to) :: string_assign_string_t_c + generic :: assignment(=) => char_assign_string, string_assign_char, & + string_assign_string, string_assign_string_t_c + end type string_t + + interface string_t + module procedure string_t_constructor_from_string_t_c + end interface string_t + + !> Wrapper for an c error condition + type, bind(c) :: error_t_c + integer(c_int) :: code_ = 0_c_int + type(string_t_c) :: category_ + type(string_t_c) :: message_ + end type error_t_c + + !> Error type + type :: error_t + private + integer :: code_ + type(string_t) :: category_ + type(string_t) :: message_ + contains + procedure :: code => error_t_code + procedure :: category => error_t_category + procedure :: message => error_t_message + procedure :: is_success => error_t_is_success + procedure :: is_error => error_t_is_error + end type error_t + + interface error_t + module procedure error_t_constructor + end interface error_t + + !> Wrapper for a c name-to-index mapping + type, bind(c) :: mapping_t_c + type(string_t_c) :: name_ + integer(c_size_t) :: index_ = 0_c_size_t + end type mapping_t_c + + !> Name-to-index mapping + type :: mapping_t + private + type(string_t) :: name_ + integer :: index_ + contains + procedure :: mapping_assign_mapping_t_c + generic :: assignment(=) => mapping_assign_mapping_t_c + procedure :: name => mapping_name + procedure :: index => mapping_index + end type mapping_t + + interface mapping_t + module procedure mapping_constructor + end interface mapping_t + + interface + function create_string_c( string ) bind(c, name="CreateString") + import :: string_t_c, c_char + character(kind=c_char, len=1), intent(in) :: string(*) + type(string_t_c) :: create_string_c + end function create_string_c + subroutine delete_string_c( string ) bind(c, name="DeleteString") + import :: string_t_c + type(string_t_c), intent(inout) :: string + end subroutine delete_string_c + end interface + +contains + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + function string_t_constructor_from_string_t_c( c_string ) result( new_string ) + + type(string_t_c), intent(inout) :: c_string + type(string_t) :: new_string + + new_string%value_ = to_f_string( c_string ) + call delete_string_c( c_string ) + + end function string_t_constructor_from_string_t_c + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Get the a string_t as a fortran string + subroutine char_assign_string( to, from ) + + character(len=:), allocatable, intent(inout) :: to + class(string_t), intent(in) :: from + + if (allocated(to)) deallocate(to) + if (.not. allocated(from%value_)) then + allocate( character( len = 0 ) :: to ) + return + end if + allocate( to, source = from%value_ ) + + end subroutine char_assign_string + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Assign a string to a string_t object + subroutine string_assign_char( to, from ) + + class(string_t), intent(inout) :: to + character(len=*), intent(in) :: from + + if (allocated(to%value_)) deallocate(to%value_) + allocate( to%value_, source = from ) + + end subroutine string_assign_char + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Copy a string + subroutine string_assign_string( to, from ) + + class(string_t), intent(inout) :: to + class(string_t), intent(in) :: from + + if (allocated(to%value_)) deallocate(to%value_) + allocate( to%value_, source = from%value_ ) + + end subroutine string_assign_string + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Copy a c string to a string_t + subroutine string_assign_string_t_c( to, from ) + + class(string_t), intent(inout) :: to + type(string_t_c), intent(in) :: from + + to%value_ = to_f_string( from ) + + end subroutine string_assign_string_t_c + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Constructor for an error_t object that takes ownership of an error_t_c + function error_t_constructor( c_error ) result( new_error ) + + type(error_t_c), intent(inout) :: c_error + type(error_t) :: new_error + + new_error%code_ = int( c_error%code_ ) + new_error%category_ = string_t( c_error%category_ ) + new_error%message_ = string_t( c_error%message_ ) + + end function error_t_constructor + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Get the error code + function error_t_code( this ) result( code ) + + class(error_t), intent(in) :: this + integer :: code + + code = this%code_ + + end function error_t_code + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Get the error category + function error_t_category( this ) result( category ) + + class(error_t), intent(in) :: this + character(len=:), allocatable :: category + + category = this%category_ + + end function error_t_category + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Get the error message + function error_t_message( this ) result( message ) + + class(error_t), intent(in) :: this + character(len=:), allocatable :: message + + message = this%message_ + + end function error_t_message + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Check if an error_t_c object represents a success condition + logical function error_t_is_success( this ) result( is_success ) + + class(error_t), intent(in) :: this + + is_success = this%code_ == 0 + + end function error_t_is_success + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Check if an error_t_c object matches a specific error condition + logical function error_t_is_error( this, category, code ) result( is_error ) + + class(error_t), intent(in) :: this + character(len=*), intent(in) :: category + integer, intent(in) :: code + + character(len=:), allocatable :: category_f + + category_f = this%category_ + is_error = int( this%code_ ) == code .and. & + category_f == category + + end function error_t_is_error + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Constructor for a mapping_t object that takes ownership of a mapping_t_c + !! object + function mapping_constructor( c_mapping ) result( new_mapping ) + + type(mapping_t_c), intent(inout) :: c_mapping + type(mapping_t) :: new_mapping + + new_mapping%name_ = string_t( c_mapping%name_ ) + new_mapping%index_ = int( c_mapping%index_ ) + + end function mapping_constructor + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Copies a mapping_t_c object to a mapping_t object + subroutine mapping_assign_mapping_t_c( to, from ) + + class(mapping_t), intent(inout) :: to + type(mapping_t_c), intent(in) :: from + + to%name_ = from%name_ + to%index_ = int( from%index_ ) + + end subroutine mapping_assign_mapping_t_c + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Get the name for a mapping_t object + function mapping_name( this ) result( name ) + + class(mapping_t), intent(in) :: this + character(len=:), allocatable :: name + + name = this%name_ + + end function mapping_name + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Get the index for a mapping_t object + function mapping_index( this ) result( index ) + + class(mapping_t), intent(in) :: this + integer :: index + + index = this%index_ + + end function mapping_index + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Copies mappings from a c array to a fortran array + function copy_mappings( c_mappings, n_mappings ) result( f_mappings ) + + type(c_ptr), intent(in) :: c_mappings + integer(c_size_t), intent(in) :: n_mappings + type(mapping_t), allocatable :: f_mappings(:) + + integer :: i + type(mapping_t_c), pointer :: mappings(:) + + call c_f_pointer( c_mappings, mappings, [ n_mappings ] ) + allocate( f_mappings( n_mappings ) ) + do i = 1, n_mappings + f_mappings(i) = mappings(i) + end do + + end function copy_mappings + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Convert a fortran character array to a c string + function to_c_string( f_string ) result( c_string ) + + use iso_c_binding, only : c_char, c_null_char + + character(len=*), intent(in) :: f_string + character(len=1, kind=c_char), allocatable :: c_string(:) + + integer :: N, i + + N = len_trim( f_string ) + allocate( c_string( N + 1 ) ) + do i = 1, N + c_string(i) = f_string(i:i) + end do + c_string(N + 1) = c_null_char + + end function to_c_string + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Convert a c string to a fortran character array + function to_f_string( c_string ) result( f_string ) + + use iso_c_binding, only : c_char, c_ptr, c_f_pointer, & + c_associated + + type(string_t_c), value, intent(in) :: c_string + character(len=:), allocatable :: f_string + + integer :: i + character(len=1, kind=c_char), pointer :: c_char_array(:) + + if ( .not. c_associated( c_string%ptr_ ) ) then + allocate( character( len = 0 ) :: f_string ) + return + end if + call c_f_pointer( c_string%ptr_, c_char_array, [ c_string%size_ + 1 ] ) + allocate( character( len = c_string%size_ ) :: f_string ) + do i = 1, c_string%size_ + f_string(i:i) = c_char_array(i) + end do + + end function to_f_string + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + !> Assert that a condition is true + subroutine assert( condition, file, line, error_message ) + + logical, intent(in) :: condition + character(len=*), intent(in) :: file + integer, intent(in) :: line + character(len=*), intent(in), optional :: error_message + + if ( .not. condition ) then + if ( present( error_message ) ) then + write(*,*) "[MUSICA ERROR at ", file, ":", line, "] ", error_message + else + write(*,*) "[MUSICA ERROR at ", file, ":", line, "] Assertion failed" + end if + stop 3 + end if + + end subroutine assert + +!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +end module musica_tuvx_util \ No newline at end of file