Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions doc/Configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -215,17 +215,45 @@ output fields retain their existing single-precision NetCDF representation.
Number of grid points around lfn=0 where ``fire_viscosity_bg`` is used

``fmoist_run``: *logical* (Default: ``.false.``)
Runs moisture model on the atmospheric grid, outputting the result as a variable named ``fmc_gc``
Enables evolving class moisture on the fire grid. The first moisture call
initializes the four dead classes from equilibrium and the live class from
``fuelmc_g_live``, using the first supplied atmospheric forcing. Initialization
occurs independently of the update cadence and copies that forcing into the
previous-forcing state. Missing, nonfinite, or invalid forcing stops the run.
Effective moisture ``fmc_g`` is used by the spread model; class state
``fmc_gc`` is stored in checkpoints, not ordinary output.

The effective initialization time is the first call's ``itimestep*dt``
timestamp, normally ``dt`` seconds from the original run origin. Initialization
applies zero elapsed moisture time. Later updates use the interval since
initialization or the last update. This retains the existing component's
timestamp convention without introducing an artificial initial advance.

``fmoist_freq``: *integer* (Default: ``0``)
Frequency to run moisture model.
Frequency of moisture updates after initialization.
0: use ``fmoist_dt``

k>0: every "k" timesteps

``fmoist_dt``: *real* (Default: ``600.0``)
[Units: s]
Time step of moisture model (only used if ``fmoist_freq=0``)
Numerical update interval of the moisture model (only used if ``fmoist_freq=0``),
measured from its effective initialization time. It is distinct from a fuel
class's physical response time. Step-based updates retain their phase relative
to the original run origin. For example, with ``dt=4`` and ``fmoist_freq=3``,
initialization occurs at 4 s, the first update at 12 s uses 8 s elapsed time,
and subsequent updates use 12 s intervals.

Checkpoints save initialization status/time, class moisture, previous forcing,
last/next update times, and the moisture activation and cadence settings.
A resumed run restores this state without reinitialization and requires the
same activation and cadence. New checkpoints cannot be written before
moisture initialization. PR #39 time-based checkpoints are accepted when their
positive next-update time establishes initialization and their saved interval
matches ``fmoist_dt``. Legacy step-based files are rejected because their
metadata cannot distinguish initialized state from the old delayed-initialization
defect. Prescribed-moisture legacy files remain readable. This compatibility
rule does not resolve the separate humidity-convention audit.

``fire_wind_height``: *real* (Default: ``6.096``)
[Units: m]
Expand Down
2 changes: 2 additions & 0 deletions nuopc/fire_behavior_nuopc.F90
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,8 @@ subroutine Realize(model, rc)

if (NUOPC_IsConnected(importState, fieldName="mean_prec_rate")) then
imp_rainrte = .TRUE.
! Start rate accumulation once; retain rainfall restored by Advertise.
if (.not. config_flags%restart) grid%fire_rain = 0.0
! importable field on Grid: mean_prec_rate
field = ESMF_FieldCreate(name="mean_prec_rate", grid=fire_grid, &
typekind=ESMF_TYPEKIND_R8, rc=rc)
Expand Down
61 changes: 57 additions & 4 deletions physics/fmc_wrffire_mod.F90
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ module fmc_wrffire_mod
! 50% Entering dormancy, coloration starting, some leaves may have dropped from stem
! 30% Completely cured, treat as dead fuel

use, intrinsic :: iso_fortran_env, only : REAL32
use, intrinsic :: iso_fortran_env, only : REAL32, INT32
use, intrinsic :: ieee_arithmetic, only : ieee_is_finite
use netcdf, only : NF90_FILL_FLOAT
use stderrout_mod, only : Stop_simulation
use netcdf_mod, only : Add_netcdf_att, Add_netcdf_dim, Add_netcdf_var_mpi, NAME_DIM_X, NAME_DIM_Y
use ros_mod, only : ros_t
Expand All @@ -25,16 +27,23 @@ module fmc_wrffire_mod
private

public :: fmc_wrffire_t, NAME_VAR_FMC_GC, NAME_ATT_FMOIST_LASTTIME, NAME_ATT_FMOIST_NEXTTIME
public :: NAME_ATT_FMC_INITIALIZED, NAME_ATT_FMC_INITTIME

integer, parameter :: MOISTURE_CLASSES = 5, NUM_FMEP = 2
real, parameter :: FMEP_DECAY_TLAG = 999999 ! time constant of assimilated adjustments of equilibria decay
character (len = *), parameter :: NAME_DIM_MOISTURE_CLASS = 'moisture_class'
character (len = *), parameter :: NAME_VAR_FMC_GC = 'fmc_gc'
character (len = *), parameter :: NAME_ATT_FMOIST_LASTTIME = 'fmc_fmoist_lasttime'
character (len = *), parameter :: NAME_ATT_FMOIST_NEXTTIME = 'fmc_fmoist_nexttime'
character (len = *), parameter :: NAME_ATT_FMC_INITIALIZED = 'fmc_initialized'
character (len = *), parameter :: NAME_ATT_FMC_INITTIME = 'fmc_initialization_time'

type, extends(fmc_t) :: fmc_wrffire_t
real :: fmoist_lasttime, fmoist_nexttime, dt_moisture
real :: initialization_time = 0.0
! Component-local state: all five moisture classes and previous forcing
! are initialized by the first advance or restored from a valid restart.
logical :: initialized = .false.
real, dimension(MOISTURE_CLASSES) :: rec_drying_lag_sec, rec_wetting_lag_sec, fmc_gc_initial_value
logical :: run_advance_moisture
real, dimension(:, :, :), allocatable :: fmc_gc ! fuel moisture contents by class
Expand Down Expand Up @@ -92,6 +101,7 @@ subroutine Add_restart_vars (this, file_restart, cfbm_comm, nx, ny, ifps, ifpe,


if (.not. allocated (this%fmc_gc)) call Stop_simulation ('FMC restart write requires allocated fmc_gc')
if (.not. this%initialized) call Stop_simulation ('FMC restart write requires initialized moisture and forcing history')

n_moisture_classes = size (this%fmc_gc, 2)
if (rank == 0) call Add_netcdf_dim (file_restart, NAME_DIM_MOISTURE_CLASS, n_moisture_classes)
Expand All @@ -107,6 +117,8 @@ subroutine Add_restart_vars (this, file_restart, cfbm_comm, nx, ny, ifps, ifpe,
ifps, ifpe, jfps, jfpe, NAME_VAR_FMC_GC, this%fmc_gc(ifps:ifpe, 1:n_moisture_classes, jfps:jfpe))

if (rank == 0) then
call Add_netcdf_att (file_restart, 'global', NAME_ATT_FMC_INITIALIZED, int (1, kind = INT32))
call Add_netcdf_att (file_restart, 'global', NAME_ATT_FMC_INITTIME, real (this%initialization_time, kind = REAL32))
call Add_netcdf_att (file_restart, 'global', NAME_ATT_FMOIST_LASTTIME, real (this%fmoist_lasttime, kind = REAL32))
call Add_netcdf_att (file_restart, 'global', NAME_ATT_FMOIST_NEXTTIME, real (this%fmoist_nexttime, kind = REAL32))
end if
Expand Down Expand Up @@ -141,6 +153,9 @@ subroutine Init_fmc_wrffire (this, fuels, fuelmc_g, fuelmc_g_live, ifms, ifme, j

this%fmoist_lasttime = itimestep * dt
this%fmoist_nexttime = this%fmoist_lasttime
this%initialized = .false.
this%initialization_time = 0.0
this%dt_moisture = 0.0

fgi_c = 0.0
fgi_c(1:mfuelcats, 1) = fuels%fgi_1h
Expand Down Expand Up @@ -202,10 +217,12 @@ subroutine Advance_fmc_model (this, fmoist_freq, fmoist_dt, itimestep, dt, ifms,

integer :: ij
real :: time_start, moisture_time
logical :: initialize


time_start = itimestep * dt
moisture_time = time_start
initialize = .not. this%initialized

this%run_advance_moisture = .false.
if (fmoist_freq > 0) then
Expand All @@ -214,8 +231,17 @@ subroutine Advance_fmc_model (this, fmoist_freq, fmoist_dt, itimestep, dt, ifms,
if (.not. moisture_time < this%fmoist_nexttime) this%run_advance_moisture = .true.
end if

! Establish class state at the first call's model timestamp, even when
! the first scheduled update is later. Initialization has no elapsed time.
if (initialize) this%run_advance_moisture = .true.
if (this%run_advance_moisture) then
this%dt_moisture = moisture_time - this%fmoist_lasttime
if (initialize) then
this%dt_moisture = 0.0
this%initialization_time = moisture_time
else
this%dt_moisture = moisture_time - this%fmoist_lasttime
if (this%dt_moisture <= 0.0) call Stop_simulation ('FMC advance requires increasing moisture time')
end if
this%fmoist_lasttime = moisture_time
if (fmoist_freq > 0)then
continue
Expand All @@ -228,10 +254,11 @@ subroutine Advance_fmc_model (this, fmoist_freq, fmoist_dt, itimestep, dt, ifms,
!$OMP PARALLEL DO &
!$OMP PRIVATE (ij)
do ij = 1, num_tiles
call this%Advance_moisture_classes (itimestep == 1, ifms, ifme, jfms, jfme, i_start(ij), i_end(ij), j_start(ij), j_end(ij), &
call this%Advance_moisture_classes (initialize, ifms, ifme, jfms, jfme, i_start(ij), i_end(ij), j_start(ij), j_end(ij), &
fire_rain, fire_t2, fire_q2, fire_psfc, fire_rain_old, fire_t2_old, fire_q2_old, fire_psfc_old, fire_rh_fire, fuelmc_g)
end do
!$OMP END PARALLEL DO
this%initialized = .true.

!$OMP PARALLEL DO &
!$OMP PRIVATE (ij)
Expand Down Expand Up @@ -301,7 +328,14 @@ subroutine Advance_moisture_classes (this, initialize, ifms, ifme, jfms, jfme, i
real :: epsilon, Pws, Pw


if (initialize) call Copy2old ()
! Reject unavailable forcing before copying it into the persistent history
! or evaluating logarithms and powers in the equilibrium calculation.
call Validate_forcing (rain, t2, q2, psfc)
if (initialize) then
call Copy2old ()
else
call Validate_forcing (rain_old, t2_old, q2_old, psfc_old)
end if

rhmax = -huge (rhmax)
rhmin = huge (rhmin)
Expand Down Expand Up @@ -442,6 +476,25 @@ subroutine Advance_moisture_classes (this, initialize, ifms, ifme, jfms, jfme, i

contains

subroutine Validate_forcing (rain_in, t_in, q_in, p_in)

real, intent (in) :: rain_in(ifms:ifme, jfms:jfme), t_in(ifms:ifme, jfms:jfme), &
q_in(ifms:ifme, jfms:jfme), p_in(ifms:ifme, jfms:jfme)
integer :: i, j

do j = jfts, jfte
do i = ifts, ifte
if (.not. all (ieee_is_finite ([rain_in(i,j), t_in(i,j), q_in(i,j), p_in(i,j)]))) &
call Stop_simulation ('FMC requires finite atmospheric forcing')
if (any ([rain_in(i,j), t_in(i,j), q_in(i,j), p_in(i,j)] >= NF90_FILL_FLOAT)) &
call Stop_simulation ('FMC atmospheric forcing is unavailable')
if (t_in(i,j) <= 0.0 .or. p_in(i,j) <= 0.0 .or. q_in(i,j) < 0.0 .or. rain_in(i,j) < 0.0) &
call Stop_simulation ('FMC requires positive temperature and pressure and nonnegative humidity and rain')
end do
end do

end subroutine Validate_forcing

subroutine Copy2old ()

implicit none
Expand Down
Loading
Loading