oxirs_physics/simulation/
parameter_extraction.rs1use crate::error::{PhysicsError, PhysicsResult};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7pub struct ParameterExtractor {
9 #[allow(dead_code)]
11 store_path: Option<String>,
12}
13
14impl ParameterExtractor {
15 pub fn new() -> Self {
17 Self { store_path: None }
18 }
19
20 pub fn with_store(store_path: impl Into<String>) -> Self {
22 Self {
23 store_path: Some(store_path.into()),
24 }
25 }
26
27 pub async fn extract(
29 &self,
30 entity_iri: &str,
31 simulation_type: &str,
32 ) -> PhysicsResult<SimulationParameters> {
33 self.extract_mock_parameters(entity_iri, simulation_type)
42 .await
43 }
44
45 #[allow(dead_code)]
47 async fn extract_from_sparql(
48 &self,
49 entity_iri: &str,
50 _simulation_type: &str,
51 ) -> PhysicsResult<SimulationParameters> {
52 let _query = format!(
54 r#"
55 PREFIX phys: <http://example.org/physics#>
56 PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
57 PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
58
59 SELECT ?property ?value ?unit ?uncertainty WHERE {{
60 <{entity_iri}> ?property ?value .
61 OPTIONAL {{ ?value phys:unit ?unit }}
62 OPTIONAL {{ ?value phys:uncertainty ?uncertainty }}
63 }}
64 "#,
65 entity_iri = entity_iri
66 );
67
68 Err(PhysicsError::ParameterExtraction(
73 "SPARQL extraction not yet implemented".to_string(),
74 ))
75 }
76
77 async fn extract_mock_parameters(
79 &self,
80 entity_iri: &str,
81 simulation_type: &str,
82 ) -> PhysicsResult<SimulationParameters> {
83 let (initial_conditions, material_properties) = match simulation_type {
85 "thermal" => {
86 let mut ic = HashMap::new();
87 ic.insert(
88 "temperature".to_string(),
89 PhysicalQuantity {
90 value: 293.15, unit: "K".to_string(),
92 uncertainty: Some(0.1),
93 },
94 );
95
96 let mut mp = HashMap::new();
97 mp.insert(
98 "thermal_conductivity".to_string(),
99 MaterialProperty {
100 name: "Thermal Conductivity".to_string(),
101 value: 1.0,
102 unit: "W/(m*K)".to_string(),
103 },
104 );
105 mp.insert(
106 "specific_heat".to_string(),
107 MaterialProperty {
108 name: "Specific Heat Capacity".to_string(),
109 value: 4186.0,
110 unit: "J/(kg*K)".to_string(),
111 },
112 );
113 mp.insert(
114 "density".to_string(),
115 MaterialProperty {
116 name: "Density".to_string(),
117 value: 1000.0,
118 unit: "kg/m^3".to_string(),
119 },
120 );
121
122 (ic, mp)
123 }
124 "mechanical" => {
125 let mut ic = HashMap::new();
126 ic.insert(
127 "displacement".to_string(),
128 PhysicalQuantity {
129 value: 0.0,
130 unit: "m".to_string(),
131 uncertainty: Some(1e-6),
132 },
133 );
134
135 let mut mp = HashMap::new();
136 mp.insert(
137 "youngs_modulus".to_string(),
138 MaterialProperty {
139 name: "Young's Modulus".to_string(),
140 value: 200e9, unit: "Pa".to_string(),
142 },
143 );
144 mp.insert(
145 "poisson_ratio".to_string(),
146 MaterialProperty {
147 name: "Poisson's Ratio".to_string(),
148 value: 0.3,
149 unit: "dimensionless".to_string(),
150 },
151 );
152
153 (ic, mp)
154 }
155 _ => (HashMap::new(), HashMap::new()),
156 };
157
158 Ok(SimulationParameters {
159 entity_iri: entity_iri.to_string(),
160 simulation_type: simulation_type.to_string(),
161 initial_conditions,
162 boundary_conditions: Vec::new(),
163 time_span: (0.0, 100.0),
164 time_steps: 100,
165 material_properties,
166 constraints: Vec::new(),
167 })
168 }
169
170 #[allow(dead_code)]
172 async fn parse_samm_model(&self, _samm_uri: &str) -> PhysicsResult<SimulationParameters> {
173 Err(PhysicsError::SammParsing(
177 "SAMM parsing not yet implemented".to_string(),
178 ))
179 }
180}
181
182impl Default for ParameterExtractor {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
190pub struct SimulationParameters {
191 pub entity_iri: String,
193
194 pub simulation_type: String,
196
197 pub initial_conditions: HashMap<String, PhysicalQuantity>,
199
200 pub boundary_conditions: Vec<BoundaryCondition>,
202
203 pub time_span: (f64, f64),
205
206 pub time_steps: usize,
208
209 pub material_properties: HashMap<String, MaterialProperty>,
211
212 pub constraints: Vec<String>,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct PhysicalQuantity {
219 pub value: f64,
220 pub unit: String,
221 pub uncertainty: Option<f64>,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct BoundaryCondition {
227 pub boundary_name: String,
228 pub condition_type: String,
229 pub value: PhysicalQuantity,
230}
231
232#[derive(Debug, Clone, Serialize, Deserialize)]
234pub struct MaterialProperty {
235 pub name: String,
236 pub value: f64,
237 pub unit: String,
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[tokio::test]
245 async fn test_parameter_extractor_thermal() {
246 let extractor = ParameterExtractor::new();
247
248 let params = extractor
249 .extract("urn:example:battery:001", "thermal")
250 .await
251 .unwrap();
252
253 assert_eq!(params.entity_iri, "urn:example:battery:001");
254 assert_eq!(params.simulation_type, "thermal");
255 assert_eq!(params.time_steps, 100);
256 assert_eq!(params.time_span, (0.0, 100.0));
257
258 let temp = params.initial_conditions.get("temperature").unwrap();
260 assert_eq!(temp.value, 293.15); assert_eq!(temp.unit, "K");
262
263 assert!(params
265 .material_properties
266 .contains_key("thermal_conductivity"));
267 assert!(params.material_properties.contains_key("specific_heat"));
268 assert!(params.material_properties.contains_key("density"));
269 }
270
271 #[tokio::test]
272 async fn test_parameter_extractor_mechanical() {
273 let extractor = ParameterExtractor::new();
274
275 let params = extractor
276 .extract("urn:example:beam:001", "mechanical")
277 .await
278 .unwrap();
279
280 assert_eq!(params.simulation_type, "mechanical");
281
282 let disp = params.initial_conditions.get("displacement").unwrap();
284 assert_eq!(disp.value, 0.0);
285 assert_eq!(disp.unit, "m");
286
287 let youngs = params.material_properties.get("youngs_modulus").unwrap();
289 assert_eq!(youngs.value, 200e9); assert_eq!(youngs.unit, "Pa");
291
292 let poisson = params.material_properties.get("poisson_ratio").unwrap();
293 assert_eq!(poisson.value, 0.3);
294 assert_eq!(poisson.unit, "dimensionless");
295 }
296
297 #[tokio::test]
298 async fn test_parameter_extractor_with_store() {
299 let extractor = ParameterExtractor::with_store("./test_store");
300
301 let params = extractor
302 .extract("urn:example:entity", "thermal")
303 .await
304 .unwrap();
305
306 assert!(!params.entity_iri.is_empty());
308 }
309
310 #[test]
311 fn test_physical_quantity_serialization() {
312 let quantity = PhysicalQuantity {
313 value: 300.0,
314 unit: "K".to_string(),
315 uncertainty: Some(0.5),
316 };
317
318 let json = serde_json::to_string(&quantity).unwrap();
319 let deserialized: PhysicalQuantity = serde_json::from_str(&json).unwrap();
320
321 assert_eq!(deserialized.value, 300.0);
322 assert_eq!(deserialized.unit, "K");
323 assert_eq!(deserialized.uncertainty, Some(0.5));
324 }
325
326 #[test]
327 fn test_simulation_parameters_serialization() {
328 let mut ic = HashMap::new();
329 ic.insert(
330 "temperature".to_string(),
331 PhysicalQuantity {
332 value: 293.15,
333 unit: "K".to_string(),
334 uncertainty: None,
335 },
336 );
337
338 let params = SimulationParameters {
339 entity_iri: "urn:test".to_string(),
340 simulation_type: "thermal".to_string(),
341 initial_conditions: ic,
342 boundary_conditions: Vec::new(),
343 time_span: (0.0, 100.0),
344 time_steps: 50,
345 material_properties: HashMap::new(),
346 constraints: Vec::new(),
347 };
348
349 let json = serde_json::to_string(¶ms).unwrap();
350 let deserialized: SimulationParameters = serde_json::from_str(&json).unwrap();
351
352 assert_eq!(deserialized.entity_iri, "urn:test");
353 assert_eq!(deserialized.simulation_type, "thermal");
354 assert_eq!(deserialized.time_steps, 50);
355 }
356
357 #[tokio::test]
358 async fn test_parameter_extractor_unknown_type() {
359 let extractor = ParameterExtractor::new();
360
361 let params = extractor
362 .extract("urn:example:entity", "unknown_type")
363 .await
364 .unwrap();
365
366 assert!(params.initial_conditions.is_empty());
368 assert!(params.material_properties.is_empty());
369 }
370}