Skip to content

Commit 0d9fb1a

Browse files
committed
Add support for adjoint testing
This is an attempt to support adjoint testing without redoing each adjoint run twice and providing more stability and less fragility. A new helper function has been added to the AMRClaw test python module. Hopefully this will also work in GeoClaw.
1 parent d6feada commit 0d9fb1a

5 files changed

Lines changed: 152 additions & 133 deletions

File tree

examples/acoustics_1d_adjoint/adjoint/test_acoustics_1d_adjoint.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#!/usr/bin/env python
12
"""
23
Test for the adjoint problem for 1D acoustics
34
"""
@@ -10,14 +11,14 @@
1011
def test_acoustics_1d_adjoint(tmp_path: Path, save: bool):
1112
"""Acoustics 1D adjoint test"""
1213

13-
ctr = test.AMRClawTestRunner(tmp_path)
14-
ctr.set_data()
15-
ctr.write_data()
16-
ctr.build_executable()
17-
ctr.run_code()
14+
runner = test.AMRClawTestRunner(tmp_path, test_path=Path(__file__).parent)
15+
runner.set_data()
16+
runner.write_data()
17+
runner.build_executable()
18+
runner.run_code()
1819

19-
ctr.check_gauge(gauge_id=0)
20-
ctr.check_gauge(gauge_id=1)
20+
runner.check_gauge(gauge_id=0)
21+
runner.check_gauge(gauge_id=1)
2122

2223

2324
if __name__ == "__main__":
Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,35 @@
1+
#!/usr/bin/env python
12
"""
23
Regression tests for 1D acoustics with adjoint flagging.
34
"""
45

56
from pathlib import Path
6-
import os
77
import pytest
88

99
import clawpack.amrclaw.test as test
1010

11-
# import adjoint.test_acoustics_1d_adjoint
12-
1311
def test_acoustics_1d_adjoint_forward(tmp_path: Path, save: bool):
1412
"""Test for a 1D acoustics adjoint-flagging forward problem test case"""
15-
16-
ctr = test.AMRClawTestRunner(tmp_path, test_path=__file__)
1713

18-
# Run adjoint problem
19-
adjoint_output = ctr.temp_path / "_adjoint_output"
20-
21-
if not adjoint_output.exists():
22-
os.makedirs(adjoint_output)
23-
adjoint_ctr = test.AMRClawTestRunner(adjoint_output,
24-
test_path=ctr.test_path / "adjoint")
25-
adjoint_ctr.set_data(setrun_path=adjoint_ctr.test_path / "setrun.py")
26-
adjoint_ctr.write_data()
27-
adjoint_ctr.build_executable()
28-
adjoint_ctr.run_code()
29-
30-
# Write problem data
31-
ctr.set_data()
32-
ctr.rundata.adjointdata.adjoint_outdir = adjoint_output
33-
ctr.write_data()
34-
35-
ctr.build_executable()
36-
ctr.run_code()
37-
38-
ctr.check_gauge(gauge_id=0)
39-
ctr.check_gauge(gauge_id=1)
40-
14+
example_path = Path(__file__).parent
15+
adjoint_path = example_path / "adjoint"
16+
adjoint_output = tmp_path / "_adjoint_output"
17+
18+
test.run_example_for_test(
19+
test.AMRClawTestRunner,
20+
adjoint_output,
21+
adjoint_path,
22+
)
23+
24+
runner = test.run_example_for_test(
25+
test.AMRClawTestRunner,
26+
tmp_path,
27+
example_path,
28+
configure=lambda r: setattr(r.rundata.adjointdata, "adjoint_outdir", adjoint_output),
29+
)
30+
31+
runner.check_gauge(0, save=save)
32+
runner.check_gauge(1, save=save)
4133

4234
if __name__ == "__main__":
4335
pytest.main([__file__])
Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,37 @@
1+
#!/usr/bin/env python
12
"""
23
Test for the adjoint problem for 2D acoustics
34
"""
45

5-
import sys
6-
import unittest
6+
from pathlib import Path
7+
import pytest
78

89
import clawpack.amrclaw.test as test
9-
import clawpack.amrclaw.data as data
1010

11-
class Acoustics2DAdjointTest(test.AMRClawRegressionTest):
11+
def configure_2d_adjoint(runner):
12+
clawdata = runner.rundata.clawdata
13+
gaugedata = runner.rundata.gaugedata
1214

13-
def runTest(self, save=False):
15+
clawdata.num_output_times = 30
16+
clawdata.tfinal = 3.0
1417

15-
# Write out data files
16-
self.load_rundata()
18+
gaugedata.gauges = [
19+
[1, 1.0, 1.0, 0.0, 10.0],
20+
[2, 3.5, 0.5, 0.0, 10.0],
21+
]
1722

18-
self.rundata.clawdata.num_output_times = 30
19-
self.rundata.clawdata.tfinal = 3.0
23+
def test_acoustics_2d_adjoint(tmp_path: Path, save: bool):
24+
adjoint_path = Path(__file__).parent
2025

21-
self.rundata.gaugedata.gauges = []
22-
self.rundata.gaugedata.gauges.append([1, 1.0, 1.0, 0., 10.])
23-
self.rundata.gaugedata.gauges.append([2, 3.5, 0.5, 0., 10.])
26+
runner = test.run_example_for_test(
27+
test.AMRClawTestRunner,
28+
tmp_path,
29+
adjoint_path,
30+
configure_runner=configure_2d_adjoint,
31+
)
2432

25-
self.write_rundata_objects()
33+
runner.check_gauge(gauge_id=1, save=save)
34+
runner.check_gauge(gauge_id=2, save=save)
2635

27-
self.run_code()
28-
29-
# Perform Tests
30-
self.check_gauges(save=save, gauge_id=1)
31-
self.check_gauges(save=save, gauge_id=2)
32-
33-
self.success = True
34-
35-
36-
if __name__=="__main__":
37-
if len(sys.argv) > 1:
38-
if bool(sys.argv[1]):
39-
# Fake the setup and save out output
40-
test = Acoustics2DAdjointTest()
41-
try:
42-
test.setUp()
43-
test.runTest(save=True)
44-
finally:
45-
test.tearDown()
46-
sys.exit(0)
47-
unittest.main()
36+
if __name__ == "__main__":
37+
pytest.main([__file__])
Lines changed: 56 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,72 @@
1+
#!/usr/bin/env python
12
"""
23
Regression tests for 2D acoustics with adjoint flagging.
34
"""
45

56
from pathlib import Path
6-
import sys
7-
import shutil
8-
import unittest
7+
import pytest
98

109
import clawpack.amrclaw.test as test
1110

12-
from adjoint.test_acoustics_2d_adjoint import Acoustics2DAdjointTest
11+
def configure_2d_adjoint(runner):
12+
clawdata = runner.rundata.clawdata
13+
gaugedata = runner.rundata.gaugedata
1314

14-
class Acoustics2DAdjointForwardTest(test.AMRClawRegressionTest):
15-
r"""Basic test for a 2D acoustics adjoint-flagging forward problem test case"""
15+
clawdata.num_output_times = 30
16+
clawdata.tfinal = 3.0
1617

18+
gaugedata.gauges = [
19+
[1, 1.0, 1.0, 0.0, 10.0],
20+
[2, 3.5, 0.5, 0.0, 10.0],
21+
]
1722

18-
def runTest(self, save=False):
19-
20-
# Run adjoint problem
21-
try:
22-
adjoint_run = Acoustics2DAdjointTest()
23-
adjoint_run.setUp()
24-
adjoint_run.runTest()
25-
26-
# Copy output to local directory
27-
adjoint_output = Path(self.temp_path) / "_adjoint_output"
23+
def configure_2d_forward(adjoint_output: Path):
24+
def _configure(runner):
25+
clawdata = runner.rundata.clawdata
26+
gaugedata = runner.rundata.gaugedata
27+
amrdata = runner.rundata.amrdata
2828

29-
if Path(adjoint_output).exists():
30-
shutil.rmtree(adjoint_output)
31-
shutil.copytree(adjoint_run.temp_path, adjoint_output)
32-
finally:
33-
adjoint_run.tearDown()
34-
35-
# Write out data files
36-
self.load_rundata()
37-
38-
self.rundata.clawdata.num_output_times = 1
39-
self.rundata.clawdata.tfinal = 3.0
29+
clawdata.num_output_times = 1
30+
clawdata.tfinal = 3.0
4031

4132
# Test gauges
42-
self.rundata.gaugedata.gauges = []
43-
self.rundata.gaugedata.gauges.append([1, 1.0, 1.0, 0., 1e9])
44-
self.rundata.gaugedata.gauges.append([2, 3.5, 0.5, 0., 1e9])
33+
gaugedata.gauges = []
34+
gaugedata.gauges.append([1, 1.0, 1.0, 0., 1e9])
35+
gaugedata.gauges.append([2, 3.5, 0.5, 0., 1e9])
4536

4637
# AMR parameters
47-
self.rundata.amrdata.amr_levels_max = 2
48-
self.rundata.amrdata.refinement_ratios_x = [2]
49-
self.rundata.amrdata.refinement_ratios_y = [2]
50-
self.rundata.amrdata.refinement_ratios_t = [2]
51-
self.rundata.amrdata.flag_richardson_tol = 1e-5
52-
self.rundata.amrdata.flag2refine_tol = 0.02
53-
54-
# Look for adjoint data
55-
self.rundata.adjointdata.adjoint_outdir = adjoint_output.resolve()
56-
57-
self.write_rundata_objects()
58-
59-
# Run code
60-
self.run_code()
61-
62-
# Perform tests
63-
self.check_gauges(save=save, gauge_id=1)
64-
self.check_gauges(save=save, gauge_id=2)
65-
66-
self.success = True
67-
68-
69-
if __name__=="__main__":
70-
if len(sys.argv) > 1:
71-
if bool(sys.argv[1]):
72-
# Fake the setup and save out output
73-
test = Acoustics2DAdjointForwardTest()
74-
try:
75-
test.setUp()
76-
test.runTest(save=True)
77-
finally:
78-
test.tearDown()
79-
sys.exit(0)
80-
unittest.main()
38+
amrdata.amr_levels_max = 2
39+
amrdata.refinement_ratios_x = [2]
40+
amrdata.refinement_ratios_y = [2]
41+
amrdata.refinement_ratios_t = [2]
42+
amrdata.flag_richardson_tol = 1e-5
43+
amrdata.flag2refine_tol = 0.02
44+
45+
runner.rundata.adjointdata.adjoint_outdir = adjoint_output
46+
47+
return _configure
48+
49+
def test_acoustics_2d_adjoint_forward(tmp_path: Path, save: bool):
50+
example_path = Path(__file__).parent
51+
adjoint_path = example_path / "adjoint"
52+
adjoint_output = tmp_path / "_adjoint_output"
53+
54+
test.run_example_for_test(
55+
test.AMRClawTestRunner,
56+
adjoint_output,
57+
adjoint_path,
58+
configure_runner=configure_2d_adjoint,
59+
)
60+
61+
runner = test.run_example_for_test(
62+
test.AMRClawTestRunner,
63+
tmp_path,
64+
example_path,
65+
configure_runner=configure_2d_forward(adjoint_output),
66+
)
67+
68+
runner.check_gauge(gauge_id=1, save=save)
69+
runner.check_gauge(gauge_id=2, save=save)
70+
71+
if __name__ == "__main__":
72+
pytest.main([__file__])

src/python/amrclaw/test.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,47 @@ class AMRClawTestRunner(test.ClawpackTestRunner):
2222
def __init__(self, path: Path, test_path: Optional[Path]=None):
2323
super(AMRClawTestRunner, self).__init__(path, test_path=test_path)
2424
self.executable_name = 'xamr'
25+
26+
27+
# Useful for running tests that need more than one example to be run, e.g. an
28+
# adjoint test that needs to run the forward problem first.
29+
def run_example_for_test(
30+
runner_cls,
31+
output_path: Path,
32+
test_path: Path,
33+
*,
34+
setrun_path: Path | None = None,
35+
configure_runner=None,
36+
build_kwargs: dict | None = None,
37+
):
38+
"""
39+
Build and run one example in a specified output directory.
40+
41+
Parameters
42+
----------
43+
runner_cls
44+
Runner class, e.g. AMRClawTestRunner.
45+
output_path
46+
Temporary directory for data files, executable, and output.
47+
test_path
48+
Path to the example directory.
49+
setrun_path
50+
Optional explicit path to setrun.py.
51+
configure
52+
Optional callback taking the runner after set_data() and before
53+
write_data().
54+
build_kwargs
55+
Optional keyword arguments passed to build_executable().
56+
"""
57+
output_path.mkdir(parents=True, exist_ok=True)
58+
59+
runner = runner_cls(output_path, test_path=test_path)
60+
runner.set_data(setrun_path=setrun_path)
61+
62+
if configure_runner is not None:
63+
configure_runner(runner)
64+
65+
runner.write_data()
66+
runner.build_executable(**(build_kwargs or {}))
67+
runner.run_code()
68+
return runner

0 commit comments

Comments
 (0)