1use cellular_raza_concepts::*;
2use serde::{Deserialize, Serialize};
3
4#[cfg(feature = "tracing")]
5use tracing::instrument;
6
7use std::collections::{BTreeMap, BTreeSet};
8use std::hash::Hash;
9use std::num::NonZeroUsize;
10
11use rand::SeedableRng;
12
13use super::errors::*;
14use super::simulation_flow::*;
15
16use super::{CellIdentifier, SubDomainPlainIndex, VoxelPlainIndex};
17
18pub struct SimulationRunner<I, Sb> {
24 pub subdomain_boxes: BTreeMap<I, Sb>,
28}
29
30#[derive(Clone, Deserialize, Serialize)]
32pub struct Voxel<C, A> {
33 pub plain_index: VoxelPlainIndex,
35 pub neighbors: BTreeSet<VoxelPlainIndex>,
37 pub cells: Vec<(CellBox<C>, A)>,
39 pub new_cells: Vec<(C, Option<CellIdentifier>)>,
41 pub id_counter: u64,
43 pub rng: rand_chacha::ChaCha8Rng,
46}
47
48#[allow(unused)]
50#[cfg_attr(feature = "tracing", instrument(skip_all))]
51pub fn construct_simulation_runner<D, S, C, A, Com, Sy, Ci>(
52 domain: D,
53 agents: Ci,
54 n_subdomains: NonZeroUsize,
55 init_aux_storage: impl Fn(&C) -> A,
56) -> Result<
57 SimulationRunner<D::SubDomainIndex, SubDomainBox<D::SubDomainIndex, S, C, A, Com, Sy>>,
58 SimulationError,
59>
60where
61 Ci: IntoIterator<Item = C>,
62 D: Domain<C, S, Ci>,
63 D::SubDomainIndex: Eq + PartialEq + core::hash::Hash + Clone + Ord,
64 <S as SubDomain>::VoxelIndex: Eq + Hash + Ord + Clone,
65 S: SortCells<C, VoxelIndex = <S as SubDomain>::VoxelIndex> + SubDomain,
66 Sy: super::simulation_flow::FromMap<SubDomainPlainIndex>,
67 Com: super::simulation_flow::FromMap<SubDomainPlainIndex>,
68{
69 #[cfg(feature = "tracing")]
70 tracing::info!("Decomposing");
71 let decomposed_domain = domain.decompose(n_subdomains, agents)?;
72
73 #[cfg(feature = "tracing")]
74 tracing::info!("Validating Map");
75 if !validate_map(&decomposed_domain.neighbor_map) {
76 return Err(SimulationError::IndexError(IndexError(format!(
77 "Neighbor Map is invalid"
78 ))));
79 }
80
81 #[cfg(feature = "tracing")]
82 tracing::info!("Constructing Neighbor Map");
83 let subdomain_index_to_subdomain_plain_index = decomposed_domain
84 .index_subdomain_cells
85 .iter()
86 .enumerate()
87 .map(|(i, (subdomain_index, _, _))| (subdomain_index.clone(), SubDomainPlainIndex(i)))
88 .collect::<BTreeMap<_, _>>();
89 let mut neighbor_map = decomposed_domain
90 .neighbor_map
91 .into_iter()
92 .map(|(index, neighbors)| {
93 (
94 subdomain_index_to_subdomain_plain_index[&index],
95 neighbors
96 .into_iter()
97 .filter_map(|sindex| {
98 if index != sindex {
99 Some(subdomain_index_to_subdomain_plain_index[&sindex])
100 } else {
101 None
102 }
103 })
104 .collect::<BTreeSet<_>>(),
105 )
106 })
107 .collect::<BTreeMap<_, _>>();
108
109 #[cfg(feature = "tracing")]
110 tracing::info!("Constructing Syncers and communicators");
111 let mut syncers = Sy::from_map(&neighbor_map)?;
112 let mut communicators = Com::from_map(&neighbor_map)?;
113 let voxel_index_to_plain_index = decomposed_domain
114 .index_subdomain_cells
115 .iter()
116 .map(|(_, subdomain, _)| subdomain.get_all_indices().into_iter())
117 .flatten()
118 .enumerate()
119 .map(|(i, x)| (x, VoxelPlainIndex(i)))
120 .collect::<BTreeMap<<S as SubDomain>::VoxelIndex, VoxelPlainIndex>>();
121 let plain_index_to_subdomain: std::collections::BTreeMap<_, _> = decomposed_domain
122 .index_subdomain_cells
123 .iter()
124 .enumerate()
125 .map(|(subdomain_index, (_, subdomain, _))| {
126 subdomain
127 .get_all_indices()
128 .into_iter()
129 .map(move |index| (subdomain_index, index))
130 })
131 .flatten()
132 .map(|(subdomain_index, voxel_index)| {
133 (
134 voxel_index_to_plain_index[&voxel_index],
135 SubDomainPlainIndex(subdomain_index),
136 )
137 })
138 .collect();
139
140 #[cfg(feature = "tracing")]
141 tracing::info!("Creating Subdomain Boxes");
142 let subdomain_boxes = decomposed_domain
143 .index_subdomain_cells
144 .into_iter()
145 .map(|(index, subdomain, cells)| {
146 let subdomain_plain_index = subdomain_index_to_subdomain_plain_index[&index];
147 let mut cells = cells.into_iter().map(|c| (c, None)).collect();
148 let mut voxel_index_to_neighbor_plain_indices: BTreeMap<_, _> = subdomain
149 .get_all_indices()
150 .into_iter()
151 .map(|voxel_index| {
152 (
153 voxel_index.clone(),
154 subdomain
155 .get_neighbor_voxel_indices(&voxel_index)
156 .into_iter()
157 .map(|neighbor_index| voxel_index_to_plain_index[&neighbor_index])
158 .collect::<BTreeSet<_>>(),
159 )
160 })
161 .collect();
162 let voxels = subdomain.get_all_indices().into_iter().map(|voxel_index| {
163 let plain_index = voxel_index_to_plain_index[&voxel_index];
164 let neighbors = voxel_index_to_neighbor_plain_indices
165 .remove(&voxel_index)
166 .ok_or(super::IndexError(format!(
167 "cellular_raza::core::chili internal error in decomposition:\
168 please file a bug report!\
169 https://github.com/jonaspleyer/cellular_raza/issues/new?\
170 title=internal%20error%20during%20domain%20decomposition",
171 )))?;
172 Ok((
173 plain_index,
174 Voxel {
175 plain_index,
176 neighbors,
177 cells: Vec::new(),
178 new_cells: Vec::new(),
179 id_counter: 0,
180 rng: rand_chacha::ChaCha8Rng::seed_from_u64(
181 decomposed_domain.rng_seed + plain_index.0 as u64,
182 ),
183 },
184 ))
185 });
186 let syncer = syncers.remove(&subdomain_plain_index).ok_or(BoundaryError(
187 "Index was not present in subdomain map".into(),
188 ))?;
189 use cellular_raza_concepts::BoundaryError;
190 let communicator =
191 communicators
192 .remove(&subdomain_plain_index)
193 .ok_or(BoundaryError(
194 "Index was not present in subdomain map".into(),
195 ))?;
196 let mut subdomain_box = SubDomainBox {
197 index: index.clone(),
198 subdomain_plain_index,
199 neighbors: neighbor_map
200 .remove(&subdomain_plain_index)
201 .unwrap()
202 .into_iter()
203 .collect(),
204 subdomain,
205 voxels: voxels.collect::<Result<_, SimulationError>>()?,
206 voxel_index_to_plain_index: voxel_index_to_plain_index.clone(),
207 plain_index_to_subdomain: plain_index_to_subdomain.clone(),
208 communicator,
209 syncer,
210 };
211 subdomain_box.insert_initial_cells(&mut cells, &init_aux_storage)?;
212 Ok((index, subdomain_box))
213 })
214 .collect::<Result<BTreeMap<_, _>, SimulationError>>()?;
215 let simulation_runner = SimulationRunner { subdomain_boxes };
216 Ok(simulation_runner)
217}
218
219pub struct SubDomainBox<I, S, C, A, Com, Sy = BarrierSync>
221where
222 S: SubDomain,
223{
224 #[allow(unused)]
225 pub index: I,
227 pub subdomain_plain_index: SubDomainPlainIndex,
229 pub neighbors: BTreeSet<SubDomainPlainIndex>,
231 pub subdomain: S,
233 pub voxels: std::collections::BTreeMap<VoxelPlainIndex, Voxel<C, A>>,
235 pub voxel_index_to_plain_index: BTreeMap<S::VoxelIndex, VoxelPlainIndex>,
237 pub plain_index_to_subdomain: std::collections::BTreeMap<VoxelPlainIndex, SubDomainPlainIndex>,
239 pub communicator: Com,
244 pub syncer: Sy,
247}
248
249impl<I, S, C, A, Com, Sy> SubDomainBox<I, S, C, A, Com, Sy>
250where
251 S: SubDomain,
252{
253 #[cfg_attr(feature = "tracing", instrument(skip_all))]
256 pub fn sync(&mut self) -> Result<(), SimulationError>
257 where
258 Sy: SyncSubDomains,
259 {
260 self.syncer.sync()
261 }
262
263 #[cfg_attr(feature = "tracing", instrument(skip_all))]
265 pub fn store_error(
266 &mut self,
267 maybe_error: Result<(), SimulationError>,
268 ) -> Result<bool, SimulationError>
269 where
270 Sy: SyncSubDomains,
271 {
272 self.syncer.store_error(maybe_error)
273 }
274
275 #[cfg_attr(feature = "tracing", instrument(skip_all))]
278 pub fn insert_initial_cells(
279 &mut self,
280 new_cells: &mut Vec<(C, Option<A>)>,
281 init_aux_storage: impl Fn(&C) -> A,
282 ) -> Result<(), cellular_raza_concepts::BoundaryError>
283 where
284 <S as SubDomain>::VoxelIndex: Eq + Hash + Ord,
285 S: SortCells<C, VoxelIndex = <S as SubDomain>::VoxelIndex>,
286 {
287 for (n_cell, (cell, aux_storage)) in new_cells.drain(..).enumerate() {
288 let voxel_index = self.subdomain.get_voxel_index_of(&cell)?;
289 let plain_index = self.voxel_index_to_plain_index[&voxel_index];
290 let voxel = self.voxels.get_mut(&plain_index).ok_or(BoundaryError(
291 "Could not find correct voxel for cell".to_owned(),
292 ))?;
293 let aux_storage = aux_storage.map_or(init_aux_storage(&cell), |x| x);
294 let cbox = CellBox::new_initial(n_cell, cell);
295 voxel.cells.push((cbox, aux_storage));
296 voxel.id_counter += 1;
297 }
298 Ok(())
299 }
300
301 pub fn run_local_cell_funcs<Func, F>(
306 &mut self,
307 func: Func,
308 next_time_point: &crate::time::NextTimePoint<F>,
309 ) -> Result<(), super::SimulationError>
310 where
311 Func: Fn(
312 &mut C,
313 &mut A,
314 F,
315 &mut rand_chacha::ChaCha8Rng,
316 ) -> Result<(), super::SimulationError>,
317 F: Copy,
318 {
319 let dt = next_time_point.increment;
320 for (_, voxel) in self.voxels.iter_mut() {
321 for (cellbox, aux_storage) in voxel.cells.iter_mut() {
322 func(&mut cellbox.cell, aux_storage, dt, &mut voxel.rng)?;
323 }
324 }
325 Ok(())
326 }
327
328 pub fn run_local_subdomain_funcs<Func, F>(
330 &mut self,
331 func: Func,
332 next_time_point: &crate::time::NextTimePoint<F>,
333 ) -> Result<(), super::SimulationError>
334 where
335 Func: Fn(
336 &mut S,
337 F,
338 ) -> Result<(), super::SimulationError>,
340 F: Copy,
341 {
342 let dt = next_time_point.increment;
343 func(&mut self.subdomain, dt)?;
344 Ok(())
345 }
346
347 #[cfg_attr(feature = "tracing", instrument(skip(self, storage_manager)))]
349 pub fn save_subdomains<
350 #[cfg(feature = "tracing")] F: core::fmt::Debug,
351 #[cfg(not(feature = "tracing"))] F,
352 >(
353 &self,
354 storage_manager: &mut crate::storage::StorageManager<SubDomainPlainIndex, S>,
355 next_time_point: &crate::time::NextTimePoint<F>,
356 ) -> Result<(), StorageError>
357 where
358 S: Clone + Serialize,
359 {
360 if let Some(crate::time::TimeEvent::PartialSave) = next_time_point.event {
361 use crate::storage::StorageInterfaceStore;
362 storage_manager.store_single_element(
363 next_time_point.iteration as u64,
364 &self.subdomain_plain_index,
365 &self.subdomain,
366 )?;
367 }
368 Ok(())
369 }
370
371 #[cfg_attr(feature = "tracing", instrument(skip(self, storage_manager)))]
373 pub fn save_cells<
374 #[cfg(feature = "tracing")] F: core::fmt::Debug,
375 #[cfg(not(feature = "tracing"))] F,
376 >(
377 &self,
378 storage_manager: &mut crate::storage::StorageManager<CellIdentifier, (CellBox<C>, A)>,
379 next_time_point: &crate::time::NextTimePoint<F>,
380 ) -> Result<(), StorageError>
381 where
382 A: Clone + Serialize,
383 C: Clone + Serialize,
384 CellBox<C>: cellular_raza_concepts::Id<Identifier = CellIdentifier>,
385 {
386 if let Some(crate::time::TimeEvent::PartialSave) = next_time_point.event {
387 use crate::storage::StorageInterfaceStore;
388 let cells = self
389 .voxels
390 .iter()
391 .map(|(_, vox)| vox.cells.iter().map(|ca| (ca.0.ref_id(), ca)))
392 .flatten();
393 storage_manager.store_batch_elements(next_time_point.iteration as u64, cells)?;
394 }
395 Ok(())
396 }
397}