cellular_raza_core/backend/chili/
mod.rs

1//! 🌶️ A modular, reusable, general-purpose backend
2//!
3//! # Usage
4//!
5//! In the following example, we provide an extremely short basic usage of the [run_simulation]
6//! macro.
7//! It performs the numerical integration of a well-defined problem.
8//! We assume that the `MyAgent` struct and `MyDomain` have already been defined and implement the
9//! [Mechanics](cellular_raza_concepts::Mechanics) concept.
10//! More details about how to use pre-existing building blocks or derive their functionality is
11//! provided by the
12//! [cellular_raza_building_blocks](https://cellular-raza.com/docs/cellular_raza_building_blocks)
13//! crate.
14//! We will then solve our system for the
15//! [`Mechanics`](https://cellular-raza.com/internals/concepts/cell/mechanics) aspect.
16//!
17//! ```
18//! # use cellular_raza_core::backend::chili::*;
19//! # use cellular_raza_core::{storage::*, time::*};
20//! # use cellular_raza_concepts::*;
21//! # use rand_chacha::ChaCha8Rng;
22//! # use serde::{Deserialize, Serialize};
23//! # use std::collections::{BTreeMap, BTreeSet};
24//! # // Define agents and domain
25//! # #[derive(Clone, Debug, Deserialize, Serialize)]
26//! # struct MyAgent {
27//! #     pos: f64,
28//! #     vel: f64
29//! # }
30//! # impl Position<f64> for MyAgent {
31//! #     fn pos(&self) -> f64 {
32//! #         self.pos
33//! #     }
34//! #     fn set_pos(&mut self, pos: &f64) {
35//! #         self.pos = *pos
36//! #     }
37//! # }
38//! # impl Velocity<f64> for MyAgent {
39//! #     fn velocity(&self) -> f64 {
40//! #         self.vel
41//! #     }
42//! #     fn set_velocity(&mut self, vel: &f64) {
43//! #         self.vel = *vel
44//! #     }
45//! # }
46//! # impl Mechanics<f64, f64, f64> for MyAgent {
47//! #     fn get_random_contribution(
48//! #         &self,
49//! #         _rng: &mut ChaCha8Rng,
50//! #         _dt: f64,
51//! #     ) -> Result<(f64, f64), RngError> {
52//! #         Ok((0.0, 0.0))
53//! #     }
54//! #     fn calculate_increment(&self, force: f64) -> Result<(f64, f64), CalcError> {
55//! #         Ok((self.vel, force))
56//! #     }
57//! # }
58//! # #[derive(Clone, Debug, Deserialize, Serialize)]
59//! # struct MyDomain {};
60//! # impl<Ci> Domain<MyAgent, MyDomain, Ci> for MyDomain
61//! # where
62//! #     Ci: IntoIterator<Item = MyAgent>
63//! # {
64//! #     type VoxelIndex = usize;
65//! #     type SubDomainIndex = usize;
66//! #     fn decompose(
67//! #         self,
68//! #         _: core::num::NonZeroUsize,
69//! #         cells: Ci,
70//! #     ) -> Result<
71//! #         DecomposedDomain<Self::SubDomainIndex, MyDomain, MyAgent>,
72//! #         cellular_raza_concepts::DecomposeError,
73//! #     > {
74//! #         Ok(DecomposedDomain {
75//! #             n_subdomains: 1.try_into().unwrap(),
76//! #             index_subdomain_cells: vec![(1, MyDomain {}, cells.into_iter().collect())],
77//! #             neighbor_map: BTreeMap::from([(1, BTreeSet::new())]),
78//! #             rng_seed: 1,
79//! #         })
80//! #     }
81//! # }
82//! # impl SubDomain for MyDomain {
83//! #     type VoxelIndex = usize;
84//! #     fn get_neighbor_voxel_indices(&self, _: &Self::VoxelIndex) -> Vec<Self::VoxelIndex> {
85//! #         Vec::new()
86//! #     }
87//! #     fn get_all_indices(&self) -> Vec<Self::VoxelIndex> {
88//! #         vec![1]
89//! #     }
90//! # }
91//! # impl SortCells<MyAgent> for MyDomain {
92//! #     type VoxelIndex = usize;
93//! #     fn get_voxel_index_of(
94//! #         &self,
95//! #         _: &MyAgent,
96//! #     ) -> Result<Self::VoxelIndex, cellular_raza_concepts::BoundaryError> {
97//! #         Ok(1)
98//! #     }
99//! # }
100//! # impl SubDomainMechanics<f64, f64> for MyDomain {
101//! #     fn apply_boundary(
102//! #         &self,
103//! #         _: &mut f64,
104//! #         _: &mut f64,
105//! #     ) -> Result<(), cellular_raza_concepts::BoundaryError> {
106//! #         Ok(())
107//! #     }
108//! # }
109//! # let t0 = 0.0;
110//! # let dt = 0.1;
111//! # let tmax = 20.0;
112//! # let save_interval = 2.0;
113//! # let initial_vel = 0.1;
114//! # let n_threads = 1.try_into().unwrap();
115//! // Initialize agents, domain, solving time and how to store results
116//! let agents = (0..10).map(|n| MyAgent {
117//!         /* Define the agent's properties */
118//! #       pos: n as f64,
119//! #       vel: initial_vel,
120//!     });
121//! let domain = MyDomain {/* Define the domain*/};
122//! let time = FixedStepsize::from_partial_save_interval(t0, dt, tmax, save_interval)?;
123//! let storage = StorageBuilder::new().priority([StorageOption::Memory]);
124//!
125//! // Group them together
126//! let settings = Settings {
127//!     n_threads,
128//!     time,
129//!     storage,
130//!     progressbar: None,
131//! };
132//!
133//! // This will perform the numerical simulation
134//! let storage_access = run_simulation!(
135//!     agents: agents,
136//!     domain: domain,
137//!     settings: settings,
138//!     aspects: [Mechanics],
139//!     core_path: cellular_raza_core,
140//! )?;
141//!
142//! // Use calculated results
143//! let history = storage_access.cells.load_all_elements()?;
144//! for (iteration, cells) in history {
145//!     // ...
146//! #     assert!(iteration >= 0);
147//! #     assert_eq!(cells.len(), 10);
148//! #     for (_, (cbox, _)) in cells {
149//! #         if let CellIdentifier::Initial(key) = cbox.get_id() {
150//! #             let calculated = key as f64 + iteration as f64 * 0.1 * initial_vel;
151//! #             let cr = cbox.cell.pos;
152//! #             assert!((calculated - cr).abs() < 1e-5);
153//! #         } else {
154//! #             panic!("This should only contain initial identifiers");
155//! #         }
156//! #     }
157//! }
158//! # Ok::<(), SimulationError>(())
159//! ```
160//!
161//! This example cannot contain all the functionality which the [chili](self) backend provides.
162//! We encourage the user to look at the
163//! [cellular-raza.com/guides](https://cellular-raza.com/guides) and
164//! [cellular-raza.com/showcase](https://cellular-raza.com/showcase) sections to get started.
165//!
166//! # Internals
167//! The [chili](self) backend uses procedural macros to generate code which
168//! results in a fully working simulation.
169//! The methods, functions and objects used in this way are formualted with generics.
170//! This enables us to write general-purpose solvers for a wide range of problems.
171//! The most important macro is the [run_simulation] macro which can be solely used to run
172//! simulations.
173//! This macro itself can be broken down into smaller pieces.
174//! - [prepare_types] Defines types used in simulation
175//!     - [build_aux_storage] AuxStorage struct used to store intermediate
176//!       values for update steps of aspects
177//!     - [build_communicator] Type which handles communication between
178//!       threads
179//! - [test_compatibility] Test compatibility of all types involved
180//! - [run_main] Defines main loop and performs the simulation
181//!
182//! These macros take a subset of keyword arguments of the [run_simulation] macro.
183//! The arguments are documented under the [run_simulation] macro.
184//!
185//! # Main Loop
186//! The [run_main] macro constructs the main loop of the simulation.
187//! It inserts functions depending on the given simulation aspects.
188//! They can be grouped into 6 steps
189//! Below, we show a list of all these functions and their corresponding aspects.
190//!
191//! #### Step 1 - Send Information
192//!
193//! In this step, each sub
194//!
195//! <style>
196//!     div table {
197//!         width: 100%;
198//!     }
199//!     table th:first-of-type {
200//!         width: 20%;
201//!     }
202//!     table th:nth-of-type(2) {
203//!         width: 35%;
204//!     }
205//!     table th:nth-of-type(3) {
206//!         width: 45%;
207//!     }
208//! </style>
209//!
210//! | Aspects | Function | Purpose |
211//! | --- | --- | --- |
212#![doc = "\
213    | `Mechanics && Interaction`\
214    | [update_mechanics_interaction_step_1](SubDomainBox::update_mechanics_interaction_step_1)\
215    | Send [PosInformation](PosInformation) between threads to get back \
216      [ForceInformation](ForceInformation) |"]
217#![doc = "\
218    | `DomainForce`\
219    | [calculate_custom_domain_force](SubDomainBox::calculate_custom_domain_force)\
220    | Uses the [SubDomainForce](cellular_raza_concepts::SubDomainForce) trait to add \
221      custom external force. |"]
222#![doc = "\
223    | `ReactionsContact`\
224    | [update_contact_reactions_step_1]\
225      (SubDomainBox::update_contact_reactions_step_1) \
226    | Sends [ReactionsContactInformation](ReactionsContactInformation) between threads. |"]
227#![doc = "\
228    | `ReactionsExtra` \
229    | [update_reactions_extra_step_1](SubDomainBox::update_reactions_extra_step_1) \
230    | Sends [ReactionsExtraBorderInfo](ReactionsExtraBorderInfo) between threads. |"]
231#![doc = "\
232    | | [sync](SubDomainBox::sync) | Wait for threads to have finished until proceeding. |"]
233//!
234//! #### Step 2 - Calculate and Return
235//! | Aspects | Function | Purpose |
236//! | --- | --- | --- |
237#![doc = "\
238    | `Mechanics && Interaction` \
239    | [update_mechanics_interaction_step_2](SubDomainBox::update_mechanics_interaction_step_2) \
240    | Calculate forces and return [ForceInformation](ForceInformation) to the original \
241      sender. |"]
242#![doc = "\
243    | `ReactionsContact` \
244    | [update_contact_reactions_step_2](SubDomainBox::update_contact_reactions_step_2) \
245    | Calculates the combined increment and returns \
246      [ReactionsContactReturn](ReactionsContactReturn) |"]
247#![doc = "\
248    | `ReactionsExtra` \
249    | [update_reactions_extra_step_2](SubDomainBox::update_reactions_extra_step_2) \
250    | Returns [ReactionsExtraBorderReturn](ReactionsExtraBorderReturn) |"]
251//!
252//! #### Step 3 - Receive and Apply
253//! | Aspects | Function | Purpose |
254//! | --- | --- | --- |
255#![doc = "\
256    | `Mechanics && Interaction` \
257    | [update_mechanics_interaction_step_3](SubDomainBox::update_mechanics_interaction_step_3) \
258    | Receives the [ForceInformation](ForceInformation) and adds within the \
259      `aux_storage`. |"]
260#![doc = "\
261    | `ReactionsContact` \
262    | [update_contact_reactions_step_3](SubDomainBox::update_contact_reactions_step_3) \
263    | Receives the [ReactionsContactReturn](ReactionsContactReturn) and adds within the `aux_storage`. |"]
264#![doc = "\
265    | `ReactionsExtra` \
266    | [update_reactions_extra_step_3](SubDomainBox::update_reactions_extra_step_3) \
267    | Receives the [ReactionsExtraBorderReturn](ReactionsExtraBorderReturn). |"]
268//!
269//! #### Pure Local Functions - Perform Update
270//! | Aspects | Function | Purpose |
271//! | --- | --- | --- |
272#![doc = "\
273    | `Mechanics` \
274    | [local_mechanics_update](local_mechanics_update) \
275    | Performs numerical integration of the position and velocity. |"]
276#![doc = "\
277    | `Interaction` \
278    | [local_interaction_react_to_neighbors](local_interaction_react_to_neighbors) \
279    | Performs changes due to neighbor counting. |"]
280#![doc = "\
281    | `Cycle` \
282    | [local_cycle_update](local_cycle_update) \
283    | Advances the cycle of the cell. This may introduce a\
284      [CycleEvent](cellular_raza_concepts::CycleEvent) |"]
285#![doc = "\
286    | `Reactions` \
287    | [local_reactions_intracellular](local_reactions_intracellular) \
288    | Calculates increment from purely intracellular reactions. |"]
289#![doc = "\
290    | `ReactionsContact` \
291    | [local_update_contact_reactions](local_update_contact_reactions) \
292    | Performs the update of the contact reactions. |"]
293#![doc = "\
294    | `ReactionsExtra` \
295    | [local_subdomain_update_reactions_extra](local_subdomain_update_reactions_extra) \
296    | Performs the update of the extracellular reactions. |"]
297#![doc = "\
298    | `Reactions` &#124;&#124; `ReactionsContact` &#124;&#124; `ReactionsExtra` \
299    | [local_reactions_use_increment](local_reactions_use_increment) \
300    | Calculates increment from purely intracellular reactions. |"]
301//!
302//! #### Step 4 - Treat Cell Positions
303//! | Aspects | Function | Purpose |
304//! | --- | --- | --- |
305#![doc = "\
306    | `Mechanics` \
307    | [apply_boundary](SubDomainBox::apply_boundary) \
308    | Apply a boundary condition. |"]
309#![doc = "\
310    | `Cycle` \
311    | [update_cell_cycle_4](SubDomainBox::update_cell_cycle_4) \
312    | Performs cell-division and other cycle events. |"]
313#![doc = "\
314    | `Mechanics` \
315    | [sort_cells_in_voxels_step_1](SubDomainBox::sort_cells_in_voxels_step_1) \
316    | Checks if cells need to be sent to different subdomain and sends them. |"]
317//!
318//! #### Step 5 - Include new Cells
319//! | Aspects | Function | Purpose |
320//! | --- | --- | --- |
321#![doc = "\
322    | `Mechanics` \
323    | [sort_cells_in_voxels_step_2](SubDomainBox::sort_cells_in_voxels_step_2) \
324    | Include newly received cells into correct subdomains. |"]
325//!
326//! # Return Type
327//! After the simulation is done, we return a [StorageAccess] struct to interoperate with stored
328//! results.
329
330pub use cellular_raza_concepts::{CellBox, CellIdentifier, SubDomainPlainIndex, VoxelPlainIndex};
331
332/// Contains structs to store aspects of the simulation and macros to construct them.
333mod aux_storage;
334#[doc(hidden)]
335pub mod compatibility_tests;
336mod datastructures;
337mod errors;
338mod proc_macro;
339mod result;
340mod setup;
341mod simulation_flow;
342mod solvers;
343mod update_cycle;
344mod update_mechanics;
345mod update_reactions;
346
347pub use aux_storage::*;
348pub use datastructures::*;
349pub use errors::*;
350pub use proc_macro::*;
351pub use result::*;
352pub use setup::*;
353pub use simulation_flow::*;
354pub use solvers::*;
355pub use update_cycle::*;
356pub use update_mechanics::*;
357pub use update_reactions::*;