4 module module_fr_sfire_model
6 use module_fr_sfire_core
7 use module_fr_sfire_util
8 use module_fr_sfire_phys
14 subroutine sfire_model ( &
15 id, & ! unique number for prints and debug
16 ifun, & ! what to do see below
17 restart,replay, & ! use existing state, prescribe spread
18 run_fuel_moisture, & ! if need update fuel moisture in pass 4
19 ifuelread,nfuel_cat0, & ! initialize fuel categories
20 ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
21 ifms,ifme,jfms,jfme, & ! fire memory dims - how declared
22 ifps,ifpe,jfps,jfpe, & ! patch - nodes owned by this process
23 ifts,ifte,jfts,jfte, & ! fire tile dims - this thread
24 time_start,dt, & ! time and increment
25 fdx,fdy, & ! fire mesh spacing,
26 ignition,hfx, & ! small array of ignition line descriptions
27 coord_xf,coord_yf, & ! fire mesh coordinates
28 fire_hfx, & ! input: given heat flux, or set inside
29 tign_in, & ! ignition time, if given
30 lfn,lfn_out,tign,fuel_frac,fire_area, & ! state: level function, ign time, fuel left, area burning
31 fuel_frac_burnt, & ! output: fuel fraction burnt in this timestep
32 fgrnhfx,fgrnqfx, & ! output: heat fluxes
33 ros,flineint,flineint2, & ! diagnostic variables
34 f_ros0,f_rosx,f_rosy,f_ros, & ! fire risk spread
35 f_int,f_lineint,f_lineint2, & ! fire risk intensities
36 nfuel_cat, & ! fuel data per point
37 fuel_time,fwh,fz0, & ! save derived internal data
41 ! This subroutine implements the fire spread model.
42 ! All quantities are on the fire grid. It inputs
43 ! winds given on the nodes of the fire grid
44 ! and outputs the heat fluxes on the cells of the fire grid.
45 ! This subroutine has no knowledge of any atmospheric model.
46 ! This code was written to conform with the WRF parallelism model, however it
47 ! does not depend on it. It can be called with domain equal to tile.
48 ! Wind and height must be given on 1 more node beyond the domain bounds.
49 ! The subroutine changes only array entries of the arguments in the tile.
50 ! Upon exit with ifun=2 (time step), lfn_out is to be copied into lfn by the caller.
51 ! When this subroutine is used on separate tiles that make a domain the value, the
52 ! it uses lfn on a strip of width 2 from neighboring tiles.
54 ! All computation is done on one tile.
56 ! This subroutine is intended to be called in a loop like
59 ! do ifun=1,6 (if initizalize run, otherwise 3,6)
60 ! start parallel loop over tiles
61 ! if ifun=1, set z and fuel data
62 ! if ifun=3, set the wind arrays
63 ! call sfire_model(....)
64 ! end parallel loop over tiles
68 ! halo exchange on z width 2
69 ! halo exchange on fuel data width 1
72 ! if ifun=3, halo exchange on winds width 2
81 integer, intent(in) :: id
82 integer, intent(in) :: ifun ! 1 = initialize run pass 1
83 ! 2 = initialize run pass 2
84 ! 3 = initialize timestep
86 ! 5 = copy timestep output to input
87 ! 6 = compute output fluxes
88 logical, intent(in):: restart ! if true, use existing state
89 logical, intent(in):: replay ! if true, use tign_g for level set
90 logical, intent(in)::run_fuel_moisture !
92 integer, intent(in) :: ifuelread,nfuel_cat0 ! for set_fire_params
93 integer, intent(in) :: ifds,ifde,jfds,jfde,& ! fire domain bounds
94 ifps,ifpe,jfps,jfpe ! patch - nodes owned by this process
95 integer, intent(in) :: ifts,ifte,jfts,jfte ! fire tile bounds
96 integer, intent(in) :: ifms,ifme,jfms,jfme ! fire memory array bounds
97 REAL,INTENT(in) :: time_start,dt ! starting time, time step
98 REAL,INTENT(in) :: fdx,fdy ! spacing of the fire mesh
100 type(lines_type), intent(in):: ignition,hfx ! descriptions of ignition lines and hfx lines
101 real, dimension(ifms:ifme, jfms:jfme), intent(in):: &
102 coord_xf,coord_yf ! node coordinates
103 real, dimension(ifms:ifme, jfms:jfme), intent(inout):: &
104 fire_hfx ! given heat flux
106 real, dimension(ifms:ifme, jfms:jfme), intent(inout):: tign_in ! given ignition times
108 REAL, INTENT(inout), dimension(ifms:ifme,jfms:jfme):: &
109 lfn , & ! level function: fire is where lfn<0 (node)
110 tign , & ! absolute time of ignition (node)
111 fuel_frac ! fuel fraction (node), currently redundant
113 REAL, INTENT(out), dimension(ifms:ifme,jfms:jfme):: &
114 fire_area, & ! fraction of each cell burning
115 fuel_frac_burnt ! fuel fraction burned in this timestep
118 REAL, INTENT(out), dimension(ifms:ifme,jfms:jfme):: &
120 fgrnhfx,fgrnqfx, & ! heat fluxes J/m^2/s (cell)
121 ros,flineint,flineint2, & ! diagnostic variables
122 f_ros0,f_rosx,f_rosy,f_ros, & ! fire risk spread
123 f_int,f_lineint,f_lineint2 ! fire risk intensities
126 ! constant arrays - set at initialization
127 real, intent(inout), dimension(ifms:ifme, jfms:jfme)::nfuel_cat ! cell based, data, constant
128 real,intent(inout),dimension(ifms:ifme,jfms:jfme):: fuel_time,fwh,fz0
129 type(fire_params),intent(inout)::fp
133 integer :: xifms,xifme,xjfms,xjfme ! memory bounds for pass-through arguments to normal spread
134 real, dimension(ifts:ifte,jfts:jfte)::fuel_frac_end
135 integer::ignited,ig,i,j,itso,iteo,jtso,jteo
136 real::tbound,err,erri,errj,maxgrad,grad,tfa,thf,mhf,tqf,mqf,aw,mw,t
137 character(len=128)::msg
138 logical:: freeze_fire
139 real::fireline_mask=0.
143 call check_mesh_2dim(ifts-1,ifte+1,jfts-1,jfte+1,ifms,ifme,jfms,jfme)
145 xifms=ifms ! dimensions for the include file
152 freeze_fire = fire_hfx_given .ne. 0
155 if(ifun.eq.1)then ! do nothing, init pass 1 is outside only
157 !! done in driver now
158 ! call init_fuel_cats ! initialize fuel subsystem
161 ! when starting replay, recompute lfn and fuel_frac
162 call message('replay, setting the level set function',level=1)
165 lfn(i,j) = tign(i,j) - time_start ! <0 if burning at the end of the time step
168 call check_lfn_tign('replay, lfn from tign',time_start,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,tign)
170 elseif(ifun.eq.2)then
171 ! initialize all arrays that the model will not change later
173 ! assuming halo on zsf done
174 ! extrapolate on 1 row of cells beyond the domain boundary
175 ! including on the halo regions
177 call continue_at_boundary(1,1,0., & ! do x direction or y direction
178 ifms,ifme,jfms,jfme, & ! memory dims
179 ifds,ifde,jfds,jfde, & ! domain dims
180 ifps,ifpe,jfps,jfpe, & ! patch dims - winds defined up to +1
181 ifts,ifte,jfts,jfte, & ! tile dims
182 itso,iteo,jtso,jteo, & ! where set now
185 ! compute the gradients once for all
190 erri = fp%dzdxf(i,j) - (fp%zsf(i+1,j)-fp%zsf(i-1,j))/(2.*fdx)
191 errj = fp%dzdyf(i,j) - (fp%zsf(i,j+1)-fp%zsf(i,j-1))/(2.*fdy)
192 err=max(err,abs(erri),abs(errj))
193 grad=sqrt(fp%dzdxf(i,j)**2+fp%dzdyf(i,j)**2)
194 maxgrad=max(maxgrad,grad)
197 !$OMP CRITICAL(SFIRE_MODEL_CRIT)
198 write(msg,*)'max gradient ',maxgrad,' max error against zsf',err
199 !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
202 call set_nfuel_cat( & ! also on restart
203 ifms,ifme,jfms,jfme, &
204 ifts,ifte,jfts,jfte, &
205 ifuelread,nfuel_cat0,&
206 fp%zsf,nfuel_cat) ! better not use the extrapolated zsf!!
208 ! uses nfuel_cat to set the other fuel data arrays
209 ! needs zsf on halo width 1 to compute the terrain gradient
210 call set_fire_params( & ! also on restart
211 ifds,ifde,jfds,jfde, &
212 ifms,ifme,jfms,jfme, &
213 ifts,ifte,jfts,jfte, &
214 fdx,fdy,nfuel_cat0, &
215 nfuel_cat,fuel_time, &
218 ! initialize model state to no fire / set tign_in
220 call init_no_fire ( &
221 ifds,ifde,jfds,jfde, &
222 ifms,ifme,jfms,jfme, &
223 ifts,ifte,jfts,jfte, &
224 fdx,fdy,time_start,dt, &
225 fuel_frac,fire_area,lfn,tign_in,tign)
229 call check_lfn_tign('after initialization:',time_start,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,tign)
232 call message('replay, recomputing fuel fraction')
234 ifds,ifde,jfds,jfde, &
235 ifms,ifme,jfms,jfme, &
236 ifts,ifte,jfts,jfte, &
237 ifms,ifme,jfms,jfme, &
238 lfn,tign,fuel_time,time_start,fuel_frac,fire_area) !fuel_frac is global
239 call check_lfn_tign('recomputed fuel fraction',time_start,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,tign)
242 elseif(ifun.eq.3)then ! ignition if so specified
245 elseif (ifun.eq.4) then ! do the timestep
247 ! #35 call check_lfn_tign('time step start',time_start,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
248 if(run_fuel_moisture)then
249 ! uses nfuel_cat to set the other fuel data arrays
250 ! needs zsf on halo width 1 to compute the terrain gradient
251 call set_fire_params( & ! also on restart
252 ifds,ifde,jfds,jfde, &
253 ifms,ifme,jfms,jfme, &
254 ifts,ifte,jfts,jfte, &
255 fdx,fdy,nfuel_cat0, &
256 nfuel_cat,fuel_time, &
260 if(fire_print_msg.ge.stat_lev)then
261 aw=fun_real(RNRM_SUM, &
262 ifms,ifme,1,1,jfms,jfme, & ! memory dims
263 ifds,ifde,1,1,jfds,jfde, & ! domain dims
264 ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
265 0,0,0, & ! staggering
266 fp%vx,fp%vy)/((ifde-ifds+1)*(jfde-jfds+1))
267 mw=fun_real(RNRM_MAX, &
268 ifms,ifme,1,1,jfms,jfme, & ! memory dims
269 ifds,ifde,1,1,jfds,jfde, & ! domain dims
270 ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
271 0,0,0, & ! staggering
274 write(msg,91)time_start,'Average surface wind',aw,'m/s'
275 call message(msg,stat_lev)
276 write(msg,91)time_start,'Maximum surface wind',mw,'m/s'
277 call message(msg,stat_lev)
281 ! compute fuel fraction at start
283 ! ifms,ifme,jfms,jfme, &
284 ! ifts,ifte,jfts,jfte, &
285 ! ifms,ifme,jfms,jfme, &
286 ! lfn,tign,fuel_time,time_start,fuel_frac,fire_area) ! fuel frac is shared
288 call print_2d_stats(ifts,ifte,jfts,jfte, &
289 ifms,ifme,jfms,jfme, &
290 fuel_frac,'model: fuel_frac start')
292 ! advance the model from time_start to time_start+dt
293 ! return the fuel fraction burnt this call in each fire cell
294 ! will call module_fr_sfire_speed::normal_spread for propagation speed
295 ! We cannot simply compute the spread rate here because that will change with the
296 ! angle of the wind and the direction of propagation, thus it is done in subroutine
297 ! normal_spread at each fire time step. Instead, we pass arguments that
298 ! the speed function may use as fp.
300 ! propagate level set function in time
302 ! lfn does not change, tign has no halos
304 if(.not. freeze_fire)then
307 ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
308 ifms,ifme,jfms,jfme, &
309 ifps,ifpe,jfps,jfpe, & ! patch - nodes owned by this process
310 ifts,ifte,jfts,jfte, &
311 time_start,dt,fdx,fdy,tbound, &
312 lfn,lfn_out,tign,ros, fp &
317 lfn_out(i,j) = tign(i,j) - (time_start + dt) ! <0 if burning at the end of the time step
322 call message('sfire_model: EXPERIMENTAL: skipping fireline propagation')
326 call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme, ros,'model: ros')
328 elseif (ifun.eq.5) then ! copy the result of timestep back to input
329 ! this cannot be done in the time step itself because of race condition
330 ! some thread may still be using lfn as input in their tile halo
332 ! fix lfn_out if needed and compute tign
333 if(.not. freeze_fire)then
336 ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
337 ifms,ifme,jfms,jfme, &
338 ifps,ifpe,jfps,jfpe, & ! patch - nodes owned by this process
339 ifts,ifte,jfts,jfte, &
340 time_start,dt,fdx,fdy,tbound, &
341 lfn,lfn_out,tign,ros, fp &
346 call check_lfn_tign('time step end',time_start+dt,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn_out, tign)
348 if(.not. freeze_fire)then
351 lfn(i,j)=lfn_out(i,j)
352 ! if want to try timestep again treat tign the same way here
353 ! even if tign does not need a halo
357 call check_lfn_tign('before ignition',time_start+dt,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
359 ! check for ignitions
360 if( fire_tign_in_time > 0.) then
361 if (ignition%num_lines > 0) then
362 call crash('ignition from lines and from tign_in are not compatible')
364 call ignite_from_tign_in( &
365 ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
366 ifms,ifme,jfms,jfme, &
367 ifts,ifte,jfts,jfte, &
368 time_start,time_start+dt, &
371 call check_lfn_tign('after ignite_from_tign_in',time_start+dt,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
375 do ig = 1,ignition%num_lines
377 ! for now, check for ignition every time step...
378 ! if(ignition%line(ig)%end_time>=time_start.and.ignition%line(ig)%start_time<time_start+dt)then
380 ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain
381 ifms,ifme,jfms,jfme, &
382 ifts,ifte,jfts,jfte, &
384 time_start,time_start+dt, &
385 coord_xf,coord_yf,ignition%unit_fxlong,ignition%unit_fxlat, &
389 call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,'lfn_ig',id)
390 call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,coord_xf,'coord_xf_ig',id)
391 call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,coord_yf,'coord_yf_ig',id)
397 call message('sfire_model: EXPERIMENTAL: skipping ignition')
400 call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme, &
401 lfn,'sfire_model: lfn out')
403 call check_lfn_tign('after ignition',time_start+dt,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
405 elseif (ifun.eq.6) then ! timestep postprocessing
406 ! have halo on lfn now
409 call fire_intensity(fp, & ! fuel properties
410 ifms,ifme,jfms,jfme, & ! memory dims
411 ifts,ifte,jfts,jfte, & ! tile dims
412 ifms,ifme,jfms,jfme, & ! ros dims
414 flineint,flineint2) ! fireline intensities out
416 if(fireline_mask < 0.) then
419 ! if the sign of lfn is the same as all of its neighbors or we are at domain boundary, we are not near the fireline
420 if( (lfn(i-1,j-1)>0. .and. lfn(i-1,j)>0. .and. lfn(i,j-1)>0. .and. lfn(i,j)>0. .and. &
421 lfn(i+1,j+1)>0. .and. lfn(i+1,j)>0. .and. lfn(i,j+1)>0. ) .or. &
422 (lfn(i-1,j-1)<0. .and. lfn(i-1,j)<0. .and. lfn(i,j-1)<0. .and. lfn(i,j)<0. .and. &
423 lfn(i+1,j+1)<0. .and. lfn(i+1,j)<0. .and. lfn(i,j+1)<0. ) .or. &
424 i.eq.ifds .or. i .eq. ifde .or. j.eq.jfds .or. j.eq.jfde) then
425 ros(i,j)=fireline_mask
426 flineint(i,j)=fireline_mask
427 flineint2(i,j)=fireline_mask
434 ifms,ifme,jfms,jfme, & ! memory dims
435 ifts,ifte,jfts,jfte, & ! tile dims
437 f_ros0,f_rosx,f_rosy,f_ros, & ! fire spread
438 f_int,f_lineint,f_lineint2) ! fire intensities for danger rating
441 select case(fire_hfx_given)
445 ! compute the heat fluxes from the fuel burned
446 ! needs lfn and tign from neighbors so halo must be updated before
447 !$OMP CRITICAL(SFIRE_MODEL_CRIT)
448 write(msg,*)'time_start=',time_start,' dt=',dt,' before fuel_left'
449 !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
451 call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,'model: lfn')
452 call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,tign,'model: tign')
453 call check_lfn_tign('before fuel_left',time_start+dt,ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn, tign)
455 call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,fuel_time,'model: fuel_time')
457 if(fire_update_fuel_frac.eq.1)then
458 ! run model the normal way
460 ifds,ifde,jfds,jfde, &
461 ifms,ifme,jfms,jfme, &
462 ifts,ifte,jfts,jfte, &
463 ifts,ifte,jfts,jfte, &
464 lfn,tign,fuel_time,time_start+dt,fuel_frac_end,fire_area) !fuel_frac_end is private and tile based
466 call print_2d_stats(ifts,ifte,jfts,jfte,ifts,ifte,jfts,jfte,fuel_frac_end,'model: fuel_frac end')
470 t = min(fuel_frac(i,j),fuel_frac_end(i,j)) ! do not allow fuel fraction to increase, in case of approximation error
471 fuel_frac_burnt(i,j)=fuel_frac(i,j)-t ! fuel lost this timestep
472 fuel_frac(i,j)=t ! copy new value to state array
475 elseif(fire_update_fuel_frac.eq.2)then
479 fuel_frac_burnt(i,j) = dt / fuel_time(i,j)
481 fuel_frac_burnt(i,j) = 0.
486 call crash('fire_update_fuel_frac value not supported')
489 call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,fuel_frac_burnt,'model: fuel_frac burned')
491 call heat_fluxes(dt,fp, &
492 ifms,ifme,jfms,jfme, &
493 ifts,ifte,jfts,jfte, &
494 ifms,ifme,jfms,jfme, & ! fuel_frac_burned has standard memory dimensions
497 fgrnhfx,fgrnqfx) !out
500 !$OMP CRITICAL(SFIRE_MODEL_CRIT)
501 write(msg,*)"model: expecting fire_hfx to be set in WRF, from wrfinput or wrfrst files"
503 !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
507 fgrnhfx(i,j) = (1. - fire_hfx_latent_part)*fire_hfx(i,j)
508 fgrnqfx(i,j) = fire_hfx_latent_part *fire_hfx(i,j)
514 call message('artificial heat flux from parameters given in namelist.input')
516 call param_hfx( time_start, &
517 ifms,ifme,jfms,jfme, &
518 ifts,ifte,jfts,jfte, &
521 fire_area,fgrnhfx,fgrnqfx)
524 call crash('bad fire_hfx_given')
527 ! this should run in any case
529 if(fire_print_msg.ge.stat_lev)then
530 tfa=fun_real(REAL_SUM, &
531 ifms,ifme,1,1,jfms,jfme, & ! memory dims
532 ifds,ifde,1,1,jfds,jfde, & ! domain dims
533 ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
534 0,0,0, & ! staggering
535 fire_area,fire_area) * fdx * fdy
536 thf=fun_real(REAL_SUM, &
537 ifms,ifme,1,1,jfms,jfme, & ! memory dims
538 ifds,ifde,1,1,jfds,jfde, & ! domain dims
539 ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
540 0,0,0, & ! staggering
541 fgrnhfx,fgrnhfx) * fdx * fdy
542 mhf=fun_real(REAL_MAX, &
543 ifms,ifme,1,1,jfms,jfme, & ! memory dims
544 ifds,ifde,1,1,jfds,jfde, & ! domain dims
545 ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
546 0,0,0, & ! staggering
548 tqf=fun_real(REAL_SUM, &
549 ifms,ifme,1,1,jfms,jfme, & ! memory dims
550 ifds,ifde,1,1,jfds,jfde, & ! domain dims
551 ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
552 0,0,0, & ! staggering
553 fgrnqfx,fgrnqfx) * fdx * fdy
554 mqf=fun_real(REAL_MAX, &
555 ifms,ifme,1,1,jfms,jfme, & ! memory dims
556 ifds,ifde,1,1,jfds,jfde, & ! domain dims
557 ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims
558 0,0,0, & ! staggering
561 write(msg,91)time_start,'Fire area ',tfa,'m^2'
562 call message(msg,stat_lev)
563 write(msg,91)time_start,'Heat output ',thf,'W'
564 call message(msg,stat_lev)
565 write(msg,91)time_start,'Max heat flux ',mhf,'W/m^2'
566 call message(msg,stat_lev)
567 write(msg,91)time_start,'Latent heat output ',tqf,'W'
568 call message(msg,stat_lev)
569 write(msg,91)time_start,'Max latent heat flux',mqf,'W/m^2'
570 call message(msg,stat_lev)
572 91 format('Time ',f11.3,' s ',a,e12.3,1x,a)
576 call print_2d_stats(ifts,ifte,jfts,jfte, &
577 ifms,ifme,jfms,jfme, &
578 fgrnhfx,'model: heat flux(J/m^2/s)')
581 !$OMP CRITICAL(SFIRE_MODEL_CRIT)
582 write(msg,*)'sfire_model: bad ifun=',ifun
583 !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
587 end subroutine sfire_model
589 subroutine param_hfx( time_now,&
590 ifms,ifme,jfms,jfme, &
591 ifts,ifte,jfts,jfte, &
594 fire_area,fgrnhfx,fgrnqfx)
595 !*** generate artifical heat flux from a parametric description
597 real, intent(in)::time_now
598 integer, intent(in):: &
599 ifms,ifme,jfms,jfme, &
601 type(lines_type), intent(in)::hfx
602 real, dimension(ifms:ifme,jfms:jfme), intent(in)::coord_xf,coord_yf ! nodal coordinates
603 real, dimension(ifms:ifme,jfms:jfme), intent(out)::fire_area,fgrnhfx,fgrnqfx ! the imposed heat flux
604 character(len=128):: msg
606 integer::i,j,k,nfa,ncells
607 real:: d2,ax,ay,hfrac,fa,thfx,t,r,radius
608 real, parameter:: sigma_mult=3. ! 3 gives drop to 1% in trans. time from gaussian kernel
609 real:: maxspeed=100 ! max speed how the circle can move
611 do j=jfts,jfte ! zero out the outputs
620 if(hfx%line(k)%radius > 0.)then
621 ! processing heatflux line i
622 ! find the time multiplier
623 t = max(hfx%line(k)%start_time - time_now, time_now - hfx%line(k)%end_time)
624 if(t > 0.)then ! postitive distance from the time interval
625 r = t / hfx%line(k)%trans_time ! position in the transition - 1 is at transition distance
626 hfrac = exp(-(sigma_mult * r)**2/2.) ! gaussian kernel
632 ! find the coordinates of the center of the heat flux circle now
633 ax = safe_prop(time_now, &
634 hfx%line(k)%start_time,&
635 hfx%line(k)%end_time,&
636 hfx%line(k)%start_x,&
638 hfx%unit_fxlong*maxspeed)
639 ay = safe_prop(time_now,&
640 hfx%line(k)%start_time,&
641 hfx%line(k)%end_time,&
642 hfx%line(k)%start_y,&
644 hfx%unit_fxlat*maxspeed)
646 radius=hfx%line(k)%radius
648 !$OMP CRITICAL(SFIRE_MODEL_CRIT)
649 write(msg,*)'hfx line ',i,' at ',time_now,'s ',hfrac,' of max ', hfx%line(k)%hfx_value
651 write(msg,*)'center ',ax,ay,' radius ',radius
653 !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
659 ! distance squared from the center
660 d2 = (hfx%unit_fxlong*(ax - coord_xf(i,j)))**2 + (hfx%unit_fxlat*(ay - coord_yf(i,j)))**2
661 if(d2 < radius**2)then
667 thfx= hfx%line(k)%hfx_value * hfrac * fa ! total heat flux at this point
668 fgrnhfx(i,j)= fgrnhfx(i,j) + (1.-fire_hfx_latent_part) * thfx
669 fgrnqfx(i,j)= fgrnqfx(i,j) + fire_hfx_latent_part * thfx
671 fire_area(i,j) = max(fire_area(i,j),fa)
678 !$OMP CRITICAL(SFIRE_MODEL_CRIT)
679 write(msg,*)'Number of cells in fire area in this tile ',nfa,' ',(100.*nfa)/ncells,' %'
681 !$OMP END CRITICAL(SFIRE_MODEL_CRIT)
685 end subroutine param_hfx
689 real function safe_prop(t,t1,t2,x1,x2,ms)
690 ! return x between x1 and x2 in the same proportion as is t between t1 and t2, safe in the case when t1=t2 and x1=x2
691 ! safe_prop = x1 + (t-t1)*(x2-x1)/(t2-t1)
692 ! future: abs((x2-x1)/(t2-t1)) capped at ms but still return x1 when t=t1 and x2 when t=t2
693 real, intent(in)::t,t1,t2,x1,x2,ms
695 if(t2 < t1)call crash('safe_prop: must have t2>t1')
696 if(.not.ms>0.)call crash('safe_prop: must have ms>0')
701 call crash('safe_prop: infinite speed')
704 p = (t - t1)/(t2 - t1) ! 0 at t1, 1 at t2
708 end function safe_prop
713 end module module_fr_sfire_model