PageRenderTime 53ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/wrfv2_fire/dyn_em/adapt_timestep_em.F

http://github.com/jbeezley/wrf-fire
FORTRAN Legacy | 544 lines | 237 code | 106 blank | 201 comment | 30 complexity | 6c547ce5961eac63265c60829494543a MD5 | raw file
Possible License(s): AGPL-1.0
  1. RECURSIVE SUBROUTINE adapt_timestep(grid, config_flags)
  2. !--------------------------------------------------------------------------
  3. !<DESCRIPTION>
  4. !<pre>
  5. !
  6. ! This routine sets the time step based on the cfl condition. It's used to
  7. ! dynamically adapt the timestep as the model runs.
  8. !
  9. ! T. Hutchinson, WSI
  10. ! March 2007
  11. !
  12. !</pre>
  13. !</DESCRIPTION>
  14. !--------------------------------------------------------------------------
  15. ! Driver layer modules
  16. USE module_domain
  17. USE module_configure
  18. USE module_dm, ONLY : wrf_dm_maxval, wrf_dm_minval, wrf_dm_mintile_double, wrf_dm_tile_val_int, wrf_dm_maxtile_real
  19. USE module_bc_em
  20. IMPLICIT NONE
  21. TYPE(domain) , TARGET , INTENT(INOUT) :: grid
  22. TYPE (grid_config_rec_type) , INTENT(IN) :: config_flags
  23. LOGICAL :: use_last2
  24. REAL :: curr_secs
  25. REAL :: max_increase_factor
  26. REAL :: time_to_output, &
  27. time_to_bc
  28. INTEGER :: idex=0, jdex=0
  29. INTEGER :: rc
  30. TYPE(WRFU_TimeInterval) :: tmpTimeInterval, dtInterval
  31. TYPE(WRFU_TimeInterval) :: dtInterval_horiz
  32. TYPE(WRFU_TimeInterval) :: dtInterval_vert
  33. TYPE(WRFU_TimeInterval) :: parent_dtInterval
  34. INTEGER :: num_small_steps
  35. integer :: tile
  36. LOGICAL :: stepping_to_bc
  37. INTEGER :: bc_time, output_time
  38. double precision :: dt = 0
  39. INTEGER, PARAMETER :: precision = 100
  40. INTEGER :: dt_num, dt_den, dt_whole
  41. INTEGER :: num, den, history_interval_sec
  42. TYPE(WRFU_TimeInterval) :: last_dtInterval
  43. REAL :: real_time
  44. REAL :: max_vert_cfl, max_horiz_cfl
  45. !
  46. ! If use_last2 is true, this routine will use the time step
  47. ! from 2 steps ago to compute the next time step. This
  48. ! is used along with step_to_output and step_to_bc
  49. use_last2 = .FALSE.
  50. !
  51. ! Assign last_dtInterval type values from restart file
  52. !
  53. CALL WRFU_TimeIntervalSet(grid%last_dtInterval, S=grid%last_dt_sec, &
  54. Sn=grid%last_dt_sec_num, Sd=grid%last_dt_sec_den)
  55. !
  56. ! If this step has already been adapted, no need to do it again.
  57. ! time step can already be adapted when adaptation_domain is
  58. ! enabled.
  59. !
  60. if (grid%last_step_updated == grid%itimestep) then
  61. return
  62. else
  63. grid%last_step_updated = grid%itimestep
  64. endif
  65. !
  66. ! For nests, set adapt_step_using_child to parent's value
  67. !
  68. if (grid%id .ne. 1) then
  69. grid%adapt_step_using_child = grid%parents(1)%ptr%adapt_step_using_child;
  70. endif
  71. !
  72. ! For nests, if we're not adapting using child nest, we only want to change
  73. ! nests' time steps when the time is conincident with the parent's time.
  74. ! So, if dtbc is not zero, simply return and leave the last time step in
  75. ! place.
  76. !
  77. ! if ((grid%id .ne. 1) .and. (.not. grid%adapt_step_using_child)) then
  78. ! if (abs(grid%dtbc) > 0.0001) then
  79. ! return
  80. ! endif
  81. ! endif
  82. last_dtInterval = grid%last_dtInterval
  83. !
  84. ! Get time since beginning of simulation start
  85. !
  86. tmpTimeInterval = domain_get_current_time ( grid ) - &
  87. domain_get_sim_start_time ( grid )
  88. !
  89. ! Calculate current time in seconds since beginning of model run.
  90. ! Unfortunately, ESMF does not seem to have a way to return
  91. ! floating point seconds based on a TimeInterval. So, we will
  92. ! calculate it here--but, this is not clean!!
  93. !
  94. curr_secs = real_time(tmpTimeInterval)
  95. !
  96. ! Calculate the maximum allowable increase in the time step given
  97. ! the user input max_step_increase_pct value and the nest ratio.
  98. !
  99. max_increase_factor = 1. + grid%max_step_increase_pct / 100.
  100. !
  101. ! If this is the first time step of the model run (indicated by current_time
  102. ! eq start_time), then set the time step to the input starting_time_step.
  103. !
  104. ! Else, calculate the time step based on cfl.
  105. !
  106. if ( (domain_get_current_time ( grid ) .eq. domain_get_start_time ( grid )) .AND. &
  107. .NOT. config_flags%restart ) then
  108. CALL WRFU_TimeIntervalSet(dtInterval, Sn=grid%starting_time_step, Sd=1)
  109. curr_secs = 0
  110. CALL WRFU_TimeIntervalSet(last_dtInterval, Sn=0, Sd=1)
  111. else
  112. if (grid%stepping_to_time) then
  113. max_vert_cfl = grid%last_max_vert_cfl
  114. max_horiz_cfl = grid%last_max_horiz_cfl
  115. else
  116. max_vert_cfl = grid%max_vert_cfl
  117. max_horiz_cfl = grid%max_horiz_cfl
  118. endif
  119. CALL calc_dt(dtInterval_vert, max_vert_cfl, max_increase_factor, &
  120. precision, last_dtInterval, grid%target_cfl)
  121. CALL calc_dt(dtInterval_horiz, max_horiz_cfl, max_increase_factor, &
  122. precision, last_dtInterval, grid%target_hcfl)
  123. if (dtInterval_vert < dtInterval_horiz) then
  124. dtInterval = dtInterval_vert
  125. else
  126. dtInterval = dtInterval_horiz
  127. endif
  128. endif
  129. ! Limit the increase of dtInterval to the specified input limit
  130. num = NINT( max_increase_factor * precision )
  131. den = precision
  132. tmpTimeInterval = last_dtInterval * num / den
  133. if ( (domain_get_current_time ( grid ) .ne. domain_get_start_time ( grid )) &
  134. .and. (dtInterval .gt. tmpTimeInterval ) ) then
  135. dtInterval = tmpTimeInterval
  136. endif
  137. !
  138. ! Here, we round off dtInterval to nearest 1/100. This prevents
  139. ! the denominator from getting too large and causing overflow.
  140. !
  141. dt = real_time(dtInterval)
  142. num = NINT(dt * precision)
  143. den = precision
  144. CALL WRFU_TimeIntervalSet(dtInterval, Sn=num, Sd=den)
  145. ! Limit the maximum dtInterval based on user input
  146. CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%max_time_step, Sd=1)
  147. if (dtInterval .gt. tmpTimeInterval ) then
  148. dtInterval = tmpTimeInterval
  149. endif
  150. ! Limit the minimum dtInterval based on user input.
  151. CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=grid%min_time_step, Sd=1)
  152. if (dtInterval .lt. tmpTimeInterval ) then
  153. dtInterval = tmpTimeInterval
  154. endif
  155. !
  156. ! Now, if this is a nest, and we are adapting based upon parent,
  157. ! we round down the time step to the nearest
  158. ! value that divides evenly into the parent time step.
  159. ! If this is a nest, and we are adapting based upon the child (i.e., the
  160. ! nest), we update the parent timestep to the next smallest multiple
  161. ! timestep.
  162. !
  163. if (grid%nested) then
  164. dt = real_time(dtInterval)
  165. if (.not. grid%adapt_step_using_child) then
  166. ! We'll calculate real numbers to get the number of small steps:
  167. num_small_steps = CEILING( grid%parents(1)%ptr%dt / dt )
  168. #ifdef DM_PARALLEL
  169. call wrf_dm_maxval(num_small_steps, idex, jdex)
  170. #endif
  171. dtInterval = domain_get_time_step(grid%parents(1)%ptr) / &
  172. num_small_steps
  173. else
  174. num_small_steps = FLOOR( grid%parents(1)%ptr%dt / dt )
  175. #ifdef DM_PARALLEL
  176. call wrf_dm_minval(num_small_steps, idex, jdex)
  177. #endif
  178. if (num_small_steps < 1) then
  179. num_small_steps = 1
  180. endif
  181. endif
  182. endif
  183. !
  184. ! Setup the values for several variables from the tile with the
  185. ! minimum dt.
  186. !
  187. dt = real_time(dtInterval)
  188. #ifdef DM_PARALLEL
  189. call wrf_dm_mintile_double(dt, tile)
  190. CALL WRFU_TimeIntervalGet(dtInterval,Sn=dt_num,Sd=dt_den,S=dt_whole)
  191. call wrf_dm_tile_val_int(dt_num, tile)
  192. call wrf_dm_tile_val_int(dt_den, tile)
  193. call wrf_dm_tile_val_int(dt_whole, tile)
  194. CALL WRFU_TimeIntervalSet(dtInterval, Sn = dt_whole*dt_den + dt_num, Sd = dt_den)
  195. call wrf_dm_maxtile_real(grid%max_vert_cfl, tile)
  196. call wrf_dm_maxtile_real(grid%max_horiz_cfl, tile)
  197. #endif
  198. if ((grid%nested) .and. (grid%adapt_step_using_child)) then
  199. grid%dt = real_time(dtInterval)
  200. ! Set parent step here.
  201. grid%parents(1)%ptr%dt = grid%dt * num_small_steps
  202. parent_dtInterval = dtInterval * num_small_steps
  203. !
  204. ! Update the parent clock based on the new time step
  205. !
  206. CALL WRFU_ClockSet ( grid%parents(1)%ptr%domain_clock, &
  207. timeStep=parent_dtInterval, &
  208. rc=rc )
  209. endif
  210. !
  211. ! Assure that we fall on a BC time. Due to a bug in WRF, the time
  212. ! step must fall on the boundary times. Only modify the dtInterval
  213. ! when this is not the first time step on this domain.
  214. !
  215. grid%stepping_to_time = .FALSE.
  216. time_to_bc = grid%interval_seconds - grid%dtbc
  217. num = INT(time_to_bc * precision + 0.5)
  218. den = precision
  219. CALL WRFU_TimeIntervalSet(tmpTimeInterval, Sn=num, Sd=den)
  220. if ( ( tmpTimeInterval .LT. dtInterval * 2 ) .and. &
  221. ( tmpTimeInterval .GT. dtInterval ) ) then
  222. dtInterval = tmpTimeInterval / 2
  223. use_last2 = .TRUE.
  224. stepping_to_bc = .true.
  225. grid%stepping_to_time = .TRUE.
  226. elseif (tmpTimeInterval .LE. dtInterval) then
  227. bc_time = NINT ( (curr_secs + time_to_bc) / ( grid%interval_seconds ) ) &
  228. * ( grid%interval_seconds )
  229. CALL WRFU_TimeIntervalSet(tmpTimeInterval, S=bc_time)
  230. dtInterval = tmpTimeInterval - &
  231. (domain_get_current_time(grid) - domain_get_sim_start_time(grid))
  232. use_last2 = .TRUE.
  233. stepping_to_bc = .true.
  234. grid%stepping_to_time = .TRUE.
  235. else
  236. stepping_to_bc = .false.
  237. endif
  238. !
  239. ! If the user has requested that we step to output, then
  240. ! assure that we fall on an output time. We look out two time steps to
  241. ! avoid having a very short time step. Very short time steps can cause model
  242. ! instability.
  243. !
  244. if ((grid%step_to_output_time) .and. (.not. stepping_to_bc) .and. &
  245. (.not. grid%nested)) then
  246. IF ( grid%history_interval_m .EQ. 0 ) grid%history_interval_m = grid%history_interval
  247. history_interval_sec = grid%history_interval_s + grid%history_interval_m*60 + &
  248. grid%history_interval_h*3600 + grid%history_interval_d*86400
  249. time_to_output = history_interval_sec - &
  250. mod( curr_secs, REAL(history_interval_sec) )
  251. num = INT(time_to_output * precision + 0.5)
  252. den = precision
  253. call WRFU_TimeIntervalSet(tmpTimeInterval, Sn=num, Sd=den)
  254. if ( ( tmpTimeInterval .LT. dtInterval * 2 ) .and. &
  255. ( tmpTimeInterval .GT. dtInterval ) ) then
  256. dtInterval = tmpTimeInterval / 2
  257. use_last2 = .TRUE.
  258. grid%stepping_to_time = .TRUE.
  259. elseif (tmpTimeInterval .LE. dtInterval) then
  260. !
  261. ! We will do some tricks here to assure that we fall exactly on an
  262. ! output time. Without the tricks, round-off error causes problems!
  263. !
  264. !
  265. ! Calculate output time. We round to nearest history time to assure
  266. ! we don't have any rounding error.
  267. !
  268. output_time = NINT ( (curr_secs + time_to_output) / &
  269. (history_interval_sec) ) * (history_interval_sec)
  270. CALL WRFU_TimeIntervalSet(tmpTimeInterval, S=output_time)
  271. dtInterval = tmpTimeInterval - &
  272. (domain_get_current_time(grid) - domain_get_sim_start_time(grid))
  273. use_last2 = .TRUE.
  274. grid%stepping_to_time = .TRUE.
  275. endif
  276. endif
  277. !
  278. ! Now, set adapt_step_using_child only if we are not stepping to an
  279. ! output time, or, it's not the start of the model run.
  280. ! Note: adapt_step_using_child is updated just before recursive call to
  281. ! adapt_timestep--see end of this function.
  282. !
  283. if (grid%id == 1) then
  284. if ((grid%adaptation_domain > 1) .and. &
  285. (grid%max_dom == 2) .and. &
  286. (.not. grid%stepping_to_time) .and. &
  287. (domain_get_current_time(grid) .ne. &
  288. domain_get_start_time(grid)) &
  289. ) then
  290. grid%adapt_step_using_child = .TRUE.
  291. else
  292. grid%adapt_step_using_child = .FALSE.
  293. endif
  294. endif
  295. if (use_last2) then
  296. grid%last_dtInterval = last_dtInterval
  297. grid%last_max_vert_cfl = grid%last_max_vert_cfl
  298. grid%last_max_horiz_cfl = grid%last_max_horiz_cfl
  299. else
  300. grid%last_dtInterval = dtInterval
  301. grid%last_max_vert_cfl = grid%max_vert_cfl
  302. grid%last_max_horiz_cfl = grid%max_horiz_cfl
  303. endif
  304. grid%dt = real_time(dtInterval)
  305. grid%last_max_vert_cfl = grid%max_vert_cfl
  306. !
  307. ! Update the clock based on the new time step
  308. !
  309. CALL WRFU_ClockSet ( grid%domain_clock, &
  310. timeStep=dtInterval, &
  311. rc=rc )
  312. !
  313. ! If we're are adapting based on the child time step,
  314. ! we call the child from here. This assures that
  315. ! child and parent are updated in sync.
  316. ! Note: This is not necessary when we are adapting based
  317. ! upon parent.
  318. !
  319. if ((grid%id == 1) .and. (grid%adapt_step_using_child)) then
  320. !
  321. ! Finally, check if we can adapt using child. If we are
  322. ! stepping to an output time, we cannot adapt based upon
  323. ! child. So, we reset the variable before calling the child.
  324. ! This covers the case that, within this parent time-step that
  325. ! we just calculated, we are stepping to an output time.
  326. !
  327. if (grid%stepping_to_time) then
  328. grid%adapt_step_using_child = .FALSE.
  329. endif
  330. call adapt_timestep(grid%nests(1)%ptr, config_flags)
  331. endif
  332. !
  333. ! Lateral boundary weight recomputation based on time step.
  334. !
  335. if (grid%id == 1) then
  336. CALL lbc_fcx_gcx ( grid%fcx , grid%gcx , grid%spec_bdy_width , &
  337. grid%spec_zone , grid%relax_zone , grid%dt , config_flags%spec_exp , &
  338. config_flags%specified , config_flags%nested )
  339. endif
  340. ! Update last timestep info for restart file
  341. CALL WRFU_TimeIntervalGet(grid%last_dtInterval, S=grid%last_dt_sec, &
  342. Sn=grid%last_dt_sec_num, Sd=grid%last_dt_sec_den)
  343. END SUBROUTINE adapt_timestep
  344. SUBROUTINE calc_dt(dtInterval, max_cfl, max_increase_factor, precision, &
  345. last_dtInterval, target_cfl)
  346. USE module_domain
  347. TYPE(WRFU_TimeInterval) ,INTENT(OUT) :: dtInterval
  348. REAL ,INTENT(IN) :: max_cfl
  349. REAL ,INTENT(IN) :: max_increase_factor
  350. INTEGER ,INTENT(IN) :: precision
  351. REAL ,INTENT(IN) :: target_cfl
  352. TYPE(WRFU_TimeInterval) ,INTENT(IN) :: last_dtInterval
  353. REAL :: factor
  354. INTEGER :: num, den
  355. if (max_cfl < 0.001) then
  356. !
  357. ! If the max_cfl is small, then we increase dtInterval the maximum
  358. ! amount allowable.
  359. !
  360. num = INT(max_increase_factor * precision + 0.5)
  361. den = precision
  362. dtInterval = last_dtInterval * num / den
  363. else
  364. !
  365. ! If the max_cfl is greater than the user input target cfl, we
  366. ! reduce the time step,
  367. ! else, we increase it.
  368. !
  369. if (max_cfl .gt. target_cfl) then
  370. !
  371. ! If we are reducing the time step, we go below target cfl by half
  372. ! the difference between max and target.
  373. ! This tends to keep the model more stable.
  374. !
  375. factor = ( target_cfl - 0.5 * (max_cfl - target_cfl) ) / max_cfl
  376. num = INT(factor * precision + 0.5)
  377. den = precision
  378. dtInterval = last_dtInterval * num / den
  379. else
  380. !
  381. ! Linearly increase dtInterval (we'll limit below)
  382. !
  383. factor = target_cfl / max_cfl
  384. num = INT(factor * precision + 0.5)
  385. den = precision
  386. dtInterval = last_dtInterval * num / den
  387. endif
  388. endif
  389. END SUBROUTINE calc_dt
  390. FUNCTION real_time( timeinterval ) RESULT ( out_time )
  391. USE module_domain
  392. IMPLICIT NONE
  393. ! This function returns a floating point time from an input time interval
  394. !
  395. ! Unfortunately, the ESMF did not provide this functionality.
  396. !
  397. ! Be careful with the output because, due to rounding, the time is only
  398. ! approximate.
  399. !
  400. ! Todd Hutchinson, WSI
  401. ! 4/17/2007
  402. ! !RETURN VALUE:
  403. REAL :: out_time
  404. INTEGER :: dt_num, dt_den, dt_whole
  405. ! !ARGUMENTS:
  406. TYPE(WRFU_TimeInterval), intent(INOUT) :: timeinterval
  407. CALL WRFU_TimeIntervalGet(timeinterval,Sn=dt_num,Sd=dt_den,S=dt_whole)
  408. if (ABS(dt_den) < 1) then
  409. out_time = dt_whole
  410. else
  411. out_time = dt_whole + dt_num / REAL(dt_den)
  412. endif
  413. END FUNCTION
  414. FUNCTION real_time_r8( timeinterval ) RESULT ( out_time )
  415. USE module_domain
  416. IMPLICIT NONE
  417. ! This function returns a double precision floating point time from an input time interval
  418. !
  419. ! Unfortunately, the ESMF did not provide this functionality.
  420. !
  421. ! Be careful with the output because, due to rounding, the time is only
  422. ! approximate.
  423. !
  424. ! Todd Hutchinson, WSI 4/17/2007
  425. ! Converted to r8, William.Gustafson@pnl.gov; 8-May-2008
  426. ! !RETURN VALUE:
  427. REAL(KIND=8) :: out_time
  428. INTEGER(KIND=8) :: dt_whole
  429. INTEGER :: dt_num, dt_den
  430. ! !ARGUMENTS:
  431. TYPE(WRFU_TimeInterval), intent(INOUT) :: timeinterval
  432. CALL WRFU_TimeIntervalGet(timeinterval,Sn=dt_num,Sd=dt_den,S_i8=dt_whole)
  433. if (ABS(dt_den) < 1) then
  434. out_time = REAL(dt_whole)
  435. else
  436. out_time = REAL(dt_whole) + REAL(dt_num,8)/REAL(dt_den,8)
  437. endif
  438. END FUNCTION real_time_r8