diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..baa5ec1 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +extend-ignore = E203 +max-line-length = 120 +exclude = doc,test,.git,.venv,__pycache__,venv diff --git a/.github/workflows/automatic_test.yml b/.github/workflows/automatic_test.yml index d6084db..70fe72b 100644 --- a/.github/workflows/automatic_test.yml +++ b/.github/workflows/automatic_test.yml @@ -1,6 +1,7 @@ name: unittest on push on: + pull_request: push: branches: - master diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml new file mode 100644 index 0000000..f46e01d --- /dev/null +++ b/.github/workflows/pre-commit.yaml @@ -0,0 +1,14 @@ +name: pre-commit + +on: + pull_request: + push: + branches: [master] + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + - uses: pre-commit/action@v3.0.0 diff --git a/.github/workflows/publish_to_pypi.yml b/.github/workflows/publish_to_pypi.yml index 6917d71..edfac79 100644 --- a/.github/workflows/publish_to_pypi.yml +++ b/.github/workflows/publish_to_pypi.yml @@ -24,4 +24,4 @@ jobs: - name: Publish on PyPI uses: pypa/gh-action-pypi-publish@master with: - password: ${{ secrets.PYPI_ACCESS_TOKEN }} \ No newline at end of file + password: ${{ secrets.PYPI_ACCESS_TOKEN }} diff --git a/.gitignore b/.gitignore index a095750..1568779 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ build/ dist/ superflexpy.egg-info/ -**/.ipynb_checkpoints \ No newline at end of file +**/.ipynb_checkpoints diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..8009f94 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,22 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.5.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files + - repo: https://github.com/PyCQA/flake8 + rev: 6.1.0 + hooks: + - id: flake8 + exclude: ^(doc/|^test/) + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + args: ["--profile", "black", "--filter-files"] + - repo: https://github.com/psf/black + rev: 23.11.0 + hooks: + - id: black diff --git a/AUTHORS b/AUTHORS index 44ecee6..8973cb8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,4 +4,4 @@ Format: Name Surname, Affiliation, e-mail Marco Dal Molin, EAWAG, marco.dalmolin.1991@gmail.com Fabrizio Fenicia, EAWAG, fabrizio.fenicia@eawag.ch -Dmitri Kavetski, University of Adelaide, dmitri.kavetski@adelaide.edu.au \ No newline at end of file +Dmitri Kavetski, University of Adelaide, dmitri.kavetski@adelaide.edu.au diff --git a/README.md b/README.md index fa4c633..3694371 100644 --- a/README.md +++ b/README.md @@ -11,4 +11,4 @@ flexible, conceptual, distributed hydrological models. Refer to the [documentation](https://superflexpy.readthedocs.io/) to learn to use the SuperflexPy, [install](https://pypi.org/project/superflexpy/) the package from here. [Examples](examples/) showing the basic usage of SuperflexPy -are available. \ No newline at end of file +are available. diff --git a/doc/build_element_code.py b/doc/build_element_code.py index c1b3932..2fb38a6 100644 --- a/doc/build_element_code.py +++ b/doc/build_element_code.py @@ -1,8 +1,7 @@ import numba as nb import numpy as np -from superflexpy.framework.element import ODEsElement -from superflexpy.framework.element import LagElement -from superflexpy.framework.element import ParameterizedElement + +from superflexpy.framework.element import LagElement, ODEsElement, ParameterizedElement # Implement a linear reservoir @@ -27,22 +26,18 @@ class LinearReservoir(ODEsElement): """ def __init__(self, parameters, states, approximation, id): - - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] # Used by get fluxes, regardless of the architecture - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] else: - message = '{}The architecture ({}) of the approximation is not correct'.format(self._error_message, - approximation.architecture) + message = "{}The architecture ({}) of the approximation is not correct".format( + self._error_message, approximation.architecture + ) raise ValueError(message) def set_input(self, input): @@ -57,7 +52,7 @@ def set_input(self, input): 1. Rainfall """ - self.input = {'P': input[0]} + self.input = {"P": input[0]} def get_output(self, solve=True): """ @@ -72,65 +67,58 @@ def get_output(self, solve=True): """ if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) - return [- fluxes[0][1]] + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) + return [-fluxes[0][1]] @staticmethod def _fluxes_function_python(S, S0, ind, P, k, dt): - if ind is None: return ( [ P, - - k * S, + -k * S, ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - k[ind] * S, + -k[ind] * S, ], 0.0, S0 + P[ind] * dt[ind], - [ - 0.0, - - k[ind] - ] + [0.0, -k[ind]], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 2), f8, f8))(optional(f8), f8, i4, f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit("Tuple((UniTuple(f8, 2), f8, f8))(optional(f8), f8, i4, f8[:], f8[:], f8[:])", nopython=True) def _fluxes_function_numba(S, S0, ind, P, k, dt): - return ( ( P[ind], - - k[ind] * S, + -k[ind] * S, ), 0.0, S0 + P[ind] * dt[ind], - ( - 0.0, - - k[ind] - ) + (0.0, -k[ind]), ) + # Implement lag function @@ -152,7 +140,6 @@ class TriangularLag(LagElement): """ def _build_weight(self, lag_time): - weight = [] for t in lag_time: @@ -160,8 +147,7 @@ def _build_weight(self, lag_time): w_i = [] for i in range(int(array_length)): - w_i.append(self._calculate_lag_area(i + 1, t) - - self._calculate_lag_area(i, t)) + w_i.append(self._calculate_lag_area(i + 1, t) - self._calculate_lag_area(i, t)) weight.append(np.array(w_i)) @@ -169,16 +155,16 @@ def _build_weight(self, lag_time): @staticmethod def _calculate_lag_area(bin, len): - if bin <= 0: value = 0 elif bin < len: - value = (bin / len)**2 + value = (bin / len) ** 2 else: value = 1 return value + # Implement a parametrized splitter @@ -214,7 +200,7 @@ def set_input(self, input): 1. Incoming flow """ - self.input = {'Q_in': input[0]} + self.input = {"Q_in": input[0]} def get_output(self, solve=True): """ @@ -233,9 +219,6 @@ def get_output(self, solve=True): # solve is not needed but kept in the interface - split_par = self._parameters[self._prefix_parameters + 'split-par'] + split_par = self._parameters[self._prefix_parameters + "split-par"] - return [ - self.input['Q_in'] * split_par, - self.input['Q_in'] * (1 - split_par) - ] + return [self.input["Q_in"] * split_par, self.input["Q_in"] * (1 - split_par)] diff --git a/doc/case_studies.rst b/doc/case_studies.rst index d105ef9..f806d55 100644 --- a/doc/case_studies.rst +++ b/doc/case_studies.rst @@ -155,4 +155,4 @@ We can now run the model and access its output (see :ref:`demo_network` for deta .. literalinclude:: model_thur_hess2020.py :language: python :lines: 262 - :linenos: \ No newline at end of file + :linenos: diff --git a/doc/changelog.rst b/doc/changelog.rst index cade322..498f81d 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -116,4 +116,4 @@ Minor changes to existing components New code ........ -- Added :code:`hymod` elements \ No newline at end of file +- Added :code:`hymod` elements diff --git a/doc/components_code.py b/doc/components_code.py index 786a00a..aa8b77c 100644 --- a/doc/components_code.py +++ b/doc/components_code.py @@ -1,8 +1,8 @@ -e1 = Element(parameters={'p1': 0.1}, states={'S': 10.0}) +e1 = Element(parameters={"p1": 0.1}, states={"S": 10.0}) u1 = Unit([e1]) u2 = Unit([e1]) -e1.set_parameters({'e1_p1': 0.2}) -u1.set_parameters({'u1_e1_p1': 0.3}) -u2.set_parameters({'u2_e1_p1': 0.4}) \ No newline at end of file +e1.set_parameters({"e1_p1": 0.2}) +u1.set_parameters({"u1_e1_p1": 0.3}) +u2.set_parameters({"u2_e1_p1": 0.4}) diff --git a/doc/conf.py b/doc/conf.py index 8cc93fb..5a8d28a 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -12,17 +12,18 @@ # import os import sys -sys.path.insert(0, os.path.abspath('../')) + +sys.path.insert(0, os.path.abspath("../")) # -- Project information ----------------------------------------------------- -project = 'SuperflexPy' -copyright = '2021, Marco Dal Molin, Dmitri Kavetski, Fabrizio Fenicia' -author = 'Marco Dal Molin, Dmitri Kavetski, Fabrizio Fenicia' +project = "SuperflexPy" +copyright = "2021, Marco Dal Molin, Dmitri Kavetski, Fabrizio Fenicia" +author = "Marco Dal Molin, Dmitri Kavetski, Fabrizio Fenicia" # The full version, including alpha/beta/rc tags -release = '1.3.0' +release = "1.3.0" # -- General configuration --------------------------------------------------- @@ -31,35 +32,33 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.napoleon', - 'sphinx.ext.viewcode', + "sphinx.ext.autodoc", + "sphinx.ext.napoleon", + "sphinx.ext.viewcode", ] napoleon_numpy_docstring = True -autodoc_member_order = 'bysource' +autodoc_member_order = "bysource" # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] -master_doc = 'index' +master_doc = "index" # -- Options for HTML output ------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = 'sphinx_rtd_theme' -html_theme_options = { - 'logo_only': True -} -html_logo = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'pics', 'logo_inverted_2.PNG') +html_theme = "sphinx_rtd_theme" +html_theme_options = {"logo_only": True} +html_logo = os.path.join(os.path.abspath(os.path.dirname(__file__)), "pics", "logo_inverted_2.PNG") # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +html_static_path = ["_static"] diff --git a/doc/customize_components_code.py b/doc/customize_components_code.py index e471d67..4d25ebb 100644 --- a/doc/customize_components_code.py +++ b/doc/customize_components_code.py @@ -1,6 +1,7 @@ -from superflexpy.framework.node import Node import numpy as np +from superflexpy.framework.node import Node + class RoutedNone(Node): """ @@ -11,8 +12,7 @@ class RoutedNone(Node): """ def _internal_routing(self, flux): - - t_internal = self.get_parameters(names=[self._prefix_local_parameters + 't_internal']) + t_internal = self.get_parameters(names=[self._prefix_local_parameters + "t_internal"]) flux_out = [] for f in flux: @@ -21,8 +21,7 @@ def _internal_routing(self, flux): return flux_out def external_routing(self, flux): - - t_external = self.get_parameters(names=[self._prefix_local_parameters + 't_external']) + t_external = self.get_parameters(names=[self._prefix_local_parameters + "t_external"]) flux_out = [] for f in flux: @@ -31,7 +30,6 @@ def external_routing(self, flux): return flux_out def _route(self, flux, time): - state = np.zeros(int(np.ceil(time))) weight = self._calculate_weight(time) @@ -45,20 +43,17 @@ def _route(self, flux, time): return np.array(out) def _calculate_weight(self, time): - weight = [] array_length = np.ceil(time) for i in range(int(array_length)): - weight.append(self._calculate_lag_area(i + 1, time) - - self._calculate_lag_area(i, time)) + weight.append(self._calculate_lag_area(i + 1, time) - self._calculate_lag_area(i, time)) return weight @staticmethod def _calculate_lag_area(portion, time): - half_time = time / 2 if portion <= 0: @@ -66,7 +61,7 @@ def _calculate_lag_area(portion, time): elif portion < half_time: value = 2 * (portion / time) ** 2 elif portion < time: - value = 1 - 2 * ((time - portion) / time)**2 + value = 1 - 2 * ((time - portion) / time) ** 2 else: value = 1 diff --git a/doc/demo_code.py b/doc/demo_code.py index 769f94a..9840282 100644 --- a/doc/demo_code.py +++ b/doc/demo_code.py @@ -1,17 +1,22 @@ # Import other libraries import os import sys -import numpy as np + import matplotlib.pyplot as plt +import numpy as np from matplotlib.lines import Line2D + +from superflexpy.framework.network import Network +from superflexpy.framework.node import Node +from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.gr4j import UnitHydrograph1 + # Import SuperflexPy from superflexpy.implementation.elements.hbv import PowerReservoir -from superflexpy.implementation.elements.gr4j import UnitHydrograph1 +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython -from superflexpy.framework.unit import Unit -from superflexpy.framework.node import Node -from superflexpy.framework.network import Network # Set the wd to the one of the file, to avoid absolute paths os.chdir(sys.path[0]) @@ -19,20 +24,18 @@ # Initialize solver (default settings) solver_python = PegasusPython() approximator = ImplicitEulerPython(root_finder=solver_python) -#%% Run element +# %% Run element # Initialize the reservoir reservoir = PowerReservoir( - parameters={'k': 0.01, 'alpha': 2.0}, - states={'S0': 10.0}, - approximation=approximator, - id='R' + parameters={"k": 0.01, "alpha": 2.0}, states={"S0": 10.0}, approximation=approximator, id="R" ) reservoir.set_timestep(1.0) # Create input -precipitation = np.array([6.5, 3.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 2.0, 0.0, - 0.0, 2.5, 1.0, 0.0, 4.0, 0.0, 1.0, 0.0, 0.0, 0.0]) +precipitation = np.array( + [6.5, 3.0, 0.0, 0.0, 0.0, 2.0, 4.0, 8.0, 2.0, 0.0, 0.0, 2.5, 1.0, 0.0, 4.0, 0.0, 1.0, 0.0, 0.0, 0.0] +) # Set input reservoir.set_input([precipitation]) @@ -43,40 +46,35 @@ # Plotting fig, ax = plt.subplots(2, 1, sharex=True, figsize=(10, 6)) -ax[0].bar(x=range(len(precipitation)), height=precipitation, color='blue') +ax[0].bar(x=range(len(precipitation)), height=precipitation, color="blue") -ax[1].plot(range(len(precipitation)), output, color='blue', lw=2, label='Outflow') +ax[1].plot(range(len(precipitation)), output, color="blue", lw=2, label="Outflow") ax_bis = ax[1].twinx() -ax_bis.plot(range(len(precipitation)), reservoir_state, color='red', lw=2, ls='--', label='Reservoir state') +ax_bis.plot(range(len(precipitation)), reservoir_state, color="red", lw=2, ls="--", label="Reservoir state") -ax[0].set_ylabel('Precipitation [mm/day]') -ax[1].set_ylabel('Flow [mm/day]') -ax_bis.set_ylabel('State [mm]') -ax[1].set_xlabel('Time [day]') +ax[0].set_ylabel("Precipitation [mm/day]") +ax[1].set_ylabel("Flow [mm/day]") +ax_bis.set_ylabel("State [mm]") +ax[1].set_xlabel("Time [day]") -lines = [Line2D([0], [0], color='blue', lw=2, label='Outflow'), - Line2D([0], [0], color='red', lw=2, ls='--', label='Reservoir state')] +lines = [ + Line2D([0], [0], color="blue", lw=2, label="Outflow"), + Line2D([0], [0], color="red", lw=2, ls="--", label="Reservoir state"), +] ax[1].legend(lines, [l.get_label() for l in lines]) -fig.savefig('pics/demo/SingleReservoir.png', res=600) +fig.savefig("pics/demo/SingleReservoir.png", res=600) -#%% Run Unit +# %% Run Unit # Reset the fast reservoir (we use it again) reservoir.reset_states() # Initialize the lag function -lag_function = UnitHydrograph1( - parameters={'lag-time': 2.3}, - states={'lag': None}, - id='lag-fun' -) +lag_function = UnitHydrograph1(parameters={"lag-time": 2.3}, states={"lag": None}, id="lag-fun") # Create unit -unit_1 = Unit( - layers=[[reservoir], [lag_function]], - id='unit-1' -) +unit_1 = Unit(layers=[[reservoir], [lag_function]], id="unit-1") unit_1.set_timestep(1.0) @@ -86,59 +84,50 @@ output = unit_1.get_output()[0] # Fast reservoir -r_state = unit_1.get_internal(id='R', attribute='state_array')[:, 0] -r_output = unit_1.call_internal(id='R', method='get_output', solve=False)[0] +r_state = unit_1.get_internal(id="R", attribute="state_array")[:, 0] +r_output = unit_1.call_internal(id="R", method="get_output", solve=False)[0] # The output of lag is the same of the unit # Plotting fig, ax = plt.subplots(3, 1, sharex=True, figsize=(10, 9)) -ax[0].bar(x=range(len(precipitation)), height=precipitation, color='blue') +ax[0].bar(x=range(len(precipitation)), height=precipitation, color="blue") -ax[1].plot(range(len(precipitation)), r_output, color='blue', lw=2, - label='Outflow') +ax[1].plot(range(len(precipitation)), r_output, color="blue", lw=2, label="Outflow") ax_bis = ax[1].twinx() -ax_bis.plot(range(len(precipitation)), r_state, color='red', lw=2, - ls='--', label='Reservoir state') - -ax[2].plot(range(len(precipitation)), output, color='blue', lw=2, - label='Outflow') - -ax[0].set_ylabel('Precipitation [mm/day]') -ax[1].set_ylabel('Outflow [mm/day]') -ax_bis.set_ylabel('State [mm]') -ax[2].set_ylabel('Outflow [mm/day]') -ax[2].set_xlabel('Time [day]') -ax[1].set_title('Reservoir') -ax[2].set_title('Lag function') - -lines = [Line2D([0], [0], color='blue', lw=2, label='Outflow'), - Line2D([0], [0], color='red', lw=2, ls='--', label='Reservoir state')] +ax_bis.plot(range(len(precipitation)), r_state, color="red", lw=2, ls="--", label="Reservoir state") + +ax[2].plot(range(len(precipitation)), output, color="blue", lw=2, label="Outflow") + +ax[0].set_ylabel("Precipitation [mm/day]") +ax[1].set_ylabel("Outflow [mm/day]") +ax_bis.set_ylabel("State [mm]") +ax[2].set_ylabel("Outflow [mm/day]") +ax[2].set_xlabel("Time [day]") +ax[1].set_title("Reservoir") +ax[2].set_title("Lag function") + +lines = [ + Line2D([0], [0], color="blue", lw=2, label="Outflow"), + Line2D([0], [0], color="red", lw=2, ls="--", label="Reservoir state"), +] ax[1].legend(lines, [l.get_label() for l in lines]) ax[2].legend(lines[:1], [l.get_label() for l in lines[:1]]) -fig.savefig('pics/demo/SingleUnit.png', res=600) +fig.savefig("pics/demo/SingleUnit.png", res=600) -#%% Run Node +# %% Run Node # Reset states of Unit1 unit_1.reset_states() -unit_1.set_states({'unit-1_lag-fun_lag': None}) # Remove after new build +unit_1.set_states({"unit-1_lag-fun_lag": None}) # Remove after new build # Create second unit -unit_2 = Unit( - layers=[[reservoir]], - id='unit-2' -) +unit_2 = Unit(layers=[[reservoir]], id="unit-2") # Put units together in a node -node_1 = Node( - units=[unit_1, unit_2], - weights=[0.7, 0.3], - area=10.0, - id='node-1' -) +node_1 = Node(units=[unit_1, unit_2], weights=[0.7, 0.3], area=10.0, id="node-1") node_1.set_timestep(1.0) node_1.set_input([precipitation]) @@ -146,62 +135,42 @@ # Solve the node output = node_1.get_output()[0] -output_unit_1 = node_1.call_internal(id='unit-1', method='get_output', solve=False)[0] -output_unit_2 = node_1.call_internal(id='unit-2', method='get_output', solve=False)[0] +output_unit_1 = node_1.call_internal(id="unit-1", method="get_output", solve=False)[0] +output_unit_2 = node_1.call_internal(id="unit-2", method="get_output", solve=False)[0] # Plotting fig, ax = plt.subplots(3, 1, sharex=True, figsize=(10, 9)) -ax[0].bar(x=range(len(precipitation)), height=precipitation, color='blue') +ax[0].bar(x=range(len(precipitation)), height=precipitation, color="blue") -ax[1].plot(range(len(precipitation)), output_unit_1, color='blue', lw=2, - label='Unit 1') -ax[1].plot(range(len(precipitation)), output_unit_2, color='red', lw=2, - ls='--', label='Unit 2') +ax[1].plot(range(len(precipitation)), output_unit_1, color="blue", lw=2, label="Unit 1") +ax[1].plot(range(len(precipitation)), output_unit_2, color="red", lw=2, ls="--", label="Unit 2") -ax[2].plot(range(len(precipitation)), output, color='blue', lw=2, - label='Outflow') +ax[2].plot(range(len(precipitation)), output, color="blue", lw=2, label="Outflow") -ax[0].set_ylabel('Precipitation [mm/day]') -ax[1].set_ylabel('Outflow [mm/day]') -ax[2].set_ylabel('Outflow [mm/day]') -ax[2].set_xlabel('Time [day]') -ax[1].set_title('Units') -ax[2].set_title('Node') +ax[0].set_ylabel("Precipitation [mm/day]") +ax[1].set_ylabel("Outflow [mm/day]") +ax[2].set_ylabel("Outflow [mm/day]") +ax[2].set_xlabel("Time [day]") +ax[1].set_title("Units") +ax[2].set_title("Node") ax[1].legend() ax[2].legend() -fig.savefig('pics/demo/SingleNode.png', res=600) +fig.savefig("pics/demo/SingleNode.png", res=600) -#%% Network +# %% Network node_1.reset_states() -node_1.set_states({'node-1_unit-1_lag-fun_lag': None}) # Remove after new build +node_1.set_states({"node-1_unit-1_lag-fun_lag": None}) # Remove after new build # Create other nodes -node_2 = Node( - units=[unit_1, unit_2], - weights=[0.3, 0.7], - area=5.0, - id='node-2' -) +node_2 = Node(units=[unit_1, unit_2], weights=[0.3, 0.7], area=5.0, id="node-2") -node_3 = Node( - units=[unit_2], - weights=[1.0], - area=3.0, - id='node-3' -) +node_3 = Node(units=[unit_2], weights=[1.0], area=3.0, id="node-3") # Create the network -net = Network( - nodes=[node_1, node_2, node_3], - topology={ - 'node-1': 'node-3', - 'node-2': 'node-3', - 'node-3': None - } -) +net = Network(nodes=[node_1, node_2, node_3], topology={"node-1": "node-3", "node-2": "node-3", "node-3": None}) node_1.set_input([precipitation]) node_2.set_input([precipitation * 0.5]) @@ -211,26 +180,21 @@ output = net.get_output() -output_unit_1_node_1 = net.call_internal(id='node-1_unit-1', - method='get_output', - solve=False)[0] +output_unit_1_node_1 = net.call_internal(id="node-1_unit-1", method="get_output", solve=False)[0] # Plotting fig, ax = plt.subplots(2, 1, sharex=True, figsize=(10, 6)) -ax[0].bar(x=range(len(precipitation)), height=precipitation, color='blue') +ax[0].bar(x=range(len(precipitation)), height=precipitation, color="blue") -ax[1].plot(range(len(precipitation)), output['node-1'][0], color='blue', lw=2, - label='node-1') -ax[1].plot(range(len(precipitation)), output['node-2'][0], color='red', lw=2, - label='node-2') -ax[1].plot(range(len(precipitation)), output['node-3'][0], color='orange', lw=2, - label='node-3') +ax[1].plot(range(len(precipitation)), output["node-1"][0], color="blue", lw=2, label="node-1") +ax[1].plot(range(len(precipitation)), output["node-2"][0], color="red", lw=2, label="node-2") +ax[1].plot(range(len(precipitation)), output["node-3"][0], color="orange", lw=2, label="node-3") -ax[0].set_ylabel('Precipitation [mm/day]') -ax[1].set_ylabel('Outflow [mm/day]') -ax[1].set_xlabel('Time [day]') +ax[0].set_ylabel("Precipitation [mm/day]") +ax[1].set_ylabel("Outflow [mm/day]") +ax[1].set_xlabel("Time [day]") ax[1].legend() -fig.savefig('pics/demo/Network.png', res=600) +fig.savefig("pics/demo/Network.png", res=600) diff --git a/doc/examples.rst b/doc/examples.rst index 6404088..d6853c3 100644 --- a/doc/examples.rst +++ b/doc/examples.rst @@ -19,4 +19,4 @@ visualized on GitHub or run in a sandbox environment. - Replicate Hymod: `visualize `_ - `run `_ - Replicate M02 in Dal Molin et al., HESS, 2020: `visualize `_ - `run `_ - Replicate M4 in Kavetski and Fenicia, WRR, 2011: `visualize `_ - `run `_ -- Modify M4 in Kavetski and Fenicia, WRR, 2011: `visualize `_ - `run `_ \ No newline at end of file +- Modify M4 in Kavetski and Fenicia, WRR, 2011: `visualize `_ - `run `_ diff --git a/doc/index.rst b/doc/index.rst index ba136fc..2bc6e0c 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -104,4 +104,4 @@ please `subscribe `_ to our mailing list. testing license reference - changelog \ No newline at end of file + changelog diff --git a/doc/installation.rst b/doc/installation.rst index bb67ee2..9b6cc09 100644 --- a/doc/installation.rst +++ b/doc/installation.rst @@ -41,4 +41,4 @@ when installing SuperflexPy. Note that Numba is required only if the modeler wishes to use the Numba optimized implementation of the numerical solvers. GPU acceleration (CUDA) is -currently not supported but will be explored in future versions. \ No newline at end of file +currently not supported but will be explored in future versions. diff --git a/doc/interfaces_code.py b/doc/interfaces_code.py index 28373c3..3f2d5ff 100644 --- a/doc/interfaces_code.py +++ b/doc/interfaces_code.py @@ -1,35 +1,24 @@ -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython -from superflexpy.implementation.root_finders.pegasus import PegasusPython -from superflexpy.implementation.elements.hbv import PowerReservoir -from superflexpy.framework.unit import Unit -import spotpy import numpy as np +import spotpy - +from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.hbv import PowerReservoir +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerPython, +) +from superflexpy.implementation.root_finders.pegasus import PegasusPython root_finder = PegasusPython() num_app = ImplicitEulerPython(root_finder=root_finder) -reservoir_1 = PowerReservoir(parameters={'k': 0.1, 'alpha': 2.0}, - states={'S0': 10.0}, - approximation=num_app, - id='FR1') -reservoir_2 = PowerReservoir(parameters={'k': 0.5, 'alpha': 1.0}, - states={'S0': 10.0}, - approximation=num_app, - id='FR2') - -hyd_mod = Unit(layers=[[reservoir_1], - [reservoir_2]], - id='model') - +reservoir_1 = PowerReservoir(parameters={"k": 0.1, "alpha": 2.0}, states={"S0": 10.0}, approximation=num_app, id="FR1") +reservoir_2 = PowerReservoir(parameters={"k": 0.5, "alpha": 1.0}, states={"S0": 10.0}, approximation=num_app, id="FR2") +hyd_mod = Unit(layers=[[reservoir_1], [reservoir_2]], id="model") class spotpy_model(object): - def __init__(self, model, inputs, dt, observations, parameters, parameter_names, output_index): - self._model = model self._model.set_input(inputs) self._model.set_timestep(dt) @@ -39,15 +28,10 @@ def __init__(self, model, inputs, dt, observations, parameters, parameter_names, self._observarions = observations self._output_index = output_index - - def parameters(self): return spotpy.parameter.generate(self._parameters) - - def simulation(self, parameters): - named_parameters = {} for p_name, p in zip(self._parameter_names, parameters): named_parameters[p_name] = p @@ -58,17 +42,11 @@ def simulation(self, parameters): return output[self._output_index] - - def evaluation(self): return self._observarions - - def objectivefunction(self, simulation, evaluation): - - obj_fun = spotpy.objectivefunctions.nashsutcliffe(evaluation=evaluation, - simulation=simulation) + obj_fun = spotpy.objectivefunctions.nashsutcliffe(evaluation=evaluation, simulation=simulation) return obj_fun @@ -77,21 +55,19 @@ def objectivefunction(self, simulation, evaluation): Q_obs = np.array([5.0, 3.2, 4.5]) - spotpy_hyd_mod = spotpy_model( model=hyd_mod, inputs=[P], dt=1.0, observations=Q_obs, parameters=[ - spotpy.parameter.Uniform('model_FR1_k', 1e-4, 1e-1), - spotpy.parameter.Uniform('model_FR2_k', 1e-3, 1.0), + spotpy.parameter.Uniform("model_FR1_k", 1e-4, 1e-1), + spotpy.parameter.Uniform("model_FR2_k", 1e-3, 1.0), ], - parameter_names=['model_FR1_k', 'model_FR2_k'], - output_index=0 + parameter_names=["model_FR1_k", "model_FR2_k"], + output_index=0, ) - -sampler = spotpy.algorithms.sceua(spotpy_hyd_mod, dbname='calibration', dbformat='csv') -sampler.sample(repetitions=5000) \ No newline at end of file +sampler = spotpy.algorithms.sceua(spotpy_hyd_mod, dbname="calibration", dbformat="csv") +sampler.sample(repetitions=5000) diff --git a/doc/license.rst b/doc/license.rst index ae03aef..93a6e53 100644 --- a/doc/license.rst +++ b/doc/license.rst @@ -6,4 +6,4 @@ License ======= .. highlight:: none -.. literalinclude:: LICENSE \ No newline at end of file +.. literalinclude:: LICENSE diff --git a/doc/model_thur_hess2020.py b/doc/model_thur_hess2020.py index ae381b3..4db082b 100644 --- a/doc/model_thur_hess2020.py +++ b/doc/model_thur_hess2020.py @@ -1,14 +1,26 @@ -import sys import pathlib +import sys + REPO_FOLDER = pathlib.Path(__file__).absolute().parents[2] sys.path.insert(0, str(REPO_FOLDER)) -from superflexpy.implementation.elements.thur_model_hess import SnowReservoir, UnsaturatedReservoir, HalfTriangularLag, PowerReservoir -from superflexpy.implementation.elements.structure_elements import Transparent, Junction, Splitter -from superflexpy.framework.unit import Unit -from superflexpy.framework.node import Node from superflexpy.framework.network import Network +from superflexpy.framework.node import Node +from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.structure_elements import ( + Junction, + Splitter, + Transparent, +) +from superflexpy.implementation.elements.thur_model_hess import ( + HalfTriangularLag, + PowerReservoir, + SnowReservoir, + UnsaturatedReservoir, +) +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython solver = PegasusPython() approximator = ImplicitEulerPython(root_finder=solver) @@ -16,84 +28,43 @@ # Fluxes in the order P, T, PET upper_splitter = Splitter( direction=[ - [0, 1, None], # P and T go to the snow reservoir - [2, None, None] # PET goes to the transparent element + [0, 1, None], # P and T go to the snow reservoir + [2, None, None], # PET goes to the transparent element ], - weight=[ - [1.0, 1.0, 0.0], - [0.0, 0.0, 1.0] - ], - id='upper-splitter' + weight=[[1.0, 1.0, 0.0], [0.0, 0.0, 1.0]], + id="upper-splitter", ) snow = SnowReservoir( - parameters={'t0': 0.0, 'k': 0.01, 'm': 2.0}, - states={'S0': 0.0}, - approximation=approximator, - id='snow' + parameters={"t0": 0.0, "k": 0.01, "m": 2.0}, states={"S0": 0.0}, approximation=approximator, id="snow" ) -upper_transparent = Transparent( - id='upper-transparent' -) +upper_transparent = Transparent(id="upper-transparent") -upper_junction = Junction( - direction=[ - [0, None], - [None, 0] - ], - id='upper-junction' -) +upper_junction = Junction(direction=[[0, None], [None, 0]], id="upper-junction") unsaturated = UnsaturatedReservoir( - parameters={'Smax': 50.0, 'Ce': 1.0, 'm': 0.01, 'beta': 2.0}, - states={'S0': 10.0}, + parameters={"Smax": 50.0, "Ce": 1.0, "m": 0.01, "beta": 2.0}, + states={"S0": 10.0}, approximation=approximator, - id='unsaturated' + id="unsaturated", ) lower_splitter = Splitter( - direction=[ - [0], - [0] - ], - weight=[ - [0.3], # Portion to slow reservoir - [0.7] # Portion to fast reservoir - ], - id='lower-splitter' + direction=[[0], [0]], + weight=[[0.3], [0.7]], # Portion to slow reservoir # Portion to fast reservoir + id="lower-splitter", ) -lag_fun = HalfTriangularLag( - parameters={'lag-time': 2.0}, - states={'lag': None}, - id='lag-fun' -) +lag_fun = HalfTriangularLag(parameters={"lag-time": 2.0}, states={"lag": None}, id="lag-fun") -fast = PowerReservoir( - parameters={'k': 0.01, 'alpha': 3.0}, - states={'S0': 0.0}, - approximation=approximator, - id='fast' -) +fast = PowerReservoir(parameters={"k": 0.01, "alpha": 3.0}, states={"S0": 0.0}, approximation=approximator, id="fast") -slow = PowerReservoir( - parameters={'k': 1e-4, 'alpha': 1.0}, - states={'S0': 0.0}, - approximation=approximator, - id='slow' -) +slow = PowerReservoir(parameters={"k": 1e-4, "alpha": 1.0}, states={"S0": 0.0}, approximation=approximator, id="slow") -lower_transparent = Transparent( - id='lower-transparent' -) +lower_transparent = Transparent(id="lower-transparent") -lower_junction = Junction( - direction=[ - [0, 0] - ], - id='lower-junction' -) +lower_junction = Junction(direction=[[0, 0]], id="lower-junction") consolidated = Unit( layers=[ @@ -106,7 +77,7 @@ [lower_transparent, fast], [lower_junction], ], - id='consolidated' + id="consolidated", ) unconsolidated = Unit( @@ -120,78 +91,28 @@ [lower_transparent, fast], [lower_junction], ], - id='unconsolidated' + id="unconsolidated", ) -andelfingen = Node( - units=[consolidated, unconsolidated], - weights=[0.24, 0.76], - area=403.3, - id='andelfingen' -) +andelfingen = Node(units=[consolidated, unconsolidated], weights=[0.24, 0.76], area=403.3, id="andelfingen") -appenzell = Node( - units=[consolidated, unconsolidated], - weights=[0.92, 0.08], - area=74.4, - id='appenzell' -) +appenzell = Node(units=[consolidated, unconsolidated], weights=[0.92, 0.08], area=74.4, id="appenzell") -frauenfeld = Node( - units=[consolidated, unconsolidated], - weights=[0.49, 0.51], - area=134.4, - id='frauenfeld' -) +frauenfeld = Node(units=[consolidated, unconsolidated], weights=[0.49, 0.51], area=134.4, id="frauenfeld") -halden = Node( - units=[consolidated, unconsolidated], - weights=[0.34, 0.66], - area=314.3, - id='halden' -) +halden = Node(units=[consolidated, unconsolidated], weights=[0.34, 0.66], area=314.3, id="halden") -herisau = Node( - units=[consolidated, unconsolidated], - weights=[0.88, 0.12], - area=16.7, - id='herisau' -) +herisau = Node(units=[consolidated, unconsolidated], weights=[0.88, 0.12], area=16.7, id="herisau") -jonschwil = Node( - units=[consolidated, unconsolidated], - weights=[0.9, 0.1], - area=401.6, - id='jonschwil' -) +jonschwil = Node(units=[consolidated, unconsolidated], weights=[0.9, 0.1], area=401.6, id="jonschwil") -mogelsberg = Node( - units=[consolidated, unconsolidated], - weights=[0.92, 0.08], - area=88.1, - id='mogelsberg' -) +mogelsberg = Node(units=[consolidated, unconsolidated], weights=[0.92, 0.08], area=88.1, id="mogelsberg") -mosnang = Node( - units=[consolidated], - weights=[1.0], - area=3.1, - id='mosnang' -) +mosnang = Node(units=[consolidated], weights=[1.0], area=3.1, id="mosnang") -stgallen = Node( - units=[consolidated, unconsolidated], - weights=[0.87, 0.13], - area=186.6, - id='stgallen' -) +stgallen = Node(units=[consolidated, unconsolidated], weights=[0.87, 0.13], area=186.6, id="stgallen") -waengi = Node( - units=[consolidated, unconsolidated], - weights=[0.63, 0.37], - area=78.9, - id='waengi' -) +waengi = Node(units=[consolidated, unconsolidated], weights=[0.63, 0.37], area=78.9, id="waengi") thur_catchment = Network( nodes=[ @@ -207,17 +128,17 @@ waengi, ], topology={ - 'andelfingen': None, - 'appenzell': 'stgallen', - 'frauenfeld': 'andelfingen', - 'halden': 'andelfingen', - 'herisau': 'halden', - 'jonschwil': 'halden', - 'mogelsberg': 'jonschwil', - 'mosnang': 'jonschwil', - 'stgallen': 'halden', - 'waengi': 'frauenfeld', - } + "andelfingen": None, + "appenzell": "stgallen", + "frauenfeld": "andelfingen", + "halden": "andelfingen", + "herisau": "halden", + "jonschwil": "halden", + "mogelsberg": "jonschwil", + "mosnang": "jonschwil", + "stgallen": "halden", + "waengi": "frauenfeld", + }, ) thur_catchment.set_timestep(1.0) @@ -233,29 +154,31 @@ mosnang, stgallen, waengi, - ] +] catchments_names = [ - 'andelfingen', - 'appenzell', - 'frauenfeld', - 'halden', - 'herisau', - 'jonschwil', - 'mogelsberg', - 'mosnang', - 'stgallen', - 'waengi', - ] + "andelfingen", + "appenzell", + "frauenfeld", + "halden", + "herisau", + "jonschwil", + "mogelsberg", + "mosnang", + "stgallen", + "waengi", +] df = {} for cat, cat_name in zip(catchments, catchments_names): - cat.set_input([ - df['P_{}'.format(cat_name)].values, - df['T_{}'.format(cat_name)].values, - df['PET_{}'.format(cat_name)].values, - ]) + cat.set_input( + [ + df["P_{}".format(cat_name)].values, + df["T_{}".format(cat_name)].values, + df["PET_{}".format(cat_name)].values, + ] + ) thur_catchment.set_timestep(1.0) diff --git a/doc/numerical_solver.rst b/doc/numerical_solver.rst index fe742fb..ecef147 100644 --- a/doc/numerical_solver.rst +++ b/doc/numerical_solver.rst @@ -246,4 +246,4 @@ seconds while the Numba version takes 2.35 seconds. :align: center The green line "net numba" in the lower panel express the run time of the Numba -implementation, i.e., excluding the compilation time. \ No newline at end of file +implementation, i.e., excluding the compilation time. diff --git a/doc/numerical_solver_code.py b/doc/numerical_solver_code.py index 33aca7a..f0691da 100644 --- a/doc/numerical_solver_code.py +++ b/doc/numerical_solver_code.py @@ -9,43 +9,35 @@ class CustomRootFinder(RootFinder): - def solve(self, diff_eq_fun, fluxes_fun, S0, dt, ind, args): - # Some code here return root class CustomNumericalApproximator(NumericalApproximator): - @staticmethod def _get_fluxes(fluxes_fun, S, S0, args, dt): - # Some code here return fluxes @staticmethod def _differential_equation(fluxes_fun, S, S0, dt, args, ind): - # Some code here return [diff_eq, min_val, max_val, d_diff_eq] -class CustomODESolver(): - +class CustomODESolver: # The class may implement other methods def solve(self, fluxes_fun, S0, dt, args): - # Some code here return states def get_fluxes(self, fluxes_fun, S, S0, dt, args): - # Some code here return fluxes diff --git a/doc/pics/contribute/schematic.drawio b/doc/pics/contribute/schematic.drawio index e785997..be35222 100644 --- a/doc/pics/contribute/schematic.drawio +++ b/doc/pics/contribute/schematic.drawio @@ -259,4 +259,4 @@ - \ No newline at end of file + diff --git a/doc/pics/schemas.drawio b/doc/pics/schemas.drawio index f7b0344..b66ce18 100644 --- a/doc/pics/schemas.drawio +++ b/doc/pics/schemas.drawio @@ -7031,4 +7031,4 @@ - \ No newline at end of file + diff --git a/doc/pics/uml/class_diagram.drawio b/doc/pics/uml/class_diagram.drawio index b552b46..f736d86 100644 --- a/doc/pics/uml/class_diagram.drawio +++ b/doc/pics/uml/class_diagram.drawio @@ -158,4 +158,4 @@ - \ No newline at end of file + diff --git a/doc/popular_models_code.py b/doc/popular_models_code.py index c704186..fc8bbe5 100644 --- a/doc/popular_models_code.py +++ b/doc/popular_models_code.py @@ -9,266 +9,253 @@ class InterceptionFilter(BaseElement): - _num_upstream = 1 _num_downstream = 1 def set_input(self, input): - self.input = {} - self.input['PET'] = input[0] - self.input['P'] = input[1] + self.input["PET"] = input[0] + self.input["P"] = input[1] def get_output(self, solve=True): + remove = np.minimum(self.input["PET"], self.input["P"]) - remove = np.minimum(self.input['PET'], self.input['P']) - - return [self.input['PET'] - remove, self.input['P'] - remove] + return [self.input["PET"] - remove, self.input["P"] - remove] class ProductionStore(ODEsElement): - def __init__(self, parameters, states, approximation, id): - - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._flux_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._flux_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._flux_function_python] def set_input(self, input): - self.input = {} - self.input['PET'] = input[0] - self.input['P'] = input[1] + self.input["PET"] = input[0] + self.input["P"] = input[1] def get_output(self, solve=True): - if solve: # Solve the differential equation - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the states - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) - - Pn_minus_Ps = self.input['P'] - fluxes[0][0] - Perc = - fluxes[0][2] + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) + + Pn_minus_Ps = self.input["P"] - fluxes[0][0] + Perc = -fluxes[0][2] return [Pn_minus_Ps + Perc] def get_aet(self): - try: S = self.state_array except AttributeError: - message = '{}get_aet method has to be run after running '.format(self._error_message) - message += 'the model using the method get_output' + message = "{}get_aet method has to be run after running ".format(self._error_message) + message += "the model using the method get_output" raise AttributeError(message) - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=S, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=S, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - return [- fluxes[0][1]] + return [-fluxes[0][1]] @staticmethod def _flux_function_python(S, S0, ind, P, x1, alpha, beta, ni, PET, dt): - if ind is None: - return( + return ( [ - P * (1 - (S / x1)**alpha), # Ps - - PET * (2 * (S / x1) - (S / x1)**alpha), # Evaporation - - ((x1**(1 - beta)) / ((beta - 1))) * (ni**(beta - 1)) * (S**beta) # Perc + P * (1 - (S / x1) ** alpha), # Ps + -PET * (2 * (S / x1) - (S / x1) ** alpha), # Evaporation + -((x1 ** (1 - beta)) / ((beta - 1))) * (ni ** (beta - 1)) * (S**beta), # Perc ], 0.0, - S0 + P * (1 - (S / x1)**alpha) * dt + S0 + P * (1 - (S / x1) ** alpha) * dt, ) else: - return( + return ( [ - P[ind] * (1 - (S / x1[ind])**alpha[ind]), # Ps - - PET[ind] * (2 * (S / x1[ind]) - (S / x1[ind])**alpha[ind]), # Evaporation - - ((x1[ind]**(1 - beta[ind])) / ((beta[ind] - 1))) * (ni[ind]**(beta[ind] - 1)) * (S**beta[ind]) # Perc + P[ind] * (1 - (S / x1[ind]) ** alpha[ind]), # Ps + -PET[ind] * (2 * (S / x1[ind]) - (S / x1[ind]) ** alpha[ind]), # Evaporation + -((x1[ind] ** (1 - beta[ind])) / ((beta[ind] - 1))) + * (ni[ind] ** (beta[ind] - 1)) + * (S ** beta[ind]), # Perc ], 0.0, - S0 + P[ind] * (1 - (S / x1[ind])**alpha[ind]) * dt[ind], + S0 + P[ind] * (1 - (S / x1[ind]) ** alpha[ind]) * dt[ind], [ - - (P[ind] * alpha[ind] / x1[ind]) * ((S / x1[ind])**(alpha[ind] - 1)), - - (PET[ind] / x1[ind]) * (2 - alpha[ind] * ((S / x1[ind])**(alpha[ind] - 1))), - - beta[ind] * ((x1[ind]**(1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) * (ni[ind]**(beta[ind] - 1)) * (S**(beta[ind] - 1)) - ] + -(P[ind] * alpha[ind] / x1[ind]) * ((S / x1[ind]) ** (alpha[ind] - 1)), + -(PET[ind] / x1[ind]) * (2 - alpha[ind] * ((S / x1[ind]) ** (alpha[ind] - 1))), + -beta[ind] + * ((x1[ind] ** (1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) + * (ni[ind] ** (beta[ind] - 1)) + * (S ** (beta[ind] - 1)), + ], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _flux_function_numba(S, S0, ind, P, x1, alpha, beta, ni, PET, dt): - - return( + return ( ( - P[ind] * (1 - (S / x1[ind])**alpha[ind]), # Ps - - PET[ind] * (2 * (S / x1[ind]) - (S / x1[ind])**alpha[ind]), # Evaporation - - ((x1[ind]**(1 - beta[ind])) / ((beta[ind] - 1))) * (ni[ind]**(beta[ind] - 1)) * (S**beta[ind]) # Perc + P[ind] * (1 - (S / x1[ind]) ** alpha[ind]), # Ps + -PET[ind] * (2 * (S / x1[ind]) - (S / x1[ind]) ** alpha[ind]), # Evaporation + -((x1[ind] ** (1 - beta[ind])) / ((beta[ind] - 1))) + * (ni[ind] ** (beta[ind] - 1)) + * (S ** beta[ind]), # Perc ), 0.0, - S0 + P[ind] * (1 - (S / x1[ind])**alpha[ind]) * dt[ind], + S0 + P[ind] * (1 - (S / x1[ind]) ** alpha[ind]) * dt[ind], ( - - (P[ind] * alpha[ind] / x1[ind]) * ((S / x1[ind])**(alpha[ind] - 1)), - - (PET[ind] / x1[ind]) * (2 - alpha[ind] * ((S / x1[ind])**(alpha[ind] - 1))), - - beta[ind] * ((x1[ind]**(1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) * (ni[ind]**(beta[ind] - 1)) * (S**(beta[ind] - 1)) - ) + -(P[ind] * alpha[ind] / x1[ind]) * ((S / x1[ind]) ** (alpha[ind] - 1)), + -(PET[ind] / x1[ind]) * (2 - alpha[ind] * ((S / x1[ind]) ** (alpha[ind] - 1))), + -beta[ind] + * ((x1[ind] ** (1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) + * (ni[ind] ** (beta[ind] - 1)) + * (S ** (beta[ind] - 1)), + ), ) class RoutingStore(ODEsElement): - def __init__(self, parameters, states, approximation, id): - - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._flux_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._flux_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._flux_function_python] def set_input(self, input): - self.input = {} - self.input['P'] = input[0] + self.input["P"] = input[0] def get_output(self, solve=True): - if solve: # Solve the differential equation - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the states - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - Qr = - fluxes[0][1] + Qr = -fluxes[0][1] F = -fluxes[0][2] return [Qr, F] @staticmethod def _flux_function_python(S, S0, ind, P, x2, x3, gamma, omega, dt): - if ind is None: - return( + return ( [ P, # P - - ((x3**(1 - gamma)) / ((gamma - 1))) * (S**gamma), # Qr - - (x2 * (S / x3)**omega), # F + -((x3 ** (1 - gamma)) / ((gamma - 1))) * (S**gamma), # Qr + -(x2 * (S / x3) ** omega), # F ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: - return( + return ( [ P[ind], # P - - ((x3[ind]**(1 - gamma[ind])) / ((gamma[ind] - 1))) * (S**gamma[ind]), # Qr - - (x2[ind] * (S / x3[ind])**omega[ind]), # F + -((x3[ind] ** (1 - gamma[ind])) / ((gamma[ind] - 1))) * (S ** gamma[ind]), # Qr + -(x2[ind] * (S / x3[ind]) ** omega[ind]), # F ], 0.0, S0 + P[ind] * dt[ind], [ 0.0, - - ((x3[ind]**(1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S**(gamma[ind] - 1)) * gamma[ind], - - (omega[ind] * x2[ind] * ((S / x3[ind])**(omega[ind] - 1))) - ] + -((x3[ind] ** (1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) + * (S ** (gamma[ind] - 1)) + * gamma[ind], + -(omega[ind] * x2[ind] * ((S / x3[ind]) ** (omega[ind] - 1))), + ], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _flux_function_numba(S, S0, ind, P, x2, x3, gamma, omega, dt): - - return( + return ( ( P[ind], # P - - ((x3[ind]**(1 - gamma[ind])) / ((gamma[ind] - 1))) * (S**gamma[ind]), # Qr - - (x2[ind] * (S / x3[ind])**omega[ind]), # F + -((x3[ind] ** (1 - gamma[ind])) / ((gamma[ind] - 1))) * (S ** gamma[ind]), # Qr + -(x2[ind] * (S / x3[ind]) ** omega[ind]), # F ), 0.0, S0 + P[ind] * dt[ind], ( 0.0, - - ((x3[ind]**(1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S**(gamma[ind] - 1)) * gamma[ind], - - (omega[ind] * x2[ind] * ((S / x3[ind])**(omega[ind] - 1))) - ) + -((x3[ind] ** (1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S ** (gamma[ind] - 1)) * gamma[ind], + -(omega[ind] * x2[ind] * ((S / x3[ind]) ** (omega[ind] - 1))), + ), ) class FluxAggregator(BaseElement): - _num_downstream = 1 _num_upstream = 1 def set_input(self, input): - self.input = {} - self.input['Qr'] = input[0] - self.input['F'] = input[1] - self.input['Q2_out'] = input[2] + self.input["Qr"] = input[0] + self.input["F"] = input[1] + self.input["Q2_out"] = input[2] def get_output(self, solve=True): - - return [self.input['Qr'] - + np.maximum(0, self.input['Q2_out'] - self.input['F'])] + return [self.input["Qr"] + np.maximum(0, self.input["Q2_out"] - self.input["F"])] class UnitHydrograph1(LagElement): - def __init__(self, parameters, states, id): - LagElement.__init__(self, parameters, states, id) def _build_weight(self, lag_time): - weight = [] for t in lag_time: array_length = np.ceil(t) w_i = [] for i in range(int(array_length)): - w_i.append(self._calculate_lag_area(i + 1, t) - - self._calculate_lag_area(i, t)) + w_i.append(self._calculate_lag_area(i + 1, t) - self._calculate_lag_area(i, t)) weight.append(np.array(w_i)) return weight @@ -278,28 +265,24 @@ def _calculate_lag_area(bin, len): if bin <= 0: value = 0 elif bin < len: - value = (bin / len)**2.5 + value = (bin / len) ** 2.5 else: value = 1 return value class UnitHydrograph2(LagElement): - def __init__(self, parameters, states, id): - LagElement.__init__(self, parameters, states, id) def _build_weight(self, lag_time): - weight = [] for t in lag_time: array_length = np.ceil(t) w_i = [] for i in range(int(array_length)): - w_i.append(self._calculate_lag_area(i + 1, t) - - self._calculate_lag_area(i, t)) + w_i.append(self._calculate_lag_area(i + 1, t) - self._calculate_lag_area(i, t)) weight.append(np.array(w_i)) return weight @@ -310,508 +293,465 @@ def _calculate_lag_area(bin, len): if bin <= 0: value = 0 elif bin < half_len: - value = 0.5 * (bin / half_len)**2.5 + value = 0.5 * (bin / half_len) ** 2.5 elif bin < len: - value = 1 - 0.5 * (2 - bin / half_len)**2.5 + value = 1 - 0.5 * (2 - bin / half_len) ** 2.5 else: value = 1 return value + x1, x2, x3, x4 = (50.0, 0.1, 20.0, 3.5) root_finder = PegasusPython() # Use the default parameters numerical_approximation = ImplicitEulerPython(root_finder) -interception_filter = InterceptionFilter(id='ir') +interception_filter = InterceptionFilter(id="ir") -production_store = ProductionStore(parameters={'x1': x1, 'alpha': 2.0, - 'beta': 5.0, 'ni': 4/9}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='ps') +production_store = ProductionStore( + parameters={"x1": x1, "alpha": 2.0, "beta": 5.0, "ni": 4 / 9}, + states={"S0": 10.0}, + approximation=numerical_approximation, + id="ps", +) -splitter = Splitter(weight=[[0.9], [0.1]], - direction=[[0], [0]], - id='spl') +splitter = Splitter(weight=[[0.9], [0.1]], direction=[[0], [0]], id="spl") -unit_hydrograph_1 = UnitHydrograph1(parameters={'lag-time': x4}, - states={'lag': None}, - id='uh1') +unit_hydrograph_1 = UnitHydrograph1(parameters={"lag-time": x4}, states={"lag": None}, id="uh1") -unit_hydrograph_2 = UnitHydrograph2(parameters={'lag-time': 2*x4}, - states={'lag': None}, - id='uh2') +unit_hydrograph_2 = UnitHydrograph2(parameters={"lag-time": 2 * x4}, states={"lag": None}, id="uh2") -routing_store = RoutingStore(parameters={'x2': x2, 'x3': x3, - 'gamma': 5.0, 'omega': 3.5}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='rs') +routing_store = RoutingStore( + parameters={"x2": x2, "x3": x3, "gamma": 5.0, "omega": 3.5}, + states={"S0": 10.0}, + approximation=numerical_approximation, + id="rs", +) -transparent = Transparent(id='tr') +transparent = Transparent(id="tr") -junction = Junction(direction=[[0, None], # First output - [1, None], # Second output - [None, 0]], # Third output - id='jun') +junction = Junction( + direction=[[0, None], [1, None], [None, 0]], id="jun" # First output # Second output # Third output +) -flux_aggregator = FluxAggregator(id='fa') +flux_aggregator = FluxAggregator(id="fa") -model = Unit(layers=[[interception_filter], - [production_store], - [splitter], - [unit_hydrograph_1, unit_hydrograph_2], - [routing_store, transparent], - [junction], - [flux_aggregator]], - id='model') +model = Unit( + layers=[ + [interception_filter], + [production_store], + [splitter], + [unit_hydrograph_1, unit_hydrograph_2], + [routing_store, transparent], + [junction], + [flux_aggregator], + ], + id="model", +) ## HyMod -class UpperZone(ODEsElement): +class UpperZone(ODEsElement): def __init__(self, parameters, states, approximation, id): - - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] def set_input(self, input): - - self.input = {'P': input[0], - 'PET': input[1]} + self.input = {"P": input[0], "PET": input[1]} def get_output(self, solve=True): - if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) return [-fluxes[0][2]] def get_AET(self): - try: S = self.state_array except AttributeError: - message = '{}get_aet method has to be run after running '.format(self._error_message) - message += 'the model using the method get_output' + message = "{}get_aet method has to be run after running ".format(self._error_message) + message += "the model using the method get_output" raise AttributeError(message) - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=S, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) - return [- fluxes[0][1]] + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=S, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) + return [-fluxes[0][1]] # PROTECTED METHODS @staticmethod def _fluxes_function_python(S, S0, ind, P, Smax, m, beta, PET, dt): - if ind is None: return ( [ P, - - PET * ((S / Smax) * (1 + m)) / ((S / Smax) + m), - - P * (1 - (1 - (S / Smax))**beta), + -PET * ((S / Smax) * (1 + m)) / ((S / Smax) + m), + -P * (1 - (1 - (S / Smax)) ** beta), ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), - - P[ind] * (1 - (1 - (S / Smax[ind]))**beta[ind]), + -PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), + -P[ind] * (1 - (1 - (S / Smax[ind])) ** beta[ind]), ], 0.0, S0 + P[ind] * dt[ind], [ 0.0, - - PET[ind] * m[ind] * Smax[ind] * (1 + m[ind]) / ((S + Smax[ind] * m[ind])**2), - - P[ind] * beta[ind] * ((1 - (S / Smax[ind]))**(beta[ind] - 1)) / Smax[ind], - ] + -PET[ind] * m[ind] * Smax[ind] * (1 + m[ind]) / ((S + Smax[ind] * m[ind]) ** 2), + -P[ind] * beta[ind] * ((1 - (S / Smax[ind])) ** (beta[ind] - 1)) / Smax[ind], + ], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _fluxes_function_numba(S, S0, ind, P, Smax, m, beta, PET, dt): - return ( ( P[ind], - - PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), - - P[ind] * (1 - (1 - (S / Smax[ind]))**beta[ind]), + -PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), + -P[ind] * (1 - (1 - (S / Smax[ind])) ** beta[ind]), ), 0.0, S0 + P[ind] * dt[ind], ( 0.0, - - PET[ind] * m[ind] * Smax[ind] * (1 + m[ind]) / ((S + Smax[ind] * m[ind])**2), - - P[ind] * beta[ind] * ((1 - (S / Smax[ind]))**(beta[ind] - 1)) / Smax[ind], - ) + -PET[ind] * m[ind] * Smax[ind] * (1 + m[ind]) / ((S + Smax[ind] * m[ind]) ** 2), + -P[ind] * beta[ind] * ((1 - (S / Smax[ind])) ** (beta[ind] - 1)) / Smax[ind], + ), ) class LinearReservoir(ODEsElement): - def __init__(self, parameters, states, approximation, id): - - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] # Used by get fluxes, regardless of the architecture - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] # METHODS FOR THE USER def set_input(self, input): - - self.input = {'P': input[0]} + self.input = {"P": input[0]} def get_output(self, solve=True): - if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, # I can use the python method since it is fast - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) - return [- fluxes[0][1]] + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, # I can use the python method since it is fast + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) + return [-fluxes[0][1]] # PROTECTED METHODS @staticmethod def _fluxes_function_python(S, S0, ind, P, k, dt): - if ind is None: return ( [ P, - - k * S, + -k * S, ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - k[ind] * S, + -k[ind] * S, ], 0.0, S0 + P[ind] * dt[ind], - [ - 0.0, - - k[ind] - ] + [0.0, -k[ind]], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:])", nopython=True + ) def _fluxes_function_numba(S, S0, ind, P, k, dt): - return ( ( P[ind], - - k[ind] * S, + -k[ind] * S, ), 0.0, S0 + P[ind] * dt[ind], - ( - 0.0, - - k[ind] - ) + (0.0, -k[ind]), ) root_finder = PegasusPython() # Use the default parameters numerical_approximation = ImplicitEulerPython(root_finder) -upper_zone = UpperZone(parameters={'Smax': 50.0, 'm': 0.01, 'beta': 2.0}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='uz') +upper_zone = UpperZone( + parameters={"Smax": 50.0, "m": 0.01, "beta": 2.0}, + states={"S0": 10.0}, + approximation=numerical_approximation, + id="uz", +) -splitter = Splitter(weight=[[0.6], [0.4]], - direction=[[0], [0]], - id='spl') +splitter = Splitter(weight=[[0.6], [0.4]], direction=[[0], [0]], id="spl") -channel_routing_1 = LinearReservoir(parameters={'k': 0.1}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='cr1') +channel_routing_1 = LinearReservoir( + parameters={"k": 0.1}, states={"S0": 10.0}, approximation=numerical_approximation, id="cr1" +) + +channel_routing_2 = LinearReservoir( + parameters={"k": 0.1}, states={"S0": 10.0}, approximation=numerical_approximation, id="cr2" +) -channel_routing_2 = LinearReservoir(parameters={'k': 0.1}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='cr2') +channel_routing_3 = LinearReservoir( + parameters={"k": 0.1}, states={"S0": 10.0}, approximation=numerical_approximation, id="cr3" +) -channel_routing_3 = LinearReservoir(parameters={'k': 0.1}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='cr3') +lower_zone = LinearReservoir(parameters={"k": 0.1}, states={"S0": 10.0}, approximation=numerical_approximation, id="lz") -lower_zone = LinearReservoir(parameters={'k': 0.1}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='lz') +transparent_1 = Transparent(id="tr1") -transparent_1 = Transparent(id='tr1') +transparent_2 = Transparent(id="tr2") -transparent_2 = Transparent(id='tr2') +junction = Junction(direction=[[0, 0]], id="jun") # First output -junction = Junction(direction=[[0, 0]], # First output - id='jun') +model = Unit( + layers=[ + [upper_zone], + [splitter], + [channel_routing_1, lower_zone], + [channel_routing_2, transparent_1], + [channel_routing_3, transparent_2], + [junction], + ], + id="model", +) -model = Unit(layers=[[upper_zone], - [splitter], - [channel_routing_1, lower_zone], - [channel_routing_2, transparent_1], - [channel_routing_3, transparent_2], - [junction]], - id='model') # M4 class UnsaturatedReservoir(ODEsElement): - def __init__(self, parameters, states, approximation, id): - - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] def set_input(self, input): - - self.input = {'P': input[0], - 'PET': input[1]} + self.input = {"P": input[0], "PET": input[1]} def get_output(self, solve=True): - if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) return [-fluxes[0][2]] def get_AET(self): - try: S = self.state_array except AttributeError: - message = '{}get_aet method has to be run after running '.format(self._error_message) - message += 'the model using the method get_output' + message = "{}get_aet method has to be run after running ".format(self._error_message) + message += "the model using the method get_output" raise AttributeError(message) - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=S, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) - return [- fluxes[0][1]] + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=S, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) + return [-fluxes[0][1]] # PROTECTED METHODS @staticmethod def _fluxes_function_python(S, S0, ind, P, Smax, m, beta, PET, dt): - if ind is None: return ( [ P, - - PET * ((S / Smax) * (1 + m)) / ((S / Smax) + m), - - P * (S / Smax)**beta, + -PET * ((S / Smax) * (1 + m)) / ((S / Smax) + m), + -P * (S / Smax) ** beta, ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), - - P[ind] * (S / Smax[ind])**beta[ind], + -PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), + -P[ind] * (S / Smax[ind]) ** beta[ind], ], 0.0, S0 + P[ind] * dt[ind], [ 0.0, - - (Ce[ind] * PET[ind] * m[ind] * (m[ind] + 1) * Smax[ind])/((S + m[ind] * Smax[ind])**2), - - (P[ind] * beta[ind] / Smax[ind]) * (S / Smax[ind])**(beta[ind] - 1), - ] + -(Ce[ind] * PET[ind] * m[ind] * (m[ind] + 1) * Smax[ind]) / ((S + m[ind] * Smax[ind]) ** 2), + -(P[ind] * beta[ind] / Smax[ind]) * (S / Smax[ind]) ** (beta[ind] - 1), + ], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _fluxes_function_numba(S, S0, ind, P, Smax, m, beta, PET, dt): - return ( ( P[ind], PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), - - P[ind] * (S / Smax[ind])**beta[ind], + -P[ind] * (S / Smax[ind]) ** beta[ind], ), 0.0, S0 + P[ind] * dt[ind], ( 0.0, - - (Ce[ind] * PET[ind] * m[ind] * (m[ind] + 1) * Smax[ind])/((S + m[ind] * Smax[ind])**2), - - (P[ind] * beta[ind] / Smax[ind]) * (S / Smax[ind])**(beta[ind] - 1), - ) + -(Ce[ind] * PET[ind] * m[ind] * (m[ind] + 1) * Smax[ind]) / ((S + m[ind] * Smax[ind]) ** 2), + -(P[ind] * beta[ind] / Smax[ind]) * (S / Smax[ind]) ** (beta[ind] - 1), + ), ) -class PowerReservoir(ODEsElement): +class PowerReservoir(ODEsElement): def __init__(self, parameters, states, approximation, id): - - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] # Used by get fluxes, regardless of the architecture - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] # METHODS FOR THE USER def set_input(self, input): - - self.input = {'P': input[0]} + self.input = {"P": input[0]} def get_output(self, solve=True): - if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, # I can use the python method since it is fast - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, # I can use the python method since it is fast + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - return [- fluxes[0][1]] + return [-fluxes[0][1]] # PROTECTED METHODS @staticmethod def _fluxes_function_python(S, S0, ind, P, k, alpha, dt): - if ind is None: return ( [ P, - - k * S**alpha, + -k * S**alpha, ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - k[ind] * S**alpha[ind], + -k[ind] * S ** alpha[ind], ], 0.0, S0 + P[ind] * dt[ind], - [ - 0.0, - - k[ind] * alpha[ind] * S**(alpha[ind] - 1) - ] + [0.0, -k[ind] * alpha[ind] * S ** (alpha[ind] - 1)], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _fluxes_function_numba(S, S0, ind, P, k, alpha, dt): - return ( ( P[ind], - - k[ind] * S**alpha[ind], + -k[ind] * S ** alpha[ind], ), 0.0, S0 + P[ind] * dt[ind], - ( - 0.0, - - k[ind] * alpha[ind] * S**(alpha[ind] - 1) - ) + (0.0, -k[ind] * alpha[ind] * S ** (alpha[ind] - 1)), ) @@ -819,23 +759,14 @@ def _fluxes_function_numba(S, S0, ind, P, k, alpha, dt): numeric_approximator = ImplicitEulerPython(root_finder=root_finder) ur = UnsaturatedReservoir( - parameters={'Smax': 50.0, 'Ce': 1.0, 'm': 0.01, 'beta': 2.0}, - states={'S0': 25.0}, + parameters={"Smax": 50.0, "Ce": 1.0, "m": 0.01, "beta": 2.0}, + states={"S0": 25.0}, approximation=numeric_approximator, - id='UR' + id="UR", ) fr = PowerReservoir( - parameters={'k': 0.1, 'alpha': 1.0}, - states={'S0': 10.0}, - approximation=numeric_approximator, - id='FR' + parameters={"k": 0.1, "alpha": 1.0}, states={"S0": 10.0}, approximation=numeric_approximator, id="FR" ) -model = Unit( - layers=[ - [ur], - [fr] - ], - id='M4' -) \ No newline at end of file +model = Unit(layers=[[ur], [fr]], id="M4") diff --git a/doc/reference.rst b/doc/reference.rst index 9f24d97..5f6d838 100644 --- a/doc/reference.rst +++ b/doc/reference.rst @@ -123,4 +123,4 @@ superflexpy.utils.numerical_approximator .. autoclass:: superflexpy.utils.numerical_approximator.NumericalApproximator :members: :special-members: __init__ - :show-inheritance: \ No newline at end of file + :show-inheritance: diff --git a/doc/requirements.txt b/doc/requirements.txt index f1d409c..9c6d62e 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1 +1 @@ -superflexpy \ No newline at end of file +superflexpy diff --git a/doc/share_models.rst b/doc/share_models.rst index dae20b9..86a3fb7 100644 --- a/doc/share_models.rst +++ b/doc/share_models.rst @@ -81,4 +81,4 @@ Dumping objects with Pickle Python offers the module `Pickle `_ to serialize objects to binary files. This approach enables the distribution of binary files, but -has the disadvantage of lacking transparency in the model structure. \ No newline at end of file +has the disadvantage of lacking transparency in the model structure. diff --git a/doc/share_models_code.py b/doc/share_models_code.py index 5729d51..79ec670 100644 --- a/doc/share_models_code.py +++ b/doc/share_models_code.py @@ -1,32 +1,25 @@ -from superflexpy.implementation.root_finders.pegasus import PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython -from superflexpy.implementation.elements.hbv import UnsaturatedReservoir, PowerReservoir from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.hbv import PowerReservoir, UnsaturatedReservoir +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerPython, +) +from superflexpy.implementation.root_finders.pegasus import PegasusPython root_finder = PegasusPython() numeric_approximator = ImplicitEulerPython(root_finder=root_finder) ur = UnsaturatedReservoir( - parameters={'Smax': 50.0, 'Ce': 1.0, 'm': 0.01, 'beta': 2.0}, - states={'S0': 25.0}, + parameters={"Smax": 50.0, "Ce": 1.0, "m": 0.01, "beta": 2.0}, + states={"S0": 25.0}, approximation=numeric_approximator, - id='UR' + id="UR", ) fr = PowerReservoir( - parameters={'k': 0.1, 'alpha': 1.0}, - states={'S0': 10.0}, - approximation=numeric_approximator, - id='FR' + parameters={"k": 0.1, "alpha": 1.0}, states={"S0": 10.0}, approximation=numeric_approximator, id="FR" ) -model = Unit( - layers=[ - [ur], - [fr] - ], - id='M4' -) +model = Unit(layers=[[ur], [fr]], id="M4") from superflexpy.implementation.models.m4_sf_2011 import model @@ -36,4 +29,4 @@ output = model.get_output() -from .my_new_model import model \ No newline at end of file +from .my_new_model import model diff --git a/doc/testing.rst b/doc/testing.rst index 8362806..eabb9cc 100644 --- a/doc/testing.rst +++ b/doc/testing.rst @@ -49,4 +49,4 @@ Automation Any push of new code to any branch on the github repository will trigger automatic testing based on the scripts contained in the folder -:code:`test/unittest`. \ No newline at end of file +:code:`test/unittest`. diff --git a/examples/README.md b/examples/README.md index b1ac72c..053073e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -17,4 +17,4 @@ The examples are the following: - [Replicate Hymod](./09_Hymod.ipynb) - [Replicate M02 in Dal Molin et al., HESS, 2020](./10_Thur_M2.ipynb) - [Replicate M4 in Kavetski and Fenicia, WRR, 2011](./11_M4_sfPaper.ipynb) -- [Modify M4 in Kavetski and Fenicia, WRR, 2011](./12_M4_sfPaper_changed.ipynb) \ No newline at end of file +- [Modify M4 in Kavetski and Fenicia, WRR, 2011](./12_M4_sfPaper_changed.ipynb) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..34bdb76 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,4 @@ +[tool.isort] +profile = "black" +[tool.black] +line-length = 120 diff --git a/setup.py b/setup.py index bf41890..3c648e9 100644 --- a/setup.py +++ b/setup.py @@ -1,24 +1,24 @@ -import setuptools import os +import setuptools + setuptools.setup( - name='superflexpy', - version='1.3.1', - author='Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski', - author_email='marco.dalmolin.1991@gmail.com', - description='Framework for building hydrological models', - long_description=open(os.path.join(os.path.dirname(__file__), - "README.md")).read(), - long_description_content_type='text/markdown', - url='https://superflexpy.readthedocs.io/en/latest/', - license='LGPL', + name="superflexpy", + version="1.3.1", + author="Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski", + author_email="marco.dalmolin.1991@gmail.com", + description="Framework for building hydrological models", + long_description=open(os.path.join(os.path.dirname(__file__), "README.md")).read(), + long_description_content_type="text/markdown", + url="https://superflexpy.readthedocs.io/en/latest/", + license="LGPL", packages=setuptools.find_packages(), classifiers=[ - 'Development Status :: 5 - Production/Stable', # https://martin-thoma.com/software-development-stages/ - 'Topic :: Scientific/Engineering :: Hydrology', + "Development Status :: 5 - Production/Stable", # https://martin-thoma.com/software-development-stages/ + "Topic :: Scientific/Engineering :: Hydrology", ], install_requires=[ - 'numpy>=1.19.*', - 'numba>=0.50.*', + "numpy>=1.19.*", + "numba>=0.50.*", ], ) diff --git a/superflexpy/__init__.py b/superflexpy/__init__.py index 672d4e1..a582eae 100644 --- a/superflexpy/__init__.py +++ b/superflexpy/__init__.py @@ -24,3 +24,5 @@ """ from . import framework, implementation, utils + +__all__ = ["framework", "implementation", "utils"] diff --git a/superflexpy/framework/__init__.py b/superflexpy/framework/__init__.py index a062882..d2f30c8 100644 --- a/superflexpy/framework/__init__.py +++ b/superflexpy/framework/__init__.py @@ -24,3 +24,5 @@ """ from . import element, network, node, unit + +__all__ = ["element", "network", "node", "unit"] diff --git a/superflexpy/framework/element.py b/superflexpy/framework/element.py index 8677369..9166eb0 100644 --- a/superflexpy/framework/element.py +++ b/superflexpy/framework/element.py @@ -26,11 +26,12 @@ of specialization. """ -from copy import deepcopy, copy +from copy import copy, deepcopy + import numpy as np -class BaseElement(): +class BaseElement: """ This is the abstract class for the creation of a BaseElement. A BaseElement does not have parameters or states. @@ -63,8 +64,8 @@ def __init__(self, id): """ self.id = id - self._error_message = 'module : superflexPy, Element : {},'.format(id) - self._error_message += ' Error message : ' + self._error_message = "module : superflexPy, Element : {},".format(id) + self._error_message += " Error message : " def set_input(self, input): """ @@ -77,7 +78,7 @@ def set_input(self, input): List of input fluxes to the element. """ - raise NotImplementedError('The set_input method must be implemented') + raise NotImplementedError("The set_input method must be implemented") def get_output(self, solve=True): """ @@ -95,7 +96,7 @@ def get_output(self, solve=True): List of output fluxes. """ - raise NotImplementedError('The get_output method must be implemented') + raise NotImplementedError("The get_output method must be implemented") @property def num_downstream(self): @@ -114,8 +115,7 @@ def num_upstream(self): return self._num_upstream def __repr__(self): - - str = 'Module: superflexPy\nElement: {}\n'.format(self.id) + str = "Module: superflexPy\nElement: {}\n".format(self.id) return str def __copy__(self): @@ -133,7 +133,7 @@ class ParameterizedElement(BaseElement): ParameterizedElement has parameters but not states. """ - _prefix_parameters = '' + _prefix_parameters = "" """ Prefix applied to the original names of the parameters """ @@ -206,7 +206,7 @@ def set_parameters(self, parameters): for k in parameters.keys(): if k not in self._parameters.keys(): - message = '{}The parameter {} does not exist'.format(self._error_message, k) + message = "{}The parameter {} does not exist".format(self._error_message, k) raise KeyError(message) self._parameters[k] = parameters[k] @@ -220,42 +220,39 @@ def add_prefix_parameters(self, prefix): Prefix to be added. It cannot contain '_'. """ - if '_' in prefix: - message = '{}The prefix cannot contain \'_\''.format(self._error_message) + if "_" in prefix: + message = "{}The prefix cannot contain '_'".format(self._error_message) raise ValueError(message) # Extract the prefixes in the parameters name - splitted = list(self._parameters.keys())[0].split('_') + splitted = list(self._parameters.keys())[0].split("_") if prefix not in splitted: # Apply the prefix for k in list(self._parameters.keys()): value = self._parameters.pop(k) - self._parameters['{}_{}'.format(prefix, k)] = value + self._parameters["{}_{}".format(prefix, k)] = value # Save the prefix for furure uses - self._prefix_parameters = '{}_{}'.format(prefix, self._prefix_parameters) + self._prefix_parameters = "{}_{}".format(prefix, self._prefix_parameters) def __repr__(self): - - str = 'Module: superflexPy\nElement: {}\n'.format(self.id) - str += 'Parameters:\n' + str = "Module: superflexPy\nElement: {}\n".format(self.id) + str += "Parameters:\n" for k in self._parameters: - str += '\t{} : {}\n'.format(k, self._parameters[k]) + str += "\t{} : {}\n".format(k, self._parameters[k]) return str def __copy__(self): p = self._parameters # Only the reference - ele = self.__class__(parameters=p, - id=self.id) + ele = self.__class__(parameters=p, id=self.id) ele._prefix_parameters = self._prefix_parameters return ele def __deepcopy__(self, memo): p = deepcopy(self._parameters) # Create a new dictionary - ele = self.__class__(parameters=p, - id=self.id) + ele = self.__class__(parameters=p, id=self.id) ele._prefix_parameters = self._prefix_parameters return ele @@ -266,7 +263,7 @@ class StateElement(BaseElement): StateElement has states but not parameters. """ - _prefix_states = '' + _prefix_states = "" """ Prefix applied to the original names of the parameters """ @@ -338,7 +335,7 @@ def set_states(self, states): for k in states.keys(): if k not in self._states.keys(): - message = '{}The state {} does not exist'.format(self._error_message, k) + message = "{}The state {} does not exist".format(self._error_message, k) raise KeyError(message) self._states[k] = states[k] @@ -349,7 +346,7 @@ def reset_states(self): """ for k in self._init_states.keys(): - k_no_prefix = k.split('_')[-1] + k_no_prefix = k.split("_")[-1] if self._init_states[k] is not None: self._states[self._prefix_states + k_no_prefix] = deepcopy(self._init_states[k]) # I have to isolate @@ -363,42 +360,39 @@ def add_prefix_states(self, prefix): Prefix to be added. It cannot contain '_'. """ - if '_' in prefix: - message = '{}The prefix cannot contain \'_\''.format(self._error_message) + if "_" in prefix: + message = "{}The prefix cannot contain '_'".format(self._error_message) raise ValueError(message) # Extract the prefixes in the parameters name - splitted = list(self._states.keys())[0].split('_') + splitted = list(self._states.keys())[0].split("_") if prefix not in splitted: # Apply the prefix for k in list(self._states.keys()): value = self._states.pop(k) - self._states['{}_{}'.format(prefix, k)] = value + self._states["{}_{}".format(prefix, k)] = value # Save the prefix for furure uses - self._prefix_states = '{}_{}'.format(prefix, self._prefix_states) + self._prefix_states = "{}_{}".format(prefix, self._prefix_states) def __repr__(self): - - str = 'Module: superflexPy\nElement: {}\n'.format(self.id) - str += 'States:\n' + str = "Module: superflexPy\nElement: {}\n".format(self.id) + str += "States:\n" for k in self._states: - str += '\t{} : {}\n'.format(k, self._states[k]) + str += "\t{} : {}\n".format(k, self._states[k]) return str def __copy__(self): s = deepcopy(self._states) # Create a new dictionary - ele = self.__class__(states=s, - id=self.id) + ele = self.__class__(states=s, id=self.id) ele._prefix_states = self._prefix_states return ele def __deepcopy__(self, memo): s = deepcopy(self._states) # Create a new dictionary - ele = self.__class__(states=s, - id=self.id) + ele = self.__class__(states=s, id=self.id) ele._prefix_states = self._prefix_states return ele @@ -432,23 +426,20 @@ def __init__(self, parameters, states, id): ParameterizedElement.__init__(self, parameters, id) def __repr__(self): - - str = 'Module: superflexPy\nElement: {}\n'.format(self.id) - str += 'Parameters:\n' + str = "Module: superflexPy\nElement: {}\n".format(self.id) + str += "Parameters:\n" for k in self._parameters: - str += '\t{} : {}\n'.format(k, self._parameters[k]) - str += 'States:\n' + str += "\t{} : {}\n".format(k, self._parameters[k]) + str += "States:\n" for k in self._states: - str += '\t{} : {}\n'.format(k, self._states[k]) + str += "\t{} : {}\n".format(k, self._states[k]) return str def __copy__(self): p = self._parameters # Only the reference s = deepcopy(self._states) # Create a new dictionary - ele = self.__class__(parameters=p, - states=s, - id=self.id) + ele = self.__class__(parameters=p, states=s, id=self.id) ele._prefix_states = self._prefix_states ele._prefix_parameters = self._prefix_parameters return ele @@ -456,9 +447,7 @@ def __copy__(self): def __deepcopy__(self, memo): p = deepcopy(self._parameters) # Create a new dictionary s = deepcopy(self._states) # Create a new dictionary - ele = self.__class__(parameters=p, - states=s, - id=self.id) + ele = self.__class__(parameters=p, states=s, id=self.id) ele._prefix_states = self._prefix_states ele._prefix_parameters = self._prefix_parameters return ele @@ -528,8 +517,7 @@ def __init__(self, parameters, states, approximation, id): have an id. """ - StateParameterizedElement.__init__(self, parameters=parameters, - states=states, id=id) + StateParameterizedElement.__init__(self, parameters=parameters, states=states, id=id) self._num_app = approximation @@ -577,23 +565,22 @@ def _solve_differential_equation(self, **kwargs): """ if len(self._solver_states) == 0: - message = '{}the attribute _solver_states must be filled'.format(self._error_message) + message = "{}the attribute _solver_states must be filled".format(self._error_message) raise ValueError(message) - self.state_array = self._num_app.solve(fun=self._fluxes, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - **kwargs) + self.state_array = self._num_app.solve( + fun=self._fluxes, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + **kwargs + ) def __copy__(self): p = self._parameters # Only the reference s = deepcopy(self._states) # Create a new dictionary - ele = self.__class__(parameters=p, - states=s, - id=self.id, - approximation=self._num_app) + ele = self.__class__(parameters=p, states=s, id=self.id, approximation=self._num_app) ele._prefix_states = self._prefix_states ele._prefix_parameters = self._prefix_parameters return ele @@ -601,10 +588,7 @@ def __copy__(self): def __deepcopy__(self, memo): p = deepcopy(self._parameters) # Create a new dictionary s = deepcopy(self._states) # Create a new dictionary - ele = self.__class__(parameters=p, - states=s, - id=self.id, - approximation=self._num_app) + ele = self.__class__(parameters=p, states=s, id=self.id, approximation=self._num_app) ele._prefix_states = self._prefix_states ele._prefix_parameters = self._prefix_parameters return ele @@ -656,7 +640,7 @@ def _build_weight(self, lag_time): List of weight array(s). """ - raise NotImplementedError('The _build_weight method must be implemented') + raise NotImplementedError("The _build_weight method must be implemented") def set_input(self, input): """ @@ -688,27 +672,26 @@ def get_output(self, solve=True): """ if solve: - # Create lists if we are dealing with scalars - if isinstance(self._parameters[self._prefix_parameters + 'lag-time'], float): - lag_time = [self._parameters[self._prefix_parameters + 'lag-time']] * len(self.input) - elif isinstance(self._parameters[self._prefix_parameters + 'lag-time'], list): - lag_time = self._parameters[self._prefix_parameters + 'lag-time'] + if isinstance(self._parameters[self._prefix_parameters + "lag-time"], float): + lag_time = [self._parameters[self._prefix_parameters + "lag-time"]] * len(self.input) + elif isinstance(self._parameters[self._prefix_parameters + "lag-time"], list): + lag_time = self._parameters[self._prefix_parameters + "lag-time"] else: - par_type = type(self._parameters[self._prefix_parameters + 'lag-time']) - message = '{}lag_time parameter of type {}'.format(self._error_message, par_type) + par_type = type(self._parameters[self._prefix_parameters + "lag-time"]) + message = "{}lag_time parameter of type {}".format(self._error_message, par_type) raise TypeError(message) - if self._states[self._prefix_states + 'lag'] is None: + if self._states[self._prefix_states + "lag"] is None: lag_state = self._init_lag_state(lag_time) else: - if isinstance(self._states[self._prefix_states + 'lag'], np.ndarray): - lag_state = [copy(self._states[self._prefix_states + 'lag'])] * len(self.input) - elif isinstance(self._states[self._prefix_states + 'lag'], list): - lag_state = self._states[self._prefix_states + 'lag'] + if isinstance(self._states[self._prefix_states + "lag"], np.ndarray): + lag_state = [copy(self._states[self._prefix_states + "lag"])] * len(self.input) + elif isinstance(self._states[self._prefix_states + "lag"], list): + lag_state = self._states[self._prefix_states + "lag"] else: - state_type = type(self._states[self._prefix_states + 'lag']) - message = '{}lag state of type {}'.format(self._error_message, state_type) + state_type = type(self._states[self._prefix_states + "lag"]) + message = "{}lag state of type {}".format(self._error_message, state_type) raise TypeError(message) self._weight = self._build_weight(lag_time) @@ -720,7 +703,9 @@ def get_output(self, solve=True): final_states[:, :-1] = final_states[:, 1:] final_states[:, -1] = 0 - self.set_states({self._prefix_states + 'lag': [final_states[i, :len(w)] for i, w in enumerate(self._weight)]}) + self.set_states( + {self._prefix_states + "lag": [final_states[i, : len(w)] for i, w in enumerate(self._weight)]} + ) return [self.state_array[:, i, 0] for i in range(len(self.input))] @@ -732,7 +717,7 @@ def reset_states(self): """ for k in self._init_states.keys(): - k_no_prefix = k.split('_')[-1] + k_no_prefix = k.split("_")[-1] self._states[self._prefix_states + k_no_prefix] = deepcopy(self._init_states[k]) # I have to isolate @staticmethod @@ -764,7 +749,7 @@ def _solve_lag(weight, lag_state, input): for flux_num, (w, ls, i) in enumerate(zip(weight, lag_state, input)): for ts in range(len(input[0])): updated_state = ls + i[ts] * w - output[ts, flux_num, :len(w)] = updated_state[:] + output[ts, flux_num, : len(w)] = updated_state[:] ls = np.append(updated_state[1:], 0) return output diff --git a/superflexpy/framework/network.py b/superflexpy/framework/network.py index e73a1c4..1f6005c 100644 --- a/superflexpy/framework/network.py +++ b/superflexpy/framework/network.py @@ -51,12 +51,12 @@ def __init__(self, nodes, topology): be a tree, each key has only one downstream element """ - self._error_message = 'module : superflexPy, Network ,' - self._error_message += ' Error message : ' + self._error_message = "module : superflexPy, Network ," + self._error_message += " Error message : " for n in nodes: if not isinstance(n, Node): - message = '{}units must be instance of the Unit class'.format(self._error_message) + message = "{}units must be instance of the Unit class".format(self._error_message) raise TypeError(message) self._content = nodes @@ -115,9 +115,9 @@ def get_output(self, solve=True): for cat_up in self._upstream[cat]: routed_out = self._content[self._content_pointer[cat_up]].external_routing(output[cat_up]) if len(loc_out) != len(routed_out): - message = '{}Upstream and downstream catchment have '.format(self._error_message) - message += 'different number of fluxed. ' - message += 'Upstream: {}, Local: {}'.format(len(routed_out), len(loc_out)) + message = "{}Upstream and downstream catchment have ".format(self._error_message) + message += "different number of fluxed. " + message += "Upstream: {}, Local: {}".format(len(routed_out), len(loc_out)) raise RuntimeError(message) for i in range(len(loc_out)): loc_out[i] += routed_out[i] * self._total_area[cat_up] @@ -155,9 +155,9 @@ def get_internal(self, id, attribute): cat_num, ele = self._find_attribute_from_name(id) if ele: - splitted = id.split('_') + splitted = id.split("_") if len(splitted) == 3: # element - ele_id = splitted[1] + '_' + splitted[2] + ele_id = splitted[1] + "_" + splitted[2] else: # unit ele_id = splitted[1] return self._content[cat_num].get_internal(ele_id, attribute) @@ -166,7 +166,7 @@ def get_internal(self, id, attribute): method = getattr(self._content[cat_num], attribute) return method except AttributeError: - message = '{}the attribute {} does not exist.'.format(self._error_message, attribute) + message = "{}the attribute {} does not exist.".format(self._error_message, attribute) raise AttributeError(message) def call_internal(self, id, method, **kwargs): @@ -192,9 +192,9 @@ def call_internal(self, id, method, **kwargs): cat_num, ele = self._find_attribute_from_name(id) if ele: - splitted = id.split('_') + splitted = id.split("_") if len(splitted) == 3: # element - ele_id = splitted[1] + '_' + splitted[2] + ele_id = splitted[1] + "_" + splitted[2] else: # unit ele_id = splitted[1] return self._content[cat_num].call_internal(ele_id, method, **kwargs) @@ -203,7 +203,7 @@ def call_internal(self, id, method, **kwargs): method = getattr(self._content[cat_num], method) return method(**kwargs) except AttributeError: - message = '{}the method {} does not exist.'.format(self._error_message, method) + message = "{}the method {} does not exist.".format(self._error_message, method) raise AttributeError(message) # PROTECTED METHODS @@ -281,7 +281,7 @@ def _find_attribute_from_name(self, id): element or a unit (True) or not. """ - splitted = id.split('_') + splitted = id.split("_") cat_num = self._find_content_from_name(id) # Options: None if cat_id not in id, number otherwise @@ -293,26 +293,26 @@ def _find_attribute_from_name(self, id): # MAGIC METHODS def __copy__(self): - message = '{}A Network cannot be copied'.format(self._error_message) + message = "{}A Network cannot be copied".format(self._error_message) raise AttributeError(message) def __deepcopy__(self, memo): - message = '{}A Network cannot be copied'.format(self._error_message) + message = "{}A Network cannot be copied".format(self._error_message) raise AttributeError(message) def __repr__(self): - str = 'Module: superflexPy\nNetwork class\n' - str += 'Nodes:\n' - str += '\t{}\n'.format(list(self._content_pointer.keys())) - str += 'Network:\n' - str += '\t{}\n'.format(self._downstream) + str = "Module: superflexPy\nNetwork class\n" + str += "Nodes:\n" + str += "\t{}\n".format(list(self._content_pointer.keys())) + str += "Network:\n" + str += "\t{}\n".format(self._downstream) for cat in self._content: - str += '********************\n' - str += '********************\n' - str += '********************\n' + str += "********************\n" + str += "********************\n" + str += "********************\n" str += cat.__repr__() - str += '\n' - str += '\n' + str += "\n" + str += "\n" return str diff --git a/superflexpy/framework/node.py b/superflexpy/framework/node.py index d9947ec..86b610b 100644 --- a/superflexpy/framework/node.py +++ b/superflexpy/framework/node.py @@ -26,6 +26,7 @@ """ from copy import copy, deepcopy + from ..utils.generic_component import GenericComponent from .unit import Unit @@ -63,8 +64,8 @@ def __init__(self, units, weights, area, id, parameters=None, states=None, share self.id = id - self._error_message = 'module : superflexPy, Node : {},'.format(id) - self._error_message += ' Error message : ' + self._error_message = "module : superflexPy, Node : {},".format(id) + self._error_message += " Error message : " # Handle local parameters and states if parameters is not None: @@ -77,7 +78,7 @@ def __init__(self, units, weights, area, id, parameters=None, states=None, share self._content = [] for h in units: if not isinstance(h, Unit): - message = '{}units must be instance of the Unit class'.format(self._error_message) + message = "{}units must be instance of the Unit class".format(self._error_message) raise TypeError(message) if shared_parameters: self._content.append(copy(h)) @@ -85,8 +86,7 @@ def __init__(self, units, weights, area, id, parameters=None, states=None, share self._content.append(deepcopy(h)) self.area = area - self._content_pointer = {hru.id: i - for i, hru in enumerate(self._content)} + self._content_pointer = {hru.id: i for i, hru in enumerate(self._content)} self._weights = deepcopy(weights) self.add_prefix_parameters(id, shared_parameters) self.add_prefix_states(id) @@ -181,14 +181,14 @@ def get_internal(self, id, attribute): hru_num, ele = self._find_attribute_from_name(id) if ele: - ele_id = id.split('_')[-1] + ele_id = id.split("_")[-1] return self._content[hru_num].get_internal(ele_id, attribute) else: try: method = getattr(self._content[hru_num], attribute) return method except AttributeError: - message = '{}the attribute {} does not exist.'.format(self._error_message, attribute) + message = "{}the attribute {} does not exist.".format(self._error_message, attribute) raise AttributeError(message) def call_internal(self, id, method, **kwargs): @@ -213,14 +213,14 @@ def call_internal(self, id, method, **kwargs): hru_num, ele = self._find_attribute_from_name(id) if ele: - ele_id = id.split('_')[-1] + ele_id = id.split("_")[-1] return self._content[hru_num].call_internal(ele_id, method, **kwargs) else: try: method = getattr(self._content[hru_num], method) return method(**kwargs) except AttributeError: - message = '{}the method {} does not exist.'.format(self._error_message, method) + message = "{}the method {} does not exist.".format(self._error_message, method) raise AttributeError(message) # METHODS USED BY THE FRAMEWORK @@ -237,22 +237,22 @@ def add_prefix_parameters(self, id, shared_parameters): """ # Add prefix to local parameters - if '_' in id: - message = '{}The prefix cannot contain \'_\''.format(self._error_message) + if "_" in id: + message = "{}The prefix cannot contain '_'".format(self._error_message) raise ValueError(message) if self._local_parameters: # the following block runs only if the dictionary is not empty # Extract the prefixes in the parameters name - splitted = list(self._local_parameters.keys())[0].split('_') + splitted = list(self._local_parameters.keys())[0].split("_") if id not in splitted: # Apply the prefix for k in list(self._local_parameters.keys()): value = self._local_parameters.pop(k) - self._local_parameters['{}_{}'.format(id, k)] = value + self._local_parameters["{}_{}".format(id, k)] = value # Save the prefix for furure uses - self._prefix_local_parameters = '{}_{}'.format(id, self._prefix_local_parameters) + self._prefix_local_parameters = "{}_{}".format(id, self._prefix_local_parameters) if not shared_parameters: for h in self._content: @@ -270,24 +270,24 @@ def add_prefix_states(self, id): """ # Add prefix to local states - if '_' in id: - message = '{}The prefix cannot contain \'_\''.format(self._error_message) + if "_" in id: + message = "{}The prefix cannot contain '_'".format(self._error_message) raise ValueError(message) if self._local_states: # the following block runs only if the dictionary is not empty # Extract the prefixes in the parameters name - splitted = list(self._local_states.keys())[0].split('_') + splitted = list(self._local_states.keys())[0].split("_") if id not in splitted: # Apply the prefix for k in list(self._local_states.keys()): value = self._local_states.pop(k) - self._local_states['{}_{}'.format(id, k)] = value + self._local_states["{}_{}".format(id, k)] = value value = self._init_local_states.pop(k) - self._init_local_states['{}_{}'.format(id, k)] = value + self._init_local_states["{}_{}".format(id, k)] = value # Save the prefix for furure uses - self._prefix_local_states = '{}_{}'.format(id, self._prefix_local_states) + self._prefix_local_states = "{}_{}".format(id, self._prefix_local_states) for h in self._content: h.add_prefix_states(id) @@ -327,7 +327,7 @@ def _find_attribute_from_name(self, id): element (True) or not. """ - splitted = id.split('_') + splitted = id.split("_") hru_num = self._find_content_from_name(id) @@ -336,7 +336,7 @@ def _find_attribute_from_name(self, id): elif len(splitted) == 1: return (hru_num, False) # We are looking for a unit else: - raise ValueError('Tmp for debug in node') + raise ValueError("Tmp for debug in node") def _internal_routing(self, flux): """ @@ -351,24 +351,24 @@ def _internal_routing(self, flux): # MAGIC METHODS def __copy__(self): - message = '{}A Node cannot be copied'.format(self._error_message) + message = "{}A Node cannot be copied".format(self._error_message) raise AttributeError(message) def __deepcopy__(self, memo): - message = '{}A Node cannot be copied'.format(self._error_message) + message = "{}A Node cannot be copied".format(self._error_message) raise AttributeError(message) def __repr__(self): - str = 'Module: superflexPy\nNode: {}\n'.format(self.id) - str += 'Units:\n' - str += '\t{}\n'.format([h.id for h in self._content]) - str += 'Weights:\n' - str += '\t{}\n'.format(self._weights) + str = "Module: superflexPy\nNode: {}\n".format(self.id) + str += "Units:\n" + str += "\t{}\n".format([h.id for h in self._content]) + str += "Weights:\n" + str += "\t{}\n".format(self._weights) for h in self._content: - str += '********************\n' - str += '********************\n' + str += "********************\n" + str += "********************\n" str += h.__repr__() - str += '\n' + str += "\n" return str diff --git a/superflexpy/framework/unit.py b/superflexpy/framework/unit.py index 0c22486..5930cb8 100644 --- a/superflexpy/framework/unit.py +++ b/superflexpy/framework/unit.py @@ -26,6 +26,7 @@ """ from copy import copy, deepcopy + from ..utils.generic_component import GenericComponent @@ -55,8 +56,8 @@ def __init__(self, layers, id, parameters=None, states=None, copy_pars=True): shared among the different Units. """ - self._error_message = 'module : superflexPy, Unit : {},'.format(id) - self._error_message += ' Error message : ' + self._error_message = "module : superflexPy, Unit : {},".format(id) + self._error_message += " Error message : " # Handle local parameters and states if parameters is not None: @@ -69,9 +70,9 @@ def __init__(self, layers, id, parameters=None, states=None, copy_pars=True): if copy_pars: # Deep-copy the elements self._layers = [] - for l in layers: + for layer in layers: self._layers.append([]) - for el in l: + for el in layer: self._layers[-1].append(deepcopy(el)) else: self._layers = layers @@ -233,27 +234,27 @@ def add_prefix_parameters(self, id): """ # Add prefix to local parameters - if '_' in id: - message = '{}The prefix cannot contain \'_\''.format(self._error_message) + if "_" in id: + message = "{}The prefix cannot contain '_'".format(self._error_message) raise ValueError(message) if self._local_parameters: # the following block runs only if the dictionary is not empty # Extract the prefixes in the parameters name - splitted = list(self._local_parameters.keys())[0].split('_') + splitted = list(self._local_parameters.keys())[0].split("_") if id not in splitted: # Apply the prefix for k in list(self._local_parameters.keys()): value = self._local_parameters.pop(k) - self._local_parameters['{}_{}'.format(id, k)] = value + self._local_parameters["{}_{}".format(id, k)] = value # Save the prefix for furure uses - self._prefix_local_parameters = '{}_{}'.format(id, self._prefix_local_parameters) + self._prefix_local_parameters = "{}_{}".format(id, self._prefix_local_parameters) - for l in self._layers: - for el in l: + for layer in self._layers: + for element in layer: try: - el.add_prefix_parameters(id) + element.add_prefix_parameters(id) except AttributeError: continue @@ -269,28 +270,28 @@ def add_prefix_states(self, id): """ # Add prefix to local states - if '_' in id: - message = '{}The prefix cannot contain \'_\''.format(self._error_message) + if "_" in id: + message = "{}The prefix cannot contain '_'".format(self._error_message) raise ValueError(message) if self._local_states: # the following block runs only if the dictionary is not empty # Extract the prefixes in the parameters name - splitted = list(self._local_states.keys())[0].split('_') + splitted = list(self._local_states.keys())[0].split("_") if id not in splitted: # Apply the prefix for k in list(self._local_states.keys()): value = self._local_states.pop(k) - self._local_states['{}_{}'.format(id, k)] = value + self._local_states["{}_{}".format(id, k)] = value value = self._init_local_states.pop(k) - self._init_local_states['{}_{}'.format(id, k)] = value + self._init_local_states["{}_{}".format(id, k)] = value # Save the prefix for furure uses - self._prefix_local_states = '{}_{}'.format(id, self._prefix_local_states) + self._prefix_local_states = "{}_{}".format(id, self._prefix_local_states) # add the Prefix to the elements - for l in self._layers: - for el in l: + for layer in self._layers: + for el in layer: try: el.add_prefix_states(id) except AttributeError: @@ -308,7 +309,7 @@ def _construct_dictionary(self): for i in range(len(self._layers)): for j in range(len(self._layers[i])): if self._layers[i][j].id in self._content_pointer: - message = '{}The element {} already exist.'.format(self._error_message, self._layers[i][j].id) + message = "{}The element {} already exist.".format(self._error_message, self._layers[i][j].id) raise KeyError(message) self._content_pointer[self._layers[i][j].id] = (i, j) @@ -343,7 +344,7 @@ def _find_attribute_from_name(self, id, function): try: method = getattr(element, function) except AttributeError: - message = '{}the method {} does not exist.'.format(self._error_message, function) + message = "{}the method {} does not exist.".format(self._error_message, function) raise AttributeError(message) return method @@ -356,11 +357,13 @@ def _check_layers(self): # Check layer 0 if len(self._layers[0]) != 1: - message = '{}layer 0 has {} elements.'.format(self._error_message, len(self._layers[0])) + message = "{}layer 0 has {} elements.".format(self._error_message, len(self._layers[0])) raise ValueError(message) if self._layers[0][0].num_upstream != 1: - message = '{}The element in layer 0 has {} upstream elements.'.format(self._error_message, len(self._layers[0][0].num_upstream)) + message = "{}The element in layer 0 has {} upstream elements.".format( + self._error_message, len(self._layers[0][0].num_upstream) + ) raise ValueError(message) # Check the other layers @@ -373,70 +376,67 @@ def _check_layers(self): num_upstream += el.num_upstream if num_downstream != num_upstream: - message = '{}Downstream : {}, Upstream : {}'.format(self._error_message, num_downstream, num_upstream) + message = "{}Downstream : {}, Upstream : {}".format(self._error_message, num_downstream, num_upstream) raise ValueError(message) # Check last layer if len(self._layers[-1]) != 1: - message = '{}last layer has {} elements.'.format(self._error_message, len(self._layers[-1])) + message = "{}last layer has {} elements.".format(self._error_message, len(self._layers[-1])) raise ValueError(message) if self._layers[-1][0].num_downstream != 1: - message = '{}The element in the last layer has {} downstream elements.'.format(self._error_message, len(self._layers[-1][0].num_downstream)) + message = "{}The element in the last layer has {} downstream elements.".format( + self._error_message, len(self._layers[-1][0].num_downstream) + ) raise ValueError(message) # MAGIC METHODS def __copy__(self): layers = [] - for l in self._layers: + for layer in self._layers: layers.append([]) - for el in l: + for el in layer: layers[-1].append(copy(el)) p = self._local_parameters s = deepcopy(self._local_states) - unit = self.__class__(layers=layers, - id=self.id, - parameters=p, - states=s, - copy_pars=False) # False because the copy is customized here + unit = self.__class__( + layers=layers, id=self.id, parameters=p, states=s, copy_pars=False + ) # False because the copy is customized here unit._prefix_local_parameters = self._prefix_local_parameters unit._prefix_local_states = self._prefix_local_states return unit def __deepcopy__(self, memo): - p = deepcopy(self._local_parameters) s = deepcopy(self._local_states) - unit = self.__class__(layers=self._layers, - id=self.id, - parameters=p, - states=s, - copy_pars=True) # init already implements deepcopy + unit = self.__class__( + layers=self._layers, id=self.id, parameters=p, states=s, copy_pars=True + ) # init already implements deepcopy unit._prefix_local_parameters = self._prefix_local_parameters unit._prefix_local_states = self._prefix_local_states return unit def __repr__(self): - str = 'Module: superflexPy\nUnit: {}\n'.format(self.id) - str += 'Layers:\n' + str = "Module: superflexPy\nUnit: {}\n".format(self.id) + str += "Layers:\n" id_layer = [] - for l in self._layers: + for layer in self._layers: id_layer.append([]) - for el in l: + for el in layer: id_layer[-1].append(el.id) - str += '\t{}\n'.format(id_layer) + str += "\t{}\n".format(id_layer) - for l in self._layers: - for el in l: - str += '********************\n' + for layer in self._layers: + for el in layer: + str += "********************\n" str += el.__repr__() - str += '\n' + str += "\n" return str diff --git a/superflexpy/implementation/__init__.py b/superflexpy/implementation/__init__.py index 7286bb5..2d0f167 100644 --- a/superflexpy/implementation/__init__.py +++ b/superflexpy/implementation/__init__.py @@ -24,3 +24,5 @@ """ from . import elements, models, numerical_approximators, root_finders + +__all__ = ["elements", "models", "numerical_approximators", "root_finders"] diff --git a/superflexpy/implementation/elements/__init__.py b/superflexpy/implementation/elements/__init__.py index 9d8677f..ddead7a 100644 --- a/superflexpy/implementation/elements/__init__.py +++ b/superflexpy/implementation/elements/__init__.py @@ -23,4 +23,6 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -from . import gr4j, hbv, structure_elements, thur_model_hess, hymod +from . import gr4j, hbv, hymod, structure_elements, thur_model_hess + +__all__ = ["gr4j", "hbv", "hymod", "structure_elements", "thur_model_hess"] diff --git a/superflexpy/implementation/elements/gr4j.py b/superflexpy/implementation/elements/gr4j.py index 169a198..e40dd41 100644 --- a/superflexpy/implementation/elements/gr4j.py +++ b/superflexpy/implementation/elements/gr4j.py @@ -38,9 +38,10 @@ https://doi.org/10.1016/S0022-1694(03)00225-7, 2003. """ -from ...framework.element import BaseElement, ODEsElement, LagElement -import numpy as np import numba as nb +import numpy as np + +from ...framework.element import BaseElement, LagElement, ODEsElement class InterceptionFilter(BaseElement): @@ -68,8 +69,8 @@ def set_input(self, input): """ self.input = {} - self.input['PET'] = input[0] - self.input['P'] = input[1] + self.input["PET"] = input[0] + self.input["P"] = input[1] def get_output(self, solve=True): """ @@ -89,9 +90,9 @@ def get_output(self, solve=True): 2. Net precipitation """ - remove = np.minimum(self.input['PET'], self.input['P']) + remove = np.minimum(self.input["PET"], self.input["P"]) - return [self.input['PET'] - remove, self.input['P'] - remove] + return [self.input["PET"] - remove, self.input["P"] - remove] class ProductionStore(ODEsElement): @@ -122,17 +123,13 @@ def __init__(self, parameters, states, approximation, id): have an id. """ - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._flux_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._flux_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._flux_function_python] def set_input(self, input): @@ -149,8 +146,8 @@ def set_input(self, input): """ self.input = {} - self.input['PET'] = input[0] - self.input['P'] = input[1] + self.input["PET"] = input[0] + self.input["P"] = input[1] def get_output(self, solve=True): """ @@ -171,22 +168,23 @@ def get_output(self, solve=True): if solve: # Solve the differential equation - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the states - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) - - Pn_minus_Ps = self.input['P'] - fluxes[0][0] - Perc = - fluxes[0][2] + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) + + Pn_minus_Ps = self.input["P"] - fluxes[0][0] + Perc = -fluxes[0][2] return [Pn_minus_Ps + Perc] def get_aet(self): @@ -202,67 +200,79 @@ def get_aet(self): try: S = self.state_array except AttributeError: - message = '{}get_aet method has to be run after running '.format(self._error_message) - message += 'the model using the method get_output' + message = "{}get_aet method has to be run after running ".format(self._error_message) + message += "the model using the method get_output" raise AttributeError(message) - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=S, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=S, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - return [- fluxes[0][1]] + return [-fluxes[0][1]] @staticmethod def _flux_function_python(S, S0, ind, P, x1, alpha, beta, ni, PET, dt): - if ind is None: - return( + return ( [ - P * (1 - (S / x1)**alpha), # Ps - - PET * (2 * (S / x1) - (S / x1)**alpha), # Evaporation - - ((x1**(1 - beta)) / ((beta - 1) * dt)) * (ni**(beta - 1)) * (S**beta) # Perc + P * (1 - (S / x1) ** alpha), # Ps + -PET * (2 * (S / x1) - (S / x1) ** alpha), # Evaporation + -((x1 ** (1 - beta)) / ((beta - 1) * dt)) * (ni ** (beta - 1)) * (S**beta), # Perc ], 0.0, - S0 + P * (1 - (S / x1)**alpha) * dt + S0 + P * (1 - (S / x1) ** alpha) * dt, ) else: - return( + return ( [ - P[ind] * (1 - (S / x1[ind])**alpha[ind]), # Ps - - PET[ind] * (2 * (S / x1[ind]) - (S / x1[ind])**alpha[ind]), # Evaporation - - ((x1[ind]**(1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) * (ni[ind]**(beta[ind] - 1)) * (S**beta[ind]) # Perc + P[ind] * (1 - (S / x1[ind]) ** alpha[ind]), # Ps + -PET[ind] * (2 * (S / x1[ind]) - (S / x1[ind]) ** alpha[ind]), # Evaporation + -((x1[ind] ** (1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) + * (ni[ind] ** (beta[ind] - 1)) + * (S ** beta[ind]), # Perc ], 0.0, - S0 + P[ind] * (1 - (S / x1[ind])**alpha[ind]) * dt[ind], + S0 + P[ind] * (1 - (S / x1[ind]) ** alpha[ind]) * dt[ind], [ - - (P[ind] * alpha[ind] / x1[ind]) * ((S / x1[ind])**(alpha[ind] - 1)), - - (PET[ind] / x1[ind]) * (2 - alpha[ind] * ((S / x1[ind])**(alpha[ind] - 1))), - - beta[ind] * ((x1[ind]**(1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) * (ni[ind]**(beta[ind] - 1)) * (S**(beta[ind] - 1)) - ] + -(P[ind] * alpha[ind] / x1[ind]) * ((S / x1[ind]) ** (alpha[ind] - 1)), + -(PET[ind] / x1[ind]) * (2 - alpha[ind] * ((S / x1[ind]) ** (alpha[ind] - 1))), + -beta[ind] + * ((x1[ind] ** (1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) + * (ni[ind] ** (beta[ind] - 1)) + * (S ** (beta[ind] - 1)), + ], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))" + "(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _flux_function_numba(S, S0, ind, P, x1, alpha, beta, ni, PET, dt): - - return( + return ( ( - P[ind] * (1 - (S / x1[ind])**alpha[ind]), # Ps - - PET[ind] * (2 * (S / x1[ind]) - (S / x1[ind])**alpha[ind]), # Evaporation - - ((x1[ind]**(1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) * (ni[ind]**(beta[ind] - 1)) * (S**beta[ind]) # Perc + P[ind] * (1 - (S / x1[ind]) ** alpha[ind]), # Ps + -PET[ind] * (2 * (S / x1[ind]) - (S / x1[ind]) ** alpha[ind]), # Evaporation + -((x1[ind] ** (1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) + * (ni[ind] ** (beta[ind] - 1)) + * (S ** beta[ind]), # Perc ), 0.0, - S0 + P[ind] * (1 - (S / x1[ind])**alpha[ind]) * dt[ind], + S0 + P[ind] * (1 - (S / x1[ind]) ** alpha[ind]) * dt[ind], ( - - (P[ind] * alpha[ind] / x1[ind]) * ((S / x1[ind])**(alpha[ind] - 1)), - - (PET[ind] / x1[ind]) * (2 - alpha[ind] * ((S / x1[ind])**(alpha[ind] - 1))), - - beta[ind] * ((x1[ind]**(1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) * (ni[ind]**(beta[ind] - 1)) * (S**(beta[ind] - 1)) - ) + -(P[ind] * alpha[ind] / x1[ind]) * ((S / x1[ind]) ** (alpha[ind] - 1)), + -(PET[ind] / x1[ind]) * (2 - alpha[ind] * ((S / x1[ind]) ** (alpha[ind] - 1))), + -beta[ind] + * ((x1[ind] ** (1 - beta[ind])) / ((beta[ind] - 1) * dt[ind])) + * (ni[ind] ** (beta[ind] - 1)) + * (S ** (beta[ind] - 1)), + ), ) @@ -292,17 +302,13 @@ def __init__(self, parameters, states, approximation, id): Itentifier of the element. All the elements of the framework must have an id. """ - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._flux_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._flux_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._flux_function_python] def set_input(self, input): @@ -318,7 +324,7 @@ def set_input(self, input): """ self.input = {} - self.input['P'] = input[0] + self.input["P"] = input[0] def get_output(self, solve=True): """ @@ -335,72 +341,76 @@ def get_output(self, solve=True): if solve: # Solve the differential equation - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the states - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - Qr = - fluxes[0][1] + Qr = -fluxes[0][1] F = -fluxes[0][2] return [Qr, F] @staticmethod def _flux_function_python(S, S0, ind, P, x2, x3, gamma, omega, dt): - if ind is None: - return( + return ( [ P, # P - - ((x3**(1 - gamma)) / ((gamma - 1) * dt)) * (S**gamma), # Qr - - (x2 * (S / x3)**omega), # F + -((x3 ** (1 - gamma)) / ((gamma - 1) * dt)) * (S**gamma), # Qr + -(x2 * (S / x3) ** omega), # F ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: - return( + return ( [ P[ind], # P - - ((x3[ind]**(1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S**gamma[ind]), # Qr - - (x2[ind] * (S / x3[ind])**omega[ind]), # F + -((x3[ind] ** (1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S ** gamma[ind]), # Qr + -(x2[ind] * (S / x3[ind]) ** omega[ind]), # F ], 0.0, S0 + P[ind] * dt[ind], [ 0.0, - - ((x3[ind]**(1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S**(gamma[ind] - 1)) * gamma[ind], - - (omega[ind] * x2[ind] * ((S / x3[ind])**(omega[ind] - 1))) / x3[ind] - ] + -((x3[ind] ** (1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) + * (S ** (gamma[ind] - 1)) + * gamma[ind], + -(omega[ind] * x2[ind] * ((S / x3[ind]) ** (omega[ind] - 1))) / x3[ind], + ], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))" + "(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _flux_function_numba(S, S0, ind, P, x2, x3, gamma, omega, dt): - - return( + return ( ( P[ind], # P - - ((x3[ind]**(1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S**gamma[ind]), # Qr - - (x2[ind] * (S / x3[ind])**omega[ind]), # F + -((x3[ind] ** (1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S ** gamma[ind]), # Qr + -(x2[ind] * (S / x3[ind]) ** omega[ind]), # F ), 0.0, S0 + P[ind] * dt[ind], ( 0.0, - - ((x3[ind]**(1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S**(gamma[ind] - 1)) * gamma[ind], - - (omega[ind] * x2[ind] * ((S / x3[ind])**(omega[ind] - 1))) - ) + -((x3[ind] ** (1 - gamma[ind])) / ((gamma[ind] - 1) * dt[ind])) * (S ** (gamma[ind] - 1)) * gamma[ind], + -(omega[ind] * x2[ind] * ((S / x3[ind]) ** (omega[ind] - 1))), + ), ) @@ -429,9 +439,9 @@ def set_input(self, input): """ self.input = {} - self.input['Qr'] = input[0] - self.input['F'] = input[1] - self.input['Q2_out'] = input[2] + self.input["Qr"] = input[0] + self.input["F"] = input[1] + self.input["Q2_out"] = input[2] def get_output(self, solve=True): """ @@ -444,8 +454,7 @@ def get_output(self, solve=True): 1. Total outflow """ - return [self.input['Qr'] - + np.maximum(0, self.input['Q2_out'] - self.input['F'])] + return [self.input["Qr"] + np.maximum(0, self.input["Q2_out"] - self.input["F"])] class UnitHydrograph1(LagElement): @@ -474,15 +483,13 @@ def __init__(self, parameters, states, id): LagElement.__init__(self, parameters, states, id) def _build_weight(self, lag_time): - weight = [] for t in lag_time: array_length = np.ceil(t) w_i = [] for i in range(int(array_length)): - w_i.append(self._calculate_lag_area(i + 1, t) - - self._calculate_lag_area(i, t)) + w_i.append(self._calculate_lag_area(i + 1, t) - self._calculate_lag_area(i, t)) weight.append(np.array(w_i)) return weight @@ -492,7 +499,7 @@ def _calculate_lag_area(bin, len): if bin <= 0: value = 0 elif bin < len: - value = (bin / len)**2.5 + value = (bin / len) ** 2.5 else: value = 1 return value @@ -524,15 +531,13 @@ def __init__(self, parameters, states, id): LagElement.__init__(self, parameters, states, id) def _build_weight(self, lag_time): - weight = [] for t in lag_time: array_length = np.ceil(t) w_i = [] for i in range(int(array_length)): - w_i.append(self._calculate_lag_area(i + 1, t) - - self._calculate_lag_area(i, t)) + w_i.append(self._calculate_lag_area(i + 1, t) - self._calculate_lag_area(i, t)) weight.append(np.array(w_i)) return weight @@ -543,9 +548,9 @@ def _calculate_lag_area(bin, len): if bin <= 0: value = 0 elif bin < half_len: - value = 0.5 * (bin / half_len)**2.5 + value = 0.5 * (bin / half_len) ** 2.5 elif bin < len: - value = 1 - 0.5 * (2 - bin / half_len)**2.5 + value = 1 - 0.5 * (2 - bin / half_len) ** 2.5 else: value = 1 return value diff --git a/superflexpy/implementation/elements/hbv.py b/superflexpy/implementation/elements/hbv.py index c5c24dd..5fa884f 100644 --- a/superflexpy/implementation/elements/hbv.py +++ b/superflexpy/implementation/elements/hbv.py @@ -27,9 +27,9 @@ """ -from ...framework.element import ODEsElement import numba as nb -import numpy as np + +from ...framework.element import ODEsElement class PowerReservoir(ODEsElement): @@ -57,17 +57,13 @@ def __init__(self, parameters, states, approximation, id): have an id. """ - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] # Used by get fluxes, regardless of the architecture - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] # METHODS FOR THE USER @@ -84,7 +80,7 @@ def set_input(self, input): 1. Rainfall """ - self.input = {'P': input[0]} + self.input = {"P": input[0]} def get_output(self, solve=True): """ @@ -99,67 +95,63 @@ def get_output(self, solve=True): """ if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, # I can use the python method since it is fast - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, # I can use the python method since it is fast + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - return [- fluxes[0][1]] + return [-fluxes[0][1]] # PROTECTED METHODS @staticmethod def _fluxes_function_python(S, S0, ind, P, k, alpha, dt): - if ind is None: return ( [ P, - - k * S**alpha, + -k * S**alpha, ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - k[ind] * S**alpha[ind], + -k[ind] * S ** alpha[ind], ], 0.0, S0 + P[ind] * dt[ind], - [ - 0.0, - - k[ind] * alpha[ind] * S**(alpha[ind] - 1) - ] + [0.0, -k[ind] * alpha[ind] * S ** (alpha[ind] - 1)], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _fluxes_function_numba(S, S0, ind, P, k, alpha, dt): # This method is used only when solving the equation return ( ( P[ind], - - k[ind] * S**alpha[ind], + -k[ind] * S ** alpha[ind], ), 0.0, S0 + P[ind] * dt[ind], - ( - 0.0, - - k[ind] * alpha[ind] * S**(alpha[ind] - 1) - ) + (0.0, -k[ind] * alpha[ind] * S ** (alpha[ind] - 1)), ) @@ -190,17 +182,13 @@ def __init__(self, parameters, states, approximation, id): have an id. """ - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] # METHODS FOR THE USER @@ -218,8 +206,7 @@ def set_input(self, input): 2. PET """ - self.input = {'P': input[0], - 'PET': input[1]} + self.input = {"P": input[0], "PET": input[1]} def get_output(self, solve=True): """ @@ -234,20 +221,21 @@ def get_output(self, solve=True): """ if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) return [-fluxes[0][2]] @@ -264,19 +252,20 @@ def get_AET(self): try: S = self.state_array except AttributeError: - message = '{}get_aet method has to be run after running '.format(self._error_message) - message += 'the model using the method get_output' + message = "{}get_aet method has to be run after running ".format(self._error_message) + message += "the model using the method get_output" raise AttributeError(message) - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=S, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=S, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - return [- fluxes[0][1]] + return [-fluxes[0][1]] # PROTECTED METHODS @@ -288,45 +277,48 @@ def _fluxes_function_python(S, S0, ind, P, Smax, Ce, m, beta, PET, dt): return ( [ P, - - Ce * PET * ((S / Smax) * (1 + m)) / ((S / Smax) + m), - - P * (S / Smax)**beta, + -Ce * PET * ((S / Smax) * (1 + m)) / ((S / Smax) + m), + -P * (S / Smax) ** beta, ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - Ce[ind] * PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), - - P[ind] * (S / Smax[ind])**beta[ind], + -Ce[ind] * PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), + -P[ind] * (S / Smax[ind]) ** beta[ind], ], 0.0, S0 + P[ind] * dt[ind], [ 0.0, - - (Ce[ind] * PET[ind] * m[ind] * (m[ind] + 1) * Smax[ind])/((S + m[ind] * Smax[ind])**2), - - (P[ind] * beta[ind] / Smax[ind]) * (S / Smax[ind])**(beta[ind] - 1), - ] + -(Ce[ind] * PET[ind] * m[ind] * (m[ind] + 1) * Smax[ind]) / ((S + m[ind] * Smax[ind]) ** 2), + -(P[ind] * beta[ind] / Smax[ind]) * (S / Smax[ind]) ** (beta[ind] - 1), + ], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 3), f8, f8,UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 3), f8, f8,UniTuple(f8, 3)))" + "(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _fluxes_function_numba(S, S0, ind, P, Smax, Ce, m, beta, PET, dt): # TODO: handle time variable parameters (Smax) -> overflow return ( ( P[ind], - - Ce[ind] * PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), - - P[ind] * (S / Smax[ind])**beta[ind], + -Ce[ind] * PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), + -P[ind] * (S / Smax[ind]) ** beta[ind], ), 0.0, S0 + P[ind] * dt[ind], ( 0.0, - - (Ce[ind] * PET[ind] * m[ind] * (m[ind] + 1) * Smax[ind])/((S + m[ind] * Smax[ind])**2), - - (P[ind] * beta[ind] / Smax[ind]) * (S / Smax[ind])**(beta[ind] - 1), - ) + -(Ce[ind] * PET[ind] * m[ind] * (m[ind] + 1) * Smax[ind]) / ((S + m[ind] * Smax[ind]) ** 2), + -(P[ind] * beta[ind] / Smax[ind]) * (S / Smax[ind]) ** (beta[ind] - 1), + ), ) diff --git a/superflexpy/implementation/elements/hymod.py b/superflexpy/implementation/elements/hymod.py index b67f3f8..dadd28e 100644 --- a/superflexpy/implementation/elements/hymod.py +++ b/superflexpy/implementation/elements/hymod.py @@ -41,10 +41,10 @@ """ -from ...framework.element import ODEsElement -import numpy as np import numba as nb +from ...framework.element import ODEsElement + class UpperZone(ODEsElement): """ @@ -73,17 +73,13 @@ def __init__(self, parameters, states, approximation, id): have an id. """ - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] def set_input(self, input): @@ -99,8 +95,7 @@ def set_input(self, input): 2. PET """ - self.input = {'P': input[0], - 'PET': input[1]} + self.input = {"P": input[0], "PET": input[1]} def get_output(self, solve=True): """ @@ -115,20 +110,21 @@ def get_output(self, solve=True): """ if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) return [-fluxes[0][2]] @@ -145,19 +141,20 @@ def get_AET(self): try: S = self.state_array except AttributeError: - message = '{}get_aet method has to be run after running '.format(self._error_message) - message += 'the model using the method get_output' + message = "{}get_aet method has to be run after running ".format(self._error_message) + message += "the model using the method get_output" raise AttributeError(message) - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=S, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=S, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - return [- fluxes[0][1]] + return [-fluxes[0][1]] # PROTECTED METHODS @@ -169,47 +166,50 @@ def _fluxes_function_python(S, S0, ind, P, Smax, m, beta, PET, dt): return ( [ P, - - PET * ((S / Smax) * (1 + m)) / ((S / Smax) + m), - - P * (1 - (1 - (S / Smax))**beta), + -PET * ((S / Smax) * (1 + m)) / ((S / Smax) + m), + -P * (1 - (1 - (S / Smax)) ** beta), ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), - - P[ind] * (1 - (1 - (S / Smax[ind]))**beta[ind]), + -PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), + -P[ind] * (1 - (1 - (S / Smax[ind])) ** beta[ind]), ], 0.0, S0 + P[ind] * dt[ind], [ 0.0, - - PET[ind] * m[ind] * Smax[ind] * (1 + m[ind]) / ((S + Smax[ind] * m[ind])**2), - - P[ind] * beta[ind] * ((1 - (S / Smax[ind]))**(beta[ind] - 1)) / Smax[ind], - ] + -PET[ind] * m[ind] * Smax[ind] * (1 + m[ind]) / ((S + Smax[ind] * m[ind]) ** 2), + -P[ind] * beta[ind] * ((1 - (S / Smax[ind])) ** (beta[ind] - 1)) / Smax[ind], + ], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 3), f8, f8, UniTuple(f8, 3)))" + "(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _fluxes_function_numba(S, S0, ind, P, Smax, m, beta, PET, dt): # TODO: handle time variable parameters (Smax) -> overflow return ( ( P[ind], - - PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), - - P[ind] * (1 - (1 - (S / Smax[ind]))**beta[ind]), + -PET[ind] * ((S / Smax[ind]) * (1 + m[ind])) / ((S / Smax[ind]) + m[ind]), + -P[ind] * (1 - (1 - (S / Smax[ind])) ** beta[ind]), ), 0.0, S0 + P[ind] * dt[ind], ( 0.0, - - PET[ind] * m[ind] * Smax[ind] * (1 + m[ind]) / ((S + Smax[ind] * m[ind])**2), - - P[ind] * beta[ind] * ((1 - (S / Smax[ind]))**(beta[ind] - 1)) / Smax[ind], - ) + -PET[ind] * m[ind] * Smax[ind] * (1 + m[ind]) / ((S + Smax[ind] * m[ind]) ** 2), + -P[ind] * beta[ind] * ((1 - (S / Smax[ind])) ** (beta[ind] - 1)) / Smax[ind], + ), ) @@ -238,17 +238,13 @@ def __init__(self, parameters, states, approximation, id): have an id. """ - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._fluxes_function_python] # Used by get fluxes, regardless of the architecture - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._fluxes_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._fluxes_function_python] # METHODS FOR THE USER @@ -265,7 +261,7 @@ def set_input(self, input): 1. Rainfall """ - self.input = {'P': input[0]} + self.input = {"P": input[0]} def get_output(self, solve=True): """ @@ -280,65 +276,60 @@ def get_output(self, solve=True): """ if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation() # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, # I can use the python method since it is fast - S=self.state_array, - S0=self._solver_states, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, # I can use the python method since it is fast + S=self.state_array, + S0=self._solver_states, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - return [- fluxes[0][1]] + return [-fluxes[0][1]] # PROTECTED METHODS @staticmethod def _fluxes_function_python(S, S0, ind, P, k, dt): - if ind is None: return ( [ P, - - k * S, + -k * S, ], 0.0, - S0 + P * dt + S0 + P * dt, ) else: return ( [ P[ind], - - k[ind] * S, + -k[ind] * S, ], 0.0, S0 + P[ind] * dt[ind], - [ - 0.0, - - k[ind] - ] + [0.0, -k[ind]], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:])", nopython=True + ) def _fluxes_function_numba(S, S0, ind, P, k, dt): # This method is used only when solving the equation return ( ( P[ind], - - k[ind] * S, + -k[ind] * S, ), 0.0, S0 + P[ind] * dt[ind], - ( - 0.0, - - k[ind] - ) + (0.0, -k[ind]), ) diff --git a/superflexpy/implementation/elements/structure_elements.py b/superflexpy/implementation/elements/structure_elements.py index 0c7a312..62ed4f3 100644 --- a/superflexpy/implementation/elements/structure_elements.py +++ b/superflexpy/implementation/elements/structure_elements.py @@ -26,7 +26,8 @@ complete the structure of the model. """ -from copy import copy, deepcopy +from copy import deepcopy + from ...framework.element import BaseElement @@ -152,8 +153,7 @@ def get_output(self, solve=True): for j in range(len(self._weight[i])): if self._direction[i][j] is None: continue - output[-1].append(self.input[self._direction[i][j]] - * self._weight[i][self._direction[i][j]]) + output[-1].append(self.input[self._direction[i][j]] * self._weight[i][self._direction[i][j]]) return output @@ -162,19 +162,17 @@ def get_output(self, solve=True): def __copy__(self): w = deepcopy(self._weight) # It is a 2D list -> I need to go deep d = deepcopy(self._direction) - return self.__class__(weight=w, - direction=d, - id=self.id) + return self.__class__(weight=w, direction=d, id=self.id) def __deepcopy__(self, memo): return self.__copy__() def __repr__(self): - str = 'Module: superflexPy\nElement: {}\n'.format(self.id) - str += 'Weight:\n' - str += '\t{}\n'.format(self._weight) - str += 'Direction:\n' - str += '\t{}\n'.format(self._direction) + str = "Module: superflexPy\nElement: {}\n".format(self.id) + str += "Weight:\n" + str += "\t{}\n".format(self._weight) + str += "Direction:\n" + str += "\t{}\n".format(self._direction) return str @@ -274,16 +272,15 @@ def get_output(self, solve=True): def __copy__(self): d = deepcopy(self._direction) - return self.__class__(direction=d, - id=self.id) + return self.__class__(direction=d, id=self.id) def __deepcopy__(self, memo): return self.__copy__() def __repr__(self): - str = 'Module: superflexPy\nElement: {}\n'.format(self.id) - str += 'Direction:\n' - str += '\t{}\n'.format(self._direction) + str = "Module: superflexPy\nElement: {}\n".format(self.id) + str += "Direction:\n" + str += "\t{}\n".format(self._direction) return str @@ -377,16 +374,15 @@ def get_output(self, solve=True): def __copy__(self): d = deepcopy(self._direction) - return self.__class__(direction=d, - id=self.id) + return self.__class__(direction=d, id=self.id) def __deepcopy__(self, memo): return self.__copy__() def __repr__(self): - str = 'Module: superflexPy\nElement: {}\n'.format(self.id) - str += 'Direction:\n' - str += '\t{}\n'.format(self._direction) + str = "Module: superflexPy\nElement: {}\n".format(self.id) + str += "Direction:\n" + str += "\t{}\n".format(self._direction) return str diff --git a/superflexpy/implementation/elements/thur_model_hess.py b/superflexpy/implementation/elements/thur_model_hess.py index a7a046a..48763ba 100644 --- a/superflexpy/implementation/elements/thur_model_hess.py +++ b/superflexpy/implementation/elements/thur_model_hess.py @@ -35,14 +35,13 @@ """ -from ...framework.element import LagElement, ODEsElement -from .hbv import PowerReservoir, UnsaturatedReservoir import numba as nb import numpy as np +from ...framework.element import LagElement, ODEsElement + class SnowReservoir(ODEsElement): - def __init__(self, parameters, states, approximation, id): """ This is the initializer of the class SnowReservoir. @@ -66,16 +65,12 @@ def __init__(self, parameters, states, approximation, id): have an id. """ - ODEsElement.__init__(self, - parameters=parameters, - states=states, - approximation=approximation, - id=id) + ODEsElement.__init__(self, parameters=parameters, states=states, approximation=approximation, id=id) self._fluxes_python = [self._flux_function_python] - if approximation.architecture == 'numba': + if approximation.architecture == "numba": self._fluxes = [self._flux_function_numba] - elif approximation.architecture == 'python': + elif approximation.architecture == "python": self._fluxes = [self._flux_function_python] def set_input(self, input): @@ -91,8 +86,7 @@ def set_input(self, input): 2. Temperature """ - self.input = {'P': input[0], - 'T': input[1]} + self.input = {"P": input[0], "T": input[1]} def get_output(self, solve=True): """ @@ -107,37 +101,35 @@ def get_output(self, solve=True): """ # Separate rain from snow - t0 = self._parameters[self._prefix_parameters + 't0'] + t0 = self._parameters[self._prefix_parameters + "t0"] - rain = np.where(self.input['T'] > t0, # Condition - self.input['P'], # True - 0.0) # False + rain = np.where(self.input["T"] > t0, self.input["P"], 0.0) # Condition # True # False - snow = self.input['P'] - rain + snow = self.input["P"] - rain if solve: - self._solver_states = [self._states[self._prefix_states + 'S0']] + self._solver_states = [self._states[self._prefix_states + "S0"]] self._solve_differential_equation(snow=snow) # Update the state - self.set_states({self._prefix_states + 'S0': self.state_array[-1, 0]}) - - fluxes = self._num_app.get_fluxes(fluxes=self._fluxes_python, - S=self.state_array, - S0=self._solver_states, - snow=snow, - dt=self._dt, - **self.input, - **{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters}, - ) + self.set_states({self._prefix_states + "S0": self.state_array[-1, 0]}) + + fluxes = self._num_app.get_fluxes( + fluxes=self._fluxes_python, + S=self.state_array, + S0=self._solver_states, + snow=snow, + dt=self._dt, + **self.input, + **{k[len(self._prefix_parameters) :]: self._parameters[k] for k in self._parameters}, + ) - actual_melt = - fluxes[0][1] + actual_melt = -fluxes[0][1] return [rain + actual_melt] @staticmethod def _flux_function_python(S, S0, ind, snow, T, t0, k, m, dt): - if S is None: S = 0 @@ -145,10 +137,10 @@ def _flux_function_python(S, S0, ind, snow, T, t0, k, m, dt): melt_potential = np.where(T > t0, k * T, 0.0) actual_melt = melt_potential * (1 - np.exp(-(S / m))) - return( + return ( [ snow, - - actual_melt, + -actual_melt, ], 0.0, S0 + snow * dt, @@ -158,46 +150,41 @@ def _flux_function_python(S, S0, ind, snow, T, t0, k, m, dt): melt_potential = k[ind] * T[ind] if T[ind] > t0[ind] else 0.0 actual_melt = melt_potential * (1 - np.exp(-(S / m[ind]))) - return( + return ( [ snow[ind], - - actual_melt, + -actual_melt, ], 0.0, S0 + snow[ind] * dt[ind], - [ - 0.0, - - melt_potential * np.exp(-(S / m[ind])) / m[ind] - ] + [0.0, -melt_potential * np.exp(-(S / m[ind])) / m[ind]], ) @staticmethod - @nb.jit('Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])', - nopython=True) + @nb.jit( + "Tuple((UniTuple(f8, 2), f8, f8, UniTuple(f8, 2)))" + "(optional(f8), f8, i4, f8[:], f8[:], f8[:], f8[:], f8[:], f8[:])", + nopython=True, + ) def _flux_function_numba(S, S0, ind, snow, T, t0, k, m, dt): - if S is None: S = 0 melt_potential = k[ind] * T[ind] if T[ind] > t0[ind] else 0.0 actual_melt = melt_potential * (1 - np.exp(-(S / m[ind]))) - return( + return ( ( snow[ind], - - actual_melt, + -actual_melt, ), 0.0, S0 + snow[ind] * dt[ind], - ( - 0.0, - - melt_potential * np.exp(-(S / m[ind])) / m[ind] - ) + (0.0, -melt_potential * np.exp(-(S / m[ind])) / m[ind]), ) class HalfTriangularLag(LagElement): - def __init__(self, parameters, states, id): """ This is the initializer of the half triangular lag function. @@ -218,15 +205,13 @@ def __init__(self, parameters, states, id): LagElement.__init__(self, parameters, states, id) def _build_weight(self, lag_time): - weight = [] for t in lag_time: array_length = np.ceil(t) w_i = [] for i in range(int(array_length)): - w_i.append(self._calculate_lag_area(i + 1, t) - - self._calculate_lag_area(i, t)) + w_i.append(self._calculate_lag_area(i + 1, t) - self._calculate_lag_area(i, t)) weight.append(np.array(w_i)) return weight @@ -236,7 +221,7 @@ def _calculate_lag_area(bin, len): if bin <= 0: value = 0 elif bin < len: - value = (bin / len)**2 + value = (bin / len) ** 2 else: value = 1 diff --git a/superflexpy/implementation/models/__init__.py b/superflexpy/implementation/models/__init__.py index 116a6ff..0ac8bdc 100644 --- a/superflexpy/implementation/models/__init__.py +++ b/superflexpy/implementation/models/__init__.py @@ -23,4 +23,6 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -from . import m4_sf_2011, gr4j, hymod, thur_M2 +from . import gr4j, hymod, m4_sf_2011, thur_M2 + +__all__ = ["gr4j", "hymod", "m4_sf_2011", "thur_M2"] diff --git a/superflexpy/implementation/models/gr4j.py b/superflexpy/implementation/models/gr4j.py index 1807188..51071d7 100644 --- a/superflexpy/implementation/models/gr4j.py +++ b/superflexpy/implementation/models/gr4j.py @@ -25,57 +25,69 @@ This file implements a version of the model GR4J """ -from superflexpy.implementation.root_finders.pegasus import PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython -from superflexpy.implementation.elements.gr4j import InterceptionFilter, ProductionStore, UnitHydrograph1, UnitHydrograph2, RoutingStore, FluxAggregator -from superflexpy.implementation.elements.structure_elements import Transparent, Splitter, Junction from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.gr4j import ( + FluxAggregator, + InterceptionFilter, + ProductionStore, + RoutingStore, + UnitHydrograph1, + UnitHydrograph2, +) +from superflexpy.implementation.elements.structure_elements import ( + Junction, + Splitter, + Transparent, +) +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerPython, +) +from superflexpy.implementation.root_finders.pegasus import PegasusPython x1, x2, x3, x4 = (50.0, 0.1, 20.0, 3.5) root_finder = PegasusPython() # Use the default parameters numerical_approximation = ImplicitEulerPython(root_finder) -interception_filter = InterceptionFilter(id='ir') - -production_store = ProductionStore(parameters={'x1': x1, 'alpha': 2.0, - 'beta': 5.0, 'ni': 4/9}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='ps') - -splitter = Splitter(weight=[[0.9], [0.1]], - direction=[[0], [0]], - id='spl') - -unit_hydrograph_1 = UnitHydrograph1(parameters={'lag-time': x4}, - states={'lag': None}, - id='uh1') - -unit_hydrograph_2 = UnitHydrograph2(parameters={'lag-time': 2*x4}, - states={'lag': None}, - id='uh2') - -routing_store = RoutingStore(parameters={'x2': x2, 'x3': x3, - 'gamma': 5.0, 'omega': 3.5}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='rs') - -transparent = Transparent(id='tr') - -junction = Junction(direction=[[0, None], # First output - [1, None], # Second output - [None, 0]], # Third output - id='jun') - -flux_aggregator = FluxAggregator(id='fa') - -model = Unit(layers=[[interception_filter], - [production_store], - [splitter], - [unit_hydrograph_1, unit_hydrograph_2], - [routing_store, transparent], - [junction], - [flux_aggregator]], - id='model') +interception_filter = InterceptionFilter(id="ir") + +production_store = ProductionStore( + parameters={"x1": x1, "alpha": 2.0, "beta": 5.0, "ni": 4 / 9}, + states={"S0": 10.0}, + approximation=numerical_approximation, + id="ps", +) + +splitter = Splitter(weight=[[0.9], [0.1]], direction=[[0], [0]], id="spl") + +unit_hydrograph_1 = UnitHydrograph1(parameters={"lag-time": x4}, states={"lag": None}, id="uh1") + +unit_hydrograph_2 = UnitHydrograph2(parameters={"lag-time": 2 * x4}, states={"lag": None}, id="uh2") + +routing_store = RoutingStore( + parameters={"x2": x2, "x3": x3, "gamma": 5.0, "omega": 3.5}, + states={"S0": 10.0}, + approximation=numerical_approximation, + id="rs", +) + +transparent = Transparent(id="tr") + +junction = Junction( + direction=[[0, None], [1, None], [None, 0]], id="jun" # First output # Second output # Third output +) + +flux_aggregator = FluxAggregator(id="fa") + +model = Unit( + layers=[ + [interception_filter], + [production_store], + [splitter], + [unit_hydrograph_1, unit_hydrograph_2], + [routing_store, transparent], + [junction], + [flux_aggregator], + ], + id="model", +) diff --git a/superflexpy/implementation/models/hymod.py b/superflexpy/implementation/models/hymod.py index 77a5d46..24b17b6 100644 --- a/superflexpy/implementation/models/hymod.py +++ b/superflexpy/implementation/models/hymod.py @@ -25,55 +25,58 @@ This file implements a version of the model Hymod """ -from superflexpy.implementation.root_finders.pegasus import PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython -from superflexpy.implementation.elements.hymod import UpperZone, LinearReservoir -from superflexpy.implementation.elements.structure_elements import Junction, Splitter, Transparent from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.hymod import LinearReservoir, UpperZone +from superflexpy.implementation.elements.structure_elements import ( + Junction, + Splitter, + Transparent, +) +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerPython, +) +from superflexpy.implementation.root_finders.pegasus import PegasusPython root_finder = PegasusPython() # Use the default parameters numerical_approximation = ImplicitEulerPython(root_finder) -upper_zone = UpperZone(parameters={'Smax': 50.0, 'm': 0.01, 'beta': 2.0}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='uz') - -splitter = Splitter(weight=[[0.6], [0.4]], - direction=[[0], [0]], - id='spl') - -channel_routing_1 = LinearReservoir(parameters={'k': 0.1}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='cr1') - -channel_routing_2 = LinearReservoir(parameters={'k': 0.1}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='cr2') - -channel_routing_3 = LinearReservoir(parameters={'k': 0.1}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='cr3') - -lower_zone = LinearReservoir(parameters={'k': 0.1}, - states={'S0': 10.0}, - approximation=numerical_approximation, - id='lz') - -transparent_1 = Transparent(id='tr1') - -transparent_2 = Transparent(id='tr2') - -junction = Junction(direction=[[0, 0]], # First output - id='jun') - -model = Unit(layers=[[upper_zone], - [splitter], - [channel_routing_1, lower_zone], - [channel_routing_2, transparent_1], - [channel_routing_3, transparent_2], - [junction]], - id='model') +upper_zone = UpperZone( + parameters={"Smax": 50.0, "m": 0.01, "beta": 2.0}, + states={"S0": 10.0}, + approximation=numerical_approximation, + id="uz", +) + +splitter = Splitter(weight=[[0.6], [0.4]], direction=[[0], [0]], id="spl") + +channel_routing_1 = LinearReservoir( + parameters={"k": 0.1}, states={"S0": 10.0}, approximation=numerical_approximation, id="cr1" +) + +channel_routing_2 = LinearReservoir( + parameters={"k": 0.1}, states={"S0": 10.0}, approximation=numerical_approximation, id="cr2" +) + +channel_routing_3 = LinearReservoir( + parameters={"k": 0.1}, states={"S0": 10.0}, approximation=numerical_approximation, id="cr3" +) + +lower_zone = LinearReservoir(parameters={"k": 0.1}, states={"S0": 10.0}, approximation=numerical_approximation, id="lz") + +transparent_1 = Transparent(id="tr1") + +transparent_2 = Transparent(id="tr2") + +junction = Junction(direction=[[0, 0]], id="jun") # First output + +model = Unit( + layers=[ + [upper_zone], + [splitter], + [channel_routing_1, lower_zone], + [channel_routing_2, transparent_1], + [channel_routing_3, transparent_2], + [junction], + ], + id="model", +) diff --git a/superflexpy/implementation/models/m4_sf_2011.py b/superflexpy/implementation/models/m4_sf_2011.py index c928524..81851aa 100644 --- a/superflexpy/implementation/models/m4_sf_2011.py +++ b/superflexpy/implementation/models/m4_sf_2011.py @@ -32,32 +32,25 @@ Water Resour. Res., 47, W11511, doi:10.1029/2011WR010748 """ -from superflexpy.implementation.root_finders.pegasus import PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython -from superflexpy.implementation.elements.hbv import UnsaturatedReservoir, PowerReservoir from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.hbv import PowerReservoir, UnsaturatedReservoir +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerPython, +) +from superflexpy.implementation.root_finders.pegasus import PegasusPython root_finder = PegasusPython() numeric_approximator = ImplicitEulerPython(root_finder=root_finder) ur = UnsaturatedReservoir( - parameters={'Smax': 50.0, 'Ce': 1.0, 'm': 0.01, 'beta': 2.0}, - states={'S0': 25.0}, + parameters={"Smax": 50.0, "Ce": 1.0, "m": 0.01, "beta": 2.0}, + states={"S0": 25.0}, approximation=numeric_approximator, - id='UR' + id="UR", ) fr = PowerReservoir( - parameters={'k': 0.1, 'alpha': 1.0}, - states={'S0': 10.0}, - approximation=numeric_approximator, - id='FR' + parameters={"k": 0.1, "alpha": 1.0}, states={"S0": 10.0}, approximation=numeric_approximator, id="FR" ) -model = Unit( - layers=[ - [ur], - [fr] - ], - id='M4' -) +model = Unit(layers=[[ur], [fr]], id="M4") diff --git a/superflexpy/implementation/models/thur_M2.py b/superflexpy/implementation/models/thur_M2.py index 55e01fa..9f5a901 100644 --- a/superflexpy/implementation/models/thur_M2.py +++ b/superflexpy/implementation/models/thur_M2.py @@ -33,13 +33,23 @@ Sci., 24, 1319–1345, https://doi.org/10.5194/hess-24-1319-2020, 2020. """ -from superflexpy.implementation.root_finders.pegasus import PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerPython -from superflexpy.implementation.elements.thur_model_hess import SnowReservoir, UnsaturatedReservoir, PowerReservoir, HalfTriangularLag -from superflexpy.implementation.elements.structure_elements import Transparent, Junction, Splitter -from superflexpy.framework.unit import Unit -from superflexpy.framework.node import Node from superflexpy.framework.network import Network +from superflexpy.framework.node import Node +from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.hbv import PowerReservoir, UnsaturatedReservoir +from superflexpy.implementation.elements.structure_elements import ( + Junction, + Splitter, + Transparent, +) +from superflexpy.implementation.elements.thur_model_hess import ( + HalfTriangularLag, + SnowReservoir, +) +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerPython, +) +from superflexpy.implementation.root_finders.pegasus import PegasusPython solver = PegasusPython() approximator = ImplicitEulerPython(root_finder=solver) @@ -47,84 +57,43 @@ # Fluxes in the order P, T, PET upper_splitter = Splitter( direction=[ - [0, 1, None], # P and T go to the snow reservoir - [2, None, None] # PET goes to the transparent element - ], - weight=[ - [1.0, 1.0, 0.0], - [0.0, 0.0, 1.0] + [0, 1, None], # P and T go to the snow reservoir + [2, None, None], # PET goes to the transparent element ], - id='upper-splitter' + weight=[[1.0, 1.0, 0.0], [0.0, 0.0, 1.0]], + id="upper-splitter", ) snow = SnowReservoir( - parameters={'t0': 0.0, 'k': 0.01, 'm': 2.0}, - states={'S0': 0.0}, - approximation=approximator, - id='snow' + parameters={"t0": 0.0, "k": 0.01, "m": 2.0}, states={"S0": 0.0}, approximation=approximator, id="snow" ) -upper_transparent = Transparent( - id='upper-transparent' -) +upper_transparent = Transparent(id="upper-transparent") -upper_junction = Junction( - direction=[ - [0, None], - [None, 0] - ], - id='upper-junction' -) +upper_junction = Junction(direction=[[0, None], [None, 0]], id="upper-junction") unsaturated = UnsaturatedReservoir( - parameters={'Smax': 50.0, 'Ce': 1.0, 'm': 0.01, 'beta': 2.0}, - states={'S0': 10.0}, + parameters={"Smax": 50.0, "Ce": 1.0, "m": 0.01, "beta": 2.0}, + states={"S0": 10.0}, approximation=approximator, - id='unsaturated' + id="unsaturated", ) lower_splitter = Splitter( - direction=[ - [0], - [0] - ], - weight=[ - [0.3], # Portion to slow reservoir - [0.7] # Portion to fast reservoir - ], - id='lower-splitter' + direction=[[0], [0]], + weight=[[0.3], [0.7]], # Portion to slow reservoir # Portion to fast reservoir + id="lower-splitter", ) -lag_fun = HalfTriangularLag( - parameters={'lag-time': 2.0}, - states={'lag': None}, - id='lag-fun' -) +lag_fun = HalfTriangularLag(parameters={"lag-time": 2.0}, states={"lag": None}, id="lag-fun") -fast = PowerReservoir( - parameters={'k': 0.01, 'alpha': 3.0}, - states={'S0': 0.0}, - approximation=approximator, - id='fast' -) +fast = PowerReservoir(parameters={"k": 0.01, "alpha": 3.0}, states={"S0": 0.0}, approximation=approximator, id="fast") -slow = PowerReservoir( - parameters={'k': 1e-4, 'alpha': 1.0}, - states={'S0': 0.0}, - approximation=approximator, - id='slow' -) +slow = PowerReservoir(parameters={"k": 1e-4, "alpha": 1.0}, states={"S0": 0.0}, approximation=approximator, id="slow") -lower_transparent = Transparent( - id='lower-transparent' -) +lower_transparent = Transparent(id="lower-transparent") -lower_junction = Junction( - direction=[ - [0, 0] - ], - id='lower-junction' -) +lower_junction = Junction(direction=[[0, 0]], id="lower-junction") consolidated = Unit( layers=[ @@ -137,7 +106,7 @@ [lower_transparent, fast], [lower_junction], ], - id='consolidated' + id="consolidated", ) unconsolidated = Unit( @@ -151,78 +120,28 @@ [lower_transparent, fast], [lower_junction], ], - id='unconsolidated' + id="unconsolidated", ) -andelfingen = Node( - units=[consolidated, unconsolidated], - weights=[0.24, 0.76], - area=403.3, - id='andelfingen' -) +andelfingen = Node(units=[consolidated, unconsolidated], weights=[0.24, 0.76], area=403.3, id="andelfingen") -appenzell = Node( - units=[consolidated, unconsolidated], - weights=[0.92, 0.08], - area=74.4, - id='appenzell' -) +appenzell = Node(units=[consolidated, unconsolidated], weights=[0.92, 0.08], area=74.4, id="appenzell") -frauenfeld = Node( - units=[consolidated, unconsolidated], - weights=[0.49, 0.51], - area=134.4, - id='frauenfeld' -) +frauenfeld = Node(units=[consolidated, unconsolidated], weights=[0.49, 0.51], area=134.4, id="frauenfeld") -halden = Node( - units=[consolidated, unconsolidated], - weights=[0.34, 0.66], - area=314.3, - id='halden' -) +halden = Node(units=[consolidated, unconsolidated], weights=[0.34, 0.66], area=314.3, id="halden") -herisau = Node( - units=[consolidated, unconsolidated], - weights=[0.88, 0.12], - area=16.7, - id='herisau' -) +herisau = Node(units=[consolidated, unconsolidated], weights=[0.88, 0.12], area=16.7, id="herisau") -jonschwil = Node( - units=[consolidated, unconsolidated], - weights=[0.9, 0.1], - area=401.6, - id='jonschwil' -) +jonschwil = Node(units=[consolidated, unconsolidated], weights=[0.9, 0.1], area=401.6, id="jonschwil") -mogelsberg = Node( - units=[consolidated, unconsolidated], - weights=[0.92, 0.08], - area=88.1, - id='mogelsberg' -) +mogelsberg = Node(units=[consolidated, unconsolidated], weights=[0.92, 0.08], area=88.1, id="mogelsberg") -mosnang = Node( - units=[consolidated], - weights=[1.0], - area=3.1, - id='mosnang' -) +mosnang = Node(units=[consolidated], weights=[1.0], area=3.1, id="mosnang") -stgallen = Node( - units=[consolidated, unconsolidated], - weights=[0.87, 0.13], - area=186.6, - id='stgallen' -) +stgallen = Node(units=[consolidated, unconsolidated], weights=[0.87, 0.13], area=186.6, id="stgallen") -waengi = Node( - units=[consolidated, unconsolidated], - weights=[0.63, 0.37], - area=78.9, - id='waengi' -) +waengi = Node(units=[consolidated, unconsolidated], weights=[0.63, 0.37], area=78.9, id="waengi") model = Network( nodes=[ @@ -238,15 +157,15 @@ waengi, ], topology={ - 'andelfingen': None, - 'appenzell': 'stgallen', - 'frauenfeld': 'andelfingen', - 'halden': 'andelfingen', - 'herisau': 'halden', - 'jonschwil': 'halden', - 'mogelsberg': 'jonschwil', - 'mosnang': 'jonschwil', - 'stgallen': 'halden', - 'waengi': 'frauenfeld', - } + "andelfingen": None, + "appenzell": "stgallen", + "frauenfeld": "andelfingen", + "halden": "andelfingen", + "herisau": "halden", + "jonschwil": "halden", + "mogelsberg": "jonschwil", + "mosnang": "jonschwil", + "stgallen": "halden", + "waengi": "frauenfeld", + }, ) diff --git a/superflexpy/implementation/numerical_approximators/__init__.py b/superflexpy/implementation/numerical_approximators/__init__.py index 89201c1..6343b7d 100644 --- a/superflexpy/implementation/numerical_approximators/__init__.py +++ b/superflexpy/implementation/numerical_approximators/__init__.py @@ -24,3 +24,5 @@ """ from . import explicit_euler, implicit_euler, runge_kutta_4 + +__all__ = ["explicit_euler", "implicit_euler", "runge_kutta_4"] diff --git a/superflexpy/implementation/numerical_approximators/explicit_euler.py b/superflexpy/implementation/numerical_approximators/explicit_euler.py index dae3ae7..452bae2 100644 --- a/superflexpy/implementation/numerical_approximators/explicit_euler.py +++ b/superflexpy/implementation/numerical_approximators/explicit_euler.py @@ -27,13 +27,13 @@ """ -import numpy as np import numba as nb +import numpy as np + from ...utils.numerical_approximator import NumericalApproximator class ExplicitEulerPython(NumericalApproximator): - def __init__(self, root_finder): """ This class creates an approximation of an ODE using explicit Euler and @@ -50,28 +50,30 @@ def __init__(self, root_finder): super().__init__(root_finder=root_finder) - self.architecture = 'python' - self._error_message = 'module : superflexPy, solver : explicit Euler' - self._error_message += ' Error message : ' + self.architecture = "python" + self._error_message = "module : superflexPy, solver : explicit Euler" + self._error_message += " Error message : " - if root_finder.architecture != 'python': - message = '{}: architecture of the root_finder must be python. Given {}'.format(self._error_message, root_finder.architecture) + if root_finder.architecture != "python": + message = "{}: architecture of the root_finder must be python. Given {}".format( + self._error_message, root_finder.architecture + ) raise ValueError(message) @staticmethod def _get_fluxes(fluxes, S, S0, args, dt): - # Calculate the state used to calculate the fluxes S = S[:-1] S = np.insert(S, 0, S0) - flux = fluxes(S, S0, None, *args) # If different method we would provide a different first argument. S0 not used. + flux = fluxes( + S, S0, None, *args + ) # If different method we would provide a different first argument. S0 not used. return np.array(flux[0]) # It is a list of vectors @staticmethod def _differential_equation(fluxes, S, S0, dt, args, ind): - # Specify a state in case None if S is None: S = S0 @@ -84,14 +86,15 @@ def _differential_equation(fluxes, S, S0, dt, args, ind): # Calculate the numerical approximation of the differential equation diff_eq = (S - S0) / dt[ind] - sum(fl) - return (diff_eq, # Fun to set to zero - fluxes_out[1], # Min search - fluxes_out[2], # Max search - None) # Derivative + return ( + diff_eq, # Fun to set to zero + fluxes_out[1], # Min search + fluxes_out[2], # Max search + None, + ) # Derivative class ExplicitEulerNumba(NumericalApproximator): - def __init__(self, root_finder): """ This class creates an approximation of an ODE using explicit Euler and @@ -108,29 +111,31 @@ def __init__(self, root_finder): super().__init__(root_finder=root_finder) - self.architecture = 'numba' - self._error_message = 'module : superflexPy, solver : explicit Euler' - self._error_message += ' Error message : ' + self.architecture = "numba" + self._error_message = "module : superflexPy, solver : explicit Euler" + self._error_message += " Error message : " - if root_finder.architecture != 'numba': - message = '{}: architecture of the root_finder must be numba. Given {}'.format(self._error_message, root_finder.architecture) + if root_finder.architecture != "numba": + message = "{}: architecture of the root_finder must be numba. Given {}".format( + self._error_message, root_finder.architecture + ) raise ValueError(message) @staticmethod def _get_fluxes(fluxes, S, S0, args, dt): - # Calculate the state used to calculate the fluxes. In this way S becomes S0 S = S[:-1] S = np.insert(S, 0, S0) - flux = fluxes(S, S0, None, *args) # If different method we would provide a different first argument. S0 not used. + flux = fluxes( + S, S0, None, *args + ) # If different method we would provide a different first argument. S0 not used. return np.array(flux[0]) # It is a list of vectors @staticmethod @nb.jit(nopython=True) def _differential_equation(fluxes, S, S0, dt, ind, args): - # Specify a state in case None if S is None: S = S0 @@ -143,7 +148,9 @@ def _differential_equation(fluxes, S, S0, dt, ind, args): # Calculate the numerical approximation of the differential equation diff_eq = (S - S0) / dt[ind] - sum(fl) - return (diff_eq, # Fun to set to zero - fluxes_out[1], # Min search - fluxes_out[2], # Max search - None) # Derivative + return ( + diff_eq, # Fun to set to zero + fluxes_out[1], # Min search + fluxes_out[2], # Max search + None, + ) # Derivative diff --git a/superflexpy/implementation/numerical_approximators/implicit_euler.py b/superflexpy/implementation/numerical_approximators/implicit_euler.py index d48af5c..7ad61b8 100644 --- a/superflexpy/implementation/numerical_approximators/implicit_euler.py +++ b/superflexpy/implementation/numerical_approximators/implicit_euler.py @@ -27,13 +27,13 @@ """ -import numpy as np import numba as nb +import numpy as np + from ...utils.numerical_approximator import NumericalApproximator class ImplicitEulerPython(NumericalApproximator): - def __init__(self, root_finder): """ This class creates an approximation of an ODE using implicit Euler and @@ -48,27 +48,28 @@ def __init__(self, root_finder): Solver used to find the root of the differential equation. """ - NumericalApproximator.__init__(self, - root_finder=root_finder) + NumericalApproximator.__init__(self, root_finder=root_finder) - self.architecture = 'python' - self._error_message = 'module : superflexPy, solver : implicit Euler' - self._error_message += ' Error message : ' + self.architecture = "python" + self._error_message = "module : superflexPy, solver : implicit Euler" + self._error_message += " Error message : " - if root_finder.architecture != 'python': - message = '{}: architecture of the root_finder must be python. Given {}'.format(self._error_message, root_finder.architecture) + if root_finder.architecture != "python": + message = "{}: architecture of the root_finder must be python. Given {}".format( + self._error_message, root_finder.architecture + ) raise ValueError(message) @staticmethod def _get_fluxes(fluxes, S, S0, args, dt): - - flux = fluxes(S, S0, None, *args) # If different method we would provide a different first argument. S0 not used. + flux = fluxes( + S, S0, None, *args + ) # If different method we would provide a different first argument. S0 not used. return np.array(flux[0]) # It is a list of vectors @staticmethod def _differential_equation(fluxes, S, S0, dt, args, ind): - # Specify a state in case None if S is None: S = S0 @@ -88,14 +89,15 @@ def _differential_equation(fluxes, S, S0, dt, args, ind): # in case the element does not calculate derivatives d_diff_eq = np.nan - return (diff_eq, # Fun to set to zero - fluxes_out[1], # Min search - fluxes_out[2], # Max search - d_diff_eq) # Derivative of fun + return ( + diff_eq, # Fun to set to zero + fluxes_out[1], # Min search + fluxes_out[2], # Max search + d_diff_eq, + ) # Derivative of fun class ImplicitEulerNumba(NumericalApproximator): - def __init__(self, root_finder): """ This class creates an approximation of an ODE using implicit Euler and @@ -110,28 +112,29 @@ def __init__(self, root_finder): Solver used to find the root of the differential equation. """ - NumericalApproximator.__init__(self, - root_finder=root_finder) + NumericalApproximator.__init__(self, root_finder=root_finder) - self.architecture = 'numba' - self._error_message = 'module : superflexPy, solver : implicit Euler' - self._error_message += ' Error message : ' + self.architecture = "numba" + self._error_message = "module : superflexPy, solver : implicit Euler" + self._error_message += " Error message : " - if root_finder.architecture != 'numba': - message = '{}: architecture of the root_finder must be numba. Given {}'.format(self._error_message, root_finder.architecture) + if root_finder.architecture != "numba": + message = "{}: architecture of the root_finder must be numba. Given {}".format( + self._error_message, root_finder.architecture + ) raise ValueError(message) @staticmethod # I do not use numba. Do not need it def _get_fluxes(fluxes, S, S0, args, dt): - - flux = fluxes(S, S0, None, *args) # If different method we would provide a different first argument. S0 not used. + flux = fluxes( + S, S0, None, *args + ) # If different method we would provide a different first argument. S0 not used. return np.array(flux[0]) # It is a list of vectors @staticmethod @nb.jit(nopython=True) def _differential_equation(fluxes, S, S0, dt, ind, args): - # Specify a state in case None if S is None: S = S0 @@ -155,7 +158,9 @@ def _differential_equation(fluxes, S, S0, dt, ind, args): d_diff_eq = (1 / dt[ind]) - sum_d_flux - return (diff_eq, # Fun to set to zero - fluxes_out[1], # Min search - fluxes_out[2], # Max search - d_diff_eq) # Derivative of fun + return ( + diff_eq, # Fun to set to zero + fluxes_out[1], # Min search + fluxes_out[2], # Max search + d_diff_eq, + ) # Derivative of fun diff --git a/superflexpy/implementation/numerical_approximators/runge_kutta_4.py b/superflexpy/implementation/numerical_approximators/runge_kutta_4.py index 8eee864..99493c3 100644 --- a/superflexpy/implementation/numerical_approximators/runge_kutta_4.py +++ b/superflexpy/implementation/numerical_approximators/runge_kutta_4.py @@ -26,13 +26,13 @@ implicit Runge Kutta of 4th order numerical approximation. """ -import numpy as np import numba as nb +import numpy as np + from ...utils.numerical_approximator import NumericalApproximator class RungeKutta4Python(NumericalApproximator): - def __init__(self, root_finder): """ This class creates an approximation of an ODE using Runge Kutta of 4th @@ -52,18 +52,18 @@ def __init__(self, root_finder): super().__init__(root_finder=root_finder) - self.architecture = 'python' - self._error_message = 'module : superflexPy, solver : Runge Kutta 4' - self._error_message += ' Error message : ' + self.architecture = "python" + self._error_message = "module : superflexPy, solver : Runge Kutta 4" + self._error_message += " Error message : " - if root_finder.architecture != 'python': - message = '{}: architecture of the root_finder must be python. Given {}'.format(self._error_message, root_finder.architecture) + if root_finder.architecture != "python": + message = "{}: architecture of the root_finder must be python. Given {}".format( + self._error_message, root_finder.architecture + ) raise ValueError(message) - @staticmethod def _get_fluxes(fluxes, S, S0, args, dt): - # Calculate the state used to calculate the fluxes S = S[:-1] S = np.insert(S, 0, S0) # In the following, S is actually S0 @@ -82,7 +82,6 @@ def _get_fluxes(fluxes, S, S0, args, dt): @staticmethod def _differential_equation(fluxes, S, S0, dt, args, ind): - # Specify a state in case None if S is None: S = S0 @@ -100,14 +99,15 @@ def _differential_equation(fluxes, S, S0, dt, args, ind): # No need to calculate the derivative since the method is explicit - return (fun_to_zero, # Fun to set to zero - min_S, # Min search - max_S, # Max search - None) # Derivative of fun -> don't need it because explicit + return ( + fun_to_zero, # Fun to set to zero + min_S, # Min search + max_S, # Max search + None, + ) # Derivative of fun -> don't need it because explicit class RungeKutta4Numba(NumericalApproximator): - def __init__(self, root_finder): """ This class creates an approximation of an ODE using Runge Kutta of 4th @@ -127,17 +127,18 @@ def __init__(self, root_finder): super().__init__(root_finder=root_finder) - self.architecture = 'numba' - self._error_message = 'module : superflexPy, solver : Runge Kutta 4' - self._error_message += ' Error message : ' + self.architecture = "numba" + self._error_message = "module : superflexPy, solver : Runge Kutta 4" + self._error_message += " Error message : " - if root_finder.architecture != 'numba': - message = '{}: architecture of the root_finder must be numba. Given {}'.format(self._error_message, root_finder.architecture) + if root_finder.architecture != "numba": + message = "{}: architecture of the root_finder must be numba. Given {}".format( + self._error_message, root_finder.architecture + ) raise ValueError(message) @staticmethod def _get_fluxes(fluxes, S, S0, args, dt): - # Calculate the state used to calculate the fluxes S = S[:-1] S = np.insert(S, 0, S0) # In the following, S is actually S0 @@ -157,7 +158,6 @@ def _get_fluxes(fluxes, S, S0, args, dt): @staticmethod @nb.jit(nopython=True) def _differential_equation(fluxes, S, S0, dt, args, ind): - # Specify a state in case None if S is None: S = S0 @@ -175,7 +175,9 @@ def _differential_equation(fluxes, S, S0, dt, args, ind): # No need to calculate the derivative since the method is explicit - return (fun_to_zero, # Fun to set to zero - min_S, # Min search - max_S, # Max search - None) # Derivative of fun -> don't need it because explicit + return ( + fun_to_zero, # Fun to set to zero + min_S, # Min search + max_S, # Max search + None, + ) # Derivative of fun -> don't need it because explicit diff --git a/superflexpy/implementation/root_finders/__init__.py b/superflexpy/implementation/root_finders/__init__.py index bf6f2b5..5ed8183 100644 --- a/superflexpy/implementation/root_finders/__init__.py +++ b/superflexpy/implementation/root_finders/__init__.py @@ -24,3 +24,5 @@ """ from . import explicit, newton, pegasus + +__all__ = ["explicit", "newton", "pegasus"] diff --git a/superflexpy/implementation/root_finders/explicit.py b/superflexpy/implementation/root_finders/explicit.py index f0fcbe7..c5006f4 100644 --- a/superflexpy/implementation/root_finders/explicit.py +++ b/superflexpy/implementation/root_finders/explicit.py @@ -29,6 +29,7 @@ import numba as nb + from ...utils.root_finder import RootFinder @@ -45,14 +46,12 @@ def __init__(self): needed. """ - super().__init__(tol_F=None, - tol_x=None, - iter_max=None) + super().__init__(tol_F=None, tol_x=None, iter_max=None) - self._name = 'ExplicitRootFinderPython' - self.architecture = 'python' - self._error_message = 'module : superflexPy, solver : {},'.format(self._name) - self._error_message += ' Error message : ' + self._name = "ExplicitRootFinderPython" + self.architecture = "python" + self._error_message = "module : superflexPy, solver : {},".format(self._name) + self._error_message += " Error message : " def solve(self, diff_eq, fluxes, S0, dt, ind, args): """ @@ -88,7 +87,7 @@ def solve(self, diff_eq, fluxes, S0, dt, ind, args): Root of the function """ - return - diff_eq(fluxes=fluxes, S=0, S0=S0, dt=dt, args=args, ind=ind)[0] + return -diff_eq(fluxes=fluxes, S=0, S0=S0, dt=dt, args=args, ind=ind)[0] class ExplicitNumba(RootFinder): @@ -104,17 +103,14 @@ def __init__(self): needed. """ - super().__init__(tol_F=None, - tol_x=None, - iter_max=None) + super().__init__(tol_F=None, tol_x=None, iter_max=None) - self._name = 'ExplicitRootFinderPython' - self.architecture = 'python' - self._error_message = 'module : superflexPy, solver : {},'.format(self._name) - self._error_message += ' Error message : ' + self._name = "ExplicitRootFinderPython" + self.architecture = "python" + self._error_message = "module : superflexPy, solver : {},".format(self._name) + self._error_message += " Error message : " @staticmethod @nb.jit(nopython=True) def solve(diff_eq, fluxes, S0, dt, ind, args, tol_F, tol_x, iter_max): - - return - diff_eq(fluxes=fluxes, S=0, S0=S0, dt=dt, args=args, ind=ind)[0] + return -diff_eq(fluxes=fluxes, S=0, S0=S0, dt=dt, args=args, ind=ind)[0] diff --git a/superflexpy/implementation/root_finders/newton.py b/superflexpy/implementation/root_finders/newton.py index 0118064..30d86c6 100644 --- a/superflexpy/implementation/root_finders/newton.py +++ b/superflexpy/implementation/root_finders/newton.py @@ -26,8 +26,9 @@ solution is forced to be bounded by the limits of acceptability. """ -import numpy as np import numba as nb +import numpy as np + from ...utils.root_finder import RootFinder @@ -52,13 +53,11 @@ def __init__(self, tol_F=1e-8, tol_x=1e-8, iter_max=10): Maximum number of iteration of the solver. After this value it raises a runtime error """ - super().__init__(tol_F=tol_F, - tol_x=tol_x, - iter_max=iter_max) - self._name = 'NewtonPython' - self.architecture = 'python' - self._error_message = 'module : superflexPy, solver : {},'.format(self._name) - self._error_message += ' Error message : ' + super().__init__(tol_F=tol_F, tol_x=tol_x, iter_max=iter_max) + self._name = "NewtonPython" + self.architecture = "python" + self._error_message = "module : superflexPy, solver : {},".format(self._name) + self._error_message += " Error message : " def solve(self, diff_eq, fluxes, S0, dt, ind, args): """ @@ -116,14 +115,13 @@ def solve(self, diff_eq, fluxes, S0, dt, ind, args): need_solve = False if fa * fb > 0: - message = '{}fa and fb have the same sign: {} vs {}'.format(self._error_message, fa, fb) + message = "{}fa and fb have the same sign: {} vs {}".format(self._error_message, fa, fb) raise ValueError(message) if need_solve: root = (a_orig + b_orig) / 2 for j in range(self._iter_max): - f, *_, df = diff_eq(fluxes=fluxes, S=root, S0=S0, dt=dt, args=args, ind=ind) if np.abs(f) < self._tol_F: @@ -139,7 +137,7 @@ def solve(self, diff_eq, fluxes, S0, dt, ind, args): a = root # Calculate new root - dx = - f / df + dx = -f / df root = root + dx if np.abs(dx) < self._tol_x: @@ -163,7 +161,7 @@ def solve(self, diff_eq, fluxes, S0, dt, ind, args): root = (a + b) / 2 if j + 1 == self._iter_max: - message = '{}not converged. iter_max : {}'.format(self._error_message, self._iter_max) + message = "{}not converged. iter_max : {}".format(self._error_message, self._iter_max) raise RuntimeError(message) return output @@ -190,18 +188,15 @@ def __init__(self, tol_F=1e-8, tol_x=1e-8, iter_max=10): Maximum number of iteration of the solver. After this value it raises a runtime error """ - super().__init__(tol_F=tol_F, - tol_x=tol_x, - iter_max=iter_max) - self._name = 'NewtonNumba' - self.architecture = 'numba' - self._error_message = 'module : superflexPy, solver : {},'.format(self._name) - self._error_message += ' Error message : ' + super().__init__(tol_F=tol_F, tol_x=tol_x, iter_max=iter_max) + self._name = "NewtonNumba" + self.architecture = "numba" + self._error_message = "module : superflexPy, solver : {},".format(self._name) + self._error_message += " Error message : " @staticmethod @nb.jit(nopython=True) def solve(diff_eq, fluxes, S0, dt, ind, args, tol_F, tol_x, iter_max): - a_orig, b_orig = diff_eq(fluxes=fluxes, S=None, S0=S0, dt=dt, args=args, ind=ind)[1:3] # Swap if a_orig > b_orig @@ -231,7 +226,6 @@ def solve(diff_eq, fluxes, S0, dt, ind, args, tol_F, tol_x, iter_max): root = (a_orig + b_orig) / 2 for j in range(iter_max): - f, *_, df = diff_eq(fluxes=fluxes, S=root, S0=S0, dt=dt, args=args, ind=ind) if np.abs(f) < tol_F: @@ -247,7 +241,7 @@ def solve(diff_eq, fluxes, S0, dt, ind, args, tol_F, tol_x, iter_max): a = root # Calculate new root - dx = - f / df + dx = -f / df root = root + dx if np.abs(dx) < tol_x: diff --git a/superflexpy/implementation/root_finders/pegasus.py b/superflexpy/implementation/root_finders/pegasus.py index cc05644..3278185 100644 --- a/superflexpy/implementation/root_finders/pegasus.py +++ b/superflexpy/implementation/root_finders/pegasus.py @@ -30,8 +30,9 @@ Dowell, M. & Jarratt, P. BIT (1972) 12: 503. https://doi.org/10.1007/BF01932959 """ -import numpy as np import numba as nb +import numpy as np + from ...utils.root_finder import RootFinder @@ -56,13 +57,11 @@ def __init__(self, tol_F=1e-8, tol_x=1e-8, iter_max=10): Maximum number of iteration of the solver. After this value it raises a runtime error """ - super().__init__(tol_F=tol_F, - tol_x=tol_x, - iter_max=iter_max) - self._name = 'PegasusPython' - self.architecture = 'python' - self._error_message = 'module : superflexPy, solver : {},'.format(self._name) - self._error_message += ' Error message : ' + super().__init__(tol_F=tol_F, tol_x=tol_x, iter_max=iter_max) + self._name = "PegasusPython" + self.architecture = "python" + self._error_message = "module : superflexPy, solver : {},".format(self._name) + self._error_message += " Error message : " def solve(self, diff_eq, fluxes, S0, dt, ind, args): """ @@ -113,14 +112,12 @@ def solve(self, diff_eq, fluxes, S0, dt, ind, args): need_solve = False if fa * fb > 0 and need_solve: - message = '{}fa and fb have the same sign: {} vs {}'.format(self._error_message, fa, fb) + message = "{}fa and fb have the same sign: {} vs {}".format(self._error_message, fa, fb) raise ValueError(message) if need_solve: - # Iterate the solver for j in range(self._iter_max): - xmin = min(a, b) xmax = max(a, b) @@ -155,7 +152,7 @@ def solve(self, diff_eq, fluxes, S0, dt, ind, args): break if j + 1 == self._iter_max: - message = '{}not converged. iter_max : {}'.format(self._error_message, self._iter_max) + message = "{}not converged. iter_max : {}".format(self._error_message, self._iter_max) raise RuntimeError(message) return output @@ -182,18 +179,15 @@ def __init__(self, tol_F=1e-8, tol_x=1e-8, iter_max=10): Maximum number of iteration of the solver. After this value it raises a runtime error """ - super().__init__(tol_F=tol_F, - tol_x=tol_x, - iter_max=iter_max) - self._name = 'PegasusNumba' - self.architecture = 'numba' - self._error_message = 'module : superflexPy, solver : {},'.format(self._name) - self._error_message += ' Error message : ' + super().__init__(tol_F=tol_F, tol_x=tol_x, iter_max=iter_max) + self._name = "PegasusNumba" + self.architecture = "numba" + self._error_message = "module : superflexPy, solver : {},".format(self._name) + self._error_message += " Error message : " @staticmethod @nb.jit(nopython=True) def solve(diff_eq, fluxes, S0, dt, ind, args, tol_F, tol_x, iter_max): - a, b = diff_eq(fluxes=fluxes, S=None, S0=S0, dt=dt, ind=ind, args=args)[1:3] fa = diff_eq(fluxes=fluxes, S=a, S0=S0, dt=dt, ind=ind, args=args)[0] fb = diff_eq(fluxes=fluxes, S=b, S0=S0, dt=dt, ind=ind, args=args)[0] @@ -214,10 +208,8 @@ def solve(diff_eq, fluxes, S0, dt, ind, args, tol_F, tol_x, iter_max): need_solve = False if need_solve: - # Iterate the solver for j in range(iter_max): - xmin = min(a, b) xmax = max(a, b) diff --git a/superflexpy/utils/__init__.py b/superflexpy/utils/__init__.py index b9ef749..fa006f7 100644 --- a/superflexpy/utils/__init__.py +++ b/superflexpy/utils/__init__.py @@ -23,4 +23,6 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -from . import generic_component, root_finder, numerical_approximator +from . import generic_component, numerical_approximator, root_finder + +__all__ = ["generic_component", "numerical_approximator", "root_finder"] diff --git a/superflexpy/utils/generic_component.py b/superflexpy/utils/generic_component.py index 0732f57..0a4db89 100644 --- a/superflexpy/utils/generic_component.py +++ b/superflexpy/utils/generic_component.py @@ -60,12 +60,12 @@ class GenericComponent(object): to the component, at initialization. """ - _prefix_local_parameters = '' + _prefix_local_parameters = "" """ Prefix applied to local parameters """ - _prefix_local_states = '' + _prefix_local_states = "" """ Prefix applied to local states """ @@ -113,7 +113,12 @@ def get_parameters(self, names=None): try: cont_pars = self._content[position].get_parameters([n]) break - except (AttributeError, KeyError): # Attribute error because the content may not have the method, Key error because the parameter may not belong to the content + except ( + AttributeError, + KeyError, + ): + # Attribute error because the content may not have the method, + # Key error because the parameter may not belong to the content continue elif position == -1: # it means local cont_pars = {n: self._local_parameters[n]} @@ -153,7 +158,7 @@ def _find_content_from_name(self, name): Index of the component in self._content """ - splitted_name = name.split('_') + splitted_name = name.split("_") try: class_id = self.id @@ -251,7 +256,12 @@ def get_states(self, names=None): try: cont_st = self._content[position].get_states([n]) break - except (AttributeError, KeyError): # Attribute error because the content may not have the method, Key error because the parameter may not belong to the content + except ( + AttributeError, + KeyError, + ): + # Attribute error because the content may not have the method, + # Key error because the parameter may not belong to the content continue elif position == -1: cont_st = {n: self._local_states[n]} @@ -336,7 +346,7 @@ def reset_states(self, id=None): if i == local_id and self._init_local_states: self.set_states(states=self._init_local_states) elif i != local_id: - i += '_X' # Needed to work with find_content_from_name + i += "_X" # Needed to work with find_content_from_name position = self._find_content_from_name(i) self._content[position].reset_states() diff --git a/superflexpy/utils/numerical_approximator.py b/superflexpy/utils/numerical_approximator.py index 7e5ddcd..effa0af 100644 --- a/superflexpy/utils/numerical_approximator.py +++ b/superflexpy/utils/numerical_approximator.py @@ -25,12 +25,13 @@ This file contains the implementation of the base class for the numerical approximator to be used to solve the elements governed by ODEs. """ -import numpy as np -import numba as nb import inspect +import numba as nb +import numpy as np + -class NumericalApproximator(): +class NumericalApproximator: """ This is the abstract class for the creation of a NumericalApproximator. It defines how the approximator of the differential equation must be @@ -43,7 +44,7 @@ class NumericalApproximator(): (e.g. numba) """ - _error_message = '' + _error_message = "" """ String to use when displaying errors. It should contain general information about the class @@ -58,7 +59,7 @@ def __init__(self, root_finder): ---------- root_finder : superflexpy.utils.root_finder.RootFinder Solver used to find the root(s) of the differential equation(s). - """ + """ self._root_finder = root_finder @@ -109,8 +110,7 @@ def solve(self, fun, S0, **kwargs): elif isinstance(kwargs[k], float): scalars.append(k) else: - message = '{}the parameter {} is of type {}'.format(self._error_message, - k, type(kwargs[k])) + message = "{}the parameter {} is of type {}".format(self._error_message, k, type(kwargs[k])) raise TypeError(message) if len(vectors) == 0: @@ -118,36 +118,36 @@ def solve(self, fun, S0, **kwargs): else: num_ts = len(kwargs[vectors[0]]) - if 'dt' not in kwargs: - message = '{}\'dt\' must be in kwargs' + if "dt" not in kwargs: + message = "{}'dt' must be in kwargs" raise KeyError(message) # Transform dt in vector since we always need it - if 'dt' not in vectors: - kwargs['dt'] = np.array([kwargs['dt']] * num_ts) + if "dt" not in vectors: + kwargs["dt"] = np.array([kwargs["dt"]] * num_ts) # Construct the output array output = [] # Set architecture - if self.architecture == 'python': + if self.architecture == "python": self._solve = self._solve_python - elif self.architecture == 'numba': + elif self.architecture == "numba": self._solve = self._solve_numba for f, s_zero in zip(fun, S0): # Find which parameters the function needs - if self.architecture == 'python': + if self.architecture == "python": fun_pars = list(inspect.signature(f).parameters) - elif self.architecture == 'numba': + elif self.architecture == "numba": # fun_pars = list(inspect.signature(f).parameters) fun_pars = list(inspect.signature(f.py_func).parameters) args = [] for arg in fun_pars: - if arg in ['S', 'S0', 'ind']: + if arg in ["S", "S0", "ind"]: continue - elif arg == 'dt': + elif arg == "dt": args.append(kwargs[arg]) # We want to treat it differently elif arg in vectors: args.append(kwargs[arg]) @@ -158,58 +158,53 @@ def solve(self, fun, S0, **kwargs): root_settings = self._root_finder.get_settings() - output.append(self._solve(root_finder=self._root_finder.solve, # Passing just the method - diff_eq=self._differential_equation, - fun=f, - S0=s_zero, - dt=kwargs['dt'], - num_ts=num_ts, - args=args, - root_settings=root_settings)) + output.append( + self._solve( + root_finder=self._root_finder.solve, # Passing just the method + diff_eq=self._differential_equation, + fun=f, + S0=s_zero, + dt=kwargs["dt"], + num_ts=num_ts, + args=args, + root_settings=root_settings, + ) + ) return np.array(output).reshape((-1, len(fun))) def get_fluxes(self, fluxes, S, S0, **kwargs): - output = [] for i, (f, s_zero) in enumerate(zip(fluxes, S0)): - # The function is only python. No need of numba in get_fluxes fun_pars = list(inspect.signature(f).parameters) args = [] for arg in fun_pars: - if arg in ['S', 'S0', 'ind']: + if arg in ["S", "S0", "ind"]: continue else: args.append(kwargs[arg]) args = tuple(args) - output.append(self._get_fluxes(fluxes=f, - S=S[:, i], # S is a 2d np array - S0=s_zero, - args=args, - dt=kwargs['dt'])) + output.append( + self._get_fluxes(fluxes=f, S=S[:, i], S0=s_zero, args=args, dt=kwargs["dt"]) # S is a 2d np array + ) return output @staticmethod - def _solve_python(root_finder, diff_eq, fun, S0, dt, num_ts, args, root_settings): # here args are all vectors of the same lenght - + def _solve_python( + root_finder, diff_eq, fun, S0, dt, num_ts, args, root_settings + ): # here args are all vectors of the same lenght # Note: root_settings not used. Here only to have uniform interface output = np.zeros(num_ts) for i in range(num_ts): - # Call the root finder - root = root_finder(diff_eq=diff_eq, - fluxes=fun, - S0=S0, - dt=dt, - ind=i, - args=args) + root = root_finder(diff_eq=diff_eq, fluxes=fun, S0=S0, dt=dt, ind=i, args=args) output[i] = root S0 = output[i] @@ -218,22 +213,24 @@ def _solve_python(root_finder, diff_eq, fun, S0, dt, num_ts, args, root_settings @staticmethod @nb.jit(nopython=True) - def _solve_numba(root_finder, diff_eq, fun, S0, dt, num_ts, args, root_settings): # here args are all vectors of the same lenght - + def _solve_numba( + root_finder, diff_eq, fun, S0, dt, num_ts, args, root_settings + ): # here args are all vectors of the same lenght output = np.zeros(num_ts) for i in range(num_ts): - # Call the root finder - root = root_finder(diff_eq=diff_eq, - fluxes=fun, - S0=S0, - dt=dt, - ind=i, - args=args, - tol_F=root_settings[0], - tol_x=root_settings[1], - iter_max=root_settings[2]) + root = root_finder( + diff_eq=diff_eq, + fluxes=fun, + S0=S0, + dt=dt, + ind=i, + args=args, + tol_F=root_settings[0], + tol_x=root_settings[1], + iter_max=root_settings[2], + ) output[i] = root S0 = output[i] @@ -242,8 +239,8 @@ def _solve_numba(root_finder, diff_eq, fun, S0, dt, num_ts, args, root_settings) @staticmethod def _differential_equation(fluxes, S, S0, dt, args): - raise NotImplementedError('The method _differential_equation must be implemented') + raise NotImplementedError("The method _differential_equation must be implemented") @staticmethod def _get_fluxes(fluxes, S, S0, args, dt): - raise NotImplementedError('The method _get_fluxes must be implemented') + raise NotImplementedError("The method _get_fluxes must be implemented") diff --git a/superflexpy/utils/root_finder.py b/superflexpy/utils/root_finder.py index 5f6890d..b87afa0 100644 --- a/superflexpy/utils/root_finder.py +++ b/superflexpy/utils/root_finder.py @@ -27,7 +27,7 @@ """ -class RootFinder(): +class RootFinder: """ This is the abstract class for the creation of a RootFinder. It defines how the solver of the differential equation must be implemented. @@ -58,7 +58,7 @@ def __init__(self, tol_F=1e-8, tol_x=1e-8, iter_max=10): self._tol_F = tol_F self._tol_x = tol_x self._iter_max = iter_max - self._name = 'Solver' + self._name = "Solver" def get_settings(self): """ @@ -81,11 +81,11 @@ def get_settings(self): ) def __repr__(self): - str = 'Module: superflexPy\nClass: {}\n'.format(self._name) - str += 'Parameters:\n' - str += '\ttol_F = {}\n'.format(self._tol_F) - str += '\ttol_x = {}\n'.format(self._tol_x) - str += '\titer_max = {}'.format(self._iter_max) + str = "Module: superflexPy\nClass: {}\n".format(self._name) + str += "Parameters:\n" + str += "\ttol_F = {}\n".format(self._tol_F) + str += "\ttol_x = {}\n".format(self._tol_x) + str += "\titer_max = {}".format(self._iter_max) return str @@ -96,4 +96,4 @@ def solve(self, *args, **kwargs): over the whole time series. """ - raise NotImplementedError('The method solve must be implemented') + raise NotImplementedError("The method solve must be implemented") diff --git a/test/reference_results/01_FR/M01_main.dat b/test/reference_results/01_FR/M01_main.dat index 591f824..d19a64d 100644 --- a/test/reference_results/01_FR/M01_main.dat +++ b/test/reference_results/01_FR/M01_main.dat @@ -21,21 +21,21 @@ HRU CHARACTERISTICS FILE NAME UNITS FILE NAME "*/flexConfig_ML01.dat" !======== -UNITS PRESENCE MATRIX +UNITS PRESENCE MATRIX FC_01(Lumped) ! ID .true. ! !======== -INPUTS DESCRIPTION +INPUTS DESCRIPTION 2 ! precipitation input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! tracer input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) 2 ! etp input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! temperature input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) !======== -DRAINAGE DISTANCES +DRAINAGE DISTANCES riverScaleInc riverScaleOut riverSlopeInc riverSlopeOut 1.0 1.0 1.0 1.0 !======== -DEPENDENCIES +DEPENDENCIES DrainsInto 0 !======== diff --git a/test/reference_results/01_FR/SuperflexRes.dat b/test/reference_results/01_FR/SuperflexRes.dat index 9668dc6..bd72c30 100644 --- a/test/reference_results/01_FR/SuperflexRes.dat +++ b/test/reference_results/01_FR/SuperflexRes.dat @@ -1,5 +1,5 @@ Data modelled by SF_ISO - C1Wv%Qstream U1F1Wv%Sf[1] + C1Wv%Qstream U1F1Wv%Sf[1] 3.15978022652271E-05 9.99684021977342E-02 3.16670111393416E-01 3.98329829080432E+00 2.66357072238012E-01 3.71694121856631E+00 diff --git a/test/reference_results/01_FR/flexConfig_Areas.dat b/test/reference_results/01_FR/flexConfig_Areas.dat index e228cdd..4cc9caa 100644 --- a/test/reference_results/01_FR/flexConfig_Areas.dat +++ b/test/reference_results/01_FR/flexConfig_Areas.dat @@ -2,4 +2,4 @@ FLEX_HRU_CHARACTERISTICS_FILE_V1.1 ! Description: HRUs characterisics (-1=NA, depending on mask Unit Presence Matrix) !======== AREAS (km2) -1.0 \ No newline at end of file +1.0 diff --git a/test/reference_results/01_FR/flexConfig_ML01.dat b/test/reference_results/01_FR/flexConfig_ML01.dat index ea05a95..785cd27 100644 --- a/test/reference_results/01_FR/flexConfig_ML01.dat +++ b/test/reference_results/01_FR/flexConfig_ML01.dat @@ -8,8 +8,8 @@ status max number of reservoirs .false. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.true. 1 ! enableFR ! -.false. 1 ! enableSR ! +.true. 1 ! enableFR ! +.false. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/01_FR/input.dat b/test/reference_results/01_FR/input.dat index cb03f47..82a9da5 100644 --- a/test/reference_results/01_FR/input.dat +++ b/test/reference_results/01_FR/input.dat @@ -1,17 +1,17 @@ -Maimai data courtesy Jeff McDonnell -1.0 ! stepsize (d) -ST1: All data in mm/d -ST2 -ST3 -* ! list directed format ok here +Maimai data courtesy Jeff McDonnell +1.0 ! stepsize (d) +ST1: All data in mm/d +ST2 +ST3 +* ! list directed format ok here year, month, day, hour, min, QC, P (mm/d), E (mm/d), Q_obs (mm/d) - 1985 1 2 0 0 0 1.000000e-001 0.000000e+000 2.646000e-001 - 1985 1 3 0 0 0 4.200000e+000 0.000000e+000 4.426105e-001 - 1985 1 4 0 0 0 0.000000e+000 0.000000e+000 3.278842e-001 - 1985 1 5 0 0 0 1.320000e+001 0.000000e+000 5.415158e-001 - 1985 1 6 0 0 0 2.400000e+000 0.000000e+000 6.209053e-001 - 1985 1 7 0 0 0 1.430000e+001 0.000000e+000 1.255737e+000 - 1985 1 8 0 0 0 4.400000e+000 0.000000e+000 7.436842e-001 - 1985 1 9 0 0 0 1.700000e+000 0.000000e+000 4.998316e-001 - 1985 1 10 0 0 0 0.000000e+000 0.000000e+000 2.404421e-001 - 1985 1 11 0 0 0 7.360000e+001 0.000000e+000 2.727445e+001 \ No newline at end of file + 1985 1 2 0 0 0 1.000000e-001 0.000000e+000 2.646000e-001 + 1985 1 3 0 0 0 4.200000e+000 0.000000e+000 4.426105e-001 + 1985 1 4 0 0 0 0.000000e+000 0.000000e+000 3.278842e-001 + 1985 1 5 0 0 0 1.320000e+001 0.000000e+000 5.415158e-001 + 1985 1 6 0 0 0 2.400000e+000 0.000000e+000 6.209053e-001 + 1985 1 7 0 0 0 1.430000e+001 0.000000e+000 1.255737e+000 + 1985 1 8 0 0 0 4.400000e+000 0.000000e+000 7.436842e-001 + 1985 1 9 0 0 0 1.700000e+000 0.000000e+000 4.998316e-001 + 1985 1 10 0 0 0 0.000000e+000 0.000000e+000 2.404421e-001 + 1985 1 11 0 0 0 7.360000e+001 0.000000e+000 2.727445e+001 diff --git a/test/reference_results/01_FR/test.INF_DAT b/test/reference_results/01_FR/test.INF_DAT index 6bc1649..fe8d0f8 100644 --- a/test/reference_results/01_FR/test.INF_DAT +++ b/test/reference_results/01_FR/test.INF_DAT @@ -20,4 +20,4 @@ DATA_SPEX_FILE_V1.3 0,0 ! quality code columns for observed input data (relative to indxQC) 0 ! quality code columns for observed other data (relative to indxQC) -1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 +1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 diff --git a/test/reference_results/01_FR/test.py b/test/reference_results/01_FR/test.py index bad7543..44b52ff 100644 --- a/test/reference_results/01_FR/test.py +++ b/test/reference_results/01_FR/test.py @@ -3,12 +3,15 @@ """ import sys + # Add the path where the Superflex module is -sys.path.append('/home/dalmo/Documents/BitBucket/superflexPython/C_so/') -from superflex import Superflex_C -import numpy as np +sys.path.append("/home/dalmo/Documents/BitBucket/superflexPython/C_so/") from os import chdir -chdir('/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/01_FR/') + +import numpy as np +from superflex import Superflex_C + +chdir("/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/01_FR/") # Initialize the class sup = Superflex_C() @@ -22,4 +25,4 @@ # Get the output output = sup.run_model(parameters) -np.savetxt('Results.csv', X = output, delimiter = ',', header = 'Q_FR, S_FR') \ No newline at end of file +np.savetxt("Results.csv", X=output, delimiter=",", header="Q_FR, S_FR") diff --git a/test/reference_results/02_UR/M01_main.dat b/test/reference_results/02_UR/M01_main.dat index 591f824..d19a64d 100644 --- a/test/reference_results/02_UR/M01_main.dat +++ b/test/reference_results/02_UR/M01_main.dat @@ -21,21 +21,21 @@ HRU CHARACTERISTICS FILE NAME UNITS FILE NAME "*/flexConfig_ML01.dat" !======== -UNITS PRESENCE MATRIX +UNITS PRESENCE MATRIX FC_01(Lumped) ! ID .true. ! !======== -INPUTS DESCRIPTION +INPUTS DESCRIPTION 2 ! precipitation input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! tracer input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) 2 ! etp input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! temperature input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) !======== -DRAINAGE DISTANCES +DRAINAGE DISTANCES riverScaleInc riverScaleOut riverSlopeInc riverSlopeOut 1.0 1.0 1.0 1.0 !======== -DEPENDENCIES +DEPENDENCIES DrainsInto 0 !======== diff --git a/test/reference_results/02_UR/SuperflexRes.dat b/test/reference_results/02_UR/SuperflexRes.dat index 7a152b8..ece206e 100644 --- a/test/reference_results/02_UR/SuperflexRes.dat +++ b/test/reference_results/02_UR/SuperflexRes.dat @@ -1,5 +1,5 @@ Data modelled by SF_ISO - C1Wv%Qstream U1F1Wv%Eact U1F1Wv%Su[1] + C1Wv%Qstream U1F1Wv%Eact U1F1Wv%Su[1] 3.00985686446989E-03 5.25897489318654E+00 4.83801524994899E+00 8.86469281119489E-02 5.13069121339945E+00 3.81867710843760E+00 0.00000000000000E+00 3.20271484273091E+00 6.15962265706688E-01 diff --git a/test/reference_results/02_UR/flexConfig_Areas.dat b/test/reference_results/02_UR/flexConfig_Areas.dat index e228cdd..4cc9caa 100644 --- a/test/reference_results/02_UR/flexConfig_Areas.dat +++ b/test/reference_results/02_UR/flexConfig_Areas.dat @@ -2,4 +2,4 @@ FLEX_HRU_CHARACTERISTICS_FILE_V1.1 ! Description: HRUs characterisics (-1=NA, depending on mask Unit Presence Matrix) !======== AREAS (km2) -1.0 \ No newline at end of file +1.0 diff --git a/test/reference_results/02_UR/flexConfig_ML01.dat b/test/reference_results/02_UR/flexConfig_ML01.dat index cfad825..c694555 100644 --- a/test/reference_results/02_UR/flexConfig_ML01.dat +++ b/test/reference_results/02_UR/flexConfig_ML01.dat @@ -8,8 +8,8 @@ status max number of reservoirs .true. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.false. 1 ! enableFR ! -.false. 1 ! enableSR ! +.false. 1 ! enableFR ! +.false. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/02_UR/input.dat b/test/reference_results/02_UR/input.dat index db886a3..c855050 100644 --- a/test/reference_results/02_UR/input.dat +++ b/test/reference_results/02_UR/input.dat @@ -1,17 +1,17 @@ -Maimai data courtesy Jeff McDonnell -1.0 ! stepsize (d) -ST1: All data in mm/d -ST2 -ST3 -* ! list directed format ok here +Maimai data courtesy Jeff McDonnell +1.0 ! stepsize (d) +ST1: All data in mm/d +ST2 +ST3 +* ! list directed format ok here year, month, day, hour, min, QC, P (mm/d), E (mm/d), Q_obs (mm/d) - 1985 1 2 0 0 0 1.000000e-001 3.830020e+000 2.646000e-001 - 1985 1 3 0 0 0 4.200000e+000 3.830020e+000 4.426105e-001 - 1985 1 4 0 0 0 0.000000e+000 3.830020e+000 3.278842e-001 - 1985 1 5 0 0 0 1.320000e+001 3.830020e+000 5.415158e-001 - 1985 1 6 0 0 0 2.400000e+000 3.830020e+000 6.209053e-001 - 1985 1 7 0 0 0 1.430000e+001 3.830020e+000 1.255737e+000 - 1985 1 8 0 0 0 4.400000e+000 3.830020e+000 7.436842e-001 - 1985 1 9 0 0 0 1.700000e+000 3.830020e+000 4.998316e-001 - 1985 1 10 0 0 0 0.000000e+000 3.830020e+000 2.404421e-001 - 1985 1 11 0 0 0 7.360000e+001 3.830020e+000 2.727445e+001 + 1985 1 2 0 0 0 1.000000e-001 3.830020e+000 2.646000e-001 + 1985 1 3 0 0 0 4.200000e+000 3.830020e+000 4.426105e-001 + 1985 1 4 0 0 0 0.000000e+000 3.830020e+000 3.278842e-001 + 1985 1 5 0 0 0 1.320000e+001 3.830020e+000 5.415158e-001 + 1985 1 6 0 0 0 2.400000e+000 3.830020e+000 6.209053e-001 + 1985 1 7 0 0 0 1.430000e+001 3.830020e+000 1.255737e+000 + 1985 1 8 0 0 0 4.400000e+000 3.830020e+000 7.436842e-001 + 1985 1 9 0 0 0 1.700000e+000 3.830020e+000 4.998316e-001 + 1985 1 10 0 0 0 0.000000e+000 3.830020e+000 2.404421e-001 + 1985 1 11 0 0 0 7.360000e+001 3.830020e+000 2.727445e+001 diff --git a/test/reference_results/02_UR/test.INF_DAT b/test/reference_results/02_UR/test.INF_DAT index 4e24150..5b9fc02 100644 --- a/test/reference_results/02_UR/test.INF_DAT +++ b/test/reference_results/02_UR/test.INF_DAT @@ -20,4 +20,4 @@ DATA_SPEX_FILE_V1.3 0,0 ! quality code columns for observed input data (relative to indxQC) 0 ! quality code columns for observed other data (relative to indxQC) -1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 +1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 diff --git a/test/reference_results/02_UR/test.py b/test/reference_results/02_UR/test.py index 61f06f2..8e87bfe 100644 --- a/test/reference_results/02_UR/test.py +++ b/test/reference_results/02_UR/test.py @@ -3,12 +3,15 @@ """ import sys + # Add the path where the Superflex module is -sys.path.append('/home/dalmo/Documents/BitBucket/superflexPython/C_so/') -from superflex import Superflex_C -import numpy as np +sys.path.append("/home/dalmo/Documents/BitBucket/superflexPython/C_so/") from os import chdir -chdir('/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/02_UR/') + +import numpy as np +from superflex import Superflex_C + +chdir("/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/02_UR/") # Initialize the class sup = Superflex_C() @@ -22,4 +25,4 @@ # Get the output output = sup.run_model(parameters) -np.savetxt('Results.csv', X = output, delimiter = ',', header = 'Q_UR, E_UR, S_UR') \ No newline at end of file +np.savetxt("Results.csv", X=output, delimiter=",", header="Q_UR, E_UR, S_UR") diff --git a/test/reference_results/03_UR_FR/M01_main.dat b/test/reference_results/03_UR_FR/M01_main.dat index 591f824..d19a64d 100644 --- a/test/reference_results/03_UR_FR/M01_main.dat +++ b/test/reference_results/03_UR_FR/M01_main.dat @@ -21,21 +21,21 @@ HRU CHARACTERISTICS FILE NAME UNITS FILE NAME "*/flexConfig_ML01.dat" !======== -UNITS PRESENCE MATRIX +UNITS PRESENCE MATRIX FC_01(Lumped) ! ID .true. ! !======== -INPUTS DESCRIPTION +INPUTS DESCRIPTION 2 ! precipitation input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! tracer input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) 2 ! etp input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! temperature input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) !======== -DRAINAGE DISTANCES +DRAINAGE DISTANCES riverScaleInc riverScaleOut riverSlopeInc riverSlopeOut 1.0 1.0 1.0 1.0 !======== -DEPENDENCIES +DEPENDENCIES DrainsInto 0 !======== diff --git a/test/reference_results/03_UR_FR/SuperflexRes.dat b/test/reference_results/03_UR_FR/SuperflexRes.dat index 2bc0f17..ea0ac80 100644 --- a/test/reference_results/03_UR_FR/SuperflexRes.dat +++ b/test/reference_results/03_UR_FR/SuperflexRes.dat @@ -1,5 +1,5 @@ Data modelled by SF_ISO - C1Wv%Qstream U1F1Wv%Eact U1F1Wv%Su[1] U1F1Wv%Sf[1] + C1Wv%Qstream U1F1Wv%Eact U1F1Wv%Su[1] U1F1Wv%Sf[1] 4.97007353652108E-09 5.25897489318654E+00 4.83801524994899E+00 3.00985189439636E-03 2.54161857842611E-05 5.13069121339945E+00 3.81867710843760E+00 9.16313638205607E-02 2.53985771637753E-05 3.20271484273091E+00 6.15962265706688E-01 9.16059652433944E-02 diff --git a/test/reference_results/03_UR_FR/flexConfig_Areas.dat b/test/reference_results/03_UR_FR/flexConfig_Areas.dat index e228cdd..4cc9caa 100644 --- a/test/reference_results/03_UR_FR/flexConfig_Areas.dat +++ b/test/reference_results/03_UR_FR/flexConfig_Areas.dat @@ -2,4 +2,4 @@ FLEX_HRU_CHARACTERISTICS_FILE_V1.1 ! Description: HRUs characterisics (-1=NA, depending on mask Unit Presence Matrix) !======== AREAS (km2) -1.0 \ No newline at end of file +1.0 diff --git a/test/reference_results/03_UR_FR/flexConfig_ML01.dat b/test/reference_results/03_UR_FR/flexConfig_ML01.dat index 28c2a5c..b94bd14 100644 --- a/test/reference_results/03_UR_FR/flexConfig_ML01.dat +++ b/test/reference_results/03_UR_FR/flexConfig_ML01.dat @@ -8,8 +8,8 @@ status max number of reservoirs .true. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.true. 1 ! enableFR ! -.false. 1 ! enableSR ! +.true. 1 ! enableFR ! +.false. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/03_UR_FR/input.dat b/test/reference_results/03_UR_FR/input.dat index db886a3..c855050 100644 --- a/test/reference_results/03_UR_FR/input.dat +++ b/test/reference_results/03_UR_FR/input.dat @@ -1,17 +1,17 @@ -Maimai data courtesy Jeff McDonnell -1.0 ! stepsize (d) -ST1: All data in mm/d -ST2 -ST3 -* ! list directed format ok here +Maimai data courtesy Jeff McDonnell +1.0 ! stepsize (d) +ST1: All data in mm/d +ST2 +ST3 +* ! list directed format ok here year, month, day, hour, min, QC, P (mm/d), E (mm/d), Q_obs (mm/d) - 1985 1 2 0 0 0 1.000000e-001 3.830020e+000 2.646000e-001 - 1985 1 3 0 0 0 4.200000e+000 3.830020e+000 4.426105e-001 - 1985 1 4 0 0 0 0.000000e+000 3.830020e+000 3.278842e-001 - 1985 1 5 0 0 0 1.320000e+001 3.830020e+000 5.415158e-001 - 1985 1 6 0 0 0 2.400000e+000 3.830020e+000 6.209053e-001 - 1985 1 7 0 0 0 1.430000e+001 3.830020e+000 1.255737e+000 - 1985 1 8 0 0 0 4.400000e+000 3.830020e+000 7.436842e-001 - 1985 1 9 0 0 0 1.700000e+000 3.830020e+000 4.998316e-001 - 1985 1 10 0 0 0 0.000000e+000 3.830020e+000 2.404421e-001 - 1985 1 11 0 0 0 7.360000e+001 3.830020e+000 2.727445e+001 + 1985 1 2 0 0 0 1.000000e-001 3.830020e+000 2.646000e-001 + 1985 1 3 0 0 0 4.200000e+000 3.830020e+000 4.426105e-001 + 1985 1 4 0 0 0 0.000000e+000 3.830020e+000 3.278842e-001 + 1985 1 5 0 0 0 1.320000e+001 3.830020e+000 5.415158e-001 + 1985 1 6 0 0 0 2.400000e+000 3.830020e+000 6.209053e-001 + 1985 1 7 0 0 0 1.430000e+001 3.830020e+000 1.255737e+000 + 1985 1 8 0 0 0 4.400000e+000 3.830020e+000 7.436842e-001 + 1985 1 9 0 0 0 1.700000e+000 3.830020e+000 4.998316e-001 + 1985 1 10 0 0 0 0.000000e+000 3.830020e+000 2.404421e-001 + 1985 1 11 0 0 0 7.360000e+001 3.830020e+000 2.727445e+001 diff --git a/test/reference_results/03_UR_FR/test.INF_DAT b/test/reference_results/03_UR_FR/test.INF_DAT index b8ce817..1e53009 100644 --- a/test/reference_results/03_UR_FR/test.INF_DAT +++ b/test/reference_results/03_UR_FR/test.INF_DAT @@ -20,4 +20,4 @@ DATA_SPEX_FILE_V1.3 0,0 ! quality code columns for observed input data (relative to indxQC) 0 ! quality code columns for observed other data (relative to indxQC) -1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 +1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 diff --git a/test/reference_results/03_UR_FR/test.py b/test/reference_results/03_UR_FR/test.py index 33a8812..9f70f1a 100644 --- a/test/reference_results/03_UR_FR/test.py +++ b/test/reference_results/03_UR_FR/test.py @@ -3,12 +3,15 @@ """ import sys + # Add the path where the Superflex module is -sys.path.append('/home/dalmo/Documents/BitBucket/superflexPython/C_so/') -from superflex import Superflex_C -import numpy as np +sys.path.append("/home/dalmo/Documents/BitBucket/superflexPython/C_so/") from os import chdir -chdir('/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/03_UR_FR/') + +import numpy as np +from superflex import Superflex_C + +chdir("/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/03_UR_FR/") # Initialize the class sup = Superflex_C() @@ -22,4 +25,4 @@ # Get the output output = sup.run_model(parameters) -np.savetxt('Results.csv', X = output, delimiter = ',', header = 'Q_out, E_UR, S_UR, S_FR') \ No newline at end of file +np.savetxt("Results.csv", X=output, delimiter=",", header="Q_out, E_UR, S_UR, S_FR") diff --git a/test/reference_results/04_UR_FR_SR/M01_main.dat b/test/reference_results/04_UR_FR_SR/M01_main.dat index 591f824..d19a64d 100644 --- a/test/reference_results/04_UR_FR_SR/M01_main.dat +++ b/test/reference_results/04_UR_FR_SR/M01_main.dat @@ -21,21 +21,21 @@ HRU CHARACTERISTICS FILE NAME UNITS FILE NAME "*/flexConfig_ML01.dat" !======== -UNITS PRESENCE MATRIX +UNITS PRESENCE MATRIX FC_01(Lumped) ! ID .true. ! !======== -INPUTS DESCRIPTION +INPUTS DESCRIPTION 2 ! precipitation input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! tracer input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) 2 ! etp input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! temperature input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) !======== -DRAINAGE DISTANCES +DRAINAGE DISTANCES riverScaleInc riverScaleOut riverSlopeInc riverSlopeOut 1.0 1.0 1.0 1.0 !======== -DEPENDENCIES +DEPENDENCIES DrainsInto 0 !======== diff --git a/test/reference_results/04_UR_FR_SR/SuperflexRes.dat b/test/reference_results/04_UR_FR_SR/SuperflexRes.dat index df706b0..538bc67 100644 --- a/test/reference_results/04_UR_FR_SR/SuperflexRes.dat +++ b/test/reference_results/04_UR_FR_SR/SuperflexRes.dat @@ -1,5 +1,5 @@ Data modelled by SF_ISO - C1Wv%Qstream U1F1Wv%Eact U1F1Wv%Su[1] U1F1Wv%Sf[1] U1F1Wv%Ss[1] + C1Wv%Qstream U1F1Wv%Eact U1F1Wv%Su[1] U1F1Wv%Sf[1] U1F1Wv%Ss[1] 2.10913914391699E-07 5.25897489318654E+00 4.83801524994899E+00 9.02956814340099E-04 2.10668913621530E-03 7.66893022236116E-06 5.13069121339945E+00 3.81867710843760E+00 2.74957816300526E-02 6.41531235022293E-02 7.66814588563560E-06 3.20271484273091E+00 6.15962265706688E-01 2.74945281550501E-02 6.41467088313462E-02 diff --git a/test/reference_results/04_UR_FR_SR/flexConfig_Areas.dat b/test/reference_results/04_UR_FR_SR/flexConfig_Areas.dat index e228cdd..4cc9caa 100644 --- a/test/reference_results/04_UR_FR_SR/flexConfig_Areas.dat +++ b/test/reference_results/04_UR_FR_SR/flexConfig_Areas.dat @@ -2,4 +2,4 @@ FLEX_HRU_CHARACTERISTICS_FILE_V1.1 ! Description: HRUs characterisics (-1=NA, depending on mask Unit Presence Matrix) !======== AREAS (km2) -1.0 \ No newline at end of file +1.0 diff --git a/test/reference_results/04_UR_FR_SR/flexConfig_ML01.dat b/test/reference_results/04_UR_FR_SR/flexConfig_ML01.dat index d164923..561ad34 100644 --- a/test/reference_results/04_UR_FR_SR/flexConfig_ML01.dat +++ b/test/reference_results/04_UR_FR_SR/flexConfig_ML01.dat @@ -8,8 +8,8 @@ status max number of reservoirs .true. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.true. 1 ! enableFR ! -.true. 1 ! enableSR ! +.true. 1 ! enableFR ! +.true. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/04_UR_FR_SR/input.dat b/test/reference_results/04_UR_FR_SR/input.dat index db886a3..c855050 100644 --- a/test/reference_results/04_UR_FR_SR/input.dat +++ b/test/reference_results/04_UR_FR_SR/input.dat @@ -1,17 +1,17 @@ -Maimai data courtesy Jeff McDonnell -1.0 ! stepsize (d) -ST1: All data in mm/d -ST2 -ST3 -* ! list directed format ok here +Maimai data courtesy Jeff McDonnell +1.0 ! stepsize (d) +ST1: All data in mm/d +ST2 +ST3 +* ! list directed format ok here year, month, day, hour, min, QC, P (mm/d), E (mm/d), Q_obs (mm/d) - 1985 1 2 0 0 0 1.000000e-001 3.830020e+000 2.646000e-001 - 1985 1 3 0 0 0 4.200000e+000 3.830020e+000 4.426105e-001 - 1985 1 4 0 0 0 0.000000e+000 3.830020e+000 3.278842e-001 - 1985 1 5 0 0 0 1.320000e+001 3.830020e+000 5.415158e-001 - 1985 1 6 0 0 0 2.400000e+000 3.830020e+000 6.209053e-001 - 1985 1 7 0 0 0 1.430000e+001 3.830020e+000 1.255737e+000 - 1985 1 8 0 0 0 4.400000e+000 3.830020e+000 7.436842e-001 - 1985 1 9 0 0 0 1.700000e+000 3.830020e+000 4.998316e-001 - 1985 1 10 0 0 0 0.000000e+000 3.830020e+000 2.404421e-001 - 1985 1 11 0 0 0 7.360000e+001 3.830020e+000 2.727445e+001 + 1985 1 2 0 0 0 1.000000e-001 3.830020e+000 2.646000e-001 + 1985 1 3 0 0 0 4.200000e+000 3.830020e+000 4.426105e-001 + 1985 1 4 0 0 0 0.000000e+000 3.830020e+000 3.278842e-001 + 1985 1 5 0 0 0 1.320000e+001 3.830020e+000 5.415158e-001 + 1985 1 6 0 0 0 2.400000e+000 3.830020e+000 6.209053e-001 + 1985 1 7 0 0 0 1.430000e+001 3.830020e+000 1.255737e+000 + 1985 1 8 0 0 0 4.400000e+000 3.830020e+000 7.436842e-001 + 1985 1 9 0 0 0 1.700000e+000 3.830020e+000 4.998316e-001 + 1985 1 10 0 0 0 0.000000e+000 3.830020e+000 2.404421e-001 + 1985 1 11 0 0 0 7.360000e+001 3.830020e+000 2.727445e+001 diff --git a/test/reference_results/04_UR_FR_SR/test.INF_DAT b/test/reference_results/04_UR_FR_SR/test.INF_DAT index 5a630e9..519d140 100644 --- a/test/reference_results/04_UR_FR_SR/test.INF_DAT +++ b/test/reference_results/04_UR_FR_SR/test.INF_DAT @@ -20,4 +20,4 @@ DATA_SPEX_FILE_V1.3 0,0 ! quality code columns for observed input data (relative to indxQC) 0 ! quality code columns for observed other data (relative to indxQC) -1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 +1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 diff --git a/test/reference_results/04_UR_FR_SR/test.py b/test/reference_results/04_UR_FR_SR/test.py index 9232f2d..27bd305 100644 --- a/test/reference_results/04_UR_FR_SR/test.py +++ b/test/reference_results/04_UR_FR_SR/test.py @@ -3,12 +3,15 @@ """ import sys + # Add the path where the Superflex module is -sys.path.append('/home/dalmo/Documents/BitBucket/superflexPython/C_so/') -from superflex import Superflex_C -import numpy as np +sys.path.append("/home/dalmo/Documents/BitBucket/superflexPython/C_so/") from os import chdir -chdir('/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/04_UR_FR_SR/') + +import numpy as np +from superflex import Superflex_C + +chdir("/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/04_UR_FR_SR/") # Initialize the class sup = Superflex_C() @@ -22,4 +25,4 @@ # Get the output output = sup.run_model(parameters) -np.savetxt('Results.csv', X = output, delimiter = ',', header = 'Q_out, E_UR, S_UR, S_FR, S_SR') \ No newline at end of file +np.savetxt("Results.csv", X=output, delimiter=",", header="Q_out, E_UR, S_UR, S_FR, S_SR") diff --git a/test/reference_results/05_2HRUs/M01_main.dat b/test/reference_results/05_2HRUs/M01_main.dat index 3d00be3..a893bf2 100644 --- a/test/reference_results/05_2HRUs/M01_main.dat +++ b/test/reference_results/05_2HRUs/M01_main.dat @@ -22,21 +22,21 @@ UNITS FILE NAME "*/flexConfig_HRU1.dat" "*/flexConfig_HRU2.dat" !======== -UNITS PRESENCE MATRIX +UNITS PRESENCE MATRIX FC_01(Cons) FC_01(NotCons) ! ID -.true. .true. ! +.true. .true. ! !======== -INPUTS DESCRIPTION +INPUTS DESCRIPTION 2 ! precipitation input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! tracer input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) 2 ! etp input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! temperature input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) !======== -DRAINAGE DISTANCES +DRAINAGE DISTANCES riverScaleInc riverScaleOut riverSlopeInc riverSlopeOut 1.0 1.0 1.0 1.0 !======== -DEPENDENCIES +DEPENDENCIES DrainsInto 0 !======== diff --git a/test/reference_results/05_2HRUs/SuperflexRes.dat b/test/reference_results/05_2HRUs/SuperflexRes.dat index 5a18a65..e9857e0 100644 --- a/test/reference_results/05_2HRUs/SuperflexRes.dat +++ b/test/reference_results/05_2HRUs/SuperflexRes.dat @@ -1,5 +1,5 @@ Data modelled by SF_ISO - C1Wv%Qstream U1F1Wv%Qstrm U1F1Wv%Eact U1F1Wv%Sf[1] U2F1Wv%Qstrm U2F1Wv%Eact U2F1Wv%Su[1] U2F1Wv%Sf[1] U2F1Wv%Ss[1] + C1Wv%Qstream U1F1Wv%Qstrm U1F1Wv%Eact U1F1Wv%Sf[1] U2F1Wv%Qstrm U2F1Wv%Eact U2F1Wv%Su[1] U2F1Wv%Sf[1] U2F1Wv%Ss[1] 1.27656692547259E-05 3.15978022652271E-05 0.00000000000000E+00 9.99684021977342E-02 2.10913914391699E-07 5.25897489318654E+00 4.83801524994899E+00 9.02956814340099E-04 2.10668913621530E-03 1.26672645915500E-01 3.16670111393416E-01 0.00000000000000E+00 3.98329829080432E+00 7.66893022236116E-06 5.13069121339945E+00 3.81867710843760E+00 2.74957816300526E-02 6.41531235022293E-02 1.06547429782736E-01 2.66357072238012E-01 0.00000000000000E+00 3.71694121856631E+00 7.66814588563560E-06 3.20271484273091E+00 6.15962265706688E-01 2.74945281550501E-02 6.41467088313462E-02 diff --git a/test/reference_results/05_2HRUs/flexConfig_Areas.dat b/test/reference_results/05_2HRUs/flexConfig_Areas.dat index d2fd5fd..6333e29 100644 --- a/test/reference_results/05_2HRUs/flexConfig_Areas.dat +++ b/test/reference_results/05_2HRUs/flexConfig_Areas.dat @@ -2,4 +2,4 @@ FLEX_HRU_CHARACTERISTICS_FILE_V1.1 ! Description: HRUs characterisics (-1=NA, depending on mask Unit Presence Matrix) !======== AREAS (km2) -40.0 60.0 \ No newline at end of file +40.0 60.0 diff --git a/test/reference_results/05_2HRUs/flexConfig_HRU1.dat b/test/reference_results/05_2HRUs/flexConfig_HRU1.dat index ca1f5dd..2eef17a 100644 --- a/test/reference_results/05_2HRUs/flexConfig_HRU1.dat +++ b/test/reference_results/05_2HRUs/flexConfig_HRU1.dat @@ -8,8 +8,8 @@ status max number of reservoirs .false. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.true. 1 ! enableFR ! -.false. 1 ! enableSR ! +.true. 1 ! enableFR ! +.false. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/05_2HRUs/flexConfig_HRU2.dat b/test/reference_results/05_2HRUs/flexConfig_HRU2.dat index d164923..561ad34 100644 --- a/test/reference_results/05_2HRUs/flexConfig_HRU2.dat +++ b/test/reference_results/05_2HRUs/flexConfig_HRU2.dat @@ -8,8 +8,8 @@ status max number of reservoirs .true. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.true. 1 ! enableFR ! -.true. 1 ! enableSR ! +.true. 1 ! enableFR ! +.true. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/05_2HRUs/input.dat b/test/reference_results/05_2HRUs/input.dat index db886a3..c855050 100644 --- a/test/reference_results/05_2HRUs/input.dat +++ b/test/reference_results/05_2HRUs/input.dat @@ -1,17 +1,17 @@ -Maimai data courtesy Jeff McDonnell -1.0 ! stepsize (d) -ST1: All data in mm/d -ST2 -ST3 -* ! list directed format ok here +Maimai data courtesy Jeff McDonnell +1.0 ! stepsize (d) +ST1: All data in mm/d +ST2 +ST3 +* ! list directed format ok here year, month, day, hour, min, QC, P (mm/d), E (mm/d), Q_obs (mm/d) - 1985 1 2 0 0 0 1.000000e-001 3.830020e+000 2.646000e-001 - 1985 1 3 0 0 0 4.200000e+000 3.830020e+000 4.426105e-001 - 1985 1 4 0 0 0 0.000000e+000 3.830020e+000 3.278842e-001 - 1985 1 5 0 0 0 1.320000e+001 3.830020e+000 5.415158e-001 - 1985 1 6 0 0 0 2.400000e+000 3.830020e+000 6.209053e-001 - 1985 1 7 0 0 0 1.430000e+001 3.830020e+000 1.255737e+000 - 1985 1 8 0 0 0 4.400000e+000 3.830020e+000 7.436842e-001 - 1985 1 9 0 0 0 1.700000e+000 3.830020e+000 4.998316e-001 - 1985 1 10 0 0 0 0.000000e+000 3.830020e+000 2.404421e-001 - 1985 1 11 0 0 0 7.360000e+001 3.830020e+000 2.727445e+001 + 1985 1 2 0 0 0 1.000000e-001 3.830020e+000 2.646000e-001 + 1985 1 3 0 0 0 4.200000e+000 3.830020e+000 4.426105e-001 + 1985 1 4 0 0 0 0.000000e+000 3.830020e+000 3.278842e-001 + 1985 1 5 0 0 0 1.320000e+001 3.830020e+000 5.415158e-001 + 1985 1 6 0 0 0 2.400000e+000 3.830020e+000 6.209053e-001 + 1985 1 7 0 0 0 1.430000e+001 3.830020e+000 1.255737e+000 + 1985 1 8 0 0 0 4.400000e+000 3.830020e+000 7.436842e-001 + 1985 1 9 0 0 0 1.700000e+000 3.830020e+000 4.998316e-001 + 1985 1 10 0 0 0 0.000000e+000 3.830020e+000 2.404421e-001 + 1985 1 11 0 0 0 7.360000e+001 3.830020e+000 2.727445e+001 diff --git a/test/reference_results/05_2HRUs/test.INF_DAT b/test/reference_results/05_2HRUs/test.INF_DAT index c5780be..d8ce96e 100644 --- a/test/reference_results/05_2HRUs/test.INF_DAT +++ b/test/reference_results/05_2HRUs/test.INF_DAT @@ -20,4 +20,4 @@ DATA_SPEX_FILE_V1.3 0,0 ! quality code columns for observed input data (relative to indxQC) 0 ! quality code columns for observed other data (relative to indxQC) -1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 +1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 diff --git a/test/reference_results/05_2HRUs/test.py b/test/reference_results/05_2HRUs/test.py index d74a8fa..a343575 100644 --- a/test/reference_results/05_2HRUs/test.py +++ b/test/reference_results/05_2HRUs/test.py @@ -3,12 +3,15 @@ """ import sys + # Add the path where the Superflex module is -sys.path.append('/home/dalmo/Documents/BitBucket/superflexPython/C_so/') -from superflex import Superflex_C -import numpy as np +sys.path.append("/home/dalmo/Documents/BitBucket/superflexPython/C_so/") from os import chdir -chdir('/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/05_2HRUs/') + +import numpy as np +from superflex import Superflex_C + +chdir("/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/05_2HRUs/") # Initialize the class sup = Superflex_C() @@ -22,4 +25,9 @@ # Get the output output = sup.run_model(parameters) -np.savetxt('Results.csv', X = output, delimiter = ',', header = 'Q_tot, Q_H1, E_FR_H1, S_FR_H1, Q_H2, E_UR_H2, S_UR_H2, S_FR_H2, S_SR_H2') \ No newline at end of file +np.savetxt( + "Results.csv", + X=output, + delimiter=",", + header="Q_tot, Q_H1, E_FR_H1, S_FR_H1, Q_H2, E_UR_H2, S_UR_H2, S_FR_H2, S_SR_H2", +) diff --git a/test/reference_results/06_3Cats_2HRUs/M01_main.dat b/test/reference_results/06_3Cats_2HRUs/M01_main.dat index 217d3a6..bec38ca 100644 --- a/test/reference_results/06_3Cats_2HRUs/M01_main.dat +++ b/test/reference_results/06_3Cats_2HRUs/M01_main.dat @@ -22,25 +22,25 @@ UNITS FILE NAME "*/flexConfig_HRU1.dat" "*/flexConfig_HRU2.dat" !======== -UNITS PRESENCE MATRIX +UNITS PRESENCE MATRIX FC_01(Cons) FC_01(NotCons) ! ID -.true. .true. ! -.true. .true. ! -.true. .true. ! +.true. .true. ! +.true. .true. ! +.true. .true. ! !======== -INPUTS DESCRIPTION +INPUTS DESCRIPTION 2 ! precipitation input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! tracer input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) 2 ! etp input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! temperature input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) !======== -DRAINAGE DISTANCES +DRAINAGE DISTANCES riverScaleInc riverScaleOut riverSlopeInc riverSlopeOut 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 !======== -DEPENDENCIES +DEPENDENCIES DrainsInto 3 3 diff --git a/test/reference_results/06_3Cats_2HRUs/SuperflexRes.dat b/test/reference_results/06_3Cats_2HRUs/SuperflexRes.dat index d1d6824..6a4df43 100644 --- a/test/reference_results/06_3Cats_2HRUs/SuperflexRes.dat +++ b/test/reference_results/06_3Cats_2HRUs/SuperflexRes.dat @@ -1,5 +1,5 @@ Data modelled by SF_ISO - C1Wv%Qstream C2Wv%Qstream C3Wv%Qstream U1F1Wv%Qstrm U1F1Wv%Sf[1] U1F2Wv%Qstrm U1F2Wv%Sf[1] U1F3Wv%Qstrm U1F3Wv%Sf[1] U2F1Wv%Qstrm U2F1Wv%Eact U2F1Wv%Su[1] U2F1Wv%Sf[1] U2F1Wv%Ss[1] U2F2Wv%Qstrm U2F2Wv%Eact U2F2Wv%Su[1] U2F2Wv%Sf[1] U2F2Wv%Ss[1] U2F3Wv%Qstrm U2F3Wv%Eact U2F3Wv%Su[1] U2F3Wv%Sf[1] U2F3Wv%Ss[1] + C1Wv%Qstream C2Wv%Qstream C3Wv%Qstream U1F1Wv%Qstrm U1F1Wv%Sf[1] U1F2Wv%Qstrm U1F2Wv%Sf[1] U1F3Wv%Qstrm U1F3Wv%Sf[1] U2F1Wv%Qstrm U2F1Wv%Eact U2F1Wv%Su[1] U2F1Wv%Sf[1] U2F1Wv%Ss[1] U2F2Wv%Qstrm U2F2Wv%Eact U2F2Wv%Su[1] U2F2Wv%Sf[1] U2F2Wv%Ss[1] U2F3Wv%Qstrm U2F3Wv%Eact U2F3Wv%Su[1] U2F3Wv%Sf[1] U2F3Wv%Ss[1] 2.25662647582890E-02 3.81688525605332E-02 1.12771263929956E-01 9.01517646935360E-02 2.40984823530646E+00 9.53635015324968E-02 2.46463649846750E+00 2.40691244882188E-01 3.56930875511780E+00 3.77647798733538E-05 8.66333600844912E-01 1.13628253428007E+01 8.12335091046881E-02 1.89569782469820E-01 3.90865792241600E-05 9.51841839101148E-01 1.13319466701597E+01 8.28436935135755E-02 1.93328710646317E-01 1.07709971884997E-04 9.39300425376884E-01 1.24001448824668E+01 1.41091633209906E-01 3.29355348974534E-01 5.11725005677066E-01 4.02122635969709E-01 8.24687478706056E-01 2.03928459750566E+00 8.39056363780081E+00 1.00427855065366E+00 6.32035794781384E+00 1.51303158564830E+00 7.44627716946950E+00 2.53847506753550E-03 5.28548466297921E-01 1.72316794839464E+01 5.65606777347228E-01 1.32525543171609E+00 6.85359513739340E-04 1.83341815146117E-01 1.51944470115249E+01 3.26482003334930E-01 7.63162884799935E-01 1.46464843989525E-03 1.17663969829459E-01 1.66378628227952E+01 4.50117759989010E-01 1.05348266359767E+00 1.51576870949395E+00 1.54963470133704E+00 2.74357035236274E+00 5.97348877487384E+00 1.28970748629270E+01 3.86009269374533E+00 1.08302652540685E+01 4.93131053854159E+00 1.19449666309279E+01 2.98620210339889E-02 9.81929730983260E-01 2.33789798168905E+01 1.54134277986947E+00 3.67042734423255E+00 9.32937306484988E-03 1.19310057311780E+00 2.02190094707055E+01 9.63080677761066E-01 2.26957180501056E+00 1.87311825142872E-02 9.02443978057787E-01 2.23476548299681E+01 1.27701834339676E+00 3.02561491244494E+00 diff --git a/test/reference_results/06_3Cats_2HRUs/flexConfig_Areas.dat b/test/reference_results/06_3Cats_2HRUs/flexConfig_Areas.dat index 8065b78..74778fe 100644 --- a/test/reference_results/06_3Cats_2HRUs/flexConfig_Areas.dat +++ b/test/reference_results/06_3Cats_2HRUs/flexConfig_Areas.dat @@ -4,4 +4,4 @@ FLEX_HRU_CHARACTERISTICS_FILE_V1.1 AREAS (km2) 2.5 7.5 8.0 12.0 -24.0 6.0 \ No newline at end of file +24.0 6.0 diff --git a/test/reference_results/06_3Cats_2HRUs/flexConfig_HRU1.dat b/test/reference_results/06_3Cats_2HRUs/flexConfig_HRU1.dat index ca1f5dd..2eef17a 100644 --- a/test/reference_results/06_3Cats_2HRUs/flexConfig_HRU1.dat +++ b/test/reference_results/06_3Cats_2HRUs/flexConfig_HRU1.dat @@ -8,8 +8,8 @@ status max number of reservoirs .false. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.true. 1 ! enableFR ! -.false. 1 ! enableSR ! +.true. 1 ! enableFR ! +.false. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/06_3Cats_2HRUs/flexConfig_HRU2.dat b/test/reference_results/06_3Cats_2HRUs/flexConfig_HRU2.dat index d164923..561ad34 100644 --- a/test/reference_results/06_3Cats_2HRUs/flexConfig_HRU2.dat +++ b/test/reference_results/06_3Cats_2HRUs/flexConfig_HRU2.dat @@ -8,8 +8,8 @@ status max number of reservoirs .true. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.true. 1 ! enableFR ! -.true. 1 ! enableSR ! +.true. 1 ! enableFR ! +.true. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/06_3Cats_2HRUs/input.dat b/test/reference_results/06_3Cats_2HRUs/input.dat index 761615b..6728ccc 100644 --- a/test/reference_results/06_3Cats_2HRUs/input.dat +++ b/test/reference_results/06_3Cats_2HRUs/input.dat @@ -1,9 +1,9 @@ -Maimai data courtesy Jeff McDonnell -1.0 ! stepsize (d) -ST1: All data in mm/d -ST2 -ST3 -* ! list directed format ok here +Maimai data courtesy Jeff McDonnell +1.0 ! stepsize (d) +ST1: All data in mm/d +ST2 +ST3 +* ! list directed format ok here Year, Month, Day, Hour, Min, P_C1, P_C2, P_C3, E_C1, E_C2, E_C3, Q_C1, QC_Q_C1, Q_C2, QC_Q_C2, Q_C3, QC_Q_C3, 1981 1 1 0 0 2.50000 2.56000 3.81000 0.59700 0.65600 0.64500 1.38891 0 1.44086 0 1.43339 0 1981 1 2 0 0 8.02000 4.86000 5.39000 0.35900 0.12500 0.08000 1.44721 0 1.34096 0 1.11329 0 @@ -14,4 +14,4 @@ ST3 1981 1 7 0 0 1.63000 5.23000 7.14000 0.00000 0.00000 0.25200 2.59718 0 2.24096 0 2.06458 0 1981 1 8 0 0 0.31000 0.79000 1.08000 0.00000 0.00000 0.00000 2.15247 0 1.87579 0 1.70221 0 1981 1 9 0 0 0.26000 0.75000 0.69000 0.00000 0.07400 0.22900 1.98105 0 1.86998 0 1.44663 0 - 1981 1 10 0 0 1.95000 1.37000 1.15000 0.00000 0.00000 0.18100 1.92873 0 1.67512 0 1.67596 0 \ No newline at end of file + 1981 1 10 0 0 1.95000 1.37000 1.15000 0.00000 0.00000 0.18100 1.92873 0 1.67512 0 1.67596 0 diff --git a/test/reference_results/06_3Cats_2HRUs/test.INF_DAT b/test/reference_results/06_3Cats_2HRUs/test.INF_DAT index 8fd8e70..96c6c38 100644 --- a/test/reference_results/06_3Cats_2HRUs/test.INF_DAT +++ b/test/reference_results/06_3Cats_2HRUs/test.INF_DAT @@ -20,4 +20,4 @@ DATA_SPEX_FILE_V1.3 0,0 ! quality code columns for observed input data (relative to indxQC) 0 ! quality code columns for observed other data (relative to indxQC) -1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 +1, 1, 10 ! iStartWarmInfern, iStartInfern, iEndInfern 1, 27, 679 / 1, 680, 1331 / 1, 27, 353 / 1, 27, 1331 / 1, 27, 6549 diff --git a/test/reference_results/06_3Cats_2HRUs/test.py b/test/reference_results/06_3Cats_2HRUs/test.py index 50e4c01..9eed7aa 100644 --- a/test/reference_results/06_3Cats_2HRUs/test.py +++ b/test/reference_results/06_3Cats_2HRUs/test.py @@ -3,12 +3,15 @@ """ import sys + # Add the path where the Superflex module is -sys.path.append('/home/dalmo/Documents/BitBucket/superflexPython/C_so/') -from superflex import Superflex_C -import numpy as np +sys.path.append("/home/dalmo/Documents/BitBucket/superflexPython/C_so/") from os import chdir -chdir('/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/06_3Cats_2HRUs/') + +import numpy as np +from superflex import Superflex_C + +chdir("/home/dalmo/Documents/BitBucket/superflexpy_aug2019/test/reference_results/06_3Cats_2HRUs/") # Initialize the class sup = Superflex_C() @@ -22,4 +25,9 @@ # Get the output output = sup.run_model(parameters) -np.savetxt('Results.csv', X = output, delimiter = ',', header = 'Q_c1, Q_c2, Q_c3, Q_c1_h1, S_c1_h1_FR, Q_c2_h1, S_c2_h1_FR, Q_c3_h1, S_c3_h1_FR, Q_c1_h2, E_c1_h2_UR, S_c1_h2_UR, S_c1_h2_FR, S_c1_h2_SR, Q_c2_h2, E_c2_h2_UR, S_c2_h2_UR, S_c2_h2_FR, S_c2_h2_SR, Q_c3_h2, E_c3_h2_UR, S_c3_h2_UR, S_c3_h2_FR, S_c3_h2_SR') \ No newline at end of file +np.savetxt( + "Results.csv", + X=output, + delimiter=",", + header="Q_c1, Q_c2, Q_c3, Q_c1_h1, S_c1_h1_FR, Q_c2_h1, S_c2_h1_FR, Q_c3_h1, S_c3_h1_FR, Q_c1_h2, E_c1_h2_UR, S_c1_h2_UR, S_c1_h2_FR, S_c1_h2_SR, Q_c2_h2, E_c2_h2_UR, S_c2_h2_UR, S_c2_h2_FR, S_c2_h2_SR, Q_c3_h2, E_c3_h2_UR, S_c3_h2_UR, S_c3_h2_FR, S_c3_h2_SR", +) diff --git a/test/reference_results/07_FR_2dt/M01_main.dat b/test/reference_results/07_FR_2dt/M01_main.dat index 591f824..d19a64d 100644 --- a/test/reference_results/07_FR_2dt/M01_main.dat +++ b/test/reference_results/07_FR_2dt/M01_main.dat @@ -21,21 +21,21 @@ HRU CHARACTERISTICS FILE NAME UNITS FILE NAME "*/flexConfig_ML01.dat" !======== -UNITS PRESENCE MATRIX +UNITS PRESENCE MATRIX FC_01(Lumped) ! ID .true. ! !======== -INPUTS DESCRIPTION +INPUTS DESCRIPTION 2 ! precipitation input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! tracer input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) 2 ! etp input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! temperature input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) !======== -DRAINAGE DISTANCES +DRAINAGE DISTANCES riverScaleInc riverScaleOut riverSlopeInc riverSlopeOut 1.0 1.0 1.0 1.0 !======== -DEPENDENCIES +DEPENDENCIES DrainsInto 0 !======== diff --git a/test/reference_results/07_FR_2dt/SuperflexRes.dat b/test/reference_results/07_FR_2dt/SuperflexRes.dat index d6a477e..1dae742 100644 --- a/test/reference_results/07_FR_2dt/SuperflexRes.dat +++ b/test/reference_results/07_FR_2dt/SuperflexRes.dat @@ -1,5 +1,5 @@ Data modelled by SF_ISO - C1Wv%Qstream U1F1Wv%Sf[1] + C1Wv%Qstream U1F1Wv%Sf[1] 1.78090058718633E-04 1.99643819882563E-01 1.06480001699028E+00 6.47004378590200E+00 6.23475464220494E-01 5.22309285746101E+00 diff --git a/test/reference_results/07_FR_2dt/flexConfig_Areas.dat b/test/reference_results/07_FR_2dt/flexConfig_Areas.dat index e228cdd..4cc9caa 100644 --- a/test/reference_results/07_FR_2dt/flexConfig_Areas.dat +++ b/test/reference_results/07_FR_2dt/flexConfig_Areas.dat @@ -2,4 +2,4 @@ FLEX_HRU_CHARACTERISTICS_FILE_V1.1 ! Description: HRUs characterisics (-1=NA, depending on mask Unit Presence Matrix) !======== AREAS (km2) -1.0 \ No newline at end of file +1.0 diff --git a/test/reference_results/07_FR_2dt/flexConfig_ML01.dat b/test/reference_results/07_FR_2dt/flexConfig_ML01.dat index ea05a95..785cd27 100644 --- a/test/reference_results/07_FR_2dt/flexConfig_ML01.dat +++ b/test/reference_results/07_FR_2dt/flexConfig_ML01.dat @@ -8,8 +8,8 @@ status max number of reservoirs .false. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.true. 1 ! enableFR ! -.false. 1 ! enableSR ! +.true. 1 ! enableFR ! +.false. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/07_FR_2dt/input.dat b/test/reference_results/07_FR_2dt/input.dat index f96f5d7..e607929 100644 --- a/test/reference_results/07_FR_2dt/input.dat +++ b/test/reference_results/07_FR_2dt/input.dat @@ -14,4 +14,4 @@ ST3 1985 1 8 0 0 0 4.400000e+000 0.000000e+000 7.436842e-001 1985 1 9 0 0 0 1.700000e+000 0.000000e+000 4.998316e-001 1985 1 10 0 0 0 0.000000e+000 0.000000e+000 2.404421e-001 - 1985 1 11 0 0 0 7.360000e+001 0.000000e+000 2.727445e+001 \ No newline at end of file + 1985 1 11 0 0 0 7.360000e+001 0.000000e+000 2.727445e+001 diff --git a/test/reference_results/07_FR_2dt/test.py b/test/reference_results/07_FR_2dt/test.py index eaf142c..3292d2e 100644 --- a/test/reference_results/07_FR_2dt/test.py +++ b/test/reference_results/07_FR_2dt/test.py @@ -3,11 +3,14 @@ """ import sys + # Add the path where the Superflex module is -sys.path.append('/home/dalmo/Documents/BitBucket/superflexPython/C_so/') -from superflex import Superflex_C -import numpy as np +sys.path.append("/home/dalmo/Documents/BitBucket/superflexPython/C_so/") from os import chdir, path + +import numpy as np +from superflex import Superflex_C + chdir(path.abspath(path.dirname(__file__))) # Initialize the class @@ -22,4 +25,4 @@ # Get the output output = sup.run_model(parameters) -np.savetxt('Results.csv', X=output, delimiter=',', header='Q_FR, S_FR') +np.savetxt("Results.csv", X=output, delimiter=",", header="Q_FR, S_FR") diff --git a/test/reference_results/08_UR_2dt/M01_main.dat b/test/reference_results/08_UR_2dt/M01_main.dat index 591f824..d19a64d 100644 --- a/test/reference_results/08_UR_2dt/M01_main.dat +++ b/test/reference_results/08_UR_2dt/M01_main.dat @@ -21,21 +21,21 @@ HRU CHARACTERISTICS FILE NAME UNITS FILE NAME "*/flexConfig_ML01.dat" !======== -UNITS PRESENCE MATRIX +UNITS PRESENCE MATRIX FC_01(Lumped) ! ID .true. ! !======== -INPUTS DESCRIPTION +INPUTS DESCRIPTION 2 ! precipitation input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! tracer input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) 2 ! etp input (1=lumped,2=perSubcatchment(ID),3=perFied) 0 ! temperature input (0=absent,1=lumped,2=perSubcatchment(ID),3=perFied) !======== -DRAINAGE DISTANCES +DRAINAGE DISTANCES riverScaleInc riverScaleOut riverSlopeInc riverSlopeOut 1.0 1.0 1.0 1.0 !======== -DEPENDENCIES +DEPENDENCIES DrainsInto 0 !======== diff --git a/test/reference_results/08_UR_2dt/SuperflexRes.dat b/test/reference_results/08_UR_2dt/SuperflexRes.dat index 14b197b..003c635 100644 --- a/test/reference_results/08_UR_2dt/SuperflexRes.dat +++ b/test/reference_results/08_UR_2dt/SuperflexRes.dat @@ -1,5 +1,5 @@ Data modelled by SF_ISO - C1Wv%Qstream U1F1Wv%Eact U1F1Wv%Su[1] + C1Wv%Qstream U1F1Wv%Eact U1F1Wv%Su[1] 5.18607360339175E-04 4.35045154127755E+00 1.49805970272422E+00 1.89561508273747E-02 4.24730450508732E+00 1.36553839089484E+00 0.00000000000000E+00 6.51167195055035E-01 6.32040007847661E-02 diff --git a/test/reference_results/08_UR_2dt/flexConfig_Areas.dat b/test/reference_results/08_UR_2dt/flexConfig_Areas.dat index e228cdd..4cc9caa 100644 --- a/test/reference_results/08_UR_2dt/flexConfig_Areas.dat +++ b/test/reference_results/08_UR_2dt/flexConfig_Areas.dat @@ -2,4 +2,4 @@ FLEX_HRU_CHARACTERISTICS_FILE_V1.1 ! Description: HRUs characterisics (-1=NA, depending on mask Unit Presence Matrix) !======== AREAS (km2) -1.0 \ No newline at end of file +1.0 diff --git a/test/reference_results/08_UR_2dt/flexConfig_ML01.dat b/test/reference_results/08_UR_2dt/flexConfig_ML01.dat index cfad825..c694555 100644 --- a/test/reference_results/08_UR_2dt/flexConfig_ML01.dat +++ b/test/reference_results/08_UR_2dt/flexConfig_ML01.dat @@ -8,8 +8,8 @@ status max number of reservoirs .true. 1 ! enableUR ! UR-B-ID: supported= 0,1 (more options easy to add) .false. 1 ! enableCR ! UR-E-ID: supported= 0,1,2,3,20,21,22,30,31,32 .false. 1 ! enableRR ! UR-D-ID: supported= 0 -.false. 1 ! enableFR ! -.false. 1 ! enableSR ! +.false. 1 ! enableFR ! +.false. 1 ! enableSR ! !======== FLEX CONSTITUTIVE RELATIONSHIPS 2,1,31,0 ! URFunID - {A,B,E,D} [see cpsm_dmsl_kit], 0=zero*, 1=linear*, 2=power, 3=refPow, 10=logistic, 11=quadroid*, 20=exp, 21=hyperExp*, 22=con2lin*, 30=tessier, 31=michMentes*, 32=lin2con*, *=algebraic, D-recommends {21,1,31,0} @@ -19,8 +19,8 @@ FLEX CONSTITUTIVE RELATIONSHIPS !======== FLEX LAG FUNCTION INFORMATION convShape smthID max number of conv states ! convolution function options: "none","halfTri"(tR),"symTri"(tS,tR),"genTri"(tS,rR,tF) -"none" -1 1 ! RR -"none" -1 20 ! FR +"none" -1 1 ! RR +"none" -1 20 ! FR "none" -1 50 ! SR smthID: -1=original, 0=quadrature, 1=midpoint, 2=trapezoidal (Lag function smoothing) !======== FLEX INITIAL CONDITIONS INFO @@ -92,7 +92,7 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "BetaQq_uCR, - " 3.0 0.1 10.0 10.0 -9999.9 1 T 0 CR, runoff coefficient function parameter, (power) Beta->0 gives stronger step-threshold in A(S) function "MedQq_uCR, - " 0.5 0.1 1.0 10.0 -9999.9 0 F 0 CR, runoff coefficient function parameter, center for logistic function (see SF 2011 papers, Table 1 parameter lambda) "BetaQq_sCR, - " 1.e-2 1.e-2 10.0 10.0 -9999.9 1 F 0 CR, (power) Beta->0 gives stronger step-threshold in A(S) function -"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow +"K_Qb_sCR, 1/t " 1.e-3 1.e-6 0.2 10.0 -9999.9 1 T 0 CR, K in Q=K*S for matrix flow "K_Qd_sCR, 1/t " 0.e-3 1.e-6 2.0 10.0 -9999.9 0 F 0 CR, K in Q=K*S for flow to drainage network "Sini_SR, mm " 1.0 1.0 1.e4 10.0 -9999.9 1 F 0 SR, initial state "MsmtQqSb_FR, mm " 0.5 1.e-2 2.0 10.0 -9999.9 1 F 0 FR, (line2line) smoothing factor for piecewise linear Q-S relation (with KmQq_FR and KpQq_FR) @@ -102,9 +102,9 @@ MORTO_PARAMETER_INFO ! (parameters that dont exist for this configuration) "Dspl_ID, - " 0.0 0.0 1.0 10.0 -9999.9 0 T 0 IR, Dspl_ID->1 diverts input from IR to UR (e.g. to represent throughfall) "Smax_IR, mm " 10.0 1.e-2 20.0 10.0 -9999.9 1 T 0 IR, store capacity "MsmtQE_S0_IR, - " 1.e-1 1.e-3 1.0 10.0 -9999.9 1 T 0 IR, smoothing parameter in interception reservoir, controls IR smoothness when odeSolverIRR={0,1,2}: MsmtQE_S0_IR->0 yields stronger threshold in IR reservoir. DK-NB1: probably should be kept fixed; DK-NB2: must be MORTO when odeSolverIRR={-1,-2}. For power smoothing IRsmth~0.3-0.5 is sufficient -!======== +!======== ACTIVE_STATE_INFO -stateName stateDef stateLo stateHi stateScal +stateName stateDef stateLo stateHi stateScal "Qstrm " 0.0 0.0 1.e4 10.0 "Qpcor " 0.0 0.0 1.e4 10.0 "Eact " 0.0 0.0 1.e4 10.0 diff --git a/test/reference_results/08_UR_2dt/test.py b/test/reference_results/08_UR_2dt/test.py index 8a186fe..fed1a51 100644 --- a/test/reference_results/08_UR_2dt/test.py +++ b/test/reference_results/08_UR_2dt/test.py @@ -3,11 +3,14 @@ """ import sys + # Add the path where the Superflex module is -sys.path.append('/home/dalmo/Documents/BitBucket/superflexPython/C_so/') -from superflex import Superflex_C -import numpy as np +sys.path.append("/home/dalmo/Documents/BitBucket/superflexPython/C_so/") from os import chdir, path + +import numpy as np +from superflex import Superflex_C + chdir(path.abspath(path.dirname(__file__))) # Initialize the class @@ -22,4 +25,4 @@ # Get the output output = sup.run_model(parameters) -np.savetxt('Results.csv', X=output, delimiter=',', header='Q_UR, E_UR, S_UR') \ No newline at end of file +np.savetxt("Results.csv", X=output, delimiter=",", header="Q_UR, E_UR, S_UR") diff --git a/test/reference_results/README.md b/test/reference_results/README.md index cbf0c42..8933dae 100644 --- a/test/reference_results/README.md +++ b/test/reference_results/README.md @@ -8,4 +8,4 @@ configuration of Superflex (Fenicia et al., 2011). ### Bibliography -Fenicia, F., D. Kavetski, and H. H. G. Savenije (2011), Elements of a flexible approach for conceptual hydrological modeling: 1. Motivation and theoretical development, Water Resources Research, 47(11), W11510, 10.1029/2010wr010174. \ No newline at end of file +Fenicia, F., D. Kavetski, and H. H. G. Savenije (2011), Elements of a flexible approach for conceptual hydrological modeling: 1. Motivation and theoretical development, Water Resources Research, 47(11), W11510, 10.1029/2010wr010174. diff --git a/test/unittest/01_FR.py b/test/unittest/01_FR.py index 807cba2..ec70e9b 100644 --- a/test/unittest/01_FR.py +++ b/test/unittest/01_FR.py @@ -23,20 +23,23 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -import unittest import sys -from os.path import abspath, join, dirname +import unittest +from os.path import abspath, dirname, join -import pandas as pd import numpy as np +import pandas as pd # Package path is 2 levels above this file -package_path = join(abspath(dirname(__file__)), '..', '..') +package_path = join(abspath(dirname(__file__)), "..", "..") sys.path.insert(0, package_path) from superflexpy.implementation.elements.hbv import PowerReservoir +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerNumba, + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusNumba, PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerNumba, ImplicitEulerPython class TestFR(unittest.TestCase): @@ -48,34 +51,32 @@ class TestFR(unittest.TestCase): """ def _init_model(self, solver): - - if solver == 'numba': + if solver == "numba": solver = PegasusNumba() num_app = ImplicitEulerNumba(root_finder=solver) - elif solver == 'python': + elif solver == "python": solver = PegasusPython() num_app = ImplicitEulerPython(root_finder=solver) - fr = PowerReservoir(parameters={'k': 0.01, - 'alpha': 2.5}, - states={'S0': 0.0}, - approximation=num_app, - id='FR') + fr = PowerReservoir(parameters={"k": 0.01, "alpha": 2.5}, states={"S0": 0.0}, approximation=num_app, id="FR") fr.set_timestep(1.0) self._model = fr def _read_inputs(self): - - data = pd.read_csv('{}/test/reference_results/01_FR/input.dat'.format(package_path), header=6, sep='\s+|,\s+|,', engine='python') + data = pd.read_csv( + "{}/test/reference_results/01_FR/input.dat".format(package_path), + header=6, + sep="\s+|,\s+|,", + engine="python", + ) self._precipitation = data.iloc[:, 6].values self._pet = data.iloc[:, 7].values def _read_outputs(self): - self._superflex_output = pd.read_csv('{}/test/reference_results/01_FR/Results.csv'.format(package_path)) + self._superflex_output = pd.read_csv("{}/test/reference_results/01_FR/Results.csv".format(package_path)) def _test_fr_start_stop(self, solver): - self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -84,7 +85,7 @@ def _test_fr_start_stop(self, solver): self._model.set_input([self._precipitation[:5]]) out = self._model.get_output() - msg = 'Fail in the first half' + msg = "Fail in the first half" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[:5, 1]), msg=msg) @@ -93,21 +94,18 @@ def _test_fr_start_stop(self, solver): self._model.set_input([self._precipitation[5:]]) out = self._model.get_output() - msg = 'Fail in the second half' + msg = "Fail in the second half" self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[5:, 1]), msg=msg) def test_fr_start_stop_python(self): - - self._test_fr_start_stop(solver='python') + self._test_fr_start_stop(solver="python") def test_fr_start_stop_numba(self): - - self._test_fr_start_stop(solver='numba') + self._test_fr_start_stop(solver="numba") def _test_2_rounds(self, solver): - self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -116,7 +114,7 @@ def _test_2_rounds(self, solver): self._model.set_input([self._precipitation]) out = self._model.get_output() - msg = 'Fail in the first round' + msg = "Fail in the first round" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[:, 1]), msg=msg) @@ -125,18 +123,16 @@ def _test_2_rounds(self, solver): self._model.reset_states() out = self._model.get_output() - msg = 'Fail in the second round' + msg = "Fail in the second round" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[:, 1]), msg=msg) def test_2_rounds_python(self): - - self._test_2_rounds(solver='python') + self._test_2_rounds(solver="python") def test_2_rounds_numba(self): - - self._test_2_rounds(solver='numba') + self._test_2_rounds(solver="numba") # if __name__ == "__main__": diff --git a/test/unittest/02_UR.py b/test/unittest/02_UR.py index 0014314..0995d11 100644 --- a/test/unittest/02_UR.py +++ b/test/unittest/02_UR.py @@ -23,20 +23,23 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -import unittest import sys -from os.path import abspath, join, dirname +import unittest +from os.path import abspath, dirname, join -import pandas as pd import numpy as np +import pandas as pd # Package path is 2 levels above this file -package_path = join(abspath(dirname(__file__)), '..', '..') +package_path = join(abspath(dirname(__file__)), "..", "..") sys.path.insert(0, package_path) from superflexpy.implementation.elements.hbv import UnsaturatedReservoir +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerNumba, + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusNumba, PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerNumba, ImplicitEulerPython class TestUR(unittest.TestCase): @@ -48,36 +51,37 @@ class TestUR(unittest.TestCase): """ def _init_model(self, solver): - - if solver == 'numba': + if solver == "numba": solver = PegasusNumba() num_app = ImplicitEulerNumba(root_finder=solver) - elif solver == 'python': + elif solver == "python": solver = PegasusPython() num_app = ImplicitEulerPython(root_finder=solver) - ur = UnsaturatedReservoir(parameters={'Smax': 50.0, - 'Ce': 1.5, - 'm': 0.01, - 'beta': 1.5}, - states={'S0': 0.2 * 50.0}, - approximation=num_app, - id='UR') + ur = UnsaturatedReservoir( + parameters={"Smax": 50.0, "Ce": 1.5, "m": 0.01, "beta": 1.5}, + states={"S0": 0.2 * 50.0}, + approximation=num_app, + id="UR", + ) ur.set_timestep(1.0) self._model = ur def _read_inputs(self): - - data = pd.read_csv('{}/test/reference_results/02_UR/input.dat'.format(package_path), header=6, sep='\s+|,\s+|,', engine='python') + data = pd.read_csv( + "{}/test/reference_results/02_UR/input.dat".format(package_path), + header=6, + sep="\s+|,\s+|,", + engine="python", + ) self._precipitation = data.iloc[:, 6].values self._pet = data.iloc[:, 7].values def _read_outputs(self): - self._superflex_output = pd.read_csv('{}/test/reference_results/02_UR/Results.csv'.format(package_path)) + self._superflex_output = pd.read_csv("{}/test/reference_results/02_UR/Results.csv".format(package_path)) def _test_ur_start_stop(self, solver): - self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -87,7 +91,7 @@ def _test_ur_start_stop(self, solver): out = self._model.get_output() aet = self._model.get_AET() - msg = 'Fail in the first half' + msg = "Fail in the first half" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg=msg) self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:5, 1]), msg=msg) @@ -98,22 +102,19 @@ def _test_ur_start_stop(self, solver): out = self._model.get_output() aet = self._model.get_AET() - msg = 'Fail in the second half' + msg = "Fail in the second half" self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg=msg) self.assertTrue(np.allclose(aet, self._superflex_output.iloc[5:, 1]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[5:, 2]), msg=msg) def test_ur_start_stop_python(self): - - self._test_ur_start_stop(solver='python') + self._test_ur_start_stop(solver="python") def test_ur_start_stop_numba(self): - - self._test_ur_start_stop(solver='numba') + self._test_ur_start_stop(solver="numba") def _test_2_rounds(self, solver): - self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -123,7 +124,7 @@ def _test_2_rounds(self, solver): out = self._model.get_output() aet = self._model.get_AET() - msg = 'Fail in the first round' + msg = "Fail in the first round" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg=msg) @@ -134,19 +135,17 @@ def _test_2_rounds(self, solver): out = self._model.get_output() aet = self._model.get_AET() - msg = 'Fail in the second round' + msg = "Fail in the second round" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[:, 2]), msg=msg) def test_2_rounds_python(self): - - self._test_2_rounds(solver='python') + self._test_2_rounds(solver="python") def test_2_rounds_numba(self): - - self._test_2_rounds(solver='numba') + self._test_2_rounds(solver="numba") if __name__ == "__main__": diff --git a/test/unittest/03_UR_FR.py b/test/unittest/03_UR_FR.py index 8af7a1a..3f8b3bb 100644 --- a/test/unittest/03_UR_FR.py +++ b/test/unittest/03_UR_FR.py @@ -23,21 +23,25 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -import unittest import sys -from os.path import abspath, join, dirname +import unittest +from os.path import abspath, dirname, join -import pandas as pd import numpy as np +import pandas as pd # Package path is 2 levels above this file -package_path = join(abspath(dirname(__file__)), '..', '..') +package_path = join(abspath(dirname(__file__)), "..", "..") sys.path.insert(0, package_path) +from superflexpy.framework.unit import Unit from superflexpy.implementation.elements.hbv import PowerReservoir, UnsaturatedReservoir +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerNumba, + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusNumba, PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerNumba, ImplicitEulerPython -from superflexpy.framework.unit import Unit + class TestHRU(unittest.TestCase): """ @@ -49,132 +53,125 @@ class TestHRU(unittest.TestCase): """ def _init_model(self, solver): - - if solver == 'numba': + if solver == "numba": solver = PegasusNumba() num_app = ImplicitEulerNumba(root_finder=solver) - elif solver == 'python': + elif solver == "python": solver = PegasusPython() num_app = ImplicitEulerPython(root_finder=solver) - fr = PowerReservoir(parameters = {'k' : 0.01, - 'alpha' : 2.5}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'FR') - - ur = UnsaturatedReservoir(parameters = {'Smax' : 50.0, - 'Ce' : 1.5, - 'm' : 0.01, - 'beta' : 1.5, - }, - states = {'S0' : 0.2*50.0, 'PET' : None}, - approximation=num_app, - id = 'UR') - - hru = Unit(layers = [ - [ur], - [fr] - ], - id = 'H1') + fr = PowerReservoir(parameters={"k": 0.01, "alpha": 2.5}, states={"S0": 0.0}, approximation=num_app, id="FR") + + ur = UnsaturatedReservoir( + parameters={ + "Smax": 50.0, + "Ce": 1.5, + "m": 0.01, + "beta": 1.5, + }, + states={"S0": 0.2 * 50.0, "PET": None}, + approximation=num_app, + id="UR", + ) + + hru = Unit(layers=[[ur], [fr]], id="H1") hru.set_timestep(1.0) self._model = hru def _read_inputs(self): - - data = pd.read_csv('{}/test/reference_results/03_UR_FR/input.dat'.format(package_path), header = 6, sep = '\s+|,\s+|,', engine = 'python') - self._precipitation = data.iloc[:,6].values - self._pet = data.iloc[:,7].values + data = pd.read_csv( + "{}/test/reference_results/03_UR_FR/input.dat".format(package_path), + header=6, + sep="\s+|,\s+|,", + engine="python", + ) + self._precipitation = data.iloc[:, 6].values + self._pet = data.iloc[:, 7].values def _read_outputs(self): - self._superflex_output = pd.read_csv('{}/test/reference_results/03_UR_FR/Results.csv'.format(package_path)) + self._superflex_output = pd.read_csv("{}/test/reference_results/03_UR_FR/Results.csv".format(package_path)) def _test_hru_start_stop(self, solver): - - self._init_model(solver = solver) + self._init_model(solver=solver) self._read_outputs() self._read_inputs() # First half of time series self._model.set_input([self._precipitation[:5], self._pet[:5]]) out = self._model.get_output() - aet = self._model.call_internal(id = 'UR', method = 'get_AET') - s_ur = self._model.get_internal(id = 'UR', attribute = 'state_array')[:, 0] - s_fr = self._model.get_internal(id = 'FR', attribute = 'state_array')[:, 0] + aet = self._model.call_internal(id="UR", method="get_AET") + s_ur = self._model.get_internal(id="UR", attribute="state_array")[:, 0] + s_fr = self._model.get_internal(id="FR", attribute="state_array")[:, 0] - msg = 'Fail in the first half' + msg = "Fail in the first half" - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg = msg) - self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:5, 1]), msg = msg) - self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:5, 2]), msg = msg) - self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:5, 3]), msg = msg) + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg=msg) + self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:5, 1]), msg=msg) + self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:5, 2]), msg=msg) + self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:5, 3]), msg=msg) # Second half of time series self._model.set_input([self._precipitation[5:], self._pet[5:]]) out = self._model.get_output() - aet = self._model.call_internal(id = 'UR', method = 'get_AET') - s_ur = self._model.get_internal(id = 'UR', attribute = 'state_array')[:, 0] - s_fr = self._model.get_internal(id = 'FR', attribute = 'state_array')[:, 0] + aet = self._model.call_internal(id="UR", method="get_AET") + s_ur = self._model.get_internal(id="UR", attribute="state_array")[:, 0] + s_fr = self._model.get_internal(id="FR", attribute="state_array")[:, 0] - msg = 'Fail in the second half' + msg = "Fail in the second half" - self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg = msg) - self.assertTrue(np.allclose(aet, self._superflex_output.iloc[5:, 1]), msg = msg) - self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[5:, 2]), msg = msg) - self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[5:, 3]), msg = msg) + self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg=msg) + self.assertTrue(np.allclose(aet, self._superflex_output.iloc[5:, 1]), msg=msg) + self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[5:, 2]), msg=msg) + self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[5:, 3]), msg=msg) def test_hru_start_stop_python(self): - - self._test_hru_start_stop(solver = 'python') + self._test_hru_start_stop(solver="python") def test_hru_start_stop_numba(self): - - self._test_hru_start_stop(solver = 'numba') + self._test_hru_start_stop(solver="numba") def _test_2_rounds(self, solver): - - self._init_model(solver = solver) + self._init_model(solver=solver) self._read_outputs() self._read_inputs() # First half of time series self._model.set_input([self._precipitation, self._pet]) out = self._model.get_output() - aet = self._model.call_internal(id = 'UR', method = 'get_AET') - s_ur = self._model.get_internal(id = 'UR', attribute = 'state_array')[:, 0] - s_fr = self._model.get_internal(id = 'FR', attribute = 'state_array')[:, 0] + aet = self._model.call_internal(id="UR", method="get_AET") + s_ur = self._model.get_internal(id="UR", attribute="state_array")[:, 0] + s_fr = self._model.get_internal(id="FR", attribute="state_array")[:, 0] - msg = 'Fail in the first round' + msg = "Fail in the first round" - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg = msg) - self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg = msg) - self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:, 2]), msg = msg) - self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:, 3]), msg = msg) + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) + self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg=msg) + self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:, 2]), msg=msg) + self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:, 3]), msg=msg) # Second half of time series self._model.reset_states() out = self._model.get_output() - aet = self._model.call_internal(id = 'UR', method = 'get_AET') - s_ur = self._model.get_internal(id = 'UR', attribute = 'state_array')[:, 0] - s_fr = self._model.get_internal(id = 'FR', attribute = 'state_array')[:, 0] + aet = self._model.call_internal(id="UR", method="get_AET") + s_ur = self._model.get_internal(id="UR", attribute="state_array")[:, 0] + s_fr = self._model.get_internal(id="FR", attribute="state_array")[:, 0] - msg = 'Fail in the second round' + msg = "Fail in the second round" - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg = msg) - self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg = msg) - self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:, 2]), msg = msg) - self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:, 3]), msg = msg) + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) + self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg=msg) + self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:, 2]), msg=msg) + self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:, 3]), msg=msg) def test_2_rounds_python(self): - - self._test_2_rounds(solver = 'python') + self._test_2_rounds(solver="python") def test_2_rounds_numba(self): + self._test_2_rounds(solver="numba") - self._test_2_rounds(solver = 'numba') if __name__ == "__main__": unittest.main() # test = TestFR() - # test.test_2_rounds_python() \ No newline at end of file + # test.test_2_rounds_python() diff --git a/test/unittest/04_UR_FR_SR.py b/test/unittest/04_UR_FR_SR.py index d6a5c57..bd22bd0 100644 --- a/test/unittest/04_UR_FR_SR.py +++ b/test/unittest/04_UR_FR_SR.py @@ -23,22 +23,26 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -import unittest import sys -from os.path import abspath, join, dirname +import unittest +from os.path import abspath, dirname, join -import pandas as pd import numpy as np +import pandas as pd # Package path is 2 levels above this file -package_path = join(abspath(dirname(__file__)), '..', '..') +package_path = join(abspath(dirname(__file__)), "..", "..") sys.path.insert(0, package_path) -from superflexpy.implementation.elements.hbv import PowerReservoir, UnsaturatedReservoir -from superflexpy.implementation.root_finders.pegasus import PegasusPython, PegasusNumba -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerNumba, ImplicitEulerPython -from superflexpy.implementation.elements.structure_elements import Splitter, Junction from superflexpy.framework.unit import Unit +from superflexpy.implementation.elements.hbv import PowerReservoir, UnsaturatedReservoir +from superflexpy.implementation.elements.structure_elements import Junction, Splitter +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerNumba, + ImplicitEulerPython, +) +from superflexpy.implementation.root_finders.pegasus import PegasusNumba, PegasusPython + class TestStructureElements(unittest.TestCase): """ @@ -49,155 +53,139 @@ class TestStructureElements(unittest.TestCase): """ def _init_model(self, solver): - - if solver == 'numba': + if solver == "numba": solver = PegasusNumba() num_app = ImplicitEulerNumba(root_finder=solver) - elif solver == 'python': + elif solver == "python": solver = PegasusPython() num_app = ImplicitEulerPython(root_finder=solver) - fr = PowerReservoir(parameters = {'k' : 0.01, - 'alpha' : 2.5}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'FR') - - sr = PowerReservoir(parameters = {'k' : 1e-4, - 'alpha' : 1.0}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'SR') - - ur = UnsaturatedReservoir(parameters = {'Smax' : 50.0, - 'Ce' : 1.5, - 'm' : 0.01, - 'beta' : 1.5, - }, - states = {'S0' : 0.2*50.0, 'PET' : None}, - approximation=num_app, - id = 'UR') - - s = Splitter(weight = [[0.3], [0.7]], - direction = [[0], [0]], - id = 'S') - - j = Junction(direction = [[0, 0]], - id = 'J') - - hru = Unit(layers = [ - [ur], - [s], - [fr, sr], - [j] - ], - id = 'H1') + fr = PowerReservoir(parameters={"k": 0.01, "alpha": 2.5}, states={"S0": 0.0}, approximation=num_app, id="FR") + + sr = PowerReservoir(parameters={"k": 1e-4, "alpha": 1.0}, states={"S0": 0.0}, approximation=num_app, id="SR") + + ur = UnsaturatedReservoir( + parameters={ + "Smax": 50.0, + "Ce": 1.5, + "m": 0.01, + "beta": 1.5, + }, + states={"S0": 0.2 * 50.0, "PET": None}, + approximation=num_app, + id="UR", + ) + + s = Splitter(weight=[[0.3], [0.7]], direction=[[0], [0]], id="S") + + j = Junction(direction=[[0, 0]], id="J") + + hru = Unit(layers=[[ur], [s], [fr, sr], [j]], id="H1") hru.set_timestep(1.0) self._model = hru def _read_inputs(self): - - data = pd.read_csv('{}/test/reference_results/04_UR_FR_SR/input.dat'.format(package_path), header = 6, sep = '\s+|,\s+|,', engine = 'python') - self._precipitation = data.iloc[:,6].values - self._pet = data.iloc[:,7].values + data = pd.read_csv( + "{}/test/reference_results/04_UR_FR_SR/input.dat".format(package_path), + header=6, + sep="\s+|,\s+|,", + engine="python", + ) + self._precipitation = data.iloc[:, 6].values + self._pet = data.iloc[:, 7].values def _read_outputs(self): - self._superflex_output = pd.read_csv('{}/test/reference_results/04_UR_FR_SR/Results.csv'.format(package_path)) + self._superflex_output = pd.read_csv("{}/test/reference_results/04_UR_FR_SR/Results.csv".format(package_path)) def _test_start_stop(self, solver): - - self._init_model(solver = solver) + self._init_model(solver=solver) self._read_outputs() self._read_inputs() # First half of time series self._model.set_input([self._precipitation[:5], self._pet[:5]]) out = self._model.get_output() - aet = self._model.call_internal(id = 'UR', method = 'get_AET') - s_ur = self._model.get_internal(id = 'UR', attribute = 'state_array')[:, 0] - s_fr = self._model.get_internal(id = 'FR', attribute = 'state_array')[:, 0] - s_sr = self._model.get_internal(id = 'SR', attribute = 'state_array')[:, 0] + aet = self._model.call_internal(id="UR", method="get_AET") + s_ur = self._model.get_internal(id="UR", attribute="state_array")[:, 0] + s_fr = self._model.get_internal(id="FR", attribute="state_array")[:, 0] + s_sr = self._model.get_internal(id="SR", attribute="state_array")[:, 0] - msg = 'Fail in the first half' + msg = "Fail in the first half" - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg = msg) - self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:5, 1]), msg = msg) - self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:5, 2]), msg = msg) - self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:5, 3]), msg = msg) - self.assertTrue(np.allclose(s_sr, self._superflex_output.iloc[:5, 4]), msg = msg) + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg=msg) + self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:5, 1]), msg=msg) + self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:5, 2]), msg=msg) + self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:5, 3]), msg=msg) + self.assertTrue(np.allclose(s_sr, self._superflex_output.iloc[:5, 4]), msg=msg) # Second half of time series self._model.set_input([self._precipitation[5:], self._pet[5:]]) out = self._model.get_output() - aet = self._model.call_internal(id = 'UR', method = 'get_AET') - s_ur = self._model.get_internal(id = 'UR', attribute = 'state_array')[:, 0] - s_fr = self._model.get_internal(id = 'FR', attribute = 'state_array')[:, 0] - s_sr = self._model.get_internal(id = 'SR', attribute = 'state_array')[:, 0] + aet = self._model.call_internal(id="UR", method="get_AET") + s_ur = self._model.get_internal(id="UR", attribute="state_array")[:, 0] + s_fr = self._model.get_internal(id="FR", attribute="state_array")[:, 0] + s_sr = self._model.get_internal(id="SR", attribute="state_array")[:, 0] - msg = 'Fail in the second half' + msg = "Fail in the second half" - self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg = msg) - self.assertTrue(np.allclose(aet, self._superflex_output.iloc[5:, 1]), msg = msg) - self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[5:, 2]), msg = msg) - self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[5:, 3]), msg = msg) - self.assertTrue(np.allclose(s_sr, self._superflex_output.iloc[5:, 4]), msg = msg) + self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg=msg) + self.assertTrue(np.allclose(aet, self._superflex_output.iloc[5:, 1]), msg=msg) + self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[5:, 2]), msg=msg) + self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[5:, 3]), msg=msg) + self.assertTrue(np.allclose(s_sr, self._superflex_output.iloc[5:, 4]), msg=msg) def test_start_stop_python(self): - - self._test_start_stop(solver = 'python') + self._test_start_stop(solver="python") def test_start_stop_numba(self): - - self._test_start_stop(solver = 'numba') + self._test_start_stop(solver="numba") def _test_2_rounds(self, solver): - - self._init_model(solver = solver) + self._init_model(solver=solver) self._read_outputs() self._read_inputs() # First half of time series self._model.set_input([self._precipitation, self._pet]) out = self._model.get_output() - aet = self._model.call_internal(id = 'UR', method = 'get_AET') - s_ur = self._model.get_internal(id = 'UR', attribute = 'state_array')[:, 0] - s_fr = self._model.get_internal(id = 'FR', attribute = 'state_array')[:, 0] - s_sr = self._model.get_internal(id = 'SR', attribute = 'state_array')[:, 0] + aet = self._model.call_internal(id="UR", method="get_AET") + s_ur = self._model.get_internal(id="UR", attribute="state_array")[:, 0] + s_fr = self._model.get_internal(id="FR", attribute="state_array")[:, 0] + s_sr = self._model.get_internal(id="SR", attribute="state_array")[:, 0] - msg = 'Fail in the first round' + msg = "Fail in the first round" - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg = msg) - self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg = msg) - self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:, 2]), msg = msg) - self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:, 3]), msg = msg) - self.assertTrue(np.allclose(s_sr, self._superflex_output.iloc[:, 4]), msg = msg) + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) + self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg=msg) + self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:, 2]), msg=msg) + self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:, 3]), msg=msg) + self.assertTrue(np.allclose(s_sr, self._superflex_output.iloc[:, 4]), msg=msg) # Second half of time series self._model.reset_states() out = self._model.get_output() - aet = self._model.call_internal(id = 'UR', method = 'get_AET') - s_ur = self._model.get_internal(id = 'UR', attribute = 'state_array')[:, 0] - s_fr = self._model.get_internal(id = 'FR', attribute = 'state_array')[:, 0] - s_sr = self._model.get_internal(id = 'SR', attribute = 'state_array')[:, 0] + aet = self._model.call_internal(id="UR", method="get_AET") + s_ur = self._model.get_internal(id="UR", attribute="state_array")[:, 0] + s_fr = self._model.get_internal(id="FR", attribute="state_array")[:, 0] + s_sr = self._model.get_internal(id="SR", attribute="state_array")[:, 0] - msg = 'Fail in the second round' + msg = "Fail in the second round" - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg = msg) - self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg = msg) - self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:, 2]), msg = msg) - self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:, 3]), msg = msg) - self.assertTrue(np.allclose(s_sr, self._superflex_output.iloc[:, 4]), msg = msg) + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) + self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg=msg) + self.assertTrue(np.allclose(s_ur, self._superflex_output.iloc[:, 2]), msg=msg) + self.assertTrue(np.allclose(s_fr, self._superflex_output.iloc[:, 3]), msg=msg) + self.assertTrue(np.allclose(s_sr, self._superflex_output.iloc[:, 4]), msg=msg) def test_2_rounds_python(self): - - self._test_2_rounds(solver = 'python') + self._test_2_rounds(solver="python") def test_2_rounds_numba(self): + self._test_2_rounds(solver="numba") - self._test_2_rounds(solver = 'numba') if __name__ == "__main__": unittest.main() # test = TestStructureElements() - # test.test_2_rounds_python() \ No newline at end of file + # test.test_2_rounds_python() diff --git a/test/unittest/05_2HRUs.py b/test/unittest/05_2HRUs.py index 120b3d6..7919c4c 100644 --- a/test/unittest/05_2HRUs.py +++ b/test/unittest/05_2HRUs.py @@ -23,23 +23,27 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -import unittest import sys -from os.path import abspath, join, dirname +import unittest +from os.path import abspath, dirname, join -import pandas as pd import numpy as np +import pandas as pd # Package path is 2 levels above this file -package_path = join(abspath(dirname(__file__)), '..', '..') +package_path = join(abspath(dirname(__file__)), "..", "..") sys.path.insert(0, package_path) +from superflexpy.framework.node import Node +from superflexpy.framework.unit import Unit from superflexpy.implementation.elements.hbv import PowerReservoir, UnsaturatedReservoir +from superflexpy.implementation.elements.structure_elements import Junction, Splitter +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerNumba, + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusNumba, PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerNumba, ImplicitEulerPython -from superflexpy.implementation.elements.structure_elements import Splitter, Junction -from superflexpy.framework.unit import Unit -from superflexpy.framework.node import Node + class TestStructureElements(unittest.TestCase): """ @@ -50,191 +54,171 @@ class TestStructureElements(unittest.TestCase): """ def _init_model(self, solver): - - if solver == 'numba': + if solver == "numba": solver = PegasusNumba() num_app = ImplicitEulerNumba(root_finder=solver) - elif solver == 'python': + elif solver == "python": solver = PegasusPython() num_app = ImplicitEulerPython(root_finder=solver) # Define HRU 1 (40%) - fr = PowerReservoir(parameters = {'k' : 0.01, - 'alpha' : 2.5}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'FR') + fr = PowerReservoir(parameters={"k": 0.01, "alpha": 2.5}, states={"S0": 0.0}, approximation=num_app, id="FR") - h1 = Unit(layers = [[fr]], id = 'H1') + h1 = Unit(layers=[[fr]], id="H1") # Define HRU 2 (60%) - fr = PowerReservoir(parameters = {'k' : 0.01, - 'alpha' : 2.5}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'FR') - - sr = PowerReservoir(parameters = {'k' : 1e-4, - 'alpha' : 1.0}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'SR') - - ur = UnsaturatedReservoir(parameters = {'Smax' : 50.0, - 'Ce' : 1.5, - 'm' : 0.01, - 'beta' : 1.5, - }, - states = {'S0' : 0.2*50.0, 'PET' : None}, - approximation=num_app, - id = 'UR') - - s = Splitter(weight = [[0.3], [0.7]], - direction = [[0], [0]], - id = 'S') - - j = Junction(direction = [[0, 0]], - id = 'J') - - h2 = Unit(layers = [ - [ur], - [s], - [fr, sr], - [j] - ], - id = 'H2') + fr = PowerReservoir(parameters={"k": 0.01, "alpha": 2.5}, states={"S0": 0.0}, approximation=num_app, id="FR") + + sr = PowerReservoir(parameters={"k": 1e-4, "alpha": 1.0}, states={"S0": 0.0}, approximation=num_app, id="SR") + + ur = UnsaturatedReservoir( + parameters={ + "Smax": 50.0, + "Ce": 1.5, + "m": 0.01, + "beta": 1.5, + }, + states={"S0": 0.2 * 50.0, "PET": None}, + approximation=num_app, + id="UR", + ) + + s = Splitter(weight=[[0.3], [0.7]], direction=[[0], [0]], id="S") + + j = Junction(direction=[[0, 0]], id="J") + + h2 = Unit(layers=[[ur], [s], [fr, sr], [j]], id="H2") # Define the catchment - cat = Node(units = [h1, h2], weights = [0.4, 0.6], area = 1.0, id = 'Cat') + cat = Node(units=[h1, h2], weights=[0.4, 0.6], area=1.0, id="Cat") cat.set_timestep(1.0) self._model = cat def _read_inputs(self): - - data = pd.read_csv('{}/test/reference_results/05_2HRUs/input.dat'.format(package_path), header = 6, sep = '\s+|,\s+|,', engine = 'python') - self._precipitation = data.iloc[:,6].values - self._pet = data.iloc[:,7].values + data = pd.read_csv( + "{}/test/reference_results/05_2HRUs/input.dat".format(package_path), + header=6, + sep="\s+|,\s+|,", + engine="python", + ) + self._precipitation = data.iloc[:, 6].values + self._pet = data.iloc[:, 7].values def _read_outputs(self): - self._superflex_output = pd.read_csv('{}/test/reference_results/05_2HRUs/Results.csv'.format(package_path)) + self._superflex_output = pd.read_csv("{}/test/reference_results/05_2HRUs/Results.csv".format(package_path)) def _test_start_stop(self, solver): - - self._init_model(solver = solver) + self._init_model(solver=solver) self._read_outputs() self._read_inputs() # First half of time series self._model.set_input([self._precipitation[:5], self._pet[:5]]) out = self._model.get_output() - out_h1 = self._model.call_internal(id = 'H1', method = 'get_output', solve = False) - s_fr_h1 = self._model.get_internal(id = 'H1_FR', attribute = 'state_array')[:, 0] - out_h2 = self._model.call_internal(id = 'H2', method = 'get_output', solve = False) - aet_h2 = self._model.call_internal(id = 'H2_UR', method = 'get_AET') - s_ur_h2 = self._model.get_internal(id = 'H2_UR', attribute = 'state_array')[:, 0] - s_fr_h2 = self._model.get_internal(id = 'H2_FR', attribute = 'state_array')[:, 0] - s_sr_h2 = self._model.get_internal(id = 'H2_SR', attribute = 'state_array')[:, 0] - - msg = 'Fail in the first half' - - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg = msg) - self.assertTrue(np.allclose(out_h1, self._superflex_output.iloc[:5, 1]), msg = msg) - self.assertTrue(np.allclose(s_fr_h1, self._superflex_output.iloc[:5, 3]), msg = msg) - self.assertTrue(np.allclose(out_h2, self._superflex_output.iloc[:5, 4]), msg = msg) - self.assertTrue(np.allclose(aet_h2, self._superflex_output.iloc[:5, 5]), msg = msg) - self.assertTrue(np.allclose(s_ur_h2, self._superflex_output.iloc[:5, 6]), msg = msg) - self.assertTrue(np.allclose(s_fr_h2, self._superflex_output.iloc[:5, 7]), msg = msg) - self.assertTrue(np.allclose(s_sr_h2, self._superflex_output.iloc[:5, 8]), msg = msg) + out_h1 = self._model.call_internal(id="H1", method="get_output", solve=False) + s_fr_h1 = self._model.get_internal(id="H1_FR", attribute="state_array")[:, 0] + out_h2 = self._model.call_internal(id="H2", method="get_output", solve=False) + aet_h2 = self._model.call_internal(id="H2_UR", method="get_AET") + s_ur_h2 = self._model.get_internal(id="H2_UR", attribute="state_array")[:, 0] + s_fr_h2 = self._model.get_internal(id="H2_FR", attribute="state_array")[:, 0] + s_sr_h2 = self._model.get_internal(id="H2_SR", attribute="state_array")[:, 0] + + msg = "Fail in the first half" + + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg=msg) + self.assertTrue(np.allclose(out_h1, self._superflex_output.iloc[:5, 1]), msg=msg) + self.assertTrue(np.allclose(s_fr_h1, self._superflex_output.iloc[:5, 3]), msg=msg) + self.assertTrue(np.allclose(out_h2, self._superflex_output.iloc[:5, 4]), msg=msg) + self.assertTrue(np.allclose(aet_h2, self._superflex_output.iloc[:5, 5]), msg=msg) + self.assertTrue(np.allclose(s_ur_h2, self._superflex_output.iloc[:5, 6]), msg=msg) + self.assertTrue(np.allclose(s_fr_h2, self._superflex_output.iloc[:5, 7]), msg=msg) + self.assertTrue(np.allclose(s_sr_h2, self._superflex_output.iloc[:5, 8]), msg=msg) # Second half of time series self._model.set_input([self._precipitation[5:], self._pet[5:]]) out = self._model.get_output() - out_h1 = self._model.call_internal(id = 'H1', method = 'get_output', solve = False) - s_fr_h1 = self._model.get_internal(id = 'H1_FR', attribute = 'state_array')[:, 0] - out_h2 = self._model.call_internal(id = 'H2', method = 'get_output', solve = False) - aet_h2 = self._model.call_internal(id = 'H2_UR', method = 'get_AET') - s_ur_h2 = self._model.get_internal(id = 'H2_UR', attribute = 'state_array')[:, 0] - s_fr_h2 = self._model.get_internal(id = 'H2_FR', attribute = 'state_array')[:, 0] - s_sr_h2 = self._model.get_internal(id = 'H2_SR', attribute = 'state_array')[:, 0] - - msg = 'Fail in the second half' - - self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg = msg) - self.assertTrue(np.allclose(out_h1, self._superflex_output.iloc[5:, 1]), msg = msg) - self.assertTrue(np.allclose(s_fr_h1, self._superflex_output.iloc[5:, 3]), msg = msg) - self.assertTrue(np.allclose(out_h2, self._superflex_output.iloc[5:, 4]), msg = msg) - self.assertTrue(np.allclose(aet_h2, self._superflex_output.iloc[5:, 5]), msg = msg) - self.assertTrue(np.allclose(s_ur_h2, self._superflex_output.iloc[5:, 6]), msg = msg) - self.assertTrue(np.allclose(s_fr_h2, self._superflex_output.iloc[5:, 7]), msg = msg) - self.assertTrue(np.allclose(s_sr_h2, self._superflex_output.iloc[5:, 8]), msg = msg) + out_h1 = self._model.call_internal(id="H1", method="get_output", solve=False) + s_fr_h1 = self._model.get_internal(id="H1_FR", attribute="state_array")[:, 0] + out_h2 = self._model.call_internal(id="H2", method="get_output", solve=False) + aet_h2 = self._model.call_internal(id="H2_UR", method="get_AET") + s_ur_h2 = self._model.get_internal(id="H2_UR", attribute="state_array")[:, 0] + s_fr_h2 = self._model.get_internal(id="H2_FR", attribute="state_array")[:, 0] + s_sr_h2 = self._model.get_internal(id="H2_SR", attribute="state_array")[:, 0] + + msg = "Fail in the second half" + + self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg=msg) + self.assertTrue(np.allclose(out_h1, self._superflex_output.iloc[5:, 1]), msg=msg) + self.assertTrue(np.allclose(s_fr_h1, self._superflex_output.iloc[5:, 3]), msg=msg) + self.assertTrue(np.allclose(out_h2, self._superflex_output.iloc[5:, 4]), msg=msg) + self.assertTrue(np.allclose(aet_h2, self._superflex_output.iloc[5:, 5]), msg=msg) + self.assertTrue(np.allclose(s_ur_h2, self._superflex_output.iloc[5:, 6]), msg=msg) + self.assertTrue(np.allclose(s_fr_h2, self._superflex_output.iloc[5:, 7]), msg=msg) + self.assertTrue(np.allclose(s_sr_h2, self._superflex_output.iloc[5:, 8]), msg=msg) def test_start_stop_python(self): - - self._test_start_stop(solver = 'python') + self._test_start_stop(solver="python") def test_start_stop_numba(self): - - self._test_start_stop(solver = 'numba') + self._test_start_stop(solver="numba") def _test_2_rounds(self, solver): - - self._init_model(solver = solver) + self._init_model(solver=solver) self._read_outputs() self._read_inputs() # First half of time series self._model.set_input([self._precipitation, self._pet]) out = self._model.get_output() - out_h1 = self._model.call_internal(id = 'H1', method = 'get_output', solve = False) - s_fr_h1 = self._model.get_internal(id = 'H1_FR', attribute = 'state_array')[:, 0] - out_h2 = self._model.call_internal(id = 'H2', method = 'get_output', solve = False) - aet_h2 = self._model.call_internal(id = 'H2_UR', method = 'get_AET') - s_ur_h2 = self._model.get_internal(id = 'H2_UR', attribute = 'state_array')[:, 0] - s_fr_h2 = self._model.get_internal(id = 'H2_FR', attribute = 'state_array')[:, 0] - s_sr_h2 = self._model.get_internal(id = 'H2_SR', attribute = 'state_array')[:, 0] - msg = 'Fail in the first round' - - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg = msg) - self.assertTrue(np.allclose(out_h1, self._superflex_output.iloc[:, 1]), msg = msg) - self.assertTrue(np.allclose(s_fr_h1, self._superflex_output.iloc[:, 3]), msg = msg) - self.assertTrue(np.allclose(out_h2, self._superflex_output.iloc[:, 4]), msg = msg) - self.assertTrue(np.allclose(aet_h2, self._superflex_output.iloc[:, 5]), msg = msg) - self.assertTrue(np.allclose(s_ur_h2, self._superflex_output.iloc[:, 6]), msg = msg) - self.assertTrue(np.allclose(s_fr_h2, self._superflex_output.iloc[:, 7]), msg = msg) - self.assertTrue(np.allclose(s_sr_h2, self._superflex_output.iloc[:, 8]), msg = msg) + out_h1 = self._model.call_internal(id="H1", method="get_output", solve=False) + s_fr_h1 = self._model.get_internal(id="H1_FR", attribute="state_array")[:, 0] + out_h2 = self._model.call_internal(id="H2", method="get_output", solve=False) + aet_h2 = self._model.call_internal(id="H2_UR", method="get_AET") + s_ur_h2 = self._model.get_internal(id="H2_UR", attribute="state_array")[:, 0] + s_fr_h2 = self._model.get_internal(id="H2_FR", attribute="state_array")[:, 0] + s_sr_h2 = self._model.get_internal(id="H2_SR", attribute="state_array")[:, 0] + msg = "Fail in the first round" + + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) + self.assertTrue(np.allclose(out_h1, self._superflex_output.iloc[:, 1]), msg=msg) + self.assertTrue(np.allclose(s_fr_h1, self._superflex_output.iloc[:, 3]), msg=msg) + self.assertTrue(np.allclose(out_h2, self._superflex_output.iloc[:, 4]), msg=msg) + self.assertTrue(np.allclose(aet_h2, self._superflex_output.iloc[:, 5]), msg=msg) + self.assertTrue(np.allclose(s_ur_h2, self._superflex_output.iloc[:, 6]), msg=msg) + self.assertTrue(np.allclose(s_fr_h2, self._superflex_output.iloc[:, 7]), msg=msg) + self.assertTrue(np.allclose(s_sr_h2, self._superflex_output.iloc[:, 8]), msg=msg) # Second half of time series self._model.reset_states() out = self._model.get_output() - out_h1 = self._model.call_internal(id = 'H1', method = 'get_output', solve = False) - s_fr_h1 = self._model.get_internal(id = 'H1_FR', attribute = 'state_array')[:, 0] - out_h2 = self._model.call_internal(id = 'H2', method = 'get_output', solve = False) - aet_h2 = self._model.call_internal(id = 'H2_UR', method = 'get_AET') - s_ur_h2 = self._model.get_internal(id = 'H2_UR', attribute = 'state_array')[:, 0] - s_fr_h2 = self._model.get_internal(id = 'H2_FR', attribute = 'state_array')[:, 0] - s_sr_h2 = self._model.get_internal(id = 'H2_SR', attribute = 'state_array')[:, 0] - - msg = 'Fail in the second round' - - self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg = msg) - self.assertTrue(np.allclose(out_h1, self._superflex_output.iloc[:, 1]), msg = msg) - self.assertTrue(np.allclose(s_fr_h1, self._superflex_output.iloc[:, 3]), msg = msg) - self.assertTrue(np.allclose(out_h2, self._superflex_output.iloc[:, 4]), msg = msg) - self.assertTrue(np.allclose(aet_h2, self._superflex_output.iloc[:, 5]), msg = msg) - self.assertTrue(np.allclose(s_ur_h2, self._superflex_output.iloc[:, 6]), msg = msg) - self.assertTrue(np.allclose(s_fr_h2, self._superflex_output.iloc[:, 7]), msg = msg) - self.assertTrue(np.allclose(s_sr_h2, self._superflex_output.iloc[:, 8]), msg = msg) + out_h1 = self._model.call_internal(id="H1", method="get_output", solve=False) + s_fr_h1 = self._model.get_internal(id="H1_FR", attribute="state_array")[:, 0] + out_h2 = self._model.call_internal(id="H2", method="get_output", solve=False) + aet_h2 = self._model.call_internal(id="H2_UR", method="get_AET") + s_ur_h2 = self._model.get_internal(id="H2_UR", attribute="state_array")[:, 0] + s_fr_h2 = self._model.get_internal(id="H2_FR", attribute="state_array")[:, 0] + s_sr_h2 = self._model.get_internal(id="H2_SR", attribute="state_array")[:, 0] + + msg = "Fail in the second round" + + self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) + self.assertTrue(np.allclose(out_h1, self._superflex_output.iloc[:, 1]), msg=msg) + self.assertTrue(np.allclose(s_fr_h1, self._superflex_output.iloc[:, 3]), msg=msg) + self.assertTrue(np.allclose(out_h2, self._superflex_output.iloc[:, 4]), msg=msg) + self.assertTrue(np.allclose(aet_h2, self._superflex_output.iloc[:, 5]), msg=msg) + self.assertTrue(np.allclose(s_ur_h2, self._superflex_output.iloc[:, 6]), msg=msg) + self.assertTrue(np.allclose(s_fr_h2, self._superflex_output.iloc[:, 7]), msg=msg) + self.assertTrue(np.allclose(s_sr_h2, self._superflex_output.iloc[:, 8]), msg=msg) def test_2_rounds_python(self): - - self._test_2_rounds(solver = 'python') + self._test_2_rounds(solver="python") def test_2_rounds_numba(self): + self._test_2_rounds(solver="numba") - self._test_2_rounds(solver = 'numba') if __name__ == "__main__": unittest.main() # test = TestStructureElements() - # test.test_start_stop_python() \ No newline at end of file + # test.test_start_stop_python() diff --git a/test/unittest/06_3Cats_2HRUs.py b/test/unittest/06_3Cats_2HRUs.py index 42d7015..0267a16 100644 --- a/test/unittest/06_3Cats_2HRUs.py +++ b/test/unittest/06_3Cats_2HRUs.py @@ -23,24 +23,28 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -import unittest import sys -from os.path import abspath, join, dirname +import unittest +from os.path import abspath, dirname, join -import pandas as pd import numpy as np +import pandas as pd # Package path is 2 levels above this file -package_path = join(abspath(dirname(__file__)), '..', '..') +package_path = join(abspath(dirname(__file__)), "..", "..") sys.path.insert(0, package_path) +from superflexpy.framework.network import Network +from superflexpy.framework.node import Node +from superflexpy.framework.unit import Unit from superflexpy.implementation.elements.hbv import PowerReservoir, UnsaturatedReservoir +from superflexpy.implementation.elements.structure_elements import Junction, Splitter +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerNumba, + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusNumba, PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerNumba, ImplicitEulerPython -from superflexpy.implementation.elements.structure_elements import Splitter, Junction -from superflexpy.framework.unit import Unit -from superflexpy.framework.node import Node -from superflexpy.framework.network import Network + class TestStructureElements(unittest.TestCase): """ @@ -51,72 +55,55 @@ class TestStructureElements(unittest.TestCase): """ def _init_model(self, solver): - - if solver == 'numba': + if solver == "numba": solver = PegasusNumba() num_app = ImplicitEulerNumba(root_finder=solver) - elif solver == 'python': + elif solver == "python": solver = PegasusPython() num_app = ImplicitEulerPython(root_finder=solver) # Define HRU 1 (40%) - fr = PowerReservoir(parameters = {'k' : 0.01, - 'alpha' : 2.5}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'FR') + fr = PowerReservoir(parameters={"k": 0.01, "alpha": 2.5}, states={"S0": 0.0}, approximation=num_app, id="FR") - h1 = Unit(layers = [[fr]], id = 'H1') + h1 = Unit(layers=[[fr]], id="H1") # Define HRU 2 (60%) - fr = PowerReservoir(parameters = {'k' : 0.01, - 'alpha' : 2.5}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'FR') - - sr = PowerReservoir(parameters = {'k' : 1e-4, - 'alpha' : 1.0}, - states = {'S0' : 0.0}, - approximation=num_app, - id = 'SR') - - ur = UnsaturatedReservoir(parameters = {'Smax' : 50.0, - 'Ce' : 1.5, - 'm' : 0.01, - 'beta' : 1.5, - }, - states = {'S0' : 0.2*50.0, 'PET' : None}, - approximation=num_app, - id = 'UR') - - s = Splitter(weight = [[0.3], [0.7]], - direction = [[0], [0]], - id = 'S') - - j = Junction(direction = [[0, 0]], - id = 'J') - - h2 = Unit(layers = [ - [ur], - [s], - [fr, sr], - [j] - ], - id = 'H2') + fr = PowerReservoir(parameters={"k": 0.01, "alpha": 2.5}, states={"S0": 0.0}, approximation=num_app, id="FR") + + sr = PowerReservoir(parameters={"k": 1e-4, "alpha": 1.0}, states={"S0": 0.0}, approximation=num_app, id="SR") + + ur = UnsaturatedReservoir( + parameters={ + "Smax": 50.0, + "Ce": 1.5, + "m": 0.01, + "beta": 1.5, + }, + states={"S0": 0.2 * 50.0, "PET": None}, + approximation=num_app, + id="UR", + ) + + s = Splitter(weight=[[0.3], [0.7]], direction=[[0], [0]], id="S") + + j = Junction(direction=[[0, 0]], id="J") + + h2 = Unit(layers=[[ur], [s], [fr, sr], [j]], id="H2") # Define the catchment - cat1 = Node(units = [h1, h2], weights = [0.25, 0.75], area = 10.0, id = 'Cat1') - cat2 = Node(units = [h1, h2], weights = [0.4, 0.6], area = 20.0, id = 'Cat2') - cat3 = Node(units = [h1, h2], weights = [0.8, 0.2], area = 30.0, id = 'Cat3') + cat1 = Node(units=[h1, h2], weights=[0.25, 0.75], area=10.0, id="Cat1") + cat2 = Node(units=[h1, h2], weights=[0.4, 0.6], area=20.0, id="Cat2") + cat3 = Node(units=[h1, h2], weights=[0.8, 0.2], area=30.0, id="Cat3") # Define the network - net = Network(nodes = [cat1, cat2, cat3], - topology = { - 'Cat1' : 'Cat3', - 'Cat2' : 'Cat3', - 'Cat3' : None, - }) + net = Network( + nodes=[cat1, cat2, cat3], + topology={ + "Cat1": "Cat3", + "Cat2": "Cat3", + "Cat3": None, + }, + ) net.set_timestep(1.0) self._cat1 = cat1 self._cat2 = cat2 @@ -124,21 +111,26 @@ def _init_model(self, solver): self._model = net def _read_inputs(self): - - data = pd.read_csv('{}/test/reference_results/06_3Cats_2HRUs/input.dat'.format(package_path), header = 6, sep = '\s+|,\s+|,', engine = 'python') - self._precipitation_c1 = data.iloc[:,5].values - self._precipitation_c2 = data.iloc[:,6].values - self._precipitation_c3 = data.iloc[:,7].values - self._pet_c1 = data.iloc[:,8].values - self._pet_c2 = data.iloc[:,9].values - self._pet_c3 = data.iloc[:,10].values + data = pd.read_csv( + "{}/test/reference_results/06_3Cats_2HRUs/input.dat".format(package_path), + header=6, + sep="\s+|,\s+|,", + engine="python", + ) + self._precipitation_c1 = data.iloc[:, 5].values + self._precipitation_c2 = data.iloc[:, 6].values + self._precipitation_c3 = data.iloc[:, 7].values + self._pet_c1 = data.iloc[:, 8].values + self._pet_c2 = data.iloc[:, 9].values + self._pet_c3 = data.iloc[:, 10].values def _read_outputs(self): - self._superflex_output = pd.read_csv('{}/test/reference_results/06_3Cats_2HRUs/Results.csv'.format(package_path)) + self._superflex_output = pd.read_csv( + "{}/test/reference_results/06_3Cats_2HRUs/Results.csv".format(package_path) + ) def _test_start_stop(self, solver): - - self._init_model(solver = solver) + self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -150,133 +142,130 @@ def _test_start_stop(self, solver): out = self._model.get_output() # Catchment_1 - cat_1_out_h1 = self._model.call_internal(id = 'Cat1_H1', method = 'get_output', solve = False) - cat_1_s_fr_h1 = self._model.get_internal(id = 'Cat1_H1_FR', attribute = 'state_array')[:, 0] - cat_1_out_h2 = self._model.call_internal(id = 'Cat1_H2', method = 'get_output', solve = False) - cat_1_aet_h2 = self._model.call_internal(id = 'Cat1_H2_UR', method = 'get_AET') - cat_1_s_ur_h2 = self._model.get_internal(id = 'Cat1_H2_UR', attribute = 'state_array')[:, 0] - cat_1_s_fr_h2 = self._model.get_internal(id = 'Cat1_H2_FR', attribute = 'state_array')[:, 0] - cat_1_s_sr_h2 = self._model.get_internal(id = 'Cat1_H2_SR', attribute = 'state_array')[:, 0] + cat_1_out_h1 = self._model.call_internal(id="Cat1_H1", method="get_output", solve=False) + cat_1_s_fr_h1 = self._model.get_internal(id="Cat1_H1_FR", attribute="state_array")[:, 0] + cat_1_out_h2 = self._model.call_internal(id="Cat1_H2", method="get_output", solve=False) + cat_1_aet_h2 = self._model.call_internal(id="Cat1_H2_UR", method="get_AET") + cat_1_s_ur_h2 = self._model.get_internal(id="Cat1_H2_UR", attribute="state_array")[:, 0] + cat_1_s_fr_h2 = self._model.get_internal(id="Cat1_H2_FR", attribute="state_array")[:, 0] + cat_1_s_sr_h2 = self._model.get_internal(id="Cat1_H2_SR", attribute="state_array")[:, 0] # Catchment_2 - cat_2_out_h1 = self._model.call_internal(id = 'Cat2_H1', method = 'get_output', solve = False) - cat_2_s_fr_h1 = self._model.get_internal(id = 'Cat2_H1_FR', attribute = 'state_array')[:, 0] - cat_2_out_h2 = self._model.call_internal(id = 'Cat2_H2', method = 'get_output', solve = False) - cat_2_aet_h2 = self._model.call_internal(id = 'Cat2_H2_UR', method = 'get_AET') - cat_2_s_ur_h2 = self._model.get_internal(id = 'Cat2_H2_UR', attribute = 'state_array')[:, 0] - cat_2_s_fr_h2 = self._model.get_internal(id = 'Cat2_H2_FR', attribute = 'state_array')[:, 0] - cat_2_s_sr_h2 = self._model.get_internal(id = 'Cat2_H2_SR', attribute = 'state_array')[:, 0] + cat_2_out_h1 = self._model.call_internal(id="Cat2_H1", method="get_output", solve=False) + cat_2_s_fr_h1 = self._model.get_internal(id="Cat2_H1_FR", attribute="state_array")[:, 0] + cat_2_out_h2 = self._model.call_internal(id="Cat2_H2", method="get_output", solve=False) + cat_2_aet_h2 = self._model.call_internal(id="Cat2_H2_UR", method="get_AET") + cat_2_s_ur_h2 = self._model.get_internal(id="Cat2_H2_UR", attribute="state_array")[:, 0] + cat_2_s_fr_h2 = self._model.get_internal(id="Cat2_H2_FR", attribute="state_array")[:, 0] + cat_2_s_sr_h2 = self._model.get_internal(id="Cat2_H2_SR", attribute="state_array")[:, 0] # Catchment_3 - cat_3_out_h1 = self._model.call_internal(id = 'Cat3_H1', method = 'get_output', solve = False) - cat_3_s_fr_h1 = self._model.get_internal(id = 'Cat3_H1_FR', attribute = 'state_array')[:, 0] - cat_3_out_h2 = self._model.call_internal(id = 'Cat3_H2', method = 'get_output', solve = False) - cat_3_aet_h2 = self._model.call_internal(id = 'Cat3_H2_UR', method = 'get_AET') - cat_3_s_ur_h2 = self._model.get_internal(id = 'Cat3_H2_UR', attribute = 'state_array')[:, 0] - cat_3_s_fr_h2 = self._model.get_internal(id = 'Cat3_H2_FR', attribute = 'state_array')[:, 0] - cat_3_s_sr_h2 = self._model.get_internal(id = 'Cat3_H2_SR', attribute = 'state_array')[:, 0] - - msg = 'Fail in the second round' - - self.assertTrue(np.allclose(out['Cat1'], self._superflex_output.iloc[:5, 0]), msg = msg) - self.assertTrue(np.allclose(out['Cat2'], self._superflex_output.iloc[:5, 1]), msg = msg) - self.assertTrue(np.allclose(out['Cat3'], self._superflex_output.iloc[:5, 2]), msg = msg) - self.assertTrue(np.allclose(cat_1_out_h1, self._superflex_output.iloc[:5, 3]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_fr_h1, self._superflex_output.iloc[:5, 4]), msg = msg) - self.assertTrue(np.allclose(cat_2_out_h1, self._superflex_output.iloc[:5, 5]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_fr_h1, self._superflex_output.iloc[:5, 6]), msg = msg) - self.assertTrue(np.allclose(cat_3_out_h1, self._superflex_output.iloc[:5, 7]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_fr_h1, self._superflex_output.iloc[:5, 8]), msg = msg) - self.assertTrue(np.allclose(cat_1_out_h2, self._superflex_output.iloc[:5, 9]), msg = msg) - self.assertTrue(np.allclose(cat_1_aet_h2, self._superflex_output.iloc[:5, 10]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_ur_h2, self._superflex_output.iloc[:5, 11]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_fr_h2, self._superflex_output.iloc[:5, 12]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_sr_h2, self._superflex_output.iloc[:5, 13]), msg = msg) - self.assertTrue(np.allclose(cat_2_out_h2, self._superflex_output.iloc[:5, 14]), msg = msg) - self.assertTrue(np.allclose(cat_2_aet_h2, self._superflex_output.iloc[:5, 15]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_ur_h2, self._superflex_output.iloc[:5, 16]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_fr_h2, self._superflex_output.iloc[:5, 17]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_sr_h2, self._superflex_output.iloc[:5, 18]), msg = msg) - self.assertTrue(np.allclose(cat_3_out_h2, self._superflex_output.iloc[:5, 19]), msg = msg) - self.assertTrue(np.allclose(cat_3_aet_h2, self._superflex_output.iloc[:5, 20]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_ur_h2, self._superflex_output.iloc[:5, 21]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_fr_h2, self._superflex_output.iloc[:5, 22]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_sr_h2, self._superflex_output.iloc[:5, 23]), msg = msg) + cat_3_out_h1 = self._model.call_internal(id="Cat3_H1", method="get_output", solve=False) + cat_3_s_fr_h1 = self._model.get_internal(id="Cat3_H1_FR", attribute="state_array")[:, 0] + cat_3_out_h2 = self._model.call_internal(id="Cat3_H2", method="get_output", solve=False) + cat_3_aet_h2 = self._model.call_internal(id="Cat3_H2_UR", method="get_AET") + cat_3_s_ur_h2 = self._model.get_internal(id="Cat3_H2_UR", attribute="state_array")[:, 0] + cat_3_s_fr_h2 = self._model.get_internal(id="Cat3_H2_FR", attribute="state_array")[:, 0] + cat_3_s_sr_h2 = self._model.get_internal(id="Cat3_H2_SR", attribute="state_array")[:, 0] + + msg = "Fail in the second round" + + self.assertTrue(np.allclose(out["Cat1"], self._superflex_output.iloc[:5, 0]), msg=msg) + self.assertTrue(np.allclose(out["Cat2"], self._superflex_output.iloc[:5, 1]), msg=msg) + self.assertTrue(np.allclose(out["Cat3"], self._superflex_output.iloc[:5, 2]), msg=msg) + self.assertTrue(np.allclose(cat_1_out_h1, self._superflex_output.iloc[:5, 3]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_fr_h1, self._superflex_output.iloc[:5, 4]), msg=msg) + self.assertTrue(np.allclose(cat_2_out_h1, self._superflex_output.iloc[:5, 5]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_fr_h1, self._superflex_output.iloc[:5, 6]), msg=msg) + self.assertTrue(np.allclose(cat_3_out_h1, self._superflex_output.iloc[:5, 7]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_fr_h1, self._superflex_output.iloc[:5, 8]), msg=msg) + self.assertTrue(np.allclose(cat_1_out_h2, self._superflex_output.iloc[:5, 9]), msg=msg) + self.assertTrue(np.allclose(cat_1_aet_h2, self._superflex_output.iloc[:5, 10]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_ur_h2, self._superflex_output.iloc[:5, 11]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_fr_h2, self._superflex_output.iloc[:5, 12]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_sr_h2, self._superflex_output.iloc[:5, 13]), msg=msg) + self.assertTrue(np.allclose(cat_2_out_h2, self._superflex_output.iloc[:5, 14]), msg=msg) + self.assertTrue(np.allclose(cat_2_aet_h2, self._superflex_output.iloc[:5, 15]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_ur_h2, self._superflex_output.iloc[:5, 16]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_fr_h2, self._superflex_output.iloc[:5, 17]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_sr_h2, self._superflex_output.iloc[:5, 18]), msg=msg) + self.assertTrue(np.allclose(cat_3_out_h2, self._superflex_output.iloc[:5, 19]), msg=msg) + self.assertTrue(np.allclose(cat_3_aet_h2, self._superflex_output.iloc[:5, 20]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_ur_h2, self._superflex_output.iloc[:5, 21]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_fr_h2, self._superflex_output.iloc[:5, 22]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_sr_h2, self._superflex_output.iloc[:5, 23]), msg=msg) # Second half of time series - self._model.set_states({'Cat1_H2_UR_PET' : self._pet_c1[5:]}) - self._model.set_states({'Cat2_H2_UR_PET' : self._pet_c2[5:]}) - self._model.set_states({'Cat3_H2_UR_PET' : self._pet_c3[5:]}) + self._model.set_states({"Cat1_H2_UR_PET": self._pet_c1[5:]}) + self._model.set_states({"Cat2_H2_UR_PET": self._pet_c2[5:]}) + self._model.set_states({"Cat3_H2_UR_PET": self._pet_c3[5:]}) self._cat1.set_input([self._precipitation_c1[5:], self._pet_c1[5:]]) self._cat2.set_input([self._precipitation_c2[5:], self._pet_c2[5:]]) self._cat3.set_input([self._precipitation_c3[5:], self._pet_c3[5:]]) out = self._model.get_output() # Catchment_1 - cat_1_out_h1 = self._model.call_internal(id = 'Cat1_H1', method = 'get_output', solve = False) - cat_1_s_fr_h1 = self._model.get_internal(id = 'Cat1_H1_FR', attribute = 'state_array')[:, 0] - cat_1_out_h2 = self._model.call_internal(id = 'Cat1_H2', method = 'get_output', solve = False) - cat_1_aet_h2 = self._model.call_internal(id = 'Cat1_H2_UR', method = 'get_AET') - cat_1_s_ur_h2 = self._model.get_internal(id = 'Cat1_H2_UR', attribute = 'state_array')[:, 0] - cat_1_s_fr_h2 = self._model.get_internal(id = 'Cat1_H2_FR', attribute = 'state_array')[:, 0] - cat_1_s_sr_h2 = self._model.get_internal(id = 'Cat1_H2_SR', attribute = 'state_array')[:, 0] + cat_1_out_h1 = self._model.call_internal(id="Cat1_H1", method="get_output", solve=False) + cat_1_s_fr_h1 = self._model.get_internal(id="Cat1_H1_FR", attribute="state_array")[:, 0] + cat_1_out_h2 = self._model.call_internal(id="Cat1_H2", method="get_output", solve=False) + cat_1_aet_h2 = self._model.call_internal(id="Cat1_H2_UR", method="get_AET") + cat_1_s_ur_h2 = self._model.get_internal(id="Cat1_H2_UR", attribute="state_array")[:, 0] + cat_1_s_fr_h2 = self._model.get_internal(id="Cat1_H2_FR", attribute="state_array")[:, 0] + cat_1_s_sr_h2 = self._model.get_internal(id="Cat1_H2_SR", attribute="state_array")[:, 0] # Catchment_2 - cat_2_out_h1 = self._model.call_internal(id = 'Cat2_H1', method = 'get_output', solve = False) - cat_2_s_fr_h1 = self._model.get_internal(id = 'Cat2_H1_FR', attribute = 'state_array')[:, 0] - cat_2_out_h2 = self._model.call_internal(id = 'Cat2_H2', method = 'get_output', solve = False) - cat_2_aet_h2 = self._model.call_internal(id = 'Cat2_H2_UR', method = 'get_AET') - cat_2_s_ur_h2 = self._model.get_internal(id = 'Cat2_H2_UR', attribute = 'state_array')[:, 0] - cat_2_s_fr_h2 = self._model.get_internal(id = 'Cat2_H2_FR', attribute = 'state_array')[:, 0] - cat_2_s_sr_h2 = self._model.get_internal(id = 'Cat2_H2_SR', attribute = 'state_array')[:, 0] + cat_2_out_h1 = self._model.call_internal(id="Cat2_H1", method="get_output", solve=False) + cat_2_s_fr_h1 = self._model.get_internal(id="Cat2_H1_FR", attribute="state_array")[:, 0] + cat_2_out_h2 = self._model.call_internal(id="Cat2_H2", method="get_output", solve=False) + cat_2_aet_h2 = self._model.call_internal(id="Cat2_H2_UR", method="get_AET") + cat_2_s_ur_h2 = self._model.get_internal(id="Cat2_H2_UR", attribute="state_array")[:, 0] + cat_2_s_fr_h2 = self._model.get_internal(id="Cat2_H2_FR", attribute="state_array")[:, 0] + cat_2_s_sr_h2 = self._model.get_internal(id="Cat2_H2_SR", attribute="state_array")[:, 0] # Catchment_3 - cat_3_out_h1 = self._model.call_internal(id = 'Cat3_H1', method = 'get_output', solve = False) - cat_3_s_fr_h1 = self._model.get_internal(id = 'Cat3_H1_FR', attribute = 'state_array')[:, 0] - cat_3_out_h2 = self._model.call_internal(id = 'Cat3_H2', method = 'get_output', solve = False) - cat_3_aet_h2 = self._model.call_internal(id = 'Cat3_H2_UR', method = 'get_AET') - cat_3_s_ur_h2 = self._model.get_internal(id = 'Cat3_H2_UR', attribute = 'state_array')[:, 0] - cat_3_s_fr_h2 = self._model.get_internal(id = 'Cat3_H2_FR', attribute = 'state_array')[:, 0] - cat_3_s_sr_h2 = self._model.get_internal(id = 'Cat3_H2_SR', attribute = 'state_array')[:, 0] - - msg = 'Fail in the second round' - - self.assertTrue(np.allclose(out['Cat1'], self._superflex_output.iloc[5:, 0]), msg = msg) - self.assertTrue(np.allclose(out['Cat2'], self._superflex_output.iloc[5:, 1]), msg = msg) - self.assertTrue(np.allclose(out['Cat3'], self._superflex_output.iloc[5:, 2]), msg = msg) - self.assertTrue(np.allclose(cat_1_out_h1, self._superflex_output.iloc[5:, 3]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_fr_h1, self._superflex_output.iloc[5:, 4]), msg = msg) - self.assertTrue(np.allclose(cat_2_out_h1, self._superflex_output.iloc[5:, 5]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_fr_h1, self._superflex_output.iloc[5:, 6]), msg = msg) - self.assertTrue(np.allclose(cat_3_out_h1, self._superflex_output.iloc[5:, 7]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_fr_h1, self._superflex_output.iloc[5:, 8]), msg = msg) - self.assertTrue(np.allclose(cat_1_out_h2, self._superflex_output.iloc[5:, 9]), msg = msg) - self.assertTrue(np.allclose(cat_1_aet_h2, self._superflex_output.iloc[5:, 10]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_ur_h2, self._superflex_output.iloc[5:, 11]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_fr_h2, self._superflex_output.iloc[5:, 12]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_sr_h2, self._superflex_output.iloc[5:, 13]), msg = msg) - self.assertTrue(np.allclose(cat_2_out_h2, self._superflex_output.iloc[5:, 14]), msg = msg) - self.assertTrue(np.allclose(cat_2_aet_h2, self._superflex_output.iloc[5:, 15]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_ur_h2, self._superflex_output.iloc[5:, 16]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_fr_h2, self._superflex_output.iloc[5:, 17]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_sr_h2, self._superflex_output.iloc[5:, 18]), msg = msg) - self.assertTrue(np.allclose(cat_3_out_h2, self._superflex_output.iloc[5:, 19]), msg = msg) - self.assertTrue(np.allclose(cat_3_aet_h2, self._superflex_output.iloc[5:, 20]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_ur_h2, self._superflex_output.iloc[5:, 21]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_fr_h2, self._superflex_output.iloc[5:, 22]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_sr_h2, self._superflex_output.iloc[5:, 23]), msg = msg) + cat_3_out_h1 = self._model.call_internal(id="Cat3_H1", method="get_output", solve=False) + cat_3_s_fr_h1 = self._model.get_internal(id="Cat3_H1_FR", attribute="state_array")[:, 0] + cat_3_out_h2 = self._model.call_internal(id="Cat3_H2", method="get_output", solve=False) + cat_3_aet_h2 = self._model.call_internal(id="Cat3_H2_UR", method="get_AET") + cat_3_s_ur_h2 = self._model.get_internal(id="Cat3_H2_UR", attribute="state_array")[:, 0] + cat_3_s_fr_h2 = self._model.get_internal(id="Cat3_H2_FR", attribute="state_array")[:, 0] + cat_3_s_sr_h2 = self._model.get_internal(id="Cat3_H2_SR", attribute="state_array")[:, 0] + + msg = "Fail in the second round" + + self.assertTrue(np.allclose(out["Cat1"], self._superflex_output.iloc[5:, 0]), msg=msg) + self.assertTrue(np.allclose(out["Cat2"], self._superflex_output.iloc[5:, 1]), msg=msg) + self.assertTrue(np.allclose(out["Cat3"], self._superflex_output.iloc[5:, 2]), msg=msg) + self.assertTrue(np.allclose(cat_1_out_h1, self._superflex_output.iloc[5:, 3]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_fr_h1, self._superflex_output.iloc[5:, 4]), msg=msg) + self.assertTrue(np.allclose(cat_2_out_h1, self._superflex_output.iloc[5:, 5]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_fr_h1, self._superflex_output.iloc[5:, 6]), msg=msg) + self.assertTrue(np.allclose(cat_3_out_h1, self._superflex_output.iloc[5:, 7]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_fr_h1, self._superflex_output.iloc[5:, 8]), msg=msg) + self.assertTrue(np.allclose(cat_1_out_h2, self._superflex_output.iloc[5:, 9]), msg=msg) + self.assertTrue(np.allclose(cat_1_aet_h2, self._superflex_output.iloc[5:, 10]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_ur_h2, self._superflex_output.iloc[5:, 11]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_fr_h2, self._superflex_output.iloc[5:, 12]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_sr_h2, self._superflex_output.iloc[5:, 13]), msg=msg) + self.assertTrue(np.allclose(cat_2_out_h2, self._superflex_output.iloc[5:, 14]), msg=msg) + self.assertTrue(np.allclose(cat_2_aet_h2, self._superflex_output.iloc[5:, 15]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_ur_h2, self._superflex_output.iloc[5:, 16]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_fr_h2, self._superflex_output.iloc[5:, 17]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_sr_h2, self._superflex_output.iloc[5:, 18]), msg=msg) + self.assertTrue(np.allclose(cat_3_out_h2, self._superflex_output.iloc[5:, 19]), msg=msg) + self.assertTrue(np.allclose(cat_3_aet_h2, self._superflex_output.iloc[5:, 20]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_ur_h2, self._superflex_output.iloc[5:, 21]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_fr_h2, self._superflex_output.iloc[5:, 22]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_sr_h2, self._superflex_output.iloc[5:, 23]), msg=msg) def test_start_stop_python(self): - - self._test_start_stop(solver = 'python') + self._test_start_stop(solver="python") def test_start_stop_numba(self): - - self._test_start_stop(solver = 'numba') + self._test_start_stop(solver="numba") def _test_2_rounds(self, solver): - - self._init_model(solver = solver) + self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -287,127 +276,126 @@ def _test_2_rounds(self, solver): out = self._model.get_output() # Catchment_1 - cat_1_out_h1 = self._model.call_internal(id = 'Cat1_H1', method = 'get_output', solve = False) - cat_1_s_fr_h1 = self._model.get_internal(id = 'Cat1_H1_FR', attribute = 'state_array')[:, 0] - cat_1_out_h2 = self._model.call_internal(id = 'Cat1_H2', method = 'get_output', solve = False) - cat_1_aet_h2 = self._model.call_internal(id = 'Cat1_H2_UR', method = 'get_AET') - cat_1_s_ur_h2 = self._model.get_internal(id = 'Cat1_H2_UR', attribute = 'state_array')[:, 0] - cat_1_s_fr_h2 = self._model.get_internal(id = 'Cat1_H2_FR', attribute = 'state_array')[:, 0] - cat_1_s_sr_h2 = self._model.get_internal(id = 'Cat1_H2_SR', attribute = 'state_array')[:, 0] + cat_1_out_h1 = self._model.call_internal(id="Cat1_H1", method="get_output", solve=False) + cat_1_s_fr_h1 = self._model.get_internal(id="Cat1_H1_FR", attribute="state_array")[:, 0] + cat_1_out_h2 = self._model.call_internal(id="Cat1_H2", method="get_output", solve=False) + cat_1_aet_h2 = self._model.call_internal(id="Cat1_H2_UR", method="get_AET") + cat_1_s_ur_h2 = self._model.get_internal(id="Cat1_H2_UR", attribute="state_array")[:, 0] + cat_1_s_fr_h2 = self._model.get_internal(id="Cat1_H2_FR", attribute="state_array")[:, 0] + cat_1_s_sr_h2 = self._model.get_internal(id="Cat1_H2_SR", attribute="state_array")[:, 0] # Catchment_2 - cat_2_out_h1 = self._model.call_internal(id = 'Cat2_H1', method = 'get_output', solve = False) - cat_2_s_fr_h1 = self._model.get_internal(id = 'Cat2_H1_FR', attribute = 'state_array')[:, 0] - cat_2_out_h2 = self._model.call_internal(id = 'Cat2_H2', method = 'get_output', solve = False) - cat_2_aet_h2 = self._model.call_internal(id = 'Cat2_H2_UR', method = 'get_AET') - cat_2_s_ur_h2 = self._model.get_internal(id = 'Cat2_H2_UR', attribute = 'state_array')[:, 0] - cat_2_s_fr_h2 = self._model.get_internal(id = 'Cat2_H2_FR', attribute = 'state_array')[:, 0] - cat_2_s_sr_h2 = self._model.get_internal(id = 'Cat2_H2_SR', attribute = 'state_array')[:, 0] + cat_2_out_h1 = self._model.call_internal(id="Cat2_H1", method="get_output", solve=False) + cat_2_s_fr_h1 = self._model.get_internal(id="Cat2_H1_FR", attribute="state_array")[:, 0] + cat_2_out_h2 = self._model.call_internal(id="Cat2_H2", method="get_output", solve=False) + cat_2_aet_h2 = self._model.call_internal(id="Cat2_H2_UR", method="get_AET") + cat_2_s_ur_h2 = self._model.get_internal(id="Cat2_H2_UR", attribute="state_array")[:, 0] + cat_2_s_fr_h2 = self._model.get_internal(id="Cat2_H2_FR", attribute="state_array")[:, 0] + cat_2_s_sr_h2 = self._model.get_internal(id="Cat2_H2_SR", attribute="state_array")[:, 0] # Catchment_3 - cat_3_out_h1 = self._model.call_internal(id = 'Cat3_H1', method = 'get_output', solve = False) - cat_3_s_fr_h1 = self._model.get_internal(id = 'Cat3_H1_FR', attribute = 'state_array')[:, 0] - cat_3_out_h2 = self._model.call_internal(id = 'Cat3_H2', method = 'get_output', solve = False) - cat_3_aet_h2 = self._model.call_internal(id = 'Cat3_H2_UR', method = 'get_AET') - cat_3_s_ur_h2 = self._model.get_internal(id = 'Cat3_H2_UR', attribute = 'state_array')[:, 0] - cat_3_s_fr_h2 = self._model.get_internal(id = 'Cat3_H2_FR', attribute = 'state_array')[:, 0] - cat_3_s_sr_h2 = self._model.get_internal(id = 'Cat3_H2_SR', attribute = 'state_array')[:, 0] - - msg = 'Fail in the second round' - - self.assertTrue(np.allclose(out['Cat1'], self._superflex_output.iloc[:, 0]), msg = msg) - self.assertTrue(np.allclose(out['Cat2'], self._superflex_output.iloc[:, 1]), msg = msg) - self.assertTrue(np.allclose(out['Cat3'], self._superflex_output.iloc[:, 2]), msg = msg) - self.assertTrue(np.allclose(cat_1_out_h1, self._superflex_output.iloc[:, 3]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_fr_h1, self._superflex_output.iloc[:, 4]), msg = msg) - self.assertTrue(np.allclose(cat_2_out_h1, self._superflex_output.iloc[:, 5]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_fr_h1, self._superflex_output.iloc[:, 6]), msg = msg) - self.assertTrue(np.allclose(cat_3_out_h1, self._superflex_output.iloc[:, 7]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_fr_h1, self._superflex_output.iloc[:, 8]), msg = msg) - self.assertTrue(np.allclose(cat_1_out_h2, self._superflex_output.iloc[:, 9]), msg = msg) - self.assertTrue(np.allclose(cat_1_aet_h2, self._superflex_output.iloc[:, 10]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_ur_h2, self._superflex_output.iloc[:, 11]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_fr_h2, self._superflex_output.iloc[:, 12]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_sr_h2, self._superflex_output.iloc[:, 13]), msg = msg) - self.assertTrue(np.allclose(cat_2_out_h2, self._superflex_output.iloc[:, 14]), msg = msg) - self.assertTrue(np.allclose(cat_2_aet_h2, self._superflex_output.iloc[:, 15]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_ur_h2, self._superflex_output.iloc[:, 16]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_fr_h2, self._superflex_output.iloc[:, 17]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_sr_h2, self._superflex_output.iloc[:, 18]), msg = msg) - self.assertTrue(np.allclose(cat_3_out_h2, self._superflex_output.iloc[:, 19]), msg = msg) - self.assertTrue(np.allclose(cat_3_aet_h2, self._superflex_output.iloc[:, 20]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_ur_h2, self._superflex_output.iloc[:, 21]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_fr_h2, self._superflex_output.iloc[:, 22]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_sr_h2, self._superflex_output.iloc[:, 23]), msg = msg) + cat_3_out_h1 = self._model.call_internal(id="Cat3_H1", method="get_output", solve=False) + cat_3_s_fr_h1 = self._model.get_internal(id="Cat3_H1_FR", attribute="state_array")[:, 0] + cat_3_out_h2 = self._model.call_internal(id="Cat3_H2", method="get_output", solve=False) + cat_3_aet_h2 = self._model.call_internal(id="Cat3_H2_UR", method="get_AET") + cat_3_s_ur_h2 = self._model.get_internal(id="Cat3_H2_UR", attribute="state_array")[:, 0] + cat_3_s_fr_h2 = self._model.get_internal(id="Cat3_H2_FR", attribute="state_array")[:, 0] + cat_3_s_sr_h2 = self._model.get_internal(id="Cat3_H2_SR", attribute="state_array")[:, 0] + + msg = "Fail in the second round" + + self.assertTrue(np.allclose(out["Cat1"], self._superflex_output.iloc[:, 0]), msg=msg) + self.assertTrue(np.allclose(out["Cat2"], self._superflex_output.iloc[:, 1]), msg=msg) + self.assertTrue(np.allclose(out["Cat3"], self._superflex_output.iloc[:, 2]), msg=msg) + self.assertTrue(np.allclose(cat_1_out_h1, self._superflex_output.iloc[:, 3]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_fr_h1, self._superflex_output.iloc[:, 4]), msg=msg) + self.assertTrue(np.allclose(cat_2_out_h1, self._superflex_output.iloc[:, 5]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_fr_h1, self._superflex_output.iloc[:, 6]), msg=msg) + self.assertTrue(np.allclose(cat_3_out_h1, self._superflex_output.iloc[:, 7]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_fr_h1, self._superflex_output.iloc[:, 8]), msg=msg) + self.assertTrue(np.allclose(cat_1_out_h2, self._superflex_output.iloc[:, 9]), msg=msg) + self.assertTrue(np.allclose(cat_1_aet_h2, self._superflex_output.iloc[:, 10]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_ur_h2, self._superflex_output.iloc[:, 11]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_fr_h2, self._superflex_output.iloc[:, 12]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_sr_h2, self._superflex_output.iloc[:, 13]), msg=msg) + self.assertTrue(np.allclose(cat_2_out_h2, self._superflex_output.iloc[:, 14]), msg=msg) + self.assertTrue(np.allclose(cat_2_aet_h2, self._superflex_output.iloc[:, 15]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_ur_h2, self._superflex_output.iloc[:, 16]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_fr_h2, self._superflex_output.iloc[:, 17]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_sr_h2, self._superflex_output.iloc[:, 18]), msg=msg) + self.assertTrue(np.allclose(cat_3_out_h2, self._superflex_output.iloc[:, 19]), msg=msg) + self.assertTrue(np.allclose(cat_3_aet_h2, self._superflex_output.iloc[:, 20]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_ur_h2, self._superflex_output.iloc[:, 21]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_fr_h2, self._superflex_output.iloc[:, 22]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_sr_h2, self._superflex_output.iloc[:, 23]), msg=msg) # Second half of time series self._model.reset_states() out = self._model.get_output() # Catchment_1 - cat_1_out_h1 = self._model.call_internal(id = 'Cat1_H1', method = 'get_output', solve = False) - cat_1_s_fr_h1 = self._model.get_internal(id = 'Cat1_H1_FR', attribute = 'state_array')[:, 0] - cat_1_out_h2 = self._model.call_internal(id = 'Cat1_H2', method = 'get_output', solve = False) - cat_1_aet_h2 = self._model.call_internal(id = 'Cat1_H2_UR', method = 'get_AET') - cat_1_s_ur_h2 = self._model.get_internal(id = 'Cat1_H2_UR', attribute = 'state_array')[:, 0] - cat_1_s_fr_h2 = self._model.get_internal(id = 'Cat1_H2_FR', attribute = 'state_array')[:, 0] - cat_1_s_sr_h2 = self._model.get_internal(id = 'Cat1_H2_SR', attribute = 'state_array')[:, 0] + cat_1_out_h1 = self._model.call_internal(id="Cat1_H1", method="get_output", solve=False) + cat_1_s_fr_h1 = self._model.get_internal(id="Cat1_H1_FR", attribute="state_array")[:, 0] + cat_1_out_h2 = self._model.call_internal(id="Cat1_H2", method="get_output", solve=False) + cat_1_aet_h2 = self._model.call_internal(id="Cat1_H2_UR", method="get_AET") + cat_1_s_ur_h2 = self._model.get_internal(id="Cat1_H2_UR", attribute="state_array")[:, 0] + cat_1_s_fr_h2 = self._model.get_internal(id="Cat1_H2_FR", attribute="state_array")[:, 0] + cat_1_s_sr_h2 = self._model.get_internal(id="Cat1_H2_SR", attribute="state_array")[:, 0] # Catchment_2 - cat_2_out_h1 = self._model.call_internal(id = 'Cat2_H1', method = 'get_output', solve = False) - cat_2_s_fr_h1 = self._model.get_internal(id = 'Cat2_H1_FR', attribute = 'state_array')[:, 0] - cat_2_out_h2 = self._model.call_internal(id = 'Cat2_H2', method = 'get_output', solve = False) - cat_2_aet_h2 = self._model.call_internal(id = 'Cat2_H2_UR', method = 'get_AET') - cat_2_s_ur_h2 = self._model.get_internal(id = 'Cat2_H2_UR', attribute = 'state_array')[:, 0] - cat_2_s_fr_h2 = self._model.get_internal(id = 'Cat2_H2_FR', attribute = 'state_array')[:, 0] - cat_2_s_sr_h2 = self._model.get_internal(id = 'Cat2_H2_SR', attribute = 'state_array')[:, 0] + cat_2_out_h1 = self._model.call_internal(id="Cat2_H1", method="get_output", solve=False) + cat_2_s_fr_h1 = self._model.get_internal(id="Cat2_H1_FR", attribute="state_array")[:, 0] + cat_2_out_h2 = self._model.call_internal(id="Cat2_H2", method="get_output", solve=False) + cat_2_aet_h2 = self._model.call_internal(id="Cat2_H2_UR", method="get_AET") + cat_2_s_ur_h2 = self._model.get_internal(id="Cat2_H2_UR", attribute="state_array")[:, 0] + cat_2_s_fr_h2 = self._model.get_internal(id="Cat2_H2_FR", attribute="state_array")[:, 0] + cat_2_s_sr_h2 = self._model.get_internal(id="Cat2_H2_SR", attribute="state_array")[:, 0] # Catchment_3 - cat_3_out_h1 = self._model.call_internal(id = 'Cat3_H1', method = 'get_output', solve = False) - cat_3_s_fr_h1 = self._model.get_internal(id = 'Cat3_H1_FR', attribute = 'state_array')[:, 0] - cat_3_out_h2 = self._model.call_internal(id = 'Cat3_H2', method = 'get_output', solve = False) - cat_3_aet_h2 = self._model.call_internal(id = 'Cat3_H2_UR', method = 'get_AET') - cat_3_s_ur_h2 = self._model.get_internal(id = 'Cat3_H2_UR', attribute = 'state_array')[:, 0] - cat_3_s_fr_h2 = self._model.get_internal(id = 'Cat3_H2_FR', attribute = 'state_array')[:, 0] - cat_3_s_sr_h2 = self._model.get_internal(id = 'Cat3_H2_SR', attribute = 'state_array')[:, 0] - - msg = 'Fail in the second round' - - self.assertTrue(np.allclose(out['Cat1'], self._superflex_output.iloc[:, 0]), msg = msg) - self.assertTrue(np.allclose(out['Cat2'], self._superflex_output.iloc[:, 1]), msg = msg) - self.assertTrue(np.allclose(out['Cat3'], self._superflex_output.iloc[:, 2]), msg = msg) - self.assertTrue(np.allclose(cat_1_out_h1, self._superflex_output.iloc[:, 3]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_fr_h1, self._superflex_output.iloc[:, 4]), msg = msg) - self.assertTrue(np.allclose(cat_2_out_h1, self._superflex_output.iloc[:, 5]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_fr_h1, self._superflex_output.iloc[:, 6]), msg = msg) - self.assertTrue(np.allclose(cat_3_out_h1, self._superflex_output.iloc[:, 7]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_fr_h1, self._superflex_output.iloc[:, 8]), msg = msg) - self.assertTrue(np.allclose(cat_1_out_h2, self._superflex_output.iloc[:, 9]), msg = msg) - self.assertTrue(np.allclose(cat_1_aet_h2, self._superflex_output.iloc[:, 10]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_ur_h2, self._superflex_output.iloc[:, 11]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_fr_h2, self._superflex_output.iloc[:, 12]), msg = msg) - self.assertTrue(np.allclose(cat_1_s_sr_h2, self._superflex_output.iloc[:, 13]), msg = msg) - self.assertTrue(np.allclose(cat_2_out_h2, self._superflex_output.iloc[:, 14]), msg = msg) - self.assertTrue(np.allclose(cat_2_aet_h2, self._superflex_output.iloc[:, 15]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_ur_h2, self._superflex_output.iloc[:, 16]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_fr_h2, self._superflex_output.iloc[:, 17]), msg = msg) - self.assertTrue(np.allclose(cat_2_s_sr_h2, self._superflex_output.iloc[:, 18]), msg = msg) - self.assertTrue(np.allclose(cat_3_out_h2, self._superflex_output.iloc[:, 19]), msg = msg) - self.assertTrue(np.allclose(cat_3_aet_h2, self._superflex_output.iloc[:, 20]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_ur_h2, self._superflex_output.iloc[:, 21]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_fr_h2, self._superflex_output.iloc[:, 22]), msg = msg) - self.assertTrue(np.allclose(cat_3_s_sr_h2, self._superflex_output.iloc[:, 23]), msg = msg) + cat_3_out_h1 = self._model.call_internal(id="Cat3_H1", method="get_output", solve=False) + cat_3_s_fr_h1 = self._model.get_internal(id="Cat3_H1_FR", attribute="state_array")[:, 0] + cat_3_out_h2 = self._model.call_internal(id="Cat3_H2", method="get_output", solve=False) + cat_3_aet_h2 = self._model.call_internal(id="Cat3_H2_UR", method="get_AET") + cat_3_s_ur_h2 = self._model.get_internal(id="Cat3_H2_UR", attribute="state_array")[:, 0] + cat_3_s_fr_h2 = self._model.get_internal(id="Cat3_H2_FR", attribute="state_array")[:, 0] + cat_3_s_sr_h2 = self._model.get_internal(id="Cat3_H2_SR", attribute="state_array")[:, 0] + + msg = "Fail in the second round" + + self.assertTrue(np.allclose(out["Cat1"], self._superflex_output.iloc[:, 0]), msg=msg) + self.assertTrue(np.allclose(out["Cat2"], self._superflex_output.iloc[:, 1]), msg=msg) + self.assertTrue(np.allclose(out["Cat3"], self._superflex_output.iloc[:, 2]), msg=msg) + self.assertTrue(np.allclose(cat_1_out_h1, self._superflex_output.iloc[:, 3]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_fr_h1, self._superflex_output.iloc[:, 4]), msg=msg) + self.assertTrue(np.allclose(cat_2_out_h1, self._superflex_output.iloc[:, 5]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_fr_h1, self._superflex_output.iloc[:, 6]), msg=msg) + self.assertTrue(np.allclose(cat_3_out_h1, self._superflex_output.iloc[:, 7]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_fr_h1, self._superflex_output.iloc[:, 8]), msg=msg) + self.assertTrue(np.allclose(cat_1_out_h2, self._superflex_output.iloc[:, 9]), msg=msg) + self.assertTrue(np.allclose(cat_1_aet_h2, self._superflex_output.iloc[:, 10]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_ur_h2, self._superflex_output.iloc[:, 11]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_fr_h2, self._superflex_output.iloc[:, 12]), msg=msg) + self.assertTrue(np.allclose(cat_1_s_sr_h2, self._superflex_output.iloc[:, 13]), msg=msg) + self.assertTrue(np.allclose(cat_2_out_h2, self._superflex_output.iloc[:, 14]), msg=msg) + self.assertTrue(np.allclose(cat_2_aet_h2, self._superflex_output.iloc[:, 15]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_ur_h2, self._superflex_output.iloc[:, 16]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_fr_h2, self._superflex_output.iloc[:, 17]), msg=msg) + self.assertTrue(np.allclose(cat_2_s_sr_h2, self._superflex_output.iloc[:, 18]), msg=msg) + self.assertTrue(np.allclose(cat_3_out_h2, self._superflex_output.iloc[:, 19]), msg=msg) + self.assertTrue(np.allclose(cat_3_aet_h2, self._superflex_output.iloc[:, 20]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_ur_h2, self._superflex_output.iloc[:, 21]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_fr_h2, self._superflex_output.iloc[:, 22]), msg=msg) + self.assertTrue(np.allclose(cat_3_s_sr_h2, self._superflex_output.iloc[:, 23]), msg=msg) def test_2_rounds_python(self): - - self._test_2_rounds(solver = 'python') + self._test_2_rounds(solver="python") def test_2_rounds_numba(self): + self._test_2_rounds(solver="numba") - self._test_2_rounds(solver = 'numba') if __name__ == "__main__": unittest.main() # test = TestStructureElements() # test._read_inputs() - # test.test_start_stop_python() \ No newline at end of file + # test.test_start_stop_python() diff --git a/test/unittest/07_FR_2dt.py b/test/unittest/07_FR_2dt.py index be2df95..1f82d36 100644 --- a/test/unittest/07_FR_2dt.py +++ b/test/unittest/07_FR_2dt.py @@ -23,20 +23,23 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -import unittest import sys -from os.path import abspath, join, dirname +import unittest +from os.path import abspath, dirname, join -import pandas as pd import numpy as np +import pandas as pd # Package path is 2 levels above this file -package_path = join(abspath(dirname(__file__)), '..', '..') +package_path = join(abspath(dirname(__file__)), "..", "..") sys.path.insert(0, package_path) from superflexpy.implementation.elements.hbv import PowerReservoir +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerNumba, + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusNumba, PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerNumba, ImplicitEulerPython class TestFR(unittest.TestCase): @@ -48,34 +51,32 @@ class TestFR(unittest.TestCase): """ def _init_model(self, solver): - - if solver == 'numba': + if solver == "numba": solver = PegasusNumba() num_app = ImplicitEulerNumba(root_finder=solver) - elif solver == 'python': + elif solver == "python": solver = PegasusPython() num_app = ImplicitEulerPython(root_finder=solver) - fr = PowerReservoir(parameters={'k': 0.01, - 'alpha': 2.5}, - states={'S0': 0.0}, - approximation=num_app, - id='FR') + fr = PowerReservoir(parameters={"k": 0.01, "alpha": 2.5}, states={"S0": 0.0}, approximation=num_app, id="FR") fr.set_timestep(2.0) self._model = fr def _read_inputs(self): - - data = pd.read_csv('{}/test/reference_results/07_FR_2dt/input.dat'.format(package_path), header=6, sep='\s+|,\s+|,', engine='python') + data = pd.read_csv( + "{}/test/reference_results/07_FR_2dt/input.dat".format(package_path), + header=6, + sep="\s+|,\s+|,", + engine="python", + ) self._precipitation = data.iloc[:, 6].values self._pet = data.iloc[:, 7].values def _read_outputs(self): - self._superflex_output = pd.read_csv('{}/test/reference_results/07_FR_2dt/Results.csv'.format(package_path)) + self._superflex_output = pd.read_csv("{}/test/reference_results/07_FR_2dt/Results.csv".format(package_path)) def _test_fr_start_stop(self, solver): - self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -84,7 +85,7 @@ def _test_fr_start_stop(self, solver): self._model.set_input([self._precipitation[:5]]) out = self._model.get_output() - msg = 'Fail in the first half' + msg = "Fail in the first half" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[:5, 1]), msg=msg) @@ -93,21 +94,18 @@ def _test_fr_start_stop(self, solver): self._model.set_input([self._precipitation[5:]]) out = self._model.get_output() - msg = 'Fail in the second half' + msg = "Fail in the second half" self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[5:, 1]), msg=msg) def test_fr_start_stop_python(self): - - self._test_fr_start_stop(solver='python') + self._test_fr_start_stop(solver="python") def test_fr_start_stop_numba(self): - - self._test_fr_start_stop(solver='numba') + self._test_fr_start_stop(solver="numba") def _test_2_rounds(self, solver): - self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -116,7 +114,7 @@ def _test_2_rounds(self, solver): self._model.set_input([self._precipitation]) out = self._model.get_output() - msg = 'Fail in the first round' + msg = "Fail in the first round" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[:, 1]), msg=msg) @@ -125,18 +123,16 @@ def _test_2_rounds(self, solver): self._model.reset_states() out = self._model.get_output() - msg = 'Fail in the second round' + msg = "Fail in the second round" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[:, 1]), msg=msg) def test_2_rounds_python(self): - - self._test_2_rounds(solver='python') + self._test_2_rounds(solver="python") def test_2_rounds_numba(self): - - self._test_2_rounds(solver='numba') + self._test_2_rounds(solver="numba") # if __name__ == "__main__": diff --git a/test/unittest/08_UR_2dt.py b/test/unittest/08_UR_2dt.py index b8a5065..1359119 100644 --- a/test/unittest/08_UR_2dt.py +++ b/test/unittest/08_UR_2dt.py @@ -23,20 +23,23 @@ DESIGNED BY: Marco Dal Molin, Fabrizio Fenicia, Dmitri Kavetski """ -import unittest import sys -from os.path import abspath, join, dirname +import unittest +from os.path import abspath, dirname, join -import pandas as pd import numpy as np +import pandas as pd # Package path is 2 levels above this file -package_path = join(abspath(dirname(__file__)), '..', '..') +package_path = join(abspath(dirname(__file__)), "..", "..") sys.path.insert(0, package_path) from superflexpy.implementation.elements.hbv import UnsaturatedReservoir +from superflexpy.implementation.numerical_approximators.implicit_euler import ( + ImplicitEulerNumba, + ImplicitEulerPython, +) from superflexpy.implementation.root_finders.pegasus import PegasusNumba, PegasusPython -from superflexpy.implementation.numerical_approximators.implicit_euler import ImplicitEulerNumba, ImplicitEulerPython class TestUR(unittest.TestCase): @@ -48,36 +51,37 @@ class TestUR(unittest.TestCase): """ def _init_model(self, solver): - - if solver == 'numba': + if solver == "numba": solver = PegasusNumba() num_app = ImplicitEulerNumba(root_finder=solver) - elif solver == 'python': + elif solver == "python": solver = PegasusPython() num_app = ImplicitEulerPython(root_finder=solver) - ur = UnsaturatedReservoir(parameters={'Smax': 50.0, - 'Ce': 1.5, - 'm': 0.01, - 'beta': 1.5}, - states={'S0': 0.2 * 50.0}, - approximation=num_app, - id='UR') + ur = UnsaturatedReservoir( + parameters={"Smax": 50.0, "Ce": 1.5, "m": 0.01, "beta": 1.5}, + states={"S0": 0.2 * 50.0}, + approximation=num_app, + id="UR", + ) ur.set_timestep(2.0) self._model = ur def _read_inputs(self): - - data = pd.read_csv('{}/test/reference_results/08_UR_2dt/input.dat'.format(package_path), header=6, sep='\s+|,\s+|,', engine='python') + data = pd.read_csv( + "{}/test/reference_results/08_UR_2dt/input.dat".format(package_path), + header=6, + sep="\s+|,\s+|,", + engine="python", + ) self._precipitation = data.iloc[:, 6].values self._pet = data.iloc[:, 7].values def _read_outputs(self): - self._superflex_output = pd.read_csv('{}/test/reference_results/08_UR_2dt/Results.csv'.format(package_path)) + self._superflex_output = pd.read_csv("{}/test/reference_results/08_UR_2dt/Results.csv".format(package_path)) def _test_ur_start_stop(self, solver): - self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -87,7 +91,7 @@ def _test_ur_start_stop(self, solver): out = self._model.get_output() aet = self._model.get_AET() - msg = 'Fail in the first half' + msg = "Fail in the first half" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:5, 0]), msg=msg) self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:5, 1]), msg=msg) @@ -98,22 +102,19 @@ def _test_ur_start_stop(self, solver): out = self._model.get_output() aet = self._model.get_AET() - msg = 'Fail in the second half' + msg = "Fail in the second half" self.assertTrue(np.allclose(out, self._superflex_output.iloc[5:, 0]), msg=msg) self.assertTrue(np.allclose(aet, self._superflex_output.iloc[5:, 1]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[5:, 2]), msg=msg) def test_ur_start_stop_python(self): - - self._test_ur_start_stop(solver='python') + self._test_ur_start_stop(solver="python") def test_ur_start_stop_numba(self): - - self._test_ur_start_stop(solver='numba') + self._test_ur_start_stop(solver="numba") def _test_2_rounds(self, solver): - self._init_model(solver=solver) self._read_outputs() self._read_inputs() @@ -123,7 +124,7 @@ def _test_2_rounds(self, solver): out = self._model.get_output() aet = self._model.get_AET() - msg = 'Fail in the first round' + msg = "Fail in the first round" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg=msg) @@ -134,19 +135,17 @@ def _test_2_rounds(self, solver): out = self._model.get_output() aet = self._model.get_AET() - msg = 'Fail in the second round' + msg = "Fail in the second round" self.assertTrue(np.allclose(out, self._superflex_output.iloc[:, 0]), msg=msg) self.assertTrue(np.allclose(aet, self._superflex_output.iloc[:, 1]), msg=msg) self.assertTrue(np.allclose(self._model.state_array[:, 0], self._superflex_output.iloc[:, 2]), msg=msg) def test_2_rounds_python(self): - - self._test_2_rounds(solver='python') + self._test_2_rounds(solver="python") def test_2_rounds_numba(self): - - self._test_2_rounds(solver='numba') + self._test_2_rounds(solver="numba") if __name__ == "__main__": diff --git a/test/unittest/test_all.sh b/test/unittest/test_all.sh index a6307a2..d714a0f 100755 --- a/test/unittest/test_all.sh +++ b/test/unittest/test_all.sh @@ -1,2 +1,2 @@ cd test/unittest/ -for f in *.py; do echo Running "$f"; python "$f"; done \ No newline at end of file +for f in *.py; do echo Running "$f"; python "$f"; done