Exemple #1
0
def _regrid(lons, lats, data, olons, olats, shuffle=False, tri=None):
    """
    performs triangulation and linear interpolation.

    lons,lats: 1d arrays of mesh lat/lon values in radians.
    data: 1d array of mesh data values.
    olons, olats: 1d arrays describing 2d output mesh (degrees)
    shuffle : if True, randomly shuffle mesh points
    tri : if not None, use existing triangulation.

    returns 2d data array on regular lat/lon mesh"""
    if tri is None:
        if shuffle:
            # randomly shuffle points (may speed up triangulation)
            ix = np.arange(len(lons))
            np.random.shuffle(ix)
            lons = lons[ix]
            lats = lats[ix]
            data = data[ix]
        t1 = time.clock()
        print 'triangulation of', len(lons), ' points'
        tri = trmesh(lons, lats)
        if shuffle:
            tri._shuffle = shuffle
            tri._ix = ix
        print 'triangulation took', time.clock() - t1, ' secs'
    olons = np.radians(olons)
    olats = np.radians(olats)
    olons, olats = np.meshgrid(olons, olats)
    t1 = time.clock()
    latlon_data = tri.interp_linear(olons, olats, data)
    print 'interpolation took', time.clock() - t1, ' secs'
    print 'min/max field:', latlon_data.min(), latlon_data.max()
    return latlon_data, tri
Exemple #2
0
#mesh_filename = 'x1.2621442.grid.nc' # 15 km mesh
mesh_nc = Dataset(mesh_filename)
lats = mesh_nc.variables['latCell'][:]
print('min/max lats:',lats.min(), lats.max())
lons = mesh_nc.variables['lonCell'][:]
print('min/max lons:',lons.min(), lons.max())

# fake test data.
def test_func(lon, lat):
    nexp = 8
    return np.cos(nexp*lon)*np.sin(0.5*lon)**nexp*np.cos(lat)**nexp+np.sin(lat)**nexp
icos_data = test_func(lons,lats)

t1 = time.clock()
print('triangulation of', len(lons),' points')
tri = trmesh(lons, lats)
print('triangulation took',time.clock()-t1,' secs')

nlons = 360; nlats = nlons/2 # 1 degree output mesh
delta = 360./nlons
olons = delta*np.arange(nlons)
olats = -90.0 + 0.5*delta + delta*np.arange(nlats)
olons = np.radians(olons)
olats = np.radians(olats)
olons, olats = np.meshgrid(olons, olats)

t1 = time.clock()
order = 1 # can be 0 (nearest neighbor) or 1 (linear)
latlon_data = tri.interp(olons,olats,icos_data,order=order)
print('interpolation took',time.clock()-t1,' secs')
Exemple #3
0
        0.5 * lon)**nexp * np.cos(lat)**nexp + np.sin(lat)**nexp


npts = 100000
lats, lons = fibonacci_pts(npts)
shuffle = True  # shuffling the points speeds up the triangulation
if shuffle:
    ix = np.arange(npts)
    np.random.shuffle(ix)
    lats = lats[ix]
    lons = lons[ix]
icos_data = test_func(lons, lats)

t1 = time.clock()
print('triangulation of', len(lons), ' points')
tri = trmesh(lons, lats)
print('triangulation took', time.clock() - t1, ' secs')

nlons = 1440
nlats = nlons / 2 + 1  # 1 degree output mesh
delta = 360. / nlons
olons = delta * np.arange(nlons)
olats = -90.0 + delta * np.arange(nlats)
olons = np.radians(olons)
olats = np.radians(olats)
olons, olats = np.meshgrid(olons, olats)

t1 = time.clock()
order = 1  # can be 0 (nearest neighbor) or 1 (linear)
latlon_data = tri.interp(olons, olats, icos_data, order=order)
print('interpolation took', time.clock() - t1, ' secs')
    def add_dem_3D(self, x, y, dem, z0=0., z1=np.infty, zref=None,
                   khorizontal=3, s=None, mode='cartesian'):
        '''
        Add topography by vertically stretching the domain in the region [z0,
        z1] - points below z0 and above z1 are kept fixed, points in between
        are linearly interpolated.

        Usage: first call add_dem_3D for each boundary that is to be perturbed
            and finally call apply_dem to add the perturbation to the mesh
            coordinates.

        The DEM can be either structured or unstructured, this is determined
        from the shapes of x and y.

        See scipy.interpolate.RectBivariateSpline for structured Spline
        interpolation and scipy.interpolate.LinearNDInterpolator
        (khorizontal=1) or scipy.interpolate.CloughTocher2DInterpolator
        (khorizontal=3) for more information on the unstructured interpolation.

        :param x: x coordinates of the DEM (colatitude in spherical modes in
            radians)
        :type x: numpy array, either 1D for structured data or 2D for
            unstructured data
        :param y: y coordinates of the DEM (longitude in spherical modes in
            radians)
        :type y: numpy array, either 1D for structured data or 2D for
            unstructured data
        :param dem: the DEM
        :type dem: numpy array
        :param z0: vertical coordinate, at which the stretching begins
        :type z0: float
        :param z1: vertical coordinate, at which the stretching ends, can be
            np.infty to just move all points above zref with the DEM
        :type z1: float
        :param zref: vertical coordinate, at which the stretching ends
        :type zref: float
        :param khorizontal: horizontal degree of the spline interpolation. For
            unstructured data either 1 or 3.
        :type khorizontal: integer
        :param s: smoothing factor
        :type s: float
        '''

        if not self.ndim == 3:  # pragma: no cover
            raise ValueError('apply_dem_3D works on 3D meshes only')

        if mode not in ['cartesian', 'spherical', 'spherical_full']:  # pragma: no cover  # NoQa
            raise ValueError("mode should be in ['cartesian', 'spherical', "
                             "'spherical_full']")

        structured = ((x.size, y.size) == dem.shape)

        # setup callable interpolation function
        if mode in ['cartesian', 'spherical']:
            # RectBivariateSpline is much faster for regular grids, so we use
            # it here.
            if structured:
                sbs = RectBivariateSpline(x, y, dem, kx=khorizontal,
                                          ky=khorizontal, s=s).ev
            else:
                if khorizontal == 1:
                    sbs = LinearNDInterpolator(np.c_[x.flatten(), y.flatten()],
                                               dem.flatten(), fill_value=0.)
                elif khorizontal == 3:
                    sbs = CloughTocher2DInterpolator(
                        np.c_[x.flatten(), y.flatten()], dem.flatten(),
                        fill_value=0.)
                else:  # pragma: no cover
                    raise ValueError('For unstructured data, only linear and '
                                     'cubic interpolation is supported.')

        elif mode == 'spherical_full':
            # full sphere interpolation functions need to be used in this case
            if structured:
                theta = x.copy()
                eps = 1e-10
                theta[theta < eps] = eps
                theta[theta > np.pi - eps] = np.pi - eps
                phi = y.copy()
                phi[phi < eps] = eps
                phi[phi > 2 * np.pi - eps] = 2 * np.pi - eps
                sbs = RectSphereBivariateSpline(theta, phi, dem, s=s).ev
            else:
                # a variant using SmoothSphereBivariateSpline

                # Disadvantage: smoothing seems to be really sensitive to the
                # absolute values and for s=0 some error appears, so trying
                # some default that seems to work:

                # if s is None:
                #     s = dem.max() * 1e-4
                # sbs = SmoothSphereBivariateSpline(x, y, dem, s=s).ev

                # a variant using LSQSphereBivariateSpline
                # Disadvantage: a regular grid has to be chosen manually, not
                # allways stable

                # eps = 1e-10
                # sbs = LSQSphereBivariateSpline(
                #     x, y, dem, np.linspace(eps, np.pi - eps, 30),
                #     np.linspace(eps, 2 * np.pi - eps, 60)).ev

                # a variant using stripack
                # Disadvantage: external dependency. Fails for regular grid
                # data and order 3. Some problems for regular grid data and
                # order 1 close to the axis.

                from stripack import trmesh

                # shuffle data to ensure it also works with structured data and
                # speed up the triangulation
                ix = np.arange(len(x))
                np.random.shuffle(ix)
                tri = trmesh(y[ix], np.pi / 2. - x[ix])

                # adapt the interface to the other interpolation functions
                def sbs(theta, phi, eps=1e-10):
                    lat = np.pi / 2. - theta

                    # can't query with an empty array
                    if len(theta) > 0:
                        return tri.interp(phi, lat, dem[ix], order=khorizontal)

        # add to topography
        if mode == 'cartesian':
            if self.topography is None:
                self.topography = np.zeros_like(self.points[:, -1])

            if zref is None:
                zref = self.points[:, 2].max()

            # manually vectorized linear interpolation
            sl1 = np.logical_and(self.points[:, 2] > zref,
                                 self.points[:, 2] <= z1)
            sl2 = np.logical_and(self.points[:, 2] <= zref,
                                 self.points[:, 2] > z0)

            dz1 = sbs(self.points[sl1, 0], self.points[sl1, 1])
            if z1 < np.infty:
                z2 = self.points[sl1, 2]
                self.topography[sl1] += dz1 * ((z1 - z2) / (z1 - zref))
            else:
                self.topography[sl1] += dz1

            dz2 = sbs(self.points[sl2, 0], self.points[sl2, 1])
            z2 = self.points[sl2, 2]
            self.topography[sl2] += dz2 * ((z2 - z0) / (zref - z0))

        elif mode in ['spherical', 'spherical_full']:
            if self.topography is None:
                self.topography = np.zeros_like(self.points)

            dr = np.zeros_like(self.points[:, -1])

            r = np.sqrt((self.points ** 2).sum(axis=1))
            th = np.zeros_like(r)
            sl = r > 0
            th[sl] = np.arccos(self.points[sl, 2] / r[sl])
            ph = np.arctan2(self.points[:, 1], self.points[:, 0])
            ph[ph < 0] += 2 * np.pi

            if zref is None:  # pragma: no cover
                zref = r.max()

            # manually vectorized linear interpolation
            sl1 = np.logical_and(r > zref, r <= z1)
            sl2 = np.logical_and(r <= zref, r > z0)

            dz1 = sbs(th[sl1], ph[sl1])
            if z1 < np.infty:
                z2 = r[sl1]
                dr[sl1] = dz1 * ((z1 - z2) / (z1 - zref))
            else:
                dr[sl1] = dz1

            dz2 = sbs(th[sl2], ph[sl2])
            z2 = r[sl2]
            dr[sl2] = dz2 * ((z2 - z0) / (zref - z0))

            sl = np.logical_or(sl1, sl2)

            self.topography[sl, 0] += dr[sl] * np.sin(th[sl]) * np.cos(ph[sl])
            self.topography[sl, 1] += dr[sl] * np.sin(th[sl]) * np.sin(ph[sl])
            self.topography[sl, 2] += dr[sl] * np.cos(th[sl])
Exemple #5
0
    def boundary(self, nodes, geodesic=True, gap=0.0):
        """
        Return a list of ordered lists of nodes on the connected parts of the
        boundary of a subset of nodes and a list of ordered lists of (lat,lon)
        coordinates of the corresponding polygons

        * EXPERIMENTAL! *
        """
        #  Optional import for this experimental method
        try:
            import stripack  # @UnresolvedImport
            # tries to import stripack.so which must have been compiled with
            # f2py -c -m stripack stripack.f90
        except ImportError:
            raise RuntimeError("NOTE: stripack.so not available, boundary() \
                               won't work.")

        N = self.N
        nodes_set = set(nodes)
        if len(nodes_set) >= N:
            return [], [], [], [(0.0, 0.0)]
        # find grid neighbours:
        if geodesic:
            if self.cartesian is not None:
                pos = self.cartesian
            else:
                # find cartesian coordinates of nodes,
                # assuming a perfect unit radius sphere:
                lat = self.grid.lat_sequence() * np.pi / 180
                lon = self.grid.lon_sequence() * np.pi / 180
                pos = self.cartesian = np.zeros((N, 3))
                coslat = np.cos(lat)
                self.cartesian[:, 0] = coslat * np.sin(lon)
                self.cartesian[:, 1] = coslat * np.cos(lon)
                self.cartesian[:, 2] = np.sin(lat)

                # find neighbours of each node in Delaunay triangulation,
                # sorted in counter-clockwise order, using stripack fortran
                # library:
                #  will contain 1-based node indices
                list_ = np.zeros(6 * (N - 2)).astype("int32")
                #  will contain 1-based list_ indices
                lptr = np.zeros(6 * (N - 2)).astype("int32")
                #  will contain 1-based list_ indices
                lend = np.zeros(N).astype("int32")
                lnew = 0
                near = np.zeros(N).astype("int32")
                foll = np.zeros(N).astype("int32")
                dist = np.zeros(N)
                ier = 0
                stripack.trmesh(
                    self.cartesian[:, 0],
                    self.cartesian[:, 1],
                    self.cartesian[:, 2],
                    list_,
                    lptr,
                    lend,
                    lnew,  # output vars
                    near,
                    foll,
                    dist,
                    ier)  # output var
                self.grid_neighbours = [None for i in range(N)]
                self.grid_neighbours_set = [None for i in range(N)]
                rN = range(N)
                for i in rN:
                    nbsi = []
                    ptr0 = ptr = lend[i] - 1
                    for j in rN:
                        nbsi.append(list_[ptr] - 1)
                        ptr = lptr[ptr] - 1
                        if ptr == ptr0:
                            break
                    self.grid_neighbours[i] = nbsi
                    self.grid_neighbours_set[i] = set(nbsi)
        else:
            raise NotImplementedError("Not yet implemented for \
                                      lat-lon-regular grids!")

        remaining = nodes_set.copy()
        boundary = []
        shape = []
        fullshape = []
        representative = []
        # find a node on the boundary and an outer neighbour:
        lam = 0.5 + gap / 2
        lam1 = 1 - lam
        while remaining:
            i = list(remaining)[0]
            this_remove = [i]
            cont = False
            while self.grid_neighbours_set[i] <= nodes_set:
                i = self.grid_neighbours[i][int(
                    np.floor(len(self.grid_neighbours[i]) * random.uniform()))]
                if i not in remaining:  # we had this earlier
                    cont = True
                    break
                this_remove.append(i)
            remaining -= set(this_remove)
            # if len(nodes_set)==151: print(i,this_remove,remaining,cont)
            if cont:
                continue
            o = list(self.grid_neighbours_set[i] - nodes_set)[0]

            # traverse boundary:
            partial_boundary = [i]
            partial_shape = [lam * pos[i] + lam1 * pos[o]]
            partial_fullshape = [0.49 * pos[i] + 0.51 * pos[o]]
            print(partial_shape)
            steps = [(i, o)]
            for it in range(N):  # at most this many steps we need
                nbi = self.grid_neighbours[i]
                j = nbi[0]
                try:
                    j = nbi[(nbi.index(o) - 1) % len(nbi)]
                except IndexError:
                    print("O!", i, o, j, nbi, self.grid_neighbours[o], steps)
                    raise
                if j in nodes_set:
                    i = j
                    partial_boundary.append(i)
                    try:
                        remaining.remove(i)
                    except KeyError:
                        pass
                else:
                    partial_fullshape.append(0.32 * pos[i] + 0.34 * pos[o] +
                                             0.34 * pos[j])
                    o = j
                partial_shape.append(lam * pos[i] + lam1 * pos[o])
                partial_fullshape.append(0.49 * pos[i] + 0.51 * pos[o])
                if (i, o) in steps:
                    break
                steps.append((i, o))

            mind2 = np.inf
            latlon_shape = []
            latlon_fullshape = []
            length = len(partial_shape) - 1
            off = length / 2
            for it in range(length):
                pos1 = partial_shape[it]
                pos2 = partial_shape[int((it + off) % length)]
                latlon_shape.append(self.cartesian2latlon(pos1))
                d2 = ((pos2 - pos1)**2).sum()
                if d2 < mind2:
                    rep = self.cartesian2latlon((pos1 + pos2) / 2)
                    mind2 = d2
            latlon_shape.append(self.cartesian2latlon(partial_shape[-1]))
            for it, _ in enumerate(partial_fullshape):
                pos1 = partial_fullshape[it]
                latlon_fullshape.append(self.cartesian2latlon(pos1))

            boundary.append(partial_boundary)
            shape.append(latlon_shape)
            fullshape.append(latlon_fullshape)
            representative.append(rep)

        # TODO: sort sub-regions by descending size!
        return boundary, shape, fullshape, representative