avila_math/calculus/
fields.rs

1//! Campos vetoriais e escalares 4D
2
3use crate::calculus::differential::{gradient_4d, partial_derivative};
4
5/// Campo vetorial 4D: F: ℝ⁴ → ℝ⁴
6///
7/// F(x, y, z, w) = (F₁, F₂, F₃, F₄)
8pub struct VectorField4D<F>
9where
10    F: Fn(&[f64; 4]) -> [f64; 4],
11{
12    pub field: F,
13}
14
15impl<F> VectorField4D<F>
16where
17    F: Fn(&[f64; 4]) -> [f64; 4],
18{
19    pub fn new(field: F) -> Self {
20        Self { field }
21    }
22
23    /// Avalia o campo em um ponto
24    pub fn eval(&self, point: &[f64; 4]) -> [f64; 4] {
25        (self.field)(point)
26    }
27
28    /// Divergência do campo: ∇·F = ∂F₁/∂x + ∂F₂/∂y + ∂F₃/∂z + ∂F₄/∂w
29    pub fn divergence(&self, point: &[f64; 4], h: f64) -> f64 {
30        divergence_4d(&self.field, point, h)
31    }
32}
33
34/// Campo escalar 4D: f: ℝ⁴ → ℝ
35pub fn scalar_field_4d<F>(f: F) -> impl Fn(&[f64; 4]) -> f64
36where
37    F: Fn(&[f64; 4]) -> f64,
38{
39    f
40}
41
42/// Campo vetorial 4D: F: ℝ⁴ → ℝ⁴
43pub fn vector_field_4d<F>(f: F) -> VectorField4D<F>
44where
45    F: Fn(&[f64; 4]) -> [f64; 4],
46{
47    VectorField4D::new(f)
48}
49
50/// Divergência de um campo vetorial 4D
51///
52/// ∇·F = ∂F₁/∂x + ∂F₂/∂y + ∂F₃/∂z + ∂F₄/∂w
53///
54/// # Example
55/// ```
56/// use avila_math::calculus::fields::divergence_4d;
57///
58/// // Campo F = (x, y, z, w)
59/// let f = |p: &[f64; 4]| [p[0], p[1], p[2], p[3]];
60///
61/// let div = divergence_4d(&f, &[1.0, 2.0, 3.0, 4.0], 1e-5);
62/// assert!((div - 4.0).abs() < 1e-4);  // div = 1+1+1+1 = 4
63/// ```
64pub fn divergence_4d<F>(field: &F, point: &[f64; 4], h: f64) -> f64
65where
66    F: Fn(&[f64; 4]) -> [f64; 4],
67{
68    let f1 = |p: &[f64]| field(&[p[0], p[1], p[2], p[3]])[0];
69    let f2 = |p: &[f64]| field(&[p[0], p[1], p[2], p[3]])[1];
70    let f3 = |p: &[f64]| field(&[p[0], p[1], p[2], p[3]])[2];
71    let f4 = |p: &[f64]| field(&[p[0], p[1], p[2], p[3]])[3];
72
73    partial_derivative(&f1, point, 0, h)
74        + partial_derivative(&f2, point, 1, h)
75        + partial_derivative(&f3, point, 2, h)
76        + partial_derivative(&f4, point, 3, h)
77}
78
79/// Curl 4D (análogo ao rotacional em 3D)
80///
81/// Em 4D, o curl é um bivector (6 componentes) representando a rotação
82/// em cada um dos 6 planos coordenados: xy, xz, xw, yz, yw, zw
83///
84/// Retorna [curl_xy, curl_xz, curl_xw, curl_yz, curl_yw, curl_zw]
85pub fn curl_4d<F>(field: &F, point: &[f64; 4], h: f64) -> [f64; 6]
86where
87    F: Fn(&[f64; 4]) -> [f64; 4],
88{
89    let f = |p: &[f64; 4]| field(p);
90
91    // Componentes do campo
92    let f1 = |p: &[f64]| f(&[p[0], p[1], p[2], p[3]])[0];
93    let f2 = |p: &[f64]| f(&[p[0], p[1], p[2], p[3]])[1];
94    let f3 = |p: &[f64]| f(&[p[0], p[1], p[2], p[3]])[2];
95    let f4 = |p: &[f64]| f(&[p[0], p[1], p[2], p[3]])[3];
96
97    // Curl nos 6 planos
98    [
99        // Plano xy: ∂F₂/∂x - ∂F₁/∂y
100        partial_derivative(&f2, point, 0, h) - partial_derivative(&f1, point, 1, h),
101        // Plano xz: ∂F₃/∂x - ∂F₁/∂z
102        partial_derivative(&f3, point, 0, h) - partial_derivative(&f1, point, 2, h),
103        // Plano xw: ∂F₄/∂x - ∂F₁/∂w
104        partial_derivative(&f4, point, 0, h) - partial_derivative(&f1, point, 3, h),
105        // Plano yz: ∂F₃/∂y - ∂F₂/∂z
106        partial_derivative(&f3, point, 1, h) - partial_derivative(&f2, point, 2, h),
107        // Plano yw: ∂F₄/∂y - ∂F₂/∂w
108        partial_derivative(&f4, point, 1, h) - partial_derivative(&f2, point, 3, h),
109        // Plano zw: ∂F₄/∂z - ∂F₃/∂w
110        partial_derivative(&f4, point, 2, h) - partial_derivative(&f3, point, 3, h),
111    ]
112}
113
114/// Campo gradiente de um escalar: grad(f) = ∇f
115pub fn gradient_field<F>(scalar_field: F) -> impl Fn(&[f64; 4]) -> [f64; 4]
116where
117    F: Fn(&[f64]) -> f64,
118{
119    move |point: &[f64; 4]| gradient_4d(&scalar_field, point, 1e-7)
120}
121
122/// Campo de velocidade radial 4D: v = r̂ (aponta para fora da origem)
123pub fn radial_field() -> impl Fn(&[f64; 4]) -> [f64; 4] {
124    |p: &[f64; 4]| {
125        let norm = (p[0] * p[0] + p[1] * p[1] + p[2] * p[2] + p[3] * p[3]).sqrt();
126        if norm == 0.0 {
127            [0.0, 0.0, 0.0, 0.0]
128        } else {
129            [p[0] / norm, p[1] / norm, p[2] / norm, p[3] / norm]
130        }
131    }
132}
133
134/// Campo constante 4D
135pub fn constant_field(value: [f64; 4]) -> impl Fn(&[f64; 4]) -> [f64; 4] {
136    move |_: &[f64; 4]| value
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_divergence_identity() {
145        // Campo F = (x, y, z, w)
146        let f = |p: &[f64; 4]| [p[0], p[1], p[2], p[3]];
147        let div = divergence_4d(&f, &[1.0, 2.0, 3.0, 4.0], 1e-5);
148
149        // div(x, y, z, w) = 1+1+1+1 = 4
150        assert!((div - 4.0).abs() < 1e-4);
151    }
152
153    #[test]
154    fn test_divergence_zero() {
155        // Campo incompressível: F = (y, -x, 0, 0)
156        let f = |p: &[f64; 4]| [p[1], -p[0], 0.0, 0.0];
157        let div = divergence_4d(&f, &[1.0, 2.0, 0.0, 0.0], 1e-5);
158
159        // div = 0
160        assert!(div.abs() < 1e-4);
161    }
162
163    #[test]
164    fn test_curl_4d() {
165        // Campo rotacional simples
166        let f = |p: &[f64; 4]| [p[1], -p[0], 0.0, 0.0];
167        let curl = curl_4d(&f, &[1.0, 2.0, 0.0, 0.0], 1e-5);
168
169        // No plano xy, curl deve ser não-zero
170        assert!(curl[0].abs() > 1e-4);
171    }
172
173    #[test]
174    fn test_radial_field() {
175        let field = radial_field();
176        let v = field(&[3.0, 0.0, 0.0, 0.0]);
177
178        // Campo radial em (3,0,0,0) deve ser (1,0,0,0)
179        assert!((v[0] - 1.0).abs() < 1e-10);
180        assert!(v[1].abs() < 1e-10);
181        assert!(v[2].abs() < 1e-10);
182        assert!(v[3].abs() < 1e-10);
183    }
184
185    #[test]
186    fn test_vector_field_4d() {
187        let field = vector_field_4d(|p: &[f64; 4]| [p[0], p[1], p[2], p[3]]);
188        let v = field.eval(&[1.0, 2.0, 3.0, 4.0]);
189
190        assert_eq!(v, [1.0, 2.0, 3.0, 4.0]);
191    }
192}