pypromice 1.5.3__py3-none-any.whl → 1.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pypromice might be problematic. Click here for more details.
- pypromice/__init__.py +2 -0
- pypromice/{qc → core/qc}/github_data_issues.py +22 -13
- pypromice/{qc → core/qc}/percentiles/compute_thresholds.py +2 -2
- pypromice/{qc → core/qc}/persistence.py +22 -29
- pypromice/{process → core/qc}/value_clipping.py +3 -3
- pypromice/core/resampling.py +142 -0
- pypromice/core/variables/__init__.py +1 -0
- pypromice/core/variables/air_temperature.py +64 -0
- pypromice/core/variables/gps.py +221 -0
- pypromice/core/variables/humidity.py +111 -0
- pypromice/core/variables/precipitation.py +108 -0
- pypromice/core/variables/pressure_transducer_depth.py +79 -0
- pypromice/core/variables/radiation.py +422 -0
- pypromice/core/variables/station_boom_height.py +75 -0
- pypromice/core/variables/station_pose.py +375 -0
- pypromice/io/bufr/__init__.py +0 -0
- pypromice/{postprocess → io/bufr}/bufr_to_csv.py +1 -1
- pypromice/{postprocess → io/bufr}/create_bufr_files.py +2 -2
- pypromice/{postprocess → io/bufr}/get_bufr.py +6 -6
- pypromice/{postprocess → io/bufr}/real_time_utilities.py +3 -3
- pypromice/io/ingest/__init__.py +0 -0
- pypromice/{utilities → io/ingest}/git.py +1 -3
- pypromice/io/ingest/l0.py +294 -0
- pypromice/io/ingest/l0_repository.py +103 -0
- pypromice/io/ingest/toa5.py +87 -0
- pypromice/{process → io}/write.py +1 -1
- pypromice/pipeline/L0toL1.py +291 -0
- pypromice/pipeline/L1toL2.py +233 -0
- pypromice/{process → pipeline}/L2toL3.py +113 -118
- pypromice/pipeline/__init__.py +4 -0
- pypromice/{process → pipeline}/aws.py +10 -82
- pypromice/{process → pipeline}/get_l2.py +2 -2
- pypromice/{process → pipeline}/get_l2tol3.py +19 -22
- pypromice/{process → pipeline}/join_l2.py +31 -32
- pypromice/{process → pipeline}/join_l3.py +16 -14
- pypromice/{process → pipeline}/resample.py +75 -51
- pypromice/{process → pipeline}/utilities.py +0 -22
- pypromice/resources/file_attributes.csv +4 -4
- pypromice/resources/variable_aliases_GC-Net.csv +2 -2
- pypromice/resources/variables.csv +27 -24
- {pypromice-1.5.3.dist-info → pypromice-1.7.0.dist-info}/METADATA +1 -2
- pypromice-1.7.0.dist-info/RECORD +65 -0
- pypromice-1.7.0.dist-info/entry_points.txt +12 -0
- pypromice/get/__init__.py +0 -1
- pypromice/get/get.py +0 -211
- pypromice/get/get_promice_data.py +0 -56
- pypromice/process/L0toL1.py +0 -564
- pypromice/process/L1toL2.py +0 -824
- pypromice/process/__init__.py +0 -4
- pypromice/process/load.py +0 -161
- pypromice-1.5.3.dist-info/RECORD +0 -54
- pypromice-1.5.3.dist-info/entry_points.txt +0 -13
- /pypromice/{postprocess → core}/__init__.py +0 -0
- /pypromice/{utilities → core}/dependency_graph.py +0 -0
- /pypromice/{qc → core/qc}/__init__.py +0 -0
- /pypromice/{qc → core/qc}/percentiles/__init__.py +0 -0
- /pypromice/{qc → core/qc}/percentiles/outlier_detector.py +0 -0
- /pypromice/{qc → core/qc}/percentiles/thresholds.csv +0 -0
- /pypromice/{process → core/variables}/wind.py +0 -0
- /pypromice/{utilities → io}/__init__.py +0 -0
- /pypromice/{postprocess → io/bufr}/bufr_utilities.py +0 -0
- /pypromice/{postprocess → io/bufr}/positions_seed.csv +0 -0
- /pypromice/{station_configuration.py → io/bufr/station_configuration.py} +0 -0
- /pypromice/{postprocess → io}/make_metadata_csv.py +0 -0
- {pypromice-1.5.3.dist-info → pypromice-1.7.0.dist-info}/WHEEL +0 -0
- {pypromice-1.5.3.dist-info → pypromice-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
- {pypromice-1.5.3.dist-info → pypromice-1.7.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
__all__ = ["adjust", "convert", "calculate_specific_humidity"]
|
|
2
|
+
|
|
3
|
+
import xarray as xr
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
# Define constants
|
|
7
|
+
T_0 = 273.15 # Ice point temperature (Kelvins)
|
|
8
|
+
T_100 = T_0+100 # Steam point temperature (Kelvins)
|
|
9
|
+
ews = 1013.246 # Saturation vapour pressure at steam point temperature (normal atmosphere) (hPa)
|
|
10
|
+
ei0 = 6.1071 # Saturation vapour pressure at ice melting point temperature (normal atmosphere) (hPa)
|
|
11
|
+
eps=0.622 # Ratio of molar masses of vapor and dry air
|
|
12
|
+
|
|
13
|
+
def adjust(rh: xr.DataArray,
|
|
14
|
+
t: xr.DataArray
|
|
15
|
+
) -> xr.DataArray:
|
|
16
|
+
"""Correct relative humidity so that values are given with respect to
|
|
17
|
+
saturation over ice in subfreezing conditions, and with respect to
|
|
18
|
+
saturation over water (as given by the instrument) above the melting
|
|
19
|
+
point temperature. Saturation water vapors are calculated after
|
|
20
|
+
Groff & Gratch method.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
rh : xr.DataArray
|
|
25
|
+
Relative humidity
|
|
26
|
+
t : xr.DataArray
|
|
27
|
+
Air temperature
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
rh_wrt_ice_or_water : xr.DataArray
|
|
32
|
+
CAdjusted relative humidity
|
|
33
|
+
"""
|
|
34
|
+
# Convert to hPa (Groff & Gratch)
|
|
35
|
+
e_s_wtr = 10**(-7.90298 * (T_100 / (t + T_0) - 1)
|
|
36
|
+
+ 5.02808 * np.log10(T_100 / (t + T_0))
|
|
37
|
+
- 1.3816E-7 * (10**(11.344 * (1 - (t + T_0) / T_100)) - 1)
|
|
38
|
+
+ 8.1328E-3 * (10**(-3.49149 * (T_100/(t + T_0) - 1)) -1)
|
|
39
|
+
+ np.log10(ews))
|
|
40
|
+
e_s_ice = 10**(-9.09718 * (T_0 / (t + T_0) - 1)
|
|
41
|
+
- 3.56654 * np.log10(T_0 / (t + T_0))
|
|
42
|
+
+ 0.876793 * (1 - (t + T_0) / T_0)
|
|
43
|
+
+ np.log10(ei0))
|
|
44
|
+
|
|
45
|
+
# Define freezing point. Why > -100?
|
|
46
|
+
nan_mask = t.notnull()
|
|
47
|
+
freezing = (t < 0) & (t > -100) & nan_mask
|
|
48
|
+
|
|
49
|
+
# Set to Groff & Gratch values when freezing, otherwise just rh
|
|
50
|
+
rh_wrt_ice_or_water = rh.where(~freezing & nan_mask,
|
|
51
|
+
other=rh*(e_s_wtr/e_s_ice))
|
|
52
|
+
return rh_wrt_ice_or_water
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def calculate_specific_humidity(t, p, rh_wrt_ice_or_water):
|
|
56
|
+
"""Calculate specific humidity.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
t : xr.DataArray
|
|
61
|
+
Air temperature
|
|
62
|
+
p : xr.DataArray
|
|
63
|
+
Air pressure
|
|
64
|
+
rh_wrt_ice_or_water : xr.DataArray
|
|
65
|
+
Adjusted relative humidity
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
xr.DataArray
|
|
70
|
+
Specific humidity (kg/kg)
|
|
71
|
+
"""
|
|
72
|
+
# Saturation vapour pressure above 0 C (hPa)
|
|
73
|
+
es_wtr = 10**(-7.90298 * (T_100 / (t + T_0) - 1) + 5.02808 * np.log10(T_100 / (t + T_0))
|
|
74
|
+
- 1.3816E-7 * (10**(11.344 * (1 - (t + T_0) / T_100)) - 1)
|
|
75
|
+
+ 8.1328E-3 * (10**(-3.49149 * (T_100 / (t + T_0) -1)) - 1) + np.log10(ews))
|
|
76
|
+
|
|
77
|
+
# Saturation vapour pressure below 0 C (hPa)
|
|
78
|
+
es_ice = 10**(-9.09718 * (T_0 / (t + T_0) - 1) - 3.56654
|
|
79
|
+
* np.log10(T_0 / (t + T_0)) + 0.876793
|
|
80
|
+
* (1 - (t + T_0) / T_0)
|
|
81
|
+
+ np.log10(ei0))
|
|
82
|
+
|
|
83
|
+
# Specific humidity at saturation (incorrect below melting point)
|
|
84
|
+
q_sat = eps * es_wtr / (p - (1 - eps) * es_wtr)
|
|
85
|
+
|
|
86
|
+
# Replace saturation specific humidity values below melting point
|
|
87
|
+
freezing = t < 0
|
|
88
|
+
q_sat[freezing] = eps * es_ice[freezing] / (p[freezing] - (1 - eps) * es_ice[freezing])
|
|
89
|
+
|
|
90
|
+
# Mask where temperature or pressure are null values
|
|
91
|
+
q_nan = np.isnan(t) | np.isnan(p)
|
|
92
|
+
q_sat[q_nan] = np.nan
|
|
93
|
+
|
|
94
|
+
# Convert to kg/kg
|
|
95
|
+
return rh_wrt_ice_or_water * q_sat / 100
|
|
96
|
+
|
|
97
|
+
def convert(qh: xr.DataArray
|
|
98
|
+
) -> xr.DataArray:
|
|
99
|
+
"""Convert specific humidity from kg/kg to g/kg units
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
qh : xr.DataArray
|
|
104
|
+
Specific humidity (kg/kg)
|
|
105
|
+
|
|
106
|
+
Returns
|
|
107
|
+
-------
|
|
108
|
+
xr.DataArray
|
|
109
|
+
Specific humidity (g/kg)
|
|
110
|
+
"""
|
|
111
|
+
return 1000 * qh
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
__all__ = ["correct_rainfall_undercatch", "get_rainfall_per_timestep", "filter_lufft_errors"]
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import xarray as xr
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def filter_lufft_errors(
|
|
8
|
+
precip: xr.DataArray, t: xr.DataArray, p: xr.DataArray, rh: xr.DataArray
|
|
9
|
+
) -> xr.DataArray:
|
|
10
|
+
"""Filter precipitation measurements where air temperature, pressure, or
|
|
11
|
+
relative humidity measurements are null values. This assumes that
|
|
12
|
+
air temperature, air pressure, relative humidity and precipitation
|
|
13
|
+
measurements are measured using the same instrument, e.g. a
|
|
14
|
+
lufft instrument.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
precip : xr.DataArray
|
|
19
|
+
Cumulative precipitation measurements
|
|
20
|
+
t : xr.DataArray
|
|
21
|
+
Air temperature measurements
|
|
22
|
+
p : xr.DataArray
|
|
23
|
+
Air pressure measurements
|
|
24
|
+
rh : xr.DataArray
|
|
25
|
+
Relative humidity measurements
|
|
26
|
+
|
|
27
|
+
Returns
|
|
28
|
+
-------
|
|
29
|
+
xr.DataArray
|
|
30
|
+
Filtered precipitation values
|
|
31
|
+
"""
|
|
32
|
+
mask = (t.isnull() | p.isnull() | rh.isnull()) & (precip == 0)
|
|
33
|
+
return precip.where(~mask)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def correct_rainfall_undercatch(
|
|
37
|
+
rainfall_per_timestep: xr.DataArray, wspd: xr.DataArray
|
|
38
|
+
) -> xr.DataArray:
|
|
39
|
+
"""Corrects rainfall amount per timestep for undercatch as in
|
|
40
|
+
Yang et al. (1999) and Box et al. (2022), based on Goodison et al. (1998).
|
|
41
|
+
|
|
42
|
+
Yang, D., Ishida, S., Goodison, B. E., and Gunther, T.: Bias correction of
|
|
43
|
+
daily precipitation measurements for Greenland,
|
|
44
|
+
https://doi.org/10.1029/1998jd200110, 1999.
|
|
45
|
+
|
|
46
|
+
Box, J., Wehrle, A., van As, D., Fausto, R., Kjeldsen, K., Dachauer, A.,
|
|
47
|
+
Ahlstrom, A. P., and Picard, G.: Greenland Ice Sheet rainfall, heat and
|
|
48
|
+
albedo feedback imapacts from the Mid-August 2021 atmospheric river,
|
|
49
|
+
Geophys. Res. Lett. 49 (11), e2021GL097356,
|
|
50
|
+
https://doi.org/10.1029/2021GL097356, 2022.
|
|
51
|
+
|
|
52
|
+
Goodison, B. E., Louie, P. Y. T., and Yang, D.: Solid Precipitation
|
|
53
|
+
Measurement Intercomparison, WMO, 1998
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
rainfall : xr.DataArray
|
|
58
|
+
Uncorrected rainfall per timestep
|
|
59
|
+
wspd : xr.DataArray
|
|
60
|
+
Wind speed measurements
|
|
61
|
+
|
|
62
|
+
Returns
|
|
63
|
+
-------
|
|
64
|
+
rainfall_cor : xr.DataArray
|
|
65
|
+
Corrected rainfall per timestep
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
# Calculate undercatch correction factor
|
|
69
|
+
corr = 100 / (100.00 - 4.37 * wspd + 0.35 * wspd * wspd)
|
|
70
|
+
|
|
71
|
+
# Fix all values below 1.02 to 1.02
|
|
72
|
+
corr = corr.where(corr > 1.02, other=1.02)
|
|
73
|
+
|
|
74
|
+
# Apply correction to rate
|
|
75
|
+
rainfall_per_timestep_cor = rainfall_per_timestep * corr
|
|
76
|
+
|
|
77
|
+
return rainfall_per_timestep_cor
|
|
78
|
+
|
|
79
|
+
def get_rainfall_per_timestep(
|
|
80
|
+
precip: xr.DataArray,
|
|
81
|
+
t: xr.DataArray
|
|
82
|
+
) -> xr.DataArray:
|
|
83
|
+
"""
|
|
84
|
+
Derive rainfall per timestep from cumulative precipitation data.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
precip : xr.DataArray
|
|
89
|
+
Cumulative precipitation measurements.
|
|
90
|
+
t : xr.DataArray
|
|
91
|
+
Air temperature measurements.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
xr.DataArray
|
|
96
|
+
Rainfall per timestep with negative values removed and
|
|
97
|
+
cold-season precipitation (T < -2 °C) filtered out.
|
|
98
|
+
"""
|
|
99
|
+
rainfall_per_timestep = precip.diff("time").reindex_like(precip)
|
|
100
|
+
|
|
101
|
+
# Removing all negative precipitation, both corrected and uncorrected
|
|
102
|
+
rainfall_per_timestep = rainfall_per_timestep.where(rainfall_per_timestep >= 0)
|
|
103
|
+
|
|
104
|
+
# Filtering cold season precipitation, both corrected and uncorrected
|
|
105
|
+
rain_in_cold = (rainfall_per_timestep > 0) & (t < -2)
|
|
106
|
+
rainfall_per_timestep = rainfall_per_timestep.where(~rain_in_cold)
|
|
107
|
+
|
|
108
|
+
return rainfall_per_timestep
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
__all__ = ["correct_and_calculate_depth", "apply_offset"]
|
|
2
|
+
|
|
3
|
+
import xarray as xr
|
|
4
|
+
import numpy as np
|
|
5
|
+
import logging
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
def correct_and_calculate_depth(z_pt: xr.DataArray,
|
|
9
|
+
air_pressure: xr.DataArray,
|
|
10
|
+
pt_antifreeze: float,
|
|
11
|
+
pt_z_factor: float,
|
|
12
|
+
pt_z_coef: float,
|
|
13
|
+
pt_z_p_coef: float
|
|
14
|
+
) -> tuple[xr.DataArray, xr.DataArray]:
|
|
15
|
+
"""Adjust pressure depth and calculate pressure transducer depth based on
|
|
16
|
+
pressure transducer fluid density
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
z_pt : xr.DataArray
|
|
21
|
+
Pressure transducer height (corrected for offset)
|
|
22
|
+
air_pressure : xr.DataArray
|
|
23
|
+
Air pressure
|
|
24
|
+
pt_antifreeze : float
|
|
25
|
+
Pressure transducer anti-freeze percentage for fluid density
|
|
26
|
+
correction
|
|
27
|
+
pt_z_factor : float
|
|
28
|
+
Pressure transducer factor
|
|
29
|
+
pt_z_coef : float
|
|
30
|
+
Pressure transducer coefficient
|
|
31
|
+
pt_z_p_coef : float
|
|
32
|
+
Pressure transducer coefficient
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
z_pt_cor : xr.DataArray
|
|
37
|
+
Pressure transducer height corrected
|
|
38
|
+
z_pt : xr.DataArray
|
|
39
|
+
Pressure transducer depth
|
|
40
|
+
"""
|
|
41
|
+
# Calculate pressure transducer fluid density
|
|
42
|
+
# TODO: Implement function w/ reference (analytical or from LUT)
|
|
43
|
+
# TODO: Track uncertainty
|
|
44
|
+
if pt_antifreeze == 50:
|
|
45
|
+
rho_af = 1092
|
|
46
|
+
elif pt_antifreeze == 100:
|
|
47
|
+
rho_af = 1145
|
|
48
|
+
else:
|
|
49
|
+
rho_af = np.nan
|
|
50
|
+
logger.info('ERROR: Incorrect metadata: "pt_antifreeze" = ' +
|
|
51
|
+
f'{pt_antifreeze}. Antifreeze mix only supported at 50% or 100%')
|
|
52
|
+
# assert(False)
|
|
53
|
+
|
|
54
|
+
# Correct pressure depth
|
|
55
|
+
z_pt_cor = z_pt * pt_z_coef * pt_z_factor * 998.0 / rho_af + 100 * (pt_z_p_coef - air_pressure) / (rho_af * 9.81)
|
|
56
|
+
|
|
57
|
+
# Calculate pressure transducer depth
|
|
58
|
+
z_pt = z_pt * pt_z_coef * pt_z_factor * 998.0 / rho_af
|
|
59
|
+
|
|
60
|
+
return z_pt_cor, z_pt
|
|
61
|
+
|
|
62
|
+
def apply_offset(z_pt: xr.DataArray,
|
|
63
|
+
z_pt_offset: int
|
|
64
|
+
) -> xr.DataArray:
|
|
65
|
+
"""Apply defined offset to pressure transducer height
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
z_pt : xr.DataArray
|
|
70
|
+
Pressure transducer height
|
|
71
|
+
z_pt_offset : xr.DataArray
|
|
72
|
+
Transducer height offset
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
xr.DataArray
|
|
77
|
+
Adjusted pressure transducer height
|
|
78
|
+
"""
|
|
79
|
+
return z_pt + z_pt_offset
|