def make_mol(struct, verbose=False): mol = openbabel.OBMol() mol.BeginModify() atoms = [] for xyz, t in zip(struct.xyz, struct.c): x, y, z = map(float, xyz) ch = struct.channels[t] atom = mol.NewAtom() atom.SetAtomicNum(ch.atomic_num) atom.SetVector(x, y, z) atoms.append(atom) fixup(atoms, mol, struct) connect_the_dots(mol, atoms, struct) fixup(atoms, mol, struct) mol.PerceiveBondOrders() fixup(atoms, mol, struct) for (i, a) in enumerate(atoms): openbabel.OBAtomAssignTypicalImplicitHydrogens(a) fixup(atoms, mol, struct) mol.EndModify() mol.AddHydrogens() fixup(atoms, mol, struct) mismatches = 0 for (a, t) in zip(atoms, struct.c): ch = struct.channels[t] if 'Donor' in ch.name and not a.IsHbondDonor(): mismatches += 1 if verbose: print("Not Donor", ch.name, a.GetX(), a.GetY(), a.GetZ()) if ch.name != 'NitrogenXSDonorAcceptor' and 'Acceptor' in ch.name and not a.IsHbondAcceptorSimple( ): #there are issues with nitrogens and openbabel protonation.. mismatches += 1 if verbose: print("Not Acceptor", ch.name, a.GetX(), a.GetY(), a.GetZ()) if 'Aromatic' in ch.name and not a.IsAromatic(): mismatches += 1 if verbose: print("Not Aromatic", ch.name, a.GetX(), a.GetY(), a.GetZ()) mol.DeleteHydrogens() return mol, mismatches
def make_obmol(struct, verbose=False): '''Create an OBMol from AtomStruct that attempts to maintain correct atom typing''' mol = ob.OBMol() mol.BeginModify() visited_mols = [] atoms = [] for xyz, t in zip(struct.xyz, struct.c): x, y, z = map(float, xyz) ch = struct.channels[t] atom = mol.NewAtom() atom.SetAtomicNum(ch.atomic_num) atom.SetVector(x, y, z) atoms.append(atom) fixup(atoms, mol, struct) visited_mols.append(ob.OBMol(mol)) connect_the_dots(mol, atoms, struct) fixup(atoms, mol, struct) visited_mols.append(ob.OBMol(mol)) mol.EndModify() mol.AddPolarHydrogens() #make implicits explicit visited_mols.append(ob.OBMol(mol)) mol.PerceiveBondOrders() fixup(atoms, mol, struct) visited_mols.append(ob.OBMol(mol)) for (i, a) in enumerate(atoms): ob.OBAtomAssignTypicalImplicitHydrogens(a) fixup(atoms, mol, struct) visited_mols.append(ob.OBMol(mol)) mol.AddHydrogens() fixup(atoms, mol, struct) visited_mols.append(ob.OBMol(mol)) #make rings all aromatic if majority of carbons are aromatic for ring in ob.OBMolRingIter(mol): if 5 <= ring.Size() <= 6: carbon_cnt = 0 aromatic_ccnt = 0 for ai in ring._path: a = mol.GetAtom(ai) if a.GetAtomicNum() == 6: carbon_cnt += 1 if a.IsAromatic(): aromatic_ccnt += 1 if aromatic_ccnt / carbon_cnt >= .5 and aromatic_ccnt != ring.Size( ): #set all ring atoms to be aromatic for ai in ring._path: a = mol.GetAtom(ai) a.SetAromatic(True) #bonds must be marked aromatic for smiles to match for bond in ob.OBMolBondIter(mol): a1 = bond.GetBeginAtom() a2 = bond.GetEndAtom() if a1.IsAromatic() and a2.IsAromatic(): bond.SetAromatic(True) visited_mols.append(ob.OBMol(mol)) mismatches = 0 for (a, t) in zip(atoms, struct.c): ch = struct.channels[t] if 'Donor' in ch.name and not a.IsHbondDonor(): mismatches += 1 if verbose: print("Not Donor", ch.name, a.GetX(), a.GetY(), a.GetZ()) if ch.name != 'NitrogenXSDonorAcceptor' and 'Acceptor' in ch.name and a.GetExplicitDegree( ) != a.GetTotalDegree(): #there are issues with nitrogens and openbabel protonation.. mismatches += 1 if verbose: print("Not Acceptor", ch.name, a.GetX(), a.GetY(), a.GetZ()) if 'Aromatic' in ch.name and not a.IsAromatic(): mismatches += 1 if verbose: print("Not Aromatic", ch.name, a.GetX(), a.GetY(), a.GetZ()) return pybel.Molecule(mol), mismatches, visited_mols
def fill_rem_valences(self, ob_mol, atoms, struct): ''' Fill empty valences with hydrogens up to the amount expected by the atom type, or a typical amount according to openbabel, and then fill remaining empty valences with higher bond orders. ''' max_vals = get_max_valences(atoms) for ob_atom, atom_type in zip(atoms, struct.atom_types): if struct.typer.explicit_h: # all Hs should already be present continue max_val = max_vals.get(ob_atom.GetIdx(), 1) if Atom.h_count in struct.typer: # this should have already been set # by set_min_h_counts, but whatever h_count = Atom.h_count(ob_atom) if h_count < atom_type.h_count: n = ob_atom.GetImplicitHCount() ob_atom.SetImplicitHCount(n + atom_type.h_count - h_count) elif ob_atom.GetExplicitValence() < max_val: # this uses explicit valence and formal charge, # and only ever INCREASES hydrogens, since it # never sets implicit H to a negative value # but it does overwrite the existing value, so # we need to save it beforehand and then add n = ob_atom.GetImplicitHCount() ob.OBAtomAssignTypicalImplicitHydrogens(ob_atom) n += ob_atom.GetImplicitHCount() ob_atom.SetImplicitHCount(n) # these have possibly changed max_vals = get_max_valences(atoms) # now increment bond orders to fill remaining valences atom_info = sort_atoms_by_valence(atoms, max_vals) for max_val, rem_val, atom in reversed(atom_info): if atom.GetExplicitValence() >= max_val: continue # else, the atom could have an empty valence # so check whether we can augment a bond, # prioritizing bonds that are too short bond_info = sort_bonds_by_stretch(ob.OBAtomBondIter(atom)) for bond_stretch, bond_len, bond in reversed(bond_info): if bond.GetBondOrder() >= 3: continue # don't go above triple # do the atoms involved in this bond have empty valences? # since we are modifying the valences in the loop, this # could have changed since calling sort_atoms_by_valence a1, a2 = bond.GetBeginAtom(), bond.GetEndAtom() min_val_diff = min( # by how much are the valences under? max_vals.get(a1.GetIdx(), 1) - a1.GetExplicitValence(), max_vals.get(a2.GetIdx(), 1) - a2.GetExplicitValence()) if min_val_diff > 0: # increase bond order bond_order = bond.GetBondOrder() # don't go above triple bond.SetBondOrder(min(bond_order + min_val_diff, 3)) # if the current atom now has its preferred valence, # break and let other atoms choose next bonds to augment if atom.GetExplicitValence() == max_vals[atom.GetIdx()]: break