pub struct Channel { /* private fields */ }Expand description
A rectangular box of incompressible fluid.
§The grid
Velocities on cell faces and pressure at cell centres. u sits on the x faces, v on the
y faces and w on the z faces, so a divergence lands at a cell centre and a pressure
gradient lands on a face, with no interpolation in either.
x and z are always periodic. y is periodic or walled, by Walls.
Implementations§
Source§impl Channel
impl Channel
Sourcepub fn new(
name: impl Into<String>,
counts: (usize, usize, usize),
cell: Length,
fluid: Fluid,
walls: Walls,
) -> Channel
pub fn new( name: impl Into<String>, counts: (usize, usize, usize), cell: Length, fluid: Fluid, walls: Walls, ) -> Channel
A box of counts cubic cells of side cell, at rest.
Sourcepub fn drive(&mut self, force: DVec3) -> &mut Channel
pub fn drive(&mut self, force: DVec3) -> &mut Channel
Drive the flow with a uniform body force per unit mass, in m/s².
A pressure gradient in disguise, and the form every closed form for channel flow is written in: a periodic box cannot carry a mean pressure gradient, so the drive has to be a force.
Sourcepub fn set_velocity(&mut self, field: impl Fn(DVec3) -> DVec3) -> &mut Channel
pub fn set_velocity(&mut self, field: impl Fn(DVec3) -> DVec3) -> &mut Channel
Set every u face from a function of position, for releasing an exact solution.
The callback is handed the face’s own centre. v and w are set the same way by
Channel::set_velocity, which takes all three at once and samples each component where
that component lives — the staggering is the whole point and a function evaluated at one
place for all three would be a different field.
Sourcepub fn velocity_at(&self, i: usize, j: usize, k: usize) -> DVec3
pub fn velocity_at(&self, i: usize, j: usize, k: usize) -> DVec3
The velocity at a cell centre, by averaging the faces around it.
Sourcepub fn mean_speed(&self) -> Velocity
pub fn mean_speed(&self) -> Velocity
The mean x velocity of the whole box.
Sourcepub fn layer_speed(&self, j: usize) -> Velocity
pub fn layer_speed(&self, j: usize) -> Velocity
The mean x velocity of one layer of cells, which is what a profile is made of.
Sourcepub fn kinetic_energy(&self) -> Energy
pub fn kinetic_energy(&self) -> Energy
Kinetic energy, ½∫ρ|u|²dV, from the face values.
Sourcepub fn momentum_x(&self) -> f64
pub fn momentum_x(&self) -> f64
Total x momentum, ρ∫u dV.
Conserved exactly in a periodic box with no force and no walls: the advection is in flux form, so every face’s contribution appears twice with opposite signs, and the pressure gradient of a periodic field sums to zero. That is a machine-precision statement and it is what a decay rate is too coarse to check.
Sourcepub fn divergence(&self) -> f64
pub fn divergence(&self) -> f64
The largest |∇·u| anywhere, times the cell — a velocity, so it compares to the flow.
After the projection this is the pressure solve’s residual and nothing else. Weaker than electromagnetism’s divergence identity, which holds exactly; here it holds to whatever the solve was asked for, and reporting it is the difference between knowing that and assuming it.
Sourcepub fn cell_reynolds(&self) -> f64
pub fn cell_reynolds(&self) -> f64
The cell Reynolds number, |u|dx/ν.
A property of the mesh and the flow in it, not of the step. See CELL_REYNOLDS_LIMIT.
Sourcepub fn peak_speed(&self) -> f64
pub fn peak_speed(&self) -> f64
The fastest face velocity anywhere.
Sourcepub fn viscous_limit(&self) -> Time
pub fn viscous_limit(&self) -> Time
The viscous limit, dx²/(6ν) — the same Fourier number conduction has.
Sourcepub fn courant_limit(&self) -> Time
pub fn courant_limit(&self) -> Time
The advective limit, dx/|u|max. Infinite for a fluid at rest.
Trait Implementations§
Source§impl Domain for Channel
impl Domain for Channel
Source§fn ledger(&self) -> Ledger
fn ledger(&self) -> Ledger
The kinetic energy the box is holding. The kinetic energy it holds, less the work the drive has put in.
Two contributions rather than their difference: Ledger::add raises an entry’s scale to
the largest thing added to it and the audit judges a change against that, so pre-summing a
near-zero net would leave no scale at all. A channel starting from rest holds exactly zero,
and the first 2.9e-12 J the drive did was judged a hundred-percent change and stopped a
correct run on its first step.
Source§fn as_field(&self) -> Option<&dyn ScalarField>
fn as_field(&self) -> Option<&dyn ScalarField>
Speed, so a flow can be looked at.
The honest caveat is in the unit and in this sentence rather than in a refusal to draw one:
velocity is a vector and this is its magnitude, so a picture of it shows where the fluid
is moving fast and not which way it is going. The components are in the JSON, and
layer_speed is what a profile should be read from.
Drawn at all because a domain nobody can see is a domain nobody trusts, and this crate’s own documentation says why that matters here more than anywhere: “it looks like a fluid” is the easiest wrong answer in computational physics to accept, and the answer to that is closed forms and a picture, not one instead of the other.
Source§fn as_any_mut(&mut self) -> Option<&mut dyn Any>
fn as_any_mut(&mut self) -> Option<&mut dyn Any>
And mutably, because a domain that can be read and not written is one a coupling can
only fail at silently. Simulation::domain_as_mut returns None when this is not
implemented, and a caller that wrote through it would do nothing and report nothing —
which is how a whole coupled body came back reporting zero strain that read as no stress
rather than as not connected.
Source§fn name(&self) -> &str
fn name(&self) -> &str
Source§fn kind(&self) -> Kind
fn kind(&self) -> Kind
Kind::Evolving.Source§fn max_stable_dt(&self, _now: Time) -> Time
fn max_stable_dt(&self, _now: Time) -> Time
now and stay stable — a CFL
condition, a diffusion limit, a contact penetration budget. Read moreSource§fn step(
&mut self,
_t: Time,
dt: Time,
_bus: &mut Exchange,
) -> Result<(), Violation>
fn step( &mut self, _t: Time, dt: Time, _bus: &mut Exchange, ) -> Result<(), Violation>
dt from t, reading inputs from bus and publishing outputs
to it. A quasi-static domain ignores dt. Read moreSource§fn readings(&self) -> Vec<Reading>
fn readings(&self) -> Vec<Reading>
Source§fn as_any(&self) -> Option<&dyn Any>
fn as_any(&self) -> Option<&dyn Any>
Any, so a caller can get the concrete type back out of a
Simulation — see Simulation::domain_as. Read moreSource§fn checkpoint(&mut self)
fn checkpoint(&mut self)
Schedule::Iterative, and Simulation::advance says so rather than
silently iterating from the wrong state.Source§fn restore(&mut self)
fn restore(&mut self)
Domain::checkpoint.Source§fn residual(&self) -> f64
fn residual(&self) -> f64
Schedule::Iterative. Zero means converged.Source§fn books_balance(&self) -> bool
fn books_balance(&self) -> bool
Source§fn supports_restore(&self) -> bool
fn supports_restore(&self) -> bool
Source§impl ScalarField for Channel
impl ScalarField for Channel
Source§fn unit(&self) -> &'static str
fn unit(&self) -> &'static str
Metres per second — a speed, not a velocity. See Channel::as_field.
Source§fn at(&self, p: LengthVec, _t: Time) -> f64
fn at(&self, p: LengthVec, _t: Time) -> f64
The speed at p, from the cell it falls in.
Nearest cell rather than trilinear, and the reason is the grid: velocity lives on the faces
and velocity_at already averages the pair across each cell to get
a centred vector. Interpolating that average again would smooth a field that has already
been smoothed once, and a viewer would be looking at a blur of a blur.
Source§fn gradient(
&self,
p: QVec3<1, 0, 0, 0, 0, 0, 0>,
t: Qty<0, 0, 1, 0, 0, 0, 0>,
h: Qty<1, 0, 0, 0, 0, 0, 0>,
) -> DVec3
fn gradient( &self, p: QVec3<1, 0, 0, 0, 0, 0, 0>, t: Qty<0, 0, 1, 0, 0, 0, 0>, h: Qty<1, 0, 0, 0, 0, 0, 0>, ) -> DVec3
h. Units are the field’s per metre.