use crate::coupling::{
CouplingDomain, CouplingReport, DomainCoupler, DomainKind, InterfaceForce, InterfaceForceVec,
InterfaceSite, InterfaceState, InterfaceStateVec,
};
pub struct FemSphCoupler {
pub interface_sites: Vec<InterfaceSite>,
pub stiffness: f64,
pub damping: f64,
}
impl FemSphCoupler {
pub fn new(interface_sites: Vec<InterfaceSite>, stiffness: f64, damping: f64) -> Self {
Self {
interface_sites,
stiffness,
damping,
}
}
}
impl DomainCoupler for FemSphCoupler {
fn step(
&mut self,
dt: f64,
state_a: &InterfaceStateVec,
state_b: &InterfaceStateVec,
) -> (InterfaceForceVec, InterfaceForceVec, CouplingReport) {
let n = self
.interface_sites
.len()
.min(state_a.states.len())
.min(state_b.states.len());
let mut forces_a = Vec::with_capacity(n);
let mut forces_b = Vec::with_capacity(n);
let mut momentum_a_to_b = [0.0_f64; 3];
let mut momentum_b_to_a = [0.0_f64; 3];
let mut sq_pos_err_sum = 0.0_f64;
for i in 0..n {
let sa: &InterfaceState = &state_a.states[i];
let sb: &InterfaceState = &state_b.states[i];
let f: [f64; 3] = std::array::from_fn(|j| {
self.stiffness * (sa.velocity[j] - sb.velocity[j]) * dt
+ self.damping * (sa.position[j] - sb.position[j])
});
forces_a.push(InterfaceForce {
id: sa.id,
force: [-f[0], -f[1], -f[2]],
});
forces_b.push(InterfaceForce {
id: sb.id,
force: f,
});
for j in 0..3 {
momentum_a_to_b[j] += f[j] * dt;
momentum_b_to_a[j] -= f[j] * dt;
let dx = sa.position[j] - sb.position[j];
sq_pos_err_sum += dx * dx;
}
}
let rms_position_error = if n > 0 {
(sq_pos_err_sum / (3 * n) as f64).sqrt()
} else {
0.0
};
let report = CouplingReport {
site_count: n,
momentum_a_to_b,
momentum_b_to_a,
rms_position_error,
};
(
InterfaceForceVec { forces: forces_a },
InterfaceForceVec { forces: forces_b },
report,
)
}
}
pub struct ConstantVelocityDomain {
pub vel: [f64; 3],
pub kind: DomainKind,
}
impl ConstantVelocityDomain {
pub fn new(vel: [f64; 3], kind: DomainKind) -> Self {
Self { vel, kind }
}
}
impl CouplingDomain for ConstantVelocityDomain {
fn kind(&self) -> DomainKind {
self.kind.clone()
}
fn sample_interface(&self, sites: &[InterfaceSite]) -> InterfaceStateVec {
let states = sites
.iter()
.map(|s| InterfaceState {
id: s.id,
position: s.position,
velocity: self.vel,
})
.collect();
InterfaceStateVec { states }
}
fn apply_interface_force(&mut self, _forces: &InterfaceForceVec) {
}
}
pub struct ImpulseDomain {
pub velocities: Vec<[f64; 3]>,
pub masses: Vec<f64>,
pub kind: DomainKind,
}
impl ImpulseDomain {
pub fn new(n: usize, mass: f64, kind: DomainKind) -> Self {
Self {
velocities: vec![[0.0; 3]; n],
masses: vec![mass; n],
kind,
}
}
}
impl CouplingDomain for ImpulseDomain {
fn kind(&self) -> DomainKind {
self.kind.clone()
}
fn sample_interface(&self, sites: &[InterfaceSite]) -> InterfaceStateVec {
let states = sites
.iter()
.map(|s| {
let idx = s.id as usize;
let velocity = if idx < self.velocities.len() {
self.velocities[idx]
} else {
[0.0; 3]
};
InterfaceState {
id: s.id,
position: s.position,
velocity,
}
})
.collect();
InterfaceStateVec { states }
}
fn apply_interface_force(&mut self, forces: &InterfaceForceVec) {
for iforce in &forces.forces {
let idx = iforce.id as usize;
if idx < self.velocities.len() {
let mass = self.masses[idx];
if mass > f64::EPSILON {
for j in 0..3 {
self.velocities[idx][j] += iforce.force[j] / mass;
}
}
}
}
}
}