Skip to main content

outram_foam_basic_lib/primitives/
spherical_tensor.rs

1// SPDX-License-Identifier: GPL-3.0-only
2// Copyright (C) 2026 OUTRAM PARK contributors
3// Derived from OpenFOAM (www.openfoam.com)
4// Copyright (C) 2004-2023 OpenFOAM Foundation
5// Copyright (C) 2016-2023 OpenCFD Ltd.
6//
7// This file is part of OUTRAM PARK.
8//
9// OUTRAM PARK is free software: you can redistribute it and/or modify it
10// under the terms of the GNU General Public License as published by the
11// Free Software Foundation, either version 3 of the License, or (at your
12// option) any later version.
13//
14// OUTRAM PARK is distributed in the hope that it will be useful, but
15// WITHOUT ANY WARRANTY; without even the implied warranty of
16// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17// General Public License for more details.
18//
19// You should have received a copy of the GNU General Public License along
20// with OUTRAM PARK.  If not, see <https://www.gnu.org/licenses/>.
21
22//! Isotropic diagonal tensor (`SphericalTensor`) — an OpenFOAM primitive that
23//! stores only the single scalar `ii` of `ii * I`.
24//!
25//! `ii` is a dimensionless `f64`. Because the tensor is a scalar multiple of
26//! the identity, its operations reduce to scalar arithmetic (trace `3*ii`,
27//! determinant `ii³`, inverse `1/ii`).
28
29use std::ops::{Add, Div, Mul, Neg, Sub};
30
31/// Isotropic diagonal tensor: represents `ii * I` where `I` is the 3×3 identity.
32/// Maps to `Foam::SphericalTensor<scalar>` (`SphericalTensorI.H`).
33#[derive(Debug, Clone, Copy, PartialEq, Default)]
34pub struct SphericalTensor {
35    pub ii: f64,
36}
37
38impl SphericalTensor {
39    /// The zero tensor (`ii = 0`).
40    pub const ZERO: Self = Self { ii: 0.0 };
41    /// The identity tensor (`ii = 1`).
42    pub const IDENTITY: Self = Self { ii: 1.0 };
43
44    /// Construct from the single isotropic component `ii` (the tensor is `ii*I`).
45    #[inline]
46    pub fn new(ii: f64) -> Self {
47        Self { ii }
48    }
49
50    /// Trace = 3 * ii
51    #[inline]
52    pub fn tr(self) -> f64 {
53        3.0 * self.ii
54    }
55
56    /// Frobenius norm squared = 3 * ii²
57    #[inline]
58    pub fn mag_sqr(self) -> f64 {
59        3.0 * self.ii * self.ii
60    }
61
62    #[inline]
63    pub fn mag(self) -> f64 {
64        self.mag_sqr().sqrt()
65    }
66
67    /// Diagonal norm squared (sum of squared diagonal entries = 3*ii²)
68    #[inline]
69    pub fn diag_sqr(self) -> f64 {
70        3.0 * self.ii * self.ii
71    }
72
73    /// Determinant = ii³
74    #[inline]
75    pub fn det(self) -> f64 {
76        self.ii * self.ii * self.ii
77    }
78
79    /// Inverse: SphericalTensor(1/ii)
80    #[inline]
81    pub fn inv(self) -> Self {
82        Self { ii: 1.0 / self.ii }
83    }
84
85    /// Double inner-product with itself: 3 * ii²
86    #[inline]
87    pub fn double_inner(self, rhs: Self) -> f64 {
88        3.0 * self.ii * rhs.ii
89    }
90
91    /// Linear interpolation
92    #[inline]
93    pub fn lerp(a: Self, b: Self, t: f64) -> Self {
94        Self {
95            ii: (1.0 - t) * a.ii + t * b.ii,
96        }
97    }
98}
99
100// --- Arithmetic operators ---
101
102impl Neg for SphericalTensor {
103    type Output = Self;
104    #[inline]
105    fn neg(self) -> Self {
106        Self { ii: -self.ii }
107    }
108}
109
110impl Add for SphericalTensor {
111    type Output = Self;
112    #[inline]
113    fn add(self, rhs: Self) -> Self {
114        Self {
115            ii: self.ii + rhs.ii,
116        }
117    }
118}
119
120impl Sub for SphericalTensor {
121    type Output = Self;
122    #[inline]
123    fn sub(self, rhs: Self) -> Self {
124        Self {
125            ii: self.ii - rhs.ii,
126        }
127    }
128}
129
130impl Mul<f64> for SphericalTensor {
131    type Output = Self;
132    #[inline]
133    fn mul(self, s: f64) -> Self {
134        Self { ii: self.ii * s }
135    }
136}
137
138impl Mul<SphericalTensor> for f64 {
139    type Output = SphericalTensor;
140    #[inline]
141    fn mul(self, st: SphericalTensor) -> SphericalTensor {
142        st * self
143    }
144}
145
146impl Div<f64> for SphericalTensor {
147    type Output = Self;
148    #[inline]
149    fn div(self, s: f64) -> Self {
150        Self { ii: self.ii / s }
151    }
152}
153
154/// `scalar / SphericalTensor` — maps to C++ `operator/(Cmpt, SphericalTensor)`
155impl Div<SphericalTensor> for f64 {
156    type Output = SphericalTensor;
157    #[inline]
158    fn div(self, st: SphericalTensor) -> SphericalTensor {
159        SphericalTensor { ii: self / st.ii }
160    }
161}
162
163// --- Free functions mirroring OpenFOAM globals ---
164
165/// Trace tr = 3*ii.
166#[inline]
167pub fn tr(st: SphericalTensor) -> f64 {
168    st.tr()
169}
170
171/// Determinant = ii³.
172#[inline]
173pub fn det(st: SphericalTensor) -> f64 {
174    st.det()
175}
176
177/// Inverse = SphericalTensor(1/ii).
178#[inline]
179pub fn inv(st: SphericalTensor) -> SphericalTensor {
180    st.inv()
181}
182
183/// Frobenius norm squared = 3*ii².
184#[inline]
185pub fn mag_sqr(st: SphericalTensor) -> f64 {
186    st.mag_sqr()
187}
188
189/// Linear interpolation `(1-t)*a + t*b` between two spherical tensors.
190#[inline]
191pub fn lerp(a: SphericalTensor, b: SphericalTensor, t: f64) -> SphericalTensor {
192    SphericalTensor::lerp(a, b, t)
193}
194
195#[cfg(test)]
196mod tests {
197    use super::*;
198
199    #[test]
200    fn identity_properties() {
201        let i = SphericalTensor::IDENTITY;
202        assert_eq!(i.tr(), 3.0);
203        assert_eq!(i.det(), 1.0);
204        assert_eq!(i.inv().ii, 1.0);
205        assert_eq!(i.mag_sqr(), 3.0);
206    }
207
208    #[test]
209    fn arithmetic() {
210        let a = SphericalTensor::new(2.0);
211        let b = SphericalTensor::new(3.0);
212        assert_eq!((a + b).ii, 5.0);
213        assert_eq!((b - a).ii, 1.0);
214        assert_eq!((a * 3.0).ii, 6.0);
215        assert_eq!((3.0 * a).ii, 6.0);
216        assert_eq!((a / 2.0).ii, 1.0);
217    }
218
219    #[test]
220    fn double_inner() {
221        let a = SphericalTensor::new(2.0);
222        let b = SphericalTensor::new(3.0);
223        assert_eq!(a.double_inner(b), 3.0 * 2.0 * 3.0);
224    }
225}