STIC-JPL 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of STIC-JPL might be problematic. Click here for more details.
- stic_jpl-1.1.0/PKG-INFO +90 -0
- stic_jpl-1.1.0/README.md +64 -0
- stic_jpl-1.1.0/STIC_JPL/STIC_JPL.py +370 -0
- stic_jpl-1.1.0/STIC_JPL/__init__.py +9 -0
- stic_jpl-1.1.0/STIC_JPL/canopy_air_stream.py +37 -0
- stic_jpl-1.1.0/STIC_JPL/closure.py +60 -0
- stic_jpl-1.1.0/STIC_JPL/constants.py +21 -0
- stic_jpl-1.1.0/STIC_JPL/diagnostic.py +70 -0
- stic_jpl-1.1.0/STIC_JPL/initialize_with_solar.py +81 -0
- stic_jpl-1.1.0/STIC_JPL/initialize_without_solar.py +85 -0
- stic_jpl-1.1.0/STIC_JPL/iterate_with_solar.py +144 -0
- stic_jpl-1.1.0/STIC_JPL/iterate_without_solar.py +121 -0
- stic_jpl-1.1.0/STIC_JPL/meteorology_conversion/__init__.py +1 -0
- stic_jpl-1.1.0/STIC_JPL/meteorology_conversion/meteorology_conversion.py +123 -0
- stic_jpl-1.1.0/STIC_JPL/net_radiation.py +38 -0
- stic_jpl-1.1.0/STIC_JPL/root_zone_initialization.py +36 -0
- stic_jpl-1.1.0/STIC_JPL/root_zone_iteration.py +66 -0
- stic_jpl-1.1.0/STIC_JPL/soil_heat_flux/__init__.py +1 -0
- stic_jpl-1.1.0/STIC_JPL/soil_heat_flux/calculate_SEBAL_soil_heat_flux.py +40 -0
- stic_jpl-1.1.0/STIC_JPL/soil_moisture_initialization.py +114 -0
- stic_jpl-1.1.0/STIC_JPL/soil_moisture_iteration.py +131 -0
- stic_jpl-1.1.0/STIC_JPL/timer/__init__.py +1 -0
- stic_jpl-1.1.0/STIC_JPL/timer/timer.py +77 -0
- stic_jpl-1.1.0/STIC_JPL/vegetation_conversion/__init__.py +1 -0
- stic_jpl-1.1.0/STIC_JPL/vegetation_conversion/vegetation_conversion.py +47 -0
- stic_jpl-1.1.0/STIC_JPL/version.txt +1 -0
- stic_jpl-1.1.0/STIC_JPL.egg-info/PKG-INFO +90 -0
- stic_jpl-1.1.0/STIC_JPL.egg-info/SOURCES.txt +33 -0
- stic_jpl-1.1.0/STIC_JPL.egg-info/dependency_links.txt +1 -0
- stic_jpl-1.1.0/STIC_JPL.egg-info/requires.txt +16 -0
- stic_jpl-1.1.0/STIC_JPL.egg-info/top_level.txt +1 -0
- stic_jpl-1.1.0/pyproject.toml +49 -0
- stic_jpl-1.1.0/setup.cfg +4 -0
- stic_jpl-1.1.0/tests/test_import_STIC.py +2 -0
- stic_jpl-1.1.0/tests/test_import_dependencies.py +18 -0
stic_jpl-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: STIC-JPL
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Surface Temperature Initiated Closure (STIC) Evapotranspiration Model Python Implementation
|
|
5
|
+
Author-email: Gregory Halverson <gregory.h.halverson@jpl.nasa.gov>, Kaniska Mallick <kaniska.mallick@list.lu>, Madeleine Pascolini-Campbell <madeleine.a.pascolini-campbell@jpl.nasa.gov>, "Claire S. Villanueva-Weeks" <claire.s.villanueva-weeks@jpl.gov>
|
|
6
|
+
Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/STIC-JPL
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: colored-logging
|
|
12
|
+
Requires-Dist: ECOv002-CMR
|
|
13
|
+
Requires-Dist: ECOv002-granules
|
|
14
|
+
Requires-Dist: GEOS5FP>=1.1.1
|
|
15
|
+
Requires-Dist: numpy
|
|
16
|
+
Requires-Dist: pandas
|
|
17
|
+
Requires-Dist: rasters
|
|
18
|
+
Requires-Dist: solar-apparent-time
|
|
19
|
+
Provides-Extra: dev
|
|
20
|
+
Requires-Dist: build; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
23
|
+
Requires-Dist: jupyter; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest; extra == "dev"
|
|
25
|
+
Requires-Dist: twine; extra == "dev"
|
|
26
|
+
|
|
27
|
+
# Surface Temperature Initiated Closure (STIC) Evapotranspiration Model Python Implementation
|
|
28
|
+
|
|
29
|
+
[](https://github.com/JPL-Evapotranspiration-Algorithms/STIC/actions/workflows/ci.yml)
|
|
30
|
+
|
|
31
|
+
This repository contains the python implementation for the Surface Temperature Initiated Closure (STIC) evapotranspiration model, used by the ECOsystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) and Surface Biology and Geology (SBG) missions.
|
|
32
|
+
|
|
33
|
+
This software package is a Python implementation of the Surface Temperature Initiated Closure (STIC) version 1.3 model designed to implement LST to solve the aerodynamic temperature, which is critical for ET modeling. The original STIC model produced by Kaniska Mallick (Mallick et al. 2015; 2018; 2022) was re-implemented from MATLAB code to Python by Gregory Halverson and Madeleine Pascolini-Campbell. The software was developed under a research grant by the NASA Research Opportunities in Space and Earth Sciences (ROSES) program. It is intended for use by the Hyperspectral Thermal Emission Spectrometer (HyTES), MODIS/ASTER (MASTER) Airborne Simulator, Ecosystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) mission and as a precursor for the Surface Biology and Geology (SBG) mission.
|
|
34
|
+
|
|
35
|
+
The software was developed as part of a research grant by the NASA Research Opportunities in Space and Earth Sciences (ROSES) program. It was designed for use by the Hyperspectral Thermal Emission Spectrometer (HyTES), MODIS/ASTER (MASTER) Airborne Simulator, Ecosystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) mission as a precursor for the Surface Biology and Geology (SBG) mission. It may also be useful for general remote sensing and GIS projects in Python. This package can be utilized for remote sensing research in Jupyter notebooks and deployed for operations in data processing pipelines.
|
|
36
|
+
|
|
37
|
+
The software is being released according to the SPD-41 open-science requirements of NASA-funded ROSES projects.
|
|
38
|
+
|
|
39
|
+
Gregory H. Halverson (they/them)<br>
|
|
40
|
+
[gregory.h.halverson@jpl.nasa.gov](mailto:gregory.h.halverson@jpl.nasa.gov)<br>
|
|
41
|
+
Lead developer<br>
|
|
42
|
+
NASA Jet Propulsion Laboratory 329G
|
|
43
|
+
|
|
44
|
+
Kaniska Mallick (he/him)<br>
|
|
45
|
+
[kaniska.mallick@list.lu](mailto:kaniska.mallick@list.lu)<br>
|
|
46
|
+
Algorithm inventor<br>
|
|
47
|
+
Luxembourg Institute of Science and Technology
|
|
48
|
+
|
|
49
|
+
Tian Hu (he/him)<br>
|
|
50
|
+
[tian.hu@list.lu](mailto:tian.hu@list.lu)<br>
|
|
51
|
+
Algorithm developer<br>
|
|
52
|
+
Luxembourg Institute of Science and Technology
|
|
53
|
+
|
|
54
|
+
Madeleine Pascolini-Campbell (she/her)<br>
|
|
55
|
+
[madeleine.a.pascolini-campbell@jpl.nasa.gov](mailto:madeleine.a.pascolini-campbell@jpl.nasa.gov)<br>
|
|
56
|
+
Algorithm developer<br>
|
|
57
|
+
NASA Jet Propulsion Laboratory 329F
|
|
58
|
+
|
|
59
|
+
Claire Villanueva-Weeks (she/her)<br>
|
|
60
|
+
[claire.s.villanueva-weeks@jpl.nasa.gov](mailto:claire.s.villanueva-weeks@jpl.nasa.gov)<br>
|
|
61
|
+
Code maintenance<br>
|
|
62
|
+
NASA Jet Propulsion Laboratory 329G
|
|
63
|
+
|
|
64
|
+
## Installation
|
|
65
|
+
|
|
66
|
+
Use the pip package manager to install the `STIC` PyPi package.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
pip install STIC
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Usage
|
|
73
|
+
|
|
74
|
+
Import the `STIC` function from the `STIC` package.
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
from STIC import STIC
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
See the [ECOSTRESS example](ECOSTRESS%20Example.ipynb) for usage.
|
|
81
|
+
|
|
82
|
+
## References
|
|
83
|
+
|
|
84
|
+
Mallick, K., Boegh, E., Trebs, I., Alfieri, J. G., Kustas, W. P., Prueger, J. H., ... & Jarvis, A. J. (2015). Reintroducing radiometric surface temperature into the P enman‐M onteith formulation. Water Resources Research, 51(8), 6214-6243. https://doi.org/10.1002/2014WR016106
|
|
85
|
+
|
|
86
|
+
Mallick, K., Toivonen, E., Trebs, I., Boegh, E., Cleverly, J., Eamus, D., ... & Garcia, M. (2018). Bridging Thermal Infrared Sensing and Physically‐Based Evapotranspiration Modeling: From Theoretical Implementation to Validation Across an Aridity Gradient in Australian Ecosystems. Water Resources Research, 54(5), 3409-3435. https://doi.org/10.1029/2017WR021357
|
|
87
|
+
|
|
88
|
+
Mallick, K., Baldocchi, D., Jarvis, A., Hu, T., Trebs, I., Sulis, M., ... & Kustas, W. P. (2022). Insights Into the Aerodynamic Versus Radiometric Surface Temperature Debate in Thermal‐Based Evaporation Modeling. Geophysical Research Letters, 49(15), e2021GL097568. https://doi.org/10.1029/2021GL097568
|
|
89
|
+
|
|
90
|
+
|
stic_jpl-1.1.0/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Surface Temperature Initiated Closure (STIC) Evapotranspiration Model Python Implementation
|
|
2
|
+
|
|
3
|
+
[](https://github.com/JPL-Evapotranspiration-Algorithms/STIC/actions/workflows/ci.yml)
|
|
4
|
+
|
|
5
|
+
This repository contains the python implementation for the Surface Temperature Initiated Closure (STIC) evapotranspiration model, used by the ECOsystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) and Surface Biology and Geology (SBG) missions.
|
|
6
|
+
|
|
7
|
+
This software package is a Python implementation of the Surface Temperature Initiated Closure (STIC) version 1.3 model designed to implement LST to solve the aerodynamic temperature, which is critical for ET modeling. The original STIC model produced by Kaniska Mallick (Mallick et al. 2015; 2018; 2022) was re-implemented from MATLAB code to Python by Gregory Halverson and Madeleine Pascolini-Campbell. The software was developed under a research grant by the NASA Research Opportunities in Space and Earth Sciences (ROSES) program. It is intended for use by the Hyperspectral Thermal Emission Spectrometer (HyTES), MODIS/ASTER (MASTER) Airborne Simulator, Ecosystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) mission and as a precursor for the Surface Biology and Geology (SBG) mission.
|
|
8
|
+
|
|
9
|
+
The software was developed as part of a research grant by the NASA Research Opportunities in Space and Earth Sciences (ROSES) program. It was designed for use by the Hyperspectral Thermal Emission Spectrometer (HyTES), MODIS/ASTER (MASTER) Airborne Simulator, Ecosystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) mission as a precursor for the Surface Biology and Geology (SBG) mission. It may also be useful for general remote sensing and GIS projects in Python. This package can be utilized for remote sensing research in Jupyter notebooks and deployed for operations in data processing pipelines.
|
|
10
|
+
|
|
11
|
+
The software is being released according to the SPD-41 open-science requirements of NASA-funded ROSES projects.
|
|
12
|
+
|
|
13
|
+
Gregory H. Halverson (they/them)<br>
|
|
14
|
+
[gregory.h.halverson@jpl.nasa.gov](mailto:gregory.h.halverson@jpl.nasa.gov)<br>
|
|
15
|
+
Lead developer<br>
|
|
16
|
+
NASA Jet Propulsion Laboratory 329G
|
|
17
|
+
|
|
18
|
+
Kaniska Mallick (he/him)<br>
|
|
19
|
+
[kaniska.mallick@list.lu](mailto:kaniska.mallick@list.lu)<br>
|
|
20
|
+
Algorithm inventor<br>
|
|
21
|
+
Luxembourg Institute of Science and Technology
|
|
22
|
+
|
|
23
|
+
Tian Hu (he/him)<br>
|
|
24
|
+
[tian.hu@list.lu](mailto:tian.hu@list.lu)<br>
|
|
25
|
+
Algorithm developer<br>
|
|
26
|
+
Luxembourg Institute of Science and Technology
|
|
27
|
+
|
|
28
|
+
Madeleine Pascolini-Campbell (she/her)<br>
|
|
29
|
+
[madeleine.a.pascolini-campbell@jpl.nasa.gov](mailto:madeleine.a.pascolini-campbell@jpl.nasa.gov)<br>
|
|
30
|
+
Algorithm developer<br>
|
|
31
|
+
NASA Jet Propulsion Laboratory 329F
|
|
32
|
+
|
|
33
|
+
Claire Villanueva-Weeks (she/her)<br>
|
|
34
|
+
[claire.s.villanueva-weeks@jpl.nasa.gov](mailto:claire.s.villanueva-weeks@jpl.nasa.gov)<br>
|
|
35
|
+
Code maintenance<br>
|
|
36
|
+
NASA Jet Propulsion Laboratory 329G
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
Use the pip package manager to install the `STIC` PyPi package.
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
pip install STIC
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
Import the `STIC` function from the `STIC` package.
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
from STIC import STIC
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
See the [ECOSTRESS example](ECOSTRESS%20Example.ipynb) for usage.
|
|
55
|
+
|
|
56
|
+
## References
|
|
57
|
+
|
|
58
|
+
Mallick, K., Boegh, E., Trebs, I., Alfieri, J. G., Kustas, W. P., Prueger, J. H., ... & Jarvis, A. J. (2015). Reintroducing radiometric surface temperature into the P enman‐M onteith formulation. Water Resources Research, 51(8), 6214-6243. https://doi.org/10.1002/2014WR016106
|
|
59
|
+
|
|
60
|
+
Mallick, K., Toivonen, E., Trebs, I., Boegh, E., Cleverly, J., Eamus, D., ... & Garcia, M. (2018). Bridging Thermal Infrared Sensing and Physically‐Based Evapotranspiration Modeling: From Theoretical Implementation to Validation Across an Aridity Gradient in Australian Ecosystems. Water Resources Research, 54(5), 3409-3435. https://doi.org/10.1029/2017WR021357
|
|
61
|
+
|
|
62
|
+
Mallick, K., Baldocchi, D., Jarvis, A., Hu, T., Trebs, I., Sulis, M., ... & Kustas, W. P. (2022). Insights Into the Aerodynamic Versus Radiometric Surface Temperature Debate in Thermal‐Based Evaporation Modeling. Geophysical Research Letters, 49(15), e2021GL097568. https://doi.org/10.1029/2021GL097568
|
|
63
|
+
|
|
64
|
+
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
from typing import Union, Callable
|
|
2
|
+
import logging
|
|
3
|
+
from datetime import datetime, timedelta
|
|
4
|
+
from os.path import join, abspath, expanduser
|
|
5
|
+
from typing import Dict, List
|
|
6
|
+
import numpy as np
|
|
7
|
+
import warnings
|
|
8
|
+
from .diagnostic import diagnostic
|
|
9
|
+
import colored_logging as cl
|
|
10
|
+
from .meteorology_conversion import calculate_air_density, calculate_specific_heat, calculate_specific_humidity, calculate_surface_pressure, celcius_to_kelvin
|
|
11
|
+
import rasters as rt
|
|
12
|
+
from GEOS5FP import GEOS5FP
|
|
13
|
+
from solar_apparent_time import solar_day_of_year_for_area, solar_hour_of_day_for_area
|
|
14
|
+
|
|
15
|
+
from .timer import Timer
|
|
16
|
+
|
|
17
|
+
from rasters import Raster, RasterGeometry
|
|
18
|
+
|
|
19
|
+
from .vegetation_conversion.vegetation_conversion import FVC_from_NDVI, LAI_from_NDVI
|
|
20
|
+
|
|
21
|
+
from .constants import *
|
|
22
|
+
from .closure import STIC_closure
|
|
23
|
+
from .soil_moisture_initialization import initialize_soil_moisture
|
|
24
|
+
from .soil_moisture_iteration import iterate_soil_moisture
|
|
25
|
+
from .net_radiation import calculate_net_longwave_radiation
|
|
26
|
+
from .initialize_with_solar import initialize_with_solar
|
|
27
|
+
from .canopy_air_stream import calculate_canopy_air_stream_vapor_pressure
|
|
28
|
+
from .initialize_without_solar import initialize_without_solar
|
|
29
|
+
from .iterate_with_solar import iterate_with_solar
|
|
30
|
+
from .iterate_without_solar import iterate_without_solar
|
|
31
|
+
from .root_zone_initialization import calculate_root_zone_moisture
|
|
32
|
+
|
|
33
|
+
from .soil_heat_flux import calculate_SEBAL_soil_heat_flux
|
|
34
|
+
|
|
35
|
+
__author__ = 'Kaniska Mallick, Madeleine Pascolini-Campbell, Gregory Halverson'
|
|
36
|
+
|
|
37
|
+
logger = logging.getLogger(__name__)
|
|
38
|
+
|
|
39
|
+
def STIC_JPL(
|
|
40
|
+
ST_C: Union[Raster, np.ndarray],
|
|
41
|
+
emissivity: Union[Raster, np.ndarray],
|
|
42
|
+
NDVI: Union[Raster, np.ndarray],
|
|
43
|
+
albedo: Union[Raster, np.ndarray],
|
|
44
|
+
Rn_Wm2: Union[Raster, np.ndarray],
|
|
45
|
+
geometry: RasterGeometry = None,
|
|
46
|
+
time_UTC: datetime = None,
|
|
47
|
+
hour_of_day: np.ndarray = None,
|
|
48
|
+
day_of_year: np.ndarray = None,
|
|
49
|
+
GEOS5FP_connection: GEOS5FP = None,
|
|
50
|
+
Ta_C: Union[Raster, np.ndarray] = None,
|
|
51
|
+
RH: Union[Raster, np.ndarray] = None,
|
|
52
|
+
G: Union[Raster, np.ndarray] = None,
|
|
53
|
+
G_method: str = DEFAULT_G_METHOD,
|
|
54
|
+
SM: Union[Raster, np.ndarray] = None,
|
|
55
|
+
Rg_Wm2: Union[Raster, np.ndarray] = None,
|
|
56
|
+
FVC: Union[Raster, np.ndarray] = None,
|
|
57
|
+
LAI: Union[Raster, np.ndarray] = None,
|
|
58
|
+
elevation_m: Union[Raster, np.ndarray] = None,
|
|
59
|
+
delta_hPa: Union[Raster, np.ndarray] = None,
|
|
60
|
+
gamma_hPa: Union[Raster, np.ndarray, float] = GAMMA_HPA,
|
|
61
|
+
rho_kgm3: Union[Raster, np.ndarray] = RHO_KGM3,
|
|
62
|
+
Cp_Jkg: Union[Raster, np.ndarray] = CP_JKG,
|
|
63
|
+
alpha: float = PT_ALPHA,
|
|
64
|
+
LE_convergence_target: float = LE_CONVERGENCE_TARGET_WM2,
|
|
65
|
+
max_iterations: int = MAX_ITERATIONS,
|
|
66
|
+
diagnostic_directory: str = None,
|
|
67
|
+
show_distributions: bool = SHOW_DISTRIBUTIONS,
|
|
68
|
+
use_variable_alpha: bool = USE_VARIABLE_ALPHA) -> Dict[str, Union[Raster, np.ndarray]]:
|
|
69
|
+
results = {}
|
|
70
|
+
|
|
71
|
+
if geometry is None and isinstance(ST_C, Raster):
|
|
72
|
+
geometry = ST_C.geometry
|
|
73
|
+
|
|
74
|
+
if GEOS5FP_connection is None:
|
|
75
|
+
GEOS5FP_connection = GEOS5FP()
|
|
76
|
+
|
|
77
|
+
if (day_of_year is None or hour_of_day is None) and time_UTC is not None and geometry is not None:
|
|
78
|
+
day_of_year = solar_day_of_year_for_area(time_UTC=time_UTC, geometry=geometry)
|
|
79
|
+
hour_of_day = solar_hour_of_day_for_area(time_UTC=time_UTC, geometry=geometry)
|
|
80
|
+
|
|
81
|
+
if time_UTC is None and day_of_year is None and hour_of_day is None:
|
|
82
|
+
raise ValueError("no time given between time_UTC, day_of_year, and hour_of_day")
|
|
83
|
+
|
|
84
|
+
diag_kwargs = {
|
|
85
|
+
"show_distributions": show_distributions,
|
|
86
|
+
"output_directory": diagnostic_directory
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
seconds_of_day = hour_of_day * 3600.0
|
|
90
|
+
|
|
91
|
+
# load air temperature in Celsius if not provided
|
|
92
|
+
if Ta_C is None:
|
|
93
|
+
Ta_C = GEOS5FP_connection.Ta_C(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
|
|
94
|
+
|
|
95
|
+
# load relative humidity if not provided
|
|
96
|
+
if RH is None:
|
|
97
|
+
RH = GEOS5FP_connection.RH(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
|
|
98
|
+
|
|
99
|
+
# calculate fraction of vegetation cover if it's not given
|
|
100
|
+
if FVC is None:
|
|
101
|
+
FVC = FVC_from_NDVI(NDVI)
|
|
102
|
+
|
|
103
|
+
# calculate leaf area index if it's not given
|
|
104
|
+
if LAI is None:
|
|
105
|
+
LAI = LAI_from_NDVI(NDVI)
|
|
106
|
+
|
|
107
|
+
# saturation air pressure in hPa
|
|
108
|
+
SVP_hPa = 6.13753 * (np.exp((17.27 * Ta_C) / (Ta_C + 237.3)))
|
|
109
|
+
|
|
110
|
+
# calculate delta term if it's not given
|
|
111
|
+
if delta_hPa is None:
|
|
112
|
+
# slope of saturation vapor pressure to air temperature (hpa/K)
|
|
113
|
+
delta_hPa = 4098 * SVP_hPa / (Ta_C + 237.3) ** 2
|
|
114
|
+
|
|
115
|
+
Ta_K = celcius_to_kelvin(Ta_C)
|
|
116
|
+
|
|
117
|
+
# actual vapor pressure at TA (hpa/K)
|
|
118
|
+
Ea_hPa = SVP_hPa * (RH)
|
|
119
|
+
Ea_Pa = Ea_hPa * 100.0
|
|
120
|
+
|
|
121
|
+
# vapor pressure deficit (hPa)
|
|
122
|
+
VPD_hPa = SVP_hPa - Ea_hPa
|
|
123
|
+
|
|
124
|
+
# swapping in the dew-point calculation from PT-JPL
|
|
125
|
+
Td_C = Ta_C - ((100 - RH * 100) / 5.0)
|
|
126
|
+
|
|
127
|
+
# difference between surface and air temperature (Celsius)
|
|
128
|
+
dTS_C = ST_C - Ta_C
|
|
129
|
+
|
|
130
|
+
# saturation vapor pressure at surface temperature (hPa/K)
|
|
131
|
+
Estar_hPa = 6.13753 * np.exp((17.27 * ST_C) / (ST_C + 237.3))
|
|
132
|
+
|
|
133
|
+
if Rg_Wm2 is None:
|
|
134
|
+
# if G is None and SM is None:
|
|
135
|
+
# raise ValueError("soil heat flux or soil moisture prior required if solar radiation is not given")
|
|
136
|
+
|
|
137
|
+
if G is None:
|
|
138
|
+
G = calculate_SEBAL_soil_heat_flux(
|
|
139
|
+
ST_C=ST_C,
|
|
140
|
+
NDVI=NDVI,
|
|
141
|
+
albedo=albedo,
|
|
142
|
+
Rn=Rn_Wm2,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
phi_Wm2 = Rn_Wm2 - G
|
|
146
|
+
|
|
147
|
+
# initialize without solar radiation
|
|
148
|
+
SM, SMrz, s1, s3, s33, s44, Ms, Tsd_C, Es_hPa, Ds = initialize_without_solar(
|
|
149
|
+
ST_C = ST_C, # Surface temperature in Celsius
|
|
150
|
+
Ta_C = Ta_C, # Air temperature in Celsius
|
|
151
|
+
dTS = dTS_C, # Temperature difference between surface and air in Celsius
|
|
152
|
+
Td_C = Td_C, # Dewpoint temperature in Celsius
|
|
153
|
+
Ea_hPa = Ea_hPa, # Actual vapor pressure in hPa
|
|
154
|
+
Estar_hPa = Estar_hPa, # Saturation vapor pressure at surface temperature (hPa/K)
|
|
155
|
+
SVP_hPa = SVP_hPa, # Saturation vapor pressure at the surface in hPa
|
|
156
|
+
delta_hPa = delta_hPa, # Slope of the saturation vapor pressure-temperature curve in hPa/K
|
|
157
|
+
phi_Wm2 = phi_Wm2, # Available energy in W/m2
|
|
158
|
+
gamma_hPa = gamma_hPa, # Psychrometric constant in hPa/°C
|
|
159
|
+
alpha = alpha # Priestley-Taylor alpha
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
SM, SMrz, Ms, s1, s3, Ep_PT, Rnsoil, LWnet_Wm2, G, Tsd_C, Ds, Es_hPa, phi_Wm2 = initialize_with_solar(
|
|
163
|
+
seconds_of_day = seconds_of_day, # time of day in seconds since midnight
|
|
164
|
+
Rg_Wm2 = Rg_Wm2, # solar radiation (W/m^2)
|
|
165
|
+
Rn_Wm2 = Rn_Wm2, # net radiation (W/m^2)
|
|
166
|
+
ST_C = ST_C, # surface temperature (Celsius)
|
|
167
|
+
emissivity = emissivity, # emissivity of the surface
|
|
168
|
+
Ta_C = Ta_C, # air temperature (Celsius)
|
|
169
|
+
dTS_C = dTS_C, # surface air temperature difference (Celsius)
|
|
170
|
+
Td_C = Td_C, # dew point temperature (Celsius)
|
|
171
|
+
VPD_hPa = VPD_hPa, # vapor pressure deficit (hPa)
|
|
172
|
+
SVP_hPa = SVP_hPa, # saturation vapor pressure at given air temperature (hPa)
|
|
173
|
+
Ea_hPa = Ea_hPa, # actual vapor pressure at air temperature (hPa)
|
|
174
|
+
Estar_hPa = Estar_hPa, # saturation vapor pressure at surface temperature (hPa)
|
|
175
|
+
delta_hPa = delta_hPa, # slope of saturation vapor pressure to air temperature (hpa/K)
|
|
176
|
+
NDVI=NDVI, # normalized difference vegetation index
|
|
177
|
+
FVC = FVC, # fractional vegetation cover
|
|
178
|
+
LAI = LAI, # leaf area index
|
|
179
|
+
albedo = albedo, # albedo of the surface
|
|
180
|
+
gamma_hPa=gamma_hPa, # psychrometric constant (hPa/°C)
|
|
181
|
+
G_method = DEFAULT_G_METHOD, # method for calculating soil heat flux
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
diagnostic(Ms, "Ms", **diag_kwargs)
|
|
185
|
+
|
|
186
|
+
# STIC analytical equations (convergence on LE)
|
|
187
|
+
gB_ms, gS_ms, dT_C, EF = STIC_closure(
|
|
188
|
+
delta_hPa=delta_hPa,
|
|
189
|
+
phi_Wm2=phi_Wm2,
|
|
190
|
+
Es_hPa=Es_hPa,
|
|
191
|
+
Ea_hPa=Ea_hPa,
|
|
192
|
+
Estar_hPa=Estar_hPa,
|
|
193
|
+
SM=SM,
|
|
194
|
+
gamma_hPa=gamma_hPa,
|
|
195
|
+
rho_kgm3=rho_kgm3,
|
|
196
|
+
Cp_Jkg=Cp_Jkg,
|
|
197
|
+
alpha=alpha
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
gBB = gB_ms
|
|
201
|
+
gSS = gS_ms
|
|
202
|
+
gBB_by_gSS = rt.where(gSS == 0, 0, gBB / gSS)
|
|
203
|
+
gB_by_gS = rt.where(gS_ms == 0, 0, gB_ms / gS_ms)
|
|
204
|
+
dT_C = dT_C
|
|
205
|
+
T0_C = dT_C + Ta_C
|
|
206
|
+
|
|
207
|
+
PET_Wm2 = ((delta_hPa * phi_Wm2 + rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa)) # Penman potential evaporation
|
|
208
|
+
|
|
209
|
+
gR = (4 * SB_SIGMA * (Ta_C + 273) ** 3 * emissivity) / (rho_kgm3 * Cp_Jkg)
|
|
210
|
+
omega = ((delta_hPa / gamma_hPa) + 1) / ((delta_hPa / gamma_hPa) + 1 + gB_by_gS)
|
|
211
|
+
LE_eq = (phi_Wm2 * (delta_hPa / gamma_hPa)) / ((delta_hPa / gamma_hPa) + 1)
|
|
212
|
+
LE_imp = (Cp_Jkg * 0.0289644 / gamma_hPa) * gS_ms * 40 * VPD_hPa
|
|
213
|
+
LE_init = omega * LE_eq + (1 - omega) * LE_imp
|
|
214
|
+
dry = (Ds > VPD_hPa) & (PET_Wm2 > phi_Wm2) & (dTS_C > 0) & (Td_C <= 0)
|
|
215
|
+
omega = rt.where(dry,
|
|
216
|
+
((delta_hPa / gamma_hPa) + 1 + gR / gB_ms) / ((delta_hPa / gamma_hPa) + 1 + gB_ms / gS_ms + gR / gS_ms + gR / gB_ms),
|
|
217
|
+
omega)
|
|
218
|
+
LE_eq = rt.where(dry, (phi_Wm2 * (delta_hPa / gamma_hPa)) / ((delta_hPa / gamma_hPa) + 1 + gR / gB_ms), LE_eq)
|
|
219
|
+
LE_init = rt.where(dry, omega * LE_eq + (1 - omega * LE_imp), LE_init)
|
|
220
|
+
|
|
221
|
+
# sensible heat flux
|
|
222
|
+
H_Wm2 = ((gamma_hPa * phi_Wm2 * (1 + gB_by_gS) - rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa * (1 + (gB_by_gS))))
|
|
223
|
+
|
|
224
|
+
LE_Wm2_new = LE_init
|
|
225
|
+
LE_Wm2_change = LE_convergence_target
|
|
226
|
+
LE_Wm2_old = LE_Wm2_new
|
|
227
|
+
LE_transpiration_Wm2 = None
|
|
228
|
+
PT_Wm2 = None
|
|
229
|
+
iteration = 1
|
|
230
|
+
LE_Wm2_max_change = 0
|
|
231
|
+
t = Timer()
|
|
232
|
+
|
|
233
|
+
while (np.nanmax(LE_Wm2_change) >= LE_convergence_target and iteration <= max_iterations):
|
|
234
|
+
logger.info(f"running STIC iteration {cl.val(iteration)} / {cl.val(max_iterations)}")
|
|
235
|
+
|
|
236
|
+
if Rg_Wm2 is None:
|
|
237
|
+
SM, SMrz, Ms, s1, e0, e0star, Tsd_C, D0, alphaN = iterate_without_solar(
|
|
238
|
+
LE = LE_Wm2_new, # Latent heat flux (W/m^2)
|
|
239
|
+
PET = PET_Wm2, # Potential evapotranspiration (W/m^2)
|
|
240
|
+
SM = SM,
|
|
241
|
+
ST_C = ST_C, # Surface temperature (°C)
|
|
242
|
+
Ta_C = Ta_C, # Air temperature (°C)
|
|
243
|
+
dTS = dTS_C, # Surface-air temperature difference (°C)
|
|
244
|
+
T0 = T0_C, # Reference temperature (°C)
|
|
245
|
+
gB = gB_ms, # Boundary layer conductance (m/s)
|
|
246
|
+
gS = gS_ms, # Stomatal conductance (m/s)
|
|
247
|
+
Ea_hPa = Ea_hPa, # Actual vapor pressure (hPa)
|
|
248
|
+
Td_C = Td_C, # Dew point temperature (°C)
|
|
249
|
+
VPD_hPa = VPD_hPa, # Vapor pressure deficit (hPa)
|
|
250
|
+
Estar = Estar_hPa, # Saturation vapor pressure at surface temperature (hPa)
|
|
251
|
+
delta = delta_hPa, # Slope of the saturation vapor pressure-temperature curve (hPa/°C)
|
|
252
|
+
phi = phi_Wm2, # available energy (W/m^2)
|
|
253
|
+
Ds = Ds, # Vapor pressure deficit at source (hPa)
|
|
254
|
+
Es = Es_hPa, # Saturation vapor pressure (hPa)
|
|
255
|
+
s3 = s3, # Slope of the saturation vapor pressure and temperature
|
|
256
|
+
s4 = s44, # Slope of the saturation vapor pressure and temperature
|
|
257
|
+
gB_by_gS = gB_by_gS, # Ratio of boundary layer conductance to stomatal conductance
|
|
258
|
+
gamma_hPa = gamma_hPa, # Psychrometric constant (hPa/°C)
|
|
259
|
+
rho_kgm3 = rho_kgm3, # Air density (kg/m^3)
|
|
260
|
+
Cp_Jkg = Cp_Jkg # Specific heat at constant pressure (J/kg/K)
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
SM, G, e0, e0star, D0, alphaN = iterate_with_solar(
|
|
264
|
+
seconds_of_day = seconds_of_day, # Seconds of the day
|
|
265
|
+
ST_C = ST_C, # Soil temperature (°C)
|
|
266
|
+
NDVI = NDVI, # Normalized Difference Vegetation Index
|
|
267
|
+
albedo = albedo, # Albedo
|
|
268
|
+
gB_ms = gB_ms, # boundary layer conductance (m/s)
|
|
269
|
+
gS_ms = gS_ms, # stomatal conductance (m/s)
|
|
270
|
+
LE_Wm2 = LE_Wm2_new, # latent heat flux (W/m^2)
|
|
271
|
+
Rg_Wm2 = Rg_Wm2, # Incoming solar radiation (W/m^2)
|
|
272
|
+
Rn_Wm2 = Rn_Wm2, # Net radiation (W/m^2)
|
|
273
|
+
LWnet_Wm2 = LWnet_Wm2, # Net longwave radiation (W/m^2)
|
|
274
|
+
Ta_C = Ta_C, # Air temperature (°C)
|
|
275
|
+
dTS_C = dTS_C, # Change in soil temperature (°C)
|
|
276
|
+
Td_C = Td_C, # Dew point temperature (°C)
|
|
277
|
+
Tsd_C = Tsd_C, # Soil dew point temperature (°C)
|
|
278
|
+
Ea_hPa = Ea_hPa, # actual vapor pressure (hPa)
|
|
279
|
+
Estar_hPa = Estar_hPa, # saturation vapor pressure at surface temperature (hPa)
|
|
280
|
+
VPD_hPa = VPD_hPa, # Vapor pressure deficit (hPa)
|
|
281
|
+
SVP_hPa = SVP_hPa, # Saturation vapor pressure (hPa)
|
|
282
|
+
delta_hPa = delta_hPa, # Slope of the saturation vapor pressure-temperature curve (hPa/°C)
|
|
283
|
+
phi_Wm2 = phi_Wm2, # Net radiation minus soil heat flux (W/m^2)
|
|
284
|
+
Es_hPa = Es_hPa, # Saturation vapor pressure (hPa)
|
|
285
|
+
s1 = s1, # Soil moisture parameter
|
|
286
|
+
s3 = s3, # Soil moisture parameter
|
|
287
|
+
FVC = FVC, # Fractional canopy cover
|
|
288
|
+
T0_C = T0_C, # Reference temperature (°C)
|
|
289
|
+
gB_by_gS = gB_by_gS, # Ratio of boundary layer conductance to stomatal conductance
|
|
290
|
+
gamma_hPa = gamma_hPa, # Psychrometric constant (hPa/°C)
|
|
291
|
+
rho_kgm3 = rho_kgm3, # Air density (kg/m^3)
|
|
292
|
+
Cp_Jkg = Cp_Jkg, # Specific heat at constant pressure (J/kg/K)
|
|
293
|
+
G_method = "santanello" # Method for calculating soil heat flux
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
if use_variable_alpha:
|
|
297
|
+
alpha = alphaN
|
|
298
|
+
logger.info(f"using variable Priestley-Taylor alpha with mean: {cl.val(np.round(np.nanmean(alpha), 3))}")
|
|
299
|
+
|
|
300
|
+
# re-estimated conductances and states
|
|
301
|
+
gB_ms, gS_ms, dT_C, EF = STIC_closure(
|
|
302
|
+
delta_hPa=delta_hPa, # Slope of the saturation vapor pressure-temperature curve (hPa/°C)
|
|
303
|
+
phi_Wm2=phi_Wm2, # available energy (W/m^2)
|
|
304
|
+
Es_hPa=Es_hPa, # Vapor pressure at the reference height (hPa)
|
|
305
|
+
Ea_hPa=Ea_hPa, # Actual vapor pressure (hPa)
|
|
306
|
+
Estar_hPa=Estar_hPa, # Saturation vapor pressure at the reference height (hPa)
|
|
307
|
+
SM=SM, # Soil moisture (m³/m³)
|
|
308
|
+
gamma_hPa=gamma_hPa, # Psychrometric constant (hPa/°C)
|
|
309
|
+
rho_kgm3=rho_kgm3, # Air density (kg/m³)
|
|
310
|
+
Cp_Jkg=Cp_Jkg, # Specific heat capacity of air (J/kg/°C)
|
|
311
|
+
alpha=alpha # Stability correction factor for conductance
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
gB_by_gS = rt.where(gS_ms == 0, 0, gB_ms / gS_ms)
|
|
315
|
+
T0_C = dT_C + Ta_C
|
|
316
|
+
# latent heat flux
|
|
317
|
+
LE_Wm2_new = ((delta_hPa * phi_Wm2 + rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa * (1 + gB_by_gS)))
|
|
318
|
+
LE_Wm2_new = rt.where(LE_Wm2_new > phi_Wm2, phi_Wm2, LE_Wm2_new)
|
|
319
|
+
# Sensible Heat Flux
|
|
320
|
+
H_Wm2 = ((gamma_hPa * phi_Wm2 * (1 + gB_by_gS) - rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa * (1 + (gB_by_gS))))
|
|
321
|
+
# potential evaporation (Penman)
|
|
322
|
+
PET_Wm2 = ((delta_hPa * phi_Wm2 + rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa))
|
|
323
|
+
# Potential Transpiration
|
|
324
|
+
PT_Wm2 = (delta_hPa * phi_Wm2 + rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa * (1 + SM * gB_by_gS)) # potential transpiration
|
|
325
|
+
# ET PARTITIONING
|
|
326
|
+
LE_soil_Wm2 = rt.clip(SM * PET_Wm2, 0, None)
|
|
327
|
+
LE_transpiration_Wm2 = rt.clip(LE_Wm2_new - LE_soil_Wm2, 0, None)
|
|
328
|
+
# change in latent heat flux estimate
|
|
329
|
+
LE_Wm2_change = np.abs(LE_Wm2_old - LE_Wm2_new)
|
|
330
|
+
LE_Wm2_new = rt.where(np.isnan(LE_Wm2_new), LE_Wm2_old, LE_Wm2_new)
|
|
331
|
+
LE_Wm2_old = LE_Wm2_new
|
|
332
|
+
LE_Wm2_max_change = np.nanmax(LE_Wm2_change)
|
|
333
|
+
logger.info(
|
|
334
|
+
f"completed STIC iteration {cl.val(iteration)} / {cl.val(max_iterations)} with max LE change: {cl.val(np.round(LE_Wm2_max_change, 3))} ({t} seconds)")
|
|
335
|
+
|
|
336
|
+
diagnostic(SM, f"SM_{iteration}", **diag_kwargs)
|
|
337
|
+
diagnostic(G, f"G_{iteration}", **diag_kwargs)
|
|
338
|
+
diagnostic(LE_Wm2_new, f"LE_{iteration}", **diag_kwargs)
|
|
339
|
+
|
|
340
|
+
if LE_Wm2_max_change <= LE_convergence_target:
|
|
341
|
+
logger.info(f"max LE change {cl.val(np.round(LE_Wm2_max_change, 3))} within convergence target {cl.val(np.round(LE_convergence_target, 3))} with {cl.val(iteration)} iteration{'s' if iteration > 1 else ''}")
|
|
342
|
+
|
|
343
|
+
iteration += 1
|
|
344
|
+
|
|
345
|
+
iteration -= 1
|
|
346
|
+
results["LE_max_change"] = LE_Wm2_max_change
|
|
347
|
+
results["iteration"] = iteration
|
|
348
|
+
|
|
349
|
+
LE = LE_Wm2_new
|
|
350
|
+
|
|
351
|
+
results["LE"] = LE
|
|
352
|
+
results["LE_change"] = LE_Wm2_change
|
|
353
|
+
results["LEt"] = LE_transpiration_Wm2
|
|
354
|
+
results["PT"] = PT_Wm2
|
|
355
|
+
results["PET"] = PET_Wm2
|
|
356
|
+
results["G"] = G
|
|
357
|
+
|
|
358
|
+
if isinstance(geometry, RasterGeometry):
|
|
359
|
+
for name, array in results.items():
|
|
360
|
+
try:
|
|
361
|
+
results[name] = Raster(array.reshape(geometry.shape), geometry=geometry)
|
|
362
|
+
except Exception as e:
|
|
363
|
+
pass
|
|
364
|
+
|
|
365
|
+
results["LE"].cmap = ET_COLORMAP
|
|
366
|
+
results["PET"].cmap = ET_COLORMAP
|
|
367
|
+
|
|
368
|
+
warnings.resetwarnings()
|
|
369
|
+
|
|
370
|
+
return results
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
import rasters as rt
|
|
5
|
+
|
|
6
|
+
from rasters import Raster
|
|
7
|
+
|
|
8
|
+
from .constants import *
|
|
9
|
+
|
|
10
|
+
def calculate_canopy_air_stream_vapor_pressure(
|
|
11
|
+
LE: Union[Raster, np.ndarray], # Latent heat flux [W/m^2]
|
|
12
|
+
Ea_hPa: Union[Raster, np.ndarray], # Actual vapor pressure [hPa]
|
|
13
|
+
Estar: Union[Raster, np.ndarray], # Saturation vapor pressure [hPa]
|
|
14
|
+
gB: Union[Raster, np.ndarray], # Conductance of boundary layer [mol/m^2/s]
|
|
15
|
+
gS: Union[Raster, np.ndarray], # Conductance of stomata [mol/m^2/s]
|
|
16
|
+
rho_kgm3: Union[Raster, np.ndarray, float] = RHO_KGM3, # Air density (kg/m^3)
|
|
17
|
+
Cp_Jkg: Union[Raster, np.ndarray, float] = CP_JKG, # Specific heat at constant pressure (J/kg/K)
|
|
18
|
+
gamma_hPa: Union[Raster, np.ndarray, float] = GAMMA_HPA, # Psychrometric constant (hPa/°C)
|
|
19
|
+
) -> Union[Raster, np.ndarray]:
|
|
20
|
+
"""
|
|
21
|
+
Calculate the canopy air stream vapor pressure.
|
|
22
|
+
|
|
23
|
+
Parameters:
|
|
24
|
+
LE (Union[Raster, np.ndarray]): Latent heat flux [W/m^2]
|
|
25
|
+
Ea_hPa (Union[Raster, np.ndarray]): Actual vapor pressure [hPa]
|
|
26
|
+
Estar (Union[Raster, np.ndarray]): Saturation vapor pressure [hPa]
|
|
27
|
+
gB (Union[Raster, np.ndarray]): Conductance of boundary layer [mol/m^2/s]
|
|
28
|
+
gS (Union[Raster, np.ndarray]): Conductance of stomata [mol/m^2/s]
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Union[Raster, np.ndarray]: The calculated canopy air stream vapor pressure [hPa]
|
|
32
|
+
"""
|
|
33
|
+
e0star = Ea_hPa + (gamma_hPa * LE * (gB + gS)) / (rho_kgm3 * Cp_Jkg * gB * gS)
|
|
34
|
+
e0star = rt.where(e0star < 0, Estar, e0star)
|
|
35
|
+
e0star = rt.where(e0star > 250, Estar, e0star)
|
|
36
|
+
|
|
37
|
+
return e0star
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from typing import Union, Tuple
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
import rasters as rt
|
|
5
|
+
|
|
6
|
+
from rasters import Raster
|
|
7
|
+
|
|
8
|
+
from .constants import *
|
|
9
|
+
|
|
10
|
+
def STIC_closure(
|
|
11
|
+
delta_hPa: Union[Raster, np.ndarray], # Slope of the saturation vapor pressure-temperature curve (hPa/°C)
|
|
12
|
+
phi_Wm2: Union[Raster, np.ndarray], # available energy (W/m^2)
|
|
13
|
+
Es_hPa: Union[Raster, np.ndarray], # Vapor pressure at the reference height (hPa)
|
|
14
|
+
Ea_hPa: Union[Raster, np.ndarray], # Actual vapor pressure (hPa)
|
|
15
|
+
Estar_hPa: Union[Raster, np.ndarray], # Saturation vapor pressure at the reference height (hPa)
|
|
16
|
+
SM: Union[Raster, np.ndarray], # Soil moisture (m³/m³)
|
|
17
|
+
gamma_hPa: float = GAMMA_HPA, # Psychrometric constant (hPa/°C)
|
|
18
|
+
rho_kgm3: float = RHO_KGM3, # Air density (kg/m³)
|
|
19
|
+
Cp_Jkg: float = CP_JKG, # Specific heat capacity of air (J/kg/°C)
|
|
20
|
+
alpha: float = PT_ALPHA # Priestley-Taylor alpha
|
|
21
|
+
) -> Tuple[Union[Raster, np.ndarray]]:
|
|
22
|
+
"""
|
|
23
|
+
STIC closure equations with modified Priestley Taylor and Penman Monteith
|
|
24
|
+
(Mallick et al., 2015, Water Resources research)
|
|
25
|
+
|
|
26
|
+
Parameters:
|
|
27
|
+
delta (np.ndarray): Slope of the saturation vapor pressure-temperature curve (hPa/°C)
|
|
28
|
+
phi (np.ndarray): available energy (W/m^2)
|
|
29
|
+
Es (np.ndarray): Vapor pressure at the reference height (hPa)
|
|
30
|
+
Ea (np.ndarray): Actual vapor pressure (hPa)
|
|
31
|
+
Estar (np.ndarray): Saturation vapor pressure at the reference height (hPa)
|
|
32
|
+
SM (np.ndarray): Soil moisture (m³/m³)
|
|
33
|
+
rho (float, optional): Air density (kg/m³). Defaults to RHO.
|
|
34
|
+
cp (float, optional): Specific heat capacity of air (J/kg/°C). Defaults to CP.
|
|
35
|
+
gamma (float, optional): Psychrometric constant (hPa/°C). Defaults to GAMMA.
|
|
36
|
+
alpha (float, optional): Stability correction factor for conductance. Defaults to ALPHA.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
gB (np.ndarray): boundary layer conductance (m s^-1)
|
|
40
|
+
gS (np.ndarray): stomatal conductance (m s^-1)
|
|
41
|
+
dT (np.ndarray): difference between surface and air temperature
|
|
42
|
+
EF (np.ndarray): evaporative fraction
|
|
43
|
+
"""
|
|
44
|
+
# boundary layer conductance (m s^-1)
|
|
45
|
+
gB = ((2 * phi_Wm2 * alpha * delta_hPa * gamma_hPa) / (2 * Cp_Jkg * delta_hPa * Es_hPa * rho_kgm3 - 2 * Cp_Jkg * delta_hPa * Ea_hPa * rho_kgm3 - 2 * Cp_Jkg * Ea_hPa * gamma_hPa * rho_kgm3 + Cp_Jkg * Es_hPa * gamma_hPa * rho_kgm3 + Cp_Jkg * Estar_hPa * gamma_hPa * rho_kgm3 - Cp_Jkg * SM * Es_hPa * gamma_hPa * rho_kgm3 + Cp_Jkg * SM * Estar_hPa * gamma_hPa * rho_kgm3))
|
|
46
|
+
gB = rt.clip(gB, 0.0001, 0.2)
|
|
47
|
+
|
|
48
|
+
# stomatal conductance (m s^-1)
|
|
49
|
+
gS = (-(2 * (phi_Wm2 * alpha * delta_hPa * Ea_hPa * gamma_hPa - phi_Wm2 * alpha * delta_hPa * Es_hPa * gamma_hPa)) / (Cp_Jkg * Estar_hPa ** 2 * gamma_hPa * rho_kgm3 - Cp_Jkg * Es_hPa ** 2 * gamma_hPa * rho_kgm3 - 2 * Cp_Jkg * delta_hPa * Es_hPa ** 2 * rho_kgm3 + 2 * Cp_Jkg * delta_hPa * Ea_hPa * Es_hPa * rho_kgm3 - 2 * Cp_Jkg * delta_hPa * Ea_hPa * Estar_hPa * rho_kgm3 + 2 * Cp_Jkg * delta_hPa * Es_hPa * Estar_hPa * rho_kgm3 + 2 * Cp_Jkg * Ea_hPa * Es_hPa * gamma_hPa * rho_kgm3 - 2 * Cp_Jkg * Ea_hPa * Estar_hPa * gamma_hPa * rho_kgm3 + Cp_Jkg * SM * Es_hPa ** 2 * gamma_hPa * rho_kgm3 + Cp_Jkg * SM * Estar_hPa ** 2 * gamma_hPa * rho_kgm3 - 2 * Cp_Jkg * SM * Es_hPa * Estar_hPa * gamma_hPa * rho_kgm3))
|
|
50
|
+
gS = rt.clip(gS, 0.0001, 0.2)
|
|
51
|
+
|
|
52
|
+
# difference between surface and air temperature (Celsius)
|
|
53
|
+
dT = (2 * delta_hPa * Es_hPa - 2 * delta_hPa * Ea_hPa - 2 * Ea_hPa * gamma_hPa + Es_hPa * gamma_hPa + Estar_hPa * gamma_hPa - SM * Es_hPa * gamma_hPa + SM * Estar_hPa * gamma_hPa + 2 * alpha * delta_hPa * Ea_hPa - 2 * alpha * delta_hPa * Es_hPa) / (2 * alpha * delta_hPa * gamma_hPa)
|
|
54
|
+
dT = rt.clip(dT, -10, 50)
|
|
55
|
+
|
|
56
|
+
# evaporative fraction
|
|
57
|
+
EF = -(2 * alpha * delta_hPa * Ea_hPa - 2 * alpha * delta_hPa * Es_hPa) / (2 * delta_hPa * Es_hPa - 2 * delta_hPa * Ea_hPa - 2 * Ea_hPa * gamma_hPa + Es_hPa * gamma_hPa + Estar_hPa * gamma_hPa - SM * Es_hPa * gamma_hPa + SM * Estar_hPa * gamma_hPa)
|
|
58
|
+
EF = rt.clip(EF, 0, 1)
|
|
59
|
+
|
|
60
|
+
return gB, gS, dT, EF
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from matplotlib.colors import LinearSegmentedColormap
|
|
2
|
+
|
|
3
|
+
RHO_KGM3 = 1.2 # Air density (kg m-3)
|
|
4
|
+
CP_JKG = 1013 # Specific heat of air at constant pressure (J/kg/K)
|
|
5
|
+
GAMMA_HPA = 0.67 # Psychrometric constant (hpa/K)
|
|
6
|
+
PT_ALPHA = 1.26 # Priestley-Taylor coefficient
|
|
7
|
+
SB_SIGMA = 5.67e-8 # Stefann Boltzmann constant
|
|
8
|
+
DEFAULT_G_METHOD = "santanello"
|
|
9
|
+
LE_CONVERGENCE_TARGET_WM2 = 2.0
|
|
10
|
+
MAX_ITERATIONS = 30
|
|
11
|
+
USE_VARIABLE_ALPHA = True
|
|
12
|
+
SHOW_DISTRIBUTIONS = True
|
|
13
|
+
|
|
14
|
+
ET_COLORMAP = LinearSegmentedColormap.from_list("ET", [
|
|
15
|
+
"#f6e8c3",
|
|
16
|
+
"#d8b365",
|
|
17
|
+
"#99974a",
|
|
18
|
+
"#53792d",
|
|
19
|
+
"#6bdfd2",
|
|
20
|
+
"#1839c5"
|
|
21
|
+
])
|