ExtDataUpdatePointer.F90 Source File


This file depends on

sourcefile~~extdataupdatepointer.f90~~EfferentGraph sourcefile~extdataupdatepointer.f90 ExtDataUpdatePointer.F90 sourcefile~keywordenforcer.f90 KeywordEnforcer.F90 sourcefile~extdataupdatepointer.f90->sourcefile~keywordenforcer.f90 sourcefile~mapl_comms.f90 MAPL_Comms.F90 sourcefile~extdataupdatepointer.f90->sourcefile~mapl_comms.f90 sourcefile~mapl_exceptionhandling.f90 MAPL_ExceptionHandling.F90 sourcefile~extdataupdatepointer.f90->sourcefile~mapl_exceptionhandling.f90 sourcefile~timestringconversion.f90 TimeStringConversion.F90 sourcefile~extdataupdatepointer.f90->sourcefile~timestringconversion.f90 sourcefile~mapl_comms.f90->sourcefile~mapl_exceptionhandling.f90 sourcefile~base_base.f90 Base_Base.F90 sourcefile~mapl_comms.f90->sourcefile~base_base.f90 sourcefile~constants.f90 Constants.F90 sourcefile~mapl_comms.f90->sourcefile~constants.f90 sourcefile~shmem.f90 Shmem.F90 sourcefile~mapl_comms.f90->sourcefile~shmem.f90 sourcefile~errorhandling.f90 ErrorHandling.F90 sourcefile~mapl_exceptionhandling.f90->sourcefile~errorhandling.f90 sourcefile~mapl_throw.f90 MAPL_Throw.F90 sourcefile~mapl_exceptionhandling.f90->sourcefile~mapl_throw.f90 sourcefile~timestringconversion.f90->sourcefile~keywordenforcer.f90 sourcefile~timestringconversion.f90->sourcefile~mapl_exceptionhandling.f90 sourcefile~base_base.f90->sourcefile~keywordenforcer.f90 sourcefile~base_base.f90->sourcefile~constants.f90 sourcefile~mapl_range.f90 MAPL_Range.F90 sourcefile~base_base.f90->sourcefile~mapl_range.f90 sourcefile~maplgrid.f90 MaplGrid.F90 sourcefile~base_base.f90->sourcefile~maplgrid.f90 sourcefile~internalconstants.f90 InternalConstants.F90 sourcefile~constants.f90->sourcefile~internalconstants.f90 sourcefile~mathconstants.f90 MathConstants.F90 sourcefile~constants.f90->sourcefile~mathconstants.f90 sourcefile~physicalconstants.f90 PhysicalConstants.F90 sourcefile~constants.f90->sourcefile~physicalconstants.f90 sourcefile~errorhandling.f90->sourcefile~mapl_throw.f90 sourcefile~shmem.f90->sourcefile~constants.f90 sourcefile~mapl_range.f90->sourcefile~mapl_exceptionhandling.f90 sourcefile~maplgrid.f90->sourcefile~keywordenforcer.f90 sourcefile~maplgrid.f90->sourcefile~constants.f90 sourcefile~maplgrid.f90->sourcefile~errorhandling.f90 sourcefile~mapl_sort.f90 MAPL_Sort.F90 sourcefile~maplgrid.f90->sourcefile~mapl_sort.f90 sourcefile~pflogger_stub.f90 pflogger_stub.F90 sourcefile~maplgrid.f90->sourcefile~pflogger_stub.f90 sourcefile~physicalconstants.f90->sourcefile~mathconstants.f90

Files dependent on this one

sourcefile~~extdataupdatepointer.f90~~AfferentGraph sourcefile~extdataupdatepointer.f90 ExtDataUpdatePointer.F90 sourcefile~extdatatypedef.f90 ExtDataTypeDef.F90 sourcefile~extdatatypedef.f90->sourcefile~extdataupdatepointer.f90 sourcefile~test_extdataupdatepointer.pf Test_ExtDataUpdatePointer.pf sourcefile~test_extdataupdatepointer.pf->sourcefile~extdataupdatepointer.f90 sourcefile~extdataderivedexportvector.f90 ExtDataDerivedExportVector.F90 sourcefile~extdataderivedexportvector.f90->sourcefile~extdatatypedef.f90 sourcefile~extdatagridcompng.f90 ExtDataGridCompNG.F90 sourcefile~extdatagridcompng.f90->sourcefile~extdatatypedef.f90 sourcefile~extdatagridcompng.f90->sourcefile~extdataderivedexportvector.f90 sourcefile~extdataoldtypescreator.f90 ExtDataOldTypesCreator.F90 sourcefile~extdatagridcompng.f90->sourcefile~extdataoldtypescreator.f90 sourcefile~extdataprimaryexportvector.f90 ExtDataPrimaryExportVector.F90 sourcefile~extdatagridcompng.f90->sourcefile~extdataprimaryexportvector.f90 sourcefile~extdataoldtypescreator.f90->sourcefile~extdatatypedef.f90 sourcefile~extdataprimaryexportvector.f90->sourcefile~extdatatypedef.f90 sourcefile~extdatadrivergridcomp.f90 ExtDataDriverGridComp.F90 sourcefile~extdatadrivergridcomp.f90->sourcefile~extdatagridcompng.f90 sourcefile~mapl_capgridcomp.f90 MAPL_CapGridComp.F90 sourcefile~mapl_capgridcomp.f90->sourcefile~extdatagridcompng.f90 sourcefile~comp_testing_driver.f90 Comp_Testing_Driver.F90 sourcefile~comp_testing_driver.f90->sourcefile~mapl_capgridcomp.f90 sourcefile~extdatadriver.f90 ExtDataDriver.F90 sourcefile~extdatadriver.f90->sourcefile~extdatadrivergridcomp.f90 sourcefile~extdatadrivermod.f90 ExtDataDriverMod.F90 sourcefile~extdatadrivermod.f90->sourcefile~extdatadrivergridcomp.f90 sourcefile~mapl_cap.f90 MAPL_Cap.F90 sourcefile~mapl_cap.f90->sourcefile~mapl_capgridcomp.f90

Source Code

#include "MAPL_Exceptions.h"
#include "MAPL_ErrLog.h"

module MAPL_ExtDataPointerUpdate
   use ESMF
   use MAPL_KeywordEnforcerMod
   use MAPL_ExceptionHandling
   use MAPL_TimeStringConversion
   use MAPL_CommsMod
   implicit none
   private

   public :: ExtDataPointerUpdate
   public :: HEARTBEAT_STRING

   type :: ExtDataPointerUpdate
      private
      logical :: disabled = .false.
      logical :: first_time_updated = .true.
      type(ESMF_TimeInterval) :: offset
      logical :: single_shot = .false.
      type(ESMF_TimeInterval) :: update_freq
      type(ESMF_Time) :: last_ring
      type(ESMF_Time) :: reference_time
      logical :: simple_alarm_created = .false.
      type(ESMF_TIme) :: last_checked
      contains
         procedure :: create_from_parameters
         procedure :: check_update
         procedure :: is_disabled
         procedure :: is_single_shot
         procedure :: disable
         procedure :: get_adjusted_time
         procedure :: get_offset
   end type

   character(len=*), parameter :: HEARTBEAT_STRING = 'HEARTBEAT'
   
contains

   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
      integer, optional, intent(out) :: rc

      adjusted_time = time+this%offset

      _RETURN(_SUCCESS)
   end function

   function get_offset(this) result(offset)
      type(ESMF_TimeInterval) :: offset
      class(ExtDataPointerUpdate), intent(in) :: this

      offset = this%offset

   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
      character(len=*), intent(in) :: update_freq
      character(len=*), intent(in) :: update_offset
      type(ESMF_Time), intent(inout) :: time
      type(ESMF_Clock), intent(inout) :: clock
      integer, optional, intent(out) :: rc

      integer :: status,int_time,year,month,day,hour,minute,second
      logical :: negative_offset
      type(ESMF_TimeInterval) :: timestep
      integer :: multiplier
      integer :: i, j
      logical :: is_heartbeat

      this%last_checked = time
      call ESMF_ClockGet(clock, timestep=timestep, _RC)
      if (update_freq == "-") then
         this%single_shot = .true.
      else if (update_freq /= "PT0S") then
         this%simple_alarm_created = .true.
         int_time = string_to_integer_time(update_time)
         hour=int_time/10000
         minute=mod(int_time/100,100)
         second=mod(int_time,100)
         call ESMF_TimeGet(time,yy=year,mm=month,dd=day,_RC)
         call ESMF_TimeSet(this%reference_time,yy=year,mm=month,dd=day,h=hour,m=minute,s=second,_RC)
         this%last_ring = this%reference_time
         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
      else
         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 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
      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.

   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(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
      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
      logical, intent(out) :: do_update
      type(ESMF_Time), intent(inout) :: use_time
      type(ESMF_Time), intent(inout) :: current_time
      logical, intent(in) :: first_time
      integer, optional, intent(out) :: rc
      type(ESMF_Time) :: next_ring

      if (this%disabled) then
         do_update = .false.
         _RETURN(_SUCCESS)
      end if
      if (this%simple_alarm_created) then
         use_time = this%get_adjusted_time(current_time)
         if (first_time) then
            do_update = .true.
            this%first_time_updated = .true.
            use_time = this%get_adjusted_time(this%last_ring)
         else
            ! normal flow
            next_ring = this%last_ring
            if (current_time > this%last_checked) then
               do while (next_ring < current_time)
                  next_ring=next_ring+this%update_freq
               enddo
               if (current_time == next_ring) then
                  do_update = .true.
                  this%last_ring = next_ring
                  this%first_time_updated = .false.
               end if
            ! if clock went backwards, so we must update, set ringtime to previous ring from working time
            else if (current_time < this%last_checked) then
               next_ring = this%last_ring
               ! the clock must have rewound past last ring
               if (this%last_ring > current_time) then
                  do while(next_ring >= current_time)
                     next_ring=next_ring-this%update_freq
                  enddo
                  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
               else if (this%last_ring < current_time) then
                    if (this%first_time_updated) then
                       do_update=.true.
                       this%first_time_updated = .false.
                       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
                  do_update =.true.
                  this%first_time_updated = .false.
                  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 = this%get_adjusted_time(current_time)
      end if
      this%last_checked = current_time

   end subroutine check_update

   function is_disabled(this) result(disabled)
      class(ExtDataPointerUpdate), intent(in) :: this
      logical :: disabled
      disabled = this%disabled
   end function is_disabled

   function is_single_shot(this) result(single_shot)
      class(ExtDataPointerUpdate), intent(in) :: this
      logical :: single_shot
      single_shot = this%single_shot
   end function is_single_shot

   subroutine disable(this)
      class(ExtDataPointerUpdate), intent(inout) :: this
      this%disabled = .true.
   end subroutine

end module MAPL_ExtDataPointerUpdate