pyVPRM 5.2__tar.gz → 5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyvprm-5.2/pyVPRM.egg-info → pyvprm-5.3}/PKG-INFO +2 -1
- {pyvprm-5.2 → pyvprm-5.3}/README.md +1 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/VPRM.py +150 -0
- pyvprm-5.3/pyVPRM/__init__.py +4 -0
- pyvprm-5.3/pyVPRM/flux_tower_libs/FFP_footprint_class.py +108 -0
- pyvprm-5.3/pyVPRM/flux_tower_libs/KM_footprint_class.py +101 -0
- pyvprm-5.3/pyVPRM/flux_tower_libs/base_footprint_class.py +351 -0
- {pyvprm-5.2/pyVPRM/lib → pyvprm-5.3/pyVPRM/flux_tower_libs}/flux_tower_class.py +119 -6
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/lib/functions.py +225 -30
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/base_manager.py +2 -2
- pyvprm-5.3/pyVPRM/sat_managers/lads_downloader.py +366 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/modis.py +0 -1
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/sentinel2.py +1 -3
- pyvprm-5.3/pyVPRM/vprm_models/model_params/__init__.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3/pyVPRM.egg-info}/PKG-INFO +2 -1
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM.egg-info/SOURCES.txt +6 -1
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM.egg-info/requires.txt +1 -0
- {pyvprm-5.2 → pyvprm-5.3}/setup.py +2 -1
- pyvprm-5.2/pyVPRM/__init__.py +0 -3
- {pyvprm-5.2 → pyvprm-5.3}/LICENSE +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/MANIFEST.in +0 -0
- {pyvprm-5.2/pyVPRM/lib → pyvprm-5.3/pyVPRM/flux_tower_libs}/__init__.py +0 -0
- {pyvprm-5.2/pyVPRM/meteorologies → pyvprm-5.3/pyVPRM/lib}/__init__.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/lib/downmodis.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/lib/fancy_plot.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/lib/fluxnet_info/fluxnet_sites.pkl +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/lib/fluxnet_info/site_infos.txt +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/lib/reproject_fiona.py +0 -0
- {pyvprm-5.2/pyVPRM/sat_managers → pyvprm-5.3/pyVPRM/meteorologies}/__init__.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/meteorologies/era5_class_dkrz.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/meteorologies/era5_class_draft.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/meteorologies/era5_land_destinE.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/meteorologies/era5_monthly_xr.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/meteorologies/met_base_class.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/meteorologies/met_local_measurement.py +0 -0
- {pyvprm-5.2/pyVPRM/vprm_configs → pyvprm-5.3/pyVPRM/sat_managers}/__init__.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/city.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/copernicus.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/esa_world_cover.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/mapbiomas.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/proba_v.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/synmap.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/viirs.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/sat_managers/viirs09ga.py +0 -0
- {pyvprm-5.2/pyVPRM/vprm_models/model_params → pyvprm-5.3/pyVPRM/vprm_configs}/__init__.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/vprm_configs/copernicus_land_cover.yaml +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/vprm_configs/esa_world_cover.yaml +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/vprm_configs/synmap.yaml +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/vprm_models/__init__.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/vprm_models/vprm_base_model.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/vprm_models/vprm_base_model_no_xeric.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/vprm_models/vprm_modified_model.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM/vprm_models/vprm_nn_model.py +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM.egg-info/dependency_links.txt +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM.egg-info/not-zip-safe +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/pyVPRM.egg-info/top_level.txt +0 -0
- {pyvprm-5.2 → pyvprm-5.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyVPRM
|
|
3
|
-
Version: 5.
|
|
3
|
+
Version: 5.3
|
|
4
4
|
Summary: Vegetation Photosynthesis and Respiration Model
|
|
5
5
|
Home-page: https://github.com/tglauch/pyVPRM/
|
|
6
6
|
Author: Theo Glauch
|
|
@@ -38,6 +38,7 @@ Requires-Dist: requests
|
|
|
38
38
|
Requires-Dist: statsmodels
|
|
39
39
|
Requires-Dist: loguru
|
|
40
40
|
Requires-Dist: fiona
|
|
41
|
+
Requires-Dist: pykalman
|
|
41
42
|
Dynamic: author
|
|
42
43
|
Dynamic: author-email
|
|
43
44
|
Dynamic: classifier
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://pypi.org/project/pyVPRM/)
|
|
4
4
|
[](https://pypi.org/project/pyVPRM/)
|
|
5
5
|

|
|
6
|
+

|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
<figure>
|
|
@@ -10,6 +10,7 @@ from pyVPRM.sat_managers.base_manager import satellite_data_manager
|
|
|
10
10
|
from pyVPRM.lib.functions import (
|
|
11
11
|
add_corners_to_1d_grid,
|
|
12
12
|
do_lowess_smoothing,
|
|
13
|
+
do_kalman_smoothing,
|
|
13
14
|
make_xesmf_grid,
|
|
14
15
|
to_esmf_grid,
|
|
15
16
|
replace_inf_runs_ignore_nans
|
|
@@ -964,6 +965,155 @@ class vprm_preprocessor:
|
|
|
964
965
|
)
|
|
965
966
|
return
|
|
966
967
|
|
|
968
|
+
def kalman(self, keys, times=None, lonlats=None, n_cpus=None, smooth_all=False):
|
|
969
|
+
"""
|
|
970
|
+
Performs the lowess smoothing
|
|
971
|
+
|
|
972
|
+
Parameters:
|
|
973
|
+
lonlats (str): If given the smearing is only performed at the
|
|
974
|
+
given lats and lons
|
|
975
|
+
Returns:
|
|
976
|
+
None
|
|
977
|
+
"""
|
|
978
|
+
self.sat_imgs.sat_img.load()
|
|
979
|
+
|
|
980
|
+
if n_cpus is None:
|
|
981
|
+
n_cpus = self.n_cpus
|
|
982
|
+
if isinstance(times, pd.core.indexes.datetimes.DatetimeIndex):
|
|
983
|
+
times = list(times)
|
|
984
|
+
if isinstance(times, list):
|
|
985
|
+
times = np.array(sorted(times))
|
|
986
|
+
if (times[-1] > self.timestamp_end) | (times[0] < self.timestamp_start):
|
|
987
|
+
logger.info(
|
|
988
|
+
"You have provied some timestamps that are not covered from satellite images.\
|
|
989
|
+
They will be ignored in the following, to avoid unreliable results"
|
|
990
|
+
)
|
|
991
|
+
times = times[
|
|
992
|
+
(times <= self.timestamp_end) & (times >= self.timestamp_start)
|
|
993
|
+
]
|
|
994
|
+
xvals = [
|
|
995
|
+
int(
|
|
996
|
+
np.round(
|
|
997
|
+
(i - self.timestamp_start).total_seconds() / (24 * 60 * 60)
|
|
998
|
+
)
|
|
999
|
+
)
|
|
1000
|
+
for i in times
|
|
1001
|
+
]
|
|
1002
|
+
elif isinstance(times, str):
|
|
1003
|
+
if times == "daily":
|
|
1004
|
+
xvals = np.arange(self.tot_num_days+1)
|
|
1005
|
+
else:
|
|
1006
|
+
logger.info("{} is not a valid str for times".format(times))
|
|
1007
|
+
return
|
|
1008
|
+
else:
|
|
1009
|
+
xvals = self.sat_imgs.sat_img["time"]
|
|
1010
|
+
logger.info("Lowess timestamps {}".format(xvals))
|
|
1011
|
+
|
|
1012
|
+
if (self.flux_tower_instances is not None) and (smooth_all is False): # Is flux tower sites are given
|
|
1013
|
+
if "timestamps" in list(self.sat_imgs.sat_img.data_vars):
|
|
1014
|
+
for key in keys:
|
|
1015
|
+
self.sat_imgs.sat_img = self.sat_imgs.sat_img.assign(
|
|
1016
|
+
{
|
|
1017
|
+
key: (
|
|
1018
|
+
["time_gap_filled", "site_names"],
|
|
1019
|
+
np.array(
|
|
1020
|
+
[
|
|
1021
|
+
do_kalman_smoothing(
|
|
1022
|
+
self.sat_imgs.sat_img.sel(site_names=i)[
|
|
1023
|
+
key
|
|
1024
|
+
].values,
|
|
1025
|
+
timestamps=self.sat_imgs.sat_img.sel(
|
|
1026
|
+
site_names=i
|
|
1027
|
+
)["timestamps"].values,
|
|
1028
|
+
)
|
|
1029
|
+
for i in self.sat_imgs.sat_img.site_names.values
|
|
1030
|
+
]
|
|
1031
|
+
).T,
|
|
1032
|
+
)
|
|
1033
|
+
}
|
|
1034
|
+
)
|
|
1035
|
+
else:
|
|
1036
|
+
for key in keys:
|
|
1037
|
+
self.sat_imgs.sat_img = self.sat_imgs.sat_img.assign(
|
|
1038
|
+
{
|
|
1039
|
+
key: (
|
|
1040
|
+
["time_gap_filled", "site_names"],
|
|
1041
|
+
np.array(
|
|
1042
|
+
[
|
|
1043
|
+
do_kalman_smoothing(
|
|
1044
|
+
self.sat_imgs.sat_img.sel(site_names=i)[
|
|
1045
|
+
key
|
|
1046
|
+
].values,
|
|
1047
|
+
timestamps=self.sat_imgs.sat_img[
|
|
1048
|
+
"time"
|
|
1049
|
+
].values,
|
|
1050
|
+
)
|
|
1051
|
+
for i in self.sat_imgs.sat_img.site_names.values
|
|
1052
|
+
]
|
|
1053
|
+
).T,
|
|
1054
|
+
)
|
|
1055
|
+
}
|
|
1056
|
+
)
|
|
1057
|
+
|
|
1058
|
+
elif lonlats is None: # If smoothing the entire array
|
|
1059
|
+
if "timestamps" in list(self.sat_imgs.sat_img.data_vars):
|
|
1060
|
+
for key in keys:
|
|
1061
|
+
self.sat_imgs.sat_img = self.sat_imgs.sat_img.assign(
|
|
1062
|
+
{
|
|
1063
|
+
key: (
|
|
1064
|
+
["time_gap_filled", "y", "x"],
|
|
1065
|
+
np.array(
|
|
1066
|
+
Parallel(n_jobs=n_cpus, max_nbytes=None)(
|
|
1067
|
+
delayed(do_kalman_smoothing)(
|
|
1068
|
+
self.sat_imgs.sat_img[key][:, :, i].values,
|
|
1069
|
+
timestamps=self.sat_imgs.sat_img[
|
|
1070
|
+
"timestamps"
|
|
1071
|
+
][:, :, i].values,
|
|
1072
|
+
)
|
|
1073
|
+
for i, x_coord in enumerate(
|
|
1074
|
+
self.sat_imgs.sat_img.x.values
|
|
1075
|
+
)
|
|
1076
|
+
)
|
|
1077
|
+
).T,
|
|
1078
|
+
)
|
|
1079
|
+
}
|
|
1080
|
+
)
|
|
1081
|
+
else:
|
|
1082
|
+
for key in keys:
|
|
1083
|
+
self.sat_imgs.sat_img = self.sat_imgs.sat_img.assign(
|
|
1084
|
+
{
|
|
1085
|
+
key: (
|
|
1086
|
+
["time_gap_filled", "y", "x"],
|
|
1087
|
+
np.array(
|
|
1088
|
+
Parallel(n_jobs=n_cpus, max_nbytes=None)(
|
|
1089
|
+
delayed(do_kalman_smoothing)(
|
|
1090
|
+
self.sat_imgs.sat_img[key][:, :, i].values,
|
|
1091
|
+
timestamps=self.sat_imgs.sat_img[
|
|
1092
|
+
"time"
|
|
1093
|
+
].values,
|
|
1094
|
+
)
|
|
1095
|
+
for i, x_coord in enumerate(
|
|
1096
|
+
self.sat_imgs.sat_img.x.values
|
|
1097
|
+
)
|
|
1098
|
+
)
|
|
1099
|
+
).T,
|
|
1100
|
+
)
|
|
1101
|
+
}
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
else:
|
|
1105
|
+
logger.info("Not implemented")
|
|
1106
|
+
# Originally had a function to smooth only at specific lat/long.
|
|
1107
|
+
# That doesn't make sense anymore, because the time dimension will change through lowess smoothing.
|
|
1108
|
+
# If this is your plan then try to crop the sat image first and then do the lowess filtering.
|
|
1109
|
+
|
|
1110
|
+
self.time_key = "time_gap_filled"
|
|
1111
|
+
self.sat_imgs.sat_img = self.sat_imgs.sat_img.assign_coords(
|
|
1112
|
+
{"time_gap_filled": list(np.arange(self.tot_num_days+1))}
|
|
1113
|
+
)
|
|
1114
|
+
self.sat_imgs.sat_img = self.sat_imgs.sat_img.sel({"time_gap_filled": list(xvals)})
|
|
1115
|
+
return
|
|
1116
|
+
|
|
967
1117
|
def clip_values(self, key, min_val, max_val, to_nan=False):
|
|
968
1118
|
if to_nan:
|
|
969
1119
|
self.sat_imgs.sat_img[key].values[
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import xarray as xr
|
|
5
|
+
from scipy.special import gamma
|
|
6
|
+
from pyVPRM.flux_tower_libs.base_footprint_class import base_footprint_manager
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class FFP_footprint_manager(base_footprint_manager):
|
|
10
|
+
def __init__(self, time_stamps, flux_tower_manager, calculation_grid_side_length, calculation_grid_pixels_per_side, era5_instance = None):
|
|
11
|
+
super().__init__(time_stamps, flux_tower_manager, calculation_grid_side_length, calculation_grid_pixels_per_side, era5_instance)
|
|
12
|
+
self.footprint_model = 'FFP'
|
|
13
|
+
|
|
14
|
+
def print_parameters(self):
|
|
15
|
+
print('Parameters of model', self.footprint_model)
|
|
16
|
+
print('u')
|
|
17
|
+
print(self.u)
|
|
18
|
+
print('ZL')
|
|
19
|
+
print(self.ZL)
|
|
20
|
+
print('u dir')
|
|
21
|
+
print(self.u_dir)
|
|
22
|
+
print('u*')
|
|
23
|
+
print(self.u_star)
|
|
24
|
+
print('sigma v')
|
|
25
|
+
print(self.sigma_v)
|
|
26
|
+
print('boundary layer h')
|
|
27
|
+
print(self.h_pbl)
|
|
28
|
+
print('z')
|
|
29
|
+
print(self.z)
|
|
30
|
+
print('t')
|
|
31
|
+
print(self.time_stamps)
|
|
32
|
+
|
|
33
|
+
def calculate_footprints(self):
|
|
34
|
+
k_karman = 0.4
|
|
35
|
+
#calculate secondary parameters
|
|
36
|
+
X_star = self.X_calculation_grid_rotated/np.expand_dims(self.z, axis =(1,2)) * np.expand_dims((1-self.z/self.h_pbl)*(self.u/self.u_star*k_karman)**-1, axis=(1,2))
|
|
37
|
+
p = np.where(self.ZL<=0, 0.8, 0.55)
|
|
38
|
+
ps1 = np.minimum(1, np.abs(self.ZL)**-1*10**-5+p)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
#calculate crosswind_integrated_flux
|
|
42
|
+
a = 1.452
|
|
43
|
+
b = -1.991
|
|
44
|
+
c = 1.462
|
|
45
|
+
d = 0.136
|
|
46
|
+
k_karman = 0.4
|
|
47
|
+
Fy_star = a*(X_star-d)**b*np.exp(-c/(X_star-d))
|
|
48
|
+
crosswind_integrated_flux = Fy_star*np.expand_dims((1-self.z/self.h_pbl)*self.u_star/(self.z*self.u*k_karman), axis=(1,2))
|
|
49
|
+
if not np.all(np.logical_or(np.isnan(crosswind_integrated_flux), crosswind_integrated_flux==0)):
|
|
50
|
+
crosswind_integrated_flux = np.where(np.isnan(crosswind_integrated_flux), 0, crosswind_integrated_flux)
|
|
51
|
+
|
|
52
|
+
#calculate crosswind_distribution
|
|
53
|
+
a_c = 2.17
|
|
54
|
+
b_c = 1.66
|
|
55
|
+
c_c = 20.0
|
|
56
|
+
sigma_y_star = a_c*(b_c*(X_star)**2/(1+c_c*X_star))**.5
|
|
57
|
+
sigma_y = sigma_y_star*np.expand_dims((self.z*self.sigma_v)/(ps1*self.u_star), axis=(1,2))
|
|
58
|
+
crosswind_distribution = 1/((2*np.pi)**.5*sigma_y) * np.exp(-self.Y_calculation_grid_rotated**2/(2*sigma_y**2))
|
|
59
|
+
crosswind_distribution = np.where(np.isnan(crosswind_distribution), 0, crosswind_distribution)
|
|
60
|
+
|
|
61
|
+
#calculate footprints
|
|
62
|
+
footprint = crosswind_distribution*crosswind_integrated_flux
|
|
63
|
+
integrated_footprint = np.nansum(footprint, axis=(1,2))*self.side_length_pixel**2
|
|
64
|
+
|
|
65
|
+
#calculate peak estimator
|
|
66
|
+
x_peak = 0.87 * self.z * (1-self.z/self.h_pbl)**-1 * (self.u/self.u_star*k_karman)
|
|
67
|
+
|
|
68
|
+
#calculate fetch estimator
|
|
69
|
+
x_70 = (-c/np.log(0.7)+d) * self.z * (1-self.z/self.h_pbl)**-1 * (self.u/self.u_star*k_karman)
|
|
70
|
+
x_90 = (-c/np.log(0.9)+d) * self.z * (1-self.z/self.h_pbl)**-1 * (self.u/self.u_star*k_karman)
|
|
71
|
+
|
|
72
|
+
#store in xarray
|
|
73
|
+
self.footprint_on_calculation_grid = xr.Dataset(
|
|
74
|
+
data_vars=dict(
|
|
75
|
+
footprint = (["t", "y", "x"], footprint),
|
|
76
|
+
integrated = (["t"], integrated_footprint),
|
|
77
|
+
x_peak = (["t"], x_peak),
|
|
78
|
+
x_70 = (["t"], x_70),
|
|
79
|
+
x_90 = (["t"], x_90),
|
|
80
|
+
|
|
81
|
+
),
|
|
82
|
+
coords=dict(
|
|
83
|
+
t=("t", self.time_stamps),
|
|
84
|
+
y=("y", self.ys_for_calculation_grid),
|
|
85
|
+
x=("x", self.xs_for_calculation_grid),
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def calculate_crosswind_integrated_flux(self):
|
|
91
|
+
k_karman = 0.4
|
|
92
|
+
#calculate secondary parameters
|
|
93
|
+
X_star = self.xs_for_calculation_grid/np.expand_dims(self.z, axis =(1)) * np.expand_dims((1-self.z/self.h_pbl)*(self.u/self.u_star*k_karman)**-1, axis=(1))
|
|
94
|
+
p = np.where(self.ZL<=0, 0.8, 0.55)
|
|
95
|
+
ps1 = np.minimum(1, np.abs(self.ZL)**-1*10**-5+p)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
#calculate crosswind_integrated_flux
|
|
99
|
+
a = 1.452
|
|
100
|
+
b = -1.991
|
|
101
|
+
c = 1.462
|
|
102
|
+
d = 0.136
|
|
103
|
+
k_karman = 0.4
|
|
104
|
+
Fy_star = a*(X_star-d)**b*np.exp(-c/(X_star-d))
|
|
105
|
+
crosswind_integrated_flux = Fy_star*np.expand_dims((1-self.z/self.h_pbl)*self.u_star/(self.z*self.u*k_karman), axis=(1))
|
|
106
|
+
if not np.all(np.logical_or(np.isnan(crosswind_integrated_flux), crosswind_integrated_flux==0)):
|
|
107
|
+
crosswind_integrated_flux = np.where(np.isnan(crosswind_integrated_flux), 0, crosswind_integrated_flux)
|
|
108
|
+
self.crosswind_integrated_flux = crosswind_integrated_flux
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import numpy as np
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import xarray as xr
|
|
5
|
+
from scipy.special import gamma
|
|
6
|
+
from pyVPRM.flux_tower_libs.base_footprint_class import base_footprint_manager
|
|
7
|
+
|
|
8
|
+
class KM_footprint_manager(base_footprint_manager):
|
|
9
|
+
def __init__(self, time_stamps, flux_tower_manager, calculation_grid_side_length, calculation_grid_pixels_per_side):
|
|
10
|
+
super().__init__(time_stamps, flux_tower_manager, calculation_grid_side_length, calculation_grid_pixels_per_side)
|
|
11
|
+
self.footprint_model = 'KM'
|
|
12
|
+
|
|
13
|
+
def print_parameters(self):
|
|
14
|
+
print('Parameters of model', self.footprint_model)
|
|
15
|
+
print('u')
|
|
16
|
+
print(self.u)
|
|
17
|
+
print('L')
|
|
18
|
+
print(self.ZL)
|
|
19
|
+
print('u dir')
|
|
20
|
+
print(self.u_dir)
|
|
21
|
+
print('u*')
|
|
22
|
+
print(self.u_star)
|
|
23
|
+
print('sigma v')
|
|
24
|
+
print(self.sigma_v)
|
|
25
|
+
print('t')
|
|
26
|
+
print(self.time_stamps)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def calculate_footprints(self):
|
|
30
|
+
#print('calculate KM footprint')
|
|
31
|
+
k_karman = 0.4
|
|
32
|
+
#calculate secondary parameters
|
|
33
|
+
stability_c = np.where(self.ZL > 0, (1+5*self.ZL), (1-16*self.ZL)**(-1/2))
|
|
34
|
+
stability_m = np.where(self.ZL > 0, (1+5*self.ZL), (1-16*self.ZL)**(-1/4))
|
|
35
|
+
n = np.where(self.ZL > 0, 1/(1+5*self.ZL), (1-24*self.ZL)/(1-16*self.ZL))
|
|
36
|
+
#stability_c = np.where(self.L > 0, (1+5*self.z/self.L), (1-16*self.z/self.L)**(-1/2))
|
|
37
|
+
#stability_m = np.where(self.L > 0, (1+5*self.z/self.L), (1-16*self.z/self.L)**(-1/4))
|
|
38
|
+
#n = np.where(self.L > 0, 1/(1+5*self.z/self.L), (1-24*self.z/self.L)/(1-16*self.z/self.L))
|
|
39
|
+
kappa = k_karman*self.u_star*self.z/(stability_c*self.z**n)
|
|
40
|
+
m = self.u_star*stability_m/(k_karman*self.u)
|
|
41
|
+
r = 2+m-n
|
|
42
|
+
mu = (1+m)/r
|
|
43
|
+
#U = self.u/self.z**m
|
|
44
|
+
U = self.u_star*stability_m/(k_karman*m*self.z**m)
|
|
45
|
+
xi = U*self.z**r/(r**2*kappa)
|
|
46
|
+
|
|
47
|
+
#calculate crosswind_integrated_flux
|
|
48
|
+
crosswind_integrated_flux = np.expand_dims(xi**mu/gamma(mu), axis=(1,2)) / self.X_calculation_grid_rotated**np.expand_dims((1+mu), axis=(1,2)) * np.exp(-np.expand_dims(xi, axis=(1,2))/self.X_calculation_grid_rotated)
|
|
49
|
+
if not np.all(np.logical_or(np.isnan(crosswind_integrated_flux), crosswind_integrated_flux==0)):
|
|
50
|
+
crosswind_integrated_flux = np.where(np.isnan(crosswind_integrated_flux), 0, crosswind_integrated_flux)
|
|
51
|
+
|
|
52
|
+
#calculate crosswind_distribution
|
|
53
|
+
u_bar = np.expand_dims(gamma(mu)/gamma(1/r)*(r**2*kappa/U)**(m/r)*U, axis=(1,2))*self.X_calculation_grid_rotated**np.expand_dims((m/r), axis=(1,2))
|
|
54
|
+
sigma_y = self.X_calculation_grid_rotated*np.expand_dims(self.sigma_v, axis=(1,2))/u_bar
|
|
55
|
+
crosswind_distribution = 1/((2*np.pi)**.5*sigma_y) * np.exp(-self.Y_calculation_grid_rotated**2/(2*sigma_y**2))
|
|
56
|
+
#if not np.all(np.logical_or(np.isnan(crosswind_distribution), crosswind_distribution==0)):
|
|
57
|
+
crosswind_distribution = np.where(np.isnan(crosswind_distribution), 0, crosswind_distribution)
|
|
58
|
+
|
|
59
|
+
#calculate footprints:
|
|
60
|
+
footprint = crosswind_distribution*crosswind_integrated_flux
|
|
61
|
+
integrated_footprint = np.nansum(footprint, axis=(1,2))*self.side_length_pixel**2
|
|
62
|
+
|
|
63
|
+
#calculate peak estimator
|
|
64
|
+
x_peak = xi/(1+mu)
|
|
65
|
+
|
|
66
|
+
#store in xarray
|
|
67
|
+
self.footprint_on_calculation_grid = xr.Dataset(
|
|
68
|
+
data_vars=dict(
|
|
69
|
+
footprint = (["t", "y", "x"], footprint),
|
|
70
|
+
integrated = (["t"], integrated_footprint),
|
|
71
|
+
x_peak = (["t"], x_peak),
|
|
72
|
+
),
|
|
73
|
+
coords=dict(
|
|
74
|
+
t=("t", self.time_stamps),
|
|
75
|
+
y=("y", self.ys_for_calculation_grid),
|
|
76
|
+
x=("x", self.xs_for_calculation_grid),
|
|
77
|
+
),
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def calculate_crosswind_integrated_flux(self):
|
|
81
|
+
k_karman = 0.4
|
|
82
|
+
#calculate secondary parameters
|
|
83
|
+
stability_c = np.where(self.ZL > 0, (1+5*self.ZL), (1-16*self.ZL)**(-1/2))
|
|
84
|
+
stability_m = np.where(self.ZL > 0, (1+5*self.ZL), (1-16*self.ZL)**(-1/4))
|
|
85
|
+
n = np.where(self.ZL > 0, 1/(1+5*self.ZL), (1-24*self.ZL)/(1-16*self.ZL))
|
|
86
|
+
#stability_c = np.where(self.L > 0, (1+5*self.z/self.L), (1-16*self.z/self.L)**(-1/2))
|
|
87
|
+
#stability_m = np.where(self.L > 0, (1+5*self.z/self.L), (1-16*self.z/self.L)**(-1/4))
|
|
88
|
+
#n = np.where(self.L > 0, 1/(1+5*self.z/self.L), (1-24*self.z/self.L)/(1-16*self.z/self.L))
|
|
89
|
+
kappa = k_karman*self.u_star*self.z/(stability_c*self.z**n)
|
|
90
|
+
m = self.u_star*stability_m/(k_karman*self.u)
|
|
91
|
+
r = 2+m-n
|
|
92
|
+
mu = (1+m)/r
|
|
93
|
+
#U = self.u/self.z**m
|
|
94
|
+
U = self.u_star*stability_m/(k_karman*m*self.z**m)
|
|
95
|
+
xi = U*self.z**r/(r**2*kappa)
|
|
96
|
+
|
|
97
|
+
#calculate crosswind_integrated_flux
|
|
98
|
+
crosswind_integrated_flux = np.expand_dims(xi**mu/gamma(mu), axis=(1)) / self.xs_for_calculation_grid**np.expand_dims((1+mu), axis=(1)) * np.exp(-np.expand_dims(xi, axis=(1))/self.xs_for_calculation_grid)
|
|
99
|
+
if not np.all(np.logical_or(np.isnan(crosswind_integrated_flux), crosswind_integrated_flux==0)):
|
|
100
|
+
crosswind_integrated_flux = np.where(np.isnan(crosswind_integrated_flux), 0, crosswind_integrated_flux)
|
|
101
|
+
self.crosswind_integrated_flux = crosswind_integrated_flux
|