From bc68752425ec17b352b095d3ec93371391a26765 Mon Sep 17 00:00:00 2001 From: rem1776 Date: Mon, 1 Jun 2026 10:35:28 -0400 Subject: [PATCH 1/6] add initial cmakelists file for coupler compilation:wq --- CMakeLists.txt | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100644 CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..4d3f3d74 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,143 @@ +cmake_minimum_required(VERSION 3.15) +project(fmscoupler LANGUAGES Fortran) + +include(FetchContent) + +# define some file locations +set(EXTERNAL_SOURCE_DIR ${CMAKE_SOURCE_DIR}/external) +file(MAKE_DIRECTORY ${EXTERNAL_SOURCE_DIR}) + +# get FMS source, already has cmake so no extra steps needed +FetchContent_Declare( + FMS + GIT_REPOSITORY https://github.com/NOAA-GFDL/FMS.git + GIT_TAG main + SOURCE_DIR ${EXTERNAL_SOURCE_DIR}/FMS +) +FetchContent_MakeAvailable(FMS) +message(STATUS "FMS clone complete. FMS source is available at ${FMS_SOURCE_DIR}") + +# set variables for component repos +set(COMPONENTS + ocean_null + atmos_null + land_null + ice_param + ice_null +) +set(ocean_null_URL https://github.com/NOAA-GFDL/ocean_null.git) +set(atmos_null_URL https://github.com/NOAA-GFDL/atmos_null.git) +set(land_null_URL https://github.com/NOAA-GFDL/land_null.git) +set(ice_param_URL https://github.com/NOAA-GFDL/ice_param.git) +set(ice_null_URL https://github.com/NOAA-GFDL/ice_null.git) + +# clone each component repo +foreach(component_name IN LISTS COMPONENTS) + set(component_url ${${component_name}_URL}) + FetchContent_Declare( + ${component_name} + GIT_REPOSITORY ${component_url} + SOURCE_DIR ${EXTERNAL_SOURCE_DIR}/${component_name} + GIT_TAG main + ) + FetchContent_MakeAvailable(${component_name}) +endforeach() + +# Build libraries for each external component +foreach(component_name IN LISTS COMPONENTS) + file(GLOB_RECURSE component_srcs + ${EXTERNAL_SOURCE_DIR}/${component_name}/*.F90 + ) + add_library(${component_name} STATIC ${component_srcs}) + target_include_directories(${component_name} PUBLIC ${EXTERNAL_SOURCE_DIR}/FMS) + target_link_libraries(${component_name} PUBLIC FMS::fms) + target_compile_definitions(${component_name} PUBLIC INTERNAL_FILE_NML) + set_target_properties(${component_name} PROPERTIES COMPILE_FLAGS "-fdefault-real-8") +endforeach() + +# ice_null has additional dependencies instead of just fms +target_link_libraries(ice_null PRIVATE FMS::fms ocean_null ice_param) + +# components are done, move onto the actual coupler code +# 2 libraries: modules from shared + full, and modules from shared + simple (might be able to combine into 1 library if there are no conflicts, but for now keep them separate) +# 2 executables: full/coupler_main + simple/coupler_main + +# define our source files, splits up modules and executables +set(SHARED_MOD_SRCS ${CMAKE_SOURCE_DIR}/shared/surface_flux.F90) +set(SIMPLE_MOD_SRCS + ${CMAKE_SOURCE_DIR}/simple/flux_exchange.F90 + ${CMAKE_SOURCE_DIR}/simple/ice_model.F90 +) +set(FULL_MOD_SRCS + ${CMAKE_SOURCE_DIR}/full/atm_land_ice_flux_exchange.F90 + ${CMAKE_SOURCE_DIR}/full/atmos_ocean_dep_fluxes_calc.F90 + ${CMAKE_SOURCE_DIR}/full/atmos_ocean_fluxes_calc.F90 + ${CMAKE_SOURCE_DIR}/full/flux_exchange.F90 + ${CMAKE_SOURCE_DIR}/full/full_coupler_mod.F90 + ${CMAKE_SOURCE_DIR}/full/ice_ocean_flux_exchange.F90 + ${CMAKE_SOURCE_DIR}/full/land_ice_flux_exchange.F90 +) +set(SIMPLE_EXEC_SRC ${CMAKE_SOURCE_DIR}/simple/coupler_main.F90) +set(FULL_EXEC_SRC ${CMAKE_SOURCE_DIR}/full/coupler_main.F90) + +add_library(coupler_shared_simple STATIC ${SIMPLE_MOD_SRCS} ${SHARED_MOD_SRCS}) +# simple and full use the same module names, so we need to set explicit .mod output directories and include them +set_target_properties(coupler_shared_simple PROPERTIES + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.mod_simple" +) +target_include_directories(coupler_shared_simple PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/.mod_simple) +set_target_properties(coupler_shared_simple PROPERTIES COMPILE_FLAGS "-fdefault-real-8") +target_compile_definitions(coupler_shared_simple PUBLIC use_AM3_physics _USE_LEGACY_LAND_) +target_link_libraries(coupler_shared_simple PRIVATE + FMS::fms + ocean_null + atmos_null + land_null + ice_param +) + +add_library(coupler_shared_full STATIC ${FULL_MOD_SRCS} ${SHARED_MOD_SRCS}) +set_target_properties(coupler_shared_full PROPERTIES + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.mod_full" +) +set_target_properties(coupler_shared_full PROPERTIES COMPILE_FLAGS "-fdefault-real-8") +target_compile_definitions(coupler_shared_full PUBLIC use_AM3_physics _USE_LEGACY_LAND_) +target_include_directories(coupler_shared_full PUBLIC + ${CMAKE_SOURCE_DIR}/full + ${CMAKE_CURRENT_BINARY_DIR}/.mod_full +) +target_link_libraries(coupler_shared_full PRIVATE + FMS::fms + ocean_null + atmos_null + land_null + ice_param + ice_null +) + +add_executable(coupler_simple ${COUPLER_SIMPLE_SRCS} ${CMAKE_SOURCE_DIR}/simple/coupler_main.F90) +set_target_properties(coupler_simple PROPERTIES COMPILE_FLAGS "-fdefault-real-8") +target_include_directories(coupler_simple PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/.mod_simple) +target_link_libraries(coupler_simple PRIVATE + coupler_simple_shared + FMS::fms + ocean_null + atmos_null + land_null + ice_param +) +set_target_properties(coupler_simple PROPERTIES RUNTIME_OUTPUT_NAME "coupler_simple.x") + +add_executable(coupler_full ${COUPLER_FULL_SRCS} ${CMAKE_SOURCE_DIR}/full/coupler_main.F90) +set_target_properties(coupler_full PROPERTIES COMPILE_FLAGS "-fdefault-real-8") +target_include_directories(coupler_full PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/.mod_full) +target_link_libraries(coupler_full PRIVATE + coupler_shared_full + FMS::fms + ocean_null + atmos_null + land_null + ice_param + ice_null +) +set_target_properties(coupler_full PROPERTIES RUNTIME_OUTPUT_NAME "coupler_full.x") From e8dfc0286a007b1541ee1602c8e8a9bb65134f06 Mon Sep 17 00:00:00 2001 From: rem1776 Date: Mon, 1 Jun 2026 11:57:45 -0400 Subject: [PATCH 2/6] fix issues with mod directories, working null model build --- CMakeLists.txt | 76 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4d3f3d74..1b292ae4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,3 +1,23 @@ +#*********************************************************************** +#* GNU Lesser General Public License +#* +#* This file is part of the GFDL Flexible Modeling System (FMS) Coupler. +#* +#* FMS Coupler is free software: you can redistribute it and/or modify +#* it under the terms of the GNU Lesser General Public License as +#* published by the Free Software Foundation, either version 3 of the +#* License, or (at your option) any later version. +#* +#* FMS Coupler 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 Lesser General Public +#* License along with FMS Coupler. +#* If not, see . +#*********************************************************************** + cmake_minimum_required(VERSION 3.15) project(fmscoupler LANGUAGES Fortran) @@ -53,16 +73,19 @@ foreach(component_name IN LISTS COMPONENTS) target_link_libraries(${component_name} PUBLIC FMS::fms) target_compile_definitions(${component_name} PUBLIC INTERNAL_FILE_NML) set_target_properties(${component_name} PROPERTIES COMPILE_FLAGS "-fdefault-real-8") + set_target_properties(${component_name} PROPERTIES + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${component_name}_mods" + ) endforeach() # ice_null has additional dependencies instead of just fms target_link_libraries(ice_null PRIVATE FMS::fms ocean_null ice_param) +target_include_directories(ice_null PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods" + "${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods" +) -# components are done, move onto the actual coupler code -# 2 libraries: modules from shared + full, and modules from shared + simple (might be able to combine into 1 library if there are no conflicts, but for now keep them separate) -# 2 executables: full/coupler_main + simple/coupler_main - -# define our source files, splits up modules and executables +# set some paths set(SHARED_MOD_SRCS ${CMAKE_SOURCE_DIR}/shared/surface_flux.F90) set(SIMPLE_MOD_SRCS ${CMAKE_SOURCE_DIR}/simple/flux_exchange.F90 @@ -80,14 +103,19 @@ set(FULL_MOD_SRCS set(SIMPLE_EXEC_SRC ${CMAKE_SOURCE_DIR}/simple/coupler_main.F90) set(FULL_EXEC_SRC ${CMAKE_SOURCE_DIR}/full/coupler_main.F90) +# library for simple coupler modules + shared add_library(coupler_shared_simple STATIC ${SIMPLE_MOD_SRCS} ${SHARED_MOD_SRCS}) -# simple and full use the same module names, so we need to set explicit .mod output directories and include them set_target_properties(coupler_shared_simple PROPERTIES - Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.mod_simple" + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/simple_coupler_mods" ) -target_include_directories(coupler_shared_simple PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/.mod_simple) set_target_properties(coupler_shared_simple PROPERTIES COMPILE_FLAGS "-fdefault-real-8") target_compile_definitions(coupler_shared_simple PUBLIC use_AM3_physics _USE_LEGACY_LAND_) +target_include_directories(coupler_shared_simple PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/atmos_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/land_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods +) target_link_libraries(coupler_shared_simple PRIVATE FMS::fms ocean_null @@ -96,15 +124,20 @@ target_link_libraries(coupler_shared_simple PRIVATE ice_param ) +# library for full coupler modules + shared add_library(coupler_shared_full STATIC ${FULL_MOD_SRCS} ${SHARED_MOD_SRCS}) set_target_properties(coupler_shared_full PROPERTIES - Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/.mod_full" + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/full_coupler_mods" ) set_target_properties(coupler_shared_full PROPERTIES COMPILE_FLAGS "-fdefault-real-8") target_compile_definitions(coupler_shared_full PUBLIC use_AM3_physics _USE_LEGACY_LAND_) target_include_directories(coupler_shared_full PUBLIC ${CMAKE_SOURCE_DIR}/full - ${CMAKE_CURRENT_BINARY_DIR}/.mod_full + ${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/atmos_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/land_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_null_mods ) target_link_libraries(coupler_shared_full PRIVATE FMS::fms @@ -115,11 +148,19 @@ target_link_libraries(coupler_shared_full PRIVATE ice_null ) + +# simple coupler executable add_executable(coupler_simple ${COUPLER_SIMPLE_SRCS} ${CMAKE_SOURCE_DIR}/simple/coupler_main.F90) set_target_properties(coupler_simple PROPERTIES COMPILE_FLAGS "-fdefault-real-8") -target_include_directories(coupler_simple PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/.mod_simple) +target_include_directories(coupler_simple PUBLIC + ${CMAKE_CURRENT_BINARY_DIR}/simple_coupler_mods + ${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/atmos_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/land_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods +) target_link_libraries(coupler_simple PRIVATE - coupler_simple_shared + coupler_shared_simple FMS::fms ocean_null atmos_null @@ -128,9 +169,18 @@ target_link_libraries(coupler_simple PRIVATE ) set_target_properties(coupler_simple PROPERTIES RUNTIME_OUTPUT_NAME "coupler_simple.x") +# full coupler executable add_executable(coupler_full ${COUPLER_FULL_SRCS} ${CMAKE_SOURCE_DIR}/full/coupler_main.F90) set_target_properties(coupler_full PROPERTIES COMPILE_FLAGS "-fdefault-real-8") -target_include_directories(coupler_full PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/.mod_full) +target_include_directories(coupler_full PUBLIC + ${CMAKE_SOURCE_DIR}/full + ${CMAKE_CURRENT_BINARY_DIR}/full_coupler_mods + ${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/atmos_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/land_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_null_mods +) target_link_libraries(coupler_full PRIVATE coupler_shared_full FMS::fms From 87a86dd4758315f9799fdea425a3324339ddb846 Mon Sep 17 00:00:00 2001 From: rem1776 Date: Mon, 1 Jun 2026 12:41:23 -0400 Subject: [PATCH 3/6] use more generic names for components, add test script --- CMakeLists.txt | 81 ++++++++++++++++++++++--------------------- t/null_model_cmake.sh | 66 +++++++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 40 deletions(-) create mode 100755 t/null_model_cmake.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b292ae4..0d56970d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,21 +35,21 @@ FetchContent_Declare( SOURCE_DIR ${EXTERNAL_SOURCE_DIR}/FMS ) FetchContent_MakeAvailable(FMS) -message(STATUS "FMS clone complete. FMS source is available at ${FMS_SOURCE_DIR}") +message(STATUS "FMS cloned to ${FMS_SOURCE_DIR}") # set variables for component repos set(COMPONENTS - ocean_null - atmos_null - land_null + ocean + atmos + land ice_param - ice_null + ice ) -set(ocean_null_URL https://github.com/NOAA-GFDL/ocean_null.git) -set(atmos_null_URL https://github.com/NOAA-GFDL/atmos_null.git) -set(land_null_URL https://github.com/NOAA-GFDL/land_null.git) +set(ocean_URL https://github.com/NOAA-GFDL/ocean_null.git) +set(atmos_URL https://github.com/NOAA-GFDL/atmos_null.git) +set(land_URL https://github.com/NOAA-GFDL/land_null.git) set(ice_param_URL https://github.com/NOAA-GFDL/ice_param.git) -set(ice_null_URL https://github.com/NOAA-GFDL/ice_null.git) +set(ice_URL https://github.com/NOAA-GFDL/ice_null.git) # clone each component repo foreach(component_name IN LISTS COMPONENTS) @@ -61,6 +61,7 @@ foreach(component_name IN LISTS COMPONENTS) GIT_TAG main ) FetchContent_MakeAvailable(${component_name}) + message(STATUS "Cloning ${${component_name}_URL} to ${EXTERNAL_SOURCE_DIR}/${component_name}") endforeach() # Build libraries for each external component @@ -79,9 +80,9 @@ foreach(component_name IN LISTS COMPONENTS) endforeach() # ice_null has additional dependencies instead of just fms -target_link_libraries(ice_null PRIVATE FMS::fms ocean_null ice_param) -target_include_directories(ice_null PRIVATE - "${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods" +target_link_libraries(ice PRIVATE FMS::fms ocean ice_param) +target_include_directories(ice PRIVATE + "${CMAKE_CURRENT_BINARY_DIR}/ocean_mods" "${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods" ) @@ -111,16 +112,16 @@ set_target_properties(coupler_shared_simple PROPERTIES set_target_properties(coupler_shared_simple PROPERTIES COMPILE_FLAGS "-fdefault-real-8") target_compile_definitions(coupler_shared_simple PUBLIC use_AM3_physics _USE_LEGACY_LAND_) target_include_directories(coupler_shared_simple PUBLIC - ${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods - ${CMAKE_CURRENT_BINARY_DIR}/atmos_null_mods - ${CMAKE_CURRENT_BINARY_DIR}/land_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ocean_mods + ${CMAKE_CURRENT_BINARY_DIR}/atmos_mods + ${CMAKE_CURRENT_BINARY_DIR}/land_mods ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods ) target_link_libraries(coupler_shared_simple PRIVATE FMS::fms - ocean_null - atmos_null - land_null + ocean + atmos + land ice_param ) @@ -133,19 +134,19 @@ set_target_properties(coupler_shared_full PROPERTIES COMPILE_FLAGS "-fdefault-re target_compile_definitions(coupler_shared_full PUBLIC use_AM3_physics _USE_LEGACY_LAND_) target_include_directories(coupler_shared_full PUBLIC ${CMAKE_SOURCE_DIR}/full - ${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods - ${CMAKE_CURRENT_BINARY_DIR}/atmos_null_mods - ${CMAKE_CURRENT_BINARY_DIR}/land_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ocean_mods + ${CMAKE_CURRENT_BINARY_DIR}/atmos_mods + ${CMAKE_CURRENT_BINARY_DIR}/land_mods ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods - ${CMAKE_CURRENT_BINARY_DIR}/ice_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_mods ) target_link_libraries(coupler_shared_full PRIVATE FMS::fms - ocean_null - atmos_null - land_null + ocean + atmos + land ice_param - ice_null + ice ) @@ -154,17 +155,17 @@ add_executable(coupler_simple ${COUPLER_SIMPLE_SRCS} ${CMAKE_SOURCE_DIR}/simple/ set_target_properties(coupler_simple PROPERTIES COMPILE_FLAGS "-fdefault-real-8") target_include_directories(coupler_simple PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/simple_coupler_mods - ${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods - ${CMAKE_CURRENT_BINARY_DIR}/atmos_null_mods - ${CMAKE_CURRENT_BINARY_DIR}/land_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ocean_mods + ${CMAKE_CURRENT_BINARY_DIR}/atmos_mods + ${CMAKE_CURRENT_BINARY_DIR}/land_mods ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods ) target_link_libraries(coupler_simple PRIVATE coupler_shared_simple FMS::fms - ocean_null - atmos_null - land_null + ocean + atmos + land ice_param ) set_target_properties(coupler_simple PROPERTIES RUNTIME_OUTPUT_NAME "coupler_simple.x") @@ -175,19 +176,19 @@ set_target_properties(coupler_full PROPERTIES COMPILE_FLAGS "-fdefault-real-8") target_include_directories(coupler_full PUBLIC ${CMAKE_SOURCE_DIR}/full ${CMAKE_CURRENT_BINARY_DIR}/full_coupler_mods - ${CMAKE_CURRENT_BINARY_DIR}/ocean_null_mods - ${CMAKE_CURRENT_BINARY_DIR}/atmos_null_mods - ${CMAKE_CURRENT_BINARY_DIR}/land_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ocean_mods + ${CMAKE_CURRENT_BINARY_DIR}/atmos_mods + ${CMAKE_CURRENT_BINARY_DIR}/land_mods ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods - ${CMAKE_CURRENT_BINARY_DIR}/ice_null_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_mods ) target_link_libraries(coupler_full PRIVATE coupler_shared_full FMS::fms - ocean_null - atmos_null - land_null + ocean + atmos + land ice_param - ice_null + ice ) set_target_properties(coupler_full PROPERTIES RUNTIME_OUTPUT_NAME "coupler_full.x") diff --git a/t/null_model_cmake.sh b/t/null_model_cmake.sh new file mode 100755 index 00000000..3f5bcef8 --- /dev/null +++ b/t/null_model_cmake.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env sh +# +# Script to build a GFDL null model, using all null components, and run +# a simple test on CI systems, like Travis CI or gitlab CI. + +script_root=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) +bld_dir=$(mktemp --directory $script_root/null.XXXXXX) +cd $bld_dir +cmake -DCMAKE_BUILD_TYPE=Debug ../.. +make -j +make + +# Report on the status of the build +if [ $? -eq 0 ] +then + echo "::note title=Build Succeeded:: null model with simple coupler built successfully." +else + echo "::error title=Build Failed:: null model with simple coupler failed compilation." + exit 1 +fi + +# Run the null models test +# Setup the run directory +mkdir ${bld_dir}/run +cd ${bld_dir}/run +mkdir RESTART +# Get the data files required for the run +tarFile=coupler_null_test_data_full_simple.tar.gz +curl -O ftp://ftp.gfdl.noaa.gov/perm/GFDL_pubrelease/test_data/${tarFile} +tar zxf ${tarFile} + +# add an io layout to the full nml +sed -i '22i io_layout = 1, 1' input-full.nml + +# Get the full namelist +ln -s input-full.nml input.nml +# Run the null model with the full coupler +mpiexec -n 1 ${bld_dir}/coupler_full.x + +# Report on the status of the run with the full coupler +if [ $? -eq 0 ] +then + echo "::note title=Run Succeeded - full coupler:: Full coupler null model ran successfully." +else + echo "::error title=Run Failed - full coupler:: Full coupler null model run failed execution." + exit 1 +fi + +# Using the same run directory, setup for the simple coupler +# Clear out the RESTART directory +mv RESTART RESTART_full +mkdir RESTART +# Get the simple namelist +rm input.nml +ln -s input-simple.nml input.nml +# Run the null simple coupler test +mpiexec -n 1 ${bld_dir}/coupler_simple.x + +# Report on the status of the run with the simple coupler +if [ $? -eq 0 ] +then + echo "::note title=Run Succeeded - simple coupler:: simple coupler null model ran successfully" +else + echo "::error title=Run Failed - simple coupler:: simple coupler null model run failed execution." + exit 1 +fi From b4753fcf346fec9bc7c56f5d6534693a43b636a5 Mon Sep 17 00:00:00 2001 From: rem1776 Date: Wed, 3 Jun 2026 12:55:20 -0400 Subject: [PATCH 4/6] fix locally cloned builds, add test script --- CMakeLists.txt | 130 ++++++++++++++++++++++++++++++++++--------------- README.md | 43 ++++++++++++++++ 2 files changed, 134 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d56970d..3c2c6c79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,52 +23,102 @@ project(fmscoupler LANGUAGES Fortran) include(FetchContent) -# define some file locations +# define some file locations set(EXTERNAL_SOURCE_DIR ${CMAKE_SOURCE_DIR}/external) file(MAKE_DIRECTORY ${EXTERNAL_SOURCE_DIR}) -# get FMS source, already has cmake so no extra steps needed -FetchContent_Declare( - FMS - GIT_REPOSITORY https://github.com/NOAA-GFDL/FMS.git - GIT_TAG main - SOURCE_DIR ${EXTERNAL_SOURCE_DIR}/FMS -) -FetchContent_MakeAvailable(FMS) -message(STATUS "FMS cloned to ${FMS_SOURCE_DIR}") +# Options for local dependencies +set(fms_path "${CMAKE_SOURCE_DIR}/../FMS" CACHE STRING "Path to local fms repository") +set(atmos_path "${CMAKE_SOURCE_DIR}/../atmos_null" CACHE STRING "Path to local atmospere component repository") +set(ocean_path "${CMAKE_SOURCE_DIR}/../ocean_null" CACHE STRING "Path to local ocean component repository") +set(land_path "${CMAKE_SOURCE_DIR}/../land_null" CACHE STRING "Path to local land component repository") +set(ice_path "${CMAKE_SOURCE_DIR}/../ice_null" CACHE STRING "Path to local ice component repository") +set(ice_param_path "${CMAKE_SOURCE_DIR}/../ice_param" CACHE STRING "Path to local ice_param component repository") + +# Options for cloning depedencies +option(FETCH_FMS "Clone FMS repository for building" OFF) +option(FETCH_COMPONENTS "Clone model component repositories for building" OFF) +set(fms_tag "main" CACHE STRING "Branch/Tag name for FMS. Only used if -DFETCH_FMS=on") +set(atmos_tag "main" CACHE STRING "Branch/Tag name for atmosphere component. Only used if -DFETCH_COMPONENTS=on") +set(ocean_tag "main" CACHE STRING "Branch/Tag name for ocean component. Only used if -DFETCH_COMPONENTS=on") +set(land_tag "main" CACHE STRING "Branch/Tag name for land component. Only used if -DFETCH_COMPONENTS=on") +set(ice_tag "main" CACHE STRING "Branch/Tag name for ice component. Only used if -DFETCH_COMPONENTS=on") +set(ice_param_tag "main" CACHE STRING "Branch/Tag name for ice_param component. Only used if -DFETCH_COMPONENTS=on") +set(fms_repo "https://github.com/noaa-gfdl/fms.git" + CACHE STRING "Repository URL for FMS. Only used if -DFETCH_FMS=on") +set(atmos_repo "https://github.com/noaa-gfdl/atmos_null.git" + CACHE STRING "Repository URL for atmosphere component. Only used if -DFETCH_COMPONENTS=on") +set(ocean_repo "https://github.com/noaa-gfdl/ocean_null.git" + CACHE STRING "Repository URL for ocean component. Only used if -DFETCH_COMPONENTS=on") +set(land_repo "https://github.com/noaa-gfdl/land_null.git" + CACHE STRING "Repository URL for land component. Only used if -DFETCH_COMPONENTS=on") +set(ice_repo "https://github.com/noaa-gfdl/ice_null.git" + CACHE STRING "Repository URL for ice component. Only used if -DFETCH_COMPONENTS=on") +set(ice_param_repo "https://github.com/noaa-gfdl/ice_param.git" + CACHE STRING "Repository URL for ice_param component. Only used if -DFETCH_COMPONENTS=on") + +# TODO some more error checking here to ensure fetching/local options make sense +if(NOT FETCH_FMS) + message(NOTICE "-DFETCH_FMS=off - expecting local FMS repository at ${fms_path}.") +endif() + +if(NOT FETCH_COMPONENTS) + message(NOTICE + "-DFETCH_COMPONENTS=off - expecting components locally available in ${CMAKE_SOURCE_DIR}/.. if path not specified" + ) +endif() + +# build FMS, cloning if -DFETCH_FMS=on, otherwise expects a local path provided +if(FETCH_FMS) + FetchContent_Declare( + FMS + GIT_REPOSITORY ${fms_repo} + GIT_TAG ${fms_tag} + SOURCE_DIR ${EXTERNAL_SOURCE_DIR}/FMS + ) + FetchContent_MakeAvailable(FMS) + message(STATUS "FMS cloned to ${FMS_SOURCE_DIR}") +else() + add_subdirectory(${fms_path} ${CMAKE_CURRENT_BINARY_DIR}/fms_build) +endif() -# set variables for component repos +# set variables for component repos +# ice_param isn't a direct dependency of the coupler, but is almost always needed by +# whatever ice_model you end up using so is included here set(COMPONENTS ocean atmos land - ice_param ice + ice_param ) -set(ocean_URL https://github.com/NOAA-GFDL/ocean_null.git) -set(atmos_URL https://github.com/NOAA-GFDL/atmos_null.git) -set(land_URL https://github.com/NOAA-GFDL/land_null.git) -set(ice_param_URL https://github.com/NOAA-GFDL/ice_param.git) -set(ice_URL https://github.com/NOAA-GFDL/ice_null.git) - -# clone each component repo +# clone or symlink each component repo foreach(component_name IN LISTS COMPONENTS) - set(component_url ${${component_name}_URL}) - FetchContent_Declare( - ${component_name} - GIT_REPOSITORY ${component_url} - SOURCE_DIR ${EXTERNAL_SOURCE_DIR}/${component_name} - GIT_TAG main - ) - FetchContent_MakeAvailable(${component_name}) - message(STATUS "Cloning ${${component_name}_URL} to ${EXTERNAL_SOURCE_DIR}/${component_name}") + if(FETCH_COMPONENTS) + set(component_url ${${component_name}_repo}) + message(STATUS "Cloning ${component_url} to ${EXTERNAL_SOURCE_DIR}/${component_name}") + FetchContent_Declare( + ${component_name} + GIT_REPOSITORY ${component_url} + SOURCE_DIR ${EXTERNAL_SOURCE_DIR}/${component_name} + GIT_TAG main + ) + FetchContent_MakeAvailable(${component_name}) + else() + set(component_path ${${component_name}_path} ) + message(STATUS "Creating symlink in ${EXTERNAL_SOURCE_DIR} for ${component_path}") + file(CREATE_LINK + "${component_path}" + "${EXTERNAL_SOURCE_DIR}/${component_name}" + SYMBOLIC + ) + endif() endforeach() # Build libraries for each external component foreach(component_name IN LISTS COMPONENTS) - file(GLOB_RECURSE component_srcs - ${EXTERNAL_SOURCE_DIR}/${component_name}/*.F90 - ) + file(GLOB_RECURSE component_srcs ${EXTERNAL_SOURCE_DIR}/${component_name}/*.F90) + # TODO if/when cmake is added to component libraries, this should use add_subdirectory instead add_library(${component_name} STATIC ${component_srcs}) target_include_directories(${component_name} PUBLIC ${EXTERNAL_SOURCE_DIR}/FMS) target_link_libraries(${component_name} PUBLIC FMS::fms) @@ -137,28 +187,27 @@ target_include_directories(coupler_shared_full PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/ocean_mods ${CMAKE_CURRENT_BINARY_DIR}/atmos_mods ${CMAKE_CURRENT_BINARY_DIR}/land_mods - ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods ${CMAKE_CURRENT_BINARY_DIR}/ice_mods + ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods ) target_link_libraries(coupler_shared_full PRIVATE FMS::fms ocean atmos land - ice_param ice + ice_param ) # simple coupler executable -add_executable(coupler_simple ${COUPLER_SIMPLE_SRCS} ${CMAKE_SOURCE_DIR}/simple/coupler_main.F90) +add_executable(coupler_simple ${CMAKE_SOURCE_DIR}/simple/coupler_main.F90) set_target_properties(coupler_simple PROPERTIES COMPILE_FLAGS "-fdefault-real-8") target_include_directories(coupler_simple PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/simple_coupler_mods ${CMAKE_CURRENT_BINARY_DIR}/ocean_mods ${CMAKE_CURRENT_BINARY_DIR}/atmos_mods ${CMAKE_CURRENT_BINARY_DIR}/land_mods - ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods ) target_link_libraries(coupler_simple PRIVATE coupler_shared_simple @@ -166,12 +215,11 @@ target_link_libraries(coupler_simple PRIVATE ocean atmos land - ice_param ) set_target_properties(coupler_simple PROPERTIES RUNTIME_OUTPUT_NAME "coupler_simple.x") # full coupler executable -add_executable(coupler_full ${COUPLER_FULL_SRCS} ${CMAKE_SOURCE_DIR}/full/coupler_main.F90) +add_executable(coupler_full ${CMAKE_SOURCE_DIR}/full/coupler_main.F90) set_target_properties(coupler_full PROPERTIES COMPILE_FLAGS "-fdefault-real-8") target_include_directories(coupler_full PUBLIC ${CMAKE_SOURCE_DIR}/full @@ -179,7 +227,6 @@ target_include_directories(coupler_full PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/ocean_mods ${CMAKE_CURRENT_BINARY_DIR}/atmos_mods ${CMAKE_CURRENT_BINARY_DIR}/land_mods - ${CMAKE_CURRENT_BINARY_DIR}/ice_param_mods ${CMAKE_CURRENT_BINARY_DIR}/ice_mods ) target_link_libraries(coupler_full PRIVATE @@ -188,7 +235,12 @@ target_link_libraries(coupler_full PRIVATE ocean atmos land - ice_param ice ) set_target_properties(coupler_full PROPERTIES RUNTIME_OUTPUT_NAME "coupler_full.x") + +# simple test run, only intended for null_model builds +enable_testing() + +add_test(NAME null_model_run + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/t/test_run.sh) diff --git a/README.md b/README.md index 96d8f02c..6c4a41de 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,49 @@ The coupling between the models is designed to conserve fluxes. For coupled mode specification file is used to initialize the model grids and perform exchanges between the models. The next sections describe how this file and associated grids are used in the coupler. +### Software Structure and Build System + +The FMScoupler repository contains 2 distinct "driver" programs, "full" and "simple", which serve as the executable +program to run GFDL developed climate models. It also contains a collection of helper modules that define routines used +in the drivers, which are split between the shared/ and the full/simple subdirectories. + +The "full" coupler has 5 direct dependencies: FMS[github.com/noaa-gfdl/fms], "ice_model_mod", "land_model_mod", +"atmos_model_mod", "ocean_model_mod". The "simple" coupler differs in that in does not require a ocean model, and +includes its own ice_model module, rather than using an external repository. "ice_param" is a dependency of the simple +ice_model, so is also included in the build. It is commonly used by GFDL ice models as well, so will be linked with +the ice component by default. + +The component modules can be provided by a number of different repositories that define the +physical calculations performed in each component. + +A cmake build is available to build the coupler and component libraries. It is currently only tested with the null model +and supports either locally cloned repositories or automagically cloning components during configuration. + +To build using local components, all source code must be cloned in a single directory: + +```{shell} +[Ryan.Mulhall@lscamd50-d example-build-dir]$ ls +atmos_null FMScoupler FMS ice_null ice_param land_null ocean_null +``` +Which then can be built/tested with: + +```{shell} +cmake FMScoupler/ +make -j +make test +``` + +To have cmake clone all dependencies, build the null model, and run a test with cmake: + +```{shell} +mkdir build +cd build +cmake -DFETCH_FMS=on -DFETCH_COMPONENTS=on .. +make -j +make test +``` + + ### Grid Specification Files At runtime, the coupled model sets up its grid using a given `grid_spec.nc` file that it reads from the INPUT subdirectory. This NetCDF file contains grid information for all of the component models From c474287714c500924377f8b5dcdbc42b2a7525e9 Mon Sep 17 00:00:00 2001 From: rem1776 Date: Wed, 3 Jun 2026 12:59:37 -0400 Subject: [PATCH 5/6] add test script for real this time --- t/{null_model_cmake.sh => test_run.sh} | 32 +++++++------------------- 1 file changed, 8 insertions(+), 24 deletions(-) rename t/{null_model_cmake.sh => test_run.sh} (65%) diff --git a/t/null_model_cmake.sh b/t/test_run.sh similarity index 65% rename from t/null_model_cmake.sh rename to t/test_run.sh index 3f5bcef8..dc7bc998 100755 --- a/t/null_model_cmake.sh +++ b/t/test_run.sh @@ -1,41 +1,25 @@ #!/usr/bin/env sh # -# Script to build a GFDL null model, using all null components, and run -# a simple test on CI systems, like Travis CI or gitlab CI. - -script_root=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) -bld_dir=$(mktemp --directory $script_root/null.XXXXXX) -cd $bld_dir -cmake -DCMAKE_BUILD_TYPE=Debug ../.. -make -j -make - -# Report on the status of the build -if [ $? -eq 0 ] -then - echo "::note title=Build Succeeded:: null model with simple coupler built successfully." -else - echo "::error title=Build Failed:: null model with simple coupler failed compilation." - exit 1 -fi +# Run script for a simple test run using the null_model +# This is the same run done in null_model_build.sh, just without any building steps +rundir=$PWD # Run the null models test # Setup the run directory -mkdir ${bld_dir}/run -cd ${bld_dir}/run +rm -rf run +mkdir ${rundir}/run +cd ${rundir}/run mkdir RESTART # Get the data files required for the run tarFile=coupler_null_test_data_full_simple.tar.gz curl -O ftp://ftp.gfdl.noaa.gov/perm/GFDL_pubrelease/test_data/${tarFile} tar zxf ${tarFile} - # add an io layout to the full nml sed -i '22i io_layout = 1, 1' input-full.nml - # Get the full namelist ln -s input-full.nml input.nml # Run the null model with the full coupler -mpiexec -n 1 ${bld_dir}/coupler_full.x +mpiexec -n 1 ${rundir}/coupler_full.x # Report on the status of the run with the full coupler if [ $? -eq 0 ] @@ -54,7 +38,7 @@ mkdir RESTART rm input.nml ln -s input-simple.nml input.nml # Run the null simple coupler test -mpiexec -n 1 ${bld_dir}/coupler_simple.x +mpiexec -n 1 ${rundir}/coupler_simple.x # Report on the status of the run with the simple coupler if [ $? -eq 0 ] From d3481d2df67ba3b03f6bfeb5d5165fb6fb029ac2 Mon Sep 17 00:00:00 2001 From: rem1776 Date: Wed, 3 Jun 2026 13:27:01 -0400 Subject: [PATCH 6/6] linter fixes --- CMakeLists.txt | 6 +++--- t/test_run.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c2c6c79..ff393d44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -107,9 +107,9 @@ foreach(component_name IN LISTS COMPONENTS) else() set(component_path ${${component_name}_path} ) message(STATUS "Creating symlink in ${EXTERNAL_SOURCE_DIR} for ${component_path}") - file(CREATE_LINK + file(CREATE_LINK "${component_path}" - "${EXTERNAL_SOURCE_DIR}/${component_name}" + "${EXTERNAL_SOURCE_DIR}/${component_name}" SYMBOLIC ) endif() @@ -239,7 +239,7 @@ target_link_libraries(coupler_full PRIVATE ) set_target_properties(coupler_full PROPERTIES RUNTIME_OUTPUT_NAME "coupler_full.x") -# simple test run, only intended for null_model builds +# simple test run, only intended for null_model builds enable_testing() add_test(NAME null_model_run diff --git a/t/test_run.sh b/t/test_run.sh index dc7bc998..7734f5f4 100755 --- a/t/test_run.sh +++ b/t/test_run.sh @@ -1,6 +1,6 @@ #!/usr/bin/env sh # -# Run script for a simple test run using the null_model +# Run script for a simple test run using the null_model # This is the same run done in null_model_build.sh, just without any building steps rundir=$PWD