1use crate::calculus::differential::{gradient_4d, partial_derivative};
4
5pub 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 pub fn eval(&self, point: &[f64; 4]) -> [f64; 4] {
25 (self.field)(point)
26 }
27
28 pub fn divergence(&self, point: &[f64; 4], h: f64) -> f64 {
30 divergence_4d(&self.field, point, h)
31 }
32}
33
34pub fn scalar_field_4d<F>(f: F) -> impl Fn(&[f64; 4]) -> f64
36where
37 F: Fn(&[f64; 4]) -> f64,
38{
39 f
40}
41
42pub fn vector_field_4d<F>(f: F) -> VectorField4D<F>
44where
45 F: Fn(&[f64; 4]) -> [f64; 4],
46{
47 VectorField4D::new(f)
48}
49
50pub 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
79pub 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 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 [
99 partial_derivative(&f2, point, 0, h) - partial_derivative(&f1, point, 1, h),
101 partial_derivative(&f3, point, 0, h) - partial_derivative(&f1, point, 2, h),
103 partial_derivative(&f4, point, 0, h) - partial_derivative(&f1, point, 3, h),
105 partial_derivative(&f3, point, 1, h) - partial_derivative(&f2, point, 2, h),
107 partial_derivative(&f4, point, 1, h) - partial_derivative(&f2, point, 3, h),
109 partial_derivative(&f4, point, 2, h) - partial_derivative(&f3, point, 3, h),
111 ]
112}
113
114pub 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
122pub 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
134pub 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 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 assert!((div - 4.0).abs() < 1e-4);
151 }
152
153 #[test]
154 fn test_divergence_zero() {
155 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 assert!(div.abs() < 1e-4);
161 }
162
163 #[test]
164 fn test_curl_4d() {
165 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 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 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}