Repository: PMEAL/OpenPNM

Issue:
Add a function for fitting PSD to experimental histograms
Following the discussion had in #2112, it might be a good idea to offer a function like the one I proposed. 

Not quite sure how to implement this though...it doesn't quite fit with the pore-scale modeling framework.  

Discussion:

Patch:
diff --git a/openpnm/models/geometry/misc.py b/openpnm/models/geometry/misc.py
index fa4df4578..f300a85bb 100644
--- a/openpnm/models/geometry/misc.py
+++ b/openpnm/models/geometry/misc.py
@@ -1,6 +1,5 @@
 r"""
-Helper methods for openpnm.geometry module.
-
+Helper methods for openpnm.models.geometry module
 """
 
 
diff --git a/openpnm/models/misc/__init__.py b/openpnm/models/misc/__init__.py
index d01329346..addff073f 100644
--- a/openpnm/models/misc/__init__.py
+++ b/openpnm/models/misc/__init__.py
@@ -10,8 +10,7 @@ generating an array of random numbers.
 
 """
 
-from .statistical_distributions import generic_distribution, normal, weibull, random
-from .simple_equations import linear, polynomial, generic_function
-from .basic_math import constant, product, scaled, clip, normalize, summation
-from .basic_math import fraction, invert, blank
+from .statistical_distributions import *
+from .simple_equations import *
+from .basic_math import *
 from .neighbor_lookups import from_neighbor_throats, from_neighbor_pores
diff --git a/openpnm/models/misc/basic_math.py b/openpnm/models/misc/basic_math.py
index ecad370a5..603bdedcb 100644
--- a/openpnm/models/misc/basic_math.py
+++ b/openpnm/models/misc/basic_math.py
@@ -5,6 +5,19 @@ from openpnm.utils import logging
 logger = logging.getLogger(__name__)
 
 
+__all__ = [
+    'blank',
+    'clip',
+    'constant',
+    'fraction',
+    'invert',
+    'normalize',
+    'scaled',
+    'summation',
+    'product',
+    ]
+
+
 def blank(target):
     pass
 
diff --git a/openpnm/models/misc/simple_equations.py b/openpnm/models/misc/simple_equations.py
index 994072d78..2e03cf895 100644
--- a/openpnm/models/misc/simple_equations.py
+++ b/openpnm/models/misc/simple_equations.py
@@ -5,6 +5,13 @@ from openpnm.utils import logging
 logger = logging.getLogger(__name__)
 
 
+__all__ = [
+    'linear',
+    'polynomial',
+    'generic_function',
+    ]
+
+
 def generic_function(target, prop, func, **kwargs):
     r"""
     Runs an arbitrary function on the given data
diff --git a/openpnm/models/misc/statistical_distributions.py b/openpnm/models/misc/statistical_distributions.py
index 8024f21dd..d44cad6d7 100644
--- a/openpnm/models/misc/statistical_distributions.py
+++ b/openpnm/models/misc/statistical_distributions.py
@@ -5,6 +5,15 @@ from openpnm.utils import logging
 logger = logging.getLogger(__name__)
 
 
+__all__ = [
+    'random',
+    'weibull',
+    'normal',
+    'generic_distribution',
+    'match_histogram',
+    ]
+
+
 def random(target, element, seed=None, num_range=[0, 1]):
     r"""
     Create an array of random numbers of a specified size.
@@ -197,3 +206,32 @@ def generic_distribution(target, seeds, func):
     seeds = target[seeds]
     value = func.ppf(seeds)
     return value
+
+
+def match_histogram(target, bin_centers, bin_heights, element='pore'):
+    r"""
+    Generate values corresponding to a given histogram
+
+    Parameters
+    ----------
+    target : OpenPNM object
+        The object for which values are to be generated
+    bin_centers : array_like
+        The x-axis of the histogram, such as pore sizes
+    bin_heights : array_like
+        The y-axis of the histogram, such as the number of pores of each size
+    element : str
+        Controls how many values to generate. Can either be 'pore' or 'throat'.
+
+    Returns
+    -------
+    vals : ndarray
+        Values corresponding to ``bin_centers`` generated in proportion to the
+        respective ``bin_heights``.
+
+    """
+    N = target._count(element)
+    h = np.cumsum(bin_heights)
+    b = np.digitize(np.random.rand(N)*np.amax(h), bins=h)
+    vals = np.array(bin_centers)[b]
+    return vals


Test patch:
diff --git a/tests/unit/models/misc/MiscTest.py b/tests/unit/models/misc/MiscTest.py
index 884e76b3e..b93a60e77 100644
--- a/tests/unit/models/misc/MiscTest.py
+++ b/tests/unit/models/misc/MiscTest.py
@@ -288,6 +288,17 @@ class MiscTest:
                       model=mods.basic_math.invert)
         assert net['pore.entry_pressure'][0] == 0.5
 
+    def test_match_histograms(self):
+        net = op.network.Cubic(shape=[5, 5, 5])
+        c = [0.1, 0.3, 0.8, 1.2]
+        h = [5, 20, 20, 100]
+        a = mods.match_histogram(target=net, bin_centers=c, bin_heights=h,
+                                 element='pore')
+        assert np.all(np.unique(a) == c)
+        vals, nums = np.unique(a, return_counts=True)
+        assert nums[3] == np.amax(nums)
+        assert nums[0] == np.amin(nums)
+
 
 if __name__ == '__main__':