1 !-------------------------------------------------------------------------------
2 ! This module was developed in RAL-NCAR/UCAR, 2019-2020 by:
3 ! *** M. Frediani and T. Juliano ***
4 ! This research was supervised by J. Knievel and B. Kosovic.
5 !-------------------------------------------------------------------------------
7 !=============================================================
8 !=============================================================
10 MODULE module_firebrand_spotting
12 USE, INTRINSIC :: IEEE_ARITHMETIC
13 USE module_domain, ONLY : get_ijk_from_grid, domain ! grid
14 USE module_configure, ONLY : grid_config_rec_type ! config_flags
15 USE module_symbols_util, ONLY : WRFU_TimeInterval, WRFU_TimeIntervalGet, WRFU_TimeIntervalSet
16 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
23 PUBLIC firebrand_spotting_em_init, firebrand_spotting_em_driver, get_local_ijk
25 ! THESE VARIABLES ARE IN MODULE SCOPE ! Careful with reassignments - don't reassign
26 ! SAVE attribute is default
28 !-------------------------------------------------------------------------------
29 ! variables in module scope: private, only available module-wide (host association)
30 !-------------------------------------------------------------------------------
31 ! They should not be declared again in suboutines (may not compile)
32 ! and must not be among routine dummy arguments. Consequently, cannot be IO variables
34 ! Runtime variables are not available at module level (e.g., namelist, tile dependent variables).
35 ! Include here only what can be set during compilation:
36 ! fixed parameters, allocatables, declarions (without initialization)
38 !-------------------------------------------------------------------------------
39 ! Fixed parameters ***MODULE SCOPE***
40 !-------------------------------------------------------------------------------
42 INTEGER, PARAMETER :: dp = KIND(0.d0) ! double precision
44 INTEGER, PARAMETER :: wrfdbg = 10
45 ! Fuel parameters must match values from module_fr_phys.F: SUBROUTINE set_fuel_params(nfuel_cat0, nfuel_cat)
46 INTEGER, PARAMETER :: no_fuel_cat = 14
47 INTEGER, PARAMETER :: nf_sb = 204 ! maximum category on
48 INTEGER, PARAMETER :: nfuelcats = 53 ! number of fuel categories that are specified
50 REAL, PARAMETER :: grav = 9.80616_dp ! gravity (m/s2)
51 REAL, PARAMETER :: rdry = 287.04_dp ! dry air (J/Kg-K)
52 REAL, PARAMETER :: p2jm = 100.0_dp ! mb to j/m3
53 REAL, PARAMETER :: rcd = 0.45_dp ! drag constant
54 REAL, PARAMETER :: rd = 287.15_dp ! gas constant dry air (J / kg K)
55 REAL, PARAMETER :: rv = 461.6_dp
56 REAL, PARAMETER :: t0 = 300.0_dp
57 REAL, PARAMETER :: cp = 7.0_dp*rd/2.0_dp
58 REAL, PARAMETER :: rcp = rd/cp
59 REAL, PARAMETER :: p1000mb= 100000.0_dp
60 REAL, PARAMETER :: r1o3 = 1.0_dp/3.0_dp ! ratio 1 over 3
61 REAL, PARAMETER :: sboltz = 5.67E-5_dp ! stefan-boltzmann (g / s3-K4)
62 REAL, PARAMETER :: emiss = 0.9_dp ! emissivity
63 REAL, PARAMETER :: cpw = 1466.0_dp ! specific heat wood (J / kg-K)
64 REAL, PARAMETER :: cpc = 712.0_dp ! specific heat char (J / kg-K)
65 REAL, PARAMETER :: beta0 = 4.8E-7_dp ! burning rate constant (m2 / s)
66 REAL, PARAMETER :: s_coeff= 110.4_dp ! Sutherland's law coefficient (K)
67 REAL, PARAMETER :: b_coeff= 1.458E-3_dp ! Sutherland's law coefficient [g / m-s-K-(1/2)]
68 REAL, PARAMETER :: shmt = 0.7_dp ! schmidt number
69 REAL, PARAMETER :: thcon = 27.0_dp ! thermal conductivity air
71 !USE module_state_description, ONLY: p_qv
72 !INTEGER, PARAMETER :: p_qv = 1
73 REAL, PARAMETER :: pr = 0.7_dp ! Prandtl
75 REAL, PARAMETER :: NEGLIGIBLE = 10*EPSILON(1.0)
76 REAL, PARAMETER :: ZERO_dp = 0.0_dp ! this is a real type variable, not a double precision type
77 REAL, PARAMETER :: dp05 = 0.5_dp
78 REAL, PARAMETER :: dp1 = 1.0_dp
81 !-------------------------------------------------------------------------------
83 !-------------------------------------------------------------------------------
85 ! Mass threshold to consider burnout (g)
86 REAL, PARAMETER :: br_min_mass = EPSILON(dp1) ! max precision for real(4)
88 ! Diameter threshold to consider burnout (mm)
89 REAL, PARAMETER :: br_min_diam = 0.0000001_dp
91 !-------------------------------------------------------------------------------
92 ! Generic variables for multiple use within module ***MODULE SCOPE***
93 !-------------------------------------------------------------------------------
95 CHARACTER (LEN=200), SAVE :: msg
96 CHARACTER (LEN=256), SAVE :: fmt
97 CHARACTER (LEN=200), DIMENSION(10) :: amsg
98 INTEGER, SAVE :: imsg ! loop counters
100 !-------------------------------------------------------------------------------
101 ! variables from namelist ***MODULE SCOPE***
102 !-------------------------------------------------------------------------------
104 ! These will be available to all subroutines and will be set in init routine
105 INTEGER, SAVE :: fs_array_maxsize ! maximum number of particles carried in simulation
106 INTEGER, SAVE :: fs_gen_levels ! one per grid pt (may change if releasing at multiple levels)
107 INTEGER, SAVE :: fs_gen_lim
108 INTEGER, SAVE :: fs_gen_dt
110 REAL, SAVE :: firebrand_dens ! 513000.0_dp ! density of firebrand (g / m3)
111 REAL, SAVE :: firebrand_dens_char ! 299000.0_dp ! density of char (g m-3) [gupta et al 2002; fuel]
114 !-------------------------------------------------------------------------------
115 ! Fixed indices, ranks ***MODULE SCOPE***
116 !-------------------------------------------------------------------------------
118 LOGICAL :: this_is_ideal = .FALSE.
120 TYPE p_properties ! fb_prop
121 REAL :: p_mass ! fbmass
122 REAL :: p_diam ! fbdiam
123 REAL :: p_effd ! fbediam
124 REAL :: p_temp ! fbtemp
125 REAL :: p_tvel ! fbvelo
126 END TYPE p_properties
128 !-------------------------------------------------------------------------------
129 ! grid and cf are not here because dimensions are given at runtime (derived types)
130 ! Also, grid values change with timestep, so they need to be passed as dummy arguments
131 !-------------------------------------------------------------------------------
133 !-------------------------------------------------------------------------------
134 ! Variable bounds - Initialized in init, used in dummy arguments in driver
136 !-------------------------------------------------------------------------------
137 INTEGER, SAVE :: ids, jds, ide, jde, kde ! domain bounds
138 INTEGER, SAVE :: ims, jms, ime, jme, kms, kme ! memory bounds
139 INTEGER, SAVE :: is, ie, js, je, ks, ke ! patch start/end
140 INTEGER, SAVE :: ifps, jfps, ifpe, jfpe ! refined fire grid bounds
144 !******************************************************************
145 !******************************************************************
149 !******************************************************************
150 !******************************************************************
154 !=============================================================
155 SUBROUTINE firebrand_spotting_em_init ( &
156 !=============================================================
173 fs_count_landed_all,&
174 fs_count_landed_hist,&
179 !=============================================================
180 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
181 USE module_firebrand_spotting_mpi, ONLY: fs_mpi_init
183 !-------------------------------------------------------------------------------
184 ! Initialize all I/O and variables declared in the registry
185 !-------------------------------------------------------------------------------
189 !-------------------------------------------------------------------------------
191 !-------------------------------------------------------------------------------
193 TYPE(domain), INTENT(IN) :: grid !
194 TYPE(grid_config_rec_type), INTENT(IN) :: cf ! run-time configuration (namelist) for domain
196 INTEGER, INTENT(INOUT) :: fs_last_gen_dt, fs_gen_idmax
197 LOGICAL, INTENT(INOUT) :: fs_count_reset
199 ! Firebrand Spotting Particle Properties (fs_p_*)
200 INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id, fs_p_dt, fs_p_src
201 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)
202 REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_mass, fs_p_diam, fs_p_effd
203 REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_temp, fs_p_tvel
204 REAL, INTENT(INOUT), DIMENSION(ims:,jms:) :: fs_count_landed_all, fs_count_landed_hist, fs_spotting_lkhd, fs_frac_landed
205 INTEGER, INTENT(INOUT), DIMENSION(ims:,jms:) :: fs_landing_mask
207 !-------------------------------------------------------------------------------
209 !-------------------------------------------------------------------------------
210 ! Module variables from namelist/default
212 fs_array_maxsize = cf%fs_array_maxsize
213 fs_gen_levels = cf%fs_firebrand_gen_levels
214 fs_gen_lim = cf%fs_firebrand_gen_lim
215 fs_gen_dt = cf%fs_firebrand_gen_dt
218 WRITE (amsg(1),*) 'SPFire_init: fspotting_em_init'
219 WRITE (amsg(2),fmt) 'SPFire_init: firebrand limit =', fs_gen_lim
220 WRITE (amsg(3),fmt) 'SPFire_init: dt =', fs_gen_dt
221 WRITE (amsg(4),fmt) 'SPFire_init: fs_array_maxsize =', fs_array_maxsize
222 WRITE (amsg(5),fmt) 'SPFire_init: fs_gen_levels =', fs_gen_levels
224 CALL wrf_debug (wrfdbg, TRIM(amsg(imsg)) )
227 !-------------------------------------------------------------------------------
228 ! Set bounds to be used as dummy arguments in driver
229 ! ***variables declared in MODULE SCOPE***
230 !-------------------------------------------------------------------------------
232 CALL get_local_ijk(grid, &
233 ifps=ifps, jfps=jfps, ifpe=ifpe, jfpe=jfpe, &
234 ids=ids, jds=jds, ide=ide, jde=jde, kde=kde, &
235 ims=ims, jms=jms, ime=ime, jme=jme, kms=kms, kme=kme, &
236 ips=is, jps=js, ipe=ie, jpe=je, kps=ks, kpe=ke)
238 WRITE (msg,'(6(i6,1x))') is, ie, js, je, ks, ke
239 CALL wrf_debug (wrfdbg, 'SPFire_init tile bounds: '//msg)
241 WRITE (msg,'(6(i6,1x))') ims, ime, jms, jme, kms, kme
242 CALL wrf_debug (wrfdbg, 'SPFire_init memory bounds: '//msg)
244 WRITE (msg,'(4(i6,1x))') ifps, ifpe, jfps, jfpe
245 CALL wrf_debug (wrfdbg, 'SPFire_init fire refined bounds: '//msg)
248 !-------------------------------------------------------------------------------
249 ! Initialize registry arrays
250 !-------------------------------------------------------------------------------
251 fs_count_reset=.FALSE.
267 fs_landing_mask(:,:) = 0
268 fs_count_landed_all (:,:) = 0.0_dp
269 fs_count_landed_hist(:,:) = 0.0_dp
270 fs_spotting_lkhd(:,:) = 0.0_dp
271 fs_frac_landed(:,:) = 0.0_dp
273 !fs_fire_ROSdt(:,:) = 0.0_dp
274 !fs_gen_inst(:,:) = 0
276 !-------------------------------------------------------------------------------
278 IF ( grid%this_is_an_ideal_run ) THEN
280 this_is_ideal = .TRUE.
281 CALL wrf_debug (wrfdbg, 'SPFire_init: Ideal Run detected' )
285 this_is_ideal = .FALSE.
286 CALL wrf_debug (wrfdbg, 'SPFire_init: Not an Ideal Run' )
291 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
292 CALL fs_mpi_init(grid)
295 END SUBROUTINE firebrand_spotting_em_init
296 !=============================================================
297 !=============================================================
300 !=============================================================
302 FUNCTION order_val(arr, ord)
303 !=============================================================
307 REAL, INTENT(IN), DIMENSION(:) :: arr
308 INTEGER, INTENT(IN) :: ord
310 INTEGER :: diff, besti, iord, i, error
312 !-----------------------------------------------------------
313 ! Return the value in the array at the given order (allowing an error of +-2 positions)
315 ! For an arr with dimension = 100
316 ! ord = 50 will return one of the values near the 50% percentile
317 ! i.e., the value in one of the positions between 48:52 if the array was ranked
319 ! The algorithm scans the array and
320 ! counts the number of elements with value below the scanned position
321 ! It returns when it finds a value where
322 ! (order - error) <= count <= (order + error)
323 !-----------------------------------------------------------
332 iord = COUNT(arr > arr(i))
333 IF (ABS(ord - iord) <= diff) besti = i
334 diff = MIN(diff, ABS(ord - iord))
335 IF (diff <= error) EXIT
338 ! WRITE(msg, '(2(i6,1x))') iord, diff
339 ! CALL wrf_debug (wrfdbg, 'SPFire_rel_order_val: '//msg)
341 order_val = arr(besti)
343 END FUNCTION order_val
344 !=============================================================
345 !=============================================================
349 !=============================================================
351 FUNCTION fire2tile(fr_arr, dsx,dsy) RESULT(new_arr)
352 !=============================================================
356 REAL, INTENT(IN), DIMENSION(ifps:ifpe,jfps:jfpe) :: fr_arr
357 INTEGER, INTENT(IN) :: dsx, dsy
358 REAL, ALLOCATABLE,DIMENSION(:,:) :: new_arr
360 INTEGER, DIMENSION(2) :: fshp
362 !-----------------------------------------------------------
363 ! Converts a 2-D array from the fire refined grid to tile
364 ! Tile values are the sum of corresponding refined gridpoints
365 !-----------------------------------------------------------
369 ALLOCATE(new_arr(1:fshp(1)/dsx, 1:fshp(2)/dsy))
370 new_arr(:,:) = ZERO_dp
372 new_arr(1:fshp(1)/dsx, 1:fshp(2)/dsy) = &
374 SUM(fr_arr(i:i+dsx-1,j:j+dsy-1)), &
375 i=ifps,ifpe-1,dsx), &
376 j=jfps,jfpe-1,dsy)], &
377 [fshp(1)/dsx,fshp(2)/dsy])
379 END FUNCTION fire2tile
380 !=============================================================
381 !=============================================================
385 !=============================================================
386 ELEMENTAL FUNCTION fuel_spotting_risk(fuel_fgi, factor, fuel_mcg)!, fuel_mcg)
387 !=============================================================
389 ! Returns the refined grid array with the fuel_spotting_risk for spotfires
390 !-------------------------------------------------------------------------------
392 ! fuel_name(k) : fuel model name
393 ! windrf(k) : wind reduction factor from 20ft to midflame height
394 ! fgi(k) : initial total mass of surface fuel (kg/m**2)
395 ! fgi_lh(k) : initial total mass of surface live fuel [sb: 1-h] (kg/m**2)
396 ! fgi_all(k) : initial total mass of surface live fuel [sb: 1-h,10-h,100-h] (kg/m**2)
397 ! fueldepthm(k) : fuel depth (m)
398 ! savr(k) : fuel particle surface-area-to-volume ratio, 1/ft
399 ! fuelmce(k) : moisture content of extinction
400 ! fueldens(k) : ovendry particle density, lb/ft^3
401 ! st(k) : fuel particle total mineral content
402 ! se(k) : fuel particle effective mineral content
403 ! weight(k) : weighting parameter that determines the slope of the mass loss curve
404 ! fci_d(k) : initial dry mass of canopy fuel
405 ! fct(k) : burn out time for canopy fuel, after dry (s)
406 ! ichap(k) : 1 if chaparral, 0 if not
407 ! fci(k) : initial total mass of canopy fuel
408 ! fcbr(k) : fuel canopy burn rate (kg/m**2/s)
409 ! hfgl : surface fire heat flux threshold to ignite canopy (w/m^2)
410 ! cmbcnst : joules per kg of dry fuel
411 ! fuelheat : fuel particle low heat content, btu/lb
412 ! fuelmc_g : fuel particle (surface) moisture content
413 ! fuelmc_c : fuel particle (canopy) moisture content
417 REAL :: fuel_spotting_risk
418 REAL, INTENT(IN) :: factor
419 REAL, INTENT(IN) :: fuel_fgi, fuel_mcg ! fmcg2df ground dead fuel moisture content (fire grid)
421 fuel_spotting_risk = factor * (dp1 - MIN(dp1, fuel_mcg/fuel_fgi)) ! *(fuel_fgi-fuel_mcg)
423 END FUNCTION fuel_spotting_risk
424 !=============================================================
425 !=============================================================
429 !=============================================================
430 ELEMENTAL FUNCTION spotting_threat_factor(fcat)
431 !=============================================================
434 INTEGER, INTENT(IN) :: fcat
435 REAL, DIMENSION(nfuelcats+1) :: factor
436 REAL :: spotting_threat_factor
438 ! **************** read this from external input ****************
439 ! **************** call it in firebrand_spotting_driver once, when grid%itimestep =1 ****************
440 ! **************** and load a registry array ****************
442 ! User defined coefficient to increase spotfire likelihood;
443 ! threat = 0 yields zero likelihood
445 factor(1) = 1.0_dp ! 1: 'Short grass (1 ft)',
446 factor(2:13) = 1.0_dp
447 factor(14) = ZERO_dp ! 14: 'no fuel',
448 factor(15:nfuelcats+1) = 1.0_dp
450 spotting_threat_factor = factor(fcat)
452 !CALL read_table_static
454 END FUNCTION spotting_threat_factor
456 !=============================================================
457 !=============================================================
460 ! !=============================================================
461 ! SUBROUTINE read_table_static ! Not debuggeed - from module_mp_thompson
462 ! !=============================================================
468 ! LOGICAL, EXTERNAL:: wrf_dm_on_monitor
469 ! INTEGER:: iunit_fs1, i
471 ! CHARACTER (LEN=64) :: filename
474 ! IF ( wrf_dm_on_monitor() ) THEN
476 ! INQUIRE ( i , OPENED = opened )
477 ! IF ( .NOT. opened ) THEN
484 ! #if defined(DM_PARALLEL) && !defined(STUBMPI)
485 ! CALL wrf_dm_bcast_bytes ( iunit_fs1 , IWORDSIZE )
487 ! IF ( iunit_fs1 < 0 ) &
488 ! CALL wrf_error_fatal('SPFire_read_table_static: Cannot find a fortran unit to read file')
490 ! IF ( wrf_dm_on_monitor() ) THEN
492 ! WRITE(msg, '(i2,1x,a20)') iunit_fs1, filename
493 ! CALL wrf_debug(wrfdbg,'SPFire_read_table_static: opening table on unit ' // msg)
495 ! OPEN(iunit_fs1,FILE=tablename, FORM='UNFORMATTED',STATUS='OLD',ERR=9009)
499 ! #define DM_BCAST_MACRO(A) CALL wrf_dm_bcast_bytes(A, size(A)*R4SIZE)
501 ! IF ( wrf_dm_on_monitor() ) READ(iunit_fs1,ERR=9010) tableval
502 ! #if defined(DM_PARALLEL) && !defined(STUBMPI)
503 ! DM_BCAST_MACRO(tableval)
509 ! CALL wrf_error_fatal('SPFire_read_table_static: error openinig static table')
513 ! CALL wrf_error_fatal('SPFire_read_table_static: error reading static table')
516 ! END SUBROUTINE read_table_static
518 ! !=============================================================
519 ! !=============================================================
522 !=============================================================
523 ELEMENTAL FUNCTION get_fuel_cat(crosswalk, cat)
524 !=============================================================
526 ! Use with Scott & Burgen fuel model (WRF CO-FSP)
530 INTEGER :: get_fuel_cat
531 LOGICAL, INTENT(IN):: crosswalk
532 REAL, INTENT(IN):: cat
534 INTEGER, DIMENSION(1:nf_sb) :: ksb ! Anderson82 + S&B2005 fuel categories array
536 !-------------------------------------------------------------------------------
540 ksb = no_fuel_cat ! 14 ! Initialize all values to no-fuel
543 ksb(1:13) = [(i, i=1, 13)]
545 IF ( crosswalk ) THEN ! Scott & Burgan crosswalks to Anderson
547 ksb([101, 104, 107])=1 ! Short grass -- 1
548 ksb(102)=2 ! Timber grass and understory -- 2
549 ksb(121:124) = 2 ! Timber grass and understory -- 2
550 ksb([103,105,106,108,109])=3 ! Tall grass -- 3
551 ksb([145, 147])=4 ! Chaparral -- 4
552 ksb(142)=5 ! Brush -- 5
553 ksb([141, 146])=6 ! Dormant Brushi -- 6
554 ksb([143, 144, 148, 149])=7 ! Southern Rough -- 7
555 ksb([181, 183, 184, 187])=8 ! Compact Timber Litter -- 8
556 ksb([182, 186, 188, 189])=9 ! Hardwood Litter -- 9
557 ksb(161: 165)=10 ! Timber (understory) -- 10
558 ksb([185, 201])=11 ! Light Logging Slash -- 11
559 ksb(202)=12 ! Medium Logging Slash -- 12
560 ksb([203, 204])=13 ! Heavy Logging Slash -- 13
562 ELSE ! full Scott and Burgan (2005)
564 ksb(101:109) = [(i, i=15,23)] ! Grass (GR)
565 ksb(121:124) = [(i, i=24,27)] ! Grass-Shrub (GS)
566 ksb(141:149) = [(i, i=28,36)] ! Shrub (SH)
567 ksb(161:165) = [(i, i=37,41)] ! Timber-Understory (TU)
568 ksb(181:189) = [(i, i=42,50)] ! Timber litter (TL)
569 ksb(201:204) = [(i, i=51,54)] ! Slash-Blowdown (SB)
573 get_fuel_cat = ksb(icat)
575 END FUNCTION get_fuel_cat
576 !=============================================================
577 !=============================================================
580 !=============================================================
581 ELEMENTAL FUNCTION firebrand_gen_factor(fcat)
582 !=============================================================
585 REAL :: firebrand_gen_factor
586 INTEGER, INTENT(IN) :: fcat
587 REAL, DIMENSION(nfuelcats+1) :: factor
590 ! **************** read this from external input ****************
591 ! **************** call it in firebrand_spotting_driver once, when grid%itimestep =1 ****************
592 ! **************** and load a registry array ****************
594 ! User defined coefficient to set release priority
595 ! factor = 0 yields zero releases from gridpoint
597 factor(1:nfuelcats+1)= 1.0_dp
598 firebrand_gen_factor = factor(fcat)
600 END FUNCTION firebrand_gen_factor
602 !=============================================================
603 !=============================================================
607 ! !=============================================================
608 ! ELEMENTAL FUNCTION firebrand_gen_maxhgt(fcat)
609 ! !=============================================================
612 ! REAL :: firebrand_gen_maxhgt
613 ! INTEGER, INTENT(IN) :: fcat
614 ! REAL, DIMENSION(nfuelcats+1) :: maxhgt
617 ! ! **************** read this from external input ****************
618 ! ! **************** call it in firebrand_spotting_driver once, when grid%itimestep =1 ****************
619 ! ! **************** and load a registry array ****************
621 ! ! User defined max height to release firebrand
623 ! maxhgt(1:nfuelcats+1)= 50.0_dp
624 ! firebrand_gen_maxhgt = maxhgt(fcat)
626 ! END FUNCTION firebrand_gen_maxhgt
628 !=============================================================
629 !=============================================================
633 !=============================================================
634 ELEMENTAL FUNCTION firebrand_gen_potential(fuel_fgi, factor, fire_rate, fuel_mcg)!, fuel_mcg)
635 !=============================================================
637 ! Returns the refined grid array with the potential for firebrand generation
638 !-------------------------------------------------------------------------------
642 REAL :: firebrand_gen_potential
643 REAL, INTENT(IN) :: factor, fire_rate
644 REAL, INTENT(IN) :: fuel_fgi, fuel_mcg ! fmcg2df ground dead fuel moisture content (fire grid)
646 firebrand_gen_potential = factor * fire_rate * fuel_fgi !*(dp1 - fuel_mcg/(dp1+fuel_mcg)) ! fuelloadm; dry load from fr_fire_phys
648 END FUNCTION firebrand_gen_potential
649 !=============================================================
650 !=============================================================
654 !=============================================================
655 ! PURE & ! can't call wrf_fatal in pure routines
656 SUBROUTINE generate_firebrands( fs_p_id, fs_p_src, fs_p_dt, fs_p_z, fs_p_x, fs_p_y, &
657 release_i, release_j, release_k, &
658 release_dt, release_src, &
659 active_br, fs_gen_idmax, myprocid, &
660 release_prop, fs_p_prop)
661 !=============================================================
665 TYPE(p_properties), INTENT(IN), DIMENSION(:):: release_prop
666 TYPE(p_properties), INTENT(OUT),DIMENSION(:):: fs_p_prop
668 INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id, fs_p_src, fs_p_dt
669 REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_z, fs_p_x, fs_p_y
670 REAL, INTENT(INOUT), DIMENSION(:) :: release_i, release_j, release_k
671 INTEGER, OPTIONAL, INTENT(IN), DIMENSION(:) :: release_dt, release_src
672 INTEGER, INTENT(INOUT) :: fs_gen_idmax
673 INTEGER, INTENT(IN) :: myprocid
674 INTEGER, INTENT(OUT) :: active_br
676 LOGICAL :: release_true
679 INTEGER :: new_release
680 LOGICAL, DIMENSION(fs_array_maxsize) :: flag_true
682 !-------------------------------------------------------------------------------
683 release_true = .FALSE. ! scalar: true when brands are ready to be released
684 flag_true = .FALSE. ! logical array: flag to find active elements in array
686 active_br = 0 ! number of active brands
687 new_release = 0 ! number of brands to release
689 ! when all brand IDs of array elements are zero: nothing active
690 active_br = COUNT( fs_p_id > 0 )
691 new_release = COUNT( (INT(release_i) > 0) .AND. (INT(release_j) > 0) )
693 IF (new_release > 0 ) release_true = .TRUE. ! to-do: include dependency on release_dt
695 IF ( .NOT. release_true) THEN
696 RETURN !CALL wrf_debug (wrfdbg, 'SPFire_generate_firebrands: Found nothing to release')
699 !-------------------------------------------------------------------------------
700 ! New brands fit in array? Release particles: set lat/lon/lev of new brands
701 !-------------------------------------------------------------------------------
703 IF (active_br + new_release <= fs_array_maxsize) THEN
705 !-------------------------------------------------------------------------------
706 ! Add new grid positions to array: create a subroutine for this: brand_release
707 !-------------------------------------------------------------------------------
709 ! Flag is True where positions are available to be filled
710 WHERE(fs_p_id == 0) flag_true = .TRUE. ! id_indx holds brand ID (unique integer)
715 ! Find an ii position available to store a new release
716 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)
717 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?')
721 IF (INT(release_i(br)) == 0 .OR. &
722 INT(release_j(br)) == 0 ) &
723 CALL wrf_error_fatal('SPFire_generate_firebrands: release_ijk is zero! Positions cannot be zero.')
725 IF (fs_gen_idmax + 10 >= HUGE(1)) fs_gen_idmax = 0
727 ! Assign values and convert height to z index
728 fs_p_x(ii) = release_i(br)
729 fs_p_y(ii) = release_j(br)
730 fs_p_z(ii) = release_k(br)
731 fs_p_id(ii) = fs_gen_idmax + 1 ! Assign new IDmax to particle
733 IF (.NOT. PRESENT(release_dt)) fs_p_dt(ii) = 0 ! firebrand has not been advected yet so dt is zero
734 IF (.NOT. PRESENT(release_src)) fs_p_src(ii) = myprocid + fs_p_id(ii)
736 IF (PRESENT(release_dt)) fs_p_dt(ii) = release_dt(br)
737 IF (PRESENT(release_src)) fs_p_src(ii) = release_src(br)
739 fs_p_prop(ii)%p_mass = release_prop(br)%p_mass
740 fs_p_prop(ii)%p_diam = release_prop(br)%p_diam
741 fs_p_prop(ii)%p_effd = release_prop(br)%p_effd
742 fs_p_prop(ii)%p_temp = release_prop(br)%p_temp
743 fs_p_prop(ii)%p_tvel = release_prop(br)%p_tvel
745 fs_gen_idmax = fs_p_id(ii)
747 flag_true(ii) = .FALSE.
752 active_br = active_br + new_release
757 ! WRITE (msg,'(2(i6,1x))') new_release, fs_gen_idmax
758 ! CALL wrf_debug (wrfdbg, 'SPFire_release: Released new brands fs_gen_idmax: '//msg)
762 ! WRITE (msg,*) active_br + new_release, fs_array_maxsize
763 ! CALL wrf_debug (wrfdbg, 'SPFire_release: brand array is full, cannot release new brands'// msg)
768 END SUBROUTINE generate_firebrands
770 !=============================================================
771 !=============================================================
776 !=============================================================
778 FUNCTION hgt2k(xp, yp, hgt, z_at_w, znw)
779 !=============================================================
781 !-------------------------------------------------------------------------------
782 ! Converts height in meters to vertical level
783 !-------------------------------------------------------------------------------
786 !INTEGER, INTENT(IN) :: ims, jms
787 REAL, INTENT(IN) :: hgt, xp, yp
788 REAL, INTENT(IN), DIMENSION(ims:,:,jms:) :: z_at_w
789 REAL, INTENT(IN), DIMENSION(:) :: znw
791 REAL :: zdz, z_lower_at_p, z_upper_at_p, x0,y0
795 ! An (xp,yp)=(1.5, 1.5) places the particle at the gridpoint center
796 ! but z_at_w is horizontally unstaggered,
797 ! so z_at_w(i,j) is at the horizontal grid center when (i,j)=(1,1)
799 ! Hence, we adjust the particle position to fetch the horizontally adjacent heights for the linear interp.
800 ! We subtract the deltax between
801 ! position at grid center and position at grid edge from the particle position xp, yp, such that
802 ! z_at_w(i,1,j) for (i,j)=(1,1) corresponds to the height at (xp,yp)=(1.5,1.5):
803 ! (i = xp - 0.5, j = yp - 0.5) = (i=1, j=1), for (xp=1.5, yp=1.5)
808 i = FLOOR(x0) ! shift indices by 0.5 because z_at_w is given at horizontal grid center
811 ! Minloc where hgt - z_k is positive: level right below brand release
812 k = MINLOC(hgt - z_at_w(i,:,j), dim=1, &
813 mask=( hgt - z_at_w(i,:,j) >= 0.0_dp ))
815 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),&
816 u_i1j0=z_at_w(i+1,k,j), u_i1j1=z_at_w(i+1,k,j+1))
818 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),&
819 u_i1j0=z_at_w(i+1,k+1,j), u_i1j1=z_at_w(i+1,k+1,j+1))
821 zdz = (hgt - z_lower_at_p) / (z_upper_at_p-z_lower_at_p)
825 !=============================================================
826 !=============================================================
831 !=============================================================
833 SUBROUTINE releaseijk2atm (nij_2d, sr_x, sr_y, fcat, maxhgt_usr, pi, pj, pk, nij)
834 !=============================================================
838 INTEGER, INTENT(IN), DIMENSION(:,:) :: nij_2d ! do i need ifps here? No
839 INTEGER, INTENT(IN), DIMENSION(:,:) :: fcat ! do i need ifps here? No
840 INTEGER, INTENT(IN) :: sr_x, sr_y
841 REAL, INTENT(IN) :: maxhgt_usr
843 REAL, INTENT(INOUT), DIMENSION(:) :: pj, pi, pk
844 INTEGER, INTENT(INOUT), DIMENSION(:) :: nij
846 INTEGER, ALLOCATABLE, DIMENSION(:) :: fpi,fpj
849 !-------------------------------------------------------------------------------
850 ! Returns a 1-D array with the release positions (i, j, k[m])
851 ! and number of particles to release (nij)
852 ! converted from fire refine grid to wrf grid
853 ! (i,j k are type float and represent the original refined grid position)
854 !-------------------------------------------------------------------------------
856 cnt = COUNT(nij_2d > 0)
863 ALLOCATE(fpi(cnt), fpj(cnt))
868 ! fpi = RESHAPE([( [(i, i=ifps, ifpe)], j=jfps, jfpe)], SHAPE(fr_arr))
869 ! fpj = RESHAPE([( [(j, i=ifps, ifpe)], j=jfps, jfpe)], SHAPE(fr_arr))
871 fpi = PACK(RESHAPE([( [(i, i=ifps, ifpe)], j=jfps, jfpe)], SHAPE(nij_2d)), mask=(nij_2d > 0))
872 fpj = PACK(RESHAPE([( [(j, i=ifps, ifpe)], j=jfps, jfpe)], SHAPE(nij_2d)), mask=(nij_2d > 0))
873 nij = PACK(nij_2d, mask=(nij_2d > 0))
875 !-------------------------------------------------------------------------------
876 ! convert from refined fire grid to atm grid
877 !-------------------------------------------------------------------------------
878 pi = fire2atm_ij(fpi, sr_x )
879 pj = fire2atm_ij(fpj, sr_y )
881 ! Release height and firebrand properties can depend on fuel category (in future developments)
882 ! Because fcat is on the refined grid, hgt and fprop must be specified here
884 ! pk = firebrand_gen_maxhgt(PACK(fcat, mask=(nij_2d > 0))) ! Use this to specify hgts for fuel types
888 END SUBROUTINE releaseijk2atm
889 !=============================================================
890 !=============================================================
894 !=============================================================
895 ELEMENTAL FUNCTION fire2atm_ij (fp_ij, sr)
896 !=============================================================
897 ! Convert refined subgrid to default atm grid
898 ! Use function separately for i and j:
899 ! pi = fire2atm_ij(fpi, grid%sr_x )
900 ! pj = fire2atm_ij(fpj, grid%sr_y )
905 INTEGER, INTENT(IN) :: fp_ij
906 INTEGER, INTENT(IN) :: sr
908 fire2atm_ij = ( dp1+ REAL(fp_ij - dp1)/REAL(sr) )
910 ! fpij = 298, sr = 3: 297/3+1 = 100
911 ! fpij = 299, sr = 3: 298/3+1 = 100.333
912 ! fpij = 300, sr = 3: 299/3+1 = 100.666
913 ! fpij = 301, sr = 3: 300/3+1 = 101
915 ! fpij = 297, sr = 4: 296/4+1 = 75.00
916 ! fpij = 298, sr = 4: 297/4+1 = 75.25
917 ! fpij = 299, sr = 4: 298/4+1 = 75.50
918 ! fpij = 300, sr = 4: 299/4+1 = 75.75
919 ! fpij = 301, sr = 4: 299/4+1 = 76.00
921 END FUNCTION fire2atm_ij
922 !=============================================================
923 !=============================================================
927 !=============================================================
928 ELEMENTAL FUNCTION atm2fire_ij (pij, sr)
929 !=============================================================
930 ! Convert default atm grid to lower left index of refined grid
931 ! (atm_i, atm_j) = (f_i : f_i + srx , f_j : f_j + sry)
935 INTEGER :: atm2fire_ij
936 INTEGER, INTENT(IN) :: pij
937 INTEGER, INTENT(IN) :: sr
939 atm2fire_ij = (pij - 1) * sr + 1
941 ! pij = 100, sr = 3: 99*3+1 = 298
942 ! pij = 101, sr = 3: 100*3+1 = 301
944 END FUNCTION atm2fire_ij
945 !=============================================================
946 !=============================================================
950 ! !=============================================================
951 ! SUBROUTINE subgrid_fire_property(fire_ij, fuel_frac, grid)
952 ! !=============================================================
954 ! ! Not used - may be delted from final code version
958 ! TYPE(domain), INTENT(IN) :: grid ! input data **Note: Intent IN**
959 ! REAL, INTENT(OUT), DIMENSION(:) :: fuel_frac
960 ! INTEGER, INTENT(IN), DIMENSION(:,:) :: fire_ij
962 ! INTEGER, ALLOCATABLE, DIMENSION(:) :: fpi, fpj
963 ! INTEGER :: k, cnt, sri, srj, srx, sry
968 ! cnt = SIZE(fire_ij(:,1))
969 ! fpi = atm2fire_ij( fire_ij(:,1), srx)
970 ! fpj = atm2fire_ij( fire_ij(:,2), sry)
971 ! !ALLOCATE( flame(cnt), fuel(cnt))
973 ! ! Get flame and fuel_frac maxvals from subgrids
974 ! DO k=1,SIZE(fire_ij(:,1))
979 ! fuel_frac(k) = MAXVAL(grid%fuel_frac( sri: sri+srx-1, srj : srj+sry-1))
981 ! !WRITE (msg,*) MAXVAL(grid%zsf(sri:sri+dsx,srj:srj+dsy)) always zero??
985 ! END SUBROUTINE subgrid_fire_property
986 ! !=============================================================
987 ! !=============================================================
991 ! !=============================================================
992 ! PURE FUNCTION uniq_ij (fpi, fpj)
993 ! !=============================================================
994 ! ! remove duplicated grid points (after converting from refined fire grid to grid)
996 ! ! Not used but quite handy - do not delete it.
1000 ! INTEGER, INTENT(IN), DIMENSION(:) :: fpi, fpj
1001 ! INTEGER, ALLOCATABLE, DIMENSION(:,:) :: uniq_ij
1002 ! LOGICAL, ALLOCATABLE, DIMENSION(:) :: mask
1007 ! ALLOCATE(mask(cnt))
1011 ! mask(i) = .NOT.( ANY(fpi(:i-1) == fpi(i) .AND. &
1012 ! fpj(:i-1) == fpj(i)))
1015 ! ALLOCATE( uniq_ij(COUNT(mask),2) )
1016 ! uniq_ij(:,1) = REAL(PACK(fpi, mask))
1017 ! uniq_ij(:,2) = REAL(PACK(fpj, mask))
1019 ! END FUNCTION uniq_ij
1021 ! !=============================================================
1022 ! !=============================================================
1026 !=============================================================
1027 SUBROUTINE prep_release_hgt(release_i, release_j, release_k, release_n, release_prop, levrand_seed, landhgt)
1028 !=============================================================
1030 !----------------------------------------------------------
1031 ! Set release locations for new particles
1032 ! and distribute particles over fs_gen_levels heights
1033 !----------------------------------------------------------
1037 TYPE(p_properties), INTENT(INOUT), DIMENSION(:) :: release_prop
1038 REAL, INTENT(INOUT), DIMENSION(:) :: release_i, release_j
1039 REAL, INTENT(INOUT), DIMENSION(:) :: release_k
1040 INTEGER, INTENT(IN), DIMENSION(:) :: release_n
1041 INTEGER, INTENT(IN) :: levrand_seed
1042 REAL, INTENT(IN) :: landhgt
1044 INTEGER :: ii, kk, cnt ! counters
1047 REAL, ALLOCATABLE, DIMENSION(:) :: rand_arr, frachgt
1048 INTEGER, ALLOCATABLE, DIMENSION(:) :: seeds
1050 minhgt = dp1 + landhgt
1052 !-------------------------------------------------------------------------------
1053 ! Calculate number of releases and heights from this fire
1054 !-------------------------------------------------------------------------------
1056 ALLOCATE(frachgt(fs_gen_levels))
1057 ALLOCATE(rand_arr(SIZE(release_n)*fs_gen_levels))
1059 IF (fs_gen_levels == 1) THEN
1062 frachgt(:) = [( REAL(kk-1)*(dp1/REAL(fs_gen_levels-1)), kk=fs_gen_levels, 1, -1)]
1065 IF (levrand_seed > 0) THEN
1067 nseeds = SIZE(rand_arr)
1068 CALL random_seed(size=nseeds)
1069 ALLOCATE(seeds(nseeds))
1070 seeds = [(( release_i(ii) * release_j(ii) * levrand_seed * kk, &
1071 kk=1, fs_gen_levels), &
1072 ii=1, SIZE(release_n))] ! force seed to vary across space
1073 CALL random_seed(put = seeds)
1075 CALL random_number(rand_arr)
1080 ii = SIZE(release_n)+1 ! array index for release at various height (append to end of the array)
1082 DO kk=2, fs_gen_levels ! release at the remaining levels below maxhgt (k=1 is already filled with maxhgt)
1083 DO cnt=1,SIZE(release_n) ! loop over release gridpoints and set a new release at the same ij for 1 < hgt < release_k
1085 !-------------------------------------------------------------------------------
1086 ! Fill release array
1087 !-------------------------------------------------------------------------------
1089 release_i(ii) = release_i(cnt) ! ii increments inside this loop
1090 release_j(ii) = release_j(cnt)
1091 IF (release_k(cnt) >= minhgt) THEN
1092 release_k(ii) = ((release_k(cnt)-minhgt) * frachgt(kk)) + minhgt ! keep a minimum height of 1-m above landhgt
1093 ! if we want to perturb the max_release_hgt in releases_k(cnt), we can change its value now
1094 IF (levrand_seed > 0) &
1095 release_k(ii) = ((release_k(cnt)-minhgt) * rand_arr(ii)) + minhgt
1097 release_k(ii) = ((release_k(cnt)-dp1) * frachgt(kk)) + dp1 ! keep a minimum height of 1-m above landhgt
1098 IF (levrand_seed > 0) &
1099 release_k(ii) = ((release_k(cnt)-dp1) * rand_arr(ii)) + dp1
1103 release_prop(ii) = release_prop(cnt)
1106 ! WRITE (msg,'(2(i4,1x),i8,1x,6(f8.3,1x))') cnt, kk, ii, &
1107 ! release_i(ii), release_j(ii), release_k(cnt), release_k(ii), frachgt(kk), rand_arr(ii)
1108 ! CALL wrf_debug (0, 'SPFire_prep_release c,k,i, ijk: '//msg)
1111 ii = ii + 1 ! increment release array index
1116 ! Add another loop here to perturb release_k from ii=,SIZE(release_n) -> currently set to maxhgt
1118 END SUBROUTINE prep_release_hgt
1119 !=============================================================
1120 !=============================================================
1124 !=============================================================
1125 FUNCTION firebrand_property(arr) RESULT(prop)
1126 !=============================================================
1130 TYPE (p_properties) :: prop
1131 REAL, INTENT(IN), DIMENSION(:) :: arr
1132 REAL, PARAMETER :: pi = 4.0_dp * ATAN (1.0_dp)
1134 prop%p_diam = arr(1) ! 10.0_dp ! mm
1135 prop%p_effd = arr(2) ! 10.0_dp ! mm
1136 prop%p_temp = arr(3) ! 900.0_dp ! K
1137 ! termvel should start with 0m/s because firebrand is released at top of trajectory
1138 prop%p_tvel = arr(4) ! 0.0_dp ! m/s
1139 prop%p_mass = (firebrand_dens * pi * (prop%p_effd/1000.0_dp)**2)/6.0_dp ! g
1141 END FUNCTION firebrand_property
1143 !=============================================================
1144 !=============================================================
1148 !=============================================================
1149 FUNCTION idx_packed_1d(mask) RESULT(mask_idx)
1150 !=============================================================
1152 ! Return an array of indices where mask is true
1154 LOGICAL, INTENT(IN), DIMENSION(:) :: mask
1155 !INTEGER, INTENT(IN) :: active_br
1156 INTEGER, ALLOCATABLE, DIMENSION(:) :: mask_idx
1157 INTEGER :: nresets, ii
1159 nresets = COUNT(mask)
1160 ALLOCATE(mask_idx(nresets))
1161 mask_idx = PACK([(ii, ii=1,SIZE(mask))], mask) ! Get flag indices
1163 END FUNCTION idx_packed_1d
1164 !=============================================================
1165 !=============================================================
1170 !=============================================================
1172 SUBROUTINE firebrand_physics(dt, hgt, loc_p, loc_t, loc_d, loc_w, fbprop)
1173 !=============================================================
1177 TYPE(p_properties), INTENT(INOUT) :: fbprop
1178 REAL, INTENT(IN) :: loc_p, loc_t, loc_d, loc_w ! local pressure, temp, density, w
1179 REAL, INTENT(IN) :: dt
1180 REAL, INTENT(INOUT) :: hgt
1181 !REAL, INTENT(INOUT) :: p_mass, p_diam, p_effd, p_temp, p_tvel
1183 !-------------------------------------------------------------------------------
1185 !-------------------------------------------------------------------------------
1187 ! WRITE (msg,'(6(f12.8,1x))') fbprop%p_mass, fbprop%p_diam, &
1188 ! fbprop%p_effd, fbprop%p_temp, fbprop%p_tvel, hgt
1189 ! CALL wrf_debug (wrfdbg, 'SPFire br physics1 m,d,e,t,v,h : '//msg)
1191 CALL burnout(p_mass = fbprop%p_mass, & ! firebrand mass (g)
1192 p_diam = fbprop%p_diam, & ! firebrand diameter (mm)
1193 p_effd = fbprop%p_effd, & ! firebrand effective diameter (mm)
1194 p_temp = fbprop%p_temp, & ! firebrand temperature (K)
1195 p_tvel = fbprop%p_tvel, & ! firebrand terminal velocity (m/s)
1196 aird = loc_d, & ! g/m3
1197 pres = loc_p, & ! Pa
1199 loc_w = loc_w, & ! m/s
1202 ! WRITE (msg,'(4(f12.8,1x))') fbprop%p_mass, fbprop%p_diam, &
1203 ! fbprop%p_effd, fbprop%p_temp
1204 ! CALL wrf_debug (wrfdbg, 'SPFire br physics2 m, d, e, t: '//msg)
1206 !-------------------------------------------------------------------------------
1207 ! firebrand terminal velocity
1208 !-------------------------------------------------------------------------------
1210 CALL termvel(p_diam = fbprop%p_diam, &
1211 p_effd = fbprop%p_effd,&
1212 p_temp = fbprop%p_temp, &
1213 p_tvel = fbprop%p_tvel, &
1214 aird = loc_d, & ! g/m3
1215 pres = loc_p, & ! Pa
1220 ! WRITE (msg,'(6(f12.8,1x))') fbprop%p_mass, fbprop%p_diam, &
1221 ! fbprop%p_effd, fbprop%p_temp, fbprop%p_tvel, hgt
1222 ! CALL wrf_debug (wrfdbg, 'SPFire br physics2 m,d,e,t,v,h : '//msg)
1226 ! Should we calculate in double precision when diam is in meters?
1227 ! double: real(8), 15 precision digits
1228 ! single: real(4), 7 precision digits
1230 END SUBROUTINE firebrand_physics
1232 !=============================================================
1233 !=============================================================
1237 !=============================================================
1238 ELEMENTAL SUBROUTINE burnout(pres, aird, temp, loc_w, dt, p_mass, p_diam, p_effd, p_temp, p_tvel)
1239 !=============================================================
1241 !-------------------------------------------------------------------------------
1242 ! This subroutine was developed in RAL-NCAR/UCAR, 2019-2020 by:
1243 ! *** T. W. Juliano ***
1245 ! Tse & Fernandez-Pello 1998 (DOI: 10.1016/ S0379-7112(97)00050-7)
1246 ! Bhutia, Jenkins & Sun 2010 (DOI:10.3894/JAMES.2010.2.4)
1247 !-------------------------------------------------------------------------------
1251 REAL, INTENT(IN) :: pres, aird, temp, loc_w, dt
1252 REAL, INTENT(INOUT) :: p_mass ! firebrand mass (g)
1253 REAL, INTENT(INOUT) :: p_diam ! firebrand diameter (mm)
1254 REAL, INTENT(INOUT) :: p_effd ! firebrand effective diameter (mm)
1255 REAL, INTENT(INOUT) :: p_temp ! firebrand temperature (K)
1256 REAL, INTENT(IN) :: p_tvel ! new firebrand terminal velocity (m/s)
1258 !-------------------------------------------------------------------------------
1259 ! Constants are at module top
1260 !-------------------------------------------------------------------------------
1262 REAL, PARAMETER :: pi = 4.0_dp * ATAN (1.0_dp)
1264 REAL :: aird2, wind, gama
1266 REAL :: p_effd2, pdia_new4, reyn, beta, deff, dmvc, dmvc2
1267 REAL :: parta, partb, dtemp, nuss, hbar, fbvol, qcon, qrad, partc
1268 REAL :: pratio, cpmix
1270 p_mass = MAX(p_mass, EPSILON(dp1))
1271 !-------------------------------------------------------------------------------
1273 ! air density surrounding firebrand
1274 aird2 = 1000.0_dp * pres / ( rd * p_temp )
1276 ! Assume vel diff between firebrand and wind is due only to
1277 ! vertical; that is, assume particle moves with horiz wind
1278 wind = ABS(loc_w - p_tvel)
1280 ! particle diameter (mm) to (m)
1281 pdia = p_diam / 1000.0_dp
1283 ! particle effective diameter (mm) to (m)
1284 pedia = p_effd / 1000.0_dp
1286 ! local atmospheric dynamic viscosity according to Sutherland's law
1287 dmvc = (b_coeff * temp**1.5_dp) / (temp + s_coeff)
1289 ! dynamic viscosity surrounding firebrand according to Sutherland's law
1290 dmvc2 = (b_coeff * p_temp**1.5_dp) / (p_temp + s_coeff)
1292 ! kinematic viscosity average for atmosphere and firebrand
1293 gama = ((dmvc / aird) + (dmvc2 / aird2))/2.0_dp
1296 reyn = wind * pdia / gama
1299 nuss = 2.0_dp + 0.6_dp * reyn**(dp05) * pr**(r1o3)
1301 ! convection heat transfer coefficient
1302 hbar = nuss * thcon / pdia
1305 beta = beta0 + beta0 * (0.276_dp * reyn**(dp05) * shmt**(r1o3))
1307 ! WRITE (msg,'(9(f12.8,1x),f12.3,1x,f12.8)') aird2, wind, pdia, &
1308 ! pedia, dmvc, dmvc2, &
1309 ! gama, reyn, nuss, hbar, beta
1310 ! CALL wrf_debug (wrfdbg, 'SPFire burnout1: '//msg)
1312 ! change in firebrand mass diameter (m)
1314 partb = SQRT(3.0_dp) * (beta**2) * (dt**2)
1315 pdia_new4 = parta - partb
1316 p_diam = pdia_new4**(0.25_dp)
1318 ! change in firebrand effective mass diameter (m)
1319 p_effd2 = (pedia**2) - beta * dt
1320 p_effd = SQRT(p_effd2)
1322 ! new firebrand mass (g)
1323 p_mass = (firebrand_dens * pi * p_effd2 * p_effd)/6.0_dp
1325 ! new firebrand volume (m3)
1326 fbvol = p_mass / firebrand_dens
1328 ! new firebrand temp (K)
1329 qcon = hbar * (p_temp - temp)
1330 qrad = sboltz * emiss * (p_temp**4 - temp**4)
1331 pratio = p_effd / p_diam
1332 cpmix = (pratio * cpw) + ((1.0_dp - pratio) * cpc)
1333 partc = 6.0_dp / (p_mass * cpmix * p_diam)
1334 dtemp = dt * fbvol * partc * (qcon + qrad)
1335 p_temp = p_temp - dtemp
1337 ! WRITE (msg,'(8(f14.12,1x))') parta, partb, pdia_new4, p_diam, p_effd2, p_effd, p_mass, fbvol
1338 ! CALL wrf_debug (wrfdbg, 'SPFire burnout2: '//msg)
1339 ! 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
1340 ! CALL wrf_debug (wrfdbg, 'SPFire burnout3: '//msg)
1342 ! firebrand diameter from (m) to (mm)
1343 p_diam = 1000.0_dp * p_diam
1344 p_effd = 1000.0_dp * p_effd
1346 END SUBROUTINE burnout
1348 !=============================================================
1349 !=============================================================
1353 !=============================================================
1355 SUBROUTINE termvel(p_diam, p_effd, p_tvel, p_temp, hgt, dt, pres, aird, temp)
1356 !=============================================================
1358 !-------------------------------------------------------------------------------
1359 ! This subroutine was developed in RAL-NCAR/UCAR, 2019-2020 by:
1360 ! *** T. W. Juliano ***
1361 ! based on Bhutia, Jenkins & Sun 2010 (DOI:10.3894/JAMES.2010.2.4)
1362 !-------------------------------------------------------------------------------
1364 ! Constants are at module top
1368 REAL, INTENT(IN) :: pres, aird, temp, dt
1369 REAL, INTENT(IN) :: p_diam ! firebrand diameter (mm)
1370 REAL, INTENT(IN) :: p_effd ! firebrand effective diameter (mm)
1371 REAL, INTENT(IN) :: p_temp ! firebrand temperature (K)
1372 REAL, INTENT(INOUT):: hgt ! particle position at height agl (m)
1373 REAL, INTENT(INOUT):: p_tvel ! new firebrand terminal velocity (m/s)
1375 REAL :: pbot, ptop, aird2, pdia, parta, partb
1376 REAL :: drop, vt, pratio ! fbtype,
1378 !-------------------------------------------------------------------------------
1380 ! air density around particle
1381 aird2 = 1000.0_dp * pres / (rd * p_temp)
1383 ! particle diameter (mm) to (m)
1384 pdia = p_diam /1000.0_dp
1387 pratio = p_effd / p_diam
1388 parta = ( (pratio*firebrand_dens) + ( (dp1-pratio) * firebrand_dens_char) ) *pdia*grav
1389 partb = 3.0_dp * ((aird + aird2) / 2.0_dp)*rcd
1391 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
1393 ! WRITE (msg,'(4(f12.6,1x))') parta, partb, pratio, SQRT(pratio)
1394 ! CALL wrf_debug (wrfdbg, 'SPFire termvel : '//msg)
1398 ! change in vertical position due to settling
1400 pbot = MAX(0.0_dp, hgt-drop)
1405 END SUBROUTINE termvel
1407 !=============================================================
1408 !=============================================================
1411 !=============================================================
1413 SUBROUTINE get_local_met(xp, yp, zp, loc_p, loc_d, loc_t, p, t, d, ihs, jhs, ihe, jhe)
1414 !=============================================================
1416 !-------------------------------------------------------------------------------
1418 ! Calculate met from variables updated in HALO after the call to microphysics:
1420 ! 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
1426 !TYPE(domain), INTENT(IN) :: grid
1427 INTEGER, INTENT(IN) :: ihs, jhs, ihe, jhe ! tile +- halo array bounds
1428 REAL, INTENT(IN) :: xp, yp, zp ! particle position
1429 REAL, INTENT(OUT) :: loc_p, loc_t, loc_d ! local met properties
1430 REAL, INTENT(IN), DIMENSION(ims:,kms:,jms:):: p,t,d
1432 INTEGER :: i, j, k , kk
1433 REAL :: tmp1, tmp2, zph ! corresponding zp at half levels
1436 j = MIN(MAX(FLOOR(yp-dp05), jhs), jhe)
1437 i = MIN(MAX(FLOOR(xp-dp05), ihs), ihe)
1439 ! physics variables with (i,j) = (1,1) corresponds to (x,y) = (1.5,1.5)
1442 ! To interpolate T(i=1:2), x=[1.5:2.5[
1443 ! See comment on function hgt2k
1445 ! boundaries ims, ..., ke are declared in module scope
1447 ! p_hydrostatic is on full levels: no vertical (de)staggering needed
1448 loc_p = u_3d_interp( x=xp-dp05, y=yp-dp05, z=zp, &
1449 u_i0j0_bot = p(i, k, j), u_i0j1_bot = p(i, k, j+1),&
1450 u_i1j0_bot = p(i+1,k, j), u_i1j1_bot = p(i+1,k, j+1),&
1451 u_i0j0_top = p(i, k+1, j), u_i0j1_top = p(i, k+1,j+1),&
1452 u_i1j0_top = p(i+1,k+1, j), u_i1j1_top = p(i+1,k+1,j+1))
1454 ! theta moist is on full levels: no vertical (de)staggering needed: th8w
1455 loc_t = u_3d_interp( x=xp-dp05, y=yp-dp05, z=zp, &
1456 u_i0j0_bot = t(i, k, j), u_i0j1_bot = t(i, k, j+1),&
1457 u_i1j0_bot = t(i+1,k, j), u_i1j1_bot = t(i+1,k, j+1),&
1458 u_i0j0_top = t(i, k+1, j), u_i0j1_top = t(i, k+1,j+1),&
1459 u_i1j0_top = t(i+1,k+1, j), u_i1j1_top = t(i+1,k+1,j+1))
1461 ! Variables on half levels: provide the k index corresponding to particle bottom & top levels
1462 ! Is a linear interpolation ok?
1464 ! when zp = 1.0, particle is at the surface on the stag grid,
1465 ! and at k=0.5 on half levels (below the first level)
1466 ! when zp = 1.5, particle is in the middle of the stag grid, and at k=1.0 on half level
1467 ! This is the location where the unstaggered variable value is valid,
1468 ! so the distance between the particle and this point must be 0.
1470 ! Physics variables are all unstaggered in x and y.
1473 k = MAX(1, FLOOR(zph))
1475 loc_d = u_3d_interp( x=xp-dp05, y=yp-dp05, z=zph, &
1476 u_i0j0_bot = d(i, k, j), u_i0j1_bot = d(i, k, j+1),&
1477 u_i1j0_bot = d(i+1,k, j), u_i1j1_bot = d(i+1,k, j+1),&
1478 u_i0j0_top = d(i, k+1, j), u_i0j1_top = d(i, k+1,j+1),&
1479 u_i1j0_top = d(i+1,k+1, j), u_i1j1_top = d(i+1,k+1,j+1))
1481 loc_d = loc_d * 1000.0_dp ! Convert from kg/m3 to g/m3
1484 END SUBROUTINE get_local_met
1486 !=============================================================
1488 !=============================================================
1492 !=============================================================
1493 SUBROUTINE firebrand_spotting_em_driver( &
1494 !=============================================================
1513 fs_count_landed_all, &
1514 fs_count_landed_hist, &
1518 fs_fuel_spotting_risk, &
1522 !-------------------------------------------------------------------------------
1524 !-------------------------------------------------------------------------------
1526 USE module_domain, ONLY: domain_get_time_since_sim_start, &
1527 domain_clock_get, is_alarm_tstep
1528 USE module_domain_type, ONLY: HISTORY_ALARM, restart_alarm, AUXHIST23_ALARM
1529 USE module_state_description, ONLY: p_qv, num_moist, param_first_scalar
1530 USE module_utility, ONLY: WRFU_Alarm !WRFU_Clock,
1531 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
1532 USE module_firebrand_spotting_mpi, ONLY: fs_mpi_init, &
1533 fs_mpi_sendbuff1_int, &
1534 fs_mpi_sendbuff1_real, &
1535 fs_mpi_recvbuff1_int, &
1536 fs_mpi_recvbuff1_real, &
1537 fs_mpi_nothing2send, &
1538 fs_mpi_send2neighbors, &
1539 fs_mpi_sendbuffsize, &
1540 fs_mpi_sendbuff_int, &
1541 fs_mpi_sendbuff_real, &
1542 fs_mpi_recvbuffsize, &
1543 fs_mpi_recvfrompatch_int, &
1544 fs_mpi_recvfrompatch_real, &
1545 fs_mpi_sendbuff_real, &
1546 fs_mpi_checkreceive, &
1548 USE module_firebrand_spotting_mpi, ONLY: neighbors, my_id, task_id, mpiprocs, &
1549 left_id, right_id, up_id, down_id, &
1550 upleft_id, upright_id, downleft_id, downright_id
1555 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
1556 LOGICAL, EXTERNAL :: wrf_dm_on_monitor
1558 !-------------------------------------------------------------------------------
1560 !-------------------------------------------------------------------------------
1562 TYPE(domain), INTENT(IN) :: grid ! input data **Note: Intent IN**
1563 TYPE (GRID_config_rec_type),INTENT(IN) :: cf ! type available by module host association
1565 !-------------------------------------------------------------------------------
1567 !-------------------------------------------------------------------------------
1569 LOGICAL, INTENT(INOUT) :: fs_count_reset
1570 INTEGER, INTENT(INOUT) :: fs_last_gen_dt, fs_gen_idmax
1572 ! Firebrand Spotting Particle Properties (fs_p_*)
1573 INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id, fs_p_src, fs_p_dt
1574 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)
1575 REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_mass, fs_p_diam, fs_p_effd
1576 REAL, INTENT(INOUT), DIMENSION(:) :: fs_p_temp, fs_p_tvel
1577 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
1578 REAL, INTENT(INOUT), DIMENSION(ims:ime,jms:jme) :: fs_count_landed_hist, fs_spotting_lkhd, fs_frac_landed
1579 INTEGER, INTENT(INOUT), DIMENSION(ims:ime,jms:jme) :: fs_landing_mask, fs_gen_inst
1580 REAL, INTENT(INOUT), DIMENSION(ifps:ifpe,jfps:jfpe) :: fs_fire_ROSdt
1582 !-------------------------------------------------------------------------------
1584 !-------------------------------------------------------------------------------
1586 !-------------------------------------------------------------------------------
1587 ! Local Variables - Arrays
1588 !-------------------------------------------------------------------------------
1590 ! Subroutine Control
1591 LOGICAL, DIMENSION(fs_array_maxsize) :: sparse_mask
1593 TYPE(p_properties), DIMENSION(fs_array_maxsize) :: fs_p_prop
1596 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
1597 REAL, DIMENSION(ims:ime, ks:ke, jms:jme):: u, v, rho, th_phy ! *_phy: half levels
1598 REAL, DIMENSION(ims:ime, ks:ke, jms:jme):: qtot, th8w, p_phy, p8w ! *8w: full levels
1599 REAL, DIMENSION(ims:ime, jms:jme) :: msft, w1
1600 REAL, DIMENSION(ks:ke) :: znw
1601 REAL, ALLOCATABLE, DIMENSION(:) :: xout, yout, zout ! firebrand position after advection
1603 !-------------------------------------------------------------------------------
1605 !-------------------------------------------------------------------------------
1607 REAL, ALLOCATABLE, DIMENSION(:,:) :: fuel_spotting_risk_fr, spotthreat_fr ! only need it for history intervals
1609 ! only need it in hist interval - do not hold memory
1610 LOGICAL, ALLOCATABLE, DIMENSION(:,:) :: landing_mask
1611 INTEGER, ALLOCATABLE, DIMENSION(:) :: landing_cnt
1612 INTEGER, ALLOCATABLE, DIMENSION(:) :: np_landed_cnt, np_nlanded ! (MPI: only Master allocates these)
1613 REAL, ALLOCATABLE, DIMENSION(:) :: np_landed_risk, np_frac_landed, np_lkhd ! (MPI: only Master allocates these)
1614 REAL, ALLOCATABLE, DIMENSION(:) :: landed_risk, recv_spot_lkhd, recv_frac_landed
1615 REAL, ALLOCATABLE, DIMENSION(:) :: tmp1
1616 INTEGER :: ndep, ndep_total
1617 !-------------------------------------------------------------------------------
1619 REAL, DIMENSION(ims:ime, jms:jme) :: dt_sum_landed ! sum timestep's deposited fb in 2-D memory array
1620 LOGICAL, SAVE :: hist_flag = .FALSE. ! time of history output
1622 !-------------------------------------------------------------------------------
1624 !-------------------------------------------------------------------------------
1626 INTEGER, ALLOCATABLE, DIMENSION(:) :: np_Ngrdpts, loc_ij
1627 REAL, ALLOCATABLE, DIMENSION(:) :: np_MeanPot
1628 REAL, ALLOCATABLE, DIMENSION(:,:) :: RelPot, fcat_factor, fr_rate_dummy
1629 INTEGER, ALLOCATABLE, DIMENSION(:,:) :: fcat_fr, n_rel_fr2d
1630 LOGICAL, ALLOCATABLE, DIMENSION(:,:) :: rel_mask
1632 INTEGER :: Ngrdpts, Npts_total, relpts, rankpts
1633 REAL :: TotPot, MeanPot, PotThr, rel_ratio, cdf_coeff, LimPot
1635 ! Firebrand Release and Transport
1636 TYPE(p_properties), ALLOCATABLE, DIMENSION(:) :: release_prop
1637 REAL, ALLOCATABLE, DIMENSION(:) :: release_i, release_j, release_k
1638 INTEGER, ALLOCATABLE, DIMENSION(:) :: release_n
1640 !-------------------------------------------------------------------------------
1641 ! Local Variables - Scalars
1642 !-------------------------------------------------------------------------------
1645 ! Variables for tile, memory, and domain bounds are in module scope & are assigned in firebrand_spotting_init
1647 INTEGER :: kk, pp, ii ! counters
1648 INTEGER :: kk1, kk2, k_end
1649 INTEGER :: active_br, prior_br, nresets, idmax, firepts, nbr_sum
1650 REAL :: rdx, rdy, dt, sr_xy ! grid length (1/dx) and wrf's time-step
1652 INTEGER :: firebrand_max_life_dt
1653 REAL :: firebrand_land_hgt, firebrand_gen_maxhgt
1654 LOGICAL :: firebrand_gen_levrand
1655 REAL :: firebrand_gen_prop_diam, firebrand_gen_prop_effd
1656 REAL :: firebrand_gen_prop_temp, firebrand_gen_prop_tvel
1657 INTEGER :: levrand_seed
1659 !-------------------------------------------------------------------------------
1660 ! MPI local Variables (some variables are in module scope)
1661 !-------------------------------------------------------------------------------
1663 INTEGER, SAVE :: MasterId = 0
1664 INTEGER, SAVE :: ntiles = 0
1665 INTEGER, SAVE :: myprocid = 0
1666 LOGICAL, SAVE :: IamMaster = .FALSE.
1668 INTEGER, ALLOCATABLE, DIMENSION(:) :: nbr_id, r_id, r_dt, r_src
1669 REAL, ALLOCATABLE, DIMENSION(:) :: r_x, r_y, r_z
1671 ! MPI receive p_properties
1672 REAL, ALLOCATABLE, DIMENSION(:) :: r_p_m,r_p_d,r_p_e,r_p_t,r_p_v
1673 TYPE(p_properties), ALLOCATABLE, DIMENSION(:) :: fb_mdetv
1676 !-------------------------------------------------------------------------------
1677 ! It was a dummy variable back when fuel moisture was not implemented in wrf-v4.0.1
1678 !-------------------------------------------------------------------------------
1680 REAL, DIMENSION(ifps:ifpe,jfps:jfpe) :: fmcg_2df
1682 !===============================================================================
1684 !===============================================================================
1687 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
1688 IF (mpiprocs == 0) &
1689 CALL fs_mpi_init(grid) ! should be called in fspotting_init (?)
1692 myprocid = my_id * 1000000
1693 IF ( wrf_dm_on_monitor() ) THEN
1698 IamMaster = .TRUE. ! initialized with FALSE. It should be TRUE in serial compilation
1699 ntiles = 1 ! used to allocate the arrays for incomming mpi comms. ntiles=1 in serial builds
1700 myprocid = 1* 1000000
1704 IF ( Is_alarm_tstep(grid%domain_clock, grid%alarms(HISTORY_ALARM)) ) hist_flag = .TRUE.
1705 !IF ( Is_alarm_tstep(grid%domain_clock, grid%alarms(AUXHIST23_ALARM)) ) hist_flag = .TRUE.
1707 CALL wrf_debug (wrfdbg, 'SPFire_driver: History output on next tstep')
1709 fs_last_gen_dt = fs_last_gen_dt + 1
1710 active_br = COUNT( fs_p_id > 0 )
1712 IF (grid%itimestep == 1) &
1713 fs_fire_ROSdt(ifps:ifpe,jfps:jfpe) =0.0_dp
1715 !===============================================================================
1716 ! Reset array after writting history file
1717 !===============================================================================
1719 IF (fs_count_reset) THEN ! if true
1720 !CALL wrf_debug (wrfdbg, 'SPFire_lkhd: resetting fs_count_reset!')
1721 fs_count_landed_hist(:,:) = ZERO_dp
1722 fs_fuel_spotting_risk(:,:) = ZERO_dp
1723 fs_count_reset = .FALSE.
1725 fs_gen_inst(is:ie,js:je) = 0
1728 fs_landing_mask(ims:ime,jms:jme) = 0
1730 !===============================================================================
1731 ! Assign properties to derived type (used locally to shorten argument list)
1732 !===============================================================================
1734 fs_p_prop%p_mass = ZERO_dp
1735 fs_p_prop%p_diam = ZERO_dp
1736 fs_p_prop%p_effd = ZERO_dp
1737 fs_p_prop%p_temp = ZERO_dp
1738 fs_p_prop%p_tvel = ZERO_dp
1740 fs_p_prop(:active_br)%p_mass = fs_p_mass(:active_br)
1741 fs_p_prop(:active_br)%p_diam = fs_p_diam(:active_br)
1742 fs_p_prop(:active_br)%p_effd = fs_p_effd(:active_br)
1743 fs_p_prop(:active_br)%p_temp = fs_p_temp(:active_br)
1744 fs_p_prop(:active_br)%p_tvel = fs_p_tvel(:active_br)
1746 !===============================================================================
1747 ! Assign user defined values from namelist
1748 !===============================================================================
1750 firebrand_max_life_dt = cf%fs_firebrand_max_life_dt
1751 firebrand_land_hgt = cf%fs_firebrand_land_hgt
1752 firebrand_gen_maxhgt = cf%fs_firebrand_gen_maxhgt
1753 firebrand_gen_levrand = cf%fs_firebrand_gen_levrand
1755 firebrand_gen_prop_diam = cf%fs_firebrand_gen_prop_diam
1756 firebrand_gen_prop_effd = cf%fs_firebrand_gen_prop_effd
1757 firebrand_gen_prop_temp = cf%fs_firebrand_gen_prop_temp
1758 firebrand_gen_prop_tvel = cf%fs_firebrand_gen_prop_tvel
1760 firebrand_dens = cf%fs_firebrand_dens ! 513000.0_dp ! density of firebrand (g / m3)
1761 firebrand_dens_char = cf%fs_firebrand_dens_char ! 299000.0_dp ! density of char (g m-3) [gupta et al 2002; fuel]
1763 !===============================================================================
1764 ! Firebrand Generation
1765 !===============================================================================
1767 !-------------------------------------------------------------------------------
1768 ! Accumulate burn rate between generation intervals
1769 !-------------------------------------------------------------------------------
1772 ! fs_fire_ROSdt(ifps:ifpe, jfps:jfpe) = fs_fire_ROSdt(ifps:ifpe, jfps:jfpe) + grid%burnt_area_dt(ifps:ifpe,jfps:jfpe)
1773 fs_fire_ROSdt(ifps:ifpe, jfps:jfpe) = grid%burnt_area_dt(ifps:ifpe,jfps:jfpe)
1774 ! Accumulation prioritises firebrands from gridpts mostly burnt vs recently ignited
1776 !-------------------------------------------------------------------------------
1777 ! Updated fuel moisture from WRF-fire
1778 !-------------------------------------------------------------------------------
1780 fmcg_2df(ifps:ifpe,jfps:jfpe) = grid%fmc_g(ifps:ifpe,jfps:jfpe)
1782 !===============================================================================
1783 ! Firebrand Generation - Release Firebrands - Part 1
1784 !===============================================================================
1786 !-------------------------------------------------------------------------------
1787 ! When a new release is due, calculate fraction of release by fuel category
1788 !-------------------------------------------------------------------------------
1790 IF (fs_last_gen_dt >= fs_gen_dt) THEN
1792 firepts = COUNT(fs_fire_ROSdt(ifps:ifpe,jfps:jfpe) > ZERO_dp)
1793 Ngrdpts = 0 ! Number of active fire points of valid fuel categories
1795 LimPot = 0.000001_dp ! 1e-6
1798 IF (firepts > 0) THEN
1800 !WRITE (msg,'(1(i6,1x))') firepts
1801 !CALL wrf_debug (wrfdbg, 'SPFire_rel firepts: '//msg)
1803 !-------------------------------------------------------------------------------
1804 ! Get fuel category from WRF-FIRE and calculate release potential
1805 !-------------------------------------------------------------------------------
1807 ALLOCATE(fcat_fr(ifps:ifpe,jfps:jfpe), fcat_factor(ifps:ifpe,jfps:jfpe), RelPot(ifps:ifpe,jfps:jfpe))!, rel_fcat(ifps:ifpe,jfps:jfpe))
1809 RelPot(:,:) = ZERO_dp
1810 fcat_factor(:,:) = ZERO_dp
1812 fcat_fr(ifps:ifpe,jfps:jfpe) = RESHAPE(get_fuel_cat(crosswalk=grid%fuel_crosswalk, &
1813 cat=grid%nfuel_cat(ifps:ifpe,jfps:jfpe)), &
1816 fcat_factor(ifps:ifpe,jfps:jfpe)= firebrand_gen_factor(fcat_fr(ifps:ifpe,jfps:jfpe)) !RESHAPE, SHAPE(fcat_fr))
1818 RelPot(ifps:ifpe,jfps:jfpe) = RESHAPE(firebrand_gen_potential(&
1819 fuel_fgi=grid%fgip(ifps:ifpe,jfps:jfpe), &
1820 fuel_mcg=fmcg_2df(ifps:ifpe,jfps:jfpe), &
1821 !fuel_mcg=grid%fmcg2df(ifps:ifpe,jfps:jfpe), &
1822 factor=fcat_factor(ifps:ifpe,jfps:jfpe),&
1823 fire_rate=fs_fire_ROSdt(ifps:ifpe,jfps:jfpe)), &
1826 ! Calculate Release Potential for each burning fuel cat
1827 !-------------------------------------------------------------------------------
1829 Ngrdpts= COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)! RelPot do not count cat with factor of 0
1832 ! Try to increase minimum LimPot to remove very low values of Release Potential
1833 !-------------------------------------------------------------------------------
1834 IF (Ngrdpts > fs_gen_lim) THEN
1835 LimPot = 10.0_dp * LimPot ! 1e-5
1836 Ngrdpts= COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)
1838 IF (Ngrdpts > fs_gen_lim) THEN
1839 LimPot = 10.0_dp * LimPot ! 1e-4
1840 Ngrdpts= COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)
1844 ! WRITE (msg,'(1(i8,1x), 1(f14.8,1x))') Ngrdpts, LimPot
1845 ! CALL wrf_debug (wrfdbg, 'SPFire_rel_lim Ngrdpts, LimP: '//msg)
1848 WRITE (msg,'(1(i6,1x))') Ngrdpts
1849 CALL wrf_debug (wrfdbg, 'SPFire_rel Ngrdpts: '//msg)
1851 ! Set a threshold for Potentials - use only highest values
1852 !-------------------------------------------------------------------------------
1853 IF (Ngrdpts > fs_gen_lim) THEN
1855 ! Find threshold value to select fs_gen_lim points
1856 !-------------------------------------------------------------------------------
1858 ALLOCATE(tmp1(Ngrdpts))
1859 tmp1(1:Ngrdpts) = PACK(RelPot(ifps:ifpe,jfps:jfpe), &
1860 mask=(RelPot(ifps:ifpe,jfps:jfpe) > LimPot))
1862 ! Find a limit value that yields an array size close to fs_gen_lim
1863 LimPot = order_val(tmp1, ORD=fs_gen_lim)
1864 !maxpot = MAXVAL(tmp1)
1867 ! WRITE (msg,'(3(i8,1x), 2(f14.8,1x))') Ngrdpts, COUNT(RelPot > LimPot), SIZE(tmp1), LimPot, maxpot
1868 ! CALL wrf_debug (wrfdbg, 'SPFire_rel_lim Ngrdpts, Cnt>Lim, LimP, max: '//msg)
1870 ! Find the median rank among remaining points
1871 !-------------------------------------------------------------------------------
1873 ALLOCATE(tmp1(COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)))
1874 tmp1 = PACK(RelPot(ifps:ifpe,jfps:jfpe), &
1875 mask=(RelPot(ifps:ifpe,jfps:jfpe) > LimPot))
1877 MeanPot = order_val(tmp1, ORD=INT(REAL(SIZE(tmp1))/2.0))
1878 !maxpot = MAXVAL(tmp1)
1881 ! WRITE (msg,'(4(i8,1x), 3(f14.8,1x))') Ngrdpts, &
1882 ! COUNT(RelPot > MeanPot), SIZE(tmp1), INT(REAL(SIZE(tmp1))/2.0), &
1883 ! MeanPot, MINVAL(tmp1), maxpot
1884 ! CALL wrf_debug (wrfdbg, 'SPFire_rel_lim Ngrdpts, Cnt>Mean, MPot, min, max: '//msg)
1886 Ngrdpts= COUNT(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)
1888 !-------------------------------------------------------------------------------
1890 ELSEIF (Ngrdpts > 0) THEN
1892 ! Find the median rank among points
1893 !-------------------------------------------------------------------------------
1894 MeanPot = order_val(PACK(RelPot(ifps:ifpe,jfps:jfpe), &
1895 mask=(RelPot(ifps:ifpe,jfps:jfpe) > LimPot)), &
1896 ORD=INT(dp05*Ngrdpts))
1898 ! WRITE (msg,'(2(i8,1x), 2(f14.8,1x))') Ngrdpts, COUNT(RelPot > MeanPot), MeanPot, LimPot
1899 ! CALL wrf_debug (wrfdbg, 'SPFire_rel_lim Ngrdpts, Cnt>Mean, MPot, LimP: '//msg)
1904 !-------------------------------------------------------------------------------
1906 IF (Ngrdpts == 0) firepts = 0
1908 !-------------------------------------------------------------------------------
1910 ENDIF ! (firepts > 0)
1911 !-------------------------------------------------------------------------------
1913 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
1914 IF (.NOT. (IamMaster)) THEN
1916 ! nbr > 0 indicates there's fire on tile and RelPot > 0, not the array size to receive in next mpi comm
1917 ! if nbr > 0, next array will be one (array: MeanPot)
1918 CALL fs_mpi_sendbuff1_int(sendto=MasterId, nbr=Ngrdpts)
1920 IF (Ngrdpts > 0) THEN
1921 CALL fs_mpi_sendbuff1_real(sendto=MasterId, nbr=MeanPot)
1922 ! WRITE (msg,'(1(i6,1x), (f14.6,1x))') Ngrdpts, MeanPot
1923 ! CALL wrf_debug (wrfdbg, 'SPFire_rel mpi_send Npts,Meanpot: '//msg)
1928 !===============================================================================
1929 ! Firebrand Generation - Release Firebrands - Part 2
1930 !===============================================================================
1932 !-------------------------------------------------------------------------------
1933 ! MPI Master: Calculate Release Field (fire gridpt cnt by fuel category)
1934 !-------------------------------------------------------------------------------
1938 CALL wrf_debug (wrfdbg, 'SPFire_rel_master --------------------------------------------------- ')
1939 !-------------------------------------------------------------------------------
1940 ! Declare arrays to receive data
1949 ALLOCATE(np_Ngrdpts(ntiles)) ! Number of fuel cat actively burning in each mpiproc
1950 ALLOCATE(np_MeanPot(ntiles))
1951 np_MeanPot(:) = ZERO_dp
1954 np_Ngrdpts(1) = Ngrdpts ! Array index for Master is 1
1955 np_MeanPot(1) = MeanPot
1957 !-------------------------------------------------------------------------------
1958 ! Receive release potential data from procs
1960 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
1962 DO kk=2, mpiprocs ! kk=1 is Master, MPI-IDs begin at 0
1963 np_Ngrdpts(kk) = fs_mpi_recvbuff1_int(fromid=kk-1) ! Comm receive: Number of pts with RelPot>0
1965 IF (np_Ngrdpts(kk) > 0) THEN
1966 np_MeanPot(kk) = fs_mpi_recvbuff1_real(fromid=kk-1)
1967 ! WRITE(msg, '(3(i8,1x), 2(f14.6,1x))') my_id, kk-1, np_Ngrdpts(kk), np_Meanpot(kk), TotPot
1968 ! CALL wrf_debug (wrfdbg, 'SPFire_rel master proc Ngrdpts, MeanPot, TotPot: '//msg)
1973 DO kk=1, ntiles ! at least 1 for serial compile
1974 TotPot = np_MeanPot(kk) * REAL(np_Ngrdpts(kk)) + TotPot
1977 !-------------------------------------------------------------------------------
1978 ! Communicate release sources between Master and procs
1980 Npts_total = SUM( np_Ngrdpts )
1981 IF ( Npts_total > 0) THEN
1983 TotPot = TotPot/(REAL(Npts_total))
1985 !-------------------------------------------------------------------------------
1986 ! Calculate potential threshold for grid pts
1988 ! All elements fit in the array
1989 IF (Npts_total <= fs_gen_lim) THEN
1992 ! Estimate threshold with a linear regression
1993 ELSE ! IF (Npts_total > fs_gen_lim)
1994 rel_ratio = REAL(fs_gen_lim)/(REAL(Npts_total))
1995 cdf_coeff = (dp1 - rel_ratio)/rel_ratio
1996 PotThr = cdf_coeff * TotPot
1999 ! WRITE(msg, '(2(i8,1x), 4(f12.6,1x))') fs_gen_lim, Npts_total, PotThr, rel_ratio, cdf_coeff, TotPot
2000 ! CALL wrf_debug (wrfdbg, 'SPFire_rel_master max, Npts, PotThr: '//msg)
2002 !-------------------------------------------------------------------------------
2005 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2007 DO kk=2, mpiprocs ! kk=1 is Master
2008 IF (np_Ngrdpts(kk) > 0) THEN
2009 CALL fs_mpi_sendbuff1_real(sendto=kk-1, nbr=PotThr)
2010 ! WRITE(msg, '(1(i8,1x), 1(f12.6,1x))') kk-1, PotThr
2011 ! CALL wrf_debug (wrfdbg, 'SPFire_rel_master mpi_send PotThr: '//msg)
2015 ENDIF ! npts_total > 0
2016 !-------------------------------------------------------------------------------
2020 !-------------------------------------------------------------------------------
2022 !===============================================================================
2023 ! Firebrand Generation - Release Firebrands - Part 3
2024 !===============================================================================
2026 IF (Ngrdpts > 0) THEN
2028 !-------------------------------------------------------------------------------
2029 ! Receive Potential Threshold from Master
2031 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2032 IF (.NOT.(IamMaster)) THEN
2034 PotThr = fs_mpi_recvbuff1_real(fromid=MasterId)
2035 ! WRITE(msg, '((i4,1x), (f14.6,1x))') my_id, PotThr
2036 ! CALL wrf_debug (wrfdbg, 'SPFire_rel receive potential threshold: '//msg)
2040 !-------------------------------------------------------------------------------
2041 ! Assign release from the various fuel cat to corresponding gridpts
2043 !CALL wrf_debug (wrfdbg, 'SPFire_rel2fr relpts>0 --------------------------------------------- ')
2045 ALLOCATE(n_rel_fr2d(ifps:ifpe, jfps:jfpe))
2046 n_rel_fr2d(ifps:ifpe, jfps:jfpe) = 0
2048 IF (PotThr > ZERO_dp) &
2049 n_rel_fr2d(ifps:ifpe,jfps:jfpe) = n_rel_fr2d(ifps:ifpe,jfps:jfpe) + &
2050 UNPACK(SPREAD(1, DIM=1, NCOPIES=Ngrdpts), &
2051 mask=(RelPot(ifps:ifpe,jfps:jfpe) >= PotThr), FIELD=0)
2053 IF (PotThr == ZERO_dp) &
2054 n_rel_fr2d(ifps:ifpe,jfps:jfpe) = n_rel_fr2d(ifps:ifpe,jfps:jfpe) + &
2055 UNPACK(SPREAD(1, DIM=1, NCOPIES=Ngrdpts), &
2056 mask=(fs_fire_ROSdt(ifps:ifpe,jfps:jfpe) > ZERO_dp), FIELD=0)
2058 !-------------------------------------------------------------------------------
2059 ! update relpts to represent value assigned to gridpts
2061 relpts = COUNT(n_rel_fr2d(ifps:ifpe,jfps:jfpe)>0) ! Limits to 1 release per refined gridpt, at various heights
2063 WRITE(msg, '(1(i8,1x))') relpts
2064 CALL wrf_debug (wrfdbg, 'SPFire_rel2fr relpts: '//msg)
2066 !-------------------------------------------------------------------------------
2067 ! Convert release locations to met grid
2069 ALLOCATE(release_i(relpts*fs_gen_levels), &
2070 release_j(relpts*fs_gen_levels), &
2071 release_k(relpts*fs_gen_levels), &
2074 release_i(:) = 0.0_dp
2075 release_j(:) = 0.0_dp
2076 release_k(:) = 0.0_dp
2079 ! Does not allow more than one release from the same refined gridpt
2080 CALL releaseijk2atm(nij_2d=n_rel_fr2d(ifps:ifpe,jfps:jfpe), &
2081 fcat=fcat_fr(ifps:ifpe,jfps:jfpe), &
2082 maxhgt_usr = firebrand_gen_maxhgt, &
2083 sr_x=grid%sr_x, sr_y=grid%sr_y, &
2084 pi=release_i(1:relpts), &
2085 pj=release_j(1:relpts), &
2086 pk=release_k(1:relpts), & ! max height
2090 ! Adjust position to corresponding refined grid center
2091 release_i(1:relpts) = release_i(1:relpts) + dp05/grid%sr_x
2092 release_j(1:relpts) = release_j(1:relpts) + dp05/grid%sr_y
2094 DO kk=1,relpts ! track release points between history outputs
2096 fs_gen_inst(INT(release_i(kk)),INT(release_j(kk))) = &
2097 fs_gen_inst(INT(release_i(kk)),INT(release_j(kk))) + 1
2101 CALL wrf_debug (wrfdbg, 'SPFire_rel2fr prep_release_hgt ---------------------------------------------')
2102 ALLOCATE(release_prop(relpts*fs_gen_levels))
2104 ! Create derived type
2105 release_prop(1:relpts) = firebrand_property([firebrand_gen_prop_diam, &
2106 firebrand_gen_prop_effd, &
2107 firebrand_gen_prop_temp, &
2108 firebrand_gen_prop_tvel])
2110 levrand_seed = 0 ! means no random levels
2111 IF (firebrand_gen_levrand) levrand_seed = cf%fs_firebrand_gen_levrand_seed + 1 ! in case user sets it to 0
2112 ! include more releases to span over various heights
2113 CALL prep_release_hgt( &
2114 release_i = release_i, &
2115 release_j = release_j, &
2116 release_k = release_k, & ! max height
2117 release_n = release_n, &
2118 landhgt = firebrand_land_hgt, &
2119 release_prop=release_prop, &
2120 levrand_seed = levrand_seed * grid%itimestep) ! force seed to vary in time
2122 prior_br = active_br
2123 idmax = fs_gen_idmax
2125 CALL wrf_debug (wrfdbg, 'SPFire_driver_call_generate_firebrands ----------------------------------------- ')
2127 IF (active_br + COUNT( INT(release_i) > 0 ) > fs_array_maxsize) &
2128 CALL wrf_debug (wrfdbg, 'SPFire_driver_release: brand array is full, cannot release new brands')
2130 CALL generate_firebrands(&
2131 fs_p_id = fs_p_id, &
2132 fs_p_src = fs_p_src, &
2133 fs_p_dt = fs_p_dt, &
2137 fs_p_prop = fs_p_prop, &
2138 fs_gen_idmax= fs_gen_idmax, &
2139 myprocid = myprocid, &
2140 release_prop= release_prop, &
2141 release_i = release_i, &
2142 release_j = release_j, &
2143 release_k = release_k, &
2144 active_br = active_br)
2146 !-------------------------------------------------------------------------------
2147 ! Track released firebrands
2148 !-------------------------------------------------------------------------------
2150 WRITE (msg,'(i8)') active_br
2151 CALL wrf_debug (wrfdbg, 'SPFire_driver: Active brands: '// msg )
2153 IF (cf%trackember) THEN
2155 DO kk=prior_br+1,active_br
2156 WRITE (msg,'(3(i8,1x),i10,1x,8(f12.6,1x))') &
2157 kk, fs_p_id(kk), fs_p_dt(kk),fs_p_src(kk), &
2158 fs_p_x(kk), fs_p_y(kk), fs_p_z(kk), &
2159 fs_p_mass(kk), fs_p_diam(kk), fs_p_effd(kk), &
2160 fs_p_temp(kk), fs_p_tvel(kk)
2161 CALL wrf_debug (0, 'SPFire p,id,dt,src,x,y,z,m,d,e,t,v: '//msg)
2164 !-------------------------------------------------------------------------------
2166 IF (active_br /= COUNT( fs_p_id > 0 ) ) CALL wrf_error_fatal('SPFire_driver: Active brands do not match!')
2170 fs_fire_ROSdt(:, :) = ZERO_dp
2172 fs_last_gen_dt = 0 ! Reset release timestep counter
2173 IF( ALLOCATED(fcat_fr)) DEALLOCATE(fcat_fr)
2174 IF( ALLOCATED(fcat_factor)) DEALLOCATE(fcat_factor)
2175 IF( ALLOCATED(RelPot)) DEALLOCATE(RelPot)
2176 IF( ALLOCATED(np_Ngrdpts)) DEALLOCATE(np_Ngrdpts)
2177 IF( ALLOCATED(np_MeanPot)) DEALLOCATE(np_MeanPot)
2178 IF( ALLOCATED(n_rel_fr2d)) DEALLOCATE(n_rel_fr2d)
2179 IF( ALLOCATED(release_prop)) DEALLOCATE(release_prop)
2181 ENDIF ! IF (fs_last_gen_dt >= fs_gen_dt) THEN
2183 !===============================================================================
2185 !===============================================================================
2187 !-------------------------------------------------------------------------------
2188 ! Nothing to transport: MPI Wait
2189 !-------------------------------------------------------------------------------
2191 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2192 IF ((active_br == 0)) THEN
2194 CALL fs_mpi_nothing2send(sendto=left_id)
2195 CALL fs_mpi_nothing2send(sendto=right_id)
2196 CALL fs_mpi_nothing2send(sendto=up_id)
2197 CALL fs_mpi_nothing2send(sendto=down_id)
2199 CALL fs_mpi_nothing2send(sendto=upleft_id)
2200 CALL fs_mpi_nothing2send(sendto=upright_id)
2201 CALL fs_mpi_nothing2send(sendto=downleft_id)
2202 CALL fs_mpi_nothing2send(sendto=downright_id)
2203 !CALL wrf_debug (wrfdbg, 'SPFire_driver: no fires or active brands in patch. MPI signal sent.')
2207 !===============================================================================
2208 !===============================================================================
2212 !===============================================================================
2213 ! Transport active firebrands
2214 !===============================================================================
2216 IF (active_br > 0) THEN
2218 !-------------------------------------------------------------------------------
2219 ! Fetch wrf variables for advection
2220 !-------------------------------------------------------------------------------
2222 ! Use arrays computed for the physics scheme on half (_phy) and full (8w) levels
2223 ! will skip calc at model top - particle will not live during journey (module_big_step_utilities_em.F)
2225 !-------------------------------------------------------------------------------
2226 ! Local met for fb physics: based on variables calculated in module_big_step_utilities_em.F
2227 !-------------------------------------------------------------------------------
2229 !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
2230 !-------------------------------------------------------------------------------
2231 ! P, Th, Rho, hgt at full levels
2233 ! Direct assignments from existing Halos:
2236 k_end = MIN( ke, kde-1 )
2239 p_phy(ims:, ks:, jms:) = ZERO_dp
2240 th_phy(ims:, ks:, jms:) = ZERO_dp
2243 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)
2244 th_phy(ims:, ks:, jms:) = grid%t_2(ims:ime,ks:k_end,jms:jme) + t0
2246 !hypsometric_opt 2 (default) computes pressure in the model by using an alternative method
2247 ! Temperature is moist theta by default (v4+, https://github.com/wrf-model/WRF/releases)
2248 IF ( ( grid%use_theta_m == 1 ) .AND. (P_Qv >= PARAM_FIRST_SCALAR) ) &
2249 th_phy = th_phy/(dp1 + Rv/Rd * grid%moist(ims:ime,ks:k_end,jms:jme,P_QV))
2252 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
2254 u(ims:, ks:, jms:) = grid%u_2(ims:ime,ks:ke,jms:jme)
2255 v(ims:, ks:, jms:) = grid%v_2(ims:ime,ks:ke,jms:jme)
2256 w(ims:, ks:, jms:) = grid%w_2(ims:ime,ks:ke,jms:jme)
2258 msft(ims:, jms:) = grid%msftx(ims:ime, jms:jme)
2262 rdx = grid%rdx ! inverse grid length [m]
2265 !-------------------------------------------------------------------------------
2266 ! Calculated variables
2268 p_hyd(ims:, ks:, jms:) = ZERO_dp
2269 dz8w(ims:, ks:, jms:) = ZERO_dp
2270 th8w(ims:, ks:, jms:) = ZERO_dp
2271 p8w(ims:, ks:, jms:) = ZERO_dp
2272 qtot(ims:, ks:, jms:) = ZERO_dp
2273 rho(ims:, ks:, jms:) = ZERO_dp
2274 z(ims:, ks:, jms:) = ZERO_dp
2276 dz8w(ims:,ks:ke-1,jms:) = z_at_w(:,2:,:)-z_at_w(:,:ke-1,:) ! z_at_w: Height above the ground at interfaces
2277 z(ims:,ks:ke-1,jms:) = dp05 * (z_at_w(:,2:,:) + z_at_w(:,:ke-1,:) )
2279 qtot(ims:, ks:k_end, jms:) = SUM(grid%moist(ims:ime,ks:k_end,jms:jme,PARAM_FIRST_SCALAR:num_moist),DIM=4)
2280 p_hyd(ims:, ke, jms:) = grid%p_top
2281 DO kk = ke-1, ks, -1
2282 p_hyd(:,kk,:) = p_hyd(:,kk+1,:) - &
2283 (dp1 + qtot(:,kk,:)) * (grid%c1h(kk) * grid%muts(:,:) + grid%c2h(kk)) * grid%dnw(kk)
2286 rho(ims:,ks:k_end,jms:) = (dp1/&
2287 (grid%al(ims:ime,ks:k_end,jms:jme)+grid%alb(ims:ime,ks:k_end,jms:jme)))&
2288 *(dp1+grid%moist(ims:ime,ks:k_end,jms:jme,P_QV))
2290 th8w(ims:,kk,jms:) = grid%fnm(kk) * th_phy(:,kk,:) + th_phy(:,kk-1,:) * grid%fnp(kk)
2291 p8w(ims:,kk,jms:) = grid%fnm(kk) * p_phy(:,kk,:) + p_phy(:,kk-1,:) * grid%fnp(kk)
2294 w1 = (z(:,1,:) - z(:,2,:))
2295 WHERE(NINT(w1*100.0) == 0 ) w1 = dp1
2296 w1 = (z_at_w(:,1,:) - z(:,2,:)) / w1
2298 th8w(:,1,:) = w1 * th_phy(:,1,:) + (dp1 - w1) * th_phy(:,2,:) ! interp at bottom only - ignore top
2299 p8w(:,1,:) = w1 * p_phy(:,1,:) + (dp1 - w1) * p_phy(:,2,:) ! interp at bottom only - ignore top
2302 z_at_w(:,kk,:)= z_at_w(:,kk,:) - z_at_w(:,1,:)
2305 !-------------------------------------------------------------------------------
2306 !-------------------------------------------------------------------------------
2308 !-------------------------------------------------------------------------------
2310 !-------------------------------------------------------------------------------
2312 CALL wrf_debug (wrfdbg, 'SPFire_driver begin transport ---------------------------------------------------- ')
2314 ALLOCATE(xout(active_br), &
2318 CALL advect_xyz_m(grid=grid, &
2319 xp= fs_p_x(:active_br), &
2320 yp= fs_p_y(:active_br), &
2321 hgt= fs_p_z(:active_br), & ! height[m] is the fixed position reference between wrf time steps
2322 dtp= fs_p_dt(:active_br),&
2323 idp= fs_p_id(:active_br),&
2334 phyd= p_hyd, & ! air pressure (Pa)
2335 thet= th8w, & ! temperature (K)
2336 rho = rho, & ! density (kg/m3)
2340 fs_p_prop=fs_p_prop(:active_br), &
2341 land_hgt = firebrand_land_hgt, &
2342 start_mom3d_dt = cf%fs_firebrand_gen_mom3d_dt, &
2346 IF (LEN(TRIM(msg)) > 1) &
2347 CALL wrf_debug (wrfdbg, 'SPFire_transp: '//msg)
2349 !-------------------------------------------------------------------------------
2350 ! Update array for output before resetting deposited indices to zero
2351 !-------------------------------------------------------------------------------
2353 fs_p_x(:active_br) = xout
2354 fs_p_y(:active_br) = yout
2355 fs_p_z(:active_br) = zout
2356 fs_p_dt(:active_br)= fs_p_dt(:active_br) +1
2358 fs_p_mass(:active_br) = fs_p_prop(:active_br)%p_mass
2359 fs_p_diam(:active_br) = fs_p_prop(:active_br)%p_diam
2360 fs_p_effd(:active_br) = fs_p_prop(:active_br)%p_effd
2361 fs_p_temp(:active_br) = fs_p_prop(:active_br)%p_temp
2362 fs_p_tvel(:active_br) = fs_p_prop(:active_br)%p_tvel
2364 CALL wrf_debug (wrfdbg, 'SPFire_driver end transport ---------------------------------------------------- ')
2366 !=============================================================
2368 !=============================================================
2370 !-------------------------------------------------------------------------------
2372 !-------------------------------------------------------------------------------
2374 WRITE (msg,'(2(i8,x))') active_br, COUNT( fs_p_id > 0 )
2375 CALL wrf_debug (wrfdbg, 'SPFire_driver: Active brands, id>0: '// msg )
2377 IF (cf%trackember) THEN
2379 WRITE (msg,'(3(i8,1x),i10,1x,8(f12.6,1x))') &
2380 kk, fs_p_id(kk), fs_p_dt(kk),fs_p_src(kk), &
2381 fs_p_x(kk), fs_p_y(kk), fs_p_z(kk), &
2382 fs_p_mass(kk), fs_p_diam(kk), fs_p_effd(kk), &
2383 fs_p_temp(kk), fs_p_tvel(kk)
2384 CALL wrf_debug (0, 'SPFire p,id,dt,src,x,y,z,m,d,e,t,v: '//msg)
2387 !-------------------------------------------------------------------------------
2389 !===============================================================================
2391 !===============================================================================
2393 !-------------------------------------------------------------------------------
2394 ! Set NaN properties for particle removal
2395 !-------------------------------------------------------------------------------
2396 sparse_mask = .FALSE.
2398 WHERE (IEEE_IS_NAN(fs_p_mass) .OR. IEEE_IS_NAN(fs_p_diam) .OR. &
2399 IEEE_IS_NAN(fs_p_effd) .OR. IEEE_IS_NAN(fs_p_temp) .OR. &
2400 IEEE_IS_NAN(fs_p_tvel)) fs_p_effd = ZERO_dp ! ediam=zero will be removed in remove_br
2403 !===============================================================================
2404 ! Remove particles - from burnout or out of domain
2405 !===============================================================================
2407 CALL remove_br(& !active_br= active_br, &
2408 fs_p_id = fs_p_id, &
2412 fs_p_dt = fs_p_dt, &
2413 fs_p_effd = fs_p_effd, &
2415 max_life_dt= firebrand_max_life_dt, &
2416 land_hgt = firebrand_land_hgt)
2417 WRITE (msg,'(i12)') nresets
2418 CALL wrf_debug (wrfdbg, 'SPFire_driver remove br: '//msg)
2421 !===============================================================================
2422 ! Spotfires: Deposited Brands
2423 !===============================================================================
2425 dt_sum_landed(ims:, jms:) = ZERO_dp
2426 CALL deposit_br(& !active_br= active_br, &
2427 fs_p_id = fs_p_id, &
2431 sum_fbrand = dt_sum_landed,&
2432 land_hgt = firebrand_land_hgt)
2434 ! The mask is based on floor(fs_p_x,y) so deposits should not exist at ie or je
2435 fs_count_landed_all(is:ie, js:je) = fs_count_landed_all(is:ie, js:je) + dt_sum_landed(is:ie, js:je)
2436 fs_count_landed_hist(is:ie, js:je) = fs_count_landed_hist(is:ie, js:je) + dt_sum_landed(is:ie, js:je)
2437 WRITE (msg,'(2(i12,x))') COUNT(dt_sum_landed(is:ie, js:je) > 0), INT(SUM(dt_sum_landed(is:ie, js:je)))
2438 CALL wrf_debug (wrfdbg, 'SPFire_driver deposit br: '//msg)
2441 !===============================================================================
2442 !===============================================================================
2444 !******************************************************************
2445 !******************************************************************
2447 !* MPI Send firebrands *
2449 !******************************************************************
2450 !******************************************************************
2453 !-------------------------------------------------------------------------------
2454 !-------------------------------------------------------------------------------
2456 ! Re-PACK before incoming MPI
2458 !===============================================================================
2459 ! MPI: Send Brands (out of this patch)
2460 !===============================================================================
2462 ! CALL wrf_debug (wrfdbg, 'SPFire_driver mpi transfer particles')
2464 ! Send / Receive arrays:
2465 ! 3 REAL arrays, 2 INTEGER arrays: fs_p_x, fs_p_y, fs_p_z, fs_p_id, fs_p_dt:
2469 ! |____|____|____|je
2471 ! |____|____|____|js
2478 !-------------------------------------------------------------------------------
2479 ! Send any particle?
2480 !-------------------------------------------------------------------------------
2482 sparse_mask = .FALSE.
2483 sparse_mask = (fs_p_id > 0 .AND. &
2484 (FLOOR(fs_p_x) < is .OR. & ! LEFT
2485 FLOOR(fs_p_x) > ie .OR. & ! RIGHT
2486 FLOOR(fs_p_y) > je .OR. & ! UP
2487 FLOOR(fs_p_y) < js )) ! DONW
2490 WRITE (msg,'(i6)') COUNT(sparse_mask)
2491 CALL wrf_debug (wrfdbg, 'SPFire_driver mpi send away: '//msg)
2493 !-------------------------------------------------------------------------------
2494 ! interrupt before MPI
2495 ! IF (COUNT(sparse_mask) > 0) CALL wrf_error_fatal( "MPI called")
2497 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2498 CALL fs_mpi_send2neighbors(task_id=task_id,&
2506 fs_p_m = fs_p_mass, &
2507 fs_p_d = fs_p_diam, &
2508 fs_p_e = fs_p_effd, &
2509 fs_p_t = fs_p_temp, &
2512 !-------------------------------------------------------------------------------
2514 !-------------------------------------------------------------------------------
2515 ! CALL wrf_debug (wrfdbg, 'SPFire_driver reset particles sent away')
2517 ! In serial compilation, firebrands outside the domain limits will be removed
2518 WHERE(sparse_mask) fs_p_id= 0
2520 !===============================================================================
2521 ! Last step for IF active_br > 0
2522 !===============================================================================
2524 ! WRITE (msg,'(2(i8,1x))') active_br, COUNT( fs_p_id > 0 )
2525 ! CALL wrf_debug (wrfdbg, 'SPFire_driver pack BEFORE: active_br >>> '// msg)
2527 !-------------------------------------------------------------------------------
2528 ! Repack all zeros: PACK where mask is TRUE
2529 !-------------------------------------------------------------------------------
2530 sparse_mask = .FALSE.
2531 sparse_mask = (fs_p_id>0)
2532 fs_p_x = PACK(fs_p_x, sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) )
2533 fs_p_y = PACK(fs_p_y, sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) )
2534 fs_p_z = PACK(fs_p_z, sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) )
2535 fs_p_id = PACK(fs_p_id, sparse_mask, SPREAD(0,1,fs_array_maxsize) )
2536 fs_p_src = PACK(fs_p_src, sparse_mask, SPREAD(0,1,fs_array_maxsize) )
2537 fs_p_dt = PACK(fs_p_dt, sparse_mask, SPREAD(0,1,fs_array_maxsize) )
2538 fs_p_mass = PACK(fs_p_mass , sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) )
2539 fs_p_diam = PACK(fs_p_diam , sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) )
2540 fs_p_effd = PACK(fs_p_effd, sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) )
2541 fs_p_temp = PACK(fs_p_temp , sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) )
2542 fs_p_tvel = PACK(fs_p_tvel , sparse_mask, SPREAD(ZERO_dp,1,fs_array_maxsize) )
2544 active_br = COUNT( fs_p_id > 0 )
2546 WRITE (msg,'(1(i8,1x))') active_br
2547 CALL wrf_debug (wrfdbg, 'SPFire_driver pack AFTER mpi_send2neighbors active_br >>> '// msg)
2552 !===============================================================================
2553 ! Active_br work done ;)
2554 !===============================================================================
2555 IF (active_br /= COUNT( fs_p_id > 0 ) ) CALL wrf_error_fatal('SPFire_driver: Active brands do not match!')
2557 !===============================================================================
2558 !===============================================================================
2562 !===============================================================================
2563 ! Calculate Spotfire Likelihood at history output time
2564 !===============================================================================
2567 ! fs_count_landed_all
2568 ! fs_count_landed_hist
2574 ! store array of data for each proc: Master only
2575 ! np_nlanded [np] ! ALLOCATABLE
2576 ! np_landed_cnt [SUM(np_nlanded)] ! ALLOCATABLE
2577 ! np_landed_risk [SUM(np_nlanded)] ! ALLOCATABLE
2578 ! np_frac_landed [SUM(np_nlanded)] ! ALLOCATABLE
2579 ! np_lkhd [SUM(np_nlanded)] ! ALLOCATABLE
2582 ! store array of local data
2583 ! landing_mask ! ALLOCATABLE
2584 ! landing_cnt ! ALLOCATABLE (mpi send)
2585 ! landed_risk ! ALLOCATABLE (mpi send)
2586 ! recv_spot_lkhd ! ALLOCATABLE (mpi recv)
2587 ! recv_frac_landed ! ALLOCATABLE (mpi recv)
2589 !===============================================================================
2590 ! Check history interval (use diag_flag defined in solve_em for microphysics)
2591 !===============================================================================
2593 IF ((hist_flag) .AND. (grid%itimestep > 1)) THEN
2595 fs_count_reset = .TRUE. ! true
2596 fs_frac_landed(ims:ime, jms:jme) = ZERO_dp
2597 fs_spotting_lkhd(ims:ime, jms:jme) = ZERO_dp
2599 ! Was there any firebrand deposit? *Note that fire may exist on a different tile
2600 !-------------------------------------------------------------------------------
2602 ALLOCATE(landing_mask(ims:ime, jms:jme)) ! Allocate memory but only mask this tile
2603 landing_mask(ims:ime, jms:jme) = .FALSE.
2604 landing_mask(is:ie, js:je) = (fs_count_landed_hist(is:ie, js:je) > 0)
2605 ndep = COUNT(landing_mask)
2607 ! convert fire_area from fire grid to atm grid
2608 !-------------------------------------------------------------------------------
2609 fs_fire_area(is:ie,js:je) = ZERO_dp
2610 fs_fire_area(is:ie, js:je) = fire2tile(fr_arr=grid%fire_area(ifps:ifpe,jfps:jfpe), &
2611 dsx=grid%sr_x,dsy=grid%sr_y)
2613 ! WRITE(msg, '(i8,1x,i12)') ndep, COUNT(fs_fire_area(is:ie,js:je)> ZERO_dp)
2614 ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd: deposit points, fire points: '//msg)
2616 IF (ndep > 0) THEN ! deposited anywhere
2618 landing_mask(ims:ime, jms:jme) = .FALSE.
2619 landing_mask(is:ie, js:je) = ((fs_count_landed_hist(is:ie, js:je) > 0) .AND. &
2620 (fs_fire_area(is:ie, js:je) == ZERO_dp) )
2621 ndep = COUNT(landing_mask)
2623 ! deposited on nonburning gridpt
2624 fs_landing_mask(is:ie, js:je) = UNPACK(SPREAD(1,DIM=1,NCOPIES=ndep), MASK=landing_mask(is:ie, js:je), FIELD=0)
2626 ! 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))
2627 ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd deposits, non fire-point deposits, fire-point deposits: '//msg)
2629 ALLOCATE(tmp1(ndep))
2630 tmp1 = PACK(fs_count_landed_hist(is:ie, js:je), landing_mask(is:ie, js:je)) ! WHERE doest work on memory bounds
2631 fs_count_landed_hist(is:ie, js:je) = ZERO_dp
2632 fs_count_landed_hist(is:ie, js:je) = UNPACK(tmp1, MASK=landing_mask(is:ie, js:je), FIELD=ZERO_dp)
2634 ! WRITE(msg, '(2(i8,1x), 1(f14.6,1x))') COUNT(fs_count_landed_hist > ZERO_dp), ndep, MAXVAL(fs_count_landed_hist)
2635 ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd deposits, non fire-point deposits, fire-point deposits: '//msg)
2638 ! Were these firebrand deposited outside the fire area?
2639 !-------------------------------------------------------------------------------
2643 ! Calculate fuel risk
2644 !-------------------------------------------------------------------------------
2646 ALLOCATE(fuel_spotting_risk_fr(ifps:ifpe,jfps:jfpe), &!fs_fuel_spotting_risk(ims:ime,jms:jme), &
2647 spotthreat_fr(ifps:ifpe,jfps:jfpe))
2648 fuel_spotting_risk_fr(:,:) = ZERO_dp
2649 fs_fuel_spotting_risk(:,:) = ZERO_dp
2650 spotthreat_fr(:,:) = ZERO_dp
2652 IF (.NOT. ALLOCATED(fcat_fr)) THEN
2653 ALLOCATE(fcat_fr(ifps:ifpe,jfps:jfpe))
2654 fcat_fr(:,:) = ZERO_dp
2655 fcat_fr(ifps:ifpe,jfps:jfpe) = RESHAPE(&
2656 get_fuel_cat(crosswalk=grid%fuel_crosswalk, &
2657 cat=grid%nfuel_cat(ifps:ifpe,jfps:jfpe)), &
2658 SHAPE(fuel_spotting_risk_fr))
2660 !WRITE(msg, *) MAXVAL(fcat_fr), MINVAL(fcat_fr)
2661 ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd fcat max,min '//msg)
2663 spotthreat_fr(ifps:ifpe,jfps:jfpe)= spotting_threat_factor(fcat_fr(ifps:ifpe,jfps:jfpe)) !RESHAPE(SHAPE(fuel_spotting_risk_fr))
2664 ! get_fuel_cat(crosswalk=grid%fuel_crosswalk, &
2665 ! cat=grid%nfuel_cat(ifps:ifpe,jfps:jfpe))), &
2666 ! WRITE(msg, *) MAXVAL(spotthreat_fr), MINVAL(spotthreat_fr)
2667 ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd spotting_threat_factor max,min'//msg)
2669 fuel_spotting_risk_fr(ifps:ifpe,jfps:jfpe) = RESHAPE(&
2670 fuel_spotting_risk(&
2671 fuel_fgi=grid%fgip(ifps:ifpe,jfps:jfpe), &
2672 fuel_mcg=fmcg_2df(ifps:ifpe,jfps:jfpe), &
2673 !fuel_mcg=grid%fmcg2df(ifps:ifpe,jfps:jfpe), &
2674 factor=spotthreat_fr(ifps:ifpe,jfps:jfpe)), &
2675 SHAPE(fuel_spotting_risk_fr)) !, fuel_mcg=grid%fmcg2df)
2677 fs_fuel_spotting_risk(is:ie,js:je) = fire2tile(fr_arr=fuel_spotting_risk_fr(ifps:ifpe,jfps:jfpe), &
2678 dsx=grid%sr_x,dsy=grid%sr_y)
2681 !-------------------------------------------------------------------------------
2685 !-------------------------------------------------------------------------------
2687 ALLOCATE(landing_cnt(ndep), landed_risk(ndep), recv_spot_lkhd(ndep), recv_frac_landed(ndep))
2688 landing_cnt = PACK(fs_count_landed_hist(is:ie,js:je), landing_mask(is:ie,js:je))
2689 landed_risk = PACK(fs_fuel_spotting_risk(is:ie,js:je), landing_mask(is:ie,js:je))
2691 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2692 IF (.NOT. (IamMaster)) THEN
2694 CALL fs_mpi_sendbuffsize(sendto=MasterId, nbr=ndep) ! Comm send >0: BuffSz
2695 CALL fs_mpi_sendbuff_int(sendto=MasterId, bsz=ndep, buff=landing_cnt(1:ndep))
2696 CALL fs_mpi_sendbuff_real(sendto=MasterId, bsz=ndep, buff=landed_risk(1:ndep))
2700 ENDIF ! (fs_count_landed_hist > 0 .AND. fire_area == 0)
2701 ENDIF ! SUM(fs_count_landed_hist) > 0
2703 !-------------------------------------------------------------------------------
2705 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2706 IF ((ndep == 0) .AND. .NOT.(IamMaster)) &
2707 CALL fs_mpi_nothing2send(sendto=MasterId) ! Comm send 0: BuffSz
2709 !-------------------------------------------------------------------------------
2710 ! Master: Calculate Likelihood Field
2711 !-------------------------------------------------------------------------------
2715 !-------------------------------------------------------------------------------
2716 ! Declare arrays to receive data
2718 ALLOCATE(np_nlanded(ntiles))
2720 np_nlanded(1) = ndep ! Array index for Master is 1
2722 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2723 DO kk=2, mpiprocs ! kk=1 is Master, MPI-IDs begin at 0
2724 np_nlanded(kk) = fs_mpi_recvbuffsize(fromid=kk-1) ! Comm receive: BuffSz
2725 ! WRITE(msg, '(2(i8,1x))') kk,np_nlanded(kk)
2726 ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd proc deposit: '//msg)
2729 !-------------------------------------------------------------------------------
2730 ! Communicate deposits between Master and procs
2732 ndep_total = SUM(np_nlanded)
2734 IF ( ndep_total > 0) THEN
2735 ALLOCATE(np_landed_cnt(ndep_total), np_landed_risk(ndep_total))
2737 np_landed_cnt(1:ndep) = landing_cnt
2738 np_landed_risk(1:ndep)= landed_risk
2740 np_landed_cnt(1) = ZERO_dp ! I don't think I need this
2741 np_landed_risk(1)= ZERO_dp
2744 !-------------------------------------------------------------------------------
2745 ! Receive deposit data from procs
2747 kk1 = ndep + 1 ! index start after master's
2749 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2750 DO kk=2, mpiprocs ! kk=1 is Master
2752 IF (np_nlanded(kk) > 0) THEN
2754 kk2 = kk1 + np_nlanded(kk) -1
2756 ! WRITE(msg, '(4(i8,1x))') kk, np_nlanded(kk), kk1, kk2
2757 ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd master kk, np_dep, k1:k2: '//msg)
2758 np_landed_cnt(kk1:kk2) = fs_mpi_recvfrompatch_int(bsz=np_nlanded(kk), fromid=kk-1)
2759 np_landed_risk(kk1:kk2)= fs_mpi_recvfrompatch_real(bsz=np_nlanded(kk), fromid=kk-1)
2760 kk1 = kk1 + np_nlanded(kk)
2765 !-------------------------------------------------------------------------------
2766 ! Calculate ratio and lkhd
2768 ALLOCATE(np_frac_landed(ndep_total), np_lkhd(ndep_total))
2769 np_frac_landed(:) = ZERO_dp
2770 np_lkhd(:) = ZERO_dp
2772 np_frac_landed(1:ndep_total) = REAL(np_landed_cnt(1:ndep_total))/REAL(SUM(np_landed_cnt))
2773 np_lkhd(1:ndep_total) = np_frac_landed(1:ndep_total) * np_landed_risk(1:ndep_total)
2774 np_lkhd(1:ndep_total) = np_lkhd(1:ndep_total)/MAXVAL(np_lkhd)
2776 ! DO pp=1,SIZE(np_landed_cnt)
2777 ! 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)
2778 ! CALL wrf_debug (wrfdbg, 'SPFire_lkhd master np_frac_landed, np_lkhd, : '//msg)
2781 !-------------------------------------------------------------------------------
2784 kk1 = ndep + 1 ! index start after master's
2786 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2787 DO kk=2, mpiprocs ! kk=1 is Master
2789 IF (np_nlanded(kk) > 0) THEN
2791 kk2 = kk1 + np_nlanded(kk) -1
2793 CALL fs_mpi_sendbuff_real(sendto=kk-1, bsz=np_nlanded(kk), buff=np_frac_landed(kk1:kk2))
2794 CALL fs_mpi_sendbuff_real(sendto=kk-1, bsz=np_nlanded(kk), buff=np_lkhd(kk1:kk2))
2796 kk1 = kk1 + np_nlanded(kk)
2800 ENDIF ! SUM(np_nlanded) > 0
2801 !-------------------------------------------------------------------------------
2805 !-------------------------------------------------------------------------------
2807 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2808 IF (.NOT.(IamMaster) .AND. (ndep > 0)) THEN
2810 recv_frac_landed = fs_mpi_recvfrompatch_real(bsz=ndep, fromid=MasterId)
2811 fs_frac_landed(is:ie,js:je) = UNPACK(recv_frac_landed, MASK=landing_mask(is:ie,js:je), FIELD=ZERO_dp)
2813 recv_spot_lkhd = fs_mpi_recvfrompatch_real(bsz=ndep, fromid=MasterId)
2814 fs_spotting_lkhd(is:ie,js:je) = UNPACK(recv_spot_lkhd, MASK=landing_mask(is:ie,js:je), FIELD=ZERO_dp)
2819 IF ((IamMaster) .AND. (ndep > 0)) THEN
2821 fs_frac_landed(is:ie,js:je) = UNPACK(np_frac_landed(1:ndep), MASK=landing_mask(is:ie,js:je), FIELD=ZERO_dp)
2822 fs_spotting_lkhd(is:ie,js:je) = UNPACK(np_lkhd(1:ndep), MASK=landing_mask(is:ie,js:je), FIELD=ZERO_dp)
2828 !===============================================================================
2829 !===============================================================================
2832 !******************************************************************
2833 !******************************************************************
2835 !* MPI Receive firebrands *
2837 !******************************************************************
2838 !******************************************************************
2841 !===============================================================================
2842 ! MPI: Receive Brands (into this patch)
2843 !===============================================================================
2847 !-------------------------------------------------------------------------------
2848 ! Anything to receive? This part is only needed when compiled in parallel
2849 !-------------------------------------------------------------------------------
2851 #if ( defined(DM_PARALLEL) && ! defined(STUBMPI) )
2853 ALLOCATE(nbr_id(neighbors))
2855 nbr_id(:)=fs_mpi_checkreceive(task_list=task_id, np=neighbors)
2857 nbr_sum = SUM(nbr_id)
2858 IF (nbr_sum > 0) THEN
2860 WRITE (msg,'(16(i4,1x))') ([task_id(ii), nbr_id(ii)], ii=1,neighbors)
2861 CALL wrf_debug (wrfdbg, 'SPFire_driver mpi_check_receive (taskID, N): '//msg)
2863 ! Receiving positions, id, dt
2864 ALLOCATE(r_x(nbr_sum), r_y(nbr_sum), r_z(nbr_sum), r_id(nbr_sum), r_src(nbr_sum), r_dt(nbr_sum))
2872 ! Receiving p_propertieserties
2873 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))
2874 ALLOCATE(fb_mdetv(nbr_sum))
2876 CALL fs_mpi_recv(np_id=nbr_id, task_id=task_id, &
2877 r_x=r_x, r_y=r_y, r_z=r_z, &
2878 r_id=r_id, r_src=r_src, r_dt=r_dt, &
2885 ! Assign values to array and release incoming particles
2886 !release_mpi_ijk = RESHAPE([r_x,r_y,r_z], [nbr_sum,3])
2887 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)]
2890 ! WRITE(msg,'(3(i6,1x),3(f12.6,1x))') r_id(kk), r_src(kk), r_dt(kk), r_x(kk), r_y(kk), r_z(kk)
2891 ! CALL wrf_debug (wrfdbg, 'SPFire_mpi recv AFTER >>> '// msg)
2894 prior_br = active_br + 1 ! Array is packed so new particles will be assgned at this position
2895 CALL generate_firebrands(&
2896 fs_p_id = fs_p_id, &
2897 fs_p_src = fs_p_src, &
2898 fs_p_dt = fs_p_dt, &
2902 fs_gen_idmax = fs_gen_idmax, &
2903 myprocid = myprocid, &
2904 active_br = active_br, &
2908 release_dt = r_dt, &
2909 release_src = r_src, &
2910 release_prop= fb_mdetv, &
2911 fs_p_prop = fs_p_prop)
2913 WRITE (msg,'(2(i8,1x))') active_br, fs_gen_idmax
2914 CALL wrf_debug (wrfdbg, 'SPFire_driver mpi recv AFTER : ii, fs_gen_idmax >>> '// msg)
2916 fs_p_mass (prior_br:active_br) = r_p_m
2917 fs_p_diam (prior_br:active_br) = r_p_d
2918 fs_p_effd(prior_br:active_br) = r_p_e
2919 fs_p_temp (prior_br:active_br) = r_p_t
2920 fs_p_tvel (prior_br:active_br) = r_p_v
2922 ! DO kk=prior_br,prior_br+MIN(nbr_sum,5)
2923 ! 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)
2924 ! CALL wrf_debug (wrfdbg, 'SPFire_mpi recv AFTER2 >>> '// msg)
2927 DEALLOCATE(r_x, r_y, r_z, r_id, r_src, r_dt)
2928 !DEALLOCATE(release_mpi_ijk)
2929 DEALLOCATE(fb_mdetv)
2930 DEALLOCATE(r_p_m, r_p_d, r_p_e, r_p_t, r_p_v)
2933 ! CALL wrf_debug (wrfdbg, 'SPFire_driver mpi nothing to receive')
2936 !-------------------------------------------------------------------------------
2938 !-------------------------------------------------------------------------------
2940 !===============================================================================
2941 ! Check history interval
2942 !===============================================================================
2947 END SUBROUTINE firebrand_spotting_em_driver
2948 !=============================================================
2950 !=============================================================
2954 !=============================================================
2955 !=============================================================
2957 !******************************************************************
2958 !******************************************************************
2960 !* Particle Transport & Physics *
2962 !******************************************************************
2963 !******************************************************************
2965 !=============================================================
2967 SUBROUTINE advect_xyz_m(grid, xp, yp, hgt, dtp, idp, &
2968 u, v, w, dt, mf, z_at_w, znw, ims, jms, kms, &
2970 xout, yout, zout, fs_p_prop, land_hgt, &
2971 start_mom3d_dt, msg)
2972 !=============================================================
2974 ! Transport a particle at a given xp, yp, zp position in meters
2975 ! given the corresponding velocities [m/s] at the enclosing grid points
2978 ! xp, yp, hgt: starting particle position
2979 ! u, v, w: wind components
2980 ! dt: time interval in sec
2981 ! ims, jms : start index of wind components
2982 ! mf: constant to convert from grid position to meters: rdx*msft(x,y)
2983 ! z_at_w: wrf height at vertical interface
2986 !-------------------------------------------------------------
2990 TYPE(domain), INTENT(IN) :: grid
2991 INTEGER, INTENT(IN) :: ims, jms, kms, start_mom3d_dt
2992 REAL, INTENT(IN) :: dt
2993 REAL, INTENT(IN) :: land_hgt
2994 REAL, INTENT(IN), DIMENSION(:) :: xp, yp, hgt ! particle position
2995 INTEGER, INTENT(IN), DIMENSION(:) :: dtp, idp ! particle life-time and ID
2996 REAL, INTENT(IN), DIMENSION(ims:, kms:, jms:) :: u, v, w, z_at_w
2997 REAL, INTENT(IN), DIMENSION(ims:, kms:, jms:) :: phyd, thet, rho
2998 REAL, INTENT(IN), DIMENSION(:) :: znw
2999 REAL, INTENT(IN), DIMENSION(ims:, jms:) :: mf ! map factor, msft * rdx
3000 REAL, INTENT(OUT), DIMENSION(:) :: xout, yout, zout!, aglh
3001 TYPE(p_properties),INTENT(INOUT),DIMENSION(:):: fs_p_prop
3003 REAL, DIMENSION(3) :: uvw2p
3005 INTEGER :: hsz, ihs, jhs, ihe, jhe ! tile +- halo array bounds
3006 REAL :: xp_m0, yp_m0, zp_m0, zp0
3007 REAL :: xp_m1, yp_m1, zp_m1, xp1, yp1, zp1
3008 REAL :: xp_m2, yp_m2, zp_m2, xp2, yp2, zp2
3010 REAL :: loc_p, loc_t, loc_d ! local met properties
3011 CHARACTER (LEN=200), INTENT(OUT) :: msg
3013 WRITE(msg, '(a100)') ' '
3014 !-------------------------------------------------------------------------------
3016 hsz = 4 ! halo size - get this value from grid%. Halo:48 = 4 points. How about staggering?
3017 ihs = is - hsz ! MAX(, ids)
3018 jhs = js - hsz ! MAX(, jds)
3019 ihe = ie -1 + hsz ! MIN(, ide-1) ! check for ide -1 bc we use i, i+1 to interpolate
3020 jhe = je -1 + hsz ! MIN(, jde-1)
3022 ! WRITE (msg,'(4(i4,1x))') ihs,ihe, jhs, jhe
3023 ! CALL wrf_debug (wrfdbg, 'SPFire_advect halo bounds ihs, ihe, jhs, jhe: >>> '//msg)
3025 !start_mom3d_dt = 4 ! min lifetime before calculating fb physics (number of timesteps for this nested grid)
3029 ! CALL wrf_debug (wrfdbg, 'SPFire advect_xyz_m ---------------------------------------------------- ')
3031 ! WRITE (msg,'(3(i8,1x),3(f12.6,1x))') pp, idp(pp), dtp(pp), xp(pp), yp(pp), hgt(pp)
3032 ! CALL wrf_debug (wrfdbg, 'SPFire BEFORE advect: pp,id,dt,xp,yp,zp: >>> '//msg)
3034 !-------------------------------------------------------------------------------
3035 ! Convert horizontal positions to meters
3036 !-------------------------------------------------------------------------------
3038 xp_m0 = xp(pp) / mf(FLOOR(xp(pp)),FLOOR(yp(pp)))
3039 yp_m0 = yp(pp) / mf(FLOOR(xp(pp)),FLOOR(yp(pp)))
3042 ! zp changes slightly between wrf time steps, so it need to be calculated again
3043 zp0 = hgt2k(xp=xp(pp), yp=yp(pp), hgt=zp_m0, z_at_w=z_at_w, znw=znw)
3044 ! (Need fractional position for interpolation)
3046 ! WRITE (msg,'(3(i8,1x),4(f12.6,1x))') pp, idp(pp), dtp(pp), xp(pp), yp(pp), hgt(pp), zp0
3047 ! CALL wrf_debug (wrfdbg, 'SPFire_advect BEFORE: pp,id,dt,xp,yp,zp,zk: >>> '//msg)
3049 ! If this is a particle deposited by an adjacent tile, do not transport it
3050 IF (zp_m0 <= land_hgt) THEN
3051 ! CALL wrf_debug (wrfdbg, 'SPFire_advect particle at deposit threshold - will not advect')
3060 !-------------------------------------------------------------------------------
3061 !-------------------------------------------------------------------------------
3065 !=============================================================
3068 !-------------------------------------------------------------------------------
3069 ! #1 interpolate velocities to particle position & advect particle
3070 !-------------------------------------------------------------------------------
3072 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
3074 xp_m1 = uvw2p(1)*DT + xp_m0
3075 yp_m1 = uvw2p(2)*DT + yp_m0
3076 zp_m1 = uvw2p(3)*DT + zp_m0
3078 !-------------------------------------------------------------------------------
3079 ! Convert horizontal positions from meters
3080 !-------------------------------------------------------------------------------
3082 xp1 = xp_m1 * mf(FLOOR(xp(pp)),FLOOR(yp(pp))) ! 2 pass will account for grid pt changes in mf & u
3083 yp1 = yp_m1 * mf(FLOOR(xp(pp)),FLOOR(yp(pp)))
3085 IF((ABS(xp1-xp(pp)) > 2.0) .OR. (ABS(yp1-yp(pp)) > 2.0)) THEN
3086 WRITE (msg,'(2(i4,1x),7(f10.6,1x))') pp, dtp(pp), xp(pp), yp(pp), zp0, &
3087 uvw2p(1), uvw2p(2), uvw2p(3), dt !
3094 !-------------------------------------------------------------------------------
3095 ! Convert z position from meters to k-fractional
3096 !-------------------------------------------------------------------------------
3098 ! CALL wrf_debug (wrfdbg, 'SPFire advect_xyz_m 1st pass --------------------------------------------- ')
3100 ! WRITE (msg,'(3(i8,1x),3(f12.6,1x))') pp, idp(pp), dtp(pp), xp1, yp1, zp_m1
3101 ! CALL wrf_debug (wrfdbg, 'SPFire after 1-pass adv: pp,id,dt,xp,yp,zp: >>> '//msg)
3102 ! WRITE (msg,*) pp, z_at_w(FLOOR(xp1),1,FLOOR(yp1))
3103 ! CALL wrf_debug (wrfdbg, 'SPFire after 1-pass adv: zn: >>> '//msg)
3104 ! WRITE (msg,*) pp, z_at_w(FLOOR(xp1-dp05),1,FLOOR(yp1-dp05))
3105 ! CALL wrf_debug (wrfdbg, 'SPFire after 1-pass adv: zn: >>> '//msg)
3107 zp1 = hgt2k(xp=xp1, yp=yp1, hgt=zp_m1, z_at_w=z_at_w, znw=znw)
3110 !-------------------------------------------------------------------------------
3111 ! Is the new position not on the same memory?
3112 !-------------------------------------------------------------------------------
3113 ! check for floor(x)+1 bc we use i and i+1 to interpolate
3114 IF (FLOOR(xp1-dp05) < ihs .OR. FLOOR(xp1-dp05) +1 > ihe .OR. &
3115 FLOOR(yp1-dp05) < jhs .OR. FLOOR(yp1-dp05) +1 > jhe) THEN
3116 ! Checking staggered positions relative to unstaggered variables (+- dp05)
3118 !-------------------------------------------------------------------------------
3120 !-------------------------------------------------------------------------------
3130 !=============================================================
3132 !-------------------------------------------------------------------------------
3133 ! #2 interpolate velocities to particle position & advect particle
3134 !-------------------------------------------------------------------------------
3136 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
3138 ! WRITE (msg,'(3(f14.9,1x))') uvw2p(1), uvw2p(2), uvw2p(3)
3139 ! CALL wrf_debug (wrfdbg, 'SPFire_advect effective vel m/s: u,v,w 2nd pass>>> '//TRIM(msg))
3141 xp_m2 = uvw2p(1)*DT + xp_m0
3142 yp_m2 = uvw2p(2)*DT + yp_m0
3143 zp_m2 = uvw2p(3)*DT + zp_m0
3145 xp2 = xp_m2 * mf(FLOOR(xp1),FLOOR(yp1))
3146 yp2 = yp_m2 * mf(FLOOR(xp1),FLOOR(yp1))
3148 IF((ABS(xp2-xp1) > 2.0) .OR. (ABS(yp2-yp1) > 2.0)) THEN
3149 WRITE (msg,'(2(i4,1x),7(f10.6,1x))') pp, dtp(pp), xp1, yp1, zp1, &
3150 uvw2p(1), uvw2p(2), uvw2p(3), dt !
3158 !-------------------------------------------------------------------------------
3159 ! Is the new position not on the same memory?
3160 !-------------------------------------------------------------------------------
3162 ! check against tile +- halo points instead
3164 IF (FLOOR(xp2-dp05) < ihs .OR. FLOOR(xp2-dp05) +1 > ihe .OR. &
3165 FLOOR(yp2-dp05) < jhs .OR. FLOOR(yp2-dp05) +1 > jhe) THEN
3166 ! Checking staggered positions relative to unstaggered variables (+- dp05)
3168 !-------------------------------------------------------------------------------
3169 ! Use xp1, yp1 to find zp2 (z_at_w will be out of bounds)
3170 !-------------------------------------------------------------------------------
3172 zp2 = hgt2k(xp=xp1, yp=yp1, hgt=zp_m2, z_at_w=z_at_w, znw=znw)
3176 zp2 = hgt2k(xp=xp2, yp=yp2, hgt=zp_m2, z_at_w=z_at_w, znw=znw)
3180 !=============================================================
3182 !-------------------------------------------------------------------------------
3183 ! Average both passes
3184 !-------------------------------------------------------------------------------
3186 xp2 = (xp1 + xp2) * dp05
3187 yp2 = (yp1 + yp2) * dp05
3188 zp_m2 = (zp_m1 + zp_m2) * dp05
3189 zp2 = (zp1 + zp2) * dp05
3195 wp = (uvw2p(3) + wp) * dp05
3199 !=============================================================
3201 !=============================================================
3202 ! WRITE (msg, '(a100)') ' '
3203 ! WRITE (msg,'(2(i6,1x),4(f12.6,1x))') pp, dtp(pp), xout(pp), yout(pp), zout(pp), zp
3204 ! CALL wrf_debug (wrfdbg, 'SPFire_advect AFTER : pp,id,dt,xp,yp,zp,kp: >>> '//msg)
3206 !=============================================================
3208 !=============================================================
3214 IF (zout(pp) <= ZERO_dp) THEN
3215 fs_p_prop(pp) = p_properties(ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP)
3217 CYCLE ! Skip physics
3220 IF (IEEE_IS_NAN(zout(pp)) .OR. ABS(zout(pp)) > HUGE(1.0)) THEN
3221 fs_p_prop(pp) = p_properties(ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP)
3226 CYCLE ! Skip physics
3229 IF (dtp(pp) < start_mom3d_dt) THEN ! Temporarily testing transport: extending particle lifetime
3231 ! CALL wrf_debug (wrfdbg, 'SPFire_physics cycle dt: '//msg)
3236 CALL get_local_met( xp = xout(pp), &
3243 p = phyd, & ! air pressure (Pa)
3244 t = thet, & ! temperature (K)
3245 d = rho, & ! density (kg/m3)
3246 loc_p = loc_p, & ! local air pressure 3d (Pa)
3247 loc_d = loc_d, & ! local density (g/m3)
3248 loc_t = loc_t) ! local temperature (K)
3250 ! WRITE (msg,'(3(f16.8,1x))') grid%p_hyd_w(FLOOR(xout(pp)),FLOOR(zp),FLOOR(yout(pp))), &
3251 ! grid%rho(FLOOR(xout(pp)),FLOOR(zp),FLOOR(yout(pp))), &
3252 ! grid%t_phy(FLOOR(xout(pp)),FLOOR(zp),FLOOR(yout(pp)))
3253 ! CALL wrf_debug (wrfdbg, 'SPFire grid%p, %rho, %t_phy : '//msg)
3255 ! WRITE (msg,'(4(f16.8,1x))') loc_p, loc_d, loc_t, wp
3256 ! CALL wrf_debug (wrfdbg, 'SPFire br met p, d, t, w : '//msg)
3258 !-------------------------------------------------------------------------------
3259 ! Firebrand Burnout and Terminal Velocity
3260 !-------------------------------------------------------------------------------
3263 CALL firebrand_physics(dt = dt, &
3269 fbprop = fs_p_prop(pp))
3271 IF (IEEE_IS_NAN(fs_p_prop(pp)%p_tvel)) THEN
3272 fs_p_prop(pp) = p_properties(ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP, ZERO_DP)
3275 ! 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
3276 ! CALL wrf_debug (wrfdbg, 'SPFire_physics AFTER: pp,id,dt,xp,yp,zp,fbvt: >>> '//msg)
3283 ! 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
3284 ! CALL wrf_debug (wrfdbg, 'SPFire_physics AFTER: pp,id,dt,xp,yp,zp,fbvt: >>> '//msg)
3288 END SUBROUTINE advect_xyz_m
3289 !=============================================================
3292 ! Cannot be elemental: array
3293 !=============================================================
3294 PURE FUNCTION uvw_3d_interp(x, y, z, u, v, w, ihs, jhs, ihe, jhe)
3295 !=============================================================
3297 ! Wrapper to interpolate U,V,W to a point within a 3D grid box
3299 ! Interpolate velocities at grid point interfaces to particle position
3300 ! input range needed: i:i+1 | i < x(i) <= i+1
3301 ! j:j+1 | j < y(j) <= j+1
3302 ! k:k+1 | k < z(k) <= k+1
3305 ! x, y, z: fractional position to interpolate velocities (e.g.: z is a fraction of the k index)
3306 ! u, v, w: wind components
3307 ! is, js : start index of wind components
3309 !-------------------------------------------------------------
3310 ! Interpolate between planes: U: xy(k), xy(k+1) stag x, dim=1
3311 ! V: xy(k), xy(k+1) stag y, dim=2
3312 ! W: xz(j), xz(j+1) stag z, dim=2
3314 ! particle position is not staggered:
3315 ! grid center is given by (FLOOR(x), FLOOR(y))
3316 !-------------------------------------------------------------
3320 INTEGER, INTENT(IN) :: ihs, jhs, ihe, jhe ! tile +- halo array bounds
3321 REAL, INTENT(IN) :: x, y, z ! fractional positions relative to integral array indices
3322 REAL, INTENT(IN), DIMENSION(ims:, 1:, jms:) :: u, v ,w
3323 REAL, DIMENSION(3) :: uvw_3d_interp
3324 REAL :: tru, trv, trw, x0, y0, z0
3325 INTEGER :: i, j, k, i0, j0, k0
3327 ! Unstaggerecd indices
3328 k = MAX(FLOOR(z), 1)
3332 ! Variable Staggering:
3333 ! particle position is equivalent to a staggered dimension
3334 ! the staggered domain begins 1/2 pnt before and ends 1/2 pnt after the regular domain
3335 ! e.g: interpolate u (stag x): x_stag = xp, y_unst = yp -0.5, z_unst = zp -0.5
3336 ! By shifting the particle position by 1/2 point,
3337 ! the unstaggered velocities will represent the grid edge instead of grid center
3341 z0 = z - dp05 ! z can be < 1.0 when below the first half level, u_3d_interp will extrapolate values
3343 i0 = FLOOR(x0) !MIN(MAX(ihs, ), ihe) ! shift indices by 0.5 because unstaggered variables given at horizontal grid center
3344 j0 = FLOOR(y0) !MIN(MAX(jhs, ), ihe)
3345 k0 = MAX(1, FLOOR(z0))
3347 !-----------------------------------------------------------------------------
3348 ! u,v interpolates between xy planes: xy(k) xy(k+1)
3349 !-----------------------------------------------------------------------------
3352 tru= u_3d_interp( x=x, y=y0, z=z0, &
3353 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), &
3354 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))
3357 trv= u_3d_interp( x=x0, y=y, z=z0, &
3358 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), &
3359 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))
3361 !-----------------------------------------------------------------------------
3362 ! w interpolates between xz planes, not xy (so y corresponds to z): xz(j) xz(j+1)
3363 !-----------------------------------------------------------------------------
3365 ! z and w are both staggered (z is derived from grid%z_at_w)
3366 trw= u_3d_interp( x=x0, y=z, z=y, &
3367 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 ), &
3368 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))
3370 !-----------------------------------------------------------------------------
3371 ! Return U, V, W at x, y, z positions
3372 !-----------------------------------------------------------------------------
3374 uvw_3d_interp = [tru, trv, trw]
3376 END FUNCTION uvw_3d_interp
3377 !=============================================================
3381 !=============================================================
3382 ELEMENTAL FUNCTION u_3d_interp(x, y, z, &
3383 u_i0j0_bot, u_i0j1_bot, u_i1j0_bot, u_i1j1_bot, &
3384 u_i0j0_top, u_i0j1_top, u_i1j0_top, u_i1j1_top )
3385 !=============================================================
3387 ! Interpolate velocities at grid point interfaces to particle position
3388 ! input range needed: u( i: i+1, j: j+1, k: k+1) |
3394 ! x, y, z: particle position to interpolate velocities
3395 ! u, v, w: wind components
3397 !-----------------------------------------------------------------------------
3398 ! Bilinear interpolation :
3399 ! The product of the value at the desired point (x,y,z) and the area enclosed by the corners
3400 ! is equal to the sum of the
3401 ! products of the value at each corner and the partial area diagonally opposite the corner
3402 !-----------------------------------------------------------------------------
3404 !-----------------------------------------------------------------------------
3405 ! The 3-D interpolation is achieved with a linear interpolation between
3406 ! the xy-interpolated points from two adjacent planes
3407 !-----------------------------------------------------------------------------
3409 !-----------------------------------------------------------------------------
3410 ! Z must be offset by 0.5 to correctly interpolate between vertically unstaggered planes
3411 !-----------------------------------------------------------------------------
3413 REAL, INTENT(IN) :: x, y, z ! fractional position
3414 REAL, INTENT(IN) :: u_i0j0_bot, u_i0j1_bot, u_i1j0_bot, u_i1j1_bot ! u_lower
3415 REAL, INTENT(IN) :: u_i0j0_top, u_i0j1_top, u_i1j0_top, u_i1j1_top ! u_upper
3418 REAL :: dbot, dtop, u_lower, u_upper
3421 !-----------------------------------------------------------------------------
3422 ! Get weighted U, V, W at particle position based on surrounding xy and xz planes
3423 !-----------------------------------------------------------------------------
3426 k = MAX(1, FLOOR(z)) ! To interpolate w, z corresponds y: ( x=x, y=z, z=y, ...
3428 ! z can be < 1.0 when interpolating below the first level on half levels
3429 ! e.g.: z=0.75, dbot = 0.75 -1 =-0.25, dtop = 2 - 0.75 =1.25
3430 ! u_3d_interp = u_upper*-0.25 + u_lower*1.25
3433 dtop = REAL(k+1) - z
3435 !-----------------------------------------------------------------------------
3439 u_lower = u_2d_interp( u_i0j0=u_i0j0_bot, u_i0j1=u_i0j1_bot, &
3440 u_i1j0=u_i1j0_bot, u_i1j1=u_i1j1_bot, xp=x, yp=y)
3441 u_upper = u_2d_interp( u_i0j0=u_i0j0_top, u_i0j1=u_i0j1_top, &
3442 u_i1j0=u_i1j0_top, u_i1j1=u_i1j1_top, xp=x, yp=y)
3444 !-----------------------------------------------------------------------------
3445 ! Apply weights and calculate U at particle position
3446 !-----------------------------------------------------------------------------
3448 u_3d_interp = u_upper * dbot + & !
3451 !-----------------------------------------------------------------------------
3453 END FUNCTION u_3d_interp
3454 !=============================================================
3456 !=============================================================
3460 !=============================================================
3461 ELEMENTAL FUNCTION u_2d_interp(u_i0j0, u_i0j1, u_i1j0, u_i1j1, xp, yp)
3462 !=============================================================
3464 ! Interpolate any variable at grid point interfaces to a position in between
3465 ! Minimum range: u(i:i+1, j:j+1) | i < xp <= i+1 & j < yp <= j+1
3468 ! u : 2D variable at grid 4 corners: (i0,j0); (i0,j1); (i1,j0); (i1,j1)
3469 ! xp, yp : position within grid box to interpolate velocity
3470 ! is, js : start index of variable array boundary
3472 !-------------------------------------------------------------
3473 ! 2D Interpolation sketch
3474 ! a, b, c, d: grid point corners
3475 ! da, db, dc, dd: distance between the nearest corner and point projection
3483 !-------------------------------------------------------------
3487 !INTEGER, INTENT(IN) :: is, js
3488 REAL, INTENT(IN) :: xp, yp
3489 REAL, INTENT(IN) :: u_i0j1, u_i0j0, u_i1j0, u_i1j1 ! DIMENSION(is:,js:) :: uu
3491 REAL :: d_a, d_b, d_c, d_d ! grid vertices
3492 REAL :: uu_a, uu_b, uu_c, uu_d ! advection at vertices
3498 uu_a = u_i0j1 ! uu(i ,j+1)
3499 uu_b = u_i0j0 ! uu(i ,j )
3500 uu_c = u_i1j0 ! uu(i+1,j )
3501 uu_d = u_i1j1 ! uu(i+1,j+1)
3503 ! WRITE (msg,'(4(f14.9,1x))') uu_a, uu_b, uu_c, uu_d
3504 ! CALL wrf_debug (wrfdbg, 'SPFire_uu_2d: uu _a,_b,_c,_d >>> '//TRIM(msg))
3506 d_a = ABS( ( REAL(i+1) - xp ) * ( REAL(j ) - yp ) )
3507 d_b = ABS( ( REAL(i+1) - xp ) * ( REAL(j+1) - yp ) )
3508 d_c = ABS( ( REAL(i ) - xp ) * ( REAL(j+1) - yp ) )
3509 d_d = ABS( ( REAL(i ) - xp ) * ( REAL(j ) - yp ) )
3511 ! WRITE (msg,'(4(f14.9,1x))') d_a, d_b, d_c, d_d
3512 ! CALL wrf_debug (wrfdbg, 'SPFire_uu_2d: d _a,_b,_c,_d >>> '//TRIM(msg))
3515 u_2d_interp = ( uu_a * d_a + &
3519 (d_a + d_b + d_c + d_d)
3521 ! WRITE (msg,*) uu_2d_interp
3522 ! CALL wrf_debug (wrfdbg, 'SPFire_uu_2d: >>> '//TRIM(msg))
3524 END FUNCTION u_2d_interp
3525 !=============================================================
3527 !=============================================================
3532 !=============================================================
3533 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)
3534 !=============================================================
3536 INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id
3537 INTEGER, INTENT(IN), DIMENSION(:) :: fs_p_dt
3538 !INTEGER, INTENT(IN) :: active_br
3539 REAL, INTENT(IN), DIMENSION(:) :: fs_p_x, fs_p_y, fs_p_z, fs_p_effd
3540 LOGICAL, DIMENSION(fs_array_maxsize) :: sparse_mask
3541 INTEGER, INTENT(OUT) :: cnt
3543 INTEGER, INTENT(IN) :: max_life_dt
3544 REAL, INTENT(IN) :: land_hgt
3546 !-------------------------------------------------------------------------------
3548 sparse_mask = .FALSE.
3549 !-------------------------------------------------------------------------------
3550 ! Flag to remove brands beyond domain bounds
3551 !-------------------------------------------------------------------------------
3553 sparse_mask = ( fs_p_id > 0 .AND. &
3554 (FLOOR(fs_p_x-dp05) < ids .OR. FLOOR(fs_p_y-dp05) < jds .OR. &
3555 FLOOR(fs_p_x-dp05)+1 > ide .OR. FLOOR(fs_p_y-dp05)+1 > jde))
3556 ! interpolation needs i, j, i+1, j+1
3557 ! 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)
3558 ! Then we use FLOOR(x), FLOOR(y), FLOOR(x)+1, FLOOR(y)+1 to interpolate
3560 !-------------------------------------------------------------------------------
3561 ! Flag to remove brands living longer than firebrand_max_life_dt
3562 !-------------------------------------------------------------------------------
3564 sparse_mask = ( sparse_mask .OR. &
3565 (fs_p_id > 0 .AND. fs_p_dt > max_life_dt))
3567 !-------------------------------------------------------------------------------
3568 ! Flag to remove brands with near zero mass
3569 !-------------------------------------------------------------------------------
3571 ! sparse_mask = (sparse_mask .OR. &
3572 ! (fs_p_id > 0 .AND. fs_p_mass <= br_min_mass))
3574 !-------------------------------------------------------------------------------
3575 ! Flag to remove brands with near zero diameter that are not at deposit height
3576 !-------------------------------------------------------------------------------
3578 sparse_mask = (sparse_mask .OR. &
3579 (fs_p_id > 0 .AND. ((fs_p_effd <= TINY(1.0) .AND. (fs_p_z > land_hgt)))))
3581 !-------------------------------------------------------------------------------
3583 !-------------------------------------------------------------------------------
3585 cnt = COUNT(sparse_mask)
3588 WHERE(sparse_mask) fs_p_id = 0
3590 ! WRITE (msg,'(3(i8,1x))') COUNT(sparse_mask), COUNT( fs_p_id > 0 )
3591 ! CALL wrf_debug (wrfdbg, 'SPFire_remove removed, remaining: '//msg)
3592 !CALL wrf_error_fatal( "end of test")
3595 END SUBROUTINE remove_br
3597 !=============================================================
3598 !=============================================================
3601 !=============================================================
3602 PURE SUBROUTINE deposit_br(fs_p_id, fs_p_x, fs_p_y, fs_p_z, sum_fbrand, land_hgt)
3603 !=============================================================
3606 INTEGER, INTENT(INOUT), DIMENSION(:) :: fs_p_id
3607 REAL, INTENT(INOUT), DIMENSION(ims:ime,jms:jme) :: sum_fbrand
3608 REAL, INTENT(IN), DIMENSION(:) :: fs_p_x, fs_p_y, fs_p_z
3609 REAL, INTENT(IN) :: land_hgt
3611 !INTEGER, INTENT(IN) :: active_br
3613 !INTEGER, ALLOCATABLE, DIMENSION(:) :: mask_idx
3614 INTEGER, ALLOCATABLE, DIMENSION(:) :: rx, ry, rid
3615 LOGICAL, DIMENSION(fs_array_maxsize) :: sparse_mask
3616 LOGICAL, DIMENSION(fs_array_maxsize) :: bounds_mask
3617 !LOGICAL, ALLOCATABLE, DIMENSION(:) :: bounds_mask
3619 INTEGER :: x, y, i0,k ! counters
3620 !CHARACTER(LEN=10) fmt
3622 !CALL wrf_debug (wrfdbg, 'SPFire_deposit: check')
3623 sum_fbrand(:,:) = ZERO_dp
3625 !-------------------------------------------------------------------------------
3626 ! Flag brands near ground (deps_lev is a declared parameter)
3627 !-------------------------------------------------------------------------------
3629 sparse_mask = .FALSE.
3630 bounds_mask = .FALSE.
3632 ! Only deposit particles on this tile
3633 bounds_mask = ((FLOOR(fs_p_x) >= is) .OR. &
3634 (FLOOR(fs_p_x) <= ie) .OR. &
3635 (FLOOR(fs_p_y) >= js) .OR. &
3636 (FLOOR(fs_p_y) <= je))
3638 sparse_mask = ( bounds_mask .AND. (fs_p_id > 0 .AND. fs_p_z <= land_hgt))
3640 !-------------------------------------------------------------------------------
3641 ! Deposit flagged brands
3642 !-------------------------------------------------------------------------------
3644 nresets = COUNT(sparse_mask)
3646 ! WRITE (msg,'(i6)') nresets
3647 ! CALL wrf_debug (wrfdbg, 'SPFire_deposit total: '//msg)
3649 IF (nresets > 0) THEN
3651 !-------------------------------------------------------------------------------
3652 ! convert position to integer indices
3653 !-------------------------------------------------------------------------------
3655 ALLOCATE(rx(nresets),ry(nresets))!, mask_idx(nresets), rid(nresets), bounds_mask(nresets))
3657 ! Get indices of particles when sparse_mask = True
3661 rx(1:nresets) = FLOOR(PACK(fs_p_x, sparse_mask))
3662 ry(1:nresets) = FLOOR(PACK(fs_p_y, sparse_mask))
3664 !-------------------------------------------------------------------------------
3665 ! Increment sum_fbrand
3666 !-------------------------------------------------------------------------------
3669 sum_fbrand(rx(k),ry(k)) = sum_fbrand(rx(k),ry(k)) + 1.0_dp
3672 !-------------------------------------------------------------------------------
3674 !-------------------------------------------------------------------------------
3676 WHERE(sparse_mask) fs_p_id= 0
3678 ! WRITE (msg,'(5(i8,1x))') COUNT(sparse_mask), COUNT( fs_p_id > 0 ), COUNT(rx >= ie), COUNT(ry >= je)
3679 ! CALL wrf_debug (wrfdbg, 'SPFire_deposit deposited, remaining, outofbounds: '//msg)
3683 END SUBROUTINE deposit_br
3686 !=============================================================
3687 !=============================================================
3691 !------------------------------------------------------------------------------
3693 !------------------------------------------------------------------------------
3696 !=============================================================
3698 SUBROUTINE get_local_ijk(grid, &
3699 ims, ime, jms, jme, kms, kme, &
3700 ips, ipe, jps, jpe, kps, kpe, &
3701 ifps, ifpe, jfps, jfpe, &
3702 ifms, ifme, jfms, jfme, &
3703 ids, ide, jds, jde, kds, kde, &
3704 m_idim, m_jdim, m_kdim, &
3705 p_idim, p_jdim, p_kdim, &
3706 d_idim, d_jdim, d_kdim)
3707 !p2m_is, p2m_ie, p2m_js, p2m_je)
3709 USE module_domain, ONLY: get_ijk_from_grid, get_ijk_from_subgrid
3713 TYPE(DOMAIN), INTENT(IN) :: grid
3714 INTEGER, INTENT(OUT), OPTIONAL :: ims, ime, jms, jme, kms, kme
3715 INTEGER, INTENT(OUT), OPTIONAL :: ips, ipe, jps, jpe, kps, kpe
3716 INTEGER, INTENT(OUT), OPTIONAL :: ifps, ifpe, jfps, jfpe
3717 INTEGER, INTENT(OUT), OPTIONAL :: ifms, ifme, jfms, jfme
3718 INTEGER, INTENT(OUT), OPTIONAL :: ids, ide, jds, jde, kds, kde
3719 INTEGER, INTENT(OUT), OPTIONAL :: m_idim, m_jdim, m_kdim
3720 INTEGER, INTENT(OUT), OPTIONAL :: p_idim, p_jdim, p_kdim
3721 INTEGER, INTENT(OUT), OPTIONAL :: d_idim, d_jdim, d_kdim
3722 !INTEGER, INTENT(OUT), OPTIONAL :: p2m_is, p2m_ie, p2m_js, p2m_je
3725 INTEGER :: iims, iime, jjms, jjme, kkms, kkme
3726 INTEGER :: iips, iipe, jjps, jjpe, kkps, kkpe
3727 INTEGER :: iids, iide, jjds, jjde, kkds, kkde
3729 INTEGER :: iifps, iifpe, jjfps, jjfpe, kkfps, kkfpe
3730 INTEGER :: iifms, iifme, jjfms, jjfme, kkfms, kkfme
3731 INTEGER :: iifds, iifde, jjfds, jjfde, kkfds, kkfde
3733 IF ((.NOT. PRESENT(ims)) .AND. &
3734 (.NOT. PRESENT(jms)) .AND. &
3735 (.NOT. PRESENT(kms)) .AND. &
3736 (.NOT. PRESENT(ime)) .AND. &
3737 (.NOT. PRESENT(jme)) .AND. &
3738 (.NOT. PRESENT(kme)) .AND. &
3740 (.NOT. PRESENT(ips)) .AND. &
3741 (.NOT. PRESENT(jps)) .AND. &
3742 (.NOT. PRESENT(kps)) .AND. &
3743 (.NOT. PRESENT(ipe)) .AND. &
3744 (.NOT. PRESENT(jpe)) .AND. &
3745 (.NOT. PRESENT(kpe)) .AND. &
3747 (.NOT. PRESENT(ifps)) .AND. &
3748 (.NOT. PRESENT(jfps)) .AND. &
3749 (.NOT. PRESENT(ifpe)) .AND. &
3750 (.NOT. PRESENT(jfpe)) .AND. &
3752 (.NOT. PRESENT(ifms)) .AND. &
3753 (.NOT. PRESENT(jfms)) .AND. &
3754 (.NOT. PRESENT(ifme)) .AND. &
3755 (.NOT. PRESENT(jfme)) .AND. &
3757 (.NOT. PRESENT(ids)) .AND. &
3758 (.NOT. PRESENT(jds)) .AND. &
3759 (.NOT. PRESENT(kds)) .AND. &
3760 (.NOT. PRESENT(ide)) .AND. &
3761 (.NOT. PRESENT(jde)) .AND. &
3762 (.NOT. PRESENT(kde)) .AND. &
3764 ! (.NOT. PRESENT(p2m_is)) .AND. &
3765 ! (.NOT. PRESENT(p2m_ie)) .AND. &
3766 ! (.NOT. PRESENT(p2m_js)) .AND. &
3767 ! (.NOT. PRESENT(p2m_je)) .AND. &
3769 (.NOT. PRESENT(m_idim)) .AND. &
3770 (.NOT. PRESENT(m_jdim)) .AND. &
3771 (.NOT. PRESENT(m_kdim)) .AND. &
3772 (.NOT. PRESENT(p_idim)) .AND. &
3773 (.NOT. PRESENT(p_jdim)) .AND. &
3774 (.NOT. PRESENT(p_kdim)) .AND. &
3775 (.NOT. PRESENT(d_idim)) .AND. &
3776 (.NOT. PRESENT(d_jdim)) .AND. &
3777 (.NOT. PRESENT(d_kdim))) &
3779 CALL wrf_debug ( 1, 'get_local_ijk function is NOT requesting a result' )
3781 CALL get_ijk_from_grid ( grid , &
3782 iids, iide, jjds, jjde, kkds, kkde, &
3783 iims, iime, jjms, jjme, kkms, kkme, &
3784 iips, iipe, jjps, jjpe, kkps, kkpe)
3786 IF (PRESENT(ifps) .OR. &
3787 PRESENT(jfps) .OR. &
3788 PRESENT(ifpe) .OR. &
3789 PRESENT(jfpe) .OR. &
3790 PRESENT(ifms) .OR. &
3791 PRESENT(jfms) .OR. &
3792 PRESENT(ifme) .OR. &
3793 PRESENT(jfme)) CALL get_ijk_from_subgrid(grid , &
3794 iifds, iifde, jjfds, jjfde, kkfds, kkfde, &
3795 iifms, iifme, jjfms, jjfme, kkfms, kkfme, &
3796 iifps, iifpe, jjfps, jjfpe, kkfps, kkfpe)
3799 IF (PRESENT(ims)) ims = iims
3800 IF (PRESENT(jms)) jms = jjms
3801 IF (PRESENT(kms)) kms = kkms
3802 IF (PRESENT(ime)) ime = iime
3803 IF (PRESENT(jme)) jme = jjme
3804 IF (PRESENT(kme)) kme = kkme
3806 IF (PRESENT(ips)) ips = iips
3807 IF (PRESENT(jps)) jps = jjps
3808 IF (PRESENT(kps)) kps = kkps
3809 IF (PRESENT(ipe)) ipe = iipe
3810 IF (PRESENT(jpe)) jpe = jjpe
3811 IF (PRESENT(kpe)) kpe = kkpe
3813 IF (PRESENT(ifps)) ifps = iifps
3814 IF (PRESENT(jfps)) jfps = jjfps
3815 IF (PRESENT(ifpe)) ifpe = iifpe
3816 IF (PRESENT(jfpe)) jfpe = jjfpe
3818 IF (PRESENT(ifms)) ifms = iifms
3819 IF (PRESENT(jfms)) jfms = jjfms
3820 IF (PRESENT(ifme)) ifme = iifme
3821 IF (PRESENT(jfme)) jfme = jjfme
3823 IF (PRESENT(ids)) ids = iids
3824 IF (PRESENT(jds)) jds = jjds
3825 IF (PRESENT(kds)) kds = kkds
3826 IF (PRESENT(ide)) ide = iide
3827 IF (PRESENT(jde)) jde = jjde
3828 IF (PRESENT(kde)) kde = kkde
3830 IF (PRESENT(m_idim)) m_idim = iime - iims + 1
3831 IF (PRESENT(m_jdim)) m_jdim = jjme - jjms + 1
3832 IF (PRESENT(m_kdim)) m_kdim = kkme - kkms + 1
3833 IF (PRESENT(p_idim)) p_idim = iipe - iips + 1
3834 IF (PRESENT(p_jdim)) p_jdim = jjpe - jjps + 1
3835 IF (PRESENT(p_kdim)) p_kdim = kkpe - kkps + 1
3836 IF (PRESENT(d_idim)) d_idim = iide - iids + 1
3837 IF (PRESENT(d_jdim)) d_jdim = jjde - jjds + 1
3838 IF (PRESENT(d_kdim)) d_kdim = kkde - kkds + 1
3840 ! IF (PRESENT(p2m_is)) p2m_is = iips - iims
3841 ! IF (PRESENT(p2m_ie)) p2m_ie = iipe - iims
3842 ! IF (PRESENT(p2m_js)) p2m_js = jjps - jjms
3843 ! IF (PRESENT(p2m_je)) p2m_je = jjpe - jjms
3845 ! p2m_is = ips - ims
3846 ! p2m_ie = ips - ims + p_idim - 1
3847 ! p2m_js = jps - jms
3848 ! p2m_je = jps - jms + p_jdim - 1
3850 END SUBROUTINE get_local_ijk
3852 !=============================================================
3853 !=============================================================
3858 END MODULE module_firebrand_spotting