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