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