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.
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
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"
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
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
, andpost_rtc_crosspol
), replacetest_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
-
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.
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.
$ 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
>>>
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) ==================