Files
openfe/openfecli/commands/atommapping.py
Alyssa Travitz 2311a2f2d9 ruff formatting part 2 - everything but the tests (#1610)
* add more checks

* make precommit manual

* apply formatting to pyproject.toml

* add TODO

* remove unneeded, add a few more

* add ruff, but turn everything off

* add openfe known first party

* format highest-level files

* first half of openfe protocols

* second half of openfe protocols

* openfe protocols formatting, with alyssa's fmt skips

* add ruff formatter to precommit

* fmt: off all vendored _rfe_utils code

* addressing review comments

* format openfe/utils

* format openfe/setup

* first batch of cli formatting

* second batch of cli formatting

* formatting the rest of openfecli commands

* format openfecli/parameters

* format openfe/storage

* run precommit

* Update openfecli/commands/gather.py

Co-authored-by: Irfan Alibay <IAlibay@users.noreply.github.com>

* update example notebooks branch for v1.7.0 docs changes (#1615)

* bump example notebooks branch

* add ipykernel to env

* roll back to fixing septop branch

* i dont think we want ipykernel

* bump to tmp_fix_docs branch

* point to branch revert-237-v1.7_cookbooks

* point to latest example notebooks release

* remove colab button, point to updated example notebooks, reorg landing page (#1618)

* remove colab button from example notebooks in docs

* point to example notebooks 2025.10.2

* replace 'try' with CLI

---------

Co-authored-by: Irfan Alibay <IAlibay@users.noreply.github.com>
2025-10-24 14:09:45 -07:00

105 lines
3.2 KiB
Python

# This code is part of OpenFE and is licensed under the MIT license.
# For details, see https://github.com/OpenFreeEnergy/openfe
import click
from openfecli import OFECommandPlugin
from openfecli.parameters import MOL, MAPPER, OUTPUT_FILE_AND_EXT
def allow_two_molecules(ctx, param, value):
"""click callback to require that --mol is specified exactly twice"""
if len(value) != 2:
raise click.BadParameter("Must specify --mol exactly twice.")
return value
@click.command("atommapping", short_help="Check the atom mapping of a given pair of ligands")
@MOL.parameter(
multiple=True,
callback=allow_two_molecules,
required=True,
help=MOL.kwargs["help"] + " Must be specified twice.",
)
@MAPPER.parameter(required=True)
@OUTPUT_FILE_AND_EXT.parameter(help=OUTPUT_FILE_AND_EXT.kwargs["help"] + " (PNG format)")
def atommapping(mol, mapper, output):
"""
This provides tools for looking at a specific atommapping.
"""
# note that the text of the docstring will be the help when you run
# `openfe atommapping --help`.
molA_str, molB_str = mol
molA = MOL.get(molA_str)
molB = MOL.get(molB_str)
mapper_cls = MAPPER.get(mapper)
mapper_obj = mapper_cls()
file, ext = OUTPUT_FILE_AND_EXT.get(output)
if file:
atommapping_visualize_main(mapper_obj, molA, molB, file, ext)
else:
atommapping_print_dict_main(mapper_obj, molA, molB)
def generate_mapping(mapper, molA, molB):
"""Utility method to extract a single mapping from a mapper.
Parameters
----------
mapper : :class:`.LigandAtomMapper`
the mapper to use to generate the mapping
molA, molB : :class:`.SmallMoleculeComponent`
molecules to map between
Returns
------
:class:`.LigandAtomMapping` :
the mapping generated by the mapper; errors if there is not exactly
one mapping generated
"""
mappings = list(mapper.suggest_mappings(molA, molB))
if len(mappings) != 1:
raise click.UsageError(
f"Found {len(mappings)} mappings; this command requires a mapper "
"to provide exactly 1 mapping"
)
return mappings[0]
def atommapping_print_dict_main(mapper, molA, molB):
"""Main function for generating and printing out the mapping"""
mapping = generate_mapping(mapper, molA, molB)
print(mapping.componentA_to_componentB)
def atommapping_visualize_main(mapper, molA, molB, file, ext):
from rdkit.Chem import Draw
from gufe.visualization import mapping_visualization as vis
mapping = generate_mapping(mapper, molA, molB)
ext_to_artist = {
"png": Draw.rdMolDraw2D.MolDraw2DCairo(600, 300, 300, 300),
}
try:
artist = ext_to_artist[ext]
except KeyError:
raise click.BadParameter(
f"Unknown file format: '{ext}'. The following formats are "
"supported: " + ", ".join([f"'{ext}'" for ext in ext_to_artist])
)
contents = vis.draw_mapping(
mapping.componentA_to_componentB,
mapping.componentA.to_rdkit(),
mapping.componentB.to_rdkit(),
d2d=artist,
)
file.write(contents)
PLUGIN = OFECommandPlugin(
command=atommapping,
section="hidden",
requires_ofe=(0, 0, 1),
)