def wrapper(*args, **kwargs): try: return func(*args, **kwargs) # Any BaseException is logged; KeyboardInterrupt and SystemExit must still be raised (see below) except BaseException as exc: home = getattr(args[0].molecule, "home", None) state = getattr(args[0].molecule, "state", None) if home is None or state is None: raise mol = unpickle()[state] if getattr(args[0].molecule, "verbose"): pretty_print(mol, to_file=True, finished=False) log_file = os.path.join(home, "QUBEKit_log.txt") logger = logger_format(log_file) logger.exception(f"\nAn exception occurred with: {func.__qualname__}\n") print( f"{COLOURS.red}\n\nAn exception occurred with: {func.__qualname__}{COLOURS.end}\n" f"Exception: {exc}\nView the log file for details." ) if isinstance(exc, SystemExit) or isinstance(exc, KeyboardInterrupt): raise # Re-raises the exception if it's not a bulk run. # Even if the exception is not raised, it is still logged. if len(args) >= 1 and hasattr(args[0], "molecule"): if getattr(args[0].molecule, "bulk_run", None) in [False, None]: raise
def __call__(self, *args, **kwargs): try: return self.func(*args, **kwargs) # Any BaseException is logged; KeyboardInterrupt and SystemExit must still be raised (see below) except Exception as exc: home = getattr(args[0].molecule, 'home', None) state = getattr(args[0].molecule, 'state', None) if home is None or state is None: raise mol = unpickle()[state] pretty_print(mol, to_file=True, finished=False) self.log_file = os.path.join(home, 'QUBEKit_log.txt') logger = self.logger_format() logger.exception( f'\nAn exception occurred with: {self.func.__qualname__}\n') print(f'\n\nAn exception occurred with: {self.func.__qualname__}\n' f'Exception: {exc}\nView the log file for details.'.upper()) # Re-raises the exception if it's not a bulk run. # Even if the exception is not raised, it is still logged. if len(args) >= 1 and hasattr(args[0], 'molecule'): if not hasattr(args[0].molecule, 'bulk_run'): raise if args[0].molecule.bulk_run is None: raise
def display_molecule_objects(*names): """ prints the requested molecule objects in a nicely formatted way, easy to copy elsewhere. To be used via the -display command :param names: list of strings where each item is the name of a molecule object such as 'basis' or 'coords' """ try: molecule = unpickle()["finalise"] except KeyError: print( "QUBEKit encountered an error during execution; returning the initial molecule objects." ) molecule = unpickle()["parametrise"] for name in names: result = getattr(molecule, name, None) if result is not None: print(f"{name}: {repr(result)}") else: print( f"Invalid molecule object: {name}. Please check the log file for the data you require." )
def stage_wrapper(self, start_key, begin_log_msg='', fin_log_msg='', torsion_options=None): """ Firstly, check if the stage start_key is in self.order; this tells you if the stage should be called or not. If it isn't in self.order: - Do nothing If it is: - Unpickle the ligand object at the start_key stage - Write to the log that something's about to be done (if specified) - Make (if not restarting) and / or move into the working directory for that stage - Do the thing - Move back out of the working directory for that stage - Write to the log that something's been done (if specified) - Pickle the ligand object again with the next_key marker as its stage """ mol = unpickle()[start_key] # Set the state for logging any exceptions should they arise mol.state = start_key # if we have a torsion options dictionary pass it to the molecule if torsion_options is not None: mol = self.store_torsions(mol, torsion_options) skipping = False if self.order[start_key] == self.skip: printf(f'{COLOURS.blue}Skipping stage: {start_key}{COLOURS.end}') append_to_log(f'skipping stage: {start_key}') skipping = True else: if begin_log_msg: printf(f'{begin_log_msg}...', end=' ') home = os.getcwd() folder_name = f'{str(self.immutable_order.index(start_key) + 1).zfill(2)}_{start_key}' make_and_change_into(folder_name) self.order[start_key](mol) self.order.pop(start_key, None) os.chdir(home) # Begin looping through self.order, but return after the first iteration. for key in self.order: next_key = key if fin_log_msg and not skipping: printf(f'{COLOURS.green}{fin_log_msg}{COLOURS.end}') mol.pickle(state=next_key) return next_key
def test_double_pickle(tmpdir, acetone): """ Make sure we can add multiple pickled objects to the same file. """ with tmpdir.as_cwd(): acetone.pickle(state="input") # remove all coords acetone.coordinates = None acetone.pickle(state="after") # now check we have both states mols = unpickle() assert "after" in mols assert "input" in mols
def test_pickle_round_trip(tmpdir, acetone): """ test dumping a molecule to pickle and loading it back in. """ with tmpdir.as_cwd(): acetone.pickle(state="test") mols = unpickle() pickle_mol = mols["test"] for atom in acetone.atoms: pickle_atom = pickle_mol.get_atom_with_name(atom.atom_name) assert pickle_atom.__dict__ == atom.__dict__ assert acetone.bonds == pickle_mol.bonds assert acetone.angles == pickle_mol.angles assert acetone.dihedrals == pickle_mol.dihedrals
def setUp(self): """ Set up the seminario test case by loading a pickled ligand that contains the hessian already """ self.home = os.getcwd() self.test_folder = os.path.join(os.path.dirname(__file__), 'files') # Make temp folder and move the pickle file in with tempfile.TemporaryDirectory() as temp: os.chdir(temp) copy(os.path.join(self.test_folder, '.QUBEKit_states'), '.QUBEKit_states') self.molecules = unpickle() self.benzene_hessian, self.benzene_mod_sem = self.molecules[ 'hessian'], self.molecules['mod_sem'] self.benzene_mod_sem_vib_1 = self.molecules['mod_sem_vib_1'] self.benzonitrile_hessian, self.benzonitrile_mod_sem = self.molecules[ 'benzonitrile_hessian'], self.molecules['benzonitrile_mod_sem']
def setUp(self): """Set up the Seminario test by loading a pickled ligand that already contains the hessian.""" self.home = os.getcwd() self.test_folder = os.path.join(os.path.dirname(__file__), "files") # Make temp folder and move the pickle file in with tempfile.TemporaryDirectory() as temp: os.chdir(temp) copy(os.path.join(self.test_folder, ".QUBEKit_states"), ".QUBEKit_states") self.molecules = unpickle() self.benzene_hessian, self.benzene_mod_sem = ( self.molecules["hessian"], self.molecules["mod_sem"], ) self.benzene_hessian.testing = True self.benzene_mod_sem_vib_1 = self.molecules["mod_sem_vib_1"] self.benzene_mod_sem_vib_1.testing = True self.benzonitrile_hessian = self.molecules["benzonitrile_hessian"] self.benzonitrile_hessian.testing = True self.benzonitrile_mod_sem = self.molecules["benzonitrile_mod_sem"] self.benzonitrile_mod_sem.testing = True
def __init__(self): # First make sure the config folder has been made missing for conda and pip home = os.path.expanduser('~') config_folder = os.path.join(home, 'QUBEKit_configs') if not os.path.exists(config_folder): os.makedirs(config_folder) printf(f'Making config folder at: {home}') self.args = self.parse_commands() # If we are doing the torsion test add the attribute to the molecule so we can catch it in execute if self.args.torsion_test: self.args.restart = 'finalise' # If it's a bulk run, handle it separately # TODO Add .sdf as possible bulk_run, not just .csv if self.args.bulk_run is not None: self.handle_bulk() elif 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 KeyError: raise KeyError('This stage was not found in the log file; was the previous stage completed?') else: # Initialise molecule if self.args.smiles: self.molecule = Ligand(*self.args.smiles) # Now we should create the initial molecule and else: self.molecule = Ligand(self.args.input) # Find which config file is being used self.molecule.config_file = self.args.config_file # Handle configs which are in a file file_configs = Configure().load_config(self.molecule.config_file) 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. self.molecule.restart = None self.molecule.end = None 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 we need to remove torsion_test as it is passed from the command line if self.args.torsion_test is False: delattr(self.molecule, 'torsion_test') # Now check if we have been supplied a dihedral file and a constraints file if self.args.dihedral_file: self.molecule.read_scan_order(self.args.dihedral_file) if self.args.constraints_file: self.molecule.constraints_file = self.args.constaints_file # 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)