示例#1
0
def fftype(mol,
           rtfFile=None,
           prmFile=None,
           method='GAFF2',
           acCharges=None,
           tmpDir=None,
           netcharge=None):
    """
    Assing atom types and force field parameters for a given molecule.
    Additionally, atom masses and improper dihedral are set.
    Optionally, atom charges can be set if `acCharges` is set (see below).

    The assignment can be done:
      1. For CHARMM CGenFF_2b6 with MATCH (method = 'CGenFF_2b6');
      2. For AMBER GAFF with antechamber (method = 'GAFF');
      3. For AMBER GAFF2 with antechamber (method = 'GAFF2');

    Parameters
    ----------
    mol : Molecule
        Molecule to use for the assignment
    rtfFile : str
        Path to a RTF file from which to read the topology
    prmFile : str
        Path to a PRM file from which to read the parameters
    method : str
        Atomtyping assignment method.
        Use :func:`fftype.listFftypemethods <htmd.parameterization.fftype.listFftypemethods>` to get a list of available
        methods.
        Default: :func:`fftype.defaultFftypemethod <htmd.parameterization.fftype.defaultFftypemethod>`
    acCharges : str
        Optionally assign charges with antechamber. Check `antechamber -L` for available options.
        Note: only works for GAFF and GAFF2.
    tmpDir: str
        Directory for temporary files. If None, a directory is created and
        deleted automatically.
    netcharge : float
        The net charge of the molecule.

    Returns
    -------
    prm : :class:`ParameterSet <parmed.parameters.ParameterSet>` object
        Returns a parmed ParameterSet object with the parameters.
    mol : :class:`Molecule <htmd.molecule.molecule.Molecule>` object
        The modified Molecule object with the matching atom types for the ParameterSet
    """

    import parmed

    if method not in fftypemethods:
        raise ValueError('Invalid method {}. Available methods {}'.format(
            method, ','.join(fftypemethods)))

    if method == 'CGenFF_2b6' and acCharges:
        raise ValueError('acCharges')

    if netcharge is None:
        netcharge = int(round(np.sum(mol.charge)))
        logger.warning(
            'Molecular charge is set to {} by adding up the atomic charges'.
            format(netcharge))

    if rtfFile and prmFile:

        from htmd.parameterization.readers import readRTF

        logger.info('Reading FF parameters from {} and {}'.format(
            rtfFile, prmFile))
        prm = parmed.charmm.CharmmParameterSet(rtfFile, prmFile)
        names, elements, atomtypes, charges, masses, impropers = readRTF(
            rtfFile)

    else:
        logger.info('Assigning atom types with {}'.format(method))

        renamed_mol = _canonicalizeAtomNames(mol)

        # Create a temporary directory
        with TemporaryDirectory() as tmpdir:

            # HACK to keep the files
            tmpdir = tmpdir if tmpDir is None else tmpDir
            logger.debug('Temporary directory: {}'.format(tmpdir))

            if method in ('GAFF', 'GAFF2'):

                from htmd.molecule.molecule import Molecule
                from htmd.parameterization.readers import readPREPI, readFRCMOD

                # Write the molecule to a file
                renamed_mol.write(os.path.join(tmpdir, 'mol.mol2'))

                atomtype = method.lower()

                # Set arguments
                cmd = [
                    'antechamber', '-at', atomtype, '-nc',
                    str(netcharge), '-fi', 'mol2', '-i', 'mol.mol2', '-fo',
                    'prepi', '-o', 'mol.prepi'
                ]
                if acCharges is not None:
                    cmd += ['-c', acCharges]

                # Run antechamber
                with TemporaryFile() as stream:
                    if subprocess.call(
                            cmd, cwd=tmpdir, stdout=stream,
                            stderr=stream) != 0:
                        raise RuntimeError('"antechamber" failed')
                    stream.seek(0)
                    for line in stream.readlines():
                        logger.debug(line)

                # Set arguments
                cmd = [
                    'parmchk2', '-f', 'prepi', '-s', atomtype, '-i',
                    'mol.prepi', '-o', 'mol.frcmod', '-a', 'Y'
                ]

                # Run parmchk2
                with TemporaryFile() as stream:
                    if subprocess.call(
                            cmd, cwd=tmpdir, stdout=stream,
                            stderr=stream) != 0:
                        raise RuntimeError('"parmchk2" failed')
                    stream.seek(0)
                    for line in stream.readlines():
                        logger.debug(line)

                # Check if antechamber did changes in atom names (and suggest the user to fix the names)
                acmol = Molecule(os.path.join(tmpdir, 'NEWPDB.PDB'),
                                 type='pdb')
                acmol.name = np.array([n.upper()
                                       for n in acmol.name]).astype(np.object)
                changed_mol_acmol = np.setdiff1d(renamed_mol.name, acmol.name)
                changed_acmol_mol = np.setdiff1d(acmol.name, renamed_mol.name)
                if len(changed_mol_acmol) != 0 or len(changed_acmol_mol) != 0:
                    raise RuntimeError(
                        'Initial atom names {} were changed by antechamber to {}. '
                        'This probably means that the start of the atom name does not match '
                        'element symbol. '
                        'Please check the molecule.'
                        ''.format(','.join(changed_mol_acmol),
                                  ','.join(changed_acmol_mol)))

                # Read the results
                prm = parmed.amber.AmberParameterSet(
                    os.path.join(tmpdir, 'mol.frcmod'))
                names, atomtypes, charges, impropers = readPREPI(
                    renamed_mol, os.path.join(tmpdir, 'mol.prepi'))
                masses, elements = readFRCMOD(
                    atomtypes, os.path.join(tmpdir, 'mol.frcmod'))

            elif method == 'CGenFF_2b6':

                from htmd.parameterization.readers import readRTF

                # Write the molecule to a file
                renamed_mol.write(os.path.join(tmpdir, 'mol.pdb'))

                # Set arguments
                cmd = [
                    'match-typer', '-charge',
                    str(netcharge), '-forcefield', 'top_all36_cgenff_new',
                    'mol.pdb'
                ]

                # Run match-type
                with TemporaryFile() as stream:
                    if subprocess.call(
                            cmd, cwd=tmpdir, stdout=stream,
                            stderr=stream) != 0:
                        raise RuntimeError('"match-typer" failed')
                    stream.seek(0)
                    for line in stream.readlines():
                        logger.debug(line)

                prm = parmed.charmm.CharmmParameterSet(
                    os.path.join(tmpdir, 'mol.rtf'),
                    os.path.join(tmpdir, 'mol.prm'))
                names, elements, atomtypes, charges, masses, impropers = readRTF(
                    os.path.join(tmpdir, 'mol.rtf'))

            else:
                raise ValueError('Invalid method {}'.format(method))

        assert np.all(renamed_mol.name == names)

    assert np.all(mol.element == elements)

    mol = mol.copy()
    mol.atomtype = atomtypes
    mol.masses = masses
    mol.impropers = impropers
    if acCharges is not None:
        mol.charge = charges

    return prm, mol
示例#2
0
def fftype(mol,
           rtfFile=None,
           prmFile=None,
           method=FFTypeMethod.CGenFF_2b6,
           acCharges=None,
           tmpDir=None,
           netcharge=None):
    """
    Function to assign atom types and force field parameters for a given molecule.

    The assigment can be done:
      1. For CHARMM CGenFF_2b6 with MATCH (method = FFTypeMethod.CGenFF_2b6);
      2. For AMBER GAFF with antechamber (method = FFTypeMethod.GAFF);
      3. For AMBER GAFF2 with antechamber (method = FFTypeMethod.GAFF2);

    Parameters
    ----------
    mol : FFMolecule
        Molecule to use for the assigment
    rtfFile : str
        Path to a RTF file from which to read the topology
    prmFile : str
        Path to a PRM file from which to read the parameters
    method : FFTypeMethod
        Assigment method
    acCharges : str
        Optionally assign charges with antechamber. Check `antechamber -L` for available options. Caution: This will
        overwrite any charges defined in the mol2 file.
    tmpDir: str
        Directory for temporary files. If None, a directory is created and
        deleted automatically.
    netcharge : float
        The net charge of the molecule.

    Returns
    -------
    prm : :class:`ParameterSet<parmed.parameters.ParameterSet>` object
        Returns a parmed ParameterSet object with the parameters.
    mol : :class:`Molecule <htmd.molecule.molecule.Molecule>` object
        The modified Molecule object with the matching atom types for the ParameterSet
    """
    if netcharge is None:
        netcharge = np.sum(mol.charge)
    netcharge = int(round(netcharge))

    if rtfFile and prmFile:
        logger.info('Reading FF parameters from {} and {}'.format(
            rtfFile, prmFile))
        prm = parmed.charmm.CharmmParameterSet(rtfFile, prmFile)
        names, elements, atomtypes, charges, masses, impropers = readRTF(
            rtfFile)
        # addParmedResidue(prm, names, elements, atomtypes, charges, impropers)
    else:
        logger.info('Assigned atom types with {}'.format(method.name))
        # Find the executables
        if method == FFTypeMethod.GAFF or method == FFTypeMethod.GAFF2:
            antechamber_binary = shutil.which("antechamber")
            if not antechamber_binary:
                raise RuntimeError("antechamber executable not found")
            parmchk2_binary = shutil.which("parmchk2")
            if not parmchk2_binary:
                raise RuntimeError("parmchk2 executable not found")
        elif method == FFTypeMethod.CGenFF_2b6:
            match_binary = shutil.which("match-typer")
            if not match_binary:
                raise RuntimeError("match-typer executable not found")
        else:
            raise ValueError('method')

        # Create a temporary directory
        with TemporaryDirectory() as tmpdir:
            # HACK to keep the files
            tmpdir = tmpdir if tmpDir is None else tmpDir

            if method == FFTypeMethod.GAFF or method == FFTypeMethod.GAFF2:
                # Write the molecule to a file
                mol.write(os.path.join(tmpdir, 'mol.mol2'))

                # Run antechamber
                if method == FFTypeMethod.GAFF:
                    atomtype = "gaff"
                elif method == FFTypeMethod.GAFF2:
                    atomtype = "gaff2"
                else:
                    raise ValueError('method')
                cmd = [
                    antechamber_binary, '-at', atomtype, '-nc',
                    str(netcharge), '-fi', 'mol2', '-i', 'mol.mol2', '-fo',
                    'prepi', '-o', 'mol.prepi'
                ]
                if acCharges is not None:
                    cmd += ['-c', acCharges]
                returncode = subprocess.call(cmd, cwd=tmpdir)
                if returncode != 0:
                    raise RuntimeError('"antechamber" failed')

                # Run parmchk2
                returncode = subprocess.call([
                    parmchk2_binary, '-f', 'prepi', '-i', 'mol.prepi', '-o',
                    'mol.frcmod', '-a', 'Y'
                ],
                                             cwd=tmpdir)
                if returncode != 0:
                    raise RuntimeError('"parmchk2" failed')

                # Read the results
                prm = parmed.amber.AmberParameterSet(
                    os.path.join(tmpdir, 'mol.frcmod'))
                names, elements, atomtypes, charges, masses, impropers = readPREPI(
                    mol, os.path.join(tmpdir, 'mol.prepi'))
                # addParmedResidue(prm, names, elements, atomtypes, charges, impropers)
            elif method == FFTypeMethod.CGenFF_2b6:

                # Write the molecule to a file
                mol.write(os.path.join(tmpdir, 'mol.pdb'))

                # Run match-type
                returncode = subprocess.call([
                    match_binary, '-charge',
                    str(netcharge), '-forcefield', 'top_all36_cgenff_new',
                    'mol.pdb'
                ],
                                             cwd=tmpdir)
                if returncode != 0:
                    raise RuntimeError('"match-typer" failed')

                prm = parmed.charmm.CharmmParameterSet(
                    os.path.join(tmpdir, 'mol.rtf'),
                    os.path.join(tmpdir, 'mol.prm'))
                names, elements, atomtypes, charges, masses, impropers = readRTF(
                    os.path.join(tmpdir, 'mol.rtf'))
                # addParmedResidue(prm, names, elements, atomtypes, charges, impropers)
            else:
                raise ValueError('Invalide method {}'.format(method))

    # Substituting values from the read-in topology
    mol.name = names
    mol.element = elements
    mol.atomtype = atomtypes
    mol.charge = charges
    mol.impropers = impropers
    if len(mol.masses) == 0:
        mol.masses = masses

    return prm, mol