flow
Kokkos cut-cell IBM incompressible Navier-Stokes solver + pnm pore extraction
Loading...
Searching...
No Matches
rayleigh_taylor.py
Go to the documentation of this file.
1#!/usr/bin/env python
2"""Variable-density validation (Phase 5): hydrostatic acid tests + Rayleigh-Taylor demonstrator.
3
41. Hydrostatic balance (the acid test): a stratified two-layer fluid at rest under gravity must
5 STAY at rest, with the discrete pressure gradient exactly rho_face*g. This detects any
6 inconsistency between the momentum face density, the body-force face value, and the projection
7 face coefficient. Inviscid: max steady velocity ~1e-16, P-gradient error ~1e-16 at density
8 ratios 3 AND 1000. (The C++ ctest `vardensity_projection` runs the same case.)
9
102. Rayleigh-Taylor: heavy over light (ratio 3, Atwood 0.5) through the FULL two-phase chain — a
11 TRANSPORTED phase fraction c drives rho via a linear-mixture closure (auto-enabling the
12 variable-density path), gravity is a closure force_z = -g*rho, momentum + projection carry the
13 variable density. The interface amplitude grows ~exponentially then nonlinearly; the measured
14 early growth rate is ~0.74x the inviscid sqrt(A g k) (viscous + finite-interface damping).
15
16Run: PYTHONPATH=<build> python rayleigh_taylor.py
17"""
18import numpy as np
19import peclet.flow as F
20
21
22def hydrostatic(ratio, mu=0.0, steps=100, g=0.1, dt=1.0, N=8, NZ=24):
23 s = F.Solver(N, N, NZ)
24 s.set_rho(1.0); s.set_mu(mu); s.set_dt(dt)
25 s.set_domain_bc(4, 1, 0, 0, 0); s.set_domain_bc(5, 1, 0, 0, 0) # walls +-z
26 s.set_pressure_geometry(np.asfortranarray(np.full((N, N, NZ), 10.0)))
27 z = np.arange(NZ)
28 rz = np.where(z < NZ // 2, ratio, 1.0).astype(np.float64) # heavy below (stable)
29 s.add_field("rho")
30 s.set_field("rho", np.asfortranarray(np.broadcast_to(rz[None, None, :], (N, N, NZ)).copy()))
31 s.set_density_mode("variable") # Chebyshev pressure driver
32 s.set_property_model("force_z", "linear", "rho", [0.0, -g])
33 m = None
34 for _ in range(steps):
35 s.step()
36 m = max(np.abs(s.get_u()).max(), np.abs(s.get_v()).max(), np.abs(s.get_w()).max())
37 p = s.get_p()
38 dp = p[N//2, N//2, 1:] - p[N//2, N//2, :-1]
39 rf = 0.5 * (rz[1:] + rz[:-1])
40 perr = np.max(np.abs(dp + g * rf)) / (g * ratio)
41 return m, perr
42
43
44def rayleigh_taylor(N=48, NZ=96, g=0.005, mu=0.002, dt=1.0, steps=240):
45 s = F.Solver(N, 4, NZ)
46 s.set_rho(1.0); s.set_mu(mu); s.set_dt(dt)
47 s.set_advection(True)
48 s.set_domain_bc(4, 1, 0, 0, 0); s.set_domain_bc(5, 1, 0, 0, 0)
49 s.set_pressure_geometry(np.asfortranarray(np.full((N, 4, NZ), 10.0)))
50 s.add_scalar("c", diffusivity=0.0, scheme=1, iters=1)
51 s.set_scalar_bc("c", 4, 1, 0.0); s.set_scalar_bc("c", 5, 1, 0.0)
52 s.set_property_model("rho", "linear", "c", [1.0, 2.0]) # rho = 1 + 2c (ratio 3)
53 s.set_property_model("force_z", "linear", "rho", [0.0, -g])
54 x, z = np.arange(N), np.arange(NZ)
55 zi = NZ / 2 + 1.5 * np.cos(2 * np.pi * x / N)
56 c0 = np.zeros((N, 4, NZ))
57 for i in range(N):
58 c0[i, :, :] = 0.5 * (1.0 + np.tanh((z[None, :] - zi[i]) / 1.5)) # heavy on top
59 s.set_field("c", np.asfortranarray(c0))
60
61 def amp():
62 c = s.get_field("c")[:, 1, :]
63 zc = np.array([np.interp(0.5, c[i, :], z) for i in range(N)])
64 return 0.5 * (zc.max() - zc.min())
65
66 hist = [amp()]
67 for it in range(steps):
68 s.step()
69 if it % 40 == 39:
70 hist.append(amp())
71 return hist
72
73
74if __name__ == "__main__":
75 for ratio in (3.0, 1000.0):
76 m, perr = hydrostatic(ratio)
77 print(f"hydrostatic ratio {ratio:g}: steady max|u| {m:.2e} P-grad rel-err {perr:.2e}")
78 assert m < 1e-12 and perr < 1e-11
80 growth = hist[-1] / hist[0]
81 print(f"Rayleigh-Taylor amplitude: {' -> '.join(f'{h:.2f}' for h in hist)} (x{growth:.1f})")
82 assert growth > 3.0 and all(hist[i+1] >= hist[i] * 0.98 for i in range(len(hist) - 1))
83 print("PASS")
hydrostatic(ratio, mu=0.0, steps=100, g=0.1, dt=1.0, N=8, NZ=24)