geos5fp 1.0.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.
geos5fp/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ from .geos5fp import *
2
+ from os.path import join, abspath, dirname
3
+
4
+ with open(join(abspath(dirname(__file__)), "version.txt")) as f:
5
+ version = f.read()
6
+
7
+ __version__ = version
8
+ __author__ = "Gregory H. Halverson"
@@ -0,0 +1 @@
1
+ from .downscaling import *
@@ -0,0 +1,271 @@
1
+ from datetime import datetime
2
+ from dateutil import parser
3
+ import numpy as np
4
+ from rasters import Raster, RasterGeometry, RasterGrid
5
+ import rasters as rt
6
+
7
+ DEFAULT_UPSAMPLING = "average"
8
+ DEFAULT_DOWNSAMPLING = "linear"
9
+
10
+ def bias_correct(
11
+ coarse_image: Raster,
12
+ fine_image: Raster,
13
+ upsampling: str = "average",
14
+ downsampling: str = "linear",
15
+ return_bias: bool = False):
16
+ fine_geometry = fine_image.geometry
17
+ coarse_geometry = coarse_image.geometry
18
+ upsampled = fine_image.to_geometry(coarse_geometry, resampling=upsampling)
19
+ bias_coarse = upsampled - coarse_image
20
+ bias_fine = bias_coarse.to_geometry(fine_geometry, resampling=downsampling)
21
+ bias_corrected_fine = fine_image - bias_fine
22
+
23
+ if return_bias:
24
+ return bias_corrected_fine, bias_fine
25
+ else:
26
+ return bias_corrected_fine
27
+
28
+ def linear_downscale(
29
+ coarse_image: Raster,
30
+ fine_image: Raster,
31
+ upsampling: str = "average",
32
+ downsampling: str = "linear",
33
+ use_gap_filling: bool = False,
34
+ apply_scale: bool = True,
35
+ apply_bias: bool = True,
36
+ return_scale_and_bias: bool = False) -> Raster:
37
+ if upsampling is None:
38
+ upsampling = DEFAULT_UPSAMPLING
39
+
40
+ if downsampling is None:
41
+ downsampling = DEFAULT_DOWNSAMPLING
42
+
43
+ coarse_geometry = coarse_image.geometry
44
+ fine_geometry = fine_image.geometry
45
+ upsampled = fine_image.to_geometry(coarse_geometry, resampling=upsampling)
46
+
47
+ if apply_scale:
48
+ scale_coarse = coarse_image / upsampled
49
+ scale_coarse = rt.where(coarse_image == 0, 0, scale_coarse)
50
+ scale_coarse = rt.where(upsampled == 0, 0, scale_coarse)
51
+ scale_fine = scale_coarse.to_geometry(fine_geometry, resampling=downsampling)
52
+ scale_corrected_fine = fine_image * scale_fine
53
+ fine_image = scale_corrected_fine
54
+ else:
55
+ scale_fine = fine_image * 0 + 1
56
+
57
+ if apply_bias:
58
+ upsampled = fine_image.to_geometry(coarse_geometry, resampling=upsampling)
59
+ bias_coarse = upsampled - coarse_image
60
+ bias_fine = bias_coarse.to_geometry(fine_geometry, resampling=downsampling)
61
+ bias_corrected_fine = fine_image - bias_fine
62
+ fine_image = bias_corrected_fine
63
+ else:
64
+ bias_fine = fine_image * 0
65
+
66
+ if use_gap_filling:
67
+ gap_fill = coarse_image.to_geometry(fine_geometry, resampling=downsampling)
68
+ fine_image = fine_image.fill(gap_fill)
69
+
70
+ if return_scale_and_bias:
71
+ fine_image["scale"] = scale_fine
72
+ fine_image["bias"] = bias_fine
73
+
74
+ return fine_image
75
+
76
+ def NDVI_to_FVC(NDVI: Raster) -> Raster:
77
+ NDVIv = 0.52 # +- 0.03
78
+ NDVIs = 0.04 # +- 0.03
79
+ FVC = rt.clip((NDVI - NDVIs) / (NDVIv - NDVIs), 0, 1)
80
+
81
+ return FVC
82
+
83
+ def downscale_air_temperature(
84
+ time_UTC: datetime,
85
+ Ta_K_coarse: Raster,
86
+ ST_K: Raster,
87
+ water: Raster = None,
88
+ fine_geometry: RasterGeometry = None,
89
+ coarse_geometry: RasterGeometry = None,
90
+ resampling: str = None,
91
+ upsampling: str = None,
92
+ downsampling: str = None,
93
+ apply_scale: bool = True,
94
+ apply_bias: bool = True,
95
+ return_scale_and_bias: bool = False) -> Raster:
96
+ """
97
+ near-surface air temperature (Ta) in Kelvin
98
+ :param time_UTC: date/time in UTC
99
+ :param geometry: optional target geometry
100
+ :param resampling: optional sampling method for resampling to target geometry
101
+ :return: raster of Ta
102
+ """
103
+
104
+ if isinstance(time_UTC, str):
105
+ time_UTC = parser.parse(time_UTC)
106
+
107
+ if fine_geometry is None:
108
+ fine_geometry = ST_K.geometry
109
+
110
+ if coarse_geometry is None:
111
+ coarse_geometry = Ta_K_coarse.geometry
112
+
113
+ ST_K_water = None
114
+
115
+ if water is not None:
116
+ ST_K_water = rt.where(water, ST_K, np.nan)
117
+ ST_K = rt.where(water, np.nan, ST_K)
118
+
119
+ scale = None
120
+ bias = None
121
+
122
+ Ta_K = linear_downscale(
123
+ coarse_image=Ta_K_coarse,
124
+ fine_image=ST_K,
125
+ upsampling=upsampling,
126
+ downsampling=downsampling,
127
+ apply_scale=apply_scale,
128
+ apply_bias=apply_bias,
129
+ return_scale_and_bias=return_scale_and_bias
130
+ )
131
+
132
+ if water is not None:
133
+ Ta_K_water = linear_downscale(
134
+ coarse_image=Ta_K_coarse,
135
+ fine_image=ST_K_water,
136
+ upsampling=upsampling,
137
+ downsampling=downsampling,
138
+ apply_scale=apply_scale,
139
+ apply_bias=apply_bias,
140
+ return_scale_and_bias=False
141
+ )
142
+
143
+ Ta_K = rt.where(water, Ta_K_water, Ta_K)
144
+
145
+ Ta_K.filenames = Ta_K_coarse.filenames
146
+
147
+ return Ta_K
148
+
149
+
150
+ def downscale_soil_moisture(
151
+ time_UTC: datetime,
152
+ fine_geometry: RasterGrid,
153
+ coarse_geometry: RasterGrid,
154
+ SM_coarse: Raster,
155
+ SM_resampled: Raster,
156
+ ST_fine: Raster,
157
+ NDVI_fine: Raster,
158
+ water: Raster,
159
+ fvlim=0.5,
160
+ a=0.5,
161
+ smoothing="linear") -> Raster:
162
+ fine = fine_geometry
163
+ ST_fine = ST_fine.mask(~water)
164
+ NDVI_fine = NDVI_fine.mask(~water)
165
+ FVC_fine = NDVI_to_FVC(NDVI_fine)
166
+ soil_fine = FVC_fine < fvlim
167
+ Tmin_coarse = ST_fine.to_geometry(coarse_geometry, resampling="min")
168
+ Tmax_coarse = ST_fine.to_geometry(coarse_geometry, resampling="max")
169
+ Ts_fine = ST_fine.mask(soil_fine)
170
+ Tsmin_coarse = Ts_fine.to_geometry(coarse_geometry, resampling="min").fill(Tmin_coarse)
171
+ Tsmax_coarse = Ts_fine.to_geometry(coarse_geometry, resampling="max").fill(Tmax_coarse)
172
+ ST_coarse = ST_fine.to_geometry(coarse_geometry, resampling="average")
173
+ SEE_coarse = (Tsmax_coarse - ST_coarse) / rt.clip(Tsmax_coarse - Tsmin_coarse, 1, None)
174
+ SM_SEE_proportion = (SM_coarse / SEE_coarse).to_geometry(fine, resampling=smoothing)
175
+ Tsmax_fine = Tsmax_coarse.to_geometry(fine_geometry, resampling=smoothing)
176
+ Tsrange_fine = (Tsmax_coarse - Tsmin_coarse).to_geometry(fine, resampling=smoothing)
177
+ SEE_fine = (Tsmax_fine - ST_fine) / rt.clip(Tsrange_fine, 1, None)
178
+
179
+ SEE_mean = SEE_coarse.to_geometry(fine, resampling=smoothing)
180
+ SM_fine = rt.clip(SM_resampled + a * SM_SEE_proportion * (SEE_fine - SEE_mean), 0, 1)
181
+ SM_fine = SM_fine.mask(~water)
182
+
183
+ return SM_fine
184
+
185
+
186
+ def downscale_vapor_pressure_deficit(
187
+ time_UTC: datetime,
188
+ VPD_Pa_coarse: Raster,
189
+ ST_K: Raster,
190
+ fine_geometry: RasterGeometry = None,
191
+ coarse_geometry: RasterGeometry = None,
192
+ resampling: str = None,
193
+ upsampling: str = None,
194
+ downsampling: str = None,
195
+ return_scale_and_bias: bool = False) -> Raster:
196
+ if upsampling is None:
197
+ upsampling = "average"
198
+
199
+ if downsampling is None:
200
+ downsampling = "linear"
201
+
202
+ if fine_geometry is None:
203
+ fine_geometry = ST_K.geometry
204
+
205
+ if coarse_geometry is None:
206
+ coarse_geometry = VPD_Pa_coarse.geometry
207
+
208
+ return linear_downscale(
209
+ coarse_image=VPD_Pa_coarse,
210
+ fine_image=ST_K,
211
+ upsampling=upsampling,
212
+ downsampling=downsampling,
213
+ return_scale_and_bias=return_scale_and_bias
214
+ )
215
+
216
+
217
+ def downscale_relative_humidity(
218
+ time_UTC: datetime,
219
+ RH_coarse: Raster,
220
+ SM,
221
+ ST_K,
222
+ VPD_kPa,
223
+ water: Raster = None,
224
+ fine_geometry: RasterGeometry = None,
225
+ coarse_geometry: RasterGeometry = None,
226
+ resampling: str = None,
227
+ upsampling: str = None,
228
+ downsampling: str = None) -> Raster:
229
+ if upsampling is None:
230
+ upsampling = "average"
231
+
232
+ if downsampling is None:
233
+ downsampling = "linear"
234
+
235
+ if fine_geometry is None:
236
+ fine_geometry = SM.geometry
237
+
238
+ if coarse_geometry is None:
239
+ coarse_geometry = RH_coarse.geometry
240
+
241
+ bias_fine = None
242
+
243
+ RH_estimate_fine = SM ** (1 / VPD_kPa)
244
+
245
+ RH = bias_correct(
246
+ coarse_image=RH_coarse,
247
+ fine_image=RH_estimate_fine,
248
+ upsampling=upsampling,
249
+ downsampling=downsampling,
250
+ return_bias=False
251
+ )
252
+
253
+ if water is not None:
254
+ ST_K_water = rt.where(water, ST_K, np.nan)
255
+ RH_coarse_complement = 1 - RH_coarse
256
+ RH_complement_water = linear_downscale(
257
+ coarse_image=RH_coarse_complement,
258
+ fine_image=ST_K_water,
259
+ upsampling=upsampling,
260
+ downsampling=downsampling,
261
+ apply_bias=True,
262
+ return_scale_and_bias=False
263
+ )
264
+
265
+ RH_water = 1 - RH_complement_water
266
+ RH = rt.where(water, RH_water, RH)
267
+
268
+ RH = rt.clip(RH, 0, 1)
269
+
270
+ return RH
271
+