use crate::mesh::triangle3d::TriangleMeshView;
use crate::physics::boundary_element::QuadratureKind;
use std::time::Instant;
use super::kernels::{
BoundaryElementFluxDensityKernel, BoundaryElementNodalValues, BoundaryElementTriangles,
BoundaryElementVectorPotentialKernel, DipoleFluxDensityKernel, DipoleMoments, DipoleSources,
DipoleTargets, DipoleVectorPotentialKernel, LinearFilamentFluxDensityKernel,
LinearFilamentSources, LinearFilamentVectorPotentialKernel,
};
use super::{
BuildMethod, ClusterTree, EvaluationScratch, HierarchicalError, HierarchicalKernel, Scalar,
SourceCollection, SourceMomentCollection, SourceNodeSummaries, TargetCollection, eval,
eval_par, scratch_len, scratch_len_par, update_summaries,
};
pub struct Diagnostics<K: HierarchicalKernel> {
pub source_tree: ClusterTree<K::Scalar>,
pub construction_seconds: f64,
pub evaluation_seconds: f64,
pub source_count: usize,
pub target_count: usize,
}
impl<K: HierarchicalKernel> Diagnostics<K> {
#[inline]
pub fn source_tree(&self) -> &ClusterTree<K::Scalar> {
&self.source_tree
}
}
pub fn flux_density_dipole_hierarchical<T: Scalar>(
loc: (&[T], &[T], &[T]),
moment: (&[T], &[T], &[T]),
obs: (&[T], &[T], &[T]),
outer_radius: &[T],
construction_method: BuildMethod,
theta: T,
par: bool,
out: (&mut [T], &mut [T], &mut [T]),
) -> Result<Diagnostics<DipoleFluxDensityKernel<T>>, HierarchicalError> {
let sources = DipoleSources::new(loc.0, loc.1, loc.2, outer_radius);
let moments = DipoleMoments::new(moment.0, moment.1, moment.2);
let targets = DipoleTargets::new(obs.0, obs.1, obs.2);
one_shot_vec3(
DipoleFluxDensityKernel::<T>::new(),
sources,
moments,
targets,
construction_method,
theta,
par,
out,
)
}
pub fn vector_potential_dipole_hierarchical<T: Scalar>(
loc: (&[T], &[T], &[T]),
moment: (&[T], &[T], &[T]),
obs: (&[T], &[T], &[T]),
outer_radius: &[T],
construction_method: BuildMethod,
theta: T,
par: bool,
out: (&mut [T], &mut [T], &mut [T]),
) -> Result<Diagnostics<DipoleVectorPotentialKernel<T>>, HierarchicalError> {
let sources = DipoleSources::new(loc.0, loc.1, loc.2, outer_radius);
let moments = DipoleMoments::new(moment.0, moment.1, moment.2);
let targets = DipoleTargets::new(obs.0, obs.1, obs.2);
one_shot_vec3(
DipoleVectorPotentialKernel::<T>::new(),
sources,
moments,
targets,
construction_method,
theta,
par,
out,
)
}
pub fn flux_density_linear_filament_hierarchical<T: Scalar>(
xyzp: (&[T], &[T], &[T]),
xyzfil: (&[T], &[T], &[T]),
dlxyzfil: (&[T], &[T], &[T]),
ifil: &[T],
wire_radius: &[T],
construction_method: BuildMethod,
theta: T,
par: bool,
out: (&mut [T], &mut [T], &mut [T]),
) -> Result<Diagnostics<LinearFilamentFluxDensityKernel<T>>, HierarchicalError> {
let sources = LinearFilamentSources::new(xyzfil, dlxyzfil, wire_radius);
let targets = DipoleTargets::new(xyzp.0, xyzp.1, xyzp.2);
one_shot_vec3(
LinearFilamentFluxDensityKernel::<T>::new(),
sources,
ifil,
targets,
construction_method,
theta,
par,
out,
)
}
pub fn vector_potential_linear_filament_hierarchical<T: Scalar>(
xyzp: (&[T], &[T], &[T]),
xyzfil: (&[T], &[T], &[T]),
dlxyzfil: (&[T], &[T], &[T]),
ifil: &[T],
wire_radius: &[T],
construction_method: BuildMethod,
theta: T,
par: bool,
out: (&mut [T], &mut [T], &mut [T]),
) -> Result<Diagnostics<LinearFilamentVectorPotentialKernel<T>>, HierarchicalError> {
let sources = LinearFilamentSources::new(xyzfil, dlxyzfil, wire_radius);
let targets = DipoleTargets::new(xyzp.0, xyzp.1, xyzp.2);
one_shot_vec3(
LinearFilamentVectorPotentialKernel::<T>::new(),
sources,
ifil,
targets,
construction_method,
theta,
par,
out,
)
}
pub fn flux_density_triangle_mesh_hierarchical(
obs: (&[f64], &[f64], &[f64]),
mesh: &TriangleMeshView<'_>,
s: &[f64],
quad_kind: QuadratureKind,
construction_method: BuildMethod,
theta: f64,
par: bool,
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<Diagnostics<BoundaryElementFluxDensityKernel<f64>>, HierarchicalError> {
mesh.validate_nodal_values(s)
.map_err(|_| HierarchicalError::LengthMismatch)?;
let sources = BoundaryElementTriangles::new(mesh);
let moments = BoundaryElementNodalValues::new(sources, s);
let targets = DipoleTargets::new(obs.0, obs.1, obs.2);
one_shot_vec3(
BoundaryElementFluxDensityKernel::<f64>::new(quad_kind),
sources,
moments,
targets,
construction_method,
theta,
par,
out,
)
}
pub fn vector_potential_triangle_mesh_hierarchical(
obs: (&[f64], &[f64], &[f64]),
mesh: &TriangleMeshView<'_>,
s: &[f64],
quad_kind: QuadratureKind,
construction_method: BuildMethod,
theta: f64,
par: bool,
out: (&mut [f64], &mut [f64], &mut [f64]),
) -> Result<Diagnostics<BoundaryElementVectorPotentialKernel<f64>>, HierarchicalError> {
mesh.validate_nodal_values(s)
.map_err(|_| HierarchicalError::LengthMismatch)?;
let sources = BoundaryElementTriangles::new(mesh);
let moments = BoundaryElementNodalValues::new(sources, s);
let targets = DipoleTargets::new(obs.0, obs.1, obs.2);
one_shot_vec3(
BoundaryElementVectorPotentialKernel::<f64>::new(quad_kind),
sources,
moments,
targets,
construction_method,
theta,
par,
out,
)
}
pub(crate) fn one_shot_vec3<K, T, S, M, C>(
kernel: K,
sources: S,
moments: M,
targets: C,
construction_method: BuildMethod,
theta: T,
par: bool,
out: (&mut [T], &mut [T], &mut [T]),
) -> Result<Diagnostics<K>, HierarchicalError>
where
K: HierarchicalKernel<Scalar = T, Output = [T; 3]> + Sync,
T: Scalar,
S: SourceCollection<K>,
M: SourceMomentCollection<K>,
K::TargetGeometry: Copy,
C: TargetCollection<K>,
{
if out.0.len() != targets.len()
|| out.1.len() != targets.len()
|| out.2.len() != targets.len()
|| !targets.valid_lengths()
|| sources.len() != moments.len()
|| !sources.valid_lengths()
|| !moments.valid_lengths()
{
return Err(HierarchicalError::LengthMismatch);
}
let construction_start = Instant::now();
let source_tree = match construction_method {
BuildMethod::LongestAxis => ClusterTree::build(sources)?,
BuildMethod::MortonLbvh => ClusterTree::build_morton_lbvh(sources)?,
};
let mut source_summaries = SourceNodeSummaries::<K>::new(source_tree.as_view());
let mut err = update_summaries(
&kernel,
source_tree.as_view(),
sources,
moments,
&mut source_summaries.node_summaries,
);
if err != HierarchicalError::Ok {
return Err(err);
}
let construction_seconds = construction_start.elapsed().as_secs_f64();
let evaluation_start = Instant::now();
let scratch_len = match par {
true => scratch_len_par(targets.len()),
false => scratch_len(),
};
let mut scratch_values = vec![[T::ZERO; 3]; scratch_len];
let mut scratch = EvaluationScratch {
contribution: &mut scratch_values,
};
let out_components = [out.0, out.1, out.2];
err = match par {
true => eval_par(
&kernel,
source_tree.as_view(),
&source_summaries.node_summaries,
sources,
targets,
moments,
theta,
out_components,
&mut scratch,
),
false => eval(
&kernel,
source_tree.as_view(),
&source_summaries.node_summaries,
sources,
targets,
moments,
theta,
out_components,
&mut scratch,
),
};
if err != HierarchicalError::Ok {
return Err(err);
}
let evaluation_seconds = evaluation_start.elapsed().as_secs_f64();
Ok(Diagnostics {
source_tree,
construction_seconds,
evaluation_seconds,
source_count: sources.len(),
target_count: targets.len(),
})
}