embedded3 item 10: the apertured cut-cell wall (AM-wall) — cutwall.rs classification, apertured projection with the compatible wall flux, cut predictor (V_u = αhA, averaged mass fluxes, implicit wall shear, inertia floor), cut load route; sphere MMS CutCell ≤ GhostBinary at n 12/24 (ratio 0.96), loads 9.3/10.7 % at n 24
CI / Build CPU-Only (Explicit) (push) Failing after 4s
Documentation / Build API Documentation (push) Failing after 4s
CI / Format Check (push) Failing after 12s
Documentation / Build User Guide (push) Successful in 5s
CI / Build (ubuntu-latest) (push) Failing after 1m50s
CI / Clippy Check (push) Failing after 2m5s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m40s
CI / Build (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (macos-latest) (push) Canceled after 0s
CI / Python Bindings (maturin) (macos-latest) (push) Canceled after 0s
CI / Distributed Training Tests (push) Canceled after 0s
CI / CI Success (push) Canceled after 0s
CI / Python Bindings (maturin) (ubuntu-latest) (push) Canceled after 0s
CI / WASM Build + Size Check (push) Canceled after 0s
CI / Build CPU-Only (Explicit) (push) Failing after 4s
Documentation / Build API Documentation (push) Failing after 4s
CI / Format Check (push) Failing after 12s
Documentation / Build User Guide (push) Successful in 5s
CI / Build (ubuntu-latest) (push) Failing after 1m50s
CI / Clippy Check (push) Failing after 2m5s
Performance Benchmarks / Run Benchmarks (push) Successful in 2m40s
CI / Build (macos-latest) (push) Canceled after 0s
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (macos-latest) (push) Canceled after 0s
CI / Python Bindings (maturin) (macos-latest) (push) Canceled after 0s
CI / Distributed Training Tests (push) Canceled after 0s
CI / CI Success (push) Canceled after 0s
CI / Python Bindings (maturin) (ubuntu-latest) (push) Canceled after 0s
CI / WASM Build + Size Check (push) Canceled after 0s
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
d337afa8f9
commit
0e4c97ed24
+215
@@ -0,0 +1,215 @@
|
||||
//! The predictor on the apertured cut-cell wall (`cutwall.rs`): one
|
||||
//! routine for the three components, addressed on the face lattice. The
|
||||
//! momentum control volume of an unknown face is `V_u = α h A` closed by
|
||||
//! the wall; its faces carry the mass fluxes averaged from the two adjacent
|
||||
//! cells (the 2D face velocities when every aperture is 1), upwind plus the
|
||||
//! TVD correction as the 2D predictor, apertured diffusion, the pressure
|
||||
//! force `−(p₊ − p₋) α A` (the projection's gradient), the wall's momentum
|
||||
//! flux `m_w U_b` with `m_w = −Σ m_f` (so a uniform field stays uniform),
|
||||
//! and the implicit wall shear `μ A_w (u − U_b)/d_f`; the time derivative
|
||||
//! carries the inertia floor.
|
||||
|
||||
use super::{Side, Solver};
|
||||
use crate::solvers::incompressible::embedded3::cutwall::INERTIA_FLOOR;
|
||||
use crate::solvers::incompressible::embedded3::field::Field;
|
||||
use crate::solvers::incompressible::simple::ConvectionScheme;
|
||||
|
||||
impl Solver {
|
||||
/// The three components' predictors on the unknown faces; the
|
||||
/// prescribed faces keep their imposed values.
|
||||
pub(super) fn cut_predictor(&self, field: &mut Field, dt: f64, t_old: f64) {
|
||||
let mask = self.mask.as_ref().expect("cut mask");
|
||||
let g = field.grid;
|
||||
let (nx, ny, nz) = (g.nx, g.ny, g.nz);
|
||||
let periodic = self.params.boundaries.periodic_z();
|
||||
let lat = mask.lattice();
|
||||
let w_range = if periodic { 0..nz } else { 1..nz };
|
||||
for c in 0..3 {
|
||||
let (ir, jr, kr) = match c {
|
||||
0 => (1..nx, 0..ny, 0..nz),
|
||||
1 => (0..nx, 1..ny, 0..nz),
|
||||
_ => (0..nx, 0..ny, w_range.clone()),
|
||||
};
|
||||
let mut updates = Vec::new();
|
||||
for k in kr {
|
||||
for j in jr.clone() {
|
||||
for i in ir.clone() {
|
||||
let fluid = match c {
|
||||
0 => self.u_is_fluid(k, j, i),
|
||||
1 => self.v_is_fluid(k, j, i),
|
||||
_ => self.w_is_fluid(k, j, i),
|
||||
};
|
||||
if !fluid {
|
||||
continue;
|
||||
}
|
||||
let p = [i as i64, j as i64, k as i64];
|
||||
let idx = lat.face(c, p).expect("face");
|
||||
updates.push((idx, self.cut_face_update(field, c, p, dt, t_old)));
|
||||
}
|
||||
}
|
||||
}
|
||||
let out: &mut Vec<f64> = match c {
|
||||
0 => &mut field.u,
|
||||
1 => &mut field.v,
|
||||
_ => &mut field.w,
|
||||
};
|
||||
for (idx, val) in updates {
|
||||
out[idx] = val;
|
||||
}
|
||||
}
|
||||
if periodic {
|
||||
for j in 0..ny {
|
||||
for i in 0..nx {
|
||||
field.w[g.wface(nz, j, i)] = field.w[g.wface(0, j, i)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The predicted value of the unknown face of component `c` at lattice
|
||||
/// `p` from the old field.
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn cut_face_update(&self, field: &Field, c: usize, p: [i64; 3], dt: f64, t_old: f64) -> f64 {
|
||||
let mask = self.mask.as_ref().expect("cut mask");
|
||||
let body = self.body.as_ref().expect("body");
|
||||
let g = field.grid;
|
||||
let h = [g.dx, g.dy, g.dz];
|
||||
let n = [g.nx as f64, g.ny as f64, g.nz as f64];
|
||||
let area = [g.dy * g.dz, g.dx * g.dz, g.dx * g.dy];
|
||||
let rho = self.fluid.density;
|
||||
let mu = self.fluid.viscosity;
|
||||
let b = self.params.boundaries;
|
||||
let sides = [[b.x0, b.x1], [b.y0, b.y1], [b.z0, b.z1]];
|
||||
let scheme = self.params.convection_scheme;
|
||||
let lat = mask.lattice();
|
||||
let old: [&[f64]; 3] = [&field.u_old, &field.v_old, &field.w_old];
|
||||
let e = |d: usize| {
|
||||
let mut v = [0i64; 3];
|
||||
v[d] = 1;
|
||||
v
|
||||
};
|
||||
let add =
|
||||
|a: [i64; 3], b: [i64; 3], s: i64| [a[0] + s * b[0], a[1] + s * b[1], a[2] + s * b[2]];
|
||||
// The old value of a face of component `cc` at `q`, `None` outside.
|
||||
let val = |cc: usize, q: [i64; 3]| lat.face(cc, q).map(|f| old[cc][f]);
|
||||
let ap = |cc: usize, q: [i64; 3]| mask.aperture(cc, q);
|
||||
let cv = mask.cv_geometry(c, p);
|
||||
let x = lat.face_position(c, p);
|
||||
let u0 = val(c, p).expect("the face");
|
||||
let ec = e(c);
|
||||
let cell_minus = add(p, ec, -1);
|
||||
let cell_plus = p;
|
||||
let ub = mask.surface_velocity_at(body, x, c, t_old);
|
||||
|
||||
let mut mass_out = 0.0;
|
||||
let mut conv = 0.0;
|
||||
let mut diff = 0.0;
|
||||
for d in 0..3 {
|
||||
let ed = e(d);
|
||||
let a_d = area[d];
|
||||
// Neighbouring faces of this component along d (None beyond a wall).
|
||||
let up1 = val(c, add(p, ed, 1));
|
||||
let up2 = val(c, add(p, ed, 2));
|
||||
let dn1 = val(c, add(p, ed, -1));
|
||||
let dn2 = val(c, add(p, ed, -2));
|
||||
// The control volume's mass fluxes through its plus / minus faces
|
||||
// along d: the averages of the two adjacent cells' face fluxes
|
||||
// (own direction: the face's and its neighbours' fluxes).
|
||||
let (m_plus, m_minus) = if d == c {
|
||||
let f_up = ap(c, add(p, ec, 1)).unwrap_or(cv.alpha) * up1.unwrap_or(u0);
|
||||
let f_dn = ap(c, add(p, ec, -1)).unwrap_or(cv.alpha) * dn1.unwrap_or(u0);
|
||||
let f0 = cv.alpha * u0;
|
||||
(0.5 * (f0 + f_up) * a_d, 0.5 * (f_dn + f0) * a_d)
|
||||
} else {
|
||||
let flux = |q: [i64; 3]| ap(d, q).unwrap_or(1.0) * val(d, q).unwrap_or(0.0);
|
||||
(
|
||||
0.5 * (flux(add(cell_minus, ed, 1)) + flux(add(cell_plus, ed, 1))) * a_d,
|
||||
0.5 * (flux(cell_minus) + flux(cell_plus)) * a_d,
|
||||
)
|
||||
};
|
||||
mass_out += m_plus - m_minus;
|
||||
// Beyond a domain side along d: the boundary value on a Velocity
|
||||
// side, the face's own value (mirror) otherwise.
|
||||
let beyond = |plus: bool| {
|
||||
let side = sides[d][usize::from(plus)];
|
||||
if side == Side::Velocity {
|
||||
let mut xb = x;
|
||||
xb[d] = if plus { n[d] * h[d] } else { 0.0 };
|
||||
[
|
||||
self.boundary(xb[0], xb[1], xb[2], t_old).0,
|
||||
self.boundary(xb[0], xb[1], xb[2], t_old).1,
|
||||
self.boundary(xb[0], xb[1], xb[2], t_old).2,
|
||||
][c]
|
||||
} else {
|
||||
u0
|
||||
}
|
||||
};
|
||||
// Convection through the plus face.
|
||||
let (u_plus, delta_plus) = match up1 {
|
||||
Some(un) => {
|
||||
let delta = if scheme == ConvectionScheme::Upwind {
|
||||
0.0
|
||||
} else if m_plus >= 0.0 {
|
||||
scheme.face_correction(dn1, u0, un)
|
||||
} else {
|
||||
scheme.face_correction(up2, un, u0)
|
||||
};
|
||||
(Self::upwind(m_plus, u0, un), delta)
|
||||
}
|
||||
None => (Self::upwind(m_plus, u0, beyond(true)), 0.0),
|
||||
};
|
||||
let (u_minus, delta_minus) = match dn1 {
|
||||
Some(ud) => {
|
||||
let delta = if scheme == ConvectionScheme::Upwind {
|
||||
0.0
|
||||
} else if m_minus >= 0.0 {
|
||||
scheme.face_correction(dn2, ud, u0)
|
||||
} else {
|
||||
scheme.face_correction(up1, u0, ud)
|
||||
};
|
||||
(Self::upwind(m_minus, ud, u0), delta)
|
||||
}
|
||||
None => (Self::upwind(m_minus, beyond(false), u0), 0.0),
|
||||
};
|
||||
conv += m_plus * (u_plus + delta_plus) - m_minus * (u_minus + delta_minus);
|
||||
// Diffusion through the plus / minus faces.
|
||||
let (g_minus, g_plus) = (cv.ap[d][0], cv.ap[d][1]);
|
||||
diff += match up1 {
|
||||
Some(un) => mu * g_plus * a_d * (un - u0) / h[d],
|
||||
None => {
|
||||
if sides[d][1] == Side::Velocity {
|
||||
mu * g_plus * a_d * (beyond(true) - u0) / (0.5 * h[d])
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
};
|
||||
diff -= match dn1 {
|
||||
Some(ud) => mu * g_minus * a_d * (u0 - ud) / h[d],
|
||||
None => {
|
||||
if sides[d][0] == Side::Velocity {
|
||||
mu * g_minus * a_d * (u0 - beyond(false)) / (0.5 * h[d])
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
// The wall's momentum flux closes the mass balance exactly.
|
||||
conv -= mass_out * ub;
|
||||
let p_plus = lat.cell(cell_plus).map_or(0.0, |ci| field.p[ci]);
|
||||
let p_minus = lat.cell(cell_minus).map_or(0.0, |ci| field.p[ci]);
|
||||
let pressure = -(p_plus - p_minus) * cv.alpha * area[c];
|
||||
let v_u = cv.alpha * h[c] * area[c];
|
||||
let source = self.momentum_source.as_ref().map_or(0.0, |f| {
|
||||
let s = f(x[0], x[1], x[2], t_old);
|
||||
[s.0, s.1, s.2][c] * v_u
|
||||
});
|
||||
let a_w =
|
||||
(cv.wall[0] * cv.wall[0] + cv.wall[1] * cv.wall[1] + cv.wall[2] * cv.wall[2]).sqrt();
|
||||
let shear = mu * a_w / cv.distance;
|
||||
let v_eff = cv.alpha.max(INERTIA_FLOOR) * h[c] * area[c];
|
||||
let inertia = rho * v_eff / dt;
|
||||
(inertia * u0 - conv + diff + pressure + source + shear * ub) / (inertia + shear)
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
//! `dz = 1`) every number is the 2D solver's. The fluid predicates are the
|
||||
//! wall's hooks (item 9).
|
||||
|
||||
mod cut_predictor;
|
||||
#[cfg(feature = "cuda")]
|
||||
pub mod device;
|
||||
mod predictor;
|
||||
@@ -110,6 +111,7 @@ pub struct Solver {
|
||||
body: Option<Body>,
|
||||
mask: Option<Mask>,
|
||||
last_ghost_correction: f64,
|
||||
wall_fluxes: Vec<f64>,
|
||||
pcg_cache: PcgCache,
|
||||
time: f64,
|
||||
initialized: bool,
|
||||
@@ -137,6 +139,7 @@ impl Solver {
|
||||
body: None,
|
||||
mask: None,
|
||||
last_ghost_correction: 0.0,
|
||||
wall_fluxes: Vec::new(),
|
||||
pcg_cache: PcgCache::default(),
|
||||
time: 0.0,
|
||||
initialized: false,
|
||||
@@ -174,7 +177,8 @@ impl Solver {
|
||||
self.mask.as_ref()
|
||||
}
|
||||
|
||||
/// The last step's ghost compatibility correction.
|
||||
/// The last step's compatibility correction: the binary wall's shared
|
||||
/// ghost flux correction, or the cut wall's wall-flux correction.
|
||||
#[must_use]
|
||||
pub fn ghost_correction(&self) -> f64 {
|
||||
self.last_ghost_correction
|
||||
@@ -226,6 +230,36 @@ impl Solver {
|
||||
.is_none_or(|m| m.is_fluid_cell(m.grid().cell(k, j, i)))
|
||||
}
|
||||
|
||||
// The apertures (1 without a cut geometry).
|
||||
#[inline]
|
||||
pub(super) fn au(&self, k: usize, j: usize, i: usize) -> f64 {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.map_or(1.0, |m| m.a_u(m.grid().uface(k, j, i)))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn av(&self, k: usize, j: usize, i: usize) -> f64 {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.map_or(1.0, |m| m.a_v(m.grid().vface(k, j, i)))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn aw(&self, k: usize, j: usize, i: usize) -> f64 {
|
||||
self.mask
|
||||
.as_ref()
|
||||
.map_or(1.0, |m| m.a_w(m.grid().wface(k, j, i)))
|
||||
}
|
||||
/// The surface velocity's compatible flux through the cell's wall at
|
||||
/// the step's new time (cut wall only; the table is rebuilt per step).
|
||||
#[inline]
|
||||
pub(super) fn wall_flux(&self, idx: usize) -> f64 {
|
||||
self.wall_fluxes[idx]
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn has_cut(&self) -> bool {
|
||||
self.mask.as_ref().is_some_and(|m| m.cut().is_some())
|
||||
}
|
||||
|
||||
pub(super) fn upwind(face_velocity: f64, upstream: f64, downstream: f64) -> f64 {
|
||||
if face_velocity >= 0.0 {
|
||||
upstream
|
||||
@@ -283,7 +317,10 @@ impl Solver {
|
||||
let (nx, ny, nz) = (g.nx, g.ny, g.nz);
|
||||
let b = self.params.boundaries;
|
||||
let periodic = b.periodic_z();
|
||||
for k in 0..nz {
|
||||
if self.has_cut() {
|
||||
self.cut_predictor(field, dt, t_old);
|
||||
}
|
||||
for k in (0..nz).filter(|_| !self.has_cut()) {
|
||||
for j in 0..ny {
|
||||
for i in 1..nx {
|
||||
if !self.u_is_fluid(k, j, i) {
|
||||
@@ -295,7 +332,7 @@ impl Solver {
|
||||
}
|
||||
}
|
||||
}
|
||||
for k in 0..nz {
|
||||
for k in (0..nz).filter(|_| !self.has_cut()) {
|
||||
for j in 1..ny {
|
||||
for i in 0..nx {
|
||||
if !self.v_is_fluid(k, j, i) {
|
||||
@@ -308,7 +345,7 @@ impl Solver {
|
||||
}
|
||||
}
|
||||
let k_range = if periodic { 0..nz } else { 1..nz };
|
||||
for k in k_range {
|
||||
for k in k_range.filter(|_| !self.has_cut()) {
|
||||
for j in 0..ny {
|
||||
for i in 0..nx {
|
||||
if !self.w_is_fluid(k, j, i) {
|
||||
@@ -365,15 +402,15 @@ impl Solver {
|
||||
let t = self.time;
|
||||
if let Some(body) = &self.body {
|
||||
if self.mask.is_none() {
|
||||
assert_eq!(
|
||||
self.params.wall_scheme,
|
||||
WallScheme::GhostBinary,
|
||||
"item 10 brings CutCell"
|
||||
);
|
||||
self.mask = Some(
|
||||
Mask::build(body, field.grid, t, self.params.boundaries)
|
||||
.expect("embedded mask"),
|
||||
);
|
||||
let mask = match self.params.wall_scheme {
|
||||
WallScheme::GhostBinary => {
|
||||
Mask::build(body, field.grid, t, self.params.boundaries)
|
||||
}
|
||||
WallScheme::CutCell => {
|
||||
Mask::build_cut(body, field.grid, t, self.params.boundaries)
|
||||
}
|
||||
};
|
||||
self.mask = Some(mask.expect("embedded mask"));
|
||||
}
|
||||
}
|
||||
self.apply_boundary_normals(field, t);
|
||||
@@ -398,6 +435,14 @@ impl Solver {
|
||||
self.momentum_predictor(field, dt, t_old);
|
||||
self.apply_boundary_normals(field, t_new);
|
||||
field.copy_to_starred();
|
||||
let mut cut_correction = None;
|
||||
if let (Some(body), Some(mask)) = (&self.body, &self.mask) {
|
||||
if mask.cut().is_some() {
|
||||
let (table, correction) = mask.wall_flux_table(body, t_new);
|
||||
self.wall_fluxes = table;
|
||||
cut_correction = Some(correction);
|
||||
}
|
||||
}
|
||||
let mut total = 0;
|
||||
let mut final_residual = f64::INFINITY;
|
||||
let mut poisson_iterations = 0;
|
||||
@@ -414,8 +459,8 @@ impl Solver {
|
||||
}
|
||||
// Ghost faces follow the corrected field (the next step's stencil data).
|
||||
if let (Some(body), Some(mask)) = (&self.body, &self.mask) {
|
||||
self.last_ghost_correction =
|
||||
mask.impose(body, &mut field.u, &mut field.v, &mut field.w, t_new);
|
||||
let imposed = mask.impose(body, &mut field.u, &mut field.v, &mut field.w, t_new);
|
||||
self.last_ghost_correction = cut_correction.unwrap_or(imposed);
|
||||
}
|
||||
self.time = t_new;
|
||||
StepResult {
|
||||
|
||||
+30
-16
@@ -39,42 +39,42 @@ impl Solver {
|
||||
extra += ae_outlet;
|
||||
}
|
||||
} else if self.u_is_fluid(k, j, i + 1) {
|
||||
problem.ae[idx] = ae_interior;
|
||||
problem.ae[idx] = ae_interior * self.au(k, j, i + 1);
|
||||
}
|
||||
if i == 0 {
|
||||
if b.x0 == outlet {
|
||||
extra += ae_outlet;
|
||||
}
|
||||
} else if self.u_is_fluid(k, j, i) {
|
||||
problem.aw[idx] = ae_interior;
|
||||
problem.aw[idx] = ae_interior * self.au(k, j, i);
|
||||
}
|
||||
if j + 1 == ny {
|
||||
if b.y1 == outlet {
|
||||
extra += an_outlet;
|
||||
}
|
||||
} else if self.v_is_fluid(k, j + 1, i) {
|
||||
problem.an[idx] = an_interior;
|
||||
problem.an[idx] = an_interior * self.av(k, j + 1, i);
|
||||
}
|
||||
if j == 0 {
|
||||
if b.y0 == outlet {
|
||||
extra += an_outlet;
|
||||
}
|
||||
} else if self.v_is_fluid(k, j, i) {
|
||||
problem.as_[idx] = an_interior;
|
||||
problem.as_[idx] = an_interior * self.av(k, j, i);
|
||||
}
|
||||
if k + 1 == nz && !periodic {
|
||||
if b.z1 == outlet {
|
||||
extra += at_outlet;
|
||||
}
|
||||
} else if self.w_is_fluid((k + 1) % nz, j, i) {
|
||||
problem.at[idx] = at_interior;
|
||||
problem.at[idx] = at_interior * self.aw((k + 1) % nz, j, i);
|
||||
}
|
||||
if k == 0 && !periodic {
|
||||
if b.z0 == outlet {
|
||||
extra += at_outlet;
|
||||
}
|
||||
} else if self.w_is_fluid(k, j, i) {
|
||||
problem.ab[idx] = at_interior;
|
||||
problem.ab[idx] = at_interior * self.aw(k, j, i);
|
||||
}
|
||||
problem.extra_diag[idx] = extra;
|
||||
}
|
||||
@@ -246,6 +246,7 @@ impl Solver {
|
||||
let g = field.grid;
|
||||
let (nx, ny, nz, dx, dy, dz) = (g.nx, g.ny, g.nz, g.dx, g.dy, g.dz);
|
||||
let rho = self.fluid.density;
|
||||
let cut = self.has_cut();
|
||||
let mut source_scale = 0.0;
|
||||
for k in 0..nz {
|
||||
for j in 0..ny {
|
||||
@@ -255,15 +256,19 @@ impl Solver {
|
||||
field.sp[idx] = 0.0;
|
||||
continue;
|
||||
}
|
||||
let divergence_flux = rho
|
||||
* ((field.u_star[g.uface(k, j, i + 1)] - field.u_star[g.uface(k, j, i)])
|
||||
let mut divergence_flux = rho
|
||||
* ((self.au(k, j, i + 1) * field.u_star[g.uface(k, j, i + 1)]
|
||||
- self.au(k, j, i) * field.u_star[g.uface(k, j, i)])
|
||||
* (dy * dz)
|
||||
+ (field.v_star[g.vface(k, j + 1, i)]
|
||||
- field.v_star[g.vface(k, j, i)])
|
||||
+ (self.av(k, j + 1, i) * field.v_star[g.vface(k, j + 1, i)]
|
||||
- self.av(k, j, i) * field.v_star[g.vface(k, j, i)])
|
||||
* (dx * dz)
|
||||
+ (field.w_star[g.wface(k + 1, j, i)]
|
||||
- field.w_star[g.wface(k, j, i)])
|
||||
+ (self.aw(k + 1, j, i) * field.w_star[g.wface(k + 1, j, i)]
|
||||
- self.aw(k, j, i) * field.w_star[g.wface(k, j, i)])
|
||||
* (dx * dy));
|
||||
if cut {
|
||||
divergence_flux += rho * self.wall_flux(idx);
|
||||
}
|
||||
field.sp[idx] = -divergence_flux;
|
||||
source_scale += divergence_flux.abs();
|
||||
}
|
||||
@@ -315,6 +320,7 @@ impl Solver {
|
||||
let g = field.grid;
|
||||
let (nx, ny, nz, dx, dy, dz) = (g.nx, g.ny, g.nz, g.dx, g.dy, g.dz);
|
||||
let rho = self.fluid.density;
|
||||
let cut = self.has_cut();
|
||||
let b = self.params.boundaries;
|
||||
let outlet = Side::PressureOutlet;
|
||||
let periodic = b.periodic_z();
|
||||
@@ -402,12 +408,20 @@ impl Solver {
|
||||
if !self.cell_is_fluid(k, j, i) {
|
||||
continue;
|
||||
}
|
||||
let divergence_flux = rho
|
||||
* ((field.u[g.uface(k, j, i + 1)] - field.u[g.uface(k, j, i)]) * (dy * dz)
|
||||
+ (field.v[g.vface(k, j + 1, i)] - field.v[g.vface(k, j, i)])
|
||||
let idx = g.cell(k, j, i);
|
||||
let mut divergence_flux = rho
|
||||
* ((self.au(k, j, i + 1) * field.u[g.uface(k, j, i + 1)]
|
||||
- self.au(k, j, i) * field.u[g.uface(k, j, i)])
|
||||
* (dy * dz)
|
||||
+ (self.av(k, j + 1, i) * field.v[g.vface(k, j + 1, i)]
|
||||
- self.av(k, j, i) * field.v[g.vface(k, j, i)])
|
||||
* (dx * dz)
|
||||
+ (field.w[g.wface(k + 1, j, i)] - field.w[g.wface(k, j, i)])
|
||||
+ (self.aw(k + 1, j, i) * field.w[g.wface(k + 1, j, i)]
|
||||
- self.aw(k, j, i) * field.w[g.wface(k, j, i)])
|
||||
* (dx * dy));
|
||||
if cut {
|
||||
divergence_flux += rho * self.wall_flux(idx);
|
||||
}
|
||||
mass_imbalance += divergence_flux.abs();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user