Demo of core functions

import pnmlib as pnm
import numpy as np
import matplotlib.pyplot as plt
from pnmlib.inspect import tree
from pnmlib.core import get_data, set_data
from IPython.display import Markdown
sim = pnm.generators.cubic([3, 3])
sim['pore.coords'] = sim.pop('node.coords')
sim['throat.conns'] = sim.pop('edge.conns')
tree(sim)
├── 'pore.coords': (9, 3), float64
└── 'throat.conns': (12, 2), int64

add_network This takes the dict from a network generator and adds it to the top level. It also adds 'pore.all' and 'throat.all' since these are used by other functions.

pnm.core.add_network(sim, sim)
tree(sim)
├── 'pore.all': 9/9, bool
├── 'pore.coords': (9, 3), float64
├── 'throat.all': 12/12, bool
└── 'throat.conns': (12, 2), int64

create_group

When creating an empty group the 'pore.all' and 'throat.all' arrays are created as place holders. It is not possible to have an empty group though, as this will break things:

pnm.core.create_group(sim, 'phase1')
tree(sim)
├── 'pore.all': 9/9, bool
├── 'pore.coords': (9, 3), float64
├── 'throat.all': 12/12, bool
├── 'throat.conns': (12, 2), int64
└── phase1
    ├── 'pore.all': 9/9, bool
    └── 'throat.all': 12/12, bool
for item in sim.keys():
    print(item)
pore.coords
throat.conns
pore.all
throat.all
phase1/pore.all
phase1/throat.all

get_group This fetches a subset of data as a standalone dict:

tmp = pnm.core.get_group(sim, 'phase1')
tree(tmp)
├── 'pore.all': 9/9, bool
└── 'throat.all': 12/12, bool

But leaves the main simulation intact:

tree(sim)
├── 'pore.all': 9/9, bool
├── 'pore.coords': (9, 3), float64
├── 'throat.all': 12/12, bool
├── 'throat.conns': (12, 2), int64
└── phase1
    ├── 'pore.all': 9/9, bool
    └── 'throat.all': 12/12, bool

pop_group This can be used to remove a group from the simulation, and it also returns it in case it’s needed:

tmp = pnm.core.pop_group(sim, 'phase1')
tree(tmp)
└── phase1
    ├── 'pore.all': 9/9, bool
    └── 'throat.all': 12/12, bool

In this case the simulation is altered:

tree(sim)
├── 'pore.all': 9/9, bool
├── 'pore.coords': (9, 3), float64
├── 'throat.all': 12/12, bool
└── 'throat.conns': (12, 2), int64

build_conduit_data The returns an array of pore-throat-pore values for use in conductance calculations:

set_data(sim, 'pore.diameter', value=2.0)
set_data(sim, 'throat.diameter', value=1.0)
D = pnm.core.build_conduit_data(sim, group='', propname='diameter')
print(D)
[[2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]
 [2. 1. 2.]]

count This is a way to programmatically find the number of pores or throats:

print(pnm.core.count(sim, 'pore'))
print(pnm.core.count(sim, 'throat'))
9
12

fold_dict Takes a flat dict and creates a nested one.

pnm.core.create_group(sim, 'phase_02')
print(sim.keys())
folded = pnm.core.fold_dict(sim, '/')
print(folded.keys())
dict_keys(['pore.coords', 'throat.conns', 'pore.all', 'throat.all', 'pore.diameter', 'throat.diameter', 'phase_02/pore.all', 'phase_02/throat.all'])
dict_keys(['phase_02', 'throat.diameter', 'pore.diameter', 'throat.all', 'pore.all', 'throat.conns', 'pore.coords'])

flatten_dict Takes a nested dict and creates a flattened one:

flattened = pnm.core.flatten_dict(folded, '/')
pnm.inspect.data(flattened)

══════════════════════════════════════════════════════════════════════════════
  #  Properties                                                   Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.coords                                                         9 / 9
  3  pore.diameter                                                       9 / 9
  4  throat.conns                                                      12 / 12
  5  throat.diameter                                                   12 / 12
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Labels                                                 Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.all                                                                9
  3  throat.all                                                             12
  4  phase_02/pore.all                                                       9
  5  phase_02/throat.all                                                    12
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

flatten_list

This is not really relevant, it’s more of a ‘just-in-case’ utility function.

generate_name This will generate a unique name based on the given prefix. It essential just adds a number larger than the largest currently in use:

name = pnm.core.generate_name(sim, 'phase')
print(name)
phase_01

get_components When multiphase mixtures are used, this function tells us which components belong to a given mixture:

pnm.core.create_group(sim, 'phase_01/phase_02')
pnm.core.create_group(sim, 'phase_01/phase_03')
comps = pnm.core.get_components(sim, 'phase_01')
print(comps)
['phase_02', 'phase_03']

set_label This creates an empty label array (filled with False) of the given name, and optionally fills in the given locs with True.

pnm.core.set_label(sim, label='network/pore.test4', locs=[1, 2, 3])
pnm.core.set_label(sim, label='network/pore.test3', locs=[1, 2, 3])
tree(sim)
├── 'pore.all': 9/9, bool
├── 'pore.coords': (9, 3), float64
├── 'pore.diameter': (9,), float64
├── 'throat.all': 12/12, bool
├── 'throat.conns': (12, 2), int64
├── 'throat.diameter': (12,), float64
├── network
│   ├── 'pore.test3': 3/9, bool
│   └── 'pore.test4': 3/9, bool
├── phase_01
│   ├── phase_03
│   │   ├── 'pore.all': 9/9, bool
│   │   └── 'throat.all': 12/12, bool
│   └── phase_02
│       ├── 'pore.all': 9/9, bool
│       └── 'throat.all': 12/12, bool
└── phase_02
    ├── 'pore.all': 9/9, bool
    └── 'throat.all': 12/12, bool

get_indices Get the indices where the given labels are True, using the specified mode which defaults to 'or'.

pnm.core.get_indices(sim, labels=['network/pore.test4', 'network/pore.test3'], mode='and')
array([1, 2, 3])
set_data(sim, 'network/pore.test2', True, [3, 4])
pnm.core.get_indices(sim, element='pore', labels=['network/pore.test2', 'network/pore.test3'], mode='xor')
array([1, 2, 4])

get_label_data Gets all the arrays on the simulation which are bool (i.e. labels):

labels = pnm.core.get_label_data(sim)
pnm.inspect.info(labels)

══════════════════════════════════════════════════════════════════════════════
  #  Properties                                                   Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Labels                                                 Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  pore.all                                                                9
  3  throat.all                                                             12
  4  phase_02/pore.all                                                       9
  5  phase_02/throat.all                                                    12
  6  phase_01/phase_02/pore.all                                              9
  7  phase_01/phase_02/throat.all                                           12
  8  phase_01/phase_03/pore.all                                              9
  9  phase_01/phase_03/throat.all                                           12
 10  network/pore.test4                                                      3
 11  network/pore.test3                                                      3
 12  network/pore.test2                                                      2
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

get_prop_data Get’s all the arrays on the simulation which are numerical.

set_data(sim, 'phase_02/pore.test', 1.1)
props = pnm.core.get_prop_data(sim)
pnm.inspect.info(props)

══════════════════════════════════════════════════════════════════════════════
  #  Properties                                                   Valid Values
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  2  phase_02/pore.test                                                  9 / 9
  3  pore.coords                                                         9 / 9
  4  pore.diameter                                                       9 / 9
  5  throat.conns                                                      12 / 12
  6  throat.diameter                                                   12 / 12
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
  #  Labels                                                 Assigned Locations
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――
――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――

get_param_data Gets all the parameters (i.e. scalars) on the simulation.

set_data(sim, 'param.T', 333.0)
set_data(sim, 'phase_01/param.T', 333.0)
tmp = pnm.core.get_param_data(sim)
tree(tmp)
├── 'param.T': 333.0
└── phase_01
    └── 'param.T': 333.0

get_pores Returns a list of pore indices where the give label(s) are present, according to the given 'mode'.

pnm.core.get_pores(sim, 'network/pore.test2')
array([3, 4])

get_throats Returns a list of throat indices where the give label(s) are present, according to the given 'mode'.

set_data(sim, 'network/throat.test5', True, [0, 1, 2])
pnm.core.get_throats(sim, 'network/throat.test5')
array([0, 1, 2])

num_pores Counts the number of pores in the simulation where the given label(s) are present, according to the specified 'mode'.

Np = pnm.core.num_pores(sim, labels=['pore.all'])
print(Np)
9

num_throats Counts the number of throats in the simulation where the given label(s) are present, according to the specified 'mode'.

Nt = pnm.core.num_throats(sim, labels=['throat.all'])
print(Nt)
12

filter_pores

pnm.core.filter_pores(sim, locs=[0, 1, 2], labels=['network/pore.test3'])
array([1, 2])