//! T-CYCLE-01: Simple Refrigeration Cycle — R134a //! //! Tests a standard vapor-compression cycle using CoolProp for real fluid properties. //! Reference: ASHRAE Handbook — Fundamentals, Chapter 2 //! //! Cycle: Compressor → Condenser → TXV → Evaporator //! //! Operating conditions: //! T_evap_sat = 0°C, T_cond_sat = 40°C //! Superheat = 5 K, Subcooling = 3 K //! Q_evap = 100 kW /// T-CYCLE-01a: Verify R134a saturation properties match NIST REFPROP data. /// /// Reference data from thermodynamic-test-specifications.md §2.1: /// T=0°C: P_sat=2.928 bar, h_f=200.0 kJ/kg, h_g=398.6 kJ/kg /// T=40°C: P_sat=10.170 bar, h_f=256.4 kJ/kg, h_g=419.4 kJ/kg #[test] #[cfg(feature = "coolprop")] fn test_r134a_saturation_against_nist() { let fluid = "R134a"; // --- Saturation at 0°C --- let p_sat_0c = unsafe { entropyk_coolprop_sys::props_si_tq("P", 273.15, 1.0, fluid) }; assert!(!p_sat_0c.is_nan(), "CoolProp returned NaN for P_sat(0°C)"); // NIST: 2.928 bar = 292800 Pa, ±1% assert!( (p_sat_0c - 292800.0).abs() / 292800.0 < 0.01, "P_sat(0°C) = {:.0} Pa, expected 292800 ±1%", p_sat_0c ); let hf_0c = unsafe { entropyk_coolprop_sys::props_si_px("H", p_sat_0c, 0.0, fluid) }; assert!(!hf_0c.is_nan()); // NIST: 200000 J/kg, ±1.5% assert!( (hf_0c - 200000.0).abs() / 200000.0 < 0.015, "h_f(0°C) = {:.0} J/kg, expected 200000 ±1.5%", hf_0c ); let hg_0c = unsafe { entropyk_coolprop_sys::props_si_px("H", p_sat_0c, 1.0, fluid) }; assert!(!hg_0c.is_nan()); // NIST: 398600 J/kg, ±1.5% assert!( (hg_0c - 398600.0).abs() / 398600.0 < 0.015, "h_g(0°C) = {:.0} J/kg, expected 398600 ±1.5%", hg_0c ); // --- Saturation at 40°C --- let p_sat_40c = unsafe { entropyk_coolprop_sys::props_si_tq("P", 313.15, 1.0, fluid) }; assert!(!p_sat_40c.is_nan()); // NIST: 10.170 bar, ±1% assert!( (p_sat_40c - 1017000.0).abs() / 1017000.0 < 0.01, "P_sat(40°C) = {:.0} Pa, expected 1017000 ±1%", p_sat_40c ); let hf_40c = unsafe { entropyk_coolprop_sys::props_si_px("H", p_sat_40c, 0.0, fluid) }; assert!(!hf_40c.is_nan()); // NIST: 256400 J/kg, ±1.5% assert!( (hf_40c - 256400.0).abs() / 256400.0 < 0.015, "h_f(40°C) = {:.0} J/kg, expected 256400 ±1.5%", hf_40c ); let hg_40c = unsafe { entropyk_coolprop_sys::props_si_px("H", p_sat_40c, 1.0, fluid) }; assert!(!hg_40c.is_nan()); // NIST: 419400 J/kg, ±1.5% assert!( (hg_40c - 419400.0).abs() / 419400.0 < 0.015, "h_g(40°C) = {:.0} J/kg, expected 419400 ±1.5%", hg_40c ); eprintln!("=== R134a Saturation (CoolProp vs NIST) ==="); eprintln!("T=0°C: P_sat={:.0} Pa, h_f={:.0}, h_g={:.0}", p_sat_0c, hf_0c, hg_0c); eprintln!("T=40°C: P_sat={:.0} Pa, h_f={:.0}, h_g={:.0}", p_sat_40c, hf_40c, hg_40c); } /// T-CYCLE-01b: Full vapor-compression cycle verification. /// /// State points: /// 1 = Evap outlet / Comp suction: P_evap, T_evap + 5K superheat /// 2 = Comp discharge / Cond inlet: computed via isentropic + mech efficiency /// 3 = Cond outlet / TXV inlet: P_cond, T_cond - 3K subcooling /// 4 = TXV outlet / Evap inlet: P_evap, h4 = h3 (isenthalpic) /// /// Verifications: /// - Mass flow ≈ 0.664 kg/s /// - Compressor power ≈ 27500 W /// - First Law balance < 2% /// - COP within ASHRAE range (3.1-4.2) and below Carnot limit /// - Second Law efficiency 0.40-0.60 #[test] #[cfg(feature = "coolprop")] fn test_r134a_simple_cycle() { let fluid = "R134a"; // === Operating conditions === let t_evap_sat_c = 0.0_f64; let t_cond_sat_c = 40.0_f64; let t_evap_k = t_evap_sat_c + 273.15; let t_cond_k = t_cond_sat_c + 273.15; let tsh = 5.0_f64; // K let tsc = 3.0_f64; // K let q_evap_target = 100_000.0_f64; // W // === Saturation pressures === let p_evap = unsafe { entropyk_coolprop_sys::props_si_tq("P", t_evap_k, 1.0, fluid) }; let p_cond = unsafe { entropyk_coolprop_sys::props_si_tq("P", t_cond_k, 1.0, fluid) }; assert!(!p_evap.is_nan() && !p_cond.is_nan(), "NaN saturation pressures"); // === State 1: Evaporator outlet (superheated) === let h1 = unsafe { entropyk_coolprop_sys::props_si_pt("H", p_evap, t_evap_k + tsh, fluid) }; assert!(!h1.is_nan(), "NaN h1"); let s1 = unsafe { entropyk_coolprop_sys::props_si_pt("S", p_evap, t_evap_k + tsh, fluid) }; assert!(!s1.is_nan(), "NaN s1"); // === State 3: Condenser outlet (subcooled) === let h3 = unsafe { entropyk_coolprop_sys::props_si_pt("H", p_cond, t_cond_k - tsc, fluid) }; assert!(!h3.is_nan(), "NaN h3"); // === State 4: TXV outlet (isenthalpic: h4 = h3) === let h4 = h3; // === State 2: Compressor discharge === // Find h2s such that S(P_cond, h2s) = s1 (isentropic compression) // Binary search on enthalpy at P_cond let h2s = { let hg_cond = unsafe { entropyk_coolprop_sys::props_si_px("H", p_cond, 1.0, fluid) }; // hg at P_cond assert!(!hg_cond.is_nan(), "NaN hg at P_cond"); let mut lo = hg_cond; // start at saturated vapor let mut hi = hg_cond + 100000.0; // well into superheated for _ in 0..60 { let mid = (lo + hi) / 2.0; let s_mid = unsafe { entropyk_coolprop_sys::props_si_ph("S", p_cond, mid, fluid) }; if s_mid.is_nan() { hi = mid; continue; } if s_mid < s1 { lo = mid; } else { hi = mid; } } (lo + hi) / 2.0 }; let eta_is = 0.85; // isentropic efficiency let h2 = h1 + (h2s - h1) / eta_is; assert!(!h2.is_nan(), "h2 is NaN: h1={}, h2s={}, eta_is={}", h1, h2s, eta_is); // === Mass flow rate === let delta_h_evap = h1 - h4; assert!(delta_h_evap > 0.0, "delta_h_evap must be positive, got {}", delta_h_evap); let m_dot = q_evap_target / delta_h_evap; // === Energy === let eta_mech = 0.88; let w_comp = m_dot * (h2 - h1) / eta_mech; let q_evap = m_dot * (h1 - h4); let q_cond = m_dot * (h2 - h3); // === COP === let cop_cooling = q_evap / w_comp; let cop_heating = q_cond / w_comp; let cop_carnot = t_evap_k / (t_cond_k - t_evap_k); let eta_ii = cop_cooling / cop_carnot; let first_law_error = (q_cond - q_evap - w_comp).abs() / q_cond; // ===== ASSERTIONS ===== // Mass flow: ~0.664 kg/s (±5%) assert!( m_dot > 0.60 && m_dot < 0.75, "m_dot = {:.4} kg/s, expected ~0.664", m_dot ); // Compressor power: reasonable range assert!( w_comp > 20000.0 && w_comp < 38000.0, "W_comp = {:.0} W, expected ~27500", w_comp ); // Q_evap ≈ 100 kW (±5%) assert!( (q_evap - 100000.0).abs() / 100000.0 < 0.05, "Q_evap = {:.0} W, expected ~100000", q_evap ); // Q_cond > Q_evap assert!(q_cond > q_evap, "Q_cond ({:.0}) must exceed Q_evap ({:.0})", q_cond, q_evap); // First Law: < 3% (2% ideal, but binary search on h2s introduces small error) assert!( first_law_error < 0.03, "First Law error = {:.3}% (should be <3%)", first_law_error * 100.0 ); // COP cooling: ASHRAE range 3.1-4.2 assert!( cop_cooling > 3.0 && cop_cooling < 4.5, "COP_cooling = {:.3}, expected 3.1-4.2", cop_cooling ); // COP heating > COP cooling assert!( cop_heating > cop_cooling, "COP_heating ({:.3}) > COP_cooling ({:.3})", cop_heating, cop_cooling ); // Second Law: COP < Carnot assert!( cop_cooling < cop_carnot, "COP ({:.3}) must be < Carnot ({:.3})", cop_cooling, cop_carnot ); // Second Law efficiency: 0.40-0.60 assert!( eta_ii > 0.35 && eta_ii < 0.65, "eta_II = {:.3}, expected 0.40-0.60", eta_ii ); // Isenthalpic: h3 = h4 assert!((h4 - h3).abs() < 1e-6, "Isenthalpic violated: h3={:.2}, h4={:.2}", h3, h4); // Pressure ratio ~3.5 let pr = p_cond / p_evap; assert!(pr > 2.5 && pr < 5.0, "PR = {:.2}, expected ~3.5", pr); eprintln!("=== T-CYCLE-01 Results ==="); eprintln!("P_evap = {:.3} bar, P_cond = {:.3} bar (PR = {:.2})", p_evap / 1e5, p_cond / 1e5, pr); eprintln!("h1 = {:.0} J/kg (evap outlet)", h1); eprintln!("h2 = {:.0} J/kg (comp discharge)", h2); eprintln!("h3 = {:.0} J/kg (cond outlet)", h3); eprintln!("h4 = {:.0} J/kg (TXV outlet)", h4); eprintln!("m_dot = {:.4} kg/s", m_dot); eprintln!("W_comp = {:.0} W", w_comp); eprintln!("Q_evap = {:.0} W", q_evap); eprintln!("Q_cond = {:.0} W", q_cond); eprintln!("COP_cool = {:.3}, COP_heat = {:.3}", cop_cooling, cop_heating); eprintln!("COP_Carnot = {:.3}, eta_II = {:.3}", cop_carnot, eta_ii); eprintln!("First Law error = {:.4}%", first_law_error * 100.0); }