[OpenPNM] Part 4 ——Phases

发布于 2023-04-03  342 次阅读


Please refresh the page if equations are not rendered correctly.
---------------------------------------------------------------

Creating Phases

OpenPNM中的所有模拟最终都需要知道流体和相的物理和热力学性质。OpenPNM提供了在子模块中计算这些值的框架,以及一组用于预测纯相和混合物性质的基本函数。因为我们不可能支持详尽的物理属性库,所以我们的策略是提供一组合理的默认函数作为一个近似值。当需要更精确的值时,可以使用许多其他包

The classPhase(基础相)

如果仿真过程很简单,那么一个相就足够了。它没有用于计算任何东西的预定义模型,因此必须直接分配已知值(例如粘度)或定义将计算所需值的模型。可以从库中获取模型,也可以自定义模型。我们将在下面介绍所有三个选项

import openpnm as op
import numpy as np
op.visualization.set_mpl_style()
#所有模拟都从定义/创建网络开始。首先创建了一个简单的立方网络,其中包含各种有用的几何属性:Demo

pn = op.network.Demo(shape=[2, 2, 1])
#定义网络后,它作为参数传递给网络模型。

phase1 = op.phase.Phase(network=pn)
print(phase1)

直接赋值

基础相创建一个(几乎)空的模型,每个孔只分配了基本的物理模型例如标准温度和压力。为了使用此对象进行模拟,需要添加一些额外的物理模型。例如,要计算渗透率,我们需要粘度。

在OpenPNM V2中有一个独立的类:Physics 去定义一些物理量,但是在V3 中,这部分内容合并到了Phase这个类里面,有些常见的相已经预定义了许多物理量,如果想要自定义相可以通过本教程后面的add_model方式添加想要的物理模型

因此,让我们直接为phase1分配一个已知值:

phase1['pore.viscosity'] = 0.001  # Pa.s
'''
标量值被扩展到完整的 ndarray
当将标量值分配给字典键时,它被分配给每个孔(或流道)。上述分配的结果如下所示。
'''
phase1['pore.viscosity']
array([0.001, 0.001, 0.001, 0.001])

使用OpenPNM自带的内置模型

如果在存在温度梯度的情况下运行模拟,而粘度是温度的强函数。在这种情况下,需要分配一个孔隙尺度模型,OpenPNM 将运行该模型来计算每个孔隙中的流体粘度。

OpenPNM 中的库包含一些可以使用的通用模型,例如多项式或线性线。四阶多项式可以拟合粘度与温度的实验数据,产生以下系数:

phase1['pore.viscosity'] = 0.001  # Pa.s
a4 = 5.8543E-11
a3 = -7.6756E-08
a2 = 3.7831E-05
a1 = -8.3156E-03
a0 = 6.8898E-01
#这些可以在模型中使用,如下所示:op.models.misc.polynomial

print('Before:', phase1['pore.viscosity'])
f = op.models.misc.polynomial
'''
define: polynomial(target, a, prop):

Calculates a property as a polynomial function of a given property

Parameters

target : Base
       The object for which these values are being calculated.  Thiscontrols the length of the calculated array, and also provides access to other necessary thermofluid properties.
a : array_like
       A list containing the polynomial coefficients, where element 0 in the list corresponds to a0 and so on.  Note that no entries can be skipped so 0 coefficients must be sent as 0.
prop : str
       The dictionary key containing the independent variable or phaseproperty to be used in the polynomial.

Returns

value : ndarray
       Array containing ``Pn(target[prop])``, where ``Pn`` is nth order polynomial with coefficients stored in ``a``.
'''
phase1.add_model(propname='pore.viscosity', 
                 model=f,
                 a = (a0, a1, a2, a3, a4),
                 prop='pore.temperature')
print('After:', phase1['pore.viscosity'])
Before: [0.001 0.001 0.001 0.001]
After: [0.00091476 0.00091476 0.00091476 0.00091476]

注意:当使用该函数分配孔隙尺度模型时,它会自动运行,因此它要么覆盖其中的任何值,要么在该位置创建一个新数组。如果一个模型在运行之前需要计算其他信息,可以在add_model()里添加regen_mode='deferred'

phase1.add_model(propname='pore.viscosity',
model=f,
a = (a0, a1, a2, a3, a4),
prop='pore.temperature'
regen_mode='deferred' )

使用孔隙尺度模型最重要的好处是,如果每个模型的属性之一发生更改,它可以重新计算每个模型。为了说明这一点,让我们将温度设置为 300 到 350 K 之间的随机数,在新温度下重新运行模型:

print('Before:', phase1['pore.viscosity'])
phase1['pore.temperature'] = 300.0 + np.random.rand(pn.Np)*50
'''
random.rand(d0, d1, ..., dn)
Random values in a given shape.
Create an array of the given shape and populate it with random samples from a uniform distribution over [0, 1).

Parameters:
d0, d1, …, dnint, optional
The dimensions of the returned array, must be non-negative. If no argument is given a single Python float is returned.

Returns:
outndarray, shape (d0, d1, ..., dn)
Random values.
'''
phase1.regenerate_models()
print('After:', phase1['pore.viscosity'])
'''
Before: [0.00091476 0.00091476 0.00091476 0.00091476]
After: [0.00066419 0.00077815 0.00065211 0.00060267]
'''

使用特定相模型例如‘水’

#由于水在模拟中非常常见,OpenPNM具有一些可直接使用的属性功能。例如,不需要手动输入数据并去合粘度对于温度的 n 次多项式,因为已经提供了:

print('Before:', phase1['pore.viscosity'])
f = op.models.phase.viscosity.water_correlation 
#Calculates viscosity of pure water or seawater at atmospheric pressure.

phase1.add_model(propname='pore.viscosity',
                 model=f)
print('After:', phase1['pore.viscosity'])
'''
Before: [0.0007246  0.00050654 0.00081815 0.00047741]
After: [0.00069612 0.00047515 0.00079382 0.00044681]
'''

分配新模型将覆盖现有模型并储存在模型中,在本例中,该模型就是我们上面定义的自定义模型。propname='pore.viscostiy'在 pn.models ['pore.viscosity@\']里

自定义模型

创建自定义模型,包含所需的任何功能,就像定义一个新的 python 函数一样简单:

def custom_mu(phase, temperature='pore.temperature'):
    T = phase[temperature]
    a4 = 5.8543E-11
    a3 = -7.6756E-08
    a2 = 3.7831E-05
    a1 = -8.3156E-03
    a0 = 6.8898E-01
    mu = a0 + a1*T + a2*T**2 + a3*T**3 + a4*T**4
    return mu

phase1.add_model(propname='pore.viscosity',
                 model=custom_mu)
print(phase1['pore.viscosity'])
#[0.0007246  0.00050654 0.00081815 0.00047741]

常见流体的类

空气、水和汞的使用非常普遍,OpenPNM 不仅为其属性提供了孔隙尺度模型,而且我们还创建了预定义的类,并且附加了所有适当的模型

water = op.phase.Water(network=pn)
print(water)

从上面的输出中可以看出,已经计算了各种各样的东西,其中大部分来自水的特定孔隙尺度模型。这些可以通过以下方式查看:

print(water.models)

可以看到,这些模型中很多都包含参数(pore.temperature和pore.salinity),这意味着这些模型在运行时会获取参数当前的值,从而使用温度的当前值。

相的种类和多相流设置

纯相

想要创建一个多相流,必须首先创建几个单相:

o2 = op.phase.Species(network=pn, species='oxygen')
print(o2)

如上所示,该类不计算给定物种的任何属性,但在属性中预定义了许多热力学属性

print(o2.params)

#这些参数用于各种属性估计方法。例如,为了计算氧气的粘度,OpenPNM提供了一个函数来实现Stiel和Thodos()的模型:

f = op.models.phase.viscosity.gas_pure_st
o2.add_model(propname='pore.viscosity',
            model=f)
print(o2['pore.viscosity'])
'''
[2.01023694e-05 2.01023694e-05 2.01023694e-05 2.01023694e-05]
'''
#此功能需要几条热力学信息,例如临界温度和临界压力。您可以在下面看到所有参数

print(o2.models)

op.models.phase.viscosity.gas_pure_st(
phase,
T='pore.temperature',
Tc='param.critical_temperature',
Pc='param.critical_pressure',
MW='param.molecular_weight',):

Calculates the viscosity of a pure gas using the correlation in:
Sharqawy M. H., Lienhard J. H., and Zubair, S. M., Desalination and Water Treatment, 2010.

#当我们重新生成模型时,m模型将获取每个孔隙个体的临界温度值,并重新计算粘度值:

o2.regenerate_models()
print(o2['pore.viscosity'])
'''
[2.01023694e-05 2.01023694e-05 2.01023694e-05 2.01023694e-05]
'''

气相和液相

OpenPNM有一套计算纯相性质的函数,但这些函数对于气体和液体是不同的。因此,我们为气体和液体提供了两个类,并已经定义了适当的模型。这些被称为StandardLiquid和StandardGas,以表明所使用的模型是提供第一近似的标准选择:

o2 = op.phase.StandardGas(network=pn, species='o2')
h2o = op.phase.StandardLiquid(network=pn, species='h2o')
print(o2)

print(h2o)

print(h2o.models)

单相混合物

要创建一个单相混合物,我们必须首先指定单个对象。下面我们使用op.phase.StandardGasMixture,它是基本类,但已经附加了用于计算组件属性的各种模型:

o2 = op.phase.StandardGas(network=pn, species='o2', name='oxygen')
n2 = op.phase.StandardGas(network=pn, species='n2', name='nitrogen')
air = op.phase.StandardGasMixture(network=pn, components=[o2, n2])
air.y(o2, 0.21)
air.y(n2, 0.79)
air.regenerate_models()
print(air)

改变混合相的组成,混合相的相关属性会重新计算:

print(o2['pore.viscosity'])
print('Before:', air['pore.viscosity'])
air.y(o2, 0.8)
air.y(n2, 0.2)
air.regenerate_models()
print('After:', air['pore.viscosity'])
'''
[2.01023694e-05 2.01023694e-05 2.01023694e-05 2.01023694e-05]
Before: [1.78543425e-05 1.78543425e-05 1.78543425e-05 1.78543425e-05]
After: [2.01881831e-05 2.01881831e-05 2.01881831e-05 2.01881831e-05]
'''

可以通过查询字典键的方法查询混合相的成分和各组分的含量

print(air['pore.mole_fraction'])
{'oxygen': array([0.8, 0.8, 0.8, 0.8]), 'nitrogen': array([0.2, 0.2, 0.2, 0.2])}

print(air['pore.mole_fraction'][o2.name])
#[0.8 0.8 0.8 0.8]


air['pore.mole_fraction'].keys()
#dict_keys(['oxygen', 'nitrogen'])

air.components
'''
{'oxygen': oxygen : ,
 'nitrogen': nitrogen : }
'''

air.y()
'''
{'oxygen': array([0.8, 0.8, 0.8, 0.8]),
 'nitrogen': array([0.2, 0.2, 0.2, 0.2])}
 '''
air.y(o2.name)
#array([0.8, 0.8, 0.8, 0.8])

单相混合物的相关操作

删除

air.remove_comp(o2.name)
air.components
'''
{'nitrogen': nitrogen : }
'''

#当且仅当出现在混合物字典中时,纯相物质被视为混合物的组成部分。从字典中添加和删除相应的数组实际上是重新定义混合物的组成。'pore.mole_fraction.'
del air['pore.mole_fraction.' + n2.name]
air.components
'''
{}
'''

#可以以相同的方式重新添加它们:
air['pore.mole_fraction.' + o2.name] = 0.21
air.components
'''
{'oxygen': oxygen : }
'''

#还有一个特定的方法:
air.add_comp(n2, mole_fraction=0.79)
air.components
'''
{'oxygen': oxygen : ,
 'nitrogen': nitrogen : }
'''

报告混合物属性 info

air.info
'''
══════════════════════════════════════════════════════════════════════════════
mixture_01 : 
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Properties                                                   Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.temperature                                                    4 / 4
  3  pore.pressure                                                       4 / 4
  4  pore.heat_capacity                                                  4 / 4
  5  pore.thermal_conductivity                                           4 / 4
  6  pore.viscosity                                                      4 / 4
  7  pore.density                                                        4 / 4
  8  pore.mole_fraction.oxygen                                           4 / 4
  9  pore.mole_fraction.nitrogen                                         4 / 4
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Labels                                                 Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.all                                                                4
  3  throat.all                                                              4
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Component Phases
══════════════════════════════════════════════════════════════════════════════
oxygen : 
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Properties                                                   Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.temperature.oxygen                                             4 / 4
  3  pore.pressure.oxygen                                                4 / 4
  4  pore.heat_capacity_gas.oxygen                                       4 / 4
  5  pore.heat_capacity.oxygen                                           4 / 4
  6  pore.thermal_conductivity.oxygen                                    4 / 4
  7  pore.viscosity.oxygen                                               4 / 4
  8  pore.density.oxygen                                                 4 / 4
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Labels                                                 Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.all.oxygen                                                         4
  3  throat.all.oxygen                                                       4
══════════════════════════════════════════════════════════════════════════════
nitrogen : 
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Properties                                                   Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.temperature.nitrogen                                           4 / 4
  3  pore.pressure.nitrogen                                              4 / 4
  4  pore.heat_capacity_gas.nitrogen                                     4 / 4
  5  pore.heat_capacity.nitrogen                                         4 / 4
  6  pore.thermal_conductivity.nitrogen                                  4 / 4
  7  pore.viscosity.nitrogen                                             4 / 4
  8  pore.density.nitrogen                                               4 / 4
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Labels                                                 Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.all.nitrogen                                                       4
  3  throat.all.nitrogen                                                     4
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
'''

获取单相混合物的组分

pore.mole_fraction

air['pore.mole_fraction']
'''
{'oxygen': array([0.21, 0.21, 0.21, 0.21]),
 'nitrogen': array([0.79, 0.79, 0.79, 0.79])}
'''

component

d = air.components
print(d.keys())
'''
dict_keys(['oxygen', 'nitrogen'])
'''

用get_comp_vals获取组分的属性

mus = air.get_comp_vals('pore.viscosity')
print(mus)
'''
{'oxygen': array([2.09391006e-05, 2.09391006e-05, 2.09391006e-05, 2.09391006e-05]), 'nitrogen': array([1.69779528e-05, 1.69779528e-05, 1.69779528e-05, 1.69779528e-05])}
'''

#也可以通过询问混合物并附加组件名称来检索组件的属性,如下所示:
air['pore.viscosity.' + o2.name]
'''
array([2.09391006e-05, 2.09391006e-05, 2.09391006e-05, 2.09391006e-05])
'''

检查一致性check_mixture_health

#例如是否所有摩尔分数加起来每个孔 1.0:

print(air.check_mixture_health())
'''
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Key                                 Value
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
mole_fraction_too_low               []
mole_fraction_too_high              []
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
'''
air.y(o2.name, 0.1)#检查o2含量是否过低(o2含量默认为0.21)
print(air.check_mixture_health())
'''
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
Key                                 Value
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
mole_fraction_too_low               (4,)
mole_fraction_too_high              []
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
'''

使用wildcard () syntax:*

air['pore.viscosity.*']
'''
{'oxygen': array([2.09391006e-05, 2.09391006e-05, 2.09391006e-05, 2.09391006e-05]),
 'nitrogen': array([1.69779528e-05, 1.69779528e-05, 1.69779528e-05, 1.69779528e-05])}
'''
Everything not saved will be lost.
最后更新于 2023-04-05