mesh_sieve/mesh_generation/
mod.rs1use crate::data::atlas::Atlas;
4use crate::data::coordinates::Coordinates;
5use crate::data::mixed_section::MixedSectionStore;
6use crate::data::section::Section;
7use crate::data::storage::VecStorage;
8use crate::io::MeshData;
9use crate::mesh_error::MeshSieveError;
10use crate::topology::cell_type::CellType;
11use crate::topology::labels::LabelSet;
12use crate::topology::periodic::PointEquivalence;
13use crate::topology::point::PointId;
14use crate::topology::sieve::{InMemorySieve, MutableSieve, Sieve};
15use std::collections::BTreeMap;
16
17pub const BOUNDARY_X_MIN: &str = "boundary_x_min";
19pub const BOUNDARY_X_MAX: &str = "boundary_x_max";
21pub const BOUNDARY_Y_MIN: &str = "boundary_y_min";
23pub const BOUNDARY_Y_MAX: &str = "boundary_y_max";
25pub const BOUNDARY_Z_MIN: &str = "boundary_z_min";
27pub const BOUNDARY_Z_MAX: &str = "boundary_z_max";
29
30type MeshDataType =
31 MeshData<InMemorySieve<PointId, ()>, f64, VecStorage<f64>, VecStorage<CellType>>;
32
33#[derive(Clone, Copy, Debug, Default)]
35pub struct Periodicity {
36 pub x: bool,
37 pub y: bool,
38 pub z: bool,
39}
40
41impl Periodicity {
42 pub fn none() -> Self {
44 Self::default()
45 }
46}
47
48#[derive(Clone, Copy, Debug, Default)]
50pub struct MeshGenerationOptions {
51 pub periodic: Periodicity,
52}
53
54#[derive(Debug)]
56pub struct GeneratedMesh {
57 pub mesh: MeshDataType,
58 pub periodic: Option<PointEquivalence>,
59}
60
61fn invalid_geometry(message: impl Into<String>) -> MeshSieveError {
62 MeshSieveError::InvalidGeometry(message.into())
63}
64
65fn build_mesh(
66 dimension: usize,
67 vertex_coords: &[Vec<f64>],
68 cells: &[Vec<usize>],
69 cell_type: CellType,
70) -> Result<(MeshDataType, Vec<PointId>), MeshSieveError> {
71 if dimension == 0 {
72 return Err(invalid_geometry("dimension must be non-zero"));
73 }
74 for (idx, coord) in vertex_coords.iter().enumerate() {
75 if coord.len() != dimension {
76 return Err(invalid_geometry(format!(
77 "vertex {idx} has dimension {}, expected {dimension}",
78 coord.len()
79 )));
80 }
81 }
82
83 let mut sieve = InMemorySieve::<PointId, ()>::default();
84 let mut next_id = 1u64;
85
86 let mut vertex_points = Vec::with_capacity(vertex_coords.len());
87 for _ in 0..vertex_coords.len() {
88 let pid = PointId::new(next_id)?;
89 next_id += 1;
90 MutableSieve::add_point(&mut sieve, pid);
91 vertex_points.push(pid);
92 }
93
94 let mut cell_points = Vec::with_capacity(cells.len());
95 for _ in 0..cells.len() {
96 let pid = PointId::new(next_id)?;
97 next_id += 1;
98 MutableSieve::add_point(&mut sieve, pid);
99 cell_points.push(pid);
100 }
101
102 for (cell_idx, vertices) in cells.iter().enumerate() {
103 let cell_point = cell_points[cell_idx];
104 for &vidx in vertices {
105 let vpoint = *vertex_points.get(vidx).ok_or_else(|| {
106 invalid_geometry(format!("cell {cell_idx} references missing vertex {vidx}"))
107 })?;
108 sieve.add_arrow(cell_point, vpoint, ())?;
109 }
110 }
111 sieve.sort_adjacency();
112
113 let mut coord_atlas = Atlas::default();
114 for &p in &vertex_points {
115 coord_atlas.try_insert(p, dimension)?;
116 }
117 let mut coords =
118 Coordinates::<f64, VecStorage<f64>>::try_new(dimension, dimension, coord_atlas)?;
119 for (p, coord) in vertex_points.iter().zip(vertex_coords.iter()) {
120 coords.section_mut().try_set(*p, coord)?;
121 }
122
123 let mut cell_atlas = Atlas::default();
124 for &p in vertex_points.iter().chain(cell_points.iter()) {
125 cell_atlas.try_insert(p, 1)?;
126 }
127 let mut cell_types = Section::<CellType, VecStorage<CellType>>::new(cell_atlas);
128 for &p in &vertex_points {
129 cell_types.try_set(p, &[CellType::Vertex])?;
130 }
131 for &p in &cell_points {
132 cell_types.try_set(p, &[cell_type])?;
133 }
134
135 Ok((
136 MeshDataType {
137 sieve,
138 coordinates: Some(coords),
139 sections: BTreeMap::new(),
140 mixed_sections: MixedSectionStore::default(),
141 labels: None,
142 cell_types: Some(cell_types),
143 discretization: None,
144 },
145 vertex_points,
146 ))
147}
148
149pub fn interval_mesh(
151 n: usize,
152 min: f64,
153 max: f64,
154 options: MeshGenerationOptions,
155) -> Result<GeneratedMesh, MeshSieveError> {
156 if n == 0 {
157 return Err(invalid_geometry("n must be positive"));
158 }
159
160 let dx = (max - min) / n as f64;
161 let mut vertices = Vec::with_capacity(n + 1);
162 for i in 0..=n {
163 vertices.push(vec![min + dx * i as f64]);
164 }
165
166 let mut cells = Vec::with_capacity(n);
167 for i in 0..n {
168 cells.push(vec![i, i + 1]);
169 }
170
171 let (mut mesh, vertex_points) = build_mesh(1, &vertices, &cells, CellType::Segment)?;
172
173 let mut labels = LabelSet::new();
174 if let Some(&first) = vertex_points.first() {
175 labels.set_label(first, BOUNDARY_X_MIN, 1);
176 }
177 if let Some(&last) = vertex_points.last() {
178 labels.set_label(last, BOUNDARY_X_MAX, 1);
179 }
180 mesh.labels = Some(labels);
181
182 let periodic = if options.periodic.x {
183 let mut eq = PointEquivalence::new();
184 if let (Some(&first), Some(&last)) = (vertex_points.first(), vertex_points.last()) {
185 eq.add_equivalence(first, last);
186 }
187 Some(eq)
188 } else {
189 None
190 };
191
192 Ok(GeneratedMesh { mesh, periodic })
193}
194
195pub fn quad_mesh(
197 nx: usize,
198 ny: usize,
199 min: [f64; 2],
200 max: [f64; 2],
201 options: MeshGenerationOptions,
202) -> Result<GeneratedMesh, MeshSieveError> {
203 if nx == 0 || ny == 0 {
204 return Err(invalid_geometry("nx and ny must be positive"));
205 }
206
207 let dx = (max[0] - min[0]) / nx as f64;
208 let dy = (max[1] - min[1]) / ny as f64;
209 let mut vertices = Vec::with_capacity((nx + 1) * (ny + 1));
210 for j in 0..=ny {
211 let y = min[1] + dy * j as f64;
212 for i in 0..=nx {
213 let x = min[0] + dx * i as f64;
214 vertices.push(vec![x, y]);
215 }
216 }
217
218 let mut cells = Vec::with_capacity(nx * ny);
219 let row_stride = nx + 1;
220 for j in 0..ny {
221 for i in 0..nx {
222 let v0 = j * row_stride + i;
223 let v1 = v0 + 1;
224 let v3 = v0 + row_stride;
225 let v2 = v3 + 1;
226 cells.push(vec![v0, v1, v2, v3]);
227 }
228 }
229
230 let (mut mesh, vertex_points) = build_mesh(2, &vertices, &cells, CellType::Quadrilateral)?;
231
232 let mut labels = LabelSet::new();
233 for j in 0..=ny {
234 for i in 0..=nx {
235 let idx = j * (nx + 1) + i;
236 let point = vertex_points[idx];
237 if i == 0 {
238 labels.set_label(point, BOUNDARY_X_MIN, 1);
239 }
240 if i == nx {
241 labels.set_label(point, BOUNDARY_X_MAX, 1);
242 }
243 if j == 0 {
244 labels.set_label(point, BOUNDARY_Y_MIN, 1);
245 }
246 if j == ny {
247 labels.set_label(point, BOUNDARY_Y_MAX, 1);
248 }
249 }
250 }
251 mesh.labels = Some(labels);
252
253 let periodic = if options.periodic.x || options.periodic.y {
254 let mut eq = PointEquivalence::new();
255 if options.periodic.x {
256 for j in 0..=ny {
257 let left_idx = j * (nx + 1);
258 let right_idx = left_idx + nx;
259 eq.add_equivalence(vertex_points[left_idx], vertex_points[right_idx]);
260 }
261 }
262 if options.periodic.y {
263 for i in 0..=nx {
264 let bottom_idx = i;
265 let top_idx = ny * (nx + 1) + i;
266 eq.add_equivalence(vertex_points[bottom_idx], vertex_points[top_idx]);
267 }
268 }
269 Some(eq)
270 } else {
271 None
272 };
273
274 Ok(GeneratedMesh { mesh, periodic })
275}
276
277pub fn hex_mesh(
279 nx: usize,
280 ny: usize,
281 nz: usize,
282 min: [f64; 3],
283 max: [f64; 3],
284 options: MeshGenerationOptions,
285) -> Result<GeneratedMesh, MeshSieveError> {
286 if nx == 0 || ny == 0 || nz == 0 {
287 return Err(invalid_geometry("nx, ny, and nz must be positive"));
288 }
289
290 let dx = (max[0] - min[0]) / nx as f64;
291 let dy = (max[1] - min[1]) / ny as f64;
292 let dz = (max[2] - min[2]) / nz as f64;
293 let mut vertices = Vec::with_capacity((nx + 1) * (ny + 1) * (nz + 1));
294 for k in 0..=nz {
295 let z = min[2] + dz * k as f64;
296 for j in 0..=ny {
297 let y = min[1] + dy * j as f64;
298 for i in 0..=nx {
299 let x = min[0] + dx * i as f64;
300 vertices.push(vec![x, y, z]);
301 }
302 }
303 }
304
305 let mut cells = Vec::with_capacity(nx * ny * nz);
306 let row_stride = nx + 1;
307 let slab_stride = row_stride * (ny + 1);
308 for k in 0..nz {
309 for j in 0..ny {
310 for i in 0..nx {
311 let base = k * slab_stride + j * row_stride + i;
312 let v0 = base;
313 let v1 = base + 1;
314 let v3 = base + row_stride;
315 let v2 = v3 + 1;
316 let v4 = base + slab_stride;
317 let v5 = v4 + 1;
318 let v7 = v4 + row_stride;
319 let v6 = v7 + 1;
320 cells.push(vec![v0, v1, v2, v3, v4, v5, v6, v7]);
321 }
322 }
323 }
324
325 let (mut mesh, vertex_points) = build_mesh(3, &vertices, &cells, CellType::Hexahedron)?;
326
327 let mut labels = LabelSet::new();
328 for k in 0..=nz {
329 for j in 0..=ny {
330 for i in 0..=nx {
331 let idx = k * (nx + 1) * (ny + 1) + j * (nx + 1) + i;
332 let point = vertex_points[idx];
333 if i == 0 {
334 labels.set_label(point, BOUNDARY_X_MIN, 1);
335 }
336 if i == nx {
337 labels.set_label(point, BOUNDARY_X_MAX, 1);
338 }
339 if j == 0 {
340 labels.set_label(point, BOUNDARY_Y_MIN, 1);
341 }
342 if j == ny {
343 labels.set_label(point, BOUNDARY_Y_MAX, 1);
344 }
345 if k == 0 {
346 labels.set_label(point, BOUNDARY_Z_MIN, 1);
347 }
348 if k == nz {
349 labels.set_label(point, BOUNDARY_Z_MAX, 1);
350 }
351 }
352 }
353 }
354 mesh.labels = Some(labels);
355
356 let periodic = if options.periodic.x || options.periodic.y || options.periodic.z {
357 let mut eq = PointEquivalence::new();
358 if options.periodic.x {
359 for k in 0..=nz {
360 for j in 0..=ny {
361 let left_idx = k * (nx + 1) * (ny + 1) + j * (nx + 1);
362 let right_idx = left_idx + nx;
363 eq.add_equivalence(vertex_points[left_idx], vertex_points[right_idx]);
364 }
365 }
366 }
367 if options.periodic.y {
368 for k in 0..=nz {
369 for i in 0..=nx {
370 let bottom_idx = k * (nx + 1) * (ny + 1) + i;
371 let top_idx = k * (nx + 1) * (ny + 1) + ny * (nx + 1) + i;
372 eq.add_equivalence(vertex_points[bottom_idx], vertex_points[top_idx]);
373 }
374 }
375 }
376 if options.periodic.z {
377 let slab = (nx + 1) * (ny + 1);
378 for j in 0..=ny {
379 for i in 0..=nx {
380 let bottom_idx = j * (nx + 1) + i;
381 let top_idx = nz * slab + j * (nx + 1) + i;
382 eq.add_equivalence(vertex_points[bottom_idx], vertex_points[top_idx]);
383 }
384 }
385 }
386 Some(eq)
387 } else {
388 None
389 };
390
391 Ok(GeneratedMesh { mesh, periodic })
392}