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; /// /// 初始化六个热力结果值计算类。 /// public PPCThermodynamicSixResultsCalculator() { _support = new LocalCalculationSupport(); } /// /// 按既定流程一次性计算六个热力结果值。 /// /// 输入数据,包含功率、流量、吸排气状态点、液路状态点、转速与排量。 /// 输出结果对象。 /// /// 错误或警告信息。 /// 当前逻辑中,若“容积效率”计算失败但其他 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。 if (!TryGetLiquidPointEnthalpy_ByTP_BarA_C(input.LiquidPressureBarA, input.LiquidTemperatureC, 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(h1_kJkg, h2_kJkg, h2s_kJkg, 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 = 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. 读取油流量 kg/h /// 3. 冷媒流量 = 总流量 - 油流量 /// 4. 再由 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; } // 再读取油流量。 if (!TryGetOilMassFlow_kg_h(oilMassFlowKgPerHour, out var mOil_kg_h, out var oilErr)) { 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; 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。 /// 失败原因。 /// 是否计算成功。 /// /// 当前流程: /// 1. BarA -> MPa /// 2. 用气相 TPRHO 求摩尔密度 D /// 3. 用 THERM 求焓 /// private bool TryGetVaporPointEnthalpy_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_VaporDensity_ByTP_MPa_C(pMPa, temperatureC, out var d_molL, out var dErr)) { error = dErr; return false; } // THERM helper 的温度输入为 K,因此把 ℃ 转成 K 后再求焓。 double tK = temperatureC + 273.15; if (!_support.TryTHERM_Enthalpy_kJkg_ByT_K_D(tK, d_molL, 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。 /// 失败原因。 /// 是否计算成功。 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; // 第一步:BarA -> MPa。 double pMPa = pressureBarA * 0.1; // 第二步:由气相 TPRHO 求摩尔密度 D。 if (!_support.TryTPRHO_VaporDensity_ByTP_MPa_C(pMPa, temperatureC, out var d_molL, out var dErr)) { error = dErr; return false; } // 第三步:用 THERM 求吸气点比焓 h1。 double tK = temperatureC + 273.15; if (!_support.TryTHERM_Enthalpy_kJkg_ByT_K_D(tK, d_molL, out h_kJkg, out var hErr)) { error = hErr; return false; } // 第四步:用 THERM 求吸气点比熵 s1。 if (!_support.TryTHERM_VaporEntropy_ByTD(temperatureC, d_molL, out s_kJkgK, out var sErr)) { error = sErr; return false; } // 第五步:由摩尔密度换算为质量比容 v1。 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); } /// /// 计算等熵效率,单位 %。 /// /// 吸气比焓 h1,单位 kJ/kg。 /// 实际排气比焓 h2,单位 kJ/kg。 /// 等熵出口比焓 h2s,单位 kJ/kg。 /// 等熵效率输出,单位 %。 /// 失败原因。 /// 是否计算成功。 private bool TryComputeIsentropicEfficiencyPct(double h1_kJkg, double h2_kJkg, double h2s_kJkg, 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)) { error = actualErr; 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); } /// /// 计算容积效率,单位 %。 /// /// 冷媒质量流量,单位 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 static readonly object _refpropLock = new object(); private static volatile bool _rpInitialized; 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) => throw new NotSupportedException(); 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 = 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); } 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 = 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; } 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 = 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_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 = 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; } 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 double GetMolarMass() { 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); return wmm * 0.001; } private static 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 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 = GetMolarMass(); if (molarMassKgPerMol <= 0) { error = "无效的摩尔质量"; return false; } s_JmolK = s_kJkgK * 1000.0 * molarMassKgPerMol; return true; } } } }