PageRenderTime 52ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/wrfv2_fire/phys/module_fr_sfire_core.F

http://github.com/jbeezley/wrf-fire
FORTRAN Legacy | 2140 lines | 1031 code | 293 blank | 816 comment | 97 complexity | 308bf6021acb7d30f760f7a60c13a95a MD5 | raw file
Possible License(s): AGPL-1.0

Large files files are truncated, but you can click here to view the full file

  1. !
  2. !*** Jan Mandel August-October 2007 email: jmandel@ucar.edu or Jan.Mandel@gmail.com
  3. !
  4. ! With contributions by Minjeong Kim.
  5. #define DEBUG_OUT
  6. #define DEBUG_PRINT
  7. !#define DEBUG_OUT_FUEL_LEFT
  8. module module_fr_sfire_core
  9. use module_fr_sfire_phys, only: fire_params , fire_ros
  10. use module_fr_sfire_util
  11. implicit none
  12. ! The mathematical core of the fire spread model. No physical constants here.
  13. !
  14. ! subroutine sfire_core: only this routine should be called from the outside.
  15. ! subroutine fuel_left: compute remaining fuel from time of ignition.
  16. ! subroutine prop_ls: propagation of curve in normal direction.
  17. contains
  18. !
  19. !****************************************
  20. !
  21. subroutine init_no_fire(&
  22. ifds,ifde,jfds,jfde, &
  23. ifms,ifme,jfms,jfme, &
  24. ifts,ifte,jfts,jfte, &
  25. fdx,fdy,time_start,dt, & ! scalars in
  26. fuel_frac,fire_area,lfn,tign) ! arrays out
  27. implicit none
  28. !*** purpose: initialize model to no fire
  29. !*** arguments
  30. integer, intent(in):: ifds,ifde,jfds,jfde ! fire domain bounds
  31. integer, intent(in):: ifts,ifte,jfts,jfte ! fire tile bounds
  32. integer, intent(in):: ifms,ifme,jfms,jfme ! array bounds
  33. real, intent(in) :: fdx,fdy,time_start,dt ! mesh spacing, time
  34. real, intent(out), dimension (ifms:ifme,jfms:jfme) :: &
  35. fuel_frac,fire_area,lfn,tign ! model state
  36. !*** calls
  37. intrinsic epsilon
  38. !*** local
  39. integer:: i,j
  40. real:: lfn_init,time_init,time_now
  41. character(len=128):: msg
  42. time_now = time_start+dt
  43. time_init = time_start+2*dt ! could be time_start+dt if not for rounding errors
  44. lfn_init = 2*max((ifde-ifds+1)*fdx,(jfde-jfds+1)*fdy) ! more than domain diameter
  45. if(fire_perimeter_time > 0.) then
  46. do j=jfts,jfte
  47. do i=ifts,ifte
  48. fuel_frac(i,j)=1. ! fuel at start is 1 by definition
  49. fire_area(i,j)=0. ! nothing burning
  50. lfn(i,j) = tign(i,j)-time_now ! use specified ignition time as level set function
  51. if(.not.lfn(i,j) > 0.)then
  52. write(msg,*)'i,j=',i,j,' tign=',tign(i,j),' <= time_now =',time_now
  53. call message(msg,level=0)
  54. call crash('init_no_fire: ignition time must be after the end of the time step')
  55. endif
  56. enddo
  57. enddo
  58. call message('init_no_fire: using given ignition time as history',level=1)
  59. else
  60. do j=jfts,jfte
  61. do i=ifts,ifte
  62. fuel_frac(i,j)=1. ! fuel at start is 1 by definition
  63. fire_area(i,j)=0. ! nothing burning
  64. tign(i,j) = time_init ! ignition in future
  65. lfn(i,j) = lfn_init ! no fire
  66. enddo
  67. enddo
  68. call message('init_no_fire: state set to no fire',level=1)
  69. endif
  70. call check_lfn_tign('init_no_fire',time_now,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
  71. end subroutine init_no_fire
  72. !
  73. !******************
  74. !
  75. subroutine ignite_fire( ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
  76. ifms,ifme,jfms,jfme, &
  77. ifts,ifte,jfts,jfte, &
  78. ignition_line, &
  79. start_ts,end_ts, &
  80. coord_xf,coord_yf, &
  81. unit_xf,unit_yf, &
  82. lfn,tign,ignited)
  83. implicit none
  84. !*** purpose: ignite a circular/line fire
  85. !*** description
  86. ! ignite fire in the region within radius r from the line (sx,sy) to (ex,ey).
  87. ! the coordinates of nodes are given as the arrays coord_xf and coord_yf
  88. ! r is given in m
  89. ! one unit of coord_xf is unit_xf m
  90. ! one unit of coord_yf is unit_yf m
  91. ! so a node (i,j) will be ignited iff for some (x,y) on the line
  92. ! || ( (coord_xf(i,j) - x)*unit_xf , (coord_yf(i,j) - y)*unit_yf ) || <= r
  93. !*** arguments
  94. integer, intent(in):: ifds,ifde,jfds,jfde ! fire domain bounds
  95. integer, intent(in):: ifts,ifte,jfts,jfte ! fire tile bounds
  96. integer, intent(in):: ifms,ifme,jfms,jfme ! array bounds
  97. type(line_type), intent(in):: ignition_line ! description of the ignition line
  98. real, intent(in):: start_ts,end_ts ! the time step start and end
  99. real, dimension(ifms:ifme, jfms:jfme), intent(in):: &
  100. coord_xf,coord_yf ! node coordinates
  101. real, intent(in):: unit_xf,unit_yf ! coordinate units in m
  102. real, intent(inout), dimension (ifms:ifme,jfms:jfme) :: &
  103. lfn, tign ! level function, ignition time (state)
  104. integer, intent(out):: ignited ! number of nodes newly ignited
  105. !*** local
  106. integer:: i,j
  107. real::lfn_new,tign_new,time_ign,ax,ay,rels,rele,d
  108. ! real:: lfn_new_chk,tign_new_chk,tmperr
  109. real:: sx,sy ! start of ignition line, from lower left corner
  110. real:: ex,ey ! end of ignition line, or zero
  111. real:: st,et ! start and end of time of the ignition line
  112. character(len=128):: msg
  113. real::cx2,cy2,dmax,axmin,axmax,aymin,aymax,dmin
  114. real:: start_x,start_y ! start of ignition line, from lower left corner
  115. real:: end_x,end_y ! end of ignition line, or zero
  116. real:: radius ! all within the radius of the line will ignite
  117. real:: start_time,end_time ! the ignition time for the start and the end of the line
  118. real:: ros,tos ! ignition rate and time of spread
  119. integer:: msglevel=4,smsg=2 ! print at this level
  120. real:: lfn_min
  121. !*** executable
  122. call check_lfn_tign('ignite_fire start',end_ts,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
  123. ! copy ignition line fields to local variables
  124. start_x = ignition_line%start_x ! x coordinate of the ignition line start point (m, or long/lat)
  125. start_y = ignition_line%start_y ! y coordinate of the ignition line start point
  126. end_x = ignition_line%end_x ! x coordinate of the ignition line end point
  127. end_y = ignition_line%end_y ! y coordinate of the ignition line end point
  128. start_time = ignition_line%start_time ! ignition time for the start point from simulation start (s)
  129. end_time = ignition_line%end_time! ignition time for the end poin from simulation start (s)
  130. radius = ignition_line%radius ! all within this radius ignites immediately
  131. ros = ignition_line%ros ! rate of spread
  132. tos = radius/ros ! time of spread to the given radius
  133. st = start_time ! the start time of ignition considered in this time step
  134. et = min(end_ts,end_time) ! the end time of the ignition segment in this time step
  135. ! this should be called whenever (start_ts, end_ts) \subset (start_time, end_time + tos)
  136. if(start_ts>et+tos .or. end_ts<st)return ! too late or too early, nothing to do
  137. if(start_time < end_time)then ! we really want to test start_time .ne. end_time, but avoiding test of floats on equality
  138. ! segment of nonzero length
  139. !rels = (st - start_time) / (end_time - start_time) ! relative position of st in the segment (start,end)
  140. !sx = start_x + rels * (end_x - start_x)
  141. !sy = start_y + rels * (end_y - start_y)
  142. rels = 0.
  143. sx = start_x
  144. sy = start_y
  145. rele = (et - start_time) / (end_time - start_time) ! relative position of et in the segment (start,end)
  146. ex = start_x + rele * (end_x - start_x)
  147. ey = start_y + rele * (end_y - start_y)
  148. else
  149. ! just a point
  150. rels = 0.
  151. rele = 1.
  152. sx = start_x
  153. sy = start_y
  154. ex = end_x
  155. ey = end_y
  156. endif
  157. cx2=unit_xf*unit_xf
  158. cy2=unit_yf*unit_yf
  159. axmin=coord_xf(ifts,jfts)
  160. aymin=coord_yf(ifts,jfts)
  161. axmax=coord_xf(ifte,jfte)
  162. aymax=coord_yf(ifte,jfte)
  163. if(fire_print_msg.ge.smsg)then
  164. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  165. write(msg,'(a,2f11.6,a,2f11.6)')'IGN from ',sx,sy,' to ',ex,ey
  166. call message(msg,level=smsg)
  167. write(msg,'(a,2f10.2,a,2f10.2,a)')'IGN timestep [',start_ts,end_ts,'] in [',start_time,end_time,']'
  168. call message(msg,level=smsg)
  169. write(msg,'(a,2g13.6,a,2g13.6)')'IGN tile coord from ',axmin,aymin,' to ',axmax,aymax
  170. call message(msg,level=smsg)
  171. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  172. endif
  173. ignited=0
  174. dmax=0
  175. dmin=huge(dmax)
  176. 11 format('IGN ',6(a,g17.7,1x))
  177. 12 format('IGN ',4(a,2g13.7,1x))
  178. ! tmperr=0.
  179. do j=jfts,jfte
  180. do i=ifts,ifte
  181. call check_lfn_tign_ij(i,j,'ignite_fire start',end_ts,lfn(i,j),tign(i,j))
  182. ax=coord_xf(i,j)
  183. ay=coord_yf(i,j)
  184. ! get d= distance from the nearest point on the ignition segment
  185. ! and time_ign = the ignition time there
  186. call nearest(d,time_ign,ax,ay,sx,sy,st,ex,ey,et,cx2,cy2)
  187. ! collect statistics
  188. dmax=max(d,dmax)
  189. dmin=min(d,dmin)
  190. ! tentative new level set function and time of ignition
  191. ! lfn_new_chk=d - min( radius, ros*(end_ts - time_ign) ) ! lft at end_ts
  192. ! tign_new_chk = time_ign + d/ros
  193. if(radius < ros*(end_ts - time_ign))then
  194. lfn_new = d - radius
  195. tign_new= time_ign + d/ros
  196. else
  197. lfn_new=d-ros*(end_ts - time_ign)
  198. tign_new=lfn_new/ros + end_ts ! consistent even with rounding errors
  199. endif
  200. ! tmperr=max(tmperr,abs(lfn_new - lfn_new_chk)+abs(tign_new-tign_new_chk))
  201. !lfn_new=lfn_new_chk
  202. !tign_new=tign_new_chk
  203. if(fire_print_msg.ge.msglevel)then
  204. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  205. write(msg,*)'IGN1 i,j=',i,j,' lfn(i,j)=',lfn(i,j),' tign(i,j)=',tign(i,j)
  206. call message(msg,level=0)
  207. write(msg,*)'IGN2 i,j=',i,j,' lfn_new= ',lfn_new, ' time_ign= ',time_ign,' d=',d
  208. call message(msg,level=msglevel)
  209. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  210. endif
  211. if(.not.lfn_new>0.) then
  212. ignited=ignited+1 ! count
  213. endif
  214. if(.not. lfn(i,j)<0. .and. lfn_new < 0.) then
  215. tign(i,j)=tign_new ! newly ignited now
  216. if(fire_print_msg.ge.msglevel)then
  217. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  218. write(msg,'(a,2i6,a,2g13.6,a,f10.2,a,2f10.2,a)')'IGN ignited cell ',i,j,' at',ax,ay, &
  219. ' time',tign(i,j),' in [',start_ts,end_ts,']'
  220. call message(msg,level=0)
  221. write(msg,'(a,g10.3,a,f10.2,a,2f10.2,a)')'IGN distance',d,' from ignition line at',time_ign,' in [',st,et,']'
  222. call message(msg,level=msglevel)
  223. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  224. endif
  225. if(tign(i,j) < start_ts .or. tign(i,j) > end_ts )then
  226. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  227. write(msg,'(a,2i6,a,f11.6,a,2f11.6,a)')'WARNING ',i,j, &
  228. ' fixing ignition time ',tign(i,j),' outside of the time step [',start_ts,end_ts,']'
  229. call message (msg)
  230. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  231. tign(i,j) = min(max(tign(i,j),start_ts),end_ts)
  232. endif
  233. endif
  234. lfn(i,j)=min(lfn(i,j),lfn_new) ! update the level set function
  235. if(fire_print_msg.ge.msglevel)then
  236. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  237. write(msg,*)'IGN3 i,j=',i,j,' lfn(i,j)=',lfn(i,j),' tign(i,j)=',tign(i,j)
  238. call message(msg,level=msglevel)
  239. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  240. endif
  241. call check_lfn_tign_ij(i,j,'ignite_fire end',end_ts,lfn(i,j),tign(i,j))
  242. enddo
  243. enddo
  244. ! write(msg,*)'ignite_fire: max error ',tmperr
  245. ! call message(msg)
  246. call check_lfn_tign("ignite_fire end:",end_ts,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
  247. if(fire_print_msg .ge. smsg)then
  248. lfn_min=huge(lfn_min)
  249. do j=jfts,jfte
  250. do i=ifts,ifte
  251. lfn_min=min(lfn_min,lfn(i,j))
  252. enddo
  253. enddo
  254. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  255. write(msg,'(a,2g13.2,a,g10.2,a,g10.2)')'IGN units ',unit_xf,unit_yf,' m max dist ',dmax,' min',dmin
  256. call message(msg,level=smsg)
  257. write(msg,'(a,f6.1,a,f8.1,a,i10,a,g10.2)')'IGN radius ',radius,' time of spread',tos, &
  258. ' ignited nodes',ignited,' lfn min',lfn_min
  259. call message(msg,level=smsg)
  260. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  261. endif
  262. end subroutine ignite_fire
  263. !
  264. !***
  265. !
  266. subroutine check_lfn_tign(s,time_now,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
  267. !*** purpose: check consistency of lfn and ignition
  268. implicit none
  269. !*** arguments
  270. character(len=*),intent(in)::s ! for print
  271. real, intent(in)::time_now ! end of timestep = time_now
  272. integer, intent(in):: ifts,ifte,jfts,jfte ! fire tile bounds
  273. integer, intent(in):: ifms,ifme,jfms,jfme ! array bounds
  274. real, intent(in), dimension (ifms:ifme,jfms:jfme) :: &
  275. lfn, tign ! level function, ignition time (state)
  276. !*** local
  277. integer:: i,j
  278. !*** executable
  279. do j=jfts,jfte
  280. do i=ifts,ifte
  281. call check_lfn_tign_ij(i,j,s,time_now,lfn(i,j),tign(i,j))
  282. enddo
  283. enddo
  284. end subroutine check_lfn_tign
  285. subroutine check_lfn_tign_ij(i,j,s,time_now,lfnij,tignij)
  286. !*** purpose: check consistency of lfn and ignition
  287. implicit none
  288. !*** arguments
  289. integer, intent(in)::i,j ! indices
  290. character(len=*),intent(in)::s ! for print
  291. real, intent(in)::time_now ! end of timestep = time_now
  292. real, intent(in):: lfnij, tignij ! level function, ignition time (state)
  293. !*** local
  294. character(len=128):: msg
  295. !*** executable
  296. if(.not. lfnij<0. .and. tignij<time_now)then
  297. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  298. write(msg,*)'i,j=',i,j,' lfn=',lfnij,' tign=',tignij,' time_now=',time_now
  299. call message(msg,level=0)
  300. write(msg,*)' tign-time_now=',tignij-time_now
  301. call message(msg,level=0)
  302. msg=s//': inconsistent state: lfn>=0 and tign<time_now'
  303. call crash(msg)
  304. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  305. endif
  306. end subroutine check_lfn_tign_ij
  307. ! called from the inside of a loop, inline if possible
  308. !DEC$ ATTRIBUTES FORCEINLINE
  309. SUBROUTINE nearest(d,t,ax,ay,sx,sy,st,ex,ey,et,cx2,cy2)
  310. implicit none
  311. !*** arguments
  312. real, intent(out):: d,t
  313. real, intent(in):: ax,ay,sx,sy,st,ex,ey,et,cx2,cy2
  314. ! input:
  315. ! ax, ay coordinates of point a
  316. ! sx,sy,ex,ey coordinates of endpoints of segment [x,y]
  317. ! st,et values at the endpoints x,y
  318. ! cx2,cy2 x and y unit squared for computing distance
  319. ! output
  320. ! d the distance of a and the nearest point z on the segment [x,y]
  321. ! t linear interpolation from the values st,et to the point z
  322. !
  323. ! method: compute d as the distance (ax,ay) from the midpoint (mx,my)
  324. ! minus a correction (because of rounding errors): |a-c|^2 = |a-m|^2 - |m-c|^2
  325. ! when |m-c| >= |s-e|/2 the nearest point is one of the endpoints
  326. ! the computation work also for the case when s=e exactly or approximately
  327. !
  328. !
  329. ! a
  330. ! /| \
  331. ! s---m-c--e
  332. !
  333. ! |m-c| = |a-m| cos (a-m,e-s)
  334. ! = |a-m| (a-m).(e-s))/(|a-m|*|e-s|)
  335. !*** local
  336. real:: mx,my,dam2,dames,am_es,cos2,dmc2,mcrel,mid_t,dif_t,des2,cx,cy
  337. character(len=128):: msg
  338. integer::msglevel=4
  339. !*** executable
  340. 11 format('IGN ',6(a,g17.7,1x))
  341. 12 format('IGN ',4(a,2g13.7,1x))
  342. ! midpoint m = (mx,my)
  343. mx = (sx + ex)*0.5
  344. my = (sy + ey)*0.5
  345. dam2=(ax-mx)*(ax-mx)*cx2+(ay-my)*(ay-my)*cy2 ! |a-m|^2
  346. des2 = (ex-sx)*(ex-sx)*cx2+(ey-sy)*(ey-sy)*cy2 ! des2 = |e-s|^2
  347. dames = dam2*des2
  348. am_es=(ax-mx)*(ex-sx)*cx2+(ay-my)*(ey-sy)*cy2 ! am_es = (a-m).(e-s)
  349. if(dames>0)then
  350. cos2 = (am_es*am_es)/dames ! cos2 = cos^2 (a-m,e-s)
  351. else ! point a already is the midpoint
  352. cos2 = 0.
  353. endif
  354. dmc2 = dam2*cos2 ! dmc2 = |m-c|^2
  355. if(4.*dmc2 < des2)then ! if |m-c|<=|e-s|/2
  356. ! d = sqrt(max(dam2 - dmc2,0.)) ! d=|a-m|^2 - |m-c|^2, guard rounding
  357. mcrel = sign(sqrt(4.*dmc2/des2),am_es) ! relative distance of c from m
  358. elseif(am_es>0)then ! if cos > 0, closest is e
  359. mcrel = 1.0
  360. else ! closest is s
  361. mcrel = -1.0
  362. endif
  363. cx = (ex + sx)*0.5 + mcrel*(ex - sx)*0.5 ! interpolate to c by going from m
  364. cy = (ey + sy)*0.5 + mcrel*(ey - sy)*0.5 ! interpolate to c by going from m
  365. d=sqrt((ax-cx)*(ax-cx)*cx2+(ay-cy)*(ay-cy)*cy2) ! |a-c|^2
  366. t = (et + st)*0.5 + mcrel*(et - st)*0.5 ! interpolate to c by going from m
  367. if(fire_print_msg.ge.msglevel)then
  368. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  369. write(msg,12)'find nearest to [',ax,ay,'] from [',sx,sy,'] [',ex,ey,']' ! DEB
  370. call message(msg,level=msglevel)
  371. write(msg,12)'end times',st,et,' scale squared',cx2,cy2 ! DEB
  372. call message(msg,level=msglevel)
  373. write(msg,11)'nearest at mcrel=',mcrel,'from the midpoint, t=',t ! DEB
  374. call message(msg,level=msglevel)
  375. write(msg,12)'nearest is [',cx,cy,'] d=',d ! DEB
  376. call message(msg,level=msglevel)
  377. write(msg,11)'dam2=',dam2,'des2=',des2,'dames=',dames
  378. call message(msg,level=msglevel)
  379. write(msg,11)'am_es=',am_es,'cos2=',cos2,'dmc2=',dmc2 ! DEB
  380. call message(msg)
  381. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  382. endif
  383. END SUBROUTINE nearest
  384. !
  385. !**********************
  386. !
  387. subroutine fuel_left( &
  388. ifds,ifde,jfds,jfde, &
  389. ims,ime,jms,jme, &
  390. its,ite,jts,jte, &
  391. ifs,ife,jfs,jfe, &
  392. lfn, tign, fuel_time, time_now, fuel_frac, fire_area)
  393. implicit none
  394. !*** purpose: determine fraction of fuel remaining
  395. !*** NOTE: because variables are cell centered, need halo/sync width 1 before
  396. !*** Jan Mandel August 2007 email: jmandel@ucar.edu or Jan.Mandel@gmail.com
  397. !*** arguments
  398. integer, intent(in) ::ifds,ifde,jfds,jfde,its,ite,jts,jte,ims,ime &
  399. ,jms,jme,ifs,ife,jfs,jfe
  400. real, intent(in), dimension(ims:ime,jms:jme)::lfn,tign,fuel_time
  401. real, intent(in):: time_now
  402. real, intent(out), dimension(ifs:ife,jfs:jfe)::fuel_frac
  403. real, intent(out), dimension(ims:ime,jms:jme):: fire_area
  404. ! ims,ime,jms,jme in memory dimensions
  405. ! its,ite,jts,jte in tile dimensions (cells where fuel_frac computed)
  406. ! ifs,ife,jfs,jfe in fuel_frac memory dimensions
  407. ! lfn in level function, at nodes at midpoints of cells
  408. ! tign in ignition time, at nodes at nodes at midpoints of cells
  409. ! fuel_time in time constant of fuel, per cell
  410. ! time_now in time now
  411. ! fuel_frac out fraction of fuel remaining, per cell
  412. ! fire_area out fraction of cell area on fire
  413. !*** local
  414. integer::i,j,ir,jr,icl,jcl,isubcl,jsubcl,i2,j2,ii,jj,its1,jts1,ite1,jte1
  415. real::fmax,frat,helpsum1,helpsum2,fuel_left_ff,fire_area_ff,rx,ry,tignf(2,2)
  416. real,dimension(3,3)::tff,lff
  417. ! help variables instead of arrays fuel_left_f and fire_area_f
  418. real::lffij,lffi1j,lffij1,lffi1j1,tifij,tifi1j,tifij1,tifi1j1,tx,ty,txx,tyy
  419. ! variables for calculation instead of lff(i,j) and tif(i,j)is lffij,tifij etc..#define IFCELLS (ite-its+1)*fuel_left_irl
  420. #define JFCELLS (jte-jts+1)*fuel_left_jrl
  421. character(len=128)::msg
  422. integer::m,omp_get_thread_num
  423. call check_mesh_2dim(its-1,ite+1,jts-1,jte+1,ims,ime,jms,jme)
  424. call check_mesh_2dim(its,ite,jts,jte,ifs,ife,jfs,jfe)
  425. call check_lfn_tign('fuel_left start',time_now,its,ite,jts,jte,ims,ime,jms,jme,lfn, tign)
  426. ! refinement
  427. ir=fuel_left_irl
  428. jr=fuel_left_jrl
  429. if ((ir.ne.2).or.(jr.ne.2)) then
  430. call crash('fuel_left: ir.ne.2 or jr.ne.2 ')
  431. endif
  432. rx=1./ir
  433. ry=1./jr
  434. ! interpolate level set function to finer grid
  435. #ifdef DEBUG_OUT_FUEL_LEFT
  436. call write_array_m(1,IFCELLS+1,1,JFCELLS+1,1,IFCELLS+1,1,JFCELLS+1,lff,'lff',0)
  437. call write_array_m(1,IFCELLS+1,1,JFCELLS+1,1,IFCELLS+1,1,JFCELLS+1,tif,'tif',0)
  438. #endif
  439. !
  440. ! example for ir=2:
  441. !
  442. ! | coarse cell |
  443. ! its-1 its ite ite+1
  444. ! -------X------------|-----.-----X-----.-----|--........----|----------X----------|---------X
  445. ! fine node 1 2 3 2*(ite-its+1)
  446. ! fine cell 1 2 cell 2*(ite-its+1)
  447. ! Loop over cells in Tile
  448. ! Changes made by Volodymyr Kondratenko 09/24/2009
  449. its1=max(its,ifds+1)
  450. ite1=min(ite,ifde-1)
  451. jts1=max(jts,jfds+1)
  452. jte1=min(jte,jfde-1)
  453. ! jm: initialize fuel_frac - we do not compute it near the domain boundary becau!se we cannot interpolate!
  454. do j=jts,jte
  455. do i=its,ite
  456. fuel_frac(i,j)=1.
  457. fire_area(i,j)=0.
  458. enddo
  459. enddo
  460. do icl=its1,ite1
  461. do jcl=jts1,jte1
  462. helpsum1=0
  463. helpsum2=0
  464. call tign_lfn_interpolation(time_now,icl,jcl,ims,ime,jms,jme, &
  465. tign,lfn,tff,lff)
  466. do isubcl=1,ir
  467. do jsubcl=1,jr
  468. if(fuel_left_method.eq.1)then
  469. call fuel_left_cell_1( fuel_left_ff, fire_area_ff, &
  470. lff(isubcl,jsubcl),lff(isubcl,jsubcl+1),lff(isubcl+1,jsubcl),lff(isubcl+1,jsubcl+1), &
  471. tff(isubcl,jsubcl),tff(isubcl,jsubcl+1),tff(isubcl+1,jsubcl),tff(isubcl+1,jsubcl+1), &
  472. time_now, fuel_time(icl,jcl))
  473. elseif(fuel_left_method.eq.2)then
  474. call fuel_left_cell_2( fuel_left_ff, fire_area_ff, &
  475. lff(isubcl,jsubcl),lff(isubcl,jsubcl+1),lff(isubcl+1,jsubcl),lff(isubcl+1,jsubcl+1), &
  476. tff(isubcl,jsubcl),tff(isubcl,jsubcl+1),tff(isubcl+1,jsubcl),tff(isubcl+1,jsubcl+1), &
  477. time_now, fuel_time(icl,jcl))
  478. ! dont forget to change fire_area_ff here
  479. else
  480. call crash('fuel_left: unknown fuel_left_method')
  481. endif
  482. ! consistency check
  483. if(fire_area_ff.lt.-1e-6 .or. &
  484. (fire_area_ff.eq.0. .and. fuel_left_ff.lt.1.-1e-6))then
  485. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  486. write(msg,'(a,2i6,2(a,f11.8))')'fuel_left: at node',i,j, &
  487. ' of refined mesh fuel burnt',1-fuel_left_ff,' fire area',fire_area_ff
  488. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  489. call crash(msg)
  490. endif
  491. helpsum1=helpsum1+fuel_left_ff
  492. helpsum2=helpsum2+fire_area_ff
  493. ! write(*,*)icl,jcl,isubcl,jsubcl,fuel_left_ff,fire_area_ff
  494. enddo
  495. enddo
  496. ! write(*,*)icl,jcl,helpsum1,helpsum2
  497. fuel_frac(icl,jcl)=helpsum1 / (ir*jr) ! multiply by weight for averaging over coarse cell
  498. fire_area(icl,jcl)=helpsum2 / (ir*jr) ! multiply by weight for averaging over coarse cell
  499. enddo
  500. enddo
  501. #ifdef DEBUG_OUT_FUEL_LEFT
  502. call write_array_m(its,ite,jts,jte,ims,ime,jms,jme,fire_area,'fire_area',0)
  503. call write_array_m(its,ite,jts,jte,ims,ime,jms,jme,fuel_frac,'fuel_frac',0)
  504. call write_array_m(1,IFCELLS,1,JFCELLS,1,IFCELLS,1,JFCELLS,fuel_left_f,'fuel_left_f',0)
  505. call write_array_m(1,IFCELLS,1,JFCELLS,1,IFCELLS,1,JFCELLS,fire_area_f,'fire_area_f',0)
  506. #endif
  507. ! consistency check after sum
  508. !fmax=0
  509. !do j=jts,jte
  510. ! do i=its,ite
  511. ! if(fire_area(i,j).eq.0.)then
  512. ! if(fuel_frac(i,j).lt.1.-1e-6)then
  513. !!$OMP CRITICAL(SFIRE_CORE_CRIT)
  514. ! write(msg,'(a,2i6,2(a,f11.8))')'fuel_left: at node',i,j, &
  515. ! ' fuel burnt',1-fuel_frac(i,j),' but fire area',fire_area(i,j)
  516. !!$OMP END CRITICAL(SFIRE_CORE_CRIT)
  517. ! call crash(msg)
  518. ! endif
  519. ! else
  520. ! frat=(1-fuel_frac(i,j))/fire_area(i,j)
  521. ! fmax=max(fmax,frat)
  522. ! endif
  523. ! enddo
  524. !enddo
  525. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  526. write(msg,'(a,4i6,a,e15.7)')'fuel_left: tile',its,ite,jts,jte,' max fuel burnt/area',fmax
  527. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  528. call message(msg)
  529. return
  530. end subroutine fuel_left
  531. !
  532. !************************
  533. !
  534. !
  535. !*************************
  536. !Subroutine that is calculating tign and lfn of four endpoints of the subcell
  537. ! that is located at isubcl,jsubcl of the cell -(icl,jcl)
  538. !
  539. subroutine tign_lfn_interpolation(time_now,icl,jcl,ims,ime,jms,jme, &
  540. tign,lfn,tff,lff)
  541. real, intent(in):: time_now ! not ignited nodes will have tign set to >= time_now
  542. integer, intent(in) :: icl,jcl
  543. integer, intent(in) :: ims,ime,jms,jme ! memory dimensions of all arrays
  544. real, intent(in), dimension(ims:ime,jms:jme)::lfn,tign
  545. real, intent(out),dimension(3,3)::tff,lff
  546. ! | | |
  547. ! -(3,1)-------------(3,2)-------------(3,3)
  548. ! | | |
  549. ! | (2,1) | (2,2) |
  550. ! | | |
  551. ! | | |
  552. ! | | |
  553. ! | | |
  554. ! (2,1)--------node-(icl,jcl)---------(2,3)-----------(icl,jcl+1)-------------|
  555. ! | sub-node (2,2) |
  556. ! | | |
  557. ! | (1,1) | (1,2) | each fire mesh cell is decomposed in 4
  558. ! | | | tff,lff is computed at the nodes of
  559. ! | | | the subcells, numbered (1,1)...(3,3)
  560. ! (1,1)-------------(1,2)-------------(1,3)--
  561. ! | | |
  562. !
  563. !**********************
  564. ! Direct calculation tif and lff, avoiding big auxiliary arrays, just for case ir=jr=2
  565. ! Checking whether icl or jcl is on the boundary
  566. call tign_lfn_four_pnts_interp(tign(icl-1,jcl-1),tign(icl-1,jcl),tign(icl,jcl-1), &
  567. tign(icl,jcl),lfn(icl-1,jcl-1),lfn(icl-1,jcl), &
  568. lfn(icl,jcl-1),lfn(icl,jcl),lff(1,1),tff(1,1),time_now)
  569. call tign_lfn_line_interp(tign(icl-1,jcl),tign(icl,jcl),lfn(icl-1,jcl),lfn(icl,jcl), &
  570. lff(1,2),tff(1,2),time_now)
  571. call tign_lfn_four_pnts_interp(tign(icl-1,jcl),tign(icl-1,jcl+1),tign(icl,jcl), &
  572. tign(icl,jcl+1),lfn(icl-1,jcl),lfn(icl-1,jcl+1), &
  573. lfn(icl,jcl),lfn(icl,jcl+1),lff(1,3),tff(1,3),time_now)
  574. call tign_lfn_line_interp(tign(icl,jcl-1),tign(icl,jcl),lfn(icl,jcl-1),lfn(icl,jcl), &
  575. lff(2,1),tff(2,1),time_now)
  576. lff(2,2)=lfn(icl,jcl)
  577. tff(2,2)=tign(icl,jcl)
  578. call tign_lfn_line_interp(tign(icl,jcl+1),tign(icl,jcl),lfn(icl,jcl+1),lfn(icl,jcl), &
  579. lff(2,3),tff(2,3),time_now)
  580. call tign_lfn_four_pnts_interp(tign(icl,jcl-1),tign(icl,jcl),tign(icl+1,jcl-1), &
  581. tign(icl+1,jcl),lfn(icl,jcl-1),lfn(icl,jcl), &
  582. lfn(icl+1,jcl-1),lfn(icl+1,jcl),lff(3,1),tff(3,1),time_now)
  583. call tign_lfn_line_interp(tign(icl+1,jcl),tign(icl,jcl),lfn(icl+1,jcl),lfn(icl,jcl), &
  584. lff(3,2),tff(3,2),time_now)
  585. call tign_lfn_four_pnts_interp(tign(icl,jcl),tign(icl,jcl+1),tign(icl+1,jcl), &
  586. tign(icl+1,jcl+1),lfn(icl,jcl),lfn(icl,jcl+1), &
  587. lfn(icl+1,jcl),lfn(icl+1,jcl+1),lff(3,3),tff(3,3),time_now)
  588. end subroutine tign_lfn_interpolation
  589. !
  590. !************************
  591. !
  592. subroutine tign_lfn_line_interp(tign1,tign2,lfn1,lfn2,lfn_subcl,tign_subcl,time_now)
  593. !***purpose: computes time of ignition of the point(*_subcl) that lies on the line
  594. ! between two points, whose lfn and tign is known
  595. !*** arguments
  596. !
  597. real,intent(in) :: tign1,tign2 ! ignition times at the two endpoints
  598. real,intent(in) :: lfn1,lfn2 ! level set function at the two endpoints
  599. real,intent(in) :: time_now ! tign>=time_now => no fire
  600. real,intent(out) :: lfn_subcl,tign_subcl ! interpolated to midpoint
  601. ! Case 1: both points are on fire -> tign is interpolated linearly
  602. ! Case 2: both points are not on fire -> tign_subcl=time_now, not burning
  603. ! Case 3: One point is not fire, another is not -> tign_subcl set from
  604. ! the equation tign - time_now = c*lfn, where the proportionality
  605. ! constant is found from the values of tign and lfn at the burning endpoint.
  606. ! In particular, lfn(x)=0 implies tign(x)=time_now, so the representations of
  607. ! the fireline by lfn and tign are consistent.
  608. ! This is a special case of Case 3 from subroutine tign_lfn_four_pnts_interp.
  609. !*** local
  610. real :: c
  611. !*** executable
  612. ! check consistency of inputs
  613. call check_lfn_tign_ij(0,1,'tign_lfn_line_interp',time_now,lfn1,tign1)
  614. call check_lfn_tign_ij(0,2,'tign_lfn_line_interp',time_now,lfn2,tign2)
  615. lfn_subcl=0.5*(lfn1+lfn2) ! interpolate lfn linearly
  616. if (.not. lfn_subcl < 0.) then ! never test floats on equality
  617. tign_subcl=time_now ! midpoint not on fire => none on fire
  618. elseif ((lfn1 < 0.).AND.(lfn2 < 0.)) then
  619. tign_subcl=0.5*(tign1+tign2) ! both on fire, interpolate linearly
  620. else ! one is on fire one is not
  621. if (lfn1 < 0) then ! 1 is on fire, 2 is not
  622. c = (tign1-time_now)/lfn1
  623. elseif (lfn2 < 0) then
  624. c = (tign2-time_now)/lfn2
  625. else
  626. call crash('tign_lfn_line_interp: one of lfn1 or lfn2 should be < 0')
  627. endif
  628. if( c < 0.)call crash('tign_lfn_line_interp: bad ignition times, c<0')
  629. tign_subcl=c*lfn_subcl+time_now;
  630. endif
  631. end subroutine tign_lfn_line_interp
  632. !
  633. !************************
  634. !
  635. subroutine tign_lfn_four_pnts_interp(tign1,tign2,tign3,tign4, &
  636. lfn1,lfn2,lfn3,lfn4,lfn_subcl,tign_subcl,time_now)
  637. !***purpose: computes time of ignition of the point(*_subcl) that lies on the middle
  638. ! of square with given 4 points on its ends, whose lfn and tign is known
  639. ! since lfn is interpolated linearly, lfn_subcl is known
  640. !***arguments
  641. real,intent(in) :: tign1,tign2,tign3,tign4 ! time of ignition of all corner points
  642. real,intent(in) :: lfn1,lfn2,lfn3,lfn4 ! lfn of central and all corner points
  643. real,intent(in) :: time_now
  644. real,intent(out) :: lfn_subcl,tign_subcl
  645. ! Case 1: all 4 points are on fire -> tign is interpolated linearly
  646. ! tign_subcl=0.25*(tign1+tign2+tign3+tign4)
  647. ! Case 2: all points are not on fire -> subcl - not burning
  648. ! Case 3: some points are on fire, others are not
  649. ! Here we assume that when lfn(x)=0 -> tign(x)=time_now (values interpolated to point x)
  650. ! For this case, tign of central point is approximated by
  651. ! tign~=c*lfn+time_now, which for our case is
  652. ! tign_subcl~=c*lfn_subcl+time_now
  653. ! where c is computed by least squares from the values at the points that are on fire
  654. !
  655. ! sum(c*lfn(Ai)+time_now-tign(Ai))^2 ---> min
  656. ! for all lfn(Ai)<0
  657. !
  658. ! solution for 'c' would be
  659. !
  660. ! sum(lfn(Ai)*lfn(Ai)
  661. ! c= ------------------------------, both sums are over Ai, s.t lfn(Ai)<0
  662. ! sum(tign(Ai)-time_now)*lfn(Ai)
  663. !
  664. real :: a,b,c,err
  665. err=0.0001
  666. call check_lfn_tign_ij(0,1,'tign_lfn_four_pnts_interp',time_now,lfn1,tign1)
  667. call check_lfn_tign_ij(0,2,'tign_lfn_four_pnts_interp',time_now,lfn2,tign2)
  668. call check_lfn_tign_ij(0,3,'tign_lfn_four_pnts_interp',time_now,lfn3,tign3)
  669. call check_lfn_tign_ij(0,4,'tign_lfn_four_pnts_interp',time_now,lfn4,tign4)
  670. lfn_subcl=0.25*(lfn1+lfn2+lfn3+lfn4)
  671. if(.not. lfn_subcl < 0.) then ! midpoint not on fire, most frequent
  672. ! Case 2
  673. tign_subcl=time_now
  674. elseif((lfn1 < 0.).AND.(lfn2 < 0.).AND.(lfn3 < 0.).AND.(lfn4 < 0.)) then
  675. ! Case 1
  676. tign_subcl=0.25*(tign1+tign2+tign3+tign4) ! all on fire, interpolate
  677. else ! some on fire
  678. ! Case 3
  679. ! tign_subcl~=c*lfn_subcl+time_now
  680. a=0;
  681. b=0;
  682. if (lfn1 < 0.) then
  683. a=a+lfn1*lfn1
  684. b=b+(tign1-time_now)*lfn1
  685. endif
  686. if (lfn2 < 0.) then
  687. a=a+lfn2*lfn2
  688. b=b+(tign2-time_now)*lfn2
  689. endif
  690. if (lfn3 < 0.) then
  691. a=a+lfn3*lfn3
  692. b=b+(tign3-time_now)*lfn3
  693. endif
  694. if (lfn4 < 0.) then
  695. a=a+lfn4*lfn4
  696. b=b+(tign4-time_now)*lfn4
  697. endif
  698. if (.not. a>0.) call crash('tign_lfn_four_pnts_interp: none of lfn < 0')
  699. if( b < 0.)call crash('tign_lfn_four_pnts_interp: bad ignition times, b<0') ! can have 0 because of rounding
  700. c=b/a;
  701. tign_subcl=c*lfn_subcl+time_now;
  702. endif
  703. end subroutine tign_lfn_four_pnts_interp
  704. !
  705. !************************
  706. !
  707. subroutine fuel_left_cell_1( fuel_frac_left, fire_frac_area, &
  708. lfn00,lfn01,lfn10,lfn11, &
  709. tign00,tign01,tign10,tign11,&
  710. time_now, fuel_time_cell)
  711. !*** purpose: compute the fuel fraction left in one cell
  712. implicit none
  713. !*** arguments
  714. real, intent(out):: fuel_frac_left, fire_frac_area !
  715. real, intent(in)::lfn00,lfn01,lfn10,lfn11 ! level set function at 4 corners of the cell
  716. real, intent(in)::tign00,tign01,tign10,tign11! ignition time at the 4 corners of the cell
  717. real, intent(in)::time_now ! the time now
  718. real, intent(in)::fuel_time_cell ! time to burns off to 1/e
  719. !*** Description
  720. ! The area burning is given by the condition L <= 0, where the function P is
  721. ! interpolated from lfn(i,j)
  722. !
  723. ! The time since ignition is the function T, interpolated in from tign(i,j)-time_now.
  724. ! The values of tign(i,j) where lfn(i,j)>=0 are ignored, tign(i,j)=0 is taken
  725. ! when lfn(i,j)=0.
  726. !
  727. ! The function computes an approxmation of the integral
  728. !
  729. !
  730. ! /\
  731. ! |
  732. ! fuel_frac_left = 1 - | 1 - exp(-T(x,y)/fuel_time_cell)) dxdy
  733. ! |
  734. ! \/
  735. ! 0<x<1
  736. ! 0<y<1
  737. ! L(x,y)<=0
  738. !
  739. ! When the cell is not burning at all (all lfn>=0), then fuel_frac(i,j)=1.
  740. ! Because of symmetries, the result should not depend on the mesh spacing dx dy
  741. ! so dx=1 and dy=1 assumed.
  742. !
  743. ! Example:
  744. !
  745. ! lfn<0 lfn>0
  746. ! (0,1) -----O--(1,1) O = points on the fireline, T=tnow
  747. ! | \ | A = the burning area for computing
  748. ! | \| fuel_frac(i,j)
  749. ! | A O
  750. ! | |
  751. ! | |
  752. ! (0,0)---------(1,0)
  753. ! lfn<0 lfn<0
  754. !
  755. ! Approximations allowed:
  756. ! The fireline can be approximated by straight line(s).
  757. ! When all cell is burning, approximation by 1 point Gaussian quadrature is OK.
  758. !
  759. ! Requirements:
  760. ! 1. The output should be a continuous function of the arrays lfn and
  761. ! tign whenever lfn(i,j)=0 implies tign(i,j)=tnow.
  762. ! 2. The output should be invariant to the symmetries of the input in each cell.
  763. ! 3. Arbitrary combinations of the signs of lfn(i,j) should work.
  764. ! 4. The result should be at least 1st order accurate in the sense that it is
  765. ! exact if the time from ignition is a linear function.
  766. !
  767. ! If time from ignition is approximated by polynomial in the burnt
  768. ! region of the cell, this is integral of polynomial times exponential
  769. ! over a polygon, which can be computed exactly.
  770. !
  771. ! Requirement 4 is particularly important when there is a significant decrease
  772. ! of the fuel fraction behind the fireline on the mesh scale, because the
  773. ! rate of fuel decrease right behind the fireline is much larger
  774. ! (exponential...). This will happen when
  775. !
  776. ! change of time from ignition within one mesh cell / fuel_time_cell is not << 1
  777. !
  778. ! This is the same as
  779. !
  780. ! mesh cell size
  781. ! X = ------------------------- is not << 1
  782. ! fireline speed * fuel_time_cell
  783. !
  784. !
  785. ! When X is large then the fuel burnt in one timestep in the cell is
  786. ! approximately proportional to length of fireline in that cell.
  787. !
  788. ! When X is small then the fuel burnt in one timestep in the cell is
  789. ! approximately proportional to the area of the burning region.
  790. !
  791. !*** calls
  792. intrinsic tiny
  793. !*** local
  794. real::ps,aps,area,ta,out
  795. real::t00,t01,t10,t11
  796. real,parameter::safe=tiny(aps)
  797. character(len=128)::msg
  798. ! the following algorithm is a very crude approximation
  799. ! minus time since ignition, 0 if no ignition yet
  800. ! it is possible to have 0 in fire region when ignitin time falls in
  801. ! inside the time step because lfn is updated at the beginning of the time step
  802. t00=tign00-time_now
  803. if(lfn00>0. .or. t00>0.)t00=0.
  804. t01=tign01-time_now
  805. if(lfn01>0. .or. t01>0.)t01=0.
  806. t10=tign10-time_now
  807. if(lfn10>0. .or. t10>0.)t10=0.
  808. t11=tign11-time_now
  809. if(lfn11>0. .or. t11>0.)t11=0.
  810. ! approximate burning area, between 0 and 1
  811. ps = lfn00+lfn01+lfn10+lfn11
  812. aps = abs(lfn00)+abs(lfn01)+abs(lfn10)+abs(lfn11)
  813. aps=max(aps,safe)
  814. area =(-ps/aps+1.)/2.
  815. area = max(area,0.) ! make sure area is between 0 and 1
  816. area = min(area,1.)
  817. ! average negative time since ignition
  818. ta=0.25*(t00+t01+t10+t11)
  819. ! exp decay in the burning area
  820. out=1.
  821. !if(area>0.)out=1. - area*(1. - exp(ta/fuel_time_cell))
  822. if(area>0)out=area*exp(ta/fuel_time_cell) + (1. - area)
  823. if(out>1.)then
  824. !$OMP CRITICAL(SFIRE_CORE_CRIT)
  825. write(msg,*)'out=',out,'>1 area=',area,' ta=',ta
  826. call message(msg)
  827. write(msg,*)'tign=',tign00,tign01,tign10,tign11,' time_now=',time_now
  828. !$OMP END CRITICAL(SFIRE_CORE_CRIT)
  829. call message(msg)
  830. !call message('WARNING: fuel_left_cell_1: fuel fraction > 1')
  831. call crash('fuel_left_cell_1: fuel fraction > 1')
  832. endif
  833. !out = max(out,0.) ! make sure out is between 0 and 1
  834. !out = min(out,1.)
  835. fuel_frac_left = out
  836. fire_frac_area = area
  837. end subroutine fuel_left_cell_1
  838. !
  839. !****************************************
  840. !
  841. ! function calculation fuel_frac made by Volodymyr Kondratenko on the base of
  842. ! the Matlab code made by Jan Mandel and the fortran port by Minjeong Kim
  843. subroutine fuel_left_cell_2( fuel_frac_left, fire_frac_area, &
  844. lfn00,lfn01,lfn10,lfn11, &
  845. tign00,tign01,tign10,tign11,&
  846. time_now, fuel_time_cell)
  847. !*** purpose: compute the fuel fraction left in one cell
  848. implicit none
  849. !*** arguments
  850. real, intent(out):: fuel_frac_left, fire_frac_area !
  851. real, intent(in)::lfn00,lfn01,lfn10,lfn11 ! level set function at 4 corners of the cell
  852. real, intent(in)::tign00,tign01,tign10,tign11! ignition time at the 4 corners of the cell
  853. real, intent(in)::time_now ! the time now
  854. real, intent(in)::fuel_time_cell ! burns time to 1/e
  855. !*** Description
  856. ! The burning area is given by the condition L <= 0, where the function L is
  857. ! interpolated from lfn(i,j)
  858. !
  859. ! The time since ignition is the function T, interpolated in from tign(i,j)-time_now.
  860. ! The values of tign(i,j) where lfn(i,j)>=0 are ignored, tign(i,j)=0 is taken
  861. ! when lfn(i,j)=0.
  862. !
  863. ! The function computes an approxmation of the integral
  864. !
  865. !
  866. ! /\
  867. ! |
  868. ! fuel_frac_left = 1 - | 1 - exp(-T(x,y)/fuel_time_cell)) dxdy
  869. ! |
  870. ! \/
  871. ! 0<x<1
  872. ! 0<y<1
  873. ! L(x,y)<=0
  874. !
  875. ! When the cell is not burning at all (all lfn>=0), then fuel_frac(i,j)=1.
  876. ! Because of symmetries, the result should not depend on the mesh spacing dx dy
  877. ! so dx=1 and dy=1 assumed.
  878. !
  879. ! Example:
  880. !
  881. ! lfn<0 lfn>0
  882. ! (0,1) -----O--(1,1) O = points on the fireline, T=tnow
  883. ! | \ | A = the burning area for computing
  884. ! | \| fuel_frac(i,j)
  885. ! | A O
  886. ! | |
  887. ! | |
  888. ! (0,0)---------(1,0)
  889. ! lfn<0 lfn<0
  890. !
  891. ! Approximations allowed:
  892. ! The fireline can be approximated by straight line(s).
  893. ! When all cell is burning, approximation by 1 point Gaussian quadrature is OK.
  894. !
  895. ! Requirements:
  896. ! 1. The output should be a continuous function of the arrays lfn and
  897. ! tign whenever lfn(i,j)=0 implies tign(i,j)=tnow.
  898. ! 2. The output should be invariant to the symmetries of the input in each cell.
  899. ! 3. Arbitrary combinations of the signs of lfn(i,j) should work.
  900. ! 4. The result should be at least 1st order accurate in the sense that it is
  901. ! exact if the time from ignition is a linear function.
  902. !
  903. ! If time from ignition is approximated by polynomial in the burnt
  904. ! region of the cell, this is integral of polynomial times exponential
  905. ! over a polygon, which can be computed exactly.
  906. !
  907. ! Requirement 4 is particularly important when there is a significant decrease
  908. ! of the fuel fraction behind the fireline on the mesh scale, because the
  909. ! rate of fuel decrease right behind the fireline is much larger
  910. ! (exponential...). This will happen when
  911. !
  912. ! change of time from ignition within one mesh cell * fuel speed is not << 1
  913. !
  914. ! This is the same as
  915. !
  916. ! mesh cell size*fuel_speed
  917. ! ------------------------- is not << 1
  918. ! fireline speed
  919. !
  920. !
  921. ! When X is large then the fuel burnt in one timestep in the cell is
  922. ! approximately proportional to length of fireline in that cell.
  923. !
  924. ! When X is small then the fuel burnt in one timestep in the cell is
  925. ! approximately proportional to the area of the burning region.
  926. !*** calls
  927. intrinsic tiny
  928. #define DREAL real(kind=8)
  929. !*** local
  930. DREAL::ps,aps,area,ta,out
  931. DREAL::t00,t01,t10,t11
  932. DREAL,parameter::safe=tiny(aps)
  933. DREAL::dx,dy ! mesh sizes
  934. integer::i,j,k
  935. DREAL,dimension(3)::u
  936. DREAL::tweight,tdist
  937. integer::kk,ll,ss
  938. DREAL::rnorm
  939. DREAL,dimension(8,2)::xylist,xytlist
  940. DREAL,dimension(8)::tlist,llist,xt
  941. DREAL,dimension(5)::xx,yy
  942. DREAL,dimension(5)::lfn,tign
  943. integer:: npoint
  944. DREAL::tt,x0,y0,xts,xte,yts,yte,xt1,xt2
  945. DREAL::lfn0,lfn1,dist,nr,s,errQ,ae,ce,ceae,a0,a1,a2,d,cet
  946. DREAL::s1,s2,s3
  947. DREAL::upper,lower,ah,ch,aa,cc,aupp,cupp,alow,clow
  948. DREAL,dimension(2,2)::mQ
  949. DREAL,dimension(2)::ut
  950. character(len=128)::msg
  951. !calls
  952. intrinsic epsilon
  953. DREAL, parameter:: zero=0.,one=1.,eps=epsilon(zero)
  954. !!!! For finite differences by VK
  955. DREAL::tign_middle,dt_dx,dt_dy,lfn_middle,a,b,c
  956. DREAL:: alg_err
  957. !*** executable
  958. call crash('fuel_left_method=2 not working, please use fuel_left_method=1')
  959. ! check consistency
  960. call check_lfn_tign_ij(0,0,'fuel_left_cell_2',time_now,lfn00,tign00)
  961. call check_lfn_tign_ij(0,1,'fuel_left_cell_2',time_now,lfn01,tign01)
  962. call check_lfn_tign_ij(1,0,'fuel_left_cell_2',time_now,lfn10,tign10)
  963. call check_lfn_tign_ij(1,1,'fuel_left_cell_2',time_now,lfn11,tign11)
  964. alg_err=0
  965. dx=1
  966. dy=1
  967. t00=time_now-tign00
  968. if(lfn00>=0. .or. t00<0.)t00=0.
  969. t01=time_now-tign01
  970. if(lfn01>=0. .or. t01<0.)t01=0.
  971. t10=time_now-tign10
  972. if(lfn10>=0. .or. t10<0.)t10=0.
  973. t11=time_now-tign11
  974. if(lfn11>=0. .or. t11<0.)t11=0.
  975. ! approximate burning area, between 0 and 1
  976. ! was taken from fuel_left_cell_1 made by Jan, will need to be changed
  977. ps = lfn00+lfn01+lfn10+lfn11
  978. aps = abs(lfn00)+abs(lfn01)+abs(lfn10)+abs(lfn11)
  979. aps=max(aps,safe)
  980. area =(-ps/aps+1.)/2.
  981. area = max(area,zero) ! make sure area is between 0 and 1
  982. area = min(area,one)
  983. !*** case0 Do nothing
  984. if ( lfn00>=0 .and. lfn10>=0 .and. lfn01>=0 .and. lfn11>=0 ) then
  985. out = 1.0 ! fuel_left, no burning
  986. area= 0.
  987. !*** case4 all four coners are burning
  988. else if (lfn00<=0 .and. lfn10<=0 .and. lfn01<=0 .and. lfn11<=0) then
  989. ! All burning
  990. ! T=u(1)*x+u(2)*y+u(3)
  991. ! t(0,0)=tign(1,1)
  992. ! t(0,fd(2))=tign(1,2)
  993. ! t(fd(1),0)=tign(2,1)
  994. ! t(fd(1),fd(2))=tign(2,2)
  995. ! t(g/2,h/2)=sum(tign(i,i))/4
  996. ! dt/dx=(1/2h)*(t10-t00+t11-t01)
  997. ! dt/dy=(1/2h)*(t01-t00+t11-t10)
  998. ! approximate T(x,y)=u(1)*x+u(2)*y+u(3) Using finite differences
  999. ! t(x,y)=t(h/2,h/2)+(x-h/2)*dt/dx+(y-h/2)*dt/dy
  1000. ! u(1)=dt/dx
  1001. ! u(2)=dt/dy
  1002. ! u(3)=t(h/2,h/2)-h/2(dt/dx+dt/dy)
  1003. tign_middle=(t00+t01+t10+t11)/4
  1004. ! since mesh_size is 1 we replace fd(1) and fd(2) by 1
  1005. dt_dx=(t10-t00+t11-t01)/2
  1006. dt_dy=(t01-t00+t11-t10)/2
  1007. u(1)=dt_dx
  1008. u(2)=dt_dy
  1009. u(3)=tign_middle-(dt_dx+dt_dy)/2
  1010. ! integrate
  1011. u(1)=-u(1)/fuel_time_cell
  1012. u(2)=-u(2)/fuel_time_cell
  1013. u(3)=-u(3)/fuel_time_cell
  1014. s1=u(1)
  1015. s2=u(2)
  1016. out=exp(u(3))*intexp(s1)*intexp(s2)
  1017. area=1
  1018. if ( out<0 .or. out>1.0 ) then
  1019. call message('WARNING: fuel_left_cell: case all burning: out should be between 0 and 1')
  1020. end if
  1021. !*** case 1,2,3- other cases
  1022. !*** part of cell is burning
  1023. else
  1024. ! set xx, yy for the coner points
  1025. ! move these values out of i and j loop to speed up
  1026. ! comments for xx, yy - make center [0,0], cyclic, counterclockwise
  1027. ! comments for lfn,tign - cyclic, counterclockwise
  1028. xx(1) = -0.5
  1029. xx(2) = 0.5
  1030. xx(3) = 0.5
  1031. xx(4) = -0.5
  1032. xx(5) = -0.5
  1033. yy(1) = -0.5
  1034. yy(2) = -0.5
  1035. yy(3) = 0.5
  1036. yy(4) = 0.5
  1037. yy(5) = -0.5
  1038. lfn(1)=lfn00
  1039. lfn(2)=lfn10
  1040. lfn(3)=lfn11
  1041. lfn(4)=lfn01
  1042. lfn(5)=lfn00
  1043. tign(1)=t00
  1044. tign(2)=t10
  1045. tign(3)=t11
  1046. tign(4)=t01
  1047. tign(5)=t00
  1048. npoint = 0 ! number of points in polygon
  1049. do k=1,4
  1050. lfn0=lfn(k )
  1051. lfn1=lfn(k+1)
  1052. if ( lfn0 <= 0.0 ) then
  1053. npoint = npoint + 1
  1054. xylist(npoint,1)=xx(k) ! add corner to list
  1055. xylist(npoint,2)=yy(k)
  1056. tlist(npoint)=-tign(k) ! time since ignition
  1057. llist(npoint)=lfn0
  1058. end if
  1059. if ( lfn0*lfn1 < 0 ) then
  1060. npoint = npoint + 1
  1061. ! coordinates of intersection of the fire line with segment k k+1
  1062. ! lfn(t)=lfn0+t*(lfn1-lfn0)=0
  1063. tt=lfn0/(lfn0-lfn1)
  1064. x0=xx(k)+( xx(k+1)-xx(k) )*tt
  1065. y0=yy(k)+( yy(k+1)-yy(k) )*tt
  1066. xylist(npoint,1)=x0
  1067. xylist(npoint,2)=y0
  1068. tlist(npoint)=0 ! now at ignition
  1069. llist(npoint)=0 ! on fireline
  1070. end if
  1071. end do
  1072. ! make the list circular and trim to size
  1073. tlist(npoint+1)=tlist(1)
  1074. llist(npoint+1)=llist(1)
  1075. xylist(npoint+1,1)=xylist(1,1)
  1076. xylist(npoint+1,2)=xylist(1,2)
  1077. ! approximate L(x,y)=u(1)*x+u(2)*y+u(3)
  1078. lfn_middle=(lfn00+lfn01+lfn10+lfn11)/4
  1079. dt_dx=(lfn10-lfn00+lfn11-lfn01)/2
  1080. dt_dy=(lfn01-lfn00+lfn11-lfn10)/2
  1081. u(1)=dt_dx
  1082. u(2)=dt_dy
  1083. u(3)=lfn_middle-(dt_dx+dt_dy)/2
  1084. ! finding the coefficient c, reminder we work over one subcell only
  1085. ! T(x,y)=c*L(x,y)+time_now
  1086. a=0
  1087. b=0
  1088. if (lfn00 <= 0) then
  1089. a=a+lfn00*lfn00
  1090. if (t00 < 0) then
  1091. call crash('fuel_burnt_fd: tign(i1) should be less then time_now')
  1092. else
  1093. b=b+t00*lfn00
  1094. end if
  1095. end if
  1096. if (lfn01 <= 0) then
  1097. a=a+lfn01*lfn01
  1098. if (t01< 0) then
  1099. call crash('fuel_burnt_fd: tign(i1) should be less then time_now')
  1100. else
  1101. b=b+t01*lfn01
  1102. end if
  1103. end if
  1104. if (lfn10<=0) then
  1105. a=a+lfn10*lfn10
  1106. if (t10<0) then
  1107. call crash('fuel_burnt_fd: tign(i1) should be less then time_now')
  1108. else
  1109. b=b+t10*lfn10
  1110. end if
  1111. end if
  1112. if (lfn11<=0) then
  1113. a=a+lfn11*lfn11
  1114. if (t11<0) then
  1115. call crash('fuel_burnt_fd: tign(i1) should be less then time_now')
  1116. else
  1117. b=b+t11*lfn11
  1118. end if
  1119. end if
  1120. if (a==0) then
  1121. call crash('fuel_burnt_fd: if c is on fire then one of cells should be on fire')
  1122. end if
  1123. c=b/a
  1124. u(1)=u(1)*c
  1125. u(2)=u(2)*c
  1126. u(3)=u(3)*c
  1127. ! rotate to gradient on x only
  1128. nr = sqrt(u(1)**2+u(2)**2)
  1129. if(.not.nr.gt.eps)then
  1130. out=1.
  1131. goto 900
  1132. endif
  1133. c = u(1)/nr
  1134. s = u(2)/nr
  1135. ! rotation such that Q*u(1:2)-[something;0]
  1136. mQ(1,1)=c
  1137. mQ(1,2)=s
  1138. mQ(2,1)=-s
  1139. mQ(2,2)=c
  1140. ! mat vec multiplication
  1141. call matvec(mQ,2,2,u,3,ut,2,2,2)
  1142. errQ = ut(2) ! should be zero
  1143. ae = -ut(1)/fuel_time_cell
  1144. ce = -u(3)/fuel_time_cell ! -T(xt,yt)/fuel_time=ae*xt+ce
  1145. cet=ce!keep ce for later
  1146. call matmatp(xylist,8,2,mQ,2,2,xytlist,8,2,npoint+1,2,2)
  1147. call sortxt( xytlist, 8,2, xt,8,npoint ) !sort ascending in x
  1148. out=0.0
  1149. aupp=0.0
  1150. cupp=0.0
  1151. alow=0.0
  1152. clow=0.0
  1153. do k=1,npoint-1
  1154. xt1=xt(k)
  1155. xt2=xt(k+1)
  1156. upper=0
  1157. lower=0
  1158. ah=0
  1159. ch=0
  1160. if ( xt2-xt1 > eps*100 ) then
  1161. ! slice of nonzero width
  1162. ! find slice height as h=ah*x+ch
  1163. do ss=1,npoint ! pass counterclockwise
  1164. xts=xytlist(ss,1) ! start point of the line
  1165. yts=xytlist(ss,2)
  1166. xte=xytlist(ss+1,1) ! end point of the line
  1167. yte=xytlist(ss+1,2)
  1168. if ( (xts>xt1 .and. xte>xt1) .or. &
  1169. (xts<xt2 .and. xte<xt2) ) then
  1170. aa = 0 ! do nothing
  1171. cc = 0
  1172. else ! line y=a*x+c through (xts,yts), (xte,yte)
  1173. aa = (yts-yte)/(xts-xte)
  1174. cc = (xts*yte-xte*yts)/(xts-xte)
  1175. if (xte<xts) then ! upper boundary
  1176. aupp = aa
  1177. cupp = cc
  1178. ah=ah+aa
  1179. ch=ch+cc
  1180. upper=upper+1
  1181. else ! lower boundary
  1182. alow = aa
  1183. clow = cc
  1184. lower=lower+1
  1185. end if
  1186. end if!(xts>xt1 .and. xte>xt1)
  1187. end do ! ss
  1188. ce=cet !use stored ce
  1189. !shift small amounts exp(-**) to avoid negative fuel burnt
  1190. if (ae*xt1+ce > 0 ) then
  1191. ce=ce-(ae*xt1+ce)!
  1192. end if
  1193. if (ae*xt2+ce > 0) then
  1194. ce=ce-(ae*xt2+ce)
  1195. end if
  1196. ah = aupp-alow
  1197. ch = cupp-clow
  1198. ! integrate (ah*x+ch)*(1-exp(ae*x+ce) from xt1 to xt2
  1199. ! numerically sound for ae->0, ae -> infty
  1200. ! this can be important for different model scales
  1201. ! esp. if someone runs the model in single precision!!
  1202. ! s1=int((ah*x+ch),x,xt1,xt2)

Large files files are truncated, but you can click here to view the full file