cellular_raza_core/backend/chili/
simulation_flow.rs

1use cellular_raza_concepts::IndexError;
2use itertools::Itertools;
3
4use std::{
5    collections::{BTreeMap, BTreeSet},
6    marker::PhantomData,
7    sync::atomic::AtomicBool,
8};
9
10#[cfg(feature = "tracing")]
11use tracing::instrument;
12
13use super::errors::SimulationError;
14
15///
16/// This very simple implementation uses the [hurdles::Barrier] struct which should
17/// in theory perform faster than the [std::sync::Barrier] struct from the standard library.
18///
19/// By using the [SyncSubDomains] trait, we can automatically create a collection of syncers
20/// which can then be simply given to the respective threads and handle synchronization.
21/// ```
22/// # use std::collections::BTreeMap;
23/// # use cellular_raza_core::backend::chili::{BarrierSync, FromMap, SyncSubDomains};
24/// let map = BTreeMap::from_iter([
25///     (0, std::collections::BTreeSet::from([1])),
26///     (1, std::collections::BTreeSet::from([0])),
27/// ]);
28/// let mut syncers = BarrierSync::from_map(&map).unwrap();
29/// assert_eq!(syncers.len(), 2);
30///
31/// let mut syncer_0 = syncers.remove(&0).unwrap();
32/// let mut syncer_1 = syncers.remove(&1).unwrap();
33///
34/// // Define the number of iterations to run
35/// let n_iterations = 10;
36/// let shared_counter = std::sync::Arc::new(std::sync::Mutex::new(0_i64));
37///
38/// let shared_counter_0 = std::sync::Arc::clone(&shared_counter);
39/// let handle_0 = std::thread::spawn(move || {
40///     for _ in 0..n_iterations {
41///         syncer_0.sync().unwrap();
42///         *shared_counter_0.lock().unwrap() += 1;
43///         syncer_0.sync().unwrap();
44///     }
45/// });
46///
47/// for i in 0..n_iterations {
48///     syncer_1.sync().unwrap();
49///     syncer_1.sync().unwrap();
50///     assert_eq!(*shared_counter.lock().unwrap(), i+1);
51/// }
52/// handle_0.join();
53/// ```
54pub struct BarrierSync {
55    barrier: hurdles::Barrier,
56    got_error: std::sync::Arc<AtomicBool>,
57}
58
59/// Validates a given map.
60///
61/// This algorithm checks if every keys neighbors also contain the specified key.
62/// If this is not the case, the map cannot be considered valid.
63/// Note that this algorithm does not check if all keys are connected.
64/// This means, disjoint sets are allowed.
65///
66/// ```
67/// use cellular_raza_core::backend::chili::validate_map;
68///
69/// let new_map = std::collections::BTreeMap::from([
70///     (1_usize, std::collections::BTreeSet::from([0,2])),
71///     (2_usize, std::collections::BTreeSet::from([1,3])),
72///     (3_usize, std::collections::BTreeSet::from([2,0])),
73///     (0_usize, std::collections::BTreeSet::from([3,1])),
74/// ]);
75///
76/// let is_valid = validate_map(&new_map);
77/// assert_eq!(is_valid, true);
78/// ```
79#[cfg_attr(feature = "tracing", instrument(skip_all))]
80pub fn validate_map<I>(map: &std::collections::BTreeMap<I, BTreeSet<I>>) -> bool
81where
82    I: Eq + core::hash::Hash + Clone + Ord,
83{
84    for (index, neighbors) in map.iter() {
85        if neighbors.iter().any(|n| match map.get(n) {
86            Some(reverse_neighbors) => !reverse_neighbors.contains(index),
87            None => true,
88        }) {
89            return false;
90        }
91    }
92    true
93}
94
95/// Undirected graph
96///
97/// This datatype is planned to be used in order to create the simulation subdomains.
98/// There are no plans to extend the use of this object.
99// TODO actually use the specifically designed UDGraph instead of FromMap trait.
100pub struct UDGraph<I>(pub(crate) Vec<(I, I)>);
101
102impl<I> UDGraph<I> {
103    /// Construct a new undirected graph.
104    ///
105    /// ```
106    /// # use cellular_raza_core::backend::chili::UDGraph;
107    /// let ud_graph: UDGraph<usize> = UDGraph::new();
108    /// ```
109    pub fn new() -> Self {
110        Self(Vec::new())
111    }
112
113    /// Push an additional connection between nodes to the UDGraph
114    ///
115    /// This will return an option in either of two cases
116    /// 1. The connection is already contained in the Graph
117    /// 2. The nodes are identical, ie. `new_connection.0==new_connection.1`
118    ///
119    /// ```
120    /// # use cellular_raza_core::backend::chili::UDGraph;
121    /// let mut ud_graph = UDGraph::new();
122    /// assert_eq!(ud_graph.push((1, 2)), None);
123    /// assert_eq!(ud_graph.push((1, 3)), None);
124    ///
125    /// // These cases will not return `None` due to the
126    /// // specified reasons above
127    /// assert_eq!(ud_graph.push((1, 2)), Some((1, 2)));
128    /// assert_eq!(ud_graph.push((1, 1)), Some((1, 1)));
129    /// ```
130    pub fn push(&mut self, new_connection: (I, I)) -> Option<(I, I)>
131    where
132        I: PartialEq,
133    {
134        if new_connection.0 == new_connection.1 {
135            return Some(new_connection);
136        }
137        if self
138            .0
139            .iter()
140            .any(|connection| connection == &new_connection)
141        {
142            return Some(new_connection);
143        }
144        self.0.push(new_connection);
145        None
146    }
147
148    /// Extends the [UDGraph] with new connections
149    ///
150    /// Will return all connections which can not be added by the [push](UDGraph::push) method.
151    ///
152    /// ```
153    /// # use cellular_raza_core::backend::chili::UDGraph;
154    /// let mut ud_graph = UDGraph::new();
155    /// let new_connections = [
156    ///     (1_f64, 2_f64),
157    ///     (2_f64, 3_f64),
158    ///     (3_f64, 4_f64),
159    /// ];
160    /// let res = ud_graph.extend(new_connections);
161    /// assert_eq!(res, vec![]);
162    /// ```
163    pub fn extend<J>(&mut self, new_connections: J) -> Vec<(I, I)>
164    where
165        I: PartialEq,
166        J: IntoIterator<Item = (I, I)>,
167    {
168        new_connections
169            .into_iter()
170            .filter_map(|new_connection| self.push(new_connection))
171            .collect()
172    }
173
174    /// Clears the [UDGraph] thus removing all connections.
175    ///
176    /// See [std::vec::Vec::clear].
177    pub fn clear(&mut self) {
178        self.0.clear()
179    }
180
181    /// Drains the [UDGraph], thus returning an iterator over the specified elements.
182    ///
183    /// See [std::vec::Vec::drain].
184    pub fn drain<R>(&mut self, range: R) -> std::vec::Drain<'_, (I, I)>
185    where
186        R: core::ops::RangeBounds<usize>,
187    {
188        self.0.drain(range)
189    }
190
191    /// Returns all nodes currently stored in the [UDGraph].
192    ///
193    /// ```
194    /// # use cellular_raza_core::backend::chili::UDGraph;
195    /// let mut ud_graph = UDGraph::new();
196    /// ud_graph.push(("a", "s"));
197    /// ud_graph.push(("a", "K"));
198    /// ud_graph.push(("h", "s"));
199    ///
200    /// assert_eq!(ud_graph.nodes(), vec![&"a", &"s", &"K", &"h"]);
201    /// ```
202    pub fn nodes(&self) -> Vec<&I>
203    where
204        I: Clone + Eq + core::hash::Hash,
205    {
206        self.0
207            .iter()
208            .map(|(c1, c2)| [c1, c2].into_iter())
209            .flatten()
210            .unique()
211            .collect()
212    }
213}
214
215impl<I> IntoIterator for UDGraph<I> {
216    type Item = (I, I);
217    type IntoIter = std::vec::IntoIter<(I, I)>;
218
219    /// Consumes the graph and returns iterator over elements.
220    ///
221    /// See [std::vec::Vec::into_iter].
222    fn into_iter(self) -> std::vec::IntoIter<(I, I)> {
223        self.0.into_iter()
224    }
225}
226
227impl<I> UDGraph<I>
228where
229    I: core::hash::Hash + Clone + Eq + Ord,
230{
231    /// Convert the [UDGraph] into a regular [BTreeMap].
232    ///
233    /// ```
234    /// # use cellular_raza_core::backend::chili::UDGraph;
235    /// let mut ud_graph = UDGraph::new();
236    /// ud_graph.push((1, 2));
237    /// ud_graph.push((2, 3));
238    /// ud_graph.push((3, 1));
239    ///
240    /// let map = ud_graph.to_btree();
241    /// assert_eq!(map.keys().len(), 3);
242    /// ```
243    pub fn to_btree(self) -> BTreeMap<I, BTreeSet<I>> {
244        let mut map: BTreeMap<_, BTreeSet<I>> = self
245            .0
246            .iter()
247            .map(|x| {
248                [
249                    (x.0.clone(), BTreeSet::new()),
250                    (x.1.clone(), BTreeSet::new()),
251                ]
252                .into_iter()
253            })
254            .flatten()
255            .collect();
256        self.0.iter().for_each(|(c1, c2)| {
257            map.entry(c1.clone()).and_modify(|v| {
258                v.insert(c2.clone());
259            });
260            map.entry(c2.clone()).and_modify(|v| {
261                v.insert(c1.clone());
262            });
263        });
264        map
265    }
266}
267
268impl<I> core::ops::Deref for UDGraph<I> {
269    type Target = Vec<(I, I)>;
270
271    fn deref(&self) -> &Self::Target {
272        &self.0
273    }
274}
275
276impl<I> From<UDGraph<I>> for PhantomData<I> {
277    #[allow(unused)]
278    fn from(value: UDGraph<I>) -> Self {
279        PhantomData
280    }
281}
282
283/// Construct a BTreeMap of the type from a graph
284///
285/// The types should be connected according to the connections specified in the graph.
286/// Afterwards, this BTreeMap can over multiple threads and used since all components
287/// are connected according the the initial graph.
288pub trait BuildFromGraph<I>
289where
290    Self: Sized,
291    I: Clone + Eq + core::hash::Hash + Ord,
292{
293    /// Builds the BTreeMap
294    fn build_from_graph(graph: UDGraph<I>) -> Result<BTreeMap<I, Self>, IndexError>;
295}
296
297impl<I> BuildFromGraph<I> for PhantomData<I>
298where
299    Self: Sized,
300    I: Clone + Eq + core::hash::Hash + Ord,
301{
302    fn build_from_graph(graph: UDGraph<I>) -> Result<BTreeMap<I, Self>, IndexError> {
303        Ok(graph
304            .into_iter()
305            .map(|(key, _)| (key, PhantomData::<I>))
306            .collect())
307    }
308}
309
310// TODO migrate to FromGraph eventually!
311/// Constructs a collection of Items from a map (graph)
312pub trait FromMap<I>
313where
314    Self: Sized,
315{
316    /// [SubDomains](cellular_raza_concepts::SubDomain) can be neighboring each
317    /// other via complicated graphs.
318    /// An easy way to represent this is by using a [BTreeMap]. We want to create Barriers which match
319    /// the specified subdomain indices.
320    fn from_map(map: &BTreeMap<I, BTreeSet<I>>) -> Result<BTreeMap<I, Self>, IndexError>
321    where
322        I: Eq + core::hash::Hash + Clone + Ord;
323}
324
325impl<I> FromMap<I> for PhantomData<I> {
326    fn from_map(map: &BTreeMap<I, BTreeSet<I>>) -> Result<BTreeMap<I, Self>, IndexError>
327    where
328        I: Eq + core::hash::Hash + Clone + Ord,
329    {
330        Ok(map
331            .into_iter()
332            .map(|(key, _)| (key.clone(), PhantomData::<I>))
333            .collect())
334    }
335}
336
337/// Responsible for syncing the simulation between different threads.
338pub trait SyncSubDomains {
339    /// Function which forces connected syncers to wait for each other.
340    /// This approach does not necessarily require all threads to wait but can mean that
341    /// only depending threads wait for each other.
342    fn sync(&mut self) -> Result<(), SimulationError>;
343    /// TODO
344    fn store_error(
345        &mut self,
346        maybe_error: Result<(), SimulationError>,
347    ) -> Result<bool, SimulationError>;
348}
349
350impl<I> FromMap<I> for BarrierSync {
351    fn from_map(map: &BTreeMap<I, BTreeSet<I>>) -> Result<BTreeMap<I, Self>, IndexError>
352    where
353        I: Eq + core::hash::Hash + Clone + Ord,
354    {
355        let barrier = hurdles::Barrier::new(map.len());
356        let got_error = std::sync::Arc::new(AtomicBool::new(false));
357        let res = map
358            .keys()
359            .map(|i| {
360                (
361                    i.clone(),
362                    Self {
363                        barrier: barrier.clone(),
364                        got_error: std::sync::Arc::clone(&got_error),
365                    },
366                )
367            })
368            .collect();
369        Ok(res)
370    }
371}
372
373impl<I> BuildFromGraph<I> for BarrierSync
374where
375    I: Clone + Eq + core::hash::Hash + std::cmp::Ord,
376{
377    fn build_from_graph(graph: UDGraph<I>) -> Result<BTreeMap<I, Self>, IndexError> {
378        let barrier = hurdles::Barrier::new(graph.len());
379        let got_error = std::sync::Arc::new(AtomicBool::new(false));
380        let res = graph
381            .nodes()
382            .into_iter()
383            .map(|key| {
384                (
385                    key.clone(),
386                    Self {
387                        barrier: barrier.clone(),
388                        got_error: std::sync::Arc::clone(&got_error),
389                    },
390                )
391            })
392            .collect();
393        Ok(res)
394    }
395}
396
397impl SyncSubDomains for BarrierSync {
398    fn sync(&mut self) -> Result<(), SimulationError> {
399        self.barrier.wait();
400        match self.got_error.load(std::sync::atomic::Ordering::Relaxed) {
401            true => Err(SimulationError::OtherThreadError(format!(
402                "Another thread returned an error. Winding down."
403            ))),
404            false => Ok(()),
405        }
406    }
407
408    fn store_error(
409        &mut self,
410        maybe_error: Result<(), SimulationError>,
411    ) -> Result<bool, SimulationError> {
412        match maybe_error {
413            Ok(_) => Ok(false),
414            Err(SimulationError::OtherThreadError(_)) => Ok(true),
415            Err(x) => {
416                self.got_error
417                    .store(true, std::sync::atomic::Ordering::Relaxed);
418                self.barrier.wait();
419                Err(x)
420            }
421        }
422    }
423}
424
425/// Handles communications between different simulation processes.
426///
427/// Often times, information needs to be exchanged between threads.
428/// For example, positional and force information of cells living at the boundary.
429///
430/// The receiver is referenced by the index `I` and will obtain the message `T`.
431/// The trait was designed around the [crossbeam_channel] sender-receiver pair.
432/// However, it should allow for more generic setups where eg. information could be shared
433/// by different means such as sharing memory.
434///
435/// Between the [Communicator::send] and [Communicator::receive] method, a synchronization step
436/// needs to happen. Otherwise, dataraces can occur and invalidate given results.
437/// See the [Sync] trait for more details on syncing between threads.
438pub trait Communicator<I, T>
439where
440    Self: Sized,
441{
442    /// Sends information to a particular receiver.
443    fn send(&mut self, receiver: &I, message: T) -> Result<(), SimulationError>;
444    /// Receives the information previously sent
445    ///
446    /// When implementing this trait, make sure to empty any existing queue or shared memory.
447    /// Otherwise received messages will be stacking up, using up more memory+
448    /// and yielding wrong results.
449    fn receive(&mut self) -> Vec<T>;
450}
451
452/// Sender-Receiver [Communicator] based on [crossbeam_channel].
453///
454/// This struct contains one receiver and multiple senders.
455/// It can be constructed by using the [FromMap] trait.
456/// ```
457/// # use cellular_raza_core::backend::chili::{ChannelComm, Communicator, FromMap};
458/// let map = std::collections::BTreeMap::from([
459///     (0, std::collections::BTreeSet::from([1])),
460///     (1, std::collections::BTreeSet::from([0, 2])),
461///     (2, std::collections::BTreeSet::from([1])),
462/// ]);
463///
464/// // Construct multiple communicators from a given map.
465/// let mut channel_comms = ChannelComm::from_map(&map).unwrap();
466///
467/// // Send a message from 0 to 1
468/// channel_comms.get_mut(&0).unwrap().send(&1, true);
469/// channel_comms.get_mut(&0).unwrap().send(&1, false);
470/// // Receive all elements at communicator 1
471/// let elements = channel_comms.get_mut(&1).unwrap().receive();
472///
473/// assert_eq!(elements, vec![true, false]);
474/// ```
475#[derive(Clone)]
476pub struct ChannelComm<I, T> {
477    senders: std::collections::BTreeMap<I, crossbeam_channel::Sender<T>>,
478    receiver: crossbeam_channel::Receiver<T>,
479}
480
481impl<T, I> FromMap<I> for ChannelComm<I, T>
482where
483    I: Ord,
484{
485    fn from_map(map: &BTreeMap<I, BTreeSet<I>>) -> Result<BTreeMap<I, Self>, IndexError>
486    where
487        I: Clone + core::hash::Hash + Eq,
488    {
489        let channels: BTreeMap<_, _> = map
490            .keys()
491            .into_iter()
492            .map(|sender_key| {
493                let (s, r) = crossbeam_channel::unbounded::<T>();
494                (sender_key, (s, r))
495            })
496            .collect();
497        let mut comms = BTreeMap::new();
498        for key in map.keys().into_iter() {
499            let senders = map
500                .get(&key)
501                .ok_or(IndexError("Network of communicators could not be constructed due to incorrect entries in map".into()))?
502                .clone()
503                .into_iter()
504                .map(|connected_key| (connected_key.clone(), channels[&connected_key].0.clone())).collect();
505
506            let comm = ChannelComm {
507                senders,
508                receiver: channels[&key].1.clone(),
509            };
510            comms.insert(key.clone(), comm);
511        }
512        Ok(comms)
513    }
514}
515
516#[cfg(test)]
517mod test_channel_comm {
518    use itertools::Itertools;
519
520    use super::*;
521
522    #[test]
523    fn test_from_map() -> Result<(), IndexError> {
524        let map = BTreeMap::from([
525            (0_usize, BTreeSet::from([3, 1])),
526            (1_usize, BTreeSet::from([0, 2])),
527            (2_usize, BTreeSet::from([1, 3])),
528            (3_usize, BTreeSet::from([2, 0])),
529        ]);
530        assert!(validate_map(&map));
531        let channel_comms = ChannelComm::<usize, ()>::from_map(&map)?;
532        assert_eq!(channel_comms.len(), 4);
533        for i in 0..4 {
534            assert!(channel_comms.keys().contains(&i));
535        }
536        for i in 0..4 {
537            let higher = (i + 1) % 4;
538            let lower = (i + 5) % 4;
539            assert!(channel_comms[&i].senders.contains_key(&lower));
540            assert!(channel_comms[&i].senders.contains_key(&higher));
541        }
542        Ok(())
543    }
544
545    #[test]
546    fn test_from_map_2() -> Result<(), Box<dyn std::error::Error>> {
547        let map = BTreeMap::from([
548            (0, BTreeSet::from([1, 2, 3])),
549            (1, BTreeSet::from([0, 2, 3])),
550            (2, BTreeSet::from([0, 1, 3])),
551            (3, BTreeSet::from([0, 1, 2])),
552        ]);
553        assert!(validate_map(&map));
554        let channel_comms = ChannelComm::<usize, Option<()>>::from_map(&map)?;
555        for i in 0..4 {
556            assert!(channel_comms.keys().contains(&i));
557        }
558        for i in 0..4 {
559            for j in 0..4 {
560                let contains = channel_comms[&i].senders.keys().contains(&j);
561                assert_eq!(contains, i != j);
562            }
563        }
564        Ok(())
565    }
566
567    fn from_map_n_line(n: usize) -> Result<(), Box<dyn std::error::Error>> {
568        if n <= 1 {
569            return Ok(());
570        }
571        let mut map =
572            BTreeMap::from([(0, BTreeSet::from([1, n])), (n, BTreeSet::from([n - 1, 0]))]);
573        for i in 1..n {
574            map.insert(i, BTreeSet::from([i - 1, i + 1]));
575        }
576        assert!(validate_map(&map));
577        let mut channel_comms = ChannelComm::<usize, usize>::from_map(&map)?;
578        channel_comms
579            .iter_mut()
580            .map(|(key, comm)| {
581                let recv_key = (key + 1) % (n + 1);
582                comm.send(&recv_key, *key)
583            })
584            .collect::<Result<Vec<_>, _>>()?;
585        channel_comms.iter_mut().for_each(|(key, comm)| {
586            let results = comm.receive();
587            assert!(results.len() > 0);
588            if key > &0 {
589                assert_eq!(results, vec![key - 1]);
590            } else {
591                assert_eq!(results, vec![n]);
592            }
593        });
594        Ok(())
595    }
596
597    #[test]
598    fn from_map_lines() {
599        for i in 2..100 {
600            from_map_n_line(i).unwrap();
601        }
602    }
603
604    #[test]
605    fn test_send() -> Result<(), Box<dyn std::error::Error>> {
606        let map = BTreeMap::from([
607            (1_usize, BTreeSet::from([2])),
608            (2_usize, BTreeSet::from([1])),
609        ]);
610        let mut channel_comms = ChannelComm::<usize, bool>::from_map(&map)?;
611        channel_comms.get_mut(&1).unwrap().send(&2, true)?;
612        channel_comms.get_mut(&2).unwrap().send(&1, false)?;
613        Ok(())
614    }
615
616    #[test]
617    fn test_empty_receive() -> Result<(), Box<dyn std::error::Error>> {
618        let map = BTreeMap::from([
619            (1_usize, BTreeSet::from([2])),
620            (2_usize, BTreeSet::from([1])),
621        ]);
622        let mut channel_comms = ChannelComm::<usize, f64>::from_map(&map)?;
623        for (_, comm) in channel_comms.iter_mut() {
624            let received_elements = comm.receive().into_iter().collect::<Vec<_>>();
625            assert_eq!(received_elements.len(), 0);
626        }
627        Ok(())
628    }
629
630    #[test]
631    fn test_send_receive() -> Result<(), Box<dyn std::error::Error>> {
632        let map = BTreeMap::from([
633            (0, BTreeSet::from([1, 2])),
634            (1, BTreeSet::from([0, 2])),
635            (2, BTreeSet::from([0, 1])),
636        ]);
637        let mut channel_comms = ChannelComm::from_map(&map)?;
638
639        // Send a dummy value
640        for (index, comm) in channel_comms.iter_mut() {
641            let next_index = (index + 1) % map.len();
642            comm.send(&next_index, next_index as f64)?;
643        }
644
645        // Receive the value
646        for (index, comm) in channel_comms.iter_mut() {
647            let received_elements = comm.receive().into_iter().collect::<Vec<_>>();
648            assert_eq!(received_elements, vec![*index as f64]);
649        }
650        Ok(())
651    }
652
653    #[test]
654    fn test_send_plain_voxel() -> Result<(), Box<dyn std::error::Error>> {
655        use crate::backend::chili::SubDomainPlainIndex;
656        let map = BTreeMap::from([
657            (
658                SubDomainPlainIndex(0),
659                BTreeSet::from([SubDomainPlainIndex(1), SubDomainPlainIndex(2)]),
660            ),
661            (
662                SubDomainPlainIndex(1),
663                BTreeSet::from([SubDomainPlainIndex(0), SubDomainPlainIndex(2)]),
664            ),
665            (
666                SubDomainPlainIndex(2),
667                BTreeSet::from([SubDomainPlainIndex(0), SubDomainPlainIndex(1)]),
668            ),
669        ]);
670        let mut channel_comms = ChannelComm::from_map(&map)?;
671
672        // Send a dummy value
673        for (index, comm) in channel_comms.iter_mut() {
674            let index = index.0;
675            let next_index = SubDomainPlainIndex((index + 1) % map.len());
676            comm.send(&next_index, next_index)?;
677        }
678
679        // Receive the value
680        for (index, comm) in channel_comms.iter_mut() {
681            let received_elements = comm.receive().into_iter().collect::<Vec<_>>();
682            assert_eq!(received_elements, vec![*index]);
683        }
684
685        Ok(())
686    }
687}
688
689impl<I, T> Communicator<I, T> for ChannelComm<I, T>
690where
691    I: core::hash::Hash + Eq + Ord,
692{
693    fn receive(&mut self) -> Vec<T> {
694        self.receiver.try_iter().collect()
695    }
696
697    fn send(&mut self, receiver: &I, message: T) -> Result<(), SimulationError> {
698        let sender = self
699            .senders
700            .get(&receiver)
701            .ok_or(super::IndexError(format!(
702                "could not find specified receiver"
703            )))?;
704        sender.send(message)?;
705        Ok(())
706    }
707}
708
709#[doc(hidden)]
710#[allow(unused)]
711mod test_derive_communicator {
712    /// ```
713    /// use cellular_raza_core::backend::chili::{SimulationError, ChannelComm, Communicator};
714    /// #[derive(Communicator)]
715    /// #[CommunicatorCorePath(cellular_raza_core)]
716    /// struct MyComm<I, T> {
717    ///     #[Comm(I, T)]
718    ///     comm: ChannelComm<I, T>
719    /// }
720    /// ```
721    fn default() {}
722
723    /// ```
724    /// use cellular_raza_core::backend::chili::{SimulationError, ChannelComm, Communicator};
725    /// #[derive(Communicator)]
726    /// #[CommunicatorCorePath(cellular_raza_core)]
727    /// struct MyDouble<I> {
728    ///     #[Comm(I, String)]
729    ///     comm1: ChannelComm<I, String>,
730    ///     #[Comm(I, f64)]
731    ///     comm2: ChannelComm<I, f64>,
732    /// }
733    /// ```
734    fn two_communicators_explicit() {}
735
736    /// ```
737    /// use cellular_raza_core::backend::chili::{SimulationError, ChannelComm, Communicator};
738    /// struct Message<T>(T);
739    /// #[derive(Communicator)]
740    /// #[CommunicatorCorePath(cellular_raza_core)]
741    /// struct MyDouble<I, T> {
742    ///     #[Comm(I, Message<T>)]
743    ///     comm1: ChannelComm<I, Message<T>>,
744    ///     #[Comm(I, f64)]
745    ///     comm2: ChannelComm<I, f64>,
746    /// }
747    /// ```
748    fn two_communicators_generic_one() {}
749}
750
751#[doc(hidden)]
752#[allow(unused)]
753mod test_derive_from_map {
754    /// ```
755    /// use cellular_raza_core::{
756    ///     backend::chili::{ChannelComm, FromMap},
757    /// };
758    /// use cellular_raza_concepts::IndexError;
759    /// #[derive(FromMap)]
760    /// #[FromMapIndex(usize)]
761    /// struct MyNewComm {
762    ///     channel_comm_1: ChannelComm<usize, String>,
763    ///     channel_comm_2: ChannelComm<usize, (f64, f32)>,
764    /// }
765    /// ```
766    fn default() {}
767
768    /// ```
769    /// use cellular_raza_core::{
770    ///     backend::chili::{ChannelComm, FromMap},
771    /// };
772    /// use cellular_raza_concepts::IndexError;
773    /// #[derive(FromMap)]
774    /// #[FromMapIndex(I)]
775    /// struct MyNewComm<I> {
776    ///     channel_comm_1: ChannelComm<I, String>,
777    ///     channel_comm_2: ChannelComm<I, (f64, f32)>,
778    /// }
779    /// ```
780    fn generic_index() {}
781
782    /// ```
783    /// use cellular_raza_core::{
784    ///     backend::chili::{ChannelComm, FromMap},
785    /// };
786    /// use cellular_raza_concepts::IndexError;
787    /// #[derive(FromMap)]
788    /// #[FromMapIndex(i16)]
789    /// struct MyNewComm<T>
790    /// where
791    ///     T: Clone,
792    /// {
793    ///     channel_comm_1: ChannelComm<i16, T>,
794    ///     channel_comm_2: ChannelComm<i16, (f64, f32)>,
795    /// }
796    /// ```
797    fn where_clause() {}
798
799    /// ```
800    /// use cellular_raza_core::{
801    ///     backend::chili::{ChannelComm, FromMap},
802    /// };
803    /// use cellular_raza_concepts::IndexError;
804    /// #[derive(FromMap)]
805    /// #[FromMapIndex(I)]
806    /// struct MyNewComm<I>
807    /// where
808    ///     I: std::fmt::Display,
809    /// {
810    ///     channel_comm_1: ChannelComm<I, f64>,
811    ///     channel_comm_2: ChannelComm<I, (f64, f32)>,
812    /// }
813    /// ```
814    fn where_clause_on_index() {}
815}
816
817#[doc(hidden)]
818#[allow(unused)]
819mod test_build_communicator {
820    macro_rules! test_build_communicator(
821        (
822            name:$func_name:ident,
823            aspects:[$($asp:ident),*]
824        ) => {
825            /// ```
826            /// use cellular_raza_core::backend::chili::build_communicator;
827            /// build_communicator!(
828            ///     aspects: [
829            #[doc = stringify!($($asp),*)]
830            ///     ],
831            ///     core_path: cellular_raza_core,
832            ///     communicator_name: __MyComm,
833            /// );
834            /// let mut map = std::collections::BTreeMap::new();
835            /// map.insert(0, std::collections::BTreeSet::from([1]));
836            /// map.insert(1, std::collections::BTreeSet::from([0]));
837            /// use cellular_raza_core::backend::chili::{ReactionsContactInformation, FromMap,
838            /// Communicator, PosInformation, ForceInformation, VoxelPlainIndex, SendCell};
839            /// let mut communicator = __MyComm::from_map(&map).unwrap().remove(&0).unwrap();
840            /// macro_rules! test_aspect (
841            ///     (Mechanics) => {
842            ///         communicator.send(&1, SendCell(
843            ///             VoxelPlainIndex::new(1),
844            ///             format!("MyCell"),
845            ///             format!("AuxStorage")
846            ///         ));
847            ///     };
848            ///     (Interaction) => {
849            ///         communicator.send(&1, PosInformation {
850            ///             pos: 1u8,
851            ///             vel: 1.0,
852            ///             info: (),
853            ///             cell_index_in_vector: 1,
854            ///             index_sender: VoxelPlainIndex::new(0),
855            ///             index_receiver: VoxelPlainIndex::new(1),
856            ///         });
857            ///         communicator.send(&1, ForceInformation {
858            ///             force: 0.1,
859            ///             cell_index_in_vector: 0,
860            ///             index_sender: VoxelPlainIndex::new(0),
861            ///         });
862            ///     };
863            ///     (ReactionsContact) => {
864            ///         communicator.send(&1, ReactionsContactInformation {
865            ///             pos: 1u8,
866            ///             intracellular: [0.0, 1.0],
867            ///             info: "hi",
868            ///             cell_index_in_vector: 0,
869            ///             index_sender: VoxelPlainIndex::new(0),
870            ///             index_receiver: VoxelPlainIndex::new(1),
871            ///         });
872            ///     };
873            /// );
874            #[doc = concat!($(
875                concat!("test_aspect!(", stringify!($asp), ");")
876            ,)*)]
877            /// ```
878            #[allow(non_snake_case)]
879            fn $func_name () {}
880        };
881    );
882
883    cellular_raza_core_proc_macro::run_test_for_aspects!(
884        test: test_build_communicator,
885        aspects: [Mechanics, Interaction, ReactionsContact]
886    );
887
888    /// ```compile_fail
889    /// build_communicator!(
890    ///     aspects: [Mechanics, Cycle],
891    ///     communicator_name: __MyComm,
892    /// );
893    /// ```
894    fn without_path() {}
895}
896
897#[cfg(test)]
898mod test_sync {
899    use super::*;
900    use std::sync::*;
901
902    fn test_single_map<S>(map: BTreeMap<usize, BTreeSet<usize>>)
903    where
904        S: 'static + SyncSubDomains + FromMap<usize> + Send + Sync,
905    {
906        // Define the number of threads and iterations to use
907        let n_iterations = 1_000;
908        let n_threads = map.len();
909
910        // We count the number of iterations via this mutex.
911        // Individual threads will increment their counter by +1 each time they are executed
912        let iteration_counter =
913            Arc::new(Mutex::new(Vec::from_iter((0..n_threads).map(|_| 0_usize))));
914
915        // Create a barrier from which we
916        let syncers = S::from_map(&map).unwrap();
917        let handles = syncers
918            .into_iter()
919            .map(|(n_thread, mut syncer)| {
920                let iteration_counter_thread = Arc::clone(&iteration_counter);
921                std::thread::spawn(move || {
922                    for n_iteration in 0..n_iterations {
923                        syncer.sync().unwrap();
924                        iteration_counter_thread.lock().unwrap()[n_thread] += 1;
925                        syncer.sync().unwrap();
926                        let current_value = iteration_counter_thread.lock().unwrap().clone();
927                        assert_eq!(current_value, vec![n_iteration + 1; n_threads]);
928                    }
929                })
930            })
931            .collect::<Vec<_>>();
932
933        for handle in handles.into_iter() {
934            handle.join().unwrap();
935        }
936    }
937
938    fn test_multiple_maps<S>()
939    where
940        S: 'static + SyncSubDomains + FromMap<usize> + Send + Sync,
941    {
942        let map0 = BTreeMap::from_iter([(0, BTreeSet::from([1])), (1, BTreeSet::from([0]))]);
943        test_single_map::<S>(map0);
944
945        let map1 = BTreeMap::from_iter([
946            (0, BTreeSet::from([1, 2])),
947            (1, BTreeSet::from([0, 2])),
948            (2, BTreeSet::from([0, 1])),
949        ]);
950        test_single_map::<S>(map1);
951
952        let map2 = BTreeMap::from_iter([
953            (0, BTreeSet::from([1, 2, 3])),
954            (1, BTreeSet::from([0, 2, 3])),
955            (2, BTreeSet::from([0, 1, 3])),
956            (3, BTreeSet::from([0, 1, 2])),
957        ]);
958        test_single_map::<S>(map2);
959
960        let map3 = BTreeMap::from_iter([
961            (0, BTreeSet::from([1, 2])),
962            (1, BTreeSet::from([0, 3])),
963            (2, BTreeSet::from([0, 3])),
964            (3, BTreeSet::from([1, 2])),
965        ]);
966        test_single_map::<S>(map3);
967
968        let map4 = BTreeMap::from_iter([
969            (0, BTreeSet::from([1])),
970            (1, BTreeSet::from([2])),
971            (2, BTreeSet::from([3])),
972            (3, BTreeSet::from([4])),
973            (4, BTreeSet::from([0])),
974        ]);
975        test_single_map::<BarrierSync>(map4);
976    }
977
978    #[test]
979    fn barrier_sync() {
980        test_multiple_maps::<BarrierSync>();
981    }
982}