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,702 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
===============================================================================
|
|
4
|
+
InvasionPercolationTimed -- Invasion Percolation with Timed Injection Rates
|
|
5
|
+
===============================================================================
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
import scipy as sp
|
|
9
|
+
import numpy as np
|
|
10
|
+
import heapq
|
|
11
|
+
from OpenPNM.Utilities import misc
|
|
12
|
+
from OpenPNM.Algorithms import GenericAlgorithm
|
|
13
|
+
from OpenPNM.Base import logging
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class InvasionPercolationTimed(GenericAlgorithm):
|
|
18
|
+
r"""
|
|
19
|
+
Invasion percolation with cluster growth timing
|
|
20
|
+
|
|
21
|
+
Parameters
|
|
22
|
+
----------
|
|
23
|
+
network : Descendent of OpenPNM.Network.GenericNetwork
|
|
24
|
+
A valid network for this algorithm
|
|
25
|
+
name : string
|
|
26
|
+
The name this algorithm will go by
|
|
27
|
+
|
|
28
|
+
Examples
|
|
29
|
+
--------
|
|
30
|
+
>>> import OpenPNM
|
|
31
|
+
>>> pn = OpenPNM.Network.TestNet()
|
|
32
|
+
>>> geo = OpenPNM.Geometry.TestGeometry(network=pn,pores=pn.pores(),throats=pn.throats())
|
|
33
|
+
>>> phase1 = OpenPNM.Phases.TestPhase(network=pn)
|
|
34
|
+
>>> phase2 = OpenPNM.Phases.TestPhase(network=pn)
|
|
35
|
+
>>> phys1 = OpenPNM.Physics.TestPhysics(network=pn, phase=phase1,pores=pn.pores(),throats=pn.throats())
|
|
36
|
+
>>> phys2 = OpenPNM.Physics.TestPhysics(network=pn, phase=phase2,pores=pn.pores(),throats=pn.throats())
|
|
37
|
+
>>> IP = OpenPNM.Algorithms.InvasionPercolationTimed(network=pn)
|
|
38
|
+
>>> IP.run(invading_phase=phase1, defending_phase=phase2, inlets=pn.pores('top'), outlets=pn.pores('bottom'),report=0)
|
|
39
|
+
IP algorithm at 0 % completion at 0.0 seconds
|
|
40
|
+
IP algorithm at 100% completion at 0.0 seconds
|
|
41
|
+
>>> IP.return_results()
|
|
42
|
+
>>> max(phase1['pore.IP_inv_seq']) #unless something changed with our test objects, this should print "60"
|
|
43
|
+
60
|
|
44
|
+
|
|
45
|
+
Suggested Improvements ::
|
|
46
|
+
|
|
47
|
+
a) Allow updating of cluster flow-rates (this will require a delta-t calculation at each step, instead of a total t calculation).
|
|
48
|
+
b) Allow for a non-linear relationship between pressure and throat-cap volume.
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
"""
|
|
52
|
+
def __init__(self,**kwords):
|
|
53
|
+
r'''
|
|
54
|
+
'''
|
|
55
|
+
super(InvasionPercolationTimed,self).__init__(**kwords)
|
|
56
|
+
logger.info("Create IP Algorithm Object")
|
|
57
|
+
|
|
58
|
+
def run(self,invading_phase,
|
|
59
|
+
defending_phase,
|
|
60
|
+
inlets=[0],
|
|
61
|
+
outlets=[-1],
|
|
62
|
+
end_condition='breakthrough',
|
|
63
|
+
capillary_pressure='capillary_pressure',
|
|
64
|
+
pore_volume_name='volume',
|
|
65
|
+
throat_volume_name='volume',
|
|
66
|
+
throat_diameter_name='diameter',
|
|
67
|
+
timing='ON',
|
|
68
|
+
inlet_flow=1e-12, #default flowrate is 1 nanoliter/sec/cluster
|
|
69
|
+
report=20):
|
|
70
|
+
r"""
|
|
71
|
+
Runs the IP algorithm
|
|
72
|
+
|
|
73
|
+
Parameters
|
|
74
|
+
----------
|
|
75
|
+
invading_phase : OpenPNM Phase Object
|
|
76
|
+
phase which will displace defending phase
|
|
77
|
+
defending_phase : OpenPNM Phase Object
|
|
78
|
+
phase which will be displaced by invading phase
|
|
79
|
+
inlets : list of integers (default: [0])
|
|
80
|
+
list of inlet nodes
|
|
81
|
+
outlets : list of integers (default: [-1])
|
|
82
|
+
list of outlet nodes
|
|
83
|
+
end_condition : string('breakthrough')
|
|
84
|
+
choice between 'breakthrough' and 'total'
|
|
85
|
+
capillary_pressure : string('capillary_pressure')
|
|
86
|
+
name given to throat capillary pressure property
|
|
87
|
+
pore_volume_name : string('volume')
|
|
88
|
+
name given to pore volume property
|
|
89
|
+
throat_diameter_name : string('diameter')
|
|
90
|
+
name given to throat diameter property
|
|
91
|
+
timing : string ('ON')
|
|
92
|
+
turns volume and flowrate calculations 'ON' or 'OFF'
|
|
93
|
+
inlet_flow : float (1)
|
|
94
|
+
m3/s for each cluster (affects timestamp of pore filling)
|
|
95
|
+
report : int (20)
|
|
96
|
+
percentage multiple at which a progress report is printed
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
Returns
|
|
100
|
+
-------
|
|
101
|
+
The algorithm will aquire the following pore data ::
|
|
102
|
+
|
|
103
|
+
invaded : True for invaded, False for uninvaded
|
|
104
|
+
defended : True for uninvaded, False for invaded
|
|
105
|
+
cluster_final : 0 for uninvaded, merged cluster number for invaded
|
|
106
|
+
cluster_original : 0 for uninvaded, original cluster number for invaded
|
|
107
|
+
inv_seq : 0 for uninvaded, simulation step for invaded
|
|
108
|
+
inv_time : 0 for uninvaded, simulation time for invaded
|
|
109
|
+
inv_sat : 0 for uninvaded, simulation saturation for invaded
|
|
110
|
+
inv_pres : 0 for uninvaded, simulation pressure for invaded
|
|
111
|
+
|
|
112
|
+
and throat data ::
|
|
113
|
+
|
|
114
|
+
invaded : True for invaded, False for uninvaded
|
|
115
|
+
defended : True for uninvaded, False for invaded
|
|
116
|
+
cluster_final : 0 for uninvaded, merged cluster number for invaded
|
|
117
|
+
inv_seq : 0 for uninvaded, simulation step for invaded
|
|
118
|
+
inv_time : 0 for uninvaded, simulation time for invaded
|
|
119
|
+
inv_sat : 0 for uninvaded, simulation saturation for invaded
|
|
120
|
+
inv_Pc : throat capillary pressures
|
|
121
|
+
inv_pres : 0 for uninvaded, simulation pressure for invaded
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
logger.info("\t end condition: "+end_condition)
|
|
126
|
+
self._inlets = inlets
|
|
127
|
+
self._outlets = outlets
|
|
128
|
+
if end_condition=='total':
|
|
129
|
+
self._brkevent = []
|
|
130
|
+
self._inlet_flow = inlet_flow
|
|
131
|
+
try: self._phase = self._net._phases[invading_phase]
|
|
132
|
+
except: self._phase = invading_phase
|
|
133
|
+
try: self._phase_def = self._net._phases[defending_phase]
|
|
134
|
+
except: self._phase_def = defending_phase
|
|
135
|
+
|
|
136
|
+
if sp.size(inlets) == 1:
|
|
137
|
+
self._inlets = [inlets]
|
|
138
|
+
if sp.size(outlets) == 1:
|
|
139
|
+
self._outlets = [outlets]
|
|
140
|
+
self._end_condition = end_condition
|
|
141
|
+
self._counter = 0
|
|
142
|
+
self._condition = 1
|
|
143
|
+
self._rough_increment = report
|
|
144
|
+
if report == 0:
|
|
145
|
+
self._rough_increment = 100
|
|
146
|
+
self._timing = timing=='ON'
|
|
147
|
+
self._capillary_pressure_name = capillary_pressure
|
|
148
|
+
self._pore_volume_name = pore_volume_name
|
|
149
|
+
self._throat_volume_name = throat_volume_name
|
|
150
|
+
self._throat_diameter_name = throat_diameter_name
|
|
151
|
+
|
|
152
|
+
super(InvasionPercolationTimed,self).run()
|
|
153
|
+
|
|
154
|
+
def _setup_for_IP(self):
|
|
155
|
+
r"""
|
|
156
|
+
Determines cluster labelling and condition for completion
|
|
157
|
+
"""
|
|
158
|
+
self._clock_start = misc.tic()
|
|
159
|
+
logger.debug( '+='*25)
|
|
160
|
+
logger.debug( 'INITIAL SETUP (STEP 1)')
|
|
161
|
+
# if empty, add Pc_entry to throat_properties
|
|
162
|
+
tdia = self._net['throat.'+self._throat_diameter_name]
|
|
163
|
+
# calculate Pc_entry from diameters
|
|
164
|
+
try:
|
|
165
|
+
self['throat.inv_Pc'] = self._phase['throat.'+self._capillary_pressure_name]
|
|
166
|
+
except:
|
|
167
|
+
logger.error('Capillary pressure not assigned to invading phase '+self._phase.name
|
|
168
|
+
+', check for capillary pressure in defending phase '+self._phase_def.name +' instead')
|
|
169
|
+
try:
|
|
170
|
+
self['throat.inv_Pc'] = self._phase_def['throat.'+self._capillary_pressure_name]
|
|
171
|
+
self._phase['throat.'+self._capillary_pressure_name] = self._phase_def['throat.'+self._capillary_pressure_name]
|
|
172
|
+
except:
|
|
173
|
+
logger.error('Capillary pressure neither assigned to defending phase '+self._phase_def.name
|
|
174
|
+
+' nor to invading phase '+self._phase.name)
|
|
175
|
+
pass
|
|
176
|
+
if self._timing:
|
|
177
|
+
# calculate Volume_coef for each throat
|
|
178
|
+
self._Tvol_coef = tdia*tdia*tdia*np.pi/12/self['throat.inv_Pc']
|
|
179
|
+
# Creating an array for invaded Pores(Np long, 0 for uninvaded, cluster number for inaveded)
|
|
180
|
+
self['pore.cluster_final'] = 0
|
|
181
|
+
self['pore.cluster_original'] = 0
|
|
182
|
+
# Creating an array for invaded throats(Nt long, 0 for uninvaded, cluster number for inaveded)
|
|
183
|
+
self['throat.cluster_final'] = 0
|
|
184
|
+
# Creating arrays for tracking invaded Pores(Np long, 0 for uninvaded, sequence for inaveded)
|
|
185
|
+
self['pore.inv_seq'] =0
|
|
186
|
+
# Creating arrays for tracking invaded Pores(Np long, 0 for uninvaded, pressure for inaveded)
|
|
187
|
+
self['pore.inv_pres'] =0
|
|
188
|
+
if self._timing:
|
|
189
|
+
# Creating arrays for tracking invaded Pores(Np long, -1 for uninvaded, simulation time for inaveded)
|
|
190
|
+
self['pore.inv_time'] = -1.
|
|
191
|
+
# Creating arrays for tracking invaded throats(Nt long, 0 for uninvaded, sequence for inaveded)
|
|
192
|
+
self['throat.inv_seq'] = 0
|
|
193
|
+
# Creating arrays for tracking invaded throats(Nt long, 0 for uninvaded, pressure for inaveded)
|
|
194
|
+
self['throat.inv_pres'] = 0
|
|
195
|
+
if self._timing:
|
|
196
|
+
# Creating arrays for tracking invaded Pores(Np long, -1 for uninvaded, simulation time for inaveded)
|
|
197
|
+
self['throat.inv_time'] = -1.
|
|
198
|
+
# Iterator variables for sequences and cluster numbers
|
|
199
|
+
clusterNumber = 1
|
|
200
|
+
# Determine how many clusters there are
|
|
201
|
+
self._clusterCount = 0
|
|
202
|
+
for i in self._inlets:
|
|
203
|
+
self._clusterCount += 1
|
|
204
|
+
# Storage for cluster information
|
|
205
|
+
self._cluster_data = {}
|
|
206
|
+
if self._timing:
|
|
207
|
+
self._cluster_data['flow_rate'] = np.ones((self._clusterCount),dtype=float)*self._inlet_flow
|
|
208
|
+
self._cluster_data['haines_pressure'] = np.zeros((self._clusterCount),dtype=float)
|
|
209
|
+
self._cluster_data['haines_time'] = np.zeros((self._clusterCount),dtype=float)
|
|
210
|
+
self._cluster_data['vol_coef'] = np.zeros((self._clusterCount),dtype=float)
|
|
211
|
+
self._cluster_data['cap_volume'] = np.zeros((self._clusterCount),dtype=float)
|
|
212
|
+
self._cluster_data['pore_volume'] = np.zeros((self._clusterCount),dtype=float)
|
|
213
|
+
self._cluster_data['throat_volume'] = np.zeros((self._clusterCount),dtype=float)
|
|
214
|
+
self._cluster_data['haines_throat'] = np.zeros((self._clusterCount),dtype=int)
|
|
215
|
+
self._cluster_data['active'] = np.ones((self._clusterCount),dtype=int)
|
|
216
|
+
self._cluster_data['transform'] = np.zeros((self._clusterCount),dtype=int)
|
|
217
|
+
for i in range(self._clusterCount):
|
|
218
|
+
self._cluster_data['transform'][i] = i+1
|
|
219
|
+
# Creating an empty list to store the list of potential throats for invasion in each cluster.
|
|
220
|
+
# its length is equal to the maximum number of possible clusters.
|
|
221
|
+
self._tlists = [[] for i in self._inlets]
|
|
222
|
+
# Creating a list for each cluster to store both potential throat and corresponding throat value
|
|
223
|
+
self._tpoints = [[] for i in self._inlets]
|
|
224
|
+
# Initializing invasion percolation for each possible cluster
|
|
225
|
+
self._pore_volumes = self._net['pore.'+self._pore_volume_name]
|
|
226
|
+
self._throat_volumes = self._net['throat.'+self._throat_volume_name]
|
|
227
|
+
for pores in self._inlets:
|
|
228
|
+
if sp.shape(pores) == ():
|
|
229
|
+
pores = [pores]
|
|
230
|
+
# Label all invaded pores with their cluster
|
|
231
|
+
self['pore.cluster_original'][pores] = clusterNumber
|
|
232
|
+
# Label all inlet pores as invaded
|
|
233
|
+
self['pore.inv_seq'][pores] = self._tseq
|
|
234
|
+
self['pore.inv_pres'][pores] = 0
|
|
235
|
+
if self._timing:
|
|
236
|
+
self['pore.inv_time'][pores] = self._sim_time
|
|
237
|
+
# Find all throats that border invaded pores
|
|
238
|
+
interface_throat_numbers = self._net.find_neighbor_throats(pores)
|
|
239
|
+
self.cluster_update(clusterNumber,pores,[],interface_throat_numbers)
|
|
240
|
+
clusterNumber += 1
|
|
241
|
+
if self._timing:
|
|
242
|
+
logger.debug( 'pore volumes')
|
|
243
|
+
logger.debug(self._cluster_data['pore_volume'])
|
|
244
|
+
logger.debug( 'cap volumes')
|
|
245
|
+
logger.debug( self._cluster_data['cap_volume'])
|
|
246
|
+
pass
|
|
247
|
+
logger.debug( 'haines_throats')
|
|
248
|
+
logger.debug( self._cluster_data['haines_throat'])
|
|
249
|
+
self._tseq += 1
|
|
250
|
+
self._pseq += 1
|
|
251
|
+
self._current_cluster = 0
|
|
252
|
+
# Calculate the distance between the inlet and outlet pores
|
|
253
|
+
self._outlet_position = np.average(self._net['pore.coords'][self._outlets],0)
|
|
254
|
+
if any([sp.shape(i) > () for i in self._inlets]):
|
|
255
|
+
inlets = []
|
|
256
|
+
for i in self._inlets:
|
|
257
|
+
inlets = sp.union1d(inlets,i)
|
|
258
|
+
inlets = sp.array(inlets,int)
|
|
259
|
+
else:
|
|
260
|
+
inlets = self._inlets
|
|
261
|
+
inlet_position = np.average(self._net['pore.coords'][inlets],0)
|
|
262
|
+
dist_sqrd = (self._outlet_position-inlet_position)*(self._outlet_position-inlet_position)
|
|
263
|
+
self._initial_distance = np.sqrt(dist_sqrd[0]+dist_sqrd[1]+dist_sqrd[2])
|
|
264
|
+
logger.debug( 'initial distance')
|
|
265
|
+
logger.debug( self._initial_distance)
|
|
266
|
+
self._current_distance = self._initial_distance
|
|
267
|
+
self._percent_complete = np.round((self._initial_distance-self._current_distance)/self._initial_distance*100, decimals = 1)
|
|
268
|
+
logger.info( 'percent complete')
|
|
269
|
+
logger.info( self._percent_complete)
|
|
270
|
+
self._rough_complete = 0
|
|
271
|
+
print(' IP algorithm at',np.int(self._rough_complete),'% completion at',np.round(misc.toc(quiet=True)),'seconds')
|
|
272
|
+
logger.debug( '+='*25)
|
|
273
|
+
|
|
274
|
+
def _do_outer_iteration_stage(self):
|
|
275
|
+
r"""
|
|
276
|
+
Executes the outer iteration stage
|
|
277
|
+
"""
|
|
278
|
+
logger.info("Outer Iteration Stage ")
|
|
279
|
+
self._pseq = 1
|
|
280
|
+
self._tseq = 1
|
|
281
|
+
self._ppres = 0
|
|
282
|
+
self._tpres = 0
|
|
283
|
+
self._NewPore = -1
|
|
284
|
+
# Time keeper
|
|
285
|
+
self._sim_time = 0
|
|
286
|
+
self._setup_for_IP()
|
|
287
|
+
self._condition_update()
|
|
288
|
+
#self['throat.cluster_final'] = np.zeros(self._net.num_throats())
|
|
289
|
+
while self._condition:
|
|
290
|
+
self._do_one_outer_iteration()
|
|
291
|
+
|
|
292
|
+
#Calculate Saturations
|
|
293
|
+
v_total = sp.sum(self._net['pore.volume'])+sp.sum(self._net['throat.volume'])
|
|
294
|
+
sat = 0.
|
|
295
|
+
self['pore.inv_sat'] = 1.
|
|
296
|
+
self['throat.inv_sat'] = 1.
|
|
297
|
+
for i in range(1,self._tseq+1):
|
|
298
|
+
inv_pores = sp.where(self['pore.inv_seq']==i)[0]
|
|
299
|
+
inv_throats = sp.where(self['throat.inv_seq']==i)[0]
|
|
300
|
+
new_sat = (sum(self._pore_volumes[inv_pores])+sum(self._throat_volumes[inv_throats]))/v_total
|
|
301
|
+
sat += new_sat
|
|
302
|
+
self['pore.inv_sat'][inv_pores] = sat
|
|
303
|
+
self['throat.inv_sat'][inv_throats] = sat
|
|
304
|
+
self.sat = sat
|
|
305
|
+
|
|
306
|
+
def _do_one_outer_iteration(self):
|
|
307
|
+
r"""
|
|
308
|
+
One iteration of an outer iteration loop for an algorithm
|
|
309
|
+
(e.g. time or parametric study)
|
|
310
|
+
"""
|
|
311
|
+
if (sp.mod(self._counter,500)==False):
|
|
312
|
+
logger.info("Outer Iteration (counter = "+str(self._counter)+")")
|
|
313
|
+
pass
|
|
314
|
+
self._do_inner_iteration_stage()
|
|
315
|
+
self._condition_update()
|
|
316
|
+
self._counter += 1
|
|
317
|
+
|
|
318
|
+
def _do_inner_iteration_stage(self):
|
|
319
|
+
r"""
|
|
320
|
+
Executes the inner iteration stage
|
|
321
|
+
"""
|
|
322
|
+
logger.debug(" Inner Iteration Stage: ")
|
|
323
|
+
|
|
324
|
+
self._plast = len(np.nonzero(self['pore.cluster_final'])[0])
|
|
325
|
+
if self._timing:
|
|
326
|
+
# determine the cluster with the earliest Haines time
|
|
327
|
+
self._current_cluster = 1 + self._cluster_data['haines_time'].tolist().index(min(self._cluster_data['haines_time']))
|
|
328
|
+
# update simulation clock
|
|
329
|
+
logger.debug( 'sim time = ')
|
|
330
|
+
logger.debug(self._sim_time)
|
|
331
|
+
logger.debug(' haines time:')
|
|
332
|
+
logger.debug( self._cluster_data['haines_time'])
|
|
333
|
+
# The code really messes up when the [0] isn't in the next line. sim_time seems to just point to a place on the haines time array
|
|
334
|
+
self._sim_time = min(self._cluster_data['haines_time'])
|
|
335
|
+
logger.debug( 'sim time after update= ')
|
|
336
|
+
logger.debug(self._sim_time)
|
|
337
|
+
else:
|
|
338
|
+
# Cycle to the next active cluster
|
|
339
|
+
condition = 0
|
|
340
|
+
loop_count = 0
|
|
341
|
+
original_cluster = self._current_cluster
|
|
342
|
+
cnum = original_cluster+1
|
|
343
|
+
while condition == 0:
|
|
344
|
+
if cnum > self._clusterCount:
|
|
345
|
+
cnum = 1
|
|
346
|
+
if self._cluster_data['active'][cnum-1] == 1:
|
|
347
|
+
condition = 1
|
|
348
|
+
self._current_cluster = cnum
|
|
349
|
+
if cnum == original_cluster:
|
|
350
|
+
loop_count = loop_count+1
|
|
351
|
+
if loop_count > 1:
|
|
352
|
+
logger.error('No clusters active. Stuck in infinite loop.')
|
|
353
|
+
pass
|
|
354
|
+
cnum = cnum + 1
|
|
355
|
+
|
|
356
|
+
# run through the Haines Jump steps
|
|
357
|
+
self._do_one_inner_iteration()
|
|
358
|
+
self._pnew = len(np.nonzero(self['pore.cluster_final'])[0])
|
|
359
|
+
self._tseq += 1
|
|
360
|
+
if self._pnew>self._plast:
|
|
361
|
+
self._pseq += 1
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _do_one_inner_iteration(self):
|
|
365
|
+
r"""
|
|
366
|
+
Executes one inner iteration
|
|
367
|
+
"""
|
|
368
|
+
logger.debug(" Inner Iteration")
|
|
369
|
+
# Fill throat and connecting pore
|
|
370
|
+
# Pop out the largest throat (lowest inv_Pc) in the list, read the throat number
|
|
371
|
+
tinvade = heapq.heappop(self._tpoints[self._current_cluster-1])[1]
|
|
372
|
+
emptyCluster = -1
|
|
373
|
+
fullCluster = self._current_cluster
|
|
374
|
+
if self._tpoints[self._current_cluster-1] == []:
|
|
375
|
+
emptyCluster = self._current_cluster
|
|
376
|
+
logger.debug( ' ')
|
|
377
|
+
logger.debug( '--------------------------------------------------')
|
|
378
|
+
logger.debug( 'STEP')
|
|
379
|
+
logger.debug(self._tseq)
|
|
380
|
+
logger.debug( 'trying to access cluster: ')
|
|
381
|
+
logger.debug(self._current_cluster)
|
|
382
|
+
logger.debug( 'when these clusters are active active: ')
|
|
383
|
+
logger.debug(sp.nonzero(self._cluster_data['active'])[0])
|
|
384
|
+
logger.debug( 'Haines at throat,time: ')
|
|
385
|
+
logger.debug(tinvade)
|
|
386
|
+
if self._timing:
|
|
387
|
+
logger.debug(self._sim_time)
|
|
388
|
+
pass
|
|
389
|
+
|
|
390
|
+
# Mark throat as invaded
|
|
391
|
+
self['throat.inv_seq'][tinvade] = self._tseq
|
|
392
|
+
self['throat.inv_pres'][tinvade] = max(max(self['throat.inv_pres']),self['throat.inv_Pc'][tinvade])
|
|
393
|
+
if self._timing:
|
|
394
|
+
self['throat.inv_time'][tinvade] = self._sim_time
|
|
395
|
+
# update self._cluster_data.['pore_volume']
|
|
396
|
+
self._cluster_data['throat_volume'][self._current_cluster-1] += self._throat_volumes[tinvade]
|
|
397
|
+
# Remove throat's contribution to the vol_coef
|
|
398
|
+
self._cluster_data['vol_coef'][self._current_cluster-1] = self._cluster_data['vol_coef'][self._current_cluster-1] - self._Tvol_coef[tinvade]
|
|
399
|
+
# Mark pore as invaded
|
|
400
|
+
Pores = self._net.find_connected_pores(tinvade)
|
|
401
|
+
# If both pores are already invaded:
|
|
402
|
+
if np.in1d(Pores,np.nonzero(self['pore.cluster_final'])[0]).all():
|
|
403
|
+
self._NewPore = -1
|
|
404
|
+
# Label invaded throat with smaller cluster number
|
|
405
|
+
#find cluster 1
|
|
406
|
+
clusters = self._cluster_data['transform'][self['pore.cluster_final'][Pores]-1]
|
|
407
|
+
logger.debug('clusters = ')
|
|
408
|
+
logger.debug(clusters)
|
|
409
|
+
self._current_cluster = min(clusters)
|
|
410
|
+
self['throat.cluster_final'][tinvade] = self._current_cluster
|
|
411
|
+
# if pores are from 2 different clusters:
|
|
412
|
+
if self['pore.cluster_final'][Pores[0]]!=self['pore.cluster_final'][Pores[1]] :
|
|
413
|
+
# find name of larger cluster number
|
|
414
|
+
maxCluster = max(clusters)
|
|
415
|
+
curCluster = self._current_cluster
|
|
416
|
+
if emptyCluster == maxCluster:
|
|
417
|
+
fullCluster = curCluster
|
|
418
|
+
if emptyCluster == curCluster:
|
|
419
|
+
fullCluster = maxCluster
|
|
420
|
+
logger.info(' ')
|
|
421
|
+
logger.info('CLUSTERS COMBINING:')
|
|
422
|
+
logger.info(curCluster)
|
|
423
|
+
logger.info(maxCluster)
|
|
424
|
+
if self._timing:
|
|
425
|
+
logger.info('at time')
|
|
426
|
+
logger.info(self._sim_time)
|
|
427
|
+
pass
|
|
428
|
+
# update the cluster transform
|
|
429
|
+
self._cluster_data['transform'][self._cluster_data['transform']==maxCluster] = [curCluster][0]
|
|
430
|
+
# check if either was inactive (broke through already)
|
|
431
|
+
if self._cluster_data['active'][maxCluster-1] + self._cluster_data['active'][self._current_cluster-1]<2:
|
|
432
|
+
logger.debug('making clusters ')
|
|
433
|
+
logger.debug(self._current_cluster)
|
|
434
|
+
logger.debug('and')
|
|
435
|
+
logger.debug(maxCluster)
|
|
436
|
+
logger.debug('inactive due to one being inactive already')
|
|
437
|
+
logger.debug(self._cluster_data['active'][curCluster-1])
|
|
438
|
+
logger.debug(self._cluster_data['active'][maxCluster-1])
|
|
439
|
+
self.cluster_remove(curCluster)
|
|
440
|
+
logger.info(' ')
|
|
441
|
+
logger.info('CLUSTER MERGED WITH A BREAKTHROUGH CLUSTER')
|
|
442
|
+
else:
|
|
443
|
+
# relabel all pores and throats from larger number with smaller number
|
|
444
|
+
cluster_pores = self.toindices((self['pore.cluster_final']==maxCluster) + (self['pore.cluster_final']==curCluster))
|
|
445
|
+
cluster_throats = self.toindices((self['throat.cluster_final']==maxCluster) + (self['throat.cluster_final']==curCluster))
|
|
446
|
+
if emptyCluster == -1:
|
|
447
|
+
cluster_int_throats = list(zip(*self._tpoints[curCluster-1]))[1] + list(zip(*self._tpoints[maxCluster-1]))[1]
|
|
448
|
+
else:
|
|
449
|
+
cluster_int_throats = list(zip(*self._tpoints[fullCluster-1]))[1]
|
|
450
|
+
self._cluster_data['flow_rate'][curCluster-1] += self._cluster_data['flow_rate'][maxCluster-1]
|
|
451
|
+
self.cluster_update(curCluster,cluster_pores,cluster_throats,cluster_int_throats,tinvade)
|
|
452
|
+
logger.info('making cluster ')
|
|
453
|
+
logger.info(maxCluster)
|
|
454
|
+
logger.info('inactive due to merge')
|
|
455
|
+
# update the old cluster's activity and time
|
|
456
|
+
self.cluster_remove(maxCluster)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
else:
|
|
460
|
+
# label invaded throat with current cluster
|
|
461
|
+
self['throat.cluster_final'][tinvade] = self._current_cluster
|
|
462
|
+
# find univaded pore, NewPore
|
|
463
|
+
self._NewPore = Pores[self['pore.cluster_final'][Pores]==0][0]
|
|
464
|
+
logger.debug( ' ')
|
|
465
|
+
logger.debug( 'INVADING PORE: ')
|
|
466
|
+
logger.debug(self._NewPore)
|
|
467
|
+
logger.debug('the other pore is one of: ')
|
|
468
|
+
logger.debug(Pores)
|
|
469
|
+
logger.debug( 'position: ')
|
|
470
|
+
logger.debug(self._net['pore.coords'][self._NewPore])
|
|
471
|
+
# label that pore as invaded
|
|
472
|
+
self['pore.cluster_final'][self._NewPore] = self._current_cluster
|
|
473
|
+
self['pore.cluster_original'][self._NewPore] = self._current_cluster
|
|
474
|
+
if self._timing:
|
|
475
|
+
self['pore.inv_time'][self._NewPore] = self._sim_time
|
|
476
|
+
self['pore.inv_seq'][self._NewPore] = self._tseq
|
|
477
|
+
self['pore.inv_pres'][self._NewPore] = max(self['throat.inv_pres'])
|
|
478
|
+
if self._timing:
|
|
479
|
+
# update self._cluster_data.['pore_volume']
|
|
480
|
+
self._cluster_data['pore_volume'][self._current_cluster-1] += self._pore_volumes[self._NewPore]
|
|
481
|
+
# Make a list of all throats neighboring pores in the cluster
|
|
482
|
+
# Update interface list
|
|
483
|
+
neighbors = self._net.find_neighbor_throats(self._NewPore)
|
|
484
|
+
for j in neighbors:
|
|
485
|
+
# If a throat is not labelled as invaded by the cluster, it must be an interfacial throat
|
|
486
|
+
if (j not in self._tlists[self._current_cluster-1]):
|
|
487
|
+
logger.debug( 'new throat:')
|
|
488
|
+
logger.debug(j)
|
|
489
|
+
logger.debug('connecting pores:')
|
|
490
|
+
logger.debug(self._net.find_connected_pores(j))
|
|
491
|
+
# Add this throat data (pressure, number) to this cluster's "heap" of throat data.
|
|
492
|
+
heapq.heappush(self._tpoints[self._current_cluster-1],(self._phase['throat.'+self._capillary_pressure_name][j],j))
|
|
493
|
+
# Add new throat number to throat list for this cluster
|
|
494
|
+
self._tlists[self._current_cluster-1].append(j)
|
|
495
|
+
if self._timing:
|
|
496
|
+
# Update the cluster's vol_coef
|
|
497
|
+
self._cluster_data['vol_coef'][self._current_cluster-1] = self._cluster_data['vol_coef'][self._current_cluster-1]+self._Tvol_coef[j]
|
|
498
|
+
if self._tpoints[self._current_cluster-1] != []:
|
|
499
|
+
# Make sure you are not re-invading a throat in the next step (might never happen with new cluster routines)
|
|
500
|
+
while self['throat.cluster_final'][self._tpoints[self._current_cluster-1][0][1]] > 0:
|
|
501
|
+
tremove = heapq.heappop(self._tpoints[self._current_cluster-1])[1]
|
|
502
|
+
if self._tpoints[self._current_cluster-1] == []:
|
|
503
|
+
logger.debug( 'making cluster ')
|
|
504
|
+
logger.debug(self._current_cluster)
|
|
505
|
+
logger.debug('inactive due to tpoints = [] ')
|
|
506
|
+
self.cluster_remove(self._current_cluster)
|
|
507
|
+
print('still happening!')
|
|
508
|
+
break
|
|
509
|
+
# Find next Haines Jump info
|
|
510
|
+
if self._tpoints[self._current_cluster-1] != []:
|
|
511
|
+
next_throat = self._tpoints[self._current_cluster-1][0][1]
|
|
512
|
+
self._cluster_data['haines_throat'][self._current_cluster-1] = next_throat
|
|
513
|
+
if self._timing:
|
|
514
|
+
self._cluster_data['haines_pressure'][self._current_cluster-1] = self._tpoints[self._current_cluster-1][0][0]
|
|
515
|
+
self._cluster_data['cap_volume'][self._current_cluster-1] = self._cluster_data['haines_pressure'][self._current_cluster-1]*self._cluster_data['vol_coef'][self._current_cluster-1]
|
|
516
|
+
# Calculate the new Haines jump time
|
|
517
|
+
logger.debug( 'haines time before last stage:')
|
|
518
|
+
logger.debug( self._cluster_data['haines_time'])
|
|
519
|
+
if self._tpoints[self._current_cluster-1] == []:
|
|
520
|
+
logger.debug('making cluster ')
|
|
521
|
+
logger.debug(self._current_cluster)
|
|
522
|
+
logger.debug('inactive due to self._tpoints being empty for that cluster')
|
|
523
|
+
self.cluster_remove(self._current_cluster)
|
|
524
|
+
if self._timing:
|
|
525
|
+
if self._cluster_data['active'][self._current_cluster-1] == 1:
|
|
526
|
+
self._cluster_data['haines_time'][self._current_cluster-1] = (self._cluster_data['pore_volume'][self._current_cluster-1]+self._cluster_data['throat_volume'][self._current_cluster-1]+self._cluster_data['cap_volume'][self._current_cluster-1])/self._cluster_data['flow_rate'][self._current_cluster-1]
|
|
527
|
+
if self._cluster_data['haines_time'][self._current_cluster-1] < self._sim_time:
|
|
528
|
+
self._cluster_data['haines_time'][self._current_cluster-1] = self._sim_time
|
|
529
|
+
logger.debug('haines time at the end of the throat stuff')
|
|
530
|
+
logger.debug(self._cluster_data['haines_time'])
|
|
531
|
+
|
|
532
|
+
def _condition_update(self):
|
|
533
|
+
# Calculate the distance between the new pore and outlet pores
|
|
534
|
+
if self._end_condition == 'breakthrough':
|
|
535
|
+
newpore_position = self._net['pore.coords'][self._NewPore]
|
|
536
|
+
dist_sqrd = (self._outlet_position-newpore_position)*(self._outlet_position-newpore_position)
|
|
537
|
+
if dist_sqrd[0].shape==(3,): # need to do this for MatFile networks because newpore_position is a nested array, not a vector (?)
|
|
538
|
+
dist_sqrd = dist_sqrd[0]
|
|
539
|
+
newpore_distance = np.sqrt(dist_sqrd[0]+dist_sqrd[1]+dist_sqrd[2])
|
|
540
|
+
logger.debug( 'newpore distance')
|
|
541
|
+
logger.debug( newpore_distance)
|
|
542
|
+
if newpore_distance < self._current_distance:
|
|
543
|
+
self._percent_complete = np.round((self._initial_distance-newpore_distance)/self._initial_distance*100, decimals = 1)
|
|
544
|
+
logger.info( 'percent complete')
|
|
545
|
+
logger.info( self._percent_complete)
|
|
546
|
+
self._current_distance = newpore_distance
|
|
547
|
+
elif self._end_condition == 'total':
|
|
548
|
+
self._percent_complete = np.round((np.sum(self['pore.cluster_final']>0)/self._net.num_pores())*100, decimals = 1)
|
|
549
|
+
if self._percent_complete > self._rough_complete + self._rough_increment:
|
|
550
|
+
self._rough_complete = np.floor(self._percent_complete/self._rough_increment)*self._rough_increment
|
|
551
|
+
print(' IP algorithm at',np.int(self._rough_complete),'% completion at',np.round(misc.toc(quiet=True)),'seconds')
|
|
552
|
+
|
|
553
|
+
# Determine if a new breakthrough position has occured
|
|
554
|
+
if self._NewPore in self._outlets:
|
|
555
|
+
logger.info( ' ')
|
|
556
|
+
logger.info( 'BREAKTHROUGH AT PORE: ')
|
|
557
|
+
logger.info(self._NewPore)
|
|
558
|
+
logger.info('in cluster ')
|
|
559
|
+
logger.info(self._current_cluster)
|
|
560
|
+
if self._timing:
|
|
561
|
+
logger.info('at time')
|
|
562
|
+
logger.info(self._sim_time)
|
|
563
|
+
pass
|
|
564
|
+
if self._end_condition == 'breakthrough':
|
|
565
|
+
self.cluster_remove(self._current_cluster)
|
|
566
|
+
elif self._end_condition == 'total':
|
|
567
|
+
self._brkevent.append(self._NewPore)
|
|
568
|
+
if np.sum(self._cluster_data['active']) == 0:
|
|
569
|
+
logger.info( ' ')
|
|
570
|
+
logger.info( 'SIMULATION FINISHED; no more active clusters')
|
|
571
|
+
if self._timing:
|
|
572
|
+
logger.info('at time')
|
|
573
|
+
logger.info(self._sim_time)
|
|
574
|
+
pass
|
|
575
|
+
self._condition = 0
|
|
576
|
+
print(' IP algorithm at 100% completion at ',np.round(misc.toc(quiet=True)),' seconds')
|
|
577
|
+
|
|
578
|
+
def cluster_update(self,cl_num,pores,throats,int_throats,bad_throat=-1):
|
|
579
|
+
r'''
|
|
580
|
+
'''
|
|
581
|
+
int_throats = sp.unique(int_throats)
|
|
582
|
+
int_throats = int_throats[int_throats!=bad_throat]
|
|
583
|
+
pores = sp.unique(pores)
|
|
584
|
+
throats = sp.unique(throats)
|
|
585
|
+
#label all pores as invaded
|
|
586
|
+
self['pore.cluster_final'][pores] = cl_num
|
|
587
|
+
if sp.shape(throats) != (0,):
|
|
588
|
+
self['throat.cluster_final'][throats] = cl_num
|
|
589
|
+
if self._timing:
|
|
590
|
+
# Calculate total volume in all invaded pores
|
|
591
|
+
self._cluster_data['pore_volume'][cl_num-1] = np.sum(self._pore_volumes[pores])
|
|
592
|
+
# Calculate total volume in all invaded throats
|
|
593
|
+
if sp.shape(throats) != (0,):
|
|
594
|
+
self._cluster_data['throat_volume'][cl_num-1] = np.sum(self._throat_volumes[throats])
|
|
595
|
+
# Sum all interfacial throats' volume coeffients for throat cap volume calculation
|
|
596
|
+
self._cluster_data['vol_coef'][cl_num-1] = np.sum(self._Tvol_coef[int_throats])
|
|
597
|
+
# Make a list of all entry pressures of the interfacial throats
|
|
598
|
+
interface_throat_pressures = self['throat.inv_Pc'][int_throats]#[0]
|
|
599
|
+
# Zip pressures and numbers together so that HeapQ can work its magic
|
|
600
|
+
Interface= list(zip(interface_throat_pressures,int_throats))
|
|
601
|
+
# Turn the zipped throat interfaces object into a heap
|
|
602
|
+
heapq.heapify(Interface)
|
|
603
|
+
# Add to the total list of interface throats in the system
|
|
604
|
+
self._tlists[cl_num-1] = int_throats.tolist()
|
|
605
|
+
# Add to the total list of invaded interface throats in the system
|
|
606
|
+
self._tpoints[cl_num-1] = Interface
|
|
607
|
+
# Pop off the first entry (lowest pressure) on the throat info list
|
|
608
|
+
invaded_throat_info = Interface[0]
|
|
609
|
+
if self._timing:
|
|
610
|
+
# Determine pressure at Haines Jump
|
|
611
|
+
self._cluster_data['haines_pressure'][cl_num-1] = invaded_throat_info[0]
|
|
612
|
+
# Calculate cap_volume at Haines Jump
|
|
613
|
+
self._cluster_data['cap_volume'][cl_num-1] = self._cluster_data['haines_pressure'][cl_num-1]*self._cluster_data['vol_coef'][cl_num-1]
|
|
614
|
+
# Calculate throat_volume at Haines Jump
|
|
615
|
+
self._cluster_data['throat_volume'][cl_num-1] = self._cluster_data['throat_volume'][cl_num-1]+self._throat_volumes[invaded_throat_info[1]]
|
|
616
|
+
# Calculate time at Haines Jump
|
|
617
|
+
self._cluster_data['haines_time'][cl_num-1] = (self._cluster_data['pore_volume'][cl_num-1]+self._cluster_data['throat_volume'][cl_num-1]+
|
|
618
|
+
self._cluster_data['cap_volume'][cl_num-1])/self._cluster_data['flow_rate'][cl_num-1]
|
|
619
|
+
# Record invaded throat
|
|
620
|
+
self._cluster_data['haines_throat'][cl_num-1] = invaded_throat_info[1]
|
|
621
|
+
|
|
622
|
+
def cluster_remove(self,cl_num):
|
|
623
|
+
if self._timing:
|
|
624
|
+
self._cluster_data['haines_time'][cl_num-1] = 1e32
|
|
625
|
+
self._cluster_data['active'][cl_num-1] = 0
|
|
626
|
+
self._tpoints[cl_num-1] = []
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def return_results(self,occupancy='occupancy',IPseq=None,IPsat=None,IPpres=None):
|
|
630
|
+
r"""
|
|
631
|
+
|
|
632
|
+
Returns
|
|
633
|
+
-------
|
|
634
|
+
The invading phase will aquire the following pore data ::
|
|
635
|
+
|
|
636
|
+
occupancy : 0. for univaded, 1. for invaded
|
|
637
|
+
IP_cluster_final : 0 for uninvaded, merged cluster number for invaded
|
|
638
|
+
IP_cluster_original : 0 for uninvaded, original cluster number for invaded
|
|
639
|
+
IP_inv_seq : 0 for uninvaded, simulation step for invaded
|
|
640
|
+
IP_inv_time : 0 for uninvaded, simulation time for invaded
|
|
641
|
+
|
|
642
|
+
and throat data ::
|
|
643
|
+
|
|
644
|
+
occupancy : 0 for univaded, 1 for invaded
|
|
645
|
+
IP_cluster_final : 0 for uninvaded, merged cluster number for invaded
|
|
646
|
+
IP_inv_seq : 0 for uninvaded, simulation step for invaded
|
|
647
|
+
IP_inv_time : 0 for uninvaded, simulation time for invaded
|
|
648
|
+
|
|
649
|
+
"""
|
|
650
|
+
self._phase['pore.IP_cluster_final']=self['pore.cluster_final']
|
|
651
|
+
self._phase['pore.IP_cluster_original']=self['pore.cluster_original']
|
|
652
|
+
self._phase['throat.IP_cluster_final']=self['throat.cluster_final']
|
|
653
|
+
self._phase['pore.IP_inv_seq']=self['pore.inv_seq']
|
|
654
|
+
self._phase['throat.IP_inv_seq']=self['throat.inv_seq']
|
|
655
|
+
if self._timing:
|
|
656
|
+
self._phase['pore.IP_inv_time']=self['pore.inv_time']
|
|
657
|
+
self._phase['throat.IP_inv_time']=self['throat.inv_time']
|
|
658
|
+
|
|
659
|
+
if IPseq==None:
|
|
660
|
+
if IPsat is not None:
|
|
661
|
+
sat_pores = self['pore.inv_sat']<=IPsat
|
|
662
|
+
sat_throats = self['throat.inv_sat']<=IPsat
|
|
663
|
+
if sum(sat_pores) == 0:
|
|
664
|
+
IPseq = 0
|
|
665
|
+
else:
|
|
666
|
+
IPseq = max([max(self['throat.inv_seq'][sat_throats]),max(self['pore.inv_seq'][sat_pores])])
|
|
667
|
+
else:
|
|
668
|
+
if IPpres != None:
|
|
669
|
+
sat_pores = self['pore.inv_pres']<=IPpres
|
|
670
|
+
sat_throats = self['throat.inv_pres']<=IPpres
|
|
671
|
+
if sum(sat_pores) == 0:
|
|
672
|
+
IPseq = 0
|
|
673
|
+
else:
|
|
674
|
+
IPseq = max([max(self['throat.inv_seq'][sat_throats]),max(self['pore.inv_seq'][sat_pores])])
|
|
675
|
+
else:
|
|
676
|
+
IPseq = self._tseq
|
|
677
|
+
|
|
678
|
+
try:
|
|
679
|
+
self._phase['pore.'+occupancy] = 0.
|
|
680
|
+
inv_pores = (self['pore.inv_seq']>0)&(self['pore.inv_seq']<=IPseq)
|
|
681
|
+
self._phase['pore.'+occupancy][inv_pores] = 1.
|
|
682
|
+
self['pore.invaded'] = inv_pores
|
|
683
|
+
self._phase['throat.'+occupancy] = 0.
|
|
684
|
+
inv_throats = (self['throat.inv_seq']>0)&(self['throat.inv_seq']<=IPseq)
|
|
685
|
+
self._phase['throat.'+occupancy][inv_throats] = 1.
|
|
686
|
+
self['throat.invaded'] = inv_throats
|
|
687
|
+
self.sat = max(self['throat.inv_sat'][inv_throats])
|
|
688
|
+
|
|
689
|
+
except:
|
|
690
|
+
print('Something bad happened while trying to update phase',self._phase.name)
|
|
691
|
+
try:
|
|
692
|
+
self._phase_def['pore.'+occupancy]=sp.array(~inv_pores,dtype='float')
|
|
693
|
+
self['pore.defended']=sp.array(~inv_pores, dtype='float')
|
|
694
|
+
self._phase_def['throat.'+occupancy]=sp.array(~inv_throats, dtype='float')
|
|
695
|
+
self['throat.defended']=sp.array(~inv_throats, dtype='float')
|
|
696
|
+
except:
|
|
697
|
+
print('A partner phase has not been set so inverse occupancy cannot be set')
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
if __name__ == '__main__':
|
|
701
|
+
import doctest
|
|
702
|
+
doctest.testmod(verbose=True)
|