def interpret_sel(s, sel): if sel is None: return AtomSelection.all(s) elif is_string(sel): return AtomSelection.from_element(s, sel) elif hasattr(sel, '__call__'): return sel(s)
def extract(s, sel_i, sel_j, isotopes, isotope_list, self_coupling, block_size): # Selections if sel_i is None: sel_i = AtomSelection.all(s) elif not isinstance(sel_i, AtomSelection): sel_i = AtomSelection(s, sel_i) if sel_j is None: sel_j = sel_i elif not isinstance(sel_j, AtomSelection): sel_j = AtomSelection(s, sel_j) # Find gammas elems = s.get_chemical_symbols() gammas = _get_isotope_data(elems, 'gamma', isotopes, isotope_list) # Viable pairs pairs = [(i, j) for i in sel_i.indices for j in sel_j.indices] if not self_coupling: pairs = [p for p in pairs if p[0] != p[1]] pairs = np.array(pairs).T # Need to sort them and remove any duplicates, also take i < j as # convention pairs = np.array( list(zip(*set([tuple(x) for x in np.sort(pairs, axis=0).T])))) pos = s.get_positions() # Split this in blocks to make sure we don't clog the memory d_ij = np.zeros((0, )) v_ij = np.zeros((0, 3)) npairs = pairs.shape[1] for b_i in range(0, npairs, block_size): block = pairs.T[b_i:b_i + block_size] r_ij = pos[block[:, 1]] - pos[block[:, 0]] # Reduce to NN r_ij, _ = minimum_periodic(r_ij, s.get_cell(), exclude_self=True) # Distance R_ij = np.linalg.norm(r_ij, axis=1) # Versors v_ij = np.concatenate([v_ij, r_ij / R_ij[:, None]], axis=0) # Couplings d_ij = np.concatenate([ d_ij, _dip_constant(R_ij * 1e-10, gammas[block[:, 0]], gammas[block[:, 1]]) ]) return {tuple(ij): [d_ij[l], v_ij[l]] for l, ij in enumerate(pairs.T)}
def decorated_extrfunc(s, selection, **kwargs): # Perform basic checks on selection if selection is None: selection = AtomSelection.all(s) elif not selection.validate(s): raise ValueError('Selection passed to transform does not apply to' ' system.') return extrfunc(s, selection, **kwargs)
def test_iterate(self): a = Atoms('HCHC', positions=[[i] * 3 for i in range(4)], cell=[4] * 3, pbc=[True] * 3) sAll = AtomSelection.all(a) # Slicing? self.assertTrue((sAll[:2].indices == [0, 1]).all()) # Iterating? for i, s in enumerate(sAll): self.assertEqual(i, s.indices[0])
def extract(s, sel_i, sel_j, tag, isotopes, isotope_list, self_coupling, force_recalc): # Compute the diagonalised eigenvectors if necessary iname_pairs = ISCDiagonal.default_name + '_' + tag + '_pairs' iname_evals = ISCDiagonal.default_name + '_' + tag + '_evals' iname_evecs = ISCDiagonal.default_name + '_' + tag + '_evecs' if iname_pairs not in s.info or force_recalc: iprop = ISCDiagonal(tag=tag) iprop(s) all_pairs = list(s.info[iname_pairs]) all_evals = s.info[iname_evals] all_evecs = s.info[iname_evecs] # Selections if sel_i is None: sel_i = AtomSelection.all(s) elif not isinstance(sel_i, AtomSelection): sel_i = AtomSelection(s, sel_i) if sel_j is None: sel_j = sel_i elif not isinstance(sel_j, AtomSelection): sel_j = AtomSelection(s, sel_j) # Find gammas elems = s.get_chemical_symbols() _nmr_data = _get_nmr_data() gammas = _get_isotope_data(elems, 'gamma', isotopes, isotope_list) sel_pairs = [(i, j) for i in sel_i.indices for j in sel_j.indices] if not self_coupling: sel_pairs = [p for p in sel_pairs if p[0] != p[1]] jc_dict = {} for sp in sel_pairs: try: i = all_pairs.index(sp) except ValueError: continue evals = _J_constant(all_evals[i], gammas[sp[0]], gammas[sp[1]]) evecs = all_evecs[i] jc_dict[sp] = {'evals': evals, 'evecs': evecs} return jc_dict
def substitutionGen(struct, subst, to_replace=None, n=1, accept=None): """Generator function to create multiple structures with a defect of a given element substituted in the existing cell. The defects will be put in place of the atoms passed in the to_replace selection. If none is passed, all atoms will be replaced in turn. Multiple defects can be included, in which case all permutations will be generated. It is also possible to reject some configurations based on the output of a filter function. | Args: | struct (ase.Atoms): the starting structure. All defects will be added | to it. | subst (str): element symbol of the defect to add. | to_replace (AtomSelection): if present, only atoms belonging to this | selection will be substituted. | n (int): number of defects to include in each structure. Default is 1. | 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 substituted atoms, and must return a | bool. If False, the structure will be rejected. | Returns: | defectGenerator (generator): an iterator object that yields | structures with all possible | substitutions. """ if to_replace is None: to_replace = AtomSelection.all(struct) defconfs = itertools.combinations(to_replace.indices, n) elems = np.array(struct.get_chemical_symbols()).astype('S2') print(subst) for dc in defconfs: dstruct = struct.copy() delems = elems.copy() delems[list(dc)] = subst dstruct.set_chemical_symbols(delems) if accept is not None: if not accept(dstruct, dc): continue yield dstruct
def extract(s, sel_i, sel_j, isotopes, isotope_list, self_coupling): # Selections if sel_i is None: sel_i = AtomSelection.all(s) elif not isinstance(sel_i, AtomSelection): sel_i = AtomSelection(s, sel_i) if sel_j is None: sel_j = sel_i elif not isinstance(sel_j, AtomSelection): sel_j = AtomSelection(s, sel_j) # Find gammas elems = s.get_chemical_symbols() _nmr_data = _get_nmr_data() gammas = _get_isotope_data(elems, 'gamma', isotopes, isotope_list) # Viable pairs pairs = [(i, j) for i in sel_i.indices for j in sel_j.indices] if not self_coupling: pairs = [p for p in pairs if p[0] != p[1]] pairs = np.array(pairs).T # Need to sort them and remove any duplicates, also take i < j as # convention pairs = np.array(zip(*set([tuple(x) for x in np.sort(pairs, axis=0).T]))) pos = s.get_positions() r_ij = pos[pairs[1]] - pos[pairs[0]] # Reduce to NN r_ij, _ = minimum_periodic(r_ij, s.get_cell(), exclude_self=True) # Distance R_ij = np.linalg.norm(r_ij, axis=1) # Versors v_ij = r_ij/R_ij[:, None] # Couplings d_ij = _dip_constant(R_ij*1e-10, gammas[pairs[0]], gammas[pairs[1]]) return {tuple(ij): [d_ij[l], v_ij[l]] for l, ij in enumerate(pairs.T)}
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 dq_buildup(self, sel_i, sel_j=None, t_max=1e-3, t_steps=1000, R_cut=3, kdq=0.155, A=1, tau=np.inf): """ Return a dictionary of double quantum buildup curves for given pairs of atoms, built according to the theory given in: G. Pileio et al., "Analytical theory of gamma-encoded double-quantum recoupling sequences in solid-state nuclear magnetic resonance" Journal of Magnetic Resonance 186 (2007) 65-74 | Args: | sel_i (AtomSelection or [int]): Selection or list of indices of | atoms for which to compute the | curves. By default is None | (= all of them). | sel_i (AtomSelection or [int]): Selection or list of indices of | atoms for which to compute the | curves with sel_i. By default is | None (= same as sel_i). | t_max (float): maximum DQ buildup time, in seconds. Default | is 1e-3. | t_steps (int): number of DQ buildup time steps. Default is 1000. | R_cut (float): cutoff radius for which periodic copies to consider | in each pair, in Angstrom. Default is 3. | kdq (float): same as the k constant in eq. 35 of the reference. A | parameter depending on the specific sequence used. | Default is 0.155. | A (float): overall scaling factor for the curve. Default is 1. | tau (float): exponential decay factor for the curve. Default | is np.inf. | Returns: | curves (dict): a dictionary of all buildup curves indexed by pair, | plus the time axis in seconds as member 't'. """ tdq = np.linspace(0, t_max, t_steps) # Selections if sel_i is None: sel_i = AtomSelection.all(s) elif not isinstance(sel_i, AtomSelection): sel_i = AtomSelection(self._sample, sel_i) if sel_j is None: sel_j = sel_i elif not isinstance(sel_j, AtomSelection): sel_j = AtomSelection(self._sample, sel_j) # Find gammas elems = self._sample.get_chemical_symbols() gammas = _get_isotope_data(elems, 'gamma', {}, self._isos) # Need to sort them and remove any duplicates, also take i < j as # convention pairs = [ tuple(sorted((i, j))) for i in sorted(sel_i.indices) for j in sorted(sel_j.indices) ] scell_shape = minimum_supcell(R_cut, latt_cart=self._sample.get_cell()) nfg, ng = supcell_gridgen(self._sample.get_cell(), scell_shape) pos = self._sample.get_positions() curves = {'t': tdq} for ij in pairs: r = pos[ij[1]] - pos[ij[0]] all_r = r[None, :] + ng all_R = np.linalg.norm(all_r, axis=1) # Apply cutoff all_R = all_R[np.where((all_R <= R_cut) * (all_R > 0))] n = all_R.shape[0] bij = _dip_constant(all_R * 1e-10, gammas[ij[0]], gammas[ij[1]]) th = 1.5 * kdq * abs(bij[:, None]) * tdq[None, :] * 2 * np.pi x = (2 * th / np.pi)**0.5 Fs, Fc = fresnel(x * 2**0.5) x[:, 0] = np.inf bdup = 0.5-(1.0/(x*8**0.5)) * \ (Fc*np.cos(2*th) + Fs*np.sin(2*th)) bdup[:, 0] = 0 curves[ij] = A * np.sum(bdup, axis=0) * np.exp(-tdq / tau) return curves