openpnm 1.0.0__zip
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- OpenPNM-1.1/MANIFEST.in +2 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__FickianDiffusion__.py +67 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__FourierConduction__.py +63 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__GenericAlgorithm__.py +235 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__GenericLinearTransport__.py +641 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolationForImbibition__.py +703 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolationTimed__.py +702 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolation__.py +156 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__OhmicConduction__.py +64 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__OrdinaryPercolation__.py +402 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__StokesFlow__.py +64 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__Tortuosity__.py +91 -0
- OpenPNM-1.1/OpenPNM/Algorithms/__init__.py +48 -0
- OpenPNM-1.1/OpenPNM/Base/__Controller__.py +480 -0
- OpenPNM-1.1/OpenPNM/Base/__Core__.py +1522 -0
- OpenPNM-1.1/OpenPNM/Base/__ModelsDict__.py +345 -0
- OpenPNM-1.1/OpenPNM/Base/__Tools__.py +72 -0
- OpenPNM-1.1/OpenPNM/Base/__init__.py +32 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Boundary__.py +80 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Cube_and_Cuboid__.py +64 -0
- OpenPNM-1.1/OpenPNM/Geometry/__GenericGeometry__.py +106 -0
- OpenPNM-1.1/OpenPNM/Geometry/__SGL10__.py +67 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Stick_and_Ball__.py +68 -0
- OpenPNM-1.1/OpenPNM/Geometry/__TestGeometry__.py +51 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Toray090__.py +68 -0
- OpenPNM-1.1/OpenPNM/Geometry/__Voronoi__.py +98 -0
- OpenPNM-1.1/OpenPNM/Geometry/__init__.py +47 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/__init__.py +33 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_area.py +27 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_centroid.py +35 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_diameter.py +127 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_misc.py +55 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_seed.py +212 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_surface_area.py +28 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_vertices.py +19 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/pore_volume.py +133 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_area.py +47 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_centroid.py +80 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_diameter.py +106 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_length.py +95 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_misc.py +42 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_normal.py +31 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_offset_vertices.py +191 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_perimeter.py +26 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_seed.py +12 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_shape_factor.py +37 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_surface_area.py +44 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_vector.py +27 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_vertices.py +19 -0
- OpenPNM-1.1/OpenPNM/Geometry/models/throat_volume.py +45 -0
- OpenPNM-1.1/OpenPNM/Network/__Cubic__.py +316 -0
- OpenPNM-1.1/OpenPNM/Network/__DelaunayCubic__.py +127 -0
- OpenPNM-1.1/OpenPNM/Network/__Delaunay__.py +600 -0
- OpenPNM-1.1/OpenPNM/Network/__GenericNetwork__.py +1184 -0
- OpenPNM-1.1/OpenPNM/Network/__MatFile__.py +331 -0
- OpenPNM-1.1/OpenPNM/Network/__TestNet__.py +109 -0
- OpenPNM-1.1/OpenPNM/Network/__init__.py +40 -0
- OpenPNM-1.1/OpenPNM/Network/models/__init__.py +12 -0
- OpenPNM-1.1/OpenPNM/Network/models/pore_topology.py +106 -0
- OpenPNM-1.1/OpenPNM/Phases/__Air__.py +63 -0
- OpenPNM-1.1/OpenPNM/Phases/__GenericPhase__.py +146 -0
- OpenPNM-1.1/OpenPNM/Phases/__Mercury__.py +71 -0
- OpenPNM-1.1/OpenPNM/Phases/__TestPhase__.py +46 -0
- OpenPNM-1.1/OpenPNM/Phases/__Water__.py +56 -0
- OpenPNM-1.1/OpenPNM/Phases/__init__.py +38 -0
- OpenPNM-1.1/OpenPNM/Phases/models/__init__.py +22 -0
- OpenPNM-1.1/OpenPNM/Phases/models/contact_angle.py +34 -0
- OpenPNM-1.1/OpenPNM/Phases/models/density.py +81 -0
- OpenPNM-1.1/OpenPNM/Phases/models/diffusivity.py +95 -0
- OpenPNM-1.1/OpenPNM/Phases/models/electrical_conductivity.py +10 -0
- OpenPNM-1.1/OpenPNM/Phases/models/misc.py +125 -0
- OpenPNM-1.1/OpenPNM/Phases/models/molar_density.py +69 -0
- OpenPNM-1.1/OpenPNM/Phases/models/molar_mass.py +31 -0
- OpenPNM-1.1/OpenPNM/Phases/models/surface_tension.py +104 -0
- OpenPNM-1.1/OpenPNM/Phases/models/thermal_conductivity.py +98 -0
- OpenPNM-1.1/OpenPNM/Phases/models/vapor_pressure.py +69 -0
- OpenPNM-1.1/OpenPNM/Phases/models/viscosity.py +103 -0
- OpenPNM-1.1/OpenPNM/Physics/__GenericPhysics__.py +111 -0
- OpenPNM-1.1/OpenPNM/Physics/__Standard__.py +51 -0
- OpenPNM-1.1/OpenPNM/Physics/__TestPhysics__.py +50 -0
- OpenPNM-1.1/OpenPNM/Physics/__init__.py +30 -0
- OpenPNM-1.1/OpenPNM/Physics/models/__init__.py +18 -0
- OpenPNM-1.1/OpenPNM/Physics/models/capillary_pressure.py +122 -0
- OpenPNM-1.1/OpenPNM/Physics/models/diffusive_conductance.py +82 -0
- OpenPNM-1.1/OpenPNM/Physics/models/electrical_conductance.py +59 -0
- OpenPNM-1.1/OpenPNM/Physics/models/generic_source_term.py +564 -0
- OpenPNM-1.1/OpenPNM/Physics/models/hydraulic_conductance.py +76 -0
- OpenPNM-1.1/OpenPNM/Physics/models/multiphase.py +133 -0
- OpenPNM-1.1/OpenPNM/Physics/models/thermal_conductance.py +67 -0
- OpenPNM-1.1/OpenPNM/Postprocessing/Graphics.py +251 -0
- OpenPNM-1.1/OpenPNM/Postprocessing/Plots.py +369 -0
- OpenPNM-1.1/OpenPNM/Postprocessing/__init__.py +10 -0
- OpenPNM-1.1/OpenPNM/Utilities/IO.py +277 -0
- OpenPNM-1.1/OpenPNM/Utilities/Shortcuts.py +17 -0
- OpenPNM-1.1/OpenPNM/Utilities/__init__.py +16 -0
- OpenPNM-1.1/OpenPNM/Utilities/misc.py +226 -0
- OpenPNM-1.1/OpenPNM/Utilities/transformations.py +1923 -0
- OpenPNM-1.1/OpenPNM/Utilities/vertexops.py +824 -0
- OpenPNM-1.1/OpenPNM/__init__.py +56 -0
- OpenPNM-1.1/OpenPNM.egg-info/PKG-INFO +11 -0
- OpenPNM-1.1/OpenPNM.egg-info/SOURCES.txt +107 -0
- OpenPNM-1.1/OpenPNM.egg-info/dependency_links.txt +1 -0
- OpenPNM-1.1/OpenPNM.egg-info/requires.txt +1 -0
- OpenPNM-1.1/OpenPNM.egg-info/top_level.txt +1 -0
- OpenPNM-1.1/PKG-INFO +11 -0
- OpenPNM-1.1/README.txt +88 -0
- OpenPNM-1.1/setup.cfg +7 -0
- OpenPNM-1.1/setup.py +39 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
===============================================================================
|
|
4
|
+
module __GenericPhase__: Base class for building Phase objects
|
|
5
|
+
===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from OpenPNM.Base import Core, Tools, logging
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
from OpenPNM.Network import GenericNetwork
|
|
11
|
+
import OpenPNM.Phases.models
|
|
12
|
+
import scipy as sp
|
|
13
|
+
|
|
14
|
+
class GenericPhase(Core):
|
|
15
|
+
r"""
|
|
16
|
+
Base class to generate a generic phase object. The user must specify models
|
|
17
|
+
and parameters for all the properties they require. Classes for several
|
|
18
|
+
common phases are included with OpenPNM and can be found under OpenPNM.Phases.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
network : OpenPNM Network object
|
|
23
|
+
The network to which this Phase should be attached
|
|
24
|
+
|
|
25
|
+
components : list of OpenPNM Phase objects
|
|
26
|
+
These Phase objects are ficticious or virtual phases that are the pure
|
|
27
|
+
components from which the mixture is made. They are used to calculate
|
|
28
|
+
and store any pure component data. If none are supplied then this
|
|
29
|
+
object will act like either a pure component, a mixture whose properties
|
|
30
|
+
are well known (like air) and need not to be found from consideration of
|
|
31
|
+
the pure component properties.
|
|
32
|
+
|
|
33
|
+
name : str, optional
|
|
34
|
+
A unique string name to identify the Phase object, typically same as
|
|
35
|
+
instance name but can be anything.
|
|
36
|
+
|
|
37
|
+
"""
|
|
38
|
+
def __init__(self,network=None,components=[],**kwargs):
|
|
39
|
+
super(GenericPhase,self).__init__(**kwargs)
|
|
40
|
+
logger.name = self.name
|
|
41
|
+
|
|
42
|
+
if network is None:
|
|
43
|
+
self._net = GenericNetwork()
|
|
44
|
+
else:
|
|
45
|
+
self._net = network
|
|
46
|
+
|
|
47
|
+
# Initialize label 'all' in the object's own info dictionaries
|
|
48
|
+
self['pore.all'] = self._net['pore.all']
|
|
49
|
+
self['throat.all'] = self._net['throat.all']
|
|
50
|
+
|
|
51
|
+
#Set standard conditions on the fluid to get started
|
|
52
|
+
self['pore.temperature'] = 298.0
|
|
53
|
+
self['pore.pressure'] = 101325.0
|
|
54
|
+
|
|
55
|
+
# Register Ohase object in Network dictionary
|
|
56
|
+
self._net['pore.'+self.name] = True
|
|
57
|
+
self._net['throat.'+self.name] = True
|
|
58
|
+
|
|
59
|
+
if components != []:
|
|
60
|
+
for comp in components:
|
|
61
|
+
self.set_component(phase=comp)
|
|
62
|
+
self._net._phases.append(self) # Append this Phase to the Network
|
|
63
|
+
|
|
64
|
+
def __setitem__(self,prop,value):
|
|
65
|
+
for phys in self._physics:
|
|
66
|
+
if (prop in phys.keys()) and ('all' not in prop.split('.')):
|
|
67
|
+
logger.error(prop+' is already defined in at least one associated Physics object')
|
|
68
|
+
return
|
|
69
|
+
super(GenericPhase,self).__setitem__(prop,value)
|
|
70
|
+
|
|
71
|
+
def __getitem__(self,key):
|
|
72
|
+
if key.split('.')[-1] == self.name:
|
|
73
|
+
element = key.split('.')[0]
|
|
74
|
+
return self[element+'.all']
|
|
75
|
+
if key not in self.keys():
|
|
76
|
+
logger.debug(key+' not on Phase, constructing data from Physics')
|
|
77
|
+
return self._interleave_data(key,sources=self._physics)
|
|
78
|
+
else:
|
|
79
|
+
return super(GenericPhase,self).__getitem__(key)
|
|
80
|
+
|
|
81
|
+
def set_component(self,phase,mode='add'):
|
|
82
|
+
r'''
|
|
83
|
+
This method is used to add or remove a ficticious phase object to this
|
|
84
|
+
object.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
phase : OpenPNM Phase object
|
|
89
|
+
This is the ficticious phase object defining a pure component.
|
|
90
|
+
|
|
91
|
+
mode : string
|
|
92
|
+
Indicates whether to 'add' or 'remove' the supplied Phase object
|
|
93
|
+
'''
|
|
94
|
+
if mode == 'add':
|
|
95
|
+
if phase.name in self.phases():
|
|
96
|
+
logger.error('Phase already present')
|
|
97
|
+
pass
|
|
98
|
+
else:
|
|
99
|
+
self._phases.append(phase) # Associate any sub-phases with self
|
|
100
|
+
phase._phases.append(self) # Associate self with sub-phases
|
|
101
|
+
#Add models for components to inherit mixture T and P
|
|
102
|
+
phase.models.add(propname='pore.temperature',model=OpenPNM.Phases.models.misc.mixture_value)
|
|
103
|
+
phase.models.add(propname='pore.pressure',model=OpenPNM.Phases.models.misc.mixture_value)
|
|
104
|
+
#Move T and P models to beginning of regeneration order
|
|
105
|
+
phase.models.reorder({'pore.temperature':0,'pore.pressure':1})
|
|
106
|
+
elif mode == 'remove':
|
|
107
|
+
if phase.name in self.phases():
|
|
108
|
+
self._phases.remove(phase)
|
|
109
|
+
phase._phases = []
|
|
110
|
+
else:
|
|
111
|
+
logger.error('Phase not found')
|
|
112
|
+
pass
|
|
113
|
+
|
|
114
|
+
def check_mixture_health(self):
|
|
115
|
+
r'''
|
|
116
|
+
Query the properties of the 'virtual phases' that make up a mixture
|
|
117
|
+
to ensure they all add up
|
|
118
|
+
'''
|
|
119
|
+
mole_sum = sp.zeros((self.Np,))
|
|
120
|
+
for comp in self._phases:
|
|
121
|
+
try:
|
|
122
|
+
mole_sum = mole_sum + comp['pore.mole_fraction']
|
|
123
|
+
except:
|
|
124
|
+
pass
|
|
125
|
+
return mole_sum
|
|
126
|
+
|
|
127
|
+
def check_physics_health(self):
|
|
128
|
+
r'''
|
|
129
|
+
Perform a check to find pores which have overlapping or undefined Physics
|
|
130
|
+
'''
|
|
131
|
+
phys = self.physics()
|
|
132
|
+
Ptemp = sp.zeros((self.Np,))
|
|
133
|
+
Ttemp = sp.zeros((self.Nt,))
|
|
134
|
+
for item in phys:
|
|
135
|
+
Pind = self['pore.'+item]
|
|
136
|
+
Tind = self['throat.'+item]
|
|
137
|
+
Ptemp[Pind] = Ptemp[Pind] + 1
|
|
138
|
+
Ttemp[Tind] = Ttemp[Tind] + 1
|
|
139
|
+
health = Tools.HealthDict()
|
|
140
|
+
health['overlapping_pores'] = sp.where(Ptemp>1)[0].tolist()
|
|
141
|
+
health['undefined_pores'] = sp.where(Ptemp==0)[0].tolist()
|
|
142
|
+
health['overlapping_throats'] = sp.where(Ttemp>1)[0].tolist()
|
|
143
|
+
health['undefined_throats'] = sp.where(Ttemp==0)[0].tolist()
|
|
144
|
+
return health
|
|
145
|
+
|
|
146
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from OpenPNM.Phases import GenericPhase
|
|
3
|
+
from OpenPNM.Phases import models as fm
|
|
4
|
+
|
|
5
|
+
class Mercury(GenericPhase):
|
|
6
|
+
r"""
|
|
7
|
+
Creates Phase object with a default name 'Hg' and preset values for
|
|
8
|
+
mercury.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
network : OpenPNM Network object
|
|
13
|
+
The network to which this phase object will be attached.
|
|
14
|
+
|
|
15
|
+
Notes
|
|
16
|
+
-----
|
|
17
|
+
This explicit association is necessary so the Phase object can initialize
|
|
18
|
+
data arrays of the correct size to store network data.
|
|
19
|
+
The initial properties are all at std conditions of T = 298 K and P = 1 atm.
|
|
20
|
+
|
|
21
|
+
References
|
|
22
|
+
----------
|
|
23
|
+
[1] Thermophysical Properties of Materials for Nuclear Engineering: IAEA, Vienna, 2008. ISBN 978-92-0-106508-7:
|
|
24
|
+
|
|
25
|
+
Examples
|
|
26
|
+
--------
|
|
27
|
+
>>> import OpenPNM
|
|
28
|
+
>>> pn = OpenPNM.Network.TestNet()
|
|
29
|
+
>>> hg = OpenPNM.Phases.Mercury(network=pn)
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
def __init__(self,name=None,**kwargs):
|
|
33
|
+
super(Mercury,self).__init__(name=name,**kwargs)
|
|
34
|
+
self._generate()
|
|
35
|
+
|
|
36
|
+
def _generate(self):
|
|
37
|
+
self['pore.molecular_weight'] = 0.2006 # kg/mol
|
|
38
|
+
self['pore.critical_pressure'] = 1.662E8 # Pa
|
|
39
|
+
self['pore.critical_temperature'] = 1733 # K
|
|
40
|
+
self['pore.critical_volume'] = 0.000189 # kg/m3
|
|
41
|
+
self['pore.contact_angle'] = 140 # Degree
|
|
42
|
+
self.models.add(propname='pore.vapor_pressure', # Pa
|
|
43
|
+
model=fm.vapor_pressure.antoine,
|
|
44
|
+
A=9.85767,
|
|
45
|
+
B=3007.129,
|
|
46
|
+
C=-10.001)
|
|
47
|
+
self.models.add(propname='pore.density', # kg/m3
|
|
48
|
+
model=fm.misc.linear,
|
|
49
|
+
poreprop='pore.temperature',
|
|
50
|
+
b=14280.9,
|
|
51
|
+
m=-2.47004)
|
|
52
|
+
self.models.add(propname='pore.molar_density',
|
|
53
|
+
model=fm.molar_density.standard) # mol/m3
|
|
54
|
+
self.models.add(propname='pore.surface_tension', # N/m
|
|
55
|
+
model=fm.misc.linear,
|
|
56
|
+
poreprop='pore.temperature',
|
|
57
|
+
b=0.56254,
|
|
58
|
+
m=-0.00028)
|
|
59
|
+
self.models.add(propname='pore.thermal_conductivity', # W/m.K
|
|
60
|
+
model=fm.misc.polynomial,
|
|
61
|
+
poreprop='pore.temperature',
|
|
62
|
+
a = [3.98691,0.0170967, -0.0000063862])
|
|
63
|
+
self.models.add(propname='pore.viscosity', # kg/m.s
|
|
64
|
+
model=fm.misc.polynomial,
|
|
65
|
+
poreprop='pore.temperature',
|
|
66
|
+
a = [0.00355837,-0.0000100131,1.23684E-08,-5.16836E-12])
|
|
67
|
+
|
|
68
|
+
if __name__ =="__main__":
|
|
69
|
+
import OpenPNM
|
|
70
|
+
pn = OpenPNM.Network.TestNet()
|
|
71
|
+
air = OpenPNM.Phases.Air(network=pn)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from OpenPNM.Phases import GenericPhase
|
|
3
|
+
|
|
4
|
+
class TestPhase(GenericPhase):
|
|
5
|
+
r'''
|
|
6
|
+
Creates Phase object with a default name 'testphase' and preset values for an air-like
|
|
7
|
+
|
|
8
|
+
Parameters
|
|
9
|
+
----------
|
|
10
|
+
network : OpenPNM Network object
|
|
11
|
+
The network to which this phase object will be attached.
|
|
12
|
+
|
|
13
|
+
Notes
|
|
14
|
+
-----
|
|
15
|
+
This explicit association is necessary so the Phase object can initialize
|
|
16
|
+
data arrays of the correct size to store network data.
|
|
17
|
+
|
|
18
|
+
Examples
|
|
19
|
+
--------
|
|
20
|
+
>>> import OpenPNM
|
|
21
|
+
>>> pn = OpenPNM.Network.TestNet()
|
|
22
|
+
>>> water = OpenPNM.Phases.Water(network=pn)
|
|
23
|
+
'''
|
|
24
|
+
def __init__(self,name=None,**kwargs):
|
|
25
|
+
super(TestPhase,self).__init__(name=name,**kwargs)
|
|
26
|
+
self._generate()
|
|
27
|
+
|
|
28
|
+
def _generate(self):
|
|
29
|
+
self['pore.temperature'] = 298.0
|
|
30
|
+
self['pore.surface_tension'] = 0.072
|
|
31
|
+
self['pore.pressure'] = 101325.0
|
|
32
|
+
self['pore.diffusivity'] = 5.4785e-06
|
|
33
|
+
self['pore.molar_density'] = 40.89
|
|
34
|
+
self['pore.viscosity'] = 1.8443e-05
|
|
35
|
+
self['pore.molecular_weight'] = 28.97
|
|
36
|
+
self['pore.critical_temperature'] = 132.5
|
|
37
|
+
self['pore.critical_volume'] = 0.0883
|
|
38
|
+
self['pore.contact_angle'] = 120
|
|
39
|
+
self['pore.thermal_conductivity'] = 1
|
|
40
|
+
self['throat.thermal_conductivity'] = self.interpolate_data(data=self['pore.thermal_conductivity'])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
if __name__ =="__main__":
|
|
44
|
+
import OpenPNM
|
|
45
|
+
pn = OpenPNM.Network.TestNet()
|
|
46
|
+
water = OpenPNM.Phases.Water(network=pn)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from OpenPNM.Phases import GenericPhase
|
|
3
|
+
from OpenPNM.Phases import models as fm
|
|
4
|
+
|
|
5
|
+
class Water(GenericPhase):
|
|
6
|
+
r'''
|
|
7
|
+
Creates Phase object with preset values for Water
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
network : OpenPNM Network object
|
|
12
|
+
The Network to which this phase object will be associated.
|
|
13
|
+
|
|
14
|
+
Notes
|
|
15
|
+
-----
|
|
16
|
+
This explicit association is necessary so the Phase object can initialize
|
|
17
|
+
data arrays of the correct size to store network data.
|
|
18
|
+
The initial properties are all at std conditions of T = 298 K and P = 1 atm.
|
|
19
|
+
|
|
20
|
+
Examples
|
|
21
|
+
--------
|
|
22
|
+
>>> import OpenPNM
|
|
23
|
+
>>> pn = OpenPNM.Network.TestNet()
|
|
24
|
+
>>> water = OpenPNM.Phases.Water(network=pn)
|
|
25
|
+
'''
|
|
26
|
+
def __init__(self,name=None,**kwargs):
|
|
27
|
+
super(Water,self).__init__(name=name,**kwargs)
|
|
28
|
+
self._generate()
|
|
29
|
+
|
|
30
|
+
def _generate(self):
|
|
31
|
+
self['pore.molecular_weight'] = 0.01802 # kg/mol
|
|
32
|
+
self['pore.critical_pressure'] = 2.2064E7 # Pa
|
|
33
|
+
self['pore.critical_temperature'] = 647.1 # K
|
|
34
|
+
self['pore.critical_volume'] = 0.003106 # kg/m3
|
|
35
|
+
self['pore.contact_angle'] = 110.0 # Degree
|
|
36
|
+
self.models.add(propname='pore.density',
|
|
37
|
+
model=fm.density.water) # kg/m3
|
|
38
|
+
self.models.add(propname='pore.molar_density',
|
|
39
|
+
model=fm.molar_density.standard) # mol/m3
|
|
40
|
+
self['pore.diffusivity'] = 1e-9 # m2/s
|
|
41
|
+
self.models.add(propname='pore.surface_tension',
|
|
42
|
+
model=fm.surface_tension.water) # N/m
|
|
43
|
+
self.models.add(propname='pore.thermal_conductivity',
|
|
44
|
+
model=fm.thermal_conductivity.water) # W/m.K
|
|
45
|
+
self.models.add(propname='pore.vapor_pressure', # Pa
|
|
46
|
+
model=fm.vapor_pressure.antoine,
|
|
47
|
+
A=10.1965,
|
|
48
|
+
B=1730.63,
|
|
49
|
+
C=-39.720)
|
|
50
|
+
self.models.add(propname='pore.viscosity',
|
|
51
|
+
model=fm.viscosity.water) # kg/m.s
|
|
52
|
+
|
|
53
|
+
if __name__ =="__main__":
|
|
54
|
+
import OpenPNM
|
|
55
|
+
pn = OpenPNM.Network.TestNet()
|
|
56
|
+
water = OpenPNM.Phases.Water(network=pn)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
###############################################################################
|
|
3
|
+
:mod:`OpenPNM.Phases` -- Phase Property Estimation Methods
|
|
4
|
+
###############################################################################
|
|
5
|
+
|
|
6
|
+
Contents
|
|
7
|
+
--------
|
|
8
|
+
GenericPhase: The basic class which defines how a Phase is instantiated. It
|
|
9
|
+
also has a few specific method for querying the health of mixtures or physics
|
|
10
|
+
objects.
|
|
11
|
+
|
|
12
|
+
Subclasses: OpenPNM includes a few pre-written subclasses that describe the
|
|
13
|
+
most commonly used materials, like Air, Water and Mercury. Creating a custom
|
|
14
|
+
Phase subclass simply requires placing a file in the Phases directory and it
|
|
15
|
+
will be automatically loaded.
|
|
16
|
+
|
|
17
|
+
Classes
|
|
18
|
+
-------
|
|
19
|
+
|
|
20
|
+
.. autoclass:: GenericPhase
|
|
21
|
+
:members:
|
|
22
|
+
|
|
23
|
+
.. autoclass:: Air
|
|
24
|
+
:members:
|
|
25
|
+
|
|
26
|
+
.. autoclass:: Water
|
|
27
|
+
:members:
|
|
28
|
+
|
|
29
|
+
.. autoclass:: Mercury
|
|
30
|
+
:members:
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
from .__GenericPhase__ import GenericPhase
|
|
34
|
+
from .__Air__ import Air
|
|
35
|
+
from .__Water__ import Water
|
|
36
|
+
from .__Mercury__ import Mercury
|
|
37
|
+
from .__TestPhase__ import TestPhase
|
|
38
|
+
from . import models
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
*******************************************************************************
|
|
3
|
+
models -- Functions for calculating thermofluid properties
|
|
4
|
+
*******************************************************************************
|
|
5
|
+
|
|
6
|
+
Contents
|
|
7
|
+
--------
|
|
8
|
+
This module contains methods for estimating phase properties
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from . import contact_angle
|
|
13
|
+
from . import density
|
|
14
|
+
from . import diffusivity
|
|
15
|
+
from . import electrical_conductivity
|
|
16
|
+
from . import misc
|
|
17
|
+
from . import molar_density
|
|
18
|
+
from . import molar_mass
|
|
19
|
+
from . import surface_tension
|
|
20
|
+
from . import thermal_conductivity
|
|
21
|
+
from . import vapor_pressure
|
|
22
|
+
from . import viscosity
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- contact_angle
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import scipy as sp
|
|
8
|
+
|
|
9
|
+
def young(phase,
|
|
10
|
+
sigma_sg,
|
|
11
|
+
sigma_sl,
|
|
12
|
+
surface_tension='pore.surface_tension',
|
|
13
|
+
**kwargs):
|
|
14
|
+
r'''
|
|
15
|
+
Calculate contact angle using Young's equation
|
|
16
|
+
|
|
17
|
+
Notes
|
|
18
|
+
-----
|
|
19
|
+
Young's equation is: sigma_lg*Cos(theta)=sigma_sg - sigma_sl
|
|
20
|
+
where
|
|
21
|
+
sigma_lg is the liquid-gas surface tension [N/m]
|
|
22
|
+
sigma_sg is the solid-gas surface tension [N/m]
|
|
23
|
+
sigma_sl is the solid-liquid interfacial tension [J/m^2]
|
|
24
|
+
theta is the Young contact angle [rad]
|
|
25
|
+
|
|
26
|
+
'''
|
|
27
|
+
if surface_tension.split('.')[0] == 'pore':
|
|
28
|
+
sigma = phase[surface_tension]
|
|
29
|
+
sigma = phase.interpolate_data(data=sigma)
|
|
30
|
+
else:
|
|
31
|
+
sigma = phase[surface_tension]
|
|
32
|
+
theta = sp.arccos((sigma_sg - sigma_sl)/phase[surface_tension])
|
|
33
|
+
theta = sp.rad2deg(theta)
|
|
34
|
+
return theta
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- density
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import scipy as _sp
|
|
8
|
+
|
|
9
|
+
def standard(phase,**kwargs):
|
|
10
|
+
r'''
|
|
11
|
+
Calculates the mass density from the molecular weight and molar density
|
|
12
|
+
'''
|
|
13
|
+
MW = phase['pore.molecular_weight']
|
|
14
|
+
rho = phase['pore.molar_density']
|
|
15
|
+
value = rho*MW
|
|
16
|
+
return value
|
|
17
|
+
|
|
18
|
+
def ideal_gas(phase,**kwargs):
|
|
19
|
+
r"""
|
|
20
|
+
Uses ideal gas law to calculate the mass density of an ideal gas
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
P, T, MW: float, array_like
|
|
25
|
+
P pressure of the gas in [Pa]
|
|
26
|
+
T temperature of the gas in [K]
|
|
27
|
+
MW molecular weight of the gas in [kg/kmole]
|
|
28
|
+
|
|
29
|
+
Returns
|
|
30
|
+
-------
|
|
31
|
+
rho, the density in [mol/m3]
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
P = phase['pore.pressure']
|
|
36
|
+
T = phase['pore.temperature']
|
|
37
|
+
MW = phase['pore.molecular_weight']
|
|
38
|
+
R = 8.31447
|
|
39
|
+
value = P/(R*T)*MW
|
|
40
|
+
return value
|
|
41
|
+
|
|
42
|
+
def water(phase,**kwargs):
|
|
43
|
+
r"""
|
|
44
|
+
Calculates density of pure water or seawater at atmospheric pressure
|
|
45
|
+
using Eq. (8) given by Sharqawy et. al [1]_. Values at temperature higher
|
|
46
|
+
than the normal boiling temperature are calculated at the saturation pressure.
|
|
47
|
+
|
|
48
|
+
Parameters
|
|
49
|
+
----------
|
|
50
|
+
T, S: strings
|
|
51
|
+
Property names where phase temperature and salinity are located.
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
rho_sw, the density of water/seawater in [kg/m3]
|
|
56
|
+
|
|
57
|
+
Notes
|
|
58
|
+
-----
|
|
59
|
+
T must be in K, and S in g of salt per kg of phase, or ppt (parts per
|
|
60
|
+
thousand)
|
|
61
|
+
VALIDITY: 273 < T < 453 K; 0 < S < 160 g/kg;
|
|
62
|
+
ACCURACY: 0.1 %
|
|
63
|
+
|
|
64
|
+
References
|
|
65
|
+
----------
|
|
66
|
+
[1] Sharqawy M. H., Lienhard J. H., and Zubair, S. M., Desalination and Water Treatment, 2010.
|
|
67
|
+
|
|
68
|
+
"""
|
|
69
|
+
T = phase['pore.temperature']
|
|
70
|
+
try:
|
|
71
|
+
S = phase['pore.salinity']
|
|
72
|
+
except:
|
|
73
|
+
S = 0
|
|
74
|
+
a1=9.9992293295E+02; a2=2.0341179217E-02; a3=-6.1624591598E-03; a4=2.2614664708E-05; a5=-4.6570659168E-08
|
|
75
|
+
b1=8.0200240891E-01; b2=-2.0005183488E-03; b3=1.6771024982E-05; b4=-3.0600536746E-08; b5=-1.6132224742E-11
|
|
76
|
+
TC = T-273.15
|
|
77
|
+
rho_w = a1 + a2*TC + a3*TC**2 + a4*TC**3 + a5*TC**4;
|
|
78
|
+
D_rho = b1*S + b2*S*TC + b3*S*(TC**2) + b4*S*(TC**3) + b5*(S**2)*(TC**2);
|
|
79
|
+
rho_sw = rho_w + D_rho
|
|
80
|
+
value = rho_sw
|
|
81
|
+
return value
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- diffusivity
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import scipy as sp
|
|
8
|
+
|
|
9
|
+
def fuller(phase,MA,MB,vA,vB,**kwargs):
|
|
10
|
+
r"""
|
|
11
|
+
Uses Fuller model to estimate diffusion coefficient for gases from first
|
|
12
|
+
principles at conditions of interest
|
|
13
|
+
|
|
14
|
+
Parameters
|
|
15
|
+
----------
|
|
16
|
+
MA : float, array_like
|
|
17
|
+
Molecular weight of component A [kg/mol]
|
|
18
|
+
MB : float, array_like
|
|
19
|
+
Molecular weight of component B [kg/mol]
|
|
20
|
+
vA: float, array_like
|
|
21
|
+
Sum of atomic diffusion volumes for component A
|
|
22
|
+
vB: float, array_like
|
|
23
|
+
Sum of atomic diffusion volumes for component B
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
T = phase['pore.temperature']
|
|
27
|
+
P = phase['pore.pressure']
|
|
28
|
+
MAB = 2*(1.0/MA+1.0/MB)**(-1)
|
|
29
|
+
MAB = MAB*1e3
|
|
30
|
+
P = P*1e-5
|
|
31
|
+
value = 0.00143*T**1.75/(P*(MAB**0.5)*(vA**(1./3)+vB**(1./3))**2)*1e-4
|
|
32
|
+
return value
|
|
33
|
+
|
|
34
|
+
def fuller_scaling(phase,DABo,To,Po,**kwargs):
|
|
35
|
+
r"""
|
|
36
|
+
Uses Fuller model to adjust a diffusion coefficient for gases from reference conditions to conditions of interest
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
phase : OpenPNM Phase Object
|
|
41
|
+
|
|
42
|
+
DABo : float, array_like
|
|
43
|
+
Diffusion coefficient at reference conditions
|
|
44
|
+
|
|
45
|
+
Po, To : float, array_like
|
|
46
|
+
Pressure & temperature at reference conditions, respectively
|
|
47
|
+
"""
|
|
48
|
+
Ti = phase['pore.temperature']
|
|
49
|
+
Pi = phase['pore.pressure']
|
|
50
|
+
value = DABo*(Ti/To)**1.75*(Po/Pi)
|
|
51
|
+
return value
|
|
52
|
+
|
|
53
|
+
def tyn_calus(phase,VA,VB,sigma_A,sigma_B,**kwargs):
|
|
54
|
+
r"""
|
|
55
|
+
Uses Tyn_Calus model to estimate diffusion coefficient in a dilute liquid solution of A in B from first principles at conditions of interest
|
|
56
|
+
|
|
57
|
+
Parameters
|
|
58
|
+
----------
|
|
59
|
+
T : float, array_like
|
|
60
|
+
Temperature of interest (K)
|
|
61
|
+
viscosity : float, array_like
|
|
62
|
+
Viscosity of solvent (Pa.s)
|
|
63
|
+
VA : float, array_like
|
|
64
|
+
Molar volume of component A at boiling temperature (m3/mol)
|
|
65
|
+
VB : float, array_like
|
|
66
|
+
Molar volume of component B at boiling temperature (m3/mol)
|
|
67
|
+
sigmaA: float, array_like
|
|
68
|
+
Surface tension of component A at boiling temperature (N/m)
|
|
69
|
+
sigmaB: float, array_like
|
|
70
|
+
Surface tension of component B at boiling temperature (N/m)
|
|
71
|
+
|
|
72
|
+
"""
|
|
73
|
+
T = phase['pore.temperature']
|
|
74
|
+
mu = phase['pore.viscosity']
|
|
75
|
+
value = 8.93e-8*(VB*1e6)**0.267/(VA*1e6)**0.433*T*(sigma_B/sigma_A)**0.15/(mu*1e3)
|
|
76
|
+
return value
|
|
77
|
+
|
|
78
|
+
def tyn_calus_Scaling(phase,DABo,To,mu_o,**kwargs):
|
|
79
|
+
r"""
|
|
80
|
+
Uses Tyn_Calus model to adjust a diffusion coeffciient for liquids from reference conditions to conditions of interest
|
|
81
|
+
|
|
82
|
+
Parameters
|
|
83
|
+
----------
|
|
84
|
+
phase : OpenPNM Phase Object
|
|
85
|
+
|
|
86
|
+
DABo : float, array_like
|
|
87
|
+
Diffusion coefficient at reference conditions
|
|
88
|
+
|
|
89
|
+
mu_o, To : float, array_like
|
|
90
|
+
Viscosity & temperature at reference conditions, respectively
|
|
91
|
+
"""
|
|
92
|
+
Ti = phase['pore.temperature']
|
|
93
|
+
mu_i = phase['pore.viscosity']
|
|
94
|
+
value = DABo*(Ti/To)*(mu_o/mu_i)
|
|
95
|
+
return value
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- electrical_conductance
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import scipy as sp
|
|
8
|
+
|
|
9
|
+
def na(**kwargs):
|
|
10
|
+
print('Not Implemented Yet')
|