Source code for CodeEntropy.levels.nodes.detect_molecules
"""Detect molecules and build grouping definitions for the reduced universe.
This module defines a static DAG node responsible for ensuring a reduced
universe is available and generating molecule groupings using the configured
grouping strategy.
"""
from __future__ import annotations
import logging
from typing import Any
from CodeEntropy.molecules.grouping import MoleculeGrouper
logger = logging.getLogger(__name__)
SharedData = dict[str, Any]
[docs]
class DetectMoleculesNode:
"""Static node that establishes molecule groups.
Produces:
shared_data["reduced_universe"]
shared_data["groups"]
shared_data["number_molecules"]
"""
def __init__(self) -> None:
"""Initialize the node with a molecule grouping helper."""
self._grouping = MoleculeGrouper()
[docs]
def run(self, shared_data: SharedData) -> dict[str, Any]:
"""Detect molecules and create grouping definitions.
Args:
shared_data: Shared data dictionary. Requires:
- "universe"
- "args"
Returns:
Dict containing:
- "groups": Molecule grouping dictionary.
- "number_molecules": Total molecule count.
Raises:
KeyError: If required keys are missing.
"""
universe = self._ensure_reduced_universe(shared_data)
grouping_strategy = self._get_grouping_strategy(shared_data)
groups = self._grouping.grouping_molecules(universe, grouping_strategy)
number_molecules = self._count_molecules(universe)
shared_data["groups"] = groups
shared_data["number_molecules"] = number_molecules
return {
"groups": groups,
"number_molecules": number_molecules,
}
def _ensure_reduced_universe(self, shared_data: SharedData) -> Any:
"""Ensure reduced_universe exists in shared_data.
Args:
shared_data: Shared data dictionary.
Returns:
Reduced universe object.
Raises:
KeyError: If no universe is available.
"""
universe = shared_data.get("reduced_universe")
if universe is None:
universe = shared_data.get("universe")
if universe is None:
raise KeyError("shared_data must contain 'universe'")
shared_data["reduced_universe"] = universe
return universe
def _get_grouping_strategy(self, shared_data: SharedData) -> str:
"""Extract grouping strategy from args.
Args:
shared_data: Shared data dictionary.
Returns:
Grouping strategy string.
"""
args = shared_data["args"]
return getattr(args, "grouping", "each")
@staticmethod
def _count_molecules(universe: Any) -> int:
"""Count molecules in the universe.
Args:
universe: MDAnalysis universe.
Returns:
Number of molecular fragments.
"""
return len(universe.atoms.fragments)