LCOV - code coverage report
Current view: top level - src - mo_edk_get_nc_time.f90 (source / functions) Hit Total Coverage
Test: edk coverage Lines: 0 73 0.0 %
Date: 2024-03-11 14:23:05 Functions: 0 1 0.0 %

          Line data    Source code
       1             : !> \file    mo_edk_get_nc_time.f90
       2             : !> \brief   \copybrief mo_edk_get_nc_time
       3             : !> \details \copydetails mo_edk_get_nc_time
       4             : 
       5             : !> \brief   Module to get time vector from NetCDF file.
       6             : !> \author  Matthias Zink
       7             : !> \date    Oct 2012
       8             : !> \authors Matthias Cuntz, Juliane Mai
       9             : !> \date    Nov 2014
      10             : !!          - time int or double
      11             : !> \author  Stephan Thober
      12             : !> \date    Sep 2015
      13             : !!          - added read for hourly data
      14             : !> \author  Robert Schweppe
      15             : !> \date    Nov 2017
      16             : !!          - restructured routine, reads vector now
      17             : !> \author  Maren Kaluza
      18             : !> \date    May 2018
      19             : !!          - fixed bug in time reading
      20             : !> \copyright Copyright 2005-\today, the CHS Developers, Sabine Attinger: All rights reserved.
      21             : !! EDK is released under the LGPLv3+ license \license_note
      22             : module mo_edk_get_nc_time
      23             : 
      24             :   implicit none
      25             : 
      26             :   private
      27             : 
      28             :   public :: get_time_vector_and_select
      29             : 
      30             : contains
      31             : 
      32             :   !> \brief   Extract time vector
      33             :   !> \details Extract time vector in unit julian hours and get supposed time step in hours
      34           0 :   subroutine get_time_vector_and_select(var, fname, inctimestep, time_start, time_cnt, target_period)
      35             : 
      36             :     use mainVar, only : period, dayhours, daysecs, yeardays
      37             :     use mo_julian, only : caldat, dec2date, julday
      38             :     use mo_kind, only : dp, i4, i8
      39             :     use mo_message, only : message
      40             :     use mo_netcdf, only : NcVariable
      41             :     use mo_string_utils, only : DIVIDE_STRING
      42             : 
      43             :     implicit none
      44             : 
      45             :     !> variable of interest
      46             :     type(NcVariable), intent(in) :: var
      47             : 
      48             :     !> fname of ncfile for error message
      49             :     character(256), intent(in) :: fname
      50             : 
      51             :     !> flag for requested time step
      52             :     integer(i4), intent(in) :: inctimestep
      53             : 
      54             :     !> time_start index of time selection
      55             :     integer(i4), intent(out) :: time_start
      56             : 
      57             :     !> time_count of indexes of time selection
      58             :     integer(i4), intent(out) :: time_cnt
      59             : 
      60             :     !> reference period
      61             :     type(period), intent(in), optional :: target_period
      62             : 
      63             :     ! reference time of NetCDF
      64             :     integer(i4) :: yRef, dRef, mRef, hRef, jRef
      65             : 
      66             :     ! netcdf attribute values
      67             :     character(256) :: AttValues
      68             : 
      69             :     ! dummies for netcdf attribute handling
      70           0 :     character(256), dimension(:), allocatable :: strArr, date, time
      71             : 
      72             :     ! native time step converter in ncfile
      73             :     integer(i8) :: time_step_seconds
      74             : 
      75             :     ! time vector
      76           0 :     integer(i8), allocatable, dimension(:) :: time_data
      77             : 
      78             :     ! period of ncfile, for clipping
      79             :     type(period) :: nc_period, clip_period
      80             : 
      81             :     integer(i4) :: ncJulSta1, dd, n_time
      82             : 
      83             :     integer(i4) :: mmcalstart, mmcalend, yycalstart, yycalend
      84             : 
      85             :     integer(i4) :: mmncstart, yyncstart
      86             : 
      87             :     ! helper variable for error output
      88             :     integer(i4) :: hstart_int, hend_int
      89             : 
      90             :     ! helper variable for error output
      91             :     character(256) :: error_msg
      92             : 
      93             : 
      94           0 :     call var%getAttribute('units', AttValues)
      95             :     ! AttValues looks like "<unit> since YYYY-MM-DD[ HH:MM:SS]"
      96             :     ! split at space
      97           0 :     call DIVIDE_STRING(trim(AttValues), ' ', strArr)
      98             : 
      99             :     ! determine reference time at '-' and convert to integer
     100           0 :     call DIVIDE_STRING(trim(strArr(3)), '-', date)
     101           0 :     read(date(1), *) yRef
     102           0 :     read(date(2), *) mRef
     103           0 :     read(date(3), *) dRef
     104             : 
     105           0 :     jRef = julday(dd = dRef, mm = mRef, yy = yRef)
     106             : 
     107             :     ! if existing also read in the time (only hour so far)
     108           0 :     hRef = 0
     109           0 :     if(size(strArr) .gt. 3) then
     110           0 :       call DIVIDE_STRING(trim(strArr(4)), ':', time)
     111           0 :       read(time(1), *) hRef
     112             :     end if
     113             : 
     114             :     ! determine the step_size
     115           0 :     if (strArr(1) .EQ. 'days') then
     116             :       time_step_seconds = int(DaySecs)
     117           0 :     else if (strArr(1) .eq. 'hours') then
     118             :       time_step_seconds = int(DaySecs / DayHours)
     119           0 :     else if (strArr(1) .eq. 'minutes') then
     120             :       time_step_seconds = int(DaySecs / DayHours / 60._dp)
     121           0 :     else if (strArr(1) .eq. 'seconds') then
     122             :       time_step_seconds = 1_i8
     123             :     else
     124             :       call message('***ERROR: Please provide the input data in (days, hours, minutes, seconds) ', &
     125           0 :               'since YYYY-MM-DD[ HH:MM:SS] in the netcdf file. Found: ', trim(AttValues))
     126           0 :       stop
     127             :     end if
     128             : 
     129             :     ! get the time vector
     130           0 :     call var%getData(time_data)
     131             :     ! convert array from units since to seconds
     132           0 :     time_data = time_data * time_step_seconds
     133             : 
     134             :     ! check for length of time vector, needs to be at least of length 2, otherwise step width check fails
     135           0 :     if (size(time_data) .le. 1) then
     136           0 :       call message('***ERROR: length of time dimension needs to be at least 2 in file: ' // trim(fname))
     137           0 :       stop
     138             :     end if
     139             : 
     140             :     ! check for equal timesteps and timestep must not be multiple of native timestep
     141             :     error_msg = '***ERROR: time_steps are not equal over all times in file and/or do not conform to' // &
     142           0 :             ' requested timestep in file (' // trim(fname) // ') : '
     143             : 
     144             :     ! compare the read period from ncfile to the period required
     145             :     ! convert julian second information back to date via conversion to float
     146             :     ! the 0.5_dp is for the different reference of fractional julian days, hours are truncated
     147           0 :     n_time = size(time_data)
     148           0 :     call dec2date(time_data(1) / DaySecs - 0.5_dp + jRef + hRef / 24._dp, nc_period%dStart, nc_period%mStart, &
     149           0 :             nc_period%yStart, hstart_int)
     150           0 :     nc_period%julStart = int(time_data(1) / DaySecs + jRef + hRef / 24._dp)
     151           0 :     call dec2date(time_data(n_time) / DaySecs - 0.5_dp + jRef + hRef / 24._dp, nc_period%dEnd, nc_period%mEnd, &
     152           0 :             nc_period%yEnd, hend_int)
     153           0 :     nc_period%julEnd = int(time_data(n_time) / DaySecs + jRef + hRef / 24._dp)
     154             : 
     155             :     ! if no target period is present, use the whole time period
     156           0 :     if (present(target_period)) then
     157           0 :       clip_period = target_period
     158             :     else
     159           0 :       clip_period = nc_period
     160             :     end if
     161             : 
     162             :     ! prepare the selection and check for required time_step
     163           0 :     select case(inctimestep)
     164             :     case(-1) ! daily
     165             :       ! difference must be 1 day
     166           0 :       if (.not. all(abs((time_data(2 : n_time) - time_data(1 : n_time - 1)) / DaySecs - 1._dp) .lt. 1.e-6)) then
     167           0 :         call message(error_msg // trim('daily'))
     168           0 :         stop
     169             :       end if
     170           0 :       ncJulSta1 = nc_period%julStart
     171           0 :       time_start = clip_period%julStart - ncJulSta1 + 1_i4
     172           0 :       time_cnt = clip_period%julEnd - clip_period%julStart + 1_i4
     173             :     case(-2) ! monthly
     174             :       ! difference must be between 28 and 31 days
     175           0 :       if (any(abs((time_data(2 : n_time) - time_data(1 : n_time - 1)) / DaySecs) .gt. 31._dp) .or. &
     176             :               any(abs((time_data(2 : n_time) - time_data(1 : n_time - 1)) / DaySecs) .lt. 28._dp)) then
     177           0 :         call message(error_msg // trim('monthly'))
     178           0 :         stop
     179             :       end if
     180             : 
     181           0 :       call caldat(clip_period%julStart, dd, mmcalstart, yycalstart)
     182           0 :       call caldat(nc_period%julStart, dd, mmncstart, yyncstart)
     183             :       ! monthly timesteps are usually set by month end, so for beginning, we need 1st of month
     184           0 :       ncJulSta1 = julday(1, mmncstart, yyncstart)
     185           0 :       call caldat(clip_period%julEnd, dd, mmcalend, yycalend)
     186           0 :       time_start = (yycalstart * 12 + mmcalstart) - (yyncstart * 12 + mmncstart) + 1_i4
     187           0 :       time_cnt = (yycalend * 12 + mmcalend) - (yycalstart * 12 + mmcalstart) + 1_i4
     188             :     case(-3) ! yearly
     189             :       ! difference must be between 365 and 366 days
     190           0 :       if (any(abs((time_data(2 : n_time) - time_data(1 : n_time - 1)) / DaySecs) .gt. (YearDays + 1._dp)) .or. &
     191             :               any(abs((time_data(2 : n_time) - time_data(1 : n_time - 1)) / DaySecs) .lt. YearDays)) then
     192           0 :         call message(error_msg // 'yearly')
     193           0 :         stop
     194             :       end if
     195           0 :       call caldat(clip_period%julStart, dd, mmcalstart, yycalstart)
     196           0 :       call caldat(nc_period%julStart, dd, mmncstart, yyncstart)
     197             :       ! yearly timesteps are usually set by year end, so for beginning, we need 1st of year
     198           0 :       ncJulSta1 = julday(1, 1, yyncstart)
     199           0 :       call caldat(clip_period%julEnd, dd, mmcalend, yycalend)
     200           0 :       time_start = yycalstart - yyncstart + 1_i4
     201           0 :       time_cnt = yycalend - yycalstart + 1_i4
     202             :     case(-4) ! hourly
     203             :       ! difference must be 1 hour
     204           0 :       if (.not. all(abs((time_data(2 : n_time) - time_data(1 : n_time - 1)) / 3600._dp - 1._dp) .lt. 1.e-6)) then
     205           0 :         call message(error_msg // 'hourly')
     206           0 :         stop
     207             :       end if
     208           0 :       ncJulSta1 = nc_period%julStart
     209           0 :       time_start = (clip_period%julStart - ncJulSta1) * 24_i4 + 1_i4 ! convert to hours; always starts at one
     210           0 :       time_cnt = (clip_period%julEnd - clip_period%julStart + 1_i4) * 24_i4 ! convert to hours
     211             :     case default ! no output at all
     212           0 :       call message('***ERROR: read_forcing_nc: unknown nctimestep switch.')
     213           0 :       stop
     214             :     end select
     215             : 
     216             :     ! Check if time steps in file cover simulation period
     217           0 :     if (.not. ((ncJulSta1 .LE. clip_period%julStart) .AND. (nc_period%julEnd .GE. clip_period%julEnd))) then
     218             :       call message('***ERROR: read_forcing_nc: time period of input data: ', trim(fname), &
     219           0 :               '          is not matching modelling period.')
     220           0 :       stop
     221             :     end if
     222             : 
     223           0 :   end subroutine get_time_vector_and_select
     224             : 
     225             : end module mo_edk_get_nc_time

Generated by: LCOV version 1.16