deep_causality_physics/dynamics/
kinematics.rs

1/*
2 * SPDX-License-Identifier: MIT
3 * Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4 */
5
6use crate::{Frequency, Mass, MomentOfInertia, PhysicsError};
7use deep_causality_multivector::{CausalMultiVector, MultiVector};
8
9#[derive(Debug, Clone, PartialEq)]
10pub struct PhysicalVector(pub CausalMultiVector<f64>);
11
12impl Default for PhysicalVector {
13    fn default() -> Self {
14        // Return a scalar 0 multivector with Euclidean metric
15        Self(
16            CausalMultiVector::new(vec![0.0], deep_causality_multivector::Metric::Euclidean(0))
17                .unwrap(),
18        )
19    }
20}
21
22impl PhysicalVector {
23    pub fn new(val: CausalMultiVector<f64>) -> Self {
24        Self(val)
25    }
26    pub fn inner(&self) -> &CausalMultiVector<f64> {
27        &self.0
28    }
29    pub fn into_inner(self) -> CausalMultiVector<f64> {
30        self.0
31    }
32}
33
34/// Calculates translational kinetic energy: $K = \frac{1}{2} m v^2$.
35///
36/// # Arguments
37/// * `mass` - Mass of the object.
38/// * `velocity` - Velocity vector (multivector).
39///
40/// # Returns
41/// * `Ok(f64)` - Kinetic energy in Joules.
42/// * `Err(PhysicsError)` - If an error occurs (unlikely with valid inputs).
43pub fn kinetic_energy_kernel(
44    mass: Mass,
45    velocity: &CausalMultiVector<f64>,
46) -> Result<f64, PhysicsError> {
47    // Ensure physically meaningful squared speed
48    let v_sq = velocity.squared_magnitude();
49    if !v_sq.is_finite() {
50        return Err(PhysicsError::NumericalInstability(
51            "Velocity squared magnitude is not finite".into(),
52        ));
53    }
54    if v_sq < -1e-12 {
55        return Err(PhysicsError::PhysicalInvariantBroken(
56            "Negative squared speed in kinetic energy calculation".into(),
57        ));
58    }
59    let v_sq_clamped = if v_sq < 0.0 { 0.0 } else { v_sq };
60    let e = 0.5 * mass.value() * v_sq_clamped;
61    Ok(e)
62}
63
64/// Calculates rotational kinetic energy: $K_{rot} = \frac{1}{2} I \omega^2$.
65///
66/// # Arguments
67/// * `inertia` - Moment of inertia.
68/// * `omega` - Angular frequency (magnitude of angular velocity).
69///
70/// # Returns
71/// * `Ok(f64)` - Rotational kinetic energy in Joules.
72pub fn rotational_kinetic_energy_kernel(
73    inertia: MomentOfInertia,
74    omega: Frequency,
75) -> Result<f64, PhysicsError> {
76    // KE_rot = 0.5 * I * w^2
77    let w = omega.value();
78    let e = 0.5 * inertia.value() * w * w;
79    Ok(e)
80}
81
82/// Calculates torque as the outer product of radius and force: $\tau = r \wedge F$.
83///
84/// In Geometric Algebra, torque is a bivector representing the plane of rotation.
85///
86/// # Arguments
87/// * `radius` - Position vector from pivot.
88/// * `force` - Force vector applied.
89///
90/// # Returns
91/// * `Result<PhysicalVector, PhysicsError>` - The torque bivector wrapped in a physical vector.
92pub fn torque_kernel(
93    radius: &CausalMultiVector<f64>,
94    force: &CausalMultiVector<f64>,
95) -> Result<PhysicalVector, PhysicsError> {
96    // Torque = r x F (Outer product in GA gives Bivector torque)
97    // T = r ^ F
98    if radius.metric() != force.metric() {
99        return Err(PhysicsError::DimensionMismatch(format!(
100            "Metric mismatch: {:?} != {:?}",
101            radius.metric(),
102            force.metric()
103        )));
104    }
105    let t = radius.outer_product(force);
106    Ok(PhysicalVector(t))
107}
108
109/// Calculates angular momentum as the outer product of radius and linear momentum: $L = r \wedge p$.
110///
111/// # Arguments
112/// * `radius` - Position vector.
113/// * `momentum` - Linear momentum vector ($p = mv$).
114///
115/// # Returns
116/// * `Result<PhysicalVector, PhysicsError>` - The angular momentum bivector.
117pub fn angular_momentum_kernel(
118    radius: &CausalMultiVector<f64>,
119    momentum: &CausalMultiVector<f64>,
120) -> Result<PhysicalVector, PhysicsError> {
121    // L = r x p = r ^ p
122    if radius.metric() != momentum.metric() {
123        return Err(PhysicsError::DimensionMismatch(format!(
124            "Metric mismatch: {:?} != {:?}",
125            radius.metric(),
126            momentum.metric()
127        )));
128    }
129    let l = radius.outer_product(momentum);
130    Ok(PhysicalVector(l))
131}