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
7 changes: 5 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,8 +546,11 @@ def dummy_climate_data(fixture_core_components):
data["molar_density_air"] = from_template()
data["molar_density_air"][lyr_str.index_filled_atmosphere] = 38.0

data["density_air"] = from_template()
data["density_air"][lyr_str.index_filled_atmosphere] = 1.255

data["specific_heat_air"] = from_template()
data["specific_heat_air"][lyr_str.index_filled_atmosphere] = 29.0
data["specific_heat_air"][lyr_str.index_filled_atmosphere] = 1.006

data["attenuation_coefficient"] = from_template()
data["attenuation_coefficient"][lyr_str.index_filled_atmosphere] = np.array(
Expand All @@ -560,7 +563,7 @@ def dummy_climate_data(fixture_core_components):
)[:, None]

data["latent_heat_vapourisation"] = from_template()
data["latent_heat_vapourisation"][lyr_str.index_filled_atmosphere] = 2254.0
data["latent_heat_vapourisation"][lyr_str.index_filled_atmosphere] = 2442.0

data["canopy_temperature"] = from_template()
data["canopy_temperature"][lyr_str.index_filled_canopy] = 25.0
Expand Down
2 changes: 1 addition & 1 deletion tests/models/abiotic/test_abiotic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ def test_setup_abiotic_model(dummy_climate_data, fixture_core_components):
model.update(time_index=0)

expected_soil_temp1 = lyr_strct.from_template()
expected_soil_temp1[lyr_strct.index_all_soil] = np.array([18.701923, 19.989061])[
expected_soil_temp1[lyr_strct.index_all_soil] = np.array([18.727418, 19.989483])[
:, None
]
xr.testing.assert_allclose(model.data["soil_temperature"], expected_soil_temp1)
35 changes: 20 additions & 15 deletions tests/models/abiotic/test_abiotic_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,29 @@ def test_calculate_molar_density_air():
)


def test_calculate_specific_heat_air():
"""Test calculate specific heat of air."""

from virtual_ecosystem.models.abiotic.abiotic_tools import (
calculate_specific_heat_air,
def test_calculate_air_density(dummy_climate_data):
"""Test calculate the density of air."""

from virtual_ecosystem.models.abiotic.abiotic_tools import calculate_air_density

consts = CoreConsts()
result = calculate_air_density(
air_temperature=dummy_climate_data["air_temperature_ref"]
.isel(time_index=0)
.to_numpy(),
atmospheric_pressure=dummy_climate_data["atmospheric_pressure_ref"]
.isel(time_index=0)
.to_numpy(),
specific_gas_constant_dry_air=consts.specific_gas_constant_dry_air,
celsius_to_kelvin=consts.zero_Celsius,
)

constants = AbioticConsts()
result = calculate_specific_heat_air(
temperature=np.array([[25.0] * 3, [20.0] * 3, [18.0] * 3]),
molar_heat_capacity_air=CoreConsts.molar_heat_capacity_air,
specific_heat_equ_factors=constants.specific_heat_equ_factors,
np.testing.assert_allclose(
result,
np.repeat(1.103205, 4),
rtol=1e-5,
atol=1e-5,
)

exp_result = np.array([[29.2075] * 3, [29.202] * 3, [29.2] * 3])

np.testing.assert_allclose(result, exp_result, rtol=1e-3, atol=1e-3)


def test_calculate_latent_heat_vapourisation():
"""Test calculation of latent heat of vapourization."""
Expand Down
5 changes: 2 additions & 3 deletions tests/models/abiotic/test_energy_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,10 @@ def test_update_air_canopy_temperature():
latent_heat_flux_canopy = np.array([[-30.0, -40.0], [-50.0, -60.0]])
air_temperature = np.array([[300.0, 295.0], [290.0, 285.0]])
canopy_temperature = np.array([[305.0, 300.0], [295.0, 290.0]])
specific_heat_air = np.array([[1005.0, 1005.0], [1005.0, 1005.0]])

# Expected outputs (calculated manually)
expected_canopy_temperature = np.array(
[[306.998597, 302.5214], [298.045954, 293.572175]]
[[356.152399, 367.60412], [380.623964, 395.36695]]
)
expected_air_temperature = np.array([[300.5, 295.5], [290.5, 285.5]])

Expand All @@ -284,7 +283,7 @@ def test_update_air_canopy_temperature():
air_temperature=air_temperature,
canopy_temperature=canopy_temperature,
emissivity_leaf=0.8,
specific_heat_air=specific_heat_air,
specific_heat_air=1.006,
density_air=1.293,
aerodynamic_resistance=10.0,
relaxation_factor=0.1,
Expand Down
16 changes: 8 additions & 8 deletions tests/models/abiotic/test_microclimate.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def test_run_microclimate(dummy_climate_data, fixture_core_components):
)

exp_soiltemp = lyr_str.from_template()
exp_soiltemp[lyr_str.index_all_soil] = np.array([17.357065, 19.959777])[:, None]
exp_soiltemp[lyr_str.index_all_soil] = np.array([17.378808, 19.9601])[:, None]
np.testing.assert_allclose(
result["soil_temperature"][lyr_str.index_all_soil],
exp_soiltemp[lyr_str.index_all_soil],
Expand All @@ -38,7 +38,7 @@ def test_run_microclimate(dummy_climate_data, fixture_core_components):

exp_cantemp = lyr_str.from_template()
exp_cantemp[lyr_str.index_filled_canopy] = np.array(
[23.023263, 22.210275, 20.819253]
[23.089618, 22.275915, 20.883682]
)[:, None]
np.testing.assert_allclose(
result["canopy_temperature"][lyr_str.index_filled_canopy],
Expand All @@ -49,13 +49,13 @@ def test_run_microclimate(dummy_climate_data, fixture_core_components):

exp_airtemp = lyr_str.from_template()
exp_airtemp[lyr_str.index_filled_atmosphere] = np.array(
[30.0, 26.322969, 25.473039, 24.019511, 19.6246]
[30.0, 26.323, 25.473, 24.019, 19.625]
)[:, None]
np.testing.assert_allclose(
result["air_temperature"],
exp_airtemp,
rtol=1e-04,
atol=1e-04,
rtol=1e-02,
atol=1e-02,
)

exp_relhum = lyr_str.from_template()
Expand All @@ -71,13 +71,13 @@ def test_run_microclimate(dummy_climate_data, fixture_core_components):

exp_vp = lyr_str.from_template()
exp_vp[lyr_str.index_filled_atmosphere] = np.array(
[1.41727, 1.195479, 1.170102, 1.127572, 1.088259]
[1.417, 1.195, 1.170, 1.127, 1.088]
)[:, None]
np.testing.assert_allclose(
result["vapour_pressure"],
exp_vp,
rtol=1e-04,
atol=1e-04,
rtol=1e-02,
atol=1e-02,
)

exp_vpd = lyr_str.from_template()
Expand Down
19 changes: 10 additions & 9 deletions tests/models/hydrology/test_above_ground.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
[
(
1.225,
2.45,
2442.0,
),
(
np.array([1.225, 1.225, 1.225]),
np.array([2.45, 2.45, 2.45]),
np.repeat(1.225, 3),
np.repeat(2442.0, 3),
),
],
)
Expand All @@ -31,19 +31,20 @@ def test_calculate_soil_evaporation(dens_air, latvap):
calculate_soil_evaporation,
)

core_consts = CoreConsts()
result = calculate_soil_evaporation(
temperature=np.array([20.0, 20.0, 30.0]),
wind_speed_surface=np.array([1.0, 0.5, 0.1]),
relative_humidity=np.array([80, 80, 90]),
atmospheric_pressure=np.array([90, 90, 90]),
relative_humidity=np.array([80.0, 80.0, 90.0]),
atmospheric_pressure=np.array([90.0, 90.0, 90.0]),
soil_moisture=np.array([0.01, 0.1, 0.5]),
soil_moisture_residual=0.1,
soil_moisture_capacity=0.9,
leaf_area_index=np.array([3, 4, 5]),
celsius_to_kelvin=273.15,
leaf_area_index=np.array([3.0, 4.0, 5.0]),
celsius_to_kelvin=core_consts.zero_Celsius,
density_air=dens_air,
latent_heat_vapourisation=latvap,
gas_constant_water_vapour=CoreConsts.gas_constant_water_vapour,
gas_constant_water_vapour=core_consts.gas_constant_water_vapour,
soil_surface_heat_transfer_coefficient=(
HydroConsts.soil_surface_heat_transfer_coefficient
),
Expand All @@ -52,7 +53,7 @@ def test_calculate_soil_evaporation(dens_air, latvap):
),
)

exp_evap = np.array([0.745206, 0.092515, 0.135078])
exp_evap = np.array([7.500966e-07, 9.312173e-08, 1.334507e-04])
np.testing.assert_allclose(result["soil_evaporation"], exp_evap, rtol=0.01)


Expand Down
16 changes: 8 additions & 8 deletions tests/models/hydrology/test_hydrology_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,12 +296,12 @@ def test_setup(
# Test 2d variables
expected_2d = {
"soil_moisture": [
[260.608295, 260.72929, 260.601055, 260.425838],
[228.163385, 228.240549, 228.143191, 228.079756],
[261.125091, 261.245902, 261.117706, 260.943488],
[228.350564, 228.428083, 228.330321, 228.266441],
],
"matric_potential": [
[-198.857517, -198.088532, -198.954992, -199.854294],
[-544.207397, -542.72211, -544.539057, -546.00744],
[-196.21647, -195.455268, -196.315208, -197.193146],
[-540.064973, -538.587559, -540.395254, -541.854458],
],
}

Expand All @@ -318,17 +318,17 @@ def test_setup(

# Test one dimensional variables
expected_1d = {
"vertical_flow": [1.112307, 1.115177, 1.115152, 1.111505],
"vertical_flow": [1.112, 1.115, 1.115, 1.112],
"total_river_discharge": [0, 0, 64864, 21514],
"surface_runoff": [0, 0, 0, 0],
"surface_runoff_accumulated": [0, 0, 0, 0],
"soil_evaporation": [14.646242, 14.646242, 14.646242, 14.646242],
"soil_evaporation": [13.097139, 13.097139, 13.097139, 13.097139],
}

for var_name, expected_vals in expected_1d.items():
np.testing.assert_allclose(
model.data[var_name],
expected_vals,
rtol=1e-4,
atol=1e-4,
rtol=1e-2,
atol=1e-2,
)
1 change: 0 additions & 1 deletion tests/models/hydrology/test_hydrology_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ def test_setup_hydrology_input_current_timestep(
# Check if all variables were created TODO switch back to subcanopy
var_list = [
"latent_heat_vapourisation",
"molar_density_air",
"current_precipitation",
"surface_temperature",
"surface_humidity",
Expand Down
3 changes: 3 additions & 0 deletions virtual_ecosystem/core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,6 @@ class CoreConsts(ConstantsDataclass):

characteristic_dimension_leaf: float = 0.01
"""Characteristic dimension of leaf, typically around 0.7 * leaf width, [m]."""

specific_gas_constant_dry_air: float = 287.05
"""Specific gas constant for dry air, [J kg-1 K-1]."""
6 changes: 3 additions & 3 deletions virtual_ecosystem/data_variables.toml
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,8 @@ variable_type = "float"

[[variable]]
axis = ["spatial"]
description = "Temperature-dependent molar density of air"
name = "molar_density_air"
description = "Temperature-dependent density of air"
name = "density_air"
unit = "kg m-3"
variable_type = "float"

Expand Down Expand Up @@ -429,7 +429,7 @@ variable_type = "float"
axis = ["spatial"]
description = "Specific heat of air"
name = "specific_heat_air"
unit = "kJ kg-1"
unit = "kJ kg-1 K-1"
variable_type = "float"

[[variable]]
Expand Down
3 changes: 1 addition & 2 deletions virtual_ecosystem/models/abiotic/abiotic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class AbioticModel(
"sensible_heat_flux",
"latent_heat_flux",
"ground_heat_flux",
"molar_density_air",
"density_air",
"specific_heat_air",
"latent_heat_vapourisation",
),
Expand Down Expand Up @@ -86,7 +86,6 @@ class AbioticModel(
),
vars_populated_by_first_update=(
"longwave_emission",
"molar_density_air",
"specific_heat_air",
"latent_heat_vapourisation",
),
Expand Down
35 changes: 20 additions & 15 deletions virtual_ecosystem/models/abiotic/abiotic_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,30 @@ def calculate_molar_density_air(
)


def calculate_specific_heat_air(
temperature: NDArray[np.float32],
molar_heat_capacity_air: float,
specific_heat_equ_factors: tuple[float, float],
) -> NDArray[np.float32]:
"""Calculate temperature-dependent specific heat of air.

Implementation after :cite:t:`maclean_microclimc_2021`.
def calculate_air_density(
air_temperature: NDArray[np.float32],
atmospheric_pressure: NDArray[np.float32],
specific_gas_constant_dry_air: float,
celsius_to_kelvin: float,
):
"""Calculate the density of air using the ideal gas law.

Args:
temperature: Air temperature, [C]
molar_heat_capacity_air: Molar heat capacity of air, [J mol-1 C-1]
specific_heat_equ_factors: Factors in calculation of molar specific heat of air

air_temperature: Air temperature, [C]
atmospheric_pressure: Atmospheric pressure, [kPa]
specific_gas_constant_dry_air: Specific gas constant for dry air, [J kg-1 K-1]
celsius_to_kelvin: Factor to convert temperature in Celsius to absolute
temperature in Kelvin
Returns:
specific heat of air at constant pressure, [J mol-1 K-1]
density of air, [kg m-3].
"""
factor_1, factor_2 = specific_heat_equ_factors
return factor_1 * temperature**2 + factor_2 * temperature + molar_heat_capacity_air
# Convert temperature from Celsius to Kelvin
temperature_k = air_temperature + celsius_to_kelvin

# Calculate density using the ideal gas law
return (
atmospheric_pressure * 1000.0 / (temperature_k * specific_gas_constant_dry_air)
)


def calculate_latent_heat_vapourisation(
Expand Down
5 changes: 0 additions & 5 deletions virtual_ecosystem/models/abiotic/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,6 @@ class AbioticConsts(ConstantsDataclass):
assume here that the reference height is above the canopy, please check the input
data carefully and be aware of limitations."""

specific_heat_equ_factors: tuple[float, float] = 2e-05, 0.0002
"""Factors in calculation of molar specific heat of air.

Implementation after :cite:t:`maclean_microclimc_2021`."""

latent_heat_vap_equ_factors: tuple[float, float] = 1.91846e6, 33.91
"""Factors in calculation of latent heat of vapourisation.

Expand Down
Loading