FMS  2025.04
Flexible Modeling System
time_interp.F90
1 !***********************************************************************
2 !* Apache License 2.0
3 !*
4 !* This file is part of the GFDL Flexible Modeling System (FMS).
5 !*
6 !* Licensed under the Apache License, Version 2.0 (the "License");
7 !* you may not use this file except in compliance with the License.
8 !* You may obtain a copy of the License at
9 !*
10 !* http://www.apache.org/licenses/LICENSE-2.0
11 !*
12 !* FMS is distributed in the hope that it will be useful, but WITHOUT
13 !* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied;
14 !* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
15 !* PARTICULAR PURPOSE. See the License for the specific language
16 !* governing permissions and limitations under the License.
17 !***********************************************************************
18 !> @defgroup time_interp_mod time_interp_mod
19 !> @ingroup time_interp
20 !> @brief Computes a weight and dates/indices for linearly interpolating between two dates.
21 !> @author Bruce Wyman
22 !!
23 !! A time type is converted into two consecutive dates plus
24 !! a fraction representing the distance between the dates.
25 !! This information can be used to interpolate between the dates.
26 !! The dates may be expressed as years, months, or days or
27 !! as indices in an array.
28 
29 module time_interp_mod
30 
31 use time_manager_mod, only: time_type, get_date, set_date, set_time, &
34  get_calendar_type, julian, gregorian, no_calendar, &
35  operator(+), operator(-), operator(>), &
36  operator(<), operator( // ), operator( / ), &
37  operator(>=), operator(<=), operator( * ), &
38  operator(==), print_date, print_time,&
40 
41 use fms_mod, only: write_version_number, &
42  error_mesg, fatal, stdout, stdlog, &
45 use mpp_mod, only: input_nml_file
46 use platform_mod
47 
48 implicit none
49 private
50 
51 !-----------------------------------------------------------------------
52 
53 public :: time_interp_init, time_interp, fraction_of_year
54 
55 !> Returns a weight and dates or indices for interpolating between two dates. The
56 !! interface fraction_of_year is provided for backward compatibility with the
57 !! previous version.\n
58 !!
59 !! Returns weight by interpolating Time between Time1 and Time2.
60 !! i.e. weight = (Time-Time1)/(Time2-Time1)
61 !! Time1 and Time2 may be specified by any of several different ways,
62 !! which is the reason for multiple interfaces.\n
63 !!
64 !! - If Time1 and Time2 are the begining and end of the year in which
65 !! Time falls, use first interface.\n
66 !!
67 !! - If Time1 and Time2 fall on year boundaries, use second interface.\n
68 !!
69 !! - If Time1 and Time2 fall on month boundaries, use third.\n
70 !!
71 !! - If Time1 and Time2 fall on day boundaries, use fourth.\n
72 !!
73 !! - If Time1 and Time2 are consecutive elements of an assending list, use fifth.
74 !! The fifth also returns the indices of Timelist between which Time falls.\n
75 !!
76 !! - The sixth interface is for cyclical data. Time_beg and Time_end specify the
77 !! begining and end of a repeating period. In this case:<br>
78 !! weight = (Time_adjusted - Time1) / (Time2 - Time1)
79 !! <br>Where:
80 !! @code{.F90}
81 !! Time1 = Timelist(index1)
82 !! Time2 = Timelist(index2)
83 !! Time_adjusted = Time - N*Period
84 !! Period = Time_end-Time_beg
85 !! @endcode
86 !! N is between (Time-Time_end)/Period and (Time-Time_beg)/Period
87 !! That is, N is the integer that results in Time_adjusted that is between Time_beg and Time_end.
88 !!
89 !! <br>Example usages:
90 !! @code{.F90}
91 !! call time_interp( Time, weight )
92 !! call time_interp( Time, weight, year1, year2 )
93 !! call time_interp( Time, weight, year1, year2, month1, month2 )
94 !! call time_interp( Time, weight, year1, year2, month1, month2, day1, day2 )
95 !! call time_interp( Time, Timelist, weight, index1, index2 [, modtime] )
96 !! call time_interp( Time, Time_beg, Time_end, Timelist, weight, index1, index2
97 !! [,correct_leap_year_inconsistency])
98 !! @endcode
99 !!
100 !! For all routines in this module the calendar type in module
101 !! time_manager must be set.
102 !!
103 !! The following private parameters are set by this module:
104 !!
105 !! seconds per minute = 60
106 !! minutes per hour = 60
107 !! hours per day = 24
108 !! months per year = 12
109 !!
110 !! @param Time The time at which the weight is computed.
111 !! @param Time_beg For cyclical interpolation: Time_beg specifies the begining time of a cycle.
112 !! @param Time_end For cyclical interpolation: Time_end specifies the ending time of a cycle.
113 !! @param Timelist For cyclical interpolation: Timelist is an array of times between Time_beg and Time_end.
114 !! Must be monotonically increasing.
115 !! @param index1 Timelist(index1) = The largest value of Timelist which is less than mod(Time,Time_end-Time_beg)
116 !! @param index2 Timelist(index2) = The smallest value of Timelist which is greater than mod(Time,Time_end-Time_beg)
117 !! @param correct_leap_year_inconsistency Turns on a kluge for an inconsistency which may occur in a special case.
118 !! When the modulo time period (i.e. Time_end - Time_beg) is a whole number of years
119 !! and is not a multiple of 4, and the calendar in use has leap years, then it is
120 !! likely that the interpolation will involve mapping a common year onto a leap year.
121 !! In this case it is often desirable, but not absolutely necessary, to use data for
122 !! Feb 28 of the leap year when it is mapped onto a common year.
123 !! To turn this on, set correct_leap_year_inconsistency=.true.
124 !! @param weight weight = (mod(Time,Time_end-Time_beg) - Timelist(index1)) / (Timelist(index2) - Timelist(index1))
125 !> @ingroup time_interp_mod
126 interface time_interp
127  module procedure time_interp_frac_r8, time_interp_year_r8, &
128  time_interp_month_r8, time_interp_day_r8, &
129  time_interp_list_r8, time_interp_modulo_r8
130  module procedure time_interp_frac_r4, time_interp_year_r4, &
131  time_interp_month_r4, time_interp_day_r4, &
132  time_interp_list_r4, time_interp_modulo_r4
133 end interface
134 
135 !> @addtogroup time_interp_mod
136 !> @{
137 integer, public, parameter :: NONE=0, year=1, month=2, day=3
138 
139 !-----------------------------------------------------------------------
140 
141  integer, parameter :: secmin = 60, minhour = 60, hourday = 24, &
142  sechour = secmin*minhour, &
143  secday = secmin*minhour*hourday
144 
145  integer, parameter :: monyear = 12
146  integer, parameter :: halfday = secday/2
147 
148  integer :: yrmod, momod, dymod
149  logical :: mod_leapyear
150 
151 ! Include variable "version" to be written to log file.
152 #include<file_version.h>
153 
154  logical :: module_is_initialized=.false.
155  logical :: perthlike_behavior=.false.
156 
157  namelist / time_interp_nml / perthlike_behavior
158 
159 contains
160 
161 
162  subroutine time_interp_init()
163  integer :: ierr, io, logunit
164 
165  if ( module_is_initialized ) return
166 
167  read (input_nml_file, time_interp_nml, iostat=io)
168  ierr = check_nml_error(io, 'time_interp_nml')
169 
170  call write_version_number("TIME_INTERP_MOD", version)
171  logunit = stdlog()
172  write(logunit,time_interp_nml)
173 
174  module_is_initialized = .true.
175 
176  end subroutine time_interp_init
177 
178 
179 !> @brief Wrapper function to return the fractional time into the current year
180 !! Always returns an r8_kind, conversion to r4 will be done implicitly if needed
181 !> @param Time time to calculate fraction with
182 !> @return real(kind=8) fraction of time passed in current year
183  function fraction_of_year (Time)
184  type(time_type), intent(in) :: time
185  real(r8_kind) :: fraction_of_year
186 
187  call time_interp ( time, fraction_of_year )
188 
189  end function fraction_of_year
190 
191 !#######################################################################
192 !> Given an array of times in ascending order and a specific time returns
193 !! values of index1 and index2 such that the Timelist(index1)<=Time and
194 !! Time<=Timelist(index2), and index2=index1+1
195 !! index1=0, index2=1 or index=n, index2=n+1 are returned to indicate that
196 !! the time is out of range
197 subroutine bisect(Timelist,Time,index1,index2)
198  type(time_type) , intent(in) :: Timelist(:)
199  type(time_type) , intent(in) :: Time
200  integer, optional, intent(out) :: index1, index2
201 
202  integer :: i,il,iu,n,i1,i2
203 
204  n = size(timelist(:))
205 
206  if (time==timelist(1)) then
207  i1 = 1 ; i2 = 2
208  else if (time==timelist(n)) then
209  i1 = n ; i2 = n+1
210  else
211  il = 0; iu=n+1
212  do while(iu-il > 1)
213  i = (iu+il)/2
214  if(timelist(i) > time) then
215  iu = i
216  else
217  il = i
218  endif
219  enddo
220  i1 = il ; i2 = il+1
221  endif
222 
223  if(PRESENT(index1)) index1 = i1
224  if(PRESENT(index2)) index2 = i2
225 end subroutine bisect
226 
227 !#######################################################################
228 ! private routines
229 !#######################################################################
230 
231  function year_midpt (yr)
232 
233  integer, intent(in) :: yr
234  type (time_type) :: year_midpt, year_beg, year_end
235 
236 
237  year_beg = set_date(yr , 1, 1)
238  year_end = set_date(yr+1, 1, 1)
239 
240  year_midpt = (year_beg + year_end) / 2
241 
242  end function year_midpt
243 
244  function month_midpt (yr, mo)
245 
246  integer, intent(in) :: yr, mo
247  type (time_type) :: month_midpt, month_beg, month_end
248 
249 ! --- beginning of this month ---
250  month_beg = set_date(yr, mo, 1)
251 
252 ! --- start of next month ---
253  if (mo < 12) then
254  month_end = set_date(yr, mo+1, 1)
255  else
256  month_end = set_date(yr+1, 1, 1)
257  endif
258 
259  month_midpt = (month_beg + month_end) / 2
260 
261  end function month_midpt
262 
263 function set_modtime (Tin, modtime) result (Tout)
264 type(time_type), intent(in) :: Tin
265 integer, intent(in), optional :: modtime
266 type(time_type) :: Tout
267 integer :: yr, mo, dy, hr, mn, se, mtime
268 
269  if(present(modtime)) then
270  mtime = modtime
271  else
272  mtime = none
273  endif
274 
275  select case (mtime)
276  case (none)
277  tout = tin
278  case (year)
279  call get_date (tin, yr, mo, dy, hr, mn, se)
280  yr = yrmod
281  ! correct leap year dates
282  if (.not.mod_leapyear .and. mo == 2 .and. dy > 28) then
283  mo = 3; dy = dy-28
284  endif
285  tout = set_date(yr, mo, dy, hr, mn, se)
286  case (month)
287  call get_date (tin, yr, mo, dy, hr, mn, se)
288  yr = yrmod; mo = momod
289  tout = set_date(yr, mo, dy, hr, mn, se)
290  case (day)
291  call get_date (tin, yr, mo, dy, hr, mn, se)
292  yr = yrmod; mo = momod; dy = dymod
293  tout = set_date(yr, mo, dy, hr, mn, se)
294  end select
295 
296 end function set_modtime
297 
298 subroutine error_handler(string)
299  character(len=*), intent(in) :: string
300 
301  call error_mesg ('time_interp_mod', trim(string), fatal)
302 
303 end subroutine error_handler
304 
305 
306 #include "time_interp_r4.fh"
307 #include "time_interp_r8.fh"
308 
309 end module time_interp_mod
310 
311 !> @}
312 ! close documentation grouping
integer function, public check_nml_error(IOSTAT, NML_NAME)
Checks the iostat argument that is returned after reading a namelist and determines if the error code...
Definition: fms.F90:523
subroutine, public write_version_number(version, tag, unit)
Prints to the log file (or a specified unit) the version id string and tag name.
Definition: fms.F90:701
logical function, public fms_error_handler(routine, message, err_msg)
Facilitates the control of fatal error conditions.
Definition: fms.F90:468
subroutine, public error_mesg(routine, message, level)
Print notes, warnings and error messages; terminates program for warning and error messages....
Definition: fms.F90:441
integer function stdout()
This function returns the current standard fortran unit numbers for output.
Definition: mpp_util.inc:42
integer function stdlog()
This function returns the current standard fortran unit numbers for log messages. Log messages,...
Definition: mpp_util.inc:58
subroutine bisect(Timelist, Time, index1, index2)
Given an array of times in ascending order and a specific time returns values of index1 and index2 su...
real(r8_kind) function, public fraction_of_year(Time)
Wrapper function to return the fractional time into the current year Always returns an r8_kind,...
Returns a weight and dates or indices for interpolating between two dates. The interface fraction_of_...
logical function, public leap_year(Time, err_msg)
Returns true if the year corresponding to the input time is a leap year (for default calendar)....
character(len=15) function, public date_to_string(time, err_msg)
Get the a character string that represents the time. The format will be yyyymmdd.hhmmss.
subroutine, public get_date(time, year, month, day, hour, minute, second, tick, err_msg)
Gets the date for different calendar types. Given a time_interval, returns the corresponding date und...
integer function, public days_in_year(Time)
Returns the number of days in the calendar year corresponding to the date represented by time for the...
subroutine, public print_date(Time, str, unit)
Prints the time to standard output (or optional unit) as a date.
integer function, public days_in_month(Time, err_msg)
Given a time, computes the corresponding date given the selected date time mapping algorithm.
real(kind=r8_kind) function, public time_type_to_real(time)
Converts time to seconds and returns it as a real number.
integer function, public get_calendar_type()
Returns default calendar type for mapping from time to date.
subroutine, public print_time(Time, str, unit)
Prints the given time_type argument as a time (using days, seconds and ticks)
subroutine, public time_list_error(T, Terr)
This routine converts the integer tdays to a string.
Given an input date in year, month, days, etc., creates a time_type that represents this time interva...
Given some number of seconds and days, returns the corresponding time_type.
Type to represent amounts of time. Implemented as seconds and days to allow for larger intervals.
Wrapper for the real to time interface Takes seconds as reals to convert to a time_type representatio...