Esempio n. 1
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]  # TODO: move into FFMolecule

    # Get RTF and PRM file names
    rtf, prm = None, None
    if args.rtf_prm:
        rtf, prm = 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')

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

    # List rotatable dihedral angles
    if args.list:

        mol = FFMolecule(args.filename,
                         method=methods[0],
                         netcharge=args.charge,
                         rtf=rtf,
                         prm=prm,
                         qm=qm,
                         outdir=args.outdir)
        print('\n === Parameterizable dihedral angles of %s ===\n' %
              args.filename)
        with open('torsions.txt', 'w') as fh:
            for dihedral in mol.getRotatableDihedrals():
                dihedral_name = '%s-%s-%s-%s' % tuple(mol.name[dihedral])
                print('  ' + dihedral_name)
                fh.write(dihedral_name + '\n')
        print()
        sys.exit(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)

        # Create the molecule
        mol = FFMolecule(args.filename,
                         method=method,
                         netcharge=args.charge,
                         rtf=rtf,
                         prm=prm,
                         qm=qm,
                         outdir=args.outdir)
        mol.printReport()

        # Copy the molecule to preserve initial coordinates
        mol_orig = mol.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 mol.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()

        # 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 = []
            for fixed_atom_name in args.fix_charge:

                if fixed_atom_name not in mol.name:
                    raise ValueError(
                        'Atom %s is not found. Check --fix-charge arguments' %
                        fixed_atom_name)

                for aton_index in range(mol.numAtoms):
                    if mol.name[aton_index] == fixed_atom_name:
                        fixed_atom_indices.append(aton_index)
                        logger.info('Charge of atom %s is fixed to %f' %
                                    (fixed_atom_name, mol.charge[aton_index]))

            # Fit ESP charges
            _, qm_dipole = mol.fitCharges(fixed=fixed_atom_indices)

            # Copy the new charges to the original molecule
            mol_orig.charge[:] = mol.charge

            # Print dipoles
            logger.info('QM dipole: %f %f %f; %f' % tuple(qm_dipole))
            mm_dipole = mol.getDipole()
            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)

            # Get all rotatable dihedrals
            all_dihedrals = mol.getRotatableDihedrals()

            # Choose which dihedrals to fit
            dihedrals = []
            all_dihedral_names = [
                '-'.join(mol.name[dihedral]) for dihedral in all_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)
                dihedrals.append(
                    all_dihedrals[all_dihedral_names.index(dihedral_name)])
            dihedrals = dihedrals if len(
                dihedrals
            ) > 0 else all_dihedrals  # Set default to all dihedral angles

            # Fit the parameters
            mol.fitDihedrals(dihedrals, args.optimize_dihedral)

        # Output the FF parameters
        print('\n == Writing results ==\n')
        mol.writeParameters(mol_orig)

        # Write energy file
        energyFile = os.path.join(mol.outdir, 'parameters', method.name,
                                  mol.output_directory_name(), 'energies.txt')
        printEnergies(mol, energyFile)
        logger.info('Write energy file: %s' % energyFile)
Esempio n. 2
0
File: cli.py Progetto: jeiros/htmd
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]  # TODO: move into FFMolecule

    # Get RTF and PRM file names
    rtf, prm = None, None
    if args.rtf_prm:
        rtf, prm = 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
    else:
        raise NotImplementedError

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

    # 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 = FakeQM()
        logger.warning('Using FakeQM')

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

    # List rotatable dihedral angles
    if args.list:

        mol = FFMolecule(args.filename, method=methods[0], netcharge=args.charge, rtf=rtf, prm=prm, qm=qm,
                         outdir=args.outdir)
        print('\n === Parameterizable dihedral angles of %s ===\n' % args.filename)
        with open('torsions.txt', 'w') as fh:
            for dihedral in mol.getRotatableDihedrals():
                dihedral_name = '%s-%s-%s-%s' % tuple(mol.name[dihedral])
                print('  '+dihedral_name)
                fh.write(dihedral_name+'\n')
        print()
        sys.exit(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)

        # Create the molecule
        mol = FFMolecule(args.filename, method=method, netcharge=args.charge, rtf=rtf, prm=prm, qm=qm,
                         outdir=args.outdir)
        mol.printReport()

        # Copy the molecule to preserve initial coordinates
        mol_orig = mol.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 mol.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()

        # 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 = []
            for fixed_atom_name in args.fix_charge:

                if fixed_atom_name not in mol.name:
                    raise ValueError('Atom %s is not found. Check --fix-charge arguments' % fixed_atom_name)

                for aton_index in range(mol.numAtoms):
                    if mol.name[aton_index] == fixed_atom_name:
                        fixed_atom_indices.append(aton_index)
                        logger.info('Charge of atom %s is fixed to %f' % (fixed_atom_name, mol.charge[aton_index]))

            # Fit ESP charges
            score, qm_dipole = mol.fitCharges(fixed=fixed_atom_indices)

            # Print results
            mm_dipole = mol.getDipole()
            score = np.sum((qm_dipole[:3] - mm_dipole[:3])**2)
            print('Charge fitting score: %f\n' % score)
            print('QM dipole: %f %f %f; %f' % tuple(qm_dipole))
            print('MM dipole: %f %f %f; %f' % tuple(mm_dipole))
            print('Dipole Chi^2 score: %f\n' % score)

        # 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)

            # Get all rotatable dihedrals
            all_dihedrals = mol.getRotatableDihedrals()

            # Choose which dihedrals to fit
            dihedrals = []
            all_dihedral_names = ['-'.join(mol.name[dihedral]) for dihedral in all_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)
                dihedrals.append(all_dihedrals[all_dihedral_names.index(dihedral_name)])
            dihedrals = dihedrals if len(dihedrals) > 0 else all_dihedrals  # Set default to all dihedral angles

            # Fit the parameters
            mol.fitDihedrals(dihedrals, args.optimize_dihedral)

        # Output the FF parameters
        print('\n == Writing results ==\n')
        mol.writeParameters(mol_orig)

        # Write energy file
        energyFile = os.path.join(mol.outdir, 'parameters', method.name, mol.output_directory_name(), 'energies.txt')
        printEnergies(mol, energyFile)
        logger.info('Write energy file: %s' % energyFile)
Esempio n. 3
0
def main_parameterize(arguments=None):
    args = cli_parser().parse_args(args=arguments)

    from htmd.parameterization.ffmolecule import FFMolecule, FFEvaluate
    from htmd.parameterization.fftype import FFTypeMethod
    from htmd.qm.qmcalculation import Theory, BasisSet, Execution, Code
    import numpy as np
    import math

    def printEnergies(m):
        print("\n == Diagnostic Energies == ")
        ffe = FFEvaluate(m)
        energies = ffe.evaluate(m.coords[:, :, 0])
        print("")
        print(" Bond     : %f" % (energies['bond']))
        print(" Angle    : %f" % (energies['angle']))
        print(" Dihedral : %f" % (energies['dihedral']))
        print(" Improper : %f" % (energies['improper']))
        print(" Electro  : %f" % (energies['elec']))
        print(" VdW      : %f" % (energies['vdw']))
        print("")

    # Communicate the # of CPUs to use to the QM engine via environment variable
    os.environ['NCPUS'] = str(args.ncpus)

    filename = args.mol
    if not os.path.exists(filename):
        print(
            "File {} not found. Please check that the file exists and that the path is correct."
            .format(filename))
        sys.exit(0)

    if args.qmcode == "Gaussian":
        code = Code.Gaussian
    elif args.qmcode == "PSI4":
        code = Code.PSI4
    elif args.qmcode == "TeraChem":
        code = Code.TeraChem
    else:
        print("Unknown QM code: {}".format(args.qmcode))
        sys.exit(1)

    if args.exec == "inline":
        execution = Execution.Inline
    elif args.exec == "LSF":
        execution = Execution.LSF
    elif args.exec == "Slurm":
        execution = Execution.Slurm
    else:
        print("Unknown execution mode: {}".format(args.exec))
        sys.exit(1)

    if args.forcefield == "CGENFF":
        methods = [FFTypeMethod.CGenFF_2b6]
    elif args.forcefield == "GAFF":
        methods = [FFTypeMethod.GAFF]
    elif args.forcefield == "GAFF2":
        methods = [FFTypeMethod.GAFF2]
    elif args.forcefield == "all":
        methods = [FFTypeMethod.CGenFF_2b6, FFTypeMethod.GAFF2]
    else:
        print("Unknown initial guess force-field: {}".format(args.forcefield))
        sys.exit(1)

    if args.basis == "6-31g-star":
        basis = BasisSet._6_31G_star
    elif args.basis == "cc-pVDZ":
        basis = BasisSet._cc_pVDZ
    else:
        print("Unknown basis {}".format(args.basis))
        sys.exit(1)

    if args.theory == "RHF":
        theory = Theory.RHF
    elif args.theory == "B3LYP":
        theory = Theory.B3LYP
    else:
        print("Unknown theory %s".format(args.theory))
        sys.exit(1)

    if args.vacuum:
        solvent = False
    else:
        solvent = True

    # Just list torsions?
    if args.list:
        print(" === Listing soft torsions of {} ===\n".format(filename))
        mol = FFMolecule(filename=filename,
                         method=methods[0],
                         netcharge=args.charge,
                         rtf=args.rtf,
                         prm=args.prm,
                         basis=basis,
                         theory=theory,
                         solvent=solvent,
                         execution=execution,
                         qmcode=code,
                         outdir=args.outdir)
        dihedrals = mol.getSoftTorsions()
        print("Detected soft torsions:")
        fh = open("torsions.txt", "w")
        for d in dihedrals:
            print("\t{}-{}-{}-{}".format(mol.name[d[0]], mol.name[d[1]],
                                         mol.name[d[2]], mol.name[d[3]]))
            print("{}-{}-{}-{}".format(mol.name[d[0]], mol.name[d[1]],
                                       mol.name[d[2]], mol.name[d[3]]),
                  file=fh)
        fh.close()
        sys.exit(0)

    # Small report
    print(" === List of arguments used ===\n")
    for i in vars(args):
        print('{:>10s}:  {:<10s}'.format(i, str(vars(args)[i])))

    print("\n === Parameterizing {} ===\n".format(filename))
    for method in methods:
        sys.stdout.flush()
        print(" === Fitting for FF %s ===\n" % method.name)

        mol = FFMolecule(filename=filename,
                         method=method,
                         netcharge=args.charge,
                         rtf=args.rtf,
                         prm=args.prm,
                         basis=basis,
                         theory=theory,
                         solvent=solvent,
                         execution=execution,
                         qmcode=code,
                         outdir=args.outdir)
        dihedrals = mol.getSoftTorsions()
        mol_orig = mol.copy()

        if not args.nomin:
            print("\n == Minimizing ==\n")
            mol.minimize()

        sys.stdout.flush()
        if not args.noesp:
            print("\n == Charge fitting ==\n")

            # Select the atoms that are to have frozen charges in the fit
            fixq = []
            if args.freezeq:
                for i in args.freezeq:
                    found = False
                    for d in range(len(mol.name)):
                        if mol.name[d] == i:
                            ni = d
                            print("Fixing charge for atom %s to %f" %
                                  (i, mol.charge[ni]))
                            fixq.append(ni)
                            found = True
                    if not found:
                        raise ValueError(
                            " No atom named %s (--freeze-charge)" % i)

            (score, qm_dipole, mm_dipole) = mol.fitCharges(fixed=fixq)

            rating = "GOOD"
            if score > 1:
                rating = "CHECK"
            if score > 10:
                rating = "BAD"

            print("Charge Chi^2 score : %f : %s" % (score, rating))
            print("QM Dipole   : %f %f %f ; %f" %
                  (qm_dipole[0], qm_dipole[1], qm_dipole[2], qm_dipole[3]))
            print("MM Dipole   : %f %f %f ; %f" %
                  (mm_dipole[0], mm_dipole[1], mm_dipole[2], mm_dipole[3]))
            d = 0.
            for i in range(3):
                x = qm_dipole[i] - mm_dipole[i]
                d = d + x * x

            rating = "GOOD"
            if score > 1:
                rating = "CHECK"
            print("Dipole Chi^2 score : %f : %s" % (d, rating))
            print("")

        sys.stdout.flush()
        # Iterative dihedral fitting
        if not args.notorsion:
            print("\n == Torsion fitting ==\n")

            scores = np.ones(len(dihedrals))
            converged = False
            iteration = 1
            ref_mm = dict()
            while not converged:
                rets = []

                print("\nIteration %d" % iteration)

                last_scores = scores
                scores = np.zeros(len(dihedrals))
                idx = 0
                for d in dihedrals:
                    name = "%s-%s-%s-%s" % (mol.name[d[0]], mol.name[d[1]],
                                            mol.name[d[2]], mol.name[d[3]])
                    if args.torsion == 'all' or name in args.torsion.split(
                            ','):
                        print("\n == Fitting torsion {} ==\n".format(name))
                        try:
                            ret = mol.fitSoftTorsion(d, geomopt=args.geomopt)
                            rets.append(ret)
                            if iteration == 1:
                                ref_mm[name] = ret
                            rating = "GOOD"
                            if ret.chisq > 10:
                                rating = "CHECK"
                            if ret.chisq > 100:
                                rating = "BAD"
                            print("Torsion %s Chi^2 score : %f : %s" %
                                  (name, ret.chisq, rating))
                            sys.stdout.flush()
                            scores[idx] = ret.chisq
                            # Always use the mm_orig from first iteration (unmodified)
                            ret.mm_original = ref_mm[name].mm_original
                            phi_original = ref_mm[name].phi
                            fn = mol.plotTorsionFit(ret,
                                                    phi_original,
                                                    show=False)
                        except Exception as e:
                            print("Error in fitting")
                            print(str(e))
                            raise
                            scores[idx] = 0.
                            pass
                            # print(fn)
                    idx += 1
                # print(scores)
                if iteration > 1:
                    converged = True
                    for j in range(len(scores)):
                        # Check convergence
                        try:
                            relerr = (scores[j] -
                                      last_scores[j]) / last_scores[j]
                        except:
                            relerr = 0.
                        if math.isnan(relerr):
                            relerr = 0.
                        convstr = "- converged"
                        if math.fabs(relerr) > 1.e-2:
                            convstr = ""
                            converged = False
                        print(" Dihedral %d relative error : %f %s" %
                              (j, relerr, convstr))

                iteration += 1

            print(" Fitting converged at iteration %d" % (iteration - 1))
            if len(rets):
                fit = mol.plotConformerEnergies(rets, show=False)
                print("\n Fit of conformer energies: RMS %f Variance %f" %
                      (fit[0], fit[1]))

        printEnergies(mol)

        # Output the ff parameters
        paramdir = os.path.join(args.outdir, "parameters", method.name,
                                mol.output_directory_name())
        print("\n == Output to {} ==\n".format(paramdir))
        try:
            os.makedirs(paramdir, exist_ok=True)
        except:
            raise OSError(
                'Directory {} could not be created. Check if you have permissions.'
                .format(paramdir))

        if method.name == "CGenFF_2b6":
            try:
                mol._rtf.write(os.path.join(paramdir, "mol.rtf"))
                mol._prm.write(os.path.join(paramdir, "mol.prm"))
                for ext in ['psf', 'xyz', 'coor', 'mol2', 'pdb']:
                    mol.write(os.path.join(paramdir, "mol." + ext))
                mol_orig.write(os.path.join(paramdir, "mol-orig.mol2"))
                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()
            except ValueError as e:
                print("Not writing CHARMM PRM: {}".format(str(e)))
        elif method.name == "GAFF" or method.name == "GAFF2":
            try:
                # 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
                typemap = mol._prm.writeFrcmod(
                    mol._rtf, os.path.join(paramdir, "mol.frcmod"))
                for ext in ['coor', 'mol2', 'pdb']:
                    mol.write(os.path.join(paramdir, "mol." + ext),
                              typemap=typemap)
                mol_orig.write(os.path.join(paramdir, "mol-orig.mol2"),
                               typemap=typemap)
                f = open(os.path.join(paramdir, "tleap.in"), "w")
                tmp = '''loadAmberParams mol.frcmod
A = loadMol2 mol.mol2
saveAmberParm A structure.prmtop mol.crd
quit'''
                print(tmp, file=f)
                f.close()
                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()
            except ValueError as e:
                print("Not writing Amber FRCMOD: {}".format(str(e)))
    sys.exit(0)