Checkout reframe tests from mpi branch

这个提交包含在:
Nathan Mannall
2025-03-17 12:17:13 +00:00
父节点 d4a757902c
当前提交 4451f43747
共有 139 个文件被更改,包括 2905 次插入0 次删除

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
1 cpu_freq model num_cpus_per_task num_nodes num_tasks num_tasks_per_node run_time simulation_time
2 2000000 benchmark_model_40 16 1 8 8 147.94 61.11
3 2000000 benchmark_model_40 16 16 128 8 74.45 16.03
4 2000000 benchmark_model_40 16 2 16 8 108.6 45.41
5 2000000 benchmark_model_40 16 4 32 8 92.18 35.0
6 2000000 benchmark_model_40 16 8 64 8 73.71 16.56
7 2250000 benchmark_model_40 16 1 8 8 171.95 53.94
8 2250000 benchmark_model_40 16 16 128 8 58.13 12.04
9 2250000 benchmark_model_40 16 2 16 8 97.73 38.73
10 2250000 benchmark_model_40 16 4 32 8 87.61 28.54
11 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
1 cpu_freq domain num_cpus_per_task num_tasks num_tasks_per_node omp_threads run_time simulation_time
2 2000000 0.1 1 1 1 66.38 56.22
3 2000000 0.1 2 1 2 37.98 30.32
4 2000000 0.1 4 1 4 22.77 17.18
5 2000000 0.1 8 1 8 16.18 10.51
6 2000000 0.1 16 1 16 14.27 8.09
7 2000000 0.1 32 1 32 17.05 10.1
8 2000000 0.1 64 1 64 28.35 19.12
9 2000000 0.1 128 1 128 85.27 65.33
10 2000000 0.15 1 1 1 182.5 174.22
11 2000000 0.15 2 1 2 102.11 92.21
12 2000000 0.15 4 1 4 55.17 49.58
13 2000000 0.15 8 1 8 37.08 31.39
14 2000000 0.15 16 1 16 33.61 25.55
15 2000000 0.15 32 1 32 26.1 19.07
16 2000000 0.15 64 1 64 33.14 23.48
17 2000000 0.15 128 1 128 78.14 59.57
18 2000000 0.2 1 1 1 386.67 374.75
19 2000000 0.2 2 1 2 206.99 197.88
20 2000000 0.2 4 1 4 109.97 104.41
21 2000000 0.2 8 1 8 73.19 64.43
22 2000000 0.2 16 1 16 62.07 54.08
23 2000000 0.2 32 1 32 48.24 40.67
24 2000000 0.2 64 1 64 55.74 44.6
25 2000000 0.2 128 1 128 101.74 83.55
26 2000000 0.3 1 1 1 1151.43 1140.37
27 2000000 0.3 2 1 2 611.66 602.01
28 2000000 0.3 4 1 4 321.48 310.84
29 2000000 0.3 8 1 8 204.9 196.05
30 2000000 0.3 16 1 16 174.01 167.72
31 2000000 0.3 32 1 32 128.94 116.76
32 2000000 0.3 64 1 64 121.17 108.21
33 2000000 0.3 128 1 128 198.33 174.66
34 2000000 0.4 1 1 1 2610.57 2598.76
35 2000000 0.4 2 1 2 1371.05 1359.44
36 2000000 0.4 4 1 4 706.84 699.5
37 2000000 0.4 8 1 8 466.36 459.21
38 2000000 0.4 16 1 16 401.64 393.83
39 2000000 0.4 32 1 32 279.96 267.74
40 2000000 0.4 64 1 64 271.98 247.58
41 2000000 0.4 128 1 128 374.76 314.99
42 2000000 0.5 1 1 1 4818.72 4806.97
43 2000000 0.5 2 1 2 2549.42 2540.13
44 2000000 0.5 4 1 4 1315.68 1306.77
45 2000000 0.5 8 1 8 864.02 855.79
46 2000000 0.5 16 1 16 755.09 748.3
47 2000000 0.5 32 1 32 548.72 527.04
48 2000000 0.5 64 1 64 473.02 414.43
49 2000000 0.5 128 1 128 594.65 443.44
50 2000000 0.6 1 1 1 8219.78 8149.55
51 2000000 0.6 2 1 2 4277.96 4266.5
52 2000000 0.6 4 1 4 2199.55 2190.22
53 2000000 0.6 8 1 8 1445.58 1438.02
54 2000000 0.6 16 1 16 1319.07 1312.13
55 2000000 0.6 32 1 32 877.47 818.48
56 2000000 0.6 64 1 64 741.43 649.31
57 2000000 0.6 128 1 128 821.9 554.22
58 2000000 0.7 1 1 1 12964.86 12949.06
59 2000000 0.7 2 1 2 6769.45 6762.25
60 2000000 0.7 4 1 4 3471.68 3465.48
61 2000000 0.7 8 1 8 2270.26 2263.86
62 2000000 0.7 16 1 16 2040.48 2033.68
63 2000000 0.7 32 1 32 1364.91 1274.35
64 2000000 0.7 64 1 64 1094.98 936.72
65 2000000 0.7 128 1 128 1163.05 775.27
66 2000000 0.8 1 1 1 19115.24 18963.06
67 2000000 0.8 2 1 2 9894.57 9868.57
68 2000000 0.8 4 1 4 5021.3 5011.79
69 2000000 0.8 8 1 8 3285.76 3272.44
70 2000000 0.8 16 1 16 3010.09 3003.21
71 2000000 0.8 32 1 32 1961.83 1789.48
72 2000000 0.8 64 1 64 1528.58 1304.87
73 2000000 0.8 128 1 128 1671.89 1115.37
74 2250000 0.1 1 1 1 46.42 38.46
75 2250000 0.1 2 1 2 27.41 19.9
76 2250000 0.1 4 1 4 15.61 11.83
77 2250000 0.1 8 1 8 13.1 9.04
78 2250000 0.1 16 1 16 11.33 5.89
79 2250000 0.1 32 1 32 13.28 6.98
80 2250000 0.1 64 1 64 22.95 14.36
81 2250000 0.1 128 1 128 63.82 47.17
82 2250000 0.15 1 1 1 122.83 114.3
83 2250000 0.15 2 1 2 67.81 58.22
84 2250000 0.15 4 1 4 37.45 33.57
85 2250000 0.15 8 1 8 32.12 28.33
86 2250000 0.15 16 1 16 28.47 23.02
87 2250000 0.15 32 1 32 21.8 15.31
88 2250000 0.15 64 1 64 26.29 17.85
89 2250000 0.15 128 1 128 67.36 50.01
90 2250000 0.2 1 1 1 249.09 240.5
91 2250000 0.2 2 1 2 131.92 122.94
92 2250000 0.2 4 1 4 73.47 69.44
93 2250000 0.2 8 1 8 68.64 59.68
94 2250000 0.2 16 1 16 58.96 50.94
95 2250000 0.2 32 1 32 42.94 35.78
96 2250000 0.2 64 1 64 44.52 36.89
97 2250000 0.2 128 1 128 84.06 68.65
98 2250000 0.3 1 1 1 713.41 703.34
99 2250000 0.3 2 1 2 369.84 363.33
100 2250000 0.3 4 1 4 211.28 203.39
101 2250000 0.3 8 1 8 190.98 186.1
102 2250000 0.3 16 1 16 169.92 163.23
103 2250000 0.3 32 1 32 117.74 109.5
104 2250000 0.3 64 1 64 116.59 101.76
105 2250000 0.3 128 1 128 162.47 134.58
106 2250000 0.4 1 1 1 1593.84 1584.41
107 2250000 0.4 2 1 2 821.79 813.12
108 2250000 0.4 4 1 4 476.39 468.35
109 2250000 0.4 8 1 8 445.69 437.75
110 2250000 0.4 16 1 16 392.55 385.05
111 2250000 0.4 32 1 32 280.65 265.65
112 2250000 0.4 64 1 64 249.73 221.02
113 2250000 0.4 128 1 128 312.19 240.34
114 2250000 0.5 1 1 1 2917.2 2908.0
115 2250000 0.5 2 1 2 1501.78 1493.7
116 2250000 0.5 4 1 4 868.33 859.61
117 2250000 0.5 8 1 8 831.58 827.08
118 2250000 0.5 16 1 16 734.53 729.57
119 2250000 0.5 32 1 32 520.43 486.83
120 2250000 0.5 64 1 64 431.9 373.89
121 2250000 0.5 128 1 128 523.72 368.59
122 2250000 0.6 1 1 1 4930.04 4918.3
123 2250000 0.6 2 1 2 2513.92 2508.71
124 2250000 0.6 4 1 4 1437.79 1433.86
125 2250000 0.6 8 1 8 1385.16 1380.08
126 2250000 0.6 16 1 16 1278.64 1274.32
127 2250000 0.6 32 1 32 843.05 800.03
128 2250000 0.6 64 1 64 683.45 575.28
129 2250000 0.6 128 1 128 736.02 466.24
130 2250000 0.7 1 1 1 7778.74 7766.64
131 2250000 0.7 2 1 2 3979.22 3973.14
132 2250000 0.7 4 1 4 2290.33 2285.86
133 2250000 0.7 8 1 8 2193.65 2185.43
134 2250000 0.7 16 1 16 1984.77 1980.02
135 2250000 0.7 32 1 32 1302.34 1186.39
136 2250000 0.7 64 1 64 1011.7 830.39
137 2250000 0.7 128 1 128 1077.62 685.29
138 2250000 0.8 1 1 1 11319.94 11306.89
139 2250000 0.8 2 1 2 5715.39 5709.03
140 2250000 0.8 4 1 4 3279.14 3260.67
141 2250000 0.8 8 1 8 3194.27 3174.3
142 2250000 0.8 16 1 16 2923.77 2918.94
143 2250000 0.8 32 1 32 1871.78 1736.6
144 2250000 0.8 64 1 64 1424.7 1179.98
145 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
1 cpu_freq model mpi_tasks num_cpus_per_task num_tasks num_tasks_per_node run_time simulation_time
2 2000000 benchmark_model_40 1 128 1 1 397.64 294.58
3 2000000 benchmark_model_40 128 1 128 128 129.22 68.77
4 2000000 benchmark_model_40 16 8 16 16 104.83 64.91
5 2000000 benchmark_model_40 2 64 2 2 192.89 151.06
6 2000000 benchmark_model_40 32 4 32 32 101.99 63.86
7 2000000 benchmark_model_40 4 32 4 4 119.14 80.95
8 2000000 benchmark_model_40 64 2 64 64 105.57 61.03
9 2000000 benchmark_model_40 8 16 8 8 102.26 58.24
10 2250000 benchmark_model_40 1 128 1 1 348.95 241.2
11 2250000 benchmark_model_40 128 1 128 128 118.04 66.21
12 2250000 benchmark_model_40 16 8 16 16 106.06 61.8
13 2250000 benchmark_model_40 2 64 2 2 189.82 140.84
14 2250000 benchmark_model_40 32 4 32 32 99.12 60.65
15 2250000 benchmark_model_40 4 32 4 4 117.36 76.9
16 2250000 benchmark_model_40 64 2 64 64 108.8 58.79
17 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

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

二进制文件未显示。

查看文件

@@ -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

查看文件

@@ -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 中更改的文件太多 显示更多