├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── pull_request_template.md
└── workflows
│ ├── ci-testing.yml
│ ├── ci.yml
│ └── python-publish.yml
├── .gitignore
├── .readthedocs.yaml
├── .zenodo.json
├── CITATION.cff
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── docs
├── Makefile
├── _static
│ └── logo.png
├── _templates
│ └── autosummary
│ │ ├── class.rst
│ │ └── module.rst
├── api
│ └── index.rst
├── conf.py
├── examples
│ ├── 01_example_zamg.ipynb
│ ├── 02_example_zamg_netcdf.ipynb
│ ├── 03_example_knmi.ipynb
│ ├── 04_example_coagmet.ipynb
│ ├── 05_example_calibration.ipynb
│ ├── 06_worked_examples_McMahon_etal_2013.ipynb
│ ├── 07_example_climate_change.ipynb
│ ├── 08_crop_coefficient.ipynb
│ ├── 09_CMIP6_data.ipynb
│ ├── 10_example_paper.ipynb
│ ├── data
│ │ ├── example_1
│ │ │ └── klima_daily.csv
│ │ ├── example_10
│ │ │ ├── 10_example_meteo.csv
│ │ │ ├── co2_conc
│ │ │ │ ├── RCP3PD_MIDYR_CONC.DAT
│ │ │ │ ├── RCP45_MIDYR_CONC.DAT
│ │ │ │ ├── RCP6_MIDYR_CONC.DAT
│ │ │ │ └── RCP85_MIDYR_CONC.DAT
│ │ │ ├── df_Guo_2016.xlsx
│ │ │ ├── elev_ens_0.25deg_reg_v25.0e.nc
│ │ │ ├── fg_ens_mean_0.25deg_reg_2018_v25.0e.nc
│ │ │ ├── hu_ens_mean_0.25deg_reg_2018_v25.0e.nc
│ │ │ ├── qq_ens_mean_0.25deg_reg_2018_v25.0e.nc
│ │ │ ├── spartacus-daily_19610101T0000_20211231T0000.nc
│ │ │ ├── tasAdjust_AUT_AT.ST_area_annual.csv
│ │ │ ├── tg_ens_mean_0.25deg_reg_2018_v25.0e.nc
│ │ │ ├── tn_ens_mean_0.25deg_reg_2018_v25.0e.nc
│ │ │ └── tx_ens_mean_0.25deg_reg_2018_v25.0e.nc
│ │ ├── example_2
│ │ │ └── incal_hourly_20120501T0000_20120930T2300.nc
│ │ ├── example_3
│ │ │ └── etmgeg_260.txt
│ │ ├── example_4
│ │ │ └── et_coagmet.txt
│ │ └── example_9
│ │ │ └── tas_day_EC-Earth3_ssp119_r4i1p1f1_gr_21000601-21000630_v20200425.nc
│ ├── index.rst
│ └── utils.py
├── index.rst
├── make.bat
├── publications.rst
├── references.rst
└── user_guide
│ ├── index.rst
│ ├── installation.rst
│ ├── methods.rst
│ └── units.rst
├── methods.xlsx
├── pyet
├── __init__.py
├── combination.py
├── meteo_utils.py
├── rad_utils.py
├── radiation.py
├── temperature.py
├── utils.py
└── version.py
├── pyproject.toml
├── requirements.txt
└── tests
├── __init__.py
├── readme.rst
├── test_all.py
├── test_rpackage.py
├── testalternative.py
└── testfao56.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | # Short Description
2 | Add a short description describing the pull request (PR) here.
3 |
4 | # Checklist before PR can be merged:
5 | - [ ] closes issue #xxxx
6 | - [ ] is documented
7 | - [ ] Format code with [Black formatting](https://black.readthedocs.io)
8 | - [ ] type hints for functions and methods
9 | - [ ] tests added / passed
10 | - [ ] Example Notebook (for new features)
11 | - [ ] Remove output for all notebooks with changes
12 |
--------------------------------------------------------------------------------
/.github/workflows/ci-testing.yml:
--------------------------------------------------------------------------------
1 | # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
3 |
4 | name: Python package
5 |
6 | on:
7 | push:
8 | branches: [ "master", "dev" ]
9 | pull_request:
10 | branches: [ "master", "dev" ]
11 |
12 | jobs:
13 | build:
14 |
15 | runs-on: ubuntu-latest
16 | strategy:
17 | fail-fast: false
18 | matrix:
19 | python-version: ["3.9", "3.10", "3.11"]
20 |
21 | steps:
22 | - uses: actions/checkout@v3
23 | - name: Set up Python ${{ matrix.python-version }}
24 | uses: actions/setup-python@v3
25 | with:
26 | python-version: ${{ matrix.python-version }}
27 | check-latest: true
28 | cache: "pip"
29 | cache-dependency-path: requirements.txt
30 | - name: Install dependencies
31 | run: |
32 | python -m pip install --upgrade pip
33 | pip install -r requirements.txt
34 | - name: Lint with flake8
35 | run: |
36 | # stop the build if there are Python syntax errors or undefined names
37 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
38 | # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
39 | flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
40 | - name: Test with pytest
41 | run: |
42 | pytest
43 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: codacy-coverage-reporter
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | branches:
9 | - master
10 |
11 | jobs:
12 | test:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | python-version: ['3.9', '3.10', '3.11', '3.12',]
17 |
18 | steps:
19 | - uses: actions/checkout@v4
20 | - name: Set up Python ${{ matrix.python-version }}
21 | uses: actions/setup-python@v5
22 | with:
23 | python-version: ${{ matrix.python-version }}
24 | - name: Install dependencies
25 | run: |
26 | python -m pip install --upgrade pip
27 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
28 | pip install codacy-coverage
29 | pip install coverage
30 | pip install -e .
31 |
32 | - name: Test with unittest
33 | run: |
34 | coverage run -m unittest discover
35 | coverage xml
36 |
37 | - name: Run codacy-coverage-reporter
38 | uses: codacy/codacy-coverage-reporter-action@master
39 | with:
40 | project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
41 | coverage-reports: coverage.xml
42 |
--------------------------------------------------------------------------------
/.github/workflows/python-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will upload a Python Package using Twine when a release is created
2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
3 |
4 | name: Upload Python Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | deploy:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v4
17 | - name: Set up Python
18 | uses: actions/setup-python@v5
19 | with:
20 | python-version: '3.x'
21 | - name: Install dependencies
22 | run: |
23 | python -m pip install --upgrade pip
24 | pip install setuptools wheel twine build
25 | - name: Build package
26 | run: python -m build
27 | - name: Upload Python Package
28 | uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
29 | with:
30 | user: __token__
31 | password: ${{ secrets.PYPY_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | *.xml
3 |
4 | *.iml
5 |
6 | *.pyc
7 |
8 | *.so
9 |
10 | pyet.egg-info
11 |
12 | examples/notebooks/.ipynb_checkpoints/
13 | build
14 | dist
15 |
16 | .idea/*
17 | .idea/.name
18 | .cache/v/cache/lastfailed
19 | *.log
20 |
21 | **/*.ipynb_checkpoints/
22 |
23 | .vscode/*
24 |
25 | .DS_Store
26 |
--------------------------------------------------------------------------------
/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | build:
9 | os: ubuntu-22.04
10 | tools:
11 | python: "3.11"
12 |
13 | # Build documentation in the docs/ directory with Sphinx
14 | sphinx:
15 | configuration: docs/conf.py
16 |
17 | # Build documentation with MkDocs
18 | #mkdocs:
19 | # configuration: mkdocs.yml
20 |
21 | # Optionally build your docs in additional formats such as PDF and ePub
22 | #formats:
23 | # - epub
24 | # - pdf
25 |
26 | # Optionally set the version of Python and requirements required to build your docs
27 | python:
28 | install:
29 | - method: pip
30 | path: .
31 | extra_requirements:
32 | - rtd
--------------------------------------------------------------------------------
/.zenodo.json:
--------------------------------------------------------------------------------
1 | {
2 | "description": "PyEt is an open source python package for calculating reference and potential Evapotranspiration for 1D (pandas.Series) and 3D (xarray.DataArrray) data.",
3 | "license": "MIT",
4 | "title": "PyEt: A Python package for estimating potential evapotranspiration",
5 | "upload_type": "software",
6 | "creators": [
7 | {
8 | "affiliation": "University of Graz, Graz, Austria.",
9 | "name": "Matevz Vremec",
10 | "orcid": "0000-0002-2730-494X"
11 | },
12 | {
13 | "affiliation": "Eawag, Dübendorf, Switzerland.",
14 | "name": "Raoul Collenteur",
15 | "orcid": "0000-0001-7843-1538"
16 | }
17 | ],
18 | "access_right": "open",
19 | "keywords": [
20 | "python",
21 | "evaporation",
22 | "hydrology",
23 | "water balance",
24 | "open source"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/CITATION.cff:
--------------------------------------------------------------------------------
1 | cff-version: 1.2.0
2 | message: "If you use this software, please cite it as below."
3 | authors:
4 | - family-names: "Vremec"
5 | given-names: "Matevz"
6 | orcid: "https://orcid.org/0000-0002-2730-494X"
7 | - family-names: "Collenteur"
8 | given-names: "Matevz"
9 | orcid: "https://orcid.org/0000-0001-7843-1538"
10 | - family-names: "Birk"
11 | given-names: "Steffen"
12 | orcid: "https://orcid.org/0000-0001-7474-3884"
13 | title: "Technical note: Improved handling of potential evapotranspiration in hydrological studies with PyEt"
14 | doi: https://doi.org/10.5194/hess-2022-417
15 | date-released: 2023
16 | url: "https://github.com/phydrus/pyet"
17 | preferred-citation:
18 | type: journal
19 | authors:
20 | - family-names: "Vremec"
21 | given-names: "Matevz"
22 | orcid: "https://orcid.org/0000-0002-2730-494X"
23 | - family-names: "Collenteur"
24 | given-names: "Matevz"
25 | orcid: "https://orcid.org/0000-0001-7843-1538"
26 | - family-names: "Birk"
27 | given-names: "Steffen"
28 | orcid: "https://orcid.org/0000-0001-7474-3884"
29 | doi: "https://doi.org/10.5194/hess-2022-417"
30 | journal: "Hydrol. Earth Syst. Sci. Discuss. [preprint]"
31 | year: "2023"
32 | title: "Technical note: Improved handling of potential evapotranspiration in hydrological studies with PyEt"
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | @mvremec.
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 phydrus
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # pyet: Estimation of Potential Evapotranspiration
4 |
5 | [](https://github.com/pyet-org/pyet/actions/workflows/ci.yml)
6 |
7 |
8 | [](https://www.codacy.com/gh/phydrus/pyet/dashboard?utm_source=github.com&utm_medium=referral&utm_content=phydrus/pyet&utm_campaign=Badge_Grade)
9 | [](https://www.codacy.com/gh/phydrus/pyet/dashboard?utm_source=github.com&utm_medium=referral&utm_content=phydrus/pyet&utm_campaign=Badge_Coverage)
10 |
11 |
12 |
13 | pyet is an open source python package for calculating reference and potential Evapotranspiration (PET) for 1D (pandas.Series)
14 | and 3D (xarray.DataArrray) data. Currently, eighteen methods for calculating daily PET are implemented:
15 |
16 | | Method name | pyet function | T | RH | R | u2 | Lat. | El. | Benchmarked? |
17 | |:------------------|:------------------|:-------|:-----------|:-------|:-------|:-------|:-------|:------------------|
18 | | Penman | penman | ✓ $^a$ | ✓ $^{b,c}$ | ✓ $^d$ | ✓ | ✓ $^d$ | ✓ $^e$ | ✓ |
19 | | Penman-Monteith | pm | ✓ $^a$ | ✓ $^{b,c}$ | ✓ $^d$ | ✓ | ✓ $^d$ | ✓ $^e$ | ✓ |
20 | | ASCE-PM | pm_asce | ✓ $^a$ | ✓ $^{b,c}$ | ✓ $^d$ | ✓ | ✓ $^d$ | ✓ $^e$ | ✓ |
21 | | FAO-56 | pm_fao56 | ✓ $^a$ | ✓ $^{b,c}$ | ✓ $^d$ | ✓ | ✓ $^d$ | ✓ $^e$ | ✓ |
22 | | Priestley-Taylor | priestley_taylor | ✓ | ✓ $^h$ | ✓ $^h$ | - | ✓ $^h$ | ✓ $^e$ | ✓ |
23 | | Kimberly-Penman | kimberly_penman | ✓ $^a$ | ✓ $^{b,c}$ | ✓ $^d$ | ✓ | ✓ $^d$ | ✓ $^e$ | - |
24 | | Thom-Oliver | thom_oliver | ✓ $^a$ | ✓ $^{b,c}$ | ✓ $^d$ | ✓ | ✓ $^d$ | ✓ $^e$ | - |
25 | | Blaney-Criddle | blaney_criddle | ✓ | - $^i$ | - $^i$ | - $^i$ | ✓ | - | ✓ |
26 | | Hamon | hamon | ✓ | - | - | - | ✓ | - | ✓ |
27 | | Romanenko | romanenko | ✓ | ✓ | - | - | - | - | ✓ |
28 | | Linacre | linacre | ✓ $^j$ | - | - | - | - | ✓ | ✓ |
29 | | Haude | haude | ✓ | ✓ $^k$ | - | - | - | - | ✓ |
30 | | Turc | turc | ✓ | ✓ | ✓ | - | - | - | ✓ |
31 | | Jensen-Haise | jensen_haise | ✓ | - | ✓ $^l$ | - | ✓ $^l$ | - | ✓ |
32 | | McGuinness-Bordne | mcguinness_bordne | ✓ | - | - | - | ✓ | - | ✓ |
33 | | Hargreaves | hargreaves | ✓ $^m$ | - | - | - | ✓ | - | ✓ |
34 | | FAO-24 radiation | fao_24 | ✓ | ✓ | ✓ | ✓ | - | ✓ $^e$ | - |
35 | | Abtew | abtew | ✓ | - | ✓ | - | - | - | ✓ |
36 | | Makkink | makkink | ✓ | - | ✓ | - | - | ✓ $^e$ | ✓ |
37 | | Oudin | oudin | ✓ | - | - | - | ✓ | - | - |
38 |
39 | $^a$ $T_{max}$ and $T_{min}$ can also be provided. $^b$ $RH_{max}$ and $RH_{min}$ can also be provided. $^c$ If actual vapor pressure is provided, RH is not needed. $^d$ Input for radiation can be (1) Net radiation, (2) solar radiation or (3) sunshine hours. If (1), then latitude is not needed. If (1, 3) latitude and elevation is needed. $^e$ One must provide either the atmospheric pressure or elevation. $^f$ The PM method can be used to estimate potential crop evapotranspiration, if leaf area index or crop height data is available. $^g$ The effect of $CO_2$ on stomatal resistance can be included using the formulation of Yang et al. 2019. $^h$ If net radiation is provided, RH and Lat are not needed. $^i$ If method==2, $u_2$, $RH_{min}$ and sunshine hours are required. $^j$ Additional input of $T_{max}$ and $T_{min}$, or $T_{dew}$. $^k$ Input can be $RH$ or actual vapor pressure. $^l$ If method==1, latitude is needed instead of $R_s$. $^m$ $T_{max}$ and $T_{min}$ also needed.
40 |
41 | ## Examples and Documentation
42 |
43 | Examples of using *pyet* can be found in the example folder:
44 |
45 | * [Example 1](): Estimating PET using pandas.Series
46 |
47 | * [Example 2](): Estimating PET using xarray.DataArray
48 |
49 | * [Example 3](): Benchmarking Makkink
50 | against [KNMI data](https://www.knmi.nl/over-het-knmi/about)
51 |
52 | * [Example 4](): Benchmarking FAO56
53 | against [CoAgMET data](https://coagmet.colostate.edu/)
54 |
55 | * [Example 5](): Calibrating the Romanenko and Abtew method against the PM-FAO56
56 |
57 | * [Example 6](): Worked examples for estimating meteorological
58 | variables and potential evapotranspiration after McMahon et al., 2013
59 |
60 | * [Example 7](): Example for estimating potential evapotranspiration under
61 | warming and elevated $CO_2$ concentrations following Yang et al., (2019)
62 |
63 | * [Example 8](): Determining the crop coefficient function with Python
64 |
65 | * [Example 9](): Estimating PET using CMIP data
66 |
67 | * [Example 10](): Notebook supporting PyEt manuscript
68 |
69 | Documentation is hosted on [ReadTheDocs](https://pyet.readthedocs.io).
70 |
71 | After defining the input data, evapotranspiration is estimated using only one
72 | line of python code:
73 |
74 | `>>> pyet.pm_fao56(tmean, wind, rn=rn, tmax=tmax, tmin=tmin, rh=rh, elevation=elevation)`
75 |
76 | We support Python >= 3.8.
77 |
78 | ## Benchmarking
79 |
80 | Most of the methods implemented in *pyet* are benchmarked against literature values from the [FAO Irrigation and
81 | drainage paper 56](https://www.fao.org/3/x0490e/x0490e00.htm), [McMahon et al., 2013 (supplementary)](https://hess.copernicus.org/articles/17/4865/2013/) and [Schrödter, 1985](https://link.springer.com/book/10.1007/978-3-642-70434-5). In addition, two comparative analysis between daily PE estimated with *pyet* and other organizations is
82 | made:
83 |
84 | * `pyet.pm_fao56` against daily PET estimated with ASCE Penman-Monteith from [CoAgMET](https://coagmet.colostate.edu/) (
85 | Colorado State University),
86 |
87 | * `pyet.makkink` against daily PET estimated with Makkink from The Royal Netherlands Meteorological
88 | Institute ([KNMI](https://www.knmi.nl/over-het-knmi/about)).
89 |
90 | ## Data dimensions
91 |
92 | As of version v1.2., *pyet* is compatible with both Pandas.Series and xarray.DataArray, which means you can now estimate
93 | potential evapotranspiration for both point and gridded data.
94 |
95 | ## Bug reports and Questions
96 |
97 | pyet is in active development, and bug reports are welcome as [GitHub
98 | Issues](https://github.com/phydrus/pyet/issues).
99 | General questions or discussions are possible through
100 | [GitHub Discussions](https://github.com/phydrus/pyet/discussions).
101 |
102 | ## Installation
103 |
104 | The *pyet* package is available from the Pypi package index and can be installed
105 | as follows::
106 |
107 | `>>> pip install pyet`
108 |
109 | To install in developer mode, use the following syntax:
110 |
111 | `>>> pip install -e .`
112 |
113 | ## Citing
114 |
115 | If you use *pyet* in one of your studies, please cite the *pyet* EGU abstract:
116 |
117 | * Vremec, M., Collenteur, R. A., and Birk, S.: Technical note: Improved handling of potential evapotranspiration in
118 | hydrological studies with PyEt, Hydrol. Earth Syst. Sci. Discuss. [preprint], https://doi.org/10.5194/hess-2022-417,
119 | in review, 2023.
120 |
121 | ```Reference
122 | @Article{hess-2022-417,
123 | AUTHOR = {Vremec, M. and Collenteur, R. A. and Birk, S.},
124 | TITLE = {Technical note: Improved handling of potential evapotranspiration in hydrological studies with \textit{PyEt}},
125 | JOURNAL = {Hydrology and Earth System Sciences Discussions},
126 | VOLUME = {2023},
127 | YEAR = {2023},
128 | PAGES = {1--23},
129 | URL = {https://hess.copernicus.org/preprints/hess-2022-417/},
130 | DOI = {10.5194/hess-2022-417}
131 | }
132 | ```
133 |
--------------------------------------------------------------------------------
/docs/Makefile:
--------------------------------------------------------------------------------
1 | # Minimal makefile for Sphinx documentation
2 | #
3 |
4 | # You can set these variables from the command line, and also
5 | # from the environment for the first two.
6 | SPHINXOPTS ?=
7 | SPHINXBUILD ?= sphinx-build
8 | SOURCEDIR = .
9 | BUILDDIR = _build
10 |
11 | # Put it first so that "make" without argument is like "make help".
12 | help:
13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
14 |
15 | .PHONY: help Makefile
16 |
17 | # Catch-all target: route all unknown targets to Sphinx using the new
18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
19 | %: Makefile
20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
21 |
--------------------------------------------------------------------------------
/docs/_static/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/_static/logo.png
--------------------------------------------------------------------------------
/docs/_templates/autosummary/class.rst:
--------------------------------------------------------------------------------
1 | {{ objname | escape | underline}}
2 |
3 | .. currentmodule:: {{ module }}
4 |
5 | .. autoclass:: {{ objname }}
6 |
7 | {% block attributes %}
8 | {% if attributes %}
9 | Attributes
10 | ----------
11 | .. autosummary::
12 | {% for item in attributes %}
13 | ~{{ name }}.{{ item }}
14 | {%- endfor %}
15 | {% endif %}
16 | {% endblock %}
17 |
18 | {% block methods %}
19 | {% if methods %}
20 | Methods
21 | -------
22 | .. autosummary::
23 | :nosignatures:
24 | :toctree: ./generated
25 | {% for item in methods %}
26 | ~{{ name }}.{{ item }}
27 | {%- endfor %}
28 | {% endif %}
29 | {% endblock %}
30 |
--------------------------------------------------------------------------------
/docs/_templates/autosummary/module.rst:
--------------------------------------------------------------------------------
1 | {{ fullname | escape | underline}}
2 |
3 | .. automodule:: {{ fullname }}
4 |
5 | {% block attributes %}
6 | {% if attributes %}
7 | .. rubric:: Module Attributes
8 |
9 | .. autosummary::
10 | :toctree: ./generated
11 | :nosignatures:
12 | {% for item in attributes %}
13 | {{ item }}
14 | {%- endfor %}
15 | {% endif %}
16 | {% endblock %}
17 |
18 | {% block functions %}
19 | {% if functions %}
20 | .. rubric:: {{ ('Functions') }}
21 |
22 | .. autosummary::
23 | :toctree: ./generated
24 | :nosignatures:
25 | {% for item in functions %}
26 | {{ item }}
27 | {%- endfor %}
28 | {% endif %}
29 | {% endblock %}
30 |
31 | {% block classes %}
32 | {% if classes %}
33 | .. rubric:: {{ ('Classes') }}
34 |
35 | .. autosummary::
36 | :toctree: ./generated
37 | :nosignatures:
38 | {% for item in classes %}
39 | {{ item }}
40 | {%- endfor %}
41 | {% endif %}
42 | {% endblock %}
43 |
44 | {% block exceptions %}
45 | {% if exceptions %}
46 | .. rubric:: {{ ('Exceptions') }}
47 |
48 | .. autosummary::
49 | :toctree: ./generated
50 | {% for item in exceptions %}
51 | {{ item }}
52 | {%- endfor %}
53 | {% endif %}
54 | {% endblock %}
55 |
56 | {% block modules %}
57 | {% if modules %}
58 | .. rubric:: Modules
59 |
60 | .. autosummary::
61 | :toctree: ./generated
62 | :template: module.rst
63 | :recursive:
64 | {% for item in modules %}
65 | {{ item }}
66 | {%- endfor %}
67 | {% endif %}
68 | {% endblock %}
--------------------------------------------------------------------------------
/docs/api/index.rst:
--------------------------------------------------------------------------------
1 | API-docs
2 | ========
3 | This section contains the Documentation of the Application Programming
4 | Interface (API) of pyet. The information in this section is automatically
5 | created from the documentation strings in original Python code. In the
6 | left-hand menu you will find the different categories of the API documentation.
7 |
8 | .. currentmodule:: pyet
9 |
10 | .. autosummary::
11 | :toctree: ./generated
12 | :nosignatures:
13 | :recursive:
14 |
15 | combination
16 | temperature
17 | radiation
18 | meteo_utils
19 | rad_utils
20 | utils
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | # Configuration file for the Sphinx documentation builder.
2 | #
3 | # This file only contains a selection of the most common options. For a full
4 | # list see the documentation:
5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html
6 |
7 | # -- Path setup --------------------------------------------------------------
8 |
9 | # If extensions (or modules to document with autodoc) are in another directory,
10 | # add these directories to sys.path here. If the directory is relative to the
11 | # documentation root, use os.path.abspath to make it absolute, like shown here.
12 | #
13 | import os
14 | import sys
15 | from datetime import date
16 |
17 | import requests
18 |
19 | year = date.today().strftime("%Y")
20 |
21 | sys.path.insert(0, os.path.abspath("."))
22 |
23 | # Get a Bibtex reference file from the Zotero group for referencing
24 | url = "https://api.zotero.org/groups/4846265/collections/M9ZRDX2U/items/"
25 | params = {"format": "bibtex", "style": "apa", "limit": 100}
26 |
27 | r = requests.get(url=url, params=params)
28 | with open("references.bib", mode="w") as file:
29 | file.write(r.text)
30 |
31 | # Get a Bibtex reference file from the Zotero group for publications list
32 | url = "https://api.zotero.org/groups/4846265/collections/UR7PHVDK/items/"
33 | params = {"format": "bibtex", "style": "apa", "limit": 100}
34 |
35 | r = requests.get(url=url, params=params)
36 | with open("publications.bib", mode="w") as file:
37 | file.write(r.text)
38 |
39 | # -- Project information -----------------------------------------------------
40 |
41 | project = "pyet"
42 | copyright = "{}, M. Vremec, R.A. Collenteur".format(year)
43 | author = "M. Vremec, R.A. Collenteur"
44 |
45 | # The full version, including alpha/beta/rc tags
46 | release = "2020"
47 |
48 | # -- General configuration ---------------------------------------------------
49 |
50 | # Add any Sphinx extension module names here, as strings. They can be
51 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
52 | # ones.
53 | extensions = [
54 | "sphinx.ext.autodoc",
55 | "sphinx.ext.autosummary",
56 | "sphinx.ext.napoleon",
57 | "sphinx.ext.doctest",
58 | "sphinx.ext.intersphinx",
59 | "sphinx.ext.todo",
60 | "sphinx.ext.mathjax",
61 | "sphinx.ext.ifconfig",
62 | "sphinx.ext.viewcode",
63 | "IPython.sphinxext.ipython_console_highlighting", # lowercase didn't work
64 | "sphinx.ext.autosectionlabel",
65 | "sphinxcontrib.bibtex",
66 | "myst_nb",
67 | "numpydoc",
68 | "sphinx_design",
69 | ]
70 |
71 | # Create custom bracket style with round brackets
72 | # From https://sphinxcontrib-bibtex.readthedocs.io/en/latest/usage.html
73 |
74 | from dataclasses import dataclass, field
75 | import sphinxcontrib.bibtex.plugin
76 |
77 | from sphinxcontrib.bibtex.style.referencing import BracketStyle
78 | from sphinxcontrib.bibtex.style.referencing.author_year import AuthorYearReferenceStyle
79 |
80 |
81 | def bracket_style() -> BracketStyle:
82 | return BracketStyle(
83 | left="(",
84 | right=")",
85 | )
86 |
87 |
88 | @dataclass
89 | class MyReferenceStyle(AuthorYearReferenceStyle):
90 | bracket_parenthetical: BracketStyle = field(default_factory=bracket_style)
91 | bracket_textual: BracketStyle = field(default_factory=bracket_style)
92 | bracket_author: BracketStyle = field(default_factory=bracket_style)
93 | bracket_label: BracketStyle = field(default_factory=bracket_style)
94 | bracket_year: BracketStyle = field(default_factory=bracket_style)
95 |
96 |
97 | sphinxcontrib.bibtex.plugin.register_plugin(
98 | "sphinxcontrib.bibtex.style.referencing", "author_year_round", MyReferenceStyle
99 | )
100 |
101 | bibtex_bibfiles = ["references.bib", "publications.bib"]
102 | bibtex_reference_style = "author_year_round"
103 |
104 | # Add any paths that contain templates here, relative to this directory.
105 | templates_path = ["_templates"]
106 |
107 | source_suffix = ".rst"
108 |
109 | # The master toctree document.
110 | master_doc = "index"
111 |
112 | # List of patterns, relative to source directory, that match files and
113 | # directories to ignore when looking for source files.
114 | # This pattern also affects html_static_path and html_extra_path.
115 | exclude_patterns = ["_build", "**.ipynb_checkpoints"]
116 |
117 | # -- Options for HTML output -------------------------------------------------
118 |
119 | # The theme to use for HTML and HTML Help pages. See the documentation for
120 | # a list of builtin themes.
121 |
122 | html_theme = "pydata_sphinx_theme"
123 | # Add any paths that contain custom static files (such as style sheets) here,
124 | # relative to this directory. They are copied after the builtin static files,
125 | # so a file named "default.css" will overwrite the builtin "default.css".
126 | html_static_path = ["_static"]
127 | html_logo = "_static/logo.png"
128 | html_use_smartypants = True
129 | html_show_sourcelink = True
130 |
131 | html_theme_options = {
132 | "github_url": "https://github.com/pyet-org/pyet",
133 | "use_edit_page_button": True,
134 | "header_links_before_dropdown": 6,
135 | "icon_links": [
136 | {
137 | "name": "GitHub", # Label for this link
138 | "url": "https://github.com/pyet-org/pyet", # required
139 | "icon": "fab fa-github-square",
140 | "type": "fontawesome", # Default is fontawesome
141 | }
142 | ],
143 | }
144 |
145 | autosummary_generate = True
146 | numpydoc_show_class_members = False
147 |
148 | # Example configuration for intersphinx: refer to the Python standard library.
149 | intersphinx_mapping = {
150 | "numpy": ("https://numpy.org/doc/stable/", None),
151 | "pandas": ("https://pandas.pydata.org/pandas-docs/stable/", None),
152 | "python": ("https://docs.python.org/3/", None),
153 | "xarray": ("https://docs.xarray.dev/en/stable/", None),
154 | }
155 |
156 | # -- myst_nb options ------------------------------------------------------------------
157 |
158 | nb_execution_allow_errors = True # Allow errors in notebooks, to see the error online
159 | nb_execution_mode = "auto"
160 |
161 | # Enable specific MyST extensions, such as "dollarmath" for math rendering
162 | myst_enable_extensions = [
163 | "dollarmath",
164 | ]
165 |
166 | # -- Numpydoc settings ----------------------------------------------------------------
167 |
168 | numpydoc_class_members_toctree = True
169 | numpydoc_show_class_members = False
170 |
--------------------------------------------------------------------------------
/docs/examples/01_example_zamg.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "999bc8b7-804c-4301-b8c7-5077a38dc06f",
6 | "metadata": {
7 | "pycharm": {
8 | "name": "#%% md\n"
9 | }
10 | },
11 | "source": [
12 | "# Potential Evapotranspiration from ZAMG data\n",
13 | "*A. Kokimova, November 2021, University of Graz*\n",
14 | "\n",
15 | "Data source: ZAMG - https://data.hub.zamg.ac.at\n",
16 | "\n",
17 | "What is done:\n",
18 | "\n",
19 | "- load the station data from ZAMG\n",
20 | "- estimate potential evapotranspiration\n",
21 | "- plot and store results"
22 | ]
23 | },
24 | {
25 | "cell_type": "code",
26 | "execution_count": null,
27 | "id": "7a2ae568-3c3b-44f5-8b82-ab5a99d35d2d",
28 | "metadata": {
29 | "pycharm": {
30 | "name": "#%%\n"
31 | }
32 | },
33 | "outputs": [],
34 | "source": [
35 | "import pandas as pd\n",
36 | "import matplotlib.pyplot as plt\n",
37 | "import numpy as np\n",
38 | "import pyet\n",
39 | "pyet.show_versions()"
40 | ]
41 | },
42 | {
43 | "cell_type": "markdown",
44 | "id": "da6f3e98-0073-4db9-b763-03beb2611e78",
45 | "metadata": {
46 | "pycharm": {
47 | "name": "#%% md\n"
48 | }
49 | },
50 | "source": [
51 | "## Loading daily data from ZAMG (Messstationen Tagesdaten)\n",
52 | "\n",
53 | "station: Graz Universität 16412\n",
54 | "\n",
55 | "Selected variables:\n",
56 | "- globalstrahlung (global radiation), J/cm2 needs to be in MJ/m3d, ZAMG abbreviation - strahl\n",
57 | "- arithmetische windgeschwindigkeit (wind speed), m/s, ZAMG abbreviation - vv\n",
58 | "- relative feuchte (relative humidity), %, ZAMG abbreviation - rel\n",
59 | "- lufttemparatur (air temperature) in 2 m, C, ZAMG abbreviation - t\n",
60 | "- lufttemperatur (air temperature) max in 2 m, C, ZAMG abbreviation - tmax\n",
61 | "- lufttemperatur (air temperature) min in 2 m, C, ZAMG abbreviation - tmin\n",
62 | "- latitute and elevation of a station"
63 | ]
64 | },
65 | {
66 | "cell_type": "code",
67 | "execution_count": null,
68 | "id": "236434b2-3c33-4772-93ec-de0cb7317209",
69 | "metadata": {
70 | "pycharm": {
71 | "name": "#%%\n"
72 | }
73 | },
74 | "outputs": [],
75 | "source": [
76 | "#read data\n",
77 | "data_16412 = pd.read_csv('data/example_1/klima_daily.csv', index_col=1, parse_dates=True)\n",
78 | "data_16412"
79 | ]
80 | },
81 | {
82 | "cell_type": "markdown",
83 | "id": "22ebf3bc-6b9a-4f81-a455-7ca1b51879dd",
84 | "metadata": {
85 | "pycharm": {
86 | "name": "#%% md\n"
87 | }
88 | },
89 | "source": [
90 | "## Calculate PET for Graz Universität - 16412"
91 | ]
92 | },
93 | {
94 | "cell_type": "code",
95 | "execution_count": null,
96 | "id": "bc5ba933-22c2-4c5b-9ca6-43a4bcdad344",
97 | "metadata": {
98 | "pycharm": {
99 | "name": "#%%\n"
100 | }
101 | },
102 | "outputs": [],
103 | "source": [
104 | "# Convert Glabalstrahlung J/cm2 to MJ/m2 by dividing to 100\n",
105 | "\n",
106 | "meteo = pd.DataFrame({\"time\":data_16412.index, \"tmean\":data_16412.t, \"tmax\":data_16412.tmax, \"tmin\":data_16412.tmin, \"rh\":data_16412.rel, \n",
107 | " \"wind\":data_16412.vv, \"rs\":data_16412.strahl/100})\n",
108 | "time, tmean, tmax, tmin, rh, wind, rs = [meteo[col] for col in meteo.columns]\n",
109 | "\n",
110 | "lat = 47.077778*np.pi/180 # Latitude of the meteorological station, converting from degrees to radians\n",
111 | "elevation = 367 # meters above sea-level\n",
112 | "\n",
113 | "# Estimate evapotranspiration with four different methods and create a dataframe\n",
114 | "pet_df = pyet.calculate_all(tmean, wind, rs, elevation, lat, tmax=tmax,\n",
115 | " tmin=tmin, rh=rh)"
116 | ]
117 | },
118 | {
119 | "cell_type": "markdown",
120 | "id": "e6e2564c-48ed-46b7-a275-fd17db592456",
121 | "metadata": {
122 | "pycharm": {
123 | "name": "#%% md\n"
124 | }
125 | },
126 | "source": [
127 | "## Plot results"
128 | ]
129 | },
130 | {
131 | "cell_type": "code",
132 | "execution_count": null,
133 | "id": "c9a4582f-219b-47f8-9ed2-dac80720cf55",
134 | "metadata": {
135 | "pycharm": {
136 | "name": "#%%\n"
137 | }
138 | },
139 | "outputs": [],
140 | "source": [
141 | "fig, axs = plt.subplots(figsize=(13,4), ncols=2)\n",
142 | "pet_df.plot(ax=axs[0])\n",
143 | "pet_df.cumsum().plot(ax=axs[1], legend=False)\n",
144 | "\n",
145 | "axs[0].set_ylabel(\"PET [mm/day]\", fontsize=12)\n",
146 | "axs[1].set_ylabel(\"Cumulative PET [mm]\", fontsize=12)\n",
147 | "axs[0].legend(ncol=6, loc=[0,1.])\n",
148 | "for i in (0,1):\n",
149 | " axs[i].set_xlabel(\"Date\", fontsize=12)"
150 | ]
151 | },
152 | {
153 | "cell_type": "markdown",
154 | "id": "1fe2a55b-838a-4e3a-a621-c5fe55db399a",
155 | "metadata": {
156 | "pycharm": {
157 | "name": "#%% md\n"
158 | }
159 | },
160 | "source": [
161 | "## Store results"
162 | ]
163 | },
164 | {
165 | "cell_type": "code",
166 | "execution_count": null,
167 | "id": "3c5e47fb-3ec7-4bc9-ad6c-be0edc67ba09",
168 | "metadata": {
169 | "pycharm": {
170 | "name": "#%%\n"
171 | }
172 | },
173 | "outputs": [],
174 | "source": [
175 | "#plt.savefig(\"PET_methods.png\", dpi=300)\n",
176 | "#pet_u.to_csv('../evap_16412.csv')"
177 | ]
178 | }
179 | ],
180 | "metadata": {
181 | "kernelspec": {
182 | "display_name": "Python 3 (ipykernel)",
183 | "language": "python",
184 | "name": "python3"
185 | },
186 | "language_info": {
187 | "codemirror_mode": {
188 | "name": "ipython",
189 | "version": 3
190 | },
191 | "file_extension": ".py",
192 | "mimetype": "text/x-python",
193 | "name": "python",
194 | "nbconvert_exporter": "python",
195 | "pygments_lexer": "ipython3",
196 | "version": "3.11.5"
197 | }
198 | },
199 | "nbformat": 4,
200 | "nbformat_minor": 5
201 | }
202 |
--------------------------------------------------------------------------------
/docs/examples/02_example_zamg_netcdf.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "999bc8b7-804c-4301-b8c7-5077a38dc06f",
6 | "metadata": {},
7 | "source": [
8 | "# Potential Evapotranspiration from ZAMG INCA data (NetCDF)\n",
9 | "*M. Vremec, October 2022, University of Graz*\n",
10 | "\n",
11 | "\n",
12 | "What is done:\n",
13 | "\n",
14 | "- load the data from ZAMG\n",
15 | "- estimate potential evapotranspiration\n",
16 | "- plot and store results\n",
17 | "\n",
18 | "Data source: ZAMG - https://data.hub.zamg.ac.at"
19 | ]
20 | },
21 | {
22 | "cell_type": "code",
23 | "execution_count": null,
24 | "id": "7a2ae568-3c3b-44f5-8b82-ab5a99d35d2d",
25 | "metadata": {},
26 | "outputs": [],
27 | "source": [
28 | "import pandas as pd\n",
29 | "import matplotlib.pyplot as plt\n",
30 | "import numpy as np\n",
31 | "import xarray as xr\n",
32 | "#import netcdf4 # Needs to be installed\n",
33 | "import pyet\n",
34 | "pyet.show_versions()"
35 | ]
36 | },
37 | {
38 | "cell_type": "markdown",
39 | "id": "da6f3e98-0073-4db9-b763-03beb2611e78",
40 | "metadata": {},
41 | "source": [
42 | "## 1. Loading daily data from ZAMG (INCA hourly NetCDF data)"
43 | ]
44 | },
45 | {
46 | "cell_type": "code",
47 | "execution_count": null,
48 | "id": "236434b2-3c33-4772-93ec-de0cb7317209",
49 | "metadata": {},
50 | "outputs": [],
51 | "source": [
52 | "#read data\n",
53 | "xr_ds = xr.open_dataset(\"data/example_2/incal_hourly_20120501T0000_20120930T2300.nc\", \n",
54 | " engine=\"netcdf4\")"
55 | ]
56 | },
57 | {
58 | "cell_type": "code",
59 | "execution_count": null,
60 | "id": "fd56788b-5aed-499b-8a6a-9c6fb68bb2f6",
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "# Resample and define input meteorological variables\n",
65 | "tmean = xr_ds[\"T2M\"].resample(time=\"1D\").mean()\n",
66 | "tmax = xr_ds[\"T2M\"].resample(time=\"1D\").max()\n",
67 | "tmin = xr_ds[\"T2M\"].resample(time=\"1D\").min()\n",
68 | "rh = xr_ds[\"RH2M\"].resample(time=\"1D\").mean()\n",
69 | "rhmax = xr_ds[\"RH2M\"].resample(time=\"1D\").max()\n",
70 | "rhmin = xr_ds[\"RH2M\"].resample(time=\"1D\").min()\n",
71 | "wind = ((np.abs(xr_ds[\"VV\"]) + np.abs(xr_ds[\"UU\"])) / 2).resample(time=\"1D\").mean()\n",
72 | "rs = xr_ds[\"GL\"].resample(time=\"1D\").mean() * 86400 / 1000000"
73 | ]
74 | },
75 | {
76 | "cell_type": "code",
77 | "execution_count": null,
78 | "id": "f5bb9668-075b-4a8c-99e5-64a998c9c9d5",
79 | "metadata": {},
80 | "outputs": [],
81 | "source": [
82 | "# Define latitude and elevation\n",
83 | "lat = tmean.lat * np.pi / 180 \n",
84 | "elevation = lat / lat * 350"
85 | ]
86 | },
87 | {
88 | "cell_type": "markdown",
89 | "id": "22ebf3bc-6b9a-4f81-a455-7ca1b51879dd",
90 | "metadata": {},
91 | "source": [
92 | "## 2. Calculate PET"
93 | ]
94 | },
95 | {
96 | "cell_type": "code",
97 | "execution_count": null,
98 | "id": "542a2a7a-7354-4ce7-a7d7-644f8f650238",
99 | "metadata": {},
100 | "outputs": [],
101 | "source": [
102 | "lat1 = 80 * np.pi / 180"
103 | ]
104 | },
105 | {
106 | "cell_type": "code",
107 | "execution_count": null,
108 | "id": "2b8859a2-37e3-40db-afc1-01b449adfc35",
109 | "metadata": {},
110 | "outputs": [],
111 | "source": [
112 | "# Estimate evapotranspiration with nine different methods \n",
113 | "pet_penman = pyet.penman(tmean, wind, rs=rs, elevation=elevation, lat=lat, tmax=tmax, tmin=tmin, rh=rh)\n",
114 | "pet_pt = pyet.priestley_taylor(tmean, rs=rs, elevation=elevation, lat=lat, tmax=tmax, tmin=tmin, rh=rh)\n",
115 | "pet_makkink = pyet.makkink(tmean, rs, elevation=elevation)\n",
116 | "pet_fao56 = pyet.pm_fao56(tmean, wind, rs=rs, elevation=elevation, lat=lat, tmax=tmax, tmin=tmin, rh=rh)\n",
117 | "pet_hamon = pyet.hamon(tmean, lat=lat, method=1)\n",
118 | "pet_oudin = pyet.oudin(tmean, lat=lat)\n",
119 | "pet_haude = pyet.haude(tmax, rh)\n",
120 | "pet_turc = pyet.turc(tmean, rs, rh)\n",
121 | "pet_har = pyet.hargreaves(tmean, tmax, tmin, lat)"
122 | ]
123 | },
124 | {
125 | "cell_type": "markdown",
126 | "id": "e6e2564c-48ed-46b7-a275-fd17db592456",
127 | "metadata": {},
128 | "source": [
129 | "## 3. Plot results"
130 | ]
131 | },
132 | {
133 | "cell_type": "code",
134 | "execution_count": null,
135 | "id": "14c613f4-edc1-4472-8ab3-5ed0e1d373f5",
136 | "metadata": {},
137 | "outputs": [],
138 | "source": [
139 | "fig, axs = plt.subplots(ncols=2, figsize=(12,3))\n",
140 | "pet_penman[:,2,2].plot(ax=axs[0], label=\"Penman\")\n",
141 | "pet_pt[:,2,2].plot(ax=axs[0], label=\"Priestley-Taylor\")\n",
142 | "pet_makkink[:,2,2].plot(ax=axs[0], label=\"Makkink\")\n",
143 | "pet_fao56[:,2,2].plot(ax=axs[0], label=\"FAO56\")\n",
144 | "pet_hamon[:,2,2].plot(ax=axs[0], label=\"Hamon\")\n",
145 | "pet_oudin[:,2,2].plot(ax=axs[0], label=\"Oudin\")\n",
146 | "pet_haude[:,2,2].plot(ax=axs[0], label=\"Haude\")\n",
147 | "pet_turc[:,2,2].plot(ax=axs[0], label=\"Turc\")\n",
148 | "pet_har[:,2,2].plot(ax=axs[0], label=\"Hargreaves\")\n",
149 | "axs[0].legend()\n",
150 | "\n",
151 | "pet_penman[:,2,2].cumsum().plot(ax=axs[1], label=\"Penman\")\n",
152 | "pet_pt[:,2,2].cumsum().plot(ax=axs[1], label=\"Priestley-Taylor\")\n",
153 | "pet_makkink[:,2,2].cumsum().plot(ax=axs[1], label=\"Makkink\")\n",
154 | "pet_fao56[:,2,2].cumsum().plot(ax=axs[1], label=\"FAO56\")\n",
155 | "pet_hamon[:,2,2].cumsum().plot(ax=axs[1], label=\"Hamon\")\n",
156 | "pet_oudin[:,2,2].cumsum().plot(ax=axs[1], label=\"Oudin\")\n",
157 | "pet_haude[:,2,2].cumsum().plot(ax=axs[1], label=\"Haude\")\n",
158 | "pet_turc[:,2,2].cumsum().plot(ax=axs[1], label=\"Turc\")\n",
159 | "pet_har[:,2,2].cumsum().plot(ax=axs[1], label=\"Hargreaves\")\n",
160 | "axs[1].legend()"
161 | ]
162 | },
163 | {
164 | "cell_type": "markdown",
165 | "id": "d6697f56-83ef-49f4-8037-9235f6e88a51",
166 | "metadata": {},
167 | "source": [
168 | "## 4. Compare point with pandas.Series vs. point from xarray.DataArray"
169 | ]
170 | },
171 | {
172 | "cell_type": "code",
173 | "execution_count": null,
174 | "id": "2b70386d-18f3-4485-bdc1-8969630018b8",
175 | "metadata": {},
176 | "outputs": [],
177 | "source": [
178 | "tmeans = xr_ds[\"T2M\"].resample(time=\"1D\").mean()[:, 4, 4].to_series()\n",
179 | "tmaxs = xr_ds[\"T2M\"].resample(time=\"1D\").max()[:, 4, 4].to_series()\n",
180 | "tmins = xr_ds[\"T2M\"].resample(time=\"1D\").min()[:, 4, 4].to_series()\n",
181 | "rhs = xr_ds[\"RH2M\"].resample(time=\"1D\").mean()[:, 4, 4].to_series()\n",
182 | "rhmaxs = xr_ds[\"RH2M\"].resample(time=\"1D\").max()[:, 4, 4].to_series()\n",
183 | "rhmins = xr_ds[\"RH2M\"].resample(time=\"1D\").min()[:, 4, 4].to_series()\n",
184 | "winds = ((np.abs(xr_ds[\"VV\"]) + np.abs(xr_ds[\"UU\"])) / 2).resample(time=\"1D\").mean()[:, 4, 4].to_series()\n",
185 | "rss = (xr_ds[\"GL\"].resample(time=\"1D\").mean() * 86400 / 1000000)[:, 4, 4].to_series()\n",
186 | "lats = float(lat[4, 4])\n",
187 | "elevations = float(elevation[4, 4])"
188 | ]
189 | },
190 | {
191 | "cell_type": "code",
192 | "execution_count": null,
193 | "id": "0ef26699-5611-4c93-bb26-4fdef1138eec",
194 | "metadata": {},
195 | "outputs": [],
196 | "source": [
197 | "# Estimate evapotranspiration with nine different methods with Pandas.Series\n",
198 | "pet_penmans = pyet.penman(tmeans, winds, rs=rss, elevation=elevations, lat=lats, tmax=tmaxs, tmin=tmins, rh=rhs)\n",
199 | "pet_pts = pyet.priestley_taylor(tmeans, rs=rss, elevation=elevations, lat=lats, tmax=tmaxs, tmin=tmins, rh=rhs)\n",
200 | "pet_makkinks = pyet.makkink(tmeans, rss, elevation=elevations)\n",
201 | "pet_fao56s = pyet.pm_fao56(tmeans, winds, rs=rss, elevation=elevations, lat=lats, tmax=tmaxs, tmin=tmins, rh=rhs)\n",
202 | "pet_hamons = pyet.hamon(tmeans, lat=lats, method=1)\n",
203 | "pet_oudins = pyet.oudin(tmeans, lat=lats)\n",
204 | "pet_haudes = pyet.haude(tmaxs, rhs)\n",
205 | "pet_turcs = pyet.turc(tmeans, rss, rhs)\n",
206 | "pet_hars = pyet.hargreaves(tmeans, tmaxs, tmins, lats)"
207 | ]
208 | },
209 | {
210 | "cell_type": "code",
211 | "execution_count": null,
212 | "id": "7165b896-c86e-460f-a592-874f2381f7d6",
213 | "metadata": {},
214 | "outputs": [],
215 | "source": [
216 | "fm = \"\"\"\n",
217 | " ABC\n",
218 | " DEF\n",
219 | " GHI\n",
220 | " \"\"\"\n",
221 | "fig,axs = plt.subplot_mosaic(mosaic=fm, figsize=(12,8))\n",
222 | "axs[\"A\"].scatter(pet_penman[:,4,4].values, pet_penmans.values)\n",
223 | "axs[\"B\"].scatter(pet_pt[:,4,4].values, pet_pts.values)\n",
224 | "axs[\"C\"].scatter(pet_makkink[:,4,4].values, pet_makkinks.values)\n",
225 | "axs[\"D\"].scatter(pet_fao56[:,4,4].values, pet_fao56s.values)\n",
226 | "axs[\"E\"].scatter(pet_hamon[:,4,4].values, pet_hamons.values)\n",
227 | "axs[\"F\"].scatter(pet_oudin[:,4,4].values, pet_oudins.values)\n",
228 | "axs[\"G\"].scatter(pet_haude[:,4,4].values, pet_haudes.values)\n",
229 | "axs[\"H\"].scatter(pet_turc[:,4,4].values, pet_turcs.values)\n",
230 | "axs[\"I\"].scatter(pet_har[:,4,4].values, pet_hars.values)\n",
231 | "for i in axs.keys():\n",
232 | " axs[i].plot([0,7], [0,7])"
233 | ]
234 | },
235 | {
236 | "cell_type": "markdown",
237 | "id": "1fe2a55b-838a-4e3a-a621-c5fe55db399a",
238 | "metadata": {},
239 | "source": [
240 | "## 5. Store results"
241 | ]
242 | },
243 | {
244 | "cell_type": "code",
245 | "execution_count": null,
246 | "id": "3c5e47fb-3ec7-4bc9-ad6c-be0edc67ba09",
247 | "metadata": {},
248 | "outputs": [],
249 | "source": [
250 | "#pet_pt.to_netcdf('../pe_pt_INCA.csv')"
251 | ]
252 | }
253 | ],
254 | "metadata": {
255 | "kernelspec": {
256 | "display_name": "Python 3 (ipykernel)",
257 | "language": "python",
258 | "name": "python3"
259 | },
260 | "language_info": {
261 | "codemirror_mode": {
262 | "name": "ipython",
263 | "version": 3
264 | },
265 | "file_extension": ".py",
266 | "mimetype": "text/x-python",
267 | "name": "python",
268 | "nbconvert_exporter": "python",
269 | "pygments_lexer": "ipython3",
270 | "version": "3.11.5"
271 | }
272 | },
273 | "nbformat": 4,
274 | "nbformat_minor": 5
275 | }
276 |
--------------------------------------------------------------------------------
/docs/examples/03_example_knmi.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "d0959a4c",
6 | "metadata": {},
7 | "source": [
8 | "# Potential Evapotranspiration from KNMI data\n",
9 | "*R.A. Collenteur, Eawag, 2023*\n",
10 | "\n",
11 | "Data source: KNMI - https://dataplatform.knmi.nl/\n",
12 | "\n",
13 | "In this notebook it is shown how to compute (potential) evapotranspiration from meteorological data using PyEt. Meteorological data is observed by the KNMI at De Bilt in the Netherlands."
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "id": "f8c6cab3",
20 | "metadata": {},
21 | "outputs": [],
22 | "source": [
23 | "import pandas as pd\n",
24 | "import matplotlib.pyplot as plt\n",
25 | "\n",
26 | "import pyet\n",
27 | "pyet.show_versions()"
28 | ]
29 | },
30 | {
31 | "cell_type": "markdown",
32 | "id": "61cb9b9e",
33 | "metadata": {},
34 | "source": [
35 | "## 1. Load KNMI Data\n",
36 | "\n",
37 | "We first load the raw meteorological data observed by the KNMI at De Bilt in the Netherlands. This datafile contains a lot of different variables, please see the end of the notebook for an explanation of all the variables. "
38 | ]
39 | },
40 | {
41 | "cell_type": "code",
42 | "execution_count": null,
43 | "id": "cbc6c337",
44 | "metadata": {},
45 | "outputs": [],
46 | "source": [
47 | "data = pd.read_csv(\"data/example_3/etmgeg_260.txt\", skiprows=46, delimiter=\",\", \n",
48 | " skipinitialspace=True, index_col=\"YYYYMMDD\", parse_dates=True).loc[\"2018\",:]\n",
49 | "data.head()"
50 | ]
51 | },
52 | {
53 | "cell_type": "markdown",
54 | "id": "be888ed3",
55 | "metadata": {},
56 | "source": [
57 | "## 2. Estimating potential evapotranspiration\n",
58 | "\n",
59 | "Now that we have the input data, we can estimate potential evapotranspiration with different estimation methods. Here we choose the Penman, Priestley-Taylor, Makkink, and Oudin methods. "
60 | ]
61 | },
62 | {
63 | "cell_type": "code",
64 | "execution_count": null,
65 | "id": "abe8a230",
66 | "metadata": {},
67 | "outputs": [],
68 | "source": [
69 | "# Preprocess the input data\n",
70 | "meteo = pd.DataFrame({\"tmean\":data.TG/10, \"tmax\":data.TX/10, \"tmin\":data.TN/10, \n",
71 | " \"rh\":data.UG, \"wind\":data.FG/10, \"rs\":data.Q/100})\n",
72 | "tmean, tmax, tmin, rh, wind, rs = [meteo[col] for col in meteo.columns]\n",
73 | "pressure = data.PG / 100 # to kPa\n",
74 | "wind = data.FG / 10 # to m/s\n",
75 | "lat = 0.91 # Latitude of the meteorological station\n",
76 | "elevation = 4 # meters above sea-level \n",
77 | "\n",
78 | "# Estimate evapotranspiration with four different methods\n",
79 | "pet_penman = pyet.penman(tmean, wind, rs=rs, elevation=4, lat=0.91, tmax=tmax, tmin=tmin, rh=rh)\n",
80 | "pet_pt = pyet.priestley_taylor(tmean, rs=rs, elevation=4, lat=0.91, tmax=tmax, tmin=tmin, rh=rh)\n",
81 | "pet_makkink = pyet.makkink(tmean, rs, elevation=4, pressure=pressure)\n",
82 | "pet_oudin = pyet.oudin(tmean, lat=0.91)"
83 | ]
84 | },
85 | {
86 | "cell_type": "markdown",
87 | "id": "578f8ac2",
88 | "metadata": {},
89 | "source": [
90 | "## 3. Plot the results\n",
91 | "\n",
92 | "We plot the cumulative sums to compare the different estimation methods."
93 | ]
94 | },
95 | {
96 | "cell_type": "code",
97 | "execution_count": null,
98 | "id": "4c5a8008",
99 | "metadata": {},
100 | "outputs": [],
101 | "source": [
102 | "fig, axs = plt.subplots(figsize=(14,3.5), ncols=2)\n",
103 | "\n",
104 | "pet = [pet_penman, pet_pt, pet_makkink, pet_makkink]\n",
105 | "names = [\"Penman\", \"Priestley-Taylor\", \"Makkink - PyEt\", \"Oudin\"]\n",
106 | "\n",
107 | "for df, name in zip(pet, names):\n",
108 | " axs[0].plot(df,label=name)\n",
109 | " axs[1].plot(df.cumsum(),label=name)\n",
110 | "\n",
111 | "axs[0].set_ylabel(\"PET [mm/day]\", fontsize=16)\n",
112 | "axs[1].set_ylabel(\"Cumulative PET [mm]\", fontsize=12)\n",
113 | "\n",
114 | "for i in (0,1):\n",
115 | " axs[i].set_xlabel(\"Date\", fontsize=12)\n",
116 | " axs[i].legend(loc=2, fontsize=14)\n",
117 | " axs[i].tick_params(\"both\", direction=\"in\", labelsize=14)\n",
118 | "plt.tight_layout()\n",
119 | "\n",
120 | "#plt.savefig(\"Figure1.png\", dpi=300)"
121 | ]
122 | },
123 | {
124 | "attachments": {},
125 | "cell_type": "markdown",
126 | "id": "75dacdc4",
127 | "metadata": {},
128 | "source": [
129 | "## 4. Comparison: pyet Makkink vs KNMI Makkink\n",
130 | "\n",
131 | "The KNMI also provides Makkink potential evapotranspiration data (column EV24). We can now compare the results from Pyet and the KNMI to confirm that these are roughly the same. Pyet also has a method `makkink_knmi` that calculates exactly the same Makkink evaporation as the KNMI but that is only suitable for the Netherlands."
132 | ]
133 | },
134 | {
135 | "cell_type": "code",
136 | "execution_count": null,
137 | "id": "95c6b234",
138 | "metadata": {},
139 | "outputs": [],
140 | "source": [
141 | "pet_knmi = data.EV24 / 10 # Makkink potential evaporation computen by the KNMI for comparison\n",
142 | "pet_makkink_knmi = pyet.makkink_knmi(tmean, rs).round(1) # same as pet_knmi (if rounded up to 1 decimal) but calculated from tmean and rs\n",
143 | "\n",
144 | "# Plot the two series against each other\n",
145 | "_, ax = plt.subplots(1, 2, figsize=(9,4), sharex=True, sharey=True)\n",
146 | "ax[0].scatter(pet_makkink, pet_knmi, s=15, color=\"C0\")\n",
147 | "ax[0].plot([0,6],[0,6], color=\"red\", label=\"1:1 line\")\n",
148 | "ax[0].set_xlabel(\"pyet-Makkink 'EV24' [mm]\")\n",
149 | "ax[0].grid()\n",
150 | "\n",
151 | "ax[1].scatter(pet_makkink_knmi, pet_knmi, s=15, color=\"C1\")\n",
152 | "ax[1].plot([0,6],[0,6], color=\"red\")\n",
153 | "ax[1].set_xlabel(\"pyet-Makkink_KNMI [mm]\")\n",
154 | "ax[1].grid()\n",
155 | "\n",
156 | "ax[0].set_ylabel(\"KNMI-Makkink [mm]\")\n",
157 | "ax[0].legend(loc=(0,1), frameon=False)\n"
158 | ]
159 | },
160 | {
161 | "cell_type": "markdown",
162 | "id": "a51c36d1",
163 | "metadata": {},
164 | "source": [
165 | "## 5. Estimation of PET - all methods"
166 | ]
167 | },
168 | {
169 | "cell_type": "code",
170 | "execution_count": null,
171 | "id": "2a2b17d3",
172 | "metadata": {},
173 | "outputs": [],
174 | "source": [
175 | "pet_df = pyet.calculate_all(tmean, wind, rs, elevation, lat, tmax=tmax,\n",
176 | " tmin=tmin, rh=rh)"
177 | ]
178 | },
179 | {
180 | "cell_type": "code",
181 | "execution_count": null,
182 | "id": "055e90d1",
183 | "metadata": {},
184 | "outputs": [],
185 | "source": [
186 | "fig, axs = plt.subplots(figsize=(13,4), ncols=2)\n",
187 | "pet_df.plot(ax=axs[0])\n",
188 | "pet_df.cumsum().plot(ax=axs[1], legend=False)\n",
189 | "\n",
190 | "axs[0].set_ylabel(\"PET [mm/day]\", fontsize=12)\n",
191 | "axs[1].set_ylabel(\"Cumulative PET [mm]\", fontsize=12)\n",
192 | "axs[0].legend(ncol=6, loc=[0,1.])\n",
193 | "for i in (0,1):\n",
194 | " axs[i].set_xlabel(\"Date\", fontsize=12)"
195 | ]
196 | },
197 | {
198 | "cell_type": "code",
199 | "execution_count": null,
200 | "id": "7e55de8b",
201 | "metadata": {},
202 | "outputs": [],
203 | "source": [
204 | "#plt.savefig(\"PET_methods.png\", dpi=300)"
205 | ]
206 | },
207 | {
208 | "cell_type": "markdown",
209 | "id": "815addc4",
210 | "metadata": {},
211 | "source": [
212 | "## References\n",
213 | "\n",
214 | "Column description of the KNMI data\n",
215 | "\n",
216 | "- DDVEC = Vector mean wind direction in degrees (360=north, 90=east, 180=south, 270=west, 0=calm/variable)\n",
217 | "- FHVEC = Vector mean windspeed (in 0.1 m/s)\n",
218 | "- FG = Daily mean windspeed (in 0.1 m/s) \n",
219 | "- FHX = Maximum hourly mean windspeed (in 0.1 m/s)\n",
220 | "- FHXH = Hourly division in which FHX was measured\n",
221 | "- FHN = Minimum hourly mean windspeed (in 0.1 m/s)\n",
222 | "- FHNH = Hourly division in which FHN was measured\n",
223 | "- FXX = Maximum wind gust (in 0.1 m/s)\n",
224 | "- FXXH = Hourly division in which FXX was measured\n",
225 | "- TG = Daily mean temperature in (0.1 degrees Celsius)\n",
226 | "- TN = Minimum temperature (in 0.1 degrees Celsius)\n",
227 | "- TNH = Hourly division in which TN was measured\n",
228 | "- TX = Maximum temperature (in 0.1 degrees Celsius)\n",
229 | "- TXH = Hourly division in which TX was measured\n",
230 | "- T10N = Minimum temperature at 10 cm above surface (in 0.1 degrees Celsius)\n",
231 | "- T10NH = 6-hourly division in which T10N was measured; 6=0-6 UT, 12=6-12 UT, 18=12-18 UT, 24=18-24 UT \n",
232 | "- SQ = Sunshine duration (in 0.1 hour) calculated from global radiation (-1 for <0.05 hour)\n",
233 | "- SP = Percentage of maximum potential sunshine duration\n",
234 | "- Q = Global radiation (in J/cm2)\n",
235 | "- DR = Precipitation duration (in 0.1 hour)\n",
236 | "- RH = Daily precipitation amount (in 0.1 mm) (-1 for <0.05 mm)\n",
237 | "- RHX = Maximum hourly precipitation amount (in 0.1 mm) (-1 for <0.05 mm)\n",
238 | "- RHXH = Hourly division in which RHX was measured\n",
239 | "- PG = Daily mean sea level pressure (in 0.1 hPa) calculated from 24 hourly values\n",
240 | "- PX = Maximum hourly sea level pressure (in 0.1 hPa)\n",
241 | "- PXH = Hourly division in which PX was measured\n",
242 | "- PN = Minimum hourly sea level pressure (in 0.1 hPa)\n",
243 | "- PNH = Hourly division in which PN was measured\n",
244 | "- VVN = Minimum visibility; 0: <100 m, 1:100-200 m, 2:200-300 m,..., 49:4900-5000 m, 50:5-6 km, 56:6-7 km, 57:7-8 km,..., 79:29-30 km, 80:30-35 km, 81:35-40 km,..., 89: >70 km)\n",
245 | "- VVNH = Hourly division in which VVN was measured\n",
246 | "- VVX = Maximum visibility; 0: <100 m, 1:100-200 m, 2:200-300 m,..., 49:4900-5000 m, 50:5-6 km, 56:6-7 km, 57:7-8 km,..., 79:29-30 km, 80:30-35 km, 81:35-40 km,..., 89: >70 km)\n",
247 | "- VVXH = Hourly division in which VVX was measured\n",
248 | "- NG = Mean daily cloud cover (in octants, 9=sky invisible)\n",
249 | "- UG = Daily mean relative atmospheric humidity (in percents)\n",
250 | "- UX = Maximum relative atmospheric humidity (in percents)\n",
251 | "- UXH = Hourly division in which UX was measured\n",
252 | "- UN = Minimum relative atmospheric humidity (in percents)\n",
253 | "- UNH = Hourly division in which UN was measured\n",
254 | "- EV24 = Potential evapotranspiration (Makkink) (in 0.1 mm)"
255 | ]
256 | },
257 | {
258 | "cell_type": "code",
259 | "execution_count": null,
260 | "id": "df028060-9d5f-4616-9797-d44e6b66002b",
261 | "metadata": {},
262 | "outputs": [],
263 | "source": []
264 | }
265 | ],
266 | "metadata": {
267 | "kernelspec": {
268 | "display_name": "Python 3 (ipykernel)",
269 | "language": "python",
270 | "name": "python3"
271 | },
272 | "language_info": {
273 | "codemirror_mode": {
274 | "name": "ipython",
275 | "version": 3
276 | },
277 | "file_extension": ".py",
278 | "mimetype": "text/x-python",
279 | "name": "python",
280 | "nbconvert_exporter": "python",
281 | "pygments_lexer": "ipython3",
282 | "version": "3.11.5"
283 | },
284 | "vscode": {
285 | "interpreter": {
286 | "hash": "8a365f246fd2b6cc3f433897ea78f6113fbca3a5aebe59945b06a3e6c75c5dde"
287 | }
288 | }
289 | },
290 | "nbformat": 4,
291 | "nbformat_minor": 5
292 | }
293 |
--------------------------------------------------------------------------------
/docs/examples/04_example_coagmet.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Potential Evapotranspiration from CoAgMET data\n",
8 | "\n",
9 | "*M. Vremec, University of Graz, 2021*\n",
10 | "\n",
11 | "In this notebook it is shown how to compute (reference) evapotranspiration ($ET_0$) from meteorological data observed by the Colorado State University (CoAgMET) at Holyoke in Colorado, USA. The notebook also includes a comparison between $ET_0$ estimated using *pyet* (FAO56) and $ET_0$ available from CoAgMET. According to CoAgMET documentation, the provided reference evapotranspiration is estimated using ASCE reference evapotranspiration for short reference grass (Wright, 2000), which corresponds to the FAO-56 method used with *pyet*.\n",
12 | "\n",
13 | "Source: https://coagmet.colostate.edu/station/selector"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import numpy as np\n",
23 | "import pandas as pd\n",
24 | "import matplotlib.pyplot as plt\n",
25 | "\n",
26 | "import pyet as pyet\n",
27 | "pyet.show_versions()"
28 | ]
29 | },
30 | {
31 | "cell_type": "markdown",
32 | "metadata": {},
33 | "source": [
34 | "## 1. Load CoAgMET Data"
35 | ]
36 | },
37 | {
38 | "cell_type": "code",
39 | "execution_count": null,
40 | "metadata": {},
41 | "outputs": [],
42 | "source": [
43 | "data = pd.read_csv(\"data/example_4/et_coagmet.txt\", parse_dates=True, index_col=\"date\")\n",
44 | "data"
45 | ]
46 | },
47 | {
48 | "cell_type": "code",
49 | "execution_count": null,
50 | "metadata": {},
51 | "outputs": [],
52 | "source": [
53 | "et0_coagmet = data[\"et_asce0\"]\n",
54 | "\n",
55 | "meteo = pd.DataFrame({\"tmean\":data[\"tavg\"], \n",
56 | " \"tmax\":data[\"tmax\"],\n",
57 | " \"tmin\":data[\"tmin\"], \n",
58 | " \"rhmax\":data[\"rhmax\"]*100,\n",
59 | " \"rhmin\":data[\"rhmin\"]*100, \n",
60 | " \"u2\":data[\"windrun\"]*1000/86400,\n",
61 | " \"rs\":data[\"solar\"]*86400/1000000})\n",
62 | "\n",
63 | "tmean, tmax, tmin, rhmax, rhmin, wind, rs = [meteo[col] for col in meteo.columns]\n",
64 | "lat = 40.49 * np.pi / 180"
65 | ]
66 | },
67 | {
68 | "cell_type": "markdown",
69 | "metadata": {},
70 | "source": [
71 | "## 2. Comparison: pyet FAO56 vs CoAgMET ASCE "
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "execution_count": null,
77 | "metadata": {},
78 | "outputs": [],
79 | "source": [
80 | "et0_fao56 = pyet.pm_fao56(tmean, wind, rs=rs, elevation=1138, lat=lat, \n",
81 | " tmax=tmax, tmin=tmin, rhmax=rhmax, rhmin=rhmin)\n",
82 | "\n",
83 | "et0_fao56.plot()\n",
84 | "et0_coagmet.plot();"
85 | ]
86 | },
87 | {
88 | "cell_type": "markdown",
89 | "metadata": {},
90 | "source": [
91 | "## 3. Plot the results\n",
92 | "\n",
93 | "We now plot the evaporation time series against each other to see how these compare. "
94 | ]
95 | },
96 | {
97 | "cell_type": "code",
98 | "execution_count": null,
99 | "metadata": {},
100 | "outputs": [],
101 | "source": [
102 | "plt.scatter(et0_fao56, et0_coagmet)\n",
103 | "plt.plot([0,15],[0,15], color=\"red\", label=\"1:1 line\")\n",
104 | "plt.legend()\n",
105 | "plt.xlabel(\"ET0 pyet-FAO56 [mm]\")\n",
106 | "plt.ylabel(\"ET0 CoAgMET-ASCE [mm]\");"
107 | ]
108 | }
109 | ],
110 | "metadata": {
111 | "kernelspec": {
112 | "display_name": "Python 3 (ipykernel)",
113 | "language": "python",
114 | "name": "python3"
115 | },
116 | "language_info": {
117 | "codemirror_mode": {
118 | "name": "ipython",
119 | "version": 3
120 | },
121 | "file_extension": ".py",
122 | "mimetype": "text/x-python",
123 | "name": "python",
124 | "nbconvert_exporter": "python",
125 | "pygments_lexer": "ipython3",
126 | "version": "3.11.5"
127 | }
128 | },
129 | "nbformat": 4,
130 | "nbformat_minor": 4
131 | }
132 |
--------------------------------------------------------------------------------
/docs/examples/05_example_calibration.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Calibration of potential evapotranspiration methods\n",
8 | "*M. Vremec, R.A. Collenteur University of Graz, 2021*\n",
9 | "\n",
10 | "In this notebook it is shown how to calibrate various (potential) evapotranspiration (PET) equations, using a linear regression relationship between daily potential evapotranspiration obtained with the FAO-56 equation and daily PET estimates obtained with the alternative methods. This notebook requires Scipy and SkLearn python packages to run."
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": null,
16 | "metadata": {},
17 | "outputs": [],
18 | "source": [
19 | "import pandas as pd\n",
20 | "import matplotlib.pyplot as plt\n",
21 | "from scipy.optimize import least_squares\n",
22 | "from sklearn.metrics import mean_squared_error\n",
23 | "\n",
24 | "import pyet as pyet"
25 | ]
26 | },
27 | {
28 | "cell_type": "markdown",
29 | "metadata": {},
30 | "source": [
31 | "## 1. Load KNMI Data"
32 | ]
33 | },
34 | {
35 | "cell_type": "code",
36 | "execution_count": null,
37 | "metadata": {},
38 | "outputs": [],
39 | "source": [
40 | "data = pd.read_csv(\"data/example_3/etmgeg_260.txt\", skiprows=46, delimiter=\",\", \n",
41 | " skipinitialspace=True, index_col=\"YYYYMMDD\", parse_dates=True).loc[\"2018\",:]\n",
42 | "data.head()"
43 | ]
44 | },
45 | {
46 | "cell_type": "markdown",
47 | "metadata": {},
48 | "source": [
49 | "## 2.Estimation of potential evapotranspiration"
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "execution_count": null,
55 | "metadata": {},
56 | "outputs": [],
57 | "source": [
58 | "# define meteorological variables needed for PE estimation\n",
59 | "meteo = pd.DataFrame({\"tmean\":data.TG/10, \"tmax\":data.TX/10, \"tmin\":data.TN/10, \"rh\":data.UG, \"wind\":data.FG/10, \"rs\":data.Q/100})\n",
60 | "tmean, tmax, tmin, rh, wind, rs = [meteo[col] for col in meteo.columns]\n",
61 | "pressure = data.PG / 100 # to kPa\n",
62 | "wind = data.FG/10 # to m/s\n",
63 | "lat = 0.91 # [rad]\n",
64 | "elevation = 4 \n",
65 | "\n",
66 | "pet_fao56 = pyet.pm_fao56(tmean, wind, rs=rs, elevation=elevation, lat=lat, \n",
67 | " tmax=tmax, tmin=tmin, rh=rh) # FAO-56 method\n",
68 | "pet_romanenko = pyet.romanenko(tmean, rh) # Romanenko method\n",
69 | "pet_abtew = pyet.abtew(tmean, rs) # Abtew method"
70 | ]
71 | },
72 | {
73 | "cell_type": "markdown",
74 | "metadata": {},
75 | "source": [
76 | "The model performance of the Abtew and Romanenko method will be evaluated using the root mean square error (RMSE), as implemented in SkLearn’s mean_squared_error method."
77 | ]
78 | },
79 | {
80 | "cell_type": "code",
81 | "execution_count": null,
82 | "metadata": {},
83 | "outputs": [],
84 | "source": [
85 | "pet_fao56.plot()\n",
86 | "pet_romanenko.plot()\n",
87 | "pet_abtew.plot()\n",
88 | "plt.ylabel(\"PE [mm/day]\")\n",
89 | "print(\"RMSE(Romanenko) = {} mm/d\".format(mean_squared_error(pet_fao56, pet_romanenko, squared=False)))\n",
90 | "print(\"RMSE(Abtew) = {} mm/d\".format(mean_squared_error(pet_fao56, pet_abtew, squared=False)))"
91 | ]
92 | },
93 | {
94 | "cell_type": "markdown",
95 | "metadata": {},
96 | "source": [
97 | "## 3. Calibration of different PE equations\n",
98 | "\n",
99 | "The least squares approach is applied to estimate the parameters in the PE equations, by minimizing the sum of the residuals between the simulated (Abtew and Romanenko) and observed (FAO-56) data. The minimization of the objective function is done using the Trust Region Reflective algorithm, as implemented in Scipy’s least squares method."
100 | ]
101 | },
102 | {
103 | "cell_type": "markdown",
104 | "metadata": {},
105 | "source": [
106 | "### 3.1 Romanenko method"
107 | ]
108 | },
109 | {
110 | "cell_type": "code",
111 | "execution_count": null,
112 | "metadata": {},
113 | "outputs": [],
114 | "source": [
115 | "# Define function for computing residuals\n",
116 | "def cal_romanenko(k, obs):\n",
117 | " return pyet.romanenko(tmean, rh, k)-obs"
118 | ]
119 | },
120 | {
121 | "cell_type": "code",
122 | "execution_count": null,
123 | "metadata": {},
124 | "outputs": [],
125 | "source": [
126 | "# estimate k in the Romanenko method\n",
127 | "x0 = 4.5 # initial estimate of parameter\n",
128 | "res_1 = least_squares(cal_romanenko, x0, args=[pet_fao56])\n",
129 | "res_1.x"
130 | ]
131 | },
132 | {
133 | "cell_type": "code",
134 | "execution_count": null,
135 | "metadata": {},
136 | "outputs": [],
137 | "source": [
138 | "# Compute RMSE using the calibrated value of k\n",
139 | "pet_romanenko_cal = pyet.romanenko(tmean, rh, k=res_1.x)\n",
140 | "print(\"RMSE(Romanenko) = {} mm/d\".format(mean_squared_error(pet_fao56, pet_romanenko_cal, squared=False)))"
141 | ]
142 | },
143 | {
144 | "cell_type": "markdown",
145 | "metadata": {},
146 | "source": [
147 | "RMSE (calibrated) = 0.546 < RMSE (uncalibrated) = 0.694"
148 | ]
149 | },
150 | {
151 | "cell_type": "markdown",
152 | "metadata": {},
153 | "source": [
154 | "### 3.2 Abtew method"
155 | ]
156 | },
157 | {
158 | "cell_type": "code",
159 | "execution_count": null,
160 | "metadata": {},
161 | "outputs": [],
162 | "source": [
163 | "# Define function for computing residuals and initial estimate of parameters\n",
164 | "def cal_abtew(k,obs):\n",
165 | " return pyet.abtew(tmean, rs, k)-obs\n",
166 | "x0 = 0.53"
167 | ]
168 | },
169 | {
170 | "cell_type": "code",
171 | "execution_count": null,
172 | "metadata": {},
173 | "outputs": [],
174 | "source": [
175 | "# estimate k in the Romanenko method\n",
176 | "res_2 = least_squares(cal_abtew, x0, args=[pet_fao56])\n",
177 | "res_2.x"
178 | ]
179 | },
180 | {
181 | "cell_type": "code",
182 | "execution_count": null,
183 | "metadata": {},
184 | "outputs": [],
185 | "source": [
186 | "pet_abtew_cali = pyet.abtew(tmean, rs, res_2.x)\n",
187 | "print(\"RMSE(Abtew) = {} mm/d\".format(mean_squared_error(pet_fao56, pet_abtew_cali, squared=False)))"
188 | ]
189 | },
190 | {
191 | "cell_type": "markdown",
192 | "metadata": {},
193 | "source": [
194 | "RMSE (calibrated) = 0.613 < RMSE (uncalibrated) = 0.741"
195 | ]
196 | },
197 | {
198 | "cell_type": "code",
199 | "execution_count": null,
200 | "metadata": {},
201 | "outputs": [],
202 | "source": [
203 | "pet_fao56.plot()\n",
204 | "pet_romanenko_cal.plot()\n",
205 | "pet_abtew_cali.plot()\n",
206 | "plt.ylabel(\"PET [mm/d]\");"
207 | ]
208 | }
209 | ],
210 | "metadata": {
211 | "kernelspec": {
212 | "display_name": "Python 3 (ipykernel)",
213 | "language": "python",
214 | "name": "python3"
215 | },
216 | "language_info": {
217 | "codemirror_mode": {
218 | "name": "ipython",
219 | "version": 3
220 | },
221 | "file_extension": ".py",
222 | "mimetype": "text/x-python",
223 | "name": "python",
224 | "nbconvert_exporter": "python",
225 | "pygments_lexer": "ipython3",
226 | "version": "3.11.5"
227 | }
228 | },
229 | "nbformat": 4,
230 | "nbformat_minor": 4
231 | }
232 |
--------------------------------------------------------------------------------
/docs/examples/07_example_climate_change.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "metadata": {},
6 | "source": [
7 | "# Potential evapotranspiration under warming and elevated $CO_2$ concentration\n",
8 | "*M. Vremec, R.A. Collenteur University of Graz, 2021*\n",
9 | "\n",
10 | "In this notebook it is shown how to estimate Potential Evapotranspiration under elevated $CO_2$ conditions and an increase in temperature.\n",
11 | "The work follows the suggestions made by Yang et al. (2019) to include the sensitivity of stomatal resistance to $CO_2$ in the Penman-Monteith equation.\n",
12 | "\n",
13 | "Yang, Y., Roderick, M.L., Zhang, S. et al. Hydrologic implications of vegetation response to elevated CO2 in climate projections. Nature Clim Change 9, 44–48 (2019). https://doi.org/10.1038/s41558-018-0361-0"
14 | ]
15 | },
16 | {
17 | "cell_type": "code",
18 | "execution_count": null,
19 | "metadata": {},
20 | "outputs": [],
21 | "source": [
22 | "import pandas as pd\n",
23 | "import numpy as np\n",
24 | "import matplotlib.pyplot as plt\n",
25 | "\n",
26 | "import pyet as pyet"
27 | ]
28 | },
29 | {
30 | "cell_type": "markdown",
31 | "metadata": {},
32 | "source": [
33 | "## 1. Load daily data from ZAMG (Messstationen Tagesdaten)\n",
34 | "\n",
35 | "station: Graz Universität 16412\n",
36 | "\n",
37 | "Selected variables:\n",
38 | "- globalstrahlung (global radiation), J/cm2 needs to be in MJ/m3d, ZAMG abbreviation - strahl\n",
39 | "- arithmetische windgeschwindigkeit (wind speed), m/s, ZAMG abbreviation - vv\n",
40 | "- relative feuchte (relative humidity), %, ZAMG abbreviation - rel\n",
41 | "- lufttemparatur (air temperature) in 2 m, C, ZAMG abbreviation - t\n",
42 | "- lufttemperatur (air temperature) max in 2 m, C, ZAMG abbreviation - tmax\n",
43 | "- lufttemperatur (air temperature) min in 2 m, C, ZAMG abbreviation - tmin\n",
44 | "- latitute and elevation of a station"
45 | ]
46 | },
47 | {
48 | "cell_type": "code",
49 | "execution_count": null,
50 | "metadata": {},
51 | "outputs": [],
52 | "source": [
53 | "#read data\n",
54 | "data_16412 = pd.read_csv('data/example_1/klima_daily.csv', index_col=1, parse_dates=True)\n",
55 | "data_16412"
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": null,
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "# Convert Glabalstrahlung J/cm2 to MJ/m2 by dividing to 100\n",
65 | "\n",
66 | "meteo = pd.DataFrame({\"time\":data_16412.index, \"tmean\":data_16412.t, \"tmax\":data_16412.tmax, \"tmin\":data_16412.tmin, \"rh\":data_16412.rel, \n",
67 | " \"wind\":data_16412.vv, \"rs\":data_16412.strahl/100})\n",
68 | "time, tmean, tmax, tmin, rh, wind, rs = [meteo[col] for col in meteo.columns]\n",
69 | "\n",
70 | "lat = 47.077778 * np.pi / 180 # Latitude of the meteorological station, converting from degrees to radians\n",
71 | "elevation = 367 # meters above sea-level"
72 | ]
73 | },
74 | {
75 | "cell_type": "markdown",
76 | "metadata": {},
77 | "source": [
78 | "## 2.Estimate potential evapotranspiration under RCP scenarios"
79 | ]
80 | },
81 | {
82 | "cell_type": "code",
83 | "execution_count": null,
84 | "metadata": {},
85 | "outputs": [],
86 | "source": [
87 | "# the first element in the list includes the rise in temperature, and the CO2 concentration\n",
88 | "rcp_26 = [1.3, 450]\n",
89 | "rcp_45 = [2, 650]\n",
90 | "rcp_85 = [4.5, 850]\n",
91 | "\n",
92 | "def pe_cc(tincrease, co2):\n",
93 | " pe = pyet.pm(tmean+tincrease, wind, rs=rs, elevation=elevation, lat=lat, \n",
94 | " tmax=tmax+tincrease, tmin=tmin+tincrease, rh=rh, co2=co2)\n",
95 | " return pe\n",
96 | "\n",
97 | "# Estimate evapotranspiration with four different methods and create a dataframe\n",
98 | "pet_df = pd.DataFrame()\n",
99 | "pet_df[\"amb.\"] = pe_cc(0, 420)\n",
100 | "pet_df[\"RCP 2.6\"] = pe_cc(rcp_26[0], rcp_26[1])\n",
101 | "pet_df[\"RCP 4.5\"] = pe_cc(rcp_45[0], rcp_45[1])\n",
102 | "pet_df[\"RCP 8.5\"] = pe_cc(rcp_85[0], rcp_85[1])"
103 | ]
104 | },
105 | {
106 | "cell_type": "code",
107 | "execution_count": null,
108 | "metadata": {},
109 | "outputs": [],
110 | "source": [
111 | "fig, axs = plt.subplots(figsize=(13,4), ncols=2)\n",
112 | "pet_df.plot(ax=axs[0])\n",
113 | "pet_df.cumsum().plot(ax=axs[1], legend=False)\n",
114 | "\n",
115 | "axs[0].set_ylabel(\"PET [mm/day]\", fontsize=12)\n",
116 | "axs[1].set_ylabel(\"Cumulative PET [mm]\", fontsize=12)\n",
117 | "axs[0].legend(ncol=6, loc=[0,1.])\n",
118 | "for i in (0,1):\n",
119 | " axs[i].set_xlabel(\"Date\", fontsize=12)"
120 | ]
121 | }
122 | ],
123 | "metadata": {
124 | "kernelspec": {
125 | "display_name": "Python 3 (ipykernel)",
126 | "language": "python",
127 | "name": "python3"
128 | },
129 | "language_info": {
130 | "codemirror_mode": {
131 | "name": "ipython",
132 | "version": 3
133 | },
134 | "file_extension": ".py",
135 | "mimetype": "text/x-python",
136 | "name": "python",
137 | "nbconvert_exporter": "python",
138 | "pygments_lexer": "ipython3",
139 | "version": "3.11.5"
140 | }
141 | },
142 | "nbformat": 4,
143 | "nbformat_minor": 4
144 | }
145 |
--------------------------------------------------------------------------------
/docs/examples/08_crop_coefficient.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "999bc8b7-804c-4301-b8c7-5077a38dc06f",
6 | "metadata": {
7 | "pycharm": {
8 | "name": "#%% md\n"
9 | }
10 | },
11 | "source": [
12 | "# Determining the crop coefficient function with Python\n",
13 | "\n",
14 | "*M. Vremec, October 2022, University of Graz*\n",
15 | "\n",
16 | "Data source: ZAMG - https://data.hub.zamg.ac.at\n",
17 | "\n",
18 | "What is done:\n",
19 | "\n",
20 | "- load the station data from ZAMG\n",
21 | "- estimate potential evapotranspiration\n",
22 | "- determine the crop coefficient function based on equation 65 in Allen et al. 1998\n",
23 | "- plot and store result"
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": null,
29 | "id": "7a2ae568-3c3b-44f5-8b82-ab5a99d35d2d",
30 | "metadata": {
31 | "pycharm": {
32 | "name": "#%%\n"
33 | }
34 | },
35 | "outputs": [],
36 | "source": [
37 | "import pandas as pd\n",
38 | "import matplotlib.pyplot as plt\n",
39 | "import numpy as np\n",
40 | "import pyet\n",
41 | "pyet.show_versions()"
42 | ]
43 | },
44 | {
45 | "cell_type": "markdown",
46 | "id": "da6f3e98-0073-4db9-b763-03beb2611e78",
47 | "metadata": {
48 | "pycharm": {
49 | "name": "#%% md\n"
50 | }
51 | },
52 | "source": [
53 | "## 1. Loading daily data from ZAMG (Messstationen Tagesdaten)\n",
54 | "\n",
55 | "station: Graz Universität 16412\n",
56 | "\n",
57 | "Selected variables:\n",
58 | "- globalstrahlung (global radiation), J/cm2 needs to be in MJ/m3d, ZAMG abbreviation - strahl\n",
59 | "- arithmetische windgeschwindigkeit (wind speed), m/s, ZAMG abbreviation - vv\n",
60 | "- relative feuchte (relative humidity), %, ZAMG abbreviation - rel\n",
61 | "- lufttemparatur (air temperature) in 2 m, C, ZAMG abbreviation - t\n",
62 | "- lufttemperatur (air temperature) max in 2 m, C, ZAMG abbreviation - tmax\n",
63 | "- lufttemperatur (air temperature) min in 2 m, C, ZAMG abbreviation - tmin\n",
64 | "- latitute and elevation of a station"
65 | ]
66 | },
67 | {
68 | "cell_type": "code",
69 | "execution_count": null,
70 | "id": "236434b2-3c33-4772-93ec-de0cb7317209",
71 | "metadata": {
72 | "pycharm": {
73 | "name": "#%%\n"
74 | }
75 | },
76 | "outputs": [],
77 | "source": [
78 | "#read data\n",
79 | "data_16412 = pd.read_csv('data/example_1/klima_daily.csv', index_col=1, parse_dates=True)\n",
80 | "data_16412"
81 | ]
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "id": "22ebf3bc-6b9a-4f81-a455-7ca1b51879dd",
86 | "metadata": {
87 | "pycharm": {
88 | "name": "#%% md\n"
89 | }
90 | },
91 | "source": [
92 | "## 2. Calculate PET for Graz Universität - 16412"
93 | ]
94 | },
95 | {
96 | "cell_type": "code",
97 | "execution_count": null,
98 | "id": "bc5ba933-22c2-4c5b-9ca6-43a4bcdad344",
99 | "metadata": {
100 | "pycharm": {
101 | "name": "#%%\n"
102 | }
103 | },
104 | "outputs": [],
105 | "source": [
106 | "# Convert Glabalstrahlung J/cm2 to MJ/m2 by dividing to 100\n",
107 | "\n",
108 | "meteo = pd.DataFrame({\"time\":data_16412.index, \"tmean\":data_16412.t, \"tmax\":data_16412.tmax, \"tmin\":data_16412.tmin, \"rh\":data_16412.rel, \n",
109 | " \"wind\":data_16412.vv, \"rs\":data_16412.strahl/100})\n",
110 | "time, tmean, tmax, tmin, rh, wind, rs = [meteo[col] for col in meteo.columns]\n",
111 | "\n",
112 | "lat = 47.077778*np.pi/180 # Latitude of the meteorological station, converting from degrees to radians\n",
113 | "elevation = 367 # meters above sea-level\n",
114 | "\n",
115 | "# Estimate potential ET with Penman-Monteith FAO-56\n",
116 | "pet_pm = pyet.pm_fao56(tmean, wind, rs=rs, elevation=elevation, \n",
117 | " lat=lat, tmax=tmax, tmin=tmin, rh=rh)"
118 | ]
119 | },
120 | {
121 | "cell_type": "markdown",
122 | "id": "696cb6aa-773d-4e1b-8a11-64ee5a5242e9",
123 | "metadata": {},
124 | "source": [
125 | "## 3. Determine the crop coefficient function\n",
126 | "\n",
127 | "Based on: https://www.fao.org/3/x0490e/x0490e0b.htm\n",
128 | "figure 34.\n",
129 | "\n",
130 | "\n",
131 | ""
132 | ]
133 | },
134 | {
135 | "cell_type": "code",
136 | "execution_count": null,
137 | "id": "f92b3767-7233-461c-a924-cafc6f5c9882",
138 | "metadata": {},
139 | "outputs": [],
140 | "source": [
141 | "Kcini = 0.3 \n",
142 | "Kcmid = 1.1\n",
143 | "Kcend = 0.65\n",
144 | "\n",
145 | "crop_ini = pd.Timestamp(\"2020-04-01\")\n",
146 | "crop_dev = pd.Timestamp(\"2020-05-01\")\n",
147 | "mid_season = pd.Timestamp(\"2020-06-01\")\n",
148 | "late_s_start = pd.Timestamp(\"2020-07-01\")\n",
149 | "late_s_end = pd.Timestamp(\"2020-08-01\")"
150 | ]
151 | },
152 | {
153 | "cell_type": "code",
154 | "execution_count": null,
155 | "id": "86d39fcb-0dea-42ca-9b7a-afa5327ad990",
156 | "metadata": {},
157 | "outputs": [],
158 | "source": [
159 | "kc = pd.Series(index=[crop_ini, crop_dev, mid_season, late_s_start, late_s_end],\n",
160 | " data=[Kcini, Kcini, Kcmid, Kcmid, Kcend])\n",
161 | "kc = kc.resample(\"d\").mean().interpolate()\n",
162 | "kc.plot()"
163 | ]
164 | },
165 | {
166 | "cell_type": "code",
167 | "execution_count": null,
168 | "id": "c73067c3-2d7e-4bb4-8cff-91f707f7c34b",
169 | "metadata": {},
170 | "outputs": [],
171 | "source": [
172 | "petc = pet_pm.loc[crop_dev:late_s_end] * kc"
173 | ]
174 | },
175 | {
176 | "cell_type": "markdown",
177 | "id": "e6e2564c-48ed-46b7-a275-fd17db592456",
178 | "metadata": {
179 | "pycharm": {
180 | "name": "#%% md\n"
181 | }
182 | },
183 | "source": [
184 | "## 4. Plot results"
185 | ]
186 | },
187 | {
188 | "cell_type": "code",
189 | "execution_count": null,
190 | "id": "c9a4582f-219b-47f8-9ed2-dac80720cf55",
191 | "metadata": {
192 | "pycharm": {
193 | "name": "#%%\n"
194 | }
195 | },
196 | "outputs": [],
197 | "source": [
198 | "pet_pm.loc[crop_dev:late_s_end].plot(label=\"Potential evapotranspiration\")\n",
199 | "petc.loc[crop_dev:late_s_end].plot(label=\"Potential crop evapotranspiration\")"
200 | ]
201 | }
202 | ],
203 | "metadata": {
204 | "kernelspec": {
205 | "display_name": "Python 3 (ipykernel)",
206 | "language": "python",
207 | "name": "python3"
208 | },
209 | "language_info": {
210 | "codemirror_mode": {
211 | "name": "ipython",
212 | "version": 3
213 | },
214 | "file_extension": ".py",
215 | "mimetype": "text/x-python",
216 | "name": "python",
217 | "nbconvert_exporter": "python",
218 | "pygments_lexer": "ipython3",
219 | "version": "3.11.5"
220 | }
221 | },
222 | "nbformat": 4,
223 | "nbformat_minor": 5
224 | }
225 |
--------------------------------------------------------------------------------
/docs/examples/09_CMIP6_data.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "markdown",
5 | "id": "d8992de4-0b85-4df3-8835-d9dd3e7a3901",
6 | "metadata": {},
7 | "source": [
8 | "# Potential Evapotranspiration from CMIP6 climate projections (NetCDF)\n",
9 | "\n",
10 | "*M. Vremec, December 2022, University of Graz*\n",
11 | "\n",
12 | "\n",
13 | "What is done:\n",
14 | "\n",
15 | "- load the data from Copernicus\n",
16 | "- estimate potential evapotranspiration\n",
17 | "- plot\n",
18 | "\n",
19 | "Data source: \n",
20 | "* Copernicus - https://cds.climate.copernicus.eu/ (SSP1-1.9, EC-Earth3 (Europe))\n",
21 | "* [CMIP6 GMD special issue articles](https://gmd.copernicus.org/articles/special_issue590.html)\n",
22 | "* [Terms of using CMIP6 data](https://pcmdi.llnl.gov/CMIP6/TermsOfUse/TermsOfUse6-2.html)"
23 | ]
24 | },
25 | {
26 | "cell_type": "code",
27 | "execution_count": null,
28 | "id": "768010e4-9e5d-4136-80ab-b8eea806dc69",
29 | "metadata": {},
30 | "outputs": [],
31 | "source": [
32 | "import numpy as np\n",
33 | "import pandas as pd\n",
34 | "import xarray as xr\n",
35 | "import pyet"
36 | ]
37 | },
38 | {
39 | "cell_type": "code",
40 | "execution_count": null,
41 | "id": "69ade8af-753a-4b8d-8680-9531ea24a070",
42 | "metadata": {},
43 | "outputs": [],
44 | "source": [
45 | "# Import data\n",
46 | "xr_ds = xr.open_dataset(\"data/example_9/tas_day_EC-Earth3_ssp119_r4i1p1f1_gr_21000601-21000630_v20200425.nc\", \n",
47 | " engine=\"netcdf4\")"
48 | ]
49 | },
50 | {
51 | "cell_type": "code",
52 | "execution_count": null,
53 | "id": "88f411a2-13b5-41c5-859c-b7d661ea5959",
54 | "metadata": {},
55 | "outputs": [],
56 | "source": [
57 | "# Define mean temperature and latitude\n",
58 | "tmean = xr_ds[\"tas\"] - 273\n",
59 | "lat = xr_ds.lat * np.pi/180"
60 | ]
61 | },
62 | {
63 | "cell_type": "code",
64 | "execution_count": null,
65 | "id": "3acf61a9-de98-4048-bcf1-70ef84e871bd",
66 | "metadata": {},
67 | "outputs": [],
68 | "source": [
69 | "# Compute PET with Oudin\n",
70 | "pet_oudin = pyet.oudin(tmean, lat=lat)"
71 | ]
72 | },
73 | {
74 | "cell_type": "code",
75 | "execution_count": null,
76 | "id": "8523231e-c618-4ad9-bb0d-204d2103ddb1",
77 | "metadata": {},
78 | "outputs": [],
79 | "source": [
80 | "pet_oudin.sel(time=\"2100-6-2\").plot()"
81 | ]
82 | },
83 | {
84 | "cell_type": "markdown",
85 | "id": "9abfc543-b01e-4f76-bc36-81094d5d842f",
86 | "metadata": {},
87 | "source": [
88 | "## Acknowledgement\n",
89 | "\n",
90 | "We acknowledge the World Climate Research Programme, which, through its Working Group on Coupled Modelling, coordinated and promoted CMIP6. We thank the climate modeling groups for producing and making available their model output, the Earth System Grid Federation (ESGF) for archiving the data and providing access, and the multiple funding agencies who support CMIP6 and ESGF."
91 | ]
92 | }
93 | ],
94 | "metadata": {
95 | "kernelspec": {
96 | "display_name": "Python 3 (ipykernel)",
97 | "language": "python",
98 | "name": "python3"
99 | },
100 | "language_info": {
101 | "codemirror_mode": {
102 | "name": "ipython",
103 | "version": 3
104 | },
105 | "file_extension": ".py",
106 | "mimetype": "text/x-python",
107 | "name": "python",
108 | "nbconvert_exporter": "python",
109 | "pygments_lexer": "ipython3",
110 | "version": "3.11.5"
111 | }
112 | },
113 | "nbformat": 4,
114 | "nbformat_minor": 5
115 | }
116 |
--------------------------------------------------------------------------------
/docs/examples/data/example_10/10_example_meteo.csv:
--------------------------------------------------------------------------------
1 | YYYYMMDD,Tmean[°C],Tmax[°C],Tmin[°C],RH[%],Rs[MJ/m2/d],u2[m/s],PET_KNMI[mm/d]
2 | 2018-01-01,6.8,8.8,5.2,84,2.24,5.0,0.3
3 | 2018-01-02,6.5,9.1,4.5,88,1.74,4.5,0.2
4 | 2018-01-03,8.8,11.2,5.4,73,0.76,8.8,0.1
5 | 2018-01-04,8.2,10.7,6.8,82,1.09,5.6,0.2
6 | 2018-01-05,6.4,8.7,4.4,87,1.16,4.0,0.2
7 | 2018-01-06,4.5,6.6,2.2,89,1.89,2.8,0.2
8 | 2018-01-07,1.0,3.1,-1.2,77,4.11,5.9,0.5
9 | 2018-01-08,1.1,3.9,-1.4,77,3.88,5.5,0.4
10 | 2018-01-09,3.0,6.1,-0.2,89,1.9,3.3,0.2
11 | 2018-01-10,6.9,7.6,5.8,94,0.69,2.8,0.1
12 | 2018-01-11,5.7,7.6,2.8,97,1.35,1.6,0.2
13 | 2018-01-12,5.0,5.9,4.1,94,1.03,1.5,0.1
14 | 2018-01-13,4.2,6.2,1.2,90,0.9,3.1,0.1
15 | 2018-01-14,2.9,5.2,0.8,82,3.69,4.3,0.4
16 | 2018-01-15,5.2,8.0,1.7,89,0.57,6.5,0.1
17 | 2018-01-16,5.2,6.9,2.5,78,3.36,4.9,0.4
18 | 2018-01-17,4.6,6.0,2.0,73,2.05,5.3,0.3
19 | 2018-01-18,4.7,10.2,0.5,85,1.67,6.4,0.2
20 | 2018-01-19,3.7,6.9,1.4,85,3.26,3.1,0.4
21 | 2018-01-20,2.2,3.6,0.8,93,1.21,2.7,0.1
22 | 2018-01-21,3.0,6.9,-0.4,85,4.31,2.6,0.5
23 | 2018-01-22,6.3,8.4,2.0,88,2.37,3.5,0.3
24 | 2018-01-23,8.1,11.2,4.6,88,2.21,5.0,0.3
25 | 2018-01-24,11.7,14.4,7.3,84,1.47,7.0,0.2
26 | 2018-01-25,7.7,9.2,5.0,88,2.21,3.8,0.3
27 | 2018-01-26,5.4,8.6,2.5,95,2.97,1.8,0.4
28 | 2018-01-27,5.8,7.2,4.4,83,2.87,5.0,0.4
29 | 2018-01-28,9.7,10.8,6.9,90,2.78,5.4,0.4
30 | 2018-01-29,8.8,10.7,4.6,84,1.66,5.8,0.2
31 | 2018-01-30,4.7,7.4,2.1,86,4.85,2.5,0.6
32 | 2018-01-31,6.5,9.5,3.2,84,0.99,5.4,0.1
33 | 2018-02-01,3.4,6.1,1.9,87,2.9,4.1,0.3
34 | 2018-02-02,4.4,6.8,-0.5,87,3.35,3.0,0.4
35 | 2018-02-03,2.0,4.6,-0.4,90,1.85,2.6,0.2
36 | 2018-02-04,1.7,3.6,-0.4,76,5.0,4.3,0.6
37 | 2018-02-05,1.0,3.4,-2.1,81,3.41,4.1,0.4
38 | 2018-02-06,-2.1,2.0,-4.6,76,7.89,3.2,0.8
39 | 2018-02-07,-2.2,1.9,-6.8,76,7.92,2.1,0.8
40 | 2018-02-08,1.0,4.7,-2.5,79,6.66,2.6,0.7
41 | 2018-02-09,0.6,2.2,-0.9,79,4.15,4.6,0.4
42 | 2018-02-10,2.9,7.0,0.2,84,4.78,3.5,0.6
43 | 2018-02-11,4.5,7.3,2.1,80,3.73,5.7,0.5
44 | 2018-02-12,3.0,6.3,0.7,74,6.43,3.7,0.8
45 | 2018-02-13,2.0,6.3,-1.2,71,8.6,4.3,1.0
46 | 2018-02-14,1.9,5.5,-2.2,65,8.73,5.4,1.0
47 | 2018-02-15,4.3,9.9,0.5,88,2.52,4.5,0.3
48 | 2018-02-16,3.2,8.7,-2.1,79,8.3,2.5,1.0
49 | 2018-02-17,0.9,7.6,-3.6,82,6.86,1.9,0.8
50 | 2018-02-18,0.7,7.3,-4.9,87,8.4,2.3,0.9
51 | 2018-02-19,1.2,3.4,-2.5,84,3.26,2.5,0.4
52 | 2018-02-20,0.8,4.6,-3.8,82,4.39,2.3,0.5
53 | 2018-02-21,0.3,4.7,-3.9,77,9.29,2.8,1.0
54 | 2018-02-22,0.2,5.0,-3.0,71,9.89,4.0,1.1
55 | 2018-02-23,-0.5,3.5,-3.8,71,10.68,4.9,1.1
56 | 2018-02-24,-0.4,3.3,-3.4,55,10.13,6.6,1.1
57 | 2018-02-25,-2.2,1.1,-4.9,56,11.4,5.7,1.1
58 | 2018-02-26,-3.3,0.0,-5.9,67,8.66,4.6,0.8
59 | 2018-02-27,-3.6,0.2,-6.4,70,10.28,3.4,0.9
60 | 2018-02-28,-6.6,-4.6,-8.5,69,8.51,6.5,0.7
61 | 2018-03-01,-4.7,-0.7,-8.3,50,8.38,9.4,0.7
62 | 2018-03-02,-3.7,-0.5,-7.2,52,8.51,8.3,0.8
63 | 2018-03-03,-1.7,1.2,-4.5,64,5.36,4.9,0.5
64 | 2018-03-04,5.0,11.3,-2.2,78,8.93,4.4,1.1
65 | 2018-03-05,6.6,12.0,3.2,80,11.25,3.7,1.5
66 | 2018-03-06,5.6,10.1,1.0,82,7.08,2.4,0.9
67 | 2018-03-07,5.9,9.5,0.8,87,7.25,2.8,0.9
68 | 2018-03-08,4.8,8.3,1.2,84,4.26,5.4,0.5
69 | 2018-03-09,6.5,9.7,2.9,82,9.73,3.3,1.3
70 | 2018-03-10,11.4,15.1,7.1,89,6.9,3.5,1.0
71 | 2018-03-11,10.2,13.9,7.1,91,5.72,2.9,0.8
72 | 2018-03-12,10.3,14.3,7.4,85,7.2,3.0,1.1
73 | 2018-03-13,5.6,8.9,3.7,92,2.05,3.4,0.3
74 | 2018-03-14,5.8,11.1,-0.4,80,13.69,3.8,1.8
75 | 2018-03-15,7.3,10.7,3.6,70,7.61,6.0,1.0
76 | 2018-03-16,3.8,6.2,0.0,87,1.99,5.8,0.2
77 | 2018-03-17,-1.4,0.0,-2.6,60,6.21,9.3,0.6
78 | 2018-03-18,-1.0,1.2,-3.0,47,10.75,8.0,1.1
79 | 2018-03-19,-0.2,4.7,-3.9,45,16.62,5.3,1.7
80 | 2018-03-20,3.8,7.9,-1.3,69,16.63,4.4,2.0
81 | 2018-03-21,3.6,8.0,-3.6,78,11.26,2.8,1.4
82 | 2018-03-22,6.4,9.9,4.1,88,6.2,3.5,0.8
83 | 2018-03-23,6.2,7.3,5.2,79,5.73,4.1,0.8
84 | 2018-03-24,6.5,12.5,0.0,77,11.32,2.5,1.5
85 | 2018-03-25,5.4,12.3,-1.8,79,13.62,2.2,1.7
86 | 2018-03-26,4.7,11.2,0.0,88,9.94,1.6,1.2
87 | 2018-03-27,5.6,9.8,0.7,88,6.69,3.3,0.9
88 | 2018-03-28,5.9,7.8,2.8,92,3.18,2.8,0.4
89 | 2018-03-29,6.5,10.7,3.6,77,12.31,3.2,1.6
90 | 2018-03-30,9.0,13.5,5.2,74,13.36,4.4,1.9
91 | 2018-03-31,7.2,12.4,2.5,83,10.5,2.0,1.4
92 | 2018-04-01,4.8,7.7,0.6,89,4.13,2.0,0.5
93 | 2018-04-02,7.7,12.1,0.4,82,7.47,3.8,1.0
94 | 2018-04-03,12.7,17.0,9.1,79,12.18,5.0,1.9
95 | 2018-04-04,10.3,14.7,7.8,83,7.17,3.7,1.1
96 | 2018-04-05,5.9,9.9,0.1,79,10.14,4.6,1.3
97 | 2018-04-06,8.8,14.7,0.0,58,19.55,5.5,2.8
98 | 2018-04-07,13.9,21.6,8.6,62,17.65,3.9,2.8
99 | 2018-04-08,15.2,23.4,7.7,72,17.23,2.0,2.9
100 | 2018-04-09,12.6,17.5,9.2,81,9.75,3.2,1.5
101 | 2018-04-10,15.5,20.0,9.5,72,12.41,3.0,2.1
102 | 2018-04-11,12.7,17.5,8.5,87,10.28,2.3,1.6
103 | 2018-04-12,10.4,14.6,6.5,89,6.96,2.9,1.0
104 | 2018-04-13,10.0,14.4,3.8,86,9.31,2.1,1.4
105 | 2018-04-14,11.5,16.1,6.9,77,12.14,2.0,1.8
106 | 2018-04-15,12.7,17.2,9.4,89,8.64,2.0,1.4
107 | 2018-04-16,11.4,16.1,5.1,77,19.18,2.8,2.9
108 | 2018-04-17,12.8,20.3,4.7,72,20.21,3.0,3.2
109 | 2018-04-18,16.3,23.5,6.9,65,21.76,2.4,3.7
110 | 2018-04-19,19.2,27.4,9.4,58,22.19,3.3,4.0
111 | 2018-04-20,18.3,26.5,10.0,66,21.81,2.3,3.9
112 | 2018-04-21,15.5,22.1,7.9,68,22.41,2.3,3.7
113 | 2018-04-22,17.9,26.6,9.9,61,19.53,3.2,3.4
114 | 2018-04-23,13.1,16.6,9.7,67,17.55,3.8,2.8
115 | 2018-04-24,12.9,15.5,9.5,76,7.46,4.7,1.2
116 | 2018-04-25,11.5,14.5,8.5,76,13.13,5.0,2.0
117 | 2018-04-26,10.3,13.7,7.7,69,18.29,4.7,2.7
118 | 2018-04-27,10.3,15.1,4.0,68,12.29,2.9,1.8
119 | 2018-04-28,11.7,14.5,4.9,79,8.59,3.1,1.3
120 | 2018-04-29,9.2,12.2,3.2,94,4.78,3.2,0.7
121 | 2018-04-30,9.5,13.2,5.8,88,5.51,4.2,0.8
122 | 2018-05-01,7.9,12.8,2.6,72,18.61,4.8,2.6
123 | 2018-05-02,10.9,16.9,1.8,67,24.57,4.6,3.7
124 | 2018-05-03,10.4,15.6,4.1,67,25.78,2.4,3.8
125 | 2018-05-04,11.9,18.5,2.3,64,25.86,2.3,4.0
126 | 2018-05-05,14.7,22.0,5.8,59,26.2,3.0,4.3
127 | 2018-05-06,17.2,25.8,6.2,57,26.79,2.5,4.6
128 | 2018-05-07,18.0,25.3,8.5,61,26.68,2.5,4.7
129 | 2018-05-08,19.1,26.6,9.7,60,26.4,2.4,4.7
130 | 2018-05-09,17.9,25.4,10.0,67,24.97,2.2,4.4
131 | 2018-05-10,12.1,16.7,5.3,80,15.25,3.0,2.4
132 | 2018-05-11,12.3,18.7,2.5,72,24.61,2.1,3.8
133 | 2018-05-12,17.0,23.3,7.2,61,20.03,2.8,3.4
134 | 2018-05-13,13.1,16.3,11.2,88,6.89,2.8,1.1
135 | 2018-05-14,18.7,25.8,11.2,71,23.03,3.6,4.1
136 | 2018-05-15,19.2,24.6,12.6,57,26.27,3.5,4.7
137 | 2018-05-16,15.3,20.7,11.0,75,18.5,4.6,3.1
138 | 2018-05-17,11.4,15.3,8.5,69,26.17,4.4,4.0
139 | 2018-05-18,10.9,14.6,8.1,69,18.08,3.3,2.7
140 | 2018-05-19,11.0,14.1,6.1,83,10.48,1.8,1.6
141 | 2018-05-20,15.4,24.2,4.7,70,26.83,2.0,4.5
142 | 2018-05-21,19.2,25.7,9.1,50,23.24,3.5,4.2
143 | 2018-05-22,18.3,24.3,14.4,68,16.09,2.3,2.8
144 | 2018-05-23,18.8,25.2,14.3,67,13.25,2.9,2.4
145 | 2018-05-24,18.3,22.6,13.3,69,10.33,3.1,1.8
146 | 2018-05-25,20.6,26.1,15.9,73,23.8,2.6,4.4
147 | 2018-05-26,23.0,29.0,14.7,59,25.74,3.1,4.9
148 | 2018-05-27,20.3,26.2,15.3,71,12.24,3.0,2.2
149 | 2018-05-28,22.5,29.7,13.1,69,24.65,2.5,4.7
150 | 2018-05-29,22.1,30.7,18.0,79,19.39,2.6,3.7
151 | 2018-05-30,21.5,26.5,16.8,80,22.12,2.0,4.1
152 | 2018-05-31,20.2,24.0,16.5,85,17.74,2.2,3.2
153 | 2018-06-01,17.6,23.4,14.4,90,11.6,2.5,2.0
154 | 2018-06-02,17.0,20.3,14.8,89,7.67,2.7,1.3
155 | 2018-06-03,19.3,23.5,15.5,79,15.68,2.3,2.8
156 | 2018-06-04,18.2,23.2,15.8,82,17.18,2.9,3.0
157 | 2018-06-05,16.7,20.8,13.4,76,20.81,2.9,3.6
158 | 2018-06-06,19.8,27.6,11.5,74,26.81,2.3,4.9
159 | 2018-06-07,22.4,28.6,12.8,62,28.21,2.0,5.3
160 | 2018-06-08,18.5,19.9,17.4,91,5.36,2.3,0.9
161 | 2018-06-09,19.8,24.5,16.2,79,23.7,3.0,4.3
162 | 2018-06-10,18.2,22.8,12.9,78,24.65,3.3,4.3
163 | 2018-06-11,16.9,22.4,10.1,75,26.24,3.8,4.5
164 | 2018-06-12,14.8,16.7,13.1,76,9.79,3.2,1.6
165 | 2018-06-13,14.7,19.4,9.6,71,17.5,3.0,2.9
166 | 2018-06-14,15.7,19.8,9.0,80,11.46,3.5,1.9
167 | 2018-06-15,18.8,24.2,14.8,72,24.25,2.0,4.3
168 | 2018-06-16,17.5,21.3,14.7,65,17.13,3.0,3.0
169 | 2018-06-17,15.6,18.2,14.2,75,9.32,3.8,1.6
170 | 2018-06-18,16.7,19.2,14.7,81,7.61,4.3,1.3
171 | 2018-06-19,17.7,20.4,13.8,83,7.0,2.6,1.2
172 | 2018-06-20,19.1,25.2,13.9,79,18.97,3.4,3.4
173 | 2018-06-21,14.4,17.7,11.6,68,19.21,5.0,3.1
174 | 2018-06-22,13.3,16.8,9.1,68,13.12,4.4,2.1
175 | 2018-06-23,15.1,20.0,8.9,71,16.3,2.5,2.7
176 | 2018-06-24,14.7,17.7,10.7,78,13.49,2.9,2.2
177 | 2018-06-25,16.5,21.4,10.5,75,23.64,2.9,4.0
178 | 2018-06-26,16.0,21.5,8.9,73,29.22,2.9,4.9
179 | 2018-06-27,17.9,24.5,9.9,75,26.9,3.4,4.7
180 | 2018-06-28,21.2,27.0,14.5,63,26.68,3.6,5.0
181 | 2018-06-29,19.5,26.1,13.7,66,29.53,3.8,5.3
182 | 2018-06-30,21.3,27.9,12.6,51,30.05,4.1,5.6
183 | 2018-07-01,21.3,26.5,15.6,36,30.56,5.8,5.7
184 | 2018-07-02,20.6,26.9,12.9,40,30.35,4.6,5.6
185 | 2018-07-03,19.6,26.0,12.5,60,29.61,3.7,5.4
186 | 2018-07-04,19.2,25.2,13.2,66,26.14,2.8,4.7
187 | 2018-07-05,18.9,24.3,13.6,74,27.72,2.5,4.9
188 | 2018-07-06,19.5,25.2,13.0,67,20.82,2.5,3.8
189 | 2018-07-07,18.3,24.9,10.0,63,25.05,2.2,4.4
190 | 2018-07-08,18.6,24.4,10.0,66,28.74,2.8,5.1
191 | 2018-07-09,17.5,22.7,11.5,76,13.15,2.9,2.3
192 | 2018-07-10,16.1,21.7,11.4,72,11.61,3.8,2.0
193 | 2018-07-11,17.8,22.7,9.3,74,17.5,3.1,3.1
194 | 2018-07-12,19.0,24.6,13.6,74,26.35,3.3,4.7
195 | 2018-07-13,18.5,24.3,12.5,73,25.91,2.5,4.6
196 | 2018-07-14,18.8,24.9,10.8,67,28.8,2.6,5.1
197 | 2018-07-15,20.8,28.7,10.6,60,27.49,2.0,5.1
198 | 2018-07-16,22.6,29.7,11.4,53,26.67,2.0,5.1
199 | 2018-07-17,20.0,26.0,12.0,67,25.65,2.9,4.7
200 | 2018-07-18,19.1,25.3,10.4,64,20.99,2.0,3.8
201 | 2018-07-19,19.6,25.9,11.8,67,21.23,2.4,3.8
202 | 2018-07-20,19.1,25.2,10.5,68,25.54,2.4,4.6
203 | 2018-07-21,21.1,26.8,14.7,65,26.25,2.4,4.9
204 | 2018-07-22,20.6,27.2,14.0,68,23.1,2.3,4.3
205 | 2018-07-23,22.5,29.7,13.1,67,26.46,1.5,5.0
206 | 2018-07-24,24.6,31.9,15.9,62,21.83,1.9,4.3
207 | 2018-07-25,24.4,30.2,18.3,60,16.23,2.5,3.2
208 | 2018-07-26,27.7,35.7,19.2,53,24.97,2.4,5.1
209 | 2018-07-27,29.7,35.4,22.4,34,25.69,4.0,5.4
210 | 2018-07-28,20.3,24.2,13.4,72,15.7,3.5,2.9
211 | 2018-07-29,21.3,26.7,13.5,56,16.07,3.5,3.0
212 | 2018-07-30,24.0,30.6,16.8,64,22.61,3.0,4.4
213 | 2018-07-31,20.6,26.6,13.0,72,21.24,3.0,3.9
214 | 2018-08-01,20.4,27.6,11.1,65,23.05,1.8,4.2
215 | 2018-08-02,22.6,29.7,14.5,64,25.01,2.2,4.8
216 | 2018-08-03,23.9,30.7,14.3,61,25.28,2.3,4.9
217 | 2018-08-04,22.4,28.3,16.2,70,21.1,2.8,4.0
218 | 2018-08-05,20.5,25.5,14.4,68,23.32,2.5,4.3
219 | 2018-08-06,23.1,31.3,12.6,57,25.31,1.9,4.9
220 | 2018-08-07,25.9,33.9,14.6,52,23.43,1.8,4.7
221 | 2018-08-08,19.6,23.2,12.0,76,12.76,3.9,2.3
222 | 2018-08-09,17.2,23.0,10.9,82,7.96,3.4,1.4
223 | 2018-08-10,16.5,21.1,12.6,73,17.66,4.9,3.0
224 | 2018-08-11,15.6,20.9,11.0,71,20.85,3.1,3.5
225 | 2018-08-12,19.8,27.1,11.2,66,15.22,2.5,2.8
226 | 2018-08-13,18.7,22.6,16.2,84,9.18,2.5,1.6
227 | 2018-08-14,18.8,22.4,15.7,79,15.08,3.1,2.7
228 | 2018-08-15,19.7,22.6,16.4,78,10.3,3.3,1.9
229 | 2018-08-16,18.4,23.6,14.9,79,14.45,3.4,2.6
230 | 2018-08-17,17.4,23.2,11.0,77,19.25,1.8,3.3
231 | 2018-08-18,17.8,21.7,10.9,78,11.59,2.9,2.0
232 | 2018-08-19,19.4,22.7,17.0,78,9.53,4.0,1.7
233 | 2018-08-20,19.6,23.1,14.5,82,9.85,2.2,1.8
234 | 2018-08-21,20.0,25.8,14.2,81,15.7,1.3,2.9
235 | 2018-08-22,19.5,25.3,13.6,82,18.97,1.9,3.4
236 | 2018-08-23,18.3,22.2,12.9,85,8.17,3.3,1.4
237 | 2018-08-24,15.5,19.8,10.8,72,16.53,3.7,2.8
238 | 2018-08-25,12.7,16.8,9.1,85,13.83,2.7,2.2
239 | 2018-08-26,14.5,19.6,7.0,72,15.74,4.1,2.6
240 | 2018-08-27,17.1,19.3,13.3,80,7.48,4.0,1.3
241 | 2018-08-28,16.7,21.3,10.1,79,11.78,1.9,2.0
242 | 2018-08-29,14.7,19.7,8.3,89,7.42,1.9,1.2
243 | 2018-08-30,15.0,19.9,8.0,76,17.95,2.5,3.0
244 | 2018-08-31,12.4,18.0,6.9,80,9.43,1.0,1.5
245 | 2018-09-01,13.5,20.8,5.2,74,17.4,1.5,2.8
246 | 2018-09-02,15.9,22.5,7.1,72,17.31,2.5,2.9
247 | 2018-09-03,18.6,22.2,14.5,83,7.83,2.4,1.4
248 | 2018-09-04,20.6,25.3,18.3,88,12.86,1.7,2.4
249 | 2018-09-05,19.0,22.8,17.0,89,9.29,2.7,1.7
250 | 2018-09-06,16.6,19.0,13.1,94,4.69,1.6,0.8
251 | 2018-09-07,14.6,17.4,12.2,76,15.32,3.7,2.5
252 | 2018-09-08,14.8,18.4,11.5,76,11.12,3.3,1.8
253 | 2018-09-09,18.3,23.6,13.3,71,14.13,3.1,2.5
254 | 2018-09-10,17.4,20.1,14.9,78,8.79,3.2,1.5
255 | 2018-09-11,18.8,22.6,14.9,79,9.69,4.8,1.7
256 | 2018-09-12,15.3,18.4,11.9,90,4.44,2.4,0.7
257 | 2018-09-13,13.0,19.5,7.0,78,17.69,1.3,2.8
258 | 2018-09-14,13.5,19.9,4.8,74,14.3,3.0,2.3
259 | 2018-09-15,14.7,20.3,8.8,80,11.07,2.5,1.8
260 | 2018-09-16,14.6,22.4,7.8,78,11.56,2.4,1.9
261 | 2018-09-17,15.3,24.2,7.6,77,15.48,2.3,2.6
262 | 2018-09-18,18.9,24.6,9.3,67,16.01,4.4,2.9
263 | 2018-09-19,19.1,23.6,13.8,72,13.15,4.5,2.4
264 | 2018-09-20,19.7,23.6,16.4,69,12.16,4.2,2.2
265 | 2018-09-21,15.4,20.0,9.5,67,9.86,6.1,1.6
266 | 2018-09-22,12.1,15.8,10.5,84,5.93,3.5,0.9
267 | 2018-09-23,9.5,10.9,5.8,95,2.62,2.0,0.4
268 | 2018-09-24,9.9,15.4,4.9,79,13.3,2.5,1.9
269 | 2018-09-25,9.4,16.4,2.5,79,15.17,1.5,2.2
270 | 2018-09-26,11.9,19.0,3.9,76,14.28,2.8,2.2
271 | 2018-09-27,13.2,21.5,6.7,73,14.74,2.1,2.3
272 | 2018-09-28,11.4,16.3,2.7,75,11.6,3.1,1.8
273 | 2018-09-29,7.5,15.5,1.0,80,13.52,0.9,1.8
274 | 2018-09-30,9.9,17.5,-0.1,77,11.87,1.8,1.7
275 | 2018-10-01,9.5,13.3,6.2,82,10.16,2.9,1.5
276 | 2018-10-02,11.7,16.5,7.8,84,5.78,3.7,0.9
277 | 2018-10-03,12.6,16.4,8.4,73,10.76,2.6,1.7
278 | 2018-10-04,14.7,18.0,11.6,82,5.71,3.3,0.9
279 | 2018-10-05,12.8,21.2,7.3,83,12.54,1.9,2.0
280 | 2018-10-06,13.9,21.0,6.3,83,11.48,2.6,1.8
281 | 2018-10-07,10.1,15.3,3.6,79,11.1,2.6,1.6
282 | 2018-10-08,9.5,16.9,2.9,84,9.8,2.0,1.4
283 | 2018-10-09,10.9,19.1,5.8,84,11.59,2.0,1.7
284 | 2018-10-10,15.1,23.5,5.0,74,11.72,3.0,1.9
285 | 2018-10-11,17.6,22.9,13.0,73,8.85,3.5,1.5
286 | 2018-10-12,18.7,22.8,14.7,76,10.23,4.0,1.8
287 | 2018-10-13,20.0,26.3,15.4,69,10.34,3.3,1.9
288 | 2018-10-14,17.4,24.2,12.6,70,9.7,3.4,1.7
289 | 2018-10-15,16.1,22.9,10.7,76,8.42,2.1,1.4
290 | 2018-10-16,15.3,24.3,8.8,80,9.42,1.6,1.6
291 | 2018-10-17,13.2,20.2,8.2,88,7.63,1.4,1.2
292 | 2018-10-18,12.2,16.3,7.3,82,8.67,2.6,1.3
293 | 2018-10-19,9.2,15.8,2.7,83,9.77,1.9,1.4
294 | 2018-10-20,8.6,15.6,1.5,87,6.52,1.0,0.9
295 | 2018-10-21,12.2,16.5,6.1,79,6.35,1.9,1.0
296 | 2018-10-22,10.8,15.0,3.1,81,7.38,2.9,1.1
297 | 2018-10-23,12.8,14.7,7.9,78,2.34,4.6,0.4
298 | 2018-10-24,14.0,16.3,12.2,89,4.52,3.7,0.7
299 | 2018-10-25,12.1,13.6,10.9,86,2.2,2.8,0.3
300 | 2018-10-26,9.5,11.8,6.0,87,1.94,3.9,0.3
301 | 2018-10-27,7.1,11.3,3.6,83,7.86,2.5,1.1
302 | 2018-10-28,4.0,7.8,0.7,74,8.63,4.0,1.1
303 | 2018-10-29,4.4,6.1,2.0,85,1.69,4.3,0.2
304 | 2018-10-30,5.7,9.4,3.7,92,1.17,4.5,0.2
305 | 2018-10-31,8.5,11.4,4.6,80,6.82,3.7,1.0
306 | 2018-11-01,10.4,13.2,8.5,82,2.98,3.4,0.4
307 | 2018-11-02,7.8,11.8,2.3,82,6.97,2.7,1.0
308 | 2018-11-03,4.3,10.1,-0.1,90,5.88,2.0,0.7
309 | 2018-11-04,5.9,11.9,0.6,90,6.02,2.6,0.8
310 | 2018-11-05,7.7,11.2,5.1,94,4.15,2.7,0.6
311 | 2018-11-06,12.4,19.0,6.8,78,6.27,3.1,1.0
312 | 2018-11-07,11.1,13.9,8.7,82,3.21,4.4,0.5
313 | 2018-11-08,9.2,13.7,4.0,83,6.54,3.1,0.9
314 | 2018-11-09,9.0,11.2,5.8,87,4.0,3.2,0.6
315 | 2018-11-10,11.6,13.4,9.3,90,1.78,4.8,0.3
316 | 2018-11-11,11.4,13.4,8.2,87,2.09,4.2,0.3
317 | 2018-11-12,10.0,11.7,8.5,95,1.76,2.5,0.3
318 | 2018-11-13,10.2,13.2,8.1,86,5.01,4.6,0.7
319 | 2018-11-14,8.0,13.2,3.8,90,5.37,2.7,0.7
320 | 2018-11-15,6.9,12.8,3.1,88,5.38,2.8,0.7
321 | 2018-11-16,3.4,7.7,0.6,96,2.16,2.3,0.3
322 | 2018-11-17,4.1,9.1,1.0,79,5.56,3.5,0.7
323 | 2018-11-18,2.9,7.8,-1.4,69,5.31,3.9,0.6
324 | 2018-11-19,4.9,6.9,2.8,80,1.73,5.3,0.2
325 | 2018-11-20,3.4,4.4,2.6,73,0.59,7.0,0.1
326 | 2018-11-21,3.4,5.3,1.8,81,2.54,3.7,0.3
327 | 2018-11-22,3.2,4.3,1.7,84,1.59,3.3,0.2
328 | 2018-11-23,3.2,6.9,-0.8,88,3.07,2.3,0.4
329 | 2018-11-24,2.4,3.5,0.6,94,1.01,2.6,0.1
330 | 2018-11-25,3.4,4.7,1.9,93,0.95,2.9,0.1
331 | 2018-11-26,4.9,6.0,3.5,86,0.9,3.6,0.1
332 | 2018-11-27,3.3,5.2,1.4,86,1.33,2.8,0.2
333 | 2018-11-28,6.1,9.5,2.8,91,0.9,5.3,0.1
334 | 2018-11-29,10.2,11.2,9.5,87,0.89,5.1,0.1
335 | 2018-11-30,9.6,11.6,6.3,83,2.36,4.5,0.3
336 | 2018-12-01,7.7,9.6,5.7,92,2.15,4.8,0.3
337 | 2018-12-02,12.0,13.2,9.6,95,0.61,5.4,0.1
338 | 2018-12-03,11.4,13.3,7.4,88,2.17,4.8,0.3
339 | 2018-12-04,4.8,9.6,0.2,85,3.5,1.6,0.4
340 | 2018-12-05,5.5,8.4,1.7,93,1.36,3.5,0.2
341 | 2018-12-06,11.1,12.3,8.4,95,0.7,4.7,0.1
342 | 2018-12-07,10.0,11.8,7.1,89,0.44,6.2,0.1
343 | 2018-12-08,9.1,11.2,6.7,81,0.9,6.7,0.1
344 | 2018-12-09,8.1,10.0,5.5,81,1.88,5.3,0.3
345 | 2018-12-10,5.5,7.7,3.6,81,2.04,3.2,0.3
346 | 2018-12-11,3.9,7.7,-0.9,88,2.39,1.6,0.3
347 | 2018-12-12,0.8,4.3,-2.9,90,1.67,2.0,0.2
348 | 2018-12-13,0.8,2.5,-0.8,89,3.6,4.0,0.4
349 | 2018-12-14,0.9,3.3,-0.8,86,3.82,2.9,0.4
350 | 2018-12-15,0.3,0.8,-1.1,73,1.06,5.0,0.1
351 | 2018-12-16,1.4,3.3,-1.0,98,0.9,3.5,0.1
352 | 2018-12-17,5.6,8.4,2.4,94,0.88,3.5,0.1
353 | 2018-12-18,6.4,7.9,4.4,87,1.82,4.8,0.2
354 | 2018-12-19,7.4,9.8,5.5,86,1.29,4.2,0.2
355 | 2018-12-20,7.5,8.7,5.4,90,0.76,4.9,0.1
356 | 2018-12-21,9.7,12.3,7.4,90,0.47,5.9,0.1
357 | 2018-12-22,9.6,10.8,8.9,85,1.27,4.6,0.2
358 | 2018-12-23,7.1,8.9,5.4,95,0.67,2.8,0.1
359 | 2018-12-24,5.7,8.4,3.2,87,3.55,1.5,0.5
360 | 2018-12-25,5.6,7.5,3.7,77,2.19,3.2,0.3
361 | 2018-12-26,4.0,5.3,1.5,89,0.91,2.6,0.1
362 | 2018-12-27,0.8,4.8,-2.7,90,2.43,1.2,0.3
363 | 2018-12-28,2.3,5.4,-3.1,95,0.46,1.6,0.1
364 | 2018-12-29,7.9,10.9,5.4,88,0.57,4.5,0.1
365 | 2018-12-30,8.5,9.5,6.2,90,1.12,2.6,0.2
366 | 2018-12-31,8.9,10.0,7.7,93,1.37,2.7,0.2
367 |
--------------------------------------------------------------------------------
/docs/examples/data/example_10/df_Guo_2016.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/df_Guo_2016.xlsx
--------------------------------------------------------------------------------
/docs/examples/data/example_10/elev_ens_0.25deg_reg_v25.0e.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/elev_ens_0.25deg_reg_v25.0e.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_10/fg_ens_mean_0.25deg_reg_2018_v25.0e.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/fg_ens_mean_0.25deg_reg_2018_v25.0e.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_10/hu_ens_mean_0.25deg_reg_2018_v25.0e.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/hu_ens_mean_0.25deg_reg_2018_v25.0e.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_10/qq_ens_mean_0.25deg_reg_2018_v25.0e.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/qq_ens_mean_0.25deg_reg_2018_v25.0e.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_10/spartacus-daily_19610101T0000_20211231T0000.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/spartacus-daily_19610101T0000_20211231T0000.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_10/tasAdjust_AUT_AT.ST_area_annual.csv:
--------------------------------------------------------------------------------
1 | Variable: ,Mean Air Temperature
2 | Variable id: ,tasAdjust
3 | Country: ,AUT
4 |
5 | ,year,CAT current policies warming level,CAT current policies median,CAT current policies 97.5th percentile,CAT current policies 2.5th percentile,NGFS current policies warming level,NGFS current policies median,NGFS current policies 97.5th percentile,NGFS current policies 2.5th percentile,NGFS delayed transition warming level,NGFS delayed transition median,NGFS delayed transition 97.5th percentile,NGFS delayed transition 2.5th percentile,NGFS net-zero 2050 warming level,NGFS net-zero 2050 median,NGFS net-zero 2050 97.5th percentile,NGFS net-zero 2050 2.5th percentile,RCP2.6 warming level,RCP2.6 median,RCP2.6 97.5th percentile,RCP2.6 2.5th percentile,RCP4.5 warming level,RCP4.5 median,RCP4.5 97.5th percentile,RCP4.5 2.5th percentile,RCP6.0 warming level,RCP6.0 median,RCP6.0 97.5th percentile,RCP6.0 2.5th percentile,RCP8.5 warming level,RCP8.5 median,RCP8.5 97.5th percentile,RCP8.5 2.5th percentile
6 | 0,2015,1.1,0.8890512177713674,1.408510881206336,0.44526810821859997,1.1,0.8890512177713674,1.5774477687874622,0.3670427360463562,1.1,0.8890512177713674,1.5774477687874622,0.3670427360463562,1.1,0.8890512177713674,1.5774477687874622,0.3670427360463562,1.1,0.8890512177713674,1.5774477687874622,0.3670427360463562,1.1,0.8890512177713674,1.5774477687874622,0.3670427360463562,1.1,0.8890512177713674,1.5774477687874622,0.3670427360463562,1.1,0.8890512177713674,1.5774477687874622,0.3670427360463562
7 | 1,2020,1.2,1.0770024714642892,1.7197989314796265,0.5274414266381425,1.2,1.0770024714642892,1.7197989314796265,0.44526810821859997,1.2,1.0770024714642892,1.7197989314796265,0.44526810821859997,1.2,1.0770024714642892,1.7197989314796265,0.44526810821859997,1.2,1.0770024714642892,1.7197989314796265,0.44526810821859997,1.2,1.0770024714642892,1.7197989314796265,0.44526810821859997,1.2,1.0770024714642892,1.7197989314796265,0.44526810821859997,1.2,1.0770024714642892,1.7197989314796265,0.44526810821859997
8 | 2,2025,1.3,1.238537242914393,2.0094256848507075,0.5274414266381425,1.4,1.3734862894755349,2.0094256848507075,0.5274414266381425,1.4,1.3734862894755349,2.0094256848507075,0.5274414266381425,1.4,1.3734862894755349,2.1705821556317697,0.5274414266381425,1.3,1.238537242914393,2.0094256848507075,0.5274414266381425,1.3,1.238537242914393,2.0094256848507075,0.5274414266381425,1.3,1.238537242914393,1.8462183039589952,0.5274414266381425,1.3,1.238537242914393,2.1705821556317697,0.5274414266381425
9 | 3,2030,1.4,1.3734862894755349,2.2969903942819005,0.6827719607509698,1.5,1.4925035458238813,2.2969903942819005,0.6827719607509698,1.5,1.4925035458238813,2.2969903942819005,0.6827719607509698,1.4,1.3734862894755349,2.2969903942819005,0.5274414266381425,1.4,1.3734862894755349,2.1705821556317697,0.5274414266381425,1.4,1.3734862894755349,2.2969903942819005,0.6827719607509698,1.4,1.3734862894755349,2.1705821556317697,0.5274414266381425,1.5,1.4925035458238813,2.403906018220526,0.6827719607509698
10 | 4,2035,1.6,1.6483088105845713,2.5275519166474134,0.8116860126209792,1.6,1.6483088105845713,2.5275519166474134,0.8116860126209792,1.6,1.6483088105845713,2.5275519166474134,0.8116860126209792,1.5,1.4925035458238813,2.403906018220526,0.6827719607509698,1.5,1.4925035458238813,2.403906018220526,0.6827719607509698,1.5,1.4925035458238813,2.403906018220526,0.6827719607509698,1.5,1.4925035458238813,2.403906018220526,0.6827719607509698,1.7,1.8020631652346106,2.800213502206507,0.8116860126209792
11 | 5,2040,1.7,1.8020631652346106,2.800213502206507,0.9140143396020267,1.7,1.8020631652346106,2.800213502206507,0.8116860126209792,1.7,1.8020631652346106,2.800213502206507,0.8116860126209792,1.5,1.4925035458238813,2.5275519166474134,0.6827719607509698,1.6,1.6483088105845713,2.5275519166474134,0.6827719607509698,1.7,1.8020631652346106,2.668109850032883,0.8116860126209792,1.6,1.6483088105845713,2.5275519166474134,0.8116860126209792,1.9,2.0205827955613227,3.164776039760339,1.0004108763702784
12 | 6,2045,1.8,1.9210692877537194,3.0434355712761003,0.9140143396020267,1.9,2.0205827955613227,3.0434355712761003,0.9140143396020267,1.7,1.8020631652346106,2.9105192809731855,0.8116860126209792,1.5,1.4925035458238813,2.5275519166474134,0.5274414266381425,1.6,1.6483088105845713,2.668109850032883,0.6827719607509698,1.8,1.9210692877537194,2.9105192809731855,0.9140143396020267,1.7,1.8020631652346106,2.800213502206507,0.8116860126209792,2.1,2.269982395111635,3.4579421805407744,1.1235954215508743
13 | 7,2050,1.9,2.0205827955613227,3.356044753044298,1.0004108763702784,2.0,2.1368265778571875,3.356044753044298,1.0004108763702784,1.7,1.8020631652346106,2.9105192809731855,0.8116860126209792,1.5,1.4925035458238813,2.668109850032883,0.5274414266381425,1.6,1.6483088105845713,2.800213502206507,0.6827719607509698,1.9,2.0205827955613227,3.164776039760339,1.0004108763702784,1.8,1.9210692877537194,3.0434355712761003,0.9140143396020267,2.3,2.4975875937898926,3.9871378830129465,1.3311144595598332
14 | 8,2055,2.0,2.1368265778571875,3.623708014775762,1.1235954215508743,2.1,2.269982395111635,3.623708014775762,1.1235954215508743,1.7,1.8020631652346106,2.9105192809731855,0.6827719607509698,1.5,1.4925035458238813,2.668109850032883,0.5274414266381425,1.7,1.8020631652346106,2.800213502206507,0.6827719607509698,2.0,2.1368265778571875,3.356044753044298,1.0004108763702784,1.9,2.0205827955613227,3.2715258447966584,1.0004108763702784,2.6,2.836387809220298,4.569576295118593,1.3980072477873422
15 | 9,2060,2.2,2.3946839311542365,3.9871378830129465,1.1235954215508743,2.3,2.4975875937898926,3.9871378830129465,1.2447290566208191,1.7,1.8020631652346106,2.9105192809731855,0.6827719607509698,1.5,1.4925035458238813,2.668109850032883,0.5274414266381425,1.7,1.8020631652346106,2.9105192809731855,0.6827719607509698,2.1,2.269982395111635,3.623708014775762,1.1235954215508743,2.1,2.269982395111635,3.356044753044298,1.1235954215508743,2.8,3.007999912702369,5.415865439189144,1.5821654081774656
16 | 10,2065,2.3,2.4975875937898926,4.1457566903311,1.2447290566208191,2.4,2.623101767961785,4.295693097201068,1.3311144595598332,1.6,1.6483088105845713,2.9105192809731855,0.6827719607509698,1.4,1.3734862894755349,2.5275519166474134,0.44526810821859997,1.7,1.8020631652346106,2.9105192809731855,0.6827719607509698,2.2,2.3946839311542365,3.801640532927255,1.1235954215508743,2.2,2.3946839311542365,3.801640532927255,1.2447290566208191,3.1,3.5149892667814746,5.69739262616833,1.6742462246399727
17 | 11,2070,2.3,2.4975875937898926,4.444873728820421,1.2447290566208191,2.5,2.737040120315001,4.569576295118593,1.3980072477873422,1.6,1.6483088105845713,2.9105192809731855,0.6827719607509698,1.4,1.3734862894755349,2.5275519166474134,0.44526810821859997,1.7,1.8020631652346106,2.9105192809731855,0.6827719607509698,2.3,2.4975875937898926,3.9871378830129465,1.2447290566208191,2.4,2.623101767961785,4.1457566903311,1.3311144595598332,3.3,3.808740248707551,6.539402341429493,1.8374226222873322
18 | 12,2075,2.4,2.623101767961785,4.821049266759197,1.3311144595598332,2.6,2.836387809220298,5.029302266482884,1.3980072477873422,1.6,1.6483088105845713,2.9105192809731855,0.6827719607509698,1.4,1.3734862894755349,2.5275519166474134,0.44526810821859997,1.7,1.8020631652346106,2.9105192809731855,0.6827719607509698,2.3,2.4975875937898926,4.295693097201068,1.2447290566208191,2.5,2.737040120315001,4.569576295118593,1.3980072477873422,3.6,4.3118900698726135,6.90394112403116,1.9854672243856566
19 | 13,2080,2.5,2.737040120315001,5.029302266482884,1.3311144595598332,2.7,2.9135046013369155,5.415865439189144,1.4816303105031128,1.6,1.6483088105845713,2.9105192809731855,0.6827719607509698,1.4,1.3734862894755349,2.5275519166474134,0.44526810821859997,1.7,1.8020631652346106,2.9105192809731855,0.6827719607509698,2.4,2.623101767961785,4.444873728820421,1.2447290566208191,2.7,2.9135046013369155,5.294610462278789,1.4816303105031128,3.8,4.77064703313016,6.90394112403116,2.0299632969221793
20 | 14,2085,2.6,2.836387809220298,5.415865439189144,1.3311144595598332,2.8,3.007999912702369,5.617082762834901,1.5821654081774656,1.6,1.6483088105845713,2.9105192809731855,0.6827719607509698,1.4,1.3734862894755349,2.5275519166474134,0.44526810821859997,1.7,1.8020631652346106,3.0434355712761003,0.5274414266381425,2.4,2.623101767961785,4.569576295118593,1.2447290566208191,2.9,3.166363630806335,5.557066700409422,1.5821654081774656,4.1,5.070912985293205,6.90394112403116,2.2175808872314104
21 | 15,2090,2.7,2.9135046013369155,5.557066700409422,1.3980072477873422,2.9,3.166363630806335,5.69739262616833,1.6742462246399727,1.6,1.6483088105845713,2.9105192809731855,0.5274414266381425,1.4,1.3734862894755349,2.5275519166474134,0.44526810821859997,1.7,1.8020631652346106,3.0434355712761003,0.5274414266381425,2.5,2.737040120315001,4.821049266759197,1.2447290566208191,3.0,3.3368940328268053,5.666330063143531,1.5821654081774656,4.4,5.1704014736919435,6.90394112403116,2.5009650840463604
22 | 16,2095,2.7,2.9135046013369155,5.666330063143531,1.3980072477873422,3.1,3.5149892667814746,5.738777599626708,1.6742462246399727,1.6,1.6483088105845713,2.9105192809731855,0.5274414266381425,1.4,1.3734862894755349,2.5275519166474134,0.44526810821859997,1.7,1.8020631652346106,3.0434355712761003,0.5274414266381425,2.5,2.737040120315001,5.029302266482884,1.2447290566208191,3.1,3.5149892667814746,5.738777599626708,1.6742462246399727,4.6,5.653391200063614,6.90394112403116,2.619561055653396
23 | 17,2100,2.8,3.007999912702369,5.69739262616833,1.3980072477873422,3.2,3.666205957968605,6.236571558260422,1.7445291676955341,1.6,1.6483088105845713,2.9105192809731855,0.5274414266381425,1.3,1.238537242914393,2.5275519166474134,0.44526810821859997,1.7,1.8020631652346106,3.0434355712761003,0.5274414266381425,2.5,2.737040120315001,5.294610462278789,1.2447290566208191,3.2,3.666205957968605,6.236571558260422,1.6742462246399727,4.9,6.2009092441744444,6.90394112403116,2.8386324227204844
24 |
--------------------------------------------------------------------------------
/docs/examples/data/example_10/tg_ens_mean_0.25deg_reg_2018_v25.0e.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/tg_ens_mean_0.25deg_reg_2018_v25.0e.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_10/tn_ens_mean_0.25deg_reg_2018_v25.0e.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/tn_ens_mean_0.25deg_reg_2018_v25.0e.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_10/tx_ens_mean_0.25deg_reg_2018_v25.0e.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_10/tx_ens_mean_0.25deg_reg_2018_v25.0e.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_2/incal_hourly_20120501T0000_20120930T2300.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_2/incal_hourly_20120501T0000_20120930T2300.nc
--------------------------------------------------------------------------------
/docs/examples/data/example_9/tas_day_EC-Earth3_ssp119_r4i1p1f1_gr_21000601-21000630_v20200425.nc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/docs/examples/data/example_9/tas_day_EC-Earth3_ssp119_r4i1p1f1_gr_21000601-21000630_v20200425.nc
--------------------------------------------------------------------------------
/docs/examples/index.rst:
--------------------------------------------------------------------------------
1 | Example Gallery
2 | ===============
3 | Below you can find examples of how *pyet* models are used for estimating (potential)
4 | evaporation.
5 |
6 | .. toctree::
7 | :hidden:
8 | :maxdepth: 1
9 |
10 | 01_example_zamg
11 | 02_example_zamg_netcdf
12 | 03_example_knmi
13 | 04_example_coagmet
14 | 05_example_calibration
15 | 06_worked_examples_McMahon_etal_2013
16 | 07_example_climate_change
17 | 08_crop_coefficient
18 | 09_CMIP6_data
19 | 10_example_paper
20 |
21 | `Estimating PET using pandas.Series`_
22 |
23 | `Estimating PET using xarray.DataArray`_
24 |
25 | `Benchmarking Makkink`_
26 |
27 | `Benchmarking FAO56`_
28 |
29 | `Calibration`_
30 |
31 | `Examples from McMahon et al., 2013`_
32 |
33 | `PET under climate change`_
34 |
35 | `Crop coefficient`_
36 |
37 | `Estimating PET using CMIP data`_
38 |
39 | `Notebook supporting PyEt GMD manuscript`_
40 |
41 | .. _Estimating PET using pandas.Series: 01_example_zamg.html
42 | .. _Estimating PET using xarray.DataArray: 02_example_zamg_netcdf.html
43 | .. _Benchmarking Makkink: 03_example_knmi.html
44 | .. _Benchmarking FAO56: 04_example_coagmet.html
45 | .. _Calibration: 05_example_calibration.html
46 | .. _Examples from McMahon et al., 2013: 06_worked_examples_McMahon_etal_2013.html
47 | .. _PET under climate change: 07_example_climate_change.html
48 | .. _Crop coefficient: 08_crop_coefficient.html
49 | .. _Estimating PET using CMIP data: 09_CMIP6_data.html
50 | .. _Notebook supporting PyEt GMD manuscript: 10_example_paper.html
51 |
52 | .. tip::
53 | The latest versions of the Jupyter Notebooks can be found in the
54 | examples folder on GitHub!
55 |
--------------------------------------------------------------------------------
/docs/examples/utils.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import pandas as pd
3 |
4 | from spotpy.objectivefunctions import kge, rsquared, bias
5 |
6 | cm1 = 1 / 2.54 # centimeters in inches
7 | figw_1c = 8.5 * cm1 # maximum width for 1 column
8 | figw_2c = 17.5 * cm1 # maximum width for 2 columns
9 |
10 |
11 | def scatter_1(ax, x, y, label="treatment", xlabel="obs", ylabel="sim",
12 | best_fit=True, veg_ws=None):
13 | compare = pd.DataFrame({"x": x, "y": y})
14 | if veg_ws is not None:
15 | compare[veg_ws == 0] = np.nan
16 | compare = compare.dropna()
17 | ax.plot(compare["x"], compare["y"], marker="o",
18 | linestyle="None", markersize=2, color="k", fillstyle="none")
19 | ax.plot([-0.1, 10], [-0.1, 10], color="dodgerblue", alpha=0.7,
20 | linewidth="0.8")
21 | ax.axes.set_xticks(np.arange(0, 10 + 2, 2))
22 | ax.axes.set_yticks(np.arange(0, 10 + 2, 2))
23 | ax.set_xlim(-0.1, 10)
24 | ax.set_ylim(-0.1, 10)
25 | if best_fit:
26 | p = np.polyfit(compare["x"], compare["y"], 1)
27 | f = np.poly1d(p)
28 |
29 | # Calculating new x's and y's
30 | x_new = np.linspace(0, 10, y.size)
31 | y_new = f(x_new)
32 |
33 | # Plotting the best fit line with the equation as a legend in latex
34 | ax.plot(x_new, y_new, "r--", linewidth="0.8")
35 | ax.text(0.02, 0.9, f"{label}", color="k", zorder=10,
36 | transform=ax.transAxes)
37 | ax.text(0.6, 0.04, "$Bias$ = " + str(
38 | round(bias(np.asarray(compare["y"]), np.asarray(compare["x"])), 2)) +
39 | "\n" + "$R^2$ = " + str(
40 | round(rsquared(np.asarray(compare["y"]), np.asarray(compare["x"])),
41 | 2)) +
42 | "\n" + "KGE = " + str(
43 | round(kge(np.asarray(compare["y"]), np.asarray(compare["x"])), 2)),
44 | color="k", zorder=10, transform=ax.transAxes)
45 | return ax
46 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | *pyet* - Estimation of Potential Evapotranspiration
2 | ===================================================
3 |
4 | *pyet* is an open source Python package for the estimation of reference and potential evapotranspiration (PET) from
5 | time series data (`Pandas `_) and gridded data (`Xarray `_). This
6 | allows users to estimate potential evapotranspiration and calibrate the models with just a few lines of python code.
7 |
8 | .. grid::
9 |
10 | .. grid-item-card:: Getting started
11 | :link: user_guide/index
12 | :link-type: doc
13 |
14 | User guide on the basic concepts of Pastas.
15 |
16 | .. grid-item-card:: Examples
17 | :link: examples/index
18 | :link-type: doc
19 |
20 | Examples of *pyet* usage.
21 |
22 | .. grid-item-card:: Code Reference
23 | :link: api/index
24 | :link-type: doc
25 |
26 | *pyet* code reference.
27 |
28 | .. grid::
29 |
30 | .. grid-item-card:: Publications
31 | :link: publications
32 | :link-type: doc
33 |
34 | Overview of publications that use *pyet*
35 |
36 | .. grid-item-card:: References
37 | :link: references
38 | :link-type: doc
39 |
40 | References used in the package.
41 |
42 | Currently, 18 methods are implemented for calculating daily PET
43 | -----------------------------
44 |
45 | .. list-table:: PET Calculation Methods
46 | :widths: 15 15 5 5 5 5 5 5 10
47 | :header-rows: 1
48 |
49 | * - Method name
50 | - pyet function
51 | - T
52 | - RH
53 | - R
54 | - u2
55 | - Lat.
56 | - El.
57 | - Benchmarked?
58 | * - Penman
59 | - penman
60 | - ✓ [1]
61 | - ✓ [2]
62 | - ✓ [3]
63 | - ✓
64 | - ✓ [3]
65 | - ✓ [4]
66 | - ✓
67 | * - Penman-Monteith
68 | - pm
69 | - ✓ [1]
70 | - ✓ [2]
71 | - ✓ [3]
72 | - ✓
73 | - ✓ [3]
74 | - ✓ [4]
75 | - ✓
76 | * - ASCE-PM
77 | - pm_asce
78 | - ✓ [1]
79 | - ✓ [2]
80 | - ✓ [3]
81 | - ✓
82 | - ✓ [3]
83 | - ✓ [4]
84 | - ✓
85 | * - FAO-56
86 | - pm_fao56
87 | - ✓ [1]
88 | - ✓ [2]
89 | - ✓ [3]
90 | - ✓
91 | - ✓ [3]
92 | - ✓ [4]
93 | - ✓
94 | * - Priestley-Taylor
95 | - priestley_taylor
96 | - ✓
97 | - ✓ [5]
98 | - ✓ [5]
99 | - -
100 | - ✓ [5]
101 | - ✓ [4]
102 | - ✓
103 | * - Kimberly-Penman
104 | - kimberly_penman
105 | - ✓ [1]
106 | - ✓ [2]
107 | - ✓ [3]
108 | - ✓
109 | - ✓ [3]
110 | - ✓ [4]
111 | - -
112 | * - Thom-Oliver
113 | - thom_oliver
114 | - ✓ [1]
115 | - ✓ [2]
116 | - ✓ [3]
117 | - ✓
118 | - ✓ [3]
119 | - ✓ [4]
120 | - -
121 | * - Blaney-Criddle
122 | - blaney_criddle
123 | - ✓
124 | - - [6]
125 | - - [6]
126 | - - [6]
127 | - ✓
128 | - -
129 | - ✓
130 | * - Hamon
131 | - hamon
132 | - ✓
133 | - -
134 | - -
135 | - -
136 | - ✓
137 | - -
138 | - ✓
139 | * - Romanenko
140 | - romanenko
141 | - ✓
142 | - ✓
143 | - -
144 | - -
145 | - -
146 | - -
147 | - ✓
148 | * - Linacre
149 | - linacre
150 | - ✓ [7]
151 | - -
152 | - -
153 | - -
154 | - -
155 | - ✓
156 | - ✓
157 | * - Haude
158 | - haude
159 | - ✓
160 | - ✓ [8]
161 | - -
162 | - -
163 | - -
164 | - -
165 | - ✓
166 | * - Turc
167 | - turc
168 | - ✓
169 | - ✓
170 | - ✓
171 | - -
172 | - -
173 | - -
174 | - ✓
175 | * - Jensen-Haise
176 | - jensen_haise
177 | - ✓
178 | - -
179 | - ✓ [9]
180 | - -
181 | - ✓ [9]
182 | - -
183 | - ✓
184 | * - McGuinness-Bordne
185 | - mcguinness_bordne
186 | - ✓
187 | - -
188 | - -
189 | - -
190 | - ✓
191 | - -
192 | - ✓
193 | * - Hargreaves
194 | - hargreaves
195 | - ✓ [10]
196 | - -
197 | - -
198 | - -
199 | - ✓
200 | - -
201 | - ✓
202 | * - FAO-24 radiation
203 | - fao_24
204 | - ✓
205 | - ✓
206 | - ✓
207 | - ✓
208 | - -
209 | - ✓ [4]
210 | - -
211 | * - Abtew
212 | - abtew
213 | - ✓
214 | - -
215 | - ✓
216 | - -
217 | - -
218 | - -
219 | - ✓
220 | * - Makkink
221 | - makkink
222 | - ✓
223 | - -
224 | - ✓
225 | - -
226 | - -
227 | - ✓ [4]
228 | - ✓
229 | * - Oudin
230 | - oudin
231 | - ✓
232 | - -
233 | - -
234 | - -
235 | - ✓
236 | - -
237 | - -
238 |
239 | .. rubric:: Footnotes
240 |
241 | .. [1] T_max and T_min can also be provided.
242 | .. [2] RH_max and RH_min can also be provided. If actual vapor pressure is provided, RH is not needed.
243 | .. [3] Input for radiation can be (1) Net radiation, (2) solar radiation, or (3) sunshine hours. If (1), then latitude is not needed. If (1, 3) then latitude and elevation are needed.
244 | .. [4] One must provide either the atmospheric pressure or elevation.
245 | .. [5] If net radiation is provided, RH and Lat are not needed.
246 | .. [6] If method==2, u2, RH_min, and sunshine hours are required.
247 | .. [7] Additional input of Tmax and Tmin, or Tdew.
248 | .. [8] Input can be RH or actual vapor pressure.
249 | .. [9] If method==1, latitude is needed instead of Rs.
250 | .. [10] Tmax and Tmin also needed.
251 |
252 |
253 | Using *pyet*? Show your support by citing us!
254 | -----------------------------
255 |
256 | If you find *pyet* useful and use it in your research or project, we kindly ask you to cite
257 | the *pyet* preprint published in Hydrology and Earth System Sciences (HESS) as follows:
258 |
259 | - Vremec, M., Collenteur, R. A., and Birk, S.: Technical note: Improved handling of potential
260 | evapotranspiration in hydrological studies with PyEt, Hydrol. Earth Syst. Sci. Discuss.
261 | [preprint], https://doi.org/10.5194/hess-2022-417, 2023.
--------------------------------------------------------------------------------
/docs/make.bat:
--------------------------------------------------------------------------------
1 | @ECHO OFF
2 |
3 | pushd %~dp0
4 |
5 | REM Command file for Sphinx documentation
6 |
7 | if "%SPHINXBUILD%" == "" (
8 | set SPHINXBUILD=sphinx-build
9 | )
10 | set SOURCEDIR=.
11 | set BUILDDIR=_build
12 |
13 | if "%1" == "" goto help
14 |
15 | %SPHINXBUILD% >NUL 2>NUL
16 | if errorlevel 9009 (
17 | echo.
18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
19 | echo.installed, then set the SPHINXBUILD environment variable to point
20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you
21 | echo.may add the Sphinx directory to PATH.
22 | echo.
23 | echo.If you don't have Sphinx installed, grab it from
24 | echo.http://sphinx-doc.org/
25 | exit /b 1
26 | )
27 |
28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
29 | goto end
30 |
31 | :help
32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
33 |
34 | :end
35 | popd
36 |
--------------------------------------------------------------------------------
/docs/publications.rst:
--------------------------------------------------------------------------------
1 | Publications
2 | ============
3 |
4 | This page provides an overview of the publications that use *pyet*. The list is generated from the public Zotero
5 | library with the references. If you have used *pyet* in your work, please add the reference to the
6 | `Zotero library `_ (collection `Publications`) and it will show up here!
7 |
8 |
9 | Peer-reviewed publications
10 | --------------------------
11 |
12 | .. bibliography:: publications.bib
13 | :all:
14 |
--------------------------------------------------------------------------------
/docs/references.rst:
--------------------------------------------------------------------------------
1 | References
2 | ==========
3 |
4 | *pyet* is built on a lot of scientific literature on the estimation of potential evapotranspiration. Here the
5 | references are listed for all the methods implemented in *pyet*, and the references used to benchmark the methods. This
6 | list is automatically generated from a public
7 | `Zotero library `_ (collection `References`). For a list of
8 | publications using *pyet* we refer to the `Publications` page of this website.
9 |
10 |
11 | .. bibliography:: references.bib
12 | :all:
13 |
14 |
--------------------------------------------------------------------------------
/docs/user_guide/index.rst:
--------------------------------------------------------------------------------
1 | User Guide
2 | ==========
3 | Here you can find guidance on how to get started with pyet, from installing the package on your computer to selecting
4 | a method and estimating potential evapotranspiration. A special section is provided on notation and units, to guide
5 | users to use the correct input data for the different methods.
6 |
7 | GitHub Discussions
8 | ------------------
9 | If you have any questions on how to use *pyet* not answered in the documentation, please ask your question on the
10 | `GitHub Discussions forum `_.
11 |
12 | .. toctree::
13 | :maxdepth: 1
14 |
15 | ./installation
16 | ./methods
17 | ./units
--------------------------------------------------------------------------------
/docs/user_guide/installation.rst:
--------------------------------------------------------------------------------
1 | Installing and Updating pyet
2 | ===============================
3 |
4 | Installing Python
5 | -----------------
6 | To install *pyet* a working version of Python 3.7 or higher has to be
7 | installed. We recommend using the `Anaconda Distribution
8 | `_ of Python. This Python distribution
9 | includes most of the python package dependencies and the Jupyter Lab
10 | software to run the notebooks. Moreover, it includes the Graphical User
11 | Interface (GUI) Spyder to start scripting in Python. However, you are free
12 | to install any Python distribution you want.
13 |
14 | Installing the *pyet* package
15 | ------------------------------
16 | The latest stable version of the *pyet* package is available from the Pypi
17 | package index.
18 |
19 | >>> pip install pyet
20 |
21 | To install in developer mode, clone the GitHub repository and use the
22 | following syntax:
23 |
24 | >>> pip install -e .
25 |
26 | Updating the *pyet* package
27 | ----------------------------
28 | If you have already installed pyet, it is possible to update pyet
29 | easily. To update, open a Windows command screen or a Mac terminal and type:
30 |
31 | >>> pip install *pyet* --upgrade
32 |
33 | Dependencies
34 | ------------
35 | pyet depends on a number of Python packages, which are all automatically
36 | installed when using the pip install manager. The following packages are
37 | necessary for the installation of pyet:
38 |
39 | .. include:: ../../requirements.txt
40 | :literal:
--------------------------------------------------------------------------------
/docs/user_guide/methods.rst:
--------------------------------------------------------------------------------
1 | Choosing a method
2 | =================
3 |
4 | This package contains a large number of methods to compute potential evapotranspiration. Each method is separately
5 | documented in the `API-Docs` section of this documentation website. The methods have been subdivided into three broad
6 | classes, depending on the input data that is required for the computation.
7 |
8 |
--------------------------------------------------------------------------------
/docs/user_guide/units.rst:
--------------------------------------------------------------------------------
1 | Notation and units
2 | ------------------
3 |
4 | Many issues and errors in the evapotranspiration estimation come from the wrong units of the input data. Throughout
5 | PyET we have tried to be consistent in the notation of the variables and their units. Table 1 provides an
6 | overview of the different variables, their units, and python function argument name. When providing arguments to any
7 | the evapotranspiration methods, it is important to make sure the units of each variable are as listed in Table 1.
8 |
9 | .. list-table::
10 | :widths: 25 50 25
11 | :header-rows: 1
12 |
13 | * - .. math:: Variable
14 | - .. math:: Description
15 | - .. math:: Units
16 | * - .. math:: PE
17 | - Potential evaporation
18 | - .. math:: mm d^{-1}
19 | * - .. math:: \Delta
20 | - Slope of vapor pressure curve
21 | - .. math:: kPa °C^{-1}
22 | * - .. math:: \gamma
23 | - Latent heat of vaporization
24 | - .. math:: MJ kg^{-1}
25 | * - .. math:: \rho_w
26 | - Water density (=1000)
27 | - .. math:: kg L^{-1}
28 | * - .. math:: \rho_a
29 | - Air density
30 | - .. math:: kg m^{-3}
31 | * - .. math:: \gamma
32 | - Psychrometric constant
33 | - .. math:: kPa °C^{-1}
34 | * - .. math:: e_s
35 | - Saturation vapour pressure
36 | - .. math:: kPa
37 | * - .. math:: e_a
38 | - Actual vapour pressure
39 | - .. math:: kPa
40 | * - .. math:: r_a
41 | - Aerodynamic resistance
42 | - .. math:: s m^{-1}
43 | * - .. math:: r_s
44 | - Surface resistance of reference crop (=70)
45 | - .. math:: s m^{-1}
46 | * - .. math:: u_2
47 | - Wind speed 2 m above soil surface
48 | - .. math:: m s^{-1}
49 | * - .. math:: T_a
50 | - Air temperature
51 | - .. math:: °C
52 | * - .. math:: T_d
53 | - Dew point temperature
54 | - .. math:: °C
55 | * - .. math:: T_{max}
56 | - Maximum air temperature
57 | - .. math:: °C
58 | * - .. math:: T_{min}
59 | - Minimum air temperature
60 | - .. math:: °C
61 | * - .. math:: R_a
62 | - Extraterrestrial radiation
63 | - .. math:: MJ m^{-2} d^{-1}
64 | * - .. math:: R_s
65 | - Global short-wave radiation
66 | - .. math:: MJ m^{-2} d^{-1}
67 | * - .. math:: R_n
68 | - Net incoming solar radiation
69 | - .. math:: MJ m^{-2} d^{-1}
70 | * - .. math:: RH
71 | - Relative humidity
72 | - .. math:: \%
73 | * - .. math:: DL
74 | - Day length
75 | - .. math:: h d^{-1}
76 | * - .. math:: \alpha
77 | - Surface albedo
78 | - .. math:: \%
79 | * - .. math:: J_D
80 | - Julian day
81 | - ordinal date
82 | * - .. math:: c_p
83 | - Air specific heat capacity
84 | - .. math:: MJ kg^{-1} °C^{-1}
--------------------------------------------------------------------------------
/methods.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyet-org/pyet/ff411787fbaeaa903b9966e28df41db9afb05a28/methods.xlsx
--------------------------------------------------------------------------------
/pyet/__init__.py:
--------------------------------------------------------------------------------
1 | from .combination import (
2 | penman,
3 | pm_asce,
4 | pm,
5 | pm_fao56,
6 | priestley_taylor,
7 | kimberly_penman,
8 | thom_oliver,
9 | calculate_all,
10 | )
11 | from .temperature import blaney_criddle, haude, hamon, romanenko, linacre
12 | from .radiation import (
13 | turc,
14 | jensen_haise,
15 | mcguinness_bordne,
16 | hargreaves,
17 | fao_24,
18 | abtew,
19 | makkink,
20 | makkink_knmi,
21 | oudin,
22 | )
23 | from .meteo_utils import (
24 | calc_psy,
25 | calc_vpc,
26 | calc_lambda,
27 | calc_press,
28 | calc_rho,
29 | calc_e0,
30 | calc_es,
31 | calc_ea,
32 | extraterrestrial_r,
33 | calc_res_surf,
34 | calc_laieff,
35 | calc_res_aero,
36 | relative_distance,
37 | solar_declination,
38 | sunset_angle,
39 | day_of_year,
40 | daylight_hours,
41 | )
42 |
43 | from .rad_utils import (
44 | calc_rad_net,
45 | calc_rad_long,
46 | calc_rad_short,
47 | calc_rad_sol_in,
48 | calc_rso,
49 | )
50 | from .version import __version__
51 | from .utils import show_versions
52 |
--------------------------------------------------------------------------------
/pyet/meteo_utils.py:
--------------------------------------------------------------------------------
1 | """The meteo_utils module contains utility functions for meteorological data.
2 |
3 | """
4 |
5 | from numpy import cos, exp, isnan, log, pi, sin, tan
6 | from pandas import Series, to_numeric
7 | from xarray import DataArray
8 | from numpy import arccos, clip, nanmax, where
9 |
10 | # Specific heat of air [MJ kg-1 °C-1]
11 | CP = 1.013 * 10**-3
12 |
13 |
14 | def calc_psy(pressure, tmean=None):
15 | """Psychrometric constant [kPa °C-1].
16 |
17 | Parameters
18 | ----------
19 | pressure: array_like
20 | atmospheric pressure [kPa].
21 | tmean: array_like
22 | average day temperature [°C].
23 |
24 | Returns
25 | -------
26 | array_like containing the Psychrometric
27 | constant [kPa °C-1].
28 |
29 | Examples
30 | --------
31 | >>> psy = calc_psy(pressure, tmean)
32 |
33 | Notes
34 | -----
35 | if tmean is none:
36 | Based on equation 8 in :cite:t:`allen_crop_1998`.
37 | elif rh is None:
38 | From FAO (1990), ANNEX V, eq. 4.
39 |
40 | """
41 | if tmean is None:
42 | return 0.000665 * pressure
43 | else:
44 | lambd = calc_lambda(tmean) # MJ kg-1
45 | return CP * pressure / (0.622 * lambd)
46 |
47 |
48 | def calc_vpc(tmean):
49 | """Slope of saturation vapour pressure curve at air Temperature [kPa °C-1].
50 |
51 | Parameters
52 | ----------
53 | tmean: array_like
54 | average day temperature [°C].
55 |
56 | Returns
57 | -------
58 | array_like containing the calculated
59 | Saturation vapour pressure [kPa °C-1].
60 |
61 | Examples
62 | --------
63 | >>> vpc = calc_vpc(tmean)
64 |
65 | Notes
66 | -----
67 | Based on equation 13. in :cite:t:`allen_crop_1998`.
68 |
69 | """
70 | es = calc_e0(tmean)
71 | return 4098 * es / (tmean + 237.3) ** 2
72 |
73 |
74 | def calc_lambda(tmean):
75 | """Latent Heat of Vaporization [MJ kg-1].
76 |
77 | Parameters
78 | ----------
79 | tmean: array_like
80 | average day temperature [°C].
81 |
82 | Returns
83 | -------
84 | array_like containing the calculated Latent Heat
85 | of Vaporization [MJ kg-1].
86 |
87 | Examples
88 | --------
89 | >>> lambd = calc_lambda(tmean)
90 |
91 | Notes
92 | -----
93 | Based on equation (3-1) in :cite:t:`allen_crop_1998`.
94 |
95 | """
96 | return 2.501 - 0.002361 * tmean
97 |
98 |
99 | def calc_press(elevation, pressure=None):
100 | """Atmospheric pressure [kPa].
101 |
102 | Parameters
103 | ----------
104 | elevation: array_like
105 | the site elevation [m].
106 | pressure: array_like, optional
107 | atmospheric pressure [kPa].
108 |
109 | Returns
110 | -------
111 | array_like containing the calculated atmospheric pressure [kPa].
112 |
113 | Examples
114 | --------
115 | >>> pressure = calc_press(elevation)
116 |
117 | Notes
118 | -----
119 | Based on equation 7 in :cite:t:`allen_crop_1998`.
120 |
121 | """
122 | if pressure is None and elevation is None:
123 | raise Exception("Please provide either pressure or the elevation!")
124 | if pressure is None:
125 | return 101.3 * ((293 - 0.0065 * elevation) / 293) ** 5.26
126 | else:
127 | return pressure
128 |
129 |
130 | def calc_rho(pressure, tmean, ea):
131 | """Atmospheric air density calculated according to :cite:t:`allen_crop_1998`.
132 |
133 | Parameters
134 | ----------
135 | pressure: array_like
136 | atmospheric pressure [kPa].
137 | tmean: array_like
138 | average day temperature [°C].
139 | ea: array_like
140 | actual vapour pressure [kPa].
141 |
142 | Returns
143 | -------
144 | float or pandas.Series or xarray.DataArray containing the calculated mean air
145 | density [kg/m3]
146 |
147 | Examples
148 | --------
149 | >>> rho = calc_rho(pressure, tmean, ea)
150 |
151 | Notes
152 | -----
153 | Based on equation (3-5) in :cite:t:`allen_crop_1998`.
154 |
155 | .. math:: rho = 3.486 \\frac{P}{T_{KV}}
156 |
157 | """
158 | # Virtual temperature [tkv]
159 | tkv = (273.16 + tmean) * (1 - 0.378 * ea / pressure) ** -1
160 | return 3.486 * pressure / tkv
161 |
162 |
163 | def calc_e0(tmean):
164 | """Saturation vapor pressure at the air temperature T [kPa].
165 |
166 | Parameters
167 | ----------
168 | tmean: array_like
169 | average day temperature [°C].
170 |
171 | Returns
172 | -------
173 | array_like containing the calculated saturation vapor pressure at the air
174 | temperature tmean [kPa].
175 |
176 | Examples
177 | --------
178 | >>> e0 = calc_e0(tmean)
179 |
180 | Notes
181 | -----
182 | Based on equation 11 in :cite:t:`allen_crop_1998`.
183 |
184 | """
185 | return 0.6108 * exp(17.27 * tmean / (tmean + 237.3))
186 |
187 |
188 | def calc_es(tmean=None, tmax=None, tmin=None):
189 | """Saturation vapor pressure [kPa].
190 |
191 | Parameters
192 | ----------
193 | tmean: array_like, optional
194 | average day temperature [°C].
195 | tmax: array_like, optional
196 | maximum day temperature [°C].
197 | tmin: array_like, optional
198 | minimum day temperature [°C].
199 |
200 | Returns
201 | -------
202 | float or pandas.Series or xarray.DataArray containing the calculated saturation
203 | vapor pressure [kPa].
204 |
205 | Examples
206 | --------
207 | >>> es = calc_es(tmean)
208 |
209 | Notes
210 | -----
211 | Based on equation 11, 12 in :cite:t:`allen_crop_1998`.
212 |
213 | """
214 | if tmax is not None:
215 | eamax = calc_e0(tmax)
216 | eamin = calc_e0(tmin)
217 | return (eamax + eamin) / 2
218 | else:
219 | return calc_e0(tmean)
220 |
221 |
222 | def calc_ea(tmean=None, tmax=None, tmin=None, rhmax=None, rhmin=None, rh=None, ea=None):
223 | """Actual vapor pressure [kPa].
224 |
225 | Parameters
226 | ----------
227 | tmean: array_like, optional
228 | average day temperature [°C].
229 | tmax: array_like, optional
230 | maximum day temperature [°C].
231 | tmin: array_like, optional
232 | minimum day temperature [°C].
233 | rhmax: array_like, optional
234 | maximum daily relative humidity [%].
235 | rhmin: array_like, optional
236 | mainimum daily relative humidity [%].
237 | rh: array_like, optional
238 | mean daily relative humidity [%].
239 | ea: array_like, optional
240 | actual vapor pressure [kPa].
241 |
242 | Returns
243 | -------
244 | float or pandas.Series or xarray.DataArray containing the calculated actual vapor
245 | pressure [kPa].
246 |
247 | Examples
248 | --------
249 | >>> ea = calc_ea(tmean, rh)
250 |
251 | Notes
252 | -----
253 | Based on equation 17, 19 in :cite:t:`allen_crop_1998`.
254 |
255 | """
256 | if ea is not None:
257 | return ea
258 | if rhmax is not None: # eq. 11
259 | esmax = calc_e0(tmax)
260 | esmin = calc_e0(tmin)
261 | return (esmin * rhmax / 200) + (esmax * rhmin / 200)
262 | else: # eq. 14
263 | if tmax is not None:
264 | es = calc_es(tmax=tmax, tmin=tmin)
265 | else:
266 | es = calc_e0(tmean)
267 | return rh / 100 * es
268 |
269 |
270 | def day_of_year(tindex):
271 | """Day of the year (1-365) based on pandas.Index.
272 |
273 | Parameters
274 | ----------
275 | tindex: pandas.DatetimeIndex
276 |
277 | Returns
278 | -------
279 | array_like with ints specifying day of year.
280 |
281 | """
282 | return Series(to_numeric(tindex.strftime("%j")), tindex, dtype=int)
283 |
284 |
285 | def solar_declination(j):
286 | """Solar declination from day of year [rad].
287 |
288 | Parameters
289 | ----------
290 | j: array_like
291 | day of the year (1-365).
292 |
293 | Returns
294 | -------
295 | array_like of solar declination [rad].
296 |
297 | Notes
298 | -------
299 | Based on equations 24 in :cite:t:`allen_crop_1998`.
300 |
301 | """
302 | return 0.409 * sin(2.0 * pi / 365.0 * j - 1.39)
303 |
304 |
305 | def sunset_angle(sol_dec, lat):
306 | """Sunset hour angle from latitude and solar declination - daily [rad].
307 |
308 | Parameters
309 | ----------
310 | sol_dec: array_like
311 | solar declination [rad].
312 | lat: array_like
313 | the site latitude [rad].
314 |
315 | Returns
316 | -------
317 | array_like containing the calculated sunset hour angle - daily [rad].
318 |
319 | Notes
320 | -----
321 | Based on equations 25 in :cite:t:`allen_crop_1998`.
322 |
323 | """
324 | if isinstance(lat, DataArray):
325 | lat = lat.expand_dims(dim={"time": sol_dec.index}, axis=0)
326 | return arccos(clip(-tan(sol_dec.values) * tan(lat).T, -1, 1)).T
327 | else:
328 | return arccos(clip(-tan(sol_dec) * tan(lat), -1, 1))
329 |
330 |
331 | def daylight_hours(tindex, lat):
332 | """Daylight hours [hour].
333 |
334 | Parameters
335 | ----------
336 | tindex: pandas.DatetimeIndex
337 | lat: array_like
338 | the site latitude [rad].
339 |
340 | Returns
341 | -------
342 | pandas.Series or xarray.DataArray containing the calculated daylight hours [hour].
343 |
344 | Notes
345 | -----
346 | Based on equation 34 in :cite:t:`allen_crop_1998`.
347 |
348 | """
349 | j = day_of_year(tindex)
350 | sol_dec = solar_declination(j)
351 | sangle = sunset_angle(sol_dec, lat)
352 | # Account for subpolar belt which returns NaN values
353 | dl = 24 / pi * sangle
354 | if isinstance(lat, DataArray):
355 | sol_dec = ((dl / dl).T * sol_dec.values).T
356 | dl = where((sol_dec > 0) & (isnan(dl)), nanmax(dl), dl)
357 | dl = where((sol_dec < 0) & (isnan(dl)), 0, dl)
358 | return dl
359 |
360 |
361 | def relative_distance(j):
362 | """Inverse relative distance between earth and sun from day of the year.
363 |
364 | Parameters
365 | ----------
366 | j: array_like
367 | day of the year (1-365).
368 |
369 | Returns
370 | -------
371 | pandas.Series specifying relative distance between earth and sun.
372 |
373 | Notes
374 | -------
375 | Based on equations 23 in :cite:t:`allen_crop_1998`.
376 |
377 | """
378 | return 1 + 0.033 * cos(2.0 * pi / 365.0 * j)
379 |
380 |
381 | def extraterrestrial_r(tindex, lat):
382 | """
383 | Extraterrestrial daily radiation [MJ m-2 d-1].
384 |
385 | Parameters
386 | ----------
387 | tindex: pandas.DatetimeIndex
388 | lat: array_like
389 | the site latitude [rad].
390 |
391 | Returns
392 | -------
393 | array_like containing the calculated extraterrestrial radiation [MJ m-2 d-1]
394 |
395 | Notes
396 | -----
397 | Based on equation 21 in :cite:t:`allen_crop_1998`.
398 |
399 | """
400 | j = day_of_year(tindex)
401 | dr = relative_distance(j)
402 | sol_dec = solar_declination(j)
403 |
404 | omega = sunset_angle(sol_dec, lat)
405 | if isinstance(lat, DataArray):
406 | lat = lat.expand_dims(dim={"time": sol_dec.index}, axis=0)
407 | xx = sin(sol_dec.values) * sin(lat.T)
408 | yy = cos(sol_dec.values) * cos(lat.T)
409 | return (118.08 / 3.141592654 * dr.values * (omega.T * xx + yy * sin(omega.T))).T
410 | else:
411 | xx = sin(sol_dec) * sin(lat)
412 | yy = cos(sol_dec) * cos(lat)
413 | return 118.08 / 3.141592654 * dr * (omega * xx + yy * sin(omega))
414 |
415 |
416 | def calc_res_surf(
417 | lai=None, r_s=None, srs=0.002, co2=300, r_l=100, lai_eff=0, croph=0.12
418 | ):
419 | """Surface resistance [s m-1].
420 |
421 | Parameters
422 | ----------
423 | lai: float or pandas.Series or xarray.DataArray, optional
424 | leaf area index [-].
425 | r_s: float or pandas.Series or xarray.DataArray, optional
426 | surface resistance [s m-1].
427 | r_l: float or pandas.Series or xarray.DataArray, optional
428 | bulk stomatal resistance [s m-1].
429 | lai_eff: float, optional
430 | 1 => LAI_eff = 0.5 * LAI
431 | 2 => LAI_eff = lai / (0.3 * lai + 1.2)
432 | 3 => LAI_eff = 0.5 * LAI; (LAI>4=4)
433 | 4 => see :cite:t:`zhang_comparison_2008`.
434 | srs: float or pandas.Series or xarray.DataArray, optional
435 | Relative sensitivity of rl to Δ[CO2] :cite:t:`yang_hydrologic_2019`.
436 | co2: float or pandas.Series or xarray.DataArray
437 | CO2 concentration [ppm].
438 | croph: float or pandas.Series or xarray.DataArray, optional crop height [m].
439 |
440 | Returns
441 | -------
442 | float or pandas.Series or xarray.DataArray containing the calculated surface
443 | resistance [s / m]
444 |
445 | """
446 | if r_s:
447 | return r_s
448 | else:
449 | fco2 = 1 + srs * (co2 - 300)
450 | if lai is None:
451 | return fco2 * r_l / (0.5 * croph * 24) # after FAO-56
452 | else:
453 | return fco2 * r_l / calc_laieff(lai=lai, lai_eff=lai_eff)
454 |
455 |
456 | def calc_laieff(lai=None, lai_eff=0):
457 | """Effective leaf area index [-].
458 |
459 | Parameters
460 | ----------
461 | lai: pandas.Series/float, optional
462 | leaf area index [-].
463 | lai_eff: float, optional
464 | 0 => LAI_eff = 0.5 * LAI
465 | 1 => LAI_eff = lai / (0.3 * lai + 1.2)
466 | 2 => LAI_eff = 0.5 * LAI; (LAI>4=4)
467 | 3 => see :cite:t:`zhang_comparison_2008`.
468 |
469 | Returns
470 | -------
471 | pandas.Series containing the calculated effective leaf area index.
472 |
473 | """
474 | if lai_eff == 0:
475 | return 0.5 * lai
476 | if lai_eff == 1:
477 | return lai / (0.3 * lai + 1.2)
478 | if lai_eff == 2:
479 | laie = lai.copy()
480 | laie[(lai > 2) & (lai < 4)] = 2
481 | laie[lai > 4] = 0.5 * lai
482 | return laie
483 | if lai_eff == 3:
484 | laie = lai.copy()
485 | laie[lai > 4] = 4
486 | return laie * 0.5
487 |
488 |
489 | def calc_res_aero(wind, croph=0.12, zw=2, zh=2, ra_method=0):
490 | """Aerodynamic resistance [s m-1].
491 |
492 | Parameters
493 | ----------
494 | wind: float or pandas.Series or xarray.DataArray
495 | mean day wind speed [m/s].
496 | croph: float or pandas.Series or xarray.DataArray, optional
497 | crop height [m].
498 | zw: float, optional
499 | height of wind measurement [m].
500 | zh: float, optional
501 | height of humidity and or air temperature measurement [m].
502 | ra_method: float, optional
503 | 0 => ra = 208/wind
504 | 1 => ra is calculated based on equation 36 in FAO (1990), ANNEX V.
505 |
506 | Returns
507 | -------
508 | pandas.Series containing the calculated aerodynamic resistance.
509 |
510 | """
511 | if ra_method == 0:
512 | wind = wind.where(wind != 0, 0.0001)
513 | return 208 / wind
514 | else:
515 | d = 0.667 * croph
516 | zom = 0.123 * croph
517 | zoh = 0.0123 * croph
518 | return (log((zw - d) / zom)) * (log((zh - d) / zoh) / (0.41**2) / wind)
519 |
--------------------------------------------------------------------------------
/pyet/rad_utils.py:
--------------------------------------------------------------------------------
1 | """The rad_utils module contains utility functions for radiation data.
2 |
3 | """
4 |
5 | from numpy import sqrt, clip, newaxis
6 |
7 | from pandas import Series
8 |
9 | from xarray import DataArray
10 |
11 | from .meteo_utils import calc_ea, extraterrestrial_r, daylight_hours
12 |
13 | from .utils import get_index, check_rad, vectorize
14 |
15 | # Stefan Boltzmann constant - hourly [MJm-2K-4h-1]
16 | STEFAN_BOLTZMANN_HOUR = 2.042 * 10**-10
17 | # Stefan Boltzmann constant - daily [MJm-2K-4d-1]
18 | STEFAN_BOLTZMANN_DAY = 4.903 * 10**-9
19 |
20 |
21 | def calc_rad_net(
22 | tmean,
23 | rn=None,
24 | rs=None,
25 | lat=None,
26 | n=None,
27 | nn=None,
28 | tmax=None,
29 | tmin=None,
30 | rhmax=None,
31 | rhmin=None,
32 | rh=None,
33 | elevation=None,
34 | rso=None,
35 | a=1.35,
36 | b=-0.35,
37 | ea=None,
38 | albedo=0.23,
39 | as1=0.25,
40 | bs1=0.5,
41 | kab=None,
42 | ):
43 | """Net radiation [MJ m-2 d-1].
44 |
45 | Parameters
46 | ----------
47 | tmean: pandas.Series/xarray.DataArray
48 | average day temperature [°C].
49 | rn: float or pandas.Series or xarray.DataArray, optional
50 | net radiation [MJ m-2 d-1].
51 | rs: float or pandas.Series or xarray.DataArray, optional
52 | incoming solar radiation [MJ m-2 d-1].
53 | lat: float/xarray.DataArray, optional
54 | the site latitude [rad].
55 | n: float or pandas.Series or xarray.DataArray, optional
56 | actual duration of sunshine [hour].
57 | nn: float or pandas.Series or xarray.DataArray, optional
58 | maximum possible duration of sunshine or daylight hours [hour].
59 | tmax: float or pandas.Series or xarray.DataArray, optional
60 | maximum day temperature [°C].
61 | tmin: float or pandas.Series or xarray.DataArray, optional
62 | minimum day temperature [°C].
63 | rhmax: float or pandas.Series or xarray.DataArray, optional
64 | maximum daily relative humidity [%].
65 | rhmin: float or pandas.Series or xarray.DataArray, optional
66 | mainimum daily relative humidity [%].
67 | rh: float or pandas.Series or xarray.DataArray, optional
68 | mean daily relative humidity [%].
69 | elevation: float/xarray.DataArray, optional
70 | the site elevation [m].
71 | rso: float or pandas.Series or xarray.DataArray, optional
72 | clear-sky solar radiation [MJ m-2 day-1].
73 | a: float, optional
74 | empirical coefficient for Net Long-Wave radiation [-].
75 | b: float, optional
76 | empirical coefficient for Net Long-Wave radiation [-].
77 | ea: float or pandas.Series or xarray.DataArray, optional
78 | actual vapor pressure [kPa].
79 | albedo: float, optional
80 | surface albedo [-]
81 | as1: float, optional
82 | regression constant, expressing the fraction of extraterrestrial reaching the
83 | earth on overcast days (n = 0) [-]
84 | bs1: float, optional
85 | empirical coefficient for extraterrestrial radiation [-]
86 | kab: float, optional
87 | coefficient derived from as1, bs1 for estimating clear-sky radiation [degrees].
88 |
89 | Returns
90 | -------
91 | float or pandas.Series or xarray.DataArray, optional containing the calculated net
92 | shortwave radiation.
93 |
94 | Notes
95 | -----
96 | Based on equation 40 in :cite:t:`allen_crop_1998`.
97 |
98 | """
99 | if rn is not None:
100 | rn = check_rad(rn)
101 | return rn
102 | else:
103 | if rs is None:
104 | rs = calc_rad_sol_in(n, lat, as1=as1, bs1=bs1, nn=nn)
105 | rns = calc_rad_short(
106 | rs=rs, lat=lat, n=n, nn=nn, albedo=albedo, as1=as1, bs1=bs1
107 | ) # [MJ/m2/d]
108 | rnl = calc_rad_long(
109 | rs=rs,
110 | tmean=tmean,
111 | tmax=tmax,
112 | tmin=tmin,
113 | rhmax=rhmax,
114 | rhmin=rhmin,
115 | rh=rh,
116 | elevation=elevation,
117 | lat=lat,
118 | rso=rso,
119 | a=a,
120 | b=b,
121 | ea=ea,
122 | kab=kab,
123 | ) # [MJ/m2/d]
124 | rn = rns - rnl
125 | rn = check_rad(rn)
126 | return rn
127 |
128 |
129 | def calc_rad_long(
130 | rs,
131 | tmean=None,
132 | tmax=None,
133 | tmin=None,
134 | rhmax=None,
135 | rhmin=None,
136 | rh=None,
137 | elevation=None,
138 | lat=None,
139 | rso=None,
140 | a=1.35,
141 | b=-0.35,
142 | ea=None,
143 | kab=None,
144 | ):
145 | """Net longwave radiation [MJ m-2 d-1].
146 |
147 | Parameters
148 | ----------
149 | rs: float or pandas.Series or xarray.DataArray, optional
150 | incoming solar radiation [MJ m-2 d-1].
151 | tmean: float or pandas.Series or xarray.DataArray, optional
152 | average day temperature [°C].
153 | tmax: float or pandas.Series or xarray.DataArray, optional
154 | maximum day temperature [°C].
155 | tmin: float or pandas.Series or xarray.DataArray, optional
156 | minimum day temperature [°C].
157 | rhmax: float or pandas.Series or xarray.DataArray, optional
158 | maximum daily relative humidity [%].
159 | rhmin: float or pandas.Series or xarray.DataArray, optional
160 | mainimum daily relative humidity [%].
161 | rh: float or pandas.Series or xarray.DataArray, optional
162 | mean daily relative humidity [%].
163 | elevation: float/xarray.DataArray, optional
164 | the site elevation [m].
165 | lat: float/xarray.DataArray, optional
166 | the site latitude [rad].
167 | rso: float or pandas.Series or xarray.DataArray, optional
168 | clear-sky solar radiation [MJ m-2 day-1].
169 | a: float, optional
170 | empirical coefficient for Net Long-Wave radiation [-].
171 | b: float, optional
172 | empirical coefficient for Net Long-Wave radiation [-].
173 | ea: float or pandas.Series or xarray.DataArray, optional
174 | actual vapor pressure [kPa].
175 | kab: float, optional
176 | coefficient that can be derived from the as and bs coefficients of the
177 | Angstrom formula, where Kab = as + bs, and where Kab represents the
178 | fraction of extraterrestrial radiation reaching the earth on clear-sky
179 | days [-].
180 |
181 | Returns
182 | -------
183 | float or pandas.Series or xarray.DataArray, optional containing the calculated net
184 | longwave radiation.
185 |
186 | Notes
187 | -----
188 | Based on equation 39 in :cite:t:`allen_crop_1998`.
189 |
190 | """
191 | if ea is None:
192 | ea = calc_ea(tmean=tmean, tmax=tmax, tmin=tmin, rhmax=rhmax, rhmin=rhmin, rh=rh)
193 |
194 | if rso is None:
195 | tindex = get_index(rs)
196 | ra = extraterrestrial_r(tindex, lat)
197 | rso = calc_rso(ra=ra, elevation=elevation, kab=kab)
198 | # Add a small constant to rso where it is zero to avoid division with zero
199 | rso = rso.where(rso != 0, 0.001)
200 | if len(rs.shape) == 3 and len(rso.shape) == 1:
201 | rso = rso.values[:, newaxis, newaxis]
202 | solar_rat = clip(rs / rso, 0.3, 1)
203 | if tmax is not None:
204 | tmp1 = STEFAN_BOLTZMANN_DAY * ((tmax + 273.16) ** 4 + (tmin + 273.16) ** 4) / 2
205 | else:
206 | tmp1 = STEFAN_BOLTZMANN_DAY * (tmean + 273.16) ** 4
207 | tmp2 = 0.34 - 0.14 * sqrt(ea) # OK
208 | tmp3 = a * solar_rat + b # OK
209 | tmp3 = clip(tmp3, 0.05, 1)
210 | rnl = tmp1 * tmp2 * tmp3
211 | return rnl
212 |
213 |
214 | def calc_rad_short(rs=None, lat=None, albedo=0.23, n=None, nn=None, as1=0.25, bs1=0.5):
215 | """Net shortwave radiation [MJ m-2 d-1].
216 |
217 | Parameters
218 | ----------
219 | rs: float or pandas.Series or xarray.DataArray, optional
220 | incoming solar radiation [MJ m-2 d-1].
221 | lat: float, optional
222 | the site latitude [rad].
223 | albedo: float or pandas.Series or xarray.DataArray, optional
224 | surface albedo [-].
225 | n: pandas.Series/xarray.DataArray, optional
226 | actual duration of sunshine [hour].
227 | as1: float, optional
228 | regression constant, expressing the fraction of extraterrestrial reaching the
229 | earth on overcast days (n = 0) [-].
230 | bs1: float, optional
231 | empirical coefficient for extraterrestrial radiation [-].
232 | nn: float or pandas.Series or xarray.DataArray, optional
233 | maximum possible duration of sunshine or daylight hours [hour].
234 |
235 | Returns
236 | -------
237 | float or pandas.Series or xarray.DataArray, optional containing the calculated
238 | net shortwave radiation.
239 |
240 | Notes
241 | -----
242 | Based on equation 38 in :cite:t:`allen_crop_1998`.
243 |
244 | """
245 | (vrs,) = vectorize(rs)
246 | if vrs is not None:
247 | return (1 - albedo) * vrs
248 | else:
249 | return (1 - albedo) * calc_rad_sol_in(n, lat, as1=as1, bs1=bs1, nn=nn)
250 |
251 |
252 | def calc_rad_sol_in(n, lat, as1=0.25, bs1=0.5, nn=None):
253 | """Incoming solar radiation [MJ m-2 d-1].
254 |
255 | Parameters
256 | ----------
257 | n: pandas.Series or xarray.DataArray
258 | actual duration of sunshine [hour].
259 | lat: float, optional
260 | the site latitude [rad].
261 | as1: float, optional
262 | regression constant, expressing the fraction of extraterrestrial reaching the
263 | earth on overcast days (n = 0) [-].
264 | bs1: float, optional
265 | empirical coefficient for extraterrestrial radiation [-].
266 | nn: pandas.Series/float, optional
267 | maximum possible duration of sunshine or daylight hours [hour].
268 |
269 | Returns
270 | -------
271 | pandas.Series containing the calculated net shortwave radiation.
272 |
273 | Notes
274 | -----
275 | Based on equation 35 in :cite:t:`allen_crop_1998`.
276 |
277 | """
278 | tindex = get_index(n)
279 | ra = extraterrestrial_r(tindex, lat)
280 | if nn is None:
281 | nn = daylight_hours(tindex, lat)
282 | return (as1 + bs1 * n / nn) * ra
283 |
284 |
285 | def calc_rso(ra, elevation, kab=None):
286 | """Clear-sky solar radiation [MJ m-2 day-1].
287 |
288 | Parameters
289 | ----------
290 | ra: pandas.Series/xarray.DataArray, optional
291 | Extraterrestrial daily radiation [MJ m-2 d-1].
292 | elevation: float/xarray.DataArray, optional
293 | the site elevation [m].
294 | kab: float, optional
295 | coefficient that can be derived from the as and bs coefficients of the
296 | Angstrom formula, where Kab = as + bs, and where Kab represents the
297 | fraction of extraterrestrial radiation reaching the earth on clear-sky
298 | days [-].
299 |
300 | Returns
301 | -------
302 | pandas.Series/xarray.DataArray, optional containing the calculated Clear-sky solar
303 | radiation.
304 |
305 | Notes
306 | -----
307 | Based on equation 37 in :cite:t:`allen_crop_1998`.
308 |
309 | """
310 | if isinstance(elevation, DataArray):
311 | tindex = get_index(ra)
312 | elevation = elevation.expand_dims(dim={"time": tindex}, axis=0)
313 | if isinstance(ra, Series):
314 | ra = ra.values[:, newaxis, newaxis]
315 | if kab is None:
316 | return (0.75 + (2 * 10**-5) * elevation) * ra
317 | else:
318 | return kab * ra
319 |
--------------------------------------------------------------------------------
/pyet/radiation.py:
--------------------------------------------------------------------------------
1 | """The radiation module contains functions of radiation PET methods
2 |
3 | """
4 |
5 | from numpy import sqrt, log
6 | from xarray import DataArray
7 | from pandas import Series
8 | from .meteo_utils import extraterrestrial_r, calc_press, calc_psy, calc_vpc, calc_lambda
9 | from .utils import get_index, check_rad, clip_zeros, pet_out, check_rh
10 |
11 |
12 | def turc(tmean, rs, rh, k=0.013, clip_zero=True):
13 | """Potential evapotranspiration calculated according to
14 | :cite:t:`turc_estimation_1961`.
15 |
16 | Parameters
17 | ----------
18 | tmean: pandas.Series or xarray.DataArray
19 | average day temperature [°C].
20 | rs: pandas.Series or xarray.DataArray
21 | incoming solar radiation [MJ m-2 d-1].
22 | rh: pandas.Series or xarray.DataArray
23 | mean daily relative humidity [%].
24 | k: float, optional
25 | calibration coefficient [-].
26 | clip_zero: bool, optional
27 | if True, replace all negative values with 0.
28 |
29 | Returns
30 | -------
31 | pandas.Series or xarray.DataArray containing the calculated
32 | Potential evapotranspiration [mm d-1].
33 |
34 | Examples
35 | --------
36 | >>> pet_turc = turc(tmean, rs, rh)
37 |
38 | Notes
39 | -----
40 | Based on equation S9.10 and S9.11 in :cite:t:`mcmahon_estimating_2013`.
41 |
42 | .. math:: PET=k(\\frac{T_{mean}}{T_{mean}+15})(23.88R_s+50)0.013;
43 | for RH>50
44 |
45 | .. math:: PET=k(1+\\frac{50-RH}{70})(\\frac{T_{mean}}{T_{mean}+15})
46 | (23.88R_s+50)0.013; for RH<50
47 |
48 | """
49 | c = tmean / tmean
50 | c = c.where(check_rh(rh) >= 50, 1 + (50 - rh) / 70)
51 | pet = k * c * tmean / (tmean + 15) * (check_rad(rs) * 23.88 + 50)
52 | pet = clip_zeros(pet, clip_zero)
53 | return pet_out(tmean, pet, "Turc")
54 |
55 |
56 | def jensen_haise(tmean, rs=None, cr=0.025, tx=-3, lat=None, method=0, clip_zero=True):
57 | """Potential evapotranspiration calculated accordinf to
58 | :cite:t:`jensen_estimating_1963`.
59 |
60 | Parameters
61 | ----------
62 | tmean: pandas.Series orxarray.DataArray
63 | average day temperature [°C].
64 | rs: pandas.Series or xarray.DataArray, optional
65 | incoming solar radiation [MJ m-2 d-1].
66 | cr: float, optional
67 | temperature coefficient [-].
68 | tx: float, optional
69 | intercept of the temperature axis [°C].
70 | lat: float/xarray.DataArray
71 | the site latitude [rad].
72 | method: float, optional
73 | 0 => after :cite:t:`jensen_evaporation_2016`
74 | 1 => after :cite:t:`oudin_which_2005`.
75 | clip_zero: bool, optional
76 | if True, replace all negative values with 0.
77 |
78 | Returns
79 | -------
80 | pandas.Series or xarray.DataArray containing the calculated potential
81 | evapotranspiration [mm d-1].
82 |
83 | Examples
84 | --------
85 | >>> pet_jh = jensen_haise(tmean, lat)
86 |
87 | Notes
88 | -----
89 | Method = 0: Based on :cite:t:`jensen_evaporation_2016`.
90 |
91 | .. math:: PET = \\frac{C_r(T_{mean}-T_x)R_s}{\\lambda}
92 |
93 | Method = 1: Based on :cite:t:`oudin_which_2005`.
94 |
95 | .. math:: PET = \\frac{R_a(T_{mean}+5)}{68\\lambda}
96 |
97 | """
98 | lambd = calc_lambda(tmean)
99 | if method == 0:
100 | if rs is None:
101 | raise Exception("If you choose method == 0, provide rs!")
102 | pet = check_rad(rs) / lambd * cr * (tmean - tx)
103 | elif method == 1:
104 | if lat is None:
105 | raise Exception("If you choose method == 1, provide lat!")
106 | index = get_index(tmean)
107 | ra = extraterrestrial_r(index, lat, tmean)
108 | pet = ra * (tmean + 5) / 68 / lambd
109 | else:
110 | raise Exception("Method can be either 0 or 1.")
111 | pet = clip_zeros(pet, clip_zero)
112 | return pet_out(tmean, pet, "Jensen_Haise")
113 |
114 |
115 | def mcguinness_bordne(tmean, lat, k=0.0147, clip_zero=True):
116 | """Potential evapotranspiration calculated according to
117 | :cite:t:`mcguinness_comparison_1972`.
118 |
119 | Parameters
120 | ----------
121 | tmean: pandas.Series or xarray.DataArray
122 | average day temperature [°C].
123 | lat: float/xarray.DataArray, optional
124 | the site latitude [rad].
125 | k: float, optional
126 | calibration coefficient [-].
127 | clip_zero: bool, optional
128 | if True, replace all negative values with 0.
129 |
130 | Returns
131 | -------
132 | pandas.Series or xarray.DataArray containing the calculated potential
133 | evapotranspiration [mm d-1].
134 |
135 | Examples
136 | --------
137 | >>> pet_mcguinness_bordne = mcguinness_bordne(tmean, lat)
138 |
139 | Notes
140 | -----
141 | Based on equation 13 in :cite:t:`xu_evaluation_2000`.
142 |
143 | .. math:: PET = k\\frac{R_a (T_{mean} + 5)}{\\lambda}
144 |
145 | """
146 | lambd = calc_lambda(tmean)
147 | index = get_index(tmean)
148 | ra = extraterrestrial_r(index, lat)
149 | if isinstance(tmean, DataArray) and isinstance(ra, Series):
150 | ra = ra.values[:, None, None]
151 | pet = k * ra * (tmean + 5) / lambd
152 | pet = clip_zeros(pet, clip_zero)
153 | return pet_out(tmean, pet, "Mcguinness_Bordne")
154 |
155 |
156 | def hargreaves(tmean, tmax, tmin, lat, k=0.0135, method=0, clip_zero=True):
157 | """Potential evapotranspiration calculated according to
158 | :cite:t:`hargreaves_estimating_1982`.
159 |
160 | Parameters
161 | ----------
162 | tmean: pandas.Series or xarray.DataArray
163 | average day temperature [°C].
164 | tmax: pandas.Series or xarray.DataArray
165 | maximum day temperature [°C].
166 | tmin: pandas.Series or xarray.DataArray
167 | minimum day temperature [°C].
168 | lat: float/xarray.DataArray
169 | the site latitude [rad].
170 | k: float, optional
171 | calirbation coefficient [-].
172 | method: float, optional
173 | 0 => after :cite:t:`jensen_evaporation_2016`
174 | 1 => after :cite:t:`mcmahon_estimating_2013`.
175 | clip_zero: bool, optional
176 | if True, replace all negative values with 0.
177 |
178 | Returns
179 | -------
180 | pandas.Series or xarray.DataArray containing the calculated potential
181 | evapotranspiration [mm d-1].
182 |
183 | Examples
184 | --------
185 | >>> pet_har = hargreaves(tmean, tmax, tmin, lat)
186 |
187 | Notes
188 | -----
189 | Method = 0; Based on equation (8-16) in :cite:t:`jensen_evaporation_2016`.
190 |
191 | .. math:: PET = k \\frac{R_a (T_{mean}+17.8)\\sqrt{(T_{max}-T_{min})}}\
192 | {\\lambda}
193 |
194 | Method = 1; Based on :cite:t:`mcmahon_estimating_2013`.
195 |
196 | .. math:: PET = chs k \\frac{R_a (T_{mean}+17.8)\\sqrt{(T_{max}-T_{min})}}\
197 | {\\lambda}
198 |
199 | , where
200 |
201 | .. math:: chs=0.00185*(T_{max}-T_{min})^2-0.0433*(T_{max}-T_{min})+0.4023
202 |
203 | """
204 | lambd = calc_lambda(tmean)
205 | index = get_index(tmean)
206 | ra = extraterrestrial_r(index, lat)
207 | if isinstance(tmean, DataArray) and isinstance(ra, Series):
208 | ra = ra.values[:, None, None]
209 | else:
210 | ra = ra.values
211 | chs = 0.00185 * (tmax - tmin) ** 2 - 0.0433 * (tmax - tmin) + 0.4023
212 | if method == 0:
213 | pet = k / 0.0135 * 0.0023 * (tmean + 17.8) * sqrt(tmax - tmin) * ra / lambd
214 | elif method == 1:
215 | pet = k * chs * sqrt(tmax - tmin) * ra / lambd * (tmean + 17.8)
216 | else:
217 | raise Exception("Method can be either 0 or 1.")
218 | pet = clip_zeros(pet, clip_zero)
219 | return pet_out(tmean, pet, "Hargreaves")
220 |
221 |
222 | def fao_24(
223 | tmean, wind, rs, rh, pressure=None, elevation=None, albedo=0.23, clip_zero=True
224 | ):
225 | """Potential evapotranspiration calculated according to
226 | :cite:t:`jensen_evapotranspiration_1990`.
227 |
228 | Parameters
229 | ----------
230 | tmean: pandas.Series or xarray.DataArray
231 | average day temperature [°C].
232 | wind: pandas.Series xarray.DataArray
233 | mean day wind speed [m/s].
234 | rs: pandas.Series xarray.DataArray
235 | incoming solar radiation [MJ m-2 d-1].
236 | rh: pandas.Series or xarray.DataArray
237 | mean daily relative humidity [%].
238 | pressure: pandas.Series or xarray.DataArray, optional
239 | atmospheric pressure [kPa].
240 | elevation: float/xarray.DataArray, optional
241 | the site elevation [m].
242 | albedo: float/xarray.DataArray, optional
243 | surface albedo [-].
244 | clip_zero: bool, optional
245 | if True, replace all negative values with 0.
246 |
247 | Returns
248 | -------
249 | pandas.Series or xarray.DataArray containing the calculated potential
250 | evapotranspiration [mm d-1].
251 |
252 | Examples
253 | --------
254 | >>> pet_fao24 = fao_24(tmean, wind, rs, rh, elevation=elevation)
255 |
256 | .. math:: PE = \\frac{- 0.3 \\Delta + R_s (1-\\alpha) w}\
257 | {\\lambda(\\Delta +\\gamma)}
258 | .. math:: w = 1.066-0.13*\\frac{rh}{100}+0.045*u_2-0.02*\\frac{rh}{100}\
259 | *u_2-3.15*(\\frac{rh}{100})^2-0.0011*u_2
260 |
261 | """
262 | pressure = calc_press(elevation, pressure)
263 | gamma = calc_psy(pressure)
264 | dlt = calc_vpc(tmean)
265 | lambd = calc_lambda(tmean)
266 |
267 | w = (
268 | 1.066
269 | - 0.13 * check_rh(rh) / 100
270 | + 0.045 * wind
271 | - 0.02 * rh / 100 * wind
272 | - 0.315 * (rh / 100) ** 2
273 | - 0.0011 * wind
274 | )
275 | pet = -0.3 + dlt / (dlt + gamma) * check_rad(rs) * (1 - albedo) * w / lambd
276 | pet = clip_zeros(pet, clip_zero)
277 | return pet_out(tmean, pet, "FAO_24")
278 |
279 |
280 | def abtew(tmean, rs, k=0.53, clip_zero=True):
281 | """Potential evapotranspiration calculated according to
282 | :cite:t:`abtew_evapotranspiration_1996`.
283 |
284 | Parameters
285 | ----------
286 | tmean: pandas.Series or xarray.DataArray
287 | average day temperature [°C].
288 | rs: pandas.Series or xarray.DataArray
289 | incoming solar radiation [MJ m-2 d-1].
290 | k: float, optional
291 | calibration coefficient [-].
292 | clip_zero: bool, optional
293 | if True, replace all negative values with 0.
294 |
295 | Returns
296 | -------
297 | pandas.Series or xarray.DataArray containing the calculated potential
298 | evapotranspiration [mm d-1].
299 |
300 | Examples
301 | --------
302 | >>> pet_abtew = abtew(tmean, rs)
303 |
304 | Notes
305 | -----
306 | Based on equation 14 in :cite:t:`xu_evaluation_2000`.
307 |
308 | .. math:: PE = \\frac{k R_s}{\\lambda}
309 |
310 | """
311 | lambd = calc_lambda(tmean)
312 | pet = k * check_rad(rs) / lambd
313 | pet = clip_zeros(pet, clip_zero)
314 | return pet_out(tmean, pet, "Abtew")
315 |
316 |
317 | def makkink(tmean, rs, pressure=None, elevation=None, k=0.65, clip_zero=True):
318 | """Potential evapotranspiration calculated according to
319 | :cite:t:`makkink_testing_1957`.
320 |
321 | Parameters
322 | ----------
323 | tmean: pandas.Series or xarray.DataArray
324 | average day temperature [°C].
325 | rs: pandas.Series or xarray.DataArray
326 | incoming solar radiation [MJ m-2 d-1].
327 | pressure: pandas.Series or xarray.DataArray, optional
328 | atmospheric pressure [kPa].
329 | elevation: float/xarray.DataArray, optional
330 | the site elevation [m].
331 | k: float, optional
332 | calirbation coefficient [-].
333 | clip_zero: bool, optional
334 | if True, replace all negative values with 0.
335 |
336 | Returns
337 | -------
338 | pandas.Series or xarray.DataArray containing the calculated potential
339 | evapotranspiration [mm d-1].
340 |
341 | Examples
342 | --------
343 | >>> pet_mak = makkink(tmean, rs, elevation=elevation)
344 |
345 | Notes
346 | -----
347 |
348 | .. math:: PET = \\frac{0.65 \\Delta (R_s)}{\\lambda(\\Delta +\\gamma)}
349 |
350 | """
351 | pressure = calc_press(elevation, pressure)
352 | gamma = calc_psy(pressure)
353 | dlt = calc_vpc(tmean)
354 | lambd = calc_lambda(tmean)
355 | pet = k * dlt / (dlt + gamma) * check_rad(rs) / lambd
356 | pet = clip_zeros(pet, clip_zero)
357 | return pet_out(tmean, pet, "Makkink")
358 |
359 |
360 | def makkink_knmi(tmean, rs, clip_zero=True):
361 | """Potential evapotranspiration calculated according to The Royal Netherlands
362 | Meteorological Institute (KNMI)
363 |
364 | Parameters
365 | ----------
366 | tmean : pandas.Series or xarray.DataArray
367 | average day temperature [°C].
368 | rs : pandas.Series or xarray.DataArray
369 | incoming solar radiation [MJ m-2 d-1].
370 | clip_zero: bool, optional
371 | if True, replace all negative values with 0.
372 |
373 | Returns
374 | -------
375 | pandas.Series or xarray.DataArray containing the calculated potential
376 | evapotranspiration [mm d-1].
377 |
378 | Examples
379 | --------
380 | >>> pet_mak = makkink_knmi(tmean, rs)
381 |
382 | Notes
383 | ----
384 | This method is only applicable to the Netherlands (~sea level) due to some
385 | emperical values. Calculating the Makkink evaporation with the original
386 | formula is more suitable for general purposes. To obtain the same value as
387 | EV24 round the value up to one decimal.
388 | """
389 | pet = (
390 | 650
391 | * (
392 | 1
393 | - (0.646 + 0.0006 * tmean)
394 | / (
395 | 7.5
396 | * log(10)
397 | * 6.107
398 | * 10 ** (7.5 * (1 - 1 / (1 + tmean / 237.3)))
399 | / (237.3 * (1 + tmean / 237.3) * (1 + tmean / 237.3))
400 | + 0.646
401 | + 0.0006 * tmean
402 | )
403 | )
404 | / (2501 - 2.38 * tmean)
405 | * rs
406 | )
407 | pet = clip_zeros(pet, clip_zero)
408 | return pet_out(tmean, pet, "Makkink_KNMI")
409 |
410 |
411 | def oudin(tmean, lat, k1=100, k2=5, clip_zero=True):
412 | """Potential evapotranspiration calculated according to :cite:t:`oudin_which_2005`.
413 |
414 | Parameters
415 | ----------
416 | tmean: pandas.Series or xarray.DataArray
417 | average day temperature [°C].
418 | lat: float or xarray.DataArray
419 | the site latitude [rad].
420 | k1: float, optional
421 | calibration coefficient [-].
422 | k2: float, optional
423 | calibration coefficient [-].
424 | clip_zero: bool, optional
425 | if True, replace all negative values with 0.
426 |
427 | Returns
428 | -------
429 | pandas.Series or xarray.DataArray containing the calculated potential
430 | evapotranspiration [mm d-1].
431 | clip_zero: bool, optional
432 | if True, replace all negative values with 0.
433 |
434 | Examples
435 | --------
436 | >>> pet_oudin = oudin(tmean, lat)
437 |
438 | Notes
439 | -----
440 | Based on equation 3 in :cite:t:`oudin_which_2005`.
441 |
442 | .. math:: PET = \\frac{R_a (T_{mean} +5)}{\\lambda 100}; if T_{mean}+5>0
443 | else: PET = 0
444 |
445 | """
446 | lambd = calc_lambda(tmean)
447 | index = get_index(tmean)
448 | ra = extraterrestrial_r(index, lat)
449 | pet = ra * (tmean + k2) / lambd / k1
450 | pet = pet.where((tmean + k2) >= 0, 0)
451 | pet = clip_zeros(pet, clip_zero)
452 | return pet_out(tmean, pet, "Oudin")
453 |
--------------------------------------------------------------------------------
/pyet/temperature.py:
--------------------------------------------------------------------------------
1 | """The temperature module contains functions of temperature PET methods.
2 |
3 | """
4 |
5 | from numpy import exp, pi, asarray, newaxis
6 | from pandas import date_range
7 | from xarray import DataArray
8 |
9 | from .meteo_utils import daylight_hours, calc_ea, calc_es, calc_e0
10 | from .utils import get_index, check_lat, clip_zeros, check_rh, pet_out
11 |
12 |
13 | def blaney_criddle(
14 | tmean,
15 | lat,
16 | a=-1.55,
17 | b=0.96,
18 | k=0.65,
19 | wind=None,
20 | rhmin=None,
21 | n=None,
22 | nn=None,
23 | py=None,
24 | method=0,
25 | clip_zero=True,
26 | ):
27 | """Potential evapotranspiration calculated according to
28 | :cite:t:`blaney_determining_1952`.
29 |
30 | Parameters
31 | ----------
32 | tmean: pandas.Series or xarray.DataArray
33 | average day temperature [°C].
34 | lat: float or xarray.DataArray, optional
35 | the site latitude [rad].
36 | a: float, optional
37 | calibration coefficient for method 0 [-].
38 | b: float, optional
39 | calibration coefficient for method 0 [-].
40 | k: float, optional
41 | calibration coefficient for method 1 [-].
42 | wind: float or pandas.Series or xarray.DataArray, optional
43 | mean day wind speed [m/s].
44 | rhmin: float or pandas.Series or xarray.DataArray, optional
45 | mainimum daily relative humidity [%].
46 | n: float or pandas.Series or xarray.DataArray, optional
47 | actual duration of sunshine [hour].
48 | nn: float or pandas.Series or xarray.DataArray, optional
49 | maximum possible duration of sunshine or daylight hours [hour].
50 | py: float or pandas.Series or xarray.DataArray, optional
51 | percentage of actual day-light hours for the day compared to the
52 | number of day-light hour during the entire year [-].
53 | method: float, optional
54 | 0 => Blaney Criddle after :cite:t:`schrodter_hinweise_1985`
55 | 1 => Blaney Criddle after :cite:t:`xu_evaluation_2001`
56 | 2 => FAO-24 Blaney Criddle after :cite:t:`mcmahon_estimating_2013`.
57 | clip_zero: bool, optional
58 | if True, replace all negative values with 0.
59 |
60 | Returns
61 | -------
62 | float or pandas.Series or xarray.DataArray containing the calculated
63 | Potential evapotranspiration [mm d-1].
64 |
65 | Examples
66 | --------
67 | >>> pet_blaney_criddle = blaney_criddle(tmean, lat)
68 |
69 | Notes
70 | -----
71 | Method = 0; Based on :cite:p:`schrodter_hinweise_1985`.
72 |
73 | .. math:: PET=a+b(py(0.46 * T_{mean} + 8.13))
74 |
75 | Method = 1; Based on :cite:p:`xu_evaluation_2001`.
76 |
77 | .. math:: PET=kpy(0.46 * T_{mean} + 8.13)
78 |
79 | Method = 2; Based on :cite:p:`mcmahon_estimating_2013`.
80 |
81 | .. math:: PET=k_1+b_{var}(py(0.46*T_{mean} + 8.13))
82 |
83 | , where:
84 |
85 | .. math:: k1 = (0.0043RH_{min}-\\frac{n}{N}-1.41)
86 |
87 | .. math:: bvar =e_0+e1 RH_{min}+e_2 \\frac{n}{N} + e_3 u_2 +
88 | e_4 RH_{min} \\frac{n}{N} + e_5 * RH_{min} * u_2
89 |
90 | .. math:: e_0=0.81917, e_1 = -0.0040922, e_2 = 1.0705, e_3 = 0.065649,
91 | e_4 = -0.0059684, e_5 = -0.0005967.
92 |
93 | """
94 | index = get_index(tmean)
95 | if nn is None:
96 | nn = daylight_hours(index, lat)
97 | if py is None:
98 | nn_sum = sum(daylight_hours(date_range("2000-1-1", "2000-12-31"), lat))
99 | py = nn / nn_sum * 100
100 | if isinstance(tmean, DataArray) and len(py.shape) == 1:
101 | py = py[:, None, None]
102 | if method == 0:
103 | pet = a + b * (py * (0.457 * tmean + 8.128))
104 | elif method == 1:
105 | pet = k * py * (0.46 * tmean + 8.13)
106 | elif method == 2:
107 | nn_sum = sum(daylight_hours(date_range("2000-1-1", "2000-12-31"), lat))
108 | py = n / nn_sum * 100
109 | if isinstance(rhmin, DataArray) and len(nn.shape) == 1:
110 | nn = nn[:, None, None]
111 | k1 = 0.0043 * rhmin - n / nn - 1.41
112 | e0, e1, e2, e3, e4, e5 = (
113 | 0.81917,
114 | -0.0040922,
115 | 1.0705,
116 | 0.065649,
117 | -0.0059684,
118 | -0.0005967,
119 | )
120 | bvar = (
121 | e0
122 | + e1 * rhmin
123 | + e2 * n / nn
124 | + e3 * wind
125 | + e4 * rhmin * n / nn
126 | + e5 * rhmin * wind
127 | )
128 | pet = k1 + bvar * py * (0.46 * tmean + 8.13)
129 | else:
130 | raise Exception("Method can be either 0, 1 or 2.")
131 | pet = clip_zeros(pet, clip_zero)
132 | return pet_out(tmean, pet, "Blaney_Criddle")
133 |
134 |
135 | def haude(tmean, rh, k=1, clip_zero=True):
136 | """Potential evapotranspiration calculated according to
137 | :cite:t:`haude_determination_1955`.
138 |
139 | Parameters
140 | ----------
141 | tmean: pandas.Series or xarray.DataArray
142 | temperature at 2pm or maximum dailty temperature [°C].
143 | rh: float or pandas.Series or xarray.DataArray
144 | average relative humidity at 2pm [%].
145 | k: float, optional
146 | calibration coefficient [-].
147 | clip_zero: bool, optional
148 | if True, replace all negative values with 0.
149 |
150 | Returns
151 | -------
152 | float or pandas.Series or xarray.DataArray containing the calculated potential
153 | evapotranspiration [mm d-1].
154 |
155 | Examples
156 | --------
157 | >>> pet_haude = haude(tmean, rh)
158 |
159 | Notes
160 | -----
161 | Following :cite:t:`haude_determination_1955` and :cite:t:`schiff_berechnung_1975`.
162 |
163 | .. math:: PET = k * f * (e_s-e_a)
164 |
165 | """
166 | e0 = calc_e0(tmean)
167 | ea = check_rh(rh) * e0 / 100
168 | # Haude coefficients from :cite:t:`schiff_berechnung_1975`
169 | fk = [0.27, 0.27, 0.28, 0.39, 0.39, 0.37, 0.35, 0.33, 0.31, 0.29, 0.27, 0.27]
170 | index = get_index(tmean)
171 | fk1 = asarray([fk[x - 1] for x in index.month])
172 | if len(tmean.shape) > 1:
173 | f = fk1[:, newaxis, newaxis] * (tmean / tmean)
174 | else:
175 | f = fk1
176 | pet = k * f * (e0 - ea) * 10 # kPa to hPa
177 | pet = clip_zeros(pet, clip_zero)
178 | return pet_out(tmean, pet, "Haude")
179 |
180 |
181 | def hamon(
182 | tmean,
183 | lat,
184 | k=1,
185 | c=13.97,
186 | cc=218.527,
187 | n=None,
188 | tmax=None,
189 | tmin=None,
190 | method=0,
191 | clip_zero=True,
192 | ):
193 | """Potential evapotranspiration calculated according to
194 | :cite:t:`hamon_estimating_1963`.
195 |
196 | Parameters
197 | ----------
198 | tmean: pandas.Series or xarray.DataArray
199 | average day temperature [°C].
200 | lat: float or xarray.DataArray
201 | the site latitude [rad].
202 | k: float, optional
203 | calibration coefficient if method = 0 [-].
204 | c: float, optional
205 | c is a constant for calculation in mm per day if method = 1.
206 | cc: float, optional
207 | calibration coefficient if method = 2 [-].
208 | n: float or pandas.Series or xarray.DataArray, optional
209 | actual duration of sunshine [hour].
210 | tmax: float or pandas.Series or xarray.DataArray
211 | maximum day temperature [°C].
212 | tmin: float or pandas.Series or xarray.DataArray
213 | minimum day temperature [°C].
214 |
215 | method: float, optional
216 | 0 => Hamon after :cite:t:`oudin_which_2005`
217 | 1 => Hamon after equation 7 in :cite:t:`ansorge_performance_2019`
218 | 2 => Hamon after equation 12 in :cite:t:`ansorge_performance_2019`.
219 | 3 => Hamon after equation 12 in :cite:t:`rosenberry_comparison_2004`.
220 | clip_zero: bool, optional
221 | if True, replace all negative values with 0.
222 |
223 | Returns
224 | -------
225 | float or pandas.Series or xarray.DataArray containing the calculated potential
226 | evapotranspiration [mm d-1].
227 |
228 | Examples
229 | --------
230 | >>> pet_hamon = hamon(tmean, lat)
231 |
232 | Notes
233 | -----
234 | Method = 0; Based on cite:t:`oudin_which_2005`.
235 |
236 | .. math:: PET = k(\\frac{DL}{12})^2 exp(\\frac{T_{mean}}{16})
237 |
238 | Method = 1; Based on equation 7 in cite:t:`ansorge_performance_2019`.
239 |
240 | .. math:: PET = c(\\frac{DL}{12})^2 pt
241 |
242 | where
243 |
244 | .. math:: pt = 4.95 \\frac{exp(0.062T_{mean})}{16}
245 |
246 | Method = 2; Based on equation 12 in cite:t:`ansorge_performance_2019`.
247 |
248 | .. math:: PET = cc\\frac{DL}{12} \\frac{1}{T_{mean} + 273.3}
249 | exp(\\frac{17.27T_{mean}}{T_{mean} + 273.3})
250 |
251 | Method = 3; Based on cite:t:`rosenberry_comparison_2004`.
252 |
253 | .. math:: PET = 14 * (n / 12) ** 2 * (216.7 * e_s * 10 /
254 | (T_{mean} + 273.3)) / 100
255 |
256 | """
257 | index = get_index(tmean)
258 | # Use transpose to work with lat either as int or xarray.DataArray
259 | dl = daylight_hours(index, lat)
260 | if len(dl.shape) < len(tmean.shape):
261 | dl = tmean / tmean * dl[:, newaxis, newaxis]
262 | if method == 0:
263 | pet = k * (dl / 12) ** 2 * exp(tmean / 16)
264 | elif method == 1:
265 | # saturated water content after Xu and Singh (2001)
266 | pt = 4.95 * exp(0.062 * tmean) / 100
267 | pet = c * (dl / 12) ** 2 * pt
268 | elif method == 2:
269 | pet = (
270 | cc
271 | * (dl / 12)
272 | * 1
273 | / (tmean + 273.3)
274 | * exp((17.26939 * tmean) / (tmean + 273.3))
275 | )
276 | elif method == 3:
277 | es = calc_es(tmean, tmax, tmin)
278 | pet = k * 14 * (n / 12) ** 2 * (216.7 * es * 10 / (tmean + 273.3)) / 100
279 | else:
280 | raise Exception("method can be either 0, 1, 2 or 3.")
281 | pet = clip_zeros(pet, clip_zero)
282 | return pet_out(tmean, pet, "Hamon")
283 |
284 |
285 | def romanenko(
286 | tmean, rh, k=4.5, rhmax=None, rhmin=None, tmax=None, tmin=None, clip_zero=True
287 | ):
288 | """Potential evapotranspiration calculated according to
289 | :cite:t:`romanenko_computation_1961`.
290 |
291 | Parameters
292 | ----------
293 | tmean: float or pandas.Series or xarray.DataArray
294 | average day temperature [°C].
295 | rh: float or pandas.Series or xarray.DataArray
296 | mean daily relative humidity [%].
297 | k: float, optional
298 | calibration coefficient [-].
299 | tmax: float or pandas.Series or xarray.DataArray
300 | maximum day temperature [°C].
301 | tmin: float or pandas.Series or xarray.DataArray
302 | minimum day temperature [°C].
303 | rhmax: pandas.Series, optional
304 | maximum daily relative humidity [%].
305 | rhmin: pandas.Series, optional
306 | mainimum daily relative humidity [%].
307 | clip_zero: bool, optional
308 | if True, replace all negative values with 0.
309 |
310 | Returns
311 | -------
312 | float or pandas.Series or xarray.DataArray containing the calculated potential
313 | evapotranspiration [mm d-1].
314 |
315 | Examples
316 | --------
317 | >>> pet_romanenko = romanenko(tmean, rh)
318 |
319 | Notes
320 | -----
321 | Based on equation 11 in :cite:p:`xu_evaluation_2001`.
322 |
323 | .. math:: PET=k(1 + (\\frac{T_{mean}}{25})^2 (1 - \\frac{e_a}{e_s})
324 |
325 | """
326 | ea = calc_ea(
327 | tmean=tmean,
328 | tmax=tmax,
329 | tmin=tmin,
330 | rhmax=check_rh(rhmax),
331 | rhmin=check_rh(rhmin),
332 | rh=check_rh(rh),
333 | )
334 | es = calc_es(tmean=tmean, tmax=tmax, tmin=tmin)
335 | pet = k * (1 + tmean / 25) ** 2 * (1 - ea / es)
336 | pet = clip_zeros(pet, clip_zero)
337 | return pet_out(tmean, pet, "Romanenko")
338 |
339 |
340 | def linacre(tmean, elevation, lat, tdew=None, tmax=None, tmin=None, clip_zero=True):
341 | """Potential evapotranspiration calculated according to
342 | :cite:t:`linacre_simple_1977`.
343 |
344 | Parameters
345 | ----------
346 | tmean: pandas.Series or array_like
347 | average day temperature [°C].
348 | elevation: array_like
349 | the site elevation [m].
350 | lat: array_like, optional
351 | the site latitude [°].
352 | tdew: pandas.Series or array_like, optional
353 | mean dew-point temperature [°C].
354 | tmax: pandas.Series or array_like, optional
355 | maximum day temperature [°C].
356 | tmin: pandas.Series or array_like, optional
357 | minimum day temperature [°C].
358 | clip_zero: bool, optional
359 | if True, replace all negative values with 0.
360 |
361 | Returns
362 | -------
363 | pandas.Series or array_like containing the calculated potential
364 | evapotranspiration [mm d-1].
365 |
366 | Examples
367 | --------
368 | >>> pet_linacre = linacre(tmean, elevation, lat)
369 |
370 | Notes
371 | -----
372 | Based on equation 5 in :cite:p:`xu_evaluation_2001`.
373 |
374 | .. math:: PET = \\frac{\\frac{500 T_m}{(100-lat)}+15 (T_a-T_d)}{80-T_a}
375 |
376 | """
377 | if tdew is None and tmax is None and tmin is None:
378 | raise Exception("Please provide either Tdew or Tmax and Tmin!")
379 | lat = check_lat(lat)
380 | lat_deg = lat / pi * 180
381 | if tdew is None:
382 | tmax, tmin = tmax.values, tmin.values
383 | tdew = 0.52 * tmin + 0.6 * tmax - 0.009 * tmax**2 - 2
384 |
385 | tm = tmean + 0.006 * elevation
386 | pet = (500 * tm / (100 - lat_deg) + 15 * (tmean - tdew)) / (80 - tmean)
387 | pet = clip_zeros(pet, clip_zero)
388 | return pet_out(tmean, pet, "Linacre")
389 |
--------------------------------------------------------------------------------
/pyet/utils.py:
--------------------------------------------------------------------------------
1 | import numpy
2 | from pandas import Series, DatetimeIndex
3 | from xarray import DataArray
4 |
5 |
6 | def show_versions():
7 | """Method to print the version of dependencies."""
8 | from pyet import __version__ as ps_version
9 | from pandas import __version__ as pd_version
10 | from numpy import __version__ as np_version
11 | from sys import version as os_version
12 | from xarray import __version__ as xr_version
13 |
14 | msg = (
15 | f"Python version: {os_version}\n"
16 | f"Numpy version: {np_version}\n"
17 | f"Pandas version: {pd_version}\n"
18 | f"xarray version: {xr_version}\n"
19 | f"Pyet version: {ps_version}"
20 | )
21 | return print(msg)
22 |
23 |
24 | def deg_to_rad(lat):
25 | """Method to convert latitude in degrees to radians.
26 |
27 | Parameters
28 | ----------
29 | lat: float or xarray.DataArray
30 | The site latitude [deg].
31 |
32 | Returns
33 | -------
34 | float or pandas.Series or xarray.DataArray containing the calculated latitude in
35 | radians [rad].
36 | """
37 | return lat * numpy.pi / 180
38 |
39 |
40 | def check_rad(rad):
41 | """Method to check if radiation was probably provided in MJ/m2d."""
42 | if rad is not None:
43 | if numpy.nanmax(rad) < 100:
44 | return rad
45 | else:
46 | raise Exception(
47 | "The radiation input provided is greater than 100 MJ/m2d, "
48 | "which is not realistic. Please convert the radiation input"
49 | " to MJ/m2d."
50 | )
51 |
52 |
53 | def check_rh(rh):
54 | """Method to check if relative humidity is provided in percentage."""
55 | if rh is not None:
56 | if numpy.nanmax(rh) > 1.0:
57 | return rh
58 | else:
59 | raise Exception(
60 | "The maximum value of relative humidity provided is smaller "
61 | "than 1 [%], which is not realistic. Please convert the "
62 | "relative humidity to [%]."
63 | )
64 | else:
65 | pass
66 |
67 |
68 | def check_lat(lat, shape=None):
69 | """Method to check if latitude was (most likely) given in radians."""
70 | if not isinstance(lat, (float, int, DataArray)):
71 | raise TypeError("lat must be a float, int or DataArray")
72 | if isinstance(lat, (float, int)) and (shape is None or len(shape) == 1):
73 | lat1 = lat
74 | else:
75 | if isinstance(lat, (float, int)) and (len(shape) > 1):
76 | raise ValueError(f"lat must be a shaped as 2D DataArray")
77 | lat1 = lat.values
78 | if not (-1.6 < numpy.mean(lat1) < 1.6):
79 | raise Exception(
80 | "Latitude must be provided in radians! Use pyet.deg_to_rad()"
81 | "to convert from degrees to radians."
82 | )
83 | return lat1
84 |
85 |
86 | def clip_zeros(s, clip_zero):
87 | """Method to replace negative values with 0 for Pandas.Series and xarray.DataArray."""
88 | if clip_zero:
89 | s = s.where((s >= 0) | s.isnull(), 0)
90 | return s
91 |
92 |
93 | def pet_out(tmean, pet, name):
94 | """Method to create pandas.Series or xarray.DataArray from numpy.ndarray"""
95 | if isinstance(tmean, (Series, DataArray)):
96 | return pet.rename(name)
97 | else:
98 | raise TypeError("Input must be either pandas.Series or xarray.DataArray!")
99 |
100 |
101 | def get_index(df):
102 | """Method to return the index of the input data."""
103 | try:
104 | index = DatetimeIndex(df.index)
105 | except AttributeError:
106 | index = DatetimeIndex(df.time)
107 | return index
108 |
109 |
110 | def vectorize(*arrays):
111 | """Vectorize pandas.Series or xarray.DataArray inputs."""
112 | vec_arrays = []
113 | for arr in arrays:
114 | if arr is None:
115 | vec_arr = None
116 | elif isinstance(arr, (int, float)):
117 | vec_arr = arr
118 | elif isinstance(arr, Series):
119 | vec_arr = arr.copy().values
120 | elif isinstance(arr, DataArray):
121 | vec_arr = arr.copy().values
122 | else:
123 | raise TypeError(
124 | f"Input must be a pandas.Series or xarray.DataArray, "
125 | f"but got {type(arr)}"
126 | )
127 | vec_arrays.append(vec_arr)
128 | return vec_arrays
129 |
--------------------------------------------------------------------------------
/pyet/version.py:
--------------------------------------------------------------------------------
1 | # This is the only location where the version will be written and changed.
2 | # Based on https://packaging.python.org/single_source_version/
3 | __version__ = "1.3.1"
4 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools", "wheel"]
3 |
4 | [project]
5 | name = "pyet"
6 | dynamic = ["version"]
7 | description = "pyet - Estimation of Potential Evaporation"
8 | readme = "README.md"
9 | authors = [
10 | {name = "Matevz Vremec", email = "matevz.vremec@uni-graz.at"},
11 | {name = "Raoul Collenteur", email = "raoul.collenteur@eawag.ch"}
12 | ]
13 | license = { file = "LICENSE" }
14 | requires-python = ">=3.9"
15 | dependencies = [
16 | "numpy >= 1.16",
17 | "xarray >= 0.18.0",
18 | "pandas >= 1.2",
19 | ]
20 | classifiers = [
21 | 'Development Status :: 5 - Production/Stable',
22 | 'Intended Audience :: Science/Research',
23 | 'Intended Audience :: Other Audience',
24 | 'License :: OSI Approved :: MIT License',
25 | 'Programming Language :: Python :: 3 :: Only',
26 | 'Programming Language :: Python :: 3.9',
27 | 'Programming Language :: Python :: 3.10',
28 | 'Programming Language :: Python :: 3.11',
29 | 'Programming Language :: Python :: 3.12',
30 | 'Topic :: Scientific/Engineering :: Hydrology',
31 | ]
32 |
33 | [project.urls]
34 | Source = "https://github.com/phydrus/pyet"
35 | Tracker = "https://github.com/phydrus/pyet/issues"
36 | Help = "https://github.com/phydrus/pyet/discussions"
37 | homepage = "https://github.com/phydrus/pyet"
38 | repository = "https://github.com/phydrus/pyet"
39 | documentation = "https://github.com/phydrus/pyet/discussions"
40 |
41 | [tool.setuptools.dynamic]
42 | version = { attr = "pyet.version.__version__" }
43 |
44 | [tool.black]
45 | line-length = 88
46 |
47 | [tool.isort]
48 | profile = "black"
49 |
50 | [tool.pytest.ini_options]
51 | testpaths = ["tests"]
52 |
53 | [project.optional-dependencies]
54 | rtd = [
55 | "sphinx-autodoc-typehints",
56 | "Ipython",
57 | "ipykernel",
58 | "pydata-sphinx-theme",
59 | "sphinx-gallery",
60 | "sphinx>=3.1",
61 | "sphinxcontrib-bibtex",
62 | "matplotlib",
63 | "myst-nb",
64 | "numpydoc",
65 | "sphinx-design",
66 | "seaborn",
67 | "netcdf4",
68 | "scikit-learn",
69 | "scipy",
70 | "spotpy",
71 | "openpyxl"
72 | ]
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy>=1.16
2 | xarray>=0.18.0
3 | pandas>=1.2
4 | flake8
5 | pytest
--------------------------------------------------------------------------------
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
2 | from tests import testfao56
3 | from tests import testalternative
4 | from tests import test_all
5 |
--------------------------------------------------------------------------------
/tests/readme.rst:
--------------------------------------------------------------------------------
1 | Tests
2 | -----
3 |
4 | This folder contains all the tests that can be run to automatically test of
5 | pyet runs correctly.
6 |
7 | All tests in this folder can be run with the following command:
8 | >>> python -m unittest -v
9 |
10 | This requires the nosetests package to be installed. If not installed run:
11 | >>> pip install unittest
--------------------------------------------------------------------------------
/tests/test_all.py:
--------------------------------------------------------------------------------
1 | """This file tests all methods for a minimal functioning."""
2 | import unittest
3 |
4 | import numpy as np
5 | import pandas as pd
6 |
7 | import pyet as et
8 |
9 | tmean = pd.Series(data=20 * np.sin(np.linspace(0, 1, 365) * 2 * np.pi),
10 | index=pd.date_range("2001-01-01", "2001-12-31", freq="D"))
11 | tmax = tmean + 2
12 | tmin = tmean - 2
13 | rh = pd.Series(data=60 * np.sin(np.linspace(0, 1, 365) * np.pi),
14 | index=pd.date_range("2001-01-01", "2001-12-31", freq="D"))
15 | rs = pd.Series(data=10 * np.sin(np.linspace(0, 1, 365) * np.pi),
16 | index=pd.date_range("2001-01-01", "2001-12-31", freq="D"))
17 | wind = pd.Series(data=5 * np.sin(np.linspace(0, 1, 365) * np.pi),
18 | index=pd.date_range("2001-01-01", "2001-12-31", freq="D"))
19 | lat = 0.9
20 | elevation = 20
21 |
22 |
23 | class Testall(unittest.TestCase):
24 | def test_calculate_all(self):
25 | et_df = et.calculate_all(tmean, wind, rs, elevation, lat, tmax, tmin,
26 | rh)
27 | self.assertIsInstance(et_df, pd.DataFrame)
28 |
--------------------------------------------------------------------------------
/tests/testfao56.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | from numpy import pi, array, full, testing
3 | from pandas import date_range, DatetimeIndex, Series
4 | from xarray import DataArray, Dataset
5 | import pyet as et
6 |
7 |
8 | class TestFAO56(unittest.TestCase):
9 | def test_press_calc(self):
10 | # Based on Example 2 and 4, p. 32 FAO.
11 | elevation_s = Series(
12 | data=[1800, 1200, 1462.4], index=date_range(start="2020-1-1", periods=3)
13 | )
14 | calculated_pressures0 = et.calc_press(elevation_s)
15 | expected_pressures = Series(
16 | data=[81.8, 87.9, 85.17], index=date_range(start="2020-1-1", periods=3)
17 | )
18 | testing.assert_allclose(
19 | expected_pressures.round(1), calculated_pressures0.round(1)
20 | )
21 |
22 | # Create a 3D DataArray with dimensions 'x', 'y', and 'time'
23 | elevation_xr = DataArray(
24 | full((2, 3, 3), 1800),
25 | coords=[
26 | ("time", date_range(start="1/1/2020", periods=2)),
27 | ("y", [1, 2, 3]),
28 | ("x", [1, 2, 3]),
29 | ],
30 | )
31 | elevation_xr.loc[{"time": "2020-01-02"}] = 1200
32 | # Apply the et.calc_press function to each element of the DataArray
33 | calculated_pressures = et.calc_press(elevation_xr)
34 | # Create expected results as DataArrays
35 | expected_pressures = DataArray(
36 | full((2, 3, 3), 81.8),
37 | coords=[
38 | ("time", date_range(start="1/1/2020", periods=2)),
39 | ("y", [1, 2, 3]),
40 | ("x", [1, 2, 3]),
41 | ],
42 | )
43 | expected_pressures.loc[{"time": "2020-01-02"}] = 87.9
44 | # Check that the results are as expected
45 | testing.assert_allclose(calculated_pressures.round(1), expected_pressures)
46 |
47 | def test_vpc(self):
48 | # Based on ASCE Table C-3
49 | tmean = [21.65, 22.9, 23.7, 22.8, 24.3, 26.0, 26.1, 26.4, 23.9, 24.2]
50 | tmean_s = Series(tmean, index=date_range(start="2020-1-1", periods=10))
51 | calculated_vpc1 = et.calc_vpc(tmean_s)
52 | vpc1 = [
53 | 0.1585,
54 | 0.1692,
55 | 0.1762,
56 | 0.1684,
57 | 0.1820,
58 | 0.199,
59 | 0.1996,
60 | 0.2027,
61 | 0.1781,
62 | 0.1809,
63 | ]
64 | expected_vpc1 = Series(vpc1, index=date_range(start="2020-1-1", periods=10))
65 | testing.assert_allclose(expected_vpc1.round(2), calculated_vpc1.round(2))
66 |
67 | # Create a 3D DataArray with dimensions 'x', 'y', and 'time'
68 | tmean_xr = DataArray(
69 | full((4, 3, 3), 21.65),
70 | coords=[
71 | ("time", date_range(start="2020-1-1", periods=4)),
72 | ("y", [1, 2, 3]),
73 | ("x", [1, 2, 3]),
74 | ],
75 | )
76 | tmean_xr.loc[{"time": ["2020-01-02", "2020-01-03", "2020-01-04"]}] = [
77 | 22.9,
78 | 23.7,
79 | 22.8,
80 | ]
81 | # Apply the et.calc_press function to each element of the DataArray
82 | calculated_vpc = et.calc_vpc(tmean_xr)
83 | # Create expected results as DataArrays
84 | expected_vpc = DataArray(
85 | full((4, 3, 3), 0.158),
86 | coords=[
87 | ("time", date_range(start="2020-1-1", periods=4)),
88 | ("y", [1, 2, 3]),
89 | ("x", [1, 2, 3]),
90 | ],
91 | )
92 | expected_vpc.loc[{"time": ["2020-01-02", "2020-01-03", "2020-01-04"]}] = [
93 | 0.1692,
94 | 0.1762,
95 | 0.1684,
96 | ]
97 | # Check that the results are as expected
98 | testing.assert_allclose(calculated_vpc.round(3), expected_vpc.round(3))
99 |
100 | def test_psy_calc(self):
101 | # Based on Example 2, p. 32 FAO. and Table C-2 in ASCE(2001)
102 | p0 = Series([81.8, 85.17], index=date_range(start="2020-1-1", periods=2))
103 | calculated_psy = et.calc_psy(p0)
104 | expected_psy = Series(
105 | [0.054, 0.0566], index=date_range(start="2020-1-1", periods=2)
106 | )
107 | testing.assert_allclose(expected_psy.round(3), calculated_psy.round(3))
108 | # Create a 3D DataArray with dimensions 'x', 'y', and 'time'
109 | press_xr = DataArray(
110 | full((2, 3, 3), 81.8),
111 | coords=[
112 | ("time", date_range(start="2020-1-1", periods=2)),
113 | ("y", [1, 2, 3]),
114 | ("x", [1, 2, 3]),
115 | ],
116 | )
117 | press_xr.loc[{"time": "2020-01-02"}] = 85.17
118 | # Apply the et.calc_press function to each element of the DataArray
119 | calculated_psy = et.calc_psy(press_xr)
120 | # Create expected results as DataArrays
121 | expected_psy = DataArray(
122 | full((2, 3, 3), 0.054),
123 | coords=[
124 | ("time", date_range(start="2020-1-1", periods=2)),
125 | ("y", [1, 2, 3]),
126 | ("x", [1, 2, 3]),
127 | ],
128 | )
129 | expected_psy.loc[{"time": "2020-01-02"}] = 0.0566
130 | # Check that the results are as expected
131 | testing.assert_allclose(calculated_psy.round(3), expected_psy.round(3))
132 |
133 | def test_e0_calc(self):
134 | # Based on Example 3 and 4, p. 36 FAO.
135 | tmean0 = Series(
136 | [24.5, 15, 19.75, 19.5], index=date_range(start="2020-1-1", periods=4)
137 | )
138 | calculated_e0 = et.calc_e0(tmean0)
139 | expected_e0 = Series(
140 | [3.075, 1.705, 2.3, 2.267], index=date_range(start="2020-1-1", periods=4)
141 | )
142 | testing.assert_allclose(expected_e0.round(1), calculated_e0.round(1))
143 |
144 | # Create a 3D DataArray with dimensions 'x', 'y', and 'time'
145 | tmean_xr = DataArray(
146 | full((4, 3, 3), 24.5),
147 | coords=[
148 | ("time", date_range(start="2020-1-1", periods=4)),
149 | ("y", [1, 2, 3]),
150 | ("x", [1, 2, 3]),
151 | ],
152 | )
153 | tmean_xr.loc[{"time": "2020-01-02"}] = 15
154 | tmean_xr.loc[{"time": "2020-01-03"}] = 19.75
155 | tmean_xr.loc[{"time": "2020-01-04"}] = 19.5
156 | # Apply the et.calc_press function to each element of the DataArray
157 | calculated_e0 = et.calc_e0(tmean_xr)
158 | # Create expected results as DataArrays
159 | expected_e0 = DataArray(
160 | full((4, 3, 3), 3.075),
161 | coords=[
162 | ("time", date_range(start="2020-1-1", periods=4)),
163 | ("y", [1, 2, 3]),
164 | ("x", [1, 2, 3]),
165 | ],
166 | )
167 | expected_e0.loc[{"time": "2020-01-02"}] = 1.705
168 | expected_e0.loc[{"time": "2020-01-03"}] = 2.3
169 | expected_e0.loc[{"time": "2020-01-04"}] = 2.267
170 | # Check that the results are as expected
171 | testing.assert_allclose(calculated_e0.round(1), expected_e0.round(1))
172 |
173 | def test_es_calc(self):
174 | # Based on Example 3 and 6, p. 36 FAO.
175 | es = et.calc_es(tmax=24.5, tmin=15.0)
176 | self.assertAlmostEqual(es, 2.39, 3)
177 |
178 | def test_ea_calc(self):
179 | # Based on Example 5, p. 39 FAO.
180 | ea1 = et.calc_ea(tmax=25.0, tmin=18.0, rhmax=82.0, rhmin=54.0)
181 | rhmean = (82 + 54) / 2
182 | ea2 = et.calc_ea(tmax=25.0, tmin=18.0, rh=rhmean)
183 | self.assertAlmostEqual(ea1, 1.70, 2)
184 | self.assertAlmostEqual(ea2, 1.78, 2)
185 |
186 | def test_relative_distance(self):
187 | # Based on Example 8, p. 47 FAO.
188 | rd = et.relative_distance(246)
189 | self.assertAlmostEqual(rd, 0.985, 3)
190 |
191 | def test_solar_declination(self):
192 | # Based on Example 8, p. 47 FAO.
193 | sd = et.solar_declination(246)
194 | self.assertAlmostEqual(sd, 0.12, 2)
195 |
196 | def test_sunset_angle(self):
197 | # Based on Example 8, p. 47 FAO.
198 | sangle = et.sunset_angle(0.12, -0.35)
199 | self.assertAlmostEqual(sangle, 1.527, 3)
200 |
201 | def test_day_of_year(self):
202 | # Based on Example 8, p. 47 FAO.
203 | doy = et.day_of_year(DatetimeIndex(["2015-09-03"]))
204 | self.assertAlmostEqual(float(doy.iloc[0]), 246, 1)
205 | # Based on ASCD Table C-3
206 | dindex = date_range("2020-07-01", "2020-07-10")
207 | doy1 = et.day_of_year(dindex).tolist()
208 | doyr = [183, 184, 185, 186, 187, 188, 189, 190, 191, 192]
209 | self.assertEqual(doy1, doyr, 1)
210 |
211 | def test_extraterrestrial_r(self):
212 | # Based on Example 8, p. 47 FAO.
213 | extrar = et.extraterrestrial_r(DatetimeIndex(["2015-09-03"]), -0.35)
214 | self.assertAlmostEqual(float(extrar), 32.2, 1)
215 |
216 | def test_daylight_hours(self):
217 | # Based on Example 9, p. 47 FAO.
218 | dayhours = et.daylight_hours(DatetimeIndex(["2015-09-03"]), -0.35)
219 | self.assertAlmostEqual(float(dayhours), 11.7, 1)
220 |
221 | def test_calc_rad_long(self):
222 | # Based on Example 10, p. 52 FAO.
223 | rs = Series([14.5], index=DatetimeIndex(["2015-05-15"]))
224 | tmax = Series([25.1], index=DatetimeIndex(["2015-05-15"]))
225 | tmin = Series([19], index=DatetimeIndex(["2015-05-15"]))
226 | ea = Series([2.1], index=DatetimeIndex(["2015-05-15"]))
227 | rso = Series([18.8], index=DatetimeIndex(["2015-05-15"]))
228 | rnl = et.calc_rad_long(rs, tmax=tmax, tmin=tmin, ea=ea, rso=rso)
229 | self.assertAlmostEqual(float(rnl), 3.5, 1)
230 |
231 | def test_calc_rad_sol_in(self):
232 | # Based on example 10, p 50 TestFAO56
233 | lat = -22.9 * pi / 180
234 | n = Series(7.1, DatetimeIndex(["2021-5-15"]))
235 | rad_sol_in = et.calc_rad_sol_in(n, lat)
236 | self.assertAlmostEqual(float(rad_sol_in.iloc[0]), 14.5, 1)
237 |
238 | def test_et_fao56(self):
239 | # Based on Example 18, p. 72 FAO.
240 | wind = Series([2.078], index=DatetimeIndex(["2015-07-06"]))
241 | tmax = Series([21.5], index=DatetimeIndex(["2015-07-06"]))
242 | tmin = Series([12.3], index=DatetimeIndex(["2015-07-06"]))
243 | tmean = (tmax + tmin) / 2
244 | rhmax = Series([84], index=DatetimeIndex(["2015-07-06"]))
245 | rhmin = Series([63], index=DatetimeIndex(["2015-07-06"]))
246 | rs = Series([22.07], index=DatetimeIndex(["2015-07-06"]))
247 | n = 9.25
248 | nn = 16.1
249 | elevation = 100
250 | lat = 50.80 * pi / 180
251 | et56 = et.pm_fao56(
252 | tmean,
253 | wind,
254 | elevation=elevation,
255 | lat=lat,
256 | rs=rs,
257 | tmax=tmax,
258 | tmin=tmin,
259 | rhmax=rhmax,
260 | rhmin=rhmin,
261 | n=n,
262 | nn=nn,
263 | )
264 | self.assertAlmostEqual(float(et56.iloc[0]), 3.9, 1)
265 | # Create an xarray Dataset with DataArrays
266 | # Create an xarray Dataset with DataArrays
267 | data = {
268 | "wind": DataArray(wind, dims=("time",)),
269 | "tmax": DataArray(tmax, dims=("time",)),
270 | "tmin": DataArray(tmin, dims=("time",)),
271 | "tmean": DataArray(tmean, dims=("time",)),
272 | "rhmax": DataArray(rhmax, dims=("time",)),
273 | "rhmin": DataArray(rhmin, dims=("time",)),
274 | "rs": DataArray(rs, dims=("time",)),
275 | "et0": DataArray([3.9], dims=("time",)),
276 | }
277 |
278 | # Create the xarray Dataset
279 | dataset = Dataset(data)
280 | # Add additional variables (n, nn, elevation, lat)
281 | dataset["n"] = n
282 | dataset["nn"] = nn
283 | dataset["elevation"] = elevation
284 | dataset["lat"] = lat
285 | et56_xr = et.pm_fao56(
286 | dataset["tmean"],
287 | dataset["wind"],
288 | elevation=dataset["elevation"],
289 | lat=dataset["lat"],
290 | rs=dataset["rs"],
291 | tmax=dataset["tmax"],
292 | tmin=dataset["tmin"],
293 | rhmax=dataset["rhmax"],
294 | rhmin=dataset["rhmin"],
295 | n=dataset["n"],
296 | nn=dataset["nn"],
297 | )
298 |
299 | testing.assert_allclose(et56_xr.round(1), dataset["et0"])
300 |
--------------------------------------------------------------------------------