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,331 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
===============================================================================
|
|
4
|
+
MatFile: Subclass to import Networks from a Matlab 'mat' file
|
|
5
|
+
===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
import scipy as sp
|
|
9
|
+
import scipy.io as spio
|
|
10
|
+
import os
|
|
11
|
+
from OpenPNM.Network import GenericNetwork
|
|
12
|
+
import OpenPNM.Geometry
|
|
13
|
+
import OpenPNM.Utilities.misc as misc
|
|
14
|
+
from OpenPNM.Base import logging
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class MatFile(GenericNetwork):
|
|
18
|
+
r'''
|
|
19
|
+
MatFile - constructs a pore network from a perfectly formatted .mat file (MATLAB)
|
|
20
|
+
Create network from Matlab file. Returns OpenPNM.Network.GenericNetwork()
|
|
21
|
+
object. The extra data of 'type' will trigger internal and boundary pores.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
filename : string
|
|
26
|
+
filename = 'standard_cubic_5x5x5.mat' (default)
|
|
27
|
+
Name of mat file
|
|
28
|
+
path : string
|
|
29
|
+
path='' (default)
|
|
30
|
+
the full path to the mat file on your computer
|
|
31
|
+
leaving blank searches for the file in the local directory
|
|
32
|
+
xtra_pore_data : list of strings
|
|
33
|
+
xtra_pore_data = ['type','shape','material']
|
|
34
|
+
any additional props to look for in the dictionary
|
|
35
|
+
xtra_throat_data : list of strings
|
|
36
|
+
xtra_throat_data = ['type','shape','material']
|
|
37
|
+
any additional props to look for in the dictionary
|
|
38
|
+
|
|
39
|
+
Examples
|
|
40
|
+
---------
|
|
41
|
+
>>> import OpenPNM, os
|
|
42
|
+
>>> fname = 'test_pn' # or 'test_pn.mat'
|
|
43
|
+
>>> longpath = os.path.abspath(__file__) # line needed for auto-tests
|
|
44
|
+
>>> path,file = os.path.split(longpath) #unfortunately, auto-tests fail without this. Just type in the path of your own file, or leave it blank to search in your current directory.
|
|
45
|
+
>>> pn = OpenPNM.Network.MatFile(filename=fname,path=path,xtra_pore_data='type',xtra_throat_data='type')
|
|
46
|
+
|
|
47
|
+
Notes
|
|
48
|
+
------
|
|
49
|
+
Matfiles should include the following variables
|
|
50
|
+
|
|
51
|
+
+----------------+------------+----------------------------------+
|
|
52
|
+
| Variable Name | Value | Description |
|
|
53
|
+
+================+============+==================================+
|
|
54
|
+
| pcoords | <Npx3> | physical coordinates, in meters, |
|
|
55
|
+
| | float | of pores to be imported |
|
|
56
|
+
+----------------+------------+----------------------------------+
|
|
57
|
+
| pdiameter | <Npx1> | pore diamters, in meters |
|
|
58
|
+
| | float | |
|
|
59
|
+
+----------------+------------+----------------------------------+
|
|
60
|
+
| pvolume | <Npx1> | pore volumes, in cubic meters |
|
|
61
|
+
| | float | |
|
|
62
|
+
+----------------+------------+----------------------------------+
|
|
63
|
+
| pnumbering | <Npx1> | = 0:1:Np-1 |
|
|
64
|
+
| | int | |
|
|
65
|
+
+----------------+------------+----------------------------------+
|
|
66
|
+
| ptype | <Npx1> | (optional) designates surfaces |
|
|
67
|
+
| | int | of pores in network. |
|
|
68
|
+
| | | (more details below) |
|
|
69
|
+
+----------------+------------+----------------------------------+
|
|
70
|
+
| tconnections | <Ntx2> | pore numbers of the two pores |
|
|
71
|
+
| | int | that each throat connects |
|
|
72
|
+
+----------------+------------+----------------------------------+
|
|
73
|
+
| tdiameter | <Ntx1> | throat diameters, in meters |
|
|
74
|
+
| | float | |
|
|
75
|
+
+----------------+------------+----------------------------------+
|
|
76
|
+
| tnumbering | <Ntx1> | = 0:1:Nt-1 |
|
|
77
|
+
| | int | |
|
|
78
|
+
+----------------+------------+----------------------------------+
|
|
79
|
+
| ttype | <Ntx1> | (optional) designates surfaces |
|
|
80
|
+
| | int | of throats in network. |
|
|
81
|
+
| | | (more details below) |
|
|
82
|
+
+----------------+------------+----------------------------------+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
'''
|
|
86
|
+
def __init__(self,filename='', path='', xtra_pore_data=None, xtra_throat_data=None,**kwargs):
|
|
87
|
+
|
|
88
|
+
r"""
|
|
89
|
+
"""
|
|
90
|
+
super(MatFile,self).__init__(**kwargs)
|
|
91
|
+
if filename == '':
|
|
92
|
+
return
|
|
93
|
+
if path == '':
|
|
94
|
+
path = os.path.abspath('.')
|
|
95
|
+
self._path = path
|
|
96
|
+
filepath = os.path.join(self._path,filename)
|
|
97
|
+
self._xtra_pore_data=xtra_pore_data
|
|
98
|
+
self._xtra_throat_data=xtra_throat_data
|
|
99
|
+
self._dictionary=spio.loadmat(filepath)
|
|
100
|
+
|
|
101
|
+
self._Np=sp.size(self._dictionary['pnumbering'])
|
|
102
|
+
self._Nt=sp.size(self._dictionary['tnumbering'])
|
|
103
|
+
|
|
104
|
+
#Run through generation steps
|
|
105
|
+
self._add_pores()
|
|
106
|
+
self._add_throats()
|
|
107
|
+
self._remove_disconnected_clusters()
|
|
108
|
+
self._add_xtra_pore_data()
|
|
109
|
+
self._add_xtra_throat_data()
|
|
110
|
+
self._add_geometry()
|
|
111
|
+
|
|
112
|
+
def _add_pores(self):
|
|
113
|
+
Pind = sp.arange(0,self._Np)
|
|
114
|
+
self['pore.all'] = sp.ones_like(Pind,dtype=bool)
|
|
115
|
+
logger.info('Writing pore data')
|
|
116
|
+
self['pore.coords']=sp.array(self._dictionary['pcoords'],float)
|
|
117
|
+
|
|
118
|
+
def _add_throats(self):
|
|
119
|
+
Tind = sp.arange(0,self._Nt)
|
|
120
|
+
self['throat.all']=sp.ones_like(Tind,dtype=bool)
|
|
121
|
+
logger.info('Writing throat data')
|
|
122
|
+
self['throat.conns']=sp.array(self._dictionary['tconnections'],int)
|
|
123
|
+
|
|
124
|
+
def _remove_disconnected_clusters(self):
|
|
125
|
+
bad_pores = sp.array([],dtype=int)
|
|
126
|
+
self._pore_map = self.pores()
|
|
127
|
+
self._throat_map = self.throats()
|
|
128
|
+
health = self.check_network_health()
|
|
129
|
+
if health['disconnected_clusters'] == []:
|
|
130
|
+
self._throat_map = self.throats()
|
|
131
|
+
self._pore_map = self.pores()
|
|
132
|
+
else:
|
|
133
|
+
Np = self.num_pores()
|
|
134
|
+
Nt = self.num_throats()
|
|
135
|
+
cluster_sizes = [sp.shape(x)[0] for x in health['disconnected_clusters']]
|
|
136
|
+
acceptable_size = min([min([50,Np/2]),max(cluster_sizes)]) # 50 or less, if it's a really small network.
|
|
137
|
+
#step through each cluster of pores. If its a small cluster, add it to the list
|
|
138
|
+
for cluster in health['disconnected_clusters']:
|
|
139
|
+
if sp.shape(cluster)[0] < acceptable_size:
|
|
140
|
+
bad_pores = sp.append(bad_pores,sp.ravel(cluster))
|
|
141
|
+
bad_throats = sp.unique(self.find_neighbor_throats(bad_pores))
|
|
142
|
+
#Create map for pores
|
|
143
|
+
if sp.shape(bad_pores)[0] > 0:
|
|
144
|
+
i = 0
|
|
145
|
+
self._pore_map = sp.zeros((Np-sp.shape(bad_pores)[0],),dtype=int)
|
|
146
|
+
for pore in self.pores():
|
|
147
|
+
if pore not in bad_pores:
|
|
148
|
+
self._pore_map[i] = pore
|
|
149
|
+
i += 1
|
|
150
|
+
#Create map for throats
|
|
151
|
+
if sp.shape(bad_throats)[0] > 0:
|
|
152
|
+
i = 0
|
|
153
|
+
self._throat_map = sp.zeros((Nt-sp.shape(bad_throats)[0],),dtype=int)
|
|
154
|
+
for throat in self.throats():
|
|
155
|
+
if throat not in bad_throats:
|
|
156
|
+
self._throat_map[i] = throat
|
|
157
|
+
i += 1
|
|
158
|
+
#Fix the pore transformer
|
|
159
|
+
try:
|
|
160
|
+
if sp.shape(bad_pores)[0] > 0:
|
|
161
|
+
i = 0
|
|
162
|
+
old_transform = self._dictionary['pname_transform']
|
|
163
|
+
self._dictionary['pname_transform'] = sp.zeros((Np-sp.shape(bad_pores)[0],),dtype=int)
|
|
164
|
+
for pore in self.pores():
|
|
165
|
+
if pore not in bad_pores:
|
|
166
|
+
self._dictionary['pname_transform'][i] = old_transform[pore]
|
|
167
|
+
i += 1
|
|
168
|
+
except:
|
|
169
|
+
logger.info('Could not update pname_transform. Imported network may not have had it.')
|
|
170
|
+
pass
|
|
171
|
+
self.trim(pores=bad_pores)
|
|
172
|
+
|
|
173
|
+
def _add_geometry(self):
|
|
174
|
+
try:
|
|
175
|
+
boundary_pores = sp.where(self['pore.type']!=0)[0]
|
|
176
|
+
boundary_throats = sp.where(self['throat.type']!=0)[0]
|
|
177
|
+
self['throat.top'] = sp.ravel(self['throat.type']==1)
|
|
178
|
+
self['throat.bottom'] = sp.ravel(self['throat.type']==6)
|
|
179
|
+
self['throat.left'] = sp.ravel(self['throat.type']==2)
|
|
180
|
+
self['throat.right'] = sp.ravel(self['throat.type']==5)
|
|
181
|
+
self['throat.front'] = sp.ravel(self['throat.type']==3)
|
|
182
|
+
self['throat.back'] = sp.ravel(self['throat.type']==4)
|
|
183
|
+
self['pore.top'] = self.tomask(pores=sp.ravel(self.find_connected_pores(self.throats('top'))))
|
|
184
|
+
self['pore.bottom'] = self.tomask(sp.ravel(self.find_connected_pores(self.throats('bottom'))))
|
|
185
|
+
self['pore.left'] = self.tomask(sp.ravel(self.find_connected_pores(self.throats('left'))))
|
|
186
|
+
self['pore.right'] = self.tomask(sp.ravel(self.find_connected_pores(self.throats('right'))))
|
|
187
|
+
self['pore.front'] = self.tomask(sp.ravel(self.find_connected_pores(self.throats('front'))))
|
|
188
|
+
self['pore.back'] = self.tomask(sp.ravel(self.find_connected_pores(self.throats('back'))))
|
|
189
|
+
add_boundaries = True
|
|
190
|
+
except:
|
|
191
|
+
boundary_pores = sp.array([])
|
|
192
|
+
boundary_throats = sp.array([])
|
|
193
|
+
logger.info('No boundary pores added.')
|
|
194
|
+
add_boundaries = False
|
|
195
|
+
Ps = sp.where([pore not in boundary_pores for pore in self.pores()])[0]
|
|
196
|
+
Ts = sp.where([throat not in boundary_throats for throat in self.throats()])[0]
|
|
197
|
+
geom = OpenPNM.Geometry.GenericGeometry(network=self,pores=Ps,throats=Ts,name='internal')
|
|
198
|
+
geom['pore.volume'] = sp.ravel(sp.array(self._dictionary['pvolume'][self._pore_map[Ps]],float))
|
|
199
|
+
geom['pore.diameter'] = sp.ravel(sp.array(self._dictionary['pdiameter'][self._pore_map[Ps]],float))
|
|
200
|
+
geom['throat.diameter'] = sp.ravel(sp.array(self._dictionary['tdiameter'][self._throat_map[Ts]],float))
|
|
201
|
+
geom.add_model(propname='pore.area',model=OpenPNM.Geometry.models.pore_area.spherical)
|
|
202
|
+
geom.add_model(propname='throat.area',model=OpenPNM.Geometry.models.throat_area.cylinder)
|
|
203
|
+
|
|
204
|
+
if add_boundaries:
|
|
205
|
+
boun = OpenPNM.Geometry.Boundary(network=self,pores=boundary_pores,throats=boundary_throats,name='boundary')
|
|
206
|
+
self['pore.top_boundary']=self.tomask(pores=self.pores(['top','boundary'],mode='intersection'))
|
|
207
|
+
self['pore.bottom_boundary']=self.tomask(pores=self.pores(['bottom','boundary'],mode='intersection'))
|
|
208
|
+
self['pore.left_boundary']=self.tomask(pores=self.pores(['left','boundary'],mode='intersection'))
|
|
209
|
+
self['pore.right_boundary']=self.tomask(pores=self.pores(['right','boundary'],mode='intersection'))
|
|
210
|
+
self['pore.front_boundary']=self.tomask(pores=self.pores(['front','boundary'],mode='intersection'))
|
|
211
|
+
self['pore.back_boundary']=self.tomask(pores=self.pores(['back','boundary'],mode='intersection'))
|
|
212
|
+
|
|
213
|
+
self['throat.top_boundary']=self.tomask(throats=self.throats(['top','boundary'],mode='intersection'))
|
|
214
|
+
self['throat.bottom_boundary']=self.tomask(throats=self.throats(['bottom','boundary'],mode='intersection'))
|
|
215
|
+
self['throat.left_boundary']=self.tomask(throats=self.throats(['left','boundary'],mode='intersection'))
|
|
216
|
+
self['throat.right_boundary']=self.tomask(throats=self.throats(['right','boundary'],mode='intersection'))
|
|
217
|
+
self['throat.front_boundary']=self.tomask(throats=self.throats(['front','boundary'],mode='intersection'))
|
|
218
|
+
self['throat.back_boundary']=self.tomask(throats=self.throats(['back','boundary'],mode='intersection'))
|
|
219
|
+
|
|
220
|
+
def _add_xtra_pore_data(self):
|
|
221
|
+
xpdata = self._xtra_pore_data
|
|
222
|
+
if xpdata is not None:
|
|
223
|
+
if type(xpdata) is type([]):
|
|
224
|
+
for pdata in xpdata:
|
|
225
|
+
try:
|
|
226
|
+
self['pore.'+pdata]=self._dictionary['p'+pdata][self._pore_map]
|
|
227
|
+
except:
|
|
228
|
+
logger.warning('Could not add pore data: '+pdata+' to network')
|
|
229
|
+
pass
|
|
230
|
+
else:
|
|
231
|
+
try:
|
|
232
|
+
self['pore.'+xpdata]=self._dictionary['p'+xpdata][self._pore_map]
|
|
233
|
+
except:
|
|
234
|
+
logger.warning('Could not add pore data: '+xpdata+' to network')
|
|
235
|
+
pass
|
|
236
|
+
|
|
237
|
+
def _add_xtra_throat_data(self):
|
|
238
|
+
xtdata = self._xtra_throat_data
|
|
239
|
+
if xtdata is not None:
|
|
240
|
+
if type(xtdata) is type([]):
|
|
241
|
+
for tdata in xtdata:
|
|
242
|
+
try:
|
|
243
|
+
self['throat.'+tdata]=self._dictionary['t'+tdata][self._throat_map]
|
|
244
|
+
except:
|
|
245
|
+
logger.warning('Could not add throat data: '+tdata+' to network')
|
|
246
|
+
pass
|
|
247
|
+
else:
|
|
248
|
+
try:
|
|
249
|
+
self['throat.'+xtdata]=self._dictionary['t'+xtdata][self._throat_map]
|
|
250
|
+
except:
|
|
251
|
+
logger.warning('Could not add throat data: '+xtdata+' to network')
|
|
252
|
+
pass
|
|
253
|
+
|
|
254
|
+
def domain_length(self,face_1,face_2):
|
|
255
|
+
r'''
|
|
256
|
+
Calculate the distance between two faces of the network
|
|
257
|
+
|
|
258
|
+
Parameters
|
|
259
|
+
----------
|
|
260
|
+
face_1 and face_2 : array_like
|
|
261
|
+
Lists of pores belonging to opposite faces of the network
|
|
262
|
+
|
|
263
|
+
Returns
|
|
264
|
+
-------
|
|
265
|
+
The length of the domain in the specified direction
|
|
266
|
+
|
|
267
|
+
Notes
|
|
268
|
+
-----
|
|
269
|
+
- Does not yet check if input faces are perpendicular to each other
|
|
270
|
+
'''
|
|
271
|
+
#Ensure given points are coplanar before proceeding
|
|
272
|
+
if misc.iscoplanar(self['pore.coords'][face_1]) and misc.iscoplanar(self['pore.coords'][face_2]):
|
|
273
|
+
#Find distance between given faces
|
|
274
|
+
x = self['pore.coords'][face_1]
|
|
275
|
+
y = self['pore.coords'][face_2]
|
|
276
|
+
Ds = misc.dist(x,y)
|
|
277
|
+
L = sp.median(sp.amin(Ds,axis=0))
|
|
278
|
+
else:
|
|
279
|
+
logger.warning('The supplied pores are not coplanar. Length will be approximate.')
|
|
280
|
+
f1 = self['pore.coords'][face_1]
|
|
281
|
+
f2 = self['pore.coords'][face_2]
|
|
282
|
+
distavg = [0,0,0]
|
|
283
|
+
distavg[0] = sp.absolute(sp.average(f1[:,0]) - sp.average(f2[:,0]))
|
|
284
|
+
distavg[1] = sp.absolute(sp.average(f1[:,1]) - sp.average(f2[:,1]))
|
|
285
|
+
distavg[2] = sp.absolute(sp.average(f1[:,2]) - sp.average(f2[:,2]))
|
|
286
|
+
L = max(distavg)
|
|
287
|
+
return L
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def domain_area(self,face):
|
|
291
|
+
r'''
|
|
292
|
+
Calculate the area of a given network face
|
|
293
|
+
|
|
294
|
+
Parameters
|
|
295
|
+
----------
|
|
296
|
+
face : array_like
|
|
297
|
+
List of pores of pore defining the face of interest
|
|
298
|
+
|
|
299
|
+
Returns
|
|
300
|
+
-------
|
|
301
|
+
The area of the specified face
|
|
302
|
+
'''
|
|
303
|
+
coords = self['pore.coords'][face]
|
|
304
|
+
rads = self['pore.diameter'][face]/2.
|
|
305
|
+
# calculate the area of the 3 principle faces of the bounding cuboid
|
|
306
|
+
dx = max(coords[:,0]+rads) - min(coords[:,0]-rads)
|
|
307
|
+
dy = max(coords[:,1]+rads) - min(coords[:,1]-rads)
|
|
308
|
+
dz = max(coords[:,2]+rads) - min(coords[:,2]-rads)
|
|
309
|
+
yz = dy*dz # x normal
|
|
310
|
+
xz = dx*dz # y normal
|
|
311
|
+
xy = dx*dy # z normal
|
|
312
|
+
# find the directions parallel to the plane
|
|
313
|
+
directions = sp.where([yz,xz,xy]!=max([yz,xz,xy]))[0]
|
|
314
|
+
try:
|
|
315
|
+
# now, use the whole network to do the area calculation
|
|
316
|
+
coords = self['pore.coords']
|
|
317
|
+
rads = self['pore.diameter']/2.
|
|
318
|
+
d0 = (max(coords[:,directions[0]]+rads) - min(coords[:,directions[0]]-rads))
|
|
319
|
+
d1 = (max(coords[:,directions[1]]+rads) - min(coords[:,directions[1]]-rads))
|
|
320
|
+
A = d0*d1
|
|
321
|
+
except:
|
|
322
|
+
# if that fails, use the max face area of the bounding cuboid
|
|
323
|
+
A = max([yz,xz,xy])
|
|
324
|
+
if not misc.iscoplanar(self['pore.coords'][face]):
|
|
325
|
+
logger.warning('The supplied pores are not coplanar. Area will be approximate')
|
|
326
|
+
pass
|
|
327
|
+
return A
|
|
328
|
+
|
|
329
|
+
if __name__ == '__main__':
|
|
330
|
+
import doctest
|
|
331
|
+
doctest.testmod(verbose=True)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
TestNet: Generate simple cubic network for testing purposes
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import scipy as sp
|
|
9
|
+
from OpenPNM.Network import GenericNetwork
|
|
10
|
+
from OpenPNM.Base import logging
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
class TestNet(GenericNetwork):
|
|
14
|
+
r"""
|
|
15
|
+
A small cubic network for quick testing purposes
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
This class accepts no arguments
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self,**kwargs):
|
|
24
|
+
super(TestNet, self).__init__(**kwargs)
|
|
25
|
+
self.generate()
|
|
26
|
+
|
|
27
|
+
def generate(self):
|
|
28
|
+
'''
|
|
29
|
+
Create test network, of cubic geometry [5,5,5]
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
This network type accepts no arguments
|
|
34
|
+
'''
|
|
35
|
+
self._generate_setup()
|
|
36
|
+
self._generate_pores()
|
|
37
|
+
self._generate_throats()
|
|
38
|
+
self._add_labels()
|
|
39
|
+
return self
|
|
40
|
+
|
|
41
|
+
def _generate_setup(self):
|
|
42
|
+
r"""
|
|
43
|
+
Perform applicable preliminary checks and calculations required for generation
|
|
44
|
+
"""
|
|
45
|
+
self._Nx = 5
|
|
46
|
+
self._Ny = 5
|
|
47
|
+
self._Nz = 5
|
|
48
|
+
self._Lc = 1
|
|
49
|
+
self._Lx = sp.float16(self._Nx*self._Lc)
|
|
50
|
+
self._Ly = sp.float16(self._Ny*self._Lc)
|
|
51
|
+
self._Lz = sp.float16(self._Nz*self._Lc)
|
|
52
|
+
|
|
53
|
+
def _generate_pores(self):
|
|
54
|
+
r"""
|
|
55
|
+
Generate the pores (coordinates, numbering and types)
|
|
56
|
+
"""
|
|
57
|
+
Nx = self._Nx
|
|
58
|
+
Ny = self._Ny
|
|
59
|
+
Nz = self._Nz
|
|
60
|
+
Lc = self._Lc
|
|
61
|
+
Np = Nx*Ny*Nz
|
|
62
|
+
ind = sp.arange(0,Np)
|
|
63
|
+
self['pore.all'] = sp.ones_like(ind,dtype=bool)
|
|
64
|
+
pore_coords = Lc/2+Lc*sp.array(sp.unravel_index(ind, dims=(Nx, Ny, Nz), order='F'),dtype=sp.float64).T
|
|
65
|
+
self['pore.coords'] = pore_coords
|
|
66
|
+
|
|
67
|
+
def _generate_throats(self):
|
|
68
|
+
r"""
|
|
69
|
+
Generate the throats (connections, numbering and types)
|
|
70
|
+
"""
|
|
71
|
+
Nx = self._Nx
|
|
72
|
+
Ny = self._Ny
|
|
73
|
+
Nz = self._Nz
|
|
74
|
+
Np = Nx*Ny*Nz
|
|
75
|
+
ind = sp.arange(0,Np)
|
|
76
|
+
#Generate throats based on pattern of the adjacency matrix
|
|
77
|
+
tpore1_1 = ind[(ind%Nx)<(Nx-1)]
|
|
78
|
+
tpore2_1 = tpore1_1 + 1
|
|
79
|
+
tpore1_2 = ind[(ind%(Nx*Ny))<(Nx*(Ny-1))]
|
|
80
|
+
tpore2_2 = tpore1_2 + Nx
|
|
81
|
+
tpore1_3 = ind[(ind%Np)<(Nx*Ny*(Nz-1))]
|
|
82
|
+
tpore2_3 = tpore1_3 + Nx*Ny
|
|
83
|
+
tpore1 = sp.hstack((tpore1_1,tpore1_2,tpore1_3))
|
|
84
|
+
tpore2 = sp.hstack((tpore2_1,tpore2_2,tpore2_3))
|
|
85
|
+
connections = sp.vstack((tpore1,tpore2)).T
|
|
86
|
+
connections = connections[sp.lexsort((connections[:, 1], connections[:, 0]))]
|
|
87
|
+
self['throat.all'] = sp.ones_like(sp.arange(0,sp.shape(tpore1)[0]),dtype=bool)
|
|
88
|
+
self['throat.conns'] = connections
|
|
89
|
+
|
|
90
|
+
def _add_labels(self):
|
|
91
|
+
coords = self['pore.coords']
|
|
92
|
+
self['pore.front'] = self.tomask(coords[:,0]<=self._Lc)
|
|
93
|
+
self['pore.left'] = self.tomask(coords[:,1]<=self._Lc)
|
|
94
|
+
self['pore.bottom'] = self.tomask(coords[:,2]<=self._Lc)
|
|
95
|
+
self['pore.back'] = self.tomask(coords[:,0]>=(self._Lc*(self._Nx-1)))
|
|
96
|
+
self['pore.right'] = self.tomask(coords[:,1]>=(self._Lc*(self._Ny-1)))
|
|
97
|
+
self['pore.top'] = self.tomask(coords[:,2]>=(self._Lc*(self._Nz-1)))
|
|
98
|
+
for item in ['top','bottom','left','right','front','back']:
|
|
99
|
+
ps = self.pores(item)
|
|
100
|
+
ts = self.find_neighbor_throats(ps)
|
|
101
|
+
ps = self.find_connected_pores(ts)
|
|
102
|
+
ps0 = self['pore.'+item][ps[:,0]]
|
|
103
|
+
ps1 = self['pore.'+item][ps[:,1]]
|
|
104
|
+
ts = ts[ps1*ps0]
|
|
105
|
+
self['throat.'+item] = self.tomask(throats=ts)
|
|
106
|
+
if __name__ == '__main__':
|
|
107
|
+
import OpenPNM
|
|
108
|
+
pn = OpenPNM.Network.TestNet()
|
|
109
|
+
print(pn.name)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
###############################################################################
|
|
3
|
+
:mod:`OpenPNM.Network`: Classes related the creation of network topology
|
|
4
|
+
###############################################################################
|
|
5
|
+
|
|
6
|
+
Contents
|
|
7
|
+
--------
|
|
8
|
+
**GenericNetwork** -- Contains many methods ` for working with the topology of the
|
|
9
|
+
networks
|
|
10
|
+
|
|
11
|
+
**Subclasses** -- Inherit from GenericNetwork, and contain additional methods for
|
|
12
|
+
actually generating topology.
|
|
13
|
+
|
|
14
|
+
Classes
|
|
15
|
+
-------
|
|
16
|
+
|
|
17
|
+
.. autoclass:: GenericNetwork
|
|
18
|
+
:members:
|
|
19
|
+
|
|
20
|
+
.. autoclass:: Cubic
|
|
21
|
+
:members:
|
|
22
|
+
|
|
23
|
+
.. autoclass:: Delaunay
|
|
24
|
+
:members:
|
|
25
|
+
|
|
26
|
+
.. autoclass:: DelaunayCubic
|
|
27
|
+
:members:
|
|
28
|
+
|
|
29
|
+
.. autoclass:: MatFile
|
|
30
|
+
:members:
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from .__GenericNetwork__ import GenericNetwork
|
|
35
|
+
from .__Cubic__ import Cubic
|
|
36
|
+
from .__Delaunay__ import Delaunay
|
|
37
|
+
from .__DelaunayCubic__ import DelaunayCubic
|
|
38
|
+
from .__MatFile__ import MatFile
|
|
39
|
+
from .__TestNet__ import TestNet
|
|
40
|
+
from . import models
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
*******************************************************************************
|
|
3
|
+
models -- Models for calculating additional network topology properties
|
|
4
|
+
*******************************************************************************
|
|
5
|
+
|
|
6
|
+
Contents
|
|
7
|
+
--------
|
|
8
|
+
This module contains methods for calculating extra topology information
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from . import pore_topology
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
pore_topology -- functions for monitoring and adjusting topology
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import scipy as _sp
|
|
8
|
+
|
|
9
|
+
def get_subscripts(network,
|
|
10
|
+
shape,
|
|
11
|
+
**kwargs):
|
|
12
|
+
r'''
|
|
13
|
+
Return the 3D subscripts (i,j,k) into the cubic network
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
shape : list
|
|
18
|
+
The (i,j,k) shape of the network in number of pores in each direction
|
|
19
|
+
|
|
20
|
+
'''
|
|
21
|
+
if network.num_pores('internal') != _sp.prod(shape):
|
|
22
|
+
print('Supplied shape does not match Network size, cannot proceed')
|
|
23
|
+
else:
|
|
24
|
+
template = _sp.atleast_3d(_sp.empty(shape))
|
|
25
|
+
a = _sp.indices(_sp.shape(template))
|
|
26
|
+
i = a[0].flatten()
|
|
27
|
+
j = a[1].flatten()
|
|
28
|
+
k = a[2].flatten()
|
|
29
|
+
ind = _sp.vstack((i,j,k)).T
|
|
30
|
+
vals = _sp.ones((network.Np,3))*_sp.nan
|
|
31
|
+
vals[network.pores('internal')] = ind
|
|
32
|
+
return vals
|
|
33
|
+
|
|
34
|
+
def adjust_spacing(network,
|
|
35
|
+
new_spacing,
|
|
36
|
+
**kwargs):
|
|
37
|
+
r'''
|
|
38
|
+
Adjust the the pore-to-pore lattice spacing on a cubic network
|
|
39
|
+
|
|
40
|
+
Parameters
|
|
41
|
+
----------
|
|
42
|
+
new_spacing : float
|
|
43
|
+
The new lattice spacing to apply
|
|
44
|
+
|
|
45
|
+
Notes
|
|
46
|
+
-----
|
|
47
|
+
At present this method only applies a uniform spacing in all directions.
|
|
48
|
+
This is a limiation of OpenPNM Cubic Networks in general, and not of the
|
|
49
|
+
method.
|
|
50
|
+
'''
|
|
51
|
+
coords = network['pore.coords']
|
|
52
|
+
try:
|
|
53
|
+
spacing = network._spacing
|
|
54
|
+
coords = coords/spacing*new_spacing
|
|
55
|
+
network._spacing = new_spacing
|
|
56
|
+
except:
|
|
57
|
+
pass
|
|
58
|
+
return coords
|
|
59
|
+
|
|
60
|
+
def reduce_coordination(network,
|
|
61
|
+
z,
|
|
62
|
+
mode='random',
|
|
63
|
+
**kwargs):
|
|
64
|
+
r'''
|
|
65
|
+
Reduce the coordination number to the specified z value
|
|
66
|
+
|
|
67
|
+
Parameters
|
|
68
|
+
----------
|
|
69
|
+
z : int
|
|
70
|
+
The coordination number or number of throats connected a pore
|
|
71
|
+
|
|
72
|
+
mode : string, optional
|
|
73
|
+
Controls the logic used to trim connections. Options are:
|
|
74
|
+
|
|
75
|
+
- 'random': (default) Throats will be randomly removed to achieve a coordination of z
|
|
76
|
+
- 'max': All pores will be adjusted to have a maximum coordination of z (not implemented yet)
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
A label array indicating which throats should be trimmed to achieve desired
|
|
81
|
+
coordination.
|
|
82
|
+
|
|
83
|
+
Notes
|
|
84
|
+
-----
|
|
85
|
+
Pores with only 1 throat will be ignored in all calculations since these
|
|
86
|
+
are generally boundary pores.
|
|
87
|
+
|
|
88
|
+
'''
|
|
89
|
+
T_trim = ~network['throat.all']
|
|
90
|
+
T_nums = network.num_neighbors(network.pores())
|
|
91
|
+
#Find protected throats
|
|
92
|
+
T_keep = network.find_neighbor_throats(pores=(T_nums==1))
|
|
93
|
+
if mode == 'random':
|
|
94
|
+
z_ave = _sp.average(T_nums[T_nums>1])
|
|
95
|
+
f_trim = (z_ave - z)/z_ave
|
|
96
|
+
T_trim = _sp.rand(network.Nt)<f_trim
|
|
97
|
+
T_trim = T_trim*(~network.tomask(throats=T_keep))
|
|
98
|
+
if mode == 'max':
|
|
99
|
+
pass
|
|
100
|
+
return T_trim
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
from OpenPNM.Phases import GenericPhase
|
|
3
|
+
from OpenPNM.Phases import models as fm
|
|
4
|
+
|
|
5
|
+
class Air(GenericPhase):
|
|
6
|
+
r"""
|
|
7
|
+
Creates Phase object with preset models and values for air
|
|
8
|
+
|
|
9
|
+
Parameters
|
|
10
|
+
----------
|
|
11
|
+
network : OpenPNM Network object
|
|
12
|
+
The network to which this phase object will be attached.
|
|
13
|
+
|
|
14
|
+
Notes
|
|
15
|
+
-----
|
|
16
|
+
The initial properties are all at std conditions of T = 298 K and P = 1 atm.
|
|
17
|
+
|
|
18
|
+
References
|
|
19
|
+
----------
|
|
20
|
+
[1] E.W. Lemmon and R.T. Jacobsen, "Viscosity and Thermal Conductivity
|
|
21
|
+
Equations for Nitrogen, Oxygen, Argon, and Air", Int. J. of Thermophysics,
|
|
22
|
+
Vol. 25, No. 1, January 2004, pp. 21-69
|
|
23
|
+
|
|
24
|
+
Examples
|
|
25
|
+
--------
|
|
26
|
+
>>> import OpenPNM
|
|
27
|
+
>>> pn = OpenPNM.Network.TestNet()
|
|
28
|
+
>>> air = OpenPNM.Phases.Air(network=pn)
|
|
29
|
+
|
|
30
|
+
"""
|
|
31
|
+
def __init__(self,name=None,**kwargs):
|
|
32
|
+
super(Air,self).__init__(name=name,**kwargs)
|
|
33
|
+
self._generate()
|
|
34
|
+
|
|
35
|
+
def _generate(self):
|
|
36
|
+
self['pore.molecular_weight'] = 0.02896 # kg/mol
|
|
37
|
+
self['pore.critical_pressure'] = 3.786E6 # Pa
|
|
38
|
+
self['pore.critical_temperature'] = 132.5 # K
|
|
39
|
+
self['pore.critical_volume'] = 0.002917 # kg/m3
|
|
40
|
+
self['pore.contact_angle'] = 110.0 # Degree
|
|
41
|
+
self.models.add(propname='pore.density',
|
|
42
|
+
model=fm.density.ideal_gas) # kg/m3
|
|
43
|
+
self.models.add(propname='pore.molar_density',
|
|
44
|
+
model=fm.molar_density.ideal_gas) # mol/m3
|
|
45
|
+
self.models.add(propname='pore.diffusivity',
|
|
46
|
+
model=fm.diffusivity.fuller,
|
|
47
|
+
MA=0.032,
|
|
48
|
+
MB=0.028,
|
|
49
|
+
vA=16.6,
|
|
50
|
+
vB=17.9)
|
|
51
|
+
self.models.add(propname='pore.thermal_conductivity', # W/m.K
|
|
52
|
+
model=fm.misc.polynomial,
|
|
53
|
+
poreprop='pore.temperature',
|
|
54
|
+
a=[0.00422791,0.0000789606,-1.56383E-08])
|
|
55
|
+
self.models.add(propname='pore.viscosity', # kg/m.s
|
|
56
|
+
model=fm.misc.polynomial,
|
|
57
|
+
poreprop='pore.temperature',
|
|
58
|
+
a=[0.00000182082,6.51815E-08,-3.48553E-11,1.11409E-14])
|
|
59
|
+
|
|
60
|
+
if __name__ =="__main__":
|
|
61
|
+
import OpenPNM
|
|
62
|
+
pn = OpenPNM.Network.TestNet()
|
|
63
|
+
air = OpenPNM.Phases.Air(network=pn)
|