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,111 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
===============================================================================
|
|
4
|
+
module __Physics__: Base class for mananging pore-scale Physics properties
|
|
5
|
+
===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from OpenPNM.Base import logging
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
from OpenPNM.Network import GenericNetwork
|
|
11
|
+
from OpenPNM.Phases import GenericPhase
|
|
12
|
+
import OpenPNM.Physics.models
|
|
13
|
+
import scipy as sp
|
|
14
|
+
|
|
15
|
+
class GenericPhysics(OpenPNM.Base.Core):
|
|
16
|
+
r"""
|
|
17
|
+
Generic class to generate Physics objects
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
network : OpenPNM Network object
|
|
22
|
+
The network to which this Physics should be attached
|
|
23
|
+
|
|
24
|
+
phase : OpenPNM Phase object
|
|
25
|
+
The Phase object to which this Physics applies
|
|
26
|
+
|
|
27
|
+
geometry : OpenPNM Geometry object
|
|
28
|
+
The Geometry object that defines the pores/throats where this Physics
|
|
29
|
+
should be applied. If this argument is supplied, then pores and
|
|
30
|
+
throats cannot be specified.
|
|
31
|
+
|
|
32
|
+
pores and/or throats : array_like
|
|
33
|
+
The list of pores and throats where this physics applies. If either are
|
|
34
|
+
left blank this will apply the physics nowhere. The locations can be
|
|
35
|
+
change after instantiation using ``set_locations()``. If pores and
|
|
36
|
+
throats are supplied, than a geometry cannot be specified.
|
|
37
|
+
|
|
38
|
+
name : str, optional
|
|
39
|
+
A unique string name to identify the Physics object, typically same as
|
|
40
|
+
instance name but can be anything. If left blank, and name will be
|
|
41
|
+
generated that include the class name and a random string.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self,network=None,phase=None,geometry=None,pores=[],throats=[],**kwargs):
|
|
48
|
+
super(GenericPhysics,self).__init__(**kwargs)
|
|
49
|
+
logger.name = self.name
|
|
50
|
+
|
|
51
|
+
#Associate with Network
|
|
52
|
+
if network is None:
|
|
53
|
+
self._net = GenericNetwork()
|
|
54
|
+
else:
|
|
55
|
+
self._net = network # Attach network to self
|
|
56
|
+
self._net._physics.append(self) # Register self with network
|
|
57
|
+
|
|
58
|
+
#Associate with Phase
|
|
59
|
+
if phase is None:
|
|
60
|
+
self._phases.append(GenericPhase())
|
|
61
|
+
else:
|
|
62
|
+
phase._physics.append(self) # Register self with phase
|
|
63
|
+
self._phases.append(phase) # Register phase with self
|
|
64
|
+
|
|
65
|
+
if geometry is not None:
|
|
66
|
+
if (pores != []) or (throats != []):
|
|
67
|
+
raise Exception('Cannot specify a Geometry AND pores or throats')
|
|
68
|
+
pores = self._net.toindices(self._net['pore.'+geometry.name])
|
|
69
|
+
throats = self._net.toindices(self._net['throat.'+geometry.name])
|
|
70
|
+
|
|
71
|
+
#Initialize a label dictionary in the associated fluid
|
|
72
|
+
self._phases[0]['pore.'+self.name] = False
|
|
73
|
+
self._phases[0]['throat.'+self.name] = False
|
|
74
|
+
self._net['pore.'+self.name] = False
|
|
75
|
+
self._net['throat.'+self.name] = False
|
|
76
|
+
self.set_locations(pores=pores,throats=throats)
|
|
77
|
+
|
|
78
|
+
def __getitem__(self,key):
|
|
79
|
+
element = key.split('.')[0]
|
|
80
|
+
# Convert self.name into 'all'
|
|
81
|
+
if key.split('.')[-1] == self.name:
|
|
82
|
+
key = element + '.all'
|
|
83
|
+
|
|
84
|
+
if key in self.keys(): # Look for data on self...
|
|
85
|
+
return super(GenericPhysics,self).__getitem__(key)
|
|
86
|
+
else: # ...Then check Network
|
|
87
|
+
return self._phases[0][key][self._phases[0][element+'.'+self.name]]
|
|
88
|
+
|
|
89
|
+
def set_locations(self,pores=[],throats=[],mode='add'):
|
|
90
|
+
r'''
|
|
91
|
+
Set the pore and throat locations of the Physics object
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
pores and throats : array_like
|
|
96
|
+
The list of pores and/or throats where the object should be applied.
|
|
97
|
+
mode : string
|
|
98
|
+
Indicates whether list of pores or throats is to be added or removed
|
|
99
|
+
from the object. Options are 'add' (default) or 'remove'.
|
|
100
|
+
'''
|
|
101
|
+
if len(pores) > 0:
|
|
102
|
+
pores = sp.array(pores,ndmin=1)
|
|
103
|
+
self._set_locations(element='pore',locations=pores,mode=mode)
|
|
104
|
+
if len(throats) > 0:
|
|
105
|
+
throats = sp.array(throats,ndmin=1)
|
|
106
|
+
self._set_locations(element='throat',locations=throats,mode=mode)
|
|
107
|
+
|
|
108
|
+
if __name__ == '__main__':
|
|
109
|
+
print('none yet')
|
|
110
|
+
|
|
111
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
===============================================================================
|
|
4
|
+
module Physics
|
|
5
|
+
===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from OpenPNM.Physics import models as pm
|
|
10
|
+
from OpenPNM.Physics.__GenericPhysics__ import GenericPhysics
|
|
11
|
+
|
|
12
|
+
class Standard(GenericPhysics):
|
|
13
|
+
r"""
|
|
14
|
+
Base class to generate a generic Physics object. The user must specify models
|
|
15
|
+
and parameters for the all the properties they require. Classes for several
|
|
16
|
+
common Physics are included with OpenPNM and can be found under OpenPNM.Physics.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
network : OpenPNM Network object
|
|
21
|
+
The network to which this Physics should be attached
|
|
22
|
+
|
|
23
|
+
phase : OpenPNM Phase object
|
|
24
|
+
The Phase object to which this Physics applies
|
|
25
|
+
|
|
26
|
+
pores and throats : array_like
|
|
27
|
+
The pores and throats where this Physics object applies
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self,**kwargs):
|
|
32
|
+
super(Standard,self).__init__(**kwargs)
|
|
33
|
+
self._generate()
|
|
34
|
+
|
|
35
|
+
def _generate(self):
|
|
36
|
+
for phase in self._phases:
|
|
37
|
+
temp = [item.split('.')[1] for item in phase.props()]
|
|
38
|
+
if 'viscosity' in temp:
|
|
39
|
+
self.models.add(propname='throat.hydraulic_conductance',
|
|
40
|
+
model=pm.hydraulic_conductance.hagen_poiseuille)
|
|
41
|
+
if 'diffusivity' in temp:
|
|
42
|
+
self.models.add(propname='throat.diffusive_conductance',
|
|
43
|
+
model=pm.diffusive_conductance.bulk_diffusion)
|
|
44
|
+
if 'surface_tension' in temp:
|
|
45
|
+
self.models.add(propname='throat.capillary_pressure',
|
|
46
|
+
model=pm.capillary_pressure.washburn)
|
|
47
|
+
|
|
48
|
+
if __name__ == '__main__':
|
|
49
|
+
print('none yet')
|
|
50
|
+
|
|
51
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
===============================================================================
|
|
4
|
+
module Physics
|
|
5
|
+
===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from OpenPNM.Physics import models as pm
|
|
10
|
+
from OpenPNM.Physics import GenericPhysics
|
|
11
|
+
|
|
12
|
+
class TestPhysics(GenericPhysics):
|
|
13
|
+
r"""
|
|
14
|
+
Base class to generate a generic Physics object. The user must specify models
|
|
15
|
+
and parameters for the all the properties they require. Classes for several
|
|
16
|
+
common Physics are included with OpenPNM and can be found under OpenPNM.Physics.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
network : OpenPNM Network object
|
|
21
|
+
The network to which this Physics should be attached
|
|
22
|
+
|
|
23
|
+
phase : OpenPNM Phase object
|
|
24
|
+
The Phase object to which this Physics applies
|
|
25
|
+
|
|
26
|
+
pores and throats : array_like
|
|
27
|
+
The pores and throats where this Physics object applies
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self,**kwargs):
|
|
32
|
+
super(TestPhysics,self).__init__(**kwargs)
|
|
33
|
+
self._generate()
|
|
34
|
+
|
|
35
|
+
def _generate(self):
|
|
36
|
+
for phase in self._phases:
|
|
37
|
+
temp = [item.split('.')[1] for item in phase.props()]
|
|
38
|
+
if 'viscosity' in temp:
|
|
39
|
+
self['throat.hydraulic_conductance'] = 1
|
|
40
|
+
if 'diffusivity' in temp:
|
|
41
|
+
self['throat.diffusive_conductance'] = 1
|
|
42
|
+
if 'surface_tension' in temp:
|
|
43
|
+
self['throat.capillary_pressure'] = 1/self._net['throat.diameter']
|
|
44
|
+
if 'thermal_conductivity' in temp:
|
|
45
|
+
self['throat.thermal_conductance'] = phase['throat.thermal_conductivity']*self._net['throat.diameter']/self._net['throat.length']
|
|
46
|
+
|
|
47
|
+
if __name__ == '__main__':
|
|
48
|
+
print('none yet')
|
|
49
|
+
|
|
50
|
+
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
###############################################################################
|
|
3
|
+
:mod:`OpenPNM.Physics` -- Pore Scale Physics Models
|
|
4
|
+
###############################################################################
|
|
5
|
+
|
|
6
|
+
Contents
|
|
7
|
+
--------
|
|
8
|
+
GenericPhysics: This base class is essentially an init statement that ensures
|
|
9
|
+
the Physics object registers itself with Phase and Network objects correctly.
|
|
10
|
+
|
|
11
|
+
Subclasses: OpenPNM includes one subclass called Standard which invokes the
|
|
12
|
+
typical pore scale physics models such as the Hagen-Poiseiulle model for
|
|
13
|
+
hydraulic conductance through a tube. Customized Physics classes can be
|
|
14
|
+
created by adding a file to the Physics directory, which will be imported
|
|
15
|
+
automatically.
|
|
16
|
+
|
|
17
|
+
Classes
|
|
18
|
+
-------
|
|
19
|
+
.. autoclass:: GenericPhysics
|
|
20
|
+
:members:
|
|
21
|
+
|
|
22
|
+
.. autoclass:: Standard
|
|
23
|
+
:members:
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
from .__GenericPhysics__ import GenericPhysics
|
|
28
|
+
from .__Standard__ import Standard
|
|
29
|
+
from .__TestPhysics__ import TestPhysics
|
|
30
|
+
from . import models
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
*******************************************************************************
|
|
3
|
+
models -- Functions for calculating pore-scale Physics models
|
|
4
|
+
*******************************************************************************
|
|
5
|
+
|
|
6
|
+
Contents
|
|
7
|
+
--------
|
|
8
|
+
This submodule contains all pore scale physics models applied to a pore network.
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from . import capillary_pressure
|
|
13
|
+
from . import diffusive_conductance
|
|
14
|
+
from . import electrical_conductance
|
|
15
|
+
from . import thermal_conductance
|
|
16
|
+
from . import hydraulic_conductance
|
|
17
|
+
from . import multiphase
|
|
18
|
+
from . import generic_source_term
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- capillary_pressure
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import scipy as _sp
|
|
9
|
+
|
|
10
|
+
def washburn(physics,
|
|
11
|
+
phase,
|
|
12
|
+
network,
|
|
13
|
+
surface_tension='pore.surface_tension',
|
|
14
|
+
contact_angle='pore.contact_angle',
|
|
15
|
+
throat_diameter='throat.diameter',
|
|
16
|
+
**kwargs):
|
|
17
|
+
r"""
|
|
18
|
+
Computes the capillary entry pressure assuming the throat is a cylindrical tube.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
network : OpenPNM Network Object
|
|
23
|
+
The Network object is
|
|
24
|
+
phase : OpenPNM Phase Object
|
|
25
|
+
Phase object for the invading phases containing the surface tension and
|
|
26
|
+
contact angle values.
|
|
27
|
+
sigma : dict key (string)
|
|
28
|
+
The dictionary key containing the surface tension values to be used. If
|
|
29
|
+
a pore property is given, it is interpolated to a throat list.
|
|
30
|
+
theta : dict key (string)
|
|
31
|
+
The dictionary key containing the contact angle values to be used. If
|
|
32
|
+
a pore property is given, it is interpolated to a throat list.
|
|
33
|
+
throat_diameter : dict key (string)
|
|
34
|
+
The dictionary key containing the throat diameter values to be used.
|
|
35
|
+
|
|
36
|
+
Notes
|
|
37
|
+
-----
|
|
38
|
+
The Washburn equation is:
|
|
39
|
+
|
|
40
|
+
.. math::
|
|
41
|
+
P_c = -\frac{2\sigma(cos(\theta))}{r}
|
|
42
|
+
|
|
43
|
+
This is the most basic approach to calculating entry pressure and is
|
|
44
|
+
suitable for highly non-wetting invading phases in most materials.
|
|
45
|
+
|
|
46
|
+
"""
|
|
47
|
+
if surface_tension.split('.')[0] == 'pore':
|
|
48
|
+
sigma = phase[surface_tension]
|
|
49
|
+
sigma = phase.interpolate_data(data=sigma)
|
|
50
|
+
else:
|
|
51
|
+
sigma = phase[surface_tension]
|
|
52
|
+
if contact_angle.split('.')[0] == 'pore':
|
|
53
|
+
theta = phase[contact_angle]
|
|
54
|
+
theta = phase.interpolate_data(data=theta)
|
|
55
|
+
else:
|
|
56
|
+
theta = phase[contact_angle]
|
|
57
|
+
r = network[throat_diameter]/2
|
|
58
|
+
value = -2*sigma*_sp.cos(_sp.radians(theta))/r
|
|
59
|
+
value = value[phase.throats(physics.name)]
|
|
60
|
+
return value
|
|
61
|
+
|
|
62
|
+
def purcell(physics,
|
|
63
|
+
phase,
|
|
64
|
+
network,
|
|
65
|
+
r_toroid,
|
|
66
|
+
surface_tension='pore.surface_tension',
|
|
67
|
+
contact_angle='pore.contact_angle',
|
|
68
|
+
throat_diameter='throat.diameter',
|
|
69
|
+
**kwargs):
|
|
70
|
+
r"""
|
|
71
|
+
Computes the throat capillary entry pressure assuming the throat is a toroid.
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
network : OpenPNM Network Object
|
|
76
|
+
The Network on which to apply the calculation
|
|
77
|
+
sigma : dict key (string)
|
|
78
|
+
The dictionary key containing the surface tension values to be used. If
|
|
79
|
+
a pore property is given, it is interpolated to a throat list.
|
|
80
|
+
theta : dict key (string)
|
|
81
|
+
The dictionary key containing the contact angle values to be used. If
|
|
82
|
+
a pore property is given, it is interpolated to a throat list.
|
|
83
|
+
throat_diameter : dict key (string)
|
|
84
|
+
The dictionary key containing the throat diameter values to be used.
|
|
85
|
+
r_toroid : float or array_like
|
|
86
|
+
The radius of the toroid surrounding the pore
|
|
87
|
+
|
|
88
|
+
Notes
|
|
89
|
+
-----
|
|
90
|
+
This approach accounts for the converging-diverging nature of many throat
|
|
91
|
+
types. Advancing the meniscus beyond the apex of the toroid requires an
|
|
92
|
+
increase in capillary pressure beyond that for a cylindical tube of the
|
|
93
|
+
same radius. The details of this equation are described by Mason and
|
|
94
|
+
Morrow [1]_, and explored by Gostick [2]_ in the context of a pore network
|
|
95
|
+
model.
|
|
96
|
+
|
|
97
|
+
References
|
|
98
|
+
----------
|
|
99
|
+
|
|
100
|
+
.. [1] G. Mason, N. R. Morrow, Effect of contact angle on capillary displacement curvatures in pore throats formed by spheres. J. Colloid Interface Sci. 168, 130 (1994).
|
|
101
|
+
.. [2] J. Gostick, Random pore network modeling of fibrous PEMFC gas diffusion media using Voronoi and Delaunay tessellations. J. Electrochem. Soc. 160, F731 (2013).
|
|
102
|
+
|
|
103
|
+
TODO: Triple check the accuracy of this equation
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
if surface_tension.split('.')[0] == 'pore':
|
|
107
|
+
sigma = phase[surface_tension]
|
|
108
|
+
sigma = phase.interpolate_data(data=sigma)
|
|
109
|
+
else:
|
|
110
|
+
sigma = phase[surface_tension]
|
|
111
|
+
if contact_angle.split('.')[0] == 'pore':
|
|
112
|
+
theta = phase[contact_angle]
|
|
113
|
+
theta = phase.interpolate_data(data=theta)
|
|
114
|
+
else:
|
|
115
|
+
theta = phase[contact_angle]
|
|
116
|
+
r = network[throat_diameter]/2
|
|
117
|
+
R = r_toroid
|
|
118
|
+
alpha = theta - 180 + _sp.arcsin(_sp.sin(_sp.radians(theta)/(1+r/R)))
|
|
119
|
+
value = (-2*sigma/r)*(_sp.cos(_sp.radians(theta - alpha))/(1 + R/r*(1-_sp.cos(_sp.radians(alpha)))))
|
|
120
|
+
value = value[phase.throats(physics.name)]
|
|
121
|
+
return value
|
|
122
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- diffusive_conductance
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import scipy as _sp
|
|
9
|
+
import OpenPNM.Utilities.misc as misc
|
|
10
|
+
|
|
11
|
+
def bulk_diffusion(physics,
|
|
12
|
+
phase,
|
|
13
|
+
network,
|
|
14
|
+
pore_molar_density='pore.molar_density',
|
|
15
|
+
pore_diffusivity='pore.diffusivity',
|
|
16
|
+
pore_area='pore.area',
|
|
17
|
+
pore_diameter='pore.diameter',
|
|
18
|
+
throat_area='throat.area',
|
|
19
|
+
throat_length='throat.length',
|
|
20
|
+
throat_diameter='throat.diameter',
|
|
21
|
+
calc_pore_len=True,
|
|
22
|
+
**kwargs):
|
|
23
|
+
r"""
|
|
24
|
+
Calculate the diffusive conductance of conduits in network, where a
|
|
25
|
+
conduit is ( 1/2 pore - full throat - 1/2 pore ) based on the areas
|
|
26
|
+
|
|
27
|
+
Parameters
|
|
28
|
+
----------
|
|
29
|
+
network : OpenPNM Network Object
|
|
30
|
+
|
|
31
|
+
phase : OpenPNM Phase Object
|
|
32
|
+
The phase of interest
|
|
33
|
+
|
|
34
|
+
Notes
|
|
35
|
+
-----
|
|
36
|
+
(1) This function requires that all the necessary phase properties already
|
|
37
|
+
be calculated.
|
|
38
|
+
|
|
39
|
+
(2) This function calculates the specified property for the *entire*
|
|
40
|
+
network then extracts the values for the appropriate throats at the end.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
#Get Nt-by-2 list of pores connected to each throat
|
|
44
|
+
Ps = network['throat.conns']
|
|
45
|
+
#Get properties in every pore in the network
|
|
46
|
+
parea = network[pore_area]
|
|
47
|
+
pdia = network[pore_diameter]
|
|
48
|
+
#Get the properties of every throat
|
|
49
|
+
tarea = network[throat_area]
|
|
50
|
+
tlen = network[throat_length]
|
|
51
|
+
#Interpolate pore phase property values to throats
|
|
52
|
+
cp = phase[pore_molar_density]
|
|
53
|
+
ct = phase.interpolate_data(data=cp)
|
|
54
|
+
DABp = phase[pore_diffusivity]
|
|
55
|
+
DABt = phase.interpolate_data(data=DABp)
|
|
56
|
+
if calc_pore_len:
|
|
57
|
+
lengths = misc.conduit_lengths(network,mode='centroid')
|
|
58
|
+
plen1 = lengths[:,0]
|
|
59
|
+
plen2 = lengths[:,2]
|
|
60
|
+
else:
|
|
61
|
+
plen1 = (0.5*pdia[Ps[:,0]])
|
|
62
|
+
plen2 = (0.5*pdia[Ps[:,1]])
|
|
63
|
+
#remove any non-positive lengths
|
|
64
|
+
plen1[plen1<=0]=1e-12
|
|
65
|
+
plen2[plen2<=0]=1e-12
|
|
66
|
+
#Find g for half of pore 1
|
|
67
|
+
gp1 = ct*DABt*parea[Ps[:,0]]/plen1
|
|
68
|
+
gp1[_sp.isnan(gp1)] = _sp.inf
|
|
69
|
+
gp1[~(gp1>0)] = _sp.inf # Set 0 conductance pores (boundaries) to inf
|
|
70
|
+
#Find g for half of pore 2
|
|
71
|
+
gp2 = ct*DABt*parea[Ps[:,1]]/plen2
|
|
72
|
+
gp2[_sp.isnan(gp2)] = _sp.inf
|
|
73
|
+
gp2[~(gp2>0)] = _sp.inf # Set 0 conductance pores (boundaries) to inf
|
|
74
|
+
#Find g for full throat
|
|
75
|
+
#remove any non-positive lengths
|
|
76
|
+
tlen[tlen<=0] = 1e-12
|
|
77
|
+
gt = ct*DABt*tarea/tlen
|
|
78
|
+
value = (1/gt + 1/gp1 + 1/gp2)**(-1)
|
|
79
|
+
value = value[phase.throats(physics.name)]
|
|
80
|
+
return value
|
|
81
|
+
|
|
82
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- electrical_conductance
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import scipy as _sp
|
|
9
|
+
|
|
10
|
+
def series_resistors(physics,
|
|
11
|
+
phase,
|
|
12
|
+
network,
|
|
13
|
+
pore_conductivity='pore.electrical_conductivity',
|
|
14
|
+
pore_area='pore.area',
|
|
15
|
+
pore_diameter='pore.diameter',
|
|
16
|
+
throat_area='throat.area',
|
|
17
|
+
throat_length='throat.length',
|
|
18
|
+
**kwargs):
|
|
19
|
+
r"""
|
|
20
|
+
Calculates the electrical conductance of throat assuming cylindrical geometry
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
network : OpenPNM Network Object
|
|
25
|
+
|
|
26
|
+
phase : OpenPNM Phase Object
|
|
27
|
+
|
|
28
|
+
Notes
|
|
29
|
+
-----
|
|
30
|
+
(1) This function requires that all the necessary phase properties already
|
|
31
|
+
be calculated.
|
|
32
|
+
|
|
33
|
+
(2) This function calculates the specified property for the *entire*
|
|
34
|
+
network then extracts the values for the appropriate throats at the end.
|
|
35
|
+
|
|
36
|
+
"""
|
|
37
|
+
#Get Nt-by-2 list of pores connected to each throat
|
|
38
|
+
Ps = network['throat.conns']
|
|
39
|
+
#Get properties in every pore in the network
|
|
40
|
+
sigmap = phase[pore_conductivity]
|
|
41
|
+
sigmat = phase.interpolate_data(sigmap)
|
|
42
|
+
#Find g for half of pore 1
|
|
43
|
+
parea = network[pore_area]
|
|
44
|
+
pdia = network[pore_diameter]
|
|
45
|
+
#remove any non-positive lengths
|
|
46
|
+
pdia[pdia<=0] = 0
|
|
47
|
+
gp1 = sigmap[Ps[:,0]]*parea[Ps[:,0]]/(0.5*pdia[Ps[:,0]])
|
|
48
|
+
#Find g for half of pore 2
|
|
49
|
+
gp2 = sigmap[Ps[:,1]]*parea[Ps[:,1]]/(0.5*pdia[Ps[:,1]])
|
|
50
|
+
#Find g for full throat
|
|
51
|
+
tarea = network[throat_area]
|
|
52
|
+
tlen = network[throat_length]
|
|
53
|
+
#remove any non-positive lengths
|
|
54
|
+
tlen[tlen<=0] = 0
|
|
55
|
+
gt = sigmat*tarea/tlen
|
|
56
|
+
value = (1/gt + 1/gp1 + 1/gp2)**(-1)
|
|
57
|
+
value = value[phase.throats(physics.name)]
|
|
58
|
+
return value
|
|
59
|
+
|