Skip to main content

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}