diff --git a/README.md b/README.md index b71b35e245..f092ff3fcc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -MPAS-v8.3.1-2.31 +MPAS-v8.3.1-2.32 ==== The Model for Prediction Across Scales (MPAS) is a collaborative project for diff --git a/src/framework/mpas_stream_list_types.inc b/src/framework/mpas_stream_list_types.inc index bd1f35615a..1b37574585 100644 --- a/src/framework/mpas_stream_list_types.inc +++ b/src/framework/mpas_stream_list_types.inc @@ -38,6 +38,7 @@ integer :: clobber_mode integer :: gattr_update = 1 integer :: io_type + integer :: output_done_marker = 0 type (MPAS_TimeInterval_type), pointer :: recordInterval => null() type (MPAS_stream_list_type), pointer :: alarmList_in => null() type (MPAS_stream_list_type), pointer :: alarmList_out => null() diff --git a/src/framework/mpas_stream_manager.F b/src/framework/mpas_stream_manager.F index bf3b86d830..4a93d685c4 100644 --- a/src/framework/mpas_stream_manager.F +++ b/src/framework/mpas_stream_manager.F @@ -1812,6 +1812,9 @@ subroutine MPAS_stream_mgr_set_property_int(manager, streamID, propertyName, pro case (MPAS_STREAM_PROPERTY_IOTYPE) stream_cursor % io_type = propertyValue + case (MPAS_STREAM_PROPERTY_DONE_MARKER) + stream_cursor % output_done_marker = propertyValue + case default STREAM_ERROR_WRITE('MPAS_stream_mgr_set_property(): No such property $i' COMMA intArgs=(/propertyName/)) STREAM_ERROR_WRITE(' or specified property is not of type integer.') @@ -3516,6 +3519,11 @@ subroutine write_stream(manager, stream, blockID, timeLevel, mgLevel, forceWrite if ( swapRecords ) then stream % nRecords = tempRecord end if + + ! Write done marker file if enabled for this stream + if (stream % output_done_marker == 1) then + call write_done_marker(stream % filename, manager % ioContext % dminfo, stream % blockWrite) + end if end if end subroutine write_stream !}}} @@ -6460,7 +6468,7 @@ subroutine stream_mgr_set_property_c(manager_c, streamID_c, propertyName_c, prop use mpas_c_interfacing, only : mpas_c_to_f_string use iso_c_binding, only : c_char, c_int, c_ptr, c_f_pointer use mpas_derived_types, only : MPAS_streamManager_type, MPAS_STREAM_MGR_NOERR, & - MPAS_STREAM_PROPERTY_OUTPUT_TIMELEVELS + MPAS_STREAM_PROPERTY_OUTPUT_TIMELEVELS, MPAS_STREAM_PROPERTY_DONE_MARKER use mpas_stream_manager, only : MPAS_stream_mgr_set_property use mpas_kind_types, only : StrKIND @@ -6487,6 +6495,8 @@ subroutine stream_mgr_set_property_c(manager_c, streamID_c, propertyName_c, prop ! Map property name string to constant if (trim(propertyName) == 'output_timelevels') then call MPAS_stream_mgr_set_property(manager, streamID, MPAS_STREAM_PROPERTY_OUTPUT_TIMELEVELS, propertyValue, ierr=ierr) + else if (trim(propertyName) == 'output_done_marker') then + call MPAS_stream_mgr_set_property(manager, streamID, MPAS_STREAM_PROPERTY_DONE_MARKER, 1, ierr=ierr) end if if (ierr == MPAS_STREAM_MGR_NOERR) then @@ -6601,3 +6611,47 @@ subroutine stream_mgr_add_variable_output_alarm_c(manager_c, streamID_c, ierr_c) end if end subroutine stream_mgr_add_variable_output_alarm_c !}}} + + +!||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +! +! subroutine write_done_marker +! +!> \brief Write a done marker file to indicate output file completion +!> \author Guoqing Ge +!> \date March 2026 +!> \details +!> Creates a file with .done extension after a successful write completes for the current output file. +!> Only called if output_done_marker property is true for the stream. +! +!----------------------------------------------------------------------- +subroutine write_done_marker(filename, dminfo, blockWrite)!{{{ + + use mpas_derived_types, only : dm_info + use mpas_dmpar, only : IO_NODE + + implicit none + + character(len=*), intent(in) :: filename + type (dm_info), intent(in) :: dminfo + logical, intent(in) :: blockWrite + + character(len=1024) :: marker_filename + character(len=8) :: date_str + character(len=10) :: time_str + integer :: unit_num + + ! For the blockWrite mode, each rank writes its own file so each creates a marker + ! For normal parallel I/O, all ranks write same file so only rank 0 creates marker + if (blockWrite .or. dminfo % my_proc_id == IO_NODE) then + marker_filename = trim(filename) // '.done' + unit_num = 99 + call date_and_time(date=date_str, time=time_str) + open(unit=unit_num, file=trim(marker_filename), status='replace', action='write') + ! Write timestamp: YYYY-MM-DD HH:MM:SS + write(unit_num, '(A)') date_str(1:4)//'-'//date_str(5:6)//'-'//date_str(7:8)//' '// & + time_str(1:2)//':'//time_str(3:4)//':'//time_str(5:6) + close(unit_num) + end if + +end subroutine write_done_marker!}}} diff --git a/src/framework/mpas_stream_manager_types.inc b/src/framework/mpas_stream_manager_types.inc index f0c3809865..10d0a7fdbb 100644 --- a/src/framework/mpas_stream_manager_types.inc +++ b/src/framework/mpas_stream_manager_types.inc @@ -22,7 +22,8 @@ MPAS_STREAM_PROPERTY_CLOBBER = 12, & MPAS_STREAM_PROPERTY_IOTYPE = 13, & MPAS_STREAM_PROPERTY_GATTR_UPDATE = 14, & - MPAS_STREAM_PROPERTY_OUTPUT_TIMELEVELS = 15 + MPAS_STREAM_PROPERTY_OUTPUT_TIMELEVELS = 15, & + MPAS_STREAM_PROPERTY_DONE_MARKER = 16 integer, public, parameter :: MPAS_STREAM_CLOBBER_NEVER = 100, & MPAS_STREAM_CLOBBER_APPEND = 101, & diff --git a/src/framework/xml_stream_parser.c b/src/framework/xml_stream_parser.c index 4a05dcce40..934acbacdf 100644 --- a/src/framework/xml_stream_parser.c +++ b/src/framework/xml_stream_parser.c @@ -1112,6 +1112,7 @@ void xml_stream_parser(char *fname, void *manager, int *mpi_comm, int *status) immutable = 1; for (stream_xml = ezxml_child(streams, "immutable_stream"); stream_xml; stream_xml = ezxml_next(stream_xml)) { const char *output_timelevels; + const char *output_done_marker; streamID = ezxml_attr(stream_xml, "name"); direction = ezxml_attr(stream_xml, "type"); filename_template = ezxml_attr(stream_xml, "filename_template"); @@ -1128,6 +1129,7 @@ void xml_stream_parser(char *fname, void *manager, int *mpi_comm, int *status) clobber = ezxml_attr(stream_xml, "clobber_mode"); gattr_update = ezxml_attr(stream_xml, "gattr_update"); iotype = ezxml_attr(stream_xml, "io_type"); + output_done_marker = ezxml_attr(stream_xml, "output_done_marker"); /* Extract the input interval, if it refer to other streams */ if ( interval_in ) { @@ -1390,6 +1392,16 @@ void xml_stream_parser(char *fname, void *manager, int *mpi_comm, int *status) return; } } + /* If output_done_marker is specified, set it as a property */ + if (output_done_marker != NULL && strstr(output_done_marker, "yes") != NULL) { + stream_mgr_set_property_c(manager, streamID, "output_done_marker", "1", &err); + if (err != 0) { + *status = 1; + return; + } + snprintf(msgbuf, MSGSIZE, " %-20s%s", "output done marker:", "yes"); + mpas_log_write_c(msgbuf, "MPAS_LOG_OUT"); + } /* Possibly add an input alarm for this stream */ if (itype == 3 || itype == 1) { @@ -1471,6 +1483,7 @@ void xml_stream_parser(char *fname, void *manager, int *mpi_comm, int *status) immutable = 0; for (stream_xml = ezxml_child(streams, "stream"); stream_xml; stream_xml = ezxml_next(stream_xml)) { const char *output_timelevels; + const char *output_done_marker; streamID = ezxml_attr(stream_xml, "name"); direction = ezxml_attr(stream_xml, "type"); filename_template = ezxml_attr(stream_xml, "filename_template"); @@ -1487,6 +1500,7 @@ void xml_stream_parser(char *fname, void *manager, int *mpi_comm, int *status) clobber = ezxml_attr(stream_xml, "clobber_mode"); gattr_update = ezxml_attr(stream_xml, "gattr_update"); iotype = ezxml_attr(stream_xml, "io_type"); + output_done_marker = ezxml_attr(stream_xml, "output_done_marker"); /* Extract the input interval, if it refer to other streams */ if ( interval_in ) { @@ -1752,6 +1766,17 @@ void xml_stream_parser(char *fname, void *manager, int *mpi_comm, int *status) mpas_log_write_c(msgbuf, "MPAS_LOG_OUT"); } + /* If output_done_marker is specified, set it as a property */ + if (output_done_marker != NULL && strstr(output_done_marker, "yes") != NULL) { + stream_mgr_set_property_c(manager, streamID, "output_done_marker", "1", &err); + if (err != 0) { + *status = 1; + return; + } + snprintf(msgbuf, MSGSIZE, " %-20s%s", "output done marker:", "yes"); + mpas_log_write_c(msgbuf, "MPAS_LOG_OUT"); + } + /* Possibly add an input alarm for this stream */ if (itype == 3 || itype == 1) { stream_mgr_add_alarm_c(manager, streamID, "input", "start", interval_in2, &err); diff --git a/testing_and_setup/ufs-community/cases/ufscommunity.hrrrv5.rap.summer/streams.atmosphere b/testing_and_setup/ufs-community/cases/ufscommunity.hrrrv5.rap.summer/streams.atmosphere index a3eda0494c..29d95c3b39 100644 --- a/testing_and_setup/ufs-community/cases/ufscommunity.hrrrv5.rap.summer/streams.atmosphere +++ b/testing_and_setup/ufs-community/cases/ufscommunity.hrrrv5.rap.summer/streams.atmosphere @@ -13,6 +13,7 @@