dyntri_core/triangulation/
checks.rs1use crate::triangulation::boundary_map::get_face;
2use crate::triangulation::simplices::{
3 CausalSimplex, CausalSimplexKind, canonical_oriented_simplex, canonical_simplex,
4};
5use crate::triangulation::{
6 CausalTriangulation, CausalTriangulation2D, CausalTriangulation3D, CausalTriangulation4D,
7};
8use crate::triangulation::{Triangulation, Triangulation2D, Triangulation3D, Triangulation4D};
9
10#[derive(Debug, thiserror::Error)]
11#[error("Neighbour {to} not correctly connected back to {from}.")]
12pub struct SimplexConnectivityError {
13 from: usize,
14 to: usize,
15}
16
17impl<const N: usize> Triangulation<N> {
19 pub fn check_biconnectivity(&self) -> Result<(), SimplexConnectivityError> {
22 for (label, simplex) in self.simplices.iter().enumerate() {
23 for (nbr_label, backindex) in simplex.get_neighbours_backindices() {
24 if self.get_neighbour(nbr_label, backindex) != label {
25 return Err(SimplexConnectivityError {
26 from: label,
27 to: nbr_label,
28 });
29 }
30 }
31 }
32 Ok(())
33 }
34}
35
36impl<K, const N: usize> CausalTriangulation<K, N> {
38 pub fn check_biconnectivity(&self) -> Result<(), SimplexConnectivityError> {
41 for (label, simplex) in self.simplices.iter().enumerate() {
42 for (nbr_label, backindex) in simplex.get_neighbours_backindices() {
43 if self.get_neighbour(nbr_label, backindex) != label {
44 return Err(SimplexConnectivityError {
45 from: label,
46 to: nbr_label,
47 });
48 }
49 }
50 }
51 Ok(())
52 }
53}
54
55#[derive(Debug, thiserror::Error)]
56pub enum SimplexCheckError {
57 #[error("Vertex count mismatch: expected {expected}, found {found}")]
58 VertexCountMismatch { expected: usize, found: usize },
59
60 #[error("Face count mismatch: expected {expected}, found {found} (2*N_(D-1) = (D+1)*N_(D))")]
61 FaceCountMismatch { expected: usize, found: usize },
62
63 #[error("Dehn-Sommerville relation violated")]
64 DehnSommervilleViolation,
65}
66
67impl Triangulation2D {
68 pub fn check_simplex_counts(&self) -> Result<(), SimplexCheckError> {
70 let n0 = self.count_vertices();
71 let n1 = self.count_n1();
72
73 if n0 != self.num_vertices() {
74 return Err(SimplexCheckError::VertexCountMismatch {
75 expected: n0,
76 found: self.num_vertices(),
77 });
78 }
79 if n1 != self.num_faces() {
80 return Err(SimplexCheckError::FaceCountMismatch {
81 expected: n1,
82 found: self.num_faces(),
83 });
84 }
85
86 Ok(())
87 }
88}
89
90impl Triangulation3D {
91 pub fn check_simplex_counts(&self) -> Result<(), SimplexCheckError> {
93 let n0 = self.count_vertices();
94 let n1 = self.count_n1();
95 let n2 = self.count_n2();
96
97 if n0 != self.num_vertices() {
98 return Err(SimplexCheckError::VertexCountMismatch {
99 expected: n0,
100 found: self.num_vertices(),
101 });
102 }
103 if n2 != self.num_faces() {
104 return Err(SimplexCheckError::FaceCountMismatch {
105 expected: n2,
106 found: self.num_faces(),
107 });
108 }
109 if 2 * n0 + n2 != 2 * n1 {
110 return Err(SimplexCheckError::DehnSommervilleViolation);
111 }
112
113 Ok(())
114 }
115}
116
117impl Triangulation4D {
118 pub fn check_simplex_counts(&self) -> Result<(), SimplexCheckError> {
120 let n0 = self.count_vertices();
121 let n1 = self.count_n1();
122 let n2 = self.count_n2();
123 let n3 = self.count_n3();
124
125 if n0 != self.num_vertices() {
126 return Err(SimplexCheckError::VertexCountMismatch {
127 expected: n0,
128 found: self.num_vertices(),
129 });
130 }
131 if n3 != self.num_faces() {
132 return Err(SimplexCheckError::FaceCountMismatch {
133 expected: n3,
134 found: self.num_faces(),
135 });
136 }
137 if 2 * n1 + 2 * n3 != 3 * n2 {
138 return Err(SimplexCheckError::DehnSommervilleViolation);
139 }
140
141 Ok(())
142 }
143}
144
145impl CausalTriangulation2D {
146 pub fn check_simplex_counts(&self) -> Result<(), SimplexCheckError> {
148 let n0 = self.count_vertices();
149 let n1 = self.count_n1();
150
151 if n0 != self.num_vertices() {
152 return Err(SimplexCheckError::VertexCountMismatch {
153 expected: n0,
154 found: self.num_vertices(),
155 });
156 }
157 if n1 != self.num_faces() {
158 return Err(SimplexCheckError::FaceCountMismatch {
159 expected: n1,
160 found: self.num_faces(),
161 });
162 }
163
164 Ok(())
165 }
166}
167
168impl CausalTriangulation3D {
169 pub fn check_simplex_counts(&self) -> Result<(), SimplexCheckError> {
171 let n0 = self.count_vertices();
172 let n1 = self.count_n1();
173 let n2 = self.count_n2();
174
175 if n0 != self.num_vertices() {
176 return Err(SimplexCheckError::VertexCountMismatch {
177 expected: n0,
178 found: self.num_vertices(),
179 });
180 }
181 if n2 != self.num_faces() {
182 return Err(SimplexCheckError::FaceCountMismatch {
183 expected: n2,
184 found: self.num_faces(),
185 });
186 }
187 if 2 * n0 + n2 != 2 * n1 {
188 return Err(SimplexCheckError::DehnSommervilleViolation);
189 }
190
191 Ok(())
192 }
193}
194
195impl CausalTriangulation4D {
196 pub fn check_simplex_counts(&self) -> Result<(), SimplexCheckError> {
198 let n0 = self.count_vertices();
199 let n1 = self.count_n1();
200 let n2 = self.count_n2();
201 let n3 = self.count_n3();
202
203 if n0 != self.num_vertices() {
204 return Err(SimplexCheckError::VertexCountMismatch {
205 expected: n0,
206 found: self.num_vertices(),
207 });
208 }
209 if n3 != self.num_faces() {
210 return Err(SimplexCheckError::FaceCountMismatch {
211 expected: n3,
212 found: self.num_faces(),
213 });
214 }
215 if 2 * n1 + 2 * n3 != 3 * n2 {
216 return Err(SimplexCheckError::DehnSommervilleViolation);
217 }
218
219 Ok(())
220 }
221}
222
223#[derive(Debug, thiserror::Error)]
224pub enum VertexConsistencyError<const N: usize> {
225 #[error("Trianglation has incorrectly matched vertices: {face:?} {nbr_face:?}")]
226 VertexMatching {
227 face: [usize; N],
228 nbr_face: [usize; N],
229 },
230 #[error("Trianglation has incorrectly oriented vertices: {face:?} {nbr_face:?}")]
231 Orientation {
232 face: [usize; N],
233 nbr_face: [usize; N],
234 },
235}
236
237macro_rules! impl_vertex_consistency_check {
238 ($triangulation:ty, $dim:expr) => {
239 impl $triangulation {
240 pub fn check_vertex_consistency(&self) -> Result<(), VertexConsistencyError<$dim>> {
242 for simplex in self.simplices.iter() {
243 for (index, (nbr_label, backindex)) in
244 simplex.get_neighbours_backindices().into_iter().enumerate()
245 {
246 let vertices = simplex.vertices;
247 let nbr_vertices = self.get_simplex_vertices(nbr_label);
248
249 let face = get_face(vertices, index);
250 let nbr_face = get_face(nbr_vertices, backindex);
251 let face_canon = canonical_simplex(face);
252 let nbr_face_canon = canonical_simplex(nbr_face);
253
254 if face_canon != nbr_face_canon {
255 return Err(VertexConsistencyError::VertexMatching { face, nbr_face });
256 }
257 }
258 }
259
260 Ok(())
261 }
262
263 pub fn check_vertex_consistency_ordered(
266 &self,
267 ) -> Result<(), VertexConsistencyError<$dim>> {
268 for simplex in self.simplices.iter() {
269 for (index, (nbr_label, backindex)) in
270 simplex.get_neighbours_backindices().into_iter().enumerate()
271 {
272 let vertices = simplex.vertices;
273 let nbr_vertices = self.get_simplex_vertices(nbr_label);
274
275 let face = get_face(vertices, index);
276 let nbr_face = get_face(nbr_vertices, backindex);
277 let (mut parity, face_canon) = canonical_oriented_simplex(face);
278 let (mut nbr_parity, nbr_face_canon) = canonical_oriented_simplex(nbr_face);
279 parity = !(parity ^ index.is_multiple_of(2));
280 nbr_parity = !(nbr_parity ^ backindex.is_multiple_of(2));
281 if face_canon != nbr_face_canon {
282 return Err(VertexConsistencyError::VertexMatching { face, nbr_face });
283 }
284 if nbr_parity == parity {
286 return Err(VertexConsistencyError::Orientation { face, nbr_face });
287 }
288 }
289 }
290
291 Ok(())
292 }
293 }
294 };
295}
296
297impl_vertex_consistency_check!(Triangulation2D, 2);
298impl_vertex_consistency_check!(Triangulation3D, 3);
299impl_vertex_consistency_check!(Triangulation4D, 4);
300
301impl_vertex_consistency_check!(CausalTriangulation2D, 2);
302impl_vertex_consistency_check!(CausalTriangulation3D, 3);
303impl_vertex_consistency_check!(CausalTriangulation4D, 4);
304
305#[derive(Debug, Clone, thiserror::Error)]
310#[error(
311 "Simplex ({label}: {simplex:?}) has incorrect absolute order of neighbour \
312 ({label_nbr}: {simplex_nbr:?}) or the incorrect SimplexKind"
313)]
314pub struct NeighbourAbsoluteOrderingError<K, const N: usize> {
315 label: usize,
316 simplex: CausalSimplex<K, N>,
317 label_nbr: usize,
318 simplex_nbr: CausalSimplex<K, N>,
319}
320
321#[derive(Debug, Clone, thiserror::Error)]
322pub enum SimplexKindError<K> {
323 #[error("Simplex vertex have time {tfound} which is inconsistent with {tsimplex}")]
324 IncorrectTime { tsimplex: u16, tfound: u16 },
325 #[error("Simplex kind {kind:?} inconsistent with found signature {signature:?}")]
326 InconsistentKind { kind: K, signature: (u8, u8) },
327}
328
329impl<K: Copy + CausalSimplexKind, const N: usize> CausalTriangulation<K, N> {
330 pub fn check_neighbour_absolute_ordering(
335 &self,
336 ) -> Result<(), NeighbourAbsoluteOrderingError<K, N>> {
337 for label in 0..self.num_simplices() {
338 let t = self.get_simplex_time(label);
339 let label_nbr = self.get_neighbour(label, 0);
341 let tnbr = self.get_simplex_time(label_nbr);
342
343 match self.get_simplex_kind(label).orientation() {
344 super::simplices::CausalOrientation::Past => {
345 if (tnbr + 1) % self.num_timeslices() != t {
346 return Err(NeighbourAbsoluteOrderingError {
347 label,
348 simplex: *self.get_simplex(label),
349 label_nbr,
350 simplex_nbr: *self.get_simplex(label_nbr),
351 });
352 }
353 }
354 super::simplices::CausalOrientation::Space => {
355 if t != tnbr {
356 return Err(NeighbourAbsoluteOrderingError {
357 label,
358 simplex: *self.get_simplex(label),
359 label_nbr,
360 simplex_nbr: *self.get_simplex(label_nbr),
361 });
362 }
363 }
364 super::simplices::CausalOrientation::Future => {
365 if tnbr != (t + 1) % self.num_timeslices() {
366 return Err(NeighbourAbsoluteOrderingError {
367 label,
368 simplex: *self.get_simplex(label),
369 label_nbr,
370 simplex_nbr: *self.get_simplex(label_nbr),
371 });
372 }
373 }
374 }
375 }
376 Ok(())
377 }
378
379 pub fn check_simplex_kinds(&self) -> Result<(), SimplexKindError<K>> {
384 for label in 0..self.num_simplices() {
385 let t_lower = self.get_simplex_time(label);
387 let t_upper = (t_lower + 1) % self.num_timeslices();
389
390 let mut lower_count = 0u8;
391 let mut upper_count = 0u8;
392 for vtime in self
393 .get_simplex_vertices(label)
394 .map(|vlabel| self.get_vertex_time(vlabel))
395 {
396 if vtime == t_lower {
397 lower_count += 1
398 } else if vtime == t_upper {
399 upper_count += 1
400 } else {
401 return Err(SimplexKindError::IncorrectTime {
402 tsimplex: t_lower,
403 tfound: vtime,
404 });
405 }
406 }
407
408 if self.get_simplex_kind(label).signature() != (lower_count, upper_count) {
409 return Err(SimplexKindError::InconsistentKind {
410 kind: self.get_simplex_kind(label),
411 signature: (lower_count, upper_count),
412 });
413 }
414 }
415 Ok(())
416 }
417}