1use cellular_raza_concepts::*;
3
4use itertools::Itertools;
6use nalgebra::SVector;
7
8use serde::{Deserialize, Serialize};
9
10pub(super) fn get_decomp_res(n_voxel: usize, n_regions: usize) -> Option<(usize, usize, usize)> {
20 let mut average_len: i64 = (n_voxel as f64 / n_regions as f64).ceil() as i64;
33
34 let residue = |n: i64, m: i64, avg: i64| n_voxel as i64 - avg * n - (avg - 1) * m;
35
36 let mut n = n_regions as i64;
37 let mut m = 0;
38
39 for _ in 0..n_regions {
40 match residue(n, m, average_len) {
41 0 => {
42 return Some((n as usize, m as usize, average_len as usize));
43 }
44 1..=i64::MAX => {
45 if n == n_regions as i64 {
46 average_len += 1;
48 n = n_regions as i64;
49 m = 0;
50 }
51 }
52 i64::MIN..0 => {
53 n -= 1;
54 m += 1;
55 }
56 }
57 }
58 None
59}
60
61#[derive(Clone, Debug)]
65pub struct CartesianCuboid<F, const D: usize> {
66 min: SVector<F, D>,
67 max: SVector<F, D>,
68 dx: SVector<F, D>,
69 n_voxels: SVector<usize, D>,
70 pub rng_seed: u64,
72}
73
74impl<F, const D: usize> CartesianCuboid<F, D>
75where
76 F: Clone,
77{
78 pub fn get_min(&self) -> SVector<F, D> {
80 self.min.clone()
81 }
82
83 pub fn get_max(&self) -> SVector<F, D> {
85 self.max.clone()
86 }
87
88 pub fn get_dx(&self) -> SVector<F, D> {
90 self.dx.clone()
91 }
92
93 pub fn get_n_voxels(&self) -> SVector<usize, D> {
95 self.n_voxels
96 }
97}
98
99impl<C, Ci, F, const D: usize> Domain<C, CartesianSubDomain<F, D>, Ci> for CartesianCuboid<F, D>
100where
101 C: Position<nalgebra::SVector<F, D>>,
102 F: 'static
103 + num::Float
104 + Copy
105 + core::fmt::Debug
106 + num::FromPrimitive
107 + num::ToPrimitive
108 + core::ops::SubAssign
109 + core::ops::Div<Output = F>
110 + core::ops::DivAssign,
111 Ci: IntoIterator<Item = C>,
112{
113 type SubDomainIndex = usize;
114 type VoxelIndex = [usize; D];
115
116 fn decompose(
117 self,
118 n_subdomains: core::num::NonZeroUsize,
119 cells: Ci,
120 ) -> Result<DecomposedDomain<Self::SubDomainIndex, CartesianSubDomain<F, D>, C>, DecomposeError>
121 {
122 #[derive(Clone, Domain)]
123 struct MyIntermdiatedomain<F, const D: usize>
124 where
125 F: 'static
126 + num::Float
127 + Copy
128 + core::fmt::Debug
129 + num::FromPrimitive
130 + num::ToPrimitive
131 + core::ops::SubAssign
132 + core::ops::Div<Output = F>
133 + core::ops::DivAssign,
134 {
135 #[DomainRngSeed]
136 #[DomainCreateSubDomains]
137 #[SortCells]
138 domain: CartesianCuboid<F, D>,
139 }
140 let my_intermediate_domain = MyIntermdiatedomain { domain: self };
141 my_intermediate_domain.decompose(n_subdomains, cells)
142 }
143}
144
145impl<F, const D: usize> CartesianCuboid<F, D>
146where
147 F: 'static + num::Float + Copy + core::fmt::Debug + num::FromPrimitive + num::ToPrimitive,
148{
149 fn check_min_max(min: &[F; D], max: &[F; D]) -> Result<(), BoundaryError>
150 where
151 F: core::fmt::Debug,
152 {
153 for i in 0..D {
154 if min[i] >= max[i] {
155 return Err(BoundaryError(format!(
156 "Min {:?} must be smaller than Max {:?} for domain boundaries!",
157 min, max
158 )));
159 }
160 }
161 Ok(())
162 }
163
164 pub fn from_boundaries_and_interaction_range(
184 min: impl Into<[F; D]>,
185 max: impl Into<[F; D]>,
186 interaction_range: F,
187 ) -> Result<Self, BoundaryError> {
188 let min: [F; D] = min.into();
190 let max: [F; D] = max.into();
191
192 Self::check_min_max(&min, &max)?;
194
195 let mut n_voxels = [0; D];
197 let mut dx = [F::zero(); D];
198 for i in 0..D {
199 let n = ((max[i] - min[i]) / interaction_range).floor();
200 n_voxels[i] = n.to_usize().ok_or(BoundaryError(
202 cellular_raza_concepts::format_error_message!(
203 format!(
204 "Cannot convert float {:?} of type {} to usize",
205 n,
206 std::any::type_name::<F>()
207 ),
208 "conversion error during domain setup"
209 ),
210 ))?;
211 dx[i] = (max[i] - min[i]) / n;
212 }
213
214 Ok(Self {
215 min: min.into(),
216 max: max.into(),
217 dx: dx.into(),
218 n_voxels: n_voxels.into(),
219 rng_seed: 0,
220 })
221 }
222
223 pub fn from_boundaries_and_n_voxels(
226 min: impl Into<[F; D]>,
227 max: impl Into<[F; D]>,
228 n_voxels: impl Into<[usize; D]>,
229 ) -> Result<Self, BoundaryError> {
230 let min: [F; D] = min.into();
231 let max: [F; D] = max.into();
232 let n_voxels: [usize; D] = n_voxels.into();
233 Self::check_min_max(&min, &max)?;
234 let mut dx: SVector<F, D> = [F::zero(); D].into();
235 for i in 0..D {
236 let n = F::from_usize(n_voxels[i]).ok_or(BoundaryError(
237 cellular_raza_concepts::format_error_message!(
238 "conversion error during domain setup",
239 format!(
240 "Cannot convert usize {} to float of type {}",
241 n_voxels[i],
242 std::any::type_name::<F>()
243 )
244 ),
245 ))?;
246 dx[i] = (max[i] - min[i]) / n;
247 }
248 Ok(Self {
249 min: min.into(),
250 max: max.into(),
251 dx,
252 n_voxels: n_voxels.into(),
253 rng_seed: 0,
254 })
255 }
256}
257
258impl<F, const D: usize> CartesianCuboid<F, D> {
259 fn get_all_voxel_indices(&self) -> impl IntoIterator<Item = [usize; D]> {
260 use itertools::*;
261 (0..D)
262 .map(|i| 0..self.n_voxels[i])
263 .multi_cartesian_product()
264 .map(|x| {
265 let mut index = [0; D];
266 index.copy_from_slice(&x);
267 index
268 })
269 }
270
271 fn get_n_indices(&self) -> usize {
273 let mut res = 1;
274 for i in 0..D {
275 res *= self.n_voxels[i];
276 }
277 res
278 }
279}
280
281mod test_domain_setup {
282 #[test]
283 fn from_boundaries_and_interaction_range() {
284 use crate::CartesianCuboid;
285 let min = [0.0; 2];
286 let max = [2.0; 2];
287 let interaction_range = 1.0;
288 let _ = CartesianCuboid::from_boundaries_and_interaction_range(min, max, interaction_range)
289 .unwrap();
290 }
292
293 #[test]
294 fn from_boundaries_and_n_voxels() {
295 use crate::CartesianCuboid;
296 let min = [-100.0f32; 55];
297 let max = [43000.0f32; 55];
298 let n_voxels = [22; 55];
299 let _ = CartesianCuboid::from_boundaries_and_n_voxels(min, max, n_voxels).unwrap();
300 }
302}
303
304impl<F, const D: usize> CartesianCuboid<F, D>
305where
306 F: 'static
307 + num::Float
308 + Copy
309 + core::fmt::Debug
310 + num::FromPrimitive
311 + num::ToPrimitive
312 + core::ops::SubAssign
313 + core::ops::Div<Output = F>
314 + core::ops::DivAssign,
315{
316 pub fn get_voxel_index_of_raw(&self, pos: &SVector<F, D>) -> Result<[usize; D], BoundaryError> {
320 Self::check_min_max(&self.min.into(), &(*pos).into())?;
321 let n_vox = (pos - self.min).component_div(&self.dx);
322 let mut res = [0usize; D];
323 for i in 0..D {
324 res[i] = n_vox[i].to_usize().ok_or(BoundaryError(
325 cellular_raza_concepts::format_error_message!(
326 "conversion error during domain setup",
327 format!(
328 "Cannot convert float {:?} of type {} to usize",
329 n_vox[i],
330 std::any::type_name::<F>()
331 )
332 ),
333 ))?;
334 }
335 Ok(res)
336 }
337}
338
339impl<C, F, const D: usize> SortCells<C> for CartesianCuboid<F, D>
340where
341 F: 'static
342 + num::Float
343 + Copy
344 + core::fmt::Debug
345 + num::FromPrimitive
346 + num::ToPrimitive
347 + core::ops::SubAssign
348 + core::ops::Div<Output = F>
349 + core::ops::DivAssign,
350 C: Position<SVector<F, D>>,
351{
352 type VoxelIndex = [usize; D];
353
354 fn get_voxel_index_of(&self, cell: &C) -> Result<Self::VoxelIndex, BoundaryError> {
355 let pos = cell.pos();
356 self.get_voxel_index_of_raw(&pos)
357 }
358}
359
360impl<C, F, const D: usize> SortCells<C> for CartesianSubDomain<F, D>
361where
362 C: Position<nalgebra::SVector<F, D>>,
363 F: 'static + num::Float + core::fmt::Debug + core::ops::SubAssign + core::ops::DivAssign,
364{
365 type VoxelIndex = [usize; D];
366
367 fn get_voxel_index_of(&self, cell: &C) -> Result<Self::VoxelIndex, BoundaryError> {
368 let pos = cell.pos();
369 self.get_index_of(pos)
370 }
371}
372
373impl<F, const D: usize> DomainRngSeed for CartesianCuboid<F, D> {
374 fn get_rng_seed(&self) -> u64 {
375 self.rng_seed
376 }
377}
378
379#[test]
380fn generate_subdomains() {
381 use DomainCreateSubDomains;
382 let min = [0.0; 3];
383 let max = [100.0; 3];
384 let interaction_range = 20.0;
385 let domain =
386 CartesianCuboid::from_boundaries_and_interaction_range(min, max, interaction_range)
387 .unwrap();
388 let sub_domains = domain
389 .create_subdomains(4.try_into().unwrap())
390 .unwrap()
391 .into_iter()
392 .collect::<Vec<_>>();
393 assert_eq!(sub_domains.len(), 4);
394 assert_eq!(
395 sub_domains
396 .iter()
397 .map(|(_, _, voxels)| voxels.len())
398 .sum::<usize>(),
399 5usize.pow(3)
400 );
401}
402
403#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
405#[serde(bound = "
406F: 'static
407 + PartialEq
408 + Clone
409 + core::fmt::Debug
410 + Serialize
411 + for<'a> Deserialize<'a>,
412[usize; D]: Serialize + for<'a> Deserialize<'a>,
413")]
414pub struct CartesianSubDomain<F, const D: usize> {
415 min: SVector<F, D>,
416 max: SVector<F, D>,
417 dx: SVector<F, D>,
418 voxels: Vec<[usize; D]>,
419 pub(crate) domain_min: SVector<F, D>,
420 pub(crate) domain_max: SVector<F, D>,
421 domain_n_voxels: SVector<usize, D>,
422}
423
424impl<F, const D: usize> CartesianSubDomain<F, D>
425where
426 F: Clone,
427{
428 pub fn get_min(&self) -> SVector<F, D> {
431 self.min.clone()
432 }
433
434 pub fn get_max(&self) -> SVector<F, D> {
437 self.max.clone()
438 }
439
440 pub fn get_dx(&self) -> SVector<F, D> {
442 self.dx.clone()
443 }
444
445 pub fn get_voxels(&self) -> Vec<[usize; D]> {
447 self.voxels.clone()
448 }
449
450 pub fn get_domain_min(&self) -> SVector<F, D> {
452 self.domain_min.clone()
453 }
454
455 pub fn get_domain_max(&self) -> SVector<F, D> {
457 self.domain_max.clone()
458 }
459
460 pub fn get_domain_n_voxels(&self) -> SVector<usize, D> {
462 self.domain_n_voxels
463 }
464}
465
466impl<F, const D: usize> CartesianSubDomain<F, D> {
467 pub fn get_index_of<P>(&self, pos: P) -> Result<[usize; D], BoundaryError>
469 where
470 [F; D]: From<P>,
471 F: 'static + num::Float + core::fmt::Debug + core::ops::SubAssign + core::ops::DivAssign,
472 {
473 let pos: [F; D] = pos.into();
474 let mut res = [0usize; D];
475 for i in 0..D {
476 let n_vox = (pos[i] - self.domain_min[i]) / self.dx[i];
477 res[i] = n_vox.to_usize().ok_or(BoundaryError(
478 cellular_raza_concepts::format_error_message!(
479 "conversion error during domain setup",
480 format!(
481 "Cannot convert float {:?} of type {} to usize",
482 n_vox,
483 std::any::type_name::<F>()
484 )
485 ),
486 ))?;
487 }
488 Ok(res)
489 }
490}
491
492impl<F, const D: usize> DomainCreateSubDomains<CartesianSubDomain<F, D>> for CartesianCuboid<F, D>
493where
494 F: 'static + num::Float + core::fmt::Debug + num::FromPrimitive,
495{
496 type SubDomainIndex = usize;
497 type VoxelIndex = [usize; D];
498
499 fn create_subdomains(
500 &self,
501 n_subdomains: core::num::NonZeroUsize,
502 ) -> Result<
503 impl IntoIterator<
504 Item = (
505 Self::SubDomainIndex,
506 CartesianSubDomain<F, D>,
507 Vec<Self::VoxelIndex>,
508 ),
509 >,
510 DecomposeError,
511 > {
512 let indices = self.get_all_voxel_indices();
513 let n_indices = self.get_n_indices();
514
515 let (n, _m, average_len) = get_decomp_res(n_indices, n_subdomains.into()).ok_or(
516 DecomposeError::Generic("Could not find a suiting decomposition".to_owned()),
517 )?;
518
519 let switcher = n * average_len;
522 let indices_grouped = indices.into_iter().enumerate().chunk_by(|(i, _)| {
523 use num::Integer;
524 if *i < switcher {
525 i.div_rem(&average_len).0
526 } else {
527 (i - switcher).div_rem(&(average_len - 1).max(1)).0 + n
528 }
529 });
530 let mut res = Vec::new();
531 for (n_subdomain, indices) in indices_grouped.into_iter() {
532 let mut min_vox = [usize::MAX; D];
533 let mut max_vox = [0; D];
534 let voxels = indices
535 .into_iter()
536 .map(|(_, index)| {
537 for i in 0..D {
538 min_vox[i] = min_vox[i].min(index[i]);
539 max_vox[i] = max_vox[i].max(index[i]);
540 }
541 index
542 })
543 .collect::<Vec<_>>();
544 let mut min = [F::zero(); D];
545 let mut max = [F::zero(); D];
546 for i in 0..D {
547 let n_vox_min = F::from_usize(min_vox[i]).ok_or(DecomposeError::Generic(
548 cellular_raza_concepts::format_error_message!(
549 "conversion error during domain setup",
550 format!(
551 "Cannot convert float {:?} of type {} to usize",
552 min_vox[i],
553 std::any::type_name::<F>()
554 )
555 ),
556 ))?;
557 let n_vox_max = F::from_usize(max_vox[i]).ok_or(DecomposeError::Generic(
558 cellular_raza_concepts::format_error_message!(
559 "conversion error during domain setup",
560 format!(
561 "Cannot convert float {:?} of type {} to usize",
562 max_vox[i],
563 std::any::type_name::<F>()
564 )
565 ),
566 ))?;
567 min[i] = self.min[i] + n_vox_min * self.dx[i];
568 max[i] = self.min[i] + (n_vox_max + F::one()) * self.dx[i];
569 }
570 let subdomain = CartesianSubDomain {
571 min: min.into(),
572 max: max.into(),
573 dx: self.dx,
574 voxels: voxels.clone(),
575 domain_min: self.min,
576 domain_max: self.max,
577 domain_n_voxels: self.n_voxels,
578 };
579 res.push((n_subdomain, subdomain, voxels));
580 }
581 Ok(res)
582 }
583}
584
585impl<Coord, F, const D: usize> SubDomainMechanics<Coord, Coord> for CartesianSubDomain<F, D>
586where
587 Coord: Clone,
588 [F; D]: From<Coord>,
589 Coord: From<[F; D]>,
590 Coord: std::fmt::Debug,
591 F: num::Float,
592{
593 fn apply_boundary(&self, pos: &mut Coord, vel: &mut Coord) -> Result<(), BoundaryError> {
594 let mut velocity: [F; D] = vel.clone().into();
595 let mut position: [F; D] = pos.clone().into();
596
597 let two = F::one() + F::one();
599
600 for i in 0..D {
602 if position[i] < self.domain_min[i] {
604 position[i] = two * self.domain_min[i] - position[i];
605 velocity[i] = velocity[i].abs();
606 }
607 if position[i] > self.domain_max[i] {
609 position[i] = two * self.domain_max[i] - position[i];
610 velocity[i] = -velocity[i].abs();
611 }
612 }
613
614 for (p, (dmin, dmax)) in position
615 .iter()
616 .zip(self.domain_min.iter().zip(self.domain_max.iter()))
617 {
618 if p < dmin || p > dmax {
619 return Err(BoundaryError(format!(
620 "Particle is out of domain at position {:?}",
621 pos
622 )));
623 }
624 }
625
626 *pos = position.into();
628 *vel = velocity.into();
629 Ok(())
630 }
631}
632
633impl<F, const D: usize> SubDomain for CartesianSubDomain<F, D> {
634 type VoxelIndex = [usize; D];
635
636 fn get_all_indices(&self) -> Vec<Self::VoxelIndex> {
637 self.voxels.clone()
638 }
639
640 fn get_neighbor_voxel_indices(&self, voxel_index: &Self::VoxelIndex) -> Vec<Self::VoxelIndex> {
641 let mut bounds = [[0; 2]; D];
643 for i in 0..D {
644 bounds[i] = [
645 (voxel_index[i] as i64 - 1).max(0) as usize,
646 (voxel_index[i] + 2).min(self.domain_n_voxels[i]),
647 ];
648 }
649
650 (0..D)
652 .map(|i| bounds[i][0]..bounds[i][1])
653 .multi_cartesian_product()
654 .map(|ind_v| {
655 let mut res = [0; D];
656 <[usize]>::copy_from_slice(&mut res, &ind_v);
657 res
658 })
659 .filter(|ind| ind != voxel_index)
660 .collect()
661 }
662}
663
664#[cfg(test)]
665mod test {
666 use super::get_decomp_res;
667 use rayon::prelude::*;
668
669 #[test]
670 fn test_get_demomp_res() {
671 #[cfg(debug_assertions)]
672 let max = 500;
673 #[cfg(not(debug_assertions))]
674 let max = 5_000;
675
676 (1..max)
677 .into_par_iter()
678 .map(|n_voxel| {
679 #[cfg(debug_assertions)]
680 let max_regions = 100;
681 #[cfg(not(debug_assertions))]
682 let max_regions = 1_000;
683 for n_regions in 1..max_regions {
684 match get_decomp_res(n_voxel, n_regions) {
685 Some(res) => {
686 let (n, m, average_len) = res;
687 assert_eq!(n + m, n_regions);
688 assert_eq!(n * average_len + m * (average_len - 1), n_voxel);
689 }
690 None => panic!(
691 "No result for inputs n_voxel: {} n_regions: {}",
692 n_voxel, n_regions
693 ),
694 }
695 }
696 })
697 .collect::<Vec<()>>();
698 }
699}