diff --git a/Registry/Registry.EM_COMMON b/Registry/Registry.EM_COMMON index 3243a523d5..600d81877e 100644 --- a/Registry/Registry.EM_COMMON +++ b/Registry/Registry.EM_COMMON @@ -3372,6 +3372,7 @@ halo HALO_EM_D3_3 dyn_em 24:u_1,u_2,v_1,v_2,w_1,w_2,t_1,t_2,ph_1,ph_2,tke_1 halo HALO_EM_D3_5 dyn_em 48:u_1,u_2,v_1,v_2,w_1,w_2,t_1,t_2,ph_1,ph_2,tke_1,tke_2,moist,chem,tracer,scalar;4:mu_1,mu_2 halo HALO_EM_E_3 dyn_em 24:u_1,u_2,v_1,v_2,w_1,w_2,t_1,t_2,ph_1,ph_2,tke_1,tke_2,;4:mu_1,mu_2 halo HALO_EM_E_5 dyn_em 48:u_1,u_2,v_1,v_2,w_1,w_2,t_1,t_2,ph_1,ph_2,tke_1,tke_2,;4:mu_1,mu_2 +halo HALO_FIREBRAND_SPOTTING_5 dyn_em 48:muts,al halo HALO_EM_MOIST_E_3 dyn_em 24:moist halo HALO_EM_MOIST_E_5 dyn_em 48:moist halo HALO_EM_MOIST_E_7 dyn_em 80:moist diff --git a/Registry/registry.fire b/Registry/registry.fire index e0ebcfc237..ef737cd5f3 100644 --- a/Registry/registry.fire +++ b/Registry/registry.fire @@ -5,36 +5,48 @@ # # declare fire package and choose which fire scheme # +# ------------------------------------------------------------------------------------------------------------------------ # # name> namelist choice> state vars> +# ------------------------------------------------------------------------------------------------------------------------ # -package fire_sfire ifire==2 - state:avg_fuel_frac,bbb,betafl,burnt_area_dt,canhfx,canqfx,dzdxf,dzdyf,fcanhfx,fcanqfx,fgip,fgrnhfx,fgrnqfx,fire_area,fire_smoke,flame_length,fmc_equi,fmc_g,fmc_gc,fmep,fmc_lag,fmoist_lasttime,fmoist_nexttime,fuel_frac,fuel_time,fxlat,fxlong,fz0,grnhfx,grnhfx_fu,grnqfx,grnqfx_fu,iboros,ischap,lfn,lfn_0,lfn_1,lfn_2,lfn_hist,lfn_s0,lfn_s1,lfn_s2,lfn_s3,lfn_time,nfuel_cat,phiwc,psfc_old,q2_old,r_0,rain_old,rh_fire,ros,ros_front,rqvfrten,rthfrten,t2_old,tign_g,uah,uf,vah,vf,zsf + +package fire_sfire ifire==2 - state:avg_fuel_frac,bbb,betafl,burnt_area_dt,canhfx,canqfx,dzdxf,dzdyf,fcanhfx,fcanqfx,fgip,fgrnhfx,fgrnqfx,fire_area,fire_smoke,flame_length,fmc_equi,fmc_g,fmc_gc,fmep,fmc_lag,fmoist_lasttime,fmoist_nexttime,fuel_frac,fuel_time,fxlat,fxlong,fz0,grnhfx,grnhfx_fu,grnqfx,grnqfx_fu,iboros,ischap,lfn,lfn_0,lfn_1,lfn_2,lfn_hist,lfn_s0,lfn_s1,lfn_s2,lfn_s3,lfn_time,nfuel_cat,phiwc,psfc_old,q2_old,r_0,rain_old,rh_fire,ros,ros_front,rqvfrten,rthfrten,t2_old,tign_g,uah,uf,vah,vf,zsf, fs_last_gen_dt, fs_gen_idmax, fs_count_reset, fs_fire_ROSdt, fs_fire_area, fs_fuel_spotting_risk, fs_count_landed_all, fs_count_landed_hist, fs_landing_mask, fs_gen_inst, fs_frac_landed, fs_spotting_lkhd package fire_fmoist_run fmoisti_run==1 - state:fmc_gc,fmep,fmc_equi,fmc_lag,rain_old,t2_old,psfc_old,rh_fire package fire_fmoist_interp fmoisti_interp==1 - state:fmc_gc +# ------------------------------------------------------------------------------------------------------------------------ +# +# ------------------------------------------------------------------------------------------------------------------------ +# +# ------------------------------------------------------------------------------------------------------------------------ # level function history support +# dimspec ign 2 constant=1 z i_lfn_history state real lfn_hist *i*j fire 1 Z i012hr "LFN_HIST" "level function history" "1" state real lfn_time {ign} fire 1 - i012hr "LFN_TIME" "level function history time" "s" - +# ------------------------------------------------------------------------------------------------------------------------ # fire variables on fire grid # -#
state real nfuel_cat *i*j fire 1 z i012hr "NFUEL_CAT" "fuel data" state real zsf *i*j fire 1 z i012hr "ZSF" "height of surface above sea level" "m" state real dzdxf *i*j fire 1 z i012hr "DZDXF" "surface gradient x" "1" state real dzdyf *i*j fire 1 z i012hr "DZDYF" "surface gradient y" "1" state real tign_g *i*j fire 1 z hr "TIGN_G" "ignition time on ground" "s" +# ------------------------------------------------------------------------------------------------------------------------ # fire variables on atm grid # +# # outputs to atm model state real rthfrten ikj fire 1 z hr "RTHFRTEN" "temperature tendency" "K/s" state real rqvfrten ikj fire 1 z hr "RQVFRTEN" "humidity tendency" +# ------------------------------------------------------------------------------------------------------------------------ # diagnostics only +# state real avg_fuel_frac ij fire 1 z hr "AVG_FUEL_FRAC" "fuel remaining averaged to atmospheric grid" "1" state real grnhfx ij fire 1 z hr "GRNHFX" "heat flux from ground fire" "W/m^2" state real grnqfx ij fire 1 z hr "GRNQFX" "moisture flux from ground fire" "W/m^2" @@ -45,7 +57,10 @@ state real vah ij fire 1 Y hr "VAH" state real grnhfx_fu ij fire 1 z r "GRNHFX_FU" "heat flux from ground fire (feedback unsensitive)" "W/m^2" state real grnqfx_fu ij fire 1 z r "GRNQFX_FU" "moisture flux from ground fire (feedback unsensitive)" "W/m^2" +# ------------------------------------------------------------------------------------------------------------------------ # sfire variables on fire grid +# +# # (also using tign_g,zs,z_at_w,dz8w,nfuel_cat,fluxes,zsf) # state real lfn *i*j fire 1 z hr "LFN" "level function" "1" @@ -69,13 +84,16 @@ state real ros *i*j fire 1 z r "ROS" "rat state real burnt_area_dt *i*j fire 1 z hr "BURNT_AREA_DT" "fraction of cell area burnt on current dt" "-" state real flame_length *i*j fire 1 z hr "FLAME_LENGTH" "fire flame length" "m" state real ros_front *i*j fire 1 z hr "ROS_FRONT" "rate of spread at fire front" "m/s" - +# +# ------------------------------------------------------------------------------------------------------------------------ # fuel moisture variables +# state real fmc_g *i*j fire 1 z i102hr "FMC_G" "ground fuel moisture contents" "1" - +# # fuel moisture model section dimspec num_fmc - namelist=nfmc z fuel_moisture_classes dimspec num_fmep - constant=2 z fuel_moisture_extended_parameters +# rconfig integer nfmc namelist,fire 1 5 - "nfmc" "number of fuel moisture classes" state real fmc_gc i{num_fmc}j fire 1 z ihr "FMC_GC" "fuel moisture contents by class" "1" state real fmep i{num_fmep}j fire 1 z ihr "FMEP" "fuel moisture extended model parameters" "1" @@ -96,11 +114,14 @@ rconfig logical fmoist_only namelist,fire max_domains .false. h rconfig integer fmoist_freq namelist,fire max_domains 0 hr "fmoist_freq" "frequency to run moisture model 0: use fmoist_dt, k>0: every k timesteps" "1" rconfig real fmoist_dt namelist,fire max_domains 600 hr "fmoist_dt " "moisture model time step" "s" rconfig real fmep_decay_tlag namelist,fire 1 999999 hr "fmep_decay_tlag" "time constant of assimilated adjustments of equilibria decay" "1" +# halo HALO_FIRE_MFG dyn_em 24:fmc_g halo HALO_FIRE_MAG dyn_em 8:fmc_gc +# - +# ------------------------------------------------------------------------------------------------------------------------ # constant data arrays +# state real fxlong *i*j fire 1 z ihr "FXLONG" "longitude of midpoints of fire cells" "degrees" state real fxlat *i*j fire 1 z ihr "FXLAT" "latitude of midpoints of fire cells" "degrees" state real fuel_time *i*j fire 1 z hr "FUEL_TIME" "fuel" @@ -113,13 +134,20 @@ state real ischap *i*j fire 1 z hr "ISCHAP" state real fz0 *i*j fire 1 z ihr "FZ0" "roughness length of fire cells" "m" state real iboros *i*j fire 1 z hr "IBOROS" "fire intensity over rate of spread" "kJ/m^2" -# + +# ======================================================================================================================== # fire configure namelist variables +# ======================================================================================================================== # +# ------------------------------------------------------------------------------------------------------------------------ #
+# ------------------------------------------------------------------------------------------------------------------------ rconfig integer ifire namelist,fire max_domains 0 rconfig integer fire_boundary_guard namelist,fire max_domains 8 - "fire_boundary_guard" "cells to stop when fire close to domain boundary" +# +# ------------------------------------------------------------------------------------------------------------------------ # ignition for WRF-Fire +# rconfig integer fire_num_ignitions namelist,fire max_domains 0 - "fire_num_ignitions" "number of ignition lines" rconfig real fire_ignition_ros1 namelist,fire max_domains 0.01 - "fire_ignition_ros1" "rate of spread during ignition" "m/s" rconfig real fire_ignition_start_lon1 namelist,fire max_domains 0. - "fire_ignition_start_long1" "long coord of start of ignition line" "deg" @@ -181,7 +209,10 @@ rconfig real fire_ignition_start_x5 namelist,fire max_domains rconfig real fire_ignition_start_y5 namelist,fire max_domains 0. - "fire_ignition_start_y5" "y coord of start of ignition line" "m" rconfig real fire_ignition_end_x5 namelist,fire max_domains 0. - "fire_ignition_end_x5" "x coord of end of ignition line" "m" rconfig real fire_ignition_end_y5 namelist,fire max_domains 0. - "fire_ignition_end_y5" "y coord of end of ignition line" "m" +# +# ------------------------------------------------------------------------------------------------------------------------ # variables from old cawfe code +# rconfig real fire_lat_init namelist,fire max_domains 0. - "fire_lat_init" "latitude to start fire" "degrees" rconfig real fire_lon_init namelist,fire max_domains 0. - "fire_lon_init" "longitude to start fire" "degrees" rconfig real fire_ign_time namelist,fire max_domains 0. - "fire_ign_time" "time when fire should be ignited" "min" @@ -193,11 +224,17 @@ rconfig real fire_ext_crwn namelist,fire max_domains rconfig real fire_wind_height namelist,fire max_domains 6.096 - "fire_wind_height" "height of uah,vah wind in fire spread formula" "m" rconfig integer fire_fuel_read namelist,fire max_domains -1 - "fire_fuel_read" "fuel categories are set by: if 0, uniform; if 1, user-presc; if 2, read from file" "" rconfig integer fire_fuel_cat namelist,fire max_domains 1 - "fire_fuel_cat" "fuel category if ifuelread=0" "" +# +# ------------------------------------------------------------------------------------------------------------------------ # sfire switches +# rconfig integer fire_fmc_read namelist,fire max_domains 1 - "fire_fmc_read" "ground fuel moisture is set by: if 0, in wrfinput; if 1, user-presc; if 2, read from file" "" rconfig integer fire_print_msg namelist,fire max_domains 0 - "fire_write_msg" "write fire statistics, 0 no writes, 1+ for more" "" rconfig integer fire_print_file namelist,fire max_domains 0 - "fire_write_file" "write fire output text files, 0 no writes, 1+ for more" "" +# +# ------------------------------------------------------------------------------------------------------------------------ # method selection +# rconfig integer fire_fuel_left_method namelist,fire max_domains 1 - "fire_fuel_left_method" "1 or 2, compute fuel_left" "" rconfig integer fire_fuel_left_irl namelist,fire max_domains 2 - "fire_fuel_left_irl" "submesh to compute fuel lwft, even, at least 2" "" rconfig integer fire_fuel_left_jrl namelist,fire max_domains 2 - "fire_fuel_left_jrl" "submesh to compute fuel lwft, even, at least 2" "" @@ -208,7 +245,10 @@ rconfig real fire_viscosity namelist,fire max_domains rconfig real fire_lfn_ext_up namelist,fire max_domains 1.0 - "fire_lfn_ext_up" "0.=extend level set function at boundary by reflection, 1.=always up" "1" rconfig integer fire_topo_from_atm namelist,fire max_domains 1 - "fire_topo_from_atm" "0 = do nothing, 1 = populate ZSF by interpolating from atmosphere" "1" rconfig integer fire_advection namelist,fire max_domains 1 - "fire_advection" "0 = fire spread computed from normal wind speed/slope, 1 = fireline particle speed projected on normal" "0" +# +# ------------------------------------------------------------------------------------------------------------------------ # experiments +# rconfig integer fire_test_steps namelist,fire max_domains 0 - "fire_test_steps" ">0 = on first call, do specified number of steps and terminate (testing only)" "1" rconfig real fire_const_time namelist,fire max_domains -1. - "fire_const_time" "time from ignition to freeze fire, <0 never" "s" rconfig real fire_const_grnhfx namelist,fire max_domains 0. - "fire_const_grnhfx" "if both >=0, the amount of constant heat flux" "1" @@ -225,30 +265,48 @@ rconfig real xrad_perturbation namelist,fire max_domains rconfig real yrad_perturbation namelist,fire max_domains 0. - "yrad_perturbation" "horizontal radius of the perturbation in N-S direction" "m" rconfig real zrad_perturbation namelist,fire max_domains 0. - "zrad_perturbation" "vertical radius of the perturbation (bubble) direction" "m" rconfig real hght_perturbation namelist,fire max_domains 0. - "hght_perturbation" "height at which the perturbation (bubble) will be suspended" "m" +# +# ------------------------------------------------------------------------------------------------------------------------ # grid stretching +# rconfig logical stretch_grd namelist,fire max_domains .true. - "stretch_grd" "vertical grid stretching (on/off)" "" rconfig logical stretch_hyp namelist,fire max_domains .false. - "stretch_hyp" "hyperbolic tang grid stretching (more levels at the surface)" "" rconfig real z_grd_scale namelist,fire max_domains 0.40 - "z_grd_scale" "zscale parameter for hyperbolic grid streching" "m" +# +# ------------------------------------------------------------------------------------------------------------------------ # surface initialization +# rconfig logical sfc_full_init namelist,fire max_domains .false. - "sfc_full_init" "full surface initialization (on/off)" "" rconfig integer sfc_lu_index namelist,fire max_domains 28 - "sfc_lu_index" "USGS landuse index definig sfc record from LANDUSE.TBL" "" rconfig real sfc_tsk namelist,fire max_domains 285.0 - "sfc_tsk" "surface skin temperature (TSK)" "K" rconfig real sfc_tmn namelist,fire max_domains 285.0 - "sfc_tmn" "soil temperature at lower boundary (TMN)" "K" +# +# ------------------------------------------------------------------------------------------------------------------------ # landuse data from files - overwrite constants +# rconfig logical fire_read_lu namelist,fire max_domains .false. - "fire_read_lu" "read land use data from file input_lu" "" rconfig logical fire_read_tsk namelist,fire max_domains .false. - "fire_read_tsk" "read file input_tsk" "" rconfig logical fire_read_tmn namelist,fire max_domains .false. - "fire_read_tmn" "read file input_tmn" "" +# +# ------------------------------------------------------------------------------------------------------------------------ # topography data from files +# rconfig logical fire_read_atm_ht namelist,fire max_domains .false. - "fire_read_atm_ht" "read terrain height on atm mesh from file" "" rconfig logical fire_read_fire_ht namelist,fire max_domains .false. - "fire_read_fire_ht" "read terrain height on fire mesh from file" "" rconfig logical fire_read_atm_grad namelist,fire max_domains .false. - "fire_read_atm_grad" "read terrain gradient on atm mesh from file" "" rconfig logical fire_read_fire_grad namelist,fire max_domains .false. - "fire_read_fire_grad" "read terrain gradient on fire mesh from file" "" +# +# ------------------------------------------------------------------------------------------------------------------------ # additional data required by Noah LSM scheme +# rconfig real sfc_vegfra namelist,fire max_domains 0.5 - "sfc_vegfra" "vegetation fraction" "" rconfig real sfc_canwat namelist,fire max_domains 0 - "sfc_canwat" "canopy water" "" rconfig integer sfc_ivgtyp namelist,fire max_domains 18 - "sfc_ivgtyp" "dominant vegetation category in the LSM scheme" "" rconfig integer sfc_isltyp namelist,fire max_domains 7 - "sfc_isltyp" "dominant soil category in the LSM scheme" "" +# +# ------------------------------------------------------------------------------------------------------------------------ # namelist options for new WRF-Fire capabilities DME +# rconfig logical fire_lsm_reinit namelist,fire max_domains .true. - "flag to activate reinitialization of level set method" rconfig integer fire_lsm_reinit_iter namelist,fire max_domains 1 - "number of iterations for the reinitialization PDE" rconfig integer fire_upwinding_reinit namelist,fire max_domains 4 - "numerical scheme (space) for reinitialization PDE: 1=WENO3, 2=WENO5, 3=hybrid WENO3-ENO1, 4=hybrid WENO5-ENO1" @@ -261,11 +319,85 @@ rconfig real fire_viscosity_bg namelist,fire max_domains rconfig real fire_viscosity_band namelist,fire max_domains 0.5 - "fire_viscosity_band" "number of times the hybrid advection band to transition from fire_viscosity_bg to fire_viscosity" "1" rconfig integer fire_viscosity_ngp namelist,fire max_domains 2 - "number of grid points around lfn=0 where low artificial viscosity is used = fire_viscosity_bg" rconfig real fire_slope_factor namelist,fire max_domains 1.0 - "slope correction factor" "-" +# ------------------------------------------------------------------------------------------------------------------------ # tracers for WRF-Fire +# state real - ikjftb tracer 1 - - - state real fire_smoke ikjftb tracer 1 - irhusdf=(bdy_interp:dt) "fire_smoke" "fire_smoke" "g_smoke/kg_air" package tracer_fire tracer_opt==3 - tracer:fire_smoke # +# ======================================================================================================================== +# Firebrand Spotting for WRF-Fire (Frediani et al 2022) +# ======================================================================================================================== +# +# variables for Firebrand Spotting: fs_[variable_name] +# Note: There is no input stream or restart output for these variables +# Firebrands live for a few minutes, so it does not make sense to restart them +# Restart runs will initialize these variables from zero +# the *only* IO flag is 'h' +# Pls do not modify them. +# ------------------------------------------------------------------------------------------------------------------------ +# Namelist +# +rconfig integer fs_array_maxsize namelist,fire 1 100000 - "fs_array_maxsize" "Max number of active firebrands allowed" +rconfig integer fs_firebrand_gen_lim namelist,fire 1 0 - "fs_firebrand_gen_lim" "Limit number of firebrands generated at any given time (set it to 0 to turn spotting off)" +rconfig integer fs_firebrand_gen_dt namelist,fire 1 5 - "fs_firebrand_gen_dt" "Interval between consecutive generation cycles (dt is a factor applied to the model's time_step)" +rconfig integer fs_firebrand_gen_levels namelist,fire 1 5 - "fs_firebrand_gen_levels" "Number of vertical levels to generate firebrands (levels are distributed between 1 meter AGL and max height)" +rconfig integer fs_firebrand_gen_maxhgt namelist,fire 1 50 - "fs_firebrand_gen_maxhgt" "Max height to generate firebrands (in meters and greater than 1)" +rconfig logical fs_firebrand_gen_levrand namelist,fire 1 .false. - "fs_firebrand_gen_levrand" "Flag to generate firebrands at random heights between 1m AGL and maxhgt" +rconfig integer fs_firebrand_gen_levrand_seed namelist,fire 1 1 - "fs_firebrand_gen_levrand_seed" "Seed used for fs_firebrand_gen_levrand" +rconfig integer fs_firebrand_gen_mom3d_dt namelist,fire 1 4 - "fs_firebrand_gen_mom3d_dt" "Number of timesteps for firebrands to build 3-D momentum after generation (i.e., massless particle transport)" +rconfig real fs_firebrand_gen_prop_diam namelist,fire 1 10.0 - "fs_firebrand_gen_prop_diam" "Firebrand initial proporty: Diameter [mm]" +rconfig real fs_firebrand_gen_prop_effd namelist,fire 1 10.0 - "fs_firebrand_gen_prop_effd" "Firebrand initial proporty: Effective Diameter [mm]" +rconfig real fs_firebrand_gen_prop_temp namelist,fire 1 900.0 - "fs_firebrand_gen_prop_temp" "Firebrand initial proporty: Temperature [K]" +rconfig real fs_firebrand_gen_prop_tvel namelist,fire 1 0.0 - "fs_firebrand_gen_prop_tvel" "Firebrand initial proporty: Terminal Velocity [m/s] (set to zero for top of trajectory)" +rconfig real fs_firebrand_dens namelist,fire 1 513000.0 - "fs_firebrand_dens" "Density of firebrands [g/m3]" +rconfig real fs_firebrand_dens_char namelist,fire 1 299000.0 - "fs_firebrand_dens_char" "Density of char [g/m3]" +rconfig integer fs_firebrand_max_life_dt namelist,fire 1 600 - "fs_firebrand_max_life_dt" "Max lifetime of firebrands (in sec)" +rconfig real fs_firebrand_land_hgt namelist,fire 1 0.15 - "fs_firebrand_land_hgt" "AGL height that firebrands land (in meters)" +rconfig logical fuel_crosswalk namelist,fire max_domains .false. - "flag - artifact from COFPS used in firebrand_spotting - it does not crosswalk between fuel models." +# +# ------------------------------------------------------------------------------------------------------------------------ +# Dimensions +# +dimspec fs_maxsize - namelist=fs_array_maxsize c fs_maxsize +# +# ------------------------------------------------------------------------------------------------------------------------ +# State +#
+# +state integer fs_last_gen_dt - dyn_em 1 - - "fs_last_gen_dt" "cycles since last firebrand generation" "count" +state integer fs_gen_idmax - dyn_em 1 - - "fs_gen_idmax" "highest ID number assigned to particle" "ID" +state logical fs_count_reset - dyn_em 1 - - "fs_count_reset" "flag to reset deposit count" +state real fs_fire_ROSdt *i*j fire 1 z - "fs_fire_ROSdt" "fire rate of spread (on fire refined grid) between generation cycles" +state real fs_fire_area ij misc 1 - h "fs_fire_area" "fire area on meteorological grid" +state real fs_fuel_spotting_risk ij misc 1 - h "fs_fuel_spotting_risk" "fuel risk for spotting likelihood" +state real fs_count_landed_all ij misc 1 - h "fs_count_landed_all" "firebrand count: landed since sim start" +state real fs_count_landed_hist ij misc 1 - h "fs_count_landed_hist" "firebrand count: landed during history interval" +state integer fs_landing_mask ij misc 1 - h "fs_landing_mask" "valid landing gridpoints" +state integer fs_gen_inst ij misc 1 - h "fs_gen_inst" "firebrand number generation - instantaneous" +state real fs_frac_landed ij misc 1 - h "fs_frac_landed" "fraction of firebrand landed (pt/total) during history interval" +state real fs_spotting_lkhd ij misc 1 - h "fs_spotting_lkhd" "fire spotting likelihood" +# +# ------------------------------------------------------------------------------------------------------------------------ +# Particle properties: fs_p_[property_name] +# +state integer fs_p_id {fs_maxsize} dyn_em 1 - - "fs_p_id" "particle unique ID" "ID" +state integer fs_p_dt {fs_maxsize} dyn_em 1 - - "fs_p_dt" "particle active time steps" "count" +state real fs_p_x {fs_maxsize} dyn_em 1 - - "fs_p_x" "particle x position" "index" +state real fs_p_y {fs_maxsize} dyn_em 1 - - "fs_p_y" "particle y position" "index" +state real fs_p_z {fs_maxsize} dyn_em 1 - - "fs_p_z" "particle z position" "index" +state real fs_p_mass {fs_maxsize} dyn_em 1 - - "fs_p_mass" "firebrand mass" "g" +state real fs_p_diam {fs_maxsize} dyn_em 1 - - "fs_p_diam" "firebrand diameter" "mm" +state real fs_p_effd {fs_maxsize} dyn_em 1 - - "fs_p_effd" "firebrand effective diameter" "mm" +state real fs_p_temp {fs_maxsize} dyn_em 1 - - "fs_p_temp" "firebrand temp" "K" +state real fs_p_tvel {fs_maxsize} dyn_em 1 - - "fs_p_tvel" "firebrand terminal velocity" "m/s" +# ------------------------------------------------------------------------------------------------------------------------ +# ======================================================================================================================== +# End of Firebrand Spotting section +# ======================================================================================================================== + + # Fire halo descriptions # halo HALO_FIRE_LFN dyn_em 48:lfn diff --git a/dyn_em/depend.dyn_em b/dyn_em/depend.dyn_em index 0cbbcecb67..310b2ede7b 100644 --- a/dyn_em/depend.dyn_em +++ b/dyn_em/depend.dyn_em @@ -252,6 +252,7 @@ start_em.o: module_bc_em.o \ ../phys/module_diag_zld.o \ ../phys/module_diag_trad_fields.o \ ../phys/module_fr_fire_driver_wrf.o \ + ../phys/module_firebrand_spotting.o \ $(CF) solve_em.o: module_small_step_em.o \ @@ -280,7 +281,8 @@ solve_em.o: module_small_step_em.o \ ../phys/module_microphysics_driver.o \ ../phys/module_microphysics_zero_out.o \ ../phys/module_physics_addtendc.o \ - ../phys/module_checkerror.o + ../phys/module_checkerror.o \ + ../phys/module_firebrand_spotting.o module_convtrans_prep.o : diff --git a/dyn_em/solve_em.F b/dyn_em/solve_em.F index d6fc975b41..5af1767de8 100644 --- a/dyn_em/solve_em.F +++ b/dyn_em/solve_em.F @@ -46,7 +46,7 @@ SUBROUTINE solve_em ( grid , config_flags & ,period_bdy_em_tracer_sub,period_em_da_sub,period_em_hydro_uv_sub & ,period_em_f_sub,period_em_g_sub & ,halo_em_f_1_sub,halo_em_init_4_sub,halo_em_thetam_sub,period_em_thetam_sub & - ,halo_em_d_pv_sub + ,halo_em_d_pv_sub,halo_firebrand_spotting_5_sub #endif USE module_utility ! Mediation layer modules @@ -78,6 +78,7 @@ SUBROUTINE solve_em ( grid , config_flags & USE module_llxy, ONLY : proj_cassini USE module_avgflx_em, ONLY : zero_avgflx, upd_avgflx USE module_cpl, ONLY : coupler_on, cpl_settime, cpl_store_input + USE module_firebrand_spotting, ONLY : firebrand_spotting_em_driver IMPLICIT NONE @@ -4746,6 +4747,54 @@ SUBROUTINE solve_em ( grid , config_flags & ENDIF #endif + +!----------------------------------------------------------------------- +! firebrand spotting (passive Lagrangian particle transport, +! tracks firebrand physics properties) +!----------------------------------------------------------------------- + + IF(config_flags%ifire == 2 .AND. & + ! Check if spotting is on + config_flags%fs_firebrand_gen_lim > 0 .AND. & + ! Check if this is the inner most grid + config_flags%max_dom == grid%id) THEN + +#ifdef DM_PARALLEL + CALL wrf_debug ( 200 , ' call HALO_FIREBRAND_SPOTTING' ) +# include "HALO_FIREBRAND_SPOTTING_5.inc" +#endif + + CALL wrf_debug ( 3 , 'solve: calling firebrand_spotting_em_driver...' ) + CALL firebrand_spotting_em_driver ( & + cf = config_flags, & + grid = grid, & + fs_p_id = grid%fs_p_id, & + fs_p_dt = grid%fs_p_dt, & + fs_p_x = grid%fs_p_x, & + fs_p_y = grid%fs_p_y, & + fs_p_z = grid%fs_p_z, & + fs_gen_inst = grid%fs_gen_inst, & + fs_p_mass = grid%fs_p_mass, & + fs_p_diam = grid%fs_p_diam, & + fs_p_effd = grid%fs_p_effd, & + fs_p_temp = grid%fs_p_temp, & + fs_p_tvel = grid%fs_p_tvel, & + fs_last_gen_dt= grid%fs_last_gen_dt, & + fs_gen_idmax = grid%fs_gen_idmax, & + fs_fire_ROSdt = grid%fs_fire_ROSdt, & + fs_fire_area = grid%fs_fire_area, & + fs_count_landed_all = grid%fs_count_landed_all, & + fs_count_landed_hist = grid%fs_count_landed_hist, & + fs_landing_mask = grid%fs_landing_mask, & + fs_spotting_lkhd = grid%fs_spotting_lkhd, & + fs_frac_landed = grid%fs_frac_landed, & + fs_fuel_spotting_risk = grid%fs_fuel_spotting_risk, & + fs_count_reset = grid%fs_count_reset) + + ENDIF +! end of firebrand spotting +!----------------------------------------------------------------------- + ! Max values of CFL for adaptive time step scheme DEALLOCATE(max_vert_cfl_tmp) diff --git a/dyn_em/start_em.F b/dyn_em/start_em.F index a4449afe14..bb590e37d2 100644 --- a/dyn_em/start_em.F +++ b/dyn_em/start_em.F @@ -29,6 +29,7 @@ SUBROUTINE start_domain_em ( grid, allowed_to_read & USE module_sf_noahmpdrv, ONLY : groundwater_init USE module_lightning_driver, ONLY : lightning_init USE module_fr_fire_driver_wrf, ONLY : fire_driver_em_init + USE module_firebrand_spotting, ONLY : firebrand_spotting_em_init USE module_stoch, ONLY : setup_rand_perturb, rand_seed, update_stoch, initialize_stoch USE module_trajectory, ONLY : trajectory_init #if (WRF_CHEM == 1) @@ -2177,6 +2178,48 @@ SUBROUTINE start_domain_em ( grid, allowed_to_read & ,ips,ipe, kps,kpe, jps,jpe ) CALL wrf_debug ( 100 , 'start_domain_em: After call to fire_driver_em_init' ) + +!----------------------------------------------------------------------- +! fire spotting (passive Lagrangian particle transport, tracks +! firebrand physics properties) +!----------------------------------------------------------------------- + + IF (config_flags%fs_firebrand_gen_lim > 0) THEN + + IF (config_flags%max_dom == grid%id) THEN + + WRITE (message,*) 'SPFire_init: In inner most grid_id: ', grid%id, ' of ', config_flags%max_dom + CALL wrf_debug ( 100 , message) + CALL wrf_debug ( 100 , 'start_em: calling firebrand_spotting_em_init ...' ) + + CALL firebrand_spotting_em_init ( & + grid = grid, & + cf = config_flags, & + fs_p_id = grid%fs_p_id, & + fs_p_dt = grid%fs_p_dt, & + fs_p_x = grid%fs_p_x, & + fs_p_y = grid%fs_p_y, & + fs_p_z = grid%fs_p_z, & + fs_p_mass = grid%fs_p_mass, & + fs_p_diam = grid%fs_p_diam, & + fs_p_effd = grid%fs_p_effd, & + fs_p_temp = grid%fs_p_temp, & + fs_p_tvel = grid%fs_p_tvel, & + fs_last_gen_dt= grid%fs_last_gen_dt, & + fs_gen_idmax = grid%fs_gen_idmax, & + fs_count_landed_all = grid%fs_count_landed_all, & + fs_count_landed_hist = grid%fs_count_landed_hist,& + fs_landing_mask = grid%fs_landing_mask, & + fs_spotting_lkhd = grid%fs_spotting_lkhd, & + fs_frac_landed = grid%fs_frac_landed, & + fs_count_reset = grid%fs_count_reset) + + CALL wrf_debug ( 100 , 'start_em: firebrand_spotting_em_init ok ' ) + ENDIF + + ENDIF +!----------------------------------------------------------------------- + endif #if ( WRFPLUS != 1 ) diff --git a/main/depend.common b/main/depend.common index 15170baa07..a3d96224e6 100644 --- a/main/depend.common +++ b/main/depend.common @@ -893,6 +893,21 @@ module_fire_debug_output.o: \ ../frame/module_configure.o \ ../share/mediation_integrate.o +module_firebrand_spotting_mpi.o: \ + ../frame/module_domain.o \ + ../frame/module_configure.o \ + ../frame/module_dm.o + +module_firebrand_spotting.o: \ + ../frame/module_domain.o \ + ../frame/module_configure.o \ + ../frame/module_dm.o \ + ../frame/module_state_description.o \ + ../frame/module_domain_type.o \ + ../external/esmf_time_f90/module_symbols_util.o \ + ../external/esmf_time_f90/module_utility.o \ + module_firebrand_spotting_mpi.o + module_fdda_spnudging.o :\ ../frame/module_dm.o \ ../frame/module_state_description.o \ diff --git a/phys/Makefile b/phys/Makefile index 77da6da046..714c02af63 100644 --- a/phys/Makefile +++ b/phys/Makefile @@ -207,7 +207,9 @@ FIRE_MODULES = \ module_fr_fire_model.o \ module_fr_fire_core.o \ module_fr_fire_phys.o \ - module_fr_fire_util.o + module_fr_fire_util.o \ + module_firebrand_spotting_mpi.o \ + module_firebrand_spotting.o DIAGNOSTIC_MODULES_EM = \ module_diag_afwa.o \ diff --git a/phys/module_firebrand_spotting.F b/phys/module_firebrand_spotting.F new file mode 100644 index 0000000000..28e6c13022 --- /dev/null +++ b/phys/module_firebrand_spotting.F @@ -0,0 +1,3814 @@ +!------------------------------------------------------------------------------- +! This module was developed in RAL-NCAR/UCAR, 2019-2020 by: +! *** M. Frediani and T. Juliano *** +! This research was supervised by J. Knievel and B. Kosovic. +!------------------------------------------------------------------------------- + +!============================================================= +!============================================================= + +MODULE module_firebrand_spotting + + USE module_domain, ONLY : get_ijk_from_grid, domain ! grid + USE module_configure, ONLY : grid_config_rec_type ! config_flags + USE module_symbols_util, ONLY : WRFU_TimeInterval, WRFU_TimeIntervalGet, WRFU_TimeIntervalSet +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + USE MPI +#endif + + IMPLICIT NONE + + PRIVATE + PUBLIC firebrand_spotting_em_init, firebrand_spotting_em_driver, get_local_ijk + + ! THESE VARIABLES ARE IN MODULE SCOPE ! Careful with reassignments - don't reassign + ! SAVE attribute is default + + !------------------------------------------------------------------------------- + ! variables in module scope: private, only available module-wide (host association) + !------------------------------------------------------------------------------- + ! They should not be declared again in suboutines (may not compile) + ! and must not be among routine dummy arguments. Consequently, cannot be IO variables + ! + ! Runtime variables are not available at module level (e.g., namelist, tile dependent variables). + ! Include here only what can be set during compilation: + ! fixed parameters, allocatables, declarions (without initialization) + + !------------------------------------------------------------------------------- + ! Fixed parameters ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + + INTEGER, PARAMETER :: dp = KIND(0.d0) ! double precision + + INTEGER, PARAMETER :: wrfdbg = 0 + ! Fuel parameters must match values from module_fr_phys.F: SUBROUTINE set_fuel_params(nfuel_cat0, nfuel_cat) + INTEGER, PARAMETER :: no_fuel_cat = 14 + INTEGER, PARAMETER :: nf_sb = 204 ! maximum category on + INTEGER, PARAMETER :: nfuelcats = 53 ! number of fuel categories that are specified + + INTEGER, PARAMETER :: sp_fuel_src_typ_n = 1 + + REAL, PARAMETER :: grav = 9.80616_dp ! gravity (m/s2) + REAL, PARAMETER :: rdry = 287.04_dp ! dry air (J/Kg-K) + REAL, PARAMETER :: p2jm = 100.0_dp ! mb to j/m3 + REAL, PARAMETER :: rcd = 0.45_dp ! drag constant + REAL, PARAMETER :: rd = 287.15_dp ! gas constant dry air (J / kg K) + REAL, PARAMETER :: rv = 461.6_dp + REAL, PARAMETER :: t0 = 300.0_dp + REAL, PARAMETER :: cp = 7.0_dp*rd/2.0_dp + REAL, PARAMETER :: rcp = rd/cp + REAL, PARAMETER :: p1000mb= 100000.0_dp + REAL, PARAMETER :: r1o3 = 1.0_dp/3.0_dp ! ratio 1 over 3 + REAL, PARAMETER :: sboltz = 5.67E-5_dp ! stefan-boltzmann (g / s3-K4) + REAL, PARAMETER :: emiss = 0.9_dp ! emissivity + REAL, PARAMETER :: cpw = 1466.0_dp ! specific heat wood (J / kg-K) + REAL, PARAMETER :: cpc = 712.0_dp ! specific heat char (J / kg-K) + REAL, PARAMETER :: beta0 = 4.8E-7_dp ! burning rate constant (m2 / s) + REAL, PARAMETER :: s_coeff= 110.4_dp ! Sutherland's law coefficient (K) + REAL, PARAMETER :: b_coeff= 1.458E-3_dp ! Sutherland's law coefficient [g / m-s-K-(1/2)] + REAL, PARAMETER :: shmt = 0.7_dp ! schmidt number + REAL, PARAMETER :: thcon = 27.0_dp ! thermal conductivity air + + !USE module_state_description, ONLY: p_qv + !INTEGER, PARAMETER :: p_qv = 1 + REAL, PARAMETER :: pr = 0.7_dp ! Prandtl + + REAL, PARAMETER :: NEGLIGIBLE = 10*EPSILON(1.0) + REAL, PARAMETER :: ZERO_dp = 0.0_dp ! this is a real type variable, not a double precision type + REAL, PARAMETER :: dp05 = 0.5_dp + REAL, PARAMETER :: dp1 = 1.0_dp + + + !------------------------------------------------------------------------------- + ! ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + + ! Mass threshold to consider burnout (g) + REAL, PARAMETER :: br_min_mass = EPSILON(dp1) ! max precision for real(4) + + ! Diameter threshold to consider burnout (mm) + REAL, PARAMETER :: br_min_diam = 0.0000001_dp + + !------------------------------------------------------------------------------- + ! Generic variables for multiple use within module ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + + CHARACTER (LEN=200), SAVE :: msg + CHARACTER (LEN=256), SAVE :: fmt + CHARACTER (LEN=200), DIMENSION(10) :: amsg + INTEGER, SAVE :: imsg ! loop counters + + !------------------------------------------------------------------------------- + ! variables from namelist ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + + ! These will be available to all subroutines and will be set in init routine + INTEGER, SAVE :: fs_array_maxsize ! maximum number of particles carried in simulation + INTEGER, SAVE :: fs_gen_levels ! one per grid pt (may change if releasing at multiple levels) + INTEGER, SAVE :: fs_gen_lim + INTEGER, SAVE :: fs_gen_dt + + REAL, SAVE :: firebrand_dens ! 513000.0_dp ! density of firebrand (g / m3) + REAL, SAVE :: firebrand_dens_char ! 299000.0_dp ! density of char (g m-3) [gupta et al 2002; fuel] + + + !------------------------------------------------------------------------------- + ! Fixed indices, ranks ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + + LOGICAL :: this_is_ideal = .FALSE. + + TYPE p_properties ! fb_prop + REAL :: p_mass ! fbmass + REAL :: p_diam ! fbdiam + REAL :: p_effd ! fbediam + REAL :: p_temp ! fbtemp + REAL :: p_tvel ! fbvelo + END TYPE p_properties + + !------------------------------------------------------------------------------- + ! grid and cf are not here because dimensions are given at runtime (derived types) + ! Also, grid values change with timestep, so they need to be passed as dummy arguments + !------------------------------------------------------------------------------- + + !------------------------------------------------------------------------------- + ! Variable bounds - Initialized in init, used in dummy arguments in driver + ! ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + INTEGER, SAVE :: ids, jds, ide, jde, kde ! domain bounds + INTEGER, SAVE :: ims, jms, ime, jme, kms, kme ! memory bounds + INTEGER, SAVE :: is, ie, js, je, ks, ke ! patch start/end + INTEGER, SAVE :: ifps, jfps, ifpe, jfpe ! refined fire grid bounds + +CONTAINS + +!****************************************************************** +!****************************************************************** +!* * +!* Module Routines * +!* * +!****************************************************************** +!****************************************************************** + + + +!============================================================= + SUBROUTINE firebrand_spotting_em_init ( & +!============================================================= + grid, & + cf, & + fs_p_id, & + fs_p_dt, & + fs_p_x, & + fs_p_y, & + fs_p_z, & + fs_last_gen_dt,& + fs_gen_idmax, & + fs_count_reset,& + fs_p_mass, & + fs_p_diam, & + fs_p_effd,& + fs_p_temp, & + fs_p_tvel, & + fs_count_landed_all,& + fs_count_landed_hist,& + fs_landing_mask,& + fs_spotting_lkhd,& + fs_frac_landed) + +!============================================================= +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + USE module_firebrand_spotting_mpi, ONLY: fs_mpi_init +#endif + !------------------------------------------------------------------------------- + ! Initialize all I/O and variables declared in the registry + !------------------------------------------------------------------------------- + + IMPLICIT NONE + + !------------------------------------------------------------------------------- + ! Arguments + !------------------------------------------------------------------------------- + + TYPE(domain), INTENT(IN) :: grid ! + TYPE(grid_config_rec_type), INTENT(IN) :: cf ! run-time configuration (namelist) for domain + + INTEGER, INTENT(INOUT) :: fs_last_gen_dt, fs_gen_idmax + LOGICAL, INTENT(INOUT) :: fs_count_reset + + ! Firebrand Spotting Particle Properties (fs_p_*) + INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id, fs_p_dt + REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_x, fs_p_y, fs_p_z ! positions are relative to grid edges: particle at grid center point (x,y,z) = (1.5, 1.5, 1.5) + REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_mass, fs_p_diam, fs_p_effd + REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_temp, fs_p_tvel + REAL, INTENT(INOUT), DIMENSION(ims:,jms:) :: fs_count_landed_all, fs_count_landed_hist, fs_spotting_lkhd, fs_frac_landed + INTEGER, INTENT(INOUT), DIMENSION(ims:,jms:) :: fs_landing_mask + + !------------------------------------------------------------------------------- + + !------------------------------------------------------------------------------- + ! Module variables from namelist/default + + fs_array_maxsize = cf%fs_array_maxsize + fs_gen_levels = cf%fs_firebrand_gen_levels + fs_gen_lim = cf%fs_firebrand_gen_lim + fs_gen_dt = cf%fs_firebrand_gen_dt + + fmt = '(A,1x,I6)' + WRITE (amsg(1),*) 'SPFire_init: fspotting_em_init' + WRITE (amsg(2),fmt) 'SPFire_init: firebrand limit =', fs_gen_lim + WRITE (amsg(3),fmt) 'SPFire_init: dt =', fs_gen_dt + WRITE (amsg(4),fmt) 'SPFire_init: fs_array_maxsize =', fs_array_maxsize + WRITE (amsg(5),fmt) 'SPFire_init: fs_gen_levels =', fs_gen_levels + DO imsg=1,5 + CALL wrf_debug (wrfdbg, TRIM(amsg(imsg)) ) + ENDDO + + !------------------------------------------------------------------------------- + ! Set bounds to be used as dummy arguments in driver + ! ***variables declared in MODULE SCOPE*** + !------------------------------------------------------------------------------- + + CALL get_local_ijk(grid, & + ifps=ifps, jfps=jfps, ifpe=ifpe, jfpe=jfpe, & + ids=ids, jds=jds, ide=ide, jde=jde, kde=kde, & + ims=ims, jms=jms, ime=ime, jme=jme, kms=kms, kme=kme, & + ips=is, jps=js, ipe=ie, jpe=je, kps=ks, kpe=ke) + + WRITE (msg,'(6(i6,1x))') is, ie, js, je, ks, ke + CALL wrf_debug (wrfdbg, 'SPFire_init tile bounds: '//msg) + + WRITE (msg,'(6(i6,1x))') ims, ime, jms, jme, kms, kme + CALL wrf_debug (wrfdbg, 'SPFire_init memory bounds: '//msg) + + WRITE (msg,'(4(i6,1x))') ifps, ifpe, jfps, jfpe + CALL wrf_debug (wrfdbg, 'SPFire_init fire refined bounds: '//msg) + + + !------------------------------------------------------------------------------- + ! Initialize registry arrays + !------------------------------------------------------------------------------- + fs_count_reset=.FALSE. + + fs_last_gen_dt = 0 + fs_gen_idmax = 0 + fs_p_id = 0 + fs_p_dt = 0 + fs_p_x = 0.0_dp + fs_p_y = 0.0_dp + fs_p_z = 0.0_dp + fs_p_mass = 0.0_dp + fs_p_diam = 0.0_dp + fs_p_effd = 0.0_dp + fs_p_temp = 0.0_dp + fs_p_tvel = 0.0_dp + + fs_landing_mask(:,:) = 0 + fs_count_landed_all (:,:) = 0.0_dp + fs_count_landed_hist(:,:) = 0.0_dp + fs_spotting_lkhd(:,:) = 0.0_dp + fs_frac_landed(:,:) = 0.0_dp + + !fs_fire_ROSdt(:,:) = 0.0_dp + !fs_gen_inst(:,:) = 0 + + !------------------------------------------------------------------------------- + + IF ( grid%this_is_an_ideal_run ) THEN + + this_is_ideal = .TRUE. + CALL wrf_debug (wrfdbg, 'SPFire_init: Ideal Run detected' ) + + ELSE + + this_is_ideal = .FALSE. + CALL wrf_debug (wrfdbg, 'SPFire_init: Not an Ideal Run' ) + + ENDIF + + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + CALL fs_mpi_init(grid) +#endif + + END SUBROUTINE firebrand_spotting_em_init +!============================================================= +!============================================================= + + +!============================================================= + PURE & + FUNCTION order_val(arr, ord) +!============================================================= + + IMPLICIT NONE + + REAL, INTENT(IN), DIMENSION(:) :: arr + INTEGER, INTENT(IN) :: ord + REAL :: order_val + INTEGER :: diff, besti, iord, i, error + + !----------------------------------------------------------- + ! Return the value in the array at the given order (allowing an error of +-2 positions) + ! E.g.: + ! For an arr with dimension = 100 + ! ord = 50 will return one of the values near the 50% percentile + ! i.e., the value in one of the positions between 48:52 if the array was ranked + ! + ! The algorithm scans the array and + ! counts the number of elements with value below the scanned position + ! It returns when it finds a value where + ! (order - error) <= count <= (order + error) + !----------------------------------------------------------- + + error = 2 + + diff = SIZE(arr) -1 + besti = 1 + + DO i=1,SIZE(arr) + + iord = COUNT(arr > arr(i)) + IF (ABS(ord - iord) <= diff) besti = i + diff = MIN(diff, ABS(ord - iord)) + IF (diff <= error) EXIT + ENDDO + + ! WRITE(msg, '(2(i6,1x))') iord, diff + ! CALL wrf_debug (wrfdbg, 'SPFire_rel_order_val: '//msg) + + order_val = arr(besti) + +END FUNCTION order_val +!============================================================= +!============================================================= + + + +!============================================================= + PURE & + FUNCTION fire2tile(fr_arr, dsx,dsy) RESULT(new_arr) +!============================================================= + + IMPLICIT NONE + + REAL, INTENT(IN), DIMENSION(ifps:ifpe,jfps:jfpe) :: fr_arr + INTEGER, INTENT(IN) :: dsx, dsy + REAL, ALLOCATABLE,DIMENSION(:,:) :: new_arr + INTEGER :: i, j + INTEGER, DIMENSION(2) :: fshp + + !----------------------------------------------------------- + ! Converts a 2-D array from the fire refined grid to tile + ! Tile values are the sum of corresponding refined gridpoints + !----------------------------------------------------------- + + fshp = SHAPE(fr_arr) + + ALLOCATE(new_arr(1:fshp(1)/dsx, 1:fshp(2)/dsy)) + new_arr(:,:) = ZERO_dp + + new_arr(1:fshp(1)/dsx, 1:fshp(2)/dsy) = & + RESHAPE([((& + SUM(fr_arr(i:i+dsx-1,j:j+dsy-1)), & + i=ifps,ifpe-1,dsx), & + j=jfps,jfpe-1,dsy)], & + [fshp(1)/dsx,fshp(2)/dsy]) + + END FUNCTION fire2tile +!============================================================= +!============================================================= + + + +!============================================================= + ELEMENTAL FUNCTION fuel_spotting_risk(fuel_fgi, factor, fuel_mcg)!, fuel_mcg) +!============================================================= + + ! Returns the refined grid array with the fuel_spotting_risk for spotfires + !------------------------------------------------------------------------------- + + ! fuel_name(k) : fuel model name + ! windrf(k) : wind reduction factor from 20ft to midflame height + ! fgi(k) : initial total mass of surface fuel (kg/m**2) + ! fgi_lh(k) : initial total mass of surface live fuel [sb: 1-h] (kg/m**2) + ! fgi_all(k) : initial total mass of surface live fuel [sb: 1-h,10-h,100-h] (kg/m**2) + ! fueldepthm(k) : fuel depth (m) + ! savr(k) : fuel particle surface-area-to-volume ratio, 1/ft + ! fuelmce(k) : moisture content of extinction + ! fueldens(k) : ovendry particle density, lb/ft^3 + ! st(k) : fuel particle total mineral content + ! se(k) : fuel particle effective mineral content + ! weight(k) : weighting parameter that determines the slope of the mass loss curve + ! fci_d(k) : initial dry mass of canopy fuel + ! fct(k) : burn out time for canopy fuel, after dry (s) + ! ichap(k) : 1 if chaparral, 0 if not + ! fci(k) : initial total mass of canopy fuel + ! fcbr(k) : fuel canopy burn rate (kg/m**2/s) + ! hfgl : surface fire heat flux threshold to ignite canopy (w/m^2) + ! cmbcnst : joules per kg of dry fuel + ! fuelheat : fuel particle low heat content, btu/lb + ! fuelmc_g : fuel particle (surface) moisture content + ! fuelmc_c : fuel particle (canopy) moisture content + + IMPLICIT NONE + + REAL :: fuel_spotting_risk + REAL, INTENT(IN) :: factor + REAL, INTENT(IN) :: fuel_fgi, fuel_mcg ! fmcg2df ground dead fuel moisture content (fire grid) + + fuel_spotting_risk = factor * (dp1 - MIN(dp1, fuel_mcg/fuel_fgi)) ! *(fuel_fgi-fuel_mcg) + + END FUNCTION fuel_spotting_risk +!============================================================= +!============================================================= + + + +!============================================================= + ELEMENTAL FUNCTION spotting_threat_factor(fcat) +!============================================================= + IMPLICIT NONE + + INTEGER, INTENT(IN) :: fcat + REAL, DIMENSION(nfuelcats+1) :: factor + REAL :: spotting_threat_factor + + ! **************** read this from external input **************** + ! **************** call it in firebrand_spotting_driver once, when grid%itimestep =1 **************** + ! **************** and load a registry array **************** + + ! User defined coefficient to increase spotfire likelihood; + ! threat = 0 yields zero likelihood + + factor(1) = ZERO_dp ! 1: 'Short grass (1 ft)', + factor(2:13) = 1.0_dp + factor(14) = ZERO_dp ! 14: 'no fuel', + factor(15:nfuelcats+1) = 1.0_dp + + spotting_threat_factor = factor(fcat) + + !CALL read_table_static + + END FUNCTION spotting_threat_factor + +!============================================================= +!============================================================= + + +! !============================================================= +! SUBROUTINE read_table_static ! Not debuggeed - from module_mp_thompson +! !============================================================= + +! USE module_domain +! USE module_dm +! IMPLICIT NONE + +! LOGICAL, EXTERNAL:: wrf_dm_on_monitor +! INTEGER:: iunit_fs1, i +! LOGICAL:: opened +! CHARACTER (LEN=64) :: filename + +! iunit_fs1 = -1 +! IF ( wrf_dm_on_monitor() ) THEN +! DO i = 20,99 +! INQUIRE ( i , OPENED = opened ) +! IF ( .NOT. opened ) THEN +! iunit_fs1 = i +! EXIT +! ENDIF +! ENDDO +! ENDIF + +! #if defined(DM_PARALLEL) && !defined(STUBMPI) +! CALL wrf_dm_bcast_bytes ( iunit_fs1 , IWORDSIZE ) +! #endif +! IF ( iunit_fs1 < 0 ) & +! CALL wrf_error_fatal('SPFire_read_table_static: Cannot find a fortran unit to read file') + +! IF ( wrf_dm_on_monitor() ) THEN + +! WRITE(msg, '(i2,1x,a20)') iunit_fs1, filename +! CALL wrf_debug(wrfdbg,'SPFire_read_table_static: opening table on unit ' // msg) + +! OPEN(iunit_fs1,FILE=tablename, FORM='UNFORMATTED',STATUS='OLD',ERR=9009) + +! ENDIF + +! #define DM_BCAST_MACRO(A) CALL wrf_dm_bcast_bytes(A, size(A)*R4SIZE) + +! IF ( wrf_dm_on_monitor() ) READ(iunit_fs1,ERR=9010) tableval +! #if defined(DM_PARALLEL) && !defined(STUBMPI) +! DM_BCAST_MACRO(tableval) +! #endif + +! RETURN + +! 9009 CONTINUE +! CALL wrf_error_fatal('SPFire_read_table_static: error openinig static table') +! RETURN + +! 9010 CONTINUE +! CALL wrf_error_fatal('SPFire_read_table_static: error reading static table') + + +! END SUBROUTINE read_table_static + +! !============================================================= +! !============================================================= + + +!============================================================= + ELEMENTAL FUNCTION get_fuel_cat(crosswalk, cat) +!============================================================= + + ! Use with Scott & Burgen fuel model (WRF CO-FSP) + + IMPLICIT NONE + + INTEGER :: get_fuel_cat + LOGICAL, INTENT(IN):: crosswalk + REAL, INTENT(IN):: cat + + INTEGER, DIMENSION(1:nf_sb) :: ksb ! Anderson82 + S&B2005 fuel categories array + INTEGER :: i, icat + !------------------------------------------------------------------------------- + + icat = INT(cat) + + ksb = no_fuel_cat ! 14 ! Initialize all values to no-fuel + + ! Anderson 1982 + ksb(1:13) = [(i, i=1, 13)] + + IF ( crosswalk ) THEN ! Scott & Burgan crosswalks to Anderson + + ksb([101, 104, 107])=1 ! Short grass -- 1 + ksb(102)=2 ! Timber grass and understory -- 2 + ksb(121:124) = 2 ! Timber grass and understory -- 2 + ksb([103,105,106,108,109])=3 ! Tall grass -- 3 + ksb([145, 147])=4 ! Chaparral -- 4 + ksb(142)=5 ! Brush -- 5 + ksb([141, 146])=6 ! Dormant Brushi -- 6 + ksb([143, 144, 148, 149])=7 ! Southern Rough -- 7 + ksb([181, 183, 184, 187])=8 ! Compact Timber Litter -- 8 + ksb([182, 186, 188, 189])=9 ! Hardwood Litter -- 9 + ksb(161: 165)=10 ! Timber (understory) -- 10 + ksb([185, 201])=11 ! Light Logging Slash -- 11 + ksb(202)=12 ! Medium Logging Slash -- 12 + ksb([203, 204])=13 ! Heavy Logging Slash -- 13 + + ELSE ! full Scott and Burgan (2005) + + ksb(101:109) = [(i, i=15,23)] ! Grass (GR) + ksb(121:124) = [(i, i=24,27)] ! Grass-Shrub (GS) + ksb(141:149) = [(i, i=28,36)] ! Shrub (SH) + ksb(161:165) = [(i, i=37,41)] ! Timber-Understory (TU) + ksb(181:189) = [(i, i=42,50)] ! Timber litter (TL) + ksb(201:204) = [(i, i=51,54)] ! Slash-Blowdown (SB) + + ENDIF + + get_fuel_cat = ksb(cat) + + END FUNCTION get_fuel_cat +!============================================================= +!============================================================= + + +!============================================================= + ELEMENTAL FUNCTION firebrand_gen_factor(fcat) +!============================================================= + IMPLICIT NONE + + REAL :: firebrand_gen_factor + INTEGER, INTENT(IN) :: fcat + REAL, DIMENSION(nfuelcats+1) :: factor + + + ! **************** read this from external input **************** + ! **************** call it in firebrand_spotting_driver once, when grid%itimestep =1 **************** + ! **************** and load a registry array **************** + + ! User defined coefficient to set release priority + ! factor = 0 yields zero releases from gridpoint + + factor(1:nfuelcats+1)= 1.0_dp + firebrand_gen_factor = factor(fcat) + + END FUNCTION firebrand_gen_factor + +!============================================================= +!============================================================= + + + +! !============================================================= +! ELEMENTAL FUNCTION firebrand_gen_maxhgt(fcat) +! !============================================================= +! IMPLICIT NONE + +! REAL :: firebrand_gen_maxhgt +! INTEGER, INTENT(IN) :: fcat +! REAL, DIMENSION(nfuelcats+1) :: maxhgt + + +! ! **************** read this from external input **************** +! ! **************** call it in firebrand_spotting_driver once, when grid%itimestep =1 **************** +! ! **************** and load a registry array **************** + +! ! User defined max height to release firebrand + +! maxhgt(1:nfuelcats+1)= 50.0_dp +! firebrand_gen_maxhgt = maxhgt(fcat) + +! END FUNCTION firebrand_gen_maxhgt + +!============================================================= +!============================================================= + + + +!============================================================= + ELEMENTAL FUNCTION firebrand_gen_potential(fuel_fgi, factor, fire_rate, fuel_mcg)!, fuel_mcg) +!============================================================= + + ! Returns the refined grid array with the potential for firebrand generation + !------------------------------------------------------------------------------- + + IMPLICIT NONE + + REAL :: firebrand_gen_potential + REAL, INTENT(IN) :: factor, fire_rate + REAL, INTENT(IN) :: fuel_fgi, fuel_mcg ! fmcg2df ground dead fuel moisture content (fire grid) + + firebrand_gen_potential = factor * fire_rate * fuel_fgi !*(dp1 - fuel_mcg/(dp1+fuel_mcg)) ! fuelloadm; dry load from fr_fire_phys + + END FUNCTION firebrand_gen_potential +!============================================================= +!============================================================= + + + +!============================================================= +! PURE & ! can't call wrf_fatal in pure routines + SUBROUTINE generate_firebrands( fs_p_id, fs_p_dt, fs_p_z, fs_p_x, fs_p_y, & + release_i, release_j, release_k, & + active_br, fs_gen_idmax, & + release_prop, fs_p_prop) +!============================================================= + + IMPLICIT NONE + + TYPE(p_properties), INTENT(IN), DIMENSION(:):: release_prop + TYPE(p_properties), INTENT(OUT),DIMENSION(:):: fs_p_prop + + INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id, fs_p_dt + REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_z, fs_p_x, fs_p_y + REAL, INTENT(INOUT), DIMENSION(:) :: release_i, release_j, release_k + INTEGER, INTENT(INOUT) :: fs_gen_idmax + INTEGER, INTENT(OUT) :: active_br + + LOGICAL :: release_true + INTEGER :: br, ii + REAL :: rx, ry, rz + INTEGER :: new_release + LOGICAL, DIMENSION(fs_array_maxsize) :: flag_true + + !------------------------------------------------------------------------------- + release_true = .FALSE. ! scalar: true when brands are ready to be released + flag_true = .FALSE. ! logical array: flag to find active elements in array + + active_br = 0 ! number of active brands + new_release = 0 ! number of brands to release + + ! when all brand IDs of array elements are zero: nothing active + active_br = COUNT( fs_p_id > 0 ) + new_release = COUNT( INT(release_i) > 0 ) + + IF (new_release > 0 ) release_true = .TRUE. ! to-do: include dependency on release_dt + + IF ( .NOT. release_true) THEN + RETURN !CALL wrf_debug (wrfdbg, 'SPFire_generate_firebrands: Found nothing to release') + ENDIF + + !------------------------------------------------------------------------------- + ! New brands fit in array? Release particles: set lat/lon/lev of new brands + !------------------------------------------------------------------------------- + + IF (active_br + new_release <= fs_array_maxsize) THEN + + !------------------------------------------------------------------------------- + ! Add new grid positions to array: create a subroutine for this: brand_release + !------------------------------------------------------------------------------- + + ! Flag is True where positions are available to be filled + WHERE(fs_p_id == 0) flag_true = .TRUE. ! id_indx holds brand ID (unique integer) + + ii = active_br + 1 + DO br=1, new_release + + ! Find an ii position available to store a new release + IF ( .NOT. flag_true(ii) ) THEN ! WRITE (msg,'(3(i6,1x))') ii, active_br, fs_p_id(ii) ! CALL wrf_debug (wrfdbg, 'SPFire_release flag false >>> '// msg) + CALL wrf_error_fatal('SPFire_generate_firebrands: Did not find free index to release brands! Did you pack fs_p_x, fs_p_y, fs_p_id, etc?') + ENDIF + + ! Sanity check + IF (INT(release_i(br)) == 0 .OR. & + INT(release_j(br)) == 0 ) & + CALL wrf_error_fatal('SPFire_generate_firebrands: release_ijk is zero! Positions cannot be zero.') + + IF (fs_gen_idmax + 10 >= HUGE(1)) fs_gen_idmax = 0 + + ! Assign values and convert height to z index + fs_p_x(ii) = release_i(br) + fs_p_y(ii) = release_j(br) + fs_p_z(ii) = release_k(br) + fs_p_id(ii) = fs_gen_idmax + 1 ! Assign new IDmax to particle + fs_p_dt(ii) = 0 ! firebrand has not been advected yet so dt is zero + fs_p_prop(ii)%p_mass = release_prop(br)%p_mass + fs_p_prop(ii)%p_diam = release_prop(br)%p_diam + fs_p_prop(ii)%p_effd = release_prop(br)%p_effd + fs_p_prop(ii)%p_temp = release_prop(br)%p_temp + fs_p_prop(ii)%p_tvel = release_prop(br)%p_tvel + + fs_gen_idmax = fs_p_id(ii) + + flag_true(ii) = .FALSE. + ii = ii + 1 + + ENDDO + + active_br = active_br + new_release + release_i = ZERO_dp + release_j = ZERO_dp + release_k = ZERO_dp + +! WRITE (msg,'(2(i6,1x))') new_release, fs_gen_idmax +! CALL wrf_debug (wrfdbg, 'SPFire_release: Released new brands fs_gen_idmax: '//msg) + +! ELSE + +! WRITE (msg,*) active_br + new_release, fs_array_maxsize +! CALL wrf_debug (wrfdbg, 'SPFire_release: brand array is full, cannot release new brands'// msg) + + ENDIF + + + END SUBROUTINE generate_firebrands + +!============================================================= +!============================================================= + + + + +!============================================================= + PURE & + FUNCTION hgt2k(xp, yp, hgt, z_at_w, znw) +!============================================================= + + !------------------------------------------------------------------------------- + ! Converts height in meters to vertical level + !------------------------------------------------------------------------------- + + IMPLICIT NONE + !INTEGER, INTENT(IN) :: ims, jms + REAL, INTENT(IN) :: hgt, xp, yp + REAL, INTENT(IN), DIMENSION(ims:,:,jms:) :: z_at_w + REAL, INTENT(IN), DIMENSION(:) :: znw + + REAL :: zdz, z_lower_at_p, z_upper_at_p, x0,y0 + REAL :: hgt2k + INTEGER :: k, i, j + + ! An (xp,yp)=(1.5, 1.5) places the particle at the gridpoint center + ! but z_at_w is horizontally unstaggered, + ! so z_at_w(i,j) is at the horizontal grid center when (i,j)=(1,1) + ! + ! Hence, we adjust the particle position to fetch the horizontally adjacent heights for the linear interp. + ! We subtract the deltax between + ! position at grid center and position at grid edge from the particle position xp, yp, such that + ! z_at_w(i,1,j) for (i,j)=(1,1) corresponds to the height at (xp,yp)=(1.5,1.5): + ! (i = xp - 0.5, j = yp - 0.5) = (i=1, j=1), for (xp=1.5, yp=1.5) + + x0 = xp - dp05 + y0 = yp - dp05 + + i = FLOOR(x0) ! shift indices by 0.5 because z_at_w is given at horizontal grid center + j = FLOOR(y0) + + ! Minloc where hgt - z_k is positive: level right below brand release + k = MINLOC(hgt - z_at_w(i,:,j), dim=1, & + mask=( hgt - z_at_w(i,:,j) >= 0.0_dp )) + + z_lower_at_p = u_2d_interp(xp=x0, yp=y0, u_i0j0=z_at_w(i, k,j), u_i0j1=z_at_w(i, k,j+1),& + u_i1j0=z_at_w(i+1,k,j), u_i1j1=z_at_w(i+1,k,j+1)) + + z_upper_at_p = u_2d_interp(xp=x0, yp=y0, u_i0j0=z_at_w(i, k+1,j), u_i0j1=z_at_w(i, k+1,j+1),& + u_i1j0=z_at_w(i+1,k+1,j), u_i1j1=z_at_w(i+1,k+1,j+1)) + + zdz = (hgt - z_lower_at_p) / (z_upper_at_p-z_lower_at_p) + hgt2k = k + zdz + + END FUNCTION hgt2k +!============================================================= +!============================================================= + + + + +!============================================================= + PURE & + SUBROUTINE releaseijk2atm (nij_2d, sr_x, sr_y, fcat, maxhgt_usr, pi, pj, pk, nij) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN), DIMENSION(:,:) :: nij_2d ! do i need ifps here? No + INTEGER, INTENT(IN), DIMENSION(:,:) :: fcat ! do i need ifps here? No + INTEGER, INTENT(IN) :: sr_x, sr_y + REAL, INTENT(IN) :: maxhgt_usr + + REAL, INTENT(INOUT), DIMENSION(:) :: pj, pi, pk + INTEGER, INTENT(INOUT), DIMENSION(:) :: nij + + INTEGER, ALLOCATABLE, DIMENSION(:) :: fpi,fpj + INTEGER :: i, j, cnt + + !------------------------------------------------------------------------------- + ! Returns a 1-D array with the release positions (i, j, k[m]) + ! and number of particles to release (nij) + ! converted from fire refine grid to wrf grid + ! (i,j k are type float and represent the original refined grid position) + !------------------------------------------------------------------------------- + + cnt = COUNT(nij_2d > 0) + + pi(:) = ZERO_dp + pj(:) = ZERO_dp + pk(:) = ZERO_dp + nij(:) = 0 + + ALLOCATE(fpi(cnt), fpj(cnt)) + fpi(:) = 0 + fpj(:) = 0 + + + ! fpi = RESHAPE([( [(i, i=ifps, ifpe)], j=jfps, jfpe)], SHAPE(fr_arr)) + ! fpj = RESHAPE([( [(j, i=ifps, ifpe)], j=jfps, jfpe)], SHAPE(fr_arr)) + + fpi = PACK(RESHAPE([( [(i, i=ifps, ifpe)], j=jfps, jfpe)], SHAPE(nij_2d)), mask=(nij_2d > 0)) + fpj = PACK(RESHAPE([( [(j, i=ifps, ifpe)], j=jfps, jfpe)], SHAPE(nij_2d)), mask=(nij_2d > 0)) + nij = PACK(nij_2d, mask=(nij_2d > 0)) + + !------------------------------------------------------------------------------- + ! convert from refined fire grid to atm grid + !------------------------------------------------------------------------------- + pi = fire2atm_ij(fpi, sr_x ) + pj = fire2atm_ij(fpj, sr_y ) + + ! Release height and firebrand properties can depend on fuel category (in future developments) + ! Because fcat is on the refined grid, hgt and fprop must be specified here + + ! pk = firebrand_gen_maxhgt(PACK(fcat, mask=(nij_2d > 0))) ! Use this to specify hgts for fuel types + pk = maxhgt_usr + + + END SUBROUTINE releaseijk2atm +!============================================================= +!============================================================= + + + +!============================================================= + ELEMENTAL FUNCTION fire2atm_ij (fp_ij, sr) +!============================================================= +! Convert refined subgrid to default atm grid +! Use function separately for i and j: +! pi = fire2atm_ij(fpi, grid%sr_x ) +! pj = fire2atm_ij(fpj, grid%sr_y ) + + IMPLICIT NONE + + REAL :: fire2atm_ij + INTEGER, INTENT(IN) :: fp_ij + INTEGER, INTENT(IN) :: sr + + fire2atm_ij = ( dp1+ REAL(fp_ij - dp1)/REAL(sr) ) + +! fpij = 298, sr = 3: 297/3+1 = 100 +! fpij = 299, sr = 3: 298/3+1 = 100.333 +! fpij = 300, sr = 3: 299/3+1 = 100.666 +! fpij = 301, sr = 3: 300/3+1 = 101 + +! fpij = 297, sr = 4: 296/4+1 = 75.00 +! fpij = 298, sr = 4: 297/4+1 = 75.25 +! fpij = 299, sr = 4: 298/4+1 = 75.50 +! fpij = 300, sr = 4: 299/4+1 = 75.75 +! fpij = 301, sr = 4: 299/4+1 = 76.00 + + END FUNCTION fire2atm_ij +!============================================================= +!============================================================= + + + +!============================================================= + ELEMENTAL FUNCTION atm2fire_ij (pij, sr) +!============================================================= +! Convert default atm grid to lower left index of refined grid +! (atm_i, atm_j) = (f_i : f_i + srx , f_j : f_j + sry) + + IMPLICIT NONE + + INTEGER :: atm2fire_ij + INTEGER, INTENT(IN) :: pij + INTEGER, INTENT(IN) :: sr + + atm2fire_ij = (pij - 1) * sr + 1 + +! pij = 100, sr = 3: 99*3+1 = 298 +! pij = 101, sr = 3: 100*3+1 = 301 + + END FUNCTION atm2fire_ij +!============================================================= +!============================================================= + + + +! !============================================================= +! SUBROUTINE subgrid_fire_property(fire_ij, fuel_frac, grid) +! !============================================================= + +! ! Not used - may be delted from final code version + +! IMPLICIT NONE + +! TYPE(domain), INTENT(IN) :: grid ! input data **Note: Intent IN** +! REAL, INTENT(OUT), DIMENSION(:) :: fuel_frac +! INTEGER, INTENT(IN), DIMENSION(:,:) :: fire_ij + +! INTEGER, ALLOCATABLE, DIMENSION(:) :: fpi, fpj +! INTEGER :: k, cnt, sri, srj, srx, sry + +! srx = grid%sr_x +! sry = grid%sr_y + +! cnt = SIZE(fire_ij(:,1)) +! fpi = atm2fire_ij( fire_ij(:,1), srx) +! fpj = atm2fire_ij( fire_ij(:,2), sry) +! !ALLOCATE( flame(cnt), fuel(cnt)) + +! ! Get flame and fuel_frac maxvals from subgrids +! DO k=1,SIZE(fire_ij(:,1)) + +! sri = fpi(k) +! srj = fpj(k) + +! fuel_frac(k) = MAXVAL(grid%fuel_frac( sri: sri+srx-1, srj : srj+sry-1)) + +! !WRITE (msg,*) MAXVAL(grid%zsf(sri:sri+dsx,srj:srj+dsy)) always zero?? + +! ENDDO + +! END SUBROUTINE subgrid_fire_property +! !============================================================= +! !============================================================= + + + +! !============================================================= +! PURE FUNCTION uniq_ij (fpi, fpj) +! !============================================================= +! ! remove duplicated grid points (after converting from refined fire grid to grid) + +! ! Not used but quite handy - do not delete it. + +! IMPLICIT NONE + +! INTEGER, INTENT(IN), DIMENSION(:) :: fpi, fpj +! INTEGER, ALLOCATABLE, DIMENSION(:,:) :: uniq_ij +! LOGICAL, ALLOCATABLE, DIMENSION(:) :: mask +! INTEGER :: cnt, i + +! cnt = SIZE(fpi) + +! ALLOCATE(mask(cnt)) +! mask = .TRUE. + +! DO i = cnt, 2, -1 +! mask(i) = .NOT.( ANY(fpi(:i-1) == fpi(i) .AND. & +! fpj(:i-1) == fpj(i))) +! END DO + +! ALLOCATE( uniq_ij(COUNT(mask),2) ) +! uniq_ij(:,1) = REAL(PACK(fpi, mask)) +! uniq_ij(:,2) = REAL(PACK(fpj, mask)) + +! END FUNCTION uniq_ij + +! !============================================================= +! !============================================================= + + + +!============================================================= + SUBROUTINE prep_release_hgt(release_i, release_j, release_k, release_n, release_prop, levrand_seed) +!============================================================= + + !---------------------------------------------------------- + ! Set release locations for new particles + ! and distribute particles over fs_gen_levels heights + !---------------------------------------------------------- + + IMPLICIT NONE + + TYPE(p_properties), INTENT(INOUT), DIMENSION(:) :: release_prop + REAL, INTENT(INOUT), DIMENSION(:) :: release_i, release_j + REAL, INTENT(INOUT), DIMENSION(:) :: release_k + INTEGER, INTENT(IN), DIMENSION(:) :: release_n + INTEGER, INTENT(IN) :: levrand_seed + + INTEGER :: ii, kk, cnt ! counters + INTEGER :: nseeds + REAL, ALLOCATABLE, DIMENSION(:) :: rand_arr, frachgt + INTEGER, ALLOCATABLE, DIMENSION(:) :: seeds + + !------------------------------------------------------------------------------- + ! Calculate number of releases and heights from this fire + !------------------------------------------------------------------------------- + + ALLOCATE(frachgt(SIZE(release_n)*fs_gen_levels)) + ALLOCATE(rand_arr(SIZE(release_n)*fs_gen_levels)) + + IF (fs_gen_levels == 1) THEN + frachgt(:) = dp1 + ELSE + frachgt(:) = [( [(REAL(ii-1)* (dp1/REAL(fs_gen_levels-1)), ii=fs_gen_levels, 1, -1)], & + kk=1,SIZE(release_n))] + ENDIF + + IF (levrand_seed > 0) THEN + + nseeds = SIZE(rand_arr) + CALL random_seed(size=nseeds) + ALLOCATE(seeds(nseeds)) + seeds = [(( release_i(ii) * release_j(ii) * levrand_seed * kk, & + kk=1, fs_gen_levels -1), & + ii=1, SIZE(release_n))] ! force seed to vary across space + CALL random_seed(put = seeds) + DEALLOCATE(seeds) + CALL random_number(rand_arr) + + ENDIF + + + ii = SIZE(release_n)+1 ! array index for release at various height (append to end of the array) + DO cnt=1,SIZE(release_n) ! loop over release gridpoints and set a new release at the same ij for 1 < hgt < release_k + + DO kk=1, fs_gen_levels -1 ! release at multiple levels + + !------------------------------------------------------------------------------- + ! Fill release array + !------------------------------------------------------------------------------- + + release_i(ii) = release_i(cnt) ! ii increments inside this loop + release_j(ii) = release_j(cnt) + release_k(ii) = (release_k(cnt)-dp1)*frachgt(ii)+dp1 ! keep a minimum height of 1-m + + ! if we want to perturb the max_release_hgt in releases_k(cnt), we can change its value now + IF (levrand_seed > 0) & + release_k(ii) = (release_k(cnt)-dp1) * rand_arr(ii) + dp1 ! keep a minimum height of 1-m + + !release_prop(cnt) = firebrand_property(pprop(cnt)) + release_prop(ii) = release_prop(cnt) + + ! IF (cnt < 3) THEN + ! WRITE (msg,'(3(i4,1x),5(f8.2,1x))') cnt, kk, ii, & + ! release_i(ii), release_j(ii), release_k(ii), release_k(cnt), rand_arr(ii) + ! CALL wrf_debug (wrfdbg, 'SPFire_prep_release c,k,i, ijk: '//msg) + ! ENDIF + + ii = ii + 1 ! increment release array index + + ENDDO + + ENDDO + + END SUBROUTINE prep_release_hgt +!============================================================= +!============================================================= + + + +!============================================================= + FUNCTION firebrand_property(arr) RESULT(prop) +!============================================================= + + IMPLICIT NONE + + TYPE (p_properties) :: prop + REAL, INTENT(IN), DIMENSION(:) :: arr + REAL, PARAMETER :: pi = 4.0_dp * ATAN (1.0_dp) + + prop%p_diam = arr(1) ! 10.0_dp ! mm + prop%p_effd = arr(2) ! 10.0_dp ! mm + prop%p_temp = arr(3) ! 900.0_dp ! K + ! termvel should start with 0m/s because firebrand is released at top of trajectory + prop%p_tvel = arr(4) ! 0.0_dp ! m/s + prop%p_mass = (firebrand_dens * pi * (prop%p_effd/1000.0_dp)**2)/6.0_dp ! g + + END FUNCTION firebrand_property + +!============================================================= +!============================================================= + + + +!============================================================= + FUNCTION idx_packed_1d(mask) RESULT(mask_idx) +!============================================================= + + ! Return an array of indices where mask is true + IMPLICIT NONE + LOGICAL, INTENT(IN), DIMENSION(:) :: mask + !INTEGER, INTENT(IN) :: active_br + INTEGER, ALLOCATABLE, DIMENSION(:) :: mask_idx + INTEGER :: nresets, ii + + nresets = COUNT(mask) + ALLOCATE(mask_idx(nresets)) + mask_idx = PACK([(ii, ii=1,SIZE(mask))], mask) ! Get flag indices + + END FUNCTION idx_packed_1d +!============================================================= +!============================================================= + + + + +!============================================================= + PURE & + SUBROUTINE firebrand_physics(dt, hgt, loc_p, loc_t, loc_d, loc_w, fbprop) +!============================================================= + + IMPLICIT NONE + + TYPE(p_properties), INTENT(INOUT) :: fbprop + REAL, INTENT(IN) :: loc_p, loc_t, loc_d, loc_w ! local pressure, temp, density, w + REAL, INTENT(IN) :: dt + REAL, INTENT(INOUT) :: hgt + !REAL, INTENT(INOUT) :: p_mass, p_diam, p_effd, p_temp, p_tvel + + !------------------------------------------------------------------------------- + ! firebrand burnout + !------------------------------------------------------------------------------- + + ! WRITE (msg,'(5(f12.8,1x))') fbprop%p_mass, fbprop%p_diam, & + ! fbprop%p_effd, fbprop%p_temp, fbprop%p_tvel + ! CALL wrf_debug (wrfdbg, 'SPFire br physics1 m, d, e, t, v: '//msg) + + CALL burnout(p_mass = fbprop%p_mass, & ! firebrand mass (g) + p_diam = fbprop%p_diam, & ! firebrand diameter (mm) + p_effd = fbprop%p_effd, & ! firebrand effective diameter (mm) + p_temp = fbprop%p_temp, & ! firebrand temperature (K) + p_tvel = fbprop%p_tvel, & ! firebrand terminal velocity (m/s) + aird = loc_d, & ! g/m3 + pres = loc_p, & ! Pa + temp = loc_t, & ! K + loc_w = loc_w, & ! m/s + dt = dt) + +! WRITE (msg,'(4(f12.8,1x))') fbprop%p_mass, fbprop%p_diam, & +! fbprop%p_effd, fbprop%p_temp +! CALL wrf_debug (wrfdbg, 'SPFire br physics2 m, d, e, t: '//msg) + + !------------------------------------------------------------------------------- + ! firebrand terminal velocity + !------------------------------------------------------------------------------- + + CALL termvel(p_diam = fbprop%p_diam, & + p_effd = fbprop%p_effd,& + p_temp = fbprop%p_temp, & + p_tvel = fbprop%p_tvel, & + aird = loc_d, & ! g/m3 + pres = loc_p, & ! Pa + temp = loc_t, & ! K + dt = dt, & ! sec + hgt = hgt) ! m + + ! WRITE (msg,'(6(f12.8,1x))') fbprop%p_mass, fbprop%p_diam, & + ! fbprop%p_effd, fbprop%p_temp, fbprop%p_tvel, hgt + ! CALL wrf_debug (wrfdbg, 'SPFire br physics2 m,d,e,t,v,h : '//msg) + + ! ********** + ! NOTE: + ! Should we calculate in double precision when diam is in meters? + ! double: real(8), 15 precision digits + ! single: real(4), 7 precision digits + + END SUBROUTINE firebrand_physics + +!============================================================= +!============================================================= + + + +!============================================================= + ELEMENTAL SUBROUTINE burnout(pres, aird, temp, loc_w, dt, p_mass, p_diam, p_effd, p_temp, p_tvel) +!============================================================= + +!------------------------------------------------------------------------------- +! This subroutine was developed in RAL-NCAR/UCAR, 2019-2020 by: +! *** T. W. Juliano *** +! based on +! Tse & Fernandez-Pello 1998 (DOI: 10.1016/ S0379-7112(97)00050-7) +! Bhutia, Jenkins & Sun 2010 (DOI:10.3894/JAMES.2010.2.4) +!------------------------------------------------------------------------------- + + IMPLICIT NONE + + REAL, INTENT(IN) :: pres, aird, temp, loc_w, dt + REAL, INTENT(INOUT) :: p_mass ! firebrand mass (g) + REAL, INTENT(INOUT) :: p_diam ! firebrand diameter (mm) + REAL, INTENT(INOUT) :: p_effd ! firebrand effective diameter (mm) + REAL, INTENT(INOUT) :: p_temp ! firebrand temperature (K) + REAL, INTENT(IN) :: p_tvel ! new firebrand terminal velocity (m/s) + + !------------------------------------------------------------------------------- + ! Constants are at module top + !------------------------------------------------------------------------------- + + REAL, PARAMETER :: pi = 4.0_dp * ATAN (1.0_dp) + + REAL :: aird2, wind, gama + REAL :: pdia, pedia + REAL :: p_effd2, pdia_new4, reyn, beta, deff, dmvc, dmvc2 + REAL :: parta, partb, dtemp, nuss, hbar, fbvol, qcon, qrad, partc + REAL :: pratio, cpmix + + p_mass = MAX(p_mass, EPSILON(dp1)) + !------------------------------------------------------------------------------- + + ! air density surrounding firebrand + aird2 = 1000.0_dp * pres / ( rd * p_temp ) + + ! Assume vel diff between firebrand and wind is due only to + ! vertical; that is, assume particle moves with horiz wind + wind = ABS(loc_w - p_tvel) + + ! particle diameter (mm) to (m) + pdia = p_diam / 1000.0_dp + + ! particle effective diameter (mm) to (m) + pedia = p_effd / 1000.0_dp + + ! local atmospheric dynamic viscosity according to Sutherland's law + dmvc = (b_coeff * temp**1.5_dp) / (temp + s_coeff) + + ! dynamic viscosity surrounding firebrand according to Sutherland's law + dmvc2 = (b_coeff * p_temp**1.5_dp) / (p_temp + s_coeff) + + ! kinematic viscosity average for atmosphere and firebrand + gama = ((dmvc / aird) + (dmvc2 / aird2))/2.0_dp + + ! reynolds number + reyn = wind * pdia / gama + + ! nusselt number + nuss = 2.0_dp + 0.6_dp * reyn**(dp05) * pr**(r1o3) + + ! convection heat transfer coefficient + hbar = nuss * thcon / pdia + + ! burning rate + beta = beta0 + beta0 * (0.276_dp * reyn**(dp05) * shmt**(r1o3)) + + ! WRITE (msg,'(9(f12.8,1x),f12.3,1x,f12.8)') aird2, wind, pdia, & + ! pedia, dmvc, dmvc2, & + ! gama, reyn, nuss, hbar, beta + ! CALL wrf_debug (wrfdbg, 'SPFire burnout1: '//msg) + + ! change in firebrand mass diameter (m) + parta = pdia**4 + partb = SQRT(3.0_dp) * (beta**2) * (dt**2) + pdia_new4 = parta - partb + p_diam = pdia_new4**(0.25_dp) + + ! change in firebrand effective mass diameter (m) + p_effd2 = (pedia**2) - beta * dt + p_effd = SQRT(p_effd2) + + ! new firebrand mass (g) + p_mass = (firebrand_dens * pi * p_effd2 * p_effd)/6.0_dp + + ! new firebrand volume (m3) + fbvol = p_mass / firebrand_dens + + ! new firebrand temp (K) + qcon = hbar * (p_temp - temp) + qrad = sboltz * emiss * (p_temp**4 - temp**4) + pratio = p_effd / p_diam + cpmix = (pratio * cpw) + ((1.0_dp - pratio) * cpc) + partc = 6.0_dp / (p_mass * cpmix * p_diam) + dtemp = dt * fbvol * partc * (qcon + qrad) + p_temp = p_temp - dtemp + + ! WRITE (msg,'(8(f14.12,1x))') parta, partb, pdia_new4, p_diam, p_effd2, p_effd, p_mass, fbvol + ! CALL wrf_debug (wrfdbg, 'SPFire burnout2: '//msg) + ! WRITE (msg,'(2(f12.3,1x),f12.8,1x,2(f12.3,1x),2(f12.8,1x))') qcon, qrad, pratio, cpmix, partc, dtemp, p_temp + ! CALL wrf_debug (wrfdbg, 'SPFire burnout3: '//msg) + + ! firebrand diameter from (m) to (mm) + p_diam = 1000.0_dp * p_diam + p_effd = 1000.0_dp * p_effd + + END SUBROUTINE burnout + +!============================================================= +!============================================================= + + + +!============================================================= + ELEMENTAL & + SUBROUTINE termvel(p_diam, p_effd, p_tvel, p_temp, hgt, dt, pres, aird, temp) +!============================================================= + +!------------------------------------------------------------------------------- +! This subroutine was developed in RAL-NCAR/UCAR, 2019-2020 by: +! *** T. W. Juliano *** +! based on Bhutia, Jenkins & Sun 2010 (DOI:10.3894/JAMES.2010.2.4) +!------------------------------------------------------------------------------- + + ! Constants are at module top + + IMPLICIT NONE + + REAL, INTENT(IN) :: pres, aird, temp, dt + REAL, INTENT(IN) :: p_diam ! firebrand diameter (mm) + REAL, INTENT(IN) :: p_effd ! firebrand effective diameter (mm) + REAL, INTENT(IN) :: p_temp ! firebrand temperature (K) + REAL, INTENT(INOUT):: hgt ! particle position at height agl (m) + REAL, INTENT(INOUT):: p_tvel ! new firebrand terminal velocity (m/s) + + REAL :: pbot, ptop, aird2, pdia, parta, partb + REAL :: drop, vt, pratio ! fbtype, + + !------------------------------------------------------------------------------- + + ! air density around particle + aird2 = 1000.0_dp * pres / (rd * p_temp) + + ! particle diameter (mm) to (m) + pdia = p_diam /1000.0_dp + + ! terminal velocity + pratio = p_effd / p_diam + parta = ( (pratio*firebrand_dens) + ( (dp1-pratio) * firebrand_dens_char) ) *pdia*grav + partb = 3.0_dp * ((aird + aird2) / 2.0_dp)*rcd + + pratio = MAX( MIN(HUGE(1.0),(parta / partb)), TINY(1.0)) ! Too large (small) values for the float type size leads to seg fault in debug mode + + ! WRITE (msg,'(4(f12.6,1x))') parta, partb, pratio, SQRT(pratio) + ! CALL wrf_debug (wrfdbg, 'SPFire termvel : '//msg) + + vt = SQRT(pratio) + + ! change in vertical position due to settling + drop = vt * ABS(dt) + pbot = MAX(0.0_dp, hgt-drop) + hgt = pbot + + p_tvel=vt + + END SUBROUTINE termvel + +!============================================================= +!============================================================= + + +!============================================================= + PURE & + SUBROUTINE get_local_met(xp, yp, zp, loc_p, loc_d, loc_t, p, t, d, ihs, jhs, ihe, jhe) +!============================================================= + + !------------------------------------------------------------------------------- + ! + ! Calculate met from variables updated in HALO after the call to microphysics: + ! + ! HALO_EM_E_5:48: u_1,u_2,v_1,v_2,w_1,w_2,t_1,t_2,ph_1,ph_2,tke_1,tke_2,;4:mu_1,mu_2 + + + + IMPLICIT NONE + + !TYPE(domain), INTENT(IN) :: grid + INTEGER, INTENT(IN) :: ihs, jhs, ihe, jhe ! tile +- halo array bounds + REAL, INTENT(IN) :: xp, yp, zp ! particle position + REAL, INTENT(OUT) :: loc_p, loc_t, loc_d ! local met properties + REAL, INTENT(IN), DIMENSION(ims:,kms:,jms:):: p,t,d + + INTEGER :: i, j, k , kk + REAL :: tmp1, tmp2, zph ! corresponding zp at half levels + + k = FLOOR(zp) + j = MIN(MAX(FLOOR(yp-dp05), jhs), jhe) + i = MIN(MAX(FLOOR(xp-dp05), ihs), ihe) + + ! physics variables with (i,j) = (1,1) corresponds to (x,y) = (1.5,1.5) + ! T(i=1): x=1.5 + ! T(i=2): x=2.5 + ! To interpolate T(i=1:2), x=[1.5:2.5[ + ! See comment on function hgt2k + + ! boundaries ims, ..., ke are declared in module scope + + ! p_hydrostatic is on full levels: no vertical (de)staggering needed + loc_p = u_3d_interp( x=xp-dp05, y=yp-dp05, z=zp, & + u_i0j0_bot = p(i, k, j), u_i0j1_bot = p(i, k, j+1),& + u_i1j0_bot = p(i+1,k, j), u_i1j1_bot = p(i+1,k, j+1),& + u_i0j0_top = p(i, k+1, j), u_i0j1_top = p(i, k+1,j+1),& + u_i1j0_top = p(i+1,k+1, j), u_i1j1_top = p(i+1,k+1,j+1)) + + ! theta moist is on full levels: no vertical (de)staggering needed: th8w + loc_t = u_3d_interp( x=xp-dp05, y=yp-dp05, z=zp, & + u_i0j0_bot = t(i, k, j), u_i0j1_bot = t(i, k, j+1),& + u_i1j0_bot = t(i+1,k, j), u_i1j1_bot = t(i+1,k, j+1),& + u_i0j0_top = t(i, k+1, j), u_i0j1_top = t(i, k+1,j+1),& + u_i1j0_top = t(i+1,k+1, j), u_i1j1_top = t(i+1,k+1,j+1)) + + ! Variables on half levels: provide the k index corresponding to particle bottom & top levels + ! Is a linear interpolation ok? + + ! when zp = 1.0, particle is at the surface on the stag grid, + ! and at k=0.5 on half levels (below the first level) + ! when zp = 1.5, particle is in the middle of the stag grid, and at k=1.0 on half level + ! This is the location where the unstaggered variable value is valid, + ! so the distance between the particle and this point must be 0. + + ! Physics variables are all unstaggered in x and y. + + zph = zp - dp05 + k = MAX(1, FLOOR(zph)) + + loc_d = u_3d_interp( x=xp-dp05, y=yp-dp05, z=zph, & + u_i0j0_bot = d(i, k, j), u_i0j1_bot = d(i, k, j+1),& + u_i1j0_bot = d(i+1,k, j), u_i1j1_bot = d(i+1,k, j+1),& + u_i0j0_top = d(i, k+1, j), u_i0j1_top = d(i, k+1,j+1),& + u_i1j0_top = d(i+1,k+1, j), u_i1j1_top = d(i+1,k+1,j+1)) + + loc_d = loc_d * 1000.0_dp ! Convert from kg/m3 to g/m3 + + + END SUBROUTINE get_local_met + +!============================================================= + +!============================================================= + + + +!============================================================= + SUBROUTINE firebrand_spotting_em_driver( & +!============================================================= + cf, & + grid, & + fs_p_id, & + fs_p_dt, & + fs_p_x, & + fs_p_y, & + fs_p_z, & + fs_gen_inst, & + fs_p_mass, & + fs_p_diam, & + fs_p_effd, & + fs_p_temp, & + fs_p_tvel, & + fs_last_gen_dt, & + fs_gen_idmax, & + fs_fire_ROSdt, & + fs_fire_area, & + fs_count_landed_all, & + fs_count_landed_hist, & + fs_landing_mask, & + fs_spotting_lkhd, & + fs_frac_landed, & + fs_fuel_spotting_risk, & + fs_count_reset) + + + !------------------------------------------------------------------------------- + ! Modules + !------------------------------------------------------------------------------- + + USE module_domain, ONLY: domain_get_time_since_sim_start, & + domain_clock_get, is_alarm_tstep + USE module_domain_type, ONLY: HISTORY_ALARM, restart_alarm, AUXHIST23_ALARM + USE module_state_description, ONLY: p_qv, num_moist, param_first_scalar + USE module_utility, ONLY: WRFU_Alarm !WRFU_Clock, +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + USE module_firebrand_spotting_mpi, ONLY: fs_mpi_init, & + fs_mpi_sendbuff1_int, & + fs_mpi_sendbuff1_real, & + fs_mpi_recvbuff1_int, & + fs_mpi_recvbuff1_real, & + fs_mpi_nothing2send, & + fs_mpi_send2neighbors, & + fs_mpi_sendbuffsize, & + fs_mpi_sendbuff_int, & + fs_mpi_sendbuff_real, & + fs_mpi_recvbuffsize, & + fs_mpi_recvfrompatch_int, & + fs_mpi_recvfrompatch_real, & + fs_mpi_sendbuff_real, & + fs_mpi_checkreceive, & + fs_mpi_recv + USE module_firebrand_spotting_mpi, ONLY: neighbors, my_id, task_id, mpiprocs, & + left_id, right_id, up_id, down_id, & + upleft_id, upright_id, downleft_id, downright_id +#endif + + + IMPLICIT NONE +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + LOGICAL, EXTERNAL :: wrf_dm_on_monitor +#endif + !------------------------------------------------------------------------------- + ! WRF variables + !------------------------------------------------------------------------------- + + TYPE(domain), INTENT(IN) :: grid ! input data **Note: Intent IN** + TYPE (GRID_config_rec_type),INTENT(IN) :: cf ! type available by module host association + + !------------------------------------------------------------------------------- + ! Dummy Arguments + !------------------------------------------------------------------------------- + + LOGICAL, INTENT(INOUT) :: fs_count_reset + INTEGER, INTENT(INOUT) :: fs_last_gen_dt, fs_gen_idmax + + ! Firebrand Spotting Particle Properties (fs_p_*) + INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id, fs_p_dt + REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_x, fs_p_y, fs_p_z ! positions are relative to grid edges: particle at grid center point (x,y,z) = (1.5, 1.5, 1.5) + REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_mass, fs_p_diam, fs_p_effd + REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_temp, fs_p_tvel + REAL, INTENT(INOUT), DIMENSION(ims:ime,jms:jme) :: fs_count_landed_all, fs_fire_area, fs_fuel_spotting_risk ! Use memory bounds so positons match indices + REAL, INTENT(INOUT), DIMENSION(ims:ime,jms:jme) :: fs_count_landed_hist, fs_spotting_lkhd, fs_frac_landed + INTEGER, INTENT(INOUT), DIMENSION(ims:ime,jms:jme) :: fs_landing_mask, fs_gen_inst + REAL, INTENT(INOUT), DIMENSION(ifps:ifpe,jfps:jfpe) :: fs_fire_ROSdt + + !------------------------------------------------------------------------------- + ! Parameters + !------------------------------------------------------------------------------- + + !------------------------------------------------------------------------------- + ! Local Variables - Arrays + !------------------------------------------------------------------------------- + + ! Subroutine Control + LOGICAL, DIMENSION(fs_array_maxsize) :: sparse_mask + + TYPE(p_properties), DIMENSION(fs_array_maxsize) :: fs_p_prop + + ! Physics from WRF + REAL, DIMENSION(ims:ime, ks:ke, jms:jme):: w, z, p_hyd, dz8w, z_at_w ! z_at_w: height at interface of mdl level + REAL, DIMENSION(ims:ime, ks:ke, jms:jme):: u, v, rho, th_phy ! *_phy: half levels + REAL, DIMENSION(ims:ime, ks:ke, jms:jme):: qtot, th8w, p_phy, p8w ! *8w: full levels + REAL, DIMENSION(ims:ime, jms:jme) :: msft, w1 + REAL, DIMENSION(ks:ke) :: znw + REAL, ALLOCATABLE, DIMENSION(:) :: xout, yout, zout ! firebrand position after advection + + !------------------------------------------------------------------------------- + ! Likelihood + !------------------------------------------------------------------------------- + + REAL, ALLOCATABLE, DIMENSION(:,:) :: fuel_spotting_risk_fr, spotthreat_fr ! only need it for history intervals + + ! only need it in hist interval - do not hold memory + LOGICAL, ALLOCATABLE, DIMENSION(:,:) :: landing_mask + INTEGER, ALLOCATABLE, DIMENSION(:) :: landing_cnt + INTEGER, ALLOCATABLE, DIMENSION(:) :: np_landed_cnt, np_nlanded ! (MPI: only Master allocates these) + REAL, ALLOCATABLE, DIMENSION(:) :: np_landed_risk, np_frac_landed, np_lkhd ! (MPI: only Master allocates these) + REAL, ALLOCATABLE, DIMENSION(:) :: landed_risk, recv_spot_lkhd, recv_frac_landed + REAL, ALLOCATABLE, DIMENSION(:) :: tmp1 + INTEGER :: ndep, ndep_total + !------------------------------------------------------------------------------- + + REAL, DIMENSION(ims:ime, jms:jme) :: dt_sum_landed ! sum timestep's deposited fb in 2-D memory array + LOGICAL, SAVE :: hist_flag = .FALSE. ! time of history output + + !------------------------------------------------------------------------------- + ! Release + !------------------------------------------------------------------------------- + + INTEGER, ALLOCATABLE, DIMENSION(:) :: np_Ngrdpts, loc_ij + REAL, ALLOCATABLE, DIMENSION(:) :: np_MeanPot + REAL, ALLOCATABLE, DIMENSION(:,:) :: RelPot, fcat_factor, fr_rate_dummy + INTEGER, ALLOCATABLE, DIMENSION(:,:) :: fcat_fr, n_rel_fr2d + LOGICAL, ALLOCATABLE, DIMENSION(:,:) :: rel_mask + + INTEGER :: Ngrdpts, Npts_total, relpts, rankpts + REAL :: TotPot, MeanPot, PotThr, rel_ratio, cdf_coeff, LimPot + + ! Firebrand Release and Transport + TYPE(p_properties), ALLOCATABLE, DIMENSION(:) :: release_prop + REAL, ALLOCATABLE, DIMENSION(:) :: release_i, release_j, release_k + INTEGER, ALLOCATABLE, DIMENSION(:) :: release_n + + !------------------------------------------------------------------------------- + ! Local Variables - Scalars + !------------------------------------------------------------------------------- + + ! Note: + ! Variables for tile, memory, and domain bounds are in module scope & are assigned in firebrand_spotting_init + + INTEGER :: kk, pp, ii ! counters + INTEGER :: kk1, kk2, k_end + INTEGER :: active_br, prior_br, nresets, idmax, firepts, nbr_sum + REAL :: rdx, rdy, dt, sr_xy ! grid length (1/dx) and wrf's time-step + + INTEGER :: firebrand_max_life_dt + REAL :: firebrand_land_hgt, firebrand_gen_maxhgt + LOGICAL :: firebrand_gen_levrand + REAL :: firebrand_gen_prop_diam, firebrand_gen_prop_effd + REAL :: firebrand_gen_prop_temp, firebrand_gen_prop_tvel + INTEGER :: levrand_seed + + !------------------------------------------------------------------------------- + ! MPI local Variables (some variables are in module scope) + !------------------------------------------------------------------------------- + + INTEGER, SAVE :: MasterId = 0 + INTEGER, SAVE :: ntiles = 0 + LOGICAL, SAVE :: IamMaster = .FALSE. + + INTEGER, ALLOCATABLE, DIMENSION(:) :: nbr_id, r_id, r_dt + REAL, ALLOCATABLE, DIMENSION(:) :: r_x, r_y, r_z + + ! MPI receive p_properties + REAL, ALLOCATABLE, DIMENSION(:) :: r_p_m,r_p_d,r_p_e,r_p_t,r_p_v + TYPE(p_properties), ALLOCATABLE, DIMENSION(:) :: fb_mdetv + + + !------------------------------------------------------------------------------- + ! It was a dummy variable back when fuel moisture was not implemented in wrf-v4.0.1 + !------------------------------------------------------------------------------- + + REAL, DIMENSION(ifps:ifpe,jfps:jfpe) :: fmcg_2df + + !=============================================================================== + ! Begin + !=============================================================================== + + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + IF (mpiprocs == 0) & + CALL fs_mpi_init(grid) ! should be called in fspotting_init (?) + + ntiles = mpiprocs + IF ( wrf_dm_on_monitor() ) THEN + IamMaster = .TRUE. + MasterId = my_id + ENDIF +#else + IamMaster = .TRUE. ! initialized with FALSE. It should be TRUE in serial compilation + ntiles = 1 ! used to allocate the arrays for incomming mpi comms. ntiles=1 in serial builds +#endif + + hist_flag = .FALSE. + IF ( Is_alarm_tstep(grid%domain_clock, grid%alarms(HISTORY_ALARM)) ) hist_flag = .TRUE. + !IF ( Is_alarm_tstep(grid%domain_clock, grid%alarms(AUXHIST23_ALARM)) ) hist_flag = .TRUE. + IF (hist_flag) & + CALL wrf_debug (wrfdbg, 'SPFire_driver: History output on next tstep') + + fs_last_gen_dt = fs_last_gen_dt + 1 + active_br = COUNT( fs_p_id > 0 ) + + IF (grid%itimestep == 1) & + fs_fire_ROSdt(ifps:ifpe,jfps:jfpe) =0.0_dp + + !=============================================================================== + ! Reset array after writting history file + !=============================================================================== + + IF (fs_count_reset) THEN ! if true + !CALL wrf_debug (wrfdbg, 'SPFire_lkhd: resetting fs_count_reset!') + fs_count_landed_hist(:,:) = ZERO_dp + fs_fuel_spotting_risk(:,:) = ZERO_dp + fs_count_reset = .FALSE. + + fs_gen_inst(is:ie,js:je) = 0 + ENDIF + + fs_landing_mask(ims:ime,jms:jme) = 0 + + !=============================================================================== + ! Assign properties to derived type (used locally to shorten argument list) + !=============================================================================== + + fs_p_prop%p_mass = ZERO_dp + fs_p_prop%p_diam = ZERO_dp + fs_p_prop%p_effd = ZERO_dp + fs_p_prop%p_temp = ZERO_dp + fs_p_prop%p_tvel = ZERO_dp + + fs_p_prop(:active_br)%p_mass = fs_p_mass(:active_br) + fs_p_prop(:active_br)%p_diam = fs_p_diam(:active_br) + fs_p_prop(:active_br)%p_effd = fs_p_effd(:active_br) + fs_p_prop(:active_br)%p_temp = fs_p_temp(:active_br) + fs_p_prop(:active_br)%p_tvel = fs_p_tvel(:active_br) + + !=============================================================================== + ! Assign user defined values from namelist + !=============================================================================== + + firebrand_max_life_dt = cf%fs_firebrand_max_life_dt + firebrand_land_hgt = cf%fs_firebrand_land_hgt + firebrand_gen_maxhgt = cf%fs_firebrand_gen_maxhgt + firebrand_gen_levrand = cf%fs_firebrand_gen_levrand + + firebrand_gen_prop_diam = cf%fs_firebrand_gen_prop_diam + firebrand_gen_prop_effd = cf%fs_firebrand_gen_prop_effd + firebrand_gen_prop_temp = cf%fs_firebrand_gen_prop_temp + firebrand_gen_prop_tvel = cf%fs_firebrand_gen_prop_tvel + + firebrand_dens = cf%fs_firebrand_dens ! 513000.0_dp ! density of firebrand (g / m3) + firebrand_dens_char = cf%fs_firebrand_dens_char ! 299000.0_dp ! density of char (g m-3) [gupta et al 2002; fuel] + + !=============================================================================== + ! Firebrand Generation + !=============================================================================== + + !------------------------------------------------------------------------------- + ! Accumulate burn rate between generation intervals + !------------------------------------------------------------------------------- + + firepts = 0 + fs_fire_ROSdt(ifps:ifpe, jfps:jfpe) = fs_fire_ROSdt(ifps:ifpe, jfps:jfpe) + grid%burnt_area_dt(ifps:ifpe,jfps:jfpe) + + !------------------------------------------------------------------------------- + ! Updated fuel moisture from WRF-fire + !------------------------------------------------------------------------------- + + fmcg_2df(ifps:ifpe,jfps:jfpe) = grid%fmc_g(ifps:ifpe,jfps:jfpe) + + !=============================================================================== + ! Firebrand Generation - Release Firebrands - Part 1 + !=============================================================================== + + !------------------------------------------------------------------------------- + ! When a new release is due, calculate fraction of release by fuel category + !------------------------------------------------------------------------------- + + IF (fs_last_gen_dt >= fs_gen_dt) THEN + + firepts = COUNT(fs_fire_ROSdt(ifps:ifpe,jfps:jfpe) > ZERO_dp) + Ngrdpts = 0 ! Number of active fire points of valid fuel categories + + LimPot = 0.000001_dp ! 1e-6 + MeanPot = ZERO_dp + + IF (firepts > 0) THEN + + !WRITE (msg,'(1(i6,1x))') firepts + !CALL wrf_debug (wrfdbg, 'SPFire_rel firepts: '//msg) + + !------------------------------------------------------------------------------- + ! Get fuel category from WRF-FIRE and calculate release potential + !------------------------------------------------------------------------------- + + ALLOCATE(fcat_fr(ifps:ifpe,jfps:jfpe), fcat_factor(ifps:ifpe,jfps:jfpe), RelPot(ifps:ifpe,jfps:jfpe))!, rel_fcat(ifps:ifpe,jfps:jfpe)) + fcat_fr(:,:) = 0 + RelPot(:,:) = ZERO_dp + fcat_factor(:,:) = ZERO_dp + + fcat_fr(ifps:ifpe,jfps:jfpe) = RESHAPE(get_fuel_cat(crosswalk=grid%fuel_crosswalk, & + cat=grid%nfuel_cat(ifps:ifpe,jfps:jfpe)), & + SHAPE(fcat_fr)) + + fcat_factor(ifps:ifpe,jfps:jfpe)= firebrand_gen_factor(fcat_fr(ifps:ifpe,jfps:jfpe)) !RESHAPE, SHAPE(fcat_fr)) + + RelPot(ifps:ifpe,jfps:jfpe) = RESHAPE(firebrand_gen_potential(& + fuel_fgi=grid%fgip(ifps:ifpe,jfps:jfpe), & + fuel_mcg=fmcg_2df(ifps:ifpe,jfps:jfpe), & + !fuel_mcg=grid%fmcg2df(ifps:ifpe,jfps:jfpe), & + factor=fcat_factor(ifps:ifpe,jfps:jfpe),& + fire_rate=fs_fire_ROSdt(ifps:ifpe,jfps:jfpe)), & + SHAPE(fcat_fr)) + + ! Calculate Release Potential for each burning fuel cat + !------------------------------------------------------------------------------- + + Ngrdpts= COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)! RelPot do not count cat with factor of 0 + + ! In Large Fires + ! Try to increase minimum LimPot to remove very low values of Release Potential + !------------------------------------------------------------------------------- + IF (Ngrdpts > fs_gen_lim) THEN + LimPot = 10.0_dp * LimPot ! 1e-5 + Ngrdpts= COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot) + + IF (Ngrdpts > fs_gen_lim) THEN + LimPot = 10.0_dp * LimPot ! 1e-4 + Ngrdpts= COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot) + + ENDIF + ENDIF + ! WRITE (msg,'(1(i8,1x), 1(f14.8,1x))') Ngrdpts, LimPot + ! CALL wrf_debug (wrfdbg, 'SPFire_rel_lim Ngrdpts, LimP: '//msg) + + + WRITE (msg,'(1(i6,1x))') Ngrdpts + CALL wrf_debug (wrfdbg, 'SPFire_rel Ngrdpts: '//msg) + + ! Set a threshold for Potentials - use only highest values + !------------------------------------------------------------------------------- + IF (Ngrdpts > fs_gen_lim) THEN + + ! Find threshold value to select fs_gen_lim points + !------------------------------------------------------------------------------- + + ALLOCATE(tmp1(Ngrdpts)) + tmp1(1:Ngrdpts) = PACK(RelPot(ifps:ifpe,jfps:jfpe), & + mask=(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)) + + ! Find a limit value that yields an array size close to fs_gen_lim + LimPot = order_val(tmp1, ORD=fs_gen_lim) + !maxpot = MAXVAL(tmp1) + DEALLOCATE(tmp1) + + ! WRITE (msg,'(3(i8,1x), 2(f14.8,1x))') Ngrdpts, COUNT(RelPot > LimPot), SIZE(tmp1), LimPot, maxpot + ! CALL wrf_debug (wrfdbg, 'SPFire_rel_lim Ngrdpts, Cnt>Lim, LimP, max: '//msg) + + ! Find the median rank among remaining points + !------------------------------------------------------------------------------- + + ALLOCATE(tmp1(COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot))) + tmp1 = PACK(RelPot(ifps:ifpe,jfps:jfpe), & + mask=(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)) + + MeanPot = order_val(tmp1, ORD=INT(REAL(SIZE(tmp1))/2.0)) + !maxpot = MAXVAL(tmp1) + DEALLOCATE(tmp1) + + ! WRITE (msg,'(4(i8,1x), 3(f14.8,1x))') Ngrdpts, & + ! COUNT(RelPot > MeanPot), SIZE(tmp1), INT(REAL(SIZE(tmp1))/2.0), & + ! MeanPot, MINVAL(tmp1), maxpot + ! CALL wrf_debug (wrfdbg, 'SPFire_rel_lim Ngrdpts, Cnt>Mean, MPot, min, max: '//msg) + + Ngrdpts= COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot) + + !------------------------------------------------------------------------------- + + ELSEIF (Ngrdpts > 0) THEN + + ! Find the median rank among points + !------------------------------------------------------------------------------- + MeanPot = order_val(PACK(RelPot(ifps:ifpe,jfps:jfpe), & + mask=(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)), & + ORD=INT(dp05*Ngrdpts)) + + ! WRITE (msg,'(2(i8,1x), 2(f14.8,1x))') Ngrdpts, COUNT(RelPot > MeanPot), MeanPot, LimPot + ! CALL wrf_debug (wrfdbg, 'SPFire_rel_lim Ngrdpts, Cnt>Mean, MPot, LimP: '//msg) + + ENDIF + + ! Prepare for MPI + !------------------------------------------------------------------------------- + + IF (Ngrdpts == 0) firepts = 0 + + !------------------------------------------------------------------------------- + + ENDIF ! (firepts > 0) + !------------------------------------------------------------------------------- + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + IF (.NOT. (IamMaster)) THEN + + ! nbr > 0 indicates there's fire on tile and RelPot > 0, not the array size to receive in next mpi comm + ! if nbr > 0, next array will be one (array: MeanPot) + CALL fs_mpi_sendbuff1_int(sendto=MasterId, nbr=Ngrdpts) + + IF (Ngrdpts > 0) THEN + CALL fs_mpi_sendbuff1_real(sendto=MasterId, nbr=MeanPot) + ! WRITE (msg,'(1(i6,1x), (f14.6,1x))') Ngrdpts, MeanPot + ! CALL wrf_debug (wrfdbg, 'SPFire_rel mpi_send Npts,Meanpot: '//msg) + ENDIF + ENDIF +#endif + + !=============================================================================== + ! Firebrand Generation - Release Firebrands - Part 2 + !=============================================================================== + + !------------------------------------------------------------------------------- + ! MPI Master: Calculate Release Field (fire gridpt cnt by fuel category) + !------------------------------------------------------------------------------- + + IF (IamMaster) THEN + + CALL wrf_debug (wrfdbg, 'SPFire_rel_master --------------------------------------------------- ') + !------------------------------------------------------------------------------- + ! Declare arrays to receive data + + Npts_total = 0 + PotThr = ZERO_dp + TotPot = ZERO_dp + rel_ratio = ZERO_dp + cdf_coeff = ZERO_dp + + + ALLOCATE(np_Ngrdpts(ntiles)) ! Number of fuel cat actively burning in each mpiproc + ALLOCATE(np_MeanPot(ntiles)) + np_MeanPot(:) = ZERO_dp + np_Ngrdpts(:) = 0 + + np_Ngrdpts(1) = Ngrdpts ! Array index for Master is 1 + np_MeanPot(1) = MeanPot + + !------------------------------------------------------------------------------- + ! Receive release potential data from procs + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + + DO kk=2, mpiprocs ! kk=1 is Master, MPI-IDs begin at 0 + np_Ngrdpts(kk) = fs_mpi_recvbuff1_int(fromid=kk-1) ! Comm receive: Number of pts with RelPot>0 + + IF (np_Ngrdpts(kk) > 0) THEN + np_MeanPot(kk) = fs_mpi_recvbuff1_real(fromid=kk-1) + ! WRITE(msg, '(3(i8,1x), 2(f14.6,1x))') my_id, kk-1, np_Ngrdpts(kk), np_Meanpot(kk), TotPot + ! CALL wrf_debug (wrfdbg, 'SPFire_rel master proc Ngrdpts, MeanPot, TotPot: '//msg) + ENDIF + ENDDO +#endif + + DO kk=1, ntiles ! at least 1 for serial compile + TotPot = np_MeanPot(kk) * REAL(np_Ngrdpts(kk)) + TotPot + ENDDO + + !------------------------------------------------------------------------------- + ! Communicate release sources between Master and procs + + Npts_total = SUM( np_Ngrdpts ) + IF ( Npts_total > 0) THEN + + TotPot = TotPot/(REAL(Npts_total)) + + !------------------------------------------------------------------------------- + ! Calculate potential threshold for grid pts + + ! All elements fit in the array + IF (Npts_total <= fs_gen_lim) THEN + PotThr = ZERO_dp + + ! Estimate threshold with a linear regression + ELSE ! IF (Npts_total > fs_gen_lim) + rel_ratio = REAL(fs_gen_lim)/(REAL(Npts_total)) + cdf_coeff = (dp1 - rel_ratio)/rel_ratio + PotThr = cdf_coeff * TotPot + ENDIF + + ! WRITE(msg, '(2(i8,1x), 4(f12.6,1x))') fs_gen_lim, Npts_total, PotThr, rel_ratio, cdf_coeff, TotPot + ! CALL wrf_debug (wrfdbg, 'SPFire_rel_master max, Npts, PotThr: '//msg) + + !------------------------------------------------------------------------------- + ! Send dat to procs + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + + DO kk=2, mpiprocs ! kk=1 is Master + IF (np_Ngrdpts(kk) > 0) THEN + CALL fs_mpi_sendbuff1_real(sendto=kk-1, nbr=PotThr) + ! WRITE(msg, '(1(i8,1x), 1(f12.6,1x))') kk-1, PotThr + ! CALL wrf_debug (wrfdbg, 'SPFire_rel_master mpi_send PotThr: '//msg) + ENDIF + ENDDO +#endif + ENDIF ! npts_total > 0 + !------------------------------------------------------------------------------- + + ENDIF ! IamMaster + + !------------------------------------------------------------------------------- + + !=============================================================================== + ! Firebrand Generation - Release Firebrands - Part 3 + !=============================================================================== + + IF (Ngrdpts > 0) THEN + + !------------------------------------------------------------------------------- + ! Receive Potential Threshold from Master + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + IF (.NOT.(IamMaster)) THEN + PotThr = ZERO_dp + PotThr = fs_mpi_recvbuff1_real(fromid=MasterId) + ! WRITE(msg, '((i4,1x), (f14.6,1x))') my_id, PotThr + ! CALL wrf_debug (wrfdbg, 'SPFire_rel receive potential threshold: '//msg) + ENDIF +#endif + + !------------------------------------------------------------------------------- + ! Assign release from the various fuel cat to corresponding gridpts + + !CALL wrf_debug (wrfdbg, 'SPFire_rel2fr relpts>0 --------------------------------------------- ') + + ALLOCATE(n_rel_fr2d(ifps:ifpe, jfps:jfpe)) + n_rel_fr2d(ifps:ifpe, jfps:jfpe) = 0 + + IF (PotThr > ZERO_dp) & + n_rel_fr2d(ifps:ifpe,jfps:jfpe) = n_rel_fr2d(ifps:ifpe,jfps:jfpe) + & + UNPACK(SPREAD(1, DIM=1, NCOPIES=Ngrdpts), & + mask=(RelPot(ifps:ifpe,jfps:jfpe) >= PotThr), FIELD=0) + + IF (PotThr == ZERO_dp) & + n_rel_fr2d(ifps:ifpe,jfps:jfpe) = n_rel_fr2d(ifps:ifpe,jfps:jfpe) + & + UNPACK(SPREAD(1, DIM=1, NCOPIES=Ngrdpts), & + mask=(fs_fire_ROSdt(ifps:ifpe,jfps:jfpe) > ZERO_dp), FIELD=0) + + !------------------------------------------------------------------------------- + ! update relpts to represent value assigned to gridpts + + relpts = COUNT(n_rel_fr2d(ifps:ifpe,jfps:jfpe)>0) ! Limits to 1 release per refined gridpt, at various heights + + WRITE(msg, '(1(i8,1x))') relpts + CALL wrf_debug (wrfdbg, 'SPFire_rel2fr relpts: '//msg) + + !------------------------------------------------------------------------------- + ! Convert release locations to met grid + + ALLOCATE(release_i(relpts*fs_gen_levels), & + release_j(relpts*fs_gen_levels), & + release_k(relpts*fs_gen_levels), & + release_n(relpts)) + + release_i(:) = 0.0_dp + release_j(:) = 0.0_dp + release_k(:) = 0.0_dp + release_n(:) = 0 + + ! Does not allow more than one release from the same refined gridpt + CALL releaseijk2atm(nij_2d=n_rel_fr2d(ifps:ifpe,jfps:jfpe), & + fcat=fcat_fr(ifps:ifpe,jfps:jfpe), & + maxhgt_usr = firebrand_gen_maxhgt, & + sr_x=grid%sr_x, sr_y=grid%sr_y, & + pi=release_i(1:relpts), & + pj=release_j(1:relpts), & + pk=release_k(1:relpts), & ! max height + nij=release_n) + + + ! Adjust position to corresponding refined grid center + release_i(1:relpts) = release_i(1:relpts) + dp05/grid%sr_x + release_j(1:relpts) = release_j(1:relpts) + dp05/grid%sr_y + + DO kk=1,relpts + + fs_gen_inst(INT(release_i(kk)),INT(release_j(kk))) = & + fs_gen_inst(INT(release_i(kk)),INT(release_j(kk))) + 1 + + ENDDO + + CALL wrf_debug (wrfdbg, 'SPFire_rel2fr prep_release_hgt ---------------------------------------------') + ALLOCATE(release_prop(relpts*fs_gen_levels)) + + ! Create derived type + release_prop(1:relpts) = firebrand_property([firebrand_gen_prop_diam, & + firebrand_gen_prop_effd, & + firebrand_gen_prop_temp, & + firebrand_gen_prop_tvel]) + + levrand_seed = 0 ! means no random levels + IF (firebrand_gen_levrand) levrand_seed = cf%fs_firebrand_gen_levrand_seed + 1 ! in case user sets it to 0 + ! include more releases to span over various heights + CALL prep_release_hgt( & + release_i = release_i, & + release_j = release_j, & + release_k = release_k, & ! max height + release_n = release_n, & + release_prop=release_prop, & + levrand_seed = levrand_seed * grid%itimestep) ! force seed to vary in time + + prior_br = active_br + idmax = fs_gen_idmax + + CALL wrf_debug (wrfdbg, 'SPFire_driver_call_generate_firebrands ----------------------------------------- ') + + IF (active_br + COUNT( INT(release_i) > 0 ) > fs_array_maxsize) & + CALL wrf_debug (wrfdbg, 'SPFire_driver_release: brand array is full, cannot release new brands') + + CALL generate_firebrands(& + fs_p_id = fs_p_id, & + fs_p_dt = fs_p_dt, & + fs_p_z = fs_p_z, & + fs_p_x = fs_p_x, & + fs_p_y = fs_p_y, & + fs_p_prop = fs_p_prop, & + fs_gen_idmax= fs_gen_idmax, & + release_prop= release_prop, & + release_i = release_i, & + release_j = release_j, & + release_k = release_k, & + active_br = active_br) + + IF (active_br /= COUNT( fs_p_id > 0 ) ) CALL wrf_error_fatal('SPFire_driver: Active brands do not match!') + + ENDIF ! Ngrdpts > 0 + + fs_fire_ROSdt(:, :) = ZERO_dp + relpts = 0 + fs_last_gen_dt = 0 ! Reset release timestep counter + IF( ALLOCATED(fcat_fr)) DEALLOCATE(fcat_fr) + IF( ALLOCATED(fcat_factor)) DEALLOCATE(fcat_factor) + IF( ALLOCATED(RelPot)) DEALLOCATE(RelPot) + IF( ALLOCATED(np_Ngrdpts)) DEALLOCATE(np_Ngrdpts) + IF( ALLOCATED(np_MeanPot)) DEALLOCATE(np_MeanPot) + IF( ALLOCATED(n_rel_fr2d)) DEALLOCATE(n_rel_fr2d) + IF( ALLOCATED(release_prop)) DEALLOCATE(release_prop) + + ENDIF ! IF (fs_last_gen_dt >= fs_gen_dt) THEN + + !=============================================================================== + ! End of Releases + !=============================================================================== + + !------------------------------------------------------------------------------- + ! Nothing to transport: MPI Wait + !------------------------------------------------------------------------------- + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + IF ((active_br == 0)) THEN + + CALL fs_mpi_nothing2send(sendto=left_id) + CALL fs_mpi_nothing2send(sendto=right_id) + CALL fs_mpi_nothing2send(sendto=up_id) + CALL fs_mpi_nothing2send(sendto=down_id) + + CALL fs_mpi_nothing2send(sendto=upleft_id) + CALL fs_mpi_nothing2send(sendto=upright_id) + CALL fs_mpi_nothing2send(sendto=downleft_id) + CALL fs_mpi_nothing2send(sendto=downright_id) + !CALL wrf_debug (wrfdbg, 'SPFire_driver: no fires or active brands in patch. MPI signal sent.') + + ENDIF +#endif + !=============================================================================== + !=============================================================================== + + + + !=============================================================================== + ! Transport active firebrands + !=============================================================================== + + IF (active_br > 0) THEN + + WRITE (msg,'(i8)') active_br + CALL wrf_debug (wrfdbg, 'SPFire_driver: Active brands: '// msg ) + + !------------------------------------------------------------------------------- + ! Fetch wrf variables for advection + !------------------------------------------------------------------------------- + + ! Use arrays computed for the physics scheme on half (_phy) and full (8w) levels + ! will skip calc at model top - particle will not live during journey (module_big_step_utilities_em.F) + + !------------------------------------------------------------------------------- + ! Local met for fb physics: based on variables calculated in module_big_step_utilities_em.F + !------------------------------------------------------------------------------- + + !HALO_EM_E_5:48: u_1,u_2,v_1,v_2,w_1,w_2,t_1,t_2,ph_1,ph_2,tke_1,tke_2,;4:mu_1,mu_2 moist + !------------------------------------------------------------------------------- + ! P, Th, Rho, hgt at full levels + + ! Direct assignments from existing Halos: + + ! Half Levels + k_end = MIN( ke, kde-1 ) + ks = 1 + + p_phy(ims:, ks:, jms:) = ZERO_dp + th_phy(ims:, ks:, jms:) = ZERO_dp + + + p_phy(ims:, ks:k_end, jms:) = grid%p(ims:ime,ks:k_end,jms:jme) + grid%pb(ims:ime,ks:k_end,jms:jme) + th_phy(ims:, ks:, jms:) = grid%t_2(ims:ime,ks:k_end,jms:jme) + t0 + + !hypsometric_opt 2 (default) computes pressure in the model by using an alternative method + ! Temperature is moist theta by default (v4+, https://github.com/wrf-model/WRF/releases) + IF ( ( grid%use_theta_m == 1 ) .AND. (P_Qv >= PARAM_FIRST_SCALAR) ) & + th_phy = th_phy/(dp1 + Rv/Rd * grid%moist(ims:ime,ks:k_end,jms:jme,P_QV)) + + ! Full Levels + z_at_w(ims:,ks:,jms:)= (grid%phb(ims:ime,ks:ke,jms:jme) + grid%ph_2(ims:ime,ks:ke,jms:jme))/grav ! ASL + + u(ims:, ks:, jms:) = grid%u_2(ims:ime,ks:ke,jms:jme) + v(ims:, ks:, jms:) = grid%v_2(ims:ime,ks:ke,jms:jme) + w(ims:, ks:, jms:) = grid%w_2(ims:ime,ks:ke,jms:jme) + + msft(ims:, jms:) = grid%msftx(ims:ime, jms:jme) + + znw = grid%znw + DT = grid%dt + rdx = grid%rdx ! inverse grid length [m] + rdy = grid%rdy + + !------------------------------------------------------------------------------- + ! Calculated variables + + p_hyd(ims:, ks:, jms:) = ZERO_dp + dz8w(ims:, ks:, jms:) = ZERO_dp + th8w(ims:, ks:, jms:) = ZERO_dp + p8w(ims:, ks:, jms:) = ZERO_dp + qtot(ims:, ks:, jms:) = ZERO_dp + rho(ims:, ks:, jms:) = ZERO_dp + z(ims:, ks:, jms:) = ZERO_dp + + dz8w(ims:,ks:ke-1,jms:) = z_at_w(:,2:,:)-z_at_w(:,:ke-1,:) ! z_at_w: Height above the ground at interfaces + z(ims:,ks:ke-1,jms:) = dp05 * (z_at_w(:,2:,:) + z_at_w(:,:ke-1,:) ) + + qtot(ims:, ks:k_end, jms:) = SUM(grid%moist(ims:ime,ks:k_end,jms:jme,PARAM_FIRST_SCALAR:num_moist),DIM=4) + p_hyd(ims:, ke, jms:) = grid%p_top + DO kk = ke-1, ks, -1 + p_hyd(:,kk,:) = p_hyd(:,kk+1,:) - & + (dp1 + qtot(:,kk,:)) * (grid%c1h(kk) * grid%muts(:,:) + grid%c2h(kk)) * grid%dnw(kk) + ENDDO + + rho(ims:,ks:k_end,jms:) = (dp1/& + (grid%al(ims:ime,ks:k_end,jms:jme)+grid%alb(ims:ime,ks:k_end,jms:jme)))& + *(dp1+grid%moist(ims:ime,ks:k_end,jms:jme,P_QV)) + DO kk = 2, k_end + th8w(ims:,kk,jms:) = grid%fnm(kk) * th_phy(:,kk,:) + th_phy(:,kk-1,:) * grid%fnp(kk) + p8w(ims:,kk,jms:) = grid%fnm(kk) * p_phy(:,kk,:) + p_phy(:,kk-1,:) * grid%fnp(kk) + ENDDO + + w1 = (z(:,1,:) - z(:,2,:)) + WHERE(NINT(w1*100.0) == 0 ) w1 = dp1 + w1 = (z_at_w(:,1,:) - z(:,2,:)) / w1 + + th8w(:,1,:) = w1 * th_phy(:,1,:) + (dp1 - w1) * th_phy(:,2,:) ! interp at bottom only - ignore top + p8w(:,1,:) = w1 * p_phy(:,1,:) + (dp1 - w1) * p_phy(:,2,:) ! interp at bottom only - ignore top + + DO kk = 1, ke + z_at_w(:,kk,:)= z_at_w(:,kk,:) - z_at_w(:,1,:) + ENDDO + + !------------------------------------------------------------------------------- + !------------------------------------------------------------------------------- + + !------------------------------------------------------------------------------- + ! Advect particle + !------------------------------------------------------------------------------- + + CALL wrf_debug (wrfdbg, 'SPFire_driver begin transport ---------------------------------------------------- ') + + ALLOCATE(xout(active_br), & + yout(active_br), & + zout(active_br)) + + CALL advect_xyz_m(grid=grid, & + xp= fs_p_x(:active_br), & + yp= fs_p_y(:active_br), & + hgt= fs_p_z(:active_br), & ! height[m] is the fixed position reference between wrf time steps + dtp= fs_p_dt(:active_br),& + idp= fs_p_id(:active_br),& + znw= znw, & + mf = rdx*msft, & + z_at_w=z_at_w, & + u= u, & + v= v, & + w= w, & + dt=dt, & + ims= ims, & + jms= jms, & + kms= ks, & + phyd= p_hyd, & ! air pressure (Pa) + thet= th8w, & ! temperature (K) + rho = rho, & ! density (kg/m3) + xout= xout, & + yout= yout, & + zout= zout, & + fs_p_prop=fs_p_prop(:active_br), & + land_hgt = firebrand_land_hgt, & + start_mom3d_dt = cf%fs_firebrand_gen_mom3d_dt, & + msg = msg) + + + IF (LEN(TRIM(msg)) > 1) & + CALL wrf_debug (wrfdbg, 'SPFire_transp: '//msg) + + !------------------------------------------------------------------------------- + ! Update array for output before resetting deposited indices to zero + !------------------------------------------------------------------------------- + + fs_p_x(:active_br) = xout + fs_p_y(:active_br) = yout + fs_p_z(:active_br) = zout + fs_p_dt(:active_br)= fs_p_dt(:active_br) +1 + + fs_p_mass(:active_br) = fs_p_prop(:active_br)%p_mass + fs_p_diam(:active_br) = fs_p_prop(:active_br)%p_diam + fs_p_effd(:active_br) = fs_p_prop(:active_br)%p_effd + fs_p_temp(:active_br) = fs_p_prop(:active_br)%p_temp + fs_p_tvel(:active_br) = fs_p_prop(:active_br)%p_tvel + + CALL wrf_debug (wrfdbg, 'SPFire_driver end transport ---------------------------------------------------- ') + + !============================================================= + ! Transport done :) + !============================================================= + + + !=============================================================================== + ! Remove particles + !=============================================================================== + + !------------------------------------------------------------------------------- + ! Set NaN properties for particle removal + !------------------------------------------------------------------------------- + sparse_mask = .FALSE. + + WHERE (ISNAN(fs_p_mass) .OR. ISNAN(fs_p_diam) .OR. & + ISNAN(fs_p_effd) .OR. ISNAN(fs_p_temp) .OR. & + ISNAN(fs_p_tvel)) fs_p_effd = ZERO_dp ! ediam=zero will be removed in remove_br + + + !=============================================================================== + ! Remove particles - from burnout or out of domain + !=============================================================================== + + CALL remove_br(& !active_br= active_br, & + fs_p_id = fs_p_id, & + fs_p_x = fs_p_x, & + fs_p_y = fs_p_y, & + fs_p_z = fs_p_z, & + fs_p_dt = fs_p_dt, & + fs_p_effd = fs_p_effd, & + cnt = nresets, & + max_life_dt= firebrand_max_life_dt, & + land_hgt = firebrand_land_hgt) + WRITE (msg,'(i12)') nresets + CALL wrf_debug (wrfdbg, 'SPFire_driver remove br: '//msg) + + + !=============================================================================== + ! Spotfires: Deposited Brands + !=============================================================================== + + dt_sum_landed(ims:, jms:) = ZERO_dp + CALL deposit_br(& !active_br= active_br, & + fs_p_id = fs_p_id, & + fs_p_x = fs_p_x, & + fs_p_y = fs_p_y, & + fs_p_z = fs_p_z, & + sum_fbrand = dt_sum_landed,& + land_hgt = firebrand_land_hgt) + + ! The mask is based on floor(fs_p_x,y) so deposits should not exist at ie or je + fs_count_landed_all(is:ie, js:je) = fs_count_landed_all(is:ie, js:je) + dt_sum_landed(is:ie, js:je) + fs_count_landed_hist(is:ie, js:je) = fs_count_landed_hist(is:ie, js:je) + dt_sum_landed(is:ie, js:je) + WRITE (msg,'(i12)') COUNT(dt_sum_landed(is:ie, js:je) > 0) + CALL wrf_debug (wrfdbg, 'SPFire_driver deposit br: '//msg) + + + !=============================================================================== + !=============================================================================== + + !****************************************************************** + !****************************************************************** + !* * + !* MPI Send firebrands * + !* * + !****************************************************************** + !****************************************************************** + + + !------------------------------------------------------------------------------- + !------------------------------------------------------------------------------- + + ! Re-PACK before incoming MPI + + !=============================================================================== + ! MPI: Send Brands (out of this patch) + !=============================================================================== + + ! CALL wrf_debug (wrfdbg, 'SPFire_driver mpi transfer particles') + + ! Send / Receive arrays: + ! 3 REAL arrays, 2 INTEGER arrays: fs_p_x, fs_p_y, fs_p_z, fs_p_id, fs_p_dt: + + ! |____|____|____| + ! | | U | | + ! |____|____|____|je + ! | L | Me | R | + ! |____|____|____|js + ! | | D | | + ! |____|____|____| + ! is ie + + ! Diagonal??? + + !------------------------------------------------------------------------------- + ! Send any particle? + !------------------------------------------------------------------------------- + + sparse_mask = .FALSE. + sparse_mask = (fs_p_id > 0 .AND. & + (FLOOR(fs_p_x) < is .OR. & ! LEFT + FLOOR(fs_p_x) > ie .OR. & ! RIGHT + FLOOR(fs_p_y) > je .OR. & ! UP + FLOOR(fs_p_y) < js )) ! DONW + + + WRITE (msg,'(i6)') COUNT(sparse_mask) + CALL wrf_debug (wrfdbg, 'SPFire_driver mpi send away: '//msg) + + !------------------------------------------------------------------------------- + ! interrupt before MPI + ! IF (COUNT(sparse_mask) > 0) CALL wrf_error_fatal( "MPI called") + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + CALL fs_mpi_send2neighbors(task_id=task_id,& + mask=sparse_mask,& + p_x = fs_p_x, & + p_y = fs_p_y, & + p_z = fs_p_z, & + p_id = fs_p_id, & + p_dt = fs_p_dt, & + fs_p_m = fs_p_mass, & + fs_p_d = fs_p_diam, & + fs_p_e = fs_p_effd, & + fs_p_t = fs_p_temp, & + fs_p_v = fs_p_tvel) +#endif + !------------------------------------------------------------------------------- + ! Reset + !------------------------------------------------------------------------------- + ! CALL wrf_debug (wrfdbg, 'SPFire_driver reset particles sent away') + + ! In serial compilation, firebrands outside the domain limits will be removed + WHERE(sparse_mask) fs_p_id= 0 + + !=============================================================================== + ! Last step for IF active_br > 0 + !=============================================================================== + + ! WRITE (msg,'(2(i8,1x))') active_br, COUNT( fs_p_id > 0 ) + ! CALL wrf_debug (wrfdbg, 'SPFire_driver pack BEFORE: active_br >>> '// msg) + + !------------------------------------------------------------------------------- + ! Repack all zeros: PACK where mask is TRUE + !------------------------------------------------------------------------------- + sparse_mask = .FALSE. + sparse_mask = (fs_p_id>0) + fs_p_x = PACK(fs_p_x, sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) ) + fs_p_y = PACK(fs_p_y, sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) ) + fs_p_z = PACK(fs_p_z, sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) ) + fs_p_id = PACK(fs_p_id, sparse_mask, SPREAD(0,1,fs_array_maxsize) ) + fs_p_dt = PACK(fs_p_dt, sparse_mask, SPREAD(0,1,fs_array_maxsize) ) + fs_p_mass = PACK(fs_p_mass , sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) ) + fs_p_diam = PACK(fs_p_diam , sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) ) + fs_p_effd = PACK(fs_p_effd, sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) ) + fs_p_temp = PACK(fs_p_temp , sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) ) + fs_p_tvel = PACK(fs_p_tvel , sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) ) + + active_br = COUNT( fs_p_id > 0 ) + + WRITE (msg,'(1(i8,1x))') active_br + CALL wrf_debug (wrfdbg, 'SPFire_driver pack AFTER mpi_send2neighbors active_br >>> '// msg) + + + ENDIF + + !=============================================================================== + ! Active_br work done ;) + !=============================================================================== + IF (active_br /= COUNT( fs_p_id > 0 ) ) CALL wrf_error_fatal('SPFire_driver: Active brands do not match!') + + !=============================================================================== + !=============================================================================== + + + + !=============================================================================== + ! Calculate Spotfire Likelihood at history output time + !=============================================================================== + + ! Registry + ! fs_count_landed_all + ! fs_count_landed_hist + ! fs_frac_landed + ! fs_spotting_lkhd + ! fs_count_reset + ! fs_fire_area + + ! store array of data for each proc: Master only + ! np_nlanded [np] ! ALLOCATABLE + ! np_landed_cnt [SUM(np_nlanded)] ! ALLOCATABLE + ! np_landed_risk [SUM(np_nlanded)] ! ALLOCATABLE + ! np_frac_landed [SUM(np_nlanded)] ! ALLOCATABLE + ! np_lkhd [SUM(np_nlanded)] ! ALLOCATABLE + ! + + ! store array of local data + ! landing_mask ! ALLOCATABLE + ! landing_cnt ! ALLOCATABLE (mpi send) + ! landed_risk ! ALLOCATABLE (mpi send) + ! recv_spot_lkhd ! ALLOCATABLE (mpi recv) + ! recv_frac_landed ! ALLOCATABLE (mpi recv) + + !=============================================================================== + ! Check history interval (use diag_flag defined in solve_em for microphysics) + !=============================================================================== + + IF ((hist_flag) .AND. (grid%itimestep > 1)) THEN + + fs_count_reset = .TRUE. ! true + fs_frac_landed(ims:ime, jms:jme) = ZERO_dp + fs_spotting_lkhd(ims:ime, jms:jme) = ZERO_dp + + ! Was there any firebrand deposit? *Note that fire may exist on a different tile + !------------------------------------------------------------------------------- + + ALLOCATE(landing_mask(ims:ime, jms:jme)) ! Allocate memory but only mask this tile + landing_mask(ims:ime, jms:jme) = .FALSE. + landing_mask(is:ie, js:je) = (fs_count_landed_hist(is:ie, js:je) > 0) + ndep = COUNT(landing_mask) + + ! convert fire_area from fire grid to atm grid + !------------------------------------------------------------------------------- + fs_fire_area(is:ie,js:je) = ZERO_dp + fs_fire_area(is:ie, js:je) = fire2tile(fr_arr=grid%fire_area(ifps:ifpe,jfps:jfpe), & + dsx=grid%sr_x,dsy=grid%sr_y) + + ! WRITE(msg, '(i8,1x,i12)') ndep, COUNT(fs_fire_area(is:ie,js:je)> ZERO_dp) + ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd: Spotfire Likelihood, deposit points: '//msg) + + IF (ndep > 0) THEN + + landing_mask(ims:ime, jms:jme) = .FALSE. + landing_mask(is:ie, js:je) = ((fs_count_landed_hist(is:ie, js:je) > 0) .AND. & + (fs_fire_area(is:ie, js:je) == ZERO_dp) ) + ndep = COUNT(landing_mask) + fs_landing_mask(is:ie, js:je) = UNPACK(SPREAD(1,DIM=1,NCOPIES=ndep), MASK=landing_mask(is:ie, js:je), FIELD=0) + + ! WRITE(msg, '(4(i8,1x))') COUNT(fs_count_landed_hist > ZERO_dp), ndep, COUNT(fs_landing_mask == 1), COUNT((fs_count_landed_hist > ZERO_dp).AND.(fs_fire_area > ZERO_dp)) + ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd deposits, non fire-point deposits, fire-point deposits: '//msg) + + ALLOCATE(tmp1(ndep)) + tmp1 = PACK(fs_count_landed_hist(is:ie, js:je), landing_mask(is:ie, js:je)) ! WHERE doest work on memory bounds + fs_count_landed_hist(is:ie, js:je) = ZERO_dp + fs_count_landed_hist(is:ie, js:je) = UNPACK(tmp1, MASK=landing_mask(is:ie, js:je), FIELD=ZERO_dp) + + ! WRITE(msg, '(2(i8,1x), 1(f14.6,1x))') COUNT(fs_count_landed_hist > ZERO_dp), ndep, MAXVAL(fs_count_landed_hist) + ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd deposits, non fire-point deposits, fire-point deposits: '//msg) + DEALLOCATE(tmp1) + + ! Were these firebrand deposited outside the fire area? + !------------------------------------------------------------------------------- + + IF (ndep > 0) THEN + + ! Calculate fuel risk + !------------------------------------------------------------------------------- + + ALLOCATE(fuel_spotting_risk_fr(ifps:ifpe,jfps:jfpe), &!fs_fuel_spotting_risk(ims:ime,jms:jme), & + spotthreat_fr(ifps:ifpe,jfps:jfpe)) + fuel_spotting_risk_fr(:,:) = ZERO_dp + fs_fuel_spotting_risk(:,:) = ZERO_dp + spotthreat_fr(:,:) = ZERO_dp + + IF (.NOT. ALLOCATED(fcat_fr)) THEN + ALLOCATE(fcat_fr(ifps:ifpe,jfps:jfpe)) + fcat_fr(:,:) = ZERO_dp + fcat_fr(ifps:ifpe,jfps:jfpe) = RESHAPE(& + get_fuel_cat(crosswalk=grid%fuel_crosswalk, & + cat=grid%nfuel_cat(ifps:ifpe,jfps:jfpe)), & + SHAPE(fuel_spotting_risk_fr)) + ENDIF + !WRITE(msg, *) MAXVAL(fcat_fr), MINVAL(fcat_fr) + ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd fcat max,min '//msg) + + spotthreat_fr(ifps:ifpe,jfps:jfpe)= spotting_threat_factor(fcat_fr(ifps:ifpe,jfps:jfpe)) !RESHAPE(SHAPE(fuel_spotting_risk_fr)) + ! get_fuel_cat(crosswalk=grid%fuel_crosswalk, & + ! cat=grid%nfuel_cat(ifps:ifpe,jfps:jfpe))), & + ! WRITE(msg, *) MAXVAL(spotthreat_fr), MINVAL(spotthreat_fr) + ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd spotting_threat_factor max,min'//msg) + + fuel_spotting_risk_fr(ifps:ifpe,jfps:jfpe) = RESHAPE(& + fuel_spotting_risk(& + fuel_fgi=grid%fgip(ifps:ifpe,jfps:jfpe), & + fuel_mcg=fmcg_2df(ifps:ifpe,jfps:jfpe), & + !fuel_mcg=grid%fmcg2df(ifps:ifpe,jfps:jfpe), & + factor=spotthreat_fr(ifps:ifpe,jfps:jfpe)), & + SHAPE(fuel_spotting_risk_fr)) !, fuel_mcg=grid%fmcg2df) + + fs_fuel_spotting_risk(is:ie,js:je) = fire2tile(fr_arr=fuel_spotting_risk_fr(ifps:ifpe,jfps:jfpe), & + dsx=grid%sr_x,dsy=grid%sr_y) + DEALLOCATE(fcat_fr) + + !------------------------------------------------------------------------------- + + + ! Prepare for MPI + !------------------------------------------------------------------------------- + + ALLOCATE(landing_cnt(ndep), landed_risk(ndep), recv_spot_lkhd(ndep), recv_frac_landed(ndep)) + landing_cnt = PACK(fs_count_landed_hist(is:ie,js:je), landing_mask(is:ie,js:je)) + landed_risk = PACK(fs_fuel_spotting_risk(is:ie,js:je), landing_mask(is:ie,js:je)) + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + IF (.NOT. (IamMaster)) THEN + + CALL fs_mpi_sendbuffsize(sendto=MasterId, nbr=ndep) ! Comm send >0: BuffSz + CALL fs_mpi_sendbuff_int(sendto=MasterId, bsz=ndep, buff=landing_cnt(1:ndep)) + CALL fs_mpi_sendbuff_real(sendto=MasterId, bsz=ndep, buff=landed_risk(1:ndep)) + + ENDIF +#endif + ENDIF ! (fs_count_landed_hist > 0 .AND. fire_area == 0) + ENDIF ! SUM(fs_count_landed_hist) > 0 + + !------------------------------------------------------------------------------- + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + IF ((ndep == 0) .AND. .NOT.(IamMaster)) & + CALL fs_mpi_nothing2send(sendto=MasterId) ! Comm send 0: BuffSz +#endif + !------------------------------------------------------------------------------- + ! Master: Calculate Likelihood Field + !------------------------------------------------------------------------------- + + IF (IamMaster) THEN + + !------------------------------------------------------------------------------- + ! Declare arrays to receive data + + ALLOCATE(np_nlanded(ntiles)) + np_nlanded(:) = 0 + np_nlanded(1) = ndep ! Array index for Master is 1 + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + DO kk=2, mpiprocs ! kk=1 is Master, MPI-IDs begin at 0 + np_nlanded(kk) = fs_mpi_recvbuffsize(fromid=kk-1) ! Comm receive: BuffSz + ! WRITE(msg, '(2(i8,1x))') kk,np_nlanded(kk) + ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd proc deposit: '//msg) + ENDDO +#endif + !------------------------------------------------------------------------------- + ! Communicate deposits between Master and procs + + ndep_total = SUM(np_nlanded) + + IF ( ndep_total > 0) THEN + ALLOCATE(np_landed_cnt(ndep_total), np_landed_risk(ndep_total)) + IF (ndep > 0) THEN + np_landed_cnt(1:ndep) = landing_cnt + np_landed_risk(1:ndep)= landed_risk + ELSE + np_landed_cnt(1) = ZERO_dp ! I don't think I need this + np_landed_risk(1)= ZERO_dp + ENDIF + + !------------------------------------------------------------------------------- + ! Receive deposit data from procs + + kk1 = ndep + 1 ! index start after master's + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + DO kk=2, mpiprocs ! kk=1 is Master + + IF (np_nlanded(kk) > 0) THEN + + kk2 = kk1 + np_nlanded(kk) -1 + + ! WRITE(msg, '(4(i8,1x))') kk, np_nlanded(kk), kk1, kk2 + ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd master kk, np_dep, k1:k2: '//msg) + np_landed_cnt(kk1:kk2) = fs_mpi_recvfrompatch_int(bsz=np_nlanded(kk), fromid=kk-1) + np_landed_risk(kk1:kk2)= fs_mpi_recvfrompatch_real(bsz=np_nlanded(kk), fromid=kk-1) + kk1 = kk1 + np_nlanded(kk) + ENDIF + ENDDO +#endif + + !------------------------------------------------------------------------------- + ! Calculate ratio and lkhd + + ALLOCATE(np_frac_landed(ndep_total), np_lkhd(ndep_total)) + np_frac_landed(:) = ZERO_dp + np_lkhd(:) = ZERO_dp + + np_frac_landed(1:ndep_total) = REAL(np_landed_cnt(1:ndep_total))/REAL(SUM(np_landed_cnt)) + np_lkhd(1:ndep_total) = np_frac_landed(1:ndep_total) * np_landed_risk(1:ndep_total) + np_lkhd(1:ndep_total) = np_lkhd(1:ndep_total)/MAXVAL(np_lkhd) + + ! DO pp=1,SIZE(np_landed_cnt) + ! WRITE(msg, '(3(i8,1x),4(f12.6,1x))') pp, np_landed_cnt(pp), SUM(np_landed_cnt), np_landed_risk(pp), np_frac_landed(pp), MAXVAL(np_lkhd), np_lkhd(pp) + ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd master np_frac_landed, np_lkhd, : '//msg) + ! ENDDO + + !------------------------------------------------------------------------------- + ! Send dat to procs + + kk1 = ndep + 1 ! index start after master's + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + DO kk=2, mpiprocs ! kk=1 is Master + + IF (np_nlanded(kk) > 0) THEN + + kk2 = kk1 + np_nlanded(kk) -1 + + CALL fs_mpi_sendbuff_real(sendto=kk-1, bsz=np_nlanded(kk), buff=np_frac_landed(kk1:kk2)) + CALL fs_mpi_sendbuff_real(sendto=kk-1, bsz=np_nlanded(kk), buff=np_lkhd(kk1:kk2)) + + kk1 = kk1 + np_nlanded(kk) + ENDIF + ENDDO +#endif + ENDIF ! SUM(np_nlanded) > 0 + !------------------------------------------------------------------------------- + + ENDIF ! IamMaster + + !------------------------------------------------------------------------------- + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + IF (.NOT.(IamMaster) .AND. (ndep > 0)) THEN + + recv_frac_landed = fs_mpi_recvfrompatch_real(bsz=ndep, fromid=MasterId) + fs_frac_landed(is:ie,js:je) = UNPACK(recv_frac_landed, MASK=landing_mask(is:ie,js:je), FIELD=ZERO_dp) + + recv_spot_lkhd = fs_mpi_recvfrompatch_real(bsz=ndep, fromid=MasterId) + fs_spotting_lkhd(is:ie,js:je) = UNPACK(recv_spot_lkhd, MASK=landing_mask(is:ie,js:je), FIELD=ZERO_dp) + + ENDIF +#endif + + IF ((IamMaster) .AND. (ndep > 0)) THEN + + fs_frac_landed(is:ie,js:je) = UNPACK(np_frac_landed(1:ndep), MASK=landing_mask(is:ie,js:je), FIELD=ZERO_dp) + fs_spotting_lkhd(is:ie,js:je) = UNPACK(np_lkhd(1:ndep), MASK=landing_mask(is:ie,js:je), FIELD=ZERO_dp) + + ENDIF + + ENDIF ! hist_flag + + !=============================================================================== + !=============================================================================== + + + !****************************************************************** + !****************************************************************** + !* * + !* MPI Receive firebrands * + !* * + !****************************************************************** + !****************************************************************** + + + !=============================================================================== + ! MPI: Receive Brands (into this patch) + !=============================================================================== + + + + !------------------------------------------------------------------------------- + ! Anything to receive? This part is only needed when compiled in parallel + !------------------------------------------------------------------------------- + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + + ALLOCATE(nbr_id(neighbors)) + nbr_id(:)=fs_mpi_checkreceive(task_list=task_id, np=neighbors) + + nbr_sum = SUM(nbr_id) + IF (nbr_sum > 0) THEN + + WRITE (msg,'(16(i4,1x))') ([task_id(ii), nbr_id(ii)], ii=1,neighbors) + CALL wrf_debug (wrfdbg, 'SPFire_driver mpi_check_receive: '//msg) + + ! Receiving positions, id, dt + ALLOCATE(r_x(nbr_sum), r_y(nbr_sum), r_z(nbr_sum), r_id(nbr_sum), r_dt(nbr_sum)) + !ALLOCATE(release_mpi_ijk(nbr_sum,3)) + + ! Receiving p_propertieserties + ALLOCATE(r_p_m(nbr_sum), r_p_d(nbr_sum), r_p_e(nbr_sum), r_p_t(nbr_sum), r_p_v(nbr_sum)) + ALLOCATE(fb_mdetv(nbr_sum)) + + CALL fs_mpi_recv(np_id=nbr_id, task_id=task_id, & + r_x=r_x, r_y=r_y, r_z=r_z, & + r_id=r_id, r_dt=r_dt, & + r_p_m=r_p_m, & + r_p_d=r_p_d, & + r_p_e=r_p_e, & + r_p_t=r_p_t, & + r_p_v=r_p_v) + + ! Assign values to array and release incoming particles + !release_mpi_ijk = RESHAPE([r_x,r_y,r_z], [nbr_sum,3]) + fb_mdetv = [(p_properties(r_p_m(kk), r_p_d(kk), r_p_e(kk), r_p_t(kk), r_p_v(kk)), kk=1,nbr_sum)] + + ! DO kk=1,MIN(nbr_sum,5) + ! WRITE(msg,'(6(f12.6,1x))') (release_mpi_ijk(kk,ii), ii=1,3), fb_mdetv(kk)%p_diam, fb_mdetv(kk)%p_temp, fb_mdetv(kk)%p_tvel + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi recv AFTER >>> '// msg) + ! ENDDO + + prior_br = active_br + 1 ! Array is packed so new particles will be assgned at this position + CALL generate_firebrands(& + fs_p_id = fs_p_id, & + fs_p_dt = fs_p_dt, & + fs_p_z = fs_p_z, & + fs_p_x = fs_p_x, & + fs_p_y = fs_p_y, & + fs_gen_idmax = fs_gen_idmax, & + active_br = active_br, & + !release_ijk = release_mpi_ijk, & + release_i = r_x, & + release_j = r_y, & + release_k = r_z, & + release_prop= fb_mdetv, & + fs_p_prop = fs_p_prop) + + WRITE (msg,'(2(i8,1x))') active_br, fs_gen_idmax + CALL wrf_debug (wrfdbg, 'SPFire_driver mpi recv AFTER : ii, fs_gen_idmax >>> '// msg) + + fs_p_mass (prior_br:active_br) = r_p_m + fs_p_diam (prior_br:active_br) = r_p_d + fs_p_effd(prior_br:active_br) = r_p_e + fs_p_temp (prior_br:active_br) = r_p_t + fs_p_tvel (prior_br:active_br) = r_p_v + + ! DO kk=prior_br,prior_br+MIN(nbr_sum,5) + ! WRITE(msg,'(2(i6,1x),6(f12.6,1x))') fs_p_id(kk), fs_p_dt(kk), fs_p_x(kk), fs_p_y(kk), fs_p_z(kk), fs_p_diam(kk), fs_p_temp(kk), fs_p_tvel(kk) + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi recv AFTER2 >>> '// msg) + ! ENDDO + + DEALLOCATE(r_x, r_y, r_z, r_id, r_dt) + !DEALLOCATE(release_mpi_ijk) + DEALLOCATE(fb_mdetv) + DEALLOCATE(r_p_m, r_p_d, r_p_e, r_p_t, r_p_v) + + ! ELSE + ! CALL wrf_debug (wrfdbg, 'SPFire_driver mpi nothing to receive') + ENDIF + DEALLOCATE(nbr_id) + !------------------------------------------------------------------------------- + ! DONE :) + !------------------------------------------------------------------------------- +#endif + !=============================================================================== + ! Check history interval + !=============================================================================== + + hist_flag = .FALSE. + + + END SUBROUTINE firebrand_spotting_em_driver +!============================================================= + +!============================================================= + + + +!============================================================= +!============================================================= + +!****************************************************************** +!****************************************************************** +!* * +!* Particle Transport & Physics * +!* * +!****************************************************************** +!****************************************************************** + +!============================================================= + PURE & + SUBROUTINE advect_xyz_m(grid, xp, yp, hgt, dtp, idp, & + u, v, w, dt, mf, z_at_w, znw, ims, jms, kms, & + phyd, thet, rho, & + xout, yout, zout, fs_p_prop, land_hgt, & + start_mom3d_dt, msg) +!============================================================= + + ! Transport a particle at a given xp, yp, zp position in meters + ! given the corresponding velocities [m/s] at the enclosing grid points + + ! arguments + ! xp, yp, hgt: starting particle position + ! u, v, w: wind components + ! dt: time interval in sec + ! ims, jms : start index of wind components + ! mf: constant to convert from grid position to meters: rdx*msft(x,y) + ! z_at_w: wrf height at vertical interface + ! znw: eta levels + + !------------------------------------------------------------- + + IMPLICIT NONE + + TYPE(domain), INTENT(IN) :: grid + INTEGER, INTENT(IN) :: ims, jms, kms, start_mom3d_dt + REAL, INTENT(IN) :: dt + REAL, INTENT(IN) :: land_hgt + REAL, INTENT(IN), DIMENSION(:) :: xp, yp, hgt ! particle position + INTEGER, INTENT(IN), DIMENSION(:) :: dtp, idp ! particle life-time and ID + REAL, INTENT(IN), DIMENSION(ims:, kms:, jms:) :: u, v, w, z_at_w + REAL, INTENT(IN), DIMENSION(ims:, kms:, jms:) :: phyd, thet, rho + REAL, INTENT(IN), DIMENSION(:) :: znw + REAL, INTENT(IN), DIMENSION(ims:, jms:) :: mf ! map factor, msft * rdx + REAL, INTENT(OUT), DIMENSION(:) :: xout, yout, zout!, aglh + TYPE(p_properties),INTENT(INOUT),DIMENSION(:):: fs_p_prop + + REAL, DIMENSION(3) :: uvw2p + INTEGER :: pp, aux + INTEGER :: hsz, ihs, jhs, ihe, jhe ! tile +- halo array bounds + REAL :: xp_m0, yp_m0, zp_m0, zp0 + REAL :: xp_m1, yp_m1, zp_m1, xp1, yp1, zp1 + REAL :: xp_m2, yp_m2, zp_m2, xp2, yp2, zp2 + REAL :: zp, wp, brz + REAL :: loc_p, loc_t, loc_d ! local met properties + CHARACTER (LEN=100), INTENT(OUT) :: msg + + WRITE(msg, '(a100)') ' ' + !------------------------------------------------------------------------------- + + hsz = 4 ! halo size - get this value from grid%. Halo:48 = 4 points. How about staggering? + ihs = is - hsz ! MAX(, ids) + jhs = js - hsz ! MAX(, jds) + ihe = ie -1 + hsz ! MIN(, ide-1) ! check for ide -1 bc we use i, i+1 to interpolate + jhe = je -1 + hsz ! MIN(, jde-1) + + ! WRITE (msg,'(4(i4,1x))') ihs,ihe, jhs, jhe + ! CALL wrf_debug (wrfdbg, 'SPFire_advect halo bounds ihs, ihe, jhs, jhe: >>> '//msg) + + !start_mom3d_dt = 4 ! min lifetime before calculating fb physics (number of timesteps for this nested grid) + + DO pp=1, SIZE(xp) + + ! CALL wrf_debug (wrfdbg, 'SPFire advect_xyz_m ---------------------------------------------------- ') + + ! WRITE (msg,'(3(i8,1x),3(f12.6,1x))') pp, idp(pp), dtp(pp), xp(pp), yp(pp), hgt(pp) + ! CALL wrf_debug (wrfdbg, 'SPFire BEFORE advect: pp,id,dt,xp,yp,zp: >>> '//msg) + + !------------------------------------------------------------------------------- + ! Convert horizontal positions to meters + !------------------------------------------------------------------------------- + + xp_m0 = xp(pp) / mf(FLOOR(xp(pp)),FLOOR(yp(pp))) + yp_m0 = yp(pp) / mf(FLOOR(xp(pp)),FLOOR(yp(pp))) + zp_m0 = hgt(pp) + + ! zp changes slightly between wrf time steps, so it need to be calculated again + zp0 = hgt2k(xp=xp(pp), yp=yp(pp), hgt=zp_m0, z_at_w=z_at_w, znw=znw) + ! (Need fractional position for interpolation) + + ! WRITE (msg,'(3(i8,1x),4(f12.6,1x))') pp, idp(pp), dtp(pp), xp(pp), yp(pp), hgt(pp), zp0 + ! CALL wrf_debug (wrfdbg, 'SPFire_advect BEFORE: pp,id,dt,xp,yp,zp,zk: >>> '//msg) + + ! If this is a particle deposited by an adjacent tile, do not transport it + IF (zp_m0 <= land_hgt) THEN + ! CALL wrf_debug (wrfdbg, 'SPFire_advect particle at deposit threshold - will not advect') + + xout(pp) = xp(pp) + yout(pp) = yp(pp) + zout(pp) = zp_m0 + CYCLE + + ENDIF + + !------------------------------------------------------------------------------- + !------------------------------------------------------------------------------- + + + ! 1ST PASS + !============================================================= + + + !------------------------------------------------------------------------------- + ! #1 interpolate velocities to particle position & advect particle + !------------------------------------------------------------------------------- + + uvw2p = uvw_3d_interp(x=xp(pp), y=yp(pp), z=zp0, u=u, v=v, w=w, ihs=ihs, jhs=jhs, ihe=ihe, jhe=jhe) ! args: grid fractional positions + + xp_m1 = uvw2p(1)*DT + xp_m0 + yp_m1 = uvw2p(2)*DT + yp_m0 + zp_m1 = uvw2p(3)*DT + zp_m0 + + !------------------------------------------------------------------------------- + ! Convert horizontal positions from meters + !------------------------------------------------------------------------------- + + xp1 = xp_m1 * mf(FLOOR(xp(pp)),FLOOR(yp(pp))) ! 2 pass will account for grid pt changes in mf & u + yp1 = yp_m1 * mf(FLOOR(xp(pp)),FLOOR(yp(pp))) + + IF((ABS(xp1-xp(pp)) > 2.0) .OR. (ABS(yp1-yp(pp)) > 2.0)) THEN + WRITE (msg,'(2(i4,1x),7(f10.6,1x))') pp, dtp(pp), xp(pp), yp(pp), zp0, & + uvw2p(1), uvw2p(2), uvw2p(3), dt ! + zout(pp) = ZERO_dp + xout(pp) = ZERO_dp + yout(pp) = ZERO_dp + CYCLE + ENDIF + + !------------------------------------------------------------------------------- + ! Convert z position from meters to k-fractional + !------------------------------------------------------------------------------- + + ! CALL wrf_debug (wrfdbg, 'SPFire advect_xyz_m 1st pass --------------------------------------------- ') + + ! WRITE (msg,'(3(i8,1x),3(f12.6,1x))') pp, idp(pp), dtp(pp), xp1, yp1, zp_m1 + ! CALL wrf_debug (wrfdbg, 'SPFire after 1-pass adv: pp,id,dt,xp,yp,zp: >>> '//msg) + ! WRITE (msg,*) pp, z_at_w(FLOOR(xp1),1,FLOOR(yp1)) + ! CALL wrf_debug (wrfdbg, 'SPFire after 1-pass adv: zn: >>> '//msg) + ! WRITE (msg,*) pp, z_at_w(FLOOR(xp1-dp05),1,FLOOR(yp1-dp05)) + ! CALL wrf_debug (wrfdbg, 'SPFire after 1-pass adv: zn: >>> '//msg) + + zp1 = hgt2k(xp=xp1, yp=yp1, hgt=zp_m1, z_at_w=z_at_w, znw=znw) + wp = uvw2p(3) + + !------------------------------------------------------------------------------- + ! Is the new position not on the same memory? + !------------------------------------------------------------------------------- + ! check for floor(x)+1 bc we use i and i+1 to interpolate + IF (FLOOR(xp1-dp05) < ihs .OR. FLOOR(xp1-dp05) +1 > ihe .OR. & + FLOOR(yp1-dp05) < jhs .OR. FLOOR(yp1-dp05) +1 > jhe) THEN + ! Checking staggered positions relative to unstaggered variables (+- dp05) + + !------------------------------------------------------------------------------- + ! Use only one pass + !------------------------------------------------------------------------------- + + xout(pp) = xp1 + yout(pp) = yp1 + zout(pp) = zp_m1 + zp = zp1 + + ELSE + + ! 2ND PASS + !============================================================= + + !------------------------------------------------------------------------------- + ! #2 interpolate velocities to particle position & advect particle + !------------------------------------------------------------------------------- + + uvw2p = uvw_3d_interp(x=xp1, y=yp1, z=zp1, u=u, v=v, w=w, ihs=ihs, jhs=jhs, ihe=ihe, jhe=jhe) ! args: grid fractional positions + + ! WRITE (msg,'(3(f14.9,1x))') uvw2p(1), uvw2p(2), uvw2p(3) + ! CALL wrf_debug (wrfdbg, 'SPFire_advect effective vel m/s: u,v,w 2nd pass>>> '//TRIM(msg)) + + xp_m2 = uvw2p(1)*DT + xp_m0 + yp_m2 = uvw2p(2)*DT + yp_m0 + zp_m2 = uvw2p(3)*DT + zp_m0 + + xp2 = xp_m2 * mf(FLOOR(xp1),FLOOR(yp1)) + yp2 = yp_m2 * mf(FLOOR(xp1),FLOOR(yp1)) + + IF((ABS(xp2-xp1) > 2.0) .OR. (ABS(yp2-yp1) > 2.0)) THEN + WRITE (msg,'(2(i4,1x),7(f10.6,1x))') pp, dtp(pp), xp1, yp1, zp1, & + uvw2p(1), uvw2p(2), uvw2p(3), dt ! + zout(pp) = ZERO_dp + xout(pp) = ZERO_dp + yout(pp) = ZERO_dp + CYCLE + ENDIF + + + !------------------------------------------------------------------------------- + ! Is the new position not on the same memory? + !------------------------------------------------------------------------------- + + ! check against tile +- halo points instead + + IF (FLOOR(xp2-dp05) < ihs .OR. FLOOR(xp2-dp05) +1 > ihe .OR. & + FLOOR(yp2-dp05) < jhs .OR. FLOOR(yp2-dp05) +1 > jhe) THEN + ! Checking staggered positions relative to unstaggered variables (+- dp05) + + !------------------------------------------------------------------------------- + ! Use xp1, yp1 to find zp2 (z_at_w will be out of bounds) + !------------------------------------------------------------------------------- + + zp2 = hgt2k(xp=xp1, yp=yp1, hgt=zp_m2, z_at_w=z_at_w, znw=znw) + + ELSE + + zp2 = hgt2k(xp=xp2, yp=yp2, hgt=zp_m2, z_at_w=z_at_w, znw=znw) + + ENDIF + + !============================================================= + + !------------------------------------------------------------------------------- + ! Average both passes + !------------------------------------------------------------------------------- + + xp2 = (xp1 + xp2) * dp05 + yp2 = (yp1 + yp2) * dp05 + zp_m2 = (zp_m1 + zp_m2) * dp05 + zp2 = (zp1 + zp2) * dp05 + + xout(pp) = xp2 + yout(pp) = yp2 + zout(pp) = zp_m2 + zp = zp2 + wp = (uvw2p(3) + wp) * dp05 + + ENDIF + + !============================================================= + ! Transport Done :) + !============================================================= + ! WRITE (msg, '(a100)') ' ' + ! WRITE (msg,'(2(i6,1x),4(f12.6,1x))') pp, dtp(pp), xout(pp), yout(pp), zout(pp), zp + ! CALL wrf_debug (wrfdbg, 'SPFire_advect AFTER : pp,id,dt,xp,yp,zp,kp: >>> '//msg) + + !============================================================= + ! Firebrand Physics + !============================================================= + + loc_p = ZERO_dp + loc_d = ZERO_dp + loc_t = ZERO_dp + + IF (zout(pp) <= ZERO_dp) THEN + fs_p_prop(pp) = p_properties(ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP) + zout(pp) = ZERO_dp + CYCLE ! Skip physics + ENDIF + + IF (ISNAN(zout(pp)) .OR. ABS(zout(pp)) > HUGE(1.0)) THEN + fs_p_prop(pp) = p_properties(ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP) + zout(pp) = ZERO_dp + xout(pp) = ZERO_dp + yout(pp) = ZERO_dp + + CYCLE ! Skip physics + ENDIF + + IF (dtp(pp) < start_mom3d_dt) THEN ! Temporarily testing transport: extending particle lifetime + ! WRITE (msg,*) dt + ! CALL wrf_debug (wrfdbg, 'SPFire_physics cycle dt: '//msg) + CYCLE + ENDIF + + + CALL get_local_met( xp = xout(pp), & + yp = yout(pp), & + zp = zp, & + ihs = ihs, & + jhs = jhs, & + ihe = ihe, & + jhe = jhe, & + p = phyd, & ! air pressure (Pa) + t = thet, & ! temperature (K) + d = rho, & ! density (kg/m3) + loc_p = loc_p, & ! local air pressure 3d (Pa) + loc_d = loc_d, & ! local density (g/m3) + loc_t = loc_t) ! local temperature (K) + + ! WRITE (msg,'(3(f16.8,1x))') grid%p_hyd_w(FLOOR(xout(pp)),FLOOR(zp),FLOOR(yout(pp))), & + ! grid%rho(FLOOR(xout(pp)),FLOOR(zp),FLOOR(yout(pp))), & + ! grid%t_phy(FLOOR(xout(pp)),FLOOR(zp),FLOOR(yout(pp))) + ! CALL wrf_debug (wrfdbg, 'SPFire grid%p, %rho, %t_phy : '//msg) + + ! WRITE (msg,'(4(f16.8,1x))') loc_p, loc_d, loc_t, wp + ! CALL wrf_debug (wrfdbg, 'SPFire br met p, d, t, w : '//msg) + + !------------------------------------------------------------------------------- + ! Firebrand Burnout and Terminal Velocity + !------------------------------------------------------------------------------- + + brz = zout(pp) + CALL firebrand_physics(dt = dt, & + hgt = brz, & + loc_w = wp, & + loc_p = loc_p, & + loc_t = loc_t, & + loc_d = loc_d, & + fbprop = fs_p_prop(pp)) + + IF (ISNAN(fs_p_prop(pp)%p_tvel)) THEN + fs_p_prop(pp) = p_properties(ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP) + zout(pp) = ZERO_dp + + ! WRITE (msg,'(3(i8,1x),4(f12.6,1x))') pp, idp(pp), dtp(pp), xout(pp), yout(pp), brz, fs_p_prop(pp)%p_tvel + ! CALL wrf_debug (wrfdbg, 'SPFire_physics AFTER: pp,id,dt,xp,yp,zp,fbvt: >>> '//msg) + + ELSE + + zout(pp) = brz + + ENDIF + ! WRITE (msg,'(3(i8,1x),4(f12.6,1x))') pp, idp(pp), dtp(pp), xout(pp), yout(pp), brz, fs_p_prop(pp)%p_tvel*dt + ! CALL wrf_debug (wrfdbg, 'SPFire_physics AFTER: pp,id,dt,xp,yp,zp,fbvt: >>> '//msg) + + ENDDO + + END SUBROUTINE advect_xyz_m +!============================================================= + + +! Cannot be elemental: array +!============================================================= +PURE FUNCTION uvw_3d_interp(x, y, z, u, v, w, ihs, jhs, ihe, jhe) +!============================================================= + + ! Wrapper to interpolate U,V,W to a point within a 3D grid box + + ! Interpolate velocities at grid point interfaces to particle position + ! input range needed: i:i+1 | i < x(i) <= i+1 + ! j:j+1 | j < y(j) <= j+1 + ! k:k+1 | k < z(k) <= k+1 + + ! arguments + ! x, y, z: fractional position to interpolate velocities (e.g.: z is a fraction of the k index) + ! u, v, w: wind components + ! is, js : start index of wind components + + !------------------------------------------------------------- + ! Interpolate between planes: U: xy(k), xy(k+1) stag x, dim=1 + ! V: xy(k), xy(k+1) stag y, dim=2 + ! W: xz(j), xz(j+1) stag z, dim=2 + ! + ! particle position is not staggered: + ! grid center is given by (FLOOR(x), FLOOR(y)) + !------------------------------------------------------------- + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: ihs, jhs, ihe, jhe ! tile +- halo array bounds + REAL, INTENT(IN) :: x, y, z ! fractional positions relative to integral array indices + REAL, INTENT(IN), DIMENSION(ims:, 1:, jms:) :: u, v ,w + REAL, DIMENSION(3) :: uvw_3d_interp + REAL :: tru, trv, trw, x0, y0, z0 + INTEGER :: i, j, k, i0, j0, k0 + + ! Unstaggerecd indices + k = MAX(FLOOR(z), 1) + j = FLOOR(y) + i = FLOOR(x) + + ! Variable Staggering: + ! particle position is equivalent to a staggered dimension + ! the staggered domain begins 1/2 pnt before and ends 1/2 pnt after the regular domain + ! e.g: interpolate u (stag x): x_stag = xp, y_unst = yp -0.5, z_unst = zp -0.5 + ! By shifting the particle position by 1/2 point, + ! the unstaggered velocities will represent the grid edge instead of grid center + + x0 = x - dp05 + y0 = y - dp05 + z0 = z - dp05 ! z can be < 1.0 when below the first half level, u_3d_interp will extrapolate values + + i0 = FLOOR(x0) !MIN(MAX(ihs, ), ihe) ! shift indices by 0.5 because unstaggered variables given at horizontal grid center + j0 = FLOOR(y0) !MIN(MAX(jhs, ), ihe) + k0 = MAX(1, FLOOR(z0)) + + !----------------------------------------------------------------------------- + ! u,v interpolates between xy planes: xy(k) xy(k+1) + !----------------------------------------------------------------------------- + + ! staggered x + tru= u_3d_interp( x=x, y=y0, z=z0, & + u_i0j0_bot=u(i, k0, j0 ), u_i0j1_bot=u(i, k0, j0+1), u_i1j0_bot=u(i+1, k0, j0 ), u_i1j1_bot=u(i+1, k0, j0+1), & + u_i0j0_top=u(i, k0+1, j0 ), u_i0j1_top=u(i, k0+1, j0+1), u_i1j0_top=u(i+1, k0+1, j0 ), u_i1j1_top=u(i+1, k0+1, j0+1)) + + ! staggered y + trv= u_3d_interp( x=x0, y=y, z=z0, & + u_i0j0_bot=v(i0, k0, j ), u_i0j1_bot=v(i0, k0, j+1), u_i1j0_bot=v(i0+1, k0, j ), u_i1j1_bot=v(i0+1, k0, j+1), & + u_i0j0_top=v(i0, k0+1, j ), u_i0j1_top=v(i0, k0+1, j+1), u_i1j0_top=v(i0+1, k0+1, j ), u_i1j1_top=v(i0+1, k0+1, j+1)) + + !----------------------------------------------------------------------------- + ! w interpolates between xz planes, not xy (so y corresponds to z): xz(j) xz(j+1) + !----------------------------------------------------------------------------- + + ! z and w are both staggered (z is derived from grid%z_at_w) + trw= u_3d_interp( x=x0, y=z, z=y, & + u_i0j0_bot=w(i0, k, j ), u_i0j1_bot=w(i0, k+1, j ), u_i1j0_bot=w(i0+1, k, j ), u_i1j1_bot=w(i0+1, k+1, j ), & + u_i0j0_top=w(i0, k, j+1), u_i0j1_top=w(i0, k+1, j+1), u_i1j0_top=w(i0+1, k, j+1), u_i1j1_top=w(i0+1, k+1, j+1)) + + !----------------------------------------------------------------------------- + ! Return U, V, W at x, y, z positions + !----------------------------------------------------------------------------- + + uvw_3d_interp = [tru, trv, trw] + +END FUNCTION uvw_3d_interp +!============================================================= + + + +!============================================================= +ELEMENTAL FUNCTION u_3d_interp(x, y, z, & + u_i0j0_bot, u_i0j1_bot, u_i1j0_bot, u_i1j1_bot, & + u_i0j0_top, u_i0j1_top, u_i1j0_top, u_i1j1_top ) +!============================================================= + + ! Interpolate velocities at grid point interfaces to particle position + ! input range needed: u( i: i+1, j: j+1, k: k+1) | + ! i < x <= i+1 + ! j < y <= j+1 + ! k < z <= k+1 + + ! arguments + ! x, y, z: particle position to interpolate velocities + ! u, v, w: wind components + + !----------------------------------------------------------------------------- + ! Bilinear interpolation : + ! The product of the value at the desired point (x,y,z) and the area enclosed by the corners + ! is equal to the sum of the + ! products of the value at each corner and the partial area diagonally opposite the corner + !----------------------------------------------------------------------------- + + !----------------------------------------------------------------------------- + ! The 3-D interpolation is achieved with a linear interpolation between + ! the xy-interpolated points from two adjacent planes + !----------------------------------------------------------------------------- + + !----------------------------------------------------------------------------- + ! Z must be offset by 0.5 to correctly interpolate between vertically unstaggered planes + !----------------------------------------------------------------------------- + + REAL, INTENT(IN) :: x, y, z ! fractional position + REAL, INTENT(IN) :: u_i0j0_bot, u_i0j1_bot, u_i1j0_bot, u_i1j1_bot ! u_lower + REAL, INTENT(IN) :: u_i0j0_top, u_i0j1_top, u_i1j0_top, u_i1j1_top ! u_upper + REAL :: u_3d_interp + + REAL :: dbot, dtop, u_lower, u_upper + INTEGER :: k, j, i + + !----------------------------------------------------------------------------- + ! Get weighted U, V, W at particle position based on surrounding xy and xz planes + !----------------------------------------------------------------------------- + + ! index + k = MAX(1, FLOOR(z)) ! To interpolate w, z corresponds y: ( x=x, y=z, z=y, ... + + ! z can be < 1.0 when interpolating below the first level on half levels + ! e.g.: z=0.75, dbot = 0.75 -1 =-0.25, dtop = 2 - 0.75 =1.25 + ! u_3d_interp = u_upper*-0.25 + u_lower*1.25 + + dbot = z - REAL(k) + dtop = REAL(k+1) - z + + !----------------------------------------------------------------------------- + ! j = FLOOR(y) + ! i = FLOOR(x) + + u_lower = u_2d_interp( u_i0j0=u_i0j0_bot, u_i0j1=u_i0j1_bot, & + u_i1j0=u_i1j0_bot, u_i1j1=u_i1j1_bot, xp=x, yp=y) + u_upper = u_2d_interp( u_i0j0=u_i0j0_top, u_i0j1=u_i0j1_top, & + u_i1j0=u_i1j0_top, u_i1j1=u_i1j1_top, xp=x, yp=y) + + !----------------------------------------------------------------------------- + ! Apply weights and calculate U at particle position + !----------------------------------------------------------------------------- + + u_3d_interp = u_upper * dbot + & ! + u_lower * dtop + + !----------------------------------------------------------------------------- + +END FUNCTION u_3d_interp +!============================================================= + +!============================================================= + + + +!============================================================= + ELEMENTAL FUNCTION u_2d_interp(u_i0j0, u_i0j1, u_i1j0, u_i1j1, xp, yp) +!============================================================= + + ! Interpolate any variable at grid point interfaces to a position in between + ! Minimum range: u(i:i+1, j:j+1) | i < xp <= i+1 & j < yp <= j+1 + + ! arguments + ! u : 2D variable at grid 4 corners: (i0,j0); (i0,j1); (i1,j0); (i1,j1) + ! xp, yp : position within grid box to interpolate velocity + ! is, js : start index of variable array boundary + + !------------------------------------------------------------- + ! 2D Interpolation sketch + ! a, b, c, d: grid point corners + ! da, db, dc, dd: distance between the nearest corner and point projection + ! + ! a + + + + + + d + ! + dc | db + + ! +____|____+ + ! + dd | da + + ! + | + + ! b + + + + + + c + !------------------------------------------------------------- + + IMPLICIT NONE + + !INTEGER, INTENT(IN) :: is, js + REAL, INTENT(IN) :: xp, yp + REAL, INTENT(IN) :: u_i0j1, u_i0j0, u_i1j0, u_i1j1 ! DIMENSION(is:,js:) :: uu + REAL :: u_2d_interp + REAL :: d_a, d_b, d_c, d_d ! grid vertices + REAL :: uu_a, uu_b, uu_c, uu_d ! advection at vertices + INTEGER :: i, j + + i = FLOOR(xp) + j = FLOOR(yp) + + uu_a = u_i0j1 ! uu(i ,j+1) + uu_b = u_i0j0 ! uu(i ,j ) + uu_c = u_i1j0 ! uu(i+1,j ) + uu_d = u_i1j1 ! uu(i+1,j+1) + + ! WRITE (msg,'(4(f14.9,1x))') uu_a, uu_b, uu_c, uu_d + ! CALL wrf_debug (wrfdbg, 'SPFire_uu_2d: uu _a,_b,_c,_d >>> '//TRIM(msg)) + + d_a = ABS( ( REAL(i+1) - xp ) * ( REAL(j ) - yp ) ) + d_b = ABS( ( REAL(i+1) - xp ) * ( REAL(j+1) - yp ) ) + d_c = ABS( ( REAL(i ) - xp ) * ( REAL(j+1) - yp ) ) + d_d = ABS( ( REAL(i ) - xp ) * ( REAL(j ) - yp ) ) + + ! WRITE (msg,'(4(f14.9,1x))') d_a, d_b, d_c, d_d + ! CALL wrf_debug (wrfdbg, 'SPFire_uu_2d: d _a,_b,_c,_d >>> '//TRIM(msg)) + + + u_2d_interp = ( uu_a * d_a + & + uu_b * d_b + & + uu_c * d_c + & + uu_d * d_d ) / & + (d_a + d_b + d_c + d_d) + + ! WRITE (msg,*) uu_2d_interp + ! CALL wrf_debug (wrfdbg, 'SPFire_uu_2d: >>> '//TRIM(msg)) + +END FUNCTION u_2d_interp +!============================================================= + +!============================================================= + + + + +!============================================================= +PURE SUBROUTINE remove_br( fs_p_id, fs_p_x, fs_p_y, fs_p_z, fs_p_dt, fs_p_effd, cnt, max_life_dt, land_hgt) +!============================================================= + IMPLICIT NONE + INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id + INTEGER, INTENT(IN), DIMENSION(:) :: fs_p_dt + !INTEGER, INTENT(IN) :: active_br + REAL, INTENT(IN), DIMENSION(:) :: fs_p_x, fs_p_y, fs_p_z, fs_p_effd + LOGICAL, DIMENSION(fs_array_maxsize) :: sparse_mask + INTEGER, INTENT(OUT) :: cnt + + INTEGER, INTENT(IN) :: max_life_dt + REAL, INTENT(IN) :: land_hgt + + !------------------------------------------------------------------------------- + + sparse_mask = .FALSE. + !------------------------------------------------------------------------------- + ! Flag to remove brands beyond domain bounds + !------------------------------------------------------------------------------- + + sparse_mask = ( fs_p_id > 0 .AND. & + (FLOOR(fs_p_x-dp05) < ids .OR. FLOOR(fs_p_y-dp05) < jds .OR. & + FLOOR(fs_p_x-dp05)+1 > ide .OR. FLOOR(fs_p_y-dp05)+1 > jde)) + ! interpolation needs i, j, i+1, j+1 + ! First we unstagger fs_p_x, fs_p_y to align with met variables (x=fs_p_x-0.5, y=fs_p_y-0.5) + ! Then we use FLOOR(x), FLOOR(y), FLOOR(x)+1, FLOOR(y)+1 to interpolate + + !------------------------------------------------------------------------------- + ! Flag to remove brands living longer than firebrand_max_life_dt + !------------------------------------------------------------------------------- + + sparse_mask = ( sparse_mask .OR. & + (fs_p_id > 0 .AND. fs_p_dt > max_life_dt)) + + !------------------------------------------------------------------------------- + ! Flag to remove brands with near zero mass + !------------------------------------------------------------------------------- + + ! sparse_mask = (sparse_mask .OR. & + ! (fs_p_id > 0 .AND. fs_p_mass <= br_min_mass)) + + !------------------------------------------------------------------------------- + ! Flag to remove brands with near zero diameter that are not at deposit height + !------------------------------------------------------------------------------- + + sparse_mask = (sparse_mask .OR. & + (fs_p_id > 0 .AND. ((fs_p_effd <= TINY(1.0) .AND. (fs_p_z > land_hgt))))) + + !------------------------------------------------------------------------------- + ! Reset array + !------------------------------------------------------------------------------- + cnt = 0 + cnt = COUNT(sparse_mask) + IF (cnt > 0) THEN + + WHERE(sparse_mask) fs_p_id = 0 + + ! WRITE (msg,'(3(i8,1x))') COUNT(sparse_mask), COUNT( fs_p_id > 0 ) + ! CALL wrf_debug (wrfdbg, 'SPFire_remove removed, remaining: '//msg) + !CALL wrf_error_fatal( "end of test") + ENDIF + +END SUBROUTINE remove_br + +!============================================================= +!============================================================= + + +!============================================================= +PURE SUBROUTINE deposit_br(fs_p_id, fs_p_x, fs_p_y, fs_p_z, sum_fbrand, land_hgt) +!============================================================= + + IMPLICIT NONE + INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id + REAL, INTENT(INOUT), DIMENSION(ims:ime,jms:jme) :: sum_fbrand + REAL, INTENT(IN), DIMENSION(:) :: fs_p_x, fs_p_y, fs_p_z + REAL, INTENT(IN) :: land_hgt + + !INTEGER, INTENT(IN) :: active_br + + !INTEGER, ALLOCATABLE, DIMENSION(:) :: mask_idx + INTEGER, ALLOCATABLE, DIMENSION(:) :: rx, ry, rid + LOGICAL, DIMENSION(fs_array_maxsize) :: sparse_mask + LOGICAL, DIMENSION(fs_array_maxsize) :: bounds_mask + !LOGICAL, ALLOCATABLE, DIMENSION(:) :: bounds_mask + INTEGER :: nresets + INTEGER :: x, y, i0,k ! counters + !CHARACTER(LEN=10) fmt + + !CALL wrf_debug (wrfdbg, 'SPFire_deposit: check') + sum_fbrand(:,:) = ZERO_dp + + !------------------------------------------------------------------------------- + ! Flag brands near ground (deps_lev is a declared parameter) + !------------------------------------------------------------------------------- + + sparse_mask = .FALSE. + bounds_mask = .FALSE. + + ! Only deposit particles on this tile + bounds_mask = ((FLOOR(fs_p_x) >= is) .OR. & + (FLOOR(fs_p_x) <= ie) .OR. & + (FLOOR(fs_p_y) >= js) .OR. & + (FLOOR(fs_p_y) <= je)) + + sparse_mask = ( bounds_mask .AND. (fs_p_id > 0 .AND. fs_p_z <= land_hgt)) + + !------------------------------------------------------------------------------- + ! Deposit flagged brands + !------------------------------------------------------------------------------- + + nresets = COUNT(sparse_mask) + + ! WRITE (msg,'(i6)') nresets + ! CALL wrf_debug (wrfdbg, 'SPFire_deposit total: '//msg) + + IF (nresets > 0) THEN + + !------------------------------------------------------------------------------- + ! convert position to integer indices + !------------------------------------------------------------------------------- + + ALLOCATE(rx(nresets),ry(nresets))!, mask_idx(nresets), rid(nresets), bounds_mask(nresets)) + + ! Get indices of particles when sparse_mask = True + !mask_idx = idx_packed_1d(mask=sparse_mask) + rx = 0 + ry = 0 + + ! rx(1:nresets) = MIN(ie, NINT(PACK(fs_p_x, sparse_mask)) ) ! Using Nint to destager grid + ! ry(1:nresets) = MIN(je, NINT(PACK(fs_p_y, sparse_mask)) ) + rx(1:nresets) = FLOOR(PACK(fs_p_x, sparse_mask)) + ry(1:nresets) = FLOOR(PACK(fs_p_y, sparse_mask)) + + + ! Use indices to fetch x, y positions and id of depositing particles + ! rid = 0 + ! rx(1:nresets) = FLOOR(fs_p_x(mask_idx)) + ! ry(1:nresets) = FLOOR(fs_p_y(mask_idx)) + ! rid(1:nresets) = fs_p_id(mask_idx) + + ! !------------------------------------------------------------------------------- + ! ! adjust depositing position to inside this tile + ! ! (incrementing sum_fbrand at memory buffer leads to memory error) + ! !------------------------------------------------------------------------------- + + ! bounds_mask = .FALSE. + ! bounds_mask = (rid > 0 .AND. & + ! ((rx < is) .OR. (rx > ie) .OR. & + ! (ry < js) .OR. (ry > je))) + + ! IF (COUNT(bounds_mask) > 0) THEN + + ! WRITE (msg,'(1(i6,1x))') COUNT(bounds_mask) + ! CALL wrf_debug (wrfdbg, 'SPFire_deposit adjust out of tile bounds'//msg) + + ! WHERE (rid > 0 .AND. rx < is) rx = is + ! WHERE (rid > 0 .AND. rx > ie) rx = ie + ! WHERE (rid > 0 .AND. ry < js) ry = js + ! WHERE (rid > 0 .AND. ry > je) ry = je + ! ENDIF + + !------------------------------------------------------------------------------- + ! Increment sum_fbrand + !------------------------------------------------------------------------------- + + DO k=1,nresets + sum_fbrand(rx(k),ry(k)) = sum_fbrand(rx(k),ry(k)) + 1.0_dp + ENDDO + + !------------------------------------------------------------------------------- + ! Reset array + !------------------------------------------------------------------------------- + + WHERE(sparse_mask) fs_p_id= 0 + + ! WRITE (msg,'(5(i8,1x))') COUNT(sparse_mask), COUNT( fs_p_id > 0 ), COUNT(rx >= ie), COUNT(ry >= je) + ! CALL wrf_debug (wrfdbg, 'SPFire_deposit deposited, remaining, outofbounds: '//msg) + + ENDIF + +END SUBROUTINE deposit_br + + +!============================================================= +!============================================================= + + + + !------------------------------------------------------------------------------ + ! UTILITIES + !------------------------------------------------------------------------------ + + + !============================================================= + + SUBROUTINE get_local_ijk(grid, & + ims, ime, jms, jme, kms, kme, & + ips, ipe, jps, jpe, kps, kpe, & + ifps, ifpe, jfps, jfpe, & + ifms, ifme, jfms, jfme, & + ids, ide, jds, jde, kds, kde, & + m_idim, m_jdim, m_kdim, & + p_idim, p_jdim, p_kdim, & + d_idim, d_jdim, d_kdim) + !p2m_is, p2m_ie, p2m_js, p2m_je) + + USE module_domain, ONLY: get_ijk_from_grid, get_ijk_from_subgrid + + IMPLICIT NONE + + TYPE(DOMAIN), INTENT(IN) :: grid + INTEGER, INTENT(OUT), OPTIONAL :: ims, ime, jms, jme, kms, kme + INTEGER, INTENT(OUT), OPTIONAL :: ips, ipe, jps, jpe, kps, kpe + INTEGER, INTENT(OUT), OPTIONAL :: ifps, ifpe, jfps, jfpe + INTEGER, INTENT(OUT), OPTIONAL :: ifms, ifme, jfms, jfme + INTEGER, INTENT(OUT), OPTIONAL :: ids, ide, jds, jde, kds, kde + INTEGER, INTENT(OUT), OPTIONAL :: m_idim, m_jdim, m_kdim + INTEGER, INTENT(OUT), OPTIONAL :: p_idim, p_jdim, p_kdim + INTEGER, INTENT(OUT), OPTIONAL :: d_idim, d_jdim, d_kdim + !INTEGER, INTENT(OUT), OPTIONAL :: p2m_is, p2m_ie, p2m_js, p2m_je + + + INTEGER :: iims, iime, jjms, jjme, kkms, kkme + INTEGER :: iips, iipe, jjps, jjpe, kkps, kkpe + INTEGER :: iids, iide, jjds, jjde, kkds, kkde + + INTEGER :: iifps, iifpe, jjfps, jjfpe, kkfps, kkfpe + INTEGER :: iifms, iifme, jjfms, jjfme, kkfms, kkfme + INTEGER :: iifds, iifde, jjfds, jjfde, kkfds, kkfde + + IF ((.NOT. PRESENT(ims)) .AND. & + (.NOT. PRESENT(jms)) .AND. & + (.NOT. PRESENT(kms)) .AND. & + (.NOT. PRESENT(ime)) .AND. & + (.NOT. PRESENT(jme)) .AND. & + (.NOT. PRESENT(kme)) .AND. & + ! + (.NOT. PRESENT(ips)) .AND. & + (.NOT. PRESENT(jps)) .AND. & + (.NOT. PRESENT(kps)) .AND. & + (.NOT. PRESENT(ipe)) .AND. & + (.NOT. PRESENT(jpe)) .AND. & + (.NOT. PRESENT(kpe)) .AND. & + ! + (.NOT. PRESENT(ifps)) .AND. & + (.NOT. PRESENT(jfps)) .AND. & + (.NOT. PRESENT(ifpe)) .AND. & + (.NOT. PRESENT(jfpe)) .AND. & + ! + (.NOT. PRESENT(ifms)) .AND. & + (.NOT. PRESENT(jfms)) .AND. & + (.NOT. PRESENT(ifme)) .AND. & + (.NOT. PRESENT(jfme)) .AND. & + ! + (.NOT. PRESENT(ids)) .AND. & + (.NOT. PRESENT(jds)) .AND. & + (.NOT. PRESENT(kds)) .AND. & + (.NOT. PRESENT(ide)) .AND. & + (.NOT. PRESENT(jde)) .AND. & + (.NOT. PRESENT(kde)) .AND. & + ! + ! (.NOT. PRESENT(p2m_is)) .AND. & + ! (.NOT. PRESENT(p2m_ie)) .AND. & + ! (.NOT. PRESENT(p2m_js)) .AND. & + ! (.NOT. PRESENT(p2m_je)) .AND. & + ! + (.NOT. PRESENT(m_idim)) .AND. & + (.NOT. PRESENT(m_jdim)) .AND. & + (.NOT. PRESENT(m_kdim)) .AND. & + (.NOT. PRESENT(p_idim)) .AND. & + (.NOT. PRESENT(p_jdim)) .AND. & + (.NOT. PRESENT(p_kdim)) .AND. & + (.NOT. PRESENT(d_idim)) .AND. & + (.NOT. PRESENT(d_jdim)) .AND. & + (.NOT. PRESENT(d_kdim))) & + ! + CALL wrf_debug ( 1, 'get_local_ijk function is NOT requesting a result' ) + + CALL get_ijk_from_grid ( grid , & + iids, iide, jjds, jjde, kkds, kkde, & + iims, iime, jjms, jjme, kkms, kkme, & + iips, iipe, jjps, jjpe, kkps, kkpe) + + IF (PRESENT(ifps) .OR. & + PRESENT(jfps) .OR. & + PRESENT(ifpe) .OR. & + PRESENT(jfpe) .OR. & + PRESENT(ifms) .OR. & + PRESENT(jfms) .OR. & + PRESENT(ifme) .OR. & + PRESENT(jfme)) CALL get_ijk_from_subgrid(grid , & + iifds, iifde, jjfds, jjfde, kkfds, kkfde, & + iifms, iifme, jjfms, jjfme, kkfms, kkfme, & + iifps, iifpe, jjfps, jjfpe, kkfps, kkfpe) + + + IF (PRESENT(ims)) ims = iims + IF (PRESENT(jms)) jms = jjms + IF (PRESENT(kms)) kms = kkms + IF (PRESENT(ime)) ime = iime + IF (PRESENT(jme)) jme = jjme + IF (PRESENT(kme)) kme = kkme + + IF (PRESENT(ips)) ips = iips + IF (PRESENT(jps)) jps = jjps + IF (PRESENT(kps)) kps = kkps + IF (PRESENT(ipe)) ipe = iipe + IF (PRESENT(jpe)) jpe = jjpe + IF (PRESENT(kpe)) kpe = kkpe + + IF (PRESENT(ifps)) ifps = iifps + IF (PRESENT(jfps)) jfps = jjfps + IF (PRESENT(ifpe)) ifpe = iifpe + IF (PRESENT(jfpe)) jfpe = jjfpe + + IF (PRESENT(ifms)) ifms = iifms + IF (PRESENT(jfms)) jfms = jjfms + IF (PRESENT(ifme)) ifme = iifme + IF (PRESENT(jfme)) jfme = jjfme + + IF (PRESENT(ids)) ids = iids + IF (PRESENT(jds)) jds = jjds + IF (PRESENT(kds)) kds = kkds + IF (PRESENT(ide)) ide = iide + IF (PRESENT(jde)) jde = jjde + IF (PRESENT(kde)) kde = kkde + + IF (PRESENT(m_idim)) m_idim = iime - iims + 1 + IF (PRESENT(m_jdim)) m_jdim = jjme - jjms + 1 + IF (PRESENT(m_kdim)) m_kdim = kkme - kkms + 1 + IF (PRESENT(p_idim)) p_idim = iipe - iips + 1 + IF (PRESENT(p_jdim)) p_jdim = jjpe - jjps + 1 + IF (PRESENT(p_kdim)) p_kdim = kkpe - kkps + 1 + IF (PRESENT(d_idim)) d_idim = iide - iids + 1 + IF (PRESENT(d_jdim)) d_jdim = jjde - jjds + 1 + IF (PRESENT(d_kdim)) d_kdim = kkde - kkds + 1 + + ! IF (PRESENT(p2m_is)) p2m_is = iips - iims + ! IF (PRESENT(p2m_ie)) p2m_ie = iipe - iims + ! IF (PRESENT(p2m_js)) p2m_js = jjps - jjms + ! IF (PRESENT(p2m_je)) p2m_je = jjpe - jjms + + ! p2m_is = ips - ims + ! p2m_ie = ips - ims + p_idim - 1 + ! p2m_js = jps - jms + ! p2m_je = jps - jms + p_jdim - 1 + + END SUBROUTINE get_local_ijk + +!============================================================= +!============================================================= + + + + +END MODULE module_firebrand_spotting + diff --git a/phys/module_firebrand_spotting_mpi.F b/phys/module_firebrand_spotting_mpi.F new file mode 100644 index 0000000000..f7f713de95 --- /dev/null +++ b/phys/module_firebrand_spotting_mpi.F @@ -0,0 +1,801 @@ +! MPI functions for lagrangian transport in Module Firebrand Spotting +! +!------------------------------------------------------------------------------- +! +!------------------------------------------------------------------------------- +! ATTENTION TO: +! Capitals vs. lower-case: In FORTRAN words use CAPITAL case +! Indent by 4 spaces +! Operators: use >, <, >=, <=, == (instead of .GT., .EQ., .GE. etc) +! Ordering of DO loops for fast memory access: +! The innermost loop goes over the 1st array dimension (fastest changing dimension) +! do j = 1, Jend; do k = 1, Kend; do i = 1, Iend; A(i, k, j); end do; end do; end do +!------------------------------------------------------------------------------- + +!============================================================= +!============================================================= + +MODULE module_firebrand_spotting_mpi + + USE module_domain, ONLY : get_ijk_from_grid, domain ! grid + USE module_configure, ONLY : grid_config_rec_type ! config_flags + !USE module_symbols_util, ONLY : WRFU_TimeInterval, WRFU_TimeIntervalGet, WRFU_TimeIntervalSet +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + USE MPI +#endif + IMPLICIT NONE + + PRIVATE + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + PUBLIC & + ! Module Variables: + neighbors, my_id, task_id, mpiprocs, & + left_id, right_id, up_id, down_id, & + upleft_id, upright_id, downleft_id, downright_id, & + ! Functions: + fs_mpi_recvfrompatch_real, & + fs_mpi_recvfrompatch_int, & + fs_mpi_recvbuffsize, & + fs_mpi_recvbuff1_real, & + fs_mpi_recvbuff1_int, & + fs_mpi_checkreceive, & + ! Subroutines: + fs_mpi_send2neighbors, & ! (task_id, mask, p_x, p_y, p_z, fs_p_m, fs_p_d, fs_p_e, fs_p_t, fs_p_v, p_id, p_dt) + fs_mpi_init, & + fs_mpi_nothing2send, & + fs_mpi_recv, & ! (np_id, task_id, r_x, r_y, r_z, r_p_m, r_p_d, r_p_e, r_p_t, r_p_v, r_id, r_dt) + fs_mpi_sendbuff_real, & + fs_mpi_sendbuff_int, & + fs_mpi_sendbuffsize, & + fs_mpi_sendbuff1_real, & + fs_mpi_sendbuff1_int +#endif + + ! THESE VARIABLES ARE IN MODULE SCOPE ! Careful with reassignments - don't reassign + ! SAVE attribute is default + + !------------------------------------------------------------------------------- + ! variables in module scope: private, only available module-wide (host association) + !------------------------------------------------------------------------------- + ! They should not be declared again in suboutines (may not compile) + ! and must not be among routine dummy arguments. Consequently, cannot be IO variables + ! + ! Runtime variables are not available at module level (e.g., namelist, tile dependent variables). + ! Include here only what can be set during compilation: + ! fixed parameters, allocatables, declarions (without initialization) + + !------------------------------------------------------------------------------- + ! Fixed parameters ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) + INTEGER, PARAMETER :: wrfdbg = 0 + + INTEGER, PARAMETER :: dp = KIND(0.d0) ! double precision + REAL, PARAMETER :: ZERO_dp = 0.0_dp ! this is a real type variable, not a double precision type + REAL, PARAMETER :: dp05 = 0.5_dp + REAL, PARAMETER :: dp1 = 1.0_dp + + !------------------------------------------------------------------------------- + ! Generic variables for multiple use within module ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + + CHARACTER (LEN=200), SAVE :: msg + CHARACTER (LEN=256), SAVE :: fmt + CHARACTER (LEN=200), DIMENSION(10) :: amsg + INTEGER, SAVE :: imsg ! loop counters + + !------------------------------------------------------------------------------- + ! MPI variables - Move particles between tiles - ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + + INTEGER, PARAMETER :: neighbors = 8 ! number of neighbor tasks - includes diagonals + INTEGER, PARAMETER :: mpi_datapack_nreal = 8 ! number of real type arrays packed together: 3 (xyz) + 5 (p_properties) + INTEGER, PARAMETER :: mpi_datapack_nint = 2 ! number of integer type arrays packed together 2 (id, dt) + + INTEGER, SAVE :: my_id, left_id, right_id, up_id, down_id + INTEGER, SAVE :: upleft_id, upright_id, downleft_id, downright_id + INTEGER, SAVE, DIMENSION(neighbors) :: task_id + + INTEGER, SAVE :: mpiprocs = 0 + !------------------------------------------------------------------------------- + ! grid and cf are not here because dimensions are given at runtime (derived types) + ! grid values change at every interaction, + ! therefore, it needs to be a dummy argument + !------------------------------------------------------------------------------- + + !------------------------------------------------------------------------------- + ! Variable bounds - Initialized in init, used in dummy arguments in driver + ! ***MODULE SCOPE*** + !------------------------------------------------------------------------------- + INTEGER, SAVE :: ids, jds, ide, jde, kde ! domain bounds + INTEGER, SAVE :: ims, jms, ime, jme, kms, kme ! memory bounds + INTEGER, SAVE :: is, ie, js, je, ks, ke ! patch start/end + INTEGER, SAVE :: ifps, jfps, ifpe, jfpe ! refined fire grid bounds +#endif + +CONTAINS +#if ( defined(DM_PARALLEL) && ! defined(STUBMPI) ) +!============================================================= +!============================================================= + + +!****************************************************************** +!****************************************************************** +!* * +!* MPI Routines * +!* * +!****************************************************************** +!****************************************************************** + +!============================================================= + FUNCTION fs_mpi_recvfrompatch_real(bsz, fromid) RESULT (buff) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: bsz, fromid + REAL, DIMENSION(bsz) :: buff ! p_x, p_y, p_z + INTEGER :: ierr, recvtag, ii + INTEGER :: stat(MPI_STATUS_SIZE) + + !------------------------------------------------------------------------------- + ! Receive a real type array of size bsz + !------------------------------------------------------------------------------- + + buff = ZERO_dp + recvtag = 2000 + fromid ! 2000: tag real type data + CALL mpi_recv(buff, bsz, MPI_FLOAT, fromid, recvtag, MPI_COMM_WORLD, stat, ierr) + + END FUNCTION fs_mpi_recvfrompatch_real +!============================================================= +!============================================================= + + +!============================================================= + FUNCTION fs_mpi_recvfrompatch_int(bsz, fromid) RESULT (buff) +!============================================================= + + IMPLICIT NONE + INTEGER, INTENT(IN) :: bsz, fromid + INTEGER, DIMENSION(bsz) :: buff + INTEGER :: ierr, recvtag, ii + INTEGER :: stat(MPI_STATUS_SIZE) + + !------------------------------------------------------------------------------- + ! Receive an int type array of size bsz + !------------------------------------------------------------------------------- + + buff = 0 + recvtag = 3000 + fromid ! 3000: tag int type data + CALL mpi_recv(buff, bsz, MPI_INTEGER, fromid, recvtag, MPI_COMM_WORLD, stat, ierr) + + END FUNCTION fs_mpi_recvfrompatch_int +!============================================================= +!============================================================= + + +!============================================================= + FUNCTION fs_mpi_recvbuffsize(fromid) RESULT(recvbuffsz) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: fromid + INTEGER :: recvbuffsz + INTEGER :: ierr, recvtag, sz , tag + INTEGER :: stat(MPI_STATUS_SIZE) + + !------------------------------------------------------------------------------- + ! Receive the buffer size (zero or nbr) + !------------------------------------------------------------------------------- + + recvbuffsz = 0 + sz = 1 ! one value corresponding to nbr (must send a number, receive is blocking) + tag = 1000 ! tag for communicating nbr + recvtag = tag + fromid + ierr = 0 + + !------------------------------------------------------------------------------- + IF (fromid > -1) THEN + CALL mpi_recv(recvbuffsz, sz, MPI_INTEGER, fromid, recvtag, MPI_COMM_WORLD, stat, ierr) + ENDIF + + END FUNCTION fs_mpi_recvbuffsize +!============================================================= +!============================================================= + +!============================================================= + FUNCTION fs_mpi_recvbuff1_real(fromid) RESULT(recvbuffsz) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: fromid + REAL :: recvbuffsz + INTEGER :: ierr, recvtag, sz , tag + INTEGER :: stat(MPI_STATUS_SIZE) + + !------------------------------------------------------------------------------- + ! Receive a real type scalar + !------------------------------------------------------------------------------- + + recvbuffsz = 0 + sz = 1 ! one value corresponding to nbr (must send a number, receive is blocking) + tag = 4000 ! tag for communicating nbr + recvtag = tag + fromid + ierr = 0 + + !------------------------------------------------------------------------------- + !IF (fromid > -1) THEN + CALL mpi_recv(recvbuffsz, sz, MPI_FLOAT, fromid, recvtag, MPI_COMM_WORLD, stat, ierr) + !ENDIF + + END FUNCTION fs_mpi_recvbuff1_real +!============================================================= +!============================================================= + + +!============================================================= + FUNCTION fs_mpi_recvbuff1_int(fromid) RESULT(recvbuffsz) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: fromid + INTEGER :: recvbuffsz + INTEGER :: ierr, recvtag, sz , tag + INTEGER :: stat(MPI_STATUS_SIZE) + + !------------------------------------------------------------------------------- + ! Receive a real type scalar + !------------------------------------------------------------------------------- + + recvbuffsz = 0 + sz = 1 ! one value corresponding to nbr (must send a number, receive is blocking) + tag = 5000 ! tag for communicating nbr + recvtag = tag + fromid + ierr = 0 + + !------------------------------------------------------------------------------- + !IF (fromid > -1) THEN + CALL mpi_recv(recvbuffsz, sz, MPI_INTEGER, fromid, recvtag, MPI_COMM_WORLD, stat, ierr) + !ENDIF + + END FUNCTION fs_mpi_recvbuff1_int +!============================================================= +!============================================================= + + + +!============================================================= + SUBROUTINE fs_mpi_send2neighbors(task_id, mask, p_x, p_y, p_z, p_id, p_dt, fs_p_m, fs_p_d, fs_p_e, fs_p_t, fs_p_v) +!============================================================= + + IMPLICIT NONE + + INTEGER, PARAMETER :: np = neighbors ! number or neighbor tasks + INTEGER, PARAMETER :: nreal = mpi_datapack_nreal ! number of real type arrays packed together + INTEGER, PARAMETER :: nint = mpi_datapack_nint ! number of integer type arrays packed together + + INTEGER, INTENT(IN), DIMENSION(:) :: task_id + LOGICAL, INTENT(IN), DIMENSION(:) :: mask + INTEGER, INTENT(IN), DIMENSION(:) :: p_id, p_dt + REAL, INTENT(IN), DIMENSION(:) :: p_x, p_y, p_z, fs_p_m, fs_p_d, fs_p_e, fs_p_t, fs_p_v + + LOGICAL, ALLOCATABLE, DIMENSION(:) :: masksendto + LOGICAL, ALLOCATABLE, DIMENSION(:) :: ml, mr, mu, md + INTEGER, ALLOCATABLE, DIMENSION(:) :: p_int + REAL, ALLOCATABLE, DIMENSION(:) :: p_real + + INTEGER :: ierr, nbr, ii, sendto, k + + !------------------------------------------------------------------------------- + + !task_id = [left_id, right_id, up_id, down_id, upleft_id, upright_id, downleft_id, downright_id] + ALLOCATE(masksendto(SIZE(mask)), ml(SIZE(mask)), mr(SIZE(mask)), mu(SIZE(mask)), md(SIZE(mask))) + + ml = .FALSE. + mr = .FALSE. + mu = .FALSE. + md = .FALSE. + + ml = (FLOOR(p_x) < is) ! MASK LEFT + mr = (FLOOR(p_x) > ie) ! MASK RIGHT + mu = (FLOOR(p_y) > je) ! MASK UP + md = (FLOOR(p_y) < js) ! MASK DONW + + !------------------------------------------------------------------------------- + ! Send to adjacent patch boundaries + !------------------------------------------------------------------------------- + + DO ii=1,np + sendto = task_id(ii) + + masksendto = .FALSE. + + IF (sendto > -1) THEN + IF (sendto == left_id) masksendto = ((mask .AND. ml) .AND. (.NOT. ( md .OR. mu) )) ! LEFT + IF (sendto == right_id) masksendto = ((mask .AND. mr) .AND. (.NOT. ( md .OR. mu) )) ! RIGHT + IF (sendto == up_id) masksendto = ((mask .AND. mu) .AND. (.NOT. ( ml .OR. mr) )) ! UP + IF (sendto == down_id) masksendto = ((mask .AND. md) .AND. (.NOT. ( ml .OR. mr) )) ! DONW + + IF (sendto == upleft_id) masksendto = (mask .AND. (mu .AND. ml) ) ! UPLEFT + IF (sendto == upright_id) masksendto = (mask .AND. (mu .AND. mr) ) ! UPRIGHT + IF (sendto == downleft_id) masksendto = (mask .AND. (md .AND. ml) ) ! DOWNLEFT + IF (sendto == downright_id) masksendto = (mask .AND. (md .AND. mr) ) ! DOWNRIGHT + + nbr = COUNT(masksendto) + + IF (nbr == 0) & + CALL fs_mpi_nothing2send(sendto=sendto) + + IF (nbr > 0) THEN + + ! WRITE (msg,'(2(i6,1x))') sendto, nbr + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi_sendaway sendto_id nbr: '//msg) + + ALLOCATE(p_real(nreal*nbr), p_int(nint*nbr)) + + p_real = [PACK(p_x,masksendto),& + PACK(p_y,masksendto),& + PACK(p_z,masksendto),& + PACK(fs_p_m,masksendto),& + PACK(fs_p_d,masksendto),& + PACK(fs_p_e,masksendto),& + PACK(fs_p_t,masksendto),& + PACK(fs_p_v,masksendto)] + p_int = [PACK(p_id,masksendto),& + PACK(p_dt,masksendto)] + + CALL fs_mpi_sendbuffsize(sendto=sendto, nbr=nbr) + CALL fs_mpi_sendbuff_real(sendto=sendto, bsz=nbr*nreal, buff=p_real) + CALL fs_mpi_sendbuff_int(sendto=sendto, bsz=nbr*nint, buff=p_int) + + ! DO k=1,MIN(nbr,5) + ! WRITE(msg,'(3(i6,1x),6(f12.6,1x))') sendto, p_int(k), p_int(k+nbr), p_real(k), p_real(k+nbr), p_real(k+2*nbr), p_real(k+4*nbr), p_real(k+6*nbr), p_real(k+7*nbr) + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi send >>> '// msg) + ! ENDDO + + + DEALLOCATE(p_real, p_int) + ENDIF + ENDIF + ENDDO + + DEALLOCATE(masksendto, ml, mr, mu, md) + +!============================================================= + END SUBROUTINE fs_mpi_send2neighbors +!============================================================= + + + + +!============================================================= + SUBROUTINE fs_mpi_init(grid) +!============================================================= + + USE module_dm, ONLY : ntasks_x, ntasks_y, mytask_x, mytask_y ! total tasks in x,y dimensions, this task i,j + + IMPLICIT NONE + INCLUDE "mpif.h" + + TYPE(domain), INTENT(IN) :: grid ! input data **Note: Intent IN** + + INTEGER :: ierr, numprocs + LOGICAL :: mpi_inited + CHARACTER (LEN=10) :: envval + + my_id = -1 + left_id = -1 + right_id = -1 + up_id = -1 + down_id = -1 + upleft_id = -1 + upright_id = -1 + downleft_id = -1 + downright_id = -1 + + CALL MPI_INITIALIZED( mpi_inited, ierr) + + IF ( .NOT. mpi_inited ) & + CALL wrf_error_fatal( "failed to initialize MPI") + + !------------------------------------------------------------------------------- + ! Who am I ? + !------------------------------------------------------------------------------- + + CALL MPI_COMM_RANK( MPI_COMM_WORLD, my_id, ierr) + CALL MPI_COMM_SIZE( MPI_COMM_WORLD, numprocs, ierr) + mpiprocs = numprocs ! mpiprocs and my_id are in module scope + + WRITE (msg,'(2(i6,1x))') my_id, numprocs + CALL wrf_debug (wrfdbg, 'SPFire_mpi mpi initialized. myid, nproc: '//msg) + + ! WRITE (msg,'(4(i6,1x))') ntasks_x, ntasks_y, mytask_x, mytask_y + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi tasks: '//msg) + + ! WRITE (msg,'(5(i9,1x))') my_id, is, ie, js, je + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi my_id, tile bounds: '//msg) + + ! CALL get_environment_variable ("WRF_NUM_TILES_X",envval, status=ierr) + ! WRITE (msg,'(1(a10,1x))') envval + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi: '//msg) + + !------------------------------------------------------------------------------- + ! Who are my neighbors? all neighbors *_id are declared in module scope + !------------------------------------------------------------------------------- + + ! get neighbors (-1 at domain bounds) + down_id = my_id - ntasks_x + up_id = my_id + ntasks_x + IF( mytask_y == 0) down_id = -1 + IF( mytask_y == (ntasks_y-1) ) up_id = -1 + + downleft_id = down_id - 1 + downright_id = down_id + 1 + upleft_id = up_id - 1 + upright_id = up_id + 1 + + IF (down_id == -1) downleft_id = -1 + IF (down_id == -1) downright_id = -1 + IF (up_id == -1) upleft_id = -1 + IF (up_id == -1) upright_id = -1 + + left_id = my_id - 1 + right_id = my_id + 1 + IF( mytask_x == 0) left_id = -1 + IF( mytask_x == (ntasks_x-1) ) right_id =-1 + + IF (left_id == -1) downleft_id = -1 + IF (left_id == -1) upleft_id = -1 + IF (right_id == -1) downright_id = -1 + IF (right_id == -1) upright_id = -1 + + ! WRITE (msg,'(5(i6,1x))') my_id, left_id, right_id, up_id, down_id + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi id, L, R, U, D: '//msg) + + ! WRITE (msg,'(4(i6,1x))') downleft_id, downright_id, upleft_id, upright_id + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi diag, DL, DR, UL, UR: '//msg) + + ! task_id is in module scope + task_id = [left_id, right_id, up_id, down_id, upleft_id, upright_id, downleft_id, downright_id] + + + ! row and column of the current process within the domain: + ! left_right ( 0 : ntasks_x -1 ) + ! up_down ( 0 : ntasks_y -1 ) + + ! my_task_i = MOD(my_id , ntasks_x) + ! my_task_j = my_id / ntasks_x + + !------------------------------------------------------------------------------- + ! Set bounds for finding the tiles to send and receive + ! *** variables declared in MODULE SCOPE *** + !------------------------------------------------------------------------------- + + CALL get_local_ijk(grid, & + ips=is, jps=js, ipe=ie, jpe=je, kps=ks, kpe=ke) + + ! WRITE (msg,'(6(i6,1x))') is, ie, js, je, ks, ke + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi_init tile bounds: '//msg) + + END SUBROUTINE fs_mpi_init +!============================================================= +!============================================================= + + + +!============================================================= + SUBROUTINE fs_mpi_nothing2send(sendto) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: sendto + INTEGER :: ierr, tag, nbr, sz + + !------------------------------------------------------------------------------- + ! Send a signal with value zero + !------------------------------------------------------------------------------- + + sz = 1 ! one value corresponding to nbr (must send a number, receive is blocking) + nbr = 0 + tag = 1000 + my_id ! tag for communicating nbr + + IF (sendto > -1) THEN + CALL mpi_send(nbr, sz, MPI_INTEGER, sendto, tag, MPI_COMM_WORLD, ierr) + ENDIF + + END SUBROUTINE fs_mpi_nothing2send + +!============================================================= +!============================================================= + + +!============================================================= + FUNCTION fs_mpi_checkreceive(task_list, np) RESULT(buffsz) +!============================================================= + + IMPLICIT NONE + + INTEGER :: np + INTEGER, DIMENSION(np) :: buffsz + INTEGER, INTENT(IN), DIMENSION(:) :: task_list + INTEGER :: ii, tmp2 + + buffsz(:) = 0 + + !------------------------------------------------------------------------------- + ! Anything to receive from any tile? + !------------------------------------------------------------------------------- + + DO ii=1,np + tmp2 = 0 + tmp2 = fs_mpi_recvbuffsize(fromid=task_list(ii)) + buffsz(ii) = tmp2 + ENDDO + + END FUNCTION fs_mpi_checkreceive + +!============================================================= +!============================================================= + + +!============================================================= + SUBROUTINE fs_mpi_recv(np_id, task_id, r_x, r_y, r_z, r_p_m, r_p_d, r_p_e, r_p_t, r_p_v, r_id, r_dt) +!============================================================= + + IMPLICIT NONE + + INTEGER, PARAMETER :: nreal = mpi_datapack_nreal ! number of real type arrays packed together + INTEGER, PARAMETER :: nint = mpi_datapack_nint ! number of integer type arrays packed together + INTEGER, PARAMETER :: np = neighbors + + INTEGER, INTENT(IN), DIMENSION(:) :: task_id, np_id + REAL, INTENT(OUT),DIMENSION(:) :: r_x, r_y, r_z + INTEGER, INTENT(OUT),DIMENSION(:) :: r_id, r_dt + REAL, INTENT(OUT),DIMENSION(:) :: r_p_m, r_p_d, r_p_e, r_p_t, r_p_v + + INTEGER :: np_sum, istart, iend, ii + INTEGER, ALLOCATABLE, DIMENSION(:,:) :: arr_int + REAL, ALLOCATABLE, DIMENSION(:,:) :: arr_real + + !------------------------------------------------------------------------------- + + np_sum = SUM(np_id) + ALLOCATE(arr_real(np_sum,nreal)) + ALLOCATE(arr_int(np_sum, nint)) + + arr_real = ZERO_dp + arr_int = 0 + + ! WRITE (msg,'(8(i4,1x))') (np_id(ii), ii=1,np) + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi_recv_all np_id >>> '// msg) + ! WRITE (msg,'(8(i4,1x))') (task_id(ii), ii=1,np) + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi_recv_all task_id >>> '// msg) + + istart = 1 + DO ii=1,np + + IF (np_id(ii) > 0) THEN + + iend = istart + np_id(ii) -1 + + ! WRITE (msg,'(4(i4,1x))') ii, np_id(ii), istart, iend + ! CALL wrf_debug (wrfdbg, 'SPFire_mpi recv all id, nbr, istart:iend >>> '// msg) +! WRITE (msg,*) SHAPE(arr_real) +! CALL wrf_debug (wrfdbg, 'SPFire_mpi_recv_all shape arr_real >>> '// msg) + + arr_real(istart:iend,:) = RESHAPE( & + fs_mpi_recvfrompatch_real(bsz=np_id(ii)*nreal, fromid=task_id(ii)), & + [np_id(ii),nreal]) + + arr_int(istart:iend,:) = RESHAPE( & + fs_mpi_recvfrompatch_int(bsz=np_id(ii)*nint, fromid=task_id(ii)), & + [np_id(ii), nint]) + + istart = istart + np_id(ii) + + ENDIF + ENDDO + + r_x = arr_real(:,1) + r_y = arr_real(:,2) + r_z = arr_real(:,3) + + r_p_m = arr_real(:,4) + r_p_d = arr_real(:,5) + r_p_e = arr_real(:,6) + r_p_t = arr_real(:,7) + r_p_v = arr_real(:,8) + + r_id = arr_int(:,1) + r_dt = arr_int(:,2) + + END SUBROUTINE fs_mpi_recv + +!============================================================= +!============================================================= + + + +!============================================================= + SUBROUTINE fs_mpi_sendbuff_real(bsz, sendto, buff) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: bsz, sendto + REAL, INTENT(IN), DIMENSION(bsz) :: buff ! p_x, p_y, p_z + INTEGER :: ierr, tag + + !------------------------------------------------------------------------------- + ! Send the type-real buffer: + ! a 1-d packed array composed of "nreal" flattened arrays, each of size "nbr" + !------------------------------------------------------------------------------- + + ierr = 0 + tag = 2000 + my_id ! 2000: tag real type data + CALL mpi_send(buff, bsz, MPI_FLOAT, sendto, tag, MPI_COMM_WORLD, ierr) + + END SUBROUTINE fs_mpi_sendbuff_real +!============================================================= + +!============================================================= + + + +!============================================================= + SUBROUTINE fs_mpi_sendbuff_int(bsz, sendto, buff) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: bsz, sendto + INTEGER, INTENT(IN), DIMENSION(bsz) :: buff ! p_id, p_dt + INTEGER :: ierr, tag + + !------------------------------------------------------------------------------- + ! Send the integer buffer: + ! a 1-d packed array composed of "nint" flattened arrays, each of size "nbr" + !------------------------------------------------------------------------------- + + ierr = 0 + tag = 3000 + my_id ! 3000: tag int type data + CALL mpi_send(buff, bsz, MPI_INTEGER, sendto, tag, MPI_COMM_WORLD, ierr) + + END SUBROUTINE fs_mpi_sendbuff_int +!============================================================= +!============================================================= + + + +!============================================================= + SUBROUTINE fs_mpi_sendbuffsize(nbr, sendto) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: nbr + INTEGER, INTENT(IN) :: sendto + INTEGER :: ierr, tag, sz + + !------------------------------------------------------------------------------- + ! Send an integer scalar or + ! Send the buffer size for an incoming array: + ! the number of elements in each array (real or int) that will be packed into one data buffer + ! and sent over by fs_mpi_sendbuff_real/int + !------------------------------------------------------------------------------- + + ierr = 0 + sz = 1 ! one value corresponding to nbr (must send a number, receive is blocking) + tag = 1000+my_id ! tag for communicating nbr + + CALL mpi_send(nbr, sz, MPI_INTEGER, sendto, tag, MPI_COMM_WORLD, ierr) + + END SUBROUTINE fs_mpi_sendbuffsize +!============================================================= +!============================================================= + + + +!============================================================= + SUBROUTINE fs_mpi_sendbuff1_real(nbr, sendto) +!============================================================= + + IMPLICIT NONE + + REAL, INTENT(IN) :: nbr + INTEGER, INTENT(IN) :: sendto + INTEGER :: ierr, tag, sz + + !------------------------------------------------------------------------------- + ! Send a real type scalar + !------------------------------------------------------------------------------- + + ierr = 0 + sz = 1 ! one value corresponding to nbr (must send a number, receive is blocking) + tag = 4000+my_id ! tag for communicating nbr + + CALL mpi_send(nbr, sz, MPI_FLOAT, sendto, tag, MPI_COMM_WORLD, ierr) + + END SUBROUTINE fs_mpi_sendbuff1_real +!============================================================= +!============================================================= + + +!============================================================= + SUBROUTINE fs_mpi_sendbuff1_int(nbr, sendto) +!============================================================= + + IMPLICIT NONE + + INTEGER, INTENT(IN) :: nbr + INTEGER, INTENT(IN) :: sendto + INTEGER :: ierr, tag, sz + + !------------------------------------------------------------------------------- + ! Send an integer scalar or + ! Send the buffer size for an incoming array: + ! the number of elements in each array (real or int) that will be packed into one data buffer + ! and sent over by fs_mpi_sendbuff_real/int + !------------------------------------------------------------------------------- + + ierr = 0 + sz = 1 ! one value corresponding to nbr (must send a number, receive is blocking) + tag = 5000+my_id ! tag for communicating nbr + + CALL mpi_send(nbr, sz, MPI_INTEGER, sendto, tag, MPI_COMM_WORLD, ierr) + + END SUBROUTINE fs_mpi_sendbuff1_int + +!============================================================= +!============================================================= + +!============================================================= + SUBROUTINE get_local_ijk(grid, ips, ipe, jps, jpe, kps, kpe) +!============================================================= + + USE module_domain, ONLY: get_ijk_from_grid + + IMPLICIT NONE + + TYPE(DOMAIN), INTENT(IN) :: grid + INTEGER, INTENT(OUT), OPTIONAL :: ips, ipe, jps, jpe, kps, kpe + + INTEGER :: iips, iipe, jjps, jjpe, kkps, kkpe + INTEGER :: iims, iime, jjms, jjme, kkms, kkme + INTEGER :: iids, iide, jjds, jjde, kkds, kkde + + + IF (& + (.NOT. PRESENT(ips)) .AND. & + (.NOT. PRESENT(jps)) .AND. & + (.NOT. PRESENT(kps)) .AND. & + (.NOT. PRESENT(ipe)) .AND. & + (.NOT. PRESENT(jpe)) .AND. & + (.NOT. PRESENT(kpe))) & + CALL wrf_debug ( 1, 'get_local_ijk function is NOT requesting a result' ) + + CALL get_ijk_from_grid ( grid , & + iids, iide, jjds, jjde, kkds, kkde, & + iims, iime, jjms, jjme, kkms, kkme, & + iips, iipe, jjps, jjpe, kkps, kkpe) + + IF (PRESENT(ips)) ips = iips + IF (PRESENT(jps)) jps = jjps + IF (PRESENT(kps)) kps = kkps + IF (PRESENT(ipe)) ipe = iipe + IF (PRESENT(jpe)) jpe = jjpe + IF (PRESENT(kpe)) kpe = kkpe + + END SUBROUTINE get_local_ijk + +!============================================================= +!============================================================= +#endif +END MODULE module_firebrand_spotting_mpi +