1 RECURSIVE SUBROUTINE adapt_timestep(grid, config_flags)
3 !--------------------------------------------------------------------------
7 ! This routine sets the time step based on the cfl condition. It's used to
8 ! dynamically adapt the timestep as the model runs.
15 !--------------------------------------------------------------------------
17 ! Driver layer modules
20 USE module_dm, ONLY : wrf_dm_maxval, wrf_dm_minval, wrf_dm_mintile_double, wrf_dm_tile_val_int, wrf_dm_maxtile_real
25 TYPE(domain) , TARGET , INTENT(INOUT) :: grid
26 TYPE (grid_config_rec_type) , INTENT(IN) :: config_flags
30 REAL :: max_increase_factor
31 REAL :: time_to_output, &
33 INTEGER :: idex=0, jdex=0
35 TYPE(WRFU_TimeInterval) :: tmpTimeInterval, dtInterval
36 TYPE(WRFU_TimeInterval) :: dtInterval_horiz
37 TYPE(WRFU_TimeInterval) :: dtInterval_vert
38 TYPE(WRFU_TimeInterval) :: parent_dtInterval
39 INTEGER :: num_small_steps
41 LOGICAL :: stepping_to_bc
42 INTEGER :: bc_time, output_time
43 double precision :: dt = 0
44 INTEGER, PARAMETER :: precision = 100
45 INTEGER :: dt_num, dt_den, dt_whole
46 INTEGER :: num, den, history_interval_sec
47 TYPE(WRFU_TimeInterval) :: last_dtInterval
49 REAL :: max_vert_cfl, max_horiz_cfl
52 ! If use_last2 is true, this routine will use the time step
53 ! from 2 steps ago to compute the next time step. This
54 ! is used along with step_to_output and step_to_bc
59 ! Assign last_dtInterval type values from restart file
62 CALL WRFU_TimeIntervalSet(grid%last_dtInterval, S=grid%last_dt_sec, &
63 Sn=grid%last_dt_sec_num, Sd=grid%last_dt_sec_den)
66 ! If this step has already been adapted, no need to do it again.
67 ! time step can already be adapted when adaptation_domain is
71 if (grid%last_step_updated == grid%itimestep) then
74 grid%last_step_updated = grid%itimestep
78 ! For nests, set adapt_step_using_child to parent's value
80 if (grid%id .ne. 1) then
81 grid%adapt_step_using_child = grid%parents(1)%ptr%adapt_step_using_child;
85 ! For nests, if we're not adapting using child nest, we only want to change
86 ! nests' time steps when the time is conincident with the parent's time.
87 ! So, if dtbc is not zero, simply return and leave the last time step in
91 ! if ((grid%id .ne. 1) .and. (.not. grid%adapt_step_using_child)) then
92 ! if (abs(grid%dtbc) > 0.0001) then
97 last_dtInterval = grid%last_dtInterval
100 ! Get time since beginning of simulation start
103 tmpTimeInterval = domain_get_current_time ( grid ) - &
104 domain_get_start_time ( grid )
107 ! Calculate current time in seconds since beginning of model run.
108 ! Unfortunately, ESMF does not seem to have a way to return
109 ! floating point seconds based on a TimeInterval. So, we will
110 ! calculate it here--but, this is not clean!!
112 curr_secs = real_time(tmpTimeInterval)
115 ! Calculate the maximum allowable increase in the time step given
116 ! the user input max_step_increase_pct value and the nest ratio.
118 max_increase_factor = 1. + grid%max_step_increase_pct / 100.
121 ! If this is the first time step of the model run (indicated by time step #1),
122 ! then set the time step to the input starting_time_step.
124 ! Else, calculate the time step based on cfl.
127 !At the initial time advanceCount == 0, but the following line instead looked
128 !for advanceCount == 1
129 !if ( ( domain_get_advanceCount ( grid ) .EQ. 1 ) .AND. ( .NOT. config_flags%restart ) ) then
130 if ( ( domain_get_advanceCount ( grid ) .EQ. 0 ) .AND. ( .NOT. config_flags%restart ) ) then
132 if ( grid%starting_time_step_den .EQ. 0 ) then
133 CALL WRFU_TimeIntervalSet(dtInterval, Sn=grid%starting_time_step, Sd=1)
135 CALL WRFU_TimeIntervalSet(dtInterval, Sn=grid%starting_time_step, Sd=grid%starting_time_step_den)
138 CALL WRFU_TimeIntervalSet(last_dtInterval, Sn=0, Sd=1)
142 if (grid%stepping_to_time) then
143 max_vert_cfl = grid%last_max_vert_cfl
144 max_horiz_cfl = grid%last_max_horiz_cfl
146 max_vert_cfl = grid%max_vert_cfl
147 max_horiz_cfl = grid%max_horiz_cfl
150 CALL calc_dt(dtInterval_vert, max_vert_cfl, max_increase_factor, &
151 precision, last_dtInterval, grid%target_cfl)
153 CALL calc_dt(dtInterval_horiz, max_horiz_cfl, max_increase_factor, &
154 precision, last_dtInterval, grid%target_hcfl)
156 if (dtInterval_vert < dtInterval_horiz) then
157 dtInterval = dtInterval_vert
159 dtInterval = dtInterval_horiz
164 ! Limit the increase of dtInterval to the specified input limit
166 num = NINT( max_increase_factor * precision )
168 tmpTimeInterval = last_dtInterval * num / den
169 if ( (domain_get_current_time ( grid ) .ne. domain_get_start_time ( grid )) &
170 .and. (dtInterval .gt. tmpTimeInterval ) ) then
171 dtInterval = tmpTimeInterval
175 ! Here, we round off dtInterval to nearest 1/100. This prevents
176 ! the denominator from getting too large and causing overflow.
178 dt = real_time(dtInterval)
179 num = NINT(dt * precision)
181 CALL WRFU_TimeIntervalSet(dtInterval, Sn=num, Sd=den)
183 ! Limit the maximum dtInterval based on user input
185 if ( grid%max_time_step_den .EQ. 0 ) then
186 CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%max_time_step, Sd=1)
188 CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%max_time_step, Sd=grid%max_time_step_den)
190 if (dtInterval .gt. tmpTimeInterval ) then
191 dtInterval = tmpTimeInterval
194 ! Limit the minimum dtInterval based on user input.
196 if ( grid%min_time_step_den .EQ. 0 ) then
197 CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%min_time_step, Sd=1)
199 CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%min_time_step, Sd=grid%min_time_step_den)
201 if (dtInterval .lt. tmpTimeInterval ) then
202 dtInterval = tmpTimeInterval
206 ! Now, if this is a nest, and we are adapting based upon parent,
207 ! we round down the time step to the nearest
208 ! value that divides evenly into the parent time step.
209 ! If this is a nest, and we are adapting based upon the child (i.e., the
210 ! nest), we update the parent timestep to the next smallest multiple
213 if (grid%nested) then
215 dt = real_time(dtInterval)
217 if (.not. grid%adapt_step_using_child) then
219 ! We'll calculate real numbers to get the number of small steps:
221 num_small_steps = CEILING( grid%parents(1)%ptr%dt / dt )
224 call wrf_dm_maxval(num_small_steps, idex, jdex)
226 dtInterval = domain_get_time_step(grid%parents(1)%ptr) / &
230 num_small_steps = FLOOR( grid%parents(1)%ptr%dt / dt )
233 call wrf_dm_minval(num_small_steps, idex, jdex)
235 if (num_small_steps < 1) then
244 ! Setup the values for several variables from the tile with the
247 dt = real_time(dtInterval)
250 call wrf_dm_mintile_double(dt, tile)
251 CALL WRFU_TimeIntervalGet(dtInterval,Sn=dt_num,Sd=dt_den,S=dt_whole)
252 call wrf_dm_tile_val_int(dt_num, tile)
253 call wrf_dm_tile_val_int(dt_den, tile)
254 call wrf_dm_tile_val_int(dt_whole, tile)
255 CALL WRFU_TimeIntervalSet(dtInterval, Sn = dt_whole*dt_den + dt_num, Sd = dt_den)
257 call wrf_dm_maxtile_real(grid%max_vert_cfl, tile)
258 call wrf_dm_maxtile_real(grid%max_horiz_cfl, tile)
261 if ((grid%nested) .and. (grid%adapt_step_using_child)) then
263 grid%dt = real_time(dtInterval)
265 ! Set parent step here.
266 grid%parents(1)%ptr%dt = grid%dt * num_small_steps
267 parent_dtInterval = dtInterval * num_small_steps
270 ! Update the parent clock based on the new time step
272 CALL WRFU_ClockSet ( grid%parents(1)%ptr%domain_clock, &
273 timeStep=parent_dtInterval, &
280 ! Assure that we fall on a BC time. Due to a bug in WRF, the time
281 ! step must fall on the boundary times. Only modify the dtInterval
282 ! when this is not the first time step on this domain.
285 grid%stepping_to_time = .FALSE.
286 time_to_bc = grid%interval_seconds - grid%dtbc
287 num = INT(time_to_bc * precision + 0.5)
289 CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=num, Sd=den)
291 if ( ( tmpTimeInterval .LT. dtInterval * 2 ) .and. &
292 ( tmpTimeInterval .GT. dtInterval ) ) then
293 dtInterval = tmpTimeInterval / 2
296 stepping_to_bc = .true.
297 grid%stepping_to_time = .TRUE.
299 elseif (tmpTimeInterval .LE. dtInterval) then
301 bc_time = NINT ( (curr_secs + time_to_bc) / ( grid%interval_seconds ) ) &
302 * ( grid%interval_seconds )
303 CALL WRFU_TimeIntervalSet(tmpTimeInterval, S=bc_time)
304 dtInterval = tmpTimeInterval - &
305 (domain_get_current_time(grid) - domain_get_start_time(grid))
308 stepping_to_bc = .true.
309 grid%stepping_to_time = .TRUE.
311 stepping_to_bc = .false.
315 ! If the user has requested that we step to output, then
316 ! assure that we fall on an output time. We look out two time steps to
317 ! avoid having a very short time step. Very short time steps can cause model
321 if ((grid%step_to_output_time) .and. (.not. stepping_to_bc) .and. &
322 (.not. grid%nested)) then
324 IF ( grid%history_interval_m .EQ. 0 ) grid%history_interval_m = grid%history_interval
325 history_interval_sec = grid%history_interval_s + grid%history_interval_m*60 + &
326 grid%history_interval_h*3600 + grid%history_interval_d*86400
328 time_to_output = history_interval_sec - &
329 mod( curr_secs, REAL(history_interval_sec) )
330 num = INT(time_to_output * precision + 0.5)
332 call WRFU_TimeIntervalSet(tmpTimeInterval, Sn=num, Sd=den)
334 if ( ( tmpTimeInterval .LT. dtInterval * 2 ) .and. &
335 ( tmpTimeInterval .GT. dtInterval ) ) then
336 dtInterval = tmpTimeInterval / 2
338 grid%stepping_to_time = .TRUE.
340 elseif (tmpTimeInterval .LE. dtInterval) then
342 ! We will do some tricks here to assure that we fall exactly on an
343 ! output time. Without the tricks, round-off error causes problems!
347 ! Calculate output time. We round to nearest history time to assure
348 ! we don't have any rounding error.
350 output_time = NINT ( (curr_secs + time_to_output) / &
351 (history_interval_sec) ) * (history_interval_sec)
352 CALL WRFU_TimeIntervalSet(tmpTimeInterval, S=output_time)
353 dtInterval = tmpTimeInterval - &
354 (domain_get_current_time(grid) - domain_get_start_time(grid))
357 grid%stepping_to_time = .TRUE.
362 ! Now, set adapt_step_using_child only if we are not stepping to an
363 ! output time, or, it's not the start of the model run.
364 ! Note: adapt_step_using_child is updated just before recursive call to
365 ! adapt_timestep--see end of this function.
368 if (grid%id == 1) then
369 if ((grid%adaptation_domain > 1) .and. &
370 (grid%max_dom == 2) .and. &
371 (.not. grid%stepping_to_time) .and. &
372 (domain_get_current_time(grid) .ne. &
373 domain_get_start_time(grid)) &
376 grid%adapt_step_using_child = .TRUE.
378 grid%adapt_step_using_child = .FALSE.
384 grid%last_dtInterval = last_dtInterval
385 grid%last_max_vert_cfl = grid%last_max_vert_cfl
386 grid%last_max_horiz_cfl = grid%last_max_horiz_cfl
388 grid%last_dtInterval = dtInterval
389 grid%last_max_vert_cfl = grid%max_vert_cfl
390 grid%last_max_horiz_cfl = grid%max_horiz_cfl
393 grid%dt = real_time(dtInterval)
395 grid%last_max_vert_cfl = grid%max_vert_cfl
398 ! Update the clock based on the new time step
400 CALL WRFU_ClockSet ( grid%domain_clock, &
401 timeStep=dtInterval, &
405 ! If we're are adapting based on the child time step,
406 ! we call the child from here. This assures that
407 ! child and parent are updated in sync.
408 ! Note: This is not necessary when we are adapting based
411 if ((grid%id == 1) .and. (grid%adapt_step_using_child)) then
413 ! Finally, check if we can adapt using child. If we are
414 ! stepping to an output time, we cannot adapt based upon
415 ! child. So, we reset the variable before calling the child.
416 ! This covers the case that, within this parent time-step that
417 ! we just calculated, we are stepping to an output time.
419 if (grid%stepping_to_time) then
420 grid%adapt_step_using_child = .FALSE.
422 call adapt_timestep(grid%nests(1)%ptr, config_flags)
426 ! Lateral boundary weight recomputation based on time step.
428 if (grid%id == 1) then
429 CALL lbc_fcx_gcx ( grid%fcx , grid%gcx , grid%spec_bdy_width , &
430 grid%spec_zone , grid%relax_zone , grid%dt , config_flags%spec_exp , &
431 config_flags%specified , config_flags%nested )
434 ! Update last timestep info for restart file
436 CALL WRFU_TimeIntervalGet(grid%last_dtInterval, S=grid%last_dt_sec, &
437 Sn=grid%last_dt_sec_num, Sd=grid%last_dt_sec_den)
439 END SUBROUTINE adapt_timestep
441 SUBROUTINE calc_dt(dtInterval, max_cfl, max_increase_factor, precision, &
442 last_dtInterval, target_cfl)
446 TYPE(WRFU_TimeInterval) ,INTENT(OUT) :: dtInterval
447 REAL ,INTENT(IN) :: max_cfl
448 REAL ,INTENT(IN) :: max_increase_factor
449 INTEGER ,INTENT(IN) :: precision
450 REAL ,INTENT(IN) :: target_cfl
451 TYPE(WRFU_TimeInterval) ,INTENT(IN) :: last_dtInterval
456 if (max_cfl < 0.001) then
458 ! If the max_cfl is small, then we increase dtInterval the maximum
461 num = INT(max_increase_factor * precision + 0.5)
463 dtInterval = last_dtInterval * num / den
467 ! If the max_cfl is greater than the user input target cfl, we
468 ! reduce the time step,
469 ! else, we increase it.
471 if (max_cfl .gt. target_cfl) then
473 ! If we are reducing the time step, we go below target cfl by half
474 ! the difference between max and target.
475 ! This tends to keep the model more stable.
478 factor = ( target_cfl - 0.5 * (max_cfl - target_cfl) ) / max_cfl
481 ! Factor can be negative in some cases so prevent factor from being
483 ! Otherwise model crashes can occur in normalize_basetime noting that
484 ! denominator of seconds cannot be negative
485 factor = MAX(0.1,factor)
488 num = INT(factor * precision + 0.5)
491 dtInterval = last_dtInterval * num / den
495 ! Linearly increase dtInterval (we'll limit below)
498 factor = target_cfl / max_cfl
499 num = INT(factor * precision + 0.5)
501 dtInterval = last_dtInterval * num / den
505 END SUBROUTINE calc_dt
508 FUNCTION real_time( timeinterval ) RESULT ( out_time )
514 ! This function returns a floating point time from an input time interval
516 ! Unfortunately, the ESMF did not provide this functionality.
518 ! Be careful with the output because, due to rounding, the time is only
521 ! Todd Hutchinson, WSI
526 INTEGER :: dt_num, dt_den, dt_whole
529 TYPE(WRFU_TimeInterval), intent(INOUT) :: timeinterval
531 CALL WRFU_TimeIntervalGet(timeinterval,Sn=dt_num,Sd=dt_den,S=dt_whole)
532 if (ABS(dt_den) < 1) then
535 out_time = dt_whole + dt_num / REAL(dt_den)
539 FUNCTION real_time_r8( timeinterval ) RESULT ( out_time )
545 ! This function returns a double precision floating point time from an input time interval
547 ! Unfortunately, the ESMF did not provide this functionality.
549 ! Be careful with the output because, due to rounding, the time is only
552 ! Todd Hutchinson, WSI 4/17/2007
553 ! Converted to r8, William.Gustafson@pnl.gov; 8-May-2008
556 REAL(KIND=8) :: out_time
557 INTEGER(selected_int_kind(14)) :: dt_whole
558 INTEGER :: dt_num, dt_den
561 TYPE(WRFU_TimeInterval), intent(INOUT) :: timeinterval
563 CALL WRFU_TimeIntervalGet(timeinterval,Sn=dt_num,Sd=dt_den,S_i8=dt_whole)
564 if (ABS(dt_den) < 1) then
565 out_time = REAL(dt_whole)
567 out_time = REAL(dt_whole) + REAL(dt_num,8)/REAL(dt_den,8)
569 END FUNCTION real_time_r8