☰
BWRS方程在天然气物性计算中的工程应用与Python实现
2026/10/20 20:49:18 网站建设 项目流程

简介:面向石油天然气行业管道设计、储气设施评估及相关技术人员,这份资源提供基于BWRS方程的天然气压缩因子与密度计算工具,适用于高压低温工况下的PVT特性建模。BWRS方程比理想气体状态方程精度更高,能描述真实气体行为,是管道输送与储气容量评估的重要依据。包体中含1个C++源文件(bwrs.cpp),约2KB,通过牛顿法或二分法等数值方法迭代求解摩尔体积,进而计算压缩因子Z与密度ρ,覆盖初始化、迭代调整等完整流程,可用于工程快速估算或教学演示。已有579人学习/下载,代码结构紧凑、逻辑完整,读者可直接获取核心算法实现,理解高压天然气物性计算的关键步骤,并为后续扩展或移植提供参考。

1. BWRS方程在天然气物性计算里到底解决什么问题

一条天然气管道仿真里,压缩因子差 5%,流量计量就能差出好几个百分点;换到高压储气库的注采循环,焓值算不准直接带偏压缩机功率估算。BWRS方程就是干这个活儿的——它把天然气的密度、压缩因子、焓、熵、气液相平衡全部从同一个11参数状态方程里推出来,避免立方型方程在高密度区间"系数看着对、算出来偏"的尴尬。这篇文章照着从业者落地路径走:方程结构拆明白、代码能跑、参数怎么来、坑在哪。适合做管道仿真、储气库设计、LNG工艺和轻烃回收的工程师直接参考复现。

2. 吃透BWRS方程的数学骨架:11个参数、混合规则和适用边界

2.1 为什么天然气物性计算绕不开BWRS:和其他状态方程的差距

天然气物性计算选型,第一反应往往是SRK或PR这类立方型状态方程。它们在汽液相平衡计算里是主力,优点是形式简单、三次方程有解析根、迭代快,工程上随便一个流程模拟软件都能算。但落到高压高密度区、含重烃的凝析气、或者对焓值精度要求高的压缩机功率计算,立方型方程的短板就很明显——密度预测偏差可以到3%~8%,而且从同一个方程推不出足够准的剩余焓,你往往还得另挂一套经验关联式来修正。

BWRS方程走的是另一条路。它是BWR方程加上Starling修正(方程名字取自提出者的姓氏首字母),用11个参数拟合轻烃体系的PVT行为,从低压气相一路覆盖到高压液相,温度适用范围基本对齐天然气工程的主战场。这里最值钱的一点是:密度、压缩因子、焓、熵、逸度全部从同一个方程推导,物性之间天然自洽。做管道瞬态仿真时,压力波传播对密度-压力-温度的一致性极其敏感;做压缩机功率核算时,焓差直接决定轴功率——这两个场景里BWRS的优势是立方型方程没法替代的。

代价也不是没有。BWRS没有解析根,密度要迭代求;参数多,抄错一个就全盘皆输;混合规则在不同文献里写法还不统一。所以这个方程"上限高、下限也低",用好了是重炮,用不好就是给自己挖坑。下面把方程拆开讲清楚。

2.2 方程展开式与11个参数的分工记忆法

BWRS方程以摩尔密度ρ为自变量,压力P写成一个带指数修正的多项式:

$$P = RT\rho + \left(B_0RT - A_0 - \frac{C_0}{T^2} + \frac{D_0}{T^3} - \frac{E_0}{T^4}\right)\rho^2 + \left(bRT - a - \frac{d}{T}\right)\rho^3 + \alpha\left(a + \frac{d}{T}\right)\rho^6 + \frac{c\rho^3}{T^2}(1+\gamma\rho^2)e^{-\gamma\rho^2}$$

看着长,其实结构是清晰的。第一项RTρ是理想气体项;ρ²项的系数里,A₀对应分子间吸引力,C₀/D₀/E₀带温度的倒数,负责描述低温下吸引力增强,B₀是体积效应;ρ³项是吸引与排斥的联合修正;ρ⁶项是稠密区的高阶排斥项,到高压液相区才真正起作用;最后那个带指数衰减的项是BWRS区别于老版BWR方程的精华——它模拟极高密度下短程排斥力随密度增加的饱和行为,没有这一项,高压液相密度会系统性偏大。

参数记不住没关系,我一般按"密度管理区间"来记:A₀、B₀、C₀、D₀、E₀这5个带下标0的参数管低密度区(气相主导);a、b、c、d这4个管中高密度区;α和γ是最后那个指数项的形状参数,管稠密区。11个参数里,带温度倒数的C₀、D₀、E₀和c、d,本质上是在拟合分子间作用力随温度的变化,理解了这一层,抄参数表的时候就不容易抄错位置。

方程里R用8.314 J/(mol·K),ρ单位是mol/m³,P是Pa。这个单位约定后面所有代码都按它走——单位制混用是BWRS落地第一翻车点,后面避坑章节专门讲。

2.3 混合规则:哪些参数用二次型组合,哪些用立方根组合

纯组分参数表拿到手只是第一步,天然气是混合物,必须把11个参数按组成混合。BWRS的混合规则在不同文献里写法不一致,这是这个方程最让人头疼的地方。我在模拟项目X里验证过的、也是工程文献里最常见的一套规则是:A₀、B₀、C₀、D₀、E₀这5个参数用二次型组合,带二元交互参数kᵢⱼ;a、b、c、d、α这5个参数用立方根组合,不额外带交互参数。写成公式就是:

$$X_m = \sum_i\sum_j x_i x_j (1-k_{ij})\sqrt{X_i X_j}, \quad X \in {A_0, B_0, C_0, D_0, E_0}$$

$$Y_m = \left(\sum_i x_i \sqrt[3]{Y_i}\right)^3, \quad Y \in {a, b, c, d, \alpha}$$

γ的处理各文献不一样,有的直接取组成摩尔分数加权平均值,有的也放进立方根组合里,我习惯取加权平均,理由是γ本身是个偏形状的参数,线性加和数值稳定、不容易在组分差异大时产生诡异值。

kᵢⱼ是个玄学参数,量级参考:烃类之间的kᵢⱼ通常很小,接近0;含CO₂或H₂S的体系会明显偏离0,0.05~0.15的范围不罕见。问题在于不同文献的kᵢⱼ和自己的参数表是配套的,不能从文献A抄参数、从文献B抄交互参数。最稳的做法是:参数表和kᵢⱼ来自同一个出处,实在凑不齐,就先把kᵢⱼ全部置零跑单组分和二元基准数据验证,看偏差趋势再决定要不要补交互参数。

还有一条血泪经验:混合规则必须和参数来源严格配套。某开发者曾经把一套用立方根混合规则回归出来的参数,拿去做二次型混合,结果甲烷-乙烷二元体系的泡点压力偏了将近10%。不是方程错了,是规则和参数不配套。

3. 用Python实现BWRS压缩因子计算模块:从方程到可调用函数

3.1 最小可运行的密度迭代函数与三种收敛保护

BWRS求压缩因子的本质是求根:给定P、T和组成,找到满足方程的摩尔密度ρ。方程对ρ是隐式的,我用牛顿法迭代,目标函数是f(ρ) = P_calc(ρ) - P。直接上代码。

import math R = 8.314 # J/(mol·K) def bwrs_pressure(rho, T, p): """给定摩尔密度 rho [mol/m3]、温度 T [K],返回 BWRS 方程计算的压强 [Pa]""" A0, B0, C0, D0, E0 = p['A0'], p['B0'], p['C0'], p['D0'], p['E0'] a, b, c, d = p['a'], p['b'], p['c'], p['d'] alpha, gamma = p['alpha'], p['gamma'] term1 = R * T * rho term2 = (B0*R*T - A0 - C0/T**2 + D0/T**3 - E0/T**4) * rho**2 term3 = (b*R*T - a - d/T) * rho**3 term6 = alpha * (a + d/T) * rho**6 term_exp = (c * rho**3 / T**2) * (1 + gamma*rho**2) * math.exp(-gamma*rho**2) return term1 + term2 + term3 + term6 + term_exp def bwrs_dpdrho(rho, T, p): """BWRS 方程对 rho 的解析导数,用于牛顿迭代""" A0, B0, C0, D0, E0 = p['A0'], p['B0'], p['C0'], p['D0'], p['E0'] a, b, c, d = p['a'], p['b'], p['c'], p['d'] alpha, gamma = p['alpha'], p['gamma'] d1 = R * T d2 = 2 * (B0*R*T - A0 - C0/T**2 + D0/T**3 - E0/T**4) * rho d3 = 3 * (b*R*T - a - d/T) * rho**2 d6 = 6 * alpha * (a + d/T) * rho**5 exp_term = math.exp(-gamma*rho**2) d_exp = (3*c*rho**2/T**2) * (1 + gamma*rho**2) * exp_term \ - (2*gamma*c*rho**4/T**2) * exp_term return d1 + d2 + d3 + d6 + d_exp

上面两个函数一个是方程本身,一个是方程对密度的解析导数。牛顿法不要用数值差分求导,BWRS高次项在稠密区数值导数误差会被指数项放大,解析导数也就多写几行的事。

接下来是带收敛保护的密度求解函数:

def solve_density(P, T, params, phase='vapor', tol=1e-9, max_iter=100): """求解给定 P[Pa]、T[K] 下的摩尔密度 [mol/m3] phase: 'vapor' 取最小正根,'liquid' 取最大正根 收敛保护: 多初值扫描 + 阻尼牛顿 + 迭代上限 """ rho_id = P / (R * T) # 理想气体密度作为参考点 if phase == 'vapor': # 气相初值:从理想气体密度向下游标度取 3 个初值 initials = [0.2*rho_id, 0.5*rho_id, rho_id] else: # 液相初值:从理想气体密度向上游标度取 3 个初值 initials = [rho_id, 5*rho_id, 30*rho_id] roots = [] for rho0 in initials: rho = rho0 for _ in range(max_iter): f = bwrs_pressure(rho, T, params) - P df = bwrs_dpdrho(rho, T, params) if abs(df) < 1e-12: break # 导数退化,放弃这个初值 step = f / df # 阻尼:步长超 20% 就砍半,防止越过非物理区 while abs(step) > 0.2 * rho and abs(step) > 1e-6: step *= 0.5 rho -= step if abs(f / P) < tol: break if rho > 0 and abs(bwrs_pressure(rho, T, params) - P) / P < 1e-6: roots.append(rho) if not roots: raise RuntimeError(f"BWRS 密度迭代未收敛: P={P:.3e} Pa, T={T:.2f} K") if phase == 'vapor': return min(roots) return max(roots) def compressibility_factor(P, T, params, phase='vapor'): """压缩因子 Z = P / (rho R T)""" rho = solve_density(P, T, params, phase=phase) return P / (rho * R * T)

这里三个收敛保护都写在代码里:一是多初值扫描,气相和液相分别从理想气体密度上下两侧取初值,避免单初值掉进错误根;二是阻尼牛顿,步长超过当前密度的20%就强制减半,防止迭代冲到负密度区间;三是迭代上限和导数退化检查,不收敛的时候直接报错而不是静默返回错误值。工程上静默错误比报错可怕得多,报错你至少知道要调参数。

调用方式很简单,params是一个字典,包含11个参数。注意Z的定义:Z = P/(ρRT),ρ是求出来的摩尔密度。如果后续要算质量流量,再乘平均摩尔质量,这一步不需要。

3.2 纯组分参数怎么来:文献参数表与回归两条路

代码里的params需要真实参数。我在这里刻意不放一组具体数值——因为BWRS参数和回归数据集强相关,我抄给你一份没经过你亲自校验的数值,比你自己查文献更危险。这个环节的正确打开方式有三条路。

第一条是查已发表的物性手册和论文附录。天然气轻烃体系的BWRS参数在几十年前就有系统回归,很多物性手册后面直接附参数表。用这条路的关键是看两件事:参数表用的单位制是英制还是SI,以及回归时覆盖的温度压力范围。曾有人抄了一组英制参数直接套SI代码,结果压强差了三个数量级,排查了半天才发现是参数表右下角一行小字写的是psia/lbmol·ft³。参数表的回归范围同样要留意,那决定了你的工况在不在有效区间内。

第二条是从商用流程模拟软件的组分库导出。很多流程模拟软件的物性包里内置了BWRS参数,可以直接查出来。这条路的好处是参数一致性有保障,缺点是你得有一份能用那个软件的许可,而且导出的参数往往不带回归数据集说明。

第三条是自己做回归,适合手头有实验数据或者处理非标组分的情况。思路是用scipy的least_squares拟合多个状态点的压缩因子或密度偏差:

def regress_bwrs(P_data, T_data, rho_data, guess_params): """最小二乘回归 BWRS 参数 P_data: 实验压力 [Pa], T_data: 实验温度 [K], rho_data: 实验密度 [mol/m3] """ from scipy.optimize import least_squares def residuals(flat_params): p = dict(zip( ['A0','B0','C0','D0','E0','a','b','c','d','alpha','gamma'], flat_params )) res = [] for P, T, rho_exp in zip(P_data, T_data, rho_data): P_calc = bwrs_pressure(rho_exp, T, p) # 相对偏差,气压和密度量纲差异大,统一用相对值 res.append((P_calc - P) / P) return res init = [guess_params[k] for k in ['A0','B0','C0','D0','E0','a','b','c','d','alpha','gamma']] result = least_squares(residuals, init, method='lm', max_nfev=2000) return dict(zip( ['A0','B0','C0','D0','E0','a','b','c','d','alpha','gamma'], result.x ))

回归的坑在于初值。BWRS参数之间有强相关性,从随机初值出发很容易收敛到局部最优。常见做法是拿同族组分(比如用丙烷参数做初始估计去拟合丁烷)的参数当初值,然后限制回归的温度压力范围不超过数据的覆盖范围。还有一条:回归前把实验数据按温度分层,多看每个温度下的残差分布,如果某个温度段系统性偏大,往往是那个温度区间数据点太少,把方程拽偏了。

3.3 多组分混合气的完整调用流程与单位约定

多组分混合的关键是先把纯组分参数按组成混合,再进入求解函数。我习惯把混合规则写成一个独立函数,参数表和交互参数从外部配置读入,避免每次调用都重新算。

def mix_bwrs_params(composition, pure_params, kij=None): """混合 BWRS 参数 composition: {'CH4': 0.85, 'C2H6': 0.10, 'N2': 0.05} pure_params: {组分名: {11个参数}} kij: 二元交互参数 {( 'CH4','C2H6' ): 0.01} """ comps = list(composition.keys()) x = [composition[c] for c in comps] kij = kij or {} # 二次型组合: A0 B0 C0 D0 E0 quad = {key: 0.0 for key in ['A0','B0','C0','D0','E0']} for idx_i, ci in enumerate(comps): for idx_j, cj in enumerate(comps): k = kij.get((ci, cj), kij.get((cj, ci), 0.0)) for key in quad: quad[key] += x[idx_i] * x[idx_j] * (1 - k) * \ math.sqrt(pure_params[ci][key] * pure_params[cj][key]) # 立方根组合: a b c d alpha cubic = {} for key in ['a','b','c','d','alpha']: s = sum(x[idx] * (pure_params[ci][key] ** (1/3)) for idx, ci in enumerate(comps)) cubic[key] = s ** 3 # gamma 用摩尔分数加权平均 gamma_mix = sum(x[idx] * pure_params[ci]['gamma'] for idx, ci in enumerate(comps)) return {**quad, **cubic, 'gamma': gamma_mix}

调用流程就三步:读参数表、按组成混合、求解密度或压缩因子。整个模块跑通后,我强烈建议先做一件事——拿纯甲烷在几个已知工况下对比查阅到的实验压缩因子数据,Z偏差控制在0.5%以内再进下一步。这一步不做,后面混合气的所有结果都是空中楼阁。

单位约定在代码里写清楚:P用Pa、T用K、ρ用mol/m³、R用8.314。参数表的数值必须能在这个单位制下让方程复现回归时的数据。怎么快速验证量纲?把参数代入P的每一项,看每一项量纲是否都是Pa。A₀配ρ²,量纲是Pa·m⁶/mol²;B₀RT配ρ²,B₀量纲是m³/mol——心里过一遍量纲能拦住一大半抄错参数的翻车现场。

4. 从压缩因子到焓熵与相平衡:把BWRS用进工程计算

4.1 剩余焓与剩余熵的推导路径和最终表达式

压缩因子只是第一步,工程计算真正要的是焓熵——压缩机功率算焓差,节流温降算等焓过程,储气库注采热效应算熵变。BWRS的好处是焓熵可以从方程本身推,不用挂外部关联式。热力学起点是剩余焓和剩余熵的定义:

$$H^R = RT(Z-1) - \int_0^\rho \frac{T(\partial P/\partial T)_\rho - P}{\rho^2},d\rho$$

$$S^R = R\ln Z - \int_0^\rho \frac{(\partial P/\partial T)_\rho - R\rho}{\rho^2},d\rho$$

把第2章的方程对温度做偏导,代入积分,整理后得到可直接编程的表达式:

$$H^R = RT(Z-1) - \left(A_0 + \frac{3C_0}{T^2} - \frac{4D_0}{T^3} + \frac{5E_0}{T^4}\right)\rho - \left(a+\frac{d}{T}\right)\frac{\rho^2}{2} + \left(\alpha a + \frac{2\alpha d}{T}\right)\frac{\rho^5}{5} + \frac{3c}{2\gamma T^2}\left[2 - (\gamma\rho^2+2)e^{-\gamma\rho^2}\right]$$

$$S^R = R\ln Z - \left(B_0R + \frac{2C_0}{T^3} - \frac{3D_0}{T^4} + \frac{4E_0}{T^5}\right)\rho - \frac{bR\rho^2}{2} + \frac{\alpha d\rho^5}{5T} + \frac{c}{\gamma T^3}\left[2 - (\gamma\rho^2+2)e^{-\gamma\rho^2}\right]$$

验证表达式对不对有个土办法:让ρ→0,此时Z→1、所有带ρ的项全部归零、两个指数括号项也归零,H^R和S^R都必须严格趋于0。写完代码先跑这一条极限检查,比对着公式抄三遍都管用。

def residual_enthalpy(rho, T, Z, p): """剩余焓 [J/mol]""" A0, C0, D0, E0 = p['A0'], p['C0'], p['D0'], p['E0'] a, d = p['a'], p['d']; alpha, gamma = p['alpha'], p['gamma'] c = p['c'] exp_part = 2 - (gamma*rho**2 + 2) * math.exp(-gamma*rho**2) hr = R*T*(Z-1) \ - (A0 + 3*C0/T**2 - 4*D0/T**3 + 5*E0/T**4)*rho \ - (a + d/T)*rho**2/2 \ + (alpha*a + 2*alpha*d/T)*rho**5/5 \ + 3*c/(2*gamma*T**2)*exp_part return hr def residual_entropy(rho, T, Z, p): """剩余熵 [J/(mol·K)]""" B0, C0, D0, E0 = p['B0'], p['C0'], p['D0'], p['E0'] b, c, d = p['b'], p['c'], p['d'] alpha, gamma = p['alpha'], p['gamma'] exp_part = 2 - (gamma*rho**2 + 2) * math.exp(-gamma*rho**2) sr = R*math.log(Z) \ - (B0*R + 2*C0/T**3 - 3*D0/T**4 + 4*E0/T**5)*rho \ - b*R*rho**2/2 \ + alpha*d*rho**5/(5*T) \ + c/(gamma*T**3)*exp_part return sr

调用顺序是先算密度和Z,再用同一套ρ、T、Z算焓熵,不要重新求根。注意剩余焓单位是J/mol,工程上习惯换成kJ/kmol就除以1000再乘1000,数值不变,纯粹是单位书写习惯。实际气体的总焓等于理想气体焓加剩余焓,理想气体焓用温度多项式关联式算,这部分和状态方程解耦,谁家的都行。

4.2 用BWRS判断相态与气液闪蒸的基本框架

气液两相区是天然气工程的常客——凝析气在管道里析出液滴、储气库注采过程两相流,都绕不开相平衡。BWRS做闪蒸的思路和立方型方程一个套路:给定T、P和总组成,用K值迭代,但每次迭代的汽液相压缩因子用BWRS分别算。完整闪蒸代码比较长,这里给一个能跑的相态判断函数和闪蒸迭代骨架。

相态判断的核心是分别用vapor根和liquid根算混合吉布斯自由能偏移,取低者为稳定相:

def gibbs_energy_shift(rho, T, Z, composition, p_mix): """混合吉布斯自由能偏移(近似判据)[J/mol]""" hr = residual_enthalpy(rho, T, Z, p_mix) sr = residual_entropy(rho, T, Z, p_mix) # 理想混合项:组成已知,R*T*sum(x*ln x) 是负值 ideal_mix = R * T * sum(x * math.log(x) for x in composition.values() if x > 0) return hr - T * sr + ideal_mix def phase_stability(P, T, composition, pure_params, kij=None): """粗判相态: 返回 'vapor' / 'liquid' / 'two-phase' """ p_mix = mix_bwrs_params(composition, pure_params, kij) g_vap = gibbs_energy_shift( solve_density(P, T, p_mix, phase='vapor'), T, compressibility_factor(P, T, p_mix, phase='vapor'), composition, p_mix) g_liq = gibbs_energy_shift( solve_density(P, T, p_mix, phase='liquid'), T, compressibility_factor(P, T, p_mix, phase='liquid'), composition, p_mix) # 两相都稳定时用面积判据粗略判断;此处用自由能差值的符号 if abs(g_vap - g_liq) < 1e-3 * R * T: return 'two-phase' return 'vapor' if g_vap < g_liq else 'liquid'

这个函数只做粗判,真正进闪蒸还是得走K值迭代。骨架思路是:第一步用Wilson公式给K值初值,第二步分别算汽液相组成和两相密度,第三步用BWRS的逸度系数更新K值,第四步检查物料平衡判据。每一步的具体写法取决于你的混合规则版本,核心注意点是液相根在近临界区可能不存在,这时候要切回单相判断,不能让程序抛异常。

闪蒸迭代里最容易翻车的是初值:K值初值给得太离谱,液相组成迭代会直接发散。我一般会限制每步组成更新的步长,新组成不超过上一步的30%变化量,宁可多迭代几步也不让组成振荡。曾有一次算凝析气在管道阀门的节流后相态,用BWRS算出来的液相分率和商用软件对不上,查了半天是液相密度根选错了——多初值扫描出来三个正根,选了最小的那个,液相密度比实际小了一半。后来统一规则:气相取最小正根,液相取最大正根,问题消失。

5. BWRS落地避坑记录:单位制、参数来源与迭代收敛的五个翻车现场

5.1 同一个组分在不同文献里参数对不上:以参数表回归范围为准

现象:用文献A的甲烷参数算出来的密度和文献B的参数差2%以上,你以为自己抄错了,其实两边都没错。 原因:BWRS参数是对实验数据回归的结果,不同文献用的回归数据集、温度压力范围、甚至目标函数权重都不一样。有的文献优化时重点保气相密度,液相精度就让了一步;有的文献正好相反。所以参数差异不是错误,是回归目标的差异。 解决:选定一个参数来源后,用该来源的回归范围做你的使用边界。如果工况超出那个范围,宁愿换一组覆盖更宽的参数,也不要强行外推。校验动作是拿参数复算几个单组分基准点,Z偏差趋势和来源文献的误差报告对得上才算通过。

5.2 单位制混用:英制参数直接套SI的典型翻车

现象:代码逻辑全对,参数表也填了,结果算出来的压力比实际大几个数量级,或者密度完全离谱。 原因:经典文献的BWRS参数大量使用英制单位——压力psia、密度lbmol/ft³、温度Rankine,气体常数相应也要换。直接把这些数值塞进SI单位代码,方程量纲全乱。 解决:拿到参数表先做量纲审计。用A₀配ρ²项检查量纲是否是Pa,B₀配ρ项检查量纲是否是m³/mol,每一项都过一遍再进代码。还有个土办法:算一个常温常压的纯甲烷Z,正常结果应该非常接近1,如果Z算出0.01或者100,九成是单位制问题。

5.3 迭代震荡与非物理根:初值策略和根的取舍

现象:牛顿迭代在某个工况下反复震荡不收敛,或者收敛到一个正跟但密度明显不合理——气相密度算得比液相还大。 原因:BWRS方程在低温高压区有多个正根,单初值牛顿法会掉进错误的吸引域;阻尼不够时,迭代步长可以跨过好根区间直接冲进非物理区。 解决:多初值扫描是最可靠的兜底方案,我前面的代码里已经内置。气相初值从0.2倍到1倍理想气体密度取三点,液相从1倍到30倍取三点,全部迭代后按相态取最小或最大正根。如果还震荡,检查是否到了近临界区——那里根的结构本身在剧变,需要专门处理。

5.4 近临界区不收敛:改主变量和保根算法的临时办法

现象:温度压力逼近临界点时,密度迭代时不时报不收敛,或者压缩因子Z在两个值之间跳变。 原因:临界点附近等温线的弯曲程度极大,BWRS方程在这个区域可能出现三个非常接近的正根,牛顿法的雅可比矩阵接近奇异,收敛半径急剧缩小。 解决:工程上如果工况真的落在临界附近,两个临时办法:一是把求解变量从ρ换成ln ρ,让迭代步长在数值上更均匀;二是加二分法保根——在牛顿法震荡时切换到二分区间搜索。更实际的做法是承认BWRS在近临界区的局限,评估一下这个工况是否能用其他专用模型,或者用已经实测的数据点做插值,不让状态方程独立扛。

5.5 混合规则与交互参数:非烃组分偏差大的常见原因

现象:甲烷-乙烷二元体系算得挺好,一加CO₂或N₂,泡点压力和密度偏差系统性偏大。 原因:混合规则本身对非烃组分的处理能力有限,更重要的是kᵢⱼ交互参数缺失——你用了带kᵢⱼ的二次型规则,但kᵢⱼ全置零,等于把非理想性全扔给了方程本身。 解决:补齐关键二元对(尤其是CO₂-烃类、N₂-烃类)的交互参数。如果查不到现成的kᵢⱼ,用一小撮二元气液平衡数据回归一个也行——目标函数就是泡点压力残差,几分钟能出结果。注意kᵢⱼ的温度依赖性:有的对在宽温域内kᵢⱼ并非常数,如果回归数据覆盖的温度范围大,要分温度段看残差,不要拿一个平均kᵢⱼ硬扛全程。

6. 验证你的BWRS模块:三组基准工况和时序仿真的提速技巧

模块写完之后,别急着进项目。我习惯跑三组基准工况,每组的验证指标卡死:

表格:BWRS模块验证矩阵

工况组介质温度范围压力范围验证指标
基准1纯甲烷250~350 K0.1~10 MPaZ偏差 < 0.5%
基准2甲烷/乙烷/丙烷混合260~320 K1~8 MPa密度偏差 < 1%
基准3含N₂/CO₂混合气240~300 K0.5~12 MPa气相密度 + 焓校验

基准1过不了就回头查参数和单位,基准1过了再走基准2,逐级加码。基准3重点看焓值,因为工程上压缩机和节流的计算都卡在焓上,密度对了焓不对会带偏功率估算。

时序仿真提速有个小习惯值得养成:管道逐段仿真或者压缩机逐工况扫描时,相邻两个状态点的密度往往变化不大,把上一个工况的密度根作为下一次迭代的初值,能省掉一大半牛顿迭代次数。具体做法是在solve_density外挂一层带记忆的封装——把上次的(组成、T、P、根)缓存起来,新工况先拿缓存的根试迭代,失败再回退多初值扫描。我在模拟项目X里用这个策略,把一次全管网的稳态仿真时间压缩了约40%,代价只是几十行缓存代码。

做这个方向的最后一条教训:BWRS不是拿来就跑的库,它是一套需要你亲自校验参数、确认混合规则、守住适用边界的工具。参数表来源不明就动手算,出了偏差你根本不知道是方程的问题还是参数的问题。先把单组分基准跑稳,再把混合规则焊死在代码里,最后才轮到焓熵和相平衡——按这个顺序走,翻车概率小一大半。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询