User Guide
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 thermophysical 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 twolayered electrode, or multimodal 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 thermophysical properties. For example, the HaganPoiseuille 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 thermophysical property data, and a specific Geometry from which it retries geometrical information. Physics objects, because they are associated onetoone 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 porescale 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: 
 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
 name (string, optional) â€“ The unique name of the object. If not given one will be generated.
 project (OpenPNM Project object, optional) â€“ The Project with which the object should be assigned. If not supplied
then a new Project is created

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.
>>> import openpnm as op
>>> obj = op.core.Base(Np=4, Nt=5)
Now query the object for its basic properties:
>>> obj.Np, obj.Nt # Number of pores and throats
(4, 5)
Add a label to the object, as a boolean with True where the label applies:
>>> obj['pore.new_label'] = [ True, False, False, True]
See list of available labels and confirm new_label was added:
>>> print(obj.labels())
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 : pore.all
2 : pore.new_label
3 : throat.all
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
Use the label to fetch pores where it was applied:
>>> Ps = obj.pores('new_label')
>>> print(Ps)
[0 3]
Find the number of pores with label:
>>> print(obj.num_pores('new_label'))
2
Convert between indices and boolean mask
>>> mask = obj.tomask(throats=[0, 2, 4])
>>> print(mask)
[ True False True False True]
>>> inds = obj.toindices(mask)
>>> print(inds)
[0 2 4]
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 Npby3 array of [x, y, z] coordinates for each pore.
 conns (array_like) â€“ An Ntby2 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
Create some pore coordinates and connections manually and assign to a
GenericNetwork instance. Consider a linear network of 4 pores and 3
throats:
0 â€•â€• 1 â€•â€• 3 â€•â€• 2
>>> coords = [[0, 0, 0], [1, 0, 0], [2, 0, 0], [3, 0, 0]]
>>> conns = [[0, 1], [1, 3], [2, 3]]
>>> pn = op.network.GenericNetwork(conns=conns, coords=coords)
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.
>>> print(pn.props())
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 : pore.coords
2 : throat.conns
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
The GenericNetwork class has several methods for querying the topology.
>>> Ps = pn.find_neighbor_pores(pores=1)
>>> print(Ps)
[0 3]
>>> Ts = pn.find_neighbor_throats(pores=[0, 1])
>>> print(Ts)
[0 1]
>>> print(pn.num_neighbors(2))
[1]
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 blankslate object with no porescale 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 to which this Phase should be attached
 name (str, optional) â€“ A unique string name to identify the Phase object, typically same as
instance name but can be anything.

Examples
Create a new empty phase:
>>> import openpnm as op
>>> pn = op.network.Cubic([10, 10, 10])
>>> phase = op.phases.GenericPhase(network=pn)
And add a model:
>>> phase.add_model(propname='pore.molar_density',
... model=op.models.phases.molar_density.ideal_gas)
Now confirm that the model was added and data was calculated. The
models attribute can be printed:
>>> print(phase.models)
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
# Property Name Parameter Value
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 pore.molar_density model: ideal_gas
pressure: pore.pressure
temperature: pore.temperature
regeneration mode: normal
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
And the Phase itself has a nice printout using print(phase) .
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 arraybased 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 Ntlong array of values, because 'throat.length' and 'throat.radius' were also Ntlong.
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:
>>> import openpnm as op
>>> import scipy as sp
>>> pn = op.network.Cubic(shape=[3, 3, 3])
>>> pn['pore.coords'][1]
array([0.5, 0.5, 1.5])
Note that pn['pore.coords'] retrieves the Numpy array from the dictionary, while the [1] retrieves the value in element 1 of the Numpy array.
Writing data is straightforward:
>>> pn['pore.foo'] = 1.0
>>> pn['pore.foo'][5]
1.0
>>> pn['pore.foo'][6] = 2.0
>>> pn['pore.foo'][6]
2.0
>>> pn['pore.foo'][5]
1.0
The above lines illustrate how a scalar value is converted to a vector (Nplong), and how specific pore values can be assigned. It is also possible to assign an entire array in one step:
>>> pn['pore.bar'] = sp.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:
>>> pn.props()
['pore.bar', 'pore.coords', 'pore.foo', 'throat.conns']
>>> pn.props('throat')
['throat.conns']
You can also view a nicely formatted list of props with print(pn.props()) .
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:
>>> pn.pores('top')
array([ 2, 5, 8, 11, 14, 17, 20, 23, 26])
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:
>>> pn['pore.top'][2] = False
>>> list(sp.where(pn['pore.top'])[0])
[5, 8, 11, 14, 17, 20, 23, 26]
>>> pn['pore.top'][2] = True # Reapply label to pore 2
Creating a new label array occurs automatically if a Boolean array is stored on an object:
>>> pn['pore.dummy_1'] = sp.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 :
>>> list(sp.where(pn['pore.dummy_2'])[0])
[3, 4, 5]
The pores and throats methods offer several useful enhancements to this approach. For instance, several labels can be queried at once:
>>> list(pn.pores(['top', 'dummy_2']))
[2, 3, 4, 5, 8, 11, 14, 17, 20, 23, 26]
And there is also a mode argument which can be used to apply set theory logic to the returned list:
>>> list(pn.pores(['top', 'dummy_2'], mode='intersection'))
[5]
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.
>>> pn.labels()
['pore.all', 'pore.back', 'pore.bottom', 'pore.dummy_1', 'pore.dummy_2', 'pore.front', 'pore.internal', 'pore.left', 'pore.right', 'pore.surface', 'pore.top', 'throat.all', 'throat.internal', 'throat.surface']
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 porescale 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:
>>> import openpnm as op
>>> pn = op.network.Cubic([5, 5, 5])
>>> geo1 = op.geometry.GenericGeometry(network=pn, pores=range(0, 75),
... throats=range(0, 150))
>>> geo2 = op.geometry.GenericGeometry(network=pn, pores=range(75, 125),
... throats=range(150, 300))
>>> geo1['pore.diameter'] = 1.0
>>> geo2['pore.diameter'] = 0.1
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:
>>> Dp = pn['pore.diameter']
>>> print(Dp[70:80])
[1. 1. 1. 1. 1. 0.1 0.1 0.1 0.1 0.1]
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:
>>> coords = geo1['pore.coords']
>>> print(coords[0:3])
[[0.5 0.5 0.5]
[0.5 0.5 1.5]
[0.5 0.5 2.5]]
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 graphtool and NetworkX) should be used to store the network topology. It was decided that network property data should be simply stored as Numpy NDarrays). 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 nondirectional, meaning that flow in either direction is equal
Other general, but nonessential 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 NpbyNp 2D matrix. A nonzero 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 Ntby2 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.
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.
>>> import openpnm as op
>>> ws = op.Workspace()
>>> ws.clear() # Clear workspace of any preexisting 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)
>>> pn1 in proj1
True
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:
>>> pn2 = op.network.Cubic(shape=[3, 3, 3])
>>> proj2 = pn2.project
>>> proj1 == proj2
False
Now that weâ€™ve successfully created 2 Networks, there will be 2 Projects open
in the Workspace:
>>> print(ws.keys())
dict_keys(['one', 'sim_01'])
The second Project was created automatically, and given a default name of
â€˜sim_01â€™.
When adding other objects, either the Network or the Project can be specified,
which is possible since there is only one Network per Project:
>>> geo1 = op.geometry.GenericGeometry(network=pn1, pores=pn1.Ps, throats=pn1.Ts, name='geo_01')
>>> geo2 = op.geometry.GenericGeometry(project=proj2, pores=pn2.Ps, throats=pn2.Ts, name='geo_02')
Projects can fetched from the Workspace by name, and renamed if
desired:
>>> proj2 = ws['sim_01']
>>> proj2.name = 'two'
>>> print(ws.keys())
dict_keys(['one', 'two'])
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:
proj = phys.project
phase = proj.find_phase(physics=phys)
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 subnetworks 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
 Creating custom behavior
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â€™.
Adding a model to an object is done as follows:
 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:
>>> import openpnm as op
>>> 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 subclasses, add porescale models, define new topology generators, and to add or change OpenPNM methods. The other approach is to install OpenPNM in your Python PATH (using pip install openpnm ) 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 straightforward 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:
def surface_roughness(roughness_parameter):
return roughness_parameter
Now, we can see this model in action by creating a script in the working directory as follows:
import my_models
a = my_models.surface_roughness(roughness_parameter=2.2)
print(a)
2.2
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:
def surface_roughness(target, roughness_parameter):
P_diam = target['pore.diameter']
projected_area = 4*3.14159*(P_diam/2)**2
rough_area = projected_area**roughness_parameter
return rough_area
We can now update our script:
import openpnm as op
import scipy as sp
import my_models
#Generate a simple Cubic Network
pn = op.network.Cubic(shape=[3,3,3])
#Generate an 'empty' Geometry with no properties
geom = op.geometry.GenericGeometry(network=pn,pores=pn.pores(),throats=pn.throats())
#Assign random pores diameters between 0 and 40
geom['pore.diameter'] = sp.rand(pn.Np,)*40
#Assign model to geometry
geom.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 porescale 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:
def surface_roughness(target, roughness_parameter, pore_diameter='pore.diameter'):
P_diam = target[pore_diameter]
projected_area = 4*3.14159*(P_diam/2)**2
rough_area = projected_area**roughness_parameter
return rough_area
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 geometry
geom.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 nondefault 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 porescale 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 porescale 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 preset porescale 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' .
from openpnm.phases import GenericPhase
class Oil(GenericPhase):
def __init__(self, **kwargs):
super().__init__(**kwargs) # This is a python thing, and calls the init of the parent class
self.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:
coords = [[1, 1, 0],
[2, 0, 0],
[0, 0, 0],
[2, 2, 0],
[0, 2, 0],
[0, 1, 0],
[1, 0, 0]]
And the connections are:
conns = [[0, 1],
[0, 3],
[0, 4],
[0, 5],
[1, 3],
[1, 6],
[2, 5],
[2, 6],
[3, 4],
[4, 5]]
A custom Network can be created by passing these two arrays in to a GenericNetwork class:
pn = op.network.GenericNetwork(coords=coords, conns=conns)
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:
from openpnm.network import GenericNetwork
class MyNetwork(GenericNetwork):
def __init__(self, **kwargs):
# Place pore locations and throat connecting generation code here
super().__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:
Algorithms
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 nonwetting fluids in porous media, drainage is defined as the displacement of the wetting phase by invasion of the nonwetting phase.
A nonwetting phase requires positive pressure to overcome surface tension and push out the wetting phase and so as the imposed pressure in the nonwetting 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 nonwetting 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 nonwetting 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 quasistatic 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 nonwetting 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:
\[S_{wp} = S^*_{wp}\left(\frac{P^*_c}{P_c}\right)^\eta\]
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:
>>> import openpnm as op
>>> ws = op.Workspace()
>>> proj = ws.new_project()
>>> pn = op.network.Cubic(shape=[10, 10, 10], project=proj, spacing=1e4)
>>> geom = op.geometry.StickAndBall(network=pn, pores=pn.Ps, throats=pn.Ts)
>>> geom['pore.volume'][pn.pores('left')] = 0
>>> hg = op.phases.Mercury(network=pn)
>>> phys = op.physics.GenericPhysics(network=pn, phase=hg, geometry=geom)
>>> phys.add_model(propname='throat.entry_pressure',
... model=op.models.physics.capillary_pressure.washburn)
>>> phys.add_model(propname='pore.pc_star',
... model=op.models.misc.from_neighbor_throats,
... throat_prop='throat.entry_pressure',
... mode='min')
>>> phys.add_model(propname='pore.late_filling',
... model=op.models.physics.multiphase.late_filling,
... pressure='pore.pressure',
... Pc_star='pore.pc_star',
... eta=1, Swp_star=0.4,
... regen_mode='deferred')
>>> phys['throat.pc_star'] = phys['throat.entry_pressure']
>>> phys.add_model(propname='throat.late_filling',
... model=op.models.physics.multiphase.late_filling,
... pressure='throat.pressure',
... Pc_star='throat.pc_star',
... eta=1, Swp_star=0.2,
... regen_mode='deferred')
>>> mip = op.algorithms.Porosimetry(project=proj)
>>> mip.setup(phase=hg)
>>> mip.set_partial_filling(propname='pore.late_filling')
>>> mip.set_inlets(pores=pn.pores('bottom'))
>>> mip.run(points=20, stop=1e7)
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, cooperative 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 drawback 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:
>>> import openpnm as op
>>> import matplotlib.pyplot as plt
>>> import scipy as sp
>>> ws = op.Workspace()
>>> proj = ws.new_project()
>>> S = sp.array([100, 100, 1])
>>> pn = op.network.Cubic(shape=S, spacing=0.0001)
>>> geom = op.geometry.StickAndBall(network=pn, pores=pn.Ps, throats=pn.Ts)
>>> water = op.phases.Water(network=pn)
>>> water.add_model(propname='throat.entry_pressure',
... model=op.models.physics.capillary_pressure.washburn)
>>> ip = op.algorithms.InvasionPercolation(network=pn)
>>> ip.setup(phase=water)
>>> ip.set_inlets(pores=[0])
>>> ip.run()
>>> water.update(ip.results(Snwp=0.5))
Images can be produced easily for 2D networks with commands such as plt.imshow(sp.reshape(ip['pore.invasion_sequence'], newshape=S[S > 1]))
and plt.imshow(sp.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 snapoff.
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 steadystate 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:
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5], spacing=1e4)
>>> phase = op.phases.GenericPhase(network=pn)
>>> phase['throat.conductance'] = 1.0 # Use dummy values
The steadystate 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 righthand 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
Continuing with the example from above:
>>> alg = op.algorithms.GenericTransport(network=pn)
>>> alg.setup(phase=phase, quantity='pore.quantity', conductance='throat.conductance')
The material balance around a given pore i assuming steadystate 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:
\[( g_{1,2} + g_{1,3} + g_{1,4} ) \cdot x_1 + g_{1,2} \cdot x_2 + g_{1,3} \cdot x_3 + g_{1,4} \cdot x_4 = 0\]
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:
>>> alg.A.shape, alg.A.format, alg.A.nnz
((125, 125), 'coo', 725)
For steadystate transport with no source or sink terms the righthand 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 righthand side of each balance equation to some nonzero 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.
>>> alg.set_value_BC(pores=pn.pores('left'), values=1)
>>> alg.set_rate_BC(pores=pn.pores('right'), values=1)
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 scikitumfpack 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:
>>> alg._apply_BCs()
>>> x = alg._solve(A=alg.A, b=alg.b)
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 nonlinear source/sink terms. The balance equation around a pore i in the presence of a source/sink term is:
\[\sum_{j=1}^n {g_{i,j} \cdot (x_i  x_j)} + R(x_i) = 0\]
where \(R(x_i)\) is some function of the quantity being solved for, and can be nonlinear. 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:
>>> alg = op.algorithms.ReactiveTransport(network=pn)
>>> alg.setup(phase=phase, quantity='pore.quantity', conductance='throat.conductance')
>>> alg.set_value_BC(pores=pn.pores('left'), values=1)
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 porescale model:
>>> phase.add_model(propname='pore.rxn',
... model=op.models.physics.generic_source_term.power_law,
... A1='pore.A', A2='pore.n', A3='pore.A3',
... X='pore.quantity')
>>> phase['pore.A'] = 1.0
>>> phase['pore.n'] = 2
>>> phase['pore.A3'] = 0
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 nonlinear 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_physics()
>>> alg._apply_sources()
>>> x = alg._run_reactive(x=None)
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 CrankNicolson scheme can be used by setting self.settings['t_scheme'] = 'cranknicolson' .

Module Reference
Contents:
openpnm.core
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
viceversa 
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 â€˜porescaleâ€™ 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 
Reruns 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 porescale
models and their associated parameters. When regenerate_models is called
the function and all the given parameters are retrieved from this dictionary
and run.
Base

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: 
 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
 name (string, optional) â€“ The unique name of the object. If not given one will be generated.
 project (OpenPNM Project object, optional) â€“ The Project with which the object should be assigned. If not supplied
then a new Project is created

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.
>>> import openpnm as op
>>> obj = op.core.Base(Np=4, Nt=5)
Now query the object for its basic properties:
>>> obj.Np, obj.Nt # Number of pores and throats
(4, 5)
Add a label to the object, as a boolean with True where the label applies:
>>> obj['pore.new_label'] = [ True, False, False, True]
See list of available labels and confirm new_label was added:
>>> print(obj.labels())
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 : pore.all
2 : pore.new_label
3 : throat.all
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
Use the label to fetch pores where it was applied:
>>> Ps = obj.pores('new_label')
>>> print(Ps)
[0 3]
Find the number of pores with label:
>>> print(obj.num_pores('new_label'))
2
Convert between indices and boolean mask
>>> mask = obj.tomask(throats=[0, 2, 4])
>>> print(mask)
[ True False True False True]
>>> inds = obj.toindices(mask)
>>> print(inds)
[0 2 4]

Np
A shortcut to query the total number of pores on the objectâ€™

Nt
A shortcut to query the total number of throats on the objectâ€™

Ps
A shortcut to get a list of all pores on the object

Ts
A shortcut to get a list of all throats on the object

check_data_health (props=[], element=None)[source]
Check the health of pore and throat data arrays.
Parameters: 
 element (string, optional) â€“ Can be either â€˜poreâ€™ or â€˜throatâ€™, which will limit the checks to
only those data arrays.
 props (list of pore (or throat) properties, optional) â€“ If given, will limit the health checks to only the specfied
properties. Also useful for checking existance.

Returns: 
 Returns a HealthDict object which a basic dictionary with an added
health attribute that is True is all entries in the dict are
 deemed healthy (empty lists), or False otherwise.

Examples
>>> import openpnm
>>> pn = openpnm.network.Cubic(shape=[5, 5, 5])
>>> h = pn.check_data_health()
>>> h.health
True

clear (element=None, mode='all')[source]
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 del object['pore.blah'] at the Python prompt. This
can also be done in a forloop to remove a list of items.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> len(pn.labels()) # There are 10 total labels on the network
12
>>> 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 locations
4
>>> pn.clear(mode='labels')
>>> 'pore.'+geom.name in pn.keys() # The geometry labels were kept
True
>>> len(pn.props()) # The network has two properties
2
>>> pn.clear(element='pore', mode='props')
>>> 'pore.coords' in pn.keys() # The pore property was removed
True
>>> pn.clear() # Remove everything except protected labels and arrays
>>> print(sorted(list(pn.keys(element='pore', mode='all'))))
['pore.all', 'pore.coords', 'pore.geo1']

filter_by_label (pores=[], throats=[], labels=None, mode='or')[source]
Returns which of the supplied pores (or throats) has the specified
label
Parameters: 
 or throats (pores,) â€“ List of pores or throats to be filtered
 labels (list of strings) â€“ The labels to apply as a filter
 mode (string) â€“
Controls how the filter is applied. Options include:
â€™orâ€™, â€˜unionâ€™, â€˜anyâ€™: (default) Returns a list of the given
locations where any of the given labels exist.
â€™andâ€™, â€˜intersectionâ€™, â€˜allâ€™: Only locations where all the
given labels are found.
â€™xorâ€™, â€˜exclusive_orâ€™: Only locations where exactly one of
the given labels are found.
â€™norâ€™, â€˜noneâ€™, â€˜notâ€™: Only locations where none of the given
labels are found.
â€™nandâ€™ : Only locations with some but not all of the given
labels are returned.
â€™xnorâ€™ : Only locations with more than one of the given
labels are returned.

Returns: 
 A list of pores (or throats) that have been filtered according the
 given criteria. The returned list is a subset of the received list of
 pores (or throats).

Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> pn.filter_by_label(pores=[0, 1, 5, 6], labels='left')
array([0, 1])
>>> Ps = pn.pores(['top', 'bottom', 'front'], mode='or')
>>> pn.filter_by_label(pores=Ps, labels=['top', 'front'],
... mode='and')
array([ 4, 9, 14, 19, 24])

interleave_data (prop)[source]
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
>>> import openpnm as op
>>> 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]

interpolate_data (propname)[source]
Determines a pore (or throat) property as the average of itâ€™s
neighboring throats (or pores)
Parameters:  propname (string) â€“ The dictionary key to the values to be interpolated. 
Returns:  
Return type:  An array containing interpolated pore (or throat) data 
Notes
This uses an unweighted average, without attempting to account for
distances or sizes of pores and throats.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[3, 1, 1])
>>> pn['pore.value'] = [1, 2, 3]
>>> pn.interpolate_data('pore.value')
array([1.5, 2.5])

keys (element=None, mode=None, deep=False)[source]
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.

Notes
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
>>> import openpnm as op
>>> 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']

labels (pores=[], throats=[], element=None, mode='union')[source]
Returns a list of labels present on the object
Additionally, this function can return labels applied to a specified
set of pores or throats
Parameters: 
 element (string) â€“ Controls whether pore or throat labels are returned. If empty then
both are returned (default).
 (or throats) (pores) â€“ The pores (or throats) whose labels are sought. If left empty a
list containing all pore and throat labels is returned.
 mode (string, optional) â€“
Controls how the query should be performed. Only applicable
when pores or throats are specified:
â€™orâ€™, â€˜unionâ€™, â€˜anyâ€™: (default) Returns the labels that are
assigned to any of the given locations.
â€™andâ€™, â€˜intersectionâ€™, â€˜allâ€™: Labels that are present on all
the given locations.
â€™xorâ€™, â€˜exclusive_orâ€™ : Labels that are present on only one
of the given locations.
â€™norâ€™, â€˜noneâ€™, â€˜notâ€™: Labels that are not present on any of
the given locations.
â€™nandâ€™: Labels that are present on all but one of the given
locations
â€™xnorâ€™: Labels that are present on more than one of the given
locations. â€˜nxorâ€™ is also accepted.

Returns: 
 A list containing the labels on the object. If
pores or
throats are given, the results are filtered according to the
 specified
mode .

Notes
Technically, â€˜nandâ€™ and â€˜xnorâ€™ should also return pores with none
of the labels but these are not included. This makes the returned list
more useful.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> pn.labels(pores=[11, 12])
['pore.all', 'pore.front', 'pore.internal', 'pore.surface']

map_pores (pores, origin, filtered=True)[source]
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 NDarray of indices is returned with missing
indices removed, otherwise a namedtuple 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 .


map_throats (throats, origin, filtered=True)[source]
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 NDarray of indices is returned with missing
indices removed, otherwise a namedtuple 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 target object. Can be an array or a tuple containing an array
 and a mask, depending on the value of
filtered .


network
A shortcut to get a handle to the associated network
There can only be one so this works

num_pores (labels='all', mode='or')[source]
Returns the number of pores of the specified labels
Parameters: 
 labels (list of strings) â€“ The pore labels that should be included in the count.
If not supplied, all pores are counted.
 labels â€“ Label of pores to be returned
 mode (string, optional) â€“
Specifies how the count should be performed. The options are:
â€™orâ€™, â€˜unionâ€™, â€˜anyâ€™ : (default) Pores with one or more of
the given labels are counted.
â€™andâ€™, â€˜intersectionâ€™, â€˜allâ€™ : Pores with all of the given
labels are counted.
â€™xorâ€™, â€˜exclusive_orâ€™ : Pores with only one of the given
labels are counted.
â€™norâ€™, â€˜noneâ€™, â€˜notâ€™ : Pores with none of the given labels
are counted.
â€™nandâ€™ : Pores with some but not all of the given labels are
counted.
â€™xnorâ€™ : Pores with more than one of the given labels are
counted.

Returns:  Np â€“ Number of pores with the specified labels

Return type:  int

Notes
Technically, â€˜nandâ€™ and â€˜xnorâ€™ should also count pores with none
of the labels, however, to make the count more useful these are not
included.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> pn.num_pores()
125
>>> pn.num_pores(labels=['top'])
25
>>> pn.num_pores(labels=['top', 'front'], mode='or')
45
>>> pn.num_pores(labels=['top', 'front'], mode='xnor')
5

num_throats (labels='all', mode='union')[source]
Return the number of throats of the specified labels
Parameters: 
 labels (list of strings, optional) â€“ The throat labels that should be included in the count.
If not supplied, all throats are counted.
 mode (string, optional) â€“
Specifies how the count should be performed. The options are:
â€™orâ€™, â€˜unionâ€™, â€˜anyâ€™ : (default) Throats with one or more of
the given labels are counted.
â€™andâ€™, â€˜intersectionâ€™, â€˜allâ€™ : Throats with all of the given
labels are counted.
â€™xorâ€™, â€˜exclusive_orâ€™ : Throats with only one of the given
labels are counted.
â€™norâ€™, â€˜noneâ€™, â€˜notâ€™ : Throats with none of the given labels
are counted.
â€™nandâ€™ : Throats with some but not all of the given labels
are counted.
â€™xnorâ€™ : Throats with more than one of the given labels are
counted.

Returns:  Nt â€“ Number of throats with the specified labels

Return type:  int

Notes
Technically, â€˜nandâ€™ and â€˜xnorâ€™ should also count throats with
none of the labels, however, to make the count more useful these are
not included.

pores (labels='all', mode='or', asmask=False)[source]
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.

Returns: 
 A Numpy array containing pore indices filtered by the logic specified
 in
mode .

Notes
Technically, nand and xnor should also return pores with none of
the labels but these are not included. This makes the returned list
more useful.
To perform more complex or compound queries, you can opt to receive
the result a a boolean mask (asmask=True ), then manipulate the
arrays manually.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> Ps = pn.pores(labels=['top', 'front'], mode='union')
>>> Ps[:5] # Look at first 5 pore indices
array([0, 1, 2, 3, 4])
>>> pn.pores(labels=['top', 'front'], mode='xnor')
array([ 4, 9, 14, 19, 24])

props (element=None, mode='all', deep=False)[source]
Returns a list containing the names of all defined pore or throat
properties.
Parameters: 
 element (string, optional) â€“ Can be either â€˜poreâ€™ or â€˜throatâ€™ to specify what properties are
returned. If no element is given, both are returned
 mode (string, optional) â€“
Controls what type of properties are returned. Options are:
â€™allâ€™ : Returns all properties on the object (default)
â€™modelsâ€™ : Returns only properties that are associated with a
model
â€™constantsâ€™ : returns data values that were not generated by
a model, but manaully created.
 deep (Boolean) â€“ If set to
True then the props on all associated subdomain
objects are returned as well.

Returns: 
 A an alphabetically sorted list containing the string name of all
 pore or throat properties currently defined. This list is an iterable,
 so is useful for scanning through properties.

Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[3, 3, 3])
>>> pn.props('pore')
['pore.coords']
>>> pn.props('throat')
['throat.conns']
>>> pn.props()
['pore.coords', 'throat.conns']

show_hist (props=[], bins=20, **kwargs)[source]
Show a quick plot of key property distributions.
Parameters: 
 props (string or list of strings) â€“ The pore and/or throat properties to be plotted as histograms
 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.

Notes
Other keyword arguments are passed to the matplotlib.pyplot.hist
function.

throats (labels='all', mode='or', asmask=False)[source]
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.

Returns: 
 A Numpy array containing throat indices filtered by the logic specified
 in
mode .

Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[3, 3, 3])
>>> Ts = pn.throats()
>>> Ts[0:5] # Look at first 5 throat indices
array([0, 1, 2, 3, 4])

toindices (mask)[source]
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
 the received mask was True.

Notes
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 .

tomask (pores=None, throats=None)[source]
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
 throat locations.

Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> mask = pn.tomask(pores=[0, 10, 20])
>>> sum(mask) # 3 nonzero elements exist in the mask (0, 10 and 20)
3
>>> len(mask) # Mask size is equal to the number of pores in network
125
>>> mask = pn.tomask(throats=[0, 10, 20])
>>> len(mask) # Mask is now equal to number of throats in network
300
Subdomain

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.
ModelsMixin

class
openpnm.core. ModelsMixin [source]
Bases: object
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.
Method or Attribute 
Functionality 
add_model 
Add a given model and parameters to the object 
regenerate_model 
Runs the model(s) to recalculate data 
remove_model 
Removes specified model as well as itâ€™s data 
Examples
Create a Demo class using Base and ModelsMixin:
>>> class Demo(op.core.Base, op.core.ModelsMixin):
... pass
>>> temp = Demo(Np=3, Nt=2)
The new class has the normal Base methods:
>>> print(temp.num_pores())
3
But also has those needed for working with models. For instance, a simple
model can be added as follows:
>>> temp.add_model(propname='pore.test',
... model=op.models.misc.constant,
... value=2)
>>> print(temp['pore.test'])
[2 2 2]
All the models and their respective parameters are stored in the models
attribute:
>>> print(temp.models)
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
# Property Name Parameter Value
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 pore.test model: constant
value: 2
regeneration mode: normal
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•

add_model (propname, model, regen_mode='normal', **kwargs)[source]
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â€™ : 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.


regenerate_models (propnames=None, exclude=[], deep=False)[source]
Reruns the specified model or models.
Parameters: 
 propnames (string or list of strings) â€“ The list of property names to be regenerated. If None are given
then ALL models are rerun (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.


remove_model (propname=None, mode=['model', 'data'])[source]
Removes model and data from object.
Parameters: 
 propname (string or list of strings) â€“ The property or list of properties to remove
 mode (list of strings) â€“
Controls what is removed. Options are:
â€™modelâ€™ : Removes the model but not any numerical data that may
already exist.
â€™dataâ€™ : Removes the data but leaves the model.
 default is both. (The) â€“

ModelsDict

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 .

dependency_graph ()[source]
Returns a NetworkX graph object of the dependencies
Notes
To visualize the dependencies, the following NetworkX function and
settings is helpful:
 nx.draw_spectral(d, arrowsize=50, font_size=32, with_labels=True,
 node_size=2000, width=3.0, edge_color=â€™lightgreyâ€™,
font_weight=â€™boldâ€™)

dependency_list ()[source]
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 .

dependency_map ()[source]
Create a graph of the dependency graph in a decent format
openpnm.network
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 zcomponent (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 
create_adjacency_matrix 
Generates a weighted adjacency matrix 
create_incidence_matrix 
Creates a weighted incidence matrix 
get_adjacency_matrix 
Returns an adjacency matrix with default weights 
get_incidence_matrix 
Returns an incidence matrix with default weights 
check_network_health 
Check various aspects of topology for problems 
GenericNetwork

class
openpnm.network. GenericNetwork (conns=None, coords=None, project=None, settings={}, **kwargs)[source]
Bases: openpnm.core.Base.Base , openpnm.core.ModelsMixin.ModelsMixin
This generic class contains the main functionality used by all networks
Parameters: 
 coords (array_like) â€“ An Npby3 array of [x, y, z] coordinates for each pore.
 conns (array_like) â€“ An Ntby2 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
Create some pore coordinates and connections manually and assign to a
GenericNetwork instance. Consider a linear network of 4 pores and 3
throats:
0 â€•â€• 1 â€•â€• 3 â€•â€• 2
>>> coords = [[0, 0, 0], [1, 0, 0], [2, 0, 0], [3, 0, 0]]
>>> conns = [[0, 1], [1, 3], [2, 3]]
>>> pn = op.network.GenericNetwork(conns=conns, coords=coords)
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.
>>> print(pn.props())
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 : pore.coords
2 : throat.conns
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
The GenericNetwork class has several methods for querying the topology.
>>> Ps = pn.find_neighbor_pores(pores=1)
>>> print(Ps)
[0 3]
>>> Ts = pn.find_neighbor_throats(pores=[0, 1])
>>> print(Ts)
[0 1]
>>> print(pn.num_neighbors(2))
[1]
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.

am
Returns an adjacency matrix in the specified sparse format, with 1â€™s
indicating the nonzero 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 rowwise 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 ones at each nonzero
location use create_adjacency_matrix .
To obtain the nondirected graph, with only uppertriangular entries,
use sp.sparse.triu(am, k=1) .

check_network_health ()[source]
This method check the network topological health by checking for:
 Isolated pores
 Islands or isolated clusters of pores
 Duplicate throats
 Bidirectional throats (ie. symmetrical adjacency matrix)
 Headless throats
Returns: 
 A dictionary containing the offending pores or throat numbers under
 each named key.
 It also returns a list of which pores and throats should be trimmed
 from the network to restore health. This list is a suggestion only,
 and is based on keeping the largest cluster and trimming the others.

Notes
 Does not yet check for duplicate pores
 Does not yet suggest which throats to remove
 This is just a â€˜checkâ€™ and does not â€˜fixâ€™ the problems it finds

create_adjacency_matrix (weights=None, fmt='coo', triu=False, drop_zeros=False)[source]
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 Ntlong, it implies that the matrix is symmetric,
so the upper and lower triangular regions are mirror images. If
it is 2*Ntlong 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 rowwise 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*Ntlong.
 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 nonzero 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.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> weights = sp.rand(pn.num_throats(), ) < 0.5
>>> am = pn.create_adjacency_matrix(weights=weights, fmt='csr')

create_incidence_matrix (weights=None, fmt='coo', drop_zeros=False)[source]
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 rowwise 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 nonzero 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 nonzeros.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> weights = sp.rand(pn.num_throats(), ) < 0.5
>>> im = pn.create_incidence_matrix(weights=weights, fmt='csr')

find_connected_pores (throats=[], flatten=False, mode='union')[source]
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 subarras 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
flatten is False )

Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> Ps = pn.find_connected_pores(throats=[0, 1])
>>> print(Ps)
[[0 1]
[1 2]]
>>> Ps = pn.find_connected_pores(throats=[0, 1], flatten=True)
>>> print(Ps)
[0 1 2]

find_connecting_throat (P1, P2)[source]
Return the throat index connecting pairs of pores
Parameters:  , P2 (P1) â€“ The indices of the pores whose throats are sought. These can be
vectors of indices, but must be the same length 
Returns: 
 Returns a list the same length as P1 (and P2) with the each element
 containing the throat index that connects the corresponding pores,
 or None` if pores are not connected.

Notes
The returned list can be converted to an NDarray, which will convert
the None values to nan . These can then be found using
scipy.isnan .
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> Ts = pn.find_connecting_throat([0, 1, 2], [2, 2, 2])
>>> print(Ts)
[None, 1, None]

find_nearby_pores (pores, r, flatten=False, include_input=False)[source]
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 returned
list. 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 subarray for each input
pore, where each subarray 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 Nlong list of such lists is
 returned. The returned lists each contain the pore for which the
 neighbors were sought.

Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[3, 3, 3])
>>> Ps = pn.find_nearby_pores(pores=[0, 1], r=1)
>>> print(Ps)
[array([3, 9]), array([ 2, 4, 10])]
>>> Ps = pn.find_nearby_pores(pores=[0, 1], r=0.5)
>>> print(Ps)
[array([], dtype=int64), array([], dtype=int64)]
>>> Ps = pn.find_nearby_pores(pores=[0, 1], r=1, flatten=True)
>>> print(Ps)
[ 2 3 4 9 10]

find_neighbor_pores (pores, mode='union', flatten=True, include_input=False)[source]
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 sublist
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.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> Ps = pn.find_neighbor_pores(pores=[0, 2])
>>> print(Ps)
[ 1 3 5 7 25 27]
>>> Ps = pn.find_neighbor_pores(pores=[0, 1])
>>> print(Ps)
[ 2 5 6 25 26]
>>> Ps = pn.find_neighbor_pores(pores=[0, 1], mode='union',
... include_input=True)
>>> print(Ps)
[ 0 1 2 5 6 25 26]
>>> Ps = pn.find_neighbor_pores(pores=[0, 2], flatten=False)
>>> print(Ps)
[array([ 1, 5, 25]), array([ 1, 3, 7, 27])]
>>> Ps = pn.find_neighbor_pores(pores=[0, 2], mode='xnor')
>>> print(Ps)
[1]
>>> Ps = pn.find_neighbor_pores(pores=[0, 2], mode='xor')
>>> print(Ps)
[ 3 5 7 25 27]

find_neighbor_throats (pores, mode='union', flatten=True)[source]
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.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> Ts = pn.find_neighbor_throats(pores=[0, 1])
>>> print(Ts)
[ 0 1 100 101 200 201]
>>> Ts = pn.find_neighbor_throats(pores=[0, 1], flatten=False)
>>> print(Ts)
[array([ 0, 100, 200]), array([ 0, 1, 101, 201])]

get_adjacency_matrix (fmt='coo')[source]
Returns an adjacency matrix in the specified sparse format, with 1â€™s
indicating the nonzero 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 rowwise 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 ones at each nonzero
location use create_adjacency_matrix .
To obtain the nondirected graph, with only uppertriangular entries,
use sp.sparse.triu(am, k=1) .

get_incidence_matrix (fmt='coo')[source]
Returns an incidence matrix in the specified sparse format, with 1â€™s
indicating the nonzero 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 rowwise 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 ones at each nonzero
location use create_incidence_matrix .

im
Returns an incidence matrix in the specified sparse format, with 1â€™s
indicating the nonzero 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 rowwise 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 ones at each nonzero
location use create_incidence_matrix .

num_neighbors (pores, mode='or', flatten=False)[source]
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.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> Np = pn.num_neighbors(pores=[0, 1], flatten=False)
>>> print(Np)
[3 4]
>>> Np = pn.num_neighbors(pores=[0, 2], flatten=True)
>>> print(Np)
6
>>> Np = pn.num_neighbors(pores=[0, 2], mode='and', flatten=True)
>>> print(Np)
1
Cubic

class
openpnm.network. Cubic (shape, spacing=[1, 1, 1], connectivity=6, name=None, project=None)[source]
Bases: openpnm.network.GenericNetwork.GenericNetwork
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() .


spacing
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.

shape
The shape of the network. Like spacing this values is meaningless
if the topology is manipulated, so an Exception is thrown.
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5], spacing=[1, 1, 1])
>>> pn.Np
125
And it can be plotted for quick visualization using:
>>> fig = op.topotools.plot_connections(network=pn)
>>> fig = op.topotools.plot_coordinates(network=pn, c='r', s=75, fig=fig)
For larger networks and more control over presentation use Paraview.

add_boundary_pores (labels=['top', 'bottom', 'front', 'back', 'left', 'right'], spacing=None)[source]
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.


from_array (array, propname)[source]
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.


to_array (values)[source]
Converts the values to a rectangular array with the same shape as the
network
Parameters:  values (array_like) â€“ An Nplong array of values to convert to 
Notes
This method can break on networks that have had boundaries added. It
will usually work IF the given values came only from â€˜internalâ€™
pores.
CubicDual

class
openpnm.network. CubicDual (shape, spacing=1, label_1='primary', label_2='secondary', **kwargs)[source]
Bases: openpnm.network.GenericNetwork.GenericNetwork
Body centered cubic lattice plus face centered nodes on the surfaces
This network is essentially a â€˜bccâ€™ lattice, except that the seconary
network (bodycentered pores) has pores on each face of the domain, which
breaks the bodycentric 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.

Examples
>>> import openpnm as op
>>> pn = op.network.CubicDual(shape=[3, 3, 3])
>>> pn.num_pores('pore.primary') # Normal cubic network is present
27
>>> pn.Np # But more pores are present from seconary network
59
And it can be plotted for quick visualization using:
>>> fig = op.topotools.plot_connections(network=pn,
... throats=pn.throats('primary'),
... color='b')
>>> fig = op.topotools.plot_connections(network=pn,
... throats=pn.throats('secondary'),
... color='r')
>>> fig = op.topotools.plot_coordinates(network=pn, c='r', s=75, fig=fig)
For larger networks and more control over presentation use Paraview.

add_boundary_pores (labels=['top', 'bottom', 'front', 'back', 'left', 'right'], spacing=None)[source]
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.

CubicTemplate

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.
See also
The , template_cylinder_annulus , template_sphere_shell
Examples
>>> import openpnm as op
>>> im = op.topotools.template_cylinder_annulus(15, 10, 5)
>>> pn = op.network.CubicTemplate(template=im)
And it can be plotted for quick visualization using:
>>> fig = op.topotools.plot_connections(network=pn)
For larger networks and more control over presentation use Paraview.
Bravais

class
openpnm.network. Bravais (shape, mode, spacing=1, **kwargs)[source]
Bases: openpnm.network.GenericNetwork.GenericNetwork
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
nonstraight connections between the various pore sites.
More information on Bravais lattice notation can be found on wikipedia.
Notes
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.
Examples
>>> import openpnm as op
>>> sc = op.network.Bravais(shape=[3, 3, 3], mode='sc')
>>> bcc = op.network.Bravais(shape=[3, 3, 3], mode='bcc')
>>> fcc = op.network.Bravais(shape=[3, 3, 3], mode='fcc')
>>> sc.Np, bcc.Np, fcc.Np
(27, 35, 63)
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:
>>> sc.num_pores('corner*'), bcc.num_pores('cor*'), fcc.num_pores('cor*')
(27, 27, 27)
Visualization of these three networks can be done quickly using the
functions in topotools. Firstly, merge them all into a single network
for convenience:
>>> bcc['pore.coords'][:, 0] += 3
>>> fcc['pore.coords'][:, 0] += 6
>>> op.topotools.merge_networks(sc, [bcc, fcc])
>>> fig = op.topotools.plot_connections(sc)
For larger networks and more control over presentation use Paraview.

add_boundary_pores (labels, spacing)[source]
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.

Voronoi

class
openpnm.network. Voronoi (shape=None, num_points=None, **kwargs)[source]
Bases: openpnm.network.DelaunayVoronoiDual.DelaunayVoronoiDual
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
Delaunay

class
openpnm.network. Delaunay (shape=None, num_points=None, **kwargs)[source]
Bases: openpnm.network.DelaunayVoronoiDual.DelaunayVoronoiDual
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() .

Notes
This class always performs the tessellation on the full set of points, then
trims any points that lie outside the given domain shape .
Examples
>>> import openpnm as op
>>> import scipy as sp
Supplying custom specified points:
>>> pts = sp.rand(200, 3)
>>> gn = op.network.Delaunay(points=pts, shape=[1, 1, 1])
>>> gn.Np
200
Which can be quickly visualized using:
>>> fig = op.topotools.plot_connections(network=gn)
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 = sp.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 trimmed
True
And visualizing:
>>> fig = op.topotools.plot_connections(network=gn)
If a domain random base points, but truly flat faces is needed use
Voronoi .
Gabriel

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 longerrange 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
>>> import openpnm as op
>>> import scipy as sp
>>> pts = sp.rand(100, 3) * [1, 1, 0] # Set zaxis to 0
>>> gn = op.network.Gabriel(shape=[1, 1, 0], points=pts)
>>> dn = op.network.Delaunay(shape=[1, 1, 0], points=pts)
Now compare them side by side:
>>> gn['pore.coords'] += [1, 0, 0]
>>> op.topotools.merge_networks(dn, gn)
>>> fig = op.topotools.plot_connections(dn)
>>> fig = op.topotools.plot_coordinates(dn, c='r', s=100, fig=fig)
DelaunayVoronoiDual

class
openpnm.network. DelaunayVoronoiDual (shape=[1, 1, 1], num_points=None, **kwargs)[source]
Bases: openpnm.network.GenericNetwork.GenericNetwork
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 dualnature 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 pregenerated 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 Zdimension 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:
>>> import openpnm as op
>>> net = op.network.DelaunayVoronoiDual(num_points=50, shape=[1, 1, 0])
The resulting network can be quickly visualized using
opnepnm.topotools.plot_connections .

add_boundary_pores (labels=['top', 'bottom', 'front', 'back', 'left', 'right'], offset=None)[source]
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.


find_pore_hulls (pores=None)[source]
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
forloop, so could be slow for large networks.

find_throat_facets (throats=None)[source]
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 forloop, so it could be slow for large networks.
openpnm.geometry
The geometry module contains the GenericGeometry class, and an
assortment of subclasses that implement specific porescale 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 bimodal pore size distribution).
Library of Preconfigured Geometry Classes
This module contains a small selection of Geometry classes that are
preconfigured with a selection of porescale 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:
>>> import openpnm as op
>>> pn = op.network.Cubic([5, 5, 5])
>>> geo = op.geometry.StickAndBall(network=pn, pores=pn.Ps, throats=pn.Ts)
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.0
True
>>> 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.7
True
This example illustrated that you can change one property (â€˜pore.seedâ€™) and
that change can be cascaded to all dependent properties (â€˜pore.diameterâ€™).
GenericGeometry

class
openpnm.geometry. GenericGeometry (network=None, project=None, pores=None, throats=None, settings={}, **kwargs)[source]
Bases: openpnm.core.Subdomain.Subdomain , openpnm.core.ModelsMixin.ModelsMixin
This generic class is meant as a starter for custom Geometry objects
It has no porescale models assigned to it, so a a blank slate. Note that
all OpenPNM Geometry subclasses 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
>>> import openpnm as op
>>> 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)
Now assign porescale models to the empty object:
>>> geom.add_model(propname='pore.size',
... model=op.models.misc.random,
... element='pore',
... num_range=[0.01, 0.1])
Confirm that the object has one added model:
>>> print(geom.models)
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
# Property Name Parameter Value
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 pore.size model: random
element: pore
num_range: [0.01, 0.1]
seed: None
regeneration mode: normal
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
The results of the model can be seen using the show_hist function:
>>> geom.show_hist('pore.size')
StickAndBall

class
openpnm.geometry. StickAndBall (**kwargs)[source]
Bases: openpnm.geometry.GenericGeometry.GenericGeometry
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:
>>> import openpnm as op
>>> import scipy as sp
>>> import matplotlib.pyplot as plt
>>> pn = op.network.CubicDual(shape=[5, 5, 5])
>>> Ps = pn.pores('primary')
>>> Ts = pn.throats('primary')
>>> geo1 = op.geometry.StickAndBall(network=pn, pores=Ps, throats=Ts)
>>> Ps = pn.pores('secondary')
>>> Ts = pn.throats(['secondary', 'interconnect'])
>>> geo2 = op.geometry.StickAndBall(network=pn, pores=Ps, throats=Ts)
Now override the â€˜pore.diameterâ€™ values on the geo2 object:
>>> geo2.remove_model('pore.diameter') # Remove model and data
>>> geo2['pore.diameter'] = sp.rand(geo2.Np)*0.05
Look at the â€˜pore.diameterâ€™ distributions on each object:
>>> fig = plt.hist(geo1['pore.diameter'], bins=20, alpha=0.5)
>>> fig = plt.hist(geo2['pore.diameter'], bins=20, alpha=0.5)
The resulting figure shows that these two Geometry object each have a
different pore size distribution, with geo2 being much smaller:
Notes
The table below gives a summary of the all the porescale models that are
included on this class.
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.seed 
model: 
random 


element 
pore 


num_range 
[0, 0.1] 


seed 
None 


regen_mode 
normal 
2 
pore.max_size 
model: 
largest_sphere 


iters 
10 


regen_mode 
normal 


fixed_diameter 
pore.fixed_diameter 
3 
pore.diameter 
model: 
product 


prop1 
pore.max_size 


prop2 
pore.seed 


regen_mode 
normal 
4 
pore.area 
model: 
sphere 


pore_diameter 
pore.diameter 


regen_mode 
normal 
5 
pore.volume 
model: 
sphere 


pore_diameter 
pore.diameter 


regen_mode 
normal 
6 
throat.max_size 
model: 
from_neighbor_pores 


mode 
min 


pore_prop 
pore.diameter 


regen_mode 
normal 
7 
throat.diameter 
model: 
scaled 


factor 
0.5 


prop 
throat.max_size 


regen_mode 
normal 
8 
throat.length 
model: 
piecewise 


pore_diameter 
pore.diameter 


regen_mode 
normal 
9 
throat.surface_area 
model: 
cylinder 


throat_diameter 
throat.diameter 


throat_length 
throat.length 


regen_mode 
normal 
10 
throat.volume 
model: 
cylinder 


throat_diameter 
throat.diameter 


throat_length 
throat.length 


regen_mode 
normal 
11 
throat.area 
model: 
cylinder 


throat_diameter 
throat.diameter 


regen_mode 
normal 
openpnm.phases
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 preconfigured
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 porescale 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:
>>> import openpnm as op
>>> import numpy as np
>>> pn = op.network.Cubic([5, 5, 5])
>>> oil = op.phases.GenericPhase(network=pn)
Now add the porescale model for viscosity from the models module:
>>> oil.add_model(propname='pore.viscosity',
... model=op.models.misc.polynomial,
... a=[50000, 1, .1],
... prop='pore.temperature')
Upon adding the model, values are immediately calculated at the Phaseâ€™s
current temperature:
>>> np.round(oil['pore.viscosity'][0])
41418.0
If the temperature is changed, the model can be regenerated to update the
viscosity values:
>>> oil['pore.temperature'] = 355
>>> np.round(oil['pore.viscosity'][0])
41418.0
Note that the oil viscosity has NOT changed! To propigate the new temperature
to all the other calculated porescale properties, call the
regenerate_models function:
>>> oil.regenerate_models()
>>> np.round(oil['pore.viscosity'][0])
37752.0
GenericPhase

class
openpnm.phases. GenericPhase (network=None, project=None, settings={}, **kwargs)[source]
Bases: openpnm.core.Base.Base , openpnm.core.ModelsMixin.ModelsMixin
This generic class is meant as a starter for custom Phase objects
This class produces a blankslate object with no porescale 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 to which this Phase should be attached
 name (str, optional) â€“ A unique string name to identify the Phase object, typically same as
instance name but can be anything.

Examples
Create a new empty phase:
>>> import openpnm as op
>>> pn = op.network.Cubic([10, 10, 10])
>>> phase = op.phases.GenericPhase(network=pn)
And add a model:
>>> phase.add_model(propname='pore.molar_density',
... model=op.models.phases.molar_density.ideal_gas)
Now confirm that the model was added and data was calculated. The
models attribute can be printed:
>>> print(phase.models)
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
# Property Name Parameter Value
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 pore.molar_density model: ideal_gas
pressure: pore.pressure
temperature: pore.temperature
regeneration mode: normal
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
And the Phase itself has a nice printout using print(phase) .
Air

class
openpnm.phases. Air (**kwargs)[source]
Bases: openpnm.phases.GenericPhase.GenericPhase
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.

Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> air = op.phases.Air(network=pn)
Notes
The table below shows all of the porescale 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.82082e06, 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. 2169
Water

class
openpnm.phases. Water (**kwargs)[source]
Bases: openpnm.phases.GenericPhase.GenericPhase
Creates Phase object with preset values for Water
Parameters:  network (OpenPNM Network object) â€“ The Network to which this phase object will be associated. 
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> water = op.phases.Water(network=pn)
Notes
The table below shows all of the porescale 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.density 
model: 
water 


temperature 
pore.temperature 


salinity 
pore.salinity 
2 
pore.molar_density 
model: 
standard 


mol_weight 
pore.molecular_weight 


density 
pore.density 
3 
pore.surface_tension 
model: 
water 


temperature 
pore.temperature 


salinity 
pore.salinity 
4 
pore.thermal_condâ€¦ 
model: 
water 


temperature 
pore.temperature 


salinity 
pore.salinity 
5 
pore.vapor_pressure 
model: 
antoine 


A 
8.088 


B 
1750.71 


C 
236.191 


temperature 
pore.temperature 
6 
pore.viscosity 
model: 
water 


temperature 
pore.temperature 


salinity 
pore.salinity 
Mercury

class
openpnm.phases. Mercury (name=None, **kwargs)[source]
Bases: openpnm.phases.GenericPhase.GenericPhase
Creates Phase object with a default name â€˜Hgâ€™ and preset values and
porescale models for mercury.
Parameters: 
 network (OpenPNM Network object) â€“ The network to which this phase object will be attached.
 project (OpenPNM Project object, optional) â€“ Can be supplied instead of
network

References
 [1] Thermophysical Properties of Materials for Nuclear Engineering: IAEA,
 Vienna, 2008. ISBN 9789201065087:
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> hg = op.phases.Mercury(network=pn)
Notes
The table below shows all of the porescale 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.vapor_pressure 
model: 
antoine 


A 
9.85767 


B 
3007.129 


C 
10.001 


regen_mode 
normal 


temperature 
pore.temperature 
2 
pore.density 
model: 
linear 


prop 
pore.temperature 


b 
14280.9 


m 
2.47004 


regen_mode 
normal 
3 
pore.molar_density 
model: 
standard 


regen_mode 
normal 


mol_weight 
pore.molecular_weight 


density 
pore.density 
4 
pore.surface_tension 
model: 
linear 


prop 
pore.temperature 


b 
0.56254 


m 
0.00028 


regen_mode 
normal 
5 
pore.thermal_condâ€¦ 
model: 
polynomial 


prop 
pore.temperature 


a 
[3.98691, 0.0170967, â€¦ 


regen_mode 
normal 
6 
pore.viscosity 
model: 
polynomial 


prop 
pore.temperature 


a 
[0.00355837, 1.00131â€¦ 


regen_mode 
normal 
openpnm.physics
This module contains the GenericPhysics class, along with a few preconfigured
classes that include common porescale physical models
The GenericPhysics Class
Library of Preconfigured Physics Classes
GenericPhysics

class
openpnm.physics. GenericPhysics (project=None, network=None, phase=None, geometry=None, settings={}, **kwargs)[source]
Bases: openpnm.core.Subdomain.Subdomain , openpnm.core.ModelsMixin.ModelsMixin
This generic class is meant as a starter for custom Physics objects
It produces a blank object with no porescale 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.


set_geometry (geometry)[source]

set_phase (phase, mode='add')[source]
Standard

class
openpnm.physics. Standard (project=None, network=None, phase=None, geometry=None, settings={}, **kwargs)[source]
Bases: openpnm.physics.GenericPhysics.GenericPhysics
Generic class to generate Physics objects
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.

openpnm.materials
This module provides a library of preconfigured NetworkGeometry 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. 
VoronoiFibers

class
openpnm.materials. VoronoiFibers (num_points=None, points=None, shape=[1, 1, 1], fiber_rad=None, resolution=0.01, name=None, linear_scale=None, **kwargs)[source]
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 pregenerated 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 zdirection 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, 504515
[4] T. G. Tranter et al. Transport in Porous Media, 2018, 121, 3, 597620
Examples
Points will be automatically generated if none are given:
>>> import openpnm as op
>>> ws = op.Workspace()
>>> ws.clear()
>>> prj = op.materials.VoronoiFibers(num_points=50,
... shape=[1e4, 1e4, 1e4],
... fiber_rad=5e6,
... resolution=1e6)
openpnm.algorithms
The algorithms module contains classes for conducting transport simulations
on pore networks.
GenericAlgorithm

class
openpnm.algorithms. GenericAlgorithm (network=None, project=None, settings={}, **kwargs)[source]
Bases: openpnm.core.Base.Base
Generic class to define the foundation of Algorithms.
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) â€“ Either a Network or a Project must be supplied

Notes
This class defines the following methods, which all raise a
NotImplementedError and must be defined by the various subclasses:
Methods 
Description 
results 
Generates an array or arrays of data produced by
the algorithm to be returned to the Phase 
setup 
Collects values to be placed in settings . The
main benefit is defining default values and
providing documentation on each settings 
reset 
Removes generated data, specified values, and
any other information lingering on an Algorithm 

reset ()[source]

results ()[source]

setup ()[source]
GenericTransport

class
openpnm.algorithms. GenericTransport (project=None, network=None, phase=None, settings={}, **kwargs)[source]
Bases: openpnm.algorithms.GenericAlgorithm.GenericAlgorithm
This class implements steadystate linear transport calculations
Parameters: 
 network (OpenPNM Network object) â€“ The Network with which this algorithm is associated
 project (OpenPNM Project object, optional) â€“ A Project can be specified instead of
network

Notes
The following table shows the methods that are accessible to the user
for settig 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 

_apply_BCs ()[source]
Applies all the boundary conditions that have been specified, by
adding values to the A and b matrices.

_build_A (force=False)[source]
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.
Parameters:  force (Boolean (default is False )) â€“ If set to True then the A matrix is built from new. If
False (the default), a cached version of A is returned. The
cached version is clean in the sense that no boundary conditions
or sources terms have been added to it. 

_build_b (force=False)[source]
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. 

_calc_eff_prop (inlets=None, outlets=None, domain_area=None, domain_length=None)[source]
Calculate the effective transport through the network
Parameters: 
 inlets (array_like) â€“ The pores where the inlet 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 boundary conditions were applied. If
not given an attempt is made to infer them from the algorithm.
 domain_area (scalar) â€“ The area of the inlet and/or outlet face (which shold match)
 domain_length (scalar) â€“ The length of the domain between the inlet and outlet faces

Returns: 

Return type:  The effective transport property through the network


_solve (A=None, b=None)[source]
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 nonlinear
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 (NDarray) â€“ The RHS matrix in any format. If not specified, then it uses
the
b matrix attached to the object.

Notes
The solver used here is specified in the settings attribute of the
algorithm.

rate (pores=[], throats=[], mode='group')[source]
Calculates the net rate of material moving into a given set of pores or
throats
Parameters: 
 pores (array_like) â€“ The pores for which the rate should be calculated
 throats (array_like) â€“ The throats through which the rate should be calculated
 mode (string, optional) â€“
Controls how to return the rate. Options are:
â€™groupâ€™: (default) Returns the cumulative rate of material
moving into the given set of pores
â€™singleâ€™ : Calculates the rate for each pore individually

Returns: 
 If
pores are specified, then the returned values indicate the
 net rate of material exiting the pore or pores. Thus a positive
 rate indicates material is leaving the pores, and negative values
 mean material is entering.
 If
throats are specified the rate is calculated in the direction of
 the gradient, thus is always positive.
 If
mode is â€˜singleâ€™ then the cumulative rate through the given
 pores (or throats) are returned as a vector, if
mode is â€˜groupâ€™
 then the individual rates are summed and returned as a scalar.


results ()[source]
Fetches the calculated quantity from the algorithm and returns it as
an array.

set_rate_BC (pores, values, mode='merge')[source]
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.
Notes
The definition of quantity is specified in the algorithmâ€™s
settings , e.g. alg.settings['quentity'] = 'pore.pressure' .

set_value_BC (pores, values, mode='merge')[source]
Apply constant value boundary conditons to the specified locations.
These are sometimes referred to as Dirichlet conditions.
Notes
The definition of quantity is specified in the algorithmâ€™s
settings , e.g. alg.settings['quentity'] = 'pore.pressure' .

setup (phase=None, quantity='', conductance='', **kwargs)[source]
This method takes several arguments that are essential to running the
algorithm and adds them to the settings.
Parameters: 
 phase (OpenPNM Phase object) â€“ The phase on which the algorithm is to be run.
 quantity (string) â€“ The name of the physical quantity to be calculated.
 conductance (string) â€“ The name of the porescale transport conductance values. These
are typically calculated by a model attached to a Physics object
associated with the given Phase.
 solver (string) â€“ To use the default scipy solver, set this value to spsolve or
umfpack. To use an iterative solver or a nonscipy solver,
additional arguments are required as described next.
 solver_family (string) â€“ The solver package to use. OpenPNM currently supports
scipy ,
pyamg and petsc (if you have it installed). The default is
scipy .
 solver_type (string) â€“ The specific solver to use. For instance, if
solver_family is
scipy then you can specify any of the iterative solvers such as
cg or gmres . [More info here]
(https://docs.scipy.org/doc/scipy/reference/sparse.linalg.html)
 solver_preconditioner (string) â€“ This is used by the PETSc solver to specify which preconditioner
to use. The default is
jacobi .
 solver_atol (scalar) â€“ Used to control the accuracy to which the iterative solver aims.
The default is 1e6.
 solver_rtol (scalar) â€“ Used by PETSc as an additional tolerance control. The default is
1e6.
 solver_maxiter (scalar) â€“ Limits the number of iterations to attempt before quiting when
aiming for the specified tolerance. The default is 5000.

ReactiveTransport

class
openpnm.algorithms. ReactiveTransport (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.GenericTransport.GenericTransport
A subclass for steadystate simulations with (optionally) source terms
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 simulations of transport phenomena with
reactions when source terms are added.

run (x=None)[source]
Builds the A and b matrices, and calls the solver specified in the
settings attribute.
Parameters:  x (NDarray) â€“ Initial guess of unknown variable 

set_source (propname, pores)[source]
Applies a given source term to the specified pores
Parameters: 
 propname (string) â€“ The property name of the source term model to be applied
 pores (array_like) â€“ The pore indices where the source term should be applied

Notes
Source terms cannot be applied in pores where boundary conditions have
already been set. Attempting to do so will result in an error being
raised.

setup (phase=None, quantity='', conductance='', rxn_tolerance=None, max_iter=None, relaxation_source=None, relaxation_quantity=None, **kwargs)[source]
This method takes several arguments that are essential to running the
algorithm and adds them to the settings
Parameters: 
 phase (OpenPNM Phase object) â€“ The phase on which the algorithm is to be run. If no value is
given, the existing value is kept.
 quantity (string) â€“ The name of the physical quantity to be calcualted such as
'pore.xxx' .
 conductance (string) â€“ The name of the porescale transport conductance values. These
are typically calculated by a model attached to a Physics object
associated with the given Phase. Example;
'throat.yyy' .
 rxn_tolerance (scalar) â€“ Tolerance to achieve. The solver returns a solution when â€˜residualâ€™
falls below â€˜rxn_toleranceâ€™. The default value is 1e05.
 max_iter (scalar) â€“ The maximum number of iterations the solver can perform to find
a solution. The default value is 5000.
 relaxation_source (scalar, between 0 and 1) â€“ A relaxation factor to control underrelaxation of the source term.
Factor approaching 0 : improved stability but slow simulation.
Factor approaching 1 : fast simulation but may be unstable.
Default value is 1 (no underrelaxation).
 relaxation_quantity (scalar, between 0 and 1) â€“ A relaxation factor to control underrelaxation for the quantity
solving for.
Factor approaching 0 : improved stability but slow simulation.
Factor approaching 1 : fast simulation but may be unstable.
Default value is 1 (no underrelaxation).

Notes
Underrelaxation is a technique used for improving stability of a
computation, particularly in the presence of highly nonlinear terms.
Underrelaxation 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.
TransientReactiveTransport

class
openpnm.algorithms. TransientReactiveTransport (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.ReactiveTransport.ReactiveTransport
A subclass of ReactiveTransport for transient/steadystate 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 steadystate simulation, and
â€˜implicitâ€™ (fast, 1st order accurate) and â€˜cranknicolsonâ€™ (slow, 2nd order
accurate) both for transient simulations.

results (times=None, **kwargs)[source]
Fetches the calculated quantity from the algorithm and returns it as
an array.
Parameters: 
 times (scalar, NDarray, 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 NDarray 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.

run (t=None)[source]
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. 

set_IC (values)[source]
A method to set simulation initial conditions
Parameters:  values (NDarray 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. 

setup (phase=None, quantity='', conductance='', t_initial=None, t_final=None, t_step=None, t_output=None, t_tolerance=None, t_precision=None, t_scheme='', **kwargs)[source]
This method takes several arguments that are essential to running the
algorithm and adds them to the settings
Parameters: 
 phase (OpenPNM Phase object) â€“ The phase on which the algorithm is to be run. If no value is
given, the existing value is kept.
 quantity (string) â€“ The name of the physical quantity to be calcualted such as
'pore.xxx' .
 conductance (string) â€“ The name of the porescale transport conductance values. These
are typically calculated by a model attached to a Physics object
associated with the given Phase. Example;
'throat.yyy' .
 t_initial (scalar, smaller than 't_final') â€“ The simulationâ€™s start time. The default value is 0.
 t_final (scalar, bigger than 't_initial') â€“ The simulationâ€™s end time. The default value is 10.
 t_step (scalar, between 't_initial' and 't_final') â€“ The simulationâ€™s time step. The default value is 0.1.
 t_output (scalar, NDarray, or list) â€“ When â€˜t_outputâ€™ is a scalar, it is considered as an output interval
to store transient solutions. The default value is 1e+08. Initial,
final and steadystate (if reached) fields are always stored. If
â€˜t_outputâ€™ > â€˜t_finalâ€™, no transient data is stored. If â€˜t_outputâ€™
is not a multiple of â€˜t_stepâ€™, â€˜t_outputâ€™ will be approximated.
When â€˜t_outputâ€™ is a list or NDarray, transient solutions
corresponding to this list or array will be stored.
 output_times (list) â€“ List of output times. The values in the list must be multiples of
the time step â€˜t_stepâ€™.
 t_tolerance (scalar) â€“ Transient solver tolerance. The simulation stops (before reaching
â€˜t_finalâ€™) when the residual falls below â€˜t_toleranceâ€™. The
default value is 1e06. The â€˜residualâ€™ measures the variation from
one timestep to another in the value of the â€˜quantityâ€™ solved for.
 rxn_tolerance (scalar) â€“ Tolerance to achieve within each time step. The solver passes to
next time step when â€˜residualâ€™ falls below â€˜rxn_toleranceâ€™. The
default value is 1e05.
 t_precision (integer) â€“ The time precision (number of decimal places).
 t_scheme (string) â€“ The time discretization scheme. Three options available: â€˜steadyâ€™
to perform a steadystate simulation, and â€˜implicitâ€™ (fast, 1st
order accurate) and â€˜cranknicolsonâ€™ (slow, 2nd order accurate) both
for transient simulations. The default value is â€˜implicitâ€™.

Notes
More settings can be adjusted in the presence of a nonlinear source
term such as underrelaxation.
See the â€˜ReactiveTransportâ€™ class documentation for details.
OrdinaryPercolation

class
openpnm.algorithms. OrdinaryPercolation (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.GenericAlgorithm.GenericAlgorithm
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.
Method 
Description 
reset 
Resets the various data arrays on the objectâ€¦ 
setup 
Used to specify necessary arguments to the sâ€¦ 
set_inlets 
Set the locations from which the invader entâ€¦ 
set_outlets 
Set the locations through which defender exiâ€¦ 
set_residual 
Specify locations of any residual invader. â€¦ 
run 
Runs the percolation algorithm to determine â€¦ 
get_percolation_tâ€¦ 
Finds the threshold value where a percolatingâ€¦ 
is_percolating 
Returns a True or False value to indicate ifâ€¦ 
get_intrusion_data 
Obtain the numerical values of the calculateâ€¦ 
plot_intrusion_curve 
Plot the percolation curve as the invader voâ€¦ 

get_intrusion_data (Pc=None)[source]
Obtain the numerical values of the calculated intrusion curve
Returns: 
 A namedtuple containing arrays of applied capillary pressures and
 invading phase saturation.


get_percolation_threshold ()[source]
Find the invasion threshold at which a cluster spans from the inlet to
the outlet sites

is_percolating (applied_pressure)[source]
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. 

plot_intrusion_curve (fig=None)[source]
Plot the percolation curve as the invader volume or number fraction vs
the applied capillary pressure.

reset ()[source]
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.

results (Pc=None)[source]
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*
 or algorithms.


run (points=25, start=None, stop=None)[source]
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 nonzero 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_inlets (pores=[], overwrite=False)[source]
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_outlets (pores=[], overwrite=False)[source]
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.


set_residual (pores=[], throats=[], overwrite=False)[source]
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.


setup (phase=None, access_limited=None, mode='', throat_entry_pressure='', pore_entry_pressure='', pore_volume='', throat_volume='')[source]
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.

Porosimetry

class
openpnm.algorithms. Porosimetry (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.OrdinaryPercolation.OrdinaryPercolation
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.

results (Pc=None)[source]

run (points=25, start=None, stop=None)[source]
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 nonzero 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_partial_filling (propname)[source]
Define which pore filling model to apply.
Parameters:  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 porescale
model(s).

setup (phase=None, quantity='', throat_entry_pressure='', pore_volume='', throat_volume='', late_pore_filling='', late_throat_filling='')[source]
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.

InvasionPercolation

class
openpnm.algorithms. InvasionPercolation (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.GenericAlgorithm.GenericAlgorithm
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.
Examples
Start by importing the usual packages:
>>> import openpnm as op
>>> import scipy as sp
>>> import matplotlib.pyplot as plt
Create 2D cubic network for easier visualizaiton:
>>> S = sp.array([100, 100, 1])
>>> pn = op.network.Cubic(shape=S, spacing=0.0001, name='pn11')
Add a basic geometry:
>>> geom = op.geometry.StickAndBall(network=pn, pores=pn.Ps, throats=pn.Ts)
Create an invading phase, and attach the capillary pressure model:
>>> water = op.phases.Water(network=pn)
>>> water.add_model(propname='throat.entry_pressure',
... model=op.models.physics.capillary_pressure.washburn)
Initialize an invasion percolation object and define inlets:
>>> ip = op.algorithms.InvasionPercolation(network=pn)
>>> ip.setup(phase=water)
>>> ip.set_inlets(pores=0)
>>> ip.run()
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.
plt.subplot(1, 2, 1)
plt.imshow(sp.reshape(ip['pore.invasion_sequence'], newshape=S[S > 1]))
plt.subplot(1, 2, 2)
plt.imshow(sp.reshape(water['pore.occupancy'], newshape=S[S > 1]))

apply_trapping (outlets)[source]
Apply trapping based on algorithm described by Y. Masson [1].
It is applied as a postprocess 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 twostep algorithm for invasion
percolation with trapping. Computers & Geosciences, 90, pp.4148
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â€™


results (Snwp=None)[source]
Returns the phase configuration at the specified nonwetting phase
(invading phase) saturation.
Parameters:  Snwp (scalar, between 0 and 1) â€“ The network saturation for which the phase configuration is
desired. 
Returns: 
 Two dictionary containing arrays that describe the pore and throat
 distribution at the given saturation. Specifically, these are
 **â€™pore.occupancyâ€™** (1 indicates the pores is invaded and 0)
 otherwise.
 **â€™throat.occupancyâ€™** (Same as described above but for throats.)


run (n_steps=None)[source]
Perform the algorithm
Parameters:  n_steps (int) â€“ The number of throats to invaded during this step 

set_inlets (pores=[], overwrite=False)[source]
Parameters:  pores (array_like) â€“ The list of inlet pores from which the Phase can enter the Network 

setup (phase, entry_pressure='', pore_volume='', throat_volume='')[source]
Set up the required parameters for the algorithm
Parameters: 
 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â€™.

MixedInvasionPercolation

class
openpnm.algorithms. MixedInvasionPercolation (settings={}, **kwargs)[source]
Bases: openpnm.algorithms.GenericAlgorithm.GenericAlgorithm
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. 
Notes
n/a

apply_flow (flowrate)[source]
Convert the invaded sequence into an invaded time for a given flow rate
considering the volume of invaded pores and throats.
Parameters:  flowrate (float) â€“ The flow rate of the injected fluid 
Returns: 
 Creates a throat array called â€˜invasion_timeâ€™ in the Algorithm
 dictionary


apply_trapping (partial=False)[source]
Apply trapping based on algorithm described by Y. Masson [1].
Parameters:  partial (boolean) â€“ Indicating whether partially filled network 
Notes
It is applied as a postprocess 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 twostep algorithm for invasion
percolation with trapping. Computers & Geosciences, 90, pp.4148
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â€™


get_intrusion_data (inv_points=None)[source]
Plot a simple drainage curve

plot_intrusion_curve (fig=None, inv_points=None)[source]
Plot a simple drainage curve

reset ()[source]
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.

results (Pc)[source]
Places the results of the IP simulation into the Phase object.
Parameters:  Pc (float) â€“ Capillary Pressure at which phase configuration was reached 

run (max_pressure=None)[source]
Perform the algorithm
Parameters:  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_inlets (pores=None, clusters=None)[source]
Parameters: 
 pores (array_like) â€“ The list of inlet pores from which the Phase can enter the Network
 clusters (list of lists  can be just one list but each list defines) â€“ a cluster of pores that share a common invasion pressure.
 Basic Invasion Percolation a queue of (Like) â€“


set_outlets (pores=[], overwrite=False)[source]
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.


set_residual (pores=[], overwrite=False)[source]
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

setup (phase=None, pore_entry_pressure='pore.entry_pressure', throat_entry_pressure='throat.entry_pressure', snap_off='', invade_isolated_Ts=False, late_pore_filling='', late_throat_filling='', cooperative_pore_filling='')[source]
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 snapoff
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.
 cooperative_pore_filling (string) â€“ The name of the model used to determine the meniscus properties
required for assessing cooperative pore filling.


setup_coop_filling (inv_points=None)[source]
Evaluate the cooperative pore filling condition that the combined
filling angle in next neighbor throats cannot exceed the geometric
angle between their throat planes.
This is used when the invading fluid has access to multiple throats
connected to a pore
Parameters:  inv_points (array_like) â€“ The invasion pressures at which to assess coopertive pore filling. 

trilaterate_v (P1, P2, P3, r1, r2, r3)[source]
Find whether 3 spheres intersect
FickianDiffusion

class
openpnm.algorithms. FickianDiffusion (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.ReactiveTransport.ReactiveTransport
A class to simulate binary diffusion.
Parameters: 
 network (OpenPNM Network object) â€“ The network on which this algorithm operates
 project (OpenPNM Project object) â€“ Either a network or a project must be specified
 name (string, optional) â€“ A unique name to give the object for easier identification. If not
given, one is generated.

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 diffusionreaction
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.

calc_effective_diffusivity (inlets=None, outlets=None, domain_area=None, domain_length=None)[source]
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.

setup (phase=None, quantity='', conductance='', **kwargs)[source]
This method takes several arguments that are essential to running the
algorithm and adds them to the settings.
Parameters: 
 phase (OpenPNM Phase object) â€“ The phase on which the algorithm is to be run.
 quantity (string) â€“ (default is
'pore.mole_fraction' ) The name of the physical
quantity to be calculated.
 conductance (string) â€“ (default is
'throat.diffusive_conductance' ) The name of the
porescale transport conductance values. These are typically
calculated by a model attached to a Physics object associated
with the given Phase.

Notes
Any additional arguments are added to the settings dictionary of
the object.
FourierConduction

class
openpnm.algorithms. FourierConduction (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.ReactiveTransport.ReactiveTransport
A subclass of GenericLinearTransport to simulate heat 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.

calc_effective_conductivity (inlets=None, outlets=None, domain_area=None, domain_length=None)[source]
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.

setup (phase=None, quantity='', conductance='', **kwargs)[source]
This method takes several arguments that are essential to running the
algorithm and adds them to the settings.
Parameters: 
 phase (OpenPNM Phase object) â€“ The phase on which the algorithm is to be run. If no value is
given, the existing value is kept.
 quantity (string) â€“ The name of the physical quantity to be calcualted. If no value is
given, the existing value is kept. The default value is
'pore.temperature' .
 conductance (string) â€“ The name of the porescale transport conductance values. These
are typically calculate by a model attached to a Physics object
associated with the given Phase. If no value is given, the
existing value is kept. The default value is
'throat.thermal_conductance' .

Notes
Any additional arguments are added to the settings dictionary of
the object.
OhmicConduction

class
openpnm.algorithms. OhmicConduction (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.ReactiveTransport.ReactiveTransport
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.

calc_effective_conductivity (inlets=None, outlets=None, domain_area=None, domain_length=None)[source]
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.

setup (phase=None, quantity='', conductance='', **kwargs)[source]
This method takes several arguments that are essential to running the
algorithm and adds them to the settings.
Parameters: 
 phase (OpenPNM Phase object) â€“ The phase on which the algorithm is to be run. If no value is
given, the existing value is kept.
 quantity (string) â€“ The name of the physical quantity to be calcualted. If no value is
given, the existing value is kept. The default value is
'pore.voltage' .
 conductance (string) â€“ The name of the porescale transport conductance values. These
are typically calculate by a model attached to a Physics object
associated with the given Phase. If no value is given, the
existing value is kept. The default value is
'throat.electrical_conductance' .

Notes
Any additional arguments are added to the settings dictionary of
the object.
StokesFlow

class
openpnm.algorithms. StokesFlow (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.ReactiveTransport.ReactiveTransport
A subclass of GenericLinearTransport to simulate viscous flow. The 2
main roles of this subclass are to set the default property names and to
implement a method for calculating the hydraulic permeability of the
network.

calc_effective_permeability (inlets=None, outlets=None, domain_area=None, domain_length=None)[source]
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.

setup (phase=None, quantity='', conductance='', **kwargs)[source]
This method takes several arguments that are essential to running the
algorithm and adds them to the settings.
Parameters: 
 phase (OpenPNM Phase object) â€“ The phase on which the algorithm is to be run. If no value is
given, the existing value is kept.
 quantity (string) â€“ The name of the physical quantity to be calcualted. If no value is
given, the existing value is kept. The default value is
'pore.pressure' .
 conductance (string) â€“ The name of the porescale transport conductance values. These
are typically calculate by a model attached to a Physics object
associated with the given Phase. If no value is given, the
existing value is kept. The default value is
'throat.hydraulic_conductance' .

Notes
Any additional arguments are added to the settings dictionary of
the object.
AdvectionDiffusion

class
openpnm.algorithms. AdvectionDiffusion (settings={}, phase=None, **kwargs)[source]
Bases: openpnm.algorithms.ReactiveTransport.ReactiveTransport
A subclass of GenericTransport to simulate advectiondiffusion

rate (pores=[], throats=[], mode='group')[source]
Calculates the net rate of material moving into a given set of pores or
throats
Parameters: 
 pores (array_like) â€“ The pores for which the rate should be calculated
 throats (array_like) â€“ The throats through which the rate should be calculated
 mode (string, optional) â€“
Controls how to return the rate. Options are:
â€™groupâ€™: (default) Returns the cumulative rate of material
moving into the given set of pores
â€™singleâ€™ : Calculates the rate for each pore individually

Returns: 
 If
pores are specified, then the returned values indicate the
 net rate of material exiting the pore or pores. Thus a positive
 rate indicates material is leaving the pores, and negative values
 mean material is entering.
 If
throats are specified the rate is calculated in the direction of
 the gradient, thus is always positive.
 If
mode is â€˜singleâ€™ then the cumulative rate through the given
 pores (or throats) are returned as a vector, if
mode is â€˜groupâ€™
 then the individual rates are summed and returned as a scalar.


set_outflow_BC (pores, mode='merge')[source]
Adds outflow boundary condition to the selected pores.
Outflow condition simply means that the gradient of the solved
quantity does not change, i.e. is 0.

setup (phase=None, quantity='', conductance='', diffusive_conductance='', hydraulic_conductance='', pressure='', s_scheme='', **kwargs)[source]
openpnm.models
This module contains a selection of porescale models for calculating
geometrical, thermophysical, and multiphysics transport properties.
Using Prewritten Models from the Library
OpenPNM includes a solid library of prewritten 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:
>>> import openpnm as op
>>> pn = op.network.Cubic([5, 5, 5])
>>> pn.add_model(propname='pore.seed',
... model=op.models.misc.random,
... element='pore',
... num_range=[0.1, 0.9])
>>> pn.add_model(propname='pore.diameter',
... model=op.models.geometry.pore_size.normal,
... seeds='pore.seed',
... loc=0.5, scale=0.1)
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.
>>> print(pn.props())
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 : pore.coords
2 : pore.diameter
3 : pore.seed
4 : throat.conns
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
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:
>>> print(pn.models)
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
# Property Name Parameter Value
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
1 pore.seed model: random
element: pore
num_range: [0.1, 0.9]
seed: None
regeneration mode: normal
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
2 pore.diameter model: normal
seeds: pore.seed
loc: 0.5
scale: 0.1
regeneration mode: normal
â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•â€•
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 (seeds before
diameters):
>>> pn.regenerate_models()
openpnm.models.misc
This submodule contains models for calculating general or generic values
that are useful for all other porescale models, such as scaling values, or
generating an array of random numbers.
Basic Math and Calculations

openpnm.models.misc.basic_math. constant (target, value)[source]
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.

openpnm.models.misc.basic_math. product (target, prop1, prop2, **kwargs)[source]
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' ).

openpnm.models.misc.basic_math. scaled (target, prop, factor)[source]
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 .

Return type:  NumPy ndarray


openpnm.models.misc.basic_math. clip (target, prop, xmax, xmin=0)[source]
Clips the given array within 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.
 xmin (float) â€“ Values below this limit will be replaced with
xmin .
 xmax (float) â€“ Values above this limit will be replaced with
xmax .


openpnm.models.misc.basic_math. normalize (target, prop, xmin=0, xmax=1)[source]
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.
 xmin (float) â€“ Lower limit of the rescaled data
 xmax (float) â€“ Upper limit of the rescaled data

Simple Equations
â€œ

openpnm.models.misc.simple_equations. generic_function (target, prop, func, **kwargs)[source]
Runs an arbitrary function on the given data
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:
>>> import openpnm as op
>>> import numpy as np
>>> pn = op.network.Cubic(shape=[5, 5, 5])
>>> geo = op.geometry.GenericGeometry(network=pn, pores=pn.Ps, throats=pn.Ts)
>>> geo['pore.rand'] = np.random.rand(geo.Np)
>>> geo.add_model(propname='pore.cos',
... model=op.models.misc.generic_function,
... func=np.cos,
... prop='pore.rand')

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.

Returns:  value â€“ Array containing m * target[prop] + b .

Return type:  NumPy ndarray

Statistical Distributions

openpnm.models.misc.statistical_distributions. random (target, element, seed=None, num_range=[0, 1])[source]
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 uniformlydistributed random numbers.

Return type:  NumPy ndarray


openpnm.models.misc.statistical_distributions. weibull (target, seeds, shape, scale, loc)[source]
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.
>>> import scipy
>>> func = scipy.stats.weibull_min(c=1.5, scale=0.0001, loc=0)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(func.ppf(q=scipy.rand(10000)), bins=50)

openpnm.models.misc.statistical_distributions. normal (target, seeds, scale, loc)[source]
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â€™.
>>> import scipy
>>> func = scipy.stats.norm(scale=.0001, loc=0.001)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(func.ppf(q=scipy.rand(10000)), bins=50)

openpnm.models.misc.statistical_distributions. generic_distribution (target, seeds, func)[source]
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 prespecified.

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:
>>> import scipy
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[3, 3, 3])
>>> geo = op.geometry.GenericGeometry(network=pn, pores=pn.Ps, throats=pn.Ts)
>>> geo.add_model(propname='pore.seed',
... model=op.models.geometry.pore_seed.random)
Now retrieve the stats distribution and add to geo as a model:
>>> stats_obj = scipy.stats.weibull_min(c=2, scale=.0001, loc=0)
>>> geo.add_model(propname='pore.size',
... model=op.models.geometry.pore_size.generic_distribution,
... seeds='pore.seed',
... func=stats_obj)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(stats_obj.ppf(q=scipy.rand(1000)), bins=50)
Neighbor Lookups

openpnm.models.misc.neighbor_lookups. from_neighbor_throats (target, throat_prop='throat.seed', mode='min')[source]
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.
 throat_prop (string) â€“ The dictionary key of the array containing the throat property to be
used in the calculation. The default is â€˜throat.seedâ€™.
 mode (string) â€“ Controls how the pore property is calculated. Options are â€˜minâ€™,
â€˜maxâ€™ and â€˜meanâ€™.

Returns:  value â€“ Array containing customized values based on those of adjacent throats.

Return type:  NumPy ndarray


openpnm.models.misc.neighbor_lookups. from_neighbor_pores (target, pore_prop='pore.seed', mode='min')[source]
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.
 pore_prop (string) â€“ The dictionary key to the array containing the pore property to be
used in the calculation. Default is â€˜pore.seedâ€™.
 mode (string) â€“ Controls how the throat property is calculated. Options are â€˜minâ€™,
â€˜maxâ€™ and â€˜meanâ€™.

Returns:  value â€“ Array containing customized values based on those of adjacent pores.

Return type:  NumPy ndarray

openpnm.models.geometry
This submodule contains porescale models that calculate geometrical properties
Pore Size
The poresize models in this submodule are used to apply desired poresize
distributions to a pore network. Most of the models accept pore seeds, and
then lookup poresizes from cumuative distribtions functions. There is also
a model for finding the largest possible sphere that can be placed on each
site.

openpnm.models.geometry.pore_size. weibull (target, shape, scale, loc, seeds='pore.seed')[source]
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.
>>> import scipy
>>> func = scipy.stats.weibull_min(c=1.5, scale=0.0001, loc=0)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(func.ppf(q=scipy.rand(10000)), bins=50)

openpnm.models.geometry.pore_size. normal (target, scale, loc, seeds='pore.seed')[source]
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â€™.
>>> import scipy
>>> func = scipy.stats.norm(scale=.0001, loc=0.001)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(func.ppf(q=scipy.rand(10000)), bins=50)

openpnm.models.geometry.pore_size. random (target, seed=None, num_range=[0, 1])[source]
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 uniformlydistributed random numbers.

Return type:  NumPy ndarray


openpnm.models.geometry.pore_size. generic_distribution (target, func, seeds='pore.seed')[source]
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 prespecified.

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:
>>> import scipy
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[3, 3, 3])
>>> geo = op.geometry.GenericGeometry(network=pn, pores=pn.Ps, throats=pn.Ts)
>>> geo.add_model(propname='pore.seed',
... model=op.models.geometry.pore_seed.random)
Now retrieve the stats distribution and add to geo as a model:
>>> stats_obj = scipy.stats.weibull_min(c=2, scale=.0001, loc=0)
>>> geo.add_model(propname='pore.size',
... model=op.models.geometry.pore_size.generic_distribution,
... seeds='pore.seed',
... func=stats_obj)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(stats_obj.ppf(q=scipy.rand(1000)), bins=50)

openpnm.models.geometry.pore_size. largest_sphere (target, fixed_diameter='pore.fixed_diameter', iters=5)[source]
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 halfway 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.

openpnm.models.geometry.pore_size. equivalent_diameter (target, pore_volume='pore.volume', pore_shape='sphere')[source]
Calculates the diameter of a sphere or edgelength 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 backcalculating from
volume. Options are â€˜sphereâ€™ (default) or â€˜cubeâ€™.

Returns:  D â€“ Array containing pore diameter values.

Return type:  NumPy ndarray

Pore Volume
These models calculate pore volumes depending on the specified shape

openpnm.models.geometry.pore_volume. sphere (target, pore_diameter='pore.diameter')[source]
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

Returns:  value â€“ Array containing pore volume values.

Return type:  NumPy ndarray


openpnm.models.geometry.pore_volume. cube (target, pore_diameter='pore.diameter')[source]
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

Returns:  value â€“ Array containing pore volume values.

Return type:  NumPy ndarray

Pore Seed
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.

openpnm.models.geometry.pore_seed. random (target, seed=None, num_range=[0, 1])[source]
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 uniformlydistributed random numbers.

Return type:  NumPy ndarray


openpnm.models.geometry.pore_seed. spatially_correlated (target, weights=None, strel=None)[source]
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 zdirection:

 :param :::
 strel = sp.array([[[0, 0, 0], [0, 0, 0], [0, 0, 0]],
 [[0, 0, 0], [1, 1, 1], [0, 0, 0]],
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]])
Returns:  values â€“ Array containing pore seed values. 
Return type:  NumPy ndarray 
Notes
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 to create an anistropic
gas diffusion layer for fuel cell electrodes.
References
Examples
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[10, 10, 10])
>>> Ps, Ts = pn.Ps, pn.Ts
>>> geom = op.geometry.GenericGeometry(network=pn, pores=Ps, throats=Ts)
>>> mod = op.models.geometry.pore_seed.spatially_correlated
>>> geom.add_model(propname='pore.seed', model=mod, weights=[2, 2, 2])
Pore Surface Area

openpnm.models.geometry.pore_surface_area. sphere (target, pore_diameter='pore.diameter', throat_area='throat.area')[source]
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 crosssectional 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.

Return type:  NumPy ndarray


openpnm.models.geometry.pore_surface_area. cube (target, pore_diameter='pore.diameter', throat_area='throat.area')[source]
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.

Return type:  NumPy ndarray

Throat Area

openpnm.models.geometry.throat_area. cylinder (target, throat_diameter='throat.diameter')[source]
Calculate throat crosssectional 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 crosssectional area values.

Return type:  NumPy ndarray


openpnm.models.geometry.throat_area. cuboid (target, throat_diameter='throat.diameter')[source]
Calculate throat crosssectional 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 crosssectional area values.

Return type:  NumPy ndarray

Throat Size

openpnm.models.geometry.throat_size. weibull (target, shape, scale, loc, seeds='throat.seed')[source]
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.
>>> import scipy
>>> func = scipy.stats.weibull_min(c=1.5, scale=0.0001, loc=0)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(func.ppf(q=scipy.rand(10000)), bins=50)

openpnm.models.geometry.throat_size. normal (target, scale, loc, seeds='throat.seed')[source]
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â€™.
>>> import scipy
>>> func = scipy.stats.norm(scale=.0001, loc=0.001)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(func.ppf(q=scipy.rand(10000)), bins=50)

openpnm.models.geometry.throat_size. random (target, seed=None, num_range=[0, 1])[source]
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 uniformlydistributed random numbers.

Return type:  NumPy ndarray


openpnm.models.geometry.throat_size. generic_distribution (target, func, seeds='throat.seed')[source]
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 prespecified.

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:
>>> import scipy
>>> import openpnm as op
>>> pn = op.network.Cubic(shape=[3, 3, 3])
>>> geo = op.geometry.GenericGeometry(network=pn, pores=pn.Ps, throats=pn.Ts)
>>> geo.add_model(propname='pore.seed',
... model=op.models.geometry.pore_seed.random)
Now retrieve the stats distribution and add to geo as a model:
>>> stats_obj = scipy.stats.weibull_min(c=2, scale=.0001, loc=0)
>>> geo.add_model(propname='pore.size',
... model=op.models.geometry.pore_size.generic_distribution,
... seeds='pore.seed',
... func=stats_obj)
>>> import matplotlib.pyplot as plt
>>> fig = plt.hist(stats_obj.ppf(q=scipy.rand(1000)), bins=50)

openpnm.models.geometry.throat_size. from_neighbor_pores (target, pore_prop='pore.diameter', mode='min')[source]
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.
 pore_prop (string) â€“ The dictionary key to the array containing the pore property to be
used in the calculation. Default is â€˜pore.seedâ€™.
 mode (string) â€“ Controls how the throat property is calculated. Options are â€˜minâ€™,
â€˜maxâ€™ and â€˜meanâ€™.

Returns:  value â€“ Array containing customized values based on those of adjacent pores.

Return type:  NumPy ndarray


openpnm.models.geometry.throat_size. equivalent_diameter (target, throat_area='throat.area', throat_shape='circle')[source]
Calculates the diameter of a cirlce or edgelength 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 crosssectional shape of the throat to assume when
backcalculating from the area. Options are â€˜circleâ€™ (default) or
â€˜squareâ€™.

Returns:  value â€“ Array containing throat equivalent diameter.

Return type:  NumPy ndarray

Throat Length

openpnm.models.geometry.throat_length. piecewise (target, throat_endpoints='throat.endpoints', throat_centroid='throat.centroid')[source]
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.

openpnm.models.geometry.throat_length. ctc (target)[source]
Calculate throat length assuming pointlike pores, i.e. centertocenter
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. 
Returns:  value â€“ Array containing throat length values. 
Return type:  NumPy ndarray 

openpnm.models.geometry.throat_length. conduit_lengths (target, throat_endpoints='throat.endpoints', throat_length='throat.length')[source]
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
 keys â€˜pore1â€™, â€˜pore2â€™, and â€˜throatâ€™.

Throat Perimeter

openpnm.models.geometry.throat_perimeter. cylinder (target, throat_diameter='throat.diameter')[source]
Calcuate the throat perimeter assuming a circular crosssection
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

Returns:  value â€“ Array containing throat perimeter values.

Return type:  NumPy ndarray


openpnm.models.geometry.throat_perimeter. cuboid (target, throat_diameter='throat.diameter')[source]
Calcuate the throat perimeter assuming a square crosssection
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.

Returns:  value â€“ Array containing throat perimeter values.

Return type:  NumPy ndarray

Throat Surface Area

openpnm.models.geometry.throat_surface_area. cylinder (target, throat_diameter='throat.diameter', throat_length='throat.length')[source]
Calculate surface 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 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.

Return type:  NumPy ndarray


openpnm.models.geometry.throat_surface_area. cuboid (target, throat_diameter='throat.diameter', throat_length='throat.length')[source]
Calculate surface 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 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.

Return type:  NumPy ndarray


openpnm.models.geometry.throat_surface_area. extrusion (target, throat_perimeter='throat.perimeter', throat_length='throat.length')[source]
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.

Return type:  NumPy ndarray

Throat Vector

openpnm.models.geometry.throat_vector. pore_to_pore (target)[source]
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 poretopore 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.
Throat Volume

openpnm.models.geometry.throat_volume. cylinder (target, throat_length='throat.length', throat_diameter='throat.diameter')[source]
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.

openpnm.models.geometry.throat_volume. cuboid (target, throat_length='throat.length', throat_diameter='throat.diameter')[source]
Calculate throat volume assuing a square crosssection
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.

openpnm.models.geometry.throat_volume. extrusion (target, throat_length='throat.length', throat_area='throat.area')[source]
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.
Throat Shape Factor

openpnm.models.geometry.throat_shape_factor. compactness (target, throat_perimeter='throat.perimeter', throat_area='throat.area')[source]
Mortensen et al. have shown that the HagenPoiseuille 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.

Returns:  alpha â€“ Array containing throat compactness values.

Return type:  NumPy ndarray

References
Mortensen N.A, Okkels F., and Bruus H. Reexamination of HagenPoiseuille
flow: Shape dependence of the hydraulic resistance in microchannels.
Physical Review E, v.71, pp.057301 (2005).

openpnm.models.geometry.throat_shape_factor. mason_morrow (target, throat_perimeter='throat.perimeter', throat_area='throat.area')[source]
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.262274 (1991).

openpnm.models.geometry.throat_shape_factor. jenkins_rao (target, throat_perimeter='throat.perimeter', throat_area='throat.area', throat_diameter='throat.indiameter')[source]
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.177180. (1984)
openpnm.models.phases
This submodule contains models for calculating thermophysical properties of
liquids, gases, and solids.
Density

openpnm.models.phases.density. water (target, temperature='pore.temperature', salinity='pore.salinity')[source]
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.

openpnm.models.phases.density. standard (target, mol_weight='pore.molecular_weight', molar_density='pore.molar_density')[source]
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)

Returns:  value â€“ Array containing density values (kg/m3)

Return type:  NumPy ndarray


openpnm.models.phases.density. ideal_gas (target, pressure='pore.pressure', temperature='pore.temperature', mol_weight='pore.molecular_weight')[source]
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]

Return type:  NumPy ndarray

Diffusivity

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].

Return type:  NumPy ndarray

Electrical Conductivity

openpnm.models.phases.electrical_conductivity. percolating_continua (target, phi_crit, tau, volume_fraction='pore.volume_fraction', bulk_property='pore.intrinsic_conductivity')[source]
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

Returns:  sigma_eff â€“ Array containing effective electrical conductivity values.

Return type:  NumPy ndarray

Notes
This model uses the following standard percolation relationship:
\[\sigma_{effective}=\sigma_{bulk}(\phi  \phi_{critical})^\lambda\]
Molar Density

openpnm.models.phases.molar_density. standard (target, mol_weight='pore.molecular_weight', density='pore.density')[source]
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]

Return type:  NumPy ndarray


openpnm.models.phases.molar_density. ideal_gas (target, pressure='pore.pressure', temperature='pore.temperature')[source]
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.

openpnm.models.phases.molar_density. vanderwaals (target, pressure='pore.pressure', temperature='pore.temperature', critical_pressure='pore.critical_pressure', critical_temperature='pore.critical_temperature')[source]
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]

Return type:  NumPy ndarray

Surface Tension

openpnm.models.phases.surface_tension. water (target, temperature='pore.temperature', salinity='pore.salinity')[source]
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.

openpnm.models.phases.surface_tension. eotvos (target, k, temperature='pore.temperature', critical_temperature='pore.critical_temperature', molar_density='pore.molar_density')[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.
 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]

Return type:  NumPy ndarray

Thermal Conductivity

openpnm.models.phases.thermal_conductivity. water (target, temperature='pore.temperature', salinity='pore.salinity')[source]
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, 393401, 1970.

openpnm.models.phases.thermal_conductivity. chung (target, Cv='pore.heat_capacity', acentric_factor='pore.acentric_factor', mol_weight='pore.molecular_weight', viscosity='pore.viscosity', temperature='pore.temperature', critical_temperature='pore.critical_temperature')[source]
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]

Return type:  NumPy ndarray


openpnm.models.phases.thermal_conductivity. sato (target, mol_weight='pore.molecular_weight', boiling_temperature='pore.boiling_point', temperature='pore.temperature', critical_temperature='pore.critical_temperature')[source]
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]

Return type:  NumPy ndarray

Vapor Pressure

openpnm.models.phases.vapor_pressure. water (target, temperature='pore.temperature', salinity='pore.salinity')[source]
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

Viscosity

openpnm.models.phases.viscosity. water (target, temperature='pore.temperature', salinity='pore.salinity')[source]
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
 Water Treatment, 2010.

openpnm.models.phases.viscosity. reynolds (target, u0, b, temperature='pore.temperature')[source]
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.


openpnm.models.phases.viscosity. chung (target, temperature='pore.temperature', mol_weight='pore.molecular_weight', critical_temperature='pore.critical_temperature', critical_volume='pore.critical_volume')[source]
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.
openpnm.models.physics
This submodule contains models for calculating properties related to physical
transport processes, including conductances, reaction rates, and capillary
effects
Capillary Pressure

openpnm.models.physics.capillary_pressure. washburn (target, surface_tension='pore.surface_tension', contact_angle='pore.contact_angle', diameter='throat.diameter')[source]
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 nonwetting invading phases in most materials.

openpnm.models.physics.capillary_pressure. purcell (target, r_toroid, surface_tension='pore.surface_tension', contact_angle='pore.contact_angle', diameter='throat.diameter')[source]
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 convergingdiverging 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 , and explored by Gostick in the context of a pore network
model.
References

openpnm.models.physics.capillary_pressure. purcell_bidirectional (target, r_toroid=5e06, num_points=1000.0, surface_tension='pore.surface_tension', contact_angle='pore.contact_angle', throat_diameter='throat.diameter', pore_diameter='pore.diameter')[source]
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.

Return type:  NumPy ndarray


openpnm.models.physics.capillary_pressure. ransohoff_snap_off (target, shape_factor=2.0, wavelength=5e06, require_pair=False, surface_tension='pore.surface_tension', contact_angle='pore.contact_angle', diameter='throat.diameter', vertices='throat.offset_vertices', **kwargs)[source]
Computes the capillary snapoff pressure assuming the throat is cylindrical
with convergingdiverging 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 crosssection 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 snapoff 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 snapoff 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.753765.

openpnm.models.physics.capillary_pressure. sinusoidal_bidirectional (target, r_toroid=5e06, num_points=1000.0, surface_tension='pore.surface_tension', contact_angle='pore.contact_angle', throat_diameter='throat.diameter', pore_diameter='pore.diameter')[source]
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.

Return type:  NumPy ndarray

Diffusive Conductance

openpnm.models.physics.diffusive_conductance. ordinary_diffusion (target, pore_area='pore.area', throat_area='throat.area', pore_diffusivity='pore.diffusivity', throat_diffusivity='throat.diffusivity', conduit_lengths='throat.conduit_lengths', conduit_shape_factors='throat.poisson_shape_factors')[source]
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
(1) This function requires that all the necessary phase properties already
be calculated.
(2) This function calculates the specified property for the entire
network then extracts the values for the appropriate throats at the end.
(3) This function assumes cylindrical throats with constant crosssection
area. Corrections for different shapes and variable crosssection area can
be imposed by passing the proper flow_shape_factor argument.

openpnm.models.physics.diffusive_conductance. taylor_aris_diffusion (target, pore_area='pore.area', throat_area='throat.area', pore_diffusivity='pore.diffusivity', pore_pressure='pore.pressure', throat_hydraulic_conductance='throat.hydraulic_conductance', throat_diffusivity='throat.diffusivity', conduit_lengths='throat.conduit_lengths', conduit_shape_factors='throat.poisson_shape_factors')[source]
Calculate the diffusive conductance of conduits in network considering the
TaylorAris effect (effect of fluid flow on diffusion), 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
 pore_pressure (string) â€“ Dictionary key of the pore pressure values
 throat_hydraulic_conductance (string) â€“ Dictionary key of the throat hydraulic_conductance 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 (with TaylorAris effect)
for conduits in the geometry attached to the given physics object.

Return type:  ndarray

Notes
(1) This function requires that all the necessary phase properties are
already calculated.
(2) This function calculates the specified property for the entire
network then extracts the values for the appropriate throats at the end.
(3) This function assumes cylindrical throats with constant crosssection
area. Corrections for different shapes and variable crosssection area can
be imposed by passing the proper flow_shape_factor argument.

openpnm.models.physics.diffusive_conductance. generic_conductance (target, transport_type, pore_area, throat_area, pore_diffusivity, throat_diffusivity, conduit_lengths, conduit_shape_factors, **kwargs)[source]
Calculate the generic conductance (could be mass, thermal, electrical,
ionic, or hydraylic) of conduits in the network, where a conduit is
( 1/2 pore  full throat  1/2 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.
 transport_type (string) â€“ Dictionary key of the transport type
 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 conductance values for conduits in the geometry
attached to the given physics object.

Return type:  ndarray

Notes
(1) This function requires that all the necessary phase properties already
be calculated.
(2) This function calculates the specified property for the entire
network then extracts the values for the appropriate throats at the end.
(3) This function assumes cylindrical throats with constant crosssection
area. Corrections for different shapes and variable crosssection area can
be imposed by passing the proper shape factor.
(4) shape_factor depends on the physics of the problem, i.e. diffusionlike
processes and fluid flow need different shape factors.
Electrical Conductance

openpnm.models.physics.electrical_conductance. series_resistors (target, pore_area='pore.area', throat_area='throat.area', pore_conductivity='pore.electrical_conductivity', throat_conductivity='throat.electrical_conductivity', conduit_lengths='throat.conduit_lengths', conduit_shape_factors='throat.poisson_shape_factors')[source]
Calculate the electrical conductance of conduits in network, where a
conduit is ( 1/2 pore  full throat  1/2 pore ). See the notes section.
