diff --git a/CHANGELOG.md b/CHANGELOG.md index c4ca38d87e77..c6c1a8c1adc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added vertical and ungridded dimensions to output for History3G - Create rank-agnostic representation of `ESMF_Field` objects as rank-3 array pointers. - Add time accumulation for output from ESMF_Field objects. +- Add tests for time accumulation ### Changed diff --git a/generic3g/actions/AccumulatorAction.F90 b/generic3g/actions/AccumulatorAction.F90 index 42336ec9a2f3..2a939d64c978 100644 --- a/generic3g/actions/AccumulatorAction.F90 +++ b/generic3g/actions/AccumulatorAction.F90 @@ -66,8 +66,6 @@ subroutine initialize(this, importState, exportState, clock, rc) call get_field(importState, import_field, _RC) call get_field(exportState, export_field, _RC) - fields_are_conformable = FieldsAreConformable(import_field, export_field, _RC) - _ASSERT(fields_are_conformable, 'Import field and export field are not conformable.') if(this%initialized()) then call ESMF_FieldDestroy(this%accumulation_field, _RC) @@ -77,8 +75,8 @@ subroutine initialize(this, importState, exportState, clock, rc) this%result_field = ESMF_FieldCreate(export_field, _RC) call this%clear_accumulator(_RC) - _UNUSED_DUMMY(clock) _RETURN(_SUCCESS) + _UNUSED_DUMMY(clock) end subroutine initialize diff --git a/generic3g/actions/CMakeLists.txt b/generic3g/actions/CMakeLists.txt index 4fdeccb74a43..90d4d5f7a110 100644 --- a/generic3g/actions/CMakeLists.txt +++ b/generic3g/actions/CMakeLists.txt @@ -11,7 +11,7 @@ target_sources(MAPL.generic3g PRIVATE TimeInterpolateAction.F90 AccumulatorAction.F90 - MeanAccumulator.F90 - MaxAccumulator.F90 - MinAccumulator.F90 + MeanAction.F90 + MaxAction.F90 + MinAction.F90 ) diff --git a/generic3g/actions/MaxAccumulator.F90 b/generic3g/actions/MaxAction.F90 similarity index 69% rename from generic3g/actions/MaxAccumulator.F90 rename to generic3g/actions/MaxAction.F90 index 959b2310e9f5..ae5a9cecebd6 100644 --- a/generic3g/actions/MaxAccumulator.F90 +++ b/generic3g/actions/MaxAction.F90 @@ -1,5 +1,5 @@ #include "MAPL_Generic.h" -module mapl3g_MaxAccumulator +module mapl3g_MaxAction use mapl3g_AccumulatorAction use MAPL_ExceptionHandling use MAPL_InternalConstantsMod, only: MAPL_UNDEFINED_REAL, MAPL_UNDEFINED_REAL64 @@ -7,29 +7,28 @@ module mapl3g_MaxAccumulator use ESMF implicit none private - public :: AccumulatorAction + public :: MaxAction - type, extends(AccumulatorAction) :: MaxAccumulator - private + type, extends(AccumulatorAction) :: MaxAction contains procedure :: accumulate_R4 => max_accumulate_R4 - end type MaxAccumulator + end type MaxAction - interface MaxAccumulator - module procedure :: construct_MaxAccumulator - end interface MaxAccumulator + interface MaxAction + module procedure :: construct_MaxAction + end interface MaxAction contains - function construct_MaxAccumulator() result(acc) - type(MaxAccumulator) :: acc + function construct_MaxAction() result(acc) + type(MaxAction) :: acc acc%CLEAR_VALUE_R4 = MAPL_UNDEFINED_REAL - end function construct_MaxAccumulator + end function construct_MaxAction subroutine max_accumulate_R4(this, update_field, rc) - class(MaxAccumulator), intent(inout) :: this + class(MaxAction), intent(inout) :: this type(ESMF_Field), intent(inout) :: update_field integer, optional, intent(out) :: rc @@ -49,4 +48,4 @@ subroutine max_accumulate_R4(this, update_field, rc) end subroutine max_accumulate_R4 -end module mapl3g_MaxAccumulator +end module mapl3g_MaxAction diff --git a/generic3g/actions/MeanAccumulator.F90 b/generic3g/actions/MeanAction.F90 similarity index 88% rename from generic3g/actions/MeanAccumulator.F90 rename to generic3g/actions/MeanAction.F90 index ee93f380f13e..961e380c868a 100644 --- a/generic3g/actions/MeanAccumulator.F90 +++ b/generic3g/actions/MeanAction.F90 @@ -1,5 +1,5 @@ #include "MAPL_Generic.h" -module mapl3g_MeanAccumulator +module mapl3g_MeanAction use mapl3g_AccumulatorAction use MAPL_InternalConstantsMod, only: MAPL_UNDEFINED_REAL, MAPL_UNDEFINED_REAL64 use MAPL_ExceptionHandling @@ -7,9 +7,9 @@ module mapl3g_MeanAccumulator use ESMF implicit none private - public :: MeanAccumulator + public :: MeanAction - type, extends(AccumulatorAction) :: MeanAccumulator + type, extends(AccumulatorAction) :: MeanAction !private integer(ESMF_KIND_R8) :: counter_scalar = 0_ESMF_KIND_I8 logical, allocatable :: valid_mean(:) @@ -21,12 +21,12 @@ module mapl3g_MeanAccumulator procedure :: calculate_mean_R4 procedure :: clear_valid_mean procedure :: accumulate_R4 => accumulate_mean_R4 - end type MeanAccumulator + end type MeanAction contains subroutine clear_mean_accumulator(this, rc) - class(MeanAccumulator), intent(inout) :: this + class(MeanAction), intent(inout) :: this integer, optional, intent(out) :: rc integer :: status @@ -39,7 +39,7 @@ subroutine clear_mean_accumulator(this, rc) end subroutine clear_mean_accumulator subroutine clear_valid_mean(this, rc) - class(MeanAccumulator), intent(inout) :: this + class(MeanAction), intent(inout) :: this integer, optional, intent(out) :: rc integer :: status @@ -53,7 +53,7 @@ subroutine clear_valid_mean(this, rc) end subroutine clear_valid_mean subroutine calculate_mean(this, rc) - class(MeanAccumulator), intent(inout) :: this + class(MeanAction), intent(inout) :: this integer, optional, intent(out) :: rc integer :: status @@ -71,7 +71,7 @@ subroutine calculate_mean(this, rc) end subroutine calculate_mean subroutine update_mean_accumulator(this, importState, exportState, clock, rc) - class(MeanAccumulator), intent(inout) :: this + class(MeanAction), intent(inout) :: this type(ESMF_State) :: importState type(ESMF_State) :: exportState type(ESMF_Clock) :: clock @@ -89,7 +89,7 @@ subroutine update_mean_accumulator(this, importState, exportState, clock, rc) end subroutine update_mean_accumulator subroutine invalidate_mean_accumulator(this, importState, exportState, clock, rc) - class(MeanAccumulator), intent(inout) :: this + class(MeanAction), intent(inout) :: this type(ESMF_State) :: importState type(ESMF_State) :: exportState type(ESMF_Clock) :: clock @@ -104,7 +104,7 @@ subroutine invalidate_mean_accumulator(this, importState, exportState, clock, rc end subroutine invalidate_mean_accumulator subroutine calculate_mean_R4(this, rc) - class(MeanAccumulator), intent(inout) :: this + class(MeanAction), intent(inout) :: this integer, optional, intent(out) :: rc integer :: status @@ -122,7 +122,7 @@ subroutine calculate_mean_R4(this, rc) end subroutine calculate_mean_R4 subroutine accumulate_mean_R4(this, update_field, rc) - class(MeanAccumulator), intent(inout) :: this + class(MeanAction), intent(inout) :: this type(ESMF_Field), intent(inout) :: update_field integer, optional, intent(out) :: rc @@ -144,4 +144,4 @@ subroutine accumulate_mean_R4(this, update_field, rc) end subroutine accumulate_mean_R4 -end module mapl3g_MeanAccumulator +end module mapl3g_MeanAction diff --git a/generic3g/actions/MinAccumulator.F90 b/generic3g/actions/MinAction.F90 similarity index 69% rename from generic3g/actions/MinAccumulator.F90 rename to generic3g/actions/MinAction.F90 index 2d27dc19558f..cd6c47ddf9c0 100644 --- a/generic3g/actions/MinAccumulator.F90 +++ b/generic3g/actions/MinAction.F90 @@ -1,5 +1,5 @@ #include "MAPL_Generic.h" -module mapl3g_MinAccumulator +module mapl3g_MinAction use mapl3g_AccumulatorAction use MAPL_ExceptionHandling use MAPL_InternalConstantsMod, only: MAPL_UNDEFINED_REAL, MAPL_UNDEFINED_REAL64 @@ -7,29 +7,28 @@ module mapl3g_MinAccumulator use ESMF implicit none private - public :: AccumulatorAction + public :: MinAction - type, extends(AccumulatorAction) :: MinAccumulator - private + type, extends(AccumulatorAction) :: MinAction contains procedure :: accumulate_R4 => min_accumulate_R4 - end type MinAccumulator + end type MinAction - interface MinAccumulator - module procedure :: construct_MinAccumulator - end interface MinAccumulator + interface MinAction + module procedure :: construct_MinAction + end interface MinAction contains - function construct_MinAccumulator() result(acc) - type(MinAccumulator) :: acc + function construct_MinAction() result(acc) + type(MinAction) :: acc acc%CLEAR_VALUE_R4 = MAPL_UNDEFINED_REAL - end function construct_MinAccumulator + end function construct_MinAction subroutine min_accumulate_R4(this, update_field, rc) - class(MinAccumulator), intent(inout) :: this + class(MinAction), intent(inout) :: this type(ESMF_Field), intent(inout) :: update_field integer, optional, intent(out) :: rc @@ -49,4 +48,4 @@ subroutine min_accumulate_R4(this, update_field, rc) end subroutine min_accumulate_R4 -end module mapl3g_MinAccumulator +end module mapl3g_MinAction diff --git a/generic3g/tests/CMakeLists.txt b/generic3g/tests/CMakeLists.txt index 41971ac9345f..73b5e2727b43 100644 --- a/generic3g/tests/CMakeLists.txt +++ b/generic3g/tests/CMakeLists.txt @@ -37,6 +37,9 @@ set (test_srcs Test_CSR_SparseMatrix.pf Test_AccumulatorAction.pf + Test_MeanAction.pf + Test_MaxAction.pf + Test_MinAction.pf ) @@ -45,7 +48,7 @@ add_pfunit_ctest(MAPL.generic3g.tests LINK_LIBRARIES MAPL.generic3g MAPL.shared MAPL.pfunit scratchpad EXTRA_INITIALIZE Initialize EXTRA_USE MAPL_pFUnit_Initialize - OTHER_SOURCES MockUserGridComp.F90 MockItemSpec.F90 + OTHER_SOURCES MockUserGridComp.F90 MockItemSpec.F90 accumulator_action_test_common.F90 WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} MAX_PES 4 ) diff --git a/generic3g/tests/Test_AccumulatorAction.pf b/generic3g/tests/Test_AccumulatorAction.pf index 37e2201d2e0e..68384db7d52f 100644 --- a/generic3g/tests/Test_AccumulatorAction.pf +++ b/generic3g/tests/Test_AccumulatorAction.pf @@ -1,32 +1,21 @@ -#define _RETURN_(R, S) if(present(R)) R = S; return -#define _RETURN(S) _RETURN_(rc, S) -#define _SUCCESS 0 #include "MAPL_TestErr.h" #include "unused_dummy.H" module Test_AccumulatorAction use mapl3g_AccumulatorAction - use mapl3g_MeanAccumulator + use accumulator_action_test_common use esmf use funit use MAPL_FieldUtils implicit none - integer(kind=ESMF_KIND_I4), parameter :: TIME_STEP = 1 - integer(kind=ESMF_KIND_I4), parameter :: START_TIME = 3000 - integer, parameter :: MAX_INDEX(2) = [4, 4] - real(kind=ESMF_KIND_R8), parameter :: MIN_CORNER_COORD(2) = [0.0_ESMF_KIND_R8, 0.0_ESMF_KIND_R8] - real(kind=ESMF_KIND_R8), parameter :: MAX_CORNER_COORD(2) = [4.0_ESMF_KIND_R8, 4.0_ESMF_KIND_R8] - type(ESMF_TypeKind_Flag), parameter :: typekind = ESMF_TYPEKIND_R4 - integer, parameter :: R4 = ESMF_KIND_R4 - integer, parameter :: R8 = ESMF_KIND_R8 - contains @Test subroutine test_construct_AccumulatorAction() type(AccumulatorAction) :: acc - @assert_that(acc%update_calculated, is(false())) + @assertFalse(acc%update_calculated, 'updated_calculated .TRUE.') + @assertFalse(acc%initialized(), 'initialized .TRUE.') end subroutine test_construct_AccumulatorAction @@ -35,28 +24,14 @@ contains type(AccumulatorAction) :: acc type(ESMF_State) :: importState, exportState type(ESMF_Clock) :: clock - type(ESMF_Field) :: import_field integer :: status - real(kind=R4), parameter :: TEST_VALUE = 1.0_R4 - real(kind=R4) :: clear_value logical :: equals_expected_value call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) - @assert_that(acc%initialized(), is(false())) - - call get_field(importState, import_field, _RC) - call FieldSet(import_field, TEST_VALUE, _RC) - - equals_expected_value = FieldIsConstant(import_field, TEST_VALUE, _RC) - @assert_that(equals_expected_value, is(true())) - call acc%initialize(importState, exportState, clock, _RC) - @assert_that(acc%initialized(), is(true())) - - clear_value = acc%CLEAR_VALUE_R4 - equals_expected_value = FieldIsConstant(acc%accumulation_field, clear_value, _RC) - @assert_that(equals_expected_value, is(true())) - + @assertTrue(acc%initialized(), 'initialized .FALSE.') + equals_expected_value = FieldIsConstant(acc%accumulation_field, acc%CLEAR_VALUE_R4, _RC) + @assertTrue(equals_expected_value, 'accumulation_field was not cleared.') call destroy_objects(importState, exportState, clock, _RC) end subroutine test_initialize @@ -75,19 +50,14 @@ contains call acc%initialize(importState, exportState, clock, _RC) call get_field(importState, import_field, _RC) call FieldSet(import_field, invalidate_value, _RC) - call acc%invalidate(importState, exportState, clock, _RC) - @assert_that(acc%update_calculated, is(false())) - + @assertFalse(acc%update_calculated, 'update_calculated .TRUE.') equals_expected_value = FieldIsConstant(acc%accumulation_field, invalidate_value, _RC) - @assert_that(equals_expected_value, is(true())) - + @assertTrue(equals_expected_value, 'accumulation_field not equal to invalidate_value') call acc%invalidate(importState, exportState, clock, _RC) - @assert_that(acc%update_calculated, is(false())) - + @assertFalse(acc%update_calculated, 'update_calculated .TRUE.') equals_expected_value = FieldIsConstant(acc%accumulation_field, 2*invalidate_value, _RC) - @assert_that(equals_expected_value, is(true())) - + @assertTrue(equals_expected_value, 'accumulation_field .FALSE.') call destroy_objects(importState, exportState, clock, _RC) end subroutine test_invalidate @@ -103,75 +73,34 @@ contains real(kind=R4) :: update_value logical :: equals_expected_value - ! Set up call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) - - ! Initialize call acc%initialize(importState, exportState, clock, _RC) - - ! Set import_field for invalidate step. call get_field(importState, import_field, _RC) call FieldSet(import_field, invalidate_value, _RC) - - ! Invalidate. call acc%invalidate(importState, exportState, clock, _RC) - - ! Check invalidate. - @assert_that(acc%update_calculated, is(false())) - equals_expected_value = FieldIsConstant(acc%accumulation_field, invalidate_value, _RC) - @assert_that(equals_expected_value, is(true())) - - ! Set expected value for update. - update_value = invalidate_value - ! Update. call acc%update(importState, exportState, clock, _RC) - - ! Check update. - @assert_that(acc%update_calculated, is(true())) - ! Check that accumulation_field is cleared. + update_value = invalidate_value + @assertTrue(acc%update_calculated, 'update_calculated .FALSE.') equals_expected_value = FieldIsConstant(acc%accumulation_field, acc%CLEAR_VALUE_R4, _RC) - @assert_that(equals_expected_value, is(true())) - ! Check result_field + @assertTrue(equals_expected_value, 'accumulation_field was not cleared.') equals_expected_value = FieldIsConstant(acc%result_field, update_value, _RC) - @assert_that(equals_expected_value, is(true())) - ! Check export_field. + @assertTrue(equals_expected_value, 'result_field not equal to update_value') call get_field(exportState, export_field, _RC) equals_expected_value = FieldIsConstant(export_field, update_value, _RC) - @assert_that(equals_expected_value, is(true())) + @assertTrue(equals_expected_value, 'export_field not equal to update_value') - ! Invalidate call acc%invalidate(importState, exportState, clock, _RC) - - ! Check invalidate. - @assert_that(acc%update_calculated, is(false())) - - ! Invalidate again. call acc%invalidate(importState, exportState, clock, _RC) - - ! Check invalidate, again. - @assert_that(acc%update_calculated, is(false())) - ! This time accumulation_field should show true accumulation. - update_value = 2 * invalidate_value - equals_expected_value = FieldIsConstant(acc%accumulation_field, update_value, _RC) - @assert_that(equals_expected_value, is(true())) - - ! Update call acc%update(importState, exportState, clock, _RC) - - ! Check update. - @assert_that(acc%update_calculated, is(true())) - ! Check that accumulation_field is cleared. + update_value = 2 * invalidate_value + @assertTrue(acc%update_calculated, 'update_calculated .FALSE') equals_expected_value = FieldIsConstant(acc%accumulation_field, acc%CLEAR_VALUE_R4, _RC) - @assert_that(equals_expected_value, is(true())) - ! This time result_field should show true accumulation. + @assertTrue(equals_expected_value, 'accumulation_field was not cleared.') equals_expected_value = FieldIsConstant(acc%result_field, update_value, _RC) - @assert_that(equals_expected_value, is(true())) - ! This time export_field should show true accumulation. + @assertTrue(equals_expected_value, 'result_field not equal to update_value.') call get_field(exportState, export_field, _RC) equals_expected_value = FieldIsConstant(export_field, update_value, _RC) - @assert_that(equals_expected_value, is(true())) - - ! Tear down. + @assertTrue(equals_expected_value, 'export_field not equal to update_value') call destroy_objects(importState, exportState, clock, _RC) end subroutine test_update @@ -182,8 +111,7 @@ contains type(ESMF_State) :: importState, exportState type(ESMF_Clock) :: clock integer :: status - type(ESMF_Field) :: update_field, import_field - type(ESMF_Grid) :: grid + type(ESMF_Field) :: update_field type(ESMF_TypeKind_Flag) :: typekind logical :: matches_expected real(kind=ESMF_KIND_R4), parameter :: value_r4 = 3.0_ESMF_KIND_R4 @@ -191,22 +119,13 @@ contains typekind = ESMF_TYPEKIND_R4 call initialize_objects(importState, exportState, clock, typekind, _RC) call acc%initialize(importState, exportState, clock, _RC) - call get_field(importState, import_field, _RC) - call ESMF_FieldGet(import_field, grid=grid, _RC) - call initialize_field(update_field, typekind=typekind, grid=grid, _RC) + call initialize_field(update_field, typekind=typekind, _RC) call FieldSet(update_field, value_r4, _RC) - call acc%accumulate(update_field, _RC) matches_expected = FieldIsConstant(acc%accumulation_field, value_r4, _RC) - @assert_that(matches_expected, is(true())) - call ESMF_FieldDestroy(update_field, _RC) - - typekind = ESMF_TYPEKIND_R8 - call initialize_field(update_field, typekind=typekind, grid=grid, _RC) - call FieldSet(update_field, 3.0_ESMF_KIND_R8, _RC) - call acc%accumulate(update_field) - @assertExceptionRaised() + @assertTrue(matches_expected, 'accumulation_field not equal to value_r4') call ESMF_FieldDestroy(update_field, _RC) + call destroy_objects(importState, exportState, clock, _RC) end subroutine test_accumulate @@ -222,10 +141,10 @@ contains call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) call acc%initialize(importState, exportState, clock, _RC) call FieldSet(acc%accumulation_field, TEST_VALUE, _RC) - is_expected_value = FieldIsConstant(acc%accumulation_field, TEST_VALUE, _RC) call acc%clear_accumulator(_RC) is_expected_value = FieldIsConstant(acc%accumulation_field, acc%CLEAR_VALUE_R4, _RC) - @assert_that(is_expected_value, is(true())) + @assertTrue(is_expected_value, 'accumulation_field was not cleared.') + call destroy_objects(importState, exportState, clock, _RC) end subroutine test_clear_accumulator @@ -238,158 +157,25 @@ contains real(kind=R4), parameter :: INITIAL_VALUE = 2.0_R4 real(kind=R4) :: update_value = 3.0_R4 real(kind=R4) :: expected_value - type(ESMF_Field) :: import_field, update_field + type(ESMF_Field) :: update_field logical :: field_is_expected_value call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) call acc%initialize(importState, exportState, clock, _RC) - call get_field(importState, import_field, _RC) - call FieldClone(import_field, update_field, _RC) + call initialize_field(update_field, typekind=typekind, _RC) call FieldSet(update_field, update_value, _RC) call FieldSet(acc%accumulation_field, INITIAL_VALUE, _RC) - expected_value = INITIAL_VALUE call acc%accumulate_R4(update_field, _RC) - expected_value = expected_value + update_value + expected_value = INITIAL_VALUE + update_value field_is_expected_value = FieldIsConstant(acc%accumulation_field, expected_value, _RC) - @assert_that(field_is_expected_value, is(true())) - - update_value = INITIAL_VALUE - call FieldSet(update_field, update_value, _RC) + @assertTrue(field_is_expected_value, 'accumulation_field not equal to expected_value.') call acc%accumulate_R4(update_field, _RC) expected_value = expected_value + update_value field_is_expected_value = FieldIsConstant(acc%accumulation_field, expected_value, _RC) - @assert_that(field_is_expected_value, is(true())) + @assertTrue(field_is_expected_value, 'accumulation_field not equal to expected_value.') + call ESMF_FieldDestroy(update_field, _RC) + call destroy_objects(importState, exportState, clock, _RC) end subroutine test_accumulate_R4 - @Test - subroutine test_calculate_mean_R4() - type(MeanAccumulator) :: acc - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer :: status - integer(kind=ESMF_KIND_I8), parameter :: COUNTER = 4 - real(kind=ESMF_KIND_R4), parameter :: MEAN = 4.0_R4 - logical :: matches_expected - - call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) - call acc%initialize(importState, exportState, clock, _RC) - call FieldSet(acc%accumulation_field, COUNTER*MEAN, _RC) - acc%counter_scalar = 4 - acc%valid_mean = .TRUE. - - call acc%calculate_mean_R4(_RC) - matches_expected = FieldIsConstant(acc%accumulation_field, MEAN, _RC) - @assert_that(matches_expected, is(true())) - - end subroutine test_calculate_mean_R4 - -! HELPER PROCEDURES - - logical function is_initialized(rc) result(lval) - integer, optional, intent(out) :: rc - integer :: status - - lval = ESMF_IsInitialized(_RC) - _RETURN(_SUCCESS) - - end function is_initialized - - subroutine initialize_field(field, typekind, grid, rc) - type(ESMF_Field), intent(inout) :: field - type(ESMF_TypeKind_Flag), intent(in) :: typekind - type(ESMF_Grid), optional, intent(inout) :: grid - integer, optional, intent(out) :: rc - type(ESMF_Grid) :: grid_ - logical :: grid_created - - integer :: status - - grid_created = .FALSE. - if(present(grid)) then - grid_created = ESMF_GridIsCreated(grid, _RC) - if(grid_created) grid_ = grid - end if - - if(.not. grid_created) then - grid_ = ESMF_GridCreateNoPeriDimUfrm(maxIndex=MAX_INDEX, & - & minCornerCoord=MIN_CORNER_COORD, maxCornerCoord=MAX_CORNER_COORD, _RC) - end if - - field = ESMF_FieldCreate(grid=grid_, typekind=typekind, _RC) - - if(present(grid)) grid = grid_ - _RETURN(_SUCCESS) - - end subroutine initialize_field - - subroutine initialize_objects(importState, exportState, clock, typekind, rc) - type(ESMF_State), intent(inout) :: importState, exportState - type(ESMF_Clock), intent(inout) :: clock - type(ESMF_TypeKind_Flag), intent(in) :: typekind - integer, optional, intent(out) :: rc - - integer :: status - type(ESMF_Field) :: importField, exportField - type(ESMF_Time) :: startTime - type(ESMF_TimeInterval) :: timeStep - type(ESMF_Grid) :: grid - - call ESMF_TimeIntervalSet(timeStep, s=TIME_STEP, _RC) - call ESMF_TimeSet(startTime, yy=START_TIME, _RC) - clock = ESMF_ClockCreate(timeStep=timeStep, startTime=startTime, _RC) - grid = ESMF_GridCreateNoPeriDimUfrm(maxIndex=MAX_INDEX, minCornerCoord=MIN_CORNER_COORD, maxCornerCoord=MAX_CORNER_COORD, _RC) - importField = ESMF_FieldCreate(grid=grid, typekind=typekind, _RC) - exportField = ESMF_FieldCreate(grid=grid, typekind=typekind, _RC) - importState = ESMF_StateCreate(stateIntent=ESMF_STATEINTENT_IMPORT, fieldList=[importField], name='import', _RC) - exportState = ESMF_StateCreate(stateIntent=ESMF_STATEINTENT_EXPORT, fieldList=[exportField], name='export', _RC) - _RETURN(_SUCCESS) - - end subroutine initialize_objects - - subroutine get_field(state, field, rc) - type(ESMF_State), intent(inout) :: state - type(ESMF_Field), intent(inout) :: field - integer, optional, intent(out) :: rc - - integer :: status - character(len=ESMF_MAXSTR) :: itemNameList(1) - - call ESMF_StateGet(state, itemNameList=itemNameList, _RC) - call ESMF_StateGet(state, itemName=itemNameList(1), field=field, _RC) - _RETURN(_SUCCESS) - - end subroutine get_field - - subroutine destroy_objects(importState, exportState, clock, rc) - type(ESMF_State), intent(inout) :: importState, exportState - type(ESMF_Clock), intent(inout) :: clock - integer, optional, intent(out) :: rc - - integer :: status - type(ESMF_Field) :: importField, exportField - type(ESMF_Grid) :: grid - - call get_field(importState, importField, _RC) - call get_field(exportState, exportField, _RC) - call ESMF_StateDestroy(importState, _RC) - call ESMF_StateDestroy(exportState, _RC) - call ESMF_FieldGet(importField, grid=grid, _RC) - call ESMF_FieldDestroy(importField, _RC) - call ESMF_FieldDestroy(exportField, _RC) - call ESMF_GridDestroy(grid, _RC) - call ESMF_ClockDestroy(clock, _RC) - _RETURN(_SUCCESS) - - end subroutine destroy_objects - - @Before - subroutine set_up() - integer :: status - - if(is_initialized()) return - call ESMF_Initialize(_RC) - - end subroutine set_up - end module Test_AccumulatorAction diff --git a/generic3g/tests/Test_MaxAction.pf b/generic3g/tests/Test_MaxAction.pf new file mode 100644 index 000000000000..37049a924820 --- /dev/null +++ b/generic3g/tests/Test_MaxAction.pf @@ -0,0 +1,45 @@ +#include "MAPL_TestErr.h" +module Test_MaxAction + use mapl3g_MaxAction + use accumulator_action_test_common + use esmf + use funit + use MAPL_FieldUtils + implicit none + +contains + + @Test + subroutine test_max_accumulate_R4() + type(MaxAction) :: acc + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer :: status + type(ESMF_Field) :: update_field + type(ESMF_TypeKind_Flag), parameter :: tk =ESMF_TYPEKIND_R4 + real(kind=ESMF_KIND_R4), pointer :: upPtr(:), accPtr(:) + real(kind=ESMF_KIND_R4), parameter :: UPDATE_VALUE = 1.0_R4, ACCUMULATED_VALUE = 3.0_R4 + real(kind=ESMF_KIND_R4) :: undef_value + real(kind=ESMF_KIND_R4), allocatable :: expected(:) + integer :: i, n + + ! Initialize + call set_undef(undef_value) + call initialize_objects(importState, exportState, clock, tk, _RC) + call acc%initialize(importState, exportState, clock, _RC) + call initialize_field(update_field, typekind=tk, _RC) + call assign_fptr(acc%accumulation_field, accPtr, _RC) + call assign_fptr(update_field, upPtr, _RC) + n = size(upPtr) + i = n - 3 + accPtr(i:n) = [undef_value, ACCUMULATED_VALUE, ACCUMULATED_VALUE, ACCUMULATED_VALUE] + upPtr(i:n) = [UPDATE_VALUE, undef_value, UPDATE_VALUE, UPDATE_VALUE+ACCUMULATED_VALUE] + expected = [UPDATE_VALUE, ACCUMULATED_VALUE, ACCUMULATED_VALUE, UPDATE_VALUE+ACCUMULATED_VALUE] + call acc%accumulate_R4(update_field, _RC) + @assertEqual(expected, accPtr, 'accumulated_field not equal to expected values') + call ESMF_FieldDestroy(update_field, _RC) + call destroy_objects(importState, exportState, clock, _RC) + + end subroutine test_max_accumulate_R4 + +end module Test_MaxAction diff --git a/generic3g/tests/Test_MeanAction.pf b/generic3g/tests/Test_MeanAction.pf new file mode 100644 index 000000000000..db44351f6bad --- /dev/null +++ b/generic3g/tests/Test_MeanAction.pf @@ -0,0 +1,205 @@ +#include "MAPL_TestErr.h" +module Test_MeanAction + + use mapl3g_MeanAction + use accumulator_action_test_common + use esmf + use funit + use MAPL_FieldUtils + implicit none + +contains + + @Test + subroutine test_calculate_mean_R4() + type(MeanAction) :: acc + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer :: status + integer(kind=ESMF_KIND_I8), parameter :: COUNTER = 4 + real(kind=ESMF_KIND_R4), parameter :: MEAN = 4.0_R4 + logical :: matches_expected + real(kind=ESMF_KIND_R4), pointer :: fptr(:) + integer :: n + logical, allocatable :: mask(:) + + call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) + call acc%initialize(importState, exportState, clock, _RC) + call FieldSet(acc%accumulation_field, COUNTER*MEAN, _RC) + acc%counter_scalar = COUNTER + + ! All points are not UNDEF and valid_mean .TRUE. + acc%valid_mean = .TRUE. + call acc%calculate_mean_R4(_RC) + matches_expected = FieldIsConstant(acc%accumulation_field, MEAN, _RC) + @assertTrue(matches_expected, 'accumulation_field not equal to MEAN') + + ! One point is UNDEF + call FieldSet(acc%accumulation_field, COUNTER*MEAN, _RC) + call assign_fptr(acc%accumulation_field, fptr, _RC) + n = size(fptr)-1 + call set_undef(fptr(n)) + allocate(mask(size(fptr))) + mask = .TRUE. + mask(n) = .FALSE. + call acc%calculate_mean_R4(_RC) + @assertTrue(all(pack(fptr, mask) == MEAN), 'Some valid points not equal to MEAN') + @assertTrue(undef(fptr(n)), 'mean at point was not UNDEF') + + ! valid_mean .FALSE. at one point + acc%valid_mean = .TRUE. + call FieldSet(acc%accumulation_field, COUNTER*MEAN, _RC) + acc%valid_mean(n) = .FALSE. + call acc%calculate_mean_R4(_RC) + @assertTrue(all(pack(fptr, acc%valid_mean) == MEAN), 'Some valid points not equal to MEAN') + @assertTrue(undef(fptr(n)), 'mean at point was not UNDEF') + + ! One point is UNDEF; valid_mean .FALSE. at one point + acc%valid_mean = .TRUE. + call FieldSet(acc%accumulation_field, COUNTER*MEAN, _RC) + acc%valid_mean(n) = .FALSE. + call assign_fptr(acc%accumulation_field, fptr, _RC) + call set_undef(fptr(n)) + mask = (.not. undef(fptr)) .and. acc%valid_mean + call acc%calculate_mean_R4(_RC) + @assertTrue(all(pack(fptr, mask) == MEAN), 'Some valid points not equal to MEAN') + @assertTrue(undef(fptr(n)), 'mean at point was not UNDEF') + call destroy_objects(importState, exportState, clock, _RC) + + end subroutine test_calculate_mean_R4 + + @Test + subroutine test_calculate_mean() + type(MeanAction) :: acc + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer :: status + integer(kind=ESMF_KIND_I8), parameter :: COUNTER = 4 + real(kind=ESMF_KIND_R4), parameter :: MEAN = 4.0_R4 + logical :: matches_expected + + call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) + call acc%initialize(importState, exportState, clock, _RC) + call FieldSet(acc%accumulation_field, COUNTER*MEAN, _RC) + acc%counter_scalar = 0_I8 + acc%valid_mean = .TRUE. + call acc%calculate_mean() + @assertExceptionRaised() + acc%counter_scalar = COUNTER + call acc%calculate_mean() + matches_expected = FieldIsConstant(acc%accumulation_field, MEAN, _RC) + @assertTrue(matches_expected, 'accumulation_field not equal to MEAN.') + call destroy_objects(importState, exportState, clock, _RC) + + end subroutine test_calculate_mean + + @Test + subroutine test_clear_accumulator() + type(MeanAction) :: acc + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer :: status + + call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) + call acc%initialize(importState, exportState, clock, _RC) + acc%counter_scalar = 4 + call acc%clear_accumulator(_RC) + @assertTrue(acc%counter_scalar == 0_I8, 'counter_scalar is nonzero.') + call destroy_objects(importState, exportState, clock, _RC) + + end subroutine test_clear_accumulator + + @Test + subroutine test_clear_valid_mean() + type(MeanAction) :: acc + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer :: status + + call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) + call acc%initialize(importState, exportState, clock, _RC) + acc%valid_mean = .TRUE. + call acc%clear_valid_mean(_RC) + @assertTrue(.not. any(acc%valid_mean), 'valid_mean .TRUE. in elements') + call destroy_objects(importState, exportState, clock, _RC) + + end subroutine test_clear_valid_mean + + @Test + subroutine test_invalidate() + type(MeanAction) :: acc + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer :: status + integer(kind=ESMF_KIND_I8), parameter :: N = 4_I8 + integer :: i + type(ESMF_Field) :: importField + + call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) + call get_field(importState, importField, _RC) + call FieldSet(importField, 1.0_R4, _RC) + call acc%initialize(importState, exportState, clock, _RC) + @assertTrue(acc%counter_scalar == 0_I8, 'counter_scalar is nonzero') + do i=1, N + call acc%invalidate(importState, exportState, clock, _RC) + end do + @assertTrue(acc%counter_scalar == N, 'counter_scalar not equal to N') + call destroy_objects(importState, exportState, clock, _RC) + + end subroutine test_invalidate + + subroutine test_accumulate_mean_R4() + type(MeanAction) :: acc + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer :: status + type(ESMF_Field) :: update_field + real(kind=ESMF_KIND_R4), pointer :: upPtr(:), accPtr(:) + real(kind=ESMF_KIND_R4), parameter :: IMPORT_VALUE = 2.0_R4 + real(kind=ESMF_KIND_R4), parameter :: UPDATE_VALUE = 3.0_R4 + real(kind=ESMF_KIND_R4) :: result_value = IMPORT_VALUE + integer :: n + type(ESMF_Field) :: importField + + call initialize_objects(importState, exportState, clock, ESMF_TYPEKIND_R4, _RC) + call get_field(importState, importField, _RC) + call FieldSet(importField, IMPORT_VALUE, _RC) + call acc%initialize(importState, exportState, clock, _RC) + call initialize_field(update_field, typekind=ESMF_TYPEKIND_R4, _RC) + call assign_fptr(update_field, upPtr, _RC) + upPtr = UPDATE_VALUE + + ! accumulated not undef, update_field not undef + call acc%accumulate_R4(update_field, _RC) + result_value = result_value + UPDATE_VALUE + call assign_fptr(acc%accumulation_field, accPtr, _RC) + @assertTrue(all(accPtr == result_value), 'accumulation_field not equal to expected value.') + + ! accumulated undef at point, update_field not undef + call assign_fptr(acc%accumulation_field, accPtr, _RC) + n = size(accPtr) - 1 + call set_undef(accPtr(n)) + call acc%accumulate_R4(update_field, _RC) + result_value = result_value + UPDATE_VALUE + @assertTrue(undef(accPtr(n)), 'invalid point is not UNDEF') + @assertTrue(all(pack(accPtr, .not. undef(accPtr)) == result_value), 'valid point not equal to expected value.') + + ! accumulated undef at point, update_field undef at point + n = size(upPtr) - 1 + call set_undef(upPtr(n)) + call acc%accumulate_R4(update_field, _RC) + result_value = result_value + UPDATE_VALUE + @assertTrue(undef(accPtr(n)), 'invalid point is not UNDEF') + + ! accumulated not undef, update_field undef at point + call FieldSet(importField, result_value, _RC) + call acc%initialize(importState, exportState, clock, _RC) + call acc%accumulate_R4(update_field, _RC) + result_value = result_value + UPDATE_VALUE + @assertTrue(undef(accPtr(n)), 'invalid point is not UNDEF') + @assertTrue(all(pack(accPtr, .not. undef(upPtr)) == result_value), 'valid point not equal to expected value.') + call destroy_objects(importState, exportState, clock, _RC) + + end subroutine test_accumulate_mean_R4 + +end module Test_MeanAction diff --git a/generic3g/tests/Test_MinAction.pf b/generic3g/tests/Test_MinAction.pf new file mode 100644 index 000000000000..0f9a3d151204 --- /dev/null +++ b/generic3g/tests/Test_MinAction.pf @@ -0,0 +1,45 @@ +#include "MAPL_TestErr.h" +module Test_MinAction + use mapl3g_MinAction + use accumulator_action_test_common + use esmf + use funit + use MAPL_FieldUtils + implicit none + +contains + + @Test + subroutine test_min_accumulate_R4() + type(MinAction) :: acc + type(ESMF_State) :: importState, exportState + type(ESMF_Clock) :: clock + integer :: status + type(ESMF_Field) :: update_field + type(ESMF_TypeKind_Flag), parameter :: tk =ESMF_TYPEKIND_R4 + real(kind=ESMF_KIND_R4), pointer :: upPtr(:), accPtr(:) + real(kind=ESMF_KIND_R4), parameter :: UPDATE_VALUE = 1.0_R4, ACCUMULATED_VALUE = 3.0_R4 + real(kind=ESMF_KIND_R4) :: undef_value + real(kind=ESMF_KIND_R4), allocatable :: expected(:) + integer :: i, n + + ! Initialize + call set_undef(undef_value) + call initialize_objects(importState, exportState, clock, tk, _RC) + call acc%initialize(importState, exportState, clock, _RC) + call initialize_field(update_field, typekind=tk, _RC) + call assign_fptr(acc%accumulation_field, accPtr, _RC) + call assign_fptr(update_field, upPtr, _RC) + n = size(upPtr) + i = n - 3 + accPtr(i:n) = [undef_value, ACCUMULATED_VALUE, ACCUMULATED_VALUE, ACCUMULATED_VALUE] + upPtr(i:n) = [UPDATE_VALUE, undef_value, UPDATE_VALUE, UPDATE_VALUE+ACCUMULATED_VALUE] + expected = [UPDATE_VALUE, ACCUMULATED_VALUE, UPDATE_VALUE, ACCUMULATED_VALUE] + call acc%accumulate_R4(update_field, _RC) + @assertEqual(expected, accPtr, 'accumulated_field not equal to expected values') + call ESMF_FieldDestroy(update_field, _RC) + call destroy_objects(importState, exportState, clock, _RC) + + end subroutine test_min_accumulate_R4 + +end module Test_MinAction diff --git a/generic3g/tests/accumulator_action_test_common.F90 b/generic3g/tests/accumulator_action_test_common.F90 new file mode 100644 index 000000000000..36b15c1ba1e7 --- /dev/null +++ b/generic3g/tests/accumulator_action_test_common.F90 @@ -0,0 +1,136 @@ +#define _RETURN_(R, S) if(present(R)) R = S; return +#define _RETURN(S) _RETURN_(rc, S) +#define _SUCCESS 0 +#include "MAPL_TestErr.h" +module accumulator_action_test_common + use esmf + use funit + use MAPL_FieldUtils + implicit none + + integer, parameter :: R4 = ESMF_KIND_R4 + integer, parameter :: R8 = ESMF_KIND_R8 + integer, parameter :: I8 = ESMF_KIND_I8 + integer(kind=ESMF_KIND_I4), parameter :: TIME_STEP = 1 + integer(kind=ESMF_KIND_I4), parameter :: START_TIME = 3000 + integer, parameter :: MAX_INDEX(2) = [4, 4] + real(kind=ESMF_KIND_R8), parameter :: MIN_CORNER_COORD(2) = [0.0_R8, 0.0_R8] + real(kind=ESMF_KIND_R8), parameter :: MAX_CORNER_COORD(2) = [4.0_R8, 4.0_R8] + type(ESMF_TypeKind_Flag), parameter :: typekind = ESMF_TYPEKIND_R4 + +contains + + logical function is_initialized(rc) result(lval) + integer, optional, intent(out) :: rc + integer :: status + + lval = ESMF_IsInitialized(_RC) + _RETURN(_SUCCESS) + + end function is_initialized + + elemental logical function undef(t) result(lval) + use MAPL_InternalConstantsMod, only: MAPL_UNDEFINED_REAL + real(kind=ESMF_KIND_R4), intent(in) :: t + + lval = t == MAPL_UNDEFINED_REAL + + end function undef + + elemental subroutine set_undef(t) + use MAPL_InternalConstantsMod, only: MAPL_UNDEFINED_REAL + real(kind=ESMF_KIND_R4), intent(inout) :: t + + t = MAPL_UNDEFINED_REAL + + end subroutine set_undef + + subroutine initialize_field(field, typekind, grid, rc) + type(ESMF_Field), intent(inout) :: field + type(ESMF_TypeKind_Flag), intent(in) :: typekind + type(ESMF_Grid), optional, intent(inout) :: grid + integer, optional, intent(out) :: rc + type(ESMF_Grid) :: grid_ + logical :: grid_created + + integer :: status + + grid_created = .FALSE. + if(present(grid)) then + grid_created = ESMF_GridIsCreated(grid, _RC) + if(grid_created) grid_ = grid + end if + + if(.not. grid_created) then + grid_ = ESMF_GridCreateNoPeriDimUfrm(maxIndex=MAX_INDEX, & + & minCornerCoord=MIN_CORNER_COORD, maxCornerCoord=MAX_CORNER_COORD, _RC) + end if + + field = ESMF_FieldCreate(grid=grid_, typekind=typekind, _RC) + + if(present(grid)) grid = grid_ + _RETURN(_SUCCESS) + + end subroutine initialize_field + + subroutine initialize_objects(importState, exportState, clock, typekind, rc) + type(ESMF_State), intent(inout) :: importState, exportState + type(ESMF_Clock), intent(inout) :: clock + type(ESMF_TypeKind_Flag), intent(in) :: typekind + integer, optional, intent(out) :: rc + + integer :: status + type(ESMF_Field) :: importField, exportField + type(ESMF_Time) :: startTime + type(ESMF_TimeInterval) :: timeStep + type(ESMF_Grid) :: grid + + call ESMF_TimeIntervalSet(timeStep, s=TIME_STEP, _RC) + call ESMF_TimeSet(startTime, yy=START_TIME, _RC) + clock = ESMF_ClockCreate(timeStep=timeStep, startTime=startTime, _RC) + grid = ESMF_GridCreateNoPeriDimUfrm(maxIndex=MAX_INDEX, minCornerCoord=MIN_CORNER_COORD, maxCornerCoord=MAX_CORNER_COORD, _RC) + importField = ESMF_FieldCreate(grid=grid, typekind=typekind, _RC) + exportField = ESMF_FieldCreate(grid=grid, typekind=typekind, _RC) + importState = ESMF_StateCreate(stateIntent=ESMF_STATEINTENT_IMPORT, fieldList=[importField], name='import', _RC) + exportState = ESMF_StateCreate(stateIntent=ESMF_STATEINTENT_EXPORT, fieldList=[exportField], name='export', _RC) + _RETURN(_SUCCESS) + + end subroutine initialize_objects + + subroutine get_field(state, field, rc) + type(ESMF_State), intent(inout) :: state + type(ESMF_Field), intent(inout) :: field + integer, optional, intent(out) :: rc + + integer :: status + character(len=ESMF_MAXSTR) :: itemNameList(1) + + call ESMF_StateGet(state, itemNameList=itemNameList, _RC) + call ESMF_StateGet(state, itemName=itemNameList(1), field=field, _RC) + _RETURN(_SUCCESS) + + end subroutine get_field + + subroutine destroy_objects(importState, exportState, clock, rc) + type(ESMF_State), intent(inout) :: importState, exportState + type(ESMF_Clock), intent(inout) :: clock + integer, optional, intent(out) :: rc + + integer :: status + type(ESMF_Field) :: importField, exportField + type(ESMF_Grid) :: grid + + call get_field(importState, importField, _RC) + call get_field(exportState, exportField, _RC) + call ESMF_StateDestroy(importState, _RC) + call ESMF_StateDestroy(exportState, _RC) + call ESMF_FieldGet(importField, grid=grid, _RC) + call ESMF_FieldDestroy(importField, _RC) + call ESMF_FieldDestroy(exportField, _RC) + call ESMF_GridDestroy(grid, _RC) + call ESMF_ClockDestroy(clock, _RC) + _RETURN(_SUCCESS) + + end subroutine destroy_objects + +end module accumulator_action_test_common