Skip to content

Commit

Permalink
Merge pull request #81 from ImperialCollegeLondon/storage-overrides
Browse files Browse the repository at this point in the history
Apply overrides in Storage and its superclasses
  • Loading branch information
liuly12 authored Oct 10, 2024
2 parents d3e8dd3 + a5335da commit 08b0328
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 13 deletions.
118 changes: 116 additions & 2 deletions tests/test_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,6 @@ def test_river_pull(self):
constants.set_simple_pollutants()
river = River(
name="",
depth=2,
length=200,
width=20,
velocity=0.2 * 86400,
Expand All @@ -206,7 +205,6 @@ def test_river_depth(self):
constants.set_simple_pollutants()
river = River(
name="",
depth=2,
length=200,
width=20,
velocity=0.2 * 86400,
Expand Down Expand Up @@ -295,6 +293,122 @@ def test_riverreservoir_environmental(self):
}
self.assertDictAlmostEqual(d2, reservoir.tank.storage, 15)

def test_storage_overrides(self):
constants.set_default_pollutants()
storage = Storage(
name="", decays={"phosphate": {"constant": 1.005, "exponent": 1.005}}
)
storage.apply_overrides(
{
"capacity": 1.43,
"area": 2.36,
"datum": 0.32,
"decays": {"nitrite": {"constant": 10.005, "exponent": 1.105}},
}
)
self.assertEqual(storage.capacity, 1.43)
self.assertEqual(storage.tank.capacity, 1.43)
self.assertEqual(storage.area, 2.36)
self.assertEqual(storage.tank.area, 2.36)
self.assertEqual(storage.datum, 0.32)
self.assertEqual(storage.tank.datum, 0.32)
self.assertDictEqual(
storage.decays,
{
"phosphate": {"constant": 1.005, "exponent": 1.005},
"nitrite": {"constant": 10.005, "exponent": 1.105},
},
)
self.assertDictEqual(
storage.tank.decays,
{
"phosphate": {"constant": 1.005, "exponent": 1.005},
"nitrite": {"constant": 10.005, "exponent": 1.105},
},
)

def test_groundwater_overrides(self):
groundwater = Groundwater(
name="", decays={"phosphate": {"constant": 1.005, "exponent": 1.005}}
)
groundwater.apply_overrides(
{
"residence_time": 27.3,
"infiltration_threshold": 200,
"infiltration_pct": 0.667,
"capacity": 1.43,
"area": 2.36,
"datum": 0.32,
"decays": {"nitrite": {"constant": 10.005, "exponent": 1.105}},
}
)
self.assertEqual(groundwater.residence_time, 27.3)
self.assertEqual(groundwater.infiltration_threshold, 200)
self.assertEqual(groundwater.infiltration_pct, 0.667)
self.assertEqual(groundwater.capacity, 1.43)
self.assertEqual(groundwater.tank.capacity, 1.43)
self.assertEqual(groundwater.area, 2.36)
self.assertEqual(groundwater.tank.area, 2.36)
self.assertEqual(groundwater.datum, 0.32)
self.assertEqual(groundwater.tank.datum, 0.32)
self.assertDictEqual(
groundwater.decays,
{
"phosphate": {"constant": 1.005, "exponent": 1.005},
"nitrite": {"constant": 10.005, "exponent": 1.105},
},
)
self.assertDictEqual(
groundwater.tank.decays,
{
"phosphate": {"constant": 1.005, "exponent": 1.005},
"nitrite": {"constant": 10.005, "exponent": 1.105},
},
)

def test_river_overrides(self):
river = River(name="")
overrides = {
"length": 27.3,
"width": 200,
"velocity": 0.667,
"damp": 1.43,
"mrf": 2.36,
"uptake_PNratio": 0.32,
"bulk_density": 5.32,
"denpar_w": 0.432,
"T_wdays": 23.5,
"halfsatINwater": 3.269,
"hsatTP": 2.431,
"limpppar": 6.473,
"prodNpar": 7.821,
"prodPpar": 8.231,
"muptNpar": 6.213,
"muptPpar": 7.021,
"max_temp_lag": 3.213,
"max_phosphorus_lag": 78.321,
}
overrides_to_check = overrides.copy()
river.apply_overrides(overrides)
for k, v in overrides_to_check.items():
if k == "area":
v = 27.3 * 200
self.assertEqual(river.tank.area, v)
if k == "capacity":
v = constants.UNBOUNDED_CAPACITY
self.assertEqual(river.tank.capacity, v)
self.assertEqual(getattr(river, k), v)
# test runtimeerrors
self.assertRaises(RuntimeError, lambda: river.apply_overrides({"area": 75.2}))
self.assertRaises(
RuntimeError, lambda: river.apply_overrides({"capacity": 123})
)

def test_riverreservoir_overrides(self):
riverreservoir = RiverReservoir(name="")
riverreservoir.apply_overrides({"environmental_flow": 154})
self.assertEqual(riverreservoir.environmental_flow, 154)


if __name__ == "__main__":
unittest.main()
5 changes: 5 additions & 0 deletions tests/test_tanks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import unittest
from unittest import TestCase

from wsimod.core import constants
from wsimod.nodes.nodes import Node
from wsimod.nodes.tanks import (
DecayQueueTank,
Expand All @@ -14,6 +15,10 @@


class MyTestClass(TestCase):
def setUp(self):
""""""
constants.set_simple_pollutants()

def assertDictAlmostEqual(self, d1, d2, accuracy=19):
"""
Expand Down
137 changes: 126 additions & 11 deletions wsimod/nodes/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@author: bdobson Converted to totals on 2022-05-03
"""
from math import exp
from typing import Any, Dict

from wsimod.core import constants
from wsimod.nodes.nodes import Node
Expand Down Expand Up @@ -73,6 +74,30 @@ def __init__(
# Mass balance
self.mass_balance_ds.append(lambda: self.tank.ds())

def apply_overrides(self, overrides=Dict[str, Any]):
"""Override parameters.
Enables a user to override any of the following parameters:
capacity, area, datum, decays.
Args:
overrides (Dict[str, Any]): Dict describing which parameters should
be overridden (keys) and new values (values). Defaults to {}.
"""
# not using pop as these items need to stay
# in the overrides to be fed into the tank overrides
if "capacity" in overrides.keys():
self.capacity = overrides["capacity"]
if "area" in overrides.keys():
self.area = overrides["area"]
if "datum" in overrides.keys():
self.datum = overrides["datum"]
if "decays" in overrides.keys():
self.decays.update(overrides["decays"])
# apply tank overrides
self.tank.apply_overrides(overrides)
super().apply_overrides(overrides)

def push_set_storage(self, vqip):
"""A node wrapper for the tank push_storage.
Expand Down Expand Up @@ -172,6 +197,23 @@ def __init__(
self.data_input_dict = data_input_dict
super().__init__(**kwargs)

def apply_overrides(self, overrides=Dict[str, Any]):
"""Override parameters.
Enables a user to override any of the following parameters:
residence_time, infiltration_threshold, infiltration_pct.
Args:
overrides (Dict[str, Any]): Dict describing which parameters should
be overridden (keys) and new values (values). Defaults to {}.
"""
self.residence_time = overrides.pop("residence_time", self.residence_time)
self.infiltration_threshold = overrides.pop(
"infiltration_threshold", self.infiltration_threshold
)
self.infiltration_pct = overrides.pop("infiltration_pct", self.infiltration_pct)
super().apply_overrides(overrides)

def distribute(self):
"""Calculate outflow with residence time and send to Nodes or Rivers."""
avail = self.tank.get_avail()["volume"] / self.residence_time
Expand Down Expand Up @@ -267,6 +309,19 @@ def __init__(self, timearea={0: 1}, data_input_dict={}, **kwargs):
initial_storage=self.initial_storage,
)

def apply_overrides(self, overrides=Dict[str, Any]):
"""Override parameters.
Enables a user to override any of the following parameters:
timearea.
Args:
overrides (Dict[str, Any]): Dict describing which parameters should
be overridden (keys) and new values (values). Defaults to {}.
"""
self.timearea = overrides.pop("timearea", self.timearea)
super().apply_overrides(overrides)

def push_set_timearea(self, vqip):
"""Push setting that enables timearea behaviour, (see __init__ for
description).Used to receive flow that is assumed to occur widely across some
Expand Down Expand Up @@ -450,7 +505,12 @@ def __init__(
_Units_: m3/day
"""
# Set parameters
self.depth = depth # [m]
self.depth = depth
if depth != 2:
raise RuntimeError(
"warning: the depth parameter is unused by River nodes because it is \
intended for capacity to be unbounded. It may be removed in a future version."
)
self.length = length # [m]
self.width = width # [m]
self.velocity = velocity # [m/dt]
Expand Down Expand Up @@ -491,16 +551,6 @@ def __init__(
)
self.muptNpar = 0.001 # [kg/m2/day] nitrogen macrophyte uptake rate
self.muptPpar = 0.0001 # 0.01, # [kg/m2/day] phosphorus macrophyte uptake rate
self.qbank_365_days = [1e6, 1e6] # [m3/day] store outflow in the previous year
self.qbank = (
1e6 # [m3/day] bankfull flow = second largest outflow in the previous year
)
self.qbankcorrpar = 0.001 # [-] correction coefficient for qbank flow
self.sedexppar = 1 # [-]
self.EPC0 = 0.05 * constants.MG_L_TO_KG_M3 # [mg/l]
self.kd_s = 0 * constants.MG_L_TO_KG_M3 # 6 * 1e-6, # [kg/m3]
self.kadsdes_s = 2 # 0.9, # [-]
self.Dsed = 0.2 # [m]

self.max_temp_lag = 20
self.lagged_temperatures = []
Expand Down Expand Up @@ -552,6 +602,56 @@ def __init__(
# self.get_dt_excess()['volume'])) _ = self.tank.push_storage(vqip_, force=True)
# return self.extract_vqip(vqip, vqip_)

def apply_overrides(self, overrides=Dict[str, Any]):
"""Override parameters.
Enables a user to override any of the following parameters:
timearea.
Args:
overrides (Dict[str, Any]): Dict describing which parameters should
be overridden (keys) and new values (values). Defaults to {}.
"""
overwrite_params = set(
[
"length",
"width",
"velocity",
"damp",
"mrf",
"uptake_PNratio",
"bulk_density",
"denpar_w",
"T_wdays",
"halfsatINwater",
"hsatTP",
"limpppar",
"prodNpar",
"prodPpar",
"muptNpar",
"muptPpar",
"max_temp_lag",
"max_phosphorus_lag",
]
)

for param in overwrite_params.intersection(overrides.keys()):
setattr(self, param, overrides.pop(param))

if "area" in overrides.keys():
raise RuntimeError(
"ERROR: specifying area is depreciated in overrides \
for river, please specify length and width instead"
)
overrides["area"] = self.length * self.width
if "capacity" in overrides.keys():
raise RuntimeError(
"ERROR: specifying capacity is depreciated in overrides \
for river, it is always set as unbounded capacity"
)
overrides["capacity"] = constants.UNBOUNDED_CAPACITY
super().apply_overrides(overrides)

def pull_check_river(self, vqip=None):
"""Check amount of water that can be pulled from river tank and upstream.
Expand Down Expand Up @@ -937,6 +1037,21 @@ def __init__(self, environmental_flow=0, **kwargs):

self.__class__.__name__ = "Reservoir"

def apply_overrides(self, overrides=Dict[str, Any]):
"""Override parameters.
Enables a user to override any of the following parameters:
environmental_flow.
Args:
overrides (Dict[str, Any]): Dict describing which parameters should
be overridden (keys) and new values (values). Defaults to {}.
"""
self.environmental_flow = overrides.pop(
"environmental_flow", self.environmental_flow
)
super().apply_overrides(overrides)

def push_set_river_reservoir(self, vqip):
"""Receive water.
Expand Down

0 comments on commit 08b0328

Please sign in to comment.