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
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ display_markdown(

The `abiotic_simple` model is a simple regression model that estimates microclimatic
variables based on empirical relationships between leaf area index (LAI) and atmospheric
temperature (T), relative humidity (RH), and vapour pressure deficit (VPD) to derive
temperature (T), relative humidity (RH), vapour pressure deficit (VPD), and wind speed
to derive
logarithmic profiles of these variables from external climate data such as regional
climate models or satellite observations. The model also provides information on
atmospheric pressure and $\ce{CO_{2}}$ and soil temperatures at different depths.
Expand Down
6 changes: 6 additions & 0 deletions tests/models/abiotic/test_abiotic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
(DEBUG, "abiotic model: required var 'topofcanopy_radiation' checked"),
(DEBUG, "abiotic model: required var 'leaf_area_index' checked"),
(DEBUG, "abiotic model: required var 'layer_heights' checked"),
(DEBUG, "abiotic model: required var 'wind_speed_ref' checked"),
)

SETUP_MANIPULATIONS = (
Expand All @@ -27,6 +28,7 @@
(INFO, "Replacing data array for 'air_temperature'"),
(INFO, "Replacing data array for 'relative_humidity'"),
(INFO, "Adding data array for 'vapour_pressure_deficit'"),
(INFO, "Replacing data array for 'wind_speed'"),
(INFO, "Replacing data array for 'atmospheric_pressure'"),
(INFO, "Adding data array for 'atmospheric_co2'"),
(INFO, "Replacing data array for 'soil_temperature'"),
Expand Down Expand Up @@ -120,6 +122,10 @@ def test_abiotic_model_initialization_no_data(caplog, fixture_core_components):
ERROR,
"abiotic model: init data missing required var 'layer_heights'",
),
(
ERROR,
"abiotic model: init data missing required var 'wind_speed_ref'",
),
(ERROR, "abiotic model: error checking vars_required_for_init, see log."),
),
)
Expand Down
11 changes: 11 additions & 0 deletions tests/models/abiotic_simple/test_abiotic_simple_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,7 @@ def test_setup(dummy_climate_data_varying_canopy, fixture_core_components):
"soil_temperature",
"atmospheric_pressure",
"atmospheric_co2",
"wind_speed",
]:
assert var in model.data

Expand All @@ -240,6 +241,16 @@ def test_setup(dummy_climate_data_varying_canopy, fixture_core_components):
]
xr.testing.assert_allclose(model.data["air_temperature"], exp_air_temp)

exp_wind = lyr_strct.from_template()
exp_wind[lyr_strct.index_filled_atmosphere] = [
[1, 1, 1, 1],
[0.993673, 0.995782, 0.997891, 0.997891],
[0.953925, 0.969284, np.nan, np.nan],
[0.885976, np.nan, np.nan, np.nan],
[0.434528, 0.623019, 0.811509, 0.811509],
]
xr.testing.assert_allclose(model.data["wind_speed"], exp_wind)

exp_soil_temp = lyr_strct.from_template()
exp_soil_temp[lyr_strct.index_all_soil] = [
[20.712458, 21.317566, 21.922674, 21.922674],
Expand Down
19 changes: 16 additions & 3 deletions tests/models/abiotic_simple/test_microclimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ def test_log_interpolation(dummy_climate_data, fixture_core_components):

# temperature
result = log_interpolation(
data=dummy_climate_data,
reference_data=dummy_climate_data["air_temperature_ref"].isel(time_index=0),
leaf_area_index_sum=leaf_area_index_sum,
layer_structure=lyr_strct,
Expand All @@ -33,7 +32,6 @@ def test_log_interpolation(dummy_climate_data, fixture_core_components):

# relative humidity
result_hum = log_interpolation(
data=dummy_climate_data,
reference_data=dummy_climate_data["relative_humidity_ref"].isel(time_index=0),
leaf_area_index_sum=leaf_area_index_sum,
layer_structure=lyr_strct,
Expand Down Expand Up @@ -63,7 +61,6 @@ def test_varying_canopy_log_interpolation(

# temperature
result = log_interpolation(
data=data,
reference_data=data["air_temperature_ref"].isel(time_index=0),
leaf_area_index_sum=leaf_area_index_sum,
layer_structure=lyr_strct,
Expand Down Expand Up @@ -211,6 +208,12 @@ def test_run_microclimate(dummy_climate_data, fixture_core_components):
exp_pressure[lyr_strct.index_atmosphere] = 96
xr.testing.assert_allclose(result["atmospheric_pressure"], exp_pressure)

exp_wind = lyr_strct.from_template()
exp_wind[lyr_strct.index_filled_atmosphere] = np.array(
[1.0, 0.993673, 0.953925, 0.885976, 0.434528]
)[:, None]
np.testing.assert_allclose(result["wind_speed"], exp_wind, rtol=1e-3, atol=1e-3)


def test_run_microclimate_varying_canopy(
dummy_climate_data_varying_canopy, fixture_core_components
Expand Down Expand Up @@ -251,6 +254,16 @@ def test_run_microclimate_varying_canopy(
]
xr.testing.assert_allclose(result["soil_temperature"], exp_soil_temp)

exp_wind = lyr_strct.from_template()
exp_wind[lyr_strct.index_filled_atmosphere] = [
[1, 1, 1, 1],
[0.993673, 0.995782, 0.997891, 0.997891],
[0.953925, 0.969284, np.nan, np.nan],
[0.885976, np.nan, np.nan, np.nan],
[0.434528, 0.623019, 0.811509, 0.811509],
]
xr.testing.assert_allclose(result["wind_speed"], exp_wind)

exp_pressure = lyr_strct.from_template()
exp_pressure[lyr_strct.index_atmosphere] = 96
xr.testing.assert_allclose(result["atmospheric_pressure"], exp_pressure)
Expand Down
1 change: 1 addition & 0 deletions virtual_ecosystem/models/abiotic/abiotic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class AbioticModel(
"topofcanopy_radiation",
"leaf_area_index",
"layer_heights",
"wind_speed_ref",
),
vars_updated=(
"air_temperature",
Expand Down
4 changes: 2 additions & 2 deletions virtual_ecosystem/models/abiotic_simple/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@

* The :mod:`~virtual_ecosystem.models.abiotic_simple.microclimate` submodule
contains a set functions and parameters that are used to calculate atmospheric
temperature, relative humidity, vapour pressure deficit, :math:`\ce{CO2}`, and
atmospheric pressure profiles as well as soil temperature profiles.
temperature, relative humidity, vapour pressure deficit, wind speed, :math:`\ce{CO2}`,
and atmospheric pressure profiles as well as soil temperature profiles.

* The :mod:`~virtual_ecosystem.models.abiotic_simple.constants` submodule provides a
set of dataclasses containing the constants required by the broader abiotic model
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,15 @@ class AbioticSimpleModel(
"soil_temperature",
"atmospheric_pressure",
"atmospheric_co2",
"wind_speed",
),
vars_required_for_update=(
"air_temperature_ref",
"relative_humidity_ref",
"vapour_pressure_deficit_ref",
"atmospheric_pressure_ref",
"atmospheric_co2_ref",
"wind_speed_ref",
"leaf_area_index",
"layer_heights",
),
Expand Down
8 changes: 8 additions & 0 deletions virtual_ecosystem/models/abiotic_simple/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,13 @@ class AbioticSimpleBounds(ConstantsDataclass):
leaf area index from :cite:t:`hardwick_relationship_2015`.
"""

wind_speed: tuple[float, float, float] = (0.001, 100.0, -0.1)
"""Bounds and gradient for wind speed, [m s-1].

Gradient for linear regression to calculate wind speed as a function of
leaf area index. The value is choses arbitrarily and needs to be replaced with
observations.
"""

soil_temperature: tuple[float, float] = (-10.0, 50.0)
"""Bounds for soil temperature, [C]."""
22 changes: 13 additions & 9 deletions virtual_ecosystem/models/abiotic_simple/microclimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
atmospheric temperature, relative humidity, and vapour pressure deficit at ground level
(1.5 m) given the above canopy conditions and leaf area index of intervening canopy. A
within canopy profile is then interpolated using a logarithmic curve between the above
canopy observation and ground level prediction.
canopy observation and ground level prediction. The same method is applied to derive a
vertical wind profile within the canopy.
Soil temperature is interpolated between the surface layer and the soil temperature at
1 m depth which equals the mean annual temperature.
The module also provides a constant vertical profile of atmospheric pressure and
Expand Down Expand Up @@ -33,8 +34,8 @@ def run_microclimate(
r"""Calculate simple microclimate.

This function uses empirical relationships between leaf area index (LAI) and
atmospheric temperature, relative humidity, and vapour pressure deficit to derive
logarithmic profiles of these variables from external climate data such as
atmospheric temperature, relative humidity, vapour pressure deficit, and wind speed
to derive logarithmic profiles of these variables from external climate data such as
regional climate models or satellite observations. Note that these sources provide
data at different heights and with different underlying assumptions which lead to
different biases in the model output. For below canopy values (1.5 m),
Expand Down Expand Up @@ -71,6 +72,7 @@ def run_microclimate(
* vapour_pressure_deficit_ref [kPa]
* atmospheric_pressure_ref [kPa]
* atmospheric_co2_ref [ppm]
* wind_speed_ref [m s-1]
* leaf_area_index [m m-1]
* layer_heights [m]

Expand All @@ -84,8 +86,8 @@ def run_microclimate(

Returns:
Dict of DataArrays for air temperature [C], relative humidity [-], vapour
pressure deficit [kPa], soil temperature [C], atmospheric pressure [kPa], and
atmospheric :math:`\ce{CO2}` [ppm]
pressure deficit [kPa], soil temperature [C], atmospheric pressure [kPa],
atmospheric :math:`\ce{CO2}` [ppm], wind speed [m s-1]
"""

output = {}
Expand All @@ -94,11 +96,15 @@ def run_microclimate(
leaf_area_index_sum = data["leaf_area_index"].sum(dim="layers")

# Interpolate atmospheric profiles
for var in ["air_temperature", "relative_humidity", "vapour_pressure_deficit"]:
for var in [
"air_temperature",
"relative_humidity",
"vapour_pressure_deficit",
"wind_speed",
]:
lower, upper, gradient = getattr(bounds, var)

output[var] = log_interpolation(
data=data,
reference_data=data[var + "_ref"].isel(time_index=time_index),
leaf_area_index_sum=leaf_area_index_sum,
layer_structure=layer_structure,
Expand Down Expand Up @@ -139,7 +145,6 @@ def run_microclimate(


def log_interpolation(
data: Data,
reference_data: DataArray,
leaf_area_index_sum: DataArray,
layer_structure: LayerStructure,
Expand All @@ -151,7 +156,6 @@ def log_interpolation(
"""LAI regression and logarithmic interpolation of variables above ground.

Args:
data: Data object
reference_data: Input variable at reference height
leaf_area_index_sum: Leaf area index summed over all layers, [m m-1]
layer_structure: The LayerStructure instance for the simulation.
Expand Down