pub mod error;
pub use error::{PDEError, PDEResult};
pub mod amr;
pub mod elliptic;
pub mod finite_difference;
pub mod finite_element;
pub mod implicit;
pub mod mesh_generation;
pub mod method_of_lines;
pub mod spectral;
pub mod hdg;
pub mod peridynamics;
pub mod stefan;
pub mod vem;
pub mod fd_solvers;
pub mod fem_1d;
pub mod mol_enhanced;
pub mod spectral_enhanced;
use scirs2_core::ndarray::{Array1, Array2};
use std::ops::Range;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BoundaryConditionType {
Dirichlet,
Neumann,
Robin,
Periodic,
}
#[derive(Debug, Clone)]
pub struct BoundaryCondition<F: 'static + std::fmt::Debug + Copy + PartialOrd> {
pub bc_type: BoundaryConditionType,
pub location: BoundaryLocation,
pub dimension: usize,
pub value: F,
pub coefficients: Option<[F; 3]>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum BoundaryLocation {
Lower,
Upper,
}
#[derive(Debug, Clone)]
pub struct Domain {
pub ranges: Vec<Range<f64>>,
pub grid_points: Vec<usize>,
}
impl Domain {
pub fn new(ranges: Vec<Range<f64>>, grid_points: Vec<usize>) -> PDEResult<Self> {
if ranges.len() != grid_points.len() {
return Err(PDEError::DomainError(
"Number of ranges must match number of grid point specifications".to_string(),
));
}
for (i, range) in ranges.iter().enumerate() {
if range.start >= range.end {
return Err(PDEError::DomainError(format!(
"Invalid range for dimension {i}: start must be less than end"
)));
}
if grid_points[i] < 3 {
return Err(PDEError::DomainError(format!(
"At least 3 grid points required for dimension {i}"
)));
}
}
Ok(Domain {
ranges,
grid_points,
})
}
pub fn dimensions(&self) -> usize {
self.ranges.len()
}
pub fn grid_spacing(&self, dimension: usize) -> PDEResult<f64> {
if dimension >= self.dimensions() {
return Err(PDEError::DomainError(format!(
"Invalid dimension: {dimension}"
)));
}
let range = &self.ranges[dimension];
let n_points = self.grid_points[dimension];
Ok((range.end - range.start) / (n_points - 1) as f64)
}
pub fn grid(&self, dimension: usize) -> PDEResult<Array1<f64>> {
if dimension >= self.dimensions() {
return Err(PDEError::DomainError(format!(
"Invalid dimension: {dimension}"
)));
}
let range = &self.ranges[dimension];
let n_points = self.grid_points[dimension];
let dx = (range.end - range.start) / ((n_points - 1) as f64);
let mut grid = Array1::zeros(n_points);
for i in 0..n_points {
grid[i] = range.start + (i as f64) * dx;
}
Ok(grid)
}
pub fn total_grid_points(&self) -> usize {
self.grid_points.iter().product()
}
}
pub trait PDEProblem<F: 'static + std::fmt::Debug + Copy + PartialOrd> {
fn domain(&self) -> &Domain;
fn boundary_conditions(&self) -> &[BoundaryCondition<F>];
fn num_variables() -> usize;
fn pde_terms() -> PDEResult<()>;
}
pub trait PDESolver<F: 'static + std::fmt::Debug + Copy + PartialOrd> {
fn solve() -> PDEResult<PDESolution<F>>;
}
#[derive(Debug, Clone)]
pub struct PDESolution<F: 'static + std::fmt::Debug + Copy + PartialOrd> {
pub grids: Vec<Array1<f64>>,
pub values: Vec<Array2<F>>,
pub error_estimate: Option<Vec<Array2<F>>>,
pub info: PDESolverInfo,
}
#[derive(Debug, Clone)]
pub struct PDESolverInfo {
pub num_iterations: usize,
pub computation_time: f64,
pub residual_norm: Option<f64>,
pub convergence_history: Option<Vec<f64>>,
pub method: String,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum PDEType {
Parabolic,
Hyperbolic,
Elliptic,
Mixed,
}