Cluster adsorbate placement

Hierarchical core + rigid fragment initialization and GA repositioning.

Configuration

class scgo.cluster_adsorbate.config.ClusterAdsorbateConfig(height_min=0.9, height_max=2.2, max_placement_attempts=80, blmin_ratio=0.7, cell_margin=6.0, random_spin_about_normal=True, validate_combined_structure=True, structure_min_distance_factor=0.4, structure_connectivity_factor=1.4, structure_check_clashes=True, structure_check_connectivity=True)[source]

Bases: object

Shared placement / relaxation settings for any small adsorbate fragment.

height_min: float = 0.9
height_max: float = 2.2
max_placement_attempts: int = 80
blmin_ratio: float = 0.7
cell_margin: float = 6.0
random_spin_about_normal: bool = True
validate_combined_structure: bool = True
structure_min_distance_factor: float = 0.4
structure_connectivity_factor: float = 1.4
structure_check_clashes: bool = True
structure_check_connectivity: bool = True

Surface site discovery

Convex-hull vertex, edge, and facet candidates for adsorbate placement on clusters and slabs.

class scgo.cluster_adsorbate.sites.SurfaceSiteCandidate(site_type, anchor, normal)[source]

Bases: object

site_type: Literal['vertex', 'edge', 'facet']
anchor: ndarray
normal: ndarray
scgo.cluster_adsorbate.sites.compute_surface_site_candidates(core)[source]

Build explicit vertex/edge/facet adsorption sites from a convex hull.

Return type:

dict[Literal['vertex', 'edge', 'facet'], list[SurfaceSiteCandidate]]

Placement and hierarchical builds

scgo.cluster_adsorbate.placement.place_fragment_on_cluster(core, fragment_template, rng, config=None, *, anchor_index=0, bond_axis=None, within_structure_site_counts=None, batch_site_counts=None, placement_metadata=None, site_core=None, clash_atoms=None)[source]

Rigidly place a gas-phase fragment with random orientation near the cluster.

Return type:

Atoms | None

scgo.cluster_adsorbate.placement.radii_derived_height_bounds(fragment_template, core, anchor_index)[source]

Heuristic height range from covalent radii of anchor and core atoms.

Return type:

tuple[float, float]

scgo.cluster_adsorbate.hierarchical.build_hierarchical_core_fragment_cluster(full_composition, adsorbate_definition, rng, previous_search_glob, fragment_templates, cluster_adsorbate_config, *, cluster_init_vacuum=8.0, init_mode='smart', max_placement_attempts=200, batch_site_counts=None, placement_metadata=None)[source]

Build core cluster, place rigid fragment(s), return gas-phase structure.

Each entry in fragment_templates is placed sequentially on distinct adsorption sites while preserving previously placed fragments.

Return type:

Atoms | None

scgo.cluster_adsorbate.hierarchical.build_adsorbate_only_cluster(fragment_templates, rng, cluster_adsorbate_config, *, adsorbate_definition=None, max_placement_attempts=200, batch_site_counts=None)[source]

Place one or more molecular fragments without a metal core.

Return type:

Atoms | None

Pre-GO feasibility checks

scgo.cluster_adsorbate.feasibility.validate_adsorbate_placement_feasibility(core_symbols, adsorbate_fragment_lengths, adsorbate_fragments=None, *, context='')[source]

Raise ValueError when fragment count likely exceeds placement capacity.

This is a fast, geometry-agnostic heuristic used before global optimization. It does not replace runtime placement validation.

Return type:

None

scgo.cluster_adsorbate.feasibility.count_adsorption_site_candidates(atoms)[source]

Return a conservative count of distinct adsorption sites on a 3D structure.

Return type:

int

scgo.cluster_adsorbate.feasibility.estimate_fragment_footprint_radius(fragment)[source]

Estimate minimum separation radius (Å) for placing a rigid fragment.

Return type:

float

GA repositioning and rigid geometry

class scgo.cluster_adsorbate.reposition.FragmentRepositionMutation(blmin, n_top, system_type, adsorbate_definition, fragment_templates=None, cluster_adsorbate_config=None, *, rng=None, verbose=False)[source]

Rigidly re-place one adsorbate fragment on a new core surface site.

Core atoms (tag 0) define the convex-hull site pool; other adsorbate fragments remain fixed as clash obstacles. Preserves fragment internal geometry by using the current fragment pose (or an input template) as the rigid body.

get_new_individual(parents)[source]

Function that returns a new individual. Overwrite in subclass.

mutate(atoms)[source]
Return type:

Atoms | None

scgo.cluster_adsorbate.rigid.enforce_frozen_adsorbate_geometry(atoms, *, n_slab, adsorbate_definition, fragment_templates, reattach_constraints=False)[source]

Snap adsorbate fragments back to templates and optionally reattach constraints.

Return type:

None

scgo.cluster_adsorbate.rigid.restore_rigid_adsorbate_fragments(atoms, *, n_slab, adsorbate_definition, fragment_templates)[source]

Reset adsorbate internal coordinates to templates in-place.

Each fragment keeps its current center and best-fit rigid orientation.

Return type:

None

Radii and steric scoring

Operator clash checks and placement ranking use covalent-radius blmin tables (BLMIN_RATIO_DEFAULT = 0.7). Structure validation uses connectivity_factor (typically 1.4) via validate_structure_for_system_type().

scgo.initialization.atomic_radii.build_blmin(symbols, ratio=0.7)[source]

Build an ASE-compatible blmin table for the given element symbols.

Return type:

dict[tuple[int, int], float]

scgo.initialization.atomic_radii.build_blmin_from_zs(zs, ratio=0.7)[source]

Build an ASE-compatible blmin table using scgo gap-filled covalent radii.

Return type:

dict[tuple[int, int], float]

scgo.initialization.steric_scoring.steric_deficit(positions, atomic_numbers, blmin)[source]

Sum of blmin violations within a single structure (lower is better).

Return type:

float

scgo.initialization.steric_scoring.steric_deficit_two_sets(left_positions, left_numbers, right_positions, right_numbers, blmin)[source]

Sum of blmin violations between two disjoint atom sets.

Return type:

float