def is_stereo_compatible(tra, sgr1, sgr2): """ is this transformation compatible with the reactant/product stereo assignments? """ cgr1 = without_stereo_parities(sgr1) cgr2 = without_stereo_parities(sgr2) atm_key_dct = full_isomorphism(apply(tra, cgr1), cgr2) # determine the stereo centers which are preserved in the transformation sgr1 = _relabel(sgr1, atm_key_dct) atm_keys = sorted(atom_stereo_keys(sgr1) & atom_stereo_keys(sgr2)) bnd_keys = sorted(bond_stereo_keys(sgr1) & bond_stereo_keys(sgr2)) atm_pars1 = dict_.values_by_key(atom_stereo_parities(sgr1), atm_keys) atm_pars2 = dict_.values_by_key(atom_stereo_parities(sgr2), atm_keys) bnd_pars1 = dict_.values_by_key(bond_stereo_parities(sgr1), bnd_keys) bnd_pars2 = dict_.values_by_key(bond_stereo_parities(sgr2), bnd_keys) atm_ngb_keys_dct1 = atom_neighbor_keys(sgr1) atm_ngb_keys_dct2 = atom_neighbor_keys(sgr2) ret = True for atm_key, par1, par2 in zip(atm_keys, atm_pars1, atm_pars2): atm_ngb_keys1 = stereo_sorted_atom_neighbor_keys( sgr1, atm_key, atm_ngb_keys_dct1[atm_key]) atm_ngb_keys2 = stereo_sorted_atom_neighbor_keys( sgr2, atm_key, atm_ngb_keys_dct2[atm_key]) if _permutation_parity(atm_ngb_keys1, atm_ngb_keys2): ret &= (par1 == par2) else: ret &= (par1 != par2) for bnd_key, par1, par2 in zip(bnd_keys, bnd_pars1, bnd_pars2): atm1_key, atm2_key = bnd_key atm1_ngb_key1 = stereo_sorted_atom_neighbor_keys( sgr1, atm1_key, atm_ngb_keys_dct1[atm1_key] - {atm2_key})[0] atm2_ngb_key1 = stereo_sorted_atom_neighbor_keys( sgr1, atm2_key, atm_ngb_keys_dct1[atm2_key] - {atm1_key})[0] atm1_ngb_key2 = stereo_sorted_atom_neighbor_keys( sgr2, atm1_key, atm_ngb_keys_dct2[atm1_key] - {atm2_key})[0] atm2_ngb_key2 = stereo_sorted_atom_neighbor_keys( sgr2, atm2_key, atm_ngb_keys_dct2[atm2_key] - {atm1_key})[0] if not ((atm1_ngb_key1 != atm1_ngb_key2) ^ (atm2_ngb_key1 != atm2_ngb_key2)): ret &= (par1 == par2) else: ret &= (par1 != par2) return ret
def _is_compatible(sgr): atm_ste_par_dct = _assigned(atom_stereo_parities(sgr)) bnd_ste_par_dct = _assigned(bond_stereo_parities(sgr)) _compat_atm_assgns = (set(known_atm_ste_par_dct.items()) <= set(atm_ste_par_dct.items())) _compat_bnd_assgns = (set(known_bnd_ste_par_dct.items()) <= set(bnd_ste_par_dct.items())) return _compat_atm_assgns and _compat_bnd_assgns
def substereomers(gra): """ all stereomers compatible with this graph's assignments """ _assigned = functools.partial( dict_.filter_by_value, func=lambda x: x is not None) known_atm_ste_par_dct = _assigned(atom_stereo_parities(gra)) known_bnd_ste_par_dct = _assigned(bond_stereo_parities(gra)) def _is_compatible(sgr): atm_ste_par_dct = _assigned(atom_stereo_parities(sgr)) bnd_ste_par_dct = _assigned(bond_stereo_parities(sgr)) _compat_atm_assgns = (set(known_atm_ste_par_dct.items()) <= set(atm_ste_par_dct.items())) _compat_bnd_assgns = (set(known_bnd_ste_par_dct.items()) <= set(bnd_ste_par_dct.items())) return _compat_atm_assgns and _compat_bnd_assgns sgrs = tuple(filter(_is_compatible, stereomers(gra))) return sgrs
def _stereo_corrected_geometry(sgr, geo, geo_idx_dct): """ correct the stereo parities of a geometry (works iterately to handle cases of higher-order stereo) """ assert sgr == explicit(sgr) gra = without_stereo_parities(sgr) if has_stereo(sgr): full_atm_ste_par_dct = atom_stereo_parities(sgr) full_bnd_ste_par_dct = bond_stereo_parities(sgr) atm_keys = set() bnd_keys = set() last_gra = None while last_gra != gra: last_gra = gra atm_keys.update(stereogenic_atom_keys(gra)) bnd_keys.update(stereogenic_bond_keys(gra)) atm_ste_par_dct = { atm_key: full_atm_ste_par_dct[atm_key] for atm_key in atm_keys } bnd_ste_par_dct = { bnd_key: full_bnd_ste_par_dct[bnd_key] for bnd_key in bnd_keys } geo, gra = _atom_stereo_corrected_geometry(gra, atm_ste_par_dct, geo, geo_idx_dct) geo, gra = _bond_stereo_corrected_geometry(gra, bnd_ste_par_dct, geo, geo_idx_dct) return geo
# GEO = ( # ('C', (-4.3870588134, -1.233231672517, 0.143749726309016)), # ('C', (3.430304171771, -2.162836645393, 0.06129774977456508)), # ('C', (-2.228277354885, 0.1940343942502, -1.0788747507898575)), # ('C', (1.642516616594, -0.2666792157215, -1.1217150146524835)), # ('C', (-0.128929182575, 1.167922277159, 0.6891116967734786)), # ('O', (2.44416862025, 4.3088944278, 2.2811617948010614)), # ('O', (-0.4839163664245, -1.530973627393, -2.314392676447687)), # ('O', (0.1546996690877, 3.88389710099, 0.8186943317590577)), # ('H', (-3.69835752847, -2.73865502820, 1.385388988006255)), # ('H', (-5.55611791656, 0.04579783881378, 1.2732325623382277)), # ('H', (-5.59710374244, -2.09637223693, -1.2950602194746856)), # ('H', (2.43356758828, -3.46358524781, 1.3249230383597128)), # ('H', (4.89255603965, -1.19599087753, 1.1592100844834574)), # ('H', (4.37095524985, -3.286477042474, -1.3988006492215568)), # ('H', (-2.909935258917, 1.611421581693, -2.430728150238205)), # ('H', (2.608452006545, 0.954321649398, -2.49176226302396)), # ('H', (-0.2313534706629, 0.353123800972, 2.5932645521380446)), # ('H', (3.243829672371, 5.45538852286, 1.0816218339892072))) XGR = set_stereo_from_geometry(SGR, GEO, geo_idx_dct=GEO_IDX_DCT) print(automol.geom.string(GEO)) REF_ATM_STE_PAR_DCT = dict_.filter_by_value(atom_stereo_parities(GRA), lambda x: x is not None) ATM_STE_PAR_DCT = dict_.filter_by_value(atom_stereo_parities(XGR), lambda x: x is not None) print(REF_ATM_STE_PAR_DCT) print(ATM_STE_PAR_DCT)
def atom_stereo_keys(sgr): """ keys to atom stereo-centers """ atm_ste_keys = dict_.keys_by_value(atom_stereo_parities(sgr), lambda x: x in [True, False]) return atm_ste_keys