def extract(s, bonds, save_info): elems = np.array(s.get_chemical_symbols()) hybrid = np.zeros(len(elems)).astype(int) if (elems == 'C').any(): # Only do this if there is any carbon... if bonds is None: # Recalculate bonds bonds = Bonds.get(s) C_i = set(list(np.where(elems == 'C')[0])) for b in bonds: C_b = C_i.intersection(b[:2]) hybrid[list(C_b)] += 1 # Now go from number of bonds to hybridation state hybrid[list(C_i)] = np.where( (hybrid[list(C_i)] > 1) * (hybrid[list(C_i)] <= 4), hybrid[list(C_i)] - 1, 0) if save_info: s.new_array(CarbonHybridationState.default_name, hybrid) return hybrid
def bondDistance(s, i, j, bond_matrix=None): """ Return the distance in number of bonds between two atoms. Returns -1 if the two atoms are not connected. Requires NetworkX to be installed to work. | Parameters: | s (ase.Atoms): the structure on which to compute the distance | i (int): index of the first atom | j (int): index of the second atom | bond_matrix (np.ndarray): pre-computed bond matrix to use. If not | provided, will be calculated with default | parameters | Returns: | r (int): computed bond distance """ if bond_matrix is None: bprop = Bonds(return_matrix=True) _, bond_matrix = bprop(s) graph = get_bonding_graph(bond_matrix) r = get_bonding_distance(graph, i, j) return r
def test_linkageprops(self): from soprano.properties.linkage import (LinkageList, Bonds, Molecules, MoleculeNumber, MoleculeMass, MoleculeCOMLinkage, MoleculeRelativeRotation, MoleculeSpectralSort, CoordinationHistogram, HydrogenBonds, HydrogenBondsNumber) from soprano.properties.transform import Rotate a = read(os.path.join(_TESTDATA_DIR, 'mol_crystal.cif')) # Test bonds testAtoms = Atoms(['C', 'C', 'C', 'C'], cell=[5, 5, 5], positions=np.array([[0, 0, 0], [4, 0, 0], [3, 3, 3], [3, 3.5, 3]]), pbc=True) testBonds = Bonds.get(testAtoms) self.assertTrue(testBonds[0][:2] == (0, 1)) self.assertTrue(testBonds[1][:2] == (2, 3)) self.assertTrue(np.all(testBonds[0][2] == (-1, 0, 0))) self.assertAlmostEqual(testBonds[0][3], 2*testBonds[1][3]) # Also test coordination histogram coord_hist = CoordinationHistogram.get(a) # Testing some qualities of the Alanine crystal... self.assertTrue(coord_hist['H']['C'][1], 16) # 16 H bonded to a C self.assertTrue(coord_hist['H']['N'][1], 12) # 12 H bonded to a N self.assertTrue(coord_hist['C']['H'][3], 4) # 4 CH3 groups self.assertTrue(coord_hist['C']['O'][2], 4) # 4 COO groups # Test molecules mols = Molecules.get(a) self.assertTrue(MoleculeNumber.get(a) == 4) self.assertTrue(np.isclose(MoleculeMass.get(a), 89.09408).all()) self.assertTrue(len(MoleculeCOMLinkage.get(a)) == 6) # Spectral sorting elems = np.array(a.get_chemical_symbols()) mol_specsort = MoleculeSpectralSort.get(a) for i in range(len(mols)-1): for j in range(i+1,len(mols)): self.assertTrue((elems[mol_specsort[i].indices] == elems[mol_specsort[j].indices]).all()) # Now testing hydrogen bonds hbs = HydrogenBonds.get(a) hbn = HydrogenBondsNumber.get(a) self.assertTrue(hbn['NH..O'] == 12) self.assertTrue(hbn['OH..O'] == 0)
def additionGen(struct, add, to_addition=None, n=1, add_r=1.2, accept=None): """Generator function to create multiple structures with an atom of a given element added in the existing cell. The atoms will be attached to the atoms passed in the to_addition selection. If none is passed, all atoms will be additioned in turn. Multiple defects can be included, in which case all permutations will be generated. The algorithm will try adding the atom in the direction that seems most compatible with all the already existing bonds. If multiple directions satisfy the condition, they will all be tested. It is also possible to reject some configurations based on the output of a filter function. | Args: | struct (ase.Atoms): the starting structure. All atoms will be added | to it. | add (str): element symbol of the atom to add. | to_replace (AtomSelection): if present, only atoms belonging to this | selection will be substituted. | n (int): number of new atoms to include in each structure. Default | is 1. | add_r (float): distance, in Angstroms, at which to add the atoms. | Default is 1.2 Ang | accept (function): a function that determines whether a generated | structure should be accepted or rejected. Takes as | input the generated structure and a tuple of | the indices of the atoms to which the new atoms | were added, and must return a bool. The newly added | atoms will always be the last n of the structure. | If False, the structure will be rejected. | Returns: | defectGenerator (generator): an iterator object that yields | structures with all possible additions. """ if to_addition is None: to_addition = AtomSelection.all(struct) # Compute bonds bonds = Bonds.get(struct) cell = struct.get_cell() pos = struct.get_positions() # Separate bonds by atoms atom_bonds = [[] if i in to_addition.indices else None for i in range(len(struct))] for b in bonds: v = pos[b[1]] - pos[b[0]] + np.dot(b[2], cell) try: atom_bonds[b[0]].append(v.copy()) except AttributeError: pass try: atom_bonds[b[1]].append(-v.copy()) except AttributeError: pass # Compute possible attachment points for each atom attach_v = [None] * len(struct) for i, bset in enumerate(atom_bonds): if bset is None: continue if len(bset) == 0: rndv = np.random.random((1, 3)) - 0.5 rndv /= np.linalg.norm(rndv, axis=1)[:, None] attach_v[i] = rndv else: attach_v[i] = utils.rep_alg(bset) attach_v = np.array(attach_v) addconfs = itertools.combinations(to_addition.indices, n) for ac in addconfs: addpos = itertools.product(*attach_v[list(ac)]) for ap in addpos: astruct = struct.copy() astruct += Atoms(add * n, positions=pos[list(ac)] + np.array(ap) * add_r) if accept is not None: if not accept(astruct, ac): continue yield astruct
def __main__(): parser = ap.ArgumentParser(description=""" Processes .magres files containing chemical groups of the form XH3 (by default, CH3 and NH3), to average the required NMR tensors for the hydrogen atoms connected to such groups. """) # Main argument parser.add_argument('input_files', type=str, nargs='+', default=None, help="Magres files on which to carry out averages") # Optional arguments parser.add_argument('-X', type=str, nargs='+', default=['C', 'N'], help="Nuclei to consider for XH3 groups " "(default: C and N)") parser.add_argument('-vdws', type=float, default=1.0, help="Van der Waals radius scale for bond calculation." " Increase for higher tolerance") parser.add_argument('-avg', type=str, nargs='+', default=['ms', 'efg'], help="Arrays to average over XH3 groups " "(default: ms and efg)") parser.add_argument('-prefix', type=str, default='avg', help="Prefix added to output files") args = parser.parse_args() bcalc = Bonds(vdw_scale=args.vdws, return_matrix=True) for f in args.input_files: try: a = io.read(f) except IOError: print('File {0} not found, skipping'.format(f)) continue # Do they have magres data? if not any([a.has(k) for k in args.avg]): print('File {0} has no data to average, skipping'.format(f)) continue # Find what to average bonds, bmat = bcalc(a) # Find XH3 groups symbs = np.array(a.get_chemical_symbols()) hinds = np.where(symbs == 'H')[0] h3groups = [] for xsymb in args.X: xinds = np.where(symbs == xsymb)[0] xinds = xinds[np.where(np.sum(bmat[xinds][:, hinds], axis=1) == 3)[0]] if len(xinds) > 0: h3groups.append(np.where(bmat[xinds][:, hinds] == 1)[1]) # Now average avg_a = a.copy() for k in args.avg: arr = a.get_array(k) for h3 in h3groups: arr[h3] = np.average(arr[h3], axis=0) avg_a.set_array(k, arr) io.write('{0}_{1}'.format(args.prefix, f), avg_a)