你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-07 15:10:13 +08:00
Add geometry object reframe tests
这个提交包含在:
@@ -9,9 +9,11 @@ from typing_extensions import TYPE_CHECKING
|
||||
|
||||
from reframe_tests.tests.base_tests import GprMaxBaseTest
|
||||
from reframe_tests.tests.regression_checks import (
|
||||
GeometryObjectMaterialsRegressionCheck,
|
||||
GeometryObjectRegressionCheck,
|
||||
GeometryViewRegressionCheck,
|
||||
H5RegressionCheck,
|
||||
ReceiverRegressionCheck,
|
||||
RegressionCheck,
|
||||
SnapshotRegressionCheck,
|
||||
)
|
||||
|
||||
@@ -38,7 +40,7 @@ class ReceiverMixin(GprMaxMixin):
|
||||
)
|
||||
self.regression_checks.append(regression_check)
|
||||
else:
|
||||
regression_check = RegressionCheck(self.output_file, reference_file)
|
||||
regression_check = H5RegressionCheck(self.output_file, reference_file)
|
||||
self.regression_checks.append(regression_check)
|
||||
|
||||
|
||||
@@ -86,6 +88,61 @@ class GeometryOnlyMixin(GprMaxMixin):
|
||||
self.executable_opts += ["--geometry-only"]
|
||||
|
||||
|
||||
class GeometryObjectMixin(GprMaxMixin):
|
||||
"""Add regression tests for geometry objects.
|
||||
|
||||
Attributes:
|
||||
geometry_objects (list[str]): List of geometry objects to run
|
||||
regression checks on.
|
||||
"""
|
||||
|
||||
geometry_objects = variable(typ.List[str], value=[])
|
||||
|
||||
def build_geometry_object_filepath(self, geometry_object: str) -> Path:
|
||||
"""Build filepath to the specified geometry object.
|
||||
|
||||
Args:
|
||||
geometry_object: Name of the geometry object.
|
||||
"""
|
||||
return Path(geometry_object).with_suffix(".h5")
|
||||
|
||||
def build_materials_filepath(self, geometry_object: str) -> Path:
|
||||
"""Build filepath to the materials output by the geometry object.
|
||||
|
||||
Args:
|
||||
geometry_object: Name of the geometry object.
|
||||
"""
|
||||
return Path(f"{geometry_object}_materials").with_suffix(".txt")
|
||||
|
||||
@run_after("setup")
|
||||
def add_geometry_object_regression_checks(self):
|
||||
"""Add a regression check for each geometry object.
|
||||
|
||||
The test will be skipped if no geometry objects have been specified.
|
||||
"""
|
||||
self.skip_if(
|
||||
len(self.geometry_objects) < 0,
|
||||
f"Must provide a list of geometry objects.",
|
||||
)
|
||||
|
||||
for geometry_object in self.geometry_objects:
|
||||
# Add materials regression check first as if this fails,
|
||||
# checking the .h5 file will almost definitely fail.
|
||||
materials_file = self.build_materials_filepath(geometry_object)
|
||||
materials_reference_file = self.build_reference_filepath(
|
||||
materials_file.name, suffix=materials_file.suffix
|
||||
)
|
||||
materials_regression_check = GeometryObjectMaterialsRegressionCheck(
|
||||
materials_file, materials_reference_file
|
||||
)
|
||||
self.regression_checks.append(materials_regression_check)
|
||||
|
||||
geometry_object_file = self.build_geometry_object_filepath(geometry_object)
|
||||
reference_file = self.build_reference_filepath(geometry_object)
|
||||
regression_check = GeometryObjectRegressionCheck(geometry_object_file, reference_file)
|
||||
self.regression_checks.append(regression_check)
|
||||
|
||||
|
||||
class GeometryViewMixin(GprMaxMixin):
|
||||
"""Add regression tests for geometry views.
|
||||
|
||||
|
@@ -9,7 +9,7 @@ from reframe.utility import osext
|
||||
|
||||
|
||||
class RegressionCheck:
|
||||
"""Compare two hdf5 files using h5diff"""
|
||||
"""Compare two files using diff"""
|
||||
|
||||
def __init__(
|
||||
self, output_file: Union[str, PathLike], reference_file: Union[str, PathLike]
|
||||
@@ -23,7 +23,8 @@ class RegressionCheck:
|
||||
"""
|
||||
self.output_file = Path(output_file)
|
||||
self.reference_file = Path(reference_file)
|
||||
self.h5diff_options: list[str] = []
|
||||
self.cmd = "diff"
|
||||
self.options: list[str] = []
|
||||
|
||||
@property
|
||||
def error_msg(self) -> str:
|
||||
@@ -57,26 +58,22 @@ class RegressionCheck:
|
||||
return sn.path_isfile(self.reference_file)
|
||||
|
||||
def run(self) -> Literal[True]:
|
||||
"""Run the regression check using h5diff.
|
||||
"""Run the regression check.
|
||||
|
||||
Returns:
|
||||
check_passed: Returns True if the output file matches the
|
||||
reference file (i.e. no output from h5diff). Otherwise,
|
||||
reference file (i.e. no output from diff). Otherwise,
|
||||
raises a SanityError.
|
||||
|
||||
Raises:
|
||||
reframe.core.exceptions.SanityError: If the output file does
|
||||
not exist, or the regression check fails.
|
||||
"""
|
||||
if runtime().system.name == "archer2":
|
||||
h5diff = "/opt/cray/pe/hdf5/default/bin/h5diff"
|
||||
else:
|
||||
h5diff = "h5diff"
|
||||
|
||||
h5diff_output = osext.run_command(
|
||||
completed_process = osext.run_command(
|
||||
[
|
||||
h5diff,
|
||||
*self.h5diff_options,
|
||||
self.cmd,
|
||||
*self.options,
|
||||
str(self.output_file.absolute()),
|
||||
str(self.reference_file),
|
||||
]
|
||||
@@ -86,16 +83,29 @@ class RegressionCheck:
|
||||
sn.path_isfile(self.output_file),
|
||||
f"Expected output file '{self.output_file}' does not exist",
|
||||
) and sn.assert_false(
|
||||
h5diff_output.stdout,
|
||||
completed_process.stdout,
|
||||
(
|
||||
f"{self.error_msg}\n"
|
||||
f"For more details run: '{' '.join(h5diff_output.args)}'\n"
|
||||
f"For more details run: '{' '.join(completed_process.args)}'\n"
|
||||
f"To re-create regression file, delete '{self.reference_file}' and rerun the test."
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class ReceiverRegressionCheck(RegressionCheck):
|
||||
class H5RegressionCheck(RegressionCheck):
|
||||
"""Compare two hdf5 files using h5diff"""
|
||||
|
||||
def __init__(
|
||||
self, output_file: Union[str, PathLike], reference_file: Union[str, PathLike]
|
||||
) -> None:
|
||||
super().__init__(output_file, reference_file)
|
||||
if runtime().system.name == "archer2":
|
||||
self.cmd = "/opt/cray/pe/hdf5/default/bin/h5diff"
|
||||
else:
|
||||
self.cmd = "h5diff"
|
||||
|
||||
|
||||
class ReceiverRegressionCheck(H5RegressionCheck):
|
||||
"""Run regression check on individual reveivers in output files.
|
||||
|
||||
This can include arbitrary receivers in each file, or two receivers
|
||||
@@ -106,7 +116,7 @@ class ReceiverRegressionCheck(RegressionCheck):
|
||||
self,
|
||||
output_file: Union[str, PathLike],
|
||||
reference_file: Union[str, PathLike],
|
||||
output_receiver: Optional[str],
|
||||
output_receiver: str,
|
||||
reference_receiver: Optional[str] = None,
|
||||
) -> None:
|
||||
"""Create a new receiver regression check.
|
||||
@@ -125,26 +135,42 @@ class ReceiverRegressionCheck(RegressionCheck):
|
||||
self.output_receiver = output_receiver
|
||||
self.reference_receiver = reference_receiver
|
||||
|
||||
self.h5diff_options.append(f"rxs/{self.output_receiver}")
|
||||
self.options.append(f"rxs/{self.output_receiver}")
|
||||
if self.reference_receiver is not None:
|
||||
self.h5diff_options.append(f"rxs/{self.reference_receiver}")
|
||||
self.options.append(f"rxs/{self.reference_receiver}")
|
||||
|
||||
@property
|
||||
def error_msg(self) -> str:
|
||||
return f"Receiver '{self.output_receiver}' failed regression check"
|
||||
|
||||
|
||||
class SnapshotRegressionCheck(RegressionCheck):
|
||||
class SnapshotRegressionCheck(H5RegressionCheck):
|
||||
"""Run regression check on a gprMax Snapshot."""
|
||||
|
||||
@property
|
||||
def error_msg(self) -> str:
|
||||
return f"Snapshot '{self.output_file.name}' failed regression check "
|
||||
return f"Snapshot '{self.output_file.name}' failed regression check"
|
||||
|
||||
|
||||
class GeometryViewRegressionCheck(RegressionCheck):
|
||||
class GeometryObjectRegressionCheck(H5RegressionCheck):
|
||||
"""Run regression check on a GprMax GeometryObject."""
|
||||
|
||||
@property
|
||||
def error_msg(self) -> str:
|
||||
return f"GeometryObject '{self.output_file.name}' failed regression check"
|
||||
|
||||
|
||||
class GeometryObjectMaterialsRegressionCheck(RegressionCheck):
|
||||
"""Run regression check on materials output by a GeometryObject."""
|
||||
|
||||
@property
|
||||
def error_msg(self) -> str:
|
||||
return f"GeometryObject materials file '{self.output_file}' failed regression check"
|
||||
|
||||
|
||||
class GeometryViewRegressionCheck(H5RegressionCheck):
|
||||
"""Run regression check on a GprMax GeometryView."""
|
||||
|
||||
@property
|
||||
def error_msg(self) -> str:
|
||||
return f"GeometryView '{self.output_file.name}' failed regression check "
|
||||
return f"GeometryView '{self.output_file.name}' failed regression check"
|
||||
|
@@ -0,0 +1,17 @@
|
||||
#title: Hertzian dipole in free-space
|
||||
#domain: 0.100 0.100 0.100
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandot 1 1e9 myWave
|
||||
#hertzian_dipole: z 0.050 0.050 0.050 myWave
|
||||
|
||||
#material: 4.9 0 1 0 myWater
|
||||
#material: 2 0 1.4 0 unusedMaterial
|
||||
#material: 3 0 2 0 boxMaterial
|
||||
|
||||
#sphere: 0.05 0.05 0.05 0.03 myWater y
|
||||
#box: 0.01 0.01 0.01 0.09 0.025 0.025 boxMaterial y
|
||||
|
||||
#geometry_objects_write: 0.02 0.02 0.02 0.06 0.06 0.06 partial_volume
|
||||
#geometry_objects_write: 0 0 0 0.1 0.1 0.1 full_volume
|
@@ -1,5 +1,6 @@
|
||||
from reframe_tests.tests.base_tests import GprMaxBaseTest
|
||||
from reframe_tests.tests.mixins import (
|
||||
GeometryObjectMixin,
|
||||
GeometryOnlyMixin,
|
||||
GeometryViewMixin,
|
||||
ReceiverMixin,
|
||||
@@ -17,3 +18,7 @@ class GprMaxSnapshotTest(SnapshotMixin, GprMaxBaseTest):
|
||||
|
||||
class GprMaxGeometryViewTest(GeometryViewMixin, GeometryOnlyMixin, GprMaxBaseTest):
|
||||
pass
|
||||
|
||||
|
||||
class GprMaxGeometryObjectTest(GeometryObjectMixin, GeometryOnlyMixin, GprMaxBaseTest):
|
||||
pass
|
||||
|
@@ -0,0 +1,20 @@
|
||||
import reframe as rfm
|
||||
from reframe.core.builtins import parameter
|
||||
|
||||
from reframe_tests.tests.mixins import MpiMixin
|
||||
from reframe_tests.tests.standard_tests import GprMaxGeometryObjectTest
|
||||
|
||||
|
||||
@rfm.simple_test
|
||||
class TestGeometryObject(GprMaxGeometryObjectTest):
|
||||
tags = {"test", "serial", "geometry only", "geometry object"}
|
||||
sourcesdir = "src/geometry_object_tests"
|
||||
model = parameter(["geometry_object_write"])
|
||||
geometry_objects = ["partial_volume", "full_volume"]
|
||||
|
||||
|
||||
@rfm.simple_test
|
||||
class TestGeometryObjectMPI(MpiMixin, TestGeometryObject):
|
||||
tags = {"test", "mpi", "geometry only", "geometry object"}
|
||||
mpi_layout = parameter([[2, 2, 2], [4, 4, 1]])
|
||||
test_dependency = TestGeometryObject
|
在新工单中引用
屏蔽一个用户