BESS-JPL 1.17.0__py3-none-any.whl → 1.18.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.

Potentially problematic release.


This version of BESS-JPL might be problematic. Click here for more details.

@@ -5,11 +5,14 @@ import rasters as rt
5
5
  from dateutil import parser
6
6
  from pandas import DataFrame
7
7
 
8
+ from .constants import *
8
9
  from .model import BESS_JPL
9
10
 
10
11
  logger = logging.getLogger(__name__)
11
12
 
12
- def process_BESS_table(input_df: DataFrame) -> DataFrame:
13
+ def process_BESS_table(
14
+ input_df: DataFrame,
15
+ C4_fraction_scale_factor: float = C4_FRACTION_SCALE_FACTOR) -> DataFrame:
13
16
  ST_C = np.array(input_df.ST_C).astype(np.float64)
14
17
  NDVI = np.array(input_df.NDVI).astype(np.float64)
15
18
 
@@ -23,28 +26,185 @@ def process_BESS_table(input_df: DataFrame) -> DataFrame:
23
26
  Ta_C = np.array(input_df.Ta).astype(np.float64)
24
27
 
25
28
  RH = np.array(input_df.RH).astype(np.float64)
26
- # Rn = np.array(input_df.Rn).astype(np.float64)
27
- # Topt = np.array(input_df.Topt).astype(np.float64)
28
- # fAPARmax = np.array(input_df.fAPARmax).astype(np.float64)
29
-
30
- # fAPARmax = np.where(fAPARmax == 0, np.nan, fAPARmax).astype(np.float64)
31
-
32
- # if "G" in input_df:
33
- # G = np.array(input_df.G).astype(np.float64)
34
- # else:
35
- # G = calculate_SEBAL_soil_heat_flux(
36
- # Rn=Rn,
37
- # ST_C=ST_C,
38
- # NDVI=NDVI,
39
- # albedo=albedo
40
- # ).astype(np.float64)
29
+
30
+ if "elevation_km" in input_df:
31
+ elevation_km = np.array(input_df.elevation_km).astype(np.float64)
32
+ else:
33
+ elevation_km = None
34
+
35
+ if "NDVI_minimum" in input_df:
36
+ NDVI_minimum = np.array(input_df.NDVI_minimum).astype(np.float64)
37
+ else:
38
+ NDVI_minimum = None
39
+
40
+ if "NDVI_maximum" in input_df:
41
+ NDVI_maximum = np.array(input_df.NDVI_maximum).astype(np.float64).astype(np.float64)
42
+ else:
43
+ NDVI_maximum = None
44
+
45
+ if "C4_fraction" in input_df:
46
+ C4_fraction = np.array(input_df.C4_fraction).astype(np.float64)
47
+ else:
48
+ C4_fraction = None
49
+
50
+ if "carbon_uptake_efficiency" in input_df:
51
+ carbon_uptake_efficiency = np.array(input_df.carbon_uptake_efficiency).astype(np.float64)
52
+ else:
53
+ carbon_uptake_efficiency = None
54
+
55
+ if "kn" in input_df:
56
+ kn = np.array(input_df.kn).astype(np.float64)
57
+ else:
58
+ kn = None
59
+
60
+ if "peakVCmax_C3" in input_df:
61
+ peakVCmax_C3 = np.array(input_df.peakVCmax_C3).astype(np.float64)
62
+ else:
63
+ peakVCmax_C3 = None
64
+
65
+ if "peakVCmax_C4" in input_df:
66
+ peakVCmax_C4 = np.array(input_df.peakVCmax_C4).astype(np.float64)
67
+ else:
68
+ peakVCmax_C4 = None
69
+
70
+ if "ball_berry_slope_C3" in input_df:
71
+ ball_berry_slope_C3 = np.array(input_df.ball_berry_slope_C3).astype(np.float64)
72
+ else:
73
+ ball_berry_slope_C3 = None
41
74
 
75
+ if "ball_berry_slope_C4" in input_df:
76
+ ball_berry_slope_C4 = np.array(input_df.ball_berry_slope_C4).astype(np.float64)
77
+ else:
78
+ ball_berry_slope_C4 = None
79
+
80
+ if "ball_berry_intercept_C3" in input_df:
81
+ ball_berry_intercept_C3 = np.array(input_df.ball_berry_intercept_C3).astype(np.float64)
82
+ else:
83
+ ball_berry_intercept_C3 = None
84
+
85
+ if "KG_climate" in input_df:
86
+ KG_climate = np.array(input_df.KG_climate)
87
+ else:
88
+ KG_climate = None
89
+
90
+ if "CI" in input_df:
91
+ CI = np.array(input_df.CI).astype(np.float64)
92
+ else:
93
+ CI = None
94
+
95
+ if "canopy_height_meters" in input_df:
96
+ canopy_height_meters = np.array(input_df.canopy_height_meters).astype(np.float64)
97
+ else:
98
+ canopy_height_meters = None
99
+
100
+ if "COT" in input_df:
101
+ COT = np.array(input_df.COT).astype(np.float64)
102
+ else:
103
+ COT = None
104
+
105
+ if "AOT" in input_df:
106
+ AOT = np.array(input_df.AOT).astype(np.float64)
107
+ else:
108
+ AOT = None
109
+
110
+ if "Ca" in input_df:
111
+ Ca = np.array(input_df.Ca).astype(np.float64)
112
+ else:
113
+ Ca = None
114
+
115
+ if "wind_speed_mps" in input_df:
116
+ wind_speed_mps = np.array(input_df.wind_speed_mps).astype(np.float64)
117
+ else:
118
+ wind_speed_mps = None
119
+
120
+ if "vapor_gccm" in input_df:
121
+ vapor_gccm = np.array(input_df.vapor_gccm).astype(np.float64)
122
+ else:
123
+ vapor_gccm = None
124
+
125
+ if "ozone_cm" in input_df:
126
+ ozone_cm = np.array(input_df.ozone_cm).astype(np.float64)
127
+ else:
128
+ ozone_cm = None
129
+
130
+ # --- Handle geometry and time columns ---
131
+ import pandas as pd
132
+ from rasters import MultiPoint, WGS84
133
+ from shapely.geometry import Point
134
+
135
+ def ensure_geometry(df):
136
+ if "geometry" in df:
137
+ if isinstance(df.geometry.iloc[0], str):
138
+ def parse_geom(s):
139
+ s = s.strip()
140
+ if s.startswith("POINT"):
141
+ coords = s.replace("POINT", "").replace("(", "").replace(")", "").strip().split()
142
+ return Point(float(coords[0]), float(coords[1]))
143
+ elif "," in s:
144
+ coords = [float(c) for c in s.split(",")]
145
+ return Point(coords[0], coords[1])
146
+ else:
147
+ coords = [float(c) for c in s.split()]
148
+ return Point(coords[0], coords[1])
149
+ df = df.copy()
150
+ df['geometry'] = df['geometry'].apply(parse_geom)
151
+ return df
152
+
153
+ input_df = ensure_geometry(input_df)
154
+
155
+ logger.info("started extracting geometry from PT-JPL-SM input table")
156
+
157
+ if "geometry" in input_df:
158
+ # Convert Point objects to coordinate tuples for MultiPoint
159
+ if hasattr(input_df.geometry.iloc[0], "x") and hasattr(input_df.geometry.iloc[0], "y"):
160
+ coords = [(pt.x, pt.y) for pt in input_df.geometry]
161
+ geometry = MultiPoint(coords, crs=WGS84)
162
+ else:
163
+ geometry = MultiPoint(input_df.geometry, crs=WGS84)
164
+ elif "lat" in input_df and "lon" in input_df:
165
+ lat = np.array(input_df.lat).astype(np.float64)
166
+ lon = np.array(input_df.lon).astype(np.float64)
167
+ geometry = MultiPoint(x=lon, y=lat, crs=WGS84)
168
+ else:
169
+ raise KeyError("Input DataFrame must contain either 'geometry' or both 'lat' and 'lon' columns.")
170
+
171
+ logger.info("completed extracting geometry from PT-JPL-SM input table")
172
+
173
+ logger.info("started extracting time from PT-JPL-SM input table")
174
+ time_UTC = pd.to_datetime(input_df.time_UTC).tolist()
175
+ logger.info("completed extracting time from PT-JPL-SM input table")
176
+
42
177
  results = BESS_JPL(
178
+ geometry=geometry,
179
+ time_UTC=time_UTC,
43
180
  ST_C=ST_C,
44
181
  albedo=albedo,
45
182
  NDVI=NDVI,
46
183
  Ta_C=Ta_C,
47
- RH=RH
184
+ RH=RH,
185
+ elevation_km=elevation_km,
186
+ NDVI_minimum=NDVI_minimum,
187
+ NDVI_maximum=NDVI_maximum,
188
+ C4_fraction=C4_fraction,
189
+ carbon_uptake_efficiency=carbon_uptake_efficiency,
190
+ kn=kn,
191
+ peakVCmax_C3=peakVCmax_C3,
192
+ peakVCmax_C4=peakVCmax_C4,
193
+ ball_berry_slope_C3=ball_berry_slope_C3,
194
+ ball_berry_slope_C4=ball_berry_slope_C4,
195
+ ball_berry_intercept_C3=ball_berry_intercept_C3,
196
+ KG_climate=KG_climate,
197
+ CI=CI,
198
+ canopy_height_meters=canopy_height_meters,
199
+ COT=COT,
200
+ AOT=AOT,
201
+ Ca=Ca,
202
+ wind_speed_mps=COT * 0 + 7.4,
203
+ vapor_gccm=vapor_gccm,
204
+ ozone_cm=ozone_cm,
205
+ albedo_visible=albedo,
206
+ albedo_NIR=albedo,
207
+ C4_fraction_scale_factor=C4_fraction_scale_factor
48
208
  )
49
209
 
50
210
  output_df = input_df.copy()
BESS_JPL/verify.py ADDED
@@ -0,0 +1,72 @@
1
+ def verify() -> bool:
2
+ """
3
+ Verifies the correctness of the PT-JPL-SM 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_BESS_inputs import load_ECOv002_calval_BESS_inputs
18
+ from .process_BESS_table import process_BESS_table
19
+ import os
20
+
21
+ # Load input and output tables
22
+ input_df = load_ECOv002_calval_BESS_inputs()
23
+ module_dir = os.path.dirname(os.path.abspath(__file__))
24
+ output_file_path = os.path.join(module_dir, "ECOv002-cal-val-BESS-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_BESS_table(input_df)
29
+
30
+ # Columns to compare (model outputs)
31
+ output_columns = [
32
+ "G_Wm2",
33
+ "Rn_Wm2",
34
+ "LE_Wm2"
35
+ ]
36
+
37
+ # Compare each output column and collect mismatches
38
+ mismatches = []
39
+ for col in output_columns:
40
+ if col not in model_df or col not in output_df:
41
+ mismatches.append((col, 'missing_column', None))
42
+ continue
43
+ model_vals = model_df[col].values
44
+ ref_vals = output_df[col].values
45
+ # Use numpy allclose for floating point comparison
46
+ if not np.allclose(model_vals, ref_vals, rtol=1e-5, atol=1e-8, equal_nan=True):
47
+ # Find indices where values differ
48
+ diffs = np.abs(model_vals - ref_vals)
49
+ max_diff = np.nanmax(diffs)
50
+ idxs = np.where(~np.isclose(model_vals, ref_vals, rtol=1e-5, atol=1e-8, equal_nan=True))[0]
51
+ mismatch_info = {
52
+ 'indices': idxs.tolist(),
53
+ 'model_values': model_vals[idxs].tolist(),
54
+ 'ref_values': ref_vals[idxs].tolist(),
55
+ 'diffs': diffs[idxs].tolist(),
56
+ 'max_diff': float(max_diff)
57
+ }
58
+ mismatches.append((col, 'value_mismatch', mismatch_info))
59
+ if mismatches:
60
+ print("Verification failed. Details:")
61
+ for col, reason, info in mismatches:
62
+ if reason == 'missing_column':
63
+ print(f" Missing column: {col}")
64
+ elif reason == 'value_mismatch':
65
+ print(f" Mismatch in column: {col}")
66
+ print(f" Max difference: {info['max_diff']}")
67
+ print(f" Indices off: {info['indices']}")
68
+ print(f" Model values: {info['model_values']}")
69
+ print(f" Reference values: {info['ref_values']}")
70
+ print(f" Differences: {info['diffs']}")
71
+ return False
72
+ return True
BESS_JPL/version.py ADDED
@@ -0,0 +1,3 @@
1
+ from importlib.metadata import version
2
+
3
+ __version__ = version("BESS-JPL")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BESS-JPL
3
- Version: 1.17.0
3
+ Version: 1.18.0
4
4
  Summary: Breathing Earth System Simulator (BESS) Gross Primary Production (GPP) and Evapotranspiration (ET) Model Python
5
5
  Author-email: Gregory Halverson <gregory.h.halverson@jpl.nasa.gov>
6
6
  Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/BESS-JPL
@@ -10,15 +10,18 @@ Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: check-distribution
13
- Requires-Dist: FLiESANN>=1.5.0
13
+ Requires-Dist: ECOv002-calval-tables>=1.6.0
14
+ Requires-Dist: FLiESANN>=1.6.1
14
15
  Requires-Dist: gedi-canopy-height>=1.1.0
15
16
  Requires-Dist: GEOS5FP>=1.1.1
16
- Requires-Dist: koppengeiger>=1.0.4
17
- Requires-Dist: MODISCI>=1.3.0
17
+ Requires-Dist: koppengeiger>=1.1.0
18
+ Requires-Dist: MODISCI>=1.4.0
19
+ Requires-Dist: monte-carlo-sensitivity
18
20
  Requires-Dist: NASADEM>=1.3.0
19
21
  Requires-Dist: numpy
20
- Requires-Dist: rasters
21
- Requires-Dist: solar-apparent-time>=1.3.2
22
+ Requires-Dist: rasters>=1.9.0
23
+ Requires-Dist: seaborn
24
+ Requires-Dist: solar-apparent-time>=1.5.3
22
25
  Provides-Extra: dev
23
26
  Requires-Dist: build; extra == "dev"
24
27
  Requires-Dist: pytest>=6.0; extra == "dev"
@@ -1,15 +1,21 @@
1
- BESS_JPL/BESS_JPL.py,sha256=CTby93_HwhONe2NOlt-8DuqDH7jL9mAc3euuzbouANg,1365
1
+ BESS_JPL/BESS_JPL.py,sha256=QfeMnPtlfjgugCYJVhpySP2e4OsD_FClTgS2WactAsA,1582
2
2
  BESS_JPL/C3_photosynthesis.py,sha256=-ormDycaWttCOoYJdOoOV_zlM6n0nxkgXxj7fyH_mIs,2760
3
3
  BESS_JPL/C4_fraction.jpeg,sha256=ECaEYWA8MnNd5z0Yq3beeUCR93KtTDxM8MrH-YYon3Y,2746
4
4
  BESS_JPL/C4_fraction.tif,sha256=DFS-J08JCjnf0hi_T_dFridAegVTiU3FGyMBjS9yc1w,25446
5
+ BESS_JPL/C4_fraction.tif.aux.xml,sha256=5Ytn5UtZdGWQUKNmg_kiOU2zRQsm82sY7bq-j3TS1QY,358
5
6
  BESS_JPL/C4_photosynthesis.py,sha256=kITtT2kW1JkYY5v8wpMUhmxfBaJMr_uGk6go5P6w0ag,2029
7
+ BESS_JPL/ECOv002-cal-val-BESS-JPL-inputs.csv,sha256=YwBaHvBXLDhCZySwwfnyhv962j5IH_YxhHex115aVKs,1247281
8
+ BESS_JPL/ECOv002-cal-val-BESS-JPL-outputs.csv,sha256=qxJElgt1PvZYNaQsa1XaeIDcZ-1WtbCXPjblX8-jLFk,1423082
9
+ BESS_JPL/ECOv002-static-tower-BESS-JPL-inputs.csv,sha256=LAVetRyuvyznQXPW7xkzBR0_UC35SCNFg6ukH5016dI,35585
10
+ BESS_JPL/ECOv002_calval_BESS_inputs.py,sha256=UaGhhPQOqly0JuiM86aJAtagNYvovQViKW1tdAv5ElA,606
11
+ BESS_JPL/ECOv002_static_tower_BESS_inputs.py,sha256=pJEindmNfm1wY0Ih-O4WyZtCwWF-NZdh7VK_aE2S-sY,621
6
12
  BESS_JPL/FVC_from_NDVI.py,sha256=LYI1ESsrO8UxlPOr_dxJYDeUsrwjVAwrV_OhL-tcAWc,567
7
13
  BESS_JPL/LAI_from_NDVI.py,sha256=uwV1voc2q45hmsSHzIhehA3ZHLivzSeoST-ht9ecSIE,783
8
14
  BESS_JPL/NDVI_maximum.jpeg,sha256=6kc0-eE_WOU31bUztdjoWsZ3ZmqHAG-ZwS7lMiZyBGA,763946
9
15
  BESS_JPL/NDVI_maximum.tif,sha256=yRgdpB-xoVNMveIUapCrnGAwHX7WkH6pVXu4HECuFfc,19664541
10
16
  BESS_JPL/NDVI_minimum.jpeg,sha256=AfpRU8PikwExBJ2IdcFKewg6aTcp3gx3y3EQMzeA7vg,644871
11
17
  BESS_JPL/NDVI_minimum.tif,sha256=wPV0CFfQSLfiTAEDlwIAI857pA7aw0iqWhVlOAZFsJg,18762242
12
- BESS_JPL/__init__.py,sha256=ilP6rzKsWrha-lUAtgU-k5oSDCZt0s0hEOOJzyPmsGc,217
18
+ BESS_JPL/__init__.py,sha256=UaK3mWbc5jNoA4kNzIWF9h7CQs64O_chKrpuxNkH48o,95
13
19
  BESS_JPL/ball_berry_intercept_C3.jpeg,sha256=c8jlRD3i8cnWXQNRUfyNTuObEpptcG6p6kQd2RGMK70,634200
14
20
  BESS_JPL/ball_berry_intercept_C3.tif,sha256=O7ATUIKtXcIjkUI2lloTiHAgvRk4gWyWjYGh5Xc_CRY,1026642
15
21
  BESS_JPL/ball_berry_slope_C3.jpeg,sha256=KdFUYG4h1nRNb_eK7w-M2kqChJnhbcGmal9Z2Jos1nI,225597
@@ -23,11 +29,11 @@ BESS_JPL/canopy_shortwave_radiation.py,sha256=TzDGFFBVHcZ3s5v-R9pQYeW9PPvYDQ168V
23
29
  BESS_JPL/carbon_uptake_efficiency.jpeg,sha256=wz2cLk_4-DRdF71xR2c-cx1MsE0CH-EDWjs2pDJcRF0,704540
24
30
  BESS_JPL/carbon_uptake_efficiency.tif,sha256=Nnc8h2ImU6_sip0WzDh3q0BRoBe-CfCbwzDtYo5BSog,819269
25
31
  BESS_JPL/carbon_water_fluxes.py,sha256=T1E1AHne3lYvkEoasqlM4oLxoSWLWmeJxy1BzrVdPUw,12987
26
- BESS_JPL/constants.py,sha256=tOYubq17dnLVKhtTI0CJUQAEeROE4KeSBpOVBqZprUU,131
32
+ BESS_JPL/constants.py,sha256=pkIfSXa8lOpolcjOPXK_NwGcw3IVNgCp9uOmiOT_R3w,203
27
33
  BESS_JPL/interpolate_C3_C4.py,sha256=adIyk03Yt6SJ_jFCUm6ivK9QpZYw10C3wJKpx1O56Pk,394
28
34
  BESS_JPL/kn.jpeg,sha256=wsI40jabT9_iPpQmOxd3GZzfAN7oZ2DubiARmULFUS0,854097
29
35
  BESS_JPL/kn.tif,sha256=nZAIAms7LRDVsadxa4z8yRqOudWu3AjJSm6l1T3ywuY,819988
30
- BESS_JPL/load_C4_fraction.py,sha256=g1NpACWIKhq9TtABqHLGvxxSFwMZ9vb27nanVE8Sf1A,493
36
+ BESS_JPL/load_C4_fraction.py,sha256=qemgzsyDVKvzvql1p_N7g23L8KYvbLw-qxjWlUC3p_Q,624
31
37
  BESS_JPL/load_NDVI_maximum.py,sha256=CADnbDULc5qW0oR7UgNN9pM0oplz7QMf8Ki09_fbj-o,401
32
38
  BESS_JPL/load_NDVI_minimum.py,sha256=RZaLXKxJe2ViEvj76_l-YSLIMyjo54nP4oAe-NEvACs,401
33
39
  BESS_JPL/load_ball_berry_intercept_C3.py,sha256=6auXZHej127rhUCFXe7vdEg9C4CosFSHLTcdqKs06Ls,388
@@ -38,16 +44,21 @@ BESS_JPL/load_kn.py,sha256=39RkQkkdmey0IPInziJI0kSiI8alRk5Hz42pzm-qihU,346
38
44
  BESS_JPL/load_peakVCmax_C3.py,sha256=A-Wg4PjPgogfpRsAMo6ZNEXM7G3KbnF7ZRV3k_jTtEA,401
39
45
  BESS_JPL/load_peakVCmax_C4.py,sha256=iVmJTitkMkB4AjfjanAi3yv1lWO4sSF6J7cjuRmaCwU,401
40
46
  BESS_JPL/meteorology.py,sha256=xIPQnIk9wGJPy8esJyxn35d6c_7UyiNuWTJXDnuSwf8,6995
41
- BESS_JPL/model.py,sha256=clIpapmr_i-oZAvc-4TdUDIHumoehsaYJWZNmVhBBuw,22993
47
+ BESS_JPL/model.py,sha256=WurAzTeyWhoc_LwCZNqm5iDfkWNVWWlHDp5EveNjE1A,24365
42
48
  BESS_JPL/peakVCmax_C3.jpeg,sha256=nVvwLx8JyRhtm5lWRW93HLz0mInshEdOCJ1tAdcFqa8,1006133
43
49
  BESS_JPL/peakVCmax_C3.tif,sha256=ax6wCOPc_ovgJJl9YRjPWNY13OCGlzs2djXqwob_U7A,1583598
44
50
  BESS_JPL/peakVCmax_C4.jpeg,sha256=s7dhpcD573KW9Se4mejDeSzbSHqPtQY2EL6KJKt7ZIo,961497
45
51
  BESS_JPL/peakVCmax_C4.tif,sha256=EST4_yy-HHYmazIv--RePL_bhLejMWql6hoV9EE38ok,1556928
46
- BESS_JPL/process_BESS_table.py,sha256=TYsDEZNELGdUc0xkB1MRQIDkVonNL5d49q7Zl1iaJvo,1472
52
+ BESS_JPL/process_BESS_table.py,sha256=CiI_huBJ2R578unMqDMb9dPQUyDuscEd_vqK0-6PHXM,6941
47
53
  BESS_JPL/soil_energy_balance.py,sha256=5TKDVkKYJ8jhjuiwbRva_03fi_0gTM0IAKbSm4WhWnY,944
48
- BESS_JPL/version.txt,sha256=-Q2F9kDoBjnO1g1dZcNwbI4pu99GLJDvdHkxmPdU4JU,6
49
- bess_jpl-1.17.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
50
- bess_jpl-1.17.0.dist-info/METADATA,sha256=3906m_oz7FCNHjyaq8IKvdIFLw1oBZQdz-5Zrc6R6MQ,6347
51
- bess_jpl-1.17.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
52
- bess_jpl-1.17.0.dist-info/top_level.txt,sha256=GaKnzt-BBktYn1o-w4Qzh_jHxse4Y3ACOxZQrB2ufhc,9
53
- bess_jpl-1.17.0.dist-info/RECORD,,
54
+ BESS_JPL/verify.py,sha256=kNXSsiK1UD_gPRNvb-485q1lpq2wUQ8VEtXJGZRKTL8,3075
55
+ BESS_JPL/version.py,sha256=oPRGJspBmrZU7gLv4L_3WPX3aGwee0inKgfzcyvkXcc,74
56
+ bess_jpl-1.18.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
57
+ examples/processing_BESS_with_rasters_and_default_parameters.py,sha256=XDlRJMAoQXFSXS0q7z5l_nMeOxFwsLasyJtrNCm-Kak,2600
58
+ tests/test_import_BESS_JPL.py,sha256=7BNKnKdIMbfUb4JVogXCXgiO7XzK9WF54q2aGXWfv3I,62
59
+ tests/test_import_dependencies.py,sha256=sPZY_G7UaENPzTQUgms5TLsQONxtqVlS7QJlTT1izW8,360
60
+ tests/test_verify.py,sha256=T7l9QIpKwon2bdPmfpL2Ac1B8-a9-WcxLGFDdTWnGVk,151
61
+ bess_jpl-1.18.0.dist-info/METADATA,sha256=YBxpMuwrqQc20HYQrIGA9QbO4HCC7yTy7-0raIsY2HY,6460
62
+ bess_jpl-1.18.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
63
+ bess_jpl-1.18.0.dist-info/top_level.txt,sha256=NVdh5Igf1HghE2bBB-rc1uYmB5woir8Q-uIjwjjuu2k,24
64
+ bess_jpl-1.18.0.dist-info/RECORD,,
@@ -0,0 +1,3 @@
1
+ BESS_JPL
2
+ examples
3
+ tests
@@ -0,0 +1,102 @@
1
+ # %% [markdown]
2
+ # # Running BESS for an ECOSTRESS Scene
3
+ #
4
+ # This is an example of running the artificial neural network emulator of the Breathing Earth Systems Simulator (BESS) corresponding to an ECOsystem Spaceborne Thermal Radiometer Experiment on Space Station (ECOSTRESS) scene.
5
+
6
+ # %%
7
+ from dateutil import parser
8
+ from matplotlib.colors import LinearSegmentedColormap
9
+ from solar_apparent_time import UTC_to_solar
10
+ import rasters as rt
11
+ from BESS_JPL import BESS_JPL
12
+ import logging
13
+
14
+
15
+ # %% [markdown]
16
+ # Here's an example ECOSTRESS surface temperature scene.
17
+
18
+ # %%
19
+ ST_filename = "ECOv002_L2T_LSTE_34366_004_11SPS_20240728T204025_0712_01_LST.tif"
20
+ ST_cmap = "bwr"
21
+ ST_C = rt.Raster.open(ST_filename, cmap=ST_cmap) - 273.15
22
+ ST_C
23
+
24
+ # %% [markdown]
25
+ # Let's get the acquisition time of the scene.
26
+
27
+ # %%
28
+ time_UTC = parser.parse(ST_filename.split("_")[6])
29
+ geometry = ST_C.geometry
30
+ longitude = geometry.centroid_latlon.x
31
+ latitude = geometry.centroid_latlon.y
32
+ time_solar = UTC_to_solar(time_UTC, longitude)
33
+ doy_solar = time_solar.timetuple().tm_yday
34
+ hour_of_day_solar = time_solar.hour + time_solar.minute / 60 + time_solar.second / 3600
35
+ print(f"{time_UTC:%Y-%m-%d %H:%M:%S} UTC")
36
+ print(f"{time_solar:%Y-%m-%d %H:%M:%S} solar apparent time at longitude {longitude}")
37
+ print(f"day of year {doy_solar} at longitude {longitude}")
38
+ print(f"hour of day {hour_of_day_solar} at longitude {longitude}")
39
+
40
+ # %%
41
+ albedo_filename = "ECOv002_L2T_STARS_11SPS_20240728_0712_01_albedo.tif"
42
+ albedo_cmap = LinearSegmentedColormap.from_list(name="albedo", colors=["black", "white"])
43
+ albedo = rt.Raster.open(albedo_filename, cmap=albedo_cmap)
44
+ albedo
45
+
46
+ # %%
47
+ NDVI_filename = "ECOv002_L2T_STARS_11SPS_20240728_0712_01_NDVI.tif"
48
+ NDVI = rt. Raster.open(NDVI_filename)
49
+
50
+ NDVI_COLORMAP_ABSOLUTE = LinearSegmentedColormap.from_list(
51
+ name="NDVI",
52
+ colors=[
53
+ (0, "#0000ff"),
54
+ (0.4, "#000000"),
55
+ (0.5, "#745d1a"),
56
+ (0.6, "#e1dea2"),
57
+ (0.8, "#45ff01"),
58
+ (1, "#325e32")
59
+ ]
60
+ )
61
+
62
+ NDVI.cmap = NDVI_COLORMAP_ABSOLUTE
63
+ NDVI
64
+
65
+ # %%
66
+ Ta_filename = "ECOv002_L3T_MET_34366_004_11SPS_20240728T204025_0712_01_Ta.tif"
67
+ Ta_C = rt.Raster.open(Ta_filename)
68
+ Ta_C.cmap = "bwr"
69
+ Ta_C
70
+
71
+ # %%
72
+ RH_filename = "ECOv002_L3T_MET_34366_004_11SPS_20240728T204025_0712_01_RH.tif"
73
+ RH = rt.Raster.open(RH_filename)
74
+ RH.cmap = "bwr_r"
75
+ RH
76
+
77
+ # %%
78
+ BESS_results = BESS_JPL(
79
+ hour_of_day=hour_of_day_solar,
80
+ day_of_year=doy_solar,
81
+ geometry=geometry,
82
+ time_UTC=time_UTC,
83
+ ST_C=ST_C,
84
+ NDVI=NDVI,
85
+ albedo=albedo,
86
+ Ta_C=Ta_C,
87
+ RH=RH
88
+ )
89
+
90
+ # %%
91
+ BESS_results["GPP"]
92
+
93
+ # %%
94
+ BESS_results["Rn"]
95
+
96
+ # %%
97
+ BESS_results["LE"]
98
+
99
+ # %%
100
+
101
+
102
+
@@ -0,0 +1,2 @@
1
+ def test_import_BESS_JPL():
2
+ from BESS_JPL import BESS_JPL
@@ -0,0 +1,17 @@
1
+ import pytest
2
+
3
+ # List of dependencies
4
+ dependencies = [
5
+ "check_distribution",
6
+ "FLiESANN",
7
+ "gedi_canopy_height",
8
+ "GEOS5FP",
9
+ "MODISCI",
10
+ "numpy",
11
+ "rasters"
12
+ ]
13
+
14
+ # Generate individual test functions for each dependency
15
+ @pytest.mark.parametrize("dependency", dependencies)
16
+ def test_dependency_import(dependency):
17
+ __import__(dependency)
tests/test_verify.py ADDED
@@ -0,0 +1,5 @@
1
+ import pytest
2
+ from BESS_JPL import verify
3
+
4
+ def test_verify():
5
+ assert verify(), "Model verification failed: outputs do not match expected results."
BESS_JPL/version.txt DELETED
@@ -1 +0,0 @@
1
- 1.15.0
@@ -1 +0,0 @@
1
- BESS_JPL