PKsPopenpnm-master/.buildinfo# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 22cacac7bbf6d490fb192cd8cd2583fe
tags: 0957a7f5604f7fa265ade309e7b795c2
PKsPopenpnm-master/objects.inv# Sphinx inventory version 2
# Project: OpenPNM
# Version:
# The remainder of this file is compressed using zlib.
x]Ks㶖WJw9b&봧s2N{b"! 1I0(/|S@EI+XVekD*pPo.iSN;N>Cv)EGcCgJs@D@$F7Ǐ"q!SVeQBs)<.^]R6M`Y#J%%F lY"*mL,a:!O"iLoeS-s9R>נ$bÔ+N@UWVC,.YuT56&8m9k@f\3ؓr'QiZG|y.M
a<:9p!BIƂ1"lU182F%dE~4ҦvKcE^MݔAKD}m7iZéZ<$g}9YIj.[x]7}#e
-n}1sU6",$#&!h8g1au*V(Fe:e4EO4g־8#2tn\yh@:vVҮT}R}ЯP=;ď1Wd^,Yx
@Mȓ#sǎٞխ^
Pp,)
m݄ͽo_EVYPCpS=/FC@!nלH>)e%BXXtOX[@p@9+
hD!&O,1/H|ĭި?
>J.;ZP@v#L`7"rPCCZ4D{a" +974JRX'k̽Ჹ;YL'I
/JW*aH5f +_|)<'#Noo&g}L'bȋj.Kg2&T=In]
9XW}CC\&h,IR&O/{RCڒe-5/OO=
ضړLFͮQ~ynMAp,90yrn |j1ľME1HX\,!Lzk\$Y4RNٵahK?ӢsֱZdWX$5qKV3r3XȉSsoKP鯬#&
OpenPNM is an open source project aiming to provide porous media researchers with a ready-made framework for performing a wide range of pore network simulations.
Highlight
The image below was extracted using the SNOW algorithm from PoreSpy, then
imported into OpenPNM for use, then exported to Paraview for visualization.
OpenPNM relies on the Scipy Stack, which includes scipy, numpy, matplotlib, pandas, and many other useful scientific packages. These packages are extremely tedious to install from source (i.e. doing pipinstallnumpy is a terrible idea). It is highly recommended to first download and install the anaconda distribution for your system. Be sure to get the latest Python 3 version. Once this is installed, you will then have a fully functioning and highly optimized compiled versions of numpy, scipy and the other great packages, and you’ll be ready to use OpenPNM.
OpenPNM can be installed using pip, as follows, which downloads the latest stable release from the Python Package Index:
pipinstallopenpnm
This will install OpenPNM into your Python environment.
To upgrade your OpenPNM to a newer version, use pipinstall--upgradeopenpnm.
Alternatively, you might wish to use the latest development version, and perhaps to edit the source code yourself. In this case, you should download or clone the repository from Github, and save it to a preferred location on your local machine. Once this is done you can install from the git repository using:
pipinstall-e"path/to/downloaded/git/repo"
The -e commands make the installation editable, so that any changes made the source files will be available the next time OpenPNM is imported. This is useful for switch branches in the Git repo, or for adding new algorithms to a custom branch.
The resulting network can be quickly visualized using plot_coordinates and
plot_connections in the Topotools module to get the following:
The network module has a number of network types to chose from, and they can be found on the Network page. You can also import networks from various outside sources, include networks that have been extracted from tomographic images using our PoreSpy Package.
The Network only contains spatial information (pore coordinates) and topological information (throat connections), so it is necessary to add geometrical information by creating a Geometry object:
In this case the StickAndBall class was used, which has preset pore-scale models that calculate properties such as pore diameters and throat lengths, based on the assumption that the pores are spherical and the throats are cylinders.
Note
The Network (pn) was passed to the Geometry class as an argument, so that geo knows which network it’s associated with.
the Geometry was assigned to specified pores (Ps) and throats (Ts), in this case it was ‘all’ of them but it’s possible to use several different Geometry objects for different subsets of the domain.
OpenPNM includes a few common phases, including Mercury, Air and Water, but also a set of pore-scale models for calculating properties of different phases.
Note
Phase objects are associated with a Network, but they are not assigned to specific pores and throats. This is because phases can exist anywhere and everywhere in the domain, and can move around.
The GenericPhysics class was used, which has NO pore-scale models attached. We will add this manually in the next step.
Note
The Network must be given as an argument so that the Physics knows which network it’s associated with
One Physics object is required for each Phase, since physics models require thermophysical properties. For example, the Hagan-Poisseuille equation requires the viscosity of the phase.
Each Physics object is also associated with a Geometry. The reason for this is to assign different pore-scale physics models to different regions. In this case both are associated with geo since there is only one Geometry in the whole domain.
We must assign models to each of our Physics. The hg phase will be used to simulate a Porosimetry experiment, so it needs a capillary pressure model, and h2o will be used in a permeability simulation so we must define a hydraulic conductance. The following shows how to fetch models from the models library, attach them to the target object, and specify the model parameters:
We are now ready to conduct some simulations. The most important step in validating a pore network model is to ensure that it reproduces experimentally measured porosimetry curves and absolute permeability.
The above code solves for the pressure in each pore and stores the result as perm['pore.pressure']. To find the permeability of the network, there is a calc_eff_permeability method on the StokeFlow class. Start by telling the algorithm the area and length of the domain (unfortunately there is no sure way to get these accurately, though values will be guesstimated if not provided)
Finally to run the algorithm and calculate network permeability use:
perm.run()K=perm.calc_effective_permeability()
Note
The calc_eff_permeability method finds K by inverting Darcy’s law, and looking up all the necessary information (pressure drop, viscosity) from the objects which the algorithm is associated.
If the domain area and length are not given, an attempt is made to estimate them but it’s more accurate to provide it.
Now that the simulation is finished, it can be saved to a .pnm file for future use. OpenPNM has two levels of management: the Workspace and the Project. Each Project contains a single network and its associated object (all the code in this guide are a single Project). The Workspace contains all the active Projects. You can save the entire Workspace including all active Projects, or you can save a single Project.
Each object has a project attribute which returns a handle to the Project to which it belongs.
>>> proj=pn.project# Retrieve the project handle
The Project object offers several useful tools, including the ability to export_data to various formats, including VTK for viewing in Paraview. Using Paraview provides much better visualization than the plot_connections and plot_coordinates used above:
Examples of performing specific tasks in OpenPNM are given in a folder on the main Github repo. The example files are tested using continuous integration so we can alerted if an example has a typo or flaw. Github also renders Jupyter Notebooks, so all the examples are given as notebooks which can be downloaded and altered if desired.
This tutorial will show step by step how to plot the networks into Paraview. This post-processing software is open-source and can be downloaded from the Paraview Website.
Now we can export it into a variety of formats using the io module, but the
VTK format is easiest for Paraview.
Note
The XDMF format is actually must better for using in Paraview if the network
is of appreciable size since it’s performance is far better, but it is
slightly more complicated since it splits the data into 2 files. The
information in this tutorial applies equally well to both formats.
This will create a file in your current working directory called ‘test_file.vtp’. There are several things to note about this line. Firstly, the ‘vtp’ file extension is used rather than ‘vtk’. The last letter indicates the type of data in the file, where ‘p’ indicates PolyData. The VTK accronym stands for Visualization Tool Kit, which is a general reference to all similar files. Secondly, current working directory can be found from the interactive python terminal by importing the os package, and typing os.get_cwd(). Finally, sending Phase data to the VTK file is optional, and you must specify which phase data to write.
Once the OpenPNM script (run_script.py) have been run, a output file named net.vtp is created. This file can be opened in Paraview and contains all the parameters computed by OpenPNM. A summary of them is seen under the Properties bar. Once the file is open, click on Apply, this action should be done after each modification.
Open the ‘test_file.vtp’ file using the normal GUI functions in Paraview:
Click and apply, the network should be seen in the view port looking somthing like this:
To visualize the pore data, we need to add some glyphs. First click on the Glyph button in the tool bar. Then, you can plot the pore data as spheres, where their size and/or their color can be mapped to some variables. In the images below spherical glyphs are assigned to the pores, where the diameter is linked to the pore diameters and the color to the concentration. Clicking on the Apply button renders these settings.
Plotting throats as nicely oriented cylinders with controlled diameter and color remains an annoyance, although we are working on getting the necessary information into the VTK file for throats be added as simply as adding cylindrical glyphs.
To visualize the throat data (like diameters or molar flow rates) we need to set up the following filters in Paraview. First, use the Shrink filter and set it up to 1. Then, the cell data needs to be transposed to the data point with CellDatatoPointdata filter. Then extract the surface with the filter ExtractSurface. Finally, the data may be plotted as tube by using the Tube filter. As previously for the pore data, either the Tube radius or color can be linked to throat data.
Throat data plotted with tubes radius proportional to throat diameter and tubes color linked to the throat mole rate:
One of the main objectives of V2 was to ‘reorganize’ the package. When we started building OpenPNM in 2012 we were new to Python and made some errors in the layout of the code that we fixed in V2. Specifically, V2 fully adopts the pep8 naming conventions, which suggests that modules be named with lowercase while classes be uppercase.
# Old wayimportOpenPNMasoppn=op.Network.Cubic(shape=[10,10,10])# New wayimportopenpnmasoppn=op.network.Cubic(shape=[10,10,10])
Note that the package itself was renamed to lowercase, as well as all the module names. The tables below give an overview of the package comparing V1 and V2:
The package itself was renamed using all lowercase letters:
Version 1
Version 2
OpenPNM
openpnm
All the submodules were renamed using lowercase letters:
Version 1
Version 2
Network
network
Phases
phases
Geometry
geometry
Physics
physics
Algorithms
algorithms
Utilities
utils
The terms Base and Core were switched; the Base module is now called core and the Core class is now Base. This is more consistent with other packages, where all the ‘core’ classes are stored in a core module, and the ‘Base’ class is the main class from which all other classes inherit (e.g. Cubic <– GenericNetwork <– Base):
Version 1
Version 2
Base
core
Base.Core
core.Base
Pore-scale models are now stored in a top-level module to improve findability:
Version 1
Version 2
Phases.models
models.phases
Geometry.models
models.geometry
Physics.models
models.physics
A new module was added called topotools to house all functions pertaining to manipulating and mangling the topology of the networks. All functions formerly found under models and/or tools have been moved:
Version 1
Version 2
Network.models
removed
Network.tools
topotools
Finally, the Postprocessing module was removed and the few functions it had worth keeping have been relocated:
In V2, we have made an effort to reduce the number of methods on each class, particularly GenericNetwork. Many of the functions that were previously found on the various networks (trim, extend) are now stored in topotools, and instead of being class methods are simple functions that accept a network as an argument an operate on the network ‘in-place’ meaning they do not return a network, but the received network is changed.
For instance:
# Old way:pn=op.Network.Cubic(shape=[10,10,10])pn.trim(pores=[1,4,7])# New way:pn=op.network.Cubic(shape=[10,10,10])op.topotools.trim(network=pn,pores=[1,4,7])
In version 2 we have moved to a more strict assignment of Physics objects to different regions, so instead of assigning them to specific pores and throats (as is still done for Geometry objects), each Physics is associated 1-to-1 with a Geometry. For example:
# Old wayimportOpenPNMasoppn=op.Network.Cubic(shape=[10,10,10])geom=op.Geometry.GenericGeometry(network=pn,pores=pn.Ps,throats=pn.Ts)water=op.Phases.Water(network=pn)phys=op.Physics.GenericPhysics(network=pn,phase=water,pores=pn.Ps,throats=pn.Ts)# New way:importopenpnmasoppn=op.network.Cubic(shape=[10,10,10])geom=op.geometry.GenericGeometry(network=pn,pores=pn.Ps,throats=pn.Ts)water=op.phases.Water(network=pn)phys=op.physics.GenericPhysics(network=pn,phase=water,geometry=geom)
The changes to the Physics instantiation were motivated by a new way organize the simulations called “The Grid”. The grid is a 2D table of objects where each column header is a Phase, each row label is a Geometry, and each row-column intersection contains the corresponding Physics. By forcing a 1-to-1 associated with Geometry and Physics, we maintain the grid structure which is helpful for object look-ups. The grid can be seen using the new Project object as follows:
importopenpnmasoppn=op.network.Cubic(shape=[10,10,10])geom=op.geometry.GenericGeometry(network=pn,pores=pn.Ps,throats=pn.Ts)water=op.phases.Water(network=pn)phys=op.physics.GenericPhysics(network=pn,phase=water,geometry=geom)proj=pn.project# (or geom.project, or water.project or phys.project)print(proj.grid)
Object look-ups have changed substantially. In V1 every object possessed a handle to it’s associated objects, such that water.network would actually contain a handle directly to the network object. This association was causing problems such as memory leaks, large objects with circular references, and complicated object deletion. In V2 we have moved away from this completely, and now all object look-ups are done with a “top down” approach. This means that you must ask the Project object to find the associated object. For example:
# Old wayphys.phases()# New wayproj=phys.projectproj.find_phase(physics=phys)
The actual looking up is done by checking the labels on each object. When an object such as phys was is created, it is given a name, such as phys_01, and the Phase with which it is associated is updated to have the labels 'pore.phys_01' and 'throat.phys_01' with True values where the phys object was assigned. This ‘top-down’ object look up will search each Phase object until it finds the label corresponding to phys, and once it does, the associated Phase is found. As similar approach is done to find which geometry is associated with which physics and so forth.
Pore-scale models are one of the more confusing aspects for new users of OpenPNM, so we have attempted to simplify them in several ways. Each object still has a models attribute which is a dictionary containing all the model definitions, but this dictionary, called the ModelsDict, is much simpler to work with.
Models are now run in order of dependencies, so if a user adds a model for ‘pore.volume’ before adding ‘pore.diameter’, OpenPNM will detect this and run the ‘pore.diameter’ model first so the values are available to subsequent models. In the event that OpenPNM cannot run models in the correct order (e.g. a needed model is missing), it will report a warning, and can be remedied by adding the missing model and calling regnerate_models.
add_model and regnerate_models are now methods of the Base class rather than being methods of the models dict. This was done to increase their visibility and make them easier to call. For example:
# Old waygeom.models.add(<argsgohere>)geom.models.regnerate()# New waygeom.add_model(<argsgohere>)geom.regnerate_models()
Models themselves are now much simpler and more flexible. Any given model can be assigned to any object. The aim where was to allow, for instance, geometrical models like ‘pore.diameter’ to be assigned directly to a Network object. The point of Geometry objects is to allow complex, heterogeneous materials (e.g. layers with different pore sizes), but this is not always needed, so users can bypass this feature. Having no Geometry objects precludes the use of Physics objects (see previous section on instantiating Physics objects), which means that pore-scale physics models (e.g. capillary pressure) must be assigned to Phase objects, which is also possible under this new flexible scheme. In all pore scale models, the object to which the model is attached is referred to as the target.
Neumann and Dirichlet boundary conditions have been renamed to Rate and Value to be more descriptive. Neumann in particular was a problem since we were not actually specifying the gradient in the BC, but the gradient multiplied by the conductance, hence the move to Rate. Dirichlet was renamed to Value was for consistency with Rate.
The behavior of the various lookup methods (pores, find_neighbor_pores) now all use the same mode keywords. The new keyswords are taken for logic theory and include ‘and’, ‘or’, ‘xor’, ‘xnor’. The functions also accept synonyms from set theory (‘intersection’, ‘union’, ‘exclusive_or’, while xnor has no equivalent).
Note
The meaning of intersection has changed
One very important change is the meaning of mode='intersection' in the find_neighbor_pores method. In version 1 this was incorrectly being used to find neighbors that shared more than one input pore, while in version 2 this means pores that share all the input pores. The mode ‘xnor’ replaces the old ‘intersection’. This change needed to be made, but is problematic since ‘intersection’ is still an accepted mode but returns a different result
Every object now has a settings attribute, which is a dictionary of key:value pairs. This was added so that we could stop storing various flags and values as attributes (sometimes hidden) on objects, and keep all such information in one place. All Algorithm objects in particular use this settings dictionary, and the setup method essentially just passes any received arguments onto the settings dict.
OpenPNM separates different types of data between 5 object types: Network, Geometry, Phase, Physics, and Algorithms. Each of these are described in more detail below, but their names hopefully indicate what sort of data or roles are assigned to each.
The main motivation for this division of data between objects is encompassed by the following table, known as the Grid, and explained below:
Network
Phase 1
Phase 2
Phase 3
Geometry 1
Physics 1
Physics 2
Physics 3
Geometry 2
Physics 4
Physics 5
Physics 6
This Grid represents a single Project. Each Project has one Network, which has Np pores and Nt throats. The Network’s main role is to house the pore coordinates and throat connection data. Because there is only one Network, it occupies the special corner location in the Grid.
A Project can have many Phases, and since each Phase has a different value for a given property (e.g. density or viscosity) a unique object is required for each one. Each Phase represents a new column in the Grid, where each column has unique values of thermo-physical properties. Phases can exist everywhere, anywhere, or nowhere in a given domain, and can redistribute during a simulation. As such, Phase properties are calculated everywhere, so they are associated with all pores and throats in the domain.
In some cases, the domain may have multiple distinct regions, such as a two-layered electrode, or multi-modal pore size distributions such as a hierarchical rock. Since Geometry objects are responsible for calculating the pore and throat sizes, it is necessary to have multiple objects for these cases (e.g. different parameters for the distribution functions). Each Geometry represents a new row in the Grid, where each row has unique values of geometrical properties. Each row also represents a subset of the total pores and throats in the domain, since each pore and throat can only be assigned to one Geometry. Thus Geometry objects have their own values of Np and Nt, corresponding to the subset of pores and throats they are in charge of.
Finally, Physics objects exist at the intersection of a row and a column. This represents the fact that a Physics object calculates values that require size information and thermo-physical properties. For example, the Hagan-Poiseuille model for hydraulic conductance requires throat diameter and length, as well as viscosity. Each Physics object is associated with a specific Phase, from which it retrieves thermo-physical property data, and a specific Geometry from which it retries geometrical information. Physics objects, because they are associated one-to-one with a Geometry, also apply to a subset of pores and throats, hence have their own values of Np and Nt.
With this Grid arrangement in mind, we can now dive into an explanation of each object and it’s particular abilities.
OpenPNM consists of 5 main object types: Network, Phases, Geometries, Physics, and Algorithms. The inheritance structure of each of these objects is shown in the diagram below. Each of these objects is a subclass of the Base class, described in more detail in the next section. Some objects are applied only to subdomains rather than the entire domains, so these inherit from the Subdomain class, which is itself a subclass of Base. Finally, some of these objects also have the ability to store pore-scale models added via the ModelsMixin mixin class.
All the objects in OpenPNM are subclasses of a single Base class, which is a subclass of the Python Dictionary (dict). So before explaining each of the specific OpenPNM subclasses, the Base class should be covered.
class openpnm.core.Base(Np=0, Nt=0, name=None, project=None)[source]
Contains methods for working with the data in the OpenPNM dict objects
Parameters:
project (OpenPNM Project object, optional) – The Project with which the object should be assigned. If not supplied
then a new Project is created
name (string, optional) – The unique name of the object. If not given one will be generated.
Np (int, default is 0) – The total number of pores to be assigned to the object
Nt (int, default is 0) – The total number of throats to be assigned to the object
Notes
This Base class is used as the template for all other OpenPNM objects,
including Networks, Geometries, Phases, Physics, and Algorithms. This
class is a subclass of the standard dict so has the usual methods such
as pop and keys, and has extra methods for working specifically
with OpenPNM data. These are outlined briefly in the following table:
Method or Attribute
Functionality
props
List of keys containing numerical arrays
labels
List of key containing boolean arrays
pores
throats
Returns pore / throat indices that have given
labels
Ps, Ts
Indices for ALL pores and throats on object
num_pores ,
num_throats
Counts the number of pores or throats with a
given label
Np, Nt
Total number of pores and throats on the object
tomask
Converts a list of pore or throat indices to a
boolean mask
toindices
Converts a boolean mask to pore or throat indices
map_pores ,
map_throats
Given indices on object B returns corresponding
indices on object A
interleave_data
Fetches data from associated objects into a
single array
interpolate_data
Given pore or throat data, interpolate the other
filter_by_label
Given indices find those with specific labels
show_hist
Method for quickly plotting histograms of data
check_data_health
Ensures all data arrays are valid and complete
In addition to the above methods, there are a few attributes which provide
access to useful items:
Attribute
Functionality
name
The string name of the object, unique to each Project
settings
A dictionary containing various setting values
project
A handle to the Project containing the object
Examples
It is possible to create an instance of Base, although it is not very
useful except for demonstration purposes as done here.
All Base objects in OpenPNM have a settings attribute which is a dictionary that stores information that dicates an objects behavior. This is particularly useful for Algorithm objects, where information is stores such as the convergence tolerance, maximum number of iterations, and so on. The list of settings on any objects can be nicly printed with print(obj.settings). Settinsg can be changed by hand (object.settings['setting_x']=1). Most of the Algorithm object possess a setup method, which accepts arguments that are then stored in the settings dictionary. These setup methods are helpful because their documentation explains what each settings means or controls.
The GenericNetwork class add more methods to the Base class than any other type in OpenPNM. These added methods are all related to the querying of topological information such as finding neighboring throats, or nearby pores. The table below gives a high level overview of these methods. For a deeper discussion of the topological data format used by OpenPNM (and thus how these queries are performed) refer to Representing Topology.
class openpnm.network.GenericNetwork(conns=None, coords=None, project=None, settings={}, **kwargs)[source]
This generic class contains the main functionality used by all networks
Parameters:
coords (array_like) – An Np-by-3 array of [x, y, z] coordinates for each pore.
conns (array_like) – An Nt-by-2 array of [head, tail] connections between pores.
Notes
The GenericNetwork class houses a number of methods used for querying and
managing the network’s spatial and topological information. The following
table gives a very short overview of the methods added those already found
on the openpnm.core.Base class.
Method or Attribute
Functionality
create_adjacency_matrix
Create an adjacency matrix using given
weights in a specified format
create_incidence_matrix
Create an incidence matrix using given
weights in a specified format
get_adjacency_matrix
Retrieve an existing adjacency matrix in
the specified format (from am)
get_incidence_matrix
Retrieve an existing incidence matrix in
the specified format (from im)
am
Returns the adjacency matrix in COO format
im
Returns the incidence matrix in COO format
find_neighbor_pores
For a given set of pores, find all
neighboring pores
find_neighbor_throats
For a given set of pores, find all
neighboring throats
find_connecting_throat
For each pair of throats find the pores
they connect
find_connected_pores
For each throat, find the pores which it
connects
num_neighbors
For a given set of pores find the number
of neighbors for each
find_nearby_pores
For a given set of pores, find pores that
are within a certain distance
check_network_health
Check the topology for any problems such
as isolated pores
Examples
>>> importopenpnmasop
Create some pore coordinates and connections manually and assign to a
GenericNetwork instance. Consider a linear network of 4 pores and 3
throats:
Networks have two required properties: ‘pore.coords’ and ‘throat.conns’.
These arrays indicate the spatial location of each pore, and which pores
are connected to which. Without these the Network object cannot function.
All of the topological queries are accomplished by inspecting the adjacency
and incidence matrices. They are created on demand, and are stored for
future use to save construction time.
The GenericPhase class is very simple subclass of The Base Class. The subclass itself adds no additional methods beyond those of Base, but it uses multiple inheritance, so inherits 3 methods from ModelsMixin, and an added attribute called models which is a ModelsDict object that stores the models and their respective parameters.
class openpnm.phases.GenericPhase(network=None, project=None, settings={}, **kwargs)[source]
This generic class is meant as a starter for custom Phase objects
This class produces a blank-slate object with no pore-scale models for
calculating any thermophysical properties. Users must add models and
specify parameters for all the properties they require.
Parameters:
network (OpenPNM network object) – The network with which this object is associated
project (OpenPNM Project object, optional) – The Project with which the object should be assigned. If not supplied
then a new Project is created
name (string, optional) – The unique name of the object. If not given one will be generated.
Geometry and Physics objects are the only two object types can be assigned to a subset of the full domain. This ability is included in the Subdomain class which is a child of the normal Base class. The only functionality added to Subdomain is the ability to set and remove which locations (pores and throats) the object is assigned to.
class openpnm.core.Subdomain(Np=0, Nt=0, name=None, project=None)[source]
This subclass of the Base class provides the ability assign the object
to specific locations (pores and throats) in the domain. This class
is subclassed by GenericGeometry and GenericPhysics.
Notes
The following table list the two methods added to Base by this subclass.
The Project object has two methods, check_geometry_health and
check_physics_health that look to make sure all locations are
assigned to one and only one Geometry and/or Physics.
Like the other classes discussed above, the GenericAlgorithm class inherits from Base, but because every algorithm is a bit different and they tend to be more complicated, discussion of the details is reserved for its own section Algorithms.
Each OpenPNM object is a Python dictionary which allows data to be stored and accessed by name, with a syntax like network['pore.diameter']. Inside each dictionary or dict are stored numerous Numpy arrays containing pore or throat data corresponding to the key (i.e. 'pore.diameter' values).
Numpy arrays are enforced, such that any data written into one of the OpenPNM object dicionaries is converted to a Numpy array. This is done to ensure that all mathematically operations throughout the code can be consistently done using vectorization. Note that any subclasses of Numpy arrays, such as Dask arrays or Unyt arrays are also acceptable.
Several rules have been implemented to control the integrity of the data:
All array names must begin with either ‘pore.’ or ‘throat.’ which serves to identify the type of information they contain.
For the sake of consistency only arrays of length Np or Nt are allowed in the dictionary. Assigning a scalar value to a dictionary results in the creation of a full length vector, either Np or Nt long, depending on the name of the array.. This effectively applies the scalar value to all locations in the network.
Any Boolean data will be treated as a label while all other numerical data is treated as a property. The difference between these is outlined below.
All pore and throat data are stored in arrays of either Np or Nt length representing the number of pores and throats on the object, respectively. This means that each pore (or throat) has a number or index that is implicitly indicated by it’s location in the arrays. All properties for pore i or throat j are stored in the array at the element i or j. Thus, the diameter for pore 15 is stored in the 'pore.diameter' array in element 15, and the length of throat 32 is stored in the 'throat.length' array at element 32. This array-based approach is ideal when using the Numpy and Scipy libraries which are designed for elementwise, vectorized programming. For instance, the volume of each throats can be found simultaneously using T_vol=3.1415*(network['throat.radius']**2)*network['throat.length']. T_vol will be an Nt-long array of values, because 'throat.length' and 'throat.radius' were also Nt-long.
The physical details about pores and throats are referred to as properties, which includes information such as pore volume and throat length. Properties are accessed using Python dictionary syntax to access the array of choice, then Numpy array indexing to access the pore or throat locations of choice:
The above lines illustrate how a scalar value is converted to a vector (Np-long), and how specific pore values can be assigned. It is also possible to assign an entire array in one step:
>>> pn['pore.bar']=np.random.rand(27)# pn has 27 pores (3*3*3)
Attempts to write an array of the wrong size will result in an error:
pn['pore.baz']=[2,3,4]
To quickly see a complete list properties on an object use the props method. You can specify whether only pore or throat properties should be returned, but the default is both:
Labels are a means of dynamically creating groups of pores and throats so they can be quickly accessed by the user. For instance, is helpful to know which pores are on the ‘top’ surface. This label is automatically added by the Cubic network generator, so a list of all pores on the ‘top’ can be retrieved by simply querying which pores possess the label ‘top’ using the pores method:
The only distinction between labels and properties is that labels are Boolean masks of True/False. Thus a True in element 10 of the array 'pore.top' means that the label ‘top’ has been applied to pore 10. Adding and removing existing labels to pores and throats is simply a matter of setting the element to True or False. For instance, to remove the label ‘top’ from pore 2:
Creating a new label array occurs automatically if a Boolean array is stored on an object:
>>> pn['pore.dummy_1']=np.random.rand(27)<0.5
A complication arises if you have a list of pore numbers you wish to label, such as [3, 4, 5]. You must first create the label array with all False values, then assign True to the desired locations:
>>> pn['pore.dummy_2']=False# Automatically assigns False to every pore>>> pn['pore.dummy_2'][[3,4,5]]=True>>> list(pn.pores('dummy_2'))[3, 4, 5]
The label functionality uses Scipy’s where method to return a list of locations where the array is True:
This set logic basically retrieves a list of all pores with the label 'top' and a second list of pores with the label dummy_2, and returns the 'intersection' of these lists, or only pores that appear in both lists.
The labels method can be used to obtain a list of all defined labels. This method optionally accepts a list of pores or throats as an argument and returns only the labels that have been applied to the specified locations.
This results can also be viewed with print(pn.labels()).
Note
The Importance of the ‘all’ Label
All objects are instantiated with a 'pore.all' and 'throat.all' label. These arrays are essential to the framework since they are used to define how long the ‘pore’ and ‘throat’ data arrays must be. In other words, the __setitem__ method checks to make sure that any ‘pore’ array it receives has the same length as 'pore.all'.
One of the features in OpenPNM is the ability to model heterogeneous materials by applying different pore-scale models to different regions. This is done by (a) creating a unique Geometry object for each region (i.e. small pores vs big pores) and (b) creating unique Physics object for each region as well (i.e. Knudsen diffusion vs Fickian diffusion). One consequence of this segregation of properties is that a single array containing values for all locations in the domain does not exist. OpenPNM offers a shortcut for this, known as interleave_data, which happens automatically, and makes it possible to query Geometry properties via the Network object, and Physics properties from the associated Phase object:
Let’s demonstrate this by creating a network and assigning two separate geometries to each half of the network:
Each of the Geometry objects has a ‘pore.diameter’ array with different values. To obtain a single array of ‘pore.diameter’ with values in the correct locations, we can use the Network as follows:
As can be seen, the ‘pore.diameter’ array contains values from both Geometry objects, and they are in their correction locations in terms of the domain number system. This is referred to as interleave_data. It also works to obtain Physics values via their associated Phase object.
Interleaving of data also works in the reverse direction, so that data only present on the network can be accessed via the Geometry objects:
Finally, interleave_data works between Subdomain objects of the same type, so that if ‘pore.volume’ is present on one but not another Geometry object, you will get an array of NaNs when asking for it on the object that does not have it:
>>> geo1['pore.volume']=3.0>>> print(geo2['pore.volume'][:5])[nan nan nan nan nan]
Note
Points to Note
Data cannot be written in this way, so that you cannot write ‘pore.diameter’ values from the Network (e.g. pn[‘pore.diameter’] = 2.0 will result in an error)
Interleaving data occurs automatically if the requested key is not found. For instance, when you request pn['pore.diameter'] it is not found, so a search is made of the associated Geometry objects and if found an array is built.
If an array named ‘pore.foo’ is already present on the Network or Phase, it cannot be created on a Geometry or Physics, resepctively, since this would break the automated interleave_data mechanism, which searches for arrays called ‘pore.foo’ on all associated objects
As the name suggests, pore network modeling borrows significantly from the fields of network and graph theory. During the development of OpenPNM, it was debated whether existing Python graph theory packages (such as graph-tool and NetworkX) should be used to store the network topology. It was decided that network property data should be simply stored as Numpy ND-arrays). This format makes the data storage very transparent and familiar since all engineers are used to working with arrays (i.e. vectors), and also very efficiently since this allows code vectorization. Fortuitously, around the same time as this discussion, Scipy introduced the compressed sparse graph library, which contains numerous graph theory algorithms that take Numpy arrays as arguments. Therefore, OpenPNM’s topology model is implemented using Numpy arrays, which is described in detail below:
The only topology definitions required by OpenPNM are:
A throat connects exactly two pores, no more and no less
Throats are non-directional, meaning that flow in either direction is equal
Other general, but non-essential rules are:
Pores can have an arbitrary number of throats, including zero; however, pores with zero throats lead to singular matrices and other problems so should be avoided.
Two pores are generally connected by no more than one throat. It is technically possible in OpenPNM to have multiple throats between a pair of pores, but it is not rigorosly supported so unintended results may arise.
In OpenPNM network topology (or connectivity) is stored as an adjacency matrix. An adjacency matrix is a Np-by-Np 2D matrix. A non-zero value at location (i, j) indicates that pores i and j are connected. Describing the network in this general fashion allows OpenPNM to be agnostic to the type of network it describes. Another important feature of the adjacency matrix is that it is highly sparse and can be stored with a variety of sparse storage schemes. OpenPNM stores the adjacency matrix in the ‘COO’ or ‘IJV’ format, which essentially stores the coordinates (I,J) and values (V) of the nonzero elements in three separate lists. This approach results in a property called 'throat.conns'; it is an Nt-by-2 array that gives the index of the two pores on either end of a given throat. The representation of an arbitrary network is shown in the following figure. It has 5 pores and 7 throats, and the 'throat.conns' array contains the (I,J,V) information to describes the adjacency matrix.
Note
Additional Thoughts on Sparse Storage
In pore networks there is generally no difference between traversing from pore i to pore j or from pore j to pore i, so a 1 is also found at location (j, i) and the matrix is symmetrical.
Since the adjacency matrix is symmetric, it is redundant to store the entire matrix when only the upper triangular part is necessary. The 'throat.conns' array only stores the upper triangular information, and i is always less than j.
Although this storage scheme is widely known as IJV, the scipy.sparse module calls this the Coordinate or COO storage scheme.
Some tasks are best performed on other types of storages scheme, such as CSR or LIL. OpenPNM converts between these internally as necessary, but users can generate a desired format using the create_adjacency_matrix method which accepts the storage type as an argument (i.e. 'csr', 'lil', etc). For a discussion of sparse storage schemes and the respective merits, see this Wikipedia article.
Querying and inspecting the pores and throats in the Network is an important tool for working with networks. The various functions that are included on the GenericNetwork class will be demonstrated below on the following cubic network:
The above queries can be more complex if a list of pores is sent, and the `mode` argument is specified. This is useful for finding neighbors surrounding a set of pores such as the fringes around an invading fluid cluster, or all throats within a cluster:
The or is a single set of unique values obtained by combining the two sets, while the intersection of these two sets includes only the values present in both (i.e. 2) The difference of these sets is all the values except those found common to both initial sets. It’s possible to specify as many pores as desired, and the set-logic is bit less obvious. More generally:
'or' returns a list of unique locations neighboring any input pores
'xor' returns a list of locations that are only neighbors to one of the input pores
'xnor' returns a list of locations that are neighbors to at least two inputs pores
In addition to these neighbor lookups, the GenericNetwork class also offers several other methods that complete the suite of lookup tools: find_connected_pores, find_connecting_throats and find_nearby_pores. There are also many more tools related to Network queries and manipulations in the Topotools module.
OpenPNM includes a Workspace Manager object that performs many of the functions found in the menu bar of a typical application’s GUI, such as saving and loading sessions.
The Workspace class is a Singleton in Object Oriented Programming jargon, meaning that only ONE instance can exist at any given time. In other words, each time a Singleton is instantiated it returns the already existing object if one already exists. This behavior is handy since it means you can instantiate the Workspace at any time, from anywhere in your workflow, and you’ll have access to the one and only Workspace object.
Another workflow management tool is the Project object. A Project defines a single simulation, which would contain a network, some geometry, phase and physics objects, and any simulations. The main roles of the Project is to group related objects together and to provide additional administrative tools.
The Project object is a Python list that has been subclassed to have many additional methods and functions, purging an object or finding a list of all phase object in the current project.
Note
Each Project can contain one and only one Network. Since every other object must be associated with a single network, so it follows that there is only one network per project. All object initializations can accept either a Network or a Project.
The Workspace and the Project work together. The Workspace is the highest level and it tracks all of the open Projects. Projects provide a lower level of oversight, with each Project keeping of track of the specific OpenPNM objects for each simulation.
The following illustrates the usage of the Workspace and Project objects.
>>> importopenpnmasop>>> ws=op.Workspace()>>> ws.clear()# Clear workspace of any pre-existing objects>>> ws.keys()dict_keys([])>>> # Create an empty Project>>> proj1=ws.new_project(name='one')>>> # Ensure Project is empty>>> proj1[]>>> # Now create a network and associate it with proj1>>> pn1=op.network.Cubic(shape=[3,3,3],project=proj1)>>> pn1inproj1True
Each Project can only have one Network. If you try to create a second
Network with the same Project, OpenPNM will complain.
Conversely, since each Network must be associated with a Project, one is
automatically created if not specified:
The Project object possesses several methods for dealing with the OpenPNM objects it contains. One of the main uses of the Project is to lookup associated objects. For instance, given a Physics object (phys), you can find which Phase it was associated with using:
Note that the Project with which each object is associated can be reached from its project attribute.
In addition to these lookup methods (others are find_physics and find_geometry) the project also has the ability to save and load single objects, as well removing objects from the Project. This latter ability is worth explaining in more detail. Consider the Grid introduced when explaining Overall Design. When removing an object, it can either result in an empty space on the Grid, or it may be desirable to remove the entire associated row or column, respectively. The purge_object method, therefore, has the ability to remove an isolated object or all of its associated objects.
When an object is purged, not only is it removed from the Project list, but all references to it in other objects in the form of labels (e.g. net[‘pore.geo_01’]) will be removed.
The Workspace object possesses methods for working dealing with Project objects, such as saving, loading, closing, and copying them.
Projects are saved with a .pnm file format, and this is done using the Python pickle library for serializing objects. The saved file is actually a dictionary that represents a subset of the Workspace, so loading a .pnm file is equivalent to unpickling the file then using the Workspace’s update method to add the contents. If the contents of the .pnm file are a list rather than a dictionary, this also works, so if you manually save a Project as a list (rather then using save_project) its still possible to load it using load_project.
Another important function of the Workspace is clone_project. As the name suggests, this creates an exact duplicate of a Project and all its objects, but they are unique in memory. This is useful for creating sub-networks of a master Network to perform small, quick calculations on.
Models are one of the most important aspects of OpenPNM, as they allow the user to specify a ‘model’ for calculating properties (e.g. ‘pore.volume’), rather than just entering numerical values (e.g. geometry_object['pore.volume']=array ).
Models offer several vital functions:
The ability to save recipes
The ability to regenerate all values when one changes
OpenPNM comes with a wide assortment of models for calculating all sorts of things such as geometrical properties, thermophysical fluid properties, and physics parameters. These are stored under the models attribute and categorized by the type of properties they produce. For instance, ‘openpnm.models.geometry.pore_diameter’ contains several methods for calculating pore diameters.
Most Base objects (GenericGeometry, GenericPhysics, GenericPhase) have a models attribute which upon instantiation of the object is filled with an empty ModelsDict object. The ModelsDict object is designed to store and interact with all models on the Base object. The ModelsDict is a subclass of the Python dictionary type, with several features added for dealing specifically with models. Each model and its associated arguments are stored under a single dictionary key under the specified ‘propname’. When a model is run the values it produces are automatically stored in the Core object’s dictionary under the same specified ‘propname’.
A handle to the desired model is retrieved, either from the included OpenPNM model libraries, or from a file containing the users custom models.
The model is attached to the target object using add_model.
This process is demonstrated by adding a random pore seed model to a Geometry object:
>>> importopenpnmasop>>> pn=op.network.Cubic([5,5,5])>>> geom=op.geometry.GenericGeometry(network=pn,pores=pn.Ps,throats=pn.Ts)>>> mod=op.models.geometry.pore_size.random# Get a handle to the desired model>>> geom.add_model(propname='pore.seed',model=mod,seed=0)
The ‘propname’ and ‘model’ arguments are required by the add_model method, and all other arguments such ‘seed’ are passed on the model (In this case it specifies the initialization value for the random number generator).
One can inspect all of the models stored on a given Base object by typing print(geom.models) at the command line:
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
# Property Name Parameter Value
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
1 pore.seed model: random
seed: 0
num_range: [0, 1]
regeneration mode: normal
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
In order to recalculate the data, the models stored in the ModelsDict dictionary must be rerun. This is accomplished with the regenerate_models method. This method takes an optional list of ‘propnames’ that should be regenerated. Models are regenerated in an order determined automatically by OpenPNM to ensure that all dependent properties are calculated first (e.g. ‘pore.diameter’ is recalculated before ‘pore.volume’ which is a function of diameter).
The OpenPNM framework was designed with extensibility in mind. Every user will apply OpenPNM to a unique problem, and will therefore require unique pore scale models, phase properties, algorithms and so on.
There are two ways to customize OpenPNM. The first is to download the source code and hack it. With this approach it is possible to create your own sub-classes, add pore-scale models, define new topology generators, and to add or change OpenPNM methods. The other approach is to install OpenPNM in your Python PATH (using pipinstallopenpnm) as with any other package such as Scipy, and write custom functions in a separate ‘working directory’ rather than in the source code. The second approach is the recommended way for several reasons. It avoids accidental changes to the framework, it allows users to keep their ‘projects’ compartmentalized, and it is much easier for users to contribute their work to OpenPNM project since sections can be merged into the framework cleanly. The second approach will be explained in detail below.
The following discussions assume that all custom files will be stored in a folder called 'my_pnm', that will be the ‘working directory’.
In the working directory, place a file called ‘my_models.py’. This file will be the home of all the custom models that will be created. Models that are in this file can be added to any object using the add_model command. The ‘models’ mechanism in OpenPNM was designed to be as straight-forward as possible, so each model is simply a function definition.
Let’s create a model called ‘surface_roughness’ that calculates the surface area of a pore accounting for surface roughness, that accepts a single ‘roughness parameter’ and is a function of pore size. Start by writing a function in the ‘my_models.py’ file that simply accepts the ‘roughness_parameter’ and returns it:
The next step is to have this model calculate something useful. Assume that surface area due to roughness scales with the projected or smooth surface area as some function, which means this function will need access to the ‘pore.diameter’ information, which is stored on Geometry objects. Thus the relevant Geometry object must be sent as an argument. OpenPNM assumes object for which the data is being calculated is passed in as target, which makes all function calls general:
importopenpnmasopimportnumpyasnpimportmy_models#Generate a simple Cubic Networkpn=op.network.Cubic(shape=[3,3,3])#Generate an 'empty' Geometry with no propertiesgeom=op.geometry.GenericGeometry(network=pn,pores=pn.pores(),throats=pn.throats())#Assign random pores diameters between 0 and 40geom['pore.diameter']=np.random.rand(pn.Np,)*40#Assign model to geometrygeom.add_model(propname='pore.surface_area',model=my_models.surface_roughness,roughness_parameter=2.2)
The same approach can be used to create models for pore-scale Physics or for calculating fluid properties that are not included with OpenPNM.
In the ‘surface_roughness’ example above, the function assumed that pore diameter data would be found under the ‘pore.diameter’ dictionary key. If for some reason, there were multiple different definitions of ‘pore diameter’, then they might be stored as ‘pore.diameter_inscribed’, and ‘pore.diameter_hydraulic’, etc. To allow the ‘surface_roughness’ function to be applied to any arbitrary pore diameter, it should be rewritten as:
Note that pore_diameter is now an argument name, which defaults to ‘pore.diameter’. Different pore diameters can be specified when calling add_model:
#Assign model to geometrygeom.add_model(propname='pore.surface_area',model=my_models.surface_roughness,pore_diameter='pore.diameter_inscribed',roughness_parameter=2.2)
All of the models provide with OpenPNM allow for this sort of non-default argument names, and it will make your custom models more general if you follow this practice.
Another way to customize OpenPNM is to create custom subclasses. This approach is best if you wish to apply several pore-scale models to a single object, since it let’s you collect them all into one place. This is done by starting with one of OpenPNMs existing Generic classes and adding a suite of pore-scale model definitions in the init stage. This is how OpenPNM classes such as Water, and StickAndBall operate. They do not actually overload or add any method, but just act as a collection of pre-set pore-scale models.
For example, let’s create a custom Phase object for an oil with temperature dependent viscosity and density. The following class definition can be added to 'my_classes.py' in the same directory as 'my_models.py'.
fromopenpnm.phasesimportGenericPhaseclassOil(GenericPhase):def__init__(self,**kwargs):super().__init__(**kwargs)# This is a python thing, and calls the init of the parent classself.add_model(propname='pore.viscosity',model=op.models.misc.polynomial,a=[10000,-111,2],prop='pore.temperature')self.add_model(propname='pore.density',model=op.models.misc.linear,prop='pore.temperature',m=2200,b=-20)
The first of the above two models creates a property called ‘pore.viscosity’, which using a polynomial function to describe the dependence on temperature (indicated by the prop argument). The second model is similar but using a linear fitting to describe the density. The values used for the coefficients of these two models will dicatate the final physical properties of the Phase, so this is now a custom phase.
Thus when you change the values of ‘pore.temperature’ on an instance of Oil, then call regenerate_models, these two models will be run and will look at the current value in ‘pore.temperture’.
Unlike Geometry, Phase and Physics objects, a Network object requires more than a collection models. In fact, Networks typically have no models. Creating a custom Network type is all about defining the locations of the pores, and the connections between the throats. Consider the following basic graph:
4-----3| \ /|5-0|| \ |2-6-1
Implementing this as a ‘custom’ Network can be done as by noting that the pore coordinates are:
The GenericNetwork class is able to handle topologies of any sort, so you can be as imaginitive as you wish when defining the network. There are several rules and assumptions about how the conns and coords data must be formatted, which are descibed in Representing Topology
To create an actual Network subclass, you can add the following to your 'my_networks.py' file:
fromopenpnm.networkimportGenericNetworkclassMyNetwork(GenericNetwork):def__init__(self,**kwargs):# Place pore locations and throat connecting generation code heresuper().__init__(conns=conns,coords=coords,**kwargs)
Algorithms can also be customized as described above. The GenericAlgorithm has a few additional methods that are meant to be implemented by subclasses, such as return and reset. The intention of this method is to send the pertinent results of a calculation ‘out’ of the Algorithm object and to the correct object in the simulation. This step is handy, but is not actually necessary. One can of course manually transfer data from an Algorithm to a Phase, for instance with:
In general terms percolation is transport associated with some threshold behaviour. Typically we are concerned with modelling
multiphase flow in porous media dominated by capillary forces and the threshold is the capillary pressure required
for a phase to enter a pore or throat by displacing another phase. However, other physical processes can be considered Percolation
such as the ising model. Percolation is associated with the presence of phases under certain conditions and the connectivity of these phases.
When referring to transport of wetting and non-wetting fluids in porous media, drainage is defined as the displacement of the wetting phase by invasion of the non-wetting phase.
A non-wetting phase requires positive pressure to overcome surface tension and push out the wetting phase and so as the imposed pressure in the non-wetting phase increases,
it pushes further into the porous media, occupying more and more of the pore space. A phase is said to be percolating when a connected cluster spans the entire domain from inlet to outlet.
Capillary pressure is determined by the wettability of the phases w.r.t the solid material characterized by the contact angle, and the geometry of the material which together shape
the meniscus. A higher curvature requires more pressure and so a non-wetting phase will require greater pressure to squeeze into smaller spaces.
Ordinary Percolation (OP) has two modes: site and bond. In OpenPNM we refer to sites as pores and bonds as throats and we can refer to both generally as elements.
OP identifies clusters of elements that are connected and occupied by the same phase given the threshold pressure and the imposed pressure.
OpenPNM uses physical models to determine what the highest pressure is required to enter a pore or throat known as the entry pressure. A very common model is the Washburn equation:
\[P_c = \frac{-2\sigma cos(\theta)}{r}\]
So to generate a porosimetry curve which is a sum of the volume of pore space occupied by the phases
of interest for a given pressure, we define a range of pressures and for each one we compare the value to the entry pressures of the elements (pores or throats, not both as discussed later).
If the entry pressure is greater than the current threshold value this element is considered invaded and can form part of an invading cluster. However, another step is required to determine whether
each cluster has access to an inlet. Invasion of the non-wetting phase must progress from an inlet and this is referred to as access limited. Another case is invasion of the wetting phase, referred to
as imbibition and this may progress by different physical mechanisms such as film growth which may be access unlimited as wetting films can permeate the entire network.
OP is a quasi-static algorithm which has more of a basis in graph theory than real physical simulations of transport in porous media. It is useful for gathering information about the pore size distribution,
but not really for simulating multiphysics. However, it’s advantages is that it can be very fast and appropriate for simulating common experimental data such as mercury intrusion porosimetry.
For this purpose we have provided a Porosimetry class which is a subclass of the OrdinaryPercolation class with some settings and additional methods to account for late pore filling:
a phenomena that occurs when a highly non-wetting phase such as mercury enters a pore whereby small spaces are not completely filled and only done so later when the pressure increases further.
This behaviour is accounted for heuristically with the following model:
where S_wp is the residual saturation of the wetting phase inside the individual pore or throat, the * notation signifies the value upon first invasion and eta is a fitting parameter.
An MIP simulation can be run with the following commands:
Our most basic implementation, the InvasionPercolation class only operates in bond mode.
Similarly to OP we are concerned with analysis of the entry pressure. However, instead of identifying connected clusters and invading them all in one step, we identify the neighboring elements
of the invading cluster and further invade one neighbor at a time along the path of least resistance. This method allows for a more accurate representation of transient flow and for more physical models associated with
the position and advancement of the meniscus within a given element. Phenomena such as trapping where clusters can become isolated, co-operative pore filling and snap off are also only possible with IP.
It is possible to define multiple inlet clusters which may progress at different rates and pressures, again allowing for more physical situations to be simulated. The draw-back to IP is that for larger networks
it can be significantly slower, although care has been taken to optimize the algorithms as much as possible using python’s heapq module.
The heapq is basically a sorted list with the smallest element at the front of the queue. So when an invasion takes place a new pore is invaded and all of the connected throats are added to the queue and become automatically sorted by entry pressure.
The next throat is then selected from the front of the queue as this is the smallest entry pressure accessible to the invading cluster and the process repeats until the network is fully invaded.
A full invasion simulation using a 2D network can be run with the following commands:
Images can be produced easily for 2D networks with commands such as plt.imshow(np.reshape(ip['pore.invasion_sequence'],newshape=S[S>1]))
and plt.imshow(np.reshape(water['pore.occupancy'],newshape=S[S>1])), which produces the following output:
Mixed Invasion Percolation, is a special case of IP where both pores and/or throats can be invaded on an individual basis, this is appropriate when the wettability of the invading and defending phases are similar,
in this case the porous media is said to have neutral wettability. Other factors other than simple pore and throat sizes can determine the shape and displacement of the meniscus and Mixed IP allows for processes in both pores and throats to happen in the same simulation such
as cooperative pore filling and throat snap-off.
When running Mixed IP in site mode the capillary pressure of the pores are used and all throats connected to an
invaded pore are also considered to be invaded on the same step as the pore. Conversely, when running in bond mode, the entry pressure of the throats is used and connected pores are automatically invaded.
This is really a convention used to speed up calculations with reasoning being that throats are typically smaller than pores. Therefore, for drainage the throats require a higher capillary pressure and so once the meniscus has reached this point
it can freely enter a larger connected space making bond percolation the most appropriate. The reverse scenario is imbibition where larger spaces provide greater resistance to flow (the ink bottle effect) and so site percolation is appropriate.
The Transport algorithms are broken into 3 separate classes, each inheriting from the one above it. The top level class is GenericTransport which implements the steady-state transport solver. Next is ReactiveTransport which adds the ability to apply source terms. Finally, the bottom class is TransientReactiveTransport which manages the transient solution of the transport problems. Each of these generic classes is then subclassed for specific phyical problems such as StokesFlow or TransientFickianDiffusion.
Consider the following example:
>>> importopenpnmasop>>> pn=op.network.Cubic(shape=[5,5,5],spacing=1e-4)>>> phase=op.phases.GenericPhase(network=pn)>>> phase['throat.conductance']=1.0# Use dummy values
The steady-state transport class contains all of the methods for setting up and solving the linear system of equations describing the material balance around each pore to find the unknown quantity x:
\[x = A^{-1} b\]
The GenericTransport class handles the following steps:
Building the coefficient matrix A
Building the right-hand side matrix b
Specifying boundary values and locations
Applying the boundary conditions by updating A and b
Calling the specified solver to find values of the unknown quantity x
The material balance around a given pore i assuming steady-state and no reaction results in the following:
\[\sum_{j=1}^n {g_{i,j} \cdot (x_i - x_j)} = 0\]
where j is the index of the neighboring pores, of which there are a total of n, g is the conductance between pores i and j, and x is the unknown quantity being solved for. Expanding this equation into matrix form, for i = 1 and j = [2, 3, 4] yields:
The A matrix is composed of a set of linear equation analogous to this one for each each pore in the network, so a network with 1000 pores will have an A matrix of 1000 by 1000. This could very quickly grow unreasonable in memory requirements, so OpenPNM always uses sparse matrices from the scipy.sparse module. The sparse A matrix is constructed using the _build_A method of the GenericTransport class automatically and is accessible via the A attribute. The A matrix can be inspected as follows:
For steady-state transport with no source or sink terms the right-hand side matrix, b is all 0’s (b is not stored as a sparse matrix since is just single column).
OpenPNM supports the two most common type of boundary conditions: constant values (also known as Dirichlet) and constant rate (similar to Neumann). The constant value condition is equivalent to writing:
\[x_i = b_i\]
This is applied to the A and b matrices by removing the balance equation on row i and replacing it with this. The constant rate boundary condition is even easier to implement since it is simply a matter of setting the right-hand side of each balance equation to some non-zero value.
The operation of altering A and b accordingly is performed by the _apply_BCs method, which occurs automatically when the user calls run. In terms of the A and b matrices, constant rate BCs require setting \(b_i\) to a value of \(n_i\), and A is untouched, while constant value BCs require setting \(b_i\) to 1 while replacing all elments in row i with 0, and setting the diagonal to \(x_i\).
Boundary conditions are specified by the user with the set_value_BC and/or set_rate_BC methods. Each of these methods requires the numerical value of the BC as well as which pores to apply them (BCs can only be applied in pores at this time). These methods store the values they recieve on the Algorithm object under ‘pore.value_BC’ and ‘pore.rate_BC’, with the given values at the corresponding locations, and NaNs elsewhere.
The final step is solving the system of equations is simply calling the run method. Before doing so, however, you can specify which solver to use in settings['solver']. The default is 'spsolve' which in turn uses the default Scipy sparse solver. On a vanilla install of Scipy, this will likely be SuperLU, which is very stable but slow. If the scikit-umfpack package has been installed, then Scipy will automatically use this by default, which is much faster. It is also possible to specify any of the iterative solvers offered by Scipy. For instance, to use conjugate gradient, use settings['solver']='cg'. Iterative solvers a much faster and can handle larger systems, but they are not always stable, so much be used with care. Most OpenPNM problems are well handled by the conjugate gradient solver.
The solution is produced by calling run method, which actually calls a few other methods behind the scenes. For the sake of illustration, let’s call these explicitly:
The _solve method computes x and returns it. The run method, which essentially just calls the above 2 methods, captures the received value of x and stores it on the Algorithm under 'pore.quantity'. The name of the quantity is specified in the settings['quantity'] and is given sensible names by default for the various subclasses (e.g.for StokesFlow it is ‘pore.pressure’).
The ReactiveTransport class inherits directly from GenericTransport, so inherits all of the mechanism described above, plus the ability to include non-linear source/sink terms. The balance equation around a pore i in the presence of a source/sink term is:
where \(R(x_i)\) is some function of the quantity being solved for, and can be non-linear. A common example is the standard 2nd order kinetics: \(R = A \cdot x^2\).
To have access to the source/sink machinery we must use an instance of ReactiveTransport:
Specifying a source/sink term requires first defining the form of the equation and its constants. This is done on the Physics or Phase objects as a pore-scale model:
Now the Algorithm can be told where to look for the source term, and where to apply it:
>>> alg.set_source(propname='pore.rxn',pores=63)# A single pore near the middle
The process of setting a source/sink term does two things. It places ‘pore.rxn’ in alg.settings['sources'] and it creates a label called ‘pore.rxn’ indicating which pores it applied to.
Note
All Transport Classes Have Reactions
If no source terms are specified to the algorithm then no attempt is made by the algorithm to add source/sink terms to the matrices, and no iterations are performed. In other words, when no source/sink terms are specified (using set_source) the ReactiveTransport class behaves exactly the same at the GenericTransport. Hence, all transport Algorithms are subclasses of ReactiveTransport.
If the source/sink term were linear (e.g. \(R_i = k \cdot x_i)\), then simply adding \(-k_i\) to the diagonal of the ith row of A would be sufficient. However, to handle the general case of non-linear source terms, OpenPNM uses a method based on Newton’s method adapted from Numerical Heat Transfer and Fluid Flow by Patankar. This involves linearizing the source term about the current value of x such that \(R_i = S_1 \cdot x_i + S_2\), which means that \(S_1\) is added the diagonal of A and \(-S_2\) is added to b.
The ReactiveTransport class has a _apply_sources method which check alg.settings['sources'] to see which source/sink terms have been added (if any). For each sourc/sink term if finds, it regenerates that model in the associated Physics and/or Phase objects using the current value ‘pore.quantity’ (which defaults to 0). Next the \(S_1\) and \(S_2\) terms are fetched from each Physics and/or Phase and applied to the corresponding pores by adding \(S_(1,i)\) to the diagonal of row i of A, and \(_S_(2,i)\) to row i of b.
The ReactiveTransport has a run method that will apply all the necessary steps for the user, including _apply_sources, and most importantly calling _solve of the GenericTransport class repeatedly until convergance is acheived on the quantity x. In this case, convergence means that the value of x used when regenerating the source/sink terms to determine \(S_1\) and \(S_2\), is sufficiently close the value of x returned by _solve.
To run the simulation using explicit steps:
>>> alg._build_A()>>> alg._build_b()>>> alg._apply_BCs()>>> alg['pore.quantity']=0# Make initial guess of quantity>>> alg._update_iterative_props()>>> alg._apply_sources()>>> x=alg.run()
It is not necessary to actually call the above methods, use run instead.
Transient algorithms inherit from ReactiveTransport, so possess all the machinery described above, plus some extra methods for setting up and performing the simulation transiently. The only additional method is set_IC for setting the initial conditions, plus there are a number of extra settings required, specifically, the start time, end time, and time step ('t_start','t_stop','t_step').
OpenPNM offers two transient transport solvers: The implicit scheme is used by default, but the Crank-Nicolson scheme can be used by setting self.settings['t_scheme']='cranknicolson'.
This module contains the main classes from which all other major objects
(Network, Geometry, Physics, Phase, and Algorithm) derive.
The Base Class
The Base class is a dict that has added methods for indexing the pores
and throats, applying labels, and managing the stored data. All OpenPNM
object inherit from Base so possess these methods:
Method
Description
num_pores,
num_throats
Returns the number of pores and throats with the
specified labels
props, labels
Returns a list of all property (numeric) or
label (boolean) arrays on the object
pores, throats
Returns pore or throat indicies where given
labels have been applied
toindices, tomaks
Convert a boolean mask to a list of indices and
vice-versa
filter_by_label
Reduces a list of indices based on labels
interpolate_data
Determines a pore (or throat) property as the
average if it’s neighbors’ values
map_pores,
map_throats
Translates pore and throat ids into indices
show_hist
Show a quick plot of key property distributi…
check_data_health
Check the health of pore and throat data
The Subdomain Class
Base objects, Networks, Phases, Algorithms, are assigned to all locations
in the domain. The Subdomain class is a direct descendent of Base
which has the added ability to be assigned to a subset of the domain. Objects
that inherit from Subdomain are Geomery and Physics.
Only two methods are added to the Subdomain class to make this work:
Method
Description
drop_locations
Removes association between an object and it’s boss
add_locations
Adds associations between an object and it’s boss
Boss objects refer to the Full Domain object it is associated with. For
Geomery objects this is the Network, and for Physics objects this is the
Phase that was specified during instantiation.
The associations between an object and it’s boss are tracked using labels in
the boss. So a Geometry object named geom1 will put labels ‘pore.geom1’
and ‘throat.geom1’ into the Network dictionary, with True values indicating
where geom1 applies.
The ModelsMixin Class
Mixins are a useful feature of Python
that allow a few methods to be added to a class that needs them. In OpenPNM,
the ability to store and run ‘pore-scale’ models is not needed by some objects
(Network, Algorithms), but is essential to Geometry, Physics, and Phase
objects.
The ModelsMixin adds the following few methods:
Method
Description
add_model
Adds a new model to the models dictionary
remove_model
Removes model and data from object
regenerate_models
Re-runs the specified model or models
In addition to these methods, the ModelsMixin also adds a models
attribute to each object. This is a dictionary that stores the pore-scale
models and their associated parameters. When regenerate_models is called
the function and all the given parameters are retrieved from this dictionary
and run.
class openpnm.core.Base(Np=0, Nt=0, name=None, project=None)[source]¶
Bases: dict
Contains methods for working with the data in the OpenPNM dict objects
Parameters:
project (OpenPNM Project object, optional) – The Project with which the object should be assigned. If not supplied
then a new Project is created
name (string, optional) – The unique name of the object. If not given one will be generated.
Np (int, default is 0) – The total number of pores to be assigned to the object
Nt (int, default is 0) – The total number of throats to be assigned to the object
Notes
This Base class is used as the template for all other OpenPNM objects,
including Networks, Geometries, Phases, Physics, and Algorithms. This
class is a subclass of the standard dict so has the usual methods such
as pop and keys, and has extra methods for working specifically
with OpenPNM data. These are outlined briefly in the following table:
Method or Attribute
Functionality
props
List of keys containing numerical arrays
labels
List of key containing boolean arrays
pores
throats
Returns pore / throat indices that have given
labels
Ps, Ts
Indices for ALL pores and throats on object
num_pores ,
num_throats
Counts the number of pores or throats with a
given label
Np, Nt
Total number of pores and throats on the object
tomask
Converts a list of pore or throat indices to a
boolean mask
toindices
Converts a boolean mask to pore or throat indices
map_pores ,
map_throats
Given indices on object B returns corresponding
indices on object A
interleave_data
Fetches data from associated objects into a
single array
interpolate_data
Given pore or throat data, interpolate the other
filter_by_label
Given indices find those with specific labels
show_hist
Method for quickly plotting histograms of data
check_data_health
Ensures all data arrays are valid and complete
In addition to the above methods, there are a few attributes which provide
access to useful items:
Attribute
Functionality
name
The string name of the object, unique to each Project
settings
A dictionary containing various setting values
project
A handle to the Project containing the object
Examples
It is possible to create an instance of Base, although it is not very
useful except for demonstration purposes as done here.
A subclassed version of the standard dict’s clear method. This can be
used to selectively clear certain data from the object, including
properties and/or labels. Importantly, it does NOT clear items that
are required to maintain the integrity of the simulation. These are
arrays that define the topology (ie. ‘pore.all’, ‘pore.coords’,
‘throat.all’, ‘throat.conns’), as well as arrays that indicate
associations bewteen objects (ie. ‘pore.geo_01’).
Parameters:
element (string or list of strings) – Can be either ‘pore’ or ‘throat’, which specifies whether ‘pore’
and/or ‘throat’ arrays should be cleared. The default is both.
mode (string or list of strings) –
This controls what is cleared from the object. Options are:
’props’ : Removes all numerical property values from the object
dictionary
’model_data’ : Removes only numerical data that were produced
by an associated model
’labels’ : Removes all labels from the object dictionary,
except those relating to the pore and throat locations of
associated objects
’all’ : Removes both ‘props’ and ‘labels’
Notes
If you wish to selectively remove some properties but not others, use
something like delobject['pore.blah'] at the Python prompt. This
can also be done in a for-loop to remove a list of items.
Examples
>>> importopenpnmasop>>> pn=op.network.Cubic(shape=[5,5,5])>>> len(pn.labels())# There are 10 total labels on the network12>>> pn.clear(mode='labels')>>> len(pn.labels())# Kept only 'pore.all' and 'throat.all'2>>> geom=op.geometry.GenericGeometry(network=pn,pores=pn.Ps,... throats=pn.Ts,name='geo1')>>> len(pn.labels())# 2 new labels were added for geometry locations4>>> pn.clear(mode='labels')>>> 'pore.'+geom.nameinpn.keys()# The geometry labels were keptTrue>>> len(pn.props())# The network has two properties2>>> pn.clear(element='pore',mode='props')>>> 'pore.coords'inpn.keys()# The pore property was removedTrue>>> pn.clear()# Remove everything except protected labels and arrays>>> print(sorted(list(pn.keys(element='pore',mode='all'))))['pore.all', 'pore.coords', 'pore.geo1']
Retrieves requested property from associated objects, to produce a full
Np or Nt length array.
Parameters:
prop (string) – The property name to be retrieved
Returns:
Return type:
A full length (Np or Nt) array of requested property values.
Notes
This makes an effort to maintain the data ‘type’ when possible; however
when data are missing this can be tricky. Data can be missing in two
different ways: A set of pores is not assisgned to a geometry or the
network contains multiple geometries and data does not exist on all.
Float and boolean data is fine, but missing ints are converted to float
when nans are inserted.
Examples
>>> importopenpnmasop>>> pn=op.network.Cubic(shape=[2,2,2])>>> Ps=pn['pore.top']>>> Ts=pn.find_neighbor_throats(pores=Ps)>>> g1=op.geometry.GenericGeometry(network=pn,pores=Ps,throats=Ts)>>> Ts=~pn.tomask(throats=Ts)>>> g2=op.geometry.GenericGeometry(network=pn,pores=~Ps,throats=Ts)>>> g1['pore.value']=1>>> print(g1['pore.value'])[1 1 1 1]>>> print(g2['pore.value'])# 'pore.value' is defined on g1, not g2[nan nan nan nan]>>> print(pn['pore.value'])[nan 1. nan 1. nan 1. nan 1.]>>> g2['pore.value']=20>>> print(pn['pore.value'])[20 1 20 1 20 1 20 1]>>> pn['pore.label']=False>>> print(g1['pore.label'])# 'pore.label' is defined on pn, not g1[False False False False]
This subclass works exactly like keys when no arguments are passed,
but optionally accepts an element and/or a mode, which filters
the output to only the requested keys.
The default behavior is exactly equivalent to the normal keys
method.
Parameters:
element (string) – Can be either ‘pore’ or ‘throat’, which limits the returned list of
keys to only ‘pore’ or ‘throat’ keys. If neither is given, then
both are assumed.
mode (string (optional)) –
Controls which keys are returned. Options are:
’labels’ : Limits the returned list of keys to only ‘labels’
(boolean arrays)
’props’ : Limits he return list of keys to only ‘props’
(numerical arrays).
’all’ : Returns both ‘labels’ and ‘props’. This is equivalent
to sending a list of both ‘labels’ and ‘props’.
If no mode is specified then the normal KeysView object is
returned.
deep (Boolean) – If set to True then the keys on all associated subdomain
objects are returned as well.
This subclass can be used to get dictionary keys of specific kinds of
data. It’s use augments props and labels by returning a list
containing both types, but possibly limited by element type (‘pores’
or ‘throats’.)
Examples
>>> importopenpnmasop>>> pn=op.network.Cubic([5,5,5])>>> pn.keys(mode='props')# Get all props['pore.coords', 'throat.conns']>>> pn.keys(mode='props',element='pore')# Get only pore props['pore.coords']
Given a list of pore on a target object, finds indices of those pores
on the calling object
Parameters:
pores (array_like) – The indices of the pores on the object specifiedin origin
origin (OpenPNM Base object) – The object corresponding to the indices given in pores
filtered (boolean (default is True)) – If True then a ND-array of indices is returned with missing
indices removed, otherwise a named-tuple containing both the
indices and a boolean mask with False indicating
which locations were not found.
Returns:
Pore indices on the calling object corresponding to the same pores
on the origin object. Can be an array or a tuple containing an
array and a mask, depending on the value of filtered.
Given a list of throats on a target object, finds indices of
those throats on the calling object
Parameters:
throats (array_like) – The indices of the throats on the object specified in origin
origin (OpenPNM Base object) – The object corresponding to the indices given in throats
filtered (boolean (default is True)) – If True then a ND-array of indices is returned with missing
indices removed, otherwise a named-tuple containing both the
indices and a boolean mask with False indicating
which locations were not found.
Returns:
Throat indices on the calling object corresponding to the same throats
on the origin object. Can be an array or a tuple containing an
array and a mask, depending on the value of filtered.
Returns pore indicies where given labels exist, according to the logic
specified by the mode argument.
Parameters:
labels (string or list of strings) – The label(s) whose pores locations are requested. This argument
also accepts ‘*’ for wildcard searches.
mode (string) –
Specifies how the query should be performed. The options are:
’or’, ‘union’, ‘any’ : (default) Pores with one or more of
the given labels are returned.
’and’, ‘intersection’, ‘all’ : Pores with all of the given
labels are returned.
’xor’, ‘exclusive_or’ : Pores with only one of the given
labels are returned.
’nor’, ‘none’, ‘not’ : Pores with none of the given labels
are returned.
’nand’ : Pores with not all of the given labels are
returned.
’xnor’ : Pores with more than one of the given labels are
returned.
asmask (boolean) – If True then a boolean array of length Np is returned with
True values indicating the pores that satisfy the query.
target (OpenPNM Base object) – If given, the returned indices will be indexed relative to the
target object. This can be used to determine how indices on
one object map onto another object.
Returns:
A Numpy array containing pore indices filtered by the logic specified
props (string or list of strings) – The pore and/or throat properties to be plotted as histograms. By
default this function will show ‘pore.diameter’, ‘throat.diameter’,
and ‘throat.length’.
bins (int or array_like) – The number of bins to use when generating the histogram. If an
array is given they are used as the bin spacing instead.
fontsize (int) – Sets the font size temporarily. The default size of matplotlib is
10, which is too small for many screens. This function has a
default of 22, which does not overwrite the matplotlib setting.
Note that you can override matplotlib setting globally with
matplotlib.rcParams['font.size']=22.
Notes
Other keyword arguments are passed to the matplotlib.pyplot.hist
function.
Returns throat locations where given labels exist, according to the
logic specified by the mode argument.
Parameters:
labels (string or list of strings) – The throat label(s) whose locations are requested. If omitted,
‘all’ throat inidices are returned. This argument also accepts
‘*’ for wildcard searches.
mode (string) –
Specifies how the query should be performed. The options are:
’or’, ‘union’, ‘any’ : (default) Throats with one or more of
the given labels are returned.
’and’, ‘intersection’, ‘all’ : Throats with all of the given
labels are returned.
’xor’, ‘exclusive_or’ : Throats with only one of the given
labels are returned.
’nor’, ‘none’, ‘not’ : Throats with none of the given labels
are returned.
’nand’ : Throats with not all of the given labels are
returned.
’xnor’ : Throats with more than one of the given labels are
returned.
asmask (boolean) – If True then a boolean array of length Nt is returned with
True values indicating the throats that satisfy the query.
target (OpenPNM Base object) – If given, the returned indices will be indexed relative to the
target object. This can be used to determine how indices on
one object map onto another object.
Returns:
A Numpy array containing throat indices filtered by the logic specified
Convert a boolean mask to a list of pore or throat indices
Parameters:
mask (array_like booleans) – A boolean array with True at locations where indices are desired.
The appropriate indices are returned based an the length of mask,
which must be either Np or Nt long.
Returns:
A list of pore or throat indices corresponding the locations where
This behavior could just as easily be accomplished by using the mask
in pn.pores()[mask] or pn.throats()[mask]. This method is
just a convenience function and is a complement to tomask.
Convert a list of pore or throat indices into a boolean mask of the
correct length
Parameters:
or throats (pores) – List of pore or throat indices. Only one of these can be specified
at a time, and the returned result will be of the corresponding
length.
Returns:
A boolean mask of length Np or Nt with True in the specified pore or
>>> importopenpnmasop>>> pn=op.network.Cubic(shape=[5,5,5])>>> mask=pn.tomask(pores=[0,10,20])>>> sum(mask)# 3 non-zero elements exist in the mask (0, 10 and 20)3>>> len(mask)# Mask size is equal to the number of pores in network125>>> mask=pn.tomask(throats=[0,10,20])>>> len(mask)# Mask is now equal to number of throats in network300
class openpnm.core.Subdomain(Np=0, Nt=0, name=None, project=None)[source]¶
Bases: openpnm.core.Base.Base
This subclass of the Base class provides the ability assign the object
to specific locations (pores and throats) in the domain. This class
is subclassed by GenericGeometry and GenericPhysics.
Notes
The following table list the two methods added to Base by this subclass.
The Project object has two methods, check_geometry_health and
check_physics_health that look to make sure all locations are
assigned to one and only one Geometry and/or Physics.
This class is meant to be combined by the Base class in multiple
inheritence. This approach is used since Network and Algorithm do not
need to have any models attribute, while Phase, Geometry, and Physics
do. By using a mixin class, all objects can inherit from Base while
the model functionality can be added only where needed.
Notes
The following table gives a brief overview of the methods that are added
to the object by this mixin. In addition to these methods, a models
attribute is also added, which is a dictionary that contains all of the
models and their parameters.
Adds a new model to the models dictionary (object.models)
Parameters:
propname (string) – The name of the property to be calculated by the model.
model (function) – A reference (handle) to the function to be used.
regen_mode (string) –
Controls how/when the model is run (See Notes for more details).
Options are:
’normal’ : (default) The model is run directly upon being
assiged, and also run every time regenerate_models is called.
’constant’ : The model is run directly upon being assigned, but
is not called again, thus making it’s data act like a constant.
If, however, the data is deleted from the object it will be
regenerated again.
’deferred’ Is not run upon being assigned, but is run the first
time that regenerate_models is called.
propnames (string or list of strings) – The list of property names to be regenerated. If None are given
then ALL models are re-run (except for those whose regen_mode
is ‘constant’).
exclude (list of strings) – Since the default behavior is to run ALL models, this can be used
to exclude specific models. It may be more convenient to supply
as list of 2 models to exclude than to specify 8 models to include.
deep (boolean) – Specifies whether or not to regenerate models on all associated
objects. For instance, if True, then all Physics models will
be regenerated when method is called on the corresponding Phase.
The default is False. The method does not work in reverse,
so regenerating models on a Physics will not update a Phase.
class openpnm.core.ModelsDict(*args, **kwargs)[source]¶
Bases: openpnm.utils.misc.PrintableDict
This subclassed dictionary is assigned to the models attribute of
all objects that inherit from the ModelsMixin class. Each dictionary
entry corresponds to an entry in the target object’s dictionary, and
contains the models and associated parameters for generating the model.
The main features of this subclass are three methods the help resolve the
order in which models should be called: dependency_list,
dependency_graph, and dependency_map.
Returns a NetworkX graph object of the dependencies
Parameters:
mode (bool, optional) – Defines whether intra- or inter-object dependency graph is desired.
Default is False, i.e. only returns dependencies within the object.
Returns a list of dependencies in the order with which they should be
called to ensure data is calculated by one model before it’s asked for
by another.
Notes
This raises an exception if the graph has cycles which means the
dependencies are unresolvable (i.e. there is no order which the
models can be called that will work). In this case it is possible
to visually inspect the graph using dependency_graph.
This module contains the GenericNetwork class, whose main purpose is to
manage the topological representation of the Network. It also houses a
collection of Network generators.
Available Network Generators
OpenPNM includes a variety of Network generators. The basically include two
families of topology: periodic lattices and tessellations of random points.
Generator Name
Description
Cubic
Simple cubic lattice with connectivity from 6 to 26
CubicDual
Body centered cubic lattice plus face centered nodes
on the surfaces
CubicTemplate
Simple cubic lattice with arbitrary domain shape
specified by a template image
Bravais
Crystal lattice types including fcc, bcc, sc, and hcp
Delaunay
Random network formed by Delaunay tessellation of
arbitrary base points
Voronoi
Random network formed by Voronoi tessellation of
arbitrary base points
Gabriel
Random network formed by Gabriel tessellation of
arbitrary base points
DelaunayVoronoiDual
Combined and interconnected Voronoi and Delaunay
tessellations
The GenericNetwork Class
All of the above Network classes derive from the GenericNetwork class. It is
a subclass of Base so contains methods for retrieving sets of pores based
on labels and so forth, but also contains the following additional methods
that are used soley for topological queries.
Pore networks require two essential pieces of information:
the spatial location of pores
the connectivity of which throats connect which pores
The GenericNetwork class and it’s subclasses are responsible for storing,
managing, and utilizing this information.
Network topology is stored using adjacency matrices. Moreover, this is stored
using a sparse matrix format
known as COO. All netowrk objects store the COO matrix as 'throat.conns'.
The spatial location of each pore is stored in Cartesian coordinates [x, y, z],
under 'pore.coords'. All networks must be 3D, so even a 2D network must
have a z-component (but set to 0).
The following methods are implemented on GenericNetwork, and look into
the 'throat.conns' and 'pore.coords' as needed.
Method
Description
num_neighbors
Counts the number of neighbors with a given label
find_neighbor_pores
Gets indices of pores neighboring a given pore
find_neighbor_throats
Gets indices of neighbor throats to a given pore
find_connected_pores
Gets indices of pores connected by a given throat
find_connecting_throat
Gets indices of the throat joining pairs of pores
find_nearby_pores
Find all pores within given distance of given pore
This generic class contains the main functionality used by all networks
Parameters:
coords (array_like) – An Np-by-3 array of [x, y, z] coordinates for each pore.
conns (array_like) – An Nt-by-2 array of [head, tail] connections between pores.
Notes
The GenericNetwork class houses a number of methods used for querying and
managing the network’s spatial and topological information. The following
table gives a very short overview of the methods added those already found
on the openpnm.core.Base class.
Method or Attribute
Functionality
create_adjacency_matrix
Create an adjacency matrix using given
weights in a specified format
create_incidence_matrix
Create an incidence matrix using given
weights in a specified format
get_adjacency_matrix
Retrieve an existing adjacency matrix in
the specified format (from am)
get_incidence_matrix
Retrieve an existing incidence matrix in
the specified format (from im)
am
Returns the adjacency matrix in COO format
im
Returns the incidence matrix in COO format
find_neighbor_pores
For a given set of pores, find all
neighboring pores
find_neighbor_throats
For a given set of pores, find all
neighboring throats
find_connecting_throat
For each pair of throats find the pores
they connect
find_connected_pores
For each throat, find the pores which it
connects
num_neighbors
For a given set of pores find the number
of neighbors for each
find_nearby_pores
For a given set of pores, find pores that
are within a certain distance
check_network_health
Check the topology for any problems such
as isolated pores
Examples
>>> importopenpnmasop
Create some pore coordinates and connections manually and assign to a
GenericNetwork instance. Consider a linear network of 4 pores and 3
throats:
Networks have two required properties: ‘pore.coords’ and ‘throat.conns’.
These arrays indicate the spatial location of each pore, and which pores
are connected to which. Without these the Network object cannot function.
All of the topological queries are accomplished by inspecting the adjacency
and incidence matrices. They are created on demand, and are stored for
future use to save construction time.
Returns an adjacency matrix in the specified sparse format, with throat
IDs indicating the non-zero values.
Parameters:
fmt (string, optional) –
The sparse storage format to return. Options are:
’coo’ : (default) This is the native format of OpenPNM data
’lil’ : Enables row-wise slice of the matrix
’csr’ : Favored by most linear algebra routines
’dok’ : Enables subscript access of locations
Notes
This method will only create the requested matrix in the specified
format if one is not already saved on the object. If not present,
this method will create and return the matrix, as well as store it
for future use.
To obtain a matrix with weights other than throat IDs at each non-zero
location use create_adjacency_matrix.
To obtain the non-directed graph, with only upper-triangular entries,
use sp.sparse.triu(am,k=1).
Generates a weighted adjacency matrix in the desired sparse format
Parameters:
weights (array_like, optional) –
An array containing the throat values to enter into the matrix
(in graph theory these are known as the ‘weights’).
If the array is Nt-long, it implies that the matrix is symmetric,
so the upper and lower triangular regions are mirror images. If
it is 2*Nt-long then it is assumed that the first Nt elements are
for the upper triangle, and the last Nt element are for the lower
triangular.
If omitted, ones are used to create a standard adjacency matrix
representing connectivity only.
fmt (string, optional) –
The sparse storage format to return. Options are:
’coo’ : (default) This is the native format of OpenPNM data
’lil’ : Enables row-wise slice of the matrix
’csr’ : Favored by most linear algebra routines
’dok’ : Enables subscript access of locations
triu (boolean (default is False)) – If True, the returned sparse matrix only contains the upper-
triangular elements. This argument is ignored if the weights
array is 2*Nt-long.
drop_zeros (boolean (default is False)) – If True, applies the eliminate_zeros method of the sparse
array to remove all zero locations.
Returns:
Return type:
An adjacency matrix in the specified Scipy sparse format.
Notes
The adjacency matrix is used by OpenPNM for finding the pores
connected to a give pore or set of pores. Specifically, an adjacency
matrix has Np rows and Np columns. Each row represents a pore,
containing non-zero values at the locations corresponding to the
indices of the pores connected to that pore. The weights argument
indicates what value to place at each location, with the default
being 1’s to simply indicate connections. Another useful option is
throat indices, such that the data values on each row indicate which
throats are connected to the pore.
Creates a weighted incidence matrix in the desired sparse format
Parameters:
weights (array_like, optional) – An array containing the throat values to enter into the matrix (In
graph theory these are known as the ‘weights’). If omitted, ones
are used to create a standard incidence matrix representing
connectivity only.
fmt (string, optional) –
The sparse storage format to return. Options are:
’coo’ : (default) This is the native format of OpenPNMs data
’lil’ : Enables row-wise slice of the matrix
’csr’ : Favored by most linear algebra routines
’dok’ : Enables subscript access of locations
drop_zeros (boolean (default is False)) – If True, applies the eliminate_zeros method of the sparse
array to remove all zero locations.
Returns:
Return type:
An incidence matrix in the specified sparse format
Notes
The incidence matrix is a cousin to the adjacency matrix, and used by
OpenPNM for finding the throats connected to a give pore or set of
pores. Specifically, an incidence matrix has Np rows and Nt columns,
and each row represents a pore, containing non-zero values at the
locations corresponding to the indices of the throats connected to that
pore. The weights argument indicates what value to place at each
location, with the default being 1’s to simply indicate connections.
Another useful option is throat indices, such that the data values
on each row indicate which throats are connected to the pore, though
this is redundant as it is identical to the locations of non-zeros.
Return a list of pores connected to the given list of throats
Parameters:
throats (array_like) – List of throats numbers
flatten (boolean, optional) – If True (default) a 1D array of unique pore numbers is
returned. If False each location in the the returned array
contains a sub-arras of neighboring pores for each input throat,
in the order they were sent.
mode (string) –
Specifies logic to filter the resulting list. Options are:
’or’ : (default) All neighbors of the input throats. This is
also known as the ‘union’ in set theory or ‘any’ in boolean logic.
Both keywords are accepted and treated as ‘or’.
’xor’ : Only neighbors of one and only one input throat. This
is useful for finding the sites that are not shared by any of the
input throats.
’xnor’ : Neighbors that are shared by two or more input
throats. This is equivalent to finding all neighbors with ‘or’,
minus those found with ‘xor’, and is useful for finding neighbors
that the inputs have in common.
’and’ : Only neighbors shared by all input throats. This is
also known as ‘intersection’ in set theory and (somtimes) as ‘all’
in boolean logic. Both keywords are accepted and treated as ‘and’.
Returns:
1D array (if flatten is True) or ndarray of arrays (if
Find all pores within a given radial distance of the input pore(s)
regardless of whether or not they are toplogically connected.
Parameters:
pores (array_like) – The list of pores for whom nearby neighbors are to be found
r (scalar) – The maximum radius within which the search should be performed
include_input (bool) – Controls whether the input pores should be included in the list of
pores nearby the other pores in the input list. So if
pores=[1,2] and 1 and 2 are within r of each other,
then 1 will be included in the returned for pores near 2, and
vice-versa if this argument is True. The default is
False.
flatten (bool) – If True returns a single list of all pores that match the
criteria, otherwise returns an array containing a sub-array for
each input pore, where each sub-array contains the pores that
are nearby to each given input pore. The default is False.
Returns:
A list of pores which are within the given spatial distance. If a
list of N pores is supplied, then a an N-long list of such lists is
returned. The returned lists each contain the pore for which the
Returns a list of pores that are direct neighbors to the given pore(s)
Parameters:
pores (array_like) – Indices of the pores whose neighbors are sought
flatten (boolean) – If True (default) the returned result is a compressed array of
all neighbors. If False, a list of lists with each sub-list
containing the neighbors for each input site. Note that an
unflattened list might be slow to generate since it is a Python
list rather than a Numpy array.
include_input (bool) – If False (default) then the input pores are not included in
the returned list(s). Note that since pores are not neighbors of
themselves, the neighbors of pore N will not include N, even if
this flag is True.
mode (string) –
Specifies logic to filter the resulting list. Options are:
’or’ : (default) All neighbors of the input pores. This is
also known as the ‘union’ in set theory or ‘any’ in boolean logic.
Both keywords are accepted and treated as ‘or’.
’xor’ : Only neighbors of one and only one input pore. This
is useful for finding the pores that are not shared by any of the
input pores. This is known as ‘exclusive_or’ in set theory, and
is an accepted input.
’xnor’ : Neighbors that are shared by two or more input pores.
This is equivalent to finding all neighbors with ‘or’, minus those
found with ‘xor’, and is useful for finding neighbors that the
inputs have in common.
’and’ : Only neighbors shared by all input pores. This is also
known as ‘intersection’ in set theory and (somtimes) as ‘all’ in
boolean logic. Both keywords are accepted and treated as ‘and’.
Returns:
If flatten is True, returns a 1D array of pore indices filtered
according to the specified mode. If flatten is False, returns
a list of lists, where each list contains the neighbors of the
corresponding input pores.
Notes
The logic options are applied to neighboring pores only, thus it
is not possible to obtain pores that are part of the global set but
not neighbors. This is because (a) the list of global pores might be
very large, and (b) it is not possible to return a list of neighbors
for each input pores if global pores are considered.
Returns a list of throats neighboring the given pore(s)
Parameters:
pores (array_like) – Indices of pores whose neighbors are sought
flatten (boolean, optional) – If True (default) a 1D array of unique throat indices is
returned. If False the returned array contains arrays of
neighboring throat indices for each input pore, in the order
they were sent.
mode (string) –
Specifies logic to filter the resulting list. Options are:
’or’ : (default) All neighbors of the input pores. This is
also known as the ‘union’ in set theory or ‘any’ in boolean logic.
Both keywords are accepted and treated as ‘or’.
’xor’ : Only neighbors of one and only one input pore. This
is useful for finding the thraots that are not shared by any of the
input pores.
’xnor’ : Neighbors that are shared by two or more input pores.
This is equivalent to finding all neighbors with ‘or’, minus those
found with ‘xor’, and is useful for finding neighbors that the
inputs have in common.
’and’ : Only neighbors shared by all input pores. This is also
known as ‘intersection’ in set theory and (somtimes) as ‘all’ in
boolean logic. Both keywords are accepted and treated as ‘and’.
Returns:
If flatten is True, returns a 1D array of throat indices
filtered according to the specified mode. If flatten is False,
returns a list of lists, where each list contains the neighbors of the
corresponding input pores.
Notes
The logic options are applied to neighboring bonds only, thus it
is not possible to obtain bonds that are part of the global set but
not neighbors. This is because (a) the list of global bonds might be
very large, and (b) it is not possible to return a list of neighbors
for each input site if global sites are considered.
Returns an adjacency matrix in the specified sparse format, with throat
IDs indicating the non-zero values.
Parameters:
fmt (string, optional) –
The sparse storage format to return. Options are:
’coo’ : (default) This is the native format of OpenPNM data
’lil’ : Enables row-wise slice of the matrix
’csr’ : Favored by most linear algebra routines
’dok’ : Enables subscript access of locations
Notes
This method will only create the requested matrix in the specified
format if one is not already saved on the object. If not present,
this method will create and return the matrix, as well as store it
for future use.
To obtain a matrix with weights other than throat IDs at each non-zero
location use create_adjacency_matrix.
To obtain the non-directed graph, with only upper-triangular entries,
use sp.sparse.triu(am,k=1).
Returns an incidence matrix in the specified sparse format, with pore
IDs indicating the non-zero values.
Parameters:
fmt (string, optional) –
The sparse storage format to return. Options are:
’coo’ : (default) This is the native format of OpenPNM data
’lil’ : Enables row-wise slice of the matrix
’csr’ : Favored by most linear algebra routines
’dok’ : Enables subscript access of locations
Notes
This method will only create the requested matrix in the specified
format if one is not already saved on the object. If not present,
this method will create and return the matrix, as well as store it
for future use.
To obtain a matrix with weights other than pore IDs at each non-zero
location use create_incidence_matrix.
Returns an incidence matrix in the specified sparse format, with pore
IDs indicating the non-zero values.
Parameters:
fmt (string, optional) –
The sparse storage format to return. Options are:
’coo’ : (default) This is the native format of OpenPNM data
’lil’ : Enables row-wise slice of the matrix
’csr’ : Favored by most linear algebra routines
’dok’ : Enables subscript access of locations
Notes
This method will only create the requested matrix in the specified
format if one is not already saved on the object. If not present,
this method will create and return the matrix, as well as store it
for future use.
To obtain a matrix with weights other than pore IDs at each non-zero
location use create_incidence_matrix.
Returns the number of neigbhoring pores for each given input pore
Parameters:
pores (array_like) – Pores whose neighbors are to be counted
flatten (boolean (optional)) – If False (default) the number of pores neighboring each input
pore as an array the same length as pores. If True the
sum total number of is counted.
mode (string) –
The logic to apply to the returned count of pores.
’or’ : (default) All neighbors of the input pores. This is
also known as the ‘union’ in set theory or ‘any’ in boolean logic.
Both keywords are accepted and treated as ‘or’.
’xor’ : Only neighbors of one and only one input pore. This
is useful for counting the pores that are not shared by any of the
input pores. This is known as ‘exclusive_or’ in set theory, and
is an accepted input.
’xnor’ : Neighbors that are shared by two or more input pores.
This is equivalent to counting all neighbors with ‘or’, minus those
found with ‘xor’, and is useful for finding neighbors that the
inputs have in common.
’and’ : Only neighbors shared by all input pores. This is also
known as ‘intersection’ in set theory and (somtimes) as ‘all’ in
boolean logic. Both keywords are accepted and treated as ‘and’.
Returns:
If flatten is False, a 1D array with number of neighbors in each
element, otherwise a scalar value of the number of neighbors.
Notes
This method literally just counts the number of elements in the array
returned by find_neighbor_pores using the same logic. Explore
those methods if uncertain about the meaning of the mode argument
here.
Simple cubic lattice with connectivity from 6 to 26
Though simple, the Cubic network offers many advantages such as easy
visualization and accurate determination of domain area and length in
transport calculations.
Parameters:
shape (array_like) – The [Nx, Ny, Nz] size of the network in terms of the number of pores in
each direction
spacing (array_like, optional) – The spacing between pore centers in each direction. If not given, then
[1, 1, 1] is assumed.
connectivity (int, optional) –
The number of connections to neighboring pores. Connections are made
symmetrically to any combination of face, edge, or corners neighbors.
The default is 6 to create a simple cubic structure, but options are:
6: Faces only
8: Corners only
12: Edges only
14: Faces and Corners
18: Faces and Edges
20: Edges and Corners
26: Faces, Edges and Corners
For a more random distribution of connectivity, use a high
connectivity (i.e. 26) and then delete a fraction of the throats
using openpnm.topotools.reduce_coordination.
name (string) – An optional name for the object to help identify it. If not given,
one will be generated.
project (OpenPNM Project object, optional) – Each OpenPNM object must be part of a Project. If none is supplied
then one will be created and this Network will be automatically
assigned to it. To create a Project use openpnm.Project().
The distance between pore centers. This value becomes meaningless
if the topology is manipulated at all (i.e. by adding boundary pores)
since there is not unique or consistent value. In such cases an
exception is thrown.
Add pores to the faces of the network for use as boundary pores.
Pores are offset from the faces by 1/2 a lattice spacing such that
they lie directly on the boundaries.
Parameters:
labels (string or list of strings) – The labels indicating the pores defining each face where boundary
pores are to be added (e.g. ‘left’ or [‘left’, ‘right’])
spacing (scalar or array_like) – The spacing of the network (e.g. [1, 1, 1]). This should be given
since it can be quite difficult to infer from the network, for
instance if boundary pores have already added to other faces.
Apply data to the network based on a rectangular array filled with
values. Each array location corresponds to a pore in the network.
Parameters:
array (array_like) – The rectangular array containing the values to be added to the
network. This array must be the same shape as the original network.
propname (string) – The name of the pore property being added.
Body centered cubic lattice plus face centered nodes on the surfaces
This network is essentially a ‘bcc’ lattice, except that the seconary
network (body-centered pores) has pores on each face of the domain, which
breaks the body-centric arranagement. This allows boundary conditions to
be applied to the seconary network for transport simuations.
Parameters:
shape (list of ints) – The size and shape of the primary cubic network in terms of the
number of pores in each direction. Secondary nodes will be added at
centers of each unit cell.
spacing (list of floats) – The distance between pores of the primary network in each of the
principal directions
label_1 (string) – The label to apply to the primary cubic lattices, which defaults to
‘primary’
label_2 (string) – The label to apply to the secondary cubic lattices, which defaults to
‘seconary’
project (OpenPNM Project object (optional)) – If not provided one will be generated and the network will be assigned
to it. It can be retrieved from net.project.
name (string) – An optional name for the object to help identify it. If not given,
one will be generated.
>>> importopenpnmasop>>> pn=op.network.CubicDual(shape=[3,3,3])>>> pn.num_pores('pore.primary')# Normal cubic network is present27>>> pn.Np# But more pores are present from seconary network59
And it can be plotted for quick visualization using:
Add boundary pores to the specified faces of the network
Pores are offset from the faces by 1/2 of the given spacing, such
that they lie directly on the boundaries.
Parameters:
labels (string or list of strings) – The labels indicating the pores defining each face where boundary
pores are to be added (e.g. ‘left’ or [‘left’, ‘right’])
spacing (scalar or array_like) – The spacing of the network (e.g. [1, 1, 1]). This must be given
since it can be quite difficult to infer from the network,
for instance if boundary pores have already added to other faces.
class openpnm.network.CubicTemplate(template, spacing=[1, 1, 1], **kwargs)[source]¶
Bases: openpnm.network.Cubic.Cubic
Simple cubic lattice with arbitrary domain shape specified by a template
image
The class creates a standard Cubic network the same shape as the provided
image, then trims pores from the network that are not in the mask.
Parameters:
template (array_like) – The array (image) describing the desired shape of the domain. All
locations in the image that are marked as True are kept while the
rest of trimmed to yeild the shape.
spacing (array_like, optional) – The spacing between pore centers in each direction. If not given, then
[1, 1, 1] is assumed.
name (string) – An optional name for the object to help identify it. If not given,
one will be generated.
project (OpenPNM Project object, optional) – Each OpenPNM object must be part of a Project. If none is supplied
then one will be created and this Network will be automatically
assigned to it. To create a Project use openpnm.Project().
Notes
The other arguments are the same as Cubic except that shape is
inferred from the template image.
Crystal lattice types including fcc, bcc, sc, and hcp
These arrangements not only allow more dense packing than the standard
Cubic for higher porosity materials, but also have more interesting
non-straight connections between the various pore sites.
shape (array_like) – The number of pores in each direction. This value is a bit ambiguous
for the more complex unit cells used here, but generally refers to the
the number for ‘corner’ sites
spacing (array_like (optional)) – The spacing between pores in all three directions. Like the shape
this is a bit ambiguous but refers to the spacing between corner sites.
Essentially it controls the dimensions of the unit cell. It a scalar
is given it is applied to all directions. The default is 1.
mode (string) –
The type of lattice to create. Options are:
’sc’ : Simple cubic (Same as Cubic)
’bcc’ : Body-centered cubic lattice
’fcc’ : Face-centered cubic lattice
’hcp’ : Hexagonal close packed (Note Implemented Yet)
name (string) – An optional name for the object to help identify it. If not given,
one will be generated.
project (OpenPNM Project object, optional) – Each OpenPNM object must be part of a Project. If none is supplied
then one will be created and this Network will be automatically
assigned to it. To create a Project use openpnm.Project().
The pores are labelled as beloning to ‘corner_sites’ and ‘body_sites’ in
bcc or ‘face_sites’ in fcc. Throats are labelled by the which type of
pores they connect, e.g. ‘throat.corner_to_body’.
Limitations:
Bravais lattice can also have a skew to them, but this is not implemented
yet.
* Support for 2D networks has not been added yet.
* Hexagonal Close Packed (hcp) has not been implemented yet, but is on the
todo list.
Since these three networks all have the same domain size, it is clear that
both ‘bcc’ and ‘fcc’ have more pores per unit volume. This is particularly
helpful for modeling higher porosity materials.
They all have the same number corner sites, which corresponds to the
[3, 3, 3] shape that was specified:
Visualization of these three networks can be done quickly using the
functions in topotools. Firstly, merge them all into a single network
for convenience:
Add boundary pores to the specified faces of the network
Pores are offset from the faces by 1/2 of the given spacing, such
that they lie directly on the boundaries.
Parameters:
labels (string or list of strings) – The labels indicating the pores defining each face where boundary
pores are to be added (e.g. ‘left’ or [‘left’, ‘right’])
spacing (scalar or array_like) – The spacing of the network (e.g. [1, 1, 1]). This must be given
since it can be quite difficult to infer from the network,
for instance if boundary pores have already added to other faces.
Random network formed by Voronoi tessellation of arbitrary base points
Parameters:
points (array_like, optional) – The base points around which to generate the Voronoi tessellation.
num_points (scalar, optional) – If points is not supplied, then this must be given. A sent of
randomly located points will be generated.
shape (array_like) –
The size of the domain. It’s possible to create cubic as well as 2D
square domains by changing the shape as follows:
[x, y, z] - will produce a normal cubic domain of dimension x, and
and z
[x, y, 0] - will produce a 2D square domain of size x by y
name (string) – An optional name for the object to help identify it. If not given,
one will be generated.
project (OpenPNM Project object, optional) – Each OpenPNM object must be part of a Project. If none is supplied
then one will be created and this Network will be automatically
assigned to it. To create a Project use openpnm.Project().
Notes
By definition these points will each lie in the center of a Voronoi cell,
so they will not be the pore centers. The number of pores in the
returned network thus will differ from the number of points supplied
Random network formed by Delaunay tessellation of arbitrary base points
Parameters:
num_points (scalar) – The number of points to place in the domain, which will become the
pore centers after the tessellation is performed. This value is
ignored if points are given.
points (array_like) – An array of coordinates indicating the [x, y, z] locations of each
point to use in the tessellation. Note that the points must be given
in rectilinear coordinates regardless of which domain shape was
specified. To convert between coordinate systems see the
convert_coords function in the openpnm.topotools module.
shape (array_like) –
The size of the domain. It’s possible to create cubic as well as 2D
square domains by changing the shape as follows:
[x, y, z] - will produce a normal cubic domain of dimension x, and
and z
[x, y, 0] - will produce a 2D square domain of size x by y
name (string) – An optional name for the object to help identify it. If not given,
one will be generated.
project (OpenPNM Project object, optional) – Each OpenPNM object must be part of a Project. If none is supplied
then one will be created and this Network will be automatically
assigned to it. To create a Project use openpnm.Project().
Upon visualization it can be seen that this network is not very cubic.
There are a few ways to combat this, but none will make a truly square
domain. Points can be generated that lie outside the domain shape
and they will be automatically trimmed.
>>> pts=np.random.rand(300,3)*1.2-0.1# Must have more points for same density>>> gn=op.network.Delaunay(points=pts,shape=[1,1,1])>>> gn.Np<300# Confirm base points have been trimmedTrue
And visualizing:
>>> fig=op.topotools.plot_connections(network=gn)
If a domain random base points, but truly flat faces is needed use
Voronoi.
class openpnm.network.Gabriel(shape, num_points=None, **kwargs)[source]¶
Bases: openpnm.network.Delaunay.Delaunay
Random network formed by Gabriel tessellation of arbitrary base points
This operates by performing a Deluanay tessellation, then removing
connections that do not adhere to the definition of the Gabriel graph
This produces a network that has fewer throats than a Delaunay network.
Since the longer-range throats tend to be removed this might be more
realistic in some cases.
Parameters:
points (array_like) – An array of coordinates indicating the [x, y, z] locations of each
point to use in the tessellation. Note that the points must be given
in rectilinear coordinates regardless of which domain shape was
specified. To convert between coordinate systems see the
convert_coords function in the openpnm.topotools module.
num_points (scalar) – The number of points to place in the domain, which will become the
pore centers after the tessellation is performed. This value is
ignored if points are given.
shape (array_like) –
The size of the domain. It’s possible to create cubic, or 2D square
domains by changing the domain shape as follows:
[x, y, z] - will produce a normal cubic domain of dimension x, and
and z
[x, y, 0] - will produce a 2D square domain of size x by y
name (string) – An optional name for the object to help identify it. If not given,
one will be generated.
project (OpenPNM Project object, optional) – Each OpenPNM object must be part of a Project. If none is supplied
then one will be created and this Network will be automatically
assigned to it. To create a Project use openpnm.Project().
Examples
>>> importopenpnmasop>>> importscipyassp>>> pts=np.random.rand(100,3)*[1,1,0]# Set z-axis to 0>>> gn=op.network.Gabriel(shape=[1,1,0],points=pts)>>> dn=op.network.Delaunay(shape=[1,1,0],points=pts)
Combined and interconnected Voronoi and Delaunay tessellations
A Delaunay tessellation is performed on a set of base points then the
corresponding Voronoi diagram is generated. Finally, each Delaunay node
is connected to it’s neighboring Voronoi vertices to create interaction
between the two networks.
All pores and throats are labelled according to their network (i.e.
‘pore.delaunay’), so they can be each assigned to a different Geometry.
The dual-nature of this network is meant for modeling transport in the void
and solid space simultaneously by treating one network (i.e. Delaunay) as
voids and the other (i.e. Voronoi) as solid. Interaction such as heat
transfer between the solid and void can be accomplished via the
interconnections between the Delaunay and Voronoi nodes.
Parameters:
num_points (integer) – The number of random base points to distribute inside the domain.
These points will become connected by the Delaunay triangulation. The
points will be generated by calling generate_base_points in
topotools.
points (array_like (num_points x 3)) – A list of coordinates for pre-generated points, typically produced
using generate_base_points in topotools. Note that base points
should extend beyond the domain so that degenerate Voronoi points
can be trimmed.
shape (array_like) –
The size and shape of the domain used for generating and trimming
excess points. The coordinates are treated as the outer corner of a
rectangle [x, y, z] whose opposite corner lies at [0, 0, 0].
By default, a domain size of [1, 1, 1] is used. To create a 2D network
set the Z-dimension to 0.
name (string) – An optional name for the object to help identify it. If not given,
one will be generated.
project (OpenPNM Project object, optional) – Each OpenPNM object must be part of a Project. If none is supplied
then one will be created and this Network will be automatically
assigned to it. To create a Project use openpnm.Project().
Examples
Points will be automatically generated if none are given:
Add boundary pores to the specified faces of the network
Pores are offset from the faces of the domain.
Parameters:
labels (string or list of strings) – The labels indicating the pores defining each face where boundary
pores are to be added (e.g. ‘left’ or [‘left’, ‘right’])
offset (scalar or array_like) – The spacing of the network (e.g. [1, 1, 1]). This must be given
since it can be quite difficult to infer from the network,
for instance if boundary pores have already added to other faces.
Finds the indices of the Voronoi nodes that define the convex hull
around the given Delaunay nodes.
Parameters:
pores (array_like) – The pores whose convex hull are sought. The given pores should be
from the ‘delaunay’ network. If no pores are given, then the hull
is found for all ‘delaunay’ pores.
Notes
This metod is not fully optimized as it scans through each pore in a
for-loop, so could be slow for large networks.
Finds the indicies of the Voronoi nodes that define the facet or
ridge between the Delaunay nodes connected by the given throat.
Parameters:
throats (array_like) – The throats whose facets are sought. The given throats should be
from the ‘delaunay’ network. If no throats are specified, all
‘delaunay’ throats are assumed.
Notes
The method is not well optimized as it scans through each given throat
inside a for-loop, so it could be slow for large networks.
The geometry module contains the GenericGeometry class, and an
assortment of subclasses that implement specific pore-scale geometrical models
The GenericGeometry Class
Geometry objects (as well as Physics objects) are Subdomain subclasses,
which allow them to be assigned to subset of the full domain (although this is
not alway necessary). This functionality was added so that networks with
distinct regions could be modelled by giving each region its own Geometry
with unique models (e.g. to give a bi-modal pore size distribution).
Library of Preconfigured Geometry Classes
This module contains a small selection of Geometry classes that are
pre-configured with a selection of pore-scale models. These classes provide
a good starting point, but generally the choice of models and parameters used
will be specific to each problem and must be designed by the user.
The StickAndBall class, as it’s name suggests assumes spherical pores and
cylindrical throats. Pore sizes are assigned by finding the largest sphere
that can fit at each site (this will be dictated by the lattice spacing used
when generating the Network), then scaling that value by a random number
between 0 and 0.1. Throat diameters are taken as half the size of the smaller
of it’s two neighbors. All other properties are calculated using the geometry
of spheres and throats.
The table belows shows the specific models used on StickAndBall:
#
Property Name
Parameter
Value
1
pore.seed
model:
random
2
pore.max_size
model:
largest_sphere
3
pore.diameter
model:
product
4
pore.area
model:
sphere
5
pore.volume
model:
sphere
6
throat.max_size
model:
from_neighbor_pores
7
throat.diameter
model:
scaled
8
throat.length
model:
piecewise
9
throat.surface_area
model:
cylinder
10
throat.volume
model:
cylinder
11
throat.area
model:
cylinder
Customizing a Preconfigured Geometry Instance
Perhaps the StickAndBall class is almost suitable but you wish to decrease
the pores sizes. The following example illustrates how to alter the
'pore.size' model accordingly:
We can reach into the models attribute and change the parameters of any
model as follows:
>>> max(geo['pore.diameter'])<1.0# Confirm largest pore is less than 1.0True>>> geo.models['pore.seed']['num_range']=[0.2,0.7]>>> geo.regenerate_models()# Must regenerate all models>>> max(geo['pore.diameter'])<0.7# Largest pore is now less than 0.7True
This example illustrated that you can change one property (‘pore.seed’) and
that change can be cascaded to all dependent properties (‘pore.diameter’).
This generic class is meant as a starter for custom Geometry objects
It has no pore-scale models assigned to it, so a a blank slate. Note that
all OpenPNM Geometry sub-classes are just GenericGeometry instances with a
number of models added.
Parameters:
network (OpenPNM Network Object) – The Network object to which this Geometry applies.
pores (array_like) – The list of pores where this Geometry applies.
throats (array_like) – The list of throats where this Geometry applies.
name (string) – A unique name to apply to the object. This name will also be used as a
label to identify where this Geometry applies.
project (OpenPNM Project object (optional)) – A Project can be specified instead of network.
Examples
>>> importopenpnmasop>>> pn=op.network.Cubic(shape=[5,5,5])>>> Ps=pn.pores('all')# Get all pores>>> Ts=pn.throats('all')# Get all throats>>> geom=op.geometry.GenericGeometry(network=pn,pores=Ps,throats=Ts)
Stick and Ball subclass of GenericGeometry. This subclass is meant as a
basic default geometry to get started quickly.
Pore diameters are randomly assigned between 0 and the largest sphere that
does not overlap with it’s nearest neighbor.
Throat diameters are half the diameter of the smaller of it’s two
neighboring pores.
Parameters:
network (OpenPNM Network object) – The network with which this Geometry should be associated
project (OpenPNM Project object, optional) – Can be supplied instead of a network
pores (array_like) – The pores in the domain where this Geometry applies
throats (array_like) – The throats in the domain where this Geometry applies
name (string) – The name of the object, which is also used as the label where this
geometry is defined.
Examples
Geometry objects (along with Physics objects) can be applied to a subset
of pores and/or throats. This allows for different geometrical property
models to be applied in different regions. This is illustrated in the
following code:
This module contains the GenericPhase class, plus several subclasses which
are preconfigured to have the properties of specific fluids
The GenericPhase Class
The GenericPhase class is a direct child of the Base class, so contains
the usual methods such as find pore indices based on labels. It does, however,
also inherit from ModelsMixin so has methods for add, removing and
regenerating models.
Library of Preconfigured Phase Classes
OpenPNM include a few Phase subclasses that contain a suite of pre-configured
models that predict the thermophysical properties of certain common phases.
Class
Comments
Water
Most models include the impact of salinity
Air
A mixture of O2 and N2, but no humidity
Mercury
Useful for porosimetry simulations, assumed theta is 140
Customizing a GenericPhase Instance
The GenericPhase class has no pore-scale models attached, so is a blank
slate for creating custom Phases. The following code snippet illustrates how
to do this to create an oil phase with a temperature dependent viscosity
model based on a simple 2nd order polynomial:
Note that the oil viscosity has NOT changed! To propigate the new temperature
to all the other calculated pore-scale properties, call the
regenerate_models function:
This generic class is meant as a starter for custom Phase objects
This class produces a blank-slate object with no pore-scale models for
calculating any thermophysical properties. Users must add models and
specify parameters for all the properties they require.
Parameters:
network (OpenPNM network object) – The network with which this object is associated
project (OpenPNM Project object, optional) – The Project with which the object should be assigned. If not supplied
then a new Project is created
name (string, optional) – The unique name of the object. If not given one will be generated.
Creates Phase object with preset models and values for air.
Parameters:
network (OpenPNM Network object) – The network to which this phase object will be attached.
project (OpenPNM Project object, optional) – The Project with which this phase should be associted. If a
network is given then this is ignored and the Network’s project is
used. If a network is not given then this is mandatory.
name (string, optional) – The name of the phase. This is useful to keep track of the objects
throughout the simulation. The name must be unique to the project. If
no name is given, one is generated.
The table below shows all of the pore-scale models that are included with
this class to calculate the physical properties of this fluid as functions
of the relevant state variables.
This object is initialized at standard conditions of 298 K and 101325 Pa.
If these conditions are changed the dependent properties can be
recalculated by calling regenerate_models.
All of these parameters can be adjusted manually by editing the entries in
the ModelsDict stored in the models attribute of the object.
For a full listing of models and their parameters use print(obj.models)
where obj is the handle to the object.
In addition to these models, this class also has a number of constant
values assigned to it which can be found by running
props(mode='constants').
#
Property Name
Parameter
Value
1
pore.molar_density
model:
ideal_gas
regen_mode
normal
pressure
pore.pressure
temperature
pore.temperature
2
pore.diffusivity
model:
fuller
MA
0.032
MB
0.028
vA
16.6
vB
17.9
regen_mode
normal
temperature
pore.temperature
pressure
pore.pressure
3
pore.thermal_cond…
model:
polynomial
prop
pore.temperature
a
[0.00422791, 7.89606e…
regen_mode
normal
4
pore.viscosity
model:
polynomial
prop
pore.temperature
a
[1.82082e-06, 6.51815…
regen_mode
normal
References
The pore scale models for this class are taken from:
[1] E.W. Lemmon and R.T. Jacobsen, “Viscosity and Thermal Conductivity
Equations for Nitrogen, Oxygen, Argon, and Air”, Int. J. of Thermophysics,
Vol. 25, No. 1, January 2004, pp. 21-69
The table below shows all of the pore-scale models that are included with
this class to calculate the physical properties of this fluid as functions
of the relevant state variables.
This object is initialized at standard conditions of 298 K and 101325 Pa.
If these conditions are changed the dependent properties can be
recalculated by calling regenerate_models.
All of these parameters can be adjusted manually by editing the entries in
the ModelsDict stored in the models attribute of the object.
For a full listing of models and their parameters use print(obj.models)
where obj is the handle to the object.
In addition to these models, this class also has a number of constant
values assigned to it which can be found by running
props(mode='constants').
The table below shows all of the pore-scale models that are included with
this class to calculate the physical properties of this fluid as functions
of the relevant state variables.
This object is initialized at standard conditions of 298 K and 101325 Pa.
If these conditions are changed the dependent properties can be
recalculated by calling regenerate_models.
All of these parameters can be adjusted manually by editing the entries in
the ModelsDict stored in the models attribute of the object.
For a full listing of models and their parameters use print(obj.models)
where obj is the handle to the object.
In addition to these models, this class also has a number of constant
values assigned to it which can be found by running
props(mode='constants').
This generic class is meant as a starter for custom Physics objects
It produces a blank object with no pore-scale models attached. Users can
add models from the models module (or create their own).
Parameters:
network (OpenPNM Network object) – The network to which this Physics should be attached
phase (OpenPNM Phase object) – The Phase object to which this Physics applies
geometry (OpenPNM Geometry object) – The Geometry object that defines the pores/throats where this Physics
should be applied.
name (str, optional) – A unique string name to identify the Physics object, typically same as
instance name but can be anything. If left blank, and name will be
generated that include the class name and a random string.
network (OpenPNM Network object) – The network to which this Physics should be attached
phase (OpenPNM Phase object) – The Phase object to which this Physics applies
geometry (OpenPNM Geometry object) – The Geometry object that defines the pores/throats where this Physics
should be applied.
name (str, optional) – A unique string name to identify the Physics object, typically same as
instance name but can be anything. If left blank, and name will be
generated that include the class name and a random string.
This module provides a library of preconfigured Network-Geometry combinations.
In most case the topology and geometry cannot be considered in isolation.
This module provides recipes that create both the Network and Geometry objects
simultaneously to ensure sensible correspondance between things like lattice
spacing and pore sizes. Some of the classes in this module have a signficant
amount of custom code (e.g. VoronoiFibers), while others are simple
recipes that combine existing models in OpenPNM (e.g. BereaCubic).
The table below gives a list of available Material generators:
Material Name
Description
VoronoiFibers
Resembles a fibrous paper or mat with straight
intersecting fibers.
Resembles a fibrous paper or mat with straight intersecting fibers.
Two geometries are created: DelaunayGeometry defines the pore space
with pores connected by a Delaunay tesselation and VoronoiGeometry defines
the fiber space with fibers forming the edges of the Voronoi diagram.
The two geometries are complimentary and can be accessed individually
via the project associated with this network object.
This class is a subclass of the DelaunayVoronoiDual network, and many of
the parameters behave in the exact same way. However, currently a
rectangular shape is the only one supported. Additionally, an image is
created for calculating size data and two parameters relate to this:
fiber_rad and resolution as detailed below.
Parameters:
num_points (integer) – The number of random base points to distribute inside the domain.
These points will become connected by the Delaunay triangulation. The
points will be generated by calling generate_base_points in
topotools.
points (array_like (num_points x 3)) – A list of coordinates for pre-generated points, typically produced
using generate_base_points in topotools. Note that base points
should extend beyond the domain so that degenerate Voronoi points
can be trimmed. Note: the spherical and cylindrical options
cannot be used here.
shape (array_like) –
The size and shape of the domain using for generating and trimming
excess points. It’s treated as the outer corner of rectangle [x, y, z]
whose opposite corner lies at [0, 0, 0].
By default, a domain size of [1, 1, 1] is used.
fiber_rad (float) – fiber radius to apply to Voronoi edges when calculating pore and throat
sizes
resolution (boolean) – Determines the size of each voxel in the image. Care should be made to
appropriately set the resolution based on the fiber_radius and the
shape of the domain so as to remain within memory constraints.
linear_scale (array_like (len 3)) – This is applied to the domain size before placing the points and then
reversed afterwards and has the effect of introducing anisotropy into
an otherwise uniformly distributed point distribution. By default no
scaling is applied. Applying [1, 1, 2] will stretch the domain by a
factor of 2 in the z-direction and this will have the affect of
aligning fibers in the x and y directions once scaling is reversed.
References
This approach to modeling fibrous materials was first presented by
Thompson [1] for simulating fluid imbibition in sorbent paper products.
Gostick [2], and Tranter et al.[3, 4] have subsequently used it to model
electrodes in fuel cells.
[1] K. E. Thompson, AlChE J., 48, 1369 (2002)
[2] J. T. Gostick, Journal of the Electrochemical Society 2013, 160, F731.
[3] T. G. Tranter et al. Fuel Cells, 2016, 16, 4, 504-515
[4] T. G. Tranter et al. Transport in Porous Media, 2018, 121, 3, 597-620
Examples
Points will be automatically generated if none are given:
This class implements steady-state linear transport calculations
Parameters:
network ((OpenPNM Network object)) – The network object to which this algorithm will apply.
name ((string, optional)) – Name of the algorithm
project ((OpenPNM Project object, optional)) – Either a Network or a Project must be supplied
Notes
The following table shows the methods that are accessible to the user
for setting up the simulation.
Methods
Description
set_value_BC
Applies constant value boundary conditions to the
specified pores
set_rate_BC
Applies constant rate boundary conditions to the
specified pores
remove_BC
Removes all boundary conditions from the
specified pores
rate
Calculates the total rate of transfer through the
given pores or throats
setup
A shortcut for applying values in the settings
attribute.
results
Returns the results of the calcualtion as a
dict with the data stored under the ‘quantity’
specified in the settings
In addition to the above methods there are also the following attributes:
Attribute
Description
A
Retrieves the coefficient matrix
b
Retrieves the RHS matrix
This class contains quite a few hidden methods (preceeded by an
underscore) that are called internally. Since these are critical to the
functioning of this algorithm they are worth outlining even though the
user does not call them directly:
Method or Attribute
Description
_build_A
Builds the A matrix based on the
‘conductance’ specified in settings
_build_b
Builds the b matrix
_apply_BCs
Applies the given BCs by adjust the A and
b matrices
_calc_eff_prop
Finds the effective property (e.g. permeability
coefficient) based on the given BCs
_solve
Runs the algorithm using the solver specified
in the settings
_get_domain_area
Attempts to estimate the area of the inlet pores
if not specified by user
_get_domain_length
Attempts to estimate the length between the
inlet and outlet faces if not specified by the
user
Builds the coefficient matrix based on conductances between pores.
The conductance to use is specified in the algorithm’s settings
under conductance. In subclasses (e.g. FickianDiffusion)
this is set by default, though it can be overwritten.
Builds the RHS matrix, without applying any boundary conditions or
source terms. This method is trivial an basically creates a column
vector of 0’s.
Parameters:
force (Boolean (default is False)) – If set to True then the b matrix is built from new. If
False (the default), a cached version of b is returned. The
cached version is clean in the sense that no boundary conditions
or sources terms have been added to it.
Sends the A and b matrices to the specified solver, and solves for x
given the boundary conditions, and source terms based on the present
value of x. This method does NOT iterate to solve for non-linear
source terms or march time steps.
Parameters:
A (sparse matrix) – The coefficient matrix in sparse format. If not specified, then
it uses the A matrix attached to the object.
b (ND-array) – The RHS matrix in any format. If not specified, then it uses
the b matrix attached to the object.
x0 (ND-array) – The initial guess for the solution of Ax = b
Notes
The solver used here is specified in the settings attribute of the
algorithm.
Apply constant rate boundary conditons to the specified locations.
This is similar to a Neumann boundary condition, but is
slightly different since it’s the conductance multiplied by the
gradient, while Neumann conditions specify just the gradient.
Parameters:
pores (array_like) – The pore indices where the condition should be applied
values (scalar or array_like) – The values of rate to apply in each pore. If a scalar is supplied
it is assigned to all locations, and if a vector is applied it
must be the same size as the indices given in pores.
mode (string, optional) –
Controls how the boundary conditions are applied. Options are:
’merge’
(Default) Adds supplied boundary conditions to
already existing conditions
’overwrite’
Deletes all boundary condition on object then
adds the given ones
Notes
The definition of quantity is specified in the algorithm’s
settings, e.g. alg.settings['quantity']='pore.pressure'.
Apply constant value boundary conditons to the specified locations.
These are sometimes referred to as Dirichlet conditions.
Parameters:
pores (array_like) – The pore indices where the condition should be applied
values (scalar or array_like) – The value to apply in each pore. If a scalar is supplied
it is assigne to all locations, and if a vector is applied is
must be the same size as the indices given in pores.
mode (string, optional) –
Controls how the boundary conditions are applied. Options are:
’merge’
(Default) Adds supplied boundary conditions to
already existing conditions
’overwrite’
Deletes all boundary condition on object then
adds the given ones
Notes
The definition of quantity is specified in the algorithm’s
settings, e.g. alg.settings['quantity']='pore.pressure'.
This allows the reuse of an algorithm inside a for-loop for parametric
studies. The default behavior means that only alg.reset() and
alg.run() must be called inside a loop. To reset the algorithm
more completely requires overriding the default arguments.
Parameters:
results (boolean) – If True (default) all previously calculated values pertaining
to results of the algorithm are removed.
bcs (boolean (default = False)) – If True all previous boundary conditions are removed.
source_terms (boolean) – If True removes source terms. The default is False.
This method takes several arguments that are essential to running the
algorithm and adds them to the settings
Parameters:
phase ((str)) – The name of the phase on which the algorithm acts
phase – The name of the phase on which the algorithm acts
Notes
Under-relaxation is a technique used for improving stability of a
computation, particularly in the presence of highly non-linear terms.
Under-relaxation used here limits the change in a variable from one
iteration to the next. An optimum choice of the relaxation factor is
one that is small enough to ensure stable simulation and large enough
to speed up the computation.
A subclass of ReactiveTransport for transient/steady-state simulations
Parameters:
network (OpenPNM Network object) – The Network with which this algorithm is associated.
project (OpenPNM Project object) – Either a Network or a Project must be specified.
Notes
This subclass performs steady and transient simulations of transport
phenomena with reactions when source terms are added. It supports 3 time
discretization schemes; ‘steady’ to perform a steady-state simulation, and
‘implicit’ (fast, 1st order accurate) and ‘cranknicolson’ (slow, 2nd order
accurate) both for transient simulations.
Fetches the calculated quantity from the algorithm and returns it as
an array.
Parameters:
times (scalar, ND-array, list of scalars, None, or string) – Time steps to be returned. The default value is None which results
in returning all time steps. If times is a scalar, only the
corresponding time step is returned. If times is an ND-array or a
list of scalars, time steps in the provided array or list are
returned. If times is ‘final’ or ‘actual’, the current value of the
quantity is returned.
t_precision (integer) – The time precision (number of decimal places). Default value is 12.
Notes
The keyword steps is interpreted in the same way as times.
Builds ‘A’ matrix of the steady system of equations to be used at each
time step to build transient ‘A’ and ‘b’. Imposes the initial
conditions and stores the initial field. Initialize transient ‘A’, ‘b’,
and source term (if present) and finally calls the transient solver.
Parameters:
t (scalar) – The time to start the simulation from. If no time is specified, the
simulation starts from ‘t_initial’ defined in the settings.
values (ND-array or scalar) – Set the initial conditions using an ‘Np’ long array. ‘Np’ being
the number of pores. If a scalar is given, the same value is
imposed to all pores.
This method takes several arguments that are essential to running the
algorithm and adds them to the settings
Notes
More settings can be adjusted in the presence of a non-linear source
term such as under-relaxation.
See the ‘ReactiveTransport’ class documentation for details.
Ordinary percolation simulation with or without access limitations.
Parameters:
network (OpenPNM Network object) – The Network upon which this simulation should be run
name (string, optional) – An identifying name for the object. If none is given then one is
generated.
project (OpenPNM Project object) – Either a Network or a Project must be specified
Notes
Ordinary percolation refers the process of finding all bonds or sites in
the network that can be invaded at a given threshold, then setting them
all to invaded in a single step.
Optionally, it is possible to then find the clusters of invaded bonds or
sites that are NOT connected to the inlets and setting them back to
an uninvaded state.
An overview of percolation theory can be found on Wikipedia
If the simulation is repeated for increasing threshold values until the
entire domain is invaded, then a percoaltion curve is obtained. The
threshold at which each site and bond was invaded is recorded, so it is
possible to find invading configurations easily using Boolean logic.
Returns a True or False value to indicate if a percolating cluster
spans between the inlet and outlet pores that were specified at the
given applied pressure.
Parameters:
applied_pressure (scalar, float) – The pressure at which percolation should be checked
Returns:
Return type:
A simple boolean True or False if percolation has occured or not.
Resets the various data arrays on the object back to their original
state. This is useful for repeating a simulation at different inlet
conditions, or invasion points for instance.
This method determines which pores and throats are filled with invading
phase at the specified capillary pressure, and creates several arrays
indicating the occupancy status of each pore and throat for the given
pressure.
Parameters:
Pc (scalar) – The capillary pressure for which an invading phase configuration
is desired.
Returns:
A dictionary containing an assortment of data about distribution
of the invading phase at the specified capillary pressure. The data
include
**’pore.occupancy’** (A value between 0 and 1 indicating the)
fractional volume of each pore that is invaded. If no late pore
filling model was applied, then this will only be integer values
(either filled or not).
**’throat.occupancy’** (The same as ‘pore.occupancy’ but for throats.)
This dictionary can be passed directly to the update method of
the *Phase object. These values can then be accessed by models*
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.
Set the locations from which the invader enters the network
Parameters:
pores (array_like) – Locations that are initially filled with invader, from which
clusters grow and invade into the network
overwrite (boolean) – If True then all existing inlet locations will be removed and
then the supplied locations will be added. If False (default),
then supplied locations are added to any already existing inlet
locations.
Set the locations through which defender exits the network.
This is only necessary for calculating the percolation threshold.
Parameters:
pores (array_like) – Locations where the defender can exit the network. Any defender
that does not have access to these sites will be trapped.
overwrite (boolean) – If True then all existing outlet locations will be removed and
then the supplied locations will be added. If False (default),
then supplied locations are added to any already existing outlet
locations.
Specify locations of any residual invader. These locations are set
to invaded at the start of the simulation.
Parameters:
pores (array_like) – The pores locations that are to be filled with invader at the
beginning of the simulation.
throats (array_like) – The throat 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.
Used to specify necessary arguments to the simulation. This method is
useful for resetting the algorithm or applying more explicit control.
Parameters:
phase (OpenPNM Phase object) – The Phase object containing the physical properties of the invading
fluid.
access_limited (boolean) – If True the invading phase can only enter the network from the
invasion sites specified with set_inlets. Otherwise, invading
clusters can appear anywhere in the network. This second case is
the normal ordinary percolation in the traditional sense, while
the first case is more physically representative of invading
fluids.
mode (string) –
Specifies the type of percolation process to simulate. Options
are:
’bond’ - The percolation process is controlled by bond entry
thresholds.
’site’ - The percolation process is controlled by site entry
thresholds.
pore_entry_pressure (string) – The dictionary key on the Phase object where the pore entry
pressure values are stored. The default is
‘pore.capillary_pressure’. This is only accessed if the mode
is set to site percolation.
throat_entry_pressure (string) – The dictionary key on the Phase object where the throat entry
pressure values are stored. The default is
‘throat.capillary_pressure’. This is only accessed if the mode
is set to bond percolation.
'pore_volume' (string) – The dictionary key containing the pore volume information.
'throat_volume' (string) – The dictionary key containing the pore volume information.
Simulates mercury instrustion porosimetry using ordinary percolation
Parameters:
network (OpenPNM Network object) – The Network upon which this simulation should be run
name (string, optional) – An identifying name for the object. If none is given then one is
generated.
project (OpenPNM Project object) – Either a Network or a Project must be specified
Notes
Mercury intrusion progresses by applying increasing pressures to the
invading mercury phase, and measuring the resultant volume of invading
fluid. This corresponds directly to an ordinary percolation process,
with access limitations enabled.
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.
propname (string) – Dictionary key on the physics object(s) containing the pore
filling model(s) to apply.
Notes
It is assumed that these models are functions of the quantity
specified in the algorithms settings. This values is applied to the
corresponding phase just prior to regenerating the given pore-scale
model(s).
Used to specify necessary arguments to the simulation. This method is
useful for resetting the algorithm or applying more explicit control.
Parameters:
phase (OpenPNM Phase object) – The Phase object containing the physical properties of the invading
fluid.
quantity (string) – The name of the quantity calculated by this algorithm. This is
used for instance, by the late pore and throat filling models
to indicate the prevailing fluid pressure in the invading phase
for calculating the extent of filling. The default is
‘pressure’. Note that there is no need to specify ‘pore’ and/or
‘throat’ with this as the given value will apply to both.
throat_entry_pressure (string) – The dictionary key on the Phase object where the throat entry
pressure values are stored. The default is
‘throat.entry_pressure’.
pore_volume (string) – The dictionary key containing the pore volume information. The
default is ‘pore.volume’.
throat_volume (string) – The dictionary key containing the throat volume information. The
default is ‘throat.volume’.
pore_partial_filling (string) – The name of the model used to determine partial pore filling as
a function of applied pressure.
throat_partial_filling (string) – The name of the model used to determine partial throat filling as
a function of applied pressure.
A classic/basic invasion percolation algorithm optimized for speed.
Parameters:
network (OpenPNM Network object) – The Network upon which the invasion will occur.
Notes
This algorithm uses a binary heap to store all a list of all accessible
throats, sorted according to entry pressure. This means that item [0] in
the heap is the most easily invaded throat, so looking up which throat
to invade next is computationally trivial. In order to keep the list
sorted new throats to the list takes more time, however, the heap data
structure is very efficient at this. Interested users can consult the
wikipedia page on binary heaps for more information.
After running the algorithm the invading phase configuration at a given
saturation can be obtained and assigned to the phase object:
>>> water.update(ip.results(Snwp=0.5))
Because it was a 2D network it’s easy to quickly visualize the invasion
pattern as an image for verification:
Note
Because the network is 2D and cubic, an image can be generated with
color corresponding to a value. The following plots the entire
invasion sequence, and the water configuraiton at Snwp = 0.5.
Apply trapping based on algorithm described by Y. Masson [1].
It is applied as a post-process and runs the percolation algorithm in
reverse assessing the occupancy of pore neighbors. Consider the
following scenario when running standard IP without trapping,
3 situations can happen after each invasion step:
The number of defending clusters stays the same and clusters can
shrink
* A cluster of size one is suppressed
* A cluster is split into multiple clusters
In reverse the following opposite situations can happen:
The number of defending clusters stays the same and clusters can
grow
* A cluster of size one is created
* Mutliple clusters merge into one cluster
With trapping the reversed rules are adjusted so that only clusters
that do not connect to a sink can grow and merge. At the point that a
neighbor connected to a sink is touched the trapped cluster stops
growing as this is the point of trapping in forward invasion time.
Logger info displays the invasion sequence and pore index and a message
with condition number based on the modified trapping rules and the
assignment of the pore to a given cluster.
Initially all invaded pores are given cluster label -1
Outlets / Sinks are given -2
New clusters that grow into fully trapped clusters are either
identified at the point of breakthrough or grow from nothing if the
full invasion sequence is run, they are assigned numbers from 0 up.
Ref:
[1] Masson, Y., 2016. A fast two-step algorithm for invasion
percolation with trapping. Computers & Geosciences, 90, pp.41-48
Parameters:
outlets (list or array of pore indices for defending fluid to escape) –
through –
Returns:
Creates a throat array called ‘pore.clusters’ in the Algorithm
dictionary. Any positive number is a trapped cluster
Also creates 2 boolean arrays Np and Nt long called ‘<element>.trapped’
phase (OpenPNM Phase object) – The phase to be injected into the Network. The Phase must have the
capillary entry pressure values for the system.
entry_pressure (string) – The dictionary key to the capillary entry pressure. If none is
supplied then the current value is retained. The default is
‘throat.capillary_pressure’.
pore_volume (string) – The dictionary key to the pore volume. If none is supplied then
the current value is retained. The default is ‘pore.volume’.
throat_volume (string) – The dictionary key to the throat volume. If none is supplied then
the current value is retained. The default is ‘throat.volume’.
An implemetation of invasion percolation which can invade bonds, sites or a
mixture of both. Inlets can be treated as individual injection points that
share a common pressure or have their own and progess independently.
Inlets can also be single pores or clusters.
Parameters:
network (OpenPNM Network object) – The Network upon which the invasion should occur.
It is applied as a post-process and runs the percolation algorithm in
reverse assessing the occupancy of pore neighbors. 3 situations can
happen on invasion without trapping:
The number of defending clusters stays the same and clusters can shrink
A cluster of size one is suppressed
A cluster is split into multiple clusters
In reverse the following situations can happen:
The number of defending clusters stays the same and clusters can grow
A cluster of size one is created
Mutliple clusters merge into one cluster
With trapping the reversed rules are adjusted so that:
Only clusters that do not connect to a sink can grow and merge.
At the point that a neighbor connected to a sink is touched, the
trapped cluster stops growing as this is the point of trapping in
forward invasion time.
Logger info displays the invasion Sequence and pore index and a message
with condition number based on the modified trapping rules and the
assignment of the pore to a given cluster.
Initially all invaded pores are given cluster label -1
Outlets / Sinks are given -2
New clusters that grow into fully trapped clusters are either
identified at the point of breakthrough or grow from nothing if the
full invasion sequence is run, they are assigned numbers from 0 up.
References
[1] Masson, Y., 2016. A fast two-step algorithm for invasion
percolation with trapping. Computers & Geosciences, 90, pp.41-48
Returns:
Creates a throat array called ‘pore.clusters’ in the Algorithm
dictionary. Any positive number is a trapped cluster. Also creates 2
boolean arrays Np and Nt long called ‘<element>.trapped’
Resets the various data arrays on the object back to their original
state. This is useful for repeating a simulation at different inlet
conditions, or invasion points for instance.
max_pressure (float) – The maximum pressure applied to the invading cluster. Any pores and
throats with entry pressure above this value will not be invaded.
Set the locations through which defender exits the network.
This is only necessary if ‘trapping’ was set to True when setup
was called.
Parameters:
pores (array_like) – Locations where the defender can exit the network. Any defender
that does not have access to these sites will be trapped.
overwrite (boolean) – If True then all existing outlet locations will be removed and
then the supplied locations will be added. If False (default),
then supplied locations are added to any already existing outlet
locations.
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
Used to specify necessary arguments to the simulation. This method is
useful for resetting the algorithm or applying more explicit control.
Parameters:
phase (OpenPNM Phase object) – The Phase object containing the physical properties of the invading
fluid.
pore_entry_pressure (string) – The dictionary key on the Phase object where the pore entry
pressure values are stored. The default is
‘pore.entry_pressure’.
throat_entry_pressure (string) – The dictionary key on the Phase object where the throat entry
pressure values are stored. The default is
‘throat.entry_pressure’.
snap_off (string) – The dictionary key on the Phase object where the throat snap-off
pressure values are stored.
invade_isolated_Ts (boolean) – If True, isolated throats are invaded at the higher invasion
pressure of their connected pores.
late_pore_filling (string) – The name of the model used to determine late pore filling as
a function of applied pressure.
late_throat_filling (string) – The name of the model used to determine late throat filling as
a function of applied pressure.
A class to simulate binary diffusion with reactions
Parameters:
network ((OpenPNM Network object)) – The network object to which this algorithm will apply.
name ((string, optional)) – Name of the algorithm
project ((OpenPNM Project object, optional)) – Either a Network or a Project must be supplied
Notes
Fickian diffusion in porous materials occurs in the void space, but
becuase the diffusion is defined to pores it is impacted by the porosity
and tortuosity of the network. Thus the total diffusive flux through the
network is reduced. This class can be used to simualte diffusion-reaction
in domains with arbitrarily complex boundary conditions, or it can be used
to calculate the effective diffusivity of the network by applying
controlled boundary conditions on opposing faces, calculate the diffusion
rate, and inverting Fick’s first law:
\[D_{eff} = N_{A}*L/(A*\Delta C_{A})\]
This class includes a method for calculating Deff automatically assuming
appropriate boundary conditions were applied (calc_eff_diffusivity).
The length and area of the domain should be supplied, but if they are
not an attempt is made to calculate them.
This calculates the effective diffusivity in this linear transport
algorithm.
Parameters:
inlets (array_like) – The pores where the inlet composition boundary conditions were
applied. If not given an attempt is made to infer them from the
algorithm.
outlets (array_like) – The pores where the outlet composition boundary conditions were
applied. If not given an attempt is made to infer them from the
algorithm.
domain_area (scalar, optional) – The area of the inlet (and outlet) boundary faces. If not given
then an attempt is made to estimate it, but it is usually
underestimated.
domain_length (scalar, optional) – The length of the domain between the inlet and outlet boundary
faces. If not given then an attempt is made to estimate it, but it
is usually underestimated.
Notes
The area and length of the domain are found using the bounding box
around the inlet and outlet pores which do not necessarily lie on the
edge of the domain, resulting in underestimation of sizes.
This calculates the effective thermal conductivity.
Parameters:
inlets (array_like) – The pores where the inlet temperature boundary conditions were
applied. If not given an attempt is made to infer them from the
algorithm.
outlets (array_like) – The pores where the outlet temperature boundary conditions were
applied. If not given an attempt is made to infer them from the
algorithm.
domain_area (scalar, optional) – The area of the inlet (and outlet) boundary faces. If not given
then an attempt is made to estimate it, but it is usually
underestimated.
domain_length (scalar, optional) – The length of the domain between the inlet and outlet boundary
faces. If not given then an attempt is made to estimate it, but it
is usually underestimated.
Notes
The area and length of the domain are found using the bounding box
around the inlet and outlet pores which do not necessarily lie on the
edge of the domain, resulting in underestimation of sizes.
A subclass of GenericLinearTransport to simulate electron and ionic
conduction. The 2 main roles of this subclass are to set the default
property names and to implement a method for calculating the effective
conductivity of the network.
This calculates the effective electrical conductivity.
Parameters:
inlets (array_like) – The pores where the inlet voltage boundary conditions were
applied. If not given an attempt is made to infer them from the
algorithm.
outlets (array_like) – The pores where the outlet voltage boundary conditions were
applied. If not given an attempt is made to infer them from the
algorithm.
domain_area (scalar, optional) – The area of the inlet (and outlet) boundary faces. If not given
then an attempt is made to estimate it, but it is usually
underestimated.
domain_length (scalar, optional) – The length of the domain between the inlet and outlet boundary
faces. If not given then an attempt is made to estimate it, but it
is usually underestimated.
Notes
The area and length of the domain are found using the bounding box
around the inlet and outlet pores which do not necessarily lie on the
edge of the domain, resulting in underestimation of sizes.
This calculates the effective permeability in this linear transport
algorithm.
Parameters:
inlets (array_like) – The pores where the inlet pressure boundary conditions were
applied. If not given an attempt is made to infer them from the
algorithm.
outlets (array_like) – The pores where the outlet pressure boundary conditions were
applied. If not given an attempt is made to infer them from the
algorithm.
domain_area (scalar, optional) – The area of the inlet (and outlet) boundary faces. If not given
then an attempt is made to estimate it, but it is usually
underestimated.
domain_length (scalar, optional) – The length of the domain between the inlet and outlet boundary
faces. If not given then an attempt is made to estimate it, but it
is usually underestimated.
Notes
The area and length of the domain are found using the bounding box
around the inlet and outlet pores which do not necessarily lie on the
edge of the domain, resulting in underestimation of sizes.
This method uses clone_pores to clone the input pores, then shifts
them the specified amount and direction, then applies the given label.
Parameters:
pores (array_like) – List of pores to offset. If no pores are specified, then it
assumes that all surface pores are to be cloned.
offset (3 x 1 array) – The distance in vector form which the cloned boundary pores should
be offset. Either this, or move_to must be specified.
move_to (3 x 1 array) – The location to move the boundary pores to. A value of None
indicates that no translation should be applied in that axis. For
instance, [None,None,0] indicates that the boundary pores should
moved along the z-axis to the specified location. Either this or
offset must be specified.
apply_label (string) – This label is applied to the boundary pores. Default is
‘boundary’.
Examples
>>> importopenpnmasop>>> pn=op.network.Cubic(shape=[5,5,5])>>> print(pn.Np)# Confirm initial Network size125>>> Ps=pn.pores('top')# Select pores on top face>>> pn.add_boundary_pores(labels=['top'])>>> print(pn.Np)# Confirm addition of 25 new pores150
This method uses clone_pores to clone the input pores, then shifts
them the specified amount and direction, then applies the given label.
Parameters:
pores (array_like) – List of pores to offset. If no pores are specified, then it
assumes that all surface pores are to be cloned.
offset (3 x 1 array) – The distance in vector form which the cloned boundary pores should
be offset. Either this, or move_to must be specified.
move_to (3 x 1 array) – The location to move the boundary pores to. A value of None
indicates that no translation should be applied in that axis. For
instance, [None,None,0] indicates that the boundary pores should
moved along the z-axis to the specified location. Either this or
offset must be specified.
apply_label (string) – This label is applied to the boundary pores. Default is
‘boundary’.
Examples
>>> importopenpnmasop>>> pn=op.network.Cubic(shape=[5,5,5])>>> print(pn.Np)# Confirm initial Network size125>>> Ps=pn.pores('top')# Select pores on top face>>> pn.add_boundary_pores(labels=['top'])>>> print(pn.Np)# Confirm addition of 25 new pores150
Returns the possible connections between two group of pores, and optionally
makes the connections.
See Notes for advanced usage.
Parameters:
network (OpenPNM Network Object) –
pores1 (array_like) – The first group of pores on the network
pores2 (array_like) – The second group of pores on the network
labels (list of strings) – The labels to apply to the new throats. This argument is only needed
if add_conns is True.
add_conns (bool) – Indicates whether the connections should be added to the supplied
network (default is True). Otherwise, the connections are returned
as an Nt x 2 array that can be passed directly to extend.
Notes
(1) The method also works if pores1 and pores2 are list of lists,
in which case it consecutively connects corresponding members of the two
lists in a 1-to-1 fashion. Example: pores1 = [[0, 1], [2, 3]] and
pores2 = [[5], [7, 9]] leads to creation of the following connections:
0-->52-->73-->71-->52-->93-->9
(2) If you want to use the batch functionality, make sure that each element
within pores1 and pores2 are of type list or ndarray.
(3) It creates the connections in a format which is acceptable by
the default OpenPNM connections (‘throat.conns’) and either adds them to
the network or returns them.
Add individual pores and/or throats to the network from a list of coords
or conns.
Parameters:
network (OpenPNM Network Object) – The Network to which pores or throats should be added
pore_coords (array_like) – The coordinates of the pores to add
throat_conns (array_like) – The throat connections to add
labels (string, or list of strings, optional) – A list of labels to apply to the new pores and throats
Notes
This needs to be enhanced so that it increases the size of all pore
and throat props and labels on ALL associated Phase objects. At the
moment it throws an error is there are any associated Phases.
This is an in-place operation, meaning the received Network object will
be altered directly.
network (OpenPNM Network Object) – The Network object on which the search should be performed
pore_pairs (array_like) – An N x 2 array containing N pairs of pores for which the shortest
path is sought.
weights (array_like, optional) – An Nt-long list of throat weights for the search. Typically this
would be the throat lengths, but could also be used to represent
the phase configuration. If no weights are given then the
standard topological connections of the Network are used.
Returns:
A dictionary containing both the pores and throats that define the
shortest path connecting each pair of input pores.
Notes
The shortest path is found using Dijkstra’s algorithm included in the
scipy.sparse.csgraph module
TODO: The returned throat path contains the correct values, but not
necessarily in the true order
Find the pores on the surface of the domain by performing a Delaunay
triangulation between the network pores and some external markers. All
pores connected to these external marker points are considered surface
pores.
Parameters:
network (OpenPNM Network Object) – The network for which the surface pores are to be found
markers (array_like) –
3 x N array of the marker coordinates to use in the triangulation. The
labeling is performed in one step, so all points are added, and then
any pores connected to at least one marker is given the provided label.
By default, this function will automatically generate 6 points outside
each axis of the network domain.
Users may wish to specify a single external marker point and provide an
appropriate label in order to identify specific faces. For instance,
the marker may be above the domain, and the label might be
‘top_surface’.
label (string) – The label to apply to the pores. The default is ‘surface’.
Notes
This function does not check whether the given markers actually lie outside
the domain, allowing the labeling of internal sufaces.
If this method fails to mark some surface pores, consider sending more
markers on each face.
Given a symmetric adjacency matrix, finds all sites that are connected
to the input sites.
Parameters:
am (scipy.sparse matrix) – The adjacency matrix of the network. Must be symmetrical such that if
sites i and j are connected, the matrix contains non-zero values
at locations (i, j) and (j, i).
flatten (boolean) – If True (default) the returned result is a compressed array of all
neighbors, or a list of lists with each sub-list containing the
neighbors for each input site. Note that an unflattened list might
be slow to generate since it is a Python list rather than a Numpy
array.
include_input (boolean) – If False (default) the input sites will be removed from the result.
logic (string) –
Specifies logic to filter the resulting list. Options are:
’or’ : (default) All neighbors of the input sites. This is also
known as the ‘union’ in set theory or ‘any’ in boolean logic. Both
keywords are accepted and treated as ‘or’.
’xor’ : Only neighbors of one and only one input site. This is
useful for finding the sites that are not shared by any of the input
sites. ‘exclusive_or’ is also accepted.
’xnor’ : Neighbors that are shared by two or more input sites. This
is equivalent to finding all neighbors with ‘or’, minus those found
with ‘xor’, and is useful for finding neighbors that the inputs have
in common. ‘nxor’ is also accepted.
’and’ : Only neighbors shared by all input sites. This is also
known as ‘intersection’ in set theory and (somtimes) as ‘all’ in
boolean logic. Both keywords are accepted and treated as ‘and’.
Returns:
An array containing the neighboring sites filtered by the given logic. If
flatten is False then the result is a list of lists containing the
neighbors of each input site.
See also
find_complement()
Notes
The logic options are applied to neighboring sites only, thus it is not
possible to obtain sites that are part of the global set but not neighbors.
This is because (a) the list global sites might be very large, and (b) it
is not possible to return a list of neighbors for each input site if global
sites are considered.
Given an incidence matrix, finds all sites that are connected to the
input sites.
This function accepts either an incidence or adjacency matrix.
Parameters:
im (scipy.sparse matrix) – The incidence matrix of the network. Must be shaped as (N-sites,
N-bonds), with non-zeros indicating which sites are connected. Either
am or im must be given. Passing in im is slower, but more
powerful as it allows for an unflattened list of neighbors.
am (scipy.sparse matrix (optional)) – The adjacency matrix of the network. Either am or im must be
given. Passing in am is faster, but does not allow for an
unflattened list.
flatten (boolean (default is True)) – Indicates whether the returned result is a compressed array of all
neighbors, or a list of lists with each sub-list containing the
neighbors for each input site. Note that an unflattened list might
be slow to generate since it is a Python list rather than a Numpy
array.
logic (string) –
Specifies logic to filter the resulting list. Options are:
’or’ : (default) All neighbors of the input sites. This is also
known as the ‘union’ in set theory or ‘any’ in boolean logic. Both
keywords are accepted and treated as ‘or’.
’xor’ : Only neighbors of one and only one input site. This is
useful for finding the sites that are not shared by any of the input
sites. ‘exclusive_or’ is also accepted’.
’xnor’ : Neighbors that are shared by two or more input sites. This
is equivalent to finding all neighbors with ‘or’, minus those found
with ‘xor’, and is useful for finding neighbors that the inputs have
in common. ‘nxor’ is also accepted.
’and’ : Only neighbors shared by all input sites. This is also
known as ‘intersection’ in set theory and (somtimes) as ‘all’ in
boolean logic. Both keywords are accepted and treated as ‘and’.
Returns:
An array containing the neighboring bonds filtered by the given logic. If
flatten is False then the result is a list of lists containing the
neighbors of each given input site.
See also
find_complement()
Notes
The logic options are applied to neighboring bonds only, thus it is not
possible to obtain bonds that are part of the global set but not neighbors.
This is because (a) the list of global bonds might be very large, and
(b) it is not possible to return a list of neighbors for each input site
if global sites are considered.
Given an adjacency matrix, finds which sites are connected to the input
bonds.
Parameters:
am (scipy.sparse matrix) – The adjacency matrix of the network. Must be symmetrical such that if
sites i and j are connected, the matrix contains non-zero values
at locations (i, j) and (j, i).
flatten (boolean (default is True)) – Indicates whether the returned result is a compressed array of all
neighbors, or a list of lists with each sub-list containing the
neighbors for each input site. Note that an unflattened list might
be slow to generate since it is a Python list rather than a Numpy
array.
logic (string) –
Specifies logic to filter the resulting list. Options are:
’or’ : (default) All neighbors of the input bonds. This is also
known as the ‘union’ in set theory or (sometimes) ‘any’ in boolean
logic. Both keywords are accepted and treated as ‘or’.
’xor’ : Only neighbors of one and only one input bond. This is
useful for finding the sites that are not shared by any of the input
bonds. ‘exclusive_or’ is also accepted.
’xnor’ : Neighbors that are shared by two or more input bonds. This
is equivalent to finding all neighbors with ‘or’, minus those found
with ‘xor’, and is useful for finding neighbors that the inputs have
in common. ‘nxor’ is also accepted.
’and’ : Only neighbors shared by all input bonds. This is also
known as ‘intersection’ in set theory and (somtimes) as ‘all’ in
boolean logic. Both keywords are accepted and treated as ‘and’.
Returns:
An array containing the connected sites, filtered by the given logic. If
flatten is False then the result is a list of lists containing the
neighbors of each given input bond. In this latter case, sites that
have been removed by the given logic are indicated by nans, thus the
array is of type float and is not suitable for indexing.
Given pairs of sites, finds the bonds which connects each pair.
Parameters:
sites (array_like) – A 2-column vector containing pairs of site indices on each row.
am (scipy.sparse matrix) – The adjacency matrix of the network. Must be symmetrical such that if
sites i and j are connected, the matrix contains non-zero values
at locations (i, j) and (j, i).
Returns:
Returns a list the same length as P1 (and P2) with each element
containing the throat number that connects the corresponding pores,
or None` if pores are not connected.
Notes
The returned list can be converted to an ND-array, which will convert
the None values to nan. These can then be found using
scipy.isnan.
Find the distance between all pores on set 1 to each pore in set 2
Parameters:
network (OpenPNM Network Object) – The network object containing the pore coordinates
pores1 (array_like) – The pore indices of the first set
pores2 (array_Like) – The pore indices of the second set. It’s OK if these indices are
partially or completely duplicating pores.
Returns:
A distance matrix with len(pores1) rows and len(pores2) columns.
The distance between pore i in pores1 and j in pores2 is
located at *(i, j) and (j, i) in the distance matrix.*
Notes
This function computes and returns a distance matrix, which is a dense
matrix of size Np_1 by Np_2, so can get large. For distances between
larger sets a KD-tree approach would be better, which is available in
scipy.spatial.
Identify connected clusters of pores in the network. This method can
also return a list of throat cluster numbers, which correspond to the
cluster numbers of the pores to which the throat is connected. Either
site and bond percolation can be considered, see description of input
arguments for details.
Parameters:
network (OpenPNM Network Object) – The network
mask (array_like, boolean) – A list of active bonds or sites (throats or pores). If the mask is
Np long, then the method will perform a site percolation, and if
the mask is Nt long bond percolation will be performed.
Returns:
A tuple containing an Np long list of pore cluster labels, and an Nt-long
list of throat cluster labels. The label numbers correspond such that
pores and throats with the same label are part of the same cluster.
Generates a set of base points for passing into the Tessellation-based
Network classes. The points can be distributed in spherical, cylindrical,
or rectilinear patterns, as well as 2D and 3D (disks and squares).
Parameters:
num_points (scalar) – The number of base points that lie within the domain. Note that the
actual number of points returned will be larger, with the extra points
lying outside the domain.
domain_size (list or array) –
Controls the size and shape of the domain, as follows:
sphere : If a single value is received, its treated as the radius
[r] of a sphere centered on [0, 0, 0].
cylinder : If a two-element list is received it’s treated as the
radius and height of a cylinder [r, z] positioned at [0, 0, 0] and
extending in the positive z-direction. If the z dimension is 0, a
disk of radius r is created.
rectangle : If a three element list is received, it’s treated
as the outer corner of rectangle [x, y, z] whose opposite corner lies
at [0, 0, 0]. If the z dimension is 0, a rectangle of size X-by-Y is
created.
density_map (array, optional) –
A an array that contains fractional values (0 < i < 1) indicating the
liklihood that a point in that region should be kept. The size of this
array can be anything, but the shape must match the domain_size;
that is for a 3D network the shape of the density_map can be
[10, 10, 10] or [50, 50, 50], depending on how important the resolution
of the density distribution is. For a 2D network the density_map
should be [10, 10].
When specifying a custom probabiliy map is it recommended to also set
values outside the given domain to zero. If not, then the correct
shape will still be returned, but with too few points in it.
reflect (boolean) – If True, the the base points are generated as specified, the reflected
about each face of the domain. This essentially tricks the
tessellation functions into creating smooth flat faces at the
boundaries once these excess pores are trimmed.
Notes
The reflection approach tends to create larger pores near the surfaces, so
it might be necessary to use the density_map argument to specify a
slightly higher density of points near the surfaces.
The Voronoi, Delaunay, Gabriel, and DelunayVoronoiDual
classes can techncially handle base points with spherical or cylindrical
domains, but the reflection across round surfaces does not create perfect
Voronoi cells so the surfaces will not be smooth.
Examples
The following generates a spherical array with higher values near the core.
It uses a distance transform to create a sphere of radius 10, then a
second distance transform to create larger values in the center away from
the sphere surface. These distance values could be further skewed by
applying a power, with values higher than 1 resulting in higher values in
the core, and fractional values smoothinging them out a bit.
>>> importopenpnmasop>>> importscipyassp>>> importscipy.ndimageasspim>>> im=np.ones([21,21,21],dtype=int)>>> im[10,10,10]=0>>> im=spim.distance_transform_edt(im)<=20# Create sphere of 1's>>> prob=spim.distance_transform_edt(im)>>> prob=prob/np.amax(prob)# Normalize between 0 and 1>>> pts=op.topotools.generate_base_points(num_points=50,... domain_size=[1,1,1],... density_map=prob)>>> net=op.network.DelaunayVoronoiDual(points=pts,shape=[1,1,1])
Finds pores on the surface of the network and labels them according to
whether they are on the top, bottom, etc. This function assumes the
network is cubic in shape (i.e. with six flat sides)
Parameters:
network (OpenPNM Network object) – The network to apply the labels
tol (scalar) – The tolerance for defining what counts as a surface pore, which is
specifically meant for random networks. All pores with tol of
the maximum or minimum along each axis are counts as pores. The
default is 0.
label (string) – An identifying label to isolate the pores on the faces of the network.
The default is ‘surface’. Surface pores can be found using
find_surface_pores.
Combines a selection of pores into a new single pore located at the
centroid of the selected pores and connected to all of their neighbors.
Parameters:
network (OpenPNM Network Object) –
pores (array_like) – The list of pores which are to be combined into a new single pore
labels (string or list of strings) – The labels to apply to the new pore and new throat connections
Notes
(1) The method also works if a list of lists is passed, in which case
it consecutively merges the given selections of pores.
(2) The selection of pores should be chosen carefully, preferrable so that
they all form a continuous cluster. For instance, it is recommended
to use the find_nearby_pores method to find all pores within a
certain distance of a given pore, and these can then be merged without
causing any abnormal connections.
Produces a 3D plot of the network topology showing how throats connect
for quick visualization without having to export data to veiw in Paraview.
Parameters:
network (OpenPNM Network Object) – The network whose topological connections to plot
throats (array_like (optional)) – The list of throats to plot if only a sub-sample is desired. This is
useful for inspecting a small region of the network. If no throats are
specified then all throats are shown.
fig (Matplotlib figure handle and line property arguments) – If a fig is supplied, then the topology will be overlaid on this
plot. This makes it possible to combine coordinates and connections,
and to color different throats differently (see kwargs)
kwargs (other named arguments) –
By also in different line properties such as color it’s possible to
plot several different sets of connections with unique colors.
For information on available line style options, visit the Matplotlib
documentation on the web
Notes
The figure handle returned by this method can be passed into
plot_coordinates to create a plot that combines pore coordinates and
throat connections, and vice versa.
Produces a 3D plot showing specified pore coordinates as markers
Parameters:
network (OpenPNM Network Object) – The network whose topological connections to plot
pores (array_like (optional)) – The list of pores to plot if only a sub-sample is desired. This is
useful for inspecting a small region of the network. If no pores are
specified then all are shown.
fig (Matplotlib figure handle) – If a fig is supplied, then the coordinates will be overlaid. This
enables the plotting of multiple different sets of pores as well as
throat connections from plot_connections.
kwargs (dict) –
By also in different marker properties such as size (s) and color
(c).
For information on available marker style options, visit the Matplotlib
documentation on the web
Notes
The figure handle returned by this method can be passed into
plot_topology to create a plot that combines pore coordinates and
throat connections, and vice versa.
Helper function for relecting a set of points about the faces of a
given domain.
Parameters:
base_pts (array_like) –
The coordinates of the base_pts to be reflected in the coordinate
system corresponding to the the domain as follows:
spherical : [r, theta, phi]
cylindrical or circular : [r, theta, z]
rectangular or square : [x, y, z]
domain_size (list or array) –
Controls the size and shape of the domain, as follows:
sphere : If a single value is received, its treated as the radius
[r] of a sphere centered on [0, 0, 0].
cylinder : If a two-element list is received it’s treated as the
radius and height of a cylinder [r, z] positioned at [0, 0, 0] and
extending in the positive z-direction. If the z dimension is 0, a
disk of radius r is created.
rectangle : If a three element list is received, it’s treated
as the outer corner of rectangle [x, y, z] whose opposite corner lies
at [0, 0, 0]. If the z dimension is 0, a rectangle of size X-by-Y is
created.
Stitches a second a network to the current network.
Parameters:
networK (OpenPNM Network Object) – The Network to which to donor Network will be attached
donor (OpenPNM Network Object) – The Network to stitch on to the current Network
P_network (array_like) – The pores on the current Network
P_donor (array_like) – The pores on the donor Network
label_suffix (string or None) – Some text to append to each label in the donor Network before
inserting them into the recipient. The default is to append no
text, but a common option would be to append the donor Network’s
name. To insert none of the donor labels, use None.
len_max (float) – Set a length limit on length of new throats
method (string (default = 'delaunay')) –
The method to use when making pore to pore connections. Options are:
’delaunay’ : Use a Delaunay tessellation
’nearest’ :Connects each pore on the receptor network to its nearest
pore on the donor network
Notes
Before stitching it is necessary to translate the pore coordinates of
one of the Networks so that it is positioned correctly relative to the
other.
This module contains a selection of pore-scale models for calculating
geometrical, thermophysical, and multiphysics transport properties.
Using Prewritten Models from the Library
OpenPNM includes a solid library of pre-written models for most basic and
standard scenarios. These are broken up into 4 categores: geometry,
phases, physics, and misc. These are further categorized by the type
of information they calculate, such as grouping all models that calculate
viscosity or throat length.
Utilizing a model on a object requires using the add_model method. This
is demonstrated below:
Upon being added to the object the models are run. The resulting data is
placed into the object using the propname as a key. Inspecting the
object reveals that these two data items are indeed present.
The actual models and their respective parameters are also stored on the
object under the models attribute, which is dictionary with the same keys
as the propnames. This can also be inspected:
Note that the model ‘pore.coordination_number’ is added to all GenericNetworks
upon instantiation, but not run. This is why the model appears in the
models attribute but the data does not yet appear when the props method
is called.
Finally, any of the models parameters can be edited by reaching into the
models dictionary as follows:
>>> pn.models['pore.seed']['num_range']=[0.5,0.9]
The 'pore.seed' values must be regenerated for this new parameter to take
effect, and the 'pore.diameter' model must also be regenerated to utilized
the new seeds. This is accomplished using regenerate_models which
automatically ensures that models are called in the correct order.
This submodule contains models for calculating general or generic values
that are useful for all other pore-scale models, such as scaling values, or
generating an array of random numbers.
Places the given constant value into the target object
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
value (scalar) – The numerical value to apply
Returns:
value – Array containing constant values equal to value.
Return type:
NumPy ndarray
Notes
This model is mostly useless and for testing purposes, but might be used
to ‘reset’ an array back to a default value.
Calculates the product of multiple property values
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
prop1 (string) – The name of the first argument
prop2 (string) – The name of the second argument
Returns:
value – Array containing product values of target[prop1], target[prop2]
Return type:
NumPy ndarray
Notes
Additional properties can be specified beyond just prop1 and prop2
by including additional arguments in the function call (i.e. prop3='pore.foo').
Scales an existing value by a factor. Useful for constricting some throat
property.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
prop (string) – The dictionary key of the array containing the values to be scaled.
factor (scalar) – The factor by which the values should be scaled.
Returns:
value – Array containing target[prop] values scaled by factor.
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
xmin (float) – Values below this limit will be replaced with xmin.
xmax (float) – Values above this limit will be replaced with xmax.
Normalizes the given array between the supplied limits
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
This allows users to place a customized calculation into the automatated
model regeneration pipeline.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
prop (string) – The dictionary key containing the array to be operated on
func (Numpy function) – A handle to the function to apply
kwargs (keyward arguments) – All arguments required by the specific Numpy function
Returns:
result – Array containing func(target[prop], **kwargs).
Return type:
NumPy ndarray
Examples
The following example shows how to use a Numpy function, but any function
can be used, as long as it returns an array object:
openpnm.models.misc.simple_equations.polynomial(target, a, prop, **kwargs)[source]¶
Calculates a property as a polynomial function of a given property
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
a (array_like) – A list containing the polynomial coefficients, where element 0 in the
list corresponds to a0 and so on. Note that no entries can be skipped
so 0 coefficients must be sent as 0.
prop (string) – The dictionary key containing the independent variable or phase
property to be used in the polynomial.
Returns:
value – Array containing Pn(target[prop]), where Pn is nth order
polynomial with coefficients stored in a.
Return type:
NumPy ndarray
openpnm.models.misc.simple_equations.linear(target, m, b, prop)[source]¶
Calculates a property as a linear function of a given property
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
b (m,) – Slope and intercept of the linear corelation
prop (string) – The dictionary key containing the independent variable or phase
property to be used in the correlation.
Create an array of random numbers of a specified size.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seed (int) – The starting seed value to send to Scipy’s random number generator.
The default is None, which means different distribution is returned
each time the model is run.
num_range (list) – A two element list indicating the low and high end of the returned
numbers.
Returns:
values – Array containing uniformly-distributed random numbers.
Produces values from a Weibull distribution given a set of random numbers.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
shape (float) – This controls the skewness of the distribution, with ‘shape’ < 1 giving
values clustered on the low end of the range with a long tail, and
‘shape’ > 1 giving a more symmetrical distribution.
scale (float) – This controls the width of the distribution with most of values falling
below this number.
loc (float) – Applies an offset to the distribution such that the smallest values are
above this number.
Returns:
values – Array containing random numbers based on Weibull distribution.
Return type:
NumPy ndarray
Examples
The following code illustrates the inner workings of this function,
which uses the ‘weibull_min’ method of the scipy.stats module. This can
be used to find suitable values of ‘shape’, ‘scale’` and ‘loc’. Note that
‘shape’ is represented by ‘c’ in the actual function call.
Produces values from a Weibull distribution given a set of random numbers.
Parameters:
target (OpenPNM Object) – The object with which this function as associated. This argument
is required to (1) set number of values to generate (geom.Np or
geom.Nt) and (2) provide access to other necessary values
(i.e. geom[‘pore.seed’]).
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
scale (float) – The standard deviation of the Normal distribution
loc (float) – The mean of the Normal distribution
Returns:
values – Array containing normally distributed random numbers.
Return type:
NumPy ndarray
Examples
The following code illustrates the inner workings of this function,
which uses the ‘norm’ method of the scipy.stats module. This can
be used to find suitable values of ‘scale’ and ‘loc’.
Accepts an ‘rv_frozen’ object from the Scipy.stats submodule and returns
values from the distribution for the given seeds
This uses the ppf method of the stats object
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
func (object) – An ‘rv_frozen’ object from the Scipy.stats library with all of the
parameters pre-specified.
Returns:
values – Array containing random numbers based on given ppf.
Return type:
NumPy ndarray
Examples
The following code illustrates the process of obtaining a ‘frozen’ Scipy
stats object and adding it as a model:
Adopt a value from the values found in neighboring throats
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
prop (string) – The dictionary key of the array containing the throat property to be
used in the calculation. The default is ‘throat.seed’.
throat_prop (string) – Same as prop, but will be deprecated.
mode (string) – Controls how the pore property is calculated. Options are ‘min’,
‘max’ and ‘mean’.
ignore_nans (boolean (default is True)) – If True the result will ignore nans in the neighbors
Returns:
value – Array containing customized values based on those of adjacent throats.
Adopt a value based on the values in neighboring pores
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
prop (string) – The dictionary key to the array containing the pore property to be
used in the calculation. Default is ‘pore.seed’.
pore_prop (string) – Same as prop but will be deprecated.
mode (string) – Controls how the throat property is calculated. Options are ‘min’,
‘max’ and ‘mean’.
ignore_nans (boolean (default is True)) – If True the result will ignore nans in the neighbors
Returns:
value – Array containing customized values based on those of adjacent pores.
The pore-size models in this sub-module are used to apply desired pore-size
distributions to a pore network. Most of the models accept pore seeds, and
then look-up pore-sizes from cumuative distribtions functions. There is also
a model for finding the largest possible sphere that can be placed on each
site.
Produces values from a Weibull distribution given a set of random numbers.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
shape (float) – This controls the skewness of the distribution, with ‘shape’ < 1 giving
values clustered on the low end of the range with a long tail, and
‘shape’ > 1 giving a more symmetrical distribution.
scale (float) – This controls the width of the distribution with most of values falling
below this number.
loc (float) – Applies an offset to the distribution such that the smallest values are
above this number.
Returns:
values – Array containing random numbers based on Weibull distribution.
Return type:
NumPy ndarray
Examples
The following code illustrates the inner workings of this function,
which uses the ‘weibull_min’ method of the scipy.stats module. This can
be used to find suitable values of ‘shape’, ‘scale’` and ‘loc’. Note that
‘shape’ is represented by ‘c’ in the actual function call.
Produces values from a Weibull distribution given a set of random numbers.
Parameters:
target (OpenPNM Object) – The object with which this function as associated. This argument
is required to (1) set number of values to generate (geom.Np or
geom.Nt) and (2) provide access to other necessary values
(i.e. geom[‘pore.seed’]).
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
scale (float) – The standard deviation of the Normal distribution
loc (float) – The mean of the Normal distribution
Returns:
values – Array containing normally distributed random numbers.
Return type:
NumPy ndarray
Examples
The following code illustrates the inner workings of this function,
which uses the ‘norm’ method of the scipy.stats module. This can
be used to find suitable values of ‘scale’ and ‘loc’.
Create an array of random numbers of a specified size.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seed (int) – The starting seed value to send to Scipy’s random number generator.
The default is None, which means different distribution is returned
each time the model is run.
num_range (list) – A two element list indicating the low and high end of the returned
numbers.
Returns:
values – Array containing uniformly-distributed random numbers.
Accepts an ‘rv_frozen’ object from the Scipy.stats submodule and returns
values from the distribution for the given seeds
This uses the ppf method of the stats object
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
func (object) – An ‘rv_frozen’ object from the Scipy.stats library with all of the
parameters pre-specified.
Returns:
values – Array containing random numbers based on given ppf.
Return type:
NumPy ndarray
Examples
The following code illustrates the process of obtaining a ‘frozen’ Scipy
stats object and adding it as a model:
Finds the maximum diameter pore that can be placed in each location without
overlapping any neighbors.
This method iteratively expands pores by increasing their diameter to
encompass half of the distance to the nearest neighbor. If the neighbor
is not growing because it’s already touching a different neighbor, then
the given pore will never quite touch this neighbor. Increating the value
of iters will get it closer, but it’s case of
[Zeno’s paradox](https://en.wikipedia.org/wiki/Zeno%27s_paradoxes) with
each step cutting the remaining distance in half
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
fixed_diameter (string) – The dictionary key containing the pore diameter values already
assigned to network, if any. If not provided a starting value is
assumed as half-way to the nearest neighbor.
iters (integer) – The number of iterations to perform when searching for maximum
diameter. This function iteratively grows pores until they touch
their nearest neighbor, which is also growing, so this parameter limits
the maximum number of iterations. The default is 10, but 5 is usally
enough.
Returns:
D – Array containing pore diameter values.
Return type:
NumPy ndarray
Notes
This model looks into all pores in the network when finding the diameter.
This means that when multiple Geometry objects are defined, it will
consider the diameter of pores on adjacent Geometries. If no diameters
have been assigned to these neighboring pores it will assume 0. If
diameter value are assigned to the neighboring pores AFTER this model is
run, the pores will overlap. This can be remedied by running this model
again.
Calculates the diameter of a sphere or edge-length of a cube with same
volume as the pore.
Parameters:
target (OpenPNM Geometry Object) – The Geometry object which this model is associated with. This controls
the length of the calculated array, and also provides access to other
necessary geometric properties.
pore_volume (string) – The dictionary key containing the pore volume values
pore_shape (string) – The shape of the pore body to assume when back-calculating from
volume. Options are ‘sphere’ (default) or ‘cube’.
Calculate pore volume from diameter assuming a spherical pore body
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls
the length of the calculated array, and also provides access to other
necessary geometric properties.
pore_diameter (string) – The dictionary key of the pore diameter values
Calculate pore volume from diameter assuming a cubic pore body
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls
the length of the calculated array, and also provides access to other
necessary geometric properties.
pore_diameter (string) – The dictionary key of the pore diameter values
Pore seed models are use to calculate random numbers for each pore, which can
subsequently be used in statistical distributions to calculate actual pore
sizes.
Create an array of random numbers of a specified size.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seed (int) – The starting seed value to send to Scipy’s random number generator.
The default is None, which means different distribution is returned
each time the model is run.
num_range (list) – A two element list indicating the low and high end of the returned
numbers.
Returns:
values – Array containing uniformly-distributed random numbers.
Generates pore seeds that are spatailly correlated with their neighbors.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
weights (list of ints, optional) – The [Nx,Ny,Nz] distances (in number of pores) in each direction that
should be correlated.
strel (array_like, optional (in place of weights)) –
The option allows full control over the spatial correlation pattern by
specifying the structuring element to be used in the convolution.
The array should be a 3D array containing the strength of correlations
in each direction. Nonzero values indicate the strength, direction
and extent of correlations. The following would achieve a basic
correlation in the z-direction:
This approach uses image convolution to replace each pore seed in the
geoemtry with a weighted average of those around it. It then converts the
new seeds back to a random distribution by assuming they new seeds are
normally distributed.
Because is uses image analysis tools, it only works on Cubic networks.
This is the appproached used by Gostick et al [2] to create an anistropic
gas diffusion layer for fuel cell electrodes.
J. Gostick et al, Pore network modeling of fibrous gas diffusion
layers for polymer electrolyte membrane fuel cells. J Power Sources
v173, pp277–290 (2007)
Calculates internal surface area of pore bodies assuming they are spherical
then subtracts the area of the neighboring throats in a crude way, by
simply considering the throat cross-sectional area, thus not accounting
for the actual curvature of the intersection.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
pore_diameter (string) – The dictionary key to the pore diameter array.
throat_area (string) – The dictioanry key to the throat area array. Throat areas are needed
since their insection with the pore are removed from the computation.
Returns:
value – Array containing pore surface area values.
Calculates internal surface area of pore bodies assuming they are cubes
then subtracts the area of the neighboring throats.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
pore_diameter (string) – The dictionary key to the pore diameter array.
throat_area (string) – The dictioanry key to the throat area array. Throat areas are needed
since their insection with the pore are removed from the computation.
Returns:
value – Array containing pore surface area values.
Calculate throat cross-sectional area for a cylindrical throat
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_diameter (string) – Dictionary key of the throat diameter values
Returns:
value – Array containing throat cross-sectional area values.
Calculate throat cross-sectional area for a cuboid throat
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_diameter (string) – Dictionary key of the throat diameter values
Returns:
value – Array containing throat cross-sectional area values.
Produces values from a Weibull distribution given a set of random numbers.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
shape (float) – This controls the skewness of the distribution, with ‘shape’ < 1 giving
values clustered on the low end of the range with a long tail, and
‘shape’ > 1 giving a more symmetrical distribution.
scale (float) – This controls the width of the distribution with most of values falling
below this number.
loc (float) – Applies an offset to the distribution such that the smallest values are
above this number.
Returns:
values – Array containing random numbers based on Weibull distribution.
Return type:
NumPy ndarray
Examples
The following code illustrates the inner workings of this function,
which uses the ‘weibull_min’ method of the scipy.stats module. This can
be used to find suitable values of ‘shape’, ‘scale’` and ‘loc’. Note that
‘shape’ is represented by ‘c’ in the actual function call.
Produces values from a Weibull distribution given a set of random numbers.
Parameters:
target (OpenPNM Object) – The object with which this function as associated. This argument
is required to (1) set number of values to generate (geom.Np or
geom.Nt) and (2) provide access to other necessary values
(i.e. geom[‘pore.seed’]).
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
scale (float) – The standard deviation of the Normal distribution
loc (float) – The mean of the Normal distribution
Returns:
values – Array containing normally distributed random numbers.
Return type:
NumPy ndarray
Examples
The following code illustrates the inner workings of this function,
which uses the ‘norm’ method of the scipy.stats module. This can
be used to find suitable values of ‘scale’ and ‘loc’.
Create an array of random numbers of a specified size.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seed (int) – The starting seed value to send to Scipy’s random number generator.
The default is None, which means different distribution is returned
each time the model is run.
num_range (list) – A two element list indicating the low and high end of the returned
numbers.
Returns:
values – Array containing uniformly-distributed random numbers.
Accepts an ‘rv_frozen’ object from the Scipy.stats submodule and returns
values from the distribution for the given seeds
This uses the ppf method of the stats object
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
seeds (string, optional) – The dictionary key on the Geometry object containing random seed values
(between 0 and 1) to use in the statistical distribution.
func (object) – An ‘rv_frozen’ object from the Scipy.stats library with all of the
parameters pre-specified.
Returns:
values – Array containing random numbers based on given ppf.
Return type:
NumPy ndarray
Examples
The following code illustrates the process of obtaining a ‘frozen’ Scipy
stats object and adding it as a model:
Adopt a value based on the values in neighboring pores
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
prop (string) – The dictionary key to the array containing the pore property to be
used in the calculation. Default is ‘pore.seed’.
pore_prop (string) – Same as prop but will be deprecated.
mode (string) – Controls how the throat property is calculated. Options are ‘min’,
‘max’ and ‘mean’.
ignore_nans (boolean (default is True)) – If True the result will ignore nans in the neighbors
Returns:
value – Array containing customized values based on those of adjacent pores.
Calculates the diameter of a cirlce or edge-length of a sqaure with same
area as the throat.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
thorat_area (string) – The dictionary key to the throat area values
throat_shape (string) – The shape cross-sectional shape of the throat to assume when
back-calculating from the area. Options are ‘circle’ (default) or
‘square’.
Returns:
value – Array containing throat equivalent diameter.
Calculate throat length from end points and optionally a centroid
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_endpoints (string) – Dictionary key of the throat endpoint values.
throat_centroid (string) – Dictionary key of the throat centroid values, optional.
Returns:
Lt – Array containing throat lengths for the given geometry.
Return type:
ndarray
Notes
(1) By default, the model assumes that the centroids of pores and the
connecting throat in each conduit are colinear.
(2) If throat_centroid is passed, the model accounts for the extra
length. This could be useful for Voronoi or extracted networks.
Calculate throat length assuming point-like pores, i.e. center-to-center
distance between pores. Also, this model assumes that pores and throat
centroids are colinear.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
Calculate conduit lengths. A conduit is defined as half pore + throat
+ half pore.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_endpoints (string) – Dictionary key of the throat endpoint values.
throat_diameter (string) – Dictionary key of the throat length values.
throat_length (string (optional)) – Dictionary key of the throat length values. If not given then the
direct distance bewteen the two throat end points is used.
Returns:
Dictionary containing conduit lengths, which can be accessed via the dict
Calcuate the throat perimeter assuming a circular cross-section
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_diameter (string) – The dictionary key of the array containing the throat diameter values
Calcuate the throat perimeter assuming a square cross-section
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_diameter (string) – The dictionary key of the array containing the throat diameter values.
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_diameter (string) – Dictionary key to the throat diameter array. Default is
‘throat.diameter’.
throat_length (string) – Dictionary key to the throat length array. Default is ‘throat.length’.
Returns:
value – Array containing throat surface area values.
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_diameter (string) – Dictionary key to the throat diameter array. Default is
‘throat.diameter’.
throat_length (string) – Dictionary key to the throat length array. Default is ‘throat.length’.
Returns:
value – Array containing throat surface area values.
Calculate surface area for an arbitrary shaped throat give the perimeter
and length.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_perimeter (string) – Dictionary key to the throat perimeter array. Default is
‘throat.perimeter’.
throat_length (string) – Dictionary key to the throat length array. Default is ‘throat.length’.
Returns:
value – Array containing throat surface area values.
Calculates throat vector as straight path between connected pores.
Parameters:
target (OpenPNM object) – The object containing the geometrical properties of the throats
Returns:
unit_vec – Array containing pore-to-pore unit vectors
Return type:
NumPy ndarray, shape = (N, 3)
Notes
There is an important impicit assumption here: the positive direction is
taken as the direction from the pore with the lower index to the higher.
This corresponds to the pores in the 1st and 2nd columns of the
‘throat.conns’ array as stored on the etwork.
Calculate throat volume assuing a cylindrical shape
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
and throat_diameter (throat_length) – The dictionary keys containing the arrays with the throat diameter and
length values.
Returns:
value – Array containing throat volume values.
Return type:
NumPy ndarray
Notes
At present this models does NOT account for the volume reprsented by the
intersection of the throat with a spherical pore body.
Calculate throat volume assuing a square cross-section
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
and throat_diameter (throat_length) – The dictionary keys containing the arrays with the throat diameter and
length values.
Returns:
value – Array containing throat volume values.
Return type:
NumPy ndarray
Notes
At present this models does NOT account for the volume reprsented by the
intersection of the throat with a spherical pore body.
Calculate throat volume from the throat area and the throat length. This
method is useful for abnormal shaped throats.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
and throat_area (throat_length) – The dictionary keys containing the arrays with the throat area and
length values.
Returns:
value – Array containing throat volume values.
Return type:
NumPy ndarray
Notes
At present this models does NOT account for the volume reprsented by the
intersection of the throat with a spherical pore body.
Mortensen et al. have shown that the Hagen-Poiseuille hydraluic resistance
is linearly dependent on the compactness. Defined as perimeter^2/area.
The dependence is not universal as shapes with sharp corners provide more
resistance than those that are more elliptical. Count the number of
vertices and apply the right correction.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_perimeter (string) – The dictionary key of the array containing the throat perimeter values.
throat_area (string) – The dictionary key of the array containing the throat area values.
Mortensen N.A, Okkels F., and Bruus H. Reexamination of Hagen-Poiseuille
flow: Shape dependence of the hydraulic resistance in microchannels.
Physical Review E, v.71, pp.057301 (2005).
Mason and Morrow relate the capillary pressure to the shaped factor in a
similar way to Mortensen but for triangles.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_perimeter (string) – The dictionary key of the array containing the throat perimeter values.
throat_area (string) – The dictionary key of the array containing the throat area values.
Returns:
value – Array containing throat shape factor values.
Return type:
NumPy ndarray
References
Mason, G. and Morrow, N.R.. Capillary behavior of a perfectly wetting
liquid in irregular triangular tubes. Journal of Colloid and Interface
Science, 141(1), pp.262-274 (1991).
Jenkins and Rao relate the capillary pressure in an eliptical throat to
the aspect ratio
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
throat_perimeter (string) – The dictionary key of the array containing the throat perimeter values.
throat_area (string) – The dictionary key of the array containing the throat area values.
throat_diameter (string) – The dictionary key of the array containing the throat diameter values.
Returns:
value – Array containing throat capillary pressure values.
Return type:
NumPy ndarray
References
Jenkins, R.G. and Rao, M.B., The effect of elliptical pores on
mercury porosimetry results. Powder technology, 38(2), pp.177-180. (1984)
Calculates density of pure water or seawater at atmospheric pressure
using Eq. (8) given by Sharqawy et. al [1]. Values at temperature higher
than the normal boiling temperature are calculated at the saturation
pressure.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
temperature (string) – The dictionary key containing the temperature values. Temperature must
be in Kelvin for this emperical equation to work
salinity (string) – The dictionary key containing the salinity values. Salinity must be
expressed in g of salt per kg of solution (ppt).
Returns:
value – The density of water/seawater in [kg/m3]
Return type:
NumPy ndarray
Notes
T must be in K, and S in g of salt per kg of phase, or ppt (parts per
thousand)
VALIDITY: 273 < T < 453 K; 0 < S < 160 g/kg;
ACCURACY: 0.1 %
References
[1] Sharqawy M. H., Lienhard J. H., and Zubair, S. M., Desalination and
Water Treatment, 2010.
Calculates the mass density from the molecular weight and molar density
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
mol_weight (string) – The dictionary key containing the molecular weight values (kg/mol)
molar_density (string) – The dictionary key containing the molar density values (mol/m3)
Uses ideal gas law to calculate the mass density of an ideal gas
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
pressure (string) – The dictionary key containing the pressure values (Pa)
temperature (string) – The dictionary key containing the temperature values (K)
mol_weight (string) – The dictionary key containing the molecular weight values (kg/mol)
Returns:
value – Array containing density values in [kg/m3]
openpnm.models.phases.diffusivity.fuller(target, MA, MB, vA, vB, temperature='pore.temperature', pressure='pore.pressure')[source]¶
Uses Fuller model to estimate diffusion coefficient for gases from first
principles at conditions of interest
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
MA (float, array_like) – Molecular weight of component A [kg/mol]
MB (float, array_like) – Molecular weight of component B [kg/mol]
vA (float, array_like) – Sum of atomic diffusion volumes for component A
vB (float, array_like) – Sum of atomic diffusion volumes for component B
pressure (string) – The dictionary key containing the pressure values in Pascals (Pa)
temperature (string) – The dictionary key containing the temperature values in Kelvin (K)
Returns:
value – Array containing gas diffusion coefficient values [m2/s].
Return type:
NumPy ndarray
openpnm.models.phases.diffusivity.fuller_scaling(target, DABo, To, Po, temperature='pore.temperature', pressure='pore.pressure')[source]¶
Uses Fuller model to adjust a diffusion coefficient for gases from
reference conditions to conditions of interest
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
DABo (float, array_like) – Diffusion coefficient at reference conditions
To (Po,) – Pressure & temperature at reference conditions, respectively
pressure (string) – The dictionary key containing the pressure values in Pascals (Pa)
temperature (string) – The dictionary key containing the temperature values in Kelvin (K)
Returns:
value – Array containing scaled gas diffusion coefficient values [m2/s].
Return type:
NumPy ndarray
openpnm.models.phases.diffusivity.tyn_calus(target, VA, VB, sigma_A, sigma_B, temperature='pore.temperature', viscosity='pore.viscosity')[source]¶
Uses Tyn_Calus model to estimate diffusion coefficient in a dilute liquid
solution of A in B from first principles at conditions of interest
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
VA (float, array_like) – Molar volume of component A at boiling temperature (m3/mol)
VB (float, array_like) – Molar volume of component B at boiling temperature (m3/mol)
sigmaA (float, array_like) – Surface tension of component A at boiling temperature (N/m)
sigmaB (float, array_like) – Surface tension of component B at boiling temperature (N/m)
pressure (string) – The dictionary key containing the pressure values in Pascals (Pa)
temperature (string) – The dictionary key containing the temperature values in Kelvin (K)
Returns:
value – Array containing liquid diffusion coefficient values [m2/s].
Return type:
NumPy ndarray
openpnm.models.phases.diffusivity.tyn_calus_scaling(target, DABo, To, mu_o, viscosity='pore.viscosity', temperature='pore.temperature')[source]¶
Uses Tyn_Calus model to adjust a diffusion coeffciient for liquids from
reference conditions to conditions of interest
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
DABo (float, array_like) – Diffusion coefficient at reference conditions
To (mu_o,) – Viscosity & temperature at reference conditions, respectively
pressure (string) – The dictionary key containing the pressure values in Pascals (Pa)
temperature (string) – The dictionary key containing the temperature values in Kelvin (K)
Returns:
value – Array containing scaled liquid diffusion coefficient values [m2/s].
Calculates the effective property of a continua using percolation theory
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
volume_fraction (string) – The dictionary key in the Phase object containing the volume fraction
of the conducting component
bulk_property (string) – The dictionary key in the Phase object containing the intrinsic
property of the conducting component
phi_crit (float) – The volume fraction below which percolation does NOT occur
tau (float) – The exponent of the percolation relationship
Calculates the molar density from the molecular weight and mass density
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
mol_weight (string) – The dictionary key containing the molecular weight in kg/mol
density (string) – The dictionary key containing the density in kg/m3
Returns:
value – Array containing molar density values [mol/m3]
Uses ideal gas law to calculate the molar density of an ideal gas
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
temperature (string) – The dictionary key containing the density in kg/m3
pressure (string) – The dictionary key containing the pressure values in Pascals (Pa)
Returns:
value – Array containing molar density values [mol/m3]
Return type:
NumPy ndarray
Notes
This method uses the SI value for the ideal gas constant, hence the need to
provide the temperature and pressure in SI. In general, OpenPNM use SI
throughout for consistency.
Uses Van der Waals equation of state to calculate the density of a real gas
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
pressure (string) – The dictionary key containing the pressure values in Pascals (Pa)
temperature (string) – The dictionary key containing the temperature values in Kelvin (K)
critical_pressure (string) – The dictionary key containing the critical pressure values in Pascals
(Pa)
critical_temperature (string) – The dictionary key containing the critical temperature values in Kelvin
(K)
Returns:
value – Array containing molar density values [mol/m3]
Calculates surface tension of pure water or seawater at atmospheric
pressure using Eq. (28) given by Sharqawy et al. Values at
temperature higher than the normal boiling temperature are calculated at
the saturation pressure.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
temperature (string) – The dictionary key containing the temperature values. Temperature must
be in Kelvin for this emperical equation to work
salinity (string) – The dictionary key containing the salinity values. Salinity must be
expressed in g of salt per kg of solution (ppt).
Returns:
value – Array containing surface tension of seawater in [N/m]
Return type:
NumPy ndarray
Notes
T must be in K, and S in g of salt per kg of phase, or ppt (parts per
thousand)
VALIDITY: 273 < T < 313 K; 0 < S < 40 g/kg;
ACCURACY: 0.2 %
References
Sharqawy M. H., Lienhard J. H., and Zubair, S. M., Desalination and
Water Treatment, 2010.
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
k (float) – Constant parameter specific to fluid
temperature (string) – The dictionary key containing the temperature values (K)
critical_temperature (string) – The dictionary key containing the critical temperature values (K)
molar_density (string) – The dictionary key containing the molar density values (K)
Returns:
value (NumPy ndarray) – Array containing surface tension values [N/m]
TODO (Needs description, and improve definition of k)
openpnm.models.phases.surface_tension.guggenheim_katayama(target, K2, n, temperature='pore.temperature', critical_temperature='pore.critical_temperature', critical_pressure='pore.critical_pressure')[source]¶
Missing description
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
K2 (scalar) – Fluid specific constant
n (scalar) – Fluid specific constant
temperature (string) – The dictionary key containing the temperature values (K)
critical_temperature (string) – The dictionary key containing the critical temperature values (K)
critical_pressure (string) – The dictionary key containing the critical pressure values (K)
Returns:
value – Array containing surface tension values [N/m]
Return type:
NumPy ndarray
openpnm.models.phases.surface_tension.brock_bird_scaling(target, sigma_o, To, temperature='pore.temperature', critical_temperature='pore.critical_temperature')[source]¶
Uses Brock_Bird model to adjust surface tension from it’s value at a given
reference temperature to temperature of interest
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
To (float) – Reference temperature (K)
sigma_o (float) – Surface tension at reference temperature (N/m)
temperature (string) – The dictionary key containing the temperature values (K)
critical_temperature (string) – The dictionary key containing the critical temperature values (K)
Returns:
value – Array containing surface tension values scaled to the temperature [N/m]
Calculates thermal conductivity of pure water or seawater at atmospheric
pressure using the correlation given by Jamieson and Tudhope. Values at
temperature higher the normal boiling temperature are calculated at the
saturation pressure.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
temperature (string) – The dictionary key containing the temperature values. Temperature must
be in Kelvin for this emperical equation to work
salinity (string) – The dictionary key containing the salinity values. Salinity must be
expressed in g of salt per kg of solution (ppt).
Returns:
value – Array containing thermal conductivity of water/seawater in [W/m.K]
Return type:
NumPy ndarray
Notes
T must be in K, and S in g of salt per kg of phase, or ppt (parts per
thousand)
VALIDITY: 273 < T < 453 K; 0 < S < 160 g/kg;
ACCURACY: 3 %
References
Jamieson, and J. S. Tudhope, Desalination, 8, 393-401, 1970.
Uses Chung et al. model to estimate thermal conductivity for gases with
low pressure(<10 bar) from first principles at conditions of interest
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
acentric_factor (string) – Dictionary key containing the acentric factor of the component
Cv (string) – Dictionary key containing the heat capacity at constant volume
(J/(mol.K))
mol_weight (string) – Dictionary key containing the molecular weight of the component
(kg/mol)
viscosity (string) – The dictionary key containing the viscosity values (Pa.s)
temperature (string) – The dictionary key containing the temperature values (K)
critical_temperatre (string) – The dictionary key containing the critical temperature values (K)
Returns:
value – Array containing thermal conductivity values in [W/m.K]
Uses Sato et al. model to estimate thermal conductivity for pure liquids
from first principles at conditions of interest
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
boiling_temperature (string) – Dictionary key containing the toiling temperature of the component (K)
mol_weight (string) – Dictionary key containing the molecular weight of the component
(kg/mol)
temperature (string) – The dictionary key containing the temperature values (K)
critical_temperature (string) – The dictionary key containing the critical temperature values (K)
Returns:
value – Array containing thermal conductivity values in [W/m.K]
Calculates vapor pressure of pure water or seawater given by [1] based on
Raoult’s law. The pure water vapor pressure is given by [2]
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
temperature (string) – The dictionary key containing the phase temperature values
salinity (string) – The dictionary key containing the phase salinity values
Returns:
value – Array containing vapor pressure of water/seawater in [Pa]
Return type:
NumPy ndarray
Notes
T must be in K, and S in g of salt per kg of phase, or ppt (parts per
thousand)
VALIDITY: 273 < T < 473 K; 0 < S < 240 g/kg;
ACCURACY: 0.5 %
References
[1] Sharqawy M. H., Lienhard J. H., and Zubair, S. M., Desalination and
Water Treatment, 2010.
[2] ASHRAE handbook: Fundamentals, ASHRAE; 2005.
openpnm.models.phases.vapor_pressure.antoine(target, A, B, C, temperature='pore.temperature')[source]¶
Uses Antoine equation [1] to estimate vapor pressure of a pure component
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
B, C (A,) – Antoine vapor pressure coefficients for pure compounds. Since virtually
all Antoine coefficients are reported for units of mmHg and C for
historical reaons, this method assumes these A, B and C values are for
mmHg and C, but converts all properties internally to return Pascals.
temperature (string) – The dictionary key containing the phase temperature values in Kelvin
[K]. Can be either pore or throat values.
Returns:
value (NumPy ndarray) – Array containing vapor pressure values [Pa]
[1] Antoine, C. (1888), Vapor Pressure (a new relationship between pressure) – and temperature, Comptes Rendus des Séances de l’Académie des Sciences
(in French) 107: 681–684, 778–780, 836–837
Calculates viscosity of pure water or seawater at atmospheric pressure
using Eq. (22) given by Sharqawy et. al [1]. Values at temperature higher
than the normal boiling temperature are calculated at the saturation
pressure.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
temperature (string) – The dictionary key containing the temperature values. Temperature must
be in Kelvin for this emperical equation to work. Can be either a pore
or throat array.
salinity (string) – The dictionary key containing the salinity values. Salinity must be
expressed in g of salt per kg of solution (ppt). Can be either a
pore or throat array, but must be consistent with temperature.
Returns:
value – Array containing viscosity of water/seawater in [kg/m.s]
Return type:
NumPy ndarray
Notes
T must be in K, and S in g of salt per kg of phase, or ppt (parts per
thousand)
VALIDITY: 273 < T < 453 K; 0 < S < 150 g/kg;
ACCURACY: 1.5 %
References
[1] Sharqawy M. H., Lienhard J. H., and Zubair, S. M., Desalination and
Uses exponential model by Reynolds [1] for the temperature dependance of
shear viscosity
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
b (u0,) – Coefficients of the viscosity exponential model (mu = u0*Exp(-b*T)
where T is the temperature in Kelvin
temperature (string) – The dictionary key containing the temperature values (K). Can be
either a pore or throat array.
Returns:
value (NumPy ndarray) – Array containing viscosity values based on Reynolds model.
[1] Reynolds O. (1886). Phil Trans Royal Soc London, v. 177, p.157.
Uses Chung et al. [1] model to estimate viscosity for gases at low
pressure (much less than the critical pressure) at conditions of interest.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
temperatre (string) – The dictionary key containing the temperature values (K)
critical_temperature (string) – The dictionary key containing the temperature values (K)
mol_weight (string) – The dictionary key containing the molecular weight values (kg/mol)
critical_volume (string) – The dictionary key containing the critical volume values (m3/kmol)
Returns:
value – Array containing viscosity values based on Chung model [kg/m.s].
Return type:
NumPy ndarray
References
[1] Chung, T.H., Lee, L.L., and Starling, K.E., Applications of Kinetic Gas
Theories and Multiparameter Correlation for Prediction of Dilute Gas
Viscosity and Thermal Conductivity”, Ind. Eng. Chem. Fundam.23:8, 1984.
This submodule contains models for calculating properties related to physical
transport processes, including conductances, reaction rates, and capillary
effects
Computes the capillary entry pressure assuming the throat in a cylindrical
tube.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
surface_tension (string) – The dictionary key containing the surface tension values to be used. If
a pore property is given, it is interpolated to a throat list.
contact_angle (string) – The dictionary key containing the contact angle values to be used. If
a pore property is given, it is interpolated to a throat list.
diameter (string) – The dictionary key containing the throat diameter values to be used.
Returns:
value – Array containing pore/throat capillary entry pressure values.
Return type:
NumPy ndarray
Notes
The Washburn equation is:
\[P_c = -\frac{2\sigma(cos(\theta))}{r}\]
This is the most basic approach to calculating entry pressure and is
suitable for highly non-wetting invading phases in most materials.
Computes the throat capillary entry pressure assuming the throat is a
toroid.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
r_toroid (float or array_like) – The radius of the toroid surrounding the pore
surface_tension (dict key (string)) – The dictionary key containing the surface tension values to be used.
If a pore property is given, it is interpolated to a throat list.
contact_angle (dict key (string)) – The dictionary key containing the contact angle values to be used.
If a pore property is given, it is interpolated to a throat list.
diameter (dict key (string)) – The dictionary key containing the throat diameter values to be used.
Returns:
value – Array containing pore/throat capillary entry pressure values.
Return type:
NumPy ndarray
Notes
This approach accounts for the converging-diverging nature of many throat
types. Advancing the meniscus beyond the apex of the toroid requires an
increase in capillary pressure beyond that for a cylindical tube of the
same radius. The details of this equation are described by Mason and
Morrow [1], and explored by Gostick [2] in the context of a pore network
model.
G. Mason, N. R. Morrow, Effect of contact angle on capillary
displacement curvatures in pore throats formed by spheres. J.
Colloid Interface Sci. 168, 130 (1994).
J. Gostick, Random pore network modeling of fibrous PEMFC gas
diffusion media using Voronoi and Delaunay tessellations. J.
Electrochem. Soc. 160, F731 (2013).
Computes the throat capillary entry pressure assuming the throat is a
toroid. Makes use of the toroidal meniscus model with mode touch.
This model accounts for mensicus protrusion into adjacent pores and
touching solid features.
It is bidirectional becauase the connected pores generally have different
sizes and this determines how far the meniscus can protrude.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
r_toroid (float or array_like) – The radius of the toroid surrounding the pore
num_points (float (Default 100)) – The number of divisions to make along the profile length to assess the
meniscus properties in order to find the touch length.
surface_tension (dict key (string)) – The dictionary key containing the surface tension values to be used.
If a pore property is given, it is interpolated to a throat list.
contact_angle (dict key (string)) – The dictionary key containing the contact angle values to be used.
If a pore property is given, it is interpolated to a throat list.
throat_diameter (dict key (string)) – The dictionary key containing the throat diameter values to be used.
pore_diameter (dict key (string)) – The dictionary key containing the pore diameter values to be used.
Returns:
value – Array containing throat capillary entry pressure values.
Computes the capillary snap-off pressure assuming the throat is cylindrical
with converging-diverging change in diamater - like the Purcell model.
The wavelength of the change in diamater is the fiber radius.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties.
shape_factor – constant dependent on the shape of throat cross-section 1.75 - 2.0, see
Ref
wavelength (float or array like) – The transverse interfacial radius of curvature at the neck
(fiber radius in fibrous media)
require_pair (bool) – Controls whether snap-off requires a pair of arc meniscii to occur.
surface_tension (dict key (string)) – The dictionary key containing the surface tension values to be used.
If a pore property is given, it is interpolated to a throat list.
contact_angle (dict key (string)) – The dictionary key containing the contact angle values to be used.
If a pore property is given, it is interpolated to a throat list.
throat_diameter (dict key (string)) – The dictionary key containing the throat diameter values to be used.
Returns:
value – Array containing throat capillary snap-off pressure values.
Return type:
NumPy ndarray
Notes
This equation should be used to calculate the snap off capillary pressure
in fribrous media
References
[1]: Ransohoff, T.C., Gauglitz, P.A. and Radke, C.J., 1987. Snap‐off of gas
bubbles in smoothly constricted noncircular capillaries. AIChE Journal,
33(5), pp.753-765.
Computes the throat capillary entry pressure assuming the throat has a
sinusoisal profile.
Makes use of the toroidal meniscus model with mode touch.
This model accounts for mensicus protrusion into adjacent pores and
touching solid features.
It is bidirectional becauase the connected pores generally have different
sizes and this determines how far the meniscus can protrude.
Parameters:
target (OpenPNM Object) – The object for which these values are being calculated. This
controls the length of the calculated array, and also provides
access to other necessary thermofluid properties
r_toroid (float or array_like) – The radius of the toroid surrounding the pore
num_points (float (Default 100)) – The number of divisions to make along the profile length to assess the
meniscus properties in order to find the touch length.
surface_tension (dict key (string)) – The dictionary key containing the surface tension values to be used.
If a pore property is given, it is interpolated to a throat list.
contact_angle (dict key (string)) – The dictionary key containing the contact angle values to be used.
If a pore property is given, it is interpolated to a throat list.
throat_diameter (dict key (string)) – The dictionary key containing the throat diameter values to be used.
pore_diameter (dict key (string)) – The dictionary key containing the pore diameter values to be used.
Returns:
value – Array containing throat capillary entry pressure values.
Calculate the diffusive conductance of conduits in network, where a
conduit is ( 1/2 pore - full throat - 1/2 pore ). See the notes section.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides access to other
necessary properties.
pore_area (string) – Dictionary key of the pore area values
throat_area (string) – Dictionary key of the throat area values
pore_diffusivity (string) – Dictionary key of the pore diffusivity values
throat_diffusivity (string) – Dictionary key of the throat diffusivity values
conduit_lengths (string) – Dictionary key of the conduit length values
conduit_shape_factors (string) – Dictionary key of the conduit DIFFUSION shape factor values
Returns:
g – Array containing diffusive conductance values for conduits in the
geometry attached to the given physics object.
Return type:
ndarray
Notes
This function assumes cylindrical throats with constant cross-section
area, and the pore area corresponds to the cross-sectional area at the
largest opening of the pore. Corrections for different shapes and variable
cross-section area can be imposed by passing the proper
conduit_shape_factors argument.
conduit_shape_factor depends on the physics of the problem,
i.e. diffusion-like processes and fluid flow need different shape factors.
Calculate the diffusive conductance of conduits in network, where a
conduit is ( 1/2 pore - full throat - 1/2 pore ). The conduit consists of 2
flate parallel plates. See the notes section.
Parameters:
target (OpenPNM Object) – The object which this model is associated with. This controls the
length of the calculated array, and also provides acce