#!/usr/bin/env python3
"""Viscosity and Reynolds Number Demo.
This example demonstrates the viscosity parameter for accurate Reynolds number
calculations across different fluids. The Reynolds number determines flow regime:
- Re < 2,300: Laminar flow (smooth, predictable)
- 2,300 < Re < 4,000: Transitional flow
- Re > 4,000: Turbulent flow (chaotic, eddies)
The viscosity parameter allows accurate Re calculations for any fluid, not just
air and water. This is critical for understanding flow behavior in:
- Motor oil and lubricants (very viscous)
- Honey and syrups (extremely viscous)
- Temperature-sensitive fluids
- Industrial process fluids
"""
import asyncio
import math
from chuk_mcp_physics.tools.fluid import calculate_drag_force
def classify_flow(reynolds_number: float) -> str:
"""Classify flow regime based on Reynolds number."""
if reynolds_number < 2300:
return "Laminar (smooth)"
elif reynolds_number < 4000:
return "Transitional"
else:
return "Turbulent (chaotic)"
async def demo_viscosity_comparison():
"""Compare drag force across fluids with different viscosities."""
print("\n" + "=" * 80)
print("DEMO 1: Same Ball, Different Fluids - The Importance of Viscosity")
print("=" * 80)
print("\nScenario: 5cm radius ball falling at 2 m/s through various fluids")
print(" - Ball radius: 0.05 m (basketball-like)")
print(" - Velocity: 2 m/s downward")
print(" - Shape: Sphere (Cd = 0.47)")
print()
radius = 0.05
area = math.pi * radius * radius
velocity = [0.0, -2.0, 0.0]
cd = 0.47
fluids = [
{
"name": "Air (20°C)",
"density": 1.225,
"viscosity": 1.825e-5,
"color": "🌫️ ",
},
{
"name": "Water (20°C)",
"density": 1000.0,
"viscosity": 1.002e-3,
"color": "💧",
},
{
"name": "Motor Oil",
"density": 900.0,
"viscosity": 0.1,
"color": "🛢️ ",
},
{
"name": "Honey (approximate)",
"density": 1420.0,
"viscosity": 10.0,
"color": "🍯",
},
]
print(f"{'Fluid':<25} {'Density':<12} {'Viscosity':<15} {'Drag':<10} {'Re':<12} {'Flow Regime':<20}")
print("-" * 105)
for fluid in fluids:
result = await calculate_drag_force(
velocity=velocity,
cross_sectional_area=area,
fluid_density=fluid["density"],
drag_coefficient=cd,
viscosity=fluid["viscosity"], # ← KEY: Explicit viscosity
)
flow_regime = classify_flow(result["reynolds_number"])
print(
f"{fluid['color']} {fluid['name']:<22} "
f"{fluid['density']:<12.1f} "
f"{fluid['viscosity']:<15.2e} "
f"{result['magnitude']:<10.2f} "
f"{result['reynolds_number']:<12.0f} "
f"{flow_regime}"
)
print("\n💡 Key Insight: Viscosity ranges over 6 orders of magnitude!")
print(" Air → Honey: viscosity increases by ~500,000x")
print(" This completely changes the flow regime and behavior.")
async def demo_motor_oil_accuracy():
"""Show the importance of explicit viscosity for motor oil."""
print("\n" + "=" * 80)
print("DEMO 2: Motor Oil - Why Explicit Viscosity Matters")
print("=" * 80)
print("\nProblem: Motor oil has water-like density (~900 kg/m³) but 100x higher viscosity!")
print("Without explicit viscosity, Reynolds number is completely wrong.\n")
radius = 0.05
area = math.pi * radius * radius
velocity = [0.0, -2.0, 0.0]
# Test 1: WITH explicit viscosity (CORRECT)
print("✅ WITH explicit viscosity parameter:")
result_correct = await calculate_drag_force(
velocity=velocity,
cross_sectional_area=area,
fluid_density=900.0,
drag_coefficient=0.47,
viscosity=0.1, # Motor oil actual viscosity
)
print(f" Viscosity used: 0.1 Pa·s (actual motor oil)")
print(f" Reynolds number: {result_correct['reynolds_number']:.0f}")
print(f" Flow regime: {classify_flow(result_correct['reynolds_number'])}")
print(f" Drag force: {result_correct['magnitude']:.2f} N")
# Test 2: WITHOUT explicit viscosity (WRONG)
print("\n❌ WITHOUT explicit viscosity parameter:")
result_wrong = await calculate_drag_force(
velocity=velocity,
cross_sectional_area=area,
fluid_density=900.0,
drag_coefficient=0.47,
# No viscosity - will estimate as water-like (1e-3) because density > 100
)
print(f" Viscosity used: 1.0e-3 Pa·s (estimated as water-like - WRONG!)")
print(f" Reynolds number: {result_wrong['reynolds_number']:.0f}")
print(f" Flow regime: {classify_flow(result_wrong['reynolds_number'])} (INCORRECT!)")
print(f" Drag force: {result_wrong['magnitude']:.2f} N (same, drag is still correct)")
# Show the error
error_factor = result_wrong["reynolds_number"] / result_correct["reynolds_number"]
print(f"\n⚠️ Error: Reynolds number is {error_factor:.0f}x too high!")
print(f" This leads to completely wrong flow regime classification.")
print(f" Actual: {classify_flow(result_correct['reynolds_number'])}")
print(f" Estimated: {classify_flow(result_wrong['reynolds_number'])}")
async def demo_temperature_effects():
"""Show how temperature affects viscosity."""
print("\n" + "=" * 80)
print("DEMO 3: Temperature Effects on Viscosity")
print("=" * 80)
print("\nWater viscosity changes significantly with temperature:")
print(" - Cold water is more viscous → higher drag, lower Re")
print(" - Hot water is less viscous → lower drag, higher Re\n")
radius = 0.05
area = math.pi * radius * radius
velocity = [0.0, -2.0, 0.0]
temperatures = [
{"temp": 0, "viscosity": 1.787e-3, "emoji": "🧊"},
{"temp": 20, "viscosity": 1.002e-3, "emoji": "🌡️"},
{"temp": 60, "viscosity": 0.467e-3, "emoji": "♨️ "},
{"temp": 100, "viscosity": 0.282e-3, "emoji": "💨"},
]
print(f"{'Temp':<15} {'Viscosity':<20} {'Re':<15} {'Flow Regime'}")
print("-" * 65)
for t in temperatures:
result = await calculate_drag_force(
velocity=velocity,
cross_sectional_area=area,
fluid_density=1000.0,
drag_coefficient=0.47,
viscosity=t["viscosity"],
)
flow_regime = classify_flow(result["reynolds_number"])
print(
f"{t['emoji']} {t['temp']}°C{'':<9} "
f"{t['viscosity']:<20.3e} "
f"{result['reynolds_number']:<15.0f} "
f"{flow_regime}"
)
print("\n💡 Key Insight: Viscosity decreases ~6x from ice-cold to boiling!")
print(" This affects flow behavior significantly.")
async def demo_industrial_fluids():
"""Show Reynolds number for various industrial fluids."""
print("\n" + "=" * 80)
print("DEMO 4: Industrial Fluids - Why One Size Doesn't Fit All")
print("=" * 80)
print("\nIndustrial processes use fluids with vastly different properties:")
print(" - Hydraulic oil, glycerin, liquid sugar, etc.")
print(" - Each requires accurate viscosity for proper flow analysis\n")
radius = 0.025 # 5cm diameter pipe
area = math.pi * radius * radius
velocity = [5.0, 0.0, 0.0] # Flowing through horizontal pipe
fluids = [
{"name": "Hydraulic Oil", "density": 870, "viscosity": 0.05, "emoji": "⚙️ "},
{"name": "Glycerin", "density": 1260, "viscosity": 1.5, "emoji": "🧴"},
{"name": "Liquid Sugar", "density": 1400, "viscosity": 3.0, "emoji": "🍬"},
{"name": "Molasses", "density": 1400, "viscosity": 5.0, "emoji": "🥞"},
]
print(f"{'Fluid':<20} {'Velocity':<12} {'Re':<15} {'Flow Regime':<20} {'Notes'}")
print("-" * 90)
for fluid in fluids:
result = await calculate_drag_force(
velocity=velocity,
cross_sectional_area=area,
fluid_density=fluid["density"],
drag_coefficient=0.47,
viscosity=fluid["viscosity"],
)
flow_regime = classify_flow(result["reynolds_number"])
# Add engineering notes
if result["reynolds_number"] < 2300:
notes = "Easy to pump"
elif result["reynolds_number"] < 4000:
notes = "Careful monitoring"
else:
notes = "High turbulence"
print(
f"{fluid['emoji']} {fluid['name']:<17} "
f"{5.0:<12.1f} "
f"{result['reynolds_number']:<15.0f} "
f"{flow_regime:<20} "
f"{notes}"
)
print("\n💡 Engineering Insight: Flow regime determines:")
print(" - Pump requirements (laminar = easier)")
print(" - Mixing behavior (turbulent = better mixing)")
print(" - Heat transfer efficiency (turbulent = better)")
print(" - Pipe friction losses (laminar = lower losses)")
async def demo_backwards_compatibility():
"""Show that old code still works without viscosity parameter."""
print("\n" + "=" * 80)
print("DEMO 5: Backwards Compatibility")
print("=" * 80)
print("\nOld code without viscosity parameter still works!")
print("It uses density-based estimation:\n")
radius = 0.05
area = math.pi * radius * radius
# Air-like density (< 100 kg/m³)
print("Air-like density (1.225 kg/m³):")
result_air = await calculate_drag_force(
velocity=[10.0, 0.0, 0.0],
cross_sectional_area=area,
fluid_density=1.225,
drag_coefficient=0.47,
# No viscosity - will estimate as air-like (1.8e-5)
)
print(f" ✅ Estimates viscosity: 1.8e-5 Pa·s (air-like)")
print(f" Reynolds number: {result_air['reynolds_number']:.0f}")
# Water-like density (> 100 kg/m³)
print("\nWater-like density (1000 kg/m³):")
result_water = await calculate_drag_force(
velocity=[0.0, -5.0, 0.0],
cross_sectional_area=area,
fluid_density=1000,
drag_coefficient=0.47,
# No viscosity - will estimate as water-like (1e-3)
)
print(f" ✅ Estimates viscosity: 1.0e-3 Pa·s (water-like)")
print(f" Reynolds number: {result_water['reynolds_number']:.0f}")
print("\n💡 Backwards Compatibility: Existing code works unchanged!")
print(" But for accurate Re with other fluids, use the viscosity parameter.")
async def main():
"""Run all demonstrations."""
print("\n" + "█" * 80)
print("█" + " " * 78 + "█")
print("█" + " " * 15 + "VISCOSITY AND REYNOLDS NUMBER DEMONSTRATIONS" + " " * 19 + "█")
print("█" + " " * 78 + "█")
print("█" * 80)
await demo_viscosity_comparison()
await demo_motor_oil_accuracy()
await demo_temperature_effects()
await demo_industrial_fluids()
await demo_backwards_compatibility()
print("\n" + "=" * 80)
print("SUMMARY: Viscosity Parameter Benefits")
print("=" * 80)
print("""
✅ Accurate Reynolds number for ANY fluid
✅ Correct flow regime classification
✅ Temperature effects modeling
✅ Industrial fluid analysis
✅ 100% backwards compatible
🎯 When to use viscosity parameter:
- Motor oil, hydraulic fluids (high viscosity)
- Honey, syrups, molasses (very high viscosity)
- Temperature-sensitive applications
- Industrial process design
- Any non-standard fluid
📚 Common viscosity values:
- Air (20°C): 1.825e-5 Pa·s
- Water (20°C): 1.002e-3 Pa·s
- Motor oil: 0.1 Pa·s
- Honey: ~10 Pa·s
- Glycerin: ~1.5 Pa·s
For air and water, viscosity parameter is optional (estimated correctly).
For other fluids, provide explicit viscosity for accurate Reynolds number!
""")
print("=" * 80)
if __name__ == "__main__":
asyncio.run(main())