use rayon::{
iter::{IntoParallelIterator, ParallelIterator},
slice::{ParallelSlice, ParallelSliceMut},
};
use crate::{
chunksize,
math::{cross3, decompose_filament, dot3, rss3},
};
use crate::{MU0_OVER_4PI, macros::*};
pub fn flux_density_point_segment_par(
xyzp: (&[f64], &[f64], &[f64]),
xyzfil: (&[f64], &[f64], &[f64]),
dlxyzfil: (&[f64], &[f64], &[f64]),
ifil: &[f64],
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<(), &'static str> {
let n = chunksize(xyzp.0.len());
let (xpc, ypc, zpc) = par_chunks_3tup!(xyzp, n);
let (bxc, byc, bzc) = mut_par_chunks_3tup!(out, n);
(bxc, byc, bzc, xpc, ypc, zpc)
.into_par_iter()
.try_for_each(|(bx, by, bz, xp, yp, zp)| {
flux_density_point_segment((xp, yp, zp), xyzfil, dlxyzfil, ifil, (bx, by, bz))
})?;
Ok(())
}
pub fn flux_density_point_segment(
xyzp: (&[f64], &[f64], &[f64]),
xyzfil: (&[f64], &[f64], &[f64]),
dlxyzfil: (&[f64], &[f64], &[f64]),
ifil: &[f64],
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<(), &'static str> {
let (xp, yp, zp) = xyzp;
let (xfil, yfil, zfil) = xyzfil;
let (dlxfil, dlyfil, dlzfil) = dlxyzfil;
let (bx, by, bz) = out;
let n = xfil.len();
let m = xp.len();
check_length!(m, xp, yp, zp, bx, by, bz);
check_length!(n, xfil, yfil, zfil, dlxfil, dlyfil, dlzfil, ifil);
bx.fill(0.0);
by.fill(0.0);
bz.fill(0.0);
for i in 0..n {
for j in 0..m {
let fil0 = (xfil[i], yfil[i], zfil[i]); let fil1 = (fil0.0 + dlxfil[i], fil0.1 + dlyfil[i], fil0.2 + dlzfil[i]); let current = ifil[i];
let obs = (xp[j], yp[j], zp[j]);
let (bxc, byc, bzc) = flux_density_point_segment_scalar((fil0, fil1, current), obs);
bx[j] += bxc;
by[j] += byc;
bz[j] += bzc;
}
}
Ok(())
}
pub fn flux_density_point_segment_scalar(
xyzifil: ((f64, f64, f64), (f64, f64, f64), f64),
xyzobs: (f64, f64, f64),
) -> (f64, f64, f64) {
let (xyz0, xyz1, ifil) = xyzifil;
let (xp, yp, zp) = xyzobs;
let ((xmid, ymid, zmid), dl) = decompose_filament(xyz0, xyz1);
let rx: f64 = xp - xmid; let ry = yp - ymid; let rz = zp - zmid;
let (rx, ry, rz) = (rx, ry, rz);
let dl = (dl.0, dl.1, dl.2);
let ifil = ifil;
let sumsq = dot3(rx, ry, rz, rx, ry, rz);
let rnorm3_inv = sumsq.powf(-1.5);
let c = (MU0_OVER_4PI) * ifil * rnorm3_inv;
let (cx, cy, cz) = cross3(dl.0, dl.1, dl.2, rx, ry, rz);
let bx = c * cx; let by = c * cy;
let bz = c * cz;
(bx, by, bz)
}
pub fn vector_potential_point_segment_par(
xyzp: (&[f64], &[f64], &[f64]),
xyzfil: (&[f64], &[f64], &[f64]),
dlxyzfil: (&[f64], &[f64], &[f64]),
ifil: &[f64],
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<(), &'static str> {
let n = chunksize(xyzp.0.len());
let (xpc, ypc, zpc) = par_chunks_3tup!(xyzp, n);
let (bxc, byc, bzc) = mut_par_chunks_3tup!(out, n);
(bxc, byc, bzc, xpc, ypc, zpc)
.into_par_iter()
.try_for_each(|(bx, by, bz, xp, yp, zp)| {
vector_potential_point_segment((xp, yp, zp), xyzfil, dlxyzfil, ifil, (bx, by, bz))
})?;
Ok(())
}
pub fn vector_potential_point_segment(
xyzp: (&[f64], &[f64], &[f64]),
xyzfil: (&[f64], &[f64], &[f64]),
dlxyzfil: (&[f64], &[f64], &[f64]),
ifil: &[f64],
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<(), &'static str> {
let (xp, yp, zp) = xyzp;
let (xfil, yfil, zfil) = xyzfil;
let (dlxfil, dlyfil, dlzfil) = dlxyzfil;
let (ax, ay, az) = out;
let n = xfil.len();
let m = xp.len();
check_length!(m, xp, yp, zp, ax, ay, az);
check_length!(n, xfil, yfil, zfil, dlxfil, dlyfil, dlzfil, ifil);
ax.fill(0.0);
ay.fill(0.0);
az.fill(0.0);
for i in 0..n {
for j in 0..m {
let fil0 = (xfil[i], yfil[i], zfil[i]); let fil1 = (fil0.0 + dlxfil[i], fil0.1 + dlyfil[i], fil0.2 + dlzfil[i]); let current = ifil[i];
let obs = (xp[j], yp[j], zp[j]);
let (axc, ayc, azc) = vector_potential_point_segment_scalar((fil0, fil1, current), obs);
ax[j] += axc;
ay[j] += ayc;
az[j] += azc;
}
}
Ok(())
}
#[inline]
pub fn vector_potential_point_segment_scalar(
xyzifil: ((f64, f64, f64), (f64, f64, f64), f64),
xyzobs: (f64, f64, f64),
) -> (f64, f64, f64) {
let (xyz0, xyz1, ifil) = xyzifil;
let ((xmid, ymid, zmid), dl) = decompose_filament(xyz0, xyz1);
let (rx, ry, rz) = (xyzobs.0 - xmid, xyzobs.1 - ymid, xyzobs.2 - zmid);
let rnorm = rss3(rx, ry, rz);
let c = MU0_OVER_4PI * (ifil / rnorm);
let ax = c * dl.0;
let ay = c * dl.1;
let az = c * dl.2;
(ax, ay, az)
}
pub fn body_force_density_point_segment_scalar(
xyzifil: ((f64, f64, f64), (f64, f64, f64), f64),
xyzobs: (f64, f64, f64),
jobs: (f64, f64, f64),
) -> (f64, f64, f64) {
let (bx, by, bz) = flux_density_point_segment_scalar(xyzifil, xyzobs);
cross3(jobs.0, jobs.1, jobs.2, bx, by, bz) }
pub fn body_force_density_point_segment(
xyzfil: (&[f64], &[f64], &[f64]),
dlxyzfil: (&[f64], &[f64], &[f64]),
ifil: &[f64],
xyzobs: (&[f64], &[f64], &[f64]),
jobs: (&[f64], &[f64], &[f64]),
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<(), &'static str> {
let (xp, yp, zp) = xyzobs;
let (jx, jy, jz) = jobs;
let (xfil, yfil, zfil) = xyzfil;
let (dlxfil, dlyfil, dlzfil) = dlxyzfil;
let (outx, outy, outz) = out;
let n = xfil.len();
let m = xp.len();
check_length!(n, xfil, yfil, zfil, dlxfil, dlyfil, dlzfil);
check_length!(m, xp, yp, zp, jx, jy, jz, outx, outy, outz);
outx.fill(0.0);
outy.fill(0.0);
outz.fill(0.0);
for i in 0..n {
for j in 0..m {
let fil0 = (xfil[i], yfil[i], zfil[i]); let fil1 = (fil0.0 + dlxfil[i], fil0.1 + dlyfil[i], fil0.2 + dlzfil[i]); let obs = (xp[j], yp[j], zp[j]); let jj = (jx[j], jy[j], jz[j]);
let (jxbx, jxby, jxbz) =
body_force_density_point_segment_scalar((fil0, fil1, ifil[i]), obs, jj);
outx[j] += jxbx;
outy[j] += jxby;
outz[j] += jxbz;
}
}
Ok(())
}
pub fn body_force_density_point_segment_par(
xyzfil: (&[f64], &[f64], &[f64]),
dlxyzfil: (&[f64], &[f64], &[f64]),
ifil: &[f64],
xyzobs: (&[f64], &[f64], &[f64]),
jobs: (&[f64], &[f64], &[f64]),
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<(), &'static str> {
let n = chunksize(xyzobs.0.len());
let (xpc, ypc, zpc) = par_chunks_3tup!(xyzobs, n);
let (jxc, jyc, jzc) = par_chunks_3tup!(jobs, n);
let (outxc, outyc, outzc) = mut_par_chunks_3tup!(out, n);
(outxc, outyc, outzc, xpc, ypc, zpc, jxc, jyc, jzc)
.into_par_iter()
.try_for_each(|(outx, outy, outz, xp, yp, zp, jx, jy, jz)| {
body_force_density_point_segment(
xyzfil,
dlxyzfil,
ifil,
(xp, yp, zp),
(jx, jy, jz),
(outx, outy, outz),
)
})?;
Ok(())
}
#[cfg(test)]
mod test {
use std::f64::consts::PI;
use super::*;
use crate::physics::linear_filament::{
inductance_piecewise_linear_filaments, vector_potential_linear_filament,
};
use crate::testing::*;
#[test]
fn test_body_force_density() {
let (rtol, atol) = (1e-9, 1e-10);
let ndiscr = 100;
let (rfil, zfil, nfil) = example_circular_filaments();
for i in 0..rfil.len() {
let (ri, zi, ni) = (rfil[i], zfil[i], nfil[i]);
let (x, y, z) = discretize_circular_filament(ri, zi, ndiscr);
let dl = (&diff(&x)[..], &diff(&y)[..], &diff(&z)[..]);
let (jxbx, jxby, jxbz) = (
&mut vec![0.0; ndiscr - 1],
&mut vec![0.0; ndiscr - 1],
&mut vec![0.0; ndiscr - 1],
);
body_force_density_point_segment(
(&x[..ndiscr - 1], &y[..ndiscr - 1], &z[..ndiscr - 1]),
dl,
&vec![ni; x.len()][..],
(&x[..ndiscr - 1], &y[..ndiscr - 1], &z[..ndiscr - 1]),
dl,
(jxbx, jxby, jxbz),
)
.unwrap();
let jxbx_sum: f64 = jxbx.iter().sum();
let jxby_sum: f64 = jxby.iter().sum();
let jxbz_sum: f64 = jxbz.iter().sum();
assert!(approx(0.0, jxbx_sum, rtol, atol));
assert!(approx(0.0, jxby_sum, rtol, atol));
assert!(approx(0.0, jxbz_sum, rtol, atol));
for j in 0..ndiscr - 1 {
let r: (f64, f64, f64) = (x[j], y[j], 0.0);
let rxjxb = cross3(r.0, r.1, r.2, jxbx[j], jxby[j], jxbz[j]);
assert!(approx(0.0, rss3(rxjxb.0, rxjxb.1, rxjxb.2), rtol, 1e-8));
}
}
for i in 0..rfil.len() {
let (ri, zif, ni) = (rfil[i], zfil[i], nfil[i]);
let (xi, yi, zi) = discretize_circular_filament(ri, zif, ndiscr);
let dli = (&diff(&xi)[..], &diff(&yi)[..], &diff(&zi)[..]);
for j in 0..rfil.len() {
if j == i {
continue;
}
let (rj, zjf, nj) = (rfil[j], zfil[j], nfil[j]);
let (xj, yj, zj) = discretize_circular_filament(rj, zjf, ndiscr);
let dlj = (&diff(&xj)[..], &diff(&yj)[..], &diff(&zj)[..]);
let mid = (
&midpoints(&xj)[..],
&midpoints(&yj)[..],
&midpoints(&zj)[..],
);
let (jxbx, jxby, jxbz) = (
&mut vec![0.0; ndiscr - 1],
&mut vec![0.0; ndiscr - 1],
&mut vec![0.0; ndiscr - 1],
);
body_force_density_point_segment(
(&xi[..ndiscr - 1], &yi[..ndiscr - 1], &zi[..ndiscr - 1]),
dli,
&vec![ni * nj; xi.len() - 1][..],
mid,
dlj,
(jxbx, jxby, jxbz),
)
.unwrap();
let jxbx_sum: f64 = jxbx.iter().sum();
let jxby_sum: f64 = jxby.iter().sum();
let jxbz_sum: f64 = jxbz.iter().sum();
assert!(approx(0.0, jxbx_sum, rtol, atol));
assert!(approx(0.0, jxby_sum, rtol, atol));
assert!(jxbz_sum.signum() == (zif - zjf).signum());
}
}
}
#[test]
fn test_vector_potential() {
let xyz = [0.0];
let dlxyz = [1.0];
const NFIL: usize = 10;
let xfil2: Vec<f64> = (0..NFIL).map(|i| (i as f64).sin() + PI).collect();
let yfil2: Vec<f64> = (0..NFIL).map(|i| (i as f64).cos() - PI).collect();
let zfil2: Vec<f64> = (0..NFIL)
.map(|i| (i as f64) - (NFIL as f64) / 2.0 + PI)
.collect();
let xyzfil2 = (
&xfil2[..=NFIL - 2],
&yfil2[..=NFIL - 2],
&zfil2[..=NFIL - 2],
);
let dlxfil2: Vec<f64> = (0..=NFIL - 2).map(|i| xfil2[i + 1] - xfil2[i]).collect();
let dlyfil2: Vec<f64> = (0..=NFIL - 2).map(|i| yfil2[i + 1] - yfil2[i]).collect();
let dlzfil2: Vec<f64> = (0..=NFIL - 2).map(|i| zfil2[i + 1] - zfil2[i]).collect();
let dlxyzfil2 = (&dlxfil2[..], &dlyfil2[..], &dlzfil2[..]);
let gl3_unit_nodes = [0.11270166537925831, 0.5, 0.8872983346207417];
let gl3_unit_weights = [0.2777777777777778, 0.4444444444444444, 0.2777777777777778];
let mut xquad2 = vec![0.0; 3 * (NFIL - 1)];
let mut yquad2 = vec![0.0; 3 * (NFIL - 1)];
let mut zquad2 = vec![0.0; 3 * (NFIL - 1)];
for i in 0..NFIL - 1 {
let row = 3 * i;
for (iq, tq) in gl3_unit_nodes.iter().enumerate() {
xquad2[row + iq] = dlxfil2[i].mul_add(*tq, xfil2[i]);
yquad2[row + iq] = dlyfil2[i].mul_add(*tq, yfil2[i]);
zquad2[row + iq] = dlzfil2[i].mul_add(*tq, zfil2[i]);
}
}
let outx = &mut [0.0; 3 * (NFIL - 1)];
let outy = &mut [0.0; 3 * (NFIL - 1)];
let outz = &mut [0.0; 3 * (NFIL - 1)];
vector_potential_point_segment(
(&xquad2, &yquad2, &zquad2),
(&xyz, &xyz, &xyz),
(&dlxyz, &dlxyz, &dlxyz),
&[1.0],
(outx, outy, outz),
)
.unwrap();
let m_from_point_segment_a = (0..NFIL - 1)
.map(|i| {
let row = 3 * i;
(0..3)
.map(|iq| {
let idx = row + iq;
gl3_unit_weights[iq]
* (outx[idx] * dlxfil2[i]
+ outy[idx] * dlyfil2[i]
+ outz[idx] * dlzfil2[i])
})
.sum::<f64>()
})
.sum::<f64>();
let outx_ref = &mut [0.0; 3 * (NFIL - 1)];
let outy_ref = &mut [0.0; 3 * (NFIL - 1)];
let outz_ref = &mut [0.0; 3 * (NFIL - 1)];
vector_potential_linear_filament(
(&xquad2, &yquad2, &zquad2),
(&xyz, &xyz, &xyz),
(&dlxyz, &dlxyz, &dlxyz),
&[1.0],
&[0.0],
(outx_ref, outy_ref, outz_ref),
)
.unwrap();
let m_from_line_a = (0..NFIL - 1)
.map(|i| {
let row = 3 * i;
(0..3)
.map(|iq| {
let idx = row + iq;
gl3_unit_weights[iq]
* (outx_ref[idx] * dlxfil2[i]
+ outy_ref[idx] * dlyfil2[i]
+ outz_ref[idx] * dlzfil2[i])
})
.sum::<f64>()
})
.sum::<f64>();
let wire_radius = [0.0];
let m = inductance_piecewise_linear_filaments(
(&xyz, &xyz, &xyz),
(&dlxyz, &dlxyz, &dlxyz),
xyzfil2,
dlxyzfil2,
&wire_radius,
)
.unwrap();
assert!(approx(m, m_from_line_a, 1e-12, 1e-15));
assert!(m_from_point_segment_a.is_finite());
let vp = |x: f64, y: f64, z: f64| {
let mut outx = [0.0];
let mut outy = [0.0];
let mut outz = [0.0];
vector_potential_point_segment(
(&[x], &[y], &[z]),
(&xyz, &xyz, &xyz),
(&dlxyz, &dlxyz, &dlxyz),
&[1.0],
(&mut outx, &mut outy, &mut outz),
)
.unwrap();
(outx[0], outy[0], outz[0])
};
let vals = [
0.25, 0.5, 2.5, 10.0, 100.0, 1000.0, -1000.0, -100.0, -10.0, -2.5, -0.5, -0.25,
];
let eps = 1e-7;
for x in vals.iter() {
for y in vals.iter() {
for z in vals.iter() {
let x = &(x + 1e-2); let y = &(y + 1e-2);
let z = &(z - 1e-2);
let mut da = [[0.0; 3]; 3];
let (ax0, ay0, az0) = vp(*x - eps, *y, *z);
let (ax1, ay1, az1) = vp(*x + eps, *y, *z);
da[0][0] = (ax1 - ax0) / (2.0 * eps);
da[0][1] = (ay1 - ay0) / (2.0 * eps);
da[0][2] = (az1 - az0) / (2.0 * eps);
let (ax0, ay0, az0) = vp(*x, *y - eps, *z);
let (ax1, ay1, az1) = vp(*x, *y + eps, *z);
da[1][0] = (ax1 - ax0) / (2.0 * eps);
da[1][1] = (ay1 - ay0) / (2.0 * eps);
da[1][2] = (az1 - az0) / (2.0 * eps);
let (ax0, ay0, az0) = vp(*x, *y, *z - eps);
let (ax1, ay1, az1) = vp(*x, *y, *z + eps);
da[2][0] = (ax1 - ax0) / (2.0 * eps);
da[2][1] = (ay1 - ay0) / (2.0 * eps);
da[2][2] = (az1 - az0) / (2.0 * eps);
let daz_dy = da[1][2];
let day_dz = da[2][1];
let daz_dx = da[0][2];
let dax_dz = da[2][0];
let day_dx = da[0][1];
let dax_dy = da[1][0];
let ca = [daz_dy - day_dz, dax_dz - daz_dx, day_dx - dax_dy];
let mut bx = [0.0];
let mut by = [0.0];
let mut bz = [0.0];
flux_density_point_segment(
(&[*x], &[*y], &[*z]),
(&xyz, &xyz, &xyz),
(&dlxyz, &dlxyz, &dlxyz),
&[1.0],
(&mut bx, &mut by, &mut bz),
)
.unwrap();
assert!(approx(bx[0], ca[0], 1e-6, 1e-15));
assert!(approx(by[0], ca[1], 1e-6, 1e-15));
assert!(approx(bz[0], ca[2], 1e-6, 1e-15));
}
}
}
}
#[test]
fn test_serial_vs_parallel() {
const NFIL: usize = 10;
const NOBS: usize = 100;
let xfil: Vec<f64> = (0..NFIL).map(|i| (i as f64).sin()).collect();
let yfil: Vec<f64> = (0..NFIL).map(|i| (i as f64).cos()).collect();
let zfil: Vec<f64> = (0..NFIL)
.map(|i| (i as f64) - (NFIL as f64) / 2.0)
.collect();
let xyzfil = (&xfil[..=NFIL - 2], &yfil[..=NFIL - 2], &zfil[..=NFIL - 2]);
let dlxfil: Vec<f64> = (0..=NFIL - 2).map(|i| xfil[i + 1] - xfil[i]).collect();
let dlyfil: Vec<f64> = (0..=NFIL - 2).map(|i| yfil[i + 1] - yfil[i]).collect();
let dlzfil: Vec<f64> = (0..=NFIL - 2).map(|i| zfil[i + 1] - zfil[i]).collect();
let dlxyzfil = (&dlxfil[..], &dlyfil[..], &dlzfil[..]);
let ifil: &[f64] = &(0..NFIL - 1).map(|i| i as f64).collect::<Vec<f64>>()[..];
let xp: Vec<f64> = (0..NOBS).map(|i| 2.0 * (i as f64).sin() + 2.1).collect();
let yp: Vec<f64> = (0..NOBS).map(|i| 4.0 * (2.0 * i as f64).cos()).collect();
let zp: Vec<f64> = (0..NOBS).map(|i| (0.1 * i as f64).exp()).collect();
let xyzp = (&xp[..], &yp[..], &zp[..]);
let out0 = &mut [0.0; NOBS];
let out1 = &mut [1.0; NOBS];
let out2 = &mut [2.0; NOBS];
let out3 = &mut [3.0; NOBS];
let out4 = &mut [4.0; NOBS];
let out5 = &mut [5.0; NOBS];
flux_density_point_segment(xyzp, xyzfil, dlxyzfil, ifil, (out0, out1, out2)).unwrap();
flux_density_point_segment_par(xyzp, xyzfil, dlxyzfil, ifil, (out3, out4, out5)).unwrap();
for i in 0..NOBS {
assert_eq!(out0[i], out3[i]);
assert_eq!(out1[i], out4[i]);
assert_eq!(out2[i], out5[i]);
}
let out0 = &mut [0.0; NOBS];
let out1 = &mut [1.0; NOBS];
let out2 = &mut [2.0; NOBS];
let out3 = &mut [3.0; NOBS];
let out4 = &mut [4.0; NOBS];
let out5 = &mut [5.0; NOBS];
vector_potential_point_segment(xyzp, xyzfil, dlxyzfil, ifil, (out0, out1, out2)).unwrap();
vector_potential_point_segment_par(xyzp, xyzfil, dlxyzfil, ifil, (out3, out4, out5))
.unwrap();
for i in 0..NOBS {
assert_eq!(out0[i], out3[i]);
assert_eq!(out1[i], out4[i]);
assert_eq!(out2[i], out5[i]);
}
}
}