def _nitrogen_protonate(self, nitrogens, previous): """ :param nitrogens: list of Nitrogens :param previous: :return: """ def reset(): for n in nitrogens: self.rwmol.GetAtomWithIdx(n).SetNumExplicitHs(0) reset() p = Chem.DetectChemistryProblems(self.rwmol) if len(p) == 0 or p[0].Message() != previous: return True for i in range(1, len(nitrogens)): for c in itertools.combinations(nitrogens, i): reset() for n in c: self.rwmol.GetAtomWithIdx(n).SetNumExplicitHs(1) p = Chem.DetectChemistryProblems(self.rwmol) if len(p) == 0 or p[0].Message() != previous: return True return False self.log.debug(f'KekulizeException likely caused by nitrogen')
def _nitrogen_protonate(self, N, previous): def reset(): for n in N: self.mol.GetAtomWithIdx(n).SetNumExplicitHs(0) reset() p = Chem.DetectChemistryProblems(self.mol) if len(p) == 0 or p[0].Message() != previous: return True for i in range(1, len(N)): for c in itertools.combinations(N, i): reset() for n in c: self.mol.GetAtomWithIdx(n).SetNumExplicitHs(1) p = Chem.DetectChemistryProblems(self.mol) if len(p) == 0 or p[0].Message() != previous: return True return False log.debug(f'KekulizeException likely caused by nitrogen')
def fix_issues(self, _previous=None): problems = Chem.DetectChemistryProblems(self.mol) if self._iterations_done > 100: log.error(f'Iterations maxed out!') return None elif self._subiterations_done > 5: log.error(f'Unfixable') return None elif len(problems) == 0: return None else: log.debug( f'(Iteration: {self._iterations_done}) N problems {len(problems)}' ) p = problems[0] log.debug( f'(Iteration: {self._iterations_done}) Issue {p.GetType()}: {p.Message()}' ) if p.Message() == _previous: self.triage_rings() ############################################################ if p.GetType() == 'KekulizeException': if p.Message() != _previous: N = self._get_nitrogens(p.GetAtomIndices()) if len(N) > 0 and self._nitrogen_protonate(N, p.Message()): pass # been fixed. else: # triage rings should have altered any not ring atoms that are aromatic. # self._get_ring_info() # so it is likely a hetatom thing. log.info( f'Ring triages seems to have failed. Is it a valence thing?' ) valence_issues = [ self._has_correct_valence(i) for i in p.GetAtomIndices() ] if not all(valence_issues): for i in p.GetAtomIndices(): self.fix_valence(i) else: log.warning( f'Attempting default valency (not max)') self._valence_mode = 'default' for i in p.GetAtomIndices(): self.fix_valence(i) self._valence_mode = 'max' else: for i in p.GetAtomIndices(): self.downgrade_ring(self.mol.GetAtomWithIdx(i)) self.triage_rings() ############################################################ elif p.GetType( ) == 'AtomKekulizeException' and 'non-ring atom' in p.Message(): atom = self.mol.GetAtomWithIdx(p.GetAtomIdx()) atom.SetIsAromatic(False) log.debug(f'Atom {p.GetAtomIdx()} set to non-aromatic.') for bond in atom.GetBonds(): bond.SetBondType(Chem.BondType.SINGLE) elif p.GetType( ) == 'AtomKekulizeException' and 'Aromatic bonds on non aromatic atom' in p.Message( ): atom = self.mol.GetAtomWithIdx(p.GetAtomIdx()) log.debug(f'Atom {p.GetAtomIdx()} set to aromatic.') atom.SetIsAromatic(True) ############################################################ elif p.GetType() == 'AtomValenceException': i = p.GetAtomIdx() self.fix_valence(i) else: log.error('???', p.GetType(), p.Message()) self._iterations_done += 1 if _previous != p.Message(): log.debug(f'{self._iterations_done} appears successful.') self._subiterations_done = 0 else: self._subiterations_done += 1 log.debug(f'{self._iterations_done} appears unsuccessful.') return self.fix_issues(_previous=p.Message())
def fix_issues(self, _previous=None) -> None: """ This get run during instantiation. It is the third and final one run before sanitization. Deals with a variety of problems. It calls itself until no problems according to `Chem.DetectChemistryProblems` exits. It is a bit shoddy and any oddity likely steps from here. TODO :return: None """ self.modifications.append(self.mol) # may not have changed. problems = Chem.DetectChemistryProblems(self.rwmol) if self._iterations_done > 100: self.log.error(f'Iterations maxed out!') return None elif self._subiterations_done > 5: self.log.error(f'Unfixable') return None elif len(problems) == 0: return None else: self.log.debug( f'(Iteration: {self._iterations_done}) N problems {len(problems)}' ) p = problems[0] self.log.debug( f'(Iteration: {self._iterations_done}) Issue {p.GetType()}: {p.Message()}' ) if p.Message() == _previous: self.triage_rings() ############################################################ if p.GetType() == 'KekulizeException': if p.Message() != _previous: N = self._get_nitrogens(p.GetAtomIndices()) if len(N) > 0 and self._nitrogen_protonate(N, p.Message()): pass # been fixed. else: # triage rings should have altered any not ring atoms that are aromatic. # self._get_ring_info() # so it is likely a hetatom thing. self.log.info( f'Ring triages seems to have failed. Is it a valence thing?' ) valence_issues = [ self._has_correct_valence(i) for i in p.GetAtomIndices() ] if not all(valence_issues): for i in p.GetAtomIndices(): self.fix_valence(i) else: self.log.warning( f'Attempting default valency (not max)') self._valence_mode = 'default' for i in p.GetAtomIndices(): self.fix_valence(i) self._valence_mode = 'max' else: for i in p.GetAtomIndices(): self.downgrade_ring(self.rwmol.GetAtomWithIdx(i)) self.triage_rings() ############################################################ elif p.GetType( ) == 'AtomKekulizeException' and 'non-ring atom' in p.Message(): atom = self.rwmol.GetAtomWithIdx(p.GetAtomIdx()) atom.SetIsAromatic(False) self.log.debug(f'Atom {p.GetAtomIdx()} set to non-aromatic.') for bond in atom.GetBonds(): bond.SetBondType(Chem.BondType.SINGLE) elif p.GetType( ) == 'AtomKekulizeException' and 'Aromatic bonds on non aromatic atom' in p.Message( ): atom = self.rwmol.GetAtomWithIdx(p.GetAtomIdx()) self.log.debug(f'Atom {p.GetAtomIdx()} set to aromatic.') atom.SetIsAromatic(True) ############################################################ elif p.GetType() == 'AtomValenceException': i = p.GetAtomIdx() self.fix_valence(i) else: self.log.error('???', p.GetType(), p.Message()) self._iterations_done += 1 if _previous != p.Message(): self.log.debug(f'{self._iterations_done} appears successful.') self._subiterations_done = 0 else: self._subiterations_done += 1 self.log.debug( f'{self._iterations_done} appears unsuccessful.') return self.fix_issues(_previous=p.Message())