From 56bd7d6aff2b93d8ebe6be1aafc75a2ab9b56dcf Mon Sep 17 00:00:00 2001 From: Jesse Lentz Date: Fri, 13 Jun 2025 13:53:35 -0400 Subject: [PATCH 1/8] Add cheat mode Add "cheat mode", which can be enabled via the `CHEAT_MODE_DIR` CPP definition. When enabled, `coupler_init` will check for the existence of a tarball corresponding to the current segment in the directory specified by `CHEAT_MODE_DIR`. If this tarball already exists, the main loop is skipped and the root PE extracts the tarball before the program exits (after `fms_end` is called). If the tarball does not exist, the main loop runs normally and the root PE creates the tarball before the program exits (after `fms_end` is called). --- full/coupler_main.F90 | 12 ++++++++++++ full/full_coupler_mod.F90 | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/full/coupler_main.F90 b/full/coupler_main.F90 index ffd4f738..59746b4d 100644 --- a/full/coupler_main.F90 +++ b/full/coupler_main.F90 @@ -687,4 +687,16 @@ program coupler_main !----------------------------------------------------------------------- +#ifdef CHEAT_MODE_DIR + if (fms_mpp_pe().eq.fms_mpp_root_pe()) then + if (cheatmode_on) then + call error_mesg("cheat mode", "Extracting results tarball: " // cheatmode_file, NOTE) + call execute_command_line("tar -xf " // cheatmode_file // " --touch --overwrite") + else + call error_mesg("cheat mode", "Generating results tarball: " // cheatmode_file, NOTE) + call execute_command_line("tar -cf " // cheatmode_file // " `find . -type f -newer input.nml | xargs`") + endif + endif +#endif + end program coupler_main diff --git a/full/full_coupler_mod.F90 b/full/full_coupler_mod.F90 index 445ef77b..ade79918 100644 --- a/full/full_coupler_mod.F90 +++ b/full/full_coupler_mod.F90 @@ -316,6 +316,11 @@ module full_coupler_mod !> coupled model initial date integer :: date_init(6) = (/ 0, 0, 0, 0, 0, 0 /) +#ifdef CHEAT_MODE_DIR + logical, public :: cheatmode_on !> Whether to extract (true) or generate (false) a results tarball + character(:), allocatable, public :: cheatmode_file !> Filename of the results tarball +#endif + contains !####################################################################### @@ -1150,6 +1155,22 @@ subroutine coupler_init(Atm, Ocean, Land, Ice, Ocean_state, Atmos_land_boundary, //trim(walldate)//' '//trim(walltime) endif +!----------------------------------------------------------------------- + +#ifdef CHEAT_MODE_DIR + allocate (character(len(CHEAT_MODE_DIR) + 26) :: cheatmode_file) + write (cheatmode_file, '(A,"/",I0.4,"-",I0.2,"-",I0.2,"_",I0.4,"-",I0.2,"-",I0.2,".tar")') & + CHEAT_MODE_DIR, date_init(1), date_init(2), date_init(3), date(1), date(2), date(3) + inquire (file=cheatmode_file, exist=cheatmode_on) + + if (cheatmode_on) then + num_cpld_calls = 0 + call error_mesg("cheat mode", "Results tarball exists: Cheat mode is ON", NOTE) + else + call error_mesg("cheat mode", "Results tarball does not exist: Cheat mode is OFF", NOTE) + endif +#endif + end subroutine coupler_init !####################################################################### From 3f3c083b97a0f5fee203df88104b0b67b4c81efc Mon Sep 17 00:00:00 2001 From: Jesse Lentz Date: Fri, 13 Jun 2025 16:03:36 -0400 Subject: [PATCH 2/8] Use quoting macro Use a quoting macro to create a string literal from `CHEAT_MODE_DIR`. --- full/full_coupler_mod.F90 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/full/full_coupler_mod.F90 b/full/full_coupler_mod.F90 index ade79918..ab0b0098 100644 --- a/full/full_coupler_mod.F90 +++ b/full/full_coupler_mod.F90 @@ -1158,9 +1158,10 @@ subroutine coupler_init(Atm, Ocean, Land, Ice, Ocean_state, Atmos_land_boundary, !----------------------------------------------------------------------- #ifdef CHEAT_MODE_DIR - allocate (character(len(CHEAT_MODE_DIR) + 26) :: cheatmode_file) +#define QUOTE(x) #x + allocate (character(len(QUOTE(CHEAT_MODE_DIR)) + 26) :: cheatmode_file) write (cheatmode_file, '(A,"/",I0.4,"-",I0.2,"-",I0.2,"_",I0.4,"-",I0.2,"-",I0.2,".tar")') & - CHEAT_MODE_DIR, date_init(1), date_init(2), date_init(3), date(1), date(2), date(3) + QUOTE(CHEAT_MODE_DIR), date_init(1), date_init(2), date_init(3), date(1), date(2), date(3) inquire (file=cheatmode_file, exist=cheatmode_on) if (cheatmode_on) then From 39fa47242f2fe5a9722d5c8910611e21eb8865ae Mon Sep 17 00:00:00 2001 From: Jesse Lentz Date: Fri, 13 Jun 2025 16:17:02 -0400 Subject: [PATCH 3/8] Replace `error_mesg` with `fms_error_mesg` --- full/coupler_main.F90 | 4 ++-- full/full_coupler_mod.F90 | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/full/coupler_main.F90 b/full/coupler_main.F90 index 59746b4d..2403d652 100644 --- a/full/coupler_main.F90 +++ b/full/coupler_main.F90 @@ -690,10 +690,10 @@ program coupler_main #ifdef CHEAT_MODE_DIR if (fms_mpp_pe().eq.fms_mpp_root_pe()) then if (cheatmode_on) then - call error_mesg("cheat mode", "Extracting results tarball: " // cheatmode_file, NOTE) + call fms_error_mesg("cheat mode", "Extracting results tarball: " // cheatmode_file, NOTE) call execute_command_line("tar -xf " // cheatmode_file // " --touch --overwrite") else - call error_mesg("cheat mode", "Generating results tarball: " // cheatmode_file, NOTE) + call fms_error_mesg("cheat mode", "Generating results tarball: " // cheatmode_file, NOTE) call execute_command_line("tar -cf " // cheatmode_file // " `find . -type f -newer input.nml | xargs`") endif endif diff --git a/full/full_coupler_mod.F90 b/full/full_coupler_mod.F90 index ab0b0098..8c2c05ac 100644 --- a/full/full_coupler_mod.F90 +++ b/full/full_coupler_mod.F90 @@ -1166,9 +1166,9 @@ subroutine coupler_init(Atm, Ocean, Land, Ice, Ocean_state, Atmos_land_boundary, if (cheatmode_on) then num_cpld_calls = 0 - call error_mesg("cheat mode", "Results tarball exists: Cheat mode is ON", NOTE) + call fms_error_mesg("cheat mode", "Results tarball exists: Cheat mode is ON", NOTE) else - call error_mesg("cheat mode", "Results tarball does not exist: Cheat mode is OFF", NOTE) + call fms_error_mesg("cheat mode", "Results tarball does not exist: Cheat mode is OFF", NOTE) endif #endif From 389d737cc8f6a7d545dc343dde269d28efef99d0 Mon Sep 17 00:00:00 2001 From: Jesse Lentz Date: Fri, 13 Jun 2025 17:18:38 -0400 Subject: [PATCH 4/8] Remove `error_mesg` calls after `fms_end` `error_mesg` calls do not seem to work after `fms_end`, so the information content of those messages is now printed at the end of `coupler_init`. --- full/coupler_main.F90 | 2 -- full/full_coupler_mod.F90 | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/full/coupler_main.F90 b/full/coupler_main.F90 index 2403d652..7f5f80a7 100644 --- a/full/coupler_main.F90 +++ b/full/coupler_main.F90 @@ -690,10 +690,8 @@ program coupler_main #ifdef CHEAT_MODE_DIR if (fms_mpp_pe().eq.fms_mpp_root_pe()) then if (cheatmode_on) then - call fms_error_mesg("cheat mode", "Extracting results tarball: " // cheatmode_file, NOTE) call execute_command_line("tar -xf " // cheatmode_file // " --touch --overwrite") else - call fms_error_mesg("cheat mode", "Generating results tarball: " // cheatmode_file, NOTE) call execute_command_line("tar -cf " // cheatmode_file // " `find . -type f -newer input.nml | xargs`") endif endif diff --git a/full/full_coupler_mod.F90 b/full/full_coupler_mod.F90 index 8c2c05ac..9726eeb5 100644 --- a/full/full_coupler_mod.F90 +++ b/full/full_coupler_mod.F90 @@ -1166,9 +1166,9 @@ subroutine coupler_init(Atm, Ocean, Land, Ice, Ocean_state, Atmos_land_boundary, if (cheatmode_on) then num_cpld_calls = 0 - call fms_error_mesg("cheat mode", "Results tarball exists: Cheat mode is ON", NOTE) + call fms_error_mesg("cheat mode", "Results tarball will be extracted: " // cheatmode_file, NOTE) else - call fms_error_mesg("cheat mode", "Results tarball does not exist: Cheat mode is OFF", NOTE) + call fms_error_mesg("cheat mode", "Results tarball will be created: " // cheatmode_file, NOTE) endif #endif From a38067eabcfaa956b41f78dd8bf17ac7bae26793 Mon Sep 17 00:00:00 2001 From: Jesse Lentz Date: Mon, 16 Jun 2025 14:12:07 -0400 Subject: [PATCH 5/8] Add `cheat_mode_mod` Add a module containing the variables and subroutines that are relevant to cheat_mode. --- full/cheat_mode_mod.F90 | 79 +++++++++++++++++++++++++++++++++++++++ full/coupler_main.F90 | 12 +++--- full/full_coupler_mod.F90 | 23 +++++------- 3 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 full/cheat_mode_mod.F90 diff --git a/full/cheat_mode_mod.F90 b/full/cheat_mode_mod.F90 new file mode 100644 index 00000000..93982061 --- /dev/null +++ b/full/cheat_mode_mod.F90 @@ -0,0 +1,79 @@ +!*********************************************************************** +!* Apache License 2.0 +!* +!* This file is part of the GFDL Flexible Modeling System (FMS) Coupler. +!* +!* Licensed under the Apache License, Version 2.0 (the "License"); +!* you may not use this file except in compliance with the License. +!* You may obtain a copy of the License at +!* +!* http://www.apache.org/licenses/LICENSE-2.0 +!* +!* FMS Coupler is distributed in the hope that it will be useful, but +!* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied; +!* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +!* PARTICULAR PURPOSE. See the License for the specific language +!* governing permissions and limitations under the License. +!*********************************************************************** + +!> @defgroup cheat_mode_mod cheat_mode_mod +!> @ingroup cheat_mode_mod +!> @brief Generate or extract a tarball containing the final contents of a run directory. +!! +!> This module supports "cheat mode", so that the workflow can be tested without the +!! computational cost of an actual model run. A directory is supplied via the +!! `cheat_mode_nml` namelist: if a tarball is found in this directory corresponding +!! to the date range of the current segment, `cheat_mode_invoke` will extract this +!! tarball; otherwise, it will generate it from the content of the run directory. + +#ifdef CHEAT_MODE + +module cheat_mode_mod + use FMS + implicit none + private + + public :: cheat_mode_tarball_exists, cheat_mode_tarball_path, cheat_mode_init, cheat_mode_invoke + + logical :: cheat_mode_tarball_exists !< Whether to extract (true) or generate (false) a tarball + character(:), allocatable :: cheat_mode_tarball_path !< Path to the tarball to be extracted or created + character(:), allocatable :: dir !< Directory containing the tarballs + + namelist /cheat_mode_nml/ dir + +!> @addtogroup cheat_mode_mod +!> @{ + +contains + + !> Initialize cheat mode. Determine the path to the tarball from `cheat_mode_nml` and + !! from the date range of the current segment. + subroutine cheat_mode_init(year0, month0, day0, year1, month1, day1) + integer, intent(in) :: year0, month0, day0 !< Initial year, month, and day of the current segment + integer, intent(in) :: year1, month1, day1 !< Final year, month, and day of the current segment + integer :: io_status, ierr + + read (input_nml_file, cheat_mode_nml, iostat=io_status) + ierr = check_nml_error(io_status, "cheat_mode_nml") + + allocate (character(len(dir) + 22) :: cheat_mode_tarball_path) + write (cheat_mode_tarball_path, '(A,"/",I0.4,I0.2,I0.2,"_",I0.4,I0.2,I0.2,".tar")') & + dir, year0, month0, day0, year1, month1, day1 + inquire (file=cheat_mode_tarball_path, exist=cheat_mode_tarball_exists) + end subroutine + + !> Invoke the tar command to either create or extract the tarball. This subroutine + !! should only be called by the root PE. + subroutine cheat_mode_invoke + if (cheat_mode_tarball_exists) then + call execute_command_line("tar -xf " // cheat_mode_tarball_path // " --touch --overwrite") + else + call execute_command_line("tar -cf " // cheat_mode_tarball_path // " `find . -type f -newer input.nml | xargs`") + endif + end subroutine cheat_mode_invoke +end module cheat_mode_mod + +#endif + +!> @} +! close documentation grouping diff --git a/full/coupler_main.F90 b/full/coupler_main.F90 index 7f5f80a7..9f362a7e 100644 --- a/full/coupler_main.F90 +++ b/full/coupler_main.F90 @@ -336,6 +336,10 @@ program coupler_main use FMS use full_coupler_mod +#ifdef CHEAT_MODE + use cheat_mode_mod, only: cheat_mode_invoke +#endif + implicit none !> model defined types. @@ -687,13 +691,9 @@ program coupler_main !----------------------------------------------------------------------- -#ifdef CHEAT_MODE_DIR +#ifdef CHEAT_MODE if (fms_mpp_pe().eq.fms_mpp_root_pe()) then - if (cheatmode_on) then - call execute_command_line("tar -xf " // cheatmode_file // " --touch --overwrite") - else - call execute_command_line("tar -cf " // cheatmode_file // " `find . -type f -newer input.nml | xargs`") - endif + call cheat_mode_invoke endif #endif diff --git a/full/full_coupler_mod.F90 b/full/full_coupler_mod.F90 index 9726eeb5..5373f588 100644 --- a/full/full_coupler_mod.F90 +++ b/full/full_coupler_mod.F90 @@ -28,6 +28,10 @@ module full_coupler_mod use fms_io_mod, only: fms_io_exit #endif +#ifdef CHEAT_MODE + use cheat_mode_mod, only: cheat_mode_init, cheat_mode_tarball_exists, cheat_mode_tarball_path +#endif + ! model interfaces used to couple the component models: ! atmosphere, land, ice, and ocean ! @@ -316,11 +320,6 @@ module full_coupler_mod !> coupled model initial date integer :: date_init(6) = (/ 0, 0, 0, 0, 0, 0 /) -#ifdef CHEAT_MODE_DIR - logical, public :: cheatmode_on !> Whether to extract (true) or generate (false) a results tarball - character(:), allocatable, public :: cheatmode_file !> Filename of the results tarball -#endif - contains !####################################################################### @@ -1157,18 +1156,14 @@ subroutine coupler_init(Atm, Ocean, Land, Ice, Ocean_state, Atmos_land_boundary, !----------------------------------------------------------------------- -#ifdef CHEAT_MODE_DIR -#define QUOTE(x) #x - allocate (character(len(QUOTE(CHEAT_MODE_DIR)) + 26) :: cheatmode_file) - write (cheatmode_file, '(A,"/",I0.4,"-",I0.2,"-",I0.2,"_",I0.4,"-",I0.2,"-",I0.2,".tar")') & - QUOTE(CHEAT_MODE_DIR), date_init(1), date_init(2), date_init(3), date(1), date(2), date(3) - inquire (file=cheatmode_file, exist=cheatmode_on) +#ifdef CHEAT_MODE + call cheat_mode_init(date_init(1), date_init(2), date_init(3), date(1), date(2), date(3)) - if (cheatmode_on) then + if (cheat_mode_tarball_exists) then + call fms_error_mesg("cheat mode", "Results tarball will be extracted: " // cheat_mode_tarball_path, NOTE) num_cpld_calls = 0 - call fms_error_mesg("cheat mode", "Results tarball will be extracted: " // cheatmode_file, NOTE) else - call fms_error_mesg("cheat mode", "Results tarball will be created: " // cheatmode_file, NOTE) + call fms_error_mesg("cheat mode", "Results tarball will be created: " // cheat_mode_tarball_path, NOTE) endif #endif From b149fa95606e6e3b72bc0806de40db03fe1e4d27 Mon Sep 17 00:00:00 2001 From: Jesse Lentz Date: Mon, 16 Jun 2025 14:52:28 -0400 Subject: [PATCH 6/8] Add `fms_` prefix Fix the namelist reading code by adding the `fms_` prefix to `input_nml_file` and `check_nml_error`. --- full/cheat_mode_mod.F90 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/full/cheat_mode_mod.F90 b/full/cheat_mode_mod.F90 index 93982061..6c602448 100644 --- a/full/cheat_mode_mod.F90 +++ b/full/cheat_mode_mod.F90 @@ -51,10 +51,10 @@ module cheat_mode_mod subroutine cheat_mode_init(year0, month0, day0, year1, month1, day1) integer, intent(in) :: year0, month0, day0 !< Initial year, month, and day of the current segment integer, intent(in) :: year1, month1, day1 !< Final year, month, and day of the current segment - integer :: io_status, ierr + integer :: io_status - read (input_nml_file, cheat_mode_nml, iostat=io_status) - ierr = check_nml_error(io_status, "cheat_mode_nml") + read (fms_input_nml_file, cheat_mode_nml, iostat=io_status) + io_status = fms_check_nml_error(io_status, "cheat_mode_nml") allocate (character(len(dir) + 22) :: cheat_mode_tarball_path) write (cheat_mode_tarball_path, '(A,"/",I0.4,I0.2,I0.2,"_",I0.4,I0.2,I0.2,".tar")') & From 16d8dff4887d5801de0a5b88716fe7a24740c11e Mon Sep 17 00:00:00 2001 From: Jesse Lentz Date: Mon, 16 Jun 2025 15:06:09 -0400 Subject: [PATCH 7/8] fms_input_nml_file -> fms_mpp_input_nml_file --- full/cheat_mode_mod.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/full/cheat_mode_mod.F90 b/full/cheat_mode_mod.F90 index 6c602448..7b192085 100644 --- a/full/cheat_mode_mod.F90 +++ b/full/cheat_mode_mod.F90 @@ -53,7 +53,7 @@ subroutine cheat_mode_init(year0, month0, day0, year1, month1, day1) integer, intent(in) :: year1, month1, day1 !< Final year, month, and day of the current segment integer :: io_status - read (fms_input_nml_file, cheat_mode_nml, iostat=io_status) + read (fms_mpp_input_nml_file, cheat_mode_nml, iostat=io_status) io_status = fms_check_nml_error(io_status, "cheat_mode_nml") allocate (character(len(dir) + 22) :: cheat_mode_tarball_path) From ae5446ca269328d60e225c4d3f62ca6234edd723 Mon Sep 17 00:00:00 2001 From: Jesse Lentz Date: Mon, 16 Jun 2025 16:18:05 -0400 Subject: [PATCH 8/8] Use `FMS_PATH_LEN` Use `FMS_PATH_LEN` to set the string lengths for the tarball's directory and path. --- full/cheat_mode_mod.F90 | 22 +++++++++++++++------- full/full_coupler_mod.F90 | 4 ++-- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/full/cheat_mode_mod.F90 b/full/cheat_mode_mod.F90 index 7b192085..cb73c140 100644 --- a/full/cheat_mode_mod.F90 +++ b/full/cheat_mode_mod.F90 @@ -30,14 +30,15 @@ module cheat_mode_mod use FMS + use platform_mod, only: FMS_PATH_LEN implicit none private public :: cheat_mode_tarball_exists, cheat_mode_tarball_path, cheat_mode_init, cheat_mode_invoke logical :: cheat_mode_tarball_exists !< Whether to extract (true) or generate (false) a tarball - character(:), allocatable :: cheat_mode_tarball_path !< Path to the tarball to be extracted or created - character(:), allocatable :: dir !< Directory containing the tarballs + character(FMS_PATH_LEN) :: cheat_mode_tarball_path !< Path to the tarball to be extracted or created + character(FMS_PATH_LEN) :: dir !< Directory containing the tarballs namelist /cheat_mode_nml/ dir @@ -56,19 +57,26 @@ subroutine cheat_mode_init(year0, month0, day0, year1, month1, day1) read (fms_mpp_input_nml_file, cheat_mode_nml, iostat=io_status) io_status = fms_check_nml_error(io_status, "cheat_mode_nml") - allocate (character(len(dir) + 22) :: cheat_mode_tarball_path) write (cheat_mode_tarball_path, '(A,"/",I0.4,I0.2,I0.2,"_",I0.4,I0.2,I0.2,".tar")') & - dir, year0, month0, day0, year1, month1, day1 - inquire (file=cheat_mode_tarball_path, exist=cheat_mode_tarball_exists) + trim(dir), year0, month0, day0, year1, month1, day1 + inquire (file=trim(cheat_mode_tarball_path), exist=cheat_mode_tarball_exists) end subroutine !> Invoke the tar command to either create or extract the tarball. This subroutine !! should only be called by the root PE. subroutine cheat_mode_invoke + integer :: exitstat + if (cheat_mode_tarball_exists) then - call execute_command_line("tar -xf " // cheat_mode_tarball_path // " --touch --overwrite") + call execute_command_line("tar -xf " // trim(cheat_mode_tarball_path) // & + " --touch --overwrite", exitstat=exitstat) else - call execute_command_line("tar -cf " // cheat_mode_tarball_path // " `find . -type f -newer input.nml | xargs`") + call execute_command_line("tar -cf " // trim(cheat_mode_tarball_path) // & + " `find . -type f -newer input.nml | xargs`", exitstat=exitstat) + endif + + if (exitstat.ne.0) then + call fms_error_mesg("cheat mode", "tar command failed", FATAL) endif end subroutine cheat_mode_invoke end module cheat_mode_mod diff --git a/full/full_coupler_mod.F90 b/full/full_coupler_mod.F90 index 5373f588..7d7e3258 100644 --- a/full/full_coupler_mod.F90 +++ b/full/full_coupler_mod.F90 @@ -1160,10 +1160,10 @@ subroutine coupler_init(Atm, Ocean, Land, Ice, Ocean_state, Atmos_land_boundary, call cheat_mode_init(date_init(1), date_init(2), date_init(3), date(1), date(2), date(3)) if (cheat_mode_tarball_exists) then - call fms_error_mesg("cheat mode", "Results tarball will be extracted: " // cheat_mode_tarball_path, NOTE) + call fms_error_mesg("cheat mode", "Results tarball will be extracted: " // trim(cheat_mode_tarball_path), NOTE) num_cpld_calls = 0 else - call fms_error_mesg("cheat mode", "Results tarball will be created: " // cheat_mode_tarball_path, NOTE) + call fms_error_mesg("cheat mode", "Results tarball will be created: " // trim(cheat_mode_tarball_path), NOTE) endif #endif