コード例 #1
0
def test_interpolation_grids_are_what_they_should_be():
    # load data and initialize interpolator
    path = path_to_assets + '/bornholm.mat'
    bathy, lat, lon = load_data_from_file(path)
    ip = Interpolator2D(bathy, lat, lon)
    lat_c = 0.5 * (lat[0] + lat[-1])
    lon_c = 0.5 * (lon[0] + lon[-1])
    assert lat_c, lon_c == center_point(lat, lon)
コード例 #2
0
def test_interpolation_tables_agree_anywhere():
    # load data and initialize interpolator
    path = path_to_assets + '/bornholm.mat'
    bathy, lat, lon = load_data_from_file(path)
    ip = Interpolator2D(bathy, lat, lon)

    # --- at origo ---
    lat_c, lon_c = center_point(lat, lon)
    #lat_c = ip.origin.latitude
    #lon_c = ip.origin.longitude
    z_ll = ip.interp(lat=lat_c, lon=lon_c)  # interpolate using lat-lon
    z_ll = float(z_ll)
    z_xy = ip.interp_xy(x=0, y=0)  # interpolate using x-y
    z_xy = float(z_xy)
    assert z_ll == pytest.approx(z_xy, rel=1e-3) or z_xy == pytest.approx(
        z_ll, abs=0.1)

    # --- 0.1 degrees north of origo ---
    lat = lat_c + 0.1
    lon = lon_c
    x, y = LLtoXY(lat=lat, lon=lon, lat_ref=lat_c, lon_ref=lon_c)
    z_ll = ip.interp(lat=lat, lon=lon)
    z_ll = float(z_ll)
    z_xy = ip.interp_xy(x=x, y=y)
    z_xy = float(z_xy)
    assert z_ll == pytest.approx(z_xy, rel=1e-3) or z_xy == pytest.approx(
        z_ll, abs=0.1)

    # --- 0.08 degrees south of origo ---
    lat = lat_c - 0.08
    lon = lon_c
    x, y = LLtoXY(lat=lat, lon=lon, lat_ref=lat_c, lon_ref=lon_c)
    z_ll = ip.interp(lat=lat, lon=lon)
    z_ll = float(z_ll)
    z_xy = ip.interp_xy(x=x, y=y)
    z_xy = float(z_xy)
    assert z_ll == pytest.approx(z_xy, rel=1e-3) or z_xy == pytest.approx(
        z_ll, abs=0.1)

    # --- at shifted origo ---
    bathy, lat, lon = load_data_from_file(path)
    ip = Interpolator2D(bathy, lat, lon, origin=(55.30, 15.10))
    lat_c = ip.origin[0]
    lon_c = ip.origin[1]
    z_ll = ip.interp(lat=lat_c, lon=lon_c)  # interpolate using lat-lon
    z_ll = float(z_ll)
    z_xy = ip.interp_xy(x=0, y=0)  # interpolate using x-y
    z_xy = float(z_xy)
    assert z_ll == pytest.approx(z_xy, rel=1e-3) or z_xy == pytest.approx(
        z_ll, abs=0.1)
コード例 #3
0
ファイル: ocean.py プロジェクト: matt24smith/kadlu
    def __init__(self,
                 load_bathymetry=0,
                 load_temp=0,
                 load_salinity=0,
                 load_wavedir=0,
                 load_waveheight=0,
                 load_waveperiod=0,
                 load_wind_uv=0,
                 load_wind_u=0,
                 load_wind_v=0,
                 load_water_uv=0,
                 load_water_u=0,
                 load_water_v=0,
                 fetch=4,
                 **kwargs):

        for kw in [
                k for k in ('south', 'west', 'north', 'east', 'top', 'bottom',
                            'start', 'end') if k not in kwargs.keys()
        ]:
            kwargs[kw] = default_val[kw]

        data = {}
        callbacks = []
        vartypes = [
            'bathy',
            'temp',
            'salinity',
            'wavedir',
            'waveheight',
            'waveperiod',
            'wind_uv',
            'wind_u',
            'wind_v',
            'water_uv',
            'water_u',
            'water_v',
        ]
        load_args = [
            load_bathymetry,
            load_temp,
            load_salinity,
            load_wavedir,
            load_waveheight,
            load_waveperiod,
            load_wind_uv,
            load_wind_u,
            load_wind_v,
            load_water_uv,
            load_water_u,
            load_water_v,
        ]

        # if load_args are not callable, convert it to a callable function
        for v, load_arg, ix in zip(vartypes, load_args, range(len(vartypes))):
            if callable(load_arg): callbacks.append(load_arg)

            elif isinstance(load_arg, str):
                key = f'{v}_{load_arg.lower()}'
                assert key in load_map.keys(
                ), f'no map for {key} in\n{load_map=}'
                callbacks.append(load_map[key])
                if fetch is not False:
                    fetch_handler(v,
                                  load_arg.lower(),
                                  parallel=fetch,
                                  **kwargs)

            elif isinstance(load_arg, (int, float)):
                data[f'{v}_val'] = load_arg
                data[f'{v}_lat'] = kwargs['south']
                data[f'{v}_lon'] = kwargs['west']
                data[f'{v}_time'] = dt_2_epoch(kwargs['start'])
                if v in var3d: data[f'{v}_depth'] = kwargs['top']
                callbacks.append(load_callback)

            elif isinstance(load_arg, (list, tuple, np.ndarray)):
                if len(load_arg) not in (3, 4):
                    raise ValueError(
                        f'invalid array shape for load_{v}. '
                        'arrays must be ordered by [val, lat, lon] for 2D data, or '
                        '[val, lat, lon, depth] for 3D data')
                data[f'{v}_val'] = load_arg[0]
                data[f'{v}_lat'] = load_arg[1]
                data[f'{v}_lon'] = load_arg[2]
                if len(load_arg) == 4: data[f'{v}_depth'] = load_arg[3]
                callbacks.append(load_callback)

            else:
                raise TypeError(
                    f'invalid type for load_{v}. '
                    'valid types include string, float, array, and callable')

        q = Queue()

        # prepare data pipeline
        pipe = zip(callbacks, vartypes)
        is_3D = [v in var3d for v in vartypes]
        is_arr = [not isinstance(arg, (int, float)) for arg in load_args]
        columns = [fcn(v=v, data=data, **kwargs) for fcn, v in pipe]
        intrpmap = [(Uniform2D, Uniform3D), (Interpolator2D, Interpolator3D)]
        reshapers = [reshape_3D if v else reshape_2D for v in is_3D]
        # map interpolations to dictionary
        self.interps = {}
        interpolators = map(lambda x, y: intrpmap[x][y], is_arr, is_3D)
        interpolations = map(lambda i, r, c, v, q=q: Process(
            target=worker, args=(i, r, c, v, q)),
                             interpolators,
                             reshapers,
                             columns,
                             vartypes)

        # assert that no empty arrays were returned by load function
        for col, var in zip(columns, vartypes):
            if isinstance(col, dict) or isinstance(col[0], (int, float)):
                continue
            assert len(col[0]) > 0, (
                f'no data found for {var} in region {fmt_coords(kwargs)}. '
                f'consider expanding the region')

        # compute interpolations in parallel and store in dict attribute
        if not os.environ.get('LOGLEVEL') == 'DEBUG':
            for i in interpolations:
                i.start()
            while len(self.interps.keys()) < len(vartypes):
                obj = q.get()
                self.interps[obj[0]] = obj[1]
            for i in interpolations:
                i.join()

        # debug mode: disable parallelization for nicer stack traces
        elif os.environ.get('LOGLEVEL') == 'DEBUG':
            logging.debug('OCEAN DEBUG MSG: parallelization disabled')
            for i, r, c, v in zip(interpolators, reshapers, columns, vartypes):
                logging.debug(f'interpolating {v}')
                logging.debug(f'{i = }\n{r = }\n{c = }\n{v = }')
                obj = i(**r(c))
                q.put((v, obj))

            while len(self.interps.keys()) < len(vartypes):
                obj = q.get()
                self.interps[obj[0]] = obj[1]
                logging.debug(
                    f'done {obj[0]}... {len(self.interps.keys())}/{len(vartypes)}'
                )

        q.close()

        # set ocean boundaries and interpolator origins
        self.boundaries = kwargs.copy()
        self.origin = center_point(lat=[kwargs['south'], kwargs['north']],
                                   lon=[kwargs['west'], kwargs['east']])
        for v in vartypes:
            self.interps[v].origin = self.origin

        return
コード例 #4
0
    def __init__(self,
                 values,
                 lats,
                 lons,
                 depths,
                 origin=None,
                 method='linear',
                 method_irreg='regularize',
                 bins_irreg_max=200):

        # 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) == 3)

        # 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:
            self.interp_ll = RegularGridInterpolator(
                (lats_rad, lons_rad, depths),
                values,
                method=method,
                bounds_error=False,
                fill_value=None)
        else:
            if method_irreg == 'regularize':

                # interpolators on irregular grid
                gd = GridData3D(u=lats_rad,
                                v=lons_rad,
                                w=depths,
                                r=values,
                                method='linear')
                gd_near = GridData3D(u=lats_rad,
                                     v=lons_rad,
                                     w=depths,
                                     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]
                depth_diffs = np.diff(np.sort(np.unique(depths)))
                depth_diffs = depth_diffs[depth_diffs > 0.1]
                bin_size = (np.min(lat_diffs), np.min(lon_diffs),
                            np.min(depth_diffs))

                # regular grid that data will be mapped to
                lats_reg, lons_reg, depths_reg = self._create_grid(
                    lats=lats,
                    lons=lons,
                    depths=depths,
                    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,
                        z=depths_reg,
                        grid=True)
                vi_near = gd_near(theta=lats_reg_rad,
                                  phi=lons_reg_rad,
                                  z=depths_reg,
                                  grid=True)
                indices_nan = np.where(np.isnan(vi))
                vi[indices_nan] = vi_near[indices_nan]

                # interpolator on regular grid
                self.interp_ll = RegularGridInterpolator(
                    (lats_reg_rad, lons_reg_rad, depths_reg),
                    vi,
                    method=method,
                    bounds_error=False,
                    fill_value=None)

            else:
                self.interp_ll = GridData3D(u=lats_rad,
                                            v=lons_rad,
                                            w=depths,
                                            r=values,
                                            method=method_irreg)

        # store grids
        self.lat_nodes = lats
        self.lon_nodes = lons
        self.depth_nodes = depths
        self.values = values
コード例 #5
0
    def __init__(self,
                 values,
                 lats,
                 lons,
                 origin=None,
                 method_irreg='regularize',
                 bins_irreg_max=2000):

        # 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