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}