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(crate) index: I,
226 pub(crate) subdomain_plain_index: SubDomainPlainIndex,
227 pub(crate) neighbors: BTreeSet<SubDomainPlainIndex>,
228 pub(crate) subdomain: S,
229 pub(crate) voxels: std::collections::BTreeMap<VoxelPlainIndex, Voxel<C, A>>,
230 pub(crate) voxel_index_to_plain_index: BTreeMap<S::VoxelIndex, VoxelPlainIndex>,
231 pub(crate) plain_index_to_subdomain:
232 std::collections::BTreeMap<VoxelPlainIndex, SubDomainPlainIndex>,
233 pub(crate) communicator: Com,
234 pub(crate) syncer: Sy,
235}
236
237impl<I, S, C, A, Com, Sy> SubDomainBox<I, S, C, A, Com, Sy>
238where
239 S: SubDomain,
240{
241 #[cfg_attr(feature = "tracing", instrument(skip_all))]
244 pub fn sync(&mut self) -> Result<(), SimulationError>
245 where
246 Sy: SyncSubDomains,
247 {
248 self.syncer.sync()
249 }
250
251 #[cfg_attr(feature = "tracing", instrument(skip_all))]
253 pub fn store_error(
254 &mut self,
255 maybe_error: Result<(), SimulationError>,
256 ) -> Result<bool, SimulationError>
257 where
258 Sy: SyncSubDomains,
259 {
260 self.syncer.store_error(maybe_error)
261 }
262
263 #[cfg_attr(feature = "tracing", instrument(skip_all))]
266 pub fn insert_initial_cells(
267 &mut self,
268 new_cells: &mut Vec<(C, Option<A>)>,
269 init_aux_storage: impl Fn(&C) -> A,
270 ) -> Result<(), cellular_raza_concepts::BoundaryError>
271 where
272 <S as SubDomain>::VoxelIndex: Eq + Hash + Ord,
273 S: SortCells<C, VoxelIndex = <S as SubDomain>::VoxelIndex>,
274 {
275 for (n_cell, (cell, aux_storage)) in new_cells.drain(..).enumerate() {
276 let voxel_index = self.subdomain.get_voxel_index_of(&cell)?;
277 let plain_index = self.voxel_index_to_plain_index[&voxel_index];
278 let voxel = self.voxels.get_mut(&plain_index).ok_or(BoundaryError(
279 "Could not find correct voxel for cell".to_owned(),
280 ))?;
281 let aux_storage = aux_storage.map_or(init_aux_storage(&cell), |x| x);
282 let cbox = CellBox::new_initial(n_cell, cell);
283 voxel.cells.push((cbox, aux_storage));
284 voxel.id_counter += 1;
285 }
286 Ok(())
287 }
288
289 pub fn run_local_cell_funcs<Func, F>(
294 &mut self,
295 func: Func,
296 next_time_point: &crate::time::NextTimePoint<F>,
297 ) -> Result<(), super::SimulationError>
298 where
299 Func: Fn(
300 &mut C,
301 &mut A,
302 F,
303 &mut rand_chacha::ChaCha8Rng,
304 ) -> Result<(), super::SimulationError>,
305 F: Copy,
306 {
307 let dt = next_time_point.increment;
308 for (_, voxel) in self.voxels.iter_mut() {
309 for (cellbox, aux_storage) in voxel.cells.iter_mut() {
310 func(&mut cellbox.cell, aux_storage, dt, &mut voxel.rng)?;
311 }
312 }
313 Ok(())
314 }
315
316 pub fn run_local_subdomain_funcs<Func, F>(
318 &mut self,
319 func: Func,
320 next_time_point: &crate::time::NextTimePoint<F>,
321 ) -> Result<(), super::SimulationError>
322 where
323 Func: Fn(
324 &mut S,
325 F,
326 ) -> Result<(), super::SimulationError>,
328 F: Copy,
329 {
330 let dt = next_time_point.increment;
331 func(&mut self.subdomain, dt)?;
332 Ok(())
333 }
334
335 #[cfg_attr(feature = "tracing", instrument(skip(self, storage_manager)))]
337 pub fn save_subdomains<
338 #[cfg(feature = "tracing")] F: core::fmt::Debug,
339 #[cfg(not(feature = "tracing"))] F,
340 >(
341 &self,
342 storage_manager: &mut crate::storage::StorageManager<SubDomainPlainIndex, S>,
343 next_time_point: &crate::time::NextTimePoint<F>,
344 ) -> Result<(), StorageError>
345 where
346 S: Clone + Serialize,
347 {
348 if let Some(crate::time::TimeEvent::PartialSave) = next_time_point.event {
349 use crate::storage::StorageInterfaceStore;
350 storage_manager.store_single_element(
351 next_time_point.iteration as u64,
352 &self.subdomain_plain_index,
353 &self.subdomain,
354 )?;
355 }
356 Ok(())
357 }
358
359 #[cfg_attr(feature = "tracing", instrument(skip(self, storage_manager)))]
361 pub fn save_cells<
362 #[cfg(feature = "tracing")] F: core::fmt::Debug,
363 #[cfg(not(feature = "tracing"))] F,
364 >(
365 &self,
366 storage_manager: &mut crate::storage::StorageManager<CellIdentifier, (CellBox<C>, A)>,
367 next_time_point: &crate::time::NextTimePoint<F>,
368 ) -> Result<(), StorageError>
369 where
370 A: Clone + Serialize,
371 C: Clone + Serialize,
372 CellBox<C>: cellular_raza_concepts::Id<Identifier = CellIdentifier>,
373 {
374 if let Some(crate::time::TimeEvent::PartialSave) = next_time_point.event {
375 use crate::storage::StorageInterfaceStore;
376 let cells = self
377 .voxels
378 .iter()
379 .map(|(_, vox)| vox.cells.iter().map(|ca| (ca.0.ref_id(), ca)))
380 .flatten();
381 storage_manager.store_batch_elements(next_time_point.iteration as u64, cells)?;
382 }
383 Ok(())
384 }
385}