Source code for htpolynet.external.ambertools

"""Manages execution of AmberTools antechamber, parmchk2.

Author: Cameron F. Abrams <cfa22@drexel.edu>
"""
import hashlib
import logging
import os
import shutil

import parmed

from ..core.coordinates import Coordinates
from ..external.command import run

logger = logging.getLogger(__name__)


[docs] def GAFFParameterize(inputPrefix, outputPrefix, input_structure_format='mol2', ambertools=None): """Manages execution of antechamber, parmchk2, and tleap to generate GAFF parameters, then converts the result to Gromacs gro/top files via parmed. Args: inputPrefix (str): basename of input structure file outputPrefix (str): basename of output files input_structure_format (str): format of input structure file, defaults to 'mol2'; 'pdb' is other option ambertools (dict | None): ambertools configuration directives, defaults to None Raises: parmed.exceptions.GromacsError: if parmed fails to convert tleap output """ ambertools = ambertools or {} chargemethod = ambertools.get('charge_method', 'bcc') logger.info(f'AmberTools> generating GAFF parameters from {inputPrefix}.{input_structure_format}') structin = f'{inputPrefix}.{input_structure_format}' mol2out = f'{outputPrefix}.mol2' frcmodout = f'{outputPrefix}.frcmod' groOut = f'{outputPrefix}.gro' topOut = f'{outputPrefix}.top' itpOut = f'{outputPrefix}.itp' # antechamber overwrites its input when input and output share the same path new_structin = structin if structin == mol2out: new_structin = f'{inputPrefix}-input.{input_structure_format}' logger.debug(f'AmberTools> Antechamber overwrites input {structin}; backing up to {new_structin}') shutil.copy(structin, new_structin) run(f'antechamber -j 4 -fi {input_structure_format} -fo mol2 -c {chargemethod}' f' -at gaff -i {new_structin} -o {mol2out} -pf Y -nc 0 -eq 1 -pl 10', quiet=False) logger.debug(f'AmberTools> Antechamber generated {mol2out}') run(f'parmchk2 -i {mol2out} -o {frcmodout} -f mol2 -s gaff', quiet=False) # Antechamber ignores SUBSTRUCTURE records, so patch the antechamber output # mol2 with the original resName/resNum before passing it to tleap. if input_structure_format == 'mol2': orig = Coordinates.read_mol2(new_structin) ac_out = Coordinates.read_mol2(mol2out) ac_out.A[['resName', 'resNum']] = orig.A[['resName', 'resNum']] leap_coords = ac_out else: leap_coords = Coordinates.read_mol2(mol2out) # tleap rejects filenames containing 'e' (treats them as scientific notation), # so hash the prefix to get a safe temporary name. leapprefix = hashlib.shake_128(outputPrefix.encode('utf-8')).hexdigest(16).replace('e', 'x') logger.debug(f'Replacing "{outputPrefix}" with hash "{leapprefix}" for tleap input files.') leap_coords.write_mol2(f'{leapprefix}.mol2') shutil.copy(frcmodout, f'{leapprefix}.frcmod') with open(f'{inputPrefix}-tleap.in', 'w') as f: f.write('\n'.join([ 'source leaprc.gaff', # Load parmchk2's patches BEFORE checking the molecule, otherwise # `check` reports any GAFF-coverage gaps (e.g. h5-ce-n2 on cyanate- # ester dimers) as `Error!`, the run-wrapper's override needle # fires, and we abort even though tleap would have completed once # the frcmod was loaded. f'loadamberparams {leapprefix}.frcmod', f'mymol = loadmol2 {leapprefix}.mol2', 'check mymol', f'saveamberparm mymol {leapprefix}-tleap.top {leapprefix}-tleap.crd', 'quit', '', ])) run(f'tleap -f {inputPrefix}-tleap.in', override=('Error!', 'Unspecified tleap error')) os.remove(f'{leapprefix}.frcmod') shutil.move(f'{leapprefix}.mol2', mol2out) shutil.move(f'{leapprefix}-tleap.top', f'{outputPrefix}-tleap.top') shutil.move(f'{leapprefix}-tleap.crd', f'{outputPrefix}-tleap.crd') try: file = parmed.load_file(f'{outputPrefix}-tleap.top', xyz=f'{outputPrefix}-tleap.crd') logger.debug(f'Writing {groOut}, {topOut}, and {itpOut}') file.save(groOut, overwrite=True) file.save(topOut, parameters=itpOut, overwrite=True) except Exception as m: logger.error('Unspecified parmed error') raise parmed.exceptions.GromacsError(m) from m