fips_md/runtime/
index.rs

1//! Structs relating to the particle, interaction and simulation indices
2
3use std::{collections::{HashMap, HashSet}};
4
5use slotmap::{SlotMap, DefaultKey};
6use anyhow::{Result, anyhow};
7use strum::IntoEnumIterator;
8use strum_macros::{EnumIter};
9
10use crate::parser::{self, CompileTimeConstant, Identifier};
11use crate::parser::ConstantSubstitution;
12
13pub type ParticleID = DefaultKey;
14pub type SimulationID = DefaultKey;
15pub type InteractionID = DefaultKey;
16pub type InteractionQuantityID = DefaultKey;
17pub type FunctionID = DefaultKey;
18
19/* -- General index structure -- */
20
21/// Combination of a slotmap with a hashmap for easy name resolution
22#[derive(Debug)]
23struct Index<ID: slotmap::Key, T> {
24    /// Internal registry for holding indexed elements of type T
25    elements: SlotMap<ID, T>,
26    /// Fast lookup table for names
27    name_table: HashMap<String, ID>
28}
29
30impl<ID: slotmap::Key, T> Index<ID,T> {
31    pub fn new() -> Self {
32        Self {
33            elements: SlotMap::with_key(),
34            name_table: HashMap::new()
35        }
36    }
37
38    pub fn insert(&mut self, element: T, name: String) -> Option<T> {
39        // Try to remove any existing element with the same name
40        let result = match self.name_table.remove(&name) {
41            Some(key) => Some(self.elements.remove(key).unwrap()),
42            None => None
43        };
44        // Insert new element
45        let key = self.elements.insert(element);
46        self.name_table.insert(name, key);
47        result
48    }
49
50    pub fn get(&self, id: &ID) -> Option<&T> {
51        self.elements.get(*id)
52    }
53
54    pub fn get_with_name(&self, name: &str) -> Option<(ID, &T)> {
55        let key = match self.name_table.get(name) {
56            None => return None,
57            Some(key) => key
58        };
59        Some((*key, self.elements.get(*key).unwrap()))
60    }
61
62    pub fn iter(&self) -> impl Iterator<Item=(ID, &T)> {
63        self.elements.iter()
64    }
65
66    pub fn iter_mut(&mut self) -> impl Iterator<Item=(ID, &mut T)> {
67        self.elements.iter_mut()
68    }
69}
70
71/* -- Particle index -- */
72
73/// Central storage for all particle metadata
74pub struct ParticleIndex {
75    /// Internal registry
76    index: Index<ParticleID, ParticleIndexEntry>
77}
78
79impl ParticleIndex {
80    /// Construct new particle index from parsed particle data
81    pub(crate) fn new(mut parsed_particles: Vec<parser::Particle>) -> Result<Self> {
82        // Create internal registry
83        let mut particle_index = Self { index: Index::new() };
84        // Try to insert all particles
85        while !parsed_particles.is_empty() {
86            while let Some(particle) = parsed_particles.pop() {
87                let name = particle.name.clone();
88                // Abort, if particle was redefined
89                match particle_index.index.insert(ParticleIndexEntry::new(particle, &particle_index)?, name) {
90                    None => {},
91                    Some(entry) => { return Err(
92                        anyhow!("Particle {} illegally redefined", entry.get_name())
93                    )}
94                }
95            }
96        }
97        // Fill registry with parsed particles
98        
99        Ok(particle_index)
100    }
101
102    pub(crate) fn get(&self, particle_id: ParticleID) -> Option<&ParticleIndexEntry> {
103        self.index.get(&particle_id)
104    }
105
106    pub(crate) fn get_particle_by_name(&self, particle_name: &str) -> Option<(ParticleID, &ParticleIndexEntry)> {
107        self.index.get_with_name(particle_name)
108    }
109
110    pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
111        for (_, particle_definition) in &mut self.index.iter_mut() {
112            particle_definition.substitute_constant(name, value)?
113        }
114        Ok(())
115    }
116
117}
118
119pub type MemberID = DefaultKey;
120
121/// Index entry for a single particle type
122#[derive(Debug)]
123pub struct ParticleIndexEntry {
124    /// Parsed particle information (members are stripped from this)
125    particle: parser::Particle,
126    /// Internal registry holding member data
127    member_index: Index<MemberID, MemberIndexEntry>
128}
129
130impl ParticleIndexEntry {
131    /// Construct new particle index entry from parsed particle
132    pub(crate) fn new(mut parsed_particle: parser::Particle, _particle_index: &ParticleIndex) -> Result<Self> {
133        // Create internal registry
134        let mut member_index = Index::new();
135        // Fill registry
136        for member in parsed_particle.members {
137            let name = member.name.clone();
138            // Abort, if particle was redefined
139            match member_index.insert(MemberIndexEntry::new(member), name) {
140                None => {},
141                Some(entry) => { return Err(
142                    anyhow!("Member {} of particle {} illegally redefined",
143                        entry.get_name(), parsed_particle.name)
144                )}
145            }
146        }
147        // Fill void left by taking all the members
148        parsed_particle.members = vec![];
149        Ok(ParticleIndexEntry {
150            particle: parsed_particle,
151            member_index
152        })
153    }
154
155    pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
156        for (_, member_definition) in &mut self.member_index.iter_mut() {
157            member_definition.substitute_constant(name, value)?
158        }
159        Ok(())
160    }
161
162    pub fn get_members(&self) -> impl Iterator<Item = (MemberID, &MemberIndexEntry)> {
163        self.member_index.iter()
164    }
165
166    pub fn get_name(&self) -> &str {
167        &self.particle.name
168    }
169
170    pub fn get_member_by_name<'a>(&'a self, member_name: &str) -> Option<(MemberID, &'a MemberIndexEntry)> {
171        self.member_index.get_with_name(member_name)
172    }
173
174    // TODO: this should never fail after passing verification
175    pub fn get_position_member<'a>(&'a self) -> Option<(MemberID, &'a MemberIndexEntry)> {
176        for (member_id, member) in self.member_index.iter() {
177            if member.is_position() {
178                return Some((member_id, member));
179            }
180        }
181        None
182    }
183
184    pub fn get_member<'a>(&'a self, member_id: &MemberID) -> Option<&'a MemberIndexEntry> {
185        self.member_index.get(member_id)
186    }
187}
188
189/// Index entry for a single particle member
190#[derive(Debug, Clone)]
191pub struct MemberIndexEntry {
192    member: parser::ParticleMember
193}
194
195impl MemberIndexEntry {
196    pub(crate) fn new(parsed_member: parser::ParticleMember) -> Self {
197        Self {
198            member: parsed_member
199        }
200    }
201
202    pub(crate) fn get_member_size(&self) -> Result<usize> {
203        self.member.typ.get_size()
204    }
205
206    pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
207        self.member.substitute_constant(name, value)
208    }
209
210    pub(crate) fn is_mutable(&self) -> bool {
211        self.member.mutable
212    }
213
214    pub(crate) fn is_position(&self) -> bool {
215        self.member.is_position
216    }
217
218    pub(crate) fn get_type(&self) -> &parser::FipsType {
219        &self.member.typ
220    }
221
222    pub(crate) fn get_name(&self) -> &str {
223        &self.member.name
224    }
225}
226
227/* -- Simulation index -- */
228
229/// Index of simulations
230pub struct SimulationIndex {
231    index: Index<SimulationID, SimulationIndexEntry>
232}
233
234impl SimulationIndex {
235    /// Construct new simulation index from parsed particle data
236    pub(crate) fn new(parsed_simulations: Vec<parser::Simulation>,
237        particle_index: &ParticleIndex)
238    -> Result<Self> {
239        // Create internal registry
240        let mut index = Index::new();
241        // Fill registry with parsed simulations
242        for simulation in parsed_simulations {
243            let name = simulation.name.clone();
244            // Abort, if particle was redefined
245            match index.insert(SimulationIndexEntry::new(simulation, particle_index)?, name) {
246                None => {},
247                Some(entry) => { return Err(
248                    anyhow!("Particle {} illegally redefined", entry.get_name())
249                )}
250            }
251        }
252        Ok(Self { index })
253    }
254
255    pub(crate) fn get_simulation_by_name(&self, name: &str) -> Option<(SimulationID, &SimulationIndexEntry)> {
256        self.index.get_with_name(name)
257    }
258
259    pub(crate) fn get_simulation(&self, simulation_id: &SimulationID) -> Option<&SimulationIndexEntry> {
260        self.index.get(simulation_id)
261    }
262
263    // pub(crate) fn iter(&self) -> impl Iterator<Item = (SimulationID, &SimulationIndexEntry)> {
264    //     self.index.iter()
265    // }
266
267    // pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
268    //     for (_, simulation_definition) in &mut self.index.iter_mut() {
269    //         simulation_definition.substitute_constant(name, value)?
270    //     }
271    //     Ok(())
272    // }
273}
274
275pub struct SimulationIndexEntry {
276    /// Parsed simulation (stripped of all statement blocks)
277    simulation: parser::Simulation,
278    /// Resolved ID of the default particle
279    default_particle: Option<ParticleID>,
280    /// Simulation blocks
281    blocks: Vec<SimulationBlock>
282}
283
284impl SimulationIndexEntry {
285    pub(crate) fn new(mut simulation: parser::Simulation,
286        particle_index: &ParticleIndex)
287    -> Result<Self> {
288        // Resolve default particle
289        let default_particle = simulation.default_particle.as_ref()
290            .map(|default_particle_name| 
291                particle_index.get_particle_by_name(&default_particle_name)
292                    .map(|(particle_id,_)| particle_id)
293                    .ok_or(anyhow!("Cannot find default particle {} for simulation {}",
294                        default_particle_name, simulation.name))
295            ).transpose()?;
296        // Extract simulation blocks (swap in empty vector)
297        let blocks = simulation.blocks;
298        simulation.blocks = vec![];
299        // Transform parser::SimulationBlock (close to syntax) to
300        // SimulationBlock (more useful for analysis)
301        let blocks = blocks.into_iter()
302            .map(|simulation_block| {
303                // Flatten parser::SimulationBlock enum to single kind field
304                let (kind, subblocks) = match simulation_block {
305                    parser::SimulationBlock::Once(once_block) => (
306                        SimulationBlockKind::Once(once_block.step),
307                        once_block.subblocks
308                    ),
309                    parser::SimulationBlock::Step(step_block) => (
310                        SimulationBlockKind::Step(step_block.step_range),
311                        step_block.subblocks
312                    ),
313                };
314                // Extract statement blocks
315                let statement_blocks = subblocks.into_iter().map(|subblock| {
316                    let particle_filter = match subblock.particle {
317                        None => SimulationParticleFilter::Default,
318                        Some(particle_name) => {
319                            // Resolve particle references
320                            let (particle_id,_) = particle_index.get_particle_by_name(&particle_name)
321                                .ok_or(anyhow!("Cannot find particle {} referenced in simulation {}", 
322                                    particle_name, simulation.name))?;
323                            SimulationParticleFilter::Single(particle_id)
324                        }
325                    };
326                    Ok(SimulationStatementBlock {
327                        particle_filter,
328                        statements: subblock.statements
329                    })
330                }).collect::<Result<Vec<_>>>()?;
331                Ok(SimulationBlock {
332                    kind, statement_blocks
333                })
334        }).collect::<Result<Vec<_>>>()?;
335        // Construct simulation entry
336        Ok(Self {
337            simulation,
338            default_particle,
339            blocks
340        })
341    }
342
343    pub(crate) fn get_name(&self) -> &str {
344        &self.simulation.name
345    }
346
347    pub(crate) fn get_default_particle(&self) -> Option<ParticleID> {
348        self.default_particle
349    }
350
351    // pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
352    //     self.simulation.substitute_constant(name, value)
353    // }
354
355    pub(crate) fn get_blocks(&self) -> &[SimulationBlock] {
356        &self.blocks
357    }
358}
359
360/// Kind of simulation block
361pub enum SimulationBlockKind {
362    /// Block that is executed only for one time step
363    Once(parser::CompileTimeConstant<usize>),
364    /// Block that is executed in multiple time steps
365    Step(parser::StepRange)
366}
367
368/// A simulation block, i.e. some instructions that are executed together in the
369/// same time step
370pub struct SimulationBlock {
371    /// Kind of simulation block
372    pub kind: SimulationBlockKind,
373    /// Blocks with simulation instructions
374    pub statement_blocks: Vec<SimulationStatementBlock>
375}
376
377/// Particles affected by a statement block
378pub enum SimulationParticleFilter {
379    /// Statement block only affects the default particle 
380    Default,
381    /// Statement blocks affects a single particle
382    Single(ParticleID)
383}
384
385pub struct SimulationStatementBlock {
386    /// Filter for particles affected by this statement block
387    pub particle_filter: SimulationParticleFilter,
388    /// Actual statements
389    pub statements: Vec<parser::Statement>
390}
391
392/* -- Interaction index -- */
393
394pub struct InteractionIndex {
395    index: Index<InteractionID, InteractionIndexEntry>
396}
397
398impl InteractionIndex {
399    pub(crate) fn new(parsed_interactions: Vec<parser::Interaction>, 
400        particle_index: &ParticleIndex) -> Result<Self> 
401    {
402        // Create internal registry
403        let mut index = Index::new();
404        // Fill registry with parsed simulations
405        for interaction in parsed_interactions {
406            let name = interaction.name.clone();
407            // Abort, if particle was redefined
408            match index.insert(InteractionIndexEntry::new(interaction, particle_index)?, name) {
409                None => {},
410                Some(entry) => { return Err(
411                    anyhow!("Interaction {} illegally redefined", entry.get_name())
412                )}
413            }
414        }
415        Ok(Self { index })
416    }
417
418    pub(crate) fn get_interaction_by_name(&self, interaction_name: &str) -> Option<(InteractionID, &InteractionIndexEntry)> {
419        self.index.get_with_name(interaction_name)
420    }
421
422    pub(crate) fn get(&self, interaction_id: InteractionID) -> Option<&InteractionIndexEntry> {
423        self.index.get(&interaction_id)
424    }
425
426    pub(crate) fn iter(&self) -> impl Iterator<Item=(InteractionID, &InteractionIndexEntry)> {
427        self.index.iter()
428    }
429
430    pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
431        for (_, interaction_definition) in &mut self.index.iter_mut() {
432            interaction_definition.substitute_constant(name, value)?
433        }
434        Ok(())
435    }
436}
437
438pub struct InteractionIndexEntry {
439    /// Parser result (quantities are stripped from this)
440    interaction: parser::Interaction,
441    /// Resolved particle ID of interacting particle A
442    particle_id_a: ParticleID,
443    /// Resolved particle ID of interacting particle B
444    particle_id_b: ParticleID,
445    /// Index of quantities
446    quantity_index: Index<InteractionQuantityID, InteractionQuantityIndexEntry>
447}
448
449impl InteractionIndexEntry {
450    pub(crate) fn new(mut interaction: parser::Interaction, particle_index: &ParticleIndex) -> Result<Self> {
451        // Create internal registry
452        let mut quantity_index = Index::new();
453        // Fill registry
454        for quantity in interaction.quantities {
455            let name = quantity.name.clone();
456            // Abort, if quantity was redefined
457            match quantity_index.insert(InteractionQuantityIndexEntry::new(quantity), name) {
458                None => {},
459                Some(entry) => { return Err(
460                    anyhow!("Quantitiy {} of interaction {} illegally redefined",
461                        entry.get_name(), interaction.name)
462                )}
463            }
464        }
465        // Fill void left by taking all the quantities
466        interaction.quantities = vec![];
467        // Resolve particle ids
468        let particle_id_a = particle_index.get_particle_by_name(&interaction.type_a)
469            .ok_or(anyhow!("Cannot resolve particle type {} in interaction {}",
470                &interaction.type_a, interaction.name))?.0;
471        let particle_id_b = particle_index.get_particle_by_name(&interaction.type_b)
472            .ok_or(anyhow!("Cannot resolve particle type {} in interaction {}",
473                &interaction.type_a, interaction.name))?.0;
474        // Create entry
475        Ok(Self {
476            interaction,
477            quantity_index,
478            particle_id_a, particle_id_b
479        })
480    }
481
482    /// Get name of interaction
483    pub(crate) fn get_name(&self) -> &str {
484        &self.interaction.name
485    }
486
487    pub(crate) fn get_type_a(&self) -> &str {
488        &self.interaction.type_a
489    }
490
491    pub(crate) fn get_type_b(&self) -> &str {
492        &self.interaction.type_b
493    }
494
495    pub(crate) fn get_name_a(&self) -> &Identifier {
496        &self.interaction.name_a
497    }
498
499    pub(crate) fn get_name_b(&self) -> &Identifier {
500        &self.interaction.name_b
501    }
502
503    pub(crate) fn get_distance_vec(&self) -> Option<&String> {
504        self.interaction.distance_vec.as_ref()
505    }
506
507    pub(crate) fn get_quantity(&self, quantity_id: InteractionQuantityID) -> Option<&InteractionQuantityIndexEntry> {
508        self.quantity_index.get(&quantity_id)
509    }
510
511    pub(crate) fn get_cutoff(&self) -> CompileTimeConstant<f64> {
512        self.interaction.cutoff.clone()
513    }
514
515    pub(crate) fn get_distance_identifier(&self) -> &Identifier {
516        &self.interaction.distance
517    }
518
519    pub(crate) fn get_common_block(&self) -> Option<&Vec<parser::Statement>> {
520        self.interaction.common_block.as_ref()
521    }
522
523    /// Get interaction quantity by name
524    pub(crate) fn get_quantity_by_name(&self, quantity: &str) -> Option<(InteractionQuantityID, &InteractionQuantityIndexEntry)> {
525        self.quantity_index.get_with_name(quantity)
526    }
527
528    pub(crate) fn iter(&self) -> impl Iterator<Item = (InteractionQuantityID, &InteractionQuantityIndexEntry)> {
529        self.quantity_index.iter()
530    }
531
532    /// Get a list of all particles affected by this interaction
533    pub(crate) fn get_affected_particles(&self, _particle_index: &ParticleIndex) -> Result<HashSet<ParticleID>> {
534        let mut affected_particles = HashSet::new();
535        // The direct types are affected for sure
536        affected_particles.insert(self.particle_id_a);
537        affected_particles.insert(self.particle_id_b);
538        // In the future we might have particle polymorphism again...
539        Ok(affected_particles)
540    }
541
542    pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
543        self.interaction.cutoff.substitute_constant(name, value)
544    }
545}
546
547pub struct InteractionQuantityIndexEntry {
548    quantity: parser::InteractionQuantity
549}
550
551impl InteractionQuantityIndexEntry {
552    /// Construct new quantity index entry from parsed particle
553    pub(crate) fn new(quantity: parser::InteractionQuantity) -> Self {
554        Self {
555            quantity
556        }
557    }
558
559    pub(crate) fn get_name(&self) -> &str {
560        &self.quantity.name
561    }
562
563    pub(crate) fn get_target_a(&self) -> &str {
564        &self.quantity.target_a
565    }
566
567    pub(crate) fn get_target_b(&self) -> &str {
568        &self.quantity.target_b
569    }
570
571    pub(crate) fn get_expression(&self) -> &parser::Expression {
572        &self.quantity.expression
573    }
574
575    pub(crate) fn get_reduction_method(&self) -> &parser::ReductionMethod {
576        &self.quantity.reduction_method
577    }
578
579    pub(crate) fn get_symmetry(&self) -> parser::InteractionSymmetry {
580        self.quantity.symmetry
581    }
582}
583
584
585/* -- Function index -- */
586
587/// Index of all global functions (including builtins!)
588pub(crate) struct FunctionIndex {
589    index: Index<FunctionID, FunctionIndexEntry>
590}
591
592impl FunctionIndex {
593    /// Create new function index (automatically instantiates builtins)
594    pub(crate) fn new(externs: Vec<parser::ExternFunctionDecl>) -> Result<Self> {
595        let mut index = Index::new();
596        for builtin in BuiltinFunction::get_all() {
597            let name = builtin.get_name().to_string();
598            match index.insert(FunctionIndexEntry::Builtin(builtin), name) {
599                Some(entry) => { return Err(anyhow!("Internal: builtin name {} duplicated", entry.get_name())) },
600                None => {},
601            }
602        }
603        for externfunc in externs {
604            let name = externfunc.name.clone();
605            match index.insert(FunctionIndexEntry::Extern(externfunc), name) {
606                Some(entry) => { return Err(anyhow!("Cannot redefine function {}", entry.get_name())) },
607                None => {},
608            }
609        }
610
611        Ok(Self {
612            index
613        })
614    }
615
616    pub(crate) fn get(&self, function_id: FunctionID) -> Option<&FunctionIndexEntry> {
617        self.index.get(&function_id)
618    }
619
620    pub(crate) fn get_functions(&self) -> impl Iterator<Item = (FunctionID, &FunctionIndexEntry)> {
621        self.index.iter()
622    }
623
624    pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
625        for (_, function_def) in &mut self.index.iter_mut() {
626            function_def.substitute_constant(name, value)?
627        }
628        Ok(())
629    }
630}
631
632pub(crate) enum FunctionIndexEntry {
633    Extern(parser::ExternFunctionDecl),
634    Builtin(BuiltinFunction)
635}
636
637impl FunctionIndexEntry {
638    pub(crate) fn get_name(&self) -> &str {
639        match self {
640            FunctionIndexEntry::Extern(externfunc) => { &externfunc.name },
641            FunctionIndexEntry::Builtin(builtin) => { builtin.get_name() },
642        }
643    }
644
645    pub(crate) fn needs_callback_target_ptr(&self) -> bool {
646        match self {
647            FunctionIndexEntry::Extern(_) => { false }, // Not supported currently
648            FunctionIndexEntry::Builtin(builtin) => { builtin.needs_callback_target_ptr() },
649        }
650    }
651
652    pub(crate) fn returns_array(&self) -> bool {
653        match self {
654            FunctionIndexEntry::Extern(externfunc) => { 
655                match externfunc.return_type {
656                    parser::FipsType::Double | parser::FipsType::Int64 => false,
657                    parser::FipsType::Array { .. } => true,
658                }
659            },
660            FunctionIndexEntry::Builtin(builtin) => { builtin.returns_array() },
661        }
662    }
663
664    pub(crate) fn substitute_constant(&mut self, name: &str, value: &parser::SubstitutionValue) -> Result<()> {
665        match self {
666            FunctionIndexEntry::Extern(func_decl) => { func_decl.substitute_constant(name, value) },
667            FunctionIndexEntry::Builtin(_) => Ok(()),
668        }
669    }
670}
671
672#[derive(EnumIter)]
673pub(crate) enum BuiltinFunction {
674    Sqrt,
675    Sin,
676    Cos,
677    RandomNormal
678}
679
680impl BuiltinFunction {
681    /// Get all builtin functions
682    pub(crate) fn get_all() -> Vec<Self> {
683        Self::iter().collect::<Vec<_>>()
684    }
685
686    /// Get name this function is called by
687    pub(crate) fn get_name(&self) -> &'static str {
688        match self {
689            Self::Sqrt => "sqrt",
690            Self::Sin => "sin",
691            Self::Cos => "cos",
692            Self::RandomNormal => "random_normal",
693        }
694    }
695
696    /// Some functions require the callback pointer as first argument (e.g. random functions)
697    /// The caller must inject this pointer into the parameter list!
698    pub(crate) fn needs_callback_target_ptr(&self) -> bool {
699        match self {
700            BuiltinFunction::Sqrt => false,
701            BuiltinFunction::Sin => false,
702            BuiltinFunction::Cos => false,
703            BuiltinFunction::RandomNormal => true,
704        }
705    }
706
707    pub(crate) fn returns_array(&self) -> bool {
708        match self {
709            Self::Sqrt => false,
710            Self::Sin => false,
711            Self::Cos => false,
712            Self::RandomNormal => false,
713        }
714    }
715}
716
717
718    // // If true, the function returns via its last parameter (the call just returns void)
719    // pub(crate) returns_array: bool