def set_residual(self, pores=[], overwrite=False): r""" Method to start invasion in a network w. residual saturation. Called after inlets are set. Parameters ---------- pores : array_like The pores locations that are to be filled with invader at the beginning of the simulation. overwrite : boolean If ``True`` then all existing inlet locations will be removed and then the supplied locations will be added. If ``False``, then supplied locations are added to any already existing locations. Notes ----- Currently works for pores only and treats inner throats, i.e. those that connect two pores in the cluster as invaded and outer ones as uninvaded. Uninvaded throats are added to a new residual cluster queue but do not start invading independently if not connected to an inlet. Step 1. Identify clusters in the phase occupancy. Step 2. Look for clusters that are connected or contain an inlet Step 3. For those that are merge into inlet cluster. May be connected to more than one - run should sort this out Step 4. For those that are isolated set the queue to not invading. Step 5. (in run) When isolated cluster is met my invading cluster it merges in and starts invading """ Ps = self._parse_indices(pores) if overwrite: self['pore.residual'] = False self['pore.residual'][Ps] = True residual = self['pore.residual'] net = self.project.network conns = net['throat.conns'] rclusters = site_percolation(conns, residual).sites rcluster_ids = np.unique(rclusters[rclusters > -1]) initial_num = len(self.queue)-1 for rcluster_id in rcluster_ids: rPs = rclusters == rcluster_id existing = np.unique(self['pore.cluster'][rPs]) existing = existing[existing > -1] if len(existing) > 0: # There was at least one inlet cluster connected to this # residual cluster, pick the first one. cluster_num = existing[0] else: # Make a new cluster queue cluster_num = len(self.queue) self.queue.append([]) queue = self.queue[cluster_num] # Set the residual pores and inner throats as part of cluster self['pore.cluster'][rPs] = cluster_num Ts = net.find_neighbor_throats(pores=rPs, flatten=True, mode='xnor') self['throat.cluster'][Ts] = cluster_num self['pore.invasion_sequence'][rPs] = 0 self['throat.invasion_sequence'][Ts] = 0 self['pore.invasion_pressure'][rPs] = -np.inf self['throat.invasion_pressure'][Ts] = -np.inf # Add all the outer throats to the queue Ts = net.find_neighbor_throats(pores=rPs, flatten=True, mode='exclusive_or') for T in Ts: data = [] # Pc data.append(self['throat.entry_pressure'][T]) # Element Index data.append(T) # Element Type (Pore of Throat) data.append('throat') hq.heappush(queue, data) self.invasion_running = [True]*len(self.queue) # we have added new clusters that are currently isolated and we # need to stop them invading until they merge into an invading # cluster for c_num in range(len(self.queue)): if c_num > initial_num: self.invasion_running[c_num] = False
def set_residual(self, pores=[], overwrite=False): r""" Method to start invasion in a network w. residual saturation. Called after inlets are set. Parameters ---------- pores : array_like The pores locations that are to be filled with invader at the beginning of the simulation. overwrite : boolean If ``True`` then all existing inlet locations will be removed and then the supplied locations will be added. If ``False``, then supplied locations are added to any already existing locations. Notes ----- Currently works for pores only and treats inner throats, i.e. those that connect two pores in the cluster as invaded and outer ones as uninvaded. Uninvaded throats are added to a new residual cluster queue but do not start invading independently if not connected to an inlet. Step 1. Identify clusters in the phase occupancy. Step 2. Look for clusters that are connected or contain an inlet Step 3. For those that are merge into inlet cluster. May be connected to more than one - run should sort this out Step 4. For those that are isolated set the queue to not invading. Step 5. (in run) When isolated cluster is met my invading cluster it merges in and starts invading """ Ps = self._parse_indices(pores) if overwrite: self['pore.residual'] = False self['pore.residual'][Ps] = True residual = self['pore.residual'] net = self.project.network conns = net['throat.conns'] rclusters = site_percolation(conns, residual).sites rcluster_ids = np.unique(rclusters[rclusters > -1]) initial_num = len(self.queue)-1 for rcluster_id in rcluster_ids: rPs = rclusters == rcluster_id existing = np.unique(self['pore.cluster'][rPs]) existing = existing[existing > -1] if len(existing) > 0: # There was at least one inlet cluster connected to this # residual cluster, pick the first one. cluster_num = existing[0] else: # Make a new cluster queue cluster_num = len(self.queue) self.queue.append([]) queue = self.queue[cluster_num] # Set the residual pores and inner throats as part of cluster self['pore.cluster'][rPs] = cluster_num Ts = net.find_neighbor_throats(pores=rPs, flatten=True, mode='xnor') self['throat.cluster'][Ts] = cluster_num self['pore.invasion_sequence'][rPs] = 0 self['throat.invasion_sequence'][Ts] = 0 self['pore.invasion_pressure'][rPs] = -np.inf self['throat.invasion_pressure'][Ts] = -np.inf # Add all the outer throats to the queue Ts = net.find_neighbor_throats(pores=rPs, flatten=True, mode='exclusive_or') for T in Ts: data = [] # Pc data.append(self['throat.entry_pressure'][T]) # Element Index data.append(T) # Element Type (Pore of Throat) data.append('throat') hq.heappush(queue, data) self.invasion_running = [True]*len(self.queue) # we have added new clusters that are currently isolated and we # need to stop them invading until they merge into an invading # cluster for c_num in range(len(self.queue)): if c_num > initial_num: self.invasion_running[c_num] = False
def run(self, points=25, start=None, stop=None): r""" Runs the percolation algorithm to determine which pores and throats will be invaded at each given pressure point. Parameters ---------- points: int or array_like An array containing the pressure points to apply. If a scalar is given then an array will be generated with the given number of points spaced between the lowest and highest values of throat entry pressures using logarithmic spacing. To specify low and high pressure points use the ``start`` and ``stop`` arguments. start : int The optional starting point to use when generating pressure points. If not given the half the lowest capillary entry pressure in the network is used. stop : int The optional stopping point to use when generating pressure points. If not given, then twice the highest capillary entry pressure in the network is used. Notes ----- The inlet sites are set to invaded to start the simulation. This means that if 'internal' pores are used as inlets the capillary pressure curve will begin at a non-zero invading phase saturation. To avoid this either set the inlet pore volumes to zero or add boundary pores to the inlet face, and set their volumes to zero. """ phase = self.project[self.settings.phase] # Parse inputs and generate list of invasion points if necessary if self.settings['mode'] == 'bond': self['throat.entry_pressure'] = \ phase[self.settings['throat_entry_threshold']] if start is None: start = np.amin(self['throat.entry_pressure'])*0.5 if stop is None: stop = np.amax(self['throat.entry_pressure'])*2.0 elif self.settings['mode'] == 'site': self['pore.entry_pressure'] = \ phase[self.settings['pore_entry_threshold']] if start is None: start = np.amin(self['pore.entry_pressure'])*0.5 if stop is None: stop = np.amax(self['pore.entry_pressure'])*2.0 else: raise Exception('Percolation type has not been set') if isinstance(points, int): points = np.logspace(start=np.log10(max(1, start)), stop=np.log10(stop), num=points) self._points = points # Ensure pore inlets have been set IF access limitations is True if self.settings['access_limited']: if np.sum(self['pore.inlets']) == 0: raise Exception('Inlet pores must be specified first') else: Pin = self['pore.inlets'] # Generate curve from points conns = self.project.network['throat.conns'] for inv_val in points: if self.settings['mode'] == 'bond': t_invaded = self['throat.entry_pressure'] <= inv_val labels = bond_percolation(conns, t_invaded) elif self.settings['mode'] == 'site': p_invaded = self['pore.entry_pressure'] <= inv_val labels = site_percolation(conns, p_invaded) # Optionally remove clusters not connected to the inlets if self.settings['access_limited']: labels = remove_isolated_clusters(labels=labels, inlets=Pin) # Store current applied pressure in newly invaded pores pinds = (self['pore.invasion_pressure'] == np.inf) * \ (labels.sites >= 0) self['pore.invasion_pressure'][pinds] = inv_val # Store current applied pressure in newly invaded throats tinds = (self['throat.invasion_pressure'] == np.inf) * \ (labels.bonds >= 0) self['throat.invasion_pressure'][tinds] = inv_val # Convert invasion pressures in sequence values Pinv = self['pore.invasion_pressure'] Tinv = self['throat.invasion_pressure'] Pseq = np.searchsorted(np.unique(Pinv), Pinv) Tseq = np.searchsorted(np.unique(Tinv), Tinv) self['pore.invasion_sequence'] = Pseq self['throat.invasion_sequence'] = Tseq
def run(self, points=25, start=None, stop=None): r""" Runs the percolation algorithm to determine which pores and throats will be invaded at each given pressure point. Parameters ---------- points: int or array_like An array containing the pressure points to apply. If a scalar is given then an array will be generated with the given number of points spaced between the lowest and highest values of throat entry pressures using logarithmic spacing. To specify low and high pressure points use the ``start`` and ``stop`` arguments. start : int The optional starting point to use when generating pressure points. If not given the half the lowest capillary entry pressure in the network is used. stop : int The optional stopping point to use when generating pressure points. If not given, then twice the highest capillary entry pressure in the network is used. Note ---- The inlet sites are set to invaded to start the simulation. This means that if 'internal' pores are used as inlets the capillary pressure curve will begin at a non-zero invading phase saturation. To avoid this either set the inlet pore volumes to zero or add boundary pores to the inlet face, and set their volumes to zero. """ phase = self.project.find_phase(self) # Parse inputs and generate list of invasion points if necessary if self.settings['mode'] == 'bond': self['throat.entry_pressure'] = \ phase[self.settings['throat_entry_threshold']] if start is None: start = sp.amin(self['throat.entry_pressure'])*0.5 if stop is None: stop = sp.amax(self['throat.entry_pressure'])*2.0 elif self.settings['mode'] == 'site': self['pore.entry_pressure'] = \ phase[self.settings['pore_entry_threshold']] if start is None: start = sp.amin(self['pore.entry_pressure'])*0.5 if stop is None: stop = sp.amax(self['pore.entry_pressure'])*2.0 else: raise Exception('Percolation type has not been set') if type(points) is int: points = sp.logspace(start=sp.log10(max(1, start)), stop=sp.log10(stop), num=points) self._points = points # Ensure pore inlets have been set IF access limitations is True if self.settings['access_limited']: if sp.sum(self['pore.inlets']) == 0: raise Exception('Inlet pores must be specified first') else: Pin = self['pore.inlets'] # Generate curve from points conns = self.project.network['throat.conns'] for inv_val in points: if self.settings['mode'] == 'bond': t_invaded = self['throat.entry_pressure'] <= inv_val labels = bond_percolation(conns, t_invaded) elif self.settings['mode'] == 'site': p_invaded = self['pore.entry_pressure'] <= inv_val labels = site_percolation(conns, p_invaded) # Optionally remove clusters not connected to the inlets if self.settings['access_limited']: labels = remove_isolated_clusters(labels=labels, inlets=Pin) # Store current applied pressure in newly invaded pores pinds = (self['pore.invasion_pressure'] == sp.inf) * \ (labels.sites >= 0) self['pore.invasion_pressure'][pinds] = inv_val # Store current applied pressure in newly invaded throats tinds = (self['throat.invasion_pressure'] == sp.inf) * \ (labels.bonds >= 0) self['throat.invasion_pressure'][tinds] = inv_val # Convert invasion pressures in sequence values Pinv = self['pore.invasion_pressure'] Tinv = self['throat.invasion_pressure'] Pseq = sp.searchsorted(sp.unique(Pinv), Pinv) Tseq = sp.searchsorted(sp.unique(Tinv), Tinv) self['pore.invasion_sequence'] = Pseq self['throat.invasion_sequence'] = Tseq