你已经派生过 gprMax
镜像自地址
https://gitee.com/sunhf/gprMax.git
已同步 2025-08-06 12:36:51 +08:00
Checkout reframe tests from mpi branch
这个提交包含在:
8
reframe_tests/.gitignore
vendored
普通文件
8
reframe_tests/.gitignore
vendored
普通文件
@@ -0,0 +1,8 @@
|
||||
output/
|
||||
perflogs/
|
||||
stage/
|
||||
reframe.log
|
||||
reframe.out
|
||||
reframe_perf.out
|
||||
|
||||
configuration/user_config.py
|
@@ -0,0 +1,11 @@
|
||||
cpu_freq,model,num_cpus_per_task,num_nodes,num_tasks,num_tasks_per_node,run_time,simulation_time
|
||||
2000000,benchmark_model_40,16,1,8,8,147.94,61.11
|
||||
2000000,benchmark_model_40,16,16,128,8,74.45,16.03
|
||||
2000000,benchmark_model_40,16,2,16,8,108.6,45.41
|
||||
2000000,benchmark_model_40,16,4,32,8,92.18,35.0
|
||||
2000000,benchmark_model_40,16,8,64,8,73.71,16.56
|
||||
2250000,benchmark_model_40,16,1,8,8,171.95,53.94
|
||||
2250000,benchmark_model_40,16,16,128,8,58.13,12.04
|
||||
2250000,benchmark_model_40,16,2,16,8,97.73,38.73
|
||||
2250000,benchmark_model_40,16,4,32,8,87.61,28.54
|
||||
2250000,benchmark_model_40,16,8,64,8,68.29,14.47
|
|
@@ -0,0 +1,145 @@
|
||||
cpu_freq,domain,num_cpus_per_task,num_tasks,num_tasks_per_node,omp_threads,run_time,simulation_time
|
||||
2000000,0.1,1,1,,1,66.38,56.22
|
||||
2000000,0.1,2,1,,2,37.98,30.32
|
||||
2000000,0.1,4,1,,4,22.77,17.18
|
||||
2000000,0.1,8,1,,8,16.18,10.51
|
||||
2000000,0.1,16,1,,16,14.27,8.09
|
||||
2000000,0.1,32,1,,32,17.05,10.1
|
||||
2000000,0.1,64,1,,64,28.35,19.12
|
||||
2000000,0.1,128,1,,128,85.27,65.33
|
||||
2000000,0.15,1,1,,1,182.5,174.22
|
||||
2000000,0.15,2,1,,2,102.11,92.21
|
||||
2000000,0.15,4,1,,4,55.17,49.58
|
||||
2000000,0.15,8,1,,8,37.08,31.39
|
||||
2000000,0.15,16,1,,16,33.61,25.55
|
||||
2000000,0.15,32,1,,32,26.1,19.07
|
||||
2000000,0.15,64,1,,64,33.14,23.48
|
||||
2000000,0.15,128,1,,128,78.14,59.57
|
||||
2000000,0.2,1,1,,1,386.67,374.75
|
||||
2000000,0.2,2,1,,2,206.99,197.88
|
||||
2000000,0.2,4,1,,4,109.97,104.41
|
||||
2000000,0.2,8,1,,8,73.19,64.43
|
||||
2000000,0.2,16,1,,16,62.07,54.08
|
||||
2000000,0.2,32,1,,32,48.24,40.67
|
||||
2000000,0.2,64,1,,64,55.74,44.6
|
||||
2000000,0.2,128,1,,128,101.74,83.55
|
||||
2000000,0.3,1,1,,1,1151.43,1140.37
|
||||
2000000,0.3,2,1,,2,611.66,602.01
|
||||
2000000,0.3,4,1,,4,321.48,310.84
|
||||
2000000,0.3,8,1,,8,204.9,196.05
|
||||
2000000,0.3,16,1,,16,174.01,167.72
|
||||
2000000,0.3,32,1,,32,128.94,116.76
|
||||
2000000,0.3,64,1,,64,121.17,108.21
|
||||
2000000,0.3,128,1,,128,198.33,174.66
|
||||
2000000,0.4,1,1,,1,2610.57,2598.76
|
||||
2000000,0.4,2,1,,2,1371.05,1359.44
|
||||
2000000,0.4,4,1,,4,706.84,699.5
|
||||
2000000,0.4,8,1,,8,466.36,459.21
|
||||
2000000,0.4,16,1,,16,401.64,393.83
|
||||
2000000,0.4,32,1,,32,279.96,267.74
|
||||
2000000,0.4,64,1,,64,271.98,247.58
|
||||
2000000,0.4,128,1,,128,374.76,314.99
|
||||
2000000,0.5,1,1,,1,4818.72,4806.97
|
||||
2000000,0.5,2,1,,2,2549.42,2540.13
|
||||
2000000,0.5,4,1,,4,1315.68,1306.77
|
||||
2000000,0.5,8,1,,8,864.02,855.79
|
||||
2000000,0.5,16,1,,16,755.09,748.3
|
||||
2000000,0.5,32,1,,32,548.72,527.04
|
||||
2000000,0.5,64,1,,64,473.02,414.43
|
||||
2000000,0.5,128,1,,128,594.65,443.44
|
||||
2000000,0.6,1,1,,1,8219.78,8149.55
|
||||
2000000,0.6,2,1,,2,4277.96,4266.5
|
||||
2000000,0.6,4,1,,4,2199.55,2190.22
|
||||
2000000,0.6,8,1,,8,1445.58,1438.02
|
||||
2000000,0.6,16,1,,16,1319.07,1312.13
|
||||
2000000,0.6,32,1,,32,877.47,818.48
|
||||
2000000,0.6,64,1,,64,741.43,649.31
|
||||
2000000,0.6,128,1,,128,821.9,554.22
|
||||
2000000,0.7,1,1,,1,12964.86,12949.06
|
||||
2000000,0.7,2,1,,2,6769.45,6762.25
|
||||
2000000,0.7,4,1,,4,3471.68,3465.48
|
||||
2000000,0.7,8,1,,8,2270.26,2263.86
|
||||
2000000,0.7,16,1,,16,2040.48,2033.68
|
||||
2000000,0.7,32,1,,32,1364.91,1274.35
|
||||
2000000,0.7,64,1,,64,1094.98,936.72
|
||||
2000000,0.7,128,1,,128,1163.05,775.27
|
||||
2000000,0.8,1,1,,1,19115.24,18963.06
|
||||
2000000,0.8,2,1,,2,9894.57,9868.57
|
||||
2000000,0.8,4,1,,4,5021.3,5011.79
|
||||
2000000,0.8,8,1,,8,3285.76,3272.44
|
||||
2000000,0.8,16,1,,16,3010.09,3003.21
|
||||
2000000,0.8,32,1,,32,1961.83,1789.48
|
||||
2000000,0.8,64,1,,64,1528.58,1304.87
|
||||
2000000,0.8,128,1,,128,1671.89,1115.37
|
||||
2250000,0.1,1,1,,1,46.42,38.46
|
||||
2250000,0.1,2,1,,2,27.41,19.9
|
||||
2250000,0.1,4,1,,4,15.61,11.83
|
||||
2250000,0.1,8,1,,8,13.1,9.04
|
||||
2250000,0.1,16,1,,16,11.33,5.89
|
||||
2250000,0.1,32,1,,32,13.28,6.98
|
||||
2250000,0.1,64,1,,64,22.95,14.36
|
||||
2250000,0.1,128,1,,128,63.82,47.17
|
||||
2250000,0.15,1,1,,1,122.83,114.3
|
||||
2250000,0.15,2,1,,2,67.81,58.22
|
||||
2250000,0.15,4,1,,4,37.45,33.57
|
||||
2250000,0.15,8,1,,8,32.12,28.33
|
||||
2250000,0.15,16,1,,16,28.47,23.02
|
||||
2250000,0.15,32,1,,32,21.8,15.31
|
||||
2250000,0.15,64,1,,64,26.29,17.85
|
||||
2250000,0.15,128,1,,128,67.36,50.01
|
||||
2250000,0.2,1,1,,1,249.09,240.5
|
||||
2250000,0.2,2,1,,2,131.92,122.94
|
||||
2250000,0.2,4,1,,4,73.47,69.44
|
||||
2250000,0.2,8,1,,8,68.64,59.68
|
||||
2250000,0.2,16,1,,16,58.96,50.94
|
||||
2250000,0.2,32,1,,32,42.94,35.78
|
||||
2250000,0.2,64,1,,64,44.52,36.89
|
||||
2250000,0.2,128,1,,128,84.06,68.65
|
||||
2250000,0.3,1,1,,1,713.41,703.34
|
||||
2250000,0.3,2,1,,2,369.84,363.33
|
||||
2250000,0.3,4,1,,4,211.28,203.39
|
||||
2250000,0.3,8,1,,8,190.98,186.1
|
||||
2250000,0.3,16,1,,16,169.92,163.23
|
||||
2250000,0.3,32,1,,32,117.74,109.5
|
||||
2250000,0.3,64,1,,64,116.59,101.76
|
||||
2250000,0.3,128,1,,128,162.47,134.58
|
||||
2250000,0.4,1,1,,1,1593.84,1584.41
|
||||
2250000,0.4,2,1,,2,821.79,813.12
|
||||
2250000,0.4,4,1,,4,476.39,468.35
|
||||
2250000,0.4,8,1,,8,445.69,437.75
|
||||
2250000,0.4,16,1,,16,392.55,385.05
|
||||
2250000,0.4,32,1,,32,280.65,265.65
|
||||
2250000,0.4,64,1,,64,249.73,221.02
|
||||
2250000,0.4,128,1,,128,312.19,240.34
|
||||
2250000,0.5,1,1,,1,2917.2,2908.0
|
||||
2250000,0.5,2,1,,2,1501.78,1493.7
|
||||
2250000,0.5,4,1,,4,868.33,859.61
|
||||
2250000,0.5,8,1,,8,831.58,827.08
|
||||
2250000,0.5,16,1,,16,734.53,729.57
|
||||
2250000,0.5,32,1,,32,520.43,486.83
|
||||
2250000,0.5,64,1,,64,431.9,373.89
|
||||
2250000,0.5,128,1,,128,523.72,368.59
|
||||
2250000,0.6,1,1,,1,4930.04,4918.3
|
||||
2250000,0.6,2,1,,2,2513.92,2508.71
|
||||
2250000,0.6,4,1,,4,1437.79,1433.86
|
||||
2250000,0.6,8,1,,8,1385.16,1380.08
|
||||
2250000,0.6,16,1,,16,1278.64,1274.32
|
||||
2250000,0.6,32,1,,32,843.05,800.03
|
||||
2250000,0.6,64,1,,64,683.45,575.28
|
||||
2250000,0.6,128,1,,128,736.02,466.24
|
||||
2250000,0.7,1,1,,1,7778.74,7766.64
|
||||
2250000,0.7,2,1,,2,3979.22,3973.14
|
||||
2250000,0.7,4,1,,4,2290.33,2285.86
|
||||
2250000,0.7,8,1,,8,2193.65,2185.43
|
||||
2250000,0.7,16,1,,16,1984.77,1980.02
|
||||
2250000,0.7,32,1,,32,1302.34,1186.39
|
||||
2250000,0.7,64,1,,64,1011.7,830.39
|
||||
2250000,0.7,128,1,,128,1077.62,685.29
|
||||
2250000,0.8,1,1,,1,11319.94,11306.89
|
||||
2250000,0.8,2,1,,2,5715.39,5709.03
|
||||
2250000,0.8,4,1,,4,3279.14,3260.67
|
||||
2250000,0.8,8,1,,8,3194.27,3174.3
|
||||
2250000,0.8,16,1,,16,2923.77,2918.94
|
||||
2250000,0.8,32,1,,32,1871.78,1736.6
|
||||
2250000,0.8,64,1,,64,1424.7,1179.98
|
||||
2250000,0.8,128,1,,128,1530.45,930.84
|
|
@@ -0,0 +1,17 @@
|
||||
cpu_freq,model,mpi_tasks,num_cpus_per_task,num_tasks,num_tasks_per_node,run_time,simulation_time
|
||||
2000000,benchmark_model_40,1,128,1,1,397.64,294.58
|
||||
2000000,benchmark_model_40,128,1,128,128,129.22,68.77
|
||||
2000000,benchmark_model_40,16,8,16,16,104.83,64.91
|
||||
2000000,benchmark_model_40,2,64,2,2,192.89,151.06
|
||||
2000000,benchmark_model_40,32,4,32,32,101.99,63.86
|
||||
2000000,benchmark_model_40,4,32,4,4,119.14,80.95
|
||||
2000000,benchmark_model_40,64,2,64,64,105.57,61.03
|
||||
2000000,benchmark_model_40,8,16,8,8,102.26,58.24
|
||||
2250000,benchmark_model_40,1,128,1,1,348.95,241.2
|
||||
2250000,benchmark_model_40,128,1,128,128,118.04,66.21
|
||||
2250000,benchmark_model_40,16,8,16,16,106.06,61.8
|
||||
2250000,benchmark_model_40,2,64,2,2,189.82,140.84
|
||||
2250000,benchmark_model_40,32,4,32,32,99.12,60.65
|
||||
2250000,benchmark_model_40,4,32,4,4,117.36,76.9
|
||||
2250000,benchmark_model_40,64,2,64,64,108.8,58.79
|
||||
2250000,benchmark_model_40,8,16,8,8,94.92,55.32
|
|
@@ -0,0 +1,169 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
from primePy import primes
|
||||
from reframe import simple_test
|
||||
from reframe.core.builtins import parameter, run_after
|
||||
|
||||
from reframe_tests.tests.base_tests import GprMaxMPIRegressionTest, GprMaxRegressionTest
|
||||
|
||||
"""ReFrame tests for performance benchmarking
|
||||
|
||||
Usage:
|
||||
cd gprMax/reframe_tests
|
||||
reframe -C configuraiton/{CONFIG_FILE} -c reframe_benchmarks.py -c base_tests.py -r
|
||||
"""
|
||||
|
||||
|
||||
def calculate_mpi_decomposition(number: int):
|
||||
factors: list[int] = primes.factors(number)
|
||||
if len(factors) < 3:
|
||||
factors += [1] * (3 - len(factors))
|
||||
elif len(factors) > 3:
|
||||
base = factors[-3:]
|
||||
factors = factors[:-3]
|
||||
for factor in reversed(factors): # Use the largest factors first
|
||||
min_index = np.argmin(base)
|
||||
base[min_index] *= factor
|
||||
factors = base
|
||||
|
||||
return sorted(factors)
|
||||
|
||||
|
||||
@simple_test
|
||||
class SingleNodeBenchmark(GprMaxRegressionTest):
|
||||
tags = {"benchmark", "single node", "openmp"}
|
||||
|
||||
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])
|
||||
cpu_freq = parameter([2000000, 2250000])
|
||||
time_limit = "8h"
|
||||
sourcesdir = "src"
|
||||
model = parameter(
|
||||
[
|
||||
"benchmark_model_10",
|
||||
"benchmark_model_15",
|
||||
"benchmark_model_20",
|
||||
"benchmark_model_30",
|
||||
"benchmark_model_40",
|
||||
"benchmark_model_50",
|
||||
"benchmark_model_60",
|
||||
"benchmark_model_70",
|
||||
"benchmark_model_80",
|
||||
]
|
||||
)
|
||||
|
||||
@run_after("init")
|
||||
def setup_env_vars(self):
|
||||
self.num_cpus_per_task = self.omp_threads
|
||||
self.env_vars["SLURM_CPU_FREQ_REQ"] = self.cpu_freq
|
||||
super().setup_env_vars()
|
||||
|
||||
|
||||
@simple_test
|
||||
class SingleNodeMPIBenchmark(GprMaxRegressionTest):
|
||||
tags = {"benchmark", "mpi", "openmp", "single node"}
|
||||
mpi_tasks = parameter([1, 2, 4, 8, 16, 32, 64, 128, 256])
|
||||
# domain = parameter([0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
|
||||
cpu_freq = parameter([2000000, 2250000])
|
||||
model = parameter(["benchmark_model_40"])
|
||||
sourcesdir = "src"
|
||||
time_limit = "1h"
|
||||
|
||||
@run_after("setup")
|
||||
def setup_env_vars(self):
|
||||
cpus_per_node = self.current_partition.processor.num_cpus
|
||||
self.skip_if(
|
||||
cpus_per_node < self.mpi_tasks,
|
||||
f"Insufficient CPUs per node ({cpus_per_node}) to run test with at least {self.mpi_tasks} processors",
|
||||
)
|
||||
|
||||
self.num_cpus_per_task = cpus_per_node // self.mpi_tasks
|
||||
self.num_tasks = cpus_per_node // self.num_cpus_per_task
|
||||
self.num_tasks_per_node = self.num_tasks
|
||||
self.extra_executable_opts = [
|
||||
"--mpi",
|
||||
*map(str, calculate_mpi_decomposition(self.num_tasks)),
|
||||
]
|
||||
|
||||
self.env_vars["SLURM_CPU_FREQ_REQ"] = self.cpu_freq
|
||||
super().setup_env_vars()
|
||||
|
||||
|
||||
@simple_test
|
||||
class MPIStrongScalingBenchmark(GprMaxRegressionTest):
|
||||
tags = {"benchmark", "mpi", "openmp"}
|
||||
|
||||
num_nodes = parameter([1, 2, 4, 8, 16])
|
||||
# domain = parameter([0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
|
||||
cpu_freq = parameter([2000000, 2250000])
|
||||
time_limit = "8h"
|
||||
sourcesdir = "src"
|
||||
model = parameter(["benchmark_model_40"])
|
||||
|
||||
# serial_dependency = SingleNodeBenchmark
|
||||
# mpi_layout = parameter([[1, 1, 1]]) # parameter([[2, 2, 2], [4, 4, 4], [6, 6, 6]])
|
||||
|
||||
def build_reference_filepath(self, suffix: str = "") -> str:
|
||||
filename = (
|
||||
f"MPIWeakScalingBenchmark_{suffix}" if len(suffix) > 0 else "MPIWeakScalingBenchmark"
|
||||
)
|
||||
reference_file = Path("regression_checks", filename).with_suffix(".h5")
|
||||
return os.path.abspath(reference_file)
|
||||
|
||||
@run_after("setup")
|
||||
def setup_env_vars(self):
|
||||
cpus_per_node = self.current_partition.processor.num_cpus
|
||||
|
||||
self.num_cpus_per_task = 16
|
||||
self.num_tasks_per_node = cpus_per_node // self.num_cpus_per_task
|
||||
self.num_tasks = self.num_tasks_per_node * self.num_nodes
|
||||
self.extra_executable_opts = [
|
||||
"--mpi",
|
||||
*map(str, calculate_mpi_decomposition(self.num_tasks)),
|
||||
]
|
||||
|
||||
self.env_vars["SLURM_CPU_FREQ_REQ"] = self.cpu_freq
|
||||
super().setup_env_vars()
|
||||
|
||||
|
||||
@simple_test
|
||||
class MPIWeakScalingBenchmark(GprMaxRegressionTest):
|
||||
tags = {"benchmark", "mpi", "openmp"}
|
||||
|
||||
num_nodes = parameter([1, 2, 4, 8, 16])
|
||||
# domain = parameter([0.1, 0.15, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])
|
||||
cpu_freq = parameter([2000000, 2250000])
|
||||
time_limit = "8h"
|
||||
sourcesdir = "src"
|
||||
model = parameter(["benchmark_model_40"])
|
||||
|
||||
def build_reference_filepath(self, suffix: str = "") -> str:
|
||||
filename = (
|
||||
f"MPIStrongScalingBenchmark_{suffix}_{self.num_nodes}"
|
||||
if len(suffix) > 0
|
||||
else f"MPIStrongScalingBenchmark_{self.num_nodes}"
|
||||
)
|
||||
reference_file = Path("regression_checks", filename).with_suffix(".h5")
|
||||
return os.path.abspath(reference_file)
|
||||
|
||||
@run_after("setup")
|
||||
def setup_env_vars(self):
|
||||
cpus_per_node = self.current_partition.processor.num_cpus
|
||||
|
||||
self.num_cpus_per_task = 16
|
||||
self.num_tasks_per_node = cpus_per_node // self.num_cpus_per_task
|
||||
self.num_tasks = self.num_tasks_per_node * self.num_nodes
|
||||
size = 0.4
|
||||
scale_factor = calculate_mpi_decomposition(self.num_nodes)
|
||||
self.prerun_cmds.append(
|
||||
f'sed -i "s/#domain: 0.4 0.4 0.4/#domain: {size * scale_factor[0]} {size * scale_factor[1]} {size * scale_factor[2]}/g" {self.model}.in'
|
||||
)
|
||||
self.extra_executable_opts = [
|
||||
"--mpi",
|
||||
*map(str, calculate_mpi_decomposition(self.num_tasks)),
|
||||
]
|
||||
|
||||
self.env_vars["SLURM_CPU_FREQ_REQ"] = self.cpu_freq
|
||||
super().setup_env_vars()
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.1 0.1 0.1
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.05 0.05 0.05 myWave
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.15 0.15 0.15
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.075 0.075 0.075 myWave
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.2 0.2 0.2
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.1 0.1 0.1 myWave
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.3 0.3 0.3
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.15 0.15 0.15 myWave
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.4 0.4 0.4
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.2 0.2 0.2 myWave
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.5 0.5 0.5
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.25 0.25 0.25 myWave
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.6 0.6 0.6
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.3 0.3 0.3 myWave
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.7 0.7 0.7
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.35 0.35 0.35 myWave
|
@@ -0,0 +1,7 @@
|
||||
#title: Benchmark model
|
||||
#domain: 0.8 0.8 0.8
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandotnorm 1 900e6 myWave
|
||||
#hertzian_dipole: x 0.4 0.4 0.4 myWave
|
@@ -0,0 +1,136 @@
|
||||
site_configuration = {
|
||||
"general": [
|
||||
{
|
||||
# Necessary if using the --restore-session flag
|
||||
"keep_stage_files": True
|
||||
}
|
||||
],
|
||||
"systems": [
|
||||
{
|
||||
"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": "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,
|
||||
"processor": {
|
||||
"num_cpus": 128,
|
||||
"num_cpus_per_socket": 64,
|
||||
"num_sockets": 2,
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
"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,
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
#SBATCH --job-name=gprMax-benchmarks
|
||||
#SBATCH --time=24:0:0
|
||||
#SBATCH --ntasks=1
|
||||
#SBATCH --partition=serial
|
||||
#SBATCH --qos=serial
|
||||
#SBATCH --output=output/archer2/rfm_bench_%J.out
|
||||
|
||||
# Set the number of threads to 1
|
||||
# This prevents any threaded system libraries from automatically
|
||||
# using threading.
|
||||
export OMP_NUM_THREADS=1
|
||||
|
||||
source ../.venv/bin/activate
|
||||
|
||||
# Any commandline arguments provided will be passed to reframe
|
||||
reframe -C configuration/archer2_settings.py -c benchmark_tests/ -c tests/base_tests.py -r --performance-report "$@"
|
||||
|
||||
sacct --format=JobID,State,Submit,Start,End,Elapsed,NodeList,ReqMem --units=M -j $SLURM_JOBID
|
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
#SBATCH --job-name=gprMax-map
|
||||
#SBATCH --time=00:20:0
|
||||
#SBATCH --partition=standard
|
||||
#SBATCH --qos=short
|
||||
#SBATCH --nodes=1
|
||||
#SBATCH --ntasks=1
|
||||
#SBATCH --cpus-per-task=1
|
||||
#SBATCH --output=output/archer2/map_%J.out
|
||||
|
||||
# USAGE: sbatch -c [ompthreads] job_scripts/archer2_map_single_node.slurm [domain]
|
||||
|
||||
export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
|
||||
export SRUN_CPUS_PER_TASK=$SLURM_CPUS_PER_TASK
|
||||
|
||||
module load arm/forge
|
||||
|
||||
source ../.venv/bin/activate
|
||||
|
||||
mkdir -p profile/archer2/
|
||||
|
||||
map -o="profile/archer2/gprMax_${1}d_${SLURM_CPUS_PER_TASK}t_$(date +%F_%H-%M)" --mpi=slurm --mpiargs="--hint=nomultithread --distribution=block:block" --profile python -m gprMax --log-level 25 src/benchmark_model_$1.in
|
||||
|
||||
sacct --format=JobID,State,Submit,Start,End,Elapsed,NodeList --units=M -j $SLURM_JOBID
|
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
#SBATCH --job-name=gprMax-tests
|
||||
#SBATCH --time=24:0:0
|
||||
#SBATCH --ntasks=1
|
||||
#SBATCH --partition=serial
|
||||
#SBATCH --qos=serial
|
||||
#SBATCH --output=output/archer2/rfm_tests_%J.out
|
||||
|
||||
# Set the number of threads to 1
|
||||
# This prevents any threaded system libraries from automatically
|
||||
# using threading.
|
||||
export OMP_NUM_THREADS=1
|
||||
|
||||
module load cray-python
|
||||
|
||||
source ../.venv/bin/activate
|
||||
|
||||
reframe -C configuration/archer2_settings.py -c tests/ -r "$@"
|
||||
|
||||
sacct --format=JobID,State,Submit,Start,End,Elapsed,NodeList --units=M -j $SLURM_JOBID
|
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:a23ccc9ed81685385ff300c0c00e70329945db364dd7eb9607a511870469c720
|
||||
size 75462640
|
@@ -0,0 +1,8 @@
|
||||
#material: 1 0 1 0 free_space
|
||||
#material: 4.9 0 1 0 myWater
|
||||
#material: 3 0 2 0 boxMaterial
|
||||
#material: 2 0 1.5 0 boxMaterial+boxMaterial+free_space+free_space
|
||||
#material: 1.5 0 1.25 0 boxMaterial+free_space+free_space+free_space
|
||||
#material: 1.975 0 1 0 free_space+free_space+free_space+myWater
|
||||
#material: 2.95 0 1 0 free_space+free_space+myWater+myWater
|
||||
#material: 3.925 0 1 0 free_space+myWater+myWater+myWater
|
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4b8f8c4979f4c82eb4be8a9a63cfaf19408b9f493d1bf3082a8226e97542ae4a
|
||||
size 4980400
|
@@ -0,0 +1,8 @@
|
||||
#material: 1 0 1 0 free_space
|
||||
#material: 4.9 0 1 0 myWater
|
||||
#material: 3 0 2 0 boxMaterial
|
||||
#material: 2 0 1.5 0 boxMaterial+boxMaterial+free_space+free_space
|
||||
#material: 1.5 0 1.25 0 boxMaterial+free_space+free_space+free_space
|
||||
#material: 1.975 0 1 0 free_space+free_space+free_space+myWater
|
||||
#material: 2.95 0 1 0 free_space+free_space+myWater+myWater
|
||||
#material: 3.925 0 1 0 free_space+myWater+myWater+myWater
|
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4a8134fca3e6228211e4a4bc3e504e64f888c55f22e025b482ec501e0051d253
|
||||
size 75740296
|
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2ccd95ff4ef73c224c99a8c321ad6f8014c673a1b33bbb741bff415dfbffd0aa
|
||||
size 4931176
|
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:82b8ac914a9fe20b49f2512215782f01305a2e3211aaac51913f88296a0e3ac5
|
||||
size 4012444
|
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
二进制文件未显示。
437
reframe_tests/tests/base_tests.py
普通文件
437
reframe_tests/tests/base_tests.py
普通文件
@@ -0,0 +1,437 @@
|
||||
"""ReFrame base classes for GprMax tests
|
||||
|
||||
Usage (run all tests):
|
||||
cd gprMax/reframe_tests
|
||||
reframe -C configuration/{CONFIG_FILE} -c tests/ -r
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Literal, Optional, Union
|
||||
|
||||
import reframe.utility.sanity as sn
|
||||
import reframe.utility.typecheck as typ
|
||||
from reframe import RunOnlyRegressionTest, simple_test
|
||||
from reframe.core.builtins import (
|
||||
parameter,
|
||||
performance_function,
|
||||
require_deps,
|
||||
required,
|
||||
run_after,
|
||||
run_before,
|
||||
sanity_function,
|
||||
variable,
|
||||
)
|
||||
from reframe.core.exceptions import DependencyError
|
||||
from reframe.utility import udeps
|
||||
|
||||
from reframe_tests.tests.regression_checks import RegressionCheck
|
||||
from reframe_tests.utilities.deferrable import path_join
|
||||
|
||||
GPRMAX_ROOT_DIR = Path(__file__).parent.parent.parent.resolve()
|
||||
PATH_TO_PYENV = os.path.join(".venv", "bin", "activate")
|
||||
|
||||
|
||||
@simple_test
|
||||
class CreatePyenvTest(RunOnlyRegressionTest):
|
||||
"""Create a fresh virtual environment for running the tests.
|
||||
|
||||
The test checks for any errors from pip installing gprMax and its
|
||||
dependencies.
|
||||
"""
|
||||
|
||||
valid_systems = ["generic", "archer2:login"]
|
||||
valid_prog_environs = ["builtin", "PrgEnv-gnu"]
|
||||
modules = ["cray-python"]
|
||||
|
||||
prerun_cmds = [
|
||||
"python -m venv --system-site-packages --prompt gprMax .venv",
|
||||
f"source {PATH_TO_PYENV}",
|
||||
"CC=cc CXX=CC FC=ftn python -m pip install --upgrade pip",
|
||||
f"CC=cc CXX=CC FC=ftn python -m pip install -r {os.path.join(GPRMAX_ROOT_DIR, 'requirements.txt')}",
|
||||
]
|
||||
executable = f"CC=cc CXX=CC FC=ftn python -m pip install -e {GPRMAX_ROOT_DIR}"
|
||||
|
||||
@run_after("init")
|
||||
def install_system_specific_dependencies(self):
|
||||
"""Install additional dependencies for specific systems."""
|
||||
if self.current_system.name == "archer2":
|
||||
"""
|
||||
Needed to prevent a pip install error.
|
||||
dask 2022.2.1 (installed) requires cloudpickle>=1.1.1, which
|
||||
is not installed and is missed by the pip dependency checks.
|
||||
|
||||
Not necessary for gprMax, but any error message is picked up
|
||||
by the sanity checks.
|
||||
"""
|
||||
self.prerun_cmds.insert(3, "CC=cc CXX=CC FC=ftn python -m pip install cloudpickle")
|
||||
|
||||
"""
|
||||
A default pip install of h5py does not have MPI support so
|
||||
it needs to built as described here:
|
||||
https://docs.h5py.org/en/stable/mpi.html#building-against-parallel-hdf5
|
||||
"""
|
||||
self.modules.append("cray-hdf5-parallel")
|
||||
self.prerun_cmds.insert(
|
||||
4, "CC=mpicc HDF5_MPI='ON' python -m pip install --no-binary=h5py h5py"
|
||||
)
|
||||
|
||||
@sanity_function
|
||||
def check_requirements_installed(self):
|
||||
"""Check packages were successfully installed.
|
||||
|
||||
Check pip is up to date and gprMax dependencies from
|
||||
requirements.txt were successfully installed. Check gprMax was
|
||||
installed successfully and no other errors were thrown.
|
||||
"""
|
||||
return (
|
||||
sn.assert_found(
|
||||
r"(Successfully installed pip)|(Requirement already satisfied: pip.*\n(?!Collecting pip))",
|
||||
self.stdout,
|
||||
"Failed to update pip",
|
||||
)
|
||||
and sn.assert_found(
|
||||
r"Successfully installed (?!(gprMax)|(pip))",
|
||||
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|error):", self.stderr)
|
||||
)
|
||||
|
||||
|
||||
class GprMaxBaseTest(RunOnlyRegressionTest):
|
||||
"""Base class that all GprMax tests should inherit from.
|
||||
|
||||
Test functionality can be augmented by using Mixin classes.
|
||||
|
||||
Attributes:
|
||||
model (parameter[str]): ReFrame parameter to specify the model
|
||||
name(s).
|
||||
sourcesdir (str): Relative path to the test's src directory.
|
||||
regression_checks (list[RegressionCheck]): List of regression
|
||||
checks to perform.
|
||||
test_dependency (type[GprMaxBaseTest] | None): Optional test
|
||||
dependency. If specified, regression checks will use
|
||||
reference files created by the test dependency.
|
||||
"""
|
||||
|
||||
valid_systems = ["archer2:compute"]
|
||||
valid_prog_environs = ["PrgEnv-gnu"]
|
||||
modules = ["cray-python"]
|
||||
|
||||
num_cpus_per_task = 16
|
||||
exclusive_access = True
|
||||
|
||||
model = parameter()
|
||||
sourcesdir = required
|
||||
executable = "time -p python -m gprMax"
|
||||
|
||||
regression_checks = variable(typ.List[RegressionCheck], value=[])
|
||||
|
||||
# TODO: Make this a ReFrame variable
|
||||
# Not currently possible as ReFrame does not think an object of type
|
||||
# reframe.core.meta.RegressionTestMeta is copyable, and so ReFrame
|
||||
# test classes cannot be specified in a variable.
|
||||
test_dependency: Optional[type["GprMaxBaseTest"]] = None
|
||||
# test_dependency = variable(type(None), type, value=None)
|
||||
|
||||
def get_test_dependency_variant_name(self, **kwargs) -> Optional[str]:
|
||||
"""Get unique ReFrame name of the test dependency variant.
|
||||
|
||||
By default, filter test dependencies by the model name.
|
||||
|
||||
Args:
|
||||
**kwargs: Additional key-value pairs to filter the parameter
|
||||
space of the test dependency. The key is the test
|
||||
parameter name and the value is either a single value or
|
||||
a unary function that evaluates to True if the parameter
|
||||
point must be kept, False otherwise.
|
||||
|
||||
Returns:
|
||||
variant_name: Unique name of the test dependency variant.
|
||||
"""
|
||||
if self.test_dependency is None:
|
||||
return None
|
||||
|
||||
# Always filter by the model parameter, but allow child classes
|
||||
# (or mixins) to override how models are filtered.
|
||||
kwargs.setdefault("model", self.model)
|
||||
|
||||
variant_nums = self.test_dependency.get_variant_nums(**kwargs)
|
||||
|
||||
if len(variant_nums) < 1:
|
||||
raise DependencyError(
|
||||
f"No variant of '{self.test_dependency.__name__}' meets conditions: {kwargs}",
|
||||
)
|
||||
|
||||
return self.test_dependency.variant_name(variant_nums[0])
|
||||
|
||||
def get_test_dependency(self) -> Optional["GprMaxBaseTest"]:
|
||||
"""Get correct ReFrame test case from the test dependency.
|
||||
|
||||
Returns:
|
||||
test_case: ReFrame test case.
|
||||
"""
|
||||
variant = self.get_test_dependency_variant_name()
|
||||
if variant is None:
|
||||
return None
|
||||
else:
|
||||
return self.getdep(variant)
|
||||
|
||||
def build_reference_filepath(self, name: Union[str, os.PathLike], suffix: str = ".h5") -> Path:
|
||||
"""Build path to the specified reference file.
|
||||
|
||||
Reference files are saved in directories per test case. If this
|
||||
test does not specify a test dependency, it will save and manage
|
||||
its own reference files in its own directory. Otherwise, it will
|
||||
use reference files saved by its test dependency.
|
||||
|
||||
Args:
|
||||
name: Name of the file.
|
||||
suffix: File extension. Default ".h5".
|
||||
|
||||
Returns:
|
||||
filepath: Absolute path to the reference file.
|
||||
"""
|
||||
target = self.get_test_dependency()
|
||||
if target is None:
|
||||
reference_dir = self.short_name
|
||||
else:
|
||||
reference_dir = target.short_name
|
||||
|
||||
reference_file = Path("regression_checks", reference_dir, name).with_suffix(suffix)
|
||||
return reference_file.absolute()
|
||||
|
||||
# TODO: Change CreatePyenvTest to a fixture instead of a test dependency
|
||||
@run_after("init")
|
||||
def inject_dependencies(self):
|
||||
"""Specify test dependencies.
|
||||
|
||||
All tests depend on the Python virtual environment building
|
||||
correctly and their own test dependency if specified.
|
||||
"""
|
||||
self.depends_on("CreatePyenvTest", udeps.by_env)
|
||||
if self.test_dependency is not None:
|
||||
variant = self.get_test_dependency_variant_name()
|
||||
self.depends_on(variant, udeps.by_env)
|
||||
|
||||
@require_deps
|
||||
def get_pyenv_path(self, CreatePyenvTest):
|
||||
"""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}")
|
||||
|
||||
@run_after("init")
|
||||
def setup_env_vars(self):
|
||||
"""Set necessary environment variables.
|
||||
|
||||
Set OMP_NUM_THREADS environment variable from num_cpus_per_task
|
||||
and other system specific varaibles.
|
||||
"""
|
||||
self.env_vars["OMP_NUM_THREADS"] = self.num_cpus_per_task
|
||||
|
||||
if self.current_system.name == "archer2":
|
||||
# Avoid inheriting slurm memory environment variables from any previous slurm job (i.e. the reframe job)
|
||||
self.prerun_cmds.append("unset SLURM_MEM_PER_NODE")
|
||||
self.prerun_cmds.append("unset SLURM_MEM_PER_CPU")
|
||||
|
||||
# Set the matplotlib cache to the work filesystem
|
||||
self.env_vars["MPLCONFIGDIR"] = "${HOME/home/work}/.config/matplotlib"
|
||||
|
||||
@run_after("init")
|
||||
def set_file_paths(self):
|
||||
"""Set default test input and output files.
|
||||
|
||||
These are set in a post-init hook to allow mixins to use them
|
||||
later in the pipeline.
|
||||
"""
|
||||
self.input_file = Path(f"{self.model}.in")
|
||||
self.output_file = Path(f"{self.model}.h5")
|
||||
|
||||
@run_before("run")
|
||||
def configure_test_run(self):
|
||||
"""Configure gprMax commandline arguments and files to keep."""
|
||||
input_file = str(self.input_file)
|
||||
output_file = str(self.output_file)
|
||||
|
||||
self.executable_opts += [
|
||||
input_file,
|
||||
"-o",
|
||||
output_file,
|
||||
"--log-level",
|
||||
"10",
|
||||
"--hide-progress-bars",
|
||||
]
|
||||
|
||||
regression_output_files = [str(r.output_file) for r in self.regression_checks]
|
||||
self.keep_files += [input_file, output_file, *regression_output_files]
|
||||
|
||||
"""
|
||||
if self.has_receiver_output:
|
||||
self.postrun_cmds = [
|
||||
f"python -m reframe_tests.utilities.plotting {self.output_file} {self.reference_file} -m {self.model}"
|
||||
]
|
||||
self.keep_files += [self.output_file, f"{self.model}.pdf"]
|
||||
|
||||
if self.is_antenna_model:
|
||||
self.postrun_cmds = [
|
||||
f"python -m toolboxes.Plotting.plot_antenna_params -save {self.output_file}"
|
||||
]
|
||||
|
||||
antenna_t1_params = f"{self.model}_t1_params.pdf"
|
||||
antenna_ant_params = f"{self.model}_ant_params.pdf"
|
||||
self.keep_files += [
|
||||
antenna_t1_params,
|
||||
antenna_ant_params,
|
||||
]
|
||||
"""
|
||||
|
||||
@run_before("run")
|
||||
def combine_task_outputs(self):
|
||||
"""Split output from each MPI rank.
|
||||
|
||||
If running with multiple MPI ranks, split the output into
|
||||
seperate files and add postrun commands to combine the files
|
||||
after the simulation has run.
|
||||
"""
|
||||
if self.num_tasks > 1:
|
||||
stdout = self.stdout.evaluate().split(".")[0]
|
||||
stderr = self.stderr.evaluate().split(".")[0]
|
||||
self.prerun_cmds.append(f"mkdir out")
|
||||
self.prerun_cmds.append(f"mkdir err")
|
||||
self.job.launcher.options = [
|
||||
f"--output=out/{stdout}_%t.out",
|
||||
f"--error=err/{stderr}_%t.err",
|
||||
]
|
||||
self.executable_opts += ["--log-all-ranks"]
|
||||
self.postrun_cmds.append(f"cat out/{stdout}_*.out >> {self.stdout}")
|
||||
self.postrun_cmds.append(f"cat err/{stderr}_*.err >> {self.stderr}")
|
||||
|
||||
def test_simulation_complete(self) -> Literal[True]:
|
||||
"""Check simulation completed successfully.
|
||||
|
||||
Returns:
|
||||
simulation_completed: Returns True if the simulation
|
||||
completed, otherwise it fails the test.
|
||||
|
||||
Raises:
|
||||
reframe.core.exceptions.SanityError: If the simulation did
|
||||
not complete.
|
||||
"""
|
||||
return sn.assert_not_found(
|
||||
r"(?i)error",
|
||||
self.stderr,
|
||||
f"An error occured. See '{path_join(self.stagedir, self.stderr)}' for details.",
|
||||
) and sn.assert_found(
|
||||
r"=== Simulation completed in ", self.stdout, "Simulation did not complete"
|
||||
)
|
||||
|
||||
def test_reference_files_exist(self) -> Literal[True]:
|
||||
"""Check all reference files exist and create any missing ones.
|
||||
|
||||
Returns:
|
||||
files_exist: Returns True if all reference files exist,
|
||||
otherwise it fails the test.
|
||||
|
||||
Raises:
|
||||
reframe.core.exceptions.SanityError: If any reference files
|
||||
do not exist.
|
||||
"""
|
||||
|
||||
# Store error messages so all references files can be checked
|
||||
# (and created if necessary) before the test is failed.
|
||||
error_messages = []
|
||||
for check in self.regression_checks:
|
||||
if not check.reference_file_exists():
|
||||
if self.test_dependency is None and check.create_reference_file():
|
||||
error_messages.append(
|
||||
f"Reference file does not exist. Creating... '{check.reference_file}'"
|
||||
)
|
||||
elif self.test_dependency is not None:
|
||||
error_messages.append(
|
||||
f"ERROR: Test dependency did not create reference file: '{check.reference_file}'"
|
||||
)
|
||||
else:
|
||||
error_messages.append(
|
||||
f"ERROR: Unable to create reference file: '{check.reference_file}'"
|
||||
)
|
||||
return sn.assert_true(len(error_messages) < 1, "\n".join(error_messages))
|
||||
|
||||
@sanity_function
|
||||
def regression_check(self) -> bool:
|
||||
"""Run sanity checks and regression checks.
|
||||
|
||||
Checks will run in the following order:
|
||||
- Check the simulation completed.
|
||||
- Check all reference files exist.
|
||||
- Run all regression checks.
|
||||
|
||||
If any of these checks fail, the test will fail and none of the
|
||||
other later checks will run.
|
||||
|
||||
Returns:
|
||||
test_passed: Returns True if all checks pass.
|
||||
|
||||
Raises:
|
||||
reframe.core.exceptions.SanityError: If any regression
|
||||
checks fail.
|
||||
"""
|
||||
|
||||
return (
|
||||
self.test_simulation_complete()
|
||||
and self.test_reference_files_exist()
|
||||
and sn.all(sn.map(lambda check: check.run(), self.regression_checks))
|
||||
)
|
||||
|
||||
@performance_function("s", perf_key="run_time")
|
||||
def extract_run_time(self):
|
||||
"""Extract total runtime from the last task to complete."""
|
||||
return sn.extractsingle(
|
||||
r"real\s+(?P<run_time>\S+)", self.stderr, "run_time", float, self.num_tasks - 1
|
||||
)
|
||||
|
||||
@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, >= 1 min and >= 1 hour cases separately.
|
||||
timeframe = sn.extractsingle(
|
||||
r"=== Simulation completed in \S+ (?P<timeframe>hour|minute|second)",
|
||||
self.stdout,
|
||||
"timeframe",
|
||||
)
|
||||
if timeframe == "hour":
|
||||
simulation_time = sn.extractall(
|
||||
r"=== Simulation completed in (?P<hours>\S+) hours?, (?P<minutes>\S+) minutes? and (?P<seconds>\S+) seconds? =*",
|
||||
self.stdout,
|
||||
["hours", "minutes", "seconds"],
|
||||
float,
|
||||
)
|
||||
hours = simulation_time[0][0]
|
||||
minutes = simulation_time[0][1]
|
||||
seconds = simulation_time[0][2]
|
||||
elif timeframe == "minute":
|
||||
hours = 0
|
||||
simulation_time = sn.extractall(
|
||||
r"=== Simulation completed in (?P<minutes>\S+) minutes? and (?P<seconds>\S+) seconds? =*",
|
||||
self.stdout,
|
||||
["minutes", "seconds"],
|
||||
float,
|
||||
)
|
||||
minutes = simulation_time[0][0]
|
||||
seconds = simulation_time[0][1]
|
||||
else:
|
||||
hours = 0
|
||||
minutes = 0
|
||||
seconds = sn.extractsingle(
|
||||
r"=== Simulation completed in (?P<seconds>\S+) seconds? =*",
|
||||
self.stdout,
|
||||
"seconds",
|
||||
float,
|
||||
)
|
||||
return hours * 3600 + minutes * 60 + seconds
|
268
reframe_tests/tests/mixins.py
普通文件
268
reframe_tests/tests/mixins.py
普通文件
@@ -0,0 +1,268 @@
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import reframe.utility.typecheck as typ
|
||||
from numpy import prod
|
||||
from reframe import RegressionMixin
|
||||
from reframe.core.builtins import parameter, required, run_after, variable
|
||||
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,
|
||||
SnapshotRegressionCheck,
|
||||
)
|
||||
|
||||
# If using a static type checker, inherit from GprMaxBaseTest as the
|
||||
# Mixin classes should always have access to resources from that class.
|
||||
# However, during execution inherit from RegressionMixin.
|
||||
if TYPE_CHECKING:
|
||||
GprMaxMixin = GprMaxBaseTest
|
||||
else:
|
||||
GprMaxMixin = RegressionMixin
|
||||
|
||||
|
||||
class ReceiverMixin(GprMaxMixin):
|
||||
number_of_receivers = variable(int, value=-1)
|
||||
|
||||
@run_after("setup")
|
||||
def add_receiver_regression_checks(self):
|
||||
reference_file = self.build_reference_filepath(self.output_file)
|
||||
|
||||
if self.number_of_receivers > 0:
|
||||
for i in range(self.number_of_receivers):
|
||||
regression_check = ReceiverRegressionCheck(
|
||||
self.output_file, reference_file, f"r{i}"
|
||||
)
|
||||
self.regression_checks.append(regression_check)
|
||||
else:
|
||||
regression_check = H5RegressionCheck(self.output_file, reference_file)
|
||||
self.regression_checks.append(regression_check)
|
||||
|
||||
|
||||
class SnapshotMixin(GprMaxMixin):
|
||||
"""Add regression tests for snapshots.
|
||||
|
||||
Attributes:
|
||||
snapshots (list[str]): List of snapshots to run regression
|
||||
checks on.
|
||||
"""
|
||||
|
||||
snapshots = variable(typ.List[str], value=[])
|
||||
|
||||
def build_snapshot_filepath(self, snapshot: str) -> Path:
|
||||
"""Build filepath to the specified snapshot.
|
||||
|
||||
Args:
|
||||
snapshot: Name of the snapshot.
|
||||
"""
|
||||
return Path(f"{self.model}_snaps", snapshot).with_suffix(".h5")
|
||||
|
||||
@run_after("setup")
|
||||
def add_snapshot_regression_checks(self):
|
||||
"""Add a regression check for each snapshot.
|
||||
|
||||
The test will be skipped if no snapshots have been specified.
|
||||
"""
|
||||
self.skip_if(
|
||||
len(self.snapshots) < 0,
|
||||
f"Must provide a list of snapshots.",
|
||||
)
|
||||
|
||||
for snapshot in self.snapshots:
|
||||
snapshot_file = self.build_snapshot_filepath(snapshot)
|
||||
reference_file = self.build_reference_filepath(snapshot)
|
||||
regression_check = SnapshotRegressionCheck(snapshot_file, reference_file)
|
||||
self.regression_checks.append(regression_check)
|
||||
|
||||
|
||||
class GeometryOnlyMixin(GprMaxMixin):
|
||||
"""Run test with geometry only flag"""
|
||||
|
||||
@run_after("setup")
|
||||
def add_geometry_only_flag(self):
|
||||
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.
|
||||
|
||||
Attributes:
|
||||
geometry_views (list[str]): List of geometry views to run
|
||||
regression checks on.
|
||||
"""
|
||||
|
||||
geometry_views = variable(typ.List[str], value=[])
|
||||
|
||||
def build_geometry_view_filepath(self, geometry_view: str) -> Path:
|
||||
"""Build filepath to the specified geometry view.
|
||||
|
||||
Args:
|
||||
geometry_view: Name of the geometry view.
|
||||
"""
|
||||
return Path(geometry_view).with_suffix(".vtkhdf")
|
||||
|
||||
@run_after("setup")
|
||||
def add_geometry_view_regression_checks(self):
|
||||
"""Add a regression check for each geometry view.
|
||||
|
||||
The test will be skipped if no geometry views have been specified.
|
||||
"""
|
||||
self.skip_if(
|
||||
len(self.geometry_views) < 0,
|
||||
f"Must provide a list of geometry views.",
|
||||
)
|
||||
|
||||
for geometry_view in self.geometry_views:
|
||||
geometry_view_file = self.build_geometry_view_filepath(geometry_view)
|
||||
reference_file = self.build_reference_filepath(geometry_view, ".vtkhdf")
|
||||
regression_check = GeometryViewRegressionCheck(geometry_view_file, reference_file)
|
||||
self.regression_checks.append(regression_check)
|
||||
|
||||
|
||||
class PythonApiMixin(GprMaxMixin):
|
||||
"""Use the GprMax Python API rather than a standard input file."""
|
||||
|
||||
@run_after("setup")
|
||||
def use_python_input_file(self):
|
||||
"""Input files for API tests will be python files."""
|
||||
self.executable = "time -p python"
|
||||
self.input_file = self.input_file.with_suffix(".py")
|
||||
|
||||
|
||||
class MpiMixin(GprMaxMixin):
|
||||
"""Run test using GprMax MPI functionality.
|
||||
|
||||
Attributes:
|
||||
mpi_layout (parameter[list[int]]): ReFrame parameter to specify
|
||||
how MPI tasks should be arranged.
|
||||
"""
|
||||
|
||||
mpi_layout = parameter()
|
||||
|
||||
@run_after("setup")
|
||||
def configure_mpi_tasks(self):
|
||||
"""Set num_tasks and add MPI specific commandline arguments."""
|
||||
self.num_tasks = int(prod(self.mpi_layout))
|
||||
self.executable_opts += ["--mpi", *map(str, self.mpi_layout)]
|
||||
|
||||
|
||||
class BScanMixin(GprMaxMixin):
|
||||
"""Test a B-scan model - a model with a moving source and receiver.
|
||||
|
||||
Attributes:
|
||||
num_models (parameter[int]): Number of models to run.
|
||||
"""
|
||||
|
||||
num_models = parameter()
|
||||
|
||||
@run_after("setup")
|
||||
def setup_bscan_test(self):
|
||||
"""Add B-scan specific commandline arguments and postrun cmds.
|
||||
|
||||
Set the number of models to run, and merge the output files.
|
||||
"""
|
||||
self.executable_opts += ["-n", str(self.num_models)]
|
||||
|
||||
self.postrun_cmds += [
|
||||
f"python -m toolboxes.Utilities.outputfiles_merge {self.model}",
|
||||
f"mv {self.model}_merged.h5 {self.output_file}",
|
||||
]
|
||||
|
||||
def get_test_dependency_variant_name(self, **kwargs) -> Optional[str]:
|
||||
"""Get unique ReFrame name of the test dependency variant.
|
||||
|
||||
By default, filter test dependencies by the model name and the
|
||||
number of models.
|
||||
|
||||
Args:
|
||||
**kwargs: Additional key-value pairs to filter the parameter
|
||||
space of the test dependency. The key is the test
|
||||
parameter name and the value is either a single value or
|
||||
a unary function that evaluates to True if the parameter
|
||||
point must be kept, False otherwise.
|
||||
|
||||
Returns:
|
||||
variant_name: Unique name of the test dependency variant.
|
||||
"""
|
||||
|
||||
kwargs.setdefault("num_models", self.num_models)
|
||||
return super().get_test_dependency_variant_name(**kwargs)
|
||||
|
||||
|
||||
class TaskfarmMixin(GprMaxMixin):
|
||||
"""Run test using GprMax taskfarm functionality."""
|
||||
|
||||
# TODO: Make this a required variabe, or create a new variable to
|
||||
# proxy it.
|
||||
# num_tasks = required
|
||||
|
||||
@run_after("setup")
|
||||
def add_taskfarm_flag(self):
|
||||
"""Add taskfarm specific commandline arguments."""
|
||||
self.executable_opts += ["--taskfarm"]
|
||||
|
||||
|
||||
class AntennaModelMixin(GprMaxMixin):
|
||||
"""Test an antenna model."""
|
||||
|
||||
pass
|
@@ -0,0 +1,176 @@
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from shutil import copyfile
|
||||
from typing import Literal, Optional, Union
|
||||
|
||||
import reframe.utility.sanity as sn
|
||||
from reframe.core.runtime import runtime
|
||||
from reframe.utility import osext
|
||||
|
||||
|
||||
class RegressionCheck:
|
||||
"""Compare two files using diff"""
|
||||
|
||||
def __init__(
|
||||
self, output_file: Union[str, PathLike], reference_file: Union[str, PathLike]
|
||||
) -> None:
|
||||
"""Create a new regression check.
|
||||
|
||||
Args:
|
||||
output_file: Path to output file generate by the test.
|
||||
reference_file: Path to reference file to run the regression
|
||||
check against.
|
||||
"""
|
||||
self.output_file = Path(output_file)
|
||||
self.reference_file = Path(reference_file)
|
||||
self.cmd = "diff"
|
||||
self.options: list[str] = []
|
||||
|
||||
@property
|
||||
def error_msg(self) -> str:
|
||||
"""Message to display if the regression check fails"""
|
||||
return "Failed regression check"
|
||||
|
||||
def create_reference_file(self) -> bool:
|
||||
"""Create reference file if it does not already exist.
|
||||
|
||||
The reference file is created as a copy of the current output
|
||||
file.
|
||||
|
||||
Returns:
|
||||
file_created: Returns True if a new file was created, False
|
||||
if the path already exists.
|
||||
"""
|
||||
if not sn.path_exists(self.reference_file):
|
||||
self.reference_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
copyfile(self.output_file, self.reference_file)
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def reference_file_exists(self) -> bool:
|
||||
"""Check if the reference file exists.
|
||||
|
||||
Returns:
|
||||
file_exists: Returns true if the reference filepath is a
|
||||
regular file, False otherwise.
|
||||
"""
|
||||
return sn.path_isfile(self.reference_file)
|
||||
|
||||
def run(self) -> Literal[True]:
|
||||
"""Run the regression check.
|
||||
|
||||
Returns:
|
||||
check_passed: Returns True if the output file matches the
|
||||
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.
|
||||
"""
|
||||
|
||||
completed_process = osext.run_command(
|
||||
[
|
||||
self.cmd,
|
||||
*self.options,
|
||||
str(self.output_file.absolute()),
|
||||
str(self.reference_file),
|
||||
]
|
||||
)
|
||||
|
||||
return sn.assert_true(
|
||||
sn.path_isfile(self.output_file),
|
||||
f"Expected output file '{self.output_file}' does not exist",
|
||||
) and sn.assert_false(
|
||||
completed_process.stdout,
|
||||
(
|
||||
f"{self.error_msg}\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 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
|
||||
in the same file.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
output_file: Union[str, PathLike],
|
||||
reference_file: Union[str, PathLike],
|
||||
output_receiver: str,
|
||||
reference_receiver: Optional[str] = None,
|
||||
) -> None:
|
||||
"""Create a new receiver regression check.
|
||||
|
||||
Args:
|
||||
output_file: Path to output file generate by the test.
|
||||
reference_file: Path to reference file to run the regression
|
||||
check against.
|
||||
output_receiver: Output receiver to check.
|
||||
reference_receiver: Optional receiver to check against in
|
||||
the reference file. If None, this will be the same as
|
||||
the output receiver.
|
||||
"""
|
||||
super().__init__(output_file, reference_file)
|
||||
|
||||
self.output_receiver = output_receiver
|
||||
self.reference_receiver = reference_receiver
|
||||
|
||||
self.options.append(f"rxs/{self.output_receiver}")
|
||||
if self.reference_receiver is not None:
|
||||
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(H5RegressionCheck):
|
||||
"""Run regression check on a gprMax Snapshot."""
|
||||
|
||||
@property
|
||||
def error_msg(self) -> str:
|
||||
return f"Snapshot '{self.output_file.name}' failed regression check"
|
||||
|
||||
|
||||
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"
|
@@ -0,0 +1,8 @@
|
||||
#title: 2D test Ex, Hy, Hz components
|
||||
#domain: 0.001 0.100 0.100
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandot 1 1e9 myWave
|
||||
#hertzian_dipole: x 0 0.050 0.050 myWave
|
||||
#rx: 0 0.070 0.070
|
@@ -0,0 +1,13 @@
|
||||
#title: 2D test Ex, Hy, Hz components
|
||||
#domain: 0.001 0.010 0.010
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#pml_cells: 0
|
||||
|
||||
#waveform: gaussiandot 1 1e9 myWave
|
||||
#hertzian_dipole: x 0 0.005 0.005 myWave
|
||||
#rx: 0 0.002 0.002
|
||||
|
||||
#material: 8 0 1 0 half_space
|
||||
#box: 0 0 0 0.001 0.004 0.004 half_space
|
@@ -0,0 +1,8 @@
|
||||
#title: 2D test Ey, Hx, Hz components
|
||||
#domain: 0.100 0.001 0.100
|
||||
#dx_dy_dz: 0.001 0.001 0.001
|
||||
#time_window: 3e-9
|
||||
|
||||
#waveform: gaussiandot 1 1e9 myWave
|
||||
#hertzian_dipole: y 0.050 0 0.050 myWave
|
||||
#rx: 0.070 0 0.070
|
某些文件未显示,因为此 diff 中更改的文件太多 显示更多
在新工单中引用
屏蔽一个用户