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.
Files changed (108) hide show
  1. OpenPNM-1.1/MANIFEST.in +2 -0
  2. OpenPNM-1.1/OpenPNM/Algorithms/__FickianDiffusion__.py +67 -0
  3. OpenPNM-1.1/OpenPNM/Algorithms/__FourierConduction__.py +63 -0
  4. OpenPNM-1.1/OpenPNM/Algorithms/__GenericAlgorithm__.py +235 -0
  5. OpenPNM-1.1/OpenPNM/Algorithms/__GenericLinearTransport__.py +641 -0
  6. OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolationForImbibition__.py +703 -0
  7. OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolationTimed__.py +702 -0
  8. OpenPNM-1.1/OpenPNM/Algorithms/__InvasionPercolation__.py +156 -0
  9. OpenPNM-1.1/OpenPNM/Algorithms/__OhmicConduction__.py +64 -0
  10. OpenPNM-1.1/OpenPNM/Algorithms/__OrdinaryPercolation__.py +402 -0
  11. OpenPNM-1.1/OpenPNM/Algorithms/__StokesFlow__.py +64 -0
  12. OpenPNM-1.1/OpenPNM/Algorithms/__Tortuosity__.py +91 -0
  13. OpenPNM-1.1/OpenPNM/Algorithms/__init__.py +48 -0
  14. OpenPNM-1.1/OpenPNM/Base/__Controller__.py +480 -0
  15. OpenPNM-1.1/OpenPNM/Base/__Core__.py +1522 -0
  16. OpenPNM-1.1/OpenPNM/Base/__ModelsDict__.py +345 -0
  17. OpenPNM-1.1/OpenPNM/Base/__Tools__.py +72 -0
  18. OpenPNM-1.1/OpenPNM/Base/__init__.py +32 -0
  19. OpenPNM-1.1/OpenPNM/Geometry/__Boundary__.py +80 -0
  20. OpenPNM-1.1/OpenPNM/Geometry/__Cube_and_Cuboid__.py +64 -0
  21. OpenPNM-1.1/OpenPNM/Geometry/__GenericGeometry__.py +106 -0
  22. OpenPNM-1.1/OpenPNM/Geometry/__SGL10__.py +67 -0
  23. OpenPNM-1.1/OpenPNM/Geometry/__Stick_and_Ball__.py +68 -0
  24. OpenPNM-1.1/OpenPNM/Geometry/__TestGeometry__.py +51 -0
  25. OpenPNM-1.1/OpenPNM/Geometry/__Toray090__.py +68 -0
  26. OpenPNM-1.1/OpenPNM/Geometry/__Voronoi__.py +98 -0
  27. OpenPNM-1.1/OpenPNM/Geometry/__init__.py +47 -0
  28. OpenPNM-1.1/OpenPNM/Geometry/models/__init__.py +33 -0
  29. OpenPNM-1.1/OpenPNM/Geometry/models/pore_area.py +27 -0
  30. OpenPNM-1.1/OpenPNM/Geometry/models/pore_centroid.py +35 -0
  31. OpenPNM-1.1/OpenPNM/Geometry/models/pore_diameter.py +127 -0
  32. OpenPNM-1.1/OpenPNM/Geometry/models/pore_misc.py +55 -0
  33. OpenPNM-1.1/OpenPNM/Geometry/models/pore_seed.py +212 -0
  34. OpenPNM-1.1/OpenPNM/Geometry/models/pore_surface_area.py +28 -0
  35. OpenPNM-1.1/OpenPNM/Geometry/models/pore_vertices.py +19 -0
  36. OpenPNM-1.1/OpenPNM/Geometry/models/pore_volume.py +133 -0
  37. OpenPNM-1.1/OpenPNM/Geometry/models/throat_area.py +47 -0
  38. OpenPNM-1.1/OpenPNM/Geometry/models/throat_centroid.py +80 -0
  39. OpenPNM-1.1/OpenPNM/Geometry/models/throat_diameter.py +106 -0
  40. OpenPNM-1.1/OpenPNM/Geometry/models/throat_length.py +95 -0
  41. OpenPNM-1.1/OpenPNM/Geometry/models/throat_misc.py +42 -0
  42. OpenPNM-1.1/OpenPNM/Geometry/models/throat_normal.py +31 -0
  43. OpenPNM-1.1/OpenPNM/Geometry/models/throat_offset_vertices.py +191 -0
  44. OpenPNM-1.1/OpenPNM/Geometry/models/throat_perimeter.py +26 -0
  45. OpenPNM-1.1/OpenPNM/Geometry/models/throat_seed.py +12 -0
  46. OpenPNM-1.1/OpenPNM/Geometry/models/throat_shape_factor.py +37 -0
  47. OpenPNM-1.1/OpenPNM/Geometry/models/throat_surface_area.py +44 -0
  48. OpenPNM-1.1/OpenPNM/Geometry/models/throat_vector.py +27 -0
  49. OpenPNM-1.1/OpenPNM/Geometry/models/throat_vertices.py +19 -0
  50. OpenPNM-1.1/OpenPNM/Geometry/models/throat_volume.py +45 -0
  51. OpenPNM-1.1/OpenPNM/Network/__Cubic__.py +316 -0
  52. OpenPNM-1.1/OpenPNM/Network/__DelaunayCubic__.py +127 -0
  53. OpenPNM-1.1/OpenPNM/Network/__Delaunay__.py +600 -0
  54. OpenPNM-1.1/OpenPNM/Network/__GenericNetwork__.py +1184 -0
  55. OpenPNM-1.1/OpenPNM/Network/__MatFile__.py +331 -0
  56. OpenPNM-1.1/OpenPNM/Network/__TestNet__.py +109 -0
  57. OpenPNM-1.1/OpenPNM/Network/__init__.py +40 -0
  58. OpenPNM-1.1/OpenPNM/Network/models/__init__.py +12 -0
  59. OpenPNM-1.1/OpenPNM/Network/models/pore_topology.py +106 -0
  60. OpenPNM-1.1/OpenPNM/Phases/__Air__.py +63 -0
  61. OpenPNM-1.1/OpenPNM/Phases/__GenericPhase__.py +146 -0
  62. OpenPNM-1.1/OpenPNM/Phases/__Mercury__.py +71 -0
  63. OpenPNM-1.1/OpenPNM/Phases/__TestPhase__.py +46 -0
  64. OpenPNM-1.1/OpenPNM/Phases/__Water__.py +56 -0
  65. OpenPNM-1.1/OpenPNM/Phases/__init__.py +38 -0
  66. OpenPNM-1.1/OpenPNM/Phases/models/__init__.py +22 -0
  67. OpenPNM-1.1/OpenPNM/Phases/models/contact_angle.py +34 -0
  68. OpenPNM-1.1/OpenPNM/Phases/models/density.py +81 -0
  69. OpenPNM-1.1/OpenPNM/Phases/models/diffusivity.py +95 -0
  70. OpenPNM-1.1/OpenPNM/Phases/models/electrical_conductivity.py +10 -0
  71. OpenPNM-1.1/OpenPNM/Phases/models/misc.py +125 -0
  72. OpenPNM-1.1/OpenPNM/Phases/models/molar_density.py +69 -0
  73. OpenPNM-1.1/OpenPNM/Phases/models/molar_mass.py +31 -0
  74. OpenPNM-1.1/OpenPNM/Phases/models/surface_tension.py +104 -0
  75. OpenPNM-1.1/OpenPNM/Phases/models/thermal_conductivity.py +98 -0
  76. OpenPNM-1.1/OpenPNM/Phases/models/vapor_pressure.py +69 -0
  77. OpenPNM-1.1/OpenPNM/Phases/models/viscosity.py +103 -0
  78. OpenPNM-1.1/OpenPNM/Physics/__GenericPhysics__.py +111 -0
  79. OpenPNM-1.1/OpenPNM/Physics/__Standard__.py +51 -0
  80. OpenPNM-1.1/OpenPNM/Physics/__TestPhysics__.py +50 -0
  81. OpenPNM-1.1/OpenPNM/Physics/__init__.py +30 -0
  82. OpenPNM-1.1/OpenPNM/Physics/models/__init__.py +18 -0
  83. OpenPNM-1.1/OpenPNM/Physics/models/capillary_pressure.py +122 -0
  84. OpenPNM-1.1/OpenPNM/Physics/models/diffusive_conductance.py +82 -0
  85. OpenPNM-1.1/OpenPNM/Physics/models/electrical_conductance.py +59 -0
  86. OpenPNM-1.1/OpenPNM/Physics/models/generic_source_term.py +564 -0
  87. OpenPNM-1.1/OpenPNM/Physics/models/hydraulic_conductance.py +76 -0
  88. OpenPNM-1.1/OpenPNM/Physics/models/multiphase.py +133 -0
  89. OpenPNM-1.1/OpenPNM/Physics/models/thermal_conductance.py +67 -0
  90. OpenPNM-1.1/OpenPNM/Postprocessing/Graphics.py +251 -0
  91. OpenPNM-1.1/OpenPNM/Postprocessing/Plots.py +369 -0
  92. OpenPNM-1.1/OpenPNM/Postprocessing/__init__.py +10 -0
  93. OpenPNM-1.1/OpenPNM/Utilities/IO.py +277 -0
  94. OpenPNM-1.1/OpenPNM/Utilities/Shortcuts.py +17 -0
  95. OpenPNM-1.1/OpenPNM/Utilities/__init__.py +16 -0
  96. OpenPNM-1.1/OpenPNM/Utilities/misc.py +226 -0
  97. OpenPNM-1.1/OpenPNM/Utilities/transformations.py +1923 -0
  98. OpenPNM-1.1/OpenPNM/Utilities/vertexops.py +824 -0
  99. OpenPNM-1.1/OpenPNM/__init__.py +56 -0
  100. OpenPNM-1.1/OpenPNM.egg-info/PKG-INFO +11 -0
  101. OpenPNM-1.1/OpenPNM.egg-info/SOURCES.txt +107 -0
  102. OpenPNM-1.1/OpenPNM.egg-info/dependency_links.txt +1 -0
  103. OpenPNM-1.1/OpenPNM.egg-info/requires.txt +1 -0
  104. OpenPNM-1.1/OpenPNM.egg-info/top_level.txt +1 -0
  105. OpenPNM-1.1/PKG-INFO +11 -0
  106. OpenPNM-1.1/README.txt +88 -0
  107. OpenPNM-1.1/setup.cfg +7 -0
  108. OpenPNM-1.1/setup.py +39 -0
@@ -0,0 +1,703 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ ===============================================================================
4
+ module __InvasionPercolationForImbibition__: Invasion Percolation Algorithm for Imbibition
5
+ ===============================================================================
6
+ """
7
+ import scipy as sp
8
+ import numpy as np
9
+ import scipy.sparse as sprs
10
+ from time import clock
11
+ import heapq
12
+ import itertools
13
+ from OpenPNM.Algorithms import InvasionPercolation
14
+ from OpenPNM.Base import logging
15
+ logger = logging.getLogger(__name__)
16
+
17
+ class InvasionPercolationForImbibition(InvasionPercolation):
18
+ r"""
19
+
20
+ Invasion Percolation (Imbibition) with cluster growth timing - Class to run IP algorithm on constructed networks
21
+
22
+ Parameters
23
+ ----------
24
+ network : Descendent of OpenPNM.Network.GenericNetwork
25
+ A valid network for this algorithm
26
+ name : string
27
+ The name this algorithm will go by
28
+
29
+ """
30
+ def __init__(self,**kwords):
31
+ r"""
32
+ """
33
+ super(InvasionPercolationForImbibition,self).__init__(**kwords)
34
+ logger.info("Create IP Imbibition Algorithm Object")
35
+
36
+ def run(self,invading_phase,
37
+ defending_phase,
38
+ inlets=[0],
39
+ outlets=[-1],
40
+ end_condition='breakthrough',
41
+ pore_volume_name='volume',
42
+ pore_diameter_name='diameter',
43
+ throat_volume_name='volume',
44
+ timing='ON',
45
+ report=20,
46
+ inlet_flow=1,
47
+ Psecond=False,
48
+ **params):
49
+ r"""
50
+
51
+ Invasion Percolation (Imbibition) with cluster growth timing - Class to run IP algorithm on constructed networks
52
+
53
+ Parameters
54
+ ----------
55
+ invading_phase : OpenPNM Phase Object
56
+ phase which will displace defending phase
57
+ defending_phase : OpenPNM Phase Object
58
+ phase which will be displaced by invading phase
59
+ inlets : list of integers (default: [0])
60
+ list of inlet nodes
61
+ outlets : list of integers (default: [-1])
62
+ list of outlet nodes
63
+ end_condition : string('breakthrough')
64
+ choice between 'breakthrough' and 'total'
65
+ pore_volume_name : string('volume')
66
+ name given to pore volume property
67
+ pore_diameter_name : string('diameter')
68
+ name given to pore diameter property
69
+ throat_volume_name : string('volume')
70
+ name given to throat volume property
71
+ timing : string ('ON')
72
+ turns volume and flowrate calculations 'ON' or 'OFF'
73
+ report : int (20)
74
+ percentage multiple at which a progress report is printed
75
+ inlet_flow : float (1)
76
+ m3/s for each cluster (affects timestamp of pore filling)
77
+ Psecond : boul (False)
78
+ is this a secondary imbibition (after drainage)?
79
+
80
+
81
+ Input Phases
82
+ ------------
83
+ The algorithm expects an invading phase with the following pore properties:
84
+ contact_angle, surface_tension
85
+ and some defending phase
86
+
87
+ Output
88
+ ------
89
+ The invading phase automatically gains pore data ::
90
+
91
+ occupancy : 0 for univaded, 1 for invaded
92
+ IP_inv_final : 0 for uninvaded, merged cluster number for invaded
93
+ IP_inv_original : 0 for uninvaded, original cluster number for invaded
94
+ IP_inv_seq : 0 for uninvaded, simulation step for invaded
95
+ IP_inv_time : 0 for uninvaded, simulation time for invaded
96
+
97
+ and throat data ::
98
+
99
+ occupancy : 0 for univaded, 1 for invaded
100
+ IP_inv : 0 for uninvaded, merged cluster number for invaded
101
+ IP_inv_seq : 0 for uninvaded, simulation step for invaded
102
+ IP_inv_time : 0 for uninvaded, simulation time for invaded
103
+
104
+ Examples
105
+ --------
106
+ >>> import OpenPNM
107
+ >>> pn = OpenPNM.Network.TestNet()
108
+ >>> geo = OpenPNM.Geometry.TestGeometry(network=pn,pores=pn.pores(),throats=pn.throats())
109
+ >>> phase1 = OpenPNM.Phases.TestPhase(network=pn)
110
+ >>> phase1['pore.contact_angle'] = 20
111
+ >>> phase2 = OpenPNM.Phases.TestPhase(network=pn)
112
+ >>> phys1 = OpenPNM.Physics.TestPhysics(network=pn, phase=phase1,pores=pn.pores(),throats=pn.throats())
113
+ >>> phys2 = OpenPNM.Physics.TestPhysics(network=pn, phase=phase2,pores=pn.pores(),throats=pn.throats())
114
+ >>> from OpenPNM.Algorithms.__InvasionPercolationForImbibition__ import InvasionPercolationForImbibition
115
+ >>> IP = InvasionPercolationForImbibition(network=pn)
116
+ >>> IP.run(invading_phase=phase1, defending_phase=phase2, inlets=pn.pores('top'), outlets=pn.pores('bottom'))
117
+ IP algorithm at 0 % completion at 0 seconds
118
+ IP algorithm at 20 % completion at 0 seconds
119
+ IP algorithm at 40 % completion at 0 seconds
120
+ IP algorithm at 100% completion at 0 seconds
121
+ >>> IP.return_results()
122
+ >>> print(len(phase1.pores('occupancy')))
123
+ 29
124
+
125
+ Suggested Improvements ::
126
+
127
+ a) Allow updating of cluster flow-rates (this will require a delta-t calculation at each step, instead of a total t calculation).
128
+ b) Allow for a non-linear relationship between pressure and throat-cap volume.
129
+
130
+ """
131
+
132
+ # if Psecond > 0 then we are doing a secondary imbibition,
133
+ # so end_condition, inlets, are treated differently
134
+ self._Psecond = Psecond
135
+ if Psecond:
136
+ end_condition='secondary'
137
+ inlets = [self._net.pores()[self._phase['pore.occupancy']>0]]
138
+ self._timing = timing=='ON'
139
+ self._pore_diameter_name = pore_diameter_name
140
+ self._throat_volume_name = throat_volume_name
141
+
142
+ super(InvasionPercolationForImbibition,self).run(invading_phase=invading_phase,
143
+ defending_phase=defending_phase,
144
+ inlets=inlets,
145
+ outlets=outlets,
146
+ end_condition=end_condition,
147
+ pore_volume_name=pore_volume_name,
148
+ timing=timing,
149
+ report=report,
150
+ inlet_flow=inlet_flow)
151
+
152
+ def _setup_for_IP(self,**params):
153
+ r"""
154
+ Determines cluster labelling and condition for completion
155
+
156
+ This code is taken from _setup_for_IP from parent InvasionPercolation, but is modified for imbibition
157
+
158
+ """
159
+ self._clock_start = clock()
160
+ logger.debug( '+='*25)
161
+ logger.debug( 'INITIAL SETUP (STEP 1)')
162
+ # if empty, add Pc_entry to pore_properties
163
+ # set the capillary pressure for pores, instead of throats
164
+ # don't need to interpolate for throats
165
+ # this is a Washburn setting, could use others
166
+ sigma = self._phase['pore.surface_tension'][0]
167
+ theta = self._phase['pore.contact_angle'][0]
168
+ if theta > 90:
169
+ print('WARNING!!!: The invading phase has a contact angle greater than 90deg, so it must be drainage. Use Invasion_Percolation for drainage.' )
170
+ pdia = self._net['pore.'+self._pore_diameter_name]
171
+ if self._Psecond:
172
+ pdia = pdia[self._phase['pore.occupancy']<=0]
173
+ # entry pressure for the pore
174
+ # should be -4.. but use +4 to make heapq work correclty (we want highest Pcap)
175
+ Pc_entry = -4*sigma*sp.cos(sp.radians(theta))/pdia # -4*sigma*sp.cos(sp.radians(theta))/pdia PCAP!!
176
+ # add to the IPforImb object
177
+ if ~self._Psecond:
178
+ self._phase['pore.Pc_entryImb']=Pc_entry
179
+
180
+ if self._timing:
181
+ # calculate Volume_coef for each pore, not throat, for imbib
182
+ # this is the volume of a half-sphere in the pore (so will work for irregular pores)
183
+ self._Pvol_coef = pdia**3*np.pi/12/(Pc_entry) # Pc_entry is stored -ve, so must be converted for vol_coeff PCAP!!
184
+ # Creating an array for invaded Pores(Np long, 0 for uninvaded, cluster number for inaveded)
185
+ if self._Psecond:
186
+ Np = sum(self._phase['pore.occupancy']<=0)
187
+ Nt = sum(self._phase['throat.occupancy']<=0)
188
+ else:
189
+ Np = self._net.num_pores()
190
+ Nt = self._net.num_throats()
191
+ self._Pinv = np.zeros((Np,1),dtype=np.int32)
192
+ self._Pinv_original = np.zeros((Np,1),dtype=np.int32)
193
+ # Creating an array for invaded throats(Nt long, 0 for uninvaded, cluster number for inaveded)
194
+ self._Tinv = np.zeros((Nt,1),dtype=np.int32)
195
+ self._Tinv_original = np.zeros((Nt,1),dtype=np.int32)
196
+ # Creating arrays for tracking invaded Pores(Np long, 0 for uninvaded, sequence for inaveded)
197
+ self._psequence = np.zeros((Np,1),dtype=np.int32)
198
+ if self._timing:
199
+ # Creating arrays for tracking invaded Pores(Np long, -1 for uninvaded, simulation time for inaveded)
200
+ self._Ptime = np.zeros((Np,1),dtype=np.float64)-1
201
+ # Creating arrays for tracking invaded throats(Nt long, 0 for uninvaded, sequence for inaveded)
202
+ self._tsequence = np.zeros((Nt,1),dtype=np.int32)
203
+ if self._timing:
204
+ # Creating arrays for tracking invaded Pores(Np long, -1 for uninvaded, simulation time for inaveded)
205
+ self._Ttime = np.zeros((Nt,1),dtype=np.float64)-1
206
+ # Creating an array for tracking the last invaded pore in each cluster.
207
+ # its length is equal to the maximum number of possible clusters.
208
+ #self.plists = np.zeros((len(self._inlets),1),dtype=np.int32)
209
+ # Iterator variables for sequences and cluster numbers
210
+ clusterNumber = 1
211
+ # Determine how many clusters there are
212
+ self._clusterCount = 0
213
+ # get boundary pores so they don't get included in cluster count
214
+ # bpores = self._net.pores(labels=['boundary','bottom'],mode='intersection')
215
+ for i in self._inlets:
216
+ # ignore boundary pores
217
+ # if ~sp.in1d(i,bpores): # don't need this anymore if all inlet pores are in the boundary
218
+ self._clusterCount += 1
219
+ # Storage for cluster information
220
+ self._cluster_data = {}
221
+ if self._timing:
222
+ self._cluster_data['flow_rate'] = np.ones((self._clusterCount),dtype=np.float64)*self._inlet_flow
223
+ self._cluster_data['haines_pressure'] = np.zeros((self._clusterCount),dtype=np.float64)
224
+ self._cluster_data['haines_time'] = np.zeros((self._clusterCount),dtype=np.float64)
225
+ self._cluster_data['vol_coef'] = np.zeros((self._clusterCount),dtype=np.float64)
226
+ self._cluster_data['cap_volume'] = np.zeros((self._clusterCount),dtype=np.float64)
227
+ self._cluster_data['throat_volume'] = np.zeros((self._clusterCount),dtype=np.float64)
228
+ self._cluster_data['haines_pore'] = np.zeros((self._clusterCount),dtype=np.int32)
229
+ self._cluster_data['active'] = np.ones((self._clusterCount),dtype=np.int8)
230
+ self._cluster_data['transform'] = np.zeros((self._clusterCount),dtype=np.int16)
231
+ for i in range(self._clusterCount):
232
+ self._cluster_data['transform'][i] = i+1
233
+ # Creating an empty list to store the list of potential pores for invasion in each cluster.
234
+ # its length is equal to the maximum number of possible clusters.
235
+ self._plists = []
236
+ # Creating a list for each cluster to store both potential pore and corresponding pore value
237
+ self._ppoints = []
238
+ # Initializing invasion percolation for each possible cluster
239
+ for i in self._inlets:
240
+ if self._timing:
241
+ # # Calculate total volume in all inlet pores
242
+ self._cluster_data['throat_volume'][clusterNumber-1] = np.sum(self._net['throat.'+self._throat_volume_name][i])
243
+ # # Label all invaded pores with their cluster
244
+ # get the throats connecting the boundary pores to the inlet pores, then those are the first filled clusters
245
+ # if sp.in1d(i,bpores): #if ~sp.in1d(i,bpores): # don't need this if all inlet pores are boundary pores
246
+ bthroat = self._net.find_neighbor_throats(i) # throat entering inlet pore from boundary pore
247
+ self._Tinv[bthroat] = clusterNumber
248
+ self._Tinv_original[bthroat] = clusterNumber #self._Pinv_original[i] = clusterNumber
249
+ # Label all boundary-->inlet throats as invaded
250
+ self._tsequence[bthroat] = self._tseq
251
+ if self._timing:
252
+ self._Ttime[bthroat] = self._sim_time
253
+ mask_bpores = sp.vstack([sp.in1d(self._net.find_connected_pores(bthroat)[:,0],i),sp.in1d(self._net.find_connected_pores(bthroat)[:,1],i)])
254
+ interface_pore_numbers = self._net.find_connected_pores(bthroat)[~mask_bpores.T] # i #self._net.find_connected_pores(np.where(self._Tinv==clusterNumber)[0])
255
+ if self._timing:
256
+ # Sum all interfacial throats' volume coeffients for throat cap volume calculation
257
+ self._cluster_data['vol_coef'][clusterNumber-1] = np.sum(self._Pvol_coef[interface_pore_numbers]) # make -ve because heapq only grabs smallest value, so all Pc are -ve for imbibition
258
+ # Make a list of all entry pressures of the connected pores
259
+ # heapq only allows grabbing the smallest number from the heap, but we need the largest Pcap (smallest pore)
260
+ # python reccomends makign the heap -ve, opping the most negative number, then making it posotive (http://stackoverflow.com/questions/2501457/what-do-i-use-for-a-max-heap-implementation-in-python)
261
+ ''' Pc_entry as -ve (when calculated above)'''
262
+ ''' interface_pore_pressure, heap values, ['haines_pressure'], and ppoints stored as -ve '''
263
+ ''' Pvol_coeff, ['vol_coeff'], ['cap_volume'] are stored as +ve (ie multiply ['haines_pressure'] by -1 when calculating ie ['cap_volume'], etc.)'''
264
+ interface_pore_pressures = Pc_entry[interface_pore_numbers]
265
+ # Zip pressures and numbers together so that HeapQ can work its magic
266
+ logger.debug('interface pores(s) found:')
267
+ logger.debug(interface_pore_numbers)
268
+ logger.debug( 'interface pore pressure(s):')
269
+ logger.debug(interface_pore_pressures)
270
+ logger.debug( ' Negative Pcap is required for imbibition to work right')
271
+ Interface= list(zip(interface_pore_pressures,interface_pore_numbers))
272
+ # Interface= list(zip([interface_pore_pressures],[interface_pore_numbers]))
273
+ # Turn the zipped throat interfaces object into a heap
274
+ heapq.heapify(Interface)
275
+ # Add to the total list of interface throats in the system
276
+ self._plists.append(interface_pore_numbers.tolist())
277
+ # Add to the total list of invaded interface pores in the system
278
+ self._ppoints.append(Interface)
279
+ # Pop off the first entry (lowest pressure) on the throat info list
280
+ invaded_pore_info = Interface[0]
281
+ if self._timing:
282
+ # Determine pressure at Haines Jump
283
+ self._cluster_data['haines_pressure'][clusterNumber-1] = invaded_pore_info[0]
284
+ # Calculate cap_volume at Haines Jump (need Haines_pressure negative, to recover correct volume)
285
+ self._cluster_data['cap_volume'][clusterNumber-1] = -self._cluster_data['haines_pressure'][clusterNumber-1]*self._cluster_data['vol_coef'][clusterNumber-1]
286
+ # Calculate time at Haines Jump
287
+ self._cluster_data['haines_time'][clusterNumber-1] = (self._cluster_data['throat_volume'][clusterNumber-1]+
288
+ self._cluster_data['cap_volume'][clusterNumber-1])/self._cluster_data['flow_rate'][clusterNumber-1]
289
+ # Record invaded throat
290
+ self._cluster_data['haines_pore'][clusterNumber-1] = invaded_pore_info[1]
291
+ clusterNumber += 1
292
+ if self._timing:
293
+ logger.debug( 'throat volumes')
294
+ logger.debug(self._cluster_data['throat_volume'])
295
+ logger.debug( 'cap volumes')
296
+ logger.debug(self._cluster_data['cap_volume'])
297
+ logger.debug( 'max throat cap volumes')
298
+ logger.debug( self._Tvol_coef*self._phase.throat_conditions["Pc_entry"])
299
+ logger.debug( 'haines_pore')
300
+ logger.debug( self._cluster_data['haines_pore'])
301
+ # if self._timing:
302
+ # logger.debug( 'max throat cap volumes')
303
+ # logger.debug( self._Tvol_coef*self._phase.throat_conditions["Pc_entry"])
304
+ # self._tseq += 1
305
+ # self._pseq += 1
306
+ self._current_cluster = 0
307
+ # Calculate the distance between the inlet and outlet pores
308
+ self._outlet_position = np.average(self._net['pore.coords'][self._outlets],0)
309
+ # TODO for calculating the inlet position - should we use distance between invaded pore and outlet, or invading throat?
310
+ # for simplicity for now, using pore coords (averaged anyways)
311
+ inlet_position = np.average(self._net['pore.coords'][self._inlets],0)
312
+ dist_sqrd = (self._outlet_position-inlet_position)*(self._outlet_position-inlet_position)
313
+ self._initial_distance = np.sqrt(dist_sqrd[0]+dist_sqrd[1]+dist_sqrd[2])
314
+ logger.debug( 'initial distance')
315
+ logger.debug( self._initial_distance)
316
+ self._current_distance = self._initial_distance
317
+ self._percent_complete = np.round((self._initial_distance-self._current_distance)/self._initial_distance*100, decimals = 1)
318
+ logger.info( 'percent complete')
319
+ logger.info( self._percent_complete)
320
+ self._rough_complete = 0
321
+ print(' IP algorithm at',np.int(self._rough_complete),'% completion at',np.int(np.round(clock())),'seconds')
322
+ logger.debug( '+='*25)
323
+
324
+ def _do_outer_iteration_stage(self):
325
+ r"""
326
+ Executes the outer iteration stage
327
+ """
328
+ logger.info("Outer Iteration Stage ")
329
+ self._pseq = 1
330
+ self._tseq = 1
331
+ self._NewPore = -1
332
+ # Time keeper
333
+ self._sim_time = 0
334
+ self._setup_for_IP()
335
+ self._condition_update()
336
+ #self._Tinv = np.zeros(self._net.num_throats())
337
+ while self._condition:
338
+ self._do_one_outer_iteration()
339
+ self['pore.IP_inv_final']=np.ravel(np.array(self._Pinv,dtype=np.int))
340
+ self['pore.IP_inv_original']=np.ravel(np.array(self._Pinv_original,dtype=np.int))
341
+ self['throat.IP_inv']=np.ravel(np.array(self._Tinv,dtype=np.int))
342
+ self['pore.IP_inv_seq']=np.ravel(np.array(self._psequence,dtype=np.int))
343
+ self['throat.IP_inv_seq']=np.ravel(np.array(self._tsequence,dtype=np.int))
344
+ if self._timing:
345
+ self['pore.IP_inv_time']=np.ravel(np.array(self._Ptime,dtype=np.float))
346
+ self['throat.IP_inv_time']=np.ravel(np.array(self._Ttime,dtype=np.float))
347
+
348
+ def _do_one_outer_iteration(self):
349
+ r"""
350
+ One iteration of an outer iteration loop for an algorithm
351
+ (e.g. time or parametric study)
352
+ """
353
+ if (sp.mod(self._counter,500)==False):
354
+ logger.info("Outer Iteration (counter = "+str(self._counter)+")")
355
+ pass
356
+ self._do_inner_iteration_stage()
357
+ self._condition_update()
358
+ self._counter += 1
359
+
360
+ def _do_inner_iteration_stage(self):
361
+ r"""
362
+ Executes the inner iteration stage
363
+ """
364
+ logger.debug(" Inner Iteration Stage: ")
365
+
366
+ self._plast = len(np.nonzero(self._Pinv)[0])
367
+ if self._timing:
368
+ # determine the cluster with the earliest Haines time
369
+ self._current_cluster = 1 + self._cluster_data['haines_time'].tolist().index(min(self._cluster_data['haines_time']))
370
+ # update simulation clock
371
+ logger.debug( 'sim time = ')
372
+ logger.debug(self._sim_time)
373
+ logger.debug(' haines time:')
374
+ logger.debug( self._cluster_data['haines_time'])
375
+ # 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
376
+ self._sim_time = min(self._cluster_data['haines_time'])
377
+ logger.debug( 'sim time after update= ')
378
+ logger.debug(self._sim_time)
379
+ else:
380
+ # Cycle to the next active cluster
381
+ condition = 0
382
+ loop_count = 0
383
+ original_cluster = self._current_cluster
384
+ cnum = original_cluster+1
385
+ while condition == 0:
386
+ if cnum > self._clusterCount:
387
+ cnum = 1
388
+ if self._cluster_data['active'][cnum-1] == 1:
389
+ condition = 1
390
+ self._current_cluster = cnum
391
+ if cnum == original_cluster:
392
+ loop_count = loop_count+1
393
+ if loop_count > 1:
394
+ logger.error('No clusters active. Stuck in infinite loop.')
395
+ pass
396
+ cnum = cnum + 1
397
+
398
+ # run through the Haines Jump steps
399
+ self._do_one_inner_iteration()
400
+ self._pnew = len(np.nonzero(self._Pinv)[0])
401
+ self._tseq += 1
402
+ if self._pnew>self._plast:
403
+ self._pseq += 1
404
+
405
+
406
+ def _do_one_inner_iteration(self):
407
+ r"""
408
+ Executes one inner iteration
409
+ """
410
+ logger.debug(" Inner Iteration")
411
+ # Fill throat and connecting pore
412
+ # Pop out the largest pore (lowest Pcap) in the list, read the pore number
413
+ try:
414
+ pinvade = heapq.heappop(self._ppoints[self._current_cluster-1])[1]
415
+ except:
416
+ print('Something bad happened trying to invade')
417
+ import pdb
418
+ # pdb.set_trace()
419
+ logger.debug( ' ')
420
+ logger.debug( '--------------------------------------------------')
421
+ logger.debug( 'STEP')
422
+ logger.debug(self._tseq)
423
+ logger.debug( 'trying to access cluster: ')
424
+ logger.debug(self._current_cluster)
425
+ logger.debug( 'when these clusters are active active: ')
426
+ logger.debug(sp.nonzero(self._cluster_data['active'])[0])
427
+ logger.debug( 'Haines at pore,time: ')
428
+ logger.debug(pinvade)
429
+ if self._timing:
430
+ logger.debug(self._sim_time)
431
+ pass
432
+
433
+ # Mark pore as invaded
434
+ self._psequence[pinvade] = self._pseq
435
+ if self._timing:
436
+ self._Ptime[pinvade] = self._sim_time
437
+ # Remove throat's contribution to the vol_coef
438
+ self._cluster_data['vol_coef'][self._current_cluster-1] = self._cluster_data['vol_coef'][self._current_cluster-1]-self._Pvol_coef[pinvade]
439
+ # Mark throat as filled
440
+ AllThroats = self._net.find_neighbor_throats(pinvade) # this finds all connected throats, need to ignore the ones that are already filled
441
+ # 1. remove invading throat and any throat already in this cluster (already invaded and part of same cluster)
442
+ Throats = AllThroats[~sp.in1d(AllThroats,np.where(sp.in1d(self._Tinv,self._current_cluster))[0])]
443
+ # 2. fidn other throats with an interface
444
+ # TODO if there is another interface, then start or continue co-operative filling
445
+ # TODO need to add a cooperative filling method
446
+ ThroatsInv = Throats[sp.in1d(Throats,np.where(self._Tinv>0)[0])]
447
+ # if a throat is already invaded (the pore that was just invaded had another interface in it, so now we have cooperative filling)
448
+ # for each already invaded throat
449
+ if len(ThroatsInv):
450
+ # if this pore has multiple interfaces
451
+ self._NewPore = -1
452
+ self._NewThroat = -1
453
+ # Label invaded pore with smallest cluster number
454
+ # find all clusters connected to the newly invaded pore
455
+ clusters = self._cluster_data['transform'][self._Tinv[AllThroats][self._Tinv[AllThroats]>0]-1] # clusters = self._cluster_data['transform'][self._Tinv[ThroatsInv]-1]
456
+ logger.debug('clusters = ')
457
+ logger.debug(clusters)
458
+ # if some throats are from different clusters - ie if len(clusters)>1
459
+ if len(clusters)>1: #if self._Pinv[Pores[0]]!=self._Pinv[Pores[1]] :
460
+ csize = 0
461
+ maxCluster = []
462
+ for c in clusters: # count occurrences of each value in cluster, in Pinv to find largest cluster
463
+ if len(self._plists[c-1]) > csize:
464
+ csize = sum(clusters==c)
465
+ maxCluster = c
466
+ # find the largest cluster -- FOR IMBIBITION, MERGING ALL SMALLER CLUSTERS INTO THE LARGEST. DIFFERENT FROM DRAININAGE SO BE CAREFUL
467
+ # update the cluster transform to name all clusters as the max
468
+ self._current_cluster = maxCluster #[0]
469
+ self._Pinv[pinvade] = self._current_cluster
470
+ logger.info(' ')
471
+ logger.info('CLUSTERS COMBINING:')
472
+ logger.info(clusters)
473
+ logger.info('into')
474
+ logger.info(maxCluster)
475
+ if self._timing:
476
+ logger.info('at time')
477
+ logger.info(self._sim_time)
478
+ pass
479
+ for c in clusters[clusters!=self._current_cluster]: # go through the clusters as they are moved into the largest cluster
480
+ self._cluster_data['transform'][self._cluster_data['transform']==c] = self._current_cluster
481
+ # relabel all pores and throats from cluster c to largest
482
+ self._Pinv[np.where(self._Pinv==c)[0]] = self._current_cluster
483
+ self._Tinv[np.where(self._Tinv==c)[0]] = self._current_cluster
484
+ # append the list of throats for the other cluster to the current cluster
485
+ self._plists[self._current_cluster-1] = self._plists[self._current_cluster-1] + self._plists[c-1]
486
+ # delete the throat lists on the other cluster
487
+ self._plists[c-1] = []
488
+ # merge the heaps of throat information
489
+ self._ppoints[self._current_cluster-1] = list(heapq.merge(self._ppoints[self._current_cluster-1],self._ppoints[c-1]))
490
+ if self._timing:
491
+ # update the clusters' vol_coefs
492
+ self._cluster_data['vol_coef'][self._current_cluster-1] += self._cluster_data['vol_coef'][c-1]
493
+ self._cluster_data['vol_coef'][c-1] = 0
494
+ # update the clusters' pore volume
495
+ self._cluster_data['throat_volume'][self._current_cluster-1] += self._cluster_data['throat_volume'][c-1]
496
+ self._cluster_data['throat_volume'][c-1] = 0
497
+ # update the clusters' flowrates
498
+ self._cluster_data['flow_rate'][self._current_cluster-1] += self._cluster_data['flow_rate'][c-1]
499
+ self._cluster_data['flow_rate'][c-1] = 0
500
+ logger.debug( 'new flowrate for cluster ')
501
+ logger.debug(self._current_cluster)
502
+ logger.debug('is')
503
+ logger.debug(self._cluster_data['flow_rate'][self._current_cluster-1])
504
+ # check if either was inactive (broke through already)
505
+ if self._cluster_data['active'][c-1] + self._cluster_data['active'][self._current_cluster-1]<2:
506
+ logger.debug('making clusters ')
507
+ logger.debug(c)
508
+ logger.debug('and')
509
+ logger.debug(self._current_cluster)
510
+ logger.debug('inactive due to one being inactive already')
511
+ logger.debug(self._cluster_data['active'][c-1])
512
+ logger.debug(self._cluster_data['active'][self._current_cluster-1])
513
+ # self._cluster_data['active'][self._current_cluster-1] = 0
514
+ self._cluster_data['active'][c-1] = 0
515
+ if self._timing:
516
+ self._cluster_data['haines_time'][c-1] = 100000000000000000000000000000000
517
+ logger.info(' ')
518
+ logger.info('CLUSTER MERGED WITH A BREAKTHROUGH CLUSTER')
519
+ logger.info('making cluster ')
520
+ logger.info(c)
521
+ logger.info('inactive due to merge')
522
+ # update the old cluster's activity and time
523
+ if self._timing:
524
+ self._cluster_data['haines_time'][c-1] = 100000000000000000000000000000000
525
+ self._cluster_data['active'][c-1] = 0
526
+ # NO IDEA WHAT THIS LINE DOES PLEASE HELP MAHMOUD
527
+ #self._tpoints[self._current_cluster-1] = list(k for k,v in itertools.groupby(self._tpoints[self._current_cluster-1]))
528
+ self._ppoints[c-1] = []
529
+
530
+ # remove else and do over the length of throats that are not filled:
531
+ # label invaded pore with current cluster
532
+ self._Pinv[pinvade] = self._current_cluster
533
+ self._NewPore = pinvade
534
+
535
+ # go through list of new invaded throats and assign to this cluster
536
+ # first remove all ThroatsInv
537
+ Throats = Throats[~sp.in1d(Throats,ThroatsInv)]
538
+ for i in Throats:
539
+ # set univaded throats, NewThroats
540
+ self._NewThroat = i # self._NewPore = Pores[self._Pinv[Pores][:,0]==0][0]
541
+ Pores = self._net.find_connected_pores(i)
542
+ pneighbor = Pores[Pores!=pinvade][0]
543
+ # if it's a boundary throat/pore, print a warning and skip to next i in for loop
544
+ if 'pore.boundary' in self._net.labels(pores=[pneighbor]):
545
+ logger.debug( ' ')
546
+ logger.debug( 'Throat: ')
547
+ logger.debug(self._NewThroat)
548
+ logger.debug('connected to pore: ')
549
+ logger.debug(pneighbor)
550
+ logger.debug(' is a boundary throat. Ignoring and moving on...')
551
+ continue
552
+ logger.debug( ' ')
553
+ logger.debug( 'INVADING THROATS: ')
554
+ logger.debug(self._NewThroat)
555
+ logger.debug('connected to Pore: ')
556
+ logger.debug(pneighbor)
557
+ # label that throat as invaded
558
+ self._Tinv[self._NewThroat] = self._current_cluster
559
+ self._Tinv_original[self._NewThroat] = self._current_cluster
560
+ if self._timing:
561
+ self._Ttime[self._NewThroat] = self._sim_time
562
+ self._tsequence[self._NewThroat] = self._tseq
563
+ if self._timing:
564
+ # update self._cluster_data.['throat_volume']
565
+ self._cluster_data['throat_volume'][self._current_cluster-1] += self._net['throat.'+self._throat_volume_name][self._NewThroat]
566
+ # Get the pore that this throat connects to
567
+ # Update interface list
568
+ # If the pore is not labelled as invaded by the cluster, it must be an interfacial pore
569
+ if (pneighbor not in self._plists[self._current_cluster-1]): # need extra [] to make a list if only one entry but screw up if multiple entries!!!
570
+ logger.debug( 'new pore:')
571
+ logger.debug(pneighbor)
572
+ logger.debug('connected throats:')
573
+ logger.debug(self._net.find_neighbor_throats(pneighbor))
574
+ # Add this pore data (pressure, number) to this cluster's "heap" of throat data.
575
+ # TODO --> eventually, generalize to capillary_pressure_name
576
+ heapq.heappush(self._ppoints[self._current_cluster-1],(self._phase['pore.Pc_entryImb'][pneighbor],pneighbor))
577
+ # Add new pore number to throat list for this cluster
578
+ # TODO for now, a pore can be in multiple plists (ie not yet invaded, but ready and willing) -- need to watch this
579
+ self._plists[self._current_cluster-1].append(pneighbor)
580
+ if self._timing:
581
+ # Update the cluster's vol_coef
582
+ self._cluster_data['vol_coef'][self._current_cluster-1] = self._cluster_data['vol_coef'][self._current_cluster-1]+self._Pvol_coef[pneighbor]
583
+ # Find next Haines Jump info
584
+ # Make sure you are not re-invading a throat
585
+ if self._ppoints[self._current_cluster-1] != []:
586
+ while self._Pinv[self._ppoints[self._current_cluster-1][0][1]] > 0:
587
+ premove = heapq.heappop(self._ppoints[self._current_cluster-1])[1]
588
+ if self._timing:
589
+ self._cluster_data['vol_coef'][self._current_cluster-1] = self._cluster_data['vol_coef'][self._current_cluster-1]-self._Pvol_coef[premove]
590
+ if self._ppoints[self._current_cluster-1] == []:
591
+ logger.debug( 'making cluster ')
592
+ logger.debug(self._current_cluster)
593
+ logger.debug('inactive due to ppoints = [] ')
594
+ self._cluster_data['active'][self._current_cluster-1] = 0
595
+ break
596
+ if self._ppoints[self._current_cluster-1] != []:
597
+ next_pore = self._ppoints[self._current_cluster-1][0][1]
598
+ self._cluster_data['haines_pore'][self._current_cluster-1] = next_pore
599
+ if self._timing:
600
+ self._cluster_data['haines_pressure'][self._current_cluster-1] = self._ppoints[self._current_cluster-1][0][0]
601
+ 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] # PCAP!!
602
+
603
+ # Calculate the new Haines jump time
604
+ logger.debug( 'haines time before last stage:')
605
+ logger.debug( self._cluster_data['haines_time'])
606
+ if self._ppoints[self._current_cluster-1] == []:
607
+ logger.debug('making cluster ')
608
+ logger.debug(self._current_cluster)
609
+ logger.debug('inactive due to self._ppoints being empty for that cluster')
610
+ self._cluster_data['active'][self._current_cluster-1] = 0
611
+ if self._timing:
612
+ self._cluster_data['haines_time'][self._current_cluster-1] = 100000000000000000000000000000000
613
+ if self._timing:
614
+ if self._cluster_data['active'][self._current_cluster-1] == 1:
615
+ self._cluster_data['haines_time'][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]
616
+ if self._cluster_data['haines_time'][self._current_cluster-1] < self._sim_time:
617
+ self._cluster_data['haines_time'][self._current_cluster-1] = self._sim_time
618
+ logger.debug('haines time at the end of the pore stuff')
619
+ logger.debug(self._cluster_data['haines_time'])
620
+
621
+ def _condition_update(self):
622
+ # Calculate the distance between the new pore and outlet pores
623
+ if self._end_condition == 'breakthrough':
624
+ newpore_position = self._net['pore.coords'][self._NewPore]
625
+ dist_sqrd = (self._outlet_position-newpore_position)*(self._outlet_position-newpore_position)
626
+ if dist_sqrd[0].shape==(3,): # need to do this for MatFile networks because newpore_position is a nested array, not a vector (?)
627
+ dist_sqrd = dist_sqrd[0]
628
+ newpore_distance = np.sqrt(dist_sqrd[0]+dist_sqrd[1]+dist_sqrd[2])
629
+ logger.debug( 'newpore distance')
630
+ logger.debug( newpore_distance)
631
+ if newpore_distance < self._current_distance:
632
+ self._percent_complete = np.round((self._initial_distance-newpore_distance)/self._initial_distance*100, decimals = 1)
633
+ logger.info( 'percent complete')
634
+ logger.info( self._percent_complete)
635
+ self._current_distance = newpore_distance
636
+ elif self._end_condition == 'total':
637
+ self._percent_complete = np.round((np.sum(self._Pinv>0)/self._net.num_pores())*100, decimals = 1)
638
+ if self._percent_complete > self._rough_complete + self._rough_increment:
639
+ self._rough_complete = np.floor(self._percent_complete/self._rough_increment)*self._rough_increment
640
+ print(' IP algorithm at',np.int(self._rough_complete),'% completion at',np.int(np.round(clock())),'seconds')
641
+
642
+
643
+ # Determine if a new breakthrough position has occured
644
+ if self._NewPore in self._outlets:
645
+ logger.info( ' ')
646
+ logger.info( 'BREAKTHROUGH AT PORE: ')
647
+ logger.info(self._NewPore)
648
+ logger.info('in cluster ')
649
+ logger.info(self._current_cluster)
650
+ if self._timing:
651
+ logger.info('at time')
652
+ logger.info(self._sim_time)
653
+ pass
654
+ if self._end_condition == 'breakthrough':
655
+ self._cluster_data['active'][self._current_cluster-1] = 0
656
+ if self._timing:
657
+ self._cluster_data['haines_time'][self._current_cluster-1] = 100000000000000000000000000000000
658
+ elif self._end_condition == 'total':
659
+ self._brkevent.append(self._NewPore)
660
+ # if self._end_condition == 'total':
661
+ if np.sum(self._cluster_data['active']) == 0:
662
+ logger.info( ' ')
663
+ logger.info( 'SIMULATION FINISHED; no more active clusters')
664
+ if self._timing:
665
+ logger.info('at time')
666
+ logger.info(self._sim_time)
667
+ pass
668
+ self._condition = 0
669
+ print(' IP algorithm at 100% completion at ',np.int(np.round(clock())),' seconds')
670
+ # TODO Need to check how total condition will work, and end. All pores or all throats?
671
+ # self._condition = not self._Tinv.all()
672
+
673
+ def return_results(self,occupancy='occupancy',IPseq='None'):
674
+ r"""
675
+ """
676
+ if IPseq=='None':
677
+ IPseq = self._pseq
678
+
679
+ try:
680
+ self._phase['pore.'+occupancy]=np.ravel(np.array(((self._psequence>0)&(self._psequence<=IPseq)),dtype=np.float))
681
+ self._phase['throat.'+occupancy]=np.ravel(np.array(((self._tsequence>0)&(self._tsequence<=IPseq)),dtype=np.float))
682
+ except:
683
+ print('Something bad happened while trying to update phase',self._phase.name)
684
+ try:
685
+ self._phase_def['pore.'+occupancy]=np.ravel(np.array(~((self._psequence>0)&(self._psequence<=IPseq)),dtype=np.float))
686
+ self._phase_def['throat.'+occupancy]=np.ravel(np.array(~((self._tsequence>0)&(self._tsequence<=IPseq)),dtype=np.float))
687
+ except:
688
+ print('A partner phase has not been set so inverse occupancy cannot be set')
689
+
690
+ if IPseq==self._pseq:
691
+ self._phase['pore.IP_inv_final']=np.ravel(np.array(self._Pinv,dtype=np.int))
692
+ self._phase['pore.IP_inv_original']=np.ravel(np.array(self._Pinv_original,dtype=np.int))
693
+ self._phase['throat.IP_inv']=np.ravel(np.array(self._Tinv,dtype=np.int))
694
+ self._phase['pore.IP_inv_seq']=np.ravel(np.array(self._psequence,dtype=np.int))
695
+ self._phase['throat.IP_inv_seq']=np.ravel(np.array(self._tsequence,dtype=np.int))
696
+ if self._timing:
697
+ self._phase['pore.IP_inv_time']=np.ravel(np.array(self._Ptime,dtype=np.float))
698
+ self._phase['throat.IP_inv_time']=np.ravel(np.array(self._Ttime,dtype=np.float))
699
+
700
+ if __name__ == '__main__':
701
+ import doctest
702
+ doctest.testmod(verbose=True)
703
+