STIC-JPL 1.1.0__py3-none-any.whl → 1.2.1__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 STIC-JPL might be problematic. Click here for more details.

@@ -0,0 +1,26 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ import rasters as rt
4
+ from rasters import Raster
5
+
6
+ KPAR = 0.5
7
+ MIN_FIPAR = 0.0
8
+ MAX_FIPAR = 1.0
9
+ MIN_LAI = 0.0
10
+ MAX_LAI = 10.0
11
+
12
+ def FVC_from_NDVI(NDVI: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
13
+ """
14
+ Convert Normalized Difference Vegetation Index (NDVI) to Fractional Vegetation Cover (FVC).
15
+
16
+ Parameters:
17
+ NDVI (Union[Raster, np.ndarray]): Input NDVI data.
18
+
19
+ Returns:
20
+ Union[Raster, np.ndarray]: Converted FVC data.
21
+ """
22
+ NDVIv = 0.52 # +- 0.03
23
+ NDVIs = 0.04 # +- 0.03
24
+ FVC = rt.clip((NDVI - NDVIs) / (NDVIv - NDVIs), 0.0, 1.0)
25
+
26
+ return FVC
@@ -9,22 +9,6 @@ MAX_FIPAR = 1.0
9
9
  MIN_LAI = 0.0
10
10
  MAX_LAI = 10.0
11
11
 
12
- def FVC_from_NDVI(NDVI: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
13
- """
14
- Convert Normalized Difference Vegetation Index (NDVI) to Fractional Vegetation Cover (FVC).
15
-
16
- Parameters:
17
- NDVI (Union[Raster, np.ndarray]): Input NDVI data.
18
-
19
- Returns:
20
- Union[Raster, np.ndarray]: Converted FVC data.
21
- """
22
- NDVIv = 0.52 # +- 0.03
23
- NDVIs = 0.04 # +- 0.03
24
- FVC = rt.clip((NDVI - NDVIs) / (NDVIv - NDVIs), 0.0, 1.0)
25
-
26
- return FVC
27
-
28
12
  def LAI_from_NDVI(
29
13
  NDVI: Union[Raster, np.ndarray],
30
14
  min_fIPAR: float = MIN_FIPAR,
STIC_JPL/STIC_JPL.py CHANGED
@@ -1,370 +1,3 @@
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
1
+ from .model import *
2
+ from .generate_STIC_inputs import generate_STIC_inputs
3
+ from .process_STIC_table import process_STIC_table
@@ -0,0 +1,11 @@
1
+ from typing import Union
2
+ import numpy as np
3
+ from rasters import Raster
4
+
5
+ def celcius_to_kelvin(T_C: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
6
+ """
7
+ convert temperature in celsius to kelvin.
8
+ :param T_C: temperature in celsius
9
+ :return: temperature in kelvin
10
+ """
11
+ return T_C + 273.15
@@ -0,0 +1,65 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+ from dateutil import parser
5
+ from pandas import DataFrame
6
+ from rasters import Point
7
+ from sentinel_tiles import sentinel_tiles
8
+ from solar_apparent_time import UTC_to_solar
9
+ from SEBAL_soil_heat_flux import calculate_SEBAL_soil_heat_flux
10
+
11
+ from .model import STIC_JPL, MAX_ITERATIONS, USE_VARIABLE_ALPHA
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ def generate_STIC_inputs(STIC_inputs_from_calval_df: DataFrame) -> DataFrame:
16
+ """
17
+ STIC_inputs_from_claval_df:
18
+ Pandas DataFrame containing the columns: tower, lat, lon, time_UTC, albedo, elevation_km
19
+ return:
20
+ Pandas DataFrame containing the columns: tower, lat, lon, time_UTC, doy, albedo, elevation_km, AOT, COT, vapor_gccm, ozone_cm, SZA, KG
21
+ """
22
+ # output_rows = []
23
+ STIC_inputs_df = STIC_inputs_from_calval_df.copy()
24
+
25
+ hour_of_day = []
26
+ doy = []
27
+ Topt = []
28
+ fAPARmax = []
29
+
30
+ for i, input_row in STIC_inputs_from_calval_df.iterrows():
31
+ tower = input_row.tower
32
+ lat = input_row.lat
33
+ lon = input_row.lon
34
+ time_UTC = input_row.time_UTC
35
+ albedo = input_row.albedo
36
+ elevation_km = input_row.elevation_km
37
+ logger.info(f"collecting STIC inputs for tower {tower} lat {lat} lon {lon} time {time_UTC} UTC")
38
+ time_UTC = parser.parse(str(time_UTC))
39
+ time_solar = UTC_to_solar(time_UTC, lon)
40
+ hour_of_day.append(time_solar.hour)
41
+ doy.append(time_UTC.timetuple().tm_yday)
42
+ date_UTC = time_UTC.date()
43
+ tile = sentinel_tiles.toMGRS(lat, lon)[:5]
44
+ tile_grid = sentinel_tiles.grid(tile=tile, cell_size=70)
45
+ rows, cols = tile_grid.shape
46
+ row, col = tile_grid.index_point(Point(lon, lat))
47
+ geometry = tile_grid[max(0, row - 1):min(row + 2, rows - 1),
48
+ max(0, col - 1):min(col + 2, cols - 1)]
49
+
50
+ if not "hour_of_day" in STIC_inputs_df.columns:
51
+ STIC_inputs_df["hour_of_day"] = hour_of_day
52
+
53
+ if not "doy" in STIC_inputs_df.columns:
54
+ STIC_inputs_df["doy"] = doy
55
+
56
+ if not "Topt" in STIC_inputs_df.columns:
57
+ STIC_inputs_df["Topt"] = Topt
58
+
59
+ if not "fAPARmax" in STIC_inputs_df.columns:
60
+ STIC_inputs_df["fAPARmax"] = fAPARmax
61
+
62
+ if "Ta" in STIC_inputs_df and "Ta_C" not in STIC_inputs_df:
63
+ STIC_inputs_df.rename({"Ta": "Ta_C"}, inplace=True)
64
+
65
+ return STIC_inputs_df
@@ -5,8 +5,7 @@ import rasters as rt
5
5
 
6
6
  from rasters import Raster
7
7
 
8
- from .vegetation_conversion.vegetation_conversion import FVC_from_NDVI
9
- from .soil_heat_flux import calculate_SEBAL_soil_heat_flux
8
+ from SEBAL_soil_heat_flux import calculate_SEBAL_soil_heat_flux
10
9
 
11
10
  from .constants import *
12
11
  from .soil_moisture_initialization import initialize_soil_moisture
@@ -5,7 +5,7 @@ import rasters as rt
5
5
 
6
6
  from rasters import Raster
7
7
 
8
- from .soil_heat_flux import calculate_SEBAL_soil_heat_flux
8
+ from SEBAL_soil_heat_flux import calculate_SEBAL_soil_heat_flux
9
9
 
10
10
  from .constants import *
11
11
  from .canopy_air_stream import calculate_canopy_air_stream_vapor_pressure
STIC_JPL/model.py ADDED
@@ -0,0 +1,369 @@
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
+ import rasters as rt
11
+ from GEOS5FP import GEOS5FP
12
+ from solar_apparent_time import solar_day_of_year_for_area, solar_hour_of_day_for_area
13
+ from SEBAL_soil_heat_flux import calculate_SEBAL_soil_heat_flux
14
+
15
+ from rasters import Raster, RasterGeometry
16
+
17
+ from .constants import *
18
+ from .closure import STIC_closure
19
+ from .soil_moisture_initialization import initialize_soil_moisture
20
+ from .soil_moisture_iteration import iterate_soil_moisture
21
+ from .net_radiation import calculate_net_longwave_radiation
22
+ from .initialize_with_solar import initialize_with_solar
23
+ from .canopy_air_stream import calculate_canopy_air_stream_vapor_pressure
24
+ from .initialize_without_solar import initialize_without_solar
25
+ from .iterate_with_solar import iterate_with_solar
26
+ from .iterate_without_solar import iterate_without_solar
27
+ from .root_zone_initialization import calculate_root_zone_moisture
28
+ from .FVC_from_NDVI import FVC_from_NDVI
29
+ from .LAI_from_NDVI import LAI_from_NDVI
30
+ from .celcius_to_kelvin import celcius_to_kelvin
31
+
32
+ from .timer import Timer
33
+
34
+ __author__ = 'Kaniska Mallick, Madeleine Pascolini-Campbell, Gregory Halverson'
35
+
36
+ logger = logging.getLogger(__name__)
37
+
38
+ def STIC_JPL(
39
+ ST_C: Union[Raster, np.ndarray],
40
+ emissivity: Union[Raster, np.ndarray],
41
+ NDVI: Union[Raster, np.ndarray],
42
+ albedo: Union[Raster, np.ndarray],
43
+ Rn_Wm2: Union[Raster, np.ndarray],
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,
50
+ RH: Union[Raster, np.ndarray] = None,
51
+ G: Union[Raster, np.ndarray] = None,
52
+ G_method: str = DEFAULT_G_METHOD,
53
+ SM: Union[Raster, np.ndarray] = None,
54
+ Rg_Wm2: Union[Raster, np.ndarray] = None,
55
+ FVC: Union[Raster, np.ndarray] = None,
56
+ LAI: Union[Raster, np.ndarray] = None,
57
+ elevation_m: Union[Raster, np.ndarray] = None,
58
+ delta_hPa: Union[Raster, np.ndarray] = None,
59
+ gamma_hPa: Union[Raster, np.ndarray, float] = GAMMA_HPA,
60
+ rho_kgm3: Union[Raster, np.ndarray] = RHO_KGM3,
61
+ Cp_Jkg: Union[Raster, np.ndarray] = CP_JKG,
62
+ alpha: float = PT_ALPHA,
63
+ LE_convergence_target: float = LE_CONVERGENCE_TARGET_WM2,
64
+ max_iterations: int = MAX_ITERATIONS,
65
+ diagnostic_directory: str = None,
66
+ show_distributions: bool = SHOW_DISTRIBUTIONS,
67
+ use_variable_alpha: bool = USE_VARIABLE_ALPHA) -> Dict[str, Union[Raster, np.ndarray]]:
68
+ results = {}
69
+
70
+ if geometry is None and isinstance(ST_C, Raster):
71
+ geometry = ST_C.geometry
72
+
73
+ if GEOS5FP_connection is None:
74
+ GEOS5FP_connection = GEOS5FP()
75
+
76
+ if (day_of_year is None or hour_of_day is None) and time_UTC is not None and geometry is not None:
77
+ day_of_year = solar_day_of_year_for_area(time_UTC=time_UTC, geometry=geometry)
78
+ hour_of_day = solar_hour_of_day_for_area(time_UTC=time_UTC, geometry=geometry)
79
+
80
+ if time_UTC is None and day_of_year is None and hour_of_day is None:
81
+ raise ValueError("no time given between time_UTC, day_of_year, and hour_of_day")
82
+
83
+ diag_kwargs = {
84
+ "show_distributions": show_distributions,
85
+ "output_directory": diagnostic_directory
86
+ }
87
+
88
+ seconds_of_day = hour_of_day * 3600.0
89
+
90
+ # load air temperature in Celsius if not provided
91
+ if Ta_C is None:
92
+ Ta_C = GEOS5FP_connection.Ta_C(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
93
+
94
+ # load relative humidity if not provided
95
+ if RH is None:
96
+ RH = GEOS5FP_connection.RH(time_UTC=time_UTC, geometry=geometry, resampling=resampling)
97
+
98
+ # calculate fraction of vegetation cover if it's not given
99
+ if FVC is None:
100
+ FVC = FVC_from_NDVI(NDVI)
101
+
102
+ # calculate leaf area index if it's not given
103
+ if LAI is None:
104
+ LAI = LAI_from_NDVI(NDVI)
105
+
106
+ # saturation air pressure in hPa
107
+ SVP_hPa = 6.13753 * (np.exp((17.27 * Ta_C) / (Ta_C + 237.3)))
108
+
109
+ # calculate delta term if it's not given
110
+ if delta_hPa is None:
111
+ # slope of saturation vapor pressure to air temperature (hpa/K)
112
+ delta_hPa = 4098 * SVP_hPa / (Ta_C + 237.3) ** 2
113
+
114
+ Ta_K = celcius_to_kelvin(Ta_C)
115
+
116
+ # actual vapor pressure at TA (hpa/K)
117
+ Ea_hPa = SVP_hPa * (RH)
118
+ Ea_Pa = Ea_hPa * 100.0
119
+
120
+ # vapor pressure deficit (hPa)
121
+ VPD_hPa = SVP_hPa - Ea_hPa
122
+
123
+ # swapping in the dew-point calculation from PT-JPL
124
+ Td_C = Ta_C - ((100 - RH * 100) / 5.0)
125
+
126
+ # difference between surface and air temperature (Celsius)
127
+ dTS_C = ST_C - Ta_C
128
+
129
+ # saturation vapor pressure at surface temperature (hPa/K)
130
+ Estar_hPa = 6.13753 * np.exp((17.27 * ST_C) / (ST_C + 237.3))
131
+
132
+ if Rg_Wm2 is None:
133
+ # if G is None and SM is None:
134
+ # raise ValueError("soil heat flux or soil moisture prior required if solar radiation is not given")
135
+
136
+ if G is None:
137
+ G = calculate_SEBAL_soil_heat_flux(
138
+ ST_C=ST_C,
139
+ NDVI=NDVI,
140
+ albedo=albedo,
141
+ Rn=Rn_Wm2,
142
+ )
143
+
144
+ phi_Wm2 = Rn_Wm2 - G
145
+
146
+ # initialize without solar radiation
147
+ SM, SMrz, s1, s3, s33, s44, Ms, Tsd_C, Es_hPa, Ds = initialize_without_solar(
148
+ ST_C = ST_C, # Surface temperature in Celsius
149
+ Ta_C = Ta_C, # Air temperature in Celsius
150
+ dTS = dTS_C, # Temperature difference between surface and air in Celsius
151
+ Td_C = Td_C, # Dewpoint temperature in Celsius
152
+ Ea_hPa = Ea_hPa, # Actual vapor pressure in hPa
153
+ Estar_hPa = Estar_hPa, # Saturation vapor pressure at surface temperature (hPa/K)
154
+ SVP_hPa = SVP_hPa, # Saturation vapor pressure at the surface in hPa
155
+ delta_hPa = delta_hPa, # Slope of the saturation vapor pressure-temperature curve in hPa/K
156
+ phi_Wm2 = phi_Wm2, # Available energy in W/m2
157
+ gamma_hPa = gamma_hPa, # Psychrometric constant in hPa/°C
158
+ alpha = alpha # Priestley-Taylor alpha
159
+ )
160
+ else:
161
+ SM, SMrz, Ms, s1, s3, Ep_PT, Rnsoil, LWnet_Wm2, G, Tsd_C, Ds, Es_hPa, phi_Wm2 = initialize_with_solar(
162
+ seconds_of_day = seconds_of_day, # time of day in seconds since midnight
163
+ Rg_Wm2 = Rg_Wm2, # solar radiation (W/m^2)
164
+ Rn_Wm2 = Rn_Wm2, # net radiation (W/m^2)
165
+ ST_C = ST_C, # surface temperature (Celsius)
166
+ emissivity = emissivity, # emissivity of the surface
167
+ Ta_C = Ta_C, # air temperature (Celsius)
168
+ dTS_C = dTS_C, # surface air temperature difference (Celsius)
169
+ Td_C = Td_C, # dew point temperature (Celsius)
170
+ VPD_hPa = VPD_hPa, # vapor pressure deficit (hPa)
171
+ SVP_hPa = SVP_hPa, # saturation vapor pressure at given air temperature (hPa)
172
+ Ea_hPa = Ea_hPa, # actual vapor pressure at air temperature (hPa)
173
+ Estar_hPa = Estar_hPa, # saturation vapor pressure at surface temperature (hPa)
174
+ delta_hPa = delta_hPa, # slope of saturation vapor pressure to air temperature (hpa/K)
175
+ NDVI=NDVI, # normalized difference vegetation index
176
+ FVC = FVC, # fractional vegetation cover
177
+ LAI = LAI, # leaf area index
178
+ albedo = albedo, # albedo of the surface
179
+ gamma_hPa=gamma_hPa, # psychrometric constant (hPa/°C)
180
+ G_method = DEFAULT_G_METHOD, # method for calculating soil heat flux
181
+ )
182
+
183
+ diagnostic(Ms, "Ms", **diag_kwargs)
184
+
185
+ # STIC analytical equations (convergence on LE)
186
+ gB_ms, gS_ms, dT_C, EF = STIC_closure(
187
+ delta_hPa=delta_hPa,
188
+ phi_Wm2=phi_Wm2,
189
+ Es_hPa=Es_hPa,
190
+ Ea_hPa=Ea_hPa,
191
+ Estar_hPa=Estar_hPa,
192
+ SM=SM,
193
+ gamma_hPa=gamma_hPa,
194
+ rho_kgm3=rho_kgm3,
195
+ Cp_Jkg=Cp_Jkg,
196
+ alpha=alpha
197
+ )
198
+
199
+ gBB = gB_ms
200
+ gSS = gS_ms
201
+ gBB_by_gSS = rt.where(gSS == 0, 0, gBB / gSS)
202
+ gB_by_gS = rt.where(gS_ms == 0, 0, gB_ms / gS_ms)
203
+ dT_C = dT_C
204
+ T0_C = dT_C + Ta_C
205
+
206
+ PET_Wm2 = ((delta_hPa * phi_Wm2 + rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa)) # Penman potential evaporation
207
+
208
+ gR = (4 * SB_SIGMA * (Ta_C + 273) ** 3 * emissivity) / (rho_kgm3 * Cp_Jkg)
209
+ omega = ((delta_hPa / gamma_hPa) + 1) / ((delta_hPa / gamma_hPa) + 1 + gB_by_gS)
210
+ LE_eq = (phi_Wm2 * (delta_hPa / gamma_hPa)) / ((delta_hPa / gamma_hPa) + 1)
211
+ LE_imp = (Cp_Jkg * 0.0289644 / gamma_hPa) * gS_ms * 40 * VPD_hPa
212
+ LE_init = omega * LE_eq + (1 - omega) * LE_imp
213
+ dry = (Ds > VPD_hPa) & (PET_Wm2 > phi_Wm2) & (dTS_C > 0) & (Td_C <= 0)
214
+ omega = rt.where(dry,
215
+ ((delta_hPa / gamma_hPa) + 1 + gR / gB_ms) / ((delta_hPa / gamma_hPa) + 1 + gB_ms / gS_ms + gR / gS_ms + gR / gB_ms),
216
+ omega)
217
+ LE_eq = rt.where(dry, (phi_Wm2 * (delta_hPa / gamma_hPa)) / ((delta_hPa / gamma_hPa) + 1 + gR / gB_ms), LE_eq)
218
+ LE_init = rt.where(dry, omega * LE_eq + (1 - omega * LE_imp), LE_init)
219
+
220
+ # sensible heat flux
221
+ 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))))
222
+
223
+ LE_Wm2_new = LE_init
224
+ LE_Wm2_change = LE_convergence_target
225
+ LE_Wm2_old = LE_Wm2_new
226
+ LE_transpiration_Wm2 = None
227
+ PT_Wm2 = None
228
+ iteration = 1
229
+ LE_Wm2_max_change = 0
230
+ t = Timer()
231
+
232
+ while (np.nanmax(LE_Wm2_change) >= LE_convergence_target and iteration <= max_iterations):
233
+ logger.info(f"running STIC iteration {cl.val(iteration)} / {cl.val(max_iterations)}")
234
+
235
+ if Rg_Wm2 is None:
236
+ SM, SMrz, Ms, s1, e0, e0star, Tsd_C, D0, alphaN = iterate_without_solar(
237
+ LE = LE_Wm2_new, # Latent heat flux (W/m^2)
238
+ PET = PET_Wm2, # Potential evapotranspiration (W/m^2)
239
+ SM = SM,
240
+ ST_C = ST_C, # Surface temperature (°C)
241
+ Ta_C = Ta_C, # Air temperature (°C)
242
+ dTS = dTS_C, # Surface-air temperature difference (°C)
243
+ T0 = T0_C, # Reference temperature (°C)
244
+ gB = gB_ms, # Boundary layer conductance (m/s)
245
+ gS = gS_ms, # Stomatal conductance (m/s)
246
+ Ea_hPa = Ea_hPa, # Actual vapor pressure (hPa)
247
+ Td_C = Td_C, # Dew point temperature (°C)
248
+ VPD_hPa = VPD_hPa, # Vapor pressure deficit (hPa)
249
+ Estar = Estar_hPa, # Saturation vapor pressure at surface temperature (hPa)
250
+ delta = delta_hPa, # Slope of the saturation vapor pressure-temperature curve (hPa/°C)
251
+ phi = phi_Wm2, # available energy (W/m^2)
252
+ Ds = Ds, # Vapor pressure deficit at source (hPa)
253
+ Es = Es_hPa, # Saturation vapor pressure (hPa)
254
+ s3 = s3, # Slope of the saturation vapor pressure and temperature
255
+ s4 = s44, # Slope of the saturation vapor pressure and temperature
256
+ gB_by_gS = gB_by_gS, # Ratio of boundary layer conductance to stomatal conductance
257
+ gamma_hPa = gamma_hPa, # Psychrometric constant (hPa/°C)
258
+ rho_kgm3 = rho_kgm3, # Air density (kg/m^3)
259
+ Cp_Jkg = Cp_Jkg # Specific heat at constant pressure (J/kg/K)
260
+ )
261
+ else:
262
+ SM, G, e0, e0star, D0, alphaN = iterate_with_solar(
263
+ seconds_of_day = seconds_of_day, # Seconds of the day
264
+ ST_C = ST_C, # Soil temperature (°C)
265
+ NDVI = NDVI, # Normalized Difference Vegetation Index
266
+ albedo = albedo, # Albedo
267
+ gB_ms = gB_ms, # boundary layer conductance (m/s)
268
+ gS_ms = gS_ms, # stomatal conductance (m/s)
269
+ LE_Wm2 = LE_Wm2_new, # latent heat flux (W/m^2)
270
+ Rg_Wm2 = Rg_Wm2, # Incoming solar radiation (W/m^2)
271
+ Rn_Wm2 = Rn_Wm2, # Net radiation (W/m^2)
272
+ LWnet_Wm2 = LWnet_Wm2, # Net longwave radiation (W/m^2)
273
+ Ta_C = Ta_C, # Air temperature (°C)
274
+ dTS_C = dTS_C, # Change in soil temperature (°C)
275
+ Td_C = Td_C, # Dew point temperature (°C)
276
+ Tsd_C = Tsd_C, # Soil dew point temperature (°C)
277
+ Ea_hPa = Ea_hPa, # actual vapor pressure (hPa)
278
+ Estar_hPa = Estar_hPa, # saturation vapor pressure at surface temperature (hPa)
279
+ VPD_hPa = VPD_hPa, # Vapor pressure deficit (hPa)
280
+ SVP_hPa = SVP_hPa, # Saturation vapor pressure (hPa)
281
+ delta_hPa = delta_hPa, # Slope of the saturation vapor pressure-temperature curve (hPa/°C)
282
+ phi_Wm2 = phi_Wm2, # Net radiation minus soil heat flux (W/m^2)
283
+ Es_hPa = Es_hPa, # Saturation vapor pressure (hPa)
284
+ s1 = s1, # Soil moisture parameter
285
+ s3 = s3, # Soil moisture parameter
286
+ FVC = FVC, # Fractional canopy cover
287
+ T0_C = T0_C, # Reference temperature (°C)
288
+ gB_by_gS = gB_by_gS, # Ratio of boundary layer conductance to stomatal conductance
289
+ gamma_hPa = gamma_hPa, # Psychrometric constant (hPa/°C)
290
+ rho_kgm3 = rho_kgm3, # Air density (kg/m^3)
291
+ Cp_Jkg = Cp_Jkg, # Specific heat at constant pressure (J/kg/K)
292
+ G_method = "santanello" # Method for calculating soil heat flux
293
+ )
294
+
295
+ if use_variable_alpha:
296
+ alpha = alphaN
297
+ logger.info(f"using variable Priestley-Taylor alpha with mean: {cl.val(np.round(np.nanmean(alpha), 3))}")
298
+
299
+ # re-estimated conductances and states
300
+ gB_ms, gS_ms, dT_C, EF = STIC_closure(
301
+ delta_hPa=delta_hPa, # Slope of the saturation vapor pressure-temperature curve (hPa/°C)
302
+ phi_Wm2=phi_Wm2, # available energy (W/m^2)
303
+ Es_hPa=Es_hPa, # Vapor pressure at the reference height (hPa)
304
+ Ea_hPa=Ea_hPa, # Actual vapor pressure (hPa)
305
+ Estar_hPa=Estar_hPa, # Saturation vapor pressure at the reference height (hPa)
306
+ SM=SM, # Soil moisture (m³/m³)
307
+ gamma_hPa=gamma_hPa, # Psychrometric constant (hPa/°C)
308
+ rho_kgm3=rho_kgm3, # Air density (kg/m³)
309
+ Cp_Jkg=Cp_Jkg, # Specific heat capacity of air (J/kg/°C)
310
+ alpha=alpha # Stability correction factor for conductance
311
+ )
312
+
313
+ gB_by_gS = rt.where(gS_ms == 0, 0, gB_ms / gS_ms)
314
+ T0_C = dT_C + Ta_C
315
+ # latent heat flux
316
+ LE_Wm2_new = ((delta_hPa * phi_Wm2 + rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa * (1 + gB_by_gS)))
317
+ LE_Wm2_new = rt.where(LE_Wm2_new > phi_Wm2, phi_Wm2, LE_Wm2_new)
318
+ # Sensible Heat Flux
319
+ 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))))
320
+ # potential evaporation (Penman)
321
+ PET_Wm2 = ((delta_hPa * phi_Wm2 + rho_kgm3 * Cp_Jkg * gB_ms * VPD_hPa) / (delta_hPa + gamma_hPa))
322
+ # Potential Transpiration
323
+ 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
324
+ # ET PARTITIONING
325
+ LE_soil_Wm2 = rt.clip(SM * PET_Wm2, 0, None)
326
+ LE_transpiration_Wm2 = rt.clip(LE_Wm2_new - LE_soil_Wm2, 0, None)
327
+ # change in latent heat flux estimate
328
+ LE_Wm2_change = np.abs(LE_Wm2_old - LE_Wm2_new)
329
+ LE_Wm2_new = rt.where(np.isnan(LE_Wm2_new), LE_Wm2_old, LE_Wm2_new)
330
+ LE_Wm2_old = LE_Wm2_new
331
+ LE_Wm2_max_change = np.nanmax(LE_Wm2_change)
332
+ logger.info(
333
+ 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)")
334
+
335
+ diagnostic(SM, f"SM_{iteration}", **diag_kwargs)
336
+ diagnostic(G, f"G_{iteration}", **diag_kwargs)
337
+ diagnostic(LE_Wm2_new, f"LE_{iteration}", **diag_kwargs)
338
+
339
+ if LE_Wm2_max_change <= LE_convergence_target:
340
+ 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 ''}")
341
+
342
+ iteration += 1
343
+
344
+ iteration -= 1
345
+ results["LE_max_change"] = LE_Wm2_max_change
346
+ results["iteration"] = iteration
347
+
348
+ LE = LE_Wm2_new
349
+
350
+ results["LE"] = LE
351
+ results["LE_change"] = LE_Wm2_change
352
+ results["LEt"] = LE_transpiration_Wm2
353
+ results["PT"] = PT_Wm2
354
+ results["PET"] = PET_Wm2
355
+ results["G"] = G
356
+
357
+ if isinstance(geometry, RasterGeometry):
358
+ for name, array in results.items():
359
+ try:
360
+ results[name] = Raster(array.reshape(geometry.shape), geometry=geometry)
361
+ except Exception as e:
362
+ pass
363
+
364
+ results["LE"].cmap = ET_COLORMAP
365
+ results["PET"].cmap = ET_COLORMAP
366
+
367
+ warnings.resetwarnings()
368
+
369
+ return results
@@ -0,0 +1,61 @@
1
+ import logging
2
+
3
+ import numpy as np
4
+ from dateutil import parser
5
+ from pandas import DataFrame
6
+ from rasters import Point
7
+ from sentinel_tiles import sentinel_tiles
8
+ from solar_apparent_time import UTC_to_solar
9
+ from SEBAL_soil_heat_flux import calculate_SEBAL_soil_heat_flux
10
+
11
+ from .model import STIC_JPL, MAX_ITERATIONS, USE_VARIABLE_ALPHA
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+ def process_STIC_table(
16
+ input_df: DataFrame,
17
+ max_iterations = MAX_ITERATIONS,
18
+ use_variable_alpha = USE_VARIABLE_ALPHA) -> DataFrame:
19
+ hour_of_day = np.float64(np.array(input_df.hour_of_day))
20
+ lon = np.float64(np.array(input_df.lon))
21
+ ST_C = np.float64(np.array(input_df.ST_C))
22
+ emissivity = np.float64(np.array(input_df.EmisWB))
23
+ NDVI = np.float64(np.array(input_df.NDVI))
24
+ albedo = np.float64(np.array(input_df.albedo))
25
+ Ta_C = np.float64(np.array(input_df.Ta_C))
26
+ RH = np.float64(np.array(input_df.RH))
27
+ Rn = np.float64(np.array(input_df.Rn))
28
+ Rg = np.float64(np.array(input_df.Rg))
29
+
30
+ if "G" in input_df:
31
+ G = np.array(input_df.G)
32
+ else:
33
+ G = calculate_SEBAL_soil_heat_flux(
34
+ Rn=Rn,
35
+ ST_C=ST_C,
36
+ NDVI=NDVI,
37
+ albedo=albedo
38
+ )
39
+
40
+ results = STIC_JPL(
41
+ hour_of_day=hour_of_day,
42
+ # longitude=lon,
43
+ ST_C = ST_C,
44
+ emissivity=emissivity,
45
+ NDVI=NDVI,
46
+ albedo=albedo,
47
+ Ta_C=Ta_C,
48
+ RH=RH,
49
+ Rn_Wm2=Rn,
50
+ G=G,
51
+ # Rg_Wm2=Rg,
52
+ max_iterations=max_iterations,
53
+ use_variable_alpha=use_variable_alpha
54
+ )
55
+
56
+ output_df = input_df.copy()
57
+
58
+ for key, value in results.items():
59
+ output_df[key] = value
60
+
61
+ return output_df
@@ -4,7 +4,8 @@ import numpy as np
4
4
  import rasters as rt
5
5
  from rasters import Raster
6
6
 
7
- from .vegetation_conversion.vegetation_conversion import FVC_from_NDVI
7
+ from .FVC_from_NDVI import FVC_from_NDVI
8
+ from .LAI_from_NDVI import LAI_from_NDVI
8
9
 
9
10
  from .constants import GAMMA_HPA
10
11
  from .root_zone_initialization import calculate_root_zone_moisture
STIC_JPL/version.txt CHANGED
@@ -1 +1 @@
1
- 1.0.4
1
+ 1.2.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: STIC-JPL
3
- Version: 1.1.0
3
+ Version: 1.2.1
4
4
  Summary: Surface Temperature Initiated Closure (STIC) Evapotranspiration Model Python Implementation
5
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
6
  Project-URL: Homepage, https://github.com/JPL-Evapotranspiration-Algorithms/STIC-JPL
@@ -9,13 +9,18 @@ Classifier: Operating System :: OS Independent
9
9
  Requires-Python: >=3.10
10
10
  Description-Content-Type: text/markdown
11
11
  Requires-Dist: colored-logging
12
- Requires-Dist: ECOv002-CMR
13
- Requires-Dist: ECOv002-granules
12
+ Requires-Dist: ECOv002-CMR>=1.0.5
13
+ Requires-Dist: ECOv002-granules>=1.0.3
14
+ Requires-Dist: ECOv003-granules
14
15
  Requires-Dist: GEOS5FP>=1.1.1
16
+ Requires-Dist: monte-carlo-sensitivity
15
17
  Requires-Dist: numpy
16
18
  Requires-Dist: pandas
17
- Requires-Dist: rasters
19
+ Requires-Dist: rasters>=1.4.6
20
+ Requires-Dist: seaborn
21
+ Requires-Dist: SEBAL-soil-heat-flux
18
22
  Requires-Dist: solar-apparent-time
23
+ Requires-Dist: verma-net-radiation>=1.1.0
19
24
  Provides-Extra: dev
20
25
  Requires-Dist: build; extra == "dev"
21
26
  Requires-Dist: pytest>=6.0; extra == "dev"
@@ -24,7 +29,8 @@ Requires-Dist: jupyter; extra == "dev"
24
29
  Requires-Dist: pytest; extra == "dev"
25
30
  Requires-Dist: twine; extra == "dev"
26
31
 
27
- # Surface Temperature Initiated Closure (STIC) Evapotranspiration Model Python Implementation
32
+ # `STIC-JPL`
33
+ ## Surface Temperature Initiated Closure (STIC) Evapotranspiration Model Python Implementation
28
34
 
29
35
  [![CI](https://github.com/JPL-Evapotranspiration-Algorithms/STIC/actions/workflows/ci.yml/badge.svg)](https://github.com/JPL-Evapotranspiration-Algorithms/STIC/actions/workflows/ci.yml)
30
36
 
@@ -63,21 +69,23 @@ NASA Jet Propulsion Laboratory 329G
63
69
 
64
70
  ## Installation
65
71
 
66
- Use the pip package manager to install the `STIC` PyPi package.
72
+ Use the pip package manager to install the `STIC-JPL` PyPi package with dashes in the name.
67
73
 
68
74
  ```
69
- pip install STIC
75
+ pip install STIC-JPL
70
76
  ```
71
77
 
72
78
  ## Usage
73
79
 
74
- Import the `STIC` function from the `STIC` package.
80
+ Import the `STIC_JPL` function from the `STIC_JPL` package with underscores in the name.
75
81
 
76
82
  ```
77
- from STIC import STIC
83
+ from STIC_JPL import STIC_JPL
78
84
  ```
79
85
 
80
- See the [ECOSTRESS example](ECOSTRESS%20Example.ipynb) for usage.
86
+ See the [ECOSTRESS example](ECOSTRESS%20Example.ipynb) notebook for usage.
87
+
88
+ See the [STIC sensitivity](STIC%20Sensitivity.ipynb) notebook for sensitivity analysis.
81
89
 
82
90
  ## References
83
91
 
@@ -0,0 +1,27 @@
1
+ STIC_JPL/FVC_from_NDVI.py,sha256=NCGMu4-XzvYqg7UOqKqNkfqwmV73zipoc09Q1IubNm0,638
2
+ STIC_JPL/LAI_from_NDVI.py,sha256=iqlCkuH53iDrAz_VbMs4y4EwHcNuGGo5nJfJtnmsO70,833
3
+ STIC_JPL/STIC_JPL.py,sha256=qqxz7JOEHgmP_hoz1ZT_66v0WNs9R0SITRt-60rYb3I,127
4
+ STIC_JPL/__init__.py,sha256=FmTp-Ir0Wbae0SbZgxlNIxq48TDrWCev17Gv1hWskxU,216
5
+ STIC_JPL/canopy_air_stream.py,sha256=UYp3l7mt0XH4SqvCGZhRzCIL-v7-Rvxxfm-C81lheSU,1637
6
+ STIC_JPL/celcius_to_kelvin.py,sha256=QZ1gA8BHBHbdlgn1v8tEMG17yY2r-SCBnaHrGXySItg,316
7
+ STIC_JPL/closure.py,sha256=AzWoLnpoZ1MlQAKq0CgpmCMfDGVsp8-hYZ50ry33cic,4031
8
+ STIC_JPL/constants.py,sha256=xIVTsdc3_Rf3EPSgSk1QPjWiojFGn4lf1Swffov3oTM,595
9
+ STIC_JPL/diagnostic.py,sha256=RXjtZbCEJMiTyRuiMGUvekDgO_GcrhXVXvorR0WlAsk,2613
10
+ STIC_JPL/generate_STIC_inputs.py,sha256=Vwuqq-PLiH2IBd1v0Vt2TVbdIRK8D1zU_uMgReCefmQ,2382
11
+ STIC_JPL/initialize_with_solar.py,sha256=P-22Wn8-sdvh2o4LGb9XfbCdXGWcOxdWwMzEYfx2Nfo,3759
12
+ STIC_JPL/initialize_without_solar.py,sha256=M_7QfP2pr2w5sQO7UOW1GPqxpmQkjmsQ526BgYVVALk,4675
13
+ STIC_JPL/iterate_with_solar.py,sha256=T9DezL59xaV63o6f7_ouCGcQwPi1dIvhlJB26TMGuDo,6373
14
+ STIC_JPL/iterate_without_solar.py,sha256=09RERpN6tdZr6ORDhMwEFXwx8941vyn1HMJQ-Q_bY6s,6140
15
+ STIC_JPL/model.py,sha256=PwN4T7Ic-TMz6c0Br39H0QGX4yK4IdEo2Ww0VP-nD0w,17323
16
+ STIC_JPL/net_radiation.py,sha256=Uwudsazul8V-x5t8KQLi3wpYi-wjMwT__eqZCRV2bIw,1187
17
+ STIC_JPL/process_STIC_table.py,sha256=5s-fxbchyVnPCzUG5KZX-RrHZDpN4JVcwu42s821Kdw,1712
18
+ STIC_JPL/root_zone_initialization.py,sha256=3JVKNDt3ebIiGVfuhBawjKs-BicNwkbfMprkzSxd4Cg,1581
19
+ STIC_JPL/root_zone_iteration.py,sha256=1XOMFE3-TdJZzUU5vouflUO4NKaZwAW6s1KJrNez3Es,3787
20
+ STIC_JPL/soil_moisture_initialization.py,sha256=wWhAmvNT8tAIm_Sul8mEj6VdNuj7K14TBGUjKfqJrso,5610
21
+ STIC_JPL/soil_moisture_iteration.py,sha256=QJXOPMxxwIIskpx9zLkXUPfuhWgFPUBcRnGhZo2UjAw,6493
22
+ STIC_JPL/timer.py,sha256=tn5e3NQmsh55Jp9Fstjf-8KJW4F8UIJs-d_ZLooFYE8,1610
23
+ STIC_JPL/version.txt,sha256=IGYgplfxzMivcrhm_c7yDmQcSPOc4n8zjOgxOUnGPlM,5
24
+ stic_jpl-1.2.1.dist-info/METADATA,sha256=jKGqTkUD0wSUa1LCmVP3fBSC7XMiLJbPYOAgnBw8C7o,5731
25
+ stic_jpl-1.2.1.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
26
+ stic_jpl-1.2.1.dist-info/top_level.txt,sha256=9NkchxttzACJcGcAaWzMaZarzX40OXQ216hERNA9LIo,9
27
+ stic_jpl-1.2.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (80.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1 +0,0 @@
1
- from .meteorology_conversion import *
@@ -1,123 +0,0 @@
1
- from typing import Union
2
- import numpy as np
3
- import rasters as rt
4
- from rasters import Raster
5
-
6
- # gas constant for dry air in joules per kilogram per kelvin
7
- RD = 286.9
8
-
9
- # gas constant for moist air in joules per kilogram per kelvin
10
- RW = 461.5
11
-
12
- # specific heat of water vapor in joules per kilogram per kelvin
13
- CPW = 1846.0
14
-
15
- # specific heat of dry air in joules per kilogram per kelvin
16
- CPD = 1005.0
17
-
18
- def kelvin_to_celsius(T_K: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
19
- """
20
- convert temperature in kelvin to celsius.
21
- :param T_K: temperature in kelvin
22
- :return: temperature in celsius
23
- """
24
- return T_K - 273.15
25
-
26
- def celcius_to_kelvin(T_C: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
27
- """
28
- convert temperature in celsius to kelvin.
29
- :param T_C: temperature in celsius
30
- :return: temperature in kelvin
31
- """
32
- return T_C + 273.15
33
-
34
- def calculate_specific_humidity(
35
- Ea_Pa: Union[Raster, np.ndarray],
36
- Ps_Pa: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
37
- """
38
- Calculate the specific humidity of air as a ratio of kilograms of water to kilograms of air.
39
-
40
- Args:
41
- Ea_Pa (Union[Raster, np.ndarray]): Actual water vapor pressure in Pascal.
42
- surface_pressure_Pa (Union[Raster, np.ndarray]): Surface pressure in Pascal.
43
-
44
- Returns:
45
- Union[Raster, np.ndarray]: Specific humidity in kilograms of water per kilograms of air.
46
- """
47
- return ((0.622 * Ea_Pa) / (Ps_Pa - (0.387 * Ea_Pa)))
48
-
49
- def calculate_specific_heat(specific_humidity: Union[Raster, np.ndarray]):
50
- # calculate specific heat capacity of the air (Cp)
51
- # in joules per kilogram per kelvin
52
- # from specific heat of water vapor (CPW)
53
- # and specific heat of dry air (CPD)
54
- Cp_Jkg = specific_humidity * CPW + (1 - specific_humidity) * CPD
55
-
56
- return Cp_Jkg
57
-
58
- def calculate_air_density(
59
- surface_pressure_Pa: Union[Raster, np.ndarray],
60
- Ta_K: Union[Raster, np.ndarray],
61
- specific_humidity: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
62
- """
63
- Calculate air density.
64
-
65
- Parameters:
66
- surface_pressure_Pa (Union[Raster, np.ndarray]): Surface pressure in Pascal.
67
- Ta_K (Union[Raster, np.ndarray]): Air temperature in Kelvin.
68
- specific_humidity (Union[Raster, np.ndarray]): Specific humidity.
69
-
70
- Returns:
71
- Union[Raster, np.ndarray]: Air density in kilograms per cubic meter.
72
- """
73
- # numerator: Pa(N / m ^ 2 = kg * m / s ^ 2); denominator: J / kg / K * K)
74
- rhoD = surface_pressure_Pa / (RD * Ta_K)
75
-
76
- # calculate air density (rho) in kilograms per cubic meter
77
- rho = rhoD * ((1.0 + specific_humidity) / (1.0 + specific_humidity * (RW / RD)))
78
-
79
- return rho
80
-
81
- def SVP_kPa_from_Ta_C(Ta_C: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
82
- """
83
- Calculate the saturation vapor pressure in kiloPascal (kPa) from air temperature in Celsius.
84
-
85
- Parameters:
86
- Ta_C (Union[Raster, np.ndarray]): Air temperature in Celsius.
87
-
88
- Returns:
89
- Union[Raster, np.ndarray]: Saturation vapor pressure in kPa.
90
-
91
- """
92
- SVP_kPa = np.clip(0.611 * np.exp((Ta_C * 17.27) / (Ta_C + 237.7)), 1, None)
93
-
94
- return SVP_kPa
95
-
96
- def SVP_Pa_from_Ta_C(Ta_C: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
97
- """
98
- Calculate the saturation vapor pressure in Pascal (Pa) from the air temperature in Celsius (Ta_C).
99
-
100
- Parameters:
101
- Ta_C (Union[Raster, np.ndarray]): Air temperature in Celsius.
102
-
103
- Returns:
104
- Union[Raster, np.ndarray]: Saturation vapor pressure in Pascal (Pa).
105
- """
106
- return SVP_kPa_from_Ta_C(Ta_C) * 1000
107
-
108
- def calculate_surface_pressure(elevation_m: Union[Raster, np.ndarray], Ta_C: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
109
- """
110
- Calculate surface pressure using elevation and air temperature.
111
-
112
- Parameters:
113
- elevation_m (Union[Raster, np.ndarray]): Elevation in meters.
114
- Ta_K (Union[Raster, np.ndarray]): Air temperature in Kelvin.
115
-
116
- Returns:
117
- Union[Raster, np.ndarray]: Surface pressure in Pascal (Pa).
118
- """
119
- Ta_K = kelvin_to_celsius(Ta_C)
120
- Ps_Pa = 101325.0 * (1.0 - 0.0065 * elevation_m / Ta_K) ** (9.807 / (0.0065 * 287.0)) # [Pa]
121
-
122
- return Ps_Pa
123
-
@@ -1 +0,0 @@
1
- from .calculate_SEBAL_soil_heat_flux import calculate_SEBAL_soil_heat_flux
@@ -1,40 +0,0 @@
1
- from typing import Union
2
-
3
- import numpy as np
4
- import rasters as rt
5
- from rasters import Raster
6
-
7
- def calculate_SEBAL_soil_heat_flux(
8
- Rn: Union[Raster, np.ndarray],
9
- ST_C: Union[Raster, np.ndarray],
10
- NDVI: Union[Raster, np.ndarray],
11
- albedo: Union[Raster, np.ndarray]) -> Union[Raster, np.ndarray]:
12
- """
13
- This function calculates the soil heat flux (G) in the Surface Energy Balance Algorithm for Land (SEBAL) model.
14
- The formula used in the function is a simplification of the more complex relationship between these variables in the energy balance at the surface.
15
-
16
- Parameters:
17
- Rn (np.ndarray): Net radiation at the surface.
18
- ST_C (np.ndarray): Surface temperature in Celsius.
19
- NDVI (np.ndarray): Normalized Difference Vegetation Index, indicating the presence and condition of vegetation.
20
- albedo (np.ndarray): Measure of the diffuse reflection of solar radiation.
21
-
22
- Returns:
23
- np.ndarray: The soil heat flux (G), a key component in the energy balance.
24
-
25
- Reference:
26
- "Evapotranspiration Estimation Based on Remote Sensing and the SEBAL Model in the Bosten Lake Basin of China" [^1^][1]
27
- """
28
- # Empirical coefficients used in the calculation
29
- coeff1 = 0.0038
30
- coeff2 = 0.0074
31
-
32
- # Vegetation cover correction factor
33
- NDVI_correction = 1 - 0.98 * NDVI ** 4
34
-
35
- # Calculation of the soil heat flux (G)
36
- G = Rn * ST_C * (coeff1 + coeff2 * albedo) * NDVI_correction
37
-
38
- G = rt.clip(G, 0, None)
39
-
40
- return G
@@ -1 +0,0 @@
1
- from .timer import *
@@ -1 +0,0 @@
1
- from .vegetation_conversion import *
@@ -1,28 +0,0 @@
1
- STIC_JPL/STIC_JPL.py,sha256=a0EpRTX6Sd75RPIqAzWckIuj9-KwbCE8T1aSEDpLM-c,17434
2
- STIC_JPL/__init__.py,sha256=FmTp-Ir0Wbae0SbZgxlNIxq48TDrWCev17Gv1hWskxU,216
3
- STIC_JPL/canopy_air_stream.py,sha256=UYp3l7mt0XH4SqvCGZhRzCIL-v7-Rvxxfm-C81lheSU,1637
4
- STIC_JPL/closure.py,sha256=AzWoLnpoZ1MlQAKq0CgpmCMfDGVsp8-hYZ50ry33cic,4031
5
- STIC_JPL/constants.py,sha256=xIVTsdc3_Rf3EPSgSk1QPjWiojFGn4lf1Swffov3oTM,595
6
- STIC_JPL/diagnostic.py,sha256=RXjtZbCEJMiTyRuiMGUvekDgO_GcrhXVXvorR0WlAsk,2613
7
- STIC_JPL/initialize_with_solar.py,sha256=6DUUHdVfeKvDk1pf_bOteSW8qd-n9pU4dowO1sMYOpw,3825
8
- STIC_JPL/initialize_without_solar.py,sha256=M_7QfP2pr2w5sQO7UOW1GPqxpmQkjmsQ526BgYVVALk,4675
9
- STIC_JPL/iterate_with_solar.py,sha256=WKySvuIJ4HzcDni0UYafmk5lXex2lJfde82kGnGUDI8,6368
10
- STIC_JPL/iterate_without_solar.py,sha256=09RERpN6tdZr6ORDhMwEFXwx8941vyn1HMJQ-Q_bY6s,6140
11
- STIC_JPL/net_radiation.py,sha256=Uwudsazul8V-x5t8KQLi3wpYi-wjMwT__eqZCRV2bIw,1187
12
- STIC_JPL/root_zone_initialization.py,sha256=3JVKNDt3ebIiGVfuhBawjKs-BicNwkbfMprkzSxd4Cg,1581
13
- STIC_JPL/root_zone_iteration.py,sha256=1XOMFE3-TdJZzUU5vouflUO4NKaZwAW6s1KJrNez3Es,3787
14
- STIC_JPL/soil_moisture_initialization.py,sha256=YF0GcHkpH5amABNPpxD8f1lLPRyIAkG2dYJTfEuIX4g,5599
15
- STIC_JPL/soil_moisture_iteration.py,sha256=QJXOPMxxwIIskpx9zLkXUPfuhWgFPUBcRnGhZo2UjAw,6493
16
- STIC_JPL/version.txt,sha256=rqWtvvV0eFJzxPOcG3aHz4AZk-DLa_Z4GkFonk7bsgw,5
17
- STIC_JPL/meteorology_conversion/__init__.py,sha256=dLF40imqJCC_l8_QMetbsj1YzkXZuFt9tQ2fsd7Wcdw,38
18
- STIC_JPL/meteorology_conversion/meteorology_conversion.py,sha256=8vzx71FLLNhR5v6EQBL8OSgAmupQcgdLX2c9tL9TPxA,4202
19
- STIC_JPL/soil_heat_flux/__init__.py,sha256=bN0dHUfqR9ob68M1TqdjQTK3kmaL5S4P08yK0Thub6k,74
20
- STIC_JPL/soil_heat_flux/calculate_SEBAL_soil_heat_flux.py,sha256=PZylUGET5-Uj6BGfhOeqS4tp2pGE__gdc5X1nidLjxw,1516
21
- STIC_JPL/timer/__init__.py,sha256=I_MQKp_aamBLUzZv0psEbRgs6GZLOJd4mmJ7bli0Ikc,21
22
- STIC_JPL/timer/timer.py,sha256=tn5e3NQmsh55Jp9Fstjf-8KJW4F8UIJs-d_ZLooFYE8,1610
23
- STIC_JPL/vegetation_conversion/__init__.py,sha256=8gnz0yK_euCV0TtVGDrSGaVfx4g0EJCG2J68ppuA5oc,37
24
- STIC_JPL/vegetation_conversion/vegetation_conversion.py,sha256=-K3FLV6pXkpxtRxQvimqqkXAgkn9mUUlHQ8FIscq1Zg,1306
25
- stic_jpl-1.1.0.dist-info/METADATA,sha256=sDO9vRDdU6d-qTxHfk-0ZoCLX5ZyT1o1F086M0hbqWA,5348
26
- stic_jpl-1.1.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
27
- stic_jpl-1.1.0.dist-info/top_level.txt,sha256=9NkchxttzACJcGcAaWzMaZarzX40OXQ216hERNA9LIo,9
28
- stic_jpl-1.1.0.dist-info/RECORD,,
File without changes