更新干度计算

This commit is contained in:
2025-11-02 01:47:18 +08:00
parent 46d086e74a
commit 585ad7c1aa
10 changed files with 829 additions and 966 deletions

View File

@@ -0,0 +1,213 @@
using System;
using CapMachine.Core;
namespace CapMachine.Wpf.PPCalculation
{
/// <summary>
/// 基于 REFPROP 的干度计算实现。
/// 注意REFPROP 为全局状态的非托管库,调用需串行化;可通过 externalRefpropLock 传入外部全局锁。
/// </summary>
public class RefpropDrynessCalculator : IDrynessCalculator
{
private readonly object _internalLock = new object();
private bool _rpInitialized = false;
private string _rpFluidFile = string.Empty;
private bool EnsureRefpropInitialized(out string error)
{
error = string.Empty;
// 从配置中心读取(与 PPCService 保持一致的键名)
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 -> R134A.FLD其它 -> R1234YF.FLD与现有实现一致
string hfldCore = configuredCryogen.Equals("R134a", StringComparison.OrdinalIgnoreCase)
? "R134A.FLD"
: "R1234YF.FLD";
if (_rpInitialized && string.Equals(_rpFluidFile, hfldCore, StringComparison.OrdinalIgnoreCase))
{
return true;
}
try
{
lock (_internalLock)
{
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;
}
}
public DrynessCalcResult CalcDrynessByTP_MPaC(double pressureMPa, double temperatureC, object externalRefpropLock = null)
{
var result = new DrynessCalcResult
{
QMolar = double.NaN,
QMass = double.NaN,
State = DrynessState.Unknown,
H = double.NaN,
Hl = double.NaN,
Hv = double.NaN,
Error = string.Empty
};
if (!EnsureRefpropInitialized(out string initErr))
{
result.Error = initErr;
return result;
}
// 纯工质
double[] x = new double[20];
x[0] = 1.0;
double tK = temperatureC + 273.15; // K
double pKPa = pressureMPa * 1000.0; // MPa -> kPa
double d = 0, Dl = 0, Dv = 0, q = 0, ee = 0, h = 0, ss = 0, cp = 0, cv = 0, w = 0;
double[] xliq = new double[20];
double[] xvap = new double[20];
string herr = new string(' ', 255);
long ierr = 0, ln = 255;
double tSat = 0, DlSat = 0, DvSat = 0;
string herrSat = new string(' ', 255);
long ierrSat = 0, kph = 1, herrLen = 255;
double hl = double.NaN, hv = double.NaN, el = 0, sl = 0, cvl = 0, cpl = 0, wl = 0, hjt = 0;
// 选择锁对象
object locker = externalRefpropLock ?? _internalLock;
lock (locker)
{
try
{
// T-P 闪蒸
IRefProp64.TPFLSHdll(ref tK, 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 ln);
result.H = h;
if (ierr != 0)
{
result.Error = $"TPFLSH 错误: {herr.Trim()} (ierr={ierr})";
}
// 相态判定
if (q >= 0 && q <= 1)
{
result.State = DrynessState.TwoPhase;
result.QMolar = q;
// QMASS 转换为质量基
double qkg = double.NaN, wliq = 0, wvap = 0;
double[] xlkg = new double[20];
double[] xvkg = new double[20];
long ierrQ = 0; string herrQ = new string(' ', 255); long lnQ = 255;
IRefProp64.QMASSdll(ref q, xliq, xvap, ref qkg, xlkg, xvkg, ref wliq, ref wvap, ref ierrQ, ref herrQ, ref lnQ);
if (ierrQ == 0) result.QMass = qkg; else result.Error = $"QMASS 错误: {herrQ.Trim()} (ierr={ierrQ})";
}
else if (q == 999)
{
result.State = DrynessState.Supercritical;
result.Error = "超临界状态,干度无定义";
}
else if (q > 1)
{
result.State = DrynessState.Superheated;
}
else
{
result.State = DrynessState.Subcooled;
}
// 求同压饱和焓 Hl/Hv用于诊断、或在外部回退逻辑使用
double pForSat = pKPa;
IRefProp64.SATPdll(ref pForSat, x, ref kph, ref tSat, ref DlSat, ref DvSat, xliq, xvap, ref ierrSat, ref herrSat, ref herrLen);
if (ierrSat == 0)
{
// 饱和液焓
double tSatLocal = tSat; double DlLocal = DlSat; double pOut = 0;
IRefProp64.THERMdll(ref tSatLocal, ref DlLocal, xliq, ref pOut, ref el, ref hl, ref sl, ref cvl, ref cpl, ref wl, ref hjt);
// 饱和气焓
tSatLocal = tSat; double DvLocal = DvSat; pOut = 0; double ev = 0, hvLocal = 0, sv = 0, cvv = 0, cpv = 0, wv = 0, hjtv = 0;
IRefProp64.THERMdll(ref tSatLocal, ref DvLocal, xvap, ref pOut, ref ev, ref hvLocal, ref sv, ref cvv, ref cpv, ref wv, ref hjtv);
result.Hl = hl;
result.Hv = hvLocal;
// 保持与现行策略一致:非两相区不回填干度,仅记录诊断
if (double.IsNaN(result.QMolar) || result.State != DrynessState.TwoPhase)
{
double denom = (hvLocal - hl);
if (Math.Abs(denom) > 1e-6)
{
double qhRaw = (h - hl) / denom;
if (string.IsNullOrWhiteSpace(result.Error))
{
result.Error = $"非两相态({result.State})TP回退不提供干度。诊断qhRaw={qhRaw:F4}";
}
}
else
{
if (string.IsNullOrWhiteSpace(result.Error)) result.Error = "临界点附近或数据异常导致饱和焓差接近 0无法通过焓差估算干度";
}
}
}
else
{
if (string.IsNullOrWhiteSpace(result.Error)) result.Error = $"SATP 失败: {herrSat.Trim()} (ierr={ierrSat})";
}
}
catch (Exception ex)
{
result.Error = $"干度计算异常: {ex.Message}";
}
}
return result;
}
public DrynessCalcResult CalcDrynessByTP_BarA_C(double pressureBarA, double temperatureC, object externalRefpropLock = null)
{
return CalcDrynessByTP_MPaC(pressureBarA / 10.0, temperatureC, externalRefpropLock);
}
}
}