Source code for pyfracval.experimental.pair_prefilters
"""Archived CCA pair-feasibility pre-filters.
The bounding-volume and SSA (surface-accessibility) prefilters were meant to
skip doomed candidate pairs before spending time on rigid-body sticking
attempts. Benchmarked head-to-head against no filtering at all (hard regime,
see ``docs/source/experiments.md``): both land within noise of the vanilla
baseline - neither improves success rate, and the extra bookkeeping doesn't
save meaningful wall time either. Kept reachable via
``cca_pair_feasibility_filter`` ("bounding_volume" / "ssa") for anyone who
wants to try a different angle later.
"""
from __future__ import annotations
import logging
import numpy as np
from ..config import OrchestratorAlgorithmConfig
logger = logging.getLogger(__name__)
[docs]
def bounding_volume_precheck(
gamma_pc: float,
r_max1: float,
r_max2: float,
gamma_real: bool,
algorithm_config: OrchestratorAlgorithmConfig | None = None,
) -> bool:
"""Check if two clusters can physically stick at distance gamma_pc."""
if not gamma_real or gamma_pc <= 0.0:
return False
algorithm_config = algorithm_config or OrchestratorAlgorithmConfig()
factor = float(algorithm_config.cca_bv_deep_penetration_factor)
rmax_diff = abs(r_max1 - r_max2)
if gamma_pc < rmax_diff * factor:
logger.debug(
f"BV pre-check reject: gamma={gamma_pc:.3f} < "
f"|r_max1-r_max2|*{factor}={rmax_diff * factor:.3f}"
)
return False
if gamma_pc > r_max1 + r_max2:
logger.debug(
f"BV pre-check reject: gamma={gamma_pc:.3f} > "
f"r_max1+r_max2={r_max1 + r_max2:.3f}"
)
return False
return True
[docs]
def surface_accessible_mask(
coords: np.ndarray,
radii: np.ndarray,
cm: np.ndarray,
r_max: float,
min_exposure: float | None = None,
algorithm_config: OrchestratorAlgorithmConfig | None = None,
) -> np.ndarray:
"""Compute surface accessibility mask for each monomer."""
n = coords.shape[0]
if n <= 1:
return np.ones(n, dtype=bool)
if min_exposure is None:
algorithm_config = algorithm_config or OrchestratorAlgorithmConfig()
min_exposure = float(algorithm_config.cca_ssa_min_exposure)
dist_to_cm = np.linalg.norm(coords - cm[np.newaxis, :], axis=1)
radial_fraction = dist_to_cm / max(r_max, 1.0e-12)
mean_r = float(np.mean(radii))
contact_dist_sq = (2.5 * mean_r) ** 2
diffs = coords[:, np.newaxis, :] - coords[np.newaxis, :, :]
d_sq = np.sum(diffs * diffs, axis=2)
neighbor_count = np.sum((d_sq < contact_dist_sq) & (d_sq > 0), axis=1)
max_neighbors = max(n - 1, 1)
isolation_fraction = 1.0 - neighbor_count / max_neighbors
exposure = 0.5 * radial_fraction + 0.5 * isolation_fraction
return exposure >= min_exposure