FMS 2025.01-dev
Flexible Modeling System
Loading...
Searching...
No Matches
time_interp.inc
1!***********************************************************************
2!* GNU Lesser General Public License
3!*
4!* This file is part of the GFDL Flexible Modeling System (FMS).
5!*
6!* FMS is free software: you can redistribute it and/or modify it under
7!* the terms of the GNU Lesser General Public License as published by
8!* the Free Software Foundation, either version 3 of the License, or (at
9!* your option) any later version.
10!*
11!* FMS is distributed in the hope that it will be useful, but WITHOUT
12!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14!* for more details.
15!*
16!* You should have received a copy of the GNU Lesser General Public
17!* License along with FMS. If not, see <http://www.gnu.org/licenses/>.
18!***********************************************************************
19
20!> @addtogroup time_interp_mod
21!> @{
22
23 !> @brief Calculates the fractional time into the current year
24 subroutine time_interp_frac_ ( Time, weight )
25
26 type(time_type), intent(in) :: Time
27 real(FMS_TI_KIND_), intent(out) :: weight !< fractional time
28
29 integer :: yr, mo, dy, hour, minute, second
30 type(time_type) :: Year_beg, Year_end
31
32
33 if ( .not. module_is_initialized ) call time_interp_init
34
35! ---- compute fractional time of year -----
36
37 call get_date (time, yr, mo, dy, hour, minute, second)
38
39 year_beg = set_date(yr , 1, 1)
40 year_end = set_date(yr+1, 1, 1)
41
42 weight = real( (time - year_beg) // (year_end - year_beg) , kind=fms_ti_kind_)
43
44 end subroutine time_interp_frac_
45
46
47 !> @brief Calculates fractional time between mid points of consecutive years
48 subroutine time_interp_year_ ( Time, weight, year1, year2 )
49
50 type(time_type), intent(in) :: Time
51 real(FMS_TI_KIND_), intent(out) :: weight !< fractional time between midpoints of year1 and year2
52 integer , intent(out) :: year1, year2
53
54 integer :: yr, mo, dy, hour, minute, second
55 type (time_type) :: Mid_year, Mid_year1, Mid_year2
56
57
58 if ( .not. module_is_initialized ) call time_interp_init()
59
60 call get_date (time, yr, mo, dy, hour, minute, second)
61
62 ! mid point of current year
63 mid_year = year_midpt(yr)
64
65 if ( time >= mid_year ) then
66 ! current time is after mid point of current year
67 year1 = yr
68 year2 = yr+1
69 mid_year2 = year_midpt(year2)
70 weight = real( (time - mid_year) // (mid_year2 - mid_year) , kind=fms_ti_kind_ )
71 else
72 ! current time is before mid point of current year
73 year2 = yr
74 year1 = yr-1
75 mid_year1 = year_midpt(year1)
76 weight = real( (time - mid_year1) // (mid_year - mid_year1), kind=fms_ti_kind_ )
77 endif
78
79 end subroutine time_interp_year_
80
81 !> @brief Calculates fractional time between mid points of consecutive months
82 subroutine time_interp_month_ ( Time, weight, year1, year2, month1, month2 )
83
84 type(time_type), intent(in) :: Time
85 real(FMS_TI_KIND_) , intent(out) :: weight
86 integer , intent(out) :: year1, year2, month1, month2
87
88 integer :: yr, mo, dy, hour, minute, second, &
89 mid_month, cur_month, mid1, mid2
90
91 if ( .not. module_is_initialized ) call time_interp_init()
92
93 call get_date (time, yr, mo, dy, hour, minute, second)
94
95 ! mid point of current month in seconds
96 mid_month = days_in_month(time) * halfday
97 ! time into current month in seconds
98 cur_month = second + secmin*minute + sechour*hour + secday*(dy-1)
99
100 if ( cur_month >= mid_month ) then
101 ! current time is after mid point of current month
102 year1 = yr; month1 = mo
103 year2 = yr; month2 = mo+1
104 if (month2 > monyear) then
105 year2 = year2+1; month2 = 1
106 endif
107 mid1 = mid_month
108 mid2 = days_in_month(set_date(year2,month2,2)) * halfday
109 weight = real(cur_month - mid1, fms_ti_kind_) / real(mid1+mid2, fms_ti_kind_)
110 else
111 ! current time is before mid point of current month
112 year2 = yr; month2 = mo
113 year1 = yr; month1 = mo-1
114 if (month1 < 1) then
115 year1 = year1-1; month1 = monyear
116 endif
117 if (year1>0) then
118 mid1 = days_in_month(set_date(year1,month1,2)) * halfday
119 else
120 ! this can happen if we are at the beginning of year 1. In this case
121 ! use December 0001 to calculate the duration of December 0000.
122 ! This should work for all calendars
123 mid1 = days_in_month(set_date(1,month1,2)) * halfday
124 endif
125 mid2 = mid_month
126 weight = real(cur_month + mid1, fms_ti_kind_) / real(mid1+mid2, fms_ti_kind_)
127 endif
128
129 end subroutine time_interp_month_
130
131 !> @brief Calculates fractional time between mid points of consecutive days
132 subroutine time_interp_day_ ( Time, weight, year1, year2, month1, month2, day1, day2 )
133
134 type(time_type), intent(in) :: Time
135 real(FMS_TI_KIND_), intent(out) :: weight
136 integer , intent(out) :: year1, year2, month1, month2, day1, day2
137
138 integer :: yr, mo, dy, hour, minute, second, sday
139
140 if ( .not. module_is_initialized ) call time_interp_init()
141
142 call get_date (time, yr, mo, dy, hour, minute, second)
143
144 ! time into current day in seconds
145 sday = second + secmin*minute + sechour*hour
146
147 if ( sday >= halfday ) then
148 ! current time is after mid point of day
149 year1 = yr; month1 = mo; day1 = dy
150 year2 = yr; month2 = mo; day2 = dy + 1
151 weight = real(sday - halfday, fms_ti_kind_) / real(secday, fms_ti_kind_)
152
153 if (day2 > days_in_month(time)) then
154 month2 = month2 + 1
155 day2 = 1
156 if (month2 > monyear) then
157 month2 = 1; year2 = year2+1
158 endif
159 endif
160 else
161 ! current time is before mid point of day
162 year2 = yr; month2 = mo ; day2 = dy
163 year1 = yr; month1 = mo; day1 = dy - 1
164 weight = real(sday + halfday,fms_ti_kind_) / real(secday,fms_ti_kind_)
165
166 if (day1 < 1) then
167 month1 = month1 - 1
168 if (month1 < 1) then
169 month1 = monyear; year1 = year1-1
170 endif
171 day1 = days_in_month(set_date(year1,month1,2))
172 endif
173 endif
174
175 end subroutine time_interp_day_
176
177 !> Part of the time_interp interface, calculates for cyclical data
178 !! Time_beg and Time_end mark a repeating period
179 !!
180 !! Finds mid points and fractional weight for a time perioid
181subroutine time_interp_modulo_(Time, Time_beg, Time_end, Timelist, weight, index1, index2, &
182 correct_leap_year_inconsistency, err_msg)
183type(time_type), intent(in) :: Time !< a specific time value
184type(time_type), intent(in) :: Time_beg !< begining of period to search with
185type(time_type), intent(in) :: Time_end !< end of period to search with
186type(time_type), intent(in) :: Timelist(:) !< ascending time values to search between
187real(FMS_TI_KIND_) , intent(out) :: weight
188integer , intent(out) :: index1, index2 !< indices of bounding time values within Timelist
189logical, intent(in), optional :: correct_leap_year_inconsistency!< When true turns on a kluge for an
190 !! inconsistency which may occur in a special case.
191 !! When the modulo time period (i.e. Time_end - Time_beg) is a
192 !! whole number of years and is not a multiple of 4, and the calendar
193 !! in use has leap years, then it is likely that the interpolation
194 !! will involve mapping a common year onto a leap year. In this case
195 !! it is often desirable, but not absolutely necessary, to use data
196 !! for Feb 28 of the leap year when it is mapped onto a common year.
197character(len=*), intent(out), optional :: err_msg
198
199 type(time_type) :: Period, T
200 integer :: is, ie,i1,i2
201 integer :: ys,ms,ds,hs,mins,ss ! components of the starting date
202 integer :: ye,me,de,he,mine,se ! components of the ending date
203 integer :: yt,mt,dt,ht,mint,st ! components of the current date
204 integer :: dt1 ! temporary value for day
205 integer :: n ! size of Timelist
206 integer :: stdoutunit
207 logical :: correct_lyr, calendar_has_leap_years, do_the_lyr_correction
208 integer, parameter :: kindl = fms_ti_kind_
209
210 if ( .not. module_is_initialized ) call time_interp_init
211 if( present(err_msg) ) err_msg = ''
212
213 stdoutunit = stdout()
214 n = size(timelist)
215
216 if (time_beg>=time_end) then
217 if(fms_error_handler('time_interp_modulo', &
218 'end of the specified time loop interval must be later than its beginning',err_msg)) return
219 endif
220
221 calendar_has_leap_years = (get_calendar_type() == julian .or. get_calendar_type() == gregorian)
222
223 period = time_end-time_beg ! period of the time axis
224
225 if(present(correct_leap_year_inconsistency)) then
226 correct_lyr = correct_leap_year_inconsistency
227 else
228 correct_lyr = .false.
229 endif
230
231 ! bring the requested time inside the specified time period
232 t = time
233
234 do_the_lyr_correction = .false.
235
236 ! Determine if the leap year correction needs to be done.
237 ! It never needs to be done unless 3 conditions are met:
238 ! 1) We are using a calendar with leap years
239 ! 2) optional argument correct_leap_year_inconsistency is present and equals .true.
240 ! 3) The modulo time period is an integer number of years
241 ! If all of these are true then set do_the_lyr_correction to .true.
242
243 if(calendar_has_leap_years .and. correct_lyr) then
244 call get_date(time_beg,ys,ms,ds,hs,mins,ss)
245 call get_date(time_end,ye,me,de,he,mine,se)
246 if(ms==me.and.ds==de.and.hs==he.and.mins==mine.and.ss==se) then
247 ! whole number of years
248 do_the_lyr_correction = .true.
249 endif
250 endif
251
252 if(do_the_lyr_correction) then
253 call get_date(t,yt,mt,dt,ht,mint,st)
254 yt = ys+modulo(yt-ys,ye-ys)
255 dt1 = dt
256 ! If it is Feb 29, but we map into a common year, use Feb 28
257 if(mt==2.and.dt==29.and..not.leap_year(set_date(yt,1,1))) dt1=28
258 t = set_date(yt,mt,dt1,ht,mint,st)
259 if (t < time_beg) then
260 ! the requested time is within the first year,
261 ! but before the starting date. So we shift it to the last year.
262 if(mt==2.and.dt==29.and..not.leap_year(set_date(ye,1,1))) dt=28
263 t = set_date(ye,mt,dt,ht,mint,st)
264 endif
265 else
266 do while ( t >= time_end )
267 t = t-period
268 enddo
269 do while ( t < time_beg )
270 t = t+period
271 enddo
272 endif
273
274 ! find indices of the first and last records in the Timelist that are within
275 ! the requested time period.
276 if (time_end<=timelist(1).or.time_beg>=timelist(n)) then
277 if(get_calendar_type() == no_calendar) then
278 call print_time(time_beg, 'Time_beg' )
279 call print_time(time_end, 'Time_end' )
280 call print_time(timelist(1), 'Timelist(1)' )
281 call print_time(timelist(n), 'Timelist(n)' )
282 else
283 call print_date(time_beg, 'Time_beg' )
284 call print_date(time_end, 'Time_end' )
285 call print_date(timelist(1), 'Timelist(1)' )
286 call print_date(timelist(n), 'Timelist(n)' )
287 endif
288 write(stdoutunit,*)'where n = size(Timelist) =',n
289 if(fms_error_handler('time_interp_modulo', &
290 'the entire time list is outside the specified time loop interval',err_msg)) return
291 endif
292
293 call bisect(timelist,time_beg,index1=i1,index2=i2)
294 if (i1 < 1) then
295 is = 1 ! Time_beg before lower boundary
296 else if (time_beg == timelist(i1)) then
297 is = i1 ! Time_beg right on the lower boundary
298 else
299 is = i2 ! Time_beg inside the interval or on upper boundary
300 endif
301 call bisect(timelist,time_end,index1=i1,index2=i2)
302 if (time_end > timelist(i1)) then
303 ie = i1
304 else if (time_end == timelist(i1)) then
305 if(time_beg == timelist(is)) then
306 ! Timelist includes time levels at both the lower and upper ends of the period.
307 ! The endpoints of Timelist specify the same point in the cycle.
308 ! This ambiguity is resolved by ignoring the last time level.
309 ie = i1-1
310 else
311 ie = i1
312 endif
313 else
314! This should never happen because bisect does not return i1 such that Time_end < Timelist(i1)
315 endif
316 if (is>=ie) then
317 if(get_calendar_type() == no_calendar) then
318 call print_time(time_beg, 'Time_beg =')
319 call print_time(time_end, 'Time_end =')
320 call print_time(timelist(1), 'Timelist(1)=')
321 call print_time(timelist(n), 'Timelist(n)=')
322 else
323 call print_date(time_beg, 'Time_beg =')
324 call print_date(time_end, 'Time_end =')
325 call print_date(timelist(1), 'Timelist(1)=')
326 call print_date(timelist(n), 'Timelist(n)=')
327 endif
328 write(stdoutunit,*)'where n = size(Timelist) =',n
329 write(stdoutunit,*)'is =',is,'ie =',ie
330 if(fms_error_handler('time_interp_modulo', &
331 'error in calculation of time list bounds within the specified time loop interval',err_msg)) return
332 endif
333
334 ! handle special cases:
335 if( t>=timelist(ie) ) then
336 ! time is after the end of the portion of the time list within the requested period
337 index1 = ie; index2 = is
338 weight = real((t-timelist(ie))//(period-(timelist(ie)-timelist(is))), fms_ti_kind_ )
339 else if (t<timelist(is)) then
340 ! time is before the beginning of the portion of the time list within the requested period
341 index1 = ie; index2 = is
342 weight = 1.0_kindl - real(((timelist(is)-t)//(period-(timelist(ie)-timelist(is)))), fms_ti_kind_ )
343 else
344 call bisect(timelist,t,index1,index2)
345 weight = real((t-timelist(index1)) // (timelist(index2)-timelist(index1)), fms_ti_kind_ )
346 endif
347
348end subroutine time_interp_modulo_
349
350subroutine time_interp_list_ ( Time, Timelist, weight, index1, index2, modtime, err_msg )
351type(time_type) , intent(in) :: Time, Timelist(:)
352real(FMS_TI_KIND_) , intent(out) :: weight
353integer , intent(out) :: index1, index2
354integer, optional, intent(in) :: modtime
355character(len=*), intent(out), optional :: err_msg
356
357integer :: n, hr, mn, se, mtime
358type(time_type) :: T, Ts, Te, Td, Period, Time_mod
359character(len=:),allocatable :: terr, tserr, teerr
360integer, parameter :: kindl = fms_ti_kind_
361
362 if ( .not. module_is_initialized ) call time_interp_init
363
364 if( present(err_msg) ) err_msg = ''
365
366 weight = 0.0_kindl; index1 = 0; index2 = 0
367 n = size(timelist(:))
368
369! setup modular time axis?
370 mtime = none
371 if (present(modtime)) then
372 mtime = modtime
373 time_mod = (timelist(1)+timelist(n))/2
374 call get_date (time_mod, yrmod, momod, dymod, hr, mn, se)
375 mod_leapyear = leap_year(time_mod)
376 endif
377
378! set period for modulo axis
379 select case (mtime)
380 case (none)
381 ! do nothing
382 case (year)
383 period = set_time(0,days_in_year(time_mod))
384 case (month)
385 ! month length must be equal
386 if (days_in_month(time_mod) /= days_in_month(time)) then
387 if(fms_error_handler('time_interp_list','modulo months must have same length',err_msg)) return
388 endif
389 period = set_time(0,days_in_month(time_mod))
390 case (day)
391 period = set_time(0,1)
392 case default
393 if(fms_error_handler('time_interp_list','invalid value for argument modtime',err_msg)) return
394 end select
395
396! If modulo time is in effect and Timelist spans a time interval exactly equal to
397! the modulo period, then the endpoints of Timelist specify the same point in the cycle.
398! This ambiguity is resolved by ignoring the last time level.
399 if (mtime /= none .and. timelist(size(timelist))-timelist(1) == period) then
400 n = size(timelist) - 1
401 else
402 n = size(timelist)
403 endif
404
405! starting and ending times from list
406 ts = timelist(1)
407 te = timelist(n)
408 td = te-ts
409 t = set_modtime(time,mtime)
410
411! Check that Timelist does not span a time interval greater than the modulo period
412 if (mtime /= none) then
413 if (td > period) then
414 if(fms_error_handler('time_interp_list','period of list exceeds modulo period',err_msg)) return
415 endif
416 endif
417
418! time falls on start or between start and end list values
419 if ( t >= ts .and. t < te ) then
420 call bisect(timelist(1:n),t,index1,index2)
421 weight = real( (t-timelist(index1)) // (timelist(index2)-timelist(index1)), fms_ti_kind_)
422
423! time falls before starting list value
424 else if ( t < ts ) then
425 if (mtime == none) then
426 call time_list_error(t,terr)
427 call time_list_error(ts,tserr)
428 call time_list_error(te,teerr)
429 if(fms_error_handler('time_interp_list',&
430 'time '//trim(terr)//' ('//date_to_string(t)//' is before range of list '//trim(tserr)//'-'//trim(teerr)//&
431 '('//date_to_string(ts)//' - '//date_to_string(te)//')',&
432 err_msg)) return
433 deallocate(terr,tserr,teerr)
434 endif
435 td = te-ts
436 weight = 1.0_kindl - real(((ts-t) // (period-td)), fms_ti_kind_ )
437 index1 = n
438 index2 = 1
439
440! time falls on ending list value
441 else if ( t == te ) then
442 if(perthlike_behavior) then
443 weight = 1.0_kindl
444 index1 = n-1
445 index2 = n
446 else
447 weight = 0.0_kindl
448 index1 = n
449 if (mtime == none) then
450 index2 = n
451 else
452 index2 = 1
453 endif
454 endif
455
456! time falls after ending list value
457 else if ( t > te ) then
458 if (mtime == none) then
459 call time_list_error(t,terr)
460 call time_list_error(ts,tserr)
461 call time_list_error(te,teerr)
462 if(fms_error_handler('time_interp_list',&
463 'time '//trim(terr)//' ('//date_to_string(t)//' is after range of list '//trim(tserr)//'-'//trim(teerr)//&
464 '('//date_to_string(ts)//' - '//date_to_string(te)//')',&
465 err_msg)) return
466 deallocate(terr,tserr,teerr)
467 endif
468 td = te-ts
469 weight = real( (t-te) // (period-td), fms_ti_kind_)
470 index1 = n
471 index2 = 1
472 endif
473
474end subroutine time_interp_list_
475!> }
subroutine time_interp_frac_(time, weight)
Calculates the fractional time into the current year.
subroutine time_interp_modulo_(time, time_beg, time_end, timelist, weight, index1, index2, correct_leap_year_inconsistency, err_msg)
Part of the time_interp interface, calculates for cyclical data Time_beg and Time_end mark a repeatin...
subroutine time_interp_day_(time, weight, year1, year2, month1, month2, day1, day2)
Calculates fractional time between mid points of consecutive days.
subroutine time_interp_month_(time, weight, year1, year2, month1, month2)
Calculates fractional time between mid points of consecutive months.
subroutine time_interp_year_(time, weight, year1, year2)
Calculates fractional time between mid points of consecutive years.