DIST‐S1 Gamma Acceptance Testing Instructions - nasa/opera-sds-pge GitHub Wiki

This page contains instructions for performing Acceptance Testing for the DIST-S1 Gamma delivery from the OPERA-ADT team. These instructions pertain to the latest version of the Gamma release, currently SAS v1.0.1. These instructions assume the user has Docker installed on their local machine.

Acquiring the DIST-S1 Gamma Docker Image

For testing delivered images

The image is currently hosted on JPL FN-Artifactory, which requires JPL VPN access and JPL credentials. You may also need to be added to the gov.nasa.jpl.opera.adt organization.

Once you have access, the container tarball delivery is available under general/gov/nasa/jpl/opera/adt/dist_s1/r3.1/gamma/dockerimg_dist-s1_gamma_1.0.1.tar and sample data is available under general/gov/nasa/jpl/opera/adt/dist_s1/r3/gamma/test_data.zip

Loading the image into Docker

The first step in running the DIST-S1 image is to load it into Docker via the following command:

docker load -i dockerimg_dist-s1_gamma_1.0.1.tar

This should add the Docker image to your local repository with the name opera/dist-s1 and the tag gamma_1.0.1.

tag="gamma_1.0.1"

For testing iterated images

When errors are found, ADT will push test images to GHCR that we can then quickly evaluate for them to make a new delivery

tag="<GHCR tag>"
docker pull ghcr.io/opera-adt/dist-s1:$tag
docker tag ghcr.io/opera-adt/dist-s1:$tag opera/dist-s1:$tag
docker rmi ghcr.io/opera-adt/dist-s1:$tag

Preparing the test data

First we must extract the provided test data:

unzip test_data.zip
  • Create the output directory and ensure it is writable:

    mkdir -p <DIST_S1_DIR>/output_dir

    chmod ga+w <DIST_S1_DIR>/output_dir

  • Create the runconfig directory:

    mkdir -p <DIST_S1_DIR>/runconfig_dir

  • Create the scratch directory and ensure it is writable:

    mkdir -p <DIST_S1_DIR>/scratch_dir

    chmod ga+w <DIST_S1_DIR>/scratch_dir

Move the golden dataset runconfig to the runconfig directory:

mv <DIST_S1_DIR>/test_data/cropped/sample_runconfig_10SGD_cropped.yml <DIST_S1_DIR>/runconfig_dir/runconfig.yaml

You will then need to edit <DIST_S1_DIR>/runconfig_dir/run_config.yml

  • For all input paths (pre_rtc_copol, pre_rtc_crosspol, post_rtc_copol, and post_rtc_crosspol), replace test_data/cropped/ with /home/ops/input_dir/ (vim command: :%s/test_data\/cropped\//\/home\/ops\/input_dir\//g)
  • Replace dst_dir path with /home/ops/scratch_dir
  • Replace product_dst_dir path with /home/ops/output_dir
  • Add the following key/value pairs into the runconfig:
    • runconfig.device: cpu
    • runconfig.n_workers_for_despeckling: 4
    • runconfig.confirmation_strategy: use_prev_product

Executing the DIST-S1 container on the sample test data

Change directory into the <DIST_S1_DIR> directory.

cd <DIST_S1_DIR>/

We're now ready to execute the DIST-S1 Gamma delivery. Run the following the command to kick off execution with the test assets and copy the output products to the mounted output_dir volume:

NOTE: Docker requires absolute paths for volume arguments, so assuming you are running from within <DIST_S1_DIR>, the $(pwd) command can be utilized as a shortcut.

docker run --rm -i --tty -u $(id -u):$(id -g) \
  -v $(pwd)/test_data/cropped:/home/ops/input_dir \
  -v $(pwd)/runconfig_dir:/home/ops/runconfig_dir \
  -v $(pwd)/output_dir:/home/ops/output_dir \
  -v $(pwd)/scratch_dir:/home/ops/scratch_dir \
  --entrypoint /opt/conda/envs/dist-s1-env/bin/dist-s1 \
  opera/dist-s1:$tag run_sas --runconfig_yml_path /home/ops/runconfig_dir/runconfig.yaml

You should see many progress bars filling your screen before the container exits.

An output product of ten GeoTIFFs and one PNG browse image should be created in the output_dir:

$ tree output_dir/
output_dir/
└── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-DIST-CONF.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-DIST-COUNT.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-DIST-DATE.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-DIST-DUR.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-DIST-LAST-DATE.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-DIST-PERC.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-DIST-STATUS-ACQ.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-DIST-STATUS.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-METRIC-MAX.tif
    ├── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1_GEN-METRIC.tif
    └── OPERA_L3_DIST-ALERT-S1_T10SGD_20250102T015857Z_20250612T165547Z_S1_30_v0.1.png

1 directory, 11 files

NOTE: The 20250612T165547Z portion of this sample product name is the production time, and will be different for each execution of the dist-s1 container. All other portions of the file names should match.

For this Acceptance Test, ensure the same set of expected files has been created on the local machine.

Comparing SAS output to golden dataset

First get the output and golden product names (for ease)

GOLDEN_PRODUCT=$(find test_data/golden_datasets/10SGD/ -maxdepth 1 -mindepth 1 -type d -name 'OPERA_L3_DIST-ALERT-S1_*' | rev | cut -d / -f 1 | rev | head -1)
OUTPUT_PRODUCT=$(find output_dir/ -maxdepth 1 -mindepth 1 -type d -name 'OPERA_L3_DIST-ALERT-S1_*' | rev | cut -d / -f 1 | rev | head -1)

The comparison between DIST-S1 products is provided in the SAS Python module. Mount the products into a new, interactive container started to a python interpreter in the DIST conda environment.

docker run --rm -i --tty --entrypoint /opt/conda/envs/dist-s1-env/bin/python \
  -v $(pwd)/test_data/golden_datasets/10SGD:/home/ops/golden_product \
  -v $(pwd)/output_dir:/home/ops/test_product \
  -e OUTPUT_PRODUCT=$OUTPUT_PRODUCT -e GOLDEN_PRODUCT=$GOLDEN_PRODUCT \
  opera/dist-s1:$tag 

Copy the following script into it:

import os
from dist_s1.data_models.output_models import ProductDirectoryData

golden_product_name = os.environ['GOLDEN_PRODUCT']
test_product_name = os.environ['OUTPUT_PRODUCT']

golden_product = ProductDirectoryData.from_product_path(f'/home/ops/golden_product/{golden_product_name}')
test_product = ProductDirectoryData.from_product_path(f'/home/ops/test_product/{test_product_name}')

if golden_product == test_product:
    print('Products match')
else:
    print('Products do not match')

If Products match is printed, the test has passed.

Sample output

$ docker run --rm -i --tty --entrypoint /opt/conda/envs/dist-s1-env/bin/python \
>       -v $(pwd)/test_data/golden_datasets/10SGD:/home/ops/golden_product \
>       -v $(pwd)/output_dir:/home/ops/test_product \
>       -e OUTPUT_PRODUCT=$OUTPUT_PRODUCT -e GOLDEN_PRODUCT=$GOLDEN_PRODUCT \
>       opera/dist-s1:$tag
Python 3.13.3 | packaged by conda-forge | (main, Apr 14 2025, 20:44:03) [GCC 13.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import os
... from dist_s1.data_models.output_models import ProductDirectoryData
...
... golden_product_name = os.environ['GOLDEN_PRODUCT']
... test_product_name = os.environ['OUTPUT_PRODUCT']
...
... golden_product = ProductDirectoryData.from_product_path(f'/home/ops/golden_product/{golden_product_name}')
... test_product = ProductDirectoryData.from_product_path(f'/home/ops/test_product/{test_product_name}')
...
... if golden_product == test_product:
...     print('Products match')
... else:
...     print('Products do not match')
...
Products match
>>>

Executing the unit test suite within the DIST-S1 container

To ensure the dist-s1 container is executing as expected, we can also run its built-in Python unit test suite from within the container:

docker run --rm --entrypoint '/bin/bash' opera/dist-s1:$tag -c -l 'cd dist-s1 && pytest tests'

For this Acceptance Test, we're currently seeing 6 failures in critical test cases, but this seems to be traced to a) the aforementioned optimization issue or b) the test expecting Earthdata Login credentials. This will be referred to ADT.

Below is the current test output:

$ docker run --rm --entrypoint '/bin/bash' opera/dist-s1:$tag -c -l 'cd dist-s1 && pytest tests'
============================= test session starts ==============================
platform linux -- Python 3.13.3, pytest-8.4.0, pluggy-1.6.0
rootdir: /home/ops/dist-s1
configfile: pyproject.toml
plugins: anyio-4.9.0, cov-6.1.1, mock-3.14.1, typeguard-4.4.3
collected 36 items

tests/test_credentials.py ...                                            [  8%]
tests/test_main.py ...                                                   [ 16%]
tests/test_output_models.py .                                            [ 19%]
tests/test_package.py .                                                  [ 22%]
tests/test_proc.py ..........                                            [ 50%]
tests/test_runconfig_model.py .                                          [ 52%]
tests/test_water_mask.py ....                                            [ 63%]
tests/test_workflows.py .............                                    [100%]

=============================== warnings summary ===============================
../../../opt/conda/envs/dist-s1-env/lib/python3.13/site-packages/botocore/utils.py:679
  /opt/conda/envs/dist-s1-env/lib/python3.13/site-packages/botocore/utils.py:679: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC).
    current_time = datetime.datetime.utcnow()

tests/test_main.py::test_dist_s1_sas_main
tests/test_workflows.py::test_dist_s1_sas_workflow
  /opt/conda/envs/dist-s1-env/lib/python3.13/site-packages/dist_s1/processing.py:217: RuntimeWarning: All-NaN slice encountered
    np.nanmax(np.stack([mdist_copol, mdist_crosspol], axis=0), axis=0)

tests/test_main.py::test_dist_s1_sas_main
  /opt/conda/envs/dist-s1-env/lib/python3.13/site-packages/dist_s1/processing.py:152: RuntimeWarning: All-NaN slice encountered
    X_dist_min = np.nanmin(disturbance_stack, axis=0)

tests/test_output_models.py::test_product_directory_data_from_product_path
  /opt/conda/envs/dist-s1-env/lib/python3.13/site-packages/dist_s1/data_models/output_models.py:321: UserWarning: Layer GEN-DIST-STATUS arrays do not match
    warn(f'Layer {layer} arrays do not match', UserWarning)

tests/test_runconfig_model.py::test_input_data_model_from_cropped_dataset
  /home/ops/dist-s1/tests/test_runconfig_model.py:101: FutureWarning: The behavior of DatetimeProperties.to_pydatetime is deprecated, in a future version this will return a Series containing python datetime objects instead of an ndarray. To retain the old behavior, call `np.array` on the result
    pre_acq_dts = np.array(df[ind_pre & ind_burst].acq_dt.dt.to_pydatetime())

tests/test_runconfig_model.py::test_input_data_model_from_cropped_dataset
  /home/ops/dist-s1/tests/test_runconfig_model.py:102: FutureWarning: The behavior of DatetimeProperties.to_pydatetime is deprecated, in a future version this will return a Series containing python datetime objects instead of an ndarray. To retain the old behavior, call `np.array` on the result
    post_acq_dts = np.array(df[ind_post & ind_burst].acq_dt.dt.to_pydatetime())

tests/test_water_mask.py::test_antimeridian_water_mask
  /opt/conda/envs/dist-s1-env/lib/python3.13/site-packages/dem_stitcher/rio_window.py:144: RuntimeWarning: Requesting extent beyond raster bounds of [-181.2105, 61.84075, -178.48, 63.36825]. Shrinking bounds in raster crs to (-178.78238793370625, 62.08806168410328, -178.48, 63.12101618031541).
    warn(

-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
================== 36 passed, 8 warnings in 284.37s (0:04:44) ==================
⚠️ **GitHub.com Fallback** ⚠️