Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
eec8882
add basic entries to enable OPERA tropo support for parsing in MintPy
dbekaert Mar 2, 2026
dc0bcdb
first attempt on tropo_opera.py
dbekaert Mar 4, 2026
0283efa
add opera functions to enable its method for tropo correction
dbekaert Mar 4, 2026
16411d2
Update src/mintpy/tropo_opera.py
dbekaert Mar 4, 2026
c059ae8
Added subsetting instead of full file download. This speeds up drasti…
dbekaert Mar 31, 2026
99acda4
fix: address review feedback (isort, memory scaling, docstring, CLI t…
dbekaert Mar 31, 2026
cfef017
fix: pyupgrade f-string parens + add asf_search to requirements
dbekaert Mar 31, 2026
9a3cbc4
add fsspec[http] to requirements for OPERA remote subsetting
dbekaert Mar 31, 2026
592494e
fsspec requires python=3.10+
yunjunz Apr 1, 2026
49ea842
Implement geometry grid reprojection support for UTM TS
sssangha Apr 2, 2026
e6feefb
fix: remove duplicate readfile import and trailing whitespace
dbekaert Apr 2, 2026
d069e64
fix: sort imports (isort) in calc_zenith_delay_from_opera_file
dbekaert Apr 2, 2026
e3da805
Refactor calc_zenith_delay_from_opera_file parameters
sssangha Apr 5, 2026
b12bc2c
Fix formatting in tropo_opera.py
sssangha Apr 6, 2026
bba8153
Refactor code for improved readability and structure
sssangha Apr 6, 2026
5faf8d4
Optimize calc_zenith_delay_from_opera_file function
sssangha Apr 13, 2026
c3c0a71
Update authorship and improve docstring clarity
sssangha Apr 13, 2026
7ed17a3
Fix comment formatting in __main__.py
sssangha Apr 13, 2026
fa8b5b7
Update author information and fix docstring format
sssangha Apr 13, 2026
26ac237
Fix docstring formatting in __main__.py
sssangha Apr 13, 2026
6d2720a
fix: docstring D212 violations — summary on first line (Codacy)
dbekaert Apr 17, 2026
057032f
fix: convert multi-line docstrings to single-line to satisfy both D21…
dbekaert Apr 17, 2026
adcac57
cli/tropo_opera: minor print out msg re-org
yunjunz May 29, 2026
b15a570
pre-commit fix
yunjunz May 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ENV PATH=/opt/conda/bin:${PATH}
ARG MINTPY_HOME=/home/mambauser/tools/MintPy
COPY --chown=mambauser:mambauser . ${MINTPY_HOME}/

ARG PYTHON_VERSION="3.9"
ARG PYTHON_VERSION="3.11"
RUN micromamba install -y -n base -c conda-forge python=${PYTHON_VERSION} \
jupyterlab ipympl gdal">=3" isce2 -f ${MINTPY_HOME}/requirements.txt && \
python -m pip install --no-cache-dir ${MINTPY_HOME} && \
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Language](https://img.shields.io/badge/python-3.8%2B-blue.svg?style=flat-square)](https://www.python.org/)
[![Language](https://img.shields.io/badge/python-3.10%2B-blue.svg?style=flat-square)](https://www.python.org/)
[![Docs Status](https://readthedocs.org/projects/mintpy/badge/?version=latest&style=flat-square)](https://mintpy.readthedocs.io/?badge=latest)
[![CircleCI](https://img.shields.io/circleci/build/github/insarlab/MintPy.svg?logo=circleci&label=tests&style=flat-square)](https://circleci.com/gh/insarlab/MintPy)
[![Docker Status](https://img.shields.io/github/actions/workflow/status/insarlab/MintPy/build-docker.yml?label=docker&style=flat-square&logo=docker&logoColor=white)](https://github.com/insarlab/MintPy/pkgs/container/mintpy)
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ Issues = "https://github.com/insarlab/MintPy/issues"
"timeseries2velocity.py" = "mintpy.cli.timeseries2velocity:main"
"timeseries_rms.py" = "mintpy.cli.timeseries_rms:main"
"tropo_gacos.py" = "mintpy.cli.tropo_gacos:main"
"tropo_opera.py" = "mintpy.cli.tropo_opera:main"
"tropo_phase_elevation.py" = "mintpy.cli.tropo_phase_elevation:main"
"tropo_pyaps3.py" = "mintpy.cli.tropo_pyaps3:main"
"tsview.py" = "mintpy.cli.tsview:main"
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
pip
argcomplete
asf_search>=12.0.0
cartopy
cvxopt
dask>=1.0
dask-jobqueue>=0.3
fsspec[http]
h5py
joblib
lxml
Expand Down
8 changes: 8 additions & 0 deletions src/mintpy/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,13 @@ def get_tropo_gacos_parser(subparsers=None):
return parser


def get_tropo_opera_parser(subparsers=None):
from mintpy.cli import tropo_opera
parser = tropo_opera.create_parser(subparsers)
parser.set_defaults(func=tropo_opera.main)
return parser


def get_tropo_phase_elevation_parser(subparsers=None):
from mintpy.cli import tropo_phase_elevation
parser = tropo_phase_elevation.create_parser(subparsers)
Expand Down Expand Up @@ -629,6 +636,7 @@ def get_parser():
get_s1ab_range_bias_parser(sp)
get_solid_earth_tides_parser(sp)
get_tropo_gacos_parser(sp)
get_tropo_opera_parser(sp)
get_tropo_phase_elevation_parser(sp)
get_tropo_pyaps3_parser(sp)
get_unwrap_error_bridging_parser(sp)
Expand Down
107 changes: 107 additions & 0 deletions src/mintpy/cli/tropo_opera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env python3
############################################################
# Program is part of MintPy #
# Copyright (c) 2013, Zhang Yunjun, Heresh Fattahi #
# Author: David Bekaert, March 2026 #
############################################################


import os
import sys

from mintpy.utils.arg_utils import create_argument_parser

############################################################################
REFERENCE = """references:
Bekaert, D. et al., OPERA L4 Tropospheric Zenith Delay products derived from ECMWF HRES model
(https://www.earthdata.nasa.gov/data/catalog/asf-opera-l4-tropo-zenith-v1-1).
"""

EXAMPLE = """example:
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
tropo_opera.py -f timeseries.h5 -g inputs/geometryRadar.h5 --dir ./OPERA
tropo_opera.py -f geo/geo_timeseries.h5 -g geo/geo_geometryRadar.h5 --dir ./OPERA
"""


def create_parser(subparsers=None):
synopsis = 'Tropospheric correction using OPERA Zenith Tropospheric Delays from ECMWF HRES data '
synopsis += '\n(https://www.earthdata.nasa.gov/data/catalog/asf-opera-l4-tropo-zenith-v1-1)'
epilog = REFERENCE + '\n' + EXAMPLE
name = __name__.split('.')[-1]
parser = create_argument_parser(
name, synopsis=synopsis, description=synopsis, epilog=epilog, subparsers=subparsers)
parser.add_argument('-f', '--file', dest='dis_file', required=True,
help='timeseries HDF5 file, i.e. timeseries.h5')
parser.add_argument('-g', '--geom', dest='geom_file', required=True,
help='geometry file.')
parser.add_argument('--dir','--opera-dir', dest='opera_dir', default='./OPERA',
help='directory to store downloaded OPERA tropospheric delay products, \n' +
'which are automatically downloaded from ASF on demand (default: %(default)s).')
parser.add_argument('-o', dest='cor_dis_file',
help='Output file name for tropospheric corrected timeseries.')

return parser


def cmd_line_parse(iargs=None):
"""Command line parser."""
# parse
parser = create_parser()
inps = parser.parse_args(args=iargs)

# import
from mintpy.utils import readfile

# check: --opera-dir (use absolute path)
inps.opera_dir = os.path.abspath(inps.opera_dir)
print('Use OPERA products at directory:', inps.opera_dir)

# check: exsitence of input files
for fname in [inps.dis_file, inps.geom_file]:
if fname and not os.path.isfile(fname):
raise FileNotFoundError(f'input file not exist: {fname}')

# check: processors & coordinates of input files
atr1 = readfile.read_attribute(inps.dis_file)
atr2 = readfile.read_attribute(inps.geom_file)
coord1 = 'geo' if 'Y_FIRST' in atr1.keys() else 'radar'
coord2 = 'geo' if 'Y_FIRST' in atr2.keys() else 'radar'
proc = atr1.get('PROCESSOR', 'isce')

# check: radar-coord product from gamma and roipac is not supported
if coord1 == 'radar' and proc in ['gamma', 'roipac']:
msg = f'Radar-coded file from {proc} is NOT supported!'
msg += '\n Try to geocode the time-series and geometry files and re-run with them instead.'
raise ValueError(msg)

# check: coordinate system must be consistent btw. displacement and geometry files
if coord1 != coord2:
n = max(len(os.path.basename(i)) for i in [inps.dis_file, inps.geom_file])
msg = 'Input time-series and geometry file are NOT in the same coordinate!'
msg += f'\n file {os.path.basename(inps.dis_file):<{n}} coordinate: {coord1}'
msg += f'\n file {os.path.basename(inps.geom_file):<{n}} coordinate: {coord2}'
raise ValueError(msg)

# default: output tropo and corrected displacement file names
inps.tropo_file = os.path.join(os.path.dirname(inps.geom_file), 'OPERA.h5')
if not inps.cor_dis_file:
inps.cor_dis_file = inps.dis_file.split('.')[0] + '_OPERA.h5'

return inps


############################################################################
def main(iargs=None):
# parse
inps = cmd_line_parse(iargs)

# import
from mintpy.tropo_opera import run_tropo_opera

# run
run_tropo_opera(inps)


############################################################################
if __name__ == '__main__':
main(sys.argv[1:])
7 changes: 6 additions & 1 deletion src/mintpy/defaults/smallbaselineApp.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ mintpy.ionosphericDelay.excludeDate12 = auto #[20080520_20090817 / no], auto fo
## NARR - NARR from NOAA [need to install PyAPS from Caltech/EarthDef; recommended for N America]
## c. gacos - use GACOS with the iterative tropospheric decomposition model (Yu et al., 2018, JGR)
## need to manually download GACOS products at http://www.gacos.net for all acquisitions before running this step
mintpy.troposphericDelay.method = auto #[pyaps / height_correlation / gacos / no], auto for pyaps
## d. opera - use OPERA L4 tropospheric zenith delay products
mintpy.troposphericDelay.method = auto #[pyaps / height_correlation / gacos / opera / no], auto for pyaps

## Notes for pyaps:
## a. GAM data latency: with the most recent SAR data, there will be GAM data missing, the correction
Expand All @@ -249,6 +250,10 @@ mintpy.troposphericDelay.minCorrelation = auto #[0.0-1.0], auto for 0
## Set the path below to directory that contains the downloaded *.ztd* files
mintpy.troposphericDelay.gacosDir = auto # [path2directory], auto for "./GACOS"

## Notes for opera:
## Set the path below to directory that contains downloaded OPERA *.nc files
mintpy.troposphericDelay.operaDir = auto # [path2directory], auto for "./OPERA"


########## 9. deramp (optional)
## Estimate and remove a phase ramp for each acquisition based on the reliable pixels.
Expand Down
3 changes: 3 additions & 0 deletions src/mintpy/defaults/smallbaselineApp_auto.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ mintpy.troposphericDelay.minCorrelation = 0
## gacos
mintpy.troposphericDelay.gacosDir = ./GACOS

## opera
mintpy.troposphericDelay.operaDir = ./OPERA


########## deramp
mintpy.deramp = no
Expand Down
13 changes: 13 additions & 0 deletions src/mintpy/smallbaselineApp.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,9 @@ def get_timeseries_filename(template, work_dir='./'):
elif method == 'gacos':
fname1 = f'{os.path.splitext(fname0)[0]}_GACOS.h5'

elif method == 'opera':
fname1 = f'{os.path.splitext(fname0)[0]}_OPERA.h5'

elif method == 'pyaps':
fname1 = f'{os.path.splitext(fname0)[0]}_{model}.h5'

Expand Down Expand Up @@ -648,6 +651,16 @@ def get_dataset_size(fname):
import mintpy.cli.tropo_phase_elevation
mintpy.cli.tropo_phase_elevation.main(iargs)

# OPERA ZTD products derived from ECMWF HRES data and available through the ASF DAAC
elif method == 'opera':
opera_dir = self.template['mintpy.troposphericDelay.operaDir']
iargs = ['-f', in_file, '-g', geom_file, '-o', out_file, '--dir', opera_dir]
print('tropospheric delay correction with OPERA ZTD products')
print('\ntropo_opera.py', ' '.join(iargs))
if ut.run_or_skip(out_file=out_file, in_file=in_file) == 'run':
import mintpy.cli.tropo_opera
mintpy.cli.tropo_opera.main(iargs)

# Weather re-analysis data with iterative tropospheric decomposition (GACOS)
# Yu et al. (2018, JGR)
elif method == 'gacos':
Expand Down
Loading