deep_causality_physics/mhd/
ideal.rs1use crate::mhd::quantities::{AlfvenSpeed, MagneticPressure};
6use crate::{Density, PhysicalField, PhysicsError};
7use deep_causality_multivector::MultiVector;
8use deep_causality_sparse::CsrMatrix;
9use deep_causality_tensor::CausalTensor;
10use deep_causality_topology::{Manifold, SimplicialComplex};
11use std::collections::HashMap;
12
13pub fn alfven_speed_kernel(
24 b_field: &PhysicalField,
25 density: &Density,
26 permeability: f64,
27) -> Result<AlfvenSpeed, PhysicsError> {
28 let b_mag = b_field.inner().squared_magnitude().sqrt();
29 let rho = density.value();
30
31 if permeability <= 0.0 {
32 return Err(PhysicsError::PhysicalInvariantBroken(
33 "Permeability must be positive".into(),
34 ));
35 }
36
37 if rho < 0.0 {
38 return Err(PhysicsError::PhysicalInvariantBroken(
39 "Density cannot be negative".into(),
40 ));
41 }
42
43 if rho == 0.0 {
44 return Err(PhysicsError::Singularity(
45 "Zero density in Alfven speed".into(),
46 ));
47 }
48
49 let denom = (permeability * rho).sqrt();
50 let va = b_mag / denom;
51
52 AlfvenSpeed::new(va)
53}
54
55pub fn magnetic_pressure_kernel(
65 b_field: &PhysicalField,
66 permeability: f64,
67) -> Result<MagneticPressure, PhysicsError> {
68 let b_sq = b_field.inner().squared_magnitude();
69
70 if permeability <= 0.0 {
71 return Err(PhysicsError::PhysicalInvariantBroken(
72 "Permeability must be positive".into(),
73 ));
74 }
75
76 let pb = b_sq / (2.0 * permeability);
77 MagneticPressure::new(pb)
78}
79
80pub fn ideal_induction_kernel(
101 v_manifold: &Manifold<f64>,
102 b_manifold: &Manifold<f64>,
103) -> Result<CausalTensor<f64>, PhysicsError> {
104 let complex = v_manifold.complex();
106 let skeletons = complex.skeletons();
107
108 if skeletons.len() < 3 {
110 return Err(PhysicsError::DimensionMismatch(
111 "Manifold must be at least 2D (preferably 3D) for induction".into(),
112 ));
113 }
114
115 let n0 = skeletons[0].simplices().len();
116 let n1 = skeletons[1].simplices().len();
117 let n2 = skeletons[2].simplices().len();
118
119 if v_manifold.data().len() < n0 + n1 + n2 {
121 return Err(PhysicsError::DimensionMismatch(
122 "v_manifold data too small".into(),
123 ));
124 }
125
126 let v_offset = n0;
129 let v_slice = &v_manifold.data().as_slice()[v_offset..v_offset + n1];
130
131 let b_offset = n0 + n1;
133 let b_slice = &b_manifold.data().as_slice()[b_offset..b_offset + n2];
134
135 if complex.hodge_star_operators().len() <= 2 {
139 return Err(PhysicsError::CalculationError(
140 "Hodge star operator for 2-forms not available".into(),
141 ));
142 }
143 let h_star_2 = &complex.hodge_star_operators()[2];
144 let star_b_data = apply_csr_f64(h_star_2, b_slice);
145
146 let wedge_data = wedge_product_1form_1form(v_slice, &star_b_data, complex)?;
149
150 let iv_b_data = apply_csr_f64(h_star_2, &wedge_data);
153
154 if complex.coboundary_operators().len() <= 1 {
158 return Err(PhysicsError::CalculationError(
159 "Coboundary operator for 1-forms not available".into(),
160 ));
161 }
162 let d_1 = &complex.coboundary_operators()[1];
163 let dt_b_neg_data = apply_csr_i8_f64(d_1, &iv_b_data);
164
165 let result_len = dt_b_neg_data.len();
168 CausalTensor::new(dt_b_neg_data, vec![result_len]).map_err(PhysicsError::from)
169}
170
171fn apply_csr_f64(matrix: &CsrMatrix<f64>, vector: &[f64]) -> Vec<f64> {
175 let (rows, _cols) = matrix.shape();
176 let mut result = vec![0.0; rows];
177
178 let row_indices = matrix.row_indices();
180 let col_indices = matrix.col_indices();
181 let values = matrix.values();
182
183 for i in 0..rows {
184 let start = row_indices[i];
185 let end = row_indices[i + 1];
186 let mut sum = 0.0;
187 for idx in start..end {
188 let col = col_indices[idx];
189 let val = values[idx];
190 if col < vector.len() {
191 sum += val * vector[col];
192 }
193 }
194 result[i] = sum;
195 }
196 result
197}
198
199fn apply_csr_i8_f64(matrix: &CsrMatrix<i8>, vector: &[f64]) -> Vec<f64> {
201 let (rows, _cols) = matrix.shape();
202 let mut result = vec![0.0; rows];
203
204 let row_indices = matrix.row_indices();
205 let col_indices = matrix.col_indices();
206 let values = matrix.values();
207
208 for i in 0..rows {
209 let start = row_indices[i];
210 let end = row_indices[i + 1];
211 let mut sum = 0.0;
212 for idx in start..end {
213 let col = col_indices[idx];
214 let val = values[idx]; if col < vector.len() {
216 sum += (val as f64) * vector[col];
217 }
218 }
219 result[i] = sum;
220 }
221 result
222}
223
224fn wedge_product_1form_1form(
231 alpha: &[f64],
232 beta: &[f64],
233 complex: &SimplicialComplex,
234) -> Result<Vec<f64>, PhysicsError> {
235 let skeletons = complex.skeletons();
236 if skeletons.len() < 3 {
237 return Err(PhysicsError::DimensionMismatch(
238 "Complex must have 2-simplices".into(),
239 ));
240 }
241 let edges = skeletons[1].simplices();
242 let faces = skeletons[2].simplices();
243
244 let mut edge_map = HashMap::with_capacity(edges.len());
247 for (idx, edge_simplex) in edges.iter().enumerate() {
248 let verts = edge_simplex.vertices();
249 if verts.len() >= 2 {
250 edge_map.insert((verts[0], verts[1]), idx);
251 }
252 }
253
254 let mut result = Vec::with_capacity(faces.len());
255
256 for face in faces {
258 let verts = face.vertices(); if verts.len() != 3 {
260 result.push(0.0);
262 continue;
263 }
264 let v0 = verts[0];
265 let v1 = verts[1];
266 let v2 = verts[2];
267
268 let e01_idx = edge_map.get(&(v0, v1));
271 let e12_idx = edge_map.get(&(v1, v2));
272
273 if let (Some(&idx01), Some(&idx12)) = (e01_idx, e12_idx) {
275 let val_alpha_01 = alpha.get(idx01).unwrap_or(&0.0);
276 let val_beta_12 = beta.get(idx12).unwrap_or(&0.0);
277 let val_beta_01 = beta.get(idx01).unwrap_or(&0.0);
278 let val_alpha_12 = alpha.get(idx12).unwrap_or(&0.0);
279
280 let term1 = val_alpha_01 * val_beta_12;
282 let term2 = val_beta_01 * val_alpha_12;
283
284 result.push(term1 - term2);
285 } else {
286 result.push(0.0);
287 }
288 }
289
290 Ok(result)
291}