From 31808b887b05b3bbe33398fe8c51b3414503d663 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Fri, 1 Mar 2024 17:09:51 +0000 Subject: [PATCH 01/13] Apply overrides on residence times --- wsimod/nodes/land.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/wsimod/nodes/land.py b/wsimod/nodes/land.py index f34fb75..f4a3d3c 100644 --- a/wsimod/nodes/land.py +++ b/wsimod/nodes/land.py @@ -11,6 +11,7 @@ from wsimod.nodes.nodes import DecayTank, Node, ResidenceTank from wsimod.nodes.nutrient_pool import NutrientPool +from typing import Any, Dict class Land(Node): """""" @@ -158,7 +159,31 @@ def __init__( self.mass_balance_ds.append(self.surface_runoff.ds) self.mass_balance_ds.append(self.subsurface_runoff.ds) self.mass_balance_ds.append(self.percolation.ds) + + def apply_overrides(self, overrides=Dict[str, Any]): + """Apply overrides to the Land. + Enables a user to override any parameter of the residence_time and update + the residence_tank accordingly. + + Args: + overrides (Dict[str, Any]): Dict describing which parameters should + be overridden (keys) and new values (values). Defaults to {}. + """ + self.surface_residence_time = overrides.pop( + "surface_residence_time", + self.surface_residence_time) + self.subsurface_residence_time = overrides.pop( + "subsurface_residence_time", + self.subsurface_residence_time) + self.percolation_residence_time = overrides.pop( + "percolation_residence_time", + self.percolation_residence_time) + self.surface_runoff.residence_time = self.surface_residence_time + self.subsurface_runoff.residence_time = self.subsurface_residence_time + self.percolation.residence_time = self.percolation_residence_time + super().apply_overrides(overrides) + def apply_irrigation(self): """Iterate over any irrigation functions (needs further testing.. From bd7e9bdd901651794844b3afc5c8d46ccc9f4ec7 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Sat, 2 Mar 2024 11:27:23 +0000 Subject: [PATCH 02/13] Apply overrides to Surface --- wsimod/nodes/land.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/wsimod/nodes/land.py b/wsimod/nodes/land.py index f4a3d3c..ae1265f 100644 --- a/wsimod/nodes/land.py +++ b/wsimod/nodes/land.py @@ -375,7 +375,32 @@ def __init__( self.inflows.append(self.simple_deposition) self.processes = [] self.outflows = [] + + def apply_overrides(self, overrides = Dict[str, Any]): + """Override parameters. + + Enables a user to override any of the following parameters: + area and depth (both will update the capacity), pollutant_load (the + entire dict does not need to be redefined, only changed values need to + be included). + Args: + overrides (Dict[str, Any]): Dict describing which parameters should + be overridden (keys) and new values (values). Defaults to {}. + """ + self.surface = overrides.pop("surface", + self.surface) + self.area = overrides.pop("area", + self.area) + self.depth = overrides.pop("depth", + self.depth) + self.capacity = self.area * self.depth + + pollutant_load = overrides.pop("pollutant_load", {}) + for key, value in pollutant_load.items(): + self.pollutant_load[key].update(value) + super().apply_overrides(overrides) + def run(self): """Call run function (called from Land node).""" if "nitrite" in constants.POLLUTANTS: From 5c9d86619c43afcb81328cef6d4a20a17073e262 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Sat, 2 Mar 2024 14:49:53 +0000 Subject: [PATCH 03/13] Apply overrides in ImperviousSurface as well as remove redundant super().apply_overrides(overrides) in Surface --- wsimod/nodes/land.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/wsimod/nodes/land.py b/wsimod/nodes/land.py index ae1265f..2677374 100644 --- a/wsimod/nodes/land.py +++ b/wsimod/nodes/land.py @@ -399,7 +399,6 @@ def apply_overrides(self, overrides = Dict[str, Any]): pollutant_load = overrides.pop("pollutant_load", {}) for key, value in pollutant_load.items(): self.pollutant_load[key].update(value) - super().apply_overrides(overrides) def run(self): """Call run function (called from Land node).""" @@ -598,7 +597,28 @@ def __init__(self, pore_depth=0, et0_to_e=1, **kwargs): self.inflows.append(self.precipitation_evaporation) self.outflows.append(self.push_to_sewers) + + def apply_overrides(self, overrides = Dict[str, Any]): + """Override parameters. + Enables a user to override any of the following parameters: + eto_to_e, pore_depth. + + Args: + overrides (Dict[str, Any]): Dict describing which parameters should + be overridden (keys) and new values (values). Defaults to {}. + """ + self.et0_to_e = overrides.pop("et0_to_e", + self.et0_to_e) + if 'depth' in overrides.keys(): + overrides.pop('depth') + print('ERROR: specifying depth is depreciated in overrides for impervious surface, please specify pore_depth instead') + self.pore_depth = overrides.pop("pore_depth", + self.pore_depth) + self.depth = self.pore_depth + self.capacity = self.area * self.depth + super().apply_overrides(overrides) + def precipitation_evaporation(self): """Inflow function that is a simple rainfall-evaporation model, updating the. From a6d1fb8f83301227d2c2b9fd9c308cbacd66d0bd Mon Sep 17 00:00:00 2001 From: liuly12 Date: Sun, 3 Mar 2024 09:58:43 +0000 Subject: [PATCH 04/13] Apply overrides for PerviousSurface and add a warning of residual override attributes in Surface --- wsimod/nodes/land.py | 55 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/wsimod/nodes/land.py b/wsimod/nodes/land.py index 2677374..e2cae31 100644 --- a/wsimod/nodes/land.py +++ b/wsimod/nodes/land.py @@ -399,6 +399,9 @@ def apply_overrides(self, overrides = Dict[str, Any]): pollutant_load = overrides.pop("pollutant_load", {}) for key, value in pollutant_load.items(): self.pollutant_load[key].update(value) + + if len(overrides) > 0: + print(f"No override behaviour defined for: {overrides.keys()}") def run(self): """Call run function (called from Land node).""" @@ -816,7 +819,59 @@ def __init__( # temperature... probably need to just give it the decay function self.outflows.append(self.route) + + def apply_overrides(self, overrides = Dict[str, Any]): + """Override parameters. + Enables a user to override any of the following parameters: + field_capacity, wilting_point, total_porosity, infiltration_capacity, + surface_coefficient, percolation_coefficient, et0_coefficient, ihacres_p, + soil_temp_w_prev, soil_temp_w_air, soil_temp_w_deep, soil_temp_deep, + and the corresponding parameter values, including field_capacity_m, + wilting_point_m, depth, capacity, subsurface_coefficient. + + Args: + overrides (Dict[str, Any]): Dict describing which parameters should + be overridden (keys) and new values (values). Defaults to {}. + """ + + self.depth /= self.total_porosity # restore the physical depth (root) + + self.field_capacity = overrides.pop("field_capacity", + self.field_capacity) + self.wilting_point = overrides.pop("wilting_point", + self.wilting_point) + self.total_porosity = overrides.pop("total_porosity", + self.total_porosity) + + self.infiltration_capacity = overrides.pop("infiltration_capacity", + self.infiltration_capacity) + self.surface_coefficient = overrides.pop("surface_coefficient", + self.surface_coefficient) + self.percolation_coefficient = overrides.pop("percolation_coefficient", + self.percolation_coefficient) + self.subsurface_coefficient = 1 - self.percolation_coefficient + self.et0_coefficient = overrides.pop("et0_coefficient", + self.et0_coefficient) + self.ihacres_p = overrides.pop("ihacres_p", + self.ihacres_p) + + self.soil_temp_w_prev = overrides.pop("soil_temp_w_prev", + self.soil_temp_w_prev) + self.soil_temp_w_air = overrides.pop("soil_temp_w_air", + self.soil_temp_w_air) + self.soil_temp_w_deep = overrides.pop("soil_temp_w_deep", + self.soil_temp_w_deep) + self.soil_temp_deep = overrides.pop("soil_temp_deep", + self.soil_temp_deep) + + super().apply_overrides(overrides) + self.field_capacity_m = self.field_capacity * self.depth + self.wilting_point_m = self.wilting_point * self.depth + # must after the depth has been changed + self.depth *= self.total_porosity # update the simulation depth + self.capacity = self.depth * self.area + def get_cmd(self): """Calculate moisture deficit (i.e., the tank excess converted to depth). From 398a3eecb7a824e81b4339c60dc2fabc1ddecc73 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:28:22 +0000 Subject: [PATCH 05/13] Apply overrides for growingsurface --- wsimod/nodes/land.py | 151 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 128 insertions(+), 23 deletions(-) diff --git a/wsimod/nodes/land.py b/wsimod/nodes/land.py index e2cae31..dc13130 100644 --- a/wsimod/nodes/land.py +++ b/wsimod/nodes/land.py @@ -1239,22 +1239,7 @@ def __init__( self.bulk_density = 1300 # [kg/m3] super().__init__(depth=depth, **kwargs) - # Infer basic sow/harvest calendar - self.harvest_sow_calendar = [ - 0, - self.sowing_day, - self.harvest_day, - self.harvest_day + 1, - 365, - ] - self.ground_cover_stages = [0, 0, self.ground_cover_max, 0, 0] - self.crop_cover_stages = [0, 0, self.crop_cover_max, 0, 0] - - # Use day number of 181 to indicate autumn-sown (from HYPE) - if self.sowing_day > 181: - self.autumn_sow = True - else: - self.autumn_sow = False + self.harvest_sow_calendar, self.ground_cover_stages, self.crop_cover_stages, self.autumn_sow = self.infer_sow_harvest_calendar() # State variables self.days_after_sow = None @@ -1263,13 +1248,7 @@ def __init__( self.crop_factor = 0 self.et0_coefficient = 1 - # Calculate parameters based on capacity/wp - self.total_available_water = self.field_capacity_m - self.wilting_point_m - if self.total_available_water < 0: - print("warning: TAW < 0...") - self.readily_available_water = ( - self.total_available_water * self.ET_depletion_factor - ) + self.total_available_water, self.readily_available_water = self.calculate_available_water() # Initiliase nutrient pools self.nutrient_pool = NutrientPool() @@ -1324,7 +1303,133 @@ def __init__( self.nutrient_pool.fast_pool.storage["P"] = initial_soil_storage[ "org-phosphorus" ] + + def infer_sow_harvest_calendar(self): + """Infer basic sow/harvest calendar and indicate autumn-sown. + Returns: + (list): havest/sow calendar + (list): ground cover stages + (list): crop cover stages + (boolean): indication for autumn-sown crops + """ + # Infer basic sow/harvest calendar + harvest_sow_calendar = [ + 0, + self.sowing_day, + self.harvest_day, + self.harvest_day + 1, + 365, + ] + ground_cover_stages = [0, 0, self.ground_cover_max, 0, 0] + crop_cover_stages = [0, 0, self.crop_cover_max, 0, 0] + + # Use day number of 181 to indicate autumn-sown (from HYPE) + if self.sowing_day > 181: + autumn_sow = True + else: + autumn_sow = False + + return harvest_sow_calendar, ground_cover_stages, crop_cover_stages, autumn_sow + + def calculate_available_water(self): + """Calculate total/readily available water based on capacity/wp. + Returns: + (float): total available water + (float): readily available water + """ + # Calculate parameters based on capacity/wp + total_available_water = self.field_capacity_m - self.wilting_point_m + if total_available_water < 0: + print("warning: TAW < 0...") + readily_available_water = ( + total_available_water * self.ET_depletion_factor + ) + + return total_available_water, readily_available_water + + def apply_overrides(self, overrides = Dict[str, Any]): + """Override parameters. + + Enables a user to override any of the following parameters + Args: + overrides (Dict[str, Any]): Dict describing which parameters should + be overridden (keys) and new values (values). Defaults to {}. + """ + self.ET_depletion_factor = overrides.pop("ET_depletion_factor", + self.ET_depletion_factor) + self.crop_cover_max = overrides.pop("crop_cover_max", + self.crop_cover_max) + self.ground_cover_max = overrides.pop("ground_cover_max", + self.ground_cover_max) + self.crop_factor_stages = overrides.pop("crop_factor_stages", + self.crop_factor_stages) + self.crop_factor_stage_dates = overrides.pop("crop_factor_stage_dates", + self.crop_factor_stage_dates) + self.sowing_day = overrides.pop("sowing_day", + self.sowing_day) + self.harvest_day = overrides.pop("harvest_day", + self.harvest_day) + self.satact = overrides.pop("satact", + self.satact) + self.thetaupp = overrides.pop("thetaupp", + self.thetaupp) + self.thetalow = overrides.pop("thetalow", + self.thetalow) + self.thetapow = overrides.pop("thetapow", + self.thetapow) + self.uptake1 = overrides.pop("uptake1", + self.uptake1) + self.uptake2 = overrides.pop("uptake2", + self.uptake2) + self.uptake3 = overrides.pop("uptake3", + self.uptake3) + self.uptake_PNratio = overrides.pop("uptake_PNratio", + self.uptake_PNratio) + self.erodibility = overrides.pop("erodibility", + self.erodibility) + self.sreroexp = overrides.pop("sreroexp", + self.sreroexp) + self.cohesion = overrides.pop("cohesion", + self.cohesion) + self.slope = overrides.pop("slope", + self.slope) + self.srfilt = overrides.pop("srfilt", + self.srfilt) + self.macrofilt = overrides.pop("macrofilt", + self.macrofilt) + self.limpar = overrides.pop("limpar", + self.limpar) + self.exppar = overrides.pop("exppar", + self.exppar) + self.hsatINs = overrides.pop("hsatINs", + self.hsatINs) + self.denpar = overrides.pop("denpar", + self.denpar) + self.adosorption_nr_limit = overrides.pop("adosorption_nr_limit", + self.adosorption_nr_limit) + self.adsorption_nr_maxiter = overrides.pop("adsorption_nr_maxiter", + self.adsorption_nr_maxiter) + self.kfr = overrides.pop("kfr", + self.kfr) + self.nfr = overrides.pop("nfr", + self.nfr) + self.kadsdes = overrides.pop("kadsdes", + self.kadsdes) + self.bulk_density = overrides.pop("bulk_density", + self.bulk_density) + + if 'depth' in overrides.keys(): + overrides.pop('depth') + print('ERROR: specifying depth is depreciated in overrides for GrowingSurface, please specify rooting_depth instead') + self.rooting_depth = overrides.pop("rooting_depth", + self.rooting_depth) + overrides['depth'] = self.rooting_depth + super().apply_overrides(overrides) + + self.harvest_sow_calendar, self.ground_cover_stages, self.crop_cover_stages, self.autumn_sow = self.infer_sow_harvest_calendar() + self.total_available_water, self.readily_available_water = self.calculate_available_water() + def pull_storage(self, vqip): """Pull water from the surface, updating the surface storage VQIP. Nutrient pool pollutants (nitrate/nitrite/ammonia/phosphate/org- phosphorus/ org-nitrogen) are From 16d5bcab16266646a155e5f68686dc7329def939 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:29:26 +0000 Subject: [PATCH 06/13] Apply overrides in IrrigationSurface --- wsimod/nodes/land.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/wsimod/nodes/land.py b/wsimod/nodes/land.py index dc13130..5c984ef 100644 --- a/wsimod/nodes/land.py +++ b/wsimod/nodes/land.py @@ -2332,6 +2332,19 @@ def __init__(self, irrigation_coefficient=0.1, **kwargs): super().__init__(**kwargs) + def apply_overrides(self, overrides = Dict[str, Any]): + """Override parameters. + + Enables a user to override irrigation_coefficient + + Args: + overrides (Dict[str, Any]): Dict describing which parameters should + be overridden (keys) and new values (values). Defaults to {}. + """ + self.irrigation_coefficient = overrides.pop("irrigation_coefficient", + self.irrigation_coefficient) + super().apply_overrides(overrides) + def irrigate(self): """Calculate water demand for crops and call parent node to acquire water, updating surface tank and nutrient pools.""" From cede467a2b9f5adcb9105d629755c292d2acd741 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:30:48 +0000 Subject: [PATCH 07/13] Apply overrides in NutrientPool --- wsimod/nodes/nutrient_pool.py | 59 +++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/wsimod/nodes/nutrient_pool.py b/wsimod/nodes/nutrient_pool.py index 54590db..2d82e5c 100644 --- a/wsimod/nodes/nutrient_pool.py +++ b/wsimod/nodes/nutrient_pool.py @@ -4,7 +4,7 @@ @author: barna """ from wsimod.core import constants - +from typing import Any, Dict class NutrientPool: """""" @@ -114,14 +114,7 @@ def __init__( self.disfpar = disfpar self.immobdpar = immobdpar - self.fraction_manure_to_fast = { - x: 1 - self.fraction_manure_to_dissolved_inorganic[x] - for x in constants.NUTRIENTS - } - self.fraction_residue_to_humus = { - x: 1 - self.fraction_residue_to_fast[x] for x in constants.NUTRIENTS - } - self.fraction_dry_n_to_fast = 1 - self.fraction_dry_n_to_dissolved_inorganic + self.fraction_manure_to_fast, self.fraction_residue_to_humus, self.fraction_dry_n_to_fast = self.calculate_fraction_parameters() # Initialise different pools self.fast_pool = NutrientStore() @@ -136,7 +129,55 @@ def __init__( self.dissolved_organic_pool, self.adsorbed_inorganic_pool, ] + + def calculate_fraction_parameters(self): + ''' Update fractions of nutrients input transformed into other forms in soil + based on the input parameters + Returns: + (dict): fraction of manure to fast pool + (dict): fraction of plant residue to humus pool + (float): fraction of dry nitrogen deposition to fast pool + ''' + fraction_manure_to_fast = { + x: 1 - self.fraction_manure_to_dissolved_inorganic[x] + for x in constants.NUTRIENTS + } + fraction_residue_to_humus = { + x: 1 - self.fraction_residue_to_fast[x] for x in constants.NUTRIENTS + } + fraction_dry_n_to_fast = 1 - self.fraction_dry_n_to_dissolved_inorganic + + return fraction_manure_to_fast, fraction_residue_to_humus, fraction_dry_n_to_fast + + def apply_overrides(self, overrides = Dict[str, Any]): + """Override parameters. + Enables a user to override any of the following parameters: + eto_to_e, pore_depth. + + Args: + overrides (Dict[str, Any]): Dict describing which parameters should + be overridden (keys) and new values (values). Defaults to {}. + """ + self.fraction_manure_to_dissolved_inorganic = overrides.pop("fraction_manure_to_dissolved_inorganic", + self.fraction_manure_to_dissolved_inorganic) + self.fraction_residue_to_fast = overrides.pop("fraction_residue_to_fast", + self.fraction_residue_to_fast) + self.fraction_dry_n_to_dissolved_inorganic = overrides.pop("fraction_dry_n_to_dissolved_inorganic", + self.fraction_dry_n_to_dissolved_inorganic) + self.degrhpar = overrides.pop("degrhpar", + self.degrhpar) + self.dishpar = overrides.pop("dishpar", + self.dishpar) + self.minfpar = overrides.pop("minfpar", + self.minfpar) + self.disfpar = overrides.pop("disfpar", + self.disfpar) + self.immobdpar = overrides.pop("immobdpar", + self.immobdpar) + + self.fraction_manure_to_fast, self.fraction_residue_to_humus, self.fraction_dry_n_to_fast = self.calculate_fraction_parameters() + def init_empty(self): """Initialise an empty nutrient to be copied.""" self.empty_nutrient = {x: 0 for x in constants.NUTRIENTS} From 517813ba59b0d47b9cbf9506c235e5943384a480 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:32:13 +0000 Subject: [PATCH 08/13] Add test_land_overrides --- tests/test_land.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tests/test_land.py b/tests/test_land.py index f67279e..cd6d5fa 100644 --- a/tests/test_land.py +++ b/tests/test_land.py @@ -21,7 +21,7 @@ PerviousSurface, Surface, ) -from wsimod.nodes.nodes import Node +from wsimod.nodes.nodes import Node, DecayTank from wsimod.nodes.sewer import Sewer from wsimod.nodes.storage import Reservoir from wsimod.orchestration.model import to_datetime @@ -1168,6 +1168,19 @@ def test_garden(self): d3 = {"phosphate": 0, "temperature": 0, "volume": 0.2 * 0.5 * 1.5} self.assertDictAlmostEqual(d3, surface.storage, 16) - + def test_land_overrides(self): + constants.set_simple_pollutants() + node = Land(name="") + node.apply_overrides({'surface_residence_time': 4.9, + 'subsurface_residence_time': 23.7, + 'percolation_residence_time': 56.1 + }) + self.assertEqual(node.surface_residence_time, 4.9) + self.assertEqual(node.surface_runoff.residence_time, 4.9) + self.assertEqual(node.subsurface_residence_time, 23.7) + self.assertEqual(node.subsurface_runoff.residence_time, 23.7) + self.assertEqual(node.percolation_residence_time, 56.1) + self.assertEqual(node.percolation.residence_time, 56.1) + if __name__ == "__main__": unittest.main() From 187f8958ec226e6de2be75eac47f38dfa85a0219 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:32:57 +0000 Subject: [PATCH 09/13] Add test_surface_overrides --- tests/test_land.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/test_land.py b/tests/test_land.py index cd6d5fa..094ae46 100644 --- a/tests/test_land.py +++ b/tests/test_land.py @@ -1182,5 +1182,20 @@ def test_land_overrides(self): self.assertEqual(node.percolation_residence_time, 56.1) self.assertEqual(node.percolation.residence_time, 56.1) + def test_surface_overrides(self): + constants.set_default_pollutants() + decaytank = DecayTank() + surface = Surface(parent=decaytank, area=5, depth=0.1) + surface.apply_overrides({'surface': 'test_surface', + 'area': 9.8, + 'depth': 7.5, + 'pollutant_load': {'nitrate': 5.7, 'phosphate': 10.1} + }) + self.assertEqual(surface.surface, 'test_surface') + self.assertEqual(surface.area, 9.8) + self.assertEqual(surface.depth, 7.5) + self.assertEqual(surface.capacity, 9.8 * 7.5) + self.assertDictEqual(surface.pollutant_load, {'nitrate': 5.7, 'phosphate': 10.1}) + if __name__ == "__main__": unittest.main() From c5693652b6aca932f801247bdefdf7a77748fcf1 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:33:43 +0000 Subject: [PATCH 10/13] Add test_impervious_overrides --- tests/test_land.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_land.py b/tests/test_land.py index 094ae46..ca3c9c7 100644 --- a/tests/test_land.py +++ b/tests/test_land.py @@ -1197,5 +1197,23 @@ def test_surface_overrides(self): self.assertEqual(surface.capacity, 9.8 * 7.5) self.assertDictEqual(surface.pollutant_load, {'nitrate': 5.7, 'phosphate': 10.1}) + def test_impervioussurface_overrides(self): + constants.set_default_pollutants() + decaytank = DecayTank() + surface = Surface(parent=decaytank, area=5, depth=0.1) + impervioussurface = ImperviousSurface( + parent=surface, area=1.5, et0_to_e=0.9, pore_depth=0.015 + ) + impervioussurface.apply_overrides({'surface': 'test_surface', + 'area': 9.8, + 'pore_depth': 7.5, + 'et0_to_e': 3.5 + }) + self.assertEqual(impervioussurface.area, 9.8) + self.assertEqual(impervioussurface.pore_depth, 7.5) + self.assertEqual(impervioussurface.depth, 7.5) + self.assertEqual(impervioussurface.capacity, 9.8 * 7.5) + self.assertEqual(impervioussurface.et0_to_e, 3.5) + if __name__ == "__main__": unittest.main() From e383c557bca628142724cf34143a5e3f7557f9d0 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:34:36 +0000 Subject: [PATCH 11/13] Add test_pervioussurface_overrides --- tests/test_land.py | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/test_land.py b/tests/test_land.py index ca3c9c7..ab05260 100644 --- a/tests/test_land.py +++ b/tests/test_land.py @@ -1214,6 +1214,51 @@ def test_impervioussurface_overrides(self): self.assertEqual(impervioussurface.depth, 7.5) self.assertEqual(impervioussurface.capacity, 9.8 * 7.5) self.assertEqual(impervioussurface.et0_to_e, 3.5) + + def test_pervioussurface_overrides(self): + constants.set_default_pollutants() + decaytank = DecayTank() + surface = Surface(parent=decaytank, area=5, depth=0.1) + pervioussurface = PerviousSurface( + parent=surface, depth=0.5, area=1.5, initial_storage=0.5 * 1.5 * 0.25 + ) + pervioussurface.apply_overrides({'field_capacity': 0.335, + 'wilting_point': 0.112, + 'total_porosity': 0.476, + 'infiltration_capacity': 0.678, + 'surface_coefficient': 0.237, + 'percolation_coefficient': 0.777, + 'et0_coefficient': 0.697, + 'ihacres_p': 10.096, + 'soil_temp_w_prev': 37.1, + 'soil_temp_w_air': 23.6, + 'soil_temp_w_deep': 3.4, + 'soil_temp_deep': 2.2, + + 'surface': 'test_surface', + 'area': 9.8, + 'depth': 7.5 + }) + self.assertEqual(pervioussurface.field_capacity, 0.335) + self.assertEqual(pervioussurface.wilting_point, 0.112) + self.assertEqual(pervioussurface.total_porosity, 0.476) + self.assertEqual(pervioussurface.infiltration_capacity, 0.678) + self.assertEqual(pervioussurface.surface_coefficient, 0.237) + self.assertEqual(pervioussurface.percolation_coefficient, 0.777) + self.assertEqual(pervioussurface.et0_coefficient, 0.697) + self.assertEqual(pervioussurface.ihacres_p, 10.096) + self.assertEqual(pervioussurface.soil_temp_w_prev, 37.1) + self.assertEqual(pervioussurface.soil_temp_w_air, 23.6) + self.assertEqual(pervioussurface.soil_temp_w_deep, 3.4) + self.assertEqual(pervioussurface.soil_temp_deep, 2.2) + + self.assertEqual(pervioussurface.field_capacity_m, 0.335 * 7.5) + self.assertEqual(pervioussurface.wilting_point_m, 0.112 * 7.5) + self.assertEqual(pervioussurface.depth, 0.476 * 7.5) + self.assertEqual(pervioussurface.area, 9.8) + self.assertEqual(pervioussurface.capacity, 7.5 * 0.476 * 9.8) + self.assertEqual(pervioussurface.surface, 'test_surface') + self.assertEqual(pervioussurface.subsurface_coefficient, 1 - 0.777) if __name__ == "__main__": unittest.main() From 12360aaebb292a16f6a92d51a91e1a2f92f99d74 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:36:08 +0000 Subject: [PATCH 12/13] Add test_growingsurface_overrides --- tests/test_land.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/test_land.py b/tests/test_land.py index ab05260..bd76992 100644 --- a/tests/test_land.py +++ b/tests/test_land.py @@ -1260,5 +1260,78 @@ def test_pervioussurface_overrides(self): self.assertEqual(pervioussurface.surface, 'test_surface') self.assertEqual(pervioussurface.subsurface_coefficient, 1 - 0.777) + def test_growingsurface_overrides(self): + constants.set_default_pollutants() + decaytank = DecayTank() + surface = Surface(parent=decaytank, area=5, depth=0.1) + pervioussurface = PerviousSurface( + parent=surface, depth=0.5, area=1.5, initial_storage=0.5 * 1.5 * 0.25 + ) + growingsurface = GrowingSurface( + parent=pervioussurface, area = 1.5 + ) + overrides = {'ET_depletion_factor': 0.521, + 'crop_cover_max': 1.342, + 'ground_cover_max': 1.111, + 'crop_factor_stages': [1, 2, 1], + 'crop_factor_stage_dates': [1, 32, 90], + 'sowing_day': 35, + 'harvest_day': 89, + 'satact': 0.567, + 'thetaupp': 4.324, + 'thetalow': 3.582, + 'thetapow': 7.324, + 'uptake1': 1.278, + 'uptake2': 2.753, + 'uptake3': 5.298, + 'uptake_PNratio': 3.263, + 'erodibility': 2.863, + 'sreroexp': 5.634, + 'cohesion': 8.903, + 'slope': 6.231, + 'srfilt': 9.231, + 'macrofilt': 7.394, + 'limpar': 4.211, + 'exppar': 5.872, + 'hsatINs': 20.321, + 'denpar': 0.204, + 'adosorption_nr_limit': 1.943, + 'adsorption_nr_maxiter': 6321, + 'kfr': 80.2, + 'nfr': 42.3, + 'kadsdes': 0.972, + 'bulk_density': 1672, + + + 'field_capacity': 0.335, + 'wilting_point': 0.112, + 'total_porosity': 0.476, + + 'rooting_depth': 7.5 + } + growingsurface.apply_overrides(overrides) + + for k, v in overrides.items(): + if isinstance(v, list): + self.assertListEqual(getattr(growingsurface, k), v) + else: + self.assertEqual(getattr(growingsurface, k), v) + + harvest_sow_calendar = [ + 0, + 35, + 89, + 89 + 1, + 365, + ] + self.assertListEqual(growingsurface.harvest_sow_calendar, harvest_sow_calendar) + self.assertListEqual(growingsurface.ground_cover_stages, [0, 0, 1.111, 0, 0]) + self.assertListEqual(growingsurface.crop_cover_stages, [0, 0, 1.342, 0, 0]) + self.assertEqual(growingsurface.autumn_sow, False) + self.assertEqual(growingsurface.total_available_water, (0.335 - 0.112) * 7.5) + self.assertEqual(growingsurface.readily_available_water, (0.335 - 0.112) * 7.5 * 0.521) + self.assertEqual(growingsurface.depth, 0.476 * 7.5) + self.assertEqual(growingsurface.capacity, 0.476 * 7.5 * 1.5) + if __name__ == "__main__": unittest.main() From fe4867ddf412ac141115810e7ddab6160cf9efa9 Mon Sep 17 00:00:00 2001 From: liuly12 Date: Mon, 4 Mar 2024 09:37:39 +0000 Subject: [PATCH 13/13] Add test_nutrientpool_overrides --- tests/test_land.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/test_land.py b/tests/test_land.py index bd76992..2205b35 100644 --- a/tests/test_land.py +++ b/tests/test_land.py @@ -1332,6 +1332,32 @@ def test_growingsurface_overrides(self): self.assertEqual(growingsurface.readily_available_water, (0.335 - 0.112) * 7.5 * 0.521) self.assertEqual(growingsurface.depth, 0.476 * 7.5) self.assertEqual(growingsurface.capacity, 0.476 * 7.5 * 1.5) + + def test_nutrientpool_overrides(self): + constants.set_default_pollutants() + decaytank = DecayTank() + surface = Surface(parent=decaytank, area=5, depth=0.1) + pervioussurface = PerviousSurface( + parent=surface, depth=0.5, area=1.5, initial_storage=0.5 * 1.5 * 0.25 + ) + growingsurface = GrowingSurface( + parent=pervioussurface, area = 1.5 + ) + overrides = {'fraction_dry_n_to_dissolved_inorganic': 0.29, + 'degrhpar': {"N": 7 * 1e-1, "P": 7 * 1e-1}, + 'dishpar': {"N": 7 * 1e-1, "P": 7 * 1e-1}, + 'minfpar': {"N": 1.00013, "P": 1.000003}, + 'disfpar': {"N": 1.000003, "P": 1.0000001}, + 'immobdpar': {"N": 1.0056, "P": 1.2866}, + 'fraction_manure_to_dissolved_inorganic': {"N": 0.35, "P": 0.21}, + 'fraction_residue_to_fast': {"N": 0.61, "P": 0.71}} + + growingsurface.nutrient_pool.apply_overrides(overrides) + for k, v in overrides.items(): + self.assertDictEqual(getattr(growingsurface.nutrient_pool, k), v) + self.assertDictEqual(growingsurface.nutrient_pool.fraction_manure_to_fast, {"N": 1-0.35, "P": 1-0.21}) + self.assertDictEqual(growingsurface.nutrient_pool.fraction_residue_to_humus, {"N": 1-0.61, "P": 1-0.71}) + self.assertEqual(growingsurface.nutrient_pool.fraction_dry_n_to_fast, 0.71) if __name__ == "__main__": unittest.main()