56TOL_K, CHECK_EVERY, N_PASS, MAX_STEPS = 1e-4, 5, 3, 1500
85 s = Cls((N, N, N), extent=(L, L, L))
89 s.set_dt(dt
if dt
is not None else 6.0 * h * h / mu)
90 s.set_body_force((1.0, 0.0, 0.0))
93 s.set_advection_scheme(advection)
95 s.set_advection(
False)
96 s.diagnostics.set_velocity_solver_params(200)
97 s.set_pressure_multigrid(
True, levels=max(2, int(np.log2(N)) - 1))
99 s.set_pressure_pcg(
True, max_iter=400, rtol=1e-12)
100 s.set_velocity_residual_tolerance(1e-12)
102 s.set_pressure_pcg(
True, max_iter=200, rtol=1e-8)
103 cx, cy, cz = s.cell_centers()
104 X, Y, Z = np.meshgrid(cx, cy, cz, indexing=
"ij")
105 R = (3.0 * PHI / (4.0 * math.pi)) ** (1.0 / 3.0) * L
106 sdf = np.sqrt((X - 0.5 * L) ** 2 + (Y - 0.5 * L) ** 2 + (Z - 0.5 * L) ** 2) - R
107 s.set_solid(np.asfortranarray(sdf), cutcell_pressure=
True)
150 print(
"G7a: a case whose plain march diverges -> converged=False for both drivers",
152 for acc, window
in ((
False, 5), (
True, 3), (
True, 5), (
True, 8)):
153 s =
sphere_solver(pf.Solver, 16, tight=
False, mu=0.0158, dt=1.234e-2, advection=
"sou")
154 res = pf.march_to_steady(s,
lambda s=s:
umean(s), rtol=1e-4, max_steps=5000,
155 accelerate=acc, window=window)
156 check(
not res.converged
and res.reason
in (
"diverged",
"max_steps"),
157 f
"accelerate={acc} window={window}: converged={res.converged} reason={res.reason} "
158 f
"steps={res.steps} (accelerated {res.accelerated_steps})")
175 print(
"adapter: signature reset, refusal re-check, configuration change (review R4, R5)",
177 def no_mix_after(Cls, change, what):
178 """`change(solver)` between calls: num_resets + 1 and NO mix on that call. Twins: four
179 identical solvers take the same 10 accelerated calls (bit-identical trajectories). C (True)
180 and D (False) without the change differ, so a mix is pending (the check is not vacuous);
181 A (True) and B (False) after the change are bitwise equal, so A's call did not mix."""
188 pending = any(
not np.array_equal(x, y)
for x, y
in zip(
state_of(sc),
state_of(sd)))
194 plain = all(np.array_equal(x, y)
for x, y
in zip(
state_of(sa),
state_of(sb)))
195 check(pending
and plain
and aa.num_resets == r0 + 1
and aa.status ==
"active",
196 f
"{what}: num_resets {r0} -> {aa.num_resets}, the call is bitwise the plain step "
197 f
"{plain} (a mix was pending without the change: {pending})")
199 dt2 = 2.0 * 6.0 * (L / 12) ** 2
201 no_mix_after(pf.Solver,
lambda s: s.set_advection(
True),
"staggered: set_advection(True)")
202 for name, Cls
in SCHEMES:
203 no_mix_after(Cls,
lambda s: s.set_dt(dt2), f
"{name}: set_dt between calls")
206 s.diagnostics.set_pressure_warmstart(
True)
210 except RuntimeError
as e:
212 check(msg
is not None and "pressure warm start" in msg
and "accelerate=False" in msg,
213 f
"{name}: set_pressure_warmstart(True) after construction -> refused at the next "
219 s.set_advection(
True)
223 except Exception
as e:
225 check(msg
is not None and "configuration change" in msg
and "reallocated" not in msg
226 and acc.num_resets == r0 + 1,
227 f
"collocated: set_advection(True) after construction -> {msg!r}, num_resets {r0} -> "
245 step, monitor, state =
scripted(values)
246 counter = steady._Counter()
247 args = dict(rtol=1e-4, check_every=5, num_passes=3, slow_rate=0.997, roundoff=1e-11,
248 max_steps=10 ** 6, counter=counter, callback=
None, phase=
"certify")
250 out = steady._certify(step, monitor, **args)
251 return out, counter.steps
263 print(
"driver certification on scripted monitor sequences", flush=
True)
272 blocks.append(blocks[-1] + d)
276 blocks.append(blocks[-1] + d)
278 growth_residual=
lambda: 0.0, growth_ref=1.0)
279 check(out ==
"pass" and steps <= 5 * budget, f
"(i) R > 1 then a geometric tail: {out} after "
280 f
"{steps} steps ({steps // 5} blocks)")
286 blocks.append(blocks[-1] + d)
289 growth_residual=
lambda: 0.0, growth_ref=1.0)
290 check(out ==
"slow" and steps == 15, f
"(ii) clean R = 0.99 tail: {out} after {steps} steps "
291 "(expected 'slow' at step 15, the first block with a ratio)")
293 blocks = [1.0 + (1e-3
if k % 2
else 0.0)
for k
in range(40)]
295 growth_residual=
lambda: 0.0, growth_ref=1.0)
296 check(out ==
"budget" and steps == 5 * budget,
297 f
"(iii) never passing: {out} after {steps} steps (expected 'budget' at {5 * budget})")
303 blocks.append(blocks[-1] + d)
306 blocks.append(blocks[-1] + d)
309 blocks.append(blocks[-1] + d)
311 growth_residual=
lambda: 0.0, growth_ref=1.0)
312 check(out ==
"budget" and steps == 5 * budget,
313 f
"(iv) two passes at R = 0.9, then R = 1.1: {out} after {steps} steps (expected 'budget' "
314 f
"at {5 * budget}, never 'pass')")