Skip to content

Commit cd977bf

Browse files
author
Marco Dal Molin
committed
Release 1.0.0
1 parent b9614dc commit cd977bf

20 files changed

Lines changed: 1544 additions & 804 deletions

setup.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22

33
setuptools.setup(
44
name='superflexpy',
5-
version='0.3.1',
5+
version='0.0.13',
66
author='Marco Dal Molin',
77
author_email='marco.dalmolin.1991@gmail.com',
88
description='Framework for building hydrological models',
9+
long_description='Framework for building hydrological models',
910
license='Apache Software License',
1011
packages=setuptools.find_packages(),
1112
classifiers=[
12-
'Development Status :: 3 - Alpha' # https://martin-thoma.com/software-development-stages/
13-
]
13+
'Development Status :: 5 - Production/Stable', # https://martin-thoma.com/software-development-stages/
14+
],
15+
install_requires=[
16+
'numpy>=1.16.4',
17+
'numba>=0.49.0',
18+
],
1419
)

superflexpy/framework/element.py

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Copyright 2019 Marco Dal Molin et al.
2+
Copyright 2020 Marco Dal Molin et al.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -37,7 +37,7 @@ class BaseElement():
3737
"""
3838
Number of downstream elements
3939
"""
40-
40+
4141
_num_upstream = None
4242
"""
4343
Number of upstream elements
@@ -469,7 +469,7 @@ class ODEsElement(StateParameterizedElement):
469469
470470
dS/dt = input - output
471471
"""
472-
472+
473473
_num_upstream = 1
474474
"""
475475
Number of upstream elements
@@ -485,7 +485,25 @@ class ODEsElement(StateParameterizedElement):
485485
List of states used by the solver of the differential equation
486486
"""
487487

488-
def __init__(self, parameters, states, solver, id):
488+
_fluxes = []
489+
"""
490+
This attribute contains a list of methods (one per differential equation)
491+
that calculate the values of the fluxes needed to solve the differential
492+
equations that control the element. The single functions must return the
493+
fluxes as a list where incoming fluxes are positive and outgoing are
494+
negative. Here is a list of the required outputs of the single functions:
495+
496+
list(floats)
497+
Values of the fluxes given states, inputs, and parameters.
498+
float
499+
Minimum value of the state. Used, sometimes, by the numerical solver
500+
to search for the solution.
501+
float
502+
Maximum value of the state. Used, sometimes, by the numerical solver
503+
to search for the solution.
504+
"""
505+
506+
def __init__(self, parameters, states, approximation, id):
489507
"""
490508
This is the initializer of the abstract class ODEsElement.
491509
@@ -498,10 +516,8 @@ def __init__(self, parameters, states, solver, id):
498516
states : dict
499517
Initial states of the element. Depending on the element the states
500518
can be either a float or a numpy.ndarray.
501-
solver : superflexpy.utils.root_finder.RootFinder
502-
Solver used to find the root(s) of the differential equation(s).
503-
Child classes may implement their own solver, therefore the type
504-
of the solver is not enforced.
519+
approximation : superflexpy.utils.numerical_approximation.NumericalApproximator
520+
Numerial method used to approximate the differential equation
505521
id : str
506522
Identifier of the element. All the elements of the framework must
507523
have an id.
@@ -510,7 +526,7 @@ def __init__(self, parameters, states, solver, id):
510526
StateParameterizedElement.__init__(self, parameters=parameters,
511527
states=states, id=id)
512528

513-
self._solver = solver
529+
self._num_app = approximation
514530

515531
def set_timestep(self, dt):
516532
"""
@@ -534,7 +550,7 @@ def get_timestep(self):
534550
"""
535551
return self._dt
536552

537-
def define_solver(self, solver):
553+
def define_numerical_approximation(self, approximation):
538554
"""
539555
This method define the solver to use for the differential equation.
540556
@@ -546,7 +562,7 @@ def define_solver(self, solver):
546562
of the solver is not enforced.
547563
"""
548564

549-
self._solver = solver
565+
self._num_app = approximation
550566

551567
def _solve_differential_equation(self, **kwargs):
552568
"""
@@ -559,29 +575,20 @@ def _solve_differential_equation(self, **kwargs):
559575
message = '{}the attribute _solver_states must be filled'.format(self._error_message)
560576
raise ValueError(message)
561577

562-
self.state_array = self._solver.solve(fun=self._differential_equation,
563-
S0=self._solver_states,
564-
dt=self._dt,
565-
**self.input,
566-
**{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters},
567-
**kwargs)
568-
569-
def _differential_equation(self):
570-
"""
571-
To be implemented by any child class. This method sets the differential
572-
equation(s) to be solved by the solver. The method must be implemented
573-
in order to satisfy the requirements of the solver.
574-
"""
575-
576-
raise NotImplementedError('The _differential_equation method must be implemented')
578+
self.state_array = self._num_app.solve(fun=self._fluxes,
579+
S0=self._solver_states,
580+
dt=self._dt,
581+
**self.input,
582+
**{k[len(self._prefix_parameters):]: self._parameters[k] for k in self._parameters},
583+
**kwargs)
577584

578585
def __copy__(self):
579586
p = self._parameters # Only the reference
580587
s = deepcopy(self._states) # Create a new dictionary
581588
ele = self.__class__(parameters=p,
582589
states=s,
583590
id=self.id,
584-
solver=self._solver)
591+
approximation=self._num_app)
585592
ele._prefix_states = self._prefix_states
586593
ele._prefix_parameters = self._prefix_parameters
587594
return ele
@@ -592,7 +599,7 @@ def __deepcopy__(self, memo):
592599
ele = self.__class__(parameters=p,
593600
states=s,
594601
id=self.id,
595-
solver=self._solver)
602+
approximation=self._num_app)
596603
ele._prefix_states = self._prefix_states
597604
ele._prefix_parameters = self._prefix_parameters
598605
return ele
@@ -728,7 +735,7 @@ def _solve_lag(weight, lag_state, input):
728735
"""
729736
This method distributes the input fluxes according to the weight array
730737
and the initial state.
731-
738+
732739
Parameters
733740
----------
734741
weight : list(numpy.ndarray)
@@ -737,7 +744,7 @@ def _solve_lag(weight, lag_state, input):
737744
List of the initial states of the lag.
738745
input : list(numpy.ndarray)
739746
List of fluxes
740-
747+
741748
Returns
742749
-------
743750
numpy.ndarray
@@ -761,12 +768,12 @@ def _init_lag_state(self, lag_time):
761768
"""
762769
This method sets the initial state of the lag to arrays of proper
763770
length.
764-
771+
765772
Parameters
766773
----------
767774
lag_time : list(float)
768775
List of lag times
769-
776+
770777
Returns
771778
-------
772779
list(numpy.ndarray)

superflexpy/framework/network.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Copyright 2019 Marco Dal Molin et al.
2+
Copyright 2020 Marco Dal Molin et al.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@
2323
"""
2424

2525
from ..utils.generic_component import GenericComponent
26+
from .node import Node
2627

2728

2829
class Network(GenericComponent):
@@ -47,12 +48,17 @@ def __init__(self, nodes, topography):
4748
be a tree, each key has only one downstream element
4849
"""
4950

50-
self._content = nodes
51-
self._downstream = topography
52-
5351
self._error_message = 'module : superflexPy, Network ,'
5452
self._error_message += ' Error message : '
5553

54+
for n in nodes:
55+
if not isinstance(n, Node):
56+
message = '{}units must be instance of the Unit class'.format(self._error_message)
57+
raise TypeError(message)
58+
59+
self._content = nodes
60+
self._downstream = topography
61+
5662
self._build_network()
5763

5864
# METHODS FOR THE USER
@@ -146,7 +152,12 @@ def get_internal(self, id, attribute):
146152
cat_num, ele = self._find_attribute_from_name(id)
147153

148154
if ele:
149-
return self._content[cat_num].get_internal(id, attribute)
155+
splitted = id.split('_')
156+
if len(splitted) == 3: # element
157+
ele_id = splitted[1] + '_' + splitted[2]
158+
else: # unit
159+
ele_id = splitted[1]
160+
return self._content[cat_num].get_internal(ele_id, attribute)
150161
else:
151162
try:
152163
method = getattr(self._content[cat_num], attribute)
@@ -178,7 +189,12 @@ def call_internal(self, id, method, **kwargs):
178189
cat_num, ele = self._find_attribute_from_name(id)
179190

180191
if ele:
181-
return self._content[cat_num].call_internal(id, method, **kwargs)
192+
splitted = id.split('_')
193+
if len(splitted) == 3: # element
194+
ele_id = splitted[1] + '_' + splitted[2]
195+
else: # unit
196+
ele_id = splitted[1]
197+
return self._content[cat_num].call_internal(ele_id, method, **kwargs)
182198
else:
183199
try:
184200
method = getattr(self._content[cat_num], method)
@@ -264,7 +280,7 @@ def _find_attribute_from_name(self, id):
264280

265281
splitted = id.split('_')
266282

267-
cat_num = self._find_content_from_name(id)
283+
cat_num = self._find_content_from_name(id) # Options: None if cat_id not in id, number otherwise
268284

269285
if len(splitted) >= 2:
270286
return (cat_num, True) # We are looking for a HRU or an element

0 commit comments

Comments
 (0)