def _align_heavy_atoms(mol1, mol2, vmol1, vmol2, ilabel1, ilabel2,
                           eq_atoms):
        """
        Align the label of topologically identical atoms of second molecule
        towards first molecule

        Args:
            mol1: First molecule. OpenBabel OBMol object
            mol2: Second molecule. OpenBabel OBMol object
            vmol1: First virtual molecule constructed by centroids. OpenBabel
                OBMol object
            vmol2: First virtual molecule constructed by centroids. OpenBabel
                OBMol object
            ilabel1: inchi label map of the first molecule
            ilabel2: inchi label map of the second molecule
            eq_atoms: equivalent atom lables

        Return:
            corrected inchi labels of heavy atoms of the second molecule
        """

        nvirtual = vmol1.NumAtoms()
        nheavy = len(ilabel1)

        for i in ilabel2:  # add all heavy atoms
            a1 = vmol1.NewAtom()
            a1.SetAtomicNum(1)
            a1.SetVector(0.0, 0.0, 0.0)  # useless, just to pair with vmol2
            oa2 = mol2.GetAtom(i)
            a2 = vmol2.NewAtom()
            a2.SetAtomicNum(1)
            # align using the virtual atoms, these atoms are not
            # used to align, but match by positions
            a2.SetVector(oa2.GetVector())

        aligner = ob.OBAlign(False, False)
        aligner.SetRefMol(vmol1)
        aligner.SetTargetMol(vmol2)
        aligner.Align()
        aligner.UpdateCoords(vmol2)

        canon_mol1 = ob.OBMol()
        for i in ilabel1:
            oa1 = mol1.GetAtom(i)
            a1 = canon_mol1.NewAtom()
            a1.SetAtomicNum(oa1.GetAtomicNum())
            a1.SetVector(oa1.GetVector())

        aligned_mol2 = ob.OBMol()
        for i in range(nvirtual + 1, nvirtual + nheavy + 1):
            oa2 = vmol2.GetAtom(i)
            a2 = aligned_mol2.NewAtom()
            a2.SetAtomicNum(oa2.GetAtomicNum())
            a2.SetVector(oa2.GetVector())

        canon_label2 = list(range(1, nheavy+1))
        for symm in eq_atoms:
            for i in symm:
                canon_label2[i-1] = -1
        for symm in eq_atoms:
            candidates1 = list(symm)
            candidates2 = list(symm)
            for c2 in candidates2:
                distance = 99999.0
                canon_idx = candidates1[0]
                a2 = aligned_mol2.GetAtom(c2)
                for c1 in candidates1:
                    a1 = canon_mol1.GetAtom(c1)
                    d = a1.GetDistance(a2)
                    if d < distance:
                        distance = d
                        canon_idx = c1
                canon_label2[c2-1] = canon_idx
                candidates1.remove(canon_idx)

        canon_inchi_orig_map2 = [(canon, inchi, orig)
                                 for canon, inchi, orig in
                                 zip(canon_label2, list(range(1, nheavy + 1)),
                                     ilabel2)]
        canon_inchi_orig_map2.sort(key=lambda m: m[0])
        heavy_atom_indices2 = tuple([x[2] for x in canon_inchi_orig_map2])
        return heavy_atom_indices2
def _align_heavy_atoms ( mol1 , mol2 , vmol1 , vmol2 , ilabel1 , ilabel2 , eq_atoms ) : nvirtual = vmol1 . NumAtoms ( ) nheavy = len ( ilabel1 ) for i in ilabel2 : a1 = vmol1 . NewAtom ( ) a1 . SetAtomicNum ( 1 ) a1 . SetVector ( 0.0 , 0.0 , 0.0 ) oa2 = mol2 . GetAtom ( i ) a2 = vmol2 . NewAtom ( ) a2 . SetAtomicNum ( 1 ) a2 . SetVector ( oa2 . GetVector ( ) ) aligner = ob . OBAlign ( False , False ) aligner . SetRefMol ( vmol1 ) aligner . SetTargetMol ( vmol2 ) aligner . Align ( ) aligner . UpdateCoords ( vmol2 ) canon_mol1 = ob . OBMol ( ) for i in ilabel1 : oa1 = mol1 . GetAtom ( i ) a1 = canon_mol1 . NewAtom ( ) a1 . SetAtomicNum ( oa1 . GetAtomicNum ( ) ) a1 . SetVector ( oa1 . GetVector ( ) ) aligned_mol2 = ob . OBMol ( ) for i in range ( nvirtual + 1 , nvirtual + nheavy + 1 ) : oa2 = vmol2 . GetAtom ( i ) a2 = aligned_mol2 . NewAtom ( ) a2 . SetAtomicNum ( oa2 . GetAtomicNum ( ) ) a2 . SetVector ( oa2 . GetVector ( ) ) canon_label2 = list ( range ( 1 , nheavy + 1 ) ) for symm in eq_atoms : for i in symm : canon_label2 [ i - 1 ] = - 1 for symm in eq_atoms : candidates1 = list ( symm ) candidates2 = list ( symm ) for c2 in candidates2 : distance = 99999.0 canon_idx = candidates1 [ 0 ] a2 = aligned_mol2 . GetAtom ( c2 ) for c1 in candidates1 : a1 = canon_mol1 . GetAtom ( c1 ) d = a1 . GetDistance ( a2 ) if d < distance : distance = d canon_idx = c1 canon_label2 [ c2 - 1 ] = canon_idx candidates1 . remove ( canon_idx ) canon_inchi_orig_map2 = [ ( canon , inchi , orig ) for canon , inchi , orig in zip ( canon_label2 , list ( range ( 1 , nheavy + 1 ) ) , ilabel2 ) ] canon_inchi_orig_map2 . sort ( key = lambda m : m [ 0 ] ) heavy_atom_indices2 = tuple ( [ x [ 2 ] for x in canon_inchi_orig_map2 ] ) return heavy_atom_indices2
Align the label of topologically identical atoms of second molecule
        towards first molecule

        Args:
            mol1: First molecule. OpenBabel OBMol object
            mol2: Second molecule. OpenBabel OBMol object
            vmol1: First virtual molecule constructed by centroids. OpenBabel
                OBMol object
            vmol2: First virtual molecule constructed by centroids. OpenBabel
                OBMol object
            ilabel1: inchi label map of the first molecule
            ilabel2: inchi label map of the second molecule
            eq_atoms: equivalent atom lables

        Return:
            corrected inchi labels of heavy atoms of the second molecule