Source code for scgo.utils.combine_atoms

"""Shared Atoms combine / surface-geometry helpers."""

from __future__ import annotations

import numpy as np
from ase import Atoms

from scgo.cluster_adsorbate.geometry import random_rotation_matrix

__all__ = [
    "concatenate_inherit_cell_pbc",
    "random_rotation_matrix",
    "slab_surface_extreme",
    "top_layer_indices",
]


[docs] def concatenate_inherit_cell_pbc(base: Atoms, mobile: Atoms) -> Atoms: """Concatenate ``base`` then ``mobile``; inherit cell and PBC from ``base``.""" if len(mobile) == 0: return base.copy() out = base.copy() + mobile.copy() out.set_cell(base.get_cell()) out.set_pbc(base.get_pbc()) return out
[docs] def slab_surface_extreme( positions_or_atoms: Atoms | np.ndarray, axis: int, *, upper: bool = True, ) -> float: """Return max (or min) Cartesian coordinate along ``axis``.""" if isinstance(positions_or_atoms, Atoms): pos = positions_or_atoms.get_positions() else: pos = np.asarray(positions_or_atoms) if len(pos) == 0: return 0.0 return float(np.max(pos[:, axis]) if upper else np.min(pos[:, axis]))
[docs] def top_layer_indices( positions: np.ndarray, axis: int, *, thickness: float = 2.5, ) -> list[int]: """Indices of atoms within ``thickness`` Å of the upper surface extreme.""" pos = np.asarray(positions) if len(pos) == 0: return [] top = slab_surface_extreme(pos, axis, upper=True) mask = pos[:, axis] >= top - thickness indices = [i for i, m in enumerate(mask) if m] return indices if indices else list(range(len(pos)))