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.

Files changed (67) hide show
  1. pypromice/__init__.py +2 -0
  2. pypromice/{qc → core/qc}/github_data_issues.py +22 -13
  3. pypromice/{qc → core/qc}/percentiles/compute_thresholds.py +2 -2
  4. pypromice/{qc → core/qc}/persistence.py +22 -29
  5. pypromice/{process → core/qc}/value_clipping.py +3 -3
  6. pypromice/core/resampling.py +142 -0
  7. pypromice/core/variables/__init__.py +1 -0
  8. pypromice/core/variables/air_temperature.py +64 -0
  9. pypromice/core/variables/gps.py +221 -0
  10. pypromice/core/variables/humidity.py +111 -0
  11. pypromice/core/variables/precipitation.py +108 -0
  12. pypromice/core/variables/pressure_transducer_depth.py +79 -0
  13. pypromice/core/variables/radiation.py +422 -0
  14. pypromice/core/variables/station_boom_height.py +75 -0
  15. pypromice/core/variables/station_pose.py +375 -0
  16. pypromice/io/bufr/__init__.py +0 -0
  17. pypromice/{postprocess → io/bufr}/bufr_to_csv.py +1 -1
  18. pypromice/{postprocess → io/bufr}/create_bufr_files.py +2 -2
  19. pypromice/{postprocess → io/bufr}/get_bufr.py +6 -6
  20. pypromice/{postprocess → io/bufr}/real_time_utilities.py +3 -3
  21. pypromice/io/ingest/__init__.py +0 -0
  22. pypromice/{utilities → io/ingest}/git.py +1 -3
  23. pypromice/io/ingest/l0.py +294 -0
  24. pypromice/io/ingest/l0_repository.py +103 -0
  25. pypromice/io/ingest/toa5.py +87 -0
  26. pypromice/{process → io}/write.py +1 -1
  27. pypromice/pipeline/L0toL1.py +291 -0
  28. pypromice/pipeline/L1toL2.py +233 -0
  29. pypromice/{process → pipeline}/L2toL3.py +113 -118
  30. pypromice/pipeline/__init__.py +4 -0
  31. pypromice/{process → pipeline}/aws.py +10 -82
  32. pypromice/{process → pipeline}/get_l2.py +2 -2
  33. pypromice/{process → pipeline}/get_l2tol3.py +19 -22
  34. pypromice/{process → pipeline}/join_l2.py +31 -32
  35. pypromice/{process → pipeline}/join_l3.py +16 -14
  36. pypromice/{process → pipeline}/resample.py +75 -51
  37. pypromice/{process → pipeline}/utilities.py +0 -22
  38. pypromice/resources/file_attributes.csv +4 -4
  39. pypromice/resources/variable_aliases_GC-Net.csv +2 -2
  40. pypromice/resources/variables.csv +27 -24
  41. {pypromice-1.5.3.dist-info → pypromice-1.7.0.dist-info}/METADATA +1 -2
  42. pypromice-1.7.0.dist-info/RECORD +65 -0
  43. pypromice-1.7.0.dist-info/entry_points.txt +12 -0
  44. pypromice/get/__init__.py +0 -1
  45. pypromice/get/get.py +0 -211
  46. pypromice/get/get_promice_data.py +0 -56
  47. pypromice/process/L0toL1.py +0 -564
  48. pypromice/process/L1toL2.py +0 -824
  49. pypromice/process/__init__.py +0 -4
  50. pypromice/process/load.py +0 -161
  51. pypromice-1.5.3.dist-info/RECORD +0 -54
  52. pypromice-1.5.3.dist-info/entry_points.txt +0 -13
  53. /pypromice/{postprocess → core}/__init__.py +0 -0
  54. /pypromice/{utilities → core}/dependency_graph.py +0 -0
  55. /pypromice/{qc → core/qc}/__init__.py +0 -0
  56. /pypromice/{qc → core/qc}/percentiles/__init__.py +0 -0
  57. /pypromice/{qc → core/qc}/percentiles/outlier_detector.py +0 -0
  58. /pypromice/{qc → core/qc}/percentiles/thresholds.csv +0 -0
  59. /pypromice/{process → core/variables}/wind.py +0 -0
  60. /pypromice/{utilities → io}/__init__.py +0 -0
  61. /pypromice/{postprocess → io/bufr}/bufr_utilities.py +0 -0
  62. /pypromice/{postprocess → io/bufr}/positions_seed.csv +0 -0
  63. /pypromice/{station_configuration.py → io/bufr/station_configuration.py} +0 -0
  64. /pypromice/{postprocess → io}/make_metadata_csv.py +0 -0
  65. {pypromice-1.5.3.dist-info → pypromice-1.7.0.dist-info}/WHEEL +0 -0
  66. {pypromice-1.5.3.dist-info → pypromice-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
  67. {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