ETo 2.0.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {eto-2.0.0 → eto-2.2.0}/PKG-INFO +22 -4
- eto-2.2.0/README.md +59 -0
- {eto-2.0.0 → eto-2.2.0}/eto/__init__.py +1 -1
- {eto-2.0.0 → eto-2.2.0}/eto/core.py +14 -5
- eto-2.2.0/eto/crop_coefficients.py +123 -0
- eto-2.2.0/eto/methods/ETo.py +61 -0
- eto-2.2.0/eto/methods/dual_kc.py +44 -0
- {eto-2.0.0 → eto-2.2.0}/eto/methods/hargreaves.py +4 -3
- eto-2.2.0/eto/param_est.py +319 -0
- {eto-2.0.0 → eto-2.2.0}/eto/tests/test_eto.py +287 -2
- eto-2.2.0/eto/tests/test_hourly_fao.py +245 -0
- eto-2.2.0/eto/util.py +28 -0
- eto-2.0.0/README.md +0 -41
- eto-2.0.0/eto/methods/ETo.py +0 -41
- eto-2.0.0/eto/param_est.py +0 -208
- eto-2.0.0/eto/util.py +0 -4
- {eto-2.0.0 → eto-2.2.0}/.gitignore +0 -0
- {eto-2.0.0 → eto-2.2.0}/LICENSE.txt +0 -0
- {eto-2.0.0 → eto-2.2.0}/eto/datasets/__init__.py +0 -0
- {eto-2.0.0 → eto-2.2.0}/eto/datasets/example_daily.csv.zip +0 -0
- {eto-2.0.0 → eto-2.2.0}/eto/datasets/example_daily_results.csv.zip +0 -0
- {eto-2.0.0 → eto-2.2.0}/eto/methods/__init__.py +0 -0
- {eto-2.0.0 → eto-2.2.0}/eto/tests/__init__.py +0 -0
- {eto-2.0.0 → eto-2.2.0}/pyproject.toml +0 -0
{eto-2.0.0 → eto-2.2.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: ETo
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: Class and functions to estimate reference ET
|
|
5
5
|
Project-URL: Homepage, https://github.com/mullenkamp/ETo
|
|
6
6
|
Project-URL: Documentation, https://mullenkamp.github.io/ETo/
|
|
@@ -24,11 +24,21 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
|
|
25
25
|
# ETo
|
|
26
26
|
|
|
27
|
-
**A Python package for calculating reference evapotranspiration**
|
|
27
|
+
**A Python package for calculating reference and crop evapotranspiration**
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
31
|
-
ETo calculates reference evapotranspiration (ETo) using the [UN-FAO 56 paper](http://www.fao.org/docrep/X0490E/X0490E00.htm) methodology. It estimates missing meteorological parameters from available data and supports
|
|
31
|
+
ETo calculates reference evapotranspiration (ETo) and crop evapotranspiration (ETc) using the [UN-FAO 56 paper](http://www.fao.org/docrep/X0490E/X0490E00.htm) methodology. It estimates missing meteorological parameters from available data and supports daily, hourly, and monthly time steps.
|
|
32
|
+
|
|
33
|
+
## Features
|
|
34
|
+
|
|
35
|
+
- **FAO 56 Penman-Monteith** reference ET (short and tall reference crops)
|
|
36
|
+
- **Hargreaves** simplified daily ET
|
|
37
|
+
- **Crop evapotranspiration** — single Kc, dual Kc (Kcb + Ke), and water stress adjustment (Ks)
|
|
38
|
+
- **Built-in crop coefficients** for 23 major crops (FAO 56 Table 12)
|
|
39
|
+
- **Automatic parameter estimation** with quality tracking
|
|
40
|
+
- **Input validation** with configurable warnings
|
|
41
|
+
- **Derived outputs** — VPD, T_dew (back-calculated), clear-sky radiation
|
|
32
42
|
|
|
33
43
|
## Documentation
|
|
34
44
|
|
|
@@ -61,5 +71,13 @@ data = {
|
|
|
61
71
|
dates = np.arange('2020-01-01', '2020-01-04', dtype='datetime64[D]')
|
|
62
72
|
|
|
63
73
|
et = ETo(data, freq='D', z_msl=500, lat=-43.6, dates=dates)
|
|
74
|
+
|
|
75
|
+
# Reference ET
|
|
64
76
|
eto = et.eto_fao() # np.ndarray of ETo in mm
|
|
77
|
+
|
|
78
|
+
# Crop ET (single Kc)
|
|
79
|
+
etc = et.etc(crop='maize_grain', stage='mid') # ETc = Kc × ETo
|
|
80
|
+
|
|
81
|
+
# Tall reference crop (ASCE alfalfa)
|
|
82
|
+
etr = et.eto_fao(ref_crop='tall')
|
|
65
83
|
```
|
eto-2.2.0/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# ETo
|
|
2
|
+
|
|
3
|
+
**A Python package for calculating reference and crop evapotranspiration**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
ETo calculates reference evapotranspiration (ETo) and crop evapotranspiration (ETc) using the [UN-FAO 56 paper](http://www.fao.org/docrep/X0490E/X0490E00.htm) methodology. It estimates missing meteorological parameters from available data and supports daily, hourly, and monthly time steps.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **FAO 56 Penman-Monteith** reference ET (short and tall reference crops)
|
|
12
|
+
- **Hargreaves** simplified daily ET
|
|
13
|
+
- **Crop evapotranspiration** — single Kc, dual Kc (Kcb + Ke), and water stress adjustment (Ks)
|
|
14
|
+
- **Built-in crop coefficients** for 23 major crops (FAO 56 Table 12)
|
|
15
|
+
- **Automatic parameter estimation** with quality tracking
|
|
16
|
+
- **Input validation** with configurable warnings
|
|
17
|
+
- **Derived outputs** — VPD, T_dew (back-calculated), clear-sky radiation
|
|
18
|
+
|
|
19
|
+
## Documentation
|
|
20
|
+
|
|
21
|
+
Full documentation is available at [mullenkamp.github.io/ETo](https://mullenkamp.github.io/ETo/).
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip install eto
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
or:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
conda install -c mullenkamp eto
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The only dependency is [NumPy](https://numpy.org/).
|
|
36
|
+
|
|
37
|
+
## Quick Example
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import numpy as np
|
|
41
|
+
from eto import ETo
|
|
42
|
+
|
|
43
|
+
data = {
|
|
44
|
+
'T_min': np.array([10.0, 12.0, 8.0]),
|
|
45
|
+
'T_max': np.array([25.0, 28.0, 22.0]),
|
|
46
|
+
}
|
|
47
|
+
dates = np.arange('2020-01-01', '2020-01-04', dtype='datetime64[D]')
|
|
48
|
+
|
|
49
|
+
et = ETo(data, freq='D', z_msl=500, lat=-43.6, dates=dates)
|
|
50
|
+
|
|
51
|
+
# Reference ET
|
|
52
|
+
eto = et.eto_fao() # np.ndarray of ETo in mm
|
|
53
|
+
|
|
54
|
+
# Crop ET (single Kc)
|
|
55
|
+
etc = et.etc(crop='maize_grain', stage='mid') # ETc = Kc × ETo
|
|
56
|
+
|
|
57
|
+
# Tall reference crop (ASCE alfalfa)
|
|
58
|
+
etr = et.eto_fao(ref_crop='tall')
|
|
59
|
+
```
|
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
Class to estimate reference ET (ETo) from the FAO 56 paper using a minimum of T_min and T_max for daily estimates and T_mean and RH_mean for hourly, but utilizing the maximum number of available met parameters. The function prioritizes the estimation of specific parameters based on the available input data.
|
|
4
4
|
"""
|
|
5
5
|
import numpy as np
|
|
6
|
+
from eto.util import is_hourly
|
|
6
7
|
from eto.param_est import param_est
|
|
7
8
|
from eto.methods.ETo import eto_fao
|
|
8
9
|
from eto.methods.hargreaves import hargreaves
|
|
10
|
+
from eto.crop_coefficients import etc, etc_adj, kc_adjust
|
|
11
|
+
from eto.methods.dual_kc import etc_dual
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
class ETo(object):
|
|
@@ -21,18 +24,20 @@ class ETo(object):
|
|
|
21
24
|
T_min, T_max, T_mean, T_dew, RH_min, RH_max, RH_mean, n_sun, U_z,
|
|
22
25
|
P, e_a). All arrays must have the same length.
|
|
23
26
|
freq : str
|
|
24
|
-
Time frequency: 'D' for daily, '
|
|
27
|
+
Time frequency: 'D' for daily, 'h' (or 'H') for hourly, 'M' for monthly.
|
|
25
28
|
day_of_year : np.ndarray of int, or None
|
|
26
29
|
Day of year (1-366). Required if dates is not provided.
|
|
27
30
|
hour : np.ndarray of int, or None
|
|
28
31
|
Hour of day (0-23). Required for hourly frequency if dates is not provided.
|
|
29
32
|
dates : np.ndarray of datetime64, or None
|
|
30
33
|
Datetime array. Used to derive day_of_year and hour if they are not provided.
|
|
34
|
+
time_label, rs_rso_init
|
|
35
|
+
Hourly only; see :func:`eto.param_est.param_est`.
|
|
31
36
|
"""
|
|
32
37
|
|
|
33
38
|
def __init__(self, data=None, freq='D', z_msl=None, lat=None, lon=None, TZ_lon=None,
|
|
34
39
|
z_u=2, K_rs=0.16, a_s=0.25, b_s=0.5, alb=0.23,
|
|
35
|
-
day_of_year=None, hour=None, dates=None):
|
|
40
|
+
day_of_year=None, hour=None, dates=None, validate=True, time_label='start', rs_rso_init=None):
|
|
36
41
|
|
|
37
42
|
if data is None:
|
|
38
43
|
pass
|
|
@@ -49,7 +54,7 @@ class ETo(object):
|
|
|
49
54
|
if dates is not None:
|
|
50
55
|
dates = np.asarray(dates)
|
|
51
56
|
day_of_year = (dates.astype('datetime64[D]') - dates.astype('datetime64[Y]')).astype(int) + 1
|
|
52
|
-
if
|
|
57
|
+
if is_hourly(freq):
|
|
53
58
|
hour = (dates - dates.astype('datetime64[D]')).astype('timedelta64[h]').astype(int)
|
|
54
59
|
elif day_of_year is not None:
|
|
55
60
|
day_of_year = np.asarray(day_of_year)
|
|
@@ -59,7 +64,7 @@ class ETo(object):
|
|
|
59
64
|
# Validate temporal array lengths
|
|
60
65
|
if len(day_of_year) != n:
|
|
61
66
|
raise ValueError('day_of_year length must match data array length')
|
|
62
|
-
if
|
|
67
|
+
if is_hourly(freq):
|
|
63
68
|
if hour is None:
|
|
64
69
|
raise ValueError('hour array or dates must be provided for hourly frequency')
|
|
65
70
|
hour = np.asarray(hour)
|
|
@@ -67,10 +72,14 @@ class ETo(object):
|
|
|
67
72
|
raise ValueError('hour length must match data array length')
|
|
68
73
|
|
|
69
74
|
self.param_est(data, freq, z_msl, lat, lon, TZ_lon, z_u, K_rs, a_s, b_s, alb,
|
|
70
|
-
day_of_year=day_of_year, hour=hour
|
|
75
|
+
day_of_year=day_of_year, hour=hour, validate=validate,
|
|
76
|
+
time_label=time_label, rs_rso_init=rs_rso_init)
|
|
71
77
|
|
|
72
78
|
|
|
73
79
|
### Add in the ETo methods
|
|
74
80
|
ETo.param_est = param_est
|
|
75
81
|
ETo.eto_fao = eto_fao
|
|
76
82
|
ETo.eto_hargreaves = hargreaves
|
|
83
|
+
ETo.etc = etc
|
|
84
|
+
ETo.etc_dual = etc_dual
|
|
85
|
+
ETo.etc_adj = etc_adj
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Crop coefficient functions for ETc estimation (FAO 56).
|
|
4
|
+
"""
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# FAO 56 Table 12 — single crop coefficients (Kc_ini, Kc_mid, Kc_end)
|
|
9
|
+
KC_TABLE = {
|
|
10
|
+
'alfalfa': (0.40, 1.20, 1.15),
|
|
11
|
+
'barley': (0.30, 1.15, 0.25),
|
|
12
|
+
'beans_dry': (0.40, 1.15, 0.35),
|
|
13
|
+
'beans_green': (0.50, 1.05, 0.90),
|
|
14
|
+
'cabbage': (0.70, 1.05, 0.95),
|
|
15
|
+
'cotton': (0.35, 1.15, 0.70),
|
|
16
|
+
'grape': (0.30, 0.85, 0.45),
|
|
17
|
+
'groundnut': (0.40, 1.15, 0.60),
|
|
18
|
+
'lettuce': (0.70, 1.00, 0.95),
|
|
19
|
+
'maize_grain': (0.30, 1.20, 0.60),
|
|
20
|
+
'maize_sweet': (0.30, 1.15, 1.05),
|
|
21
|
+
'millet': (0.30, 1.00, 0.30),
|
|
22
|
+
'onion_dry': (0.70, 1.05, 0.75),
|
|
23
|
+
'peanut': (0.40, 1.15, 0.60),
|
|
24
|
+
'potato': (0.50, 1.15, 0.75),
|
|
25
|
+
'rice': (1.05, 1.20, 0.90),
|
|
26
|
+
'sorghum': (0.30, 1.00, 0.55),
|
|
27
|
+
'soybean': (0.40, 1.15, 0.50),
|
|
28
|
+
'sugarcane': (0.40, 1.25, 0.75),
|
|
29
|
+
'sunflower': (0.35, 1.15, 0.35),
|
|
30
|
+
'tomato': (0.60, 1.15, 0.80),
|
|
31
|
+
'wheat_winter': (0.70, 1.15, 0.25),
|
|
32
|
+
'wheat_spring': (0.30, 1.15, 0.25),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def kc_adjust(Kc, u2, RH_min, h):
|
|
37
|
+
"""
|
|
38
|
+
Climate adjustment of Kc for non-standard conditions (FAO 56 Eq 62).
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
Kc : float
|
|
43
|
+
Tabulated crop coefficient (Kc_mid or Kc_end).
|
|
44
|
+
u2 : float
|
|
45
|
+
Mean daily wind speed at 2 m (m/s).
|
|
46
|
+
RH_min : float
|
|
47
|
+
Mean minimum relative humidity (%).
|
|
48
|
+
h : float
|
|
49
|
+
Mean plant height (m).
|
|
50
|
+
|
|
51
|
+
Returns
|
|
52
|
+
-------
|
|
53
|
+
float
|
|
54
|
+
Adjusted Kc.
|
|
55
|
+
"""
|
|
56
|
+
return Kc + (0.04*(u2 - 2) - 0.004*(RH_min - 45)) * (h/3)**0.3
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def etc(self, Kc=None, crop=None, stage=None, **kwargs):
|
|
60
|
+
"""
|
|
61
|
+
Crop evapotranspiration ETc = Kc * ETo (FAO 56 Eq 58, single Kc).
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
Kc : float or np.ndarray, optional
|
|
66
|
+
Crop coefficient. If not provided, looked up from KC_TABLE.
|
|
67
|
+
crop : str, optional
|
|
68
|
+
Crop name (key in KC_TABLE). Required if Kc is not provided.
|
|
69
|
+
stage : str, optional
|
|
70
|
+
Growth stage: 'ini', 'mid', or 'end'. Required if crop is provided
|
|
71
|
+
without Kc.
|
|
72
|
+
**kwargs
|
|
73
|
+
Passed to eto_fao().
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
np.ndarray
|
|
78
|
+
ETc in mm.
|
|
79
|
+
"""
|
|
80
|
+
if Kc is None:
|
|
81
|
+
if crop is None:
|
|
82
|
+
raise ValueError('Either Kc or crop must be provided')
|
|
83
|
+
crop_lower = crop.lower()
|
|
84
|
+
if crop_lower not in KC_TABLE:
|
|
85
|
+
raise ValueError(f'Unknown crop: {crop}. Available: {sorted(KC_TABLE.keys())}')
|
|
86
|
+
if stage is None:
|
|
87
|
+
raise ValueError('stage must be provided when using crop lookup')
|
|
88
|
+
stage_idx = {'ini': 0, 'mid': 1, 'end': 2}
|
|
89
|
+
if stage not in stage_idx:
|
|
90
|
+
raise ValueError(f"stage must be 'ini', 'mid', or 'end', got '{stage}'")
|
|
91
|
+
Kc = KC_TABLE[crop_lower][stage_idx[stage]]
|
|
92
|
+
|
|
93
|
+
return np.asarray(Kc) * self.eto_fao(**kwargs)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def etc_adj(self, Kc, TAW, Dr, p=0.5, **kwargs):
|
|
97
|
+
"""
|
|
98
|
+
Adjusted ETc under water stress (FAO 56 Eq 84).
|
|
99
|
+
|
|
100
|
+
ETc_adj = Ks * Kc * ETo
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
Kc : float or np.ndarray
|
|
105
|
+
Crop coefficient.
|
|
106
|
+
TAW : float or np.ndarray
|
|
107
|
+
Total available water in root zone (mm).
|
|
108
|
+
Dr : float or np.ndarray
|
|
109
|
+
Root zone depletion (mm).
|
|
110
|
+
p : float
|
|
111
|
+
Depletion fraction for no stress (default 0.5).
|
|
112
|
+
**kwargs
|
|
113
|
+
Passed to eto_fao().
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
np.ndarray
|
|
118
|
+
ETc_adj in mm.
|
|
119
|
+
"""
|
|
120
|
+
TAW = np.asarray(TAW, dtype=np.float64)
|
|
121
|
+
Dr = np.asarray(Dr, dtype=np.float64)
|
|
122
|
+
Ks = np.clip((TAW - Dr) / ((1 - p) * TAW), 0, 1)
|
|
123
|
+
return Ks * np.asarray(Kc) * self.eto_fao(**kwargs)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Function to estimate reference ET (ETo) from the FAO 56 paper using a minimum of T_min and T_max for daily estimates and T_mean and RH_mean for hourly, but utilizing the maximum number of available met parameters.
|
|
4
|
+
"""
|
|
5
|
+
import numpy as np
|
|
6
|
+
from eto.util import is_hourly
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def eto_fao(self, max_ETo=15, min_ETo=0, ref_crop='short', decimals=2):
|
|
10
|
+
"""
|
|
11
|
+
Function to estimate reference ET (ETo) from the `FAO 56 paper <http://www.fao.org/docrep/X0490E/X0490E00.htm>`_ [1]_ using a minimum of T_min and T_max for daily estimates and T_mean and RH_mean for hourly, but optionally utilising the maximum number of available met parameters.
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
max_ETo : float, int or None
|
|
16
|
+
The max realistic value of ETo (mm); higher values become NaN. None disables the check.
|
|
17
|
+
min_ETo : float, int or None
|
|
18
|
+
The min realistic value of ETo (mm); lower values are raised to it. None keeps negative
|
|
19
|
+
values (raw FAO-56, e.g. night-time dew).
|
|
20
|
+
ref_crop : str
|
|
21
|
+
Reference crop type: 'short' (FAO 56 grass) or 'tall' (ASCE alfalfa).
|
|
22
|
+
decimals : int or None
|
|
23
|
+
Decimal places of the result; None returns it unrounded.
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
np.ndarray
|
|
28
|
+
Estimated ETo in mm.
|
|
29
|
+
|
|
30
|
+
References
|
|
31
|
+
----------
|
|
32
|
+
|
|
33
|
+
.. [1] Allen, R. G., Pereira, L. S., Raes, D., & Smith, M. (1998). Crop evapotranspiration-Guidelines for computing crop water requirements-FAO Irrigation and drainage paper 56. FAO, Rome, 300(9), D05109.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
######
|
|
37
|
+
## ETo equation — select Cn and Cd based on ref_crop and frequency
|
|
38
|
+
if is_hourly(self.freq):
|
|
39
|
+
if ref_crop == 'tall':
|
|
40
|
+
Cn = 66
|
|
41
|
+
Cd = np.where(self.ts_param['R_n'] > 0, 0.25, 1.7)
|
|
42
|
+
else:
|
|
43
|
+
Cn = 37
|
|
44
|
+
Cd = 0.34
|
|
45
|
+
ETo_FAO = (0.408*self.ts_param['delta']*(self.ts_param['R_n'] - self.ts_param['G']) + self.ts_param['gamma']*Cn/(self.ts_param['T_mean'] + 273)*self.ts_param['U_2']*(self.ts_param['e_mean'] - self.ts_param['e_a']))/(self.ts_param['delta'] + self.ts_param['gamma']*(1 + Cd*self.ts_param['U_2']))
|
|
46
|
+
else:
|
|
47
|
+
if ref_crop == 'tall':
|
|
48
|
+
Cn = 1600
|
|
49
|
+
Cd = 0.38
|
|
50
|
+
else:
|
|
51
|
+
Cn = 900
|
|
52
|
+
Cd = 0.34
|
|
53
|
+
ETo_FAO = (0.408*self.ts_param['delta']*(self.ts_param['R_n'] - self.ts_param['G']) + self.ts_param['gamma']*Cn/(self.ts_param['T_mean'] + 273)*self.ts_param['U_2']*(self.ts_param['e_s'] - self.ts_param['e_a']))/(self.ts_param['delta'] + self.ts_param['gamma']*(1 + Cd*self.ts_param['U_2']))
|
|
54
|
+
|
|
55
|
+
## Clamp negatives to min_ETo, NaN for suspect highs
|
|
56
|
+
if min_ETo is not None:
|
|
57
|
+
ETo_FAO = np.maximum(ETo_FAO, min_ETo)
|
|
58
|
+
if max_ETo is not None:
|
|
59
|
+
ETo_FAO = np.where(ETo_FAO > max_ETo, np.nan, ETo_FAO)
|
|
60
|
+
|
|
61
|
+
return ETo_FAO if decimals is None else np.round(ETo_FAO, decimals)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Dual crop coefficient method (FAO 56 Eq 58).
|
|
4
|
+
"""
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def etc_dual(self, Kcb, Ke=None, Kr=None, Kc_max=None, few=None, **kwargs):
|
|
9
|
+
"""
|
|
10
|
+
Dual crop coefficient ETc = (Kcb + Ke) * ETo (FAO 56 Eq 58).
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
Kcb : float or np.ndarray
|
|
15
|
+
Basal crop coefficient.
|
|
16
|
+
Ke : float or np.ndarray, optional
|
|
17
|
+
Soil evaporation coefficient. If not provided, estimated from
|
|
18
|
+
Kr, Kc_max, few, and Kcb.
|
|
19
|
+
Kr : float or np.ndarray, optional
|
|
20
|
+
Evaporation reduction coefficient (0-1). Required if Ke not provided.
|
|
21
|
+
Kc_max : float or np.ndarray, optional
|
|
22
|
+
Maximum Kc following rain/irrigation. Required if Ke not provided.
|
|
23
|
+
few : float or np.ndarray, optional
|
|
24
|
+
Fraction of soil surface wetted and exposed (0-1). Required if Ke
|
|
25
|
+
not provided.
|
|
26
|
+
**kwargs
|
|
27
|
+
Passed to eto_fao().
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
np.ndarray
|
|
32
|
+
ETc in mm.
|
|
33
|
+
"""
|
|
34
|
+
if Ke is None:
|
|
35
|
+
if Kr is None or Kc_max is None or few is None:
|
|
36
|
+
raise ValueError('When Ke is not provided, Kr, Kc_max, and few are required')
|
|
37
|
+
Kcb = np.asarray(Kcb, dtype=np.float64)
|
|
38
|
+
Kr = np.asarray(Kr, dtype=np.float64)
|
|
39
|
+
Kc_max = np.asarray(Kc_max, dtype=np.float64)
|
|
40
|
+
few = np.asarray(few, dtype=np.float64)
|
|
41
|
+
# FAO 56 Eq 71: Ke = Kr * (Kc_max - Kcb), limited by few * Kc_max
|
|
42
|
+
Ke = np.minimum(Kr * (Kc_max - Kcb), few * Kc_max)
|
|
43
|
+
|
|
44
|
+
return (np.asarray(Kcb) + np.asarray(Ke)) * self.eto_fao(**kwargs)
|
|
@@ -5,6 +5,7 @@ Created on Sun May 13 09:45:39 2018
|
|
|
5
5
|
@author: MichaelEK
|
|
6
6
|
"""
|
|
7
7
|
import numpy as np
|
|
8
|
+
from eto.util import is_hourly
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def hargreaves(self, max_ETo=15, min_ETo=0):
|
|
@@ -27,13 +28,13 @@ def hargreaves(self, max_ETo=15, min_ETo=0):
|
|
|
27
28
|
######
|
|
28
29
|
## ETo equation
|
|
29
30
|
|
|
30
|
-
if
|
|
31
|
+
if is_hourly(self.freq):
|
|
31
32
|
raise ValueError('Hargreaves should not be calculated at time frequencies of less than a day.')
|
|
32
33
|
|
|
33
34
|
ETo_Har = 0.0023*(self.ts_param['T_mean'] + 17.8)*((self.ts_param['T_max'] - self.ts_param['T_min']) **0.5)*self.ts_param['R_a']*0.408
|
|
34
35
|
|
|
35
|
-
##
|
|
36
|
+
## Clamp negatives to min_ETo, NaN for suspect highs
|
|
37
|
+
ETo_Har = np.maximum(ETo_Har, min_ETo)
|
|
36
38
|
ETo_Har[ETo_Har > max_ETo] = np.nan
|
|
37
|
-
ETo_Har[ETo_Har < min_ETo] = np.nan
|
|
38
39
|
|
|
39
40
|
return np.round(ETo_Har, 2)
|