Пример #1
0
def correct_ifgs(config: Configuration) -> None:
    """
    Top level function to perform PyRate workflow on given interferograms
    """
    params = config.__dict__
    __validate_correct_steps(params)

    # work out the tiling and add to params dict
    _update_params_with_tiles(params)

    # create the preread_ifgs dict for use with tiled data
    _create_ifg_dict(params)

    ifg_paths = [ifg_path.tmp_sampled_path for ifg_path in params[C.INTERFEROGRAM_FILES]]

    # create initial tiled phase_data numpy files on disc
    save_numpy_phase(ifg_paths, params)

    params[C.REFX_FOUND], params[C.REFY_FOUND] = ref_pixel_calc_wrapper(params)

    # run through the correct steps in user specified sequence
    for step in params['correct']:
        if step == 'phase_closure':
            correct_steps[step](params, config)
        else:
            correct_steps[step](params)
    log.info("Finished 'correct' step")
Пример #2
0
def spatio_temporal_filter(params: dict) -> None:
    """
    Applies a spatio-temporal filter to remove the atmospheric phase screen
    (APS) and saves the corrected interferograms. Firstly the incremental
    time series is computed using the SVD method, before a cascade of temporal
    then spatial Gaussian filters is applied. The resulting APS corrections are
    saved to disc before being subtracted from each interferogram.

    :param params: Dictionary of PyRate configuration parameters.
    """
    if params[C.APSEST]:
        log.info('Doing APS spatio-temporal filtering')
    else:
        log.info('APS spatio-temporal filtering not required')
        return
    tiles = params[C.TILES]
    preread_ifgs = params[C.PREREAD_IFGS]
    ifg_paths = [
        ifg_path.tmp_sampled_path for ifg_path in params[C.INTERFEROGRAM_FILES]
    ]

    # perform some checks on existing ifgs
    log.debug('Checking APS correction status')
    if mpiops.run_once(shared.check_correction_status, ifg_paths,
                       ifc.PYRATE_APS_ERROR):
        log.debug('Finished APS correction')
        return  # return if True condition returned

    aps_paths = [MultiplePaths.aps_error_path(i, params) for i in ifg_paths]
    if all(a.exists() for a in aps_paths):
        log.warning('Reusing APS errors from previous run')
        _apply_aps_correction(ifg_paths, aps_paths, params)
        return

    # obtain the incremental time series using SVD
    tsincr = _calc_svd_time_series(ifg_paths, params, preread_ifgs, tiles)
    mpiops.comm.barrier()

    # get lists of epochs and ifgs
    ifgs = list(OrderedDict(sorted(preread_ifgs.items())).values())
    epochlist = mpiops.run_once(get_epochs, ifgs)[0]

    # first perform temporal high pass filter
    ts_hp = temporal_high_pass_filter(tsincr, epochlist, params)

    # second perform spatial low pass filter to obtain APS correction in ts domain
    ifg = Ifg(ifg_paths[0])  # just grab any for parameters in slpfilter
    ifg.open()
    ts_aps = spatial_low_pass_filter(ts_hp, ifg, params)
    ifg.close()

    # construct APS corrections for each ifg
    _make_aps_corrections(ts_aps, ifgs, params)

    # apply correction to ifgs and save ifgs to disc.
    _apply_aps_correction(ifg_paths, aps_paths, params)

    # update/save the phase_data in the tiled numpy files
    shared.save_numpy_phase(ifg_paths, params)
Пример #3
0
 def __run_once(self):
     tiles = self.params[cf.TILES]
     mst_files = [Configuration.mst_path(self.params, t.index) for t in tiles]
     correct._copy_mlooked(self.params)
     correct._create_ifg_dict(self.params)
     save_numpy_phase(self.ifg_paths, self.params)
     mst.mst_calc_wrapper(self.params)
     assert all(m.exists() for m in mst_files)
     return [os.stat(o).st_mtime for o in mst_files]
Пример #4
0
def process_ifgs(ifg_paths, params, rows, cols):
    """
    Top level function to perform PyRate workflow on given interferograms

    :param list ifg_paths: List of interferogram paths
    :param dict params: Dictionary of configuration parameters
    :param int rows: Number of sub-tiles in y direction
    :param int cols: Number of sub-tiles in x direction

    :return: refpt: tuple of reference pixel x and y position
    :rtype: tuple
    :return: maxvar: array of maximum variance values of interferograms
    :rtype: ndarray
    :return: vcmt: Variance-covariance matrix array
    :rtype: ndarray
    """

    if mpiops.size > 1:  # turn of multiprocessing during mpi jobs
        params[cf.PARALLEL] = False
    outdir = params[cf.TMPDIR]
    if not os.path.exists(outdir):
        shared.mkdir_p(outdir)

    tiles = mpiops.run_once(get_tiles, ifg_paths[0], rows, cols)

    preread_ifgs = _create_ifg_dict(ifg_paths, params=params)

    # validate user supplied ref pixel
    refpixel.validate_supplied_lat_lon(params)
    refpx, refpy = _ref_pixel_calc(ifg_paths, params)

    # remove non ifg keys
    _ = [preread_ifgs.pop(k) for k in ['gt', 'epochlist', 'md', 'wkt']]

    multi_paths = params[cf.INTERFEROGRAM_FILES]
    _orb_fit_calc(multi_paths, params, preread_ifgs)

    _ref_phase_estimation(ifg_paths, params, refpx, refpy)

    shared.save_numpy_phase(ifg_paths, tiles, params)
    _mst_calc(ifg_paths, params, tiles, preread_ifgs)

    # spatio-temporal aps filter
    wrap_spatio_temporal_filter(ifg_paths, params, tiles, preread_ifgs)

    maxvar, vcmt = _maxvar_vcm_calc(ifg_paths, params, preread_ifgs)
    # save phase data tiles as numpy array for timeseries and stackrate calc

    shared.save_numpy_phase(ifg_paths, tiles, params)

    _timeseries_calc(ifg_paths, params, vcmt, tiles, preread_ifgs)

    _stack_calc(ifg_paths, params, vcmt, tiles, preread_ifgs)

    log.info('PyRate workflow completed')
    return (refpx, refpy), maxvar, vcmt
Пример #5
0
def dem_error_calc_wrapper(params: dict) -> None:
    """
    MPI wrapper for DEM error correction
    :param params: Dictionary of PyRate configuration parameters.
    """
    if not params[C.DEMERROR]:
        log.info("DEM error correction not required")
        return

    # geometry information needed to calculate Bperp for each pixel using first IFG in list
    ifg_paths = [
        ifg_path.tmp_sampled_path for ifg_path in params[C.INTERFEROGRAM_FILES]
    ]

    # check if DEM error correction is already available
    if mpiops.run_once(__check_and_apply_demerrors_found_on_disc, ifg_paths,
                       params):
        log.warning("Reusing DEM error correction from previous run!!!")
    else:
        log.info("Calculating DEM error correction")
        # read and open the first IFG in list
        ifg0_path = ifg_paths[0]
        ifg0 = Ifg(ifg0_path)
        ifg0.open(readonly=True)

        # not currently implemented for ROIPAC data which breaks some tests
        # if statement can be deleted once ROIPAC is deprecated from PyRate
        if ifg0.meta_data[ifc.PYRATE_INSAR_PROCESSOR] == 'ROIPAC':
            log.warning(
                "Geometry calculations are not implemented for ROI_PAC")
            return

        if params[C.BASE_FILE_LIST] is None:
            log.warning(
                "No baseline files supplied: DEM error has not been computed")
            return

        # todo: subtract other corrections (e.g. orbital) from displacement phase before estimating the DEM error

        # the following code is a quick way to do the bperp calculation, but is not identical to the GAMMA output
        # where the near range of the first SLC is used for each pair.
        # calculate look angle for interferograms (using the Near Range of the first SLC)
        # look_angle = geometry.calc_local_geometry(ifg0, None, rg, lon, lat, params)
        # bperp = geometry.calc_local_baseline(ifg0, az, look_angle)

        # split full arrays in to tiles for parallel processing
        tiles_split(_process_dem_error_per_tile, params)

        preread_ifgs = params[C.PREREAD_IFGS]
        # write dem error and correction values to file
        mpiops.run_once(_write_dem_errors, ifg_paths, params, preread_ifgs)
        shared.save_numpy_phase(ifg_paths, params)

        log.debug('Finished DEM error correction step')
Пример #6
0
 def __run_once(self):
     dem_files = [
         MultiplePaths.dem_error_path(i, self.params)
         for i in self.ifg_paths
     ]
     correct._copy_mlooked(self.params)
     correct._update_params_with_tiles(self.params)
     correct._create_ifg_dict(self.params)
     save_numpy_phase(self.ifg_paths, self.params)
     dem_error_calc_wrapper(self.params)
     assert all(m.exists() for m in dem_files)
     return [os.stat(o).st_mtime for o in dem_files]
Пример #7
0
    def test_calc_dem_errors(self):
        # validate output of current version of the code with saved files from an independent test run
        # only the reference phase and dem_error are used in this test

        # saved dem_error.tif (expected)
        dem_error_tif_exp = join(dem_error_path, 'dem_error.tif')
        dem = DEM(dem_error_tif_exp)
        dem_error_exp = dem.data
        # run relevant parts of the 'correct' step
        correct._copy_mlooked(self.params)
        correct._update_params_with_tiles(self.params)
        correct._create_ifg_dict(self.params)
        save_numpy_phase(self.ifg_paths, self.params)
        # subtract the reference phase to enable comparison with a 'normal' pyrate run
        ref_phase_est_wrapper(self.params)
        dem_error_calc_wrapper(self.params)
        # dem_error.tif from this run (result)
        dem_error_tif_res = join(self.params[C.DEM_ERROR_DIR], 'dem_error.tif')
        dem = DEM(dem_error_tif_res)
        dem_error_res = dem.data
        # check equality
        np.testing.assert_allclose(dem_error_exp, dem_error_res)

        # ifg correction files in subdirectory out/dem_error/
        # three different ifgs:
        # ifg1 -> short_baseline_ifg: 20180106-20180319 (ca. 3 m)
        # ifg2 -> long_baseline_ifg: 20180130-20180412(ca. 108 m)
        # ifg3 -> medium_baseline_ifg: 20180412-20180518 (ca. 48 m)
        # load saved files
        dem_error_ifg1_path = join(dem_error_path,
                                   '20180106-20180319_ifg_20_dem_error.npy')
        dem_error_ifg1_exp = np.load(dem_error_ifg1_path)
        dem_error_ifg2_path = join(dem_error_path,
                                   '20180130-20180412_ifg_20_dem_error.npy')
        dem_error_ifg2_exp = np.load(dem_error_ifg2_path)
        dem_error_ifg3_path = join(dem_error_path,
                                   '20180412-20180518_ifg_20_dem_error.npy')
        dem_error_ifg3_exp = np.load(dem_error_ifg3_path)
        # load correction values saved from this run (result)
        dem_error_ifg1_path = Path(self.params[C.DEM_ERROR_DIR]).joinpath(
            '20180106-20180319_ifg_20_dem_error.npy')
        dem_error_ifg1_res = np.load(dem_error_ifg1_path)
        dem_error_ifg2_path = Path(self.params[C.DEM_ERROR_DIR]).joinpath(
            '20180130-20180412_ifg_20_dem_error.npy')
        dem_error_ifg2_res = np.load(dem_error_ifg2_path)
        dem_error_ifg3_path = Path(self.params[C.DEM_ERROR_DIR]).joinpath(
            '20180412-20180518_ifg_20_dem_error.npy')
        dem_error_ifg3_res = np.load(dem_error_ifg3_path)
        # check equality
        np.testing.assert_allclose(dem_error_ifg1_exp, dem_error_ifg1_res)
        np.testing.assert_allclose(dem_error_ifg2_exp, dem_error_ifg2_res)
        np.testing.assert_allclose(dem_error_ifg3_exp, dem_error_ifg3_res)
Пример #8
0
def orb_fit_calc_wrapper(params: dict) -> None:
    """
    MPI wrapper for orbital fit correction
    """
    multi_paths = params[cf.INTERFEROGRAM_FILES]
    if not params[cf.ORBITAL_FIT]:
        log.info('Orbital correction not required!')
        return
    ifg_paths = [p.tmp_sampled_path for p in multi_paths]
    remove_orbital_error(ifg_paths, params)
    mpiops.comm.barrier()
    shared.save_numpy_phase(ifg_paths, params)
    log.debug('Finished Orbital error correction')
Пример #9
0
def process_ifgs(ifg_paths, params, rows, cols):
    """
    Top level function to perform PyRate workflow on given interferograms

    :param list ifg_paths: List of interferogram paths
    :param dict params: Dictionary of configuration parameters
    :param int rows: Number of sub-tiles in y direction
    :param int cols: Number of sub-tiles in x direction

    :return: refpt: tuple of reference pixel x and y position
    :rtype: tuple
    :return: maxvar: array of maximum variance values of interferograms
    :rtype: ndarray
    :return: vcmt: Variance-covariance matrix array
    :rtype: ndarray
    """
    if mpiops.size > 1:  # turn of multiprocessing during mpi jobs
        params[cf.PARALLEL] = False

    tiles = mpiops.run_once(get_tiles, ifg_paths[0], rows, cols)

    preread_ifgs = _create_ifg_dict(ifg_paths, params=params, tiles=tiles)

    # _mst_calc(ifg_paths, params, tiles, preread_ifgs)

    refpx, refpy = _ref_pixel_calc(ifg_paths, params)
    log.debug("refpx, refpy: " + str(refpx) + " " + str(refpy))

    # remove non ifg keys
    _ = [preread_ifgs.pop(k) for k in ['gt', 'epochlist', 'md', 'wkt']]

    _orb_fit_calc(ifg_paths, params, preread_ifgs)

    _ref_phase_estimation(ifg_paths, params, refpx, refpy)

    _mst_calc(ifg_paths, params, tiles, preread_ifgs)

    # spatio-temporal aps filter
    _wrap_spatio_temporal_filter(ifg_paths, params, tiles, preread_ifgs)

    maxvar, vcmt = _maxvar_vcm_calc(ifg_paths, params, preread_ifgs)

    # save phase data tiles as numpy array for timeseries and linrate calc
    shared.save_numpy_phase(ifg_paths, tiles, params)

    _timeseries_calc(ifg_paths, params, vcmt, tiles, preread_ifgs)

    _linrate_calc(ifg_paths, params, vcmt, tiles, preread_ifgs)

    log.info('PyRate workflow completed')
    return (refpx, refpy), maxvar, vcmt
Пример #10
0
def _create_ifg_dict(dest_tifs, params, tiles):
    """
    1. Convert ifg phase data into numpy binary files.
    2. Save the preread_ifgs dict with information about the ifgs that are
    later used for fast loading of Ifg files in IfgPart class

    :param list dest_tifs: List of destination tifs
    :param dict params: Config dictionary
    :param list tiles: List of all Tile instances

    :return: preread_ifgs: Dictionary containing information regarding
                interferograms that are used later in workflow
    :rtype: dict
    """
    ifgs_dict = {}
    process_tifs = mpiops.array_split(dest_tifs)
    shared.save_numpy_phase(dest_tifs, tiles, params)
    for d in process_tifs:
        ifg = shared._prep_ifg(d, params)
        ifgs_dict[d] = PrereadIfg(path=d,
                                  nan_fraction=ifg.nan_fraction,
                                  master=ifg.master,
                                  slave=ifg.slave,
                                  time_span=ifg.time_span,
                                  nrows=ifg.nrows,
                                  ncols=ifg.ncols,
                                  metadata=ifg.meta_data)
        ifg.close()
    ifgs_dict = _join_dicts(mpiops.comm.allgather(ifgs_dict))

    preread_ifgs_file = join(params[cf.TMPDIR], 'preread_ifgs.pk')

    if mpiops.rank == MASTER_PROCESS:

        # add some extra information that's also useful later
        gt, md, wkt = shared.get_geotiff_header_info(process_tifs[0])
        ifgs_dict['epochlist'] = algorithm.get_epochs(ifgs_dict)[0]
        ifgs_dict['gt'] = gt
        ifgs_dict['md'] = md
        ifgs_dict['wkt'] = wkt
        # dump ifgs_dict file for later use
        cp.dump(ifgs_dict, open(preread_ifgs_file, 'wb'))

    mpiops.comm.barrier()
    preread_ifgs = OrderedDict(
        sorted(cp.load(open(preread_ifgs_file, 'rb')).items()))
    log.debug('Finished converting phase_data to numpy '
              'in process {}'.format(mpiops.rank))
    return preread_ifgs
Пример #11
0
 def setup_method(cls):
     cls.conf = common.TEST_CONF_GAMMA
     params = Configuration(cls.conf).__dict__
     conv2tif.main(params)
     params = Configuration(cls.conf).__dict__
     prepifg.main(params)
     cls.params = Configuration(cls.conf).__dict__
     correct._copy_mlooked(cls.params)
     correct._update_params_with_tiles(cls.params)
     correct._create_ifg_dict(cls.params)
     multi_paths = cls.params[cf.INTERFEROGRAM_FILES]
     cls.ifg_paths = [p.tmp_sampled_path for p in multi_paths]
     cls.ifgs = [shared.Ifg(i) for i in cls.ifg_paths]
     for i in cls.ifgs:
         i.open()
     shared.save_numpy_phase(cls.ifg_paths, cls.params)
     correct.mst_calc_wrapper(cls.params)
Пример #12
0
def phase_closure_wrapper(params: dict, config: Configuration) -> dict:
    """
    This wrapper will run the iterative phase closure check to return a stable
    list of checked interferograms, and then mask pixels in interferograms that
    exceed the unwrapping error threshold.
    :param params: Dictionary of PyRate configuration parameters. 
    :param config: Configuration class instance.
    :return: params: Updated dictionary of PyRate configuration parameters.
    """

    if not params[C.PHASE_CLOSURE]:
        log.info("Phase closure correction is not required!")
        return

    rets = iterative_closure_check(config)
    if rets is None:
        log.info("Zero loops are returned from the iterative closure check.")
        log.warning("Abandoning phase closure correction without modifying the interferograms.")
        return
        
    ifg_files, ifgs_breach_count, num_occurences_each_ifg = rets

    # update params with closure checked ifg list
    params[C.INTERFEROGRAM_FILES] = \
        mpiops.run_once(update_ifg_list, ifg_files, params[C.INTERFEROGRAM_FILES])

    if mpiops.rank == 0:
        with open(config.phase_closure_filtered_ifgs_list(params), 'w') as f:
            lines = [p.converted_path + '\n' for p in params[C.INTERFEROGRAM_FILES]]
            f.writelines(lines)

    # mask ifgs with nans where phase unwrap threshold is breached
    if mpiops.rank == 0:
        mask_pixels_with_unwrapping_errors(ifgs_breach_count, num_occurences_each_ifg, params)

    _create_ifg_dict(params) # update the preread_ifgs dict

    ifg_paths = [ifg_path.tmp_sampled_path for ifg_path in params[C.INTERFEROGRAM_FILES]]
    # update/save the phase_data in the tiled numpy files
    save_numpy_phase(ifg_paths, params)

    return params
Пример #13
0
def wrap_spatio_temporal_filter(params):
    """
    A wrapper for the spatio-temporal filter so it can be tested.
    See docstring for spatio_temporal_filter.
    """
    if params[cf.APSEST]:
        log.info('Doing APS spatio-temporal filtering')
    else:
        log.info('APS spatio-temporal filtering not required')
        return
    tiles = params[cf.TILES]
    preread_ifgs = params[cf.PREREAD_IFGS]
    ifg_paths = [
        ifg_path.tmp_sampled_path
        for ifg_path in params[cf.INTERFEROGRAM_FILES]
    ]

    # perform some checks on existing ifgs
    log.debug('Checking APS correction status')
    if mpiops.run_once(shared.check_correction_status, ifg_paths,
                       ifc.PYRATE_APS_ERROR):
        log.debug('Finished APS correction')
        return  # return if True condition returned

    aps_error_files_on_disc = [
        MultiplePaths.aps_error_path(i, params) for i in ifg_paths
    ]
    if all(a.exists() for a in aps_error_files_on_disc):
        log.warning("Reusing APS errors from previous run!!!")
        for ifg_path, a in mpiops.array_split(
                list(zip(ifg_paths, aps_error_files_on_disc))):
            phase = np.load(a)
            _save_aps_corrected_phase(ifg_path, phase)
    else:
        tsincr = _calc_svd_time_series(ifg_paths, params, preread_ifgs, tiles)
        mpiops.comm.barrier()

        spatio_temporal_filter(tsincr, ifg_paths, params, preread_ifgs)
    mpiops.comm.barrier()
    shared.save_numpy_phase(ifg_paths, params)
Пример #14
0
def ref_phase_est_wrapper(params):
    """
    Wrapper for reference phase estimation.
    """
    ifg_paths = [
        ifg_path.tmp_sampled_path for ifg_path in params[C.INTERFEROGRAM_FILES]
    ]
    refpx, refpy = params[C.REFX_FOUND], params[C.REFY_FOUND]
    if len(ifg_paths) < 2:
        raise ReferencePhaseError(
            "At least two interferograms required for reference phase correction ({len_ifg_paths} "
            "provided).".format(len_ifg_paths=len(ifg_paths)))

    # this is not going to be true as we now start with fresh multilooked ifg copies - remove?
    if mpiops.run_once(shared.check_correction_status, ifg_paths,
                       ifc.PYRATE_REF_PHASE):
        log.warning(
            'Reference phase correction already applied to ifgs; returning')
        return

    ifgs = [Ifg(ifg_path) for ifg_path in ifg_paths]
    # Save reference phase numpy arrays to disk.
    ref_phs_file = Configuration.ref_phs_file(params)

    # If ref phase file exists on disk, then reuse - subtract ref_phase from ifgs and return
    if ref_phs_file.exists():
        ref_phs = np.load(ref_phs_file)
        _update_phase_and_metadata(ifgs, ref_phs, params)
        shared.save_numpy_phase(ifg_paths, params)
        return ref_phs, ifgs

    # determine the reference phase for each ifg
    if params[C.REF_EST_METHOD] == 1:
        log.info("Calculating reference phase as median of interferogram")
        ref_phs = est_ref_phase_ifg_median(ifg_paths, params)
    elif params[C.REF_EST_METHOD] == 2:
        log.info(
            'Calculating reference phase in a patch surrounding pixel (x, y): ({}, {})'
            .format(refpx, refpy))
        ref_phs = est_ref_phase_patch_median(ifg_paths, params, refpx, refpy)
    else:
        raise ReferencePhaseError(
            "No such option, set parameter 'refest' to '1' or '2'.")

    # gather all reference phases from distributed processes and save to disk
    if mpiops.rank == MAIN_PROCESS:
        collected_ref_phs = np.zeros(len(ifg_paths), dtype=np.float64)
        process_indices = mpiops.array_split(range(len(ifg_paths))).astype(
            np.uint16)
        collected_ref_phs[process_indices] = ref_phs
        for r in range(1, mpiops.size):
            process_indices = mpiops.array_split(range(len(ifg_paths)),
                                                 r).astype(np.uint16)
            this_process_ref_phs = np.zeros(shape=len(process_indices),
                                            dtype=np.float64)
            mpiops.comm.Recv(this_process_ref_phs, source=r, tag=r)
            collected_ref_phs[process_indices] = this_process_ref_phs
        np.save(file=ref_phs_file, arr=collected_ref_phs)
    else:
        collected_ref_phs = np.empty(len(ifg_paths), dtype=np.float64)
        mpiops.comm.Send(ref_phs, dest=MAIN_PROCESS, tag=mpiops.rank)

    mpiops.comm.Bcast(collected_ref_phs, root=0)

    # subtract ref_phase from ifgs
    _update_phase_and_metadata(ifgs, collected_ref_phs, params)

    mpiops.comm.barrier()
    shared.save_numpy_phase(ifg_paths, params)

    log.debug("Finished reference phase correction")

    # Preserve old return value so tests don't break.
    return ref_phs, ifgs