PageRenderTime 47ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/wrfv2_fire/phys/module_fr_sfire_phys.F

http://github.com/jbeezley/wrf-fire
FORTRAN Legacy | 1755 lines | 965 code | 237 blank | 553 comment | 75 complexity | bc832edc5d8b31d4f09e343acb31d622 MD5 | raw file
Possible License(s): AGPL-1.0
  1. !
  2. !*** Jan Mandel August-October 2007 email: jmandel@ucar.edu or Jan.Mandel@gmail.com
  3. !
  4. ! This file contains parts copied and/or adapted from earlier codes by
  5. ! Terry Clark, Janice Coen, Don Latham, and Net Patton.
  6. module module_fr_sfire_phys
  7. use module_model_constants, only: cp,xlv
  8. use module_fr_sfire_util
  9. implicit none
  10. PRIVATE
  11. type fire_params
  12. real,pointer,dimension(:,:):: vx,vy ! wind velocity (m/s)
  13. real,pointer,dimension(:,:):: zsf ! terrain height (m)
  14. real,pointer,dimension(:,:):: dzdxf,dzdyf ! terrain grad (1)
  15. real,pointer,dimension(:,:):: bbb,phisc,phiwc,r_0 ! spread formula coefficients
  16. real,pointer,dimension(:,:):: fgip ! init mass of surface fuel (kg/m^2)
  17. real,pointer,dimension(:,:):: ischap ! 1 if chapparal
  18. real,pointer,dimension(:,:):: fuel_time ! time to burn to 1/e (s)
  19. real,pointer,dimension(:,:):: fmc_g ! fuel moisture contents, ground (1)
  20. real,pointer,dimension(:,:):: nfuel_cat ! fuel category (integer values)
  21. end type fire_params
  22. ! subroutines and functions
  23. PUBLIC:: init_fuel_cats,fire_ros,heat_fluxes,set_nfuel_cat,set_fire_params, &
  24. write_fuels_m,fire_risk,fire_intensity,fuel_moisture,advance_moisture,fuel_name,&
  25. fire_rate_of_spread
  26. ! types
  27. public:: fire_params
  28. ! variables
  29. PUBLIC:: fire_wind_height,fcz0,fcwh,have_fuel_cats,nfuelcats,no_fuel_cat,no_fuel_cat2,windrf,moisture_classes
  30. PUBLIC:: mfuelcats
  31. ! NOTE: fcwh and fcz0 are called fwh and fz0 in read/write statements
  32. ! moisture behavior, see Mandel et al EGU 2012
  33. !! To add moisture classes:
  34. ! 1. change parameter max_moisture_classes below
  35. ! 2. change the default of nfmc to the same value in Registry/registry.fire
  36. ! 3. add the appropriate lines real::fmc_gw<number>= <default values>
  37. ! 4. add dfault
  38. !*** dimensions
  39. INTEGER, PARAMETER :: mfuelcats = 30 ! max number of fuel categories
  40. integer, parameter::max_moisture_classes=5
  41. !***
  42. integer, parameter::zm=max_moisture_classes - 3
  43. integer:: moisture_classes=3
  44. real, dimension(max_moisture_classes):: drying_lag,wetting_lag,saturation_moisture,saturation_rain, &
  45. rain_threshold,rec_drying_lag_sec,rec_wetting_lag_sec
  46. integer, dimension(max_moisture_classes):: drying_model,wetting_model,fmc_gc_initialization
  47. ! relative weights of moisture class for each fuel category
  48. integer::itmp
  49. CHARACTER (len=80), DIMENSION(max_moisture_classes), save :: moisture_class_name
  50. real, dimension(mfuelcats):: & ! should sum up to one
  51. fmc_gw01=(/ (1.0, itmp=1,mfuelcats) /), &
  52. fmc_gw02=(/ (0.0, itmp=1,mfuelcats) /), &
  53. fmc_gw03=(/ (0.0, itmp=1,mfuelcats) /), &
  54. fmc_gw04=(/ (0.0, itmp=1,mfuelcats) /), &
  55. fmc_gw05=(/ (0.0, itmp=1,mfuelcats) /)
  56. data moisture_class_name /'1-hour fuel','10-hour fuel','100-hour fuel',zm*'NOT USED'/
  57. data drying_lag /1., 10., 100. , zm*0./ ! time lag (h) approaching equilibrium moisture
  58. data wetting_lag /14, 140., 1400., zm*0./ ! time lag (h) for approaching saturation in rain
  59. data saturation_moisture /2.5, 2.5, 2.5 , zm*0./ ! saturation moisture contents (1) in rain
  60. data saturation_rain /8.0, 8.0, 8.0 , zm*0./ ! stronger rain matters only in duration (mm/h)
  61. data rain_threshold /0.05, 0.05, 0.05, zm*0 /! rain intensity this small is same as nothing
  62. data drying_model /1, 1, 1, zm*1 /
  63. data wetting_model /1, 1, 1, zm*1 /
  64. data fmc_gc_initialization /2, 2, 2, zm*2 / ! initialization 0=input, 1=from fuelmc_g, 2=from equilibrium
  65. real, dimension(7)::eq_p
  66. data eq_p/ 1.035e-09, & !(3.893e-10, 1.681e-09) ! coefficients of the equilibrium fuel moisture polynomial
  67. -2.62e-07, & !(-4.593e-07, -6.473e-08) ! fitted from the graph in Schroeder and Buck (1970)
  68. 2.507e-05, & !(2.194e-06, 4.795e-05)
  69. -0.001107, & !(-0.002353, 0.000139)
  70. 0.02245, & !(-0.009188, 0.05409)
  71. -0.05901, & !(-0.3721, 0.254)
  72. 3.043/ !(2.17, 3.915)
  73. ! =========================================================================
  74. ! Following table copied from module_fr_cawfe_fuel by Ned Patton with minor changes.
  75. ! Based on: Clark, T. L., J. L. Coen and D. Latham: 2004,
  76. ! "Description of a coupled atmosphere-fire model",
  77. ! International Journal of Wildland Fire, 13, 49-63.
  78. !
  79. ! edited by Jan Mandel jmandel@ucar.edu September 2007
  80. !
  81. ! - moved all fuel related constants and the initialization subroutine here
  82. ! - copied descriptions for fuel categories from fire_sfc.m4 in the original CAWFE code
  83. ! This file had to be copied under a new name because packages in wrf physics
  84. ! layer are not allowed to call each other.
  85. !D in col 2 means quantity derived from the others
  86. !
  87. ! Scalar constants (data same for all fuel categories):
  88. ! HFGL SURFACE FIRE HEAT FLUX THRESHOLD TO IGNITE CANOPY (W/m^2)
  89. ! CMBCNST JOULES PER KG OF DRY FUEL
  90. ! FUELHEAT FUEL PARTICLE LOW HEAT CONTENT, BTU/LB
  91. ! FUELMC_G FUEL PARTICLE (SURFACE) MOISTURE CONTENT
  92. !D BMST RATIO OF LATENT TO SENSIBLE HEAT FROM SFC BURN:
  93. ! % of total fuel mass that is water (not quite
  94. ! = % fuel moisture). BMST= (H20)/(H20+DRY)
  95. ! so BMST = FUELMC_G / (1 + FUELMC_G) where
  96. ! FUELMC_G = ground fuel moisture
  97. !
  98. ! Data arrays indexed by fuel category:
  99. ! FGI INITIAL TOTAL MASS OF SURFACE FUEL (KG/M**2)
  100. ! FUELDEPTHM FUEL DEPTH, IN M (CONVERTED TO FT)
  101. ! SAVR FUEL PARTICLE SURFACE-AREA-TO-VOLUME RATIO, 1/FT
  102. ! GRASS: 3500., 10 hr fuel: 109., 100 hr fuel: 30.
  103. ! FUELMCE MOISTURE CONTENT OF EXTINCTION; 0.30 FOR MANY DEAD FUELS; 0.15 FOR GRASS
  104. ! FUELDENS OVENDRY PARTICLE DENSITY, LB/FT^3
  105. ! ST FUEL PARTICLE TOTAL MINERAL CONTENT
  106. ! SE FUEL PARTICLE EFFECTIVE MINERAL CONTENT
  107. ! WEIGHT WEIGHTING PARAMETER THAT DETERMINES THE SLOPE OF THE MASS LOSS CURVE
  108. ! RANGES FROM ~5 (FAST BURNUP) TO 1000 ( ~40% DECR OVER 10 MIN).
  109. ! FCI_D INITIAL DRY MASS OF CANOPY FUEL
  110. ! FCT BURN OUT TIME FOR CANOPY FUEL, AFTER DRY (S)
  111. ! ichap 1 if chaparral, 0 if not
  112. !D FCI INITIAL TOTAL MASS OF CANOPY FUEL
  113. !D FCBR FUEL CANOPY BURN RATE (KG/M**2/S)
  114. !
  115. ! scalar fuel coefficients
  116. REAL, SAVE:: cmbcnst,hfgl,fuelmc_g,fuelmc_c, fire_wind_height
  117. ! computed values
  118. REAL, SAVE:: fuelheat
  119. ! defaults, may be changed in init_fuel_cats
  120. DATA cmbcnst / 17.433e+06/ ! J/kg dry fuel
  121. DATA hfgl / 17.e4 / ! W/m^2
  122. DATA fuelmc_g / 0.08 / ! set = 0 for dry ground fuel
  123. DATA fuelmc_c / 1.00 / ! set = 0 for dry canopy
  124. DATA fire_wind_height/6.096/ ! m, 6.096m Behave, 0 to use fwh in each category
  125. ! REAL, PARAMETER :: bmst = fuelmc_g/(1+fuelmc_g)
  126. ! REAL, PARAMETER :: fuelheat = cmbcnst * 4.30e-04 ! convert J/kg to BTU/lb
  127. ! real, parameter :: xlv = 2.5e6 ! to make it selfcontained
  128. ! real, parameter :: cp = 7.*287./2 ! to make it selfcontained
  129. ! fuel categorytables
  130. INTEGER, PARAMETER :: nf=14 ! fuel cats in data stmts, for fillers only`
  131. INTEGER, SAVE :: nfuelcats = 13 ! number of fuel categories, can be reset from namelist.fire
  132. INTEGER, PARAMETER :: zf = mfuelcats-nf ! number of zero fillers in data stmt
  133. INTEGER, SAVE :: no_fuel_cat = 14 ! special no fuel category outside of 1:nfuelcats
  134. INTEGER, SAVE :: no_fuel_cat2 = 14 ! all categories between no_fuel_cat and no_fuel_cat2 are no fuel
  135. INTEGER, SAVE :: ibeh=1 ! type of spread formula
  136. CHARACTER (len=80), DIMENSION(mfuelcats ), save :: fuel_name
  137. INTEGER, DIMENSION( mfuelcats ), save :: ichap
  138. REAL , DIMENSION( mfuelcats ), save :: windrf,weight,fgi,fci,fci_d,fct,fcbr, &
  139. fueldepthm,fueldens,fuelmce, &
  140. fcwh,fcz0, ffw, &
  141. savr,st,se,adjr0,adjrw,adjrs, &
  142. fmc_gl_stdev,fmc_gl_ndwi_0,fmc_gl_ndwi_rate,fmc_gl_ndwi_stdev
  143. REAL , DIMENSION( mfuelcats , max_moisture_classes), save :: fmc_gw
  144. ! =============================================================================
  145. ! Standard 13 fire behavior fuel models (for surface fires), along with some
  146. ! estimated canopy properties (for crown fire).
  147. ! =============================================================================
  148. DATA fuel_name / &
  149. 'FUEL MODEL 1: Short grass (1 ft)', &
  150. 'FUEL MODEL 2: Timber (grass and understory)', &
  151. 'FUEL MODEL 3: Tall grass (2.5 ft)', &
  152. 'FUEL MODEL 4: Chaparral (6 ft)', &
  153. 'FUEL MODEL 5: Brush (2 ft) ', &
  154. 'FUEL MODEL 6: Dormant brush, hardwood slash', &
  155. 'FUEL MODEL 7: Southern rough', &
  156. 'FUEL MODEL 8: Closed timber litter', &
  157. 'FUEL MODEL 9: Hardwood litter', &
  158. 'FUEL MODEL 10: Timber (litter + understory)', &
  159. 'FUEL MODEL 11: Light logging slash', &
  160. 'FUEL MODEL 12: Medium logging slash', &
  161. 'FUEL MODEL 13: Heavy logging slash', &
  162. 'FUEL MODEL 14: no fuel', &
  163. zf*' '/
  164. DATA windrf /0.36, 0.36, 0.44, 0.55, 0.42, 0.44, 0.44, &
  165. 0.36, 0.36, 0.36, 0.36, 0.43, 0.46, 1e-7, zf*0 / ! added jmandel October 2010
  166. DATA fgi / 0.166, 0.897, 0.675, 2.468, 0.785, 1.345, 1.092, &
  167. 1.121, 0.780, 2.694, 2.582, 7.749, 13.024, 1.e-7, zf*0. /
  168. DATA fueldepthm /0.305, 0.305, 0.762, 1.829, 0.61, 0.762,0.762, &
  169. 0.0610, 0.0610, 0.305, 0.305, 0.701, 0.914, 0.305,zf*0. /
  170. DATA savr / 3500., 2784., 1500., 1739., 1683., 1564., 1562., &
  171. 1889., 2484., 1764., 1182., 1145., 1159., 3500., zf*0. /
  172. DATA fuelmce / 0.12, 0.15, 0.25, 0.20, 0.20, 0.25, 0.40, &
  173. 0.30, 0.25, 0.25, 0.15, 0.20, 0.25, 0.12 , zf*0. /
  174. DATA fueldens / nf * 32., zf*0. / ! 32 if solid, 19 if rotten.
  175. DATA st / nf* 0.0555 , zf*0./
  176. DATA se / nf* 0.010 , zf*0./
  177. ! ----- Notes on weight: (4) - best fit of Latham data;
  178. ! (5)-(7) could be 60-120; (8)-(10) could be 300-1600;
  179. ! (11)-(13) could be 300-1600
  180. DATA weight / 7., 7., 7., 180., 100., 100., 100., &
  181. 900., 900., 900., 900., 900., 900., 7. , zf*0./
  182. ! ----- 1.12083 is 5 tons/acre. 5-50 tons/acre orig., 100-300 after blowdown
  183. DATA fci_d / 0., 0., 0., 1.123, 0., 0., 0., &
  184. 1.121, 1.121, 1.121, 1.121, 1.121, 1.121, 0., zf*0./
  185. DATA fct / 60., 60., 60., 60., 60., 60., 60., &
  186. 60., 120., 180., 180., 180., 180. , 60. , zf*0. /
  187. DATA ichap / 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 , zf*0/
  188. DATA fcwh / 6.096, 6.096, 6.096, 6.096, 6.096, 6.096, 6.096, &
  189. 6.096, 6.096, 6.096, 6.096, 6.096, 6.096, 6.096, zf*0. / ! consistent with BEHAVE
  190. ! roughness length 0.13*fueldepthm except cat 3 fz0=0.1 for consistency with landuse
  191. ! fz0 = 0.0396,0.0396,0.0991,0.2378,0.0793,0.0991,0.0991,
  192. DATA fcz0 / 0.0396,0.0396,0.1000,0.2378,0.0793,0.0991,0.0991, &
  193. 0.0079,0.0079,0.0396,0.0396,0.0911,0.1188,0.0396, zf * 0. /
  194. !DATA fcwh /0.6 , 0.6, 1.5, 36, 1.2, 1.5, 1.5, &
  195. ! 0.12, 0.12, 0.6, 0.6, 1.38, 1.8, 1.8, zf*0 / ! fuel wind height, added jm 2/23/11
  196. !DATA fcz0 /0.3, 0.3, 0.75, 18., 0.6, 0.75, 0.75, &
  197. ! 0.06, 0.06, 0.3, 0.3, 0.69, 0.9, 0.9, zf*0 / ! fuel roughness height, added jm 2/23/11
  198. DATA ffw /nf* 0.9, zf*0/
  199. DATA fmc_gl_ndwi_0 /nf*0.1, zf*0./
  200. DATA fmc_gl_ndwi_rate /nf*0.6, zf*0./
  201. DATA fmc_gl_ndwi_stdev /nf*0.2, zf*0./
  202. DATA fmc_gl_stdev /nf*0.2, zf*0./
  203. DATA adjr0 /mfuelcats*1./
  204. DATA adjrw /mfuelcats*1./
  205. DATA adjrs /mfuelcats*1./
  206. ! =========================================================================
  207. logical, save :: have_fuel_cats=.false.
  208. contains
  209. subroutine fuel_moisture( &
  210. id, & ! for prints and maybe file names
  211. nfmc, &
  212. ids,ide, jds,jde, & ! atm grid dimensions
  213. ims,ime, jms,jme, &
  214. ips,ipe,jps,jpe, &
  215. its,ite,jts,jte, &
  216. ifds, ifde, jfds, jfde, & ! fire grid dimensions
  217. ifms, ifme, jfms, jfme, &
  218. ifts,ifte,jfts,jfte, &
  219. ir,jr, & ! atm/fire grid ratio
  220. nfuel_cat, & ! fuel data
  221. fndwi, & ! satellite sensing on fire grid
  222. fmc_gc, & ! moisture contents by class on atmospheric grid
  223. fmc_g & ! weighted fuel moisture contents on fire grid
  224. )
  225. implicit none
  226. !**** arguments
  227. integer, intent(in):: &
  228. id,nfmc, &
  229. ids,ide, jds,jde, & ! atm grid dimensions
  230. ims,ime, jms,jme, &
  231. ips,ipe,jps,jpe, &
  232. its,ite,jts,jte, &
  233. ifds, ifde, jfds, jfde, & ! fire grid dimensions
  234. ifms, ifme, jfms, jfme, &
  235. ifts,ifte,jfts,jfte, &
  236. ir,jr ! atm/fire grid ratio
  237. real,intent(in),dimension(ifms:ifme,jfms:jfme):: nfuel_cat, & ! fuel data
  238. fndwi ! satellite sensing interpolated to fire grid
  239. real,intent(inout),dimension(ims:ime,nfmc,jms:jme):: fmc_gc
  240. real,intent(out),dimension(ifms:ifme,jfms:jfme):: fmc_g ! fuel data
  241. !**** local
  242. real, dimension(its-1:ite+1,jts-1:jte+1):: fmc_k ! copy of fmc_gc(:,k,:)
  243. real, dimension(ifts:ifte,jfts:jfte):: fmc_f, & ! interpolation of fmc_gc(:,k,:) to the fire grid
  244. nwdi_f ! inerpolation of nwdi to the fire grid
  245. integer::i,j,k,n
  246. integer::ibs,ibe,jbs,jbe
  247. real::f1,w1,w2,f2,fa,fc
  248. character(len=128)::msg
  249. call check_mesh_2dim(ifts,ifte,jfts,jfte,ifds,ifde,jfds,jfde) ! check if fire tile fits into domain
  250. call check_mesh_2dim(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme) ! check if fire tile fits into memory
  251. do j=jfts,jfte
  252. do i=ifts,ifte
  253. fmc_g(i,j)=0. ! initialize sum over classes
  254. enddo
  255. enddo
  256. ! one beyond the tile but not beyond the domain boundary
  257. ibs=max(ids,its-1)
  258. ibe=min(ide,ite+1)
  259. jbs=max(jds,jts-1)
  260. jbe=min(jde,jte+1)
  261. call check_mesh_2dim(ibs,ibe,jbs,jbe,ims,ime,jms,jme) ! check if tile with halo fits into memory
  262. do k=1,moisture_classes
  263. ! copy halo beyond the tile but not beyond the domain boundary
  264. do j=jbs,jbe
  265. do i=ibs,ibe
  266. fmc_k(i,j)=fmc_gc(i,k,j) ! copy slice to 2d array
  267. enddo
  268. enddo
  269. call print_2d_stats(ibs,ibe,jbs,jbe,its-1,ite+1,jts-1,jte+1,fmc_k,'fuel_moisture: fmc_k')
  270. ! interpolate moisture contents in the class k to the fire mesh
  271. call interpolate_z2fire(id,0, & ! for debug output, <= 0 no output
  272. ids,ide,jds,jde, & ! atm grid dimensions
  273. its-1,ite+1,jts-1,jte+1, & ! memory dimensions
  274. ips,ipe,jps,jpe, &
  275. its,ite,jts,jte, &
  276. ifds, ifde, jfds, jfde, & ! fire grid dimensions
  277. ifts, ifte, jfts, jfte, &
  278. ifts,ifte, jfts,jfte, &
  279. ir,jr, & ! atm/fire grid ratio
  280. fmc_k, & ! atm grid arrays in
  281. fmc_f) ! fire grid arrays out
  282. call print_2d_stats(ifts,ifte,jfts,jfte,ifts,ifte,jfts,jfte,fmc_f,'fuel_moisture: fmc_f')
  283. if(k .eq. kfmc_ndwi)then ! if live moisture, assimilate ndwi
  284. call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,fndwi,'fuel_moisture: fndwi')
  285. write(msg,'(a,i4)')'Assimilating NDWI in fuel moisture class ',k
  286. call message(msg)
  287. endif
  288. ! add moisture contents for class k to the fuel moisture
  289. do j=jfts,jfte
  290. do i=ifts,ifte
  291. n = nfuel_cat(i,j)
  292. if(n > 0)then
  293. if(k .ne. kfmc_ndwi)then
  294. fmc_g(i,j)=fmc_g(i,j)+fmc_gw(n,k)*fmc_f(i,j) ! add to sum over classes
  295. else ! if live moisture, assimilate ndwi
  296. f1=fmc_f(i,j)
  297. w1 = fmc_gl_stdev(n)
  298. w1 = 1./(w1*w1) ! weight of forecast
  299. w2 = fmc_gl_ndwi_stdev(n)
  300. w2 = 1./(w2*w2) ! weight of update
  301. f2 = fmc_gl_ndwi_0(n) + fmc_gl_ndwi_rate(n) * fndwi(i,j) ! from regression
  302. fa = (w1*f1 + w2*f2) / (w1 + w2) ! updated value
  303. fc = fmc_gw(n,k)*fa ! times proportion of live fuel
  304. fmc_g(i,j)=fmc_g(i,j)+fc ! add to sum over classes
  305. ! write(*,*)'NDWI:',i,j,f1,f2,w1,w2,f1,fa,fmc_gw(n,k),fc,fmc_g(i,j)
  306. endif
  307. endif
  308. enddo
  309. enddo
  310. enddo
  311. call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,fmc_g,'fuel_moisture: fmc_g')
  312. end subroutine fuel_moisture
  313. subroutine advance_moisture( &
  314. initialize, & ! initialize timestepping. true on the first call at time 0, then false
  315. ims,ime, jms,jme, & ! memory dimensions
  316. its,ite, jts,jte, & ! tile dimensions
  317. nfmc, & ! dimension of moisture fields
  318. moisture_dt, & ! timestep = time step time elapsed from the last call
  319. fmep_decay_tlag, & ! moisture extended model assimilated diffs. decay time lag
  320. rainc, rainnc, & ! accumulated rain
  321. t2, q2, psfc, & ! temperature (K), vapor contents (kg/kg), pressure (Pa) at the surface
  322. rain_old, & ! previous value of accumulated rain
  323. t2_old, q2_old, psfc_old, & ! previous values of the atmospheric state at surface
  324. rh_fire, & ! relative humidity at surface, for diagnostic only
  325. fmc_gc, & ! fuel moisture by class, updated
  326. fmep, & ! fuel moisture extended model parameters
  327. fmc_equi, & ! fuel moisture equilibrium by class, for diagnostics only
  328. fmc_lag & ! fuel moisture tendency by classe, for diagnostics only
  329. )
  330. implicit none
  331. !*** arguments
  332. logical, intent(in):: initialize
  333. integer, intent(in):: &
  334. ims,ime, jms,jme, & ! memory dimensions
  335. its,ite, jts,jte, & ! tile dimensions
  336. nfmc ! number of moisture fields
  337. real, intent(in):: moisture_dt, fmep_decay_tlag
  338. real, intent(in), dimension(ims:ime,jms:jme):: t2, q2, psfc, rainc, rainnc
  339. real, intent(inout), dimension(ims:ime,jms:jme):: t2_old, q2_old, psfc_old, rain_old
  340. real, intent(inout), dimension(ims:ime,nfmc,jms:jme):: fmc_gc
  341. real, intent(inout), dimension(ims:ime,2,jms:jme):: fmep
  342. real, intent(out), dimension(ims:ime,nfmc,jms:jme):: fmc_equi, fmc_lag
  343. real, intent(out), dimension(ims:ime,jms:jme)::rh_fire
  344. !*** global
  345. ! fuel properties moisture set by init_fuel_cats
  346. !*** local
  347. integer:: i,j,k
  348. real::rain_int, T, P, Q, QRS, ES, RH, tend, EMC_d, EMC_w, EMC, R, rain_diff, fmc, rlag, equi, &
  349. d, w, rhmax, rhmin, change, rainmax,rainmin, fmc_old, H, deltaS, deltaE
  350. real, parameter::tol=1e-2 ! relative change larger than that will switch to exponential ode solver
  351. character(len=256)::msg
  352. logical::bad_wrf
  353. integer::msglevel=2
  354. logical, parameter::check_rh=.false.
  355. integer::check_data=2 ! 0=nothing, 1=replace quietly, 2=warning (also printed if msglevel>2), 3=crash
  356. real::epsilon,Pws,Pw,t2_min,q2_min,psfc_min
  357. real::t2_floor=200. ! minimum allowed temperature (K)
  358. real::q2_floor=1e-8 ! miniumu allowed moisture contents (kg/kg)
  359. real::psfc_floor=1000. ! mimimum allowed surface pressiure (Pa)
  360. !*** executable
  361. ! check arguments
  362. if(msglevel>1)then
  363. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  364. write(msg,'(a,f10.2,a,i4,a,i4)')'advance_moisture dt=',moisture_dt,'s using ',moisture_classes,' classes from possible ',nfmc
  365. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  366. call message(msg,level=2)
  367. endif
  368. if(moisture_classes > nfmc .or. moisture_classes > max_moisture_classes)then
  369. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  370. write(msg,*)'advance_moisture: moisture_classes=',moisture_classes, &
  371. ' > nfmc=',nfmc,' or > max_moisture_classes=',max_moisture_classes
  372. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  373. call crash(msg)
  374. endif
  375. call print_2d_stats(its,ite,jts,jte,ims,ime,jms,jme,t2,'T2')
  376. call print_2d_stats(its,ite,jts,jte,ims,ime,jms,jme,q2,'Q2')
  377. call print_2d_stats(its,ite,jts,jte,ims,ime,jms,jme,psfc,'PSFC')
  378. if(initialize) then
  379. call message('advance_moisture: initializing, copying surface variables to old')
  380. call copy2old
  381. else
  382. call print_3d_stats_by_slice(its,ite,1,moisture_classes,jts,jte,ims,ime,1,nfmc,jms,jme,fmc_gc,'before advance fmc_gc')
  383. endif
  384. if(check_data.ge.2 .or. msglevel.ge.2)then
  385. t2_min = huge(t2_min)
  386. q2_min = huge(q2_min)
  387. psfc_min = huge(psfc_min)
  388. do j=jts,jte
  389. do i=its,ite
  390. t2_min=min(t2(i,j),t2_min)
  391. q2_min=min(q2(i,j),q2_min)
  392. psfc_min=min(psfc(i,j),psfc_min)
  393. enddo
  394. enddo
  395. bad_wrf = ( t2_min<t2_floor .or. psfc_min<psfc_floor .or. q2_min<q2_floor)
  396. if(bad_wrf .or. msglevel.ge.2)then
  397. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  398. 91 format(a,3(2x,a,e11.3))
  399. write(msg,91)'minimal ','t2',t2_min,'q2',q2_min,'psfc',psfc_min
  400. call message(msg,level=0)
  401. write(msg,91)'floor ','t2',t2_floor,'q2',q2_floor,'psfc',psfc_floor
  402. call message(msg,level=0)
  403. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  404. endif
  405. if(bad_wrf)then
  406. if(check_data.ge.3)then
  407. call crash('advance_moisture: invalid data passed from WRF')
  408. else
  409. call message('WARNING: advance_moisture: nonphysical input values replaced by floor',level=0)
  410. endif
  411. endif
  412. endif
  413. ! one time step
  414. rhmax=-huge(rhmax)
  415. rhmin=huge(rhmin)
  416. rainmax=-huge(rainmax)
  417. rainmin= huge(rainmin)
  418. do j=jts,jte
  419. do k=1,moisture_classes
  420. do i=its,ite
  421. ! old fuel moisture contents
  422. ! compute the rain intensity from the difference of accumulated rain
  423. rain_diff = ((rainc(i,j) + rainnc(i,j)) - rain_old(i,j))
  424. if(moisture_dt > 0.)then
  425. rain_int = 3600. * rain_diff / moisture_dt
  426. else
  427. rain_int = 0.
  428. endif
  429. rainmax = max(rainmax,rain_int)
  430. rainmin = min(rainmin,rain_int)
  431. R = rain_int - rain_threshold(k)
  432. ! average the inputs for second order accuracy
  433. T = 0.5 * (t2_old(i,j) + t2(i,j))
  434. P = 0.5 * (psfc_old(i,j) + psfc(i,j))
  435. Q = 0.5 * (q2_old(i,j) + q2(i,j))
  436. ! replace nonphysical values by floor
  437. if(check_data .ge. 1)then
  438. T = max(T,t2_floor)
  439. P = max(P,psfc_floor)
  440. Q = max(Q,q2_floor)
  441. endif
  442. ! compute the relative humidity
  443. ! ES=610.78*exp(17.269*(T-273.161)/(T-35.861))
  444. ! QRS=0.622*ES/(P-0.378*ES)
  445. ! RH = Q/QRS
  446. ! function rh_from_q from Adam Kochanski following Murphy and Koop, Q.J.R. Meteorol. Soc (2005) 131 1539-1565 eq. (10)
  447. epsilon = 0.622 ! Molecular weight of water (18.02 g/mol) to molecular weight of dry air (28.97 g/mol)
  448. ! vapor pressure [Pa]
  449. Pw=q*P/(epsilon+(1-epsilon)*q);
  450. ! saturation vapor pressure [Pa]
  451. Pws= exp( 54.842763 - 6763.22/T - 4.210 * log(T) + 0.000367*T + &
  452. tanh(0.0415*(T - 218.8)) * (53.878 - 1331.22/T - 9.44523 * log(T) + 0.014025*T))
  453. !realtive humidity [1]
  454. RH = Pw/Pws
  455. rh_fire(i,j)=RH
  456. rhmax=max(RH,rhmax)
  457. rhmin=min(RH,rhmin)
  458. deltaE = fmep(i,1,j)
  459. deltaS = fmep(i,2,j)
  460. if(.not.check_rh)then
  461. RH = min(RH,1.0)
  462. else
  463. if(RH < 0.0 .or. RH > 1.0 .or. RH .ne. RH )then
  464. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  465. write(msg,'(a,2i6,5(a,f10.2))')'At i,j ',i,j,' RH=',RH, &
  466. ' from T=',T,' P=',P,' Q=',Q
  467. call message(msg)
  468. call crash('Relative humidity must be between 0 and 1, saturated water contents must be >0')
  469. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  470. endif
  471. endif
  472. !print *,'ADV_MOIST i=',i,' j=',j,' T=',T,' P=',P,' Q=',Q,' ES=',ES,' QRS=',QRS,' RH=',RH
  473. if (R > 0.) then
  474. select case(wetting_model(k))
  475. case(1) ! saturation_moisture=2.5 wetting_lag=14h saturation_rain=8 mm/h calibrated to VanWagner&Pickett 1985 per 24 hours
  476. EMC_w=saturation_moisture(k) + deltaS
  477. EMC_d=saturation_moisture(k) + deltaS
  478. rlag=rec_wetting_lag_sec(k) * (1. - exp(-R/saturation_rain(k)))
  479. end select
  480. else ! not raining
  481. select case(drying_model(k))
  482. case(1) ! Van Wagner and Pickett (1972, 1985) per Viney (1991) eq (7) (8)
  483. H = RH * 100.
  484. d=0.942*H**0.679 + 0.4994e-4*exp(0.1*H) + 0.18*(21.1+273.15-T)*(1-exp(-0.115*H)) ! equilibrium moisture for drying
  485. w=0.618*H**0.753 + 0.4540e-4*exp(0.1*H) + 0.18*(21.1+273.15-T)*(1-exp(-0.115*H)) ! equilibrium moisture for adsorbtion
  486. if(d.ne.d.or.w.ne.w)call crash('equilibrium moisture calculation failed, result is NaN')
  487. d = d*0.01
  488. w = w*0.01
  489. EMC_d = max(max(d,w)+deltaE,0.0)
  490. EMC_w = max(min(d,w)+deltaE,0.0)
  491. rlag=rec_drying_lag_sec(k)
  492. end select
  493. endif
  494. !*** MODELS THAT ARE NOT OF THE EXPONENTIAL TIME LAG KIND
  495. ! ARE RESPONSIBLE FOR THEIR OWN LOGIC, THESE MODELS
  496. ! SHOULD COMPUTE fmc_gc(i,k,j) DIRECTLY AND SET TLAG < 0
  497. !
  498. if(rlag > 0.0)then
  499. if(.not.initialize .or. fmc_gc_initialization(k).eq.0)then ! take old from before, no initialization
  500. fmc_old = fmc_gc(i,k,j)
  501. elseif(fmc_gc_initialization(k).eq.1)then ! from scalar fuelmc_g
  502. fmc_old = fuelmc_g
  503. elseif(fmc_gc_initialization(k).eq.2)then ! from computed equilibrium
  504. fmc_old=0.5*(EMC_d+EMC_w)
  505. else
  506. call crash('bad value of fmc_gc_initialization(k), must be between 0 and 2')
  507. endif
  508. equi = max(min(fmc_old, EMC_d),EMC_w) ! take lower or upper equilibrium value
  509. change = moisture_dt * rlag
  510. if(change < tol)then
  511. if(fire_print_msg.ge.3)call message('midpoint method')
  512. fmc = fmc_old + (equi - fmc_old)*change*(1.0 - 0.5*change) ! 2nd order Taylor
  513. else
  514. if(fire_print_msg.ge.3)call message('exponential method')
  515. fmc = fmc_old + (equi - fmc_old)*(1 - exp(-change))
  516. endif
  517. fmc_gc(i,k,j) = fmc
  518. ! diagnostics out
  519. fmc_equi(i,k,j)=equi
  520. fmc_lag(i,k,j)=1.0/(3600.0*rlag)
  521. ! diagnostic prints
  522. if(fire_print_msg.ge.3)then
  523. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  524. write(msg,*)'i=',i,' j=',j,'EMC_w=',EMC_w,' EMC_d=',EMC_d
  525. call message(msg)
  526. write(msg,*)'fmc_old=',fmc,' equi=',equi,' change=',change,' fmc=',fmc
  527. call message(msg)
  528. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  529. endif
  530. endif
  531. enddo
  532. enddo
  533. enddo
  534. ! assimilated differences decay
  535. do j=jts,jte
  536. do k=1,2
  537. do i=its,ite
  538. change = moisture_dt / (fmep_decay_tlag * 3600.)
  539. if(change < tol) then
  540. fmep(i,k,j) = fmep(i,k,j)*(1.0 - change * (1.0 - 0.5 * change))
  541. else
  542. fmep(i,k,j) = fmep(i,k,j)*exp(-change)
  543. endif
  544. enddo
  545. enddo
  546. enddo
  547. if(fire_print_msg.ge.2)then
  548. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  549. write(msg,2)'Rain intensity min',rainmin, ' max',rainmax,' mm/h'
  550. call message(msg)
  551. if(rainmin <0.)then
  552. call message('WARNING rain accumulation must increase')
  553. endif
  554. write(msg,2)'Relative humidity min',100*rhmin,' max',100*rhmax,'%'
  555. call message(msg)
  556. if(.not.(rhmax<=1.0 .and. rhmin>=0))then
  557. call message('WARNING Relative humidity must be between 0 and 100%')
  558. endif
  559. 2 format(2(a,f10.2),a)
  560. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  561. endif
  562. call print_3d_stats_by_slice(its,ite,1,moisture_classes,jts,jte,ims,ime,1,nfmc,jms,jme,fmc_equi,'equilibrium fmc_equi')
  563. call print_3d_stats_by_slice(its,ite,1,moisture_classes,jts,jte,ims,ime,1,nfmc,jms,jme,fmc_lag,'time lag')
  564. call print_3d_stats_by_slice(its,ite,1,moisture_classes,jts,jte,ims,ime,1,nfmc,jms,jme,fmc_gc,'after advance fmc_gc')
  565. call copy2old
  566. return
  567. contains
  568. subroutine copy2old
  569. do j=jts,jte
  570. do i=its,ite
  571. rain_old(i,j) = rainc(i,j) + rainnc(i,j)
  572. t2_old(i,j) = t2(i,j)
  573. q2_old(i,j) = q2(i,j)
  574. psfc_old(i,j) = psfc(i,j)
  575. enddo
  576. enddo
  577. end subroutine copy2old
  578. subroutine get_equi_moist
  579. end subroutine get_equi_moist
  580. end subroutine advance_moisture
  581. subroutine init_fuel_cats(init_fuel_moisture)
  582. implicit none
  583. !*** purpose: initialize fuel tables and variables by constants
  584. !*** arguments:
  585. logical, intent(in)::init_fuel_moisture
  586. logical, external:: wrf_dm_on_monitor
  587. !$ integer, external:: OMP_GET_THREAD_NUM
  588. !*** local
  589. integer:: i,j,k,ii,iounit,ierr,kk
  590. character(len=128):: msg
  591. REAL , DIMENSION( mfuelcats ) :: fwh, fz0
  592. !*** executable
  593. ! read
  594. namelist /fuel_scalars/ cmbcnst,hfgl,fuelmc_g,fuelmc_c,nfuelcats,no_fuel_cat,no_fuel_cat2,fire_wind_height,ibeh
  595. namelist /fuel_categories/ fuel_name,windrf,fgi,fueldepthm,savr, &
  596. fuelmce,fueldens,st,se,weight,fci_d,fct,ichap,fwh,fz0,ffw, &
  597. fmc_gl_ndwi_0,fmc_gl_ndwi_rate,fmc_gl_ndwi_stdev, fmc_gl_stdev, &
  598. adjr0,adjrw,adjrs,fmc_gw01,fmc_gw02,fmc_gw03,fmc_gw04,fmc_gw05
  599. namelist /moisture/ moisture_classes,drying_lag,wetting_lag,saturation_moisture,saturation_rain,rain_threshold, &
  600. drying_model,wetting_model, moisture_class_name,fmc_gc_initialization
  601. !$ if (OMP_GET_THREAD_NUM() .ne. 0)then
  602. !$ call crash('init_fuel_cats: must be called from master thread')
  603. !$ endif
  604. IF ( wrf_dm_on_monitor() ) THEN
  605. ! we are the master task
  606. ! copy in defaults
  607. fwh=fcwh
  608. fz0=fcz0
  609. ! read the file
  610. iounit=open_text_file('namelist.fire','read')
  611. read(iounit,fuel_scalars,iostat=ierr)
  612. if(ierr.ne.0)call crash('init_fuel_cats: error reading namelist fuel_scalars in file namelist.fire')
  613. read(iounit,fuel_categories,iostat=ierr)
  614. if(ierr.ne.0)call crash('init_fuel_cats: error reading namelist fuel_categories in file namelist.fire')
  615. if(init_fuel_moisture)then
  616. read(iounit,moisture,iostat=ierr)
  617. if(ierr.ne.0)call crash('init_fuel_cats: error reading namelist moisture in file namelist.fire')
  618. endif
  619. fmc_gw(1:mfuelcats,1)=fmc_gw01
  620. fmc_gw(1:mfuelcats,2)=fmc_gw02
  621. fmc_gw(1:mfuelcats,3)=fmc_gw03
  622. fmc_gw(1:mfuelcats,4)=fmc_gw04
  623. fmc_gw(1:mfuelcats,5)=fmc_gw05
  624. CLOSE(iounit)
  625. ! copy out to permanent names
  626. fcwh=fwh
  627. fcz0=fz0
  628. if (nfuelcats>mfuelcats) then
  629. write(msg,*)'nfuelcats=',nfuelcats,' too large, increase mfuelcats'
  630. call crash(msg)
  631. endif
  632. if (no_fuel_cat >= 1 .and. no_fuel_cat <= nfuelcats)then
  633. write(msg,*)'no_fuel_cat=',no_fuel_cat,' may not be between 1 and nfuelcats=',nfuelcats
  634. call crash(msg)
  635. endif
  636. if (no_fuel_cat > no_fuel_cat2)then
  637. write(msg,*)'no_fuel_cat=',no_fuel_cat,' must not be larger than no_fuel_cat2=',no_fuel_cat2
  638. call crash(msg)
  639. endif
  640. ENDIF
  641. ! broadcast fuel tables
  642. call wrf_dm_bcast_real(cmbcnst,1)
  643. call wrf_dm_bcast_real(hfgl,1)
  644. call wrf_dm_bcast_real(fuelmc_g,1)
  645. call wrf_dm_bcast_real(fuelmc_c,1)
  646. call wrf_dm_bcast_real(fire_wind_height,1)
  647. call wrf_dm_bcast_integer(nfuelcats,1)
  648. call wrf_dm_bcast_integer(no_fuel_cat,1)
  649. call wrf_dm_bcast_integer(no_fuel_cat2,1)
  650. call wrf_dm_bcast_integer(ibeh,1)
  651. call wrf_dm_bcast_real(windrf, nfuelcats)
  652. call wrf_dm_bcast_real(fgi, nfuelcats)
  653. call wrf_dm_bcast_real(fueldepthm,nfuelcats)
  654. call wrf_dm_bcast_real(savr, nfuelcats)
  655. call wrf_dm_bcast_real(fuelmce, nfuelcats)
  656. call wrf_dm_bcast_real(fueldens, nfuelcats)
  657. call wrf_dm_bcast_real(st, nfuelcats)
  658. call wrf_dm_bcast_real(se, nfuelcats)
  659. call wrf_dm_bcast_real(weight, nfuelcats)
  660. call wrf_dm_bcast_real(fci_d, nfuelcats)
  661. call wrf_dm_bcast_real(fct, nfuelcats)
  662. call wrf_dm_bcast_integer(ichap, nfuelcats)
  663. call wrf_dm_bcast_real(fcwh, nfuelcats)
  664. call wrf_dm_bcast_real(fcz0, nfuelcats)
  665. call wrf_dm_bcast_real(ffw, nfuelcats)
  666. call wrf_dm_bcast_real(adjr0, nfuelcats)
  667. call wrf_dm_bcast_real(adjrw, nfuelcats)
  668. call wrf_dm_bcast_real(adjrs, nfuelcats)
  669. call wrf_dm_bcast_real(fmc_gl_ndwi_0, nfuelcats)
  670. call wrf_dm_bcast_real(fmc_gl_ndwi_rate, nfuelcats)
  671. call wrf_dm_bcast_real(fmc_gl_ndwi_stdev,nfuelcats)
  672. call wrf_dm_bcast_real(fmc_gl_stdev, nfuelcats)
  673. ! broadcast moisture tables
  674. call wrf_dm_bcast_integer(moisture_classes,1)
  675. call wrf_dm_bcast_real(drying_lag, max_moisture_classes)
  676. call wrf_dm_bcast_real(wetting_lag, max_moisture_classes)
  677. call wrf_dm_bcast_real(saturation_moisture, max_moisture_classes)
  678. call wrf_dm_bcast_real(saturation_rain, max_moisture_classes)
  679. call wrf_dm_bcast_real(rain_threshold, max_moisture_classes)
  680. call wrf_dm_bcast_integer(drying_model, max_moisture_classes)
  681. call wrf_dm_bcast_integer(wetting_model, max_moisture_classes)
  682. call wrf_dm_bcast_integer(fmc_gc_initialization, max_moisture_classes)
  683. call wrf_dm_bcast_real(fmc_gw, mfuelcats*max_moisture_classes)
  684. ! moisture model derived scalars
  685. do i=1,moisture_classes
  686. rec_drying_lag_sec(i) = 1.0/(3600.0*drying_lag(i))
  687. rec_wetting_lag_sec(i) = 1.0/(3600.0*wetting_lag(i))
  688. enddo
  689. !-------------------------------- fuel model
  690. ! compute derived scalars
  691. fuelheat = cmbcnst * 4.30e-04 ! convert J/kg to BTU/lb
  692. ! compute derived fuel category coefficients
  693. DO i = 1,nfuelcats
  694. fci(i) = (1.+fuelmc_c)*fci_d(i)
  695. if(fct(i) .ne. 0.)then
  696. fcbr(i) = fci_d(i)/fct(i) ! to avoid division by zero
  697. else
  698. fcbr(i) = 0
  699. endif
  700. END DO
  701. ! prints
  702. call message('**********************************************************')
  703. call message('FUEL COEFFICIENTS')
  704. write(msg,8)'cmbcnst ',cmbcnst
  705. call message(msg)
  706. write(msg,8)'hfgl ',hfgl
  707. call message(msg)
  708. write(msg,8)'fuelmc_g ',fuelmc_g
  709. call message(msg)
  710. write(msg,8)'fuelmc_c ',fuelmc_c
  711. call message(msg)
  712. write(msg,8)'fuelheat ',fuelheat
  713. call message(msg)
  714. write(msg,7)'nfuelcats ',nfuelcats
  715. call message(msg)
  716. write(msg,7)'no_fuel_cat',no_fuel_cat
  717. call message(msg)
  718. write(msg,7)'no_fuel_cat2',no_fuel_cat2
  719. call message(msg)
  720. if(init_fuel_moisture)then
  721. write(msg,7)'moisture_classes',moisture_classes
  722. call message(msg)
  723. endif
  724. j=1
  725. 7 format(a,5(1x,i8,4x))
  726. 8 format(a,5(1x,g12.5e2))
  727. 9 format(a,5(1x,a))
  728. 10 format(a,i2.2,2x,5(1x,g12.5e2))
  729. do i=1,nfuelcats,j
  730. k=min(i+j-1,nfuelcats)
  731. call message(' ')
  732. write(msg,7)'CATEGORY ',(ii,ii=i,k)
  733. call message(msg)
  734. write(msg,9)'fuel name ',(trim(fuel_name(ii)),ii=i,k)
  735. call message(msg)
  736. write(msg,8)'fwh ',(fcwh(ii),ii=i,k)
  737. call message(msg)
  738. write(msg,8)'fz0 ',(fcz0(ii),ii=i,k)
  739. call message(msg)
  740. write(msg,8)'windrf ',(windrf(ii),ii=i,k)
  741. call message(msg)
  742. write(msg,8)'fgi ',(fgi(ii),ii=i,k)
  743. call message(msg)
  744. write(msg,8)'fueldepthm',(fueldepthm(ii),ii=i,k)
  745. call message(msg)
  746. write(msg,8)'savr ',(savr(ii),ii=i,k)
  747. call message(msg)
  748. write(msg,8)'fuelmce ',(fuelmce(ii),ii=i,k)
  749. call message(msg)
  750. write(msg,8)'fueldens ',(fueldens(ii),ii=i,k)
  751. call message(msg)
  752. write(msg,8)'st ',(st(ii),ii=i,k)
  753. call message(msg)
  754. write(msg,8)'se ',(se(ii),ii=i,k)
  755. call message(msg)
  756. write(msg,8)'weight ',(weight(ii),ii=i,k)
  757. call message(msg)
  758. write(msg,8)'fci_d ',(fci_d(ii),ii=i,k)
  759. call message(msg)
  760. write(msg,8)'fct ',(fct(ii),ii=i,k)
  761. call message(msg)
  762. write(msg,7)'ichap ',(ichap(ii),ii=i,k)
  763. call message(msg)
  764. write(msg,8)'fci ',(fci(ii),ii=i,k)
  765. call message(msg)
  766. write(msg,8)'fcbr ',(fcbr(ii),ii=i,k)
  767. call message(msg)
  768. write(msg,8)'ffw ',(ffw(ii),ii=i,k)
  769. call message(msg)
  770. write(msg,8)'adjr0 ',(adjr0(ii),ii=i,k)
  771. call message(msg)
  772. write(msg,8)'adjrw ',(adjrw(ii),ii=i,k)
  773. call message(msg)
  774. write(msg,8)'adjrs ',(adjrs(ii),ii=i,k)
  775. call message(msg)
  776. if(init_fuel_moisture)then
  777. do kk=1,moisture_classes
  778. write(msg,10)'fmc_gw',kk,(fmc_gw(ii,kk),ii=i,k)
  779. call message(msg)
  780. enddo
  781. endif
  782. if(kfmc_ndwi>0)then
  783. write(msg,8)'fmc_gl_stdev ',(fmc_gl_stdev(ii),ii=i,k)
  784. call message(msg)
  785. write(msg,8)'fmc_gl_ndwi_0 ',(fmc_gl_ndwi_0(ii),ii=i,k)
  786. call message(msg)
  787. write(msg,8)'fmc_gl_ndwi_rate ',(fmc_gl_ndwi_rate(ii),ii=i,k)
  788. call message(msg)
  789. write(msg,8)'fmc_gl_ndwi_stdev',(fmc_gl_ndwi_stdev(ii),ii=i,k)
  790. call message(msg)
  791. endif
  792. enddo
  793. call message(' ')
  794. call message('**********************************************************')
  795. if(init_fuel_moisture)then
  796. j=1
  797. do i=1,moisture_classes,j
  798. k=min(i+j-1,nfuelcats)
  799. call message(' ')
  800. write(msg,7)'FUEL MOISTURE CLASS',(ii,ii=i,k)
  801. call message(msg)
  802. write(msg,9)'moisture class name ',(trim(moisture_class_name(ii)),ii=i,k)
  803. call message(msg)
  804. write(msg,7)'drying_model ',(drying_model(ii),ii=i,k)
  805. call message(msg)
  806. write(msg,8)'drying_lag (h) ',(drying_lag(ii),ii=i,k)
  807. call message(msg)
  808. write(msg,7)'wetting_model ',(wetting_model(ii),ii=i,k)
  809. call message(msg)
  810. write(msg,7)'fmc_gc_initialization ',(fmc_gc_initialization(ii),ii=i,k)
  811. call message(msg)
  812. write(msg,8)'wetting_lag (h) ',(wetting_lag(ii),ii=i,k)
  813. call message(msg)
  814. write(msg,8)'saturation_moisture (1)',(saturation_moisture(ii),ii=i,k)
  815. call message(msg)
  816. write(msg,8)'saturation_rain (mm/h) ',(saturation_rain(ii),ii=i,k)
  817. call message(msg)
  818. write(msg,8)'rain_threshold (mm/h) ',(rain_threshold(ii),ii=i,k)
  819. call message(msg)
  820. enddo
  821. call message(' ')
  822. call message('**********************************************************')
  823. call message(' ')
  824. endif
  825. have_fuel_cats=.true.
  826. ! and print to file
  827. IF ( wrf_dm_on_monitor() ) THEN
  828. call write_fuels_m(61,30.,1.)
  829. ENDIF
  830. end subroutine init_fuel_cats
  831. subroutine write_fuels_m(nsteps,maxwind,maxslope)
  832. implicit none
  833. integer, intent(in):: nsteps ! number of steps for speed computation
  834. real, intent(in):: maxwind,maxslope ! computer from zero to these
  835. integer:: iounit,k,j,i,isave
  836. type(fire_params)::fp
  837. real, dimension(1:3,1:nsteps), target::vx,vy,zsf,dzdxf,dzdyf,bbb,phisc,phiwc,r_0,fgip,ischap,fmc_g,wind,nfuel_cat
  838. real, dimension(1:3,1:nsteps)::fuel_time,ros,fwh,fz0
  839. real::ros_back,ros_wind,ros_slope,propx,propy,r
  840. if(.not.have_fuel_cats)call crash('write_fuels_m: fuel categories not yet set')
  841. fp%vx=>vx
  842. fp%vy=>vy
  843. fp%dzdxf=>dzdxf
  844. fp%dzdyf=>dzdyf
  845. fp%bbb=>bbb
  846. fp%phisc=>phisc
  847. fp%phiwc=>phiwc
  848. fp%r_0=>r_0
  849. fp%fgip=>fgip
  850. fp%ischap=>ischap
  851. fp%fmc_g=>fmc_g
  852. fp%nfuel_cat=>nfuel_cat
  853. iounit = open_text_file('fuels.m','write')
  854. 10 format('fuel(',i3,').',a,'=',"'",a,"'",';% ',a)
  855. do k=1,nfuelcats
  856. write(iounit,10)k,'fuel_name',trim(fuel_name(k)),'FUEL MODEL NAME'
  857. call write_var(k,'windrf',windrf(k),'WIND REDUCTION FACTOR FROM FCWH TO MIDFLAME HEIGHT' )
  858. call write_var(k,'fwh',fcwh(k),'WIND HEIGHT TO INTERPOLATE VERTICALLY TO (M)' )
  859. call write_var(k,'fz0',fcz0(k),'ROUGHNESS LENGTH FOR VERTICAL WIND LOG INTERPOLATION (M)' )
  860. call write_var(k,'fgi',fgi(k),'INITIAL TOTAL MASS OF SURFACE FUEL (KG/M**2)' )
  861. call write_var(k,'fueldepthm',fueldepthm(k),'FUEL DEPTH (M)')
  862. call write_var(k,'savr',savr(k),'FUEL PARTICLE SURFACE-AREA-TO-VOLUME RATIO, 1/FT')
  863. call write_var(k,'fuelmce',fuelmce(k),'MOISTURE CONTENT OF EXTINCTION')
  864. call write_var(k,'fueldens',fueldens(k),'OVENDRY PARTICLE DENSITY, LB/FT^3')
  865. call write_var(k,'st',st(k),'FUEL PARTICLE TOTAL MINERAL CONTENT')
  866. call write_var(k,'se',se(k),'FUEL PARTICLE EFFECTIVE MINERAL CONTENT')
  867. call write_var(k,'weight',weight(k),'WEIGHTING PARAMETER THAT DETERMINES THE SLOPE OF THE MASS LOSS CURVE')
  868. call write_var(k,'fci_d',fci_d(k),'INITIAL DRY MASS OF CANOPY FUEL')
  869. call write_var(k,'fct',fct(k),'BURN OUT TIME FOR CANOPY FUEL, AFTER DRY (S)')
  870. call write_var(k,'ichap',float(ichap(k)),'1 if chaparral, 0 if not')
  871. call write_var(k,'fci',fci(k),'INITIAL TOTAL MASS OF CANOPY FUEL')
  872. call write_var(k,'fcbr',fcbr(k),'FUEL CANOPY BURN RATE (KG/M**2/S)')
  873. call write_var(k,'adjr0',adjr0(k),'MULTIPLICATIVE ADJUSTMENT OF BACKING SPREAD RATE')
  874. call write_var(k,'adjrw',adjrw(k),'MULTIPLICATIVE ADJUSTMENT OF WIND CONTRIBUTION TO SPREAD RATE')
  875. call write_var(k,'adjrs',adjrs(k),'MULTIPLICATIVE ADJUSTMENT OF SLOPE CONTRIBUTION TO SPREAD RATE')
  876. call write_var(k,'ffw',ffw(k),'FUEL FRACTION CONSUMED IN THE FLAMING ZONE')
  877. call write_var(k,'hfgl',hfgl,'SURFACE FIRE HEAT FLUX THRESHOLD TO IGNITE CANOPY (W/m^2)')
  878. call write_var(k,'cmbcnst',cmbcnst,'JOULES PER KG OF DRY FUEL')
  879. call write_var(k,'fuelheat',fuelheat,'FUEL PARTICLE LOW HEAT CONTENT, BTU/LB')
  880. call write_var(k,'fuelmc_g',fuelmc_g,'FUEL PARTICLE (SURFACE) MOISTURE CONTENT')
  881. call write_var(k,'fuelmc_c',fuelmc_c,'FUEL PARTICLE (CANOPY) MOISTURE CONTENT')
  882. ! set up fuel arrays
  883. !subroutine set_fire_params( &
  884. ! ifds,ifde,jfds,jfde, &
  885. ! ifms,ifme,jfms,jfme, &
  886. ! ifts,ifte,jfts,jfte, &
  887. ! fdx,fdy,nfuel_cat0, &
  888. ! nfuel_cat,fuel_time, &
  889. ! fp )
  890. nfuel_cat = k
  891. do j=1,nsteps ! set moisture - must be before set_fire_params
  892. fmc_g(1,j)=fuelmc_g
  893. fmc_g(2,j)=fuelmc_g
  894. fmc_g(3,j)=(fuelmce(k)*(j-1))/(nsteps-2)
  895. enddo
  896. isave=fire_fmc_read
  897. fire_fmc_read=0
  898. call set_fire_params( &
  899. 1,3,1,nsteps, &
  900. 1,3,1,nsteps, &
  901. 1,3,1,nsteps, &
  902. 0.,0.,k, &
  903. nfuel_cat,fuel_time, &
  904. fp )
  905. fire_fmc_read=isave
  906. ! set up windspeed slope moisture table
  907. propx=1.
  908. propy=0.
  909. do j=1,nsteps
  910. r=float(j-1)/float(nsteps-1)
  911. ! line 1 varies windspeed (in x direction), zero slope
  912. wind(1,j)=maxwind*r
  913. vx(1,j)=wind(1,j)*windrf(k)
  914. vy(1,j)=0.
  915. dzdxf(1,j)=0.
  916. dzdyf(1,j)=0.
  917. ! line 2 varies slope (in x direction), zero slope
  918. vx(2,j)=0.
  919. vy(2,j)=0.
  920. dzdxf(2,j)=maxslope*r
  921. dzdyf(2,j)=0.
  922. ! line 3 varies moisture, zero slope and wind
  923. vx(3,j)=0.
  924. vy(3,j)=0.
  925. dzdxf(3,j)=0.
  926. dzdyf(3,j)=0.
  927. enddo
  928. do j=1,nsteps
  929. do i=1,3
  930. call fire_ros(ros_back,ros_wind,ros_slope, &
  931. propx,propy,i,j,fp)
  932. ros(i,j)=ros_back+ros_wind+ros_slope
  933. enddo
  934. write(iounit,13)k,'wind',j,wind(1,j),'wind speed at 6.1m'
  935. write(iounit,13)k,'ros_wind',j,ros(1,j),'rate of spread for the wind speed at 6.1m'
  936. write(iounit,13)k,'slope',j,dzdxf(2,j),'slope'
  937. write(iounit,13)k,'ros_slope',j,ros(2,j),'rate of spread for the slope'
  938. write(iounit,13)k,'fmc_g',j,fmc_g(3,j),'fuel moisture content'
  939. write(iounit,13)k,'ros_fmc_g',j,ros(3,j),'rate of spread for the fuel moisture content'
  940. enddo
  941. enddo
  942. 13 format('fuel(',i3,').',a,'(',i3,')=',g12.5e2,';% ',a)
  943. close(iounit)
  944. ! stop
  945. contains
  946. subroutine write_var(k,name,value,descr)
  947. ! write entry for one variable
  948. integer, intent(in)::k
  949. character(len=*), intent(in)::name,descr
  950. real, intent(in)::value
  951. write(iounit,11)k,name,value
  952. write(iounit,12)k,name,descr
  953. 11 format('fuel(',i3,').',a,'=',g12.5e2, ';')
  954. 12 format('fuel(',i3,').',a,"_descr='",a,"';")
  955. end subroutine write_var
  956. end subroutine write_fuels_m
  957. !
  958. !*******************
  959. !
  960. subroutine set_fire_params( &
  961. ifds,ifde,jfds,jfde, &
  962. ifms,ifme,jfms,jfme, &
  963. ifts,ifte,jfts,jfte, &
  964. fdx,fdy,nfuel_cat0, &
  965. nfuel_cat,fuel_time, &
  966. fp )
  967. implicit none
  968. !*** purpose: Set all fire model params arrays, constant values.
  969. !*** arguments
  970. integer, intent(in)::ifds,ifde,jfds,jfde ! fire domain bounds
  971. integer, intent(in)::ifts,ifte,jfts,jfte ! fire tile bounds
  972. integer, intent(in)::ifms,ifme,jfms,jfme ! memory array bounds
  973. real, intent(in):: fdx,fdy ! fire mesh spacing
  974. integer,intent(in)::nfuel_cat0 ! default fuel category, if nfuel_cat=0
  975. real, intent(inout),dimension(ifms:ifme, jfms:jfme)::nfuel_cat ! fuel data
  976. real, intent(out), dimension(ifms:ifme, jfms:jfme)::fuel_time ! fire params arrays
  977. type(fire_params),intent(inout)::fp
  978. !*** local
  979. real:: fuelload, fueldepth, rtemp1, rtemp2, &
  980. qig, epsilon, rhob, wn, betaop, e, c, &
  981. xifr, etas, etam, a, gammax, gamma, ratio, ir, &
  982. fuelloadm,fdxinv,fdyinv,betafl, bmst
  983. integer:: i,j,k
  984. integer::nerr
  985. character(len=128)::msg
  986. !*** executable
  987. if(.not.have_fuel_cats)call crash('set_fire_params: fuel categories not yet set')
  988. call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,fp%fmc_g,'set_fire_params: fmc_g')
  989. nerr=0
  990. do j=jfts,jfte
  991. do i=ifts,ifte
  992. ! fuel category
  993. k=int( nfuel_cat(i,j) )
  994. if(k.ge.no_fuel_cat.and.k.le.no_fuel_cat2)then ! no fuel
  995. fp%fgip(i,j)=0. ! no mass
  996. fp%ischap(i,j)=0.
  997. fp%phisc(i,j)=0. !
  998. fp%bbb(i,j)=1. !
  999. fuel_time(i,j)=7./0.85 ! does not matter, just what was there before
  1000. fp%phiwc(i,j)=0.
  1001. fp%r_0(i,j)=0. ! no fuel, no spread.
  1002. else
  1003. ! if(k.eq.0.and.nfuel_cat0.ge.1.and.nfuel_cat0.le.nfuelcats)then
  1004. ! ! replace k=0 by default
  1005. ! k=nfuel_cat0
  1006. ! nerr=nerr+1
  1007. ! endif
  1008. if(k.lt.1.or.k.gt.nfuelcats)then
  1009. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  1010. write(msg,'(3(a,i5))')'nfuel_cat(', i ,',',j,')=',k
  1011. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  1012. call message(msg)
  1013. if(k.eq.0)then
  1014. call message('Possibly nfuel_cat is uninitialized on input')
  1015. endif
  1016. call crash('set_fire_params: fuel category out of bounds')
  1017. endif
  1018. fuel_time(i,j)=weight(k)/0.85 ! cell based
  1019. ! do not understand calculations of stime in binit.m4
  1020. ! set fuel time constant: weight=1000=>40% decrease over 10 min
  1021. ! fuel decreases as exp(-t/fuel_time)
  1022. ! exp(-600*0.85/1000) = approx 0.6
  1023. fp%ischap(i,j)=ichap(k)
  1024. fp%fgip(i,j)=fgi(k)
  1025. if(fire_fmc_read.eq.1)then
  1026. fp%fmc_g(i,j)=fuelmc_g
  1027. endif
  1028. ! print *,'fmc_g:',fire_fmc_read,i,j,fp%fmc_g(i,j)
  1029. ! end jm addition
  1030. !
  1031. !*** rest copied from wf2_janice/fire_startup.m4 with minimal changes
  1032. !
  1033. ! ...Settings of fire spread parameters from Rothermel follows. These
  1034. ! don't need to be recalculated later.
  1035. bmst = fp%fmc_g(i,j) / (1.+fp%fmc_g(i,j))
  1036. fuelloadm= (1.-bmst) * fgi(k) ! fuelload without moisture
  1037. fuelload = fuelloadm * (.3048)**2 * 2.205 ! to lb/ft^2
  1038. fueldepth = fueldepthm(k)/0.3048 ! to ft
  1039. betafl = fuelload/(fueldepth * fueldens(k))! packing ratio
  1040. betaop = 3.348 * savr(k)**(-0.8189) ! optimum packing ratio
  1041. qig = 250. + 1116.*fp%fmc_g(i,j) ! heat of preignition, btu/lb
  1042. epsilon = exp(-138./savr(k) ) ! effective heating number
  1043. rhob = fuelload/fueldepth ! ovendry bulk density, lb/ft^3
  1044. c = 7.47 * exp( -0.133 * savr(k)**0.55) ! const in wind coef
  1045. fp%bbb(i,j) = 0.02526 * savr(k)**0.54 ! const in wind coef
  1046. !if(fire_wind_log_interp .eq. 4 .or. fire_use_windrf .eq. 1) then
  1047. ! c = c * windrf(k)**fp%bbb(i,j) ! jm: multiply wind by reduction factor
  1048. !endif
  1049. e = 0.715 * exp( -3.59e-4 * savr(k)) ! const in wind coef
  1050. fp%phiwc(i,j) = c * (betafl/betaop)**(-e)
  1051. ! phis = 5.275 *(fp%betafl(i,j))**(-0.3) *tanphim**2 ! slope factor
  1052. fp%phisc(i,j) = 5.275 *(betafl)**(-0.3) ! const in slope coeff
  1053. rtemp2 = savr(k)**1.5
  1054. gammax = rtemp2/(495. + 0.0594*rtemp2) ! maximum rxn vel, 1/min
  1055. a = 1./(4.774 * savr(k)**0.1 - 7.27) ! coef for optimum rxn vel
  1056. ratio = betafl/betaop
  1057. gamma = gammax *(ratio**a) *exp(a*(1.-ratio)) !optimum rxn vel, 1/min
  1058. wn = fuelload/(1 + st(k)) ! net fuel loading, lb/ft^2
  1059. rtemp1 = fp%fmc_g(i,j)/fuelmce(k)
  1060. etam = 1.-2.59*rtemp1 +5.11*rtemp1**2 -3.52*rtemp1**3 !moist damp coef
  1061. etam = max(etam,0.0)
  1062. etas = 0.174* se(k)**(-0.19) ! mineral damping coef
  1063. ir = gamma * wn * fuelheat * etam * etas !rxn intensity,btu/ft^2 min
  1064. ! jm irm = ir * 1055./( 0.3048**2 * 60.) * 1.e-6 !for mw/m^2
  1065. ! jm: irm set but never used??
  1066. xifr = exp( (0.792 + 0.681*savr(k)**0.5) &
  1067. * (betafl+0.1)) /(192. + 0.2595*savr(k)) ! propagating flux ratio
  1068. ! ... r_0 is the spread rate for a fire on flat ground with no wind.
  1069. fp%r_0(i,j) = ir*xifr/(rhob * epsilon *qig) ! default spread rate in ft/min
  1070. fp%r_0(i,j) = fp%r_0(i,j) * .00508 ! convert to m/s
  1071. fp%phiwc(i,j) = fp%phiwc(i,j) * fp%r_0(i,j) ! premultiply wind coefficient so it can be used additively
  1072. fp%phisc(i,j) = fp%phisc(i,j) * fp%r_0(i,j) ! premultiply wind coefficient so it can be used additively
  1073. ! apply adjustments
  1074. fp%r_0(i,j) = fp%r_0(i,j) * adjr0(k)
  1075. fp%phiwc(i,j) = fp%phiwc(i,j) * adjrw(k)
  1076. fp%phisc(i,j) = fp%phisc(i,j) * adjrs(k)
  1077. ! test fmc
  1078. if(fp%r_0(i,j) > 1e-6 .and. fp%fmc_g(i,j) > fuelmce(k))then
  1079. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  1080. write(msg,'(a,2i6,3(a,e14.6))') 'set_fire_params: at ',i,j,' base rate of spread',fp%r_0(i,j),' moisture ',fp%fmc_g(i,j),'> extinction =',fuelmce(k)
  1081. call message(msg,level=0)
  1082. write(msg,'(5(a,e14.6))')'rtemp1=',rtemp1,' etam=',etam
  1083. call message(msg,level=0)
  1084. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  1085. call warning('propagation above extinction moisture',level=0)
  1086. endif
  1087. endif
  1088. enddo
  1089. enddo
  1090. if(nerr.gt.1)then
  1091. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  1092. write(msg,'(a,i6,a)')'set_fire_params: WARNING: fuel category 0 replaced in',nerr,' cells'
  1093. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  1094. call message(msg)
  1095. endif
  1096. end subroutine set_fire_params
  1097. !
  1098. !*******************
  1099. !
  1100. subroutine heat_fluxes(dt,fp, &
  1101. ifms,ifme,jfms,jfme, & ! memory dims
  1102. ifts,ifte,jfts,jfte, & ! tile dims
  1103. iffs,iffe,jffs,jffe, & ! fuel_frac_burnt dims
  1104. fgip,fuel_frac_burnt, & !in
  1105. grnhft,grnqft) !out
  1106. implicit none
  1107. !*** purpose
  1108. ! compute the heat fluxes on the fire grid cells
  1109. !*** arguments
  1110. type(fire_params), intent(in)::fp
  1111. real, intent(in)::dt ! dt the fire time step (the fire model advances time by this)
  1112. integer, intent(in)::ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,iffs,iffe,jffs,jffe ! dimensions
  1113. real, intent(in),dimension(ifms:ifme,jfms:jfme):: fgip
  1114. real, intent(in),dimension(iffs:iffe,jffs:jffe):: fuel_frac_burnt
  1115. real, intent(out),dimension(ifms:ifme,jfms:jfme):: grnhft
  1116. real, intent(out),dimension(ifms:ifme,jfms:jfme),optional:: grnqft
  1117. !*** local
  1118. integer::i,j
  1119. real:: dmass,bmst
  1120. logical::latent
  1121. !*** executable
  1122. latent = present(grnqft)
  1123. do j=jfts,jfte
  1124. do i=ifts,ifte
  1125. dmass = & ! ground fuel dry mass burnt this call (kg/m^2)
  1126. fgip(i,j) & ! init mass from fuel model no (kg/m^2) = fgi(nfuel_cat(i,j)
  1127. * fuel_frac_burnt(i,j) ! fraction burned this call (1)
  1128. bmst = fp%fmc_g(i,j)/(1.+fp%fmc_g(i,j))
  1129. grnhft(i,j) = (dmass/dt)*(1.-bmst)*cmbcnst ! J/m^2/sec
  1130. if(latent)grnqft(i,j) = (bmst+(1.-bmst)*.56)*(dmass/dt)*xlv !
  1131. ! bmst = relative water contents; 0.56 = est. ratio of water from burning
  1132. ! xlv = nominal specific latent heat of water J/kg (dependence on temperature ignored)
  1133. ! xlv is defined in module_model_constants
  1134. enddo
  1135. enddo
  1136. end subroutine heat_fluxes
  1137. !
  1138. !**********************
  1139. !
  1140. subroutine set_nfuel_cat( &
  1141. ifms,ifme,jfms,jfme, &
  1142. ifts,ifte,jfts,jfte, &
  1143. ifuelread,nfuel_cat0,zsf,nfuel_cat)
  1144. implicit none
  1145. ! set fuel distributions for testing
  1146. integer, intent(in):: ifts,ifte,jfts,jfte, &
  1147. ifms,ifme,jfms,jfme
  1148. integer, intent(in)::ifuelread,nfuel_cat0
  1149. real, intent(in), dimension(ifms:ifme, jfms:jfme)::zsf
  1150. real, intent(out), dimension(ifms:ifme, jfms:jfme)::nfuel_cat
  1151. !*** local
  1152. ! parameters to control execution
  1153. integer:: i,j,iu1
  1154. real:: t1
  1155. character(len=128)msg
  1156. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  1157. write(msg,'(a,i3)')'set_nfuel_cat: ifuelread=',ifuelread
  1158. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  1159. call message(msg)
  1160. if (ifuelread .eq. -1 .or. ifuelread .eq. 2) then
  1161. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  1162. call message('set_nfuel_cat: assuming nfuel_cat initialized already')
  1163. call message(msg)
  1164. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  1165. else if (ifuelread .eq. 0) then
  1166. !
  1167. do j=jfts,jfte
  1168. do i=ifts,ifte
  1169. nfuel_cat(i,j)=real(nfuel_cat0)
  1170. enddo
  1171. enddo
  1172. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  1173. write(msg,'(a,i3)')'set_nfuel_cat: fuel initialized with category',nfuel_cat0
  1174. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  1175. call message(msg)
  1176. else if (ifuelread .eq. 1) then
  1177. !
  1178. ! make dependent on altitude (co mountains/forest vs. plains)
  1179. ! 2000 m : 6562 ft ; 1600 m: 5249 ft
  1180. ! ... user defines fuel category spatial variability ! param!
  1181. do j=jfts,jfte
  1182. do i=ifts,ifte
  1183. ! nfuel_cat(i,j)= 2 ! grass with understory ! jm does nothing
  1184. !jm t1=zsf(i,j)*slngth/100.
  1185. t1 = zsf(i,j) ! this is in m
  1186. if(t1.le.1524.)then ! up to 5000 ft
  1187. nfuel_cat(i,j)= 3 ! tall grass
  1188. else if(t1.ge.1524. .and. t1.le.2073.)then ! 5.0-6.8 kft.
  1189. nfuel_cat(i,j)= 2 ! grass with understory
  1190. else if(t1.ge.2073..and.t1.le.2438.)then ! 6.8-8.0 kft.
  1191. nfuel_cat(i,j)= 8 ! timber litter - 10 (ponderosa)
  1192. else if(t1.gt.2438. .and. t1.le. 3354.) then ! 8.0-11.0 kft.
  1193. ! ... could also be mixed conifer.
  1194. nfuel_cat(i,j)= 10 ! timber litter - 8 (lodgepole)
  1195. else if(t1.gt.3354. .and. t1.le. 3658.) then ! 11.0-12.0 kft
  1196. nfuel_cat(i,j)= 1 ! alpine meadow - 1
  1197. else if(t1.gt.3658. ) then ! > 12.0 kft
  1198. nfuel_cat(i,j)= 14 ! no fuel.
  1199. endif
  1200. enddo
  1201. enddo
  1202. call message('set_nfuel_cat: fuel initialized by altitude')
  1203. else
  1204. call crash('set_nfuel_cat: bad ifuelread')
  1205. endif
  1206. ! .............end load fuel categories (or constant) here.
  1207. end subroutine set_nfuel_cat
  1208. !
  1209. !**********************
  1210. !
  1211. real function fire_rate_of_spread(propx, propy, i,j,fp)
  1212. ! compute rate of spread at grid node (i,j) in the direction (dx,dy)
  1213. implicit none
  1214. !***arguments
  1215. real, intent(in)::propx, propy! direction, need not be normalized
  1216. integer, intent(in)::i,j ! node mesh coordinates
  1217. type(fire_params),intent(in)::fp
  1218. !*** local
  1219. real:: ros_back,ros_wind,ros_slope,nvx,nvy,scale,rr
  1220. !*** executable
  1221. scale=sqrt(propx*propx+propy*propy)
  1222. if (.not. scale > 0.) scale =1.
  1223. nvx=propx/scale
  1224. nvy=propy/scale
  1225. call fire_ros(ros_back,ros_wind,ros_slope, nvx,nvy,i,j,fp)
  1226. rr = ros_back+ros_wind+ros_slope
  1227. if(fire_grows_only.gt.0)rr=max(rr,0.)
  1228. fire_rate_of_spread = rr
  1229. end function fire_rate_of_spread
  1230. subroutine fire_ros(ros_back,ros_wind,ros_slope, &
  1231. propx,propy,i,j,fp)
  1232. implicit none
  1233. ! compute the wind speed and slope normal to the fireline and call fire_ros_cawfe
  1234. !*** arguments
  1235. real, intent(out)::ros_back,ros_wind,ros_slope ! rate of spread: backing, due to wind, due to slope
  1236. real, intent(in)::propx,propy
  1237. integer, intent(in)::i,j ! node mesh coordinates
  1238. type(fire_params),intent(in)::fp
  1239. !*** local
  1240. real:: speed, tanphi ! windspeed and slope in the directino normal to the fireline
  1241. real::cor_wind,cor_slope,nvx,nvy,scale
  1242. !*** executable
  1243. ! make sure normal direction is size 1
  1244. !scale=sqrt(propx*propx+propy*propy)+tiny(scale)
  1245. scale=1.
  1246. nvx=propx/scale
  1247. nvy=propy/scale
  1248. if (fire_advection.ne.0) then ! from flags in module_fr_sfire_util
  1249. ! wind speed is total speed
  1250. speed = sqrt(fp%vx(i,j)*fp%vx(i,j)+ fp%vy(i,j)*fp%vy(i,j))+tiny(speed)
  1251. ! slope is total slope
  1252. tanphi = sqrt(fp%dzdxf(i,j)*fp%dzdxf(i,j) + fp%dzdyf(i,j)*fp%dzdyf(i,j))+tiny(tanphi)
  1253. ! cos of wind and spread, if >0
  1254. cor_wind = max(0.,(fp%vx(i,j)*nvx + fp%vy(i,j)*nvy)/speed)
  1255. ! cos of slope and spread, if >0
  1256. cor_slope = max(0., (fp%dzdxf(i,j)*nvx + fp%dzdyf(i,j)*nvy)/tanphi)
  1257. else
  1258. ! wind speed in spread direction
  1259. speed = fp%vx(i,j)*nvx + fp%vy(i,j)*nvy
  1260. ! slope in spread direction
  1261. tanphi = fp%dzdxf(i,j)*nvx + fp%dzdyf(i,j)*nvy
  1262. cor_wind=1.
  1263. cor_slope=1.
  1264. endif
  1265. call fire_ros_cawfe(ros_back,ros_wind,ros_slope, &
  1266. speed,tanphi,cor_wind,cor_slope,i,j,fp)
  1267. end subroutine fire_ros
  1268. !
  1269. !***
  1270. !
  1271. subroutine fire_ros_cawfe(ros_back,ros_wind,ros_slope, &
  1272. speed,tanphi,cor_wind,cor_slope,i,j,fp)
  1273. implicit none
  1274. ! find rate of spread from wind speed and slope
  1275. ! copied from wf2_janice
  1276. ! with the following changes ONLY:
  1277. ! 0.5*(speed + abs(speed)) -> max(speed,0.)
  1278. ! index l -> j
  1279. ! took out some prints
  1280. ! argument fuelloadm never used??
  1281. ! not using nfuel_cat here - cell info was put into arrays passed as arguments
  1282. ! in include file to avoid transcription errors when used elsewhere
  1283. ! betaop is absorbed in phiwc, see module_fr_sfire_model/fire_startup
  1284. ! return the backing, wind, and slope contributions to the rate of spread separately
  1285. ! because they may be needed to take advantage of known wind and slope vectors.
  1286. ! They should add up to get the total rate of spread.
  1287. !ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
  1288. ! ... calculates fire spread rate with mcarthur formula or Rothermel
  1289. ! using fuel type of fuel cell
  1290. !
  1291. !
  1292. ! m/s =(ft/min) *.3048/60. =(ft/min) * .00508 ! conversion rate
  1293. ! ft/min = m/s * 2.2369 * 88. = m/s * 196.850 ! conversion rate
  1294. !
  1295. !ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
  1296. !*** arguments
  1297. real, intent(out)::ros_back,ros_wind,ros_slope ! rate of spread: backing, due to wind, due to slope
  1298. real, intent(in)::speed,tanphi,cor_wind,cor_slope
  1299. integer, intent(in)::i,j ! node mesh coordinates
  1300. type(fire_params),intent(in)::fp
  1301. !*** local
  1302. real:: umid, phis, phiw, spdms, umidm, excess, tanphim,ros
  1303. real, parameter::ros_max=6.
  1304. character(len=128)msg
  1305. integer::k
  1306. !*** executable
  1307. if (.not. fp%ischap(i,j) > 0.) then ! if not chaparral, do not test for .eq. 0 for speed
  1308. if (ibeh .eq. 1) then ! use Rothermel formula
  1309. ! ... if wind is 0 or into fireline, phiw = 0, &this reduces to backing ros.
  1310. spdms = max(speed,0.) !
  1311. umidm = min(spdms,30.) ! max input wind spd is 30 m/s !param!
  1312. umid = umidm * 196.850 ! m/s to ft/min
  1313. ! eqn.: phiw = c * umid**bbb(i,j) * (fp%betafl(i,j)/betaop)**(-e) ! wind coef
  1314. ros_wind = fp%phiwc(i,j) * (umid**fp%bbb(i,j)) ! wind coef
  1315. tanphim=max(tanphi,0.0)
  1316. tanphim=min(tanphim,5.0) ! jm
  1317. ! phis = 5.275 *(fp%betafl(i,j))**(-0.3) *tanphim**2 ! slope factor
  1318. ros_slope = fp%phisc(i,j) *tanphim**2 ! slope factor
  1319. ! rosm = fp%r_0(i,j)*(1. + phiw + phis) * .00508 ! spread rate, m/s
  1320. ros_back = fp%r_0(i,j)
  1321. elseif(ibeh.eq.2)then ! for testing only, spread rate = wind but not < 0
  1322. ros_back = 0.
  1323. ros_wind = max(speed,0.)
  1324. ros_slope= 0.
  1325. elseif(ibeh.eq.3)then ! for testing only, spread rate = wind but not < 0
  1326. ros_back = 0.
  1327. ros_wind = speed
  1328. ros_slope= 0.
  1329. elseif(ibeh.eq.0)then ! MacArthur formula (Australian)
  1330. ! rosm = 0.18*exp(0.8424*max(speed,0.))
  1331. ros_back = 0.18
  1332. ros_wind = 0.18*exp(0.8424*max(speed,0.)) - ros_back
  1333. ros_slope =0.
  1334. ! note: ros = ros_back + ros_wind + ros_slope
  1335. else ! error, but prevent unintialized variables
  1336. ! just so that there is something there
  1337. ros_back=-999.
  1338. ros_wind=-999.
  1339. ros_slope=-999.
  1340. endif
  1341. k = int(fp%nfuel_cat(i,j))
  1342. ros=ros_back+ros_wind+ros_slope
  1343. if(ros > 1e-6 .and. fp%fmc_g(i,j) > fuelmce(k))then
  1344. !$OMP CRITICAL(SFIRE_PHYS_CRIT)
  1345. write(msg,'(a,2i6,3(a,e13.5))') 'WARNING: fire_ros_cawfe: at ',i,j,' rate of spread',ros,' moisture ',fp%fmc_g(i,j),'> extinction =',fuelmce(k)
  1346. !$OMP END CRITICAL(SFIRE_PHYS_CRIT)
  1347. call warning(msg)
  1348. endif
  1349. !
  1350. else ! chaparral model from Clark et al 2004
  1351. ! .... spread rate has no dependency on fuel character, only windspeed.
  1352. spdms = max(speed,0.)
  1353. ! rosm = 1.2974 * spdms**1.41 ! spread rate, m/s
  1354. ! note: backing ros is 0 for chaparral without setting nozero value below
  1355. !sp_n=.03333
  1356. ! chaparral backing fire spread rate 0.033 m/s ! param!
  1357. !rosm= max(rosm, sp_n) ! no less than backing r.o.s.
  1358. ros_back=.03333 ! chaparral backing fire spread rate 0.033 m/s ! param!
  1359. ros_wind = 1.2974 * spdms**1.41 ! spread rate, m/s
  1360. ros_wind = max(ros_wind, ros_back)-ros_back
  1361. ros_slope =0.
  1362. endif
  1363. ! multiply by the correction factors (from angle calculations)
  1364. ros_wind=ros_wind*cor_wind
  1365. ros_slope=ros_slope*cor_slope
  1366. !
  1367. ! ----------note! put an 6 m/s cap on max spread rate -----------
  1368. ! rosm= min(rosm, 6.) ! no faster than this cap ! param !
  1369. excess = ros_back + ros_wind + ros_slope - ros_max
  1370. if (excess > 0.)then
  1371. ! take it out of wind and slope in proportion
  1372. ros_wind = ros_wind - excess*ros_wind/(ros_wind+ros_slope)
  1373. ros_slope = ros_slope - excess*ros_slope/(ros_wind+ros_slope)
  1374. endif
  1375. ! ... to rescale to veloc. carried by model, mult x (svel*snorm(1,3))= .1
  1376. !jm: huh ???
  1377. ! fire_ros = 0.1*rosm
  1378. !
  1379. !write(msg,*)i,j,' speed=',speed,' tanphi',tanphi,' ros=',ros_back,ros_wind,ros_slope
  1380. !call message(msg)
  1381. return
  1382. contains
  1383. real function nrm2(u,v)
  1384. real, intent(in)::u,v
  1385. nrm2=sqrt(u*u+v*v)
  1386. end function nrm2
  1387. end subroutine fire_ros_cawfe
  1388. subroutine fire_risk(fp, &
  1389. ifms,ifme,jfms,jfme, & ! memory dims
  1390. ifts,ifte,jfts,jfte, & ! tile dims
  1391. nfuel_cat, &
  1392. f_ros0,f_rosx,f_rosy,f_ros, & ! fire spread diagnostic variables
  1393. f_int,f_lineint,f_lineint2) ! fireline intensities for danger rating
  1394. !*** arguments
  1395. type(fire_params), intent(in)::fp
  1396. integer, intent(in):: &
  1397. ifms,ifme,jfms,jfme, & ! memory dims
  1398. ifts,ifte,jfts,jfte ! tile dims
  1399. real, intent(in), dimension(ifms:ifme,jfms:jfme) :: nfuel_cat
  1400. real, intent(out), dimension(ifms:ifme,jfms:jfme) :: &
  1401. f_ros0,f_rosx,f_rosy,f_ros, & ! fire spread diagnostic variables
  1402. f_int,f_lineint,f_lineint2 ! fire intensities for danger rating
  1403. !*** local
  1404. integer:: i,j,k
  1405. real:: cor_wind=1.,cor_slope=1.,dt_fake=1.
  1406. real:: ros_back,ros_wind,ros_slope,speed,tanphi,front_speed,ros_x,ros_y
  1407. !*** executable
  1408. do j=jfts,jfte
  1409. do i=ifts,ifte
  1410. ! compute the fire spread rate and vector
  1411. ! wind speed is total speed
  1412. speed = sqrt(fp%vx(i,j)*fp%vx(i,j)+ fp%vy(i,j)*fp%vy(i,j))+tiny(speed)
  1413. ! slope is total slope
  1414. tanphi = sqrt(fp%dzdxf(i,j)*fp%dzdxf(i,j) + fp%dzdyf(i,j)*fp%dzdyf(i,j))+tiny(tanphi)
  1415. call fire_ros_cawfe(ros_back,ros_wind,ros_slope, &
  1416. speed,tanphi,cor_wind,cor_slope,i,j,fp)
  1417. ros_x = ros_wind * fp%vx(i,j)/speed + ros_slope * fp%dzdxf(i,j)/tanphi ! x direction component
  1418. ros_y = ros_wind * fp%vy(i,j)/speed + ros_slope * fp%dzdyf(i,j)/tanphi ! y direction component
  1419. ! store to out
  1420. f_ros0(i,j) = ros_back ! direction-less spread rate component
  1421. f_rosx(i,j) = ros_x
  1422. f_rosy(i,j) = ros_y
  1423. ! max fire front speed in this location (m/s)
  1424. f_ros(i,j) = ros_back + sqrt(ros_x*ros_x + ros_y*ros_y)
  1425. enddo
  1426. enddo
  1427. call fire_intensity(fp, & ! fuel properties
  1428. ifms,ifme,jfms,jfme, & ! memory dims
  1429. ifts,ifte,jfts,jfte, & ! tile dims
  1430. ifms,ifme,jfms,jfme, & ! f_ros dims
  1431. f_ros,nfuel_cat, & !in
  1432. f_lineint,f_lineint2,f_int) ! fireline intensities out
  1433. end subroutine fire_risk
  1434. !
  1435. !***
  1436. !
  1437. subroutine fire_intensity(fp, & ! fuel params
  1438. ifms,ifme,jfms,jfme, & ! memory dims
  1439. ifts,ifte,jfts,jfte, & ! tile dims
  1440. irms,irme,jrms,jrme, & ! memory dims for ros
  1441. ros,nfuel_cat, & ! rate of spread in
  1442. fibyram,filimit,f_int) ! intensities out
  1443. !*** arguments
  1444. type(fire_params), intent(in)::fp
  1445. integer, intent(in):: &
  1446. ifms,ifme,jfms,jfme, & ! memory dims
  1447. ifts,ifte,jfts,jfte, & ! tile dims
  1448. irms,irme,jrms,jrme ! memory dims for ros
  1449. real, intent(in), dimension(irms:irme,jrms:jrme) :: ros ! in rate of spread
  1450. real, intent(in), dimension(ifms:ifme,jfms:jfme) :: nfuel_cat
  1451. real, intent(out), dimension(ifms:ifme,jfms:jfme) :: &
  1452. fibyram,filimit ! out fireline intensities
  1453. real, intent(out), dimension(ifms:ifme,jfms:jfme), optional :: f_int ! fire intensity (J/m^2/s)
  1454. !*** local
  1455. integer:: i,j,k
  1456. real, dimension(ifts:ifte,jfts:jfte):: rate_frac
  1457. real:: dt_fake=1.
  1458. !*** executable
  1459. call heat_fluxes(dt_fake,fp, &
  1460. ifms,ifme,jfms,jfme, & ! memory dims
  1461. ifts,ifte,jfts,jfte, & ! tile dims
  1462. irms,irme,jrms,jrme, & ! ros dims
  1463. fp%fgip,ros, & !in
  1464. fibyram) !out
  1465. ! multiply by fuel fraction consumed in flaming zone for the category
  1466. do j=jfts,jfte
  1467. do i=ifts,ifte
  1468. k=int( nfuel_cat(i,j) )
  1469. fibyram(i,j)=fibyram(i,j)*ffw(k)
  1470. enddo
  1471. enddo
  1472. ! fuel fraction loss per fire front unit length traveled per unit time
  1473. ! burn_rate(i,j) = 0.5 * front_speed / fp%fuel_time(i,j)
  1474. ! fireline element of length ds moves in time dt by front_speed * dt covering area ds * dt * front_speed (m^2)
  1475. ! after time dt the fuel fraction decrease is 0 at the leading edge and dt/fuel_time at the trailing edge
  1476. ! so the average fuel consumption over this zone is 0.5 * dt/fuel_time (1)
  1477. ! fuel load is fgip (kg/m^2)
  1478. ! and the amount of fuel burned fireline length ds travels over time dt is 0.5 fgip * ds * dt^2 * front_speed/fuel_time (kg)
  1479. ! note: dt because 1. it travels more 2. it burns longer
  1480. ! fgip*burn_rate_frac(i,j) = (kg/m^2) * (m/s^2) = kg/m/s^2
  1481. ! http://www.forestencyclopedia.net/p/p487
  1482. ! H = I*w*r = (J/kg) * (kg/m^2) * (m/s) = J/m/s
  1483. ! fuel fraction loss per fire front unit length traveled
  1484. do j=jfts,jfte
  1485. do i=ifts,ifte
  1486. rate_frac(i,j)=0.5*ros(i,j)/fp%fuel_time(i,j)
  1487. enddo
  1488. enddo
  1489. ! multiply by heat contents * fuel load
  1490. call heat_fluxes(dt_fake,fp, &
  1491. ifms,ifme,jfms,jfme, & ! memory dims
  1492. ifts,ifte,jfts,jfte, & ! tile dims
  1493. ifts,ifte,jfts,jfte, & ! rate_frac dims
  1494. fp%fgip,rate_frac, & !in
  1495. filimit) !out
  1496. if(present(f_int))then
  1497. do j=jfts,jfte
  1498. do i=ifts,ifte
  1499. k=int( nfuel_cat(i,j) )
  1500. ! in time tr the fraction of fuel consumed is ffw=1-exp(-tr/fuel_time)
  1501. ! giving flame residence time tr = - log(1-ffw)*fuel_time
  1502. ! fire intensity is heat contents * fuel load * fraction consumed / flame residence time
  1503. ! J/kg * kg/m^2 * 1 / s = J/m^2/s
  1504. ! this is averaged over the flame residence time,
  1505. rate_frac(i,j)=ffw(k)/(fp%fuel_time(i,j)*(-log(1.-ffw(k))))
  1506. enddo
  1507. enddo
  1508. ! multiply by heat contents * fuel load J/m^2
  1509. call heat_fluxes(dt_fake,fp, &
  1510. ifms,ifme,jfms,jfme, & ! memory dims
  1511. ifts,ifte,jfts,jfte, & ! tile dims
  1512. ifts,ifte,jfts,jfte, & ! rate_frac dims
  1513. fp%fgip,rate_frac, & !in
  1514. f_int) !out
  1515. endif
  1516. end subroutine fire_intensity
  1517. !*** executable
  1518. end module module_fr_sfire_phys