Source code for scgo.initialization.steric_scoring

"""Steric deficit scoring and blmin distance lookups for placement and GA."""

from __future__ import annotations

import numpy as np
from scipy.spatial.distance import cdist, pdist, squareform


def get_blmin_distance(
    blmin: dict, atomic_number_a: int, atomic_number_b: int
) -> float:
    """Minimum allowed distance for an element pair from an ASE-style blmin table."""
    key = (int(atomic_number_a), int(atomic_number_b))
    if key in blmin:
        return blmin[key]
    return blmin[(int(atomic_number_b), int(atomic_number_a))]


[docs] def steric_deficit(positions, atomic_numbers, blmin: dict) -> float: """Sum of blmin violations within a single structure (lower is better).""" n_atoms = len(positions) if n_atoms <= 1: return 0.0 distances = squareform(pdist(positions)) numbers = np.asarray(atomic_numbers, dtype=int) deficit = 0.0 for i in range(n_atoms): for j in range(i + 1, n_atoms): gap = get_blmin_distance(blmin, numbers[i], numbers[j]) - distances[i, j] if gap > 0.0: deficit += gap return deficit
[docs] def steric_deficit_two_sets( left_positions, left_numbers, right_positions, right_numbers, blmin: dict, ) -> float: """Sum of blmin violations between two disjoint atom sets.""" if len(left_positions) == 0 or len(right_positions) == 0: return 0.0 distances = cdist(left_positions, right_positions) left_z = np.asarray(left_numbers, dtype=int) right_z = np.asarray(right_numbers, dtype=int) required = np.array( [ [get_blmin_distance(blmin, int(zi), int(zj)) for zj in right_z] for zi in left_z ], dtype=float, ) return float(np.maximum(required - distances, 0.0).sum())