use crate::math::rss3;
use crate::mesh::TriangleMeshView;
pub use crate::mesh::elements::tri::mapping::{
area as calc_tri_area, map_point as map_tri_uv, normal as calc_tri_normal,
};
pub use crate::mesh::elements::tri::quadrature::{QuadratureKind, triangle_quadrature_count};
pub(crate) use crate::mesh::elements::tri::quadrature::{
TRIANGLE_MAX_QUADRATURE_POINTS, triangle_quadrature_points,
};
mod body_force_density;
mod flux_density;
mod inductance;
mod vector_potential;
#[cfg(test)]
mod test;
pub(crate) const TRIANGLE_NEAR_SUBDIVISION_DISTANCE_FACTOR: f64 = 1.0;
pub use flux_density::{
flux_density_triangle, flux_density_triangle_mesh, flux_density_triangle_mesh_mapping,
flux_density_triangle_mesh_mapping_par, flux_density_triangle_mesh_par,
triangle_flux_density_basis, triangle_mesh_flux_density_from_potential_vectors,
};
pub use body_force_density::{
triangle_basis_force_block, triangle_force_from_potential_vectors,
triangle_mesh_force_from_potential_vectors, triangle_mesh_force_mapping,
triangle_mesh_force_mapping_from_circular_filaments,
triangle_mesh_force_mapping_from_circular_filaments_par,
triangle_mesh_force_mapping_from_dipoles, triangle_mesh_force_mapping_from_dipoles_par,
triangle_mesh_force_mapping_from_linear_filaments,
triangle_mesh_force_mapping_from_linear_filaments_par, triangle_mesh_force_mapping_par,
triangle_mesh_self_force_mapping, triangle_mesh_self_force_mapping_par,
triangle_mesh_triangle_forces_from_potential_vectors,
};
pub use inductance::{
triangle_basis_mutual_inductance, triangle_basis_mutual_inductance_block,
triangle_geometric_coupling, triangle_geometric_coupling_regular,
triangle_inductance_from_potential_vectors,
triangle_mesh_flux_linkage_from_source_coefficients,
triangle_mesh_flux_linkage_mapping_from_dipoles,
triangle_mesh_flux_linkage_mapping_from_dipoles_par,
triangle_mesh_inductance_from_potential_vectors,
triangle_mesh_inductance_mapping_from_circular_filaments,
triangle_mesh_inductance_mapping_from_circular_filaments_par,
triangle_mesh_inductance_mapping_from_linear_filaments,
triangle_mesh_inductance_mapping_from_linear_filaments_par, triangle_mesh_inductance_matrix,
triangle_mesh_inductance_matrix_par, triangle_mesh_interaction_energy_from_source_coefficients,
};
pub use vector_potential::{
triangle_mesh_vector_potential_from_potential_vectors, triangle_vector_potential_basis,
vector_potential_triangle, vector_potential_triangle_mesh,
vector_potential_triangle_mesh_mapping, vector_potential_triangle_mesh_mapping_par,
vector_potential_triangle_mesh_par,
};
const TRIANGLE_SELF_DUFFY_SAMPLES: usize = 16;
#[inline]
fn triangle_basis_current_density(n0: [f64; 3], n1: [f64; 3], n2: [f64; 3]) -> (f64, [f64; 3]) {
let v01 = [n1[0] - n0[0], n1[1] - n0[1], n1[2] - n0[2]]; let v02 = [n2[0] - n0[0], n2[1] - n0[1], n2[2] - n0[2]]; let tri_area = calc_tri_area(n0, n1, n2); let jref = [
(v02[0] - v01[0]) / (2.0 * tri_area), (v02[1] - v01[1]) / (2.0 * tri_area), (v02[2] - v01[2]) / (2.0 * tri_area), ];
(tri_area, jref)
}
#[inline]
fn triangle_basis_current_densities(n0: [f64; 3], n1: [f64; 3], n2: [f64; 3]) -> [[f64; 3]; 3] {
[
triangle_basis_current_density(n0, n1, n2).1, triangle_basis_current_density(n1, n2, n0).1, triangle_basis_current_density(n2, n0, n1).1, ]
}
#[inline]
pub fn triangle_current_density(n0: [f64; 3], n1: [f64; 3], n2: [f64; 3], s: [f64; 3]) -> [f64; 3] {
let basis = triangle_basis_current_densities(n0, n1, n2);
[
s[0] * basis[0][0] + s[1] * basis[1][0] + s[2] * basis[2][0], s[0] * basis[0][1] + s[1] * basis[1][1] + s[2] * basis[2][1], s[0] * basis[0][2] + s[1] * basis[1][2] + s[2] * basis[2][2], ]
}
#[inline]
pub fn triangle_mesh_current_density(
mesh: &TriangleMeshView<'_>,
s: &[f64],
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<(), &'static str> {
mesh.validate_nodal_values(s)?;
let ntri = mesh.len();
if out.0.len() != ntri || out.1.len() != ntri || out.2.len() != ntri {
return Err("Output dimension mismatch");
}
for i in 0..ntri {
let tri_nodes = mesh.triangle_nodes(i);
let tri_s = mesh.triangle_scalars(i, s);
let j = triangle_current_density(tri_nodes[0], tri_nodes[1], tri_nodes[2], tri_s); out.0[i] = j[0]; out.1[i] = j[1]; out.2[i] = j[2]; }
Ok(())
}
#[inline]
pub fn triangle_mesh_quadrature_points(
mesh: &TriangleMeshView<'_>,
quad_kind: QuadratureKind,
out: (&mut [f64], &mut [f64], &mut [f64]),
weights: &mut [f64],
) -> Result<(), &'static str> {
let quad_points = triangle_quadrature_points(quad_kind);
let nqp = quad_points.len();
let nout = mesh.len() * nqp;
if out.0.len() != nout || out.1.len() != nout || out.2.len() != nout || weights.len() != nout {
return Err("Output dimension mismatch");
}
for i in 0..mesh.len() {
let [n0, n1, n2] = mesh.triangle_nodes(i);
let tri_area = calc_tri_area(n0, n1, n2);
for (k, qp) in quad_points.iter().enumerate() {
let idx = i * nqp + k;
let point = map_tri_uv(n0, n1, n2, [qp[1], qp[2]]); out.0[idx] = point[0]; out.1[idx] = point[1]; out.2[idx] = point[2]; weights[idx] = qp[0] * tri_area; }
}
Ok(())
}
#[inline]
fn points_match(a: [f64; 3], b: [f64; 3]) -> bool {
rss3(a[0] - b[0], a[1] - b[1], a[2] - b[2]) < 1e-12
}
#[inline]
fn triangles_identical(
src0: [f64; 3],
src1: [f64; 3],
src2: [f64; 3],
tgt0: [f64; 3],
tgt1: [f64; 3],
tgt2: [f64; 3],
) -> bool {
let src = [src0, src1, src2];
let tgt = [tgt0, tgt1, tgt2];
src.iter().all(|&s| tgt.iter().any(|&t| points_match(s, t)))
}