rtx-cfd three_d: the predictors split into piso_predictor.rs (file-size rule), the w predictor's index helpers cleaned; tests unchanged and green
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 / Build (macos-latest) (push) Waiting to run
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
Documentation / Build User Guide (push) Successful in 6s
CI / Build (ubuntu-latest) (push) Failing after 3s
CI / Format Check (push) Failing after 4s
CI / Clippy Check (push) Failing after 4s
Performance Benchmarks / Run Benchmarks (push) Failing after 15s
CI / Build CPU-Only (Explicit) (push) Failing after 1m30s
Documentation / Build API Documentation (push) Failing after 1m33s
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 / Build (macos-latest) (push) Waiting to run
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
Documentation / Build User Guide (push) Successful in 6s
CI / Build (ubuntu-latest) (push) Failing after 3s
CI / Format Check (push) Failing after 4s
CI / Clippy Check (push) Failing after 4s
Performance Benchmarks / Run Benchmarks (push) Failing after 15s
CI / Build CPU-Only (Explicit) (push) Failing after 1m30s
Documentation / Build API Documentation (push) Failing after 1m33s
Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
616d2a3394
commit
e0cbb99343
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
pub mod flow_field;
|
pub mod flow_field;
|
||||||
pub mod piso_host;
|
pub mod piso_host;
|
||||||
|
mod piso_predictor;
|
||||||
pub mod poisson;
|
pub mod poisson;
|
||||||
|
|
||||||
pub use flow_field::FlowField3D;
|
pub use flow_field::FlowField3D;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ impl Boundaries3 {
|
|||||||
.contains(&SideBoundary3::PressureOutlet)
|
.contains(&SideBoundary3::PressureOutlet)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn periodic_z(self) -> bool {
|
pub(super) fn periodic_z(self) -> bool {
|
||||||
self.z0 == SideBoundary3::Periodic
|
self.z0 == SideBoundary3::Periodic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ type Vec3Fn = Box<dyn Fn(f64, f64, f64, f64) -> (f64, f64, f64) + Send + Sync>;
|
|||||||
pub struct Piso3Solver {
|
pub struct Piso3Solver {
|
||||||
pub fluid: Fluid3,
|
pub fluid: Fluid3,
|
||||||
pub params: Piso3Parameters,
|
pub params: Piso3Parameters,
|
||||||
momentum_source: Option<Vec3Fn>,
|
pub(super) momentum_source: Option<Vec3Fn>,
|
||||||
boundary_velocity: Option<Vec3Fn>,
|
boundary_velocity: Option<Vec3Fn>,
|
||||||
pcg_cache: PcgCache3,
|
pcg_cache: PcgCache3,
|
||||||
time: f64,
|
time: f64,
|
||||||
@@ -158,7 +158,7 @@ impl Piso3Solver {
|
|||||||
self.poisson_profile
|
self.poisson_profile
|
||||||
}
|
}
|
||||||
|
|
||||||
fn boundary(&self, x: f64, y: f64, z: f64, t: f64) -> (f64, f64, f64) {
|
pub(super) fn boundary(&self, x: f64, y: f64, z: f64, t: f64) -> (f64, f64, f64) {
|
||||||
self.boundary_velocity
|
self.boundary_velocity
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or((0.0, 0.0, 0.0), |f| f(x, y, z, t))
|
.map_or((0.0, 0.0, 0.0), |f| f(x, y, z, t))
|
||||||
@@ -182,7 +182,7 @@ impl Piso3Solver {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upwind(face_velocity: f64, upstream: f64, downstream: f64) -> f64 {
|
pub(super) fn upwind(face_velocity: f64, upstream: f64, downstream: f64) -> f64 {
|
||||||
if face_velocity >= 0.0 {
|
if face_velocity >= 0.0 {
|
||||||
upstream
|
upstream
|
||||||
} else {
|
} else {
|
||||||
@@ -233,702 +233,6 @@ impl Piso3Solver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Plane above `k` (wrapping when periodic).
|
|
||||||
#[inline]
|
|
||||||
fn k_up(&self, k: usize, nz: usize) -> Option<usize> {
|
|
||||||
if k + 1 < nz {
|
|
||||||
Some(k + 1)
|
|
||||||
} else if self.params.boundaries.periodic_z() {
|
|
||||||
Some(0)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn k_down(&self, k: usize, nz: usize) -> Option<usize> {
|
|
||||||
if k > 0 {
|
|
||||||
Some(k - 1)
|
|
||||||
} else if self.params.boundaries.periodic_z() {
|
|
||||||
Some(nz - 1)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The predictor's right-hand side on the u face `(k, j, i)`, `i = 1..nx`:
|
|
||||||
/// the 2D `u_rhs` expression for expression, then `− conv_z + diff_z`.
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub(crate) fn u_rhs(
|
|
||||||
&self,
|
|
||||||
field: &FlowField3D,
|
|
||||||
k: usize,
|
|
||||||
j: usize,
|
|
||||||
i: usize,
|
|
||||||
t_old: f64,
|
|
||||||
) -> f64 {
|
|
||||||
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 nu = self.fluid.viscosity / rho;
|
|
||||||
let b = self.params.boundaries;
|
|
||||||
let velocity = SideBoundary3::Velocity;
|
|
||||||
let uo = &field.u_old;
|
|
||||||
let vo = &field.v_old;
|
|
||||||
let wo = &field.w_old;
|
|
||||||
let uf = |kk: usize, jj: usize, ii: usize| g.uface(kk, jj, ii);
|
|
||||||
let vf = |kk: usize, jj: usize, ii: usize| g.vface(kk, jj, ii);
|
|
||||||
let wf = |kk: usize, jj: usize, ii: usize| g.wface(kk, jj, ii);
|
|
||||||
let zc = (k as f64 + 0.5) * dz;
|
|
||||||
let u_p = uo[uf(k, j, i)];
|
|
||||||
|
|
||||||
let ue_face = 0.5 * (uo[uf(k, j, i)] + uo[uf(k, j, i + 1)]);
|
|
||||||
let uw_face = 0.5 * (uo[uf(k, j, i - 1)] + uo[uf(k, j, i)]);
|
|
||||||
|
|
||||||
let south_is_wall = j == 0;
|
|
||||||
let north_is_wall = j + 1 == ny;
|
|
||||||
|
|
||||||
let vn_face = 0.5 * (vo[vf(k, j + 1, i - 1)] + vo[vf(k, j + 1, i)]);
|
|
||||||
let vs_face = 0.5 * (vo[vf(k, j, i - 1)] + vo[vf(k, j, i)]);
|
|
||||||
|
|
||||||
let beyond_north = if b.y1 == velocity {
|
|
||||||
self.boundary(i as f64 * dx, ny as f64 * dy, zc, t_old).0
|
|
||||||
} else {
|
|
||||||
u_p
|
|
||||||
};
|
|
||||||
let beyond_south = if b.y0 == velocity {
|
|
||||||
self.boundary(i as f64 * dx, 0.0, zc, t_old).0
|
|
||||||
} else {
|
|
||||||
u_p
|
|
||||||
};
|
|
||||||
|
|
||||||
let conv_x = (ue_face * Self::upwind(ue_face, uo[uf(k, j, i)], uo[uf(k, j, i + 1)])
|
|
||||||
- uw_face * Self::upwind(uw_face, uo[uf(k, j, i - 1)], uo[uf(k, j, i)]))
|
|
||||||
/ dx;
|
|
||||||
let conv_y = (vn_face
|
|
||||||
* if north_is_wall {
|
|
||||||
Self::upwind(vn_face, u_p, beyond_north)
|
|
||||||
} else {
|
|
||||||
Self::upwind(vn_face, uo[uf(k, j, i)], uo[uf(k, j + 1, i)])
|
|
||||||
}
|
|
||||||
- vs_face
|
|
||||||
* if south_is_wall {
|
|
||||||
Self::upwind(vs_face, beyond_south, u_p)
|
|
||||||
} else {
|
|
||||||
Self::upwind(vs_face, uo[uf(k, j - 1, i)], uo[uf(k, j, i)])
|
|
||||||
})
|
|
||||||
/ dy;
|
|
||||||
|
|
||||||
let scheme = self.params.convection_scheme;
|
|
||||||
let mut conv_x = conv_x;
|
|
||||||
let mut conv_y = conv_y;
|
|
||||||
if scheme != ConvectionScheme::Upwind {
|
|
||||||
let delta_e = if ue_face >= 0.0 {
|
|
||||||
scheme.face_correction(
|
|
||||||
Some(uo[uf(k, j, i - 1)]),
|
|
||||||
uo[uf(k, j, i)],
|
|
||||||
uo[uf(k, j, i + 1)],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let far = (i + 2 <= nx).then(|| uo[uf(k, j, i + 2)]);
|
|
||||||
scheme.face_correction(far, uo[uf(k, j, i + 1)], uo[uf(k, j, i)])
|
|
||||||
};
|
|
||||||
let delta_w = if uw_face >= 0.0 {
|
|
||||||
let far = (i >= 2).then(|| uo[uf(k, j, i - 2)]);
|
|
||||||
scheme.face_correction(far, uo[uf(k, j, i - 1)], uo[uf(k, j, i)])
|
|
||||||
} else {
|
|
||||||
scheme.face_correction(
|
|
||||||
Some(uo[uf(k, j, i + 1)]),
|
|
||||||
uo[uf(k, j, i)],
|
|
||||||
uo[uf(k, j, i - 1)],
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let delta_n = if north_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if vn_face >= 0.0 {
|
|
||||||
let far = (j >= 1).then(|| uo[uf(k, j - 1, i)]);
|
|
||||||
scheme.face_correction(far, uo[uf(k, j, i)], uo[uf(k, j + 1, i)])
|
|
||||||
} else {
|
|
||||||
let far = (j + 2 < ny).then(|| uo[uf(k, j + 2, i)]);
|
|
||||||
scheme.face_correction(far, uo[uf(k, j + 1, i)], uo[uf(k, j, i)])
|
|
||||||
};
|
|
||||||
let delta_s = if south_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if vs_face >= 0.0 {
|
|
||||||
let far = (j >= 2).then(|| uo[uf(k, j - 2, i)]);
|
|
||||||
scheme.face_correction(far, uo[uf(k, j - 1, i)], uo[uf(k, j, i)])
|
|
||||||
} else {
|
|
||||||
let far = (j + 1 < ny).then(|| uo[uf(k, j + 1, i)]);
|
|
||||||
scheme.face_correction(far, uo[uf(k, j, i)], uo[uf(k, j - 1, i)])
|
|
||||||
};
|
|
||||||
conv_x += (ue_face * delta_e - uw_face * delta_w) / dx;
|
|
||||||
conv_y += (vn_face * delta_n - vs_face * delta_s) / dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff_x = nu * (uo[uf(k, j, i + 1)] - 2.0 * u_p + uo[uf(k, j, i - 1)]) / (dx * dx);
|
|
||||||
|
|
||||||
let flux_north = if north_is_wall {
|
|
||||||
if b.y1 == velocity {
|
|
||||||
let u_wall = self.boundary(i as f64 * dx, ny as f64 * dy, zc, t_old).0;
|
|
||||||
nu * (u_wall - u_p) / (0.5 * dy)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu * (uo[uf(k, j + 1, i)] - u_p) / dy
|
|
||||||
};
|
|
||||||
let flux_south = if south_is_wall {
|
|
||||||
if b.y0 == velocity {
|
|
||||||
let u_wall = self.boundary(i as f64 * dx, 0.0, zc, t_old).0;
|
|
||||||
nu * (u_p - u_wall) / (0.5 * dy)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu * (u_p - uo[uf(k, j - 1, i)]) / dy
|
|
||||||
};
|
|
||||||
let diff_y = (flux_north - flux_south) / dy;
|
|
||||||
|
|
||||||
let pressure_gradient =
|
|
||||||
-(field.p[g.cell(k, j, i)] - field.p[g.cell(k, j, i - 1)]) / (rho * dx);
|
|
||||||
|
|
||||||
let body_force = self.momentum_source.as_ref().map_or(0.0, |f| {
|
|
||||||
f(i as f64 * dx, (j as f64 + 0.5) * dy, zc, t_old).0 / rho
|
|
||||||
});
|
|
||||||
|
|
||||||
let rhs_2d = -conv_x - conv_y + diff_x + diff_y + pressure_gradient + body_force;
|
|
||||||
|
|
||||||
// --- the z terms, the y pattern turned along k ---
|
|
||||||
let ku = self.k_up(k, nz);
|
|
||||||
let kd = self.k_down(k, nz);
|
|
||||||
let top_is_wall = ku.is_none();
|
|
||||||
let bottom_is_wall = kd.is_none();
|
|
||||||
// The w faces above/below the u face: on top of the cells west and
|
|
||||||
// east of it (face k + 1 of cell k is face index k + 1; periodic:
|
|
||||||
// the face at k = nz equals the face at 0).
|
|
||||||
let wt_face = 0.5 * (wo[wf(k + 1, j, i - 1)] + wo[wf(k + 1, j, i)]);
|
|
||||||
let wb_face = 0.5 * (wo[wf(k, j, i - 1)] + wo[wf(k, j, i)]);
|
|
||||||
let beyond_top = if b.z1 == velocity {
|
|
||||||
self.boundary(i as f64 * dx, (j as f64 + 0.5) * dy, nz as f64 * dz, t_old)
|
|
||||||
.0
|
|
||||||
} else {
|
|
||||||
u_p
|
|
||||||
};
|
|
||||||
let beyond_bottom = if b.z0 == velocity {
|
|
||||||
self.boundary(i as f64 * dx, (j as f64 + 0.5) * dy, 0.0, t_old)
|
|
||||||
.0
|
|
||||||
} else {
|
|
||||||
u_p
|
|
||||||
};
|
|
||||||
let u_up = ku.map(|kk| uo[uf(kk, j, i)]);
|
|
||||||
let u_dn = kd.map(|kk| uo[uf(kk, j, i)]);
|
|
||||||
let mut conv_z = (wt_face
|
|
||||||
* match u_up {
|
|
||||||
Some(un) => Self::upwind(wt_face, u_p, un),
|
|
||||||
None => Self::upwind(wt_face, u_p, beyond_top),
|
|
||||||
}
|
|
||||||
- wb_face
|
|
||||||
* match u_dn {
|
|
||||||
Some(ud) => Self::upwind(wb_face, ud, u_p),
|
|
||||||
None => Self::upwind(wb_face, beyond_bottom, u_p),
|
|
||||||
})
|
|
||||||
/ dz;
|
|
||||||
if scheme != ConvectionScheme::Upwind {
|
|
||||||
let far_up2 = ku
|
|
||||||
.and_then(|kk| self.k_up(kk, nz))
|
|
||||||
.map(|kk| uo[uf(kk, j, i)]);
|
|
||||||
let far_dn2 = kd
|
|
||||||
.and_then(|kk| self.k_down(kk, nz))
|
|
||||||
.map(|kk| uo[uf(kk, j, i)]);
|
|
||||||
let delta_t = if top_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if wt_face >= 0.0 {
|
|
||||||
scheme.face_correction(u_dn, u_p, u_up.unwrap_or(u_p))
|
|
||||||
} else {
|
|
||||||
scheme.face_correction(far_up2, u_up.unwrap_or(u_p), u_p)
|
|
||||||
};
|
|
||||||
let delta_b = if bottom_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if wb_face >= 0.0 {
|
|
||||||
scheme.face_correction(far_dn2, u_dn.unwrap_or(u_p), u_p)
|
|
||||||
} else {
|
|
||||||
scheme.face_correction(u_up, u_p, u_dn.unwrap_or(u_p))
|
|
||||||
};
|
|
||||||
conv_z += (wt_face * delta_t - wb_face * delta_b) / dz;
|
|
||||||
}
|
|
||||||
let flux_top = match u_up {
|
|
||||||
Some(un) => nu * (un - u_p) / dz,
|
|
||||||
None => {
|
|
||||||
if b.z1 == velocity {
|
|
||||||
nu * (beyond_top - u_p) / (0.5 * dz)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let flux_bottom = match u_dn {
|
|
||||||
Some(ud) => nu * (u_p - ud) / dz,
|
|
||||||
None => {
|
|
||||||
if b.z0 == velocity {
|
|
||||||
nu * (u_p - beyond_bottom) / (0.5 * dz)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let diff_z = (flux_top - flux_bottom) / dz;
|
|
||||||
|
|
||||||
rhs_2d - conv_z + diff_z
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The v face `(k, j, i)`, `j = 1..ny`: the 2D `v_rhs` then the z terms.
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub(crate) fn v_rhs(
|
|
||||||
&self,
|
|
||||||
field: &FlowField3D,
|
|
||||||
k: usize,
|
|
||||||
j: usize,
|
|
||||||
i: usize,
|
|
||||||
t_old: f64,
|
|
||||||
) -> f64 {
|
|
||||||
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 nu = self.fluid.viscosity / rho;
|
|
||||||
let b = self.params.boundaries;
|
|
||||||
let velocity = SideBoundary3::Velocity;
|
|
||||||
let uo = &field.u_old;
|
|
||||||
let vo = &field.v_old;
|
|
||||||
let wo = &field.w_old;
|
|
||||||
let uf = |kk: usize, jj: usize, ii: usize| g.uface(kk, jj, ii);
|
|
||||||
let vf = |kk: usize, jj: usize, ii: usize| g.vface(kk, jj, ii);
|
|
||||||
let wf = |kk: usize, jj: usize, ii: usize| g.wface(kk, jj, ii);
|
|
||||||
let zc = (k as f64 + 0.5) * dz;
|
|
||||||
let v_p = vo[vf(k, j, i)];
|
|
||||||
|
|
||||||
let vn_face = 0.5 * (vo[vf(k, j, i)] + vo[vf(k, j + 1, i)]);
|
|
||||||
let vs_face = 0.5 * (vo[vf(k, j - 1, i)] + vo[vf(k, j, i)]);
|
|
||||||
|
|
||||||
let west_is_wall = i == 0;
|
|
||||||
let east_is_wall = i + 1 == nx;
|
|
||||||
|
|
||||||
let ue_face = 0.5 * (uo[uf(k, j - 1, i + 1)] + uo[uf(k, j, i + 1)]);
|
|
||||||
let uw_face = 0.5 * (uo[uf(k, j - 1, i)] + uo[uf(k, j, i)]);
|
|
||||||
let beyond_east = if b.x1 == velocity {
|
|
||||||
self.boundary(nx as f64 * dx, j as f64 * dy, zc, t_old).1
|
|
||||||
} else {
|
|
||||||
v_p
|
|
||||||
};
|
|
||||||
let beyond_west = if b.x0 == velocity {
|
|
||||||
self.boundary(0.0, j as f64 * dy, zc, t_old).1
|
|
||||||
} else {
|
|
||||||
v_p
|
|
||||||
};
|
|
||||||
|
|
||||||
let conv_y = (vn_face * Self::upwind(vn_face, vo[vf(k, j, i)], vo[vf(k, j + 1, i)])
|
|
||||||
- vs_face * Self::upwind(vs_face, vo[vf(k, j - 1, i)], vo[vf(k, j, i)]))
|
|
||||||
/ dy;
|
|
||||||
let conv_x = (ue_face
|
|
||||||
* if east_is_wall {
|
|
||||||
Self::upwind(ue_face, v_p, beyond_east)
|
|
||||||
} else {
|
|
||||||
Self::upwind(ue_face, vo[vf(k, j, i)], vo[vf(k, j, i + 1)])
|
|
||||||
}
|
|
||||||
- uw_face
|
|
||||||
* if west_is_wall {
|
|
||||||
Self::upwind(uw_face, beyond_west, v_p)
|
|
||||||
} else {
|
|
||||||
Self::upwind(uw_face, vo[vf(k, j, i - 1)], vo[vf(k, j, i)])
|
|
||||||
})
|
|
||||||
/ dx;
|
|
||||||
|
|
||||||
let scheme = self.params.convection_scheme;
|
|
||||||
let mut conv_x = conv_x;
|
|
||||||
let mut conv_y = conv_y;
|
|
||||||
if scheme != ConvectionScheme::Upwind {
|
|
||||||
let delta_n = if vn_face >= 0.0 {
|
|
||||||
scheme.face_correction(
|
|
||||||
Some(vo[vf(k, j - 1, i)]),
|
|
||||||
vo[vf(k, j, i)],
|
|
||||||
vo[vf(k, j + 1, i)],
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
let far = (j + 2 <= ny).then(|| vo[vf(k, j + 2, i)]);
|
|
||||||
scheme.face_correction(far, vo[vf(k, j + 1, i)], vo[vf(k, j, i)])
|
|
||||||
};
|
|
||||||
let delta_s = if vs_face >= 0.0 {
|
|
||||||
let far = (j >= 2).then(|| vo[vf(k, j - 2, i)]);
|
|
||||||
scheme.face_correction(far, vo[vf(k, j - 1, i)], vo[vf(k, j, i)])
|
|
||||||
} else {
|
|
||||||
scheme.face_correction(
|
|
||||||
Some(vo[vf(k, j + 1, i)]),
|
|
||||||
vo[vf(k, j, i)],
|
|
||||||
vo[vf(k, j - 1, i)],
|
|
||||||
)
|
|
||||||
};
|
|
||||||
let delta_e = if east_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if ue_face >= 0.0 {
|
|
||||||
let far = (i >= 1).then(|| vo[vf(k, j, i - 1)]);
|
|
||||||
scheme.face_correction(far, vo[vf(k, j, i)], vo[vf(k, j, i + 1)])
|
|
||||||
} else {
|
|
||||||
let far = (i + 2 < nx).then(|| vo[vf(k, j, i + 2)]);
|
|
||||||
scheme.face_correction(far, vo[vf(k, j, i + 1)], vo[vf(k, j, i)])
|
|
||||||
};
|
|
||||||
let delta_w = if west_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if uw_face >= 0.0 {
|
|
||||||
let far = (i >= 2).then(|| vo[vf(k, j, i - 2)]);
|
|
||||||
scheme.face_correction(far, vo[vf(k, j, i - 1)], vo[vf(k, j, i)])
|
|
||||||
} else {
|
|
||||||
let far = (i + 1 < nx).then(|| vo[vf(k, j, i + 1)]);
|
|
||||||
scheme.face_correction(far, vo[vf(k, j, i)], vo[vf(k, j, i - 1)])
|
|
||||||
};
|
|
||||||
conv_y += (vn_face * delta_n - vs_face * delta_s) / dy;
|
|
||||||
conv_x += (ue_face * delta_e - uw_face * delta_w) / dx;
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff_y = nu * (vo[vf(k, j + 1, i)] - 2.0 * v_p + vo[vf(k, j - 1, i)]) / (dy * dy);
|
|
||||||
|
|
||||||
let flux_east = if east_is_wall {
|
|
||||||
if b.x1 == velocity {
|
|
||||||
let v_wall = self.boundary(nx as f64 * dx, j as f64 * dy, zc, t_old).1;
|
|
||||||
nu * (v_wall - v_p) / (0.5 * dx)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu * (vo[vf(k, j, i + 1)] - v_p) / dx
|
|
||||||
};
|
|
||||||
let flux_west = if west_is_wall {
|
|
||||||
if b.x0 == velocity {
|
|
||||||
let v_wall = self.boundary(0.0, j as f64 * dy, zc, t_old).1;
|
|
||||||
nu * (v_p - v_wall) / (0.5 * dx)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu * (v_p - vo[vf(k, j, i - 1)]) / dx
|
|
||||||
};
|
|
||||||
let diff_x = (flux_east - flux_west) / dx;
|
|
||||||
|
|
||||||
let pressure_gradient =
|
|
||||||
-(field.p[g.cell(k, j, i)] - field.p[g.cell(k, j - 1, i)]) / (rho * dy);
|
|
||||||
|
|
||||||
let body_force = self.momentum_source.as_ref().map_or(0.0, |f| {
|
|
||||||
f((i as f64 + 0.5) * dx, j as f64 * dy, zc, t_old).1 / rho
|
|
||||||
});
|
|
||||||
|
|
||||||
let rhs_2d = -conv_x - conv_y + diff_x + diff_y + pressure_gradient + body_force;
|
|
||||||
|
|
||||||
// --- z terms ---
|
|
||||||
let ku = self.k_up(k, nz);
|
|
||||||
let kd = self.k_down(k, nz);
|
|
||||||
let top_is_wall = ku.is_none();
|
|
||||||
let bottom_is_wall = kd.is_none();
|
|
||||||
let wt_face = 0.5 * (wo[wf(k + 1, j - 1, i)] + wo[wf(k + 1, j, i)]);
|
|
||||||
let wb_face = 0.5 * (wo[wf(k, j - 1, i)] + wo[wf(k, j, i)]);
|
|
||||||
let beyond_top = if b.z1 == velocity {
|
|
||||||
self.boundary((i as f64 + 0.5) * dx, j as f64 * dy, nz as f64 * dz, t_old)
|
|
||||||
.1
|
|
||||||
} else {
|
|
||||||
v_p
|
|
||||||
};
|
|
||||||
let beyond_bottom = if b.z0 == velocity {
|
|
||||||
self.boundary((i as f64 + 0.5) * dx, j as f64 * dy, 0.0, t_old)
|
|
||||||
.1
|
|
||||||
} else {
|
|
||||||
v_p
|
|
||||||
};
|
|
||||||
let v_up = ku.map(|kk| vo[vf(kk, j, i)]);
|
|
||||||
let v_dn = kd.map(|kk| vo[vf(kk, j, i)]);
|
|
||||||
let mut conv_z = (wt_face
|
|
||||||
* match v_up {
|
|
||||||
Some(vn) => Self::upwind(wt_face, v_p, vn),
|
|
||||||
None => Self::upwind(wt_face, v_p, beyond_top),
|
|
||||||
}
|
|
||||||
- wb_face
|
|
||||||
* match v_dn {
|
|
||||||
Some(vd) => Self::upwind(wb_face, vd, v_p),
|
|
||||||
None => Self::upwind(wb_face, beyond_bottom, v_p),
|
|
||||||
})
|
|
||||||
/ dz;
|
|
||||||
if scheme != ConvectionScheme::Upwind {
|
|
||||||
let far_up2 = ku
|
|
||||||
.and_then(|kk| self.k_up(kk, nz))
|
|
||||||
.map(|kk| vo[vf(kk, j, i)]);
|
|
||||||
let far_dn2 = kd
|
|
||||||
.and_then(|kk| self.k_down(kk, nz))
|
|
||||||
.map(|kk| vo[vf(kk, j, i)]);
|
|
||||||
let delta_t = if top_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if wt_face >= 0.0 {
|
|
||||||
scheme.face_correction(v_dn, v_p, v_up.unwrap_or(v_p))
|
|
||||||
} else {
|
|
||||||
scheme.face_correction(far_up2, v_up.unwrap_or(v_p), v_p)
|
|
||||||
};
|
|
||||||
let delta_b = if bottom_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if wb_face >= 0.0 {
|
|
||||||
scheme.face_correction(far_dn2, v_dn.unwrap_or(v_p), v_p)
|
|
||||||
} else {
|
|
||||||
scheme.face_correction(v_up, v_p, v_dn.unwrap_or(v_p))
|
|
||||||
};
|
|
||||||
conv_z += (wt_face * delta_t - wb_face * delta_b) / dz;
|
|
||||||
}
|
|
||||||
let flux_top = match v_up {
|
|
||||||
Some(vn) => nu * (vn - v_p) / dz,
|
|
||||||
None => {
|
|
||||||
if b.z1 == velocity {
|
|
||||||
nu * (beyond_top - v_p) / (0.5 * dz)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let flux_bottom = match v_dn {
|
|
||||||
Some(vd) => nu * (v_p - vd) / dz,
|
|
||||||
None => {
|
|
||||||
if b.z0 == velocity {
|
|
||||||
nu * (v_p - beyond_bottom) / (0.5 * dz)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let diff_z = (flux_top - flux_bottom) / dz;
|
|
||||||
|
|
||||||
rhs_2d - conv_z + diff_z
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The w face `(k, j, i)` between cells `k − 1` (wrapping when periodic)
|
|
||||||
/// and `k`: the v pattern with z as its own direction and x, y transverse.
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub(crate) fn w_rhs(
|
|
||||||
&self,
|
|
||||||
field: &FlowField3D,
|
|
||||||
k: usize,
|
|
||||||
j: usize,
|
|
||||||
i: usize,
|
|
||||||
t_old: f64,
|
|
||||||
) -> f64 {
|
|
||||||
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 nu = self.fluid.viscosity / rho;
|
|
||||||
let b = self.params.boundaries;
|
|
||||||
let velocity = SideBoundary3::Velocity;
|
|
||||||
let periodic = b.periodic_z();
|
|
||||||
let uo = &field.u_old;
|
|
||||||
let vo = &field.v_old;
|
|
||||||
let wo = &field.w_old;
|
|
||||||
let uf = |kk: usize, jj: usize, ii: usize| g.uface(kk, jj, ii);
|
|
||||||
let vf = |kk: usize, jj: usize, ii: usize| g.vface(kk, jj, ii);
|
|
||||||
let wf = |kk: usize, jj: usize, ii: usize| g.wface(kk, jj, ii);
|
|
||||||
// The cells below and above this face, and the faces around them.
|
|
||||||
let k_below = if k > 0 { k - 1 } else { nz - 1 }; // k = 0 only when periodic
|
|
||||||
let k_above = k % nz; // k = nz only when periodic (the same face as 0)
|
|
||||||
let wface_of = |kk: usize| wf(kk % (nz + 1), j, i);
|
|
||||||
// Own-direction neighbours: the faces k − 1 and k + 1 (wrapping).
|
|
||||||
let w_dn_idx = if k > 0 {
|
|
||||||
wf(k - 1, j, i)
|
|
||||||
} else {
|
|
||||||
wf(nz - 1, j, i)
|
|
||||||
};
|
|
||||||
let w_up_idx = if k + 1 <= nz {
|
|
||||||
if k + 1 == nz && periodic {
|
|
||||||
wf(0, j, i)
|
|
||||||
} else {
|
|
||||||
wf(k + 1, j, i)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
wf(1, j, i)
|
|
||||||
};
|
|
||||||
let _ = wface_of;
|
|
||||||
let zf = k as f64 * dz;
|
|
||||||
let w_p = wo[wf(k, j, i)];
|
|
||||||
|
|
||||||
let wt_face = 0.5 * (wo[wf(k, j, i)] + wo[w_up_idx]);
|
|
||||||
let wb_face = 0.5 * (wo[w_dn_idx] + wo[wf(k, j, i)]);
|
|
||||||
|
|
||||||
let west_is_wall = i == 0;
|
|
||||||
let east_is_wall = i + 1 == nx;
|
|
||||||
let south_is_wall = j == 0;
|
|
||||||
let north_is_wall = j + 1 == ny;
|
|
||||||
|
|
||||||
let ue_face = 0.5 * (uo[uf(k_below, j, i + 1)] + uo[uf(k_above, j, i + 1)]);
|
|
||||||
let uw_face = 0.5 * (uo[uf(k_below, j, i)] + uo[uf(k_above, j, i)]);
|
|
||||||
let vn_face = 0.5 * (vo[vf(k_below, j + 1, i)] + vo[vf(k_above, j + 1, i)]);
|
|
||||||
let vs_face = 0.5 * (vo[vf(k_below, j, i)] + vo[vf(k_above, j, i)]);
|
|
||||||
let beyond_east = if b.x1 == velocity {
|
|
||||||
self.boundary(nx as f64 * dx, (j as f64 + 0.5) * dy, zf, t_old)
|
|
||||||
.2
|
|
||||||
} else {
|
|
||||||
w_p
|
|
||||||
};
|
|
||||||
let beyond_west = if b.x0 == velocity {
|
|
||||||
self.boundary(0.0, (j as f64 + 0.5) * dy, zf, t_old).2
|
|
||||||
} else {
|
|
||||||
w_p
|
|
||||||
};
|
|
||||||
let beyond_north = if b.y1 == velocity {
|
|
||||||
self.boundary((i as f64 + 0.5) * dx, ny as f64 * dy, zf, t_old)
|
|
||||||
.2
|
|
||||||
} else {
|
|
||||||
w_p
|
|
||||||
};
|
|
||||||
let beyond_south = if b.y0 == velocity {
|
|
||||||
self.boundary((i as f64 + 0.5) * dx, 0.0, zf, t_old).2
|
|
||||||
} else {
|
|
||||||
w_p
|
|
||||||
};
|
|
||||||
|
|
||||||
let conv_z = (wt_face * Self::upwind(wt_face, w_p, wo[w_up_idx])
|
|
||||||
- wb_face * Self::upwind(wb_face, wo[w_dn_idx], w_p))
|
|
||||||
/ dz;
|
|
||||||
let conv_x = (ue_face
|
|
||||||
* if east_is_wall {
|
|
||||||
Self::upwind(ue_face, w_p, beyond_east)
|
|
||||||
} else {
|
|
||||||
Self::upwind(ue_face, w_p, wo[wf(k, j, i + 1)])
|
|
||||||
}
|
|
||||||
- uw_face
|
|
||||||
* if west_is_wall {
|
|
||||||
Self::upwind(uw_face, beyond_west, w_p)
|
|
||||||
} else {
|
|
||||||
Self::upwind(uw_face, wo[wf(k, j, i - 1)], w_p)
|
|
||||||
})
|
|
||||||
/ dx;
|
|
||||||
let conv_y = (vn_face
|
|
||||||
* if north_is_wall {
|
|
||||||
Self::upwind(vn_face, w_p, beyond_north)
|
|
||||||
} else {
|
|
||||||
Self::upwind(vn_face, w_p, wo[wf(k, j + 1, i)])
|
|
||||||
}
|
|
||||||
- vs_face
|
|
||||||
* if south_is_wall {
|
|
||||||
Self::upwind(vs_face, beyond_south, w_p)
|
|
||||||
} else {
|
|
||||||
Self::upwind(vs_face, wo[wf(k, j - 1, i)], w_p)
|
|
||||||
})
|
|
||||||
/ dy;
|
|
||||||
|
|
||||||
let scheme = self.params.convection_scheme;
|
|
||||||
let mut conv_x = conv_x;
|
|
||||||
let mut conv_y = conv_y;
|
|
||||||
let mut conv_z = conv_z;
|
|
||||||
if scheme != ConvectionScheme::Upwind {
|
|
||||||
// Own direction: far nodes two faces away (wrapping when periodic).
|
|
||||||
let far_up2 = if periodic {
|
|
||||||
Some(wo[wf((k + 2) % nz, j, i)])
|
|
||||||
} else {
|
|
||||||
(k + 2 <= nz).then(|| wo[wf(k + 2, j, i)])
|
|
||||||
};
|
|
||||||
let far_dn2 = if periodic {
|
|
||||||
Some(wo[wf((k + nz - 2) % nz, j, i)])
|
|
||||||
} else {
|
|
||||||
(k >= 2).then(|| wo[wf(k - 2, j, i)])
|
|
||||||
};
|
|
||||||
let delta_t = if wt_face >= 0.0 {
|
|
||||||
scheme.face_correction(Some(wo[w_dn_idx]), w_p, wo[w_up_idx])
|
|
||||||
} else {
|
|
||||||
scheme.face_correction(far_up2, wo[w_up_idx], w_p)
|
|
||||||
};
|
|
||||||
let delta_b = if wb_face >= 0.0 {
|
|
||||||
scheme.face_correction(far_dn2, wo[w_dn_idx], w_p)
|
|
||||||
} else {
|
|
||||||
scheme.face_correction(Some(wo[w_up_idx]), w_p, wo[w_dn_idx])
|
|
||||||
};
|
|
||||||
let delta_e = if east_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if ue_face >= 0.0 {
|
|
||||||
let far = (i >= 1).then(|| wo[wf(k, j, i - 1)]);
|
|
||||||
scheme.face_correction(far, w_p, wo[wf(k, j, i + 1)])
|
|
||||||
} else {
|
|
||||||
let far = (i + 2 < nx).then(|| wo[wf(k, j, i + 2)]);
|
|
||||||
scheme.face_correction(far, wo[wf(k, j, i + 1)], w_p)
|
|
||||||
};
|
|
||||||
let delta_w = if west_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if uw_face >= 0.0 {
|
|
||||||
let far = (i >= 2).then(|| wo[wf(k, j, i - 2)]);
|
|
||||||
scheme.face_correction(far, wo[wf(k, j, i - 1)], w_p)
|
|
||||||
} else {
|
|
||||||
let far = (i + 1 < nx).then(|| wo[wf(k, j, i + 1)]);
|
|
||||||
scheme.face_correction(far, w_p, wo[wf(k, j, i - 1)])
|
|
||||||
};
|
|
||||||
let delta_n = if north_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if vn_face >= 0.0 {
|
|
||||||
let far = (j >= 1).then(|| wo[wf(k, j - 1, i)]);
|
|
||||||
scheme.face_correction(far, w_p, wo[wf(k, j + 1, i)])
|
|
||||||
} else {
|
|
||||||
let far = (j + 2 < ny).then(|| wo[wf(k, j + 2, i)]);
|
|
||||||
scheme.face_correction(far, wo[wf(k, j + 1, i)], w_p)
|
|
||||||
};
|
|
||||||
let delta_s = if south_is_wall {
|
|
||||||
0.0
|
|
||||||
} else if vs_face >= 0.0 {
|
|
||||||
let far = (j >= 2).then(|| wo[wf(k, j - 2, i)]);
|
|
||||||
scheme.face_correction(far, wo[wf(k, j - 1, i)], w_p)
|
|
||||||
} else {
|
|
||||||
let far = (j + 1 < ny).then(|| wo[wf(k, j + 1, i)]);
|
|
||||||
scheme.face_correction(far, w_p, wo[wf(k, j - 1, i)])
|
|
||||||
};
|
|
||||||
conv_z += (wt_face * delta_t - wb_face * delta_b) / dz;
|
|
||||||
conv_x += (ue_face * delta_e - uw_face * delta_w) / dx;
|
|
||||||
conv_y += (vn_face * delta_n - vs_face * delta_s) / dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
let diff_z = nu * (wo[w_up_idx] - 2.0 * w_p + wo[w_dn_idx]) / (dz * dz);
|
|
||||||
let flux_east = if east_is_wall {
|
|
||||||
if b.x1 == velocity {
|
|
||||||
nu * (beyond_east - w_p) / (0.5 * dx)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu * (wo[wf(k, j, i + 1)] - w_p) / dx
|
|
||||||
};
|
|
||||||
let flux_west = if west_is_wall {
|
|
||||||
if b.x0 == velocity {
|
|
||||||
nu * (w_p - beyond_west) / (0.5 * dx)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu * (w_p - wo[wf(k, j, i - 1)]) / dx
|
|
||||||
};
|
|
||||||
let diff_x = (flux_east - flux_west) / dx;
|
|
||||||
let flux_north = if north_is_wall {
|
|
||||||
if b.y1 == velocity {
|
|
||||||
nu * (beyond_north - w_p) / (0.5 * dy)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu * (wo[wf(k, j + 1, i)] - w_p) / dy
|
|
||||||
};
|
|
||||||
let flux_south = if south_is_wall {
|
|
||||||
if b.y0 == velocity {
|
|
||||||
nu * (w_p - beyond_south) / (0.5 * dy)
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
nu * (w_p - wo[wf(k, j - 1, i)]) / dy
|
|
||||||
};
|
|
||||||
let diff_y = (flux_north - flux_south) / dy;
|
|
||||||
|
|
||||||
let pressure_gradient =
|
|
||||||
-(field.p[g.cell(k_above, j, i)] - field.p[g.cell(k_below, j, i)]) / (rho * dz);
|
|
||||||
let body_force = self.momentum_source.as_ref().map_or(0.0, |f| {
|
|
||||||
f((i as f64 + 0.5) * dx, (j as f64 + 0.5) * dy, zf, t_old).2 / rho
|
|
||||||
});
|
|
||||||
|
|
||||||
-conv_x - conv_y - conv_z + diff_x + diff_y + diff_z + pressure_gradient + body_force
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The explicit predictor on the fluid faces; outlet faces zero-gradient.
|
/// The explicit predictor on the fluid faces; outlet faces zero-gradient.
|
||||||
fn momentum_predictor(&self, field: &mut FlowField3D, dt: f64, t_old: f64) {
|
fn momentum_predictor(&self, field: &mut FlowField3D, dt: f64, t_old: f64) {
|
||||||
let g = field.grid;
|
let g = field.grid;
|
||||||
|
|||||||
@@ -0,0 +1,700 @@
|
|||||||
|
//! The three explicit predictors of the 3D PISO step (`Piso3Solver`): the
|
||||||
|
//! 2D `u_rhs`/`v_rhs` expression for expression with the z terms appended,
|
||||||
|
//! and `w_rhs` as the v pattern turned along z. Split from `piso_host.rs`
|
||||||
|
//! for the file-size rule; `impl Piso3Solver` continues here.
|
||||||
|
|
||||||
|
use super::flow_field::FlowField3D;
|
||||||
|
use super::piso_host::{Piso3Solver, SideBoundary3};
|
||||||
|
use crate::solvers::incompressible::simple::ConvectionScheme;
|
||||||
|
|
||||||
|
impl Piso3Solver {
|
||||||
|
/// Plane above `k` (wrapping when periodic).
|
||||||
|
#[inline]
|
||||||
|
fn k_up(&self, k: usize, nz: usize) -> Option<usize> {
|
||||||
|
if k + 1 < nz {
|
||||||
|
Some(k + 1)
|
||||||
|
} else if self.params.boundaries.periodic_z() {
|
||||||
|
Some(0)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn k_down(&self, k: usize, nz: usize) -> Option<usize> {
|
||||||
|
if k > 0 {
|
||||||
|
Some(k - 1)
|
||||||
|
} else if self.params.boundaries.periodic_z() {
|
||||||
|
Some(nz - 1)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The predictor's right-hand side on the u face `(k, j, i)`, `i = 1..nx`:
|
||||||
|
/// the 2D `u_rhs` expression for expression, then `− conv_z + diff_z`.
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
pub(crate) fn u_rhs(
|
||||||
|
&self,
|
||||||
|
field: &FlowField3D,
|
||||||
|
k: usize,
|
||||||
|
j: usize,
|
||||||
|
i: usize,
|
||||||
|
t_old: f64,
|
||||||
|
) -> f64 {
|
||||||
|
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 nu = self.fluid.viscosity / rho;
|
||||||
|
let b = self.params.boundaries;
|
||||||
|
let velocity = SideBoundary3::Velocity;
|
||||||
|
let uo = &field.u_old;
|
||||||
|
let vo = &field.v_old;
|
||||||
|
let wo = &field.w_old;
|
||||||
|
let uf = |kk: usize, jj: usize, ii: usize| g.uface(kk, jj, ii);
|
||||||
|
let vf = |kk: usize, jj: usize, ii: usize| g.vface(kk, jj, ii);
|
||||||
|
let wf = |kk: usize, jj: usize, ii: usize| g.wface(kk, jj, ii);
|
||||||
|
let zc = (k as f64 + 0.5) * dz;
|
||||||
|
let u_p = uo[uf(k, j, i)];
|
||||||
|
|
||||||
|
let ue_face = 0.5 * (uo[uf(k, j, i)] + uo[uf(k, j, i + 1)]);
|
||||||
|
let uw_face = 0.5 * (uo[uf(k, j, i - 1)] + uo[uf(k, j, i)]);
|
||||||
|
|
||||||
|
let south_is_wall = j == 0;
|
||||||
|
let north_is_wall = j + 1 == ny;
|
||||||
|
|
||||||
|
let vn_face = 0.5 * (vo[vf(k, j + 1, i - 1)] + vo[vf(k, j + 1, i)]);
|
||||||
|
let vs_face = 0.5 * (vo[vf(k, j, i - 1)] + vo[vf(k, j, i)]);
|
||||||
|
|
||||||
|
let beyond_north = if b.y1 == velocity {
|
||||||
|
self.boundary(i as f64 * dx, ny as f64 * dy, zc, t_old).0
|
||||||
|
} else {
|
||||||
|
u_p
|
||||||
|
};
|
||||||
|
let beyond_south = if b.y0 == velocity {
|
||||||
|
self.boundary(i as f64 * dx, 0.0, zc, t_old).0
|
||||||
|
} else {
|
||||||
|
u_p
|
||||||
|
};
|
||||||
|
|
||||||
|
let conv_x = (ue_face * Self::upwind(ue_face, uo[uf(k, j, i)], uo[uf(k, j, i + 1)])
|
||||||
|
- uw_face * Self::upwind(uw_face, uo[uf(k, j, i - 1)], uo[uf(k, j, i)]))
|
||||||
|
/ dx;
|
||||||
|
let conv_y = (vn_face
|
||||||
|
* if north_is_wall {
|
||||||
|
Self::upwind(vn_face, u_p, beyond_north)
|
||||||
|
} else {
|
||||||
|
Self::upwind(vn_face, uo[uf(k, j, i)], uo[uf(k, j + 1, i)])
|
||||||
|
}
|
||||||
|
- vs_face
|
||||||
|
* if south_is_wall {
|
||||||
|
Self::upwind(vs_face, beyond_south, u_p)
|
||||||
|
} else {
|
||||||
|
Self::upwind(vs_face, uo[uf(k, j - 1, i)], uo[uf(k, j, i)])
|
||||||
|
})
|
||||||
|
/ dy;
|
||||||
|
|
||||||
|
let scheme = self.params.convection_scheme;
|
||||||
|
let mut conv_x = conv_x;
|
||||||
|
let mut conv_y = conv_y;
|
||||||
|
if scheme != ConvectionScheme::Upwind {
|
||||||
|
let delta_e = if ue_face >= 0.0 {
|
||||||
|
scheme.face_correction(
|
||||||
|
Some(uo[uf(k, j, i - 1)]),
|
||||||
|
uo[uf(k, j, i)],
|
||||||
|
uo[uf(k, j, i + 1)],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let far = (i + 2 <= nx).then(|| uo[uf(k, j, i + 2)]);
|
||||||
|
scheme.face_correction(far, uo[uf(k, j, i + 1)], uo[uf(k, j, i)])
|
||||||
|
};
|
||||||
|
let delta_w = if uw_face >= 0.0 {
|
||||||
|
let far = (i >= 2).then(|| uo[uf(k, j, i - 2)]);
|
||||||
|
scheme.face_correction(far, uo[uf(k, j, i - 1)], uo[uf(k, j, i)])
|
||||||
|
} else {
|
||||||
|
scheme.face_correction(
|
||||||
|
Some(uo[uf(k, j, i + 1)]),
|
||||||
|
uo[uf(k, j, i)],
|
||||||
|
uo[uf(k, j, i - 1)],
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let delta_n = if north_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if vn_face >= 0.0 {
|
||||||
|
let far = (j >= 1).then(|| uo[uf(k, j - 1, i)]);
|
||||||
|
scheme.face_correction(far, uo[uf(k, j, i)], uo[uf(k, j + 1, i)])
|
||||||
|
} else {
|
||||||
|
let far = (j + 2 < ny).then(|| uo[uf(k, j + 2, i)]);
|
||||||
|
scheme.face_correction(far, uo[uf(k, j + 1, i)], uo[uf(k, j, i)])
|
||||||
|
};
|
||||||
|
let delta_s = if south_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if vs_face >= 0.0 {
|
||||||
|
let far = (j >= 2).then(|| uo[uf(k, j - 2, i)]);
|
||||||
|
scheme.face_correction(far, uo[uf(k, j - 1, i)], uo[uf(k, j, i)])
|
||||||
|
} else {
|
||||||
|
let far = (j + 1 < ny).then(|| uo[uf(k, j + 1, i)]);
|
||||||
|
scheme.face_correction(far, uo[uf(k, j, i)], uo[uf(k, j - 1, i)])
|
||||||
|
};
|
||||||
|
conv_x += (ue_face * delta_e - uw_face * delta_w) / dx;
|
||||||
|
conv_y += (vn_face * delta_n - vs_face * delta_s) / dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
let diff_x = nu * (uo[uf(k, j, i + 1)] - 2.0 * u_p + uo[uf(k, j, i - 1)]) / (dx * dx);
|
||||||
|
|
||||||
|
let flux_north = if north_is_wall {
|
||||||
|
if b.y1 == velocity {
|
||||||
|
let u_wall = self.boundary(i as f64 * dx, ny as f64 * dy, zc, t_old).0;
|
||||||
|
nu * (u_wall - u_p) / (0.5 * dy)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nu * (uo[uf(k, j + 1, i)] - u_p) / dy
|
||||||
|
};
|
||||||
|
let flux_south = if south_is_wall {
|
||||||
|
if b.y0 == velocity {
|
||||||
|
let u_wall = self.boundary(i as f64 * dx, 0.0, zc, t_old).0;
|
||||||
|
nu * (u_p - u_wall) / (0.5 * dy)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nu * (u_p - uo[uf(k, j - 1, i)]) / dy
|
||||||
|
};
|
||||||
|
let diff_y = (flux_north - flux_south) / dy;
|
||||||
|
|
||||||
|
let pressure_gradient =
|
||||||
|
-(field.p[g.cell(k, j, i)] - field.p[g.cell(k, j, i - 1)]) / (rho * dx);
|
||||||
|
|
||||||
|
let body_force = self.momentum_source.as_ref().map_or(0.0, |f| {
|
||||||
|
f(i as f64 * dx, (j as f64 + 0.5) * dy, zc, t_old).0 / rho
|
||||||
|
});
|
||||||
|
|
||||||
|
let rhs_2d = -conv_x - conv_y + diff_x + diff_y + pressure_gradient + body_force;
|
||||||
|
|
||||||
|
// --- the z terms, the y pattern turned along k ---
|
||||||
|
let ku = self.k_up(k, nz);
|
||||||
|
let kd = self.k_down(k, nz);
|
||||||
|
let top_is_wall = ku.is_none();
|
||||||
|
let bottom_is_wall = kd.is_none();
|
||||||
|
// The w faces above/below the u face: on top of the cells west and
|
||||||
|
// east of it (face k + 1 of cell k is face index k + 1; periodic:
|
||||||
|
// the face at k = nz equals the face at 0).
|
||||||
|
let wt_face = 0.5 * (wo[wf(k + 1, j, i - 1)] + wo[wf(k + 1, j, i)]);
|
||||||
|
let wb_face = 0.5 * (wo[wf(k, j, i - 1)] + wo[wf(k, j, i)]);
|
||||||
|
let beyond_top = if b.z1 == velocity {
|
||||||
|
self.boundary(i as f64 * dx, (j as f64 + 0.5) * dy, nz as f64 * dz, t_old)
|
||||||
|
.0
|
||||||
|
} else {
|
||||||
|
u_p
|
||||||
|
};
|
||||||
|
let beyond_bottom = if b.z0 == velocity {
|
||||||
|
self.boundary(i as f64 * dx, (j as f64 + 0.5) * dy, 0.0, t_old)
|
||||||
|
.0
|
||||||
|
} else {
|
||||||
|
u_p
|
||||||
|
};
|
||||||
|
let u_up = ku.map(|kk| uo[uf(kk, j, i)]);
|
||||||
|
let u_dn = kd.map(|kk| uo[uf(kk, j, i)]);
|
||||||
|
let mut conv_z = (wt_face
|
||||||
|
* match u_up {
|
||||||
|
Some(un) => Self::upwind(wt_face, u_p, un),
|
||||||
|
None => Self::upwind(wt_face, u_p, beyond_top),
|
||||||
|
}
|
||||||
|
- wb_face
|
||||||
|
* match u_dn {
|
||||||
|
Some(ud) => Self::upwind(wb_face, ud, u_p),
|
||||||
|
None => Self::upwind(wb_face, beyond_bottom, u_p),
|
||||||
|
})
|
||||||
|
/ dz;
|
||||||
|
if scheme != ConvectionScheme::Upwind {
|
||||||
|
let far_up2 = ku
|
||||||
|
.and_then(|kk| self.k_up(kk, nz))
|
||||||
|
.map(|kk| uo[uf(kk, j, i)]);
|
||||||
|
let far_dn2 = kd
|
||||||
|
.and_then(|kk| self.k_down(kk, nz))
|
||||||
|
.map(|kk| uo[uf(kk, j, i)]);
|
||||||
|
let delta_t = if top_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if wt_face >= 0.0 {
|
||||||
|
scheme.face_correction(u_dn, u_p, u_up.unwrap_or(u_p))
|
||||||
|
} else {
|
||||||
|
scheme.face_correction(far_up2, u_up.unwrap_or(u_p), u_p)
|
||||||
|
};
|
||||||
|
let delta_b = if bottom_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if wb_face >= 0.0 {
|
||||||
|
scheme.face_correction(far_dn2, u_dn.unwrap_or(u_p), u_p)
|
||||||
|
} else {
|
||||||
|
scheme.face_correction(u_up, u_p, u_dn.unwrap_or(u_p))
|
||||||
|
};
|
||||||
|
conv_z += (wt_face * delta_t - wb_face * delta_b) / dz;
|
||||||
|
}
|
||||||
|
let flux_top = match u_up {
|
||||||
|
Some(un) => nu * (un - u_p) / dz,
|
||||||
|
None => {
|
||||||
|
if b.z1 == velocity {
|
||||||
|
nu * (beyond_top - u_p) / (0.5 * dz)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let flux_bottom = match u_dn {
|
||||||
|
Some(ud) => nu * (u_p - ud) / dz,
|
||||||
|
None => {
|
||||||
|
if b.z0 == velocity {
|
||||||
|
nu * (u_p - beyond_bottom) / (0.5 * dz)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let diff_z = (flux_top - flux_bottom) / dz;
|
||||||
|
|
||||||
|
rhs_2d - conv_z + diff_z
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The v face `(k, j, i)`, `j = 1..ny`: the 2D `v_rhs` then the z terms.
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
pub(crate) fn v_rhs(
|
||||||
|
&self,
|
||||||
|
field: &FlowField3D,
|
||||||
|
k: usize,
|
||||||
|
j: usize,
|
||||||
|
i: usize,
|
||||||
|
t_old: f64,
|
||||||
|
) -> f64 {
|
||||||
|
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 nu = self.fluid.viscosity / rho;
|
||||||
|
let b = self.params.boundaries;
|
||||||
|
let velocity = SideBoundary3::Velocity;
|
||||||
|
let uo = &field.u_old;
|
||||||
|
let vo = &field.v_old;
|
||||||
|
let wo = &field.w_old;
|
||||||
|
let uf = |kk: usize, jj: usize, ii: usize| g.uface(kk, jj, ii);
|
||||||
|
let vf = |kk: usize, jj: usize, ii: usize| g.vface(kk, jj, ii);
|
||||||
|
let wf = |kk: usize, jj: usize, ii: usize| g.wface(kk, jj, ii);
|
||||||
|
let zc = (k as f64 + 0.5) * dz;
|
||||||
|
let v_p = vo[vf(k, j, i)];
|
||||||
|
|
||||||
|
let vn_face = 0.5 * (vo[vf(k, j, i)] + vo[vf(k, j + 1, i)]);
|
||||||
|
let vs_face = 0.5 * (vo[vf(k, j - 1, i)] + vo[vf(k, j, i)]);
|
||||||
|
|
||||||
|
let west_is_wall = i == 0;
|
||||||
|
let east_is_wall = i + 1 == nx;
|
||||||
|
|
||||||
|
let ue_face = 0.5 * (uo[uf(k, j - 1, i + 1)] + uo[uf(k, j, i + 1)]);
|
||||||
|
let uw_face = 0.5 * (uo[uf(k, j - 1, i)] + uo[uf(k, j, i)]);
|
||||||
|
let beyond_east = if b.x1 == velocity {
|
||||||
|
self.boundary(nx as f64 * dx, j as f64 * dy, zc, t_old).1
|
||||||
|
} else {
|
||||||
|
v_p
|
||||||
|
};
|
||||||
|
let beyond_west = if b.x0 == velocity {
|
||||||
|
self.boundary(0.0, j as f64 * dy, zc, t_old).1
|
||||||
|
} else {
|
||||||
|
v_p
|
||||||
|
};
|
||||||
|
|
||||||
|
let conv_y = (vn_face * Self::upwind(vn_face, vo[vf(k, j, i)], vo[vf(k, j + 1, i)])
|
||||||
|
- vs_face * Self::upwind(vs_face, vo[vf(k, j - 1, i)], vo[vf(k, j, i)]))
|
||||||
|
/ dy;
|
||||||
|
let conv_x = (ue_face
|
||||||
|
* if east_is_wall {
|
||||||
|
Self::upwind(ue_face, v_p, beyond_east)
|
||||||
|
} else {
|
||||||
|
Self::upwind(ue_face, vo[vf(k, j, i)], vo[vf(k, j, i + 1)])
|
||||||
|
}
|
||||||
|
- uw_face
|
||||||
|
* if west_is_wall {
|
||||||
|
Self::upwind(uw_face, beyond_west, v_p)
|
||||||
|
} else {
|
||||||
|
Self::upwind(uw_face, vo[vf(k, j, i - 1)], vo[vf(k, j, i)])
|
||||||
|
})
|
||||||
|
/ dx;
|
||||||
|
|
||||||
|
let scheme = self.params.convection_scheme;
|
||||||
|
let mut conv_x = conv_x;
|
||||||
|
let mut conv_y = conv_y;
|
||||||
|
if scheme != ConvectionScheme::Upwind {
|
||||||
|
let delta_n = if vn_face >= 0.0 {
|
||||||
|
scheme.face_correction(
|
||||||
|
Some(vo[vf(k, j - 1, i)]),
|
||||||
|
vo[vf(k, j, i)],
|
||||||
|
vo[vf(k, j + 1, i)],
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
let far = (j + 2 <= ny).then(|| vo[vf(k, j + 2, i)]);
|
||||||
|
scheme.face_correction(far, vo[vf(k, j + 1, i)], vo[vf(k, j, i)])
|
||||||
|
};
|
||||||
|
let delta_s = if vs_face >= 0.0 {
|
||||||
|
let far = (j >= 2).then(|| vo[vf(k, j - 2, i)]);
|
||||||
|
scheme.face_correction(far, vo[vf(k, j - 1, i)], vo[vf(k, j, i)])
|
||||||
|
} else {
|
||||||
|
scheme.face_correction(
|
||||||
|
Some(vo[vf(k, j + 1, i)]),
|
||||||
|
vo[vf(k, j, i)],
|
||||||
|
vo[vf(k, j - 1, i)],
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let delta_e = if east_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if ue_face >= 0.0 {
|
||||||
|
let far = (i >= 1).then(|| vo[vf(k, j, i - 1)]);
|
||||||
|
scheme.face_correction(far, vo[vf(k, j, i)], vo[vf(k, j, i + 1)])
|
||||||
|
} else {
|
||||||
|
let far = (i + 2 < nx).then(|| vo[vf(k, j, i + 2)]);
|
||||||
|
scheme.face_correction(far, vo[vf(k, j, i + 1)], vo[vf(k, j, i)])
|
||||||
|
};
|
||||||
|
let delta_w = if west_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if uw_face >= 0.0 {
|
||||||
|
let far = (i >= 2).then(|| vo[vf(k, j, i - 2)]);
|
||||||
|
scheme.face_correction(far, vo[vf(k, j, i - 1)], vo[vf(k, j, i)])
|
||||||
|
} else {
|
||||||
|
let far = (i + 1 < nx).then(|| vo[vf(k, j, i + 1)]);
|
||||||
|
scheme.face_correction(far, vo[vf(k, j, i)], vo[vf(k, j, i - 1)])
|
||||||
|
};
|
||||||
|
conv_y += (vn_face * delta_n - vs_face * delta_s) / dy;
|
||||||
|
conv_x += (ue_face * delta_e - uw_face * delta_w) / dx;
|
||||||
|
}
|
||||||
|
|
||||||
|
let diff_y = nu * (vo[vf(k, j + 1, i)] - 2.0 * v_p + vo[vf(k, j - 1, i)]) / (dy * dy);
|
||||||
|
|
||||||
|
let flux_east = if east_is_wall {
|
||||||
|
if b.x1 == velocity {
|
||||||
|
let v_wall = self.boundary(nx as f64 * dx, j as f64 * dy, zc, t_old).1;
|
||||||
|
nu * (v_wall - v_p) / (0.5 * dx)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nu * (vo[vf(k, j, i + 1)] - v_p) / dx
|
||||||
|
};
|
||||||
|
let flux_west = if west_is_wall {
|
||||||
|
if b.x0 == velocity {
|
||||||
|
let v_wall = self.boundary(0.0, j as f64 * dy, zc, t_old).1;
|
||||||
|
nu * (v_p - v_wall) / (0.5 * dx)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nu * (v_p - vo[vf(k, j, i - 1)]) / dx
|
||||||
|
};
|
||||||
|
let diff_x = (flux_east - flux_west) / dx;
|
||||||
|
|
||||||
|
let pressure_gradient =
|
||||||
|
-(field.p[g.cell(k, j, i)] - field.p[g.cell(k, j - 1, i)]) / (rho * dy);
|
||||||
|
|
||||||
|
let body_force = self.momentum_source.as_ref().map_or(0.0, |f| {
|
||||||
|
f((i as f64 + 0.5) * dx, j as f64 * dy, zc, t_old).1 / rho
|
||||||
|
});
|
||||||
|
|
||||||
|
let rhs_2d = -conv_x - conv_y + diff_x + diff_y + pressure_gradient + body_force;
|
||||||
|
|
||||||
|
// --- z terms ---
|
||||||
|
let ku = self.k_up(k, nz);
|
||||||
|
let kd = self.k_down(k, nz);
|
||||||
|
let top_is_wall = ku.is_none();
|
||||||
|
let bottom_is_wall = kd.is_none();
|
||||||
|
let wt_face = 0.5 * (wo[wf(k + 1, j - 1, i)] + wo[wf(k + 1, j, i)]);
|
||||||
|
let wb_face = 0.5 * (wo[wf(k, j - 1, i)] + wo[wf(k, j, i)]);
|
||||||
|
let beyond_top = if b.z1 == velocity {
|
||||||
|
self.boundary((i as f64 + 0.5) * dx, j as f64 * dy, nz as f64 * dz, t_old)
|
||||||
|
.1
|
||||||
|
} else {
|
||||||
|
v_p
|
||||||
|
};
|
||||||
|
let beyond_bottom = if b.z0 == velocity {
|
||||||
|
self.boundary((i as f64 + 0.5) * dx, j as f64 * dy, 0.0, t_old)
|
||||||
|
.1
|
||||||
|
} else {
|
||||||
|
v_p
|
||||||
|
};
|
||||||
|
let v_up = ku.map(|kk| vo[vf(kk, j, i)]);
|
||||||
|
let v_dn = kd.map(|kk| vo[vf(kk, j, i)]);
|
||||||
|
let mut conv_z = (wt_face
|
||||||
|
* match v_up {
|
||||||
|
Some(vn) => Self::upwind(wt_face, v_p, vn),
|
||||||
|
None => Self::upwind(wt_face, v_p, beyond_top),
|
||||||
|
}
|
||||||
|
- wb_face
|
||||||
|
* match v_dn {
|
||||||
|
Some(vd) => Self::upwind(wb_face, vd, v_p),
|
||||||
|
None => Self::upwind(wb_face, beyond_bottom, v_p),
|
||||||
|
})
|
||||||
|
/ dz;
|
||||||
|
if scheme != ConvectionScheme::Upwind {
|
||||||
|
let far_up2 = ku
|
||||||
|
.and_then(|kk| self.k_up(kk, nz))
|
||||||
|
.map(|kk| vo[vf(kk, j, i)]);
|
||||||
|
let far_dn2 = kd
|
||||||
|
.and_then(|kk| self.k_down(kk, nz))
|
||||||
|
.map(|kk| vo[vf(kk, j, i)]);
|
||||||
|
let delta_t = if top_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if wt_face >= 0.0 {
|
||||||
|
scheme.face_correction(v_dn, v_p, v_up.unwrap_or(v_p))
|
||||||
|
} else {
|
||||||
|
scheme.face_correction(far_up2, v_up.unwrap_or(v_p), v_p)
|
||||||
|
};
|
||||||
|
let delta_b = if bottom_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if wb_face >= 0.0 {
|
||||||
|
scheme.face_correction(far_dn2, v_dn.unwrap_or(v_p), v_p)
|
||||||
|
} else {
|
||||||
|
scheme.face_correction(v_up, v_p, v_dn.unwrap_or(v_p))
|
||||||
|
};
|
||||||
|
conv_z += (wt_face * delta_t - wb_face * delta_b) / dz;
|
||||||
|
}
|
||||||
|
let flux_top = match v_up {
|
||||||
|
Some(vn) => nu * (vn - v_p) / dz,
|
||||||
|
None => {
|
||||||
|
if b.z1 == velocity {
|
||||||
|
nu * (beyond_top - v_p) / (0.5 * dz)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let flux_bottom = match v_dn {
|
||||||
|
Some(vd) => nu * (v_p - vd) / dz,
|
||||||
|
None => {
|
||||||
|
if b.z0 == velocity {
|
||||||
|
nu * (v_p - beyond_bottom) / (0.5 * dz)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let diff_z = (flux_top - flux_bottom) / dz;
|
||||||
|
|
||||||
|
rhs_2d - conv_z + diff_z
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The w face `(k, j, i)` between cells `k − 1` (wrapping when periodic)
|
||||||
|
/// and `k`: the v pattern with z as its own direction and x, y transverse.
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
|
pub(crate) fn w_rhs(
|
||||||
|
&self,
|
||||||
|
field: &FlowField3D,
|
||||||
|
k: usize,
|
||||||
|
j: usize,
|
||||||
|
i: usize,
|
||||||
|
t_old: f64,
|
||||||
|
) -> f64 {
|
||||||
|
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 nu = self.fluid.viscosity / rho;
|
||||||
|
let b = self.params.boundaries;
|
||||||
|
let velocity = SideBoundary3::Velocity;
|
||||||
|
let periodic = b.periodic_z();
|
||||||
|
let uo = &field.u_old;
|
||||||
|
let vo = &field.v_old;
|
||||||
|
let wo = &field.w_old;
|
||||||
|
let uf = |kk: usize, jj: usize, ii: usize| g.uface(kk, jj, ii);
|
||||||
|
let vf = |kk: usize, jj: usize, ii: usize| g.vface(kk, jj, ii);
|
||||||
|
let wf = |kk: usize, jj: usize, ii: usize| g.wface(kk, jj, ii);
|
||||||
|
// The cells below and above this face (k = 0 only when periodic).
|
||||||
|
let k_below = if k > 0 { k - 1 } else { nz - 1 };
|
||||||
|
let k_above = k % nz;
|
||||||
|
// Own-direction neighbours: the faces k − 1 and k + 1 (wrapping).
|
||||||
|
let w_dn_idx = if k > 0 {
|
||||||
|
wf(k - 1, j, i)
|
||||||
|
} else {
|
||||||
|
wf(nz - 1, j, i)
|
||||||
|
};
|
||||||
|
let w_up_idx = if k + 1 == nz && periodic {
|
||||||
|
wf(0, j, i)
|
||||||
|
} else {
|
||||||
|
wf(k + 1, j, i)
|
||||||
|
};
|
||||||
|
let zf = k as f64 * dz;
|
||||||
|
let w_p = wo[wf(k, j, i)];
|
||||||
|
|
||||||
|
let wt_face = 0.5 * (wo[wf(k, j, i)] + wo[w_up_idx]);
|
||||||
|
let wb_face = 0.5 * (wo[w_dn_idx] + wo[wf(k, j, i)]);
|
||||||
|
|
||||||
|
let west_is_wall = i == 0;
|
||||||
|
let east_is_wall = i + 1 == nx;
|
||||||
|
let south_is_wall = j == 0;
|
||||||
|
let north_is_wall = j + 1 == ny;
|
||||||
|
|
||||||
|
let ue_face = 0.5 * (uo[uf(k_below, j, i + 1)] + uo[uf(k_above, j, i + 1)]);
|
||||||
|
let uw_face = 0.5 * (uo[uf(k_below, j, i)] + uo[uf(k_above, j, i)]);
|
||||||
|
let vn_face = 0.5 * (vo[vf(k_below, j + 1, i)] + vo[vf(k_above, j + 1, i)]);
|
||||||
|
let vs_face = 0.5 * (vo[vf(k_below, j, i)] + vo[vf(k_above, j, i)]);
|
||||||
|
let beyond_east = if b.x1 == velocity {
|
||||||
|
self.boundary(nx as f64 * dx, (j as f64 + 0.5) * dy, zf, t_old)
|
||||||
|
.2
|
||||||
|
} else {
|
||||||
|
w_p
|
||||||
|
};
|
||||||
|
let beyond_west = if b.x0 == velocity {
|
||||||
|
self.boundary(0.0, (j as f64 + 0.5) * dy, zf, t_old).2
|
||||||
|
} else {
|
||||||
|
w_p
|
||||||
|
};
|
||||||
|
let beyond_north = if b.y1 == velocity {
|
||||||
|
self.boundary((i as f64 + 0.5) * dx, ny as f64 * dy, zf, t_old)
|
||||||
|
.2
|
||||||
|
} else {
|
||||||
|
w_p
|
||||||
|
};
|
||||||
|
let beyond_south = if b.y0 == velocity {
|
||||||
|
self.boundary((i as f64 + 0.5) * dx, 0.0, zf, t_old).2
|
||||||
|
} else {
|
||||||
|
w_p
|
||||||
|
};
|
||||||
|
|
||||||
|
let conv_z = (wt_face * Self::upwind(wt_face, w_p, wo[w_up_idx])
|
||||||
|
- wb_face * Self::upwind(wb_face, wo[w_dn_idx], w_p))
|
||||||
|
/ dz;
|
||||||
|
let conv_x = (ue_face
|
||||||
|
* if east_is_wall {
|
||||||
|
Self::upwind(ue_face, w_p, beyond_east)
|
||||||
|
} else {
|
||||||
|
Self::upwind(ue_face, w_p, wo[wf(k, j, i + 1)])
|
||||||
|
}
|
||||||
|
- uw_face
|
||||||
|
* if west_is_wall {
|
||||||
|
Self::upwind(uw_face, beyond_west, w_p)
|
||||||
|
} else {
|
||||||
|
Self::upwind(uw_face, wo[wf(k, j, i - 1)], w_p)
|
||||||
|
})
|
||||||
|
/ dx;
|
||||||
|
let conv_y = (vn_face
|
||||||
|
* if north_is_wall {
|
||||||
|
Self::upwind(vn_face, w_p, beyond_north)
|
||||||
|
} else {
|
||||||
|
Self::upwind(vn_face, w_p, wo[wf(k, j + 1, i)])
|
||||||
|
}
|
||||||
|
- vs_face
|
||||||
|
* if south_is_wall {
|
||||||
|
Self::upwind(vs_face, beyond_south, w_p)
|
||||||
|
} else {
|
||||||
|
Self::upwind(vs_face, wo[wf(k, j - 1, i)], w_p)
|
||||||
|
})
|
||||||
|
/ dy;
|
||||||
|
|
||||||
|
let scheme = self.params.convection_scheme;
|
||||||
|
let mut conv_x = conv_x;
|
||||||
|
let mut conv_y = conv_y;
|
||||||
|
let mut conv_z = conv_z;
|
||||||
|
if scheme != ConvectionScheme::Upwind {
|
||||||
|
// Own direction: far nodes two faces away (wrapping when periodic).
|
||||||
|
let far_up2 = if periodic {
|
||||||
|
Some(wo[wf((k + 2) % nz, j, i)])
|
||||||
|
} else {
|
||||||
|
(k + 2 <= nz).then(|| wo[wf(k + 2, j, i)])
|
||||||
|
};
|
||||||
|
let far_dn2 = if periodic {
|
||||||
|
Some(wo[wf((k + nz - 2) % nz, j, i)])
|
||||||
|
} else {
|
||||||
|
(k >= 2).then(|| wo[wf(k - 2, j, i)])
|
||||||
|
};
|
||||||
|
let delta_t = if wt_face >= 0.0 {
|
||||||
|
scheme.face_correction(Some(wo[w_dn_idx]), w_p, wo[w_up_idx])
|
||||||
|
} else {
|
||||||
|
scheme.face_correction(far_up2, wo[w_up_idx], w_p)
|
||||||
|
};
|
||||||
|
let delta_b = if wb_face >= 0.0 {
|
||||||
|
scheme.face_correction(far_dn2, wo[w_dn_idx], w_p)
|
||||||
|
} else {
|
||||||
|
scheme.face_correction(Some(wo[w_up_idx]), w_p, wo[w_dn_idx])
|
||||||
|
};
|
||||||
|
let delta_e = if east_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if ue_face >= 0.0 {
|
||||||
|
let far = (i >= 1).then(|| wo[wf(k, j, i - 1)]);
|
||||||
|
scheme.face_correction(far, w_p, wo[wf(k, j, i + 1)])
|
||||||
|
} else {
|
||||||
|
let far = (i + 2 < nx).then(|| wo[wf(k, j, i + 2)]);
|
||||||
|
scheme.face_correction(far, wo[wf(k, j, i + 1)], w_p)
|
||||||
|
};
|
||||||
|
let delta_w = if west_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if uw_face >= 0.0 {
|
||||||
|
let far = (i >= 2).then(|| wo[wf(k, j, i - 2)]);
|
||||||
|
scheme.face_correction(far, wo[wf(k, j, i - 1)], w_p)
|
||||||
|
} else {
|
||||||
|
let far = (i + 1 < nx).then(|| wo[wf(k, j, i + 1)]);
|
||||||
|
scheme.face_correction(far, w_p, wo[wf(k, j, i - 1)])
|
||||||
|
};
|
||||||
|
let delta_n = if north_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if vn_face >= 0.0 {
|
||||||
|
let far = (j >= 1).then(|| wo[wf(k, j - 1, i)]);
|
||||||
|
scheme.face_correction(far, w_p, wo[wf(k, j + 1, i)])
|
||||||
|
} else {
|
||||||
|
let far = (j + 2 < ny).then(|| wo[wf(k, j + 2, i)]);
|
||||||
|
scheme.face_correction(far, wo[wf(k, j + 1, i)], w_p)
|
||||||
|
};
|
||||||
|
let delta_s = if south_is_wall {
|
||||||
|
0.0
|
||||||
|
} else if vs_face >= 0.0 {
|
||||||
|
let far = (j >= 2).then(|| wo[wf(k, j - 2, i)]);
|
||||||
|
scheme.face_correction(far, wo[wf(k, j - 1, i)], w_p)
|
||||||
|
} else {
|
||||||
|
let far = (j + 1 < ny).then(|| wo[wf(k, j + 1, i)]);
|
||||||
|
scheme.face_correction(far, w_p, wo[wf(k, j - 1, i)])
|
||||||
|
};
|
||||||
|
conv_z += (wt_face * delta_t - wb_face * delta_b) / dz;
|
||||||
|
conv_x += (ue_face * delta_e - uw_face * delta_w) / dx;
|
||||||
|
conv_y += (vn_face * delta_n - vs_face * delta_s) / dy;
|
||||||
|
}
|
||||||
|
|
||||||
|
let diff_z = nu * (wo[w_up_idx] - 2.0 * w_p + wo[w_dn_idx]) / (dz * dz);
|
||||||
|
let flux_east = if east_is_wall {
|
||||||
|
if b.x1 == velocity {
|
||||||
|
nu * (beyond_east - w_p) / (0.5 * dx)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nu * (wo[wf(k, j, i + 1)] - w_p) / dx
|
||||||
|
};
|
||||||
|
let flux_west = if west_is_wall {
|
||||||
|
if b.x0 == velocity {
|
||||||
|
nu * (w_p - beyond_west) / (0.5 * dx)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nu * (w_p - wo[wf(k, j, i - 1)]) / dx
|
||||||
|
};
|
||||||
|
let diff_x = (flux_east - flux_west) / dx;
|
||||||
|
let flux_north = if north_is_wall {
|
||||||
|
if b.y1 == velocity {
|
||||||
|
nu * (beyond_north - w_p) / (0.5 * dy)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nu * (wo[wf(k, j + 1, i)] - w_p) / dy
|
||||||
|
};
|
||||||
|
let flux_south = if south_is_wall {
|
||||||
|
if b.y0 == velocity {
|
||||||
|
nu * (w_p - beyond_south) / (0.5 * dy)
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nu * (w_p - wo[wf(k, j - 1, i)]) / dy
|
||||||
|
};
|
||||||
|
let diff_y = (flux_north - flux_south) / dy;
|
||||||
|
|
||||||
|
let pressure_gradient =
|
||||||
|
-(field.p[g.cell(k_above, j, i)] - field.p[g.cell(k_below, j, i)]) / (rho * dz);
|
||||||
|
let body_force = self.momentum_source.as_ref().map_or(0.0, |f| {
|
||||||
|
f((i as f64 + 0.5) * dx, (j as f64 + 0.5) * dy, zf, t_old).2 / rho
|
||||||
|
});
|
||||||
|
|
||||||
|
-conv_x - conv_y - conv_z + diff_x + diff_y + diff_z + pressure_gradient + body_force
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user