R4-i + R5 design pass: RTX_FSI2O_REGEN_ONCE (one patch regeneration per coupled step; dies at step 0 — the registered test is void), the step CSV's subit/dres_y/dres_norm/fx_nodal/fy_nodal columns; the cut predictor's per-face term probe (enable_term_probe) and the curved instrument's exact side/wall viscous integrals — the static convex wall's flat residual is the viscous closure's first-order relative accuracy on O(1/h) fluxes
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / WASM Build + Size Check (push) Blocked by required conditions
CI / Distributed Training Tests (push) Blocked by required conditions
CI / CI Success (push) Blocked by required conditions
CI / Build (macos-latest) (push) Waiting to run
CI / Build CPU-Only (Explicit) (push) Failing after 4s
CI / Clippy Check (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 4s
CI / Format Check (push) Failing after 12s
CI / Build (ubuntu-latest) (push) Failing after 1m26s
Documentation / Build API Documentation (push) Failing after 1m28s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m7s
CI / Test (macos-latest) (push) Blocked by required conditions
CI / Test (ubuntu-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (macos-latest) (push) Blocked by required conditions
CI / Python Bindings (maturin) (ubuntu-latest) (push) Blocked by required conditions
CI / WASM Build + Size Check (push) Blocked by required conditions
CI / Distributed Training Tests (push) Blocked by required conditions
CI / CI Success (push) Blocked by required conditions
CI / Build (macos-latest) (push) Waiting to run
CI / Build CPU-Only (Explicit) (push) Failing after 4s
CI / Clippy Check (push) Failing after 4s
Documentation / Build User Guide (push) Successful in 4s
CI / Format Check (push) Failing after 12s
CI / Build (ubuntu-latest) (push) Failing after 1m26s
Documentation / Build API Documentation (push) Failing after 1m28s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m7s
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
f5735fdeaa
commit
501b45f9d0
+24
-3
@@ -111,7 +111,8 @@ impl Solver {
|
|||||||
let cv = mask.cv_geometry(c, p);
|
let cv = mask.cv_geometry(c, p);
|
||||||
// S2-7b: the convective sides' own apertures (host prototype).
|
// S2-7b: the convective sides' own apertures (host prototype).
|
||||||
let conv_parts = if mask.conv_sides_exact {
|
let conv_parts = if mask.conv_sides_exact {
|
||||||
mask.cut().and_then(|cut| mask.exact_cv_side_parts(cut, c, p))
|
mask.cut()
|
||||||
|
.and_then(|cut| mask.exact_cv_side_parts(cut, c, p))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
@@ -159,7 +160,9 @@ impl Solver {
|
|||||||
// The two half faces' own apertures times their faces' values.
|
// The two half faces' own apertures times their faces' values.
|
||||||
let v = |q: [i64; 3]| val(d, q).unwrap_or(0.0);
|
let v = |q: [i64; 3]| val(d, q).unwrap_or(0.0);
|
||||||
(
|
(
|
||||||
0.5 * (parts[d][1].0 * v(add(cell_minus, ed, 1)) + parts[d][1].1 * v(add(cell_plus, ed, 1))) * a_d,
|
0.5 * (parts[d][1].0 * v(add(cell_minus, ed, 1))
|
||||||
|
+ parts[d][1].1 * v(add(cell_plus, ed, 1)))
|
||||||
|
* a_d,
|
||||||
0.5 * (parts[d][0].0 * v(cell_minus) + parts[d][0].1 * v(cell_plus)) * a_d,
|
0.5 * (parts[d][0].0 * v(cell_minus) + parts[d][0].1 * v(cell_plus)) * a_d,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -260,7 +263,8 @@ impl Solver {
|
|||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
let cvq = mask.cv_geometry(c, q);
|
let cvq = mask.cv_geometry(c, q);
|
||||||
let ubq = mask.surface_velocity_at(body, foot_of(q, lat.face_position(c, q)), c, t_old);
|
let ubq =
|
||||||
|
mask.surface_velocity_at(body, foot_of(q, lat.face_position(c, q)), c, t_old);
|
||||||
// Explicit, with a coefficient ∝ 1/d_f: take the gradient from
|
// Explicit, with a coefficient ∝ 1/d_f: take the gradient from
|
||||||
// the faces at least TRANSVERSE_DISTANCE_FLOOR h off the wall —
|
// the faces at least TRANSVERSE_DISTANCE_FLOOR h off the wall —
|
||||||
// a FULL neighbour too, over its own distance along the cut
|
// a FULL neighbour too, over its own distance along the cut
|
||||||
@@ -426,6 +430,23 @@ impl Solver {
|
|||||||
+ wall_rhs)
|
+ wall_rhs)
|
||||||
/ (inertia + shear + wall_implicit);
|
/ (inertia + shear + wall_implicit);
|
||||||
let v_alpha = fraction * h[c] * area[c];
|
let v_alpha = fraction * h[c] * area[c];
|
||||||
|
if let Some(tp) = self.term_probe.borrow_mut().as_mut() {
|
||||||
|
let m = rho * v_eff;
|
||||||
|
let terms = [
|
||||||
|
-conv / m,
|
||||||
|
diff / m,
|
||||||
|
(shear * (ub - u_star) - shear_explicit) / m,
|
||||||
|
pressure / m,
|
||||||
|
source / m,
|
||||||
|
(wall_rhs - wall_implicit * u_star) / m,
|
||||||
|
(u_star - u0) / dt,
|
||||||
|
];
|
||||||
|
let v = &mut tp[c];
|
||||||
|
if v.len() <= idx_f {
|
||||||
|
v.resize(idx_f + 1, [0.0; 7]);
|
||||||
|
}
|
||||||
|
v[idx_f] = terms;
|
||||||
|
}
|
||||||
(u_star, rho * (v_eff - v_alpha) * (u_star - u0) / dt)
|
(u_star, rho * (v_eff - v_alpha) * (u_star - u0) / dt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,7 +230,8 @@ impl Default for Parameters {
|
|||||||
.is_ok_and(|v| v == "off"),
|
.is_ok_and(|v| v == "off"),
|
||||||
// ON by default since S2-7 (`=h` reproduces the records before it).
|
// ON by default since S2-7 (`=h` reproduces the records before it).
|
||||||
wall_exchange_axis: std::env::var("RTX_E3_WALL_EXCHANGE").map_or(true, |v| v != "h"),
|
wall_exchange_axis: std::env::var("RTX_E3_WALL_EXCHANGE").map_or(true, |v| v != "h"),
|
||||||
wall_exchange_foot: std::env::var("RTX_E3_WALL_EXCHANGE").is_ok_and(|v| v == "axisfoot"),
|
wall_exchange_foot: std::env::var("RTX_E3_WALL_EXCHANGE")
|
||||||
|
.is_ok_and(|v| v == "axisfoot"),
|
||||||
conv_sides_exact: std::env::var("RTX_E3_CONV_SIDES").is_ok_and(|v| v == "exact"),
|
conv_sides_exact: std::env::var("RTX_E3_CONV_SIDES").is_ok_and(|v| v == "exact"),
|
||||||
wall_flux_true_normal: std::env::var("RTX_E3_WALL_FLUX").is_ok_and(|v| v == "true"),
|
wall_flux_true_normal: std::env::var("RTX_E3_WALL_FLUX").is_ok_and(|v| v == "true"),
|
||||||
wall_foot_centroid: std::env::var("RTX_E3_WALL_FOOT").is_ok_and(|v| v == "centroid"),
|
wall_foot_centroid: std::env::var("RTX_E3_WALL_FOOT").is_ok_and(|v| v == "centroid"),
|
||||||
@@ -238,7 +239,8 @@ impl Default for Parameters {
|
|||||||
momentum_volume_tiled: std::env::var("RTX_E3_MOMENTUM_VOLUME")
|
momentum_volume_tiled: std::env::var("RTX_E3_MOMENTUM_VOLUME")
|
||||||
.is_ok_and(|v| v == "tiled"),
|
.is_ok_and(|v| v == "tiled"),
|
||||||
cv_sides_exact: std::env::var("RTX_E3_CV_SIDES").is_ok_and(|v| v == "exact"),
|
cv_sides_exact: std::env::var("RTX_E3_CV_SIDES").is_ok_and(|v| v == "exact"),
|
||||||
wall_order2_centroid: std::env::var("RTX_E3_WALL_ORDER2").is_ok_and(|v| v == "centroid"),
|
wall_order2_centroid: std::env::var("RTX_E3_WALL_ORDER2")
|
||||||
|
.is_ok_and(|v| v == "centroid"),
|
||||||
// ON by default since S2-5 (`=0` reproduces the records before it).
|
// ON by default since S2-5 (`=0` reproduces the records before it).
|
||||||
diffusion_centroid: std::env::var("RTX_E3_DIFFUSION_CENTROID")
|
diffusion_centroid: std::env::var("RTX_E3_DIFFUSION_CENTROID")
|
||||||
.map_or(true, |v| v != "0"),
|
.map_or(true, |v| v != "0"),
|
||||||
@@ -269,6 +271,11 @@ pub struct Solver {
|
|||||||
/// cut predictor only): the box route counts it, the operator route
|
/// cut predictor only): the box route counts it, the operator route
|
||||||
/// does not.
|
/// does not.
|
||||||
pub(super) floor_source: std::cell::Cell<[f64; 3]>,
|
pub(super) floor_source: std::cell::Cell<[f64; 3]>,
|
||||||
|
/// R5 design-pass instrument (host cut predictor only): per unknown
|
||||||
|
/// face of each component, the last step's momentum terms as
|
||||||
|
/// accelerations `[−conv, diff, wall shear, pressure, source, solid
|
||||||
|
/// exchange, total] / (ρ V_eff)`; `None` (the default) records nothing.
|
||||||
|
pub(super) term_probe: std::cell::RefCell<Option<[Vec<[f64; 7]>; 3]>>,
|
||||||
/// The closure lag of the pressure corrections over the last step
|
/// The closure lag of the pressure corrections over the last step
|
||||||
/// (`Mask::closure_lag` summed over the correctors; host path).
|
/// (`Mask::closure_lag` summed over the correctors; host path).
|
||||||
pub(super) pressure_lag: std::cell::Cell<[f64; 3]>,
|
pub(super) pressure_lag: std::cell::Cell<[f64; 3]>,
|
||||||
@@ -308,6 +315,7 @@ impl Solver {
|
|||||||
params,
|
params,
|
||||||
momentum_source: None,
|
momentum_source: None,
|
||||||
floor_source: std::cell::Cell::new([0.0; 3]),
|
floor_source: std::cell::Cell::new([0.0; 3]),
|
||||||
|
term_probe: std::cell::RefCell::new(None),
|
||||||
pressure_lag: std::cell::Cell::new([0.0; 3]),
|
pressure_lag: std::cell::Cell::new([0.0; 3]),
|
||||||
vol_old: Vec::new(),
|
vol_old: Vec::new(),
|
||||||
apertures_old: None,
|
apertures_old: None,
|
||||||
@@ -402,6 +410,16 @@ impl Solver {
|
|||||||
.expect("embedded mask")
|
.expect("embedded mask")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// R5 instrument: record the cut predictor's momentum terms per face
|
||||||
|
/// from the next step on (host path only; a no-op on the device path).
|
||||||
|
pub fn enable_term_probe(&self) {
|
||||||
|
*self.term_probe.borrow_mut() = Some([Vec::new(), Vec::new(), Vec::new()]);
|
||||||
|
}
|
||||||
|
/// The recorded terms per component face index (see `term_probe`).
|
||||||
|
#[must_use]
|
||||||
|
pub fn term_probe(&self) -> Option<[Vec<[f64; 7]>; 3]> {
|
||||||
|
self.term_probe.borrow().clone()
|
||||||
|
}
|
||||||
/// The small-cell floor's momentum source over the last step (a force
|
/// The small-cell floor's momentum source over the last step (a force
|
||||||
/// on the fluid; host cut predictor only, zero otherwise).
|
/// on the fluid; host cut predictor only, zero otherwise).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
|||||||
@@ -58,9 +58,17 @@ impl Exact {
|
|||||||
let (dx, dy) = (x - CENTRE.0, y - CENTRE.1);
|
let (dx, dy) = (x - CENTRE.0, y - CENTRE.1);
|
||||||
let r = (dx * dx + dy * dy).sqrt().max(1e-12);
|
let r = (dx * dx + dy * dy).sqrt().max(1e-12);
|
||||||
let ut = if r < R1 {
|
let ut = if r < R1 {
|
||||||
if rigid || !outer_drives() { OMEGA * r } else { 0.0 }
|
if rigid || !outer_drives() {
|
||||||
|
OMEGA * r
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
} else if r > R2 {
|
} else if r > R2 {
|
||||||
if rigid || outer_drives() { OMEGA * r } else { 0.0 }
|
if rigid || outer_drives() {
|
||||||
|
OMEGA * r
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.u_theta(r)
|
self.u_theta(r)
|
||||||
};
|
};
|
||||||
@@ -145,12 +153,14 @@ fn reading(n: usize, rigid: bool) {
|
|||||||
for k in 0..nz {
|
for k in 0..nz {
|
||||||
for j in 0..ny {
|
for j in 0..ny {
|
||||||
for i in 0..=nx {
|
for i in 0..=nx {
|
||||||
field.u[g.uface(k, j, i)] = ex.velocity(i as f64 * h, (j as f64 + 0.5) * h, rigid).0;
|
field.u[g.uface(k, j, i)] =
|
||||||
|
ex.velocity(i as f64 * h, (j as f64 + 0.5) * h, rigid).0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for j in 0..=ny {
|
for j in 0..=ny {
|
||||||
for i in 0..nx {
|
for i in 0..nx {
|
||||||
field.v[g.vface(k, j, i)] = ex.velocity((i as f64 + 0.5) * h, j as f64 * h, rigid).1;
|
field.v[g.vface(k, j, i)] =
|
||||||
|
ex.velocity((i as f64 + 0.5) * h, j as f64 * h, rigid).1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -287,7 +297,8 @@ fn reading(n: usize, rigid: bool) {
|
|||||||
for i in 0..=nx {
|
for i in 0..=nx {
|
||||||
let f = g.uface(0, j, i);
|
let f = g.uface(0, j, i);
|
||||||
if mask.u_kind(f) == rtx_cfd::solvers::incompressible::embedded3::FaceKind::Ghost {
|
if mask.u_kind(f) == rtx_cfd::solvers::incompressible::embedded3::FaceKind::Ghost {
|
||||||
let e = (field.u[f] - ex.velocity(i as f64 * h, (j as f64 + 0.5) * h, rigid).0) / (OMEGA * R1);
|
let e = (field.u[f] - ex.velocity(i as f64 * h, (j as f64 + 0.5) * h, rigid).0)
|
||||||
|
/ (OMEGA * R1);
|
||||||
gsq += e * e;
|
gsq += e * e;
|
||||||
gn += 1;
|
gn += 1;
|
||||||
gmax = gmax.max(e.abs());
|
gmax = gmax.max(e.abs());
|
||||||
@@ -303,7 +314,13 @@ fn reading(n: usize, rigid: bool) {
|
|||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
" {} n {n}: walls' offsets {off_in:+.4} h (inner) {off_out:+.4} h (outer), positive = inside the fluid; fit A {a:.5} B {b:.5} (exact {:.5} {:.5}, {} points); pressure error of ρ(ΩR1)²: full cells {:.3e} ({cnt}), cut cells {:.3e} mean {:+.3e} ({n_cut}), fraction < 0.5 {:.3e} ({n_small}), ≥ 0.5 {:.3e} ({n_large}), inner wall {:.3e} ({n_in}), outer wall {:.3e} ({n_out}); merged {}; residual {last_res:.1e}",
|
" {} n {n}: walls' offsets {off_in:+.4} h (inner) {off_out:+.4} h (outer), positive = inside the fluid; fit A {a:.5} B {b:.5} (exact {:.5} {:.5}, {} points); pressure error of ρ(ΩR1)²: full cells {:.3e} ({cnt}), cut cells {:.3e} mean {:+.3e} ({n_cut}), fraction < 0.5 {:.3e} ({n_small}), ≥ 0.5 {:.3e} ({n_large}), inner wall {:.3e} ({n_in}), outer wall {:.3e} ({n_out}); merged {}; residual {last_res:.1e}",
|
||||||
if rigid { "rigid" } else if outer_drives() { "outer-driven" } else { "couette" },
|
if rigid {
|
||||||
|
"rigid"
|
||||||
|
} else if outer_drives() {
|
||||||
|
"outer-driven"
|
||||||
|
} else {
|
||||||
|
"couette"
|
||||||
|
},
|
||||||
ex.a,
|
ex.a,
|
||||||
ex.b,
|
ex.b,
|
||||||
pts.len(),
|
pts.len(),
|
||||||
@@ -374,20 +391,29 @@ fn probe(n: usize, rigid: bool) {
|
|||||||
let g = Grid::cubic(nx, ny, nz, h);
|
let g = Grid::cubic(nx, ny, nz, h);
|
||||||
let mut field = Field::new(g);
|
let mut field = Field::new(g);
|
||||||
solver.initialize(&mut field);
|
solver.initialize(&mut field);
|
||||||
let tables = solver.mask().expect("mask").face_shift_tables().expect("shift tables").clone();
|
let tables = solver
|
||||||
|
.mask()
|
||||||
|
.expect("mask")
|
||||||
|
.face_shift_tables()
|
||||||
|
.expect("shift tables")
|
||||||
|
.clone();
|
||||||
for k in 0..nz {
|
for k in 0..nz {
|
||||||
for j in 0..ny {
|
for j in 0..ny {
|
||||||
for i in 0..=nx {
|
for i in 0..=nx {
|
||||||
let f = g.uface(k, j, i);
|
let f = g.uface(k, j, i);
|
||||||
let t = &tables[0][3 * f..3 * f + 3];
|
let t = &tables[0][3 * f..3 * f + 3];
|
||||||
field.u[f] = ex.velocity(i as f64 * h + t[0], (j as f64 + 0.5) * h + t[1], rigid).0;
|
field.u[f] = ex
|
||||||
|
.velocity(i as f64 * h + t[0], (j as f64 + 0.5) * h + t[1], rigid)
|
||||||
|
.0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for j in 0..=ny {
|
for j in 0..=ny {
|
||||||
for i in 0..nx {
|
for i in 0..nx {
|
||||||
let f = g.vface(k, j, i);
|
let f = g.vface(k, j, i);
|
||||||
let t = &tables[1][3 * f..3 * f + 3];
|
let t = &tables[1][3 * f..3 * f + 3];
|
||||||
field.v[f] = ex.velocity((i as f64 + 0.5) * h + t[0], j as f64 * h + t[1], rigid).1;
|
field.v[f] = ex
|
||||||
|
.velocity((i as f64 + 0.5) * h + t[0], j as f64 * h + t[1], rigid)
|
||||||
|
.1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,25 +425,119 @@ fn probe(n: usize, rigid: bool) {
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
let (body, mask) = (solver.body().expect("body"), solver.mask().expect("mask"));
|
let (body, mask) = (solver.body().expect("body"), solver.mask().expect("mask"));
|
||||||
mask.impose(body, &mut field.u, &mut field.v, &mut field.w, solver.time());
|
mask.impose(
|
||||||
|
body,
|
||||||
|
&mut field.u,
|
||||||
|
&mut field.v,
|
||||||
|
&mut field.w,
|
||||||
|
solver.time(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
let dt = 0.5 * h * h / (6.0 * MU);
|
let dt = 0.5 * h * h / (6.0 * MU);
|
||||||
|
// R5 design pass: the predictor's terms per face (viscous group =
|
||||||
|
// diffusion + wall shear + solid exchange, zero on the exact field;
|
||||||
|
// inertial group = convection + pressure + source, zero on the exact
|
||||||
|
// steady field): which group carries the cut layer's residual.
|
||||||
|
solver.enable_term_probe();
|
||||||
solver.advance(&mut field, dt);
|
solver.advance(&mut field, dt);
|
||||||
|
let terms = solver.term_probe().expect("term probe");
|
||||||
let mask = solver.mask().expect("mask");
|
let mask = solver.mask().expect("mask");
|
||||||
let pp = &field.p_prime;
|
let pp = &field.p_prime;
|
||||||
let a_max = OMEGA * OMEGA * R2;
|
let a_max = OMEGA * OMEGA * R2;
|
||||||
let is_cut = |c: usize| mask.vol(c) < 1.0 - 1e-9;
|
let is_cut = |c: usize| mask.vol(c) < 1.0 - 1e-9;
|
||||||
// [wall][band]: bands α<¼, ¼–½, ½–¾, ¾–1, full next to cut.
|
// [wall][band]: bands α<¼, ¼–½, ½–¾, ¾–1, full next to cut, interior;
|
||||||
let mut acc: [[Vec<f64>; 5]; 2] = Default::default();
|
// per face [total, viscous group, inertial group] / a_max.
|
||||||
|
let mut acc: [[Vec<[f64; 9]>; 6]; 2] = Default::default();
|
||||||
|
// R5 design pass: the EXACT viscous line integrals over the momentum
|
||||||
|
// control volume of a face — the open parts of its four sides (the
|
||||||
|
// side-diffusion group: diffusion + solid exchange) and the wall arc
|
||||||
|
// inside it (the wall-shear group) — from the analytic gradient of the
|
||||||
|
// Taylor–Couette field, as accelerations on the same `V_eff` the
|
||||||
|
// predictor uses. Their sum is the quadrature error (∇²u = 0).
|
||||||
|
let grad = |c: usize, x: f64, y: f64| -> [f64; 2] {
|
||||||
|
let (xp, yp) = (x - CENTRE.0, y - CENTRE.1);
|
||||||
|
let r = (xp * xp + yp * yp).sqrt().max(1e-12);
|
||||||
|
let (f, fp) = if rigid {
|
||||||
|
(OMEGA, 0.0)
|
||||||
|
} else {
|
||||||
|
(ex.a + ex.b / (r * r), -2.0 * ex.b / (r * r * r))
|
||||||
|
};
|
||||||
|
if c == 0 {
|
||||||
|
[-fp * (xp / r) * yp, -f - fp * (yp / r) * yp]
|
||||||
|
} else {
|
||||||
|
[f + fp * (xp / r) * xp, fp * (yp / r) * xp]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let in_gap = |x: f64, y: f64| {
|
||||||
|
let r = r_of(x, y);
|
||||||
|
(R1..=R2).contains(&r)
|
||||||
|
};
|
||||||
|
let side = |c: usize, x0: f64, y0: f64, x1: f64, y1: f64, n: [f64; 2]| -> f64 {
|
||||||
|
let m = 4096;
|
||||||
|
let len = ((x1 - x0).powi(2) + (y1 - y0).powi(2)).sqrt();
|
||||||
|
let mut s = 0.0;
|
||||||
|
for q in 0..m {
|
||||||
|
let t = (q as f64 + 0.5) / m as f64;
|
||||||
|
let (x, y) = (x0 + t * (x1 - x0), y0 + t * (y1 - y0));
|
||||||
|
if in_gap(x, y) {
|
||||||
|
let g = grad(c, x, y);
|
||||||
|
s += (g[0] * n[0] + g[1] * n[1]) * len / m as f64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MU * s
|
||||||
|
};
|
||||||
|
let arc = |c: usize, xa: f64, xb: f64, ya: f64, yb: f64| -> f64 {
|
||||||
|
let mut s = 0.0;
|
||||||
|
let m = 1 << 20;
|
||||||
|
for (r, sign) in [(R1, -1.0), (R2, 1.0)] {
|
||||||
|
for q in 0..m {
|
||||||
|
let th = std::f64::consts::TAU * (q as f64 + 0.5) / m as f64;
|
||||||
|
let (x, y) = (CENTRE.0 + r * th.cos(), CENTRE.1 + r * th.sin());
|
||||||
|
if x >= xa && x < xb && y >= ya && y < yb {
|
||||||
|
let g = grad(c, x, y);
|
||||||
|
let n = [sign * th.cos(), sign * th.sin()];
|
||||||
|
s += (g[0] * n[0] + g[1] * n[1]) * r * std::f64::consts::TAU / m as f64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MU * s
|
||||||
|
};
|
||||||
|
// (side-diffusion, wall-shear) exact accelerations for the face of
|
||||||
|
// component `c` whose CV is the box [xa, xb] × [ya, yb], aperture `a`.
|
||||||
|
let exact_terms = |c: usize, xa: f64, xb: f64, ya: f64, yb: f64, a: f64| -> (f64, f64) {
|
||||||
|
let d = side(c, xa, ya, xa, yb, [-1.0, 0.0])
|
||||||
|
+ side(c, xb, ya, xb, yb, [1.0, 0.0])
|
||||||
|
+ side(c, xa, ya, xb, ya, [0.0, -1.0])
|
||||||
|
+ side(c, xa, yb, xb, yb, [0.0, 1.0]);
|
||||||
|
let w = arc(c, xa, xb, ya, yb);
|
||||||
|
let v_eff = a.max(0.1) * h * h * h;
|
||||||
|
(d * h / (RHO * v_eff), w * h / (RHO * v_eff))
|
||||||
|
};
|
||||||
let bin_of = |a: f64, near: bool| -> Option<usize> {
|
let bin_of = |a: f64, near: bool| -> Option<usize> {
|
||||||
if a < 1.0 {
|
if a < 1.0 {
|
||||||
Some(((a * 4.0).floor() as usize).min(3))
|
Some(((a * 4.0).floor() as usize).min(3))
|
||||||
} else if near {
|
} else if near {
|
||||||
Some(4)
|
Some(4)
|
||||||
} else {
|
} else {
|
||||||
None
|
Some(5)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// [total, viscous group, inertial group, diffusion, wall shear, solid exchange] / a_max.
|
||||||
|
// …, then the side-diffusion error (diff + exch − exact), the wall-shear
|
||||||
|
// error (shear − exact) and the exact sum (the quadrature's own error).
|
||||||
|
let groups = |t: &[f64; 7], total: f64, exact: (f64, f64)| -> [f64; 9] {
|
||||||
|
[
|
||||||
|
total / a_max,
|
||||||
|
(t[1] + t[2] + t[5]) / a_max,
|
||||||
|
(t[0] + t[3] + t[4]) / a_max,
|
||||||
|
t[1] / a_max,
|
||||||
|
t[2] / a_max,
|
||||||
|
t[5] / a_max,
|
||||||
|
(t[1] + t[5] - exact.0) / a_max,
|
||||||
|
(t[2] - exact.1) / a_max,
|
||||||
|
(exact.0 + exact.1) / a_max,
|
||||||
|
]
|
||||||
|
};
|
||||||
let wall_of = |x: f64, y: f64| usize::from(r_of(x, y) >= 0.5 * (R1 + R2));
|
let wall_of = |x: f64, y: f64| usize::from(r_of(x, y) >= 0.5 * (R1 + R2));
|
||||||
for j in 0..ny {
|
for j in 0..ny {
|
||||||
for i in 1..nx {
|
for i in 1..nx {
|
||||||
@@ -426,9 +546,24 @@ fn probe(n: usize, rigid: bool) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (cm, cp) = (g.cell(0, j, i - 1), g.cell(0, j, i));
|
let (cm, cp) = (g.cell(0, j, i - 1), g.cell(0, j, i));
|
||||||
let Some(b) = bin_of(mask.a_u(f), is_cut(cm) || is_cut(cp)) else { continue };
|
let Some(b) = bin_of(mask.a_u(f), is_cut(cm) || is_cut(cp)) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let star = field.u[f] + (dt / RHO) * mask.grad_weight(0, f) * (pp[cp] - pp[cm]) / h;
|
let star = field.u[f] + (dt / RHO) * mask.grad_weight(0, f) * (pp[cp] - pp[cm]) / h;
|
||||||
acc[wall_of(i as f64 * h, (j as f64 + 0.5) * h)][b].push((star - field.u_old[f]) / dt / a_max);
|
let t = terms[0].get(f).copied().unwrap_or([0.0; 7]);
|
||||||
|
let exact = exact_terms(
|
||||||
|
0,
|
||||||
|
(i as f64 - 0.5) * h,
|
||||||
|
(i as f64 + 0.5) * h,
|
||||||
|
j as f64 * h,
|
||||||
|
(j as f64 + 1.0) * h,
|
||||||
|
mask.a_u(f),
|
||||||
|
);
|
||||||
|
acc[wall_of(i as f64 * h, (j as f64 + 0.5) * h)][b].push(groups(
|
||||||
|
&t,
|
||||||
|
(star - field.u_old[f]) / dt,
|
||||||
|
exact,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for j in 1..ny {
|
for j in 1..ny {
|
||||||
@@ -438,9 +573,24 @@ fn probe(n: usize, rigid: bool) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let (cm, cp) = (g.cell(0, j - 1, i), g.cell(0, j, i));
|
let (cm, cp) = (g.cell(0, j - 1, i), g.cell(0, j, i));
|
||||||
let Some(b) = bin_of(mask.a_v(f), is_cut(cm) || is_cut(cp)) else { continue };
|
let Some(b) = bin_of(mask.a_v(f), is_cut(cm) || is_cut(cp)) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
let star = field.v[f] + (dt / RHO) * mask.grad_weight(1, f) * (pp[cp] - pp[cm]) / h;
|
let star = field.v[f] + (dt / RHO) * mask.grad_weight(1, f) * (pp[cp] - pp[cm]) / h;
|
||||||
acc[wall_of((i as f64 + 0.5) * h, j as f64 * h)][b].push((star - field.v_old[f]) / dt / a_max);
|
let t = terms[1].get(f).copied().unwrap_or([0.0; 7]);
|
||||||
|
let exact = exact_terms(
|
||||||
|
1,
|
||||||
|
i as f64 * h,
|
||||||
|
(i as f64 + 1.0) * h,
|
||||||
|
(j as f64 - 0.5) * h,
|
||||||
|
(j as f64 + 0.5) * h,
|
||||||
|
mask.a_v(f),
|
||||||
|
);
|
||||||
|
acc[wall_of((i as f64 + 0.5) * h, j as f64 * h)][b].push(groups(
|
||||||
|
&t,
|
||||||
|
(star - field.v_old[f]) / dt,
|
||||||
|
exact,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let scale = RHO * (OMEGA * R1).powi(2);
|
let scale = RHO * (OMEGA * R1).powi(2);
|
||||||
@@ -464,23 +614,55 @@ fn probe(n: usize, rigid: bool) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let e = (pp[c] - lvl) / scale;
|
let e = (pp[c] - lvl) / scale;
|
||||||
let w = if is_cut(c) { wall_of((i as f64 + 0.5) * h, (j as f64 + 0.5) * h) } else { 2 };
|
let w = if is_cut(c) {
|
||||||
|
wall_of((i as f64 + 0.5) * h, (j as f64 + 0.5) * h)
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
};
|
||||||
psq[w] += e * e;
|
psq[w] += e * e;
|
||||||
pn[w] += 1;
|
pn[w] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let rms = |v: &[f64]| (v.iter().map(|x| x * x).sum::<f64>() / v.len().max(1) as f64).sqrt();
|
let rms = |v: &[[f64; 9]], k: usize| {
|
||||||
let names = ["α<¼", "¼–½", "½–¾", "¾–1", "full next to cut"];
|
(v.iter().map(|x| x[k] * x[k]).sum::<f64>() / v.len().max(1) as f64).sqrt()
|
||||||
let mode = if rigid { "rigid" } else if outer_drives() { "outer-driven" } else { "couette" };
|
};
|
||||||
|
let names = ["α<¼", "¼–½", "½–¾", "¾–1", "full next to cut", "interior"];
|
||||||
|
let mode = if rigid {
|
||||||
|
"rigid"
|
||||||
|
} else if outer_drives() {
|
||||||
|
"outer-driven"
|
||||||
|
} else {
|
||||||
|
"couette"
|
||||||
|
};
|
||||||
for (w, wname) in ["inner wall", "outer wall"].iter().enumerate() {
|
for (w, wname) in ["inner wall", "outer wall"].iter().enumerate() {
|
||||||
let mut line = format!(" probe {mode} n {n} {wname}:");
|
let mut line = format!(" probe {mode} n {n} {wname}:");
|
||||||
for (b, name) in names.iter().enumerate() {
|
for (b, name) in names.iter().enumerate() {
|
||||||
line += &format!(" {name} {} rms {:.3e};", acc[w][b].len(), rms(&acc[w][b]));
|
line += &format!(
|
||||||
|
" {name} {} rms {:.3e} (visc {:.3e} inert {:.3e}; diff {:.3e} shear {:.3e} exch {:.3e}; ERR sides {:.3e} wall {:.3e} quad {:.1e});",
|
||||||
|
acc[w][b].len(),
|
||||||
|
rms(&acc[w][b], 0),
|
||||||
|
rms(&acc[w][b], 1),
|
||||||
|
rms(&acc[w][b], 2),
|
||||||
|
rms(&acc[w][b], 3),
|
||||||
|
rms(&acc[w][b], 4),
|
||||||
|
rms(&acc[w][b], 5),
|
||||||
|
rms(&acc[w][b], 6),
|
||||||
|
rms(&acc[w][b], 7),
|
||||||
|
rms(&acc[w][b], 8)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
line += &format!(" p' at its cut cells {:.3e} ({})", (psq[w] / pn[w].max(1) as f64).sqrt(), pn[w]);
|
line += &format!(
|
||||||
|
" p' at its cut cells {:.3e} ({})",
|
||||||
|
(psq[w] / pn[w].max(1) as f64).sqrt(),
|
||||||
|
pn[w]
|
||||||
|
);
|
||||||
println!("{line}");
|
println!("{line}");
|
||||||
}
|
}
|
||||||
println!(" probe {mode} n {n} interior p' {:.3e} ({})", (psq[2] / pn[2].max(1) as f64).sqrt(), pn[2]);
|
println!(
|
||||||
|
" probe {mode} n {n} interior p' {:.3e} ({})",
|
||||||
|
(psq[2] / pn[2].max(1) as f64).sqrt(),
|
||||||
|
pn[2]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -240,6 +240,15 @@ pub struct OversetFluid {
|
|||||||
/// The last fluid step's mass defects (patch acceptor ring, background
|
/// The last fluid step's mass defects (patch acceptor ring, background
|
||||||
/// fringe) and the patch's max divergence.
|
/// fringe) and the patch's max divergence.
|
||||||
pub last_defects: Cell<(f64, f64, f64)>,
|
pub last_defects: Cell<(f64, f64, f64)>,
|
||||||
|
/// R4-i `RTX_FSI2O_REGEN_ONCE`: one patch regeneration per coupled
|
||||||
|
/// step — the first pass's meshes (one per substep) are reused by every
|
||||||
|
/// later subiteration of the same step (the wall polygon and velocity
|
||||||
|
/// still follow the candidate). Active between `begin_coupled_step`
|
||||||
|
/// calls only; `restore` rewinds the cursor to the step's first substep.
|
||||||
|
pub regen_once: bool,
|
||||||
|
pub regen_hold: bool,
|
||||||
|
pub regen_cache: Vec<rtx_cfd::mesh::PatchMesh>,
|
||||||
|
pub regen_cursor: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OversetFluid {
|
impl OversetFluid {
|
||||||
@@ -503,6 +512,10 @@ impl OversetFluid {
|
|||||||
correctors_total: Cell::new(0),
|
correctors_total: Cell::new(0),
|
||||||
last_d: zero_d,
|
last_d: zero_d,
|
||||||
last_defects: Cell::new((0.0, 0.0, 0.0)),
|
last_defects: Cell::new((0.0, 0.0, 0.0)),
|
||||||
|
regen_once: std::env::var("RTX_FSI2O_REGEN_ONCE").is_ok(),
|
||||||
|
regen_hold: false,
|
||||||
|
regen_cache: Vec::new(),
|
||||||
|
regen_cursor: 0,
|
||||||
warm_base: None,
|
warm_base: None,
|
||||||
warm_sweeps,
|
warm_sweeps,
|
||||||
})
|
})
|
||||||
@@ -918,6 +931,12 @@ impl OversetFluid {
|
|||||||
self.last_d = d.to_vec();
|
self.last_d = d.to_vec();
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
if self.regen_once && self.regen_hold && self.regen_cursor < self.regen_cache.len() {
|
||||||
|
let mesh = self.regen_cache[self.regen_cursor].clone();
|
||||||
|
self.regen_cursor += 1;
|
||||||
|
self.last_d = d.to_vec();
|
||||||
|
return self.solver.set_patch_mesh(mesh);
|
||||||
|
}
|
||||||
let mesh = match self.patch_for(d) {
|
let mesh = match self.patch_for(d) {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -925,10 +944,22 @@ impl OversetFluid {
|
|||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
if self.regen_once && self.regen_hold {
|
||||||
|
self.regen_cache.push(mesh.clone());
|
||||||
|
self.regen_cursor += 1;
|
||||||
|
}
|
||||||
self.last_d = d.to_vec();
|
self.last_d = d.to_vec();
|
||||||
self.solver.set_patch_mesh(mesh)
|
self.solver.set_patch_mesh(mesh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// R4-i: a new coupled step begins — the first pass regenerates, the
|
||||||
|
/// later passes reuse (`RTX_FSI2O_REGEN_ONCE`; a no-op otherwise).
|
||||||
|
pub fn begin_coupled_step(&mut self) {
|
||||||
|
self.regen_hold = self.regen_once;
|
||||||
|
self.regen_cache.clear();
|
||||||
|
self.regen_cursor = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// The last geometry, for the offline reproduction
|
/// The last geometry, for the offline reproduction
|
||||||
/// (`patch_cylinder_flag_deformed.rs`, `RTX_CF_EDGES_FILE`): one edge
|
/// (`patch_cylinder_flag_deformed.rs`, `RTX_CF_EDGES_FILE`): one edge
|
||||||
/// per block, `x y` per line, when `RTX_FSI2O_DUMP_DIR` is set.
|
/// per block, `x y` per line, when `RTX_FSI2O_DUMP_DIR` is set.
|
||||||
@@ -1198,6 +1229,7 @@ impl OversetFluid {
|
|||||||
let t0 = std::time::Instant::now();
|
let t0 = std::time::Instant::now();
|
||||||
self.solver.restore(&saved.0);
|
self.solver.restore(&saved.0);
|
||||||
self.field = saved.1.clone();
|
self.field = saved.1.clone();
|
||||||
|
self.regen_cursor = 0;
|
||||||
self.t_restore
|
self.t_restore
|
||||||
.set(self.t_restore.get() + t0.elapsed().as_secs_f64());
|
.set(self.t_restore.get() + t0.elapsed().as_secs_f64());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,6 +137,11 @@ pub fn run_march_overset(case: BenchmarkCase, config: &OversetMarchConfig) -> Ov
|
|||||||
std::env::var("RTX_FSI2O_FICT_MASS").unwrap_or_else(|_| "0".into()),
|
std::env::var("RTX_FSI2O_FICT_MASS").unwrap_or_else(|_| "0".into()),
|
||||||
std::env::var("RTX_FSI2O_ROBIN_ALPHA").unwrap_or_else(|_| "0".into()),
|
std::env::var("RTX_FSI2O_ROBIN_ALPHA").unwrap_or_else(|_| "0".into()),
|
||||||
);
|
);
|
||||||
|
if std::env::var("RTX_FSI2O_REGEN_ONCE").is_ok() {
|
||||||
|
println!(
|
||||||
|
" patch regeneration: ONCE per coupled step (R4-i, RTX_FSI2O_REGEN_ONCE): the first pass's mesh is reused by the later subiterations"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Phase 1: rigid flag to t_release (`RTX_FSI2O_LOAD=dir` replaces the
|
// Phase 1: rigid flag to t_release (`RTX_FSI2O_LOAD=dir` replaces the
|
||||||
// march with the saved state; `RTX_FSI2O_SAVE=dir` saves it).
|
// march with the saved state; `RTX_FSI2O_SAVE=dir` saves it).
|
||||||
@@ -399,7 +404,11 @@ pub fn run_march_overset(case: BenchmarkCase, config: &OversetMarchConfig) -> Ov
|
|||||||
// cell imbalance.
|
// cell imbalance.
|
||||||
let mut step_csv = std::env::var("RTX_FSI2O_STEP_CSV").ok().map(|p| {
|
let mut step_csv = std::env::var("RTX_FSI2O_STEP_CSV").ok().map(|p| {
|
||||||
let mut f = std::fs::File::create(p).expect("step csv");
|
let mut f = std::fs::File::create(p).expect("step csv");
|
||||||
writeln!(f, "t,ux,uy,drag,lift,defect_patch,defect_bg,patch_div").unwrap();
|
// R4-i columns: the step's subiterations, the last pass's signed
|
||||||
|
// y-residual Σ_k (d_new − d_candidate)_y and its norm, and the
|
||||||
|
// nodal load the structure was given (Σ F_x, Σ F_y) — against the
|
||||||
|
// wall-integral force of the same state (drag, lift).
|
||||||
|
writeln!(f, "t,ux,uy,drag,lift,defect_patch,defect_bg,patch_div,subit,dres_y,dres_norm,fx_nodal,fy_nodal").unwrap();
|
||||||
f
|
f
|
||||||
});
|
});
|
||||||
let (t_fluid, t_structure) = (std::cell::Cell::new(0.0_f64), std::cell::Cell::new(0.0_f64));
|
let (t_fluid, t_structure) = (std::cell::Cell::new(0.0_f64), std::cell::Cell::new(0.0_f64));
|
||||||
@@ -433,6 +442,7 @@ pub fn run_march_overset(case: BenchmarkCase, config: &OversetMarchConfig) -> Ov
|
|||||||
if robin_alpha > 0.0 {
|
if robin_alpha > 0.0 {
|
||||||
robin_datum.replace(fluid.borrow().inner_tractions());
|
robin_datum.replace(fluid.borrow().inner_tractions());
|
||||||
}
|
}
|
||||||
|
fluid.borrow_mut().begin_coupled_step();
|
||||||
let saved = fluid.borrow().snapshot();
|
let saved = fluid.borrow().snapshot();
|
||||||
type PassResult = (DynamicState, Vec<(NodeId, Vector3<f64>)>, f64, usize);
|
type PassResult = (DynamicState, Vec<(NodeId, Vector3<f64>)>, f64, usize);
|
||||||
let latest: RefCell<Option<PassResult>> = RefCell::new(None);
|
let latest: RefCell<Option<PassResult>> = RefCell::new(None);
|
||||||
@@ -498,6 +508,7 @@ pub fn run_march_overset(case: BenchmarkCase, config: &OversetMarchConfig) -> Ov
|
|||||||
let tol_step = cfg.tol_floor.max(cfg.rtol * increment);
|
let tol_step = cfg.tol_floor.max(cfg.rtol * increment);
|
||||||
let retry_at = (5.0 * tol_step).max(0.1 * increment);
|
let retry_at = (5.0 * tol_step).max(0.1 * increment);
|
||||||
let acceptable = (cfg.stall_accept * tol_step).max(0.1 * increment);
|
let acceptable = (cfg.stall_accept * tol_step).max(0.1 * increment);
|
||||||
|
let mut step_iterations = 0usize;
|
||||||
let mut outcome = if let Some(iqn) = iqn.as_mut() {
|
let mut outcome = if let Some(iqn) = iqn.as_mut() {
|
||||||
iqn.set_tolerance(tol_step).unwrap();
|
iqn.set_tolerance(tol_step).unwrap();
|
||||||
iqn.solve(&d_predicted, pass)
|
iqn.solve(&d_predicted, pass)
|
||||||
@@ -533,6 +544,7 @@ pub fn run_march_overset(case: BenchmarkCase, config: &OversetMarchConfig) -> Ov
|
|||||||
Ok(c) => {
|
Ok(c) => {
|
||||||
total_subiterations += c.iterations;
|
total_subiterations += c.iterations;
|
||||||
max_subiterations = max_subiterations.max(c.iterations);
|
max_subiterations = max_subiterations.max(c.iterations);
|
||||||
|
step_iterations = c.iterations;
|
||||||
}
|
}
|
||||||
Err(
|
Err(
|
||||||
rtx_fsi::FsiError::CouplingNotConverged {
|
rtx_fsi::FsiError::CouplingNotConverged {
|
||||||
@@ -549,6 +561,7 @@ pub fn run_march_overset(case: BenchmarkCase, config: &OversetMarchConfig) -> Ov
|
|||||||
worst_stall = worst_stall.max(residual);
|
worst_stall = worst_stall.max(residual);
|
||||||
total_subiterations += iterations;
|
total_subiterations += iterations;
|
||||||
max_subiterations = max_subiterations.max(iterations);
|
max_subiterations = max_subiterations.max(iterations);
|
||||||
|
step_iterations = iterations;
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!(
|
println!(
|
||||||
@@ -560,6 +573,25 @@ pub fn run_march_overset(case: BenchmarkCase, config: &OversetMarchConfig) -> Ov
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let (new_state, nodal, conservation, faces) = latest.borrow_mut().take().expect("pass ran");
|
let (new_state, nodal, conservation, faces) = latest.borrow_mut().take().expect("pass ran");
|
||||||
|
// R4-i: the last pass's residual (the structure's answer against the
|
||||||
|
// candidate the fluid's committed state sits at) and the nodal load.
|
||||||
|
let (dres_y, dres_norm, fx_nodal, fy_nodal) = {
|
||||||
|
let d_new = extract(&new_state);
|
||||||
|
let d_cand = &fluid.borrow().last_d;
|
||||||
|
let mut sy = 0.0_f64;
|
||||||
|
let mut n2 = 0.0_f64;
|
||||||
|
for k in 0..d_new.len() / 2 {
|
||||||
|
sy += d_new[2 * k + 1] - d_cand[2 * k + 1];
|
||||||
|
n2 += (d_new[2 * k] - d_cand[2 * k]).powi(2)
|
||||||
|
+ (d_new[2 * k + 1] - d_cand[2 * k + 1]).powi(2);
|
||||||
|
}
|
||||||
|
let (mut fx, mut fy) = (0.0_f64, 0.0_f64);
|
||||||
|
for (_, f) in &nodal {
|
||||||
|
fx += f.x;
|
||||||
|
fy += f.y;
|
||||||
|
}
|
||||||
|
(sy, n2.sqrt(), fx, fy)
|
||||||
|
};
|
||||||
flag_state = new_state;
|
flag_state = new_state;
|
||||||
committed_nodal = nodal;
|
committed_nodal = nodal;
|
||||||
prev_area.set(fluid.borrow().shared.read().unwrap().area());
|
prev_area.set(fluid.borrow().shared.read().unwrap().area());
|
||||||
@@ -590,7 +622,7 @@ pub fn run_march_overset(case: BenchmarkCase, config: &OversetMarchConfig) -> Ov
|
|||||||
let (dp, db, pd) = fluid.borrow().last_defects.get();
|
let (dp, db, pd) = fluid.borrow().last_defects.get();
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
"{t_now:.6},{ux:.6e},{uy:.6e},{drag_now:.6e},{lift_now:.6e},{dp:.3e},{db:.3e},{pd:.3e}"
|
"{t_now:.6},{ux:.6e},{uy:.6e},{drag_now:.6e},{lift_now:.6e},{dp:.3e},{db:.3e},{pd:.3e},{step_iterations},{dres_y:.3e},{dres_norm:.3e},{fx_nodal:.6e},{fy_nodal:.6e}"
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user