fips_md/runtime/
borrows.rs

1//! Structs representing borrows into the internal particle data
2
3use std::{sync::{RwLockReadGuard, RwLockWriteGuard}};
4
5use anyhow::{anyhow, Result};
6
7use crate::{
8    runtime::{ParticleID, ParticleIndexEntry, ParticleData},
9    parser::FipsType
10};
11
12use super::MemberData;
13
14/// Struct representing an immutable borrow of data belonging to a single particle member
15pub struct FipsBorrow<'a> {
16    /// The borrowed member data
17    member_data: RwLockReadGuard<'a, MemberData>,
18    /// The type of data (used in error checking to prevent unsound casts)
19    typ: FipsType
20}
21
22/// Struct representing an immutable borrow of data belonging to a single particle member
23pub struct FipsBorrowMut<'a> {
24    /// The borrowed member data
25    member_data: RwLockWriteGuard<'a, MemberData>,
26    /// The type of data (used in error checking to prevent unsound casts)
27    typ: FipsType
28}
29
30/// A mutable borrow of all data relating to one particle type
31pub struct ParticleBorrowMut<'a> {
32    particle_id: ParticleID,
33    particle_definition: &'a ParticleIndexEntry,
34    particle_data: RwLockWriteGuard<'a, ParticleData>
35}
36
37impl<'a> ParticleBorrowMut<'a> {
38    pub(crate) fn new(particle_id: ParticleID, particle_definition: &'a ParticleIndexEntry, 
39        particle_data: RwLockWriteGuard<'a, ParticleData>) -> Self
40    {
41        Self {
42            particle_id, particle_data, particle_definition
43        }
44    }
45
46    /// Get particle id
47    pub fn get_particle_id(&self) -> ParticleID {
48        self.particle_id
49    }
50
51    /// Get number of particles of this type
52    pub fn get_particle_count(&self) -> usize {
53        self.particle_data.get_particle_count()
54    }
55
56    /// Borrow all data of a member of this particle type immutably
57    pub fn borrow_member (&self, name: &str) -> Option<FipsBorrow> {
58        match self.particle_definition.get_member_by_name(name) {
59            None => None,
60            Some((member_id, member_definition)) => {
61                let member_data = self.particle_data.borrow_member(&member_id);
62                match member_data {
63                    None => return None,
64                    Some(member_data) => {
65                        let typ = member_definition.get_type();
66                        Some(FipsBorrow {
67                            member_data,
68                            typ: typ.clone()
69                        })
70                    }
71                }
72            }
73        }
74    }
75
76    /// Borrow all data of a member of this particle type mutably
77    pub fn borrow_member_mut (&self, name: &str) -> Option<FipsBorrowMut> {
78        match self.particle_definition.get_member_by_name(name) {
79            None => None,
80            Some((member_id, member_definition)) => {
81                let member_data = self.particle_data.borrow_member_mut(&member_id);
82                match member_data {
83                    None => return None,
84                    Some(member_data) => {
85                        if member_data.is_uniform() {
86                            panic!("Cannot borrow uniform member mutably. Use the uniform_members parameter in create_particles to initialize uniform members.");
87                        }
88                        else {
89                            let typ = member_definition.get_type();
90                            Some(FipsBorrowMut {
91                                member_data, typ: typ.clone()
92                            })
93                        }
94                    }
95                }
96            }
97        }
98    }
99}
100
101impl<'a> FipsBorrow<'a> {
102    /// Interpret the data as a single i64 (only sensible for constants)
103    pub fn as_i64(&self) -> Result<i64> {
104        if !self.member_data.is_uniform() {
105            Err(anyhow!("Cannot access member as single i64 since it is allocated per-particle. Did you mean as_i64_slice()?"))
106        }
107        else {
108            // Type must be scalar and based on i64
109            if self.typ.is_scalar() && self.typ.is_i64_derived() {
110                Ok(self.member_data.as_i64())
111            }
112            else {
113                Err(anyhow!("Member cannot be cast to single i64 (type was {})", self.typ))
114            }
115        }
116    }
117
118    /// Interpret the data as a single f64 (only sensible for constants)
119    pub fn as_f64(&self) -> Result<f64> {
120        if !self.member_data.is_uniform() {
121            Err(anyhow!("Cannot access member as single f64 since it is allocated per-particle. Did you mean as_f64_slice()?"))
122        }
123        else {
124            // Type must be scalar and based on f64
125            if self.typ.is_scalar() && self.typ.is_f64_derived() {
126                Ok(self.member_data.as_f64())
127            }
128            else {
129                Err(anyhow!("Member cannot be cast to single f64 (type was {})", self.typ))
130            }
131        }
132    }
133
134    /// Interpret the data as a slice of double values
135    pub fn as_f64_slice(&self) -> Result<&[f64]> {
136        // Anything can be cast to a slice as long as it is based on f64 values
137        if self.typ.is_f64_derived() {
138            Ok(self.member_data.as_f64_slice())
139        }
140        else {
141            Err(anyhow!("Member cannot be cast to slice of f64 (type was {})", self.typ))
142        }
143    }
144
145    /// Interpret the data as a slice of double values
146    pub fn as_i64_slice(&self) -> Result<&[i64]> {
147        // Anything can be cast to a slice as long as it is based on f64 values
148        if self.typ.is_i64_derived() {
149            Ok(self.member_data.as_i64_slice())
150        }
151        else {
152            Err(anyhow!("Member cannot be cast to slice of i64 (type was {})", self.typ))
153        }
154    }
155}
156
157impl<'a> FipsBorrowMut<'a> {
158    // TODO: Remove as_i64 and as_f64? Constants cannot be borrowed mutably anyway
159
160    /// Interpret the data as a single i64 (only sensible for constants)
161    pub fn as_i64(&self) -> Result<i64> {
162        if !self.member_data.is_uniform() {
163            Err(anyhow!("Cannot access member as single i64 since it is allocated per-particle. Did you mean as_i64_slice()?"))
164        }
165        else {
166            // Type must be scalar and based on i64
167            if self.typ.is_scalar() && self.typ.is_i64_derived() {
168                Ok(self.member_data.as_i64())
169            }
170            else {
171                Err(anyhow!("Member cannot be cast to single i64 (type was {})", self.typ))
172            }
173        }
174    }
175
176    /// Interpret the data as a single f64 (only sensible for constants)
177    pub fn as_f64(&self) -> Result<f64> {
178        if !self.member_data.is_uniform() {
179            Err(anyhow!("Cannot access member as single f64 since it is allocated per-particle. Did you mean as_f64_slice()?"))
180        }
181        else {
182            // Type must be scalar and based on f64
183            if self.typ.is_scalar() && self.typ.is_f64_derived() {
184                Ok(self.member_data.as_f64())
185            }
186            else {
187                Err(anyhow!("Member cannot be cast to single f64 (type was {})", self.typ))
188            }
189        }
190    }
191
192    /// Interpret the data as a slice of double values
193    pub fn as_f64_slice(&mut self) -> Result<&mut [f64]> {
194        // Anything can be cast to a slice as long as it is based on f64 values
195        if self.typ.is_f64_derived() {
196            Ok(self.member_data.as_f64_slice_mut())
197        }
198        else {
199            Err(anyhow!("Member cannot be cast to slice of f64 (type was {})", self.typ))
200        }
201    }
202
203    /// Interpret the data as a slice of double values
204    pub fn as_i64_slice(&mut self) -> Result<&mut [i64]> {
205        // Anything can be cast to a slice as long as it is based on f64 values
206        if self.typ.is_i64_derived() {
207            Ok(self.member_data.as_i64_slice_mut())
208        }
209        else {
210            Err(anyhow!("Member cannot be cast to slice of i64 (type was {})", self.typ))
211        }
212    }
213}