use std::collections::HashMap;
use anyhow::{Result, anyhow};
use crate::codegen::{CompiledRuntime};
mod builder;
mod domain;
mod index;
mod interaction;
mod borrows;
mod particle_data;
mod types;
pub use builder::*;
pub use domain::*;
pub use index::*;
pub use interaction::*;
pub use borrows::*;
pub use particle_data::*;
pub use types::*;
use super::parser;
pub struct Runtime {
pub(crate) domain: Domain,
current_time: f64,
time_step: f64,
pub(crate) rng_seeds: Option<Vec<u64>>,
pub(crate) particle_store: ParticleStore,
pub(crate) particle_index: ParticleIndex,
pub(crate) simulation_index: SimulationIndex,
pub(crate) function_index: FunctionIndex,
pub(crate) interaction_index: InteractionIndex,
pub(crate) enabled_interactions: HashMap<InteractionID, InteractionDetails>,
pub(crate) threads_per_particle: HashMap<ParticleID, usize>,
pub(crate) constants: HashMap<String, parser::SubstitutionValue>,
}
pub(crate) const BUILTIN_CONST_NDIM: &str = "NDIM";
pub(crate) const BUILTIN_CONST_DT: &str = "DT";
pub(crate) const BUILTIN_CONSTANTS: &[&str] = &[
BUILTIN_CONST_NDIM,
BUILTIN_CONST_DT
];
impl Runtime {
pub fn new(unit: parser::Unit, domain: Domain, current_time: f64, time_step: f64) -> Result<Self> {
let ndim = domain.get_dim();
let particle_index = ParticleIndex::new(unit.particles)?;
let simulation_index = SimulationIndex::new(unit.simulations, &particle_index)?;
let interaction_index = InteractionIndex::new(unit.interactions, &particle_index)?;
let function_index = FunctionIndex::new(unit.extern_functiondecls)?;
let mut runtime = Self {
domain,
current_time, time_step,
rng_seeds: None,
particle_store: ParticleStore::new(),
particle_index, simulation_index, interaction_index, function_index,
enabled_interactions: HashMap::new(),
threads_per_particle: HashMap::new(),
constants: HashMap::new()
};
runtime.define_constant(BUILTIN_CONST_NDIM.into(), parser::SubstitutionValue::Usize(ndim), true)?;
Ok(runtime)
}
pub fn create_particles<'a>(&'a mut self, particle_name: &str, count: usize,
uniform_members: &UniformMembers, num_threads: usize) -> Result<ParticleBorrowMut<'a>>
{
let particle = match self.particle_index.get_particle_by_name(particle_name) {
None => return Err(anyhow!("Cannot find particle {}", particle_name)),
Some(particle) => particle
};
let particle_data = self.particle_store.create_particles(particle, count, &uniform_members)?;
self.threads_per_particle.insert(particle.0, num_threads);
let (particle_id, particle_definition) = particle;
Ok(ParticleBorrowMut::new(particle_id, particle_definition, particle_data))
}
pub fn define_constant_i64<S: Into<String>>(&mut self, name: S, value: i64) -> Result<()> {
let name = name.into();
let value = parser::SubstitutionValue::I64(value);
self.define_constant(name, value, false)
}
pub fn define_constant_f64<S: Into<String>>(&mut self, name: S, value: f64) -> Result<()> {
let name = name.into();
let value = parser::SubstitutionValue::F64(value);
self.define_constant(name, value, false)
}
pub fn define_constant_usize<S: Into<String>>(&mut self, name: S, value: usize) -> Result<()> {
let name = name.into();
let value = parser::SubstitutionValue::Usize(value);
self.define_constant(name, value, false)
}
fn define_constant(&mut self, name: String, value: parser::SubstitutionValue, builtin: bool) -> Result<()> {
if self.constants.contains_key(&name) {
return Err(anyhow!("Cannot redefine constant {}", name));
};
if !builtin && BUILTIN_CONSTANTS.iter().any(|builtin_name| *builtin_name == name) {
return Err(anyhow!("Cannot define constant {} due to built-in constant with the same name", name))
};
self.particle_index.substitute_constant(&name, &value)?;
self.interaction_index.substitute_constant(&name, &value)?;
self.function_index.substitute_constant(&name, &value)?;
self.constants.insert(name, value);
Ok(())
}
pub fn enable_interaction(&mut self, name: &str, details: InteractionDetails) -> Result<()> {
let (interaction,_) = self.interaction_index.get_interaction_by_name(name)
.ok_or(anyhow!("Cannot find interaction with name {}", name))?;
if self.enabled_interactions.contains_key(&interaction) {
return Err(anyhow!("Interaction with name {} is already enabled", name));
}
self.enabled_interactions.insert(interaction, details);
Ok(())
}
pub fn get_time(&self) -> f64 {
self.current_time
}
pub fn set_time(&mut self, time: f64) {
self.current_time = time
}
pub fn get_time_step(&self) -> f64 {
self.time_step
}
pub fn set_time_step(&mut self, time_step: f64) {
self.time_step = time_step
}
pub fn seed_rngs(&mut self, seeds: Vec<u64>) {
self.rng_seeds = Some(seeds);
}
pub fn get_memory_usage(&self) -> usize {
let mut memory = 0;
memory += self.particle_store.get_memory_usage();
memory
}
pub fn compile(self, simulation_name: &str) -> Result<CompiledRuntime> {
let (simulation, _) = self.simulation_index.get_simulation_by_name(simulation_name)
.ok_or(anyhow!("Cannot find simulation with name {}", simulation_name))?;
CompiledRuntime::new(self, simulation)
}
pub fn borrow_particle_mut(&self, particle_name: &str) -> Result<ParticleBorrowMut> {
let (particle_id, particle_definition) = match self.particle_index.get_particle_by_name(particle_name) {
None => return Err(anyhow!("Cannot find particle {}", particle_name)),
Some(particle) => particle
};
let particle_data = self.particle_store.get_particle_mut(particle_id)
.ok_or(anyhow!("Particle type {} exists, but has not been initialized yet (call create_particles() first)", particle_name))?;
Ok(ParticleBorrowMut::new(particle_id, particle_definition, particle_data))
}
}