- 科学计算
【免费下载链接】CoolProp
Thermophysical properties for the masses
CoolProp 的高级接口(PropsSI等)以字符串解析参数,便捷但开销较大;低级接口则完全基于枚举值与浮点数、不使用任何字符串,是高级接口内部真正调用的执行路径。本文基于仓库中的 LowLevelAPI.rst 官方文档展开,逐节覆盖低级接口的核心用法——AbstractState实例的创建与复用、输入对(input pairs)的生成、keyed_output批量取数、相区强制指定(specify_phase)、多饱和根消歧(update_with_guesses)、高效偏导数(first_partial_deriv/first_two_phase_deriv/ 饱和线导数)、参考态切换以及面向无 C++ 类绑定语言(Excel、FORTRAN、Julia 等)的 C 函数层——并结合 AbstractState 头文件、CoolPropLib.h 等源码给出实现依据,读完即可在性能敏感的循环与闪蒸场景中以正确的姿势使用低级接口。
1. 低级接口的设计定位与实例化原则
低级接口的首要价值是速度:它只接受整型枚举(CoolProp::parameters、CoolProp::input_pairs)和双精度浮点数,避开了字符串比较与解析的开销。CoolProp 的高级接口本质上就是在内部转调这套低级接口,因此凡是热路径上的代码,直接调用低级接口都能获得更稳定、更低延迟的表现。
在 C++ 层面,整个体系建立在一个名为AbstractState的抽象基类之上,它定义了一组各物性后端(property backends)必须实现的协议。从 AbstractState.h 的源码结构可以看到,update()被声明为纯虚函数(L967),而calc_specify_phase、calc_first_saturation_deriv、calc_first_two_phase_deriv等计算钩子则提供了“未实现即抛NotImplementedError”的默认行为(L695-L758)。这种协议式设计使得接入一个全新的物性库非常容易——只要其接口能映射到AbstractState的结构即可,官方正是用同样方式接入了 REFPROP 后端。
文档明确给出的使用原则:
- 每个流体(或混合物)只构造一个后端实例并反复调用其方法。构造函数存在可观的计算开销,构造一次后应在整个程序生命周期内传递该实例;
- 对表格类后端(TTSE、Bicubic,见 Tabular.rst)和 SVD 压缩表格后端(SVDSBTL,见 SVDSBTL.rst),这一原则从“建议”升级为硬性要求:每次构造需支付约80–140 ms的表加载成本。SVDSBTL 提供进程级内存缓存,使同一进程中同流体/同选项的第二次构造便宜得多,但那是缓解措施而非替代品;TTSE/Bicubic 没有此缓存,新进程永远要付完整加载成本;
- 在 C++ 中,
AbstractState::factory返回的是裸指针,文档强烈建议用std::shared_ptr包裹,让析构自动发生。
2. 第一个示例:C++ 中计算水的饱和态
文档中的入门示例(源码见 AbstractState1.cxx)展示了低级接口最典型的应用——给定压力与干度求饱和液相性质:
#include "CoolProp/CoolProp.h" #include "CoolProp/AbstractState.h" #include <iostream> #include <memory> using namespace CoolProp; int main() { std::shared_ptr<AbstractState> Water(AbstractState::factory("HEOS", "Water")); Water->update(PQ_INPUTS, 101325, 0); // SI units std::cout << "T: " << Water->T() << " K" << std::endl; std::cout << "rho': " << Water->rhomass() << " kg/m^3" << std::endl; std::cout << "rho': " << Water->rhomolar() << " mol/m^3" << std::endl; std::cout << "h': " << Water->hmass() << " J/kg" << std::endl; std::cout << "h': " << Water->hmolar() << " J/mol" << std::endl; std::cout << "s': " << Water->smass() << " J/kg/K" << std::endl; std::cout << "s': " << Water->smolar() << " J/mol/K" << std::endl; return EXIT_SUCCESS; }实际运行输出(见 AbstractState1.cxx.output):
T: 373.124 K rho': 958.367 kg/m^3 rho': 53197.5 mol/m^3 h': 419058 J/kg h': 7549.44 J/mol s': 1306.92 J/kg/K s': 23.5445 J/mol/K同样的计算若走高级接口,等价于一次PropsSI("T", "P", 101325, "Q", 1, "Water")调用。注意本例中“构造一次、多个访问器(T()、rhomass()、hmass()…)连取”的写法正是低开销的关键:状态只算一次,各属性从同一个已解析的状态中读取。
3. 输入对(Input Pairs)与generate_update_pair
AbstractState::update的第一个参数是CoolProp::input_pairs枚举值(如PT_INPUTS、PQ_INPUTS、DmolarT_INPUTS),完整清单定义在CoolProp::input_pairs(源码文档见CoolProp::input_pairs)。
如果只知道两个输入变量及其数值、不确定该用哪个输入对枚举,可以调用CoolProp::generate_update_pair自动生成。从 DataStructures.h 的源码看(L381-L439),该函数是一个模板:内部按match_pair(key1, key2, ...)逐一比对两个parameters枚举(顺序无关,swap参数处理交换),命中后返回对应枚举并把两个数值重排到与枚举定义一致的槽位(例如iHmolar在前则v1为摩尔焓),无效组合返回INPUT_PAIR_INVALID。这一点很重要:Python 版可以多返回值,C++ 版无法如此,因此 C++ 侧的值交换发生在输出参数out1/out2上,文档对此专门给出了 warning。
Python 中的示例:
In [0]: import CoolProp In [0]: CoolProp.CoolProp.generate_update_pair(CoolProp.iT, 300, CoolProp.iDmolar, 1e-6) In [0]: CoolProp.DmolarT_INPUTS即:把“温度 300 K + 摩尔密度 1e-6 mol/m³”归一为DmolarT_INPUTS(密度在前、温度在后)。
4.keyed_output与访问器函数:两种取数方式
简单输出函数(如AbstractState::rhomolar)与CoolProp::parameters中的键是一一对应的,因此取数有两条等价路径:
- 访问器函数:
HEOS.p()、HEOS.rhomass()——语义清晰、编译期友好; keyed_output:HEOS.keyed_output(CoolProp.iP)——可以对一组键做循环,避免把一堆访问器调用硬编码在代码里(AbstractState.h 中定义,另有trivial_keyed_output等变体)。
文档示例:
In [0]: import CoolProp In [0]: HEOS = CoolProp.AbstractState("HEOS", "Water") In [0]: HEOS.update(CoolProp.DmolarT_INPUTS, 1e-6, 300) In [0]: HEOS.p() In [0]: [HEOS.keyed_output(k) for k in [CoolProp.iP, CoolProp.iHmass, CoolProp.iHmolar]]当需要“遍历一组输出键”或键集合在运行期才确定时,keyed_output是唯一可扩展的写法。
5. 只有低级接口才有的能力
第一个例子用高级接口也能做,但那样要多次调用压力-干度闪蒸,而每次闪蒸都要做复杂的相平衡迭代,速度很慢。更重要的是,大量功能只存在于低级接口。文档给出的两类典型能力:
(1)强制相区加速闪蒸
In [0]: HEOS = CoolProp.AbstractState("HEOS", "Water") # Do a flash call that is a very low density state point, definitely vapor In [0]: %timeit HEOS.update(CoolProp.DmolarT_INPUTS, 1e-6, 300) # Specify the phase - for some inputs (especially density-temperature), this will result in a # more direct evaluation of the equation of state without checking the saturation boundary In [0]: HEOS.specify_phase(CoolProp.iphase_gas) # We try it again - a bit faster In [0]: %timeit HEOS.update(CoolProp.DmolarT_INPUTS, 1e-6, 300) # Reset the specification of phase In [0]: HEOS.specify_phase(CoolProp.iphase_not_imposed)(2)混合物露点及两相组成(高级接口无对应直接调用):
# A mixture of methane and ethane In [0]: HEOS = CoolProp.AbstractState("HEOS", "Methane&Ethane") # Set the mole fractions of the mixture In [0]: HEOS.set_mole_fractions([0.2,0.8]) # Do the dewpoint calculation In [0]: HEOS.update(CoolProp.PQ_INPUTS, 101325, 1) # Liquid phase molar density In [0]: HEOS.saturated_liquid_keyed_output(CoolProp.iDmolar) # Vapor phase molar density In [0]: HEOS.saturated_vapor_keyed_output(CoolProp.iDmolar) # Liquid phase mole fractions In [0]: HEOS.mole_fractions_liquid() # Vapor phase mole fractions - # Should be the bulk composition back since we are doing a dewpoint calculation In [0]: HEOS.mole_fractions_vapor()6. 指定相区(Imposing the Phase)
每次update()默认都要根据输入对判定相态,可能触发一次不小的闪蒸计算。CoolProp 允许在update()前手动指定相区(源码见 AbstractState.h 的specify_phase,它只是转发到后端的calc_specify_phase)。文档实测:对DmolarT_INPUTS这类输入,指定相区可带来约 10% 级别的加速——对某些输入对加速明显,对另一些则几乎无收益(若该输入对无论如何都要做闪蒸,指定相可能被内部忽略)。
可用的相区常量(CoolProp枚举):
| 相区常量 | 相区含义 |
|---|---|
iphase_liquid | p < pcrit & T < Tcrit,饱和线以上(液相区) |
iphase_gas | p < pcrit & T < Tcrit,饱和线以下(气相区) |
iphase_twophase | p < pcrit & T < Tcrit,液/气两相混合 |
iphase_supercritical_liquid | p > pcrit & T < Tcrit |
iphase_supercritical_gas | p < pcrit & T > Tcrit |
iphase_supercritical | p > pcrit & T > Tcrit |
iphase_not_imposed | (默认)由 CoolProp 自行判定相区 |
解除指定有两种写法:
# Reset the specification of phase In [0]: HEOS.specify_phase(CoolProp.iphase_not_imposed) # Or, more simply In [0]: HEOS.unspecify_phase()文档强调两点实践约束:相区指定应紧邻update()调用之前;且绝对关键——输入点必须确实落在所指定的相区内。若指定了错误相区,update()可能抛出异常或返回错误结果;相态不绝对确定时,宁可让 CoolProp 自行判定、付出一些计算代价。
7. 多饱和根的消歧:update_with_guesses与温度猜测
一部分饱和闪蒸输入对存在同一输入值对应多个温度解的情况,默认update返回哪一个由区间法求解器的状态决定(实现定义)。文档列出的实际会出现的情形:
HmolarQ_INPUTS/HmassQ_INPUTS在汽相侧(Q = 1):许多流体的饱和汽相焓 $h_g(T)$ 在远低于临界温度处出现峰值(水约 540 K),峰值两侧一大段焓值区间对应两个饱和温度;QSmolar_INPUTS/QSmass_INPUTS汽相侧:部分流体形状相同;DmolarQ_INPUTS/DmassQ_INPUTS在液相侧(Q = 0)且流体为水或重水(D2O):$\rho_L(T)$ 在 4 °C(水)/ 11 °C(重水)附近有极大值,略低于峰值的密度值对应两个饱和温度。
选择特定根的方式是调用update_with_guesses,并通过CoolProp::GuessesStructure提供温度提示。从实现原理看,闪蒸内部使用饱和线的Chebyshev superancillary表示,构造时会被切分为可证明单调的温度子区间:guess.T选中包含它的子区间,TOMS748 括号法在该子区间内找到唯一根(AbstractState.h 中update_with_guesses定义了该接口)。
水 $h_g(T)$ 峰值两侧的两根示例:
In [1]: import CoolProp.CoolProp as CP In [2]: HEOS = CP.AbstractState("HEOS", "Water") # h_g(T) on water saturated vapor has a maximum near 540 K. h_target = # h_g(470 K) is also reached at some T > 540 K on the falling side. In [3]: HEOS.update(CP.QT_INPUTS, 1.0, 470.0) In [4]: h_target = HEOS.hmolar() # Guess near the low-T root → returns ~470 K In [5]: g = CP.PyGuessesStructure(); g.T = 470.0 In [6]: HEOS.update_with_guesses(CP.HmolarQ_INPUTS, h_target, 1.0, g) In [7]: HEOS.T() # Guess past the peak → returns the *other* root In [8]: g.T = 620.0 In [9]: HEOS.update_with_guesses(CP.HmolarQ_INPUTS, h_target, 1.0, g) In [10]: HEOS.T()水饱和液密度峰值(4 °C 附近)的两根示例(同一模式适用于DmolarQ_INPUTS与QSmolar_INPUTS):
# Water saturated-liquid density max around T ≈ 277.13 K (4 °C). # Pick a target ρ between ρ(near triple) and ρ(near peak) so two roots exist. In [11]: HEOS.update(CP.QT_INPUTS, 0.0, 277.0) In [12]: rho_near_peak = HEOS.rhomolar() In [13]: HEOS.update(CP.QT_INPUTS, 0.0, 273.5) In [14]: rho_near_triple = HEOS.rhomolar() In [15]: rho_target = 0.5*(rho_near_peak + rho_near_triple) # Guess on the rising branch → root below the peak In [16]: g.T = 274.0; HEOS.update_with_guesses(CP.DmolarQ_INPUTS, rho_target, 0.0, g) In [17]: HEOS.T() # Guess on the falling branch → root above the peak In [18]: g.T = 282.0; HEOS.update_with_guesses(CP.DmolarQ_INPUTS, rho_target, 0.0, g) In [19]: HEOS.T()文档给出的若干重要细节(原文笔记逐条继承):
- 该功能要求流体带有 Chebyshev superancillary(标准 CoolProp 构建中的每种纯流体都满足);$h_{sat}(T)$、$s_{sat}(T)$ 的 Chebyshev 展开首次使用时惰性构建,通过采样密度 superancillary 的节点评估 EOS,并在构建时捕获当前参考态;
- 分支选择逻辑只读取
guess.T,GuessesStructure的其他字段对这些输入对被忽略; - 该代码路径仅支持纯流体(非伪纯流体);混合物与伪纯流体会抛
CoolProp::NotImplementedError; - 首次惰性构建完成后,每次调用的候选根枚举成本在微秒量级(每个单调子区间内一次分段 Chebyshev TOMS748 求根);
- 首次调用会惰性构建热类(caloric)superancillary,成本约10–50 ms,可被后续所有调用摊薄;也可以在启动时先调用一次
update_with_guesses预构建。调用CoolProp::set_reference_state后缓存会失效并重新构建,保证系数始终匹配当前参考态。
另外两个行为变化值得注意:
- 若直接调用不带猜测的
update且输入落在多根区间,CoolProp 现在会抛出CoolProp::MultipleSolutionsError(而非静默任选一个),错误信息会列出候选温度并指向update_with_guesses;单根输入照常工作; - 想在不触发异常的前提下探测某
(value, Q)是否多根:用两个充分拉开的guess.T各调用一次update_with_guesses,比较返回的T——差异显著即多根,若多位数一致则解唯一。
8. 低成本的偏导数:first_partial_deriv与second_partial_deriv
低级接口可以以非常低的计算成本获取偏导数(接口文档见CoolProp::AbstractState::first_partial_deriv与second_partial_deriv,实现入口在 AbstractState.h)。文档示例(Python):
In [1]: import CoolProp In [2]: HEOS = CoolProp.AbstractState("HEOS", "Water") In [3]: HEOS.update(CoolProp.PT_INPUTS, 101325, 300) In [4]: HEOS.cpmass() In [4]: HEOS.first_partial_deriv(CoolProp.iHmass, CoolProp.iT, CoolProp.iP) In [4]: %timeit HEOS.first_partial_deriv(CoolProp.iHmass, CoolProp.iT, CoolProp.iP) # See how much faster this is? In [4]: %timeit CoolProp.CoolProp.PropsSI('d(Hmass)/d(T)|P', 'P', 101325, 'T', 300, 'Water') In [4]: HEOS.first_partial_deriv(CoolProp.iSmass, CoolProp.iT, CoolProp.iDmass) # In the same way you can do second partial derivatives # This is the second mixed partial derivative of entropy with respect to density and temperature In [4]: HEOS.second_partial_deriv(CoolProp.iSmass, CoolProp.iT, CoolProp.iDmass, CoolProp.iDmass, CoolProp.iT) # This is the second partial derivative of entropy with respect to density at constant temperature In [4]: HEOS.second_partial_deriv(CoolProp.iSmass,CoolProp.iDmass,CoolProp.iT,CoolProp.iDmass,CoolProp.iT)参数语义:first_partial_deriv(Of, Wrt, Constant)即 $(\partial \mathrm{Of} / \partial \mathrm{Wrt})|_{\mathrm{Constant}}$;second_partial_deriv(Of, Wrt1, Constant1, Wrt2, Constant2)依次给出混合二阶偏导。由于状态已经更新过,重复取导数无需再次闪蒸,因此%timeit对比中低级接口显著快于等价的PropsSI导数字符串(后者每次都走一次高级接口的解析与闪蒸)。
9. 两相与饱和线导数:Thorade 导数与杠杆规则
两相导数:Thorade 的两相导数实现于CoolProp::AbstractState::first_two_phase_deriv;沿饱和线的导数则实现于first_saturation_deriv与second_saturation_deriv。示例:
In [0]: import CoolProp In [0]: HEOS = CoolProp.AbstractState("HEOS", "Water") In [0]: HEOS.update(CoolProp.QT_INPUTS, 0, 300) # First saturation derivative calculated analytically In [0]: HEOS.first_saturation_deriv(CoolProp.iP, CoolProp.iT) In [0]: HEOS.update(CoolProp.QT_INPUTS, 0, 300 + 0.001); p2 = HEOS.p() In [0]: HEOS.update(CoolProp.QT_INPUTS, 0, 300 - 0.001); p1 = HEOS.p() # First saturation derivative calculated numerically In [0]: (p2-p1)/(2*0.001) In [0]: HEOS.update(CoolProp.QT_INPUTS, 0.1, 300) # The d(Dmass)/d(Hmass)|P two-phase derivative In [0]: HEOS.first_two_phase_deriv(CoolProp.iDmass, CoolProp.iHmass, CoolProp.iP) # The d(Dmass)/d(Hmass)|P two-phase derivative using splines In [0]: HEOS.first_two_phase_deriv_splined(CoolProp.iDmass, CoolProp.iHmass, CoolProp.iP, 0.3) # The d(Q)/d(Hmolar)|P two-phase derivative of vapor quality In [0]: HEOS.first_two_phase_deriv(CoolProp.iQ, CoolProp.iHmolar, CoolProp.iP) # The d(Q)/d(P)|Hmolar two-phase derivative of vapor quality In [0]: HEOS.first_two_phase_deriv(CoolProp.iQ, CoolProp.iP, CoolProp.iHmolar)前两组演示了“解析导数 vs 数值差分”的交叉验证;后两组给出干度 $Q$ 的两相导数。文档给出了其推导依据:干度导数直接来自杠杆规则$Q = (h - h')/(h'' - h')$,可得
$$(\partial Q/\partial h)_p = 1/(h'' - h'), \qquad (\partial Q/\partial p)_h = -[(1-Q),\mathrm{d}h'/\mathrm{d}p + Q,\mathrm{d}h''/\mathrm{d}p]/(h'' - h'),$$
其中 $h'$、$h''$ 为饱和液/汽焓。配对规则:摩尔干度iQ配摩尔焓iHmolar,质量干度iQmass配质量焓iHmass;对纯流体两者数值相等。这些导数只定义在两相穹顶内部,且只通过低级接口first_two_phase_deriv提供,高级接口PropsSI的导数字符串不可用。
文档特别列出的限制(均为实现事实):
- 这两个干度导数仅限纯流体,否则抛
NotImplementedError——因为推导要求 $h'$、$h''$ 只是压力的函数;混合物有温度滑移(干度相关相组成),伪纯流体的泡/露点在同温下压力不同,杠杆规则均不成立。对REFPROP后端的伪纯流体,拒绝与否由泡/露压差判定(该差值在临界点处消失,故约在 $10^{-9}$ 约化温度距离 $T_c$ 以内不再拒绝); - 在临界点处 $h'' - h' \to 0$,导数发散,此时抛
ValueError而非返回非有限值(C++ 异常类型;Python 包装层统一表现为ValueError); - 表格类后端(
BICUBIC、TTSE)不实现这两个导数,请向HEOS或REFPROP实例请求。
文档还附了一段绘制 $d\rho/dh|_p$ 随干度变化的完整脚本(亚冷液相段 + 两相段,含样条版本对比),可原样运行:
import numpy as np import CoolProp import matplotlib.pyplot as plt AS = CoolProp.AbstractState('HEOS','Water') # Saturated liquid AS.update(CoolProp.PQ_INPUTS, 101325, 0) Ts = AS.T() h_fg = AS.saturated_vapor_keyed_output(CoolProp.iHmass) - AS.saturated_liquid_keyed_output(CoolProp.iHmass) cl = AS.cpmass() # Subcooled liquid x, y = [], [] for T in np.linspace(Ts - 30, Ts - 0.1, 1000): AS.update(CoolProp.PT_INPUTS, 101325, T) x.append(-cl*(Ts-T)/h_fg) y.append(AS.first_partial_deriv(CoolProp.iDmass, CoolProp.iHmass, CoolProp.iP)) plt.plot(x, y, label = 'Subcooled') # Two-phase derivatives (normal and splined) x, y1 = [], [] for Q in np.linspace(0, 0.3, 1000): AS.update(CoolProp.PQ_INPUTS, 101325, Q) x.append(AS.Q()) y1.append(AS.first_two_phase_deriv_splined(CoolProp.iDmass, CoolProp.iHmass, CoolProp.iP, 0.3)) plt.plot(x, y1, label = 'Two-phase (splined)') # Two-phase derivatives (normal and splined) x, y1 = [], [] for Q in np.linspace(0.0, 0.6, 1000): AS.update(CoolProp.PQ_INPUTS, 101325, Q) x.append(AS.Q()) y1.append(AS.first_two_phase_deriv(CoolProp.iDmass, CoolProp.iHmass, CoolProp.iP)) plt.plot(x, y1, label = 'Two-phase') plt.title(r'$d\rho/dh|p$') plt.xlabel('vapor quality (-)') plt.ylabel(r'$d\rho/dh|p$') plt.ylim(-0.005, 0.005) plt.legend(loc='best')10. 参考态:低级接口下的陷阱与演示
参考态的高级接口用法(见 HighLevelAPI.rst 中的参考态章节)同样适用于低级接口,但有一条必须警惕的行为:
set_reference_stateS(Python 中为set_reference_state)应在代码最开始调用一次,之后不再变更;- 已经由工厂函数创建的
AbstractState实例不会感知后续参考态的更改——这是有意设计,但容易被忽略。
文档给出的演示(注意 AS1/AS2 的差异):
In [1]: import CoolProp as CP # This one doesn't see the change in reference state In [1]: AS1 = CoolProp.AbstractState('HEOS','n-Propane'); In [1]: AS1.update(CoolProp.QT_INPUTS, 0, 233.15); In [1]: CoolProp.CoolProp.set_reference_state('n-Propane','ASHRAE') # This one gets the update in the reference state In [1]: AS2 = CoolProp.AbstractState('HEOS','n-Propane'); In [1]: AS2.update(CoolProp.QT_INPUTS, 0, 233.15); # Note how the AS1 has its default value (change in reference state is not seen) # and AS2 does see the new reference state In [1]: print('{0}, {1}'.format(AS1.hmass(), AS2.hmass())) # Back to the original value In [1]: CoolProp.CoolProp.set_reference_state('n-Propane','DEF')这与第 7 节的 superancillary 缓存行为相呼应:切换参考态后,缓存在首次惰性构建时捕获的参考态会被重建,而旧实例仍按旧参考态工作。工程上的结论是:固定参考态 → 再创建实例,顺序不能颠倒。
11. 以低级接口方式调用 REFPROP
若已安装 REFPROP 库,可以用与 CoolProp 完全相同的低级接口写法、仅把后端名换成REFPROP来调用它:
In [0]: import CoolProp In [0]: REFPROP = CoolProp.AbstractState("REFPROP", "WATER") In [0]: REFPROP.update(CoolProp.DmolarT_INPUTS, 1e-6, 300) In [0]: REFPROP.p(), REFPROP.hmass(), REFPROP.hmolar() In [0]: [REFPROP.keyed_output(k) for k in [CoolProp.iP, CoolProp.iHmass, CoolProp.iHmolar]]这正是第 1 节所述“协议化后端”设计的直接收益:REFPROP 作为外部库被无缝映射进AbstractState结构,调用方代码与HEOS版本逐字相同。
12. 面向无 C++ 类绑定语言的 C 函数层:CoolPropLib.h
对无法直接实例化 C++ 类(或其语言包装)但能调用共享库的语言(Excel、FORTRAN、Julia 等),CoolProp 提供了一层纯 C 函数接口,全部定义在 CoolPropLib.h,函数名均以AbstractState开头。该接口内部生成并管理AbstractState指针,外部拿到的是长整型句柄(handle)。关键函数在源码中的位置:
| 函数 | 源码位置 | 作用 |
|---|---|---|
get_param_index | CoolPropLib.h L253 | 参数名 →parameters整型索引 |
get_input_pair_index | CoolPropLib.h L258 | 输入对名 →input_pairs整型索引 |
AbstractState_factory | CoolPropLib.h L346 | 创建实例,返回 handle |
AbstractState_set_fractions | CoolPropLib.h L377 | 设置混合物摩尔分数 |
AbstractState_update | CoolPropLib.h L523 | 执行输入对更新 |
AbstractState_keyed_output | CoolPropLib.h L554 | 按键取输出 |
AbstractState_update_and_common_out | CoolPropLib.h L670 | 批量更新 + 5 个常用输出 |
AbstractState_update_and_5_out | CoolPropLib.h L716 | 批量更新 + 5 个用户指定输出 |
AbstractState_free | CoolPropLib.h L366 | 显式释放 handle |
以 Julia 包装为例(文档原示例,纯单点计算与混合物两路闪蒸):
julia> import CoolProp julia> PT_INPUTS = CoolProp.get_input_pair_index("PT_INPUTS") 7 julia> cpmass = CoolProp.get_param_index("C") 34 julia> handle = CoolProp.AbstractState_factory("HEOS", "Water") 0 julia> CoolProp.AbstractState_update(handle,PT_INPUTS,101325, 300) julia> CoolProp.AbstractState_keyed_output(handle,cpmass) 4180.635776569655 julia> CoolProp.AbstractState_free(handle) julia> handle = CoolProp.AbstractState_factory("HEOS", "Water&Ethanol") 1 julia> PQ_INPUTS = CoolProp.get_input_pair_index("PQ_INPUTS") 2 julia> T = CoolProp.get_param_index("T") 18 julia> CoolProp.AbstractState_set_fractions(handle, [0.4, 0.6]) julia> CoolProp.AbstractState_update(handle,PQ_INPUTS,101325, 0) julia> CoolProp.AbstractState_keyed_output(handle,T) 352.3522142890429 julia> CoolProp.AbstractState_free(handle)AbstractState_free并非必需——受管AbstractState实例会自动释放;但若要以这种方式生成成千上万乃至百万个实例,定期清理是合理的做法。
C++ 直接调用 C 层的批量基准(见 HighLevelLowLevel.cxx)展示了句柄接口的吞吐形态——注意这里使用了BICUBIC&HEOS混合后端,在 20000 点的 HmassP 闪蒸循环上逐点调用update + keyed_output:
#include "CoolProp/CoolPropLib.h" #include "CoolProp/detail/tools.h" #include <vector> #include <time.h> int main() { double t1, t2; const long buffersize = 500; long errcode = 0; char buffer[buffersize]; long handle = AbstractState_factory("BICUBIC&HEOS", "Water", &errcode, buffer, buffersize); long _HmassP = get_input_pair_index("HmassP_INPUTS"); long _Dmass = get_param_index("Dmass"); long len = 20000; std::vector<double> h = linspace(700000.0, 1500000.0, len); std::vector<double> p = linspace(2.8e6, 3.0e6, len); double summer = 0; t1 = clock(); for (long i = 0; i < len; ++i) { AbstractState_update(handle, _HmassP, h[i], p[i], &errcode, buffer, buffersize); summer += AbstractState_keyed_output(handle, _Dmass, &errcode, buffer, buffersize); } t2 = clock(); std::cout << format("value(all): %0.13g, %g us/call\n", summer, ((double)(t2 - t1)) / CLOCKS_PER_SEC / double(len) * 1e6); return EXIT_SUCCESS; }实际输出(HighLevelLowLevel.cxx.output):
value(all): 8339004.432514, 0.8 us/call另有一个“批量多输出”的变体(HighLevelLowLevelMulti.cxx):一次调用同时取回 5 个常用输出(T、P、Dmolar、Hmolar、Smolar),或 5 个由get_param_index指定的用户自定义输出,进一步摊薄逐点取数的句柄调用开销:
#include "CoolProp/CoolPropLib.h" #include "CoolProp/detail/tools.h" #include <vector> #include <time.h> int main() { const long buffer_size = 1000, length = 100000; long ierr; char herr[buffer_size]; long handle = AbstractState_factory("BICUBIC&HEOS", "Water", &ierr, herr, buffer_size); std::vector<double> T(length), p(length), rhomolar(length), hmolar(length), smolar(length); std::vector<double> input1 = linspace(700000.0, 1500000.0, length); std::vector<double> input2 = linspace(2.8e6, 3.0e6, length); long input_pair = get_input_pair_index("HmassP_INPUTS"); double t1 = clock(); AbstractState_update_and_common_out(handle, input_pair, &(input1[0]), &(input2[0]), length, &(T[0]), &(p[0]), &(rhomolar[0]), &(hmolar[0]), &(smolar[0]), &ierr, herr, buffer_size); double t2 = clock(); std::cout << format("value(commons): %g us/call\n", ((double)(t2 - t1)) / CLOCKS_PER_SEC / double(length) * 1e6); std::vector<long> outputs(5); outputs[0] = get_param_index("T"); outputs[1] = get_param_index("P"); outputs[2] = get_param_index("Dmolar"); outputs[3] = get_param_index("Hmolar"); outputs[4] = get_param_index("Smolar"); std::vector<double> out1(length), out2(length), out3(length), out4(length), out5(length); t1 = clock(); AbstractState_update_and_5_out(handle, input_pair, &(input1[0]), &(input2[0]), length, &(outputs[0]), &(out1[0]), &(out2[0]), &(out3[0]), &(out4[0]), &(out5[0]), &ierr, herr, buffer_size); t2 = clock(); std::cout << format("value(user-specified): %g us/call\n", ((double)(t2 - t1)) / CLOCKS_PER_SEC / double(length) * 1e6); }该函数层实现了低级接口的一个“有限子集”;文档说明如需更多功能应向项目 issue 区提需求。
13. 小结:何时用低级接口,如何用最对
综合 LowLevelAPI.rst 与仓库源码,可以把低级接口的价值与纪律浓缩为:
- 性能:无字符串解析,输入/输出全部枚举 + 浮点;高级接口内部即调用它;热循环中应复用同一个
AbstractState实例,表格类后端(TTSE/Bicubic/SVDSBTL)的构造成本(约 80–140 ms/次)决定了实例复用是硬约束; - 输入组织:
update用input_pairs枚举定位输入槽位,generate_update_pair(DataStructures.h L381-L439)负责把乱序的键值对归一化,并处理 C++ 侧的值交换语义; - 输出组织:访问器与
keyed_output等价,后者支持键循环与运行期键集;C 层还有update_and_common_out/update_and_5_out两个批量变体; - 只有低级才有的能力:
specify_phase加速(相区常量表见第 6 节,指定错误相区会引发异常或错误结果,务必紧邻update()前指定)、混合物露点与两相组成、update_with_guesses多根消歧(依赖 superancillary 单调子区间 + TOMS748,仅纯流体;默认update遇多根现抛MultipleSolutionsError)、first/second_partial_deriv低成本偏导、Thorade 两相导数与饱和线导数(干度导数由杠杆规则推出,限纯流体、表后端未实现)、REFPROP 后端的同构调用; - 参考态纪律:
set_reference_state在创建实例前调用一次;已创建的实例不感知后续变更,且会驱动 caloric superancillary 缓存的失效重建; - 跨语言:不能绑定 C++ 类的语言经
CoolPropLib.h的AbstractState_*C 函数(handle 模式)获得 1 对 1 的低级能力,AbstractState_free可选但大批量创建实例时建议定期调用。
- 科学计算
【免费下载链接】CoolProp
Thermophysical properties for the masses
相关推荐
CoolProp 高级接口(High-Level API)完全指南:PropsSI、相位控制、混合物与偏导数实战
CoolProp 高级接口(High Level API)完全指南:PropsSI、相位控制、混合物与偏导数实战 本文以 CoolProp 官方文档《High
科学计算CoolProp 快速上手指南:PropsSI / HAPropsSI 物性计算与 Python 绘图实战
CoolProp 快速上手指南:PropsSI / HAPropsSI 物性计算与 Python 绘图实战 导读 本篇指南以 CoolProp 官方文档的 Ex
科学计算ShowDoc 中的 PSR-7 接口速查:七大 HTTP 消息接口方法与源码级实践
ShowDoc 中的 PSR 7 接口速查:七大 HTTP 消息接口方法与源码级实践 本文以 ShowDoc 仓库自带的 psr/http message 包文
文档知识库后端前端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考