def main(args): fname = args.file save_file = args.save_file # List of variables that are already included in the WRF output and that we want to compute using the wrf-python variables = dict(primary=[ 'XLAT', 'XLONG', 'T2', 'SWDOWN', 'LWUPB', 'GLW', 'PSFC', 'RAINC', 'RAINNC', 'RAINSH', 'SNOWNC', 'SST', 'DIFFUSE_FRAC', 'LANDMASK', 'LAKEMASK', 'PBLH', 'TSK', 'UST' ], computed=['rh2', 'slp', 'mdbz']) # Generate height table for interpolation of U and V components gen_heights = [ 20, 320, 20 ] # minimum height, maximum height, distance between heights # Output time units time_units = 'seconds since 1970-01-01 00:00:00' os.makedirs(os.path.dirname(save_file), exist_ok=True) # create_dir(os.path.dirname(save_file)) # Create list of heights between min and max height separated by a stride value defined above heights = list(np.arange(gen_heights[0], gen_heights[1], gen_heights[2])) heights.append(gen_heights[1]) # Open using netCDF toolbox ncfile = xr.open_dataset(fname) original_global_attributes = ncfile.attrs ncfile = ncfile._file_obj.ds # Load primary variables and append to list primary_vars = {} for var in variables['primary']: primary_vars[var] = cf.delete_attr(getvar(ncfile, var)) # Calculate diagnostic variables defined above and add to dictionary diagnostic_vars = {} for dvar in variables['computed']: diagnostic_vars[dvar.upper()] = cf.delete_attr(getvar(ncfile, dvar)) # Subtract terrain height from height above sea level new_z = getvar(ncfile, 'z') - getvar(ncfile, 'ter') # Calculate u and v components of wind rotated to Earth coordinates uvm = getvar(ncfile, 'uvmet') # interpolate u and v components of wind to 0-200m by 10m uvtemp = interplevel(uvm, new_z, heights, default_fill(np.float32)) uvtemp = uvtemp.rename({'level': 'height'}) utemp, vtemp = cf.split_uvm(uvtemp) # Calculate 10m u and v components of wind rotated to Earth coordinates and split into separate variables primary_vars['U10'], primary_vars['V10'] = cf.split_uvm( getvar(ncfile, 'uvmet10')) # Concatenate the list of calculated u and v values into data array. Append to diagnostic_vars list diagnostic_vars['U'] = xr.concat(utemp, dim='height') diagnostic_vars['V'] = xr.concat(vtemp, dim='height') # Interpolate u and v components of wind to Boundary Layer Heights for wind gust calculation uvpblh = interplevel(uvm, new_z, primary_vars['PBLH']) uvpblh = cf.delete_attr(uvpblh).drop(['u_v' ]) # drop unnecessary attributes upblh = uvpblh[0].rename('upblh') vpblh = uvpblh[1].rename('vpblh') # Calculate wind gust sfcwind = np.sqrt(primary_vars['U10']**2 + primary_vars['V10']**2) pblwind = np.sqrt(upblh**2 + vpblh**2) delwind = pblwind - sfcwind pblval = primary_vars['PBLH'] / 2000 pblval = pblval.where( pblval < 0.5, other=0.5 ) # if the value is less than 0.5, keep it. otherwise, change to 0.5 delwind = delwind * (1 - pblval) gust = sfcwind + delwind gust = gust.drop('level') # drop the 'level' coordinate gust = gust.astype(np.float32) # Create xarray dataset of primary and diagnostic variables ds = xr.Dataset({**primary_vars, **diagnostic_vars}) ds['U'] = ds.U.astype(np.float32) ds['V'] = ds.V.astype(np.float32) ds['height'] = ds.height.astype(np.int32) try: del ds.U.attrs['vert_units'] del ds.V.attrs['vert_units'] except KeyError: pass ds['Times'] = np.array([ pd.Timestamp(ds.Time.data).strftime('%Y-%m-%d_%H:%M:%S') ]).astype('<S19') ds = ds.expand_dims('Time', axis=0) # Add description and units for lon, lat dimensions for georeferencing ds['XLAT'].attrs['description'] = 'latitude' ds['XLAT'].attrs['units'] = 'degree_north' ds['XLONG'].attrs['description'] = 'longitude' ds['XLONG'].attrs['units'] = 'degree_east' # Set XTIME attribute ds['XTIME'].attrs['units'] = 'minutes' # Set lon attributes ds['XLONG'].attrs['long_name'] = 'Longitude' ds['XLONG'].attrs['standard_name'] = 'longitude' ds['XLONG'].attrs['short_name'] = 'lon' ds['XLONG'].attrs['units'] = 'degrees_east' ds['XLONG'].attrs['axis'] = 'X' ds['XLONG'].attrs['valid_min'] = np.float32(-180.0) ds['XLONG'].attrs['valid_max'] = np.float32(180.0) # Set lat attributes ds['XLAT'].attrs['long_name'] = 'Latitude' ds['XLAT'].attrs['standard_name'] = 'latitude' ds['XLAT'].attrs['short_name'] = 'lat' ds['XLAT'].attrs['units'] = 'degrees_north' ds['XLAT'].attrs['axis'] = 'Y' ds['XLAT'].attrs['valid_min'] = np.float32(-90.0) ds['XLAT'].attrs['valid_max'] = np.float32(90.0) # Set depth attributes ds['height'].attrs['long_name'] = 'Height Above Ground Level' ds['height'].attrs['standard_name'] = 'height' ds['height'].attrs[ 'comment'] = 'Derived from subtracting terrain height from height above sea level' ds['height'].attrs['units'] = 'm' ds['height'].attrs['axis'] = 'Z' ds['height'].attrs['positive'] = 'up' # Set u attributes ds['U'].attrs['long_name'] = 'Eastward Wind Component' ds['U'].attrs['standard_name'] = 'eastward_wind' ds['U'].attrs['short_name'] = 'u' ds['U'].attrs['units'] = 'm s-1' ds['U'].attrs['description'] = 'earth rotated u' ds['U'].attrs['valid_min'] = np.float32(-300) ds['U'].attrs['valid_max'] = np.float32(300) # Set v attributes ds['V'].attrs['long_name'] = 'Northward Wind Component' ds['V'].attrs['standard_name'] = 'northward_wind' ds['V'].attrs['short_name'] = 'v' ds['V'].attrs['units'] = 'm s-1' ds['V'].attrs['description'] = 'earth rotated v' ds['V'].attrs['valid_min'] = np.float32(-300) ds['V'].attrs['valid_max'] = np.float32(300) # Set u10 attributes ds['U10'].attrs['long_name'] = 'Eastward Wind Component - 10m' ds['U10'].attrs['standard_name'] = 'eastward_wind' ds['U10'].attrs['short_name'] = 'u' ds['U10'].attrs['units'] = 'm s-1' ds['U10'].attrs['description'] = '10m earth rotated u' ds['U10'].attrs['valid_min'] = np.float32(-300) ds['U10'].attrs['valid_max'] = np.float32(300) # Set v10 attributes ds['V10'].attrs['long_name'] = 'Northward Wind Component - 10m' ds['V10'].attrs['standard_name'] = 'northward_wind' ds['V10'].attrs['short_name'] = 'v' ds['V10'].attrs['units'] = 'm s-1' ds['V10'].attrs['description'] = '10m earth rotated v' ds['V10'].attrs['valid_min'] = np.float32(-300) ds['V10'].attrs['valid_max'] = np.float32(300) # set primary attributes ds['GLW'].attrs[ 'standard_name'] = 'surface_downwelling_longwave_flux_in_air' ds['GLW'].attrs['long_name'] = 'Surface Downwelling Longwave Flux' ds['LWUPB'].attrs['standard_name'] = 'surface_upwelling_longwave_flux' ds['LWUPB'].attrs['long_name'] = 'Surface Upwelling Longwave Flux' ds['PSFC'].attrs['standard_name'] = 'surface_air_pressure' ds['PSFC'].attrs['long_name'] = 'Air Pressure at Surface' ds['RH2'].attrs['standard_name'] = 'relative_humidity' ds['RH2'].attrs['long_name'] = 'Relative Humidity' ds['SLP'].attrs['standard_name'] = 'air_pressure_at_sea_level' ds['SLP'].attrs['long_name'] = 'Air Pressure at Sea Level' ds['SWDOWN'].attrs[ 'standard_name'] = 'surface_downwelling_shortwave_flux_in_air' ds['SWDOWN'].attrs['long_name'] = 'Surface Downwelling Shortwave Flux' ds['T2'].attrs['standard_name'] = 'air_temperature' ds['T2'].attrs['long_name'] = 'Air Temperature at 2m' ds['RAINC'].attrs['long_name'] = 'Accumulated Total Cumulus Precipitation' ds['RAINNC'].attrs[ 'long_name'] = 'Accumulated Total Grid Scale Precipitation' ds['RAINSH'].attrs[ 'long_name'] = 'Accumulated Shallow Cumulus Precipitation' ds['SNOWNC'].attrs['standard_name'] = 'surface_snow_thickness' ds['SNOWNC'].attrs[ 'long_name'] = 'Accumulated Total Grid Scale Snow and Ice' ds['SNOWNC'].attrs['description'] = '{}; water equivalent'.format( ds['SNOWNC'].description) ds['SST'].attrs['standard_name'] = 'sea_surface_temperature' ds['SST'].attrs['long_name'] = 'Sea Surface Temperature' ds['DIFFUSE_FRAC'].attrs[ 'long_name'] = 'Diffuse Fraction of Surface Shortwave Irradiance' ds['MDBZ'].attrs['long_name'] = 'Maximum Radar Reflectivity' ds['TSK'].attrs['long_name'] = 'Surface Skin Temperature' ds['LANDMASK'].attrs['standard_name'] = 'land_binary_mask' ds['LANDMASK'].attrs['long_name'] = 'Land Mask' ds['LAKEMASK'].attrs['long_name'] = 'Lake Mask' ds['PBLH'].attrs[ 'long_name'] = 'Height of the Top of the Planetary Boundary Layer (PBL)' ds['UST'].attrs['long_name'] = 'Friction Velocity' ds['XTIME'].attrs['long_name'] = 'minutes since simulation start' # add the calculated wind gust to the dataset windgust_attrs = dict( long_name='Near Surface Wind Gust', description= 'Calculated wind gust, computed by mixing down momentum from the level at the ' 'top of the planetary boundary layer', units='m s-1') ds['WINDGUST'] = xr.Variable(gust.dims, gust.values, attrs=windgust_attrs) ds['WINDGUST'] = ds['WINDGUST'].expand_dims('Time', axis=0) # Set time attribute ds['Time'].attrs['standard_name'] = 'time' datetime_format = '%Y%m%dT%H%M%SZ' created = pd.Timestamp(pd.datetime.utcnow()).strftime( datetime_format) # creation time Timestamp time_start = pd.Timestamp(pd.Timestamp( ds.Time.data[0])).strftime(datetime_format) time_end = pd.Timestamp(pd.Timestamp( ds.Time.data[0])).strftime(datetime_format) global_attributes = OrderedDict([ ('title', 'Rutgers Weather Research and Forecasting Model'), ('summary', 'Processed netCDF containing subset of RUWRF output'), ('keywords', 'Weather Advisories > Marine Weather/Forecast'), ('Conventions', 'CF-1.7'), ('naming_authority', 'edu.rutgers.marine.rucool'), ('history', 'Hourly WRF raw output processed into new hourly file with selected variables.' ), ('processing_level', 'Level 2'), ('comment', 'WRF Model operated by RUCOOL'), ('acknowledgement', 'This data is provided by the Rutgers Center for Ocean Observing Leadership. Funding is provided by the New Jersey Board of Public Utilities).' ), ('standard_name_vocabulary', 'CF Standard Name Table v41'), ('date_created', created), ('creator_name', 'Joseph Brodie'), ('creator_email', '*****@*****.**'), ('creator_url', 'rucool.marine.rutgers.edu'), ('institution', 'Center for Ocean Observing and Leadership, Department of Marine & Coastal Sciences, Rutgers University' ), ('project', 'New Jersey Board of Public Utilities - Offshore Wind Energy - RUWRF Model' ), ('geospatial_lat_min', -90), ('geospatial_lat_max', 90), ('geospatial_lon_min', -180), ('geospatial_lon_max', 180), ('geospatial_vertical_min', 0.0), ('geospatial_vertical_max', 0.0), ('geospatial_vertical_positive', 'down'), ('time_coverage_start', time_start), ('time_coverage_end', time_end), ('creator_type', 'person'), ('creator_institution', 'Rutgers University'), ('contributor_name', 'Joseph Brodie'), ('contributor_role', 'Director of Atmospheric Research'), ('geospatial_lat_units', 'degrees_north'), ('geospatial_lon_units', 'degrees_east'), ('date_modified', created), ('date_issued', created), ('date_metadata_modified', created), ('keywords_vocabulary', 'GCMD Science Keywords'), ('platform', 'WRF Model Run'), ('cdm_data_type', 'Grid'), ('references', 'http://maracoos.org/node/146 https://rucool.marine.rutgers.edu/facilities https://rucool.marine.rutgers.edu/data' ) ]) global_attributes.update(original_global_attributes) ds = ds.assign_attrs(global_attributes) # Add compression to all variables encoding = {} for k in ds.data_vars: encoding[k] = {'zlib': True, 'complevel': 1} # add the encoding for time so xarray exports the proper time. # Also remove compression from dimensions. They should never have fill values encoding['Time'] = dict(units=time_units, calendar='gregorian', zlib=False, _FillValue=False, dtype=np.double) encoding['XLONG'] = dict(zlib=False, _FillValue=False) encoding['XLAT'] = dict(zlib=False, _FillValue=False) encoding['height'] = dict(zlib=False, _FillValue=False, dtype=np.int32) ds.to_netcdf(save_file, encoding=encoding, format='netCDF4', engine='netcdf4', unlimited_dims='Time')
def main(args): fname = args.file save_file = args.save_file # List of variables to subset variables = ['XLAT', 'XLONG', 'temp', 'rh', 'z', 'pressure', 'ter', 'slp', 'cloudfrac', 'td', 'height_agl'] # Output time units time_units = 'seconds since 1970-01-01 00:00:00' os.makedirs(os.path.dirname(save_file), exist_ok=True) # Open using netCDF toolbox ncfile = xr.open_dataset(fname) original_global_attributes = ncfile.attrs ncfile = ncfile._file_obj.ds # to use xarray as an input to wrf-python (instead of netcdf4) # Load variables and append to list vars = {} for var in variables: if var == 'z': # change 'z' to 'height_msl' varname = 'height_msl' else: varname = var vars[varname] = cf.delete_attr(getvar(ncfile, var)) # Get u and v components of wind rotated to Earth coordinates uvm = getvar(ncfile, 'uvmet') for uv in ['u', 'v']: da = uvm.sel(u_v=uv) da = cf.delete_attr(da).drop(['u_v']) # delete extra attributes and drop the extra coordinate da = da.rename(uv) vars[uv] = da # add to variable dictionary # Create xarray dataset ds = xr.Dataset(vars) ds['Times'] = np.array([pd.Timestamp(ds.Time.data).strftime('%Y-%m-%d_%H:%M:%S')]).astype('<S19') ds = ds.expand_dims('Time', axis=0) # format bottom_top and low_mid_high dimensions ds['bottom_top'] = np.int32(ds.bottom_top) ds['low_mid_high'] = np.array([300, 2000, 6000], dtype='int32') # add attributes for model levels ds['bottom_top'].attrs['units'] = '1' ds['bottom_top'].attrs['long_name'] = 'Model Level' ds['bottom_top'].attrs['comment'] = 'Integer coordinate for native WRF model level' ds['bottom_top'].attrs['axis'] = 'Z' ds['bottom_top'].attrs['positive'] = 'up' ds['bottom_top'].attrs['_CoordinateAxisType'] = 'GeoZ' # need this attribute for THREDDs aggregation ds['bottom_top'].attrs['_CoordinateZisPositive'] = 'up' # need this attribute for THREDDs aggregation ds['low_mid_high'].attrs['units'] = 'm' ds['low_mid_high'].attrs['long_name'] = 'Cloud Layer' ds['low_mid_high'].attrs['comment'] = 'Cloud layer, low 300 m, mid 2000 m, high 6000 m' ds['low_mid_high'].attrs['axis'] = 'Z' ds['low_mid_high'].attrs['positive'] = 'up' ds['low_mid_high'].attrs['_CoordinateAxisType'] = 'Height' # need this attribute for THREDDs aggregation ds['low_mid_high'].attrs['_CoordinateZisPositive'] = 'up' # need this attribute for THREDDs aggregation # Add description and units for lon, lat dimensions for georeferencing ds['XLAT'].attrs['description'] = 'latitude' ds['XLAT'].attrs['units'] = 'degree_north' ds['XLONG'].attrs['description'] = 'longitude' ds['XLONG'].attrs['units'] = 'degree_east' # Set time attribute ds['Time'].attrs['standard_name'] = 'time' ds['Time'].attrs['long_name'] = 'Time' # Set XTIME attribute ds['XTIME'].attrs['units'] = 'minutes' ds['XTIME'].attrs['long_name'] = 'minutes since simulation start' # Set lon attributes ds['XLONG'].attrs['long_name'] = 'Longitude' ds['XLONG'].attrs['standard_name'] = 'longitude' ds['XLONG'].attrs['short_name'] = 'lon' ds['XLONG'].attrs['units'] = 'degrees_east' ds['XLONG'].attrs['axis'] = 'X' ds['XLONG'].attrs['valid_min'] = np.float32(-180.0) ds['XLONG'].attrs['valid_max'] = np.float32(180.0) # Set lat attributes ds['XLAT'].attrs['long_name'] = 'Latitude' ds['XLAT'].attrs['standard_name'] = 'latitude' ds['XLAT'].attrs['short_name'] = 'lat' ds['XLAT'].attrs['units'] = 'degrees_north' ds['XLAT'].attrs['axis'] = 'Y' ds['XLAT'].attrs['valid_min'] = np.float32(-90.0) ds['XLAT'].attrs['valid_max'] = np.float32(90.0) # Set attributes ds['temp'].attrs['long_name'] = 'Air Temperature' ds['temp'].attrs['standard_name'] = 'air_temperature' ds['rh'].attrs['long_name'] = 'Relative Humidity' ds['rh'].attrs['standard_name'] = 'relative_humidity' ds['u'].attrs['long_name'] = 'Eastward Wind Component' ds['u'].attrs['standard_name'] = 'eastward_wind' ds['u'].attrs['short_name'] = 'u' ds['u'].attrs['description'] = 'earth rotated u' ds['u'].attrs['valid_min'] = np.float32(-300) ds['u'].attrs['valid_max'] = np.float32(300) ds['v'].attrs['long_name'] = 'Northward Wind Component' ds['v'].attrs['standard_name'] = 'northward_wind' ds['v'].attrs['short_name'] = 'v' ds['v'].attrs['description'] = 'earth rotated v' ds['v'].attrs['valid_min'] = np.float32(-300) ds['v'].attrs['valid_max'] = np.float32(300) ds['height_msl'].attrs['long_name'] = 'Model Height for Mass Grid (MSL)' ds['height_msl'].attrs['standard_name'] = 'height_above_mean_sea_level' ds['height_msl'].attrs['comment'] = 'Model height above mean sea level' ds['pressure'].attrs['long_name'] = 'Air Pressure' ds['pressure'].attrs['standard_name'] = 'air_pressure' ds['ter'].attrs['long_name'] = 'Model Terrain Height' ds['slp'].attrs['long_name'] = 'Air Pressure at Sea Level' ds['slp'].attrs['standard_name'] = 'air_pressure_at_mean_sea_level' ds['cloudfrac'].attrs['long_name'] = 'Layer Cloud Area Fraction' ds['cloudfrac'].attrs['standard_name'] = 'cloud_area_fraction_in_atmosphere_layer' ds['td'].attrs['long_name'] = 'Dew Point Temperature' ds['td'].attrs['standard_name'] = 'dew_point_temperature' ds['height_agl'].attrs['long_name'] = 'Model Height for Mass Grid (AGL)' ds['height_agl'].attrs['standard_name'] = 'height' ds['height_agl'].attrs['comment'] = 'Model height above ground level' ds['height_agl'].attrs['axis'] = 'Z' ds['height_agl'].attrs['positive'] = 'up' datetime_format = '%Y%m%dT%H%M%SZ' created = pd.Timestamp(pd.datetime.utcnow()).strftime(datetime_format) # creation time Timestamp time_start = pd.Timestamp(pd.Timestamp(ds.Time.data[0])).strftime(datetime_format) time_end = pd.Timestamp(pd.Timestamp(ds.Time.data[0])).strftime(datetime_format) global_attributes = OrderedDict([ ('title', 'Rutgers Weather Research and Forecasting Model'), ('summary', 'Processed netCDF containing subset of RUWRF output'), ('keywords', 'Weather Advisories > Marine Weather/Forecast'), ('Conventions', 'CF-1.7'), ('naming_authority', 'edu.rutgers.marine.rucool'), ('history', 'Hourly WRF raw output processed into new hourly file with selected variables.'), ('processing_level', 'Level 2'), ('comment', 'WRF Model operated by RUCOOL'), ('acknowledgement', 'This data is provided by the Rutgers Center for Ocean Observing Leadership. Funding is provided by the New Jersey Board of Public Utilities.'), ('standard_name_vocabulary', 'CF Standard Name Table v72'), ('date_created', created), ('creator_name', 'Joseph Brodie'), ('creator_email', '*****@*****.**'), ('creator_url', 'rucool.marine.rutgers.edu'), ('institution', 'Center for Ocean Observing and Leadership, Department of Marine & Coastal Sciences, Rutgers University'), ('project', 'New Jersey Board of Public Utilities - Offshore Wind Energy - RUWRF Model'), ('geospatial_lat_min', -90), ('geospatial_lat_max', 90), ('geospatial_lon_min', -180), ('geospatial_lon_max', 180), ('geospatial_vertical_min', 0.0), ('geospatial_vertical_max', 0.0), ('geospatial_vertical_positive', 'down'), ('time_coverage_start', time_start), ('time_coverage_end', time_end), ('creator_type', 'person'), ('creator_institution', 'Rutgers University'), ('contributor_name', 'Joseph Brodie'), ('contributor_role', 'Director of Atmospheric Research'), ('geospatial_lat_units', 'degrees_north'), ('geospatial_lon_units', 'degrees_east'), ('date_modified', created), ('date_issued', created), ('date_metadata_modified', created), ('keywords_vocabulary', 'GCMD Science Keywords'), ('platform', 'WRF Model Run'), ('cdm_data_type', 'Grid'), ('references', 'http://maracoos.org/node/146 https://rucool.marine.rutgers.edu/facilities https://rucool.marine.rutgers.edu/data')]) global_attributes.update(original_global_attributes) ds = ds.assign_attrs(global_attributes) # Add compression to all variables encoding = {} for k in ds.data_vars: encoding[k] = {'zlib': True, 'complevel': 1} # add the encoding for time so xarray exports the proper time. # Also remove compression from dimensions. They should never have fill values encoding['Time'] = dict(units=time_units, calendar='gregorian', zlib=False, _FillValue=False, dtype=np.double) encoding['XLONG'] = dict(zlib=False, _FillValue=False) encoding['XLAT'] = dict(zlib=False, _FillValue=False) ds.to_netcdf(save_file, encoding=encoding, format='netCDF4', engine='netcdf4', unlimited_dims='Time')
def main(args): fname = args.file save_file = args.save_file # List of variables that are already included in the WRF output variables = ['XLAT', 'XLONG', 'LANDMASK', 'LAKEMASK'] # Output time units time_units = 'seconds since 1970-01-01 00:00:00' os.makedirs(os.path.dirname(save_file), exist_ok=True) # Create list of heights in meters and pressure (mb) for interpolation of U and V components heights_m = [50, 100, 150, 500, 600, 700, 800, 900, 1000, 1500, 2000, 2500] heights_mb = [200, 300, 500, 700, 850, 925] # Open using netCDF toolbox ncfile = xr.open_dataset(fname) original_global_attributes = ncfile.attrs ncfile = ncfile._file_obj.ds # Load variables and append to dictionary nc_vars = {} for v in variables: nc_vars[v] = cf.delete_attr(getvar(ncfile, v)) # Calculate u and v components of wind rotated to Earth coordinates uvm = getvar(ncfile, 'uvmet') # Subtract terrain height from height above sea level for height in meters new_z = getvar(ncfile, 'z') - getvar(ncfile, 'ter') # interpolate u and v components of wind to defined heights (in meters) uvtemp = interplevel(uvm, new_z, heights_m, default_fill(np.float32)) uvtemp = uvtemp.rename({'level': 'height'}) utemp, vtemp = cf.split_uvm(uvtemp) # Concatenate the list of calculated u and v values into data array nc_vars['UH'] = xr.concat(utemp, dim='height') nc_vars['VH'] = xr.concat(vtemp, dim='height') # Get pressure - units are Pa so convert to mb p = getvar(ncfile, 'p') * .01 # interpolate u and v components of wind to defined pressure heights (mb) uvtemp = interplevel(uvm, p, heights_mb, default_fill(np.float32)) uvtemp = uvtemp.rename({'level': 'pressure'}) utemp, vtemp = cf.split_uvm(uvtemp) # get geopotential height and interpolate to defined pressure heights (mb) ght = g_geoht.get_height(ncfile, msl=True) geoht = interplevel(ght, p, heights_mb, default_fill(np.float32)) geoht = geoht.rename({'level': 'pressure'}) # Concatenate the list of calculated u and v values and geopotential height into data array nc_vars['UP'] = xr.concat(utemp, dim='pressure') nc_vars['VP'] = xr.concat(vtemp, dim='pressure') nc_vars['geoht'] = xr.concat(geoht, dim='pressure') # Calculate 10m u and v components of wind rotated to Earth coordinates and split into separate variables nc_vars['U10'], nc_vars['V10'] = cf.split_uvm(getvar(ncfile, 'uvmet10')) # Create xarray dataset of variables ds = xr.Dataset({**nc_vars}) ds['UH'] = ds.UH.astype(np.float32) ds['VH'] = ds.VH.astype(np.float32) ds['UP'] = ds.UP.astype(np.float32) ds['VP'] = ds.VP.astype(np.float32) ds['geoht'] = ds.geoht.astype(np.float32) ds['height'] = ds.height.astype(np.int32) ds['pressure'] = ds.pressure.astype(np.int32) try: del ds.UH.attrs['vert_units'] del ds.VH.attrs['vert_units'] del ds.UP.attrs['vert_units'] del ds.VP.attrs['vert_units'] except KeyError: pass ds['Times'] = np.array([ pd.Timestamp(ds.Time.data).strftime('%Y-%m-%d_%H:%M:%S') ]).astype('<S19') ds = ds.expand_dims('Time', axis=0) # Add description and units for lon, lat dimensions for georeferencing ds['XLAT'].attrs['description'] = 'latitude' ds['XLAT'].attrs['units'] = 'degree_north' ds['XLONG'].attrs['description'] = 'longitude' ds['XLONG'].attrs['units'] = 'degree_east' # Set XTIME attribute ds['XTIME'].attrs['units'] = 'minutes' # Set lon attributes ds['XLONG'].attrs['long_name'] = 'Longitude' ds['XLONG'].attrs['standard_name'] = 'longitude' ds['XLONG'].attrs['short_name'] = 'lon' ds['XLONG'].attrs['units'] = 'degrees_east' ds['XLONG'].attrs['axis'] = 'X' ds['XLONG'].attrs['valid_min'] = np.float32(-180.0) ds['XLONG'].attrs['valid_max'] = np.float32(180.0) # Set lat attributes ds['XLAT'].attrs['long_name'] = 'Latitude' ds['XLAT'].attrs['standard_name'] = 'latitude' ds['XLAT'].attrs['short_name'] = 'lat' ds['XLAT'].attrs['units'] = 'degrees_north' ds['XLAT'].attrs['axis'] = 'Y' ds['XLAT'].attrs['valid_min'] = np.float32(-90.0) ds['XLAT'].attrs['valid_max'] = np.float32(90.0) # Set depth attributes ds['height'].attrs['long_name'] = 'Height Above Ground Level' ds['height'].attrs['standard_name'] = 'height' ds['height'].attrs[ 'comment'] = 'Derived from subtracting terrain height from height above sea level' ds['height'].attrs['units'] = 'm' ds['height'].attrs['axis'] = 'Z' ds['height'].attrs['positive'] = 'up' ds['pressure'].attrs['long_name'] = 'Pressure' ds['pressure'].attrs['standard_name'] = 'air_pressure' ds['pressure'].attrs['units'] = 'millibars' ds['pressure'].attrs['axis'] = 'Z' ds['pressure'].attrs['positive'] = 'up' # Set u attributes - interpolated to height in meters ds['UH'].attrs['long_name'] = 'Eastward Wind Component' ds['UH'].attrs['standard_name'] = 'eastward_wind' ds['UH'].attrs['short_name'] = 'u' ds['UH'].attrs['units'] = 'm s-1' ds['UH'].attrs[ 'description'] = 'earth rotated u, interpolated to Height Above Ground Level in meters' ds['UH'].attrs['valid_min'] = np.float32(-300) ds['UH'].attrs['valid_max'] = np.float32(300) # Set v attributes - interpolated to height in meters ds['VH'].attrs['long_name'] = 'Northward Wind Component' ds['VH'].attrs['standard_name'] = 'northward_wind' ds['VH'].attrs['short_name'] = 'v' ds['VH'].attrs['units'] = 'm s-1' ds['VH'].attrs[ 'description'] = 'earth rotated v, interpolated to Height Above Ground Level in meters' ds['VH'].attrs['valid_min'] = np.float32(-300) ds['VH'].attrs['valid_max'] = np.float32(300) # Set u attributes - interpolated to pressure in mb ds['UP'].attrs['long_name'] = 'Eastward Wind Component' ds['UP'].attrs['standard_name'] = 'eastward_wind' ds['UP'].attrs['short_name'] = 'u' ds['UP'].attrs['units'] = 'm s-1' ds['UP'].attrs[ 'description'] = 'earth rotated u, interpolated to pressure in millibars' ds['UP'].attrs['valid_min'] = np.float32(-300) ds['UP'].attrs['valid_max'] = np.float32(300) # Set v attributes - interpolated to pressure in mb ds['VP'].attrs['long_name'] = 'Northward Wind Component' ds['VP'].attrs['standard_name'] = 'northward_wind' ds['VP'].attrs['short_name'] = 'v' ds['VP'].attrs['units'] = 'm s-1' ds['VP'].attrs[ 'description'] = 'earth rotated v, interpolated to pressure in millibars' ds['VP'].attrs['valid_min'] = np.float32(-300) ds['VP'].attrs['valid_max'] = np.float32(300) # Set u10 attributes ds['U10'].attrs['long_name'] = 'Eastward Wind Component - 10m' ds['U10'].attrs['standard_name'] = 'eastward_wind' ds['U10'].attrs['short_name'] = 'u' ds['U10'].attrs['units'] = 'm s-1' ds['U10'].attrs['description'] = '10m earth rotated u' ds['U10'].attrs['valid_min'] = np.float32(-300) ds['U10'].attrs['valid_max'] = np.float32(300) # Set v10 attributes ds['V10'].attrs['long_name'] = 'Northward Wind Component - 10m' ds['V10'].attrs['standard_name'] = 'northward_wind' ds['V10'].attrs['short_name'] = 'v' ds['V10'].attrs['units'] = 'm s-1' ds['V10'].attrs['description'] = '10m earth rotated v' ds['V10'].attrs['valid_min'] = np.float32(-300) ds['V10'].attrs['valid_max'] = np.float32(300) # Set geopotential height attributes ds['geoht'].attrs['long_name'] = 'Geopotential Height Above Mean Sea Level' ds['geoht'].attrs['standard_name'] = 'geopotential_height' ds['geoht'].attrs['units'] = 'm' ds['geoht'].attrs[ 'description'] = 'geopotential height above mean sea level, interpolated to pressure in millibars' ds['LANDMASK'].attrs['standard_name'] = 'land_binary_mask' ds['LANDMASK'].attrs['long_name'] = 'Land Mask' ds['LAKEMASK'].attrs['long_name'] = 'Lake Mask' ds['XTIME'].attrs['long_name'] = 'minutes since simulation start' # Set time attribute ds['Time'].attrs['standard_name'] = 'time' datetime_format = '%Y%m%dT%H%M%SZ' created = pd.Timestamp(pd.datetime.utcnow()).strftime( datetime_format) # creation time Timestamp time_start = pd.Timestamp(pd.Timestamp( ds.Time.data[0])).strftime(datetime_format) time_end = pd.Timestamp(pd.Timestamp( ds.Time.data[0])).strftime(datetime_format) global_attributes = OrderedDict([ ('title', 'Rutgers Weather Research and Forecasting Model'), ('summary', 'Processed netCDF containing subset of RUWRF output'), ('keywords', 'Weather Advisories > Marine Weather/Forecast'), ('Conventions', 'CF-1.7'), ('naming_authority', 'edu.rutgers.marine.rucool'), ('history', '10-minute WRF raw output processed into new 10-minute file with selected variables.' ), ('processing_level', 'Level 2'), ('comment', 'WRF Model operated by RUCOOL'), ('acknowledgement', 'This data is provided by the Rutgers Center for Ocean Observing Leadership. Funding is provided by the New Jersey Board of Public Utilities).' ), ('standard_name_vocabulary', 'CF Standard Name Table v41'), ('date_created', created), ('creator_name', 'Joseph Brodie'), ('creator_email', '*****@*****.**'), ('creator_url', 'rucool.marine.rutgers.edu'), ('institution', 'Center for Ocean Observing and Leadership, Department of Marine & Coastal Sciences, Rutgers University' ), ('project', 'New Jersey Board of Public Utilities - Offshore Wind Energy - RUWRF Model' ), ('geospatial_lat_min', -90), ('geospatial_lat_max', 90), ('geospatial_lon_min', -180), ('geospatial_lon_max', 180), ('geospatial_vertical_min', 0.0), ('geospatial_vertical_max', 0.0), ('geospatial_vertical_positive', 'down'), ('time_coverage_start', time_start), ('time_coverage_end', time_end), ('creator_type', 'person'), ('creator_institution', 'Rutgers University'), ('contributor_name', 'Joseph Brodie'), ('contributor_role', 'Director of Atmospheric Research'), ('geospatial_lat_units', 'degrees_north'), ('geospatial_lon_units', 'degrees_east'), ('date_modified', created), ('date_issued', created), ('date_metadata_modified', created), ('keywords_vocabulary', 'GCMD Science Keywords'), ('platform', 'WRF Model Run'), ('cdm_data_type', 'Grid'), ('references', 'http://maracoos.org/node/146 https://rucool.marine.rutgers.edu/facilities https://rucool.marine.rutgers.edu/data' ) ]) global_attributes.update(original_global_attributes) ds = ds.assign_attrs(global_attributes) # Add compression to all variables encoding = {} for k in ds.data_vars: encoding[k] = {'zlib': True, 'complevel': 1} # add the encoding for time so xarray exports the proper time. # Also remove compression from dimensions. They should never have fill values encoding['Time'] = dict(units=time_units, calendar='gregorian', zlib=False, _FillValue=False, dtype=np.double) encoding['XLONG'] = dict(zlib=False, _FillValue=False) encoding['XLAT'] = dict(zlib=False, _FillValue=False) encoding['height'] = dict(zlib=False, _FillValue=False, dtype=np.int32) encoding['pressure'] = dict(zlib=False, _FillValue=False, dtype=np.int32) ds.to_netcdf(save_file, encoding=encoding, format='netCDF4', engine='netcdf4', unlimited_dims='Time')