oxirs_physics/gpu/mod.rs
1//! GPU-accelerated kernels for physics simulations.
2//!
3//! When compiled with the `gpu` feature, this module exposes dispatchers that
4//! attempt to offload finite-element stress assembly, the Navier-Stokes
5//! pressure-Poisson solve, and heat-diffusion stencil updates onto a GPU
6//! compute backend supplied by `scirs2_core`. When the `gpu` feature is
7//! disabled (the default), every dispatcher returns
8//! [`GpuError::BackendUnavailable`] immediately so callers can fall back to
9//! the existing CPU paths without conditional compilation at the call site.
10//!
11//! This mirrors the SAMM W3-S12 GPU pattern: feature-gated, default off,
12//! pure-Rust default surface, opt-in C/Fortran via the `gpu` feature.
13//!
14//! # Layout
15//!
16//! - [`stress_assembly`] — element stiffness and mass matrix assembly.
17//! - [`navier_stokes_kernel`] — pressure-Poisson and Jacobi pressure solves.
18//! - [`heat_kernel`] — explicit and ADI heat-diffusion stencils.
19//!
20//! # Feature gate
21//!
22//! Enable via `Cargo.toml`:
23//!
24//! ```toml
25//! [dependencies]
26//! oxirs-physics = { version = "*", features = ["gpu"] }
27//! ```
28//!
29//! # Example
30//!
31//! ```rust,no_run
32//! use oxirs_physics::gpu::{GpuElementDescriptor, GpuError, StressAssemblyDispatcher};
33//!
34//! let dispatcher = StressAssemblyDispatcher::new();
35//! let elements = vec![GpuElementDescriptor::default(); 8];
36//! let result = dispatcher.dispatch_stiffness_assembly(&elements);
37//! // Without `gpu` feature: Err(GpuError::BackendUnavailable)
38//! ```
39
40pub mod heat_kernel;
41pub mod navier_stokes_kernel;
42pub mod stress_assembly;
43
44use thiserror::Error;
45
46/// Errors that can arise when using a GPU dispatch path.
47#[derive(Debug, Clone, PartialEq, Eq, Error)]
48pub enum GpuError {
49 /// The GPU backend is not compiled in or is unavailable at runtime.
50 #[error("GPU backend is unavailable; falling back to CPU")]
51 BackendUnavailable,
52
53 /// A kernel produced an unexpected result.
54 #[error("GPU kernel dispatch error: {0}")]
55 DispatchError(String),
56
57 /// Inputs to a GPU kernel were malformed.
58 #[error("GPU kernel input error: {0}")]
59 InvalidInput(String),
60}
61
62/// Result type for GPU dispatch operations.
63pub type GpuResult<T> = Result<T, GpuError>;
64
65/// Whether the underlying compute backend is available at runtime.
66#[inline]
67pub fn backend_available() -> bool {
68 #[cfg(feature = "gpu")]
69 {
70 true
71 }
72 #[cfg(not(feature = "gpu"))]
73 {
74 false
75 }
76}
77
78pub use heat_kernel::HeatKernelDispatcher;
79pub use navier_stokes_kernel::NavierStokesKernelDispatcher;
80pub use stress_assembly::{
81 FemElementKind, GpuElementContribution, GpuElementDescriptor, StressAssemblyDispatcher,
82};
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn backend_available_matches_feature() {
90 #[cfg(feature = "gpu")]
91 assert!(backend_available());
92 #[cfg(not(feature = "gpu"))]
93 assert!(!backend_available());
94 }
95
96 #[test]
97 fn gpu_error_display() {
98 assert!(GpuError::BackendUnavailable
99 .to_string()
100 .contains("unavailable"));
101 assert!(GpuError::DispatchError("foo".to_string())
102 .to_string()
103 .contains("foo"));
104 assert!(GpuError::InvalidInput("bad".to_string())
105 .to_string()
106 .contains("bad"));
107 }
108}