Skip to content

Commit 61ca58e

Browse files
authored
Merge pull request #289 from jmcvey3/dual_profile
Add capability to read ADCP dual profile configurations
2 parents 5d014f4 + 2e292b0 commit 61ca58e

File tree

11 files changed

+360
-238
lines changed

11 files changed

+360
-238
lines changed
300 KB
Binary file not shown.
1006 Bytes
Binary file not shown.
-16 Bytes
Binary file not shown.
981 Bytes
Binary file not shown.
Binary file not shown.
980 Bytes
Binary file not shown.
145 KB
Binary file not shown.

mhkit/dolfyn/io/base.py

Lines changed: 123 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -113,69 +113,65 @@ def _handle_nan(data):
113113

114114

115115
def _create_dataset(data):
116-
"""Creates an xarray dataset from dictionary created from binary
116+
"""
117+
Creates an xarray dataset from dictionary created from binary
117118
readers.
118119
Direction 'dir' coordinates are set in `set_coords`
119120
"""
120-
ds = xr.Dataset()
121-
tag = ["_avg", "_b5", "_echo", "_bt", "_gps", "_altraw", "_sl"]
122121

123-
FoR = {}
124-
try:
125-
beams = data["attrs"]["n_beams"]
126-
except:
122+
tag = ["_avg", "_b5", "_echo", "_bt", "_gps", "_altraw", "_altraw_avg", "_sl"]
123+
124+
ds_dict = {}
125+
for key in data["coords"]:
126+
ds_dict[key] = {"dims": (key), "data": data["coords"][key]}
127+
128+
# Set various coordinate frames
129+
if "n_beams_avg" in data["attrs"]:
127130
beams = data["attrs"]["n_beams_avg"]
131+
else:
132+
beams = data["attrs"]["n_beams"]
128133
n_beams = max(min(beams, 4), 3)
129134
beams = np.arange(1, n_beams + 1, dtype=np.int32)
130-
FoR["beam"] = xr.DataArray(
131-
beams,
132-
dims=["beam"],
133-
name="beam",
134-
attrs={"units": "1", "long_name": "Beam Reference Frame"},
135-
)
136-
FoR["dir"] = xr.DataArray(
137-
beams,
138-
dims=["dir"],
139-
name="dir",
140-
attrs={"units": "1", "long_name": "Reference Frame"},
141-
)
142135

136+
ds_dict["beam"] = {"dims": ("beam"), "data": beams}
137+
ds_dict["dir"] = {"dims": ("dir"), "data": beams}
138+
data["units"].update({"beam": "1", "dir": "1"})
139+
data["long_name"].update({"beam": "Beam Reference Frame", "dir": "Reference Frame"})
140+
141+
# Iterate through data variables and add them to new dictionary
143142
for key in data["data_vars"]:
144143
# orientation matrices
145144
if "mat" in key:
146145
if "inst" in key: # beam2inst & inst2head orientation matrices
147-
ds[key] = xr.DataArray(
148-
data["data_vars"][key],
149-
coords={"x1": beams, "x2": beams},
150-
dims=["x1", "x2"],
151-
attrs={"units": "1", "long_name": "Rotation Matrix"},
152-
)
146+
if "x1" not in ds_dict:
147+
ds_dict["x1"] = {"dims": ("x1"), "data": beams}
148+
ds_dict["x2"] = {"dims": ("x2"), "data": beams}
149+
150+
ds_dict[key] = {"dims": ("x1", "x2"), "data": data["data_vars"][key]}
151+
data["units"].update({key: "1"})
152+
data["long_name"].update({key: "Rotation Matrix"})
153+
153154
elif "orientmat" in key: # earth2inst orientation matrix
154155
if any(val in key for val in tag):
155156
tg = "_" + key.rsplit("_")[-1]
156157
else:
157158
tg = ""
158-
earth = xr.DataArray(
159-
["E", "N", "U"],
160-
dims=["earth"],
161-
name="earth",
162-
attrs={"units": "1", "long_name": "Earth Reference Frame"},
163-
)
164-
inst = xr.DataArray(
165-
["X", "Y", "Z"],
166-
dims=["inst"],
167-
name="inst",
168-
attrs={"units": "1", "long_name": "Instrument Reference Frame"},
159+
160+
ds_dict["earth"] = {"dims": ("earth"), "data": ["E", "N", "U"]}
161+
ds_dict["inst"] = {"dims": ("inst"), "data": ["X", "Y", "Z"]}
162+
ds_dict[key] = {
163+
"dims": ("earth", "inst", "time" + tg),
164+
"data": data["data_vars"][key],
165+
}
166+
data["units"].update(
167+
{"earth": "1", "inst": "1", key: data["units"]["orientmat"]}
169168
)
170-
time = data["coords"]["time" + tg]
171-
ds[key] = xr.DataArray(
172-
data["data_vars"][key],
173-
coords={"earth": earth, "inst": inst, "time" + tg: time},
174-
dims=["earth", "inst", "time" + tg],
175-
attrs={
176-
"units": data["units"]["orientmat"],
177-
"long_name": data["long_name"]["orientmat"],
178-
},
169+
data["long_name"].update(
170+
{
171+
"earth": "Earth Reference Frame",
172+
"inst": "Instrument Reference Frame",
173+
key: data["long_name"]["orientmat"],
174+
}
179175
)
180176

181177
# quaternion units never change
@@ -184,67 +180,43 @@ def _create_dataset(data):
184180
tg = "_" + key.rsplit("_")[-1]
185181
else:
186182
tg = ""
187-
q = xr.DataArray(
188-
["w", "x", "y", "z"],
189-
dims=["q"],
190-
name="q",
191-
attrs={"units": "1", "long_name": "Quaternion Vector Components"},
192-
)
193-
time = data["coords"]["time" + tg]
194-
ds[key] = xr.DataArray(
195-
data["data_vars"][key],
196-
coords={"q": q, "time" + tg: time},
197-
dims=["q", "time" + tg],
198-
attrs={
199-
"units": data["units"]["quaternions"],
200-
"long_name": data["long_name"]["quaternions"],
201-
},
202-
)
203-
else:
204-
# Assign each variable to a dataArray
205-
ds[key] = xr.DataArray(data["data_vars"][key])
206-
# Assign metadata to each dataArray
207-
for md in ["units", "long_name", "standard_name"]:
208-
if key in data[md]:
209-
ds[key].attrs[md] = data[md][key]
210-
try: # make sure ones with tags get units
211-
tg = "_" + key.rsplit("_")[-1]
212-
if any(val in key for val in tag):
213-
ds[key].attrs[md] = data[md][key[: -len(tg)]]
214-
except:
215-
pass
216183

217-
# Fill in dimensions and coordinates for each dataArray
184+
if "q" not in ds_dict:
185+
ds_dict["q"] = {"dims": ("q"), "data": ["w", "x", "y", "z"]}
186+
data["units"].update({"q": "1"})
187+
data["long_name"].update({"q": "Quaternion Vector Components"})
188+
189+
ds_dict[key] = {"dims": ("q", "time" + tg), "data": data["data_vars"][key]}
190+
data["units"].update({key: data["units"]["quaternions"]})
191+
data["long_name"].update({key: data["long_name"]["quaternions"]})
192+
193+
else:
218194
shp = data["data_vars"][key].shape
219-
l = len(shp)
220-
if l == 1: # 1D variables
221-
if any(val in key for val in tag):
195+
if len(shp) == 1: # 1D variables
196+
if "_altraw_avg" in key:
197+
tg = "_altraw_avg"
198+
elif any(val in key for val in tag):
222199
tg = "_" + key.rsplit("_")[-1]
223200
else:
224201
tg = ""
225-
ds[key] = ds[key].rename({"dim_0": "time" + tg})
226-
ds[key] = ds[key].assign_coords(
227-
{"time" + tg: data["coords"]["time" + tg]}
228-
)
202+
ds_dict[key] = {"dims": ("time" + tg), "data": data["data_vars"][key]}
229203

230-
elif l == 2: # 2D variables
204+
elif len(shp) == 2: # 2D variables
231205
if key == "echo":
232-
ds[key] = ds[key].rename(
233-
{"dim_0": "range_echo", "dim_1": "time_echo"}
234-
)
235-
ds[key] = ds[key].assign_coords(
236-
{
237-
"range_echo": data["coords"]["range_echo"],
238-
"time_echo": data["coords"]["time_echo"],
239-
}
240-
)
241-
elif key == "samp_altraw": # raw altimeter samples
242-
ds[key] = ds[key].rename(
243-
{"dim_0": "n_altraw", "dim_1": "time_altraw"}
244-
)
245-
ds[key] = ds[key].assign_coords(
246-
{"time_altraw": data["coords"]["time_altraw"]}
247-
)
206+
ds_dict[key] = {
207+
"dims": ("range_echo", "time_echo"),
208+
"data": data["data_vars"][key],
209+
}
210+
elif key == "samp_altraw":
211+
ds_dict[key] = {
212+
"dims": ("n_altraw", "time_altraw"),
213+
"data": data["data_vars"][key],
214+
}
215+
elif key == "samp_altraw_avg":
216+
ds_dict[key] = {
217+
"dims": ("n_altraw_avg", "time_altraw_avg"),
218+
"data": data["data_vars"][key],
219+
}
248220

249221
# ADV/ADCP instrument vector data, bottom tracking
250222
elif shp[0] == n_beams and not any(val in key for val in tag[:3]):
@@ -259,31 +231,36 @@ def _create_dataset(data):
259231
dim0 = "beam"
260232
else:
261233
dim0 = "dir"
262-
ds[key] = ds[key].rename({"dim_0": dim0, "dim_1": "time" + tg})
263-
ds[key] = ds[key].assign_coords(
264-
{dim0: FoR[dim0], "time" + tg: data["coords"]["time" + tg]}
265-
)
234+
ds_dict[key] = {
235+
"dims": (dim0, "time" + tg),
236+
"data": data["data_vars"][key],
237+
}
238+
266239
# ADCP IMU data
267240
elif shp[0] == 3:
268241
if not any(val in key for val in tag):
269242
tg = ""
270243
else:
271244
tg = [val for val in tag if val in key]
272245
tg = tg[0]
273-
dirIMU = xr.DataArray(
274-
[1, 2, 3],
275-
dims=["dirIMU"],
276-
name="dirIMU",
277-
attrs={"units": "1", "long_name": "Reference Frame"},
278-
)
279-
ds[key] = ds[key].rename({"dim_0": "dirIMU", "dim_1": "time" + tg})
280-
ds[key] = ds[key].assign_coords(
281-
{"dirIMU": dirIMU, "time" + tg: data["coords"]["time" + tg]}
282-
)
283246

284-
ds[key].attrs["coverage_content_type"] = "physicalMeasurement"
247+
if "dirIMU" not in ds_dict:
248+
ds_dict["dirIMU"] = {"dims": ("dirIMU"), "data": [1, 2, 3]}
249+
data["units"].update({"dirIMU": "1"})
250+
data["long_name"].update({"dirIMU": "Reference Frame"})
251+
252+
ds_dict[key] = {
253+
"dims": ("dirIMU", "time" + tg),
254+
"data": data["data_vars"][key],
255+
}
285256

286-
elif l == 3: # 3D variables
257+
elif "b5" in tg:
258+
ds_dict[key] = {
259+
"dims": ("range_b5", "time_b5"),
260+
"data": data["data_vars"][key],
261+
}
262+
263+
elif len(shp) == 3: # 3D variables
287264
if "vel" in key:
288265
dim0 = "dir"
289266
else: # amp, corr, prcnt_gd, status
@@ -294,43 +271,43 @@ def _create_dataset(data):
294271
tg = "_avg"
295272
else:
296273
tg = ""
297-
ds[key] = ds[key].rename(
298-
{"dim_0": dim0, "dim_1": "range" + tg, "dim_2": "time" + tg}
299-
)
300-
ds[key] = ds[key].assign_coords(
301-
{
302-
dim0: FoR[dim0],
303-
"range" + tg: data["coords"]["range" + tg],
304-
"time" + tg: data["coords"]["time" + tg],
305-
}
306-
)
274+
ds_dict[key] = {
275+
"dims": (dim0, "range" + tg, "time" + tg),
276+
"data": data["data_vars"][key],
277+
}
278+
307279
elif "b5" in key:
308-
# xarray can't handle coords of length 1
309-
ds[key] = ds[key][0]
310-
ds[key] = ds[key].rename({"dim_1": "range_b5", "dim_2": "time_b5"})
311-
ds[key] = ds[key].assign_coords(
312-
{
313-
"range_b5": data["coords"]["range_b5"],
314-
"time_b5": data["coords"]["time_b5"],
315-
}
316-
)
280+
# "vel_b5" sometimes stored as (1, range_b5, time_b5)
281+
ds_dict[key] = {
282+
"dims": ("range_b5", "time_b5"),
283+
"data": data["data_vars"][key][0],
284+
}
317285
elif "sl" in key:
318-
ds[key] = ds[key].rename(
319-
{"dim_0": dim0, "dim_1": "range_sl", "dim_2": "time"}
320-
)
321-
ds[key] = ds[key].assign_coords(
322-
{
323-
"range_sl": data["coords"]["range_sl"],
324-
"time": data["coords"]["time"],
325-
}
326-
)
286+
ds_dict[key] = {
287+
"dims": (dim0, "range_sl", "time"),
288+
"data": data["data_vars"][key],
289+
}
327290
else:
328-
ds = ds.drop_vars(key)
329291
warnings.warn(f"Variable not included in dataset: {key}")
330292

293+
# Create dataset
294+
ds = xr.Dataset.from_dict(ds_dict)
295+
296+
# Assign data array attributes
297+
for key in ds.variables:
298+
for md in ["units", "long_name", "standard_name"]:
299+
if key in data[md]:
300+
ds[key].attrs[md] = data[md][key]
301+
if len(ds[key].shape) > 1:
331302
ds[key].attrs["coverage_content_type"] = "physicalMeasurement"
303+
try: # make sure ones with tags get units
304+
tg = "_" + key.rsplit("_")[-1]
305+
if any(val in key for val in tag):
306+
ds[key].attrs[md] = data[md][key[: -len(tg)]]
307+
except:
308+
pass
332309

333-
# coordinate attributes
310+
# Assign coordinate attributes
334311
for ky in ds.dims:
335312
ds[ky].attrs["coverage_content_type"] = "coordinate"
336313
r_list = [r for r in ds.coords if "range" in r]
@@ -344,7 +321,7 @@ def _create_dataset(data):
344321
ds[ky].attrs["long_name"] = "Time"
345322
ds[ky].attrs["standard_name"] = "time"
346323

347-
# dataset metadata
324+
# Set dataset metadata
348325
ds.attrs = data["attrs"]
349326

350327
return ds

0 commit comments

Comments
 (0)