Skip to main content
Glama
Physics MCP Server

Physics MCP Server

by IBM

Physics MCP Server

License: Apache 2.0 Python 3.11+ Test Coverage Tests

Features: 🌀 Magnus Force • 💨 Wind Effects • 🏔️ Altitude • 🌡️ Temperature • 🔄 Tumbling Drag • 🎮 Rigid-Body Sims • 📊 515 Tests

A Model Context Protocol (MCP) server that brings comprehensive physics simulation and calculation capabilities to Large Language Models.

Transform your LLM into a physics engine! This MCP server provides 55 specialized tools spanning classical mechanics, fluid dynamics, rotational motion, and rigid-body simulations. Built for seamless integration with Claude, ChatGPT, and any MCP-compatible AI system.

📚 Table of Contents

Related MCP server: ucon-mcp

🌟 What is This?

Physics MCP Server enables LLMs to perform sophisticated physics calculations and simulations through a standardized protocol. Instead of hallucinating physics formulas or making calculation errors, LLMs can now:

  • Calculate precisely: Use validated physics formulas for exact results

  • Simulate realistically: Run rigid-body physics with the Rapier engine

  • Visualize motion: Generate trajectory data for React Three Fiber, Remotion, and other 3D frameworks

  • Teach interactively: Answer physics questions with real calculations, not memorized facts

  • Design intelligently: Analyze forces, collisions, and motion for engineering applications

Why MCP?

The Model Context Protocol provides a standardized way to extend LLM capabilities beyond text generation. This server implements MCP to give language models direct access to:

  • ⚡ Instant physics calculations (projectile motion, forces, energy)

  • 🎮 Rigid-body simulations (collisions, bounces, stacking)

  • 📊 Motion analysis (trajectory fitting, kinematics, velocity profiles)

  • 🌊 Fluid dynamics (drag, buoyancy, lift, Bernoulli)

  • 🔄 Rotational mechanics (torque, angular momentum, gyroscopes)

  • ⚖️ Static analysis (equilibrium, beam reactions, friction)

  • 🔄 Unit conversions (62 unit types: velocity, distance, mass, time, acceleration, torque, frequency, data size, and more)

📦 What's Included

55 Physics Tools Across 10 Categories

Category

Tools

Description

Basic Mechanics

8 tools

Projectile motion, forces, energy, momentum, collisions

Fluid Dynamics

10 tools

Drag, buoyancy, terminal velocity, lift, Magnus force, Bernoulli

Rotational Dynamics

5 tools

Torque, moment of inertia, angular momentum, rotational KE

Oscillations

5 tools

Springs, pendulums, harmonic motion, damping

Circular Motion

5 tools

Centripetal force, orbits, banking angles, escape velocity

Statics

7 tools

Force balance, torque balance, friction, beam reactions

Kinematics

7 tools

Motion analysis, trajectory fitting, velocity calculations, projectile with drag

Collisions

2 tools

Elastic and inelastic 3D collisions with energy loss

Conservation Laws

4 tools

Energy, momentum, and angular momentum verification

Unit Conversions

2 tools

62 unit types across 16 categories (velocity, distance, mass, time, acceleration, torque, frequency, data, etc.)

Realistic vs Ideal Physics Comparison

See the dramatic difference when including real-world effects:

Sport

Scenario

Ideal (No Drag)

Realistic (With Enhancements)

Difference

⚾ Baseball

90 mph fastball

87.5m

52.3m (with drag)

-40% range

⛳ Golf

Pro drive at sea level

251m

129m (with drag)

-49% range

⛳ Golf

Same drive in Denver

251m

181m (drag + altitude)

-28% range

⚽ Soccer

Free kick with wind

25m straight

26m + 5.6m curve (wind + spin)

Bends 5.6m!

🎾 Tennis

Serve on hot day

27m

26.8m (less drag)

+2.3% vs cold

💡 Key Insight: Real physics matters! Air resistance can reduce range by 20-70% depending on the sport.

Two Calculation Modes

  1. Analytic Mode (Built-in, no setup)

    • Instant mathematical calculations

    • Perfect for education and quick answers

    • Exact solutions using physics formulas

    • Now includes: Advanced drag, spin (Magnus), wind, altitude effects

  2. Simulation Mode (Requires Rapier service)

    • Full rigid-body dynamics

    • Complex multi-object interactions

    • Realistic material properties and constraints

🚀 Quick Start (30 seconds)

# Try it instantly with uvx (no installation needed)
uvx chuk-mcp-physics

# Or with the public Rapier service for simulations
RAPIER_SERVICE_URL=https://rapier.chukai.io uvx chuk-mcp-physics

# Or use the public hosted MCP server (no local installation)
# Add to Claude Desktop config with URL: https://physics.chukai.io/mcp

For Claude Desktop: Add to your config file:

Option 1: Public Hosted MCP Server (Easiest - No Installation)

{
  "mcpServers": {
    "physics": {
      "command": "node",
      "args": ["-e", "require('https').get('physics.chukai.io/mcp')"]
    }
  }
}

Option 2: Local uvx (Recommended)

{
  "mcpServers": {
    "physics": {
      "command": "uvx",
      "args": ["chuk-mcp-physics"],
      "env": {
        "RAPIER_SERVICE_URL": "https://rapier.chukai.io"
      }
    }
  }
}

🎯 Use Cases

1. Interactive Physics Education

LLM as Physics Tutor

User: "If I throw a ball at 20 m/s at 45°, how far will it go?"
LLM: [calls calculate_projectile_motion]
     "The ball will travel 40.8 meters and reach a maximum height of 10.2 meters..."
     [generates visualization with trajectory points]

Real-World Problem Solving

  • Students ask physics questions in natural language

  • LLM calculates exact answers using the MCP tools

  • Can generate trajectory plots, force diagrams, energy graphs

  • Interactive "what-if" scenarios: "What if gravity was half?"


2. Game Development & Prototyping

Ballistics Design

User: "I'm designing a cannon in my game. Initial velocity 50 m/s, 30° angle.
       Will it clear a 15m wall at 80m distance?"
LLM: [calls calculate_projectile_motion]
     "At 80m, the projectile is at 18.6m height - it WILL clear the wall.
      It lands at 110.9m range."

Collision Detection

User: "Two spaceships: Ship A at (0,0,0) moving at (10,0,0) m/s, Ship B at (100,5,0)
       moving at (-8,0,0) m/s. Will they collide?"
LLM: [calls check_collision]
     "Yes, collision in 5.3 seconds at position (53.0, 2.65, 0.0) with impact speed 18 m/s"

Rapid Iteration

  • Test different launch angles, speeds, masses without coding

  • Verify collision logic before implementation

  • Generate realistic physics data for procedural content


3. 3D Visualization & Animation (React Three Fiber)

Automated Animation Data Generation

User: "Create a realistic basketball shot animation - 7m throw into 3m high basket"
LLM: [calls calculate_projectile_motion with solved angle]
     [returns trajectory_points array]
     "Here's the trajectory data for R3F. The ball needs 52° launch angle..."

     <mesh position={interpolate(trajectory)}>
       <sphereGeometry args={[0.12]} />
     </mesh>

Rigid-Body Simulations

User: "Simulate 10 boxes falling and stacking in a pile"
LLM: [calls create_simulation]
     [calls add_rigid_body for ground + 10 boxes with random positions]
     [calls record_trajectory for each box]
     "Here are 10 trajectory arrays for your R3F scene, ready to use..."

Use Cases:

  • Product visualizations (dropping phones, bouncing balls)

  • Architectural collapse simulations (building demolition previews)

  • Sports analytics visualizations (ball trajectories, collision analysis)

  • Sci-fi effects (asteroid fields, debris clouds)


4. Engineering & Design Analysis

Vehicle Crash Prediction

User: "Two cars: Car A (1500kg) at 30 m/s, Car B (1200kg) at 25 m/s approaching.
       Distance 100m. When do they collide and what's the impact energy?"
LLM: [calls check_collision for timing]
     [calls calculate_kinetic_energy for both cars]
     [calls calculate_momentum for momentum analysis]
     "Collision in 1.82 seconds. Total kinetic energy: 1,050,000 J..."

Safety Analysis

  • Calculate impact forces for crash test scenarios

  • Predict collision times for autonomous vehicle planning

  • Analyze momentum transfer in industrial equipment

Structural Testing (with Rapier simulations)

  • Simulate falling objects hitting structures

  • Test load-bearing capacity under dynamic loads

  • Model chain reactions (domino effects)


5. Sports & Athletics

Realistic Ball Trajectories with Air Resistance

User: "How far does a 90 mph baseball fastball actually travel with air resistance?"
LLM: [calls calculate_projectile_with_drag with baseball parameters]
     "With drag (realistic): 52.3m range
      Without drag (vacuum): 87.5m range
      Air resistance reduces range by 40%! Energy lost to drag: 89.2 J..."

Golf Drive Analysis

User: "Pro golfer hits 70 m/s (155 mph) at 12° angle. How far with real air resistance?"
LLM: [calls calculate_projectile_with_drag with golf ball parameters]
     "With drag: 129.4m (142 yards)
      Without drag: 251.0m (274 yards)
      Dimples reduce drag coefficient from 0.47 to 0.25 - saves ~50% range loss!"

Basketball 3-Pointer

User: "What launch angle for a 7.5 m/s shot from 6.75m away (3-point line)?"
LLM: [calls calculate_projectile_with_drag iterating angles]
     "With air resistance, optimal angle is 48° (high arc).
      Range: 6.73m (close!), max height: 3.2m, flight time: 1.1s..."

Shot Analysis Applications

  • Baseball: Pitch trajectories, drag reduces 90mph fastball range by 40%

  • Golf: Drive distance with dimpled ball (Cd=0.25 vs smooth ball Cd=0.47)

  • Basketball: Arc optimization for free throws and 3-pointers

  • Soccer: Penalty kick trajectories, minimal drag at short distances

  • Track & field: Javelin, shot put with realistic air resistance

  • Tennis: Serve and groundstroke trajectory analysis


6. Astrophysics & Space Exploration

Orbital Mechanics (Simplified)

User: "Two asteroids on collision course. A: 500m radius at (0,0,0) moving 15 km/s.
       B: 300m radius at (100km, 2km, 0) moving -12 km/s. Impact prediction?"
LLM: [calls check_collision with appropriate units]
     "Collision in 3.47 seconds at closest approach distance 650m.
      They will NOT collide - miss distance is 150m..."

Applications:

  • Asteroid impact prediction

  • Satellite collision avoidance

  • Debris field analysis

  • Launch trajectory planning (simplified cases)


7. Fluid Dynamics & Marine/Aerospace Engineering

Underwater Torpedo Simulation

User: "A torpedo is launched underwater at 20 m/s. It weighs 100kg, has a
       streamlined shape (Cd=0.04), and cross-section of 0.03 m².
       How far does it travel in 30 seconds?"
LLM: [calls simulate_underwater_motion]
     "The torpedo travels 147.5 meters before drag and buoyancy slow it down.
      Final velocity: 0.2 m/s. Maximum depth: 729.9 m..."

Terminal Velocity & Drag Analysis

User: "What's the terminal velocity of a skydiver (70kg, 0.7m² area)?"
LLM: [calls calculate_terminal_velocity]
     "Terminal velocity is 40 m/s (90 mph) in belly-down position.
      Takes 12.2 seconds to reach 95% of terminal velocity..."

Buoyancy & Float/Sink Predictions

User: "Will a 1kg steel ball (10cm diameter) float in water?"
LLM: [calls calculate_buoyancy]
     "No, it will sink. Buoyant force is 5.14 N, but weight is 9.81 N.
      The ball is denser than water..."

Applications:

  • Marine engineering: Submarine drag, torpedo trajectories, underwater vehicles

  • Aerospace: Parachute descent, atmospheric re-entry, drag optimization

  • Sports science: Swimming efficiency, diving trajectories

  • Product design: Floatation devices, drag reduction, hydrodynamics

  • Environmental: Particle settling rates, pollutant dispersion


8. Film & VFX Pre-visualization

Stunt Planning

User: "Car jumps off 3m ramp at 25 m/s, 20° angle. How far does it fly and where does it land?"
LLM: [calls calculate_projectile_motion]
     "Airtime: 1.75 seconds, landing at 42.9m horizontal distance,
      impact speed 26.3 m/s. Recommend crashmat at 40-45m mark..."

Destruction Sequences

  • Building collapses with rigid-body sim

  • Explosion debris trajectories

  • Vehicle stunts and crashes

  • Realistic object interactions


8. Military & Defense (Training/Education)

Ballistics Training

User: "Artillery shell: muzzle velocity 800 m/s, 45° elevation. Range and time of flight?"
LLM: [calls calculate_projectile_motion]
     "Range: 65.3 km, flight time: 115.5 seconds, max altitude: 16.3 km"

Collision Avoidance

  • Projectile trajectory analysis

  • Impact point prediction

  • Intercept course calculations


9. Robotics & Automation

Path Planning

User: "Robot arm needs to toss part into bin 2m away, 0.5m higher.
       What velocity is needed?"
LLM: [reverse-calculates using projectile_motion multiple times]
     "Minimum velocity: 4.7 m/s at 38° angle. Recommend 5.0 m/s for safety margin..."

Collision Detection

  • Multi-robot coordination

  • Object catching/throwing

  • Assembly line optimization


10. Data Science & Research

Physics Simulations for ML Training Data

# Generate thousands of collision scenarios for ML model training
for i in range(10000):
    result = await check_collision(random_params())
    training_data.append({
        'features': params,
        'label': result.will_collide,
        'impact_time': result.collision_time
    })

Use Cases:

  • Generate labeled physics data for ML models

  • Validate physics-informed neural networks

  • Test scientific hypotheses with rapid iteration

  • Monte Carlo simulations (vary parameters, aggregate results)


🚀 Real-World Example Workflows

Workflow 1: Basketball Shot Optimizer

1. User: "I'm 2m tall shooting from free-throw line (4.6m). Basket is 3.05m high.
          What's the minimum velocity needed?"

2. LLM calls calculate_projectile_motion with varying velocities
   - Try v=5 m/s → doesn't reach
   - Try v=7 m/s → reaches
   - Binary search finds minimum: v=6.2 m/s at 52° angle

3. LLM: "Minimum velocity is 6.2 m/s at 52° launch angle.
         For comfortable margin, use 7.0 m/s (typical free throw speed).
         Here's the trajectory visualization..."

Workflow 2: Car Crash Investigation

1. User: "Analyze accident: Car A (1500kg) skid marks 30m, Car B (1200kg) skid marks 25m.
          Coefficient of friction 0.7. What were impact speeds?"

2. LLM:
   - Calculates deceleration from friction: a = μg = 0.7 × 9.81 = 6.87 m/s²
   - Uses v² = 2ad to find velocities
   - Calls calculate_kinetic_energy for both cars
   - Calls calculate_momentum for momentum analysis

3. LLM: "Car A impact speed: ~20.3 m/s (45 mph), Car B: ~18.5 m/s (41 mph).
         Total kinetic energy at impact: 513,000 J. Here's the force analysis..."

Workflow 3: Game Level Design with Physics Simulation

1. User: "Create a Rube Goldberg machine: ball rolls down ramp, hits dominos,
          dominos knock ball into basket"

2. LLM:
   - Calls create_simulation(gravity_y=-9.81)
   - Adds ground plane (static)
   - Adds ramp (static, angled)
   - Adds ball (dynamic, sphere, position at ramp top)
   - Adds 10 dominos (dynamic boxes in a line)
   - Adds basket (static)
   - Calls step_simulation(steps=1000)
   - Analyzes contacts to verify chain reaction
   - Calls record_trajectory for each piece

3. LLM: "Simulation complete! Ball triggers all dominos successfully.
         Here are the trajectories for R3F visualization.
         Domino #3 falls at t=1.2s, domino #7 at t=2.1s..."

Workflow 4: Satellite Collision Warning

1. User: "Satellite A: position (6700km, 0, 0), velocity (0, 7.5km/s, 0)
          Satellite B: position (6650km, 50km, 0), velocity (0, 7.6km/s, 0.1km/s)
          Collision risk?"

2. LLM:
   - Calls check_collision with satellite data
   - Analyzes closest approach

3. LLM: "No collision. Closest approach: 48.3 km at t=412 seconds.
         Satellites are in similar orbits but safe separation.
         Recommend: monitor as orbits may precess over time."

🎨 Visualization Integration

React Three Fiber (R3F) Example

function PhysicsAnimation() {
  const [trajectory, setTrajectory] = useState([]);

  useEffect(() => {
    // LLM generated this trajectory data via MCP
    fetch('/api/mcp/record_trajectory', {
      body: JSON.stringify({
        sim_id: "sim_xyz",
        body_id: "ball",
        steps: 300
      })
    }).then(res => setTrajectory(res.frames));
  }, []);

  return (
    <Canvas>
      <AnimatedBall trajectory={trajectory} />
      <Ground />
    </Canvas>
  );
}

function AnimatedBall({ trajectory }) {
  const ref = useRef();

  useFrame((state) => {
    const t = state.clock.getElapsedTime();
    const frame = trajectory[Math.floor(t / 0.016) % trajectory.length];
    if (frame && ref.current) {
      ref.current.position.fromArray(frame.position);
      ref.current.quaternion.fromArray(frame.orientation);
    }
  });

  return (
    <mesh ref={ref}>
      <sphereGeometry args={[0.5]} />
      <meshStandardMaterial color="orange" />
    </mesh>
  );
}

📊 Trajectory Data Format

All trajectory recordings follow a canonical schema for maximum interoperability with R3F, Remotion, Three.js, and other animation systems.

Schema Definition

interface Trajectory {
  dt: number;              // Time step between frames (seconds)
  frames: Frame[];         // Ordered list of frames
  meta: {
    body_id: string;       // Fully qualified: "rapier://sim-123/body-1"
    total_time: number;    // Total duration (seconds)
    num_frames: number;    // Frame count
  };
}

interface Frame {
  t: number;                               // Absolute time (seconds)
  position: [number, number, number];      // [x, y, z] in meters
  rotation: [number, number, number, number];  // Quaternion [x, y, z, w]
  velocity?: [number, number, number];     // Optional: linear velocity (m/s)
  angular_velocity?: [number, number, number];  // Optional: angular velocity (rad/s)
}

JSON Example

{
  "dt": 0.016,
  "frames": [
    {
      "t": 0.0,
      "position": [0, 1, 0],
      "rotation": [0, 0, 0, 1],
      "velocity": [0, 0, 0]
    },
    {
      "t": 0.016,
      "position": [0.1, 1.01, 0],
      "rotation": [0, 0.01, 0, 0.9999],
      "velocity": [6.25, 0.61, 0]
    }
  ],
  "meta": {
    "body_id": "rapier://sim-abc123/ball",
    "total_time": 4.8,
    "num_frames": 300
  }
}

Usage in React Three Fiber

import { useRef } from "react";
import { useFrame } from "@react-three/fiber";

function AnimatedObject({ trajectory }) {
  const ref = useRef();

  useFrame((state) => {
    const elapsed = state.clock.getElapsedTime();
    const frameIdx = Math.floor(elapsed / trajectory.dt);
    const frame = trajectory.frames[frameIdx % trajectory.frames.length];

    if (frame && ref.current) {
      ref.current.position.fromArray(frame.position);
      ref.current.quaternion.fromArray(frame.rotation);
    }
  });

  return (
    <mesh ref={ref}>
      <sphereGeometry args={[0.5]} />
      <meshStandardMaterial color="orange" />
    </mesh>
  );
}

Usage in Remotion

import { useCurrentFrame } from "remotion";
import { ThreeCanvas } from "@remotion/three";

export const PhysicsAnimation = ({ trajectory }) => {
  const frame = useCurrentFrame();
  const frameData = trajectory.frames[frame];

  return (
    <AbsoluteFill>
      <ThreeCanvas>
        <mesh position={frameData.position}>
          <sphereGeometry />
        </mesh>
      </ThreeCanvas>
    </AbsoluteFill>
  );
};

Design Notes

  • Quaternions for rotation: More compact and interpolation-friendly than Euler angles

  • Absolute time: Each frame has absolute time t, making scrubbing easier

  • Constant dt: Frames are evenly spaced, simplifying playback

  • Optional velocities: Include if needed for motion blur or physics visualization

  • Qualified body_id: Format is rapier://sim-{id}/{body_id} for traceability


🛠️ Installation

Prerequisites

The fastest way to try chuk-mcp-physics without installation:

# Run directly with uvx (no installation needed)
uvx chuk-mcp-physics

# With environment variables
uvx --with chuk-mcp-physics chuk-mcp-physics

Installation Methods

# Install globally
pip install chuk-mcp-physics

# Or with pipx (isolated environment)
pipx install chuk-mcp-physics

# Run the server
chuk-mcp-physics

# Or via python module
python -m chuk_mcp_physics.server

Option 2: Install from Source

# Clone repository (from your source location)
cd chuk-mcp-physics

# Install in development mode
make dev-install

# Run the server
chuk-mcp-physics

With Claude Desktop

Add to your Claude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "physics": {
      "command": "uvx",
      "args": ["chuk-mcp-physics"],
      "env": {
        "PHYSICS_PROVIDER": "rapier",
        "RAPIER_SERVICE_URL": "https://rapier.chukai.io"
      }
    }
  }
}

Option 2: Using Installed Package

{
  "mcpServers": {
    "physics": {
      "command": "python",
      "args": ["-m", "chuk_mcp_physics.server"],
      "env": {
        "PHYSICS_PROVIDER": "rapier",
        "RAPIER_SERVICE_URL": "https://rapier.chukai.io"
      }
    }
  }
}

Option 3: Analytic Only (No External Service)

{
  "mcpServers": {
    "physics": {
      "command": "uvx",
      "args": ["chuk-mcp-physics"],
      "env": {
        "PHYSICS_PROVIDER": "analytic"
      }
    }
  }
}

📖 Available Tools

Tool Organization

Tools are organized into two tiers to help you choose the right abstraction:

1️⃣ Analytic Primitives (No External Service)

Best for: Quick calculations, education, simple scenarios

Direct formula-based calculations that return instant results:

Tool

What It Does

Example Use Case

calculate_projectile_motion

Ballistic trajectory using kinematic equations

"How far does a cannonball go?"

check_collision

Predict if two spheres will collide

"Will these asteroids hit?"

calculate_force

F = ma calculations

"What force accelerates this car?"

calculate_kinetic_energy

KE = ½mv²

"How much energy in this crash?"

calculate_momentum

p = mv

"What's the momentum transfer?"

calculate_potential_energy

PE = mgh

"What's the energy at height?"

calculate_work_power

Work (F·d) and power (W/t)

"How much work lifting this box?"

calculate_elastic_collision

1D elastic collision (conserves energy & momentum)

"Pool balls colliding?"

calculate_drag_force

Air/water resistance (F = ½ρv²C_dA)

"What's the drag on this car?"

calculate_buoyancy

Will it float? (Archimedes)

"Does a steel ball float in water?"

calculate_terminal_velocity

Maximum fall speed

"How fast does a skydiver fall?"

simulate_underwater_motion

Underwater trajectory with drag & buoyancy

"How far does a torpedo travel?"

Characteristics:

  • ⚡ Instant execution (< 1ms)

  • 📦 No external dependencies

  • 🎯 Exact mathematical solutions

  • ✅ Always available (no service required)

Limitations:

  • Only spherical objects (for collisions)

  • No complex shapes

  • No multi-body interactions

  • No friction or material properties

2️⃣ Simulation Primitives (Requires Rapier Service)

Best for: Complex physics, multi-body dynamics, visualization data

Low-level building blocks for rigid-body simulations:

Tool

What It Does

When to Use

create_simulation

Initialize physics world

Start any simulation

add_rigid_body

Add objects (box, sphere, capsule, etc.)

Build scene

step_simulation

Advance time

Run physics

record_trajectory

Capture motion for R3F/Remotion

Generate animation data

destroy_simulation

Cleanup resources

End simulation

Characteristics:

  • 🦀 Requires Rapier service

  • 🔄 Stateful (track simulation ID)

  • 🧩 Composable (combine for complex scenarios)

  • 💪 Full rigid-body dynamics

Typical workflow:

# 1. Create world
sim = create_simulation(gravity_y=-9.81)

# 2. Add objects
add_rigid_body(sim.sim_id, "ground", type="static", shape="plane")
add_rigid_body(sim.sim_id, "ball", type="dynamic", shape="sphere",
               radius=0.5, position=[0, 5, 0])

# 3. Run simulation
step_simulation(sim.sim_id, steps=300)

# 4. Record for visualization
trajectory = record_trajectory(sim.sim_id, "ball", steps=300)

# 5. Cleanup
destroy_simulation(sim.sim_id)

📋 Complete Tool Reference

Basic Mechanics (8 tools)

Tool

Purpose

Example

calculate_projectile_motion

Ballistic trajectories

"How far does a cannonball travel?"

check_collision

Predict sphere collisions

"Will these asteroids hit?"

calculate_force

F = ma calculations

"What force accelerates this car?"

calculate_kinetic_energy

KE = ½mv²

"Energy in moving object?"

calculate_momentum

p = mv

"Momentum of moving object?"

calculate_potential_energy

PE = mgh

"Energy at height?"

calculate_work_power

Work (F·d) and power (W/t)

"Work done lifting object?"

calculate_elastic_collision

1D elastic collision

"Pool ball velocities after impact?"

Fluid Dynamics (10 tools)

Tool

Purpose

Example

calculate_drag_force

Air/water resistance

"Drag on car at speed?"

calculate_buoyancy

Will it float? (Archimedes)

"Does steel ball float?"

calculate_terminal_velocity

Maximum fall speed

"Skydiver terminal velocity?"

simulate_underwater_motion

Underwater trajectory

"How far does torpedo travel?"

calculate_lift_force

Aerodynamic lift

"Lift on aircraft wing?"

calculate_magnus_force

Force on spinning ball

"Why does curveball curve?"

calculate_bernoulli

Pressure in flowing fluid

"Pressure in pipe constriction?"

calculate_pressure_at_depth

Hydrostatic pressure

"Pressure at 30m depth?"

calculate_reynolds_number

Flow regime classification

"Is flow turbulent?"

calculate_venturi_effect

Flow through constriction

"Velocity in throat?"

Rotational Dynamics (5 tools)

Tool

Purpose

Example

calculate_torque

τ = r × F (cross product)

"Torque from wrench?"

calculate_moment_of_inertia

Rotational inertia

"MOI of spinning disk?"

calculate_angular_momentum

L = Iω

"Angular momentum of wheel?"

calculate_rotational_kinetic_energy

Rotational KE = ½Iω²

"Energy in flywheel?"

calculate_angular_acceleration

α = τ/I

"How fast does it spin up?"

Oscillations & Waves (5 tools)

Tool

Purpose

Example

calculate_hookes_law

Spring force F = -kx

"Force in compressed spring?"

calculate_spring_mass_period

Oscillation period

"How fast does mass oscillate?"

calculate_simple_harmonic_motion

Position at time t

"Where is mass at t=2s?"

calculate_damped_oscillation

Motion with damping

"How quickly does it settle?"

calculate_pendulum_period

Pendulum swing time

"Period of 1m pendulum?"

Circular Motion & Orbits (5 tools)

Tool

Purpose

Example

calculate_centripetal_force

F_c = mv²/r

"Force for circular motion?"

calculate_orbital_period

Kepler's 3rd law

"Satellite orbital period?"

calculate_banking_angle

Optimal curve angle

"Banking for highway curve?"

calculate_escape_velocity

Escape from gravity

"Earth escape velocity?"

analyze_circular_orbit

Complete orbital analysis

"Analyze ISS orbit?"

Statics & Equilibrium (7 tools)

Tool

Purpose

Example

check_force_balance

Verify ΣF = 0

"Are forces balanced?"

check_torque_balance

Verify Στ = 0

"Will seesaw balance?"

calculate_center_of_mass

Balance point

"Where is center of mass?"

calculate_static_friction

Max friction force

"Will object slip?"

calculate_normal_force

Force on incline

"Normal force on ramp?"

check_equilibrium

Force + torque balance

"Is structure stable?"

calculate_beam_reactions

Support forces

"Reaction forces on beam?"

Kinematics Analysis (7 tools)

Tool

Purpose

Example

calculate_acceleration_from_position

Derive from position data

"Acceleration from motion capture?"

calculate_jerk

Rate of acceleration change

"How jerky is motion?"

fit_trajectory

Find trajectory equation

"Fit parabola to data?"

generate_motion_graph

Position/velocity/accel graphs

"Generate motion graphs?"

calculate_average_speed

Speed along path

"Average speed on route?"

calculate_instantaneous_velocity

Velocity at exact time

"Speed at t=2.5s?"

calculate_projectile_with_drag

Realistic projectile with air resistance

"How far does baseball actually go?"

Advanced Collisions (2 tools)

Tool

Purpose

Example

calculate_elastic_collision_3d

3D perfect collision

"Pool balls in 3D?"

calculate_inelastic_collision_3d

3D collision with energy loss

"Car crash analysis?"

Conservation Laws (4 tools)

Tool

Purpose

Example

check_energy_conservation

Verify energy conserved

"Is collision realistic?"

check_momentum_conservation

Verify momentum conserved

"Is momentum preserved?"

check_angular_momentum_conservation

Verify L conserved

"Is rotation valid?"

track_energy_dissipation

Energy loss over time

"Where did energy go?"

Unit Conversions (2 tools)

Tool

Purpose

Supported Units

convert_unit

Convert between units

Velocity: m/s, km/h, mph, ft/s, knotsDistance: m, km, mi, ft, yd, inMass: kg, g, lb, ozForce: N, kN, lbfEnergy: J, kJ, cal, BTU, kWhPower: W, kW, hpTemperature: K, C, FAngle: rad, degPressure: Pa, kPa, bar, psi, atmArea: m², km², ft², acreVolume: m³, L, gal, ft³Time: s, min, hr, dayAcceleration: m/s², g, ft/s²Torque: N·m, lb·ft, lb·inFrequency: Hz, kHz, MHz, GHzData Size: B, KB, MB, GB

list_unit_conversions

Get all supported units

Returns complete list of conversions

Examples:

# Natural language queries work perfectly
"Convert 60 mph to m/s"  # → 26.82 m/s
"How fast is 100 km/h in mph?"  # → 62.14 mph
"What's 10 kg in pounds?"  # → 22.05 lb
"Convert 100 feet to meters"  # → 30.48 m
"What's 98.6°F in Celsius?"  # → 37°C
"Convert 3 g-force to m/s²"  # → 29.42 m/s²
"What's 300 N·m in lb·ft?"  # → 221.27 lb·ft
"Convert 1 hour to seconds"  # → 3600 s

Features:

  • ⚡ Instant conversions (no external service needed)

  • 🔄 Automatic indirect conversions (e.g., mph → km/h via m/s)

  • 📐 70+ unit types across 16 categories

  • 🎯 Perfect for natural language physics queries

  • 🚀 Includes engineering units (torque, acceleration, frequency)


Common Drag Coefficients (Cd) Reference

For use with calculate_projectile_with_drag tool:

Object

Drag Coefficient (Cd)

Notes

Sports Balls

Baseball

0.4

Stitched surface

Golf ball (dimpled)

0.25

Dimples reduce drag by ~50%

Golf ball (smooth)

0.47

Without dimples (don't use!)

Basketball

0.55

Large, textured surface

Soccer ball

0.25

Modern, smooth panels

Tennis ball

0.55

Fuzzy surface

Football (American)

0.05-0.15

Highly streamlined, orientation-dependent

Generic Shapes

Sphere (smooth)

0.47

Default reference

Flat plate (perpendicular)

1.28

Maximum drag

Streamlined body

0.04

Teardrop/airfoil

Cylinder (perpendicular)

1.15

Like a pole

Vehicles

Car (modern)

0.25-0.35

Aerodynamic design

Truck

0.6-0.9

Boxy shape

Bicycle + rider

0.9

Upright position

Human Body

Skydiver (belly-down)

1.0-1.3

Maximum drag

Skydiver (head-down)

0.7

Streamlined

Projectiles

Bullet (supersonic)

0.295

Pointed nose

Artillery shell

0.15-0.25

Streamlined

Basic Usage Example:

# Baseball pitch with realistic drag
result = await calculate_projectile_with_drag(
    initial_velocity=40.23,  # 90 mph
    angle_degrees=10,
    mass=0.145,
    cross_sectional_area=0.0043,  # π × (0.037m)²
    drag_coefficient=0.4  # Baseball Cd
)

Advanced Projectile Features

The calculate_projectile_with_drag tool supports optional enhancements for ultra-realistic simulations:

🌀 Magnus Force (Spin Effects)

Spin creates a pressure differential that deflects the ball's path. Essential for:

  • Baseball: Curveballs (topspin drops), fastballs (backspin lifts)

  • Golf: Backspin increases carry, sidespin causes slices/hooks

  • Soccer: Bending free kicks around defensive walls

  • Tennis: Topspin brings ball down faster

# Baseball curveball with topspin
result = await calculate_projectile_with_drag(
    initial_velocity=35,
    angle_degrees=0,
    mass=0.145,
    cross_sectional_area=0.0043,
    drag_coefficient=0.4,
    spin_rate=261.8,  # 2500 rpm = 261.8 rad/s
    spin_axis=[0, 0, -1]  # Topspin (negative z-axis)
)
# Returns lateral_deflection and magnus_force_max

Spin Parameters:

  • spin_rate: Rotation speed in rad/s (convert from RPM: rpm × 2π/60)

  • spin_axis: Unit vector [x, y, z] indicating spin direction

    • [0, 0, 1] = Backspin (lifts)

    • [0, 0, -1] = Topspin (drops)

    • [0, 1, 0] = Sidespin (hooks/slices)

💨 Wind Effects

Constant wind vector affects trajectory throughout flight:

# Soccer free kick with 5 m/s crosswind
result = await calculate_projectile_with_drag(
    initial_velocity=28,
    angle_degrees=12,
    mass=0.43,
    cross_sectional_area=0.0388,
    drag_coefficient=0.25,
    wind_velocity=[5.0, 0.0]  # [horizontal, vertical] in m/s
)
# Returns wind_drift showing total deflection

Wind Types:

  • Tailwind: [+X, 0] - increases range

  • Headwind: [-X, 0] - decreases range

  • Crosswind: [X, 0] - lateral deflection

  • Updraft: [0, +Y] - increases height and flight time

  • Downdraft: [0, -Y] - decreases height

🏔️ Altitude & Temperature Effects

Air density varies with elevation and temperature, dramatically affecting drag:

# Golf drive in Denver (1600m elevation, 20°C)
result = await calculate_projectile_with_drag(
    initial_velocity=70,
    angle_degrees=12,
    mass=0.0459,
    cross_sectional_area=0.00143,
    drag_coefficient=0.25,
    altitude=1600,  # meters above sea level
    temperature=20  # Celsius
)
# Returns effective_air_density showing actual density used

Real-World Impact:

  • Denver (1600m): ~10% longer drives than sea level

  • Hot day (+20°C): ~2-3% less drag than cold day

  • Everest Base Camp (5300m): ~50% less air density!

Air Density Formula:

ρ(h,T) = ρ₀ × exp(-Mgh/RT₀) × (T₀/T)

Where:

  • ρ₀ = sea level density (1.225 kg/m³)

  • h = altitude (meters)

  • T = temperature (Kelvin)

  • M = molar mass of air (0.029 kg/mol)

  • g = gravity (9.81 m/s²)

  • R = gas constant (8.314 J/(mol·K))

🌟 Combined Effects Example

# Golf ball with ALL effects (spin + wind + altitude)
result = await calculate_projectile_with_drag(
    initial_velocity=70,
    angle_degrees=12,
    mass=0.0459,
    cross_sectional_area=0.00143,
    drag_coefficient=0.25,
    spin_rate=200,  # Backspin
    spin_axis=[0, 0, 1],
    wind_velocity=[3, 0],  # Tailwind
    altitude=1000,  # Moderate elevation
    temperature=25  # Warm day
)
# All effects combine for maximum realism!

See Examples:

  • examples/sports_projectiles_with_drag.py - Basic drag effects

  • examples/advanced_projectile_effects.py - Magnus force, wind, altitude


🔄 Orientation-Dependent Drag (Rapier Simulations)

For tumbling objects like footballs, frisbees, and javelins, drag varies dramatically based on orientation. A football in a perfect spiral has 3-6× less drag than when tumbling end-over-end!

Available via Rapier rigid-body simulations using the add_rigid_body tool with orientation-dependent drag parameters.

How It Works

Objects moving through air experience drag that depends on their orientation:

  • Football spiral: Streamlined along flight path → low drag (~0.1 Cd)

  • Football tumbling: Broadside to airflow → high drag (~0.6 Cd)

  • Frisbee flat: Minimal cross-section → low drag (~0.08 Cd)

  • Frisbee tilted: Larger cross-section → higher drag

New add_rigid_body Parameters:

drag_coefficient: float      # Base Cd value
drag_area: float             # Reference cross-sectional area (m²)
drag_axis_ratios: [x, y, z]  # Drag variation along body axes
fluid_density: float         # Fluid density (air=1.225, water=1000)

Example: Football Spiral vs Tumble

# Perfect spiral (low drag along Y-axis)
await add_rigid_body(
    sim_id,
    id="spiral",
    shape="capsule",
    size=[0.17, 0.28],  # diameter, length
    mass=0.42,
    position=[0, 2, 0],
    velocity=[vx, vy, 0],
    angular_velocity=[0, 126, 0],  # 20 rev/s spin
    # Orientation-dependent drag
    drag_coefficient=0.1,
    drag_area=0.023,  # End-on area
    drag_axis_ratios=[1.0, 0.2, 1.0],  # 5× less drag along Y
    fluid_density=1.225
)

# Tumbling (higher drag, averaging all orientations)
await add_rigid_body(
    sim_id,
    id="tumble",
    shape="capsule",
    size=[0.17, 0.28],
    mass=0.42,
    position=[0, 2, 0],
    velocity=[vx, vy, 0],
    angular_velocity=[31.4, 0, 0],  # Tumbling rotation
    # Orientation-dependent drag
    drag_coefficient=0.6,  # Higher base Cd
    drag_area=0.048,  # Broadside area
    drag_axis_ratios=[0.8, 1.0, 0.8],  # Less streamlining
    fluid_density=1.225
)

Result: Spiral can travel 20-40% farther than tumble!

Common drag_axis_ratios Patterns

The drag_axis_ratios parameter specifies how drag varies along each body-local axis [X, Y, Z]:

Object

Ratios

Streamlined Axis

Use Case

Football spiral

[1.0, 0.2, 1.0]

Y (length)

Perfect pass

Javelin

[1.5, 0.2, 1.5]

Y (length)

Optimal flight

Frisbee flat

[1.0, 0.1, 1.0]

Y (vertical)

Stable throw

Sphere

[1.0, 1.0, 1.0]

None

Basketball, etc.

Disc tumbling

[0.8, 1.0, 0.8]

Less variation

Wobbly throw

Physical Meaning:

  • 0.2 = 20% of base drag along that axis (very streamlined)

  • 1.0 = 100% of base drag (normal)

  • 1.5 = 150% of base drag (higher resistance)

Real-World Examples

⚽ Football Throw (spiral vs tumble):

# Spiral: 45-50 yards typical
# Tumble: 30-35 yards (30-40% loss)

🥏 Frisbee (stable vs wobbling):

# Stable (600 rpm spin): 60-80 meters
# Wobbling (slow spin): 30-40 meters (50% loss)

🏹 Javelin (optimal vs poor technique):

# Optimal angle: 70-90 meters (Olympic level)
# Poor release: 40-50 meters (45% loss)

Important Notes

⚠️ Requires Rapier Service: Orientation-dependent drag calculations are performed by the Rapier physics service (Rust implementation). The Python MCP server defines the API and passes parameters to Rapier.

🎯 When to Use:

  • Sports simulations (football, frisbee, discus)

  • Projectile accuracy (javelin, arrows, darts)

  • Aerospace applications (rocket tumbling, debris)

🔬 Physics: The drag force is calculated in the Rapier service using the body's current orientation (quaternion) to transform body-local drag coefficients into world-space drag forces.

Hybrid Drag Implementation: Rapier uses a hybrid approach to handle extreme drag cases:

  • Normal drag (ratio < 2.0): Force-based orientation-dependent drag with full anisotropic behavior

  • Extreme drag (ratio ≥ 2.0): Damping-based drag for stability when drag-to-weight ratio is very high

    • Automatically activates for objects like ping pong balls (high drag, low mass)

    • Prevents numerical instabilities while maintaining realistic energy dissipation

    • Logged as INFO when triggered: "Body 'name' has extreme drag (ratio=X.XX), using damping"

Where drag_ratio = 0.5 * fluid_density * drag_coefficient * drag_area * v_typical^2 / (mass * g)

Measurement Notes: When analyzing trajectories, use max(x_positions) instead of final_x to measure range. Rapier's solver may occasionally jitter backward slightly near ground impact, but this is a measurement artifact, not a physics error. The drag forces always oppose motion correctly.

See Example:

  • examples/tumbling_projectiles.py - Football, frisbee, and javelin orientation effects


✨ Phase 1 Features (Production Ready)

All Phase 1 features are complete, tested (98% coverage), and deployed to production!

Phase 1.1: Bounce Detection 🏀

Automatically detect and analyze bounces in ball trajectories with energy loss calculations.

What it does:

  • Detects bounce events from trajectory data (velocity reversals near ground)

  • Calculates energy loss percentage for each bounce

  • Provides before/after velocities and heights

  • Perfect for answering "how many bounces?" and "when does it stop?"

Tool: record_trajectory_with_events

Example:

# Record a bouncing ball
traj = await record_trajectory_with_events(
    sim_id=sim_id,
    body_id="ball",
    steps=300,
    detect_bounces=True,
    bounce_height_threshold=0.01  # 1cm = "on ground"
)

print(f"Detected {len(traj.bounces)} bounces")
for bounce in traj.bounces:
    print(f"Bounce #{bounce.bounce_number}:")
    print(f"  Time: {bounce.time:.2f}s")
    print(f"  Height: {bounce.height_at_bounce:.3f}m")
    print(f"  Energy loss: {bounce.energy_loss_percent:.1f}%")
    print(f"  Speed before: {bounce.speed_before:.2f} m/s")
    print(f"  Speed after: {bounce.speed_after:.2f} m/s")

Output:

Detected 5 bounces
Bounce #1: Time: 1.43s, Height: 0.00m, Energy loss: 36.0%, Speed: 14.0→9.0 m/s
Bounce #2: Time: 2.51s, Height: 0.00m, Energy loss: 36.0%, Speed: 8.9→5.7 m/s
Bounce #3: Time: 3.23s, Height: 0.00m, Energy loss: 36.0%, Speed: 5.7→3.6 m/s
...

Use Cases:

  • Sports analytics (basketball arc, tennis serve bounces)

  • Product testing (phone drop tests, durability simulations)

  • Game development (realistic ball physics)

  • Education (demonstrate energy conservation)

See: examples/06_bounce_detection.py for full demo


Phase 1.2: Contact Events 📊

Real-time collision tracking with detailed contact information from the physics engine.

What it does:

  • Tracks all contact events between bodies during simulation

  • Reports contact start, ongoing, and end events

  • Provides impulse magnitudes, normals, and relative velocities

  • Essential for collision analysis and force calculations

Included in: All trajectory recordings (record_trajectory, record_trajectory_with_events)

Contact Event Data:

ContactEvent(
    time=1.43,                          # When contact occurred
    body_a="ball",                      # First body
    body_b="ground",                    # Second body
    contact_point=[0.0, 0.05, 0.0],    # World space position
    normal=[0.0, 1.0, 0.0],            # Contact normal (from A to B)
    impulse_magnitude=14.2,             # Collision impulse (N⋅s)
    relative_velocity=[0.0, -14.0, 0.0], # Relative velocity
    event_type="started"                # "started", "ongoing", or "ended"
)

Example:

traj = await record_trajectory(sim_id, "ball", steps=300)

# Analyze contacts
for event in traj.contact_events:
    if event.event_type == "started":
        print(f"Collision at t={event.time:.2f}s")
        print(f"  Bodies: {event.body_a} ↔ {event.body_b}")
        print(f"  Impulse: {event.impulse_magnitude:.1f} N⋅s")
        print(f"  Impact speed: {abs(event.relative_velocity[1]):.1f} m/s")

Use Cases:

  • Collision analysis (car crashes, sports impacts)

  • Force calculations (derive forces from impulses)

  • Interaction tracking (which objects touched what)

  • VFX triggers (spark effects on collisions)

See: examples/07_contact_events.py for full demo


Phase 1.3: Joints & Constraints 🔗

Connect rigid bodies with realistic joints for complex mechanical systems.

What it does:

  • Create constraints between bodies (hinges, sliders, ball-and-socket, fixed)

  • Build complex systems (pendulums, chains, ragdolls, machinery)

  • Realistic mechanical motion (doors, wheels, linkages)

  • Perfect for simulating articulated structures

Tool: add_joint

Joint Types:

Type

Description

Example Uses

FIXED

Rigid connection (glue)

Attach hat to head, weld joints

REVOLUTE

Hinge rotation around axis

Doors, pendulums, wheels

SPHERICAL

Ball-and-socket rotation

Ragdoll shoulders, gimbal mounts

PRISMATIC

Sliding along axis

Pistons, elevators, sliders

Example - Simple Pendulum:

# Create anchor point
await add_rigid_body(
    sim_id=sim_id,
    body_id="anchor",
    body_type="static",
    shape="sphere",
    size=[0.05],
    position=[0.0, 3.0, 0.0]
)

# Create pendulum bob
await add_rigid_body(
    sim_id=sim_id,
    body_id="bob",
    body_type="dynamic",
    shape="sphere",
    size=[0.2],
    mass=1.0,
    position=[1.5, 1.5, 0.0]  # Start displaced
)

# Connect with revolute joint (hinge)
await add_joint(
    sim_id=sim_id,
    joint=JointDefinition(
        id="hinge",
        joint_type=JointType.REVOLUTE,
        body_a="anchor",
        body_b="bob",
        anchor_a=[0.0, 0.0, 0.0],    # Center of anchor
        anchor_b=[0.0, 0.2, 0.0],    # Top of bob
        axis=[0.0, 0.0, 1.0]         # Rotate around Z-axis
    )
)

# Simulate and see realistic pendulum motion!
traj = await record_trajectory(sim_id, "bob", steps=300)

Example - Multi-Link Chain:

# Create anchor
await add_rigid_body(sim_id, "anchor", body_type="static", ...)

# Create 3 chain links
for i in range(3):
    await add_rigid_body(
        sim_id,
        f"link{i}",
        body_type="dynamic",
        shape="box",
        size=[0.1, 0.4, 0.1],
        position=[0.0, 2.5 - i*0.5, 0.0]
    )

# Connect with spherical joints (ball-and-socket)
await add_joint(sim_id, JointDefinition(
    id="joint0",
    joint_type=JointType.SPHERICAL,
    body_a="anchor",
    body_b="link0",
    anchor_a=[0.0, 0.0, 0.0],
    anchor_b=[0.0, 0.2, 0.0]
))

await add_joint(sim_id, JointDefinition(
    id="joint1",
    joint_type=JointType.SPHERICAL,
    body_a="link0",
    body_b="link1",
    anchor_a=[0.0, -0.2, 0.0],
    anchor_b=[0.0, 0.2, 0.0]
))

# ... and so on

Use Cases:

  • Mechanical systems (engines, gears, levers)

  • Character animation (ragdolls, inverse kinematics)

  • Vehicle suspension (wheels, shocks)

  • Architectural simulations (doors, drawbridges)

See: examples/08_pendulum.py for full demo


Phase 1.4: Damping & Advanced Controls 🌬️

Realistic energy dissipation through linear and angular damping.

What it does:

  • Simulates air resistance (linear damping)

  • Simulates rotational friction (angular damping)

  • Makes simulations more realistic and stable

  • Perfect for settling physics and reducing "floaty" motion

Parameters: Added to add_rigid_body tool

Damping Parameters:

await add_rigid_body(
    sim_id=sim_id,
    body_id="damped_ball",
    body_type="dynamic",
    shape="sphere",
    size=[0.5],
    mass=1.0,
    position=[0.0, 5.0, 0.0],

    # Phase 1.4: Damping
    linear_damping=0.5,   # 0.0 (none) to 1.0 (high) - like air resistance
    angular_damping=0.3   # 0.0 (none) to 1.0 (high) - like rotational friction
)

Effect of Linear Damping:

  • 0.0 = No air resistance (vacuum physics)

  • 0.1-0.3 = Light damping (tennis ball in air)

  • 0.5-0.7 = Moderate damping (underwater motion)

  • 0.9+ = Heavy damping (very viscous fluid)

Effect of Angular Damping:

  • 0.0 = Spins forever (vacuum)

  • 0.1-0.3 = Realistic friction (rolling ball)

  • 0.5-0.7 = High friction (rough surface)

  • 0.9+ = Almost no rotation (sticky surface)

Comparison:

# Without damping - bounces forever
await add_rigid_body(..., linear_damping=0.0)
# Result: Ball bounces 20+ times, takes 30+ seconds to settle

# With damping - realistic settling
await add_rigid_body(..., linear_damping=0.5)
# Result: Ball bounces 5 times, settles in ~5 seconds

Use Cases:

  • Realistic object motion (not "floaty" game physics)

  • Faster settling (less simulation time needed)

  • Underwater simulations (high damping)

  • Space simulations (zero damping)

See: examples/09_phase1_complete.py for all Phase 1 features combined


Phase 1.5: Fluid Dynamics 🌊

Analytical fluid calculations for drag, buoyancy, and underwater motion.

What it does:

  • Calculates drag forces (quadratic air/water resistance)

  • Computes buoyancy using Archimedes' principle

  • Determines terminal velocity for falling objects

  • Simulates underwater projectile motion with drag and buoyancy

Tools Available:

1. calculate_drag_force - Air/Water Resistance

Calculate the force opposing motion through a fluid.

# Ball falling through water
result = await calculate_drag_force(
    velocity=[0, -5.0, 0],          # 5 m/s downward
    cross_sectional_area=0.00785,   # π*r² for 10cm diameter
    fluid_density=1000,              # water (air=1.225)
    drag_coefficient=0.47,           # sphere (streamlined=0.04)
    viscosity=1.002e-3               # optional: water viscosity for accurate Re
)

print(f"Drag force: {result['magnitude']:.1f} N (upward)")
print(f"Reynolds number: {result['reynolds_number']:.0f}")

Common drag coefficients:

  • Sphere: 0.47

  • Streamlined (torpedo): 0.04

  • Flat plate: 1.28

  • Human (standing): 1.0-1.3

  • Car: 0.25-0.35

Optional viscosity parameter (for accurate Reynolds number):

  • Water at 20°C: 1.002e-3 Pa·s

  • Air at 20°C: 1.825e-5 Pa·s

  • Motor oil: 0.1 Pa·s

  • If omitted, estimated from density (water-like if >100 kg/m³, else air-like)

2. calculate_buoyancy - Will it Float?

Determine buoyant force and whether objects float or sink.

# Check if 1kg steel ball floats
volume = (4/3) * π * (0.05)**3  # 10cm diameter sphere
result = await calculate_buoyancy(
    volume=0.000524,          # m³
    fluid_density=1000        # water
)

weight = 1.0 * 9.81  # 9.81 N
buoyancy = result['buoyant_force']  # 5.14 N

# weight > buoyancy → SINKS

3. calculate_terminal_velocity - Maximum Fall Speed

Calculate the speed where drag equals weight.

# Skydiver terminal velocity
result = await calculate_terminal_velocity(
    mass=70,                      # kg
    cross_sectional_area=0.7,     # m² (belly-down)
    fluid_density=1.225,          # air
    drag_coefficient=1.0          # human
)

print(f"Terminal velocity: {result['terminal_velocity']:.1f} m/s")
# Result: ~40 m/s (90 mph)
print(f"Time to 95%: {result['time_to_95_percent']:.1f}s")

4. simulate_underwater_motion - Full Fluid Simulation

Simulate motion through fluids with drag and buoyancy forces.

# Torpedo launched underwater
result = await simulate_underwater_motion(
    initial_velocity=[20, 0, 0],   # 20 m/s forward
    mass=100,                       # kg
    volume=0.05,                    # m³
    cross_sectional_area=0.03,      # m²
    fluid_density=1000,             # water
    drag_coefficient=0.04,          # streamlined
    duration=30.0
)

print(f"Distance traveled: {result['total_distance']:.1f}m")
print(f"Final velocity: {result['final_velocity']}")
print(f"Max depth: {result['max_depth']:.1f}m")

Use Cases:

  • Marine engineering: Torpedo trajectories, submarine drag

  • Aerospace: Skydiving, parachute descent, atmospheric re-entry

  • Sports: Swimming, diving, underwater ballistics

  • Product design: Drag optimization, floatation devices

  • Environmental: Particle settling, pollutant dispersion

Physics Models:

  • Quadratic drag: F_drag = 0.5 * ρ * v² * C_d * A

  • Buoyancy: F_b = ρ_fluid * V * g (Archimedes)

  • Terminal velocity: v_t = √(2mg / ρC_dA)

  • Numerical integration for complex underwater motion

See: examples/10_fluid_dynamics.py for comprehensive demonstrations


🎉 Phase 1 Complete Summary

Status: ✅ All features production-ready

Feature

Status

Tool

Coverage

Bounce Detection

✅ Shipped

record_trajectory_with_events

100%

Contact Events

✅ Shipped

All trajectory tools

100%

Joints & Constraints

✅ Shipped

add_joint

100%

Damping Controls

✅ Shipped

add_rigid_body

100%

Fluid Dynamics

✅ Shipped

calculate_drag_force, calculate_buoyancy, calculate_terminal_velocity, simulate_underwater_motion

100%

Test Coverage: 98% (350 tests passing)

Deployment:

Examples: See examples/06_bounce_detection.py through examples/10_fluid_dynamics.py


🚀 Phase 2 Features (Production Ready)

All Phase 2 features are complete, tested (98% coverage), and deployed to production!

Phase 2.1: Rotational Dynamics 🔄

Complete rotational motion calculations including torque, moment of inertia, angular momentum, and rotational kinetic energy.

Tools Available:

Tool

Description

Example Use

calculate_torque

Calculate torque from force and position (τ = r × F)

"What torque does this wrench apply?"

calculate_moment_of_inertia

Moment of inertia for common shapes (disk, sphere, rod, etc.)

"What's the rotational inertia?"

calculate_angular_momentum

Angular momentum (L = Iω)

"How much rotational momentum?"

calculate_rotational_kinetic_energy

Rotational KE (½Iω²)

"Energy in spinning flywheel?"

calculate_angular_acceleration

Angular acceleration (α = τ/I)

"How fast does it spin up?"

Example - Calculate Torque:

result = await calculate_torque(
    force_x=50.0,
    force_y=0.0,
    force_z=0.0,
    position_x=0.0,
    position_y=0.0,
    position_z=0.8  # 80cm wrench
)
# torque magnitude = 40 N⋅m

Use Cases:

  • Mechanical engineering (gear systems, engines)

  • Robotics (joint torques, motor sizing)

  • Sports science (bat swings, golf clubs)

  • Aerospace (satellite attitude control)


Phase 2.2: Oscillations & Waves 🌊

Harmonic motion and spring systems with damping effects.

Tools Available:

Tool

Description

Example Use

calculate_hookes_law

Spring force and potential energy (F = -kx)

"How much force in compressed spring?"

calculate_spring_mass_period

Period and frequency of spring-mass system

"How fast does it oscillate?"

calculate_simple_harmonic_motion

Position, velocity, acceleration at time t

"Where is the mass at t=2s?"

calculate_damped_oscillation

Damped harmonic motion (underdamped, critically damped, overdamped)

"How quickly does it settle?"

calculate_pendulum_period

Period of simple pendulum

"How long is one swing?"

Example - Spring-Mass System:

result = await calculate_spring_mass_period(
    mass=0.5,           # 500g mass
    spring_constant=20.0  # N/m
)
# period ≈ 0.99s, frequency ≈ 1.01 Hz

Use Cases:

  • Mechanical design (suspension systems, vibration isolation)

  • Seismology (earthquake oscillations)

  • Electronics (LC circuits, resonance)

  • Horology (pendulum clocks)


Phase 2.3: Circular Motion & Orbits 🌍

Circular motion, orbital mechanics, and centripetal forces.

Tools Available:

Tool

Description

Example Use

calculate_centripetal_force

Force required for circular motion

"What force keeps car on curve?"

calculate_orbital_period

Period and velocity for circular orbit

"How long is satellite orbit?"

calculate_banking_angle

Optimal banking for curved road

"What angle for this turn?"

calculate_escape_velocity

Minimum velocity to escape gravity

"Can rocket escape Earth?"

analyze_circular_orbit

Complete orbital analysis (altitude, period, velocity)

"Analyze ISS orbit"

Example - Satellite Orbit:

result = await analyze_circular_orbit(
    altitude=400000.0,       # 400 km above surface
    planet_mass=5.972e24,    # Earth mass
    planet_radius=6.371e6    # Earth radius
)
# orbital_velocity ≈ 7670 m/s
# period ≈ 5530 seconds (92 minutes)

Use Cases:

  • Space missions (orbital calculations, satellite deployment)

  • Astrophysics (planetary motion, binary stars)

  • Transportation (highway curve design)

  • Amusement parks (loop-the-loop, centrifuges)


Phase 2.4: Advanced Collisions 💥

3D collision calculations with elastic and inelastic collisions.

Tools Available:

Tool

Description

Example Use

calculate_elastic_collision_3d

3D elastic collision (energy conserved)

"Pool ball collisions in 3D"

calculate_inelastic_collision_3d

3D inelastic collision with restitution

"Car crash with energy loss"

Example - Car Crash:

result = await calculate_inelastic_collision_3d(
    mass1=1500.0,
    velocity1=[20.0, 0.0, 0.0],
    mass2=1200.0,
    velocity2=[-15.0, 0.0, 0.0],
    coefficient_of_restitution=0.0  # Perfectly inelastic
)
# final_velocity1 = [1.11, 0, 0]
# final_velocity2 = [1.11, 0, 0]  # Stick together
# energy_loss > 0 (deformation energy)

Phase 2.5: Conservation Laws ⚖️

Verify and track conservation of energy, momentum, and angular momentum.

Tools Available:

Tool

Description

Example Use

check_energy_conservation

Verify total energy is conserved

"Is this collision realistic?"

check_momentum_conservation

Verify momentum is conserved

"Does this violate physics?"

check_angular_momentum_conservation

Verify angular momentum conserved

"Is rotation energy conserved?"

track_energy_dissipation

Track energy loss over trajectory

"Where did the energy go?"

Example - Validate Collision:

result = await check_energy_conservation(
    initial_kinetic_energy=100.0,
    final_kinetic_energy=50.0,
    initial_potential_energy=0.0,
    final_potential_energy=50.0
)
# is_conserved = True (100 = 50 + 50)
# energy_difference ≈ 0

Phase 2.6: Statics & Equilibrium ⚖️

Static equilibrium analysis for structures and forces.

Tools Available:

Tool

Description

Example Use

check_force_balance

Verify ΣF = 0 (force equilibrium)

"Are these forces balanced?"

check_torque_balance

Verify Στ = 0 (torque equilibrium)

"Will this seesaw balance?"

calculate_center_of_mass

Find center of mass for system

"Where is the balance point?"

calculate_static_friction

Maximum friction force, will object slip?

"Will box slide down ramp?"

calculate_normal_force

Normal force on inclined plane

"What force on ramp?"

check_equilibrium

Complete equilibrium check (force + torque)

"Is structure stable?"

calculate_beam_reactions

Reaction forces for simply supported beam

"What are support forces?"

Example - Beam Analysis:

result = await calculate_beam_reactions(
    beam_length=10.0,
    loads=[1000, 500],  # Two point loads
    load_positions=[3.0, 7.0]  # Positions along beam
)
# reaction_left = 800 N
# reaction_right = 700 N
# is_balanced = True

Use Cases:

  • Structural engineering (bridges, buildings)

  • Mechanical design (levers, balances)

  • Architecture (load analysis)

  • Safety analysis (stability checks)


Phase 2.7: Kinematics Analysis 📊

Analyze motion data to extract velocities, accelerations, and trajectories.

Tools Available:

Tool

Description

Example Use

calculate_acceleration_from_position

Derive velocity and acceleration from position data

"Analyze motion capture data"

calculate_jerk

Calculate jerk (rate of change of acceleration)

"How jerky is this motion?"

fit_trajectory

Fit polynomial to trajectory (linear, quadratic, cubic)

"Find trajectory equation"

generate_motion_graph

Generate position/velocity/acceleration graphs

"Visualize kinematics"

calculate_average_speed

Average speed along path

"What's average speed?"

calculate_instantaneous_velocity

Velocity at specific time with interpolation

"Speed at exact moment?"

Example - Motion Analysis:

result = await calculate_acceleration_from_position(
    times=[0, 1, 2, 3, 4],
    positions=[[0,0,0], [5,0,0], [10,0,0], [15,0,0], [20,0,0]]
)
# velocities = [[5,0,0], [5,0,0], ...]  # Constant 5 m/s
# average_acceleration ≈ [0,0,0]  # No acceleration

Use Cases:

  • Motion capture analysis (sports, biomechanics)

  • Robotics (trajectory planning, motion smoothness)

  • Autonomous vehicles (trajectory optimization)

  • Scientific research (particle tracking)


Phase 2.8: Advanced Fluid Dynamics 💨

Extended fluid calculations including lift, Magnus force, Bernoulli, and viscous flow.

Tools Available:

Tool

Description

Example Use

calculate_lift_force

Aerodynamic lift (L = ½ρv²C_LA)

"What lift on wing?"

calculate_magnus_force

Force on spinning ball

"Why does curveball curve?"

calculate_bernoulli

Bernoulli's equation for flowing fluids

"Pressure in pipe constriction?"

calculate_pressure_at_depth

Hydrostatic pressure

"Pressure at 30m depth?"

calculate_reynolds_number

Flow regime (laminar/turbulent)

"Is flow turbulent?"

calculate_venturi_effect

Flow through constriction

"Velocity in throat?"

Example - Aircraft Wing:

result = await calculate_lift_force(
    velocity=70,          # m/s (~250 km/h)
    wing_area=20.0,       # m²
    lift_coefficient=1.2,
    fluid_density=1.225   # air
)
# lift_force ≈ 73,500 N

Use Cases:

  • Aerospace engineering (aircraft design, aerodynamics)

  • Marine engineering (hull design, submarine motion)

  • Sports science (ball trajectories, swimming efficiency)

  • HVAC design (airflow optimization)


🎉 Phase 2 Complete Summary

Status: ✅ All features production-ready

Feature Area

Tools

Coverage

Rotational Dynamics

5 tools

100%

Oscillations

5 tools

100%

Circular Motion

5 tools

100%

Advanced Collisions

2 tools

100%

Conservation Laws

4 tools

100%

Statics

7 tools

100%

Kinematics Analysis

6 tools

100%

Advanced Fluids

6 tools

100%

Total Tools: 79 tools across all categories Test Coverage: 98% overall (350 tests passing)


Next Up: Phase 3 - Performance Optimization & Visualization

  • Batch simulation API for ML training data

  • Parameter sweep utilities

  • Enhanced visualization exports

  • Performance profiling and optimization


🎓 Example Conversations

Projectile Motion

User: A golf ball is hit at 70 m/s at 15 degrees. How far does it go and what's the flight time?

LLM: [calls calculate_projectile_motion(70, 15)]

     Range ≈ 249.7 m
     Time of flight ≈ 3.694 s
     Maximum height ≈ 16.73 m

     Here are 50 trajectory points for visualization...
User: What angle gives maximum range for a cannonball fired at 100 m/s?

LLM: [calls calculate_projectile_motion multiple times with different angles]

     The optimal angle is 45°, which gives:
     - Maximum range: 1019.4 m
     - Flight time: 14.4 s
     - Peak height: 254.8 m

Collision Detection

User: Two asteroids: one at (0,0,0) moving at 1000 m/s in x direction,
      another at (50000, 100, 0) moving at -800 m/s in x. Will they collide?

LLM: [calls check_collision with both objects]

     Yes, they will collide!
     - Collision time: ~27.8 seconds
     - Impact location: (27,778, 100, 0)
     - Impact speed: 1,800 m/s
     - This would be a devastating impact given the closing speed.
User: Two cars 500m apart, one at 60 mph, other at 45 mph heading toward each other.
      When do they collide?

LLM: [calls check_collision]

     Collision in 10.7 seconds at position 250m from car 1's starting point.
     Combined approach speed is 105 mph (46.9 m/s).

Force & Energy Calculations

User: What force is needed to accelerate a 1500 kg car at 3 m/s²?

LLM: [calls calculate_force(1500, 3)]

     Force required: 4,500 N (Newtons)
     This is about 1,011 pounds of force.
User: What's the kinetic energy of a 2000 kg car at 30 m/s (about 67 mph)?

LLM: [calls calculate_kinetic_energy(2000, 30)]

     Kinetic energy: 900,000 J (Joules)
     That's 900 kJ or 0.25 kWh of energy.
     For perspective, this is why car crashes at highway speeds are so dangerous.

Momentum Analysis

User: Calculate momentum of a 70 kg runner at 10 m/s

LLM: [calls calculate_momentum(70, 10)]

     Momentum: 700 kg⋅m/s
     Direction: same as velocity vector

Complex Multi-Step Problems

User: I'm designing a water fountain. Water shoots up at 15 m/s.
      How high does it go and when does it come back down?

LLM: [calls calculate_projectile_motion(15, 90)]

     The water will:
     - Rise to maximum height: 11.47 m
     - Take 1.53 seconds to reach peak
     - Return to ground level after 3.06 seconds total
     - Have the same 15 m/s speed when it lands (just downward)
User: A basketball player shoots from 2m high at 7 m/s toward a hoop
      3.05m high and 4.6m away. What angles work?

LLM: [calls calculate_projectile_motion with various angles]

     Two possible angles work:
     1. Low arc: 38° (faster, flatter shot)
     2. High arc: 52° (slower, higher shot)

     Most players use 50-55° for better chance of going in.

Rigid Body Simulations

User: Create a simulation of a ball dropping from 10 meters and bouncing

LLM: [calls create_simulation(gravity_y=-9.81)]
     [calls add_rigid_body for ground plane (static)]
     [calls add_rigid_body for ball (dynamic, sphere, position=(0,10,0))]
     [calls record_trajectory(steps=200)]

     Simulation complete! The ball:
     - Hits ground at t=1.43s with velocity 14.0 m/s
     - Bounces to 7.5m (with restitution=0.8)
     - Makes 5 bounces before settling

     Here's the full trajectory data for R3F visualization...
User: Simulate 5 boxes stacked, then another box hits them from the side

LLM: [calls create_simulation]
     [calls add_rigid_body for ground]
     [calls add_rigid_body 5 times for stacked boxes]
     [calls add_rigid_body for projectile box with velocity]
     [calls step_simulation(300)]
     [calls record_trajectory for each box]

     The collision causes a realistic toppling effect!
     Boxes 1-2 fall left, boxes 3-5 scatter right.
     Peak chaos at t=0.8s. All settled by t=3.2s.

     Full trajectory data ready for 3D visualization...

💡 Try These Prompts

Copy and paste these into your LLM chat to see the physics tools in action:

Projectile Motion

  • A golf ball is hit at 70 m/s at 15 degrees. How far does it go and what's the flight time?

  • What angle gives maximum range for a cannonball fired at 100 m/s?

  • If I throw a javelin at 28 m/s from 2 meters high, what angle gives maximum distance?

  • A basketball player shoots from 2m high at 7 m/s toward a hoop 3.05m high and 4.6m away. What angles work?

Collision Detection

  • Two cars 500m apart, one at 60 mph, other at 45 mph heading toward each other. When do they collide?

  • Two asteroids: one at (0,0,0) moving at 1000 m/s in x direction, another at (50000, 100, 0) moving at -800 m/s in x. Will they collide?

  • Spaceship A at (10000,0,0) moving at (-50,0,0) m/s, spaceship B at (-10000,100,0) moving at (45,0,0) m/s. Collision check?

Force, Energy & Momentum

  • What force is needed to accelerate a 1500 kg car at 3 m/s²?

  • What's the kinetic energy of a 2000 kg car traveling at 30 m/s?

  • Calculate the momentum of a 70 kg runner sprinting at 10 m/s

  • How much energy does a 0.145 kg baseball have when pitched at 45 m/s?

Real-World Applications

  • I'm designing a water fountain. Water shoots up at 15 m/s. How high does it go?

  • A cannon on a 50 meter cliff fires horizontally at 200 m/s. How far from the base does the projectile land?

  • Two cars crash: Car A (1500kg) at 30 m/s, Car B (1200kg) at 25 m/s. What's the total kinetic energy at impact?

Simulations (Requires Rapier Service)

  • Create a simulation of a ball dropping from 10 meters height and bouncing on the ground

  • Simulate 5 boxes stacked on top of each other, then have another box hit them from the side

  • Create a Newton's cradle with 5 spheres and record their motion

  • Simulate a sphere rolling down a 30-degree ramp


📝 Example Scripts

The examples/ directory contains working demonstration scripts:

Ready to Run (No External Services Required)

These examples use the built-in analytic provider and work immediately:

  • 00_quick_start.py - Quick demo of all 5 analytic tools

  • 01_simple_projectile.py - Cannonball trajectories, basketball shots, angle comparisons

  • 02_collision_detection.py - Car crashes, near misses, asteroid collisions

  • 03_force_energy_momentum.py - F=ma, kinetic energy, momentum conservation

  • 04_r3f_visualization.py - Generate React Three Fiber visualization data

# Run any example
python examples/00_quick_start.py
python examples/01_simple_projectile.py
# ... etc

Requires Rapier Service

These examples demonstrate rigid-body simulations and Phase 1 features. They need the Rapier service running:

  • 05_rapier_simulation.py - Bouncing balls, collisions, stacking boxes

  • 06_bounce_detection.py - Phase 1.1: Automatic bounce detection and energy analysis

  • 07_contact_events.py - Phase 1.2: Real-time contact tracking and collision events

  • 08_pendulum.py - Phase 1.3: Joints and constraints (pendulums, chains)

  • 09_phase1_complete.py - Phase 1.4: All Phase 1 features (damping, bounces, contacts, joints)

  • 10_fluid_dynamics.py - Phase 1.5: Fluid calculations (drag, buoyancy, terminal velocity)

  • 11_rotational_dynamics.py - Phase 2.1: Torque, angular momentum, gyroscopes

  • 12_oscillations.py - Phase 2.2: Springs, pendulums, harmonic motion, damping

  • 13_circular_motion.py - Phase 2.3: Orbital mechanics, centripetal force

  • 14_statics.py - Phase 2.6: Static equilibrium, force balance, beam analysis

  • 15_kinematics_analysis.py - Phase 2.7: Motion analysis, trajectory fitting

  • 16_roulette_simulation.py - 🎰 Casino Roulette - Complete showcase of multi-body physics, collisions, and energy dissipation

# Option 1: Use public Rapier service (easiest)
export RAPIER_SERVICE_URL=https://rapier.chukai.io
python examples/05_rapier_simulation.py
python examples/06_bounce_detection.py
# ... etc

# Option 2: Run local Rapier service with Docker
docker run -p 9000:9000 chuk-rapier-service
export RAPIER_SERVICE_URL=http://localhost:9000
python examples/05_rapier_simulation.py

Note:

  • Examples 00-04 work instantly (no external services)

  • Examples 05-09 showcase advanced rigid-body physics with Rapier

  • Use the public Rapier service at https://rapier.chukai.io or run your own


⚙️ Configuration

Environment Variables

# Provider selection
PHYSICS_PROVIDER=analytic          # or "rapier"

# Rapier service (only if using Rapier provider)
# The default is automatically determined:
# - On Fly.io: uses https://rapier.chukai.io (public service)
# - Locally: uses http://localhost:9000
#
# Override with:
RAPIER_SERVICE_URL=https://rapier.chukai.io  # or http://localhost:9000

# Optional configuration
RAPIER_TIMEOUT=30.0
RAPIER_MAX_RETRIES=3
RAPIER_RETRY_DELAY=1.0

YAML Configuration

Create physics.yaml in your working directory or ~/.config/chuk-mcp-physics/:

default_provider: rapier

providers:
  # Override provider per tool type
  simulations: rapier
  projectile_motion: analytic

rapier:
  # Public service (recommended)
  service_url: https://rapier.chukai.io

  # Or local development
  # service_url: http://localhost:9000

  timeout: 30.0
  max_retries: 3
  retry_delay: 1.0

🛡️ Safety & Limits

Understanding these limits helps prevent timeouts, instabilities, and confusion:

Parameter

Recommended

Maximum

Notes

Units

meters, kg, seconds

-

SI units throughout

dt

0.008 - 0.033

0.001 - 0.1

<0.008 = overkill, >0.033 = unstable

steps

100 - 5000

10,000

Depends on dt and complexity

bodies

1 - 100

1,000

Performance degrades >100

gravity

-20 to 0 m/s²

-100 to +100

Earth = -9.81

velocity

0 - 100 m/s

1,000 m/s

Very high speeds may cause tunneling

mass

0.1 - 10,000 kg

1e-6 - 1e6

Extreme ratios cause instability

Public Service Limits

The public Rapier service at https://rapier.chukai.io has these limits:

  • Max steps per call: 5,000

  • Max bodies per simulation: 100

  • Max concurrent simulations: 10 per IP

  • Request timeout: 30 seconds

  • Max simulation lifetime: 1 hour (auto-cleanup)

For larger simulations, run your own Rapier service (see RAPIER_SERVICE.md).

Common Pitfalls

❌ Simulation explodes or bodies fly away

Symptoms:

  • Bodies gain extreme velocities

  • Objects disappear from view

  • NaN values in positions

Causes:

  • dt too large for the forces involved

  • Very high mass ratios (1g object hitting 1000kg object)

  • Extreme initial velocities

Solutions:

  • Reduce dt to 0.008 or lower

  • Use more similar masses (within 2-3 orders of magnitude)

  • Limit initial velocities to <100 m/s

❌ Simulation runs very slowly

Symptoms:

  • Request takes >10 seconds

  • Timeout errors

  • High CPU usage

Causes:

  • Too many bodies (>100)

  • Very small dt (<0.005)

  • Complex mesh colliders

  • Too many steps (>5000)

Solutions:

  • Reduce body count or simplify shapes

  • Increase dt (balance accuracy vs. speed)

  • Break large step counts into multiple calls

  • Use primitive shapes (sphere, box) instead of meshes

❌ Objects tunnel through each other

Symptoms:

  • Fast-moving objects pass through walls

  • Collisions not detected

  • Objects appear inside each other

Causes:

  • Very high velocities + large dt

  • Thin colliders (<0.1m)

  • Disabled continuous collision detection (CCD)

Solutions:

  • Reduce dt for high-speed scenarios

  • Thicken colliders (minimum 0.1m recommended)

  • Reduce velocities

  • Enable CCD if available (future feature)

Best Practices

  1. Start simple: Test with 2-3 bodies before scaling up

  2. Validate inputs: Check for NaN, Inf, extreme values before simulation

  3. Monitor performance: Track step time, adjust dt/steps accordingly

  4. Cleanup: Always destroy_simulation when done (prevent memory leaks)

  5. Use analytic when possible: For simple scenarios, analytic is faster and exact


🔧 Development

# Navigate to project directory
cd chuk-mcp-physics

# Install in development mode
make dev-install

# Run tests
make test

# Run tests with coverage
make test-cov

# Format code
make format

# Run all checks
make check

# Build package
make build

🐳 Docker Deployment

# Build image
make docker-build

# Run container
make docker-run

# Access at http://localhost:8000

☁️ Production Deployment

Live Public Services

Current Production Services:

  • MCP Physics Server: https://physics.chukai.io/mcp

    • Public hosted MCP server endpoint

    • No local installation required

    • Pre-configured with Rapier service

    • Ready to use in Claude Desktop

  • Rapier Physics Engine: https://rapier.chukai.io

    • Public API for physics simulations

    • No authentication required for basic usage

    • Rate limits may apply

Quick Test:

# Test the public Rapier service
curl https://rapier.chukai.io/health

# Use with chuk-mcp-physics locally
export RAPIER_SERVICE_URL=https://rapier.chukai.io
uvx chuk-mcp-physics

Deploy Your Own Rapier Service

If you need your own private Rapier service instance:

1. Deploy Rapier Service to Fly.io with Redis

cd rapier-service

# Login to Fly.io
fly auth login

# Create Redis instance for distributed storage
fly redis create
# Choose: your-rapier-redis, region sjc (or your preferred region), plan 256MB

# Set Redis URL secret (use URL from previous step)
fly secrets set REDIS_URL="redis://default:password@fly-your-rapier-redis.upstash.io"

# Create and deploy the service
fly apps create your-rapier-physics
fly deploy

# Verify Redis connection in logs
fly logs
# Look for: "📦 Initialized RedisStorage backend"

# Add custom domain (optional)
fly certs add rapier.yourdomain.com -a your-rapier-physics

# Verify service is running
curl https://your-rapier-physics.fly.dev/health

Redis Benefits:

  • ✅ Horizontal scaling (multiple service instances share state)

  • ✅ Automatic cleanup (TTL-based simulation expiration)

  • ✅ Distributed coordination across instances

  • ✅ Session persistence within TTL window

Configuration: The service uses Redis automatically on Fly.io (see rapier-service/fly.toml). For local development, it defaults to in-memory storage.

See rapier-service/FLY_REDIS_SETUP.md for detailed Redis setup guide, monitoring, and troubleshooting.

2. Configure chuk-mcp-physics to Use Your Service

# Option 1: Environment variable
export RAPIER_SERVICE_URL=https://rapier.yourdomain.com
uvx chuk-mcp-physics

# Option 2: YAML config (physics.yaml)
# rapier:
#   service_url: https://rapier.yourdomain.com

Why deploy your own?

  • 🔒 Private instance for production workloads

  • 📈 Custom scaling and resource allocation

  • 🌍 Deploy closer to your users (different regions)

  • 💾 Persistent simulations and custom configurations

See DEPLOYMENT.md for complete deployment guide, scaling strategies, and CI/CD setup.


🦀 Rapier Service Setup

For full rigid-body simulations, you have several options:

Option 1: Use Public Service (Easiest)

# No setup required - just configure the URL
export RAPIER_SERVICE_URL=https://rapier.chukai.io
uvx chuk-mcp-physics

Option 2: Run Locally with Docker

# Using Docker
docker run -p 9000:9000 chuk-rapier-service

# Configure to use local service
export RAPIER_SERVICE_URL=http://localhost:9000
uvx chuk-mcp-physics

Option 3: Build from Source

# Build and run the Rust service
cd rapier-service
cargo run --release

# In another terminal
export RAPIER_SERVICE_URL=http://localhost:9000
uvx chuk-mcp-physics

See RAPIER_SERVICE.md for:

  • Complete API specification

  • Rust implementation guide

  • Docker deployment details

  • Testing examples


📊 Comparison: Analytic vs Rapier

Feature

Analytic Provider

Rapier Provider

Projectile motion

✅ Exact (kinematic eqs)

✅ Simulated

Simple collisions

✅ Exact (sphere-sphere, elastic)

✅ Simulated

Force/energy/momentum

✅ F=ma, KE, PE, momentum, work/power

✅ Can derive from sim

Fluid dynamics

✅ Drag, buoyancy, terminal velocity

❌ Not supported

Rigid-body dynamics

❌ Not supported

✅ Full 3D/2D physics

Complex shapes

❌ Spheres only

✅ Box, capsule, mesh, etc

Friction/restitution

❌ Not modeled

✅ Full material properties

Multi-body systems

❌ Not supported

✅ Unlimited bodies

Constraints/joints

❌ Not supported

✅ Hinges, sliders, etc

Performance

⚡ Instant

🐇 Fast (Rust)

Setup

📦 Built-in

🦀 Requires Rapier service

Recommendation:

  • Use Analytic for simple calculations, education, quick answers

  • Use Rapier for complex simulations, games, visualizations, multi-body dynamics


🤝 Contributing

Contributions welcome! Please see CONTRIBUTING.md.


📄 License

Apache License 2.0 - see LICENSE for details.

This is a demonstration project provided as-is for learning and testing purposes.


🙏 Acknowledgments


📚 See Also

Available Tools

62 tools
add_jointA

Add a joint/constraint to connect two rigid bodies.

Joints allow you to constrain the motion between bodies:
- FIXED: Rigid connection (glue objects together)
- REVOLUTE: Hinge rotation around an axis (doors, pendulums)
- SPHERICAL: Ball-and-socket rotation (ragdolls, gimbals)
- PRISMATIC: Sliding along an axis (pistons, elevators)

Args:
    sim_id: Simulation identifier
    joint: Joint definition with type and parameters

Returns:
    joint_id: Unique identifier for the created joint

Example - Simple Pendulum:
    # Create fixed anchor point
    add_rigid_body(
        sim_id=sim_id,
        body_id="anchor",
        body_type="static",
        shape="sphere",
        size=[0.05],
        position=[0.0, 5.0, 0.0],
    )

    # Create pendulum bob
    add_rigid_body(
        sim_id=sim_id,
        body_id="bob",
        body_type="dynamic",
        shape="sphere",
        size=[0.1],
        mass=1.0,
        position=[0.0, 3.0, 0.0],
    )

    # Connect with revolute joint (hinge)
    add_joint(
        sim_id=sim_id,
        joint=JointDefinition(
            id="pendulum_joint",
            joint_type="revolute",
            body_a="anchor",
            body_b="bob",
            anchor_a=[0.0, 0.0, 0.0],  # Center of anchor
            anchor_b=[0.0, 0.1, 0.0],   # Top of bob
            axis=[0.0, 0.0, 1.0],        # Rotate around Z-axis
        ),
    )

Inputs: sim_id (required), joint (required).

add_rigid_bodyA

Add a rigid body to an existing simulation.

Creates a new physics body (static, dynamic, or kinematic) with specified
shape, mass, and initial conditions. Bodies interact via collisions.

Args:
    sim_id: Simulation ID from create_simulation
    body_id: Unique identifier for this body (user-defined string)
    body_type: "static", "dynamic", or "kinematic"
        - static: Never moves (ground, walls)
        - dynamic: Affected by forces and collisions
        - kinematic: Moves but not affected by forces (scripted motion)
    shape: Collider shape: "box", "sphere", "capsule", "cylinder", "plane"
    size: Shape dimensions:
        - box: [width, height, depth]
        - sphere: [radius]
        - capsule: [half_height, radius]
        - cylinder: [half_height, radius]
        - plane: not needed (use normal/offset instead)
    mass: Mass in kilograms (for dynamic bodies). Default 1.0
    normal: Normal vector [x, y, z] for plane shape. Default [0, 1, 0] (upward)
    offset: Offset along normal for plane. Default 0.0
    position: Initial position [x, y, z]. Default [0, 0, 0]
    orientation: Initial orientation quaternion [x, y, z, w]. Default [0, 0, 0, 1] (identity)
    velocity: Initial linear velocity [x, y, z]. Default [0, 0, 0]
    angular_velocity: Initial angular velocity [x, y, z]. Default [0, 0, 0]
    restitution: Bounciness (0.0 = no bounce, 1.0 = perfect bounce). Default 0.5
    friction: Surface friction (0.0 = ice, 1.0 = rubber). Default 0.5
    is_sensor: If true, detects collisions but doesn't respond physically. Default false
    linear_damping: Linear velocity damping (0.0-1.0) - like air resistance. Default 0.0
    angular_damping: Angular velocity damping (0.0-1.0) - like rotational friction. Default 0.0
    drag_coefficient: Base drag coefficient (Cd) for orientation-dependent drag. Optional
    drag_area: Reference cross-sectional area (m²) for drag calculation. Optional
    drag_axis_ratios: Drag variation along body axes [x, y, z]. E.g., [1.0, 0.2, 1.0] for streamlined along Y. Optional
    fluid_density: Fluid density (kg/m³). Air=1.225, Water=1000. Default 1.225

Returns:
    body_id (echo of the input ID)

Tips for LLMs:
    - Create ground FIRST: body_type="static", shape="plane", normal=[0, 1, 0]
    - Box size is full width/height/depth (not half-extents)
    - Sphere size is [radius] (array with one element)
    - Quaternions: identity = [0, 0, 0, 1] (no rotation)
    - Common restitution: steel=0.8, wood=0.5, clay=0.1
    - Common friction: ice=0.05, wood=0.4, rubber=1.0

Example:
    # Add a ground plane
    await add_rigid_body(
        sim_id=sim_id,
        body_id="ground",
        body_type="static",
        shape="plane",
        normal=[0, 1, 0]
    )

    # Add a bouncing ball
    await add_rigid_body(
        sim_id=sim_id,
        body_id="ball",
        body_type="dynamic",
        shape="sphere",
        size=[0.5],  # radius = 0.5m
        mass=1.0,
        position=[0, 10, 0],
        restitution=0.7
    )

    # Add a falling box
    await add_rigid_body(
        sim_id=sim_id,
        body_id="box",
        body_type="dynamic",
        shape="box",
        size=[1.0, 1.0, 1.0],
        mass=10.0,
        position=[0.0, 5.0, 0.0]
    )

Inputs: sim_id (required), body_id (required), body_type (required), shape (required), +17 more.

analyze_circular_orbitA

Analyze circular orbit at given altitude above planet surface.

Comprehensive orbital analysis combining period, velocity, and acceleration.

Args:
    altitude: Altitude above surface in meters
    planet_mass: Planet mass in kg
    planet_radius: Planet radius in meters
    gravitational_constant: G in m³/(kg⋅s²) (default 6.674e-11)

Returns:
    Dict containing:
        - orbital_radius: r from planet center in meters
        - orbital_velocity: v in m/s
        - period_seconds: Orbital period in seconds
        - period_minutes: Orbital period in minutes
        - centripetal_acceleration: a_c in m/s²

Example - LEO satellite at 400km altitude:
    result = await analyze_circular_orbit(
        altitude=400000,  # 400 km
        planet_mass=5.972e24,  # Earth
        planet_radius=6.371e6  # Earth
    )
    # v ≈ 7,670 m/s, T ≈ 92.6 min

Inputs: altitude (required), planet_mass (required), planet_radius (required), gravitational_constant.

calculate_acceleration_from_positionA

Calculate acceleration by numerical differentiation of position data.

Uses central differences for numerical differentiation:
v[i] ≈ (r[i+1] - r[i-1]) / (2Δt)
a[i] ≈ (v[i+1] - v[i-1]) / (2Δt)

Args:
    times: Time values in seconds (or JSON string)
    positions: Position vectors [[x,y,z], ...] in meters (or JSON string)

Returns:
    Dict containing:
        - velocities: Velocity vectors [[x,y,z], ...] in m/s
        - accelerations: Acceleration vectors [[x,y,z], ...] in m/s²
        - average_velocity: Average velocity [x,y,z] in m/s
        - average_acceleration: Average acceleration [x,y,z] in m/s²

Example - Analyze recorded position data:
    result = await calculate_acceleration_from_position(
        times=[0, 1, 2, 3],
        positions=[[0,0,0], [5,0,0], [10,0,0], [15,0,0]]
    )

Inputs: times (required), positions (required).

calculate_angular_accelerationA

Calculate angular acceleration: α = τ / I.

Angular acceleration is the rotational equivalent of linear acceleration.
Determined by net torque and moment of inertia.

Args:
    torque: Torque magnitude in N⋅m
    moment_of_inertia: Moment of inertia in kg⋅m²

Returns:
    Dict containing:
        - angular_acceleration: α in rad/s²

Tips for LLMs:
    - Rotational version of F = ma → τ = Iα
    - Larger I means slower angular acceleration for same torque
    - Use for: motor acceleration, spinning up flywheels

Example - Motor accelerating a wheel:
    result = await calculate_angular_acceleration(
        torque=10.0,  # N⋅m
        moment_of_inertia=0.5  # kg⋅m²
    )
    # α = 20 rad/s²

Inputs: torque (required), moment_of_inertia (required).

calculate_angular_momentumA

Calculate angular momentum: L = I × ω.

Angular momentum is the rotational equivalent of linear momentum.
It's conserved in the absence of external torques (like ice skater spinning).

Args:
    moment_of_inertia: Moment of inertia in kg⋅m²
    angular_velocity_x: X component of angular velocity in rad/s
    angular_velocity_y: Y component of angular velocity in rad/s
    angular_velocity_z: Z component of angular velocity in rad/s

Returns:
    Dict containing:
        - angular_momentum: L vector [x, y, z] in kg⋅m²/s
        - magnitude: L magnitude in kg⋅m²/s

Tips for LLMs:
    - Angular momentum is conserved when no external torques act
    - Ice skater pulls arms in → I decreases → ω increases (L constant)
    - Gyroscopes resist changes in angular momentum direction

Example - Spinning figure skater:
    # Arms extended: I = 3.0 kg⋅m², ω = 5 rad/s
    result = await calculate_angular_momentum(
        moment_of_inertia=3.0,
        angular_velocity_x=0.0,
        angular_velocity_y=5.0,
        angular_velocity_z=0.0
    )
    # L = 15 kg⋅m²/s (conserved when arms pulled in)

Inputs: moment_of_inertia (required), angular_velocity_x (required), angular_velocity_y (required), angular_velocity_z (required).

calculate_average_speedA

Calculate average speed along a path.

Average speed = total distance / total time
(Distance is path length, not displacement)

Args:
    positions: Position vectors [[x,y,z], ...] in meters (or JSON string)
    times: Time values in seconds (or JSON string)

Returns:
    Dict containing:
        - average_speed: Average speed in m/s
        - total_distance: Total path length in meters
        - total_time: Total elapsed time in seconds
        - displacement_magnitude: Straight-line displacement in meters
        - displacement: Displacement vector [x,y,z] in meters

Example - Car on winding road:
    result = await calculate_average_speed(
        positions=[[0,0,0], [10,5,0], [20,10,0], [15,20,0]],
        times=[0, 10, 20, 30]
    )

Inputs: positions (required), times (required).

calculate_banking_angleA

Calculate ideal banking angle: θ = arctan(v² / (rg)).

For a banked curve, the ideal angle where no friction is needed
to maintain the turn at a given speed.

Args:
    velocity: Speed in m/s
    radius: Turn radius in meters
    gravity: Gravitational acceleration in m/s² (default 9.81)

Returns:
    Dict containing:
        - angle_radians: Banking angle in radians
        - angle_degrees: Banking angle in degrees

Tips for LLMs:
    - Faster speed → steeper banking angle
    - Tighter turn → steeper banking angle
    - NASCAR tracks banked ~30° for high-speed turns
    - At ideal angle, normal force provides all centripetal force

Example - Highway exit ramp:
    result = await calculate_banking_angle(
        velocity=25,  # m/s (90 km/h)
        radius=100  # meter radius turn
    )
    # θ ≈ 32.5°

Inputs: velocity (required), radius (required), gravity.

calculate_beam_reactionsA

Calculate reaction forces for a simply supported beam.

Uses moment equilibrium about supports to find reaction forces.

Args:
    beam_length: Beam length in meters
    loads: Point loads in Newtons (downward positive) (or JSON string)
    load_positions: Positions of loads from left end in meters (or JSON string)

Returns:
    Dict containing:
        - reaction_left: Reaction force at left support in Newtons
        - reaction_right: Reaction force at right support in Newtons
        - total_load: Total downward load in Newtons
        - is_balanced: Whether reactions balance loads

Example - Beam with two loads:
    result = await calculate_beam_reactions(
        beam_length=10.0,
        loads=[1000, 500],
        load_positions=[3.0, 7.0]
    )

Inputs: beam_length (required), loads (required), load_positions (required).

calculate_bernoulliA

Calculate Bernoulli's equation: P + (1/2)ρv² + ρgh = constant.

Energy conservation for flowing fluids.

Args:
    pressure1: Pressure at point 1 in Pascals
    velocity1: Flow velocity at point 1 in m/s
    height1: Height at point 1 in meters
    velocity2: Flow velocity at point 2 in m/s (optional)
    height2: Height at point 2 in meters (optional)
    fluid_density: Fluid density in kg/m³ (default 1000 for water)
    gravity: Gravitational acceleration in m/s² (default 9.81)

Returns:
    Dict containing:
        - total_pressure_1: Total pressure at point 1
        - static_pressure_1: Static pressure component
        - dynamic_pressure_1: Dynamic pressure component
        - hydrostatic_pressure_1: Hydrostatic pressure component
        - pressure2: Pressure at point 2 (if velocity2/height2 given)

Example - Water tank with outlet:
    result = await calculate_bernoulli(
        pressure1=101325,  # Atmospheric at top
        velocity1=0,  # Still water
        height1=10,  # 10m height
        velocity2=14,  # Exit velocity
        height2=0,  # Ground level
        fluid_density=1000
    )

Inputs: pressure1 (required), velocity1 (required), height1 (required), velocity2, +3 more.

calculate_buoyancyA

Calculate buoyancy force using Archimedes' principle.

The buoyant force equals the weight of displaced fluid:
    F_b = ρ_fluid * V_submerged * g

Args:
    volume: Object volume in m³
    fluid_density: Fluid density in kg/m³ (water=1000, air=1.225)
    gravity: Gravitational acceleration in m/s² (default 9.81)
    submerged_fraction: Fraction submerged 0.0-1.0 (default 1.0 = fully submerged)

Returns:
    Buoyant force (upward) and displaced mass

Example - Checking if a 1kg ball will float:
    # 10cm diameter sphere: V = (4/3)πr³ = 0.000524 m³
    result = await calculate_buoyancy(
        volume=0.000524,
        fluid_density=1000  # water
    )
    # buoyant_force = 5.14 N
    # If weight (mg) < buoyant force, it floats
    # 1kg * 9.81 = 9.81 N > 5.14 N, so it sinks

Inputs: volume (required), fluid_density (required), gravity, submerged_fraction.

calculate_center_of_massA

Calculate center of mass for a system of point masses.

Formula: r_cm = Σ(m_i × r_i) / Σm_i

Args:
    masses: List of masses in kg (or JSON string)
    positions: List of positions [[x,y,z], ...] in meters (or JSON string)

Returns:
    Dict containing:
        - center_of_mass: Position [x, y, z] in meters
        - total_mass: Total system mass in kg

Example - Three-mass system:
    result = await calculate_center_of_mass(
        masses=[1.0, 2.0, 3.0],
        positions=[[0,0,0], [1,0,0], [2,0,0]]
    )
    # center_of_mass ≈ [1.5, 0, 0]

Inputs: masses (required), positions (required).

calculate_centripetal_forceA

Calculate centripetal force: F_c = m v² / r.

Force required to keep an object moving in a circle.
Always points toward the center of the circular path.

Args:
    mass: Mass in kg
    velocity: Speed (velocity magnitude) in m/s
    radius: Radius of circular path in meters

Returns:
    Dict containing:
        - centripetal_force: F_c in Newtons
        - centripetal_acceleration: a_c in m/s²

Tips for LLMs:
    - Not a new force - it's the net inward force (tension, friction, gravity)
    - Faster speed → much more force needed (v² relationship)
    - Tighter turn → more force needed
    - Use for: car turns, satellite orbits, centrifuges

Example - Car turning:
    result = await calculate_centripetal_force(
        mass=1500,  # kg
        velocity=20,  # m/s (72 km/h)
        radius=50  # meter turn radius
    )
    # F_c = 12000 N (provided by friction between tires and road)

Inputs: mass (required), velocity (required), radius (required).

calculate_damped_oscillationA

Calculate damped oscillation with friction/resistance.

Real oscillators lose energy over time due to damping (air resistance,
friction). Three regimes: underdamped, critically damped, overdamped.

Args:
    mass: Mass in kg
    spring_constant: k in N/m
    damping_coefficient: b in kg/s (damping strength)
    time: Time t in seconds
    initial_position: Initial position in meters (default 1.0)
    initial_velocity: Initial velocity in m/s (default 0.0)

Returns:
    Dict containing:
        - position: x(t) in meters
        - velocity: v(t) in m/s
        - damping_ratio: ζ (zeta) = b/(2√(mk))
        - regime: "underdamped", "critically_damped", or "overdamped"

Damping regimes:
    - ζ < 1: Underdamped (oscillates, gradually decays)
    - ζ = 1: Critically damped (returns fastest without oscillating)
    - ζ > 1: Overdamped (slow return, no oscillation)

Example - Car suspension:
    result = await calculate_damped_oscillation(
        mass=300,  # kg (quarter car mass)
        spring_constant=20000,  # N/m
        damping_coefficient=2000,  # kg/s
        time=1.0
    )
    # Should be slightly underdamped for comfort

Inputs: mass (required), spring_constant (required), damping_coefficient (required), time (required), +2 more.

calculate_drag_forceA

Calculate drag force for an object moving through a fluid.

The drag force opposes motion and is given by:
    F_drag = 0.5 * ρ * v² * C_d * A

Common drag coefficients:
    - Sphere: 0.47
    - Streamlined shape: 0.04
    - Flat plate (perpendicular): 1.28
    - Human (standing): 1.0-1.3
    - Car: 0.25-0.35

Args:
    velocity: Velocity vector [x, y, z] in m/s (or JSON string)
    cross_sectional_area: Area perpendicular to flow in m²
    fluid_density: Fluid density in kg/m³ (water=1000, air=1.225)
    drag_coefficient: Drag coefficient (default 0.47 for sphere)
    viscosity: Dynamic viscosity in Pa·s (water=1.002e-3, air=1.825e-5, oil=0.1).
        If not provided, estimated from fluid_density for Reynolds number calculation.

Returns:
    Drag force vector, magnitude, and Reynolds number

Example - Ball falling through water:
    result = await calculate_drag_force(
        velocity=[0, -5.0, 0],
        cross_sectional_area=0.00785,  # π * (0.05m)² for 10cm diameter
        fluid_density=1000,  # water
        drag_coefficient=0.47,
        viscosity=1.002e-3  # water viscosity for accurate Reynolds number
    )
    # Returns upward drag force opposing downward motion

Example - Ball falling through motor oil:
    result = await calculate_drag_force(
        velocity=[0, -2.0, 0],
        cross_sectional_area=0.00785,
        fluid_density=900,  # oil
        drag_coefficient=0.47,
        viscosity=0.1  # motor oil is much more viscous
    )

Inputs: velocity (required), cross_sectional_area (required), fluid_density (required), drag_coefficient, +1 more.

calculate_elastic_collisionA

Calculate final velocities after a 1D elastic collision.

Uses conservation of momentum and energy to solve for final velocities.
Assumes perfectly elastic collision (no energy loss).

Args:
    mass1: Mass of first object in kg
    velocity1: Initial velocity of first object in m/s (1D)
    mass2: Mass of second object in kg
    velocity2: Initial velocity of second object in m/s (1D)

Returns:
    Dict containing:
        - final_velocity1: Final velocity of object 1 in m/s
        - final_velocity2: Final velocity of object 2 in m/s
        - initial_kinetic_energy: Total KE before (J)
        - final_kinetic_energy: Total KE after (J) - should equal initial
        - initial_momentum: Total momentum before (kg⋅m/s)
        - final_momentum: Total momentum after (kg⋅m/s) - should equal initial

Example - Pool ball collision:
    result = await calculate_elastic_collision(
        mass1=0.17,      # kg (pool ball)
        velocity1=2.0,   # m/s (moving right)
        mass2=0.17,      # kg (pool ball)
        velocity2=0.0    # m/s (stationary)
    )
    # Result: ball 1 stops, ball 2 moves at 2.0 m/s

Inputs: mass1 (required), velocity1 (required), mass2 (required), velocity2 (required).

calculate_elastic_collision_3dA

Calculate 3D elastic collision (perfect energy conservation).

Special case of collision where no kinetic energy is lost (e = 1.0).
Both momentum and energy are conserved.

Args:
    mass1: Mass of object 1 in kg
    velocity1: Velocity of object 1 [x, y, z] in m/s (or JSON string)
    mass2: Mass of object 2 in kg
    velocity2: Velocity of object 2 [x, y, z] in m/s (or JSON string)

Returns:
    Dict containing:
        - final_velocity1: Final velocity [x, y, z] in m/s
        - final_velocity2: Final velocity [x, y, z] in m/s
        - initial_momentum: Total momentum [x, y, z]
        - final_momentum: Total momentum [x, y, z]
        - initial_kinetic_energy: Total KE in Joules
        - final_kinetic_energy: Total KE in Joules

Tips for LLMs:
    - Ideal approximation for billiard balls, Newton's cradle
    - Both momentum and energy conserved
    - Equal masses + head-on → velocities exchange
    - Use for educational examples, idealized systems

Example - Pool balls:
    result = await calculate_elastic_collision_3d(
        mass1=0.17,  # kg (pool ball)
        velocity1=[2, 0, 0],  # 2 m/s
        mass2=0.17,  # kg
        velocity2=[0, 0, 0]  # stationary
    )
    # Result: ball 1 stops, ball 2 moves at 2 m/s

Inputs: mass1 (required), velocity1 (required), mass2 (required), velocity2 (required).

calculate_escape_velocityA

Calculate escape velocity: v_escape = √(2GM/r).

Minimum speed needed to escape a celestial body's gravitational pull.
Independent of the escaping object's mass.

Args:
    mass: Mass of celestial body in kg
    radius: Radius of celestial body in meters
    gravitational_constant: G in m³/(kg⋅s²) (default 6.674e-11)

Returns:
    Dict containing:
        - escape_velocity: v_escape in m/s
        - escape_velocity_kmh: v_escape in km/h (for convenience)

Tips for LLMs:
    - Earth: v_escape ≈ 11,200 m/s (40,320 km/h)
    - Moon: v_escape ≈ 2,380 m/s
    - Sun: v_escape ≈ 617,500 m/s
    - Independent of escape direction or mass of escaping object

Example - Earth escape velocity:
    result = await calculate_escape_velocity(
        mass=5.972e24,  # Earth mass (kg)
        radius=6.371e6  # Earth radius (meters)
    )
    # v_escape ≈ 11,186 m/s

Inputs: mass (required), radius (required), gravitational_constant.

calculate_forceA

Calculate force from mass and acceleration using Newton's Second Law (F = ma).

Computes the force vector required to produce a given acceleration on a mass.
Fundamental for dynamics, engineering, and understanding motion.

Args:
    mass: Mass in kilograms (must be positive)
    acceleration_x: X component of acceleration in m/s²
    acceleration_y: Y component of acceleration in m/s²
    acceleration_z: Z component of acceleration in m/s²

Returns:
    ForceCalculationResponse containing:
        - force: Force vector [x, y, z] in Newtons
        - magnitude: Force magnitude in Newtons

Tips for LLMs:
    - 1 Newton = force to accelerate 1 kg at 1 m/s²
    - On Earth, weight force = mass × 9.81 N (vertical)
    - Use magnitude to compare total force regardless of direction
    - Common accelerations: car braking ~10 m/s², elevator ~2 m/s²

Example:
    # Force to accelerate a 1500kg car at 3 m/s² forward
    result = await calculate_force(
        mass=1500.0,
        acceleration_x=3.0,
        acceleration_y=0.0,
        acceleration_z=0.0
    )
    print(f"Required force: {result.magnitude:.0f} N")

Inputs: mass (required), acceleration_x (required), acceleration_y (required), acceleration_z (required).

calculate_hookes_lawA

Calculate spring force using Hooke's Law: F = -kx.

The restoring force is proportional to displacement from equilibrium.
Fundamental for springs, elastic materials, and simple harmonic motion.

Args:
    spring_constant: Spring constant k in N/m (stiffness)
    displacement: Displacement from equilibrium in meters

Returns:
    Dict containing:
        - force: Restoring force magnitude in Newtons
        - potential_energy: Elastic potential energy in Joules

Tips for LLMs:
    - Stiffer spring → larger k → more force for same displacement
    - Potential energy stored in spring: PE = (1/2)kx²
    - Negative sign in F = -kx means force opposes displacement

Example - Compressing a car spring:
    result = await calculate_hookes_law(
        spring_constant=10000,  # N/m (stiff car spring)
        displacement=0.05  # 5cm compression
    )
    # Force = 500 N, PE = 12.5 J

Inputs: spring_constant (required), displacement (required).

calculate_inelastic_collision_3dA

Calculate 3D collision with coefficient of restitution.

Models realistic collisions where some kinetic energy is lost.
Coefficient of restitution (e) determines how much energy is retained.

Args:
    mass1: Mass of object 1 in kg
    velocity1: Velocity of object 1 [x, y, z] in m/s (or JSON string)
    mass2: Mass of object 2 in kg
    velocity2: Velocity of object 2 [x, y, z] in m/s (or JSON string)
    coefficient_of_restitution: e (0.0 = perfectly inelastic, 1.0 = perfectly elastic)

Returns:
    Dict containing:
        - final_velocity1: Final velocity [x, y, z] in m/s
        - final_velocity2: Final velocity [x, y, z] in m/s
        - initial_momentum: Total initial momentum [x, y, z]
        - final_momentum: Total final momentum [x, y, z]
        - initial_kinetic_energy: Total initial KE in Joules
        - final_kinetic_energy: Total final KE in Joules
        - energy_loss: Energy lost in Joules
        - energy_loss_percent: % of energy lost

Coefficient of restitution values:
    - e = 0.0: Perfectly inelastic (clay, putty) - objects stick
    - e = 0.5: Very inelastic (wet clay)
    - e = 0.7: Moderately elastic (basketball)
    - e = 0.9: Highly elastic (Super Ball)
    - e = 1.0: Perfectly elastic (ideal, no energy loss)

Tips for LLMs:
    - Momentum is always conserved (regardless of e)
    - Energy lost = (1 - e²) × initial KE in center-of-mass frame
    - Use e=1.0 for billiard balls, e=0.0 for car crashes

Example - Car crash:
    result = await calculate_inelastic_collision_3d(
        mass1=1500,  # kg
        velocity1=[20, 0, 0],  # 20 m/s east
        mass2=1200,  # kg
        velocity2=[-15, 0, 0],  # 15 m/s west
        coefficient_of_restitution=0.1  # very inelastic
    )
    # Massive energy loss, objects nearly stick together

Inputs: mass1 (required), velocity1 (required), mass2 (required), velocity2 (required), +1 more.

calculate_instantaneous_velocityA

Calculate instantaneous velocity at a specific time.

Uses interpolation if target_time is between data points,
otherwise uses numerical differentiation.

Args:
    positions: Position vectors [[x,y,z], ...] in meters (or JSON string)
    times: Time values in seconds (or JSON string)
    target_time: Time at which to calculate velocity in seconds

Returns:
    Dict containing:
        - velocity: Velocity vector [x,y,z] in m/s
        - speed: Speed magnitude in m/s
        - interpolated: Whether interpolation was used
        - time: Target time (echo)

Example:
    result = await calculate_instantaneous_velocity(
        positions=[[0,0,0], [3,4,0], [6,8,0]],
        times=[0, 1, 2],
        target_time=1.0
    )
    # speed = 5 m/s

Inputs: positions (required), times (required), target_time (required).

calculate_jerkA

Calculate jerk (rate of change of acceleration).

Jerk = da/dt is important for comfort in vehicles and mechanical design.

Args:
    times: Time values in seconds (or JSON string)
    accelerations: Acceleration vectors [[x,y,z], ...] in m/s² (or JSON string)

Returns:
    Dict containing:
        - jerks: Jerk vectors [[x,y,z], ...] in m/s³
        - average_jerk: Average jerk [x,y,z] in m/s³
        - max_jerk_magnitude: Maximum jerk magnitude in m/s³

Example:
    result = await calculate_jerk(
        times=[0, 1, 2, 3],
        accelerations=[[0,0,0], [2,0,0], [4,0,0], [6,0,0]]
    )
    # jerk_x ≈ 2 m/s³ (constant)

Inputs: times (required), accelerations (required).

calculate_kinetic_energyA

Calculate kinetic energy from mass and velocity (KE = ½mv²).

Computes the energy of motion for a moving object. Energy is scalar
(direction doesn't matter, only speed). Useful for collision analysis,
vehicle safety, and understanding energy transfer.

Args:
    mass: Mass in kilograms (must be positive)
    velocity_x: X component of velocity in m/s
    velocity_y: Y component of velocity in m/s
    velocity_z: Z component of velocity in m/s

Returns:
    KineticEnergyResponse containing:
        - kinetic_energy: Energy in Joules (J)
        - speed: Velocity magnitude in m/s

Tips for LLMs:
    - 1 Joule = 1 kg⋅m²/s² = energy to lift 102g by 1m on Earth
    - Kinetic energy doubles mass → doubles energy, doubles speed → 4× energy
    - Car at highway speed (~30 m/s, 1500 kg) ≈ 675,000 J
    - Use to compare impact severity or stopping distances

Example:
    # Energy of a 0.145kg baseball at 40 m/s
    result = await calculate_kinetic_energy(
        mass=0.145,
        velocity_x=40.0,
        velocity_y=0.0,
        velocity_z=0.0
    )
    print(f"Kinetic energy: {result.kinetic_energy:.1f} J")

Inputs: mass (required), velocity_x (required), velocity_y (required), velocity_z (required).

calculate_lift_forceA

Calculate lift force using: L = (1/2) ρ v² C_L A.

Based on Bernoulli's principle and wing aerodynamics.

Args:
    velocity: Flow velocity in m/s
    wing_area: Wing area in m²
    lift_coefficient: Lift coefficient C_L (dimensionless)
    fluid_density: Fluid density in kg/m³ (air=1.225)

Returns:
    Dict containing:
        - lift_force: Lift force in Newtons
        - dynamic_pressure: Dynamic pressure (q) in Pascals

Example - Aircraft wing:
    result = await calculate_lift_force(
        velocity=70,  # m/s (~250 km/h)
        wing_area=20.0,  # m²
        lift_coefficient=1.2,
        fluid_density=1.225
    )

Inputs: velocity (required), wing_area (required), lift_coefficient (required), fluid_density.

calculate_magnus_forceA

Calculate Magnus force on a spinning ball.

The Magnus force is perpendicular to both velocity and spin axis.
Causes curve balls in sports.

Args:
    velocity: Ball velocity [x, y, z] in m/s (or JSON string)
    angular_velocity: Angular velocity [x, y, z] in rad/s (or JSON string)
    radius: Ball radius in meters
    fluid_density: Fluid density in kg/m³ (air=1.225)

Returns:
    Dict containing:
        - magnus_force: Magnus force vector [x, y, z] in Newtons
        - magnus_force_magnitude: Force magnitude in Newtons
        - spin_rate: Spin rate (angular velocity magnitude) in rad/s

Example - Soccer ball curve:
    result = await calculate_magnus_force(
        velocity=[20, 0, 0],  # 20 m/s forward
        angular_velocity=[0, 0, 50],  # 50 rad/s topspin
        radius=0.11,  # Soccer ball
        fluid_density=1.225
    )

Inputs: velocity (required), angular_velocity (required), radius (required), fluid_density.

calculate_moment_of_inertiaA

Calculate moment of inertia for various shapes.

Moment of inertia (I) is the rotational equivalent of mass. It determines
how difficult it is to change an object's rotation. Depends on both mass
distribution and rotation axis.

Args:
    shape: Shape type - "sphere", "solid_sphere", "hollow_sphere", "rod", "disk", "cylinder", "box"
    mass: Mass in kilograms
    radius: Radius for sphere/disk/cylinder (meters)
    length: Length for rod (meters)
    width: Width for box (meters)
    height: Height for box/cylinder (meters)
    depth: Depth for box (meters)
    axis: Rotation axis - "center", "end" (for rod), "x", "y", "z" (for box)

Returns:
    Dict containing:
        - moment_of_inertia: I in kg⋅m²
        - shape: Shape type
        - axis: Rotation axis

Common formulas:
    - Solid sphere (center): I = (2/5)mr²
    - Hollow sphere (center): I = (2/3)mr²
    - Rod (center): I = (1/12)mL²
    - Rod (end): I = (1/3)mL²
    - Disk (center): I = (1/2)mr²

Example - Spinning wheel:
    result = await calculate_moment_of_inertia(
        shape="disk",
        mass=5.0,  # 5kg wheel
        radius=0.3  # 30cm radius
    )
    # I = 0.225 kg⋅m²

Inputs: shape (required), mass (required), radius, length, +4 more.

calculate_momentumA

Calculate momentum from mass and velocity (p = mv).

Computes the momentum vector, which represents "quantity of motion."
Momentum is conserved in collisions, making it crucial for analyzing
impacts, explosions, and rocket propulsion.

Args:
    mass: Mass in kilograms (must be positive)
    velocity_x: X component of velocity in m/s
    velocity_y: Y component of velocity in m/s
    velocity_z: Z component of velocity in m/s

Returns:
    MomentumResponse containing:
        - momentum: Momentum vector [x, y, z] in kg⋅m/s
        - magnitude: Momentum magnitude in kg⋅m/s

Tips for LLMs:
    - Momentum is a vector (has direction), unlike kinetic energy
    - Total momentum before collision = total momentum after (conservation)
    - Large mass × small velocity can equal small mass × large velocity
    - Use to analyze: collisions, recoil, rocket thrust

Example:
    # Momentum of a 70kg person running at 5 m/s
    result = await calculate_momentum(
        mass=70.0,
        velocity_x=5.0,
        velocity_y=0.0,
        velocity_z=0.0
    )
    print(f"Momentum: {result.magnitude:.1f} kg⋅m/s")

Inputs: mass (required), velocity_x (required), velocity_y (required), velocity_z (required).

calculate_normal_forceA

Calculate normal force on an inclined plane.

On an incline at angle θ:
- N = mg cos(θ) + F_additional
- Weight component perpendicular: mg cos(θ)
- Weight component parallel: mg sin(θ)

Args:
    mass: Object mass in kg
    gravity: Gravitational acceleration in m/s² (default 9.81)
    angle_degrees: Incline angle in degrees (0 = horizontal)
    additional_force: Additional perpendicular force in Newtons (optional)

Returns:
    Dict containing:
        - normal_force: Normal force in Newtons
        - weight_component_perpendicular: Weight component ⊥ to surface
        - weight_component_parallel: Weight component ∥ to surface

Example - Box on 30° ramp:
    result = await calculate_normal_force(
        mass=10.0,
        angle_degrees=30.0
    )
    # normal_force ≈ 84.9 N

Inputs: mass (required), gravity, angle_degrees, additional_force.

calculate_orbital_periodA

Calculate orbital period: T = 2π√(r³/GM).

Kepler's Third Law for circular orbits. Period depends on orbital
radius and central body mass.

Args:
    orbital_radius: Orbital radius in meters (from center of central body)
    central_mass: Mass of central body in kg
    gravitational_constant: G in m³/(kg⋅s²) (default 6.674e-11)

Returns:
    Dict containing:
        - period: Orbital period in seconds
        - orbital_velocity: v in m/s
        - period_hours: Period in hours (for convenience)
        - period_days: Period in days (for convenience)

Tips for LLMs:
    - Higher orbit → longer period
    - More massive central body → shorter period
    - Earth: M = 5.972e24 kg, R = 6.371e6 m
    - Moon orbit: r ≈ 384,400 km, T ≈ 27.3 days
    - ISS orbit: r ≈ 6,771 km (altitude 400 km), T ≈ 90 minutes

Example - ISS orbit:
    result = await calculate_orbital_period(
        orbital_radius=6.771e6,  # meters
        central_mass=5.972e24  # Earth mass (kg)
    )
    # T ≈ 5,558 seconds ≈ 92.6 minutes

Inputs: orbital_radius (required), central_mass (required), gravitational_constant.

calculate_pendulum_periodA

Calculate pendulum period: T = 2π√(L/g).

Period of a simple pendulum depends only on length and gravity
(for small amplitudes). Includes correction for large amplitudes.

Args:
    length: Pendulum length in meters (pivot to center of mass)
    gravity: Gravitational acceleration in m/s² (default 9.81)
    amplitude_degrees: Amplitude in degrees (optional, for large angle correction)

Returns:
    Dict containing:
        - period: T in seconds
        - frequency: f in Hz
        - angular_frequency: ω in rad/s
        - small_angle_approximation: Whether small angle formula was used

Tips for LLMs:
    - Period independent of mass (Galileo's discovery)
    - Period independent of amplitude (for small angles < 15°)
    - Longer pendulum → longer period
    - Use for: clocks, playground swings, seismometers

Example - Grandfather clock:
    result = await calculate_pendulum_period(
        length=0.994,  # meters (for 2-second period)
        gravity=9.81
    )
    # T = 2.0 seconds

Inputs: length (required), gravity, amplitude_degrees.

calculate_potential_energyA

Calculate gravitational potential energy.

Computes PE = mgh (mass × gravity × height).
Also returns the equivalent velocity if the object falls from that height.

Args:
    mass: Object mass in kilograms
    height: Height above reference point in meters
    gravity: Gravitational acceleration in m/s² (default 9.81 for Earth)

Returns:
    Dict containing:
        - potential_energy: PE in Joules
        - equivalent_kinetic_velocity: Speed if dropped from height (m/s)

Example - Object at 10m height:
    result = await calculate_potential_energy(mass=2.0, height=10.0)
    # PE = 196.2 J
    # Velocity if dropped = 14.0 m/s

Inputs: mass (required), height (required), gravity.

calculate_pressure_at_depthA

Calculate pressure at depth: P = P_atm + ρgh.

Hydrostatic pressure increases with depth.

Args:
    depth: Depth below surface in meters
    fluid_density: Fluid density in kg/m³ (water=1000, seawater=1025)
    atmospheric_pressure: Pressure at surface in Pascals (default 101325)
    gravity: Gravitational acceleration in m/s² (default 9.81)

Returns:
    Dict containing:
        - total_pressure: Total pressure in Pascals
        - gauge_pressure: Pressure above atmospheric in Pascals
        - pressure_atmospheres: Pressure in atmospheres (1 atm = 101325 Pa)

Example - Scuba diving at 30m:
    result = await calculate_pressure_at_depth(
        depth=30,  # meters
        fluid_density=1025,  # seawater
        atmospheric_pressure=101325
    )
    # Result: ~4 atmospheres

Inputs: depth (required), fluid_density (required), atmospheric_pressure, gravity.

calculate_projectile_motionA

Calculate projectile motion trajectory using kinematic equations.

Computes the complete trajectory of a projectile launched at an angle,
including maximum height, range, time of flight, and sample trajectory points.
Perfect for ballistics, sports analysis, or educational demonstrations.

Args:
    initial_velocity: Initial velocity in meters per second (m/s). Must be positive.
    angle_degrees: Launch angle in degrees from horizontal (0-90).
        0° = horizontal, 45° = maximum range, 90° = straight up
    initial_height: Initial height above ground in meters. Default 0.0 (ground level).
    gravity: Gravitational acceleration in m/s². Default 9.81 (Earth surface).
        Use 1.62 for Moon, 3.71 for Mars, etc.

Returns:
    ProjectileMotionResponse containing:
        - max_height: Maximum height reached (meters)
        - range: Horizontal distance traveled (meters)
        - time_of_flight: Total time in air (seconds)
        - trajectory_points: List of [x, y] sample points for plotting

Tips for LLMs:
    - 45° gives maximum range on flat ground (no air resistance)
    - For R3F visualization: convert trajectory_points to 3D by adding z=0
    - trajectory_points are evenly spaced in time (50 samples)
    - Air resistance is NOT modeled - this is ideal ballistic motion
    - Use for: cannon balls, baseballs, basketball shots, water fountains

Example:
    # Calculate trajectory of a cannonball fired at 50 m/s at 30°
    result = await calculate_projectile_motion(
        initial_velocity=50.0,
        angle_degrees=30.0,
        initial_height=2.0
    )
    print(f"Range: {result.range:.1f}m, Max height: {result.max_height:.1f}m")

Inputs: initial_velocity (required), angle_degrees (required), initial_height, gravity.

calculate_projectile_with_dragA

Calculate projectile motion including air resistance (drag).

Uses numerical integration (RK4) to solve motion equations with:
- Quadratic drag force: F_drag = 0.5 * ρ * v² * Cd * A
- Magnus force (spin effects): F_magnus = 0.5 * ρ * Cl * A * ω * r * v
- Wind effects (constant wind vector)
- Variable air density (altitude and temperature effects)

This provides REALISTIC trajectories for sports balls, projectiles,
and other objects moving through air or water. Compare with
calculate_projectile_motion (no drag) to see dramatic differences!

Common drag coefficients (Cd):
    - Sphere: 0.47 (default)
    - Baseball: 0.4
    - Golf ball: 0.25 (dimples reduce drag)
    - Football (American): 0.05-0.15 (orientation-dependent)
    - Basketball: 0.55
    - Soccer ball: 0.25
    - Skydiver (belly-down): 1.0-1.3
    - Streamlined car: 0.25-0.35

Args:
    initial_velocity: Launch velocity in m/s
    angle_degrees: Launch angle in degrees (0-90)
    mass: Object mass in kg
    cross_sectional_area: Cross-section perpendicular to motion in m²
    initial_height: Launch height in meters (default 0)
    drag_coefficient: Drag coefficient Cd (default 0.47 for sphere)
    fluid_density: Fluid density in kg/m³ (air=1.225, water=1000)
    gravity: Gravitational acceleration m/s² (default 9.81)
    time_step: Integration time step in seconds (default 0.01)
    max_time: Maximum simulation time in seconds (default 30)
    spin_rate: Spin rate in rad/s for Magnus force (default 0, no spin)
    spin_axis: Spin axis unit vector [x, y, z] (default [0, 0, 1] = vertical)
    wind_velocity: Wind velocity [vx, vy] in m/s (default [0, 0], no wind)
    altitude: Altitude above sea level in meters (default 0, affects air density)
    temperature: Air temperature in Celsius (default 15, affects air density)

Returns:
    Dict containing:
        - max_height: Maximum altitude reached (m)
        - range: Horizontal distance traveled (m)
        - time_of_flight: Total flight time (s)
        - impact_velocity: Speed at landing (m/s)
        - impact_angle: Angle at landing (degrees below horizontal)
        - trajectory_points: [[x, y], ...] for plotting
        - energy_lost_to_drag: Energy dissipated by drag (J)
        - initial_kinetic_energy: Initial KE (J)
        - final_kinetic_energy: Final KE (J)
        - lateral_deflection: Lateral deflection from spin/wind (m)
        - magnus_force_max: Maximum Magnus force magnitude (N)
        - wind_drift: Total wind drift (m)
        - effective_air_density: Effective air density used (kg/m³)

Example - Baseball curveball (2500 rpm backspin):
    result = await calculate_projectile_with_drag(
        initial_velocity=40.23,  # 90 mph
        angle_degrees=10,
        mass=0.145,
        cross_sectional_area=0.0043,
        drag_coefficient=0.4,
        spin_rate=261.8,  # 2500 rpm = 261.8 rad/s
        spin_axis=[0, 0, 1]  # Backspin (vertical axis)
    )
    # Backspin increases range and height!

Example - Golf ball at altitude (Denver, 1600m):
    result = await calculate_projectile_with_drag(
        initial_velocity=70,
        angle_degrees=12,
        mass=0.0459,
        cross_sectional_area=0.00143,
        drag_coefficient=0.25,
        altitude=1600,  # Denver elevation
        temperature=20  # Summer day
    )
    # Less air resistance = longer drive!

Example - Soccer free kick with wind:
    result = await calculate_projectile_with_drag(
        initial_velocity=25,
        angle_degrees=15,
        mass=0.43,
        cross_sectional_area=0.0388,
        drag_coefficient=0.25,
        wind_velocity=[5, 0],  # 5 m/s tailwind
        spin_rate=50,  # Sidespin for curve
        spin_axis=[0, 1, 0]  # Horizontal axis
    )
    # Wind drift + Magnus curve!

Inputs: initial_velocity (required), angle_degrees (required), mass (required), cross_sectional_area (required), +11 more.

calculate_reynolds_numberA

Calculate Reynolds number: Re = ρvL/μ.

Determines flow regime (laminar, transitional, turbulent).

Args:
    velocity: Flow velocity in m/s
    characteristic_length: Characteristic length in meters (pipe diameter, etc.)
    fluid_density: Fluid density in kg/m³
    dynamic_viscosity: Dynamic viscosity in Pa·s (water=0.001, air=1.8e-5)

Returns:
    Dict containing:
        - reynolds_number: Re (dimensionless)
        - flow_regime: "laminar" (Re<2300), "transitional" (2300-4000), "turbulent" (Re>4000)

Example - Water in pipe:
    result = await calculate_reynolds_number(
        velocity=2.0,  # m/s
        characteristic_length=0.05,  # 5cm diameter
        fluid_density=1000,  # water
        dynamic_viscosity=0.001
    )
    # Re = 100,000 → turbulent

Inputs: velocity (required), characteristic_length (required), fluid_density (required), dynamic_viscosity (required).

calculate_rotational_kinetic_energyA

Calculate rotational kinetic energy: KE_rot = (1/2) I ω².

Energy of rotation. A spinning object has kinetic energy even if
its center of mass is stationary.

Args:
    moment_of_inertia: Moment of inertia in kg⋅m²
    angular_velocity: Angular velocity magnitude in rad/s

Returns:
    Dict containing:
        - rotational_ke: Rotational kinetic energy in Joules

Tips for LLMs:
    - Total KE = translational KE + rotational KE
    - Rolling object has both types of kinetic energy
    - Flywheel energy storage uses this principle

Example - Car wheel at highway speed:
    result = await calculate_rotational_kinetic_energy(
        moment_of_inertia=0.5,  # kg⋅m²
        angular_velocity=100.0  # rad/s (fast spinning)
    )
    # KE_rot = 2500 J

Inputs: moment_of_inertia (required), angular_velocity (required).

calculate_simple_harmonic_motionA

Calculate simple harmonic motion: x(t) = A cos(ωt + φ).

Position, velocity, and acceleration for sinusoidal oscillation.
Models ideal springs, pendulums, and many other oscillating systems.

Args:
    amplitude: Amplitude A in meters (maximum displacement)
    angular_frequency: ω in rad/s (ω = 2πf)
    time: Time t in seconds
    phase: Phase shift φ in radians (default 0)

Returns:
    Dict containing:
        - position: x(t) in meters
        - velocity: v(t) = -Aω sin(ωt + φ) in m/s
        - acceleration: a(t) = -Aω² cos(ωt + φ) in m/s²

Tips for LLMs:
    - Position and acceleration are 180° out of phase
    - Maximum velocity occurs at equilibrium (x = 0)
    - Maximum acceleration occurs at maximum displacement

Example - Oscillating mass:
    result = await calculate_simple_harmonic_motion(
        amplitude=0.1,  # 10cm amplitude
        angular_frequency=5.0,  # rad/s
        time=1.0  # at t = 1s
    )

Inputs: amplitude (required), angular_frequency (required), time (required), phase.

calculate_spring_mass_periodA

Calculate period of spring-mass system: T = 2π√(m/k).

Natural oscillation frequency of a mass attached to a spring.
Independent of amplitude (for ideal springs).

Args:
    mass: Mass in kg
    spring_constant: Spring constant k in N/m

Returns:
    Dict containing:
        - period: T in seconds
        - frequency: f in Hz
        - angular_frequency: ω in rad/s

Tips for LLMs:
    - Heavier mass → longer period (slower oscillation)
    - Stiffer spring → shorter period (faster oscillation)
    - ω = 2πf = √(k/m)

Example - Mass on spring:
    result = await calculate_spring_mass_period(
        mass=0.5,  # 500g mass
        spring_constant=20.0  # N/m
    )
    # T ≈ 0.99s, f ≈ 1.01 Hz

Inputs: mass (required), spring_constant (required).

calculate_static_frictionA

Calculate maximum static friction force: f_s,max = μ_s × N.

Determines whether an object will slip under applied force.

Args:
    normal_force: Normal force in Newtons
    coefficient_static_friction: Coefficient of static friction μ_s
    applied_force: Applied horizontal force in Newtons (optional)

Returns:
    Dict containing:
        - max_static_friction: Maximum static friction in Newtons
        - will_slip: Whether object will slip (if applied_force provided)
        - friction_force: Actual friction force (if applied_force provided)

Example - Box on floor:
    result = await calculate_static_friction(
        normal_force=100,
        coefficient_static_friction=0.5,
        applied_force=40
    )
    # will_slip = False (40N < 50N max)

Inputs: normal_force (required), coefficient_static_friction (required), applied_force.

calculate_terminal_velocityA

Calculate terminal velocity when drag equals weight.

At terminal velocity, forces balance:
    F_drag = F_weight
    v_terminal = √(2mg / ρC_dA)

Args:
    mass: Object mass in kg
    cross_sectional_area: Area perpendicular to fall direction in m²
    fluid_density: Fluid density in kg/m³ (air=1.225, water=1000)
    drag_coefficient: Drag coefficient (sphere=0.47, skydiver=1.0)
    gravity: Gravitational acceleration in m/s² (default 9.81)

Returns:
    Terminal velocity, time to 95%, and drag force at terminal

Example - Skydiver terminal velocity:
    result = await calculate_terminal_velocity(
        mass=70,  # kg
        cross_sectional_area=0.7,  # m² (belly-down position)
        fluid_density=1.225,  # air
        drag_coefficient=1.0,  # human
    )
    # v_terminal ≈ 54 m/s (120 mph)

Inputs: mass (required), cross_sectional_area (required), fluid_density (required), drag_coefficient, +1 more.

calculate_torqueA

Calculate torque from force and position: τ = r × F (cross product).

Torque is the rotational equivalent of force. It causes angular acceleration
and depends on both the force magnitude and the distance from the pivot point.

Args:
    force_x: X component of force in Newtons
    force_y: Y component of force in Newtons
    force_z: Z component of force in Newtons
    position_x: X component of position vector from pivot to force application (meters)
    position_y: Y component of position vector from pivot to force application (meters)
    position_z: Z component of position vector from pivot to force application (meters)

Returns:
    Dict containing:
        - torque: Torque vector [x, y, z] in N⋅m
        - magnitude: Torque magnitude in N⋅m

Tips for LLMs:
    - Torque direction follows right-hand rule (perpendicular to force and position)
    - Maximum torque when force is perpendicular to position vector
    - Zero torque when force is parallel to position vector
    - Use for: wrenches, door hinges, motors, gears

Example - Opening a door:
    result = await calculate_torque(
        force_x=50.0,  # Push perpendicular to door
        force_y=0.0,
        force_z=0.0,
        position_x=0.0,
        position_y=0.0,
        position_z=0.8  # 0.8m from hinge
    )
    # Torque = 40 N⋅m

Inputs: force_x (required), force_y (required), force_z (required), position_x (required), +2 more.

calculate_venturi_effectA

Calculate Venturi effect (flow through constriction).

Uses continuity equation and Bernoulli's principle.

Args:
    inlet_diameter: Inlet diameter in meters
    throat_diameter: Throat (constriction) diameter in meters
    inlet_velocity: Inlet velocity in m/s
    fluid_density: Fluid density in kg/m³

Returns:
    Dict containing:
        - throat_velocity: Velocity at throat in m/s
        - pressure_drop: Pressure drop from inlet to throat in Pascals
        - flow_rate: Volumetric flow rate in m³/s

Example - Venturi meter:
    result = await calculate_venturi_effect(
        inlet_diameter=0.1,  # 10 cm
        throat_diameter=0.05,  # 5 cm
        inlet_velocity=2.0,  # m/s
        fluid_density=1000  # water
    )
    # throat_velocity = 8 m/s (4x area reduction)

Inputs: inlet_diameter (required), throat_diameter (required), inlet_velocity (required), fluid_density (required).

calculate_work_powerA

Calculate work done by a force and optionally power.

Work is the dot product: W = F · d
Power (if time given): P = W / t

Args:
    force: Force vector [x, y, z] in Newtons (or JSON string)
    displacement: Displacement vector [x, y, z] in meters (or JSON string)
    time: Time taken in seconds (optional, for power calculation)

Returns:
    Dict containing:
        - work: Work done in Joules
        - power: Power in Watts (if time provided, else None)

Example - Pushing box 5m with 100N force:
    result = await calculate_work_power(
        force=[100, 0, 0],
        displacement=[5, 0, 0],
        time=10.0
    )
    # Work = 500 J, Power = 50 W

Inputs: force (required), displacement (required), time.

check_angular_momentum_conservationA

Verify conservation of angular momentum.

Checks whether total angular momentum is conserved. Angular momentum
is conserved when no external torques act on the system.

Args:
    initial_angular_momentum: Initial L [x, y, z] in kg⋅m²/s (or JSON string)
    final_angular_momentum: Final L [x, y, z] in kg⋅m²/s (or JSON string)
    tolerance: Tolerance (fraction, default 0.01 = 1%)

Returns:
    Dict containing:
        - initial_L_magnitude: Initial |L| in kg⋅m²/s
        - final_L_magnitude: Final |L| in kg⋅m²/s
        - L_difference: Difference [x, y, z]
        - L_difference_magnitude: |ΔL|
        - L_difference_percent: % difference
        - is_conserved: Whether L is conserved within tolerance

Tips for LLMs:
    - Conserved when no external torques (isolated rotation)
    - Ice skater spinning: pull arms in → I decreases → ω increases (L constant)
    - Gyroscope: resists changes to L direction
    - Planets orbiting: L conserved → elliptical orbits

Example - Figure skater:
    # Arms extended → Arms pulled in
    result = await check_angular_momentum_conservation(
        initial_angular_momentum=[0, 15, 0],  # kg⋅m²/s
        final_angular_momentum=[0, 15.05, 0],
        tolerance=0.01
    )

Inputs: initial_angular_momentum (required), final_angular_momentum (required), tolerance.

check_collisionA

Check if two moving spherical objects will collide.

Predicts whether two moving spheres will collide within a time window,
and if so, calculates when and where the collision occurs. Uses analytic
relative motion to find exact collision time (if any).

Args:
    body1_position: Position of first object [x, y, z] in meters
    body1_velocity: Velocity of first object [x, y, z] in m/s
    body1_radius: Radius of first object in meters (must be positive)
    body2_position: Position of second object [x, y, z] in meters
    body2_velocity: Velocity of second object [x, y, z] in m/s
    body2_radius: Radius of second object in meters (must be positive)
    max_time: Maximum time to check in seconds. Default 10.0.

Returns:
    CollisionCheckResponse containing:
        - will_collide: True if collision will occur
        - collision_time: Time until collision in seconds (if collision occurs)
        - collision_point: Approximate collision location [x, y, z] (if collision occurs)
        - impact_speed: Relative velocity at impact in m/s (if collision occurs)
        - closest_approach_distance: Minimum distance between objects
        - closest_approach_time: Time of closest approach

Tips for LLMs:
    - Objects are modeled as spheres (point masses with radius)
    - Collision detection is exact for constant velocity motion
    - Returns earliest collision time if multiple intersections
    - If no collision, check closest_approach_distance to see how close they get
    - Use for: asteroid tracking, car crash prediction, sports ball interactions
    - For complex shapes or forces, use create_simulation instead

Example:
    # Check if two cars will collide
    result = await check_collision(
        body1_position=[0.0, 0.0, 0.0],
        body1_velocity=[10.0, 0.0, 0.0],
        body1_radius=2.0,
        body2_position=[50.0, 1.0, 0.0],
        body2_velocity=[-8.0, 0.0, 0.0],
        body2_radius=2.0
    )
    if result.will_collide:
        print(f"Collision in {result.collision_time:.2f} seconds at {result.impact_speed:.1f} m/s")

Inputs: body1_position (required), body1_velocity (required), body1_radius (required), body2_position (required), +3 more.

check_energy_conservationA

Verify conservation of energy in a physics process.

Checks whether total mechanical energy is conserved (or correctly dissipated).
Useful for validating simulation results and understanding energy transfer.

Args:
    initial_kinetic_energy: Initial KE in Joules
    final_kinetic_energy: Final KE in Joules
    initial_potential_energy: Initial PE in Joules
    final_potential_energy: Final PE in Joules
    expected_energy_loss: Expected energy loss (from friction, etc.) in Joules
    tolerance: Tolerance for conservation check (fraction, default 0.01 = 1%)

Returns:
    Dict containing:
        - initial_total_energy: Initial total energy in Joules
        - final_total_energy: Final total energy in Joules
        - energy_difference: Energy difference in Joules
        - energy_difference_percent: % difference
        - is_conserved: Whether energy is conserved within tolerance
        - expected_loss: Expected energy loss in Joules
        - actual_loss: Actual energy loss in Joules

Tips for LLMs:
    - In isolated systems, total energy is conserved
    - With friction/damping, expect energy loss
    - Small numerical errors are normal in simulations
    - Use to validate simulation accuracy

Example - Bouncing ball with energy loss:
    result = await check_energy_conservation(
        initial_kinetic_energy=0,
        final_kinetic_energy=0,
        initial_potential_energy=10,  # J (at 1m height)
        final_potential_energy=6.4,  # J (bounced to 0.64m)
        expected_energy_loss=3.6,  # 36% loss (e=0.8)
        tolerance=0.01
    )

Inputs: initial_kinetic_energy (required), final_kinetic_energy (required), initial_potential_energy (required), final_potential_energy (required), +2 more.

check_equilibriumA

Check complete static equilibrium: ΣF = 0 and Στ = 0.

For static equilibrium, both force and torque must be balanced.

Args:
    forces: List of force vectors [[x,y,z], ...] in N (or JSON string)
    force_positions: Positions where forces applied [[x,y,z], ...] (or JSON string)
    pivot_point: Pivot point for torque calculation [x,y,z] (default [0,0,0])
    tolerance: Tolerance for equilibrium check (default 0.01)

Returns:
    Dict containing:
        - force_balanced: Whether ΣF = 0
        - torque_balanced: Whether Στ = 0
        - in_equilibrium: Whether system is in static equilibrium
        - net_force: Net force [x, y, z] in N
        - net_torque: Net torque [x, y, z] in N⋅m

Example - Beam with two forces:
    result = await check_equilibrium(
        forces=[[0, 100, 0], [0, -100, 0]],
        force_positions=[[1, 0, 0], [2, 0, 0]]
    )

Inputs: forces (required), force_positions (required), pivot_point, tolerance.

check_force_balanceA

Check if forces are in equilibrium: ΣF = 0.

Verifies whether a system of forces is balanced (net force = 0).
Essential for statics problems and structural analysis.

Args:
    forces: List of force vectors [[x,y,z], ...] in Newtons (or JSON string)
    tolerance: Tolerance for equilibrium check (fraction, default 0.01)

Returns:
    Dict containing:
        - net_force: Net force vector [x, y, z] in Newtons
        - net_force_magnitude: Net force magnitude in Newtons
        - is_balanced: Whether forces are in equilibrium
        - individual_magnitudes: Magnitude of each force

Example - Bridge support forces:
    result = await check_force_balance(
        forces=[[0, 1000, 0], [0, 500, 0], [0, -1500, 0]],
        tolerance=0.01
    )
    # is_balanced = True if net force ≈ 0

Inputs: forces (required), tolerance.

check_momentum_conservationA

Verify conservation of momentum.

Checks whether total momentum is conserved in a collision or interaction.
Momentum should be conserved in isolated systems (no external forces).

Args:
    initial_momentum: Initial total momentum [x, y, z] in kg⋅m/s (or JSON string)
    final_momentum: Final total momentum [x, y, z] in kg⋅m/s (or JSON string)
    tolerance: Tolerance for conservation check (fraction, default 0.01 = 1%)

Returns:
    Dict containing:
        - initial_momentum_magnitude: Initial |p| in kg⋅m/s
        - final_momentum_magnitude: Final |p| in kg⋅m/s
        - momentum_difference: Difference [x, y, z]
        - momentum_difference_magnitude: |Δp|
        - momentum_difference_percent: % difference
        - is_conserved: Whether momentum is conserved within tolerance

Tips for LLMs:
    - Momentum is ALWAYS conserved in isolated systems
    - Vector quantity - direction matters
    - Use to validate collision calculations
    - External forces (friction, etc.) can change total momentum

Example - Collision verification:
    result = await check_momentum_conservation(
        initial_momentum=[3000, 0, 0],  # kg⋅m/s
        final_momentum=[2995, 5, 0],  # slightly off
        tolerance=0.01
    )

Inputs: initial_momentum (required), final_momentum (required), tolerance.

check_torque_balanceA

Check if torques are in equilibrium: Στ = 0.

Verifies whether a system of torques is balanced (net torque = 0).
Essential for rotational equilibrium and lever problems.

Args:
    torques: List of torque vectors [[x,y,z], ...] in N⋅m (or JSON string)
    tolerance: Tolerance for equilibrium check (fraction, default 0.01)

Returns:
    Dict containing:
        - net_torque: Net torque vector [x, y, z] in N⋅m
        - net_torque_magnitude: Net torque magnitude in N⋅m
        - is_balanced: Whether torques are in equilibrium
        - individual_magnitudes: Magnitude of each torque

Example - Seesaw balance:
    result = await check_torque_balance(
        torques=[[0, 0, 100], [0, 0, -100]],
        tolerance=0.01
    )

Inputs: torques (required), tolerance.

convert_unitA
Convert a value from one unit to another.

Supports 62 unit types across 16 categories:
- Velocity: m/s, km/h, mph, ft/s, knots
- Distance: m, km, mi, ft, yd, in
- Mass: kg, g, lb, oz
- Force: N, kN, lbf
- Energy: J, kJ, cal, BTU, kWh
- Power: W, kW, hp
- Temperature: K, C, F
- Angle: rad, deg
- Pressure: Pa, kPa, bar, psi, atm
- Area: m², km², ft², acre
- Volume: m³, L, gal, ft³
- Time: s, min, hr, day
- Acceleration: m/s², g, ft/s²
- Torque: N·m, lb·ft, lb·in
- Frequency: Hz, kHz, MHz, GHz
- Data Size: B, KB, MB, GB

Enables natural language queries like:
- "Convert 60 mph to m/s"
- "How fast is 100 km/h in mph?"
- "Convert 10 kg to pounds"

Args:
    value: The numeric value to convert
    from_unit: Source unit (e.g., 'mph', 'kg', 'J')
    to_unit: Target unit (e.g., 'm/s', 'lb', 'kWh')

Returns:
    Dictionary with:
    - original_value: Input value
    - original_unit: Input unit
    - converted_value: Result value
    - converted_unit: Result unit
    - formatted: Human-readable string

Examples:
    >>> convert_unit(100, 'm/s', 'mph')
    {
        "original_value": 100,
        "original_unit": "m/s",
        "converted_value": 223.694,
        "converted_unit": "mph",
        "formatted": "100 m/s = 223.694 mph"
    }

    >>> convert_unit(60, 'mph', 'km/h')
    {
        "original_value": 60,
        "original_unit": "mph",
        "converted_value": 96.56064,
        "converted_unit": "km/h",
        "formatted": "60 mph = 96.56 km/h"
    }

Inputs: value (required), from_unit (required), to_unit (required).

create_simulationA

Create a new physics simulation using Rapier engine.

Initializes a new rigid-body physics world with configurable gravity and
timestep. Returns a simulation ID used for all subsequent operations.

Args:
    gravity_x: X component of gravity vector (m/s²). Default 0.0
    gravity_y: Y component of gravity vector (m/s²). Default -9.81 (Earth down)
    gravity_z: Z component of gravity vector (m/s²). Default 0.0
    dimensions: 2 or 3 for 2D/3D simulation. Default 3.
    dt: Simulation timestep in seconds. Default 0.016 (60 FPS).
        Smaller = more accurate but slower, larger = faster but less stable
    integrator: Integration method. Options: "euler", "verlet", "rk4". Default "verlet".

Returns:
    SimulationCreateResponse containing:
        - sim_id: Unique simulation identifier (use for all other sim calls)
        - config: Echo of the configuration used

Tips for LLMs:
    - Keep simulation IDs in memory for the conversation session
    - Default gravity is Earth standard (9.81 m/s² down = -Y direction)
    - dt=0.016 ≈ 60 FPS, dt=0.008 ≈ 120 FPS (higher accuracy)
    - "verlet" integrator is good default (stable, energy-conserving)
    - Remember to destroy_simulation when done to free resources

Requires:
    - Rapier provider must be configured (see config.py)
    - Rapier service must be running (see RAPIER_SERVICE.md)

Example:
    # Create simulation with Earth gravity
    sim = await create_simulation(
        gravity_y=-9.81,
        dt=0.016
    )
    # Use sim.sim_id for add_body, step_simulation, etc.

Inputs: gravity_x, gravity_y, gravity_z, dimensions, +2 more.

destroy_simulationA

Destroy a simulation and free resources.

Cleanup when done with a simulation. Important for long-running servers
to avoid memory leaks.

Args:
    sim_id: Simulation ID to destroy

Returns:
    Success message

Tips for LLMs:
    - Always destroy simulations when conversation ends or changes topic
    - Rapier service keeps simulations in memory until explicitly destroyed
    - Good practice: destroy after recording trajectory or final state

Example:
    await destroy_simulation(sim_id)

Inputs: sim_id (required).

fit_trajectoryA

Fit polynomial to trajectory data.

Useful for smoothing noisy data or finding trajectory equations.
Default fit_type="quadratic" fits parabolic trajectory (constant acceleration).

Args:
    times: Time values in seconds (or JSON string)
    positions: Position vectors [[x,y,z], ...] in meters (or JSON string)
    fit_type: Polynomial type - "linear", "quadratic", or "cubic" (default "quadratic")

Returns:
    Dict containing:
        - coefficients_x: Polynomial coefficients for x(t)
        - coefficients_y: Polynomial coefficients for y(t)
        - coefficients_z: Polynomial coefficients for z(t)
        - r_squared: R² goodness of fit (0-1)
        - predicted_positions: Fitted positions [[x,y,z], ...]

Example - Projectile motion:
    result = await fit_trajectory(
        times=[0, 1, 2, 3],
        positions=[[0,0,0], [10,15,0], [20,20,0], [30,15,0]],
        fit_type="quadratic"
    )
    # Fits x(t) = c0 + c1*t + c2*t²

Inputs: times (required), positions (required), fit_type.

generate_motion_graphA

Generate motion graph data (position, velocity, acceleration vs time).

Calculates velocity and acceleration from position data and extracts
the specified component for graphing.

Args:
    times: Time values in seconds (or JSON string)
    positions: Position vectors [[x,y,z], ...] in meters (or JSON string)
    component: Which component to analyze - "x", "y", "z", or "magnitude" (default)

Returns:
    Dict containing:
        - times: Time values
        - positions: Position values (selected component)
        - velocities: Velocity values (selected component)
        - accelerations: Acceleration values (selected component)
        - max_velocity: Maximum velocity magnitude
        - max_acceleration: Maximum acceleration magnitude
        - component: Which component was analyzed

Example:
    result = await generate_motion_graph(
        times=[0, 1, 2, 3],
        positions=[[0,0,0], [5,0,0], [20,0,0], [45,0,0]],
        component="x"
    )
    # Automatically calculates v and a

Inputs: times (required), positions (required), component.

list_unit_conversionsA
List all supported unit conversions.

Returns a dictionary mapping category names to lists of supported units.

Returns:
    Dictionary with supported unit categories:
    - velocity: Speed units
    - distance: Length units
    - mass: Weight units
    - force: Force units
    - energy: Energy units
    - power: Power units
    - temperature: Temperature scales
    - angle: Angular units
    - pressure: Pressure units
    - area: Area units
    - volume: Volume units

Example:
    >>> list_unit_conversions()
    {
        "velocity": ["m/s", "km/h", "mph", "ft/s", "knots"],
        "distance": ["m", "km", "mi", "ft", "yd", "in"],
        "mass": ["kg", "g", "lb", "oz"],
        ...
    }

Inputs: no named parameters.

record_trajectoryA

Record the trajectory of a specific body over time.

Steps the simulation and records position/orientation/velocity at each
timestep for one body. Perfect for generating animation data for R3F.

Args:
    sim_id: Simulation ID
    body_id: ID of the body to track
    steps: Number of timesteps to record
    dt: Optional timestep override. If None, uses config default.

Returns:
    TrajectoryResponse containing:
        - body_id: Tracked body identifier
        - frames: List of trajectory frames with time, position, orientation, velocity
        - total_time: Total simulated time in seconds
        - num_frames: Number of frames recorded

Tips for LLMs:
    - Each frame has: time, position [x,y,z], orientation [x,y,z,w], velocity [x,y,z]
    - Frames are evenly spaced in time (every dt seconds)
    - Output is R3F-compatible: use position/orientation directly in Three.js
    - For 60 FPS video: record at dt=1/60 ≈ 0.0167
    - Typical recording: 100-1000 frames (1.6-16 seconds at 60 FPS)

Example:
    # Record 5 seconds of a falling ball
    traj = await record_trajectory(
        sim_id=sim_id,
        body_id="ball",
        steps=300  # 300 × 0.016 ≈ 5 seconds
    )
    # Use traj.frames in React Three Fiber for animation

Inputs: sim_id (required), body_id (required), steps (required), dt.

record_trajectory_with_eventsA

Record trajectory and automatically detect collision and bounce events.

This is an enhanced version of record_trajectory that analyzes the motion
and detects important events like bounces and collisions. Perfect for
answering questions like "how many times did the ball bounce?"

Args:
    sim_id: Simulation ID
    body_id: Body to track
    steps: Number of simulation steps to record
    dt: Optional custom timestep (overrides simulation default)
    detect_bounces: Whether to detect bounce events (default True)
    bounce_height_threshold: Maximum height to consider as "on ground" in meters (default 0.01)

Returns:
    TrajectoryWithEventsResponse containing:
        - frames: Trajectory frames (positions, velocities)
        - bounces: Detected bounce events with energy loss
        - contact_events: Contact/collision events (future)

Tips for LLMs:
    - Use this instead of record_trajectory when you need event detection
    - Bounces are detected from velocity reversals near the ground
    - Each bounce includes: time, position, speeds before/after, energy loss
    - Use `trajectory.bounces` to count or analyze bounces
    - Adjust bounce_height_threshold for different ground shapes

Example:
    # Record ball bouncing and count bounces
    traj = await record_trajectory_with_events(
        sim_id=sim_id,
        body_id="ball",
        steps=600,
        detect_bounces=True,
        bounce_height_threshold=0.01  # 1cm threshold
    )
    print(f"Detected {len(traj.bounces)} bounces")
    for bounce in traj.bounces:
        print(f"Bounce #{bounce.bounce_number} at t={bounce.time:.2f}s")

Inputs: sim_id (required), body_id (required), steps (required), dt, +2 more.

simulate_underwater_motionA

Simulate underwater projectile motion with drag and buoyancy.

Uses numerical integration to simulate motion under:
- Gravity (downward)
- Buoyancy (upward, from displaced fluid)
- Drag (opposes motion)

Args:
    initial_velocity: Initial velocity [x, y, z] in m/s
    mass: Object mass in kg
    volume: Object volume in m³
    cross_sectional_area: Cross-sectional area in m²
    fluid_density: Fluid density in kg/m³ (default 1000 for water)
    fluid_viscosity: Fluid viscosity in Pa·s (default 1.002e-3 for water)
    initial_position: Initial position [x, y, z] in m (default [0,0,0])
    drag_coefficient: Drag coefficient (default 0.47 for sphere)
    gravity: Gravitational acceleration in m/s² (default 9.81)
    duration: Simulation duration in seconds (default 10.0)
    dt: Time step in seconds (default 0.01)

Returns:
    Complete trajectory, final state, max depth, and total distance

Example - Torpedo launch:
    result = await simulate_underwater_motion(
        initial_velocity=[20, 0, 0],  # 20 m/s forward
        mass=100,  # kg
        volume=0.05,  # m³
        cross_sectional_area=0.03,  # m²
        fluid_density=1000,  # water
        drag_coefficient=0.04,  # streamlined
        duration=30.0
    )

Inputs: initial_velocity (required), mass (required), volume (required), cross_sectional_area (required), +7 more.

step_simulationA

Step the simulation forward in time.

Advances the physics simulation by running the integrator for N steps.
Returns the complete state of all bodies after stepping.

Args:
    sim_id: Simulation ID
    steps: Number of timesteps to simulate. Default 1.
        Example: steps=600 with dt=0.016 = 9.6 seconds of simulation
    dt: Optional timestep override (seconds). If None, uses config default.

Returns:
    SimulationStepResponse containing:
        - sim_id: Simulation identifier
        - time: Current simulation time in seconds
        - bodies: List of all body states with positions, velocities, contacts

Tips for LLMs:
    - Each body state includes position, orientation (quaternion), velocities
    - contacts array shows active collisions with impulse magnitudes
    - For real-time preview: steps=1, call repeatedly
    - For final result: steps=1000+, call once
    - Large step counts may timeout - limit to ~10,000 steps per call

Example:
    # Simulate 10 seconds at 60 FPS
    result = await step_simulation(
        sim_id=sim_id,
        steps=600  # 600 steps × 0.016s = 9.6s
    )
    for body in result.bodies:
        print(f"{body.id}: position={body.position}")

Inputs: sim_id (required), steps, dt.

track_energy_dissipationA

Track energy dissipation over a trajectory.

Analyzes how energy changes over time in a recorded trajectory.
Useful for understanding damping, bounces, and energy loss mechanisms.

Args:
    trajectory_data: Trajectory data dict with 'frames' field
    mass: Object mass in kg
    gravity: Gravitational acceleration in m/s² (default 9.81)
    reference_height: Reference height for PE in meters (default 0.0)

Returns:
    Dict containing:
        - frames: Energy data for each frame (time, KE, PE, total E)
        - initial_total_energy: Initial total energy in Joules
        - final_total_energy: Final total energy in Joules
        - total_energy_loss: Total energy dissipated in Joules
        - total_energy_loss_percent: % of energy lost
        - average_power_dissipated: Average power in Watts (J/s)

Tips for LLMs:
    - Use after record_trajectory or record_trajectory_with_events
    - Visualize energy vs time to see where energy is lost
    - Identifies bounces, friction effects, air resistance
    - Power = rate of energy dissipation

Example - Bouncing ball energy analysis:
    traj = await record_trajectory_with_events(sim_id, "ball", 600)
    result = await track_energy_dissipation(
        trajectory_data=traj.model_dump(),
        mass=0.5,  # 500g ball
        gravity=9.81
    )
    # See how energy decreases with each bounce

Inputs: trajectory_data (required), mass (required), gravity, reference_height.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 62 tools

    Disambiguation2/5

    Many tools have overlapping purposes, such as calculate_elastic_collision vs calculate_elastic_collision_3d, which are essentially the same operation in 1D vs 3D. Similarly, check_force_balance and check_equilibrium both verify force balance, and check_equilibrium also includes torque balance, causing potential confusion. The distinction between some tools (e.g., calculate_orbital_period vs analyze_circular_orbit) is subtle and may lead to misselection.

    Naming Consistency3/5

    Most tools follow a consistent 'calculate_' or 'check_' or 'simulate_' prefix pattern, making them predictable. However, there are minor inconsistencies like 'list_unit_conversions' (no verb prefix) and 'convert_unit' without a consistent prefix, plus some names like 'track_energy_dissipation' vs 'calculate_energy' are not parallel. Overall, the pattern is readable but not perfectly uniform.

    Tool Count2/5

    With 62 tools, this server is extremely heavy. Many tools are highly specialized (e.g., calculate_bernoulli, calculate_venturi_effect) and could be combined or consolidated. The sheer number overwhelms an agent and makes selection difficult. Some tools are redundant variations (1D vs 3D collisions) that inflate the count without adding distinct value.

    Completeness4/5

    The tool surface covers a comprehensive range of mechanics (kinematics, dynamics, fluids, oscillations, rotational motion, statics) and includes simulation capabilities with CRUD-like operations (create, add, step, record, destroy). Missing pieces include wave mechanics, electromagnetism, quantum physics, or thermal physics, which are beyond the server's apparent scope. Within its mechanical focus, coverage is solid, though there is no explicit 'get_simulation_state' tool separate from step_simulation, and no batch operations for large parameter sweeps.

    Maintenance

    ActivitySlowing
    ResponsivenessLimited data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      D
      maintenance
      Enables physicists to perform computer algebra calculations, create scientific plots, solve differential equations, work with tensor algebra and quantum mechanics, and parse natural language physics problems. Supports unit conversion, physical constants, and generates comprehensive reports with optional GPU acceleration.
      11
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Verified unit conversion and dimensional analysis for AI agents. 190+ units, 31 domain formulas (clinical, physics, aerospace, SRE), physical constants with uncertainty propagation. Refuses invalid conversions structurally: the tool that won't convert mg to mL and knows the difference between torque and energy.
      AGPL 3.0
    • A
      license
      Not graded
      quality
      D
      maintenance
      Exposes MuJoCo physics simulation to AI assistants via 65 MCP tools, enabling natural language control of robotics simulation, trajectory optimization, contact analysis, and video export.
      10
      MIT
    • A
      license
      B
      quality
      C
      maintenance
      Enables LLMs to run arbitrary-precision integer and bitwise operations, endianness swaps, IEEE 754 float decomposition, hashing/checksum/modular math, data codecs, statistics, and trigonometry through 29 specialized tools. Its chained batch execution engine lets multiple dependent steps run in one call, eliminating multi-turn round-trip overhead.
      29
      23 npm
      19
      MIT