cellular_raza_core/backend/cpu_os_threads/
config.rs

1use super::{Agent, ForceBound, PositionBound, VelocityBound};
2use crate::backend::cpu_os_threads::domain_decomposition::AuxiliaryCellPropertyStorage;
3use crate::storage::StorageManager;
4use cellular_raza_concepts::domain_old::*;
5use cellular_raza_concepts::{CellBox, VoxelPlainIndex};
6
7use super::domain_decomposition::{
8    ConcentrationBoundaryInformation, DomainBox, ForceInformation, IndexBoundaryInformation,
9    MultiVoxelContainer, PlainIndex, PosInformation, VoxelBox,
10};
11use super::supervisor::ControllerBox;
12use super::supervisor::SimulationSupervisor;
13use cellular_raza_concepts::CellIdentifier;
14
15use std::collections::{BTreeMap, HashMap};
16use std::marker::PhantomData;
17
18use crossbeam_channel::{Receiver, Sender, unbounded};
19use hurdles::Barrier;
20
21use serde::{Deserialize, Serialize};
22
23use rayon::prelude::*;
24
25/// Contains non-volatile configurations such as display settings etc.
26#[derive(Serialize, Deserialize, Clone, Debug)]
27pub struct SimulationConfig {
28    /// Shows a progressbar when running the simulation.
29    pub progressbar: Option<String>,
30}
31
32impl Default for SimulationConfig {
33    fn default() -> Self {
34        SimulationConfig { progressbar: None }
35    }
36}
37
38/// # Store meta parameters for simulation
39#[derive(Clone, Serialize, Deserialize)]
40pub struct SimulationMetaParams {
41    /// Number of threads to use for parallelization. This number may be limited by the
42    /// available number of voxels.
43    pub n_threads: usize,
44    /// Sets the initial random seed of whole simulation
45    pub rng_seed: u64,
46}
47
48impl Default for SimulationMetaParams {
49    fn default() -> Self {
50        Self {
51            n_threads: 1,
52            rng_seed: 0,
53        }
54    }
55}
56
57// TODO rethink how to specify time points to save
58// we need to frequently save cells and environment
59// Sometimes we need full snapshots for recovery purposes
60/// Contains information about the discretization of time and when to save results.
61#[derive(Clone, Serialize, Deserialize)]
62pub struct TimeSetup {
63    /// Initial time point of the simulation.
64    pub t_start: f64,
65    /// Time points at which to evaluate the simulation. The additional [bool]
66    /// determines if we also save the results.
67    pub t_eval: Vec<(f64, bool)>,
68}
69
70/// # Complete Set of parameters controlling execution flow of simulation
71#[derive(Clone, Serialize, Deserialize)]
72pub struct SimulationSetup<Dom, Cel, Cont = ()> {
73    pub(crate) domain: Dom,
74    // TODO use something like an iterator in this place to possibly process in parallel
75    pub(crate) cells: Vec<Cel>,
76    pub(crate) time: TimeSetup,
77    pub(crate) meta_params: SimulationMetaParams,
78    pub(crate) storage: crate::storage::StorageBuilder<true>,
79    pub(crate) controller: Cont,
80}
81
82#[macro_export]
83#[doc(hidden)]
84macro_rules! create_simulation_setup (
85    (
86        Domain: $domain:expr,
87        Cells: $cells:expr,
88        Time: $time:expr,
89        MetaParams: $meta_params:expr,
90        Storage: $storage:expr,
91        Controller: $controller:expr$(,)?
92    ) =>  {
93        SimulationSetup::new(
94            $domain,
95            $cells,
96            $time,
97            $meta_params,
98            $storage,
99            $controller,
100        )
101    };
102
103    (
104        Domain: $domain:expr,
105        Cells: $cells:expr,
106        Time: $time:expr,
107        MetaParams: $meta_params:expr,
108        Storage: $storage:expr$(,)?
109    ) =>  {
110        create_simulation_setup!(
111            Domain: $domain,
112            Cells: $cells,
113            Time: $time,
114            MetaParams: $meta_params,
115            Storage: $storage,
116            Controller: (),
117        )
118    };
119);
120#[doc(inline)]
121pub use crate::create_simulation_setup;
122
123impl<Dom, Cel, Cont> SimulationSetup<Dom, Cel, Cont> {
124    /// Construct a new [SimulationSetup] which is required to initialize the [SimulationSupervisor].
125    pub fn new<V>(
126        domain: Dom,
127        cells: V,
128        time: TimeSetup,
129        meta_params: SimulationMetaParams,
130        storage: crate::storage::StorageBuilder<true>,
131        controller: Cont,
132    ) -> SimulationSetup<Dom, Cel, Cont>
133    where
134        V: IntoIterator<Item = Cel>,
135    {
136        SimulationSetup {
137            domain,
138            cells: cells.into_iter().collect(),
139            time,
140            meta_params,
141            storage,
142            controller,
143        }
144    }
145}
146
147/// Image type used for saving results to a file.
148#[derive(Clone, Debug, Deserialize, Serialize)]
149pub enum ImageType {
150    /// Saves images as ".png" file.
151    BitMap,
152    // TODO
153    // Svg,
154}
155
156/// Contains settings for plotting results.
157#[derive(Serialize, Deserialize, Clone, Debug)]
158pub struct PlottingConfig {
159    /// Image size in pixels. The plotting function of the underlying simulation domain
160    /// may choose to modifiy this parameter.
161    pub image_size: u32,
162    /// The number of threads used for plotting. Notice that this number is not the same as
163    /// in [SimulationMetaParams].
164    pub n_threads: Option<usize>,
165    /// The [ImageType] to export.
166    pub image_type: ImageType,
167    /// Shows a progressbar while exporting images.
168    pub show_progressbar: bool,
169}
170
171impl Default for PlottingConfig {
172    fn default() -> Self {
173        PlottingConfig {
174            image_size: 1000,
175            n_threads: None,
176            image_type: ImageType::BitMap,
177            show_progressbar: true,
178        }
179    }
180}
181
182/// Contains methods to modify voxels when initializing the simulation domain.
183#[derive(Clone)]
184pub struct Strategies<'a, Vox>
185where
186    Vox: 'a + Clone,
187{
188    /// Strategies for the modification of existing voxels before the simulation has started.
189    pub voxel_definition_strategies: &'a dyn Fn(&mut Vox),
190}
191
192impl<
193    Pos,
194    For,
195    Inf,
196    Vel,
197    ConcVecExtracellular,
198    ConcBoundaryExtracellular,
199    ConcVecIntracellular,
200    Cel,
201    Ind,
202    Vox,
203    Dom,
204    Cont,
205    Obs,
206>
207    SimulationSupervisor<
208        MultiVoxelContainer<
209            Ind,
210            Pos,
211            Vel,
212            For,
213            Inf,
214            Vox,
215            Dom,
216            Cel,
217            ConcVecExtracellular,
218            ConcBoundaryExtracellular,
219            ConcVecIntracellular,
220        >,
221        Dom,
222        Cel,
223        Cont,
224        Obs,
225    >
226where
227    Dom: Domain<Cel, Ind, Vox> + Clone + 'static,
228    Ind: Index + 'static,
229    Pos: Serialize + for<'a> Deserialize<'a> + PositionBound + 'static + std::fmt::Debug,
230    For: Serialize + for<'a> Deserialize<'a> + ForceBound + 'static,
231    Vel: Serialize + for<'a> Deserialize<'a> + VelocityBound + 'static,
232    ConcVecExtracellular: Serialize + for<'a> Deserialize<'a>,
233    ConcBoundaryExtracellular: Serialize + for<'a> Deserialize<'a>,
234    ConcVecIntracellular: Serialize + for<'a> Deserialize<'a> + num::Zero,
235    Vox: Voxel<Ind, Pos, Vel, For> + Clone + 'static,
236    Cel: Agent<Pos, Vel, For, Inf> + 'static,
237    VoxelBox<
238        Ind,
239        Pos,
240        Vel,
241        For,
242        Vox,
243        Cel,
244        ConcVecExtracellular,
245        ConcBoundaryExtracellular,
246        ConcVecIntracellular,
247    >: Clone,
248    Cont: Serialize + for<'a> Deserialize<'a>,
249{
250    /// Construct a new [SimulationSupervisor] from a given [SimulationSetup].
251    pub fn initialize_from_setup(
252        setup: SimulationSetup<Dom, Cel, Cont>,
253    ) -> SimulationSupervisor<
254        MultiVoxelContainer<
255            Ind,
256            Pos,
257            Vel,
258            For,
259            Inf,
260            Vox,
261            Dom,
262            Cel,
263            ConcVecExtracellular,
264            ConcBoundaryExtracellular,
265            ConcVecIntracellular,
266        >,
267        Dom,
268        Cel,
269        Cont,
270        Obs,
271    >
272    where
273        Cel: Sized,
274    {
275        let no_strategy = |_: &mut Vox| {};
276        Self::initialize_with_strategies(
277            setup,
278            Strategies {
279                voxel_definition_strategies: &no_strategy,
280            },
281        )
282    }
283
284    /// Construct a new [SimulationSupervisor] from a given [SimulationSetup] with [Strategies].
285    pub fn initialize_with_strategies(
286        setup: SimulationSetup<Dom, Cel, Cont>,
287        strategies: Strategies<Vox>,
288    ) -> SimulationSupervisor<
289        MultiVoxelContainer<
290            Ind,
291            Pos,
292            Vel,
293            For,
294            Inf,
295            Vox,
296            Dom,
297            Cel,
298            ConcVecExtracellular,
299            ConcBoundaryExtracellular,
300            ConcVecIntracellular,
301        >,
302        Dom,
303        Cel,
304        Cont,
305        Obs,
306    >
307    where
308        Cel: Sized,
309    {
310        // Create groups of voxels to put into our MultiVelContainers
311        let voxel_chunks = <Dom>::generate_contiguous_multi_voxel_regions(
312            &setup.domain,
313            setup.meta_params.n_threads,
314        )
315        .unwrap();
316        let n_threads = voxel_chunks.len();
317
318        let convert_to_plain_index: BTreeMap<Ind, PlainIndex> = setup
319            .domain
320            .get_all_indices()
321            .into_iter()
322            .enumerate()
323            .map(|(count, ind)| (ind, count as PlainIndex))
324            .collect();
325        let convert_to_index: BTreeMap<PlainIndex, Ind> = convert_to_plain_index
326            .iter()
327            .map(|(i, j)| (j.clone(), i.clone()))
328            .collect();
329
330        let mut index_to_thread = BTreeMap::<Ind, usize>::new();
331        let mut plain_index_to_thread = BTreeMap::<PlainIndex, usize>::new();
332        for (n_thread, chunk) in voxel_chunks.iter().enumerate() {
333            for (ind, _) in chunk.iter() {
334                index_to_thread.insert(ind.clone(), n_thread);
335                plain_index_to_thread.insert(convert_to_plain_index[&ind], n_thread);
336            }
337        }
338
339        // Create MultiVelContainer from voxel chunks
340        let multivoxelcontainers;
341
342        // Create sender receiver pairs for all threads
343        let sender_receiver_pairs_cell: Vec<(
344            Sender<(
345                CellBox<Cel>,
346                AuxiliaryCellPropertyStorage<Pos, Vel, For, ConcVecIntracellular>,
347            )>,
348            Receiver<(
349                CellBox<Cel>,
350                AuxiliaryCellPropertyStorage<Pos, Vel, For, ConcVecIntracellular>,
351            )>,
352        )> = (0..n_threads).map(|_| unbounded()).collect();
353        let sender_receiver_pairs_pos: Vec<(
354            Sender<PosInformation<Pos, Vel, Inf>>,
355            Receiver<PosInformation<Pos, Vel, Inf>>,
356        )> = (0..n_threads).map(|_| unbounded()).collect();
357        let sender_receiver_pairs_force: Vec<(
358            Sender<ForceInformation<For>>,
359            Receiver<ForceInformation<For>>,
360        )> = (0..n_threads).map(|_| unbounded()).collect();
361
362        let sender_receiver_pairs_boundary_concentrations: Vec<(
363            Sender<ConcentrationBoundaryInformation<ConcBoundaryExtracellular, Ind>>,
364            Receiver<ConcentrationBoundaryInformation<ConcBoundaryExtracellular, Ind>>,
365        )> = (0..n_threads).map(|_| unbounded()).collect();
366        let sender_receiver_pairs_boundary_index: Vec<(
367            Sender<IndexBoundaryInformation<Ind>>,
368            Receiver<IndexBoundaryInformation<Ind>>,
369        )> = (0..n_threads).map(|_| unbounded()).collect();
370
371        // Create a barrier to synchronize all threads
372        let barrier = Barrier::new(n_threads);
373
374        // Create an intermediate mapping just for this setup
375        // Map voxel index to thread number
376        let mut plain_index_to_thread: BTreeMap<PlainIndex, usize> = BTreeMap::new();
377        for (i, chunk) in voxel_chunks.iter().enumerate() {
378            for (index, _) in chunk {
379                plain_index_to_thread.insert(convert_to_plain_index[&index], i);
380            }
381        }
382
383        // Sort cells into correct voxels
384        let n_chunks = voxel_chunks.len();
385        let chunk_size = (setup.cells.len() as f64 / n_threads as f64).ceil() as usize;
386
387        let mut sorted_cells = setup
388            .cells
389            .into_par_iter()
390            .enumerate()
391            .chunks(chunk_size)
392            .map(|cell_chunk| {
393                let mut cs = BTreeMap::<usize, BTreeMap<PlainIndex, Vec<(usize, Cel)>>>::new();
394                for cell in cell_chunk.into_iter() {
395                    let index = setup.domain.get_voxel_index(&cell.1);
396                    let plain_index = convert_to_plain_index[&index];
397                    let id_thread = plain_index_to_thread[&plain_index];
398                    match cs.get_mut(&id_thread) {
399                        Some(index_to_cells) => match index_to_cells.get_mut(&plain_index) {
400                            Some(cs) => cs.push(cell),
401                            None => {
402                                index_to_cells.insert(plain_index, vec![cell]);
403                            }
404                        },
405                        None => {
406                            cs.insert(id_thread, BTreeMap::from([(plain_index, vec![cell])]));
407                        }
408                    }
409                }
410                cs
411            })
412            .reduce(
413                || {
414                    (0..n_chunks)
415                        .map(|i| (i, BTreeMap::new()))
416                        .collect::<BTreeMap<usize, BTreeMap<PlainIndex, Vec<(usize, Cel)>>>>()
417                },
418                |mut acc, x| {
419                    for (id_thread, idc) in x.into_iter() {
420                        for (index, mut cells) in idc.into_iter() {
421                            match acc.get_mut(&id_thread) {
422                                Some(index_to_cells) => match index_to_cells.get_mut(&index) {
423                                    Some(cs) => cs.append(&mut cells),
424                                    None => {
425                                        index_to_cells.insert(index, cells);
426                                    }
427                                },
428                                None => {
429                                    acc.insert(id_thread, BTreeMap::from([(index, cells)]));
430                                }
431                            }
432                        }
433                    }
434                    return acc;
435                },
436            );
437
438        let voxel_and_raw_cells: Vec<(
439            Vec<(PlainIndex, Vox)>,
440            BTreeMap<PlainIndex, Vec<(usize, Cel)>>,
441        )> = voxel_chunks
442            .into_iter()
443            .enumerate()
444            .map(|(i, chunk)| {
445                (
446                    chunk
447                        .into_iter()
448                        .map(|(ind, vox)| (convert_to_plain_index[&ind], vox))
449                        .collect(),
450                    sorted_cells.remove(&i).unwrap(),
451                )
452            })
453            .collect();
454
455        let voxel_and_cell_boxes: Vec<(
456            Vec<(PlainIndex, Vox)>,
457            BTreeMap<PlainIndex, Vec<CellBox<Cel>>>,
458        )> = voxel_and_raw_cells
459            .into_iter()
460            .map(|(chunk, sorted_cells)| {
461                let res = (
462                    chunk,
463                    sorted_cells
464                        .into_iter()
465                        .map(|(ind, mut cells)| {
466                            cells.sort_by(|(i, _), (j, _)| i.cmp(&j));
467                            (
468                                ind,
469                                cells
470                                    .into_iter()
471                                    .enumerate()
472                                    .map(|(n_cell, (_, cell))| {
473                                        CellBox::new(
474                                            VoxelPlainIndex(ind),
475                                            n_cell as u64,
476                                            cell,
477                                            None,
478                                        )
479                                    })
480                                    .collect(),
481                            )
482                        })
483                        .collect(),
484                );
485                res
486            })
487            .collect();
488
489        // # Create all storage solutions
490        // First initialize the builder
491        let builder = setup.storage.init();
492        let meta_infos_builder = builder
493            .clone()
494            .suffix(builder.get_suffix().join("meta_infos"));
495        let meta_infos =
496            StorageManager::<(), SimulationSetup<DomainBox<Dom>, Cel, Cont>>::open_or_create(
497                meta_infos_builder,
498                0,
499            )
500            .unwrap();
501
502        // Create all multivoxelcontainers
503        use rand::{RngCore, SeedableRng};
504        use rand_chacha::ChaCha8Rng;
505        let mut rng_generator = ChaCha8Rng::seed_from_u64(setup.meta_params.rng_seed.clone());
506        multivoxelcontainers = voxel_and_cell_boxes
507            .into_iter()
508            .enumerate()
509            .map(|(i, (chunk, mut index_to_cells))| {
510                let mut voxels: BTreeMap<
511                    PlainIndex,
512                    VoxelBox<
513                        Ind,
514                        Pos,
515                        Vel,
516                        For,
517                        Vox,
518                        Cel,
519                        ConcVecExtracellular,
520                        ConcBoundaryExtracellular,
521                        ConcVecIntracellular,
522                    >,
523                > = chunk
524                    .clone()
525                    .into_iter()
526                    .map(|(plain_index, voxel)| {
527                        let cells = match index_to_cells.remove(&plain_index) {
528                            Some(cs) => cs,
529                            None => Vec::new(),
530                        };
531                        let neighbors = setup
532                            .domain
533                            .get_neighbor_voxel_indices(&convert_to_index[&plain_index])
534                            .into_iter()
535                            .map(|i| convert_to_plain_index[&i])
536                            .collect::<Vec<_>>();
537                        let vbox = VoxelBox::<
538                            Ind,
539                            Pos,
540                            Vel,
541                            For,
542                            Vox,
543                            Cel,
544                            ConcVecExtracellular,
545                            ConcBoundaryExtracellular,
546                            ConcVecIntracellular,
547                        >::new(
548                            plain_index,
549                            convert_to_index[&plain_index].clone(),
550                            voxel,
551                            neighbors,
552                            cells,
553                            rng_generator.next_u64(),
554                        );
555                        (plain_index, vbox)
556                    })
557                    .collect();
558
559                // Create non-occupied voxels
560                for (ind, _) in voxels.iter() {
561                    match index_to_cells.get(&ind) {
562                        Some(_) => (),
563                        None => {
564                            index_to_cells.insert(ind.clone(), Vec::new());
565                        }
566                    }
567                }
568
569                // Quick macro to create senders
570                macro_rules! create_senders {
571                    ($sr_pairs: expr) => {
572                        chunk
573                            .clone()
574                            .into_iter()
575                            .map(|(plain_index, _)| {
576                                setup
577                                    .domain
578                                    .get_neighbor_voxel_indices(&convert_to_index[&plain_index])
579                                    .into_iter()
580                                    .map(|ind| {
581                                        (
582                                            index_to_thread[&ind],
583                                            $sr_pairs[index_to_thread[&ind]].0.clone(),
584                                        )
585                                    })
586                            })
587                            .flatten()
588                            .collect::<HashMap<usize, _>>()
589                    };
590                }
591
592                let senders_cell = create_senders!(sender_receiver_pairs_cell);
593                let senders_pos = create_senders!(sender_receiver_pairs_pos);
594                let senders_force = create_senders!(sender_receiver_pairs_force);
595                let senders_boundary_index = create_senders!(sender_receiver_pairs_boundary_index);
596                let senders_boundary_concentrations =
597                    create_senders!(sender_receiver_pairs_boundary_concentrations);
598
599                // TODO catch these errors!
600                let storage_cells_builder = builder
601                    .clone()
602                    .suffix(builder.get_suffix().join("cell_storage"));
603
604                let storage_cells = StorageManager::<CellIdentifier, CellBox<Cel>>::open_or_create(
605                    storage_cells_builder,
606                    i as u64,
607                )
608                .unwrap();
609                let storage_voxels_builder = builder
610                    .clone()
611                    .suffix(builder.get_suffix().join("voxel_storage"));
612                let storage_voxels =
613                    StorageManager::<
614                        PlainIndex,
615                        VoxelBox<
616                            Ind,
617                            Pos,
618                            Vel,
619                            For,
620                            Vox,
621                            Cel,
622                            ConcVecExtracellular,
623                            ConcBoundaryExtracellular,
624                            ConcVecIntracellular,
625                        >,
626                    >::open_or_create(storage_voxels_builder, i as u64)
627                    .unwrap();
628
629                voxels.iter_mut().for_each(|(_, voxelbox)| {
630                    (strategies.voxel_definition_strategies)(&mut voxelbox.voxel)
631                });
632
633                // Define the container for many voxels
634                let cont = MultiVoxelContainer {
635                    voxels,
636                    index_to_plain_index: convert_to_plain_index.clone(),
637
638                    domain: DomainBox::from(setup.domain.clone()),
639
640                    index_to_thread: index_to_thread.clone(),
641                    plain_index_to_thread: plain_index_to_thread.clone(),
642
643                    senders_cell,
644                    senders_pos,
645                    senders_force,
646
647                    senders_boundary_index,
648                    senders_boundary_concentrations,
649
650                    receiver_cell: sender_receiver_pairs_cell[i].1.clone(),
651                    receiver_pos: sender_receiver_pairs_pos[i].1.clone(),
652                    receiver_force: sender_receiver_pairs_force[i].1.clone(),
653
654                    receiver_index: sender_receiver_pairs_boundary_index[i].1.clone(),
655                    receiver_concentrations: sender_receiver_pairs_boundary_concentrations[i]
656                        .1
657                        .clone(),
658
659                    barrier: barrier.clone(),
660
661                    storage_cells,
662                    storage_voxels,
663
664                    mvc_id: i as u32,
665                };
666
667                return cont;
668            })
669            .collect();
670
671        SimulationSupervisor {
672            worker_threads: Vec::new(),
673            multivoxelcontainers,
674
675            time: setup.time,
676            meta_params: setup.meta_params,
677            storage: builder,
678
679            domain: setup.domain.into(),
680
681            config: SimulationConfig {
682                progressbar: Some("".to_string()),
683            },
684
685            meta_infos,
686
687            controller_box: std::sync::Arc::new(std::sync::Mutex::new(ControllerBox {
688                controller: setup.controller,
689                measurements: BTreeMap::new(),
690            })),
691            phantom_cont: PhantomData,
692            phantom_obs: PhantomData,
693        }
694    }
695}