diff --git a/diag_manager/fms_diag_yaml.F90 b/diag_manager/fms_diag_yaml.F90 index 9a423697e0..6e184bfc58 100644 --- a/diag_manager/fms_diag_yaml.F90 +++ b/diag_manager/fms_diag_yaml.F90 @@ -32,6 +32,7 @@ module fms_diag_yaml_mod #ifdef use_yaml use fms_diag_yaml_object_mod, only: diagYamlFiles_type, diagYamlFilesVar_type, diag_yaml_files_obj_init, & NUM_SUB_REGION_ARRAY +use diag_data_mod, only: DIAG_NULL use yaml_parser_mod use mpp_mod @@ -40,6 +41,7 @@ module fms_diag_yaml_mod private public :: diag_yaml_object_init, diag_yaml_object_end +public :: diagYamlObject_type, get_diag_yaml_obj, get_title, get_basedate, get_diag_files, get_diag_fields !> @} integer, parameter :: basedate_size = 6 @@ -54,6 +56,8 @@ module fms_diag_yaml_mod contains procedure :: get_title !< Returns the title procedure :: get_basedate !< Returns the basedate array + procedure :: get_diag_files !< Returns the diag_files array + procedure :: get_diag_fields !< Returns the diag_field array end type diagYamlObject_type type (diagYamlObject_type) :: diag_yaml !< Obj containing the contents of the diag_table.yaml @@ -62,9 +66,19 @@ module fms_diag_yaml_mod !> @{ contains +!> @brief gets the diag_yaml module variable +!! @return a copy of the diag_yaml module variable +function get_diag_yaml_obj() & +result(res) + type (diagYamlObject_type) :: res + + res = diag_yaml +end function get_diag_yaml_obj + !> @brief get the basedate of a diag_yaml type !! @return the basedate as an integer array -pure function get_basedate (diag_yaml) result (diag_basedate) +pure function get_basedate (diag_yaml) & +result (diag_basedate) class (diagYamlObject_type), intent(in) :: diag_yaml !< The diag_yaml integer, dimension (basedate_size) :: diag_basedate !< Basedate array result to return @@ -73,13 +87,34 @@ end function get_basedate !> @brief get the title of a diag_yaml type !! @return the title of the diag table as an allocated string -pure function get_title (diag_yaml) result (diag_title) +pure function get_title (diag_yaml) & + result (diag_title) class (diagYamlObject_type), intent(in) :: diag_yaml !< The diag_yaml character(len=:),allocatable :: diag_title !< Basedate array result to return diag_title = diag_yaml%diag_title end function get_title +!> @brief get the diag_files of a diag_yaml type +!! @return the diag_files +pure function get_diag_files(diag_yaml) & +result(diag_files) + class (diagYamlObject_type), intent(in) :: diag_yaml !< The diag_yaml + type(diagYamlFiles_type), allocatable, dimension (:) :: diag_files!< History file info + + diag_files = diag_yaml%diag_files +end function get_diag_files + +!> @brief get the diag_fields of a diag_yaml type +!! @return the diag_fields +pure function get_diag_fields(diag_yaml) & +result(diag_fields) + class (diagYamlObject_type), intent(in) :: diag_yaml !< The diag_yaml + type(diagYamlFilesVar_type), allocatable, dimension (:) :: diag_fields !< Diag fields info + + diag_fields = diag_yaml%diag_fields +end function get_diag_fields + !> @brief Uses the yaml_parser_mod to read in the diag_table and fill in the !! diag_yaml object subroutine diag_yaml_object_init @@ -114,9 +149,16 @@ subroutine diag_yaml_object_init nvars = get_num_blocks(diag_yaml_id, "varlist", parent_block_id=diag_file_ids(i)) allocate(var_ids(nvars)) call get_block_ids(diag_yaml_id, "varlist", var_ids, parent_block_id=diag_file_ids(i)) + allocate(diag_yaml%diag_files(i)%file_varlist(nvars)) nvars_loop: do j = 1, nvars var_count = var_count + 1 + !> Save the filename in the diag_field type + diag_yaml%diag_fields(var_count)%var_fname = diag_yaml%diag_files(i)%file_fname + call fill_in_diag_fields(diag_yaml_id, var_ids(j), diag_yaml%diag_fields(var_count)) + + !> Save the variable name in the diag_file type + diag_yaml%diag_files(i)%file_varlist(j) = diag_yaml%diag_fields(var_count)%var_varname enddo nvars_loop deallocate(var_ids) enddo nfiles_loop @@ -129,6 +171,7 @@ subroutine diag_yaml_object_end() integer :: i !< For do loops do i = 1, size(diag_yaml%diag_files, 1) + if(allocated(diag_yaml%diag_files(i)%file_varlist)) deallocate(diag_yaml%diag_files(i)%file_varlist) if(allocated(diag_yaml%diag_files(i)%file_global_meta)) deallocate(diag_yaml%diag_files(i)%file_global_meta) if(allocated(diag_yaml%diag_files(i)%file_sub_region%lat_lon_sub_region)) & deallocate(diag_yaml%diag_files(i)%file_sub_region%lat_lon_sub_region) @@ -162,18 +205,27 @@ subroutine fill_in_diag_files(diag_yaml_id, diag_file_id, fileobj) call diag_get_value_from_key(diag_yaml_id, diag_file_id, "file_name", fileobj%file_fname) call diag_get_value_from_key(diag_yaml_id, diag_file_id, "freq_units", fileobj%file_frequnit) call get_value_from_key(diag_yaml_id, diag_file_id, "freq", fileobj%file_freq) + call check_file_freq(fileobj) + call diag_get_value_from_key(diag_yaml_id, diag_file_id, "unlimdim", fileobj%file_unlimdim) call diag_get_value_from_key(diag_yaml_id, diag_file_id, "time_units", fileobj%file_timeunit) + call check_file_time_units(fileobj) + call diag_get_value_from_key(diag_yaml_id, diag_file_id, "write_file", fileobj%string_file_write, is_optional=.true.) if (fileobj%string_file_write .eq. "false") fileobj%file_write = .false. call diag_get_value_from_key(diag_yaml_id, diag_file_id, "realm", fileobj%file_realm, is_optional=.true.) + call check_file_realm(fileobj) + call get_value_from_key(diag_yaml_id, diag_file_id, "new_file_freq", fileobj%file_new_file_freq, is_optional=.true.) call diag_get_value_from_key(diag_yaml_id, diag_file_id, "new_file_freq_units", fileobj%file_new_file_freq_units, & is_optional=.true.) + call check_new_file_freq(fileobj) + call diag_get_value_from_key(diag_yaml_id, diag_file_id, "start_time", fileobj%file_start_time, is_optional=.true.) call get_value_from_key(diag_yaml_id, diag_file_id, "file_duration", fileobj%file_duration, is_optional=.true.) call diag_get_value_from_key(diag_yaml_id, diag_file_id, "file_duration_units", fileobj%file_duration_units, & - is_optional=.true.) + is_optional=.true.) + call check_file_duration(fileobj) nsubregion = 0 nsubregion = get_num_blocks(diag_yaml_id, "sub_region", parent_block_id=diag_file_id) @@ -182,13 +234,19 @@ subroutine fill_in_diag_files(diag_yaml_id, diag_file_id, fileobj) call diag_get_value_from_key(diag_yaml_id, sub_region_id(1), "grid_type", fileobj%file_sub_region%grid_type) if (trim(fileobj%file_sub_region%grid_type) .eq. "latlon") then allocate(fileobj%file_sub_region%lat_lon_sub_region(8)) + fileobj%file_sub_region%lat_lon_sub_region = DIAG_NULL call get_sub_region(diag_yaml_id, sub_region_id(1), fileobj%file_sub_region%lat_lon_sub_region) elseif (trim(fileobj%file_sub_region%grid_type) .eq. "index") then allocate(fileobj%file_sub_region%index_sub_region(8)) + fileobj%file_sub_region%index_sub_region = DIAG_NULL call get_sub_region(diag_yaml_id, sub_region_id(1), fileobj%file_sub_region%index_sub_region) call get_value_from_key(diag_yaml_id, sub_region_id(1), "tile", fileobj%file_sub_region%tile, is_optional=.true.) if (fileobj%file_sub_region%tile .eq. 0) call mpp_error(FATAL, "The tile number is required when defining a "//& "subregion. Check your subregion entry for "//trim(fileobj%file_fname)) + else + call mpp_error(FATAL, trim(fileobj%file_sub_region%grid_type)//" is not a valid region type. & + &The acceptable values are latlon and index. & + &Check your entry for file:"//trim(fileobj%file_fname)) endif elseif (nsubregion .ne. 0) then call mpp_error(FATAL, "diag_yaml_object_init: file "//trim(fileobj%file_fname)//" has multiple region blocks") @@ -219,8 +277,7 @@ subroutine fill_in_diag_files(diag_yaml_id, diag_file_id, fileobj) subroutine fill_in_diag_fields(diag_file_id, var_id, field) integer, intent(in) :: diag_file_id !< Id of the file block in the yaml file integer, intent(in) :: var_id !< Id of the variable block in the yaml file - type(diagYamlFilesVar_type), intent(out) :: field !< diagYamlFilesVar_type obj to read the contents into - + type(diagYamlFilesVar_type), intent(inout) :: field !< diagYamlFilesVar_type obj to read the contents into integer :: natt !< Number of attributes in variable integer :: var_att_id(1) !< Id of the variable attribute block @@ -232,8 +289,12 @@ subroutine fill_in_diag_fields(diag_file_id, var_id, field) field%var_write = .true. call diag_get_value_from_key(diag_file_id, var_id, "var_name", field%var_varname) call diag_get_value_from_key(diag_file_id, var_id, "reduction", field%var_reduction) + call check_field_reduction(field) + call diag_get_value_from_key(diag_file_id, var_id, "module", field%var_module) call diag_get_value_from_key(diag_file_id, var_id, "kind", field%var_skind) + call check_field_kind(field) + call diag_get_value_from_key(diag_file_id, var_id, "write_var", field%string_var_write, is_optional=.true.) if (trim(field%string_var_write) .eq. "false") field%var_write = .false. @@ -313,6 +374,139 @@ function get_total_num_vars(diag_yaml_id, diag_file_ids) & end do end function +!> @brief This checks if the file frequency in a diag file is valid and crashes if it isn't +subroutine check_file_freq(fileobj) + type(diagYamlFiles_type), intent(inout) :: fileobj !< diagYamlFiles_type obj to check + + if (fileobj%file_freq < 1 ) & + call mpp_error(FATAL, "freq must be greater than 0. & + &Check you entry for"//trim(fileobj%file_fname)) + if(.not. is_valid_time_units(fileobj%file_frequnit)) & + call mpp_error(FATAL, trim(fileobj%file_frequnit)//" is not a valid file_frequnit. & + &The acceptable values are seconds, minuts, hours, days, months, years. & + &Check your entry for file:"//trim(fileobj%file_fname)) +end subroutine check_file_freq + +!> @brief This checks if the time unit in a diag file is valid and crashes if it isn't +subroutine check_file_time_units (fileobj) + type(diagYamlFiles_type), intent(inout) :: fileobj !< diagYamlFiles_type obj to checK + + if(.not. is_valid_time_units(fileobj%file_timeunit)) & + call mpp_error(FATAL, trim(fileobj%file_timeunit)//" is not a valid time_unit. & + &The acceptable values are seconds, minuts, hours, days, months, years. & + &Check your entry for file:"//trim(fileobj%file_fname)) +end subroutine check_file_time_units + +!> @brief This checks if the realm in a diag file is valid and crashes if it isn't +subroutine check_file_realm(fileobj) + type(diagYamlFiles_type), intent(inout) :: fileobj !< diagYamlFiles_type obj to checK + + select case (TRIM(fileobj%file_realm)) + case ("ATM", "OCN", "LND", "ICE", "") + case default + call mpp_error(FATAL, trim(fileobj%file_realm)//" is an invalid realm! & + &The acceptable values are ATM, OCN, LND, ICE. & + &Check your entry for file:"//trim(fileobj%file_fname)) + end select + +end subroutine check_file_realm + +!> @brief This checks if the new file frequency in a diag file is valid and crashes if it isn't +subroutine check_new_file_freq(fileobj) + type(diagYamlFiles_type), intent(inout) :: fileobj !< diagYamlFiles_type obj to check + + if (fileobj%file_new_file_freq > 0) then + if (trim(fileobj%file_new_file_freq_units) .eq. "") & + call mpp_error(FATAL, "new_file_freq_units is required if using new_file_freq. & + &Check your entry for file:"//trim(fileobj%file_fname)) + + if (.not. is_valid_time_units(fileobj%file_new_file_freq_units)) & + call mpp_error(FATAL, trim(fileobj%file_new_file_freq_units)//" is not a valid new_file_freq_units. & + &The acceptable values are seconds, minuts, hours, days, months, years. & + &Check your entry for file:"//trim(fileobj%file_fname)) + endif +end subroutine check_new_file_freq + +!> @brief This checks if the file duration in a diag file is valid and crashes if it isn't +subroutine check_file_duration(fileobj) + type(diagYamlFiles_type), intent(inout) :: fileobj !< diagYamlFiles_type obj to check + + if (fileobj%file_duration > 0) then + if(trim(fileobj%file_duration_units) .eq. "") & + call mpp_error(FATAL, "file_duration_units is required if using file_duration. & + &Check your entry for file:"//trim(fileobj%file_fname)) + + if (.not. is_valid_time_units(fileobj%file_duration_units)) & + call mpp_error(FATAL, trim(fileobj%file_duration_units)//" is not a valid file_duration_units. & + &The acceptable values are seconds, minuts, hours, days, months, years. & + &Check your entry for file:"//trim(fileobj%file_duration_units)) + endif +end subroutine check_file_duration + +!> @brief This checks if the kind of a diag field is valid and crashes if it isn't +subroutine check_field_kind(field) + type(diagYamlFilesVar_type), intent(in) :: field !< diagYamlFilesVar_type obj to read the contents into + + select case (TRIM(field%var_skind)) + case ("double", "float") + case default + call mpp_error(FATAL, trim(field%var_skind)//" is an invalid kind! & + &The acceptable values are double and float. & + &Check your entry for file:"//trim(field%var_varname)//" in file "//trim(field%var_fname)) + end select + +end subroutine check_field_kind + +!> @brief This checks if the reduction of a diag field is valid and crashes if it isn't +subroutine check_field_reduction(field) + type(diagYamlFilesVar_type), intent(in) :: field !< diagYamlFilesVar_type obj to read the contents into + + integer :: n_diurnal !< number of diurnal samples + integer :: pow_value !< The power value + integer :: ioerror !< io error status after reading in the diurnal samples + + n_diurnal = 0 + pow_value = 0 + ioerror = 0 + if (field%var_reduction(1:7) .eq. "diurnal") then + READ (UNIT=field%var_reduction(8:LEN_TRIM(field%var_reduction)), FMT=*, IOSTAT=ioerror) n_diurnal + if (ioerror .ne. 0) & + call mpp_error(FATAL, "Error getting the number of diurnal samples from "//trim(field%var_reduction)) + if (n_diurnal .le. 0) & + call mpp_error(FATAL, "Diurnal samples should be greater than 0. & + & Check your entry for file:"//trim(field%var_varname)//" in file "//trim(field%var_fname)) + elseif (field%var_reduction(1:3) .eq. "pow") then + READ (UNIT=field%var_reduction(4:LEN_TRIM(field%var_reduction)), FMT=*, IOSTAT=ioerror) pow_value + if (ioerror .ne. 0) & + call mpp_error(FATAL, "Error getting the power value from "//trim(field%var_reduction)) + if (pow_value .le. 0) & + call mpp_error(FATAL, "The power value should be greater than 0. & + & Check your entry for file:"//trim(field%var_varname)//" in file "//trim(field%var_fname)) + else + select case (TRIM(field%var_reduction)) + case ("none", "average", "min", "max", "rms") + case default + call mpp_error(FATAL, trim(field%var_reduction)//" is an invalid reduction method! & + &The acceptable values are none, average, pow##, diurnal##, min, max, and rms. & + &Check your entry for file:"//trim(field%var_varname)//" in file "//trim(field%var_fname)) + end select + endif +end subroutine check_field_reduction + +!> @brief This checks if a time unit is valid +!! @return Flag indicating if the time units are valid +pure function is_valid_time_units(time_units) & +result(is_valid) + character(len=*), intent(in) :: time_units + logical :: is_valid + + select case (TRIM(time_units)) + case ("seconds", "minutes", "hours", "days", "months", "years") + is_valid = .true. + case default + is_valid = .false. + end select +end function is_valid_time_units #endif end module fms_diag_yaml_mod !> @} diff --git a/diag_manager/fms_diag_yaml_object.F90 b/diag_manager/fms_diag_yaml_object.F90 index e406885084..7cc6db38f3 100644 --- a/diag_manager/fms_diag_yaml_object.F90 +++ b/diag_manager/fms_diag_yaml_object.F90 @@ -99,6 +99,7 @@ module fms_diag_yaml_object_mod procedure :: get_file_duration_units procedure :: get_file_varlist procedure :: get_file_global_meta + procedure :: is_global_meta end type diagYamlFiles_type @@ -129,7 +130,7 @@ module fms_diag_yaml_object_mod procedure :: get_var_units procedure :: get_var_write procedure :: get_var_attributes - + procedure :: is_var_attributes end type diagYamlFilesVar_type contains @@ -239,6 +240,15 @@ pure function get_file_global_meta (diag_files_obj) result (res) character (:), allocatable :: res(:,:) !< What is returned res = diag_files_obj%file_global_meta end function get_file_global_meta +!> @brief Inquiry for whether file_global_meta is allocated +!! @return Flag indicating if file_global_meta is allocated +function is_global_meta(diag_files_obj) result(res) + class (diagYamlFiles_type), intent(in) :: diag_files_obj !< The object being inquiried + logical :: res + res = .false. + if (allocated(diag_files_obj%file_global_meta)) & + res = .true. +end function !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! @@ -316,6 +326,15 @@ pure function get_var_attributes(diag_var_obj) result (res) character (len=MAX_STR_LEN), allocatable :: res (:,:) !< What is returned res = diag_var_obj%var_attributes end function get_var_attributes +!> @brief Inquiry for whether var_attributes is allocated +!! @return Flag indicating if var_attributes is allocated +function is_var_attributes(diag_var_obj) result(res) + class (diagYamlFilesVar_type), intent(in) :: diag_var_obj !< The object being inquiried + logical :: res + res = .false. + if (allocated(diag_var_obj%var_attributes)) & + res = .true. +end function is_var_attributes !> @brief Initializes the non string values of a diagYamlFiles_type to its !! default values @@ -325,8 +344,7 @@ subroutine diag_yaml_files_obj_init(obj) obj%file_freq = 0 obj%file_write = .true. obj%file_duration = 0 - obj%file_sub_region%lat_lon_sub_region = -999. - obj%file_sub_region%index_sub_region = -999 + obj%file_new_file_freq = 0 obj%file_sub_region%tile = 0 end subroutine diag_yaml_files_obj_init diff --git a/test_fms/diag_manager/Makefile.am b/test_fms/diag_manager/Makefile.am index 05fd840297..1c55871f00 100644 --- a/test_fms/diag_manager/Makefile.am +++ b/test_fms/diag_manager/Makefile.am @@ -29,11 +29,12 @@ LDADD = $(top_builddir)/libFMS/libFMS.la # Build this test program. check_PROGRAMS = test_diag_manager test_diag_manager_time test_diag_object_container \ - test_diag_dlinked_list + test_diag_dlinked_list test_diag_yaml # This is the source code for the test. test_diag_manager_SOURCES = test_diag_manager.F90 test_diag_manager_time_SOURCES = test_diag_manager_time.F90 +test_diag_yaml_SOURCES = test_diag_yaml.F90 test_diag_object_container_SOURCES = test_diag_object_container.F90 test_diag_dlinked_list_SOURCES = test_diag_dlinked_list.F90 @@ -41,7 +42,14 @@ test_diag_dlinked_list_SOURCES = test_diag_dlinked_list.F90 TESTS = test_diag_manager2.sh # Copy over other needed files to the srcdir -EXTRA_DIST = input.nml_base diagTables test_diag_manager2.sh +EXTRA_DIST = input.nml_base diagTables test_diag_manager2.sh check_crashes.sh -# Clean up -CLEANFILES = input.nml *.nc *.out diag_table +if SKIP_PARSER_TESTS +skipflag="skip" +else +skipflag="" +endif + +TESTS_ENVIRONMENT = parser_skip=${skipflag} + +CLEANFILES = *.yaml input.nml *.nc *.out diag_table diff --git a/test_fms/diag_manager/check_crashes.sh b/test_fms/diag_manager/check_crashes.sh new file mode 100755 index 0000000000..da68fdf81f --- /dev/null +++ b/test_fms/diag_manager/check_crashes.sh @@ -0,0 +1,163 @@ +#!/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 . +#*********************************************************************** + +# This is part of the GFDL FMS package. This is a shell script to +# execute tests in the test_fms/data_override directory. + +# Set common test settings. +. ../test_common.sh + +printf "&check_crashes_nml \n checking_crashes = .true. \n/" | cat > input.nml + +echo "Test 27: Missing tile when using the 'index' grid type" +touch input.nml +sed '/tile/d' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since 'tile' was missing and the 'index' grid type was used" + exit 3 +fi + +echo "Test 28: Missing new_file_freq_units when using new_file_freq_units" +touch input.nml +sed '/new_file_freq_units/d' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since 'new_file_freq_units' was missing and new_file_freq was used" + exit 3 +fi + +echo "Test 29: new_file_freq_units is not valid" +touch input.nml +sed 's/new_file_freq_units: hours/new_file_freq_units: mullions/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since 'new_file_freq_units' is not valid" + exit 3 +fi + +echo "Test 30: Missing file_duration_units when using file_duration" +touch input.nml +sed '/file_duration_units/d' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since 'file_duration_units' was missing and file_duration was used" + exit 3 +fi + +echo "Test 31: file_duration_units is not valid" +touch input.nml +sed 's/file_duration_units: hours/file_duration_units: mullions/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since 'file_duration_units' is not valid" + exit 3 +fi + +echo "Test 32: freq units is not valid" +touch input.nml +sed 's/freq_units: hours/freq_units: mullions/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since the freq units is not valid" + exit 3 +fi + +echo "Test 33: freq is less than 0" +touch input.nml +sed 's/freq: 6/freq: -666/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since freq is not valid" + exit 3 +fi + +echo "Test 34: realm is not valid" +touch input.nml +sed 's/realm: ATM/realm: mullions/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since realm is not valid" + exit 3 +fi + +echo "Test 35: kind is not valid" +touch input.nml +sed 's/kind: float/kind: mullions/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since the kind is not valid" + exit 3 +fi + +echo "Test 36: reduction is not valid" +touch input.nml +sed 's/reduction: average/reduction: mullions/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since the reduction method is not valid" + exit 3 +fi + +echo "Test 37: diurnal samples is less than 0" +touch input.nml +sed 's/reduction: average/reduction: diurnal0/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since the number of diurnal samples is less than 0" + exit 3 +fi + +echo "Test 38: diurnal samples is not an integer" +touch input.nml +sed 's/reduction: average/reduction: diurnal99r/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since the number of diurnal samples is not valid" + exit 3 +fi + +echo "Test 39: power value is less than 0" +touch input.nml +sed 's/reduction: average/reduction: pow0/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since the power value is less than" + exit 3 +fi + +echo "Test 40: power value is not an integer" +touch input.nml +sed 's/reduction: average/reduction: pow99r/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since the power value is not valid" + exit 3 +fi + +echo "Test 41: the sub_region grid_type is not valid" +touch input.nml +sed 's/grid_type: latlon/grid_type: ice_cream/g' $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 > diag_table.yaml +run_test test_diag_yaml 1 $parser_skip && echo "It worked?" +if [ $? -eq 0 ]; then + echo "Test should have failed since the sub_region grid_type" + exit 3 +fi diff --git a/test_fms/diag_manager/diagTables/diag_table_yaml_26 b/test_fms/diag_manager/diagTables/diag_table_yaml_26 new file mode 100644 index 0000000000..d82038bd6a --- /dev/null +++ b/test_fms/diag_manager/diagTables/diag_table_yaml_26 @@ -0,0 +1,61 @@ +title: test_diag_manager +base_date: 2 1 1 0 0 0 +diag_files: +- file_name: wild_card_name%4yr%2mo%2dy%2hr + freq: 6 + freq_units: hours + time_units: hours + unlimdim: time + new_file_freq: 6 + new_file_freq_units: hours + start_time: 2 1 1 0 0 0 + file_duration: 12 + file_duration_units: hours + write_file: false + realm: ATM + varlist: + - module: test_diag_manager_mod + var_name: sst + output_name: sst + reduction: average + kind: float + write_var: false + global_meta: + - is_a_file: true +- file_name: normal + freq: 24 + freq_units: days + time_units: hours + unlimdim: records + varlist: + - module: test_diag_manager_mod + var_name: sst + output_name: sst + reduction: average + kind: float + write_var: true + attributes: + - do_sst: .true. + sub_region: + - grid_type: latlon + dim1_begin: 64.0 + dim3_end: 20.0 +- file_name: normal2 + freq: 24 + freq_units: days + time_units: hours + unlimdim: records + write_file: true + varlist: + - module: test_diag_manager_mod + var_name: sstt + output_name: sstt + reduction: average + kind: float + long_name: S S T + sub_region: + - grid_type: index + tile: 1 + dim2_begin: 10 + dim2_end: 20 + dim1_begin: 10 diff --git a/test_fms/diag_manager/test_diag_manager2.sh b/test_fms/diag_manager/test_diag_manager2.sh index d424ec55d0..355912924a 100755 --- a/test_fms/diag_manager/test_diag_manager2.sh +++ b/test_fms/diag_manager/test_diag_manager2.sh @@ -102,6 +102,13 @@ touch input.nml cp $top_srcdir/test_fms/diag_manager/diagTables/diag_table_25 diag_table run_test test_diag_manager_time 1 +echo "Test 26: diag_yaml_init" +touch input.nml +cp $top_srcdir/test_fms/diag_manager/diagTables/diag_table_yaml_26 diag_table.yaml +run_test test_diag_yaml 1 $parser_skip + +. $top_srcdir/test_fms/diag_manager/check_crashes.sh + echo "Test container" rm -f input.nml diag_table touch input.nml diff --git a/test_fms/diag_manager/test_diag_yaml.F90 b/test_fms/diag_manager/test_diag_yaml.F90 new file mode 100644 index 0000000000..d939de7b91 --- /dev/null +++ b/test_fms/diag_manager/test_diag_yaml.F90 @@ -0,0 +1,311 @@ +!*********************************************************************** +!* 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 . +!*********************************************************************** + +!> @brief This program tests the diag_yaml_object_init and diag_yaml_object_end subroutines +!! in fms_diag_yaml_mod +program test_diag_yaml + +#ifdef use_yaml +use FMS_mod, only: fms_init, fms_end +use fms_diag_yaml_mod +use fms_diag_yaml_object_mod +use mpp_mod +use platform_mod + +implicit none + +!< @brief Interface used to compare two different values +interface compare_result +subroutine compare_result_0d(key_name, res, expected_res) + character(len=*), intent(in) :: key_name !< Name of the key to compare + class(*), intent(in) :: res !< Value obtained from reading the file + class(*), intent(in) :: expected_res !< Value expected +end subroutine compare_result_0d + +subroutine compare_result_1d(key_name, res, expected_res) + character(len=*), intent(in) :: key_name !< Name of the key to compare + class(*), intent(in) :: res(:) !< Value obtained from reading the file + class(*), intent(in) :: expected_res(:) !< Value expected +end subroutine compare_result_1d +end interface compare_result + +type(diagYamlObject_type) :: my_yaml !< diagYamlObject obtained from diag_yaml_object_init +type(diagYamlObject_type) :: ans !< expected diagYamlObject +logical :: checking_crashes = .false.!< Flag indicating that you are checking crashes +integer :: i !< For do loops +integer :: io_status !< The status after reading the input.nml + +type(diagYamlFiles_type), allocatable, dimension (:) :: diag_files !< Files from the diag_yaml +type(diagYamlFilesVar_type), allocatable, dimension(:) :: diag_fields !< Fields from the diag_yaml + +namelist / check_crashes_nml / checking_crashes + +call fms_init() + +read (input_nml_file, check_crashes_nml, iostat=io_status) +if (io_status > 0) call mpp_error(FATAL,'=>check_crashes: Error reading input.nml') + +call diag_yaml_object_init + +my_yaml = get_diag_yaml_obj() + +if (.not. checking_crashes) then + call compare_result("base_date", my_yaml%get_basedate(), (/2, 1, 1, 0, 0 , 0 /)) + call compare_result("title", my_yaml%get_title(), "test_diag_manager") + + diag_files = my_yaml%get_diag_files() + call compare_result("nfiles", size(diag_files), 3) + call compare_diag_files(diag_files) + + diag_fields = my_yaml%get_diag_fields() + call compare_result("nfields", size(diag_fields), 3) + call compare_diag_fields(diag_fields) + +endif +deallocate(diag_files) +deallocate(diag_fields) + +call diag_yaml_object_end + +call fms_end() + +contains + +!> @brief Compares a diagYamlFilesVar_type with the expected result and +!! crashes if they don't match +subroutine compare_diag_fields(res) + type(diagYamlFilesVar_type), intent(in) :: res(:) !< diag_field info read from yaml file + character (len=255), dimension(:, :), allocatable :: var_attributes !< Variable attributes + + call compare_result("var_fname 1", res(1)%get_var_fname(), "wild_card_name%4yr%2mo%2dy%2hr") + call compare_result("var_fname 2", res(2)%get_var_fname(), "normal") + call compare_result("var_fname 3", res(3)%get_var_fname(), "normal2") + + call compare_result("var_varname 1", res(1)%get_var_varname(), "sst") + call compare_result("var_varname 2", res(2)%get_var_varname(), "sst") + call compare_result("var_varname 3", res(3)%get_var_varname(), "sstt") + + call compare_result("var_reduction 1", res(1)%get_var_reduction(), "average") + call compare_result("var_reduction 2", res(2)%get_var_reduction(), "average") + call compare_result("var_reduction 3", res(3)%get_var_reduction(), "average") + + call compare_result("var_module 1", res(1)%get_var_module(), "test_diag_manager_mod") + call compare_result("var_module 2", res(2)%get_var_module(), "test_diag_manager_mod") + call compare_result("var_module 3", res(3)%get_var_module(), "test_diag_manager_mod") + + call compare_result("var_skind 1", res(1)%get_var_skind(), "float") + call compare_result("var_skind 2", res(2)%get_var_skind(), "float") + call compare_result("var_skind 3", res(3)%get_var_skind(), "float") + + call compare_result("var_write 1", res(1)%get_var_write(), .false.) + call compare_result("var_write 2", res(2)%get_var_write(), .true.) + call compare_result("var_write 3", res(3)%get_var_write(), .true.) + + call compare_result("var_outname 1", res(1)%get_var_outname(), "sst") + call compare_result("var_outname 2", res(2)%get_var_outname(), "sst") + call compare_result("var_outname 3", res(3)%get_var_outname(), "sstt") + + call compare_result("var_longname 1", res(1)%get_var_longname(), "") + call compare_result("var_longname 2", res(2)%get_var_longname(), "") + call compare_result("var_longname 3", res(3)%get_var_longname(), "S S T") + + if (res(1)%is_var_attributes()) call mpp_error(FATAL, "The variable attributes for the first file was set?") + + var_attributes = res(2)%get_var_attributes() + if (.not. allocated(var_attributes)) call mpp_error(FATAL, "The variable attributes for the second file was not set") + call compare_result("var attributes key", var_attributes(1,1), "do_sst") + call compare_result("var attributes value", var_attributes(1,2), ".true.") + deallocate(var_attributes) + + if (res(3)%is_var_attributes()) call mpp_error(FATAL, "The variable attributes for the third file was set?") + +end subroutine + +!> @brief Compares a diagYamlFiles_type with the expected result and +!! crashes if they don't match +subroutine compare_diag_files(res) + type(diagYamlFiles_type), intent(in) :: res(:) !< diag_file info read from yaml file + + character (len=255), dimension(:), allocatable :: varlist !< List of variables + character (len=255), dimension(:, :), allocatable :: global_meta !< List of global meta + + call compare_result("file_fname 1", res(1)%get_file_fname(), "wild_card_name%4yr%2mo%2dy%2hr") + call compare_result("file_fname 2", res(2)%get_file_fname(), "normal") + call compare_result("file_fname 3", res(3)%get_file_fname(), "normal2") + + call compare_result("file_freq 1", res(1)%get_file_freq(), 6) + call compare_result("file_freq 2", res(2)%get_file_freq(), 24) + call compare_result("file_freq 3", res(3)%get_file_freq(), 24) + + call compare_result("file_frequnit 1", res(1)%get_file_frequnit(), "hours") + call compare_result("file_frequnit 2", res(2)%get_file_frequnit(), "days") + call compare_result("file_frequnit 3", res(3)%get_file_frequnit(), "days") + + call compare_result("file_timeunit 1", res(1)%get_file_timeunit(), "hours") + call compare_result("file_timeunit 2", res(2)%get_file_timeunit(), "hours") + call compare_result("file_timeunit 3", res(3)%get_file_timeunit(), "hours") + + call compare_result("file_unlimdim 1", res(1)%get_file_unlimdim(), "time") + call compare_result("file_unlimdim 2", res(2)%get_file_unlimdim(), "records") + call compare_result("file_unlimdim 3", res(3)%get_file_unlimdim(), "records") + + call compare_result("file_realm 1", res(1)%get_file_realm(), "ATM") + call compare_result("file_realm 2", res(2)%get_file_realm(), "") + call compare_result("file_realm 3", res(3)%get_file_realm(), "") + + call compare_result("file_write 1", res(1)%get_file_write(), .false.) + call compare_result("file_write 2", res(2)%get_file_write(), .true.) + call compare_result("file_write 3", res(3)%get_file_write(), .true.) + + call compare_result("file_new_file_freq 1", res(1)%get_file_new_file_freq(), 6) + call compare_result("file_new_file_freq 2", res(2)%get_file_new_file_freq(), 0) + call compare_result("file_new_file_freq 3", res(3)%get_file_new_file_freq(), 0) + + call compare_result("file_new_file_freq_units 1", res(1)%get_file_new_file_freq_units(), "hours") + call compare_result("file_new_file_freq_units 2", res(2)%get_file_new_file_freq_units(), "") + call compare_result("file_new_file_freq_units 3", res(3)%get_file_new_file_freq_units(), "") + + call compare_result("file_duration 1", res(1)%get_file_duration(), 12) + call compare_result("file_duration 2", res(2)%get_file_duration(), 0) + call compare_result("file_duration 3", res(3)%get_file_duration(), 0) + + call compare_result("file_duration_units 1", res(1)%get_file_duration_units(), "hours") + call compare_result("file_duration_units 2", res(2)%get_file_duration_units(), "") + call compare_result("file_duration_units 3", res(3)%get_file_duration_units(), "") + + call compare_result("file_start_time 1", res(1)%get_file_start_time(), "2 1 1 0 0 0") + call compare_result("file_start_time 2", res(2)%get_file_start_time(), "") + call compare_result("file_start_time 3", res(3)%get_file_start_time(), "") + + varlist = res(1)%get_file_varlist() + if (.not. allocated(varlist)) call mpp_error(FATAL, "The varlist for the first file was not set") + call compare_result("number_variables 1", size(varlist), 1) + call compare_result("varlist 1", varlist(1), "sst") + deallocate(varlist) + + varlist = res(2)%get_file_varlist() + if (.not. allocated(varlist)) call mpp_error(FATAL, "The varlist for the first file was not set") + call compare_result("number_variables 2", size(varlist), 1) + call compare_result("varlist 2", varlist(1), "sst") + deallocate(varlist) + + varlist = res(3)%get_file_varlist() + if (.not. allocated(varlist)) call mpp_error(FATAL, "The varlist for the first file was not set") + call compare_result("number_variables 3", size(varlist), 1) + call compare_result("varlist 3", varlist(1), "sstt") + deallocate(varlist) + + global_meta= res(1)%get_file_global_meta() + if (.not. allocated(global_meta)) call mpp_error(FATAL, "The global meta for the first file was not set") + call compare_result("attributes key", global_meta(1,1), "is_a_file") + call compare_result("attributes value", global_meta(1,2), "true") + deallocate(global_meta) + + if (res(2)%is_global_meta()) call mpp_error(FATAL, "The global meta for the second file was set?") + if (res(3)%is_global_meta()) call mpp_error(FATAL, "The global meta for the third file was set?") + +end subroutine compare_diag_files + +#endif +end program test_diag_yaml + +#ifdef use_yaml +!< @brief Compare a key value with the expected result +subroutine compare_result_0d(key_name, res, expected_res) + use platform_mod + use mpp_mod + character(len=*), intent(in) :: key_name !< Name of the key to compare + class(*), intent(in) :: res !< Value obtained from reading the file + class(*), intent(in) :: expected_res !< Value expected + + print *, "Comparing ", trim(key_name) + select type(res) + type is(character(len=*)) + select type(expected_res) + type is(character(len=*)) + if(trim(res) .ne. trim(expected_res)) & + call mpp_error(FATAL, "Error!: "//trim(key_name)//" is not the expected result. "//trim(res)//" ne "//& + trim(expected_res)//".") + end select + type is (integer(kind=i4_kind)) + select type(expected_res) + type is(integer(kind=i4_kind)) + if (res .ne. expected_res) then + print *, res, " ne ", expected_res + call mpp_error(FATAL, "Error!: "//trim(key_name)//" is not the expected result.") + endif + end select + type is (logical) + select type(expected_res) + type is(logical) + if ((res .and. .not. expected_res) .or. (.not. res .and. expected_res)) then + print*, res, " ne ", expected_res + call mpp_error(FATAL, "Error!:"//trim(key_name)//" is not the expected result") + endif + end select + end select + +end subroutine compare_result_0d + +!< @brief Compare a 1d key value with the expected result +subroutine compare_result_1d(key_name, res, expected_res) + use platform_mod + use mpp_mod + character(len=*), intent(in) :: key_name !< Name of the key to compare + class(*), intent(in) :: res(:) !< Value obtained from reading the file + class(*), intent(in) :: expected_res(:) !< Value expected + + integer :: i + + print *, "Comparing ", trim(key_name) + + select type(res) + type is (integer(kind=i4_kind)) + select type(expected_res) + type is (integer(kind=i4_kind)) + do i = 1, size(res,1) + if( res(i) .ne. expected_res(i)) then + print *, res, " ne ", expected_res + call mpp_error(FATAL, "Error!: "//trim(key_name)//" is not the expected result. ") + endif + enddo + end select + type is (real(kind=r4_kind)) + select type(expected_res) + type is (real(kind=r4_kind)) + do i = 1, size(res,1) + if( res(i) .ne. expected_res(i)) then + print *, res, " ne ", expected_res + call mpp_error(FATAL, "Error!: "//trim(key_name)//" is not the expected result. ") + endif + enddo + end select + type is (real(kind=r8_kind)) + select type(expected_res) + type is (real(kind=r8_kind)) + do i = 1, size(res,1) + if( res(i) .ne. expected_res(i)) then + print *, res, " ne ", expected_res + call mpp_error(FATAL, "Error!: "//trim(key_name)//" is not the expected result. ") + endif + enddo + end select + end select +end subroutine compare_result_1d +#endif