Exemplo n.º 1
0
def _get_queue(args):

    logger.info('=== Computation queue ===')

    # Create a queue
    logger.info('Queue type: {}'.format(args.queue))
    if args.queue == 'local':
        from htmd.queues.localqueue import LocalCPUQueue
        queue = LocalCPUQueue()
    elif args.queue == 'Slurm':
        from htmd.queues.slurmqueue import SlurmQueue
        queue = SlurmQueue(_configapp=args.code.lower())
    elif args.queue == 'LSF':
        from htmd.queues.lsfqueue import LsfQueue
        queue = LsfQueue(_configapp=args.code.lower())
    elif args.queue == 'PBS':
        from htmd.queues.pbsqueue import PBSQueue
        queue = PBSQueue()  # TODO: configure
    elif args.queue == 'AceCloud':
        from htmd.queues.acecloudqueue import AceCloudQueue
        queue = AceCloudQueue()  # TODO: configure
        queue.groupname = args.groupname
        queue.hashnames = True
    else:
        raise AssertionError()

    # Configure the queue
    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

    return queue
Exemplo n.º 2
0
    def __init__(self):
        from moleculekit.molecule import Molecule

        super().__init__()

        self._arg('molecule', ':class: `moleculekit.molecule.Molecule`', 'Molecule',
                  default=None, validator=val.Object(Molecule), required=True)
        self._arg('charge', 'int', 'Charge of the molecule in electron charges', default=None,
                  validator=val.Number(int, 'ANY'), required=True)
        self._arg('multiplicity', 'int', 'Multiplicity of the molecule',
                  default=1, validator=val.Number(int, 'POS'))
        self._arg('theory', 'str', 'Level of theory',
                  default='B3LYP', validator=val.String(), valid_values=self.THEORIES)
        self._arg('correction', 'str', 'Empirical dispersion correction',
                  default='none', validator=val.String(), valid_values=self.CORRECTIONS)
        self._arg('basis', 'str', 'Basis set',
                  default='6-31G*', validator=val.String(), valid_values=self.BASIS_SETS)
        self._arg('solvent', 'str', 'Implicit solvent',
                  default='vacuum', validator=val.String(), valid_values=self.SOLVENTS)
        self._arg('esp_points', ':class: `numpy.ndarray`', 'Point to calculate ESP',
                  default=None, nargs='*')  # TODO implement validator
        self._arg('optimize', 'boolean', 'Optimize geometry',
                  default=False, validator=val.Boolean())
        self._arg('restrained_dihedrals', ':class: `numpy.ndarray`',
                  'List of restrained dihedrals (0-based indices)',
                  default=None, nargs='*')  # TODO implement validator
        self._arg('queue', ':class:`SimQueue <htmd.queues.simqueue.SimQueue>` object',
                  'Queue object used to run simulations',
                  default=LocalCPUQueue())
        self._arg('directory', 'str', 'Working directory',
                  default='.', validator=val.String())
Exemplo n.º 3
0
    def test_adaptive(self):
        from sklearn.cluster import MiniBatchKMeans
        from htmd.queues.localqueue import LocalCPUQueue
        from moleculekit.projections.metricdistance import MetricDistance

        import numpy as np
        import random
        np.random.seed(
            0)  # Needed for the clustering to always give same results
        random.seed(0)

        md = AdaptiveBandit()
        md.app = LocalCPUQueue()
        md.generatorspath = 'generators'
        md.inputpath = 'input'
        md.datapath = 'data'
        md.coorname = 'input.coor'
        md.filter = True
        md.filtersel = 'all'

        md.clustmethod = MiniBatchKMeans
        md.projection = MetricDistance('protein resid 173 and name CA',
                                       'resname BEN and name C1 C2 C3 C7')
        md.ticadim = 2
        md.nmin = 1
        md.nmax = 2
        md.nepochs = 9999
        md.nframes = 1000000

        md.reward_method = 'mean'
        md.exploration = 0.01
        md.actionspace = 'tica'
        md.actionpool = -1
        md.recluster = False

        md.save = True
        md.dryrun = True
        md.run()
Exemplo n.º 4
0
Arquivo: cli.py Projeto: 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)
Exemplo n.º 5
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)
Exemplo n.º 6
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)
Exemplo n.º 7
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)