diff --git a/tests/models/abiotic_simple/test_abiotic_simple_model.py b/tests/models/abiotic_simple/test_abiotic_simple_model.py index 6f8ab159d..902dcd23b 100644 --- a/tests/models/abiotic_simple/test_abiotic_simple_model.py +++ b/tests/models/abiotic_simple/test_abiotic_simple_model.py @@ -73,11 +73,10 @@ def test_abiotic_simple_model_initialization( @pytest.mark.parametrize( - "cfg_string,satvap1,raises,expected_log_entries", + "cfg_string,raises,expected_log_entries", [ pytest.param( "[core.timing]\nupdate_interval = '1 week'\n[abiotic_simple]\n", - [0.61078, 7.5, 237.3], does_not_raise(), tuple( [ @@ -94,8 +93,7 @@ def test_abiotic_simple_model_initialization( pytest.param( "[core.timing]\nupdate_interval = '1 week'\n" "[abiotic_simple.constants]\n" - "saturation_vapour_pressure_factors = [1.0, 2.0, 3.0]\n", - [1.0, 2.0, 3.0], + "initial_net_radiation = 20\n", does_not_raise(), tuple( [ @@ -115,7 +113,6 @@ def test_generate_abiotic_simple_model( caplog, dummy_climate_data_varying_canopy, cfg_string, - satvap1, raises, expected_log_entries, ): diff --git a/tests/models/abiotic_simple/test_microclimate_simple.py b/tests/models/abiotic_simple/test_microclimate_simple.py index 924948403..66263278b 100644 --- a/tests/models/abiotic_simple/test_microclimate_simple.py +++ b/tests/models/abiotic_simple/test_microclimate_simple.py @@ -72,7 +72,6 @@ def test_varying_canopy_calculate_vapour_pressure_deficit( def test_run_microclimate_varying_canopy( dummy_climate_data_varying_canopy, fixture_core_components, - fixture_abiotic_constants, fixture_core_constants, fixture_abiotic_simple_configuration, ): @@ -89,8 +88,7 @@ def test_run_microclimate_varying_canopy( data=data, layer_structure=lyr_strct, time_index=0, - simple_constants=fixture_abiotic_simple_configuration.constants, - abiotic_constants=fixture_abiotic_constants, + constants=fixture_abiotic_simple_configuration.constants, core_constants=fixture_core_constants, bounds=fixture_abiotic_simple_configuration.bounds, ) diff --git a/virtual_ecosystem/models/abiotic/abiotic_model.py b/virtual_ecosystem/models/abiotic/abiotic_model.py index a38825d33..98cd28bce 100644 --- a/virtual_ecosystem/models/abiotic/abiotic_model.py +++ b/virtual_ecosystem/models/abiotic/abiotic_model.py @@ -27,11 +27,7 @@ class as a child of the :class:`~virtual_ecosystem.core.base_model.BaseModel` cl calculate_vapour_pressure_deficit, run_simple_microclimate, ) -from virtual_ecosystem.models.abiotic_simple.model_config import ( - AbioticSimpleBounds, - AbioticSimpleConfiguration, - AbioticSimpleConstants, -) +from virtual_ecosystem.models.abiotic_simple.model_config import AbioticSimpleBounds class AbioticModel( @@ -120,10 +116,9 @@ def __init__( self.model_constants: AbioticConstants """Set of constants for the abiotic model.""" - self.simple_constants: AbioticSimpleConstants - """Set of constants for simple abiotic model.""" - self.simple_bounds: AbioticSimpleBounds - """Set of bound for simple abiotic model.""" + self.bounds: AbioticSimpleBounds + """A set of bounds on microclimates variables, used with both the simple model + of the initial state and the full energy balance calculations.""" self.pyrealm_constants: PyrealmConst """Pyrealm constants.""" @@ -152,8 +147,7 @@ def from_config( "abiotic", AbioticConfiguration ) - # Hard coding these here until we figure out how to resolve config issues - abiotic_simple_configuration = AbioticSimpleConfiguration() + # Hard coding this until we figure out how to pass in. pyrealm_consts = PyrealmConst() LOGGER.info( @@ -164,15 +158,15 @@ def from_config( data=data, core_components=core_components, static=model_configuration.static, - model_constants=model_configuration.constants, - simple_config=abiotic_simple_configuration, + constants=model_configuration.constants, + bounds=model_configuration.bounds, pyrealm_consts=pyrealm_consts, ) def _setup( self, model_constants: AbioticConstants = AbioticConstants(), - simple_config: AbioticSimpleConfiguration = AbioticSimpleConfiguration(), + bounds: AbioticSimpleBounds = AbioticSimpleBounds(), pyrealm_constants: PyrealmConst = PyrealmConst(), **kwargs, ) -> None: @@ -185,14 +179,13 @@ def _setup( Args: model_constants: Set of constants for the abiotic model. - simple_config: Configuration options for the abiotic simple model. + bounds: A set of bounds to be applied to abiotic variables. pyrealm_constants: Additional configuration options to the pyrealm package. **kwargs: Further arguments to the setup method. """ self.model_constants = model_constants - self.simple_constants = simple_config.constants - self.simple_bounds = simple_config.bounds + self.bounds = bounds self.pyrealm_constants = pyrealm_constants # create soil temperature array @@ -219,10 +212,9 @@ def _setup( data=self.data, layer_structure=self.layer_structure, time_index=0, - simple_constants=self.simple_constants, - abiotic_constants=self.model_constants, + constants=self.model_constants, core_constants=self.core_constants, - bounds=self.simple_bounds, + bounds=self.bounds, ) # Generate initial profiles of canopy temperature and heat fluxes from soil and @@ -260,7 +252,7 @@ def _update(self, time_index: int, **kwargs: Any) -> None: abiotic_constants=self.model_constants, core_constants=self.core_constants, pyrealm_const=self.pyrealm_constants, - abiotic_bounds=self.simple_bounds, + abiotic_bounds=self.bounds, ) self.data.add_from_dict(output_dict=update_dict) diff --git a/virtual_ecosystem/models/abiotic/model_config.py b/virtual_ecosystem/models/abiotic/model_config.py index e7423e04f..dd334f1d6 100644 --- a/virtual_ecosystem/models/abiotic/model_config.py +++ b/virtual_ecosystem/models/abiotic/model_config.py @@ -5,20 +5,17 @@ simulation. """ # noqa: D205 -from virtual_ecosystem.core.configuration import Configuration, ModelConfigurationRoot +from virtual_ecosystem.core.configuration import ModelConfigurationRoot +from virtual_ecosystem.models.abiotic_simple.model_config import ( + AbioticSharedConstants, + AbioticSimpleBounds, + AbioticSimpleConstants, +) -class AbioticConstants(Configuration): +class AbioticConstants(AbioticSharedConstants): """Dataclass to store all constants for the `abiotic` model.""" - leaf_emissivity: float = 0.98 - """Leaf emissivity, unitless. - - Leaf emissivity is a measure of how efficiently a leaf emits thermal radiation - compared to a perfect blackbody, typically ranging from 0.95 to 0.99. Value for - tropical vegetation is taken from :cite:t:`ma_an_2019`. - """ - leaf_albedo: float = 0.15 """Leaf albedo, unitless. @@ -142,14 +139,6 @@ class AbioticConstants(Configuration): The value is takes from a study that compares changes in surface albedo before and after deforestation in South East Asia :cite:p:`wilson_role_2020`.""" - soil_emissivity: float = 0.95 - """Soil emissivity, dimensionless. - - Soil emissivity is a measure of how efficiently the soil surface emits thermal - radiation compared to a perfect blackbody, with values typically ranging from 0.90 - to 0.98 depending on soil texture, moisture, and surface roughness. Value taken - from :cite:t:`molders_plant_2005`.""" - saturated_pressure_slope_parameters: tuple[float, float, float, float] = ( 4098.0, 0.6108, @@ -174,3 +163,10 @@ class AbioticConfiguration(ModelConfigurationRoot): constants: AbioticConstants = AbioticConstants() """Constants for the abiotic model""" + + bounds: AbioticSimpleBounds = AbioticSimpleBounds() + """Bounds for abiotic variables.""" + + simple_constants: AbioticSimpleConstants = AbioticSimpleConstants() + """Constants used for the simple abiotic model, used to set up initial + conditions.""" diff --git a/virtual_ecosystem/models/abiotic_simple/abiotic_simple_model.py b/virtual_ecosystem/models/abiotic_simple/abiotic_simple_model.py index 73a1c7468..b95f2e727 100644 --- a/virtual_ecosystem/models/abiotic_simple/abiotic_simple_model.py +++ b/virtual_ecosystem/models/abiotic_simple/abiotic_simple_model.py @@ -19,7 +19,6 @@ class as a child of the :class:`~virtual_ecosystem.core.base_model.BaseModel` cl from virtual_ecosystem.core.core_components import CoreComponents from virtual_ecosystem.core.data import Data from virtual_ecosystem.core.logger import LOGGER -from virtual_ecosystem.models.abiotic.model_config import AbioticConstants from virtual_ecosystem.models.abiotic_simple.microclimate_simple import ( calculate_vapour_pressure_deficit, run_simple_microclimate, @@ -101,8 +100,6 @@ def __init__( """Set of constants for the abiotic simple model""" self.bounds: AbioticSimpleBounds """Upper and lower bounds for abiotic variables.""" - self.abiotic_constants: AbioticConstants - """Abiotic constants required for some calculations""" @classmethod def from_config( @@ -131,9 +128,6 @@ def from_config( ) ) - # Hard coding abiotic constants here until we resolve the config setup - abiotic_constants: AbioticConstants = AbioticConstants() - LOGGER.info( "Information required to initialise the abiotic simple model successfully " "extracted." @@ -143,13 +137,11 @@ def from_config( core_components=core_components, static=model_configuration.static, model_configuration=model_configuration, - abiotic_constants=abiotic_constants, ) def _setup( self, model_configuration: AbioticSimpleConfiguration, - abiotic_constants: AbioticConstants, **kwargs, ) -> None: """Function to set up the abiotic simple model. @@ -166,7 +158,6 @@ def _setup( # Populate model attributes self.model_constants = model_configuration.constants self.bounds = model_configuration.bounds - self.abiotic_constants = abiotic_constants # create soil temperature array self.data["soil_temperature"] = self.layer_structure.from_template() @@ -207,8 +198,7 @@ def _update(self, time_index: int, **kwargs: Any) -> None: data=self.data, layer_structure=self.layer_structure, time_index=time_index, - simple_constants=self.model_constants, - abiotic_constants=self.abiotic_constants, + constants=self.model_constants, core_constants=self.core_constants, bounds=self.bounds, ) diff --git a/virtual_ecosystem/models/abiotic_simple/microclimate_simple.py b/virtual_ecosystem/models/abiotic_simple/microclimate_simple.py index 35d5636a8..cd53f08c5 100644 --- a/virtual_ecosystem/models/abiotic_simple/microclimate_simple.py +++ b/virtual_ecosystem/models/abiotic_simple/microclimate_simple.py @@ -33,8 +33,7 @@ def run_simple_microclimate( data: Data, layer_structure: LayerStructure, time_index: int, # could be datetime? - simple_constants: AbioticSimpleConstants, - abiotic_constants: AbioticConstants, + constants: AbioticSimpleConstants | AbioticConstants, core_constants: CoreConstants, bounds: AbioticSimpleBounds, ) -> dict[str, DataArray]: @@ -88,8 +87,7 @@ def run_simple_microclimate( data: Data object layer_structure: The LayerStructure instance for the simulation. time_index: Time index, integer - simple_constants: Set of constants for the abiotic simple model - abiotic_constants: Set of constants for the abiotic model + constants: Set of constants for the abiotic simple model core_constants: Set of constants shared across all models bounds: Upper and lower allowed values for vertical profiles, used to constrain log interpolation. Note that currently no conservation of water and energy! @@ -160,14 +158,14 @@ def run_simple_microclimate( # Calculate net radiation, [W m-2]. canopy_longwave_emission = energy_balance.calculate_longwave_emission( temperature=canopy_temperature.to_numpy(), - emissivity=abiotic_constants.leaf_emissivity, + emissivity=constants.leaf_emissivity, stefan_boltzmann=core_constants.stefan_boltzmann_constant, ) soil_longwave_emission = energy_balance.calculate_longwave_emission( temperature=output["soil_temperature"][ layer_structure.index_topsoil_scalar ].to_numpy(), - emissivity=abiotic_constants.soil_emissivity, + emissivity=constants.soil_emissivity, stefan_boltzmann=core_constants.stefan_boltzmann_constant, ) diff --git a/virtual_ecosystem/models/abiotic_simple/model_config.py b/virtual_ecosystem/models/abiotic_simple/model_config.py index a90ac4851..04e9862d4 100644 --- a/virtual_ecosystem/models/abiotic_simple/model_config.py +++ b/virtual_ecosystem/models/abiotic_simple/model_config.py @@ -3,15 +3,32 @@ from virtual_ecosystem.core.configuration import Configuration, ModelConfigurationRoot -class AbioticSimpleConstants(Configuration): - """Dataclass to store all constants for the `abiotic_simple` model.""" +class AbioticSharedConstants(Configuration): + """Shared abiotic constants. + + This is a pydantic basemodel to hold constants shared between the `abiotic` and + `abiotic_simple` models. + """ + + leaf_emissivity: float = 0.98 + """Leaf emissivity, unitless. + + Leaf emissivity is a measure of how efficiently a leaf emits thermal radiation + compared to a perfect blackbody, typically ranging from 0.95 to 0.99. Value for + tropical vegetation is taken from :cite:t:`ma_an_2019`. + """ + + soil_emissivity: float = 0.95 + """Soil emissivity, dimensionless. + + Soil emissivity is a measure of how efficiently the soil surface emits thermal + radiation compared to a perfect blackbody, with values typically ranging from 0.90 + to 0.98 depending on soil texture, moisture, and surface roughness. Value taken + from :cite:t:`molders_plant_2005`.""" - saturation_vapour_pressure_factors: tuple[float, float, float] = ( - 0.61078, - 7.5, - 237.3, - ) - """Factors for saturation vapour pressure calculation.""" + +class AbioticSimpleConstants(AbioticSharedConstants): + """Dataclass to store all constants for the `abiotic_simple` model.""" initial_net_radiation: float = 10.0 """Initial value for net radiation per layer, W m-2.