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) :: parent_dtInterval
37 INTEGER :: num_small_steps
39 LOGICAL :: stepping_to_bc
40 INTEGER :: bc_time, output_time
41 double precision :: dt = 0
42 INTEGER, PARAMETER :: precision = 100
43 INTEGER :: dt_num, dt_den, dt_whole
46 TYPE(WRFU_TimeInterval) :: last_dtInterval
50 ! If use_last2 is true, this routine will use the time step
51 ! from 2 steps ago to compute the next time step. This
52 ! is used along with step_to_output and step_to_bc
57 ! If this step has already been adapted, no need to do it again.
58 ! time step can already be adapted when adaptation_domain is
62 if (grid%last_step_updated == grid%itimestep) then
65 grid%last_step_updated = grid%itimestep
69 ! For nests, set adapt_this_step_using_child to parent's value
71 if (grid%id .ne. 1) then
72 grid%adapt_this_step_using_child = grid%parents(1)%ptr%adapt_this_step_using_child;
76 ! For nests, if we're not adapting using child nest, we only want to change
77 ! nests' time steps when the time is conincident with the parent's time.
78 ! So, if dtbc is not zero, simply return and leave the last time step in
82 ! if ((grid%id .ne. 1) .and. (.not. grid%adapt_this_step_using_child)) then
83 ! if (abs(grid%dtbc) > 0.0001) then
88 last_dtInterval = grid%last_dtInterval
94 tmpTimeInterval = domain_get_current_time ( grid ) - &
95 domain_get_start_time ( grid )
98 ! Calculate current time in seconds since beginning of model run.
99 ! Unfortunately, ESMF does not seem to have a way to return
100 ! floating point seconds based on a TimeInterval. So, we will
101 ! calculate it here--but, this is not clean!!
103 curr_secs = real_time(tmpTimeInterval)
106 ! Calculate the maximum allowable increase in the time step given
107 ! the user input max_step_increase_pct value and the nest ratio.
109 max_increase_factor = 1. + grid%max_step_increase_pct / 100.
112 ! If this is the first time step of the model run (indicated by current_time
113 ! eq start_time), then set the time step to the input starting_time_step.
115 ! Else, calculate the time step based on cfl.
117 if (domain_get_current_time ( grid ) .eq. domain_get_start_time ( grid )) then
118 CALL WRFU_TimeIntervalSet(dtInterval, Sn=grid%starting_time_step, Sd=1)
120 CALL WRFU_TimeIntervalSet(last_dtInterval, Sn=0, Sd=1)
123 if (grid%max_cfl_val < 0.001) then
125 ! If the max_cfl_val is small, then we increase dtInterval the maximum
128 num = INT(max_increase_factor * precision + 0.5)
130 dtInterval = last_dtInterval * num / den
134 ! If the max_cfl_val is greater than the user input target cfl, we
135 ! reduce the time step,
136 ! else, we increase it.
138 if (grid%max_cfl_val .gt. grid%target_cfl) then
140 ! If we are reducing the time step, we go below target cfl by half the
141 ! difference between max and target
142 ! This tends to keep the model more stable.
145 factor = ( (grid%target_cfl - 0.5 * &
146 (grid%max_cfl_val - grid%target_cfl) ) / grid%max_cfl_val )
147 num = INT(factor * precision + 0.5)
150 dtInterval = last_dtInterval * num / den
154 ! Linearly increase dtInterval (we'll limit below)
157 factor = grid%target_cfl / grid%max_cfl_val
158 num = INT(factor * precision + 0.5)
160 dtInterval = last_dtInterval * num / den
167 ! Limit the increase of dtInterval to the specified input limit
169 num = NINT( max_increase_factor * precision )
171 tmpTimeInterval = last_dtInterval * num / den
172 if ( (domain_get_current_time ( grid ) .ne. domain_get_start_time ( grid )) &
173 .and. (dtInterval .gt. tmpTimeInterval ) ) then
174 dtInterval = tmpTimeInterval
178 ! Here, we round off dtInterval to nearest 1/100. This prevents
179 ! the denominator from getting too large and causing overflow.
181 dt = real_time(dtInterval)
182 num = NINT(dt * precision)
184 CALL WRFU_TimeIntervalSet(dtInterval, Sn=num, Sd=den)
186 ! Limit the maximum dtInterval based on user input
188 CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%max_time_step, Sd=1)
189 if (dtInterval .gt. tmpTimeInterval ) then
190 dtInterval = tmpTimeInterval
193 ! Limit the minimum dtInterval based on user input.
195 CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%min_time_step, Sd=1)
196 if (dtInterval .lt. tmpTimeInterval ) then
197 dtInterval = tmpTimeInterval
201 ! Now, if this is a nest, and we are adapting based upon parent,
202 ! we round down the time step to the nearest
203 ! value that divides evenly into the parent time step.
204 ! If this is a nest, and we are adapting based upon the child (i.e., the
205 ! nest), we update the parent timestep to the next smallest multiple
208 if (grid%nested) then
210 dt = real_time(dtInterval)
212 if (.not. grid%adapt_this_step_using_child) then
214 ! We'll calculate real numbers to get the number of small steps:
216 num_small_steps = CEILING( grid%parents(1)%ptr%dt / dt )
219 call wrf_dm_maxval(num_small_steps, idex, jdex)
221 dtInterval = domain_get_time_step(grid%parents(1)%ptr) / &
225 num_small_steps = FLOOR( grid%parents(1)%ptr%dt / dt )
228 call wrf_dm_minval(num_small_steps, idex, jdex)
230 if (num_small_steps < 1) then
239 ! Setup the values for several variables from the tile with the
242 dt = real_time(dtInterval)
245 call wrf_dm_mintile_double(dt, tile)
246 CALL WRFU_TimeIntervalGet(dtInterval,Sn=dt_num,Sd=dt_den,S=dt_whole)
247 call wrf_dm_tile_val_int(dt_num, tile)
248 call wrf_dm_tile_val_int(dt_den, tile)
249 call wrf_dm_tile_val_int(dt_whole, tile)
250 CALL WRFU_TimeIntervalSet(dtInterval, Sn = dt_whole*dt_den + dt_num, Sd = dt_den)
252 call wrf_dm_maxtile_real(grid%max_cfl_val, tile)
253 call wrf_dm_maxtile_real(grid%max_vert_cfl, tile)
254 call wrf_dm_maxtile_real(grid%max_horiz_cfl, tile)
257 if ((grid%nested) .and. (grid%adapt_this_step_using_child)) then
259 grid%dt = real_time(dtInterval)
261 ! Set parent step here.
262 grid%parents(1)%ptr%dt = grid%dt * num_small_steps
263 parent_dtInterval = dtInterval * num_small_steps
266 ! Update the parent clock based on the new time step
268 CALL WRFU_ClockSet ( grid%parents(1)%ptr%domain_clock, &
269 timeStep=parent_dtInterval, &
276 ! Assure that we fall on a BC time. Due to a bug in WRF, the time
277 ! step must fall on the boundary times. Only modify the dtInterval
278 ! when this is not the first time step on this domain.
281 grid%stepping_to_time = .FALSE.
282 time_to_bc = grid%interval_seconds - grid%dtbc
283 num = INT(time_to_bc * precision + 0.5)
285 CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=num, Sd=den)
287 if ( ( tmpTimeInterval .LT. dtInterval * 2 ) .and. &
288 ( tmpTimeInterval .GT. dtInterval ) ) then
289 dtInterval = tmpTimeInterval / 2
292 stepping_to_bc = .true.
293 grid%stepping_to_time = .TRUE.
295 elseif (tmpTimeInterval .LE. dtInterval) then
297 bc_time = NINT ( (curr_secs + time_to_bc) / ( grid%interval_seconds ) ) &
298 * ( grid%interval_seconds )
299 CALL WRFU_TimeIntervalSet(tmpTimeInterval, S=bc_time)
300 dtInterval = tmpTimeInterval - &
301 (domain_get_current_time(grid) - domain_get_start_time(grid))
304 stepping_to_bc = .true.
305 grid%stepping_to_time = .TRUE.
307 stepping_to_bc = .false.
311 ! If the user has requested that we step to output, then
312 ! assure that we fall on an output time. We look out two time steps to
313 ! avoid having a very short time step. Very short time steps can cause model
317 if ((grid%step_to_output_time) .and. (.not. stepping_to_bc) .and. &
318 (.not. grid%nested)) then
320 time_to_output = grid%history_interval*60 - &
321 mod(curr_secs, grid%history_interval*60.0)
322 num = INT(time_to_output * precision + 0.5)
324 call WRFU_TimeIntervalSet(tmpTimeInterval, Sn=num, Sd=den)
326 if ( ( tmpTimeInterval .LT. dtInterval * 2 ) .and. &
327 ( tmpTimeInterval .GT. dtInterval ) ) then
328 dtInterval = tmpTimeInterval / 2
330 grid%stepping_to_time = .TRUE.
332 elseif (tmpTimeInterval .LE. dtInterval) then
334 ! We will do some tricks here to assure that we fall exactly on an
335 ! output time. Without the tricks, round-off error causes problems!
339 ! Calculate output time. We round to nearest history time to assure
340 ! we don't have any rounding error.
342 output_time = NINT ( (curr_secs + time_to_output) / &
343 ( grid%history_interval * 60 ) ) * (grid%history_interval * 60)
344 CALL WRFU_TimeIntervalSet(tmpTimeInterval, S=output_time)
345 dtInterval = tmpTimeInterval - &
346 (domain_get_current_time(grid) - domain_get_start_time(grid))
349 grid%stepping_to_time = .TRUE.
354 ! Now, set adapt_this_step_using_child only if we are not stepping to an
355 ! output time, or, it's not the start of the model run.
356 ! Note: adapt_this_step_using_child is updated just before recursive call to
357 ! adapt_timestep--see end of this function.
360 if (grid%id == 1) then
361 if ((grid%adaptation_domain > 1) .and. &
362 (grid%max_dom == 2) .and. &
363 (.not. grid%stepping_to_time) .and. &
364 (domain_get_current_time(grid) .ne. &
365 domain_get_start_time(grid)) &
368 grid%adapt_this_step_using_child = .TRUE.
370 grid%adapt_this_step_using_child = .FALSE.
376 grid%last_dtInterval = last_dtInterval
378 grid%last_dtInterval = dtInterval
381 grid%dt = real_time(dtInterval)
383 grid%last_max_vert_cfl = grid%max_vert_cfl
386 ! Update the clock based on the new time step
388 CALL WRFU_ClockSet ( grid%domain_clock, &
389 timeStep=dtInterval, &
393 ! If we're are adapting based on the child time step,
394 ! we call the child from here. This assures that
395 ! child and parent are updated in sync.
396 ! Note: This is not necessary when we are adapting based
399 if ((grid%id == 1) .and. (grid%adapt_this_step_using_child)) then
401 ! Finally, check if we can adapt using child. If we are
402 ! stepping to an output time, we cannot adapt based upon
403 ! child. So, we reset the variable before calling the child.
404 ! This covers the case that, within this parent time-step that
405 ! we just calculated, we are stepping to an output time.
407 if (grid%stepping_to_time) then
408 grid%adapt_this_step_using_child = .FALSE.
410 call adapt_timestep(grid%nests(1)%ptr, config_flags)
414 ! Lateral boundary weight recomputation based on time step.
416 if (grid%id == 1) then
417 CALL lbc_fcx_gcx ( grid%fcx , grid%gcx , grid%spec_bdy_width , &
418 grid%spec_zone , grid%relax_zone , grid%dt , config_flags%spec_exp , &
419 config_flags%specified , config_flags%nested )
422 END SUBROUTINE adapt_timestep
424 FUNCTION real_time( timeinterval ) RESULT ( out_time )
430 ! This function returns a floating point time from an input time interval
432 ! Unfortunately, the ESMF did not provide this functionality.
434 ! Be careful with the output because, due to rounding, the time is only
437 ! Todd Hutchinson, WSI
442 INTEGER :: dt_num, dt_den, dt_whole
445 TYPE(WRFU_TimeInterval), intent(INOUT) :: timeinterval
447 CALL WRFU_TimeIntervalGet(timeinterval,Sn=dt_num,Sd=dt_den,S=dt_whole)
448 if (ABS(dt_den) < 1) then
451 out_time = dt_whole + dt_num / REAL(dt_den)
456 FUNCTION real_time_r8( timeinterval ) RESULT ( out_time )
462 ! This function returns a double precision floating point time from an input time interval
464 ! Unfortunately, the ESMF did not provide this functionality.
466 ! Be careful with the output because, due to rounding, the time is only
469 ! Todd Hutchinson, WSI 4/17/2007
470 ! Converted to r8, William.Gustafson@pnl.gov; 8-May-2008
473 REAL(KIND=8) :: out_time
474 INTEGER(KIND=8) :: dt_whole
475 INTEGER :: dt_num, dt_den
478 TYPE(WRFU_TimeInterval), intent(INOUT) :: timeinterval
480 CALL WRFU_TimeIntervalGet(timeinterval,Sn=dt_num,Sd=dt_den,S_i8=dt_whole)
481 if (ABS(dt_den) < 1) then
482 out_time = REAL(dt_whole)
484 out_time = REAL(dt_whole) + REAL(dt_num,8)/REAL(dt_den,8)
486 END FUNCTION real_time_r8