Initial commit
This commit is contained in:
@@ -0,0 +1,297 @@
|
||||
// Copyright (c) 2024 RustyTorch++ Team
|
||||
// Licensed under the Apache License, Version 2.0
|
||||
|
||||
//! Numerical integration (quadrature) rules for finite elements.
|
||||
//!
|
||||
//! This module provides comprehensive Gauss quadrature implementations
|
||||
//! for all supported element types with optimized GPU operations.
|
||||
|
||||
pub mod quadrature_adaptive;
|
||||
pub mod quadrature_types;
|
||||
|
||||
pub use quadrature_adaptive::*;
|
||||
pub use quadrature_types::*;
|
||||
|
||||
#[cfg(disabled)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_1d_gauss_legendre() {
|
||||
let rule = QuadratureRule::gauss_legendre_1d(2).unwrap();
|
||||
assert_eq!(rule.num_points(), 2);
|
||||
assert_eq!(rule.dimension, 1);
|
||||
|
||||
// Test integration of f(x) = x^2 from -1 to 1
|
||||
// Exact integral is 2/3
|
||||
let result = rule.integrate(|coords| coords.xi() * coords.xi());
|
||||
assert!((result - 2.0 / 3.0).abs() < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2d_quad_rule() {
|
||||
let rule = QuadratureRule::quad(2).unwrap();
|
||||
assert_eq!(rule.num_points(), 4);
|
||||
assert_eq!(rule.dimension, 2);
|
||||
|
||||
// Test integration of f(x,y) = 1 over [-1,1]^2
|
||||
// Exact integral is 4
|
||||
let result = rule.integrate(|_| 1.0);
|
||||
assert!((result - 4.0).abs() < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_3d_hex_rule() {
|
||||
let rule = QuadratureRule::hex(2).unwrap();
|
||||
assert_eq!(rule.num_points(), 8);
|
||||
assert_eq!(rule.dimension, 3);
|
||||
|
||||
// Test integration of f(x,y,z) = 1 over [-1,1]^3
|
||||
// Exact integral is 8
|
||||
let result = rule.integrate(|_| 1.0);
|
||||
assert!((result - 8.0).abs() < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_triangle_rule() {
|
||||
let rule = QuadratureRule::triangle(1).unwrap();
|
||||
assert_eq!(rule.num_points(), 1);
|
||||
|
||||
// Test integration of f(x,y) = 1 over unit triangle
|
||||
// Area of reference triangle is 0.5
|
||||
let result = rule.integrate(|_| 1.0);
|
||||
assert!((result - 0.5).abs() < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tetrahedron_rule() {
|
||||
let rule = QuadratureRule::tetrahedron(1).unwrap();
|
||||
assert_eq!(rule.num_points(), 1);
|
||||
|
||||
// Test integration of f(x,y,z) = 1 over unit tetrahedron
|
||||
// Volume of reference tetrahedron is 1/6
|
||||
let result = rule.integrate(|_| 1.0);
|
||||
assert!((result - 1.0 / 6.0).abs() < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_high_order_accuracy() {
|
||||
// Test that higher order rules are more accurate
|
||||
let rule_2 = QuadratureRule::gauss_legendre_1d(2).unwrap();
|
||||
let rule_4 = QuadratureRule::gauss_legendre_1d(4).unwrap();
|
||||
|
||||
// Integrate f(x) = x^4
|
||||
let exact = 2.0 / 5.0;
|
||||
let result_2 = rule_2.integrate(|coords| coords.xi().powi(4));
|
||||
let result_4 = rule_4.integrate(|coords| coords.xi().powi(4));
|
||||
|
||||
let error_2 = (result_2 - exact).abs();
|
||||
let error_4 = (result_4 - exact).abs();
|
||||
|
||||
assert!(error_4 < error_2);
|
||||
assert!(error_4 < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_vector_integration() {
|
||||
use nalgebra::DVector;
|
||||
|
||||
let rule = QuadratureRule::quad(2).unwrap();
|
||||
|
||||
// Integrate vector function [x, y]
|
||||
let result = rule.integrate_vector(
|
||||
|coords| DVector::from_vec(vec![coords.xi(), coords.eta()]),
|
||||
2,
|
||||
);
|
||||
|
||||
// Both components should integrate to 0 over symmetric domain
|
||||
assert!(result[0].abs() < 1e-14);
|
||||
assert!(result[1].abs() < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quadrature_validation() {
|
||||
let rule = QuadratureRule::quad(3).unwrap();
|
||||
assert!(rule.validate().is_ok());
|
||||
|
||||
// Test that weights sum correctly
|
||||
let weight_sum: f64 = rule.points.iter().map(|p| p.weight).sum();
|
||||
assert!((weight_sum - 4.0).abs() < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_quadrature_point_access() {
|
||||
let rule = QuadratureRule::line(2).unwrap();
|
||||
|
||||
// Valid access
|
||||
let point = rule.point(0).unwrap();
|
||||
assert!(point.weight > 0.0);
|
||||
|
||||
// Invalid access
|
||||
let invalid = rule.point(100);
|
||||
assert!(invalid.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gauss_lobatto_rule() {
|
||||
let rule = HighOrderQuadrature::gauss_lobatto_1d(3).unwrap();
|
||||
assert_eq!(rule.num_points(), 3);
|
||||
|
||||
// Check that endpoints are included
|
||||
assert_eq!(rule.points[0].coords.xi(), -1.0);
|
||||
assert_eq!(rule.points[2].coords.xi(), 1.0);
|
||||
|
||||
// Test integration
|
||||
let result = rule.integrate(|_| 1.0);
|
||||
assert!((result - 2.0).abs() < 1e-14);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pyramid_rule() {
|
||||
let rule = HighOrderQuadrature::pyramid(1).unwrap();
|
||||
assert_eq!(rule.num_points(), 1);
|
||||
|
||||
// Basic integration test
|
||||
let result = rule.integrate(|_| 1.0);
|
||||
assert!(result > 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wedge_rule() {
|
||||
let rule = HighOrderQuadrature::wedge(2).unwrap();
|
||||
assert!(rule.num_points() > 0);
|
||||
|
||||
// Test that it's a valid 3D rule
|
||||
assert_eq!(rule.dimension, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adaptive_quadrature_1d() {
|
||||
let base_rule = QuadratureRule::line(2).unwrap();
|
||||
let adaptive = AdaptiveQuadrature::new(base_rule, 1e-6, 3);
|
||||
|
||||
// Test function: f(x) = x^2, integral from -1 to 1 = 2/3
|
||||
let result = adaptive
|
||||
.integrate(|coords| coords.xi() * coords.xi())
|
||||
.unwrap();
|
||||
assert!((result - 2.0 / 3.0).abs() < 1e-6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adaptive_quadrature_2d() {
|
||||
let base_rule = QuadratureRule::quad(2).unwrap();
|
||||
let adaptive = AdaptiveQuadrature::new(base_rule, 1e-6, 3);
|
||||
|
||||
// Test function: f(x,y) = x*y, integral over [-1,1]^2 = 0
|
||||
let result = adaptive
|
||||
.integrate(|coords| coords.xi() * coords.eta())
|
||||
.unwrap();
|
||||
assert!(result.abs() < 1e-10, "Expected 0, got {}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adaptive_quadrature_3d() {
|
||||
let base_rule = QuadratureRule::hex(2).unwrap();
|
||||
let adaptive = AdaptiveQuadrature::new(base_rule, 1e-6, 3);
|
||||
|
||||
// Test function: f(x,y,z) = x*y*z, integral over [-1,1]^3 = 0
|
||||
let result = adaptive
|
||||
.integrate(|coords| coords.xi() * coords.eta() * coords.zeta())
|
||||
.unwrap();
|
||||
assert!(result.abs() < 1e-10, "Expected 0, got {}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adaptive_quadrature_discontinuous_function() {
|
||||
let base_rule = QuadratureRule::line(4).unwrap();
|
||||
let adaptive = AdaptiveQuadrature::new(base_rule, 1e-4, 5);
|
||||
|
||||
// Test discontinuous function (requires subdivision for accuracy)
|
||||
let result = adaptive
|
||||
.integrate(|coords| if coords.xi() > 0.0 { 1.0 } else { -1.0 })
|
||||
.unwrap();
|
||||
|
||||
// Integral should be 0 (equal positive and negative areas)
|
||||
assert!(result.abs() < 1e-3, "Expected ~0, got {}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adaptive_quadrature_polynomial() {
|
||||
let base_rule = QuadratureRule::line(3).unwrap();
|
||||
let adaptive = AdaptiveQuadrature::new(base_rule, 1e-8, 4);
|
||||
|
||||
// Test polynomial: f(x) = x^4, integral from -1 to 1 = 2/5
|
||||
let result = adaptive.integrate(|coords| coords.xi().powi(4)).unwrap();
|
||||
let expected = 2.0 / 5.0;
|
||||
assert!(
|
||||
(result - expected).abs() < 1e-12,
|
||||
"Expected {}, got {}",
|
||||
expected,
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_adaptive_quadrature_convergence() {
|
||||
let base_rule = QuadratureRule::line(2).unwrap();
|
||||
|
||||
// Test with tight tolerance
|
||||
let adaptive_tight = AdaptiveQuadrature::new(base_rule.clone(), 1e-10, 5);
|
||||
let result_tight = adaptive_tight
|
||||
.integrate(|coords| coords.xi().exp())
|
||||
.unwrap();
|
||||
|
||||
// Test with loose tolerance
|
||||
let adaptive_loose = AdaptiveQuadrature::new(base_rule, 1e-4, 2);
|
||||
let result_loose = adaptive_loose
|
||||
.integrate(|coords| coords.xi().exp())
|
||||
.unwrap();
|
||||
|
||||
// Tight tolerance should be more accurate
|
||||
let expected = (1.0_f64.exp() - (-1.0_f64).exp()); // e - e^(-1)
|
||||
assert!((result_tight - expected).abs() < (result_loose - expected).abs());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subdomain_creation_1d() {
|
||||
let base_rule = QuadratureRule::line(2).unwrap();
|
||||
let adaptive = AdaptiveQuadrature::new(base_rule, 1e-6, 3);
|
||||
|
||||
// Test subdomain rule creation
|
||||
let subdomain_rule = adaptive.create_subdomain_rule_1d(-1.0, 0.0).unwrap();
|
||||
|
||||
// Verify the subdomain rule integrates correctly over [-1, 0]
|
||||
let result = subdomain_rule.integrate(|_| 1.0); // Constant function
|
||||
assert!((result - 1.0).abs() < 1e-10, "Expected 1.0, got {}", result); // Length of interval
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subdomain_creation_2d() {
|
||||
let base_rule = QuadratureRule::quad(2).unwrap();
|
||||
let adaptive = AdaptiveQuadrature::new(base_rule, 1e-6, 3);
|
||||
|
||||
// Test 2D subdomain rule creation
|
||||
let subdomain_rule = adaptive
|
||||
.create_subdomain_rule_2d(-1.0, 0.0, -1.0, 0.0)
|
||||
.unwrap();
|
||||
|
||||
// Verify the subdomain rule integrates correctly over [-1,0] x [-1,0]
|
||||
let result = subdomain_rule.integrate(|_| 1.0); // Constant function
|
||||
assert!((result - 1.0).abs() < 1e-10, "Expected 1.0, got {}", result); // Area of quarter
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_subdomain_creation_3d() {
|
||||
let base_rule = QuadratureRule::hex(2).unwrap();
|
||||
let adaptive = AdaptiveQuadrature::new(base_rule, 1e-6, 3);
|
||||
|
||||
// Test 3D subdomain rule creation
|
||||
let subdomain_rule = adaptive
|
||||
.create_subdomain_rule_3d(-1.0, 0.0, -1.0, 0.0, -1.0, 0.0)
|
||||
.unwrap();
|
||||
|
||||
// Verify the subdomain rule integrates correctly over [-1,0]^3
|
||||
let result = subdomain_rule.integrate(|_| 1.0); // Constant function
|
||||
assert!((result - 1.0).abs() < 1e-10, "Expected 1.0, got {}", result); // Volume of eighth
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user