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