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
|
@@ -3,21 +3,20 @@ import logging, sys, os, unittest
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import xarray as xr
|
|
5
5
|
from argparse import ArgumentParser
|
|
6
|
-
from pypromice.
|
|
7
|
-
from pypromice.process.write import prepare_and_write
|
|
6
|
+
from pypromice.io.write import prepare_and_write
|
|
8
7
|
logger = logging.getLogger(__name__)
|
|
9
8
|
|
|
10
9
|
def parse_arguments_join():
|
|
11
10
|
parser = ArgumentParser(description="AWS L2 joiner for merging together two L2 products, for example an L2 RAW and L2 TX data product. An hourly, daily and monthly L2 data product is outputted to the defined output path")
|
|
12
11
|
parser.add_argument('-s', '--file1', type=str, required=True,
|
|
13
12
|
help='Path to source L2 file, which will be preferenced in merge process')
|
|
14
|
-
parser.add_argument('-t', '--file2', type=str, required=True,
|
|
13
|
+
parser.add_argument('-t', '--file2', type=str, required=True,
|
|
15
14
|
help='Path to target L2 file, which will be used to fill gaps in merge process')
|
|
16
|
-
parser.add_argument('-o', '--outpath', default=os.getcwd(), type=str, required=True,
|
|
15
|
+
parser.add_argument('-o', '--outpath', default=os.getcwd(), type=str, required=True,
|
|
17
16
|
help='Path where to write output')
|
|
18
|
-
parser.add_argument('-v', '--variables', default=None, type=str, required=False,
|
|
17
|
+
parser.add_argument('-v', '--variables', default=None, type=str, required=False,
|
|
19
18
|
help='Path to variables look-up table .csv file for variable name retained'''),
|
|
20
|
-
parser.add_argument('-m', '--metadata', default=None, type=str, required=False,
|
|
19
|
+
parser.add_argument('-m', '--metadata', default=None, type=str, required=False,
|
|
21
20
|
help='Path to metadata table .csv file for metadata information'''),
|
|
22
21
|
args = parser.parse_args()
|
|
23
22
|
return args
|
|
@@ -25,7 +24,7 @@ def parse_arguments_join():
|
|
|
25
24
|
def loadArr(infile):
|
|
26
25
|
if infile.split('.')[-1].lower() == 'csv':
|
|
27
26
|
df = pd.read_csv(infile, index_col=0, parse_dates=True)
|
|
28
|
-
ds = xr.Dataset.from_dataframe(df)
|
|
27
|
+
ds = xr.Dataset.from_dataframe(df)
|
|
29
28
|
elif infile.split('.')[-1].lower() == 'nc':
|
|
30
29
|
with xr.open_dataset(infile) as ds:
|
|
31
30
|
ds.load()
|
|
@@ -35,7 +34,7 @@ def loadArr(infile):
|
|
|
35
34
|
ds[varname].encoding = {}
|
|
36
35
|
|
|
37
36
|
try:
|
|
38
|
-
name = ds.attrs['station_id']
|
|
37
|
+
name = ds.attrs['station_id']
|
|
39
38
|
except:
|
|
40
39
|
name = infile.split('/')[-1].split('.')[0].split('_hour')[0].split('_10min')[0]
|
|
41
40
|
ds.attrs['station_id'] = name
|
|
@@ -46,7 +45,6 @@ def loadArr(infile):
|
|
|
46
45
|
|
|
47
46
|
logger.info(f'{name} array loaded from {infile}')
|
|
48
47
|
return ds, name
|
|
49
|
-
|
|
50
48
|
|
|
51
49
|
def join_l2(file1,file2,outpath,variables,metadata) -> xr.Dataset:
|
|
52
50
|
logging.basicConfig(
|
|
@@ -55,43 +53,44 @@ def join_l2(file1,file2,outpath,variables,metadata) -> xr.Dataset:
|
|
|
55
53
|
stream=sys.stdout,
|
|
56
54
|
)
|
|
57
55
|
# Check files
|
|
58
|
-
if os.path.isfile(file1) and os.path.isfile(file2):
|
|
56
|
+
if os.path.isfile(file1) and os.path.isfile(file2):
|
|
59
57
|
|
|
60
58
|
# Load data arrays
|
|
61
59
|
ds1, n1 = loadArr(file1)
|
|
62
|
-
ds2, n2 = loadArr(file2)
|
|
63
|
-
|
|
60
|
+
ds2, n2 = loadArr(file2)
|
|
61
|
+
|
|
64
62
|
# Check stations match
|
|
65
63
|
if n1.lower() == n2.lower():
|
|
66
|
-
|
|
67
|
-
# Merge arrays
|
|
64
|
+
# Merge arrays
|
|
68
65
|
logger.info(f'Combining {file1} with {file2}...')
|
|
69
66
|
name = n1
|
|
70
67
|
all_ds = ds1.combine_first(ds2)
|
|
71
68
|
|
|
72
|
-
#
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
# combine_first works terrible for accumulated values
|
|
70
|
+
# we rather combine semi-accumulated precipitation in block
|
|
71
|
+
for var in ['precip_u', 'precip_l']:
|
|
72
|
+
if hasattr(all_ds, var):
|
|
73
|
+
if all_ds[var].notnull().any():
|
|
74
|
+
tx_data_no_overlap =(ds2[var]
|
|
75
|
+
.sel(time=slice(ds1.time.values[-1], ds2.time.values[-1]))
|
|
76
|
+
.isel(time=slice(1, None))) # this line prevents redundant timestamps
|
|
77
|
+
all_ds[var] = xr.concat(
|
|
78
|
+
[ds1[var], tx_data_no_overlap], dim='time'
|
|
79
|
+
).sortby('time')
|
|
81
80
|
else:
|
|
82
81
|
logger.info(f'Mismatched station names {n1}, {n2}')
|
|
83
|
-
exit()
|
|
84
|
-
|
|
85
|
-
elif os.path.isfile(file1):
|
|
82
|
+
exit()
|
|
83
|
+
|
|
84
|
+
elif os.path.isfile(file1):
|
|
86
85
|
ds1, name = loadArr(file1)
|
|
87
86
|
logger.info(f'Only one file found {file1}...')
|
|
88
|
-
all_ds = ds1
|
|
87
|
+
all_ds = ds1
|
|
89
88
|
|
|
90
89
|
elif os.path.isfile(file2):
|
|
91
90
|
ds2, name = loadArr(file2)
|
|
92
91
|
logger.info(f'Only one file found {file2}...')
|
|
93
|
-
all_ds = ds2
|
|
94
|
-
|
|
92
|
+
all_ds = ds2
|
|
93
|
+
|
|
95
94
|
else:
|
|
96
95
|
logger.info(f'Invalid files {file1}, {file2}')
|
|
97
96
|
exit()
|
|
@@ -100,13 +99,13 @@ def join_l2(file1,file2,outpath,variables,metadata) -> xr.Dataset:
|
|
|
100
99
|
|
|
101
100
|
# Resample to hourly, daily and monthly datasets and write to file
|
|
102
101
|
prepare_and_write(all_ds, outpath, variables, metadata, resample = False)
|
|
103
|
-
|
|
102
|
+
|
|
104
103
|
logger.info(f'Files saved to {os.path.join(outpath, name)}...')
|
|
105
104
|
return all_ds
|
|
106
105
|
|
|
107
106
|
def main():
|
|
108
107
|
args = parse_arguments_join()
|
|
109
108
|
_ = join_l2(args.file1, args.file2, args.outpath, args.variables, args.metadata)
|
|
110
|
-
|
|
111
|
-
if __name__ == "__main__":
|
|
109
|
+
|
|
110
|
+
if __name__ == "__main__":
|
|
112
111
|
main()
|
|
@@ -3,10 +3,11 @@ import json
|
|
|
3
3
|
import logging, os, sys, toml
|
|
4
4
|
from argparse import ArgumentParser
|
|
5
5
|
|
|
6
|
-
from pypromice.
|
|
6
|
+
from pypromice.io.ingest.git import get_commit_hash_and_check_dirty
|
|
7
7
|
|
|
8
8
|
import pypromice.resources
|
|
9
|
-
from pypromice.
|
|
9
|
+
from pypromice.io.write import prepare_and_write
|
|
10
|
+
|
|
10
11
|
import numpy as np
|
|
11
12
|
import pandas as pd
|
|
12
13
|
import xarray as xr
|
|
@@ -493,20 +494,21 @@ def join_l3(config_folder, site, folder_l3, folder_gcnet, outpath, variables, me
|
|
|
493
494
|
l3_merged.z_ice_surf.to_series(), l3.z_ice_surf.to_series()
|
|
494
495
|
),
|
|
495
496
|
)
|
|
496
|
-
|
|
497
|
+
|
|
497
498
|
# saves attributes
|
|
498
499
|
attrs = l3_merged.attrs
|
|
499
500
|
# merging by time block
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
501
|
+
t_start = l3.time.values[0]
|
|
502
|
+
t_stop = l3_merged.time.values[0]
|
|
503
|
+
|
|
504
|
+
l3_part = l3.sel(time=slice(t_start, t_stop))
|
|
505
|
+
|
|
506
|
+
# we don't want the first timestamp of l3_merged to also be in l3
|
|
507
|
+
if l3_part.time.values[-1] == t_stop:
|
|
508
|
+
l3_part = l3_part.isel(time=slice(None, -1))
|
|
509
|
+
|
|
510
|
+
l3_merged = xr.concat([l3_part, l3_merged], dim="time")
|
|
511
|
+
|
|
510
512
|
# restauring attributes
|
|
511
513
|
l3_merged.attrs = attrs
|
|
512
514
|
|
|
@@ -540,7 +542,7 @@ def join_l3(config_folder, site, folder_l3, folder_gcnet, outpath, variables, me
|
|
|
540
542
|
if outpath is not None:
|
|
541
543
|
prepare_and_write(l3_merged, outpath, v, m, "60min", nc_compression=True)
|
|
542
544
|
prepare_and_write(l3_merged, outpath, v, m, "1D", nc_compression=True)
|
|
543
|
-
prepare_and_write(l3_merged, outpath, v, m, "
|
|
545
|
+
prepare_and_write(l3_merged, outpath, v, m, "MS", nc_compression=True)
|
|
544
546
|
return l3_merged, sorted_list_station_data
|
|
545
547
|
|
|
546
548
|
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
|
-
"""
|
|
4
|
-
Created on Mon Jun 10 10:58:39 2024
|
|
5
|
-
|
|
6
|
-
@author: pho
|
|
7
|
-
"""
|
|
8
3
|
import logging
|
|
9
4
|
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
10
6
|
import xarray as xr
|
|
11
|
-
|
|
7
|
+
|
|
8
|
+
from pypromice.core.resampling import get_completeness_mask, DEFAULT_COMPLETENESS_THRESHOLDS
|
|
9
|
+
from pypromice.core.variables.wind import calculate_directional_wind_speed
|
|
12
10
|
logger = logging.getLogger(__name__)
|
|
13
11
|
|
|
14
|
-
def resample_dataset(ds_h, t):
|
|
12
|
+
def resample_dataset(ds_h, t, completeness_thresholds=DEFAULT_COMPLETENESS_THRESHOLDS):
|
|
15
13
|
'''Resample L2 AWS data, e.g. hourly to daily average. This uses pandas
|
|
16
14
|
DataFrame resampling at the moment as a work-around to the xarray Dataset
|
|
17
15
|
resampling. As stated, xarray resampling is a lengthy process that takes
|
|
@@ -24,8 +22,13 @@ def resample_dataset(ds_h, t):
|
|
|
24
22
|
ds_h : xarray.Dataset
|
|
25
23
|
L3 AWS dataset either at 10 min (for raw data) or hourly (for tx data)
|
|
26
24
|
t : str
|
|
27
|
-
Resample factor, same variable definition as in
|
|
25
|
+
Resample factor( "60min", "1D" or "MS"), same variable definition as in
|
|
28
26
|
pandas.DataFrame.resample()
|
|
27
|
+
completeness_thresholds : Dict
|
|
28
|
+
A dict with, for each variable, the lower limit of completness of an
|
|
29
|
+
hourly/daily/monthly aggregate (nr of samples in aggregate / expected
|
|
30
|
+
nr of samples). Aggregates below that limit are replaced by NaNs.
|
|
31
|
+
Must include a "default" value used for variables not listed explicitly.
|
|
29
32
|
|
|
30
33
|
Returns
|
|
31
34
|
-------
|
|
@@ -33,20 +36,42 @@ def resample_dataset(ds_h, t):
|
|
|
33
36
|
L3 AWS dataset resampled to the frequency defined by t
|
|
34
37
|
'''
|
|
35
38
|
# Convert dataset to DataFrame
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
df_h = ds_h.to_dataframe()
|
|
40
|
+
|
|
38
41
|
# Identify non-numeric columns
|
|
39
|
-
non_numeric_cols =
|
|
40
|
-
|
|
42
|
+
non_numeric_cols = df_h.select_dtypes(exclude=['number']).columns
|
|
43
|
+
|
|
41
44
|
# Log a warning and drop non-numeric columns
|
|
42
45
|
if len(non_numeric_cols) > 0:
|
|
43
46
|
for col in non_numeric_cols:
|
|
44
|
-
unique_values =
|
|
45
|
-
logger.warning(f"Dropping column '{col}' because it is of type '{
|
|
47
|
+
unique_values = df_h[col].unique()
|
|
48
|
+
logger.warning(f"Dropping column '{col}' because it is of type '{df_h[col].dtype}' and contains unique values: {unique_values}")
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
df_h = df_h.drop(columns=non_numeric_cols)
|
|
48
51
|
# Resample the DataFrame
|
|
49
|
-
|
|
52
|
+
df_resampled = df_h.resample(t).mean()
|
|
53
|
+
|
|
54
|
+
# exception for precip_u and precip_l which are semi-accumulated with some resets
|
|
55
|
+
# Taking the max value within the resampled time step will preserve the
|
|
56
|
+
# general shape of the curve
|
|
57
|
+
for var in ['precip_u', 'precip_l']:
|
|
58
|
+
if var in df_h.columns:
|
|
59
|
+
df_resampled[var] = df_h[var].resample(t).max()
|
|
60
|
+
|
|
61
|
+
# exception for rainfall which should be summed when aggregated into
|
|
62
|
+
# hourly/daily/monthly values. This ignores missing data.
|
|
63
|
+
for var in ['rainfall_u', 'rainfall_cor_u', 'rainfall_l', 'rainfall_cor_l']:
|
|
64
|
+
if var in df_h.columns:
|
|
65
|
+
df_resampled[var] = df_h[var].resample(t).sum()
|
|
66
|
+
|
|
67
|
+
# Apply completeness filter based on the the data frame time index
|
|
68
|
+
completeness_mask = get_completeness_mask(
|
|
69
|
+
data_frame=df_h,
|
|
70
|
+
resample_offset=t,
|
|
71
|
+
completeness_thresholds=completeness_thresholds,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
df_resampled[~completeness_mask] = np.nan
|
|
50
75
|
|
|
51
76
|
# taking the 10 min data and using it as instantaneous values:
|
|
52
77
|
is_10_minutes_timestamp = (ds_h.time.diff(dim='time') / np.timedelta64(1, 's') == 600)
|
|
@@ -54,75 +79,75 @@ def resample_dataset(ds_h, t):
|
|
|
54
79
|
cols_to_update = ['p_i', 't_i', 'rh_i', 'rh_i_wrt_ice_or_water', 'wspd_i', 'wdir_i','wspd_x_i','wspd_y_i']
|
|
55
80
|
cols_origin = ['p_u', 't_u', 'rh_u', 'rh_u_wrt_ice_or_water', 'wspd_u', 'wdir_u','wspd_x_u','wspd_y_u']
|
|
56
81
|
timestamp_10min = ds_h.time.where(is_10_minutes_timestamp, drop=True).to_index()
|
|
57
|
-
timestamp_round_hour =
|
|
82
|
+
timestamp_round_hour = df_resampled.index
|
|
58
83
|
timestamp_to_update = timestamp_round_hour.intersection(timestamp_10min)
|
|
59
|
-
|
|
84
|
+
|
|
60
85
|
for col, col_org in zip(cols_to_update, cols_origin):
|
|
61
|
-
if col not in
|
|
62
|
-
|
|
86
|
+
if col not in df_resampled.columns:
|
|
87
|
+
df_resampled[col] = np.nan
|
|
63
88
|
else:
|
|
64
89
|
# if there are already instantaneous values in the dataset
|
|
65
90
|
# we want to keep them as they are
|
|
66
91
|
# removing timestamps where there is already t_i filled from a TX file
|
|
67
92
|
missing_instantaneous = ds_h.reindex(time=timestamp_to_update)[col].isnull()
|
|
68
93
|
timestamp_to_update = timestamp_to_update[missing_instantaneous]
|
|
69
|
-
|
|
94
|
+
df_resampled.loc[timestamp_to_update, col] = ds_h.reindex(
|
|
70
95
|
time= timestamp_to_update
|
|
71
96
|
)[col_org].values
|
|
72
97
|
if col == 'p_i':
|
|
73
|
-
|
|
74
|
-
|
|
98
|
+
df_resampled.loc[timestamp_to_update, col] = df_resampled.loc[timestamp_to_update, col].values-1000
|
|
75
99
|
|
|
76
100
|
# recalculating wind direction from averaged directional wind speeds
|
|
77
101
|
for var in ['wdir_u','wdir_l']:
|
|
78
102
|
boom = var.split('_')[1]
|
|
79
|
-
if var in
|
|
80
|
-
if ('wspd_x_'+boom in
|
|
81
|
-
|
|
103
|
+
if var in df_resampled.columns:
|
|
104
|
+
if ('wspd_x_'+boom in df_resampled.columns) & ('wspd_y_'+boom in df_resampled.columns):
|
|
105
|
+
df_resampled[var] = _calcWindDir(df_resampled['wspd_x_'+boom], df_resampled['wspd_y_'+boom])
|
|
82
106
|
else:
|
|
83
107
|
logger.info(var+' in dataframe but not wspd_x_'+boom+' nor wspd_y_'+boom+', recalculating them')
|
|
84
108
|
ds_h['wspd_x_'+boom], ds_h['wspd_y_'+boom] = calculate_directional_wind_speed(ds_h['wspd_'+boom], ds_h['wdir_'+boom])
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
109
|
+
df_resampled[['wspd_x_'+boom, 'wspd_y_'+boom]] = ds_h[['wspd_x_'+boom, 'wspd_y_'+boom]].to_dataframe().resample(t).mean()
|
|
110
|
+
df_resampled[var] = _calcWindDir(df_resampled['wspd_x_'+boom], df_resampled['wspd_y_'+boom])
|
|
111
|
+
|
|
88
112
|
# recalculating relative humidity from average vapour pressure and average
|
|
89
113
|
# saturation vapor pressure
|
|
90
114
|
for var in ['rh_u','rh_l']:
|
|
91
115
|
lvl = var.split('_')[1]
|
|
92
|
-
if var in
|
|
116
|
+
if var in df_resampled.columns:
|
|
93
117
|
if ('t_'+lvl in ds_h.keys()):
|
|
94
118
|
es_wtr, es_cor = calculateSaturationVaporPressure(ds_h['t_'+lvl])
|
|
95
119
|
p_vap = ds_h[var] / 100 * es_wtr
|
|
96
|
-
|
|
97
|
-
|
|
120
|
+
|
|
121
|
+
df_resampled[var] = (p_vap.to_series().resample(t).mean() \
|
|
98
122
|
/ es_wtr.to_series().resample(t).mean())*100
|
|
99
|
-
if var+'_wrt_ice_or_water' in
|
|
100
|
-
|
|
123
|
+
if var+'_wrt_ice_or_water' in df_resampled.keys():
|
|
124
|
+
df_resampled[var+'_wrt_ice_or_water'] = (p_vap.to_series().resample(t).mean() \
|
|
101
125
|
/ es_cor.to_series().resample(t).mean())*100
|
|
102
|
-
|
|
126
|
+
|
|
103
127
|
# passing each variable attribute to the ressample dataset
|
|
104
128
|
vals = []
|
|
105
|
-
for c in
|
|
129
|
+
for c in df_resampled.columns:
|
|
106
130
|
if c in ds_h.data_vars:
|
|
107
131
|
vals.append(xr.DataArray(
|
|
108
|
-
data=
|
|
109
|
-
coords={'time':
|
|
132
|
+
data=df_resampled[c], dims=['time'],
|
|
133
|
+
coords={'time':df_resampled.index}, attrs=ds_h[c].attrs))
|
|
110
134
|
else:
|
|
111
135
|
vals.append(xr.DataArray(
|
|
112
|
-
data=
|
|
113
|
-
coords={'time':
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
136
|
+
data=df_resampled[c], dims=['time'],
|
|
137
|
+
coords={'time':df_resampled.index}, attrs=None))
|
|
138
|
+
|
|
139
|
+
ds_resampled = xr.Dataset(dict(zip(df_resampled.columns,vals)), attrs=ds_h.attrs)
|
|
140
|
+
|
|
141
|
+
return ds_resampled
|
|
117
142
|
|
|
118
143
|
|
|
119
144
|
def calculateSaturationVaporPressure(t, T_0=273.15, T_100=373.15, es_0=6.1071,
|
|
120
|
-
es_100=1013.246, eps=0.622):
|
|
145
|
+
es_100=1013.246, eps=0.622):
|
|
121
146
|
'''Calculate specific humidity
|
|
122
|
-
|
|
147
|
+
|
|
123
148
|
Parameters
|
|
124
149
|
----------
|
|
125
|
-
T_0 : float
|
|
150
|
+
T_0 : float
|
|
126
151
|
Steam point temperature. Default is 273.15.
|
|
127
152
|
T_100 : float
|
|
128
153
|
Steam point temperature in Kelvin
|
|
@@ -132,14 +157,13 @@ def calculateSaturationVaporPressure(t, T_0=273.15, T_100=373.15, es_0=6.1071,
|
|
|
132
157
|
Saturation vapour pressure at the melting point (hPa)
|
|
133
158
|
es_100 : float
|
|
134
159
|
Saturation vapour pressure at steam point temperature (hPa)
|
|
135
|
-
|
|
136
160
|
Returns
|
|
137
161
|
-------
|
|
138
162
|
xarray.DataArray
|
|
139
163
|
Saturation vapour pressure with regard to water above 0 C (hPa)
|
|
140
164
|
xarray.DataArray
|
|
141
165
|
Saturation vapour pressure where subfreezing timestamps are with regards to ice (hPa)
|
|
142
|
-
'''
|
|
166
|
+
'''
|
|
143
167
|
# Saturation vapour pressure above 0 C (hPa)
|
|
144
168
|
es_wtr = 10**(-7.90298 * (T_100 / (t + T_0) - 1) + 5.02808 * np.log10(T_100 / (t + T_0))
|
|
145
169
|
- 1.3816E-7 * (10**(11.344 * (1 - (t + T_0) / T_100)) - 1)
|
|
@@ -149,11 +173,11 @@ def calculateSaturationVaporPressure(t, T_0=273.15, T_100=373.15, es_0=6.1071,
|
|
|
149
173
|
es_ice = 10**(-9.09718 * (T_0 / (t + T_0) - 1) - 3.56654
|
|
150
174
|
* np.log10(T_0 / (t + T_0)) + 0.876793
|
|
151
175
|
* (1 - (t + T_0) / T_0)
|
|
152
|
-
+ np.log10(es_0))
|
|
153
|
-
|
|
176
|
+
+ np.log10(es_0))
|
|
177
|
+
|
|
154
178
|
# Saturation vapour pressure (hPa)
|
|
155
179
|
es_cor = xr.where(t < 0, es_ice, es_wtr)
|
|
156
|
-
|
|
180
|
+
|
|
157
181
|
return es_wtr, es_cor
|
|
158
182
|
|
|
159
183
|
def _calcWindDir(wspd_x, wspd_y):
|
|
@@ -44,25 +44,3 @@ def addBasicMeta(ds, vars_df):
|
|
|
44
44
|
ds[v].attrs[c] = vars_df[c][v]
|
|
45
45
|
return ds
|
|
46
46
|
|
|
47
|
-
def populateMeta(ds, conf, skip):
|
|
48
|
-
'''Populate L0 Dataset with metadata dictionary
|
|
49
|
-
|
|
50
|
-
Parameters
|
|
51
|
-
----------
|
|
52
|
-
ds : xarray.Dataset
|
|
53
|
-
L0 dataset
|
|
54
|
-
conf : dict
|
|
55
|
-
Metadata dictionary
|
|
56
|
-
skip : list
|
|
57
|
-
List of column names to skip parsing to metadata
|
|
58
|
-
|
|
59
|
-
Returns
|
|
60
|
-
-------
|
|
61
|
-
ds : xarray.Dataset
|
|
62
|
-
L0 dataset with metadata populated as Dataset attributes
|
|
63
|
-
'''
|
|
64
|
-
# skip = ["columns", "skiprows"]
|
|
65
|
-
for k in conf.keys():
|
|
66
|
-
if k not in skip: ds.attrs[k] = conf[k]
|
|
67
|
-
return ds
|
|
68
|
-
|
|
@@ -3,13 +3,13 @@ acknowledgements,The Programme for Monitoring of the Greenland Ice Sheet (PROMIC
|
|
|
3
3
|
alt.axis,Z
|
|
4
4
|
alt.coverage_content_type,coordinate
|
|
5
5
|
gps_alt.positive,up
|
|
6
|
-
cdm_data_type,
|
|
6
|
+
cdm_data_type,Station
|
|
7
7
|
comment,https://doi.org/10.22008/promice/data/aws
|
|
8
8
|
contributor_name,
|
|
9
9
|
contributor_role,
|
|
10
10
|
conventions,ACDD-1.3; CF-1.7
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
creator_email,pho@geus.dk
|
|
12
|
+
creator_url,https://promice.dk
|
|
13
13
|
creator_institution,Geological Survey of Denmark and Greenland (GEUS)
|
|
14
14
|
creator_name,Penelope How
|
|
15
15
|
creator_type,person
|
|
@@ -22,7 +22,7 @@ geospatial_lon_extents_match,gps_lon
|
|
|
22
22
|
geospatial_lon_resolution,
|
|
23
23
|
geospatial_lon_units,degrees_east
|
|
24
24
|
geospatial_vertical_resolution,
|
|
25
|
-
geospatial_vertical_units,
|
|
25
|
+
geospatial_vertical_units,meters
|
|
26
26
|
institution,Geological Survey of Denmark and Greenland (GEUS)
|
|
27
27
|
instrument,See https://doi.org/10.5194/essd-13-3819-2021
|
|
28
28
|
instrument_vocabulary,GCMD:GCMD Keywords
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
field,standard_name,long_name,units,coverage_content_type,coordinates,instantaneous_hourly,where_to_find,lo,hi,
|
|
1
|
+
field,standard_name,long_name,units,coverage_content_type,coordinates,instantaneous_hourly,where_to_find,lo,hi,dependent_variables,station_type,L0,L2,L3,max_decimals
|
|
2
2
|
time,time,Time,yyyy-mm-dd HH:MM:SS,physicalMeasurement,time,,,,,,all,1,1,1,
|
|
3
3
|
rec,record,Record,-,referenceInformation,time,,L0 or L2,,,,all,1,1,0,0
|
|
4
|
-
p_u,air_pressure,Air pressure (upper boom),hPa,physicalMeasurement,time,FALSE,,650,1100,
|
|
5
|
-
p_l,air_pressure,Air pressure (lower boom),hPa,physicalMeasurement,time,FALSE,,650,1100,
|
|
6
|
-
t_u,air_temperature,Air temperature (upper boom),degrees_C,physicalMeasurement,time,FALSE,,-80,40,
|
|
7
|
-
t_l,air_temperature,Air temperature (lower boom),degrees_C,physicalMeasurement,time,FALSE,,-80,40,
|
|
8
|
-
rh_u,relative_humidity,Relative humidity (upper boom),%,physicalMeasurement,time,FALSE,,0,100,
|
|
4
|
+
p_u,air_pressure,Air pressure (upper boom),hPa,physicalMeasurement,time,FALSE,,650,1100,z_pt_cor dlhf_u dshf_u,all,1,1,1,4
|
|
5
|
+
p_l,air_pressure,Air pressure (lower boom),hPa,physicalMeasurement,time,FALSE,,650,1100,dlhf_l dshf_l,two-boom,1,1,1,4
|
|
6
|
+
t_u,air_temperature,Air temperature (upper boom),degrees_C,physicalMeasurement,time,FALSE,,-80,40,z_boom_cor_l z_boom_cor_u z_stake_cor qh_u rh_u_wrt_ice_or_water dlhf_u dshf_u,all,1,1,1,4
|
|
7
|
+
t_l,air_temperature,Air temperature (lower boom),degrees_C,physicalMeasurement,time,FALSE,,-80,40,dlhf_l dshf_l,two-boom,1,1,1,4
|
|
8
|
+
rh_u,relative_humidity,Relative humidity (upper boom),%,physicalMeasurement,time,FALSE,,0,100,qh_u rh_u_wrt_ice_or_water dlhf_u dshf_u,all,1,1,1,4
|
|
9
9
|
rh_u_wrt_ice_or_water,relative_humidity_with_respect_to_ice_or_water,Relative humidity (upper boom) with respect to saturation over ice in subfreezing conditions and over water otherwise,%,modelResult,time,FALSE,L2 or later,0,150,"",all,0,1,1,4
|
|
10
10
|
qh_u,specific_humidity,Specific humidity (upper boom),kg/kg,modelResult,time,FALSE,L2 or later,0,100,"",all,0,1,1,4
|
|
11
|
-
rh_l,relative_humidity,Relative humidity (lower boom),%,physicalMeasurement,time,FALSE,,0,100,
|
|
11
|
+
rh_l,relative_humidity,Relative humidity (lower boom),%,physicalMeasurement,time,FALSE,,0,100,qh_l rh_l_wrt_ice_or_water dlhf_l dshf_l,two-boom,1,1,1,4
|
|
12
12
|
rh_l_wrt_ice_or_water,relative_humidity_with_respect_to_ice_or_water,Relative humidity (lower boom) with respect to saturation over ice in subfreezing conditions and over water otherwise,%,modelResult,time,FALSE,L2 or later,0,150,"",two-boom,0,1,1,4
|
|
13
13
|
qh_l,specific_humidity,Specific humidity (lower boom),kg/kg,modelResult,time,FALSE,L2 or later,0,100,,two-boom,0,1,1,4
|
|
14
|
-
wspd_u,wind_speed,Wind speed (upper boom),m s-1,physicalMeasurement,time,FALSE,,0,100,wdir_u wspd_x_u wspd_y_u,all,1,1,1,4
|
|
15
|
-
wspd_l,wind_speed,Wind speed (lower boom),m s-1,physicalMeasurement,time,FALSE,,0,100,wdir_l wspd_x_l wspd_y_l,two-boom,1,1,1,4
|
|
14
|
+
wspd_u,wind_speed,Wind speed (upper boom),m s-1,physicalMeasurement,time,FALSE,,0,100,wdir_u wspd_x_u wspd_y_u rainfall_cor_u,all,1,1,1,4
|
|
15
|
+
wspd_l,wind_speed,Wind speed (lower boom),m s-1,physicalMeasurement,time,FALSE,,0,100,wdir_l wspd_x_l wspd_y_l rainfall_cor_l,two-boom,1,1,1,4
|
|
16
16
|
wdir_u,wind_from_direction,Wind from direction (upper boom),degrees,physicalMeasurement,time,FALSE,,1,360,wspd_x_u wspd_y_u,all,1,1,1,4
|
|
17
17
|
wdir_std_u,wind_from_direction_standard_deviation,Wind from direction (standard deviation),degrees,qualityInformation,time,FALSE,L0 or L2,,,,one-boom,1,1,0,4
|
|
18
18
|
wdir_l,wind_from_direction,Wind from direction (lower boom),degrees,physicalMeasurement,time,FALSE,,1,360,wspd_x_l wspd_y_l,two-boom,1,1,1,4
|
|
@@ -33,23 +33,26 @@ dlhf_u,surface_downward_latent_heat_flux,Latent heat flux (upper boom),W m-2,mod
|
|
|
33
33
|
dlhf_l,surface_downward_latent_heat_flux,Latent heat flux (lower boom),W m-2,modelResult,time,FALSE,L3 or later,,,,two-boom,0,0,1,4
|
|
34
34
|
dshf_u,surface_downward_sensible_heat_flux,Sensible heat flux (upper boom),W m-2,modelResult,time,FALSE,L3 or later,,,,all,0,0,1,4
|
|
35
35
|
dshf_l,surface_downward_sensible_heat_flux,Sensible heat flux (lower boom),W m-2,modelResult,time,FALSE,L3 or later,,,,two-boom,0,0,1,4
|
|
36
|
-
z_boom_u,distance_to_surface_from_boom,Upper boom height,m,physicalMeasurement,time,TRUE,,0.3,10,
|
|
36
|
+
z_boom_u,distance_to_surface_from_boom,Upper boom height,m,physicalMeasurement,time,TRUE,,0.3,10,z_boom_cor_u,all,1,1,1,4
|
|
37
|
+
z_boom_cor_u,distance_to_surface_from_boom_corrected,Upper boom height - corrected,m,modelResult,time,TRUE,,0.3,10,z_boom_u,all,1,1,1,4
|
|
37
38
|
z_boom_q_u,distance_to_surface_from_boom_quality,Upper boom height (quality),-,qualityInformation,time,TRUE,L0 or L2,,,,all,1,1,0,4
|
|
38
|
-
z_boom_l,distance_to_surface_from_boom,Lower boom height,m,physicalMeasurement,time,TRUE,,0.3,5,
|
|
39
|
+
z_boom_l,distance_to_surface_from_boom,Lower boom height,m,physicalMeasurement,time,TRUE,,0.3,5,z_boom_cor_l,two-boom,1,1,1,4
|
|
40
|
+
z_boom_cor_l,distance_to_surface_from_boom_corrected,Lower boom height - corrected,m,modelResult,time,TRUE,,0.3,5,z_boom_l,two-boom,1,1,1,4
|
|
39
41
|
z_boom_q_l,distance_to_surface_from_boom_quality,Lower boom height (quality),-,qualityInformation,time,TRUE,L0 or L2,,,,two-boom,1,1,0,4
|
|
40
|
-
z_stake,distance_to_surface_from_stake_assembly,Stake height,m,physicalMeasurement,time,TRUE,,0.3,8
|
|
42
|
+
z_stake,distance_to_surface_from_stake_assembly,Stake height,m,physicalMeasurement,time,TRUE,,0.3,8,z_stake_cor,one-boom,1,1,1,4
|
|
43
|
+
z_stake_cor,distance_to_surface_from_stake_assembly_corrected,Stake height - corrected,m,physicalMeasurement,time,TRUE,,0.3,8,z_stake,one-boom,1,1,1,4
|
|
41
44
|
z_stake_q,distance_to_surface_from_stake_assembly_quality,Stake height (quality),-,qualityInformation,time,TRUE,L0 or L2,,,,one-boom,1,1,0,4
|
|
42
|
-
z_pt,depth_of_pressure_transducer_in_ice,Depth of pressure transducer in ice,m,physicalMeasurement,time,FALSE,,0,30,
|
|
45
|
+
z_pt,depth_of_pressure_transducer_in_ice,Depth of pressure transducer in ice,m,physicalMeasurement,time,FALSE,,0,30,z_pt_cor,one-boom,1,1,1,4
|
|
43
46
|
z_pt_cor,depth_of_pressure_transducer_in_ice_corrected,Depth of pressure transducer in ice - corrected,m,modelResult,time,FALSE,L2 or later,0,30,,one-boom,0,1,1,4
|
|
44
47
|
z_surf_combined,height_of_surface_combined,"Surface height combined from multiple sensors, relative to ice surface height at installation",m,modelResult,time,FALSE,L3,,,,all,0,0,1,4
|
|
45
48
|
z_ice_surf,height_of_ice_surface,"Ice surface height, relative to ice surface height at installation and calculated from pt_cor and z_stake",m,modelResult,time,FALSE,L3,,,,one-boom,0,0,1,4
|
|
46
49
|
snow_height,height_of_snow,"Snow surface height, relative to ice surface",m,modelResult,time,FALSE,L3,0,,,one-boom,0,0,1,4
|
|
47
|
-
precip_u,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
precip_l,
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
precip_u,precipitation_raw_upper,Semi-accumulated uncorrected liquid precipitation (upper boom),mm,physicalMeasurement,time,TRUE,L0 or L2,0,,rainfall_u,all,1,1,0,4
|
|
51
|
+
rainfall_u,rainfall_per_timestep_uncorrected_upper,Rainfall within timestep uncorrected for undercatch (upper boom),mm,modelResult,time,FALSE,L2 or later,0,,rainfall_cor_u,all,0,1,1,4
|
|
52
|
+
rainfall_cor_u,rainfall_per_timestep_corrected_upper,Rainfall within timestep corrected for undercatch (upper boom),mm,modelResult,time,FALSE,L2 or later,0,,,all,0,1,1,4
|
|
53
|
+
precip_l,precipitation_raw_lower,Semi-accumulated uncorrected liquid precipitation (lower boom),mm,physicalMeasurement,time,TRUE,L0 or L2,0,,rainfall_l,two-boom,1,1,0,4
|
|
54
|
+
rainfall_l,rainfall_per_timestep_uncorrected_lower,Rainfall within timestep uncorrected for undercatch (lower boom),mm,modelResult,time,FALSE,L2 or later,0,,rainfall_cor_l,all,0,1,1,4
|
|
55
|
+
rainfall_cor_l,rainfall_per_timestep_corrected_lower,Rainfall within timestep corrected for undercatch (lower boom),mm,modelResult,time,FALSE,L2 or later,0,,,all,0,1,1,4
|
|
53
56
|
t_i_1,ice_temperature_at_t1,Ice temperature at sensor 1,degrees_C,physicalMeasurement,time,FALSE,,-80,1,,all,1,1,1,4
|
|
54
57
|
t_i_2,ice_temperature_at_t2,Ice temperature at sensor 2,degrees_C,physicalMeasurement,time,FALSE,,-80,1,,all,1,1,1,4
|
|
55
58
|
t_i_3,ice_temperature_at_t3,Ice temperature at sensor 3,degrees_C,physicalMeasurement,time,FALSE,,-80,1,,all,1,1,1,4
|
|
@@ -73,8 +76,8 @@ d_t_i_9,depth_of_thermistor_9,Depth of thermistor 9,m,modelResult,time,FALSE,L3,
|
|
|
73
76
|
d_t_i_10,depth_of_thermistor_10,Depth of thermistor 10,m,modelResult,time,FALSE,L3,-10,100,,two-boom,0,0,1,4
|
|
74
77
|
d_t_i_11,depth_of_thermistor_11,Depth of thermistor 11,m,modelResult,time,FALSE,L3,-10,100,,two-boom,0,0,1,4
|
|
75
78
|
t_i_10m,10m_subsurface_temperature,10 m subsurface temperature,degrees_C,modelResult,time,FALSE,L3,-70,0,,all,0,0,1,4
|
|
76
|
-
tilt_x,platform_view_angle_x,Tilt to east,degrees,physicalMeasurement,time,FALSE,,-30,30,
|
|
77
|
-
tilt_y,platform_view_angle_y,Tilt to north,degrees,physicalMeasurement,time,FALSE,,-30,30,
|
|
79
|
+
tilt_x,platform_view_angle_x,Tilt to east,degrees,physicalMeasurement,time,FALSE,,-30,30,dsr_cor usr_cor,all,1,1,1,4
|
|
80
|
+
tilt_y,platform_view_angle_y,Tilt to north,degrees,physicalMeasurement,time,FALSE,,-30,30,dsr_cor usr_cor,all,1,1,1,4
|
|
78
81
|
rot,platform_azimuth_angle,Station rotation from true North,degrees,physicalMeasurement,time,FALSE,,0,360,,all,1,1,1,2
|
|
79
82
|
gps_lat,gps_latitude,Latitude,degrees_north,physicalMeasurement,time,TRUE,,50,83,,all,1,1,1,6
|
|
80
83
|
gps_lon,gps_longitude,Longitude,degrees_east,physicalMeasurement,time,TRUE,,5,70,,all,1,1,1,6
|
|
@@ -95,10 +98,10 @@ fan_dc_u,fan_current,Fan current (upper boom),mA,physicalMeasurement,time,TRUE,L
|
|
|
95
98
|
fan_dc_l,fan_current,Fan current (lower boom),mA,physicalMeasurement,time,TRUE,,0,200,,two-boom,1,1,0,2
|
|
96
99
|
freq_vw,frequency_of_precipitation_wire_vibration,Frequency of vibrating wire in precipitation gauge,Hz,physicalMeasurement,time,TRUE,L0 or L2,0,10000,"",,1,1,0,
|
|
97
100
|
t_log,temperature_of_logger,Logger temperature,degrees_C,physicalMeasurement,time,TRUE,,-80,40,,one-boom,1,1,0,4
|
|
98
|
-
t_rad,temperature_of_radiation_sensor,Radiation sensor temperature,degrees_C,physicalMeasurement,time,FALSE,,-80,40,
|
|
101
|
+
t_rad,temperature_of_radiation_sensor,Radiation sensor temperature,degrees_C,physicalMeasurement,time,FALSE,,-80,40,dlr ulr,all,1,1,1,4
|
|
99
102
|
p_i,air_pressure,Air pressure (instantaneous) minus 1000,hPa,physicalMeasurement,time,TRUE,,-350,100,,all,1,1,1,4
|
|
100
|
-
t_i,air_temperature,Air temperature (instantaneous),degrees_C,physicalMeasurement,time,TRUE,,-80,40
|
|
101
|
-
rh_i,relative_humidity,Relative humidity (instantaneous),%,physicalMeasurement,time,TRUE,,0,150,
|
|
103
|
+
t_i,air_temperature,Air temperature (instantaneous),degrees_C,physicalMeasurement,time,TRUE,,-80,40,rh_i_wrt_ice_or_water,all,1,1,1,4
|
|
104
|
+
rh_i,relative_humidity,Relative humidity (instantaneous),%,physicalMeasurement,time,TRUE,,0,150,rh_i_wrt_ice_or_water,all,1,1,1,4
|
|
102
105
|
rh_i_wrt_ice_or_water,relative_humidity_with_respect_to_ice_or_water,Relative humidity (instantaneous) with respect to saturation over ice in subfreezing conditions and over water otherwise,%,modelResult,time,TRUE,L2 or later,0,100,,all,0,1,1,4
|
|
103
106
|
wspd_i,wind_speed,Wind speed (instantaneous),m s-1,physicalMeasurement,time,TRUE,,0,100,wdir_i wspd_x_i wspd_y_i,all,1,1,1,4
|
|
104
107
|
wdir_i,wind_from_direction,Wind from direction (instantaneous),degrees,physicalMeasurement,time,TRUE,,1,360,wspd_x_i wspd_y_i,all,1,1,1,4
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pypromice
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.0
|
|
4
4
|
Summary: PROMICE/GC-Net data processing toolbox
|
|
5
5
|
Home-page: https://github.com/GEUS-Glaciology-and-Climate/pypromice
|
|
6
6
|
Author: GEUS Glaciology and Climate
|
|
@@ -25,7 +25,6 @@ Requires-Dist: toml
|
|
|
25
25
|
Requires-Dist: scipy>=1.9.0
|
|
26
26
|
Requires-Dist: Bottleneck
|
|
27
27
|
Requires-Dist: netcdf4
|
|
28
|
-
Requires-Dist: pyDataverse==0.3.1
|
|
29
28
|
Requires-Dist: eccodes
|
|
30
29
|
Requires-Dist: scikit-learn>=1.1.0
|
|
31
30
|
Dynamic: author
|