Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
089f14a
hourly time_interval in vpd update
vgro Jul 31, 2025
2ac50bc
corrected sign of energy fluxes in residual
vgro Aug 5, 2025
d080eba
adjust vertical mixing to work with NaN
vgro Aug 5, 2025
d322a10
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
vgro Aug 5, 2025
2f871ee
latvap indexed correctly in newton, added todos
vgro Aug 6, 2025
a428b08
fixed temp by adjusting input data to time step, limit fluxes to avoi…
vgro Aug 6, 2025
ceea066
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
vgro Aug 6, 2025
dd4309b
docs fixed
vgro Aug 6, 2025
aed7519
Merge branches '959-sort-out-time-interval-in-energy-balance' and '95…
vgro Aug 6, 2025
d2c8cf0
added more information on vertical mixing
vgro Aug 7, 2025
b16b44c
Array based mixing
davidorme Aug 7, 2025
d9fee3d
updated vertical mixing
vgro Aug 11, 2025
3471252
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
vgro Aug 11, 2025
380c283
limits added to avoid overshoot, need to go in central pace
vgro Aug 12, 2025
1b1211e
updated bounds and specific humidity
vgro Aug 13, 2025
244b651
Merge branch '959-sort-out-time-interval-in-energy-balance' of https:…
vgro Aug 13, 2025
52451cd
replaced calculation of satvap with pyrealm implementation
vgro Aug 13, 2025
decc6d8
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
vgro Aug 13, 2025
00e69e6
prevent overshoot in all layers
vgro Aug 14, 2025
4a5d87d
simplified handling of overshoot
vgro Aug 18, 2025
092710e
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
vgro Aug 18, 2025
757b060
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
vgro Aug 20, 2025
c61be07
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
vgro Aug 20, 2025
7cc5840
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
davidorme Aug 27, 2025
b954767
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
davidorme Aug 27, 2025
81e0a5d
Clamping function
davidorme Aug 27, 2025
34aa308
Merge branch 'develop' into 959-sort-out-time-interval-in-energy-balance
vgro Sep 1, 2025
4e28166
Merge pull request #1011 from ImperialCollegeLondon/959-clamping
vgro Sep 1, 2025
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
71 changes: 40 additions & 31 deletions tests/models/abiotic/test_abiotic_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,40 +369,49 @@ def test_setup_abiotic_model(

model.update(time_index=0)

expected_soil_temp1 = lyr_strct.from_template()
expected_soil_temp1[lyr_strct.index_all_soil] = np.array(
[
[22.3935, 24.064858, 25.343753, 25.343753],
[20.040154, 20.068193, 20.089648, 20.089648],
],
# Check that values fall within a reasonable expected range
soil_temps = model.data["soil_temperature"].isel(layers=lyr_strct.index_all_soil)

# To test with varying canopy layers, need to mask
canopy_mask = ~np.isnan(
dummy_climate_data_varying_canopy["canopy_temperature"].isel(
layers=lyr_strct.index_filled_canopy
)
)
expected_soil_moist = lyr_strct.from_template()
expected_soil_moist[lyr_strct.index_all_soil] = np.array([5.0, 500])[:, None]
xr.testing.assert_allclose(
model.data["soil_temperature"], expected_soil_temp1, rtol=1e-3
atm_mask = ~np.isnan(
dummy_climate_data_varying_canopy["air_temperature"].isel(
layers=lyr_strct.index_filled_atmosphere
)
)
xr.testing.assert_allclose(model.data["soil_moisture"], expected_soil_moist)

exp_air_temp = lyr_strct.from_template()

exp_air_temp[lyr_strct.index_filled_atmosphere] = np.array(
[
[30.000105, 30.000091, 30.000047, 30.000047],
[29.925696, 29.938505, 29.970583, 29.970583],
[29.42155, 29.613321, np.nan, np.nan],
[28.558176, np.nan, np.nan, np.nan],
[23.158518, 26.130326, 29.416104, 29.416104],
]
canopy_temp_result = model.data["canopy_temperature"].isel(
layers=lyr_strct.index_filled_canopy
)
xr.testing.assert_allclose(model.data["air_temperature"], exp_air_temp)

exp_canopytemp = lyr_strct.from_template()
exp_canopytemp[lyr_strct.index_filled_canopy] = np.array(
[
[28.489317, 31.736854, 31.631768, 31.631768],
[29.414784, 29.609833, np.nan, np.nan],
[28.551829, np.nan, np.nan, np.nan],
]
air_temp_result = model.data["air_temperature"].isel(
layers=lyr_strct.index_filled_atmosphere
)
rel_hum_result = model.data["relative_humidity"].isel(
layers=lyr_strct.index_filled_atmosphere
)

xr.testing.assert_allclose(model.data["canopy_temperature"], exp_canopytemp)
# Use the mask as a DataArray for .where()
valid_values_can_temp = canopy_temp_result.where(canopy_mask)
valid_values_air_temp = air_temp_result.where(atm_mask)
valid_values_rel_hum = rel_hum_result.where(atm_mask)

# Now drop the NaNs (i.e., masked values)
valid_values_can_temp_clean = valid_values_can_temp.dropna(dim="layers", how="any")
valid_values_air_temp_clean = valid_values_air_temp.dropna(dim="layers", how="any")
valid_values_rel_hum_clean = valid_values_rel_hum.dropna(dim="layers", how="any")

# Now do the test
assert ((soil_temps >= 18.0) & (soil_temps <= 28.0)).all()
assert (
(valid_values_can_temp_clean >= 15.0) & (valid_values_can_temp_clean <= 40.0)
).all()
assert (
(valid_values_air_temp_clean >= 15.0) & (valid_values_air_temp_clean <= 40.0)
).all()
assert (
(valid_values_rel_hum_clean >= 0.0) & (valid_values_rel_hum_clean <= 100.0)
).all()
32 changes: 32 additions & 0 deletions tests/models/abiotic/test_abiotic_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,35 @@ def test_compute_layer_thickness_for_varying_canopy(
)

np.testing.assert_allclose(result, exp_result)


def test_calculate_specific_humidity(
dummy_climate_data_varying_canopy, fixture_core_components
):
"""Test specific humidity."""

from virtual_ecosystem.models.abiotic.abiotic_tools import (
calculate_specific_humidity,
)

data = dummy_climate_data_varying_canopy
atm_index = fixture_core_components.layer_structure.index_filled_atmosphere

result = calculate_specific_humidity(
air_temperature=data["air_temperature"][atm_index].to_numpy(),
relative_humidity=data["relative_humidity"][atm_index].to_numpy(),
atmospheric_pressure=data["atmospheric_pressure"][atm_index].to_numpy(),
molecular_weight_ratio_water_to_dry_air=0.622,
pyrealm_const=PyrealmConst(),
)

exp_result = np.array(
[
[0.025064, 0.025064, 0.025064, 0.025064],
[0.024934, 0.024934, 0.024934, 0.024934],
[0.02412, 0.02412, np.nan, np.nan],
[0.02274, np.nan, np.nan, np.nan],
[0.01638, 0.01638, 0.01638, 0.01638],
]
)
np.testing.assert_allclose(result, exp_result, rtol=1e-4, atol=1e-4)
2 changes: 1 addition & 1 deletion tests/models/abiotic/test_energy_balance.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,12 +415,12 @@ def test_update_humidity_vpd(
density_air=data["density_air"][atm_index].to_numpy(),
mixing_coefficient=mixing_coefficient,
ventilation_rate=ventilation_rate,
wind_speed=data["wind_speed_ref"].isel(time_index=0).to_numpy(),
molecular_weight_ratio_water_to_dry_air=(
CoreConsts.molecular_weight_ratio_water_to_dry_air
),
dry_air_factor=1 - CoreConsts.molecular_weight_ratio_water_to_dry_air,
cell_area=fixture_core_components.grid.cell_area,
limits=(0, 60),
time_interval=time_interval,
)

Expand Down
Loading
Loading