# Ligand Preparation Prepare ligand parameters, reuse them across structures, and calculate interaction scores. Preparation produces protonated coordinates, MMFF94 charges, TMol atom types, and Cartesian bonded parameters in a new `ParameterDatabase`. The resulting types support scoring and minimization. ## Supported chemistry `prepare_ligands=True` also handles nonstandard polymers and covalent groups. Backbone type is inferred from connectivity. The following fixtures use this same preparation path: | Class | Example fixture | | --- | --- | | Modified alpha-amino acid (PTM) | `ncaa_fixtures/collagen_hyp_1bkv.cif` (4-hydroxyproline), `ncaa_fixtures/phosphopeptide_5ema.cif` (phosphoserine) | | N-substituted backbone | `ncaa_fixtures/nmethyl_peptide_6mvz.cif` | | Beta and gamma backbones | `ncaa_fixtures/beta_peptide_3c3g.cif`, `ncaa_fixtures/gamma_peptide_1gac.cif` | | D-amino acid | `ncaa_fixtures/6dmz_mod_d.cif` (with its L mirror, `6dmz_mod_l.cif`) | | Terminal caps | `ncaa_fixtures/capped_peptide_ace_nme.cif`, `capped_peptide_ace_nh2.cif` | | Chromophore / macrocycle | `ncaa_fixtures/chromophore_nrq_3svu.cif` | | Modified DNA | `ncaa_fixtures/na_dna_5mc_1d17.cif` (5-methylcytosine), `na_dna_8og_183d.cif` (8-oxoguanine), `na_dna_ttd_1ttd.cif` (thymine dimer) | | Modified RNA | `ncaa_fixtures/na_rna_psu_1bzt.cif` (pseudouridine), `na_rna_2ome_310d.cif` (2'-O-methyl) | | N- and O-glycans | `covalent_fixtures/nglycan_tree_1ax2.cif`, `covalent_fixtures/oglycan_sia_1g1s.cif` | | Covalent conjugate | `covalent_fixtures/lys_biotin_1bdo.cif` (biotinylated lysine) | | Covalent inhibitor | `covalent_fixtures/peptide_inhibitor_7zv5.cif` | These fixtures are available under `tmol/tests/data` in a source checkout or source distribution; release wheels omit tests and fixtures. For example, load your own mmCIF containing a nonstandard polymer: ```python import torch from tmol.io import atom_array_from_file, pose_stack_from_atom_array array = atom_array_from_file("modified_polymer.cif") pose, context = pose_stack_from_atom_array( array, torch.device("cuda"), prepare_ligands=True, return_context=True ) ``` `context.parameter_database` carries the generated chemistry, and {func}`tmol.ligand.write_params_file` persists it as `.tmol` so a later run reads it back instead of regenerating. Reuse the context across structures that share the same components; see [Reuse a prepared context](#reuse-a-prepared-context). A prepared polymer residue records which backbone it was recognized as in `properties.polymer.backbone_type`: `alpha_aa`, `nonstandard_aa`, `dna`, `rna`, or `nonstandard_na`. That value selects the terminus patches and the torsion terms the residue is scored with, so it is the first thing to check when a residue is not treated the way you expect. ## Preparation functions There are three single-ligand entry points: ```python from tmol.ligand import ( prepare_ligand_from_cif, prepare_ligand_from_mol2, prepare_ligand_from_smiles, ) param_db, co = prepare_ligand_from_mol2("ligand.mol2") param_db, co = prepare_ligand_from_cif("ligand.cif") param_db, co = prepare_ligand_from_smiles("c1ccccc1C(=O)O", res_name="L_1") ``` Each returns a new `(ParameterDatabase, CanonicalOrdering)`. The input database is not mutated. MOL2 and CIF input use their bond tables to derive chemistry; source heavy-atom names are retained. CIF and SMILES preparation apply pH-dependent protonation (default pH 7.4), generate conformers and calculate MMFF94 charges. MOL2 preparation and `write_params_from_mol2()` share three modes: - `mode="auto"` (default): preserve complete inputs with supported, finite, charge-conserving partial charges; otherwise run the preparation pipeline. - `mode="keep"`: require prepared input and preserve its protonation and charges. - `mode="regenerate"`: always run pH-dependent preparation, including for neutralized phosphate inputs with explicit hydrogens and valid partial charges. A MOL2 charge-model label alone does not establish the intended protonation pH. ## Load a complex For full protein-ligand structures, load with Biotite and let `pose_stack_from_atom_array()` prepare every non-standard residue: ```python import torch import tmol from tmol.database import ParameterDatabase from tmol.io import atom_array_from_file, pose_stack_from_atom_array device = torch.device("cuda" if tmol.has_cuda() and torch.cuda.is_available() else "cpu") structure = atom_array_from_file("complex.cif") pose_stack, context = pose_stack_from_atom_array( structure, device, prepare_ligands=True, param_db=ParameterDatabase.get_default(), return_context=True, ) ``` Known residues need only residue names, atom names, and coordinates. For example, prepare a ligand from MOL2 once, then load coordinate-only PDB or CIF complexes: ```python from tmol.io import pose_stack_from_file from tmol.ligand import write_params_from_mol2 write_params_from_mol2( "ligand.mol2", "ligand.tmol", res_name="L_1", mode="auto" ) pose_stack, context = pose_stack_from_file( "complex.pdb", device, ligand_params_files=["ligand.tmol"], return_context=True, ) ``` Use the complex's ligand residue name and matching atom names. Alternatively, pass the database returned by `prepare_ligand_from_mol2()` as `param_db=`. Neither route regenerates known ligand parameters. For mixtures of known and unknown components, add `prepare_ligands=True`: saved parameters are reused, and only unknown chemistry enters preparation. Unknown residues need chemical bond orders from the input or CCD; otherwise the error names the residue that needs more information. PDB `CONECT` records alone do not provide those orders. Name a custom ligand with a code containing `_` (TMol generates `L_1`, `L_2`, ...): no CCD entry has one. A PDB or CIF residue whose atoms or bonds contradict the CCD entry of its name (PDBbind names ligands `MOL` or `ACT`) is read as its own component. ## Reuse a prepared context When scoring many structures that contain the same ligand definitions, build the structure-independent context once: ```python from tmol.io import ( build_context_from_atom_array, pose_stack_from_atom_array, ) context = build_context_from_atom_array(struct0, device, prepare_ligands=True) for structure in structures: pose_stack = pose_stack_from_atom_array(structure, device, context=context) ``` This skips rebuilding the parameter database, canonical ordering, residue type set, and packed block types for every structure. ## Save prepared ligands For manual edits or cold reuse, write `.tmol` params and load them later: ```python from tmol.database import ParameterDatabase from tmol.ligand import prepare_ligands param_db, co = prepare_ligands( atom_array, ph=7.4, params_output="my_ligands.tmol", ) param_db, co = prepare_ligands( atom_array, param_db=ParameterDatabase.get_default(), params_files=["my_ligands.tmol"], ) ``` The same prepared files can be passed through IO: ```python pose_stack, context = pose_stack_from_atom_array( structure, device, prepare_ligands=True, ligand_params_files=["my_ligands.tmol"], return_context=True, ) ``` ## Prepare from SMILES The ligand-prep script writes a TMol `.tmol` parameter bundle: ```bash python scripts/ligand_prep/smiles_to_params.py "" \ --res-name L_1 --ph 7.4 ``` Useful flags include `--no-protonate`, `--heavy-chi-samples`, and `--seed` for a reproducible conformer. `.tmol` is TMol's parameter format. ## Interaction scores Use the ligand-aware score function with an explicit ligand block mask: ```python from tmol.ops import calculate_block_pair_ddg interaction = calculate_block_pair_ddg( pose_stack, ligand_mask, sfxn=sfxn, minimize=False, pack=False, database=context.parameter_database, ) ``` With both flags disabled, the helper returns a fixed-coordinate, weighted cross-mask block-pair interaction score. `minimize` defaults to `True`; `pack=True` additionally invokes local repacking. Set those options only when the resulting refined structure is part of the intended scoring convention. ## Troubleshooting `prepare_ligands=True` is strict by default. An unpreparable ligand raises `LigandPreparationError` rather than silently disappearing. Pass `strict_ligands=False` only when dropping unprepared ligands is acceptable. If pose construction says `Unrecognized 3lc `, the residue code was not in the active `CanonicalOrdering`. Usually this means the ligand was not prepared or was skipped under lenient preparation. If a ligand appears to score as zero, make sure the score function was built from the ligand-extended database: ```python sfxn = beta2016_score_function(device, param_db=context.parameter_database) ``` ## Public API Import supported ligand-preparation functions from `tmol.ligand`. Files whose names begin with an underscore are implementation details and may change without a compatibility alias. The {doc}`ligand API reference ` lists the currently supported exports. ## Examples and reference {doc}`Ligand tutorial ` · {doc}`Ligand API ` · {doc}`Scoring `