Skip to content

Commit

Permalink
feat: Add flexible timing feature in modern diag_manager (NOAA-GFDL#1077
Browse files Browse the repository at this point in the history
)
  • Loading branch information
uramirez8707 authored and rem1776 committed May 1, 2024
1 parent 5c370b5 commit e7fd8f0
Show file tree
Hide file tree
Showing 10 changed files with 615 additions and 174 deletions.
6 changes: 6 additions & 0 deletions diag_manager/diag_data.F90
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ MODULE diag_data_mod
INTEGER, PARAMETER :: time_sum = 5 !< The reudction method is sum
INTEGER, PARAMETER :: time_diurnal = 6 !< The reduction method is diurnal
INTEGER, PARAMETER :: time_power = 7 !< The reduction method is power
CHARACTER(len=7) :: avg_name = 'average' !< Name of the average fields
CHARACTER(len=8) :: no_units = "NO UNITS"!< String indicating that the variable has no units
!> @}

!> @brief Contains the coordinates of the local domain to output.
Expand Down Expand Up @@ -378,6 +380,10 @@ MODULE diag_data_mod
!! <TT>.TRUE.</TT> is only supported if the diag_manager_init
!! routine is called with the optional time_init parameter.
LOGICAL :: use_modern_diag = .false. !< Namelist flag to use the modernized diag_manager code
LOGICAL :: use_clock_average = .false. !< .TRUE. if the averaging of variable is done based on the clock
!! For example, if doing daily averages and your start the simulation in
!! day1_hour3, it will do the average between day1_hour3 to day2_hour 0
!! the default behavior will do the average between day1 hour3 to day2 hour3
! <!-- netCDF variable -->

REAL :: FILL_VALUE = NF_FILL_REAL !< Fill value used. Value will be <TT>NF90_FILL_REAL</TT> if using the
Expand Down
13 changes: 9 additions & 4 deletions diag_manager/diag_manager.F90
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ MODULE diag_manager_mod
& get_ticks_per_second
USE mpp_mod, ONLY: mpp_get_current_pelist, mpp_pe, mpp_npes, mpp_root_pe, mpp_sum

USE mpp_mod, ONLY: input_nml_file
USE mpp_mod, ONLY: input_nml_file, mpp_error

USE fms_mod, ONLY: error_mesg, FATAL, WARNING, NOTE, stdout, stdlog, write_version_number,&
& fms_error_handler, check_nml_error, lowercase
Expand All @@ -231,7 +231,7 @@ MODULE diag_manager_mod
& use_cmor, issue_oor_warnings, oor_warnings_fatal, oor_warning, pack_size,&
& max_out_per_in_field, flush_nc_files, region_out_use_alt_value, max_field_attributes, output_field_type,&
& max_file_attributes, max_axis_attributes, prepend_date, DIAG_FIELD_NOT_FOUND, diag_init_time,diag_data_init,&
& use_modern_diag, diag_null
& use_modern_diag, use_clock_average, diag_null

USE diag_data_mod, ONLY: fileobj, fileobjU, fnum_for_domain, fileobjND
USE diag_table_mod, ONLY: parse_diag_table
Expand Down Expand Up @@ -3923,7 +3923,8 @@ SUBROUTINE diag_manager_init(diag_model_subset, time_init, err_msg)
& max_input_fields, max_axes, do_diag_field_log, write_bytes_in_file, debug_diag_manager,&
& max_num_axis_sets, max_files, use_cmor, issue_oor_warnings,&
& oor_warnings_fatal, max_out_per_in_field, flush_nc_files, region_out_use_alt_value, max_field_attributes,&
& max_file_attributes, max_axis_attributes, prepend_date, field_log_separator, use_modern_diag
& max_file_attributes, max_axis_attributes, prepend_date, field_log_separator, use_modern_diag, &
& use_clock_average

! If the module was already initialized do nothing
IF ( module_is_initialized ) RETURN
Expand Down Expand Up @@ -3977,6 +3978,10 @@ SUBROUTINE diag_manager_init(diag_model_subset, time_init, err_msg)
END IF
END IF

IF (.not. use_modern_diag .and. use_clock_average) &
call mpp_error(FATAL, "diag_manager_mod: You cannot set use_modern_diag=.false. and &
& use_clock_average=.true. in diag_manager_nml")

IF ( mpp_pe() == mpp_root_pe() ) THEN
WRITE (stdlog_unit, diag_manager_nml)
END IF
Expand Down Expand Up @@ -4037,7 +4042,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)
endif
if (.not. use_modern_diag) then
CALL parse_diag_table(DIAG_SUBSET=diag_subset_output, ISTAT=mystat, ERR_MSG=err_msg_local)
Expand Down
215 changes: 173 additions & 42 deletions diag_manager/fms_diag_file_object.F90

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions diag_manager/fms_diag_object.F90
Original file line number Diff line number Diff line change
Expand Up @@ -456,9 +456,11 @@ subroutine fms_diag_send_complete(this, time_step)

if (diag_file%is_time_to_write(time_step)) then
call diag_file%increase_unlimited_dimension()
call diag_file%write_time_data(time_step)
!TODO call diag_file%add_variable_data()
call diag_file%write_time_data()
!TODO call diag_file%add_variable_data()
call diag_file%update_next_write(time_step)
call diag_file%update_current_new_file_freq_index(time_step)
if (diag_file%is_time_to_close_file(time_step)) call diag_file%close_diag_file
endif
enddo
#endif
Expand Down
109 changes: 94 additions & 15 deletions diag_manager/fms_diag_time_utils.F90
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
module fms_diag_time_utils_mod

use time_manager_mod, only: time_type, increment_date, increment_time, get_calendar_type, NO_CALENDAR, leap_year, &
get_date, get_time, operator(>), operator(<), operator(-)
get_date, get_time, operator(>), operator(<), operator(-), set_date
use diag_data_mod, only: END_OF_RUN, EVERY_TIME, DIAG_SECONDS, DIAG_MINUTES, DIAG_HOURS, DIAG_DAYS, DIAG_MONTHS, &
DIAG_YEARS
DIAG_YEARS, use_clock_average
USE constants_mod, ONLY: SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE
use fms_mod, only: fms_error_handler
use mpp_mod, only: mpp_error, FATAL
Expand All @@ -52,65 +52,144 @@ TYPE(time_type) FUNCTION diag_time_inc(time, output_freq, output_units, err_msg)
!! An empty string indicates the next output
!! time was found successfully.

if (use_clock_average) then
diag_time_inc = diag_clock_time_inc(time, output_freq, output_units, err_msg)
else
diag_time_inc = diag_forecast_time_inc(time, output_freq, output_units, err_msg)
endif
end function diag_time_inc

!> @brief Determine the next time data/file is to be written based on the frequency and units using the clock.
!! For example, if doing daily averages and the input time is day1_hour3, the output time will be day2_hour0.
!! @return the next time data/file is to be written
TYPE(time_type) FUNCTION diag_clock_time_inc(time, output_freq, output_units, err_msg)
TYPE(time_type), INTENT(in) :: time !< Current model time.
INTEGER, INTENT(in) :: output_freq !< Output frequency number value.
INTEGER, INTENT(in) :: output_units !< Output frequency unit.
CHARACTER(len=*), INTENT(out), OPTIONAL :: err_msg !< Function error message.
!! An empty string indicates the next output
!! time was found successfully.
CHARACTER(len=128) :: error_message_local !< Local variable to store the error_message
integer :: cyear !< The current year stored in the time type
integer :: cmonth !< The current month stored in the time type
integer :: cday !< The current day stored in the time type
integer :: chour !< The current hour stored in the time type
integer :: cmin !< The current minute stored in the time type
integer :: csecond !< The current second stored in the time type
type(time_type) :: my_time !< Time set at the begining of the <output_freq>

IF ( PRESENT(err_msg) ) err_msg = ''
error_message_local = ''

IF ( get_calendar_type() == NO_CALENDAR) then
error_message_local = 'If using use_clock_average =.TRUE., your calendar must be set.'
IF ( fms_error_handler('diag_clock_time_inc',error_message_local,err_msg) ) RETURN
endif

! special values for output frequency are -1 for output at end of run
! and 0 for every timestep. Need to check for these here?
! Return zero time increment, hopefully this value is never used
IF ( output_freq == END_OF_RUN .OR. output_freq == EVERY_TIME ) THEN
diag_time_inc = time
diag_clock_time_inc = time
RETURN
END IF

call get_date(Time, cyear, cmonth, cday, chour, cmin, csecond)

select case (output_units)
case (DIAG_SECONDS)
my_time = set_date(cyear, cmonth, cday, chour, cmin, csecond) !< set my_time to the begining of the hour
diag_clock_time_inc = increment_date(my_time, 0, 0, 0, 0, 0, output_freq, err_msg=error_message_local)
case (DIAG_MINUTES)
my_time = set_date(cyear, cmonth, cday, chour, cmin, 0) !< set my_time to the begining of the hour
diag_clock_time_inc = increment_date(my_time, 0, 0, 0, 0, output_freq, 0, err_msg=error_message_local)
case (DIAG_HOURS)
my_time = set_date(cyear, cmonth, cday, chour, 0, 0) !< set my_time to the begining of the hour
diag_clock_time_inc = increment_date(my_time, 0, 0, 0, output_freq, 0, 0, err_msg=error_message_local)
case (DIAG_DAYS)
my_time = set_date(cyear, cmonth, cday, 0, 0, 0) !< set my_time to the begining of the day
diag_clock_time_inc = increment_date(my_time, 0, 0, output_freq, 0, 0, 0, err_msg=error_message_local)
case (DIAG_MONTHS)
my_time = set_date(cyear, cmonth, 1, 0, 0, 0) !< set my_time to the begining of the month
diag_clock_time_inc = increment_date(my_time, 0, output_freq, 0, 0, 0, 0, err_msg=error_message_local)
case (DIAG_YEARS)
my_time = set_date(cyear, 1, 1, 0, 0, 0) !< set my_time to the begining of the year
diag_clock_time_inc = increment_date(my_time, output_freq, 0, 0, 0, 0, 0, err_msg=error_message_local)
end select

end function diag_clock_time_inc

!> @brief Determine the next time data/file is to be written based on the frequency and units using forecast time.
!! For example, if doing daily averages and the input time is day1_hour3, the output time will be day2_hour3.
!! @return the next time data/file is to be written
TYPE(time_type) FUNCTION diag_forecast_time_inc(time, output_freq, output_units, err_msg)
TYPE(time_type), INTENT(in) :: time !< Current model time.
INTEGER, INTENT(in) :: output_freq !< Output frequency number value.
INTEGER, INTENT(in) :: output_units !< Output frequency unit.
CHARACTER(len=*), INTENT(out), OPTIONAL :: err_msg !< Function error message.
!! An empty string indicates the next output
!! time was found successfully.

CHARACTER(len=128) :: error_message_local !< Local variable to store the error_message

IF ( PRESENT(err_msg) ) err_msg = ''
error_message_local = ''

! special values for output frequency are -1 for output at end of run
! and 0 for every timestep. Need to check for these here?
! Return zero time increment, hopefully this value is never used
IF ( output_freq == END_OF_RUN .OR. output_freq == EVERY_TIME ) THEN
diag_forecast_time_inc = time
RETURN
END IF

! Make sure calendar was not set after initialization
IF ( output_units == DIAG_SECONDS ) THEN
IF ( get_calendar_type() == NO_CALENDAR ) THEN
diag_time_inc = increment_time(time, output_freq, 0, err_msg=error_message_local)
diag_forecast_time_inc = increment_time(time, output_freq, 0, err_msg=error_message_local)
ELSE
diag_time_inc = increment_date(time, 0, 0, 0, 0, 0, output_freq, err_msg=error_message_local)
diag_forecast_time_inc = increment_date(time, 0, 0, 0, 0, 0, output_freq, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_MINUTES ) THEN
IF ( get_calendar_type() == NO_CALENDAR ) THEN
diag_time_inc = increment_time(time, NINT(output_freq*SECONDS_PER_MINUTE), 0, &
diag_forecast_time_inc = increment_time(time, NINT(output_freq*SECONDS_PER_MINUTE), 0, &
&err_msg=error_message_local)
ELSE
diag_time_inc = increment_date(time, 0, 0, 0, 0, output_freq, 0, err_msg=error_message_local)
diag_forecast_time_inc = increment_date(time, 0, 0, 0, 0, output_freq, 0, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_HOURS ) THEN
IF ( get_calendar_type() == NO_CALENDAR ) THEN
diag_time_inc = increment_time(time, NINT(output_freq*SECONDS_PER_HOUR), 0, err_msg=error_message_local)
diag_forecast_time_inc = increment_time(time, NINT(output_freq*SECONDS_PER_HOUR), 0, &
&err_msg=error_message_local)
ELSE
diag_time_inc = increment_date(time, 0, 0, 0, output_freq, 0, 0, err_msg=error_message_local)
diag_forecast_time_inc = increment_date(time, 0, 0, 0, output_freq, 0, 0, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_DAYS ) THEN
IF (get_calendar_type() == NO_CALENDAR) THEN
diag_time_inc = increment_time(time, 0, output_freq, err_msg=error_message_local)
diag_forecast_time_inc = increment_time(time, 0, output_freq, err_msg=error_message_local)
ELSE
diag_time_inc = increment_date(time, 0, 0, output_freq, 0, 0, 0, err_msg=error_message_local)
diag_forecast_time_inc = increment_date(time, 0, 0, output_freq, 0, 0, 0, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_MONTHS ) THEN
IF (get_calendar_type() == NO_CALENDAR) THEN
error_message_local = 'output units of months NOT allowed with no calendar'
ELSE
diag_time_inc = increment_date(time, 0, output_freq, 0, 0, 0, 0, err_msg=error_message_local)
diag_forecast_time_inc = increment_date(time, 0, output_freq, 0, 0, 0, 0, err_msg=error_message_local)
END IF
ELSE IF ( output_units == DIAG_YEARS ) THEN
IF ( get_calendar_type() == NO_CALENDAR ) THEN
error_message_local = 'output units of years NOT allowed with no calendar'
ELSE
diag_time_inc = increment_date(time, output_freq, 0, 0, 0, 0, 0, err_msg=error_message_local)
diag_forecast_time_inc = increment_date(time, output_freq, 0, 0, 0, 0, 0, err_msg=error_message_local)
END IF
ELSE
error_message_local = 'illegal output units'
END IF

IF ( error_message_local /= '' ) THEN
IF ( fms_error_handler('diag_time_inc',error_message_local,err_msg) ) RETURN
IF ( fms_error_handler('diag_forecast_time_inc',error_message_local,err_msg) ) RETURN
END IF
END FUNCTION diag_time_inc
END FUNCTION diag_forecast_time_inc

!> @brief This function determines a string based on current time.
!! This string is used as suffix in output file name
Expand Down
Loading

0 comments on commit e7fd8f0

Please sign in to comment.