Skip to content

Commit a4c21b9

Browse files
committed
Initial commit
1 parent f8f3b74 commit a4c21b9

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

xwrf/config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,3 +82,13 @@ cf_attribute_map:
8282
standard_name: integral_of_surface_upward_heat_flux_in_air_wrt_time
8383
ACLHF:
8484
standard_name: integral_of_surface_upward_latent_heat_flux_in_air_wrf_time
85+
VAR_SSO:
86+
units: m2
87+
description: Variance of Subgrid Scale Orography MSL
88+
HGT_M:
89+
units: m
90+
description: GMTED2010 30-arc-second topography height MSL
91+
PRES:
92+
units: Pa
93+
ST:
94+
units: kelvin

xwrf/io_plugin.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import itertools
2+
import os
3+
import pathlib
4+
import re
5+
import warnings
6+
7+
import pandas as pd
8+
import xarray as xr
9+
10+
from .config import config
11+
12+
_LAT_COORDS = ('XLAT', 'XLAT_M', 'XLAT_U', 'XLAT_V', 'CLAT', 'XLAT_C')
13+
14+
_LON_COORDS = ('XLONG', 'XLONG_M', 'XLONG_U', 'XLONG_V', 'CLONG', 'XLONG_C')
15+
16+
_TIME_COORD_VARS = ('XTIME', 'Times', 'Time', 'time')
17+
18+
_ALL_COORDS = set(itertools.chain(*[_LAT_COORDS, _LON_COORDS, _TIME_COORD_VARS]))
19+
20+
_BOOLEAN_UNITS_ATTRS = ('-', 'flag', '0/1 Flag')
21+
22+
23+
def is_remote_uri(path: str) -> bool:
24+
"""Finds URLs of the form protocol:// or protocol::
25+
This also matches for http[s]://, which were the only remote URLs
26+
supported in <=v0.16.2.
27+
"""
28+
return bool(re.search(r'^[a-z][a-z0-9]*(\://|\:\:)', path))
29+
30+
31+
def _normalize_path(path):
32+
if isinstance(path, pathlib.Path):
33+
path = str(path)
34+
35+
if isinstance(path, str) and not is_remote_uri(path):
36+
path = os.path.abspath(os.path.expanduser(path))
37+
38+
return path
39+
40+
41+
def clean(dataset):
42+
"""
43+
Clean up the dataset.
44+
"""
45+
coords = set(dataset.variables).intersection(_ALL_COORDS)
46+
dataset = dataset.set_coords(coords)
47+
for coord in dataset.coords:
48+
attrs = dataset[coord].attrs
49+
encoding = dataset[coord].encoding
50+
if coord in _TIME_COORD_VARS:
51+
try:
52+
dataset[coord].data = pd.to_datetime(
53+
list(map(lambda x: x.decode('utf-8'), dataset[coord].data.tolist())),
54+
format='%Y-%m-%d_%H:%M:%S',
55+
)
56+
except:
57+
warnings.warn(f'Failed to parse time coordinate: {coord}', stacklevel=2)
58+
59+
elif coord in (_LON_COORDS + _LAT_COORDS) and dataset[coord].ndim == 3:
60+
61+
attrs = dataset[coord].attrs
62+
encoding = dataset[coord].encoding
63+
dataset = dataset.assign_coords(
64+
{coord: (dataset[coord].dims[1:], dataset[coord].data[0, :, :])}
65+
)
66+
dataset[coord].attrs = attrs
67+
dataset[coord].encoding = encoding
68+
69+
return dataset
70+
71+
72+
def make_units_quantify_ready(dataset):
73+
for var in dataset.data_vars:
74+
if dataset[var].attrs.get('units') in _BOOLEAN_UNITS_ATTRS:
75+
dataset[var].attrs.pop('units', None)
76+
77+
78+
def modify_attrs_to_cf(dataset):
79+
vars_to_update = set(config.get('cf_attribute_map').keys()).intersection(set(dataset.keys()))
80+
81+
for var in vars_to_update:
82+
dataset[var].attrs.update(config.get(f'cf_attribute_map.{var}'))
83+
84+
85+
class WRFBackendEntrypoint(xr.backends.BackendEntrypoint):
86+
def open_dataset(
87+
self,
88+
filename_or_obj,
89+
mask_and_scale=True,
90+
decode_times=True,
91+
concat_characters=True,
92+
decode_coords=True,
93+
drop_variables=None,
94+
use_cftime=None,
95+
decode_timedelta=None,
96+
group=None,
97+
mode='r',
98+
format='NETCDF4',
99+
clobber=True,
100+
diskless=False,
101+
persist=False,
102+
lock=None,
103+
autoclose=False,
104+
):
105+
106+
filename_or_obj = _normalize_path(filename_or_obj)
107+
store = xr.backends.NetCDF4DataStore.open(
108+
filename_or_obj,
109+
mode=mode,
110+
format=format,
111+
clobber=clobber,
112+
diskless=diskless,
113+
persist=persist,
114+
lock=lock,
115+
autoclose=autoclose,
116+
)
117+
118+
store_entrypoint = xr.backends.store.StoreBackendEntrypoint()
119+
120+
with xr.core.utils.close_on_error(store):
121+
dataset = store_entrypoint.open_dataset(
122+
store,
123+
mask_and_scale=mask_and_scale,
124+
decode_times=decode_times,
125+
concat_characters=concat_characters,
126+
decode_coords=decode_coords,
127+
drop_variables=drop_variables,
128+
use_cftime=use_cftime,
129+
decode_timedelta=decode_timedelta,
130+
)
131+
132+
make_units_quantify_ready(dataset)
133+
modify_attrs_to_cf(dataset)
134+
return clean(dataset)

0 commit comments

Comments
 (0)