Skip to content

molclusters.config

config

Provides the MolClsConfig class that stores the configuration information for the analysis.

RuleType

Bases: StrEnum

Enum class for types of rules.

Rule

Base rule class.

CMRule

Bases: Rule

Rule class for cm rule.

__str__

__str__() -> str

Format the rule in the config file's own syntax.

Returns:

Type Description
str

E.g. cm 5.0.

Source code in src/molclusters/config.py
def __str__(self) -> str:
    """Format the rule in the config file's own syntax.

    Returns
    -------
    str
        E.g. ``cm 5.0``.
    """
    return f"cm {self.dist}"

HBRule

Bases: Rule

Rule class for hb rule.

Besides the donor-acceptor distance and D-H-A angle cutoffs, it holds the criteria that pick out the rule's hydrogens (mass strictly between h_mass_min and h_mass_max, charge above h_charge_min) and acceptors (charge below a_charge_max). The defaults are MDAnalysis' HydrogenBondAnalysis.guess_hydrogens()/guess_acceptors() ones.

__str__

__str__() -> str

Format the rule in the config file's own syntax, defaults filled in.

Returns:

Type Description
str

E.g. hb d 3.5 a 150.0 hmin 0.9 hmax 1.1 hq 0.3 aq -0.5.

Source code in src/molclusters/config.py
def __str__(self) -> str:
    """Format the rule in the config file's own syntax, defaults filled in.

    Returns
    -------
    str
        E.g. ``hb d 3.5 a 150.0 hmin 0.9 hmax 1.1 hq 0.3 aq -0.5``.
    """
    return " ".join(
        [
            "hb",
            *(f"{flag} {getattr(self, attr)}" for flag, attr in _HB_FLAGS.items()),
        ]
    )

MolClsConfig

Bases: BaseSettings

Settings class for input parameters.

lammps_timestep_ps property

lammps_timestep_ps: float | None

The LAMMPS timestep size, in ps.

Returns:

Type Description
float | None

lammps_timestep converted to ps, or None if it isn't set.

resname_for_resid

resname_for_resid(resid: int) -> str | None

Look up the resname configured for a LAMMPS molecule id.

Parameters:

Name Type Description Default
resid int

A LAMMPS molecule id (the mol column), which MDAnalysis exposes as resid for a topology parsed from a LAMMPS DATA file.

required

Returns:

Type Description
str | None

The name assigned to resid via lammps_resnames, or None if it isn't covered.

Source code in src/molclusters/config.py
def resname_for_resid(self, resid: int) -> str | None:
    """
    Look up the resname configured for a LAMMPS molecule id.

    Parameters
    ----------
    resid : int
        A LAMMPS molecule id (the ``mol`` column), which MDAnalysis exposes as
        `resid` for a topology parsed from a LAMMPS DATA file.

    Returns
    -------
    str | None
        The name assigned to `resid` via `lammps_resnames`, or None if it isn't
        covered.
    """
    idx = bisect.bisect_right(self._lammps_resid_starts, resid) - 1
    if idx < 0:
        return None

    start, end, name = self._lammps_resid_ranges[idx]
    return name if start <= resid <= end else None

is_ignored_composition

is_ignored_composition(resnames: Iterable[str]) -> bool

Check whether a cluster composition is configured to be ignored.

Parameters:

Name Type Description Default
resnames Iterable[str]

The residue names making up a candidate cluster.

required

Returns:

Type Description
bool

True if this composition matches an ignore_composition entry.

Source code in src/molclusters/config.py
def is_ignored_composition(self, resnames: Iterable[str]) -> bool:
    """
    Check whether a cluster composition is configured to be ignored.

    Parameters
    ----------
    resnames : Iterable[str]
        The residue names making up a candidate cluster.

    Returns
    -------
    bool
        True if this composition matches an `ignore_composition` entry.
    """
    return frozenset(resnames) in self._ignore_composition

describe

describe() -> str

Describe the configuration the analysis actually runs with.

Unlike the raw config file, this shows the parsed rules with their defaults filled in, the solute keyword expanded, and the LAMMPS molecule id ranges merged, so a user can check what was understood.

Returns:

Type Description
str

A multi-line, human-readable description.

Source code in src/molclusters/config.py
def describe(self) -> str:
    """
    Describe the configuration the analysis actually runs with.

    Unlike the raw config file, this shows the parsed rules with their
    defaults filled in, the `solute` keyword expanded, and the LAMMPS molecule
    id ranges merged, so a user can check what was understood.

    Returns
    -------
    str
        A multi-line, human-readable description.
    """

    def names(values: Iterable[str] | None) -> str:
        return ", ".join(values) if values else "none"

    lines = ["rules (distances in angstrom, angles in degrees):"]
    lines += [
        f"  {mi} - {mj}: {self._rules[mi, mj]}" for mi, mj in sorted(self._rules)
    ]
    lines.append(f"solute: {names(self.solute)}")
    lines.append(f"solvent: {names(self.solvent)}")
    lines.append(f"nucleus: {names(self.nucleus)}")
    lines.append(
        "follow: "
        + (
            "solute (one solute-<resid>.gro per solute)"
            if self._follow_solute
            else "none"
        )
    )

    comps = self.ignore_composition or []
    lines.append(
        f"ignore_composition: {'; '.join(' + '.join(sorted(c)) for c in comps) or 'none'}"
    )
    lines.append(f"distance_backend: {self.distance_backend} (cm rules only)")

    # both import this module
    from .analysis.builtins import BUILTINS
    from .report import report_name

    running = [b.name for b in BUILTINS if b.runs(self)]
    off = [b.name for b in BUILTINS if not self.analyses.get(b.name, True)]
    lines.append(
        f"analyses: {', '.join(running) or 'none'}"
        + (f" (turned off: {', '.join(off)})" if off else "")
    )
    lines.append(
        f"report_compression: {self.report_compression} "
        f"({report_name(self.report_compression)})"
    )

    ranges = [
        f"{name} = {start}" if start == end else f"{name} = {start}-{end}"
        for start, end, name in self._lammps_resid_ranges
    ]
    lines.append(f"lammps_resnames: {', '.join(ranges) or 'none'}")
    lines.append(
        "lammps_timestep: none (LAMMPS dump times are step numbers)"
        if self._lammps_timestep_ps is None
        else f"lammps_timestep: {self._lammps_timestep_ps:g} ps"
    )

    return "\n".join(lines)

read_config

read_config(path: Path | str) -> MolClsConfig

Read a configuration file and return an MolClsConfig object.

Support JSON, YAML, and TOML file formats based on the file extension.

Parameters:

Name Type Description Default
path Path

The path to the configuration file.

required

Returns:

Type Description
MolClsConfig

An instance of MolClsConfig initialized with the data from the configuration file.

Raises:

Type Description
ValueError

If the file extension is not supported.

Notes
  • Supported file extensions: '.json', '.yaml', '.yml', '.toml'.
  • Requires the corresponding libraries for each file type (e.g., json, yaml, tomllib).
Source code in src/molclusters/config.py
def read_config(path: Path | str) -> MolClsConfig:
    """
    Read a configuration file and return an MolClsConfig object.

    Support JSON, YAML, and TOML file formats based on the file extension.

    Parameters
    ----------
    path : Path
        The path to the configuration file.

    Returns
    -------
    MolClsConfig
        An instance of MolClsConfig initialized with the data from the configuration file.

    Raises
    ------
    ValueError
        If the file extension is not supported.

    Notes
    -----
    - Supported file extensions: '.json', '.yaml', '.yml', '.toml'.
    - Requires the corresponding libraries for each file type (e.g., `json`, `yaml`, `tomllib`).
    """
    path = Path(path)

    logger.info(f"Reading config file: {path.resolve()}")

    match path.suffix:
        case ".json":
            with path.open() as f:
                data = json.load(f)
        case ".yaml" | ".yml":
            with path.open() as f:
                data = yaml.safe_load(f)
        case ".toml":
            with path.open("rb") as f:
                data = tomllib.load(f)
        case _:
            raise ValueError(
                f"Unsupported config type: {path}. Only JSON, YAML, and TOML supported."
            )

    logger.debug(f"Config file contents: {data}")

    # `extra="ignore"` would otherwise silently drop a misspelled key
    unknown = sorted(data.keys() - MolClsConfig.model_fields.keys())
    if unknown:
        logger.warning(
            f"Ignoring unknown config key(s) {unknown}; check them for typos. "
            f"Known keys: {sorted(MolClsConfig.model_fields)}."
        )

    return MolClsConfig(**data)