From b554b328c83e1f1cb9ff1b51e8a7bf788def40bb Mon Sep 17 00:00:00 2001 From: uramirez8707 <49168881+uramirez8707@users.noreply.github.com> Date: Fri, 14 Jun 2024 13:07:53 -0400 Subject: [PATCH] fix: modern_diag_manager use the correct date in file name for static files (#1540) --- diag_manager/diag_manager.F90 | 2 +- diag_manager/fms_diag_file_object.F90 | 29 +++-- diag_manager/fms_diag_object.F90 | 13 +- test_fms/diag_manager/Makefile.am | 7 +- test_fms/diag_manager/test_prepend_date.F90 | 124 ++++++++++++++++++++ test_fms/diag_manager/test_prepend_date.sh | 87 ++++++++++++++ 6 files changed, 244 insertions(+), 18 deletions(-) create mode 100644 test_fms/diag_manager/test_prepend_date.F90 create mode 100755 test_fms/diag_manager/test_prepend_date.sh diff --git a/diag_manager/diag_manager.F90 b/diag_manager/diag_manager.F90 index eeab1a522..ab94fe690 100644 --- a/diag_manager/diag_manager.F90 +++ b/diag_manager/diag_manager.F90 @@ -4210,7 +4210,7 @@ SUBROUTINE diag_manager_init(diag_model_subset, time_init, err_msg) END IF if (use_modern_diag) then - CALL fms_diag_object%init(diag_subset_output) + CALL fms_diag_object%init(diag_subset_output, time_init) endif if (.not. use_modern_diag) then CALL parse_diag_table(DIAG_SUBSET=diag_subset_output, ISTAT=mystat, ERR_MSG=err_msg_local) diff --git a/diag_manager/fms_diag_file_object.F90 b/diag_manager/fms_diag_file_object.F90 index 060dfd8c3..612e080db 100644 --- a/diag_manager/fms_diag_file_object.F90 +++ b/diag_manager/fms_diag_file_object.F90 @@ -35,7 +35,7 @@ module fms_diag_file_object_mod get_base_second, time_unit_list, time_average, time_rms, time_max, time_min, time_sum, & time_diurnal, time_power, time_none, avg_name, no_units, pack_size_str, & middle_time, begin_time, end_time, MAX_STR_LEN, index_gridtype, latlon_gridtype, & - null_gridtype, flush_nc_files + null_gridtype, flush_nc_files, diag_init_time use time_manager_mod, only: time_type, operator(>), operator(/=), operator(==), get_date, get_calendar_type, & VALID_CALENDAR_TYPES, operator(>=), date_to_string, & OPERATOR(/), OPERATOR(+), operator(<) @@ -259,9 +259,13 @@ logical function fms_diag_files_object_init (files_array) !> Set the start_time of the file to the base_time and set up the *_output variables obj%done_writing_data = .false. - obj%start_time = get_base_time() - obj%last_output = get_base_time() - obj%model_time = get_base_time() + + !! Set this to the time passed in to diag_manager_init + !! This will be the base_time if nothing was passed in + !! This time is appended to the filename if the prepend_date namelist is .True. + obj%start_time = diag_init_time + obj%last_output = diag_init_time + obj%model_time = diag_init_time obj%next_output = diag_time_inc(obj%start_time, obj%get_file_freq(), obj%get_file_frequnit()) obj%next_next_output = diag_time_inc(obj%next_output, obj%get_file_freq(), obj%get_file_frequnit()) @@ -1003,20 +1007,21 @@ end subroutine define_new_subaxis !! So it needs to make sure that the start_time is the same for each variable. The initial value is the base_time subroutine add_start_time(this, start_time) class(fmsDiagFile_type), intent(inout) :: this !< The file object - TYPE(time_type), intent(in) :: start_time !< Start time to add to the fileobj + TYPE(time_type), intent(in) :: start_time !< Start time passed into register_diag_field - !< If the start_time sent in is equal to the base_time return because - !! this%start_time was already set to the base_time - if (start_time .eq. get_base_time()) return + !< If the start_time sent in is equal to the diag_init_time return because + !! this%start_time was already set to the diag_init_time + if (start_time .eq. diag_init_time) return - if (this%start_time .ne. get_base_time()) then - !> If the this%start_time is not equal to the base_time from the diag_table - !! this%start_time was already updated so make sure it is the same or error out + if (this%start_time .ne. diag_init_time) then + !> If the this%start_time is not equal to the diag_init_time from the diag_table + !! this%start_time was already updated so make sure it is the same for the current variable + !! or error out if (this%start_time .ne. start_time)& call mpp_error(FATAL, "The variables associated with the file:"//this%get_file_fname()//" have"& &" different start_time") else - !> If the this%start_time is equal to the base_time, + !> If the this%start_time is equal to the diag_init_time, !! simply update it with the start_time and set up the *_output variables this%model_time = start_time this%start_time = start_time diff --git a/diag_manager/fms_diag_object.F90 b/diag_manager/fms_diag_object.F90 index 70141a007..a1fc92cc3 100644 --- a/diag_manager/fms_diag_object.F90 +++ b/diag_manager/fms_diag_object.F90 @@ -22,7 +22,7 @@ module fms_diag_object_mod &DIAG_FIELD_NOT_FOUND, diag_not_registered, max_axes, TWO_D_DOMAIN, & &get_base_time, NULL_AXIS_ID, get_var_type, diag_not_registered, & &time_none, time_max, time_min, time_sum, time_average, time_diurnal, & - &time_power, time_rms, r8, NO_DOMAIN + &time_power, time_rms, r8, NO_DOMAIN, diag_init_time USE time_manager_mod, ONLY: set_time, set_date, get_time, time_type, OPERATOR(>=), OPERATOR(>),& & OPERATOR(<), OPERATOR(==), OPERATOR(/=), OPERATOR(/), OPERATOR(+), ASSIGNMENT(=), get_date, & @@ -118,14 +118,23 @@ module fms_diag_object_mod !! Reads the diag_table.yaml and fills in the yaml object !! Allocates the diag manager object arrays for files, fields, and buffers !! Initializes variables -subroutine fms_diag_object_init (this,diag_subset_output) +subroutine fms_diag_object_init (this,diag_subset_output, time_init) class(fmsDiagObject_type) :: this !< Diag mediator/controller object integer :: diag_subset_output !< Subset of the diag output? + INTEGER, DIMENSION(6), OPTIONAL, INTENT(IN) :: time_init !< Model time diag_manager initialized + #ifdef use_yaml if (this%initialized) return ! allocate(diag_objs(get_num_unique_fields())) CALL diag_yaml_object_init(diag_subset_output) + + !! Doing this here, because the base_time is not set until the yaml is parsed + !! if time_init is present, it will be set in diag_manager_init + if (.not. present(time_init)) then + diag_init_time = get_base_time() + endif + this%axes_initialized = fms_diag_axis_object_init(this%diag_axis) this%files_initialized = fms_diag_files_object_init(this%FMS_diag_files) this%fields_initialized = fms_diag_fields_object_init(this%FMS_diag_fields) diff --git a/test_fms/diag_manager/Makefile.am b/test_fms/diag_manager/Makefile.am index df5a8a19f..a224eb245 100644 --- a/test_fms/diag_manager/Makefile.am +++ b/test_fms/diag_manager/Makefile.am @@ -34,7 +34,7 @@ check_PROGRAMS = test_diag_manager test_diag_manager_time \ check_time_min check_time_max check_time_sum check_time_avg test_diag_diurnal check_time_diurnal \ check_time_pow check_time_rms check_subregional test_cell_measures test_var_masks \ check_var_masks test_multiple_send_data test_diag_out_yaml test_output_every_freq \ - test_dm_weights + test_dm_weights test_prepend_date # This is the source code for the test. test_output_every_freq_SOURCES = test_output_every_freq.F90 @@ -64,6 +64,7 @@ check_subregional_SOURCES = check_subregional.F90 test_var_masks_SOURCES = test_var_masks.F90 check_var_masks_SOURCES = check_var_masks.F90 test_multiple_send_data_SOURCES = test_multiple_send_data.F90 +test_prepend_date_SOURCES = test_prepend_date.F90 TEST_EXTENSIONS = .sh SH_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ @@ -73,7 +74,7 @@ SH_LOG_DRIVER = env AM_TAP_AWK='$(AWK)' $(SHELL) \ TESTS = test_diag_manager2.sh test_time_none.sh test_time_min.sh test_time_max.sh test_time_sum.sh \ test_time_avg.sh test_time_pow.sh test_time_rms.sh test_time_diurnal.sh test_cell_measures.sh \ test_subregional.sh test_var_masks.sh test_multiple_send_data.sh test_output_every_freq.sh \ - test_dm_weights.sh test_flush_nc_file.sh + test_dm_weights.sh test_flush_nc_file.sh test_prepend_date.sh testing_utils.mod: testing_utils.$(OBJEXT) @@ -81,7 +82,7 @@ testing_utils.mod: testing_utils.$(OBJEXT) EXTRA_DIST = test_diag_manager2.sh check_crashes.sh test_time_none.sh test_time_min.sh test_time_max.sh \ test_time_sum.sh test_time_avg.sh test_time_pow.sh test_time_rms.sh test_time_diurnal.sh \ test_cell_measures.sh test_subregional.sh test_var_masks.sh test_multiple_send_data.sh \ - test_flush_nc_file.sh test_dm_weights.sh test_output_every_freq.sh + test_flush_nc_file.sh test_dm_weights.sh test_output_every_freq.sh test_prepend_date.sh if USING_YAML skipflag="" diff --git a/test_fms/diag_manager/test_prepend_date.F90 b/test_fms/diag_manager/test_prepend_date.F90 new file mode 100644 index 000000000..24a5ae298 --- /dev/null +++ b/test_fms/diag_manager/test_prepend_date.F90 @@ -0,0 +1,124 @@ +!*********************************************************************** +!* 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 programs tests diag manager when the init date is prepended to the file name +program test_prepend_date + + use fms_mod, only: fms_init, fms_end, string + use diag_manager_mod, only: diag_axis_init, send_data, diag_send_complete, diag_manager_set_time_end, & + register_diag_field, diag_manager_init, diag_manager_end, register_static_field, & + diag_axis_init + use time_manager_mod, only: time_type, operator(+), JULIAN, set_time, set_calendar_type, set_date + use mpp_mod, only: FATAL, mpp_error, input_nml_file + use fms2_io_mod, only: FmsNetcdfFile_t, open_file, close_file, read_data, get_dimension_size + use platform_mod, only: r4_kind + + implicit none + + integer :: id_var0, id_var2, id_var1 !< diag field ids + integer :: id_axis1 !< Id for axis + logical :: used !< for send_data calls + integer :: ntimes = 48 !< Number of time steps + real :: vdata !< Buffer to store the data + type(time_type) :: Time !< "Model" time + type(time_type) :: Time_step !< Time step for the "simulation" + integer :: i !< For do loops + logical :: pass_diag_time = .True. !< .True. if passing the time to diag_manager_init + + integer :: io_status !< Status when reading the namelist + + namelist / test_prepend_date_nml / pass_diag_time + + call fms_init + + read (input_nml_file, test_prepend_date_nml, iostat=io_status) + if (io_status > 0) call mpp_error(FATAL,'=>test_prepend_date: Error reading input.nml') + + call set_calendar_type(JULIAN) + + ! This is going to be different from the base_date + if (pass_diag_time) then + call diag_manager_init(time_init=(/2, 1, 1, 0, 0, 0/)) + else + call diag_manager_init() + endif + + Time = set_date(2,1,1,0,0,0) + Time_step = set_time (3600,0) !< 1 hour + call diag_manager_set_time_end(set_date(2,1,3,0,0,0)) + + id_axis1 = diag_axis_init('dummy_axis', (/real(1.)/), "mullions", "X") + id_var0 = register_diag_field ('ocn_mod', 'var0', Time) + id_var2 = register_static_field ('ocn_mod', 'var2', (/id_axis1/)) + + ! This is a different start_time, should lead to a crash if the variable is in the diag table yaml + id_var1 = register_diag_field ('ocn_mod', 'var1', set_date(2,1,6,0,0,0)) + + used = send_data(id_var2, real(123.456)) + do i = 1, ntimes + Time = Time + Time_step + vdata = real(i) + + used = send_data(id_var0, vdata, Time) !< Sending data every hour! + + call diag_send_complete(Time_step) + enddo + + call diag_manager_end(Time) + + call check_output() + call fms_end + + contains + + !< @brief Check the diag manager output + subroutine check_output() + type(FmsNetcdfFile_t) :: fileobj !< Fms2io fileobj + integer :: var_size !< Size of the variable reading + real(kind=r4_kind), allocatable :: var_data(:) !< Buffer to read variable data to + integer :: j !< For looping + + if (.not. open_file(fileobj, "00020101.test_non_static.nc", "read")) & + call mpp_error(FATAL, "Error opening file:00020101.test_non_static.nc to read") + + call get_dimension_size(fileobj, "time", var_size) + if (var_size .ne. 48) call mpp_error(FATAL, "The dimension of time in the file:test_0days is not the "//& + "correct size!") + allocate(var_data(var_size)) + var_data = -999.99 + + call read_data(fileobj, "var0", var_data) + do j = 1, var_size + if (var_data(j) .ne. real(j, kind=r4_kind)) call mpp_error(FATAL, "The variable data for var1 at time level:"//& + string(j)//" is not the correct value!") + enddo + + call close_file(fileobj) + + if (.not. open_file(fileobj, "00020101.test_static.nc", "read")) & + call mpp_error(FATAL, "Error opening file:00020101.test_static.nc to read") + + call read_data(fileobj, "var2", var_data(1)) + if (var_data(1) .ne. real(123.456, kind=r4_kind)) call mpp_error(FATAL, & + "The variable data for var2 is not the correct value!") + + call close_file(fileobj) + + end subroutine check_output +end program test_prepend_date diff --git a/test_fms/diag_manager/test_prepend_date.sh b/test_fms/diag_manager/test_prepend_date.sh new file mode 100755 index 000000000..13bbf7c77 --- /dev/null +++ b/test_fms/diag_manager/test_prepend_date.sh @@ -0,0 +1,87 @@ +#!/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 . +#*********************************************************************** + +# Set common test settings. +. ../test-lib.sh + +if [ -z "${skipflag}" ]; then +# create and enter directory for in/output files +output_dir + +cat <<_EOF > diag_table.yaml +title: test_prepend_date +base_date: 1 1 1 0 0 0 +diag_files: +- file_name: test_non_static + time_units: hours + unlimdim: time + freq: 1 hours + varlist: + - module: ocn_mod + var_name: var0 + reduction: average + kind: r4 +- file_name: test_static + time_units: hours + unlimdim: time + freq: -1 hours + varlist: + - module: ocn_mod + var_name: var2 + reduction: none + kind: r4 +_EOF + +# remove any existing files that would result in false passes during checks +rm -f *.nc +my_test_count=1 +printf "&diag_manager_nml \n use_modern_diag=.true. \n/" | cat > input.nml +test_expect_success "Running diag_manager and checking that the date was prepended correctly (test $my_test_count)" ' + mpirun -n 1 ../test_prepend_date +' + +cat <<_EOF > diag_table.yaml +title: test_prepend_date +base_date: 1 1 1 0 0 0 +diag_files: +- file_name: test_non_static + time_units: hours + unlimdim: time + freq: 1 hours + varlist: + - module: ocn_mod + var_name: var0 + reduction: average + kind: r4 + - module: ocn_mod + var_name: var1 + reduction: average + kind: r4 +_EOF + +printf "&diag_manager_nml \n use_modern_diag=.true. \n/ \n &test_prepend_date_nml \n pass_diag_time=.false. \n /" | cat > input.nml + +test_expect_failure "Running diag_manager with fields that have a different start time (test $my_test_count)" ' + mpirun -n 1 ../test_prepend_date +' + +fi +test_done