pub struct FvMatrix {
pub mesh: Arc<FvMesh>,
pub ldu: LduMatrix,
pub source: Field<f64>,
}Expand description
Sparse implicit matrix equation A·φ = b for a scalar field φ.
Mirrors Foam::fvMatrix<scalar> from
src/finiteVolume/fvMatrices/fvMatrix/fvMatrix.H.
Assembled incrementally by fvm:: operators in Layer 3; solved via
self.solve().
Fields§
§mesh: Arc<FvMesh>Mesh the equation is defined on (shares the face addressing).
ldu: LduMatrixSparse LDU coefficients of the operator A.
source: Field<f64>Right-hand-side source term, length n_cells.
Implementations§
Source§impl FvMatrix
impl FvMatrix
Sourcepub fn new(mesh: Arc<FvMesh>) -> Self
pub fn new(mesh: Arc<FvMesh>) -> Self
Create a new zero-initialised FvMatrix for the given mesh.
The LDU face addressing holds the mesh’s internal faces, then one slot
per CyclicCoupling (conformal periodic
seam), then one slot per AmiWeight of each
AmiCoupling (non-conformal periodic / AMI
seam, in ami_couplings order — see
FvMesh::ami_ldu_start). Every
matrix built on a given mesh therefore shares one structure — a
prerequisite for the coefficient-wise matrix arithmetic (+/−).
Operators that ignore seams (e.g. fvm::ddt) simply leave those slots
zero; fvm::laplacian/fvm::div fill them via the paired-cell coupling.
Sourcepub fn solve(
&self,
name: impl Into<String>,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve( &self, name: impl Into<String>, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve A·φ = source and return the solution as a VolScalarField.
Uses Gauss-Seidel as the default solver. Layer 3 will add conjugate gradient / GAMG when those are ported.
Sourcepub fn solve_cg(
&self,
name: impl Into<String>,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_cg( &self, name: impl Into<String>, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve the system with preconditioned conjugate gradient (cold start).
PCG requires a symmetric SPD matrix (upper == lower), which holds for
the pressure Poisson equation assembled by fvm::laplacian. It converges
in O(√κ) iterations versus O(κ) for Gauss-Seidel — dramatically faster
for the elliptic pressure solve. Do not use it for the asymmetric
convection-bearing momentum matrix.
This starts the iteration from x = 0. In a transient solver, prefer
solve_cg_with_guess to warm-start from the
previous time step’s field — near steady state that converges in a
handful of iterations instead of from scratch every step.
Sourcepub fn solve_cg_with_guess(
&self,
name: impl Into<String>,
initial: &VolScalarField,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_cg_with_guess( &self, name: impl Into<String>, initial: &VolScalarField, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve with PCG, warm-started from initial (typically the previous
time step’s field).
The DIC-preconditioned CG begins from initial.internal instead of zero.
For a transient run approaching steady state the solution barely changes
between steps, so the starting residual is tiny and the solve completes
in very few iterations — often zero. See
conjugate_gradient for the
preconditioner and warm-start details.
Sourcepub fn solve_gamg(
&self,
name: impl Into<String>,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_gamg( &self, name: impl Into<String>, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve the system with GAMG (algebraic multigrid), cold-started from
x = 0.
GAMG requires a symmetric SPD matrix (upper == lower) — the pressure
Poisson case. It is near mesh-independent (a handful of V-cycles), unlike
PCG whose iteration count grows with the mesh, so it is the better choice
for the pressure solve on fine grids. As with PCG, prefer
solve_gamg_with_guess in a transient
loop to warm-start from the previous time step’s field.
Sourcepub fn solve_gamg_with_guess(
&self,
name: impl Into<String>,
initial: &VolScalarField,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_gamg_with_guess( &self, name: impl Into<String>, initial: &VolScalarField, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve with GAMG, warm-started from initial (typically the previous
time step’s field). See gamg for the
multigrid algorithm and solve_gamg for the cold
variant.
Sourcepub fn solve_bicgstab(
&self,
name: impl Into<String>,
options: KrylovOptions,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_bicgstab( &self, name: impl Into<String>, options: KrylovOptions, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve the system with preconditioned BiCGStab, cold-started from
x = 0.
This is the solver to use for any equation carrying a convection term —
scalar transport, energy, species — where the matrix is asymmetric
(lower[f] != upper[f]) because upwinding puts the flux on the donor side
only. PCG (solve_cg) and GAMG
(solve_gamg) require symmetry and must not be used
on those matrices; the only prior alternative was
solve’s Gauss-Seidel, whose iteration count grows like
the condition number.
With the default KrylovOptions this is ILU(0)-preconditioned
BiCGStab — the direct analogue of OpenFOAM’s PBiCGStab with DILU.
settings.tolerance is tested against the relative 2-norm residual
||b − A·phi||₂ / ||b||₂ (the same measure solve_cg uses), not the
L1-scaled measure solve reports.
In a transient loop prefer
solve_bicgstab_with_guess to
warm-start from the previous time step.
Sourcepub fn solve_bicgstab_with_guess(
&self,
name: impl Into<String>,
initial: &VolScalarField,
options: KrylovOptions,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_bicgstab_with_guess( &self, name: impl Into<String>, initial: &VolScalarField, options: KrylovOptions, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve with preconditioned BiCGStab, warm-started from initial
(typically the previous time step’s field).
See solve_bicgstab for when to use BiCGStab at
all. Near steady state the starting residual is already tiny, so the
solve completes in a handful of iterations — often zero.
Sourcepub fn solve_gmres(
&self,
name: impl Into<String>,
options: KrylovOptions,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_gmres( &self, name: impl Into<String>, options: KrylovOptions, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve the system with restarted, right-preconditioned GMRES(m),
cold-started from x = 0.
GMRES minimises the residual over the Krylov subspace, so its residual
history is monotone and it cannot break down the way BiCGStab can — at
the cost of storing options.restart basis vectors
(O(restart · n_cells) memory). Reach for it when
solve_bicgstab reports converged = false on a
strongly nonnormal (high-Peclet, highly non-orthogonal) matrix.
Like solve_bicgstab, settings.tolerance is
tested against ||b − A·phi||₂ / ||b||₂.
Sourcepub fn solve_gmres_with_guess(
&self,
name: impl Into<String>,
initial: &VolScalarField,
options: KrylovOptions,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_gmres_with_guess( &self, name: impl Into<String>, initial: &VolScalarField, options: KrylovOptions, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve with restarted GMRES(m), warm-started from initial.
See solve_gmres.
Sourcepub fn solve_krylov(
&self,
name: impl Into<String>,
initial: Option<&VolScalarField>,
method: KrylovMethod,
options: KrylovOptions,
settings: SolverSettings,
) -> (VolScalarField, SolverPerformance)
pub fn solve_krylov( &self, name: impl Into<String>, initial: Option<&VolScalarField>, method: KrylovMethod, options: KrylovOptions, settings: SolverSettings, ) -> (VolScalarField, SolverPerformance)
Solve with the Krylov method named by method, optionally warm-started.
The general form behind solve_bicgstab and
solve_gmres; use it when the method is chosen at
run time (e.g. fall back from BiCGStab to GMRES after a breakdown)
instead of being fixed at the call site.
initial is the starting iterate; None starts from zero.
Sourcepub fn add_to_diag(&mut self, coeff: &Field<f64>)
pub fn add_to_diag(&mut self, coeff: &Field<f64>)
Add coeff * I to the diagonal (e.g. from a time derivative term).
Sourcepub fn add_to_source(&mut self, term: &Field<f64>)
pub fn add_to_source(&mut self, term: &Field<f64>)
Add coeff[c] to the source at cell c.
Sourcepub fn add_face_coeff(&mut self, face: usize, coeff: f64)
pub fn add_face_coeff(&mut self, face: usize, coeff: f64)
Add upper/lower contributions from a face (used by fvm::laplacian etc.).
Sourcepub fn set_reference(&mut self, cell: usize, value: f64)
pub fn set_reference(&mut self, cell: usize, value: f64)
Pin one cell to a reference value — fixes the singular pressure matrix in a closed domain (no fixed-pressure BCs).
Adds a large coefficient to diag[cell] and a matching term to
source[cell] so that the solution is forced toward value.
Sourcepub fn a_field(&self) -> VolScalarField
pub fn a_field(&self) -> VolScalarField
Diagonal coefficient per cell: A[c] = diag[c].
Used in PISO: rAU = 1 / pEqn.a_field().
Sourcepub fn h_field(&self, x: &VolScalarField) -> VolScalarField
pub fn h_field(&self, x: &VolScalarField) -> VolScalarField
Off-diagonal residual: H[c] = source[c] − Σ off-diag · x.
For a zero initial guess x = 0 this is just source.
Used in pressure correction: H = pEqn.h_field(p).
Trait Implementations§
Source§impl AddAssign for FvMatrix
impl AddAssign for FvMatrix
Source§fn add_assign(&mut self, rhs: Self)
fn add_assign(&mut self, rhs: Self)
+= operation. Read more