//! S2-5 host ladder: DFG 2D-1 (Re 20) on a periodic-z slab of 4 cells with //! every knob of the cut wall read from the environment — the instrument //! for host-only prototypes (`RTX_E3_PRESSURE_CENTROID=1`). Reference //! c_D 5.57954, c_L 0.010619, Δp 0.117520. `RTX_E3_DFG_NY` (default 62). use rtx_cfd::solvers::incompressible::ConvectionScheme; use rtx_cfd::solvers::incompressible::embedded3::{ Body, Boundaries, Field, Fluid, Grid, Parameters, Side, Solver, WallScheme, }; const H: f64 = 0.41; const L: f64 = 2.2; const D: f64 = 0.1; const CX: f64 = 0.2; const CY: f64 = 0.2; const U_M: f64 = 0.3; const U_BAR: f64 = 2.0 / 3.0 * U_M; const RHO: f64 = 1.0; const NU: f64 = 1e-3; fn inflow(y: f64, _z: f64) -> f64 { 4.0 * U_M * y * (H - y) / (H * H) } #[test] #[ignore = "host DFG at ny 31 with the routes split (about half an hour)"] fn dfg_2d1_on_the_host() { let ny: usize = std::env::var("RTX_E3_DFG_NY") .ok() .and_then(|v| v.parse().ok()) .unwrap_or(62); let h = H / ny as f64; let nx = (L / h).round() as usize; let nz = 4; let lz = nz as f64 * h; let dt = (0.3 * h / U_M).min(0.5 * h * h / (6.0 * NU)); let mut solver = Solver::new( Fluid { density: RHO, viscosity: RHO * NU, reference_velocity: U_BAR, reference_length: D, }, Parameters { corrector_steps: 2, tolerance: 1e-8, convection_scheme: ConvectionScheme::TvdVanAlbada, wall_scheme: WallScheme::CutCell, boundaries: Boundaries { x1: Side::PressureOutlet, z0: Side::Periodic, z1: Side::Periodic, ..Boundaries::default() }, ..Parameters::default() }, ); solver.set_boundary_velocity(|x, y, z, _t| { if x <= 0.0 { (inflow(y, z), 0.0, 0.0) } else { (0.0, 0.0, 0.0) } }); solver.set_body(Body::extruded( rtx_cfd::solvers::incompressible::EmbeddedBody::circle(CX, CY, 0.5 * D), lz, )); let g = Grid::cubic(nx, ny, nz, h); let mut field = Field::new(g); for k in 0..nz { for j in 0..ny { let u0 = inflow((j as f64 + 0.5) * h, (k as f64 + 0.5) * h); for i in 0..=nx { field.u[g.uface(k, j, i)] = u0; } } } solver.initialize(&mut field); let coef = 2.0 / (RHO * U_BAR * U_BAR * D * lz); let steps = (12.0 / dt).ceil() as usize; let start = std::time::Instant::now(); let mut last = (0.0, 0.0); for step in 0..steps { let r = solver.advance(&mut field, dt); if (step + 1) % (steps / 20).max(1) == 0 || step + 1 == steps { let t = solver.time(); let mask = solver.mask().unwrap(); let body = solver.body().unwrap(); let mu = RHO * NU; let (po, so) = mask.cut_wall_force_parts(body, &field, mu, t).unwrap(); let ex = mask .cut_wall_exchange_force(body, &field, mu, RHO, t, None) .unwrap(); let (pr, sr) = mask .cut_wall_force_reconstructed_parts(body, &field, mu, t, None) .unwrap(); let margin = 1.5 * D; let ci = |x: f64| ((x / h).round() as usize).clamp(2, nx - 2); let cj = |y: f64| ((y / h).round() as usize).clamp(2, ny - 2); let bx = ( ci(CX - margin), ci(CX + margin), cj(CY - 0.15), cj(CY + 0.15), 0, nz, ); let fcv = mask.control_volume_force_with_walls(&field, dt, RHO, mu, None, bx, false); let cd = |f: [f64; 3]| coef * f[0]; println!( " t {t:7.3}: c_D operator {:.4} (p {:.4} + s {:.4} + exchange {:.4}) | reconstructed {:.4} (p {:.4} + s {:.4}) | box {:.4} c_L {:.5} Δp {:.5} (quadratic probes {:.5}); residual {:.1e} [{:.0} s]", cd(po) + cd(so) + cd(ex), cd(po), cd(so), cd(ex), cd(pr) + cd(sr), cd(pr), cd(sr), cd(fcv), coef * (po[1] + so[1] + ex[1]), mask.pressure_at(&field.p, CX - 0.5 * D, CY, 0.5 * lz) .unwrap_or(f64::NAN) - mask .pressure_at(&field.p, CX + 0.5 * D, CY, 0.5 * lz) .unwrap_or(f64::NAN), { // Δp by quadratic extrapolation along the normal from probes // at 1.5 h, 2.5 h, 3.5 h (whole-fluid stencils only). let wall_p = |sign: f64| { let at = |d: f64| { mask.pressure_at(&field.p, CX + sign * (0.5 * D + d * h), CY, 0.5 * lz) .unwrap_or(f64::NAN) }; 4.375 * at(1.5) - 5.25 * at(2.5) + 1.875 * at(3.5) }; wall_p(-1.0) - wall_p(1.0) }, r.final_residual, start.elapsed().as_secs_f64() ); let now = (cd(po) + cd(so) + cd(ex), cd(fcv)); if (now.0 - last.0).abs() < 1e-4 * now.0.abs() && (now.1 - last.1).abs() < 1e-4 * now.1.abs() && t > 2.0 { println!(" settled"); break; } last = now; } } }