using System;
namespace CapMachine.Wpf.PPCalculation
{
///
/// 干度计算(基于焓法)的独立封装:
/// - 先分别计算气路/液路阀前的单相质量比焓(TPRHO + THERM)
/// - 再计算吸气压力对应的饱和液/饱和气质量比焓(SATP + THERM)
/// - 最终按既定流程计算混合焓与干度,并限幅到 [0,1]
///
/// 注意:该类仅做封装以便维护,不应改变既有干度计算过程与逻辑。
/// 调用方需确保 REFPROP 已完成 SETPATH/SETUP 初始化(与现有 PPCService 保持一致)。
///
public sealed class EnthalpyDrynessCalculator
{
private readonly object _refpropLock;
///
/// 构造函数。
///
/// 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)
{
// 气体流量 kg/h = 冷媒流量 kg/h - 液冷媒流量 kg/h
double gasFlowKgPerH = input.VRVFlowKgPerH - input.LiqRefFlowKgPerH;
// 定义气相质量焓 kJ/kg(注意:保持既有逻辑,默认 0,仅在成功计算时赋值)
double gas_hVap_kJkg = 0.0;
// 步骤1: 计算气路阀前气相焓 h_vap (单相气相)
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;
}
}
// 定义液相质量焓 kJ/kg(保持既有逻辑,默认 0,仅在成功计算时赋值)
double liquid_hLiq_kJkg = 0.0;
// 步骤2: 计算液路阀前液相焓 h_liq (单相液相)
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;
}
}
// 定义饱和液/饱和气质量焓 kJ/kg(保持既有逻辑,默认 0,仅在同时成功时赋值)
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;
}
// 干度1:mg=气体流量;ml=液体流量
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);
// 干度2:mg=气体流量+润滑油流量;ml=液体流量(保持既有策略)
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,
gas_hVap_kJkg,
liquid_hLiq_kJkg,
hSatL_kJkg,
hSatV_kJkg,
ok1,
dryness1,
hMix1,
err1,
ok2,
dryness2,
hMix2,
err2);
}
///
/// 获取指定组分的摩尔质量(kg/mol)。
///
/// 组分ID(icomp)。
/// 摩尔质量(kg/mol)。
/// 错误信息。
/// 是否成功。
private bool TryGetMolarMassKgPerMol(long componentId, out double molarMassKgPerMol, out string error)
{
molarMassKgPerMol = double.NaN;
error = string.Empty;
try
{
double wmm = 0, Trp = 0, Tnbpt = 0, Tc = 0, Pc = 0, Dc = 0, Zc = 0, acf = 0, dip = 0, Rgas = 0;
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;
if (double.IsNaN(molarMassKgPerMol) || double.IsInfinity(molarMassKgPerMol) || molarMassKgPerMol <= 0)
{
error = "无效的摩尔质量";
return false;
}
return true;
}
catch (Exception ex)
{
error = $"获取组分{componentId}的摩尔质量时出错: {ex.Message}";
return false;
}
}
///
/// TPRHOdll 封装:按 T(℃)、P(MPa) 计算“气相”摩尔密度 D [mol/L](kph=2)。
///
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;
}
///
/// THERMdll 封装:按 T(℃) 与 D[mol/L] 计算气相质量焓 h_vap [kJ/kg]。
///
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;
}
///
/// TPRHOdll 封装:按 T(℃)、P(MPa) 计算“液相”摩尔密度 D [mol/L](kph=1)。
///
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;
}
///
/// THERMdll 封装:按 T(℃) 与 D[mol/L] 计算液相质量焓 h_liq [kJ/kg]。
///
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;
}
///
/// SATPdll:由压力 P(MPa) 求饱和温度 Tsat[K]、饱和液/气摩尔密度 Dl/Dv [mol/L]。
///
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;
}
///
/// THERMdll:由 T[K] 与 D[mol/L] 计算质量比焓 h[kJ/kg]。
///
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;
}
///
/// 便捷:由压力 P(MPa) 直接得到“饱和液”质量比焓 h_liq[kJ/kg]。
///
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);
}
///
/// 便捷:由压力 P(MPa) 直接得到“饱和气”质量比焓 h_vap[kJ/kg]。
///
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);
}
///
/// 按图片的最终流程计算干度:
/// 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
///
private 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;
}
///
/// 按图片的最终流程计算干度2:
/// 计算逻辑同干度1,但气体流量 mg = 气体流量 + 润滑油流量。
///
private 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;
}
}
}