你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-07 15:10:13 +08:00
Seperate out tests and benchmarks
这个提交包含在:
@@ -1,12 +1,13 @@
|
||||
"""ReFrame base classes for GprMax tests"""
|
||||
import os
|
||||
import pathlib
|
||||
|
||||
import reframe as rfm
|
||||
import reframe.utility.sanity as sn
|
||||
from reframe.core.builtins import performance_function, require_deps, run_after, sanity_function
|
||||
from reframe.utility import udeps
|
||||
|
||||
from configuration.user_config import GPRMAX_ROOT_DIR
|
||||
|
||||
|
||||
GPRMAX_ROOT_DIR = pathlib.Path(__file__).parent.parent.resolve()
|
||||
PATH_TO_PYENV = os.path.join(".venv", "bin", "activate")
|
||||
|
||||
|
||||
@@ -19,7 +20,7 @@ class CreatePyenvTest(rfm.RunOnlyRegressionTest):
|
||||
prerun_cmds = [
|
||||
"python -m venv --system-site-packages --prompt gprMax .venv",
|
||||
f"source {PATH_TO_PYENV}",
|
||||
f"pip install -r {os.path.join(GPRMAX_ROOT_DIR, 'requirements.txt')}"
|
||||
f"pip install -r {os.path.join(GPRMAX_ROOT_DIR, 'requirements.txt')}",
|
||||
]
|
||||
executable = f"pip install -e {GPRMAX_ROOT_DIR}"
|
||||
|
||||
@@ -29,11 +30,13 @@ class CreatePyenvTest(rfm.RunOnlyRegressionTest):
|
||||
Check packages successfully installed from requirements.txt
|
||||
Check gprMax installed successfully and no other errors thrown
|
||||
"""
|
||||
return sn.assert_found(r"Successfully installed (?!gprMax)", self.stdout, "Failed to install requirements") \
|
||||
and sn.assert_found(r"Successfully installed gprMax", self.stdout, "Failed to install gprMax") \
|
||||
and sn.assert_not_found(r"finished with status 'error'", self.stdout) \
|
||||
and sn.assert_not_found(r"ERROR:", self.stderr)
|
||||
|
||||
return (
|
||||
sn.assert_found(r"Successfully installed (?!gprMax)", self.stdout, "Failed to install requirements")
|
||||
and sn.assert_found(r"Successfully installed gprMax", self.stdout, "Failed to install gprMax")
|
||||
and sn.assert_not_found(r"finished with status 'error'", self.stdout)
|
||||
and sn.assert_not_found(r"ERROR:", self.stderr)
|
||||
)
|
||||
|
||||
|
||||
class GprmaxBaseTest(rfm.RunOnlyRegressionTest):
|
||||
valid_systems = ["archer2:compute"]
|
||||
@@ -56,44 +59,39 @@ class GprmaxBaseTest(rfm.RunOnlyRegressionTest):
|
||||
"""Add prerun command to load the built Python environment"""
|
||||
path_to_pyenv = os.path.join(CreatePyenvTest(part="login").stagedir, PATH_TO_PYENV)
|
||||
self.prerun_cmds.append(f"source {path_to_pyenv}")
|
||||
|
||||
|
||||
@sanity_function
|
||||
def test_simulation_complete(self):
|
||||
"""Check simulation completed successfully"""
|
||||
# TODO: Check for correctness/regression rather than just completing
|
||||
return sn.assert_found(r"=== Simulation completed in ", self.stdout)
|
||||
|
||||
@performance_function('s', perf_key='run_time')
|
||||
|
||||
@performance_function("s", perf_key="run_time")
|
||||
def extract_run_time(self):
|
||||
"""Extract total runtime"""
|
||||
return sn.extractsingle(
|
||||
r'real\s+(?P<run_time>\S+)',
|
||||
self.stderr,
|
||||
"run_time",
|
||||
float
|
||||
)
|
||||
|
||||
@performance_function('s', perf_key='simulation_time')
|
||||
return sn.extractsingle(r"real\s+(?P<run_time>\S+)", self.stderr, "run_time", float)
|
||||
|
||||
@performance_function("s", perf_key="simulation_time")
|
||||
def extract_simulation_time(self):
|
||||
"""Extract simulation time reported by gprMax"""
|
||||
|
||||
# sn.extractall throws an error if a group has value None.
|
||||
# Therefore have to handle the < 1 min and >= 1 min cases separately.
|
||||
if sn.extractsingle(r"=== Simulation completed in \S+ (?P<case>minute|seconds)", self.stdout, "case") == "minute":
|
||||
if (
|
||||
sn.extractsingle(r"=== Simulation completed in \S+ (?P<case>minute|seconds)", self.stdout, "case")
|
||||
== "minute"
|
||||
):
|
||||
simulation_time = sn.extractall(
|
||||
r"=== Simulation completed in (?P<minutes>\S+) minutes? and (?P<seconds>\S+) seconds =*",
|
||||
self.stdout,
|
||||
["minutes", "seconds"],
|
||||
float
|
||||
float,
|
||||
)
|
||||
minutes = simulation_time[0][0]
|
||||
seconds = simulation_time[0][1]
|
||||
else:
|
||||
minutes = 0
|
||||
seconds = sn.extractsingle(
|
||||
r"=== Simulation completed in (?P<seconds>\S+) seconds =*",
|
||||
self.stdout,
|
||||
"seconds",
|
||||
float
|
||||
r"=== Simulation completed in (?P<seconds>\S+) seconds =*", self.stdout, "seconds", float
|
||||
)
|
||||
return minutes * 60 + seconds
|
||||
|
@@ -1,158 +1,125 @@
|
||||
site_configuration = {
|
||||
'systems': [
|
||||
"systems": [
|
||||
{
|
||||
'name': 'archer2',
|
||||
'descr': 'ARCHER2',
|
||||
'hostnames': ['uan','ln','dvn'],
|
||||
'modules_system': 'lmod',
|
||||
'partitions': [
|
||||
"name": "archer2",
|
||||
"descr": "ARCHER2",
|
||||
"hostnames": ["uan", "ln", "dvn"],
|
||||
"modules_system": "lmod",
|
||||
"partitions": [
|
||||
{
|
||||
'name': 'login',
|
||||
'descr': 'Login nodes',
|
||||
'scheduler': 'local',
|
||||
'launcher': 'local',
|
||||
'environs': ['PrgEnv-gnu','PrgEnv-cray','PrgEnv-aocc'],
|
||||
"name": "login",
|
||||
"descr": "Login nodes",
|
||||
"scheduler": "local",
|
||||
"launcher": "local",
|
||||
"environs": ["PrgEnv-gnu", "PrgEnv-cray", "PrgEnv-aocc"],
|
||||
},
|
||||
{
|
||||
'name': 'compute',
|
||||
'descr': 'Compute nodes',
|
||||
'scheduler': 'slurm',
|
||||
'launcher': 'srun',
|
||||
'access': ['--hint=nomultithread','--distribution=block:block','--partition=standard','--qos=standard'],
|
||||
'environs': ['PrgEnv-gnu','PrgEnv-cray','PrgEnv-aocc'],
|
||||
'max_jobs': 16,
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
'environments': [
|
||||
{
|
||||
'name': 'PrgEnv-gnu',
|
||||
'modules': ['PrgEnv-gnu'],
|
||||
'cc': 'cc',
|
||||
'cxx': 'CC',
|
||||
'ftn': 'ftn',
|
||||
'target_systems': ['archer2']
|
||||
},
|
||||
{
|
||||
'name': 'PrgEnv-cray',
|
||||
'modules': ['PrgEnv-cray'],
|
||||
'cc': 'cc',
|
||||
'cxx': 'CC',
|
||||
'ftn': 'ftn',
|
||||
'target_systems': ['archer2']
|
||||
},
|
||||
{
|
||||
'name': 'PrgEnv-aocc',
|
||||
'modules': ['PrgEnv-aocc'],
|
||||
'cc': 'cc',
|
||||
'cxx': 'CC',
|
||||
'ftn': 'ftn',
|
||||
'target_systems': ['archer2']
|
||||
},
|
||||
],
|
||||
'logging': [
|
||||
{
|
||||
'level': 'debug',
|
||||
'handlers': [
|
||||
{
|
||||
'type': 'stream',
|
||||
'name': 'stdout',
|
||||
'level': 'info',
|
||||
'format': '%(message)s'
|
||||
},
|
||||
{
|
||||
'type': 'file',
|
||||
'name': 'reframe.out',
|
||||
'level': 'info',
|
||||
'format': '[%(asctime)s] %(check_info)s: %(message)s',
|
||||
'append': True
|
||||
},
|
||||
{
|
||||
'type': 'file',
|
||||
'name': 'reframe.log',
|
||||
'level': 'debug',
|
||||
'format': '[%(asctime)s] %(levelname)s %(levelno)s: %(check_info)s: %(message)s', # noqa: E501
|
||||
'append': False
|
||||
}
|
||||
],
|
||||
'handlers_perflog': [
|
||||
{
|
||||
'type': 'file',
|
||||
'name': 'reframe_perf.out',
|
||||
'level': 'info',
|
||||
'format': '[%(asctime)s] %(check_info)s %(check_perfvalues)s',
|
||||
'format_perfvars': '| %(check_perf_var)s: %(check_perf_value)s %(check_perf_unit)s (r: %(check_perf_ref)s l: %(check_perf_lower_thres)s u: %(check_perf_upper_thres)s) ',
|
||||
'append': True
|
||||
},
|
||||
{
|
||||
'type': 'filelog',
|
||||
'prefix': '%(check_system)s/%(check_partition)s',
|
||||
'level': 'info',
|
||||
'format': (
|
||||
'%(check_result)s, %(check_job_completion_time)s, '
|
||||
'%(check_name)s, %(check_short_name)s, %(check_jobid)s, '
|
||||
'%(check_num_tasks)s, %(check_num_cpus_per_task)s, %(check_num_tasks_per_node)s, '
|
||||
'%(check_#ALL)s' # Any remaining loggable test attributes should be test parameters
|
||||
),
|
||||
'ignore_keys': [
|
||||
'check_build_locally',
|
||||
'check_build_time_limit',
|
||||
'check_descr',
|
||||
'check_display_name',
|
||||
'check_env_vars',
|
||||
'check_exclusive_access',
|
||||
'check_executable',
|
||||
'check_executable_opts',
|
||||
'check_extra_resources',
|
||||
'check_hashcode',
|
||||
'check_job_completion_time_unix',
|
||||
'check_job_exitcode',
|
||||
'check_job_nodelist',
|
||||
'check_job_submit_time',
|
||||
'check_jobid',
|
||||
'check_keep_files',
|
||||
'check_local',
|
||||
'check_maintainers',
|
||||
'check_max_pending_time',
|
||||
'check_modules',
|
||||
'check_name',
|
||||
'check_num_cpus_per_task',
|
||||
'check_num_gpus_per_node',
|
||||
'check_num_tasks',
|
||||
'check_num_tasks_per_core',
|
||||
'check_num_tasks_per_node',
|
||||
'check_num_tasks_per_socket',
|
||||
'check_outputdir',
|
||||
'check_partition',
|
||||
'check_prebuild_cmds',
|
||||
'check_prefix',
|
||||
'check_prerun_cmds',
|
||||
'check_postbuild_cmds',
|
||||
'check_postrun_cmds',
|
||||
'check_readonly_files',
|
||||
'check_short_name',
|
||||
'check_sourcepath',
|
||||
'check_sourcesdir',
|
||||
'check_stagedir',
|
||||
'check_strict_check',
|
||||
'check_system',
|
||||
'check_tags',
|
||||
'check_time_limit',
|
||||
'check_unique_name',
|
||||
'check_use_multithreading',
|
||||
'check_valid_prog_environs',
|
||||
'check_valid_systems',
|
||||
'check_variables'
|
||||
"name": "compute",
|
||||
"descr": "Compute nodes",
|
||||
"scheduler": "slurm",
|
||||
"launcher": "srun",
|
||||
"access": [
|
||||
"--hint=nomultithread",
|
||||
"--distribution=block:block",
|
||||
"--partition=standard",
|
||||
"--qos=standard",
|
||||
],
|
||||
'format_perfvars': (
|
||||
'%(check_perf_value)s|%(check_perf_unit)s|'
|
||||
'%(check_perf_ref)s|%(check_perf_lower_thres)s|'
|
||||
'%(check_perf_upper_thres)s|'
|
||||
),
|
||||
'append': True
|
||||
}
|
||||
]
|
||||
"environs": ["PrgEnv-gnu", "PrgEnv-cray", "PrgEnv-aocc"],
|
||||
"max_jobs": 16,
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
"environments": [
|
||||
{
|
||||
"name": "PrgEnv-gnu",
|
||||
"modules": ["PrgEnv-gnu"],
|
||||
"cc": "cc",
|
||||
"cxx": "CC",
|
||||
"ftn": "ftn",
|
||||
"target_systems": ["archer2"],
|
||||
},
|
||||
{
|
||||
"name": "PrgEnv-cray",
|
||||
"modules": ["PrgEnv-cray"],
|
||||
"cc": "cc",
|
||||
"cxx": "CC",
|
||||
"ftn": "ftn",
|
||||
"target_systems": ["archer2"],
|
||||
},
|
||||
{
|
||||
"name": "PrgEnv-aocc",
|
||||
"modules": ["PrgEnv-aocc"],
|
||||
"cc": "cc",
|
||||
"cxx": "CC",
|
||||
"ftn": "ftn",
|
||||
"target_systems": ["archer2"],
|
||||
},
|
||||
],
|
||||
"logging": [
|
||||
{
|
||||
"level": "debug",
|
||||
"handlers": [
|
||||
{"type": "stream", "name": "stdout", "level": "info", "format": "%(message)s"},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "reframe.out",
|
||||
"level": "info",
|
||||
"format": "[%(asctime)s] %(check_info)s: %(message)s",
|
||||
"append": True,
|
||||
},
|
||||
{
|
||||
"type": "file",
|
||||
"name": "reframe.log",
|
||||
"level": "debug",
|
||||
"format": "[%(asctime)s] %(levelname)s %(levelno)s: %(check_info)s: %(message)s", # noqa: E501
|
||||
"append": False,
|
||||
},
|
||||
],
|
||||
"handlers_perflog": [
|
||||
{
|
||||
"type": "file",
|
||||
"name": "reframe_perf.out",
|
||||
"level": "info",
|
||||
"format": "[%(asctime)s] %(check_info)s job_id=%(check_jobid)s %(check_perfvalues)s",
|
||||
"format_perfvars": "| %(check_perf_var)s: %(check_perf_value)s %(check_perf_unit)s (r: %(check_perf_ref)s l: %(check_perf_lower_thres)s u: %(check_perf_upper_thres)s) ",
|
||||
"append": True,
|
||||
},
|
||||
{
|
||||
"type": "filelog",
|
||||
"prefix": "%(check_system)s/%(check_partition)s",
|
||||
"level": "info",
|
||||
"format": (
|
||||
"%(check_result)s,%(check_job_completion_time)s,"
|
||||
"%(check_info)s,%(check_jobid)s,"
|
||||
"%(check_num_tasks)s,%(check_num_cpus_per_task)s,%(check_num_tasks_per_node)s,"
|
||||
"%(check_perfvalues)s"
|
||||
),
|
||||
"format_perfvars": (
|
||||
"%(check_perf_value)s,%(check_perf_unit)s,"
|
||||
"%(check_perf_ref)s,%(check_perf_lower_thres)s,"
|
||||
"%(check_perf_upper_thres)s,"
|
||||
),
|
||||
"append": True,
|
||||
},
|
||||
{
|
||||
"type": "filelog",
|
||||
"prefix": "%(check_system)s/%(check_partition)s/latest",
|
||||
"level": "info",
|
||||
"format": (
|
||||
"%(check_result)s,%(check_job_completion_time)s,"
|
||||
"%(check_info)s,%(check_jobid)s,"
|
||||
"%(check_num_tasks)s,%(check_num_cpus_per_task)s,%(check_num_tasks_per_node)s,"
|
||||
"%(check_perfvalues)s"
|
||||
),
|
||||
"format_perfvars": (
|
||||
"%(check_perf_value)s,%(check_perf_unit)s,"
|
||||
"%(check_perf_ref)s,%(check_perf_lower_thres)s,"
|
||||
"%(check_perf_upper_thres)s,"
|
||||
),
|
||||
"append": False,
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
38
tests/reframe_benchmarks.py
普通文件
38
tests/reframe_benchmarks.py
普通文件
@@ -0,0 +1,38 @@
|
||||
import reframe as rfm
|
||||
from reframe.core.builtins import parameter, run_after
|
||||
|
||||
from base_tests import GprmaxBaseTest
|
||||
|
||||
|
||||
"""ReFrame tests for performance benchmarking
|
||||
|
||||
Usage:
|
||||
cd gprMax/tests
|
||||
reframe -C configuraiton/{CONFIG_FILE} -c reframe_benchmarks.py -c base_tests.py -r
|
||||
"""
|
||||
|
||||
|
||||
@rfm.simple_test
|
||||
class BenchmarkTest(GprmaxBaseTest):
|
||||
|
||||
tags = {"benchmark", "single node", "openmp"}
|
||||
|
||||
num_tasks = 1
|
||||
omp_threads = parameter([1, 2, 4, 8, 16, 32, 64, 128])
|
||||
domain = parameter([0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
|
||||
time_limit = "4h"
|
||||
|
||||
@run_after("init")
|
||||
def setup_omp(self):
|
||||
self.num_cpus_per_task = self.omp_threads
|
||||
super().setup_omp()
|
||||
|
||||
@run_after("init")
|
||||
def create_model_file(self):
|
||||
input_file = f"benchmark_model_{self.domain}.in"
|
||||
self.executable_opts = [input_file]
|
||||
self.keep_files = [input_file]
|
||||
|
||||
@run_after("init")
|
||||
def set_cpu_freq(self):
|
||||
self.env_vars["SLURM_CPU_FREQ_REQ"] = 2250000
|
@@ -1,10 +1,8 @@
|
||||
import reframe as rfm
|
||||
from reframe.core.builtins import parameter
|
||||
|
||||
from base_tests import GprmaxBaseTest
|
||||
from reframe.core.builtins import parameter, run_after
|
||||
|
||||
|
||||
"""ReFrame tests for benchmarking and basic functionality
|
||||
"""ReFrame tests for basic functionality
|
||||
|
||||
Usage:
|
||||
cd gprMax/tests
|
||||
@@ -14,6 +12,7 @@ from base_tests import GprmaxBaseTest
|
||||
|
||||
@rfm.simple_test
|
||||
class BScanTest(GprmaxBaseTest):
|
||||
tags = {"test", "mpi", "taskfarm"}
|
||||
|
||||
executable_opts = "cylinder_Bscan_2D.in -n 64 -mpi".split()
|
||||
num_tasks = 8
|
||||
@@ -22,18 +21,21 @@ class BScanTest(GprmaxBaseTest):
|
||||
|
||||
@rfm.simple_test
|
||||
class BasicModelsTest(GprmaxBaseTest):
|
||||
tags = {"test", "serial"}
|
||||
|
||||
# List of available basic test models
|
||||
model = parameter([
|
||||
"2D_ExHyHz",
|
||||
"2D_EyHxHz",
|
||||
"2D_EzHxHy",
|
||||
"cylinder_Ascan_2D",
|
||||
"hertzian_dipole_fs",
|
||||
"hertzian_dipole_hs",
|
||||
"hertzian_dipole_dispersive",
|
||||
"magnetic_dipole_fs",
|
||||
])
|
||||
model = parameter(
|
||||
[
|
||||
"2D_ExHyHz",
|
||||
"2D_EyHxHz",
|
||||
"2D_EzHxHy",
|
||||
"cylinder_Ascan_2D",
|
||||
"hertzian_dipole_fs",
|
||||
"hertzian_dipole_hs",
|
||||
"hertzian_dipole_dispersive",
|
||||
"magnetic_dipole_fs",
|
||||
]
|
||||
)
|
||||
num_cpus_per_task = 16
|
||||
|
||||
@run_after("init")
|
||||
@@ -43,27 +45,3 @@ class BasicModelsTest(GprmaxBaseTest):
|
||||
self.executable_opts = [input_file, "-o", output_file]
|
||||
self.postrun_cmds = [f"python -m toolboxes.Plotting.plot_Ascan -save {output_file}"]
|
||||
self.keep_files = [input_file, output_file, f"{self.model}.pdf"]
|
||||
|
||||
|
||||
@rfm.simple_test
|
||||
class BenchmarkTest(GprmaxBaseTest):
|
||||
|
||||
num_tasks = 1
|
||||
omp_threads = parameter([1, 2, 4, 8, 16, 32, 64, 128])
|
||||
domain = parameter([0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
|
||||
time_limit = "4h"
|
||||
|
||||
@run_after("init")
|
||||
def setup_omp(self):
|
||||
self.num_cpus_per_task = self.omp_threads
|
||||
super().setup_omp()
|
||||
|
||||
@run_after("init")
|
||||
def create_model_file(self):
|
||||
input_file = f"benchmark_model_{self.domain}.in"
|
||||
self.executable_opts = [input_file]
|
||||
self.keep_files = [input_file]
|
||||
|
||||
@run_after("init")
|
||||
def set_cpu_freq(self):
|
||||
self.env_vars["SLURM_CPU_FREQ_REQ"] = 2250000
|
||||
|
@@ -1,64 +0,0 @@
|
||||
"""A series of models with different domain sizes used for benchmarking.
|
||||
The domain is free space with a simple source (Hertzian Dipole) and
|
||||
receiver at the centre.
|
||||
"""
|
||||
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
import gprMax
|
||||
|
||||
# Cube side lengths (in cells) for different domains
|
||||
DOMAINS = [0.10, 0.15, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80]
|
||||
|
||||
# Number of OpenMP threads to benchmark each domain size
|
||||
OMP_THREADS = [1, 2, 4, 8, 16, 32, 64, 128]
|
||||
|
||||
# Discretisation
|
||||
dl = 0.001
|
||||
|
||||
|
||||
@pytest.mark.parametrize("domain", DOMAINS)
|
||||
@pytest.mark.parametrize("omp_threads", OMP_THREADS)
|
||||
def test_simple_benchmarks(request, benchmark, domain, omp_threads):
|
||||
|
||||
output_dir = Path(os.path.dirname(request.fspath), "tmp", request.node.name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
output_filepath = output_dir / "model.h5"
|
||||
|
||||
# Domain
|
||||
x = domain
|
||||
y = x
|
||||
z = x
|
||||
|
||||
scene = gprMax.Scene()
|
||||
|
||||
title = gprMax.Title(name=request.node.name)
|
||||
domain = gprMax.Domain(p1=(x, y, z))
|
||||
dxdydz = gprMax.Discretisation(p1=(dl, dl, dl))
|
||||
time_window = gprMax.TimeWindow(time=3e-9)
|
||||
wv = gprMax.Waveform(wave_type="gaussiandotnorm", amp=1, freq=900e6, id="MySource")
|
||||
src = gprMax.HertzianDipole(p1=(x / 2, y / 2, z / 2), polarisation="x", waveform_id="MySource")
|
||||
rx = gprMax.Rx(p1=(x / 4, y / 4, z / 4))
|
||||
|
||||
omp = gprMax.OMPThreads(n=omp_threads)
|
||||
|
||||
scenes = []
|
||||
scene.add(title)
|
||||
scene.add(domain)
|
||||
scene.add(dxdydz)
|
||||
scene.add(time_window)
|
||||
scene.add(wv)
|
||||
scene.add(src)
|
||||
scene.add(omp)
|
||||
scene.add(rx)
|
||||
scenes.append(scene)
|
||||
|
||||
# Run benchmark once (i.e. 1 round)
|
||||
benchmark.pedantic(gprMax.run, kwargs={'scenes': scenes, 'n': len(scenes), 'geometry_only': False, 'outputfile': output_filepath, 'gpu': None})
|
||||
|
||||
# Automatically choose number of rounds.
|
||||
# benchmark(gprMax.run, scenes=scenes, n=len(scenes), geometry_only=False, outputfile=output_filepath, gpu=None)
|
@@ -1,187 +0,0 @@
|
||||
# Copyright (C) 2015-2023: The University of Edinburgh, United Kingdom
|
||||
# Authors: Craig Warren, Antonis Giannopoulos, and John Hartley
|
||||
#
|
||||
# This file is part of gprMax.
|
||||
#
|
||||
# gprMax is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# gprMax is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with gprMax. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import h5py
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import gprMax
|
||||
from testing.analytical_solutions import hertzian_dipole_fs
|
||||
from tests.utilities.data import get_data_from_h5_file, calculate_diffs
|
||||
from tests.utilities.plotting import plot_dataset_comparison, plot_diffs
|
||||
|
||||
from gprMax.utilities.logging import logging_config
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging_config(name=__name__)
|
||||
|
||||
if sys.platform == "linux":
|
||||
plt.switch_backend("agg")
|
||||
|
||||
|
||||
"""Compare field outputs
|
||||
|
||||
Usage:
|
||||
cd gprMax
|
||||
pytest tests/test_models.py
|
||||
"""
|
||||
|
||||
# Specify directory containing basic models to test
|
||||
BASIC_MODELS_DIRECTORY = Path(__file__).parent / "data" / "models_basic"
|
||||
|
||||
# List of available basic test models
|
||||
BASIC_MODELS = [
|
||||
"2D_ExHyHz",
|
||||
"2D_EyHxHz",
|
||||
"2D_EzHxHy",
|
||||
"cylinder_Ascan_2D",
|
||||
"hertzian_dipole_fs",
|
||||
"hertzian_dipole_hs",
|
||||
"hertzian_dipole_dispersive",
|
||||
"magnetic_dipole_fs",
|
||||
]
|
||||
|
||||
# Specify directory containing analytical models to test
|
||||
ANALYTICAL_MODELS_DIRECTORY = Path(__file__).parent / "data" / "models_analytical"
|
||||
|
||||
# List of available analytical models
|
||||
ANALYTICAL_MODELS = ["hertzian_dipole_fs_analytical"]
|
||||
|
||||
FIELD_COMPONENTS_BASE_PATH = "/rxs/rx1/"
|
||||
|
||||
|
||||
def create_ascan_comparison_plots(test_time, test_data, ref_time, ref_data, model_name, output_base):
|
||||
fig1 = plot_dataset_comparison(test_time, test_data, ref_time, ref_data, model_name)
|
||||
fig1.savefig(output_base.with_suffix(".png"), dpi=150, format="png", bbox_inches="tight", pad_inches=0.1)
|
||||
|
||||
# Required to correctly calculate diffs
|
||||
assert test_time.shape == ref_time.shape
|
||||
assert np.all(test_time == ref_time)
|
||||
assert test_data.shape == ref_data.shape
|
||||
|
||||
data_diffs = calculate_diffs(test_data, ref_data)
|
||||
|
||||
fig2 = plot_diffs(test_time, data_diffs)
|
||||
fig2.savefig(Path(f"{output_base}_diffs.png"), dpi=150, format="png", bbox_inches="tight", pad_inches=0.1)
|
||||
|
||||
logger.info(f"Output data folder: {output_base.parent}")
|
||||
|
||||
plt.close(fig1)
|
||||
plt.close(fig2)
|
||||
|
||||
|
||||
|
||||
def run_test(model_name, input_base, data_directory, analytical_func=None, gpu=None, opencl=None):
|
||||
input_filepath = input_base.with_suffix(".in")
|
||||
reference_filepath = Path(f"{input_base}_ref.h5")
|
||||
|
||||
output_base = data_directory / model_name
|
||||
output_filepath = output_base.with_suffix(".h5")
|
||||
|
||||
# Run model
|
||||
gprMax.run(inputfile=input_filepath, outputfile=output_filepath, gpu=gpu, opencl=opencl)
|
||||
|
||||
test_time, test_data = get_data_from_h5_file(output_filepath)
|
||||
|
||||
if analytical_func is not None:
|
||||
ref_time = test_time
|
||||
ref_data = analytical_func(output_filepath)
|
||||
else:
|
||||
ref_time, ref_data = get_data_from_h5_file(reference_filepath)
|
||||
|
||||
create_ascan_comparison_plots(test_time, test_data, ref_time, ref_data, model_name, output_base)
|
||||
|
||||
data_diffs = calculate_diffs(test_data, ref_data)
|
||||
max_diff = round(np.max(data_diffs), 2)
|
||||
assert max_diff <= 0
|
||||
|
||||
|
||||
def run_regression_test(request, ndarrays_regression, model_name, input_base, data_directory, gpu=None, opencl=None):
|
||||
input_filepath = input_base.with_suffix(".in")
|
||||
|
||||
output_dir = Path(os.path.dirname(request.fspath), "tmp", request.node.name)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
output_base = output_dir / model_name
|
||||
output_filepath = output_base.with_suffix(".h5")
|
||||
|
||||
data_base = data_directory / request.node.name
|
||||
reference_filepath = data_base.with_suffix(".npz")
|
||||
|
||||
# Run model
|
||||
gprMax.run(inputfile=input_filepath, outputfile=output_filepath, gpu=gpu, opencl=opencl)
|
||||
|
||||
test_time, test_data = get_data_from_h5_file(output_filepath)
|
||||
|
||||
# May not exist if first time running the regression test
|
||||
if os.path.exists(reference_filepath):
|
||||
reference_file = np.load(reference_filepath)
|
||||
|
||||
ref_time = reference_file["time"]
|
||||
ref_data = reference_file["data"]
|
||||
|
||||
create_ascan_comparison_plots(test_time, test_data, ref_time, ref_data, model_name, output_base)
|
||||
|
||||
ndarrays_regression.check({"time": test_time, "data": test_data}, basename=os.path.relpath(data_base, data_directory))
|
||||
|
||||
|
||||
def calc_hertzian_dipole_fs_analytical_solution(filepath):
|
||||
with h5py.File(filepath, "r") as file:
|
||||
# Tx/Rx position to feed to analytical solution
|
||||
rx_pos = file[FIELD_COMPONENTS_BASE_PATH].attrs["Position"]
|
||||
tx_pos = file["/srcs/src1/"].attrs["Position"]
|
||||
rx_pos_relative = ((rx_pos[0] - tx_pos[0]), (rx_pos[1] - tx_pos[1]), (rx_pos[2] - tx_pos[2]))
|
||||
|
||||
# Analytical solution of a dipole in free space
|
||||
data = hertzian_dipole_fs(
|
||||
file.attrs["Iterations"], file.attrs["dt"], file.attrs["dx_dy_dz"], rx_pos_relative
|
||||
)
|
||||
return data
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", BASIC_MODELS)
|
||||
def test_basic_models(model, datadir):
|
||||
|
||||
base_filepath = Path(BASIC_MODELS_DIRECTORY, model, model)
|
||||
run_test(model, base_filepath, datadir)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", ANALYTICAL_MODELS)
|
||||
def test_analyitical_models(datadir, model):
|
||||
|
||||
base_filepath = Path(ANALYTICAL_MODELS_DIRECTORY, model)
|
||||
run_test(model, base_filepath, datadir, analytical_func=calc_hertzian_dipole_fs_analytical_solution)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", BASIC_MODELS)
|
||||
def test_basic_models_regression(request, ndarrays_regression, datadir, model):
|
||||
|
||||
base_filepath = Path(BASIC_MODELS_DIRECTORY, model, model)
|
||||
run_regression_test(request, ndarrays_regression, model, base_filepath, datadir)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("model", ANALYTICAL_MODELS)
|
||||
def test_analytical_models_regression(request, ndarrays_regression, datadir, model):
|
||||
|
||||
base_filepath = Path(ANALYTICAL_MODELS_DIRECTORY, model)
|
||||
run_regression_test(request, ndarrays_regression, model, base_filepath, datadir)
|
在新工单中引用
屏蔽一个用户