1module timeCoord_mod
2 ! MODULE timeCoord_mod (prefix='tim' category='7. Low-level data objects')
3 !
4 !:Purpose: To store public variables and procedures related to the time
5 ! coordinate.
6 !
7 use midasMpi_mod
8 use varNameList_mod
9 use utilities_mod
10 implicit none
11 save
12 private
13
14 ! public variables
15 public :: tim_dstepobs, tim_dstepobsinc, tim_windowsize
16 public :: tim_nstepobs, tim_nstepobsinc, tim_referencetime
17 public :: tim_fullyUseExtremeTimeBins
18 ! public procedures
19 public :: tim_setup, tim_initialized
20 public :: tim_getDateStamp, tim_setDateStamp, tim_getStampList, tim_getStepObsIndex
21 public :: tim_getDateStampFromFile, tim_dateStampToYYYYMMDDHH, tim_getValidDateTimeFromList
22
23 character(len=4) :: varNameForDate
24 character(len=6) :: tim_referencetime
25 real(8) :: tim_dstepobs
26 real(8) :: tim_dstepobsinc
27 real(8) :: tim_windowsize
28 integer :: tim_nstepobs
29 integer :: tim_nstepobsinc
30 logical :: tim_fullyUseExtremeTimeBins
31 integer :: datestamp = 0 ! datestamp is usually the centre of time window
32 logical :: initialized = .false.
33
34 integer, external :: get_max_rss
35
36contains
37
38 subroutine tim_readNml()
39 !
40 !:Purpose: Read the namelist block NAMTIME.
41 !
42 !:Namelist parameters:
43 ! :dstepobs: time step (hrs) between successive trial fields
44 ! for use in OmP determation. Set to dwindowsize for
45 ! single trial field, i.e. use of 3dvar instead of 3dvar-FGAT.
46 ! nstepobs = number of trial fields
47 ! :dstepobsinc: time step (hrs) between obs groupings in time. Set to
48 ! dwindowsize for use of a single obs group.
49 ! nstepobsinc = number of obs time intervals
50 ! :dwindowsize: Time window size (hrs).
51 !
52 !:Comment:
53 !
54 ! Provided dates and number of provided trial field files must be
55 ! consistent with nstepobs, dstepobs and dwindowsize with reference datestamp
56 ! corresponding to the date of the middle trial field file.
57 !
58 implicit none
59
60 ! Locals:
61 integer :: nulnam, ierr, fnom, fclos
62 logical, save :: firstCall = .true.
63
64 ! Namelist variables:
65 real(8) :: dstepobs ! time step length for background state (in hours)
66 real(8) :: dstepobsinc ! time step length for increment and/or B matrix (in hours)
67 real(8) :: dwindowsize ! length of assimilation window (in hours)
68 character(len=6) :: referencetime ! location of 'date' within the window: 'middle' or 'start'
69 logical :: fullyUseExtremeTimeBins ! choose to use full-size bins at both ends of window (usually only half size)
70
71 NAMELIST /NAMTIME/dstepobs, dstepobsinc, dwindowsize, referencetime, fullyUseExtremeTimeBins
72
73 if (.not.firstCall) then
74 write(*,*) 'tim_readNml: already initialized, just return'
75 return
76 else
77 firstCall = .false.
78 end if
79
80 ! Set default values for namelist variables
81 dstepobs = 6.0d0
82 dstepobsinc = 6.0d0
83 dwindowsize = 6.0d0
84 referenceTime = 'middle'
85 fullyUseExtremeTimeBins = .false.
86
87 ! Read the namelist
88 nulnam = 0
89 ierr = fnom(nulnam, './flnml', 'FTN+SEQ+R/O', 0)
90 read(nulnam, nml=namtime, iostat=ierr)
91 if (ierr /= 0) call utl_abort('tim_readNml: Error reading namelist')
92 if (mmpi_myid == 0) write(*,nml=namtime)
93 ierr = fclos(nulnam)
94
95 ! Set the module variables for timestep length, number of timesteps and window length
96 tim_dstepobs = dstepobs
97 tim_dstepobsinc = dstepobsinc
98 tim_windowsize = dwindowsize
99 tim_referencetime = referenceTime
100 tim_fullyUseExtremeTimeBins = fullyUseExtremeTimeBins
101
102 if (tim_fullyUseExtremeTimeBins .and. tim_referencetime=="middle") then
103 write(*,*) "Warning: tim_fullyUseExtremeTimeBins==.true. and tim_referencetime=='middle' is a non-standard combination"
104 write(*,*) "Is it really what you want ?"
105 end if
106
107 if (dstepobs > dwindowsize) then
108 if (mmpi_myid == 0) write(*,*) 'tim_readNml: dstepobs>dwindowsize. Reset to dwindowsize value.'
109 tim_dstepobs = tim_windowsize
110 end if
111 if (dstepobsinc > dwindowsize) then
112 if (mmpi_myid == 0) write(*,*) 'tim_readNml: dstepobsinc>dwindowsize. Reset to dwindowsize value.'
113 tim_dstepobsinc = tim_windowsize
114 end if
115 if (tim_referencetime == "middle") then
116 tim_nstepobs = 2*nint(((tim_windowsize - tim_dstepobs)/2.d0)/tim_dstepobs) + 1
117 tim_nstepobsinc = 2*nint(((tim_windowsize - tim_dstepobsinc)/2.d0)/tim_dstepobsinc) + 1
118 end if
119
120 if (trim(tim_referencetime) == "start") then
121 tim_nstepobs = max(nint(tim_windowsize/tim_dstepobs), 1)
122 tim_nstepobsinc = max(nint(tim_windowsize/tim_dstepobsinc), 1)
123 end if
124
125 end subroutine tim_readNml
126
127
128 subroutine tim_getDateStampFromEnvVar(dateStamp)
129 !
130 !:Purpose: Determine the date from the environment variable MIDAS_DATE.
131 !
132 implicit none
133
134 ! Arguments:
135 integer, intent(inout) :: dateStamp
136
137 ! Locals:
138 integer :: lengthValidDateStr, status, datePrint, timePrint, imode, ierr
139 integer(8) :: dateTimePrint
140 character(len=256) :: validDateStr
141 integer :: newdate
142
143 status = 0
144 call get_environment_variable('MIDAS_DATE',validDateStr,lengthValidDateStr,status,.true.)
145
146 if (status > 1) then
147 call utl_abort('tim_getDateStampFromEnvVar: Problem when getting the environment variable MIDAS_DATE')
148 end if
149 if (status == 1) then
150 write(*,*) 'tim_getDateStampFromEnvVar: WARNING: The environment variable MIDAS_DATE has not been detected!'
151 !call utl_abort('tim_getDateStampFromEnvVar: The environment variable MIDAS_DATE has not been detected!')
152 return
153 end if
154
155 write(*,*)
156 write(*,*) 'tim_getDateStampFromEnvVar: The environment variable MIDAS_DATE has correctly been detected'
157
158 ! convert string to long integer
159 read (validDateStr,*) dateTimePrint
160
161 ! split dateTime long integer into separate date and time values
162 if (lengthValidDateStr == 10) then
163 datePrint = dateTimePrint/100
164 timePrint = (dateTimePrint - datePrint*100) * 1000000
165 else if (lengthValidDateStr == 12) then
166 datePrint = dateTimePrint/10000
167 timePrint = (dateTimePrint - datePrint*10000) * 10000
168 else if (lengthValidDateStr == 14) then
169 datePrint = dateTimePrint/1000000
170 timePrint = (dateTimePrint - datePrint*1000000) * 100
171 else if (lengthValidDateStr == 16) then
172 datePrint = dateTimePrint/100000000
173 timePrint = (dateTimePrint - datePrint*100000000)
174 else
175 write(*,*) 'length of MIDAS_DATE = ', lengthValidDateStr
176 call utl_abort('tim_getDateStampFromEnvVar: Unexpected length of variable MIDAS_DATE')
177 end if
178
179 ! convert to CMC dateStamp
180 imode = 3 ! printable to stamp
181 ierr = newdate(datestamp, datePrint, timePrint, imode)
182
183 write(*,*) 'tim_getDateStampFromEnvVar: envVar, validDate, dateStamp = ', trim(validDateStr), dateTimePrint, dateStamp
184
185 end subroutine tim_getDateStampFromEnvVar
186
187
188 subroutine tim_setup(fileNameForDate_opt)
189 !
190 !:Purpose: Setup of obs time window size and related trial field
191 ! time step for OmP determination.
192 !
193 implicit none
194
195 ! Arguments:
196 character(len=*), optional, intent(in) :: fileNameForDate_opt
197
198 ! Locals:
199 integer :: ierr, newdate, imode, prntdate, prnttime
200 integer :: dateStampEnvVar
201
202 call tim_readNml()
203
204 if (initialized) then
205 write(*,*) 'tim_setup: already initialized, just return'
206 return
207 end if
208
209 ! First try to set dateStamp from MIDAS_DATE
210 dateStampEnvVar = 0
211 call tim_getDateStampFromEnvVar(dateStampEnvVar)
212
213 ! Possibly set the datestamp (except when set later from burp files)
214 if (dateStampEnvVar /= 0) then
215 write(*,*) 'tim_setup: ====================================================='
216 write(*,*) 'tim_setup: DATESTAMP set by value in supplied MIDAS_DATE'
217 write(*,*) 'tim_setup: ====================================================='
218 dateStamp = dateStampEnvVar
219 imode = -3 ! stamp to printable
220 ierr = newdate(datestamp, prntdate, prnttime, imode)
221 write(*,*) 'tim_setup: printdate = ',prntdate
222 write(*,*) 'tim_setup: printtime = ',prnttime
223 write(*,*) 'tim_setup: datestamp = ',datestamp
224 else if (present(fileNameForDate_opt)) then
225 write(*,*) 'tim_setup: ====================================================='
226 write(*,*) 'tim_setup: DATESTAMP set by value in supplied file'
227 write(*,*) 'tim_setup: ====================================================='
228 datestamp = tim_getDatestampFromFile(fileNameForDate_opt)
229 imode = -3 ! stamp to printable
230 ierr = newdate(datestamp, prntdate, prnttime, imode)
231 write(*,*) 'tim_setup: printdate = ',prntdate
232 write(*,*) 'tim_setup: printtime = ',prnttime
233 write(*,*) 'tim_setup: datestamp = ',datestamp
234 else
235 write(*,*) 'tim_setup: =========================================================='
236 write(*,*) 'tim_setup: DATESTAMP not set in this subroutine, use tim_setDateStamp'
237 write(*,*) 'tim_setup: =========================================================='
238 end if
239
240 if (mmpi_myid == 0) write(*,*) 'tim_setup: dobs_windowsize=',tim_windowsize
241 if (mmpi_myid == 0) write(*,*) 'tim_setup: dstepobs =',tim_dstepobs
242 if (mmpi_myid == 0) write(*,*) 'tim_setup: nstepobs =',tim_nstepobs
243 if (mmpi_myid == 0) write(*,*) 'tim_setup: dstepobsinc =',tim_dstepobsinc
244 if (mmpi_myid == 0) write(*,*) 'tim_setup: nstepobsinc =',tim_nstepobsinc
245 if (mmpi_myid == 0) write(*,*) 'tim_setup: tim_referencetime =',tim_referencetime
246
247 initialized = .true.
248
249 end subroutine tim_setup
250
251
252 function tim_initialized() result(initialized_out)
253 implicit none
254
255 ! Result:
256 logical initialized_out
257
258 initialized_out = initialized
259
260 end function tim_initialized
261
262
263 function tim_getDatestampFromFile(fileName, varNameForDate_opt) result(dateStamp_out)
264 !
265 !:Purpose: to extract the dateStamp from the supplied file.
266 !
267 implicit none
268
269 ! Arguments:
270 character(len=*), intent(in) :: fileName
271 character(len=*), optional, intent(in) :: varNameForDate_opt
272 ! Result:
273 integer :: dateStamp_out
274
275 ! Locals:
276 integer :: nulFile, ierr
277 integer, parameter :: maxNumDates = 2000
278 integer :: numDates, ikeys(maxNumDates), varIndex
279 integer :: fnom, fstouv, fstinl, fstprm, fstfrm, fclos, newdate
280 integer :: prntdate, prnttime, imode, windowIndex, windowsPerDay, dateStamp_tmp
281 logical :: fileExists, foundWindow, foundVarNameInFile
282 real(8) :: leadTimeInHours, windowBegHour, windowEndHour, fileHour, middleHour
283 integer :: ideet, inpas, dateStamp_origin, ini, inj, ink, inbits, idatyp
284 integer :: ip1, ip2, ip3, ig1, ig2, ig3, ig4, iswa, ilng, idltf, iubc
285 integer :: iextra1, iextra2, iextra3
286 character(len=2) :: typvar
287 character(len=4) :: nomvar
288 character(len=12) :: etiket
289 character(len=1) :: grtyp
290
291 if (mmpi_myid == 0) then
292
293 ! Check if file for any date within the analysis window (except the last) exists
294 inquire(file=trim(fileName), exist=fileExists)
295 if (.not.fileExists) then
296 call utl_abort('tim_getDateStampFromFile: file not found '//trim(fileName))
297 end if
298
299 ! Determine variable to use for the date (default is P0)
300 varNameForDate = 'P0'
301 if (present(varNameForDate_opt)) then
302
303 varNameForDate = trim(varNameForDate_opt)
304 write(*,*) 'tim_getDateStampFromFile: defining dateStamp from the variable = ', varNameForDate
305
306 ! If P0 not present, look for another suitable variable in the file
307 else if (.not. utl_varNamePresentInFile(varNameForDate,fileName_opt=trim(fileName))) then
308
309 foundVarNameInFile = .false.
310 do varIndex = 1, vnl_numvarmax
311 varNameForDate = vnl_varNameList(varIndex)
312 ! check if variable is in the file
313 if (.not. utl_varNamePresentInFile(varNameForDate,fileName_opt=trim(fileName))) cycle
314 foundVarNameInFile = .true.
315 exit
316 end do
317
318 if (.not. foundVarNameInFile) then
319 call utl_abort('tim_getDateStampFromFile: NO variables found in the file!!!')
320 end if
321
322 end if
323
324 ! Extract the datestamp from the file
325 nulFile = 0
326 ierr = fnom(nulFile,trim(fileName),'RND+OLD+R/O',0)
327 ierr = fstouv(nulFile,'RND+OLD')
328 ierr = fstinl(nulFile,ini,inj,ink,-1,' ',-1,-1,-1,' ', &
329 trim(varNameForDate),ikeys,numdates,maxnumdates)
330 if (ikeys(1) <= 0) then
331 call utl_abort('tim_getDateStampFromFile: Could not find variable ' // &
332 trim(varNameForDate) // ' in the supplied file')
333 end if
334 write(*,*) 'tim_getDateStampFromFile: number of dates found = ', numDates
335 ierr = fstprm(ikeys(1), dateStamp_origin, ideet, inpas, ini, inj, &
336 ink, inbits, idatyp, ip1, ip2, ip3, &
337 typvar, nomvar, etiket, grtyp, ig1, ig2, ig3, ig4, &
338 iswa, ilng, idltf, iubc, iextra1, iextra2, iextra3)
339 leadTimeInHours = real(ideet*inpas,8)/3600.0d0
340 call incdatr(dateStamp_out, dateStamp_origin, leadTimeInHours)
341
342 ierr = fstfrm(nulFile)
343 ierr = fclos(nulFile)
344
345 end if
346
347 call rpn_comm_bcast(dateStamp_out, 1, 'MPI_INTEGER', 0, 'GRID', ierr)
348
349 if (tim_referenceTime == 'middle') then
350 ! Modify date to ensure that it corresponds to the middle of the window
351 ! Note: For this, we have to assume that the date in the file
352 ! does NOT correspond to the final time of the window
353 imode = -3 ! stamp to printable, time is HHMMSShh
354 ierr = newdate(datestamp_out, prntdate, prnttime, imode)
355 fileHour = real(prnttime,8)/1000000.0d0
356 windowsPerDay = nint(24.0d0 / tim_windowsize)
357 foundWindow = .false.
358 window_loop: do windowIndex = 0, windowsPerDay
359 windowBegHour = (real(windowIndex,8) * tim_windowsize) - (tim_windowsize/2.0d0)
360 windowEndHour = (real(windowIndex,8) * tim_windowsize) + (tim_windowsize/2.0d0)
361 if (fileHour >= windowBegHour .and. fileHour < windowEndHour) then
362 foundWindow = .true.
363 middleHour = real(windowIndex,8) * tim_windowsize
364 exit window_loop
365 end if
366 end do window_loop
367
368 if (.not. foundWindow) then
369 write(*,*) 'windowsPerDay, fileHour=', windowsPerDay, fileHour
370 call utl_abort('tim_getDateStampFromFile: could not determine assimilation window position')
371 end if
372
373 ! handle special case when window centered on hour 24
374 if (nint(middleHour) == 24) then
375 ! add 24h to dateStamp and recompute prntdate
376 dateStamp_tmp = dateStamp_out
377 call incdatr(dateStamp_out, dateStamp_tmp, 24.0d0)
378 imode = -3 ! stamp to printable, only prntdate will be used
379 ierr = newdate(datestamp_out, prntdate, prnttime, imode)
380
381 ! subtract 24h from middleHour
382 middleHour = 0.0d0
383 end if
384
385 prnttime = nint(middleHour) * 1000000
386 imode = 3 ! printable to stamp
387 ierr = newdate(datestamp_out, prntdate, prnttime, imode)
388
389 end if
390
391 end function tim_getDateStampFromFile
392
393
394 subroutine tim_setDatestamp(datestamp_in)
395 !
396 !:Purpose: to control access to the minimization object. Sets the date
397 ! of the window centre of analysis validity to the indicated value.
398 !
399 implicit none
400
401 ! Arguments:
402 integer, intent(in) :: datestamp_in
403
404 if (.not.initialized) call utl_abort('tim_setDateStamp: module not initialized')
405
406 datestamp = datestamp_in
407
408 end subroutine tim_setDatestamp
409
410
411 function tim_getDatestamp() result(datestamp_out)
412 !
413 !:Purpose: to control access to the minimization object. Returns the date
414 ! of the window centre of analysis validity.
415 !
416 implicit none
417
418 ! Result:
419 integer :: datestamp_out
420
421 if (.not.initialized) call utl_abort('tim_getDateStamp: module not initialized')
422
423 datestamp_out = datestamp
424
425 end function tim_getDatestamp
426
427
428 subroutine tim_getStampList(datestamplist, numStep, referenceDateStamp)
429 !
430 !:Purpose: Compute a list of STAMPS corresponding to stepobs time
431 !
432 implicit none
433
434 ! Arguments:
435 integer, intent(in) :: numStep ! number of step obs
436 integer, intent(in) :: referenceDateStamp ! Synoptic time
437 integer, intent(out) :: datestamplist(numStep)
438
439 ! Locals:
440 integer :: stepIndex
441 real(8) :: dldelt ! delta time in hours between middle time and each step
442 real(8) :: dtstep ! delta time in hours between step obs
443
444 if (.not. initialized) call utl_abort('tim_getStampList: module not initialized')
445
446 if (referenceDateStamp == -1) then
447
448 if (mmpi_myid == 0) write(*,*) 'tim_getStampList: datestamp is not specified, keep as -1'
449 datestamplist(:) = -1
450
451 else
452
453 if (tim_referencetime == "middle") then
454 if (numStep > 1) then
455 dtstep = tim_windowsize/(real(numStep-1,8))
456 else
457 dtstep = tim_windowsize
458 end if
459
460 do stepIndex = 1, numStep
461 dldelt = (stepIndex - ((numStep-1)/2 + 1)) * dtstep
462 call incdatr(datestamplist(stepIndex), referenceDateStamp, dldelt)
463 end do
464 end if
465
466 if (trim(tim_referencetime) == "start") then
467
468 dtstep = tim_windowsize/(real(numStep,8))
469
470 do stepIndex = 1, numStep
471 dldelt = (stepIndex - 1) * dtstep
472 call incdatr(datestamplist(stepIndex), referenceDateStamp, dldelt)
473 end do
474
475 end if
476
477 end if
478
479 end subroutine tim_getStampList
480
481
482 subroutine tim_getStepObsIndex(dnstepobs, referenceDateStamp, obsYYYMMDD, obsHHMM, numStep)
483 !
484 !:Purpose: Return the stepobs index as a real number (-1.0 if out of range)
485 !
486 implicit none
487
488 ! Arguments:
489 real(8), intent(out) :: dnstepobs ! number of stepobs from reference time
490 integer, intent(in) :: referenceDateStamp ! Synop CMC date-time stamp
491 integer, intent(in) :: obsYYYMMDD ! Obs date YYYYMMDD
492 integer, intent(in) :: obsHHMM ! Obs time HHMM
493 integer, intent(in) :: numStep ! number of stepobs in assimilation window
494
495 ! Locals:
496 integer :: newdate, istat, imode
497 real(8) :: dddt ! delta time in hours
498 integer :: istobs ! obs CMC date-time stamp
499 integer :: itobs ! obs time HHMMSShh
500 real(8) :: dlhours ! delta time from synop time
501
502 if (.not. initialized) call utl_abort('tim_getStepObsIndex: module not initialized')
503
504 ! Building observation stamp
505 imode = 3 ! printable to stamp
506 itobs = obsHHMM * 10000
507 istat = newdate(istobs, obsYYYMMDD, itobs, imode)
508
509 ! Difference (in hours) between obs time and reference time
510 call difdatr(istobs, referenceDateStamp, dlhours)
511
512 if (numStep > 1) then
513 ! FGAT: more than 1 trial field in time window
514 if (tim_referencetime == "middle") then
515 dddt = tim_windowsize / (real(numStep-1,8))
516 else
517 dddt = tim_windowsize / (real(numStep,8))
518 end if
519 dnstepobs = dlhours / dddt ! number of step obs from reference (e.g. synoptic)
520 if (tim_referencetime == "middle") then
521 dnstepobs = dnstepobs + real((numStep+1)/2,8)
522 end if
523 if (trim(tim_referencetime) == "start") then
524 dnstepobs = dnstepobs + 1.d0
525 end if
526 if (dnstepobs < 0.5d0.or.dnstepobs > (0.5d0+real(numStep,8))) dnstepobs = -1.0d0
527
528 else
529 ! 3D: only 1 trial field in time window
530 if (tim_referencetime == "middle") then
531 if (dlhours < -tim_windowsize/2.0D0 .or. dlhours > tim_windowsize/2.0D0) then
532 ! outside time window
533 dnstepobs = -1.0d0
534 else
535 ! inside time window
536 dnstepobs = 1.0d0
537 end if
538 else
539 dddt = tim_windowsize
540 if (dlhours < -dddt/2.0d0 .or. dlhours > tim_windowsize + dddt/2.d0) then
541 ! outside time window
542 dnstepobs = -1.0d0
543 else
544 ! inside time window
545 dnstepobs = 1.0d0
546 end if
547 endif
548
549 end if
550
551 end subroutine tim_getStepObsIndex
552
553 !----------------------------------------------------------------------------------------
554 ! tim_dateStampToDDMMYYYY
555 !----------------------------------------------------------------------------------------
556 subroutine tim_dateStampToYYYYMMDDHH(dateStamp, prnttime, dd, mm, ndays, yyyy, verbose_opt)
557 !
558 !:Purpose: to get day (DD), month (MM), number of days in this month
559 ! and year (YYYY) from dateStamp
560 !
561 implicit none
562
563 ! Arguments:
564 integer, intent(in) :: dateStamp
565 integer, intent(inout) :: prnttime, dd, mm, ndays, yyyy
566 logical, optional, intent(in) :: verbose_opt
567
568 ! Locals:
569 character(len=8) :: yyyymmdd
570 character(len=3), parameter :: months(12) = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
571 integer :: ndaysM(12)
572 integer :: imode, ierr, newdate, prntdate
573 logical :: verbose = .True.
574
575 ndaysM(:) = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
576
577 imode = -3 ! stamp to printable
578 ierr = newdate(dateStamp, prntdate, prnttime, imode)
579 write(yyyymmdd,'(i8)') prntdate
580 read (yyyymmdd(7:8), '(i)') dd
581 read (yyyymmdd(5:6), '(i)') mm
582 read (yyyymmdd(1:4), '(i)') yyyy
583
584 ! leap year
585 if (mm == 2 .and. mod(yyyy,4)==0) ndaysM(mm) = 29
586 ndays = ndaysM(mm)
587
588 if (present(verbose_opt)) verbose = verbose_opt
589
590 if(verbose) then
591 write(*,*) 'tim_dateStampToYYYYMMDDHH: date = ', prntdate
592 write(*,*) 'tim_dateStampToYYYYMMDDHH: year = ', yyyy
593 write(*,'(a,i5,a,i5,a)') 'tim_dateStampToYYYYMMDDHH: month = ', mm, ' ( '// months(mm)//' where there are ', ndays, ' days)'
594 write(*,*) 'tim_dateStampToYYYYMMDDHH: day = ', dd
595 write(*,*) 'tim_dateStampToYYYYMMDDHH: time = ', prnttime
596 end if
597
598 end subroutine tim_dateStampToYYYYMMDDHH
599
600 !----------------------------------------------------------------------------------------
601 ! tim_getValidDateTimeFromList
602 !----------------------------------------------------------------------------------------
603 subroutine tim_getValidDateTimeFromList(headDateValues, headTimeValues, validDate, validtime)
604 implicit none
605
606 ! Arguments:
607 integer, intent(in) :: headDateValues(:)
608 integer, intent(in) :: headTimeValues(:)
609 integer, intent(out) :: validDate
610 integer, intent(out) :: validTime
611
612 ! Locals:
613 integer :: numDates, numWindowsPerDay, windowIndex, timeMin, timeMax, dateMin, dateMax
614 integer :: windowBoundaryMin, windowBoundaryMax, validTimeMin, validTimeMax, validDateMin, validDateMax
615 integer(8) :: dateTimeMin, dateTimeMax
616 integer(8), allocatable :: dateTimeValues(:), windowBoundaries(:)
617 integer :: ier, imode
618 integer :: newdate, dateStampIn, dateStampOut
619
620 call tim_readNml()
621
622 numDates = size(headDateValues)
623 checkNumDates: if (numDates > 0) then
624 write(*,*) 'tim_getValidDateTimeFromList: check inputs: time min/max = ', minval(headTimeValues), maxval(headTimeValues)
625 write(*,*) 'tim_getValidDateTimeFromList: check inputs: date min/max = ', minval(headDateValues), maxval(headDateValues)
626 allocate(dateTimeValues(size(headDateValues)))
627 dateTimeValues(:) = headDateValues(:)
628 dateTimeValues(:) = 10000*dateTimeValues(:) + headTimeValues(:)
629
630 dateTimeMin = minval(dateTimeValues(:))
631 dateTimeMax = maxval(dateTimeValues(:))
632 deallocate(dateTimeValues)
633 dateMin = dateTimeMin/10000
634 dateMax = dateTimeMax/10000
635 timeMin = dateTimeMin - 10000*(dateTimeMin/10000)
636 timeMax = dateTimeMax - 10000*(dateTimeMax/10000)
637 ! convert from hhmm to just minutes: hhmm - 100*(hhmm/100) + 60*(hhmm/100)
638 timeMin = timeMin - 100*(timeMin/100) + 60*(timeMin/100)
639 timeMax = timeMax - 100*(timeMax/100) + 60*(timeMax/100)
640 write(*,*) 'tim_getValidDateTimeFromList: min/max DateTime = ', dateTimeMin, dateTimeMax
641 write(*,*) 'tim_getValidDateTimeFromList: min/max time (in minutes) = ', timeMin, timeMax
642 if (tim_windowSize < 24.0d0) then
643 numWindowsPerDay = nint(24.0/tim_windowSize)
644
645 ! define boundaries between assimilation windows in minutes relative to 0UTC
646 allocate(windowBoundaries(0:numWindowsPerDay))
647 do windowIndex = 0, numWindowsPerDay
648 ! example for windowSize=6h, boundaries = -3h,+3h,+9h,+15h,+21h
649 windowBoundaries(windowIndex) = nint(windowIndex*60.0*tim_windowSize - 60.0*tim_windowSize/2.0)
650 end do
651 write(*,*) 'tim_getValidDateTimeFromList: boundaries (in minutes) = ', windowBoundaries(:)
652
653 ! find left boundary of window where timeMin/Max are located
654 windowBoundaryMin = -1
655 windowBoundaryMax = -1
656 do windowIndex = 0, numWindowsPerDay
657 if (timeMin >= windowBoundaries(windowIndex)) then
658 windowBoundaryMin = windowIndex
659 end if
660 if (timeMax >= windowBoundaries(windowIndex)) then
661 windowBoundaryMax = windowIndex
662 end if
663 end do
664
665 ! find validTimeMin/Max from left boundary
666 validTimeMin = nint((windowBoundaries(windowBoundaryMin) + 60.0*tim_windowSize/2.0)/60.0)
667 if (validTimeMin >= 24) then
668 validTimeMin = 0
669 imode = 3
670 ier = newdate(dateStampIn, dateMin, validTimeMin, imode)
671 call incdat(dateStampOut, dateStampIn, 24) ! add 1 day to get validDate
672 imode = -3
673 ier = newdate(dateStampOut, validDateMin, validTimeMin, imode)
674 validTimeMin = 0
675 else
676 validDateMin = dateMin
677 end if
678 validTimeMax = nint((windowBoundaries(windowBoundaryMax) + 60.0*tim_windowSize/2.0)/60.0)
679 if (validTimeMax >= 24) then
680 validTimeMax = 0
681 imode = 3
682 ier = newdate(dateStampIn, dateMax, validTimeMax, imode)
683 call incdat(dateStampOut, dateStampIn, 24) ! add 1 day to get validDate
684 imode = -3
685 ier = newdate(dateStampOut, validDateMax, validTimeMax, imode)
686 validTimeMax = 0
687 else
688 validDateMax = dateMax
689 end if
690 write(*,*) 'tim_getValidDateTimeFromList: date from Min/Max = ', validDateMin, validDateMax
691 write(*,*) 'tim_getValidDateTimeFromList: hour from Min/Max = ', validTimeMin, validTimeMax
692 if (validTimeMin /= validTimeMax) call utl_abort('validTimeMin/Max not equal')
693 validTime = validTimeMin
694 validDate = validDateMin
695 deallocate(windowBoundaries)
696 else
697 write(*,*) 'tim_getValidDateTimeFromList: WARNING: window size equal or greater than 1 day, cannot get dateStamp'
698 validTime = 0
699 validDate = 0
700 end if
701 end if checkNumDates
702
703 end subroutine tim_getValidDateTimeFromList
704
705end module timeCoord_mod