def upscale_field(lons, lats, field, x_scale=2, y_scale=2, is_degrees=True): ''' Takes a field defined on a sphere using lons/lats and returns an upscaled version, using cubic spline interpolation. ''' if is_degrees: lons = lons * np.pi / 180. lats = (lats + 90) * np.pi / 180. d_lon = lons[1] - lons[0] d_lat = lats[1] - lats[0] new_lon = np.linspace(lons[0], lons[-1], len(lons) * x_scale) new_lat = np.linspace(lats[0], lats[-1], len(lats) * x_scale) mesh_new_lat, mesh_new_lon = np.meshgrid(new_lat, new_lon) if True: lut = RectSphereBivariateSpline(lats[1:-1], lons[1:-1], field[1:-1, 1:-1]) interp_field = lut.ev(mesh_new_lat[1:-1, 1:-1].ravel(), mesh_new_lon[1:-1, 1:-1].ravel()).reshape( mesh_new_lon.shape[0] - 2, mesh_new_lon.shape[1] - 2).T else: pass if is_degrees: new_lon = new_lon * 180. / np.pi new_lat = (new_lat * 180. / np.pi) - 90 return new_lon[1:-1], new_lat[1:-1], interp_field
def resize_interpolate(ary_in, new_size): if hasattr(ary_in, 'dtype'): lons = sorted(list(set(ary_in['x']))) lats = sorted(list(set(ary_in['y']))) zs = ary_in['z'] else: lons, lats, zs = (numpy.array(sorted(list(set(x)))) for x in (zip(*ary_in))) #print('lls: ', lons, lats) zs = numpy.array([rw[2] for rw in ary_in]) # new_lons = numpy.linspace(min(lons), max(lons), new_size[0]) * numpy.pi / 180. new_lats = numpy.linspace(min(lats), max(lats), new_size[1]) * numpy.pi / 180. #return new_lons, new_lats new_lats, new_lons = numpy.meshgrid(new_lats, new_lons) # data = numpy.array(zs) data.shape = (numpy.size(lats), numpy.size(lons) ) # or is it len(lats), len(lons) (yes, i think it is) lut = RectSphereBivariateSpline(lats, lons, data) #data_interp = lut.ev(new_lats.ravel(), new_lons.ravel()) data_interp = lut.ev(new_lats.ravel(), new_lons.ravel()).reshape(new_size).T data_interp = data_interp.reshape((data_interp.size, )) #return data_interp # # return np.core.records.fromarrays( zip(*[[x * 180 / numpy.pi, y * 180 / numpy.pi, z] for (x, y), z in zip( itertools.product(new_lons.reshape(( new_lons.size, )), new_lats.reshape(( new_lats.size, ))), data_interp)]), dtype=[('x', '>f8'), ('y', '>f8'), ('z', '>f8')])
def upscale_field(lons, lats, field, x_scale=2, y_scale=2, is_degrees=True): ''' Takes a field defined on a sphere using lons/lats and returns an upscaled version, using cubic spline interpolation. ''' if is_degrees: lons = lons * np.pi / 180. lats = (lats + 90) * np.pi / 180. d_lon = lons[1] - lons[0] d_lat = lats[1] - lats[0] new_lon = np.linspace(lons[0], lons[-1], len(lons) * x_scale) new_lat = np.linspace(lats[0], lats[-1], len(lats) * x_scale) mesh_new_lat, mesh_new_lon = np.meshgrid(new_lat, new_lon) if True: lut = RectSphereBivariateSpline(lats[1:-1], lons[1:-1], field[1:-1, 1:-1]) interp_field = lut.ev(mesh_new_lat[1:-1, 1:-1].ravel(), mesh_new_lon[1:-1, 1:-1].ravel()).reshape(mesh_new_lon.shape[0] - 2, mesh_new_lon.shape[1] - 2).T else: pass if is_degrees: new_lon = new_lon * 180. / np.pi new_lat = (new_lat * 180. / np.pi) - 90 return new_lon[1:-1], new_lat[1:-1], interp_field
def ncepGFSmodel2swath(lats, lons, data, lats_2, lons_2): func = RectSphereBivariateSpline(lats, lons, data) data_2 = func.ev(lats_2.ravel(),\ lons_2.ravel())\ .reshape(lats_2.shape) return data_2
def apply_gia(sites_of_retain4, dir_file_gia): # read the rate of radial displacement (UP) on a 0.2x0.2 grid from ICE-6G_D(VM5a) data_ICE_6G_D = dir_file_gia # Open the dataset and print out metadeta ds = xr.open_dataset(data_ICE_6G_D) # convert lons and lats of sites in [d,m,s] to lons and colats in radian sites_of_retain4_lons = sites_of_retain4['Lon. °E'] * u.deg.to(u.rad) sites_of_retain4_lats = sites_of_retain4['Lat. °N'] * u.deg.to(u.rad) sites_of_retain4_colats = np.pi / 2 - sites_of_retain4_lats # colats and lons in radians lats, lons, up_rate = ds['Lat'], ds['Lon'], ds['Drad_250'] colats_rad = np.deg2rad(90 - lats) lons_rad = np.deg2rad(lons) lut = RectSphereBivariateSpline(colats_rad, lons_rad, up_rate) up_rate_sites = lut.ev(sites_of_retain4_colats, sites_of_retain4_lons) sites_of_retain5 = sites_of_retain4[up_rate_sites < 0.75] return sites_of_retain5
def create_model(file_name=None, tag=None): '''Takes .ffe model, converts into healpix and smooths the response''' ##Make an empty array for healpix projection beam_response = zeros(len_empty_healpix) * nan ##Load ffe model data in, which is stored in real and imaginary components ##of a phi/theta polaristaion representation of the beam ##apparently this is normal to beam engineers data = loadtxt(file_name) theta = data[:, 0] phi = data[:, 1] re_theta = data[:, 2] im_theta = data[:, 3] re_phi = data[:, 4] im_phi = data[:, 5] ##Convert to complex numbers X_theta = re_theta.astype(complex) X_theta.imag = im_theta X_phi = re_phi.astype(complex) X_phi.imag = im_phi ##make a coord grid for the data in the .ffe model theta_range = linspace(epsilon, 90, 91) phi_range = linspace(epsilon, 359.0, 360) theta_mesh, phi_mesh = meshgrid(theta_range, phi_range) ##Convert reference model into power from the complex values ##make an inf values super small power = 10 * log10(abs(X_theta)**2 + abs(X_phi)**2) power[where(power == -inf)] = -80 ##Had a problem with edge effects, leave off near horizon values power = power[:-91] ##Get things into correct shape and do an interpolation ##s is a paramater I had to play with to get by eye nice results power.shape = phi_mesh.shape lut = RectSphereBivariateSpline(theta_range * (pi / 180.0), phi_range * (pi / 180.0), power.T, s=0.1) ##Get the theta and phi of all healpixels ip = arange(len_empty_healpix) theta_rad, phi_rad = hp.pix2ang(nside, ip) ##Use spline to map beam into healpixels beam_response = lut.ev(theta_rad, phi_rad) return beam_response, theta_mesh, phi_mesh, power, theta
def I_sc(u, phi): ''' Given a calculated, discretized, scattered intensity (Ifull), evaluate it at the given u, phi. Use linear interpolation on a sphere. Global variables: Ifull, u_bounds, phi_bounds ''' ui = (u_bounds[1:] + u_bounds[:-1]) / 2 phii = (phi_bounds[1:] + phi_bounds[:-1]) / 2 thi = arccos(ui)[::-1] Ii = Ifull[-1, ::-1, :] # There is a discontinuity at u=0 (th=pi/2), so interpolation over either # the top half or bottom half plane. In practice, we only need the bottom. # Another detail is that we should specify a point at zenith, otherwise # it's extrapolating. zenith_val = np.mean(Ii[0, :]) # Because this interpolator is not ideal, create a ghost data point to # enforce linear interpolation across 0-2pi jump in phi phii_new = concatenate(([0], phii)) Ii_new = zeros((N, R + 1)) Ii_new[:, 0] = (Ii[:, -1] + Ii[:, 0]) / 2 Ii_new[:, 1:] = Ii Iint = RectSphereBivariateSpline(thi[:N / 2], phii_new, Ii_new[:N / 2, :], pole_values=(zenith_val, None)) return Iint(arccos(u), phi).item()
def regrid(phi_old, lambda_old, nphi_new, nlambda_new, data): Drc = data[1:-1, 1:-1, 0].copy() lut = RectSphereBivariateSpline(phi_old + pi / 2, lambda_old + pi, Drc.T) dlambda1 = 2 * pi / nlambda_new dphi1 = pi / nphi_new phi1 = np.array( [-((pi / 2) - dphi1 / 2) + (j) * dphi1 for j in range(nphi_new)]) + pi / 2 lambd1 = (np.array([-(pi) + (i) * dlambda1 for i in range(nlambda_new)])) + pi glambd, gphi = np.meshgrid(lambd1, phi1) interp = (lut.ev(gphi.ravel(), glambd.ravel())).reshape(nphi_new, nlambda_new).T return interp, phi1, lambd1, dphi1, dlambda1
def geointerp(lats, lons, data, grid_size_deg, mesh=False): '''We want to interpolate it to a global x-degree grid''' deg2rad = np.pi / 180. new_lats = np.linspace(grid_size_deg, 180, 180 / grid_size_deg) new_lons = np.linspace(grid_size_deg, 360, 360 / grid_size_deg) new_lats_mesh, new_lons_mesh = np.meshgrid(new_lats * deg2rad, new_lons * deg2rad) '''We need to set up the interpolator object''' # lats = [float(lat) for lat in lats] print lats * 2 #*deg2rad lut = RectSphereBivariateSpline(lats * deg2rad, lons * deg2rad, data) '''Finally we interpolate the data. The RectSphereBivariateSpline object only takes 1-D arrays as input, therefore we need to do some reshaping.''' new_lats = new_lats_mesh.ravel() new_lons = new_lons_mesh.ravel() data_interp = lut.ev(new_lats, new_lons) if mesh == True: data_interp = data_interp.reshape( (360 / grid_size_deg, 180 / grid_size_deg)).T return new_lats / deg2rad, new_lons / deg2rad, data_interp
def init_interpo_dec(): """ Generates an object of scipy.interpolate.RectSphereBivariateSpline interpo_dec required by magdec() that interpolates magnetic declination (also called magnetic variation) based on the data table calculated from the NOAA webpage https://www.ngdc.noaa.gov/geomag/calculators/magcalc.shtml#igrfgrid with the following input: Southern most lat: 90 S Northern most lat: 90 N Lat Step Size: 1.0 Western most long: 180 W Eastern most long: 179 E Lon Step Size: 1.0 Elevation: Mean sea level 0 Feet Magnetic component: Declination Model: WMM (2019-2024) Start Date: 2020 09 20 End Date: 2020 09 20 Step size: 1.0 Result format: CSV The grid size can be adjusted but the (1 deg by 1 deg) size should suffice for practical purpose, as long as the the grids cover the entire Earth surface. The interpolation is performed at sea-level, but no significant difference would be noticed up to FL600 or beyond. See docstring of geo.magdec() for more information. Created by : Yaofu Zhou """ declination = pd.read_csv('bluesky/tools/declination_sealevel.csv',\ comment='#',usecols=[1,2,4],header=None, names=['lat','lon','dec']) index_last = len(declination.index) lat_1st = declination.iloc[0, 0] lon_1st = declination.iloc[0, 1] num_lon = (declination.lat == lat_1st).sum() num_lat = (declination.lon == lon_1st).sum() lat_last = declination.iloc[index_last - 1, 0] lon_last = declination.iloc[index_last - 1, 1] lat_x = np.linspace(lat_last, lat_1st, num_lat) * np.pi / 180. lon_y = np.linspace(lon_1st, lon_last, num_lon) * np.pi / 180. distinct_lat = np.unique(declination.lat) dec_xy = np.zeros([1, num_lon]) for val in distinct_lat: dec = np.array(declination.loc[declination.lat == val, 'dec'].values)\ .reshape(1,num_lon) dec_xy = np.vstack((dec_xy, dec)) dec_xy = dec_xy[1:, :] interpo_dec = RectSphereBivariateSpline(lat_x+np.pi/2., lon_y+np.pi,\ dec_xy) return interpo_dec
def geointerp(lats,lons,data,grid_size_deg, mesh=False): '''We want to interpolate it to a global x-degree grid''' deg2rad = np.pi/180. new_lats = np.linspace(grid_size_deg, 180, 180/grid_size_deg) new_lons = np.linspace(grid_size_deg, 360, 360/grid_size_deg) new_lats_mesh, new_lons_mesh = np.meshgrid(new_lats*deg2rad, new_lons*deg2rad) '''We need to set up the interpolator object''' # lats = [float(lat) for lat in lats] print lats*2#*deg2rad lut = RectSphereBivariateSpline(lats*deg2rad, lons*deg2rad, data) '''Finally we interpolate the data. The RectSphereBivariateSpline object only takes 1-D arrays as input, therefore we need to do some reshaping.''' new_lats = new_lats_mesh.ravel() new_lons = new_lons_mesh.ravel() data_interp = lut.ev(new_lats,new_lons) if mesh == True: data_interp = data_interp.reshape((360/grid_size_deg, 180/grid_size_deg)).T return new_lats/deg2rad, new_lons/deg2rad, data_interp
def _loadEGM96(): """load the EGM96 geoid model into a spline object""" #load the data resource file into a string flc = resource_string(__name__, "data/egm96.dac") #setup basic coordinates lon = sp.linspace(0, 2 * sp.pi, 1440, False) lat = sp.linspace(0, sp.pi, 721) #parse the raw data string data = sp.fromstring(flc, sp.dtype(sp.int16).newbyteorder("B"), 1038240).reshape((lat.size, lon.size)) / 100.0 #interpolate the bad boy lut = RectSphereBivariateSpline(lat[1:-1], lon, data[1:-1], pole_values=(sp.mean(data[1]), sp.mean(data[-1]))) return lut
def interpolate2d_sphere(lat, lon, param, radians=True, **kwargs): """ Interpolate on rectangular mesh on sphere Convenience function to call `RectSphereBivariateSpline <http://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.RectSphereBivariateSpline.html>`_ Parameters ---------- lat_rad : array_like 1-D array of latitude coordinates in strictly ascending order. Coordinates must be given in radians, and lie within ``(0, pi)``. lon_rad : array_like 1-D array of longitude coordinates in strictly ascending order. Coordinates must be given in radians and lie within the interval ``(0, 2*pi)``. param : array_like 2-D array of parameter with shape ``(lat_rad.size, lon_rad.size)`` radians : bool, optional Returns ------- `scipy.interpolate.RectSphereBivariateSpline` Spline function to be used for evaluation of interpolation Notes ----- Keyword arguments passed on to `RectSphereBivariateSpline`_. """ d2r = np.pi / 180 if not radians: lat *= d2r lon *= d2r return RectSphereBivariateSpline(lat, lon, param, **kwargs)
class Interpolator2D(): """ Class for interpolating 2D (lat,lon) geospatial data. For irregular grids, the data values must be passed as a 1d array and all three arrays (values, lats, lons) must have the same length. For regular grids, the data values must be passed as a 2d array with shape (M,N) where M and N are the lengths of the latitude and longitude array, respectively. Attributes: values: 1d or 2d numpy array Values to be interpolated lats: 1d numpy array Latitude values lons: 1d numpy array Longitude values origin: tuple(float,float) Reference location (origo of XY coordinate system). method_irreg : {‘linear’, ‘nearest’, ‘cubic’, ‘regularize’}, optional Interpolation method used for irregular grids. Note that 'nearest' is usually significantly faster than the 'linear' and 'cubic'. If the 'regularize' is selected, the data is first mapped onto a regular grid by means of a linear interpolation (for points outside the area covered by the data, a nearest-point interpolation is used). The bin size of the regular grid is specified via the reg_bin argument. bins_irreg_max: int Maximum number of bins along either axis of the regular grid onto which the irregular data is mapped. Only relevant if method_irreg is set to 'regularize'. Default is 2000. """ def __init__(self, values, lats, lons, origin=None, method_irreg='regularize', bins_irreg_max=400): # compute coordinates of origin, if not provided if origin is None: origin = center_point(lats, lons) self.origin = origin # check if bathymetry data are on a regular or irregular grid reggrid = (np.ndim(values) == 2) # convert to radians lats_rad, lons_rad = torad(lats, lons) # necessary to resolve a mismatch between scipy and underlying Fortran code # https://github.com/scipy/scipy/issues/6556 if np.min(lons_rad) < 0: self._lon_corr = np.pi else: self._lon_corr = 0 lons_rad += self._lon_corr # initialize lat-lon interpolator if reggrid: # regular grid if len(lats) > 2 and len(lons) > 2: self.interp_ll = RectSphereBivariateSpline(u=lats_rad, v=lons_rad, r=values) elif len(lats) > 1 and len(lons) > 1: z = np.swapaxes(values, 0, 1) self.interp_ll = interp2d(x=lats_rad, y=lons_rad, z=z, kind='linear') elif len(lats) == 1: self.interp_ll = interp1d(x=lons_rad, y=np.squeeze(values), kind='linear') elif len(lons) == 1: self.interp_ll = interp1d(x=lats_rad, y=np.squeeze(values), kind='linear') else: # irregular grid if len(np.unique(lats)) <= 1 or len(np.unique(lons)) <= 1: self.interp_ll = GridData2D(u=lats_rad, v=lons_rad, r=values, method='nearest') elif method_irreg == 'regularize': # initialize interpolators on irregular grid if len(np.unique(lats)) >= 2 and len(np.unique(lons)) >= 2: method = 'linear' else: method = 'nearest' gd = GridData2D(u=lats_rad, v=lons_rad, r=values, method=method) gd_near = GridData2D(u=lats_rad, v=lons_rad, r=values, method='nearest') # determine bin size for regular grid lat_diffs = np.diff(np.sort(np.unique(lats))) lat_diffs = lat_diffs[lat_diffs > 1e-4] lon_diffs = np.diff(np.sort(np.unique(lons))) lon_diffs = lon_diffs[lon_diffs > 1e-4] bin_size = (np.min(lat_diffs), np.min(lon_diffs)) # regular grid that data will be mapped to lats_reg, lons_reg = self._create_grid(lats=lats, lons=lons, bin_size=bin_size, max_bins=bins_irreg_max) # map to regular grid lats_reg_rad, lons_reg_rad = torad(lats_reg, lons_reg) lons_reg_rad += self._lon_corr vi = gd(theta=lats_reg_rad, phi=lons_reg_rad, grid=True) vi_near = gd_near(theta=lats_reg_rad, phi=lons_reg_rad, grid=True) indices_nan = np.where(np.isnan(vi)) vi[indices_nan] = vi_near[indices_nan] # initialize interpolator on regular grid self.interp_ll = RectSphereBivariateSpline(u=lats_reg_rad, v=lons_reg_rad, r=vi) else: self.interp_ll = GridData2D(u=lats_rad, v=lons_rad, r=values, method=method_irreg) # store data used for interpolation self.lat_nodes = lats self.lon_nodes = lons self.values = values def get_nodes(self): return (self.values, self.lat_nodes, self.lon_nodes) def interp_xy(self, x, y, grid=False, x_deriv_order=0, y_deriv_order=0): """ Interpolate using planar coordinate system (xy). x and y can be floats or arrays. If grid is set to False, the interpolation will be evaluated at the positions (x_i, y_i), where x=(x_1,...,x_N) and y=(y_1,...,y_N). Note that in this case, x and y must have the same length. If grid is set to True, the interpolation will be evaluated at all combinations (x_i, y_j), where x=(x_1,...,x_N) and y=(y_1,...,y_M). Note that in this case, the lengths of x and y do not have to be the same. Args: x: float or array x-coordinate of the positions(s) where the interpolation is to be evaluated y: float or array y-coordinate of the positions(s) where the interpolation is to be evaluated grid: bool Specify how to combine elements of x and y. x_deriv_order: int Order of x-derivative y_deriv_order: int Order of y-derivative Returns: zi: Interpolated interpolation values """ lat, lon = XYtoLL(x=x, y=y, lat_ref=self.origin[0], lon_ref=self.origin[1], grid=grid) if grid: M = lat.shape[0] N = lat.shape[1] lat = np.reshape(lat, newshape=(M * N)) lon = np.reshape(lon, newshape=(M * N)) zi = self.interp(lat=lat, lon=lon, squeeze=False, lat_deriv_order=y_deriv_order, lon_deriv_order=x_deriv_order) if x_deriv_order + y_deriv_order > 0: r = DLDL_over_DXDY(lat=lat, lat_deriv_order=y_deriv_order, lon_deriv_order=x_deriv_order) zi *= r if grid: zi = np.reshape(zi, newshape=(M, N)) if np.ndim(zi) == 2: zi = np.swapaxes(zi, 0, 1) zi = np.squeeze(zi) if np.ndim(zi) == 0 or (np.ndim(zi) == 1 and len(zi) == 1): zi = float(zi) return zi def interp(self, lat, lon, grid=False, squeeze=True, lat_deriv_order=0, lon_deriv_order=0): """ Interpolate using spherical coordinate system (latitude-longitude). lat and lot can be floats or arrays. If grid is set to False, the interpolation will be evaluated at the coordinates (lat_i, lon_i), where lat=(lat_1,...,lat_N) and lon=(lon_1,...,lon_N). Note that in this case, lat and lon must have the same length. If grid is set to True, the interpolation will be evaluated at all combinations (lat_i, lon_j), where lat=(lat_1,...,lat_N) and lon=(lon_1,...,lon_M). Note that in this case, the lengths of lat and lon do not have to be the same. Derivates are given per radians^n, where n is the overall derivative order. Args: lat: float or array latitude of the positions(s) where the interpolation is to be evaluated lon: float or array longitude of the positions(s) where the interpolation is to be evaluated grid: bool Specify how to combine elements of lat and lon. If lat and lon have different lengths, specifying grid has no effect as it is automatically set to True. lat_deriv_order: int Order of latitude-derivative lon_deriv_order: int Order of longitude-derivative Returns: zi: Interpolated values (or derivates) """ lat = np.squeeze(np.array(lat)) lon = np.squeeze(np.array(lon)) lat_rad, lon_rad = torad(lat, lon) lon_rad += self._lon_corr if isinstance(self.interp_ll, interp2d): zi = self.interp_ll.__call__(x=lat_rad, y=lon_rad, dx=lat_deriv_order, dy=lon_deriv_order) if grid: zi = np.swapaxes(zi, 0, 1) if not grid and np.ndim(zi) == 2: zi = np.diagonal(zi) elif isinstance(self.interp_ll, interp1d): if len(self.lat_nodes) > 1: zi = self.interp_ll(x=lat_rad) elif len(self.lon_nodes) > 1: zi = self.interp_ll(x=lon_rad) else: zi = self.interp_ll.__call__(theta=lat_rad, phi=lon_rad, grid=grid, dtheta=lat_deriv_order, dphi=lon_deriv_order) if squeeze: zi = np.squeeze(zi) if np.ndim(zi) == 0 or (np.ndim(zi) == 1 and len(zi) == 1): zi = float(zi) return zi def _create_grid(self, lats, lons, bin_size, max_bins): """ Created regular lat-lon grid with uniform spacing that covers a set of (lat,lon) coordinates. Args: lats: numpy.array Latitude values in degrees lons: numpy.array Longitude values in degrees bin_size: float or tuple(float,float) Lat and long bin size max_bins: int Maximum number of bins along either axis Returns: : numpy.array, numpy.array Latitude and longitude values of the regular grid """ if isinstance(bin_size, (int, float)): bin_size = (bin_size, bin_size) res = [] for v, dv in zip([lats, lons], bin_size): v_min = np.min(v) - dv v_max = np.max(v) + dv num = max(3, int((v_max - v_min) / dv) + 1) num = min(max_bins, num) v_reg = np.linspace(v_min, v_max, num=num) res.append(v_reg) return tuple(res)
def interpol(latitude,longitude,variable): ''' This thing interpolates ''' lut=RSBS(latitude,longitude,variable) interpolated_vairable=lut.ev(latai.ravel(),lonai.ravel()).reshape((361, 576)) return interpolated_vairable
""" This is an example test for scipy RectSphereBivariateSpline interpolator. It is meant to be used for approximation of a rectangular grid on a sphere, e.g., for lat/lon interpolation. """ import numpy as np from scipy.interpolate import RectSphereBivariateSpline import matplotlib.pyplot as plt lats = np.linspace(10, 170, 9) * np.pi / 180. lons = np.linspace(0, 350, 18) * np.pi / 180. data = np.dot(np.atleast_2d(90. - np.linspace(-80., 80., 18)).T, np.atleast_2d(180. - np.abs(np.linspace(0., 350., 9)))).T new_lats = np.linspace(1, 180, 180) * np.pi / 180 new_lons = np.linspace(1, 360, 360) * np.pi / 180 new_lats, new_lons = np.meshgrid(new_lats, new_lons) lut = RectSphereBivariateSpline(lats, lons, data) data_interp = lut.ev(new_lats.ravel(), new_lons.ravel()).reshape((360, 180)).T fig = plt.figure() ax1 = fig.add_subplot(211) ax1.imshow(data, interpolation='nearest') ax2 = fig.add_subplot(212) ax2.imshow(data_interp, interpolation='nearest') plt.show()
lons = np.linspace(0, 350, 18) * np.pi / 180. data = np.dot( np.atleast_2d(90. - np.linspace(-80., 80., 18)).T, np.atleast_2d(180. - np.abs(np.linspace(0., 350., 9)))).T # We want to interpolate it to a global one-degree grid new_lats = np.linspace(1, 180, 180) * np.pi / 180 new_lons = np.linspace(1, 360, 360) * np.pi / 180 new_lats, new_lons = np.meshgrid(new_lats, new_lons) # We need to set up the interpolator object from scipy.interpolate import RectSphereBivariateSpline lut = RectSphereBivariateSpline(lats, lons, data) # Finally we interpolate the data. The `RectSphereBivariateSpline` object # only takes 1-D arrays as input, therefore we need to do some reshaping. data_interp = lut.ev(new_lats.ravel(), new_lons.ravel()).reshape((360, 180)).T # Looking at the original and the interpolated data, one can see that the # interpolant reproduces the original data very well: import matplotlib.pyplot as plt fig = plt.figure() ax1 = fig.add_subplot(211) ax1.imshow(data, interpolation='nearest') ax2 = fig.add_subplot(212)
def create_model(nside, file_name=None): """Takes feko .ffe reference model, converts into healpix and smooths the response :param nside: Healpix nside :param file_name: Path to :samp:`.ffe` feko model of reference tiles :returns: - :class:`tuple` of (beam_response, theta_mesh, phi_mesh, power, theta) """ # Make an empty array for healpix projection len_empty_healpix = hp.nside2npix(nside) beam_response = np.zeros(len_empty_healpix) * np.nan # Had problems with having coords = 0 when interpolating, so make a small number # and call it epsilon for some reason epsilon = 1.0e-12 # Load ffe model data in, which is stored in real and imaginary components # of a phi/theta polaristaion representation of the beam # apparently this is normal to beam engineers data = np.loadtxt(file_name) theta = data[:, 0] re_theta = data[:, 2] im_theta = data[:, 3] re_phi = data[:, 4] im_phi = data[:, 5] # Convert to complex numbers X_theta = re_theta.astype(complex) X_theta.imag = im_theta X_phi = re_phi.astype(complex) X_phi.imag = im_phi # make a coord grid for the data in the .ffe model theta_range = np.linspace(epsilon, 90, 91) phi_range = np.linspace(epsilon, 359.0, 360) theta_mesh, phi_mesh = np.meshgrid(theta_range, phi_range) # Convert reference model into power from the complex values power = 10 * np.log10(abs(X_theta)**2 + abs(X_phi)**2) # Make an inf values super small power[np.where(power == -np.inf)] = -80 # Had a problem with edge effects, leave off near horizon values # Basically remove the last 90 values of power list, where θ == 90 power = power[:-91] # Get things into correct shape and do an interpolation power.shape = phi_mesh.shape # Bivariate spline approximation over a rectangular mesh on a sphere # s is a paramater I had to play with to get by eye nice results # s: positive smoothing factor lut = RectSphereBivariateSpline(theta_range * (np.pi / 180.0), phi_range * (np.pi / 180.0), power.T, s=0.1) # Get the theta and phi of all healpixels i_pix = np.arange(hp.nside2npix(nside)) theta_rad, phi_rad = hp.pix2ang(nside, i_pix) # Evaluate the interpolated function at healpix gridpoints # Use spline to map beam into healpixels beam_response = lut.ev(theta_rad, phi_rad) # rescale beam response to have peak value of 0 dB beam_response = beam_response - np.nanmax(beam_response) return beam_response, theta_mesh, phi_mesh, power, theta
PM1 = fn.variables['DUSMASS25_avg'][:]*1e9 PM2 = fn.variables['SSSMASS25_avg'][:]*1e9 PM3 = fn.variables['SO4SMASS_avg' ][:]*1e9*1.375 PM4 = fn.variables['OCSMASS_avg' ][:]*1e9 PM5 = fn.variables['BCSMASS_avg' ][:]*1e9 PM25 = PM1+PM2+PM3+PM4+PM5 # By Definition from MERRAero fn.close(); del fn clon=-98.5795 # central lat/lon for imaging. Dead-center of US. clat= 39.8282 # ---------------------------------------------------------------- # Interpolates coarse (144x288) 500 hPa data into finer (361x540) # surface grid. lut=RSBS(latp5,lonp5,u5) u5i=lut.ev(lat2.ravel(),lon2.ravel()).reshape((361, 540)) del lut lut=RSBS(latp5,lonp5,v5) v5i=lut.ev(lat2.ravel(),lon2.ravel()).reshape((361, 540)) del lut lut=RSBS(latp5,lonp5,hgt) hgti=lut.ev(lat2.ravel(),lon2.ravel()).reshape((361, 540)); del hgt, u5, v5, lonp5, latp5, lonp2, latp2, lon2, lat2, lon5, lat5 #fig = plt.figure() #ax1 = fig.add_subplot(211) #ax1.imshow(hgt, interpolation='nearest') #ax2 = fig.add_subplot(212) #ax2.imshow(hgti, interpolation='nearest')
def apply_gsrm(sites_of_retain3_pv, ITRF_ID, dir_file_gsrm): # read the lons, lats, and heights width = (5, 3, 10, 25, 4, 3, 5, 4, 3, 5, 8) data_lonlat = np.genfromtxt(ITRF_ID, delimiter=width, dtype=np.str, skip_header=2, skip_footer=1, autostrip=True) n = len(data_lonlat) code_pt = np.core.defchararray.add(data_lonlat[:, 0], data_lonlat[:, 1]) CODE_PT = sites_of_retain3_pv['CODE'] + sites_of_retain3_pv['PT'] _, __, _index = np.intersect1d(CODE_PT, code_pt, return_indices=True) sites_of_retain3_lonlat = data_lonlat[_index] # convert lons and lats of sites in [d,m,s] to lons and colats in radian sites_of_retain3_lons = np.array(sites_of_retain3_lonlat[:, 4:7], dtype=np.float) sites_of_retain3_geod_lats = np.array(sites_of_retain3_lonlat[:, 7:10], dtype=np.float) sites_of_retain3_lons = [tuple(x) for x in sites_of_retain3_lons] sites_of_retain3_geod_lats = [tuple(x) for x in sites_of_retain3_geod_lats] sites_of_retain3_lons_deg = Angle(sites_of_retain3_lons, unit=u.deg) sites_of_retain3_lats_geod_deg = Angle(sites_of_retain3_geod_lats, unit=u.deg) sites_of_retain3_lats_deg = geode2geocen(sites_of_retain3_lats_geod_deg) sites_of_retain3_lons = sites_of_retain3_lons_deg.rad sites_of_retain3_lats = sites_of_retain3_lats_deg.to(u.rad).value df = pd.DataFrame(sites_of_retain3_lonlat[:, :4], columns=['CODE', 'PT', 'DOMES', 'STATION DESCRIPTION']) df['Geod. Lat. °N'] = sites_of_retain3_lats_geod_deg df['Lat. °N'] = sites_of_retain3_lats_deg df['Lon. °E'] = sites_of_retain3_lons_deg df['H'] = sites_of_retain3_lonlat[:, -1] sites_of_retain3_lonlat = df sites_of_retain3_colats = np.pi / 2 - sites_of_retain3_lats # read the GSRMv2.1 data_gsrmv21 = np.loadtxt(dir_file_gsrm) # calculate second invariant strain rate sisr = np.sqrt(data_gsrmv21[:, 8]**2 + data_gsrmv21[:, 9]**2).reshape( 1750, 3600) # flip the lons and lats sisr = np.flip(sisr, 0) sisr[:, :1800], sisr[:, 1800:] = sisr[:, 1800:].copy(), sisr[:, :1800].copy() # lons and lats after flipping lons = np.linspace(0.05, 359.95, 3600) lats = np.linspace(87.45, -87.45, 1750) # colats and lons in radians colats_rad = np.deg2rad(90 - lats) lons_rad = np.deg2rad(lons) lut = RectSphereBivariateSpline(colats_rad, lons_rad, sisr) sisr_sites = lut.ev(sites_of_retain3_colats, sites_of_retain3_lons) sites_of_retain4_lonlat = sites_of_retain3_lonlat[ sisr_sites < 0.1].reset_index(drop=True) CODE = sites_of_retain4_lonlat['CODE'] CODE_pv = sites_of_retain3_pv['CODE'] _, _index, __ = np.intersect1d(CODE_pv, CODE, return_indices=True) sites_of_retain4_pv = sites_of_retain3_pv.loc[_index].reset_index( drop=True) sites_of_retain4 = pd.merge(sites_of_retain4_lonlat, sites_of_retain4_pv, on=['CODE', 'PT'], validate="one_to_one") return sites_of_retain4
def interpolate_RSBS(data, sz, lon1=None, lon2=None, lat1=None, lat2=None, fignum=None): # interpolate using scipy.interpolate.RectSphereBivariateSpline # # this needs some tuning, but it appears to be working and semi-functional # ... but tends to break for complex array. let's try scipy.interp2d(): #http://docs.scipy.org/doc/scipy-0.14.0/reference/generated/scipy.interpolate.interp2d.html # print('data: ', data[0:5]) # lons = sorted(list(set([x for x, y, z in data]))) lats = sorted(list(set([y for x, y, z in data]))) #print('lls: ', len(lats), len(lons)) data = numpy.reshape([rw[2] for rw in data], (len(lats), len(lons))) #print('sh: ', numpy.shape(data)) ##### plt.figure(fignum + 2) plt.clf() ax1 = plt.gca() ax1.imshow(data, interpolation='nearest') ##### # lon1 = (lon1 or min(lons)) lon2 = (lon2 or max(lons)) lat1 = (lat1 or min(lats)) lat2 = (lat2 or max(lats)) # #new_lats = np.linspace(1, 180, 180) * np.pi / 180 #new_lons = np.linspace(1, 360, 360) * np.pi / 180 new_lats = np.linspace(lat1, lat2, sz[0]) * np.pi / 180 new_lons = np.linspace(lon1, lon2, sz[1]) * np.pi / 180 new_lats, new_lons = np.meshgrid(new_lats, new_lons) # We need to set up the interpolator object #from scipy.interpolate import RectSphereBivariateSpline lut = RectSphereBivariateSpline(lats, lons, data) # #lut = RectBivariateSpline(lats, lons, data) print(len(new_lats), len(new_lons[0]), len(data), len(data[0]), new_lats.shape) # Finally we interpolate the data. The `RectSphereBivariateSpline` object # only takes 1-D arrays as input, therefore we need to do some reshaping. data_interp = lut.ev(new_lats.ravel(), new_lons.ravel()).reshape(new_lats.shape).T # Looking at the original and the interpolated data, one can see that the # interpolant reproduces the original data very well: # if fignum != None: fig = plt.figure(fignum + 0) ax1 = fig.add_subplot(211) ax1.imshow(data, interpolation='nearest') ax2 = fig.add_subplot(212) ax2.imshow(data_interp, interpolation='nearest') plt.show() # # Chosing the optimal value of ``s`` can be a delicate task. Recommended # values for ``s`` depend on the accuracy of the data values. If the user # has an idea of the statistical errors on the data, she can also find a # proper estimate for ``s``. By assuming that, if she specifies the # right ``s``, the interpolator will use a spline ``f(u,v)`` which exactly # reproduces the function underlying the data, she can evaluate # ``sum((r(i,j)-s(u(i),v(j)))**2)`` to find a good estimate for this ``s``. # For example, if she knows that the statistical errors on her # ``r(i,j)``-values are not greater than 0.1, she may expect that a good # ``s`` should have a value not larger than ``u.size * v.size * (0.1)**2``. # If nothing is known about the statistical error in ``r(i,j)``, ``s`` must # be determined by trial and error. The best is then to start with a very # large value of ``s`` (to determine the least-squares polynomial and the # corresponding upper bound ``fp0`` for ``s``) and then to progressively # decrease the value of ``s`` (say by a factor 10 in the beginning, i.e. # ``s = fp0 / 10, fp0 / 100, ...`` and more carefully as the approximation # shows more detail) to obtain closer fits. # The interpolation results for different values of ``s`` give some insight # into this process: fig2 = plt.figure(fignum + 1) s = [3e9, 2e9, 1e9, 1e8] for ii in range(len(s)): #lut = RectSphereBivariateSpline(lats, lons, data, s=s[ii]) RectBivariateSpline(lats, lons, data, s=s[ii]) data_interp = lut.ev(new_lats.ravel(), new_lons.ravel()).reshape(new_lons.shape).T ax = fig2.add_subplot(2, 2, ii + 1) ax.imshow(data_interp, interpolation='nearest') ax.set_title("s = %g" % s[ii]) plt.show()
def main(): # Suppose we have global data on a coarse grid import numpy as np lats = np.linspace(10, 170, 9) * np.pi / 180. lons = np.linspace(0, 350, 18) * np.pi / 180. data = np.dot( np.atleast_2d(90. - np.linspace(-80., 80., 18)).T, np.atleast_2d(180. - np.abs(np.linspace(0., 350., 9)))).T # We want to interpolate it to a global one-degree grid new_lats = np.linspace(1, 180, 180) * np.pi / 180 new_lons = np.linspace(1, 360, 360) * np.pi / 180 new_lats, new_lons = np.meshgrid(new_lats, new_lons) # We need to set up the interpolator object from scipy.interpolate import RectSphereBivariateSpline lut = RectSphereBivariateSpline(lats, lons, data) # Finally we interpolate the data. The `RectSphereBivariateSpline` object # only takes 1-D arrays as input, therefore we need to do some reshaping. data_interp = lut.ev(new_lats.ravel(), new_lons.ravel()).reshape( (360, 180)).T # Looking at the original and the interpolated data, one can see that the # interpolant reproduces the original data very well: import matplotlib.pyplot as plt fig = plt.figure() ax1 = fig.add_subplot(211) ax1.imshow(data, interpolation='nearest') ax2 = fig.add_subplot(212) ax2.imshow(data_interp, interpolation='nearest') plt.show() # Chosing the optimal value of ``s`` can be a delicate task. Recommended # values for ``s`` depend on the accuracy of the data values. If the user # has an idea of the statistical errors on the data, she can also find a # proper estimate for ``s``. By assuming that, if she specifies the # right ``s``, the interpolator will use a spline ``f(u,v)`` which exactly # reproduces the function underlying the data, she can evaluate # ``sum((r(i,j)-s(u(i),v(j)))**2)`` to find a good estimate for this ``s``. # For example, if she knows that the statistical errors on her # ``r(i,j)``-values are not greater than 0.1, she may expect that a good # ``s`` should have a value not larger than ``u.size * v.size * (0.1)**2``. # If nothing is known about the statistical error in ``r(i,j)``, ``s`` must # be determined by trial and error. The best is then to start with a very # large value of ``s`` (to determine the least-squares polynomial and the # corresponding upper bound ``fp0`` for ``s``) and then to progressively # decrease the value of ``s`` (say by a factor 10 in the beginning, i.e. # ``s = fp0 / 10, fp0 / 100, ...`` and more carefully as the approximation # shows more detail) to obtain closer fits. # The interpolation results for different values of ``s`` give some insight # into this process: fig2 = plt.figure() s = [3e9, 2e9, 1e9, 1e8] for ii in range(len(s)): lut = RectSphereBivariateSpline(lats, lons, data, s=s[ii]) data_interp = lut.ev(new_lats.ravel(), new_lons.ravel()).reshape( (360, 180)).T ax = fig2.add_subplot(2, 2, ii + 1) ax.imshow(data_interp, interpolation='nearest') ax.set_title("s = %g" % s[ii]) plt.show()
def __init__(self, values, lats, lons, origin=None, method_irreg='regularize', bins_irreg_max=400): # compute coordinates of origin, if not provided if origin is None: origin = center_point(lats, lons) self.origin = origin # check if bathymetry data are on a regular or irregular grid reggrid = (np.ndim(values) == 2) # convert to radians lats_rad, lons_rad = torad(lats, lons) # necessary to resolve a mismatch between scipy and underlying Fortran code # https://github.com/scipy/scipy/issues/6556 if np.min(lons_rad) < 0: self._lon_corr = np.pi else: self._lon_corr = 0 lons_rad += self._lon_corr # initialize lat-lon interpolator if reggrid: # regular grid if len(lats) > 2 and len(lons) > 2: self.interp_ll = RectSphereBivariateSpline(u=lats_rad, v=lons_rad, r=values) elif len(lats) > 1 and len(lons) > 1: z = np.swapaxes(values, 0, 1) self.interp_ll = interp2d(x=lats_rad, y=lons_rad, z=z, kind='linear') elif len(lats) == 1: self.interp_ll = interp1d(x=lons_rad, y=np.squeeze(values), kind='linear') elif len(lons) == 1: self.interp_ll = interp1d(x=lats_rad, y=np.squeeze(values), kind='linear') else: # irregular grid if len(np.unique(lats)) <= 1 or len(np.unique(lons)) <= 1: self.interp_ll = GridData2D(u=lats_rad, v=lons_rad, r=values, method='nearest') elif method_irreg == 'regularize': # initialize interpolators on irregular grid if len(np.unique(lats)) >= 2 and len(np.unique(lons)) >= 2: method = 'linear' else: method = 'nearest' gd = GridData2D(u=lats_rad, v=lons_rad, r=values, method=method) gd_near = GridData2D(u=lats_rad, v=lons_rad, r=values, method='nearest') # determine bin size for regular grid lat_diffs = np.diff(np.sort(np.unique(lats))) lat_diffs = lat_diffs[lat_diffs > 1e-4] lon_diffs = np.diff(np.sort(np.unique(lons))) lon_diffs = lon_diffs[lon_diffs > 1e-4] bin_size = (np.min(lat_diffs), np.min(lon_diffs)) # regular grid that data will be mapped to lats_reg, lons_reg = self._create_grid(lats=lats, lons=lons, bin_size=bin_size, max_bins=bins_irreg_max) # map to regular grid lats_reg_rad, lons_reg_rad = torad(lats_reg, lons_reg) lons_reg_rad += self._lon_corr vi = gd(theta=lats_reg_rad, phi=lons_reg_rad, grid=True) vi_near = gd_near(theta=lats_reg_rad, phi=lons_reg_rad, grid=True) indices_nan = np.where(np.isnan(vi)) vi[indices_nan] = vi_near[indices_nan] # initialize interpolator on regular grid self.interp_ll = RectSphereBivariateSpline(u=lats_reg_rad, v=lons_reg_rad, r=vi) else: self.interp_ll = GridData2D(u=lats_rad, v=lons_rad, r=values, method=method_irreg) # store data used for interpolation self.lat_nodes = lats self.lon_nodes = lons self.values = values
def scatter_along_los(dhf, tau0, P, h, th_look, phi_look, om=1., q=None, full_fov_deg=None, M=20, N=20, R=20, N_int=30, R_int=30, Q_int=30, tol=1e-4, K=20, verbose=True): ''' For given lines of sight, calculate the direct and scattered brightness measurements, for both the in-FoV and out-of-FoV (i.e., stray light) components. dhf: function that takes x,y and returns the column brightness tau0: optical thickness of atmosphere P: scattering phase function P(u0,u1,phi0,phi1) h: height of airglow layer [m] th_look: (array) zenith angle of observation (deg, geophysical convention) phi_look: (array) azimuth angle of observation (deg, geophysical convention) om: single-scattering albedo q: stray light function (None = ignore stray light) full_fov_deg: the field of view in degrees (only used if q is specified) M: number of tau grid points N: number of u grid points R: number of phi grid points N_int: number of u grid points to use in sky integration R_int: number of phi grid points to use in sky integration Q_int: number of u and phi grid points to use in stray light calculation tol: stop iterating when the relative change in the solution is less than this K: maximum iterations verbose: whether to print progress Returns: g_sc: (array) measured brightness from atmospheric scattering g_dir: (array) measured brightness from direct ray s_sc: (array) measured brightness from stray light of scattered signal s_dir: (array) measured brightness from stray light of direct signal ''' Ifull, tau, u_bounds, phi_bounds = white_light_scatter(dhf, tau0, P, h, om=om, M=M, N=N, R=R, N_int=N_int, R_int=R_int, tol=tol, K=K, verbose=verbose) # Precalculate interpolation function ui = (u_bounds[1:] + u_bounds[:-1]) / 2 phii = (phi_bounds[1:] + phi_bounds[:-1]) / 2 thi = arccos(ui)[::-1] ######## VERSION USING ROTATED COORDINATES ############# Ii = Ifull[-1, ::-1, :] # There is a discontinuity at u=0 (th=pi/2), so interpolate over either # the top half or bottom half plane. In practice, we only need the bottom. # Another detail is that we should specify a point at zenith, otherwise # it's extrapolating. zenith_val = np.mean(Ii[0, :]) # Define interpolation for phi in 90 to 270 Iint0 = RectSphereBivariateSpline(thi[:N / 2], phii, Ii[:N / 2, :], pole_values=(zenith_val, None)) # Define interpolation for phi in 270 to 90, using a rotated coordinate system to avoid problems at phi=0 r0 = sum(phii < pi / 2) + 1 # index just after 90 deg r1 = sum(phii < 3 * pi / 2) - 1 # index just before 270 deg phii_rot = np.concatenate((phii[r1:] - 2 * pi, phii[:r0 + 1])) + pi Ii_rot = np.concatenate((Ii[:, r1:], Ii[:, :r0 + 1]), axis=1) Iint1 = RectSphereBivariateSpline(thi[:N / 2], phii_rot, Ii_rot[:N / 2, :], pole_values=(zenith_val, None)) def I_sc(u, phi): ''' Given a calculated, discretized, scattered intensity (Ifull), evaluate it at the given u, phi. Use linear interpolation on a sphere. Global variables: Iint0, Iint1 ''' if phi > np.pi / 2 and phi < 3 * np.pi / 2: return Iint0(arccos(u), phi).item() else: phi_rot = np.mod(phi + pi, 2 * pi) return Iint1(arccos(u), phi_rot).item() def I_dir(dhf, tau0, u, phi): ''' Evaluate the direct intensity. Global variables: RE,h ''' if u <= 0.0: return 0.0 # no incident light looking downwards th = arccos(u) gamma = arcsin(RE / (RE + h) * sqrt(1 - u**2)) rho = (th - gamma) * (RE + h) x = -rho * cos(phi) y = -rho * sin(phi) return dhf(x, y) * sec(gamma) * exp(-tau0 / u) # Determine correct u,phi (in math coordinates, incoming ray convention) to use # for direct signal g_sc = [] g_dir = [] for i in range(len(th_look)): u_dir = cos(th_look[i] * pi / 180) phi_dir = mod(pi / 2 - phi_look[i] * pi / 180 + pi, 2 * pi) g_sc.append(I_sc(u_dir, phi_dir)) g_dir.append(I_dir(dhf, tau0, u_dir, phi_dir)) g_sc = array(g_sc) g_dir = array(g_dir) # Stray light calculation, if desired: s_sc = zeros(len(g_sc)) s_dir = zeros(len(g_dir)) if q: # Grid points for stray light integral: if full_fov_deg is None: raise Exception( 'If "q" is specified, "full_fov_deg" needs to be specified') dS = 2 * pi * (1 - cos(full_fov_deg / 2 * pi / 180)) # solid angle th_bound_vec = linspace(pi / 2, full_fov_deg / 2 * pi / 180, Q_int + 1) # don't include FoV u_bound_vec = cos(th_bound_vec) u_vec = (u_bound_vec[1:] + u_bound_vec[:-1]) / 2 du_vec = u_bound_vec[1:] - u_bound_vec[:-1] phi_bound_vec = linspace(0, 2 * pi, Q_int + 1) phi_vec = (phi_bound_vec[1:] + phi_bound_vec[:-1]) / 2 dphi_vec = phi_bound_vec[1:] - phi_bound_vec[:-1] for looki in range(len(th_look)): # Collect integral term at each grid point d_gsc_stray = zeros( (Q_int, Q_int) ) # terms of integral at each grid point (see notes 2016-03-28) d_gdir_stray = zeros( (Q_int, Q_int) ) # terms of integral at each grid point (see notes 2016-03-28) for i in range(Q_int): for j in range(Q_int): th = arccos(u_vec[i]) ph = phi_vec[j] # th (rad) is the angle of the incoming ray relative to -z # ph (rad) is the (math-convention) azimuth angle of the incoming ray # th_look (deg) is the angle of the look direction relative to +z # phi_look (deg) is the (geophysical-convention) azimuth angle of the look direction th_look_rad = th_look[looki] * pi / 180 phi_look_rad = phi_look[looki] * pi / 180 # unit vector (x,y,z)=(E,N,U) of look direction look = [ sin(th_look_rad) * sin(phi_look_rad), sin(th_look_rad) * cos(phi_look_rad), cos(th_look_rad) ] # unit vector (x,y,z)=(E,N,U) of direction to source src = [-sin(th) * cos(ph), -sin(th) * sin(ph), cos(th)] angle_between = arccos(np.dot(look, src)) d_gsc_stray[i,j] = u_vec[i] * I_sc(u_vec[i],phi_vec[j]) * q(angle_between) * \ du_vec[i] * dphi_vec[j] d_gdir_stray[i,j] = u_vec[i] * I_dir(dhf,tau0, u_vec[i],phi_vec[j]) * q(angle_between) * \ du_vec[i] * dphi_vec[j] # Add these stray components to the previously-calculated in-FoV component, of both # the direct and atmosphere-scattered signal. # Determine correct u,phi (in math coordinates, incoming ray convention) to use # for direct signal. # Technically I should scale the in-FoV component by dS, but that's the same as # scaling the stray component by 1/dS. (For backwards compatibility). u_dir = cos(th_look[looki] * pi / 180) phi_dir = mod(pi / 2 - phi_look[looki] * pi / 180 + pi, 360) s_sc[looki] = 1 / dS * sum(d_gsc_stray) s_dir[looki] = 1 / dS * sum(d_gdir_stray) return g_sc, g_dir, s_sc, s_dir
def _calculate_spatial(self): """ Pre-calculate the relative Zodiacal Light brightness variation with sky position """ # Normalise scaling factor to a value of 1.0 at the NEP zl_scale = ZodiacalLight.zl_scale / 77 # Expand range of angles to cover the full sphere by using symmetry beta = np.array( np.concatenate((-np.flipud(ZodiacalLight.beta)[:-1], ZodiacalLight.beta))) * u.degree llsun = np.array( np.concatenate( (ZodiacalLight.llsun, 360 - np.flipud(ZodiacalLight.llsun)[1:-1]))) * u.degree zl_scale = np.concatenate((np.flipud(zl_scale)[:-1], zl_scale)) zl_scale = np.concatenate((zl_scale, np.fliplr(zl_scale)[:, 1:-1]), axis=1) # Convert angles to radians within the required ranges. beta = beta.to(u.radian).value + np.pi / 2 llsun = llsun.to(u.radian).value # For initial cartesian interpolation want the hole in the middle of the data, # i.e. want to remap longitudes onto -180 to 180, not 0 to 360 degrees. # Only want the region of closely spaced data point near to the Sun. beta_c = beta[3:-3] nl = len(llsun) llsun_c = np.concatenate( (llsun[nl // 2 + 9:] - 2 * np.pi, llsun[:nl // 2 - 8])) zl_scale_c = np.concatenate( (zl_scale[3:-3, nl // 2 + 9:], zl_scale[3:-3, :nl // 2 - 8]), axis=1) # Convert everthing to 1D arrays (lists) of x, y and z coordinates. llsuns, betas = np.meshgrid(llsun_c, beta_c) llsuns_c = llsuns.ravel() betas_c = betas.ravel() zl_scale_cflat = zl_scale_c.ravel() # Indices of the non-NaN points good = np.where(np.isfinite(zl_scale_cflat)) # 2D cartesian interpolation function zl_scale_c_interp = SmoothBivariateSpline(betas_c[good], llsuns_c[good], zl_scale_cflat[good]) # Calculate interpolated values zl_scale_fill = zl_scale_c_interp(beta_c, llsun_c) # Remap the interpolated values back to original ranges of coordinates zl_patch = np.zeros(zl_scale.shape) zl_patch[3:16, 0:10] = zl_scale_fill[:, 9:] zl_patch[3:16, -9:] = zl_scale_fill[:, :9] # Fill the hole in the original data with values from the cartesian interpolation zl_patched = np.where(np.isfinite(zl_scale), zl_scale, zl_patch) # Spherical interpolation function from the full, filled data set self._spatial = RectSphereBivariateSpline(beta, llsun, zl_patched, \ pole_continuity=(False,False), pole_values=(1.0, 1.0), \ pole_exact=True, pole_flat=False)
def regular_gldas(lats,lons,grids): ''' Normalize the GLDAS grid data to meet the requirements of spherical harmonic expansion with pyshtools based on the sampling theorem of Driscoll and Healy (1994). The normalized grid data has the following characteristics: (1) the first latitudinal band corresponds to 90 N, and the latitudinal band for 90 S is not included, and the latitudinal sampling interval is 180/n degrees. (2) the first longitudinal band is 0 E, and the longitude band for 360 E is not included, and the longitudinal sampling interval is 360/n for an equally spaced grid. Usage: gldas_new = regular_gldas(lats,lons,grids) Inputs: lats -> [float array] latitudes of gldas grid data lons -> [float array] longitudes of gldas grid data grids -> [float 2d array] gldas grids data Parameters: Outputs: gldas_new -> float 2d array] normalized grids data ''' # Extend the spatial extent of the GLDAS grid to a grid with a shape of n x 2n n = int(180/(lats[1] - lats[0])) lats_new = np.linspace(lats.max(),-lats.max(),n) lons_new = np.linspace(180-lons.max(),180+lons.max(),2*n) nlats,nlons = len(lats),len(lons) nlats_new,nlons_new = len(lats_new),len(lons_new) grids_new = [] for grid in grids: grid_new = np.zeros((nlats_new,nlons_new)) # Flip the grid data along latitude grid_new[:nlats,:] = np.flip(grid,0) # Swap the grid data along the longitude direction to make the longitude range from [-180W ~ 180E] to [0E ~ 360E] grid_new[:,:nlats_new],grid_new[:,nlats_new:] = grid_new[:,nlats_new:].copy(),grid_new[:,:nlats_new].copy() grids_new.append(grid_new) grids_new = np.array(grids_new) grids_new[np.isnan(grids_new)] = 0 trunc = np.abs(grids_new[grids_new != 0]).min()/2 lats_interp = lats_new + lons_new.min() lons_interp = lons_new - lons_new.min() grids_interp = [] # colatitude and longitude before interpolation colats_rad = np.deg2rad(90 - lats_new) lons_rad = np.deg2rad(lons_new) # colatitude and longitude after interpolation colats_rad_interp = np.deg2rad(90 - lats_interp) lons_rad_interp = np.deg2rad(lons_interp) lons_rad_interp, colats_rad_interp = np.meshgrid(lons_rad_interp, colats_rad_interp) # grid data after interpolation for grid_new in grids_new: lut = RectSphereBivariateSpline(colats_rad,lons_rad,grid_new) grid_interp = lut.ev(colats_rad_interp,lons_rad_interp).reshape(nlats_new,nlons_new) grids_interp.append(grid_interp) grids_interp = np.array(grids_interp) grids_interp[np.abs(grids_interp) < trunc] = 0 return lats_interp,lons_interp,grids_interp