SUBROUTINE adapt_timestep
(docs) (grid, config_flags) 2,19
!--------------------------------------------------------------------------
!<DESCRIPTION>
!<pre>
!
! This routine sets the time step based on the cfl condition. It's used to
! dynamically adapt the timestep as the model runs.
!
! T. Hutchinson, WSI
! March 2007
!
!</pre>
!</DESCRIPTION>
!--------------------------------------------------------------------------
! Driver layer modules
USE module_domain
USE module_configure
USE module_dm
USE module_bc_em
IMPLICIT NONE
TYPE(domain) , TARGET , INTENT(INOUT) :: grid
TYPE (grid_config_rec_type) , INTENT(IN) :: config_flags
LOGICAL :: use_last2
REAL :: curr_secs
REAL :: max_increase_factor
REAL :: time_to_output, &
time_to_bc
INTEGER :: idex=0, jdex=0
INTEGER :: rc
TYPE(WRFU_TimeInterval) :: tmpTimeInterval, dtInterval
INTEGER :: num_small_steps
integer :: tile
LOGICAL :: stepping_to_bc
INTEGER :: bc_time, output_time
double precision :: dt
INTEGER, PARAMETER :: precision = 100
INTEGER :: dt_num, dt_den, dt_whole
REAL :: factor
INTEGER :: num, den
TYPE(WRFU_TimeInterval) :: last_dtInterval
REAL :: real_time
!
! If use_last2 is true, this routine will use the time step
! from 2 steps ago to compute the next time step. This
! is used along with step_to_output and step_to_bc
use_last2 = .FALSE.
!
! For nests, we only want to change nests' time steps when the time is
! conincident with the parent's time. So, if dtbc is not
! zero, simply return and leave the last time step in place.
!
if (config_flags%nested) then
if (abs(grid%dtbc) > 0.0001) then
return
endif
endif
last_dtInterval = grid%last_dtInterval
!
! Get current time
!
tmpTimeInterval = domain_get_current_time ( grid ) - &
domain_get_start_time ( grid )
!
! Calculate current time in seconds since beginning of model run.
! Unfortunately, ESMF does not seem to have a way to return
! floating point seconds based on a TimeInterval. So, we will
! calculate it here--but, this is not clean!!
!
curr_secs = real_time
(tmpTimeInterval)
!
! Calculate the maximum allowable increase in the time step given
! the user input max_step_increase_pct value and the nest ratio.
!
if (config_flags%nested) then
max_increase_factor = 1. + &
grid%parent_time_step_ratio * grid%max_step_increase_pct / 100.
else
max_increase_factor = 1. + grid%max_step_increase_pct / 100.
endif
!
! If this is the first time step of the model run (indicated by current_time
! eq start_time), then set the time step to the input starting_time_step.
!
! Else, calculate the time step based on cfl.
!
if (domain_get_current_time ( grid ) .eq. domain_get_start_time ( grid )) then
CALL WRFU_TimeIntervalSet(dtInterval, Sn=grid%starting_time_step, Sd=1)
curr_secs = 0
CALL WRFU_TimeIntervalSet(last_dtInterval, Sn=0, Sd=1)
else
if (grid%max_cfl_val < 0.001) then
!
! If the max_cfl_val is small, then we increase dtInterval the maximum
! amount allowable.
!
num = INT(max_increase_factor * precision + 0.5)
den = precision
dtInterval = last_dtInterval * num / den
else
!
! If the max_cfl_val is greater than the user input target cfl, we
! reduce the time step,
! else, we increase it.
!
if (grid%max_cfl_val .gt. grid%target_cfl) then
!
! If we are reducing the time step, we go below target cfl by half the
! difference between max and target (or 75% of the last time step,
! which ever is greater).
! This tends to keep the model more stable.
!
factor = MAX ( 0.75 , ( (grid%target_cfl - 0.5 * &
(grid%max_cfl_val - grid%target_cfl) ) / grid%max_cfl_val ) )
num = INT(factor * precision + 0.5)
den = precision
dtInterval = last_dtInterval * num / den
else
!
! Linearly increase dtInterval (we'll limit below)
!
factor = grid%target_cfl / grid%max_cfl_val
num = INT(factor * precision + 0.5)
den = precision
dtInterval = last_dtInterval * num / den
endif
endif
endif
! Limit the increase of dtInterval to the specified input limit
num = NINT( max_increase_factor * precision )
den = precision
tmpTimeInterval = last_dtInterval * num / den
if ( (domain_get_current_time ( grid ) .ne. domain_get_start_time ( grid )) &
.and. (dtInterval .gt. tmpTimeInterval ) ) then
dtInterval = tmpTimeInterval
endif
!
! Here, we round off dtInterval to nearest 1/100. This prevents
! the denominator from getting too large and causing overflow.
!
dt = real_time
(dtInterval)
num = NINT(dt * precision)
den = precision
CALL WRFU_TimeIntervalSet(dtInterval, Sn=num, Sd=den)
! Limit the maximum dtInterval based on user input
CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%max_time_step, Sd=1)
if (dtInterval .gt. tmpTimeInterval ) then
dtInterval = tmpTimeInterval
endif
! Limit the minimum dtInterval based on user input.
CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%min_time_step, Sd=1)
if (dtInterval .lt. tmpTimeInterval ) then
dtInterval = tmpTimeInterval
endif
!
! Now, if this is a nest, we round down the time step to the nearest
! value that divides evenly into the parent time step.
!
if (config_flags%nested) then
! We'll calculate real numbers to get the number of small steps:
dt = real_time
(dtInterval)
num_small_steps = CEILING( grid%parents(1)%ptr%dt / dt )
#ifdef DM_PARALLEL
call wrf_dm_maxval
(num_small_steps, idex, jdex)
#endif
dtInterval = domain_get_time_step
(grid%parents(1)%ptr) / &
num_small_steps
endif
!
! Setup the values for several variables from the tile with the
! minimum dt.
!
dt = real_time
(dtInterval)
#ifdef DM_PARALLEL
call wrf_dm_mintile_double
(dt, tile)
CALL WRFU_TimeIntervalGet(dtInterval,Sn=dt_num,Sd=dt_den,S=dt_whole)
call wrf_dm_tile_val_int
(dt_num, tile)
call wrf_dm_tile_val_int
(dt_den, tile)
call wrf_dm_tile_val_int
(dt_whole, tile)
CALL WRFU_TimeIntervalSet(dtInterval, Sn = dt_whole*dt_den + dt_num, Sd = dt_den)
call wrf_dm_maxtile_real
(grid%max_cfl_val, tile)
call wrf_dm_maxtile_real
(grid%max_vert_cfl, tile)
call wrf_dm_maxtile_real
(grid%max_horiz_cfl, tile)
#endif
!
! Assure that we fall on a BC time. Due to a bug in WRF, the time
! step must fall on the boundary times. Only modify the dtInterval
! when this is not the first time step on this domain.
!
if ( real_time(domain_get_current_time(grid) - domain_get_start_time(grid)) .GT. 0.01 ) THEN
time_to_bc = grid%interval_seconds - grid%dtbc
num = INT(time_to_bc * precision + 0.5)
den = precision
CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=num, Sd=den)
if ( ( tmpTimeInterval .LT. dtInterval * 2 ) .and. &
( tmpTimeInterval .GT. dtInterval ) ) then
dtInterval = tmpTimeInterval / 2
use_last2 = .TRUE.
stepping_to_bc = .true.
elseif (tmpTimeInterval .LE. dtInterval) then
bc_time = NINT ( (curr_secs + time_to_bc) / ( grid%interval_seconds ) ) &
* ( grid%interval_seconds )
CALL WRFU_TimeIntervalSet(tmpTimeInterval, S=bc_time)
dtInterval = tmpTimeInterval - &
(domain_get_current_time(grid) - domain_get_start_time(grid))
use_last2 = .TRUE.
stepping_to_bc = .true.
else
stepping_to_bc = .false.
endif
else
stepping_to_bc = .false.
tmpTimeInterval = dtInterval
endif
!
! If the user has requested that we step to output, then
! assure that we fall on an output time. We look out two time steps to
! avoid having a very short time step. Very short time steps can cause model
! instability.
!
if ((grid%step_to_output_time) .and. (.not. stepping_to_bc) .and. &
(.not. config_flags%nested)) then
time_to_output = grid%history_interval*60 - &
mod(curr_secs, grid%history_interval*60.0)
num = INT(time_to_output * precision + 0.5)
den = precision
call WRFU_TimeIntervalSet(tmpTimeInterval, Sn=num, Sd=den)
if ( ( tmpTimeInterval .LT. dtInterval * 2 ) .and. &
( tmpTimeInterval .GT. dtInterval ) ) then
dtInterval = tmpTimeInterval / 2
use_last2 = .TRUE.
elseif (tmpTimeInterval .LE. dtInterval) then
!
! We will do some tricks here to assure that we fall exactly on an
! output time. Without the tricks, round-off error causes problems!
!
!
! Calculate output time. We round to nearest history time to assure
! we don't have any rounding error.
!
output_time = NINT ( (curr_secs + time_to_output) / &
( grid%history_interval * 60 ) ) * (grid%history_interval * 60)
CALL WRFU_TimeIntervalSet(tmpTimeInterval, S=output_time)
dtInterval = tmpTimeInterval - &
(domain_get_current_time(grid) - domain_get_start_time(grid))
use_last2 = .TRUE.
endif
endif
if (use_last2) then
grid%last_dtInterval = last_dtInterval
else
grid%last_dtInterval = dtInterval
endif
grid%dt = real_time
(dtInterval)
grid%last_max_vert_cfl = grid%max_vert_cfl
!
! Update the clock based on the new time step
!
CALL WRFU_ClockSet ( grid%domain_clock, &
timeStep=dtInterval, &
rc=rc )
!
! Lateral boundary weight recomputation based on time step.
!
CALL lbc_fcx_gcx
( grid%fcx , grid%gcx , grid%spec_bdy_width , &
grid%spec_zone , grid%relax_zone , grid%dt , config_flags%spec_exp , &
config_flags%specified , config_flags%nested )
END SUBROUTINE adapt_timestep
FUNCTION real_time
(docs) ( timeinterval ) RESULT ( out_time ) 6,1
USE module_domain
IMPLICIT NONE
! This function returns a floating point time from an input time interval
!
! Unfortunately, the ESMF did not provide this functionality.
!
! Be careful with the output because, due to rounding, the time is only
! approximate.
!
! Todd Hutchinson, WSI
! 4/17/2007
! !RETURN VALUE:
REAL :: out_time
INTEGER :: dt_num, dt_den, dt_whole
! !ARGUMENTS:
TYPE(WRFU_TimeInterval), intent(INOUT) :: timeinterval
CALL WRFU_TimeIntervalGet(timeinterval,Sn=dt_num,Sd=dt_den,S=dt_whole)
if (ABS(dt_den) < 1) then
out_time = dt_whole
else
out_time = dt_whole + dt_num / REAL(dt_den)
endif
END FUNCTION
FUNCTION real_time_r8
(docs) ( timeinterval ) RESULT ( out_time ),1
USE module_domain
IMPLICIT NONE
! This function returns a double precision floating point time from an input time interval
!
! Unfortunately, the ESMF did not provide this functionality.
!
! Be careful with the output because, due to rounding, the time is only
! approximate.
!
! Todd Hutchinson, WSI 4/17/2007
! Converted to r8, William.Gustafson@pnl.gov; 8-May-2008
! !RETURN VALUE:
REAL(KIND=8) :: out_time
INTEGER(KIND=8) :: dt_whole
INTEGER :: dt_num, dt_den
! !ARGUMENTS:
TYPE(WRFU_TimeInterval), intent(INOUT) :: timeinterval
CALL WRFU_TimeIntervalGet(timeinterval,Sn=dt_num,Sd=dt_den,S_i8=dt_whole)
if (ABS(dt_den) < 1) then
out_time = dt_whole
else
out_time = dt_whole + REAL(dt_num/dt_den,8)
endif
END FUNCTION real_time_r8