scirs2_integrate/pde/mod.rs
1//! Partial Differential Equation (PDE) solvers
2//!
3//! This module provides implementations of various numerical methods for
4//! solving partial differential equations (PDEs).
5//!
6//! ## Supported Methods
7//!
8//! * Method of Lines (MOL): Converts PDEs to systems of ODEs by discretizing spatial derivatives
9//! * Finite Difference Methods: Approximates derivatives using differences between grid points
10//! * Finite Element Methods: Approximates solutions using basis functions on a mesh
11//! * Spectral Methods: Approximates solutions using global basis functions
12//!
13//! ## Supported Equation Types
14//!
15//! * Parabolic PDEs (e.g., heat equation)
16//! * Hyperbolic PDEs (e.g., wave equation)
17//! * Elliptic PDEs (e.g., Poisson equation)
18//! * Systems of coupled PDEs
19
20pub mod error;
21pub use error::{PDEError, PDEResult};
22
23// Submodules for different PDE solution approaches
24pub mod amr;
25pub mod elliptic;
26pub mod finite_difference;
27pub mod finite_element;
28pub mod implicit;
29pub mod mesh_generation;
30pub mod method_of_lines;
31pub mod spectral;
32
33// Enhanced PDE solvers (v0.3.0)
34pub mod fd_solvers;
35pub mod fem_1d;
36pub mod mol_enhanced;
37pub mod spectral_enhanced;
38
39use scirs2_core::ndarray::{Array1, Array2};
40use std::ops::Range;
41
42/// Enum representing different types of boundary conditions
43#[derive(Debug, Clone, Copy, PartialEq)]
44pub enum BoundaryConditionType {
45 /// Dirichlet boundary condition (fixed value)
46 Dirichlet,
47
48 /// Neumann boundary condition (fixed derivative)
49 Neumann,
50
51 /// Robin/mixed boundary condition (linear combination of value and derivative)
52 Robin,
53
54 /// Periodic boundary condition
55 Periodic,
56}
57
58/// Struct representing a boundary condition for a PDE
59#[derive(Debug, Clone)]
60pub struct BoundaryCondition<F: 'static + std::fmt::Debug + Copy + PartialOrd> {
61 /// Type of boundary condition
62 pub bc_type: BoundaryConditionType,
63
64 /// Location of the boundary (low or high end of a dimension)
65 pub location: BoundaryLocation,
66
67 /// Dimension to which this boundary condition applies
68 pub dimension: usize,
69
70 /// Value for Dirichlet conditions, or derivative value for Neumann conditions
71 pub value: F,
72
73 /// Coefficients for Robin boundary conditions (a*u + b*du/dn = c)
74 /// For Robin conditions: [a, b, c]
75 pub coefficients: Option<[F; 3]>,
76}
77
78/// Enum representing the location of a boundary
79#[derive(Debug, Clone, Copy, PartialEq)]
80pub enum BoundaryLocation {
81 /// Lower boundary of the domain
82 Lower,
83
84 /// Upper boundary of the domain
85 Upper,
86}
87
88/// Domain for the PDE problem
89#[derive(Debug, Clone)]
90pub struct Domain {
91 /// Ranges defining the spatial domain for each dimension
92 pub ranges: Vec<Range<f64>>,
93
94 /// Number of grid points in each dimension
95 pub grid_points: Vec<usize>,
96}
97
98impl Domain {
99 /// Create a new domain with given ranges and number of grid points
100 pub fn new(ranges: Vec<Range<f64>>, grid_points: Vec<usize>) -> PDEResult<Self> {
101 if ranges.len() != grid_points.len() {
102 return Err(PDEError::DomainError(
103 "Number of ranges must match number of grid point specifications".to_string(),
104 ));
105 }
106
107 for (i, range) in ranges.iter().enumerate() {
108 if range.start >= range.end {
109 return Err(PDEError::DomainError(format!(
110 "Invalid range for dimension {i}: start must be less than end"
111 )));
112 }
113
114 if grid_points[i] < 3 {
115 return Err(PDEError::DomainError(format!(
116 "At least 3 grid points required for dimension {i}"
117 )));
118 }
119 }
120
121 Ok(Domain {
122 ranges,
123 grid_points,
124 })
125 }
126
127 /// Get the number of dimensions in the domain
128 pub fn dimensions(&self) -> usize {
129 self.ranges.len()
130 }
131
132 /// Get the grid spacing for a given dimension
133 pub fn grid_spacing(&self, dimension: usize) -> PDEResult<f64> {
134 if dimension >= self.dimensions() {
135 return Err(PDEError::DomainError(format!(
136 "Invalid dimension: {dimension}"
137 )));
138 }
139
140 let range = &self.ranges[dimension];
141 let n_points = self.grid_points[dimension];
142
143 Ok((range.end - range.start) / (n_points - 1) as f64)
144 }
145
146 /// Generate a grid for the given dimension
147 pub fn grid(&self, dimension: usize) -> PDEResult<Array1<f64>> {
148 if dimension >= self.dimensions() {
149 return Err(PDEError::DomainError(format!(
150 "Invalid dimension: {dimension}"
151 )));
152 }
153
154 let range = &self.ranges[dimension];
155 let n_points = self.grid_points[dimension];
156 let dx = (range.end - range.start) / ((n_points - 1) as f64);
157
158 let mut grid = Array1::zeros(n_points);
159 for i in 0..n_points {
160 grid[i] = range.start + (i as f64) * dx;
161 }
162
163 Ok(grid)
164 }
165
166 /// Get the total number of grid points in the domain
167 pub fn total_grid_points(&self) -> usize {
168 self.grid_points.iter().product()
169 }
170}
171
172/// Trait for PDE problems
173pub trait PDEProblem<F: 'static + std::fmt::Debug + Copy + PartialOrd> {
174 /// Get the domain of the PDE problem
175 fn domain(&self) -> &Domain;
176
177 /// Get the boundary conditions of the PDE problem
178 fn boundary_conditions(&self) -> &[BoundaryCondition<F>];
179
180 /// Get the number of dependent variables in the PDE
181 fn num_variables() -> usize;
182
183 /// Get the PDE terms (implementation depends on the specific PDE type)
184 fn pde_terms() -> PDEResult<()>;
185}
186
187/// Trait for PDE solvers
188pub trait PDESolver<F: 'static + std::fmt::Debug + Copy + PartialOrd> {
189 /// Solve the PDE problem
190 fn solve() -> PDEResult<PDESolution<F>>;
191}
192
193/// Solution to a PDE problem
194#[derive(Debug, Clone)]
195pub struct PDESolution<F: 'static + std::fmt::Debug + Copy + PartialOrd> {
196 /// Grid points in each dimension
197 pub grids: Vec<Array1<f64>>,
198
199 /// Solution values
200 /// For a 1D problem with one variable: u(x)
201 /// For a 2D problem with one variable: u(x,y)
202 /// For a 1D problem with two variables: [u(x), v(x)]
203 /// Shape depends on the problem dimensions and number of variables
204 pub values: Vec<Array2<F>>,
205
206 /// Error estimate (if available)
207 pub error_estimate: Option<Vec<Array2<F>>>,
208
209 /// Additional solver information
210 pub info: PDESolverInfo,
211}
212
213/// Information about the PDE solver run
214#[derive(Debug, Clone)]
215pub struct PDESolverInfo {
216 /// Number of iterations performed
217 pub num_iterations: usize,
218
219 /// Computation time in seconds
220 pub computation_time: f64,
221
222 /// Final residual norm
223 pub residual_norm: Option<f64>,
224
225 /// Convergence history
226 pub convergence_history: Option<Vec<f64>>,
227
228 /// Method used to solve the PDE
229 pub method: String,
230}
231
232/// Enum representing PDE types
233#[derive(Debug, Clone, Copy, PartialEq)]
234pub enum PDEType {
235 /// Parabolic PDE (e.g., heat equation)
236 Parabolic,
237
238 /// Hyperbolic PDE (e.g., wave equation)
239 Hyperbolic,
240
241 /// Elliptic PDE (e.g., Poisson equation)
242 Elliptic,
243
244 /// Mixed type PDE
245 Mixed,
246}