Exemplo n.º 1
0
    def handle_bulk(self):
        """
        Getting and setting configs for bulk runs is a little different, requiring this method.
        The configs are taken from the .csv, then the .ini, then the terminal.
        This is repeated for each molecule in the bulk run, then Execute is called.

        Configs cannot be changed between molecule analyses as config data is
        only loaded once at the start; -restart is required for that.
        """

        csv_file = self.args.bulk_run
        # mol_data_from_csv handles defaults if no argument is given
        bulk_data = mol_data_from_csv(csv_file)

        names = list(bulk_data)

        home = os.getcwd()

        for name in names:
            printf(f'Analysing: {name}\n')

            # Get pdb from smiles or name if no smiles is given
            if bulk_data[name]['smiles'] is not None:
                smiles_string = bulk_data[name]['smiles']
                rdkit = RDKit()
                self.file = rdkit.smiles_to_pdb(smiles_string, name)

            else:
                self.file = f'{name}.pdb'

            # Initialise molecule, ready to add configs to it
            self.molecule = Ligand(self.file)

            # Read each row in bulk data and set it to the molecule object
            for key, val in bulk_data[name].items():
                setattr(self.molecule, key, val)

            setattr(self.molecule, 'skip', [])

            # Using the config file from the .csv, gather the .ini file configs
            file_configs = Configure.load_config(self.molecule.config)
            for key, val in file_configs.items():
                setattr(self.molecule, key, val)

            # Handle configs which are changed by terminal commands
            for key, val in vars(self.args).items():
                if val is not None:
                    setattr(self.molecule, key, val)

            # Now that all configs are stored correctly: execute.
            Execute(self.molecule)

            os.chdir(home)

        sys.exit(
            'Bulk analysis complete.\nUse QUBEKit -progress to view the completion progress of your molecules'
        )
Exemplo n.º 2
0
    def __init__(self):
        # First make sure the config folder has been made missing for conda and pip
        home = str(Path.home())
        config_folder = f'{home}/QUBEKit_configs/'
        if not os.path.exists(config_folder):
            os.makedirs(config_folder)
            print(f'Making config folder at: {home}')

        self.args = self.parse_commands()

        # If it's a bulk run, handle it separately
        # TODO Add .sdf as possible bulk_run, not just .csv
        if self.args.bulk_run:
            self.handle_bulk()

        if self.args.restart is not None:
            # Find the pickled checkpoint file and load it as the molecule
            try:
                self.molecule = unpickle()[self.args.restart]
            except FileNotFoundError:
                raise FileNotFoundError('No checkpoint file found!')
        else:
            if self.args.smiles:
                self.file = RDKit().smiles_to_pdb(*self.args.smiles)
            else:
                self.file = self.args.input

            # Initialise molecule
            self.molecule = Ligand(self.file)

        # Find which config file is being used
        self.molecule.config = self.args.config_file

        # Handle configs which are in a file
        file_configs = Configure.load_config(self.molecule.config)
        for name, val in file_configs.items():
            setattr(self.molecule, name, val)

        # Although these may be None always, they need to be explicitly set anyway.
        setattr(self.molecule, 'restart', None)
        setattr(self.molecule, 'end', None)
        setattr(self.molecule, 'skip', None)

        # Handle configs which are changed by terminal commands
        for name, val in vars(self.args).items():
            if val is not None:
                setattr(self.molecule, name, val)

        # If restarting put the molecule back into the checkpoint file with the new configs
        if self.args.restart is not None:
            self.molecule.pickle(state=self.args.restart)
        # Now that all configs are stored correctly: execute.
        Execute(self.molecule)
Exemplo n.º 3
0
    def __init__(self):
        self.args = self.parse_commands()

        # If it's a bulk run, handle it separately
        # TODO Add .sdf as possible bulk_run, not just .csv
        if self.args.bulk_run:
            self.handle_bulk()

        if self.args.restart:
            self.file = [
                file for file in os.listdir(os.getcwd()) if '.pdb' in file
            ][0]
        else:
            if self.args.smiles:
                self.file = RDKit.smiles_to_pdb(self.args.smiles)
            else:
                self.file = self.args.input

        # Initialise molecule
        self.molecule = Ligand(self.file)

        # Find which config file is being used
        self.molecule.config = self.args.config_file

        # Handle configs which are in a file
        file_configs = Configure.load_config(self.molecule.config)
        for name, val in file_configs.items():
            setattr(self.molecule, name, val)

        # Although these may be None always, they need be explicitly set anyway.
        setattr(self.molecule, 'restart', None)
        setattr(self.molecule, 'end', None)
        setattr(self.molecule, 'skip', None)

        # Handle configs which are changed by terminal commands
        for name, val in vars(self.args).items():
            if val is not None:
                setattr(self.molecule, name, val)

        # Now that all configs are stored correctly: execute.
        Execute(self.molecule)
Exemplo n.º 4
0
    def parse_commands():
        """
        Parses commands from the terminal using argparse.

        Contains classes for handling actions as well as simple arg parsers for config changes.
        """

        # Action classes
        class SetupAction(argparse.Action):
            """The setup action class that is called when setup is found in the command line."""
            def __call__(self, pars, namespace, values, option_string=None):
                """This function is executed when setup is called."""

                choice = int(
                    input(
                        'You can now edit config files using QUBEKit, choose an option to continue:\n'
                        '1) Edit a config file\n'
                        '2) Create a new master template\n'
                        '3) Make a normal config file\n'
                        '4) Cancel\n>'))

                if choice == 1:
                    inis = Configure.show_ini()
                    name = input(
                        f'Enter the name or number of the config file to edit\n'
                        f'{"".join(f"{inis.index(ini)}:{ini}    " for ini in inis)}\n>'
                    )
                    # make sure name is right
                    if name in inis:
                        Configure.ini_edit(name)
                    else:
                        Configure.ini_edit(inis[int(name)])

                elif choice == 2:
                    Configure.ini_writer('master_config.ini')
                    Configure.ini_edit('master_config.ini')

                elif choice == 3:
                    name = input(
                        'Enter the name of the config file to create\n>')
                    Configure.ini_writer(name)
                    Configure.ini_edit(name)

                else:
                    sys.exit(
                        'Cancelling setup; no changes made. '
                        'If you accidentally entered the wrong key, restart with QUBEKit -setup'
                    )

                sys.exit()

        class CSVAction(argparse.Action):
            """The csv creation class run when the csv option is used."""
            def __call__(self, pars, namespace, values, option_string=None):
                """This function is executed when csv is called."""

                generate_bulk_csv(values)
                sys.exit()

        class ProgressAction(argparse.Action):
            """Run the pretty progress function to get the progress of all running jobs."""
            def __call__(self, pars, namespace, values, option_string=None):
                """This function is executed when progress is called."""

                pretty_progress()
                sys.exit()

        class TorsionMakerAction(argparse.Action):
            """Help the user make a torsion scan file."""
            def __call__(self, pars, namespace, values, option_string=None):
                """This function is executed when Torsion maker is called."""
                # load in the ligand molecule
                mol = Ligand(values)

                # Prompt the user for the scan order
                scanner = TorsionScan(mol)
                scanner.find_scan_order()

                # Write out the scan file
                with open('QUBE_torsions.txt', 'w+') as qube:
                    qube.write(
                        '# dihedral definition by atom indices starting from 1\n#  i      j      k      l\n'
                    )
                    for scan in mol.scan_order:
                        scan_di = mol.dihedrals[scan][0]
                        qube.write(
                            f'  {scan_di[0]:2}     {scan_di[1]:2}     {scan_di[2]:2}     {scan_di[3]:2}\n'
                        )
                printf('QUBE_torsions.txt made.')

                sys.exit()

        def string_to_bool(string):
            """Convert a string to a bool for argparse use when casting to bool"""
            return True if string.lower() in ['true', 't', 'yes', 'y'
                                              ] else False

        # TODO Get intro (in a nice way) from the README.md
        # TODO Add command-line args for every possible config change
        intro = ''
        parser = argparse.ArgumentParser(
            prog='QUBEKit',
            formatter_class=argparse.RawDescriptionHelpFormatter,
            description=intro)

        # Add all of the command line options in the arg parser
        parser.add_argument(
            '-c',
            '--charge',
            default=0,
            type=int,
            help='Enter the charge of the molecule, default 0.')
        parser.add_argument('-m',
                            '--multiplicity',
                            default=1,
                            type=int,
                            help='Enter the multiplicity of the '
                            'molecule, default 1.')
        parser.add_argument(
            '-threads',
            '--threads',
            type=int,
            help=
            'Number of threads used in various stages of analysis, especially for engines like '
            'PSI4, Gaussian09, etc. Value is given as an int.')
        parser.add_argument(
            '-memory',
            '--memory',
            type=int,
            help=
            'Amount of memory used in various stages of analysis, especially for engines like '
            'PSI4, Gaussian09, etc. Value is given as an int, e.g. 6GB is simply 6.'
        )
        parser.add_argument(
            '-ddec',
            '--ddec_version',
            choices=[3, 6],
            type=int,
            help=
            'Enter the ddec version for charge partitioning, does not effect ONETEP partitioning.'
        )
        parser.add_argument(
            '-geo',
            '--geometric',
            choices=[True, False],
            type=string_to_bool,
            help=
            'Turn on geometric to use this during the qm optimisations, recommended.'
        )
        parser.add_argument(
            '-bonds',
            '--bonds_engine',
            choices=['psi4', 'g09'],
            help='Choose the QM code to calculate the bonded terms.')
        parser.add_argument(
            '-charges',
            '--charges_engine',
            choices=['onetep', 'chargemol'],
            help='Choose the method to do the charge partioning.')
        parser.add_argument(
            '-density',
            '--density_engine',
            choices=['onetep', 'g09', 'psi4'],
            help=
            'Enter the name of the QM code to calculate the electron density of the molecule.'
        )
        parser.add_argument(
            '-solvent',
            '--solvent',
            choices=[True, False],
            type=string_to_bool,
            help='Enter whether or not you would like to use a solvent.')
        # Maybe separate into known solvents and IPCM constants?
        parser.add_argument(
            '-convergence',
            '--convergence',
            choices=['GAU', 'GAU_TIGHT', 'GAU_VERYTIGHT'],
            help='Enter the convergence criteria for the optimisation.')
        parser.add_argument(
            '-param',
            '--parameter_engine',
            choices=['xml', 'antechamber', 'openff'],
            help=
            'Enter the method of where we should get the initial molecule parameters from, '
            'if xml make sure the xml has the same name as the pdb file.')
        parser.add_argument(
            '-mm',
            '--mm_opt_method',
            default='openmm',
            choices=['openmm', 'rdkit_mff', 'rdkit_uff'],
            help='Enter the mm optimisation method for pre qm optimisation.')
        parser.add_argument(
            '-config',
            '--config_file',
            default='default_config',
            choices=Configure.show_ini(),
            help=
            'Enter the name of the configuration file you wish to use for this run from the list '
            'available, defaults to master.')
        parser.add_argument(
            '-theory',
            '--theory',
            help='Enter the name of the qm theory you would like to use.')
        parser.add_argument('-basis',
                            '--basis',
                            help='Enter the basis set you would like to use.')
        parser.add_argument('-restart',
                            '--restart',
                            choices=[
                                'parametrise', 'mm_optimise', 'qm_optimise',
                                'hessian', 'mod_sem', 'density', 'charges',
                                'lennard_jones', 'torsion_scan',
                                'torsion_optimise'
                            ],
                            default=None,
                            help='Enter the restart point of a QUBEKit job.')
        parser.add_argument('-end',
                            '-end',
                            choices=[
                                'mm_optimise', 'qm_optimise', 'hessian',
                                'mod_sem', 'density', 'charges',
                                'lennard_jones', 'torsion_scan',
                                'torsion_optimise', 'finalise'
                            ],
                            default=None,
                            help='Enter the end point of the QUBEKit job.')
        parser.add_argument(
            '-progress',
            '--progress',
            nargs='?',
            const=True,
            help='Get the current progress of a QUBEKit single or bulk job.',
            action=ProgressAction)
        parser.add_argument(
            '-skip',
            '--skip',
            nargs='+',
            choices=[
                'mm_optimise', 'qm_optimise', 'hessian', 'mod_sem', 'density',
                'charges', 'lennard_jones', 'torsion_scan', 'torsion_optimise',
                'finalise'
            ],
            default=None,
            help='Option to skip certain stages of the execution.')
        parser.add_argument(
            '-tor_test',
            '--torsion_test',
            default=False,
            choices=[True, False],
            type=string_to_bool,
            help=
            'Enter True if you would like to run a torsion test on the chosen torsions.'
        )
        parser.add_argument(
            '-tor_make',
            '--torsion_maker',
            action=TorsionMakerAction,
            help=
            'Allow QUBEKit to help you make a torsion input file for the given molecule'
        )

        # Add mutually exclusive groups to stop wrong combinations of options,
        # e.g. setup should not be ran with another command
        groups = parser.add_mutually_exclusive_group()
        groups.add_argument(
            '-setup',
            '--setup_config',
            nargs='?',
            const=True,
            help='Setup a new configuration or edit an existing one.',
            action=SetupAction)
        groups.add_argument(
            '-sm',
            '--smiles',
            help='Enter the smiles string of a molecule as a starting point.')
        groups.add_argument(
            '-bulk',
            '--bulk_run',
            help=
            'Enter the name of the csv file to run as bulk, bulk will use smiles unless it finds '
            'a molecule file with the same name.')
        groups.add_argument(
            '-csv',
            '--csv_filename',
            action=CSVAction,
            help=
            'Enter the name of the csv file you would like to create for bulk runs.'
        )
        groups.add_argument(
            '-i',
            '--input',
            help='Enter the molecule input pdb file (only pdb so far!)')
        groups.add_argument(
            '-log',
            '--log',
            type=str,
            help=
            'Enter a name to tag working directories with. Can be any alphanumeric string.'
        )

        return parser.parse_args()
Exemplo n.º 5
0
            def __call__(self, pars, namespace, values, option_string=None):
                """This function is executed when setup is called."""

                choice = int(
                    input(
                        'You can now edit config files using QUBEKit, choose an option to continue:\n'
                        '1) Edit a config file\n'
                        '2) Create a new master template\n'
                        '3) Make a normal config file\n'
                        '4) Cancel\n>'))

                if choice == 1:
                    inis = Configure.show_ini()
                    name = input(
                        f'Enter the name or number of the config file to edit\n'
                        f'{"".join(f"{inis.index(ini)}:{ini}    " for ini in inis)}\n>'
                    )
                    # make sure name is right
                    if name in inis:
                        Configure.ini_edit(name)
                    else:
                        Configure.ini_edit(inis[int(name)])

                elif choice == 2:
                    Configure.ini_writer('master_config.ini')
                    Configure.ini_edit('master_config.ini')

                elif choice == 3:
                    name = input(
                        'Enter the name of the config file to create\n>')
                    Configure.ini_writer(name)
                    Configure.ini_edit(name)

                else:
                    sys.exit(
                        'Cancelling setup; no changes made. '
                        'If you accidentally entered the wrong key, restart with QUBEKit -setup'
                    )

                sys.exit()