Beispiel #1
0
def writeParameters(mol,
                    parameters,
                    qm,
                    method,
                    netcharge,
                    outdir,
                    original_coords=None):

    paramDir = os.path.join(outdir, 'parameters', method.name,
                            _qm_method_name(qm))
    os.makedirs(paramDir, exist_ok=True)

    typemap = None
    extensions = ('mol2', 'pdb', 'coor')

    if method == FFTypeMethod.CGenFF_2b6:
        extensions += ('psf', 'rtf', 'prm')

        # TODO: remove?
        f = open(os.path.join(paramDir, "input.namd"), "w")
        tmp = '''parameters mol.prm
paraTypeCharmm on
coordinates mol.pdb
bincoordinates mol.coor
temperature 0
timestep 0
1-4scaling 1.0
exclude scaled1-4
outputname .out
outputenergies 1
structure mol.psf
cutoff 20.
switching off
stepsPerCycle 1
rigidbonds none
cellBasisVector1 50. 0. 0.
cellBasisVector2 0. 50. 0.
cellBasisVector3 0. 0. 50.
run 0'''
        print(tmp, file=f)
        f.close()

    elif method in (FFTypeMethod.GAFF, FFTypeMethod.GAFF2):
        # types need to be remapped because Amber FRCMOD format limits the type to characters
        # writeFrcmod does this on the fly and returns a mapping that needs to be applied to the mol
        # TODO: get rid of this mapping
        frcFile = os.path.join(paramDir, 'mol.frcmod')
        typemap = getAtomTypeMapping(parameters)
        writeFRCMOD(mol, parameters, frcFile, typemap=typemap)
        logger.info('Write FRCMOD file: %s' % frcFile)

        tleapFile = os.path.join(paramDir, 'tleap.in')
        with open(tleapFile, 'w') as file_:
            file_.write('loadAmberParams mol.frcmod\n')
            file_.write('A = loadMol2 mol.mol2\n')
            file_.write('saveAmberParm A structure.prmtop mol.crd\n')
            file_.write('quit\n')
        logger.info('Write tleap input file: %s' % tleapFile)

        # TODO: remove?
        f = open(os.path.join(paramDir, "input.namd"), "w")
        tmp = '''parmfile structure.prmtop
amber on
coordinates mol.pdb
bincoordinates mol.coor
temperature 0
timestep 0
1-4scaling 0.83333333
exclude scaled1-4
outputname .out
outputenergies 1
cutoff 20.
switching off
stepsPerCycle 1
rigidbonds none
cellBasisVector1 50. 0. 0.
cellBasisVector2 0. 50. 0.
cellBasisVector3 0. 0. 50.
run 0'''
        print(tmp, file=f)
        f.close()

    else:
        raise NotImplementedError

    def remapAtomTypes(mol):
        tmpmol = mol
        if typemap is not None:
            tmpmol = mol.copy()
            tmpmol.atomtype[:] = [
                typemap[atomtype] for atomtype in mol.atomtype
            ]
        return tmpmol

    tmpmol = remapAtomTypes(mol)

    for ext in extensions:
        file_ = os.path.join(paramDir, "mol." + ext)
        if ext == 'prm':
            writePRM(mol, parameters, file_)
        elif ext == 'rtf':
            writeRTF(mol, parameters, netcharge, file_)
        else:
            tmpmol.write(file_)
        logger.info('Write %s file: %s' % (ext.upper(), file_))

    if original_coords is not None:
        molFile = os.path.join(paramDir, 'mol-orig.mol2')
        tmpmol.coords = original_coords
        tmpmol.write(molFile)
        logger.info(
            'Write MOL2 file (with original coordinates): {}'.format(molFile))
Beispiel #2
0
def main_parameterize(arguments=None):

    args = getArgumentParser().parse_args(args=arguments)

    if not os.path.exists(args.filename):
        raise ValueError('File %s cannot be found' % args.filename)

    method_map = {
        'GAFF': FFTypeMethod.GAFF,
        'GAFF2': FFTypeMethod.GAFF2,
        'CGENFF': FFTypeMethod.CGenFF_2b6
    }
    methods = [method_map[method] for method in args.forcefield]

    # Get RTF and PRM file names
    rtfFile, prmFile = None, None
    if args.rtf_prm:
        rtfFile, prmFile = args.rtf_prm

    # Create a queue for QM
    if args.queue == 'local':
        queue = LocalCPUQueue()
    elif args.queue == 'Slurm':
        queue = SlurmQueue(_configapp=args.code.lower())
    elif args.queue == 'LSF':
        queue = LsfQueue(_configapp=args.code.lower())
    elif args.queue == 'PBS':
        queue = PBSQueue()  # TODO: configure
    elif args.queue == 'AceCloud':
        queue = AceCloudQueue()  # TODO: configure
        queue.groupname = args.groupname
        queue.hashnames = True
    else:
        raise NotImplementedError

    # Override default ncpus
    if args.ncpus:
        logger.info('Overriding ncpus to {}'.format(args.ncpus))
        queue.ncpu = args.ncpus
    if args.memory:
        logger.info('Overriding memory to {}'.format(args.memory))
        queue.memory = args.memory

    # Create a QM object
    if args.code == 'Psi4':
        qm = Psi4()
    elif args.code == 'Gaussian':
        qm = Gaussian()
    else:
        raise NotImplementedError

    # This is for debugging only!
    if args.fake_qm:
        qm = FakeQM2()
        logger.warning('Using FakeQM')

    # Get rotatable dihedral angles
    mol = Molecule(args.filename)
    mol = canonicalizeAtomNames(mol)
    mol, equivalents, all_dihedrals = getEquivalentsAndDihedrals(mol)
    netcharge = args.charge if args.charge is not None else int(
        round(np.sum(mol.charge)))

    if args.list:
        print('\n === Parameterizable dihedral angles of {} ===\n'.format(
            args.filename))
        with open('torsions.txt', 'w') as fh:
            for dihedral in all_dihedrals:
                dihedral_name = '-'.join(mol.name[list(dihedral[0])])
                print('  {}'.format(dihedral_name))
                fh.write(dihedral_name + '\n')
        print()
        sys.exit(0)

    # Set up the QM object
    qm.theory = args.theory
    qm.basis = args.basis
    qm.solvent = args.environment
    qm.queue = queue
    qm.charge = netcharge

    # Select which dihedrals to fit
    parameterizable_dihedrals = [list(dih[0]) for dih in all_dihedrals]
    if len(args.dihedral) > 0:
        all_dihedral_names = [
            '-'.join(mol.name[list(dihedral[0])]) for dihedral in all_dihedrals
        ]
        parameterizable_dihedrals = []
        for dihedral_name in args.dihedral:
            if dihedral_name not in all_dihedral_names:
                raise ValueError(
                    '%s is not recognized as a rotatable dihedral angle' %
                    dihedral_name)
            parameterizable_dihedrals.append(
                list(
                    all_dihedrals[all_dihedral_names.index(dihedral_name)][0]))

    # Print arguments
    print('\n === Arguments ===\n')
    for key, value in vars(args).items():
        print('{:>12s}: {:s}'.format(key, str(value)))

    print('\n === Parameterizing %s ===\n' % args.filename)
    for method in methods:
        print(" === Fitting for %s ===\n" % method.name)
        printReport(mol, netcharge, equivalents, all_dihedrals)

        parameters, mol = fftype(mol,
                                 method=method,
                                 rtfFile=rtfFile,
                                 prmFile=prmFile,
                                 netcharge=args.charge)
        if isinstance(qm, FakeQM2):
            qm._parameters = parameters

        # Copy the molecule to preserve initial coordinates
        orig_coor = mol.coords.copy()

        # Update B3LYP to B3LYP-D3
        # TODO: this is silent and not documented stuff
        if qm.theory == 'B3LYP':
            qm.correction = 'D3'

        # Update basis sets
        # TODO: this is silent and not documented stuff
        if netcharge < 0 and qm.solvent == 'vacuum':
            if qm.basis == '6-31G*':
                qm.basis = '6-31+G*'
            if qm.basis == 'cc-pVDZ':
                qm.basis = 'aug-cc-pVDZ'
            logger.info('Changing basis sets to %s' % qm.basis)

        # Minimize molecule
        if args.minimize:
            print('\n == Minimizing ==\n')
            mol = minimize(mol, qm, args.outdir)

        # Fit ESP charges
        if args.fit_charges:
            print('\n == Fitting ESP charges ==\n')

            # Set random number generator seed
            if args.seed:
                np.random.seed(args.seed)

            # Select the atoms with fixed charges
            fixed_atom_indices = getFixedChargeAtomIndices(
                mol, args.fix_charge)

            # Fit ESP charges
            mol, _, esp_charges, qm_dipole = fitCharges(
                mol, qm, args.outdir, fixed=fixed_atom_indices)

            # Print dipoles
            logger.info('QM dipole: %f %f %f; %f' % tuple(qm_dipole))
            mm_dipole = getDipole(mol)
            if np.all(np.isfinite(mm_dipole)):
                logger.info('MM dipole: %f %f %f; %f' % tuple(mm_dipole))
            else:
                logger.warning(
                    'MM dipole cannot be computed. Check if elements are detected correctly.'
                )

        # Fit dihedral angle parameters
        if args.fit_dihedral:
            print('\n == Fitting dihedral angle parameters ==\n')

            # Set random number generator seed
            if args.seed:
                np.random.seed(args.seed)

            # Invent new atom types for dihedral atoms
            mol, originaltypes = inventAtomTypes(mol,
                                                 parameterizable_dihedrals,
                                                 equivalents)
            parameters = recreateParameters(mol, originaltypes, parameters)
            parameters = createMultitermDihedralTypes(parameters)
            if isinstance(qm, FakeQM2):
                qm._parameters = parameters

            # Fit the parameters
            fitDihedrals(mol,
                         qm,
                         method,
                         parameters,
                         all_dihedrals,
                         parameterizable_dihedrals,
                         args.outdir,
                         geomopt=args.optimize_dihedral)

        # Output the FF parameters
        print('\n == Writing results ==\n')
        writeParameters(mol,
                        parameters,
                        qm,
                        method,
                        netcharge,
                        args.outdir,
                        original_coords=orig_coor)

        # Write energy file
        energyFile = os.path.join(args.outdir, 'parameters', method.name,
                                  _qm_method_name(qm), 'energies.txt')
        printEnergies(mol, parameters, energyFile)
        logger.info('Write energy file: %s' % energyFile)
Beispiel #3
0
def _fit_charges(mol, args, qm, atom_types):

    from htmd.charge import fitGasteigerCharges, fitChargesWithAntechamber, fitESPCharges, symmetrizeCharges
    from htmd.parameterization.util import guessBondType, getFixedChargeAtomIndices, getDipole, _qm_method_name
    from htmd.parameterization.detect import detectEquivalentAtoms

    logger.info('=== Atomic charge fitting ===')
    logger.info('Method: {}'.format(args.charge_type))

    if args.charge_type == 'None':

        # TODO move to argument validation
        if len(args.fix_charge) > 0:
            logger.warning('Flag --fix-charge does not have effect!')

        logger.info('Atomic charges are taken from {}'.format(args.filename))

    elif args.charge_type == 'Gasteiger':

        # TODO move to argument validation
        if len(args.fix_charge) > 0:
            logger.warning('Flag --fix-charge does not have effect!')

        # TODO move to _prepare_molecule
        if np.any(mol.bondtype == "un"):
            logger.info('Guessing bond types')
            mol = guessBondType(mol)

        mol = fitGasteigerCharges(mol, atom_types=atom_types)

        charge = int(round(np.sum(mol.charge)))
        if args.charge != charge:
            raise RuntimeError(
                'Molecular charge is set to {}, but Gasteiger atomic charges add up to {}.'
                .format(args.charge, charge))

    elif args.charge_type == 'AM1-BCC':

        # TODO move to argument validation
        if len(args.fix_charge) > 0:
            logger.warning('Flag --fix-charge does not have effect!')

        mol = fitChargesWithAntechamber(mol, type='bcc', molCharge=args.charge)
        mol = symmetrizeCharges(mol)

    elif args.charge_type == 'ESP':

        # Detect equivalent atom groups
        logger.info('Equivalent atom groups:')
        atom_groups = [
            group for group in detectEquivalentAtoms(mol)[0] if len(group) > 1
        ]
        for atom_group in atom_groups:
            logger.info('    {}'.format(', '.join(mol.name[list(atom_group)])))

        # Select the atoms with fixed charges
        fixed_atom_indices = getFixedChargeAtomIndices(mol, args.fix_charge)

        # Create an ESP directory
        espDir = os.path.join(args.outdir, "esp", _qm_method_name(qm))
        os.makedirs(espDir, exist_ok=True)

        charge = int(round(np.sum(mol.charge)))
        if args.charge != charge:
            logger.warning(
                'Molecular charge is set to {}, but atomic charges add up to {}'
                ''.format(args.charge, charge))
            if len(args.fix_charge) > 0:
                raise RuntimeError(
                    'Flag --fix-charge cannot be used when atomic charges are inconsistent with passed '
                    'molecular charge {}'.format(args.charge))
            mol.charge[:] = args.charge / mol.numAtoms

        # Set random number generator seed
        if args.seed:
            np.random.seed(args.seed)

        # Fit ESP charges
        mol, extra = fitESPCharges(mol, qm, espDir, fixed=fixed_atom_indices)

        # Print QM dipole
        logger.info(
            'QM dipole: {:6.3f} {:6.3f} {:6.3f}; total: {:6.3f}'.format(
                *extra['qm_dipole']))

    else:
        raise ValueError()

    # Print MM dipole
    mm_dipole = getDipole(mol)
    logger.info('MM dipole: {:6.3f} {:6.3f} {:6.3f}; total: {:6.3f}'.format(
        *mm_dipole))

    # Print the new charges
    logger.info('Atomic charges:')
    for name, charge in zip(mol.name, mol.charge):
        logger.info('   {:4s}: {:6.3f}'.format(name, charge))
    logger.info('Molecular charge: {:6.3f}'.format(np.sum(mol.charge)))

    return mol
Beispiel #4
0
def main_parameterize(arguments=None):

    args = getArgumentParser().parse_args(args=arguments)

    if not os.path.exists(args.filename):
        raise ValueError('File %s cannot be found' % args.filename)

    method_map = {'GAFF': FFTypeMethod.GAFF, 'GAFF2': FFTypeMethod.GAFF2, 'CGENFF': FFTypeMethod.CGenFF_2b6}
    methods = [method_map[method] for method in args.forcefield]

    # Get RTF and PRM file names
    rtfFile, prmFile = None, None
    if args.rtf_prm:
        rtfFile, prmFile = args.rtf_prm

    # Create a queue for QM
    if args.queue == 'local':
        queue = LocalCPUQueue()
    elif args.queue == 'Slurm':
        queue = SlurmQueue(_configapp=args.code.lower())
    elif args.queue == 'LSF':
        queue = LsfQueue(_configapp=args.code.lower())
    elif args.queue == 'PBS':
        queue = PBSQueue()  # TODO: configure
    elif args.queue == 'AceCloud':
        queue = AceCloudQueue()  # TODO: configure
        queue.groupname = args.groupname
        queue.hashnames = True
    else:
        raise NotImplementedError

    # Override default ncpus
    if args.ncpus:
        logger.info('Overriding ncpus to {}'.format(args.ncpus))
        queue.ncpu = args.ncpus
    if args.memory:
        logger.info('Overriding memory to {}'.format(args.memory))
        queue.memory = args.memory

    # Create a QM object
    if args.code == 'Psi4':
        qm = Psi4()
    elif args.code == 'Gaussian':
        qm = Gaussian()
    else:
        raise NotImplementedError

    # This is for debugging only!
    if args.fake_qm:
        qm = FakeQM2()
        logger.warning('Using FakeQM')

    # Get rotatable dihedral angles
    mol = Molecule(args.filename)
    mol = canonicalizeAtomNames(mol)
    mol, equivalents, all_dihedrals = getEquivalentsAndDihedrals(mol)
    netcharge = args.charge if args.charge is not None else int(round(np.sum(mol.charge)))

    if args.list:
        print('\n === Parameterizable dihedral angles of {} ===\n'.format(args.filename))
        with open('torsions.txt', 'w') as fh:
            for dihedral in all_dihedrals:
                dihedral_name = '-'.join(mol.name[list(dihedral[0])])
                print('  {}'.format(dihedral_name))
                fh.write(dihedral_name+'\n')
        print()
        sys.exit(0)

    # Set up the QM object
    qm.theory = args.theory
    qm.basis = args.basis
    qm.solvent = args.environment
    qm.queue = queue
    qm.charge = netcharge

    # Select which dihedrals to fit
    parameterizable_dihedrals = [list(dih[0]) for dih in all_dihedrals]
    if len(args.dihedral) > 0:
        all_dihedral_names = ['-'.join(mol.name[list(dihedral[0])]) for dihedral in all_dihedrals]
        parameterizable_dihedrals = []
        for dihedral_name in args.dihedral:
            if dihedral_name not in all_dihedral_names:
                raise ValueError('%s is not recognized as a rotatable dihedral angle' % dihedral_name)
            parameterizable_dihedrals.append(list(all_dihedrals[all_dihedral_names.index(dihedral_name)][0]))

    # Print arguments
    print('\n === Arguments ===\n')
    for key, value in vars(args).items():
        print('{:>12s}: {:s}'.format(key, str(value)))

    print('\n === Parameterizing %s ===\n' % args.filename)
    for method in methods:
        print(" === Fitting for %s ===\n" % method.name)
        printReport(mol, netcharge, equivalents, all_dihedrals)

        parameters, mol = fftype(mol, method=method, rtfFile=rtfFile, prmFile=prmFile, netcharge=args.charge)
        if isinstance(qm, FakeQM2):
            qm._parameters = parameters

        # Copy the molecule to preserve initial coordinates
        orig_coor = mol.coords.copy()

        # Update B3LYP to B3LYP-D3
        # TODO: this is silent and not documented stuff
        if qm.theory == 'B3LYP':
            qm.correction = 'D3'

        # Update basis sets
        # TODO: this is silent and not documented stuff
        if netcharge < 0 and qm.solvent == 'vacuum':
            if qm.basis == '6-31G*':
                qm.basis = '6-31+G*'
            if qm.basis == 'cc-pVDZ':
                qm.basis = 'aug-cc-pVDZ'
            logger.info('Changing basis sets to %s' % qm.basis)

        # Minimize molecule
        if args.minimize:
            print('\n == Minimizing ==\n')
            mol = minimize(mol, qm, args.outdir)

        # Fit ESP charges
        if args.fit_charges:
            print('\n == Fitting ESP charges ==\n')

            # Set random number generator seed
            if args.seed:
                np.random.seed(args.seed)

            # Select the atoms with fixed charges
            fixed_atom_indices = getFixedChargeAtomIndices(mol, args.fix_charge)

            # Fit ESP charges
            mol, _, esp_charges, qm_dipole = fitCharges(mol, qm, args.outdir, fixed=fixed_atom_indices)

            # Print dipoles
            logger.info('QM dipole: %f %f %f; %f' % tuple(qm_dipole))
            mm_dipole = getDipole(mol)
            if np.all(np.isfinite(mm_dipole)):
                logger.info('MM dipole: %f %f %f; %f' % tuple(mm_dipole))
            else:
                logger.warning('MM dipole cannot be computed. Check if elements are detected correctly.')

        # Fit dihedral angle parameters
        if args.fit_dihedral:
            print('\n == Fitting dihedral angle parameters ==\n')

            # Set random number generator seed
            if args.seed:
                np.random.seed(args.seed)

            # Invent new atom types for dihedral atoms
            mol, originaltypes = inventAtomTypes(mol, parameterizable_dihedrals, equivalents)
            parameters = recreateParameters(mol, originaltypes, parameters)
            parameters = createMultitermDihedralTypes(parameters)
            if isinstance(qm, FakeQM2):
                qm._parameters = parameters

            # Fit the parameters
            fitDihedrals(mol, qm, method, parameters, all_dihedrals, parameterizable_dihedrals, args.outdir, geomopt=args.optimize_dihedral)

        # Output the FF parameters
        print('\n == Writing results ==\n')
        writeParameters(mol, parameters, qm, method, netcharge, args.outdir, original_coords=orig_coor)

        # Write energy file
        energyFile = os.path.join(args.outdir, 'parameters', method.name, _qm_method_name(qm), 'energies.txt')
        printEnergies(mol, parameters, energyFile)
        logger.info('Write energy file: %s' % energyFile)
Beispiel #5
0
def writeParameters(mol, parameters, qm, method, netcharge, outdir, original_coords=None):

    paramDir = os.path.join(outdir, 'parameters', method.name, _qm_method_name(qm))
    os.makedirs(paramDir, exist_ok=True)

    typemap = None
    extensions = ('mol2', 'pdb', 'coor')

    if method == FFTypeMethod.CGenFF_2b6:
        extensions += ('psf', 'rtf', 'prm')

        # TODO: remove?
        f = open(os.path.join(paramDir, "input.namd"), "w")
        tmp = '''parameters mol.prm
paraTypeCharmm on
coordinates mol.pdb
bincoordinates mol.coor
temperature 0
timestep 0
1-4scaling 1.0
exclude scaled1-4
outputname .out
outputenergies 1
structure mol.psf
cutoff 20.
switching off
stepsPerCycle 1
rigidbonds none
cellBasisVector1 50. 0. 0.
cellBasisVector2 0. 50. 0.
cellBasisVector3 0. 0. 50.
run 0'''
        print(tmp, file=f)
        f.close()

    elif method in (FFTypeMethod.GAFF, FFTypeMethod.GAFF2):
        # types need to be remapped because Amber FRCMOD format limits the type to characters
        # writeFrcmod does this on the fly and returns a mapping that needs to be applied to the mol
        # TODO: get rid of this mapping
        frcFile = os.path.join(paramDir, 'mol.frcmod')
        typemap = getAtomTypeMapping(parameters)
        writeFRCMOD(mol, parameters, frcFile, typemap=typemap)
        logger.info('Write FRCMOD file: %s' % frcFile)

        tleapFile = os.path.join(paramDir, 'tleap.in')
        with open(tleapFile, 'w') as file_:
            file_.write('loadAmberParams mol.frcmod\n')
            file_.write('A = loadMol2 mol.mol2\n')
            file_.write('saveAmberParm A structure.prmtop mol.crd\n')
            file_.write('quit\n')
        logger.info('Write tleap input file: %s' % tleapFile)

        # TODO: remove?
        f = open(os.path.join(paramDir, "input.namd"), "w")
        tmp = '''parmfile structure.prmtop
amber on
coordinates mol.pdb
bincoordinates mol.coor
temperature 0
timestep 0
1-4scaling 0.83333333
exclude scaled1-4
outputname .out
outputenergies 1
cutoff 20.
switching off
stepsPerCycle 1
rigidbonds none
cellBasisVector1 50. 0. 0.
cellBasisVector2 0. 50. 0.
cellBasisVector3 0. 0. 50.
run 0'''
        print(tmp, file=f)
        f.close()

    else:
        raise NotImplementedError

    def remapAtomTypes(mol):
        tmpmol = mol
        if typemap is not None:
            tmpmol = mol.copy()
            tmpmol.atomtype[:] = [typemap[atomtype] for atomtype in mol.atomtype]
        return tmpmol

    tmpmol = remapAtomTypes(mol)

    for ext in extensions:
        file_ = os.path.join(paramDir, "mol." + ext)
        if ext == 'prm':
            writePRM(mol, parameters, file_)
        elif ext == 'rtf':
            writeRTF(mol, parameters, netcharge, file_)
        else:
            tmpmol.write(file_)
        logger.info('Write %s file: %s' % (ext.upper(), file_))

    if original_coords is not None:
        molFile = os.path.join(paramDir, 'mol-orig.mol2')
        tmpmol.coords = original_coords
        tmpmol.write(molFile)
        logger.info('Write MOL2 file (with original coordinates): {}'.format(molFile))