1use std::collections::HashMap;
4
5use anyhow::{Result, anyhow};
6
7use crate::codegen::{CompiledRuntime};
8
9mod builder;
10mod domain;
11mod index;
12mod interaction;
13mod borrows;
14mod particle_data;
15mod types;
16
17pub use builder::*;
18pub use domain::*;
19pub use index::*;
20pub use interaction::*;
21pub use borrows::*;
22pub use particle_data::*;
23pub use types::*;
24
25use super::parser;
26
27pub struct Runtime {
28 pub(crate) domain: Domain,
30 current_time: f64,
32 time_step: f64,
34 pub(crate) rng_seeds: Option<Vec<u64>>,
36 pub(crate) particle_store: ParticleStore,
38 pub(crate) particle_index: ParticleIndex,
40 pub(crate) simulation_index: SimulationIndex,
42 pub(crate) function_index: FunctionIndex,
44 pub(crate) interaction_index: InteractionIndex,
46 pub(crate) enabled_interactions: HashMap<InteractionID, InteractionDetails>,
48 pub(crate) threads_per_particle: HashMap<ParticleID, usize>,
50 pub(crate) constants: HashMap<String, parser::SubstitutionValue>,
53}
54
55pub(crate) const BUILTIN_CONST_NDIM: &str = "NDIM";
57pub(crate) const BUILTIN_CONST_DT: &str = "DT";
59pub(crate) const BUILTIN_CONSTANTS: &[&str] = &[
61 BUILTIN_CONST_NDIM,
62 BUILTIN_CONST_DT
63];
64
65impl Runtime {
66 pub fn new(unit: parser::Unit, domain: Domain, current_time: f64, time_step: f64) -> Result<Self> {
67 let ndim = domain.get_dim();
68 let particle_index = ParticleIndex::new(unit.particles)?;
71 let simulation_index = SimulationIndex::new(unit.simulations, &particle_index)?;
72 let interaction_index = InteractionIndex::new(unit.interactions, &particle_index)?;
73 let function_index = FunctionIndex::new(unit.extern_functiondecls)?;
74 let mut runtime = Self {
75 domain,
76 current_time, time_step,
77 rng_seeds: None,
78 particle_store: ParticleStore::new(),
79 particle_index, simulation_index, interaction_index, function_index,
80 enabled_interactions: HashMap::new(),
81 threads_per_particle: HashMap::new(),
82 constants: HashMap::new()
83 };
84 runtime.define_constant(BUILTIN_CONST_NDIM.into(), parser::SubstitutionValue::Usize(ndim), true)?;
86 Ok(runtime)
88 }
89
90 pub fn create_particles<'a>(&'a mut self, particle_name: &str, count: usize,
91 uniform_members: &UniformMembers, num_threads: usize) -> Result<ParticleBorrowMut<'a>>
92 {
93 let particle = match self.particle_index.get_particle_by_name(particle_name) {
95 None => return Err(anyhow!("Cannot find particle {}", particle_name)),
96 Some(particle) => particle
97 };
98 let particle_data = self.particle_store.create_particles(particle, count, &uniform_members)?;
100 self.threads_per_particle.insert(particle.0, num_threads);
101 let (particle_id, particle_definition) = particle;
103 Ok(ParticleBorrowMut::new(particle_id, particle_definition, particle_data))
104 }
105
106 pub fn define_constant_i64<S: Into<String>>(&mut self, name: S, value: i64) -> Result<()> {
108 let name = name.into();
109 let value = parser::SubstitutionValue::I64(value);
110 self.define_constant(name, value, false)
111 }
112
113 pub fn define_constant_f64<S: Into<String>>(&mut self, name: S, value: f64) -> Result<()> {
115 let name = name.into();
116 let value = parser::SubstitutionValue::F64(value);
117 self.define_constant(name, value, false)
118 }
119
120 pub fn define_constant_usize<S: Into<String>>(&mut self, name: S, value: usize) -> Result<()> {
122 let name = name.into();
123 let value = parser::SubstitutionValue::Usize(value);
124 self.define_constant(name, value, false)
125 }
126
127 fn define_constant(&mut self, name: String, value: parser::SubstitutionValue, builtin: bool) -> Result<()> {
129 if self.constants.contains_key(&name) {
131 return Err(anyhow!("Cannot redefine constant {}", name));
132 };
133 if !builtin && BUILTIN_CONSTANTS.iter().any(|builtin_name| *builtin_name == name) {
135 return Err(anyhow!("Cannot define constant {} due to built-in constant with the same name", name))
136 };
137 self.particle_index.substitute_constant(&name, &value)?;
139 self.interaction_index.substitute_constant(&name, &value)?;
140 self.function_index.substitute_constant(&name, &value)?;
141 self.constants.insert(name, value);
143 Ok(())
144 }
145
146 pub fn enable_interaction(&mut self, name: &str, details: InteractionDetails) -> Result<()> {
147 let (interaction,_) = self.interaction_index.get_interaction_by_name(name)
148 .ok_or(anyhow!("Cannot find interaction with name {}", name))?;
149 if self.enabled_interactions.contains_key(&interaction) {
150 return Err(anyhow!("Interaction with name {} is already enabled", name));
151 }
152 self.enabled_interactions.insert(interaction, details);
153 Ok(())
154 }
155
156 pub fn get_time(&self) -> f64 {
158 self.current_time
159 }
160
161 pub fn set_time(&mut self, time: f64) {
163 self.current_time = time
164 }
165
166 pub fn get_time_step(&self) -> f64 {
168 self.time_step
169 }
170
171 pub fn set_time_step(&mut self, time_step: f64) {
173 self.time_step = time_step
174 }
175
176 pub fn seed_rngs(&mut self, seeds: Vec<u64>) {
178 self.rng_seeds = Some(seeds);
179 }
180
181 pub fn get_memory_usage(&self) -> usize {
185 let mut memory = 0;
186 memory += self.particle_store.get_memory_usage();
187 memory
188 }
189
190 pub fn compile(self, simulation_name: &str) -> Result<CompiledRuntime> {
192 let (simulation, _) = self.simulation_index.get_simulation_by_name(simulation_name)
194 .ok_or(anyhow!("Cannot find simulation with name {}", simulation_name))?;
195 CompiledRuntime::new(self, simulation)
196 }
197
198 pub fn borrow_particle_mut(&self, particle_name: &str) -> Result<ParticleBorrowMut> {
200 let (particle_id, particle_definition) = match self.particle_index.get_particle_by_name(particle_name) {
202 None => return Err(anyhow!("Cannot find particle {}", particle_name)),
203 Some(particle) => particle
204 };
205 let particle_data = self.particle_store.get_particle_mut(particle_id)
206 .ok_or(anyhow!("Particle type {} exists, but has not been initialized yet (call create_particles() first)", particle_name))?;
207 Ok(ParticleBorrowMut::new(particle_id, particle_definition, particle_data))
208 }
209}