ushma 1.2.0

Thermodynamics simulation — heat transfer, entropy, equations of state, thermal properties
Documentation
//! Error types for ushma.

#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum UshmaError {
    #[error("temperature must be positive: {kelvin} K (absolute zero is 0 K)")]
    InvalidTemperature { kelvin: f64 },

    #[error("pressure must be positive: {pascals} Pa")]
    InvalidPressure { pascals: f64 },

    #[error("volume must be positive: {cubic_meters} m³")]
    InvalidVolume { cubic_meters: f64 },

    #[error("negative thermal conductivity: {value} W/(m⋅K)")]
    InvalidConductivity { value: f64 },

    #[error("negative specific heat: {value} J/(kg⋅K)")]
    InvalidSpecificHeat { value: f64 },

    #[error("phase transition undefined at T={temperature} K, P={pressure} Pa")]
    UndefinedPhaseTransition { temperature: f64, pressure: f64 },

    #[error("division by zero in thermal calculation: {context}")]
    DivisionByZero { context: String },

    #[error("invalid parameter: {reason}")]
    InvalidParameter { reason: String },

    #[error("substance not found: {name}")]
    SubstanceNotFound { name: String },

    #[error("steam table out of range: T={temperature} K, P={pressure} Pa")]
    SteamTableOutOfRange { temperature: f64, pressure: f64 },

    #[error("invalid quality (dryness fraction): x={quality}, must be in [0, 1]")]
    InvalidQuality { quality: f64 },

    #[error("superheated steam lookup out of range: T={temperature} K, P={pressure} Pa")]
    SuperheatOutOfRange { temperature: f64, pressure: f64 },

    #[error("invalid compression ratio: r={ratio}, must be > 1")]
    InvalidCompressionRatio { ratio: f64 },

    #[error("invalid cutoff ratio: rc={ratio}, must be > 1")]
    InvalidCutoffRatio { ratio: f64 },

    #[error("invalid pressure ratio: rp={ratio}, must be > 1")]
    InvalidPressureRatio { ratio: f64 },

    #[error("invalid cycle parameter: {reason}")]
    InvalidCycleParameter { reason: String },
}

pub type Result<T> = std::result::Result<T, UshmaError>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_invalid_temperature() {
        let e = UshmaError::InvalidTemperature { kelvin: -10.0 };
        let msg = e.to_string();
        assert!(msg.contains("-10"));
        assert!(msg.contains("positive"));
    }

    #[test]
    fn test_invalid_pressure() {
        let e = UshmaError::InvalidPressure { pascals: -100.0 };
        assert!(e.to_string().contains("-100"));
    }

    #[test]
    fn test_invalid_volume() {
        let e = UshmaError::InvalidVolume { cubic_meters: -0.5 };
        assert!(e.to_string().contains("-0.5"));
    }

    #[test]
    fn test_invalid_conductivity() {
        let e = UshmaError::InvalidConductivity { value: -1.0 };
        assert!(e.to_string().contains("-1"));
    }

    #[test]
    fn test_invalid_specific_heat() {
        let e = UshmaError::InvalidSpecificHeat { value: -500.0 };
        assert!(e.to_string().contains("-500"));
    }

    #[test]
    fn test_phase_transition() {
        let e = UshmaError::UndefinedPhaseTransition {
            temperature: 373.15,
            pressure: 101325.0,
        };
        let msg = e.to_string();
        assert!(msg.contains("373.15"));
    }

    #[test]
    fn test_division_by_zero() {
        let e = UshmaError::DivisionByZero {
            context: "heat flux".into(),
        };
        assert!(e.to_string().contains("heat flux"));
    }

    #[test]
    fn test_invalid_parameter() {
        let e = UshmaError::InvalidParameter {
            reason: "mole count must be positive".into(),
        };
        assert!(e.to_string().contains("mole count"));
    }

    #[test]
    fn test_result_alias() {
        let ok: Result<f64> = Ok(300.0);
        assert!(ok.is_ok());
        let err: Result<f64> = Err(UshmaError::InvalidTemperature { kelvin: -1.0 });
        assert!(err.is_err());
    }

    #[test]
    fn test_substance_not_found() {
        let e = UshmaError::SubstanceNotFound {
            name: "unobtainium".into(),
        };
        assert!(e.to_string().contains("unobtainium"));
    }

    #[test]
    fn test_steam_table_out_of_range() {
        let e = UshmaError::SteamTableOutOfRange {
            temperature: 700.0,
            pressure: 1e8,
        };
        let msg = e.to_string();
        assert!(msg.contains("700"));
        assert!(msg.contains("steam table"));
    }

    #[test]
    fn test_invalid_quality() {
        let e = UshmaError::InvalidQuality { quality: 1.5 };
        let msg = e.to_string();
        assert!(msg.contains("1.5"));
        assert!(msg.contains("[0, 1]"));
    }

    #[test]
    fn test_superheat_out_of_range() {
        let e = UshmaError::SuperheatOutOfRange {
            temperature: 200.0,
            pressure: 101325.0,
        };
        let msg = e.to_string();
        assert!(msg.contains("200"));
        assert!(msg.contains("superheated"));
    }

    #[test]
    fn test_invalid_compression_ratio() {
        let e = UshmaError::InvalidCompressionRatio { ratio: 0.5 };
        let msg = e.to_string();
        assert!(msg.contains("0.5"));
        assert!(msg.contains("compression"));
    }

    #[test]
    fn test_invalid_cutoff_ratio() {
        let e = UshmaError::InvalidCutoffRatio { ratio: 0.8 };
        assert!(e.to_string().contains("cutoff"));
    }

    #[test]
    fn test_invalid_pressure_ratio() {
        let e = UshmaError::InvalidPressureRatio { ratio: -1.0 };
        assert!(e.to_string().contains("pressure ratio"));
    }

    #[test]
    fn test_invalid_cycle_parameter() {
        let e = UshmaError::InvalidCycleParameter {
            reason: "T3 must exceed T2".into(),
        };
        assert!(e.to_string().contains("T3 must exceed T2"));
    }

    #[test]
    fn test_error_is_debug() {
        let e = UshmaError::InvalidTemperature { kelvin: 0.0 };
        let debug = format!("{:?}", e);
        assert!(debug.contains("InvalidTemperature"));
    }
}