ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_FireWindExtract.H File Reference

Atmospheric wind sampled onto the fire grid. More...

#include <AMReX_MultiFab.H>
#include <AMReX_Geometry.H>
#include <cmath>
#include <ERF_FireGrid.H>
Include dependency graph for ERF_FireWindExtract.H:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

void fill_fire_wind_from_interpolation (amrex::MultiFab &fire_wind_ref, amrex::MultiFab &fire_wind_extract_z_mf, const amrex::MultiFab &xvel_mf, const amrex::MultiFab &yvel_mf, const amrex::MultiFab &z_phys_cc_mf, const amrex::MultiFab &fire_surface_z_mf, const amrex::MultiFab &fire_col_ground_mf, const FireGrid &fg, amrex::Real z_ref, int nz, const amrex::MultiFab *z_ref_cell_mf=nullptr, int wind_interp=1, const amrex::MultiFab *col_open=nullptr, const amrex::MultiFab *col_roof=nullptr, amrex::Real z_sample=0.0, amrex::Real z0_log=0.1, bool below_first_log=false)
 Extract wind from direct vertical interpolation on fire grid. More...
 
void extract_fire_wind_k0 (amrex::MultiFab &u_k0, amrex::MultiFab &v_k0, amrex::MultiFab &h_k0, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &z_phys_cc, const amrex::MultiFab *z_phys_nd, const amrex::Geometry &geom)
 The wind at the lowest cell centre of each atmospheric column, and its height above that column's ground. More...
 

Detailed Description

Atmospheric wind sampled onto the fire grid.

The atmosphere side of the fire coupling: the wind of the atmospheric columns at a height above ground (a uniform height, or one per fire cell that the fire engine supplies), interpolated linearly in height and bilinearly or nearest between the surrounding columns, with an optional two-height log-law resampling; no MOST reconstruction is involved. A fire engine may read its wind through it. What the engine does with the wind afterwards (wind adjustment factors, terrain corrections) is the engine's.

Function Documentation

◆ extract_fire_wind_k0()

void extract_fire_wind_k0 ( amrex::MultiFab &  u_k0,
amrex::MultiFab &  v_k0,
amrex::MultiFab &  h_k0,
const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  z_phys_cc,
const amrex::MultiFab *  z_phys_nd,
const amrex::Geometry &  geom 
)

The wind at the lowest cell centre of each atmospheric column, and its height above that column's ground.

For an engine that drives its fire with the k = 0 wind of the atmospheric columns rather than a wind interpolated to a height (LSFIRE does). The face velocities are averaged to the k = 0 cell centre; the height is that centre minus the column's ground, the mean of its four k = 0 nodes (the ground compute_fire_surface_height uses), or minus the domain floor without nodal heights.

Parameters
[out]u_k0,v_k0Wind [m/s] on the k = 0 slabs of the level (one per box of the level, the BoxArray of FireForcing::T_k0), with any ghost cells: those are filled with FillBoundary
[out]h_k0Height of that wind above the column's ground [m], laid out as u_k0
[in]xvel,yvelFace velocities of the level (FireForcing::xvel, yvel)
[in]z_phys_ccCell-centre height of the level [m]
[in]z_phys_ndNodal height of the level [m], or nullptr (flat floor)
[in]geomGeometry of the level
268 {
269  using namespace amrex;
270  const int k0 = geom.Domain().smallEnd(2);
271  const Real z_floor = geom.ProbLo(2);
272  const bool use_znd = (z_phys_nd != nullptr);
273  for (MFIter mfi(u_k0); mfi.isValid(); ++mfi) {
274  const Box bx = mfi.validbox();
275  const auto u = xvel.const_array(mfi);
276  const auto v = yvel.const_array(mfi);
277  const auto zcc = z_phys_cc.const_array(mfi);
278  const Array4<Real const> znd = use_znd ? z_phys_nd->const_array(mfi) : Array4<Real const>{};
279  const auto us = u_k0.array(mfi);
280  const auto vs = v_k0.array(mfi);
281  const auto hs = h_k0.array(mfi);
282  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int) noexcept
283  {
284  us(i,j,0) = Real(0.5) * (u(i,j,k0) + u(i+1,j,k0));
285  vs(i,j,0) = Real(0.5) * (v(i,j,k0) + v(i,j+1,k0));
286  const Real zsfc = use_znd ? Real(0.25) * (znd(i,j,k0) + znd(i+1,j,k0) + znd(i,j+1,k0) + znd(i+1,j+1,k0))
287  : z_floor;
288  hs(i,j,0) = zcc(i,j,k0) - zsfc;
289  });
290  }
291  u_k0.FillBoundary(geom.periodicity());
292  v_k0.FillBoundary(geom.periodicity());
293  h_k0.FillBoundary(geom.periodicity());
294 }
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ xvel
Definition: ERF_IndexDefines.H:255
@ yvel
Definition: ERF_IndexDefines.H:256
Definition: ERF_ConsoleIO.cpp:17
Here is the call graph for this function:

◆ fill_fire_wind_from_interpolation()

void fill_fire_wind_from_interpolation ( amrex::MultiFab &  fire_wind_ref,
amrex::MultiFab &  fire_wind_extract_z_mf,
const amrex::MultiFab &  xvel_mf,
const amrex::MultiFab &  yvel_mf,
const amrex::MultiFab &  z_phys_cc_mf,
const amrex::MultiFab &  fire_surface_z_mf,
const amrex::MultiFab &  fire_col_ground_mf,
const FireGrid &  fg,
amrex::Real  z_ref,
int  nz,
const amrex::MultiFab *  z_ref_cell_mf = nullptr,
int  wind_interp = 1,
const amrex::MultiFab *  col_open = nullptr,
const amrex::MultiFab *  col_roof = nullptr,
amrex::Real  z_sample = 0.0,
amrex::Real  z0_log = 0.1,
bool  below_first_log = false 
)

Extract wind from direct vertical interpolation on fire grid.

Maps atmospheric wind to fire grid at reference height by interpolating vertically through the atmospheric column at the fire cell location. Uses cell-center wind values (averaged from face-staggered data) and linearly interpolates to the target height above terrain.

When z_ref_cell_mf is non-null, each fire cell is sampled at its own height from that field (for example a per-fuel wind height, as in the WRF-SFIRE fcwh convention) instead of the global z_ref.

Parameters
[out]fire_wind_refu,v at z_ref on fire grid [m/s]
[out]fire_wind_extract_z_mfWind extraction height z_target [m]
[in]xvel_mfx-velocity (face-staggered at i+1/2, j, k)
[in]yvel_mfy-velocity (face-staggered at i, j+1/2, k)
[in]z_phys_cc_mfCell-center terrain height z_phys_cc[0] on atm grid
[in]fgFire grid
[in]z_refGlobal fallback wind reference height [m]
[in]nzNumber of vertical levels
[in]z_ref_cell_mfHeight above ground [m] to sample each fire cell at, on the fire grid; nullptr = the global z_ref
[in]below_first_logTarget below the lowest cell centre: false takes the lowest centre's wind as it is; true scales it with a neutral log profile of roughness z0_log, U(z) = U(z_1) ln(z/z0)/ln(z_1/z0)

Algorithm per fire cell (i_f, j_f):

  1. Map to atmospheric column: i_a = i_f / C, j_a = j_f / C
  2. Get ground elevation: z_surf = fire_surface_z(i_f, j_f), the average of the four k = 0 nodes of atmospheric cell (i_a, j_a). Using z_phys_cc(i_a, j_a, 0) instead would sit half an atmospheric cell above the ground.
  3. Lookup wind height:
    • If z_ref_cell_mf != nullptr: z_ref_cell = z_ref_cell_mf(i_f, j_f)
    • Otherwise: z_ref_cell = z_ref (global)
  4. Compute target height: z_target = z_surf + z_ref_cell
  5. Bisection on the column's z_phys_cc profile for k_lo such that z_phys_cc(i_a, j_a, k_lo) <= z_target < z_phys_cc(i_a, j_a, k_lo+1), clamped to [0, nz-2]; a target above the top takes the highest centre, a target below the first centre the lowest centre's wind scaled by the log profile (below_first_log) or as it is
  6. Compute interpolation weight: alpha = (z_target - z_phys_cc(i_a, j_a, k_lo)) / (z_phys_cc(i_a, j_a, k_lo+1) - z_phys_cc(i_a, j_a, k_lo)) Clamp alpha to [0, 1]
  7. Average u/v from bracketing face values to get cell centers: u_cc(i,j,k) = 0.5 * (xvel(i,j,k) + xvel(i+1,j,k)) v_cc(i,j,k) = 0.5 * (yvel(i,j,k) + yvel(i,j+1,k))
  8. Interpolate: fw(i_f,j_f,0,0) = u_cc(k_lo) + alpha*(u_cc(k_lo+1) - u_cc(k_lo)) fw(i_f,j_f,0,1) = v_cc(k_lo) + alpha*(v_cc(k_lo+1) - v_cc(k_lo))
  9. Store z_target in fire_wind_extract_z_mf

With wind_interp = 1 (bilinear, the default) steps 3-8 run for each of the four atmospheric columns surrounding the fire cell, every one sampled at the reference height above its own ground, and the results are blended with bilinear weights. With wind_interp = 0 only the column containing the cell is used, which makes the fire-grid wind piecewise constant on atmospheric cells.

With col_open and col_roof given (2D atmospheric-column fields, one ghost cell) each bilinear weight is multiplied by the column's open fraction when its roof is above the cell's wind height, and the four weights are renormalised, so a column inside a building does not contribute its relaxed in-building velocity to the fire cells around it. A cell whose four columns are all closed keeps the plain weights; it is inside a footprint and masked anyway.

With z_sample > 0 (an engine input) every column is sampled at z_sample above its ground instead of at the target height, and the result is brought down to the target height with a neutral log profile of roughness z0_log: U(z_ref) = U(z_sample) ln(z_ref / z0) / ln(z_sample / z0). This is the second interpolation option of the Community Fire Behavior Model (Jimenez y Munoz et al. 2026), meant to drive the fire with a wind taken above its own plume. fire_wind_extract_z then records the sampled height; the open-column test uses that height as well.