BESS-JPL 1.6.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 BESS-JPL might be problematic. Click here for more details.

Files changed (72) hide show
  1. bess_jpl-1.6.0/.github/workflows/ci.yml +34 -0
  2. bess_jpl-1.6.0/.github/workflows/python-publish.yml +39 -0
  3. bess_jpl-1.6.0/.gitignore +162 -0
  4. bess_jpl-1.6.0/BESS_JPL/BESS.py +468 -0
  5. bess_jpl-1.6.0/BESS_JPL/BESS_JPL.py +23 -0
  6. bess_jpl-1.6.0/BESS_JPL/C3_photosynthesis.py +72 -0
  7. bess_jpl-1.6.0/BESS_JPL/C4_fraction.jpeg +0 -0
  8. bess_jpl-1.6.0/BESS_JPL/C4_fraction.tif +0 -0
  9. bess_jpl-1.6.0/BESS_JPL/C4_photosynthesis.py +56 -0
  10. bess_jpl-1.6.0/BESS_JPL/FVC_from_NDVI.py +22 -0
  11. bess_jpl-1.6.0/BESS_JPL/LAI_from_NDVI.py +29 -0
  12. bess_jpl-1.6.0/BESS_JPL/NDVI_maximum.jpeg +0 -0
  13. bess_jpl-1.6.0/BESS_JPL/NDVI_maximum.tif +0 -0
  14. bess_jpl-1.6.0/BESS_JPL/NDVI_minimum.jpeg +0 -0
  15. bess_jpl-1.6.0/BESS_JPL/NDVI_minimum.tif +0 -0
  16. bess_jpl-1.6.0/BESS_JPL/SZA/__init__.py +1 -0
  17. bess_jpl-1.6.0/BESS_JPL/SZA/daylight_hours.py +74 -0
  18. bess_jpl-1.6.0/BESS_JPL/__init__.py +10 -0
  19. bess_jpl-1.6.0/BESS_JPL/ball_berry_intercept_C3.jpeg +0 -0
  20. bess_jpl-1.6.0/BESS_JPL/ball_berry_intercept_C3.tif +0 -0
  21. bess_jpl-1.6.0/BESS_JPL/ball_berry_slope_C3.jpeg +0 -0
  22. bess_jpl-1.6.0/BESS_JPL/ball_berry_slope_C3.tif +0 -0
  23. bess_jpl-1.6.0/BESS_JPL/ball_berry_slope_C4.jpeg +0 -0
  24. bess_jpl-1.6.0/BESS_JPL/ball_berry_slope_C4.tif +0 -0
  25. bess_jpl-1.6.0/BESS_JPL/calculate_VCmax.py +54 -0
  26. bess_jpl-1.6.0/BESS_JPL/canopy_energy_balance.py +146 -0
  27. bess_jpl-1.6.0/BESS_JPL/canopy_longwave_radiation.py +92 -0
  28. bess_jpl-1.6.0/BESS_JPL/canopy_shortwave_radiation.py +234 -0
  29. bess_jpl-1.6.0/BESS_JPL/carbon_uptake_efficiency.jpeg +0 -0
  30. bess_jpl-1.6.0/BESS_JPL/carbon_uptake_efficiency.tif +0 -0
  31. bess_jpl-1.6.0/BESS_JPL/carbon_water_fluxes.py +277 -0
  32. bess_jpl-1.6.0/BESS_JPL/constants.py +8 -0
  33. bess_jpl-1.6.0/BESS_JPL/interpolate_C3_C4.py +12 -0
  34. bess_jpl-1.6.0/BESS_JPL/kn.jpeg +0 -0
  35. bess_jpl-1.6.0/BESS_JPL/kn.tif +0 -0
  36. bess_jpl-1.6.0/BESS_JPL/load_C4_fraction.py +10 -0
  37. bess_jpl-1.6.0/BESS_JPL/load_NDVI_maximum.py +10 -0
  38. bess_jpl-1.6.0/BESS_JPL/load_NDVI_minimum.py +10 -0
  39. bess_jpl-1.6.0/BESS_JPL/load_ball_berry_intercept_C3.py +10 -0
  40. bess_jpl-1.6.0/BESS_JPL/load_ball_berry_slope_C3.py +10 -0
  41. bess_jpl-1.6.0/BESS_JPL/load_ball_berry_slope_C4.py +10 -0
  42. bess_jpl-1.6.0/BESS_JPL/load_carbon_uptake_efficiency.py +10 -0
  43. bess_jpl-1.6.0/BESS_JPL/load_kn.py +10 -0
  44. bess_jpl-1.6.0/BESS_JPL/load_peakVCmax_C3.py +10 -0
  45. bess_jpl-1.6.0/BESS_JPL/load_peakVCmax_C4.py +10 -0
  46. bess_jpl-1.6.0/BESS_JPL/meteorology.py +204 -0
  47. bess_jpl-1.6.0/BESS_JPL/peakVCmax_C3.jpeg +0 -0
  48. bess_jpl-1.6.0/BESS_JPL/peakVCmax_C3.tif +0 -0
  49. bess_jpl-1.6.0/BESS_JPL/peakVCmax_C4.jpeg +0 -0
  50. bess_jpl-1.6.0/BESS_JPL/peakVCmax_C4.tif +0 -0
  51. bess_jpl-1.6.0/BESS_JPL/soil_energy_balance.py +35 -0
  52. bess_jpl-1.6.0/BESS_JPL/vegetation_conversion/__init__.py +1 -0
  53. bess_jpl-1.6.0/BESS_JPL/vegetation_conversion/vegetation_conversion.py +71 -0
  54. bess_jpl-1.6.0/BESS_JPL/version.txt +1 -0
  55. bess_jpl-1.6.0/BESS_JPL.egg-info/PKG-INFO +95 -0
  56. bess_jpl-1.6.0/BESS_JPL.egg-info/SOURCES.txt +70 -0
  57. bess_jpl-1.6.0/BESS_JPL.egg-info/dependency_links.txt +1 -0
  58. bess_jpl-1.6.0/BESS_JPL.egg-info/requires.txt +16 -0
  59. bess_jpl-1.6.0/BESS_JPL.egg-info/top_level.txt +1 -0
  60. bess_jpl-1.6.0/C4 Fraction.ipynb +56 -0
  61. bess_jpl-1.6.0/Dockerfile +27 -0
  62. bess_jpl-1.6.0/LICENSE +201 -0
  63. bess_jpl-1.6.0/Li_2023_RSE.pdf +0 -0
  64. bess_jpl-1.6.0/MANIFEST.in +1 -0
  65. bess_jpl-1.6.0/PKG-INFO +95 -0
  66. bess_jpl-1.6.0/Processing BESS with rasters.ipynb +9608 -0
  67. bess_jpl-1.6.0/README.md +67 -0
  68. bess_jpl-1.6.0/makefile +59 -0
  69. bess_jpl-1.6.0/pyproject.toml +43 -0
  70. bess_jpl-1.6.0/setup.cfg +4 -0
  71. bess_jpl-1.6.0/tests/test_import_breathing_earth_system_simulator.py +2 -0
  72. bess_jpl-1.6.0/tests/test_import_dependencies.py +17 -0
@@ -0,0 +1,34 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+ branches:
9
+ - main
10
+
11
+ jobs:
12
+ test:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ python-version: ["3.10"]
17
+
18
+ steps:
19
+ - name: Checkout repository
20
+ uses: actions/checkout@v3 # This is already the latest version
21
+
22
+ - name: Set up Python
23
+ uses: actions/setup-python@v4 # This is also the latest version
24
+ with:
25
+ python-version: ${{ matrix.python-version }}
26
+
27
+ - name: Install dependencies
28
+ run: |
29
+ python -m pip install --upgrade pip
30
+ pip install .[dev] # Use dev to install pytest
31
+
32
+ - name: Run tests
33
+ run: |
34
+ pytest
@@ -0,0 +1,39 @@
1
+ # This workflow will upload a Python Package using Twine when a release is created
2
+ # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
3
+
4
+ # This workflow uses actions that are not certified by GitHub.
5
+ # They are provided by a third-party and are governed by
6
+ # separate terms of service, privacy policy, and support
7
+ # documentation.
8
+
9
+ name: Upload Python Package
10
+
11
+ on:
12
+ release:
13
+ types: [published]
14
+
15
+ permissions:
16
+ contents: read
17
+
18
+ jobs:
19
+ deploy:
20
+
21
+ runs-on: ubuntu-latest
22
+
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - name: Set up Python
26
+ uses: actions/setup-python@v3
27
+ with:
28
+ python-version: '3.x'
29
+ - name: Install dependencies
30
+ run: |
31
+ python -m pip install --upgrade pip
32
+ pip install build
33
+ - name: Build package
34
+ run: python -m build
35
+ - name: Publish package
36
+ uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
37
+ with:
38
+ user: __token__
39
+ password: ${{ secrets.PYPI_API_TOKEN }}
@@ -0,0 +1,162 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ .idea/
161
+
162
+ .DS_Store
@@ -0,0 +1,468 @@
1
+ from typing import Union
2
+ from datetime import datetime
3
+ import logging
4
+ import numpy as np
5
+
6
+ import rasters as rt
7
+ from rasters import Raster, RasterGeometry
8
+
9
+ from check_distribution import check_distribution
10
+
11
+ from sun_angles import calculate_SZA_from_DOY_and_hour
12
+
13
+ from koppengeiger import load_koppen_geiger
14
+ from gedi_canopy_height import load_canopy_height
15
+ from FLiESANN import FLiESANN
16
+ from GEOS5FP import GEOS5FP
17
+ from MODISCI import MODISCI
18
+
19
+ from .constants import *
20
+ from .vegetation_conversion import LAI_from_NDVI
21
+ from .canopy_shortwave_radiation import canopy_shortwave_radiation
22
+ from .carbon_water_fluxes import carbon_water_fluxes
23
+ from .meteorology import meteorology, SVP_Pa_from_Ta_K
24
+ from .interpolate_C3_C4 import interpolate_C3_C4
25
+ from .calculate_VCmax import calculate_VCmax
26
+ from .load_NDVI_minimum import load_NDVI_minimum
27
+ from .load_NDVI_maximum import load_NDVI_maximum
28
+ from .load_C4_fraction import load_C4_fraction
29
+ from .load_carbon_uptake_efficiency import load_carbon_uptake_efficiency
30
+ from .load_kn import load_kn
31
+ from .load_peakVCmax_C3 import load_peakVCmax_C3
32
+ from .load_peakVCmax_C4 import load_peakVCmax_C4
33
+ from .load_ball_berry_intercept_C3 import load_ball_berry_intercept_C3
34
+ from .load_ball_berry_slope_C3 import load_ball_berry_slope_C3
35
+ from .load_ball_berry_slope_C4 import load_ball_berry_slope_C4
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+ def BESS(
40
+ ST_C: Union[Raster, np.ndarray], # surface temperature in Celsius
41
+ NDVI: Union[Raster, np.ndarray], # NDVI
42
+ albedo: Union[Raster, np.ndarray], # surface albedo
43
+ elevation_km: Union[Raster, np.ndarray], # elevation in kilometers
44
+ geometry: RasterGeometry = None,
45
+ time_UTC: datetime = None,
46
+ hour_of_day: np.ndarray = None,
47
+ day_of_year: np.ndarray = None,
48
+ GEOS5FP_connection: GEOS5FP = None,
49
+ Ta_C: Union[Raster, np.ndarray] = None, # air temperature in Celsius
50
+ RH: Union[Raster, np.ndarray] = None, # relative humidity as a proportion
51
+ NDVI_minimum: Union[Raster, np.ndarray] = None, # minimum NDVI
52
+ NDVI_maximum: Union[Raster, np.ndarray] = None, # maximum NDVI
53
+ Rg: Union[Raster, np.ndarray] = None, # incoming shortwave radiation in W/m^2
54
+ VISdiff: Union[Raster, np.ndarray] = None, # diffuse visible radiation in W/m^2
55
+ VISdir: Union[Raster, np.ndarray] = None, # direct visible radiation in W/m^2
56
+ NIRdiff: Union[Raster, np.ndarray] = None, # diffuse near-infrared radiation in W/m^2
57
+ NIRdir: Union[Raster, np.ndarray] = None, # direct near-infrared radiation in W/m^2
58
+ UV: Union[Raster, np.ndarray] = None, # incoming ultraviolet radiation in W/m^2
59
+ albedo_visible: Union[Raster, np.ndarray] = None, # surface albedo in visible wavelengths (initialized to surface albedo if left as None)
60
+ albedo_NIR: Union[Raster, np.ndarray] = None, # surface albedo in near-infrared wavelengths (initialized to surface albedo if left as None)
61
+ COT: Union[Raster, np.ndarray] = None, # cloud optical thickness
62
+ AOT: Union[Raster, np.ndarray] = None, # aerosol optical thickness
63
+ vapor_gccm: Union[Raster, np.ndarray] = None, # water vapor in g/ccm
64
+ ozone_cm: Union[Raster, np.ndarray] = None, # ozone in cm
65
+ KG_climate: Union[Raster, np.ndarray] = None, # KG climate
66
+ canopy_height_meters: Union[Raster, np.ndarray] = None, # canopy height in meters
67
+ Ca: Union[Raster, np.ndarray] = None, # atmospheric CO2 concentration in ppm
68
+ wind_speed_mps: Union[Raster, np.ndarray] = None, # wind speed in meters per second
69
+ SZA: Union[Raster, np.ndarray] = None, # solar zenith angle in degrees
70
+ canopy_temperature_C: Union[Raster, np.ndarray] = None, # canopy temperature in Celsius (initialized to surface temperature if left as None)
71
+ soil_temperature_C: Union[Raster, np.ndarray] = None, # soil temperature in Celsius (initialized to surface temperature if left as None)
72
+ C4_fraction: Union[Raster, np.ndarray] = None, # fraction of C4 plants
73
+ carbon_uptake_efficiency: Union[Raster, np.ndarray] = None, # intrinsic quantum efficiency for carbon uptake
74
+ kn: np.ndarray = None,
75
+ ball_berry_intercept_C3: np.ndarray = None, # Ball-Berry intercept for C3 plants
76
+ ball_berry_intercept_C4: Union[np.ndarray, float] = BALL_BERRY_INTERCEPT_C4, # Ball-Berry intercept for C4 plants
77
+ ball_berry_slope_C3: np.ndarray = None, # Ball-Berry slope for C3 plants
78
+ ball_berry_slope_C4: np.ndarray = None, # Ball-Berry slope for C4 plants
79
+ peakVCmax_C3: np.ndarray = None, # peak maximum carboxylation rate for C3 plants
80
+ peakVCmax_C4: np.ndarray = None, # peak maximum carboxylation rate for C4 plants
81
+ CI: Union[Raster, np.ndarray] = None,
82
+ resampling: str = RESAMPLING): # clumping index
83
+ if geometry is None and isinstance(ST_C, Raster):
84
+ geometry = ST_C.geometry
85
+
86
+ if (day_of_year is None or hour_of_day is None) and time_UTC is not None and geometry is not None:
87
+ day_of_year = solar_day_of_year_for_area(time_UTC=time_UTC, geometry=geometry)
88
+ hour_of_day = solar_hour_of_day_for_area(time_UTC=time_UTC, geometry=geometry)
89
+
90
+ if time_UTC is None and day_of_year is None and hour_of_day is None:
91
+ raise ValueError("no time given between time_UTC, day_of_year, and hour_of_day")
92
+
93
+ # load air temperature in Celsius if not provided
94
+ if Ta_C is None:
95
+ Ta_C = GEOS5FP_connection.Ta_C(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
96
+
97
+ # load relative humidity if not provided
98
+ if RH is None:
99
+ RH = GEOS5FP_connection.RH(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
100
+
101
+ # load minimum NDVI if not provided
102
+ if NDVI_minimum is None and geometry is not None:
103
+ NDVI_minimum = load_NDVI_minimum(geometry=geometry, resampling=resampling)
104
+
105
+ # load maximum NDVI if not provided
106
+ if NDVI_maximum is None and geometry is not None:
107
+ NDVI_maximum = load_NDVI_maximum(geometry=geometry, resampling=resampling)
108
+
109
+ # load C4 fraction if not provided
110
+ if C4_fraction is None:
111
+ C4_fraction = load_C4_fraction(geometry=geometry, resampling=resampling)
112
+
113
+ # load carbon uptake efficiency if not provided
114
+ if carbon_uptake_efficiency is None:
115
+ carbon_uptake_efficiency = load_carbon_uptake_efficiency(geometry=geometry, resampling=resampling)
116
+
117
+ # load kn if not provided
118
+ if kn is None:
119
+ kn = load_kn(geometry=geometry, resampling=resampling)
120
+
121
+ # load peak VC max for C3 plants if not provided
122
+ if peakVCmax_C3 is None:
123
+ peakVCmax_C3 = load_peakVCmax_C3(geometry=geometry, resampling=resampling)
124
+
125
+ # load peak VC max for C4 plants if not provided
126
+ if peakVCmax_C4 is None:
127
+ peakVCmax_C4 = load_peakVCmax_C4(geometry=geometry, resampling=resampling)
128
+
129
+ # load Ball-Berry slope for C3 plants if not provided
130
+ if ball_berry_slope_C3 is None:
131
+ ball_berry_slope_C3 = load_ball_berry_slope_C3(geometry=geometry, resampling=resampling)
132
+
133
+ # load Ball-Berry slope for C4 plants if not provided
134
+ if ball_berry_slope_C4 is None:
135
+ ball_berry_slope_C4 = load_ball_berry_slope_C4(geometry=geometry, resampling=resampling)
136
+
137
+ # load Ball-Berry intercept for C3 plants if not provided
138
+ if ball_berry_intercept_C3 is None:
139
+ ball_berry_intercept_C3 = load_ball_berry_intercept_C3(geometry=geometry, resampling=resampling)
140
+
141
+ # check if any of the FLiES outputs are not given
142
+ if None in (Rg, VISdiff, VISdir, NIRdiff, NIRdir, UV, albedo_visible, albedo_NIR):
143
+ # load cloud optical thickness if not provided
144
+ if COT is None:
145
+ COT = GEOS5FP_connection.COT(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
146
+
147
+ # load aerosol optical thickness if not provided
148
+ if AOT is None:
149
+ AOT = GEOS5FP_connection.AOT(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
150
+
151
+ ## FIXME fix FLiES interface
152
+
153
+ # run FLiES radiative transfer model
154
+ FLiES_results = FLiESANN(
155
+ day_of_year=day_of_year,
156
+ hour_of_day=hour_of_day,
157
+ albedo=albedo,
158
+ COT=COT,
159
+ AOT=AOT,
160
+ vapor_gccm=vapor_gccm,
161
+ ozone_cm=ozone_cm,
162
+ elevation_km=elevation_km,
163
+ SZA=SZA,
164
+ KG_climate=KG_climate,
165
+ geometry=geometry,
166
+ GEOS5FP_connection=GEOS5FP_connection
167
+ )
168
+
169
+ # extract FLiES outputs
170
+ Rg = FLiES_results["Rg"]
171
+ VISdiff = FLiES_results["VISdiff"]
172
+ VISdir = FLiES_results["VISdir"]
173
+ NIRdiff = FLiES_results["NIRdiff"]
174
+ NIRdir = FLiES_results["NIRdir"]
175
+ UV = FLiES_results["UV"]
176
+ albedo_visible = FLiES_results["VIS"]
177
+ albedo_NIR = FLiES_results["NIR"]
178
+
179
+ # load koppen geiger climate classification if not provided
180
+ if KG_climate is None:
181
+ KG_climate = load_koppen_geiger(geometry=geometry)
182
+
183
+ # load canopy height in meters if not provided
184
+ if canopy_height_meters is None:
185
+ canopy_height_meters = load_canopy_height(geometry=geometry, resampling=resampling)
186
+
187
+ # load CO2 concentration in ppm if not provided
188
+ if Ca is None:
189
+ Ca = GEOS5FP_connection.CO2SC(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
190
+
191
+ # load wind speed in meters per second if not provided
192
+ if wind_speed_mps is None:
193
+ wind_speed_mps = GEOS5FP_connection.wind_speed(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
194
+
195
+ # canopy temperature defaults to surface temperature
196
+ if canopy_temperature_C is None:
197
+ canopy_temperature_C = ST_C
198
+
199
+ # soil temperature defaults to surface temperature
200
+ if soil_temperature_C is None:
201
+ soil_temperature_C = ST_C
202
+
203
+ # visible albedo defaults to surface albedo
204
+ if albedo_visible is None:
205
+ albedo_visible = albedo
206
+
207
+ # near-infrared albedo defaults to surface albedo
208
+ if albedo_NIR is None:
209
+ albedo_NIR = albedo
210
+
211
+ # calculate solar zenith angle if not provided
212
+ if SZA is None:
213
+ SZA = calculate_SZA_from_DOY_and_hour(lat, lon, day_of_year, hour_of_day)
214
+
215
+ if CI is None and geometry is not None:
216
+ modisci = MODISCI()
217
+ CI = modisci.CI(geometry=geometry, resampling=resampling)
218
+
219
+ # canopy height defaults to zero
220
+ canopy_height_meters = np.where(np.isnan(canopy_height_meters), 0, canopy_height_meters)
221
+
222
+ # calculate saturation vapor pressure in Pascal from air temperature in Kelvin
223
+ Ta_K = Ta_C + 273.15
224
+ SVP_Pa = SVP_Pa_from_Ta_K(Ta_K)
225
+
226
+ # calculate actual vapor pressure in Pascal from relative humidity and saturation vapor pressure
227
+ Ea_Pa = RH * SVP_Pa
228
+
229
+ # convert elevation to meters
230
+ elevation_m = elevation_km * 1000
231
+
232
+ latitude = geometry.lat
233
+
234
+ Ps_Pa, VPD_Pa, RH, desTa, ddesTa, gamma, Cp, rhoa, epsa, R, Rc, Rs, SFd, SFd2, DL, Ra, fStress = meteorology(
235
+ day_of_year=day_of_year,
236
+ hour_of_day=hour_of_day,
237
+ latitude=latitude,
238
+ elevation_m=elevation_m,
239
+ SZA=SZA,
240
+ Ta_K=Ta_K,
241
+ Ea_Pa=Ea_Pa,
242
+ Rg=Rg,
243
+ wind_speed_mps=wind_speed_mps,
244
+ canopy_height_meters=canopy_height_meters
245
+ )
246
+
247
+ meteorology_outputs = {
248
+ "Ps_Pa": Ps_Pa,
249
+ "VPD_Pa": VPD_Pa,
250
+ "RH": RH,
251
+ "desTa": desTa,
252
+ "ddesTa": ddesTa,
253
+ "gamma": gamma,
254
+ "Cp": Cp,
255
+ "rhoa": rhoa,
256
+ "epsa": epsa,
257
+ "R": R,
258
+ "Rc": Rc,
259
+ "Rs": Rs,
260
+ "SFd": SFd,
261
+ "SFd2": SFd2,
262
+ "DL": DL,
263
+ "Ra": Ra,
264
+ "fStress": fStress
265
+ }
266
+
267
+ # Check the distribution for each variable
268
+ for var_name, var_value in meteorology_outputs.items():
269
+ check_distribution(var_value, var_name, time_UTC)
270
+
271
+ # convert NDVI to LAI
272
+ LAI = LAI_from_NDVI(NDVI)
273
+ LAI_minimum = LAI_from_NDVI(NDVI_minimum)
274
+ LAI_maximum = LAI_from_NDVI(NDVI_maximum)
275
+
276
+ VCmax_C3_sunlit, VCmax_C4_sunlit, VCmax_C3_shaded, VCmax_C4_shaded = calculate_VCmax(
277
+ LAI=LAI,
278
+ LAI_minimum=LAI_minimum,
279
+ LAI_maximum=LAI_maximum,
280
+ peakVCmax_C3=peakVCmax_C3,
281
+ peakVCmax_C4=peakVCmax_C4,
282
+ SZA=SZA,
283
+ kn=kn
284
+ )
285
+
286
+ # List of variable names and their corresponding values
287
+ VCmax_outputs = {
288
+ "VCmax_C3_sunlit": VCmax_C3_sunlit,
289
+ "VCmax_C4_sunlit": VCmax_C4_sunlit,
290
+ "VCmax_C3_shaded": VCmax_C3_shaded,
291
+ "VCmax_C4_shaded": VCmax_C4_shaded
292
+ }
293
+
294
+ # Check the distribution for each variable
295
+ for var_name, var_value in VCmax_outputs.items():
296
+ check_distribution(var_value, var_name, time_UTC)
297
+
298
+ sunlit_fraction, APAR_sunlit, APAR_shaded, ASW_sunlit, ASW_shaded, ASW_soil, G = canopy_shortwave_radiation(
299
+ PARDiff=VISdiff, # diffuse photosynthetically active radiation in W/m^2
300
+ PARDir=VISdir, # direct photosynthetically active radiation in W/m^2
301
+ NIRDiff=NIRdiff, # diffuse near-infrared radiation in W/m^2
302
+ NIRDir=NIRdir, # direct near-infrared radiation in W/m^2
303
+ UV=UV, # incoming ultraviolet radiation in W/m^2
304
+ SZA=SZA, # solar zenith angle in degrees
305
+ LAI=LAI, # leaf area index
306
+ CI=CI, # clumping index
307
+ albedo_visible=albedo_visible, # surface albedo in visible wavelengths
308
+ albedo_NIR=albedo_NIR # surface albedo in near-infrared wavelengths
309
+ )
310
+
311
+ # List of variable names and their corresponding values
312
+ canopy_radiation_outputs = {
313
+ "sunlit_fraction": sunlit_fraction,
314
+ "APAR_sunlit": APAR_sunlit,
315
+ "APAR_shaded": APAR_shaded,
316
+ "ASW_sunlit": ASW_sunlit,
317
+ "ASW_shaded": ASW_shaded,
318
+ "ASW_soil": ASW_soil,
319
+ "G": G
320
+ }
321
+
322
+ # Check the distribution for each variable
323
+ for var_name, var_value in canopy_radiation_outputs.items():
324
+ check_distribution(var_value, var_name, time_UTC)
325
+
326
+
327
+ canopy_temperature_K = canopy_temperature_C + 273.15
328
+ soil_temperature_K = soil_temperature_C + 273.15
329
+
330
+ GPP_C3, LE_C3, LE_soil_C3, LE_canopy_C3, Rn_C3, Rn_soil_C3, Rn_canopy_C3 = carbon_water_fluxes(
331
+ canopy_temperature_K=canopy_temperature_K, # canopy temperature in Kelvin
332
+ soil_temperature_K=soil_temperature_K, # soil temperature in Kelvin
333
+ LAI=LAI, # leaf area index
334
+ Ta_K=Ta_K, # air temperature in Kelvin
335
+ APAR_sunlit=APAR_sunlit, # sunlit leaf absorptance of photosynthetically active radiation
336
+ APAR_shaded=APAR_shaded, # shaded leaf absorptance of photosynthetically active radiation
337
+ ASW_sunlit=ASW_sunlit, # sunlit absorbed shortwave radiation
338
+ ASW_shaded=ASW_shaded, # shaded absorbed shortwave radiation
339
+ ASW_soil=ASW_soil, # absorbed shortwave radiation of soil
340
+ Vcmax25_sunlit=VCmax_C3_sunlit, # sunlit maximum carboxylation rate at 25 degrees C
341
+ Vcmax25_shaded=VCmax_C3_shaded, # shaded maximum carboxylation rate at 25 degrees C
342
+ ball_berry_slope=ball_berry_slope_C3, # Ball-Berry slope for C3 photosynthesis
343
+ ball_berry_intercept=ball_berry_intercept_C3, # Ball-Berry intercept for C3 photosynthesis
344
+ sunlit_fraction=sunlit_fraction, # fraction of sunlit leaves
345
+ G=G, # soil heat flux
346
+ SZA=SZA, # solar zenith angle
347
+ Ca=Ca, # atmospheric CO2 concentration
348
+ Ps_Pa=Ps_Pa, # surface pressure in Pascal
349
+ gamma=gamma, # psychrometric constant
350
+ Cp=Cp, # specific heat of air in J/kg/K
351
+ rhoa=rhoa, # density of air in kg/m3
352
+ VPD_Pa=VPD_Pa, # vapor pressure deficit in Pascal
353
+ RH=RH, # relative humidity as a fraction
354
+ desTa=desTa,
355
+ ddesTa=ddesTa,
356
+ epsa=epsa,
357
+ Rc=Rc,
358
+ Rs=Rs,
359
+ carbon_uptake_efficiency=carbon_uptake_efficiency, # intrinsic quantum efficiency for carbon uptake
360
+ fStress=fStress,
361
+ C4_photosynthesis=False # C3 or C4 photosynthesis
362
+ )
363
+
364
+ # List of variable names and their corresponding values
365
+ carbon_water_fluxes_outputs = {
366
+ "GPP_C3": GPP_C3,
367
+ "LE_C3": LE_C3,
368
+ "LE_soil_C3": LE_soil_C3,
369
+ "LE_canopy_C3": LE_canopy_C3,
370
+ "Rn_C3": Rn_C3,
371
+ "Rn_soil_C3": Rn_soil_C3,
372
+ "Rn_canopy_C3": Rn_canopy_C3
373
+ }
374
+
375
+ # Check the distribution for each variable
376
+ for var_name, var_value in carbon_water_fluxes_outputs.items():
377
+ check_distribution(var_value, var_name, time_UTC)
378
+
379
+
380
+ GPP_C4, LE_C4, LE_soil_C4, LE_canopy_C4, Rn_C4, Rn_soil_C4, Rn_canopy_C4 = carbon_water_fluxes(
381
+ canopy_temperature_K=canopy_temperature_K, # canopy temperature in Kelvin
382
+ soil_temperature_K=soil_temperature_K, # soil temperature in Kelvin
383
+ LAI=LAI, # leaf area index
384
+ Ta_K=Ta_K, # air temperature in Kelvin
385
+ APAR_sunlit=APAR_sunlit, # sunlit leaf absorptance of photosynthetically active radiation
386
+ APAR_shaded=APAR_shaded, # shaded leaf absorptance of photosynthetically active radiation
387
+ ASW_sunlit=ASW_sunlit, # sunlit absorbed shortwave radiation
388
+ ASW_shaded=ASW_shaded, # shaded absorbed shortwave radiation
389
+ ASW_soil=ASW_soil, # absorbed shortwave radiation of soil
390
+ Vcmax25_sunlit=VCmax_C4_sunlit, # sunlit maximum carboxylation rate at 25 degrees C
391
+ Vcmax25_shaded=VCmax_C4_shaded, # shaded maximum carboxylation rate at 25 degrees C
392
+ ball_berry_slope=ball_berry_slope_C4, # Ball-Berry slope for C4 photosynthesis
393
+ ball_berry_intercept=ball_berry_intercept_C4, # Ball-Berry intercept for C4 photosynthesis
394
+ sunlit_fraction=sunlit_fraction, # fraction of sunlit leaves
395
+ G=G, # soil heat flux
396
+ SZA=SZA, # solar zenith angle
397
+ Ca=Ca, # atmospheric CO2 concentration
398
+ Ps_Pa=Ps_Pa, # surface pressure in Pascal
399
+ gamma=gamma, # psychrometric constant
400
+ Cp=Cp, # specific heat of air in J/kg/K
401
+ rhoa=rhoa, # density of air in kg/m3
402
+ VPD_Pa=VPD_Pa, # vapor pressure deficit in Pascal
403
+ RH=RH, # relative humidity as a fraction
404
+ desTa=desTa,
405
+ ddesTa=ddesTa,
406
+ epsa=epsa,
407
+ Rc=Rc,
408
+ Rs=Rs,
409
+ carbon_uptake_efficiency=carbon_uptake_efficiency, # intrinsic quantum efficiency for carbon uptake
410
+ fStress=fStress,
411
+ C4_photosynthesis=True # C3 or C4 photosynthesis
412
+ )
413
+
414
+ # List of variable names and their corresponding values
415
+ carbon_water_fluxes_C4_outputs = {
416
+ "GPP_C4": GPP_C4,
417
+ "LE_C4": LE_C4,
418
+ "LE_soil_C4": LE_soil_C4,
419
+ "LE_canopy_C4": LE_canopy_C4,
420
+ "Rn_C4": Rn_C4,
421
+ "Rn_soil_C4": Rn_soil_C4,
422
+ "Rn_canopy_C4": Rn_canopy_C4
423
+ }
424
+
425
+ # Check the distribution for each variable
426
+ for var_name, var_value in carbon_water_fluxes_C4_outputs.items():
427
+ check_distribution(var_value, var_name, time_UTC)
428
+
429
+ # interpolate C3 and C4 GPP
430
+ ST_K = ST_C + 273.15
431
+ GPP = np.clip(interpolate_C3_C4(GPP_C3, GPP_C4, C4_fraction), 0, 50)
432
+ GPP = np.where(np.isnan(ST_K), np.nan, GPP)
433
+
434
+ # upscale from instantaneous to daily
435
+
436
+ # upscale GPP to daily
437
+ GPP_daily = 1800 * GPP / SFd * 1e-6 * 12 # Eq. (3) in Ryu et al 2008
438
+ GPP_daily = np.where(SFd < 0.01, 0, GPP_daily)
439
+ GPP_daily = np.where(SZA >= 90, 0, GPP_daily)
440
+
441
+ # interpolate C3 and C4 net radiation
442
+ Rn = np.clip(interpolate_C3_C4(Rn_C3, Rn_C4, C4_fraction), 0, 1000)
443
+
444
+ # interpolate C3 and C4 soil net radiation
445
+ Rn_soil = np.clip(interpolate_C3_C4(Rn_soil_C3, Rn_soil_C4, C4_fraction), 0, 1000)
446
+
447
+ # interpolate C3 and C4 canopy net radiation
448
+ Rn_canopy = np.clip(interpolate_C3_C4(Rn_canopy_C3, Rn_canopy_C4, C4_fraction), 0, 1000)
449
+
450
+ # interpolate C3 and C4 latent heat flux
451
+ LE = np.clip(interpolate_C3_C4(LE_C3, LE_C4, C4_fraction), 0, 1000)
452
+
453
+ # interpolate C3 and C4 soil latent heat flux
454
+ LE_soil = np.clip(interpolate_C3_C4(LE_soil_C3, LE_soil_C4, C4_fraction), 0, 1000)
455
+
456
+ # interpolate C3 and C4 canopy latent heat flux
457
+ LE_canopy = np.clip(interpolate_C3_C4(LE_canopy_C3, LE_canopy_C4, C4_fraction), 0, 1000)
458
+
459
+ return {
460
+ "GPP": GPP,
461
+ "GPP_daily": GPP_daily,
462
+ "Rn": Rn,
463
+ "Rn_soil": Rn_soil,
464
+ "Rn_canopy": Rn_canopy,
465
+ "LE": LE,
466
+ "LE_soil": LE_soil,
467
+ "LE_canopy": LE_canopy
468
+ }