use crate::simulation_aspects::{SimulationAspect, SimulationAspects};
#[derive(Clone, Eq, PartialEq, Debug)]
pub enum Parallelizer {
OsThreads,
Rayon,
}
impl syn::parse::Parse for Parallelizer {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let ident: syn::Ident = input.parse()?;
match ident.clone().to_string().as_str() {
"OsThreads" => Ok(Self::OsThreads),
"Rayon" => Ok(Self::Rayon),
_ => Err(syn::Error::new(ident.span(), "Not a valid parallelizer")),
}
}
}
impl Parallelizer {
fn parallelize_execution(
&self,
code: &proc_macro2::TokenStream,
core_path: &syn::Path,
settings: &syn::Ident,
) -> proc_macro2::TokenStream {
let core_path = &core_path;
match &self {
Self::OsThreads => quote::quote!({
let mut handles = vec![];
for (key, mut sbox) in runner
.subdomain_boxes
.into_iter()
{
let #settings = #settings.clone();
let mut _storage_manager_subdomains = _storage_manager_subdomains_base
.clone_to_new_instance(key as u64);
let mut _storage_manager_cells = _storage_manager_cells_base
.clone_to_new_instance(key as u64);
let handle = std::thread::Builder::new()
.name(format!("cellular_raza-worker_thread-{:03.0}", key))
.spawn(move ||
-> Result<_, #core_path::backend::chili::SimulationError> {#code})?;
handles.push(handle);
}
let mut storage_accesses = vec![];
for handle in handles {
let result = handle
.join()
.expect("Could not join threads after simulation has finished")?;
storage_accesses.push(result);
}
let storage_access = storage_accesses.pop()
.ok_or(#core_path::storage::StorageError::InitError(
format!("Simulation Threads did not yield any storage managers")
))?;
Result::<_, #core_path::backend::chili::SimulationError>::Ok(storage_access)
}),
Self::Rayon => quote::quote!({
use #core_path::rayon::prelude::*;
let mut storage_accesses = runner.subdomain_boxes
.into_par_iter()
.map(|(key, mut sbox)| -> Result<
_,
#core_path::backend::chili::SimulationError
> {
let mut _storage_manager_subdomains = _storage_manager_subdomains_base
.clone_to_new_instance(key as u64);
let mut _storage_manager_cells = _storage_manager_cells_base
.clone_to_new_instance(key as u64);
#code
})
.collect::<Result<Vec<_>, #core_path::backend::chili::SimulationError>>()?;
let storage_access = storage_accesses
.pop()
.ok_or(#core_path::storage::StorageError::InitError(
format!("Simulation threads did not yield any storage managers")
))?;
Result::<_, #core_path::backend::chili::SimulationError>::Ok(storage_access)
}),
}
}
}
pub const DEFAULT_MECHANICS_SOLVER_ORDER: usize = 2;
pub const DEFAULT_REACTIONS_SOLVER_ORDER_INTRA: usize = 4;
pub const DEFAULT_REACTIONS_SOLVER_ORDER_CONTACT: usize = 2;
pub fn default_update_mechanics_interaction_step_1_fn_name() -> syn::Ident {
syn::Ident::new(
"update_mechanics_interaction_step_1",
proc_macro2::Span::call_site(),
)
}
pub fn default_update_mechanics_interaction_step_2_fn_name() -> syn::Ident {
syn::Ident::new(
"update_mechanics_interaction_step_2",
proc_macro2::Span::call_site(),
)
}
pub fn default_update_mechanics_interaction_step_3_fn_name() -> syn::Ident {
syn::Ident::new(
"update_mechanics_interaction_step_3",
proc_macro2::Span::call_site(),
)
}
define_kwargs!(
KwargsSim,
KwargsSimParsed,
domain: syn::Ident,
agents: syn::Ident,
settings: syn::Ident,
aspects: SimulationAspects,
@optionals
core_path: syn::Path | crate::kwargs::convert_core_path(None),
parallelizer: Parallelizer | Parallelizer::OsThreads,
determinism: bool | true,
aux_storage_name: syn::Ident | crate::aux_storage::default_aux_storage_name(),
zero_force_default: syn::ExprClosure | crate::aux_storage::zero_force_default(),
zero_reactions_default: syn::ExprClosure | crate::aux_storage::zero_reactions_default(),
communicator_name: syn::Ident | crate::communicator::default_communicator_name(),
mechanics_solver_order: usize | crate::run_sim::DEFAULT_MECHANICS_SOLVER_ORDER,
reactions_intra_solver_order: usize | crate::run_sim::DEFAULT_REACTIONS_SOLVER_ORDER_INTRA,
reactions_contact_solver_order: usize | crate::run_sim::DEFAULT_REACTIONS_SOLVER_ORDER_CONTACT,
update_mechanics_interaction_step_1: syn::Ident |
crate::run_sim::default_update_mechanics_interaction_step_1_fn_name(),
update_mechanics_interaction_step_2: syn::Ident |
crate::run_sim::default_update_mechanics_interaction_step_2_fn_name(),
update_mechanics_interaction_step_3: syn::Ident |
crate::run_sim::default_update_mechanics_interaction_step_3_fn_name(),
);
define_kwargs!(
KwargsCompatibility,
KwargsCompatibilityParsed,
domain: syn::Ident,
agents: syn::Ident,
settings: syn::Ident,
aspects: SimulationAspects,
@optionals
core_path: syn::Path | crate::kwargs::convert_core_path(None),
@from
KwargsSim
);
define_kwargs!(
KwargsPrepareTypes,
KwargsPrepareTypesParsed,
aspects: SimulationAspects,
@optionals
core_path: syn::Path | crate::kwargs::convert_core_path(None),
aux_storage_name: syn::Ident | crate::aux_storage::default_aux_storage_name(),
zero_force_default: syn::ExprClosure | crate::aux_storage::zero_force_default(),
zero_reactions_default: syn::ExprClosure | crate::aux_storage::zero_reactions_default(),
communicator_name: syn::Ident | crate::communicator::default_communicator_name(),
@from
KwargsSim,
);
define_kwargs!(
KwargsMain,
KwargsMainParsed,
domain: syn::Ident,
agents: syn::Ident,
settings: syn::Ident,
aspects: SimulationAspects,
@optionals
core_path: syn::Path | crate::kwargs::convert_core_path(None),
parallelizer: Parallelizer | Parallelizer::Rayon,
determinism: bool | true,
aux_storage_name: syn::Ident | crate::aux_storage::default_aux_storage_name(),
zero_force_default: syn::ExprClosure | crate::aux_storage::zero_force_default(),
zero_reactions_default: syn::ExprClosure | crate::aux_storage::zero_reactions_default(),
communicator_name: syn::Ident | crate::communicator::default_communicator_name(),
mechanics_solver_order: usize | crate::run_sim::DEFAULT_MECHANICS_SOLVER_ORDER,
reactions_intra_solver_order: usize | crate::run_sim::DEFAULT_REACTIONS_SOLVER_ORDER_INTRA,
reactions_contact_solver_order: usize | crate::run_sim::DEFAULT_REACTIONS_SOLVER_ORDER_CONTACT,
update_mechanics_interaction_step_1: syn::Ident |
crate::run_sim::default_update_mechanics_interaction_step_1_fn_name(),
update_mechanics_interaction_step_2: syn::Ident |
crate::run_sim::default_update_mechanics_interaction_step_2_fn_name(),
update_mechanics_interaction_step_3: syn::Ident |
crate::run_sim::default_update_mechanics_interaction_step_3_fn_name(),
@from
KwargsSim
);
pub fn run_main_update(kwargs: KwargsMain) -> proc_macro2::TokenStream {
use SimulationAspect::*;
use quote::quote;
let mut step_1 = proc_macro2::TokenStream::new();
let mut step_2 = proc_macro2::TokenStream::new();
let mut step_3 = proc_macro2::TokenStream::new();
let mut step_4 = proc_macro2::TokenStream::new();
let mut step_5 = proc_macro2::TokenStream::new();
let mut local_func_names = Vec::<proc_macro2::TokenStream>::new();
let mut local_subdomain_func_names = Vec::<proc_macro2::TokenStream>::new();
let core_path = &kwargs.core_path;
let settings = &kwargs.settings;
let determinism = &kwargs.determinism;
let mechanics_solver_order = kwargs.mechanics_solver_order;
let reactions_intra_solver_order = kwargs.reactions_intra_solver_order;
let aux_storage_constructor = crate::aux_storage::default_aux_storage_initializer(&kwargs);
if kwargs
.aspects
.contains_multiple(vec![&Mechanics, &Interaction])
{
let umis_fn_name_1 = &kwargs.update_mechanics_interaction_step_1;
let umis_fn_name_2 = &kwargs.update_mechanics_interaction_step_2;
let umis_fn_name_3 = &kwargs.update_mechanics_interaction_step_3;
step_1.extend(quote!(sbox. #umis_fn_name_1 ()?;));
step_2.extend(quote!(sbox. #umis_fn_name_2 (#determinism)?;));
step_3.extend(quote!(sbox. #umis_fn_name_3 (#determinism)?;));
}
if kwargs.aspects.contains(&Mechanics) {
local_func_names.push(quote!(
#core_path::backend::chili::local_mechanics_update::<
_,
_,
_,
_,
_,
_,
#mechanics_solver_order
>));
step_4.extend(quote!(sbox.apply_boundary()?;));
}
if kwargs.aspects.contains(&Interaction) {
local_func_names
.push(quote!(#core_path::backend::chili::local_interaction_react_to_neighbors));
}
if kwargs.aspects.contains(&DomainForce) {
step_1.extend(quote!(sbox.calculate_custom_domain_force()?;));
}
if kwargs.aspects.contains(&Cycle) {
local_func_names.push(quote!(#core_path::backend::chili::local_cycle_update));
step_4.extend(quote!(sbox.update_cell_cycle_4(&#aux_storage_constructor)?;));
}
if kwargs.aspects.contains(&Mechanics) {
step_4.extend(quote!(sbox.sort_cells_in_voxels_step_1()?;));
step_5.extend(quote!(sbox.sort_cells_in_voxels_step_2(#determinism)?;));
}
if kwargs.aspects.contains(&Reactions) {
local_func_names.push(
quote!(#core_path::backend::chili::local_reactions_intracellular::<
_,
_,
_,
_,
#reactions_intra_solver_order,
>),
);
}
if kwargs.aspects.contains(&ReactionsContact) {
step_1.extend(quote!(sbox.update_contact_reactions_step_1()?;));
step_2.extend(quote!(sbox.update_contact_reactions_step_2(#determinism)?;));
step_3.extend(quote!(sbox.update_contact_reactions_step_3(#determinism)?;));
local_func_names.push(quote!(#core_path::backend::chili::local_update_contact_reactions));
}
if kwargs
.aspects
.contains_any([&Reactions, &ReactionsContact, &ReactionsExtra])
{
local_func_names.push(quote!(#core_path::backend::chili::local_reactions_use_increment));
}
if kwargs.aspects.contains(&ReactionsExtra) {
step_1.extend(quote!(sbox.update_reactions_extra_step_1()?;));
step_2.extend(quote!(sbox.update_reactions_extra_step_2(#determinism)?;));
step_3.extend(quote!(sbox.update_reactions_extra_step_3(#determinism)?;));
local_subdomain_func_names
.push(quote!(#core_path::backend::chili::local_subdomain_update_reactions_extra));
}
let update_local_subdomain_funcs = quote!(
sbox.run_local_subdomain_funcs(
#[inline(always)]
|
subdomain: &mut _,
dt,
| -> Result<(), #core_path::backend::chili::SimulationError> {
#(
#local_subdomain_func_names(subdomain, dt)?;
)*
Ok(())
},
&next_time_point
)?;
);
let update_local_cell_funcs = quote!(
sbox.run_local_cell_funcs(
#[inline(always)]
|
cell: &mut _,
aux_storage: &mut _,
dt,
rng: &mut rand_chacha::ChaCha8Rng
| -> Result<(), #core_path::backend::chili::SimulationError> {
#(
#local_func_names(cell, aux_storage, dt, rng)?;
)*
Ok(())
},
&next_time_point
)?;
);
quote!(
let mut _time_stepper = #settings.time.clone();
use #core_path::time::TimeStepper;
#[allow(unused)]
let mut pb = match (key, &#settings.progressbar) {
(0, Some(title)) => Some(_time_stepper.initialize_bar(Some(&title))?),
_ => None,
};
if let Some(next_time_point) = _time_stepper.save_initial() {
sbox.save_subdomains(&mut _storage_manager_subdomains, &next_time_point)?;
sbox.save_cells(&mut _storage_manager_cells, &next_time_point)?;
}
while let Some(next_time_point) = _time_stepper.advance()? {
let mut f = || -> Result<(), #core_path::backend::chili::SimulationError> {
#step_1
sbox.sync()?;
#step_2
sbox.sync()?;
#step_3
#update_local_subdomain_funcs
#update_local_cell_funcs
#step_4
sbox.sync()?;
#step_5
match (&mut pb, &#settings.progressbar.is_some()) {
(Some(bar), true) => _time_stepper.update_bar(bar)?,
_ => (),
};
sbox.save_subdomains(&mut _storage_manager_subdomains, &next_time_point)?;
sbox.save_cells(&mut _storage_manager_cells, &next_time_point)?;
Ok(())
};
let e = f();
if sbox.store_error(e)? {break}
}
Ok(#core_path::backend::chili::StorageAccess {
cells: _storage_manager_cells.clone(),
subdomains: _storage_manager_subdomains.clone(),
})
)
}
pub fn run_main(kwargs: KwargsMain) -> proc_macro2::TokenStream {
let asp = &kwargs
.aspects
.items
.iter()
.map(|asp| asp.ident.clone())
.collect::<Vec<_>>();
let domain = &kwargs.domain;
let agents = &kwargs.agents;
let settings = &kwargs.settings;
let core_path = &kwargs.core_path;
let aux_storage_name = &kwargs.aux_storage_name;
let communicator_name = &kwargs.communicator_name;
let aux_storage_placeholders = crate::aux_storage::generics_placeholders(
kwargs.clone(),
kwargs.mechanics_solver_order,
kwargs.reactions_contact_solver_order,
);
let aux_storage_constructor = crate::aux_storage::default_aux_storage_initializer(&kwargs);
let update_func = run_main_update(kwargs.clone());
let create_storage = quote::quote!(
let builder = #settings.storage.clone().init();
let builder_subdomains = builder.clone().suffix(builder.get_suffix().join("subdomains"));
let builder_cells = builder.clone().suffix(builder.get_suffix().join("cells"));
let _storage_manager_subdomains_base: #core_path::storage::StorageManager<
#core_path::backend::chili::SubDomainPlainIndex,
_
> =
#core_path::storage::StorageManager::open_or_create(builder_subdomains, 0)?;
let _storage_manager_cells_base: #core_path::storage::StorageManager<_, _> =
#core_path::storage::StorageManager::open_or_create(builder_cells, 0)?;
);
let parallelized_update_func =
kwargs
.parallelizer
.parallelize_execution(&update_func, core_path, settings);
quote::quote!({
type _Syncer = #core_path::backend::chili::BarrierSync;
let __run_sim = || -> Result<
#core_path::backend::chili::StorageAccess<_, _>,
#core_path::backend::chili::SimulationError
> {
let mut runner = #core_path::backend::chili::construct_simulation_runner::<
_,
_,
_,
#aux_storage_name<#(#aux_storage_placeholders),*>,
#core_path::backend::chili::communicator_generics_placeholders!(
name: #communicator_name,
aspects: [#(#asp),*]
),
_Syncer,
_
>(
#domain,
#agents,
#settings.n_threads,
#aux_storage_constructor,
)?;
#create_storage
let res = #parallelized_update_func?;
Result::<_, #core_path::backend::chili::SimulationError>::Ok(res)
};
__run_sim()
})
}
pub fn prepare_types(kwargs: KwargsPrepareTypes) -> proc_macro2::TokenStream {
let aux_storage_builder = super::aux_storage::Builder {
struct_name: kwargs.aux_storage_name.clone(),
core_path: kwargs.core_path.clone(),
aspects: kwargs.aspects.clone(),
};
let mut output = aux_storage_builder.build_aux_storage();
let communicator_builder = super::communicator::CommunicatorBuilder {
struct_name: kwargs.communicator_name.clone(),
core_path: kwargs.core_path.clone(),
aspects: kwargs.aspects.clone(),
};
output.extend(communicator_builder.build_communicator());
output
}
pub fn test_compatibility(kwargs: KwargsCompatibility) -> proc_macro2::TokenStream {
let core_path = &kwargs.core_path;
let domain = &kwargs.domain;
let agents = &kwargs.agents;
let settings = &kwargs.settings;
let mut output = quote::quote!(
#core_path::backend::chili::compatibility_tests::domain_agents(
&#domain,
&#agents
);
);
use SimulationAspect::*;
if kwargs
.aspects
.contains_multiple(vec![&Mechanics, &Interaction])
{
output.extend(quote::quote!(
#core_path::backend::chili::compatibility_tests::mechanics_interaction(
&#agents
);
));
}
if kwargs.aspects.contains(&Mechanics) {
output.extend(quote::quote!(
#core_path::backend::chili::compatibility_tests::time_stepper_mechanics(
&#settings.time,
&#agents,
);
#core_path::backend::chili::compatibility_tests::mechanics_implemented(
&#agents,
);
));
}
if kwargs.aspects.contains(&Cycle) {
output.extend(quote::quote!(
#core_path::backend::chili::compatibility_tests::cycle_implemented(
&#agents,
);
));
}
if kwargs.aspects.contains(&ReactionsContact) {
output.extend(quote::quote!(
#core_path::backend::chili::compatibility_tests::reactions_contact_implemented(
&#agents,
);
));
}
if kwargs.aspects.contains(&Reactions) {
output.extend(quote::quote!(
#core_path::backend::chili::compatibility_tests::reactions_implemented(
&#agents,
);
));
}
if kwargs.aspects.contains(&ReactionsExtra) {
output.extend(quote::quote!(
#core_path::backend::chili::compatibility_tests::subdomain_reactions_implemented(
&#domain,
&#agents
);
));
}
output
}
pub fn run_simulation(kwargs: KwargsSim) -> proc_macro2::TokenStream {
let types = prepare_types(KwargsPrepareTypes::from(kwargs.clone()));
let kwargs_compat = KwargsCompatibility::from(kwargs.clone());
let test_compat = test_compatibility(kwargs_compat);
let kwargs_main = KwargsMain::from(kwargs.clone());
let run_main = run_main(kwargs_main);
quote::quote!({
#types
#test_compat
#run_main
})
}