outram_foam_basic_lib/fv_operators/
adjust_phi.rs1use crate::fields::boundary::bc::BoundaryCondition;
23use crate::fields::surface_field::SurfaceScalarField;
24use crate::fields::vol_field::VolVectorField;
25
26pub fn adjust_phi(phi: &mut SurfaceScalarField, u: &VolVectorField) -> bool {
40 let mesh = &phi.mesh;
41
42 let mut total_flux = 0.0_f64;
44 let mut adjustable_n = 0usize;
45
46 for (pi, patch) in mesh.patches.iter().enumerate() {
47 for fi in 0..patch.size {
48 total_flux += phi.boundary[pi].values[fi];
49 if matches!(u.boundary[pi].bc, BoundaryCondition::ZeroGradient) {
50 adjustable_n += 1;
51 }
52 }
53 }
54
55 if adjustable_n == 0 || total_flux.abs() < 1e-30 {
56 return false;
57 }
58
59 let correction = -total_flux / adjustable_n as f64;
61 for (pi, patch) in mesh.patches.iter().enumerate() {
62 if matches!(u.boundary[pi].bc, BoundaryCondition::ZeroGradient) {
63 for fi in 0..patch.size {
64 phi.boundary[pi].values[fi] += correction;
65 }
66 }
67 }
68 true
69}
70
71#[cfg(test)]
72mod tests {
73 use super::*;
74 use crate::fields::boundary::bc::{BoundaryCondition, PatchField};
75 use crate::fields::field::Field;
76 use crate::fields::vol_field::VolVectorField;
77 use crate::mesh::fv_mesh::{BoundaryPatch, FvMeshBuilder, PatchKind};
78 use crate::primitives::Vector3;
79 use std::sync::Arc;
80
81 fn two_cell_mesh() -> Arc<crate::mesh::fv_mesh::FvMesh> {
82 Arc::new(
83 FvMeshBuilder::new()
84 .n_cells(2)
85 .n_internal_faces(1)
86 .owner(vec![0, 1, 0])
87 .neighbour(vec![1])
88 .patches(vec![
89 BoundaryPatch::new("right", 1, 1, PatchKind::Wall),
90 BoundaryPatch::new("left", 2, 1, PatchKind::Wall),
91 ])
92 .cell_volumes(vec![1.0, 1.0])
93 .cell_centres(vec![
94 Vector3::new(0.25, 0.0, 0.0),
95 Vector3::new(0.75, 0.0, 0.0),
96 ])
97 .face_area_vectors(vec![
98 Vector3::new(1.0, 0.0, 0.0),
99 Vector3::new(1.0, 0.0, 0.0),
100 Vector3::new(-1.0, 0.0, 0.0),
101 ])
102 .face_centres(vec![
103 Vector3::new(0.5, 0.0, 0.0),
104 Vector3::new(1.0, 0.0, 0.0),
105 Vector3::new(0.0, 0.0, 0.0),
106 ])
107 .build()
108 .unwrap(),
109 )
110 }
111
112 #[test]
113 fn balances_net_outflow() {
114 let m = two_cell_mesh();
115 let bnd = vec![
117 PatchField {
118 bc: BoundaryCondition::ZeroGradient,
119 values: Field::new(vec![0.5]),
120 },
121 PatchField {
122 bc: BoundaryCondition::ZeroGradient,
123 values: Field::new(vec![-1.0]),
124 },
125 ];
126 let u_bnd = vec![
127 crate::fields::boundary::bc::PatchField {
128 bc: BoundaryCondition::ZeroGradient,
129 values: Field::new(vec![Vector3::ZERO]),
130 },
131 crate::fields::boundary::bc::PatchField {
132 bc: BoundaryCondition::FixedValue(Vector3::new(-1.0, 0.0, 0.0)),
133 values: Field::new(vec![Vector3::new(-1.0, 0.0, 0.0)]),
134 },
135 ];
136 let u = VolVectorField::new("U", m.clone(), Field::new(vec![Vector3::ZERO; 2]), u_bnd);
137 let mut phi = SurfaceScalarField::new("phi", m.clone(), Field::new(vec![0.0]), bnd);
138
139 let adjusted = adjust_phi(&mut phi, &u);
140 assert!(adjusted);
141 let net: f64 = phi
143 .boundary
144 .iter()
145 .flat_map(|p| p.values.iter().cloned())
146 .sum();
147 assert!(net.abs() < 1e-12, "net flux = {net}");
148 }
149
150 #[test]
151 fn no_adjustment_when_balanced() {
152 let m = two_cell_mesh();
153 let bnd = vec![
154 PatchField {
155 bc: BoundaryCondition::ZeroGradient,
156 values: Field::new(vec![1.0]),
157 },
158 PatchField {
159 bc: BoundaryCondition::ZeroGradient,
160 values: Field::new(vec![-1.0]),
161 },
162 ];
163 let u_bnd = vec![
164 PatchField {
165 bc: BoundaryCondition::ZeroGradient,
166 values: Field::new(vec![Vector3::ZERO]),
167 },
168 PatchField {
169 bc: BoundaryCondition::ZeroGradient,
170 values: Field::new(vec![Vector3::ZERO]),
171 },
172 ];
173 let u = VolVectorField::new("U", m.clone(), Field::new(vec![Vector3::ZERO; 2]), u_bnd);
174 let mut phi = SurfaceScalarField::new("phi", m.clone(), Field::new(vec![0.0]), bnd);
175 let adjusted = adjust_phi(&mut phi, &u);
176 assert!(!adjusted);
177 }
178}