PageRenderTime 41ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/wrfv2_fire/phys/module_fr_sfire_model.F

http://github.com/jbeezley/wrf-fire
FORTRAN Legacy | 654 lines | 379 code | 88 blank | 187 comment | 19 complexity | 4b6556c89e111c892f8fe8fd71692e0f MD5 | raw file
Possible License(s): AGPL-1.0
  1. !
  2. #define DEBUG_OUT
  3. module module_fr_sfire_model
  4. use module_fr_sfire_core
  5. use module_fr_sfire_util
  6. use module_fr_sfire_phys
  7. implicit none
  8. contains
  9. subroutine sfire_model ( &
  10. id, & ! unique number for prints and debug
  11. ifun, & ! what to do see below
  12. restart,replay, & ! use existing state, prescribe spread
  13. run_fuel_moisture, & ! if need update fuel moisture in pass 4
  14. ifuelread,nfuel_cat0, & ! initialize fuel categories
  15. ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
  16. ifms,ifme,jfms,jfme, & ! fire memory dims - how declared
  17. ifps,ifpe,jfps,jfpe, & ! patch - nodes owned by this process
  18. ifts,ifte,jfts,jfte, & ! fire tile dims - this thread
  19. time_start,dt, & ! time and increment
  20. fdx,fdy, & ! fire mesh spacing,
  21. ignition,hfx, & ! small array of ignition line descriptions
  22. coord_xf,coord_yf, & ! fire mesh coordinates
  23. fire_hfx, & ! input: given heat flux, or set inside
  24. lfn,lfn_out,tign,fuel_frac,fire_area, & ! state: level function, ign time, fuel left, area burning
  25. fuel_frac_burnt, & ! output: fuel fraction burnt in this timestep
  26. fgrnhfx,fgrnqfx, & ! output: heat fluxes
  27. ros,flineint,flineint2, & ! diagnostic variables
  28. f_ros0,f_rosx,f_rosy,f_ros, & ! fire risk spread
  29. f_int,f_lineint,f_lineint2, & ! fire risk intensities
  30. nfuel_cat, & ! fuel data per point
  31. fuel_time,fwh,fz0, & ! save derived internal data
  32. fp &
  33. )
  34. ! This subroutine implements the fire spread model.
  35. ! All quantities are on the fire grid. It inputs
  36. ! winds given on the nodes of the fire grid
  37. ! and outputs the heat fluxes on the cells of the fire grid.
  38. ! This subroutine has no knowledge of any atmospheric model.
  39. ! This code was written to conform with the WRF parallelism model, however it
  40. ! does not depend on it. It can be called with domain equal to tile.
  41. ! Wind and height must be given on 1 more node beyond the domain bounds.
  42. ! The subroutine changes only array entries of the arguments in the tile.
  43. ! Upon exit with ifun=2 (time step), lfn_out is to be copied into lfn by the caller.
  44. ! When this subroutine is used on separate tiles that make a domain the value, the
  45. ! it uses lfn on a strip of width 2 from neighboring tiles.
  46. !
  47. ! All computation is done on one tile.
  48. !
  49. ! This subroutine is intended to be called in a loop like
  50. !
  51. !
  52. ! do ifun=1,6 (if initizalize run, otherwise 3,6)
  53. ! start parallel loop over tiles
  54. ! if ifun=1, set z and fuel data
  55. ! if ifun=3, set the wind arrays
  56. ! call sfire_model(....)
  57. ! end parallel loop over tiles
  58. !
  59. !
  60. ! if ifun=0
  61. ! halo exchange on z width 2
  62. ! halo exchange on fuel data width 1
  63. ! endif
  64. !
  65. ! if ifun=3, halo exchange on winds width 2
  66. !
  67. ! enddo
  68. implicit none
  69. !*** arguments
  70. ! control switches
  71. integer, intent(in) :: id
  72. integer, intent(in) :: ifun ! 1 = initialize run pass 1
  73. ! 2 = initialize run pass 2
  74. ! 3 = initialize timestep
  75. ! 4 = do one timestep
  76. ! 5 = copy timestep output to input
  77. ! 6 = compute output fluxes
  78. logical, intent(in):: restart ! if true, use existing state
  79. logical, intent(in):: replay ! if true, use tign_g for level set
  80. logical, intent(in)::run_fuel_moisture !
  81. ! scalar data
  82. integer, intent(in) :: ifuelread,nfuel_cat0 ! for set_fire_params
  83. integer, intent(in) :: ifds,ifde,jfds,jfde,& ! fire domain bounds
  84. ifps,ifpe,jfps,jfpe ! patch - nodes owned by this process
  85. integer, intent(in) :: ifts,ifte,jfts,jfte ! fire tile bounds
  86. integer, intent(in) :: ifms,ifme,jfms,jfme ! fire memory array bounds
  87. REAL,INTENT(in) :: time_start,dt ! starting time, time step
  88. REAL,INTENT(in) :: fdx,fdy ! spacing of the fire mesh
  89. ! array data
  90. type(lines_type), intent(in):: ignition,hfx ! descriptions of ignition lines and hfx lines
  91. real, dimension(ifms:ifme, jfms:jfme), intent(in):: &
  92. coord_xf,coord_yf ! node coordinates
  93. real, dimension(ifms:ifme, jfms:jfme), intent(inout):: &
  94. fire_hfx ! given heat flux
  95. ! state
  96. REAL, INTENT(inout), dimension(ifms:ifme,jfms:jfme):: &
  97. lfn , & ! level function: fire is where lfn<0 (node)
  98. tign , & ! absolute time of ignition (node)
  99. fuel_frac ! fuel fraction (node), currently redundant
  100. REAL, INTENT(out), dimension(ifms:ifme,jfms:jfme):: &
  101. fire_area, & ! fraction of each cell burning
  102. fuel_frac_burnt ! fuel fraction burned in this timestep
  103. ! output
  104. REAL, INTENT(out), dimension(ifms:ifme,jfms:jfme):: &
  105. lfn_out, & !
  106. fgrnhfx,fgrnqfx, & ! heat fluxes J/m^2/s (cell)
  107. ros,flineint,flineint2, & ! diagnostic variables
  108. f_ros0,f_rosx,f_rosy,f_ros, & ! fire risk spread
  109. f_int,f_lineint,f_lineint2 ! fire risk intensities
  110. ! constant arrays - set at initialization
  111. real, intent(inout), dimension(ifms:ifme, jfms:jfme)::nfuel_cat ! cell based, data, constant
  112. real,intent(inout),dimension(ifms:ifme,jfms:jfme):: fuel_time,fwh,fz0
  113. type(fire_params),intent(inout)::fp
  114. !*** local
  115. integer :: xifms,xifme,xjfms,xjfme ! memory bounds for pass-through arguments to normal spread
  116. real, dimension(ifts:ifte,jfts:jfte)::fuel_frac_end
  117. integer::ignited,ig,i,j,itso,iteo,jtso,jteo
  118. real::tbound,err,erri,errj,maxgrad,grad,tfa,thf,mhf,tqf,mqf,aw,mw,t
  119. character(len=128)::msg
  120. logical:: freeze_fire
  121. real::fireline_mask=-1.
  122. !*** executable
  123. call check_mesh_2dim(ifts-1,ifte+1,jfts-1,jfte+1,ifms,ifme,jfms,jfme)
  124. xifms=ifms ! dimensions for the include file
  125. xifme=ifme
  126. xjfms=jfms
  127. xjfme=jfme
  128. ! init flags
  129. freeze_fire = fire_hfx_given .ne. 0
  130. if(ifun.eq.1)then ! do nothing, init pass 1 is outside only
  131. ! !$OMP SINGLE
  132. !! done in driver now
  133. ! call init_fuel_cats ! initialize fuel subsystem
  134. ! !$OMP END SINGLE
  135. if(replay) then
  136. ! when starting replay, recompute lfn and fuel_frac
  137. call message('replay, setting the level set function',level=1)
  138. do j=jfts,jfte
  139. do i=ifts,ifte
  140. lfn(i,j) = tign(i,j) - time_start ! <0 if burning at the end of the time step
  141. enddo
  142. enddo
  143. endif
  144. call check_lfn_tign('starting replay',time_start,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,tign)
  145. elseif(ifun.eq.2)then
  146. ! initialize all arrays that the model will not change later
  147. ! assuming halo on zsf done
  148. ! extrapolate on 1 row of cells beyond the domain boundary
  149. ! including on the halo regions
  150. call continue_at_boundary(1,1,0., & ! do x direction or y direction
  151. ifms,ifme,jfms,jfme, & ! memory dims
  152. ifds,ifde,jfds,jfde, & ! domain dims
  153. ifps,ifpe,jfps,jfpe, & ! patch dims - winds defined up to +1
  154. ifts,ifte,jfts,jfte, & ! tile dims
  155. itso,iteo,jtso,jteo, & ! where set now
  156. fp%zsf) ! array
  157. ! compute the gradients once for all
  158. err=0.
  159. maxgrad=0.
  160. do j=jfts,jfte
  161. do i=ifts,ifte
  162. erri = fp%dzdxf(i,j) - (fp%zsf(i+1,j)-fp%zsf(i-1,j))/(2.*fdx)
  163. errj = fp%dzdyf(i,j) - (fp%zsf(i,j+1)-fp%zsf(i,j-1))/(2.*fdy)
  164. err=max(err,abs(erri),abs(errj))
  165. grad=sqrt(fp%dzdxf(i,j)**2+fp%dzdyf(i,j)**2)
  166. maxgrad=max(maxgrad,grad)
  167. enddo
  168. enddo
  169. !$OMP CRITICAL(SFIRE_MODEL_CRIT)
  170. write(msg,*)'max gradient ',maxgrad,' max error against zsf',err
  171. !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
  172. call message(msg)
  173. call set_nfuel_cat( & ! also on restart
  174. ifms,ifme,jfms,jfme, &
  175. ifts,ifte,jfts,jfte, &
  176. ifuelread,nfuel_cat0,&
  177. fp%zsf,nfuel_cat) ! better not use the extrapolated zsf!!
  178. ! uses nfuel_cat to set the other fuel data arrays
  179. ! needs zsf on halo width 1 to compute the terrain gradient
  180. call set_fire_params( & ! also on restart
  181. ifds,ifde,jfds,jfde, &
  182. ifms,ifme,jfms,jfme, &
  183. ifts,ifte,jfts,jfte, &
  184. fdx,fdy,nfuel_cat0, &
  185. nfuel_cat,fuel_time, &
  186. fp)
  187. ! initialize model state to no fire
  188. if(.not.restart)then
  189. call init_no_fire ( &
  190. ifds,ifde,jfds,jfde, &
  191. ifms,ifme,jfms,jfme, &
  192. ifts,ifte,jfts,jfte, &
  193. fdx,fdy,time_start,dt, &
  194. fuel_frac,fire_area,lfn,tign)
  195. endif
  196. if(replay) then
  197. call message('replay, recomputing fuel fraction')
  198. call check_lfn_tign('recomputing fuel fraction',time_start,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,tign)
  199. call fuel_left( &
  200. ifds,ifde,jfds,jfde, &
  201. ifms,ifme,jfms,jfme, &
  202. ifts,ifte,jfts,jfte, &
  203. ifms,ifme,jfms,jfme, &
  204. lfn,tign,fuel_time,time_start,fuel_frac,fire_area) !fuel_frac is global
  205. endif
  206. elseif(ifun.eq.3)then ! ignition if so specified
  207. elseif (ifun.eq.4) then ! do the timestep
  208. if(run_fuel_moisture)then
  209. ! uses nfuel_cat to set the other fuel data arrays
  210. ! needs zsf on halo width 1 to compute the terrain gradient
  211. call set_fire_params( & ! also on restart
  212. ifds,ifde,jfds,jfde, &
  213. ifms,ifme,jfms,jfme, &
  214. ifts,ifte,jfts,jfte, &
  215. fdx,fdy,nfuel_cat0, &
  216. nfuel_cat,fuel_time, &
  217. fp)
  218. endif
  219. if(fire_print_msg.ge.stat_lev)then
  220. aw=fun_real(RNRM_SUM, &
  221. ifms,ifme,1,1,jfms,jfme, & ! memory dims
  222. ifds,ifde,1,1,jfds,jfde, & ! domain dims
  223. ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
  224. 0,0,0, & ! staggering
  225. fp%vx,fp%vy)/((ifde-ifds+1)*(jfde-jfds+1))
  226. mw=fun_real(RNRM_MAX, &
  227. ifms,ifme,1,1,jfms,jfme, & ! memory dims
  228. ifds,ifde,1,1,jfds,jfde, & ! domain dims
  229. ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
  230. 0,0,0, & ! staggering
  231. fp%vx,fp%vy)
  232. !$OMP MASTER
  233. write(msg,91)time_start,'Average surface wind',aw,'m/s'
  234. call message(msg,stat_lev)
  235. write(msg,91)time_start,'Maximum surface wind',mw,'m/s'
  236. call message(msg,stat_lev)
  237. !$OMP END MASTER
  238. endif
  239. ! compute fuel fraction at start
  240. ! call fuel_left( &
  241. ! ifms,ifme,jfms,jfme, &
  242. ! ifts,ifte,jfts,jfte, &
  243. ! ifms,ifme,jfms,jfme, &
  244. ! lfn,tign,fuel_time,time_start,fuel_frac,fire_area) ! fuel frac is shared
  245. call print_2d_stats(ifts,ifte,jfts,jfte, &
  246. ifms,ifme,jfms,jfme, &
  247. fuel_frac,'model: fuel_frac start')
  248. ! advance the model from time_start to time_start+dt
  249. ! return the fuel fraction burnt this call in each fire cell
  250. ! will call module_fr_sfire_speed::normal_spread for propagation speed
  251. ! We cannot simply compute the spread rate here because that will change with the
  252. ! angle of the wind and the direction of propagation, thus it is done in subroutine
  253. ! normal_spread at each fire time step. Instead, we pass arguments that
  254. ! the speed function may use as fp.
  255. ! propagate level set function in time
  256. ! set lfn_out tign
  257. ! lfn does not change, tign has no halos
  258. if(.not. freeze_fire)then
  259. if(.not.replay) then
  260. call prop_ls(id, &
  261. ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
  262. ifms,ifme,jfms,jfme, &
  263. ifps,ifpe,jfps,jfpe, & ! patch - nodes owned by this process
  264. ifts,ifte,jfts,jfte, &
  265. time_start,dt,fdx,fdy,tbound, &
  266. lfn,lfn_out,tign,ros, fp &
  267. )
  268. else
  269. do j=jfts,jfte
  270. do i=ifts,ifte
  271. lfn_out(i,j) = tign(i,j) - (time_start + dt) ! <0 if burning at the end of the time step
  272. enddo
  273. enddo
  274. endif
  275. else
  276. call message('sfire_model: EXPERIMENTAL: skipping fireline propagation')
  277. endif
  278. elseif (ifun.eq.5) then ! copy the result of timestep back to input
  279. ! this cannot be done in the time step itself because of race condition
  280. ! some thread may still be using lfn as input in their tile halo
  281. if(.not. freeze_fire)then
  282. do j=jfts,jfte
  283. do i=ifts,ifte
  284. lfn(i,j)=lfn_out(i,j)
  285. ! if want to try timestep again treat tign the same way here
  286. ! even if tign does not need a halo
  287. enddo
  288. enddo
  289. ! check for ignitions
  290. do ig = 1,ignition%num_lines
  291. ! for now, check for ignition every time step...
  292. ! if(ignition%line(ig)%end_time>=time_start.and.ignition%line(ig)%start_time<time_start+dt)then
  293. call ignite_fire( &
  294. ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
  295. ifms,ifme,jfms,jfme, &
  296. ifts,ifte,jfts,jfte, &
  297. ignition%line(ig), &
  298. time_start,time_start+dt, &
  299. coord_xf,coord_yf,ignition%unit_fxlong,ignition%unit_fxlat, &
  300. lfn,tign,ignited)
  301. #ifdef DEBUG_OUT
  302. call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,'lfn_ig',id)
  303. call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,coord_xf,'coord_xf_ig',id)
  304. call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,coord_yf,'coord_yf_ig',id)
  305. #endif
  306. ! endif
  307. enddo
  308. else
  309. call message('sfire_model: EXPERIMENTAL: skipping ignition')
  310. endif
  311. call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme, &
  312. lfn,'sfire_model: lfn out')
  313. elseif (ifun.eq.6) then ! timestep postprocessing
  314. ! have halo on lfn now
  315. ! diagnostics
  316. call fire_intensity(fp, & ! fuel properties
  317. ifms,ifme,jfms,jfme, & ! memory dims
  318. ifts,ifte,jfts,jfte, & ! tile dims
  319. ifms,ifme,jfms,jfme, & ! ros dims
  320. ros,nfuel_cat, & !in
  321. flineint,flineint2) ! fireline intensities out
  322. if(fireline_mask < 0.) then
  323. do j=jfts,jfte
  324. do i=ifts,ifte
  325. ! if the sign of lfn is the same as all of its neighbors or we are at domain boundary, we are not near the fireline
  326. if( (lfn(i-1,j-1)>0. .and. lfn(i-1,j)>0. .and. lfn(i,j-1)>0. .and. lfn(i,j)>0. .and. &
  327. lfn(i+1,j+1)>0. .and. lfn(i+1,j)>0. .and. lfn(i,j+1)>0. ) .or. &
  328. (lfn(i-1,j-1)<0. .and. lfn(i-1,j)<0. .and. lfn(i,j-1)<0. .and. lfn(i,j)<0. .and. &
  329. lfn(i+1,j+1)<0. .and. lfn(i+1,j)<0. .and. lfn(i,j+1)<0. ) .or. &
  330. i.eq.ifds .or. i .eq. ifde .or. j.eq.jfds .or. j.eq.jfde) then
  331. ros(i,j)=fireline_mask
  332. flineint(i,j)=fireline_mask
  333. flineint2(i,j)=fireline_mask
  334. endif
  335. enddo
  336. enddo
  337. endif
  338. call fire_risk(fp, &
  339. ifms,ifme,jfms,jfme, & ! memory dims
  340. ifts,ifte,jfts,jfte, & ! tile dims
  341. nfuel_cat, & !
  342. f_ros0,f_rosx,f_rosy,f_ros, & ! fire spread
  343. f_int,f_lineint,f_lineint2) ! fire intensities for danger rating
  344. select case(fire_hfx_given)
  345. case(0) ! normal
  346. ! compute the heat fluxes from the fuel burned
  347. ! needs lfn and tign from neighbors so halo must be updated before
  348. !$OMP CRITICAL(SFIRE_MODEL_CRIT)
  349. write(msg,*)'time_start=',time_start,' dt=',dt,' before fuel_left'
  350. !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
  351. call message(msg)
  352. call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,'model: lfn')
  353. call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,tign,'model: tign')
  354. call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,fuel_time,'model: fuel_time')
  355. call fuel_left(&
  356. ifds,ifde,jfds,jfde, &
  357. ifms,ifme,jfms,jfme, &
  358. ifts,ifte,jfts,jfte, &
  359. ifts,ifte,jfts,jfte, &
  360. lfn,tign,fuel_time,time_start+dt,fuel_frac_end,fire_area) !fuel_frac_end is private and tile based
  361. call print_2d_stats(ifts,ifte,jfts,jfte,ifts,ifte,jfts,jfte,fuel_frac_end,'model: fuel_frac end')
  362. do j=jfts,jfte
  363. do i=ifts,ifte
  364. t = min(fuel_frac(i,j),fuel_frac_end(i,j)) ! do not allow fuel fraction to increase, in case of approximation error
  365. fuel_frac_burnt(i,j)=fuel_frac(i,j)-t ! fuel lost this timestep
  366. fuel_frac(i,j)=t ! copy new value to state array
  367. enddo
  368. enddo
  369. call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,fuel_frac_burnt,'model: fuel_frac burned')
  370. call heat_fluxes(dt,fp, &
  371. ifms,ifme,jfms,jfme, &
  372. ifts,ifte,jfts,jfte, &
  373. ifms,ifme,jfms,jfme, & ! fuel_frac_burned has standard memory dimensions
  374. fp%fgip, &
  375. fuel_frac_burnt, & !
  376. fgrnhfx,fgrnqfx) !out
  377. case (1, 2)
  378. !$OMP CRITICAL(SFIRE_MODEL_CRIT)
  379. write(msg,*)"model: expecting fire_hfx to be set in WRF, from wrfinput or wrfrst files"
  380. call message(msg)
  381. !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
  382. do j=jfts,jfte
  383. do i=ifts,ifte
  384. fgrnhfx(i,j) = (1. - fire_hfx_latent_part)*fire_hfx(i,j)
  385. fgrnqfx(i,j) = fire_hfx_latent_part *fire_hfx(i,j)
  386. enddo
  387. enddo
  388. case (3)
  389. call message('artificial heat flux from parameters given in namelist.input')
  390. call param_hfx( time_start, &
  391. ifms,ifme,jfms,jfme, &
  392. ifts,ifte,jfts,jfte, &
  393. coord_xf,coord_yf, &
  394. hfx, &
  395. fire_area,fgrnhfx,fgrnqfx)
  396. case default
  397. call crash('bad fire_hfx_given')
  398. end select
  399. ! this should run in any case
  400. if(fire_print_msg.ge.stat_lev)then
  401. tfa=fun_real(REAL_SUM, &
  402. ifms,ifme,1,1,jfms,jfme, & ! memory dims
  403. ifds,ifde,1,1,jfds,jfde, & ! domain dims
  404. ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
  405. 0,0,0, & ! staggering
  406. fire_area,fire_area) * fdx * fdy
  407. thf=fun_real(REAL_SUM, &
  408. ifms,ifme,1,1,jfms,jfme, & ! memory dims
  409. ifds,ifde,1,1,jfds,jfde, & ! domain dims
  410. ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
  411. 0,0,0, & ! staggering
  412. fgrnhfx,fgrnhfx) * fdx * fdy
  413. mhf=fun_real(REAL_MAX, &
  414. ifms,ifme,1,1,jfms,jfme, & ! memory dims
  415. ifds,ifde,1,1,jfds,jfde, & ! domain dims
  416. ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
  417. 0,0,0, & ! staggering
  418. fgrnhfx,fgrnhfx)
  419. tqf=fun_real(REAL_SUM, &
  420. ifms,ifme,1,1,jfms,jfme, & ! memory dims
  421. ifds,ifde,1,1,jfds,jfde, & ! domain dims
  422. ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
  423. 0,0,0, & ! staggering
  424. fgrnqfx,fgrnqfx) * fdx * fdy
  425. mqf=fun_real(REAL_MAX, &
  426. ifms,ifme,1,1,jfms,jfme, & ! memory dims
  427. ifds,ifde,1,1,jfds,jfde, & ! domain dims
  428. ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
  429. 0,0,0, & ! staggering
  430. fgrnqfx,fgrnqfx)
  431. !$OMP MASTER
  432. write(msg,91)time_start,'Fire area ',tfa,'m^2'
  433. call message(msg,stat_lev)
  434. write(msg,91)time_start,'Heat output ',thf,'W'
  435. call message(msg,stat_lev)
  436. write(msg,91)time_start,'Max heat flux ',mhf,'W/m^2'
  437. call message(msg,stat_lev)
  438. write(msg,91)time_start,'Latent heat output ',tqf,'W'
  439. call message(msg,stat_lev)
  440. write(msg,91)time_start,'Max latent heat flux',mqf,'W/m^2'
  441. call message(msg,stat_lev)
  442. !$OMP END MASTER
  443. 91 format('Time ',f11.3,' s ',a,e12.3,1x,a)
  444. endif
  445. call print_2d_stats(ifts,ifte,jfts,jfte, &
  446. ifms,ifme,jfms,jfme, &
  447. fgrnhfx,'model: heat flux(J/m^2/s)')
  448. else
  449. !$OMP CRITICAL(SFIRE_MODEL_CRIT)
  450. write(msg,*)'sfire_model: bad ifun=',ifun
  451. !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
  452. call crash(msg)
  453. endif
  454. end subroutine sfire_model
  455. subroutine param_hfx( time_now,&
  456. ifms,ifme,jfms,jfme, &
  457. ifts,ifte,jfts,jfte, &
  458. coord_xf,coord_yf, &
  459. hfx, &
  460. fire_area,fgrnhfx,fgrnqfx)
  461. !*** generate artifical heat flux from a parametric description
  462. !*** arguments
  463. real, intent(in)::time_now
  464. integer, intent(in):: &
  465. ifms,ifme,jfms,jfme, &
  466. ifts,ifte,jfts,jfte
  467. type(lines_type), intent(in)::hfx
  468. real, dimension(ifms:ifme,jfms:jfme), intent(in)::coord_xf,coord_yf ! nodal coordinates
  469. real, dimension(ifms:ifme,jfms:jfme), intent(out)::fire_area,fgrnhfx,fgrnqfx ! the imposed heat flux
  470. character(len=128):: msg
  471. !*** local
  472. integer::i,j,k,nfa,ncells
  473. real:: d2,ax,ay,hfrac,fa,thfx,t,r,radius
  474. real, parameter:: sigma_mult=3. ! 3 gives drop to 1% in trans. time from gaussian kernel
  475. real:: maxspeed=100 ! max speed how the circle can move
  476. do j=jfts,jfte ! zero out the outputs
  477. do i=ifts,ifte
  478. fire_area(i,j)=0
  479. fgrnhfx(i,j) = 0.
  480. fgrnqfx(i,j) = 0.
  481. enddo
  482. enddo
  483. do k=1,hfx%num_lines
  484. if(hfx%line(k)%radius > 0.)then
  485. ! processing heatflux line i
  486. ! find the time multiplier
  487. t = max(hfx%line(k)%start_time - time_now, time_now - hfx%line(k)%end_time)
  488. if(t > 0.)then ! postitive distance from the time interval
  489. r = t / hfx%line(k)%trans_time ! position in the transition - 1 is at transition distance
  490. hfrac = exp(-(sigma_mult * r)**2/2.) ! gaussian kernel
  491. else
  492. hfrac = 1.0
  493. endif
  494. ! find the coordinates of the center of the heat flux circle now
  495. ax = safe_prop(time_now, &
  496. hfx%line(k)%start_time,&
  497. hfx%line(k)%end_time,&
  498. hfx%line(k)%start_x,&
  499. hfx%line(k)%end_x, &
  500. hfx%unit_fxlong*maxspeed)
  501. ay = safe_prop(time_now,&
  502. hfx%line(k)%start_time,&
  503. hfx%line(k)%end_time,&
  504. hfx%line(k)%start_y,&
  505. hfx%line(k)%end_y, &
  506. hfx%unit_fxlat*maxspeed)
  507. radius=hfx%line(k)%radius
  508. !$OMP CRITICAL(SFIRE_MODEL_CRIT)
  509. write(msg,*)'hfx line ',i,' at ',time_now,'s ',hfrac,' of max ', hfx%line(k)%hfx_value
  510. call message(msg)
  511. write(msg,*)'center ',ax,ay,' radius ',radius
  512. call message(msg)
  513. !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
  514. nfa=0
  515. ncells=0
  516. do j=jfts,jfte
  517. do i=ifts,ifte
  518. ! distance squared from the center
  519. d2 = (hfx%unit_fxlong*(ax - coord_xf(i,j)))**2 + (hfx%unit_fxlat*(ay - coord_yf(i,j)))**2
  520. if(d2 < radius**2)then
  521. fa=1.
  522. else
  523. fa=0.
  524. endif
  525. ! set heat fluxes
  526. thfx= hfx%line(k)%hfx_value * hfrac * fa ! total heat flux at this point
  527. fgrnhfx(i,j)= fgrnhfx(i,j) + (1.-fire_hfx_latent_part) * thfx
  528. fgrnqfx(i,j)= fgrnqfx(i,j) + fire_hfx_latent_part * thfx
  529. ! set fire area
  530. fire_area(i,j) = max(fire_area(i,j),fa)
  531. ! set stats
  532. nfa=nfa+fa;
  533. ncells=ncells+1
  534. enddo
  535. enddo
  536. !$OMP CRITICAL(SFIRE_MODEL_CRIT)
  537. write(msg,*)'Number of cells in fire area in this tile ',nfa,' ',(100.*nfa)/ncells,' %'
  538. call message(msg)
  539. !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
  540. endif
  541. enddo
  542. end subroutine param_hfx
  543. real function safe_prop(t,t1,t2,x1,x2,ms)
  544. ! return x between x1 and x2 in the same proportion as is t between t1 and t2, safe in the case when t1=t2 and x1=x2
  545. ! safe_prop = x1 + (t-t1)*(x2-x1)/(t2-t1)
  546. ! future: abs((x2-x1)/(t2-t1)) capped at ms but still return x1 when t=t1 and x2 when t=t2
  547. real, intent(in)::t,t1,t2,x1,x2,ms
  548. real:: p,x
  549. if(t2 < t1)call crash('safe_prop: must have t2>t1')
  550. if(.not.ms>0.)call crash('safe_prop: must have ms>0')
  551. if(t1 .eq. t2)then
  552. if(x1.eq.x2)then
  553. x=x1
  554. else
  555. call crash('safe_prop: infinite speed')
  556. endif
  557. else
  558. p = (t - t1)/(t2 - t1) ! 0 at t1, 1 at t2
  559. x = x1*(1.-p) + x2*p
  560. endif
  561. safe_prop=x
  562. end function safe_prop
  563. !
  564. !*****************
  565. !
  566. end module module_fr_sfire_model