[docs]@docstr.get_sections(base='Algorithm',sections=['Parameters'])@docstr.dedentclassAlgorithm(ParserMixin,LabelMixin,Base2):r""" Generic class to define the foundation of Algorithms Parameters ---------- %(Base.parameters)s """def__init__(self,network,name='alg_?',**kwargs):super().__init__(network=network,name=name,**kwargs)self.settings._update(AlgorithmSettings())self['pore.all']=np.ones([network.Np,],dtype=bool)self['throat.all']=np.ones([network.Nt,],dtype=bool)# @functools.cached_property@propertydefiterative_props(self):r""" Finds and returns properties that need to be iterated while running the algorithm. """importnetworkxasnxphase=self.project[self.settings.phase]# Generate global dependency graphdg=nx.compose_all([x.models.dependency_graph(deep=True)forxin[phase]])variable_props=self.settings["variable_props"].copy()variable_props.add(self.settings["quantity"])base=list(variable_props)# Find all props downstream that depend on base propsdg=nx.DiGraph(nx.edge_dfs(dg,source=base))iflen(dg.nodes)==0:return[]iterative_props=list(nx.dag.lexicographical_topological_sort(dg))# "variable_props" should be in the returned list but not "quantity"ifself.settings.quantityiniterative_props:iterative_props.remove(self.settings["quantity"])returniterative_propsdef_update_iterative_props(self,iterative_props=None):""" Regenerates phase, geometries, and physics objects using the current value of ``quantity``. Notes ----- The algorithm directly writes the value of 'quantity' into the phase, which is against one of the OpenPNM rules of objects not being able to write into each other. """ifiterative_propsisNone:iterative_props=self.iterative_propsifnotiterative_props:return# Fetch objects associated with the algorithmphase=self.project[self.settings.phase]# Update 'quantity' on phase with the most recent valuequantity=self.settings['quantity']phase[quantity]=self.x# Regenerate all associated objectsphase.regenerate_models(propnames=iterative_props)
[docs]defclear_BCs(self,bctype=[]):r""" Clear all BCs of the given type(s) Parameters ---------- bctype : str or list(str) The specific type of boundary condition to clear. If not provided all types will be cleared. """self.set_BC(pores=None,bctype=bctype,mode='remove')
[docs]defset_BC(self,pores=None,bctype=[],bcvalues=[],mode='add'):r""" The main method for setting and adjusting boundary conditions. This method is called by other more convenient wrapper functions like ``set_value_BC``. Parameters ---------- pores : array_like The pores where the boundary conditions should be applied. If ``None`` is given then *all* pores are assumed. This is useful when ``mode='remove'``. bctype : str Specifies the type or the name of boundary condition to apply. This can be anything, but normal options are 'rate' and 'value'. If an empty list is provided, then all bc types will be assumed. This is useful for clearing all bcs if ``mode='remove'`` and ``pores= None``. bcvalues : int or array_like The boundary value to apply, such as concentration or rate. If a single value is given, it's assumed to apply to all locations. Different values can be applied to all pores in the form of an array of the same length as ``pores``. Note that using ``mode='add'`` and ``values=np.nan`` is equivalent to removing bcs from the given ``pores``. mode : str or list of str, optional Controls how the boundary conditions are applied. Options are: ============ ===================================================== mode meaning ============ ===================================================== 'add' (default) Adds the supplied boundary conditions to the given locations. Raises an exception if values of any type already exist in the given locations. 'overwrite' Adds supplied boundary conditions to the given locations, including overwriting conditions of the given type or any other type that may be present in the given locations. 'remove' Removes boundary conditions of the specified type from the specified locations. If ``bctype`` is not specified then *all* types are removed. If no locations are given then values are remvoed from *all* locations. ============ ===================================================== If a list of strings is provided, then each mode in the list is handled in order, so that ``['remove', 'add']`` will give the same results add ``'overwrite'``. Notes ----- It is not possible to have multiple boundary conditions for a specified location in one algorithm. """# If a list of modes was given, handle them each in orderifnotisinstance(mode,str):foriteminmode:self.set_BC(pores=pores,bctype=bctype,bcvalues=bcvalues,mode=item)return# If a list of bctypes was given, handle them each in orderiflen(bctype)==0:bctype=self['pore.bc'].keys()ifnotisinstance(bctype,str):foriteminbctype:self.set_BC(pores=pores,bctype=item,bcvalues=bcvalues,mode=mode)return# Begin methodbc_types=list(self['pore.bc'].keys())other_types=np.setdiff1d(bc_types,bctype).tolist()mode=self._parse_mode(mode,allowed=['overwrite','add','remove'],single=True)# Infer the value that indicates "no bc" based on array dtypeno_bc=get_no_bc(self[f'pore.bc.{bctype}'])ifporesisNone:pores=self.Pspores=self._parse_indices(pores)# Deal with size of the given bcvaluesvalues=np.array(bcvalues)ifvalues.size==1:values=np.ones_like(pores,dtype=values.dtype)*values# Ensure values and pores are the same sizeifvalues.size>1andvalues.size!=pores.size:raiseException('The number of values must match the number of locations')# Finally adjust the BCs according to modeifmode=='add':mask=np.ones_like(pores,dtype=bool)# Indices of pores to keepforiteminself['pore.bc'].keys():# Remove pores that are takenmask[isfinite(self[f'pore.bc.{item}'][pores])]=Falseifnotnp.all(mask):# Raise exception if some conflicts foundmsg="Some of the given locations already have BCs, " \
+"either use mode='remove' first or " \
+"use mode='overwrite' instead"raiseException(msg)self[f"pore.bc.{bctype}"][pores[mask]]=values[mask]elifmode=='overwrite':# Put given values in specified BC, sort out conflicts belowself[f"pore.bc.{bctype}"][pores]=values# Collect indices that are present for other BCs for removalmask=np.ones_like(pores,dtype=bool)foriteminother_types:self[f"pore.bc.{item}"][pores]=get_no_bc(self[f"pore.bc.{item}"])# Make a note of any BCs values of other typesmask[isfinite(self[f'pore.bc.{item}'][pores])]=Falseifnotnp.all(mask):# Warn that other values were overwrittenmsg='Some of the given locations already have BCs of ' \
+'another type, these will be overwritten'logger.warning(msg)elifmode=='remove':self[f"pore.bc.{bctype}"][pores]=no_bc