module cam_history_buffers use shr_kind_mod, only: r8 => shr_kind_r8 use cam_history_support, only: max_chars, field_info, hentry, dim_index_2d use cam_abortutils, only: endrun use pio, only: var_desc_t implicit none contains subroutine hbuf_accum_inst (buf8, field, nacs, dimind, idim, flag_xyfill, fillvalue) ! !----------------------------------------------------------------------- ! ! Purpose: Accumulate instantaneous values of field in 2-D hbuf. ! Set accumulation counter to 1. ! !----------------------------------------------------------------------- ! real(r8), pointer :: buf8(:,:) ! 2-D history buffer integer, pointer :: nacs(:) ! accumulation counter integer, intent(in) :: idim ! Longitude dimension of field array logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue real(r8), intent(in ) :: field(idim,*) ! real*8 array type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index real(r8), intent(in) :: fillvalue ! ! Local indices ! integer :: ieu, jeu ! number of elements in each dimension integer :: i, k ! loop indices logical :: bad ! flag indicates input field fillvalues not applied consistently ! with vertical level call dimind%dim_sizes(ieu, jeu) do k=1,jeu do i=1,ieu buf8(i,k) = field(i,k) end do end do if (flag_xyfill) then do i=1,ieu if (field(i,1) == fillvalue) then nacs(i) = 0 else nacs(i) = 1 end if end do else nacs(1) = 1 end if return end subroutine hbuf_accum_inst !####################################################################### subroutine hbuf_accum_add (buf8, field, nacs, dimind, idim, flag_xyfill, fillvalue) ! !----------------------------------------------------------------------- ! ! Purpose: Add the values of field to 2-D hbuf. ! Increment accumulation counter by 1. ! !----------------------------------------------------------------------- ! real(r8), pointer :: buf8(:,:) ! 2-D history buffer integer, pointer :: nacs(:) ! accumulation counter integer, intent(in) :: idim ! Longitude dimension of field array logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue real(r8), intent(in ) :: field(idim,*) ! real*8 array type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index real(r8), intent(in) :: fillvalue ! ! Local indices ! integer :: ieu, jeu ! number of elements in each dimension integer :: i,k ! indices call dimind%dim_sizes(ieu, jeu) if (flag_xyfill) then do k=1,jeu do i=1,ieu if (field(i,k) /= fillvalue) then buf8(i,k) = buf8(i,k) + field(i,k) end if end do end do ! ! Ensure input field has fillvalue defined invariant in the z-direction, then increment nacs ! call check_accum (field, idim, ieu, jeu, fillvalue) do i=1,ieu if (field(i,1) /= fillvalue) then nacs(i) = nacs(i) + 1 end if end do else do k=1,jeu do i=1,ieu buf8(i,k) = buf8(i,k) + field(i,k) end do end do nacs(1) = nacs(1) + 1 end if return end subroutine hbuf_accum_add !####################################################################### subroutine hbuf_accum_variance (hbuf, sbuf, field, nacs, dimind, idim, flag_xyfill, fillvalue) ! !----------------------------------------------------------------------- ! ! Purpose: accumulate a running variance for standard deviations ! ! The method of computing variance is from http://www.johndcook.com/blog/standard_deviation/ ! and ! 1962 paper by B. P. Welford and is presented in Donald Knuth's Art of ! Computer Programming, Vol 2, page 232, 3rd edition. !----------------------------------------------------------------------- ! real(r8), pointer :: hbuf(:,:) ! 2-D history buffer real(r8), pointer :: sbuf(:,:) ! 2-D history buffer integer, pointer :: nacs(:) ! accumulation counter integer, intent(in) :: idim ! Longitude dimension of field array logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue real(r8), intent(in) :: field(idim,*) ! real*8 array type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index real(r8), intent(in) :: fillvalue ! ! Local indices ! integer :: ieu, jeu ! number of elements in each dimension integer :: i,k ! indices real(r8) :: tmp call dimind%dim_sizes(ieu, jeu) if (flag_xyfill) then ! ! Ensure input field has fillvalue defined invariant in the z-direction, then increment nacs ! call check_accum (field, idim, ieu, jeu, fillvalue) do i=1,ieu if (field(i,1) /= fillvalue) then nacs(i) = nacs(i) + 1 end if end do do k=1,jeu do i=1,ieu if (field(i,k) /= fillvalue) then if (nacs(i)==1) then hbuf(i,k) = field(i,k) sbuf(i,k) = 0._r8 else tmp = hbuf(i,k) hbuf(i,k) = hbuf(i,k) + (field(i,k)-hbuf(i,k))/nacs(i) sbuf(i,k) = sbuf(i,k) + (field(i,k)-hbuf(i,k))*(field(i,k)-tmp) endif end if end do end do else ! increment counter before variance calculation nacs(1) = nacs(1) + 1 do k=1,jeu do i=1,ieu if (nacs(1)==1) then hbuf(i,k) = field(i,k) sbuf(i,k) = 0._r8 else tmp = hbuf(i,k) hbuf(i,k) = hbuf(i,k) + (field(i,k)-hbuf(i,k))/nacs(1) sbuf(i,k) = sbuf(i,k) + (field(i,k)-hbuf(i,k))*(field(i,k)-tmp) endif end do end do end if end subroutine hbuf_accum_variance !####################################################################### subroutine hbuf_accum_add00z (buf8, field, nacs, dimind, idim, flag_xyfill, fillvalue) ! !----------------------------------------------------------------------- ! ! Purpose: Add the values of field to 2-D hbuf, only if time is 00z. ! Increment accumulation counter by 1. ! !----------------------------------------------------------------------- ! use time_manager, only: get_curr_date real(r8), pointer :: buf8(:,:) ! 2-D history buffer integer, pointer :: nacs(:) ! accumulation counter integer, intent(in) :: idim ! Longitude dimension of field array logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue real(r8), intent(in ) :: field(idim,*) ! real*8 array type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index real(r8), intent(in) :: fillvalue ! ! Local indices ! integer :: ieu, jeu ! number of elements in each dimension integer :: i,k ! indices integer :: yr, mon, day, tod ! get the time of day, return if not 00z call get_curr_date (yr,mon,day,tod) if (tod /= 0) return call dimind%dim_sizes(ieu, jeu) if (flag_xyfill) then do k=1,jeu do i=1,ieu if (field(i,k) /= fillvalue) then buf8(i,k) = buf8(i,k) + field(i,k) end if end do end do ! ! Ensure input field has fillvalue defined invariant in the z-direction, then increment nacs ! call check_accum (field, idim, ieu, jeu, fillvalue) do i=1,ieu if (field(i,1) /= fillvalue) then nacs(i) = nacs(i) + 1 end if end do else do k=1,jeu do i=1,ieu buf8(i,k) = buf8(i,k) + field(i,k) end do end do nacs(1) = nacs(1) + 1 end if return end subroutine hbuf_accum_add00z !####################################################################### subroutine hbuf_accum_max (buf8, field, nacs, dimind, idim, flag_xyfill, fillvalue) ! !----------------------------------------------------------------------- ! ! Purpose: Accumulate the maximum values of field in 2-D hbuf ! Set accumulation counter to 1. ! !----------------------------------------------------------------------- ! real(r8), pointer :: buf8(:,:) ! 2-D history buffer integer, pointer :: nacs(:) ! accumulation counter integer, intent(in) :: idim ! Longitude dimension of field array logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue real(r8), intent(in ) :: field(idim,*) ! real*8 array type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index real(r8), intent(in) :: fillvalue ! ! Local indices ! integer :: ieu, jeu ! number of elements in each dimension integer :: i, k call dimind%dim_sizes(ieu, jeu) if (flag_xyfill) then do k=1,jeu do i=1,ieu if (nacs(i) == 0) then buf8(i,k) = -huge (buf8) end if if (field(i,k) > buf8(i,k) .and. field(i,k) /= fillvalue) then buf8(i,k) = field(i,k) end if end do end do else do k=1,jeu do i=1,ieu if (nacs(1) == 0) then buf8(i,k) = -huge (buf8) end if if (field(i,k) > buf8(i,k)) then buf8(i,k) = field(i,k) end if end do end do end if if (flag_xyfill) then call check_accum (field, idim, ieu, jeu,fillvalue) do i=1,ieu if (field(i,1) /= fillvalue) then nacs(i) = 1 end if end do else nacs(1) = 1 end if return end subroutine hbuf_accum_max !####################################################################### subroutine hbuf_accum_min (buf8, field, nacs, dimind, idim, flag_xyfill, fillvalue) ! !----------------------------------------------------------------------- ! ! Purpose: Accumulate the minimum values of field in 2-D hbuf ! Set accumulation counter to 1. ! !----------------------------------------------------------------------- ! real(r8), pointer :: buf8(:,:) ! 2-D history buffer integer, pointer :: nacs(:) ! accumulation counter integer, intent(in) :: idim ! Longitude dimension of field array logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue real(r8), intent(in ) :: field(idim,*) ! real*8 array type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index real(r8), intent(in) :: fillvalue ! ! Local indices ! integer :: ieu, jeu ! number of elements in each dimension integer :: i, k call dimind%dim_sizes(ieu, jeu) if (flag_xyfill) then do k=1,jeu do i=1,ieu if (nacs(i) == 0) then buf8(i,k) = +huge (buf8) end if if (field(i,k) < buf8(i,k) .and. field(i,k) /= fillvalue) then buf8(i,k) = field(i,k) end if end do end do else do k=1,jeu do i=1,ieu if (nacs(1) == 0) then buf8(i,k) = +huge (buf8) end if if (field(i,k) < buf8(i,k)) then buf8(i,k) = field(i,k) end if end do end do end if if (flag_xyfill) then call check_accum (field, idim, ieu, jeu, fillvalue) do i=1,ieu if (field(i,1) /= fillvalue) then nacs(i) = 1 end if end do else nacs(1) = 1 end if return end subroutine hbuf_accum_min subroutine hbuf_accum_addlcltime (buf8, field, nacs, dimind, idim, flag_xyfill, fillvalue, c , decomp_type,& lcltod_start, lcltod_stop) ! !----------------------------------------------------------------------- ! ! Purpose: Add the values of field to 2-D hbuf, only if the local time ! is in the range specified. ! Increment accumulation counter by 1. ! !----------------------------------------------------------------------- ! use time_manager, only: get_curr_date use phys_grid, only: get_rlon_all_p, phys_decomp use physconst, only: pi use phys_grid, only: get_ncols_p use dyn_grid, only: dyn_grid_get_elem_coords type (dim_index_2d), intent(in ) :: dimind ! 2-D dimension index real(r8), pointer :: buf8(:,:) ! 2-D history buffer integer, pointer :: nacs(:) ! accumulation counter integer, intent(in) :: idim ! Longitude dimension of field array logical, intent(in) :: flag_xyfill ! non-applicable xy points flagged with fillvalue real(r8), intent(in) :: field(idim,*) ! real*8 array integer, intent(in) :: c ! chunk (physics) or latitude (dynamics) index integer, intent(in) :: decomp_type, lcltod_start, lcltod_stop real(r8), intent(in) :: fillvalue ! ! Local indices ! integer :: ieu, jeu ! number of elements in each dimension integer :: i,k ! indices integer :: yr, mon, day, tod integer :: ncols integer, allocatable :: lcltod(:) ! local time of day (secs) logical, allocatable :: inavg(:) ! is the column in the desired local time range? real(r8), allocatable :: rlon(:) ! column longitude (radians) integer, allocatable :: cdex(:) ! global column index call dimind%dim_sizes(ieu, jeu) allocate( inavg(1:ieu) , lcltod(1:ieu) ) lcltod(:) = 0 ! ! Get the time of day and longitude and compute the local time. ! call get_curr_date (yr,mon,day,tod) if ( decomp_type == phys_decomp ) then ncols = get_ncols_p(c) ieu = ncols allocate( rlon(ncols) ) call get_rlon_all_p(c, ncols, rlon) lcltod(1:ncols) = mod((tod) + (nint(86400._r8 * rlon(1:ncols) / 2._r8 / pi)), 86400) else ncols = ieu allocate(rlon(ncols),cdex(ncols)) call dyn_grid_get_elem_coords( c, rlon=rlon, cdex=cdex ) lcltod(:) = -999999 where( cdex(:)>0 ) lcltod(1:ieu) = mod((tod) + (nint(86400._r8 * rlon(1:ncols) / 2._r8 / pi)), 86400) endif ! ! Set a flag to indicate that the column is in the requested local time range. ! If lcltod_stop is less than lcltod_start, then the time is wrapping around 24 hours. ! inavg(:) = .false. if (lcltod_stop < lcltod_start) then ! the ".and.(lcltod(:)>0" condition was added to exclude the undefined (-999999) columns where((lcltod(:) >= lcltod_start) .or. ((lcltod(:) < lcltod_stop).and.(lcltod(:)>0))) inavg(:) = .true. else if (lcltod_stop == lcltod_start) then where(lcltod(1:ieu) == lcltod_start) inavg(1:ieu) = .true. else where((lcltod(:) >= lcltod_start) .and. (lcltod(:) < lcltod_stop)) inavg(:) = .true. end if if (flag_xyfill) then do k=1,jeu do i=1,ieu if (inavg(i) .and. (field(i,k) /= fillvalue)) then buf8(i,k) = buf8(i,k) + field(i,k) end if end do end do ! ! Ensure input field has fillvalue defined invariant in the z-direction, then increment nacs ! call check_accum (field, idim, ieu, jeu, fillvalue) do i=1,ieu if (inavg(i) .and. (field(i,1) /= fillvalue)) then nacs(i) = nacs(i) + 1 end if end do else do k=1,jeu do i=1,ieu if (inavg(i)) then buf8(i,k) = buf8(i,k) + field(i,k) end if end do end do ! ! NOTE: Because of the local time, some columns in the chunk may not be used in the ! average, so nacs need to be dimension with the number of columns unlike the other ! averaging techniques. ! do i=1,ieu if (inavg(i)) nacs(i) = nacs(i) + 1 end do end if deallocate( inavg , rlon, lcltod ) if (allocated(cdex)) deallocate(cdex) return end subroutine hbuf_accum_addlcltime !####################################################################### !####################################################################### subroutine check_accum (field, idim, ieu, jeu, fillvalue) ! integer, intent(in) :: idim real(r8), intent(in) :: field(idim,*) ! real*8 array integer, intent(in) :: ieu,jeu ! loop ranges logical :: bad integer :: i,k real(r8), intent(in) :: fillvalue ! ! For multilevel fields ensure that all levels have fillvalue applied consistently ! bad = .false. do k=2,jeu do i=1,ieu if (field(i,1) == fillvalue .and. field(i,k) /= fillvalue .or. & field(i,1) /= fillvalue .and. field(i,k) == fillvalue) then bad = .true. end if end do end do if (bad) then call endrun ('CHECK_ACCUM: inconsistent level values') end if return end subroutine check_accum end module cam_history_buffers