outram_foam_basic_lib/interface/
one_dimensional_meshing.rs1use crate::mesh::{BoundaryPatch, FvMesh, FvMeshBuilder, MeshError, PatchKind};
2use crate::primitives::Vector3;
3use uom::si::area::square_meter;
4use uom::si::f64::*;
5use uom::si::length::meter;
6
7pub fn create_one_d_mesh(
55 l: Length,
56 xs_area: Area,
57 number_of_cells: i64,
58) -> Result<FvMesh, MeshError> {
59 if number_of_cells < 1 {
60 return Err(MeshError::NonPositiveCellCount {
61 got: number_of_cells,
62 });
63 }
64 let n = number_of_cells as usize;
65 let l_m = l.get::<meter>();
66 let a_m2 = xs_area.get::<square_meter>();
67 let dx = l_m / n as f64;
68
69 let n_internal = n - 1;
75 let n_faces = n + 1;
76
77 let mut owner = Vec::with_capacity(n_faces);
78 let mut neighbour = Vec::with_capacity(n_internal);
79 for i in 0..n_internal {
80 owner.push(i);
81 neighbour.push(i + 1);
82 }
83 owner.push(n - 1); owner.push(0); let cell_volumes: Vec<f64> = vec![dx * a_m2; n];
88 let cell_centres: Vec<Vector3> = (0..n)
89 .map(|i| Vector3::new((i as f64 + 0.5) * dx, 0.0, 0.0))
90 .collect();
91
92 let mut face_centres = Vec::with_capacity(n_faces);
93 let mut face_area_vectors = Vec::with_capacity(n_faces);
94
95 for i in 0..n_internal {
97 face_centres.push(Vector3::new((i as f64 + 1.0) * dx, 0.0, 0.0));
98 face_area_vectors.push(Vector3::new(a_m2, 0.0, 0.0));
99 }
100 face_centres.push(Vector3::new(l_m, 0.0, 0.0));
102 face_area_vectors.push(Vector3::new(a_m2, 0.0, 0.0));
103 face_centres.push(Vector3::new(0.0, 0.0, 0.0));
105 face_area_vectors.push(Vector3::new(-a_m2, 0.0, 0.0));
106
107 let patches = vec![
110 BoundaryPatch::new("right", n_internal, 1, PatchKind::Patch),
111 BoundaryPatch::new("left", n_internal + 1, 1, PatchKind::Patch),
112 ];
113
114 FvMeshBuilder::new()
115 .n_cells(n)
116 .n_internal_faces(n_internal)
117 .owner(owner)
118 .neighbour(neighbour)
119 .patches(patches)
120 .cell_volumes(cell_volumes)
121 .cell_centres(cell_centres)
122 .face_area_vectors(face_area_vectors)
123 .face_centres(face_centres)
124 .build()
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use uom::si::area::square_meter;
131 use uom::si::length::meter;
132
133 #[test]
134 fn single_cell_mesh_is_valid() {
135 let mesh = create_one_d_mesh(
136 Length::new::<meter>(1.0),
137 Area::new::<square_meter>(0.01),
138 1,
139 )
140 .unwrap();
141 assert_eq!(mesh.n_cells, 1);
142 assert_eq!(mesh.n_internal_faces, 0);
143 assert_eq!(mesh.n_faces, 2);
144 assert_eq!(mesh.neighbour.len(), 0);
145 mesh.validate().unwrap();
146 }
147
148 #[test]
149 fn ten_cell_mesh_topology() {
150 let mesh = create_one_d_mesh(
151 Length::new::<meter>(1.0),
152 Area::new::<square_meter>(0.05),
153 10,
154 )
155 .unwrap();
156 assert_eq!(mesh.n_cells, 10);
157 assert_eq!(mesh.n_internal_faces, 9);
158 assert_eq!(mesh.n_faces, 11);
159 assert_eq!(mesh.owner.len(), 11);
160 assert_eq!(mesh.neighbour.len(), 9);
161 mesh.validate().unwrap();
162 }
163
164 #[test]
165 fn cell_volumes_are_uniform() {
166 let l = 2.0_f64;
167 let a = 0.03_f64;
168 let n = 5_i64;
169 let mesh =
170 create_one_d_mesh(Length::new::<meter>(l), Area::new::<square_meter>(a), n).unwrap();
171 let expected = l * a / n as f64;
172 for &v in &mesh.cell_volumes {
173 assert!((v - expected).abs() < 1e-14, "volume {v} != {expected}");
174 }
175 }
176
177 #[test]
178 fn cell_centres_are_midpoints() {
179 let n = 4_i64;
180 let dx = 1.0 / n as f64;
181 let mesh = create_one_d_mesh(Length::new::<meter>(1.0), Area::new::<square_meter>(1.0), n)
182 .unwrap();
183 for (i, c) in mesh.cell_centres.iter().enumerate() {
184 let expected_x = (i as f64 + 0.5) * dx;
185 assert!((c.x - expected_x).abs() < 1e-14);
186 assert_eq!(c.y, 0.0);
187 assert_eq!(c.z, 0.0);
188 }
189 }
190
191 #[test]
192 fn boundary_patches_named_correctly() {
193 let mesh = create_one_d_mesh(Length::new::<meter>(1.0), Area::new::<square_meter>(1.0), 3)
194 .unwrap();
195 assert_eq!(mesh.patches[0].name, "right");
196 assert_eq!(mesh.patches[1].name, "left");
197 }
198
199 #[test]
200 fn left_face_area_vector_points_negative_x() {
201 let a = 0.1_f64;
202 let mesh =
203 create_one_d_mesh(Length::new::<meter>(1.0), Area::new::<square_meter>(a), 3).unwrap();
204 let left_sf = mesh.face_area_vectors.last().unwrap();
206 assert!((left_sf.x + a).abs() < 1e-14);
207 assert_eq!(left_sf.y, 0.0);
208 }
209
210 #[test]
211 fn zero_cells_returns_error() {
212 assert!(
213 create_one_d_mesh(Length::new::<meter>(1.0), Area::new::<square_meter>(1.0), 0,)
214 .is_err()
215 );
216 }
217}