PM-JPL 1.2.1__py3-none-any.whl → 1.7.0__py3-none-any.whl

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.
Files changed (66) hide show
  1. PMJPL/ECOv002-cal-val-PM-JPL-inputs.csv +1048 -0
  2. PMJPL/ECOv002-cal-val-PM-JPL-outputs.csv +1048 -0
  3. PMJPL/ECOv002-static-tower-PM-JPL-inputs.csv +122 -0
  4. PMJPL/ECOv002_calval_PMJPL_inputs.py +19 -0
  5. PMJPL/ECOv002_static_tower_PMJPL_inputs.py +19 -0
  6. PMJPL/PMJPL.py +45 -378
  7. PMJPL/{parameters.py → PMJPL_parameter_from_IGBP.py} +22 -7
  8. PMJPL/VPD_factor.py +1 -1
  9. PMJPL/__init__.py +1 -6
  10. PMJPL/calculate_gamma.py +128 -0
  11. PMJPL/canopy_aerodynamic_resistance.py +151 -18
  12. PMJPL/canopy_conductance.py +71 -15
  13. PMJPL/closed_minimum_temperature.py +11 -0
  14. PMJPL/closed_vapor_pressure_deficit.py +11 -0
  15. PMJPL/constants.py +6 -1
  16. PMJPL/correctance_factor.py +56 -7
  17. PMJPL/generate_PMJPL_inputs.py +263 -0
  18. PMJPL/interception.py +1 -3
  19. PMJPL/leaf_conductance_to_evaporated_water.py +11 -0
  20. PMJPL/leaf_conductance_to_sensible_heat.py +58 -0
  21. PMJPL/maximum_boundary_layer_resistance.py +11 -0
  22. PMJPL/minimum_boundary_layer_resistance.py +11 -0
  23. PMJPL/{tmin_factor.py → minimum_temperature_factor.py} +2 -2
  24. PMJPL/mod16.csv +19 -0
  25. PMJPL/model.py +690 -0
  26. PMJPL/open_minimum_temperature.py +11 -0
  27. PMJPL/open_vapor_pressure_deficit.py +11 -0
  28. PMJPL/potential_soil_evaporation.py +2 -2
  29. PMJPL/potential_stomatal_conductance.py +11 -0
  30. PMJPL/process_PMJPL_table.py +208 -0
  31. PMJPL/process_daily_ET_table.py +40 -0
  32. PMJPL/transpiration.py +4 -4
  33. PMJPL/verify.py +77 -0
  34. PMJPL/version.py +4 -0
  35. PMJPL/wet_canopy_resistance.py +1 -0
  36. PMJPL/wet_soil_evaporation.py +4 -4
  37. {pm_jpl-1.2.1.dist-info → pm_jpl-1.7.0.dist-info}/METADATA +18 -21
  38. pm_jpl-1.7.0.dist-info/RECORD +42 -0
  39. {pm_jpl-1.2.1.dist-info → pm_jpl-1.7.0.dist-info}/WHEEL +1 -1
  40. PMJPL/MCD12C1/MCD12C1.py +0 -10
  41. PMJPL/MCD12C1/__init__.py +0 -1
  42. PMJPL/SEBAL/SEBAL.py +0 -45
  43. PMJPL/SEBAL/__init__.py +0 -1
  44. PMJPL/downscaling/__init__.py +0 -1
  45. PMJPL/downscaling/downscaling.py +0 -271
  46. PMJPL/downscaling/linear_downscale.py +0 -71
  47. PMJPL/evapotranspiration_conversion/__init__.py +0 -1
  48. PMJPL/evapotranspiration_conversion/evapotranspiration_conversion.py +0 -80
  49. PMJPL/fwet.py +0 -21
  50. PMJPL/meteorology_conversion/__init__.py +0 -1
  51. PMJPL/meteorology_conversion/meteorology_conversion.py +0 -123
  52. PMJPL/penman_monteith/__init__.py +0 -1
  53. PMJPL/penman_monteith/penman_monteith.py +0 -20
  54. PMJPL/priestley_taylor/__init__.py +0 -1
  55. PMJPL/priestley_taylor/priestley_taylor.py +0 -27
  56. PMJPL/santanello/__init__.py +0 -1
  57. PMJPL/santanello/santanello.py +0 -46
  58. PMJPL/soil_heat_flux/__init__.py +0 -1
  59. PMJPL/soil_heat_flux/soil_heat_flux.py +0 -62
  60. PMJPL/vegetation_conversion/__init__.py +0 -1
  61. PMJPL/vegetation_conversion/vegetation_conversion.py +0 -47
  62. PMJPL/verma_net_radiation/__init__.py +0 -1
  63. PMJPL/verma_net_radiation/verma_net_radiation.py +0 -108
  64. pm_jpl-1.2.1.dist-info/RECORD +0 -44
  65. {pm_jpl-1.2.1.dist-info → pm_jpl-1.7.0.dist-info}/licenses/LICENSE +0 -0
  66. {pm_jpl-1.2.1.dist-info → pm_jpl-1.7.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,11 @@
1
+ from .PMJPL_parameter_from_IGBP import PMJPL_parameter_from_IGBP
2
+ from rasters import Raster, RasterGeometry, VectorGeometry
3
+ from typing import Union
4
+
5
+ def open_vapor_pressure_deficit(IGBP: Union[Raster, int], geometry=None, IGBP_upsampling_resolution_meters=None):
6
+ return PMJPL_parameter_from_IGBP(
7
+ variable="VPD_open",
8
+ IGBP=IGBP,
9
+ geometry=geometry,
10
+ IGBP_upsampling_resolution_meters=IGBP_upsampling_resolution_meters
11
+ )
@@ -11,7 +11,7 @@ def calculate_potential_soil_evaporation(
11
11
  rho: float,
12
12
  Cp_Jkg: float,
13
13
  FVC: float,
14
- VPD: float,
14
+ VPD_Pa: float,
15
15
  ras: float,
16
16
  fwet: float,
17
17
  rtot: float,
@@ -39,7 +39,7 @@ def calculate_potential_soil_evaporation(
39
39
  - The Penman-Monteith equation takes into account various factors such as radiation, air density, heat capacity, vegetation cover, vapor pressure deficit, aerodynamic resistance, soil wetness, and total resistance.
40
40
  - The function returns the potential soil evaporation as either a Raster object or a NumPy array, depending on the input data type.
41
41
  """
42
- numerator = (delta_Pa * Asoil + rho * Cp_Jkg * (1.0 - FVC) * VPD / ras) * (1.0 - fwet)
42
+ numerator = (delta_Pa * Asoil + rho * Cp_Jkg * (1.0 - FVC) * VPD_Pa / ras) * (1.0 - fwet)
43
43
  denominator = delta_Pa + gamma_Pa * rtot / ras
44
44
  LE_soil_pot = numerator / denominator
45
45
 
@@ -0,0 +1,11 @@
1
+ from .PMJPL_parameter_from_IGBP import PMJPL_parameter_from_IGBP
2
+ from rasters import Raster, RasterGeometry, VectorGeometry
3
+ from typing import Union
4
+
5
+ def potential_stomatal_conductance(IGBP: Union[Raster, int], geometry=None, IGBP_upsampling_resolution_meters=None):
6
+ return PMJPL_parameter_from_IGBP(
7
+ variable="CL",
8
+ IGBP=IGBP,
9
+ geometry=geometry,
10
+ IGBP_upsampling_resolution_meters=IGBP_upsampling_resolution_meters
11
+ )
@@ -0,0 +1,208 @@
1
+ """
2
+ Module: process_PMJPL_table.py
3
+
4
+ This module provides a function to process input data for the PM-JPL (Penman-Monteith Jet Propulsion Laboratory) model.
5
+ It prepares the required variables from a pandas DataFrame, handles missing or alternative column names, computes derived variables as needed, and runs the PM-JPL model to generate output variables, which are appended to the input DataFrame.
6
+ """
7
+ import logging
8
+
9
+ import numpy as np
10
+ import pandas as pd
11
+ import rasters as rt
12
+ from rasters import MultiPoint, WGS84
13
+
14
+ from dateutil import parser
15
+ from pandas import DataFrame
16
+
17
+ from .constants import *
18
+ from .PMJPL import PMJPL
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ # FIXME include additional inputs required by PM-JPL that were not required by PT-JPL
23
+
24
+ def process_PMJPL_table(
25
+ input_df: DataFrame,
26
+ upscale_to_daylight: bool = False,
27
+ regenerate_net_radiation: bool = False
28
+ ) -> DataFrame:
29
+ """
30
+ Processes an input DataFrame to prepare all required variables for the PM-JPL model,
31
+ runs the model, and returns a DataFrame with the model outputs appended as new columns.
32
+
33
+ This function is designed to work with tabular (pandas DataFrame) data, such as point or site-level measurements or extracted pixel values from gridded products. It is compatible with DataFrames produced by ECOSTRESS Cal-Val or similar sources, and is suitable for both single-site and batch sensitivity analysis workflows.
34
+
35
+ The function is commonly used as a forward process in sensitivity or perturbation analysis, and can be chained with net radiation calculations prior to running the PM-JPL model.
36
+
37
+ Expected Input DataFrame Columns:
38
+ - 'NDVI': Normalized Difference Vegetation Index (required)
39
+ - 'ST_C': Surface temperature in Celsius (required)
40
+ - 'albedo': Surface albedo (required)
41
+ - 'Ta_C' or 'Ta': Air temperature in Celsius (required)
42
+ - 'RH': Relative humidity (0-1, required)
43
+ - 'Rn_Wm2': Net radiation (W/m^2, required)
44
+ - 'time_UTC': Time in UTC (required)
45
+ - 'geometry': Geometry object (optional, will be constructed from 'lat' and 'lon' if missing)
46
+ - 'lat', 'lon': Latitude and longitude (optional, used to construct geometry if needed)
47
+
48
+ The function will attempt to load or compute any missing optional variables using spatial context if possible.
49
+
50
+ Returns:
51
+ DataFrame: The input DataFrame with PM-JPL model outputs added as columns. Output columns include:
52
+ - 'LE': Total instantaneous evapotranspiration
53
+ - 'LE_canopy': Canopy transpiration
54
+ - 'LE_soil': Soil evaporation
55
+ - 'LE_interception': Interception evaporation
56
+ - 'PET': Potential evapotranspiration
57
+
58
+ Example:
59
+ Suppose you have a CSV file with columns: NDVI, ST_C, albedo, Ta_C, RH, Rn, time_UTC, lat, lon
60
+
61
+ ```python
62
+ import pandas as pd
63
+ from PMJPL.process_PMJPL_table import process_PMJPL_table
64
+
65
+ # Load your data
66
+ df = pd.read_csv('my_input_data.csv')
67
+
68
+ # Process the table and run the PM-JPL model
69
+ output_df = process_PMJPL_table(df)
70
+
71
+ # The output DataFrame will have new columns: 'LE', 'LE_canopy', 'LE_soil', 'LE_interception', 'PET'
72
+ print(output_df.head())
73
+ ```
74
+
75
+ Notes:
76
+ - If any required columns are missing, a KeyError will be raised.
77
+ - If geometry is not provided, latitude and longitude columns are required to construct spatial context.
78
+ - All input columns should be numeric and of compatible shape.
79
+ - This function is suitable for batch-processing site-level or point data tables for ET partitioning and for use in sensitivity analysis workflows.
80
+ """
81
+ logger.info("starting PM-JPL table processing")
82
+
83
+ # Extract and typecast surface temperature (ST_C) and NDVI
84
+ ST_C = np.array(input_df.ST_C).astype(np.float64)
85
+ emissivity = np.array(input_df.emissivity).astype(np.float64)
86
+ NDVI = np.array(input_df.NDVI).astype(np.float64)
87
+
88
+ # Mask NDVI values below threshold (0.06) as NaN
89
+ NDVI = np.where(NDVI > 0.06, NDVI, np.nan).astype(np.float64)
90
+
91
+ # Extract and typecast albedo
92
+ albedo = np.array(input_df.albedo).astype(np.float64)
93
+
94
+ # Handle air temperature column name differences (Ta_C or Ta)
95
+ if "Ta_C" in input_df:
96
+ Ta_C = np.array(input_df.Ta_C).astype(np.float64)
97
+ elif "Ta" in input_df:
98
+ Ta_C = np.array(input_df.Ta).astype(np.float64)
99
+ else:
100
+ raise KeyError("Input DataFrame must contain either 'Ta_C' or 'Ta' column.")
101
+
102
+ # Extract and typecast relative humidity and net radiation
103
+ RH = np.array(input_df.RH).astype(np.float64)
104
+
105
+ if "SWin_Wm2" in input_df:
106
+ SWin_Wm2 = np.array(input_df.SWin_Wm2).astype(np.float64)
107
+ else:
108
+ SWin_Wm2 = None
109
+
110
+ if "Rn_Wm2" in input_df:
111
+ Rn_Wm2 = np.array(input_df.Rn_Wm2).astype(np.float64)
112
+ else:
113
+ Rn_Wm2 = None
114
+
115
+ if "Rn_daily_Wm2" in input_df:
116
+ Rn_daylight_Wm2 = np.array(input_df.Rn_daily_Wm2).astype(np.float64)
117
+ else:
118
+ Rn_daylight_Wm2 = None
119
+
120
+ if "Tmin_C" in input_df:
121
+ Tmin_C = np.array(input_df.Tmin_C).astype(np.float64)
122
+ else:
123
+ Tmin_C = None
124
+
125
+ if "elevation_km" in input_df:
126
+ elevation_km = np.array(input_df.elevation_km).astype(np.float64)
127
+ else:
128
+ elevation_km = None
129
+
130
+ if "IGBP" in input_df:
131
+ IGBP = np.array(input_df.IGBP).astype(np.int8)
132
+ else:
133
+ IGBP = None
134
+
135
+ # --- Handle geometry and time columns ---
136
+ import pandas as pd
137
+ from rasters import MultiPoint, WGS84
138
+ from shapely.geometry import Point
139
+
140
+ def ensure_geometry(df):
141
+ if "geometry" in df:
142
+ if isinstance(df.geometry.iloc[0], str):
143
+ def parse_geom(s):
144
+ s = s.strip()
145
+ if s.startswith("POINT"):
146
+ coords = s.replace("POINT", "").replace("(", "").replace(")", "").strip().split()
147
+ return Point(float(coords[0]), float(coords[1]))
148
+ elif "," in s:
149
+ coords = [float(c) for c in s.split(",")]
150
+ return Point(coords[0], coords[1])
151
+ else:
152
+ coords = [float(c) for c in s.split()]
153
+ return Point(coords[0], coords[1])
154
+ df = df.copy()
155
+ df['geometry'] = df['geometry'].apply(parse_geom)
156
+ return df
157
+
158
+ input_df = ensure_geometry(input_df)
159
+
160
+ logger.info("started extracting geometry from PM-JPL input table")
161
+
162
+ if "geometry" in input_df:
163
+ # Convert Point objects to coordinate tuples for MultiPoint
164
+ if hasattr(input_df.geometry.iloc[0], "x") and hasattr(input_df.geometry.iloc[0], "y"):
165
+ coords = [(pt.x, pt.y) for pt in input_df.geometry]
166
+ geometry = MultiPoint(coords, crs=WGS84)
167
+ else:
168
+ geometry = MultiPoint(input_df.geometry, crs=WGS84)
169
+ elif "lat" in input_df and "lon" in input_df:
170
+ lat = np.array(input_df.lat).astype(np.float64)
171
+ lon = np.array(input_df.lon).astype(np.float64)
172
+ geometry = MultiPoint(x=lon, y=lat, crs=WGS84)
173
+ else:
174
+ raise KeyError("Input DataFrame must contain either 'geometry' or both 'lat' and 'lon' columns.")
175
+
176
+ logger.info("completed extracting geometry from PM-JPL input table")
177
+
178
+ logger.info("started extracting time from PM-JPL input table")
179
+ time_UTC = pd.to_datetime(input_df.time_UTC).tolist()
180
+ logger.info("completed extracting time from PM-JPL input table")
181
+
182
+ # --- Pass time and geometry to the model ---
183
+ results = PMJPL(
184
+ geometry=geometry,
185
+ NDVI=NDVI,
186
+ Ta_C=Ta_C,
187
+ ST_C=ST_C,
188
+ emissivity=emissivity,
189
+ RH=RH,
190
+ Rn_Wm2=Rn_Wm2,
191
+ Rn_daylight_Wm2=Rn_daylight_Wm2,
192
+ SWin_Wm2=SWin_Wm2,
193
+ albedo=albedo,
194
+ Tmin_C=Tmin_C,
195
+ IGBP=IGBP,
196
+ elevation_km=elevation_km,
197
+ time_UTC=time_UTC,
198
+ upscale_to_daylight=upscale_to_daylight,
199
+ regenerate_net_radiation=regenerate_net_radiation
200
+ )
201
+
202
+ output_df = input_df.copy()
203
+ for key, value in results.items():
204
+ output_df[key] = value
205
+
206
+ logger.info("PM-JPL table processing complete")
207
+
208
+ return output_df
@@ -0,0 +1,40 @@
1
+ import pandas as pd
2
+
3
+ from sun_angles import SHA_deg_from_DOY_lat, daylight_from_SHA, sunrise_from_SHA
4
+ from verma_net_radiation import daylight_Rn_integration_verma
5
+ from daylight_evapotranspiration import daylight_ET_from_daily_LE
6
+
7
+ from meteorology_conversion import celcius_to_kelvin
8
+
9
+ def process_daily_ET_table(input_df: pd.DataFrame) -> pd.DataFrame:
10
+ hour_of_day = input_df.hour_of_day
11
+ DOY = input_df.doy
12
+ lat = input_df.lat
13
+ LE = input_df.LE
14
+ Rn = input_df.Rn
15
+ EF = LE / Rn
16
+
17
+ SHA_deg = SHA_deg_from_DOY_lat(DOY=DOY, latitude=lat)
18
+ sunrise_hour = sunrise_from_SHA(SHA_deg)
19
+ daylight_hours = daylight_from_SHA(SHA_deg)
20
+
21
+ Rn_daylight = daylight_Rn_integration_verma(
22
+ Rn=Rn,
23
+ hour_of_day=hour_of_day,
24
+ DOY=DOY,
25
+ lat=lat,
26
+ sunrise_hour=sunrise_hour,
27
+ daylight_hours=daylight_hours
28
+ )
29
+
30
+ LE_daylight = EF * Rn_daylight
31
+ ET = daylight_ET_from_daily_LE(LE_daylight, daylight_hours)
32
+
33
+ output_df = input_df.copy()
34
+ output_df["EF"] = EF
35
+ output_df["sunrise_hour"] = sunrise_hour
36
+ output_df["daylight_hours"] = daylight_hours
37
+ output_df["Rn_daylight"] = Rn_daylight
38
+ output_df["ET"] = ET
39
+
40
+ return output_df
PMJPL/transpiration.py CHANGED
@@ -8,8 +8,8 @@ from .constants import GAMMA_PA
8
8
  def calculate_transpiration(
9
9
  delta_Pa: Union[Raster, np.ndarray],
10
10
  Ac: Union[Raster, np.ndarray],
11
- rho: Union[Raster, np.ndarray],
12
- Cp: Union[Raster, np.ndarray],
11
+ rho_kgm3: Union[Raster, np.ndarray],
12
+ Cp_Jkg: Union[Raster, np.ndarray],
13
13
  VPD_Pa: Union[Raster, np.ndarray],
14
14
  FVC: Union[Raster, np.ndarray],
15
15
  ra: Union[Raster, np.ndarray],
@@ -34,11 +34,11 @@ def calculate_transpiration(
34
34
  Returns:
35
35
  Union[Raster, np.ndarray]: transpiration (LEc) in mm/day.
36
36
  """
37
- numerator = (delta_Pa * Ac + (rho * Cp * FVC * VPD_Pa / ra)) * (1.0 - fwet)
37
+ numerator = (delta_Pa * Ac + (rho_kgm3 * Cp_Jkg * FVC * VPD_Pa / ra)) * (1.0 - fwet)
38
38
  denominator = delta_Pa + gamma_Pa * (1.0 + (rs / ra))
39
39
  LEc = numerator / denominator
40
40
 
41
41
  # fill transpiration with zero
42
- LEc = rt.where(rt.isnan(LEc), 0.0, LEc)
42
+ LEc = rt.where(np.isnan(LEc), 0.0, LEc)
43
43
 
44
44
  return LEc
PMJPL/verify.py ADDED
@@ -0,0 +1,77 @@
1
+ def verify() -> bool:
2
+ """
3
+ Verifies the correctness of the PT-JPL model implementation by comparing
4
+ its outputs to a reference dataset.
5
+
6
+ This function loads a known input table and the corresponding expected output table.
7
+ It runs the model on the input data, then compares the resulting outputs to the
8
+ reference outputs for key variables using strict numerical tolerances. If all
9
+ outputs match within tolerance, the function returns True. Otherwise, it prints
10
+ which column failed and returns False.
11
+
12
+ Returns:
13
+ bool: True if all model outputs match the reference outputs within tolerance, False otherwise.
14
+ """
15
+ import pandas as pd
16
+ import numpy as np
17
+ from .ECOv002_calval_PMJPL_inputs import load_ECOv002_calval_PMJPL_inputs
18
+ from .process_PMJPL_table import process_PMJPL_table
19
+ import os
20
+
21
+ # Load input and output tables
22
+ input_df = load_ECOv002_calval_PMJPL_inputs()
23
+ module_dir = os.path.dirname(os.path.abspath(__file__))
24
+ output_file_path = os.path.join(module_dir, "ECOv002-cal-val-PM-JPL-outputs.csv")
25
+ output_df = pd.read_csv(output_file_path)
26
+
27
+ # Run the model on the input table
28
+ model_df = process_PMJPL_table(input_df)
29
+
30
+ # Columns to compare (model outputs)
31
+ output_columns = [
32
+ "G_Wm2",
33
+ # "Rn_soil_Wm2",
34
+ # "LE_soil_Wm2",
35
+ # "Rn_canopy_Wm2",
36
+ # "PET_Wm2",
37
+ # "LE_canopy_Wm2",
38
+ # "LE_interception_Wm2",
39
+ "LE_Wm2"
40
+ ]
41
+
42
+ # Compare each output column and collect mismatches
43
+ mismatches = []
44
+ for col in output_columns:
45
+ if col not in model_df or col not in output_df:
46
+ mismatches.append((col, 'missing_column', None))
47
+ continue
48
+ model_vals = model_df[col].values
49
+ ref_vals = output_df[col].values
50
+ # Use numpy allclose for floating point comparison
51
+ if not np.allclose(model_vals, ref_vals, rtol=1e-5, atol=1e-8, equal_nan=True):
52
+ # Find indices where values differ
53
+ diffs = np.abs(model_vals - ref_vals)
54
+ max_diff = np.nanmax(diffs)
55
+ idxs = np.where(~np.isclose(model_vals, ref_vals, rtol=1e-5, atol=1e-8, equal_nan=True))[0]
56
+ mismatch_info = {
57
+ 'indices': idxs.tolist(),
58
+ 'model_values': model_vals[idxs].tolist(),
59
+ 'ref_values': ref_vals[idxs].tolist(),
60
+ 'diffs': diffs[idxs].tolist(),
61
+ 'max_diff': float(max_diff)
62
+ }
63
+ mismatches.append((col, 'value_mismatch', mismatch_info))
64
+ if mismatches:
65
+ print("Verification failed. Details:")
66
+ for col, reason, info in mismatches:
67
+ if reason == 'missing_column':
68
+ print(f" Missing column: {col}")
69
+ elif reason == 'value_mismatch':
70
+ print(f" Mismatch in column: {col}")
71
+ print(f" Max difference: {info['max_diff']}")
72
+ print(f" Indices off: {info['indices']}")
73
+ print(f" Model values: {info['model_values']}")
74
+ print(f" Reference values: {info['ref_values']}")
75
+ print(f" Differences: {info['diffs']}")
76
+ return False
77
+ return True
PMJPL/version.py ADDED
@@ -0,0 +1,4 @@
1
+ from os.path import join, abspath, dirname
2
+
3
+ from importlib.metadata import version
4
+ __version__ = version("PM-JPL")
@@ -18,4 +18,5 @@ def calculate_wet_canopy_resistance(
18
18
  :param fwet: relative surface wetness
19
19
  :return: wet canopy resistance
20
20
  """
21
+ # print(f"conductance: {conductance.shape}, LAI: {LAI.shape}, fwet: {fwet.shape}")
21
22
  return rt.clip(1.0 / rt.clip(conductance * LAI * fwet, 1.0 / max_resistance, None), min_resistance, max_resistance)
@@ -7,10 +7,10 @@ from .constants import GAMMA_PA
7
7
  def calculate_wet_soil_evaporation(
8
8
  delta_Pa: Union[Raster, np.ndarray],
9
9
  Asoil: Union[Raster, np.ndarray],
10
- rho: Union[Raster, np.ndarray],
11
- Cp: Union[Raster, np.ndarray],
10
+ rho_kgm3: Union[Raster, np.ndarray],
11
+ Cp_Jkg: Union[Raster, np.ndarray],
12
12
  FVC: Union[Raster, np.ndarray],
13
- VPD: Union[Raster, np.ndarray],
13
+ VPD_Pa: Union[Raster, np.ndarray],
14
14
  ras: Union[Raster, np.ndarray],
15
15
  fwet: Union[Raster, np.ndarray],
16
16
  rtot: Union[Raster, np.ndarray],
@@ -29,7 +29,7 @@ def calculate_wet_soil_evaporation(
29
29
  :param gamme: gamma constant (default: GAMMA)
30
30
  :return: wet soil evaporation in watts per square meter
31
31
  """
32
- numerator = (delta_Pa * Asoil + rho * Cp * (1.0 - FVC) * VPD / ras) * fwet
32
+ numerator = (delta_Pa * Asoil + rho_kgm3 * Cp_Jkg * (1.0 - FVC) * VPD_Pa / ras) * fwet
33
33
  denominator = delta_Pa + gamma_Pa * rtot / ras
34
34
  LE_soil_wet = numerator / denominator
35
35
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PM-JPL
3
- Version: 1.2.1
3
+ Version: 1.7.0
4
4
  Summary: JPL implementation of the MOD16 evapotranspiration algorithm for high resolution instantaneous remote sensing imagery
5
5
  Author-email: Gregory Halverson <gregory.h.halverson@jpl.nasa.gov>, Qiaozhen Mu <qiaozhen@ntsg.umt.edu>, Maosheng Zhao <zhao@ntsg.umt.edu>, "Steven W. Running" <swr@ntsg.umt.edu>, "Claire S. Villanueva-Weeks" <claire.s.villanueva-weeks@jpl.gov>
6
6
  Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/PM-JPL
@@ -9,28 +9,25 @@ Classifier: Operating System :: OS Independent
9
9
  Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
- Requires-Dist: affine
13
- Requires-Dist: astropy
14
- Requires-Dist: geopandas
15
- Requires-Dist: h5py
16
- Requires-Dist: keras
17
- Requires-Dist: matplotlib
12
+ Requires-Dist: carlson-fractional-vegetation-cover
13
+ Requires-Dist: carlson-leaf-area-index
14
+ Requires-Dist: check-distribution
15
+ Requires-Dist: daylight-evapotranspiration>=1.2.1
16
+ Requires-Dist: ECOv002-CMR>=1.0.5
17
+ Requires-Dist: ECOv002-granules>=1.0.3
18
+ Requires-Dist: ECOv003-granules
19
+ Requires-Dist: GEOS5FP>=1.1.1
20
+ Requires-Dist: MCD12C1_2019_v006
21
+ Requires-Dist: meteorology-conversion
22
+ Requires-Dist: NASADEM
18
23
  Requires-Dist: numpy
19
24
  Requires-Dist: pandas
20
- Requires-Dist: pillow
21
- Requires-Dist: pygeos
22
- Requires-Dist: pyproj
23
- Requires-Dist: pyresample
24
- Requires-Dist: rasterio
25
- Requires-Dist: rasters
26
- Requires-Dist: requests
27
- Requires-Dist: scikit-image
28
- Requires-Dist: scipy
29
- Requires-Dist: shapely
30
- Requires-Dist: six
31
- Requires-Dist: sun-angles
32
- Requires-Dist: tensorflow
33
- Requires-Dist: urllib3
25
+ Requires-Dist: priestley-taylor
26
+ Requires-Dist: PTJPL>=1.5.1
27
+ Requires-Dist: rasters>=1.11.0
28
+ Requires-Dist: SEBAL-soil-heat-flux
29
+ Requires-Dist: sun-angles>=1.3.0
30
+ Requires-Dist: verma-net-radiation>=1.8.0
34
31
  Provides-Extra: dev
35
32
  Requires-Dist: build; extra == "dev"
36
33
  Requires-Dist: pytest>=6.0; extra == "dev"
@@ -0,0 +1,42 @@
1
+ PMJPL/ECOv002-cal-val-PM-JPL-inputs.csv,sha256=8U5x7QZI6qklpCNdEWuAt19RS2BtYjWDDgG1D49CREY,1060696
2
+ PMJPL/ECOv002-cal-val-PM-JPL-outputs.csv,sha256=i2nEvXaZqRX7IlXyd2BkDK1clrVcTcDGqV1QoMG30qI,1623539
3
+ PMJPL/ECOv002-static-tower-PM-JPL-inputs.csv,sha256=2ckj9kpAS8QSLets0jegf1G8Z8mSYjYT80k12kDQZlc,14915
4
+ PMJPL/ECOv002_calval_PMJPL_inputs.py,sha256=c0tqGN-oJWoQUGZq5s3EdllrgkOQxMOBwdit_5a8s3U,608
5
+ PMJPL/ECOv002_static_tower_PMJPL_inputs.py,sha256=nvMOIqZtJPKs--YHH4IyxAjfOGVv4BH8EriKMJolFek,618
6
+ PMJPL/PMJPL.py,sha256=8YXZpOT181YYWnSal0-yQliKkJev1OZKR2Qdx_vK2t0,3554
7
+ PMJPL/PMJPL_parameter_from_IGBP.py,sha256=3mzk957afEp0jBKWxpSDhzm33ZGkG_5RCyijlpQTlwk,2537
8
+ PMJPL/VPD_factor.py,sha256=ezHMnnMPGNBt6ZTyiwsTEyMr8fwre6JDsHQea68P7T0,970
9
+ PMJPL/__init__.py,sha256=jke4us8XYaXqU4fL97WYb7Ot1kdD4hV_2NyqFchUh6Q,91
10
+ PMJPL/calculate_gamma.py,sha256=1rrIe9gz3VvkzaLBURSQDke3drIHnqoyBOdNfyvxhaM,5900
11
+ PMJPL/canopy_aerodynamic_resistance.py,sha256=eU5x8riEROyynsMiWDHR_sDCeDexsWDlhuU69tUvC2s,8490
12
+ PMJPL/canopy_conductance.py,sha256=ow7Egm_2wu2Zu5WxkESaUABf7UQA0ldmyPe5Wf4FQ2Y,4814
13
+ PMJPL/closed_minimum_temperature.py,sha256=kMnu9vXK0qDDfyWk5-gdbEkLWzcn28Hz_NplMkyeiUE,461
14
+ PMJPL/closed_vapor_pressure_deficit.py,sha256=v-bihgWfvaklR6f1jkb7XT4hIDk04O2zC08b8QmVJAQ,463
15
+ PMJPL/constants.py,sha256=kFu_YZDpDHvKTOuxICaUV-VCf-B8S_2ZQ79gx2rjzjg,890
16
+ PMJPL/correctance_factor.py,sha256=bVsu23oeTJoeMe-SIsH6cdFyATsLhgwvQUz79yf6kcQ,3181
17
+ PMJPL/generate_PMJPL_inputs.py,sha256=YxConMMvqC1HQdg04fOZIqGLp_-CXzR4SRhiTSm4S8o,13523
18
+ PMJPL/interception.py,sha256=2kDqMefH_kzMO9mLVY9BPFSoJE4K0pkQCXwrSi2CR9c,1605
19
+ PMJPL/leaf_conductance_to_evaporated_water.py,sha256=04_w2Davv3LXqaeSOd_fwRp0wuG4rrjOXBGY4bwvw9U,467
20
+ PMJPL/leaf_conductance_to_sensible_heat.py,sha256=nNQliqi0bMRFqXXva4s05nJnhdhgv1PGLVHlwPK3IuA,3602
21
+ PMJPL/maximum_boundary_layer_resistance.py,sha256=fy2r8B3Ucs9mqI8JYLjS6BYL03iSAIPHQc-ZCd4UUlA,464
22
+ PMJPL/minimum_boundary_layer_resistance.py,sha256=sYbLK_nPrnSD8LMMr_phULaAoBJk-5Nx1AT_D5q70ww,464
23
+ PMJPL/minimum_temperature_factor.py,sha256=bHET3MLD9MlGwd8d5l0DR-4AY1emwJ1Eoqv7FxmZCWk,1585
24
+ PMJPL/mod16.csv,sha256=oq-4b7dCqIrxTX2RcHjZMIZuqE8qThkW3bykqNpw18c,849
25
+ PMJPL/model.py,sha256=RwRd8DHIEM4H33fopO9T0XLVVv06ADwoH_TZjlb2894,26866
26
+ PMJPL/open_minimum_temperature.py,sha256=m03AU4REmcPEcWDyixSkNPYGgG_YAfGeYAg6LwPXjZY,457
27
+ PMJPL/open_vapor_pressure_deficit.py,sha256=d5Q1EqOCOZE9QKDU0k1trSujHytz_QeRehHHjwhUI5I,459
28
+ PMJPL/potential_soil_evaporation.py,sha256=O8bHGM4ARqSXJDHu6IWneSEy5qScenotGR3uawijGWg,1829
29
+ PMJPL/potential_stomatal_conductance.py,sha256=MlK9_GATJ_yMr2YEqp_8wz6y4chWufrUEO9xrRaQqGs,456
30
+ PMJPL/process_PMJPL_table.py,sha256=sGM3Q45p94DwLgo9RIKpt2XzZ6ElLoluibGtXzpfCE0,8266
31
+ PMJPL/process_daily_ET_table.py,sha256=xwyWvsSNSswNDX-CYljJaHsDz5ktWPsDCvjuxQ3tJ2w,1202
32
+ PMJPL/soil_moisture_constraint.py,sha256=URUGsE1_p2P1dVQ8qKRcKb93hahYWtAmYqoassRu-PI,667
33
+ PMJPL/transpiration.py,sha256=lhRm2A0yxrlJxDw2he-k3PhE7G-sBsVvAMKo5fmh7wU,1998
34
+ PMJPL/verify.py,sha256=NJ4YFWnil-1WnVt7OmNoDDaqVyn6ghzWGGFyIqN_bss,3216
35
+ PMJPL/version.py,sha256=7e7k9a2pQYZR4Fyhl6UDY8NQ1lFi5ChR0Zk9Mpl45BU,115
36
+ PMJPL/wet_canopy_resistance.py,sha256=Eape5EBJuoP-IRvD0SWHJKCJWhzm03vcG72OZVYqt_Q,879
37
+ PMJPL/wet_soil_evaporation.py,sha256=mw9JG1oXfVdSWnVRLP2SPd53mSRK96b8k3E1zf0ci7A,1440
38
+ pm_jpl-1.7.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
39
+ pm_jpl-1.7.0.dist-info/METADATA,sha256=EZvJWQw-dmMYhm36Vkk8qkt8OEmspTrzC7O3ZXFxiHo,4399
40
+ pm_jpl-1.7.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
41
+ pm_jpl-1.7.0.dist-info/top_level.txt,sha256=YaAFwdYHUxfUW08hiuFquUEdDGrsYn1duuMMjJKh0Ws,6
42
+ pm_jpl-1.7.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
PMJPL/MCD12C1/MCD12C1.py DELETED
@@ -1,10 +0,0 @@
1
- from os.path import join, abspath, dirname
2
- import numpy as np
3
- import rasters as rt
4
- from rasters import Raster, RasterGeometry
5
-
6
- def load_MCD12C1_IGBP(geometry: RasterGeometry = None) -> Raster:
7
- filename = join(abspath(dirname(__file__)), "MCD12C1.A2019001.006.2020220162300.tif")
8
- image = rt.Raster.open(filename, geometry=geometry, resampling="nearest")
9
-
10
- return image
PMJPL/MCD12C1/__init__.py DELETED
@@ -1 +0,0 @@
1
- from .MCD12C1 import *
PMJPL/SEBAL/SEBAL.py DELETED
@@ -1,45 +0,0 @@
1
- import numpy as np
2
- import pandas as pd
3
- import rasters as rt
4
-
5
- def calculate_soil_heat_flux(Rn: np.ndarray, ST_C: np.ndarray, NDVI: np.ndarray, albedo: np.ndarray) -> np.ndarray:
6
- """
7
- This function calculates the soil heat flux (G) in the Surface Energy Balance Algorithm for Land (SEBAL) model.
8
- The formula used in the function is a simplification of the more complex relationship between these variables in the energy balance at the surface.
9
-
10
- Parameters:
11
- Rn (np.ndarray): Net radiation at the surface.
12
- ST_C (np.ndarray): Surface temperature in Celsius.
13
- NDVI (np.ndarray): Normalized Difference Vegetation Index, indicating the presence and condition of vegetation.
14
- albedo (np.ndarray): Measure of the diffuse reflection of solar radiation.
15
-
16
- Returns:
17
- np.ndarray: The soil heat flux (G), a key component in the energy balance.
18
-
19
- Reference:
20
- "Evapotranspiration Estimation Based on Remote Sensing and the SEBAL Model in the Bosten Lake Basin of China" [^1^][1]
21
- """
22
- # Empirical coefficients used in the calculation
23
- coeff1 = 0.0038
24
- coeff2 = 0.0074
25
-
26
- # Vegetation cover correction factor
27
- NDVI_correction = 1 - 0.98 * NDVI ** 4
28
-
29
- # Calculation of the soil heat flux (G)
30
- G = Rn * ST_C * (coeff1 + coeff2 * albedo) * NDVI_correction
31
-
32
- G = rt.clip(G, 0, None)
33
-
34
- return G
35
-
36
- def process_SEBAL_G_table(input_df: pd.DataFrame) -> pd.DataFrame:
37
- Rn = input_df.Rn
38
- ST_C = input_df.ST_C
39
- NDVI = input_df.NDVI
40
- albedo = input_df.albedo
41
- G = calculate_soil_heat_flux(Rn=Rn, ST_C=ST_C, NDVI=NDVI, albedo=albedo)
42
- output_df = input_df.copy()
43
- output_df["G"] = G
44
-
45
- return output_df
PMJPL/SEBAL/__init__.py DELETED
@@ -1 +0,0 @@
1
- from .SEBAL import *
@@ -1 +0,0 @@
1
- from .downscaling import *