🏡 usage.nim

Usage Examples

Julia

Use of this package can be found in the Streamfall.jl package here.

Examples of direct function calls through Julia's ccall method is shown below.

Julia pre-v1.5.3

ccall((:calc_outflow, "./lib/ihacres.so"), Cdouble,(Cdouble, Cdouble), 1.0, 1.0)

Julia v1.5.3 onwards

These versions include a @ccall macro which simplifies usage. Note: if on Windows, replace .so with .dll.

const IHACRES = "./lib/ihacres.so"
@ccall IHACRES.calc_outflow(10.0::Cdouble, 8.0::Cdouble)::Cdouble

Python

Use of the compiled DLL/so/dynlib is possible via the ctypes package, but declaring input/return types can get tedious (see the tutorial here).

Instead, the most straightforward approach is to use the nimporter package.

In future, a dedicated Python package may be made available.

  1. Clone ihacres_nim
$ git clone https://github.com/ConnectedSystems/ihacres_nim.git
  1. Install nimporter and ihacres-nim as a Python package
$ pip install nimporter
$ pip install -e .

Example usage is provided under tests/py_example.py

$ python tests/py_example.py

The contents of py_example.py are shown below.

import nimporter  # Required prior to any Nim module import

from ihacres import climate
from ihacres import flow
from ihacres import cmd as ihacres_cmd

nimporter.build_nim_extensions()  # Build Nim extension


def run_ihacres(cmd, rainfall, evaporation, inflow, quickflow, slowflow,
                gw_state, gw_exchange, extraction):
    '''Example run function.

    Parameters
    ----------
    cmd : catchment moisture deficit, $M_{k}$ in the literature.
    rainfall : precipitation ($P$)
    evaporation : ($E$)
    inflow : flow from previous node
    quickflow : quickflow at previous time step, $(V_{q-1})$
    slowflow : slowflow at previous time step, $(V_{s-1})$
    gw_state : groundwater storage index at $t-1$
    gw_exchange : volume flux, interaction with groundwater
    extraction : volume extracted from stream
    '''
    d, d2, e, f = run_ihacres.d, run_ihacres.d2, run_ihacres.e, run_ihacres.f
    a, b, alpha, s = run_ihacres.a, run_ihacres.b, run_ihacres.alpha, run_ihacres.s
    catchment_area = run_ihacres.catchment_area

    mf, U, r = ihacres_cmd.calc_ft_interim_cmd(cmd, rainfall, d, d2, alpha)
    ET = climate.calc_ET(e, evaporation, mf, f, d)
    cmd = ihacres_cmd.calc_cmd(cmd, rainfall, ET, U, r)

    Vq, Vs, outflow = flow.calc_ft_flows(quickflow, slowflow, U, r,
                                         catchment_area, a, b)

    # if node routes to another node
    gw_state, outflow = flow.routing(gw_state, s, inflow, outflow, extraction, gw_exchange)

    return {
        "flow": (gw_state, outflow),
        "state": (Vq, Vs, cmd)
    }

# Here we leverage the fact that everything in Python is an object
# including functions. We assign the model parameters to the function
# such that the function itself represents a node in a stream network.
# Of course, you could (should?) define a Class instead.
run_ihacres.d = 200.0  # flow threshold
run_ihacres.d2 = 2.0   # flow threshold, multiplier applied to `d`
run_ihacres.e = 0.1    # temperature to PET conversion factor
run_ihacres.f = 0.1    # plant stress threshold factor (applied to `d`). Determines effective rainfall.
run_ihacres.a = 54.35  # quickflow scaling factor
run_ihacres.b = 0.012  # slowflow scaling factor
run_ihacres.alpha = 0.727   #  effective rainfall scaling factor
run_ihacres.s = 2.5  # groundwater store factor
run_ihacres.catchment_area = 100.0  # area in km^2


# Initial conditions
cmd, quickflow, slowflow = 214.65, 0, 0

# Assume these are all 0.0
inflow = 0.0
gw_exchange = 0.0
extraction = 0.0

# Ideally, these would be read in via Numpy or Pandas
rainfall_ts = [70.0, 10.0, 0.0, 0.0, 200.0]
evaporation_ts = [2.0, 6.5, 7.0, 5.0, 1.0]

# Set up arrays to record state
outflow = [None] * len(rainfall_ts)
gw_state = [None] * (len(rainfall_ts)+1)
gw_state[0] = 0.0

# Run model (this would be in its own function)
for i in range(len(outflow)):
    progress = run_ihacres(cmd, rainfall_ts[i], evaporation_ts[i], inflow, quickflow, slowflow,
                           gw_state[i], gw_exchange, extraction)
    quickflow, slowflow, cmd = progress["state"]
    gw_state[i+1], outflow[i] = progress["flow"]

# Remove initial gw state to line up records
gw_state = gw_state[1:]

print(outflow)

The streamflow output should be:

    [2219.61106301161, 300.57003807505123, 36.753372670041266, 14.953875444140436, 8905.129670965001]
import nimib


nbInit

nbDoc.context["highlight"] = """
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.5.0/styles/default.min.css">
<script src="assets/highlight.pack.js"></script>
<script>hljs.highlightAll();</script>"""

let
    index = read("index.nim".RelativeFile)

nbText: """

## Usage Examples

### Julia

Use of this package can be found in the [Streamfall.jl](https://github.com/ConnectedSystems/Streamfall.jl) package
[here](https://github.com/ConnectedSystems/Streamfall.jl/blob/d0558990c4c1ffce6f14e914d854ab2b83282867/src/IHACRESNode.jl#L186).

Examples of direct function calls through Julia's `ccall` method is shown below.

**Julia pre-v1.5.3**

```julia
ccall((:calc_outflow, "./lib/ihacres.so"), Cdouble,(Cdouble, Cdouble), 1.0, 1.0)
```

**Julia v1.5.3 onwards**

These versions include a `@ccall` macro which simplifies usage.
Note: if on Windows, replace `.so` with `.dll`.

```julia
const IHACRES = "./lib/ihacres.so"
@ccall IHACRES.calc_outflow(10.0::Cdouble, 8.0::Cdouble)::Cdouble
```


### Python

Use of the compiled DLL/so/dynlib is possible via the `ctypes` package, but declaring input/return types can get tedious (see the tutorial [here](https://docs.python.org/3/library/ctypes.html)).

Instead, the most straightforward approach is to use the `nimporter` package.

In future, a dedicated Python package may be made available.

1. Clone `ihacres_nim`

```bash
$ git clone https://github.com/ConnectedSystems/ihacres_nim.git
```


2. Install `nimporter` and `ihacres-nim` as a Python package

```bash
$ pip install nimporter
$ pip install -e .
```

Example usage is provided under [tests/py_example.py](https://github.com/ConnectedSystems/ihacres_nim/tree/main/tests)

```bash
$ python tests/py_example.py
```

The contents of `py_example.py` are shown below.


```python
import nimporter  # Required prior to any Nim module import

from ihacres import climate
from ihacres import flow
from ihacres import cmd as ihacres_cmd

nimporter.build_nim_extensions()  # Build Nim extension


def run_ihacres(cmd, rainfall, evaporation, inflow, quickflow, slowflow,
                gw_state, gw_exchange, extraction):
    '''Example run function.

    Parameters
    ----------
    cmd : catchment moisture deficit, $M_{k}$ in the literature.
    rainfall : precipitation ($P$)
    evaporation : ($E$)
    inflow : flow from previous node
    quickflow : quickflow at previous time step, $(V_{q-1})$
    slowflow : slowflow at previous time step, $(V_{s-1})$
    gw_state : groundwater storage index at $t-1$
    gw_exchange : volume flux, interaction with groundwater
    extraction : volume extracted from stream
    '''
    d, d2, e, f = run_ihacres.d, run_ihacres.d2, run_ihacres.e, run_ihacres.f
    a, b, alpha, s = run_ihacres.a, run_ihacres.b, run_ihacres.alpha, run_ihacres.s
    catchment_area = run_ihacres.catchment_area

    mf, U, r = ihacres_cmd.calc_ft_interim_cmd(cmd, rainfall, d, d2, alpha)
    ET = climate.calc_ET(e, evaporation, mf, f, d)
    cmd = ihacres_cmd.calc_cmd(cmd, rainfall, ET, U, r)

    Vq, Vs, outflow = flow.calc_ft_flows(quickflow, slowflow, U, r,
                                         catchment_area, a, b)

    # if node routes to another node
    gw_state, outflow = flow.routing(gw_state, s, inflow, outflow, extraction, gw_exchange)

    return {
        "flow": (gw_state, outflow),
        "state": (Vq, Vs, cmd)
    }

# Here we leverage the fact that everything in Python is an object
# including functions. We assign the model parameters to the function
# such that the function itself represents a node in a stream network.
# Of course, you could (should?) define a Class instead.
run_ihacres.d = 200.0  # flow threshold
run_ihacres.d2 = 2.0   # flow threshold, multiplier applied to `d`
run_ihacres.e = 0.1    # temperature to PET conversion factor
run_ihacres.f = 0.1    # plant stress threshold factor (applied to `d`). Determines effective rainfall.
run_ihacres.a = 54.35  # quickflow scaling factor
run_ihacres.b = 0.012  # slowflow scaling factor
run_ihacres.alpha = 0.727   #  effective rainfall scaling factor
run_ihacres.s = 2.5  # groundwater store factor
run_ihacres.catchment_area = 100.0  # area in km^2


# Initial conditions
cmd, quickflow, slowflow = 214.65, 0, 0

# Assume these are all 0.0
inflow = 0.0
gw_exchange = 0.0
extraction = 0.0

# Ideally, these would be read in via Numpy or Pandas
rainfall_ts = [70.0, 10.0, 0.0, 0.0, 200.0]
evaporation_ts = [2.0, 6.5, 7.0, 5.0, 1.0]

# Set up arrays to record state
outflow = [None] * len(rainfall_ts)
gw_state = [None] * (len(rainfall_ts)+1)
gw_state[0] = 0.0

# Run model (this would be in its own function)
for i in range(len(outflow)):
    progress = run_ihacres(cmd, rainfall_ts[i], evaporation_ts[i], inflow, quickflow, slowflow,
                           gw_state[i], gw_exchange, extraction)
    quickflow, slowflow, cmd = progress["state"]
    gw_state[i+1], outflow[i] = progress["flow"]

# Remove initial gw state to line up records
gw_state = gw_state[1:]

print(outflow)
```

The streamflow output should be:

```
    [2219.61106301161, 300.57003807505123, 36.753372670041266, 14.953875444140436, 8905.129670965001]
```
"""

nbSave