Source code for abcmodel.land.soil.standard

from dataclasses import dataclass, field, replace

import jax.numpy as jnp
from jax import Array

from ...abstracts import AbstractCoupledState
from ...utils import PhysicalConstants as cst
from ..abstracts import AbstractSoilModel, AbstractSoilState


[docs] @dataclass class StandardSoilState(AbstractSoilState): """Standard soil state.""" wg: Array = field( metadata={ "label": r"$w_g$", "unit": "m^3 m^{-3}", "description": "Soil moisture content", } ) """Soil moisture content in the root zone [m3 m-3].""" temp_soil: Array = field( metadata={ "label": r"$T_{soil}$", "unit": "K", "description": "Soil temperature", } ) """Soil temperature [K].""" temp2: Array = field( metadata={ "label": r"$T_{soil,2}$", "unit": "K", "description": "Deep soil temperature", } ) """Deep soil temperature [K].""" rssoil: Array = field( default_factory=lambda: jnp.array(1.0e6), metadata={ "label": r"$r_{soil}$", "unit": "s m^{-1}", "description": "Soil resistance", }, ) """Soil resistance [m s-1].""" temp_soil_tend: Array = field( default_factory=lambda: jnp.array(0.0), metadata={ "label": r"$\partial T_{soil} / \partial t$", "unit": "K s^{-1}", "description": "Soil temperature tendency", }, ) """Soil temperature tendency [K s-1].""" wgtend: Array = field( default_factory=lambda: jnp.array(0.0), metadata={ "label": r"$\partial w_g / \partial t$", "unit": "m^3 m^{-3} s^{-1}", "description": "Soil moisture tendency", }, ) """Soil moisture tendency [m3 m-3 s-1]."""
[docs] class StandardSoilModel(AbstractSoilModel[StandardSoilState]): """Standard soil model with moisture and temperature dynamics. Args: a: Clapp and Hornberger (1978) retention curve parameter. Default is 0.219. b: Clapp and Hornberger (1978) retention curve parameter. Default is 4.90. p: Clapp and Hornberger (1978) retention curve parameter. Default is 4.0. cgsat: saturated soil heat capacity [J m-3 K-1]. Default is 3.56e-6. wsat: saturated soil moisture content [m3 m-3]. Default is 0.472. wfc: soil moisture content at field capacity [m3 m-3]. Default is 0.323. wwilt: soil moisture content at wilting point [m3 m-3]. Default is 0.171. w2: soil moisture content at the second layer [m3 m-3]. Default is 0.21. d1: depth of the top soil layer [m]. Default is 0.1. c1sat: saturated soil hydraulic conductivity parameter [-]. Default is 0.132. c2ref: reference soil hydraulic conductivity parameter [-]. Default is 1.8. rssoilmin: minimum soil resistance [s m-1]. Default is 50.0. """ def __init__( self, a: float = 0.219, b: float = 4.90, p: float = 4.0, cgsat: float = 3.56e-6, wsat: float = 0.472, wfc: float = 0.323, wwilt: float = 0.171, w2: float = 0.21, d1: float = 0.1, c1sat: float = 0.132, c2ref: float = 1.8, rssoilmin: float = 50.0, ): self.a = a self.b = b self.p = p self.cgsat = cgsat self.wsat = wsat self.wfc = wfc self.wwilt = wwilt self.w2 = w2 self.d1 = d1 self.c1sat = c1sat self.c2ref = c2ref self.rssoilmin = rssoilmin
[docs] def init_state( self, wg: float = 0.21, temp_soil: float = 285.0, temp2: float = 286.0, rssoil: float = 1.0e6, ) -> StandardSoilState: """Initialize the soil state. Args: wg: Volumetric soil moisture [m3 m-3]. Default is 0.21. temp_soil: Soil temperature [K]. Default is 285.0. temp2: Deep soil temperature [K]. Default is 286.0. rssoil: Soil resistance [s m-1]. Default is 1.0e6. Returns: The initialized StandardSoilState. """ return StandardSoilState( wg=jnp.array(wg), temp_soil=jnp.array(temp_soil), temp2=jnp.array(temp2), rssoil=jnp.array(rssoil), )
[docs] def run(self, state: AbstractCoupledState) -> StandardSoilState: """Compute soil surface resistance.""" rssoil = self.compute_soil_resistance(state.land.wg) return replace(state.land.soil, rssoil=rssoil)
[docs] def compute_soil_resistance(self, wg: Array) -> Array: """Compute the soil resistance rssoil.""" f2 = jnp.where( wg > self.wwilt, (self.wfc - self.wwilt) / (wg - self.wwilt), 1.0e8, ) return self.rssoilmin * f2
[docs] def compute_temp_soil_tend( self, gf: Array, temp_soil: Array, temp2: Array ) -> Array: """Compute the soil temperature tendency.""" cg = self.cgsat * (self.wsat / self.w2) ** (self.b / (2.0 * jnp.log(10.0))) return cg * gf - 2.0 * jnp.pi / 86400.0 * (temp_soil - temp2)
[docs] def compute_wgtend(self, wg: Array, le_soil: Array) -> Array: """Compute the soil moisture tendency.""" c1 = self.c1sat * (self.wsat / wg) ** (self.b / 2.0 + 1.0) c2 = self.c2ref * (self.w2 / (self.wsat - self.w2)) wgeq = self.w2 - self.wsat * self.a * ( (self.w2 / self.wsat) ** self.p * (1.0 - (self.w2 / self.wsat) ** (8.0 * self.p)) ) evap_loss = -c1 / (cst.rhow * self.d1) * le_soil / cst.lv deep_grad = c2 / 86400.0 * (wg - wgeq) return evap_loss + deep_grad
[docs] def run_tends(self, state: StandardSoilState, surf_state) -> StandardSoilState: """Compute soil tendencies that depend on surface fluxes.""" wgtend = self.compute_wgtend(state.wg, surf_state.le_soil) temp_soil_tend = self.compute_temp_soil_tend( surf_state.gf, state.temp_soil, state.temp2 ) return replace(state, wgtend=wgtend, temp_soil_tend=temp_soil_tend)
[docs] def integrate(self, state: StandardSoilState, dt: float) -> StandardSoilState: """Integrate soil moisture and temperature forward in time.""" wg = state.wg + dt * state.wgtend temp_soil = state.temp_soil + dt * state.temp_soil_tend return replace(state, wg=wg, temp_soil=temp_soil)