Skip to content

Commit

Permalink
feat: add support for multiple yaml tables with ensembles and nests (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
uramirez8707 authored Oct 24, 2024
1 parent 95cd7b0 commit ee31f06
Show file tree
Hide file tree
Showing 14 changed files with 613 additions and 20 deletions.
5 changes: 5 additions & 0 deletions data_override/README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [Converting legacy data_table to data_table.yaml](README.MD#3-converting-legacy-data_table-to-data_tableyaml)
- [Examples](README.MD#4-examples)
- [External Weight File Structure](README.MD#5-external-weight-file-structure)
- [Ensemble and Nest Support](README.MD#6-ensemble-and-nest-support)

#### 1. YAML Data Table format:
Each entry in the data_table has the following key values:
Expand Down Expand Up @@ -200,3 +201,7 @@ variables:
- weight(:,:,2) -> (i,j+1)
- weight(:,:,3) -> (i+1,j)
- weight(:,:,4) -> (i+1,j+1)

#### 6. Ensemble and Nest Support

It may be desired to have each member of an ensemble use a different forcing file. In other to support this, FMS allows for each ensemble member to have its own data_table.yaml. For example, for a run with 2 ensemble members, fms will search for data_table_ens_01.yaml and data_table_ens_02.yaml. However, if both the data_table.yaml and the data_table_ens_* files are present, the code will crash as only 1 option is allowed. Similary, each nest can have its own data_table (data_table_nest_01.yaml), but in this case FMS will not crash if both data_table_nest_01.yaml and data_table.yaml are present. The main grid will use the data_table.yaml and the first nest will use the data_table_nest_01.yaml file.
15 changes: 12 additions & 3 deletions data_override/include/data_override.inc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
! modules. These modules are not intended to be used directly - they should be
! used through the data_override_mod API. See data_override.F90 for details.

use platform_mod, only: r4_kind, r8_kind, FMS_PATH_LEN
use platform_mod, only: r4_kind, r8_kind, FMS_PATH_LEN, FMS_FILE_LEN
use yaml_parser_mod
use constants_mod, only: DEG_TO_RAD
use mpp_mod, only : mpp_error, FATAL, WARNING, NOTE, stdout, stdlog, mpp_max
Expand All @@ -45,7 +45,7 @@ use mpp_domains_mod, only : domainUG, mpp_pass_SG_to_UG, mpp_get_UG_SG_domain, N
use time_manager_mod, only: time_type, OPERATOR(>), OPERATOR(<)
use fms2_io_mod, only : FmsNetcdfFile_t, open_file, close_file, &
read_data, fms2_io_init, variable_exists, &
get_mosaic_tile_file, file_exists
get_mosaic_tile_file, file_exists, get_instance_filename
use get_grid_version_mod, only: get_grid_version_1, get_grid_version_2
use fms_string_utils_mod, only: string

Expand Down Expand Up @@ -591,9 +591,18 @@ subroutine read_table_yaml(data_table)
integer :: nentries, mentries
integer :: i
character(len=50) :: buffer
character(len=FMS_FILE_LEN) :: filename !< Name of the expected data_table.yaml
integer :: file_id
file_id = open_and_parse_file("data_table.yaml")
! If doing and ensemble or nest run add the filename appendix (ens_XX or nest_XX) to the filename
call get_instance_filename("data_table.yaml", filename)
if (index(trim(filename), "ens_") .ne. 0) then
if (file_exists(filename) .and. file_exists("data_table.yaml")) &
call mpp_error(FATAL, "Both data_table.yaml and "//trim(filename)//" exists, pick one!")
endif
file_id = open_and_parse_file(trim(filename))
if (file_id==999) then
nentries = 0
else
Expand Down
4 changes: 4 additions & 0 deletions diag_manager/diag_yaml_format.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The purpose of this document is to explain the diag_table yaml format.
- [2.6 Sub_region Section](diag_yaml_format.md#26-sub_region-section)
- [3. More examples](diag_yaml_format.md#3-more-examples)
- [4. Schema](diag_yaml_format.md#4-schema)
- [5. Ensemble and Nest Support](diag_yaml_format.md#5-ensemble-and-nest-support)

### 1. Converting from legacy ascii diag_table format

Expand Down Expand Up @@ -349,3 +350,6 @@ diag_files:
A formal specification of the file format, in the form of a JSON schema, can be
found in the [gfdl_msd_schemas](https://github.com/NOAA-GFDL/gfdl_msd_schemas)
repository on Github.

### 5. Ensemble and Nest Support
When using nests, it may be desired for a nest to have a different file frequency or number of variables from the parent grid. This may allow users to save disk space and reduce simulations time. In order to supports, FMS allows each nest to have a different diag_table.yaml from the parent grid. For example, if running with 1 test FMS will use diag_table.yaml for the parent grid and diag_table.nest_01.yaml for the first nest Similary, each ensemble member can have its own diag_table (diag_table_ens_XX.yaml, where XX is the ensemble number). However, for the ensemble case if both the diag_table.yaml and the diag_table_ens_* files are present, the code will crash as only 1 option is allowed.
10 changes: 9 additions & 1 deletion diag_manager/fms_diag_yaml.F90
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ module fms_diag_yaml_mod
fms_f2c_string
use platform_mod, only: r4_kind, i4_kind, r8_kind, i8_kind, FMS_FILE_LEN
use fms_mod, only: lowercase
use fms2_io_mod, only: file_exists, get_instance_filename

implicit none

Expand Down Expand Up @@ -381,10 +382,17 @@ subroutine diag_yaml_object_init(diag_subset_output)
!! outputing data at every frequency)
character(len=:), allocatable :: filename!< Diag file name (for error messages)
logical :: is_instantaneous !< .True. if the file is instantaneous (i.e no averaging)
character(len=FMS_FILE_LEN) :: yamlfilename !< Name of the expected diag_table.yaml

if (diag_yaml_module_initialized) return

diag_yaml_id = open_and_parse_file("diag_table.yaml")
! If doing and ensemble or nest run add the filename appendix (ens_XX or nest_XX) to the filename
call get_instance_filename("diag_table.yaml", yamlfilename)
if (index(trim(yamlfilename), "ens_") .ne. 0) then
if (file_exists(yamlfilename) .and. file_exists("diag_table.yaml")) &
call mpp_error(FATAL, "Both diag_table.yaml and "//trim(yamlfilename)//" exists, pick one!")
endif
diag_yaml_id = open_and_parse_file(trim(yamlfilename))

call diag_get_value_from_key(diag_yaml_id, 0, "title", diag_yaml%diag_title)
call get_value_from_key(diag_yaml_id, 0, "base_date", diag_yaml%diag_basedate)
Expand Down
15 changes: 12 additions & 3 deletions field_manager/field_manager.F90
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ module field_manager_mod
use fms_mod, only : lowercase, &
write_version_number, &
check_nml_error
use fms2_io_mod, only: file_exists
use fms2_io_mod, only: file_exists, get_instance_filename
use platform_mod, only: r4_kind, r8_kind, FMS_PATH_LEN, FMS_FILE_LEN
#ifdef use_yaml
use fm_yaml_mod
Expand Down Expand Up @@ -606,18 +606,27 @@ subroutine read_field_table_yaml(nfields, table_name)
logical :: fm_success !< logical for whether fm_change_list was a success
logical :: subparams !< logical whether subparams exist in this iteration

character(len=FMS_FILE_LEN) :: filename !< Name of the expected field_table.yaml

if (.not.PRESENT(table_name)) then
tbl_name = 'field_table.yaml'
else
tbl_name = trim(table_name)
endif
if (.not. file_exists(trim(tbl_name))) then

call get_instance_filename(tbl_name, filename)
if (index(trim(filename), "ens_") .ne. 0) then
if (file_exists(filename) .and. file_exists(tbl_name)) &
call mpp_error(FATAL, "Both "//trim(tbl_name)//" and "//trim(filename)//" exists, pick one!")
endif

if (.not. file_exists(trim(filename))) then
if(present(nfields)) nfields = 0
return
endif

! Construct my_table object
call build_fmTable(my_table, trim(tbl_name))
call build_fmTable(my_table, trim(filename))

do h=1,size(my_table%types)
do i=1,size(my_table%types(h)%models)
Expand Down
10 changes: 8 additions & 2 deletions fms2_io/fms_io_utils.F90
Original file line number Diff line number Diff line change
Expand Up @@ -824,8 +824,14 @@ subroutine get_instance_filename(name_in,name_out)
if ( i .ne. 0 ) then
name_out = name_in(1:i-1)//'.'//trim(filename_appendix)//name_in(i:length)
else
!< If .nc is not in the name, add the appendix at the end of the file
name_out = name_in(1:length) //'.'//trim(filename_appendix)
i = index(trim(name_in), ".yaml", back=.true.)
if (i .ne. 0) then
!< If .yaml is in the filename add the appendix before it
name_out = name_in(1:i-1)//'.'//trim(filename_appendix)//name_in(i:length)
else
!< If .nc and .yaml are not in the name, add the appendix at the end of the file
name_out = name_in(1:length) //'.'//trim(filename_appendix)
endif
end if
end if

Expand Down
4 changes: 2 additions & 2 deletions test_fms/data_override/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,11 @@ TESTS_ENVIRONMENT= test_input_path="@TEST_INPUT_PATH@" \
# Run the test program.

TESTS = test_data_override2.sh test_data_override_init.sh test_data_override2_mono.sh test_data_override2_ongrid.sh \
test_data_override2_scalar.sh test_data_override_weights.sh
test_data_override2_scalar.sh test_data_override_weights.sh test_data_override_ensembles.sh

# Include these files with the distribution.
EXTRA_DIST = test_data_override2.sh test_data_override_init.sh test_data_override2_mono.sh test_data_override2_ongrid.sh \
test_data_override2_scalar.sh test_data_override_weights.sh
test_data_override2_scalar.sh test_data_override_weights.sh test_data_override_ensembles.sh

# Clean up
CLEANFILES = input.nml *.nc* *.out diag_table data_table data_table.yaml INPUT/* *.dpi *.spi *.dyn *.spl *-files/*
99 changes: 99 additions & 0 deletions test_fms/data_override/test_data_override_ensembles.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/bin/sh

#***********************************************************************
#* GNU Lesser General Public License
#*
#* This file is part of the GFDL Flexible Modeling System (FMS).
#*
#* FMS 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 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. If not, see <http://www.gnu.org/licenses/>.
#***********************************************************************
#
# Copyright (c) 2019-2021 Ed Hartnett, Uriel Ramirez, Seth Underwood

# Set common test settings.
. ../test-lib.sh

output_dir
[ ! -d "INPUT" ] && mkdir -p "INPUT"

cat <<_EOF > data_table.ens_01.yaml
data_table:
- grid_name: OCN
fieldname_in_model: runoff
override_file:
- fieldname_in_file: runoff
file_name: INPUT/runoff.daitren.clim.1440x1080.v20180328_ens_01.nc
interp_method: none
factor: 1.0
_EOF

cat <<_EOF > data_table.ens_02.yaml
data_table:
- grid_name: OCN
fieldname_in_model: runoff
override_file:
- fieldname_in_file: runoff
file_name: INPUT/runoff.daitren.clim.1440x1080.v20180328_ens_02.nc
interp_method: none
factor: 1.0
_EOF

cat <<_EOF > input_base.nml
&data_override_nml
use_data_table_yaml = .True.
/
&test_data_override_ongrid_nml
test_case = 5
write_only = .False.
/
&ensemble_nml
ensemble_size = 2
/
_EOF

#The test only runs with yaml
if [ -z $parser_skip ]; then
for KIND in r4 r8
do
rm -rf INPUT/.
sed 's/write_only = .False./write_only = .True./g' input_base.nml > input.nml
test_expect_success "Creating input files (${KIND})" '
mpirun -n 12 ../test_data_override_ongrid_${KIND}
'

cp input_base.nml input.nml
test_expect_success "test_data_override with two ensembles -yaml (${KIND})" '
mpirun -n 12 ../test_data_override_ongrid_${KIND}
'
done

cat <<_EOF > data_table.yaml
data_table:
- grid_name: OCN
fieldname_in_model: runoff
override_file:
- fieldname_in_file: runoff
file_name: INPUT/runoff.daitren.clim.1440x1080.v20180328_ens_02.nc
interp_method: none
factor: 1.0
_EOF

test_expect_failure "test_data_override with both data_table.yaml and data_table.ens_xx.yaml files" '
mpirun -n 12 ../test_data_override_ongrid_${KIND}
'
rm -rf INPUT
fi
test_done
Loading

0 comments on commit ee31f06

Please sign in to comment.