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,27 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- throat_vector
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import scipy as _sp
|
|
8
|
+
|
|
9
|
+
def pore_to_pore(geometry,network,**kwargs):
|
|
10
|
+
r"""
|
|
11
|
+
Calculates throat vector as straight path between connected pores.
|
|
12
|
+
|
|
13
|
+
Notes
|
|
14
|
+
-----
|
|
15
|
+
There is an important impicit assumption here: the positive direction is
|
|
16
|
+
taken as the direction from the pore with the lower index to the higher.
|
|
17
|
+
This corresponds to the pores in the 1st and 2nd columns of the
|
|
18
|
+
'conns' array as stored on the network.
|
|
19
|
+
"""
|
|
20
|
+
throats = network.throats(geometry.name)
|
|
21
|
+
pores = network.find_connected_pores(throats,flatten=False)
|
|
22
|
+
C0 = network['pore.coords'][pores,0]
|
|
23
|
+
C1 = network['pore.coords'][pores,1]
|
|
24
|
+
V = C1 - C0
|
|
25
|
+
L = _sp.array(_sp.sqrt(_sp.sum(V[:,:]**2,axis=1)),ndmin=1)
|
|
26
|
+
value = V/_sp.array(L,ndmin=2).T
|
|
27
|
+
return value
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
throat_vertices -- update throat vertices from Vornoi object
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import scipy as _sp
|
|
8
|
+
|
|
9
|
+
def voronoi(network,
|
|
10
|
+
geometry,
|
|
11
|
+
**kwargs):
|
|
12
|
+
r"""
|
|
13
|
+
Update the pore vertices from the voronoi vertices
|
|
14
|
+
"""
|
|
15
|
+
throats = geometry.map_throats(network,geometry.throats())
|
|
16
|
+
value = _sp.ndarray(len(throats),dtype=object)
|
|
17
|
+
for i in range(len(throats)):
|
|
18
|
+
value[i]=_sp.asarray(list(network["throat.vert_index"][throats[i]].values()))
|
|
19
|
+
return value
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
Submodule -- throat_volume
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import scipy as _sp
|
|
8
|
+
|
|
9
|
+
def cylinder(geometry,
|
|
10
|
+
throat_length='throat.length',
|
|
11
|
+
throat_diameter='throat.diameter',
|
|
12
|
+
**kwargs):
|
|
13
|
+
r"""
|
|
14
|
+
Calculate throat diameter from seeds for a cylindrical throat
|
|
15
|
+
- note: this will need to account for volume taken up by spherical pore bodies
|
|
16
|
+
"""
|
|
17
|
+
leng = geometry[throat_length]
|
|
18
|
+
diam = geometry[throat_diameter]
|
|
19
|
+
value = _sp.pi/4*leng*diam**2
|
|
20
|
+
return value
|
|
21
|
+
|
|
22
|
+
def cuboid(geometry,
|
|
23
|
+
throat_length='throat.length',
|
|
24
|
+
throat_diameter='throat.diameter',
|
|
25
|
+
**kwargs):
|
|
26
|
+
r"""
|
|
27
|
+
Calculate throat volume of cuboidal throat
|
|
28
|
+
- note: this will need to account for volume taken up by spherical pore bodies
|
|
29
|
+
"""
|
|
30
|
+
leng = geometry[throat_length]
|
|
31
|
+
diam = geometry[throat_diameter]
|
|
32
|
+
value = leng*diam**2
|
|
33
|
+
return value
|
|
34
|
+
|
|
35
|
+
def extrusion(geometry,
|
|
36
|
+
throat_length='throat.length',
|
|
37
|
+
throat_area='throat.area',
|
|
38
|
+
**kwargs):
|
|
39
|
+
r"""
|
|
40
|
+
Calculate volume from the throat area and the throat length
|
|
41
|
+
"""
|
|
42
|
+
leng = geometry[throat_length]
|
|
43
|
+
area = geometry[throat_area]
|
|
44
|
+
value = leng*area
|
|
45
|
+
return value
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
===============================================================================
|
|
4
|
+
Cubic: Generate lattice-like networks
|
|
5
|
+
===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
import numpy as np
|
|
9
|
+
import scipy as sp
|
|
10
|
+
import OpenPNM.Utilities.misc as misc
|
|
11
|
+
from OpenPNM.Network import GenericNetwork
|
|
12
|
+
from OpenPNM.Base import logging
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class Cubic(GenericNetwork):
|
|
16
|
+
r"""
|
|
17
|
+
This class generates a cubic network of the specified size and shape.
|
|
18
|
+
Alternatively, an arbitrary domain shape defined by a supplied template.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
name : string
|
|
23
|
+
A unique name for the network
|
|
24
|
+
|
|
25
|
+
shape : tuple of ints
|
|
26
|
+
The (i,j,k) size and shape of the network.
|
|
27
|
+
|
|
28
|
+
connectivity : int
|
|
29
|
+
The number of connections to neighboring pores. Connections are made
|
|
30
|
+
symmetrically to any combination of face, edge or corners neighbors.
|
|
31
|
+
|
|
32
|
+
Options are:
|
|
33
|
+
|
|
34
|
+
- 6: Faces only
|
|
35
|
+
- 8: Corners only
|
|
36
|
+
- 12: Edges Only
|
|
37
|
+
- 14: Faces and Corners
|
|
38
|
+
- 18: Faces and Edges
|
|
39
|
+
- 20: Edges and Corners
|
|
40
|
+
- 26: Faces, Edges and Corners
|
|
41
|
+
|
|
42
|
+
template : array of booleans
|
|
43
|
+
An (i,j,k) array with True where the Network should be defiend and
|
|
44
|
+
False elsewhere. This approach is useful for creating networks of non-
|
|
45
|
+
cuboid shape like spheres or cylinders, but still with a cubic lattice
|
|
46
|
+
topology.
|
|
47
|
+
|
|
48
|
+
Examples
|
|
49
|
+
--------
|
|
50
|
+
>>> import OpenPNM
|
|
51
|
+
>>> pn = OpenPNM.Network.Cubic(shape=[3,4,5])
|
|
52
|
+
>>> pn.Np
|
|
53
|
+
60
|
|
54
|
+
|
|
55
|
+
It is also possible to create Networks with cubic connectivity but
|
|
56
|
+
non-Cubic shape by provding an array with True values where the network
|
|
57
|
+
should exist to the ``template`` argument. The example below produces a sphere:
|
|
58
|
+
|
|
59
|
+
>>> img = sp.ones([11,11,11])
|
|
60
|
+
>>> img[5,5,5] = 0
|
|
61
|
+
>>> from scipy.ndimage import distance_transform_bf as dt
|
|
62
|
+
>>> img = dt(img) < 5 # Create a sphere of True
|
|
63
|
+
>>> pn = OpenPNM.Network.Cubic(template=img)
|
|
64
|
+
>>> pn.Np
|
|
65
|
+
485
|
|
66
|
+
|
|
67
|
+
If random distributions of coordination number is desired, one option is
|
|
68
|
+
to create a Cubic network with many connections and the trim some:
|
|
69
|
+
|
|
70
|
+
>>> pn = OpenPNM.Network.Cubic(shape=[5,5,5],connectivity=26)
|
|
71
|
+
>>> Nt_original = pn.Nt
|
|
72
|
+
>>> mod = OpenPNM.Network.models.pore_topology.reduce_coordination
|
|
73
|
+
>>> pn.add_model(propname='throat.to_drop',model=mod,z=10,mode='random')
|
|
74
|
+
>>> pn.trim(throats=pn['throat.to_drop'])
|
|
75
|
+
>>> pn.Nt < Nt_original
|
|
76
|
+
True
|
|
77
|
+
"""
|
|
78
|
+
def __init__(self, shape=None, template=None, spacing=1, connectivity=6, **kwargs):
|
|
79
|
+
super(Cubic, self).__init__(**kwargs)
|
|
80
|
+
|
|
81
|
+
if shape is not None:
|
|
82
|
+
arr = np.atleast_3d(np.empty(shape))
|
|
83
|
+
elif template is not None:
|
|
84
|
+
arr = sp.array(template,ndmin=3,dtype=bool)
|
|
85
|
+
else:
|
|
86
|
+
arr = np.atleast_3d(np.empty([1,1,1]))
|
|
87
|
+
|
|
88
|
+
self._shape = sp.shape(arr) # Store original network shape
|
|
89
|
+
self._spacing = spacing # Store network spacing instead of calculating it
|
|
90
|
+
|
|
91
|
+
points = np.array([i for i,v in np.ndenumerate(arr)], dtype=float)
|
|
92
|
+
points += 0.5
|
|
93
|
+
points *= spacing
|
|
94
|
+
|
|
95
|
+
I = np.arange(arr.size).reshape(arr.shape)
|
|
96
|
+
|
|
97
|
+
face_joints = [
|
|
98
|
+
(I[:,:,:-1], I[:,:,1:]),
|
|
99
|
+
(I[:,:-1], I[:,1:]),
|
|
100
|
+
(I[:-1], I[1:]),
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
corner_joints = [
|
|
104
|
+
(I[:-1,:-1,:-1], I[1:,1:,1:]),
|
|
105
|
+
(I[:-1,:-1,1:], I[1:,1:,:-1]),
|
|
106
|
+
(I[:-1,1:,:-1], I[1:,:-1,1:]),
|
|
107
|
+
(I[1:,:-1,:-1], I[:-1,1:,1:]),
|
|
108
|
+
]
|
|
109
|
+
|
|
110
|
+
edge_joints = [
|
|
111
|
+
(I[:,:-1,:-1], I[:,1:,1:]),
|
|
112
|
+
(I[:,:-1,1:], I[:,1:,:-1]),
|
|
113
|
+
(I[:-1,:,:-1], I[1:,:,1:]),
|
|
114
|
+
(I[1:,:,:-1], I[:-1,:,1:]),
|
|
115
|
+
(I[1:,1:,:], I[:-1,:-1,:]),
|
|
116
|
+
(I[1:,:-1,:], I[:-1,1:,:]),
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
if connectivity == 6:
|
|
120
|
+
joints = face_joints
|
|
121
|
+
elif connectivity == 8:
|
|
122
|
+
joints = corner_joints
|
|
123
|
+
elif connectivity == 12:
|
|
124
|
+
joints = corner_joints
|
|
125
|
+
elif connectivity == 14:
|
|
126
|
+
joints = face_joints + corner_joints
|
|
127
|
+
elif connectivity == 18:
|
|
128
|
+
joints = face_joints + edge_joints
|
|
129
|
+
elif connectivity == 20:
|
|
130
|
+
joints = edge_joints + corner_joints
|
|
131
|
+
elif connectivity == 26:
|
|
132
|
+
joints = face_joints + corner_joints + edge_joints
|
|
133
|
+
else:
|
|
134
|
+
raise Exception('Invalid connectivity receieved. Must be 6, 8, 12, 14, 18, 20 or 26')
|
|
135
|
+
|
|
136
|
+
I = np.arange(arr.size).reshape(arr.shape)
|
|
137
|
+
tails, heads = [], []
|
|
138
|
+
for T,H in joints:
|
|
139
|
+
tails.extend(T.flat)
|
|
140
|
+
heads.extend(H.flat)
|
|
141
|
+
pairs = np.vstack([tails, heads]).T
|
|
142
|
+
|
|
143
|
+
self['pore.coords'] = points
|
|
144
|
+
self['throat.conns'] = pairs
|
|
145
|
+
self['pore.all'] = np.ones(len(self['pore.coords']), dtype=bool)
|
|
146
|
+
self['throat.all'] = np.ones(len(self['throat.conns']), dtype=bool)
|
|
147
|
+
self['pore.index'] = sp.arange(0,len(self['pore.coords']))
|
|
148
|
+
|
|
149
|
+
x,y,z = self['pore.coords'].T
|
|
150
|
+
self['pore.internal'] = self['pore.all']
|
|
151
|
+
self['pore.front'] = x <= x.min()
|
|
152
|
+
self['pore.back'] = x >= x.max()
|
|
153
|
+
self['pore.left'] = y <= y.min()
|
|
154
|
+
self['pore.right'] = y >= y.max()
|
|
155
|
+
self['pore.bottom'] = z <= z.min()
|
|
156
|
+
self['pore.top'] = z >= z.max()
|
|
157
|
+
|
|
158
|
+
#If an image was sent as 'template', then trim network to image shape
|
|
159
|
+
if template is not None:
|
|
160
|
+
self.trim(~arr.flatten())
|
|
161
|
+
|
|
162
|
+
def add_boundaries(self):
|
|
163
|
+
r'''
|
|
164
|
+
This method uses ``clone_pores`` to clone the surface pores (labeled
|
|
165
|
+
'left','right', etc), then shifts them to the periphery of the domain,
|
|
166
|
+
and gives them the label 'right_face', 'left_face', etc.
|
|
167
|
+
'''
|
|
168
|
+
x,y,z = self['pore.coords'].T
|
|
169
|
+
|
|
170
|
+
Lc = sp.amax(sp.diff(x)) #this currently works but is very fragile
|
|
171
|
+
|
|
172
|
+
offset = {}
|
|
173
|
+
offset['front'] = offset['left'] = offset['bottom'] = [0,0,0]
|
|
174
|
+
offset['back'] = [x.max()+Lc/2,0,0]
|
|
175
|
+
offset['right'] = [0,y.max()+Lc/2,0]
|
|
176
|
+
offset['top'] = [0,0,z.max()+Lc/2]
|
|
177
|
+
|
|
178
|
+
scale = {}
|
|
179
|
+
scale['front'] = scale['back'] = [0,1,1]
|
|
180
|
+
scale['left'] = scale['right'] = [1,0,1]
|
|
181
|
+
scale['bottom'] = scale['top'] = [1,1,0]
|
|
182
|
+
|
|
183
|
+
for label in ['front','back','left','right','bottom','top']:
|
|
184
|
+
ps = self.pores(label)
|
|
185
|
+
self.clone_pores(pores=ps,apply_label=[label+'_boundary','boundary'])
|
|
186
|
+
#Translate cloned pores
|
|
187
|
+
ind = self.pores(label+'_boundary')
|
|
188
|
+
coords = self['pore.coords'][ind]
|
|
189
|
+
coords = coords*scale[label] + offset[label]
|
|
190
|
+
self['pore.coords'][ind] = coords
|
|
191
|
+
|
|
192
|
+
def asarray(self,values):
|
|
193
|
+
r'''
|
|
194
|
+
Retreive values as a rectangular array, rather than the OpenPNM list format
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
values : array_like
|
|
199
|
+
The values from the network (in a list) to insert into the array
|
|
200
|
+
|
|
201
|
+
Notes
|
|
202
|
+
-----
|
|
203
|
+
This method can break on networks that have had boundaries added. It
|
|
204
|
+
will usually work IF the list of values came only from 'internal' pores.
|
|
205
|
+
'''
|
|
206
|
+
if sp.shape(values)[0] > self.num_pores('internal'):
|
|
207
|
+
raise Exception('The received values are bigger than the original network')
|
|
208
|
+
Ps = sp.array(self['pore.index'][self.pores('internal')],dtype=int)
|
|
209
|
+
arr = sp.ones(self._shape)*sp.nan
|
|
210
|
+
ind = sp.unravel_index(Ps,self._shape)
|
|
211
|
+
arr[ind[0],ind[1],ind[2]] = values
|
|
212
|
+
return arr
|
|
213
|
+
|
|
214
|
+
def fromarray(self,array,propname):
|
|
215
|
+
r'''
|
|
216
|
+
Apply data to the network based on a rectangular array filled with
|
|
217
|
+
values. Each array location corresponds to a pore in the network.
|
|
218
|
+
|
|
219
|
+
Parameters
|
|
220
|
+
----------
|
|
221
|
+
array : array_like
|
|
222
|
+
The rectangular array containing the values to be added to the
|
|
223
|
+
network. This array must be the same shape as the original network.
|
|
224
|
+
|
|
225
|
+
propname : string
|
|
226
|
+
The name of the pore property being added.
|
|
227
|
+
'''
|
|
228
|
+
array = sp.atleast_3d(array)
|
|
229
|
+
if sp.shape(array) != self._shape:
|
|
230
|
+
raise Exception('The received array does not match the original network')
|
|
231
|
+
temp = array.flatten()
|
|
232
|
+
Ps = sp.array(self['pore.index'][self.pores('internal')],dtype=int)
|
|
233
|
+
propname = 'pore.' + propname.split('.')[-1]
|
|
234
|
+
self[propname] = sp.nan
|
|
235
|
+
self[propname][self.pores('internal')] = temp[Ps]
|
|
236
|
+
|
|
237
|
+
def domain_length(self,face_1,face_2):
|
|
238
|
+
r'''
|
|
239
|
+
Calculate the distance between two faces of the network
|
|
240
|
+
|
|
241
|
+
Parameters
|
|
242
|
+
----------
|
|
243
|
+
face_1 and face_2 : array_like
|
|
244
|
+
Lists of pores belonging to opposite faces of the network
|
|
245
|
+
|
|
246
|
+
Returns
|
|
247
|
+
-------
|
|
248
|
+
The length of the domain in the specified direction
|
|
249
|
+
|
|
250
|
+
Notes
|
|
251
|
+
-----
|
|
252
|
+
- Does not yet check if input faces are perpendicular to each other
|
|
253
|
+
'''
|
|
254
|
+
#Ensure given points are coplanar before proceeding
|
|
255
|
+
if misc.iscoplanar(self['pore.coords'][face_1]) and misc.iscoplanar(self['pore.coords'][face_2]):
|
|
256
|
+
#Find distance between given faces
|
|
257
|
+
x = self['pore.coords'][face_1]
|
|
258
|
+
y = self['pore.coords'][face_2]
|
|
259
|
+
Ds = misc.dist(x,y)
|
|
260
|
+
L = sp.median(sp.amin(Ds,axis=0))
|
|
261
|
+
else:
|
|
262
|
+
logger.warning('The supplied pores are not coplanar. Length will be approximate.')
|
|
263
|
+
f1 = self['pore.coords'][face_1]
|
|
264
|
+
f2 = self['pore.coords'][face_2]
|
|
265
|
+
distavg = [0,0,0]
|
|
266
|
+
distavg[0] = sp.absolute(sp.average(f1[:,0]) - sp.average(f2[:,0]))
|
|
267
|
+
distavg[1] = sp.absolute(sp.average(f1[:,1]) - sp.average(f2[:,1]))
|
|
268
|
+
distavg[2] = sp.absolute(sp.average(f1[:,2]) - sp.average(f2[:,2]))
|
|
269
|
+
L = max(distavg)
|
|
270
|
+
return L
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def domain_area(self,face):
|
|
274
|
+
r'''
|
|
275
|
+
Calculate the area of a given network face
|
|
276
|
+
|
|
277
|
+
Parameters
|
|
278
|
+
----------
|
|
279
|
+
face : array_like
|
|
280
|
+
List of pores of pore defining the face of interest
|
|
281
|
+
|
|
282
|
+
Returns
|
|
283
|
+
-------
|
|
284
|
+
The area of the specified face
|
|
285
|
+
'''
|
|
286
|
+
coords = self['pore.coords'][face]
|
|
287
|
+
rads = self['pore.diameter'][face]/2.
|
|
288
|
+
# calculate the area of the 3 principle faces of the bounding cuboid
|
|
289
|
+
dx = max(coords[:,0]+rads) - min(coords[:,0]-rads)
|
|
290
|
+
dy = max(coords[:,1]+rads) - min(coords[:,1]-rads)
|
|
291
|
+
dz = max(coords[:,2]+rads) - min(coords[:,2]-rads)
|
|
292
|
+
yz = dy*dz # x normal
|
|
293
|
+
xz = dx*dz # y normal
|
|
294
|
+
xy = dx*dy # z normal
|
|
295
|
+
# find the directions parallel to the plane
|
|
296
|
+
directions = sp.where([yz,xz,xy]!=max([yz,xz,xy]))[0]
|
|
297
|
+
try:
|
|
298
|
+
# now, use the whole network to do the area calculation
|
|
299
|
+
coords = self['pore.coords']
|
|
300
|
+
rads = self['pore.diameter']/2.
|
|
301
|
+
d0 = (max(coords[:,directions[0]]+rads) - min(coords[:,directions[0]]-rads))
|
|
302
|
+
d1 = (max(coords[:,directions[1]]+rads) - min(coords[:,directions[1]]-rads))
|
|
303
|
+
A = d0*d1
|
|
304
|
+
except:
|
|
305
|
+
# if that fails, use the max face area of the bounding cuboid
|
|
306
|
+
A = max([yz,xz,xy])
|
|
307
|
+
if not misc.iscoplanar(self['pore.coords'][face]):
|
|
308
|
+
logger.warning('The supplied pores are not coplanar. Area will be approximate')
|
|
309
|
+
pass
|
|
310
|
+
return A
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
if __name__ == '__main__':
|
|
314
|
+
import doctest
|
|
315
|
+
doctest.testmod(verbose=True)
|
|
316
|
+
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""
|
|
2
|
+
===============================================================================
|
|
3
|
+
DelaunayCubic: Generate semi-random networks based on Delaunay Tessellations and perturbed cubic lattices
|
|
4
|
+
===============================================================================
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
import OpenPNM
|
|
8
|
+
import scipy as sp
|
|
9
|
+
import sys
|
|
10
|
+
import numpy as np
|
|
11
|
+
from OpenPNM.Network.__Delaunay__ import Delaunay
|
|
12
|
+
from OpenPNM.Base import logging
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
class DelaunayCubic(Delaunay):
|
|
16
|
+
r"""
|
|
17
|
+
This class contains the methods for creating a *Delaunay* network topology
|
|
18
|
+
based connecting pores with a Delaunay tessellation.
|
|
19
|
+
|
|
20
|
+
This Subclass of Delaunay generates points on a cubic lattice and then perturbs them to prevent degeneracy
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
name : string
|
|
26
|
+
A unique name for the network
|
|
27
|
+
|
|
28
|
+
Examples
|
|
29
|
+
--------
|
|
30
|
+
>>> import OpenPNM
|
|
31
|
+
>>> pn = OpenPNM.Network.DelaunayCubic(shape=[5,5,5], spacing=[4e-5,4e-5,4e-5],jiggle_factor=0.01)
|
|
32
|
+
>>> pn.num_pores()
|
|
33
|
+
125
|
|
34
|
+
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self,shape=None, template=None, spacing=[1,1,1],jiggle_factor=0.1,arrangement='SC',**kwargs):
|
|
38
|
+
'''
|
|
39
|
+
Create Delauny network object
|
|
40
|
+
'''
|
|
41
|
+
if shape is not None:
|
|
42
|
+
self._arr = np.atleast_3d(np.empty(shape))
|
|
43
|
+
elif template is not None:
|
|
44
|
+
self._arr = sp.array(template,ndmin=3,dtype=bool)
|
|
45
|
+
else:
|
|
46
|
+
self._arr = np.atleast_3d(np.empty([3,3,3]))
|
|
47
|
+
|
|
48
|
+
self._shape = sp.shape(self._arr) # Store original network shape
|
|
49
|
+
self._spacing = sp.asarray(spacing) # Store network spacing instead of calculating it
|
|
50
|
+
self._num_pores = np.prod(np.asarray(self._shape))
|
|
51
|
+
self._domain_size = np.asarray(self._shape)*self._spacing
|
|
52
|
+
self._jiggle_factor = jiggle_factor
|
|
53
|
+
self._arrangement = arrangement
|
|
54
|
+
super(DelaunayCubic,self).__init__(num_pores=self._num_pores,domain_size=self._domain_size,**kwargs)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _generate_pores(self):
|
|
58
|
+
r"""
|
|
59
|
+
Generate the pores with numbering scheme.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
points = np.array([i for i,v in np.ndenumerate(self._arr)], dtype=float)
|
|
63
|
+
points += 0.5
|
|
64
|
+
|
|
65
|
+
"-----------------------------------------------------------------------------------"
|
|
66
|
+
"2D Orthorhombic adjustment - shift even rows back a bit and odd rows forward a bit"
|
|
67
|
+
" 0 0 0 "
|
|
68
|
+
" 0 0 0 0 "
|
|
69
|
+
" 0 0 0 "
|
|
70
|
+
if self._arrangement == 'O':
|
|
71
|
+
shift_y=np.array([0,0.25,0])
|
|
72
|
+
shift_x=np.array([0.25,0,0])
|
|
73
|
+
points[(points[:,0] % 2 == 0)] -= shift_y
|
|
74
|
+
points[(points[:,2] % 2 != 0)] -= shift_x
|
|
75
|
+
points[(points[:,0] % 2 != 0)] += shift_y
|
|
76
|
+
points[(points[:,2] % 2 == 0)] += shift_x
|
|
77
|
+
"-----------------------------------------------------------------------------------"
|
|
78
|
+
"BCC = Body Centre Cubic "
|
|
79
|
+
if self._arrangement == 'BCC':
|
|
80
|
+
body_points=[]
|
|
81
|
+
for i in range(1,self._shape[0]):
|
|
82
|
+
for j in range(1,self._shape[1]):
|
|
83
|
+
for k in range(1,self._shape[2]):
|
|
84
|
+
body_points.append([i,j,k])
|
|
85
|
+
body_points = np.asarray(body_points)
|
|
86
|
+
points = np.concatenate((points,body_points))
|
|
87
|
+
"-----------------------------------------------------------------------------------"
|
|
88
|
+
"FCC = Face Centre Cubic "
|
|
89
|
+
if self._arrangement == 'FCC':
|
|
90
|
+
face_points=[]
|
|
91
|
+
for i in range(1,self._shape[0]):
|
|
92
|
+
for j in range(1,self._shape[1]):
|
|
93
|
+
for k in range(1,self._shape[2]):
|
|
94
|
+
left = [i-0.5,j,k]
|
|
95
|
+
right = [i+0.5,j,k]
|
|
96
|
+
back = [i,j-0.5,k]
|
|
97
|
+
front = [i,j+0.5,k]
|
|
98
|
+
bottom = [i,j,k-0.5]
|
|
99
|
+
top = [i,j,k+0.5]
|
|
100
|
+
if left not in face_points:
|
|
101
|
+
face_points.append(left)
|
|
102
|
+
if right not in face_points:
|
|
103
|
+
face_points.append(right)
|
|
104
|
+
if back not in face_points:
|
|
105
|
+
face_points.append(back)
|
|
106
|
+
if front not in face_points:
|
|
107
|
+
face_points.append(front)
|
|
108
|
+
if bottom not in face_points:
|
|
109
|
+
face_points.append(bottom)
|
|
110
|
+
if top not in face_points:
|
|
111
|
+
face_points.append(top)
|
|
112
|
+
face_points = np.asarray(face_points)
|
|
113
|
+
points = np.concatenate((points,face_points))
|
|
114
|
+
|
|
115
|
+
jiggle = (np.random.rand(len(points),3)-0.5)*self._jiggle_factor
|
|
116
|
+
points += jiggle
|
|
117
|
+
points *= self._spacing
|
|
118
|
+
|
|
119
|
+
self['pore.coords'] = points
|
|
120
|
+
logger.debug(sys._getframe().f_code.co_name+": End of method")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
if __name__ == '__main__':
|
|
125
|
+
#Run doc tests
|
|
126
|
+
import doctest
|
|
127
|
+
doctest.testmod(verbose=True)
|