From b04fc71d6a17aa4ac0c0fa44796e1c7c0c61f65b Mon Sep 17 00:00:00 2001 From: Tyrone CT Date: Wed, 6 May 2026 15:20:57 +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=E5=B9=B2=E5=BA=A6=E7=9A=84=E5=85=AC=E5=BC=8F=E7=9A=84?= =?UTF-8?q?=E6=9B=B4=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CapMachine.Wpf/App.config | 1 + .../EnthalpyDrynessCalculator.cs | 640 ++++++++++++++++++ CapMachine.Wpf/Services/PPCService.cs | 414 +++++------ .../PPCThermodynamicSixResultsCalculator.cs | 124 +++- 4 files changed, 890 insertions(+), 289 deletions(-) create mode 100644 CapMachine.Wpf/PPCalculation/EnthalpyDrynessCalculator.cs diff --git a/CapMachine.Wpf/App.config b/CapMachine.Wpf/App.config index 3d94554..ce3c408 100644 --- a/CapMachine.Wpf/App.config +++ b/CapMachine.Wpf/App.config @@ -9,6 +9,7 @@ + \ No newline at end of file diff --git a/CapMachine.Wpf/PPCalculation/EnthalpyDrynessCalculator.cs b/CapMachine.Wpf/PPCalculation/EnthalpyDrynessCalculator.cs new file mode 100644 index 0000000..ae1d9b2 --- /dev/null +++ b/CapMachine.Wpf/PPCalculation/EnthalpyDrynessCalculator.cs @@ -0,0 +1,640 @@ +using CapMachine.Core; +using System; + +namespace CapMachine.Wpf.PPCalculation +{ + /// + /// 干度(品质)计算器:按“焓加权混合 + 饱和焓归一化”的流程计算 Dryness1/Dryness2。 + /// + public sealed class EnthalpyDrynessCalculator + { + private readonly object _refpropLock; + + private static volatile bool _rpInitialized; + + /// + /// 构造函数。 + /// + /// REFPROP 全局互斥锁对象(必须与系统其它 REFPROP 调用共用,以避免并发竞态)。 + public EnthalpyDrynessCalculator(object refpropLock) + { + _refpropLock = refpropLock ?? throw new ArgumentNullException(nameof(refpropLock)); + } + + /// + /// 干度计算输入模型(以 Tag 读数为准)。 + /// + public readonly struct Input + { + public Input( + double gasPreValvePressBarA, + double gasPreValveTempC, + double txvFrPressBarA, + double txvFrTempC, + double inhPressBarA, + double vrvFlowKgPerH, + double liqRefFlowKgPerH, + double lubeFlowKgPerH) + { + GasPreValvePressBarA = gasPreValvePressBarA; + GasPreValveTempC = gasPreValveTempC; + TxvFrPressBarA = txvFrPressBarA; + TxvFrTempC = txvFrTempC; + InhPressBarA = inhPressBarA; + VRVFlowKgPerH = vrvFlowKgPerH; + LiqRefFlowKgPerH = liqRefFlowKgPerH; + LubeFlowKgPerH = lubeFlowKgPerH; + } + + public double GasPreValvePressBarA { get; } + public double GasPreValveTempC { get; } + public double TxvFrPressBarA { get; } + public double TxvFrTempC { get; } + public double InhPressBarA { get; } + + public double VRVFlowKgPerH { get; } + public double LiqRefFlowKgPerH { get; } + public double LubeFlowKgPerH { get; } + } + + /// + /// 干度计算输出模型。 + /// + public readonly struct Result + { + public Result( + double gasFlowKgPerH, + double gasEnthalpy_kJkg, + double liquidEnthalpy_kJkg, + double satLiquidEnthalpy_kJkg, + double satVaporEnthalpy_kJkg, + bool isDryness1Success, + double dryness1_01, + double hMix1_kJkg, + string error1, + bool isDryness2Success, + double dryness2_01, + double hMix2_kJkg, + string error2) + { + GasFlowKgPerH = gasFlowKgPerH; + GasEnthalpy_kJkg = gasEnthalpy_kJkg; + LiquidEnthalpy_kJkg = liquidEnthalpy_kJkg; + SatLiquidEnthalpy_kJkg = satLiquidEnthalpy_kJkg; + SatVaporEnthalpy_kJkg = satVaporEnthalpy_kJkg; + + IsDryness1Success = isDryness1Success; + Dryness1_01 = dryness1_01; + HMix1_kJkg = hMix1_kJkg; + Error1 = error1 ?? string.Empty; + + IsDryness2Success = isDryness2Success; + Dryness2_01 = dryness2_01; + HMix2_kJkg = hMix2_kJkg; + Error2 = error2 ?? string.Empty; + } + + public double GasFlowKgPerH { get; } + public double GasEnthalpy_kJkg { get; } + public double LiquidEnthalpy_kJkg { get; } + public double SatLiquidEnthalpy_kJkg { get; } + public double SatVaporEnthalpy_kJkg { get; } + + public bool IsDryness1Success { get; } + public double Dryness1_01 { get; } + public double HMix1_kJkg { get; } + public string Error1 { get; } + + public bool IsDryness2Success { get; } + public double Dryness2_01 { get; } + public double HMix2_kJkg { get; } + public string Error2 { get; } + } + + /// + /// 计算干度 1/2。 + /// + /// 输入数据。 + /// 计算结果(包含两路干度及中间量)。 + public Result Calculate(Input input) + { + if (!EnsureRefpropInitialized(out _)) + { + return new Result( + gasFlowKgPerH: double.NaN, + gasEnthalpy_kJkg: double.NaN, + liquidEnthalpy_kJkg: double.NaN, + satLiquidEnthalpy_kJkg: double.NaN, + satVaporEnthalpy_kJkg: double.NaN, + isDryness1Success: false, + dryness1_01: double.NaN, + hMix1_kJkg: double.NaN, + error1: "REFPROP 未初始化", + isDryness2Success: false, + dryness2_01: double.NaN, + hMix2_kJkg: double.NaN, + error2: "REFPROP 未初始化"); + } + + double gasFlowKgPerH = input.VRVFlowKgPerH - input.LiqRefFlowKgPerH; + + double gas_hVap_kJkg = 0.0; + if (TryTPRHO_VaporDensity_ByTP_MPa_C(input.GasPreValvePressBarA * 0.1, input.GasPreValveTempC, out var dVap_molL, out _)) + { + if (TryTHERM_VaporEnthalpy_ByTD(input.GasPreValveTempC, dVap_molL, out var hVap_kJkg, out _)) + { + gas_hVap_kJkg = hVap_kJkg; + } + } + + double liquid_hLiq_kJkg = 0.0; + if (TryTPRHO_LiquidDensity_ByTP_MPa_C(input.TxvFrPressBarA * 0.1, input.TxvFrTempC, out var dLiq_molL, out _)) + { + if (TryTHERM_LiquidEnthalpy_ByTD(input.TxvFrTempC, dLiq_molL, out var hLiq_kJkg, out _)) + { + liquid_hLiq_kJkg = hLiq_kJkg; + } + } + + double hSatL_kJkg = 0.0; + double hSatV_kJkg = 0.0; + if (TryGetSaturationLiquidEnthalpy_ByP_MPa(input.InhPressBarA * 0.1, out var satL, out _) && + TryGetSaturationVaporEnthalpy_ByP_MPa(input.InhPressBarA * 0.1, out var satV, out _)) + { + hSatL_kJkg = satL; + hSatV_kJkg = satV; + } + + bool ok1 = TryComputeDrynessByEnthalpy( + gas_hVap_kJkg, + liquid_hLiq_kJkg, + gasFlowKgPerH, + input.LiqRefFlowKgPerH, + hSatL_kJkg, + hSatV_kJkg, + out var dryness1, + out var hMix1, + out var err1); + + bool ok2 = TryComputeDrynessByEnthalpy2( + gas_hVap_kJkg, + liquid_hLiq_kJkg, + gasFlowKgPerH, + input.LubeFlowKgPerH, + input.LiqRefFlowKgPerH, + hSatL_kJkg, + hSatV_kJkg, + out var dryness2, + out var hMix2, + out var err2); + + return new Result( + gasFlowKgPerH: gasFlowKgPerH, + gasEnthalpy_kJkg: gas_hVap_kJkg, + liquidEnthalpy_kJkg: liquid_hLiq_kJkg, + satLiquidEnthalpy_kJkg: hSatL_kJkg, + satVaporEnthalpy_kJkg: hSatV_kJkg, + isDryness1Success: ok1, + dryness1_01: dryness1, + hMix1_kJkg: hMix1, + error1: err1, + isDryness2Success: ok2, + dryness2_01: dryness2, + hMix2_kJkg: hMix2, + error2: err2); + } + + /// + /// REFPROP 初始化(幂等)。 + /// + /// 失败原因。 + /// 是否初始化成功。 + 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; + 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; + } + } + + private 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; + } + + 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_VaporEnthalpy_ByTD(double temperatureC, double densityMolPerL, out double h_vap_kJ_per_kg, out string error) + { + h_vap_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; + + if (!TryGetMolarMassKgPerMol(1, out var molarMassKgPerMol, out 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_vap_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001; + 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; + + if (!TryGetMolarMassKgPerMol(1, out var molarMassKgPerMol, out 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_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, Dv = 0; + double[] xliq = new double[20]; + double[] xvap = new double[20]; + long ierr = 0, 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; + } + + private 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, e = 0, hJmol = 0, sJmolK = 0, cv = 0, cp = 0, w = 0, hjt = 0; + + if (!TryGetMolarMassKgPerMol(1, out var molarMassKgPerMol, out 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; + } + + private bool TryGetSaturationLiquidEnthalpy_ByP_MPa(double pressureMPa, out double h_liq_kJkg, out string error) + { + h_liq_kJkg = double.NaN; + error = string.Empty; + + if (!TrySATP_SaturationByP_MPa(pressureMPa, out double tSatK, out double Dl, out _, out error)) + { + return false; + } + + return TryTHERM_Enthalpy_kJkg_ByT_K_D(tSatK, Dl, out h_liq_kJkg, out error); + } + + private bool TryGetSaturationVaporEnthalpy_ByP_MPa(double pressureMPa, out double h_vap_kJkg, out string error) + { + h_vap_kJkg = double.NaN; + error = string.Empty; + + if (!TrySATP_SaturationByP_MPa(pressureMPa, out double tSatK, out _, out double Dv, out error)) + { + return false; + } + + return TryTHERM_Enthalpy_kJkg_ByT_K_D(tSatK, Dv, out h_vap_kJkg, out error); + } + + private static bool TryComputeDrynessByEnthalpy( + double hVap_kJkg, + double hLiq_kJkg, + double mGas_kg_h, + double mLiq_kg_h, + double hSatL_kJkg, + double hSatV_kJkg, + out double dryness, + out double hMix_kJkg, + out string error) + { + dryness = double.NaN; + hMix_kJkg = double.NaN; + error = string.Empty; + + if (double.IsNaN(hVap_kJkg) || double.IsNaN(hLiq_kJkg) || double.IsNaN(hSatL_kJkg) || double.IsNaN(hSatV_kJkg)) + { + error = "输入焓值存在 NaN"; + return false; + } + if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h)) + { + error = "输入质量流量存在 NaN"; + return false; + } + + double mg = Math.Max(0.0, mGas_kg_h); + double ml = Math.Max(0.0, mLiq_kg_h); + double mSum = mg + ml; + if (mSum <= 0) + { + error = "气液质量流量之和为 0,无法进行加权混合焓计算"; + return false; + } + + hMix_kJkg = (hVap_kJkg * mg + hLiq_kJkg * ml) / mSum; + + double denom = (hSatV_kJkg - hSatL_kJkg); + const double eps = 1e-9; + if (Math.Abs(denom) < eps) + { + error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)"; + return false; + } + + double x = (hMix_kJkg - hSatL_kJkg) / denom; + + if (double.IsNaN(x) || double.IsInfinity(x)) + { + error = "干度计算结果异常(NaN/Inf)"; + return false; + } + + dryness = Math.Min(1.0, Math.Max(0.0, x)); + return true; + } + + private static bool TryComputeDrynessByEnthalpy2( + double hVap_kJkg, + double hLiq_kJkg, + double mGas_kg_h, + double lubeFlow_kg_h, + double mLiq_kg_h, + double hSatL_kJkg, + double hSatV_kJkg, + out double dryness, + out double hMix_kJkg, + out string error) + { + dryness = double.NaN; + hMix_kJkg = double.NaN; + error = string.Empty; + + if (double.IsNaN(hVap_kJkg) || double.IsNaN(hLiq_kJkg) || double.IsNaN(hSatL_kJkg) || double.IsNaN(hSatV_kJkg)) + { + error = "输入焓值存在 NaN"; + return false; + } + if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h)) + { + error = "输入质量流量存在 NaN"; + return false; + } + + double mg = Math.Max(0.0, mGas_kg_h) + Math.Max(0.0, lubeFlow_kg_h); + double ml = Math.Max(0.0, mLiq_kg_h); + double mSum = mg + ml; + if (mSum <= 0) + { + error = "气液质量流量之和为 0,无法进行加权混合焓计算"; + return false; + } + + hMix_kJkg = (hVap_kJkg * mg + hLiq_kJkg * ml) / mSum; + + double denom = (hSatV_kJkg - hSatL_kJkg); + const double eps = 1e-9; + if (Math.Abs(denom) < eps) + { + error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)"; + return false; + } + + double x = (hMix_kJkg - hSatL_kJkg) / denom; + + if (double.IsNaN(x) || double.IsInfinity(x)) + { + error = "干度计算结果异常(NaN/Inf)"; + return false; + } + + dryness = Math.Min(1.0, Math.Max(0.0, x)); + return true; + } + + private static bool TryGetMolarMassKgPerMol(long componentId, out double molarMassKgPerMol, out string error) + { + molarMassKgPerMol = double.NaN; + error = string.Empty; + + try + { + 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; + 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 (molarMassKgPerMol <= 0) + { + error = "无效的摩尔质量"; + return false; + } + + return true; + } + catch (Exception ex) + { + error = $"读取摩尔质量异常: {ex.Message}"; + return false; + } + } + } +} diff --git a/CapMachine.Wpf/Services/PPCService.cs b/CapMachine.Wpf/Services/PPCService.cs index d1e4a76..d71834f 100644 --- a/CapMachine.Wpf/Services/PPCService.cs +++ b/CapMachine.Wpf/Services/PPCService.cs @@ -32,9 +32,10 @@ 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; /// /// 标签中心 @@ -71,39 +72,39 @@ namespace CapMachine.Wpf.Services InhTempTag = TagManager.DicTags.GetValueOrDefault("吸气温度[℃]"); - //InhTempTag = TagManager.DicTags.GetValueOrDefault("吸气温度[℃]")!; - //ComCapBusVolTag = TagManager.DicTags.GetValueOrDefault("通讯母线电压[V]"); - //ComCapBusCurTag = TagManager.DicTags.GetValueOrDefault("通讯母线电流[A]"); - //ComCapPwTag = TagManager.DicTags.GetValueOrDefault("通讯功率[W]"); - //OS2TempTag = TagManager.DicTags.GetValueOrDefault("吸气混合器温度[℃]"); - - //TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]")!; - //TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]")!; - TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]"); TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]"); - //LiqRefFlowTag = TagManager.DicTags.GetValueOrDefault("液冷媒流量[kg/h]"); + GasPreValvePressTag = TagManager.DicTags.GetValueOrDefault("气路阀前压力[BarA]"); + GasPreValveTempTag = TagManager.DicTags.GetValueOrDefault("气路阀前温度[℃]"); - //kg/h + DrynessTag = TagManager.DicTags.GetValueOrDefault("干度[-]"); + if (DrynessTag == null) + { + DrynessTag = TagManager.DicTags.GetValueOrDefault("干度"); + } - VRVTag = TagManager.DicTags.GetValueOrDefault("冷媒流量[L/min]"); + VRVTag = TagManager.DicTags.GetValueOrDefault("冷媒流量[kg/h]"); + if (VRVTag == null) + { + VRVTag = TagManager.DicTags.GetValueOrDefault("冷媒流量[L/min]"); + } - //润滑油流量 + LiqRefFlowTag = TagManager.DicTags.GetValueOrDefault("液冷媒流量[kg/h]"); + if (LiqRefFlowTag == null) + { + LiqRefFlowTag = TagManager.DicTags.GetValueOrDefault("液体流量[kg/h]"); + } - LubeFlowTag = TagManager.DicTags.GetValueOrDefault("润滑油流量[L/min]"); - - - //Cond1TempTag = TagManager.DicTags.GetValueOrDefault("冷凝器出口水温[℃]"); - //CondInTempTag = TagManager.DicTags.GetValueOrDefault("冷凝器进口温度[℃]"); - - //Superheat = TagManager.DicTags.GetValueOrDefault("过热度[K]"); - //Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]"); + LubeFlowTag = TagManager.DicTags.GetValueOrDefault("润滑油流量[kg/h]"); + if (LubeFlowTag == null) + { + LubeFlowTag = TagManager.DicTags.GetValueOrDefault("润滑油流量[L/min]"); + } Superheat = TagManager.DicTags.GetValueOrDefault("过热度[K]"); - Subcool = TagManager.DicTags.GetValueOrDefault("过冷度[K]"); HeatingCapacity = TagManager.DicTags.GetValueOrDefault("制热量Qh[KW]"); @@ -113,15 +114,37 @@ namespace CapMachine.Wpf.Services COPCool = TagManager.DicTags.GetValueOrDefault("压缩机性能系数(制冷)[K]"); VoltricEff = TagManager.DicTags.GetValueOrDefault("容积效率nv[%]"); - SuperHeatCoolConfig.FluidsPath = ConfigHelper.GetValue("FluidsPath"); SuperHeatCoolConfig.Cryogen = ConfigHelper.GetValue("Cryogen"); _superheatSubcoolCalculator = new PPCSuperheatSubcoolCalculator(); - _thermodynamicSixResultsCalculator = new PPCThermodynamicSixResultsCalculator(); + _thermodynamicSixResultsCalculator = new PPCThermodynamicSixResultsCalculator(_refpropLock); + _enthalpyDrynessCalculator = new EnthalpyDrynessCalculator(_refpropLock); + ReloadTherdyH3TempOffset(); RtScanDeviceStart(); } + private const string TherdyH3TempOffsetConfigKey = "Therdy_H3TempOffset_C"; + + public void ReloadTherdyH3TempOffset() + { + double offsetC = -10.0; + try + { + string raw = ConfigHelper.GetValue(TherdyH3TempOffsetConfigKey); + if (!string.IsNullOrWhiteSpace(raw) && double.TryParse(raw, out var parsed)) + { + offsetC = parsed; + } + } + catch (Exception ex) + { + Logger?.Error($"读取 {TherdyH3TempOffsetConfigKey} 失败: {ex.Message}"); + } + + _thermodynamicSixResultsCalculator.SetH3TempOffset_C(offsetC); + } + /// /// 当前的配置 /// @@ -227,7 +250,6 @@ namespace CapMachine.Wpf.Services /// public ITag LubeFlowTag { get; set; } - public ITag HeatingCapacity { get; set; } public ITag COPHeat { get; set; } public ITag IsentrpEff { get; set; } @@ -235,7 +257,6 @@ namespace CapMachine.Wpf.Services public ITag COPCool { get; set; } public ITag VoltricEff { get; set; } - /// /// 风量数据-乘以系数的后的最终结果 /// @@ -261,6 +282,48 @@ namespace CapMachine.Wpf.Services /// private bool DebugLog { get; set; } = false; + private double _HeatingCapacityQh_kW; + public double HeatingCapacityQh_kW + { + get { return _HeatingCapacityQh_kW; } + set { _HeatingCapacityQh_kW = value; RaisePropertyChanged(); } + } + + private double _COPHeating; + public double COPHeating + { + get { return _COPHeating; } + set { _COPHeating = value; RaisePropertyChanged(); } + } + + private double _IsentropicEfficiencyPct; + public double IsentropicEfficiencyPct + { + get { return _IsentropicEfficiencyPct; } + set { _IsentropicEfficiencyPct = value; RaisePropertyChanged(); } + } + + private double _CoolingCapacityQc_kW; + public double CoolingCapacityQc_kW + { + get { return _CoolingCapacityQc_kW; } + set { _CoolingCapacityQc_kW = value; RaisePropertyChanged(); } + } + + private double _COPCooling; + public double COPCooling + { + get { return _COPCooling; } + set { _COPCooling = value; RaisePropertyChanged(); } + } + + private double _VolumetricEfficiencyPct; + public double VolumetricEfficiencyPct + { + get { return _VolumetricEfficiencyPct; } + set { _VolumetricEfficiencyPct = value; RaisePropertyChanged(); } + } + /// /// PLC扫描线程 /// @@ -281,6 +344,21 @@ namespace CapMachine.Wpf.Services } } + if (TryUpdateDryness(out var drynessErr)) + { + if (!string.IsNullOrWhiteSpace(drynessErr)) + { + Logger?.Error($"干度计算警告: {drynessErr}"); + } + } + else + { + if (!string.IsNullOrWhiteSpace(drynessErr)) + { + Logger?.Error($"干度计算失败: {drynessErr}"); + } + } + if (TryUpdateThermodynamicSixResults(out var thermoErr)) { if (!string.IsNullOrWhiteSpace(thermoErr)) @@ -305,27 +383,12 @@ namespace CapMachine.Wpf.Services }); } - /// - /// 更新过热度与过冷度结果。 - /// - /// - /// 错误汇总输出。 - /// 当两个结果都失败时,返回拼接后的失败原因; - /// 当仅一个结果失败时,只返回该项失败原因; - /// 当至少有一项成功更新时,方法返回 。 - /// - /// - /// 是否至少成功更新了一个结果。 - /// 该方法保持原有行为:过热度和过冷度彼此独立,只要其中之一成功就返回 。 - /// private bool TryUpdateSuperheatAndSubcool(out string error) { error = string.Empty; bool updated = false; StringBuilder errorBuilder = new StringBuilder(); - // 第一段:收集过热度所需标签,并把实时值直接交给独立计算类。 - // PPCService 只负责“取值 + 回写”,具体热力学过程由 PPCSuperheatSubcoolCalculator 承担。 if (InhPressTag == null || InhTempTag == null || Superheat == null) { AppendCalculationError(errorBuilder, "缺少过热度计算标签"); @@ -340,8 +403,6 @@ namespace CapMachine.Wpf.Services AppendCalculationError(errorBuilder, superheatErr); } - // 第二段:收集过冷度所需标签,并按同样方式委托给独立计算类。 - // 两个结果互不阻塞,保持与旧实现一致的“部分成功也可回写”的策略。 if (TxvFrPressTag == null || TxvFrTempTag == null || Subcool == null) { AppendCalculationError(errorBuilder, "缺少过冷度计算标签"); @@ -375,234 +436,67 @@ namespace CapMachine.Wpf.Services errorBuilder.Append(error); } - /// - /// 按图片的最终流程计算干度: - /// 1) 质量流量加权混合焓 h_mix = (h_vap*mg + h_liq*ml) / (mg + ml) - /// 2) 干度 x = (h_mix - h_l) / (h_v - h_l),并限幅到 [0,1] - /// - /// 入参单位: - /// - hVap_kJkg, hLiq_kJkg, hSatL_kJkg, hSatV_kJkg 均为 kJ/kg - /// - mGas_kg_h, mLiq_kg_h 均为 kg/h - /// 返回:true 表示成功,输出 x∈[0,1] 与 h_mix;false 返回 error - /// - /// 气相质量焓 h_vap [kJ/kg] - /// 液相质量焓 h_liq [kJ/kg] - /// 气体质量流量 mg [kg/h] - /// 液体质量流量 ml [kg/h] - /// 饱和液质量焓 h_l [kJ/kg] - /// 饱和气质量焓 h_v [kJ/kg] - /// 输出干度 x ∈ [0,1] - /// 输出混合后总比焓 h_mix [kJ/kg] - /// Err - /// - public bool TryComputeDrynessByEnthalpy( - double hVap_kJkg, // 气相质量焓 h_vap [kJ/kg] - double hLiq_kJkg, // 液相质量焓 h_liq [kJ/kg] - double mGas_kg_h, // 气体质量流量 mg [kg/h] - double mLiq_kg_h, // 液体质量流量 ml [kg/h] - double hSatL_kJkg, // 饱和液质量焓 h_l [kJ/kg] - double hSatV_kJkg, // 饱和气质量焓 h_v [kJ/kg] - out double dryness, // 输出干度 x ∈ [0,1] - out double hMix_kJkg, // 输出混合后总比焓 h_mix [kJ/kg] - out string error) + private bool TryUpdateDryness(out string error) { - dryness = double.NaN; - hMix_kJkg = double.NaN; error = string.Empty; + bool updated = false; + StringBuilder errorBuilder = new StringBuilder(); - // 1) 合法性校验 - if (double.IsNaN(hVap_kJkg) || double.IsNaN(hLiq_kJkg) || double.IsNaN(hSatL_kJkg) || double.IsNaN(hSatV_kJkg)) + if (GasPreValvePressTag == null || GasPreValveTempTag == null || TxvFrPressTag == null || TxvFrTempTag == null || InhPressTag == null) { - error = "输入焓值存在 NaN"; + AppendCalculationError(errorBuilder, "缺少干度计算压力/温度标签"); + error = errorBuilder.ToString(); return false; } - if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h)) + if (VRVTag == null || LiqRefFlowTag == null) { - error = "输入质量流量存在 NaN"; - return false; - } - // 负值处理:小于 0 视为 0(避免传感器噪声或符号错误影响) - double mg = Math.Max(0.0, mGas_kg_h); - double ml = Math.Max(0.0, mLiq_kg_h); - double mSum = mg + ml; - if (mSum <= 0) - { - error = "气液质量流量之和为 0,无法进行加权混合焓计算"; + AppendCalculationError(errorBuilder, "缺少干度计算流量标签"); + error = errorBuilder.ToString(); return false; } - // 2) 质量流量加权混合焓(严格按图片:上、下两路相加后除以总流量) - hMix_kJkg = (hVap_kJkg * mg + hLiq_kJkg * ml) / mSum; - - // 3) 干度计算:x = (h_mix - h_l) / (h_v - h_l) - double denom = (hSatV_kJkg - hSatL_kJkg); - const double eps = 1e-9; - if (Math.Abs(denom) < eps) + double lubeFlowKgPerH = 0.0; + if (LubeFlowTag != null) { - error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)"; - return false; + lubeFlowKgPerH = LubeFlowTag.EngPvValue; } - double x = (hMix_kJkg - hSatL_kJkg) / denom; + 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)); - // 4) 限幅到 [0,1] - if (double.IsNaN(x) || double.IsInfinity(x)) + if (drynessResult.IsDryness1Success) { - error = "干度计算结果异常(NaN/Inf)"; - return false; + if (DrynessTag != null) + { + DrynessTag.EngPvValue = Math.Round(drynessResult.Dryness1_01, 4); + } + updated = true; } - dryness = Math.Min(1.0, Math.Max(0.0, x)); - return true; - } - - - /// - /// 按图片的最终流程计算干度2: - /// 干度2的计算:临时用作为对比使用 - /// 1) 质量流量加权混合焓 h_mix = (h_vap*mg + h_liq*ml) / (mg + ml) - /// 2) 干度 x = (h_mix - h_l) / (h_v - h_l),并限幅到 [0,1] - /// - /// 入参单位: - /// - hVap_kJkg, hLiq_kJkg, hSatL_kJkg, hSatV_kJkg 均为 kJ/kg - /// - mGas_kg_h, mLiq_kg_h 均为 kg/h - /// 返回:true 表示成功,输出 x∈[0,1] 与 h_mix;false 返回 error - /// - /// 气相质量焓 h_vap [kJ/kg] - /// 液相质量焓 h_liq [kJ/kg] - /// 气体质量流量 mg [kg/h] - /// 液体质量流量 ml [kg/h] - /// 饱和液质量焓 h_l [kJ/kg] - /// 饱和气质量焓 h_v [kJ/kg] - /// 输出干度 x ∈ [0,1] - /// 输出混合后总比焓 h_mix [kJ/kg] - /// Err - /// - public bool TryComputeDrynessByEnthalpy2( - double hVap_kJkg, // 气相质量焓 h_vap [kJ/kg] - double hLiq_kJkg, // 液相质量焓 h_liq [kJ/kg] - double mGas_kg_h, // 气体质量流量 mg [kg/h] - double lubeFlow_kg_h, // 润滑油流量 mg [kg/h] - double mLiq_kg_h, // 液体质量流量 ml [kg/h] - double hSatL_kJkg, // 饱和液质量焓 h_l [kJ/kg] - double hSatV_kJkg, // 饱和气质量焓 h_v [kJ/kg] - out double dryness, // 输出干度 x ∈ [0,1] - out double hMix_kJkg, // 输出混合后总比焓 h_mix [kJ/kg] - out string error) - { - dryness = double.NaN; - hMix_kJkg = double.NaN; - error = string.Empty; - - // 1) 合法性校验 - if (double.IsNaN(hVap_kJkg) || double.IsNaN(hLiq_kJkg) || double.IsNaN(hSatL_kJkg) || double.IsNaN(hSatV_kJkg)) + else { - error = "输入焓值存在 NaN"; - return false; - } - if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h)) - { - error = "输入质量流量存在 NaN"; - return false; - } - // 负值处理:小于 0 视为 0(避免传感器噪声或符号错误影响) - //double mg1 = Math.Max(0.0, mGas_kg_h); - double mg = Math.Max(0.0, mGas_kg_h) + Math.Max(0.0, lubeFlow_kg_h); // 这个是改动 气体流量再加上润滑油流量 - double ml = Math.Max(0.0, mLiq_kg_h); - double mSum = mg + ml; - if (mSum <= 0) - { - error = "气液质量流量之和为 0,无法进行加权混合焓计算"; - return false; + AppendCalculationError(errorBuilder, drynessResult.Error1); } - // 2) 质量流量加权混合焓(严格按图片:上、下两路相加后除以总流量) - hMix_kJkg = (hVap_kJkg * mg + hLiq_kJkg * ml) / mSum; - - // 3) 干度计算:x = (h_mix - h_l) / (h_v - h_l) - double denom = (hSatV_kJkg - hSatL_kJkg); - const double eps = 1e-9; - if (Math.Abs(denom) < eps) + if (drynessResult.IsDryness2Success) { - error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)"; - return false; + DrynessTag2Value = Math.Round(drynessResult.Dryness2_01, 4); + updated = true; + } + else + { + AppendCalculationError(errorBuilder, drynessResult.Error2); } - double x = (hMix_kJkg - hSatL_kJkg) / denom; - - // 4) 限幅到 [0,1] - if (double.IsNaN(x) || double.IsInfinity(x)) - { - error = "干度计算结果异常(NaN/Inf)"; - return false; - } - dryness = Math.Min(1.0, Math.Max(0.0, x)); - return true; - } - - - - - ///制热量、压缩机性能系数COP(制热)、等熵效率、制冷量、压缩机性能系数COP(制冷)、容积效率 计算 - #region - - private double _HeatingCapacityQh_kW; - /// - /// 制热量 Qh [kW] - /// - public double HeatingCapacityQh_kW - { - get { return _HeatingCapacityQh_kW; } - set { _HeatingCapacityQh_kW = value; RaisePropertyChanged(); } - } - - private double _COPHeating; - /// - /// 压缩机性能系数 COP(制热)[-] - /// - public double COPHeating - { - get { return _COPHeating; } - set { _COPHeating = value; RaisePropertyChanged(); } - } - - private double _IsentropicEfficiencyPct; - /// - /// 等熵效率 ηs [%] - /// - public double IsentropicEfficiencyPct - { - get { return _IsentropicEfficiencyPct; } - set { _IsentropicEfficiencyPct = value; RaisePropertyChanged(); } - } - - private double _CoolingCapacityQc_kW; - /// - /// 制冷量 Qc [kW] - /// - public double CoolingCapacityQc_kW - { - get { return _CoolingCapacityQc_kW; } - set { _CoolingCapacityQc_kW = value; RaisePropertyChanged(); } - } - - private double _COPCooling; - /// - /// 压缩机性能系数 COP(制冷)[-] - /// - public double COPCooling - { - get { return _COPCooling; } - set { _COPCooling = value; RaisePropertyChanged(); } - } - - private double _VolumetricEfficiencyPct; - /// - /// 容积效率 ηv [%] - /// - public double VolumetricEfficiencyPct - { - get { return _VolumetricEfficiencyPct; } - set { _VolumetricEfficiencyPct = value; RaisePropertyChanged(); } + error = errorBuilder.ToString(); + return updated; } /// @@ -690,11 +584,6 @@ namespace CapMachine.Wpf.Services error = "缺少总流量(冷媒流量)标签"; return false; } - if (LubeFlowTag == null) - { - error = "缺少油流量标签"; - return false; - } if (InhPressTag == null || InhTempTag == null) { error = "缺少吸气压力/吸气温度标签"; @@ -715,10 +604,15 @@ namespace CapMachine.Wpf.Services error = "缺少转速标签"; return false; } - if (!TryGetCompressorDisplacement_cc(out var displacementCc, out var displacementErr)) + if (!TryGetCompressorDisplacement_cc(out var displacementCc, out _)) { - error = displacementErr; - return false; + displacementCc = double.NaN; + } + + double oilFlowKgPerH = 0.0; + if (LubeFlowTag != null) + { + oilFlowKgPerH = LubeFlowTag.EngPvValue; } // 将实时标签值与配置值组装为独立计算类可直接消费的输入对象。 @@ -726,7 +620,7 @@ namespace CapMachine.Wpf.Services { CompressorPowerW = HVPwTag.EngPvValue, TotalMassFlowKgPerHour = VRVTag.EngPvValue, - OilMassFlowKgPerHour = LubeFlowTag.EngPvValue, + OilMassFlowKgPerHour = oilFlowKgPerH, SuctionPressureBarA = InhPressTag.EngPvValue, SuctionTemperatureC = InhTempTag.EngPvValue, DischargePressureBarA = ExPressTag.EngPvValue, @@ -776,7 +670,5 @@ namespace CapMachine.Wpf.Services return true; } - - #endregion } } diff --git a/CapMachine.Wpf/Services/PPCThermodynamicSixResultsCalculator.cs b/CapMachine.Wpf/Services/PPCThermodynamicSixResultsCalculator.cs index 80ed3fd..bcb3349 100644 --- a/CapMachine.Wpf/Services/PPCThermodynamicSixResultsCalculator.cs +++ b/CapMachine.Wpf/Services/PPCThermodynamicSixResultsCalculator.cs @@ -151,12 +151,19 @@ namespace CapMachine.Wpf.Services /// private readonly LocalCalculationSupport _support; + private double _h3TempOffset_C = -10.0; + /// /// 初始化六个热力结果值计算类。 /// - public PPCThermodynamicSixResultsCalculator() + public PPCThermodynamicSixResultsCalculator(object refpropLock) { - _support = new LocalCalculationSupport(); + _support = new LocalCalculationSupport(refpropLock); + } + + public void SetH3TempOffset_C(double offsetC) + { + _h3TempOffset_C = offsetC; } /// @@ -219,7 +226,18 @@ namespace CapMachine.Wpf.Services } // 第 5 步:由液路压力/温度求液路比焓 h3。 - if (!TryGetLiquidPointEnthalpy_ByTP_BarA_C(input.LiquidPressureBarA, input.LiquidTemperatureC, out var h3_kJkg, out var p3Err)) + 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; @@ -240,7 +258,7 @@ namespace CapMachine.Wpf.Services } // 第 8 步:计算等熵效率。 - if (!TryComputeIsentropicEfficiencyPct(h1_kJkg, h2_kJkg, h2s_kJkg, out var etaS_pct, out var etaSErr)) + if (!TryComputeIsentropicEfficiencyPct(mRef_kg_s, h1_kJkg, h2s_kJkg, w_kW, out var etaS_pct, out var etaSErr)) { error = etaSErr; return false; @@ -259,7 +277,7 @@ namespace CapMachine.Wpf.Services // 同时把失败原因放到 error 中,供调用方决定是否记录为警告。 if (!TryComputeVolumetricEfficiencyPct(mRef_kg_s, v1_m3kg, input.CompressorSpeedRpm, input.CompressorDisplacementCc, out var etaV_pct, out var etaVErr)) { - error = etaVErr; + error = string.IsNullOrWhiteSpace(error) ? etaVErr : $"{error}; {etaVErr}"; return true; } @@ -303,9 +321,8 @@ namespace CapMachine.Wpf.Services /// /// 当前流程保持与图片/旧代码一致: /// 1. 读取总流量 kg/h - /// 2. 读取油流量 kg/h - /// 3. 冷媒流量 = 总流量 - 油流量 - /// 4. 再由 kg/h 换算为 kg/s + /// 2. 冷媒流量 = 总流量 + /// 3. 再由 kg/h 换算为 kg/s /// private bool TryGetRefrigerantMassFlow_kg_s(double totalMassFlowKgPerHour, double oilMassFlowKgPerHour, out double mRef_kg_s, out string error) { @@ -319,17 +336,10 @@ namespace CapMachine.Wpf.Services return false; } - // 再读取油流量。 - if (!TryGetOilMassFlow_kg_h(oilMassFlowKgPerHour, out var mOil_kg_h, out var oilErr)) + double mRef_kg_h = mTotal_kg_h; + if (mRef_kg_h <= 0) { - error = oilErr; - return false; - } - - // 计算真正参与循环的冷媒质量流量:mRef = mTotal - mOil。 - if (!TryComputeRefrigerantMassFlow_kg_h(mTotal_kg_h, mOil_kg_h, out var mRef_kg_h, out var refErr)) - { - error = refErr; + error = $"冷媒质量流量<=0,总流量={mTotal_kg_h}kg/h"; return false; } @@ -814,33 +824,45 @@ namespace CapMachine.Wpf.Services /// /// 计算等熵效率,单位 %。 /// + /// 冷媒质量流量,单位 kg/s。 /// 吸气比焓 h1,单位 kJ/kg。 - /// 实际排气比焓 h2,单位 kJ/kg。 /// 等熵出口比焓 h2s,单位 kJ/kg。 + /// 压缩机功率,单位 kW。 /// 等熵效率输出,单位 %。 /// 失败原因。 /// 是否计算成功。 - private bool TryComputeIsentropicEfficiencyPct(double h1_kJkg, double h2_kJkg, double h2s_kJkg, out double etaS_pct, out string error) + 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; - // 实际压缩焓升:(h2 - h1)。 - if (!TryComputeEnthalpyDifference_kJkg(h2_kJkg, h1_kJkg, "实际压缩焓升(h2-h1)", out var actualRise_kJkg, out var actualErr)) + if (double.IsNaN(mRef_kg_s) || double.IsInfinity(mRef_kg_s) || mRef_kg_s <= 0) { - error = actualErr; + error = "无效冷媒质量流量"; + return false; + } + + if (double.IsNaN(w_kW) || double.IsInfinity(w_kW) || w_kW <= 0) + { + error = "无效压缩机功率"; return false; } - // 等熵压缩焓升:(h2s - h1)。 if (!TryComputeEnthalpyDifference_kJkg(h2s_kJkg, h1_kJkg, "等熵压缩焓升(h2s-h1)", out var isentropicRise_kJkg, out var isoErr)) { error = isoErr; return false; } - // 等熵效率 = 等熵焓升 / 实际焓升 * 100%。 - return TryComputeEfficiencyPct(isentropicRise_kJkg, actualRise_kJkg, "等熵效率", out etaS_pct, out error); + 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; } /// @@ -1055,6 +1077,7 @@ namespace CapMachine.Wpf.Services return true; } + /// /// 六个热力结果计算类私有的底层物性支持实现。 /// @@ -1064,9 +1087,14 @@ namespace CapMachine.Wpf.Services /// private sealed class LocalCalculationSupport : IPPCCalculationSupport { - private static readonly object _refpropLock = new object(); + 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; @@ -1139,7 +1167,47 @@ namespace CapMachine.Wpf.Services } } - public bool TrySATP_SaturationByP_MPa(double pressureMPa, out double tSatK, out double Dl_molL, out double Dv_molL, out string error) => throw new NotSupportedException(); + 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) {