1"""Sweep pressure/velocity multigrid settings for the periodic-sphere benchmark."""
7from copy
import deepcopy
9THIS_DIR = os.path.dirname(__file__)
10sys.path.append(THIS_DIR)
12from validate_periodic_spheres_n128
import get_reference_k, run_case, warm_up_cuda
24 """Build one tuning configuration record."""
27 "enable_pressure_mg": enable_pressure_mg,
28 "pressure_mg_params": pressure_mg_params,
29 "enable_velocity_mg": enable_velocity_mg,
30 "velocity_mg_params": velocity_mg_params,
31 "pressure_iter": pressure_iter,
32 "velocity_iter": velocity_iter,
37 make_config(
"rbgs_p50_v2",
False, (4, 2, 2, 32, 2),
False, (4, 1, 1, 16, 1), 50, 2),
38 make_config(
"rbgs_p20_v2",
False, (4, 2, 2, 32, 2),
False, (4, 1, 1, 16, 1), 20, 2),
39 make_config(
"rbgs_p10_v2",
False, (4, 2, 2, 32, 2),
False, (4, 1, 1, 16, 1), 10, 2),
40 make_config(
"rbgs_p20_v4",
False, (4, 2, 2, 32, 2),
False, (4, 1, 1, 16, 1), 20, 4),
41 make_config(
"pmg_default_v2",
True, (4, 2, 2, 32, 2),
False, (4, 1, 1, 16, 1), 50, 2),
42 make_config(
"pmg_light_v2",
True, (4, 1, 1, 16, 1),
False, (4, 1, 1, 16, 1), 50, 2),
43 make_config(
"pmg_light_v4",
True, (4, 1, 1, 16, 1),
False, (4, 1, 1, 16, 1), 50, 4),
44 make_config(
"pmg_deep_v1",
True, (4, 2, 2, 32, 2),
False, (4, 1, 1, 16, 1), 50, 1),
45 make_config(
"pmg_light_l3_v2",
True, (3, 1, 1, 16, 1),
False, (3, 1, 1, 16, 1), 50, 2),
46 make_config(
"pmg_light_l3_v1",
True, (3, 1, 1, 16, 1),
False, (3, 1, 1, 16, 1), 50, 1),
47 make_config(
"pmg_light_l3_v4",
True, (3, 1, 1, 16, 1),
False, (3, 1, 1, 16, 1), 50, 4),
48 make_config(
"pmg_light_l5_v2",
True, (5, 1, 1, 16, 1),
False, (5, 1, 1, 16, 1), 50, 2),
49 make_config(
"pmg_default_l5_v2",
True, (5, 2, 2, 32, 2),
False, (5, 1, 1, 16, 1), 50, 2),
50 make_config(
"pmg_mid_l5_v2",
True, (5, 1, 1, 16, 2),
False, (5, 1, 1, 16, 1), 50, 2),
51 make_config(
"pmg_light_l5_p20_v2",
True, (5, 1, 1, 16, 1),
False, (5, 1, 1, 16, 1), 20, 2),
52 make_config(
"fullmg_fast",
True, (4, 1, 1, 16, 1),
True, (4, 1, 1, 16, 1), 50, 2),
53 make_config(
"fullmg_balanced",
True, (4, 1, 1, 16, 1),
True, (4, 2, 2, 32, 1), 50, 2),
54 make_config(
"fullmg_inner4",
True, (4, 4, 4, 32, 1),
True, (4, 4, 4, 32, 1), 50, 2),
55 make_config(
"fullmg_inner6",
True, (4, 6, 6, 32, 1),
True, (4, 6, 6, 32, 1), 50, 2),
56 make_config(
"fullmg_vcycle2",
True, (4, 1, 1, 16, 1),
True, (4, 1, 1, 16, 2), 50, 2),
57 make_config(
"fullmg_robust",
True, (4, 2, 2, 32, 2),
True, (4, 2, 2, 32, 1), 50, 2),
58 make_config(
"fullmg_balanced_l3",
True, (3, 1, 1, 16, 1),
True, (3, 2, 2, 32, 1), 50, 2),
59 make_config(
"fullmg_balanced_l5",
True, (5, 1, 1, 16, 1),
True, (5, 2, 2, 32, 1), 50, 2),
60 make_config(
"fullmg_balanced_l5_v4",
True, (5, 1, 1, 16, 1),
True, (5, 2, 2, 32, 1), 50, 4),
61 make_config(
"fullmg_robust_l5",
True, (5, 2, 2, 32, 2),
True, (5, 2, 2, 32, 1), 50, 2),
66 """Execute one tuning point and capture failures in the output row."""
67 row = deepcopy(config)
69 row[
"resolution"] = res
70 row[
"k_ref"] = get_reference_k(phi)
76 config[
"enable_pressure_mg"],
77 config[
"pressure_mg_params"],
78 config[
"enable_velocity_mg"],
79 config[
"velocity_mg_params"],
80 config[
"pressure_iter"],
81 config[
"velocity_iter"],
82 args.outer_iterations,
87 args.convergence_stride,
88 args.convergence_rel_tol,
91 row[
"ref_rel_error"] = abs(row[
"k_sim"] - row[
"k_ref"]) / row[
"k_ref"]
92 row[
"within_reference_tol"] = row[
"ref_rel_error"] <= args.reference_rel_tol
93 except Exception
as exc:
94 row[
"status"] = f
"failed: {exc}"
95 row[
"elapsed_s"] = float(
"nan")
96 row[
"per_step_s"] = float(
"nan")
97 row[
"steps_taken"] = 0
98 row[
"outer_iterations_mean"] = float(
"nan")
99 row[
"outer_iterations_max"] = 0
100 row[
"k_sim"] = float(
"nan")
101 row[
"ref_rel_error"] = float(
"nan")
102 row[
"within_reference_tol"] =
False
107 """Persist the sweep results for later analysis."""
108 os.makedirs(os.path.dirname(path), exist_ok=
True)
113 "enable_pressure_mg",
114 "enable_velocity_mg",
117 "pressure_mg_params",
118 "velocity_mg_params",
122 "outer_iterations_mean",
123 "outer_iterations_max",
127 "within_reference_tol",
130 with open(path,
"w", newline=
"", encoding=
"utf-8")
as handle:
131 writer = csv.DictWriter(handle, fieldnames=fieldnames)
134 out = {key: row.get(key)
for key
in fieldnames}
139 """Print the fastest acceptable configurations for each case."""
141 f
"Tuning setup: resolutions={args.resolutions}, phis={args.phis}, "
142 f
"outer_iterations={args.outer_iterations}, outer_tol={args.outer_tol}, "
143 f
"outer_mode={args.outer_mode}, "
144 f
"max_steps={args.max_steps}"
149 grouped.setdefault((row[
"resolution"], row[
"phi"]), []).append(row)
151 for (res, phi), group_rows
in sorted(grouped.items()):
152 print(f
"Case: N={res}, phi={phi}")
154 f
"{'config':<20} {'time(s)':>10} {'steps':>8} {'outer_mean':>12} "
155 f
"{'outer_max':>10} {'K':>10} {'err%':>8} {'ok':>6} {'status':<24}"
160 row[
"status"] !=
"ok",
161 not row[
"within_reference_tol"],
162 row[
"elapsed_s"]
if row[
"elapsed_s"] == row[
"elapsed_s"]
else float(
"inf"),
167 f
"{row['name']:<20} {row['elapsed_s']:10.3f} {row['steps_taken']:8d} "
168 f
"{row['outer_iterations_mean']:12.2f} {row['outer_iterations_max']:10d} "
169 f
"{row['k_sim']:10.4f} {100.0 * row['ref_rel_error']:8.2f} "
170 f
"{str(row['within_reference_tol']):>6} {row['status']:<24}"
176 """Parse CLI arguments for the multigrid parameter sweep."""
177 parser = argparse.ArgumentParser(
178 description=
"Tune RBGS and multigrid settings for periodic spheres at lower resolutions."
180 parser.add_argument(
"--phis", type=float, nargs=
"+", default=[0.001, 0.5236])
181 parser.add_argument(
"--resolutions", type=int, nargs=
"+", default=[32, 64])
182 parser.add_argument(
"--dt", type=float, default=1.0)
183 parser.add_argument(
"--max-steps", type=int, default=80)
184 parser.add_argument(
"--convergence-stride", type=int, default=5)
185 parser.add_argument(
"--convergence-rel-tol", type=float, default=1e-8)
186 parser.add_argument(
"--outer-iterations", type=int, default=800)
187 parser.add_argument(
"--outer-tol", type=float, default=1e-6)
188 parser.add_argument(
"--outer-mode", type=int, default=0)
189 parser.add_argument(
"--reference-rel-tol", type=float, default=0.02)
194 help=
"Optional list of config names to run.",
198 default=
"output/periodic_spheres_multigrid_tuning.csv",
200 return parser.parse_args()
204 """Run the configured parameter sweep and emit a CSV summary."""
208 if args.configs
is not None:
209 wanted = set(args.configs)
210 configs = [config
for config
in CONFIGS
if config[
"name"]
in wanted]
211 missing = sorted(wanted - {config[
"name"]
for config
in configs})
213 raise ValueError(f
"unknown configs requested: {missing}")
215 for res
in args.resolutions:
216 for phi
in args.phis:
217 for config
in configs:
219 f
"Running config={config['name']} N={res} phi={phi} "
220 f
"outer_tol={args.outer_tol}"
226 print(f
"Wrote tuning CSV to {args.output_csv}")
229if __name__ ==
"__main__":
print_summary(rows, args)
make_config(name, enable_pressure_mg, pressure_mg_params, enable_velocity_mg, velocity_mg_params, pressure_iter, velocity_iter)
run_config(phi, res, config, args)