def test_utilities(): eps = 1e-5 atoms = Icosahedron('Cu', 3) atoms.numbers[[0, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30]] = 79 atoms.center(vacuum=0.0) atoms.calc = EMT() with FIRE(atoms, logfile=None) as opt: opt.run(fmax=0.05) rmax = 8. nbins = 5 rdf, dists = get_rdf(atoms, rmax, nbins) calc_dists = np.arange(rmax / (2 * nbins), rmax, rmax / nbins) assert all(abs(dists - calc_dists) < eps) calc_rdf = [0., 0.84408157, 0.398689, 0.23748934, 0.15398546] assert all(abs(rdf - calc_rdf) < eps) dm = atoms.get_all_distances() s = np.zeros(5) for c in [(29, 29), (29, 79), (79, 29), (79, 79)]: inv_norm = len(np.where(atoms.numbers == c[0])[0]) / len(atoms) s += get_rdf(atoms, rmax, nbins, elements=c, distance_matrix=dm, no_dists=True) * inv_norm assert all(abs(s - calc_rdf) < eps) AuAu = get_rdf(atoms, rmax, nbins, elements=(79, 79), distance_matrix=dm, no_dists=True) assert all(abs(AuAu[-2:] - [0.12126445, 0.]) < eps) bulk = L1_2(['Au', 'Cu'], size=(3, 3, 3), latticeconstant=2 * np.sqrt(2)) rdf = get_rdf(bulk, 4.2, 5)[0] calc_rdf = [0., 0., 1.43905094, 0.36948605, 1.34468694] assert all(abs(rdf - calc_rdf) < eps)
def bond_count_vec(self, data): """Bond counting with a distribution measure for coordination. Parameters ---------- data : object Data object with atomic distances. Returns ------- track_nnmat : list List with summed number of atoms with given coordination numbers. """ elements = sorted(set(self.get_atomic_numbers(data))) # Get coordination number counting. dm = self.get_all_distances(data) rdf, dists = get_rdf(data, 10., 200, dm) nndist = dists[np.argmax(rdf)] + 0.2 track_nnmat = np.zeros((self.max_bonds, len(elements), len(elements))) for j in range(len(data)): row = elements.index(data[j].number) neighbors = [ k for k in range(len(dm[j])) if 0.1 < dm[j][k] < nndist ] ln = len(neighbors) if ln > 12: continue for l in neighbors: column = elements.index(data[l].number) track_nnmat[ln][row][column] += 1 return track_nnmat.ravel()
from ase.optimize.fire import FIRE from ase.lattice.compounds import L1_2 from ase.ga.utilities import get_rdf eps = 1e-5 atoms = Icosahedron('Cu', 3) atoms.numbers[[0, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30]] = 79 atoms.set_calculator(EMT()) opt = FIRE(atoms, logfile=None) opt.run(fmax=0.05) rmax = 8. nbins = 5 rdf, dists = get_rdf(atoms, rmax, nbins) calc_dists = np.arange(rmax / (2 * nbins), rmax, rmax / nbins) assert all(abs(dists - calc_dists) < eps) calc_rdf = [0., 0.84408157, 0.398689, 0.23748934, 0.15398546] assert all(abs(rdf - calc_rdf) < eps) dm = atoms.get_all_distances() s = np.zeros(5) for c in [(29, 29), (29, 79), (79, 29), (79, 79)]: inv_norm = len(np.where(atoms.numbers == c[0])[0]) / len(atoms) s += get_rdf( atoms, rmax, nbins, elements=c, distance_matrix=dm, no_dists=True) * inv_norm assert all(abs(s - calc_rdf) < eps) AuAu = get_rdf(atoms,
def nearestneighbour_vec(self, data): """Nearest neighbour average, Topics in Catalysis, 2014, 57, 33. This is a slightly modified version of the code found in the `ase.ga` module. Parameters ---------- data : object Data object with atomic numbers available. Returns ------- nnlist : list Feature vector that will be n**2 where n is the number of atomic species passed to the class. """ # Return feature names in no atomic data is passed. if data is None: msg = 'Class must have atom_types set to return feature names.' assert hasattr(self, 'atom_types') and self.atom_types is not \ None, msg names = [] for i in self.atom_types: names += [ '{0}_{1}_nnmat'.format(i, j) for j in self.atom_types ] return names # WARNING: Will be set permanently whichever atom is first passed. if self.atom_types is None: msg = 'atom_types variable will be set permanently to whichever ' msg += 'atom object is first passed' warnings.warn(msg) self.atom_types = sorted(frozenset(self.get_atomic_numbers(data))) # Calculate the distance parameters. dm = self.get_all_distances(data) rdf, dists = get_rdf(data, 10., 200, dm) nndist = dists[np.argmax(rdf)] + 0.2 # initialize correct shape feature martix. nnmat = np.zeros((len(self.atom_types), len(self.atom_types))) # Calculate the neighbor properties. for i, d in enumerate(data): row = [ j for j in range(len(self.atom_types)) if d.number == self.atom_types[j] ][0] neighbors = [j for j in range(len(dm[i])) if dm[i][j] < nndist] for n in neighbors: column = [ j for j in range(len(self.atom_types)) if data[n].number == self.atom_types[j] ][0] nnmat[row][column] += 1 # Normalize the features. for i, el in enumerate(self.atom_types): nnmat[i] /= len( [j for j in range(len(data)) if data[int(j)].number == el]) # convert matrix to vector and replace np.nan values. nnlist = np.nan_to_num(nnmat.flatten()) return nnlist
def get_rdf(self, rmax, nbins, imageIdx=None, elements=None, return_dists=False): """Get RDF. Wrapper for :meth:`ase.ga.utilities.get_rdf` with more selection possibilities. Parameters: rmax: float Maximum distance of RDF. nbins: int Number of bins to divide RDF. imageIdx: int/slice/None Images to analyze, see :func:`_get_slice` for details. elements: str/int/list/tuple Make partial RDFs. If elements is *None*, a full RDF is calculated. If elements is an *integer* or a *list/tuple of integers*, only those atoms will contribute to the RDF (like a mask). If elements is a *string* or a *list/tuple of strings*, only Atoms of those elements will contribute. Returns: return: list of lists / list of tuples of lists If return_dists is True, the returned tuples contain (rdf, distances). Otherwise only rdfs for each image are returned. """ sl = self._get_slice(imageIdx) r = [] el = None for image in self.images[sl]: if elements is None: tmpImage = image #integers elif isinstance(elements, int): tmpImage = Atoms(cell=image.get_cell(), pbc=image.get_pbc()) tmpImage.append(image[elements]) #strings elif isinstance(elements, str): tmpImage = Atoms(cell=image.get_cell(), pbc=image.get_pbc()) for idx in self._get_symbol_idxs(image, elements): tmpImage.append(image[idx]) #lists elif isinstance(elements, list) or isinstance(elements, tuple): #list of ints if all(isinstance(x, int) for x in elements): if len(elements) == 2: #use builtin get_rdf mask el = elements tmpImage = image else: #create dummy image tmpImage = Atoms(cell=image.get_cell(), pbc=image.get_pbc()) for idx in elements: tmpImage.append(image[idx]) #list of strings elif all(isinstance(x, str) for x in elements): tmpImage = Atoms(cell=image.get_cell(), pbc=image.get_pbc()) for element in elements: for idx in self._get_symbol_idxs(image, element): tmpImage.append(image[idx]) else: raise ValueError( "Unsupported type of elements given in ase.geometry.analysis.Analysis.get_rdf!" ) else: raise ValueError( "Unsupported type of elements given in ase.geometry.analysis.Analysis.get_rdf!" ) r.append( get_rdf(tmpImage, rmax, nbins, elements=el, no_dists=(not return_dists))) return r
print atoms up = Atoms() up.set_cell(atoms.get_cell()) up_target = 'X' dn = Atoms() dn.set_cell(atoms.get_cell()) dn_target = 'He' nuclei = Atoms() nuclei.set_cell(atoms.get_cell()) for a in range(len(atoms)): if atoms[a].symbol == up_target: up.append(atoms[a]) if atoms[a].symbol == dn_target: dn.append(atoms[a]) if atoms[a].symbol != up_target and atoms[a].symbol != dn_target: nuclei.append(atoms[a]) data_up = get_rdf(up, l, nmax) data_dn = get_rdf(dn, l, nmax) data_nuclei = get_rdf(nuclei, l, nmax) data_all = get_rdf(atoms, l, nmax) # data_up[0]/max(data_up[0])*len(up) # data_dn[0]/max(data_dn[0])*len(dn) bar(data_up[1], data_up[0], label='up', width=0.02) bar(data_dn[1], data_dn[0], label='dn', width=0.01) bar(data_nuclei[1], data_nuclei[0], label='nuclei', width=0.02) #bar(data_all[1],data_all[0],label='all',width=0.05) legend() show()
def estimate_rcut(dbfiles, element1, element2): ''' Returns an estimated, optimal cutoff radius for the repulsive interaction between the given elements. It is estimated as the radius for the first minimum after the first maximum of the radial distribution function (RDF), as employed in e.g. http://dx.doi.org/10.1021/acs.jctc.5b00742 dbfiles: list of database filenames which contain the structures for calculating the RDF element1: the symbol of the first element element2: the symbol of the second element ''' num1 = atomic_numbers[element1] num2 = atomic_numbers[element2] cr1 = covalent_radii[num1] cr2 = covalent_radii[num2] rlim = 2 * (cr1 + cr2) d = 0.1 nbins = int(rlim / d) rdf_tot = np.zeros(nbins) dist = None for dbfile in dbfiles: db = connect(dbfile) for row in db.select(relaxed=1): atoms = row.toatoms() sym = atoms.get_chemical_symbols() if element1 not in sym or element2 not in sym: continue cell = atoms.get_cell() vol = atoms.get_volume() rep = [1, 1, 1] for i in range(3): if atoms.pbc[i]: axb = np.cross(cell[(i + 1) % 3, :], cell[(i + 2) % 3, :]) h = vol / np.linalg.norm(axb) rep[i] = int(np.ceil((2.001 * rlim) / h)) a = atoms.repeat(rep) rdf, dist = get_rdf(a, rlim, nbins, elements=(num1, num2)) rdf_tot += rdf if dist is None: print('Warning: no %s-%s nearest neighbours in data sets' % \ (element1, element2)) return 0. rdf_smooth = gaussian_filter1d(rdf_tot, sigma=1, mode='reflect') imax = len(dist) - 1 for imax in find_peaks(rdf_smooth)[0]: if rdf_smooth[imax] > 0.5 * np.max(rdf_smooth): break imin = min(imax + 1, len(dist) - 1) for imin in find_peaks(-rdf_smooth)[0]: if imin > imax: break rmin = dist[imin] rmax = dist[imax] rcut = rmin if imin > imax else rlim print('%s-%s RDF: 1st maximum = %.3f' % (element1, element2, rmax)) print('%s-%s RDF: subsequent minimum = %.3f' % (element1, element2, rmin)) print('%s-%s RDF: suggested rcut = %.3f' % (element1, element2, rcut)) try: plt.plot(dist, rdf_tot, '--', label='as-is') plt.plot(dist, rdf_smooth, '-', label='smoothened') plt.plot([rmax, rmax], [0, max(rdf_tot)], label='first maximum') plt.plot([rcut, rcut], [0, max(rdf_tot)], label='suggested rcut') plt.xlabel('%s-%s distance [Angstrom]' % (element1, element2)) plt.ylabel('RDF [-]') plt.legend(loc='upper left') plt.savefig('%s-%s_rdf.pdf' % (element1, element2)) plt.clf() except Exception as err: print(err.message) return rcut
from ase.optimize.fire import FIRE from ase.lattice.compounds import L1_2 from ase.ga.utilities import get_rdf eps = 1e-5 atoms = Icosahedron('Cu', 3) atoms.numbers[[0, 13, 15, 16, 18, 19, 21, 22, 24, 25, 27, 28, 30]] = 79 atoms.set_calculator(EMT()) opt = FIRE(atoms, logfile=None) opt.run(fmax=0.05) rmax = 8. nbins = 5 rdf, dists = get_rdf(atoms, rmax, nbins) calc_dists = np.arange(rmax / (2 * nbins), rmax, rmax / nbins) assert all(abs(dists - calc_dists) < eps) calc_rdf = [0., 0.84408157, 0.398689, 0.23748934, 0.15398546] assert all(abs(rdf - calc_rdf) < eps) dm = atoms.get_all_distances() s = np.zeros(5) for c in [(29, 29), (29, 79), (79, 29), (79, 79)]: inv_norm = len(np.where(atoms.numbers == c[0])[0]) / len(atoms) s += get_rdf(atoms, rmax, nbins, elements=c, distance_matrix=dm, no_dists=True) * inv_norm assert all(abs(s - calc_rdf) < eps) AuAu = get_rdf(atoms, rmax, nbins, elements=(79, 79), distance_matrix=dm, no_dists=True)