use super::functions::*;
pub struct MortarContactElement {
pub n_quad_pts: usize,
pub penalty: f64,
}
impl MortarContactElement {
pub fn mortar_gap_integral(
&self,
slave_a: [f64; 3],
slave_b: [f64; 3],
master_a: [f64; 3],
master_b: [f64; 3],
surface_normal: [f64; 3],
) -> f64 {
let (xi_pts, weights) = gauss_quad_1d(self.n_quad_pts);
let mut integral = 0.0;
let seg_len = {
let d = [
slave_b[0] - slave_a[0],
slave_b[1] - slave_a[1],
slave_b[2] - slave_a[2],
];
(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]).sqrt()
};
for (&xi, &w) in xi_pts.iter().zip(weights.iter()) {
let t = 0.5 * (xi + 1.0);
let slave_pt = [
slave_a[0] + t * (slave_b[0] - slave_a[0]),
slave_a[1] + t * (slave_b[1] - slave_a[1]),
slave_a[2] + t * (slave_b[2] - slave_a[2]),
];
let proj = project_point_to_segment(slave_pt, master_a, master_b);
let d = [
slave_pt[0] - proj[0],
slave_pt[1] - proj[1],
slave_pt[2] - proj[2],
];
let gap =
d[0] * surface_normal[0] + d[1] * surface_normal[1] + d[2] * surface_normal[2];
integral += w * gap * seg_len * 0.5;
}
integral
}
pub fn constraint_residual(
&self,
slave_a: [f64; 3],
slave_b: [f64; 3],
master_a: [f64; 3],
master_b: [f64; 3],
surface_normal: [f64; 3],
) -> f64 {
let g_int = self.mortar_gap_integral(slave_a, slave_b, master_a, master_b, surface_normal);
self.penalty * (-g_int).max(0.0)
}
}
pub struct ContactSearch {
pub candidate_pairs: Vec<(usize, usize)>,
}
impl ContactSearch {
pub fn brute_force_search(nodes_a: &[[f64; 3]], nodes_b: &[[f64; 3]], cutoff: f64) -> Self {
let mut pairs = Vec::new();
for (i, a) in nodes_a.iter().enumerate() {
for (j, b) in nodes_b.iter().enumerate() {
let dx = a[0] - b[0];
let dy = a[1] - b[1];
let dz = a[2] - b[2];
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
if dist <= cutoff {
pairs.push((i, j));
}
}
}
Self {
candidate_pairs: pairs,
}
}
pub fn pair_count(&self) -> usize {
self.candidate_pairs.len()
}
}
pub struct PenaltyContact {
pub stiffness: f64,
pub friction_coeff: f64,
}
impl PenaltyContact {
pub fn new(stiffness: f64, friction: f64) -> Self {
Self {
stiffness,
friction_coeff: friction,
}
}
pub fn normal_force(&self, gap: f64) -> f64 {
self.stiffness * (-gap).max(0.0)
}
pub fn friction_force(&self, f_n: f64, sliding_vel: f64) -> f64 {
const EPSILON: f64 = 1e-6;
self.friction_coeff * f_n * (sliding_vel / EPSILON).tanh()
}
pub fn contact_residual(&self, gap: f64, sliding_vel: f64) -> (f64, f64) {
let f_n = self.normal_force(gap);
let f_t = self.friction_force(f_n, sliding_vel);
(f_n, f_t)
}
}
pub struct AugmentedLagrangianContact {
pub penalty: f64,
pub lambda_n: f64,
pub lambda_t: f64,
}
impl AugmentedLagrangianContact {
pub fn new(penalty: f64) -> Self {
Self {
penalty,
lambda_n: 0.0,
lambda_t: 0.0,
}
}
pub fn update_multipliers(&mut self, gap: f64, sliding: f64, friction_coeff: f64) {
self.lambda_n += self.penalty * gap.min(0.0);
let trial = self.lambda_t + self.penalty * sliding;
let limit = friction_coeff * self.lambda_n.abs();
self.lambda_t = trial.clamp(-limit, limit);
}
pub fn force(&self, gap: f64) -> f64 {
self.lambda_n + self.penalty * gap.min(0.0)
}
}
#[derive(Debug, Clone)]
pub struct Aabb {
pub min: [f64; 3],
pub max: [f64; 3],
}
impl Aabb {
pub fn from_points(points: &[[f64; 3]]) -> Self {
if points.is_empty() {
return Self {
min: [0.0; 3],
max: [0.0; 3],
};
}
let mut min = points[0];
let mut max = points[0];
for p in points.iter().skip(1) {
for i in 0..3 {
if p[i] < min[i] {
min[i] = p[i];
}
if p[i] > max[i] {
max[i] = p[i];
}
}
}
Self { min, max }
}
pub fn expand(&mut self, margin: f64) {
for i in 0..3 {
self.min[i] -= margin;
self.max[i] += margin;
}
}
pub fn overlaps(&self, other: &Aabb) -> bool {
for i in 0..3 {
if self.max[i] < other.min[i] || self.min[i] > other.max[i] {
return false;
}
}
true
}
pub fn contains_point(&self, point: [f64; 3]) -> bool {
for (i, &pt) in point.iter().enumerate() {
if pt < self.min[i] || pt > self.max[i] {
return false;
}
}
true
}
pub fn volume(&self) -> f64 {
let mut vol = 1.0;
for i in 0..3 {
vol *= (self.max[i] - self.min[i]).max(0.0);
}
vol
}
}
pub struct AcceleratedContactSearch;
impl AcceleratedContactSearch {
pub fn aabb_broad_phase(aabbs_a: &[Aabb], aabbs_b: &[Aabb]) -> Vec<(usize, usize)> {
let mut candidate_pairs = Vec::new();
for (i, a) in aabbs_a.iter().enumerate() {
for (j, b) in aabbs_b.iter().enumerate() {
if a.overlaps(b) {
candidate_pairs.push((i, j));
}
}
}
candidate_pairs
}
pub fn bucket_search(
nodes_a: &[[f64; 3]],
nodes_b: &[[f64; 3]],
bucket_size: f64,
cutoff: f64,
) -> Vec<(usize, usize)> {
use std::collections::HashMap;
let mut buckets: HashMap<(i64, i64, i64), Vec<usize>> = HashMap::new();
for (j, b) in nodes_b.iter().enumerate() {
let ix = (b[0] / bucket_size).floor() as i64;
let iy = (b[1] / bucket_size).floor() as i64;
let iz = (b[2] / bucket_size).floor() as i64;
buckets.entry((ix, iy, iz)).or_default().push(j);
}
let mut pairs = Vec::new();
for (i, a) in nodes_a.iter().enumerate() {
let ix = (a[0] / bucket_size).floor() as i64;
let iy = (a[1] / bucket_size).floor() as i64;
let iz = (a[2] / bucket_size).floor() as i64;
for dix in -1..=1 {
for diy in -1..=1 {
for diz in -1..=1 {
let key = (ix + dix, iy + diy, iz + diz);
if let Some(indices) = buckets.get(&key) {
for &j in indices {
let b = &nodes_b[j];
let dx = a[0] - b[0];
let dy = a[1] - b[1];
let dz = a[2] - b[2];
let dist = (dx * dx + dy * dy + dz * dz).sqrt();
if dist <= cutoff {
pairs.push((i, j));
}
}
}
}
}
}
}
pairs
}
}
pub struct NodeToSegmentElement {
pub penalty_normal: f64,
pub penalty_tangential: f64,
pub friction_coeff: f64,
}
impl NodeToSegmentElement {
pub fn new(penalty_normal: f64, penalty_tangential: f64, friction_coeff: f64) -> Self {
Self {
penalty_normal,
penalty_tangential,
friction_coeff,
}
}
pub fn gap(
slave_pos: [f64; 3],
master_a: [f64; 3],
master_b: [f64; 3],
surface_normal: [f64; 3],
) -> f64 {
let proj = project_point_to_segment(slave_pos, master_a, master_b);
let d = [
slave_pos[0] - proj[0],
slave_pos[1] - proj[1],
slave_pos[2] - proj[2],
];
d[0] * surface_normal[0] + d[1] * surface_normal[1] + d[2] * surface_normal[2]
}
pub fn penalty_force(
&self,
slave_pos: [f64; 3],
master_a: [f64; 3],
master_b: [f64; 3],
surface_normal: [f64; 3],
) -> [f64; 3] {
let g = Self::gap(slave_pos, master_a, master_b, surface_normal);
let f_n = self.penalty_normal * (-g).max(0.0);
[
f_n * surface_normal[0],
f_n * surface_normal[1],
f_n * surface_normal[2],
]
}
pub fn shape_functions(
slave_pos: [f64; 3],
master_a: [f64; 3],
master_b: [f64; 3],
) -> [f64; 2] {
let ab = [
master_b[0] - master_a[0],
master_b[1] - master_a[1],
master_b[2] - master_a[2],
];
let ap = [
slave_pos[0] - master_a[0],
slave_pos[1] - master_a[1],
slave_pos[2] - master_a[2],
];
let ab_sq = ab[0] * ab[0] + ab[1] * ab[1] + ab[2] * ab[2];
if ab_sq < 1e-30 {
return [1.0, 0.0];
}
let dot = ap[0] * ab[0] + ap[1] * ab[1] + ap[2] * ab[2];
let t = (dot / ab_sq).clamp(0.0, 1.0);
[1.0 - t, t]
}
pub fn contact_stiffness_matrix(
&self,
surface_normal: [f64; 3],
in_contact: bool,
) -> [[f64; 3]; 3] {
if !in_contact {
return [[0.0; 3]; 3];
}
let k = self.penalty_normal;
let mut ke = [[0.0f64; 3]; 3];
for i in 0..3 {
for j in 0..3 {
ke[i][j] = k * surface_normal[i] * surface_normal[j];
}
}
ke
}
}
pub struct ContactStressRecovery {
pub element_area: f64,
}
impl ContactStressRecovery {
pub fn normal_pressure(&self, normal_force: f64) -> f64 {
if self.element_area < 1e-30 {
return 0.0;
}
normal_force / self.element_area
}
pub fn shear_stress(&self, tangential_force: f64) -> f64 {
if self.element_area < 1e-30 {
return 0.0;
}
tangential_force / self.element_area
}
pub fn von_mises_contact_stress(&self, normal_force: f64, tangential_force: f64) -> f64 {
let sigma_n = self.normal_pressure(normal_force);
let tau = self.shear_stress(tangential_force);
(sigma_n * sigma_n + 3.0 * tau * tau).sqrt()
}
pub fn max_shear_stress(&self, normal_force: f64, tangential_force: f64) -> f64 {
let sigma_n = self.normal_pressure(normal_force);
let tau = self.shear_stress(tangential_force);
((sigma_n / 2.0).powi(2) + tau * tau).sqrt()
}
pub fn nodal_stress_vector(&self, normal_force: f64, normal_dir: [f64; 3]) -> [f64; 3] {
let p = self.normal_pressure(normal_force);
[p * normal_dir[0], p * normal_dir[1], p * normal_dir[2]]
}
}
pub struct TiedContact {
pub tied_pairs: Vec<(usize, usize)>,
pub penalty: f64,
}
impl TiedContact {
pub fn new(pairs: Vec<(usize, usize)>, penalty: f64) -> Self {
Self {
tied_pairs: pairs,
penalty,
}
}
pub fn tied_force(&self, slave_disp: [f64; 3], master_disp: [f64; 3]) -> ([f64; 3], [f64; 3]) {
let mut f_slave = [0.0; 3];
let mut f_master = [0.0; 3];
for i in 0..3 {
let gap = slave_disp[i] - master_disp[i];
f_slave[i] = -self.penalty * gap;
f_master[i] = self.penalty * gap;
}
(f_slave, f_master)
}
pub fn tied_stiffness(&self) -> [[f64; 6]; 6] {
let k = self.penalty;
let mut ke = [[0.0; 6]; 6];
for i in 0..3 {
ke[i][i] = k;
ke[i][i + 3] = -k;
ke[i + 3][i] = -k;
ke[i + 3][i + 3] = k;
}
ke
}
}
pub struct SlidingContact {
pub normal_stiffness: f64,
pub tangential_stiffness: f64,
pub friction_coeff: f64,
pub accumulated_slip: Vec<[f64; 3]>,
}
impl SlidingContact {
pub fn new(k_n: f64, k_t: f64, mu: f64, n_pairs: usize) -> Self {
Self {
normal_stiffness: k_n,
tangential_stiffness: k_t,
friction_coeff: mu,
accumulated_slip: vec![[0.0; 3]; n_pairs],
}
}
pub fn contact_forces(
&self,
gap: f64,
normal: [f64; 3],
tangential_disp: [f64; 3],
) -> ([f64; 3], [f64; 3], bool) {
let pen = (-gap).max(0.0);
let f_n = self.normal_stiffness * pen;
let f_n_vec = [f_n * normal[0], f_n * normal[1], f_n * normal[2]];
let mut f_t_trial = [0.0; 3];
let mut f_t_mag_sq = 0.0;
for i in 0..3 {
f_t_trial[i] = self.tangential_stiffness * tangential_disp[i];
f_t_mag_sq += f_t_trial[i] * f_t_trial[i];
}
let f_t_mag = f_t_mag_sq.sqrt();
let f_t_limit = self.friction_coeff * f_n;
let (f_t_vec, is_sliding);
if f_t_mag <= f_t_limit || f_t_mag < 1e-30 {
f_t_vec = f_t_trial;
is_sliding = false;
} else {
let scale = f_t_limit / f_t_mag;
f_t_vec = [
f_t_trial[0] * scale,
f_t_trial[1] * scale,
f_t_trial[2] * scale,
];
is_sliding = true;
}
(f_n_vec, f_t_vec, is_sliding)
}
pub fn update_slip(&mut self, pair_idx: usize, slip_increment: [f64; 3]) {
for (i, &inc) in slip_increment.iter().enumerate() {
self.accumulated_slip[pair_idx][i] += inc;
}
}
pub fn reset_slip(&mut self, pair_idx: usize) {
self.accumulated_slip[pair_idx] = [0.0; 3];
}
}