# Physical Types

Entropyk uses the **NewType pattern** for compile-time unit safety. This prevents accidental mixing of units (e.g., adding Pascals to bar) at compile time.

## Overview

All physical types store values in **SI base units** internally:

| Type | Internal Unit | Display Unit |
|------|---------------|--------------|
| `Pressure` | Pascals (Pa) | Pa, bar, PSI |
| `Temperature` | Kelvin (K) | K, °C, °F |
| `Enthalpy` | J/kg | J/kg, kJ/kg |
| `MassFlow` | kg/s | kg/s, g/s |
| `Power` | Watts (W) | W, kW, MW |

## Pressure

```rust
use entropyk_core::Pressure;

// Create from different units
let p1 = Pressure::from_pascals(101_325.0);  // 1 atm
let p2 = Pressure::from_bar(1.0);             // 1 bar = 100,000 Pa
let p3 = Pressure::from_psi(14.7);            // ~1 atm

// Convert to different units
println!("{} Pa", p1.to_pascals());   // 101325.0
println!("{} bar", p1.to_bar());       // 1.01325
println!("{} PSI", p1.to_psi());       // ~14.7

// Arithmetic (same type)
let p_sum = p1 + p2;
let p_diff = p1 - p2;
let p_double = p1 * 2.0;
let p_half = p1 / 2.0;

// Comparison
assert!(p1 > p2);  // PartialOrd implemented
```

### Common Pressure Values

| Description | Value |
|-------------|-------|
| Atmospheric pressure | 101,325 Pa (1.01325 bar) |
| Low-pressure refrigerant (R134a evaporating at -10°C) | ~200 kPa (2 bar) |
| High-pressure refrigerant (R134a condensing at 40°C) | ~1,000 kPa (10 bar) |

## Temperature

```rust
use entropyk_core::Temperature;

// Create from different units
let t1 = Temperature::from_kelvin(273.15);   // 0°C
let t2 = Temperature::from_celsius(25.0);     // 25°C = 298.15 K
let t3 = Temperature::from_fahrenheit(32.0);  // 0°C = 273.15 K

// Convert to different units
println!("{} K", t2.to_kelvin());      // 298.15
println!("{} °C", t2.to_celsius());    // 25.0
println!("{} °F", t2.to_fahrenheit()); // 77.0

// Arithmetic
let t_sum = t1 + Temperature::from_kelvin(10.0);
let t_diff = t2 - t1;  // 25 K difference
```

### Thermodynamic Reference Points

| Description | Kelvin | Celsius |
|-------------|--------|---------|
| Absolute zero | 0 K | -273.15°C |
| Water freezing point | 273.15 K | 0°C |
| Room temperature | 293.15 K | 20°C |
| Water boiling point (1 atm) | 373.15 K | 100°C |

## Enthalpy

Specific enthalpy (energy per unit mass) is the primary state variable in thermodynamic cycles.

```rust
use entropyk_core::Enthalpy;

// Create from different units
let h1 = Enthalpy::from_joules_per_kg(400_000.0);     // 400 kJ/kg
let h2 = Enthalpy::from_kilojoules_per_kg(400.0);      // Same value

// Convert
println!("{} J/kg", h1.to_joules_per_kg());     // 400000.0
println!("{} kJ/kg", h1.to_kilojoules_per_kg()); // 400.0

// Arithmetic
let h_diff = h2 - h1;  // Enthalpy difference (e.g., for work/heat)
```

### Typical Enthalpy Values for R134a

| State | Enthalpy (kJ/kg) |
|-------|------------------|
| Saturated liquid at 0°C | ~200 |
| Saturated vapor at 0°C | ~400 |
| Superheated vapor at 40°C, 1.2 MPa | ~420 |
| Subcooled liquid at 40°C, 1.0 MPa | ~256 |

## MassFlow

Mass flow rate is critical for energy balance calculations.

```rust
use entropyk_core::MassFlow;

// Create from different units
let m1 = MassFlow::from_kg_per_s(0.1);      // 100 g/s
let m2 = MassFlow::from_grams_per_s(100.0);  // Same value

// Convert
println!("{} kg/s", m1.to_kg_per_s());   // 0.1
println!("{} g/s", m1.to_grams_per_s()); // 100.0

// Zero-flow regularization (IMPORTANT for solver stability)
let zero_flow = MassFlow::from_kg_per_s(0.0);
let safe_flow = zero_flow.regularized();  // Returns at least 1e-12 kg/s

// Use regularized flow in denominators
let enthalpy_per_mass = Enthalpy::from_joules_per_kg(1000.0);
let power = enthalpy_per_mass * safe_flow;  // Won't cause NaN
```

### Zero-Flow Regularization

When a component is off (zero mass flow), dividing by mass flow would cause NaN or Inf. The `regularized()` method clamps the flow to at least `1e-12 kg/s`:

```rust
// BAD: Can cause NaN when flow is zero
let specific_power = power / flow.to_kg_per_s();

// GOOD: Safe for zero-flow conditions
let specific_power = power / flow.regularized().to_kg_per_s();
```

## Power

```rust
use entropyk_core::Power;

// Create from different units
let p1 = Power::from_watts(1000.0);       // 1 kW
let p2 = Power::from_kilowatts(1.0);       // Same value
let p3 = Power::from_megawatts(0.001);     // Same value

// Convert
println!("{} W", p1.to_watts());       // 1000.0
println!("{} kW", p1.to_kilowatts());  // 1.0
println!("{} MW", p1.to_megawatts());  // 0.001
```

## Type Safety Benefits

The NewType pattern catches unit errors at compile time:

```rust
// This WON'T COMPILE - cannot add Pressure to Temperature
let p = Pressure::from_bar(1.0);
let t = Temperature::from_celsius(25.0);
// let bad = p + t;  // Error: mismatched types

// This WORKS - same types
let p1 = Pressure::from_bar(1.0);
let p2 = Pressure::from_bar(2.0);
let p_sum = p1 + p2;  // OK: Pressure + Pressure = Pressure
```

## Python Bindings

In Python, the types are exposed as simple classes:

```python
import entropyk

# Create types
p = entropyk.Pressure.from_bar(1.0)
t = entropyk.Temperature.from_celsius(25.0)
h = entropyk.Enthalpy.from_kilojoules_per_kg(400.0)
m = entropyk.MassFlow.from_kg_per_s(0.1)

# Convert
print(p.to_pascals())  # 100000.0
print(t.to_kelvin())   # 298.15
```

## Best Practices

1. **Always use the type constructors** - Never pass raw `f64` values for physical quantities
2. **Use regularized mass flow** - When dividing by mass flow, always use `.regularized()`
3. **Document units in comments** - When interfacing with external APIs, note the expected units
4. **Convert at boundaries** - Convert to/from SI units at system boundaries (user input, file I/O)

## Next Steps

- [Components Reference](./03-components.md) - See how types are used in component parameters
- [Building Systems](./04-building-systems.md) - Connect components with type-safe ports
