! #define DEBUG_OUT module module_fr_fire_model 1 use module_fr_fire_core use module_fr_fire_util use module_fr_fire_phys contains subroutine fire_model ( & 1,36 id, & ! unique number for prints and debug ifun, & ! what to do see below restart, & need_lfn_update, & ! if lfn needs to be synced between tiles num_ignitions, & ! number of ignitions before advancing ifuelread,nfuel_cat0, & ! initialize fuel categories ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain ifms,ifme,jfms,jfme, & ! fire memory dims - how declared ifps,ifpe,jfps,jfpe, & ! patch - nodes owned by this process ifts,ifte,jfts,jfte, & ! fire tile dims - this thread time_start,dt, & ! time and increment fdx,fdy, & ! fire mesh spacing, ignition_line, & ! small array of ignition line descriptions ignitions_done,ignited_tile, & coord_xf,coord_yf,unit_xf,unit_yf, & ! fire mesh coordinates lfn,lfn_out,tign,fuel_frac,fire_area, & ! state: level function, ign time, fuel left, area burning grnhfx,grnqfx, & ! output: heat fluxes ros, & ! output: rate of spread nfuel_cat, & ! fuel data per point fuel_time, & ! save derived internal data fp & ) ! This subroutine implements the fire spread model. ! All quantities are on the fire grid. It inputs ! winds given on the nodes of the fire grid ! and outputs the heat fluxes on the cells of the fire grid. ! This subroutine has no knowledge of any atmospheric model. ! This code was written to conform with the WRF parallelism model, however it ! does not depend on it. It can be called with domain equal to tile. ! Wind and height must be given on 1 more node beyond the domain bounds. ! The subroutine changes only array entries of the arguments in the tile. ! Upon exit with ifun=2 (time step), lfn_out is to be copied into lfn by the caller. ! When this subroutine is used on separate tiles that make a domain the value, the ! it uses lfn on a strip of width 2 from neighboring tiles. ! ! All computation is done on one tile. ! ! This subroutine is intended to be called in a loop like ! ! ! do ifun=1,6 (if initizalize run, otherwise 3,6) ! start parallel loop over tiles ! if ifun=1, set z and fuel data ! if ifun=3, set the wind arrays ! call fire_model(....) ! end parallel loop over tiles ! ! if need_lfn_update, halo exchange on lfn width 2 ! ! if ifun=0 ! halo exchange on z width 2 ! halo exchange on fuel data width 1 ! endif ! ! if ifun=3, halo exchange on winds width 2 ! ! enddo implicit none !*** arguments ! control switches integer, intent(in) :: id integer, intent(in) :: ifun ! 1 = initialize run pass 1 ! 2 = initialize run pass 2 ! 3 = initialize timestep ! 4 = do one timestep ! 5 = copy timestep output to input ! 6 = compute output fluxes logical, intent(in):: restart ! if true, use existing state logical, intent(out)::need_lfn_update ! if true, halo update on lfn afterwards ! scalar data integer, intent(in) :: num_ignitions ! number of ignition lines integer, intent(in) :: ifuelread,nfuel_cat0 ! for set_fire_params integer, intent(in) :: ifds,ifde,jfds,jfde,& ! fire domain bounds ifps,ifpe,jfps,jfpe ! patch - nodes owned by this process integer, intent(in) :: ifts,ifte,jfts,jfte ! fire tile bounds integer, intent(in) :: ifms,ifme,jfms,jfme ! fire memory array bounds REAL,INTENT(in) :: time_start,dt ! starting time, time step REAL,INTENT(in) :: fdx,fdy ! spacing of the fire mesh ! array data type(ignition_line_type), dimension (num_ignitions), intent(in):: ignition_line ! descriptions of ignition lines integer, intent(out):: ignited_tile(num_ignitions),ignitions_done real, dimension(ifms:ifme, jfms:jfme), intent(in):: & coord_xf,coord_yf ! node coordinates real, intent(in):: unit_xf,unit_yf ! coordinate units in m ! state REAL, INTENT(inout), dimension(ifms:ifme,jfms:jfme):: & lfn , & ! level function: fire is where lfn<0 (node) tign , & ! absolute time of ignition (node) fuel_frac ! fuel fraction (node), currently redundant REAL, INTENT(out), dimension(ifms:ifme,jfms:jfme):: & fire_area ! fraction of each cell burning ! output REAL, INTENT(out), dimension(ifms:ifme,jfms:jfme):: & lfn_out, & ! grnhfx,grnqfx, & ! heat fluxes J/m^2/s (cell) ros ! output: rate of spread ! constant arrays - set at initialization real, intent(inout), dimension(ifms:ifme, jfms:jfme)::nfuel_cat ! cell based, data, constant real,intent(inout),dimension(ifms:ifme,jfms:jfme):: fuel_time type(fire_params),intent(inout)::fp !*** local integer :: xifms,xifme,xjfms,xjfme ! memory bounds for pass-through arguments to normal spread real, dimension(ifts:ifte,jfts:jfte)::fuel_frac_burnt,fuel_frac_end integer::ignited,ig,i,j,itso,iteo,jtso,jteo real::tbound,err,erri,errj,maxgrad,grad,tfa,thf,mhf,tqf,mqf,aw,mw character(len=128)::msg logical:: freeze_fire integer:: stat_lev=1 !*** executable call check_mesh_2dim(ifts-1,ifte+1,jfts-1,jfte+1,ifms,ifme,jfms,jfme) xifms=ifms ! dimensions for the include file xifme=ifme xjfms=jfms xjfme=jfme ! init flags need_lfn_update=.false. ignitions_done=0 freeze_fire = fire_const_time > 0. .and. time_start < fire_const_time if(ifun.eq.1)then ! do nothing, init pass 1 is outside only elseif(ifun.eq.2)then ! initialize all arrays that the model will not change later ! assuming halo on zsf done ! extrapolate on 1 row of cells beyond the domain boundary ! including on the halo regions call continue_at_boundary(1,1,0., & ! do x direction or y direction ifms,ifme,jfms,jfme, & ! memory dims ifds,ifde,jfds,jfde, & ! domain dims ifps,ifpe,jfps,jfpe, & ! patch dims - winds defined up to +1 ifts,ifte,jfts,jfte, & ! tile dims itso,iteo,jtso,jteo, & ! where set now fp%zsf) ! array ! compute the gradients once for all err=0. maxgrad=0. do j=jfts,jfte do i=ifts,ifte erri = fp%dzdxf(i,j) - (fp%zsf(i+1,j)-fp%zsf(i-1,j))/(2.*fdx) errj = fp%dzdyf(i,j) - (fp%zsf(i,j+1)-fp%zsf(i,j-1))/(2.*fdy) err=max(err,abs(erri),abs(errj)) grad=sqrt(fp%dzdxf(i,j)**2+fp%dzdyf(i,j)**2) maxgrad=max(maxgrad,grad) enddo enddo !$OMP CRITICAL(FIRE_MODEL_CRIT) write(msg,*)'max gradient ',maxgrad,' max error against zsf',err !$OMP END CRITICAL(FIRE_MODEL_CRIT) call message(msg) if(.not.restart)call set_nfuel_cat( & ifms,ifme,jfms,jfme, & ifts,ifte,jfts,jfte, & ifuelread,nfuel_cat0,& fp%zsf,nfuel_cat) ! better not use the extrapolated zsf!! ! uses nfuel_cat to set the other fuel data arrays ! needs zsf on halo width 1 to compute the terrain gradient if(.not.restart)call set_fire_params( & ifds,ifde,jfds,jfde, & ifms,ifme,jfms,jfme, & ifts,ifte,jfts,jfte, & fdx,fdy,nfuel_cat0, & nfuel_cat,fuel_time, & fp & ) ! initialize model state to no fire if(.not.restart)then call init_no_fire ( & ifds,ifde,jfds,jfde, & ifms,ifme,jfms,jfme, & ifts,ifte,jfts,jfte, & fdx,fdy,time_start, & fuel_frac,fire_area,lfn,tign) need_lfn_update=.true. ! because we have set lfn endif elseif(ifun.eq.3)then ! ignition if so specified elseif (ifun.eq.4) then ! do the timestep if(fire_print_msg.ge.stat_lev)then aw=fun_real(RNRM_SUM, & ifms,ifme,1,1,jfms,jfme, & ! memory dims ifds,ifde,1,1,jfds,jfde, & ! domain dims ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims 0,0,0, & ! staggering fp%vx,fp%vy)/((ifde-ifds+1)*(jfde-jfds+1)) mw=fun_real(RNRM_MAX, & ifms,ifme,1,1,jfms,jfme, & ! memory dims ifds,ifde,1,1,jfds,jfde, & ! domain dims ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims 0,0,0, & ! staggering fp%vx,fp%vy) !$OMP MASTER write(msg,91)time_start,'Average wind ',aw,'m/s' call message(msg,stat_lev) write(msg,91)time_start,'Maximum wind ',mw,'m/s' call message(msg,stat_lev) !$OMP END MASTER endif ! compute fuel fraction at start ! call fuel_left( & ! ifms,ifme,jfms,jfme, & ! ifts,ifte,jfts,jfte, & ! ifms,ifme,jfms,jfme, & ! lfn,tign,fuel_time,time_start,fuel_frac,fire_area) ! fuel frac is shared call print_2d_stats(ifts,ifte,jfts,jfte, & ifms,ifme,jfms,jfme, & fuel_frac,'model: fuel_frac start') ! advance the model from time_start to time_start+dt ! return the fuel fraction burnt this call in each fire cell ! will call module_fr_fire_speed::normal_spread for propagation speed ! We cannot simply compute the spread rate here because that will change with the ! angle of the wind and the direction of propagation, thus it is done in subroutine ! normal_spread at each fire time step. Instead, we pass arguments that ! the speed function may use as fp. ! propagate level set function in time ! set lfn_out tign ! lfn does not change, tign has no halos if(.not. freeze_fire)then call prop_ls(id, & ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain ifms,ifme,jfms,jfme, & ifps,ifpe,jfps,jfpe, & ! patch - nodes owned by this process ifts,ifte,jfts,jfte, & time_start,dt,fdx,fdy,tbound, & lfn,lfn_out,tign,ros, fp & ) else call message('fire_model: EXPERIMENTAL: skipping fireline propagation') endif elseif (ifun.eq.5) then ! copy the result of timestep back to input ! this cannot be done in the time step itself because of race condition ! some thread may still be using lfn as input in their tile halo if(.not. freeze_fire)then do j=jfts,jfte do i=ifts,ifte lfn(i,j)=lfn_out(i,j) ! if want to try timestep again treat tign the same way here ! even if tign does not need a halo enddo enddo endif ! check for ignitions do ig = 1,num_ignitions ! for now, check for ignition every time step... ! if(ignition_line(ig)%end_time>=time_start.and.ignition_line(ig)%start_time<time_start+dt)then call ignite_fire( & ifds,ifde,jfds,jfde, & ! fire domain dims - the whole domain ifms,ifme,jfms,jfme, & ifts,ifte,jfts,jfte, & ignition_line(ig), & time_start,time_start+dt, & coord_xf,coord_yf,unit_xf,unit_yf, & lfn,tign,ignited) ignitions_done=ignitions_done+1 ignited_tile(ignitions_done)=ignited ! need_lfn_update=.true. ! if ignition, lfn changed #ifdef DEBUG_OUT call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,lfn,'lfn_ig',id) call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,coord_xf,'coord_xf_ig',id) call write_array_m(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme,coord_yf,'coord_yf_ig',id) #endif ! endif enddo call print_2d_stats(ifts,ifte,jfts,jfte,ifms,ifme,jfms,jfme, & lfn,'fire_model: lfn out') need_lfn_update=.true. ! duh elseif (ifun.eq.6) then ! timestep postprocessing if(.not. freeze_fire)then ! compute the heat fluxes from the fuel burned ! needs lfn and tign from neighbors so halo must be updated before call fuel_left(& ifms,ifme,jfms,jfme, & ifts,ifte,jfts,jfte, & ifts,ifte,jfts,jfte, & lfn,tign,fuel_time,time_start+dt,fuel_frac_end,fire_area) !fuel_frac_end is private and tile based call print_2d_stats(ifts,ifte,jfts,jfte, & ifts,ifte,jfts,jfte, & fuel_frac_end,'model: fuel_frac end') do j=jfts,jfte do i=ifts,ifte fuel_frac_burnt(i,j)=fuel_frac(i,j)-fuel_frac_end(i,j) ! fuel lost this timestep fuel_frac(i,j)=fuel_frac_end(i,j) ! copy new value to state array enddo enddo call print_2d_stats(ifts,ifte,jfts,jfte, & ifts,ifte,jfts,jfte, & fuel_frac_burnt,'model: fuel_frac burned') call heat_fluxes(dt, & ifms,ifme,jfms,jfme, & ifts,ifte,jfts,jfte, & ifts,ifte,jfts,jfte, & ! fuel_frac_burned is tile dimensioned fp%fgip, & fuel_frac_burnt, & ! grnhfx,grnqfx) !out if(fire_print_msg.ge.stat_lev)then tfa=fun_real(REAL_SUM, & ifms,ifme,1,1,jfms,jfme, & ! memory dims ifds,ifde,1,1,jfds,jfde, & ! domain dims ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims 0,0,0, & ! staggering fire_area,fire_area) * fdx * fdy thf=fun_real(REAL_SUM, & ifms,ifme,1,1,jfms,jfme, & ! memory dims ifds,ifde,1,1,jfds,jfde, & ! domain dims ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims 0,0,0, & ! staggering grnhfx,grnhfx) * fdx * fdy mhf=fun_real(REAL_MAX, & ifms,ifme,1,1,jfms,jfme, & ! memory dims ifds,ifde,1,1,jfds,jfde, & ! domain dims ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims 0,0,0, & ! staggering grnhfx,grnhfx) tqf=fun_real(REAL_SUM, & ifms,ifme,1,1,jfms,jfme, & ! memory dims ifds,ifde,1,1,jfds,jfde, & ! domain dims ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims 0,0,0, & ! staggering grnqfx,grnqfx) * fdx * fdy mqf=fun_real(REAL_MAX, & ifms,ifme,1,1,jfms,jfme, & ! memory dims ifds,ifde,1,1,jfds,jfde, & ! domain dims ifts,ifte,1,1,jfts,jfte, & ! patch or tile dims 0,0,0, & ! staggering grnqfx,grnqfx) !$OMP MASTER write(msg,91)time_start,'Fire area ',tfa,'m^2' call message(msg,stat_lev) write(msg,91)time_start,'Heat output ',thf,'W' call message(msg,stat_lev) write(msg,91)time_start,'Max heat flux ',mhf,'W/m^2' call message(msg,stat_lev) write(msg,91)time_start,'Latent heat output ',tqf,'W' call message(msg,stat_lev) write(msg,91)time_start,'Max latent heat flux',mqf,'W/m^2' call message(msg,stat_lev) !$OMP END MASTER 91 format('Time ',f11.3,' s ',a,e12.3,1x,a) endif else call message('fire_model: EXPERIMENTAL: skipping fuel burnt computation') if (fire_const_grnhfx >= 0. .and. fire_const_grnqfx >= 0.) then !$OMP CRITICAL(FIRE_MODEL_CRIT) write(msg,'(a,2e12.3,a)')'fire_model: EXPERIMENTAL output constant heat flux', & fire_const_grnhfx, fire_const_grnqfx, ' W/s' !$OMP END CRITICAL(FIRE_MODEL_CRIT) call message(msg) do j=jfts,jfte do i=ifts,ifte grnhfx(i,j)=fire_const_grnhfx grnqfx(i,j)=fire_const_grnqfx enddo enddo endif endif call print_2d_stats(ifts,ifte,jfts,jfte, & ifms,ifme,jfms,jfme, & grnhfx,'model: heat flux(J/m^2/s)') else !$OMP CRITICAL(FIRE_MODEL_CRIT) write(msg,*)'fire_model: bad ifun=',ifun !$OMP END CRITICAL(FIRE_MODEL_CRIT) call crash(msg) endif end subroutine fire_model ! !***************** ! end module module_fr_fire_model