Source code for rgpycrumbs.geom.plt_rings

#!/usr/bin/env python3
"""Draw primitive rings and the bridges between them.

.. versionadded:: 1.11.0

The picture is ``chemparseplot.plot.rings``: one xyzrender hull per face
``pydseams.yoda.ringNetwork`` returns, and a dashed stroke on each bridge
whose ends both lie on a returned ring. Query atoms, when given, stay where
the file puts them. They are a label for a Wannier centre, not the coordinate
that enters a mean-square displacement.

Prefer the dispatcher::

    rgpycrumbs geom plt-rings molecule.sdf rings.svg
    rgpycrumbs geom plt-rings molecule.xyz rings.svg --cutoff 1.95

``uv run --script`` on this file is the same isolated environment the
dispatcher builds. An XYZ file has no bonds, so it needs ``--cutoff`` inside
the gap between the longest bond and the shortest nonbonded contact. 3.5
angstrom is the ice neighbour list and is the wrong graph for a thiophene.
"""

# /// script
# requires-python = ">=3.12"
# dependencies = [
#   "click>=8.1",
#   "numpy>=1.26",
#   "matplotlib>=3.8.2",
#   "pydseamslib>=2.9.1",
#   "xyzrender>=0.3.8",
#   "chemparseplot>=1.11",
# ]
# ///

from __future__ import annotations

from pathlib import Path

import click
from chemparseplot.plot.rings import (
    format_report,
    read_structure,
    render_primitive_rings,
)

try:
    from rgpycrumbs._aux import warn_on_direct_script_import
except ImportError:  # pragma: no cover - script copy without the package root
[docs] warn_on_direct_script_import = None
if warn_on_direct_script_import is not None: warn_on_direct_script_import(__name__, "rgpycrumbs geom plt-rings") @click.command() @click.argument( "structure", type=click.Path(exists=True, dir_okay=False, path_type=Path), ) @click.argument("output", type=click.Path(dir_okay=False, path_type=Path)) @click.option( "--cutoff", type=float, default=None, help="Heavy-atom cutoff in angstrom. Required for XYZ, refused for SDF.", ) @click.option( "--max-depth", type=int, default=6, show_default=True, help="Largest ring ringNetwork generates.", ) @click.option( "--queries", type=click.Path(exists=True, dir_okay=False, path_type=Path), default=None, help="XYZ of query points, drawn where they sit.", ) @click.option("--config", default="paton", show_default=True, help="xyzrender preset.") @click.option("--canvas-size", type=int, default=900, show_default=True)
[docs] def main( structure: Path, output: Path, *, cutoff: float | None, max_depth: int, queries: Path | None, config: str, canvas_size: int, ) -> None: """Render primitive rings for an SDF or an XYZ file.""" symbols, coords, bonds = read_structure(structure) query_coords = None if queries is not None: _qsym, query_coords, _qbonds = read_structure(queries) if bonds is not None and cutoff is not None: raise SystemExit("this file already has bonds; omit --cutoff") if bonds is None and cutoff is None: raise SystemExit("XYZ has no bonds; pass --cutoff inside the bond/nonbonded gap") report, assignments = render_primitive_rings( symbols, coords, output, bonds=bonds, cutoff=cutoff, max_depth=max_depth, queries=query_coords, config=config, canvas_size=canvas_size, ) click.echo(format_report(report, assignments))
if __name__ == "__main__": main()