Source code for dcma.tools
"""
Basic general toolbox.
"""
import inspect
import os
import pkg_resources
import numpy as np
[docs]def path_relative_to_module(relative_path): # type (object) -> object
"""Get file from a path that is relative to caller's module.
Returns: absolute path as string"""
caller = inspect.stack()[1]
mod = inspect.getmodule(caller[0])
return os.path.normpath(pkg_resources.resource_filename(
mod.__name__, relative_path))
[docs]def bin_edges_to_centers(edges):
centers = [0.5 * (edges[i + 1] + edges[i])
for i in range(len(edges) - 1)]
return np.array(centers)
[docs]def bin_edges_to_widths(edges):
widths = [abs(edges[i + 1] - edges[i])
for i in range(len(edges) - 1)]
return np.array(widths)
[docs]def are_edges_consistent(edges_list, weights=None):
"""
Check if the bin edges for different replicas are consistent.
Args:
edges_list: a list of numpy arrays, where each array holds a set of edges
weights: weights for the replicas
Returns:
A boolean
"""
if weights is None:
weights = [1.0 for _ in edges_list]
edges = np.average(edges_list, axis=0, weights=weights)
for e in edges_list:
if not np.all(
np.absolute((e - edges) / (edges + 1e-4)) < 0.1
):
return False
return True
[docs]class UnitConverter(object):
"""
In the c++ part of the program, all diffusion (and free energy profiles)
are normalized, i.e. they are agnostic of the coordinates. This converter
class helps to convert between units.
- Permeabilities are given in cm/s.
- Diffusion profiles are given in [10^-5 cm^2/s].
- Free energies are given in [k_B T].
"""
def __init__(self, edges, weights=None, test_bin_consistency=True):
if not type(edges) is list:
edges = [edges]
assert all(type(e) is np.ndarray for e in edges)
if weights is None:
weights = [1.0 for _ in edges]
self._edges = np.average(edges, axis=0, weights=weights)
self._bin_widths = bin_edges_to_widths(self._edges)
self._bin_centers = bin_edges_to_centers(self._edges)
if test_bin_consistency:
assert are_edges_consistent(edges, weights)
[docs] @staticmethod
def from_transitions(transition_matrices):
return UnitConverter(
[tmat.edges for tmat in transition_matrices],
[tmat.weight for tmat in transition_matrices]
)
@property
def bin_widths(self):
""" bin width in Angstrom"""
return self._bin_widths
@property
def n_bins(self):
""" number of bins """
return len(self._bin_widths)
@property
def edges(self):
return self._edges
@property
def bin_centers(self):
""" bin centers in Angstrom"""
return self._bin_centers
[docs] def get_generalized_centers(self):
"""
Returns an array of size (n_bins + 2) that contains the bin centers plus "virtual" centers to the left and
right. The latter are required to properly define the effect of biasing potentials at the edges.
"""
generalized_centers = self.bin_centers
d = (self.bin_widths[0] + self.bin_widths[-1]) / 2.0
beyond_left = generalized_centers[0] - d
beyond_right = generalized_centers[-1] + d
generalized_centers = np.insert(generalized_centers, 0, beyond_left)
generalized_centers = np.append(generalized_centers, [beyond_right])
return generalized_centers
[docs] def normalize_diffusion(self, diff, apply_log=False):
"""
Args:
diff: diffusion profile in 10^-5 cm^2/s
apply_log: Whether to apply a natural logarithm
to the dimensionless diffusion.
(log D is used internally by the c++ code
and constant diffusion profiles (initial profiles)
are given as log D)
Returns:
Dimensionless diffusion profile
"""
d = diff / self._bin_widths**2 / 10
# (factor 10 converts from 10e-5 cm^2/s to A^2/ps)
if apply_log:
d = np.log(d)
return d
[docs] def denormalize_diffusion(self, diff):
""" The reverse transformation of normalize_diffusion."""
return diff * self._bin_widths**2 * 10
[docs]def comma_string_to_list(some_string, converter=float):
"""
Converts "1,1,1" to [1.0, 1.0, 1.0].
"""
some_list = some_string.strip().split(',')
return [converter(l) for l in some_list]