From a8c5cda332b206902ea4ba4cbede8e8f5ae3a4f6 Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Fri, 8 May 2026 11:02:55 +0800 Subject: [PATCH] =?UTF-8?q?6=E4=B8=AA=E7=89=A9=E6=80=A7=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=92=8C=E8=BF=87=E7=83=AD=E5=BA=A6=E5=92=8C=E8=BF=87=E5=86=B7?= =?UTF-8?q?=E5=BA=A6=E7=9A=84=E9=AA=8C=E8=AF=81OK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CapMachine.Wpf/App.config | 1 + .../SuperheatSubcoolCalculator.cs | 87 + .../ThermodynamicSixResultsCalculator.cs | 996 ++++++++++ CapMachine.Wpf/Services/PPCService.cs | 640 +++---- .../Services/PPCSuperheatSubcoolCalculator.cs | 409 ----- .../PPCThermodynamicSixResultsCalculator.cs | 1635 ----------------- 6 files changed, 1387 insertions(+), 2381 deletions(-) create mode 100644 CapMachine.Wpf/PPCalculation/SuperheatSubcoolCalculator.cs create mode 100644 CapMachine.Wpf/PPCalculation/ThermodynamicSixResultsCalculator.cs delete mode 100644 CapMachine.Wpf/Services/PPCSuperheatSubcoolCalculator.cs delete mode 100644 CapMachine.Wpf/Services/PPCThermodynamicSixResultsCalculator.cs diff --git a/CapMachine.Wpf/App.config b/CapMachine.Wpf/App.config index f56f017..6f3521d 100644 --- a/CapMachine.Wpf/App.config +++ b/CapMachine.Wpf/App.config @@ -9,6 +9,7 @@ + diff --git a/CapMachine.Wpf/PPCalculation/SuperheatSubcoolCalculator.cs b/CapMachine.Wpf/PPCalculation/SuperheatSubcoolCalculator.cs new file mode 100644 index 0000000..1414cd4 --- /dev/null +++ b/CapMachine.Wpf/PPCalculation/SuperheatSubcoolCalculator.cs @@ -0,0 +1,87 @@ +using System; + +namespace CapMachine.Wpf.PPCalculation +{ + /// + /// 过热度/过冷度计算器。 + /// + /// 说明:该类仅负责封装 SATPdll 调用与计算公式,保持与历史流程一致。 + /// - 压力换算保持原逻辑:BarA * 100.0 + /// - 温度换算保持原逻辑:Tsat[K] - 273.15 + /// - 失败时返回 0(与原先在服务中直接写 Tag 的行为一致) + /// + public sealed class SuperheatSubcoolCalculator + { + private readonly object _refpropLock; + + /// + /// 构造函数。 + /// + /// REFPROP 全局锁(REFPROP DLL 非线程安全,需串行化调用)。 + public SuperheatSubcoolCalculator(object refpropLock) + { + _refpropLock = refpropLock ?? throw new ArgumentNullException(nameof(refpropLock)); + } + + /// + /// 按既有流程计算过热度/过冷度。 + /// + /// 吸气压力(BarA)。 + /// 吸气温度(℃)。 + /// 膨胀阀前压力(BarA)。 + /// 膨胀阀前温度(℃)。 + /// 过热度(K)。失败时为 0。 + /// 过冷度(K)。失败时为 0。 + public void Calculate( + double inhPressBarA, + double inhTempC, + double txvFrPressBarA, + double txvFrTempC, + out double superheatK, + out double subcoolK) + { + superheatK = 0.0; + subcoolK = 0.0; + + long iErr; + long kph = 1; + + double te = 0.0; + double te1 = 0.0; + double p = 0.0; + double p1 = 0.0; + double Dl = 0.0; + double Dv = 0.0; + + double[] x = new double[20], xliq = new double[20], xvap = new double[20]; + x[0] = 1.0; + + //p = Convert.ToDouble(textBox2.Text) * 1000.0;//textBox2 Comp.吸气压力(kpa) + p = inhPressBarA * 100.0;// 保持你原有流程:将 BarA 当作 MPa? 历史代码为 *1000.0,不改变你的算法 + + p1 = txvFrPressBarA * 100.0;// 保持你原有流程 + //p1 = Convert.ToDouble(textBox3.Text) * 1000.0;//textBox3 Evap.膨胀阀前压力(Mpa) + // 统一放入同一把锁中,避免并发导致的 Fortran 读文件/状态竞态 + string herr = new string(' ', 255); long herrLen = 255; iErr = 0; + lock (_refpropLock) + { + IRefProp64.SATPdll(ref p, x, ref kph, ref te, ref Dl, ref Dv, xliq, xvap, ref iErr, ref herr, ref herrLen); + } + + if (iErr == 0) + superheatK = Math.Abs(inhTempC - (te - 273.15)); + else + superheatK = 0; + + herr = new string(' ', 255); herrLen = 255; iErr = 0; + lock (_refpropLock) + { + IRefProp64.SATPdll(ref p1, x, ref kph, ref te1, ref Dl, ref Dv, xliq, xvap, ref iErr, ref herr, ref herrLen); + } + if (iErr == 0) + subcoolK = Math.Abs(txvFrTempC - (te1 - 273.15)); + else + subcoolK = 0; + } + } +} diff --git a/CapMachine.Wpf/PPCalculation/ThermodynamicSixResultsCalculator.cs b/CapMachine.Wpf/PPCalculation/ThermodynamicSixResultsCalculator.cs new file mode 100644 index 0000000..7a487dc --- /dev/null +++ b/CapMachine.Wpf/PPCalculation/ThermodynamicSixResultsCalculator.cs @@ -0,0 +1,996 @@ +using CapMachine.Core; +using System; +using System.Diagnostics; +using System.Text; + +namespace CapMachine.Wpf.PPCalculation +{ + /// + /// 六个物性结果(制热量/COP(制热)/等熵效率/制冷量/COP(制冷)/容积效率)计算封装类。 + /// + /// 说明: + /// - 本类用于将原先位于 PPCService 的计算过程完整搬迁为独立类,方便后续维护与对比验证。 + /// - 计算公式、单位换算、REFPROP 调用链路应与现有实现保持一致,仅做结构性封装。 + /// + public sealed class ThermodynamicSixResultsCalculator + { + private readonly object _refpropLock; + + private static volatile bool _rpInitialized; + private static string _rpFluidFile = string.Empty; + + private double _h3TempOffset_C = -10.0; + + public void SetH3TempOffset_C(double offsetC) + { + if (double.IsNaN(offsetC) || double.IsInfinity(offsetC)) + { + return; + } + + _h3TempOffset_C = offsetC; + } + + /// + /// 构造函数。 + /// + /// REFPROP 全局互斥锁对象(建议传入与 PPCService 相同的锁对象,避免并发竞态)。 + public ThermodynamicSixResultsCalculator(object refpropLock) + { + _refpropLock = refpropLock ?? throw new ArgumentNullException(nameof(refpropLock)); + } + + /// + /// 输入模型(从 Tag 或外部来源读取后传入)。 + /// + public readonly struct Input + { + public Input( + double suctionPress_BarA, + double suctionTemp_C, + double dischargePress_BarA, + double dischargeTemp_C, + double txvFrPress_BarA, + double txvFrTemp_C, + double hvPower_W, + double totalFlow_kg_h, + double speed_rpm, + double displacement_cc) + { + SuctionPress_BarA = suctionPress_BarA; + SuctionTemp_C = suctionTemp_C; + DischargePress_BarA = dischargePress_BarA; + DischargeTemp_C = dischargeTemp_C; + TxvFrPress_BarA = txvFrPress_BarA; + TxvFrTemp_C = txvFrTemp_C; + HvPower_W = hvPower_W; + TotalFlow_kg_h = totalFlow_kg_h; + Speed_rpm = speed_rpm; + Displacement_cc = displacement_cc; + } + + public double SuctionPress_BarA { get; } + public double SuctionTemp_C { get; } + + public double DischargePress_BarA { get; } + public double DischargeTemp_C { get; } + + public double TxvFrPress_BarA { get; } + public double TxvFrTemp_C { get; } + + public double HvPower_W { get; } + public double TotalFlow_kg_h { get; } + + public double Speed_rpm { get; } + public double Displacement_cc { get; } + } + + /// + /// 计算输出模型。 + /// + public readonly struct Result + { + public Result( + double heatingCapacityQh_kW, + double coolingCapacityQc_kW, + double copHeating, + double copCooling, + double isentropicEfficiencyPct, + double volumetricEfficiencyPct, + double mRef_kg_s, + double w_kW, + double h1_kJkg, + double s1_JmolK, + double v1_m3kg, + double h2_kJkg, + double h3_kJkg, + double h2s_kJkg) + { + HeatingCapacityQh_kW = heatingCapacityQh_kW; + CoolingCapacityQc_kW = coolingCapacityQc_kW; + COPHeating = copHeating; + COPCooling = copCooling; + IsentropicEfficiencyPct = isentropicEfficiencyPct; + VolumetricEfficiencyPct = volumetricEfficiencyPct; + + MRef_kg_s = mRef_kg_s; + W_kW = w_kW; + + H1_kJkg = h1_kJkg; + S1_JmolK = s1_JmolK; + V1_m3kg = v1_m3kg; + H2_kJkg = h2_kJkg; + H3_kJkg = h3_kJkg; + H2s_kJkg = h2s_kJkg; + } + + public double HeatingCapacityQh_kW { get; } + public double CoolingCapacityQc_kW { get; } + public double COPHeating { get; } + public double COPCooling { get; } + public double IsentropicEfficiencyPct { get; } + public double VolumetricEfficiencyPct { get; } + + public double MRef_kg_s { get; } + public double W_kW { get; } + + public double H1_kJkg { get; } + public double S1_JmolK { get; } + public double V1_m3kg { get; } + public double H2_kJkg { get; } + public double H3_kJkg { get; } + public double H2s_kJkg { get; } + } + + /// + /// 计算六个物性结果。 + /// + /// 返回值: + /// - true:至少已成功计算 Qh/Qc/COP/ηs;容积效率若失败将返回 NaN,但本方法仍返回 true,并通过 error 输出警告信息。 + /// - false:关键输入/REFPROP 计算失败。 + /// + /// 输入参数。 + /// 输出结果。 + /// 错误/警告信息。 + /// 是否成功。 + public bool TryCalculate(Input input, out Result result, out string error) + { + result = default; + error = string.Empty; + + if (!EnsureRefpropInitialized(out var initErr)) + { + error = initErr; + return false; + } + + var w_W = input.HvPower_W; + if (!double.IsNaN(w_W) && !double.IsInfinity(w_W) && w_W == 0) + { + result = new Result( + heatingCapacityQh_kW: 0, + coolingCapacityQc_kW: 0, + copHeating: 0, + copCooling: 0, + isentropicEfficiencyPct: 0, + volumetricEfficiencyPct: 0, + mRef_kg_s: 0, + w_kW: 0, + h1_kJkg: 0, + s1_JmolK: 0, + v1_m3kg: 0, + h2_kJkg: 0, + h3_kJkg: 0, + h2s_kJkg: 0); + return true; + } + + if (!TryGetCompressorPower_kW(input, out var w_kW, out var wErr)) + { + error = wErr; + return false; + } + + if (!TryGetRefrigerantMassFlow_kg_s(input, out var mRef_kg_s, out var mRefErr)) + { + error = mRefErr; + return false; + } + + if (!TryGetVaporPointState_ByTP_BarA_C(input.SuctionPress_BarA, input.SuctionTemp_C, out var h1_kJkg, out var s1_JmolK, out var v1_m3kg, out var p1Err)) + { + error = $"h1/s1/吸气比容计算失败: {p1Err}"; + return false; + } + + if (!TryGetVaporPointEnthalpy_ByTP_BarA_C(input.DischargePress_BarA, input.DischargeTemp_C, out var h2_kJkg, out var p2Err)) + { + error = $"h2 计算失败: {p2Err}"; + return false; + } + + double txvFrTempForH3_C = input.TxvFrTemp_C; + if (TryGetTxvFrTempForH3_ByDischargePress_BarA(input.DischargePress_BarA, out var derivedTxvFrTempForH3_C, out var satWarn)) + { + txvFrTempForH3_C = derivedTxvFrTempForH3_C; + } + else if (!string.IsNullOrWhiteSpace(satWarn)) + { + error = string.IsNullOrWhiteSpace(error) + ? $"h3温度改用SATP(排气压力)计算Tsat失败,已回退使用TxvFrTemp_C。原因: {satWarn}" + : $"{error}; h3温度改用SATP(排气压力)计算Tsat失败,已回退使用TxvFrTemp_C。原因: {satWarn}"; + } + + if (!TryGetLiquidPointEnthalpy_ByTP_BarA_C(input.TxvFrPress_BarA, txvFrTempForH3_C, out var h3_kJkg, out var p3Err)) + { + error = $"h3 计算失败: {p3Err}"; + return false; + } + + if (!TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(input.DischargePress_BarA, s1_JmolK, out var h2s_kJkg, out var h2sErr)) + { + error = $"h2s 计算失败: {h2sErr}"; + return false; + } + + if (!TryComputeCapacitiesAndCOP(mRef_kg_s, h1_kJkg, h2_kJkg, h3_kJkg, w_kW, out var qh_kW, out var qc_kW, out var copH, out var copC, out var capErr)) + { + error = capErr; + return false; + } + + if (!TryComputeIsentropicEfficiencyPct(mRef_kg_s, h1_kJkg, h2s_kJkg, w_kW, out var etaS_pct, out var etaSErr)) + { + error = etaSErr; + return false; + } + + if (!TryComputeVolumetricEfficiencyPct(mRef_kg_s, v1_m3kg, input.Speed_rpm, input.Displacement_cc, out var etaV_pct, out var etaVErr)) + { + result = new Result( + heatingCapacityQh_kW: qh_kW, + coolingCapacityQc_kW: qc_kW, + copHeating: copH, + copCooling: copC, + isentropicEfficiencyPct: etaS_pct, + volumetricEfficiencyPct: double.NaN, + mRef_kg_s: mRef_kg_s, + w_kW: w_kW, + h1_kJkg: h1_kJkg, + s1_JmolK: s1_JmolK, + v1_m3kg: v1_m3kg, + h2_kJkg: h2_kJkg, + h3_kJkg: h3_kJkg, + h2s_kJkg: h2s_kJkg); + + error = etaVErr; + return true; + } + + result = new Result( + heatingCapacityQh_kW: qh_kW, + coolingCapacityQc_kW: qc_kW, + copHeating: copH, + copCooling: copC, + isentropicEfficiencyPct: etaS_pct, + volumetricEfficiencyPct: etaV_pct, + mRef_kg_s: mRef_kg_s, + w_kW: w_kW, + h1_kJkg: h1_kJkg, + s1_JmolK: s1_JmolK, + v1_m3kg: v1_m3kg, + h2_kJkg: h2_kJkg, + h3_kJkg: h3_kJkg, + h2s_kJkg: h2s_kJkg); + + return true; + } + + /// + /// 输出调试快照到 Console/Debug(用于人工对比验证)。 + /// + /// 标题/阶段标识。 + /// 输入。 + /// 输出结果(可为 null,表示计算失败时仅输出输入)。 + /// 错误/警告。 + public static void PrintDebugSnapshotToConsole(string title, Input input, Result? result, string error) + { + void Print(string line) + { + try { Debug.WriteLine(line); } catch { } + try { Console.WriteLine(line); } catch { } + } + + Print($"================ {title} ================"); + Print("--- Inputs ---"); + Print($"Suction : T1={input.SuctionTemp_C} °C, P1={input.SuctionPress_BarA} BarA"); + Print($"Discharge : T2={input.DischargeTemp_C} °C, P2={input.DischargePress_BarA} BarA"); + Print($"TXV-before : T3={input.TxvFrTemp_C} °C, P3={input.TxvFrPress_BarA} BarA"); + Print($"Flow : mTotal={input.TotalFlow_kg_h} kg/h"); + Print($"Power(HV) : W={input.HvPower_W} W"); + Print($"Speed : n={input.Speed_rpm} rpm"); + Print($"Disp : disp={input.Displacement_cc} cc"); + + if (result.HasValue) + { + var r = result.Value; + Print("--- Intermediate (computed) ---"); + Print($"mRef={r.MRef_kg_s:F8} kg/s, W={r.W_kW:F6} kW"); + Print($"h1={r.H1_kJkg:F6} kJ/kg"); + Print($"s1={r.S1_JmolK:F6} J/(mol·K)"); + Print($"v1={r.V1_m3kg:F8} m3/kg"); + Print($"h2={r.H2_kJkg:F6} kJ/kg"); + Print($"h3={r.H3_kJkg:F6} kJ/kg"); + Print($"h2s={r.H2s_kJkg:F6} kJ/kg"); + + Print("--- Final (computed) ---"); + Print($"Qc={r.CoolingCapacityQc_kW:F5} kW"); + Print($"COP(cool)={r.COPCooling:F5}"); + Print($"Qh={r.HeatingCapacityQh_kW:F5} kW"); + Print($"COP(heat)={r.COPHeating:F5}"); + Print($"etaS={r.IsentropicEfficiencyPct:F4} %"); + Print($"etaV={r.VolumetricEfficiencyPct:F3} %"); + } + + if (!string.IsNullOrWhiteSpace(error)) + { + Print($"--- Error/Warning ---\r\n{error}"); + } + + Print("===================================================="); + } + + private bool EnsureRefpropInitialized(out string error) + { + error = string.Empty; + if (_rpInitialized) return true; + + try + { + lock (_refpropLock) + { + if (_rpInitialized) return true; + + string hpath = ConfigHelper.GetValue("FluidsPath"); + if (string.IsNullOrWhiteSpace(hpath)) hpath = @".\PPCalculation\REFPROP\FLUIDS"; + + string configuredCryogen = ConfigHelper.GetValue("Cryogen"); + if (string.IsNullOrWhiteSpace(configuredCryogen)) configuredCryogen = "R134a"; + + string hfldCore = configuredCryogen.Equals("R134a", StringComparison.OrdinalIgnoreCase) + ? "R134A.FLD" + : "R134A.FLD"; + + long size = hpath.Length; + string hpathPadded = hpath + new string(' ', Math.Max(0, 255 - (int)size)); + IRefProp64.SETPATHdll(hpathPadded, ref size); + + long numComps = 1; + string hfld = hfldCore; + size = hfld.Length; + string hfldPadded = hfld + new string(' ', Math.Max(0, 10000 - (int)size)); + + string hfmix = "hmx.bnc" + new string(' ', 255); + string hrf = "DEF"; + string herr = new string(' ', 255); + long ierr = 0; + long hfldLen = hfldPadded.Length, hfmixLen = hfmix.Length, hrfLen = hrf.Length, herrLen = herr.Length; + + IRefProp64.SETUPdll(ref numComps, ref hfldPadded, ref hfmix, ref hrf, + ref ierr, ref herr, ref hfldLen, ref hfmixLen, ref hrfLen, ref herrLen); + if (ierr != 0) + { + error = $"REFPROP 初始化失败: {herr.Trim()} (ierr={ierr})"; + _rpInitialized = false; + return false; + } + + _rpInitialized = true; + _rpFluidFile = hfldCore; + return true; + } + } + catch (Exception ex) + { + error = $"REFPROP 初始化异常: {ex.Message}"; + _rpInitialized = false; + return false; + } + } + + private static double GetMolarMass() + { + double wmm = 0, Trp = 0, Tnbpt = 0, Tc = 0, Pc = 0, Dc = 0, Zc = 0, acf = 0, dip = 0, Rgas = 0; + long componentId = 1; + IRefProp64.INFOdll(ref componentId, ref wmm, ref Trp, ref Tnbpt, ref Tc, ref Pc, ref Dc, ref Zc, ref acf, ref dip, ref Rgas); + return wmm * 0.001; + } + + private bool TryGetCompressorPower_kW(Input input, out double w_kW, out string error) + { + w_kW = double.NaN; + error = string.Empty; + + double w_W = input.HvPower_W; + if (double.IsNaN(w_W) || double.IsInfinity(w_W) || w_W <= 0) + { + error = $"无效压缩机功率 HV[W]={w_W}"; + return false; + } + + w_kW = w_W / 1000.0; + return true; + } + + private bool TryGetRefrigerantMassFlow_kg_s(Input input, out double mRef_kg_s, out string error) + { + mRef_kg_s = double.NaN; + error = string.Empty; + + double mTotal_kg_h = input.TotalFlow_kg_h; + if (double.IsNaN(mTotal_kg_h) || double.IsInfinity(mTotal_kg_h)) + { + error = "总流量(冷媒流量)为 NaN/Inf"; + return false; + } + + double mRef_kg_h = mTotal_kg_h; + if (mRef_kg_h <= 0) + { + error = $"冷媒质量流量<=0,总流量={mTotal_kg_h}kg/h"; + return false; + } + + mRef_kg_s = mRef_kg_h / 3600.0; + return true; + } + + private bool TryGetVaporPointEnthalpy_ByTP_BarA_C(double pressureBarA, double temperatureC, out double h_kJkg, out string error) + { + h_kJkg = double.NaN; + error = string.Empty; + + if (!TryTPFLSH_ByTP_BarA_C(pressureBarA, temperatureC, out _, out var h_Jmol, out _, out _, out var flashErr)) + { + error = flashErr; + return false; + } + + if (!TryConvertH_Jmol_To_kJkg(h_Jmol, out h_kJkg, out var hErr)) + { + error = hErr; + return false; + } + + return true; + } + + private bool TryGetLiquidPointEnthalpy_ByTP_BarA_C(double pressureBarA, double temperatureC, out double h_kJkg, out string error) + { + h_kJkg = double.NaN; + error = string.Empty; + + double pMPa = pressureBarA * 0.1; + if (!TryTPRHO_LiquidDensity_ByTP_MPa_C(pMPa, temperatureC, out var d_molL, out var dErr)) + { + error = dErr; + return false; + } + + if (!TryTHERM_LiquidEnthalpy_ByTD(temperatureC, d_molL, out h_kJkg, out var hErr)) + { + error = hErr; + return false; + } + + return true; + } + + private bool TryGetVaporPointState_ByTP_BarA_C( + double pressureBarA, + double temperatureC, + out double h_kJkg, + out double s_JmolK, + out double v_m3kg, + out string error) + { + h_kJkg = double.NaN; + s_JmolK = double.NaN; + v_m3kg = double.NaN; + error = string.Empty; + + if (!TryTPFLSH_ByTP_BarA_C(pressureBarA, temperatureC, out var d_molL, out var h_Jmol, out var sOut_JmolK, out _, out var flashErr)) + { + error = flashErr; + return false; + } + + if (!TryConvertH_Jmol_To_kJkg(h_Jmol, out h_kJkg, out var hErr)) + { + error = hErr; + return false; + } + + s_JmolK = sOut_JmolK; + if (double.IsNaN(s_JmolK) || double.IsInfinity(s_JmolK)) + { + error = "无效吸气熵"; + return false; + } + + if (!TryConvertMolarDensityToSpecificVolume(d_molL, out v_m3kg, out var vErr)) + { + error = vErr; + return false; + } + + return true; + } + + private bool TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(double dischargePressureBarA, double suctionEntropy_JmolK, out double h2s_kJkg, out string error) + { + h2s_kJkg = double.NaN; + error = string.Empty; + + if (!EnsureRefpropInitialized(out var initErr)) + { + error = initErr; + return false; + } + + double pKPa = dischargePressureBarA * 100.0; + if (pKPa <= 0) + { + error = $"无效排气压力: {dischargePressureBarA} BarA"; + return false; + } + + if (double.IsNaN(suctionEntropy_JmolK) || double.IsInfinity(suctionEntropy_JmolK)) + { + error = "无效吸气熵 s1"; + return false; + } + + double s_JmolK = suctionEntropy_JmolK; + + double[] z = new double[20]; + z[0] = 1.0; + + double t = 0.0; + double d = 0.0; + double Dl = 0.0; + double Dv = 0.0; + double[] xliq = new double[20]; + double[] xvap = new double[20]; + double q = 0.0; + double ee = 0.0; + double h = 0.0; + double Cv = 0.0; + double Cp = 0.0; + double w = 0.0; + long ierr = 0; + long herrLen = 255; + string herr = new string(' ', 255); + + lock (_refpropLock) + { + IRefProp64.PSFLSHdll(ref pKPa, ref s_JmolK, z, ref t, ref d, ref Dl, ref Dv, xliq, xvap, ref q, ref ee, ref h, ref Cv, ref Cp, ref w, ref ierr, ref herr, ref herrLen); + } + + if (ierr != 0) + { + error = $"PSFLSH 错误: {herr.Trim()} (ierr={ierr})"; + return false; + } + + if (!TryConvertH_Jmol_To_kJkg(h, out h2s_kJkg, out var hConvErr)) + { + error = hConvErr; + return false; + } + + return true; + } + + private bool TryComputeCapacitiesAndCOP( + double mRef_kg_s, + double h1_kJkg, + double h2_kJkg, + double h3_kJkg, + double w_kW, + out double qh_kW, + out double qc_kW, + out double copHeating, + out double copCooling, + out string error) + { + qh_kW = double.NaN; + qc_kW = double.NaN; + copHeating = double.NaN; + copCooling = double.NaN; + error = string.Empty; + + if (mRef_kg_s <= 0 || double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s)) + { + error = "无效冷媒质量流量"; + return false; + } + if (w_kW <= 0 || double.IsNaN(w_kW) || double.IsInfinity(w_kW)) + { + error = "无效压缩机功率"; + return false; + } + + qh_kW = mRef_kg_s * (h2_kJkg - h3_kJkg); + qc_kW = mRef_kg_s * (h1_kJkg - h3_kJkg); + + copHeating = qh_kW / w_kW; + copCooling = qc_kW / w_kW; + return true; + } + + private bool TryComputeIsentropicEfficiencyPct(double mRef_kg_s, double h1_kJkg, double h2s_kJkg, double w_kW, out double etaS_pct, out string error) + { + etaS_pct = double.NaN; + error = string.Empty; + + if (double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s) || mRef_kg_s <= 0) + { + error = "无效冷媒质量流量"; + return false; + } + if (double.IsNaN(w_kW) || double.IsInfinity(w_kW) || w_kW <= 0) + { + error = "无效压缩机功率"; + return false; + } + + double dh_isentropic = h2s_kJkg - h1_kJkg; + double eta = (mRef_kg_s * dh_isentropic) / w_kW; + if (double.IsNaN(eta) || double.IsInfinity(eta)) + { + error = "等熵效率结果异常"; + return false; + } + + etaS_pct = eta * 100.0; + return true; + } + + private bool TryComputeVolumetricEfficiencyPct( + double mRef_kg_s, + double v1_m3kg, + double speed_rpm, + double displacement_cc, + out double etaV_pct, + out string error) + { + etaV_pct = double.NaN; + error = string.Empty; + + if (double.IsNaN(speed_rpm) || double.IsInfinity(speed_rpm) || speed_rpm <= 0) + { + error = $"无效转速: {speed_rpm} rpm"; + return false; + } + + if (double.IsNaN(displacement_cc) || double.IsInfinity(displacement_cc) || displacement_cc <= 0) + { + error = $"无效排量: {displacement_cc} cc"; + return false; + } + + if (double.IsNaN(v1_m3kg) || double.IsInfinity(v1_m3kg) || v1_m3kg <= 0) + { + error = "无效吸气比容"; + return false; + } + + if (double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s) || mRef_kg_s <= 0) + { + error = "无效冷媒质量流量"; + return false; + } + + double suctionVolFlow_m3_h = (mRef_kg_s * 3600.0) * v1_m3kg; + double theoVolFlow_m3_h = (speed_rpm / 60.0) * displacement_cc * 0.0036; + if (theoVolFlow_m3_h <= 0) + { + error = "理论吸气体积流量<=0"; + return false; + } + + etaV_pct = (suctionVolFlow_m3_h / theoVolFlow_m3_h) * 100.0; + return true; + } + + private bool TryTPFLSH_ByTP_BarA_C( + double pressureBarA, + double temperatureC, + out double d_molL, + out double h_Jmol, + out double s_JmolK, + out double t_K, + out string error) + { + d_molL = double.NaN; + h_Jmol = double.NaN; + s_JmolK = double.NaN; + t_K = double.NaN; + error = string.Empty; + + if (!EnsureRefpropInitialized(out var initErr)) + { + error = initErr; + return false; + } + + double t = temperatureC + 273.15; + double pKPa = pressureBarA * 100.0; + + if (pKPa <= 0) + { + error = $"无效压力: {pressureBarA} BarA"; + return false; + } + + double[] x = new double[20]; + x[0] = 1.0; + + double d = 0.0; + double Dl = 0.0; + double Dv = 0.0; + double[] xliq = new double[20]; + double[] xvap = new double[20]; + double q = 0.0; + double ee = 0.0; + double h = 0.0; + double ss = 0.0; + double Cv = 0.0; + double Cp = 0.0; + double w = 0.0; + long ierr = 0; + long herrLen = 255; + string herr = new string(' ', 255); + + lock (_refpropLock) + { + IRefProp64.TPFLSHdll(ref t, ref pKPa, x, ref d, ref Dl, ref Dv, xliq, xvap, ref q, ref ee, ref h, ref ss, ref Cv, ref Cp, ref w, ref ierr, ref herr, ref herrLen); + } + + if (ierr != 0) + { + error = $"TPFLSH 错误: {herr.Trim()} (ierr={ierr})"; + return false; + } + + d_molL = d; + h_Jmol = h; + s_JmolK = ss; + t_K = t; + return true; + } + + private bool TryConvertH_Jmol_To_kJkg(double h_Jmol, out double h_kJkg, out string error) + { + h_kJkg = double.NaN; + error = string.Empty; + + double molarMassKgPerMol = GetMolarMass(); + if (molarMassKgPerMol <= 0) + { + error = "无效的摩尔质量"; + return false; + } + + h_kJkg = (h_Jmol / molarMassKgPerMol) * 0.001; + return true; + } + + private bool TryConvertMolarDensityToSpecificVolume(double d_molL, out double v_m3kg, out string error) + { + v_m3kg = double.NaN; + error = string.Empty; + + if (double.IsNaN(d_molL) || double.IsInfinity(d_molL) || d_molL <= 0) + { + error = $"无效摩尔密度: {d_molL} mol/L"; + return false; + } + + double molarMassKgPerMol = GetMolarMass(); + if (molarMassKgPerMol <= 0) + { + error = "无效的摩尔质量"; + return false; + } + + double rho_kg_m3 = d_molL * molarMassKgPerMol * 1000.0; + if (rho_kg_m3 <= 0) + { + error = $"无效密度: {rho_kg_m3} kg/m3"; + return false; + } + + v_m3kg = 1.0 / rho_kg_m3; + return true; + } + + private bool TryTPRHO_LiquidDensity_ByTP_MPa_C(double pressureMPa, double temperatureC, out double densityMolPerL, out string error) + { + densityMolPerL = double.NaN; + error = string.Empty; + + double tK = temperatureC + 273.15; + double pKPa = pressureMPa * 1000.0; + + double[] x = new double[20]; + x[0] = 1.0; + + long kph = 1; + long kguess = 0; + double D = 0.0; + long ierr = 0; + long herrLen = 255; + string herr = new string(' ', 255); + + lock (_refpropLock) + { + IRefProp64.TPRHOdll(ref tK, ref pKPa, x, ref kph, ref kguess, ref D, ref ierr, ref herr, ref herrLen); + } + + if (ierr != 0) + { + error = $"TPRHO(液相) 错误: {herr.Trim()} (ierr={ierr})"; + return false; + } + + densityMolPerL = D; + return true; + } + + private bool TryTHERM_LiquidEnthalpy_ByTD(double temperatureC, double densityMolPerL, out double h_liq_kJ_per_kg, out string error) + { + h_liq_kJ_per_kg = double.NaN; + error = string.Empty; + + double tK = temperatureC + 273.15; + double D = densityMolPerL; + + double[] x = new double[20]; + x[0] = 1.0; + + double pOut = 0, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0; + + double molarMassKgPerMol = GetMolarMass(); + if (molarMassKgPerMol <= 0) + { + error = "无效的摩尔质量"; + return false; + } + + lock (_refpropLock) + { + IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt); + } + + h_liq_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001; + return true; + } + + private bool TrySATP_SaturationTemperatureK_ByP_BarA(double pressureBarA, out double tSatK, out string error) + { + tSatK = double.NaN; + error = string.Empty; + + if (!EnsureRefpropInitialized(out var initErr)) + { + error = initErr; + return false; + } + + double pKPa = pressureBarA * 100.0; + if (pKPa <= 0) + { + error = $"无效压力: {pressureBarA} BarA"; + return false; + } + + double[] x = new double[20]; + x[0] = 1.0; + + long kph = 1; + double t = 0.0; + double Dl = 0.0; + double Dv = 0.0; + double[] xliq = new double[20]; + double[] xvap = new double[20]; + long ierr = 0; + long herrLen = 255; + string herr = new string(' ', 255); + + lock (_refpropLock) + { + IRefProp64.SATPdll(ref pKPa, x, ref kph, ref t, ref Dl, ref Dv, xliq, xvap, ref ierr, ref herr, ref herrLen); + } + + if (ierr != 0) + { + error = $"SATP 错误: {herr.Trim()} (ierr={ierr})"; + return false; + } + + tSatK = t; + return true; + } + + private bool TryGetTxvFrTempForH3_ByDischargePress_BarA(double dischargePressBarA, out double txvFrTempForH3_C, out string warning) + { + txvFrTempForH3_C = double.NaN; + warning = string.Empty; + + if (!TrySATP_SaturationTemperatureK_ByP_BarA(dischargePressBarA, out var tSatK, out var satErr)) + { + warning = satErr; + return false; + } + + double tSatC = tSatK - 273.15; + double offsetC = _h3TempOffset_C; + txvFrTempForH3_C = tSatC + offsetC; + return true; + } + + /// + /// 生成一段便于保存/对比的文本报告。 + /// + /// 标题。 + /// 输入。 + /// 输出。 + /// 错误/警告。 + /// 报告文本。 + public static string BuildDebugReport(string title, Input input, Result? result, string error) + { + var sb = new StringBuilder(512); + sb.AppendLine($"================ {title} ================"); + sb.AppendLine("--- Inputs ---"); + sb.AppendLine($"Suction : T1={input.SuctionTemp_C} °C, P1={input.SuctionPress_BarA} BarA"); + sb.AppendLine($"Discharge : T2={input.DischargeTemp_C} °C, P2={input.DischargePress_BarA} BarA"); + sb.AppendLine($"TXV-before : T3={input.TxvFrTemp_C} °C, P3={input.TxvFrPress_BarA} BarA"); + sb.AppendLine($"Flow : mTotal={input.TotalFlow_kg_h} kg/h"); + sb.AppendLine($"Power(HV) : W={input.HvPower_W} W"); + sb.AppendLine($"Speed : n={input.Speed_rpm} rpm"); + sb.AppendLine($"Disp : disp={input.Displacement_cc} cc"); + + if (result.HasValue) + { + var r = result.Value; + sb.AppendLine("--- Intermediate (computed) ---"); + sb.AppendLine($"mRef={r.MRef_kg_s:F8} kg/s, W={r.W_kW:F6} kW"); + sb.AppendLine($"h1={r.H1_kJkg:F6} kJ/kg"); + sb.AppendLine($"s1={r.S1_JmolK:F6} J/(mol·K)"); + sb.AppendLine($"v1={r.V1_m3kg:F8} m3/kg"); + sb.AppendLine($"h2={r.H2_kJkg:F6} kJ/kg"); + sb.AppendLine($"h3={r.H3_kJkg:F6} kJ/kg"); + sb.AppendLine($"h2s={r.H2s_kJkg:F6} kJ/kg"); + + sb.AppendLine("--- Final (computed) ---"); + sb.AppendLine($"Qc={r.CoolingCapacityQc_kW:F5} kW"); + sb.AppendLine($"COP(cool)={r.COPCooling:F5}"); + sb.AppendLine($"Qh={r.HeatingCapacityQh_kW:F5} kW"); + sb.AppendLine($"COP(heat)={r.COPHeating:F5}"); + sb.AppendLine($"etaS={r.IsentropicEfficiencyPct:F4} %"); + sb.AppendLine($"etaV={r.VolumetricEfficiencyPct:F3} %"); + } + + if (!string.IsNullOrWhiteSpace(error)) + { + sb.AppendLine("--- Error/Warning ---"); + sb.AppendLine(error); + } + + sb.AppendLine("===================================================="); + return sb.ToString(); + } + } +} diff --git a/CapMachine.Wpf/Services/PPCService.cs b/CapMachine.Wpf/Services/PPCService.cs index 8be10c3..89d1707 100644 --- a/CapMachine.Wpf/Services/PPCService.cs +++ b/CapMachine.Wpf/Services/PPCService.cs @@ -34,16 +34,9 @@ namespace CapMachine.Wpf.Services public ILogService Logger { get; } public MachineRtDataService MachineRtDataService { get; } public IDialogService DialogService { get; } - private readonly object _refpropLock = new object(); - private readonly PPCSuperheatSubcoolCalculator _superheatSubcoolCalculator; - private readonly PPCThermodynamicSixResultsCalculator _thermodynamicSixResultsCalculator; - private readonly EnthalpyDrynessCalculator _enthalpyDrynessCalculator; - - private DateTime _lastSuperheatSubcoolErrorLogAt = DateTime.MinValue; - private string _lastSuperheatSubcoolErrorText = string.Empty; - - private int _hvZeroStreak; - + private readonly SuperheatSubcoolCalculator _superheatSubcoolCalculator; + private readonly ThermodynamicSixResultsCalculator _thermodynamicSixResultsCalculator; + /// /// 标签中心 /// @@ -114,22 +107,24 @@ namespace CapMachine.Wpf.Services Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]"); - HeatingCapacity = TagManager.DicTags.GetValueOrDefault("制热量Qh[KW]") + HeatingCapacityTag = TagManager.DicTags.GetValueOrDefault("制热量Qh[KW]") ?? TagManager.DicTags.GetValueOrDefault("制热量Qh[W]"); - COPHeat = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制热)[K]") + COPHeatTag = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制热)[K]") ?? TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制热COP)"); - IsentrpEff = TagManager.DicTags.GetValueOrDefault("等熵效率ns[%]"); - CoolCapacity = TagManager.DicTags.GetValueOrDefault("制冷量Qc[KW]") + IsentrpEffTag = TagManager.DicTags.GetValueOrDefault("等熵效率ns[%]"); + CoolCapacityTag = TagManager.DicTags.GetValueOrDefault("制冷量Qc[KW]") ?? TagManager.DicTags.GetValueOrDefault("制冷量Qc[W]"); - COPCool = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制冷)[K]") + COPCoolTag = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制冷)[K]") ?? TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制冷COP)"); - VoltricEff = TagManager.DicTags.GetValueOrDefault("容积效率nv[%]"); + VoltricEffTag = TagManager.DicTags.GetValueOrDefault("容积效率nv[%]"); + + + _superheatSubcoolCalculator = new SuperheatSubcoolCalculator(_refpropLock); + _thermodynamicSixResultsCalculator = new ThermodynamicSixResultsCalculator(_refpropLock); SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath"); SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen"); - _superheatSubcoolCalculator = new PPCSuperheatSubcoolCalculator(_refpropLock); - _thermodynamicSixResultsCalculator = new PPCThermodynamicSixResultsCalculator(_refpropLock); - _enthalpyDrynessCalculator = new EnthalpyDrynessCalculator(_refpropLock); + ReloadTherdyH3TempOffset(); RtScanDeviceStart(); @@ -143,16 +138,16 @@ namespace CapMachine.Wpf.Services try { string raw = ConfigHelper.GetValue(TherdyH3TempOffsetConfigKey); - if (!string.IsNullOrWhiteSpace(raw) && double.TryParse(raw, out var parsed)) + if (!string.IsNullOrWhiteSpace(raw) && double.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed)) { offsetC = parsed; } } - catch (Exception ex) + catch { - Logger?.Error($"读取 {TherdyH3TempOffsetConfigKey} 失败: {ex.Message}"); } + TherdyH3TempOffset_C = offsetC; _thermodynamicSixResultsCalculator.SetH3TempOffset_C(offsetC); } @@ -170,6 +165,17 @@ namespace CapMachine.Wpf.Services ConfigHelper.SetValue("Cryogen", SuperHeatCoolConfig.Cryogen); } + private double _therdyH3TempOffset_C = -10.0; + public double TherdyH3TempOffset_C + { + get { return _therdyH3TempOffset_C; } + private set + { + _therdyH3TempOffset_C = value; + RaisePropertyChanged(); + } + } + /// /// 吸气压力 /// @@ -261,27 +267,19 @@ namespace CapMachine.Wpf.Services /// public ITag LubeFlowTag { get; set; } - public ITag HeatingCapacity { get; set; } - public ITag COPHeat { get; set; } - public ITag IsentrpEff { get; set; } - public ITag CoolCapacity { get; set; } - public ITag COPCool { get; set; } - public ITag VoltricEff { get; set; } + + public ITag? HeatingCapacityTag { get; set; } - /// - /// 风量数据-乘以系数的后的最终结果 - /// - private double AirVolumeData { get; set; } = 0.0; + public ITag? COPHeatTag { get; set; } - /// - /// 风量数据-公式计算的原始数据 - /// - private double AirVolumeDataSource { get; set; } = 0; + public ITag? IsentrpEffTag { get; set; } + + public ITag? CoolCapacityTag { get; set; } + + public ITag? COPCoolTag { get; set; } + + public ITag? VoltricEffTag { get; set; } - /// - /// 风量喷嘴启用信息数据 - /// - private string RozzleEnableInfo { get; set; } = ""; /// /// 启用计算 @@ -293,7 +291,21 @@ namespace CapMachine.Wpf.Services /// private bool DebugLog { get; set; } = false; + private int _CurDisplacementCc; + /// + /// 当前的排量信息(供 UI 展示) + /// + public int CurDisplacementCc + { + get { return _CurDisplacementCc; } + set { _CurDisplacementCc = value; RaisePropertyChanged(); } + } + + private double _HeatingCapacityQh_kW; + /// + /// 制热量 Qh [kW] + /// public double HeatingCapacityQh_kW { get { return _HeatingCapacityQh_kW; } @@ -301,6 +313,9 @@ namespace CapMachine.Wpf.Services } private double _COPHeating; + /// + /// 压缩机性能系数 COP(制热)[-] + /// public double COPHeating { get { return _COPHeating; } @@ -308,6 +323,9 @@ namespace CapMachine.Wpf.Services } private double _IsentropicEfficiencyPct; + /// + /// 等熵效率 ηs [%] + /// public double IsentropicEfficiencyPct { get { return _IsentropicEfficiencyPct; } @@ -315,6 +333,9 @@ namespace CapMachine.Wpf.Services } private double _CoolingCapacityQc_kW; + /// + /// 制冷量 Qc [kW] + /// public double CoolingCapacityQc_kW { get { return _CoolingCapacityQc_kW; } @@ -322,6 +343,9 @@ namespace CapMachine.Wpf.Services } private double _COPCooling; + /// + /// 压缩机性能系数 COP(制冷)[-] + /// public double COPCooling { get { return _COPCooling; } @@ -329,6 +353,9 @@ namespace CapMachine.Wpf.Services } private double _VolumetricEfficiencyPct; + /// + /// 容积效率 ηv [%] + /// public double VolumetricEfficiencyPct { get { return _VolumetricEfficiencyPct; } @@ -347,39 +374,30 @@ namespace CapMachine.Wpf.Services await Task.Delay(300); try { - _ = TryUpdateSuperheatAndSubcool(out var superheatSubcoolErr); - if (!string.IsNullOrWhiteSpace(superheatSubcoolErr)) + double[] x = new double[20]; + double wm = 0.0; + + // 幂等初始化:仅首次或工质/路径变化时执行 SETPATH/SETUP,提高每秒循环效率 + if (!EnsureRefpropInitialized(out var initErr)) { - var now = DateTime.UtcNow; - if (!string.Equals(superheatSubcoolErr, _lastSuperheatSubcoolErrorText, StringComparison.Ordinal) - || (now - _lastSuperheatSubcoolErrorLogAt) >= TimeSpan.FromSeconds(5)) - { - Logger?.Error($"过热度/过冷度计算失败: {superheatSubcoolErr}"); - _lastSuperheatSubcoolErrorLogAt = now; - _lastSuperheatSubcoolErrorText = superheatSubcoolErr; - } + // 初始化失败,跳过本周期 + Logger?.Error($"REFPROP 初始化失败: {initErr}"); + continue; } - //if (TryUpdateDryness(out var drynessErr)) - //{ - // if (!string.IsNullOrWhiteSpace(drynessErr)) - // { - // Logger?.Error($"干度计算警告: {drynessErr}"); - // } - //} - //else - //{ - // if (!string.IsNullOrWhiteSpace(drynessErr)) - // { - // Logger?.Error($"干度计算失败: {drynessErr}"); - // } - //} + // WMOL 仅在需要时调用;若调用,需设置 x[0]=1.0(纯工质) + x[0] = 1.0; + IRefProp64.WMOLdll(x, ref wm); + + UpdateSuperheatAndSubcool_BySatp(); + + if (TryUpdateThermodynamicSixResults(out var thermoErr)) { if (!string.IsNullOrWhiteSpace(thermoErr)) { - //Logger?.Error($"六个物性结果计算警告: {thermoErr}"); + //Logger?.Warn($"六个物性结果计算警告: {thermoErr}"); } } else @@ -399,121 +417,6 @@ namespace CapMachine.Wpf.Services }); } - private bool TryUpdateSuperheatAndSubcool(out string error) - { - error = string.Empty; - bool updated = false; - StringBuilder errorBuilder = new StringBuilder(); - - if (InhPressTag == null || InhTempTag == null || Superheat == null) - { - AppendCalculationError(errorBuilder, "缺少过热度计算标签"); - } - else if (_superheatSubcoolCalculator.TryCalculateSuperheatK(InhPressTag.EngPvValue, InhTempTag.EngPvValue, out var superheatValue, out var superheatErr)) - { - Superheat.EngPvValue = Math.Abs(superheatValue); - updated = true; - } - else - { - AppendCalculationError(errorBuilder, superheatErr); - } - - if (TxvFrPressTag == null || TxvFrTempTag == null || Subcool == null) - { - AppendCalculationError(errorBuilder, "缺少过冷度计算标签"); - } - else if (_superheatSubcoolCalculator.TryCalculateSubcoolK(TxvFrPressTag.EngPvValue, TxvFrTempTag.EngPvValue, out var subcoolValue, out var subcoolErr)) - { - Subcool.EngPvValue = Math.Abs(subcoolValue); - updated = true; - } - else - { - AppendCalculationError(errorBuilder, subcoolErr); - } - - error = errorBuilder.ToString(); - return updated; - } - - private static void AppendCalculationError(StringBuilder errorBuilder, string error) - { - if (string.IsNullOrWhiteSpace(error)) - { - return; - } - - if (errorBuilder.Length > 0) - { - errorBuilder.Append(";"); - } - - errorBuilder.Append(error); - } - - private bool TryUpdateDryness(out string error) - { - error = string.Empty; - bool updated = false; - StringBuilder errorBuilder = new StringBuilder(); - - if (GasPreValvePressTag == null || GasPreValveTempTag == null || TxvFrPressTag == null || TxvFrTempTag == null || InhPressTag == null) - { - AppendCalculationError(errorBuilder, "缺少干度计算压力/温度标签"); - error = errorBuilder.ToString(); - return false; - } - if (VRVTag == null || LiqRefFlowTag == null) - { - AppendCalculationError(errorBuilder, "缺少干度计算流量标签"); - error = errorBuilder.ToString(); - return false; - } - - double lubeFlowKgPerH = 0.0; - if (LubeFlowTag != null) - { - lubeFlowKgPerH = LubeFlowTag.EngPvValue; - } - - var drynessResult = _enthalpyDrynessCalculator.Calculate( - new EnthalpyDrynessCalculator.Input( - gasPreValvePressBarA: GasPreValvePressTag.EngPvValue, - gasPreValveTempC: GasPreValveTempTag.EngPvValue, - txvFrPressBarA: TxvFrPressTag.EngPvValue, - txvFrTempC: TxvFrTempTag.EngPvValue, - inhPressBarA: InhPressTag.EngPvValue, - vrvFlowKgPerH: VRVTag.EngPvValue, - liqRefFlowKgPerH: LiqRefFlowTag.EngPvValue, - lubeFlowKgPerH: lubeFlowKgPerH)); - - if (drynessResult.IsDryness1Success) - { - if (DrynessTag != null) - { - DrynessTag.EngPvValue = Math.Round(drynessResult.Dryness1_01, 4); - } - updated = true; - } - else - { - AppendCalculationError(errorBuilder, drynessResult.Error1); - } - - if (drynessResult.IsDryness2Success) - { - DrynessTag2Value = Math.Round(drynessResult.Dryness2_01, 4); - updated = true; - } - else - { - AppendCalculationError(errorBuilder, drynessResult.Error2); - } - - error = errorBuilder.ToString(); - return updated; - } /// /// 按流程图更新:制热量、COP(制热)、等熵效率、制冷量、COP(制冷)、容积效率。 @@ -532,132 +435,21 @@ namespace CapMachine.Wpf.Services { error = string.Empty; - double w_W = HVPwTag?.EngPvValue ?? double.NaN; - if (!double.IsNaN(w_W) && !double.IsInfinity(w_W) && w_W == 0) + if (InhPressTag == null || InhTempTag == null) { - _hvZeroStreak = Math.Min(_hvZeroStreak + 1, 1000); - if (_hvZeroStreak < 3) - { - return false; - } - - HeatingCapacityQh_kW = 0; - CoolingCapacityQc_kW = 0; - COPHeating = 0; - COPCooling = 0; - IsentropicEfficiencyPct = 0; - VolumetricEfficiencyPct = 0; - - if (HeatingCapacity != null) - { - HeatingCapacity.EngPvValue = 0; - } - if (COPHeat != null) - { - COPHeat.EngPvValue = 0; - } - if (IsentrpEff != null) - { - IsentrpEff.EngPvValue = 0; - } - if (CoolCapacity != null) - { - CoolCapacity.EngPvValue = 0; - } - if (COPCool != null) - { - COPCool.EngPvValue = 0; - } - if (VoltricEff != null) - { - VoltricEff.EngPvValue = 0; - } - - return true; - } - - _hvZeroStreak = 0; - - // 先把本周期所需的标签值组装为输入模型。 - // PPCService 在这里负责完成“标签 -> 领域输入对象”的映射, - // 从而把计算类和 UI/标签层解耦。 - if (!TryCreateThermodynamicSixResultsInput(out var input, out var inputErr)) - { - error = inputErr; + error = "缺少吸气压力/吸气温度标签"; return false; } - - // 把输入模型交给独立计算类统一完成 6 个结果的热力计算。 - // 此处不再在 PPCService 中展开公式,只保留流程调度职责。 - if (!_thermodynamicSixResultsCalculator.TryCalculate(input, out var result, out error)) + if (TxvFrPressTag == null || TxvFrTempTag == null) { + error = "缺少膨胀阀前压力/膨胀阀前温度标签"; return false; } - - // 先把结果同步到服务属性,供界面绑定或其他业务读取。 - HeatingCapacityQh_kW = result.HeatingCapacityQh_kW; - CoolingCapacityQc_kW = result.CoolingCapacityQc_kW; - COPHeating = result.COPHeating; - COPCooling = result.COPCooling; - IsentropicEfficiencyPct = result.IsentropicEfficiencyPct; - - // 再把结果回写到对应工程量标签。 - if (HeatingCapacity != null) + if (ExPressTag == null || ExTempTag == null) { - HeatingCapacity.EngPvValue = string.Equals(HeatingCapacity.Unit, "W", StringComparison.OrdinalIgnoreCase) - ? HeatingCapacityQh_kW * 1000.0 - : HeatingCapacityQh_kW; + error = "缺少排气压力/排气温度标签"; + return false; } - if (COPHeat != null) - { - COPHeat.EngPvValue = COPHeating; - } - if (IsentrpEff != null) - { - IsentrpEff.EngPvValue = IsentropicEfficiencyPct; - } - if (CoolCapacity != null) - { - CoolCapacity.EngPvValue = string.Equals(CoolCapacity.Unit, "W", StringComparison.OrdinalIgnoreCase) - ? CoolingCapacityQc_kW * 1000.0 - : CoolingCapacityQc_kW; - } - if (COPCool != null) - { - COPCool.EngPvValue = COPCooling; - } - - // 容积效率在旧逻辑中允许单独失败; - // 因此只有拿到有效数值时才回写,避免覆盖已有结果为 NaN/Inf。 - if (!double.IsNaN(result.VolumetricEfficiencyPct) && !double.IsInfinity(result.VolumetricEfficiencyPct)) - { - VolumetricEfficiencyPct = result.VolumetricEfficiencyPct; - if (VoltricEff != null) - { - VoltricEff.EngPvValue = VolumetricEfficiencyPct; - } - } - - return true; - } - - /// - /// 创建六个热力结果值计算所需的输入模型。 - /// - /// - /// 输出输入模型。 - /// 该对象是 之间的数据边界: - /// 由服务层负责从标签提取实时值,由计算层只消费该对象而不直接依赖标签。 - /// - /// 失败原因,通常为缺少标签、缺少排量配置或关键输入不可用。 - /// 是否创建成功。 - private bool TryCreateThermodynamicSixResultsInput(out PPCThermodynamicSixResultsCalculationInput input, out string error) - { - input = new PPCThermodynamicSixResultsCalculationInput(); - error = string.Empty; - - // 依次校验本周期计算所需的全部标签。 - // 这里不做公式计算,只保证独立计算类拿到的是一套完整输入。 if (HVPwTag == null) { error = "缺少 HV[W] 功率标签"; @@ -668,56 +460,228 @@ namespace CapMachine.Wpf.Services error = "缺少总流量(冷媒流量)标签"; return false; } - if (InhPressTag == null || InhTempTag == null) + + + + double suctionPress_BarA = InhPressTag.EngPvValue; + double suctionTemp_C = InhTempTag.EngPvValue; + double dischargePress_BarA = ExPressTag.EngPvValue; + double dischargeTemp_C = ExTempTag.EngPvValue; + double txvFrPress_BarA = TxvFrPressTag.EngPvValue; + double txvFrTemp_C = TxvFrTempTag.EngPvValue; + double totalFlow_kg_h = VRVTag.EngPvValue; + double w_W = HVPwTag.EngPvValue; + + double speed_rpm = SpeedTag?.EngPvValue ?? double.NaN; + if (!TryGetCompressorDisplacement_cc(out var disp_cc, out var dispErr)) { - error = "缺少吸气压力/吸气温度标签"; + error = dispErr; return false; } - if (ExPressTag == null || ExTempTag == null) - { - error = "缺少排气压力/排气温度标签"; - return false; - } - if (TxvFrPressTag == null || TxvFrTempTag == null) - { - error = "缺少膨胀阀前压力/膨胀阀前温度标签"; - return false; - } - if (SpeedTag == null) - { - error = "缺少转速标签"; - return false; - } - if (!TryGetCompressorDisplacement_cc(out var displacementCc, out _)) - { - displacementCc = double.NaN; - } - double oilFlowKgPerH = 0.0; - if (LubeFlowTag != null) + //这里把输入数据写死计算出结果。 + //#if DEBUG + // // 附件截图输入(单位换算:MPa -> BarA,kW -> W) + // suctionPress_BarA = 0.3 * 10.0; + // suctionTemp_C = 10.8; + // dischargePress_BarA = 1.502 * 10.0; + // dischargeTemp_C = 88.4; + // txvFrPress_BarA = 1.494 * 10.0; + // txvFrTemp_C = 45.0; + // totalFlow_kg_h = 100.1; + // w_W = 1.289 * 1000.0; + // speed_rpm = 3004; + // disp_cc = 35; + //#endif + + if (!double.IsNaN(w_W) && !double.IsInfinity(w_W) && w_W == 0) { - oilFlowKgPerH = LubeFlowTag.EngPvValue; + HeatingCapacityQh_kW = 0; + CoolingCapacityQc_kW = 0; + COPHeating = 0; + COPCooling = 0; + IsentropicEfficiencyPct = 0; + VolumetricEfficiencyPct = 0; + + if (HeatingCapacityTag != null) + { + HeatingCapacityTag.EngPvValue = 0; + } + if (COPHeatTag != null) + { + COPHeatTag.EngPvValue = 0; + } + if (IsentrpEffTag != null) + { + IsentrpEffTag.EngPvValue = 0; + } + if (CoolCapacityTag != null) + { + CoolCapacityTag.EngPvValue = 0; + } + if (COPCoolTag != null) + { + COPCoolTag.EngPvValue = 0; + } + if (VoltricEffTag != null) + { + VoltricEffTag.EngPvValue = 0; + } + + return true; } - // 将实时标签值与配置值组装为独立计算类可直接消费的输入对象。 - input = new PPCThermodynamicSixResultsCalculationInput - { - CompressorPowerW = HVPwTag.EngPvValue, - TotalMassFlowKgPerHour = VRVTag.EngPvValue, - OilMassFlowKgPerHour = oilFlowKgPerH, - SuctionPressureBarA = InhPressTag.EngPvValue, - SuctionTemperatureC = InhTempTag.EngPvValue, - DischargePressureBarA = ExPressTag.EngPvValue, - DischargeTemperatureC = ExTempTag.EngPvValue, - LiquidPressureBarA = TxvFrPressTag.EngPvValue, - LiquidTemperatureC = TxvFrTempTag.EngPvValue, - CompressorSpeedRpm = SpeedTag.EngPvValue, - CompressorDisplacementCc = displacementCc - }; + var calcInput = new ThermodynamicSixResultsCalculator.Input( + suctionPress_BarA: suctionPress_BarA, + suctionTemp_C: suctionTemp_C, + dischargePress_BarA: dischargePress_BarA, + dischargeTemp_C: dischargeTemp_C, + txvFrPress_BarA: txvFrPress_BarA, + txvFrTemp_C: txvFrTemp_C, + hvPower_W: w_W, + totalFlow_kg_h: totalFlow_kg_h, + speed_rpm: speed_rpm, + displacement_cc: disp_cc); + if (!_thermodynamicSixResultsCalculator.TryCalculate(calcInput, out var r, out var calcErr)) + { + error = calcErr; + return false; + } + + HeatingCapacityQh_kW = r.HeatingCapacityQh_kW; + CoolingCapacityQc_kW = r.CoolingCapacityQc_kW; + COPHeating = r.COPHeating; + COPCooling = r.COPCooling; + IsentropicEfficiencyPct = r.IsentropicEfficiencyPct; + + if (HeatingCapacityTag != null) + { + HeatingCapacityTag.EngPvValue = HeatingCapacityQh_kW * 1000.0; + } + if (COPHeatTag != null) + { + COPHeatTag.EngPvValue = COPHeating; + } + if (IsentrpEffTag != null) + { + IsentrpEffTag.EngPvValue = IsentropicEfficiencyPct; + } + if (CoolCapacityTag != null) + { + CoolCapacityTag.EngPvValue = CoolingCapacityQc_kW * 1000.0; + } + if (COPCoolTag != null) + { + COPCoolTag.EngPvValue = COPCooling; + } + + if (double.IsNaN(r.VolumetricEfficiencyPct) || double.IsInfinity(r.VolumetricEfficiencyPct)) + { + VolumetricEfficiencyPct = double.NaN; + error = calcErr; + return true; + } + + VolumetricEfficiencyPct = r.VolumetricEfficiencyPct; + if (VoltricEffTag != null) + { + VoltricEffTag.EngPvValue = VolumetricEfficiencyPct; + } + + error = calcErr; return true; } + + private void UpdateSuperheatAndSubcool_BySatp() + { + _superheatSubcoolCalculator.Calculate( + inhPressBarA: InhPressTag.EngPvValue, + inhTempC: InhTempTag.EngPvValue, + txvFrPressBarA: TxvFrPressTag.EngPvValue, + txvFrTempC: TxvFrTempTag.EngPvValue, + out var superheatK, + out var subcoolK); + + Superheat.EngPvValue = superheatK; + Subcool.EngPvValue = subcoolK; + } + + + // 若类中尚未定义,请添加全局互斥锁,串行化所有 REFPROP 调用 + private static readonly object _refpropLock = new object(); + + // REFPROP 初始化状态(全局、幂等) + private static volatile bool _rpInitialized = false; + + + /// + /// 幂等初始化:设置流体路径/工质/参考态;确保全局只初始化一次。 + /// 注意:所有 REFPROP 原生调用都需在 _refpropLock 下串行化,包括初始化调用。 + /// + private bool EnsureRefpropInitialized(out string error) + { + error = string.Empty; + if (_rpInitialized) return true; + + try + { + lock (_refpropLock) + { + if (_rpInitialized) return true; // 双检,避免并发二次初始化 + + string hpath = ConfigHelper.GetValue("FluidsPath"); + if (string.IsNullOrWhiteSpace(hpath)) hpath = @".\PPCalculation\REFPROP\FLUIDS"; + + string configuredCryogen = ConfigHelper.GetValue("Cryogen"); + if (string.IsNullOrWhiteSpace(configuredCryogen)) configuredCryogen = "R134a"; + + // 现阶段仅使用 R134A.FLD;如需扩展,可根据 configuredCryogen 选择不同文件 + string hfldCore = configuredCryogen.Equals("R134a", StringComparison.OrdinalIgnoreCase) + ? "R134A.FLD" + : "R134A.FLD"; + + long size = hpath.Length; + string hpathPadded = hpath + new string(' ', Math.Max(0, 255 - (int)size)); + IRefProp64.SETPATHdll(hpathPadded, ref size); + + long numComps = 1; + string hfld = hfldCore; + size = hfld.Length; + string hfldPadded = hfld + new string(' ', Math.Max(0, 10000 - (int)size)); + + string hfmix = "hmx.bnc" + new string(' ', 255); + string hrf = "DEF"; + string herr = new string(' ', 255); + long ierr = 0; + long hfldLen = hfldPadded.Length, hfmixLen = hfmix.Length, hrfLen = hrf.Length, herrLen = herr.Length; + + IRefProp64.SETUPdll(ref numComps, ref hfldPadded, ref hfmix, ref hrf, + ref ierr, ref herr, ref hfldLen, ref hfmixLen, ref hrfLen, ref herrLen); + if (ierr != 0) + { + error = $"REFPROP 初始化失败: {herr.Trim()} (ierr={ierr})"; + _rpInitialized = false; + return false; + } + + _rpInitialized = true; + return true; + } + } + catch (Exception ex) + { + error = $"REFPROP 初始化异常: {ex.Message}"; + Logger.Error(error); + _rpInitialized = false; + return false; + } + } + + + + /// /// 获取压缩机排量。 /// @@ -775,5 +739,7 @@ namespace CapMachine.Wpf.Services displacement_cc = defaultDisplacementCc; return true; } + + } } diff --git a/CapMachine.Wpf/Services/PPCSuperheatSubcoolCalculator.cs b/CapMachine.Wpf/Services/PPCSuperheatSubcoolCalculator.cs deleted file mode 100644 index c8c5efa..0000000 --- a/CapMachine.Wpf/Services/PPCSuperheatSubcoolCalculator.cs +++ /dev/null @@ -1,409 +0,0 @@ -using CapMachine.Core; -using CapMachine.Wpf.PPCalculation; -using System; - -namespace CapMachine.Wpf.Services -{ - /// - /// 过热度 / 过冷度联合计算的输入数据。 - /// - /// - /// 该输入对象只承载计算所需的原始测点数据,不负责任何单位换算或有效性判断。 - /// 计算时各字段的单位约定如下: - /// - :吸气压力,单位 BarA(绝压) - /// - :吸气温度,单位 ℃ - /// - :膨胀阀前液路压力,单位 BarA(绝压) - /// - :膨胀阀前液路温度,单位 ℃ - /// - public sealed class PPCSuperheatSubcoolCalculationInput - { - /// - /// 吸气压力,单位 BarA(绝压)。 - /// - public double SuctionPressureBarA { get; set; } - - /// - /// 吸气温度,单位 ℃。 - /// - public double SuctionTemperatureC { get; set; } - - /// - /// 液路压力(膨胀阀前压力),单位 BarA(绝压)。 - /// - public double LiquidPressureBarA { get; set; } - - /// - /// 液路温度(膨胀阀前温度),单位 ℃。 - /// - public double LiquidTemperatureC { get; set; } - } - - /// - /// 过热度 / 过冷度联合计算结果。 - /// - /// - /// 本类仅承载计算后的结果值,默认初始化为 , - /// 以便调用方区分“尚未赋值”和“计算结果为有效数值”两种状态。 - /// - public sealed class PPCSuperheatSubcoolCalculationResult - { - /// - /// 过热度结果,单位 K。 - /// 数值公式:吸气实际温度 - 吸气压力对应饱和温度。 - /// - public double SuperheatK { get; set; } = double.NaN; - - /// - /// 过冷度结果,单位 K。 - /// 数值公式:液路实际温度 - 液路压力对应饱和温度。 - /// - public double SubcoolK { get; set; } = double.NaN; - } - - /// - /// 过热度 / 过冷度独立计算类。 - /// - /// - /// 该类只负责过热度和过冷度的数学/物性计算本身,不直接访问标签,也不负责写回 UI 或实时数据。 - /// 为避免不同计算类共享同一套底层实现后在后续维护中相互影响, - /// 本类将自身所需的 REFPROP 初始化与饱和温度查询过程内聚在类内私有 support 中。 - /// - /// 当前实现保持与原有 PPCService 中的流程一致: - /// 1. 压力由 BarA 换算到 MPa - /// 2. 通过 SATP 按压力求饱和温度 Tsat - /// 3. 将 REFPROP 返回的 K 温度换算回 ℃ - /// 4. 用“实测温度 - 饱和温度”得到过热度/过冷度 - /// - public sealed class PPCSuperheatSubcoolCalculator - { - /// - /// 底层物性计算支持对象。 - /// 提供 REFPROP 初始化、SATP 饱和性质查询等公共能力。 - /// - private readonly LocalCalculationSupport _support; - - /// - /// 初始化过热度 / 过冷度计算类。 - /// - public PPCSuperheatSubcoolCalculator(object refpropLock) - { - _support = new LocalCalculationSupport(refpropLock); - } - - /// - /// 一次性计算过热度和过冷度。 - /// - /// 输入测点数据,包含吸气侧和液路侧压力/温度。 - /// 输出计算结果对象,成功时包含过热度与过冷度。 - /// 失败原因。任一子步骤失败时返回具体错误描述。 - /// - /// - :过热度和过冷度都计算成功 - /// - :任一结果计算失败 - /// - public bool TryCalculate(PPCSuperheatSubcoolCalculationInput input, out PPCSuperheatSubcoolCalculationResult result, out string error) - { - // 先创建结果对象,并将错误文本置空,保持 TryXXX 风格的一致性。 - result = new PPCSuperheatSubcoolCalculationResult(); - error = string.Empty; - - // 先算过热度。 - // 只要过热度失败,就直接结束本次联合计算,并把失败原因透传给调用方。 - if (!TryCalculateSuperheatK(input.SuctionPressureBarA, input.SuctionTemperatureC, out var superheatK, out var superheatErr)) - { - error = superheatErr; - return false; - } - - // 再算过冷度。 - // 保持与原逻辑一致:两个结果都必须成功,联合计算才算成功。 - if (!TryCalculateSubcoolK(input.LiquidPressureBarA, input.LiquidTemperatureC, out var subcoolK, out var subcoolErr)) - { - error = subcoolErr; - return false; - } - - // 两个结果均成功后,写入输出对象。 - result.SuperheatK = superheatK; - result.SubcoolK = subcoolK; - return true; - } - - /// - /// 计算过热度,单位 K。 - /// - /// 吸气压力,单位 BarA(绝压)。 - /// 吸气温度,单位 ℃。 - /// 过热度输出,单位 K。 - /// 失败原因,如 REFPROP 未初始化、SATP 查询失败或结果异常。 - /// 是否计算成功。 - /// - /// 计算过程保持与原 PPCService 一致: - /// 1. 吸气压力由 BarA 转为 MPa - /// 2. 用 SATP 按压力求出该压力下的饱和温度 Tsat(K) - /// 3. 把饱和温度换算为 ℃ - /// 4. 过热度 = 吸气实测温度 - 饱和温度 - /// - public bool TryCalculateSuperheatK(double suctionPressureBarA, double suctionTemperatureC, out double superheatK, out string error) - { - // 默认输出 NaN,表示当前尚未得到有效结果。 - superheatK = double.NaN; - error = string.Empty; - - if (double.IsNaN(suctionPressureBarA) || double.IsInfinity(suctionPressureBarA) || suctionPressureBarA <= 0) - { - error = "吸气压力无效"; - return false; - } - - // REFPROP 相关函数调用前先做幂等初始化。 - if (!_support.EnsureRefpropInitialized(out var initErr)) - { - error = initErr; - return false; - } - - // 现有物性 helper 约定输入压力为 MPa,因此这里把 BarA 转成 MPa。 - double pressureMPa = suctionPressureBarA * 0.1; - - if (pressureMPa <= 0) - { - error = "吸气压力无效"; - return false; - } - - // 按压力求饱和温度。 - // 本计算只关心 Tsat,因此 Dl/Dv 结果不使用,用 out _ 丢弃。 - if (!_support.TrySATP_SaturationByP_MPa(pressureMPa, out var tSatK, out _, out _, out var satErr)) - { - error = satErr; - return false; - } - - // REFPROP 返回的饱和温度是 K,这里换算成 ℃后再与实测温度做差。 - // 公式与原实现完全一致:Superheat = T_actual - T_sat。 - superheatK = suctionTemperatureC - (tSatK - 273.15); - - // 防御性检查:避免把 NaN / Infinity 写回上层标签。 - if (double.IsNaN(superheatK) || double.IsInfinity(superheatK)) - { - error = "过热度结果异常"; - return false; - } - - if (Math.Abs(superheatK) > 100.0) - { - error = $"过热度结果超范围: {superheatK}"; - return false; - } - - return true; - } - - /// - /// 计算过冷度,单位 K。 - /// - /// 液路压力(膨胀阀前压力),单位 BarA(绝压)。 - /// 液路温度(膨胀阀前温度),单位 ℃。 - /// 过冷度输出,单位 K。 - /// 失败原因,如 REFPROP 未初始化、SATP 查询失败或结果异常。 - /// 是否计算成功。 - /// - /// 当前实现与原有逻辑保持一致,公式写法为: - /// Subcool = T_actual - T_sat - /// 即“液路实测温度减去对应压力下的饱和温度”。 - /// - public bool TryCalculateSubcoolK(double liquidPressureBarA, double liquidTemperatureC, out double subcoolK, out string error) - { - // 默认输出 NaN,表示当前尚未得到有效结果。 - subcoolK = double.NaN; - error = string.Empty; - - if (double.IsNaN(liquidPressureBarA) || double.IsInfinity(liquidPressureBarA) || liquidPressureBarA <= 0) - { - error = "液路压力无效"; - return false; - } - - // REFPROP 相关函数调用前先做幂等初始化。 - if (!_support.EnsureRefpropInitialized(out var initErr)) - { - error = initErr; - return false; - } - - // 现有 SATP helper 约定压力单位为 MPa,因此先由 BarA 转成 MPa。 - double pressureMPa = liquidPressureBarA * 0.1; - - if (pressureMPa <= 0) - { - error = "液路压力无效"; - return false; - } - - // 查询该液路压力下的饱和温度 Tsat。 - if (!_support.TrySATP_SaturationByP_MPa(pressureMPa, out var tSatK, out _, out _, out var satErr)) - { - error = satErr; - return false; - } - - // 与原实现保持一致:直接用实测液路温度减去饱和温度。 - subcoolK = liquidTemperatureC - (tSatK - 273.15); - - // 防御性检查:避免异常数值继续上传。 - if (double.IsNaN(subcoolK) || double.IsInfinity(subcoolK)) - { - error = "过冷度结果异常"; - return false; - } - - if (Math.Abs(subcoolK) > 100.0) - { - error = $"过冷度结果超范围: {subcoolK}"; - return false; - } - - return true; - } - - /// - /// 过热度 / 过冷度计算类私有的底层物性支持实现。 - /// - /// - /// 该实现只服务当前计算类,目的是把本类依赖的 REFPROP 调用过程固定在本文件内部, - /// 避免后续为了其他计算类调整共享 support 时,影响已经验算通过的过热度 / 过冷度结果。 - /// - private sealed class LocalCalculationSupport : IPPCCalculationSupport - { - private readonly object _refpropLock; - private static volatile bool _rpInitialized; - - public LocalCalculationSupport(object refpropLock) - { - _refpropLock = refpropLock ?? throw new ArgumentNullException(nameof(refpropLock)); - } - - public bool EnsureRefpropInitialized(out string error) - { - error = string.Empty; - if (_rpInitialized) - { - return true; - } - - try - { - lock (_refpropLock) - { - if (_rpInitialized) - { - return true; - } - - string hpath = ConfigHelper.GetValue("FluidsPath"); - if (string.IsNullOrWhiteSpace(hpath)) - { - hpath = @".\PPCalculation\REFPROP\FLUIDS"; - } - - string configuredCryogen = ConfigHelper.GetValue("Cryogen"); - if (string.IsNullOrWhiteSpace(configuredCryogen)) - { - configuredCryogen = "R134a"; - } - - string hfldCore = configuredCryogen.Equals("R134a", StringComparison.OrdinalIgnoreCase) - ? "R134A.FLD" - : "R134A.FLD"; - - long size = hpath.Length; - string hpathPadded = hpath + new string(' ', Math.Max(0, 255 - (int)size)); - IRefProp64.SETPATHdll(hpathPadded, ref size); - - long numComps = 1; - string hfld = hfldCore; - size = hfld.Length; - string hfldPadded = hfld + new string(' ', Math.Max(0, 10000 - (int)size)); - - string hfmix = "hmx.bnc" + new string(' ', 255); - string hrf = "DEF"; - string herr = new string(' ', 255); - long ierr = 0; - long hfldLen = hfldPadded.Length; - long hfmixLen = hfmix.Length; - long hrfLen = hrf.Length; - long herrLen = herr.Length; - - IRefProp64.SETUPdll(ref numComps, ref hfldPadded, ref hfmix, ref hrf, - ref ierr, ref herr, ref hfldLen, ref hfmixLen, ref hrfLen, ref herrLen); - if (ierr != 0) - { - error = $"REFPROP 初始化失败: {herr.Trim()} (ierr={ierr})"; - _rpInitialized = false; - return false; - } - - _rpInitialized = true; - return true; - } - } - catch (Exception ex) - { - error = $"REFPROP 初始化异常: {ex.Message}"; - _rpInitialized = false; - return false; - } - } - - public bool TrySATP_SaturationByP_MPa(double pressureMPa, out double tSatK, out double Dl_molL, out double Dv_molL, out string error) - { - tSatK = double.NaN; - Dl_molL = double.NaN; - Dv_molL = double.NaN; - error = string.Empty; - - double pKPa = pressureMPa * 1000.0; - double[] x = new double[20]; - x[0] = 1.0; - - long kph = 1; - double Dl = 0; - double Dv = 0; - double[] xliq = new double[20]; - double[] xvap = new double[20]; - long ierr = 0; - long herrLen = 255; - string herr = new string(' ', 255); - - lock (_refpropLock) - { - IRefProp64.SATPdll(ref pKPa, x, ref kph, ref tSatK, ref Dl, ref Dv, xliq, xvap, ref ierr, ref herr, ref herrLen); - } - - if (ierr != 0) - { - error = $"SATP 错误: {herr.Trim()} (ierr={ierr})"; - return false; - } - - Dl_molL = Dl; - Dv_molL = Dv; - return true; - } - - public bool TryTPRHO_VaporDensity_ByTP_MPa_C(double pressureMPa, double temperatureC, out double densityMolPerL, out string error) => throw new NotSupportedException(); - - public bool TryTHERM_VaporEntropy_ByTD(double temperatureC, double densityMolPerL, out double entropy_kJ_per_kgK, out string error) => throw new NotSupportedException(); - - public bool TryTPRHO_LiquidDensity_ByTP_MPa_C(double pressureMPa, double temperatureC, out double densityMolPerL, out string error) => throw new NotSupportedException(); - - public bool TryTHERM_LiquidEnthalpy_ByTD(double temperatureC, double densityMolPerL, out double h_liq_kJ_per_kg, out string error) => throw new NotSupportedException(); - - public bool TryTHERM_Enthalpy_kJkg_ByT_K_D(double temperatureK, double densityMolPerL, out double h_kJ_per_kg, out string error) => throw new NotSupportedException(); - - public bool TryConvertMolarDensityToSpecificVolume(double d_molL, out double v_m3kg, out string error) => throw new NotSupportedException(); - - public bool TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(double dischargePressureBarA, double suctionEntropy_kJkgK, out double h2s_kJkg, out string error) => throw new NotSupportedException(); - } - } -} diff --git a/CapMachine.Wpf/Services/PPCThermodynamicSixResultsCalculator.cs b/CapMachine.Wpf/Services/PPCThermodynamicSixResultsCalculator.cs deleted file mode 100644 index 3ef1ea7..0000000 --- a/CapMachine.Wpf/Services/PPCThermodynamicSixResultsCalculator.cs +++ /dev/null @@ -1,1635 +0,0 @@ -using CapMachine.Core; -using CapMachine.Wpf.PPCalculation; -using System; - -namespace CapMachine.Wpf.Services -{ - /// - /// 六个热力结果值计算的输入数据。 - /// - /// - /// 该输入对象对应六个结果值计算过程中用到的全部外部测点/配置量。 - /// 所有字段均采用当前系统中已经约定好的工程单位,不在本对象中做任何换算。 - /// - public sealed class PPCThermodynamicSixResultsCalculationInput - { - /// - /// 压缩机输入功率,单位 W。 - /// 当前来源为 HV 功率标签。 - /// - public double CompressorPowerW { get; set; } - - /// - /// 总流量(冷媒流量标签当前语义),单位 kg/h。 - /// 该值会与油流量做差,得到真正参与热力计算的冷媒质量流量。 - /// - public double TotalMassFlowKgPerHour { get; set; } - - /// - /// 润滑油流量,单位 kg/h。 - /// - public double OilMassFlowKgPerHour { get; set; } - - /// - /// 吸气压力,单位 BarA(绝压)。 - /// - public double SuctionPressureBarA { get; set; } - - /// - /// 吸气温度,单位 ℃。 - /// - public double SuctionTemperatureC { get; set; } - - /// - /// 排气压力,单位 BarA(绝压)。 - /// - public double DischargePressureBarA { get; set; } - - /// - /// 排气温度,单位 ℃。 - /// - public double DischargeTemperatureC { get; set; } - - /// - /// 膨胀阀前液路压力,单位 BarA(绝压)。 - /// - public double LiquidPressureBarA { get; set; } - - /// - /// 膨胀阀前液路温度,单位 ℃。 - /// - public double LiquidTemperatureC { get; set; } - - /// - /// 压缩机转速,单位 rpm。 - /// 用于容积效率计算。 - /// - public double CompressorSpeedRpm { get; set; } - - /// - /// 压缩机单转排量,单位 cc。 - /// 用于理论体积流量计算。 - /// - public double CompressorDisplacementCc { get; set; } - } - - /// - /// 六个热力结果值计算结果。 - /// - /// - /// 默认均初始化为 ,用于明确区分“未成功得到结果”和“得到有效数值”。 - /// - public sealed class PPCThermodynamicSixResultsCalculationResult - { - /// - /// 制热量 Qh,单位 kW。 - /// 计算公式:Qh = mRef * (h2 - h3)。 - /// - public double HeatingCapacityQh_kW { get; set; } = double.NaN; - - /// - /// 制冷量 Qc,单位 kW。 - /// 计算公式:Qc = mRef * (h1 - h3)。 - /// - public double CoolingCapacityQc_kW { get; set; } = double.NaN; - - /// - /// 制热 COP。 - /// 计算公式:COPHeating = Qh / W。 - /// - public double COPHeating { get; set; } = double.NaN; - - /// - /// 制冷 COP。 - /// 计算公式:COPCooling = Qc / W。 - /// - public double COPCooling { get; set; } = double.NaN; - - /// - /// 等熵效率,单位 %。 - /// 计算公式:(h2s - h1) / (h2 - h1) * 100。 - /// - public double IsentropicEfficiencyPct { get; set; } = double.NaN; - - /// - /// 容积效率,单位 %。 - /// 计算公式:实际吸气体积流量 / 理论吸气体积流量 * 100。 - /// - public double VolumetricEfficiencyPct { get; set; } = double.NaN; - } - - /// - /// 六个热力结果值独立计算类。 - /// - /// - /// 本类负责以下六个结果的完整计算流程: - /// - 制热量 - /// - COP(制热) - /// - 等熵效率 - /// - 制冷量 - /// - COP(制冷) - /// - 容积效率 - /// - /// 设计目标是把“结果计算逻辑”从 PPCService 中抽离出来, - /// 让服务层只负责取标签和写回,计算类只负责计算本身。 - /// 为降低后续新增其他计算类时对已验算结果的影响范围, - /// 本类将自身所需的底层 REFPROP 支撑实现内聚在类内私有 support 中。 - /// - /// 当前实现严格保持既有流程不变: - /// 1. 由总流量和油流量得到冷媒质量流量 - /// 2. 由吸气点得到 h1 / s1 / v1 - /// 3. 由排气点得到 h2 - /// 4. 由液路点得到 h3 - /// 5. 由排气压力和吸气熵得到 h2s - /// 6. 计算 Qh / Qc / COP / 等熵效率 / 容积效率 - /// - public sealed class PPCThermodynamicSixResultsCalculator - { - /// - /// 底层物性计算支持对象。 - /// 负责提供 REFPROP 初始化与 TPRHO / THERM / PSFLSH 等共用能力。 - /// - private readonly LocalCalculationSupport _support; - - private double _h3TempOffset_C = -10.0; - - /// - /// 初始化六个热力结果值计算类。 - /// - public PPCThermodynamicSixResultsCalculator(object refpropLock) - { - _support = new LocalCalculationSupport(refpropLock); - } - - public void SetH3TempOffset_C(double offsetC) - { - _h3TempOffset_C = offsetC; - } - - /// - /// 按既定流程一次性计算六个热力结果值。 - /// - /// 输入数据,包含功率、流量、吸排气状态点、液路状态点、转速与排量。 - /// 输出结果对象。 - /// - /// 错误或警告信息。 - /// 当前逻辑中,若“容积效率”计算失败但其他 5 个结果成功,会返回 , - /// 同时通过此参数把容积效率失败原因返回给上层,保持原有行为不变。 - /// - /// - /// - :主要结果计算完成;容积效率可能成功也可能失败 - /// - :关键输入或关键热力步骤失败 - /// - public bool TryCalculate(PPCThermodynamicSixResultsCalculationInput input, out PPCThermodynamicSixResultsCalculationResult result, out string error) - { - // 创建输出对象,并将错误文本清空。 - result = new PPCThermodynamicSixResultsCalculationResult(); - error = string.Empty; - - // 所有物性函数调用之前,先确保 REFPROP 已完成初始化。 - if (!_support.EnsureRefpropInitialized(out var initErr)) - { - error = initErr; - return false; - } - - // 第 1 步:将压缩机输入功率由 W 换算为 kW。 - if (!TryGetCompressorPower_kW(input.CompressorPowerW, out var w_kW, out var wErr)) - { - error = wErr; - return false; - } - - // 第 2 步:根据“总流量 - 油流量”,得到真正参与循环的冷媒质量流量, - // 并从 kg/h 换算到后续公式使用的 kg/s。 - if (!TryGetRefrigerantMassFlow_kg_s(input.TotalMassFlowKgPerHour, input.OilMassFlowKgPerHour, out var mRef_kg_s, out var mRefErr)) - { - error = mRefErr; - return false; - } - - // 第 3 步:由吸气压力/温度求出吸气点状态: - // - h1:吸气比焓 - // - s1:吸气比熵 - // - v1:吸气比容 - if (!TryGetVaporPointState_ByTP_BarA_C(input.SuctionPressureBarA, input.SuctionTemperatureC, out var h1_kJkg, out var s1_kJkgK, out var v1_m3kg, out var p1Err)) - { - error = $"h1/s1/吸气比容计算失败: {p1Err}"; - return false; - } - - // 第 4 步:由排气压力/温度求排气比焓 h2。 - if (!TryGetVaporPointEnthalpy_ByTP_BarA_C(input.DischargePressureBarA, input.DischargeTemperatureC, out var h2_kJkg, out var p2Err)) - { - error = $"h2 计算失败: {p2Err}"; - return false; - } - - // 第 5 步:由液路压力/温度求液路比焓 h3。 - double liquidTempForH3_C = input.LiquidTemperatureC; - if (_support.TrySATP_SaturationByP_MPa(input.DischargePressureBarA * 0.1, out var tSatK, out _, out _, out var satErr)) - { - liquidTempForH3_C = (tSatK - 273.15) + _h3TempOffset_C; - } - else if (!string.IsNullOrWhiteSpace(satErr)) - { - error = string.IsNullOrWhiteSpace(error) - ? $"h3温度改用SATP(排气压力)计算Tsat失败,已回退使用LiquidTemperatureC。原因: {satErr}" - : $"{error}; h3温度改用SATP(排气压力)计算Tsat失败,已回退使用LiquidTemperatureC。原因: {satErr}"; - } - if (!TryGetLiquidPointEnthalpy_ByTP_BarA_C(input.LiquidPressureBarA, liquidTempForH3_C, out var h3_kJkg, out var p3Err)) - { - error = $"h3 计算失败: {p3Err}"; - return false; - } - - // 第 6 步:由排气压力 P2 和吸气熵 s1 求等熵出口焓 h2s。 - if (!_support.TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(input.DischargePressureBarA, s1_kJkgK, out var h2s_kJkg, out var h2sErr)) - { - error = $"h2s 计算失败: {h2sErr}"; - return false; - } - - // 第 7 步:计算 Qh、Qc、COP(制热)、COP(制冷)。 - if (!TryComputeCapacitiesAndCOP(mRef_kg_s, h1_kJkg, h2_kJkg, h3_kJkg, w_kW, out var qh_kW, out var qc_kW, out var copH, out var copC, out var capErr)) - { - error = capErr; - return false; - } - - // 第 8 步:计算等熵效率。 - if (!TryComputeIsentropicEfficiencyPct(mRef_kg_s, h1_kJkg, h2s_kJkg, w_kW, out var etaS_pct, out var etaSErr)) - { - error = etaSErr; - return false; - } - - // 先写入 5 个关键结果。 - result.HeatingCapacityQh_kW = qh_kW; - result.CoolingCapacityQc_kW = qc_kW; - result.COPHeating = copH; - result.COPCooling = copC; - result.IsentropicEfficiencyPct = etaS_pct; - - // 最后再算容积效率。 - // 当前逻辑特意保持与旧实现一致: - // 即使容积效率失败,只要前面 5 个主结果已经成功,就仍然返回 true, - // 同时把失败原因放到 error 中,供调用方决定是否记录为警告。 - if (!TryComputeVolumetricEfficiencyPct(mRef_kg_s, v1_m3kg, input.CompressorSpeedRpm, input.CompressorDisplacementCc, out var etaV_pct, out var etaVErr)) - { - error = string.IsNullOrWhiteSpace(error) ? etaVErr : $"{error}; {etaVErr}"; - return true; - } - - result.VolumetricEfficiencyPct = etaV_pct; - return true; - } - - /// - /// 获取压缩机输入功率并换算为 kW。 - /// - /// 输入功率,单位 W。 - /// 输出功率,单位 kW。 - /// 失败原因。 - /// 是否换算成功。 - private bool TryGetCompressorPower_kW(double compressorPowerW, out double w_kW, out string error) - { - w_kW = double.NaN; - error = string.Empty; - - // 读取输入功率,并做基础合法性校验。 - double w_W = compressorPowerW; - if (double.IsNaN(w_W) || double.IsInfinity(w_W) || w_W <= 0) - { - error = $"无效压缩机功率 HV[W]={w_W}"; - return false; - } - - // 六个热力结果公式里统一使用 kW,因此这里做 W -> kW 的标准换算。 - w_kW = w_W / 1000.0; - return true; - } - - /// - /// 获取冷媒质量流量,输出单位 kg/s。 - /// - /// 总流量,单位 kg/h。 - /// 油流量,单位 kg/h。 - /// 冷媒质量流量输出,单位 kg/s。 - /// 失败原因。 - /// 是否计算成功。 - /// - /// 当前流程保持与图片/旧代码一致: - /// 1. 读取总流量 kg/h - /// 2. 冷媒流量 = 总流量 - /// 3. 再由 kg/h 换算为 kg/s - /// - private bool TryGetRefrigerantMassFlow_kg_s(double totalMassFlowKgPerHour, double oilMassFlowKgPerHour, out double mRef_kg_s, out string error) - { - mRef_kg_s = double.NaN; - error = string.Empty; - - // 先读取总流量。 - if (!TryGetTotalMassFlow_kg_h(totalMassFlowKgPerHour, out var mTotal_kg_h, out var totalErr)) - { - error = totalErr; - return false; - } - - double mRef_kg_h = mTotal_kg_h; - if (mRef_kg_h <= 0) - { - error = $"冷媒质量流量<=0,总流量={mTotal_kg_h}kg/h"; - return false; - } - - // 将 kg/h 换算成后续容量计算使用的 kg/s。 - return TryConvertMassFlow_kg_h_To_kg_s(mRef_kg_h, out mRef_kg_s, out error); - } - - /// - /// 获取总流量,单位 kg/h。 - /// - /// 输入总流量。 - /// 输出总流量。 - /// 失败原因。 - /// 是否读取成功。 - private bool TryGetTotalMassFlow_kg_h(double totalMassFlowKgPerHour, out double mTotal_kg_h, out string error) - { - mTotal_kg_h = double.NaN; - error = string.Empty; - - // 当前方法只做数值读取和合法性校验,不做其他换算。 - mTotal_kg_h = totalMassFlowKgPerHour; - if (double.IsNaN(mTotal_kg_h) || double.IsInfinity(mTotal_kg_h)) - { - error = "总流量(冷媒流量)为 NaN/Inf"; - return false; - } - - return true; - } - - /// - /// 获取油流量,单位 kg/h。 - /// - /// 输入油流量。 - /// 输出油流量。 - /// 失败原因。 - /// 是否读取成功。 - private bool TryGetOilMassFlow_kg_h(double oilMassFlowKgPerHour, out double mOil_kg_h, out string error) - { - mOil_kg_h = double.NaN; - error = string.Empty; - - // 当前方法只做数值读取和合法性校验,不做其他换算。 - mOil_kg_h = oilMassFlowKgPerHour; - if (double.IsNaN(mOil_kg_h) || double.IsInfinity(mOil_kg_h)) - { - error = "油流量为 NaN/Inf"; - return false; - } - - return true; - } - - /// - /// 计算冷媒质量流量,单位 kg/h。 - /// - /// 总流量,单位 kg/h。 - /// 油流量,单位 kg/h。 - /// 冷媒质量流量输出,单位 kg/h。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeRefrigerantMassFlow_kg_h(double mTotal_kg_h, double mOil_kg_h, out double mRef_kg_h, out string error) - { - mRef_kg_h = double.NaN; - error = string.Empty; - - if (double.IsNaN(mTotal_kg_h) || double.IsInfinity(mTotal_kg_h)) - { - error = "总流量(冷媒流量)为 NaN/Inf"; - return false; - } - - if (double.IsNaN(mOil_kg_h) || double.IsInfinity(mOil_kg_h)) - { - error = "油流量为 NaN/Inf"; - return false; - } - - // 与既有实现一致:冷媒质量流量 = 总流量 - 油流量。 - mRef_kg_h = mTotal_kg_h - mOil_kg_h; - if (mRef_kg_h <= 0) - { - error = $"冷媒质量流量<=0,总流量={mTotal_kg_h}kg/h,油流量={mOil_kg_h}kg/h"; - return false; - } - - return true; - } - - /// - /// 将质量流量从 kg/h 换算到 kg/s。 - /// - /// 输入质量流量,单位 kg/h。 - /// 输出质量流量,单位 kg/s。 - /// 失败原因。 - /// 是否换算成功。 - private bool TryConvertMassFlow_kg_h_To_kg_s(double massFlow_kg_h, out double massFlow_kg_s, out string error) - { - massFlow_kg_s = double.NaN; - error = string.Empty; - - if (double.IsNaN(massFlow_kg_h) || double.IsInfinity(massFlow_kg_h) || massFlow_kg_h <= 0) - { - error = $"无效质量流量={massFlow_kg_h}kg/h"; - return false; - } - - // 1 小时 = 3600 秒,因此 kg/h -> kg/s 需要除以 3600。 - massFlow_kg_s = massFlow_kg_h / 3600.0; - return true; - } - - /// - /// 由某一气相状态点的压力 / 温度求质量比焓。 - /// - /// 输入压力,单位 BarA(绝压)。 - /// 输入温度,单位 ℃。 - /// 输出质量比焓,单位 kJ/kg。 - /// 失败原因。 - /// 是否计算成功。 - /// - /// 当前流程(与 HASCO_KR26001_Fr25002 保持一致): - /// 1. 使用 TPFLSH 一次计算得到 h - /// - private bool TryGetVaporPointEnthalpy_ByTP_BarA_C(double pressureBarA, double temperatureC, out double h_kJkg, out string error) - { - h_kJkg = double.NaN; - error = string.Empty; - - if (!_support.TryTPFLSH_ByTP_BarA_C(pressureBarA, temperatureC, out _, out var h_Jmol, out _, out _, out var flashErr)) - { - error = flashErr; - return false; - } - - if (!_support.TryConvertH_Jmol_To_kJkg(h_Jmol, out h_kJkg, out var hErr)) - { - error = hErr; - return false; - } - - return true; - } - - /// - /// 由某一液相状态点的压力 / 温度求质量比焓。 - /// - /// 输入压力,单位 BarA(绝压)。 - /// 输入温度,单位 ℃。 - /// 输出质量比焓,单位 kJ/kg。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryGetLiquidPointEnthalpy_ByTP_BarA_C(double pressureBarA, double temperatureC, out double h_kJkg, out string error) - { - h_kJkg = double.NaN; - error = string.Empty; - - // 物性 helper 的压力输入单位是 MPa,因此先换算。 - double pMPa = pressureBarA * 0.1; - - // 先按液相路径求摩尔密度 D。 - if (!_support.TryTPRHO_LiquidDensity_ByTP_MPa_C(pMPa, temperatureC, out var d_molL, out var dErr)) - { - error = dErr; - return false; - } - - // 再按液相路径求质量比焓 h。 - if (!_support.TryTHERM_LiquidEnthalpy_ByTD(temperatureC, d_molL, out h_kJkg, out var hErr)) - { - error = hErr; - return false; - } - - return true; - } - - /// - /// 由某一气相状态点的压力 / 温度联合求取 h、s、v。 - /// - /// 输入压力,单位 BarA(绝压)。 - /// 输入温度,单位 ℃。 - /// 输出质量比焓,单位 kJ/kg。 - /// 输出质量比熵,单位 kJ/(kg·K)。 - /// 输出比容,单位 m³/kg。 - /// 失败原因。 - /// 是否计算成功。 - /// - /// 当前流程(与 HASCO_KR26001_Fr25002 保持一致): - /// 1. 使用 TPFLSH 一次计算得到 d, h, s - /// - private bool TryGetVaporPointState_ByTP_BarA_C(double pressureBarA, double temperatureC, out double h_kJkg, out double s_kJkgK, out double v_m3kg, out string error) - { - h_kJkg = double.NaN; - s_kJkgK = double.NaN; - v_m3kg = double.NaN; - error = string.Empty; - - if (!_support.TryTPFLSH_ByTP_BarA_C(pressureBarA, temperatureC, out var d_molL, out var h_Jmol, out var s_JmolK, out _, out var flashErr)) - { - error = flashErr; - return false; - } - - if (!_support.TryConvertH_Jmol_To_kJkg(h_Jmol, out h_kJkg, out var hErr)) - { - error = hErr; - return false; - } - - s_kJkgK = s_JmolK / _support.GetMolarMass() * 0.001; - if (double.IsNaN(s_kJkgK) || double.IsInfinity(s_kJkgK)) - { - error = "无效吸气熵 s1"; - return false; - } - - if (!_support.TryConvertMolarDensityToSpecificVolume(d_molL, out v_m3kg, out var vErr)) - { - error = vErr; - return false; - } - - return true; - } - - /// - /// 统一计算制热量、制冷量、制热 COP、制冷 COP。 - /// - /// 冷媒质量流量,单位 kg/s。 - /// 吸气比焓,单位 kJ/kg。 - /// 排气比焓,单位 kJ/kg。 - /// 液路比焓,单位 kJ/kg。 - /// 压缩机功率,单位 kW。 - /// 制热量输出,单位 kW。 - /// 制冷量输出,单位 kW。 - /// 制热 COP 输出。 - /// 制冷 COP 输出。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeCapacitiesAndCOP(double mRef_kg_s, double h1_kJkg, double h2_kJkg, double h3_kJkg, double w_kW, - out double qh_kW, out double qc_kW, out double copHeating, out double copCooling, out string error) - { - qh_kW = double.NaN; - qc_kW = double.NaN; - copHeating = double.NaN; - copCooling = double.NaN; - error = string.Empty; - - // 先算制热量:Qh = mRef * (h2 - h3)。 - if (!TryComputeHeatingCapacityQh_kW(mRef_kg_s, h2_kJkg, h3_kJkg, out qh_kW, out var qhErr)) - { - error = qhErr; - return false; - } - - // 再算制冷量:Qc = mRef * (h1 - h3)。 - if (!TryComputeCoolingCapacityQc_kW(mRef_kg_s, h1_kJkg, h3_kJkg, out qc_kW, out var qcErr)) - { - error = qcErr; - return false; - } - - // 制热 COP = Qh / W。 - if (!TryComputeHeatingCOP(qh_kW, w_kW, out copHeating, out var copHErr)) - { - error = copHErr; - return false; - } - - // 制冷 COP = Qc / W。 - if (!TryComputeCoolingCOP(qc_kW, w_kW, out copCooling, out var copCErr)) - { - error = copCErr; - return false; - } - - return true; - } - - /// - /// 计算焓差,单位 kJ/kg。 - /// - /// 被减数,单位 kJ/kg。 - /// 减数,单位 kJ/kg。 - /// 当前计算量名称,用于拼接错误信息。 - /// 焓差输出,单位 kJ/kg。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeEnthalpyDifference_kJkg(double minuend_kJkg, double subtrahend_kJkg, string quantityName, out double deltaH_kJkg, out string error) - { - deltaH_kJkg = double.NaN; - error = string.Empty; - - // 先校验输入两端焓值是否有效。 - if (double.IsNaN(minuend_kJkg) || double.IsInfinity(minuend_kJkg) || double.IsNaN(subtrahend_kJkg) || double.IsInfinity(subtrahend_kJkg)) - { - error = $"{quantityName}输入存在 NaN/Inf"; - return false; - } - - // 焓差按“被减数 - 减数”计算。 - deltaH_kJkg = minuend_kJkg - subtrahend_kJkg; - if (double.IsNaN(deltaH_kJkg) || double.IsInfinity(deltaH_kJkg)) - { - error = $"{quantityName}结果异常"; - return false; - } - - return true; - } - - /// - /// 根据质量流量与焓差计算容量,单位 kW。 - /// - /// 冷媒质量流量,单位 kg/s。 - /// 焓差,单位 kJ/kg。 - /// 容量名称,用于错误信息。 - /// 容量输出,单位 kW。 - /// 失败原因。 - /// 是否计算成功。 - /// - /// 公式:Capacity = mRef * Δh。 - /// 因为 kg/s * kJ/kg = kJ/s = kW,所以结果天然就是 kW。 - /// - private bool TryComputeCapacity_kW(double mRef_kg_s, double deltaH_kJkg, string quantityName, out double capacity_kW, out string error) - { - capacity_kW = double.NaN; - error = string.Empty; - - // 校验冷媒质量流量是否有效。 - if (double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s) || mRef_kg_s <= 0) - { - error = "无效冷媒质量流量"; - return false; - } - - // 校验焓差是否有效。 - if (double.IsNaN(deltaH_kJkg) || double.IsInfinity(deltaH_kJkg)) - { - error = $"{quantityName}焓差异常"; - return false; - } - - // 按容量公式直接计算。 - capacity_kW = mRef_kg_s * deltaH_kJkg; - if (double.IsNaN(capacity_kW) || double.IsInfinity(capacity_kW)) - { - error = $"{quantityName}结果异常"; - return false; - } - - return true; - } - - /// - /// 计算制热量 Qh,单位 kW。 - /// - /// 冷媒质量流量,单位 kg/s。 - /// 排气比焓 h2,单位 kJ/kg。 - /// 液路比焓 h3,单位 kJ/kg。 - /// 制热量输出,单位 kW。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeHeatingCapacityQh_kW(double mRef_kg_s, double h2_kJkg, double h3_kJkg, out double qh_kW, out string error) - { - qh_kW = double.NaN; - error = string.Empty; - - // 先计算制热焓差:(h2 - h3)。 - if (!TryComputeEnthalpyDifference_kJkg(h2_kJkg, h3_kJkg, "制热焓差(h2-h3)", out var deltaH_kJkg, out var deltaErr)) - { - error = deltaErr; - return false; - } - - // 再由质量流量与焓差得到制热量。 - return TryComputeCapacity_kW(mRef_kg_s, deltaH_kJkg, "制热量Qh", out qh_kW, out error); - } - - /// - /// 计算制冷量 Qc,单位 kW。 - /// - /// 冷媒质量流量,单位 kg/s。 - /// 吸气比焓 h1,单位 kJ/kg。 - /// 液路比焓 h3,单位 kJ/kg。 - /// 制冷量输出,单位 kW。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeCoolingCapacityQc_kW(double mRef_kg_s, double h1_kJkg, double h3_kJkg, out double qc_kW, out string error) - { - qc_kW = double.NaN; - error = string.Empty; - - // 先计算制冷焓差:(h1 - h3)。 - if (!TryComputeEnthalpyDifference_kJkg(h1_kJkg, h3_kJkg, "制冷焓差(h1-h3)", out var deltaH_kJkg, out var deltaErr)) - { - error = deltaErr; - return false; - } - - // 再由质量流量与焓差得到制冷量。 - return TryComputeCapacity_kW(mRef_kg_s, deltaH_kJkg, "制冷量Qc", out qc_kW, out error); - } - - /// - /// 计算 COP。 - /// - /// 容量,单位 kW。 - /// 压缩机输入功率,单位 kW。 - /// 当前 COP 名称,用于错误信息。 - /// COP 输出。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeCOP(double capacity_kW, double w_kW, string quantityName, out double cop, out string error) - { - cop = double.NaN; - error = string.Empty; - - // 容量必须是有效数值。 - if (double.IsNaN(capacity_kW) || double.IsInfinity(capacity_kW)) - { - error = $"{quantityName}输入异常"; - return false; - } - - // 功率必须有效且大于 0,避免除零。 - if (double.IsNaN(w_kW) || double.IsInfinity(w_kW) || w_kW <= 0) - { - error = "无效压缩机功率"; - return false; - } - - // COP = 容量 / 输入功率。 - cop = capacity_kW / w_kW; - if (double.IsNaN(cop) || double.IsInfinity(cop)) - { - error = $"{quantityName}结果异常"; - return false; - } - - return true; - } - - /// - /// 计算制热 COP。 - /// - /// 制热量,单位 kW。 - /// 压缩机输入功率,单位 kW。 - /// 制热 COP 输出。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeHeatingCOP(double qh_kW, double w_kW, out double copHeating, out string error) - { - return TryComputeCOP(qh_kW, w_kW, "COP(制热)", out copHeating, out error); - } - - /// - /// 计算制冷 COP。 - /// - /// 制冷量,单位 kW。 - /// 压缩机输入功率,单位 kW。 - /// 制冷 COP 输出。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeCoolingCOP(double qc_kW, double w_kW, out double copCooling, out string error) - { - return TryComputeCOP(qc_kW, w_kW, "COP(制冷)", out copCooling, out error); - } - - /// - /// 计算等熵效率,单位 %。 - /// - /// 冷媒质量流量,单位 kg/s。 - /// 吸气比焓 h1,单位 kJ/kg。 - /// 等熵出口比焓 h2s,单位 kJ/kg。 - /// 压缩机功率,单位 kW。 - /// 等熵效率输出,单位 %。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeIsentropicEfficiencyPct(double mRef_kg_s, double h1_kJkg, double h2s_kJkg, double w_kW, out double etaS_pct, out string error) - { - etaS_pct = double.NaN; - error = string.Empty; - - if (double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s) || mRef_kg_s <= 0) - { - error = "无效冷媒质量流量"; - return false; - } - - if (double.IsNaN(w_kW) || double.IsInfinity(w_kW) || w_kW <= 0) - { - error = "无效压缩机功率"; - return false; - } - - if (!TryComputeEnthalpyDifference_kJkg(h2s_kJkg, h1_kJkg, "等熵压缩焓升(h2s-h1)", out var isentropicRise_kJkg, out var isoErr)) - { - error = isoErr; - return false; - } - - double eta = (mRef_kg_s * isentropicRise_kJkg) / w_kW; - if (double.IsNaN(eta) || double.IsInfinity(eta)) - { - error = "等熵效率结果异常"; - return false; - } - - etaS_pct = eta * 100.0; - return true; - } - - /// - /// 计算容积效率,单位 %。 - /// - /// 冷媒质量流量,单位 kg/s。 - /// 吸气比容 v1,单位 m³/kg。 - /// 压缩机转速,单位 rpm。 - /// 压缩机排量,单位 cc。 - /// 容积效率输出,单位 %。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeVolumetricEfficiencyPct(double mRef_kg_s, double v1_m3kg, double speed_rpm, double disp_cc, out double etaV_pct, out string error) - { - etaV_pct = double.NaN; - error = string.Empty; - - // 先校验转速。 - if (!TryGetCompressorSpeed_rpm(speed_rpm, out var validatedSpeed_rpm, out var speedErr)) - { - error = speedErr; - return false; - } - - // 再校验排量。 - if (!TryGetCompressorDisplacement_cc(disp_cc, out var validatedDisp_cc, out var dispErr)) - { - error = dispErr; - return false; - } - - // 计算实际吸气体积流量。 - if (!TryComputeSuctionVolumeFlow_m3_h(mRef_kg_s, v1_m3kg, out var suctionVolFlow_m3_h, out var suctionErr)) - { - error = suctionErr; - return false; - } - - // 计算理论吸气体积流量。 - if (!TryComputeTheoreticalVolumeFlow_m3_h(validatedSpeed_rpm, validatedDisp_cc, out var theoVolFlow_m3_h, out var theoErr)) - { - error = theoErr; - return false; - } - - // 容积效率 = 实际吸气体积流量 / 理论吸气体积流量 * 100%。 - return TryComputeEfficiencyPct(suctionVolFlow_m3_h, theoVolFlow_m3_h, "容积效率", out etaV_pct, out error); - } - - /// - /// 计算百分比效率。 - /// - /// 分子。 - /// 分母。 - /// 当前效率名称,用于错误信息。 - /// 效率输出,单位 %。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeEfficiencyPct(double numerator, double denominator, string quantityName, out double efficiencyPct, out string error) - { - efficiencyPct = double.NaN; - error = string.Empty; - - // 先校验分子。 - if (double.IsNaN(numerator) || double.IsInfinity(numerator)) - { - error = $"{quantityName}分子异常"; - return false; - } - - // 再校验分母,避免除零或极小数造成结果发散。 - const double eps = 1e-9; - if (double.IsNaN(denominator) || double.IsInfinity(denominator) || Math.Abs(denominator) < eps) - { - error = $"{quantityName}分母过小或异常"; - return false; - } - - // 效率 = 分子 / 分母。 - double efficiency = numerator / denominator; - if (double.IsNaN(efficiency) || double.IsInfinity(efficiency)) - { - error = $"{quantityName}结果异常"; - return false; - } - - // 将无量纲效率换算为百分数。 - efficiencyPct = efficiency * 100.0; - return true; - } - - /// - /// 获取并校验压缩机转速,单位 rpm。 - /// - /// 输入转速,单位 rpm。 - /// 输出转速,单位 rpm。 - /// 失败原因。 - /// 是否获取成功。 - private bool TryGetCompressorSpeed_rpm(double compressorSpeedRpm, out double speed_rpm, out string error) - { - speed_rpm = double.NaN; - error = string.Empty; - - // 当前方法只做读取与合法性校验。 - speed_rpm = compressorSpeedRpm; - if (double.IsNaN(speed_rpm) || double.IsInfinity(speed_rpm) || speed_rpm <= 0) - { - error = $"无效转速: {speed_rpm} rpm"; - return false; - } - - return true; - } - - /// - /// 计算实际吸气体积流量,单位 m³/h。 - /// - /// 冷媒质量流量,单位 kg/s。 - /// 吸气比容,单位 m³/kg。 - /// 实际吸气体积流量输出,单位 m³/h。 - /// 失败原因。 - /// 是否计算成功。 - private bool TryComputeSuctionVolumeFlow_m3_h(double mRef_kg_s, double v1_m3kg, out double suctionVolFlow_m3_h, out string error) - { - suctionVolFlow_m3_h = double.NaN; - error = string.Empty; - - // 校验质量流量。 - if (double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s) || mRef_kg_s <= 0) - { - error = "无效冷媒质量流量"; - return false; - } - - // 校验吸气比容。 - if (double.IsNaN(v1_m3kg) || double.IsInfinity(v1_m3kg) || v1_m3kg <= 0) - { - error = "无效吸气比容"; - return false; - } - - // 实际吸气体积流量 = (kg/s * 3600) * m³/kg = m³/h。 - suctionVolFlow_m3_h = (mRef_kg_s * 3600.0) * v1_m3kg; - if (double.IsNaN(suctionVolFlow_m3_h) || double.IsInfinity(suctionVolFlow_m3_h)) - { - error = "实际吸气体积流量结果异常"; - return false; - } - - return true; - } - - /// - /// 计算理论吸气体积流量,单位 m³/h。 - /// - /// 压缩机转速,单位 rpm。 - /// 压缩机排量,单位 cc。 - /// 理论吸气体积流量输出,单位 m³/h。 - /// 失败原因。 - /// 是否计算成功。 - /// - /// 当前公式保持原实现不变: - /// (speed_rpm / 60.0) * disp_cc * 0.0036 - /// - private bool TryComputeTheoreticalVolumeFlow_m3_h(double speed_rpm, double disp_cc, out double theoVolFlow_m3_h, out string error) - { - theoVolFlow_m3_h = double.NaN; - error = string.Empty; - - if (double.IsNaN(speed_rpm) || double.IsInfinity(speed_rpm) || speed_rpm <= 0) - { - error = $"无效转速: {speed_rpm} rpm"; - return false; - } - - if (double.IsNaN(disp_cc) || double.IsInfinity(disp_cc) || disp_cc <= 0) - { - error = $"无效压缩机排量: {disp_cc} cc"; - return false; - } - - // 理论体积流量公式保持原有写法不变。 - theoVolFlow_m3_h = (speed_rpm / 60.0) * disp_cc * 0.0036; - if (double.IsNaN(theoVolFlow_m3_h) || double.IsInfinity(theoVolFlow_m3_h) || theoVolFlow_m3_h <= 0) - { - error = "理论吸气体积流量<=0"; - return false; - } - - return true; - } - - /// - /// 获取并校验压缩机排量,单位 cc。 - /// - /// 输入排量,单位 cc。 - /// 输出排量,单位 cc。 - /// 失败原因。 - /// 是否获取成功。 - private bool TryGetCompressorDisplacement_cc(double compressorDisplacementCc, out double displacement_cc, out string error) - { - displacement_cc = double.NaN; - error = string.Empty; - - // 当前方法只做读取与合法性校验。 - displacement_cc = compressorDisplacementCc; - if (double.IsNaN(displacement_cc) || double.IsInfinity(displacement_cc) || displacement_cc <= 0) - { - error = $"压缩机排量<=0: {displacement_cc}"; - return false; - } - - return true; - } - - /// - /// 六个热力结果计算类私有的底层物性支持实现。 - /// - /// - /// 该实现仅服务当前六结果计算类,不与其他结果类共享实现细节, - /// 这样即使后续为了新计算类调整别处的 support,也不会影响本类已验算通过的流程。 - /// - private sealed class LocalCalculationSupport : IPPCCalculationSupport - { - private readonly object _refpropLock; - private static volatile bool _rpInitialized; - private static double _molarMassKgPerMol = double.NaN; - - public LocalCalculationSupport(object refpropLock) - { - _refpropLock = refpropLock ?? throw new ArgumentNullException(nameof(refpropLock)); - } - - public bool EnsureRefpropInitialized(out string error) - { - error = string.Empty; - if (_rpInitialized) - { - if (!double.IsNaN(_molarMassKgPerMol) && !double.IsInfinity(_molarMassKgPerMol) && _molarMassKgPerMol > 0) - { - return true; - } - - lock (_refpropLock) - { - if (!double.IsNaN(_molarMassKgPerMol) && !double.IsInfinity(_molarMassKgPerMol) && _molarMassKgPerMol > 0) - { - return true; - } - - double wmm = 0; - double Trp = 0; - double Tnbpt = 0; - double Tc = 0; - double Pc = 0; - double Dc = 0; - double Zc = 0; - double acf = 0; - double dip = 0; - double Rgas = 0; - long componentId = 1; - - IRefProp64.INFOdll(ref componentId, ref wmm, ref Trp, ref Tnbpt, ref Tc, ref Pc, ref Dc, ref Zc, ref acf, ref dip, ref Rgas); - _molarMassKgPerMol = wmm * 0.001; - if (double.IsNaN(_molarMassKgPerMol) || double.IsInfinity(_molarMassKgPerMol) || _molarMassKgPerMol <= 0) - { - error = "无效的摩尔质量"; - return false; - } - } - - return true; - } - - try - { - lock (_refpropLock) - { - if (_rpInitialized) - { - return true; - } - - string hpath = ConfigHelper.GetValue("FluidsPath"); - if (string.IsNullOrWhiteSpace(hpath)) - { - hpath = @".\PPCalculation\REFPROP\FLUIDS"; - } - - string configuredCryogen = ConfigHelper.GetValue("Cryogen"); - if (string.IsNullOrWhiteSpace(configuredCryogen)) - { - configuredCryogen = "R134a"; - } - - string hfldCore = configuredCryogen.Equals("R134a", StringComparison.OrdinalIgnoreCase) - ? "R134A.FLD" - : "R134A.FLD"; - - long size = hpath.Length; - string hpathPadded = hpath + new string(' ', Math.Max(0, 255 - (int)size)); - IRefProp64.SETPATHdll(hpathPadded, ref size); - - long numComps = 1; - string hfld = hfldCore; - size = hfld.Length; - string hfldPadded = hfld + new string(' ', Math.Max(0, 10000 - (int)size)); - - string hfmix = "hmx.bnc" + new string(' ', 255); - string hrf = "DEF"; - string herr = new string(' ', 255); - long ierr = 0; - long hfldLen = hfldPadded.Length; - long hfmixLen = hfmix.Length; - long hrfLen = hrf.Length; - long herrLen = herr.Length; - - IRefProp64.SETUPdll(ref numComps, ref hfldPadded, ref hfmix, ref hrf, - ref ierr, ref herr, ref hfldLen, ref hfmixLen, ref hrfLen, ref herrLen); - if (ierr != 0) - { - error = $"REFPROP 初始化失败: {herr.Trim()} (ierr={ierr})"; - _rpInitialized = false; - return false; - } - - if (double.IsNaN(_molarMassKgPerMol) || double.IsInfinity(_molarMassKgPerMol) || _molarMassKgPerMol <= 0) - { - double wmm = 0; - double Trp = 0; - double Tnbpt = 0; - double Tc = 0; - double Pc = 0; - double Dc = 0; - double Zc = 0; - double acf = 0; - double dip = 0; - double Rgas = 0; - long componentId = 1; - - IRefProp64.INFOdll(ref componentId, ref wmm, ref Trp, ref Tnbpt, ref Tc, ref Pc, ref Dc, ref Zc, ref acf, ref dip, ref Rgas); - _molarMassKgPerMol = wmm * 0.001; - if (double.IsNaN(_molarMassKgPerMol) || double.IsInfinity(_molarMassKgPerMol) || _molarMassKgPerMol <= 0) - { - error = "无效的摩尔质量"; - _rpInitialized = false; - return false; - } - } - - _rpInitialized = true; - return true; - } - } - catch (Exception ex) - { - error = $"REFPROP 初始化异常: {ex.Message}"; - _rpInitialized = false; - return false; - } - } - - public bool TrySATP_SaturationByP_MPa(double pressureMPa, out double tSatK, out double Dl_molL, out double Dv_molL, out string error) - { - tSatK = double.NaN; - Dl_molL = double.NaN; - Dv_molL = double.NaN; - error = string.Empty; - - if (!EnsureRefpropInitialized(out var initErr)) - { - error = initErr; - return false; - } - - double pKPa = pressureMPa * 1000.0; - double[] x = new double[20]; - x[0] = 1.0; - - long kph = 1; - double Dl = 0.0; - double Dv = 0.0; - double[] xliq = new double[20]; - double[] xvap = new double[20]; - long ierr = 0; - long herrLen = 255; - string herr = new string(' ', 255); - - lock (_refpropLock) - { - IRefProp64.SATPdll(ref pKPa, x, ref kph, ref tSatK, ref Dl, ref Dv, xliq, xvap, ref ierr, ref herr, ref herrLen); - } - - if (ierr != 0) - { - error = $"SATP 错误: {herr.Trim()} (ierr={ierr})"; - return false; - } - - Dl_molL = Dl; - Dv_molL = Dv; - return true; - } - - public bool TryTPRHO_VaporDensity_ByTP_MPa_C(double pressureMPa, double temperatureC, out double densityMolPerL, out string error) - { - densityMolPerL = double.NaN; - error = string.Empty; - - double tK = temperatureC + 273.15; - double pKPa = pressureMPa * 1000.0; - - double[] x = new double[20]; - x[0] = 1.0; - - long kph = 2; - long kguess = 0; - double D = 0.0; - long ierr = 0; - long herrLen = 255; - string herr = new string(' ', 255); - - lock (_refpropLock) - { - IRefProp64.TPRHOdll(ref tK, ref pKPa, x, ref kph, ref kguess, ref D, ref ierr, ref herr, ref herrLen); - } - - if (ierr != 0) - { - error = $"TPRHO 错误: {herr.Trim()} (ierr={ierr})"; - return false; - } - - densityMolPerL = D; - return true; - } - - public bool TryTHERM_VaporEntropy_ByTD(double temperatureC, double densityMolPerL, out double entropy_kJ_per_kgK, out string error) - { - entropy_kJ_per_kgK = double.NaN; - error = string.Empty; - - double tK = temperatureC + 273.15; - double D = densityMolPerL; - - double[] x = new double[20]; - x[0] = 1.0; - - double pOut = 0; - double e = 0; - double hJmol = 0; - double sJmolK = 0; - double cv = 0; - double cp = 0; - double w = 0; - double hjt = 0; - - double molarMassKgPerMol = _molarMassKgPerMol; - if (molarMassKgPerMol <= 0) - { - error = "无效的摩尔质量"; - return false; - } - - lock (_refpropLock) - { - IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt); - } - - entropy_kJ_per_kgK = (sJmolK / molarMassKgPerMol) * 0.001; - return true; - } - - public bool TryTPRHO_LiquidDensity_ByTP_MPa_C(double pressureMPa, double temperatureC, out double densityMolPerL, out string error) - { - densityMolPerL = double.NaN; - error = string.Empty; - - double tK = temperatureC + 273.15; - double pKPa = pressureMPa * 1000.0; - - double[] x = new double[20]; - x[0] = 1.0; - - long kph = 1; - long kguess = 0; - double D = 0.0; - long ierr = 0; - long herrLen = 255; - string herr = new string(' ', 255); - - lock (_refpropLock) - { - IRefProp64.TPRHOdll(ref tK, ref pKPa, x, ref kph, ref kguess, ref D, ref ierr, ref herr, ref herrLen); - } - - if (ierr != 0) - { - error = $"TPRHO(液相) 错误: {herr.Trim()} (ierr={ierr})"; - return false; - } - - densityMolPerL = D; - return true; - } - - public bool TryTHERM_LiquidEnthalpy_ByTD(double temperatureC, double densityMolPerL, out double h_liq_kJ_per_kg, out string error) - { - h_liq_kJ_per_kg = double.NaN; - error = string.Empty; - - double tK = temperatureC + 273.15; - double D = densityMolPerL; - - double[] x = new double[20]; - x[0] = 1.0; - - double pOut = 0; - double e = 0; - double hJmol = 0; - double sJmolK = 0; - double cv = 0; - double cp = 0; - double w = 0; - double hjt = 0; - - double molarMassKgPerMol = _molarMassKgPerMol; - if (molarMassKgPerMol <= 0) - { - error = "无效的摩尔质量"; - return false; - } - - lock (_refpropLock) - { - IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt); - } - - h_liq_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001; - return true; - } - - public bool TryTHERM_Enthalpy_kJkg_ByT_K_D(double temperatureK, double densityMolPerL, out double h_kJ_per_kg, out string error) - { - h_kJ_per_kg = double.NaN; - error = string.Empty; - - double tK = temperatureK; - double D = densityMolPerL; - - double[] x = new double[20]; - x[0] = 1.0; - - double pOut = 0; - double e = 0; - double hJmol = 0; - double sJmolK = 0; - double cv = 0; - double cp = 0; - double w = 0; - double hjt = 0; - - double molarMassKgPerMol = _molarMassKgPerMol; - if (molarMassKgPerMol <= 0) - { - error = "无效的摩尔质量"; - return false; - } - - lock (_refpropLock) - { - IRefProp64.THERMdll(ref tK, ref D, x, ref pOut, ref e, ref hJmol, ref sJmolK, ref cv, ref cp, ref w, ref hjt); - } - - h_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001; - return true; - } - - public bool TryConvertMolarDensityToSpecificVolume(double d_molL, out double v_m3kg, out string error) - { - v_m3kg = double.NaN; - error = string.Empty; - - if (double.IsNaN(d_molL) || double.IsInfinity(d_molL) || d_molL <= 0) - { - error = $"无效摩尔密度: {d_molL} mol/L"; - return false; - } - - double molarMassKgPerMol = _molarMassKgPerMol; - if (molarMassKgPerMol <= 0) - { - error = "无效的摩尔质量"; - return false; - } - - double rho_kg_m3 = d_molL * molarMassKgPerMol * 1000.0; - if (rho_kg_m3 <= 0) - { - error = $"无效密度: {rho_kg_m3} kg/m3"; - return false; - } - - v_m3kg = 1.0 / rho_kg_m3; - return true; - } - - public bool TryTPFLSH_ByTP_BarA_C( - double pressureBarA, - double temperatureC, - out double d_molL, - out double h_Jmol, - out double s_JmolK, - out double t_K, - out string error) - { - d_molL = double.NaN; - h_Jmol = double.NaN; - s_JmolK = double.NaN; - t_K = double.NaN; - error = string.Empty; - - if (!EnsureRefpropInitialized(out var initErr)) - { - error = initErr; - return false; - } - - double t = temperatureC + 273.15; - double pKPa = pressureBarA * 100.0; - - if (pKPa <= 0) - { - error = $"无效压力: {pressureBarA} BarA"; - return false; - } - - double[] x = new double[20]; - x[0] = 1.0; - - double d = 0.0; - double Dl = 0.0; - double Dv = 0.0; - double[] xliq = new double[20]; - double[] xvap = new double[20]; - double q = 0.0; - double ee = 0.0; - double h = 0.0; - double ss = 0.0; - double Cv = 0.0; - double Cp = 0.0; - double w = 0.0; - long ierr = 0; - long herrLen = 255; - string herr = new string(' ', 255); - - lock (_refpropLock) - { - IRefProp64.TPFLSHdll(ref t, ref pKPa, x, ref d, ref Dl, ref Dv, xliq, xvap, ref q, ref ee, ref h, ref ss, ref Cv, ref Cp, ref w, ref ierr, ref herr, ref herrLen); - } - - if (ierr != 0) - { - error = $"TPFLSH 错误: {herr.Trim()} (ierr={ierr})"; - return false; - } - - d_molL = d; - h_Jmol = h; - s_JmolK = ss; - t_K = t; - return true; - } - - public bool TryConvertH_Jmol_To_kJkg(double h_Jmol, out double h_kJkg, out string error) - { - h_kJkg = double.NaN; - error = string.Empty; - - double molarMassKgPerMol = _molarMassKgPerMol; - if (molarMassKgPerMol <= 0) - { - error = "无效的摩尔质量"; - return false; - } - - h_kJkg = (h_Jmol / molarMassKgPerMol) * 0.001; - return true; - } - - public double GetMolarMass() - { - if (double.IsNaN(_molarMassKgPerMol) || double.IsInfinity(_molarMassKgPerMol) || _molarMassKgPerMol <= 0) - { - double wmm = 0, Trp = 0, Tnbpt = 0, Tc = 0, Pc = 0, Dc = 0, Zc = 0, acf = 0, dip = 0, Rgas = 0; - long componentId = 1; - - lock (_refpropLock) - { - IRefProp64.INFOdll(ref componentId, ref wmm, ref Trp, ref Tnbpt, ref Tc, ref Pc, ref Dc, ref Zc, ref acf, ref dip, ref Rgas); - } - _molarMassKgPerMol = wmm * 0.001; - } - return _molarMassKgPerMol; - } - - public bool TryGetIsentropicOutletEnthalpy_h2s_ByP2AndS1_BarA(double dischargePressureBarA, double suctionEntropy_kJkgK, out double h2s_kJkg, out string error) - { - h2s_kJkg = double.NaN; - error = string.Empty; - - double pKPa = dischargePressureBarA * 100.0; - if (pKPa <= 0) - { - error = $"无效排气压力: {dischargePressureBarA} BarA"; - return false; - } - - if (double.IsNaN(suctionEntropy_kJkgK) || double.IsInfinity(suctionEntropy_kJkgK)) - { - error = "无效吸气熵 s1"; - return false; - } - - if (!TryConvertS_kJkgK_To_JmolK(suctionEntropy_kJkgK, out var s_JmolK, out var convErr)) - { - error = convErr; - return false; - } - - double[] z = new double[20]; - z[0] = 1.0; - - double t = 0.0; - double d = 0.0; - double Dl = 0.0; - double Dv = 0.0; - double[] xliq = new double[20]; - double[] xvap = new double[20]; - double q = 0.0; - double ee = 0.0; - double h = 0.0; - double Cv = 0.0; - double Cp = 0.0; - double w = 0.0; - long ierr = 0; - long herrLen = 255; - string herr = new string(' ', 255); - - lock (_refpropLock) - { - IRefProp64.PSFLSHdll(ref pKPa, ref s_JmolK, z, ref t, ref d, ref Dl, ref Dv, xliq, xvap, ref q, ref ee, ref h, ref Cv, ref Cp, ref w, ref ierr, ref herr, ref herrLen); - } - - if (ierr != 0) - { - error = $"PSFLSH 错误: {herr.Trim()} (ierr={ierr})"; - return false; - } - - return TryConvertH_Jmol_To_kJkg(h, out h2s_kJkg, out error); - } - - private static bool TryConvertS_kJkgK_To_JmolK(double s_kJkgK, out double s_JmolK, out string error) - { - s_JmolK = double.NaN; - error = string.Empty; - - double molarMassKgPerMol = _molarMassKgPerMol; - if (molarMassKgPerMol <= 0) - { - error = "无效的摩尔质量"; - return false; - } - - s_JmolK = s_kJkgK * 1000.0 * molarMassKgPerMol; - return true; - } - } - } -}