Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • a83768b
  • /
  • pyrate
  • /
  • core
  • /
  • orbital.py
Raw File Download

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge
swh:1:cnt:98d49eddb9650108729fe8fc86119ec7c92a35b7
directory badge
swh:1:dir:ed693b95c0a5007ecce0ec1d4ec6ff72befbfa5b

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
(requires biblatex-software package)
Generating citation ...
(requires biblatex-software package)
Generating citation ...
orbital.py
#   This Python module is part of the PyRate software package.
#
#   Copyright 2022 Geoscience Australia
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
"""
This Python module implements residual orbital corrections for interferograms.
"""
# pylint: disable=invalid-name
import tempfile
from typing import Optional, List, Dict, Iterable
from collections import OrderedDict
from pathlib import Path
from numpy import empty, isnan, reshape, float32, squeeze
from numpy import dot, vstack, zeros, meshgrid
import numpy as np
from numpy.linalg import pinv, cond

import pyrate.constants as C
from pyrate.core.algorithm import first_second_ids, get_all_epochs
from pyrate.core import shared, ifgconstants as ifc, prepifg_helper, mst, mpiops
from pyrate.core.shared import nanmedian, Ifg, InputTypes, iterable_split
from pyrate.core.logger import pyratelogger as log
from pyrate.prepifg import find_header
from pyrate.configuration import MultiplePaths
# Orbital correction tasks
#
# TODO: options for multilooking
# 1) do the 2nd stage mlook at prepifg.py/generate up front, then delete in
#    workflow afterward
# 2) refactor prep_ifgs() call to take input filenames and params & generate
#    mlooked versions from that
#    this needs to be more generic to call at any point in the runtime.


# Design notes:
# The orbital correction code includes several enhancements. PyRate creates
# sparse arrays for the linear inversion, which contain many empty cells.
# This is unnecessary for the independent method, and temporarily wastes
# potentially a lot of memory.
#
# For the independent method, PyRate makes individual small design matrices and
# corrects the Ifgs one by one. If required in the correction, the intercept
# option adds an extra column of ones to include in the inversion.
#
# Network method design matrices are mostly empty, and intercept terms are handled
# differently. Individual design matrices (== independent method DMs) are
# placed in the sparse network design matrix. Intercept terms are not included in the
# smaller DMs to prevent unwanted cols being inserted. This is why some funcs
# appear to ignore the intercept parameter in the networked method. Network DM
# intercept terms are cols of 1s in a diagonal line on the RHS of the sparse array.

MAIN_PROCESS = 0

# ORBITAL ERROR correction constants
INDEPENDENT_METHOD = C.INDEPENDENT_METHOD
NETWORK_METHOD = C.NETWORK_METHOD

PLANAR = C.PLANAR
QUADRATIC = C.QUADRATIC
PART_CUBIC = C.PART_CUBIC


def remove_orbital_error(ifgs: List, params: dict) -> None:
    """
    Wrapper function for PyRate orbital error removal functionality.

    NB: the ifg data is modified in situ, rather than create intermediate
    files. The network method assumes the given ifgs have already been reduced
    to a minimum spanning tree network.
    """
    ifg_paths = [i.data_path for i in ifgs] if isinstance(ifgs[0], Ifg) else ifgs
    degree = params[C.ORBITAL_FIT_DEGREE]
    method = params[C.ORBITAL_FIT_METHOD]
    orbfitlksx = params[C.ORBITAL_FIT_LOOKS_X]
    orbfitlksy = params[C.ORBITAL_FIT_LOOKS_Y]

    # Sanity check of the orbital params
    if type(orbfitlksx) != int or type(orbfitlksy) != int:
        msg = f"Multi-look factors for orbital correction should be of type: int"
        raise OrbitalError(msg)
    if degree not in [PLANAR, QUADRATIC, PART_CUBIC]:
        msg = "Invalid degree of %s for orbital correction" % C.ORB_DEGREE_NAMES.get(degree)
        raise OrbitalError(msg)
    if method not in [NETWORK_METHOD, INDEPENDENT_METHOD]:
        msg = "Invalid method of %s for orbital correction" % C.ORB_METHOD_NAMES.get(method)
        raise OrbitalError(msg)

    # Give informative log messages based on selected options
    log.info(f'Calculating {__degrees_as_string(degree)} orbital correction using '
             f'{__methods_as_string(method)} method')
    if orbfitlksx > 1 or orbfitlksy > 1:
        log.info(f'Multi-looking interferograms for orbital correction with '
                 f'factors of X = {orbfitlksx} and Y = {orbfitlksy}')

    if method == INDEPENDENT_METHOD:
        iterable_split(independent_orbital_correction, ifg_paths, params)

    elif method == NETWORK_METHOD:
        # Here we do all the multilooking in one process, but in memory
        # could use multiple processes if we write data to disc during
        # remove_orbital_error step
        # TODO: performance comparison of saving multilooked files on
        # disc vs in-memory single-process multilooking
        #
        # The gdal swig bindings prevent us from doing multi-looking in parallel
        # when using multiprocessing because the multilooked ifgs are held in
        # memory using in-memory tifs. Parallelism using MPI is possible.
        # TODO: Use a flag to select mpi parallel vs multiprocessing in the
        # iterable_split function, which will use mpi but can fall back on
        # single process based on the flag for the multiprocessing side.
        if mpiops.rank == MAIN_PROCESS:
            mlooked = __create_multilooked_datasets(params)
            _validate_mlooked(mlooked, ifg_paths)
            network_orbital_correction(ifg_paths, params, mlooked)
    else:
        raise OrbitalError("Unrecognised orbital correction method")


def __create_multilooked_datasets(params):
    exts, ifg_paths, multi_paths = __extents_from_params(params)
    mlooked_datasets = [_create_mlooked_dataset(m, i, exts, params) for m, i in zip(multi_paths, ifg_paths)]

    mlooked = [Ifg(m) for m in mlooked_datasets]
    for m in mlooked:
        m.initialize()
        shared.nan_and_mm_convert(m, params)
    return mlooked


def __extents_from_params(params):
    multi_paths = params[C.INTERFEROGRAM_FILES]
    ifg_paths = [p.tmp_sampled_path for p in multi_paths]
    rasters = [shared.dem_or_ifg(r) for r in ifg_paths]
    crop_opt = prepifg_helper.ALREADY_SAME_SIZE
    xlooks = params[C.ORBITAL_FIT_LOOKS_X]
    ylooks = params[C.ORBITAL_FIT_LOOKS_Y]
    exts = prepifg_helper.get_analysis_extent(crop_opt, rasters, xlooks, ylooks, None)
    return exts, ifg_paths, multi_paths


def _create_mlooked_dataset(multi_path, ifg_path, exts, params):
    '''
    Wrapper to generate a multi-looked dataset for a single ifg
    '''
    header = find_header(multi_path, params)
    thresh = params[C.NO_DATA_AVERAGING_THRESHOLD]
    crop_opt = prepifg_helper.ALREADY_SAME_SIZE
    xlooks = params[C.ORBITAL_FIT_LOOKS_X]
    ylooks = params[C.ORBITAL_FIT_LOOKS_Y]
    out_path = tempfile.mktemp()
    log.debug(f'Multi-looking {ifg_path} with factors X = {xlooks} and Y = {ylooks} for orbital correction')
    resampled_data, out_ds = prepifg_helper.prepare_ifg(
        ifg_path, xlooks, ylooks, exts, thresh, crop_opt, header, False, out_path
    )
    return out_ds


def _validate_mlooked(mlooked, ifgs):
    '''
    Basic sanity checking of the multilooked ifgs.
    '''

    if len(mlooked) != len(ifgs):
        msg = "Mismatching # ifgs and # multilooked ifgs"
        raise OrbitalError(msg)

    if not all([hasattr(i, 'phase_data') for i in mlooked]):
        msg = "Mismatching types in multilooked ifgs arg:\n%s" % mlooked
        raise OrbitalError(msg)


def _get_num_params(degree, intercept: Optional[bool] = False):
    '''
    Returns number of model parameters from string parameter
    '''

    if degree == PLANAR:
        nparams = 2
    elif degree == QUADRATIC:
        nparams = 5
    elif degree == PART_CUBIC:
        nparams = 6
    else:
        msg = "Invalid orbital model degree: %s" \
              % C.ORB_DEGREE_NAMES.get(degree)
        raise OrbitalError(msg)

    # NB: independent method only, network method handles intercept terms differently
    if intercept:
        nparams += 1  # c in y = mx + c
    return nparams


def independent_orbital_correction(ifg_path, params):
    """
    Calculates and removes an orbital error surface from a single independent
    interferogram.

    Warning: This will write orbital error corrected phase_data to the ifg.

    :param Ifg class instance ifg: the interferogram to be corrected
    :param dict params: dictionary of configuration parameters

    :return: None - interferogram phase data is updated and saved to disk
    """
    log.debug(f"Orbital correction of {ifg_path}")
    degree = params[C.ORBITAL_FIT_DEGREE]
    offset = params[C.ORBFIT_OFFSET]
    intercept = params[C.ORBFIT_INTERCEPT]
    xlooks = params[C.ORBITAL_FIT_LOOKS_X]
    ylooks = params[C.ORBITAL_FIT_LOOKS_Y]

    ifg0 = shared.Ifg(ifg_path) if isinstance(ifg_path, str) else ifg_path

    # get full-resolution design matrix
    fullres_dm = get_design_matrix(ifg0, degree, intercept=intercept)

    ifg = shared.dem_or_ifg(ifg_path) if isinstance(ifg_path, str) else ifg_path
    multi_path = MultiplePaths(ifg.data_path, params)
    orb_on_disc = MultiplePaths.orb_error_path(ifg.data_path, params)

    if not ifg.is_open:
        ifg.open()
    shared.nan_and_mm_convert(ifg, params)
    fullres_ifg = ifg  # keep a backup
    fullres_phase = fullres_ifg.phase_data

    if orb_on_disc.exists():
        log.info(f'Reusing already computed orbital fit correction: {orb_on_disc}')
        orbital_correction = np.load(file=orb_on_disc)
    else:
        # Multi-look the ifg data if either X or Y is greater than 1
        if (xlooks > 1) or (ylooks > 1):
            exts, _, _ = __extents_from_params(params)
            mlooked = _create_mlooked_dataset(multi_path, ifg.data_path, exts, params)
            ifg = Ifg(mlooked) # multi-looked Ifg object
            ifg.initialize()
            shared.nan_and_mm_convert(ifg, params)

        # vectorise phase data, keeping NODATA
        vphase = reshape(ifg.phase_data, ifg.num_cells)

        # compute design matrix for multi-looked data
        mlooked_dm = get_design_matrix(ifg, degree, intercept=intercept)

        # invert to obtain the correction image (forward model) at full-res
        orbital_correction = __orb_correction(fullres_dm, mlooked_dm, fullres_phase, vphase, offset=offset)

        # save correction to disc
        if not orb_on_disc.parent.exists():
            shared.mkdir_p(orb_on_disc.parent)

        np.save(file=orb_on_disc, arr=orbital_correction)

    # subtract orbital correction from the full-res ifg
    fullres_ifg.phase_data -= orbital_correction

    # set orbfit meta tag and save phase to file
    _save_orbital_error_corrected_phase(fullres_ifg, params)


def __orb_correction(fullres_dm, mlooked_dm, fullres_phase, mlooked_phase, offset=False):
    """
    Function to perform the inversion to obtain orbital model parameters
    and return the orbital correction as the full resolution forward model.
    """
    # perform inversion using pseudoinverse of DM
    orbparams = __orb_inversion(mlooked_dm, mlooked_phase)

    # compute forward model at full resolution
    fullorb = reshape(dot(fullres_dm, orbparams), fullres_phase.shape)

    # Estimate the offset of the interferogram as the median of ifg minus model
    # Only needed if reference phase correction has already been applied?
    if offset:
        offset_removal = nanmedian(np.ravel(fullres_phase - fullorb))
    else:
        offset_removal = 0
    orbital_correction = fullorb - offset_removal
    return orbital_correction


def __orb_inversion(design_matrix, data):
    """Inversion using pseudoinverse of design matrix"""
    # remove NaN elements before inverting to get the model
    B = design_matrix[~isnan(data)]
    d = data[~isnan(data)]

    return pinv(B) @ d


def network_orbital_correction(ifg_paths, params, m_ifgs: Optional[List] = None):
    """
    This algorithm implements a network inversion to determine orbital
    corrections for a set of interferograms forming a connected network.

    Warning: This will write orbital error corrected phase_data to the ifgs.

    :param list ifg_paths: List of Ifg class objects reduced to a minimum spanning tree network
    :param dict params: dictionary of configuration parameters
    :param list m_ifgs: list of multilooked Ifg class objects (sequence must be multilooked versions of 'ifgs' arg)

    :return: None - interferogram phase data is updated and saved to disk
    """
    # pylint: disable=too-many-locals, too-many-arguments
    offset = params[C.ORBFIT_OFFSET]
    degree = params[C.ORBITAL_FIT_DEGREE]
    preread_ifgs = params[C.PREREAD_IFGS]
    intercept = params[C.ORBFIT_INTERCEPT]
    scale = params[C.ORBFIT_SCALE]

    # all orbit corrections available?
    if isinstance(ifg_paths[0], str):
        if __check_and_apply_orberrors_found_on_disc(ifg_paths, params):
            log.warning("Reusing orbfit errors from previous run!!!")
            return
        # all corrections are available in numpy files already saved - return
        ifgs = [shared.Ifg(i) for i in ifg_paths]
    else:  # alternate test paths # TODO: improve
        ifgs = ifg_paths
    src_ifgs = ifgs if m_ifgs is None else m_ifgs
    src_ifgs = mst.mst_from_ifgs(src_ifgs)[3]  # use networkx mst

    if preread_ifgs:
        temp_ifgs = OrderedDict(sorted(preread_ifgs.items())).values()
        ids = first_second_ids(get_all_epochs(temp_ifgs))
    else:
        ids = first_second_ids(get_all_epochs(ifgs))

    nepochs = len(set(ids))

    # call the actual inversion routine
    coefs = calc_network_orb_correction(src_ifgs, degree, scale, nepochs, intercept=intercept)

    # create full res DM to expand determined coefficients into full res
    # orbital correction (eg. expand coarser model to full size)
    if preread_ifgs:
        temp_ifg = Ifg(ifg_paths[0])  # ifgs here are paths
        temp_ifg.open()
        dm = get_design_matrix(temp_ifg, degree, intercept=intercept, scale=scale)
        temp_ifg.close()
    else:
        ifg = ifgs[0]
        dm = get_design_matrix(ifg, degree, intercept=intercept, scale=scale)

    for i in ifg_paths:
        # open if not Ifg instance
        if isinstance(i, str):  # pragma: no cover
            # are paths
            i = Ifg(i)
            i.open(readonly=False)
            shared.nan_and_mm_convert(i, params)
        _remove_network_orb_error(coefs, dm, i, ids, offset, params)

def calc_network_orb_correction(src_ifgs, degree, scale, nepochs, intercept=False):
    """
    Calculate and return coefficients for the network orbital correction model
    given a set of ifgs:
    :param list src_ifgs: iterable of Ifg objects
    :param str degree: the degree of the orbital fit (planar, quadratic or part-cubic)
    :param int scale: Scale factor for design matrix to improve inversion robustness
    :param int nepochs: The number of epochs in the network
    :param intercept: whether to include a constant offset to fit to each ifg. This
    intercept is discarded and not returned.

    :return coefs: a list of coefficient lists, indexed by epoch. The coefficient lists are in the following order:

    PLANAR - x, y
    QUADRATIC - x^2, y^2, x*y, x, y
    PART_CUBIC - x*y^2, x^2, y^2, x*y, x, y
    """
    vphase = vstack([i.phase_data.reshape((i.num_cells, 1)) for i in src_ifgs])
    vphase = squeeze(vphase)
    B = get_network_design_matrix(src_ifgs, degree, scale, intercept=intercept)
    orbparams = __orb_inversion(B, vphase)
    ncoef = _get_num_params(degree) + intercept
    # extract all params except intercept terms
    coefs = [orbparams[i:i+ncoef] for i in range(0, nepochs * ncoef, ncoef)]
    return coefs

def __check_and_apply_orberrors_found_on_disc(ifg_paths, params):
    saved_orb_err_paths = [MultiplePaths.orb_error_path(ifg_path, params) for ifg_path in ifg_paths]
    for p, i in zip(saved_orb_err_paths, ifg_paths):
        if p.exists():
            orb = np.load(p)
            if isinstance(i, str):
                # are paths
                ifg = Ifg(i)
                ifg.open(readonly=False)
                shared.nan_and_mm_convert(ifg, params)
            else:
                ifg = i
            ifg.phase_data -= orb
            # set orbfit meta tag and save phase to file
            _save_orbital_error_corrected_phase(ifg, params)
    return all(p.exists() for p in saved_orb_err_paths)


def _remove_network_orb_error(coefs, dm, ifg, ids, offset, params):
    """
    remove network orbital error from input interferograms
    """
    saved_orb_err_path = MultiplePaths.orb_error_path(ifg.data_path, params)
    orb = dm.dot(coefs[ids[ifg.second]] - coefs[ids[ifg.first]])
    orb = orb.reshape(ifg.shape)
    # Estimate the offset of the interferogram as the median of ifg minus model
    # Only needed if reference phase correction has already been applied?
    if offset:
        # brings all ifgs to same reference level
        orb -= nanmedian(np.ravel(ifg.phase_data - orb))
    # subtract orbital error from the ifg
    ifg.phase_data -= orb

    # save orb error on disc
    np.save(file=saved_orb_err_path, arr=orb)
    # set orbfit meta tag and save phase to file
    _save_orbital_error_corrected_phase(ifg, params)


def _save_orbital_error_corrected_phase(ifg, params):
    """
    Convenience function to update metadata and save latest phase after
    orbital fit correction
    """
    # set orbfit tags after orbital error correction
    ifg.dataset.SetMetadataItem(ifc.PYRATE_ORB_METHOD, __methods_as_string(params[C.ORBITAL_FIT_METHOD]))
    ifg.dataset.SetMetadataItem(ifc.PYRATE_ORB_DEG, __degrees_as_string(params[C.ORBITAL_FIT_DEGREE]))
    ifg.dataset.SetMetadataItem(ifc.PYRATE_ORB_XLOOKS, str(params[C.ORBITAL_FIT_LOOKS_X]))
    ifg.dataset.SetMetadataItem(ifc.PYRATE_ORB_YLOOKS, str(params[C.ORBITAL_FIT_LOOKS_Y]))
    ifg.dataset.SetMetadataItem(ifc.PYRATE_ORBITAL_ERROR, ifc.ORB_REMOVED)
    ifg.write_modified_phase()
    ifg.close()


def __methods_as_string(method):
    """Look up table to get orbital method string names"""
    meth = {1:ifc.PYRATE_ORB_INDEPENDENT, 2:ifc.PYRATE_ORB_NETWORK}
    return str(meth[method])


def __degrees_as_string(degree):
    """Look up table to get orbital degree string names"""
    deg = {1: ifc.PYRATE_ORB_PLANAR, 2: ifc.PYRATE_ORB_QUADRATIC, 3: ifc.PYRATE_ORB_PART_CUBIC}
    return str(deg[degree])


# TODO: subtract reference pixel coordinate from x and y
def get_design_matrix(ifg, degree, intercept: Optional[bool] = True, scale: Optional[int] = 1):
    """
    Returns orbital error design matrix with columns for model parameters.

    :param Ifg class instance ifg: interferogram to get design matrix for
    :param str degree: model to fit (PLANAR / QUADRATIC / PART_CUBIC)
    :param bool intercept: whether to include column for the intercept term.
    :param int scale: Scale factor for design matrix to improve inversion robustness

    :return: dm: design matrix
    :rtype: ndarray
    """
    if not ifg.is_open:
        ifg.open()

    if degree not in [PLANAR, QUADRATIC, PART_CUBIC]:
        raise OrbitalError("Invalid degree argument")

    if scale < 1:
        raise OrbitalError("Scale argument must be greater or equal to 1")

    # scaling required with higher degree models to help with param estimation
    xsize = ifg.x_size / scale if scale else ifg.x_size
    ysize = ifg.y_size / scale if scale else ifg.y_size

    # mesh needs to start at 1, otherwise first cell resolves to 0 and ignored
    xg, yg = [g+1 for g in meshgrid(range(ifg.ncols), range(ifg.nrows))]
    x = xg.reshape(ifg.num_cells) * xsize
    y = yg.reshape(ifg.num_cells) * ysize

    # TODO: performance test this vs np.concatenate (n by 1 cols)??
    dm = empty((ifg.num_cells, _get_num_params(degree, intercept)), dtype=float32)

    # apply positional parameter values, multiply pixel coordinate by cell size
    # to get distance (a coord by itself doesn't tell us distance from origin)
    if degree == PLANAR:
        dm[:, 0] = x
        dm[:, 1] = y
    elif degree == QUADRATIC:
        dm[:, 0] = x**2
        dm[:, 1] = y**2
        dm[:, 2] = x * y
        dm[:, 3] = x
        dm[:, 4] = y
    elif degree == PART_CUBIC:
        dm[:, 0] = x * (y**2)
        dm[:, 1] = x**2
        dm[:, 2] = y**2
        dm[:, 3] = x * y
        dm[:, 4] = x
        dm[:, 5] = y
    if intercept:
        dm[:, -1] = np.ones(ifg.num_cells) # estimate the intercept term

    # report condition number of the design matrix - L2-norm computed using SVD
    log.debug(f'The condition number of the design matrix is {cond(dm)}')

    return dm


def get_network_design_matrix(ifgs, degree, scale, intercept=True):
    # pylint: disable=too-many-locals
    """
    Returns larger-format design matrix for network error correction. The
    network design matrix includes rows which relate to those of NaN cells.

    :param list ifgs: List of Ifg class objects
    :param str degree: model to fit (PLANAR / QUADRATIC / PART_CUBIC)
    :param int scale: Scale factor for design matrix to improve inversion robustness
    :param bool intercept: whether to include columns for intercept estimation.

    :return: netdm: network design matrix
    :rtype: ndarray
    """

    if degree not in [PLANAR, QUADRATIC, PART_CUBIC]:
        raise OrbitalError("Invalid degree argument")

    if scale < 1:
        raise OrbitalError("Scale argument must be greater or equal to 1")

    nifgs = len(ifgs)
    if nifgs < 1:
        # can feasibly do correction on a single Ifg/2 epochs
        raise OrbitalError("Invalid number of Ifgs: %s" % nifgs)

    # init sparse network design matrix
    nepochs = len(set(get_all_epochs(ifgs)))

    # no intercepts here; they are included separately below
    ncoef = _get_num_params(degree)
    shape = [ifgs[0].num_cells * nifgs, ncoef * nepochs]

    if intercept:
        shape[1] += nepochs  # add extra space for intercepts

    netdm = zeros(shape, dtype=float32)

    # calc location for individual design matrices
    dates = [ifg.first for ifg in ifgs] + [ifg.second for ifg in ifgs]
    ids = first_second_ids(dates)
    tmpdm = get_design_matrix(ifgs[0], degree, intercept=intercept, scale=scale)

    # iteratively build up sparse matrix
    for i, ifg in enumerate(ifgs):
        rs = i * ifg.num_cells  # starting row
        m = ids[ifg.first] * (ncoef + intercept)  # start col for first
        s = ids[ifg.second] * (ncoef + intercept)  # start col for second
        netdm[rs:rs + ifg.num_cells, m:m + ncoef + intercept] = -tmpdm
        netdm[rs:rs + ifg.num_cells, s:s + ncoef + intercept] = tmpdm

    return netdm


class OrbitalError(Exception):
    """
    Generic class for errors in orbital correction.
    """


def orb_fit_calc_wrapper(params: dict) -> None:
    """
    MPI wrapper for orbital fit correction
    """
    multi_paths = params[C.INTERFEROGRAM_FILES]
    if not params[C.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()
    # update phase_data saved in tiled numpy files
    shared.save_numpy_phase(ifg_paths, params)
    log.debug('Finished Orbital error correction')

back to top

Software Heritage — Copyright (C) 2015–2026, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Content policy— Contact— JavaScript license information— Web API