Skip to content

fingerprint

filtered_histogram(mesh, internal, external, bins=200, xrange=None, yrange=None, samples_per_edge=4, include_inverse=False)

Create histogram with multiple samples per face.

Parameters:

Name Type Description Default
mesh

mesh with vertex attributes

required
internal

atomic number for internal atom

required
external

atomic number for external atom

required
bins int

number of bins for histogram (default: 200)

200
xrange tuple

range for x-axis (d_i)

None
yrange tuple

range for y-axis (d_e)

None
samples_per_edge int

number of samples per triangle edge (default: 4)

4
include_inverse bool

if True, also include contacts where internal and external are swapped (e.g., C...H and H...C). Default: False

False

Returns:

Name Type Description
tuple

(histogram, xedges, yedges) as returned by np.histogram2d

Source code in chmpy/crystal/fingerprint.py
def filtered_histogram(
    mesh, internal, external, bins=200, xrange=None, yrange=None, samples_per_edge=4, include_inverse=False
):
    """Create histogram with multiple samples per face.

    Args:
        mesh: mesh with vertex attributes
        internal: atomic number for internal atom
        external: atomic number for external atom
        bins (int, optional): number of bins for histogram (default: 200)
        xrange (tuple, optional): range for x-axis (d_i)
        yrange (tuple, optional): range for y-axis (d_e)
        samples_per_edge (int, optional): number of samples per triangle edge (default: 4)
        include_inverse (bool, optional): if True, also include contacts where internal and
            external are swapped (e.g., C...H and H...C). Default: False

    Returns:
        tuple: (histogram, xedges, yedges) as returned by np.histogram2d
    """
    di = mesh.vertex_attributes["d_i"]
    de = mesh.vertex_attributes["d_e"]
    if xrange is None:
        xrange = np.min(di), np.max(di)
    if yrange is None:
        yrange = np.min(de), np.max(de)

    di_atom = mesh.vertex_attributes["nearest_atom_internal"]
    de_atom = mesh.vertex_attributes["nearest_atom_external"]
    vertex_mask = (de_atom == external) & (di_atom == internal)

    if include_inverse:
        vertex_mask_inverse = (de_atom == internal) & (di_atom == external)
        vertex_mask = vertex_mask | vertex_mask_inverse

    face_mask = np.any(vertex_mask[mesh.faces], axis=1)
    filtered_faces = mesh.faces[face_mask]

    if len(filtered_faces) == 0:
        return np.histogram2d([], [], bins=bins, range=(xrange, yrange))

    vertices = np.stack([di, de], axis=1)

    interpolated, weights = sample_face_points(
        vertices, filtered_faces, samples_per_edge
    )

    di_samples = interpolated[..., 0].flatten()
    de_samples = interpolated[..., 1].flatten()

    weights_tiled = np.tile(weights, len(filtered_faces))

    return np.histogram2d(
        di_samples, de_samples, bins=bins, range=(xrange, yrange), weights=weights_tiled
    )

filtered_histogram_by_elements(mesh, internal_element, external_element, bins=200, xrange=None, yrange=None, samples_per_edge=4, include_inverse=False)

Create filtered histogram by specifying element symbols.

This is a convenience method that allows filtering by element symbols instead of atomic numbers.

Parameters:

Name Type Description Default
mesh

mesh with vertex attributes

required
internal_element str or int

element symbol (e.g., "C") or atomic number for internal atom

required
external_element str or int

element symbol (e.g., "H") or atomic number for external atom

required
bins int

number of bins for histogram (default: 200)

200
xrange tuple

range for x-axis (d_i)

None
yrange tuple

range for y-axis (d_e)

None
samples_per_edge int

number of samples per triangle edge (default: 4)

4
include_inverse bool

if True, also include contacts where internal and external are swapped (e.g., C...H and H...C). Default: False

False

Returns:

Name Type Description
tuple

(histogram, xedges, yedges) as returned by np.histogram2d

Examples:

>>> # Filter for C...H contacts only
>>> hist = filtered_histogram_by_elements(mesh, "C", "H")
>>> # Filter for both C...H and H...C contacts
>>> hist = filtered_histogram_by_elements(mesh, "C", "H", include_inverse=True)
>>> # Or equivalently using atomic numbers
>>> hist = filtered_histogram_by_elements(mesh, 6, 1, include_inverse=True)
Source code in chmpy/crystal/fingerprint.py
def filtered_histogram_by_elements(
    mesh, internal_element, external_element, bins=200, xrange=None, yrange=None, samples_per_edge=4, include_inverse=False
):
    """Create filtered histogram by specifying element symbols.

    This is a convenience method that allows filtering by element symbols
    instead of atomic numbers.

    Args:
        mesh: mesh with vertex attributes
        internal_element (str or int): element symbol (e.g., "C") or atomic number for internal atom
        external_element (str or int): element symbol (e.g., "H") or atomic number for external atom
        bins (int, optional): number of bins for histogram (default: 200)
        xrange (tuple, optional): range for x-axis (d_i)
        yrange (tuple, optional): range for y-axis (d_e)
        samples_per_edge (int, optional): number of samples per triangle edge (default: 4)
        include_inverse (bool, optional): if True, also include contacts where internal and
            external are swapped (e.g., C...H and H...C). Default: False

    Returns:
        tuple: (histogram, xedges, yedges) as returned by np.histogram2d

    Examples:
        >>> # Filter for C...H contacts only
        >>> hist = filtered_histogram_by_elements(mesh, "C", "H")
        >>> # Filter for both C...H and H...C contacts
        >>> hist = filtered_histogram_by_elements(mesh, "C", "H", include_inverse=True)
        >>> # Or equivalently using atomic numbers
        >>> hist = filtered_histogram_by_elements(mesh, 6, 1, include_inverse=True)
    """
    if isinstance(internal_element, str):
        internal_element = Element.from_string(internal_element).atomic_number
    if isinstance(external_element, str):
        external_element = Element.from_string(external_element).atomic_number

    return filtered_histogram(
        mesh, internal_element, external_element, bins, xrange, yrange, samples_per_edge, include_inverse
    )

fingerprint_histogram(mesh, bins=200, xrange=None, yrange=None, samples_per_edge=4)

Create histogram for all faces with multiple samples per face.

Source code in chmpy/crystal/fingerprint.py
def fingerprint_histogram(mesh, bins=200, xrange=None, yrange=None, samples_per_edge=4):
    """Create histogram for all faces with multiple samples per face."""
    di = mesh.vertex_attributes["d_i"]
    de = mesh.vertex_attributes["d_e"]
    if xrange is None:
        xrange = np.min(di), np.max(di)
    if yrange is None:
        yrange = np.min(de), np.max(de)

    vertices = np.stack([di, de], axis=1)

    interpolated, weights = sample_face_points(vertices, mesh.faces, samples_per_edge)

    di_samples = interpolated[..., 0].flatten()
    de_samples = interpolated[..., 1].flatten()

    weights_tiled = np.tile(weights, len(mesh.faces))

    return np.histogram2d(
        di_samples, de_samples, bins=bins, range=(xrange, yrange), weights=weights_tiled
    )

sample_face_points(vertices, faces, samples_per_edge=4)

Generate sample points within triangle faces using barycentric coordinates.

Source code in chmpy/crystal/fingerprint.py
def sample_face_points(vertices, faces, samples_per_edge=4):
    """Generate sample points within triangle faces using barycentric coordinates."""
    points = []
    weights = []

    for i in range(samples_per_edge + 1):
        for j in range(samples_per_edge + 1 - i):
            a = i / samples_per_edge
            b = j / samples_per_edge
            c = 1.0 - a - b
            points.append([a, b, c])
            weights.append(1.0 / ((samples_per_edge + 1) * (samples_per_edge + 2) / 2))

    points = np.array(points)
    weights = np.array(weights)

    face_vertices = vertices[faces]

    points = points[:, None, :]

    interpolated = np.sum(points[..., None] * face_vertices[None, ...], axis=2)

    return interpolated, weights