flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
tune_sdf_packing_multigrid.py
Go to the documentation of this file.
1"""Tune multigrid parameters for the SDF sphere-packing pressure solve."""
2import argparse
3import csv
4import os
5import sys
6from copy import deepcopy
7
8THIS_DIR = os.path.dirname(__file__)
9sys.path.append(THIS_DIR)
10
11from benchmark_sdf_packing_multigrid import load_sdf, run_case, warm_up_cuda
12
13
15 name,
16 method,
17 pressure_iter=50,
18 velocity_iter=2,
19 pressure_mg=(4, 1, 1, 16, 1),
20 velocity_mg=(4, 2, 2, 32, 1),
21):
22 return {
23 "name": name,
24 "method": method,
25 "pressure_iter": pressure_iter,
26 "velocity_iter": velocity_iter,
27 "pressure_mg": pressure_mg,
28 "velocity_mg": velocity_mg,
29 }
30
31
32CONFIGS = [
33 make_config("rbgs_p50_v2", "rbgs", pressure_iter=50, velocity_iter=2),
34 make_config("rbgs_p20_v2", "rbgs", pressure_iter=20, velocity_iter=2),
35 make_config("rbgs_p10_v2", "rbgs", pressure_iter=10, velocity_iter=2),
36 make_config("rbgs_p20_v4", "rbgs", pressure_iter=20, velocity_iter=4),
37 make_config("pmg_l2_light", "pmg", pressure_mg=(2, 1, 1, 16, 1)),
38 make_config("pmg_l3_light", "pmg", pressure_mg=(3, 1, 1, 16, 1)),
39 make_config("pmg_l3_light_v1", "pmg", velocity_iter=1, pressure_mg=(3, 1, 1, 16, 1)),
40 make_config("pmg_l3_light_v4", "pmg", velocity_iter=4, pressure_mg=(3, 1, 1, 16, 1)),
41 make_config("pmg_l3_light_b8", "pmg", pressure_mg=(3, 1, 1, 8, 1)),
42 make_config("pmg_l3_light_b32", "pmg", pressure_mg=(3, 1, 1, 32, 1)),
43 make_config("pmg_l3_mid", "pmg", pressure_mg=(3, 1, 1, 16, 2)),
44 make_config("pmg_l3_default", "pmg", pressure_mg=(3, 2, 2, 32, 2)),
45 make_config("pmg_l4_light", "pmg", pressure_mg=(4, 1, 1, 16, 1)),
46 make_config("fullmg_l2_balanced", "fullmg", pressure_mg=(2, 1, 1, 16, 1), velocity_mg=(2, 2, 2, 32, 1)),
47 make_config("fullmg_l3_balanced", "fullmg", pressure_mg=(3, 1, 1, 16, 1), velocity_mg=(3, 2, 2, 32, 1)),
48 make_config("fullmg_l3_inner4", "fullmg", pressure_mg=(3, 1, 1, 16, 1), velocity_mg=(3, 4, 4, 32, 1)),
49 make_config("fullmg_l3_robust", "fullmg", pressure_mg=(3, 2, 2, 32, 1), velocity_mg=(3, 4, 4, 64, 1)),
50]
51
52
53def apply_config(args, config):
54 cfg = deepcopy(args)
55 cfg.pressure_iter = config["pressure_iter"]
56 cfg.velocity_iter = config["velocity_iter"]
57 (
58 cfg.pressure_mg_levels,
59 cfg.pressure_mg_pre,
60 cfg.pressure_mg_post,
61 cfg.pressure_mg_bottom,
62 cfg.pressure_mg_cycles,
63 ) = config["pressure_mg"]
64 (
65 cfg.velocity_mg_levels,
66 cfg.velocity_mg_pre,
67 cfg.velocity_mg_post,
68 cfg.velocity_mg_bottom,
69 cfg.velocity_mg_cycles,
70 ) = config["velocity_mg"]
71 return cfg
72
73
74def run_config(base_args, sdf_zyx, origin, spacing, config):
75 args = apply_config(base_args, config)
76 row = {
77 "name": config["name"],
78 "method": config["method"],
79 "pressure_iter": config["pressure_iter"],
80 "velocity_iter": config["velocity_iter"],
81 "pressure_mg": config["pressure_mg"],
82 "velocity_mg": config["velocity_mg"],
83 "status": "ok",
84 }
85 try:
86 result = run_case(args, sdf_zyx, origin, spacing, config["method"])
87 row.update(result)
88 except Exception as exc:
89 row.update(
90 {
91 "status": f"failed: {exc}",
92 "elapsed_s": float("nan"),
93 "per_step_s": float("nan"),
94 "steps_taken": 0,
95 "outer_iterations_mean": float("nan"),
96 "outer_iterations_max": 0,
97 "u_mean": float("nan"),
98 "v_mean": float("nan"),
99 "w_mean": float("nan"),
100 "p_mean": float("nan"),
101 "u_abs_max": float("nan"),
102 "p_abs_max": float("nan"),
103 }
104 )
105 return row
106
107
108def write_csv(rows, output_csv):
109 os.makedirs(os.path.dirname(output_csv), exist_ok=True)
110 fieldnames = [
111 "name",
112 "method",
113 "pressure_iter",
114 "velocity_iter",
115 "pressure_mg",
116 "velocity_mg",
117 "elapsed_s",
118 "per_step_s",
119 "steps_taken",
120 "outer_iterations_mean",
121 "outer_iterations_max",
122 "u_mean",
123 "u_abs_max",
124 "p_abs_max",
125 "status",
126 ]
127 with open(output_csv, "w", newline="", encoding="utf-8") as handle:
128 writer = csv.DictWriter(handle, fieldnames=fieldnames)
129 writer.writeheader()
130 for row in rows:
131 writer.writerow({name: row.get(name) for name in fieldnames})
132
133
134def print_summary(rows, args):
135 print(f"Packing SDF: {args.sdf}")
136 print(
137 f"dt={args.dt}, max_steps={args.max_steps}, convergence_stride={args.convergence_stride}, "
138 f"convergence_rel_tol={args.convergence_rel_tol}, outer_tol={args.outer_tol}, outer_mode={args.outer_mode}"
139 )
140 print()
141 print(
142 f"{'config':<20} {'method':<8} {'time(s)':>10} {'steps':>8} {'outer_mean':>12} "
143 f"{'outer_max':>10} {'u_mean':>12} {'status':<24}"
144 )
145 print("-" * 110)
146 best = sorted(
147 rows,
148 key=lambda row: (
149 row["status"] != "ok",
150 row["elapsed_s"] if row["elapsed_s"] == row["elapsed_s"] else float("inf"),
151 ),
152 )
153 for row in best:
154 print(
155 f"{row['name']:<20} {row['method']:<8} {row['elapsed_s']:10.3f} "
156 f"{row['steps_taken']:8d} {row['outer_iterations_mean']:12.2f} "
157 f"{row['outer_iterations_max']:10d} {row['u_mean']:12.4e} {row['status']:<24}"
158 )
159
160
162 parser = argparse.ArgumentParser(
163 description="Tune RBGS and multigrid settings on a real SDF packing geometry."
164 )
165 parser.add_argument(
166 "--sdf",
167 default=os.path.abspath(os.path.join(THIS_DIR, "../ring_packing_sdf.vti")),
168 )
169 parser.add_argument("--configs", nargs="+", default=None)
170 parser.add_argument("--dt", type=float, default=1.0)
171 parser.add_argument("--max-steps", type=int, default=100)
172 parser.add_argument("--convergence-stride", type=int, default=10)
173 parser.add_argument("--convergence-rel-tol", type=float, default=1e-8)
174 parser.add_argument("--force-x", type=float, default=1.0)
175 parser.add_argument("--force-y", type=float, default=0.0)
176 parser.add_argument("--force-z", type=float, default=0.0)
177 parser.add_argument("--rho", type=float, default=0.0)
178 parser.add_argument("--mu", type=float, default=1.0)
179 parser.add_argument("--ibm-scheme", type=int, default=0)
180 parser.add_argument("--outer-iterations", type=int, default=800)
181 parser.add_argument("--outer-tol", type=float, default=1e-6)
182 parser.add_argument("--outer-mode", type=int, default=1)
183 parser.add_argument(
184 "--output-csv",
185 default="output/packing_multigrid_tuning.csv",
186 )
187 return parser.parse_args()
188
189
190def main():
191 args = parse_args()
192 if args.configs is None:
193 configs = CONFIGS
194 else:
195 wanted = set(args.configs)
196 configs = [config for config in CONFIGS if config["name"] in wanted]
197 missing = sorted(wanted - {config["name"] for config in configs})
198 if missing:
199 raise ValueError(f"unknown configs requested: {missing}")
200
201 sdf_zyx, origin, spacing = load_sdf(args.sdf)
202 warm_up_cuda()
203
204 rows = []
205 for config in configs:
206 print(f"Running config={config['name']}")
207 rows.append(run_config(args, sdf_zyx, origin, spacing, config))
208
209 write_csv(rows, args.output_csv)
210 print_summary(rows, args)
211 print(f"Wrote tuning CSV to {args.output_csv}")
212
213
214if __name__ == "__main__":
215 main()
run_config(base_args, sdf_zyx, origin, spacing, config)
make_config(name, method, pressure_iter=50, velocity_iter=2, pressure_mg=(4, 1, 1, 16, 1), velocity_mg=(4, 2, 2, 32, 1))