干度的恢复,干度的计算恢复到4f452b5这个干度计算方法上

封装了干度的执行方法
This commit is contained in:
2026-04-15 23:26:58 +08:00
parent a47ed5b817
commit e3641ebe84
2 changed files with 769 additions and 246 deletions

View File

@@ -10,6 +10,7 @@ using Prism.Events;
using Prism.Mvvm;
using Prism.Services.Dialogs;
using SixLabors.ImageSharp.ColorSpaces;
using System.Diagnostics;
using System.Globalization;
using System.Collections.Generic;
using System.Text;
@@ -35,6 +36,7 @@ namespace CapMachine.Wpf.Services
public MachineRtDataService MachineRtDataService { get; }
public IDialogService DialogService { get; }
private readonly IDrynessCalculator _drynessCalculator;
private readonly EnthalpyDrynessCalculator _enthalpyDrynessCalculator;
/// <summary>
/// 标签中心
@@ -58,6 +60,7 @@ namespace CapMachine.Wpf.Services
MachineRtDataService = machineRtDataService;
DialogService = dialogService;
_drynessCalculator = drynessCalculator;
_enthalpyDrynessCalculator = new EnthalpyDrynessCalculator(_refpropLock);
TagManager = MachineRtDataService.TagManger;
if (TagManager.TryGetShortTagByName("转速[rpm]", out ShortValueTag? speedShortTag))
@@ -95,8 +98,7 @@ namespace CapMachine.Wpf.Services
//TxvFrTempTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前温度[℃]")!;
//TxvFrPressTag = TagManager.DicTags.GetValueOrDefault("膨胀阀前压力[BarA]")!;
if (TagManager.TryGetShortTagByName("膨胀阀前温度[℃]", out ShortValueTag? TxvFrTempShortTag) ||
TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out TxvFrTempShortTag))
if (TagManager.TryGetShortTagByName("SUBCOOL出口温度[℃]", out ShortValueTag? TxvFrTempShortTag))
{
TxvFrTempTag = TxvFrTempShortTag!;
}
@@ -326,6 +328,70 @@ namespace CapMachine.Wpf.Services
/// </summary>
private bool DebugLog { get; set; } = false;
private DateTime _lastDrynessSnapshotUtc = DateTime.MinValue;
private static readonly TimeSpan _drynessSnapshotInterval = TimeSpan.FromSeconds(5);
/// <summary>
/// 干度计算过程值输出限频用于现场快速定位干度为何为0/为何被限幅。
/// </summary>
private void LogDrynessSnapshotIfNeeded(
string stage,
string error,
double dryness01,
double hMix_kJkg,
double gasInput_kJkg,
double liquidInput_kJkg,
double hSatL_kJkg,
double hSatV_kJkg,
double mGas_kg_h,
double mLube_kg_h,
double mLiq_kg_h)
{
var now = DateTime.UtcNow;
if (now - _lastDrynessSnapshotUtc < _drynessSnapshotInterval)
{
return;
}
_lastDrynessSnapshotUtc = now;
double denom = hSatV_kJkg - hSatL_kJkg;
double xRaw = double.NaN;
if (!double.IsNaN(hMix_kJkg) && !double.IsNaN(hSatL_kJkg) && !double.IsNaN(denom) && Math.Abs(denom) > 1e-9)
{
xRaw = (hMix_kJkg - hSatL_kJkg) / denom;
}
double vrv = VRVTag?.PVModel?.EngValue ?? double.NaN;
double liqRef = LiqRefFlowTag?.PVModel?.EngValue ?? double.NaN;
double lube = LubeFlowTag?.PVModel?.EngValue ?? double.NaN;
double gasFlow = vrv - liqRef;
double mg = Math.Max(0.0, mGas_kg_h) + Math.Max(0.0, mLube_kg_h);
double ml = Math.Max(0.0, mLiq_kg_h);
double mSum = mg + ml;
var msg =
$"[DrynessSnap] Stage={stage}; Err={error}; x={dryness01:F6} ({dryness01 * 100.0:F3}%); xRaw={xRaw}; " +
$"hMix={hMix_kJkg}; hSatL={hSatL_kJkg}; hSatV={hSatV_kJkg}; denom={denom}; " +
$"GasInput={gasInput_kJkg}; LiquidInput={liquidInput_kJkg}; " +
$"mGas={mGas_kg_h}kg/h, mLube={mLube_kg_h}kg/h, mLiq={mLiq_kg_h}kg/h => mg={mg}kg/h, ml={ml}kg/h, mSum={mSum}kg/h; " +
$"VRV={vrv}kg/h(Addr={VRVTag?.PVModel?.Address}), LiqRef={liqRef}kg/h(Addr={LiqRefFlowTag?.PVModel?.Address}), Lube={lube}kg/h(Addr={LubeFlowTag?.PVModel?.Address}), GasFlow(VRV-LiqRef)={gasFlow}kg/h; " +
$"InhP={InhPressTag?.PVModel?.EngValue}BarA(Addr={InhPressTag?.PVModel?.Address}), InhT={InhTempTag?.PVModel?.EngValue}C(Addr={InhTempTag?.PVModel?.Address}), " +
$"TxvP={TxvFrPressTag?.PVModel?.EngValue}BarA(Addr={TxvFrPressTag?.PVModel?.Address}), TxvT={TxvFrTempTag?.PVModel?.EngValue}C(Addr={TxvFrTempTag?.PVModel?.Address}), " +
$"GasPreValveP={GasPreValvePressTag?.PVModel?.EngValue}BarA(Addr={GasPreValvePressTag?.PVModel?.Address}), GasPreValveT={GasPreValveTempTag?.PVModel?.EngValue}C(Addr={GasPreValveTempTag?.PVModel?.Address})";
Debug.WriteLine(msg);
try
{
Console.WriteLine(msg);
}
catch
{
// ignored
}
}
/// <summary>
/// PLC扫描线程
/// </summary>
@@ -393,99 +459,87 @@ namespace CapMachine.Wpf.Services
//干度技术
//气体流量kg/h=冷媒流量kg/h-液冷媒流量kg/h
var GasFlowKgPerH = VRVTag.PVModel.EngValue - LiqRefFlowTag.PVModel.EngValue;//气体流量kg/h
var drynessResult = _enthalpyDrynessCalculator.Calculate(
new EnthalpyDrynessCalculator.Input(
GasPreValvePressTag.PVModel.EngValue,
GasPreValveTempTag.PVModel.EngValue,
TxvFrPressTag.PVModel.EngValue,
TxvFrTempTag.PVModel.EngValue,
InhPressTag.PVModel.EngValue,
VRVTag.PVModel.EngValue,
LiqRefFlowTag.PVModel.EngValue,
LubeFlowTag.PVModel.EngValue));
//摩尔质量kg/mol
var molarMassKgPerMol = GetMolarMass();
//定义气相质量焓 kJ/(kg·K)
var Gas_kJkgK = 0.0;
//步骤1:计算气路阀前气相焓h vap(单相气相)
if (TryTPRHO_VaporDensity_ByTP_MPa_C(GasPreValvePressTag.PVModel.EngValue * 0.1, GasPreValveTempTag.PVModel.EngValue, out var D_molL, out var D_molLErr1))
if (drynessResult.IsDryness1Success)
{
if (TryTHERM_VaporEntropy_ByTD(GasPreValveTempTag.PVModel.EngValue, D_molL, out var s_kJkgK, out var D_molLErr2))
{
// s_kJkgK 即为图片中的“气相质量熵 kJ/(kg·K)”
Gas_kJkgK = s_kJkgK;
}
}
DrynessTag2Value = drynessResult.Dryness1_01 * 100.0;
//定义液相质量焓 kJ/(kg·K)
var Liquid_kJkg = 0.0;
//步骤2:计算液路阀前气相焓hlig(单相液相) TxvFrTempTag 液体阀前温度 TxvFrPressTag 液体阀前压力
// 1) 先求 D_liq
if (TryTPRHO_LiquidDensity_ByTP_MPa_C(TxvFrPressTag.PVModel.EngValue * 0.1, TxvFrTempTag.PVModel.EngValue, out var D_liq_molL, out var D_liqErr1))
{
// 2) 再用 THERM 求 h_liq
if (TryTHERM_LiquidEnthalpy_ByTD(TxvFrTempTag.PVModel.EngValue, D_liq_molL, out var h_liq_kJkg, out var D_liqErr2))
{
// h_liq_kJkg 即为图片中的“液相质量焓 h_liq kJ/kg
Liquid_kJkg = h_liq_kJkg;
}
else
{
// 处理 err2
}
//if (drynessResult.Dryness1_01 <= 0)
//{
// LogDrynessSnapshotIfNeeded(
// "TryComputeDrynessByEnthalpy(SuccessButZero)",
// string.Empty,
// drynessResult.Dryness1_01,
// drynessResult.HMix1_kJkg,
// drynessResult.GasEnthalpy_kJkg,
// drynessResult.LiquidEnthalpy_kJkg,
// drynessResult.SatLiquidEnthalpy_kJkg,
// drynessResult.SatVaporEnthalpy_kJkg,
// drynessResult.GasFlowKgPerH,
// 0,
// LiqRefFlowTag.PVModel.EngValue);
//}
}
else
{
// 处理 err1
//LogDrynessSnapshotIfNeeded(
// "TryComputeDrynessByEnthalpy(Fail)",
// drynessResult.Error1,
// double.NaN,
// double.NaN,
// drynessResult.GasEnthalpy_kJkg,
// drynessResult.LiquidEnthalpy_kJkg,
// drynessResult.SatLiquidEnthalpy_kJkg,
// drynessResult.SatVaporEnthalpy_kJkg,
// drynessResult.GasFlowKgPerH,
// 0,
// LiqRefFlowTag.PVModel.EngValue);
}
//定义饱和液质量焓hl kJ/kg
var Liquid_h_liq = 0.0;
//定义饱和气质量焓hl k)/kg
var Gas_h_vap = 0.0;
if (TryGetSaturationLiquidEnthalpy_ByP_MPa(InhPressTag.PVModel.EngValue * 0.1, out var h_liq, out var h_liqErr1) &&
TryGetSaturationVaporEnthalpy_ByP_MPa(InhPressTag.PVModel.EngValue * 0.1, out var h_vap, out var h_vapErr2))
if (drynessResult.IsDryness2Success)
{
// h_liq / h_vap 即为图片右侧的两个“饱和液/饱和气 质量焓 kJ/kg”
Liquid_h_liq = h_liq;
Gas_h_vap = h_vap;
}
// 气相焓 h_vap_kJkg由 TPRHO 气相 + THERM
// 液相焓 h_liq_kJkg由 TPRHO 液相 + THERM
// 饱和液 / 气焓 h_l / h_v由 SATP +THERM
//气/液质量流量 mg/ml
if (TryComputeDrynessByEnthalpy(
Gas_kJkgK, Liquid_kJkg,//气相质量焓 h vap [k/kg] 液相质量焓 h liq [kJ/kg]
GasFlowKgPerH, LiqRefFlowTag.PVModel.EngValue,//气体质量流量 mg [kg/h] 液体质量流量 ml [kg/h]
Liquid_h_liq, Gas_h_vap, //饱和液质量焓 h liq [kJ/kg] 饱和气质量焓 h vap [kJ/kg]
out var GasValue, out var hMix, out var err))
{
DrynessTag2Value = GasValue * 100.0;
// x 为最终干度 [0..1]hMix 为混合后比焓
DrynessTag.PVModel.EngValue = drynessResult.Dryness2_01 * 100.0;
//if (drynessResult.Dryness2_01 <= 0)
//{
// LogDrynessSnapshotIfNeeded(
// "TryComputeDrynessByEnthalpy2(SuccessButZero)",
// string.Empty,
// drynessResult.Dryness2_01,
// drynessResult.HMix2_kJkg,
// drynessResult.GasEnthalpy_kJkg,
// drynessResult.LiquidEnthalpy_kJkg,
// drynessResult.SatLiquidEnthalpy_kJkg,
// drynessResult.SatVaporEnthalpy_kJkg,
// drynessResult.GasFlowKgPerH,
// LubeFlowTag.PVModel.EngValue,
// LiqRefFlowTag.PVModel.EngValue);
//}
}
else
{
// 处理 err
}
//计算干度2
// 气相焓 h_vap_kJkg由 TPRHO 气相 + THERM
// 液相焓 h_liq_kJkg由 TPRHO 液相 + THERM
// 饱和液 / 气焓 h_l / h_v由 SATP +THERM
//气/液质量流量 mg/ml
//***************** 经过验证,当前方法是正确 *****************
if (TryComputeDrynessByEnthalpy2(
Gas_kJkgK, Liquid_kJkg,//气相质量焓 h vap [k/kg] 液相质量焓 h liq [kJ/kg]
GasFlowKgPerH, LubeFlowTag.PVModel.EngValue, //气体质量流量 mg [kg/h] 润滑油流量
LiqRefFlowTag.PVModel.EngValue,// 液体质量流量 ml [kg/h]
Liquid_h_liq, Gas_h_vap, //饱和液质量焓 h liq [kJ/kg] 饱和气质量焓 h vap [kJ/kg]
out var GasValue2, out var hMix2, out var err2))
{
// x 为最终干度 [0..1]hMix 为混合后比焓
DrynessTag.PVModel.EngValue = GasValue2 * 100.0;
}
else
{
// 处理 err2
//LogDrynessSnapshotIfNeeded(
// "TryComputeDrynessByEnthalpy2(Fail)",
// drynessResult.Error2,
// double.NaN,
// double.NaN,
// drynessResult.GasEnthalpy_kJkg,
// drynessResult.LiquidEnthalpy_kJkg,
// drynessResult.SatLiquidEnthalpy_kJkg,
// drynessResult.SatVaporEnthalpy_kJkg,
// drynessResult.GasFlowKgPerH,
// LubeFlowTag.PVModel.EngValue,
// LiqRefFlowTag.PVModel.EngValue);
}
if (TryUpdateThermodynamicSixResults(out var thermoErr))
@@ -714,6 +768,40 @@ namespace CapMachine.Wpf.Services
}
public 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; // K
double D = densityMolPerL; // mol/L
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_vap_kJ_per_kg = (hJmol / molarMassKgPerMol) * 0.001;
return true;
}
/// <summary>
///
/// 计算液路阀前液相密度D_liq(单相液相) 液相密度D_liq mol/L
@@ -952,170 +1040,6 @@ namespace CapMachine.Wpf.Services
return TryTHERM_Enthalpy_kJkg_ByT_K_D(tSatK, Dv, out h_vap_kJkg, out error);
}
/// <summary>
/// 按图片的最终流程计算干度:
/// 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_mixfalse 返回 error
/// </summary>
/// <param name="hVap_kJkg">气相质量焓 h_vap [kJ/kg]</param>
/// <param name="hLiq_kJkg">液相质量焓 h_liq [kJ/kg]</param>
/// <param name="mGas_kg_h">气体质量流量 mg [kg/h]</param>
/// <param name="mLiq_kg_h">液体质量流量 ml [kg/h]</param>
/// <param name="hSatL_kJkg">饱和液质量焓 h_l [kJ/kg]</param>
/// <param name="hSatV_kJkg">饱和气质量焓 h_v [kJ/kg]</param>
/// <param name="dryness">输出干度 x ∈ [0,1]</param>
/// <param name="hMix_kJkg">输出混合后总比焓 h_mix [kJ/kg]</param>
/// <param name="error">Err</param>
/// <returns></returns>
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)
{
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))
{
error = "输入焓值存在 NaN";
return false;
}
if (double.IsNaN(mGas_kg_h) || double.IsNaN(mLiq_kg_h))
{
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无法进行加权混合焓计算";
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)
{
error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)";
return false;
}
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;
}
/// <summary>
/// 按图片的最终流程计算干度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_mixfalse 返回 error
/// </summary>
/// <param name="hVap_kJkg">气相质量焓 h_vap [kJ/kg]</param>
/// <param name="hLiq_kJkg">液相质量焓 h_liq [kJ/kg]</param>
/// <param name="mGas_kg_h">气体质量流量 mg [kg/h]</param>
/// <param name="mLiq_kg_h">液体质量流量 ml [kg/h]</param>
/// <param name="hSatL_kJkg">饱和液质量焓 h_l [kJ/kg]</param>
/// <param name="hSatV_kJkg">饱和气质量焓 h_v [kJ/kg]</param>
/// <param name="dryness">输出干度 x ∈ [0,1]</param>
/// <param name="hMix_kJkg">输出混合后总比焓 h_mix [kJ/kg]</param>
/// <param name="error">Err</param>
/// <returns></returns>
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))
{
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;
}
// 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)
{
error = "饱和气/液焓差过小,无法计算干度(可能接近临界点或输入异常)";
return false;
}
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;
}