# Story 10.1: New Physical Types

Status: done

## Story

As a thermodynamic simulation engineer,
I want type-safe physical types for concentration, volumetric flow, relative humidity, and vapor quality,
So that I can model brine mixtures, air-handling systems, and two-phase refrigerants without unit confusion.

## Acceptance Criteria

1. **Given** the existing `types.rs` module with NewType pattern
   **When** I add the 4 new types
   **Then** they follow the exact same pattern as `Pressure`, `Temperature`, `Enthalpy`, `MassFlow`

2. **Concentration**: represents glycol/brine mixture fraction (0.0 to 1.0)
   - Internal unit: dimensionless fraction
   - Conversions: `from_fraction()`, `from_percent()`, `to_fraction()`, `to_percent()`
   - Clamped to [0.0, 1.0] on construction

3. **VolumeFlow**: represents volumetric flow rate
   - Internal unit: cubic meters per second (m³/s)
   - Conversions: `from_m3_per_s()`, `from_l_per_s()`, `from_l_per_min()`, `from_m3_per_h()`, `to_m3_per_s()`, `to_l_per_s()`, `to_l_per_min()`, `to_m3_per_h()`

4. **RelativeHumidity**: represents air moisture level (0.0 to 1.0)
   - Internal unit: dimensionless fraction
   - Conversions: `from_fraction()`, `from_percent()`, `to_fraction()`, `to_percent()`
   - Clamped to [0.0, 1.0] on construction

5. **VaporQuality**: represents refrigerant two-phase state (0.0 to 1.0)
   - Internal unit: dimensionless fraction (0 = saturated liquid, 1 = saturated vapor)
   - Conversions: `from_fraction()`, `from_percent()`, `to_fraction()`, `to_percent()`
   - Clamped to [0.0, 1.0] on construction
   - Constants: `SATURATED_LIQUID`, `SATURATED_VAPOR`
   - Helper methods: `is_saturated_liquid()`, `is_saturated_vapor()`

6. **Given** the new types
   **When** compiling code that mixes types incorrectly
   **Then** compilation fails (type safety)

7. All types implement: `Debug`, `Clone`, `Copy`, `PartialEq`, `PartialOrd`, `Display`, `From<f64>`
8. All types implement arithmetic traits: `Add`, `Sub`, `Mul<f64>`, `Div<f64>`, and reverse `Mul<Type> for f64`
9. Unit tests cover all conversions, edge cases (0, 1, negatives), and type safety
10. Documentation with examples for each public method

## Tasks / Subtasks

- [x] Task 1: Add Concentration type (AC: #2)
  - [x] 1.1 Define struct with `#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]`
  - [x] 1.2 Implement `from_fraction()` with clamping to [0.0, 1.0]
  - [x] 1.3 Implement `from_percent()` with clamping
  - [x] 1.4 Implement `to_fraction()`, `to_percent()`
  - [x] 1.5 Implement `Display` with "%" suffix
  - [x] 1.6 Implement `From<f64>`, `Add`, `Sub`, `Mul<f64>`, `Div<f64>`, reverse `Mul`
  - [x] 1.7 Add unit tests: conversions, clamping, arithmetic, display

- [x] Task 2: Add VolumeFlow type (AC: #3)
  - [x] 2.1 Define struct with SI unit (m³/s)
  - [x] 2.2 Implement `from_m3_per_s()`, `from_l_per_s()`, `from_l_per_min()`, `from_m3_per_h()`
  - [x] 2.3 Implement `to_m3_per_s()`, `to_l_per_s()`, `to_l_per_min()`, `to_m3_per_h()`
  - [x] 2.4 Implement `Display` with " m³/s" suffix
  - [x] 2.5 Implement `From<f64>`, `Add`, `Sub`, `Mul<f64>`, `Div<f64>`, reverse `Mul`
  - [x] 2.6 Add unit tests: all conversions, arithmetic, display

- [x] Task 3: Add RelativeHumidity type (AC: #4)
  - [x] 3.1 Define struct with clamping to [0.0, 1.0]
  - [x] 3.2 Implement `from_fraction()`, `from_percent()` with clamping
  - [x] 3.3 Implement `to_fraction()`, `to_percent()`
  - [x] 3.4 Implement `Display` with "% RH" suffix
  - [x] 3.5 Implement `From<f64>`, `Add`, `Sub`, `Mul<f64>`, `Div<f64>`, reverse `Mul`
  - [x] 3.6 Add unit tests: conversions, clamping, arithmetic, display

- [x] Task 4: Add VaporQuality type (AC: #5)
  - [x] 4.1 Define struct with clamping to [0.0, 1.0]
  - [x] 4.2 Implement `from_fraction()`, `from_percent()` with clamping
  - [x] 4.3 Implement `to_fraction()`, `to_percent()`
  - [x] 4.4 Add constants `SATURATED_LIQUID = VaporQuality(0.0)`, `SATURATED_VAPOR = VaporQuality(1.0)`
  - [x] 4.5 Implement `is_saturated_liquid()`, `is_saturated_vapor()` with tolerance 1e-9
  - [x] 4.6 Implement `Display` with " (quality)" suffix
  - [x] 4.7 Implement `From<f64>`, `Add`, `Sub`, `Mul<f64>`, `Div<f64>`, reverse `Mul`
  - [x] 4.8 Add unit tests: conversions, clamping, constants, helper methods, arithmetic

- [x] Task 5: Update module exports (AC: #6)
  - [x] 5.1 Add types to `crates/core/src/lib.rs` exports
  - [x] 5.2 Verify `cargo doc --package entropyk-core` renders correctly

- [x] Task 6: Validation
  - [x] 6.1 Run `cargo test --package entropyk-core types::tests`
  - [x] 6.2 Run `cargo clippy --package entropyk-core -- -D warnings`
  - [x] 6.3 Run `cargo test --workspace` to ensure no regressions

### Review Follow-ups (AI) - FIXED

- [x] [AI-Review][MEDIUM] Update types.rs module documentation to list all 12 physical types [types.rs:1-25]
- [x] [AI-Review][MEDIUM] Update lib.rs crate documentation with all types and improved example [lib.rs:8-44]
- [x] [AI-Review][MEDIUM] Correct test count from 64 to 52 in Dev Agent Record
- [x] [AI-Review][LOW] Add compile_fail doctest for type safety demonstration [types.rs:23-31]
- [x] [AI-Review][LOW] Document VolumeFlow negative value behavior (reverse flow) [types.rs:610-628]

## Dev Notes

### Architecture Patterns (MUST follow)

From `architecture.md` - Critical Pattern: NewType for Unit Safety:

```rust
// Pattern: Tuple struct with SI base unit internally
pub struct Concentration(pub f64);

// NEVER use bare f64 in public APIs
fn set_concentration(c: Concentration)  // ✓ Correct
fn set_concentration(c: f64)            // ✗ WRONG
```

### Existing Type Pattern Reference

See `crates/core/src/types.rs:29-115` for the exact pattern to follow (Pressure example).

Key elements:
1. Tuple struct: `pub struct TypeName(pub f64)`
2. `#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]`
3. `from_*` factory methods
4. `to_*` accessor methods
5. `impl fmt::Display` with unit suffix
6. `impl From<f64>` for direct conversion
7. Arithmetic traits: `Add`, `Sub`, `Mul<f64>`, `Div<f64>`, and reverse `Mul<Type> for f64`
8. Comprehensive tests using `approx::assert_relative_eq!`

### Clamping Strategy for Bounded Types

For `Concentration`, `RelativeHumidity`, and `VaporQuality`:

```rust
impl Concentration {
    /// Creates a Concentration, clamped to [0.0, 1.0].
    pub fn from_fraction(value: f64) -> Self {
        Concentration(value.clamp(0.0, 1.0))
    }
    
    /// Creates a Concentration from percentage, clamped to [0, 100]%.
    pub fn from_percent(value: f64) -> Self {
        Concentration((value / 100.0).clamp(0.0, 1.0))
    }
}
```

**Rationale**: Clamping prevents invalid physical states (e.g., negative concentration) while avoiding panics. This follows the Zero-Panic Policy from architecture.md.

### SI Units Summary

| Type | SI Unit | Other Units |
|------|---------|-------------|
| Concentration | - (fraction 0-1) | % |
| VolumeFlow | m³/s | L/s, L/min, m³/h |
| RelativeHumidity | - (fraction 0-1) | % |
| VaporQuality | - (fraction 0-1) | % |

### Conversion Factors

```rust
// VolumeFlow
const LITERS_PER_M3: f64 = 1000.0;      // 1 m³ = 1000 L
const SECONDS_PER_MINUTE: f64 = 60.0;   // 1 min = 60 s
const SECONDS_PER_HOUR: f64 = 3600.0;   // 1 h = 3600 s
// m³/h to m³/s: divide by 3600
// L/s to m³/s: divide by 1000
// L/min to m³/s: divide by 1000*60 = 60000
```

### Test Tolerances (from architecture.md)

Use `approx::assert_relative_eq!` with appropriate tolerances:

```rust
use approx::assert_relative_eq;

// General conversions: 1e-10
assert_relative_eq!(c.to_fraction(), 0.5, epsilon = 1e-10);

// Display format: exact string match
assert_eq!(format!("{}", c), "50%");
```

### Project Structure Notes

- **File to modify**: `crates/core/src/types.rs`
- **Export file**: `crates/core/src/lib.rs`
- **Test location**: Inline in `types.rs` under `#[cfg(test)] mod tests`
- **Alignment**: Follows unified project structure - types in core crate, re-exported from lib.rs

### References

- [Source: architecture.md#L476-L506] - NewType pattern rationale
- [Source: architecture.md#L549-L576] - Scientific testing tolerances
- [Source: crates/core/src/types.rs:29-115] - Existing Pressure implementation (exact pattern to follow)
- [Source: crates/core/src/types.rs:313-L416] - MassFlow with regularization pattern
- [Source: crates/core/src/types.rs:700-L1216] - Test patterns with approx

### Dependencies on Other Stories

None - this is the foundation story for Epic 10.

### Downstream Dependencies

- Story 10-2 (RefrigerantSource/Sink) needs `VaporQuality`
- Story 10-3 (BrineSource/Sink) needs `Concentration`, `VolumeFlow`
- Story 10-4 (AirSource/Sink) needs `RelativeHumidity`, `VolumeFlow`

### Common LLM Mistakes to Avoid

1. **Don't use `#[should_panic]` tests** - Use clamping instead of panics (Zero-Panic Policy)
2. **Don't forget reverse `Mul`** - `2.0 * concentration` must work
3. **Don't skip `Display`** - All types need human-readable output
4. **Don't use different patterns** - Must match existing types exactly
5. **Don't forget `From<f64>`** - Required for ergonomics

## Dev Agent Record

### Agent Model Used

glm-5 (zai-anthropic/glm-5)

### Debug Log References

None - implementation completed without issues.

### Completion Notes List

- Implemented 4 new physical types: `Concentration`, `VolumeFlow`, `RelativeHumidity`, `VaporQuality`
- All types follow the existing NewType pattern exactly as specified
- Added 52 new unit tests (107 total tests pass in types module)
- Bounded types (`Concentration`, `RelativeHumidity`, `VaporQuality`) use clamping with re-clamping on arithmetic operations
- `VaporQuality` includes `SATURATED_LIQUID` and `SATURATED_VAPOR` constants plus helper methods
- All types re-exported from `lib.rs` for ergonomic access
- Documentation with examples generated successfully
- Added `compile_fail` doctest demonstrating type safety (types cannot be mixed)
- Updated module and crate documentation to include all physical types

### File List

- crates/core/src/types.rs (modified - added 4 new types + tests)
- crates/core/src/lib.rs (modified - updated exports)

## Change Log

- 2026-02-23: Completed implementation of all 4 physical types. All 107 tests pass. Clippy clean. Documentation builds successfully.
- 2026-02-23: Code Review Follow-ups - Fixed documentation gaps (module docs, crate docs), corrected test count (52 not 64), added compile_fail doctest for type safety, documented VolumeFlow negative value behavior

## Senior Developer Review (AI)

**Reviewer:** Code Review Agent (glm-5)  
**Date:** 2026-02-23  
**Outcome:** ✅ Approved with auto-fixes applied

### Issues Found & Fixed

**MEDIUM (3):**
1. ✅ **Module documentation outdated** - Updated types.rs module header to list all 12 physical types
2. ✅ **Crate documentation outdated** - Updated lib.rs crate documentation with all types and improved example
3. ✅ **Test count inflation** - Corrected Dev Agent Record from "64" to "52" new tests

**LOW (2):**
4. ✅ **Missing compile_fail doctest** - Added `compile_fail` doctest demonstrating type safety
5. ✅ **VolumeFlow negative values undocumented** - Added note about reverse flow capability

### Verification Results

- ✅ All 107 unit tests pass
- ✅ All 23 doc tests pass (including new compile_fail test)
- ✅ Clippy clean (0 warnings)
- ✅ Documentation builds successfully
- ✅ Sprint status synced: 10-1-new-physical-types → done

### Summary

Implementation is solid and follows the established NewType pattern correctly. All bounded types properly clamp values, arithmetic operations preserve bounds, and the code is well-tested. Documentation now accurately reflects the implementation.
