From b68fa7fe23bd9328078784bdeb12ee3e6bb808ca Mon Sep 17 00:00:00 2001 From: bzhao Date: Thu, 22 Aug 2024 10:15:06 -0400 Subject: [PATCH 01/90] added support for calling refresh method of a GC if regeistered; suuport for CICE6 rewind --- generic/MAPL_Generic.F90 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/generic/MAPL_Generic.F90 b/generic/MAPL_Generic.F90 index 13d2a97122c9..00aa0ab960b9 100644 --- a/generic/MAPL_Generic.F90 +++ b/generic/MAPL_Generic.F90 @@ -2753,6 +2753,7 @@ recursive subroutine MAPL_GenericRefresh ( GC, IMPORT, EXPORT, CLOCK, RC ) character(len=ESMF_MAXSTR) :: CHILD_NAME character(len=14) :: datestamp ! YYYYMMDD_HHMMz integer :: status + integer :: UserRC integer :: I type (MAPL_MetaComp), pointer :: STATE character(len=1) :: separator @@ -2857,6 +2858,12 @@ recursive subroutine MAPL_GenericRefresh ( GC, IMPORT, EXPORT, CLOCK, RC ) ! call the actual record method call MAPL_StateRefresh (GC, IMPORT, EXPORT, CLOCK, RC=status ) _VERIFY(status) + + if (associated(STATE%phase_coldstart)) then + call ESMF_GridCompReadRestart(GC, importState=import, & + exportState=export, clock=CLOCK, phase=MAPL_MAX_PHASES+1, userRC=userRC, _RC) + endif + endif call MAPL_TimerOff(STATE,"GenRefreshMine",_RC) call MAPL_TimerOff(STATE,"GenRefreshTot",_RC) From 02bc1f98af4dfdbafb2c11814f4c66044db160ef Mon Sep 17 00:00:00 2001 From: bzhao Date: Thu, 22 Aug 2024 11:14:55 -0400 Subject: [PATCH 02/90] updated CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21e7af342bf4..13f41d1b2063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Added a call in GenericRefresh to allow GC's refresh method to be called; in support + of CICE6 rewind - Added new driver, CapDriver.x, to excerise the MAPL_Cap with the configuratable component also used by ExtDataDriver.x - Added Fortran interface to UDUNITS2 - NOTE: This now means MAPL depends on UDUNITS2 (and transitively, expat) From 2451c6bb959ab814b58d502504e94d29abe57ebf Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Wed, 9 Oct 2024 09:34:14 -0400 Subject: [PATCH 03/90] Add heartbeat option for offset --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 61 +++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index 39ed6270bc5a..1a1fe6fa6add 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -56,8 +56,13 @@ subroutine create_from_parameters(this,update_time,update_freq,update_offset,tim integer :: status,int_time,year,month,day,hour,minute,second,neg_index logical :: negative_offset + type(ESMF_TimeInterval) :: dt + integer :: multiplier + integer :: i + logical :: is_heartbeat this%last_checked = time + dt = ESMF_ClockGet(clock, timestep=dt, _RC) if (update_freq == "-") then this%single_shot = .true. else if (update_freq /= "PT0S") then @@ -71,22 +76,48 @@ subroutine create_from_parameters(this,update_time,update_freq,update_offset,tim this%last_ring = this%reference_time this%update_freq = string_to_esmf_timeinterval(update_freq,_RC) end if - negative_offset = .false. - if (index(update_offset,"-") > 0) then - negative_offset = .true. - neg_index = index(update_offset,"-") - end if - if (negative_offset) then - this%offset=string_to_esmf_timeinterval(update_offset(neg_index+1:),_RC) - this%offset = -this%offset + i = index(update_offset,"-") + 1 + negative_offset = i > 1 + call heartbeat_string(update_offset(i:), is_heartbeat=is_heartbeat, multiplier) + if(is_heartbeat) then + this%offset = multiplier * dt else - this%offset=string_to_esmf_timeinterval(update_offset,_RC) + this%offset=string_to_esmf_timeinterval(update_offset(i:),_RC) end if + if(negative_offset) this%offset = -this%offset _RETURN(_SUCCESS) _UNUSED_DUMMY(clock) end subroutine create_from_parameters + subroutine heartbeat_string(timestring, is_heartbeat, multiplier) + character(len=*), intent(in) :: timestring + logical, intent(out) :: is_heartbeat + integer, intent(out) :: multiplier + character(len=*), parameter :: HEARTBEAT = 'HEARTBEAT' + character(len=:), allocatable :: uppercase + integer :: i + + multiplier = 1 + uppercase = to_upper(timestring) + i = index(uppercase, HEARTBEAT) + is_heartbeat = (i > 0) + + end subroutine heartbeat_string + + function convert_heartbeat(timestring, multiplier) result(tintv) + type(ESMF_TimeInterval) :: tintv + character(len=*), intent(in) :: timestring + integer, optional, intent(in) :: multiplier + integer :: multiplier_ + type(ESMF_TimeInterval) :: heartbeat + + multiplier_ = 1 + if(present(multiplier)) multiplier_=multiplier + tintv = multiplier_ * heartbeat + + end function convert_heartbeat + subroutine check_update(this,do_update,use_time,current_time,first_time,rc) class(ExtDataPointerUpdate), intent(inout) :: this logical, intent(out) :: do_update @@ -101,11 +132,11 @@ subroutine check_update(this,do_update,use_time,current_time,first_time,rc) _RETURN(_SUCCESS) end if if (this%simple_alarm_created) then - use_time = current_time+this%offset + use_time = this%get_adjusted_time(current_time) if (first_time) then do_update = .true. this%first_time_updated = .true. - use_time = this%last_ring + this%offset + use_time = this%get_adjusted_time(last_ring) else ! normal flow next_ring = this%last_ring @@ -126,7 +157,7 @@ subroutine check_update(this,do_update,use_time,current_time,first_time,rc) do while(next_ring >= current_time) next_ring=next_ring-this%update_freq enddo - use_time = next_ring+this%offset + use_time = this%get_adjusted_time(next_ring) this%last_ring = next_ring do_update = .true. ! alarm never rang during the previous advance, only update the previous update was the first time @@ -134,20 +165,20 @@ subroutine check_update(this,do_update,use_time,current_time,first_time,rc) if (this%first_time_updated) then do_update=.true. this%first_time_updated = .false. - use_time = this%last_ring + this%offset + use_time = this%get_adjusted_time(last_ring) end if ! otherwise we land on a time when the alarm would ring and we would update else if (this%last_ring == current_time) then do_update =.true. this%first_time_updated = .false. - use_time = current_time+this%offset + use_time = this%get_adjusted_time(current_time) end if end if end if else do_update = .true. if (this%single_shot) this%disabled = .true. - use_time = current_time+this%offset + use_time = this%get_adjusted_time(current_time) end if this%last_checked = current_time From 3b2aa520b3fd6ed83ace1a89d85021ed217c8632 Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Wed, 9 Oct 2024 11:52:27 -0400 Subject: [PATCH 04/90] Add parse_heartbeat_string --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 45 +++++++++++--------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index 1a1fe6fa6add..258c8c15eea7 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -54,15 +54,15 @@ subroutine create_from_parameters(this,update_time,update_freq,update_offset,tim type(ESMF_Clock), intent(inout) :: clock integer, optional, intent(out) :: rc - integer :: status,int_time,year,month,day,hour,minute,second,neg_index + integer :: status,int_time,year,month,day,hour,minute,second logical :: negative_offset - type(ESMF_TimeInterval) :: dt + type(ESMF_TimeInterval) :: timestep integer :: multiplier integer :: i logical :: is_heartbeat this%last_checked = time - dt = ESMF_ClockGet(clock, timestep=dt, _RC) + call ESMF_ClockGet(clock, timestep=timestep, _RC) if (update_freq == "-") then this%single_shot = .true. else if (update_freq /= "PT0S") then @@ -78,9 +78,9 @@ subroutine create_from_parameters(this,update_time,update_freq,update_offset,tim end if i = index(update_offset,"-") + 1 negative_offset = i > 1 - call heartbeat_string(update_offset(i:), is_heartbeat=is_heartbeat, multiplier) + call parse_heartbeat_timestring(update_offset(i:), is_heartbeat=is_heartbeat, multiplier=multiplier) if(is_heartbeat) then - this%offset = multiplier * dt + this%offset = multiplier * timestep else this%offset=string_to_esmf_timeinterval(update_offset(i:),_RC) end if @@ -90,7 +90,7 @@ subroutine create_from_parameters(this,update_time,update_freq,update_offset,tim end subroutine create_from_parameters - subroutine heartbeat_string(timestring, is_heartbeat, multiplier) + subroutine parse_heartbeat_timestring(timestring, is_heartbeat, multiplier) character(len=*), intent(in) :: timestring logical, intent(out) :: is_heartbeat integer, intent(out) :: multiplier @@ -103,20 +103,25 @@ subroutine heartbeat_string(timestring, is_heartbeat, multiplier) i = index(uppercase, HEARTBEAT) is_heartbeat = (i > 0) - end subroutine heartbeat_string - - function convert_heartbeat(timestring, multiplier) result(tintv) - type(ESMF_TimeInterval) :: tintv - character(len=*), intent(in) :: timestring - integer, optional, intent(in) :: multiplier - integer :: multiplier_ - type(ESMF_TimeInterval) :: heartbeat - - multiplier_ = 1 - if(present(multiplier)) multiplier_=multiplier - tintv = multiplier_ * heartbeat - - end function convert_heartbeat + end subroutine parse_heartbeat_timestring + + function to_upper(s) result(u) + character(len=:), allocatable :: u + character(len=*), intent(in) :: s + character(len=*), parameter :: LOWER = 'qwertyuiopasdfghjklzxcvbnm' + character(len=*), parameter :: UPPER = 'QWERTYUIOPASDFGHJKLZXCVBNM' + character :: ch + integer :: i, j + + u = s + do i = 1, len(u) + ch = u(i:i) + j = index(LOWER, ch) + if(j > 0) ch = UPPER(j:j) + u(i:i) = ch + end do + + end function to_upper subroutine check_update(this,do_update,use_time,current_time,first_time,rc) class(ExtDataPointerUpdate), intent(inout) :: this From e2d3626393b35066857394848de2fd42bf6704a0 Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Wed, 9 Oct 2024 12:54:57 -0400 Subject: [PATCH 05/90] Fix bug with get_adjusted_time. --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index 258c8c15eea7..1ce36037f208 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -37,10 +37,12 @@ module MAPL_ExtDataPointerUpdate function get_adjusted_time(this,time,rc) result(adjusted_time) type(ESMF_Time) :: adjusted_time class(ExtDataPointerUpdate), intent(inout) :: this - type(ESMF_Time), intent(in) :: time + type(ESMF_Time), optional, intent(in) :: time integer, optional, intent(out) :: rc - adjusted_time = time+this%offset + adjusted_time = this%last_ring + if(present(time)) adjusted_time = time + adjusted_time = adjusted_time+this%offset _RETURN(_SUCCESS) end function @@ -141,7 +143,7 @@ subroutine check_update(this,do_update,use_time,current_time,first_time,rc) if (first_time) then do_update = .true. this%first_time_updated = .true. - use_time = this%get_adjusted_time(last_ring) + use_time = this%get_adjusted_time(this%last_ring) else ! normal flow next_ring = this%last_ring @@ -170,7 +172,7 @@ subroutine check_update(this,do_update,use_time,current_time,first_time,rc) if (this%first_time_updated) then do_update=.true. this%first_time_updated = .false. - use_time = this%get_adjusted_time(last_ring) + use_time = this%get_adjusted_time(this%last_ring) end if ! otherwise we land on a time when the alarm would ring and we would update else if (this%last_ring == current_time) then From fc693738e4c5081b3e8df492bcba48ae2fe9a69c Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Wed, 9 Oct 2024 22:30:43 -0400 Subject: [PATCH 06/90] Add tests --- gridcomps/ExtData2G/CMakeLists.txt | 2 + gridcomps/ExtData2G/tests/CMakeLists.txt | 20 +++++ .../tests/Test_ExtDataUpdatePointer.pf | 84 +++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 gridcomps/ExtData2G/tests/CMakeLists.txt create mode 100644 gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf diff --git a/gridcomps/ExtData2G/CMakeLists.txt b/gridcomps/ExtData2G/CMakeLists.txt index 9f4d4dbc6dbe..8748c09a717d 100644 --- a/gridcomps/ExtData2G/CMakeLists.txt +++ b/gridcomps/ExtData2G/CMakeLists.txt @@ -36,3 +36,5 @@ set_target_properties (${this} PROPERTIES Fortran_MODULE_DIRECTORY ${include_${t if (CMAKE_Fortran_COMPILER_ID MATCHES Intel AND CMAKE_BUILD_TYPE MATCHES Release) set_source_files_properties(ExtDataGridCompNG.F90 PROPERTIES COMPILE_OPTIONS ${FOPT1}) endif () + +add_subdirectory(tests EXCLUDE_FROM_ALL) diff --git a/gridcomps/ExtData2G/tests/CMakeLists.txt b/gridcomps/ExtData2G/tests/CMakeLists.txt new file mode 100644 index 000000000000..e1cb4a0ce709 --- /dev/null +++ b/gridcomps/ExtData2G/tests/CMakeLists.txt @@ -0,0 +1,20 @@ +set(MODULE_DIRECTORY "${esma_include}/gridcomps/ExtData2G/ExtData2G/tests") + +set (test_srcs + Test_ExtDataUpdatePointer.pf + ) + +add_pfunit_ctest(MAPL.ExtData2G.tests + TEST_SOURCES ${test_srcs} + LINK_LIBRARIES MAPL.ExtData2G + ) +set_target_properties(MAPL.ExtData2G.tests PROPERTIES Fortran_MODULE_DIRECTORY ${MODULE_DIRECTORY}) +set_tests_properties(MAPL.ExtData2G.tests PROPERTIES LABELS "ESSENTIAL") + +# With this test, it was shown that if you are building with the GNU Fortran +# compiler and *not* on APPLE, then you need to link with the dl library. +if (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) + target_link_libraries(ExtData2G.tests ${CMAKE_DL_LIBS}) +endif () + +add_dependencies(build-tests MAPL.ExtData2G.tests) diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf new file mode 100644 index 000000000000..71b880453c09 --- /dev/null +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -0,0 +1,84 @@ +module Test_ExtDataUpdatePointer + use MAPL_ExtDataPointerUpdate + use funit + use esmf + implicit none + + type(ExtDataPointerUpdate) :: ex + + integer, parameter :: TIME_STEP_IN_SECONDS = 500 + character(len=*), parameter :: START_TIME_STRING = '20241231T123000' + character(len=*), parameter :: TIME_STRING = '20241231T124000' + character(len=*), parameter :: UPDATE_TIME = 'T200000' + character(len=*), parameter :: UPDATE_FREQ = '-' + character(len=*), parameter :: DEFAULT_UPDATE_OFFSET = 'PT900H' + integer :: status_stack(1024) + integer :: status_stack_size = 0 + type(ESMF_Time) :: start_time + type(ESMF_TimeInterval) :: timestep + type(ESMF_Time) :: time + type(ESMF_Clock) :: clock + integer :: status + integer, parameter :: SUCCESS = 0 + integer, parameter :: FAIL = -1 + integer :: past_status + +contains + + @Test + subroutine test_get_adjusted_time_without_time_argument() + @assertTrue(.FALSE.) + end subroutine test_get_adjusted_time_without_time_argument + + @Test + subroutine test_get_adjusted_time_with_time_argument() + @assertTrue(.FALSE.) + end subroutine test_get_adjusted_time_with_time_argument + + @Before + subroutine set_up() + status = SUCCESS + status_stack = status + status_stack_size = 0 + call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, rc=status) + call add_status(status_stack, status_stack_size, status) + call ESMF_TimeIntervalSet(timestep, s=TIME_STEP_IN_SECONDS, rc=status) + call add_status(status_stack, status_stack_size, status) + call ESMF_TimeSet(start_time, timeString=START_TIME_STRING, rc=status) + call add_status(status_stack, status_stack_size, status) +! call ESMF_TimeSet(start_time, yy=START_YY, mm=START_MM, dd=START_DD, rc=status) +! call ESMF_TimeSet(start_time, h=START_H, m=START_M, s=START_S, rc=status) + clock = call ESMF_ClockCreate(timestep=timestep, startTime=start_time, rc=status) + call add_status(status_stack, status_stack_size, status) + call ESMF_TimeSet(time, timeString=TIME_STRING, rc=status) + call add_status(status_stack, status_stack_size, status) + ex = ExtDataUpdatePointer() + end subroutine set_up + + @After + subroutine tear_down() + call ESMF_ClockDestroy(clock, rc=status) + call add_status(status_stack, status_stack_size, status) + call ESMF_Finalize(rc=status) + call add_status(status_stack, status_stack_size, status) + end subroutine tear_down + + logical function os_true(intval) result(lval) + integer, intent(in) :: intval + + lval = (intval == 0) + + end function os_true + + subroutine add_status(status_stack, size_ss, status) result(sz) + integer, intent(inout) :: status_stack(:) + integer, intent(inout) :: size_ss + integer, intent(in) :: status + + size_ss = min(ss_size+1, size(status_stack)) + status_stack(2:size_ss) = status_stack(1:size(status_stack)-1) + status_stack(1) = status + + end subroutine add_status + +end module Test_ExtDataUpdatePointer From 616144c62500733bf7fbf6bd9f9953e5db80e056 Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Wed, 9 Oct 2024 23:50:58 -0400 Subject: [PATCH 07/90] set_up & tear_down function. --- .../tests/Test_ExtDataUpdatePointer.pf | 90 +++++++++---------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index 71b880453c09..c75eed1a6a1e 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -1,84 +1,78 @@ +#include "MAPL_Generic.h" +#if defined(I_AM_PFUNIT) +# undef I_AM_PFUNIT +#endif +#define I_AM_PFUNIT + module Test_ExtDataUpdatePointer use MAPL_ExtDataPointerUpdate use funit use esmf + use MAPL_ExceptionHandling implicit none - type(ExtDataPointerUpdate) :: ex integer, parameter :: TIME_STEP_IN_SECONDS = 500 - character(len=*), parameter :: START_TIME_STRING = '20241231T123000' - character(len=*), parameter :: TIME_STRING = '20241231T124000' - character(len=*), parameter :: UPDATE_TIME = 'T200000' + character(len=*), parameter :: START_TIME_STRING = '2024-12-31T12:30:00' + integer, parameter :: YY=2024, MM=12, DD=31, H=12, M=30, S=0 + character(len=*), parameter :: TIME_STRING = '2024-12-31T12:40:00' + character(len=*), parameter :: UPDATE_TIME = 'T20:00:00' character(len=*), parameter :: UPDATE_FREQ = '-' - character(len=*), parameter :: DEFAULT_UPDATE_OFFSET = 'PT900H' - integer :: status_stack(1024) - integer :: status_stack_size = 0 + character(len=*), parameter :: DEFAULT_UPDATE_OFFSET = 'PT300S' type(ESMF_Time) :: start_time type(ESMF_TimeInterval) :: timestep type(ESMF_Time) :: time type(ESMF_Clock) :: clock - integer :: status integer, parameter :: SUCCESS = 0 - integer, parameter :: FAIL = -1 - integer :: past_status contains @Test subroutine test_get_adjusted_time_without_time_argument() - @assertTrue(.FALSE.) + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + type(ESMF_Time) :: adjusted_time + type(ESMF_Time) :: expected_time + + call ex%create_from_parameters(UPDATE_TIME, UPDATE_FREQ, DEFAULT_UPDATE_OFFSET, time, clock, _RC) + adjusted_time = ex%get_adjusted_time() + @assertTrue(.TRUE.) + end subroutine test_get_adjusted_time_without_time_argument @Test subroutine test_get_adjusted_time_with_time_argument() - @assertTrue(.FALSE.) +! type(ExtDataPointerUpdate) :: ex + + @assertTrue(.TRUE.) + end subroutine test_get_adjusted_time_with_time_argument @Before subroutine set_up() + integer :: status, rc + status = SUCCESS - status_stack = status - status_stack_size = 0 - call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, rc=status) - call add_status(status_stack, status_stack_size, status) - call ESMF_TimeIntervalSet(timestep, s=TIME_STEP_IN_SECONDS, rc=status) - call add_status(status_stack, status_stack_size, status) - call ESMF_TimeSet(start_time, timeString=START_TIME_STRING, rc=status) - call add_status(status_stack, status_stack_size, status) -! call ESMF_TimeSet(start_time, yy=START_YY, mm=START_MM, dd=START_DD, rc=status) -! call ESMF_TimeSet(start_time, h=START_H, m=START_M, s=START_S, rc=status) - clock = call ESMF_ClockCreate(timestep=timestep, startTime=start_time, rc=status) - call add_status(status_stack, status_stack_size, status) - call ESMF_TimeSet(time, timeString=TIME_STRING, rc=status) - call add_status(status_stack, status_stack_size, status) - ex = ExtDataUpdatePointer() + call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, _RC) +! call ESMF_TimeSet(start_time, timeString=START_TIME_STRING, _RC) + call ESMF_TimeSet(start_time, yy=YY, _RC) + call ESMF_TimeSet(start_time, mm=MM, _RC) + call ESMF_TimeSet(start_time, dd=DD, _RC) + call ESMF_TimeSet(start_time, h=H, _RC) + call ESMF_TimeSet(start_time, m=M, _RC) + call ESMF_TimeSet(start_time, s=S, _RC) + clock = ESMF_ClockCreate(timestep=timestep, startTime=start_time, _RC) + call ESMF_TimeSet(time, timeString=TIME_STRING, _RC) + end subroutine set_up @After subroutine tear_down() - call ESMF_ClockDestroy(clock, rc=status) - call add_status(status_stack, status_stack_size, status) - call ESMF_Finalize(rc=status) - call add_status(status_stack, status_stack_size, status) - end subroutine tear_down - - logical function os_true(intval) result(lval) - integer, intent(in) :: intval - - lval = (intval == 0) + integer :: status, rc - end function os_true + call ESMF_ClockDestroy(clock, _RC) + call ESMF_Finalize(_RC) - subroutine add_status(status_stack, size_ss, status) result(sz) - integer, intent(inout) :: status_stack(:) - integer, intent(inout) :: size_ss - integer, intent(in) :: status - - size_ss = min(ss_size+1, size(status_stack)) - status_stack(2:size_ss) = status_stack(1:size(status_stack)-1) - status_stack(1) = status - - end subroutine add_status + end subroutine tear_down end module Test_ExtDataUpdatePointer From f4fb7c03c43e725e4bf6391797bcfeb342068bf1 Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Fri, 11 Oct 2024 12:55:52 -0400 Subject: [PATCH 08/90] Add create_from_parameters test. --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 6 +- .../tests/Test_ExtDataUpdatePointer.pf | 124 ++++++++++++------ 2 files changed, 89 insertions(+), 41 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index 1ce36037f208..15625d406d33 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -37,12 +37,10 @@ module MAPL_ExtDataPointerUpdate function get_adjusted_time(this,time,rc) result(adjusted_time) type(ESMF_Time) :: adjusted_time class(ExtDataPointerUpdate), intent(inout) :: this - type(ESMF_Time), optional, intent(in) :: time + type(ESMF_Time), intent(in) :: time integer, optional, intent(out) :: rc - adjusted_time = this%last_ring - if(present(time)) adjusted_time = time - adjusted_time = adjusted_time+this%offset + adjusted_time = time+this%offset _RETURN(_SUCCESS) end function diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index c75eed1a6a1e..b57fc45a4826 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -12,57 +12,37 @@ module Test_ExtDataUpdatePointer implicit none - integer, parameter :: TIME_STEP_IN_SECONDS = 500 - character(len=*), parameter :: START_TIME_STRING = '2024-12-31T12:30:00' - integer, parameter :: YY=2024, MM=12, DD=31, H=12, M=30, S=0 - character(len=*), parameter :: TIME_STRING = '2024-12-31T12:40:00' - character(len=*), parameter :: UPDATE_TIME = 'T20:00:00' - character(len=*), parameter :: UPDATE_FREQ = '-' - character(len=*), parameter :: DEFAULT_UPDATE_OFFSET = 'PT300S' + integer, parameter :: TIME_STEP_IN_SECONDS = 1 + integer, parameter :: REFERENCE_TIME_FIELDS(*) = [2024, 12, 31, 20] + integer, parameter :: START_TIME_FIELDS(*) = [2024, 01, 01] + integer, parameter :: DEFAULT_TIME_FIELDS(*) = REFERENCE_TIME_FIELDS(1:3) + integer, parameter :: UPDATE_TIME_FIELDS(*) = REFERENCE_TIME_FIELDS(4:) + character(len=*), parameter :: UPDATE_TIMESTRING = 'T20:00:00' + character(len=*), parameter :: UPDATE_FREQ_STRING = '-' type(ESMF_Time) :: start_time type(ESMF_TimeInterval) :: timestep - type(ESMF_Time) :: time type(ESMF_Clock) :: clock + type(ESMF_Time) :: time + type(ESMF_TimeInterval) :: time_interval + type(ESMF_Time) :: update_time + type(ESMF_Time) :: reference_time integer, parameter :: SUCCESS = 0 contains - @Test - subroutine test_get_adjusted_time_without_time_argument() - type(ExtDataPointerUpdate) :: ex - integer :: status, rc - type(ESMF_Time) :: adjusted_time - type(ESMF_Time) :: expected_time - - call ex%create_from_parameters(UPDATE_TIME, UPDATE_FREQ, DEFAULT_UPDATE_OFFSET, time, clock, _RC) - adjusted_time = ex%get_adjusted_time() - @assertTrue(.TRUE.) - - end subroutine test_get_adjusted_time_without_time_argument - - @Test - subroutine test_get_adjusted_time_with_time_argument() -! type(ExtDataPointerUpdate) :: ex - - @assertTrue(.TRUE.) - - end subroutine test_get_adjusted_time_with_time_argument - @Before subroutine set_up() integer :: status, rc status = SUCCESS call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, _RC) -! call ESMF_TimeSet(start_time, timeString=START_TIME_STRING, _RC) - call ESMF_TimeSet(start_time, yy=YY, _RC) - call ESMF_TimeSet(start_time, mm=MM, _RC) - call ESMF_TimeSet(start_time, dd=DD, _RC) - call ESMF_TimeSet(start_time, h=H, _RC) - call ESMF_TimeSet(start_time, m=M, _RC) - call ESMF_TimeSet(start_time, s=S, _RC) + call ESMF_TimeIntervalSet(time_interval, _RC) + call ESMF_TimeIntervalSet(timestep, s=TIME_STEP_IN_SECONDS, _RC) + call make_esmf_time(START_TIME_FIELDS, start_time, _RC) + call make_esmf_time(DEFAULT_TIME_FIELDS, time, _RC) + call make_esmf_time(UPDATE_TIME_FIELDS, update_time, time_only=.TRUE., _RC) + call make_esmf_time(REFERENCE_TIME_FIELDS, reference_time, _RC) clock = ESMF_ClockCreate(timestep=timestep, startTime=start_time, _RC) - call ESMF_TimeSet(time, timeString=TIME_STRING, _RC) end subroutine set_up @@ -70,9 +50,79 @@ contains subroutine tear_down() integer :: status, rc + call ESMF_TimeSet(start_time, _RC) + call ESMF_TimeIntervalSet(timestep, _RC) call ESMF_ClockDestroy(clock, _RC) + call ESMF_TimeSet(time, _RC) + call ESMF_TimeIntervalSet(time_interval, _RC) + call ESMF_TimeSet(time, _RC) + call ESMF_TimeSet(update_time, _RC) + call ESMF_TimeSet(reference_time, _RC) call ESMF_Finalize(_RC) end subroutine tear_down + subroutine make_esmf_time(fields, time, time_only, rc) + integer, intent(in) :: fields(:) + type(ESMF_Time), intent(inout) :: time + logical, optional, intent(in) :: time_only + integer, optional, intent(out) :: rc + integer :: status, n(6), i, k + + status = 0 + k = size(fields) + if(k == 0 .or. k > size(n)) status = -1 + _VERIFY(status) + n = 0 + i = 1 + if(present(time_only)) then + if(time_only) i = 4 + end if + n(i:i+k-1) = fields(1:k) + call ESMF_TimeSet(time, yy=n(1), mm=n(2), dd=n(3), h=n(4), m=n(5), s=n(6), _RC) + _RETURN(_SUCCESS) + + end subroutine make_esmf_time + + @Test + subroutine test_get_adjusted_time + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + character(len=32) :: offset_string + type(ESMF_TimeInterval) :: offset + integer :: ios + integer, parameter :: OFFSET_IN_SECONDS = 300 + logical :: actual_matches_expected + + write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS + _VERIFY(ios) + call ESMF_TimeIntervalSet(offset, s=OFFSET_IN_SECONDS, _RC) + !call ESMF_TimeSet(time, timeString=DEFAULT_TIMESTRING, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, time, clock, _RC) + actual_matches_expected = (ex%get_adjusted_time(time) == (time + offset)) + @assertTrue(actual_matches_expected, 'Adjusted time does match expected time.') + + end subroutine test_get_adjusted_time + + @Test + subroutine test_create_from_parameters_string_positive() + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + character(len=32) :: offset_string + integer, parameter :: OFFSET_IN_SECONDS = 300 + type(ESMF_TimeInterval) :: offset + integer :: ios + logical, parameter :: FIRST_TIME = .TRUE. + logical :: actual_matches_expected, do_update + type(ESMF_Time) :: use_time, current_time + + write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS + _VERIFY(ios) + call ESMF_TimeIntervalSet(offset, s=OFFSET_IN_SECONDS, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, time, clock, _RC) + call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) + actual_matches_expected = (use_time == reference_time+offset) + @assertTrue(actual_matches_expected, 'use_time does not match expected time.') + + end subroutine test_create_from_parameters_string_positive end module Test_ExtDataUpdatePointer From 28b58a8e49e10d41304d7bfb6e8235beb59522bd Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Fri, 11 Oct 2024 13:29:33 -0400 Subject: [PATCH 09/90] (+/-)ISO_8601 string & (+/-)heartbeat tests pass. --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 10 +-- .../tests/Test_ExtDataUpdatePointer.pf | 61 ++++++++++++++++++- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index 15625d406d33..e71104db899e 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -11,6 +11,7 @@ module MAPL_ExtDataPointerUpdate private public :: ExtDataPointerUpdate + public :: HEARTBEAT_STRING type :: ExtDataPointerUpdate private @@ -32,7 +33,9 @@ module MAPL_ExtDataPointerUpdate procedure :: get_adjusted_time end type - contains + character(len=*), parameter :: HEARTBEAT_STRING = 'HEARTBEAT' + +contains function get_adjusted_time(this,time,rc) result(adjusted_time) type(ESMF_Time) :: adjusted_time @@ -94,13 +97,10 @@ subroutine parse_heartbeat_timestring(timestring, is_heartbeat, multiplier) character(len=*), intent(in) :: timestring logical, intent(out) :: is_heartbeat integer, intent(out) :: multiplier - character(len=*), parameter :: HEARTBEAT = 'HEARTBEAT' - character(len=:), allocatable :: uppercase integer :: i multiplier = 1 - uppercase = to_upper(timestring) - i = index(uppercase, HEARTBEAT) + i = index(to_upper(timestring), HEARTBEAT_STRING) is_heartbeat = (i > 0) end subroutine parse_heartbeat_timestring diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index b57fc45a4826..b316ac992ac6 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -97,7 +97,6 @@ contains write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS _VERIFY(ios) call ESMF_TimeIntervalSet(offset, s=OFFSET_IN_SECONDS, _RC) - !call ESMF_TimeSet(time, timeString=DEFAULT_TIMESTRING, _RC) call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, time, clock, _RC) actual_matches_expected = (ex%get_adjusted_time(time) == (time + offset)) @assertTrue(actual_matches_expected, 'Adjusted time does match expected time.') @@ -125,4 +124,64 @@ contains @assertTrue(actual_matches_expected, 'use_time does not match expected time.') end subroutine test_create_from_parameters_string_positive + + @Test + subroutine test_create_from_parameters_string_negative() + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + character(len=32) :: offset_string + integer, parameter :: OFFSET_IN_SECONDS = 300 + type(ESMF_TimeInterval) :: offset + integer :: ios + logical, parameter :: FIRST_TIME = .TRUE. + logical :: actual_matches_expected, do_update + type(ESMF_Time) :: use_time, current_time + + write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS + offset_string = '-' // offset_string + _VERIFY(ios) + call ESMF_TimeIntervalSet(offset, s=-OFFSET_IN_SECONDS, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, time, clock, _RC) + call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) + actual_matches_expected = (use_time == reference_time-offset) + @assertTrue(actual_matches_expected, 'use_time does not match expected time.') + + end subroutine test_create_from_parameters_string_negative + + @Test + subroutine test_create_from_parameters_heartbeat_positive() + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + character(len=*), parameter :: OFFSET_STRING = HEARTBEAT_STRING + type(ESMF_TimeInterval) :: offset + logical, parameter :: FIRST_TIME = .TRUE. + logical :: actual_matches_expected, do_update + type(ESMF_Time) :: use_time, current_time + + offset = timestep + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, time, clock, _RC) + call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) + actual_matches_expected = (use_time == reference_time+offset) + @assertTrue(actual_matches_expected, 'use_time does not match expected time.') + + end subroutine test_create_from_parameters_heartbeat_positive + + @Test + subroutine test_create_from_parameters_heartbeat_negative() + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + character(len=*), parameter :: OFFSET_STRING = '-' // HEARTBEAT_STRING + type(ESMF_TimeInterval) :: offset + logical, parameter :: FIRST_TIME = .TRUE. + logical :: actual_matches_expected, do_update + type(ESMF_Time) :: use_time, current_time + + offset = -timestep + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, time, clock, _RC) + call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) + actual_matches_expected = (use_time == reference_time+offset) + @assertTrue(actual_matches_expected, 'use_time does not match expected time.') + + end subroutine test_create_from_parameters_heartbeat_negative + end module Test_ExtDataUpdatePointer From 0a4d44e342803e73bccab5a5f0acef6e9547d341 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 08:30:49 -0400 Subject: [PATCH 10/90] Add options to benchmark scripts --- .../checkpoint_simulator.F90 | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 index 96bad4dfd6a5..0f7a6cff1048 100644 --- a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 +++ b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 @@ -58,6 +58,40 @@ module mapl_checkpoint_support_mod procedure :: reset end type + ! This will define the command line options we will use + ! The RC file currently has: + ! NX: 4 # NX and NY are the decomposition of each face of the cubed sphere + ! NY: 4 + ! IM_WORLD: 90 # the cubed-sphere resolution to write + ! LM: 137 # number of levels in each 3D variable + ! NUM_WRITERS: 1 # number of processes that will write (must be multiple of 6 + ! NUM_ARRAYS: 5 # number of 3D arrays to write + ! NTRIALS: 2 # number of trials + ! # the rest of these are optional + ! SPLIT_FILE: .false. # whether each process writes to it's own file or the same file default false + ! GATHER_3D: .false. # whether to gather a level at a time or full variables, default false + ! WRITE_BARRIER: .false. # put a barrier after the write + ! RANDOM_DATA: .true. # whether to put random data in the array to be written + ! DO_WRITES: .true. # whether to skip writing, so you can just time the MPI. default false + ! + ! We also want a new option to allow reading through an rc file + ! + type cli_options + integer :: nx + integer :: ny + integer :: im_world + integer :: lm + integer :: num_writers + integer :: num_arrays + integer :: n_trials + logical :: split_file = .false. + logical :: gather_3d = .false. + logical :: write_barrier = .false. + logical :: random_data = .true. + logical :: do_writes = .true. + character(len=:), allocatable :: config_file + end type cli_options + contains subroutine set_parameters(this,config_file) @@ -679,6 +713,7 @@ program checkpoint_tester use mapl_checkpoint_support_mod use MPI use NetCDF + use fargparse use, intrinsic :: iso_fortran_env, only: REAL64, INT64 implicit NONE @@ -692,6 +727,10 @@ program checkpoint_tester real(kind=REAL64) :: mean_throughput, mean_fs_throughput real(kind=REAL64) :: std_throughput, std_fs_throughput + type(ArgParser), target :: parser + type(StringUnlimitedMap) :: options + type(cli_options) :: cli + call system_clock(count=start_app,count_rate=count_rate) call MPI_Init(status) _VERIFY(status) @@ -706,6 +745,81 @@ program checkpoint_tester call MPI_Barrier(MPI_COMM_WORLD,status) _VERIFY(status) + parser = ArgParser() + + call parser%add_argument("--nx", & + help="The number of cells in the x direction", & + action="store", & + type="integer", & + default=4) + + call parser%add_argument("--ny", & + help="The number of cells in the y direction", & + action="store", & + type="integer", & + default=4) + + call parser%add_argument("--im_world", & + help="The resolution of the cubed sphere", & + action="store", & + type="integer", & + default=90) + + call parser%add_argument("--lm", & + help="The number of levels in each 3D variable", & + action="store", & + type="integer", & + default=137) + + call parser%add_argument("--num_writers", & + help="The number of processes that will write", & + action="store", & + type="integer", & + default=1) + + call parser%add_argument("--num_arrays", & + help="The number of 3D arrays to write", & + action="store", & + type="integer", & + default=5) + + call parser%add_argument("--ntrials", & + help="The number of trials to run", & + action="store", & + type="integer", & + default=3) + + call parser%add_argument("--split_file", & + help="Split the file into multiple files", & + action="store_true", & + default=.false.) + + call parser%add_argument("--gather_3d", & + help="Gather 3D data", & + action="store_true", & + default=.false.) + + call parser%add_argument("--write_barrier", & + help="Add a write barrier", & + action="store_true", & + default=.false.) + + call parser%add_argument("--random_data", & + help="Use random data", & + action="store_true", & + default=.true.) + + call parser%add_argument("--do_writes", & + help="Write data", & + action="store_true", & + default=.true.) + + call parser%add_argument("--config_file", & + help="The configuration file to use", & + action="store", & + type="string", & + default="*") + call support%set_parameters("checkpoint_benchmark.rc") call MPI_Barrier(MPI_COMM_WORLD,status) _VERIFY(status) From e1b572a3f8db56361aeb432c579f99863febf638 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 09:02:54 -0400 Subject: [PATCH 11/90] More fixes --- .../checkpoint_simulator.F90 | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 index 0f7a6cff1048..53a6716df583 100644 --- a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 +++ b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 @@ -730,6 +730,7 @@ program checkpoint_tester type(ArgParser), target :: parser type(StringUnlimitedMap) :: options type(cli_options) :: cli + class(*), pointer :: option call system_clock(count=start_app,count_rate=count_rate) call MPI_Init(status) @@ -745,6 +746,7 @@ program checkpoint_tester call MPI_Barrier(MPI_COMM_WORLD,status) _VERIFY(status) + call parser%initialize() parser = ArgParser() call parser%add_argument("--nx", & @@ -820,6 +822,48 @@ program checkpoint_tester type="string", & default="*") + option => options%at("nx") + if (associated(option)) call cast(option, cli%nx) + + option => options%at("ny") + if (associated(option)) call cast(option, cli%ny) + + option => options%at("im_world") + if (associated(option)) call cast(option, cli%im_world) + + option => options%at("lm") + if (associated(option)) call cast(option, cli%lm) + + option => options%at("num_writers") + if (associated(option)) call cast(option, cli%num_writers) + + option => options%at("num_arrays") + if (associated(option)) call cast(option, cli%num_arrays) + + option => options%at("ntrials") + if (associated(option)) call cast(option, cli%n_trials) + + option => options%at("split_file") + if (associated(option)) call cast(option, cli%split_file) + + option => options%at("gather_3d") + if (associated(option)) call cast(option, cli%gather_3d) + + option => options%at("write_barrier") + if (associated(option)) call cast(option, cli%write_barrier) + + option => options%at("random_data") + if (associated(option)) call cast(option, cli%random_data) + + option => options%at("do_writes") + if (associated(option)) call cast(option, cli%do_writes) + + option => options%at("config_file") + if (associated(option)) call cast(option, cli%config_file) + + + + call support%set_parameters("checkpoint_benchmark.rc") call MPI_Barrier(MPI_COMM_WORLD,status) _VERIFY(status) From 53ef0e1c10d2a15fa91c906402f7fe5055047f63 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 09:21:20 -0400 Subject: [PATCH 12/90] More fixes 2 --- .../checkpoint_simulator.F90 | 107 +++++++++++++----- 1 file changed, 77 insertions(+), 30 deletions(-) diff --git a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 index 53a6716df583..fa8a97e3704b 100644 --- a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 +++ b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 @@ -45,7 +45,8 @@ module mapl_checkpoint_support_mod integer(kind=INT64) :: create_file_time integer(kind=INT64) :: close_file_time contains - procedure :: set_parameters + procedure :: set_parameters_by_config + procedure :: set_parameters_by_cli procedure :: compute_decomposition procedure :: allocate_n_arrays procedure :: create_arrays @@ -94,7 +95,7 @@ module mapl_checkpoint_support_mod contains - subroutine set_parameters(this,config_file) + subroutine set_parameters_by_config(this,config_file) class(test_support), intent(inout) :: this character(len=*), intent(in) :: config_file type(ESMF_Config) :: config @@ -170,7 +171,46 @@ function get_integer_key(config,label,default_val) result(val) end if end function - end subroutine + end subroutine set_parameters_by_config + + subroutine set_parameters_by_cli(this,cli) + class(test_support), intent(inout) :: this + type(cli_options), intent(in) :: cli + + logical :: is_present + integer :: comm_size, status,error_code,rc + + this%extra_info = .false. + this%write_barrier = cli%write_barrier + this%do_writes = cli%do_writes + this%do_chunking = .true. + this%gather_3d = cli%gather_3d + this%split_file = cli%split_file + this%nx = cli%nx + this%ny = cli%ny + this%im_world = cli%im_world + this%lm = cli%lm + this%num_writers = cli%num_writers + this%num_arrays = cli%num_arrays + this%n_trials = cli%n_trials + this%random = cli%random_data + + this%write_counter = 0 + this%write_3d_time = 0 + this%write_2d_time = 0 + this%create_file_time = 0 + this%close_file_time = 0 + this%data_volume = 0.d0 + this%time_writing = 0.d0 + this%mpi_time = 0.0 + call MPI_COMM_SIZE(MPI_COMM_WORLD,comm_size,status) + _VERIFY(status) + if (comm_size /= (this%nx*this%ny*6)) then + call MPI_Abort(mpi_comm_world,error_code,status) + _VERIFY(status) + endif + + end subroutine set_parameters_by_cli subroutine reset(this) class(test_support), intent(inout) :: this @@ -822,49 +862,56 @@ program checkpoint_tester type="string", & default="*") - option => options%at("nx") - if (associated(option)) call cast(option, cli%nx) + ! We first look for a configuration file + option => options%at("config_file") + if (associated(option)) call cast(option, cli%config_file) - option => options%at("ny") - if (associated(option)) call cast(option, cli%ny) + ! if we have it, we load the configuration file + if (cli%config_file /= "*") then + call support%set_parameters_by_config(cli%config_file) + else - option => options%at("im_world") - if (associated(option)) call cast(option, cli%im_world) + option => options%at("nx") + if (associated(option)) call cast(option, cli%nx) - option => options%at("lm") - if (associated(option)) call cast(option, cli%lm) + option => options%at("ny") + if (associated(option)) call cast(option, cli%ny) - option => options%at("num_writers") - if (associated(option)) call cast(option, cli%num_writers) + option => options%at("im_world") + if (associated(option)) call cast(option, cli%im_world) - option => options%at("num_arrays") - if (associated(option)) call cast(option, cli%num_arrays) + option => options%at("lm") + if (associated(option)) call cast(option, cli%lm) - option => options%at("ntrials") - if (associated(option)) call cast(option, cli%n_trials) + option => options%at("num_writers") + if (associated(option)) call cast(option, cli%num_writers) - option => options%at("split_file") - if (associated(option)) call cast(option, cli%split_file) + option => options%at("num_arrays") + if (associated(option)) call cast(option, cli%num_arrays) - option => options%at("gather_3d") - if (associated(option)) call cast(option, cli%gather_3d) + option => options%at("ntrials") + if (associated(option)) call cast(option, cli%n_trials) - option => options%at("write_barrier") - if (associated(option)) call cast(option, cli%write_barrier) + option => options%at("split_file") + if (associated(option)) call cast(option, cli%split_file) - option => options%at("random_data") - if (associated(option)) call cast(option, cli%random_data) + option => options%at("gather_3d") + if (associated(option)) call cast(option, cli%gather_3d) - option => options%at("do_writes") - if (associated(option)) call cast(option, cli%do_writes) + option => options%at("write_barrier") + if (associated(option)) call cast(option, cli%write_barrier) - option => options%at("config_file") - if (associated(option)) call cast(option, cli%config_file) + option => options%at("random_data") + if (associated(option)) call cast(option, cli%random_data) + option => options%at("do_writes") + if (associated(option)) call cast(option, cli%do_writes) + call support%set_parameters_by_cli(cli) + end if - call support%set_parameters("checkpoint_benchmark.rc") + !call support%set_parameters("checkpoint_benchmark.rc") call MPI_Barrier(MPI_COMM_WORLD,status) _VERIFY(status) From fd473ee5891d9196eac691ea929c38739e53b681 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 09:48:25 -0400 Subject: [PATCH 13/90] Add verbose --- .../checkpoint_simulator.F90 | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 index fa8a97e3704b..b7e2cac7438b 100644 --- a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 +++ b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 @@ -771,6 +771,7 @@ program checkpoint_tester type(StringUnlimitedMap) :: options type(cli_options) :: cli class(*), pointer :: option + logical :: verbose call system_clock(count=start_app,count_rate=count_rate) call MPI_Init(status) @@ -786,9 +787,19 @@ program checkpoint_tester call MPI_Barrier(MPI_COMM_WORLD,status) _VERIFY(status) - call parser%initialize() + call parser%initialize('checkpoint_simulator.x') parser = ArgParser() + call parser%add_argument("--config_file", & + help="The configuration file to use", & + action="store", & + type="string") + + call parser%add_argument("--verbose", & + help="Be verbose", & + action="store_true", & + default=.false.) + call parser%add_argument("--nx", & help="The number of cells in the x direction", & action="store", & @@ -856,23 +867,25 @@ program checkpoint_tester action="store_true", & default=.true.) - call parser%add_argument("--config_file", & - help="The configuration file to use", & - action="store", & - type="string", & - default="*") + options = parser%parse_args() + + option => options%at("verbose") + if (associated(option)) call cast(option, verbose) ! We first look for a configuration file option => options%at("config_file") + write(*,*) "config_file: associated(option) = ",associated(option) if (associated(option)) call cast(option, cli%config_file) ! if we have it, we load the configuration file - if (cli%config_file /= "*") then + if (allocated(cli%config_file)) then call support%set_parameters_by_config(cli%config_file) else option => options%at("nx") + write(*,*) "nx: associated(option) = ",associated(option) if (associated(option)) call cast(option, cli%nx) + write(*,*) "cli%nx = ",cli%nx option => options%at("ny") if (associated(option)) call cast(option, cli%ny) From 09f907b762e0f9e5f20255008e7dd648fd96db9e Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Tue, 15 Oct 2024 10:25:51 -0400 Subject: [PATCH 14/90] Latest to fix failing tests --- gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index b316ac992ac6..4672e2f4ab64 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -77,8 +77,12 @@ contains i = 1 if(present(time_only)) then if(time_only) i = 4 + _HERE, 'time_only: ', time_only + _HERE, 'i, i+k-1, k, size(n), size(fields): ', i, i+k-1, k, size(n) + _HERE, 'n = ', n end if n(i:i+k-1) = fields(1:k) + _HERE, 'n = ', n call ESMF_TimeSet(time, yy=n(1), mm=n(2), dd=n(3), h=n(4), m=n(5), s=n(6), _RC) _RETURN(_SUCCESS) From 998b6307309d3b3139332d92aac4692496988ebb Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Tue, 15 Oct 2024 10:53:32 -0400 Subject: [PATCH 15/90] Additional troubleshooting --- gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index 4672e2f4ab64..e6bfc8501b50 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -78,7 +78,7 @@ contains if(present(time_only)) then if(time_only) i = 4 _HERE, 'time_only: ', time_only - _HERE, 'i, i+k-1, k, size(n), size(fields): ', i, i+k-1, k, size(n) + _HERE, 'i, i+k-1, k, size(n): ', i, i+k-1, k, size(n) _HERE, 'n = ', n end if n(i:i+k-1) = fields(1:k) From c6285208a2141e0ccce5f2e400e9df82ab2ffc93 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 12:04:40 -0400 Subject: [PATCH 16/90] Add netcdf_writes --- .../checkpoint_simulator.F90 | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 index b7e2cac7438b..be344f4bf579 100644 --- a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 +++ b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 @@ -90,6 +90,7 @@ module mapl_checkpoint_support_mod logical :: write_barrier = .false. logical :: random_data = .true. logical :: do_writes = .true. + logical :: netcdf_writes = .true. character(len=:), allocatable :: config_file end type cli_options @@ -183,6 +184,7 @@ subroutine set_parameters_by_cli(this,cli) this%extra_info = .false. this%write_barrier = cli%write_barrier this%do_writes = cli%do_writes + this%netcdf_writes = cli%netcdf_writes this%do_chunking = .true. this%gather_3d = cli%gather_3d this%split_file = cli%split_file @@ -209,6 +211,7 @@ subroutine set_parameters_by_cli(this,cli) call MPI_Abort(mpi_comm_world,error_code,status) _VERIFY(status) endif + write (*,*) "comm_size: ", comm_size end subroutine set_parameters_by_cli @@ -867,6 +870,11 @@ program checkpoint_tester action="store_true", & default=.true.) + call parser%add_argument("--netcdf_writes", & + help="Write data as netcdf", & + action="store_true", & + default=.true.) + options = parser%parse_args() option => options%at("verbose") @@ -874,51 +882,61 @@ program checkpoint_tester ! We first look for a configuration file option => options%at("config_file") - write(*,*) "config_file: associated(option) = ",associated(option) if (associated(option)) call cast(option, cli%config_file) ! if we have it, we load the configuration file if (allocated(cli%config_file)) then + if (verbose .and. rank == 0) write(*,*) "Using configuration file ",cli%config_file call support%set_parameters_by_config(cli%config_file) else option => options%at("nx") - write(*,*) "nx: associated(option) = ",associated(option) if (associated(option)) call cast(option, cli%nx) - write(*,*) "cli%nx = ",cli%nx + if (verbose .and. rank == 0) write(*,*) "nx = ",cli%nx option => options%at("ny") if (associated(option)) call cast(option, cli%ny) + if (verbose .and. rank == 0) write(*,*) "ny = ",cli%ny option => options%at("im_world") if (associated(option)) call cast(option, cli%im_world) + if (verbose .and. rank == 0) write(*,*) "im_world = ",cli%im_world option => options%at("lm") if (associated(option)) call cast(option, cli%lm) + if (verbose .and. rank == 0) write(*,*) "lm = ",cli%lm option => options%at("num_writers") if (associated(option)) call cast(option, cli%num_writers) + if (verbose .and. rank == 0) write(*,*) "num_writers = ",cli%num_writers option => options%at("num_arrays") if (associated(option)) call cast(option, cli%num_arrays) + if (verbose .and. rank == 0) write(*,*) "num_arrays = ",cli%num_arrays option => options%at("ntrials") if (associated(option)) call cast(option, cli%n_trials) + if (verbose .and. rank == 0) write(*,*) "n_trials = ",cli%n_trials option => options%at("split_file") if (associated(option)) call cast(option, cli%split_file) + if (verbose .and. rank == 0) write(*,*) "split_file = ",cli%split_file option => options%at("gather_3d") if (associated(option)) call cast(option, cli%gather_3d) + if (verbose .and. rank == 0) write(*,*) "gather_3d = ",cli%gather_3d option => options%at("write_barrier") if (associated(option)) call cast(option, cli%write_barrier) + if (verbose .and. rank == 0) write(*,*) "write_barrier = ",cli%write_barrier option => options%at("random_data") if (associated(option)) call cast(option, cli%random_data) + if (verbose .and. rank == 0) write(*,*) "random_data = ",cli%random_data option => options%at("do_writes") if (associated(option)) call cast(option, cli%do_writes) + if (verbose .and. rank == 0) write(*,*) "do_writes = ",cli%do_writes call support%set_parameters_by_cli(cli) From 2f834befaf377bb97169e933e3ea6b5013b474ad Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 13:18:50 -0400 Subject: [PATCH 17/90] Clean up --- benchmarks/io/checkpoint_simulator/README.md | 37 +- .../checkpoint_simulator.F90 | 344 +++++++++--------- benchmarks/io/restart_simulator/README.md | 40 +- .../restart_simulator/restart_simulator.F90 | 322 +++++++++++++++- 4 files changed, 542 insertions(+), 201 deletions(-) diff --git a/benchmarks/io/checkpoint_simulator/README.md b/benchmarks/io/checkpoint_simulator/README.md index 4466e69af71f..c74a048512c2 100644 --- a/benchmarks/io/checkpoint_simulator/README.md +++ b/benchmarks/io/checkpoint_simulator/README.md @@ -1,6 +1,27 @@ This benchmark simulates writing a series of 3D variables of a given cubed-sphere resolution to a file using the same strategies as used by the real checkpoint code in MAPL -The code has the following options and needs an ESMF rc file named checkpoint\_benchmark.rc +The code has the following command line options: +``` + optional arguments: + -h, --help This message. + --config_file The configuration file to use + --nx The number of cells in the x direction (default=4) + --ny The number of cells in the y direction (default=4) + --im_world The resolution of the cubed sphere (default=90) + --lm The number of levels in each 3D variable (default=137) + --num_writers The number of processes that will write (default=1) + --num_arrays The number of 3D arrays to write (default=5) + --ntrials The number of trials to run (default=3) + --split_file Split the file into multiple files (default=False) + --gather_3d Gather 3D data (default=False) + --write_barrier Add a write barrier (default=False) + --no_random_data Do not use random data (default=False) + --do_no_writes Do not write data (default=False) + --no_netcdf_writes Do not write data as netcdf (default=False) + --no_chunking Do not chunk (default=False) +``` + +NOTE 1: If you specify a `config_file` it must be an ESMF Config file with the following options: - "NX:" the x distribution for each face - "NY:" the y distribution for each face @@ -8,12 +29,12 @@ The code has the following options and needs an ESMF rc file named checkpoint\_b - "LM:" the number of levels - "NUM\_WRITERS:" the number of writing processes either to a single or independent files - "NUM\_ARRAYS:" the number of 3D variables to write to the file -- "CHUNK:" whether to chunk, default true -- "GATHER\_3D:" gather all levels at once (default is false which means a level at a time is gathered) -- "SPLIT\_FILE:" default false, if true, each writer writes to and independent file -- "WRITE\_BARRIER:" default false, add a barrier before each write to for synchronization -- "DO\_WRITES:" default true, if false skips writing (so just an mpi test at that point) +- "CHUNK:" whether to chunk, default `.true.` +- "GATHER\_3D:" gather all levels at once (default is `.false.` which means a level at a time is gathered) +- "SPLIT\_FILE:" default `.false.`, if `.true.`, each writer writes to and independent file +- "WRITE\_BARRIER:" default `.false.`, add a barrier before each write to for synchronization +- "DO\_WRITES:" default `.true.`, if `.false.` skips writing (so just an mpi test at that point) - "NTRIALS:" default 1, the number of trials to make writing -- "RANDOM\_DATA:" default true, if true will arrays with random data, if false sets the array to the rank of the process +- "RANDOM\_DATA:" default `.true.`, if `.true.` will arrays with random data, if `.false.` sets the array to the rank of the process -Note that whatever you set NX and NY to the program must be run on `6*NX*NY` processors and the number of writers must evenly divide `6*NY` +NOTE 2: that whatever you set NX and NY to the program must be run on `6*NX*NY` processors and the number of writers must evenly divide `6*NY` diff --git a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 index be344f4bf579..5b213d271b57 100644 --- a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 +++ b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 @@ -6,6 +6,7 @@ module mapl_checkpoint_support_mod use MPI use NetCDF use MAPL_ErrorHandlingMod + use fargparse use, intrinsic :: iso_fortran_env, only: INT64, REAL64, REAL32 implicit none @@ -59,24 +60,6 @@ module mapl_checkpoint_support_mod procedure :: reset end type - ! This will define the command line options we will use - ! The RC file currently has: - ! NX: 4 # NX and NY are the decomposition of each face of the cubed sphere - ! NY: 4 - ! IM_WORLD: 90 # the cubed-sphere resolution to write - ! LM: 137 # number of levels in each 3D variable - ! NUM_WRITERS: 1 # number of processes that will write (must be multiple of 6 - ! NUM_ARRAYS: 5 # number of 3D arrays to write - ! NTRIALS: 2 # number of trials - ! # the rest of these are optional - ! SPLIT_FILE: .false. # whether each process writes to it's own file or the same file default false - ! GATHER_3D: .false. # whether to gather a level at a time or full variables, default false - ! WRITE_BARRIER: .false. # put a barrier after the write - ! RANDOM_DATA: .true. # whether to put random data in the array to be written - ! DO_WRITES: .true. # whether to skip writing, so you can just time the MPI. default false - ! - ! We also want a new option to allow reading through an rc file - ! type cli_options integer :: nx integer :: ny @@ -91,11 +74,163 @@ module mapl_checkpoint_support_mod logical :: random_data = .true. logical :: do_writes = .true. logical :: netcdf_writes = .true. + logical :: do_chunking = .true. character(len=:), allocatable :: config_file end type cli_options contains + function parse_arguments() result(options) + + type(StringUnlimitedMap) :: options + type(ArgParser), target :: parser + + call parser%initialize('checkpoint_simulator.x') + parser = ArgParser() + + call parser%add_argument("--config_file", & + help="The configuration file to use", & + action="store", & + type="string") + + call parser%add_argument("--nx", & + help="The number of cells in the x direction (default=4)", & + action="store", & + type="integer", & + default=4) + + call parser%add_argument("--ny", & + help="The number of cells in the y direction (default=4)", & + action="store", & + type="integer", & + default=4) + + call parser%add_argument("--im_world", & + help="The resolution of the cubed sphere (default=90)", & + action="store", & + type="integer", & + default=90) + + call parser%add_argument("--lm", & + help="The number of levels in each 3D variable (default=137)", & + action="store", & + type="integer", & + default=137) + + call parser%add_argument("--num_writers", & + help="The number of processes that will write (default=1)", & + action="store", & + type="integer", & + default=1) + + call parser%add_argument("--num_arrays", & + help="The number of 3D arrays to write (default=5)", & + action="store", & + type="integer", & + default=5) + + call parser%add_argument("--ntrials", & + help="The number of trials to run (default=3)", & + action="store", & + type="integer", & + default=3) + + call parser%add_argument("--split_file", & + help="Split the file into multiple files (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--gather_3d", & + help="Gather 3D data (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--write_barrier", & + help="Add a write barrier (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--no_random_data", & + help="Do not use random data (default=False)", & + action="store_true", & + default=.False.) + + call parser%add_argument("--do_no_writes", & + help="Do not write data (default=False)", & + action="store_true", & + default=.False.) + + call parser%add_argument("--no_netcdf_writes", & + help="Do not write data as netcdf (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--no_chunking", & + help="Do not chunk (default=False)", & + action="store_true", & + default=.false.) + + options = parser%parse_args() + + end function parse_arguments + + subroutine get_cli_options(options, cli) + type(StringUnlimitedMap), intent(in) :: options + type(cli_options), intent(out) :: cli + class(*), pointer :: option + logical :: tmp + + option => options%at("config_file") + if (associated(option)) call cast(option, cli%config_file) + + option => options%at("nx") + if (associated(option)) call cast(option, cli%nx) + + option => options%at("ny") + if (associated(option)) call cast(option, cli%ny) + + option => options%at("im_world") + if (associated(option)) call cast(option, cli%im_world) + + option => options%at("lm") + if (associated(option)) call cast(option, cli%lm) + + option => options%at("num_writers") + if (associated(option)) call cast(option, cli%num_writers) + + option => options%at("num_arrays") + if (associated(option)) call cast(option, cli%num_arrays) + + option => options%at("ntrials") + if (associated(option)) call cast(option, cli%n_trials) + + option => options%at("split_file") + if (associated(option)) call cast(option, cli%split_file) + + option => options%at("gather_3d") + if (associated(option)) call cast(option, cli%gather_3d) + + option => options%at("write_barrier") + if (associated(option)) call cast(option, cli%write_barrier) + + option => options%at("no_random_data") + if (associated(option)) call cast(option, tmp) + cli%random_data = .not. tmp + + option => options%at("do_no_writes") + if (associated(option)) call cast(option, tmp) + cli%do_writes = .not. tmp + + option => options%at("no_netcdf_writes") + if (associated(option)) call cast(option, tmp) + cli%netcdf_writes = .not. tmp + + option => options%at("no_chunking") + if (associated(option)) call cast(option, tmp) + cli%do_chunking = .not. tmp + + end subroutine get_cli_options + subroutine set_parameters_by_config(this,config_file) class(test_support), intent(inout) :: this character(len=*), intent(in) :: config_file @@ -185,7 +320,7 @@ subroutine set_parameters_by_cli(this,cli) this%write_barrier = cli%write_barrier this%do_writes = cli%do_writes this%netcdf_writes = cli%netcdf_writes - this%do_chunking = .true. + this%do_chunking = cli%do_chunking this%gather_3d = cli%gather_3d this%split_file = cli%split_file this%nx = cli%nx @@ -211,7 +346,6 @@ subroutine set_parameters_by_cli(this,cli) call MPI_Abort(mpi_comm_world,error_code,status) _VERIFY(status) endif - write (*,*) "comm_size: ", comm_size end subroutine set_parameters_by_cli @@ -433,20 +567,25 @@ subroutine create_file(this) write(fc,'(I0.3)')writer_rank fname = "checkpoint_"//fc//".nc4" status = nf90_create(fname,ior(NF90_NETCDF4,NF90_CLOBBER), this%ncid) + _VERIFY(status) chunk_factor = 1 else fname = "checkpoint.nc4" status = nf90_create(fname,create_mode, this%ncid, comm=this%writers_comm, info=info) + _VERIFY(status) chunk_factor = this%num_writers end if status = nf90_def_dim(this%ncid,"lon",this%im_world,xdim) + _VERIFY(status) if (this%split_file) then y_size = this%im_world*6/this%num_writers else y_size = this%im_world*6 end if status = nf90_def_dim(this%ncid,"lat",y_size,ydim) + _VERIFY(status) status = nf90_def_dim(this%ncid,"lev",this%lm,zdim) + _VERIFY(status) if (this%gather_3d) then z_chunk = this%lm else @@ -455,11 +594,15 @@ subroutine create_file(this) do i=1,this%num_arrays if (this%do_chunking) then status = nf90_def_var(this%ncid,this%bundle(i)%field_name,NF90_FLOAT,[xdim,ydim,zdim],varid,chunksizes=[this%im_world,y_size/chunk_factor,z_chunk]) + _VERIFY(status) else status = nf90_def_var(this%ncid,this%bundle(i)%field_name,NF90_FLOAT,[xdim,ydim,zdim],varid) + _VERIFY(status) end if status = nf90_def_var_fill(this%ncid,varid,NF90_NOFILL,0) + _VERIFY(status) !status = nf90_var_par_access(this%ncid,varid,NF90_COLLECTIVE) ! you can turn this on if you really want to hork up performance + !_VERIFY(status) enddo status = nf90_enddef(this%ncid) end if @@ -558,15 +701,19 @@ subroutine write_variable(this,var_name,local_var) jsize=jsize + (this%jn(myrow+j) - this%j1(myrow+j) + 1) enddo allocate(VAR(IM_WORLD,jsize,this%lm), stat=status) + _VERIFY(status) allocate(recvbuf(IM_WORLD*jsize*this%lm), stat=status) + _VERIFY(status) end if if(myiorank/=0) then allocate(recvbuf(0), stat=status) + _VERIFY(status) endif call mpi_gatherv( local_var, size(local_var), MPI_REAL, recvbuf, recvcounts, displs, MPI_REAL, & 0, this%gather_comm, status ) + _VERIFY(status) call system_clock(count=end_mpi) this%time_mpi = this%mpi_time + (end_mpi - start_mpi) if (this%write_barrier) then @@ -609,7 +756,9 @@ subroutine write_variable(this,var_name,local_var) if (this%do_writes) then if (this%netcdf_writes) then status = nf90_inq_varid(this%ncid,name=var_name ,varid=varid) + _VERIFY(status) status = nf90_put_var(this%ncid,varid,var,start,cnt) + _VERIFY(status) else write(this%ncid)var end if @@ -726,7 +875,9 @@ subroutine write_level(this,var_name,local_var,z_index) if (this%do_writes) then if (this%netcdf_writes) then status = nf90_inq_varid(this%ncid,name=var_name ,varid=varid) + _VERIFY(status) status = nf90_put_var(this%ncid,varid,var,start,cnt) + _VERIFY(status) else write(this%ncid)var end if @@ -770,11 +921,8 @@ program checkpoint_tester real(kind=REAL64) :: mean_throughput, mean_fs_throughput real(kind=REAL64) :: std_throughput, std_fs_throughput - type(ArgParser), target :: parser type(StringUnlimitedMap) :: options type(cli_options) :: cli - class(*), pointer :: option - logical :: verbose call system_clock(count=start_app,count_rate=count_rate) call MPI_Init(status) @@ -790,159 +938,19 @@ program checkpoint_tester call MPI_Barrier(MPI_COMM_WORLD,status) _VERIFY(status) - call parser%initialize('checkpoint_simulator.x') - parser = ArgParser() - - call parser%add_argument("--config_file", & - help="The configuration file to use", & - action="store", & - type="string") - - call parser%add_argument("--verbose", & - help="Be verbose", & - action="store_true", & - default=.false.) - - call parser%add_argument("--nx", & - help="The number of cells in the x direction", & - action="store", & - type="integer", & - default=4) - - call parser%add_argument("--ny", & - help="The number of cells in the y direction", & - action="store", & - type="integer", & - default=4) - - call parser%add_argument("--im_world", & - help="The resolution of the cubed sphere", & - action="store", & - type="integer", & - default=90) - - call parser%add_argument("--lm", & - help="The number of levels in each 3D variable", & - action="store", & - type="integer", & - default=137) - - call parser%add_argument("--num_writers", & - help="The number of processes that will write", & - action="store", & - type="integer", & - default=1) - - call parser%add_argument("--num_arrays", & - help="The number of 3D arrays to write", & - action="store", & - type="integer", & - default=5) - - call parser%add_argument("--ntrials", & - help="The number of trials to run", & - action="store", & - type="integer", & - default=3) - - call parser%add_argument("--split_file", & - help="Split the file into multiple files", & - action="store_true", & - default=.false.) - - call parser%add_argument("--gather_3d", & - help="Gather 3D data", & - action="store_true", & - default=.false.) - - call parser%add_argument("--write_barrier", & - help="Add a write barrier", & - action="store_true", & - default=.false.) - - call parser%add_argument("--random_data", & - help="Use random data", & - action="store_true", & - default=.true.) - - call parser%add_argument("--do_writes", & - help="Write data", & - action="store_true", & - default=.true.) - - call parser%add_argument("--netcdf_writes", & - help="Write data as netcdf", & - action="store_true", & - default=.true.) - - options = parser%parse_args() - - option => options%at("verbose") - if (associated(option)) call cast(option, verbose) - - ! We first look for a configuration file - option => options%at("config_file") - if (associated(option)) call cast(option, cli%config_file) + options = parse_arguments() + + call get_cli_options(options,cli) ! if we have it, we load the configuration file if (allocated(cli%config_file)) then - if (verbose .and. rank == 0) write(*,*) "Using configuration file ",cli%config_file + if (rank == 0) write(*,*) "Using configuration file ",cli%config_file + if (rank == 0) write(*,*) "NOTE: This overrides any other command line options" call support%set_parameters_by_config(cli%config_file) else - - option => options%at("nx") - if (associated(option)) call cast(option, cli%nx) - if (verbose .and. rank == 0) write(*,*) "nx = ",cli%nx - - option => options%at("ny") - if (associated(option)) call cast(option, cli%ny) - if (verbose .and. rank == 0) write(*,*) "ny = ",cli%ny - - option => options%at("im_world") - if (associated(option)) call cast(option, cli%im_world) - if (verbose .and. rank == 0) write(*,*) "im_world = ",cli%im_world - - option => options%at("lm") - if (associated(option)) call cast(option, cli%lm) - if (verbose .and. rank == 0) write(*,*) "lm = ",cli%lm - - option => options%at("num_writers") - if (associated(option)) call cast(option, cli%num_writers) - if (verbose .and. rank == 0) write(*,*) "num_writers = ",cli%num_writers - - option => options%at("num_arrays") - if (associated(option)) call cast(option, cli%num_arrays) - if (verbose .and. rank == 0) write(*,*) "num_arrays = ",cli%num_arrays - - option => options%at("ntrials") - if (associated(option)) call cast(option, cli%n_trials) - if (verbose .and. rank == 0) write(*,*) "n_trials = ",cli%n_trials - - option => options%at("split_file") - if (associated(option)) call cast(option, cli%split_file) - if (verbose .and. rank == 0) write(*,*) "split_file = ",cli%split_file - - option => options%at("gather_3d") - if (associated(option)) call cast(option, cli%gather_3d) - if (verbose .and. rank == 0) write(*,*) "gather_3d = ",cli%gather_3d - - option => options%at("write_barrier") - if (associated(option)) call cast(option, cli%write_barrier) - if (verbose .and. rank == 0) write(*,*) "write_barrier = ",cli%write_barrier - - option => options%at("random_data") - if (associated(option)) call cast(option, cli%random_data) - if (verbose .and. rank == 0) write(*,*) "random_data = ",cli%random_data - - option => options%at("do_writes") - if (associated(option)) call cast(option, cli%do_writes) - if (verbose .and. rank == 0) write(*,*) "do_writes = ",cli%do_writes - call support%set_parameters_by_cli(cli) - end if - !call support%set_parameters("checkpoint_benchmark.rc") call MPI_Barrier(MPI_COMM_WORLD,status) _VERIFY(status) diff --git a/benchmarks/io/restart_simulator/README.md b/benchmarks/io/restart_simulator/README.md index 3152425b0575..a5e31dd72dce 100644 --- a/benchmarks/io/restart_simulator/README.md +++ b/benchmarks/io/restart_simulator/README.md @@ -1,19 +1,41 @@ This benchmark simulates writing a series of 3D variables of a given cubed-sphere resolution to a file using the same strategies as used by the real checkpoint code in MAPL -The code has the following options and needs an ESMF rc file named checkpoint\_benchmark.rc +The code has the following command line options: + +``` + -h, --help This message. + --config_file The configuration file to use + --nx The number of cells in the x direction (default=4) + --ny The number of cells in the y direction (default=4) + --im_world The resolution of the cubed sphere (default=90) + --lm The number of levels in each 3D variable (default=137) + --num_readers The number of processes that will read (default=1) + --num_arrays The number of 3D arrays to read (default=5) + --ntrials The number of trials to run (default=3) + --split_file Split the file into multiple files (default=False) + --scatter_3d Scatter 3D data (default=False) + --read_barrier Add a read barrier (default=False) + --no_random_data Do not random data (default=False) + --do_no_reads Do not read data (default=False) + --no_netcdf_reads Do not read data as netcdf (default=False) +``` + +NOTE 1: This program *REQUIRES* a file called `checkpoint.nc4` that is generated by the `checkpoint_benchmark.x` code + +NOTE 2: If you specify a `config_file` it must be an ESMF Config file with the following options: - "NX:" the x distribution for each face - "NY:" the y distribution for each face - "IM\_WORLD:" the cube resolution -- "LM:" the nubmer of levels +- "LM:" the number of levels - "NUM\_WRITERS:" the number of writing processes either to a single or independent files - "NUM\_ARRAYS:" the number of 3D variables to write to the file -- "CHUNK:" whether to chunk, default true -- "SCATTER\_3D:" gather all levels at once (default is false which means a level at a time is gathered) -- "SPLIT\_FILE:" default false, if true, each writer writes to and independent file -- "WRITE\_BARRIER:" default false, add a barrier before each write to for synchronization -- "DO\_WRITES:" default true, if false skips writing (so just an mpi test at that point) +- "CHUNK:" whether to chunk, default `.true.` +- "SCATTER\_3D:" gather all levels at once (default is `.false` which means a level at a time is gathered) +- "SPLIT\_FILE:" default `.false`, if `.true.`, each writer writes to and independent file +- "WRITE\_BARRIER:" default `.false`, add a barrier before each write to for synchronization +- "DO\_WRITES:" default `.true.`, if `.false` skips writing (so just an mpi test at that point) - "NTRIAL:" default 1, the number of trials to make writing -- "RANDOM\_DATA:" default true, if true will arrays with random data, if false sets the array to the rank of the process +- "RANDOM\_DATA:" default `.true.`, if `.true.` will arrays with random data, if `.false` sets the array to the rank of the process -Note that whatever you set NX and NY to the program must be run on 6*NY*NY processors and the number of writers must evenly divide 6*NY +NOTE 3: whatever you set NX and NY to the program must be run on `6*NY*NY` processors and the number of writers must evenly divide `6*NY` diff --git a/benchmarks/io/restart_simulator/restart_simulator.F90 b/benchmarks/io/restart_simulator/restart_simulator.F90 index 235cba280b5b..d57facd1f464 100644 --- a/benchmarks/io/restart_simulator/restart_simulator.F90 +++ b/benchmarks/io/restart_simulator/restart_simulator.F90 @@ -6,6 +6,7 @@ module mapl_restart_support_mod use NetCDF use MAPL_ErrorHandlingMod use MAPL_MemUtilsMod + use fargparse use, intrinsic :: iso_fortran_env, only: INT64, REAL64, REAL32 implicit none @@ -44,7 +45,8 @@ module mapl_restart_support_mod integer(kind=INT64) :: open_file_time integer(kind=INT64) :: close_file_time contains - procedure :: set_parameters + procedure :: set_parameters_by_config + procedure :: set_parameters_by_cli procedure :: compute_decomposition procedure :: allocate_n_arrays procedure :: create_arrays @@ -57,14 +59,173 @@ module mapl_restart_support_mod procedure :: reset end type + type cli_options + integer :: nx + integer :: ny + integer :: im_world + integer :: lm + integer :: num_readers + integer :: num_arrays + integer :: n_trials + logical :: split_file = .false. + logical :: scatter_3d = .false. + logical :: read_barrier = .false. + logical :: random_data = .true. + logical :: do_reads = .true. + logical :: netcdf_reads = .true. + character(len=:), allocatable :: config_file + end type cli_options + contains - subroutine set_parameters(this,config_file) + function parse_arguments() result(options) + + type(StringUnlimitedMap) :: options + type(ArgParser), target :: parser + + call parser%initialize('checkpoint_simulator.x') + parser = ArgParser() + + call parser%add_argument("--config_file", & + help="The configuration file to use", & + action="store", & + type="string") + + call parser%add_argument("--nx", & + help="The number of cells in the x direction (default=4)", & + action="store", & + type="integer", & + default=4) + + call parser%add_argument("--ny", & + help="The number of cells in the y direction (default=4)", & + action="store", & + type="integer", & + default=4) + + call parser%add_argument("--im_world", & + help="The resolution of the cubed sphere (default=90)", & + action="store", & + type="integer", & + default=90) + + call parser%add_argument("--lm", & + help="The number of levels in each 3D variable (default=137)", & + action="store", & + type="integer", & + default=137) + + call parser%add_argument("--num_readers", & + help="The number of processes that will read (default=1)", & + action="store", & + type="integer", & + default=1) + + call parser%add_argument("--num_arrays", & + help="The number of 3D arrays to read (default=5)", & + action="store", & + type="integer", & + default=5) + + call parser%add_argument("--ntrials", & + help="The number of trials to run (default=3)", & + action="store", & + type="integer", & + default=3) + + call parser%add_argument("--split_file", & + help="Split the file into multiple files (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--scatter_3d", & + help="Scatter 3D data (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--read_barrier", & + help="Add a read barrier (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--no_random_data", & + help="Do not random data (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--do_no_reads", & + help="Do not read data (default=False)", & + action="store_true", & + default=.false.) + + call parser%add_argument("--no_netcdf_reads", & + help="Do not read data as netcdf (default=False)", & + action="store_true", & + default=.false.) + + options = parser%parse_args() + + end function parse_arguments + + subroutine get_cli_options(options, cli) + type(StringUnlimitedMap), intent(in) :: options + type(cli_options), intent(out) :: cli + class(*), pointer :: option + logical :: tmp + + option => options%at("config_file") + if (associated(option)) call cast(option, cli%config_file) + + option => options%at("nx") + if (associated(option)) call cast(option, cli%nx) + + option => options%at("ny") + if (associated(option)) call cast(option, cli%ny) + + option => options%at("im_world") + if (associated(option)) call cast(option, cli%im_world) + + option => options%at("lm") + if (associated(option)) call cast(option, cli%lm) + + option => options%at("num_readers") + if (associated(option)) call cast(option, cli%num_readers) + + option => options%at("num_arrays") + if (associated(option)) call cast(option, cli%num_arrays) + + option => options%at("ntrials") + if (associated(option)) call cast(option, cli%n_trials) + + option => options%at("split_file") + if (associated(option)) call cast(option, cli%split_file) + + option => options%at("scatter_3d") + if (associated(option)) call cast(option, cli%scatter_3d) + + option => options%at("read_barrier") + if (associated(option)) call cast(option, cli%read_barrier) + + option => options%at("no_random_data") + if (associated(option)) call cast(option, tmp) + cli%random_data = .not. tmp + + option => options%at("do_no_reads") + if (associated(option)) call cast(option, tmp) + cli%do_reads = .not. tmp + + option => options%at("no_netcdf_reads") + if (associated(option)) call cast(option, tmp) + cli%netcdf_reads = .not. tmp + + end subroutine get_cli_options + + subroutine set_parameters_by_config(this,config_file) class(test_support), intent(inout) :: this character(len=*), intent(in) :: config_file type(ESMF_Config) :: config - integer :: comm_size, status,error_code + integer :: comm_size, status,error_code, rc config = ESMF_ConfigCreate() this%extra_info = .false. @@ -95,7 +256,11 @@ subroutine set_parameters(this,config_file) this%time_reading = 0.d0 this%mpi_time = 0.0 call MPI_COMM_SIZE(MPI_COMM_WORLD,comm_size,status) - if (comm_size /= (this%nx*this%ny*6)) call MPI_Abort(mpi_comm_world,error_code,status) + _VERIFY(status) + if (comm_size /= (this%nx*this%ny*6)) then + call MPI_Abort(mpi_comm_world,error_code,status) + _VERIFY(status) + endif contains @@ -129,7 +294,46 @@ function get_integer_key(config,label,default_val) result(val) end if end function - end subroutine + end subroutine set_parameters_by_config + + subroutine set_parameters_by_cli(this,cli) + class(test_support), intent(inout) :: this + type(cli_options), intent(in) :: cli + + logical :: is_present + integer :: comm_size, status,error_code,rc + + this%extra_info = .false. + this%read_barrier = cli%read_barrier + this%do_reads = cli%do_reads + this%netcdf_reads = cli%netcdf_reads + this%scatter_3d = cli%scatter_3d + this%split_file = cli%split_file + this%nx = cli%nx + this%ny = cli%ny + this%im_world = cli%im_world + this%lm = cli%lm + this%num_readers = cli%num_readers + this%num_arrays = cli%num_arrays + this%n_trials = cli%n_trials + this%random = cli%random_data + + this%read_counter = 0 + this%read_3d_time = 0 + this%read_2d_time = 0 + this%open_file_time = 0 + this%close_file_time = 0 + this%data_volume = 0.d0 + this%time_reading = 0.d0 + this%mpi_time = 0.0 + call MPI_COMM_SIZE(MPI_COMM_WORLD,comm_size,status) + _VERIFY(status) + if (comm_size /= (this%nx*this%ny*6)) then + call MPI_Abort(mpi_comm_world,error_code,status) + _VERIFY(status) + endif + + end subroutine set_parameters_by_cli subroutine reset(this) class(test_support), intent(inout) :: this @@ -170,12 +374,13 @@ subroutine allocate_n_arrays(this,im,jm) integer, intent(in) :: im integer, intent(in) :: jm - integer :: n,rank,status + integer :: n,rank,status,rc character(len=3) :: formatted_int integer :: seed_size integer, allocatable :: seeds(:) call MPI_COMM_RANK(MPI_COMM_WORLD,rank,status) + _VERIFY(status) call random_seed(size=seed_size) allocate(seeds(seed_size)) seeds = rank @@ -196,10 +401,12 @@ subroutine create_arrays(this) class(test_support), intent(inout) :: this integer, allocatable :: ims(:),jms(:) - integer :: rank, status,comm_size,n,i,j,rank_counter,offset,index_offset + integer :: rank, status,comm_size,n,i,j,rank_counter,offset,index_offset,rc call MPI_Comm_Rank(MPI_COMM_WORLD,rank,status) + _VERIFY(status) call MPI_Comm_Size(MPI_COMM_WORLD,comm_size,status) + _VERIFY(status) allocate(this%bundle(this%num_arrays)) ims = this%compute_decomposition(axis=1) jms = this%compute_decomposition(axis=2) @@ -248,16 +455,19 @@ subroutine create_arrays(this) subroutine create_communicators(this) class(test_support), intent(inout) :: this - integer :: myid,status,nx0,ny0,color,j,ny_by_readers,local_ny + integer :: myid,status,nx0,ny0,color,j,ny_by_readers,local_ny,rc local_ny = this%ny*6 call MPI_Comm_Rank(mpi_comm_world,myid,status) + _VERIFY(status) nx0 = mod(myid,this%nx) + 1 ny0 = myid/this%nx + 1 color = nx0 call MPI_Comm_Split(MPI_COMM_WORLD,color,myid,this%ycomm,status) + _VERIFY(status) color = ny0 call MPI_Comm_Split(MPI_COMM_WORLD,color,myid,this%xcomm,status) + _VERIFY(status) ny_by_readers = local_ny/this%num_readers @@ -267,15 +477,19 @@ subroutine create_communicators(this) color = MPI_UNDEFINED end if call MPI_COMM_SPLIT(MPI_COMM_WORLD,color,myid,this%readers_comm,status) + _VERIFY(status) + if (this%num_readers == local_ny) then this%scatter_comm = this%xcomm else j = ny0 - mod(ny0-1,ny_by_readers) call MPI_COMM_SPLIT(MPI_COMM_WORLD,j,myid,this%scatter_comm,status) + _VERIFY(status) end if - call MPI_BARRIER(mpi_comm_world,status) + call MPI_BARRIER(mpi_comm_world, status) + _VERIFY(status) end subroutine @@ -283,7 +497,7 @@ subroutine create_communicators(this) subroutine close_file(this) class(test_support), intent(inout) :: this - integer :: status + integer :: status, rc integer(kind=INT64) :: sub_start,sub_end @@ -292,11 +506,13 @@ subroutine close_file(this) if (this%readers_comm /= MPI_COMM_NULL) then if (this%netcdf_reads) then status = nf90_close(this%ncid) + _VERIFY(status) else close(this%ncid) end if end if call MPI_BARRIER(MPI_COMM_WORLD,status) + _VERIFY(status) call system_clock(count=sub_end) this%close_file_time = sub_end-sub_start end subroutine @@ -319,21 +535,37 @@ subroutine open_file(this) create_mode = IOR(create_mode,NF90_SHARE) create_mode = IOR(create_mode,NF90_MPIIO) call MPI_INFO_CREATE(info,status) + _VERIFY(status) call MPI_INFO_SET(info,"cb_buffer_size","16777216",status) + _VERIFY(status) call MPI_INFO_SET(info,"romio_cb_write","enable",status) + _VERIFY(status) if (this%extra_info) then call MPI_INFO_SET(info,"IBM_largeblock_io","true",status) + _VERIFY(status) call MPI_INFO_SET(info,"striping_unit","4194304",status) + _VERIFY(status) end if if (this%readers_comm /= MPI_COMM_NULL) then if (this%split_file) then call MPI_COMM_RANK(this%readers_comm,writer_rank,status) + _VERIFY(status) write(fc,'(I0.3)')writer_rank fname = "checkpoint_"//fc//".nc4" status = nf90_open(fname,ior(NF90_NETCDF4,NF90_CLOBBER), this%ncid) + if (status /= NF90_NOERR) then + write(*,*) "Error opening file ",fname + call MPI_Abort(MPI_COMM_WORLD,rc,status) + _VERIFY(status) + end if else fname = "checkpoint.nc4" status = nf90_open(fname,create_mode, this%ncid, comm=this%readers_comm, info=info) + if (status /= NF90_NOERR) then + write(*,*) "Error opening file ",fname + call MPI_Abort(MPI_COMM_WORLD,rc,status) + _VERIFY(status) + end if end if end if else @@ -347,6 +579,7 @@ subroutine open_file(this) end if end if call MPI_BARRIER(MPI_COMM_WORLD,status) + _VERIFY(status) call system_clock(count=sub_end) this%open_file_time = sub_end-sub_start end subroutine @@ -354,13 +587,15 @@ subroutine open_file(this) subroutine read_file(this) class(test_support), intent(inout) :: this - integer :: status,i,l + integer :: status,i,l,rc integer(kind=INT64) :: sub_start,sub_end call MPI_BARRIER(MPI_COMM_WORLD,status) + _VERIFY(status) call system_clock(count=sub_start) call MPI_BARRIER(MPI_COMM_WORLD,status) + _VERIFY(status) do i=1,this%num_arrays if (this%scatter_3d) then call this%read_variable(this%bundle(i)%field_name,this%bundle(i)%field) @@ -371,10 +606,13 @@ subroutine read_file(this) end if enddo call MPI_BARRIER(MPI_COMM_WORLD,status) + _VERIFY(status) call system_clock(count=sub_end) call MPI_BARRIER(MPI_COMM_WORLD,status) + _VERIFY(status) this%read_3d_time = sub_end-sub_start call MPI_BARRIER(MPI_COMM_WORLD,status) + _VERIFY(status) end subroutine subroutine read_variable(this,var_name,local_var) @@ -387,7 +625,7 @@ subroutine read_variable(this,var_name,local_var) integer :: start(3), cnt(3) integer :: jsize, jprev, num_io_rows integer, allocatable :: sendcounts(:), displs(:) - integer :: im_world,jm_world,varid + integer :: im_world,jm_world,varid,rc real, allocatable :: var(:,:,:) integer(kind=INT64) :: start_time,end_time,count_rate,lev,start_mpi,end_mpi real(kind=REAL64) :: io_time @@ -398,11 +636,15 @@ subroutine read_variable(this,var_name,local_var) ndes_x = size(this%in) call mpi_comm_rank(this%ycomm,myrow,status) + _VERIFY(status) call mpi_comm_rank(this%scatter_comm,myiorank,status) + _VERIFY(status) call mpi_comm_size(this%scatter_comm,num_io_rows,status) + _VERIFY(status) num_io_rows=num_io_rows/ndes_x allocate (sendcounts(ndes_x*num_io_rows), displs(ndes_x*num_io_rows), stat=status) + _VERIFY(status) if(myiorank==0) then do j=1,num_io_rows @@ -437,7 +679,9 @@ subroutine read_variable(this,var_name,local_var) if (this%do_reads) then if (this%netcdf_reads) then status = nf90_inq_varid(this%ncid,name=var_name ,varid=varid) + _VERIFY(status) status = nf90_get_var(this%ncid,varid,var,start,cnt) + _VERIFY(status) else write(this%ncid)var end if @@ -478,9 +722,13 @@ subroutine read_variable(this,var_name,local_var) call system_clock(count=start_mpi) call mpi_scatterv( buf, sendcounts, displs, MPI_REAL, local_var, size(local_var), MPI_REAL, & 0, this%scatter_comm, status ) + _VERIFY(status) call system_clock(count=end_mpi) this%time_mpi = this%mpi_time + (end_mpi - start_mpi) - if (this%read_barrier) call MPI_Barrier(MPI_COMM_WORLD,status) + if (this%read_barrier) then + call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) + end if deallocate(buf, stat=status) deallocate (sendcounts, displs, stat=status) @@ -498,7 +746,7 @@ subroutine read_level(this,var_name,local_var,z_index) integer :: start(3), cnt(3) integer :: jsize, jprev, num_io_rows integer, allocatable :: sendcounts(:), displs(:) - integer :: im_world,jm_world,varid + integer :: im_world,jm_world,varid,rc real, allocatable :: var(:,:) integer(kind=INT64) :: start_time,end_time,count_rate,start_mpi,end_mpi real(kind=REAL64) :: io_time @@ -509,11 +757,15 @@ subroutine read_level(this,var_name,local_var,z_index) ndes_x = size(this%in) call mpi_comm_rank(this%ycomm,myrow,status) + _VERIFY(status) call mpi_comm_rank(this%scatter_comm,myiorank,status) + _VERIFY(status) call mpi_comm_size(this%scatter_comm,num_io_rows,status) + _VERIFY(status) num_io_rows=num_io_rows/ndes_x allocate (sendcounts(ndes_x*num_io_rows), displs(ndes_x*num_io_rows), stat=status) + _VERIFY(status) if(myiorank==0) then do j=1,num_io_rows @@ -531,7 +783,9 @@ subroutine read_level(this,var_name,local_var,z_index) jsize=jsize + (this%jn(myrow+j) - this%j1(myrow+j) + 1) enddo allocate(VAR(IM_WORLD,jsize), stat=status) + _VERIFY(status) allocate(buf(IM_WORLD*jsize), stat=status) + _VERIFY(status) start(1) = 1 if (this%split_file) then @@ -548,7 +802,9 @@ subroutine read_level(this,var_name,local_var,z_index) if (this%do_reads) then if (this%netcdf_reads) then status = nf90_inq_varid(this%ncid,name=var_name ,varid=varid) + _VERIFY(status) status = nf90_get_var(this%ncid,varid,var,start,cnt) + _VERIFY(status) else read(this%ncid)var end if @@ -585,9 +841,10 @@ subroutine read_level(this,var_name,local_var,z_index) allocate(buf(0), stat=status) endif - call system_clock(count=start_mpi) + call system_clock(count=start_mpi) call mpi_scatterv( buf, sendcounts, displs, MPI_REAL, local_var, size(local_var), MPI_REAL, & 0, this%scatter_comm, status ) + _VERIFY(status) call system_clock(count=end_mpi) this%mpi_time = this%mpi_time + (end_mpi - start_mpi) if (this%read_barrier) call MPI_Barrier(MPI_COMM_WORLD,status) @@ -599,11 +856,13 @@ subroutine read_level(this,var_name,local_var,z_index) end module +#define I_AM_MAIN #include "MAPL_ErrLog.h" program checkpoint_tester use ESMF use MPI use NetCDF + use fargparse use mapl_restart_support_mod use, intrinsic :: iso_fortran_env, only: REAL64, INT64 implicit NONE @@ -618,24 +877,47 @@ program checkpoint_tester real(kind=REAL64) :: mean_throughput, mean_fs_throughput real(kind=REAL64) :: std_throughput, std_fs_throughput + type(StringUnlimitedMap) :: options + type(cli_options) :: cli + call system_clock(count=start_app,count_rate=count_rate) call MPI_Init(status) + _VERIFY(status) call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) call MPI_Comm_Rank(MPI_COMM_WORLD,rank,status) + _VERIFY(status) support%my_rank = rank call MPI_Comm_Size(MPI_COMM_WORLD,comm_size,status) + _VERIFY(status) call ESMF_Initialize(logKindFlag=ESMF_LOGKIND_NONE,mpiCommunicator=MPI_COMM_WORLD) call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) + + options = parse_arguments() + + call get_cli_options(options,cli) + + ! if we have it, we load the configuration file + if (allocated(cli%config_file)) then + if (rank == 0) write(*,*) "Using configuration file ",cli%config_file + if (rank == 0) write(*,*) "NOTE: This overrides any other command line options" + call support%set_parameters_by_config(cli%config_file) + else + call support%set_parameters_by_cli(cli) + end if - call support%set_parameters("restart_benchmark.rc") call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) call support%create_arrays() call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) call support%create_communicators() call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) allocate(total_throughput(support%n_trials)) allocate(all_proc_throughput(support%n_trials)) @@ -645,14 +927,18 @@ program checkpoint_tester call system_clock(count=start_read) call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) call support%open_file() call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) call support%read_file() call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) call support%close_file() call MPI_Barrier(MPI_COMM_WORLD,status) + _VERIFY(status) call system_clock(count=end_time) read_time = real(end_time-start_read,kind=REAL64)/real(count_rate,kind=REAL64) @@ -664,10 +950,14 @@ program checkpoint_tester if (support%readers_comm /= MPI_COMM_NULL) then call MPI_COMM_SIZE(support%readers_comm,reader_size,status) + _VERIFY(status) call MPI_COMM_RANK(support%readers_comm,reader_rank,status) + _VERIFY(status) call MPI_AllReduce(support%data_volume,average_volume,1,MPI_DOUBLE_PRECISION,MPI_SUM,support%readers_comm,status) + _VERIFY(status) average_volume = average_volume/real(reader_size,kind=REAL64) call MPI_AllReduce(support%time_reading,average_time,1,MPI_DOUBLE_PRECISION,MPI_SUM,support%readers_comm,status) + _VERIFY(status) average_time = average_time/real(reader_size,kind=REAL64) end if if (rank == 0) then From 31c296b9881ccd373d649138e4a68e729f2e8a8f Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 14:45:03 -0400 Subject: [PATCH 18/90] Turn off zstandard with Baselibs --- CHANGELOG.md | 2 ++ CMakeLists.txt | 4 ++-- pfio/CMakeLists.txt | 14 ++++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5eb5be4b69d..f1e94ec47e66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Fixed issue of some Baselibs builds appearing to support zstandard. This is not possible due to Baselibs building HDF5 and netCDF as static libraries + ### Removed ### Deprecated diff --git a/CMakeLists.txt b/CMakeLists.txt index 27e90cb1f936..eaf64e196beb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,8 +156,8 @@ else () # This is an ESMF version test when using Baselibs which doesn't use the # same find_package internally in ESMA_cmake as used above (with a version # number) so this lets us at least trap use of old Baselibs here. - if (ESMF_VERSION VERSION_LESS 8.6.0) - message(FATAL_ERROR "ESMF must be at least 8.6.0") + if (ESMF_VERSION VERSION_LESS 8.6.1) + message(FATAL_ERROR "ESMF must be at least 8.6.1") endif () endif () diff --git a/pfio/CMakeLists.txt b/pfio/CMakeLists.txt index 3c6b826eb4f3..0e0da89957d8 100644 --- a/pfio/CMakeLists.txt +++ b/pfio/CMakeLists.txt @@ -109,7 +109,7 @@ check_c_source_compiles(" NETCDF_HAS_QUANTIZE) if (NETCDF_HAS_QUANTIZE) message(STATUS "netCDF has quantize capability") - add_definitions(-DNF_HAS_QUANTIZE) + target_compile_definitions(${this} PRIVATE NF_HAS_QUANTIZE) else () message(STATUS "netCDF does not have quantize capability") endif () @@ -128,9 +128,19 @@ check_c_source_compiles(" } " NETCDF_HAS_ZSTD) + +# NOTE: Even if the check above succeeds, zstandard is *not* +# possible with Baselibs (builds HDF5, netCDF as static) +# so we want to check for Baselibs first. + +if (Baselibs_FOUND) + message(STATUS "Baselibs found, zstandard capability not possible") + set(NETCDF_HAS_ZSTD FALSE) +endif () + if (NETCDF_HAS_ZSTD) message(STATUS "netCDF has zstandard capability") - add_definitions(-DNF_HAS_ZSTD) + target_compile_definitions(${this} PRIVATE NF_HAS_ZSTD) else () message(STATUS "netCDF does not have zstandard capability") endif () From 533040101252e54db701a2df0050688caee3a6e3 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 14:47:37 -0400 Subject: [PATCH 19/90] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f1e94ec47e66..fb3f346ee38d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Update ESMF version for Baselibs to match that of Spack for consistency + ### Fixed - Fixed issue of some Baselibs builds appearing to support zstandard. This is not possible due to Baselibs building HDF5 and netCDF as static libraries From 2ffb01b8d3f7db78b90201b8e6190116f8d00d74 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 15 Oct 2024 15:19:09 -0400 Subject: [PATCH 20/90] Define target before using --- pfio/CMakeLists.txt | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/pfio/CMakeLists.txt b/pfio/CMakeLists.txt index 0e0da89957d8..26f151530a68 100644 --- a/pfio/CMakeLists.txt +++ b/pfio/CMakeLists.txt @@ -91,6 +91,26 @@ set (srcs StringVectorUtil.F90 ) +if (BUILD_WITH_PFLOGGER) + find_package (PFLOGGER REQUIRED) +endif () + +esma_add_library (${this} SRCS ${srcs} DEPENDENCIES MAPL.shared MAPL.profiler NetCDF::NetCDF_Fortran NetCDF::NetCDF_C TYPE ${MAPL_LIBRARY_TYPE}) + +target_link_libraries (${this} PUBLIC GFTL::gftl-v2 GFTL_SHARED::gftl-shared-v2 PFLOGGER::pflogger PRIVATE MPI::MPI_Fortran OpenMP::OpenMP_Fortran) +target_include_directories (${this} PUBLIC + $) + +set_target_properties (${this} PROPERTIES Fortran_MODULE_DIRECTORY ${include_${this}}) +# Kludge for OSX security and DYLD_LIBRARY_PATH ... +foreach(dir ${OSX_EXTRA_LIBRARY_PATH}) + target_link_libraries(${this} PRIVATE "-Xlinker -rpath -Xlinker ${dir}") +endforeach() + +if (SUPPORT_FOR_MPI_ALLOC_MEM_CPTR) + target_compile_definitions(${this} PRIVATE SUPPORT_FOR_MPI_ALLOC_MEM_CPTR) +endif () + ############################################################### # Check to see if quantize capability is present in netcdf-c. # ############################################################### @@ -145,26 +165,6 @@ else () message(STATUS "netCDF does not have zstandard capability") endif () -if (BUILD_WITH_PFLOGGER) - find_package (PFLOGGER REQUIRED) -endif () - -esma_add_library (${this} SRCS ${srcs} DEPENDENCIES MAPL.shared MAPL.profiler NetCDF::NetCDF_Fortran NetCDF::NetCDF_C TYPE ${MAPL_LIBRARY_TYPE}) - -target_link_libraries (${this} PUBLIC GFTL::gftl-v2 GFTL_SHARED::gftl-shared-v2 PFLOGGER::pflogger PRIVATE MPI::MPI_Fortran OpenMP::OpenMP_Fortran) -target_include_directories (${this} PUBLIC - $) - -set_target_properties (${this} PROPERTIES Fortran_MODULE_DIRECTORY ${include_${this}}) -# Kludge for OSX security and DYLD_LIBRARY_PATH ... -foreach(dir ${OSX_EXTRA_LIBRARY_PATH}) - target_link_libraries(${this} PRIVATE "-Xlinker -rpath -Xlinker ${dir}") -endforeach() - -if (SUPPORT_FOR_MPI_ALLOC_MEM_CPTR) - target_compile_definitions(${this} PRIVATE SUPPORT_FOR_MPI_ALLOC_MEM_CPTR) -endif () - ecbuild_add_executable ( TARGET pfio_open_close.x SOURCES pfio_open_close.F90 From 6629c73669be269a2d859b3c8cfda68c1f3947a5 Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Tue, 15 Oct 2024 17:45:56 -0400 Subject: [PATCH 21/90] Save current. --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 12 ++ .../tests/Test_ExtDataUpdatePointer.pf | 159 +++++++++++------- 2 files changed, 113 insertions(+), 58 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index e71104db899e..b1d38a2c495f 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -31,6 +31,7 @@ module MAPL_ExtDataPointerUpdate procedure :: is_single_shot procedure :: disable procedure :: get_adjusted_time + procedure :: get_offset end type character(len=*), parameter :: HEARTBEAT_STRING = 'HEARTBEAT' @@ -48,6 +49,17 @@ function get_adjusted_time(this,time,rc) result(adjusted_time) _RETURN(_SUCCESS) end function + function get_offset(this, rc) result(offset) + type(ESMF_TimeInterval) :: offset + class(ExtDataPointerUpdate), intent(in) :: this + integer, optional, intent(out) :: rc + integer :: status + + offset = this%offset + _RETURN(_SUCCESS) + + end function get_offset + subroutine create_from_parameters(this,update_time,update_freq,update_offset,time,clock,rc) class(ExtDataPointerUpdate), intent(inout) :: this character(len=*), intent(in) :: update_time diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index e6bfc8501b50..e1f930ba96f8 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -12,40 +12,61 @@ module Test_ExtDataUpdatePointer implicit none + integer, parameter :: SUCCESS = 0 integer, parameter :: TIME_STEP_IN_SECONDS = 1 - integer, parameter :: REFERENCE_TIME_FIELDS(*) = [2024, 12, 31, 20] - integer, parameter :: START_TIME_FIELDS(*) = [2024, 01, 01] - integer, parameter :: DEFAULT_TIME_FIELDS(*) = REFERENCE_TIME_FIELDS(1:3) - integer, parameter :: UPDATE_TIME_FIELDS(*) = REFERENCE_TIME_FIELDS(4:) + integer, parameter :: REFERENCE_TIME_FIELDS(*) = [2024, 12, 31, 20, 0, 0] + integer, parameter :: NF = size(REFERENCE_TIME_FIELDS) + integer, parameter :: START_TIME_FIELDS(*) = [2024, 01, 01, 0, 0, 0] + integer, parameter :: DEFAULT_TIME_FIELDS(*) = [REFERENCE_TIME_FIELDS(1:3), 0, 0, 0] + integer, parameter :: UPDATE_TIME_FIELDS(*) = [0, 1, 1, REFERENCE_TIME_FIELDS(4:)] + integer :: actual(NF), expected(NF) character(len=*), parameter :: UPDATE_TIMESTRING = 'T20:00:00' character(len=*), parameter :: UPDATE_FREQ_STRING = '-' type(ESMF_Time) :: start_time type(ESMF_TimeInterval) :: timestep type(ESMF_Clock) :: clock - type(ESMF_Time) :: time + type(ESMF_Time) :: previous_time type(ESMF_TimeInterval) :: time_interval type(ESMF_Time) :: update_time type(ESMF_Time) :: reference_time - integer, parameter :: SUCCESS = 0 contains @Before subroutine set_up() integer :: status, rc + logical :: uninitialized status = SUCCESS - call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, _RC) + uninitialized = .not. ESMF_IsInitialized(_RC) +! if(uninitialized) call set_up_initial(REFERENCE_TIME_FIELDS, DEFAULT_TIME_FIELDS, UPDATE_TIME_FIELDS, _RC) + if(uninitialized) then + call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, logKindFlag=ESMF_LOGKIND_NONE, defaultLogKindFlag=ESMF_LOGKIND_NONE, _RC) + end if call ESMF_TimeIntervalSet(time_interval, _RC) call ESMF_TimeIntervalSet(timestep, s=TIME_STEP_IN_SECONDS, _RC) call make_esmf_time(START_TIME_FIELDS, start_time, _RC) - call make_esmf_time(DEFAULT_TIME_FIELDS, time, _RC) - call make_esmf_time(UPDATE_TIME_FIELDS, update_time, time_only=.TRUE., _RC) + call make_esmf_time(DEFAULT_TIME_FIELDS, previous_time, _RC) + call make_esmf_time(UPDATE_TIME_FIELDS, update_time, _RC) call make_esmf_time(REFERENCE_TIME_FIELDS, reference_time, _RC) clock = ESMF_ClockCreate(timestep=timestep, startTime=start_time, _RC) - + end subroutine set_up + subroutine set_up_initial(reference, default, update, rc) + integer, intent(in) :: reference(NF) + integer, intent(inout) :: default(NF) + integer, intent(inout) :: update(NF) + integer, optional, intent(out) :: rc + integer :: status + + status = SUCCESS + call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, logKindFlag=ESMF_LOGKIND_NONE, defaultLogKindFlag=ESMF_LOGKIND_NONE, _RC) + default = [reference(1:3), 0, 0, 0] + update = [0, 1, 1, reference(4:)] + _RETURN(_SUCCESS) + end subroutine set_up_initial + @After subroutine tear_down() integer :: status, rc @@ -53,41 +74,53 @@ contains call ESMF_TimeSet(start_time, _RC) call ESMF_TimeIntervalSet(timestep, _RC) call ESMF_ClockDestroy(clock, _RC) - call ESMF_TimeSet(time, _RC) + call ESMF_TimeSet(previous_time, _RC) call ESMF_TimeIntervalSet(time_interval, _RC) - call ESMF_TimeSet(time, _RC) call ESMF_TimeSet(update_time, _RC) call ESMF_TimeSet(reference_time, _RC) - call ESMF_Finalize(_RC) end subroutine tear_down - subroutine make_esmf_time(fields, time, time_only, rc) - integer, intent(in) :: fields(:) - type(ESMF_Time), intent(inout) :: time - logical, optional, intent(in) :: time_only + subroutine make_esmf_time(f, datetime, rc) + integer, intent(in) :: f(NF) + type(ESMF_Time), intent(inout) :: datetime integer, optional, intent(out) :: rc - integer :: status, n(6), i, k + integer :: status status = 0 - k = size(fields) - if(k == 0 .or. k > size(n)) status = -1 - _VERIFY(status) - n = 0 - i = 1 - if(present(time_only)) then - if(time_only) i = 4 - _HERE, 'time_only: ', time_only - _HERE, 'i, i+k-1, k, size(n): ', i, i+k-1, k, size(n) - _HERE, 'n = ', n - end if - n(i:i+k-1) = fields(1:k) - _HERE, 'n = ', n - call ESMF_TimeSet(time, yy=n(1), mm=n(2), dd=n(3), h=n(4), m=n(5), s=n(6), _RC) + call ESMF_TimeSet(datetime, yy=f(1), mm=f(2), dd=f(3), h=f(4), m=f(5), s=f(6), _RC) _RETURN(_SUCCESS) end subroutine make_esmf_time + subroutine get_int_time(datetime, n, rc) + type(ESMF_Time), intent(in) :: datetime + integer, intent(inout) :: n(NF) + integer, optional, intent(out) :: rc + integer :: status + + status = 0 + n = -1 + call ESMF_TimeGet(datetime, yy=n(1), mm=n(2), dd=n(3), h=n(4), m=n(5), s=n(6), _RC) + + _RETURN(_SUCCESS) + + end subroutine get_int_time + + subroutine get_int_time_interval(interval, n, rc) + type(ESMF_TimeInterval), intent(in) :: interval + integer, intent(inout) :: n(NF) + integer, optional, intent(out) :: rc + integer :: status + + status = 0 + n = 0 + !call ESMF_TimeIntervalGet(interval, yy=n(1), mm=n(2), dd=n(3), h=n(4), m=n(5), s=n(6), _RC) + + _RETURN(_SUCCESS) + + end subroutine get_int_time_interval + @Test subroutine test_get_adjusted_time type(ExtDataPointerUpdate) :: ex @@ -96,17 +129,18 @@ contains type(ESMF_TimeInterval) :: offset integer :: ios integer, parameter :: OFFSET_IN_SECONDS = 300 - logical :: actual_matches_expected write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS _VERIFY(ios) call ESMF_TimeIntervalSet(offset, s=OFFSET_IN_SECONDS, _RC) - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, time, clock, _RC) - actual_matches_expected = (ex%get_adjusted_time(time) == (time + offset)) - @assertTrue(actual_matches_expected, 'Adjusted time does match expected time.') + call get_int_time(previous_time+offset, expected, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, previous_time, clock, _RC) + call get_int_time(ex%get_adjusted_time(previous_time), actual, _RC) + @assertEqual(expected, actual, 'Adjusted time does match expected time.') end subroutine test_get_adjusted_time + @Test subroutine test_create_from_parameters_string_positive() type(ExtDataPointerUpdate) :: ex @@ -115,21 +149,27 @@ contains integer, parameter :: OFFSET_IN_SECONDS = 300 type(ESMF_TimeInterval) :: offset integer :: ios - logical, parameter :: FIRST_TIME = .TRUE. - logical :: actual_matches_expected, do_update - type(ESMF_Time) :: use_time, current_time + integer :: expected, actual +! logical, parameter :: FIRST_TIME = .TRUE. +! logical :: do_update + type(ESMF_TimeInterval) :: interval +! type(ESMF_Time) :: use_time, current_time write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS _VERIFY(ios) call ESMF_TimeIntervalSet(offset, s=OFFSET_IN_SECONDS, _RC) - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, time, clock, _RC) - call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) - actual_matches_expected = (use_time == reference_time+offset) - @assertTrue(actual_matches_expected, 'use_time does not match expected time.') + !call get_int_time_interval(offset, expected, _RC) + expected = OFFSET_IN_SECONDS + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, previous_time, clock, _RC) + !call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) + interval = ex%get_offset() + call ESMF_TimeIntervalGet(interval, s=actual, _RC) +! call get_int_time_interval(interval, actual, _RC) + @assertEqual(expected, actual, 'Updated time interval does match expected time interval.') end subroutine test_create_from_parameters_string_positive - @Test + !@Test subroutine test_create_from_parameters_string_negative() type(ExtDataPointerUpdate) :: ex integer :: status, rc @@ -138,53 +178,56 @@ contains type(ESMF_TimeInterval) :: offset integer :: ios logical, parameter :: FIRST_TIME = .TRUE. - logical :: actual_matches_expected, do_update + logical :: do_update type(ESMF_Time) :: use_time, current_time write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS offset_string = '-' // offset_string _VERIFY(ios) call ESMF_TimeIntervalSet(offset, s=-OFFSET_IN_SECONDS, _RC) - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, time, clock, _RC) +! call get_int_time(reference_time+offset, expected, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, previous_time, clock, _RC) call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) - actual_matches_expected = (use_time == reference_time-offset) - @assertTrue(actual_matches_expected, 'use_time does not match expected time.') +! call get_int_time(use_time, actual, _RC) + @assertEqual(expected, actual, 'Updated time does match expected time.') end subroutine test_create_from_parameters_string_negative - @Test + !@Test subroutine test_create_from_parameters_heartbeat_positive() type(ExtDataPointerUpdate) :: ex integer :: status, rc character(len=*), parameter :: OFFSET_STRING = HEARTBEAT_STRING type(ESMF_TimeInterval) :: offset logical, parameter :: FIRST_TIME = .TRUE. - logical :: actual_matches_expected, do_update + logical :: do_update type(ESMF_Time) :: use_time, current_time offset = timestep - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, time, clock, _RC) +! call get_int_time(reference_time + offset, expected,_RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, previous_time, clock, _RC) call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) - actual_matches_expected = (use_time == reference_time+offset) - @assertTrue(actual_matches_expected, 'use_time does not match expected time.') +! call get_int_time(use_time, actual, _RC) + @assertEqual(expected, actual, 'Updated time does match expected time.') end subroutine test_create_from_parameters_heartbeat_positive - @Test + !@Test subroutine test_create_from_parameters_heartbeat_negative() type(ExtDataPointerUpdate) :: ex integer :: status, rc character(len=*), parameter :: OFFSET_STRING = '-' // HEARTBEAT_STRING type(ESMF_TimeInterval) :: offset logical, parameter :: FIRST_TIME = .TRUE. - logical :: actual_matches_expected, do_update + logical :: do_update type(ESMF_Time) :: use_time, current_time offset = -timestep - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, time, clock, _RC) +! call get_int_time(reference_time + offset, expected,_RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, previous_time, clock, _RC) call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) - actual_matches_expected = (use_time == reference_time+offset) - @assertTrue(actual_matches_expected, 'use_time does not match expected time.') +! call get_int_time(use_time, actual, _RC) + @assertEqual(expected, actual, 'Updated time does match expected time.') end subroutine test_create_from_parameters_heartbeat_negative From d7874e3f263466dbbd47748f427e3b3ebc9bdb3e Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Tue, 15 Oct 2024 18:52:57 -0400 Subject: [PATCH 22/90] All unit tests pass. --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 33 +++-- .../tests/Test_ExtDataUpdatePointer.pf | 116 ++++++------------ 2 files changed, 63 insertions(+), 86 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index b1d38a2c495f..abb3d177912b 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -49,14 +49,11 @@ function get_adjusted_time(this,time,rc) result(adjusted_time) _RETURN(_SUCCESS) end function - function get_offset(this, rc) result(offset) + function get_offset(this) result(offset) type(ESMF_TimeInterval) :: offset class(ExtDataPointerUpdate), intent(in) :: this - integer, optional, intent(out) :: rc - integer :: status offset = this%offset - _RETURN(_SUCCESS) end function get_offset @@ -109,14 +106,36 @@ subroutine parse_heartbeat_timestring(timestring, is_heartbeat, multiplier) character(len=*), intent(in) :: timestring logical, intent(out) :: is_heartbeat integer, intent(out) :: multiplier - integer :: i + character(len=:), allocatable :: found_string multiplier = 1 - i = index(to_upper(timestring), HEARTBEAT_STRING) - is_heartbeat = (i > 0) + call split_on(to_upper(timestring), HEARTBEAT_STRING, found_string=found_string) + is_heartbeat = len(found_string) > 0 + ! For now, multiplier is simply set to 1. In the future, as needed, the before_string + ! and after_string arguments of split_on can be used to parse for a multiplier. end subroutine parse_heartbeat_timestring + subroutine split_on(string, substring, found_string, before_string, after_string) + character(len=*), intent(in) :: string, substring + character(len=:), allocatable, intent(out) :: found_string + character(len=:), optional, allocatable, intent(out) :: before_string, after_string + integer :: i + + i = index(to_upper(string), substring) + found_string = '' + if(i > 0) found_string = string(i:i+len(substring)-1) + if(present(before_string)) then + before_string = '' + if(i > 1) before_string = string(:i-1) + end if + if(present(after_string)) then + after_string = '' + if(i + len(substring) <= len(string)) after_string = string(i+len(substring):) + end if + + end subroutine split_on + function to_upper(s) result(u) character(len=:), allocatable :: u character(len=*), intent(in) :: s diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index e1f930ba96f8..aa83f710b18b 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -11,7 +11,6 @@ module Test_ExtDataUpdatePointer use MAPL_ExceptionHandling implicit none - integer, parameter :: SUCCESS = 0 integer, parameter :: TIME_STEP_IN_SECONDS = 1 integer, parameter :: REFERENCE_TIME_FIELDS(*) = [2024, 12, 31, 20, 0, 0] @@ -19,13 +18,13 @@ module Test_ExtDataUpdatePointer integer, parameter :: START_TIME_FIELDS(*) = [2024, 01, 01, 0, 0, 0] integer, parameter :: DEFAULT_TIME_FIELDS(*) = [REFERENCE_TIME_FIELDS(1:3), 0, 0, 0] integer, parameter :: UPDATE_TIME_FIELDS(*) = [0, 1, 1, REFERENCE_TIME_FIELDS(4:)] - integer :: actual(NF), expected(NF) character(len=*), parameter :: UPDATE_TIMESTRING = 'T20:00:00' character(len=*), parameter :: UPDATE_FREQ_STRING = '-' + character(len=*), parameter :: ERR_MSG = 'Actual offset does match expected offset.' type(ESMF_Time) :: start_time type(ESMF_TimeInterval) :: timestep type(ESMF_Clock) :: clock - type(ESMF_Time) :: previous_time + type(ESMF_Time) :: default_time type(ESMF_TimeInterval) :: time_interval type(ESMF_Time) :: update_time type(ESMF_Time) :: reference_time @@ -39,34 +38,19 @@ contains status = SUCCESS uninitialized = .not. ESMF_IsInitialized(_RC) -! if(uninitialized) call set_up_initial(REFERENCE_TIME_FIELDS, DEFAULT_TIME_FIELDS, UPDATE_TIME_FIELDS, _RC) if(uninitialized) then call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, logKindFlag=ESMF_LOGKIND_NONE, defaultLogKindFlag=ESMF_LOGKIND_NONE, _RC) end if call ESMF_TimeIntervalSet(time_interval, _RC) call ESMF_TimeIntervalSet(timestep, s=TIME_STEP_IN_SECONDS, _RC) call make_esmf_time(START_TIME_FIELDS, start_time, _RC) - call make_esmf_time(DEFAULT_TIME_FIELDS, previous_time, _RC) + call make_esmf_time(DEFAULT_TIME_FIELDS, default_time, _RC) call make_esmf_time(UPDATE_TIME_FIELDS, update_time, _RC) call make_esmf_time(REFERENCE_TIME_FIELDS, reference_time, _RC) clock = ESMF_ClockCreate(timestep=timestep, startTime=start_time, _RC) end subroutine set_up - subroutine set_up_initial(reference, default, update, rc) - integer, intent(in) :: reference(NF) - integer, intent(inout) :: default(NF) - integer, intent(inout) :: update(NF) - integer, optional, intent(out) :: rc - integer :: status - - status = SUCCESS - call ESMF_Initialize(defaultCalKind=ESMF_CALKIND_GREGORIAN, logKindFlag=ESMF_LOGKIND_NONE, defaultLogKindFlag=ESMF_LOGKIND_NONE, _RC) - default = [reference(1:3), 0, 0, 0] - update = [0, 1, 1, reference(4:)] - _RETURN(_SUCCESS) - end subroutine set_up_initial - @After subroutine tear_down() integer :: status, rc @@ -74,13 +58,14 @@ contains call ESMF_TimeSet(start_time, _RC) call ESMF_TimeIntervalSet(timestep, _RC) call ESMF_ClockDestroy(clock, _RC) - call ESMF_TimeSet(previous_time, _RC) + call ESMF_TimeSet(default_time, _RC) call ESMF_TimeIntervalSet(time_interval, _RC) call ESMF_TimeSet(update_time, _RC) call ESMF_TimeSet(reference_time, _RC) end subroutine tear_down + ! Set ESMF_Time using an integer array of datetime fields. subroutine make_esmf_time(f, datetime, rc) integer, intent(in) :: f(NF) type(ESMF_Time), intent(inout) :: datetime @@ -93,6 +78,7 @@ contains end subroutine make_esmf_time + ! Put ESMF_Time output args into an integer array. subroutine get_int_time(datetime, n, rc) type(ESMF_Time), intent(in) :: datetime integer, intent(inout) :: n(NF) @@ -107,20 +93,6 @@ contains end subroutine get_int_time - subroutine get_int_time_interval(interval, n, rc) - type(ESMF_TimeInterval), intent(in) :: interval - integer, intent(inout) :: n(NF) - integer, optional, intent(out) :: rc - integer :: status - - status = 0 - n = 0 - !call ESMF_TimeIntervalGet(interval, yy=n(1), mm=n(2), dd=n(3), h=n(4), m=n(5), s=n(6), _RC) - - _RETURN(_SUCCESS) - - end subroutine get_int_time_interval - @Test subroutine test_get_adjusted_time type(ExtDataPointerUpdate) :: ex @@ -129,13 +101,14 @@ contains type(ESMF_TimeInterval) :: offset integer :: ios integer, parameter :: OFFSET_IN_SECONDS = 300 + integer :: expected(NF), actual(NF) write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS _VERIFY(ios) call ESMF_TimeIntervalSet(offset, s=OFFSET_IN_SECONDS, _RC) - call get_int_time(previous_time+offset, expected, _RC) - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, previous_time, clock, _RC) - call get_int_time(ex%get_adjusted_time(previous_time), actual, _RC) + call get_int_time(default_time+offset, expected, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, default_time, clock, _RC) + call get_int_time(ex%get_adjusted_time(default_time), actual, _RC) @assertEqual(expected, actual, 'Adjusted time does match expected time.') end subroutine test_get_adjusted_time @@ -147,87 +120,72 @@ contains integer :: status, rc character(len=32) :: offset_string integer, parameter :: OFFSET_IN_SECONDS = 300 - type(ESMF_TimeInterval) :: offset integer :: ios integer :: expected, actual -! logical, parameter :: FIRST_TIME = .TRUE. -! logical :: do_update type(ESMF_TimeInterval) :: interval -! type(ESMF_Time) :: use_time, current_time write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS _VERIFY(ios) - call ESMF_TimeIntervalSet(offset, s=OFFSET_IN_SECONDS, _RC) - !call get_int_time_interval(offset, expected, _RC) expected = OFFSET_IN_SECONDS - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, previous_time, clock, _RC) - !call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, default_time, clock, _RC) interval = ex%get_offset() call ESMF_TimeIntervalGet(interval, s=actual, _RC) -! call get_int_time_interval(interval, actual, _RC) - @assertEqual(expected, actual, 'Updated time interval does match expected time interval.') + @assertEqual(expected, actual, ERR_MSG) end subroutine test_create_from_parameters_string_positive - !@Test + @Test subroutine test_create_from_parameters_string_negative() type(ExtDataPointerUpdate) :: ex integer :: status, rc character(len=32) :: offset_string integer, parameter :: OFFSET_IN_SECONDS = 300 - type(ESMF_TimeInterval) :: offset integer :: ios - logical, parameter :: FIRST_TIME = .TRUE. - logical :: do_update - type(ESMF_Time) :: use_time, current_time + integer :: expected, actual + type(ESMF_TimeInterval) :: interval write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS - offset_string = '-' // offset_string _VERIFY(ios) - call ESMF_TimeIntervalSet(offset, s=-OFFSET_IN_SECONDS, _RC) -! call get_int_time(reference_time+offset, expected, _RC) - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, previous_time, clock, _RC) - call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) -! call get_int_time(use_time, actual, _RC) - @assertEqual(expected, actual, 'Updated time does match expected time.') + offset_string = '-' // offset_string + expected = -OFFSET_IN_SECONDS + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, default_time, clock, _RC) + interval = ex%get_offset() + call ESMF_TimeIntervalGet(interval, s=actual, _RC) + @assertEqual(expected, actual, ERR_MSG) end subroutine test_create_from_parameters_string_negative - !@Test + @Test subroutine test_create_from_parameters_heartbeat_positive() type(ExtDataPointerUpdate) :: ex integer :: status, rc character(len=*), parameter :: OFFSET_STRING = HEARTBEAT_STRING - type(ESMF_TimeInterval) :: offset - logical, parameter :: FIRST_TIME = .TRUE. - logical :: do_update - type(ESMF_Time) :: use_time, current_time + type(ESMF_TimeInterval) :: offset, interval + integer :: expected, actual offset = timestep -! call get_int_time(reference_time + offset, expected,_RC) - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, previous_time, clock, _RC) - call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) -! call get_int_time(use_time, actual, _RC) - @assertEqual(expected, actual, 'Updated time does match expected time.') + call ESMF_TimeIntervalGet(offset, s=expected, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, default_time, clock, _RC) + interval = ex%get_offset() + call ESMF_TimeIntervalGet(interval, s=actual, _RC) + @assertEqual(expected, actual, ERR_MSG) end subroutine test_create_from_parameters_heartbeat_positive - !@Test + @Test subroutine test_create_from_parameters_heartbeat_negative() type(ExtDataPointerUpdate) :: ex integer :: status, rc character(len=*), parameter :: OFFSET_STRING = '-' // HEARTBEAT_STRING - type(ESMF_TimeInterval) :: offset - logical, parameter :: FIRST_TIME = .TRUE. - logical :: do_update - type(ESMF_Time) :: use_time, current_time + type(ESMF_TimeInterval) :: offset, interval + integer :: expected, actual offset = -timestep -! call get_int_time(reference_time + offset, expected,_RC) - call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, previous_time, clock, _RC) - call ex%check_update(do_update, use_time, current_time, FIRST_TIME, _RC) -! call get_int_time(use_time, actual, _RC) - @assertEqual(expected, actual, 'Updated time does match expected time.') + call ESMF_TimeIntervalGet(offset, s=expected, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, default_time, clock, _RC) + interval = ex%get_offset() + call ESMF_TimeIntervalGet(interval, s=actual, _RC) + @assertEqual(expected, actual, ERR_MSG) end subroutine test_create_from_parameters_heartbeat_negative From af8c1c3b8c78a2365b73ec08ddb6ade210588f6f Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Tue, 15 Oct 2024 23:50:40 -0400 Subject: [PATCH 23/90] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5eb5be4b69d..53c3c6d00dfc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- Allow update offsets of ±timestep in ExtData2G ### Changed From 2528357300d477edce4347dc8fb7045e2df15e6c Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 16 Oct 2024 07:27:38 -0400 Subject: [PATCH 24/90] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c5eb5be4b69d..9e91f66f39ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- Added commandline options to `checkpoint_benchmark.x` and `restart_benchmark.x` to allow for easier testing of different configurations. Note that the old configuration file style of input is allowed via the `--config_file` option (which overrides any other command line options) + ### Fixed ### Removed From 925414cc6caff447a9de863d01b063079106f51b Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Wed, 16 Oct 2024 11:16:03 -0400 Subject: [PATCH 25/90] Fix problem with cmake --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 10 +++++++--- gridcomps/ExtData2G/tests/CMakeLists.txt | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index abb3d177912b..81cdcef59785 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -102,14 +102,18 @@ subroutine create_from_parameters(this,update_time,update_freq,update_offset,tim end subroutine create_from_parameters - subroutine parse_heartbeat_timestring(timestring, is_heartbeat, multiplier) + subroutine parse_heartbeat_timestring(timestring, is_heartbeat, multiplier, rc) character(len=*), intent(in) :: timestring logical, intent(out) :: is_heartbeat integer, intent(out) :: multiplier character(len=:), allocatable :: found_string + character(len=:), allocatable :: upper + integer, optional, intent(out) :: rc + integer :: status multiplier = 1 - call split_on(to_upper(timestring), HEARTBEAT_STRING, found_string=found_string) + upper = ESMF_UtilStringUpperCase(timestring, _RC) + call split_on(upper, HEARTBEAT_STRING, found_string=found_string) is_heartbeat = len(found_string) > 0 ! For now, multiplier is simply set to 1. In the future, as needed, the before_string ! and after_string arguments of split_on can be used to parse for a multiplier. @@ -122,7 +126,7 @@ subroutine split_on(string, substring, found_string, before_string, after_string character(len=:), optional, allocatable, intent(out) :: before_string, after_string integer :: i - i = index(to_upper(string), substring) + i = index(string, substring) found_string = '' if(i > 0) found_string = string(i:i+len(substring)-1) if(present(before_string)) then diff --git a/gridcomps/ExtData2G/tests/CMakeLists.txt b/gridcomps/ExtData2G/tests/CMakeLists.txt index e1cb4a0ce709..46fb62b8dfeb 100644 --- a/gridcomps/ExtData2G/tests/CMakeLists.txt +++ b/gridcomps/ExtData2G/tests/CMakeLists.txt @@ -1,4 +1,4 @@ -set(MODULE_DIRECTORY "${esma_include}/gridcomps/ExtData2G/ExtData2G/tests") +set(MODULE_DIRECTORY "${esma_include}/gridcomps/ExtData2G/tests") set (test_srcs Test_ExtDataUpdatePointer.pf @@ -14,7 +14,7 @@ set_tests_properties(MAPL.ExtData2G.tests PROPERTIES LABELS "ESSENTIAL") # With this test, it was shown that if you are building with the GNU Fortran # compiler and *not* on APPLE, then you need to link with the dl library. if (CMAKE_Fortran_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) - target_link_libraries(ExtData2G.tests ${CMAKE_DL_LIBS}) + target_link_libraries(MAPL.ExtData2G.tests ${CMAKE_DL_LIBS}) endif () add_dependencies(build-tests MAPL.ExtData2G.tests) From 7a1a1b34f2879b8e0b7140422164193a8aadd760 Mon Sep 17 00:00:00 2001 From: Atanas Trayanov Date: Wed, 16 Oct 2024 14:10:16 -0400 Subject: [PATCH 26/90] Re-design the custom refresh method --- generic/MAPL_Generic.F90 | 42 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/generic/MAPL_Generic.F90 b/generic/MAPL_Generic.F90 index 00aa0ab960b9..9c8ef22dea2a 100644 --- a/generic/MAPL_Generic.F90 +++ b/generic/MAPL_Generic.F90 @@ -351,11 +351,33 @@ module MAPL_GenericMod module procedure MAPL_AddAttributeToFields_I4 end interface + interface + subroutine i_Run(gc, import_state, export_state, clock, unusable, rc) + use mapl_KeywordEnforcerMod + use ESMF + implicit none + type(ESMF_GridComp), intent(inout) :: gc + type(ESMF_State), intent(inout) :: import_state + type(ESMF_State), intent(inout) :: export_state + type(ESMF_Clock), intent(inout) :: clock + class(KeywordEnforcer), optional, intent(in) :: unusable + integer, optional, intent(out) :: rc + end subroutine i_Run + end interface + ! ======================================================================= integer, parameter :: LAST_ALARM = 99 + ! The next variable is the lesser of two evils: we need a flag the represents MAPL_CustomRefresh + ! In PR 28xx the assuption was that we could use ESMF_ReadRestart, which has other issues + ! Here we intention us ESMF_Method_None, since it is very unlikely someone in the GEOS/MAPL + ! community will use that flag + + type (ESMF_Method_Flag), public :: MAPL_Method_Refresh = ESMF_Method_None + integer, parameter, public :: MAPL_CustomRefreshPhase = 99 + type MAPL_GenericWrap type(MAPL_MetaComp ), pointer :: MAPLOBJ end type MAPL_GenericWrap @@ -425,6 +447,10 @@ module MAPL_GenericMod integer , pointer :: phase_final(:) => null() integer , pointer :: phase_record(:) => null() integer , pointer :: phase_coldstart(:)=> null() + integer , pointer :: phase_refresh(:)=> null() + procedure(), public, nopass , pointer :: customRefresh => null() + +! procedure(), pointer, nopass :: run_entry_point => null() ! Make accessors? type(ESMF_GridComp) :: RootGC @@ -2859,9 +2885,13 @@ recursive subroutine MAPL_GenericRefresh ( GC, IMPORT, EXPORT, CLOCK, RC ) call MAPL_StateRefresh (GC, IMPORT, EXPORT, CLOCK, RC=status ) _VERIFY(status) - if (associated(STATE%phase_coldstart)) then - call ESMF_GridCompReadRestart(GC, importState=import, & - exportState=export, clock=CLOCK, phase=MAPL_MAX_PHASES+1, userRC=userRC, _RC) +! I_Run + if (associated(STATE%customRefresh)) then + call ESMF_GridCompInitialize(GC, importState=import, & + exportState=export, clock=CLOCK, & + phase=MAPL_CustomRefreshPhase, & + userRC=userRC, _RC) + _VERIFY(userRC) endif endif @@ -3989,6 +4019,12 @@ subroutine MAPL_GridCompSetEntryPoint(GC, registeredMethod, usersRoutine, RC) phase = MAPL_AddMethod(META%phase_record, RC=status) else if (registeredMethod == ESMF_METHOD_READRESTART) then phase = MAPL_AddMethod(META%phase_coldstart, RC=status) + else if (registeredMethod == MAPL_METHOD_REFRESH) then + phase = MAPL_AddMethod(META%phase_refresh, RC=status) + meta%customRefresh => usersRoutine + call ESMF_GridCompSetEntryPoint(GC, ESMF_METHOD_INITIALIZE, & + usersRoutine, phase=MAPL_CustomRefreshPhase, _RC) + _RETURN(ESMF_SUCCESS) else _RETURN(ESMF_FAILURE) endif From bc55755bc98251b3523fd7dc925d8ca50c02de6e Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Wed, 16 Oct 2024 16:30:47 -0400 Subject: [PATCH 27/90] Add tests to compare results from ISO8601 Duration and HEARTBEAT --- .../tests/Test_ExtDataUpdatePointer.pf | 75 ++++++++++++++++--- 1 file changed, 64 insertions(+), 11 deletions(-) diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index aa83f710b18b..7e78ee71eab3 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -18,6 +18,7 @@ module Test_ExtDataUpdatePointer integer, parameter :: START_TIME_FIELDS(*) = [2024, 01, 01, 0, 0, 0] integer, parameter :: DEFAULT_TIME_FIELDS(*) = [REFERENCE_TIME_FIELDS(1:3), 0, 0, 0] integer, parameter :: UPDATE_TIME_FIELDS(*) = [0, 1, 1, REFERENCE_TIME_FIELDS(4:)] + integer, parameter :: STRLEN = 32 character(len=*), parameter :: UPDATE_TIMESTRING = 'T20:00:00' character(len=*), parameter :: UPDATE_FREQ_STRING = '-' character(len=*), parameter :: ERR_MSG = 'Actual offset does match expected offset.' @@ -93,11 +94,22 @@ contains end subroutine get_int_time + subroutine make_offset_string(offset, offset_string, rc) + integer, intent(in) :: offset + character(len=*), intent(out) :: offset_string + integer, optional, intent(out) :: rc + integer :: status + + write(offset_string, fmt='("PT", I03, "S")', iostat=status) offset + _VERIFY(status) + + end subroutine make_offset_string + @Test subroutine test_get_adjusted_time type(ExtDataPointerUpdate) :: ex integer :: status, rc - character(len=32) :: offset_string + character(len=STRLEN) :: offset_string type(ESMF_TimeInterval) :: offset integer :: ios integer, parameter :: OFFSET_IN_SECONDS = 300 @@ -113,19 +125,16 @@ contains end subroutine test_get_adjusted_time - @Test subroutine test_create_from_parameters_string_positive() type(ExtDataPointerUpdate) :: ex integer :: status, rc - character(len=32) :: offset_string + character(len=STRLEN) :: offset_string integer, parameter :: OFFSET_IN_SECONDS = 300 - integer :: ios integer :: expected, actual type(ESMF_TimeInterval) :: interval - write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS - _VERIFY(ios) + call make_offset_string(OFFSET_IN_SECONDS, offset_string, _RC) expected = OFFSET_IN_SECONDS call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, default_time, clock, _RC) interval = ex%get_offset() @@ -133,19 +142,17 @@ contains @assertEqual(expected, actual, ERR_MSG) end subroutine test_create_from_parameters_string_positive - + @Test subroutine test_create_from_parameters_string_negative() type(ExtDataPointerUpdate) :: ex integer :: status, rc - character(len=32) :: offset_string + character(len=STRLEN) :: offset_string integer, parameter :: OFFSET_IN_SECONDS = 300 - integer :: ios integer :: expected, actual type(ESMF_TimeInterval) :: interval - write(offset_string, fmt='("PT", I03, "S")', iostat=ios) OFFSET_IN_SECONDS - _VERIFY(ios) + call make_offset_string(OFFSET_IN_SECONDS, offset_string, _RC) offset_string = '-' // offset_string expected = -OFFSET_IN_SECONDS call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, default_time, clock, _RC) @@ -189,4 +196,50 @@ contains end subroutine test_create_from_parameters_heartbeat_negative + @Test + subroutine test_compare_positive_string_to_positive_heartbeat() + type(ExtDataPointerUpdate) :: ex_str, ex_hb + integer :: status, rc + type(ESMF_TimeInterval) :: intv_str, intv_hb + integer :: expected, actual + character(len=STRLEN) :: offset_string + + call make_offset_string(TIME_STEP_IN_SECONDS, offset_string, _RC) + + call ex_str%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, default_time, clock, _RC) + intv_str = ex_str%get_offset() + call ESMF_TimeIntervalGet(intv_str, s=expected, _RC) + + call ex_hb%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, HEARTBEAT_STRING, default_time, clock, _RC) + intv_hb = ex_hb%get_offset() + call ESMF_TimeIntervalGet(intv_hb, s=actual, _RC) + + @assertEqual(expected, actual, ERR_MSG) + + end subroutine test_compare_positive_string_to_positive_heartbeat + + @Test + subroutine test_compare_negative_string_to_negative_heartbeat() + type(ExtDataPointerUpdate) :: ex_str, ex_hb + integer :: status, rc + type(ESMF_TimeInterval) :: intv_str, intv_hb + integer :: expected, actual + character(len=STRLEN) :: offset_string + + call make_offset_string(TIME_STEP_IN_SECONDS, offset_string, _RC) + offset_string = '-' // offset_string + + call ex_str%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, offset_string, default_time, clock, _RC) + intv_str = ex_str%get_offset() + call ESMF_TimeIntervalGet(intv_str, s=expected, _RC) + + call ex_hb%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, '-' // HEARTBEAT_STRING, default_time, clock, _RC) + intv_hb = ex_hb%get_offset() + call ESMF_TimeIntervalGet(intv_hb, s=actual, _RC) + + @assertEqual(expected, actual, ERR_MSG) + + end subroutine test_compare_negative_string_to_negative_heartbeat + + end module Test_ExtDataUpdatePointer From cfdff25ffbf5acb9126f1e4125a927a4991e026a Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Wed, 16 Oct 2024 18:09:27 -0400 Subject: [PATCH 28/90] Allow "+" in front of "update_offset" string. --- gridcomps/ExtData2G/ExtDataUpdatePointer.F90 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 index 81cdcef59785..6bb45a82fccd 100644 --- a/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 +++ b/gridcomps/ExtData2G/ExtDataUpdatePointer.F90 @@ -70,7 +70,7 @@ subroutine create_from_parameters(this,update_time,update_freq,update_offset,tim logical :: negative_offset type(ESMF_TimeInterval) :: timestep integer :: multiplier - integer :: i + integer :: i, j logical :: is_heartbeat this%last_checked = time @@ -89,7 +89,10 @@ subroutine create_from_parameters(this,update_time,update_freq,update_offset,tim this%update_freq = string_to_esmf_timeinterval(update_freq,_RC) end if i = index(update_offset,"-") + 1 + j = index(update_offset, '+') + 1 + _ASSERT(i==1 .or. j==1, '"+" and "-" cannot both be present in update_offset string.') negative_offset = i > 1 + if(.not. negative_offset) i = j call parse_heartbeat_timestring(update_offset(i:), is_heartbeat=is_heartbeat, multiplier=multiplier) if(is_heartbeat) then this%offset = multiplier * timestep From a82497f610f738195d31f53654f53bc068df9cf6 Mon Sep 17 00:00:00 2001 From: Darian Boggs Date: Thu, 17 Oct 2024 00:43:12 -0400 Subject: [PATCH 29/90] Add tests for "+HEARTBEAT" strings --- .../tests/Test_ExtDataUpdatePointer.pf | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf index 7e78ee71eab3..7abcf9859aea 100644 --- a/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf +++ b/gridcomps/ExtData2G/tests/Test_ExtDataUpdatePointer.pf @@ -241,5 +241,51 @@ contains end subroutine test_compare_negative_string_to_negative_heartbeat + @Test + subroutine test_create_from_parameters_heartbeat_positive_explicit() + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + character(len=*), parameter :: OFFSET_STRING = '+' // HEARTBEAT_STRING + type(ESMF_TimeInterval) :: offset, interval + integer :: expected, actual + + offset = timestep + call ESMF_TimeIntervalGet(offset, s=expected, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, default_time, clock, _RC) + interval = ex%get_offset() + call ESMF_TimeIntervalGet(interval, s=actual, _RC) + @assertEqual(expected, actual, ERR_MSG) + + end subroutine test_create_from_parameters_heartbeat_positive_explicit + + @Test + subroutine test_create_from_parameters_heartbeat_positive_negative() + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + character(len=*), parameter :: OFFSET_STRING = '+-' // HEARTBEAT_STRING + type(ESMF_TimeInterval) :: offset, interval + integer :: expected, actual + + offset = timestep + call ESMF_TimeIntervalGet(offset, s=expected, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, default_time, clock, rc=status) + @assertFalse(status == 0, 'An exception should have been thrown.') + + end subroutine test_create_from_parameters_heartbeat_positive_negative + + @Test + subroutine test_create_from_parameters_heartbeat_negative_positive() + type(ExtDataPointerUpdate) :: ex + integer :: status, rc + character(len=*), parameter :: OFFSET_STRING = '-+' // HEARTBEAT_STRING + type(ESMF_TimeInterval) :: offset, interval + integer :: expected, actual + + offset = timestep + call ESMF_TimeIntervalGet(offset, s=expected, _RC) + call ex%create_from_parameters(UPDATE_TIMESTRING, UPDATE_FREQ_STRING, OFFSET_STRING, default_time, clock, rc=status) + @assertFalse(status == 0, 'An exception should have been thrown.') + + end subroutine test_create_from_parameters_heartbeat_negative_positive end module Test_ExtDataUpdatePointer From 69bd3db9b96ba0fd7c48370d65f3f12133df3b23 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Thu, 17 Oct 2024 13:56:16 -0400 Subject: [PATCH 30/90] Have to force the turn off --- pfio/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pfio/CMakeLists.txt b/pfio/CMakeLists.txt index 26f151530a68..15f43fdc4388 100644 --- a/pfio/CMakeLists.txt +++ b/pfio/CMakeLists.txt @@ -155,7 +155,7 @@ check_c_source_compiles(" if (Baselibs_FOUND) message(STATUS "Baselibs found, zstandard capability not possible") - set(NETCDF_HAS_ZSTD FALSE) + set(NETCDF_HAS_ZSTD FALSE CACHE BOOL "netCDF has zstandard capability" FORCE) endif () if (NETCDF_HAS_ZSTD) From 654a45393f9ca86936997e7778e4d273560e504f Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Fri, 18 Oct 2024 14:06:26 -0400 Subject: [PATCH 31/90] Use positive options --- benchmarks/io/checkpoint_simulator/README.md | 28 ++++++------- .../checkpoint_simulator.F90 | 40 +++++++++---------- benchmarks/io/restart_simulator/README.md | 26 ++++++------ .../restart_simulator/restart_simulator.F90 | 32 +++++++-------- 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/benchmarks/io/checkpoint_simulator/README.md b/benchmarks/io/checkpoint_simulator/README.md index c74a048512c2..a576c2d8aa83 100644 --- a/benchmarks/io/checkpoint_simulator/README.md +++ b/benchmarks/io/checkpoint_simulator/README.md @@ -5,20 +5,20 @@ The code has the following command line options: optional arguments: -h, --help This message. --config_file The configuration file to use - --nx The number of cells in the x direction (default=4) - --ny The number of cells in the y direction (default=4) - --im_world The resolution of the cubed sphere (default=90) - --lm The number of levels in each 3D variable (default=137) - --num_writers The number of processes that will write (default=1) - --num_arrays The number of 3D arrays to write (default=5) - --ntrials The number of trials to run (default=3) - --split_file Split the file into multiple files (default=False) - --gather_3d Gather 3D data (default=False) - --write_barrier Add a write barrier (default=False) - --no_random_data Do not use random data (default=False) - --do_no_writes Do not write data (default=False) - --no_netcdf_writes Do not write data as netcdf (default=False) - --no_chunking Do not chunk (default=False) + --nx The number of cells in the x direction (default: 4) + --ny The number of cells in the y direction (default: 4) + --im_world The resolution of the cubed sphere (default: 90) + --lm The number of levels in each 3D variable (default: 137) + --num_writers The number of processes that will write (default: 1) + --num_arrays The number of 3D arrays to write (default: 5) + --ntrials The number of trials to run (default: 3) + --split_file Split the file into multiple files (default: do not split) + --gather_3d Gather all levels at once instead of one at a time (default: gather one at a time) + --write_barrier Add a barrier after every write (default: no barrier) + --static_data Use static data (rank of process) instead of random data (default: random data) + --suppress_writes Do not write data (default: write data) + --write_binary Write binary data instead of NetCDF (default: write NetCDF) + --no_chunking Do not chunk output (default: chunk the output) ``` NOTE 1: If you specify a `config_file` it must be an ESMF Config file with the following options: diff --git a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 index 5b213d271b57..7643f957e906 100644 --- a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 +++ b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 @@ -94,79 +94,79 @@ function parse_arguments() result(options) type="string") call parser%add_argument("--nx", & - help="The number of cells in the x direction (default=4)", & + help="The number of cells in the x direction (default: 4)", & action="store", & type="integer", & default=4) call parser%add_argument("--ny", & - help="The number of cells in the y direction (default=4)", & + help="The number of cells in the y direction (default: 4)", & action="store", & type="integer", & default=4) call parser%add_argument("--im_world", & - help="The resolution of the cubed sphere (default=90)", & + help="The resolution of the cubed sphere (default: 90)", & action="store", & type="integer", & default=90) call parser%add_argument("--lm", & - help="The number of levels in each 3D variable (default=137)", & + help="The number of levels in each 3D variable (default: 137)", & action="store", & type="integer", & default=137) call parser%add_argument("--num_writers", & - help="The number of processes that will write (default=1)", & + help="The number of processes that will write (default: 1)", & action="store", & type="integer", & default=1) call parser%add_argument("--num_arrays", & - help="The number of 3D arrays to write (default=5)", & + help="The number of 3D arrays to write (default: 5)", & action="store", & type="integer", & default=5) call parser%add_argument("--ntrials", & - help="The number of trials to run (default=3)", & + help="The number of trials to run (default: 3)", & action="store", & type="integer", & default=3) call parser%add_argument("--split_file", & - help="Split the file into multiple files (default=False)", & + help="Split the file into multiple files (default: do not split)", & action="store_true", & default=.false.) call parser%add_argument("--gather_3d", & - help="Gather 3D data (default=False)", & + help="Gather all levels at once instead of one at a time (default: gather one at a time)", & action="store_true", & default=.false.) call parser%add_argument("--write_barrier", & - help="Add a write barrier (default=False)", & + help="Add a barrier after every write (default: no barrier)", & action="store_true", & default=.false.) - call parser%add_argument("--no_random_data", & - help="Do not use random data (default=False)", & + call parser%add_argument("--static_data", & + help="Use static data (rank of process) instead of random data (default: random data)", & action="store_true", & default=.False.) - call parser%add_argument("--do_no_writes", & - help="Do not write data (default=False)", & + call parser%add_argument("--suppress_writes", & + help="Do not write data (default: write data)", & action="store_true", & default=.False.) - call parser%add_argument("--no_netcdf_writes", & - help="Do not write data as netcdf (default=False)", & + call parser%add_argument("--write_binary", & + help="Write binary data instead of NetCDF (default: write NetCDF)", & action="store_true", & default=.false.) call parser%add_argument("--no_chunking", & - help="Do not chunk (default=False)", & + help="Do not chunk output (default: chunk the output)", & action="store_true", & default=.false.) @@ -213,15 +213,15 @@ subroutine get_cli_options(options, cli) option => options%at("write_barrier") if (associated(option)) call cast(option, cli%write_barrier) - option => options%at("no_random_data") + option => options%at("static_data") if (associated(option)) call cast(option, tmp) cli%random_data = .not. tmp - option => options%at("do_no_writes") + option => options%at("suppress_writes") if (associated(option)) call cast(option, tmp) cli%do_writes = .not. tmp - option => options%at("no_netcdf_writes") + option => options%at("write_binary") if (associated(option)) call cast(option, tmp) cli%netcdf_writes = .not. tmp diff --git a/benchmarks/io/restart_simulator/README.md b/benchmarks/io/restart_simulator/README.md index a5e31dd72dce..fe36e1567ff6 100644 --- a/benchmarks/io/restart_simulator/README.md +++ b/benchmarks/io/restart_simulator/README.md @@ -5,19 +5,19 @@ The code has the following command line options: ``` -h, --help This message. --config_file The configuration file to use - --nx The number of cells in the x direction (default=4) - --ny The number of cells in the y direction (default=4) - --im_world The resolution of the cubed sphere (default=90) - --lm The number of levels in each 3D variable (default=137) - --num_readers The number of processes that will read (default=1) - --num_arrays The number of 3D arrays to read (default=5) - --ntrials The number of trials to run (default=3) - --split_file Split the file into multiple files (default=False) - --scatter_3d Scatter 3D data (default=False) - --read_barrier Add a read barrier (default=False) - --no_random_data Do not random data (default=False) - --do_no_reads Do not read data (default=False) - --no_netcdf_reads Do not read data as netcdf (default=False) + --nx The number of cells in the x direction (default: 4) + --ny The number of cells in the y direction (default: 4) + --im_world The resolution of the cubed sphere (default: 90) + --lm The number of levels in each 3D variable (default: 137) + --num_readers The number of processes that will read (default: 1) + --num_arrays The number of 3D arrays to read (default: 5) + --ntrials The number of trials to run (default: 3) + --split_file Read split files instead of a single file (default: read single file) + --scatter_3d Scatter all the levels at once instead of one at a time (default: scatter one at a time) + --read_barrier Add a barrier after every read (default: no barrier) + --static_data Use static data (rank of process) instead of random data (default: random data) + --suppress_reads Do not read data (default: read data) + --read_binary Read binary data instead of netCDF (default: netCDF data) ``` NOTE 1: This program *REQUIRES* a file called `checkpoint.nc4` that is generated by the `checkpoint_benchmark.x` code diff --git a/benchmarks/io/restart_simulator/restart_simulator.F90 b/benchmarks/io/restart_simulator/restart_simulator.F90 index d57facd1f464..a5f777e984ec 100644 --- a/benchmarks/io/restart_simulator/restart_simulator.F90 +++ b/benchmarks/io/restart_simulator/restart_simulator.F90 @@ -92,74 +92,74 @@ function parse_arguments() result(options) type="string") call parser%add_argument("--nx", & - help="The number of cells in the x direction (default=4)", & + help="The number of cells in the x direction (default: 4)", & action="store", & type="integer", & default=4) call parser%add_argument("--ny", & - help="The number of cells in the y direction (default=4)", & + help="The number of cells in the y direction (default: 4)", & action="store", & type="integer", & default=4) call parser%add_argument("--im_world", & - help="The resolution of the cubed sphere (default=90)", & + help="The resolution of the cubed sphere (default: 90)", & action="store", & type="integer", & default=90) call parser%add_argument("--lm", & - help="The number of levels in each 3D variable (default=137)", & + help="The number of levels in each 3D variable (default: 137)", & action="store", & type="integer", & default=137) call parser%add_argument("--num_readers", & - help="The number of processes that will read (default=1)", & + help="The number of processes that will read (default: 1)", & action="store", & type="integer", & default=1) call parser%add_argument("--num_arrays", & - help="The number of 3D arrays to read (default=5)", & + help="The number of 3D arrays to read (default: 5)", & action="store", & type="integer", & default=5) call parser%add_argument("--ntrials", & - help="The number of trials to run (default=3)", & + help="The number of trials to run (default: 3)", & action="store", & type="integer", & default=3) call parser%add_argument("--split_file", & - help="Split the file into multiple files (default=False)", & + help="Read split files instead of a single file (default: read single file)", & action="store_true", & default=.false.) call parser%add_argument("--scatter_3d", & - help="Scatter 3D data (default=False)", & + help="Scatter all the levels at once instead of one at a time (default: scatter one at a time)", & action="store_true", & default=.false.) call parser%add_argument("--read_barrier", & - help="Add a read barrier (default=False)", & + help="Add a barrier after every read (default: no barrier)", & action="store_true", & default=.false.) - call parser%add_argument("--no_random_data", & - help="Do not random data (default=False)", & + call parser%add_argument("--static_data", & + help="Use static data (rank of process) instead of random data (default: random data)", & action="store_true", & default=.false.) - call parser%add_argument("--do_no_reads", & - help="Do not read data (default=False)", & + call parser%add_argument("--suppress_reads", & + help="Do not read data (default: read data)", & action="store_true", & default=.false.) - call parser%add_argument("--no_netcdf_reads", & - help="Do not read data as netcdf (default=False)", & + call parser%add_argument("--read_binary", & + help="Read binary data instead of netCDF (default: netCDF data)", & action="store_true", & default=.false.) From 76a668d6eadfb84ac02ffa944883c725297b6634 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 22 Oct 2024 14:47:24 -0400 Subject: [PATCH 32/90] Update to ESMA_env v4.31.0 (ESMF 8.7.0) --- CHANGELOG.md | 14 ++++++++++++++ components.yaml | 4 ++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b19301c2a7c..c0d1fbb5bc9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added + - Allow update offsets of ±timestep in ExtData2G ### Changed - Update ESMF version for Baselibs to match that of Spack for consistency +- Update `components.yaml` + - ESMA_env v4.32.0 + - Baselibs 7.27.0 + - ESMF 8.7.0 + - curl 8.10.1 + - NCO 5.2.8 + - CDO 2.4.4 + - GSL 2.8 + - jpeg 9f + - Various build fixes + - ESMA_cmake v3.52.0 + - Fixes for using MAPL as a library in spack builds of GEOSgcm + - Various backports from v4 ### Fixed diff --git a/components.yaml b/components.yaml index 4360ab32a56f..271f302b4f9d 100644 --- a/components.yaml +++ b/components.yaml @@ -5,13 +5,13 @@ MAPL: ESMA_env: local: ./ESMA_env remote: ../ESMA_env.git - tag: v4.30.1 + tag: v4.31.0 develop: main ESMA_cmake: local: ./ESMA_cmake remote: ../ESMA_cmake.git - tag: v3.51.0 + tag: v3.52.0 develop: develop ecbuild: From 7cfb7d629f7b1c748b464d5560e7b4bf81fa876f Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 23 Oct 2024 07:38:06 -0400 Subject: [PATCH 33/90] Update readmes --- benchmarks/io/checkpoint_simulator/README.md | 2 +- .../checkpoint_simulator.F90 | 2 +- benchmarks/io/restart_simulator/README.md | 21 +++++++++---------- .../restart_simulator/restart_simulator.F90 | 6 +++--- 4 files changed, 15 insertions(+), 16 deletions(-) diff --git a/benchmarks/io/checkpoint_simulator/README.md b/benchmarks/io/checkpoint_simulator/README.md index a576c2d8aa83..bce5cf452ab0 100644 --- a/benchmarks/io/checkpoint_simulator/README.md +++ b/benchmarks/io/checkpoint_simulator/README.md @@ -34,7 +34,7 @@ NOTE 1: If you specify a `config_file` it must be an ESMF Config file with the f - "SPLIT\_FILE:" default `.false.`, if `.true.`, each writer writes to and independent file - "WRITE\_BARRIER:" default `.false.`, add a barrier before each write to for synchronization - "DO\_WRITES:" default `.true.`, if `.false.` skips writing (so just an mpi test at that point) -- "NTRIALS:" default 1, the number of trials to make writing +- "NTRIALS:" default 3, the number of trials to make writing - "RANDOM\_DATA:" default `.true.`, if `.true.` will arrays with random data, if `.false.` sets the array to the rank of the process NOTE 2: that whatever you set NX and NY to the program must be run on `6*NX*NY` processors and the number of writers must evenly divide `6*NY` diff --git a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 index 7643f957e906..3627e50ffde3 100644 --- a/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 +++ b/benchmarks/io/checkpoint_simulator/checkpoint_simulator.F90 @@ -257,7 +257,7 @@ subroutine set_parameters_by_config(this,config_file) this%write_barrier = get_logical_key(config,"WRITE_BARRIER:",.false.) this%do_writes = get_logical_key(config,"DO_WRITES:",.true.) this%netcdf_writes = get_logical_key(config,"NETCDF_WRITES:",.true.) - this%n_trials = get_integer_key(config,"NTRIALS:",1) + this%n_trials = get_integer_key(config,"NTRIALS:",3) this%random = get_logical_key(config,"RANDOM_DATA:",.true.) this%write_counter = 0 diff --git a/benchmarks/io/restart_simulator/README.md b/benchmarks/io/restart_simulator/README.md index fe36e1567ff6..d89c48741b53 100644 --- a/benchmarks/io/restart_simulator/README.md +++ b/benchmarks/io/restart_simulator/README.md @@ -1,4 +1,4 @@ -This benchmark simulates writing a series of 3D variables of a given cubed-sphere resolution to a file using the same strategies as used by the real checkpoint code in MAPL +This benchmark simulates reading a series of 3D variables of a given cubed-sphere resolution to a file using the same strategies as used by the real checkpoint code in MAPL The code has the following command line options: @@ -20,7 +20,7 @@ The code has the following command line options: --read_binary Read binary data instead of netCDF (default: netCDF data) ``` -NOTE 1: This program *REQUIRES* a file called `checkpoint.nc4` that is generated by the `checkpoint_benchmark.x` code +NOTE 1: This program *REQUIRES* a file called `checkpoint.nc4` that is generated by the `checkpoint_benchmark.x` code. NOTE 2: If you specify a `config_file` it must be an ESMF Config file with the following options: @@ -28,14 +28,13 @@ NOTE 2: If you specify a `config_file` it must be an ESMF Config file with the f - "NY:" the y distribution for each face - "IM\_WORLD:" the cube resolution - "LM:" the number of levels -- "NUM\_WRITERS:" the number of writing processes either to a single or independent files -- "NUM\_ARRAYS:" the number of 3D variables to write to the file -- "CHUNK:" whether to chunk, default `.true.` -- "SCATTER\_3D:" gather all levels at once (default is `.false` which means a level at a time is gathered) -- "SPLIT\_FILE:" default `.false`, if `.true.`, each writer writes to and independent file -- "WRITE\_BARRIER:" default `.false`, add a barrier before each write to for synchronization -- "DO\_WRITES:" default `.true.`, if `.false` skips writing (so just an mpi test at that point) -- "NTRIAL:" default 1, the number of trials to make writing +- "NUM\_READERS:" the number of reading processes either from a single or independent files +- "NUM\_ARRAYS:" the number of 3D variables to read from the file +- "SCATTER\_3D:" scatter all levels at once (default is `.false` which means a level at a time is gathered) +- "SPLIT\_FILE:" default `.false`, if `.true.`, each reader reads from an independent file +- "READ\_BARRIER:" default `.false`, add a barrier before each read for synchronization +- "DO\_READS:" default `.true.`, if `.false` skips reading (so just an mpi test at that point) +- "NTRIALS:" default 3, the number of trials to make for each read - "RANDOM\_DATA:" default `.true.`, if `.true.` will arrays with random data, if `.false` sets the array to the rank of the process -NOTE 3: whatever you set NX and NY to the program must be run on `6*NY*NY` processors and the number of writers must evenly divide `6*NY` +NOTE 3: whatever you set NX and NY to the program must be run on `6*NY*NY` processors and the number of readers must evenly divide `6*NY` diff --git a/benchmarks/io/restart_simulator/restart_simulator.F90 b/benchmarks/io/restart_simulator/restart_simulator.F90 index a5f777e984ec..710b59c2c6c3 100644 --- a/benchmarks/io/restart_simulator/restart_simulator.F90 +++ b/benchmarks/io/restart_simulator/restart_simulator.F90 @@ -241,10 +241,10 @@ subroutine set_parameters_by_config(this,config_file) this%scatter_3d = get_logical_key(config,"SCATTER_3D:",.false.) this%split_file = get_logical_key(config,"SPLIT_FILE:",.false.) this%extra_info = get_logical_key(config,"EXTRA_INFO:",.false.) - this%read_barrier = get_logical_key(config,"read_BARRIER:",.false.) + this%read_barrier = get_logical_key(config,"READ_BARRIER:",.false.) this%do_reads = get_logical_key(config,"DO_READS:",.true.) - this%netcdf_reads = get_logical_key(config,"netcdf_reads:",.true.) - this%n_trials = get_integer_key(config,"NTRIALS:",1) + this%netcdf_reads = get_logical_key(config,"NETCDF_READS:",.true.) + this%n_trials = get_integer_key(config,"NTRIALS:",3) this%random = get_logical_key(config,"RANDOM_DATA:",.true.) this%read_counter = 0 From 0ccb0d3cfd6ca6091c78555b5113d4f38de58768 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 23 Oct 2024 15:07:04 -0400 Subject: [PATCH 34/90] Update CI to use Baselibs 7.27.0 --- .circleci/config.yml | 4 ++-- .github/workflows/workflow.yml | 4 ++-- CHANGELOG.md | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0de92fb286bb..049218678a44 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -16,12 +16,12 @@ parameters: # Anchors to prevent forgetting to update a version os_version: &os_version ubuntu20 -baselibs_version: &baselibs_version v7.25.0 +baselibs_version: &baselibs_version v7.27.0 bcs_version: &bcs_version v11.6.0 tag_build_arg_name: &tag_build_arg_name maplversion orbs: - ci: geos-esm/circleci-tools@4 + ci: geos-esm/circleci-tools@dev:09c2f0d8f885c7aa78de6570ecb4678e722c88c5 workflows: build-and-test-MAPL: diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index d6c9d61fbf4d..4e74fdea31f0 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -35,7 +35,7 @@ jobs: name: Build and Test MAPL GNU runs-on: ubuntu-latest container: - image: gmao/ubuntu20-geos-env-mkl:v7.25.0-openmpi_5.0.2-gcc_13.2.0 + image: gmao/ubuntu20-geos-env-mkl:v7.27.0-openmpi_5.0.2-gcc_13.2.0 # Per https://github.com/actions/virtual-environments/issues/1445#issuecomment-713861495 # It seems like we might not need secrets on GitHub Actions which is good for forked # pull requests @@ -86,7 +86,7 @@ jobs: name: Build and Test MAPL Intel runs-on: ubuntu-latest container: - image: gmao/ubuntu20-geos-env:v7.25.0-intelmpi_2021.13-ifort_2021.13 + image: gmao/ubuntu20-geos-env:v7.27.0-intelmpi_2021.13-ifort_2021.13 # Per https://github.com/actions/virtual-environments/issues/1445#issuecomment-713861495 # It seems like we might not need secrets on GitHub Actions which is good for forked # pull requests diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d1fbb5bc9e..c36bba97a714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESMA_cmake v3.52.0 - Fixes for using MAPL as a library in spack builds of GEOSgcm - Various backports from v4 +- Update CI to use v7.27.0 Baselibs ### Fixed From cc66518878c69780a6b9dd8888ba1e32991e3b43 Mon Sep 17 00:00:00 2001 From: Ricardo Todling Date: Thu, 24 Oct 2024 12:23:41 -0400 Subject: [PATCH 35/90] simpler - better -grid name def - used in GSI --- base/MAPL_DefGridName.F90 | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/base/MAPL_DefGridName.F90 b/base/MAPL_DefGridName.F90 index c6eeb8504f03..101b3f183cc2 100644 --- a/base/MAPL_DefGridName.F90 +++ b/base/MAPL_DefGridName.F90 @@ -5,38 +5,17 @@ subroutine MAPL_DefGridName (im,jm,gridname,iamroot) character(len=*),intent(out)::gridname character(len=2) poletype character(len=3) llcb -character(len=30) myfmt +character(len=30) imstr,jmstr poletype='PC' if(mod(jm,2)==0) poletype='PE' llcb='-DC' ! lat-lon if(6*im==jm) llcb='-CF' ! cubed -! there has to be a smarter way to do this format -if(im>10.and.im<100.and.& - jm>10.and.jm<100) then - myfmt='(a,i2,a,i2,a)' -endif -if(im>100.and.im<1000.and.& - jm>10.and.jm<100) then - myfmt='(a,i3,a,i2,a)' -endif -if(im>100.and.im<1000.and.& - jm>100.and.jm<1000) then - myfmt='(a,i3,a,i3,a)' -endif -if(im>1000.and.im<10000.and.& - jm>100 .and.jm<1000) then - myfmt='(a,i4,a,i3,a)' -endif -if(im>100 .and.im<1000.and.& - jm>1000.and.jm<100) then - myfmt='(a,i3,a,i4,a)' -endif -if(im>1000.and.im<10000.and.& - jm>1000.and.jm<10000) then - myfmt='(a,i4,a,i4,a)' -endif -write(gridname,fmt=trim(myfmt)) trim(poletype),im,'x',jm,trim(llcb) -if(iamroot)print*,'MAPL_DefGridName: ',trim(gridname) +write(imstr,*) im +write(jmstr,*) jm + +gridname=trim(poletype)//trim(adjustl(imstr))//'x'//& + trim(adjustl(jmstr))//trim(llcb) + end subroutine MAPL_DefGridName From 5c37df7aed81aba3558bf121d65d90b4f44575cd Mon Sep 17 00:00:00 2001 From: Ricardo Todling Date: Thu, 24 Oct 2024 12:37:23 -0400 Subject: [PATCH 36/90] about --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c0d1fbb5bc9e..be9a58f54a58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Allow update offsets of ±timestep in ExtData2G +- Minor revision (and generalization) of grid-def for GSI purposes ### Changed From edb9ebaecec0bacc251296332835594f419f6df8 Mon Sep 17 00:00:00 2001 From: Tom Clune Date: Thu, 24 Oct 2024 13:25:04 -0400 Subject: [PATCH 37/90] Update base/MAPL_DefGridName.F90 --- base/MAPL_DefGridName.F90 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/base/MAPL_DefGridName.F90 b/base/MAPL_DefGridName.F90 index 101b3f183cc2..72372da7199c 100644 --- a/base/MAPL_DefGridName.F90 +++ b/base/MAPL_DefGridName.F90 @@ -12,10 +12,9 @@ subroutine MAPL_DefGridName (im,jm,gridname,iamroot) llcb='-DC' ! lat-lon if(6*im==jm) llcb='-CF' ! cubed -write(imstr,*) im -write(jmstr,*) jm +write(imstr,'(I0)') im +write(jmstr,'(I0)') jm -gridname=trim(poletype)//trim(adjustl(imstr))//'x'//& - trim(adjustl(jmstr))//trim(llcb) +gridname=trim(poletype)//trim(imstr) // 'x' // trim(jmstr) // trim(llcb) end subroutine MAPL_DefGridName From a01d0170c805d83803b11354f17fb50437aff9fe Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Mon, 28 Oct 2024 15:16:42 -0400 Subject: [PATCH 38/90] Update to new v4 orb --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 049218678a44..6050daafddf1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ bcs_version: &bcs_version v11.6.0 tag_build_arg_name: &tag_build_arg_name maplversion orbs: - ci: geos-esm/circleci-tools@dev:09c2f0d8f885c7aa78de6570ecb4678e722c88c5 + ci: geos-esm/circleci-tools@4 workflows: build-and-test-MAPL: From 6964c98e22eaf120ffda5a0401f93f0aa2a91882 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Thu, 31 Oct 2024 11:19:31 -0400 Subject: [PATCH 39/90] Update to use GCC 14 in CI GNU runs --- .circleci/config.yml | 2 +- .github/workflows/workflow.yml | 2 +- CHANGELOG.md | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6050daafddf1..1289bf60a242 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ bcs_version: &bcs_version v11.6.0 tag_build_arg_name: &tag_build_arg_name maplversion orbs: - ci: geos-esm/circleci-tools@4 + ci: geos-esm/circleci-tools@dev:0a07ff6bfd3c3f40cfa5aa80d8622be79f9e1615 workflows: build-and-test-MAPL: diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index 4e74fdea31f0..967d7aa53f0b 100644 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -35,7 +35,7 @@ jobs: name: Build and Test MAPL GNU runs-on: ubuntu-latest container: - image: gmao/ubuntu20-geos-env-mkl:v7.27.0-openmpi_5.0.2-gcc_13.2.0 + image: gmao/ubuntu20-geos-env-mkl:v7.27.0-openmpi_5.0.5-gcc_14.2.0 # Per https://github.com/actions/virtual-environments/issues/1445#issuecomment-713861495 # It seems like we might not need secrets on GitHub Actions which is good for forked # pull requests diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fedddeda00b..d609284abd96 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,7 +28,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - ESMA_cmake v3.52.0 - Fixes for using MAPL as a library in spack builds of GEOSgcm - Various backports from v4 -- Update CI to use v7.27.0 Baselibs +- Updates to CI + - Use v7.27.0 Baselibs + - Use GCC 14 for GNU tests ### Fixed From f9faa492211a49e31855aa323a244f995e2cd611 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Thu, 31 Oct 2024 12:27:27 -0400 Subject: [PATCH 40/90] Back to v4 orb --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1289bf60a242..6050daafddf1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -21,7 +21,7 @@ bcs_version: &bcs_version v11.6.0 tag_build_arg_name: &tag_build_arg_name maplversion orbs: - ci: geos-esm/circleci-tools@dev:0a07ff6bfd3c3f40cfa5aa80d8622be79f9e1615 + ci: geos-esm/circleci-tools@4 workflows: build-and-test-MAPL: From 10b09056e6755e90535f29d9a8787dab15c06733 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 6 Nov 2024 06:50:42 -0500 Subject: [PATCH 41/90] Add CMake to skip ExtData2G unit test if pfunit not found --- gridcomps/ExtData2G/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gridcomps/ExtData2G/CMakeLists.txt b/gridcomps/ExtData2G/CMakeLists.txt index 8748c09a717d..a8ebfa49eff3 100644 --- a/gridcomps/ExtData2G/CMakeLists.txt +++ b/gridcomps/ExtData2G/CMakeLists.txt @@ -37,4 +37,6 @@ if (CMAKE_Fortran_COMPILER_ID MATCHES Intel AND CMAKE_BUILD_TYPE MATCHES Release set_source_files_properties(ExtDataGridCompNG.F90 PROPERTIES COMPILE_OPTIONS ${FOPT1}) endif () -add_subdirectory(tests EXCLUDE_FROM_ALL) +if(PFUNIT_FOUND) + add_subdirectory(tests EXCLUDE_FROM_ALL) +endif() From fdceeece416cda4b20698f7a8a51c1d451e1004e Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 6 Nov 2024 08:24:30 -0500 Subject: [PATCH 42/90] Add CI test to build without pfunit --- .circleci/config.yml | 15 +++++++++++++++ CHANGELOG.md | 1 + 2 files changed, 16 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 6050daafddf1..c9c0f7861d39 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -60,6 +60,21 @@ workflows: # ExtData1G tests were removed from ESSENTIAL, so we run them separately here as UFS might still use 1G? ctest_options: "-L 'ESSENTIAL|EXTDATA1G_SMALL_TESTS' --output-on-failure" + # Builds MAPL without pFUnit support + - ci/build: + name: build-MAPL-without-pFUnit-on-<< matrix.compiler >> + context: + - docker-hub-creds + matrix: + parameters: + compiler: [ifort] + baselibs_version: *baselibs_version + repo: MAPL + mepodevelop: false + remove_pfunit: true + run_unit_tests: true + ctest_options: "-L 'ESSENTIAL' --output-on-failure" + # Run MAPL Tutorials - ci/run_mapl_tutorial: name: run-<< matrix.tutorial_name >>-Tutorial-with-<< matrix.compiler >> diff --git a/CHANGELOG.md b/CHANGELOG.md index faa2abd5873e..aee52b7bc2d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updates to CI - Use v7.27.0 Baselibs - Use GCC 14 for GNU tests + - Add pFUnit-less build test ### Fixed From 1d1560db6b5c21ba9d510be0f5bfd8f29d0cadf6 Mon Sep 17 00:00:00 2001 From: Sourish Basu Date: Wed, 6 Nov 2024 12:48:32 -0500 Subject: [PATCH 43/90] Print file and variable names in case of errors, and pring offending collection name in case of duplicate collection --- base/MAPL_CFIO.F90 | 4 ++-- gridcomps/ExtData2G/ExtDataConfig.F90 | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/base/MAPL_CFIO.F90 b/base/MAPL_CFIO.F90 index eeef17d68e43..87b787edaf91 100644 --- a/base/MAPL_CFIO.F90 +++ b/base/MAPL_CFIO.F90 @@ -2840,8 +2840,8 @@ subroutine MAPL_CFIOReadBundle ( FILETMPL, TIME, BUNDLE, NOREAD, RC, & call fill_grads_template ( filename, filetmpl, & experiment_id=EXPID, nymd=nymd, nhms=nhms, rc=status ) _VERIFY(STATUS) - !call WRITE_PARALLEL("CFIO: Reading " // trim(filename)) - if (mapl_am_i_root()) write(*,*)"CFIO: Reading ",trim(filename)," at ",nymd," ",nhms + + if (mapl_am_i_root()) write(*,'(a, ": Reading ", a, " at ", i8, " ", i6)') Iam, trim(filename), nymd, nhms cfioIsCreated = .false. diff --git a/gridcomps/ExtData2G/ExtDataConfig.F90 b/gridcomps/ExtData2G/ExtDataConfig.F90 index 6ee6f96af98f..9a07aa65adab 100644 --- a/gridcomps/ExtData2G/ExtDataConfig.F90 +++ b/gridcomps/ExtData2G/ExtDataConfig.F90 @@ -68,6 +68,7 @@ recursive subroutine new_ExtDataConfig_from_yaml(ext_config,config_file,current_ logical :: file_found logical :: is_right_type character(len=:), allocatable :: sub_configs(:) + character(len=ESMF_MAXSTR) :: error_message _UNUSED_DUMMY(unusable) @@ -107,7 +108,8 @@ recursive subroutine new_ExtDataConfig_from_yaml(ext_config,config_file,current_ do while (ESMF_HConfigIterLoop(hconfigIter,hconfigIterBegin,hconfigIterEnd)) hconfig_key = ESMF_HConfigAsStringMapKey(hconfigIter,_RC) temp_ds => ext_config%file_stream_map%at(hconfig_key) - _ASSERT(.not.associated(temp_ds),"defined duplicate named collection") + write(error_message, '("defined duplicate named collection ", a)') trim(hconfig_key) + _ASSERT(.not.associated(temp_ds), error_message) single_collection = ESMF_HConfigCreateAtMapVal(hconfigIter,_RC) ds = ExtDataFileStream(single_collection,current_time,_RC) call ext_config%file_stream_map%insert(trim(hconfig_key),ds) From 5077b9d555e34a67b0d921bfc3ab9de570e0a8ab Mon Sep 17 00:00:00 2001 From: Tom Clune Date: Wed, 6 Nov 2024 14:34:34 -0500 Subject: [PATCH 44/90] Update gridcomps/ExtData2G/ExtDataConfig.F90 --- gridcomps/ExtData2G/ExtDataConfig.F90 | 1 - 1 file changed, 1 deletion(-) diff --git a/gridcomps/ExtData2G/ExtDataConfig.F90 b/gridcomps/ExtData2G/ExtDataConfig.F90 index 9a07aa65adab..958549dd0abb 100644 --- a/gridcomps/ExtData2G/ExtDataConfig.F90 +++ b/gridcomps/ExtData2G/ExtDataConfig.F90 @@ -68,7 +68,6 @@ recursive subroutine new_ExtDataConfig_from_yaml(ext_config,config_file,current_ logical :: file_found logical :: is_right_type character(len=:), allocatable :: sub_configs(:) - character(len=ESMF_MAXSTR) :: error_message _UNUSED_DUMMY(unusable) From e1a049d239c5abb7a72901cb9d4a50d3c4908317 Mon Sep 17 00:00:00 2001 From: Tom Clune Date: Wed, 6 Nov 2024 14:46:48 -0500 Subject: [PATCH 45/90] Update gridcomps/ExtData2G/ExtDataConfig.F90 --- gridcomps/ExtData2G/ExtDataConfig.F90 | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/gridcomps/ExtData2G/ExtDataConfig.F90 b/gridcomps/ExtData2G/ExtDataConfig.F90 index 958549dd0abb..c7b13c44c5ec 100644 --- a/gridcomps/ExtData2G/ExtDataConfig.F90 +++ b/gridcomps/ExtData2G/ExtDataConfig.F90 @@ -107,8 +107,7 @@ recursive subroutine new_ExtDataConfig_from_yaml(ext_config,config_file,current_ do while (ESMF_HConfigIterLoop(hconfigIter,hconfigIterBegin,hconfigIterEnd)) hconfig_key = ESMF_HConfigAsStringMapKey(hconfigIter,_RC) temp_ds => ext_config%file_stream_map%at(hconfig_key) - write(error_message, '("defined duplicate named collection ", a)') trim(hconfig_key) - _ASSERT(.not.associated(temp_ds), error_message) + _ASSERT(.not.associated(temp_ds),"defined duplicate named collection " // trim(hconfig_key)) single_collection = ESMF_HConfigCreateAtMapVal(hconfigIter,_RC) ds = ExtDataFileStream(single_collection,current_time,_RC) call ext_config%file_stream_map%insert(trim(hconfig_key),ds) From 88f45e683010db51b332d112efbbaea8ccbcf88b Mon Sep 17 00:00:00 2001 From: Yonggang Yu Date: Fri, 8 Nov 2024 11:29:06 -0700 Subject: [PATCH 46/90] ... From db3b06a6dec4f1d446e5162c70a3c817ef057f51 Mon Sep 17 00:00:00 2001 From: Yonggang Yu Date: Fri, 8 Nov 2024 14:39:14 -0700 Subject: [PATCH 47/90] Fix bugs in trajectory sampler when test against ARGOS_geolocation_sample - add missing if group_name /='' - add back the missing first time point in trajectory --- base/Plain_netCDF_Time.F90 | 10 +++++----- gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 | 4 +++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/base/Plain_netCDF_Time.F90 b/base/Plain_netCDF_Time.F90 index 8733f178b3ab..33c63a601a82 100644 --- a/base/Plain_netCDF_Time.F90 +++ b/base/Plain_netCDF_Time.F90 @@ -221,13 +221,13 @@ subroutine get_v1d_netcdf_R8(filename, name, array, Xdim, group_name, rc) integer :: ncid, varid, ncid2 call check_nc_status(nf90_open(trim(fileName), NF90_NOWRITE, ncid), _RC) - if(present(group_name)) then + if(present(group_name) .AND. group_name/='') then ncid2= ncid call check_nc_status(nf90_inq_ncid(ncid2, group_name, ncid), _RC) end if call check_nc_status(nf90_inq_varid(ncid, name, varid), _RC) call check_nc_status(nf90_get_var(ncid, varid, array), _RC) - if(present(group_name)) then + if(present(group_name) .AND. group_name/='') then call check_nc_status(nf90_close(ncid2), _RC) else call check_nc_status(nf90_close(ncid), _RC) @@ -255,7 +255,7 @@ subroutine get_v1d_netcdf_R8_complete(filename, varname, array, att_name, att_va call check_nc_status(nf90_open(trim(fileName), NF90_NOWRITE, ncid), _RC) ncid_sv = ncid - if(present(group_name)) then + if(present(group_name) .AND. group_name/='') then call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) ! mod ncid = ncid_grp @@ -295,7 +295,7 @@ subroutine get_att_real_netcdf(filename, varname, att_name, att_value, group_nam call check_nc_status(nf90_open(trim(fileName), NF90_NOWRITE, ncid), _RC) ncid_sv = ncid - if(present(group_name)) then + if(present(group_name) .AND. group_name/='') then call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) ! overwrite ncid = ncid_grp @@ -323,7 +323,7 @@ subroutine get_att_char_netcdf(filename, varname, att_name, att_value, group_nam call check_nc_status(nf90_open(trim(fileName), NF90_NOWRITE, ncid), _RC) ncid_sv = ncid - if(present(group_name)) then + if(present(group_name) .AND. group_name/='') then call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) ! overwrite ncid = ncid_grp diff --git a/gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 b/gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 index dc4e8f258851..15d3d11317f4 100644 --- a/gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 +++ b/gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 @@ -741,7 +741,9 @@ zero_obs = .false. if (jt1/=jt2) then zero_obs = .false. - if (jt1==0) jt1=1 + !-- YGYU: 8-Nov-2024 : + ! this fix bug, otherwise, the first time point is missing in ARGOS_geolocation + !!if (jt1==0) jt1=1 else ! at most one obs point exist, set it .true. zero_obs = .true. From f5776af95094b1cf0323e0aa3d56002a17307703 Mon Sep 17 00:00:00 2001 From: Yonggang Yu Date: Fri, 8 Nov 2024 15:02:05 -0700 Subject: [PATCH 48/90] more addition --- base/Plain_netCDF_Time.F90 | 49 ++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/base/Plain_netCDF_Time.F90 b/base/Plain_netCDF_Time.F90 index 33c63a601a82..b9c163816647 100644 --- a/base/Plain_netCDF_Time.F90 +++ b/base/Plain_netCDF_Time.F90 @@ -218,20 +218,21 @@ subroutine get_v1d_netcdf_R8(filename, name, array, Xdim, group_name, rc) real(REAL64), dimension(Xdim), intent(out) :: array integer, optional, intent(out) :: rc integer :: status - integer :: ncid, varid, ncid2 + integer :: ncid, varid, ncid2, ncid_sv call check_nc_status(nf90_open(trim(fileName), NF90_NOWRITE, ncid), _RC) - if(present(group_name) .AND. group_name/='') then - ncid2= ncid - call check_nc_status(nf90_inq_ncid(ncid2, group_name, ncid), _RC) + ncid_sv = ncid + + if(present(group_name)) then + if(group_name/='') then + ncid2= ncid + call check_nc_status(nf90_inq_ncid(ncid2, group_name, ncid), _RC) + end if end if call check_nc_status(nf90_inq_varid(ncid, name, varid), _RC) call check_nc_status(nf90_get_var(ncid, varid, array), _RC) - if(present(group_name) .AND. group_name/='') then - call check_nc_status(nf90_close(ncid2), _RC) - else - call check_nc_status(nf90_close(ncid), _RC) - end if + + call check_nc_status(nf90_close(ncid_sv), _RC) _RETURN(_SUCCESS) end subroutine get_v1d_netcdf_R8 @@ -255,10 +256,12 @@ subroutine get_v1d_netcdf_R8_complete(filename, varname, array, att_name, att_va call check_nc_status(nf90_open(trim(fileName), NF90_NOWRITE, ncid), _RC) ncid_sv = ncid - if(present(group_name) .AND. group_name/='') then - call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) - ! mod - ncid = ncid_grp + if(present(group_name)) then + if(group_name/='') then + call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) + ! mod + ncid = ncid_grp + end if end if call check_nc_status(nf90_inq_varid(ncid, varname, varid), _RC) call check_nc_status(nf90_get_var(ncid, varid, array), _RC) @@ -295,10 +298,12 @@ subroutine get_att_real_netcdf(filename, varname, att_name, att_value, group_nam call check_nc_status(nf90_open(trim(fileName), NF90_NOWRITE, ncid), _RC) ncid_sv = ncid - if(present(group_name) .AND. group_name/='') then - call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) - ! overwrite - ncid = ncid_grp + if(present(group_name)) then + if(group_name/='') then + call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) + ! overwrite + ncid = ncid_grp + end if end if call check_nc_status(nf90_inq_varid(ncid, varname, varid), _RC) call check_nc_status(nf90_get_att(ncid, varid, att_name, att_value), _RC) @@ -323,10 +328,12 @@ subroutine get_att_char_netcdf(filename, varname, att_name, att_value, group_nam call check_nc_status(nf90_open(trim(fileName), NF90_NOWRITE, ncid), _RC) ncid_sv = ncid - if(present(group_name) .AND. group_name/='') then - call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) - ! overwrite - ncid = ncid_grp + if(present(group_name)) then + if(group_name/='') then + call check_nc_status(nf90_inq_ncid(ncid, group_name, ncid_grp), _RC) + ! overwrite + ncid = ncid_grp + end if end if call check_nc_status(nf90_inq_varid(ncid, varname, varid), _RC) call check_nc_status(nf90_get_att(ncid, varid, att_name, att_value), _RC) From 1e078a47c0d0440af473f2de3e37c070f7d4144b Mon Sep 17 00:00:00 2001 From: Yonggang Yu Date: Fri, 8 Nov 2024 15:07:14 -0700 Subject: [PATCH 49/90] add items in CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aee52b7bc2d4..0e9ee90c14f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow update offsets of ±timestep in ExtData2G - Minor revision (and generalization) of grid-def for GSI purposes +- Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point ### Changed From bc5f383e57ba5554f92ee69f9fd1c4d69149786e Mon Sep 17 00:00:00 2001 From: Yonggang Yu Date: Sat, 9 Nov 2024 01:05:21 -0700 Subject: [PATCH 50/90] clean up --- gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 b/gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 index 15d3d11317f4..4ae3193970c9 100644 --- a/gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 +++ b/gridcomps/History/Sampler/MAPL_TrajectoryMod_smod.F90 @@ -732,22 +732,11 @@ times_R8_full(1), times_R8_full(nend)) call lgr%debug ('%a %i20 %i20', 'jt1, jt2 [final intercepted position]', jt1, jt2) - -! if (jt1==jt2) then -! _FAIL('Epoch Time is too small, empty grid is generated, increase Epoch') -! endif - - !-- shift the zero item to index 1 - zero_obs = .false. if (jt1/=jt2) then zero_obs = .false. - !-- YGYU: 8-Nov-2024 : - ! this fix bug, otherwise, the first time point is missing in ARGOS_geolocation - !!if (jt1==0) jt1=1 else ! at most one obs point exist, set it .true. zero_obs = .true. - !! if (jt1==0) jt1=1 end if ! From fe648c20cadf1bb47b21bd8c5d99ab5408c3be65 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 12 Nov 2024 10:52:56 -0500 Subject: [PATCH 51/90] Fixes #2845. Allow ESMF_CONFIG_FILE to specify ESMF control file --- CHANGELOG.md | 3 ++- gridcomps/Cap/MAPL_Cap.F90 | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e9ee90c14f0..df6ee93de371 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow update offsets of ±timestep in ExtData2G - Minor revision (and generalization) of grid-def for GSI purposes -- Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point +- Add ability to use an `ESMF_CONFIG_FILE` environment variable to specify name of file to pass in pre-`ESMF_Initialize` options to ESMF (see [ESMF Docs](https://earthsystemmodeling.org/docs/release/latest/ESMF_refdoc/node4.html#SECTION04024000000000000000) for allowed flags. ### Changed @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed issue of some Baselibs builds appearing to support zstandard. This is not possible due to Baselibs building HDF5 and netCDF as static libraries +- Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point ### Removed diff --git a/gridcomps/Cap/MAPL_Cap.F90 b/gridcomps/Cap/MAPL_Cap.F90 index 146e85d2339a..0911e66b3db4 100644 --- a/gridcomps/Cap/MAPL_Cap.F90 +++ b/gridcomps/Cap/MAPL_Cap.F90 @@ -274,9 +274,10 @@ subroutine run_model(this, comm, unusable, rc) integer :: rank, ierror integer :: status class(Logger), pointer :: lgr - logical :: file_exists + logical :: esmfConfigFileExists type (ESMF_VM) :: vm - character(len=:), allocatable :: esmfComm + character(len=:), allocatable :: esmfComm, esmfConfigFile + integer :: esmfConfigFileLen _UNUSED_DUMMY(unusable) @@ -288,16 +289,27 @@ subroutine run_model(this, comm, unusable, rc) call MPI_COMM_RANK(comm, rank, status) _VERIFY(status) + ! We look to see if the user has set an environment variable for the + ! name of the ESMF configuration file. If they have, we use that. If not, + ! we use the default of "ESMF.rc" for backward compatibility + call get_environment_variable('ESMF_CONFIG_FILE', value=esmfConfigFile, length=esmfConfigFileLen, status=status) + if (status /= 0) then + esmfConfigFile = 'ESMF.rc' + esmfConfigFileLen = len_trim(esmfConfigFile) + end if + if (rank == 0) then - inquire(file='ESMF.rc', exist=file_exists) + inquire(file=esmfConfigFile, exist=esmfConfigFileExists) end if - call MPI_BCAST(file_exists, 1, MPI_LOGICAL, 0, comm, status) + call MPI_BCAST(esmfConfigFileExists, 1, MPI_LOGICAL, 0, comm, status) + _VERIFY(status) + call MPI_BCAST(esmfConfigFile, esmfConfigFileLen, MPI_CHARACTER, 0, comm, status) _VERIFY(status) ! If the file exists, we pass it into ESMF_Initialize, else, we ! use the one from the command line arguments - if (file_exists) then - call ESMF_Initialize (configFileName='ESMF.rc', mpiCommunicator=comm, vm=vm, _RC) + if (esmf_config_file_exists) then + call ESMF_Initialize (configFileName=esmfConfig_file, mpiCommunicator=comm, vm=vm, _RC) else call ESMF_Initialize (logKindFlag=this%cap_options%esmf_logging_mode, mpiCommunicator=comm, vm=vm, _RC) end if From 8256274d8c168db9e9d184e3abc3cfca9b4aa42f Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 12 Nov 2024 11:10:42 -0500 Subject: [PATCH 52/90] Fix typo --- gridcomps/Cap/MAPL_Cap.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gridcomps/Cap/MAPL_Cap.F90 b/gridcomps/Cap/MAPL_Cap.F90 index 0911e66b3db4..8e417d5228a7 100644 --- a/gridcomps/Cap/MAPL_Cap.F90 +++ b/gridcomps/Cap/MAPL_Cap.F90 @@ -308,7 +308,7 @@ subroutine run_model(this, comm, unusable, rc) ! If the file exists, we pass it into ESMF_Initialize, else, we ! use the one from the command line arguments - if (esmf_config_file_exists) then + if (esmfConfigFileExists) then call ESMF_Initialize (configFileName=esmfConfig_file, mpiCommunicator=comm, vm=vm, _RC) else call ESMF_Initialize (logKindFlag=this%cap_options%esmf_logging_mode, mpiCommunicator=comm, vm=vm, _RC) From 956645874da1ecfe9936992e3edc44f666fbfd7e Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 12 Nov 2024 11:11:10 -0500 Subject: [PATCH 53/90] Fix typo 2. Jeesh --- gridcomps/Cap/MAPL_Cap.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gridcomps/Cap/MAPL_Cap.F90 b/gridcomps/Cap/MAPL_Cap.F90 index 8e417d5228a7..240db09dad31 100644 --- a/gridcomps/Cap/MAPL_Cap.F90 +++ b/gridcomps/Cap/MAPL_Cap.F90 @@ -309,7 +309,7 @@ subroutine run_model(this, comm, unusable, rc) ! If the file exists, we pass it into ESMF_Initialize, else, we ! use the one from the command line arguments if (esmfConfigFileExists) then - call ESMF_Initialize (configFileName=esmfConfig_file, mpiCommunicator=comm, vm=vm, _RC) + call ESMF_Initialize (configFileName=esmfConfigFile, mpiCommunicator=comm, vm=vm, _RC) else call ESMF_Initialize (logKindFlag=this%cap_options%esmf_logging_mode, mpiCommunicator=comm, vm=vm, _RC) end if From c11a8be9246c3c024315e602cca859db76903489 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 12 Nov 2024 13:44:06 -0500 Subject: [PATCH 54/90] Fix up problems with environment variable getting --- gridcomps/Cap/MAPL_Cap.F90 | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/gridcomps/Cap/MAPL_Cap.F90 b/gridcomps/Cap/MAPL_Cap.F90 index 240db09dad31..052f1654d22e 100644 --- a/gridcomps/Cap/MAPL_Cap.F90 +++ b/gridcomps/Cap/MAPL_Cap.F90 @@ -292,10 +292,20 @@ subroutine run_model(this, comm, unusable, rc) ! We look to see if the user has set an environment variable for the ! name of the ESMF configuration file. If they have, we use that. If not, ! we use the default of "ESMF.rc" for backward compatibility - call get_environment_variable('ESMF_CONFIG_FILE', value=esmfConfigFile, length=esmfConfigFileLen, status=status) - if (status /= 0) then - esmfConfigFile = 'ESMF.rc' - esmfConfigFileLen = len_trim(esmfConfigFile) + + ! Step one: default to ESMF.rc + + esmfConfigFile = 'ESMF.rc' + esmfConfigFileLen = len(esmfConfigFile) + + ! Step two: get the length of the environment variable + call get_environment_variable('ESMF_CONFIG_FILE', length=esmfConfigFileLen, status=status) + ! Step three: if the environment variable exists, get the value of the environment variable + if (status == 0) then + ! We need to deallocate so we can reallocate + deallocate(esmfConfigFile) + allocate(character(len = esmfConfigFileLen) :: esmfConfigFile) + call get_environment_variable('ESMF_CONFIG_FILE', value=esmfConfigFile, status=status) end if if (rank == 0) then From a4abe8dc5a55bff44cbcb02d311f5b496805953a Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 12 Nov 2024 14:19:39 -0500 Subject: [PATCH 55/90] Add a logger message if using a file --- gridcomps/Cap/MAPL_Cap.F90 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/gridcomps/Cap/MAPL_Cap.F90 b/gridcomps/Cap/MAPL_Cap.F90 index 052f1654d22e..536e014e8f5f 100644 --- a/gridcomps/Cap/MAPL_Cap.F90 +++ b/gridcomps/Cap/MAPL_Cap.F90 @@ -316,9 +316,12 @@ subroutine run_model(this, comm, unusable, rc) call MPI_BCAST(esmfConfigFile, esmfConfigFileLen, MPI_CHARACTER, 0, comm, status) _VERIFY(status) + lgr => logging%get_logger('MAPL') + ! If the file exists, we pass it into ESMF_Initialize, else, we ! use the one from the command line arguments if (esmfConfigFileExists) then + call lgr%info("Using ESMF configuration file: %a", esmfConfigFile) call ESMF_Initialize (configFileName=esmfConfigFile, mpiCommunicator=comm, vm=vm, _RC) else call ESMF_Initialize (logKindFlag=this%cap_options%esmf_logging_mode, mpiCommunicator=comm, vm=vm, _RC) @@ -334,7 +337,6 @@ subroutine run_model(this, comm, unusable, rc) call ESMF_MeshSetMOAB(this%cap_options%with_esmf_moab, rc=status) _VERIFY(status) - lgr => logging%get_logger('MAPL') call lgr%info("Running with MOAB library for ESMF Mesh: %l1", this%cap_options%with_esmf_moab) call this%initialize_cap_gc(rc=status) From 8356384ac3185bcbcc81019edfd1ad495c58e846 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 13 Nov 2024 10:31:46 -0500 Subject: [PATCH 56/90] Update changelog --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e9ee90c14f0..9db82f277f94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use v7.27.0 Baselibs - Use GCC 14 for GNU tests - Add pFUnit-less build test +- Improve some writes to be more informative + - In `base/MAPL_CFIO.F90`, added `Iam` to a print statement so that when a read fails we know which routine failed + - In `gridcomps/ExtData2G/ExtDataConfig.F90`, print out the name of the duplicate collection that causes model to fail ### Fixed From cf60fdb73003ec6ca0f842207e595c3b13e9c3a2 Mon Sep 17 00:00:00 2001 From: Weiyuan Jiang Date: Wed, 13 Nov 2024 15:07:54 -0500 Subject: [PATCH 57/90] check tableEnd efficiently --- CHANGELOG.md | 1 + gridcomps/Cap/MAPL_CapGridComp.F90 | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e9ee90c14f0..dd4571e4279e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- refactored tableEnd check - Added commandline options to `checkpoint_benchmark.x` and `restart_benchmark.x` to allow for easier testing of different configurations. Note that the old configuration file style of input is allowed via the `--config_file` option (which overrides any other command line options) - Update ESMF version for Baselibs to match that of Spack for consistency - Update `components.yaml` diff --git a/gridcomps/Cap/MAPL_CapGridComp.F90 b/gridcomps/Cap/MAPL_CapGridComp.F90 index 2ee0e4dca2fd..5b26d34718b5 100644 --- a/gridcomps/Cap/MAPL_CapGridComp.F90 +++ b/gridcomps/Cap/MAPL_CapGridComp.F90 @@ -979,12 +979,11 @@ function get_vec_from_config(config, key, rc) result(vec) cap_import = "" if (present) then - - do while(trim(cap_import) /= "::") + do while( .true.) call ESMF_ConfigNextLine(config, tableEnd=tableEnd, _RC) if (tableEnd) exit call ESMF_ConfigGetAttribute(config, cap_import, _RC) - if (trim(cap_import) /= "::") call vec%push_back(trim(cap_import)) + call vec%push_back(trim(cap_import)) end do end if _RETURN(_SUCCESS) From 25c8a5c6bda8e0790cfce175b082804d8a905f6b Mon Sep 17 00:00:00 2001 From: Weiyuan Jiang Date: Wed, 13 Nov 2024 15:32:51 -0500 Subject: [PATCH 58/90] Added return to fetch_data --- CHANGELOG.md | 1 + pfio/AbstractDataReference.F90 | 2 +- pfio/ServerThread.F90 | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e9ee90c14f0..aefa93e3faa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added macro _RETURN(_SUCCESS) to fetch_data - Allow update offsets of ±timestep in ExtData2G - Minor revision (and generalization) of grid-def for GSI purposes - Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point diff --git a/pfio/AbstractDataReference.F90 b/pfio/AbstractDataReference.F90 index 8c4a06d89597..6ec0b0f235e3 100644 --- a/pfio/AbstractDataReference.F90 +++ b/pfio/AbstractDataReference.F90 @@ -319,7 +319,7 @@ subroutine fetch_data(this,offset_address,global_shape,offset_start, rc) case default _FAIL("dimension not supported yet") end select - + _RETURN(_SUCCESS) end subroutine fetch_data integer function get_length_base(this) result(length) diff --git a/pfio/ServerThread.F90 b/pfio/ServerThread.F90 index bf2d61cd52bf..99874f729d69 100644 --- a/pfio/ServerThread.F90 +++ b/pfio/ServerThread.F90 @@ -1154,7 +1154,7 @@ subroutine get_DataFromMem( this, multi_data_read, rc) offset_address = c_loc(i_ptr(offset+1)) - call mem_data_reference%fetch_data(offset_address,q%global_count,q%start-q%global_start+1) + call mem_data_reference%fetch_data(offset_address,q%global_count,q%start-q%global_start+1, _RC) call this%insert_RequestHandle(q%request_id, & & connection%put(q%request_id, mem_data_reference)) From 84c6832a5117a1f342f41270825762c486110e91 Mon Sep 17 00:00:00 2001 From: Weiyuan Jiang Date: Thu, 14 Nov 2024 09:34:33 -0500 Subject: [PATCH 59/90] rename cap_import to more generic name --- gridcomps/Cap/MAPL_CapGridComp.F90 | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/gridcomps/Cap/MAPL_CapGridComp.F90 b/gridcomps/Cap/MAPL_CapGridComp.F90 index 5b26d34718b5..a8bf1f967b62 100644 --- a/gridcomps/Cap/MAPL_CapGridComp.F90 +++ b/gridcomps/Cap/MAPL_CapGridComp.F90 @@ -967,23 +967,22 @@ end function get_CapGridComp_from_gc function get_vec_from_config(config, key, rc) result(vec) + type(StringVector) :: vec type(ESMF_Config), intent(inout) :: config character(len=*), intent(in) :: key integer, intent(out), optional :: rc logical :: present, tableEnd integer :: status - character(len=ESMF_MAXSTR) :: cap_import - type(StringVector) :: vec + character(len=ESMF_MAXSTR) :: value call ESMF_ConfigFindLabel(config, key//":", isPresent = present, _RC) - cap_import = "" if (present) then - do while( .true.) + do while(.true.) call ESMF_ConfigNextLine(config, tableEnd=tableEnd, _RC) if (tableEnd) exit - call ESMF_ConfigGetAttribute(config, cap_import, _RC) - call vec%push_back(trim(cap_import)) + call ESMF_ConfigGetAttribute(config, value, _RC) + call vec%push_back(trim(value)) end do end if _RETURN(_SUCCESS) From ce10328e6f9986c08ba4451b3480f84f712909cd Mon Sep 17 00:00:00 2001 From: Tom Clune Date: Thu, 14 Nov 2024 10:55:23 -0500 Subject: [PATCH 60/90] Update gridcomps/Cap/MAPL_CapGridComp.F90 --- gridcomps/Cap/MAPL_CapGridComp.F90 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gridcomps/Cap/MAPL_CapGridComp.F90 b/gridcomps/Cap/MAPL_CapGridComp.F90 index a8bf1f967b62..a868c8bd3914 100644 --- a/gridcomps/Cap/MAPL_CapGridComp.F90 +++ b/gridcomps/Cap/MAPL_CapGridComp.F90 @@ -978,7 +978,7 @@ function get_vec_from_config(config, key, rc) result(vec) call ESMF_ConfigFindLabel(config, key//":", isPresent = present, _RC) if (present) then - do while(.true.) + do call ESMF_ConfigNextLine(config, tableEnd=tableEnd, _RC) if (tableEnd) exit call ESMF_ConfigGetAttribute(config, value, _RC) From c76374a4a353d55af8276eed604683821c9c3e3e Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Mon, 18 Nov 2024 08:51:17 -0500 Subject: [PATCH 61/90] Workaround for ifx bug --- CHANGELOG.md | 1 + base/CMakeLists.txt | 9 --------- pfio/ArrayReference.F90 | 40 ++++++++++++++++++++++++++++++++++++---- pfio/CMakeLists.txt | 14 +++++++------- 4 files changed, 44 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd4571e4279e..16d5aafa9b0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed issue of some Baselibs builds appearing to support zstandard. This is not possible due to Baselibs building HDF5 and netCDF as static libraries +- Workaround ifx bug in `pfio/ArrayReference.F90` ### Removed diff --git a/base/CMakeLists.txt b/base/CMakeLists.txt index 43ffa5fb4f06..f5fb9ddfe640 100644 --- a/base/CMakeLists.txt +++ b/base/CMakeLists.txt @@ -1,14 +1,5 @@ esma_set_this (OVERRIDE MAPL.base) -if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") - if(CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 20) - if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER 17) - add_definitions(-D__ifort_18) - endif() - endif() -endif() - - set (srcs MAPL_Profiler.F90 CFIOCollection.F90 MAPL_RegridderManager.F90 diff --git a/pfio/ArrayReference.F90 b/pfio/ArrayReference.F90 index 67a9635ea132..5289c8b74f36 100644 --- a/pfio/ArrayReference.F90 +++ b/pfio/ArrayReference.F90 @@ -87,7 +87,7 @@ function new_ArrayReference_1d(array, rc) result(reference) reference%shape = shape(array) _RETURN(_SUCCESS) - + end function new_ArrayReference_1d function new_ArrayReference_2d(array, rc) result(reference) @@ -151,7 +151,7 @@ function new_ArrayReference_3d(array, rc) result(reference) reference%shape = shape(array) _RETURN(_SUCCESS) - + end function new_ArrayReference_3d @@ -167,16 +167,32 @@ function new_ArrayReference_4d(array, rc) result(reference) select type (array) type is (real(kind=REAL32)) +#if defined(ODD_IFX_BUG) + if (has_address) reference%base_address = c_loc(array(1,1,1,1)) +#else if (has_address) reference%base_address = c_loc(array) +#endif reference%type_kind = pFIO_REAL32 type is (real(kind=REAL64)) +#if defined(ODD_IFX_BUG) + if (has_address) reference%base_address = c_loc(array(1,1,1,1)) +#else if (has_address) reference%base_address = c_loc(array) +#endif reference%type_kind = pFIO_REAL64 type is (integer(kind=INT32)) +#if defined(ODD_IFX_BUG) + if (has_address) reference%base_address = c_loc(array(1,1,1,1)) +#else if (has_address) reference%base_address = c_loc(array) +#endif reference%type_kind = pFIO_INT32 type is (integer(kind=INT64)) +#if defined(ODD_IFX_BUG) + if (has_address) reference%base_address = c_loc(array(1,1,1,1)) +#else if (has_address) reference%base_address = c_loc(array) +#endif reference%type_kind = pFIO_INT64 class default _FAIL( "ArrayRef does not support this type") @@ -184,7 +200,7 @@ function new_ArrayReference_4d(array, rc) result(reference) reference%shape = shape(array) _RETURN(_SUCCESS) - + end function new_ArrayReference_4d function new_ArrayReference_5d(array, rc) result(reference) @@ -199,16 +215,32 @@ function new_ArrayReference_5d(array, rc) result(reference) select type (array) type is (real(kind=REAL32)) +#if defined(ODD_IFX_BUG) + if (has_address) reference%base_address = c_loc(array(1,1,1,1,1)) +#else if (has_address) reference%base_address = c_loc(array) +#endif reference%type_kind = pFIO_REAL32 type is (real(kind=REAL64)) +#if defined(ODD_IFX_BUG) + if (has_address) reference%base_address = c_loc(array(1,1,1,1,1)) +#else if (has_address) reference%base_address = c_loc(array) +#endif reference%type_kind = pFIO_REAL64 type is (integer(kind=INT32)) +#if defined(ODD_IFX_BUG) + if (has_address) reference%base_address = c_loc(array(1,1,1,1,1)) +#else if (has_address) reference%base_address = c_loc(array) +#endif reference%type_kind = pFIO_INT32 type is (integer(kind=INT64)) +#if defined(ODD_IFX_BUG) + if (has_address) reference%base_address = c_loc(array(1,1,1,1,1)) +#else if (has_address) reference%base_address = c_loc(array) +#endif reference%type_kind = pFIO_INT64 class default _FAIL( "ArrayRef does not support this type") @@ -217,7 +249,7 @@ function new_ArrayReference_5d(array, rc) result(reference) reference%shape = shape(array) _RETURN(_SUCCESS) - + end function new_ArrayReference_5d integer function type_kind(element, rc) diff --git a/pfio/CMakeLists.txt b/pfio/CMakeLists.txt index 15f43fdc4388..d842f99674b0 100644 --- a/pfio/CMakeLists.txt +++ b/pfio/CMakeLists.txt @@ -1,12 +1,12 @@ esma_set_this (OVERRIDE MAPL.pfio) -if(CMAKE_Fortran_COMPILER_ID STREQUAL "Intel") - if(CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 20) - if(CMAKE_Fortran_COMPILER_VERSION VERSION_GREATER 17) - add_definitions(-D__ifort_18) - endif() - endif() -endif() +# This is a workaround for a current ifx bug +# Technically, this bug is only due to a bug between +# ifx and ArrayReference.F90, but in CMake land, it +# is hard to remove OpenMP flags for a single file. +if (CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM") + set_source_files_properties(ArrayReference.F90 PROPERTIES COMPILE_DEFINITIONS ODD_IFX_BUG) +endif () set (srcs # pFIO Files From 8027a896c06a3099e8832936fbffccd72f804bb9 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 19 Nov 2024 12:49:10 -0500 Subject: [PATCH 62/90] Turn on ifx builds --- .circleci/config.yml | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c9c0f7861d39..9287cdbfb2fc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -33,7 +33,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] cmake_generator: ['Unix Makefiles','Ninja'] baselibs_version: *baselibs_version repo: MAPL @@ -49,7 +49,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] baselibs_version: *baselibs_version repo: MAPL mepodevelop: false @@ -67,7 +67,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [ifort] + compiler: [ifort, ifx] baselibs_version: *baselibs_version repo: MAPL mepodevelop: false @@ -82,7 +82,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] tutorial_name: - hello_world - parent_no_children @@ -199,23 +199,23 @@ workflows: compiler_version: "2021.13" image_name: geos-env tag_build_arg_name: *tag_build_arg_name - #- ci/publish_docker: - #filters: - #tags: - #only: /^v.*$/ - #name: publish-ifx-docker-image - #context: - #- docker-hub-creds - #- ghcr-creds - #os_version: *os_version - #baselibs_version: *baselibs_version - #container_name: mapl - #mpi_name: intelmpi - #mpi_version: "2021.13" - #compiler_name: ifx - #compiler_version: "2024.2" - #image_name: geos-env - #tag_build_arg_name: *tag_build_arg_name + - ci/publish_docker: + filters: + tags: + only: /^v.*$/ + name: publish-ifx-docker-image + context: + - docker-hub-creds + - ghcr-creds + os_version: *os_version + baselibs_version: *baselibs_version + container_name: mapl + mpi_name: intelmpi + mpi_version: "2021.14" + compiler_name: ifx + compiler_version: "2025.0" + image_name: geos-env + tag_build_arg_name: *tag_build_arg_name - ci/publish_docker: filters: tags: @@ -228,8 +228,8 @@ workflows: baselibs_version: *baselibs_version container_name: mapl mpi_name: openmpi - mpi_version: 5.0.2 + mpi_version: 5.0.5 compiler_name: gcc - compiler_version: 13.2.0 + compiler_version: 14.2.0 image_name: geos-env-mkl tag_build_arg_name: *tag_build_arg_name From 203a96e7df7d2db5995a0f01c9c857679c714ec5 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 19 Nov 2024 13:02:40 -0500 Subject: [PATCH 63/90] Turn on ifx for GEOSgcm --- .circleci/config.yml | 6 +++--- CHANGELOG.md | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9287cdbfb2fc..d2c9434434ec 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -104,7 +104,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] baselibs_version: *baselibs_version repo: GEOSgcm checkout_fixture: true @@ -119,7 +119,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] requires: - build-GEOSgcm-on-<< matrix.compiler >> repo: GEOSgcm @@ -133,7 +133,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] requires: - build-GEOSgcm-on-<< matrix.compiler >> repo: GEOSgcm diff --git a/CHANGELOG.md b/CHANGELOG.md index 16d5aafa9b0d..a92c2819cf58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Use v7.27.0 Baselibs - Use GCC 14 for GNU tests - Add pFUnit-less build test + - Enable ifx tests ### Fixed From 96825006b34690560e0d1bc2aadc8f1e31d28001 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 20 Nov 2024 08:48:53 -0500 Subject: [PATCH 64/90] Welp, turn off ifx for GEOSgcm --- .circleci/config.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d2c9434434ec..9287cdbfb2fc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -104,7 +104,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort, ifx] + compiler: [gfortran, ifort] baselibs_version: *baselibs_version repo: GEOSgcm checkout_fixture: true @@ -119,7 +119,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort, ifx] + compiler: [gfortran, ifort] requires: - build-GEOSgcm-on-<< matrix.compiler >> repo: GEOSgcm @@ -133,7 +133,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort, ifx] + compiler: [gfortran, ifort] requires: - build-GEOSgcm-on-<< matrix.compiler >> repo: GEOSgcm From 87695c350c009923ada53e8fb55e72ab6a464750 Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Thu, 21 Nov 2024 15:03:18 -0500 Subject: [PATCH 65/90] updates --- base/MAPL_LatLonGridFactory.F90 | 99 +++++++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 12 deletions(-) diff --git a/base/MAPL_LatLonGridFactory.F90 b/base/MAPL_LatLonGridFactory.F90 index fbbbfe3a41e7..0e2f59db9a32 100644 --- a/base/MAPL_LatLonGridFactory.F90 +++ b/base/MAPL_LatLonGridFactory.F90 @@ -218,10 +218,16 @@ function create_basic_grid(this, unusable, rc) result(grid) integer, optional, intent(out) :: rc integer :: status + type(ESMF_PoleKind_Flag) :: polekindflag(2) _UNUSED_DUMMY(unusable) if (this%periodic) then + if (this%pole == "XY") then + polekindflag = ESMF_POLEKIND_NONE + else + polekindflag = ESMF_POLEKIND_BIPOLE + end if grid = ESMF_GridCreate1PeriDim( & & name = this%grid_name, & & countsPerDEDim1=this%ims, & @@ -232,6 +238,7 @@ function create_basic_grid(this, unusable, rc) result(grid) & coordDep1=[1,2], & & coordDep2=[1,2], & & coordSys=ESMF_COORDSYS_SPH_RAD, & + & polekindflag=polekindflag, & & rc=status) _VERIFY(status) else @@ -673,7 +680,7 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi integer :: i_min, i_max real(kind=REAL64) :: d_lat, d_lat_temp, extrap_lat - logical :: is_valid, use_file_coords, compute_lons, compute_lats + logical :: is_valid, use_file_coords, compute_lons, compute_lats, has_bnds _UNUSED_DUMMY(unusable) @@ -759,6 +766,10 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi where(this%lon_centers > 180) this%lon_centers=this%lon_centers-360 end if + has_bnds = coordinate_has_bounds(file_metadata, lon_name, _RC) + if (has_bnds) then + this%lon_corners = get_coordinate_bounds(file_metadata, lon_name, _RC) + end if v => file_metadata%get_coordinate_variable(lat_name, rc=status) _VERIFY(status) @@ -773,6 +784,11 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi _FAIL('unsupported type of data; must be REAL32 or REAL64') end select + has_bnds = coordinate_has_bounds(file_metadata, lat_name, _RC) + if (has_bnds) then + this%lat_corners = get_coordinate_bounds(file_metadata, lat_name, _RC) + end if + ! Check: is this a "mis-specified" pole-centered grid? if (size(this%lat_centers) >= 4) then @@ -804,14 +820,14 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi end if end if - ! Corners are the midpoints of centers (and extrapolated at the ! poles for lats.) - allocate(this%lon_corners(im+1), this%lat_corners(jm+1)) - - this%lon_corners(1) = (this%lon_centers(im) + this%lon_centers(1))/2 - 180 - this%lon_corners(2:im) = (this%lon_centers(1:im-1) + this%lon_centers(2:im))/2 - this%lon_corners(im+1) = (this%lon_centers(im) + this%lon_centers(1))/2 + 180 + if (.not. allocated(this%lon_corners)) then + allocate(this%lon_corners(im+1)) + this%lon_corners(1) = (this%lon_centers(im) + this%lon_centers(1))/2 - 180 + this%lon_corners(2:im) = (this%lon_centers(1:im-1) + this%lon_centers(2:im))/2 + this%lon_corners(im+1) = (this%lon_centers(im) + this%lon_centers(1))/2 + 180 + end if ! This section about pole/dateline is probably not needed in file data case. if (abs(this%lon_centers(1) + 180) < 1000*epsilon(1.0)) then @@ -826,10 +842,13 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi this%dateline = 'XY' this%lon_range = RealMinMax(this%lon_centers(1), this%lon_centers(jm)) end if - - this%lat_corners(1) = this%lat_centers(1) - (this%lat_centers(2)-this%lat_centers(1))/2 - this%lat_corners(2:jm) = (this%lat_centers(1:jm-1) + this%lat_centers(2:jm))/2 - this%lat_corners(jm+1) = this%lat_centers(jm) - (this%lat_centers(jm-1)-this%lat_centers(jm))/2 + + if (.not. allocated(this%lat_corners)) then + allocate(this%lat_corners(jm+1)) + this%lat_corners(1) = this%lat_centers(1) - (this%lat_centers(2)-this%lat_centers(1))/2 + this%lat_corners(2:jm) = (this%lat_centers(1:jm-1) + this%lat_centers(2:jm))/2 + this%lat_corners(jm+1) = this%lat_centers(jm) - (this%lat_centers(jm-1)-this%lat_centers(jm))/2 + end if if (abs(this%lat_centers(1) + 90) < 1000*epsilon(1.0)) then this%pole = 'PC' @@ -1139,7 +1158,6 @@ subroutine check_and_fill_consistency(this, unusable, rc) ! Check regional vs global if (this%pole == 'XY') then ! regional - this%periodic = .false. _ASSERT(this%lat_range%min /= MAPL_UNDEFINED_REAL, 'uninitialized min for lat_range') _ASSERT(this%lat_range%max /= MAPL_UNDEFINED_REAL, 'uninitialized min for lat_range') else ! global @@ -1928,5 +1946,62 @@ function generate_file_reference3D(this,fpointer,metaData) result(ref) _UNUSED_DUMMY(metaData) end function generate_file_reference3D + function coordinate_has_bounds(metadata, coord_name, rc) result(has_bounds) + logical :: has_bounds + type(FileMetadata), intent(in) :: metadata + character(len=*), intent(in) :: coord_name + integer, optional, intent(out) :: rc + + type(Variable), pointer :: var + integer :: status + + var => metadata%get_variable(coord_name, _RC) + has_bounds = var%is_attribute_present("bounds") + + _RETURN(_SUCCESS) + end function + + function get_coordinate_bounds(metadata, coord_name, rc) result(coord_bounds) + real(kind=REAL64), allocatable :: coord_bounds(:) + type(FileMetadata), intent(in) :: metadata + character(len=*), intent(in) :: coord_name + integer, optional, intent(out) :: rc + + type(Variable), pointer :: var + type(Attribute), pointer :: attr + integer :: status, im, i + class(*), pointer :: attr_val + character(len=:), allocatable :: bnds_name, source_file + real(kind=REAL64), allocatable :: file_bounds(:,:) + type(NetCDF4_FileFormatter) :: file_formatter + + + var => metadata%get_variable(coord_name, _RC) + attr => var%get_attribute("bounds", _RC) + attr_val => attr%get_value() + select type(attr_val) + type is(character(*)) + bnds_name = attr_val + class default + _FAIL('coordinate bounds must be a string') + end select + im = metadata%get_dimension(coord_name, _RC) + allocate(coord_bounds(im+1), _STAT) + allocate(file_bounds(im,2), _STAT) + source_file = metadata%get_source_file() + + call file_formatter%open(source_file, PFIO_READ, _RC) + call file_formatter%get_var(coord_name, file_bounds, _RC) + call file_formatter%close(_RC) + do i=1,im-1 + _ASSERT(file_bounds(i,2)==file_bounds(i+1,1), "Bounds are not contiguous in file") + enddo + do i=1,im + coord_bounds(i) = file_bounds(i,1) + coord_bounds(i+1) = file_bounds(i,2) + enddo + + _RETURN(_SUCCESS) + end function end module MAPL_LatLonGridFactoryMod From 9153589986bc53a1fafb4f91b6e4406cb777783f Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Fri, 22 Nov 2024 10:36:23 -0500 Subject: [PATCH 66/90] more updates --- base/MAPL_LatLonGridFactory.F90 | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/base/MAPL_LatLonGridFactory.F90 b/base/MAPL_LatLonGridFactory.F90 index 0e2f59db9a32..260ebc6afd87 100644 --- a/base/MAPL_LatLonGridFactory.F90 +++ b/base/MAPL_LatLonGridFactory.F90 @@ -851,10 +851,13 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi end if if (abs(this%lat_centers(1) + 90) < 1000*epsilon(1.0)) then + write(*,*)'bmaa here 1' this%pole = 'PC' else if (abs(this%lat_corners(1) + 90) < 1000*epsilon(1.0)) then + write(*,*)'bmaa here 2' this%pole = 'PE' else ! assume XY + write(*,*)'bmaa here 3' this%pole = 'XY' this%lat_range = RealMinMax(this%lat_centers(1), this%lat_centers(jm)) end if @@ -1987,18 +1990,18 @@ function get_coordinate_bounds(metadata, coord_name, rc) result(coord_bounds) end select im = metadata%get_dimension(coord_name, _RC) allocate(coord_bounds(im+1), _STAT) - allocate(file_bounds(im,2), _STAT) + allocate(file_bounds(2,im), _STAT) source_file = metadata%get_source_file() - + call file_formatter%open(source_file, PFIO_READ, _RC) - call file_formatter%get_var(coord_name, file_bounds, _RC) + call file_formatter%get_var(bnds_name, file_bounds, _RC) call file_formatter%close(_RC) do i=1,im-1 - _ASSERT(file_bounds(i,2)==file_bounds(i+1,1), "Bounds are not contiguous in file") + _ASSERT(file_bounds(2,i)==file_bounds(1,i+1), "Bounds are not contiguous in file") enddo do i=1,im - coord_bounds(i) = file_bounds(i,1) - coord_bounds(i+1) = file_bounds(i,2) + coord_bounds(i) = file_bounds(1,i) + coord_bounds(i+1) = file_bounds(2,i) enddo _RETURN(_SUCCESS) From ded9a8f8811c7995a4bad6d9e7c59e4fd8c2c105 Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Fri, 22 Nov 2024 16:34:53 -0500 Subject: [PATCH 67/90] more updates --- base/MAPL_LatLonGridFactory.F90 | 42 +++++++++++++++++++++++++++++---- griddedio/FieldBundleRead.F90 | 2 +- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/base/MAPL_LatLonGridFactory.F90 b/base/MAPL_LatLonGridFactory.F90 index 260ebc6afd87..7d7adf040f67 100644 --- a/base/MAPL_LatLonGridFactory.F90 +++ b/base/MAPL_LatLonGridFactory.F90 @@ -56,6 +56,8 @@ module MAPL_LatLonGridFactoryMod integer :: px, py logical :: is_halo_initialized = .false. logical :: periodic = .true. + character(len=:), allocatable :: lon_bounds_name + character(len=:), allocatable :: lat_bounds_name contains procedure :: make_new_grid procedure :: create_basic_grid @@ -226,7 +228,7 @@ function create_basic_grid(this, unusable, rc) result(grid) if (this%pole == "XY") then polekindflag = ESMF_POLEKIND_NONE else - polekindflag = ESMF_POLEKIND_BIPOLE + polekindflag = ESMF_POLEKIND_MONOPOLE end if grid = ESMF_GridCreate1PeriDim( & & name = this%grid_name, & @@ -768,6 +770,7 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi has_bnds = coordinate_has_bounds(file_metadata, lon_name, _RC) if (has_bnds) then + this%lon_bounds_name = get_coordinate_bounds_name(file_metadata, lon_name, _RC) this%lon_corners = get_coordinate_bounds(file_metadata, lon_name, _RC) end if @@ -786,6 +789,7 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi has_bnds = coordinate_has_bounds(file_metadata, lat_name, _RC) if (has_bnds) then + this%lat_bounds_name = get_coordinate_bounds_name(file_metadata, lat_name, _RC) this%lat_corners = get_coordinate_bounds(file_metadata, lat_name, _RC) end if @@ -851,13 +855,10 @@ subroutine initialize_from_file_metadata(this, file_metadata, unusable, force_fi end if if (abs(this%lat_centers(1) + 90) < 1000*epsilon(1.0)) then - write(*,*)'bmaa here 1' this%pole = 'PC' else if (abs(this%lat_corners(1) + 90) < 1000*epsilon(1.0)) then - write(*,*)'bmaa here 2' this%pole = 'PE' else ! assume XY - write(*,*)'bmaa here 3' this%pole = 'XY' this%lat_range = RealMinMax(this%lat_centers(1), this%lat_centers(jm)) end if @@ -1872,7 +1873,15 @@ function get_file_format_vars(this) result(vars) character(len=:), allocatable :: vars _UNUSED_DUMMY(this) - vars = 'lon,lat' + if (allocated(this%lon_bounds_name) .and. allocated(this%lat_bounds_name)) then + vars = 'lon,lat,'//this%lon_bounds_name//','//this%lat_bounds_name + else if (allocated(this%lon_bounds_name) .and. (.not. allocated(this%lat_bounds_name))) then + vars = 'lon,lat,'//this%lon_bounds_name + else if (allocated(this%lat_bounds_name) .and. (.not. allocated(this%lon_bounds_name))) then + vars = 'lon,lat,'//this%lat_bounds_name + else if ((.not.allocated(this%lat_bounds_name)) .and. (.not. allocated(this%lon_bounds_name))) then + vars = 'lon,lat' + end if end function get_file_format_vars @@ -1964,6 +1973,29 @@ function coordinate_has_bounds(metadata, coord_name, rc) result(has_bounds) _RETURN(_SUCCESS) end function + function get_coordinate_bounds_name(metadata, coord_name, rc) result(coord_bounds_name) + character(len=:), allocatable :: coord_bounds_name + type(FileMetadata), intent(in) :: metadata + character(len=*), intent(in) :: coord_name + integer, optional, intent(out) :: rc + + type(Variable), pointer :: var + type(Attribute), pointer :: attr + integer :: status + class(*), pointer :: attr_val + + var => metadata%get_variable(coord_name, _RC) + attr => var%get_attribute("bounds", _RC) + attr_val => attr%get_value() + select type(attr_val) + type is(character(*)) + coord_bounds_name = attr_val + class default + _FAIL('coordinate bounds must be a string') + end select + _RETURN(_SUCCESS) + end function + function get_coordinate_bounds(metadata, coord_name, rc) result(coord_bounds) real(kind=REAL64), allocatable :: coord_bounds(:) type(FileMetadata), intent(in) :: metadata diff --git a/griddedio/FieldBundleRead.F90 b/griddedio/FieldBundleRead.F90 index 6f0bd2b09c65..b8da3d92e091 100644 --- a/griddedio/FieldBundleRead.F90 +++ b/griddedio/FieldBundleRead.F90 @@ -71,7 +71,7 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ factory => get_factory(file_grid,rc=status) _VERIFY(status) grid_vars = factory%get_file_format_vars() - exclude_vars = grid_vars//",lev,time,lons,lats" + exclude_vars = grid_vars//",lev,time,lons,lats,time_bnds" if (has_vertical_level) lev_size = metadata%get_dimension(trim(lev_name)) variables => metadata%get_variables() From 935a4b8ed847f42a4d6e7664bd42b9ee9ee0ec9e Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Fri, 22 Nov 2024 16:37:25 -0500 Subject: [PATCH 68/90] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd4571e4279e..a3599eb08549 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow update offsets of ±timestep in ExtData2G - Minor revision (and generalization) of grid-def for GSI purposes - Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point +- Allow lat-lon grid factory to detect and use CF compliant lat-lon bounds in a file when making a grid ### Changed @@ -39,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed issue of some Baselibs builds appearing to support zstandard. This is not possible due to Baselibs building HDF5 and netCDF as static libraries +- Fixed a bug where the periodicity around the earth of the lat-lon grid was not being set properly when grid did not span from pole to pole ### Removed From 8ce9c6a8757594f1d480f62ac7b91c8c1509d5cd Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Mon, 25 Nov 2024 13:35:45 -0500 Subject: [PATCH 69/90] comments --- griddedio/FieldBundleRead.F90 | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/griddedio/FieldBundleRead.F90 b/griddedio/FieldBundleRead.F90 index b8da3d92e091..a16ab0597285 100644 --- a/griddedio/FieldBundleRead.F90 +++ b/griddedio/FieldBundleRead.F90 @@ -91,6 +91,15 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ enddo end if + write(*,*)'bmaa exclude ',','//trim(exclude_vars)//',' + write(*,*)'bmaa var_name ',trim(var_name) + write(*,*)'bmaa index(exclude_var,var_name) ',index(','//trim(exclude_vars)//',',','//trim(var_name)//',') + write(*,*)'bmaa hardcode string index comma vs no ',index(',lon,lat,lon_bnds,lat_bnds,lev,time,lons,lats,time_bnds,',",lat_bnds,"),index(',lon,lat,lon_bnds,lat_bnds,lev,time,lons,lats,time_bnds,',"lat_bnds") + write(*,*)'bmaa allocatable string index comma vs no ',index(exclude_vars,",lat_bnds,"),index(exclude_vars,"lat_bnds") + write(*,*)'bmaa allocatable string index comma vs no ',index(exclude_vars,",time_bnds,"),index(exclude_vars,"time_bnds") + write(*,*)'bmaa allocatable string index comma vs no ',index(exclude_vars,",time,"),index(exclude_vars,"time") + write(*,*)'bmaa allocatable string index comma vs no ',index(exclude_vars,",lat,"),index(exclude_vars,"lat") + if (index(','//trim(exclude_vars)//',',','//trim(var_name)//',') > 0) then call var_iter%next() cycle @@ -137,6 +146,7 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ _VERIFY(status) call ESMF_AttributeSet(field,name='VLOCATION',value=location,rc=status) _VERIFY(status) + write(*,*)"bmaa getting units for ",trim(var_name) units = metadata%get_var_attr_string(var_name,'units',_RC) long_name = metadata%get_var_attr_string(var_name,'long_name',_RC) call ESMF_AttributeSet(field,name='UNITS',value=units,rc=status) From bf45beaf7f7f200ff7ef8916dd5dbcc74bcee753 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 26 Nov 2024 13:02:42 -0500 Subject: [PATCH 70/90] Maybe ifx needs more memory? --- .circleci/config.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9287cdbfb2fc..aad1064de96e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -104,7 +104,8 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] + resource_class: xlarge baselibs_version: *baselibs_version repo: GEOSgcm checkout_fixture: true @@ -119,7 +120,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] requires: - build-GEOSgcm-on-<< matrix.compiler >> repo: GEOSgcm @@ -133,7 +134,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort] + compiler: [gfortran, ifort, ifx] requires: - build-GEOSgcm-on-<< matrix.compiler >> repo: GEOSgcm From 232f08b8f1a95507919aaa1abd3be8f8356c54fb Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 26 Nov 2024 13:30:53 -0500 Subject: [PATCH 71/90] Circleci orb should default to xlarge --- .circleci/config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index aad1064de96e..d2c9434434ec 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -105,7 +105,6 @@ workflows: matrix: parameters: compiler: [gfortran, ifort, ifx] - resource_class: xlarge baselibs_version: *baselibs_version repo: GEOSgcm checkout_fixture: true From 2065c98cacc54a7470726942b146e60fbe73b53d Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 26 Nov 2024 14:20:11 -0500 Subject: [PATCH 72/90] Try release build for GEOSadas --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d2c9434434ec..81c1fae3e2fa 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -176,7 +176,8 @@ workflows: fixture_branch: feature/mathomp4/mapldevelop checkout_mapl_branch: true mepodevelop: false - rebuild_procs: 1 + rebuild_procs: 4 + build_type: Release build-and-publish-docker: when: From 2c7a8dfe2fcc740fb55e8c9a9b8e4ee78044aeec Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 27 Nov 2024 10:36:11 -0500 Subject: [PATCH 73/90] Fixes for GEOSadas CI --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c9c0f7861d39..01fe02b99c2b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -176,7 +176,8 @@ workflows: fixture_branch: feature/mathomp4/mapldevelop checkout_mapl_branch: true mepodevelop: false - rebuild_procs: 1 + rebuild_procs: 4 + build_type: Release build-and-publish-docker: when: From f05d7af7c8a82e81ad7c2c9f2ba0c9fd5a20fe21 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 27 Nov 2024 10:37:13 -0500 Subject: [PATCH 74/90] CI changes for GEOSadas --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c9c0f7861d39..01fe02b99c2b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -176,7 +176,8 @@ workflows: fixture_branch: feature/mathomp4/mapldevelop checkout_mapl_branch: true mepodevelop: false - rebuild_procs: 1 + rebuild_procs: 4 + build_type: Release build-and-publish-docker: when: From dedb09b81d6996dc988558fc51f5b9e7da90453f Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Wed, 27 Nov 2024 10:53:15 -0500 Subject: [PATCH 75/90] Remove ifx from coupled run --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 81c1fae3e2fa..58c5b8ee9a8d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -133,7 +133,7 @@ workflows: - docker-hub-creds matrix: parameters: - compiler: [gfortran, ifort, ifx] + compiler: [gfortran, ifort] requires: - build-GEOSgcm-on-<< matrix.compiler >> repo: GEOSgcm From 89e5348f2ed679cec79c6ad8b9cc4c48667eb6ac Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Mon, 2 Dec 2024 13:46:50 -0500 Subject: [PATCH 76/90] Update for f2py-meson support --- CHANGELOG.md | 9 +++++++-- CMakeLists.txt | 2 +- components.yaml | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd4571e4279e..4c1be6318f47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed -- refactored tableEnd check +- Change minimum CMake version to 3.24 + - This is needed for f2py and meson support +- Refactored tableEnd check - Added commandline options to `checkpoint_benchmark.x` and `restart_benchmark.x` to allow for easier testing of different configurations. Note that the old configuration file style of input is allowed via the `--config_file` option (which overrides any other command line options) - Update ESMF version for Baselibs to match that of Spack for consistency - Update `components.yaml` @@ -28,9 +30,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - GSL 2.8 - jpeg 9f - Various build fixes - - ESMA_cmake v3.52.0 + - ESMA_cmake v3.55.0 - Fixes for using MAPL as a library in spack builds of GEOSgcm - Various backports from v4 + - Code for capturing `mepo status` output + - Fixes for f2py and meson (NOTE: Requires CMake minimum version of 3.24 in project for complete functionality) + - Fixes for `MPI_STACK` code run multiple times - Updates to CI - Use v7.27.0 Baselibs - Use GCC 14 for GNU tests diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dc1502bdf3c..2970acee7162 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.23) +cmake_minimum_required (VERSION 3.24) get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(NOT is_multi_config AND NOT (CMAKE_BUILD_TYPE OR DEFINED ENV{CMAKE_BUILD_TYPE})) diff --git a/components.yaml b/components.yaml index 271f302b4f9d..3d65a1df9aa4 100644 --- a/components.yaml +++ b/components.yaml @@ -5,13 +5,13 @@ MAPL: ESMA_env: local: ./ESMA_env remote: ../ESMA_env.git - tag: v4.31.0 + tag: v4.32.0 develop: main ESMA_cmake: local: ./ESMA_cmake remote: ../ESMA_cmake.git - tag: v3.52.0 + tag: v3.55.0 develop: develop ecbuild: From 3ae25f304b28a17621e137bc70bb27103b5d1636 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Mon, 2 Dec 2024 14:18:58 -0500 Subject: [PATCH 77/90] Clarify changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a92c2819cf58..80817b23c383 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed issue of some Baselibs builds appearing to support zstandard. This is not possible due to Baselibs building HDF5 and netCDF as static libraries -- Workaround ifx bug in `pfio/ArrayReference.F90` +- Workaround ifx bug in `pfio/ArrayReference.F90` (NOTE: This currently targets all versions of ifx, but will need to be qualified or removed in the future) ### Removed From d9b2fbfea166f7e2260fcf8532726ccf39b0727b Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Tue, 3 Dec 2024 09:56:23 -0500 Subject: [PATCH 78/90] fixes #3210 --- base/FileMetadataUtilities.F90 | 79 +++----------------- pfio/Variable.F90 | 133 +++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+), 68 deletions(-) diff --git a/base/FileMetadataUtilities.F90 b/base/FileMetadataUtilities.F90 index ea8c858f7ccb..b42ff3ba8de1 100644 --- a/base/FileMetadataUtilities.F90 +++ b/base/FileMetadataUtilities.F90 @@ -116,30 +116,15 @@ function get_var_attr_real32(this,var_name,attr_name,rc) result(attr_real32) character(len=*), intent(in) :: attr_name integer, optional, intent(out) :: rc - real(REAL32) :: tmp(1) - real(REAL64) :: tmpd(1) integer :: status character(:), allocatable :: fname - type(Attribute), pointer :: attr type(Variable), pointer :: var - class(*), pointer :: attr_val(:) fname = this%get_file_name(_RC) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) - attr => var%get_attribute(attr_name,_RC) - _ASSERT(associated(attr),"no attribute named "//attr_name//" in "//var_name//" in "//fname) - attr_val => attr%get_values() - select type(attr_val) - type is(real(kind=REAL32)) - tmp = attr_val - attr_real32 = tmp(1) - type is(real(kind=REAL64)) - tmpd = attr_val - attr_real32 = REAL(tmpd(1)) - class default - _FAIL('unsupported subclass (not real32) for units of attribute named '//attr_name//' in '//var_name//' in '//fname) - end select + attr_real32 = var%get_attribute_real32(attr_name, rc=status) + _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_real32 @@ -151,28 +136,17 @@ function get_var_attr_real64(this,var_name,attr_name,rc) result(attr_real64) character(len=*), intent(in) :: attr_name integer, optional, intent(out) :: rc - real(REAL64) :: tmp(1) integer :: status character(:), allocatable :: fname - type(Attribute), pointer :: attr type(Variable), pointer :: var - class(*), pointer :: attr_val(:) fname = this%get_file_name(_RC) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) - attr => var%get_attribute(attr_name,_RC) - _ASSERT(associated(attr),"no attribute named "//attr_name//" in "//var_name//" in "//fname) - attr_val => attr%get_values() - select type(attr_val) - type is(real(kind=REAL64)) - tmp = attr_val - attr_real64 = tmp(1) - class default - _FAIL('unsupported subclass (not real64) for units of attribute named '//attr_name//' in '//var_name//' in '//fname) - end select - + attr_real64 = var%get_attribute_real64(attr_name, rc=status) + _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) + end function get_var_attr_real64 function get_var_attr_int32(this,var_name,attr_name,rc) result(attr_int32) @@ -182,26 +156,15 @@ function get_var_attr_int32(this,var_name,attr_name,rc) result(attr_int32) character(len=*), intent(in) :: attr_name integer, optional, intent(out) :: rc - integer(INT32) :: tmp(1) integer :: status character(:), allocatable :: fname - type(Attribute), pointer :: attr type(Variable), pointer :: var - class(*), pointer :: attr_val(:) fname = this%get_file_name(_RC) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) - attr => var%get_attribute(attr_name,_RC) - _ASSERT(associated(attr),"no attribute named "//attr_name//" in "//var_name//" in "//fname) - attr_val => attr%get_values() - select type(attr_val) - type is(integer(kind=INT32)) - tmp = attr_val - attr_int32 = tmp(1) - class default - _FAIL('unsupported subclass (not int32) for units of attribute named '//attr_name//' in '//var_name//' in '//fname) - end select + attr_int32 = var%get_attribute_int32(attr_name, rc=status) + _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_int32 @@ -213,26 +176,15 @@ function get_var_attr_int64(this,var_name,attr_name,rc) result(attr_int64) character(len=*), intent(in) :: attr_name integer, optional, intent(out) :: rc - integer(INT64) :: tmp(1) integer :: status character(:), allocatable :: fname - type(Attribute), pointer :: attr type(Variable), pointer :: var - class(*), pointer :: attr_val(:) fname = this%get_file_name(_RC) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) - attr => var%get_attribute(attr_name,_RC) - _ASSERT(associated(attr),"no attribute named "//attr_name//" in "//var_name//" in "//fname) - attr_val => attr%get_values() - select type(attr_val) - type is(integer(kind=INT64)) - tmp = attr_val - attr_int64 = tmp(1) - class default - _FAIL('unsupported subclass (not int64) for units of attribute named '//attr_name//' in '//var_name//' in '//fname) - end select + attr_int64 = var%get_attribute_int64(attr_name, rc=status) + _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_int64 @@ -246,22 +198,13 @@ function get_var_attr_string(this,var_name,attr_name,rc) result(attr_string) integer :: status character(:), allocatable :: fname - type(Attribute), pointer :: attr type(Variable), pointer :: var - class(*), pointer :: attr_val fname = this%get_file_name(_RC) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) - attr => var%get_attribute(attr_name,_RC) - _ASSERT(associated(attr),"no attribute named "//attr_name//" in "//var_name//" in "//fname) - attr_val => attr%get_value() - select type(attr_val) - type is(character(*)) - attr_string = attr_val - class default - _FAIL('unsupported subclass (not string) for units of attribute named '//attr_name//' in '//var_name//' in '//fname) - end select + attr_string = var%get_attribute_string(attr_name, rc=status) + _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_string diff --git a/pfio/Variable.F90 b/pfio/Variable.F90 index 84958a172945..001e22a92968 100644 --- a/pfio/Variable.F90 +++ b/pfio/Variable.F90 @@ -12,6 +12,7 @@ module pFIO_VariableMod use pFIO_AttributeMod use pFIO_StringAttributeMapMod use pFIO_StringAttributeMapUtilMod + use, intrinsic :: iso_fortran_env, only: REAL32, REAL64, INT32, INT64 implicit none private @@ -40,6 +41,11 @@ module pFIO_VariableMod procedure :: get_const_value procedure :: get_attribute + procedure :: get_attribute_string + procedure :: get_attribute_int32 + procedure :: get_attribute_int64 + procedure :: get_attribute_real32 + procedure :: get_attribute_real64 generic :: add_attribute => add_attribute_0d generic :: add_attribute => add_attribute_1d procedure :: add_attribute_0d @@ -258,6 +264,133 @@ function get_attribute(this, attr_name, rc) result(attr) _RETURN(_SUCCESS) end function get_attribute + function get_attribute_string(this, attr_name, rc) result(attr_string) + character(len=:), allocatable :: attr_string + class (Variable), target, intent(in) :: this + character(len=*), intent(in) :: attr_name + integer, optional, intent(out) :: rc + + integer :: status + type(Attribute), pointer :: attr + class(*), pointer :: attr_val + + attr => this%get_attribute(attr_name,_RC) + _ASSERT(associated(attr),"no such attribute "//attr_name) + attr_val => attr%get_value() + select type(attr_val) + type is(character(*)) + attr_string = attr_val + class default + _FAIL('unsupported subclass (not string) of attribute named '//attr_name) + end select + + _RETURN(_SUCCESS) + end function get_attribute_string + + function get_attribute_real32(this,attr_name,rc) result(attr_real32) + real(REAL32) :: attr_real32 + class(Variable), intent(inout) :: this + character(len=*), intent(in) :: attr_name + integer, optional, intent(out) :: rc + + real(REAL32) :: tmp(1) + real(REAL64) :: tmpd(1) + integer :: status + type(Attribute), pointer :: attr + class(*), pointer :: attr_val(:) + + attr => this%get_attribute(attr_name,_RC) + _ASSERT(associated(attr),"no attribute named "//attr_name) + attr_val => attr%get_values() + select type(attr_val) + type is(real(kind=REAL32)) + tmp = attr_val + attr_real32 = tmp(1) + type is(real(kind=REAL64)) + tmpd = attr_val + attr_real32 = REAL(tmpd(1)) + class default + _FAIL('unsupported subclass (not real32) for units of attribute named '//attr_name) + end select + + _RETURN(_SUCCESS) + end function get_attribute_real32 + + function get_attribute_real64(this,attr_name,rc) result(attr_real64) + real(REAL64) :: attr_real64 + class(Variable), intent(inout) :: this + character(len=*), intent(in) :: attr_name + integer, optional, intent(out) :: rc + + real(REAL64) :: tmp(1) + integer :: status + type(Attribute), pointer :: attr + class(*), pointer :: attr_val(:) + + attr => this%get_attribute(attr_name,_RC) + _ASSERT(associated(attr),"no such attribute "//attr_name) + attr_val => attr%get_values() + select type(attr_val) + type is(real(kind=REAL64)) + tmp = attr_val + attr_real64 = tmp(1) + class default + _FAIL('unsupported subclass (not real64) for units of attribute named '//attr_name) + end select + + _RETURN(_SUCCESS) + end function get_attribute_real64 + + function get_attribute_int32(this,attr_name,rc) result(attr_int32) + integer(INT32) :: attr_int32 + class(Variable), intent(inout) :: this + character(len=*), intent(in) :: attr_name + integer, optional, intent(out) :: rc + + integer(INT32) :: tmp(1) + integer :: status + type(Attribute), pointer :: attr + class(*), pointer :: attr_val(:) + + attr => this%get_attribute(attr_name,_RC) + _ASSERT(associated(attr),"no attribute named "//attr_name) + attr_val => attr%get_values() + select type(attr_val) + type is(integer(kind=INT32)) + tmp = attr_val + attr_int32 = tmp(1) + class default + _FAIL('unsupported subclass (not int32) for units of attribute named '//attr_name) + end select + + _RETURN(_SUCCESS) + end function get_attribute_int32 + + function get_attribute_int64(this,attr_name,rc) result(attr_int64) + integer(INT64) :: attr_int64 + class(Variable), intent(inout) :: this + character(len=*), intent(in) :: attr_name + integer, optional, intent(out) :: rc + + integer(INT64) :: tmp(1) + integer :: status + type(Attribute), pointer :: attr + class(*), pointer :: attr_val(:) + + attr => this%get_attribute(attr_name,_RC) + _ASSERT(associated(attr),"no attribute named "//attr_name) + attr_val => attr%get_values() + select type(attr_val) + type is(integer(kind=INT64)) + tmp = attr_val + attr_int64 = tmp(1) + class default + _FAIL('unsupported subclass (not int64) for units of attribute named '//attr_name) + end select + + _RETURN(_SUCCESS) + end function get_attribute_int64 + subroutine add_const_value(this, const_value, rc) class (Variable), target, intent(inout) :: this type (UnlimitedEntity), intent(in) :: const_value From daab7d73ad0b5ad26eb1887eaa7672355ae1d4f5 Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Tue, 3 Dec 2024 11:18:51 -0500 Subject: [PATCH 79/90] oops --- base/FileMetadataUtilities.F90 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/base/FileMetadataUtilities.F90 b/base/FileMetadataUtilities.F90 index b42ff3ba8de1..133037ce3c4a 100644 --- a/base/FileMetadataUtilities.F90 +++ b/base/FileMetadataUtilities.F90 @@ -124,7 +124,7 @@ function get_var_attr_real32(this,var_name,attr_name,rc) result(attr_real32) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) attr_real32 = var%get_attribute_real32(attr_name, rc=status) - _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) + _ASSERT(status == _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_real32 @@ -144,7 +144,7 @@ function get_var_attr_real64(this,var_name,attr_name,rc) result(attr_real64) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) attr_real64 = var%get_attribute_real64(attr_name, rc=status) - _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) + _ASSERT(status == _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_real64 @@ -164,7 +164,7 @@ function get_var_attr_int32(this,var_name,attr_name,rc) result(attr_int32) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) attr_int32 = var%get_attribute_int32(attr_name, rc=status) - _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) + _ASSERT(status == _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_int32 @@ -184,7 +184,7 @@ function get_var_attr_int64(this,var_name,attr_name,rc) result(attr_int64) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) attr_int64 = var%get_attribute_int64(attr_name, rc=status) - _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) + _ASSERT(status == _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_int64 @@ -204,7 +204,7 @@ function get_var_attr_string(this,var_name,attr_name,rc) result(attr_string) var => this%get_variable(var_name,_RC) _ASSERT(associated(var),"no variable named "//var_name//" in "//fname) attr_string = var%get_attribute_string(attr_name, rc=status) - _ASSERT(status /= _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) + _ASSERT(status == _SUCCESS, 'failed to get attribute named '//attr_name//' in '//var_name//' in '//fname) _RETURN(_SUCCESS) end function get_var_attr_string From 5ad02a1936d525e787b89f72b56a2bf172c157e5 Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Tue, 3 Dec 2024 11:50:46 -0500 Subject: [PATCH 80/90] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c1be6318f47..0a0293c1ee8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow update offsets of ±timestep in ExtData2G - Minor revision (and generalization) of grid-def for GSI purposes - Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point +- PFIO/Variable class, new procedures to retrieve string/reals/int attributes from a variable ### Changed From d1b0f0d5c7086836929ac9a59a39e21f6c56575d Mon Sep 17 00:00:00 2001 From: Benjamin Auer Date: Tue, 3 Dec 2024 12:16:48 -0500 Subject: [PATCH 81/90] more updates --- base/MAPL_LatLonGridFactory.F90 | 1 + griddedio/FieldBundleRead.F90 | 33 ++++++++++++--------------------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/base/MAPL_LatLonGridFactory.F90 b/base/MAPL_LatLonGridFactory.F90 index 7d7adf040f67..8d4de3ada406 100644 --- a/base/MAPL_LatLonGridFactory.F90 +++ b/base/MAPL_LatLonGridFactory.F90 @@ -1871,6 +1871,7 @@ function get_file_format_vars(this) result(vars) class (LatLonGridFactory), intent(inout) :: this character(len=:), allocatable :: vars + integer :: i _UNUSED_DUMMY(this) if (allocated(this%lon_bounds_name) .and. allocated(this%lat_bounds_name)) then diff --git a/griddedio/FieldBundleRead.F90 b/griddedio/FieldBundleRead.F90 index a16ab0597285..352dd414c330 100644 --- a/griddedio/FieldBundleRead.F90 +++ b/griddedio/FieldBundleRead.F90 @@ -48,8 +48,8 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ type(StringVariableMap), pointer :: variables type(Variable), pointer :: this_variable type(StringVariableMapIterator) :: var_iter - character(len=:), pointer :: var_name,dim_name - character(len=:), allocatable :: lev_name + character(len=:), pointer :: var_name_ptr,dim_name + character(len=:), allocatable :: lev_name,var_name type(ESMF_Field) :: field type (StringVector), pointer :: dimensions type (StringVectorIterator) :: dim_iter @@ -71,14 +71,15 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ factory => get_factory(file_grid,rc=status) _VERIFY(status) grid_vars = factory%get_file_format_vars() - exclude_vars = grid_vars//",lev,time,lons,lats,time_bnds" + exclude_vars = ","//grid_vars//",lev,time,time_bnds," if (has_vertical_level) lev_size = metadata%get_dimension(trim(lev_name)) variables => metadata%get_variables() var_iter = variables%begin() do while (var_iter /= variables%end()) var_has_levels = .false. - var_name => var_iter%key() + var_name_ptr => var_iter%key() + var_name = ","//var_name_ptr//"," this_variable => var_iter%value() if (has_vertical_level) then @@ -91,29 +92,20 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ enddo end if - write(*,*)'bmaa exclude ',','//trim(exclude_vars)//',' - write(*,*)'bmaa var_name ',trim(var_name) - write(*,*)'bmaa index(exclude_var,var_name) ',index(','//trim(exclude_vars)//',',','//trim(var_name)//',') - write(*,*)'bmaa hardcode string index comma vs no ',index(',lon,lat,lon_bnds,lat_bnds,lev,time,lons,lats,time_bnds,',",lat_bnds,"),index(',lon,lat,lon_bnds,lat_bnds,lev,time,lons,lats,time_bnds,',"lat_bnds") - write(*,*)'bmaa allocatable string index comma vs no ',index(exclude_vars,",lat_bnds,"),index(exclude_vars,"lat_bnds") - write(*,*)'bmaa allocatable string index comma vs no ',index(exclude_vars,",time_bnds,"),index(exclude_vars,"time_bnds") - write(*,*)'bmaa allocatable string index comma vs no ',index(exclude_vars,",time,"),index(exclude_vars,"time") - write(*,*)'bmaa allocatable string index comma vs no ',index(exclude_vars,",lat,"),index(exclude_vars,"lat") - - if (index(','//trim(exclude_vars)//',',','//trim(var_name)//',') > 0) then + if (index(trim(exclude_vars),trim(var_name)) > 0) then call var_iter%next() cycle end if create_variable = .true. if (present(only_vars)) then - if (index(','//trim(only_vars)//',',','//trim(var_name)//',') < 1) create_variable = .false. + if (index(','//trim(only_vars)//',',trim(var_name)) < 1) create_variable = .false. end if if (create_variable) then if(var_has_levels) then if (grid_size(3) == lev_size) then location=MAPL_VLocationCenter dims = MAPL_DimsHorzVert - field= ESMF_FieldCreate(grid,name=trim(var_name),typekind=ESMF_TYPEKIND_R4, & + field= ESMF_FieldCreate(grid,name=trim(var_name_ptr),typekind=ESMF_TYPEKIND_R4, & ungriddedUbound=[grid_size(3)],ungriddedLBound=[1], rc=status) block real, pointer :: ptr3d(:,:,:) @@ -123,7 +115,7 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ else if (grid_size(3)+1 == lev_size) then location=MAPL_VLocationEdge dims = MAPL_DimsHorzVert - field= ESMF_FieldCreate(grid,name=trim(var_name),typekind=ESMF_TYPEKIND_R4, & + field= ESMF_FieldCreate(grid,name=trim(var_name_ptr),typekind=ESMF_TYPEKIND_R4, & ungriddedUbound=[grid_size(3)],ungriddedLBound=[0], rc=status) block real, pointer :: ptr3d(:,:,:) @@ -134,7 +126,7 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ else location=MAPL_VLocationNone dims = MAPL_DimsHorzOnly - field= ESMF_FieldCreate(grid,name=trim(var_name),typekind=ESMF_TYPEKIND_R4, & + field= ESMF_FieldCreate(grid,name=trim(var_name_ptr),typekind=ESMF_TYPEKIND_R4, & rc=status) block real, pointer :: ptr2d(:,:) @@ -146,9 +138,8 @@ subroutine MAPL_create_bundle_from_metdata_id(bundle,metadata_id,file_name,only_ _VERIFY(status) call ESMF_AttributeSet(field,name='VLOCATION',value=location,rc=status) _VERIFY(status) - write(*,*)"bmaa getting units for ",trim(var_name) - units = metadata%get_var_attr_string(var_name,'units',_RC) - long_name = metadata%get_var_attr_string(var_name,'long_name',_RC) + units = metadata%get_var_attr_string(var_name_ptr,'units',_RC) + long_name = metadata%get_var_attr_string(var_name_ptr,'long_name',_RC) call ESMF_AttributeSet(field,name='UNITS',value=units,rc=status) _VERIFY(status) call ESMF_AttributeSet(field,name='LONG_NAME',value=long_name,rc=status) From 9de255babbb24fbe6d59582f1aee1408e34e82f6 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Tue, 3 Dec 2024 14:36:43 -0500 Subject: [PATCH 82/90] Fix up CI image code --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 01fe02b99c2b..8b900ee522ae 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -229,8 +229,8 @@ workflows: baselibs_version: *baselibs_version container_name: mapl mpi_name: openmpi - mpi_version: 5.0.2 + mpi_version: 5.0.5 compiler_name: gcc - compiler_version: 13.2.0 + compiler_version: 14.2.0 image_name: geos-env-mkl tag_build_arg_name: *tag_build_arg_name From 9a2fee5d61489747b7799d0a8f07282d2b531034 Mon Sep 17 00:00:00 2001 From: Tom Clune Date: Wed, 4 Dec 2024 16:01:19 -0500 Subject: [PATCH 83/90] Update base/MAPL_LatLonGridFactory.F90 --- base/MAPL_LatLonGridFactory.F90 | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/base/MAPL_LatLonGridFactory.F90 b/base/MAPL_LatLonGridFactory.F90 index 8d4de3ada406..b7d076e5a2a3 100644 --- a/base/MAPL_LatLonGridFactory.F90 +++ b/base/MAPL_LatLonGridFactory.F90 @@ -1874,14 +1874,12 @@ function get_file_format_vars(this) result(vars) integer :: i _UNUSED_DUMMY(this) - if (allocated(this%lon_bounds_name) .and. allocated(this%lat_bounds_name)) then - vars = 'lon,lat,'//this%lon_bounds_name//','//this%lat_bounds_name - else if (allocated(this%lon_bounds_name) .and. (.not. allocated(this%lat_bounds_name))) then - vars = 'lon,lat,'//this%lon_bounds_name - else if (allocated(this%lat_bounds_name) .and. (.not. allocated(this%lon_bounds_name))) then - vars = 'lon,lat,'//this%lat_bounds_name - else if ((.not.allocated(this%lat_bounds_name)) .and. (.not. allocated(this%lon_bounds_name))) then - vars = 'lon,lat' + vars = 'lon,lat' + if (allocated(this%lon_bounds_name)) then + vars = vars // ',' // this%lon_bounds_name + end if + if (allocated(this%lat_bounds_name)) then + vars = vars // ',' // this%lat_bounds_name end if end function get_file_format_vars From 7005c9d91ba56683d2091508f04773a2f8976d7e Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Thu, 5 Dec 2024 11:42:15 -0500 Subject: [PATCH 84/90] Fix bad merge --- CHANGELOG.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aba4ebf21778..efbb36ca0a13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,11 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added macro _RETURN(_SUCCESS) to fetch_data +- Added macro `_RETURN(_SUCCESS)` to fetch_data - Allow update offsets of ±timestep in ExtData2G - Minor revision (and generalization) of grid-def for GSI purposes - Add ability to use an `ESMF_CONFIG_FILE` environment variable to specify name of file to pass in pre-`ESMF_Initialize` options to ESMF (see [ESMF Docs](https://earthsystemmodeling.org/docs/release/latest/ESMF_refdoc/node4.html#SECTION04024000000000000000) for allowed flags. -- Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point - Allow lat-lon grid factory to detect and use CF compliant lat-lon bounds in a file when making a grid - PFIO/Variable class, new procedures to retrieve string/reals/int attributes from a variable @@ -51,11 +50,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed issue of some Baselibs builds appearing to support zstandard. This is not possible due to Baselibs building HDF5 and netCDF as static libraries -<<<<<<< HEAD - Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point -======= - Fixed a bug where the periodicity around the earth of the lat-lon grid was not being set properly when grid did not span from pole to pole ->>>>>>> develop ### Removed From e9b9476312de22ed094951ce96e424da9f613bf7 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Thu, 5 Dec 2024 11:59:05 -0500 Subject: [PATCH 85/90] Add a _VERIFY --- gridcomps/Cap/MAPL_Cap.F90 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gridcomps/Cap/MAPL_Cap.F90 b/gridcomps/Cap/MAPL_Cap.F90 index 536e014e8f5f..88d17d5c52a4 100644 --- a/gridcomps/Cap/MAPL_Cap.F90 +++ b/gridcomps/Cap/MAPL_Cap.F90 @@ -301,11 +301,12 @@ subroutine run_model(this, comm, unusable, rc) ! Step two: get the length of the environment variable call get_environment_variable('ESMF_CONFIG_FILE', length=esmfConfigFileLen, status=status) ! Step three: if the environment variable exists, get the value of the environment variable - if (status == 0) then + if (status == 0) then ! variable exists ! We need to deallocate so we can reallocate deallocate(esmfConfigFile) allocate(character(len = esmfConfigFileLen) :: esmfConfigFile) call get_environment_variable('ESMF_CONFIG_FILE', value=esmfConfigFile, status=status) + _VERIFY(status) end if if (rank == 0) then From aa313a0c9f4f6dca7363134f33117e2413e96d57 Mon Sep 17 00:00:00 2001 From: Matthew Thompson Date: Thu, 5 Dec 2024 14:09:02 -0500 Subject: [PATCH 86/90] Clarify comment --- pfio/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pfio/CMakeLists.txt b/pfio/CMakeLists.txt index d842f99674b0..178695eb74ab 100644 --- a/pfio/CMakeLists.txt +++ b/pfio/CMakeLists.txt @@ -2,8 +2,8 @@ esma_set_this (OVERRIDE MAPL.pfio) # This is a workaround for a current ifx bug # Technically, this bug is only due to a bug between -# ifx and ArrayReference.F90, but in CMake land, it -# is hard to remove OpenMP flags for a single file. +# ifx, OpenMP, and ArrayReference.F90, but in CMake land, it +# is hard to remove OpenMP flags for a *single* file. if (CMAKE_Fortran_COMPILER_ID STREQUAL "IntelLLVM") set_source_files_properties(ArrayReference.F90 PROPERTIES COMPILE_DEFINITIONS ODD_IFX_BUG) endif () From 69d34a2ec8f660c8f5c62c793dd672748c77f217 Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Thu, 5 Dec 2024 14:49:37 -0500 Subject: [PATCH 87/90] WIP: Fixes needed for flang (#3176) * Fixes needed for flang * Update changelog * Remove general use pfio * Update to changelog to try and trigger CI --- CHANGELOG.md | 1 + generic/AbstractComponent.F90 | 7 ++++--- generic/MAPL_Generic.F90 | 4 +++- gridcomps/History/Sampler/MAPL_EpochSwathMod.F90 | 1 - include/unused_dummy.H | 6 +++++- pfio/AbstractServer.F90 | 9 +++++++++ 6 files changed, 22 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index efbb36ca0a13..e55b7840bf5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fixed issue of some Baselibs builds appearing to support zstandard. This is not possible due to Baselibs building HDF5 and netCDF as static libraries +- Updates to support llvm-flang - Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point - Fixed a bug where the periodicity around the earth of the lat-lon grid was not being set properly when grid did not span from pole to pole diff --git a/generic/AbstractComponent.F90 b/generic/AbstractComponent.F90 index 094333e88616..c9db2c74a891 100644 --- a/generic/AbstractComponent.F90 +++ b/generic/AbstractComponent.F90 @@ -1,4 +1,5 @@ module mapl_AbstractComponent + use pFlogger, only: t_Logger => Logger implicit none private @@ -96,7 +97,7 @@ subroutine i_RunChild(this, name, clock, phase, unusable, rc) end subroutine i_RunChild subroutine i_SetLogger(this, logger) - use pfl_logger, only: t_Logger => Logger + import t_Logger import AbstractComponent implicit none class(AbstractComponent), intent(inout) :: this @@ -105,7 +106,7 @@ subroutine i_SetLogger(this, logger) end subroutine i_SetLogger function i_GetLogger(this) result(logger) - use pfl_logger, only: t_Logger => Logger + import t_Logger import AbstractComponent implicit none class(t_Logger), pointer :: logger @@ -113,5 +114,5 @@ function i_GetLogger(this) result(logger) end function i_GetLogger end interface - + end module mapl_AbstractComponent diff --git a/generic/MAPL_Generic.F90 b/generic/MAPL_Generic.F90 index 13d2a97122c9..1aa38f283b91 100644 --- a/generic/MAPL_Generic.F90 +++ b/generic/MAPL_Generic.F90 @@ -9805,8 +9805,10 @@ subroutine READIT(WHICH) if (io_rank == 0) then print *,'Using parallel IO for reading file: ',trim(DATAFILE) -#ifdef __NAG_COMPILER_RELEASE +#if defined( __NAG_COMPILER_RELEASE) _FAIL('NAG does not provide ftell. Convert to stream I/O') +#elif defined(__flang__) + _FAIL('flang does not provide ftell. Convert to stream I/O') #else offset = _FTELL(UNIT)+4 #endif diff --git a/gridcomps/History/Sampler/MAPL_EpochSwathMod.F90 b/gridcomps/History/Sampler/MAPL_EpochSwathMod.F90 index cf051b2b66a8..3f692e4b2244 100644 --- a/gridcomps/History/Sampler/MAPL_EpochSwathMod.F90 +++ b/gridcomps/History/Sampler/MAPL_EpochSwathMod.F90 @@ -15,7 +15,6 @@ module MAPL_EpochSwathMod use MAPL_TimeDataMod use MAPL_VerticalDataMod use MAPL_Constants - use pFIO use MAPL_GriddedIOItemVectorMod use MAPL_GriddedIOItemMod use MAPL_ExceptionHandling diff --git a/include/unused_dummy.H b/include/unused_dummy.H index 6d7063924148..6ffecb5dfcf1 100644 --- a/include/unused_dummy.H +++ b/include/unused_dummy.H @@ -10,4 +10,8 @@ #ifdef _UNUSED_DUMMY # undef _UNUSED_DUMMY #endif -#define _UNUSED_DUMMY(x) if (.false.) then; associate (q____ => x); end associate; endif +#if defined(__flang__) +# define _UNUSED_DUMMY(x) if (.false.) then; print*, shape(x); endif +#else +# define _UNUSED_DUMMY(x) if (.false.) then; associate (q____ => x); end associate; endif +#endif diff --git a/pfio/AbstractServer.F90 b/pfio/AbstractServer.F90 index d93911f302b8..519378445683 100644 --- a/pfio/AbstractServer.F90 +++ b/pfio/AbstractServer.F90 @@ -205,7 +205,10 @@ subroutine set_status(this,status) !$omp critical (counter_status) this%status = status + ! llvm-flang has an issue with omp flush of complex data structures +#if !defined(__flang__) !$omp flush (this) +#endif !$omp end critical (counter_status) end subroutine set_status @@ -217,7 +220,10 @@ subroutine update_status(this, rc) !$omp critical (counter_status) this%status = this%status -1 status = this%status + ! llvm-flang has an issue with omp flush of complex data structures +#if !defined(__flang__) !$omp flush (this) +#endif !$omp end critical (counter_status) if (status /= 0) then _RETURN(_SUCCESS) @@ -290,7 +296,10 @@ subroutine set_AllBacklogIsEmpty(this,status) !$omp critical (backlog_status) this%all_backlog_is_empty = status + ! llvm-flang has an issue with omp flush of complex data structures +#if !defined(__flang__) !$omp flush (this) +#endif !$omp end critical (backlog_status) end subroutine set_AllBacklogIsEmpty From c304f5fabd9afbde476757fa75a7de3e0d1ca0e4 Mon Sep 17 00:00:00 2001 From: Atanas Trayanov <50172245+atrayano@users.noreply.github.com> Date: Fri, 6 Dec 2024 09:35:38 -0500 Subject: [PATCH 88/90] Update generic/MAPL_Generic.F90 Co-authored-by: Tom Clune --- generic/MAPL_Generic.F90 | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/generic/MAPL_Generic.F90 b/generic/MAPL_Generic.F90 index 5d02b4669767..077c257c0838 100644 --- a/generic/MAPL_Generic.F90 +++ b/generic/MAPL_Generic.F90 @@ -352,16 +352,15 @@ module MAPL_GenericMod end interface interface - subroutine i_Run(gc, import_state, export_state, clock, unusable, rc) + subroutine i_Run(gc, import_state, export_state, clock, rc) use mapl_KeywordEnforcerMod use ESMF implicit none - type(ESMF_GridComp), intent(inout) :: gc - type(ESMF_State), intent(inout) :: import_state - type(ESMF_State), intent(inout) :: export_state - type(ESMF_Clock), intent(inout) :: clock - class(KeywordEnforcer), optional, intent(in) :: unusable - integer, optional, intent(out) :: rc + type(ESMF_GridComp) :: gc + type(ESMF_State):: import_state + type(ESMF_State) :: export_state + type(ESMF_Clock) :: clock + integer, intent(out) :: rc end subroutine i_Run end interface From 4830949dde508052a6155a11dda40a519efc7baa Mon Sep 17 00:00:00 2001 From: Atanas Trayanov Date: Fri, 6 Dec 2024 10:08:04 -0500 Subject: [PATCH 89/90] Added I_run to the decaration of customRefresh. --- generic/MAPL_Generic.F90 | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/generic/MAPL_Generic.F90 b/generic/MAPL_Generic.F90 index 077c257c0838..28c371198197 100644 --- a/generic/MAPL_Generic.F90 +++ b/generic/MAPL_Generic.F90 @@ -447,10 +447,8 @@ end subroutine i_Run integer , pointer :: phase_record(:) => null() integer , pointer :: phase_coldstart(:)=> null() integer , pointer :: phase_refresh(:)=> null() - procedure(), public, nopass , pointer :: customRefresh => null() + procedure(i_run), public, nopass, pointer :: customRefresh => null() -! procedure(), pointer, nopass :: run_entry_point => null() - ! Make accessors? type(ESMF_GridComp) :: RootGC type(ESMF_GridComp) , pointer :: parentGC => null() From 4a0b7e138bace9d8cff979a11438f3a67dacfb6e Mon Sep 17 00:00:00 2001 From: Matt Thompson Date: Fri, 6 Dec 2024 12:50:39 -0500 Subject: [PATCH 90/90] Prepare for 2.51.0 Release (#3224) --- CHANGELOG.md | 18 +++++++++++++----- CMakeLists.txt | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 973f9dd9aa44..4458f034c4c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +### Changed + +### Fixed + +### Removed + +### Deprecated + +## [2.51.0] - 2024-12-06 + +### Added + - Added macro `_RETURN(_SUCCESS)` to fetch_data - Allow update offsets of ±timestep in ExtData2G - Minor revision (and generalization) of grid-def for GSI purposes - Add ability to use an `ESMF_CONFIG_FILE` environment variable to specify name of file to pass in pre-`ESMF_Initialize` options to ESMF (see [ESMF Docs](https://earthsystemmodeling.org/docs/release/latest/ESMF_refdoc/node4.html#SECTION04024000000000000000) for allowed flags. - Allow lat-lon grid factory to detect and use CF compliant lat-lon bounds in a file when making a grid - PFIO/Variable class, new procedures to retrieve string/reals/int attributes from a variable -- Added a call in GenericRefresh to allow GC's refresh method to be called; in support +- Added a call in GenericRefresh to allow GC's refresh method to be called; in support of CICE6 rewind ### Changed @@ -58,10 +70,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Trajectory sampler: fix a bug when group_name does not exist in netCDF file and a bug that omitted the first time point - Fixed a bug where the periodicity around the earth of the lat-lon grid was not being set properly when grid did not span from pole to pole -### Removed - -### Deprecated - ## [2.50.3] - 2024-12-02 ### Fixed diff --git a/CMakeLists.txt b/CMakeLists.txt index 5bcfdff376b0..cfbafb015abd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ endif () project ( MAPL - VERSION 2.50.3 + VERSION 2.51.0 LANGUAGES Fortran CXX C) # Note - CXX is required for ESMF # Set the possible values of build type for cmake-gui