1use super::*;
2
3pub struct SpatialDiscretizer {
5 discretization_method: SpatialDiscretizationMethod,
6 grid_generator: GridGenerator,
7 mesh_generator: MeshGenerator,
8 stencil_operators: StencilOperators,
9}
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub enum SpatialDiscretizationMethod {
14 Structured,
16 Unstructured,
18 AdaptiveMeshRefinement,
20 MovingMesh,
22 SpectralElement,
24 DiscontinuousGalerkin,
26}
27
28pub struct GridGenerator {
30 grid_type: GridType,
31 grid_parameters: GridParameters,
32 quality_metrics: GridQualityMetrics,
33}
34
35#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
37pub enum GridType {
38 Cartesian,
40 Curvilinear,
42 BodyFitted,
44 Overset,
46 Chimera,
48 Adaptive,
50}
51
52#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct GridParameters {
55 pub domain_bounds: Vec<(f64, f64)>,
56 pub grid_spacing: Vec<f64>,
57 pub stretching_function: Option<String>,
58 pub boundary_layer: Option<BoundaryLayerConfig>,
59}
60
61#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct BoundaryLayerConfig {
64 pub thickness: f64,
65 pub stretching_ratio: f64,
66 pub num_points: usize,
67}
68
69#[derive(Debug, Clone)]
71pub struct GridQualityMetrics {
72 pub orthogonality: f64,
73 pub skewness: f64,
74 pub aspect_ratio: f64,
75 pub smoothness: f64,
76 pub expansion_ratio: f64,
77}
78
79pub struct MeshGenerator {
81 mesh_type: MeshType,
82 mesh_parameters: MeshParameters,
83 quality_metrics: MeshQualityMetrics,
84}
85
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88pub enum MeshType {
89 Triangular,
91 Quadrilateral,
93 Tetrahedral,
95 Hexahedral,
97 Mixed,
99 Hybrid,
101}
102
103#[derive(Debug, Clone, Serialize, Deserialize)]
105pub struct MeshParameters {
106 pub element_size: f64,
107 pub grading_factor: f64,
108 pub refinement_regions: Vec<RefinementRegion>,
109 pub boundary_layer: Option<BoundaryLayerConfig>,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct RefinementRegion {
115 pub region_bounds: Vec<(f64, f64)>,
116 pub refinement_factor: f64,
117 pub element_size: f64,
118}
119
120#[derive(Debug, Clone)]
122pub struct MeshQualityMetrics {
123 pub element_quality: f64,
124 pub node_distribution: f64,
125 pub connectivity: f64,
126 pub aspect_ratio: f64,
127}
128
129pub struct StencilOperators {
131 operators: HashMap<String, StencilOperator>,
132 boundary_stencils: HashMap<String, BoundaryStencil>,
133}
134
135#[derive(Debug, Clone)]
137pub struct StencilOperator {
138 pub operator_id: String,
139 pub operator_type: StencilType,
140 pub stencil_points: Vec<StencilPoint>,
141 pub coefficients: Vec<f64>,
142}
143
144#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
146pub enum StencilType {
147 Central,
149 Forward,
151 Backward,
153 Upwind,
155 HighOrderCompact,
157 WENO,
159 ENO,
161}
162
163#[derive(Debug, Clone)]
165pub struct StencilPoint {
166 pub relative_position: Vec<i32>,
167 pub weight: f64,
168}
169
170#[derive(Debug, Clone)]
172pub struct BoundaryStencil {
173 pub stencil_id: String,
174 pub boundary_type: BoundaryType,
175 pub stencil_points: Vec<StencilPoint>,
176 pub coefficients: Vec<f64>,
177}
178
179impl SpatialDiscretizer {
180 pub fn new() -> Self {
181 Self {
182 discretization_method: SpatialDiscretizationMethod::Structured,
183 grid_generator: GridGenerator::new(),
184 mesh_generator: MeshGenerator::new(),
185 stencil_operators: StencilOperators::new(),
186 }
187 }
188
189 pub fn initialize(&mut self) -> Result<(), PhysicsError> {
190 self.grid_generator.initialize()?;
191 self.mesh_generator.initialize()?;
192 Ok(())
193 }
194
195 pub fn get_discretization_method(&self) -> &SpatialDiscretizationMethod {
197 &self.discretization_method
198 }
199
200 pub fn set_discretization_method(&mut self, method: SpatialDiscretizationMethod) {
202 self.discretization_method = method;
203 }
204
205 pub fn get_stencil_operators(&self) -> &StencilOperators {
207 &self.stencil_operators
208 }
209
210 pub fn get_stencil_operators_mut(&mut self) -> &mut StencilOperators {
212 &mut self.stencil_operators
213 }
214}
215
216impl GridGenerator {
217 pub fn new() -> Self {
218 Self {
219 grid_type: GridType::Cartesian,
220 grid_parameters: GridParameters::new(),
221 quality_metrics: GridQualityMetrics::new(),
222 }
223 }
224
225 pub fn initialize(&mut self) -> Result<(), PhysicsError> {
226 Ok(())
227 }
228
229 pub fn get_grid_type(&self) -> &GridType {
231 &self.grid_type
232 }
233
234 pub fn set_grid_type(&mut self, grid_type: GridType) {
236 self.grid_type = grid_type;
237 }
238
239 pub fn get_grid_parameters(&self) -> &GridParameters {
241 &self.grid_parameters
242 }
243
244 pub fn get_grid_parameters_mut(&mut self) -> &mut GridParameters {
246 &mut self.grid_parameters
247 }
248
249 pub fn get_quality_metrics(&self) -> &GridQualityMetrics {
251 &self.quality_metrics
252 }
253
254 pub fn get_quality_metrics_mut(&mut self) -> &mut GridQualityMetrics {
256 &mut self.quality_metrics
257 }
258}
259
260impl GridParameters {
261 pub fn new() -> Self {
262 Self {
263 domain_bounds: vec![(0.0, 1.0), (0.0, 1.0), (0.0, 1.0)],
264 grid_spacing: vec![0.01, 0.01, 0.01],
265 stretching_function: None,
266 boundary_layer: None,
267 }
268 }
269}
270
271impl GridQualityMetrics {
272 pub fn new() -> Self {
273 Self {
274 orthogonality: 1.0,
275 skewness: 0.0,
276 aspect_ratio: 1.0,
277 smoothness: 1.0,
278 expansion_ratio: 1.0,
279 }
280 }
281}
282
283impl MeshGenerator {
284 pub fn new() -> Self {
285 Self {
286 mesh_type: MeshType::Hexahedral,
287 mesh_parameters: MeshParameters::new(),
288 quality_metrics: MeshQualityMetrics::new(),
289 }
290 }
291
292 pub fn initialize(&mut self) -> Result<(), PhysicsError> {
293 Ok(())
294 }
295
296 pub fn get_mesh_type(&self) -> &MeshType {
298 &self.mesh_type
299 }
300
301 pub fn set_mesh_type(&mut self, mesh_type: MeshType) {
303 self.mesh_type = mesh_type;
304 }
305
306 pub fn get_mesh_parameters(&self) -> &MeshParameters {
308 &self.mesh_parameters
309 }
310
311 pub fn get_mesh_parameters_mut(&mut self) -> &mut MeshParameters {
313 &mut self.mesh_parameters
314 }
315
316 pub fn get_quality_metrics(&self) -> &MeshQualityMetrics {
318 &self.quality_metrics
319 }
320
321 pub fn get_quality_metrics_mut(&mut self) -> &mut MeshQualityMetrics {
323 &mut self.quality_metrics
324 }
325}
326
327impl MeshParameters {
328 pub fn new() -> Self {
329 Self {
330 element_size: 0.01,
331 grading_factor: 1.2,
332 refinement_regions: Vec::new(),
333 boundary_layer: None,
334 }
335 }
336}
337
338impl MeshQualityMetrics {
339 pub fn new() -> Self {
340 Self {
341 element_quality: 1.0,
342 node_distribution: 1.0,
343 connectivity: 1.0,
344 aspect_ratio: 1.0,
345 }
346 }
347}
348
349impl StencilOperators {
350 pub fn new() -> Self {
351 Self {
352 operators: HashMap::new(),
353 boundary_stencils: HashMap::new(),
354 }
355 }
356
357 pub fn register_operator(&mut self, name: &str, stencil: StencilOperator) {
359 self.operators.insert(name.to_string(), stencil);
360 }
361
362 pub fn register_boundary_stencil(&mut self, name: &str, stencil: BoundaryStencil) {
364 self.boundary_stencils.insert(name.to_string(), stencil);
365 }
366
367 pub fn apply_derivative(
377 &self,
378 name: &str,
379 field: &[f64],
380 dx: f64,
381 index: usize,
382 ) -> Result<f64, PhysicsError> {
383 let stencil = self.operators.get(name).ok_or_else(|| {
384 PhysicsError::SolverError(format!("Stencil operator '{}' not registered", name))
385 })?;
386
387 if stencil.stencil_points.len() != stencil.coefficients.len() {
388 return Err(PhysicsError::SolverError(format!(
389 "Stencil operator '{}' has mismatched points/coefficients",
390 name
391 )));
392 }
393
394 let n = field.len() as isize;
395 let mut sum = 0.0f64;
396 for (point, coeff) in stencil
397 .stencil_points
398 .iter()
399 .zip(stencil.coefficients.iter())
400 {
401 let offset = point.relative_position.first().copied().unwrap_or(0) as isize;
402 let idx = index as isize + offset;
403 if idx < 0 || idx >= n {
404 return Err(PhysicsError::SolverError(format!(
405 "Stencil operator '{}' accesses out-of-bounds index {} (field len {})",
406 name, idx, n
407 )));
408 }
409 sum += coeff * field[idx as usize];
410 }
411
412 if dx <= 0.0 {
413 return Err(PhysicsError::SolverError("dx must be positive".to_string()));
414 }
415
416 Ok(sum / dx)
417 }
418
419 pub fn central_difference_2nd_order() -> StencilOperator {
424 StencilOperator {
425 operator_id: "central_difference_2nd_order".to_string(),
426 operator_type: StencilType::Central,
427 stencil_points: vec![
428 StencilPoint {
429 relative_position: vec![-1],
430 weight: 1.0,
431 },
432 StencilPoint {
433 relative_position: vec![0],
434 weight: 1.0,
435 },
436 StencilPoint {
437 relative_position: vec![1],
438 weight: 1.0,
439 },
440 ],
441 coefficients: vec![-0.5, 0.0, 0.5],
442 }
443 }
444
445 pub fn forward_difference_1st_order() -> StencilOperator {
450 StencilOperator {
451 operator_id: "forward_difference_1st_order".to_string(),
452 operator_type: StencilType::Forward,
453 stencil_points: vec![
454 StencilPoint {
455 relative_position: vec![0],
456 weight: 1.0,
457 },
458 StencilPoint {
459 relative_position: vec![1],
460 weight: 1.0,
461 },
462 ],
463 coefficients: vec![-1.0, 1.0],
464 }
465 }
466}