Skip to content

Commit d33db1a

Browse files
authored
Merge pull request #2 from rcjackson/inital_repo
ADD: First working package, no unit tests yet.
2 parents 8d81eb1 + 8a6d0eb commit d33db1a

8 files changed

Lines changed: 202 additions & 210 deletions

File tree

radclss/config/default_config.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"""
77
# Define variables to drop from RadCLss from the respective datastreams
88
DEFAULT_DISCARD_VAR = {'radar' : ['classification_mask',
9-
'censor_mask',
109
'uncorrected_copol_correlation_coeff',
1110
'uncorrected_differential_phase',
1211
'uncorrected_differential_reflectivity',

radclss/core/radclss_core.py

Lines changed: 95 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
import xarray as xr
44
import act
55
import xradar as xd
6+
import numpy as np
67

78
from ..util.column_utils import subset_points, match_datasets_act
8-
from ..util.dod import adjust_radclss_dod
99
from ..config.default_config import DEFAULT_DISCARD_VAR
1010
from ..config.output_config import get_output_config
1111
from dask.distributed import Client, as_completed
@@ -68,7 +68,7 @@ def radclss(volumes, input_site_dict, serial=True, dod_version='', discard_var={
6868
current_client = Client.current()
6969
if current_client is None:
7070
raise RuntimeError("No Dask client found. Please start a Dask client before running in parallel mode.")
71-
results = current_client.map(subset_points, volumes["radar"], input_site_dict=input_site_dict)
71+
results = current_client.map(subset_points, volumes["radar"], sonde=volumes["sonde"], input_site_dict=input_site_dict)
7272
for done_work in as_completed(results, with_results=False):
7373
try:
7474
columns.append(done_work.result())
@@ -78,45 +78,78 @@ def radclss(volumes, input_site_dict, serial=True, dod_version='', discard_var={
7878
for rad in volumes['radar']:
7979
if verbose:
8080
print(f"Processing file: {rad}")
81-
columns.append(subset_points(rad, input_site_dict=input_site_dict))
81+
columns.append(subset_points(rad, sonde=volumes["sonde"], input_site_dict=input_site_dict))
8282
if verbose:
8383
print("Processed file: ", rad)
8484
print("Current number of successful columns: ", len(columns))
8585
print("Last processed file results: ")
8686
print(columns[-1])
8787

8888
# Assemble individual columns into single DataSet
89-
try:
90-
# Concatenate all extracted columns across time dimension to form daily timeseries
91-
output_config = get_output_config()
92-
output_platform = output_config['platform']
93-
output_level = output_config['level']
94-
ds_concat = xr.concat([data for data in columns if data], dim="time")
95-
ds = act.io.create_ds_from_arm_dod(f'{output_platform}-{output_level}',
96-
{'time': ds_concat.sizes['time'],
97-
'height': ds_concat.sizes['height'],
98-
'station': ds_concat.sizes['station']},
99-
version=dod_version)
100-
101-
102-
ds['time'] = ds_concat.sel(station=base_station).base_time
103-
ds['time_offset'] = ds_concat.sel(station=base_station).base_time
104-
ds['base_time'] = ds_concat.sel(station=base_station).isel(time=0).base_time
105-
ds['lat'] = ds_concat.isel(time=0).lat
106-
ds['lon'] = ds_concat.isel(time=0).lon
107-
ds['alt'] = ds_concat.isel(time=0).alt
108-
for var in ds_concat.data_vars:
109-
if var not in ['time', 'time_offset', 'base_time', 'lat', 'lon', 'alt']:
89+
#try:
90+
# Concatenate all extracted columns across time dimension to form daily timeseries
91+
output_config = get_output_config()
92+
output_platform = output_config['platform']
93+
output_level = output_config['level']
94+
ds_concat = xr.concat([data for data in columns if data], dim="time")
95+
if verbose:
96+
print("Grabbing DOD for platform/level: ", f'{output_platform}.{output_level}')
97+
ds = act.io.create_ds_from_arm_dod(f'{output_platform}.{output_level}',
98+
{'time': ds_concat.sizes['time'],
99+
'height': ds_concat.sizes['height'],
100+
'station': ds_concat.sizes['station']},
101+
version=dod_version)
102+
103+
104+
ds['time'] = ds_concat.sel(station=base_station).base_time
105+
ds['time_offset'] = ds_concat.sel(station=base_station).base_time
106+
ds['base_time'] = ds_concat.sel(station=base_station).isel(time=0).base_time
107+
ds['station'] = ds_concat['station']
108+
ds['height'] = ds_concat['height']
109+
ds['lat'][:] = ds_concat.isel(time=0)["lat"][:]
110+
ds['lon'][:] = ds_concat.isel(time=0)["lon"][:]
111+
ds['alt'][:] = ds_concat.isel(time=0)["alt"][:]
112+
113+
for var in ds_concat.data_vars:
114+
if var not in ['time', 'time_offset', 'base_time', 'lat', 'lon', 'alt']:
115+
if var in ds.data_vars:
116+
if verbose:
117+
print(f"Adding variable to output dataset: {var}")
118+
print(f"Original dtype: {ds[var].dtype}, New dtype: {ds_concat[var].dtype}")
119+
old_type = ds[var].dtype
120+
121+
# Assign data and convert to original dtype
110122
ds[var][:] = ds_concat[var][:]
123+
ds[var] = ds[var].astype(old_type)
124+
if "_FillValue" in ds[var].attrs:
125+
if isinstance(ds[var].attrs["_FillValue"], str):
126+
if ds[var].dtype == 'float32':
127+
ds[var].attrs["_FillValue"] = np.float32(ds[var].attrs["_FillValue"])
128+
elif ds[var].dtype == 'float64':
129+
ds[var].attrs["_FillValue"] = np.float64(ds[var].attrs["_FillValue"])
130+
elif ds[var].dtype == 'int32':
131+
ds[var].attrs["_FillValue"] = np.int32(ds[var].attrs["_FillValue"])
132+
elif ds[var].dtype == 'int64':
133+
ds[var].attrs["_FillValue"] = np.int64(ds[var].attrs["_FillValue"])
134+
ds[var] = ds[var].fillna(ds[var].attrs["_FillValue"]).astype(float)
135+
if "missing_value" in ds[var].attrs:
136+
if isinstance(ds[var].attrs["missing_value"], str):
137+
if ds[var].dtype == 'float32':
138+
ds[var].attrs["missing_value"] = np.float32(ds[var].attrs["missing_value"])
139+
elif ds[var].dtype == 'float64':
140+
ds[var].attrs["missing_value"] = np.float64(ds[var].attrs["missing_value"])
141+
elif ds[var].dtype == 'int32':
142+
ds[var].attrs["missing_value"] = np.int32(ds[var].attrs["missing_value"])
143+
elif ds[var].dtype == 'int64':
144+
ds[var].attrs["missing_value"] = np.int64(ds[var].attrs["missing_value"])
145+
ds[var] = ds[var].fillna(ds[var].attrs["missing_value"]).astype(float)
111146

112-
# Remove all the unused CMAC variables
113-
ds = ds.drop_vars(discard_var["radar"])
114-
# Drop duplicate latitude and longitude
115-
ds = ds.drop_vars(['latitude', 'longitude'])
116-
del ds_concat
117-
except ValueError as e:
118-
print(f"Error concatenating columns: {e}")
119-
ds = None
147+
# Remove all the unused CMAC variables
148+
# Drop duplicate latitude and longitude
149+
del ds_concat
150+
#except ValueError as e:
151+
# print(f"Error concatenating columns: {e}")
152+
# ds = None
120153

121154
# Free up Memory
122155
del columns
@@ -130,14 +163,24 @@ def radclss(volumes, input_site_dict, serial=True, dod_version='', discard_var={
130163
# Find all of the met stations and match to columns
131164
vol_keys = list(volumes.keys())
132165
for k in vol_keys:
133-
instrument, site = k.split("_", 1)
134-
166+
if len(volumes[k]) == 0:
167+
if verbose:
168+
print(f"No files found for instrument/site: {k}")
169+
continue
170+
if "_" in k:
171+
instrument, site = k.split("_", 1)
172+
else:
173+
instrument = k
174+
site = base_station
135175
if instrument == "met":
176+
if verbose:
177+
print(f"Matching MET data for site: {site}")
136178
ds = match_datasets_act(ds,
137179
volumes[k][0],
138180
site.upper(),
139181
resample="mean",
140-
discard=discard_var['met'])
182+
discard=discard_var['met'],
183+
verbose=verbose)
141184

142185
# Radiosonde
143186
if instrument == "sonde":
@@ -156,45 +199,51 @@ def radclss(volumes, input_site_dict, serial=True, dod_version='', discard_var={
156199
site.upper(),
157200
discard=discard_var[instrument],
158201
DataSet=True,
159-
resample="mean")
202+
resample="mean",
203+
verbose=verbose)
160204
# clean up
161205
del grd_ds
162206

163207
if instrument == "pluvio":
164208
# Weighing Bucket Rain Gauge
165209
ds = match_datasets_act(ds,
166-
volumes[k][0],
210+
volumes[k],
167211
site.upper(),
168-
discard=discard_var["pluvio"])
212+
discard=discard_var["pluvio"],
213+
verbose=verbose)
169214

170215
if instrument == "ld":
171216
ds = match_datasets_act(ds,
172-
volumes[k][0],
217+
volumes[k],
173218
site.upper(),
174219
discard=discard_var['ldquants'],
175220
resample="mean",
176-
prefix="ldquants_")
177-
221+
prefix="ldquants_",
222+
verbose=verbose)
178223

179224
if instrument == "vd":
180225
# Laser Disdrometer - Supplemental Site
181226
ds = match_datasets_act(ds,
182-
volumes[k][0],
227+
volumes[k],
183228
site.upper(),
184229
discard=discard_var['vdisquants'],
185230
resample="mean",
186-
prefix="vdisquants_")
231+
prefix="vdisquants_",
232+
verbose=verbose)
187233

188234
if instrument == "wxt":
189235
# Laser Disdrometer - Supplemental Site
190236
ds = match_datasets_act(ds,
191-
volumes[k][0],
237+
volumes[k],
192238
site.upper(),
193239
discard=discard_var['wxt'],
194-
resample="mean")
240+
resample="mean",
241+
verbose=verbose)
195242

196243
else:
197244
# There is no column extraction
198245
raise RuntimeError(": RadCLss FAILURE (All Columns Failed to Extract): ")
199-
246+
del ds["base_time"].attrs["units"]
247+
del ds["time_offset"].attrs["units"]
248+
del ds["time"].attrs["units"]
200249
return ds

radclss/io/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .write import write_radclss_output

radclss/io/write.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import json
2+
import urllib.request
3+
import warnings
4+
5+
def write_radclss_output(ds, output_filename, process, version=None):
6+
"""Write the RadCLSS output dataset to a NetCDF file.
7+
8+
Parameters
9+
----------
10+
ds : xarray.Dataset
11+
The RadCLSS output dataset.
12+
output_filename : str
13+
The path to the output NetCDF file.
14+
process : str
15+
The process name (i.e. radclss)
16+
version : str
17+
The version of the process used. Set to None to use the latest version.
18+
"""
19+
# Write the dataset to a NetCDF file
20+
base_url = 'https://pcm.arm.gov/pcm/api/dods/'
21+
22+
# Get data from DOD api
23+
with urllib.request.urlopen(base_url + process) as url:
24+
data = json.loads(url.read().decode())
25+
keys = list(data['versions'].keys())
26+
#if version not in keys:
27+
# warnings.warn(
28+
# ' '.join(
29+
# ['Version:', version, 'not available or not specified. Using Version:', keys[-1]]
30+
# ),
31+
# UserWarning,
32+
# )
33+
version = keys[-1]
34+
variables = data['versions'][version]['vars']
35+
encoding = {}
36+
for v in variables:
37+
type_str = v['type']
38+
if v['name'] in ds.variables:
39+
if type_str == 'float':
40+
encoding[v['name']] = {'dtype': 'float32'}
41+
elif type_str == 'double':
42+
encoding[v['name']] = {'dtype': 'float64'}
43+
elif type_str == 'short':
44+
encoding[v['name']] = {'dtype': 'int16'}
45+
elif type_str == 'int':
46+
encoding[v['name']] = {'dtype': 'int32'}
47+
elif type_str == "char":
48+
encoding[v['name']] = {'dtype': 'S1'}
49+
elif type_str == "byte":
50+
encoding[v['name']] = {'dtype': 'int8'}
51+
52+
ds.to_netcdf(output_filename, format='NETCDF4_CLASSIC', encoding=encoding)

radclss/util/__init__py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
from .column_utils import subset_points, match_datasets_act
2-
from .dod import adjust_radclss_dod
1+
from .column_utils import subset_points, match_datasets_act

radclss/util/column_utils.py

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
import act
33
import numpy as np
44
import xarray as xr
5-
import xradar as xd
65
import datetime
76

87
from ..config import DEFAULT_DISCARD_VAR
@@ -51,15 +50,16 @@ def subset_points(nfile, input_site_dict, sonde=None, height_bins=np.arange(500,
5150
#try:
5251
# Read in the file
5352

54-
xradar_ds = xd.io.open_cfradial1_datatree(nfile)
55-
for var in DEFAULT_DISCARD_VAR['radar']:
56-
if var in xradar_ds.data_vars:
57-
xradar_ds = xradar_ds.drop_vars(var)
58-
if xradar_ds["/sweep_0"]["sweep_mode"] == "rhi":
59-
xradar_ds.close()
60-
return None
61-
radar = xradar_ds.pyart.to_radar()
62-
xradar_ds.close()
53+
#xradar_ds = xd.io.open_cfradial1_datatree(nfile)
54+
#for var in DEFAULT_DISCARD_VAR['radar']:
55+
# if var in xradar_ds.data_vars:
56+
# xradar_ds = xradar_ds.drop_vars(var)
57+
#if xradar_ds["/sweep_0"]["sweep_mode"] == "rhi":
58+
# xradar_ds.close()
59+
# return None
60+
#radar = xradar_ds.pyart.to_radar()
61+
#xradar_ds.close()
62+
radar = pyart.io.read(nfile, exclude_fields=DEFAULT_DISCARD_VAR['radar'])
6363
# Check for single sweep scans
6464
if np.ma.is_masked(radar.sweep_start_ray_index["data"][1:]):
6565
radar.sweep_start_ray_index["data"] = np.ma.array([0])
@@ -160,7 +160,8 @@ def match_datasets_act(column,
160160
discard,
161161
resample='sum',
162162
DataSet=False,
163-
prefix=None):
163+
prefix=None,
164+
verbose=False):
164165
"""
165166
Time synchronization of a Ground Instrumentation Dataset to
166167
a Radar Column for Specific Locations using the ARM ACT package
@@ -195,6 +196,9 @@ def match_datasets_act(column,
195196
prefix : str
196197
prefix for the desired spelling of variable names for the input
197198
datastream (to fix duplicate variable names between instruments)
199+
200+
verbose : boolean
201+
Boolean flag to set verbose output during processing. Default is False.
198202
199203
Returns
200204
-------
@@ -255,6 +259,15 @@ def match_datasets_act(column,
255259
matched[var].attrs.update(source=matched.datastream)
256260

257261
# Merge the two DataSets
258-
column = xr.merge([column, matched])
259-
262+
for k in matched.data_vars:
263+
if k in column.data_vars:
264+
column[k].sel(station=site)[:] = matched.sel(station=site)[k][:].astype(column[k].dtype)
265+
if "_FillValue" in column[k].attrs:
266+
if isinstance(column[k].attrs["_FillValue"], str):
267+
column[k].attrs["_FillValue"] = float(column[k].attrs["_FillValue"])
268+
column[k] = column[k].fillna(column[k].attrs["_FillValue"]).astype(float)
269+
if "missing_value" in column[k].attrs:
270+
if isinstance(column[k].attrs["missing_value"], str):
271+
column[k].attrs["missing_value"] = float(column[k].attrs["missing_value"])
272+
column[k] = column[k].fillna(column[k].attrs["missing_value"]).astype(float)
260273
return column

0 commit comments

Comments
 (0)