def getOctBondDistances(mol): ## This function gets ## ax and equitorial ## min and max bond lengths liglist, ligdents, ligcons = ligand_breakdown(mol) ax_ligand_list, eq_ligand_list, ax_natoms_list, eq_natoms_list, ax_con_int_list, eq_con_int_list, ax_con_list, eq_con_list, built_ligand_list = ligand_assign_consistent( mol, liglist, ligdents, ligcons, False, False) ax_dist = list() eq_dist = list() for ax_ligs in ax_con_list: tempList = list() for conatms in ax_ligs: tempList.append( distance( mol.getAtom(mol.findMetal()[0]).coords(), mol.getAtom(conatms).coords())) ax_dist.append(tempList) for eq_ligs in eq_con_list: tempList = list() for conatms in eq_ligs: tempList.append( distance( mol.getAtom(mol.findMetal()[0]).coords(), mol.getAtom(conatms).coords())) eq_dist.append(tempList) return ax_dist, eq_dist
def normalize_vector(v): length = distance(v, [0, 0, 0]) if length: nv = [float(i)/length for i in v] else: nv = [0, 0, 0] return nv
def DistErr(x, *args): """Computes distance error function for scipy optimization. Copied from E3 in pp. 311 of ref. [1] Parameters ---------- x : np.array 1D array of coordinates to be optimized. *args : dict Other parameters (refer to scipy.optimize docs) Returns ------- E : np.array Objective function """ E = 0 LB, UB, natoms = args for i in range(natoms - 1): for j in range(i + 1, natoms): ri = [x[3 * i], x[3 * i + 1], x[3 * i + 2]] rj = [x[3 * j], x[3 * j + 1], x[3 * j + 2]] dij = distance(ri, rj) uij = UB[i][j] lij = LB[i][j] E += (dij**2 / (uij**2) - 1)**2 E += (2 * lij**2 / (lij**2 + dij**2) - 1)**2 return np.asarray(E)
def minimum_ML_dist(mol): core = mol.getAtom(mol.findMetal()[0]).coords() min_dist = 1000 for atom_inds in mol.getBondedAtomsSmart(mol.findMetal()[0]): dist = distance(core, mol.getAtom(atom_inds).coords()) if (dist < min_dist) and (dist > 0): min_dist = dist return min_dist
def maximum_any_dist(mol): core = mol.getAtom(mol.findMetal()[0]) max_dist = 0 for atoms in mol.getAtoms(): dist = distance(core.coords(), atoms.coords()) if (dist > max_dist): max_dist = dist return max_dist
def maximum_ML_dist(mol): core = mol.getAtom(mol.findMetal()[0]).coords() max_dist = 0 for atom_inds in mol.getBondedAtomsSmart(mol.findMetal()[0]): dist = distance(core, mol.getAtom(atom_inds).coords()) if (dist > max_dist): max_dist = dist return max_dist
def mean_ML_dist(mol): core = mol.getAtom(mol.findMetal()[0]).coords() mean_dist = 0.0 for atom_inds in mol.getBondedAtomsSmart(mol.findMetal()[0]): dist = distance(core, mol.getAtom(atom_inds).coords()) mean_dist += float(dist) mean_dist = mean_dist / float(len(mol.getBondedAtomsSmart(mol.findMetal()[0]))) return mean_dist
def periodic_mindist(mol, surf, dim): ### calculates minimum distance between atoms in 2 molecules ### # INPUT # - mol: mol3D class, molecule # - surf: mol3D class, the surface # - dim: list of float, replication # OUTPUT # - mind: minimum distance between atoms of the 2 mol objects mind = 1000 for atom1 in mol.getAtoms(): for atom0 in surf.getAtoms(): if (distance_2d_torus(atom1.coords(), atom0.coords(), dim) < mind): mind = distance(atom1.coords(), atom0.coords()) return mind
def periodic_selfdist(mol, dim): ### calculates minimum distance between atoms in 2 molecules ## # INPUT # - mol: mol3D class, molecule # - dim: list of floats, replication # OUTPUT # - mind: minimum distance between atoms of the 2 mol and periodic # images mind = 1000 for ii, atom1 in enumerate(mol.getAtoms()): for jj, atom0 in enumerate(mol.getAtoms()): if (distance_2d_torus(atom1.coords(), atom0.coords(), dim) < mind) and (ii != jj): mind = distance(atom1.coords(), atom0.coords()) return mind
def substplacecheap(core, connPts, catom): corerem = mol3D() corerem.copymol3D(core) corerem.deleteatom(catom) # complex less O atom mdist = -1 cpoint = [0, 0, 0] for P in connPts: if corerem.mindisttopoint(P) < 1: # auto-reject if too close d0 = -2 else: d0 = distance(core.centermass(), P)+0.5 * \ log(corerem.mindisttopoint(P)-1) if d0 > mdist: mdist = d0 cpoint = P return cpoint
def fdistance(xyzf): # setting properties xyz = mol3D() xyz.readfromxyz(xyzf) # getting idxs of interest midx = xyz.findMetal()[0] # monometallic complexes mcoord = xyz.getAtom(midx).coords() # list of idx of the first-coord sphere fidx_list = xyz.getBondedAtoms(midx) fdistance_list = [] for idx in fidx_list: fcoord = xyz.getAtom(idx).coords() d = distance(mcoord, fcoord) fdistance_list.append(float(d)) return fdistance_list
def DistErrGrad(x, *args): """Computes gradient of distance error function for scipy optimization. Copied from E3 in pp. 311 of ref. [1] Parameters ---------- x : np.array 1D array of coordinates to be optimized. *args : dict Other parameters (refer to scipy.optimize docs) Returns ------- g : np.array Objective function gradient """ LB, UB, natoms = args g = np.zeros(3 * natoms) for i in range(natoms): jr = list(range(natoms)) jr.remove(i) for j in jr: ri = [x[3 * i], x[3 * i + 1], x[3 * i + 2]] rj = [x[3 * j], x[3 * j + 1], x[3 * j + 2]] dij = distance(ri, rj) uij = UB[i][j] lij = LB[i][j] g[3 * i] += (4 * ((dij / uij)**2 - 1) / (uij**2) - (8 / lij**2) * (2 * (lij**2 / (lij**2 + dij**2)) - 1) / ((1 + (dij / lij)**2)**2)) * (x[3 * i] - x[3 * j]) # xi g[3 * i + 1] += (4 * ((dij / uij)**2 - 1) / (uij**2) - (8 / lij**2) * (2 * (lij**2 / (lij**2 + dij**2)) - 1) / ((1 + (dij / lij)**2)**2)) * (x[3 * i + 1] - x[3 * j + 1] ) # yi g[3 * i + 2] += (4 * ((dij / uij)**2 - 1) / (uij**2) - (8 / lij**2) * (2 * (lij**2 / (lij**2 + dij**2)) - 1) / ((1 + (dij / lij)**2)**2)) * (x[3 * i + 2] - x[3 * j + 2] ) # zi return g
def autocorrelation(mol, prop_vec, orig, d, oct=True, catoms=None, use_dist=False): ## this function returns the autocorrelation ## for one atom # Inputs: # mol - mol3D class # prop_vec - vector, property of atoms in mol in order of index # orig - int, zero-indexed starting atom # d - int, number of hops to travel # oct - bool, if complex is octahedral, will use better bond checks result_vector = np.zeros(d + 1) hopped = 0 active_set = set([orig]) historical_set = set() if not use_dist: result_vector[hopped] = prop_vec[orig] * prop_vec[orig] else: result_vector[hopped] = 0.5 * abs(prop_vec[orig]) ** 2.4 / mol.natoms while hopped < (d): hopped += 1 new_active_set = set() for this_atom in active_set: ## prepare all atoms attached to this connection # print('called in AC') this_atoms_neighbors = mol.getBondedAtomsSmart(this_atom, oct=oct) for bound_atoms in this_atoms_neighbors: if (bound_atoms not in historical_set) and (bound_atoms not in active_set): new_active_set.add(bound_atoms) # print('new active set at hop = ' +str(hopped) + ' is ' +str(new_active_set)) for inds in new_active_set: if not use_dist: result_vector[hopped] += prop_vec[orig] * prop_vec[inds] else: this_dist = distance(mol.getAtom(orig).coords(), mol.getAtom(inds).coords()) result_vector[hopped] += prop_vec[orig] * prop_vec[inds] / (this_dist * mol.natoms) historical_set.update(active_set) active_set = new_active_set return (result_vector)
def create_columb_matrix(mol): ## create Coulomb matrix from mol3D information index_set = list(range(0, mol.natoms)) ## fetch the database of nuclear charges globs = globalvars() amassdict = globs.amass() A = numpy.matrix(numpy.zeros((mol.natoms, mol.natoms))) ## main build for i in index_set: for j in index_set: if i == j: A[i, j] = 0.5 * numpy.power( float(amassdict[mol.getAtom(i).symbol()][1]), (2.4)) else: this_dist = distance( mol.getAtom(i).coords(), mol.getAtom(j).coords()) if this_dist != 0.0: A[i, j] = float( amassdict[mol.getAtom(i).symbol()][1]) * float( amassdict[mol.getAtom(j).symbol()][1]) / float( this_dist) else: A[i, j] = 0 ## sort by columns in increasing order weights = [] for col in A.T: weights.append(numpy.linalg.norm(col)) sort_weights = (numpy.argsort(weights))[::-1] A = A[:, sort_weights] ## sort by rows in increasing order weights = [] for row in A: weights.append(numpy.linalg.norm(row)) sort_weights = (numpy.argsort(weights))[::-1] A = A[sort_weights, :] return A
def fpriority(mol): # setting up variables fidx_list = [] sidx_list = [] satno_list = [] ref_list = [] fd_list = [] fa_list = [] idx_list = [0] * 6 exit_signal = True # getting bond-order matrix mol.convert2OBMol() BOMatrix = mol.populateBOMatrix() # preping for the loop fidx_list.append(mol.findMetal()) for i in range(len(fidx_list)): for fidx in fidx_list[i]: for sidx in mol.getBondedAtoms(fidx): sidx_list.append([sidx]) for i in range(len(fidx_list)): for fidx in fidx_list[i]: for j in range(len(sidx_list)): for sidx in sidx_list[j]: BO = int(BOMatrix[fidx][sidx]) if BO == 0: BO = 1 satno_str = str(mol.getAtom(sidx).atno) satno_list.append(int(BO * satno_str)) for satno in set(satno_list): satnocount = satno_list.count(satno) if satnocount > 1: s_sel_list = [ i for i, atno in enumerate(satno_list) if atno is satno ] exit_signal = False for i in range(len(fidx_list)): for fidx in fidx_list[i]: ref_list.append(fidx) # starting the loop tidx_list = [] tatno_list = [] for i in range(len(sidx_list)): tidx_list.append([]) tatno_list.append([]) while not exit_signal: fpriority_list = [] for i in s_sel_list: t_list = [] for sidx in sidx_list[i]: for tidx in mol.getBondedAtoms(sidx): if tidx not in ref_list: t_list.append(tidx) tidx_list[i] = t_list # print(sidx_list) # print(tidx_list) for i in s_sel_list: for sidx in sidx_list[i]: atno_list = tatno_list[i] ls = [] for j in s_sel_list: for tidx in tidx_list[j]: BO = int(BOMatrix[sidx][tidx]) tatno_str = str(mol.getAtom(tidx).atno) ls.append(BO * tatno_str) sorted(ls, reverse=True) for j in ls: atno_list.append(j) a = ''.join(atno_list) tatno_list[i] = [a] sidx_list = [] for i in range(len(tidx_list)): sidx_list.append(tidx_list[i]) for i in s_sel_list: for sidx in sidx_list[i]: ref_list.append(sidx) test_list = [] for i in range(len(sidx_list)): test_list.append([]) # get priorities for i in range(len(satno_list)): atno_list = [] atno_list.append(str(satno_list[i])) if tatno_list[i] == []: atno_list.append('') else: atno_list.append(tatno_list[i][0]) a = '.'.join(atno_list) fpriority_list.append(float(a)) if tidx_list == test_list or len(set(fpriority_list)) == 6: exit_signal = True # get distance fd_list = [] mcoord = mol.atoms[mol.findMetal()[0]].coords() idx_list = np.argsort(np.array(fpriority_list)) fpriority_list = np.array(fpriority_list)[idx_list].tolist() sidx_list = mol.getBondedAtoms(fidx_list[0][0]) for idx in idx_list: scoord = mol.getAtom(sidx_list[idx]).coords() r = distance(mcoord, scoord) fd_list.append(r) # idx = np.argsort(np.array(fpriority_list))[-1] # sidx_list = mol.getBondedAtomsByCoordNo(fidx_list[0][0],6) # refcoord = mol.getAtom(sidx_list[idx]).coords() # mcoord = mol.getAtom(fidx_list[0][0]).coords() # idx0 = 0 # dist5 = 0 # idx5 = 0 # idx1_4 = [] # fprio1_4 = [] # sxyzs = [] # ssd_list = [] # for i, sidx in enumerate(sidx_list): # sxyz = mol.getAtom(sidx).coords() # dist = distance(refcoord,sxyz) # if dist == 0: # idx0 = i # elif dist > dist5: # dist5 = dist # idx5 = i # idx1_4.append(i) # fprio1_4.append(fpriority_list[i]) # sxyzs.append(sxyz) # ssd_list.append(dist) # fd_list.append(distance(mcoord,sxyz)) # idx1_4.pop(idx0) # idx1_4.pop(idx5) # fprio1_4.pop(idx0) # fprio1_4.pop(idx5) # idx_list[0] = idx0 # idx_list[5] = idx5 # idx1 = idx1_4[np.argsort(np.array(fprio1_4))[3]] # sxyz1 = sxyzs[idx1] # idx2_ = idx1_4[np.argsort(np.array(fprio1_4))[2]] # sxyz2_ = sxyzs[idx2_] # idx3_ = idx1_4[np.argsort(np.array(fprio1_4))[1]] # sxyz3_ = sxyzs[idx3_] # idx4_ = idx1_4[np.argsort(np.array(fprio1_4))[0]] # sxyz4_ = sxyzs[idx4_] # fd1_4 = [] # fd1_4.append(distance(sxyz1, sxyz1)) # fd1_4.append(distance(sxyz1, sxyz2_)) # fd1_4.append(distance(sxyz1, sxyz3_)) # fd1_4.append(distance(sxyz1, sxyz4_)) # idx3 = idx1_4[np.argsort(np.array(fd1_4))[-1]] + idx1 - 4 # if idx3 == idx2_: # if fpriority_list[idx3_] > fpriority_list[idx4_]: # idx2 = idx3_ # idx4 = idx4_ # else: # idx2 = idx4_ # idx4 = idx2_ # elif idx3 == idx4_: # if fpriority_list[idx2_] > fpriority_list[idx3_]: # idx2 = idx2_ # idx4 = idx3_ # else: # idx2 = idx3_ # idx4 = idx2_ # else: # if fpriority_list[idx2_] > fpriority_list[idx4_]: # idx2 = idx2_ # idx4 = idx4_ # else: # idx2 = idx4_ # idx4 = idx2_ # # get ax, eq, ax idxes # idx_list[1] = idx1 # idx_list[2] = idx2 # idx_list[3] = idx3 # idx_list[4] = idx4 # fpriority_list = np.array(fpriority_list)[idx_list].tolist() # fd_list = np.array(fd_list)[idx_list].tolist() return fpriority_list, fd_list, idx_list
def decorate_ligand(args, ligand_to_decorate, decoration, decoration_index): # structgen depends on decoration_manager, and decoration_manager depends on structgen.ffopt # Thus, this import needs to be placed here to avoid a circular dependence from molSimplify.Scripts.structgen import ffopt # INPUT # - args: placeholder for input arguments # - ligand_to_decorate: mol3D ligand # - decoration: list of smiles/decorations # - decoration_index: list of ligand atoms to replace # OUTPUT # - new_ligand: built ligand # - complex3D: list of all mol3D ligands and core # - emsg: error messages #if args.debug: # print 'decorating ligand' lig = ligand_to_decorate ## reorder to ensure highest atom index ## removed first sort_order = [ i[0] for i in sorted(enumerate(decoration_index), key=lambda x: x[1]) ] sort_order = sort_order[::-1] ## reverse decoration_index = [decoration_index[i] for i in sort_order] decoration = [decoration[i] for i in sort_order] if args.debug: print(('decoration_index is ' + str(decoration_index))) licores = getlicores() if not isinstance(lig, mol3D): lig, emsg = lig_load(lig, licores) else: lig.convert2OBMol() lig.charge = lig.OBMol.GetTotalCharge() lig.convert2mol3D() # convert to mol3D ## create new ligand merged_ligand = mol3D() merged_ligand.copymol3D(lig) for i, dec in enumerate(decoration): print(('** decoration number ' + str(i) + ' attaching ' + dec + ' at site ' + str(decoration_index[i]) + '**\n')) dec, emsg = lig_load(dec, licores) # dec.OBMol.AddHydrogens() dec.convert2mol3D() # convert to mol3D if args.debug: print(i) print(decoration_index) print((merged_ligand.getAtom(decoration_index[i]).symbol())) print((merged_ligand.getAtom(decoration_index[i]).coords())) merged_ligand.writexyz('basic.xyz') #dec.writexyz('dec' + str(i) + '.xyz') Hs = dec.getHsbyIndex(0) if len(Hs) > 0 and (not len(dec.cat)): dec.deleteatom(Hs[0]) dec.charge = dec.charge - 1 #dec.writexyz('dec_noH' + str(i) + '.xyz') if len(dec.cat) > 0: decind = dec.cat[0] else: decind = 0 dec.alignmol(dec.getAtom(decind), merged_ligand.getAtom(decoration_index[i])) r1 = dec.getAtom(decind).coords() r2 = dec.centermass() # center of mass rrot = r1 decb = mol3D() decb.copymol3D(dec) #################################### # center of mass of local environment (to avoid bad placement of bulky ligands) auxmol = mol3D() for at in dec.getBondedAtoms(decind): auxmol.addAtom(dec.getAtom(at)) if auxmol.natoms > 0: r2 = auxmol.centermass() # overwrite global with local centermass #################################### # rotate around axis and get both images theta, u = rotation_params(merged_ligand.centermass(), r1, r2) #print('u = ' + str(u) + ' theta = ' + str(theta)) dec = rotate_around_axis(dec, rrot, u, theta) if args.debug: dec.writexyz('dec_ARA' + str(i) + '.xyz') decb = rotate_around_axis(decb, rrot, u, theta - 180) if args.debug: decb.writexyz('dec_ARB' + str(i) + '.xyz') d1 = distance(dec.centermass(), merged_ligand.centermass()) d2 = distance(decb.centermass(), merged_ligand.centermass()) dec = dec if (d2 < d1) else decb # pick best one ##################################### # check for linear molecule auxm = mol3D() for at in dec.getBondedAtoms(decind): auxm.addAtom(dec.getAtom(at)) if auxm.natoms > 1: r0 = dec.getAtom(decind).coords() r1 = auxm.getAtom(0).coords() r2 = auxm.getAtom(1).coords() if checkcolinear(r1, r0, r2): theta, urot = rotation_params( r1, merged_ligand.getAtom(decoration_index[i]).coords(), r2) theta = vecangle( vecdiff( r0, merged_ligand.getAtom(decoration_index[i]).coords()), urot) dec = rotate_around_axis(dec, r0, urot, theta) ## get the default distance between atoms in question connection_neighbours = merged_ligand.getAtom( merged_ligand.getBondedAtomsnotH(decoration_index[i])[0]) new_atom = dec.getAtom(decind) target_distance = connection_neighbours.rad + new_atom.rad position_to_place = vecdiff(new_atom.coords(), connection_neighbours.coords()) old_dist = norm(position_to_place) missing = (target_distance - old_dist) / 2 dec.translate([missing * position_to_place[j] for j in [0, 1, 2]]) r1 = dec.getAtom(decind).coords() u = vecdiff(r1, merged_ligand.getAtom(decoration_index[i]).coords()) dtheta = 2 optmax = -9999 totiters = 0 decb = mol3D() decb.copymol3D(dec) # check for minimum distance between atoms and center of mass distance while totiters < 180: #print('totiters '+ str(totiters)) dec = rotate_around_axis(dec, r1, u, dtheta) d0 = dec.mindist( merged_ligand) # try to maximize minimum atoms distance d0cm = dec.distance( merged_ligand) # try to maximize center of mass distance iteropt = d0cm + d0 # optimization function if (iteropt > optmax): # if better conformation, keep decb = mol3D() decb.copymol3D(dec) optmax = iteropt #temp = mol3D() #temp.copymol3D(merged_ligand) #temp.combine(decb) #temp.writexyz('opt_iter_'+str(totiters)+'.xyz') #print('new max! ' + str(iteropt) ) totiters += 1 dec = decb if args.debug: dec.writexyz('dec_aligned' + str(i) + '.xyz') print(('natoms before delete ' + str(merged_ligand.natoms))) print(('obmol before delete at ' + str(decoration_index[i]) + ' is ' + str(merged_ligand.OBMol.NumAtoms()))) ## store connectivity for deleted H BO_mat = merged_ligand.populateBOMatrix() row_deleted = BO_mat[decoration_index[i]] bonds_to_add = [] # find where to put the new bonds ->>> Issue here. for j, els in enumerate(row_deleted): if els > 0: # if there is a bond with an atom number # before the deleted atom, all is fine # else, we subtract one as the row will be be removed if j < decoration_index[i]: bond_partner = j else: bond_partner = j - 1 if len(dec.cat) > 0: bonds_to_add.append( (bond_partner, (merged_ligand.natoms - 1) + dec.cat[0], els)) else: bonds_to_add.append( (bond_partner, merged_ligand.natoms - 1, els)) ## perfrom delete merged_ligand.deleteatom(decoration_index[i]) merged_ligand.convert2OBMol() if args.debug: merged_ligand.writexyz('merged del ' + str(i) + '.xyz') ## merge and bond merged_ligand.combine(dec, bond_to_add=bonds_to_add) merged_ligand.convert2OBMol() if args.debug: merged_ligand.writexyz('merged' + str(i) + '.xyz') merged_ligand.printxyz() print('************') merged_ligand.convert2OBMol() merged_ligand, emsg = ffopt('MMFF94', merged_ligand, [], 0, [], False, [], 100) BO_mat = merged_ligand.populateBOMatrix() if args.debug: merged_ligand.writexyz('merged_relaxed.xyz') print(BO_mat) return (merged_ligand)
def tsgen(mode, args, rootdir, core, substr, compreact, substreact, globs): emsg = False this_diag = run_diag() strfiles = [] adjsidx = substr.getBondedAtoms(substreact)[0] adjcidx = core.getBondedAtoms(compreact)[0] # initialize connecting and frozen atoms for FF opt frozenats = [] for i in range(0, core.natoms): frozenats.append(i) # also freeze the abstracted atom and the heavy atom bonded to it frozenats.append(core.natoms+substreact) frozenats.append(core.natoms+adjsidx) connected = [core.natoms+substreact] # START FUNCTIONALIZING sanity = False if mode == 2: emsg = 'Sorry, this mode is not supported yet. Exiting...' return strfiles, emsg, this_diag elif mode == 1: # oxidative addition of a single group # get first connecting point MXBL = MXdistcoeff*(core.getAtom(compreact).rad + substr.getAtom(substreact).rad) cpoint = getconnection(core, compreact, MXBL) # distort substrate molecule XYBL = XYcoeff*(substr.getAtom(substreact).rad + substr.getAtom(adjsidx).rad) substr.BCM(adjsidx, substreact, XYBL) # align substrate molecule substr.alignmol(substr.getAtom(substreact), atom3D('H', cpoint)) tmp3D = mol3D() tmp3D.copymol3D(core) tmp3D.addAtom(atom3D('Cl', cpoint)) ligalignpts = getconnections( tmp3D, tmp3D.natoms-1, compreact, XYBL, MXYang) if args.substplaceff: # full FF substrate placement print('Full FF-based substrate placement specified.') en_min = 1e6 for n, P in enumerate(ligalignpts): print(('Evaluating FF energy of point ' + str(n+1)+' of '+str(len(ligalignpts)))) coretmp = mol3D() coretmp.copymol3D(core) substrtmp = mol3D() substrtmp.copymol3D(substr) ts3Dtmp, enc = substplaceff_mode1( coretmp, substrtmp, substreact, compreact, cpoint, P, args, connected, frozenats) if enc < en_min: en_min = enc ts3D = mol3D() ts3D.copymol3D(ts3Dtmp) else: # cheap substrate placement print('Cheap substrate placement') ligalignpt = substplacecheap(core, ligalignpts, compreact) ts3D, enc = substplaceff_mode1( core, substr, substreact, compreact, cpoint, ligalignpt, args, connected, frozenats) elif mode == 3: # abstraction # distort A-B bond ABBL = distance(core.getAtomCoords(compreact), core.getAtomCoords( adjcidx)) + 0.05*(core.getAtom(compreact).rad + core.getAtom(adjcidx).rad) core.BCM(compreact, adjcidx, ABBL) # set B-X distance BXBL = 1.1*(substr.getAtom(substreact).rad + core.getAtom(compreact).rad) # get possible connecting points connPts = getconnections(core, compreact, adjcidx, BXBL, ABXang) if args.substplaceff: # full FF substrate placement print('Full FF-based substrate placement specified.') en_min = 1e6 for n, P in enumerate(connPts): print(('Evaluating FF energy of point ' + str(n+1)+' of '+str(len(connPts)))) coretmp = mol3D() coretmp.copymol3D(core) substrtmp = mol3D() substrtmp.copymol3D(substr) ts3Dtmp, enc = substplaceff_mode3( coretmp, substrtmp, substreact, compreact, P, args, connected, frozenats) if enc < en_min: en_min = enc ts3D = mol3D() ts3D.copymol3D(ts3Dtmp) else: # cheap substrate placement print('Cheap substrate placement') cpoint = substplacecheap(core, connPts, compreact) ts3D, enc = substplaceff_mode3( core, substr, substreact, compreact, cpoint, args, connected, frozenats) if 'a' in args.ffoption: print('FF optimized remainder of substrate') ts3D.charge += substr.charge # END FUNCTIONALIZING fname = name_TS(rootdir, args.core, substr, args, bind=args.bind, bsmi=args.nambsmi) ts3D.writexyz(fname) strfiles.append(fname) getinputargs(args, fname) pfold = rootdir.split('/', 1)[-1] # check for molecule sanity sanity, d0 = ts3D.sanitycheck(True) if args.debug: print(('setting sanity diag, min dist at ' + str(d0) + ' (higher is better)')) this_diag.set_sanity(sanity, d0) this_diag.set_mol(ts3D) this_diag.write_report(fname+'.report') del ts3D if sanity: print(('WARNING: Generated complex is not good! Minimum distance between atoms:' + "{0:.2f}".format(d0)+'A\n')) print(('\nIn folder '+pfold+' generated 1 structure(s)!')) return strfiles, emsg, this_diag
def GetBoundsMatrices(mol, natoms, catoms=[], shape=[], A=[]): """Generate distance bounds matrices. The basic idea is outlined in ref [1]. We first apply 1-2 (bond length) and 1-3 (bond angle) constraints, read from the FF-optimized initial conformer. Next, to bias the search towards coordinating conformers, approximate connection atom distance constraints based on topological distances are also included. Parameters ---------- mol : mol3D mol3D class instance of molecule. natoms : int Number of atoms in the molecule. catoms : list, optional List of ligand connection atoms. Default is Empty. shape : dict Dict containing angles. A : list List of lists making a distance 2 connectivity matrix. Returns ------- LB : np.array Lower bound matrix. UB : np.array Upper bound matrix. """ LB = np.zeros((natoms, natoms)) # lower bound UB = np.zeros((natoms, natoms)) # upper bound, both symmetric # Set constraints for all atoms excluding the dummy metal atom for i in range(natoms - 1): for j in range(natoms - 1): # 1-2 constraints: UB = LB = BL if mol.OBMol.GetBond(i + 1, j + 1) is not None: UB[i][j] = distance(mol.getAtomCoords(i), mol.getAtomCoords(j)) UB[j][i] = distance(mol.getAtomCoords(i), mol.getAtomCoords(j)) LB[i][j] = distance(mol.getAtomCoords(i), mol.getAtomCoords(j)) LB[j][i] = distance(mol.getAtomCoords(i), mol.getAtomCoords(j)) for i in range(natoms - 1): for j in range(natoms - 1): for k in range(natoms - 1): # 1-3 constraints: UB = LB = BL if mol.OBMol.GetBond( i + 1, j + 1) is not None and mol.OBMol.GetBond( j + 1, k + 1) is not None and j != k and i != k: AB = vecdiff(mol.getAtomCoords(j), mol.getAtomCoords(i)) BC = vecdiff(mol.getAtomCoords(k), mol.getAtomCoords(j)) UB[i][k] = CosRule(norm(AB), norm(BC), 180 - vecangle(AB, BC)) UB[k][i] = CosRule(norm(AB), norm(BC), 180 - vecangle(AB, BC)) LB[i][k] = CosRule(norm(AB), norm(BC), 180 - vecangle(AB, BC)) LB[k][i] = CosRule(norm(AB), norm(BC), 180 - vecangle(AB, BC)) # Set constraints for atoms bonded to the dummy metal atom # Currently assumes all M-L bonds are 2 Angstroms dummy_idx = natoms - 1 M_L_bond = 2 for catom in catoms: # Set 1-2 constraints UB[catom][dummy_idx] = M_L_bond UB[dummy_idx][catom] = M_L_bond LB[catom][dummy_idx] = M_L_bond LB[dummy_idx][catom] = M_L_bond if len(catoms) > 1: # Set 1-3 contraints for ligating atoms for i in range(len(catoms[:-1])): for j in range(i + 1, len(catoms)): angle = shape[str(i) + '-' + str(j)] lig_distance = CosRule(M_L_bond, M_L_bond, angle) UB[catoms[i]][catoms[j]] = lig_distance UB[catoms[j]][catoms[i]] = lig_distance LB[catoms[i]][catoms[j]] = lig_distance LB[catoms[j]][catoms[i]] = lig_distance expanded_vdwrad = vdwrad.copy() expanded_vdwrad[ 'Fe'] = 1.5 # Default vdw radius for the dummy metal is 1.5 for i in range(natoms): for j in range(i): # fill LBs with sums of vdW radii and UBs with arbitrary large cutoff if LB[i][j] == 0: LB[i][j] = expanded_vdwrad[mol.getAtom( i).sym] + expanded_vdwrad[mol.getAtom(j).sym] LB[j][i] = expanded_vdwrad[mol.getAtom( i).sym] + expanded_vdwrad[mol.getAtom(j).sym] UB[i][j] = 100 UB[j][i] = 100 return LB, UB
def oct_comp(file_in, angle_ref=oct_angle_ref, catoms_arr=None, debug=False): my_mol = create_mol_with_xyz(_file_in=file_in) num_coord_metal, catoms = get_num_coord_metal(file_in=file_in) # metal_ind = my_mol.findMetal()[0] metal_coord = my_mol.getAtomCoords(my_mol.findMetal()[0]) catom_coord = [] if not catoms_arr == None: catoms = catoms_arr num_coord_metal = len(catoms_arr) theta_arr, oct_dist = [], [] for atom in catoms: coord = my_mol.getAtomCoords(atom) catom_coord.append(coord) th_input_arr = [] for idx1, coord1 in enumerate(catom_coord): delr1 = (np.array(coord1) - np.array(metal_coord)).tolist() theta_tmp = [] for idx2, coord2 in enumerate(catom_coord): if idx2 != idx1: delr2 = (np.array(coord2) - np.array(metal_coord)).tolist() theta = vecangle(delr1, delr2) theta_tmp.append(theta) th_input_arr.append([catoms[idx1], theta_tmp]) th_output_arr, sum_del_angle, catoms_arr, max_del_sig_angle = loop_target_angle_arr( th_input_arr, angle_ref) if debug: print(('th:', th_output_arr)) print(('sum_del:', sum_del_angle)) print(('catoms_arr:', catoms_arr)) print( ('catoms_type:', [my_mol.getAtom(x).symbol() for x in catoms_arr])) for idx, ele in enumerate(th_output_arr): theta_arr.append([catoms_arr[idx], sum_del_angle[idx], ele]) # theta_arr.sort(key=sort_sec_ele) # theta_trunc_arr = theta_arr[0:6] theta_trunc_arr = theta_arr # print('truncated theta array:', theta_trunc_arr) theta_trunc_arr_T = list(map(list, list(zip(*theta_trunc_arr)))) oct_catoms = theta_trunc_arr_T[0] oct_angle_devi = theta_trunc_arr_T[1] oct_angle_all = theta_trunc_arr_T[2] if debug: print(('Summation of deviation angle for catoms:', oct_angle_devi)) print(('Angle for catoms:', oct_angle_all)) for atom in oct_catoms: coord = catom_coord[catoms.index(atom)] dist = distance(coord, metal_coord) oct_dist.append(dist) oct_dist.sort() # print('!!!oct_dist:', oct_dist) try: # For Oct dist_del_arr = np.array([ oct_dist[3] - oct_dist[0], oct_dist[4] - oct_dist[1], oct_dist[5] - oct_dist[2] ]) min_posi = np.argmin(dist_del_arr) if min_posi == 0: dist_eq, dist_ax = oct_dist[:4], oct_dist[4:] elif min_posi == 1: dist_eq, dist_ax = oct_dist[1:5], [oct_dist[0], oct_dist[5]] else: dist_eq, dist_ax = oct_dist[2:], oct_dist[:2] except IndexError: # For one empty site if (oct_dist[3] - oct_dist[0]) > (oct_dist[4] - oct_dist[1]): dist_ax, dist_eq = oct_dist[:1], oct_dist[1:] # ax dist is smaller else: dist_ax, dist_eq = oct_dist[4:], oct_dist[:4] # eq dist is smaller dist_del_all = oct_dist[-1] - oct_dist[0] if debug: print(('dist:', dist_eq, dist_ax)) dist_del_eq = max(dist_eq) - min(dist_eq) dist_del_ax = max(dist_ax) - min(dist_ax) dist_del_eq_ax = max(abs(max(dist_eq) - min(dist_ax)), abs(max(dist_ax) - min(dist_eq))) oct_dist_del = [dist_del_eq, dist_del_ax, dist_del_eq_ax, dist_del_all] if debug: print(('distance difference for catoms to metal (eq, ax, eq_ax):', oct_dist_del)) return oct_angle_devi, oct_dist_del, max_del_sig_angle, catoms_arr
def oct_comp(file_in, angle_ref=oct_angle_ref, catoms_arr=None, debug=False): my_mol = create_mol_with_xyz(_file_in=file_in) num_coord_metal, catoms = get_num_coord_metal(file_in=file_in) # metal_ind = my_mol.findMetal()[0] metal_coord = my_mol.getAtomCoords(my_mol.findMetal()[0]) catom_coord = [] if not catoms_arr == None: catoms = catoms_arr num_coord_metal = len(catoms_arr) theta_arr, oct_dist = [], [] for atom in catoms: coord = my_mol.getAtomCoords(atom) catom_coord.append(coord) th_input_arr = [] for idx1, coord1 in enumerate(catom_coord): delr1 = (np.array(coord1) - np.array(metal_coord)).tolist() theta_tmp = [] for idx2, coord2 in enumerate(catom_coord): if idx2 != idx1: delr2 = (np.array(coord2) - np.array(metal_coord)).tolist() theta = vecangle(delr1, delr2) theta_tmp.append(theta) th_input_arr.append([catoms[idx1], theta_tmp]) th_output_arr, sum_del_angle, catoms_arr, max_del_sig_angle = loop_target_angle_arr(th_input_arr, angle_ref) if debug: print('th:', th_output_arr) print('sum_del:', sum_del_angle) print('catoms_arr:', catoms_arr) print('catoms_type:', [my_mol.getAtom(x).symbol() for x in catoms_arr]) for idx, ele in enumerate(th_output_arr): theta_arr.append([catoms_arr[idx], sum_del_angle[idx], ele]) # theta_arr.sort(key=sort_sec_ele) # theta_trunc_arr = theta_arr[0:6] theta_trunc_arr = theta_arr # print('truncated theta array:', theta_trunc_arr) theta_trunc_arr_T = list(map(list, zip(*theta_trunc_arr))) oct_catoms = theta_trunc_arr_T[0] oct_angle_devi = theta_trunc_arr_T[1] oct_angle_all = theta_trunc_arr_T[2] if debug: print('Summation of deviation angle for catoms:', oct_angle_devi) print('Angle for catoms:', oct_angle_all) for atom in oct_catoms: coord = catom_coord[catoms.index(atom)] dist = distance(coord, metal_coord) oct_dist.append(dist) oct_dist.sort() # print('!!!oct_dist:', oct_dist) try: ### For Oct dist_del_arr = np.array([oct_dist[3] - oct_dist[0], oct_dist[4] - oct_dist[1], oct_dist[5] - oct_dist[2]]) min_posi = np.argmin(dist_del_arr) if min_posi == 0: dist_eq, dist_ax = oct_dist[:4], oct_dist[4:] elif min_posi == 1: dist_eq, dist_ax = oct_dist[1:5], [oct_dist[0], oct_dist[5]] else: dist_eq, dist_ax = oct_dist[2:], oct_dist[:2] except IndexError: ## For one empty site if (oct_dist[3] - oct_dist[0]) > (oct_dist[4] - oct_dist[1]): dist_ax, dist_eq = oct_dist[:1], oct_dist[1:] # ax dist is smaller else: dist_ax, dist_eq = oct_dist[4:], oct_dist[:4] # eq dist is smaller dist_del_all = oct_dist[-1] - oct_dist[0] if debug: print('dist:', dist_eq, dist_ax) dist_del_eq = max(dist_eq) - min(dist_eq) dist_del_ax = max(dist_ax) - min(dist_ax) dist_del_eq_ax = max(abs(max(dist_eq) - min(dist_ax)), abs(max(dist_ax) - min(dist_eq))) oct_dist_del = [dist_del_eq, dist_del_ax, dist_del_eq_ax, dist_del_all] if debug: print('distance difference for catoms to metal (eq, ax, eq_ax):', oct_dist_del) return oct_angle_devi, oct_dist_del, max_del_sig_angle, catoms_arr