Skip to content

Commit d23fd52

Browse files
yzheng6GitHub Enterprise
authored andcommitted
End2end workflow (#802)
* Added end-to-end workflow in CI workflow test suite * Added two end-to-end tests datasets: ALOS PALSAR data over Chile and Rosamond
1 parent 6441f9a commit d23fd52

18 files changed

+1950
-26
lines changed

tools/imagesets/centos7conda/distrib_nisar/Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ RUN cd /opt \
1111
&& git clone https://[email protected]/NISAR-ADT/QualityAssurance \
1212
&& git clone https://[email protected]/NISAR-ADT/CFChecker \
1313
&& git clone https://[email protected]/NISAR-ADT/calTools \
14-
&& cd /opt/QualityAssurance && git checkout cd455fd && rm -rf .git \
15-
&& cd /opt/CFChecker && git checkout R1.1 && rm -rf .git \
14+
&& cd /opt/QualityAssurance && git checkout R2 && rm -rf .git \
15+
&& cd /opt/CFChecker && git checkout R2 && rm -rf .git \
1616
&& cd /opt/calTools && git checkout R2 && rm -rf .git
1717

1818
FROM $distrib_img

tools/imagesets/imgset.py

Lines changed: 93 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ def distribrun(self, testdir, cmd, logfile=None, dataname=None, nisarimg=False,
324324
--rm -i {self.tty} {img} sh -ci"
325325
run_with_logging(dockercall, cmd, logger, printlog=self.printlog)
326326

327-
def workflowtest(self, wfname, testname, dataname, pyname, arg=""): # hmmmmmmmmm
327+
def workflowtest(self, wfname, testname, dataname, pyname, suf="", description="", arg=""):
328328
"""
329329
Run the specified workflow test using the distrib image.
330330
@@ -334,22 +334,42 @@ def workflowtest(self, wfname, testname, dataname, pyname, arg=""): # hmmmmmmmmm
334334
Workflow name (e.g. "rslc")
335335
testname : str
336336
Workflow test name (e.g. "RSLC_REE1")
337-
dataname : str
338-
Test input data (e.g. "L0B_RRSD_REE1")
337+
dataname : str or iterable of str or None
338+
Test input dataset(s) to be mounted (e.g. "L0B_RRSD_REE1", ["L0B_RRSD_REE1", "L0B_RRSD_REE2"]).
339+
If None, no input datasets are used.
339340
pyname : str
340341
Name of the isce3 module to execute (e.g. "pybind_nisar.workflows.focus")
342+
suf: str
343+
Suffix in runconfig and output directory name to differentiate between
344+
reference and secondary data in end-to-end tests
345+
description: str
346+
Extra test description to print out to differentiate between
347+
reference and secondary data in end-to-end tests
341348
arg : str, optional
342349
Additional command line argument(s) to pass to the workflow
343350
"""
344-
print(f"\nRunning workflow test {testname}\n")
351+
print(f"\nRunning workflow test {testname}{description}\n")
345352
testdir = os.path.abspath(pjoin(self.testdir, testname))
346-
os.makedirs(pjoin(testdir, f"output_{wfname}"), exist_ok=True)
347-
os.makedirs(pjoin(testdir, f"scratch_{wfname}"), exist_ok=True)
348-
# copy test runconfig to test directory
349-
shutil.copyfile(pjoin(runconfigdir, f"{testname}.yaml"),
350-
pjoin(testdir, f"runconfig_{wfname}.yaml"))
351-
log = pjoin(testdir, f"output_{wfname}", "stdouterr.log")
352-
cmd = [f"time python3 -m {pyname} {arg} runconfig_{wfname}.yaml"]
353+
# create input directories before docker volume mount to avoid root ownership
354+
# of these directories
355+
if dataname is not None:
356+
if type(dataname) is not list:
357+
dataname = [dataname]
358+
for data in dataname:
359+
os.makedirs(pjoin(testdir, f"input_{data}"), exist_ok=True)
360+
# create output directories
361+
os.makedirs(pjoin(testdir, f"output_{wfname}{suf}"), exist_ok=True)
362+
os.makedirs(pjoin(testdir, f"scratch_{wfname}{suf}"), exist_ok=True)
363+
# copy test runconfig to test directory (for end-to-end testing, we need to
364+
# distinguish between the runconfig files for each individual workflow)
365+
if testname.startswith("end2end"):
366+
inputrunconfig = f"{testname}_{wfname}{suf}.yaml"
367+
else:
368+
inputrunconfig = f"{testname}{suf}.yaml"
369+
shutil.copyfile(pjoin(runconfigdir, inputrunconfig),
370+
pjoin(testdir, f"runconfig_{wfname}{suf}.yaml"))
371+
log = pjoin(testdir, f"output_{wfname}{suf}", "stdouterr.log")
372+
cmd = [f"time python3 -m {pyname} {arg} runconfig_{wfname}{suf}.yaml"]
353373
try:
354374
self.distribrun(testdir, cmd, logfile=log, dataname=dataname,
355375
loghdlrname=f'wftest.{os.path.basename(testdir)}')
@@ -380,6 +400,32 @@ def insartest(self, tests=None):
380400
for testname, dataname in tests:
381401
self.workflowtest("insar", testname, dataname, "pybind_nisar.workflows.insar", arg="--restart")
382402

403+
def end2endtest(self, tests=None):
404+
"""
405+
Run all workflows for one pair of L0B input data, including RSLC, GSLC, GCOV, RIFG, RUNW, GUNW.
406+
The GSLC, GCOV, and InSAR products are generated from outputs of the RSLC workflow.
407+
"""
408+
if tests is None:
409+
tests = workflowtests['end2end'].items()
410+
for testname, dataname in tests:
411+
# copy runconfigs and create output direcotories
412+
testdir = os.path.abspath(pjoin(self.testdir, testname))
413+
for wfname in ['rslc', 'gslc', 'gcov', 'insar']:
414+
if wfname == 'rslc':
415+
pyname = 'pybind_nisar.workflows.focus'
416+
else:
417+
pyname = f'pybind_nisar.workflows.{wfname}'
418+
419+
if wfname == 'insar':
420+
self.workflowtest(wfname, testname, dataname, pyname, arg="--restart",
421+
description=" InSAR product")
422+
else:
423+
self.workflowtest(wfname, testname, dataname, pyname, suf="_ref",
424+
description=f" {wfname.upper()} reference product")
425+
self.workflowtest(wfname, testname, dataname, pyname, suf="_sec",
426+
description=f" {wfname.upper()} secondary product")
427+
428+
383429
def noisesttest(self, tests=None):
384430
if tests is None:
385431
tests = workflowtests['noisest'].items()
@@ -443,7 +489,7 @@ def mintests(self):
443489
self.ptatest(tests=list(workflowtests['pta'].items())[:1])
444490
self.beamformtest(tests=list(workflowtests['beamform'].items())[:1])
445491

446-
def workflowqa(self, wfname, testname):
492+
def workflowqa(self, wfname, testname, suf="", description=""):
447493
"""
448494
Run QA and CF compliance checking for the specified workflow using the NISAR distrib image.
449495
@@ -453,15 +499,22 @@ def workflowqa(self, wfname, testname):
453499
Workflow name (e.g. "rslc")
454500
testname: str
455501
Workflow test name (e.g. "RSLC_REE1")
456-
"""
457-
print(f"\nRunning workflow QA on test {testname}\n")
502+
suf: str
503+
Suffix in runconfig and output directory name to differentiate between
504+
reference and secondary data in end-to-end tests
505+
description: str
506+
Extra test description to print out to differentiate between
507+
reference and secondary data in end-to-end tests
508+
"""
509+
print(f"\nRunning workflow QA on test {testname}{description}\n")
458510
testdir = os.path.abspath(pjoin(self.testdir, testname))
459-
os.makedirs(pjoin(testdir, f"qa_{wfname}"), exist_ok=True)
460-
log = pjoin(testdir, f"qa_{wfname}", "stdouterr.log")
461-
cmd = [f"time cfchecks.py output_{wfname}/{wfname}.h5",
462-
f"""time verify_{wfname}.py --fpdf qa_{wfname}/graphs.pdf \
463-
--fhdf qa_{wfname}/stats.h5 --flog qa_{wfname}/qa.log --validate \
464-
--quality output_{wfname}/{wfname}.h5"""]
511+
os.makedirs(pjoin(testdir, f"qa_{wfname}{suf}"), exist_ok=True)
512+
log = pjoin(testdir, f"qa_{wfname}{suf}", "stdouterr.log")
513+
# run qa command
514+
cmd = [f"time cfchecks.py output_{wfname}{suf}/{wfname}.h5",
515+
f"""time verify_{wfname}.py --fpdf qa_{wfname}{suf}/graphs.pdf \
516+
--fhdf qa_{wfname}{suf}/stats.h5 --flog qa_{wfname}{suf}/qa.log --validate \
517+
--quality output_{wfname}{suf}/{wfname}.h5"""]
465518
try:
466519
self.distribrun(testdir, cmd, logfile=log, nisarimg=True,
467520
loghdlrname=f'wfqa.{os.path.basename(testdir)}')
@@ -515,7 +568,7 @@ def insarqa(self, tests=None):
515568
testdir = os.path.abspath(pjoin(self.testdir, testname))
516569
# run QA for each of the InSAR products
517570
for product in ['rifg', 'runw', 'gunw']:
518-
print(f"\nRunning workflow QA on InSAR test {testname} product {product.upper()}\n")
571+
print(f"\nRunning workflow QA on test {testname} {product.upper()} product\n")
519572
qadir = pjoin(testdir, f"qa_{product}")
520573
os.makedirs(qadir, exist_ok=True)
521574
log = pjoin(qadir,f"stdouterr.log")
@@ -529,11 +582,27 @@ def insarqa(self, tests=None):
529582
loghdlrname=f'wfqa.{os.path.basename(testdir)}.{product}')
530583
except subprocess.CalledProcessError as e:
531584
if product == 'gunw':
532-
raise RuntimeError(f"Workflow QA on InSAR test {testname} product {product.upper()} failed\n") from e
585+
raise RuntimeError(f"Workflow QA on test {testname} {product.upper()} product failed\n") from e
533586
else:
534587
# do not exit since CF checker errors are expected
535-
print(f"Found known errors running CF Checker on InSAR test {testname} product {product.upper()}\n")
536-
588+
print(f"Found known errors running CF Checker on test {testname} {product.upper()} product\n")
589+
590+
def end2endqa(self, tests=None):
591+
"""
592+
Run QA on all end2end workflow test results for one pair of L0B input data, including RSLC, GSLC, GCOV,
593+
RIFG, RUNW, GUNW.
594+
595+
"""
596+
if tests is None:
597+
tests = workflowtests['end2end'].items()
598+
for testname, dataname in tests:
599+
for wfname in ['rslc', 'gslc', 'gcov']:
600+
for suf, descr in [('_ref', 'reference'), ('_sec', 'secondary')]:
601+
self.workflowqa(wfname, testname, suf=suf, description=f' {wfname.upper()} {descr} product')
602+
603+
self.insarqa([testname])
604+
605+
537606
def minqa(self):
538607
"""
539608
Only run qa for first test in each workflow
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
runconfig:
2+
name: NISAR_L2-L-GCOV_RUNCONFIG
3+
4+
groups:
5+
PGENameGroup:
6+
PGEName: GCOV_L_PGE
7+
8+
InputFileGroup:
9+
# REQUIRED - One NISAR L1B RSLC formatted HDF5 file
10+
InputFilePath:
11+
- output_rslc_ref/rslc.h5
12+
13+
DynamicAncillaryFileGroup:
14+
# REQUIRED - Use the provided DEM as input
15+
DEMFile: input_L0B_RRSD_ALPSRP110160680_Rosamond/dem.tif
16+
17+
ProductPathGroup:
18+
# Directory where PGE will place results. Irrelevant to SAS.
19+
ProductPath: output_gcov_ref
20+
21+
# Directory where SAS can write temporary data
22+
ScratchPath: scratch_gcov_ref
23+
24+
# SAS writes output product to the following file. PGE may rename.
25+
# NOTE: For R2 will need to handle mixed-mode case with multiple outputs of RSLC workflow.
26+
SASOutputFile: output_gcov_ref/gcov.h5
27+
28+
PrimaryExecutable:
29+
ProductType: GCOV
30+
31+
DebugLevelGroup:
32+
DebugSwitch: false
33+
34+
#adt section - isce3 + pyre workflow
35+
processing:
36+
# OPTIONAL - Frequencies and polarisations to be processed
37+
input_subset:
38+
# OPTIONAL - List of frequencies to process. Default empty representing all
39+
list_of_frequencies:
40+
# keys for frequency A and B are required.
41+
# valid options for polarizations
42+
# 'all' for all polarizations found in RSLC
43+
# [polarizations] for list of specific frequency(s) e.g. [HH, HV] or [HH]
44+
# empty for no polarizations
45+
A:
46+
47+
# OPTIONAL - If we want full covariance instead of diagonals only. Default False
48+
fullcovariance: False
49+
50+
# TODO OPTIONAL - Only checked when internet access is available
51+
dem_download:
52+
# OPTIONAL - s3 bucket / curl URL / local file
53+
source:
54+
top_left:
55+
x:
56+
y:
57+
bottom_right:
58+
x:
59+
y:
60+
61+
# OPTIONAL - if amplitude data needs to be mulitlooked before GCOV generation
62+
pre_process:
63+
azimuth_looks: 1
64+
range_looks: 1
65+
66+
# OPTIONAL - to control behavior of RTC module
67+
rtc:
68+
output_type: gamma0
69+
70+
# OPTIONAL - Choices:
71+
# "bilinear_distribution" (default)
72+
# "area_projection"
73+
algorithm_type: area_projection
74+
75+
# OPTIONAL - Choices:
76+
# "beta0" (default)
77+
# "sigma0"
78+
input_terrain_radiometry: sigma0
79+
80+
# OPTIONAL - Minimum RTC area factor in dB
81+
rtc_min_value_db: -30
82+
83+
# OPTIONAL - Mechanism to specify output posting and DEM
84+
geocode:
85+
# OPTIONAL -
86+
algorithm_type: area_projection
87+
88+
# OPTIONAL - Choices: "single_block", "geogrid", "geogrid_radargrid", and "auto" (default)
89+
memory_mode:
90+
91+
# OPTIONAL - Processing upsampling factor applied to input geogrid
92+
geogrid_upsampling: 1
93+
94+
# OPTIONAL - absolute radiometric correction
95+
abs_rad_cal: 1
96+
97+
save_nlooks: True
98+
save_rtc: True
99+
100+
# OPTIONAL - Same as input DEM if not provided.
101+
outputEPSG:
102+
103+
# OPTIONAL - Spacing between pixels, in same units as output EPSG.
104+
# If no provided, values will match spacing in provided DEM
105+
output_posting:
106+
A:
107+
x_posting:
108+
y_posting:
109+
B:
110+
x_posting:
111+
y_posting:
112+
113+
# OPTIONAL - To control output grid in same units as output EPSG
114+
y_snap:
115+
116+
# OPTIONAL - To control output grid in same units as output EPSG
117+
x_snap:
118+
119+
# OPTIONAL - Can control with absolute values or with snap values
120+
top_left:
121+
# OPTIONAL - Set top-left y in same units as output EPSG
122+
y_abs:
123+
# OPTIONAL - Set top-left x in same units as output EPSG
124+
x_abs:
125+
126+
# OPTIONAL - Can control with absolute values or with snap values
127+
bottom_right:
128+
y_abs:
129+
x_abs:
130+
131+
# OPTIONAL - if noise correction desired (for ISRO)
132+
noise_correction:
133+
# OPTIONAL -
134+
apply_correction: False
135+
136+
# OPTIONAL -
137+
correction_type:
138+
139+
# OPTIONAL - To setup type of worker
140+
worker:
141+
# OPTIONAL - To prevent downloading DEM / other data automatically. Default True
142+
internet_access: False
143+
144+
# OPTIONAL - To explicitly use GPU capability if available. Default False
145+
gpu_enabled: False
146+
# TODO
147+
#gpu_id: 0
148+
149+
# OPTIONAL - Location to save logs, use runconfig*_yaml.log if not specified
150+
logging:
151+
path: output_gcov_ref/gcov.log
152+
write_mode: 'w'
153+

0 commit comments

Comments
 (0)