Source code for iadpython.grid

# pylint: disable=invalid-name
# pylint: disable=too-many-instance-attributes
# pylint: disable=too-many-locals
# pylint: disable=too-many-arguments
# pylint: disable=consider-using-f-string

"""Class for doing inverse adding-doubling calculations for a sample.

Example::

    >>> import iadpython

    >>> exp = iadpython.Experiment(0.5, 0.1, default_g=0.5)
    >>> grid = iadpython.Grid()
    >>> grid.calc(exp)
    >>> print(grid)
"""

import numpy as np


[docs] class Grid(): """Class to track pre-calculated R & T values. There is a long story associated with these routines. I spent a lot of time trying to find an empirical function to allow a guess at a starting value for the inversion routine. Basically nothing worked very well. There were too many special cases and what not. So I decided to calculate a whole bunch of reflection and transmission values and keep their associated optical properties linked nearby. """ def __init__(self, search=None, default=None, N=21): """Object initialization.""" self.search = search self.default = default self.N = N self.a = np.zeros((N, N)) self.b = np.zeros((N, N)) self.g = np.zeros((N, N)) self.ur1 = np.zeros((N, N)) self.ut1 = np.zeros((N, N)) def __str__(self): """Return basic details as a string for printing.""" s = "---------------- Grid ---------------\n" if self.search is None: s += "search = None\n" else: s += "search = %s\n" % self.search if self.default is None: s += "default = None\n" else: s += "default = %.5f\n" % self.default s += matrix_as_string(self.a, "a") s += matrix_as_string(self.b, "b") s += matrix_as_string(self.g, "g") s += matrix_as_string(self.ur1, "ur1") s += matrix_as_string(self.ut1, "ut1") return s
[docs] def calc(self, exp, default=None): """Precalculate a grid.""" if default is not None: self.default = default a = np.linspace(0, 1, self.N) b = np.linspace(0, 10, self.N) g = np.linspace(-0.99, 0.99, self.N) self.search = exp.search if self.search == 'find_ab': self.g = np.full((self.N, self.N), self.default) self.a, self.b = np.meshgrid(a, b) if self.search == 'find_ag': self.b = np.full((self.N, self.N), self.default) self.a, self.g = np.meshgrid(a, g) if self.search == 'find_bg': self.a = np.full((self.N, self.N), self.default) self.b, self.g = np.meshgrid(b, g) for i in range(self.N): for j in range(self.N): exp.sample.a = self.a[i, j] exp.sample.b = self.b[i, j] exp.sample.g = self.g[i, j] self.ur1[i, j], self.ut1[i, j], _, _ = exp.sample.rt()
[docs] def min_abg(self, mr, mt): """Find closest a, b, g closest to mr and mt.""" if self.ur1 is None: raise ValueError("Grid.calc(exp) must be called before Grid.min_abg") A = np.abs(mr - self.ur1) + np.abs(mt - self.ut1) ii_flat = A.argmin() i, j = ii_flat // A.shape[1], ii_flat % A.shape[1] return self.a[i, j], self.b[i, j], self.g[i, j]
[docs] def is_stale(self, default): """Decide if current grid is still useful.""" if self.default is None: return True if self.default != default: return True return False
[docs] def matrix_as_string(x, label=''): """Return matrix as a string.""" if x is None: return "" n, m = x.shape ndashes = (80 - len(label) - 2) // 2 s = "\n" s += '-' * ndashes + ' ' + label + ' ' + '-' * ndashes s += "\n[\n" for i in range(n): s += '[' for j in range(m): s += "%6.3f, " % x[i, j] s += "], \n" s += "]\n" return s