pub struct FvMesh {Show 13 fields
pub n_cells: usize,
pub n_internal_faces: usize,
pub n_faces: usize,
pub owner: Vec<usize>,
pub neighbour: Vec<usize>,
pub patches: Vec<BoundaryPatch>,
pub cyclic_couplings: Vec<CyclicCoupling>,
pub ami_couplings: Vec<AmiCoupling>,
pub cell_volumes: Vec<f64>,
pub cell_centres: Vec<Vector3>,
pub face_area_vectors: Vec<Vector3>,
pub face_areas: Vec<f64>,
pub face_centres: Vec<Vector3>,
}Expand description
Finite-volume mesh — topology and geometry in a flat data structure.
Mirrors Foam::fvMesh (src/finiteVolume/fvMesh/fvMesh.H) but without
the C++ inheritance chain (polyMesh → primitiveMesh → lduMesh).
Only the data required by the FV operators is stored.
§Face ordering (OpenFOAM convention)
[0 .. n_internal_faces) ← internal faces (have both owner & neighbour)
[n_internal_faces .. n_faces) ← boundary faces (owner only)The neighbour array has length n_internal_faces; boundary faces have no
entry in neighbour.
§Getting one
This was a documented friction point in an API dogfood run (gh #58): the
prelude exports FvMesh, but nothing told the reader how to obtain one,
and they ended up reading the test suite to find out. The ready-made
constructors are listed here so that is not necessary:
FvMesh::periodic_1d— a 1-D periodic column ofncells. The usual choice for verifying an operator against an analytic solution, since periodicity removes boundary treatment from the comparison.FvMesh::periodic_ring_ami— a periodic ring with a non-conformal AMI interface, for exercising the AMI paths.FvMeshBuilder— build one face by face when neither fits.
Meshes are shared by the field types via Arc, so wrap it once:
use outram_foam_basic_lib::prelude::*;
use std::sync::Arc;
// 16 cells over a unit length, unit cross-sectional area.
let mesh = Arc::new(FvMesh::periodic_1d(16, 1.0, 1.0));
assert_eq!(mesh.n_cells, 16); // a public field, not a method
// A field is then built on that mesh.
let phi = VolScalarField::uniform("phi", mesh.clone(), 0.0);
assert_eq!(phi.internal.len(), 16);Fields§
§n_cells: usizeNumber of cells.
n_internal_faces: usizeNumber of internal faces (both owner and neighbour defined).
n_faces: usizeTotal number of faces (internal + boundary).
owner: Vec<usize>owner[f] — cell that owns face f (for all faces).
neighbour: Vec<usize>neighbour[f] — cell on the other side of internal face f.
Length == n_internal_faces; boundary faces have no neighbour.
patches: Vec<BoundaryPatch>Boundary patch descriptors (one per patch, in face-index order).
cyclic_couplings: Vec<CyclicCoupling>Across-seam cell couplings from PatchKind::Cyclic (periodic) patch
pairs, one entry per matched boundary-face pair. Empty for a mesh with no
(resolved) cyclic pairs. Each entry is treated by the FV operators and
the LDU matrix exactly like an internal face joining
CyclicCoupling::owner and CyclicCoupling::neighbour, appended to
the LDU face addressing after the n_internal_faces internal faces.
ami_couplings: Vec<AmiCoupling>Across-seam couplings from PatchKind::CyclicAmi (non-conformal
periodic) patch pairs, one entry per target seam face. Empty for a
mesh with no AMI pairs. Each entry couples its target cell to a weighted
set of source cells (the geometric face overlaps); the FV operators and
the LDU matrix append one LDU face per AmiWeight
after the internal faces and the cyclic_couplings
(see ami_ldu_start). Mirrors OpenFOAM
cyclicAMIFvPatchField.
cell_volumes: Vec<f64>Cell volumes V[c] [m³].
cell_centres: Vec<Vector3>Cell centres C[c] [m].
face_area_vectors: Vec<Vector3>Face area vectors Sf[f] [m²], pointing from owner toward neighbour
(or outward for boundary faces).
face_areas: Vec<f64>Face area magnitudes |Sf[f]| [m²].
face_centres: Vec<Vector3>Face centres Cf[f] [m].
Implementations§
Source§impl FvMesh
impl FvMesh
Sourcepub fn ami_ldu_start(&self) -> usize
pub fn ami_ldu_start(&self) -> usize
First LDU face index occupied by AMI seam couplings.
AMI partial-seam faces live after the internal faces and the
CyclicCouplings, so the AMI block starts
at n_internal_faces + cyclic_couplings.len(). Operators and
FvMatrix::new both iterate the AMI
couplings in order from this base, one LDU face per AmiWeight, so the
addressing lines up.
Sourcepub fn n_ami_faces(&self) -> usize
pub fn n_ami_faces(&self) -> usize
Total number of AMI partial-seam LDU faces — the sum of each AMI target face’s number of weighted source contributions.
Sourcepub fn periodic_ring_ami(
n_a: usize,
n_b: usize,
lx: f64,
ly: f64,
depth: f64,
) -> FvMesh
pub fn periodic_ring_ami( n_a: usize, n_b: usize, lx: f64, ly: f64, depth: f64, ) -> FvMesh
Build a non-conformal periodic ring with two cyclicAMI seams,
programmatically (no polyMesh parser).
The mesh is a closed loop of two cell columns, periodic in x, whose two column-joining interfaces are both AMI seams so the two halves may be subdivided differently in the transverse (y) direction — the defining non-conformality AMI exists to handle:
wrap seam (x = 0 ≡ x = lx, period +lx·x̂)
┌──────────────────────────────────────────────┐
│ │
A column (n_a cells in y) B column (n_b cells in y)
│ x ∈ [0, lx/2] x ∈ [lx/2, lx] │
└────────── mid seam (x = lx/2) ────────────────┘- A column —
n_acells stacked iny ∈ [0, ly], centres atx = lx/4; each cell owns a mid-seam face (x = lx/2, target of the mid AMI) and a wrap-seam face (x = 0, source of the wrap AMI). - B column —
n_bcells stacked iny ∈ [0, ly], centres atx = 3lx/4; each cell owns a mid-seam face (x = lx/2, source of the mid AMI) and a wrap-seam face (x = lx, target of the wrap AMI).
There are no internal faces: every coupling is through one of the two
AMI seams. Each cell has equal in-/out-seam face area, so a uniform field
advects around the loop conservatively (A·1 = 0), and the diffusion
stencil is the periodic circulant one. Cell-to-cell distance across either
seam is lx/2 (a quarter-width from each owner to the seam plane on both
sides), matching an interior face of that spacing.
When n_a == n_b and the two halves align (equal transverse
tiling), every AMI weight is 1 and the couplings reduce to the plain
CyclicCoupling one-to-one seam — the
mesh then decomposes into n_a independent 2-cell periodic loops, each
identical to periodic_1d(2, lx, (ly/n_a)·depth)
(the matching-mesh limit used in this crate’s AMI V&V).
§Parameters
n_a— transverse cell count of the A (mid-target / wrap-source) column (≥ 1);n_b— transverse cell count of the B (mid-source / wrap-target) column (≥ 1);lx— periodic (x) domain length[m](> 0);ly— transverse (y) domain extent[m](> 0);depth— out-of-plane (z) face depth[m](> 0).
Cells are ordered A_0 … A_{n_a-1}, B_0 … B_{n_b-1}. Patches are, in
order: "A_right" (mid target), "B_left" (mid source), "B_right"
(wrap target), "A_left" (wrap source), all PatchKind::CyclicAmi.
§Panics
Panics if any of n_a, n_b is 0 or any of lx, ly, depth is not
positive.
OpenFOAM analogue: a blockMesh with a cyclicAMI patch pair on two
non-conformal column interfaces.
Source§impl FvMesh
impl FvMesh
Sourcepub fn n_boundary_faces(&self) -> usize
pub fn n_boundary_faces(&self) -> usize
Total number of boundary faces.
Sourcepub fn is_internal_face(&self, f: usize) -> bool
pub fn is_internal_face(&self, f: usize) -> bool
True if face f is an internal face (has a neighbour cell).
Sourcepub fn patch_for_face(&self, f: usize) -> Option<(usize, usize)>
pub fn patch_for_face(&self, f: usize) -> Option<(usize, usize)>
Given a global face index f that is a boundary face, return
(patch_index, local_face_index_within_patch).
Returns None if f is an internal face.
Sourcepub fn n_cyclic_couplings(&self) -> usize
pub fn n_cyclic_couplings(&self) -> usize
Number of across-seam cyclic couplings (length of
cyclic_couplings).
Sourcepub fn cyclic_coupling_face(&self, i: usize) -> usize
pub fn cyclic_coupling_face(&self, i: usize) -> usize
LDU face index of cyclic coupling i.
Cyclic couplings live after the internal faces in the LDU addressing,
so coupling i occupies face n_internal_faces + i. The FV operators
use this to write the off-diagonal coefficient of a periodic seam into
the matrix.
Sourcepub fn cyclic_partner_face(&self, global_face: usize) -> Option<usize>
pub fn cyclic_partner_face(&self, global_face: usize) -> Option<usize>
Given a global boundary face index on a resolved PatchKind::Cyclic
patch, return the global face index of its matching partner face across
the periodic seam.
Returns None for internal faces, for non-cyclic boundary faces, and for
cyclic faces whose partner patch is unresolved. Local face i of a
cyclic patch maps to local face i of its partner (OpenFOAM half0/half1
ordering).
Sourcepub fn periodic_1d(n: usize, length: f64, area: f64) -> FvMesh
pub fn periodic_1d(n: usize, length: f64, area: f64) -> FvMesh
Build a uniform 1-D periodic (cyclic) mesh: n equal cells along the
x-axis over [0, length], with the two ends joined by a cyclic patch
pair so the domain wraps around.
This is the programmatic constructor for a periodic seam (the first-pass
path — the polyMesh parser does not yet read cyclic neighbourPatch
ordering). The resulting mesh has:
ncells of widthh = length / n, centres at(i + 0.5)·h, volumeh·area[m³];n − 1internal faces (area vector+x·area[m²]);- two 1-face cyclic patches —
"left"atx = 0(patch 0, outward normal−x) and"right"atx = length(patch 1, outward normal+x) — that are each other’s partners.
The derived CyclicCoupling joins cell 0 (left owner) to cell n − 1
(right owner) with the same cell-to-cell distance h as an interior
face, so a periodic problem discretised on this mesh reproduces the
equivalent all-internal ring mesh (see the crate’s cyclic V&V tests).
§Parameters
n— number of cells (must be ≥ 2 for a meaningful periodic loop);length— domain length[m];area— uniform cross-sectional face area[m²].
§Panics
Panics if n < 2 (a periodic loop needs at least two cells) or if
length/area are not positive.
OpenFOAM analogue: a blockMesh block with a cyclic patch pair on its
two x-normal faces.