def netCDF_empty(self, ncfile_out, stations, nc_in): # TODO: change date type from f4 to f8 for lat and lon ''' Creates an empty station file to hold interpolated reults. The number of stations is defined by the variable stations, variables are determined by the variable list passed from the gridded original netCDF. ncfile_out: full name of the file to be created stations: station list read with common_utils.StationListRead() variables: variables read from netCDF handle lev: list of pressure levels, empty is [] (default) ''' rootgrp = netcdf_base(ncfile_out, len(stations), None, 'hours since 1980-01-01 00:00:00') station = rootgrp["station"] latitude = rootgrp["latitude"] longitude = rootgrp["longitude"] height = rootgrp["height"] # assign station characteristics station[:] = list(stations['station_number']) latitude[:] = list(stations['latitude_dd']) longitude[:] = list(stations['longitude_dd']) height[:] = list(stations['elevation_m']) # extra treatment for pressure level files try: lev = nc_in.variables['level'][:] logger.info("Creating empty 3D file (has pressure levels)") level = rootgrp.createDimension('level', len(lev)) level = rootgrp.createVariable('level', 'i4', ('level')) level.long_name = 'pressure_level' level.units = 'hPa' level[:] = lev except Exception: logger.info("Creating empty 2D file (without pressure levels)") lev = [] # remove extra variables varlist_merra = [str_encode(x) for x in nc_in.variables.keys()] # create and assign variables based on input file for n, var in enumerate(varlist_merra): if variables_skip(var): continue logger.debug(f"Add empty variable: {var}") # extra treatment for pressure level files if len(lev): tmp = rootgrp.createVariable(var, 'f4', ('time', 'level', 'station')) else: tmp = rootgrp.createVariable(var, 'f4', ('time', 'station')) tmp.long_name = str_encode( nc_in.variables[var].long_name) # for merra2 tmp.units = str_encode(nc_in.variables[var].units) # close the file rootgrp.close() logger.debug(f"Created empty netcdf file {ncfile_out}")
def levels2elevation(self, ncfile_in, ncfile_out): """ Linear 1D interpolation of pressure level data available for individual stations to station elevation. Where and when stations are below the lowest pressure level, they are assigned the value of the lowest pressure level. """ # open file ncf = nc.MFDataset(ncfile_in, 'r', aggdim='time') height = ncf.variables['height'][:] nt = len(ncf.variables['time'][:]) nl = len(ncf.variables['level'][:]) # list variables varlist = [str_encode(x) for x in ncf.variables.keys()] for V in [ 'time', 'station', 'latitude', 'longitude', 'level', 'height', 'z' ]: varlist.remove(V) # === open and prepare output netCDF file ============================== # dimensions: station, time # variables: latitude(station), longitude(station), elevation(station) # others: ...(time, station) # stations are integer numbers # create a file (Dataset object, also the root group). rootgrp = netcdf_base(ncfile_out=ncfile_out, n_stations=len(height), n_time=nt, time_units='hours since 1900-01-01 00:00:0.0') rootgrp.source = 'ERA-Interim, interpolated (bi)linearly to stations' time = rootgrp['time'] station = rootgrp['station'] latitude = rootgrp['latitude'] longitude = rootgrp['longitude'] height = rootgrp['height'] # assign base variables time[:] = ncf.variables['time'][:] station[:] = ncf.variables['station'][:] latitude[:] = ncf.variables['latitude'][:] longitude[:] = ncf.variables['longitude'][:] height[:] = ncf.variables['height'][:] # create and assign variables from input file for var in varlist: tmp = rootgrp.createVariable(var, 'f4', ('time', 'station')) tmp.long_name = str_encode(ncf.variables[var].long_name) tmp.units = str_encode(ncf.variables[var].units) # add air pressure as new variable var = 'air_pressure' varlist.append(var) tmp = rootgrp.createVariable(var, 'f4', ('time', 'station')) tmp.long_name = var.encode('UTF8') tmp.units = 'hPa'.encode('UTF8') # end file prepation =================================================== # loop over stations for n, h in enumerate(height): # convert geopotential [mbar] to height [m], shape: (time, level) elevation = ncf.variables['z'][:, :, n] / 9.80665 # TODO: check if height of stations in data range # difference in elevation, level directly above will be >= 0 elev_diff = elevation - h # vector of level indices that fall directly above station. # Apply after ravel() of data. va = np.argmin(elev_diff + (elev_diff < 0) * 100000, axis=1) # mask for situations where station is below lowest level mask = va < (nl - 1) va += np.arange(elevation.shape[0]) * elevation.shape[1] # Vector level indices that fall directly below station. # Apply after ravel() of data. vb = va + mask # +1 when OK, +0 when below lowest level wa, wb = self.calculate_weights(elev_diff, va, vb) #loop over variables and apply interpolation weights for v, var in enumerate(varlist): if var == 'air_pressure': # pressure [Pa] variable from levels, shape: (time, level) data = np.repeat([ncf.variables['level'][:]], len(time), axis=0).ravel() else: #read data from netCDF data = ncf.variables[var][:, :, n].ravel() ipol = data[va] * wa + data[vb] * wb # interpolated value rootgrp.variables[var][:, n] = ipol # assign to file rootgrp.close() ncf.close()
def levels2elevation(self, ncfile_in, ncfile_out): """ Linear 1D interpolation of pressure level data available for individual stations to station elevation. Where and when stations are below the lowest pressure level, they are assigned the value of the lowest pressure level. """ # open file # TODO: check the aggdim does not work ncf = nc.MFDataset(ncfile_in, 'r', aggdim='time') height = ncf.variables['height'][:] nt = len(ncf.variables['time'][:]) nl = len(ncf.variables['level'][:]) # list variables varlist = [str_encode(x) for x in ncf.variables.keys()] for V in ['time', 'station', 'latitude', 'longitude', 'level','height','z']: varlist.remove(V) if self.ens: varlist.remove('number') # === open and prepare output netCDF file ============================== # dimensions: station, time # variables: latitude(station), longitude(station), elevation(station) # others: ...(time, station) # stations are integer numbers # create a file (Dataset object, also the root group). rootgrp = netcdf_base(ncfile_out, len(height), nt, 'hours since 1900-01-01 00:00:0.0', ncf) if self.ens: rootgrp.source = 'ERA5 10-member ensemble, interpolated (bi)linearly to stations' else: rootgrp.source = 'ERA5, interpolated (bi)linearly to stations' time = rootgrp['time'] station = rootgrp['station'] latitude = rootgrp['latitude'] longitude = rootgrp['longitude'] height = rootgrp['height'] # assign base variables time[:] = ncf.variables['time'][:] station[:] = ncf.variables['station'][:] latitude[:] = ncf.variables['latitude'][:] longitude[:] = ncf.variables['longitude'][:] height[:] = ncf.variables['height'][:] # create and assign variables from input file for var in varlist: if self.ens: tmp = rootgrp.createVariable(var, 'f4',('time','number','station')) else: tmp = rootgrp.createVariable(var,'f4',('time', 'station')) tmp.long_name = str_encode(ncf.variables[var].long_name) tmp.units = str_encode(ncf.variables[var].units) # add air pressure as new variable var = 'air_pressure' varlist.append(var) if self.ens: tmp = rootgrp.createVariable(var,'f4',('time','number','station')) else: tmp = rootgrp.createVariable(var,'f4',('time','station')) tmp.long_name = var.encode('UTF8') tmp.units = 'hPa'.encode('UTF8') # end file prepation =================================================== # loop over stations for n, h in enumerate(height): if self.ens: num = ncf.variables['number'][:] for ni in num: elevation = ncf.variables['z'][:,ni,:,n] / 9.80665 elev_diff, va, vb = self.ele_interpolate(elevation, h, nl) wa, wb = self.calculate_weights(elev_diff, va, vb) for v, var in enumerate(varlist): if var == 'air_pressure': # pressure [Pa] variable from levels, shape: (time, level) data = np.repeat([ncf.variables['level'][:]], len(time),axis=0).ravel() else: # read data from netCDF data = ncf.variables[var][:,ni,:,n].ravel() ipol = data[va] * wa + data[vb] * wb # interpolated value rootgrp.variables[var][:,ni,n] = ipol # assign to file else: # convert geopotential [mbar] to height [m], shape: (time, level) elevation = ncf.variables['z'][:,:,n] / 9.80665 elev_diff, va, vb = self.ele_interpolate(elevation, h, nl) wa, wb = self.calculate_weights(elev_diff, va, vb) # loop over variables and apply interpolation weights for v, var in enumerate(varlist): if var == 'air_pressure': # pressure [Pa] variable from levels, shape: (time, level) data = np.repeat([ncf.variables['level'][:]], len(time),axis=0).ravel() else: # read data from netCDF data = ncf.variables[var][:,:,n].ravel() ipol = data[va] * wa + data[vb] * wb # interpolated value rootgrp.variables[var][:,n] = ipol # assign to file rootgrp.close() ncf.close()