Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/wdboggs/3422 ref time to ref offset #3439

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 38 additions & 83 deletions esmf_utils/ESMF_Time_Utilities.F90
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ module mapl3g_ESMF_Time_Utilities
private

public :: zero_time_interval
public :: intervals_are_compatible
public :: times_and_intervals_are_compatible
public :: intervals_and_offset_are_compatible

interface zero_time_interval
module procedure :: get_zero
Expand All @@ -23,71 +22,51 @@ module mapl3g_ESMF_Time_Utilities

contains

! must be possible to compare intervals, based on their nonzero units
! smaller interval must divide the larger interval evenly
! assumes they have the same sign.
subroutine intervals_are_compatible(larger, smaller, compatible, rc)
type(ESMF_TimeInterval), intent(in) :: larger
type(ESMF_TimeInterval), intent(in) :: smaller
logical, intent(out) :: compatible
integer, optional, intent(out) :: rc
integer :: status

_ASSERT(smaller /= get_zero(), 'The smaller unit must be nonzero.')
associate(abs_larger => ESMF_TimeIntervalAbsValue(larger), abs_smaller => ESMF_TimeIntervalAbsValue(smaller))
compatible = abs_larger >= abs_smaller
_RETURN_UNLESS(compatible)
call can_compare_intervals(larger, smaller, compatible, _RC)
_RETURN_UNLESS(compatible)
compatible = mod(abs_larger, abs_smaller) == get_zero()
end associate

_RETURN(_SUCCESS)

end subroutine intervals_are_compatible

! intervals must be comparable, abs(interval1) >= abs(interval2)
! abs(interval2) must evenly divide absolute difference of times
subroutine times_and_intervals_are_compatible(interval1, time1, interval2, time2, compatible, rc)
type(ESMF_Time), intent(in) :: time1
type(ESMF_Time), intent(in) :: time2
type(ESMF_TimeInterval), intent(in) :: interval1
subroutine intervals_and_offset_are_compatible(interval, interval2, offset, compatible, rc)
type(ESMF_TimeInterval), intent(in) :: interval
type(ESMF_TimeInterval), intent(in) :: interval2
type(ESMF_TimeInterval), optional, intent(in) :: offset
logical, intent(out) :: compatible
integer, optional, intent(inout) :: rc
integer :: status
type(ESMF_TimeInterval) :: absdiff

call intervals_are_compatible(interval1, interval2, compatible, _RC)
_RETURN_UNLESS(compatible)
absdiff = ESMF_TimeIntervalAbsValue(time1 - time2)
compatible = mod(absdiff, ESMF_TimeIntervalAbsValue(interval2)) == get_zero()
type(ESMF_TimeInterval), pointer :: zero => null()
integer(kind=I4) :: units(NUM_INTERVAL_UNITS), units2(NUM_INTERVAL_UNITS)

compatible = .FALSE.
zero => zero_time_interval()
_ASSERT(interval2 /= zero, 'The second interval must be nonzero.')
units = to_array(interval, _RC)
units2 = to_array(interval2, _RC)
_RETURN_IF(cannot_compare(units == 0, units2 == 0))
associate(abs1 => ESMF_TimeIntervalAbsValue(interval), &
& abs2 => ESMF_TimeIntervalAbsValue(interval2))
_RETURN_IF(abs1 < abs2 .or. mod(abs1, abs2) /= zero)
compatible = abs1 >= abs2 .and. mod(abs1, abs2) == zero
_RETURN_UNLESS(present(offset))
compatible = compatible .and. mod(ESMF_TimeIntervalAbsValue(offset), abs2) == zero
end associate
_RETURN(_SUCCESS)

end subroutine times_and_intervals_are_compatible

! These combinations (larger, smaller): (yy and/or mm, d), (yy and/or mm, h),
! (yy and/or mm, m), and (yy and/or mm, s) do not work because the
! ESMF_TimeInterval overload of the mod function gives incorrect results for
! these combinations. Presumably ms, us, and ns for the smaller interval do
! not work.
subroutine can_compare_intervals(larger, smaller, comparable, rc)
type(ESMF_TimeInterval), intent(in) :: larger
type(ESMF_TimeInterval), intent(in) :: smaller
logical, intent(out) :: comparable
integer, optional, intent(out) :: rc
integer :: status
contains

comparable = has_only_years_and_months(larger, _RC)
comparable = comparable .and. has_only_years_and_months(smaller, _RC)
_RETURN_IF(comparable)
! These combinations (larger, smaller): (yy and/or mm, d), (yy and/or mm, h),
! (yy and/or mm, m), and (yy and/or mm, s) do not work because the
! ESMF_TimeInterval overload of the mod function gives incorrect results for
! these combinations. Presumably ms, us, and ns for the smaller interval do
! not work.

comparable = has_no_years_or_months(larger, _RC)
comparable = comparable .and. has_no_years_or_months(smaller, _RC)
_RETURN(_SUCCESS)
logical function cannot_compare(z, z2)
logical, intent(in) :: z(:), z2(:)
integer, parameter :: MONTH = 2

cannot_compare = any(z .neqv. z2) .or. .not. (all(z(:MONTH)) .or. all(z(MONTH+1:)))

end function cannot_compare

end subroutine intervals_and_offset_are_compatible

end subroutine can_compare_intervals

function get_zero() result(zero)
type(ESMF_TimeInterval), pointer :: zero
logical, save :: zero_is_uninitialized = .TRUE.
Expand All @@ -100,40 +79,16 @@ function get_zero() result(zero)

end function get_zero

subroutine as_array(interval, units, rc)
function to_array(interval, rc) result(units)
integer(kind=I4) :: units(NUM_INTERVAL_UNITS)
type(ESMF_TimeInterval), intent(in) :: interval
integer(kind=I4), intent(out) :: units(NUM_INTERVAL_UNITS)
integer, optional, intent(out) :: rc
integer :: status

call ESMF_TimeIntervalGet(interval, yy=units(1), mm=units(2), d=units(3), &
& h=units(4), m=units(5), s=units(6), ms=units(7), us=units(8), ns=units(9), _RC)
_RETURN(_SUCCESS)

end subroutine as_array

logical function has_only_years_and_months(interval, rc)
type(ESMF_TimeInterval), intent(in) :: interval
integer, optional, intent(out) :: rc
integer :: status
integer(kind=I4) :: units(NUM_INTERVAL_UNITS)

call as_array(interval, units, _RC)
has_only_years_and_months = all(units(3:) == 0)
_RETURN(_SUCCESS)

end function has_only_years_and_months

logical function has_no_years_or_months(interval, rc)
type(ESMF_TimeInterval), intent(in) :: interval
integer, optional, intent(out) :: rc
integer :: status
integer(kind=I4) :: units(NUM_INTERVAL_UNITS)

call as_array(interval, units, _RC)
has_no_years_or_months = all(units(1:2) == 0)
_RETURN(_SUCCESS)

end function has_no_years_or_months
end function to_array

end module mapl3g_ESMF_Time_Utilities
172 changes: 86 additions & 86 deletions esmf_utils/tests/Test_ESMF_Time_Utilities.pf
Original file line number Diff line number Diff line change
Expand Up @@ -22,91 +22,91 @@ contains

end subroutine test_get_zero

@Test
subroutine test_intervals_are_compatible()
type(ESMF_TimeInterval) :: larger
type(ESMF_TimeInterval) :: smaller
integer(kind=ESMF_KIND_I4), parameter :: YY = 3
integer(kind=ESMF_KIND_I4), parameter :: MM = 3
integer(kind=ESMF_KIND_I4), parameter :: DD = 3
integer(kind=ESMF_KIND_I4), parameter :: H = 3
logical :: compatible
integer :: status

call ESMF_TimeIntervalSet(larger, d=3*DD, _RC)
call ESMF_TimeIntervalSet(smaller, d=DD, _RC)
call intervals_are_compatible(larger, smaller, compatible, _RC)
@assertTrue(compatible, 'The intervals are compatible.')

call intervals_are_compatible(smaller, larger, compatible, _RC)
@assertFalse(compatible, 'The larger unit must come first.')

call ESMF_TimeIntervalSet(smaller, d=2*DD, _RC)
call intervals_are_compatible(larger, smaller, compatible, _RC)
@assertFalse(compatible, 'The smaller interval does not divide the larger interval evenly.')

end subroutine test_intervals_are_compatible

@Test
subroutine test_times_and_intervals_are_compatible()
type(ESMF_TimeInterval) :: larger
type(ESMF_TimeInterval) :: smaller
type(ESMF_Time) :: time1
type(ESMF_Time) :: time2
logical :: compatible
integer :: status

call ESMF_TimeSet(time1, yy=1582, mm=10, dd=16, h=7, _RC)
call ESMF_TimeSet(time2, yy=1582, mm=10, dd=15, h=19, _RC)
call ESMF_TimeIntervalSet(larger, d=1, _RC)
call ESMF_TimeIntervalSet(smaller, h = 6, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertTrue(compatible, 'The times and intervals are compatible.')

call ESMF_TimeSet(time2, yy=1582, mm=10, dd=15, h=18, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertFalse(compatible, 'The time difference is not evenly divisible by the smaller interval.')

call ESMF_TimeSet(time1, yy=1582, mm=10, dd=16, h=18, _RC)
call ESMF_TimeIntervalSet(larger, h=6, _RC)
call ESMF_TimeIntervalSet(smaller, h=4, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertFalse(compatible, 'The larger interval is not evenly divisible by the smaller interval.')

call ESMF_TimeIntervalSet(larger, mm=1, _RC)
call ESMF_TimeIntervalSet(smaller, d=1, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertFalse(compatible, 'Larger interval cannot include months.')

call ESMF_TimeIntervalSet(larger, d=90, _RC)
call ESMF_TimeIntervalSet(smaller, mm=1, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertFalse(compatible, 'Smaller interval cannot include months.')

call ESMF_TimeIntervalSet(larger, yy=1, _RC)
call ESMF_TimeIntervalSet(smaller, d=1, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertFalse(compatible, 'Larger interval cannot include years.')

call ESMF_TimeIntervalSet(larger, d=365, _RC)
call ESMF_TimeIntervalSet(smaller, yy=1, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertFalse(compatible, 'Smaller interval cannot include years.')

call ESMF_TimeSet(time1, yy=1582, mm=10, _RC)
call ESMF_TimeSet(time2, yy=1583, mm=10, _RC)
call ESMF_TimeIntervalSet(larger, yy=3, _RC)
call ESMF_TimeIntervalSet(smaller, yy=1, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertTrue(compatible, 'The intervals are compatible.')

call ESMF_TimeSet(time1, yy=1582, mm=10, _RC)
call ESMF_TimeSet(time2, yy=1583, mm=10, _RC)
call ESMF_TimeIntervalSet(larger, mm=3, _RC)
call ESMF_TimeIntervalSet(smaller, mm=1, _RC)
call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
@assertTrue(compatible, 'The intervals are compatible.')

end subroutine test_times_and_intervals_are_compatible
! @Test
! subroutine test_intervals_are_compatible()
! type(ESMF_TimeInterval) :: larger
! type(ESMF_TimeInterval) :: smaller
! integer(kind=ESMF_KIND_I4), parameter :: YY = 3
! integer(kind=ESMF_KIND_I4), parameter :: MM = 3
! integer(kind=ESMF_KIND_I4), parameter :: DD = 3
! integer(kind=ESMF_KIND_I4), parameter :: H = 3
! logical :: compatible
! integer :: status
!
! call ESMF_TimeIntervalSet(larger, d=3*DD, _RC)
! call ESMF_TimeIntervalSet(smaller, d=DD, _RC)
! call intervals_are_compatible(larger, smaller, compatible, _RC)
! @assertTrue(compatible, 'The intervals are compatible.')
!
! call intervals_are_compatible(smaller, larger, compatible, _RC)
! @assertFalse(compatible, 'The larger unit must come first.')
!
! call ESMF_TimeIntervalSet(smaller, d=2*DD, _RC)
! call intervals_are_compatible(larger, smaller, compatible, _RC)
! @assertFalse(compatible, 'The smaller interval does not divide the larger interval evenly.')
!
! end subroutine test_intervals_are_compatible

! @Test
! subroutine test_times_and_intervals_are_compatible()
! type(ESMF_TimeInterval) :: larger
! type(ESMF_TimeInterval) :: smaller
! type(ESMF_Time) :: time1
! type(ESMF_Time) :: time2
! logical :: compatible
! integer :: status
!
! call ESMF_TimeSet(time1, yy=1582, mm=10, dd=16, h=7, _RC)
! call ESMF_TimeSet(time2, yy=1582, mm=10, dd=15, h=19, _RC)
! call ESMF_TimeIntervalSet(larger, d=1, _RC)
! call ESMF_TimeIntervalSet(smaller, h = 6, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertTrue(compatible, 'The times and intervals are compatible.')
!
! call ESMF_TimeSet(time2, yy=1582, mm=10, dd=15, h=18, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertFalse(compatible, 'The time difference is not evenly divisible by the smaller interval.')
!
! call ESMF_TimeSet(time1, yy=1582, mm=10, dd=16, h=18, _RC)
! call ESMF_TimeIntervalSet(larger, h=6, _RC)
! call ESMF_TimeIntervalSet(smaller, h=4, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertFalse(compatible, 'The larger interval is not evenly divisible by the smaller interval.')
!
! call ESMF_TimeIntervalSet(larger, mm=1, _RC)
! call ESMF_TimeIntervalSet(smaller, d=1, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertFalse(compatible, 'Larger interval cannot include months.')
!
! call ESMF_TimeIntervalSet(larger, d=90, _RC)
! call ESMF_TimeIntervalSet(smaller, mm=1, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertFalse(compatible, 'Smaller interval cannot include months.')
!
! call ESMF_TimeIntervalSet(larger, yy=1, _RC)
! call ESMF_TimeIntervalSet(smaller, d=1, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertFalse(compatible, 'Larger interval cannot include years.')
!
! call ESMF_TimeIntervalSet(larger, d=365, _RC)
! call ESMF_TimeIntervalSet(smaller, yy=1, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertFalse(compatible, 'Smaller interval cannot include years.')
!
! call ESMF_TimeSet(time1, yy=1582, mm=10, _RC)
! call ESMF_TimeSet(time2, yy=1583, mm=10, _RC)
! call ESMF_TimeIntervalSet(larger, yy=3, _RC)
! call ESMF_TimeIntervalSet(smaller, yy=1, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertTrue(compatible, 'The intervals are compatible.')
!
! call ESMF_TimeSet(time1, yy=1582, mm=10, _RC)
! call ESMF_TimeSet(time2, yy=1583, mm=10, _RC)
! call ESMF_TimeIntervalSet(larger, mm=3, _RC)
! call ESMF_TimeIntervalSet(smaller, mm=1, _RC)
! call times_and_intervals_are_compatible(larger, time1, smaller, time2, compatible, _RC)
! @assertTrue(compatible, 'The intervals are compatible.')
!
! end subroutine test_times_and_intervals_are_compatible

end module Test_ESMF_Time_Utilities
14 changes: 7 additions & 7 deletions generic3g/ComponentSpecParser.F90
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,17 @@ module mapl3g_ComponentSpecParser
character(*), parameter :: KEY_VERTICAL_DIM_SPEC = 'vertical_dim_spec'
character(*), parameter :: KEY_ACCUMULATION_TYPE = 'accumulation_type'
character(*), parameter :: KEY_TIMESTEP = 'timestep'
character(*), parameter :: KEY_REFERENCE_TIME = 'reference_time'
character(*), parameter :: KEY_REFERENCE_TIME_OFFSET = 'reference_time_offset'

!>
! Submodule declarations
INTERFACE
module function parse_component_spec(hconfig, registry, timeStep, refTime, rc) result(spec)
module function parse_component_spec(hconfig, registry, timeStep, refTime_offset, rc) result(spec)
type(ComponentSpec) :: spec
type(ESMF_HConfig), target, intent(inout) :: hconfig
type(StateRegistry), target, intent(in) :: registry
type(ESMF_TimeInterval), optional, intent(in) :: timeStep
type(ESMF_Time), optional, intent(in) :: refTime
type(ESMF_TimeInterval), optional, intent(in) :: refTime_offset
integer, optional, intent(out) :: rc
end function parse_component_spec

Expand All @@ -85,11 +85,11 @@ module function parse_geometry_spec(mapl_cfg, registry, rc) result(geometry_spec
integer, optional, intent(out) :: rc
end function parse_geometry_spec

module function parse_var_specs(hconfig, timeStep, refTime, rc) result(var_specs)
module function parse_var_specs(hconfig, timeStep, refTime_offset, rc) result(var_specs)
type(VariableSpecVector) :: var_specs
type(ESMF_HConfig), intent(in) :: hconfig
type(ESMF_TimeInterval), optional, intent(in) :: timeStep
type(ESMF_Time), optional, intent(in) :: refTime
type(ESMF_TimeInterval), optional, intent(in) :: refTime_offset
integer, optional, intent(out) :: rc
end function parse_var_specs

Expand Down Expand Up @@ -117,10 +117,10 @@ module function parse_child(hconfig, rc) result(child)
integer, optional, intent(out) :: rc
end function parse_child

module subroutine parse_timespec(hconfig, timeStep, refTime, rc)
module subroutine parse_timespec(hconfig, timeStep, refTime_offset, rc)
type(ESMF_HConfig), intent(in) :: hconfig
type(ESMF_TimeInterval), allocatable, intent(out) :: timeStep
type(ESMF_Time), allocatable, intent(out) :: refTime
type(ESMF_TimeInterval), allocatable, intent(out) :: refTime_offset
integer, optional, intent(out) :: rc
end subroutine parse_timespec

Expand Down
Loading
Loading