def len_supercell(atoms): tags = atoms.get_tags() if not tags.any(): logger.warning( 'atoms object appears to have only 1 layer or tags are missing') bottom_layer = atoms[tags == 0] x_tags, x_positions = get_layers(bottom_layer, (1, 0, 0), 0.3) x = len(x_positions) y_tags, y_positions = get_layers(bottom_layer, (0, 1, 0), 0.3) y = len(y_positions) z_tags, z_positions = get_layers(atoms, (0, 0, 1), 0.3) z = len(z_positions) return x, y, z
def create_surface(sc_shape): from scipy.spatial import cKDTree as KDTree atoms = bulk("Al") * sc_shape if sc_shape[2] % 2 != 0: raise ValueError("The third direction has to be divisible by 2!") num_layers = int(sc_shape[2] / 2) # Create a cut such that a3 vector is the normal vector slab = cut(atoms, a=(1, 0, 0), b=(0, 1, 0), c=(0, 0, 1), nlayers=num_layers) tags, _ = get_layers(slab, (1, -1, 0)) for tag, atom in zip(tags, slab): if tag % 2 == 0: atom.symbol = "Mg" else: atom.symbol = "Si" tree = KDTree(atoms.get_positions()) used_indices = [] for atom in slab: _, closest_indx = tree.query(atom.position) if closest_indx in used_indices: raise RuntimeError("Two atoms are mapped onto the same index!") atoms[closest_indx].symbol = atom.symbol used_indices.append(closest_indx) return atoms
def from_ase(atoms, zsubs): def get_layer(atoms, atom): atomlayers = geometry.get_layers(atoms, (0, 0, 1), tolerance=0.01) return atomlayers[0][atom.index] sublattices = {} layer_by_atom = geometry.get_layers(atoms, (0, 0, 1), tolerance=0.01)[0] layers = list(set(layer_by_atom)) if collections.Counter(zsubs[0]) == collections.Counter(layers): vectors = np.array([v for v in atoms.get_cell()]) sites = np.array(atoms.get_scaled_positions()) numbers = atoms.get_atomic_numbers() sublattices[0] = [at.index for at in atoms] else: vectors = np.array([v for v in atoms.get_cell()]) sites = np.array(atoms.get_scaled_positions()) numbers = atoms.get_atomic_numbers() for ind, lat in enumerate(zsubs): sublat = [] for at in atoms: if get_layer(atoms, at) in sublat: sublat.append(at.index) sublattices[ind] = sublat return (vectors, sites, numbers, sublattices)
def test_layered(self): if not available: self.skipTest(reason) atoms = bulk("Al", crystalstructure="sc", a=5.0) atoms *= (10, 10, 10) layers, dist = get_layers(atoms, (1, 0, 0)) for atom in atoms: if layers[atom.index] % 2 == 0: atom.symbol = "Al" else: atom.symbol = "Mg" k = 2.0 * np.pi / 10.0 updater = DiffractionUpdater(atoms=atoms, k_vector=[k, 0, 0], active_symbols=["Mg"], all_symbols=["Al", "Mg"]) self.assertAlmostEqual(np.abs(updater.value), 0.5) updater = DiffractionUpdater(atoms=atoms, k_vector=[0, k, 0], active_symbols=["Mg"], all_symbols=["Al", "Mg"]) self.assertAlmostEqual(np.abs(updater.value), 0.0)
def plot_pdos_layer(self, atoms=None, layers=None, miller=(0, 0, 1), dz=0.3, Emin=-5, Emax=3, total=False, select=None, fill=True, smearing=None, output=None): ''' ''' atoms = self.atoms if not layers: layers = get_layers(atoms, miller, dz)[0] nlayers = max(layers) + 1 fig, axs = plt.subplots(nlayers, 1, figsize=(10, nlayers * 2), sharex=True) indexs = range(len(atoms)) images = [] xindex = (self.pdos_energies > Emin) & (self.pdos_energies < Emax) for ilayer in range(nlayers): iax = nlayers - ilayer - 1 # axs[iax].set_yticks([]) index = [j for j in indexs if layers[j] == ilayer] images.append(atoms[index]) pdos_kinds = self.merge_kinds(index) self.plot_pdos(energies=self.pdos_energies, pdos_kinds=pdos_kinds, select=select, Emin=Emin, Emax=Emax, ax=axs[iax], legend=False, xylabel=False, fill=fill, smearing=smearing) axs[iax].axvline(0, color='b') # print(iax, ilayer) # print(index) # axs[iax].set_xlim([Emin, Emax]) plt.xlabel('E - E$_{Fermi}$ (eV)', size='16') plt.subplots_adjust(hspace=0) fig.text(0.05, 0.5, 'PDOS (a.u.)', size='16', va='center', rotation='vertical') if output is not None: # output = '{0}-pdos.png'.format(self.prefix) plt.savefig('%s' % output) # view(images) return axs, images
def mgsi(initial=None, comment=None): if initial is None: pureAl = bulk('Al', cubic=True) * (N, N, N) pureAl = wrap_and_sort_by_position(pureAl) tags, _ = get_layers(pureAl, (0, 0, 1)) success = False while not success: print("Generating new initial precipitate") initial, success = create_precipitate(pureAl.copy(), tags, normal_radius) db = dataset.connect(DB_NAME) ref_tbl = db[REF] vac_tbl = db[VAC] sol_tbl = db[SOL] comment_tbl = db[COMMENT] runID = hex(random.randint(0, 2**32 - 1)) if comment is not None: comment_tbl.insert({'runID': runID, 'comment': comment}) eci = {} with open("data/almgsix_normal_ce.json", 'r') as infile: data = json.load(infile) eci = data['eci'] settings = settingsFromJSON("data/settings_almgsiX_voldev.json") settings.basis_func_type = "binary_linear" atoms = attach_calculator(settings, initial.copy(), eci) atoms.numbers = initial.numbers ref_energy = atoms.get_potential_energy() ref_tbl.insert({'runID': runID, 'energy': ref_energy}) for atom in atoms: if atom.symbol in ['Mg', 'Si']: pos = atom.position sol_tbl.insert({ 'runID': runID, 'symbol': atom.symbol, 'X': pos[0], 'Y': pos[1], 'Z': pos[2] }) ref, neighbors = neighbor_list('ij', atoms, 3.0) for ref, nb in zip(ref, neighbors): if atoms[ref].symbol == 'Al' and atoms[nb].symbol in ['Mg', 'Si']: atoms[ref].symbol = 'X' e = atoms.get_potential_energy() atoms[ref].symbol = 'Al' pos = atoms[ref].position vac_tbl.insert({ 'runID': runID, 'X': pos[0], 'Y': pos[1], 'Z': pos[2], 'energy': e }) atoms = removeAl(atoms)
def fix_layers(atoms, miller = (0, 0, 1), tol = 1.0, n = [0, 4]): ''' ''' layers = get_layers(atoms, miller, tol)[0] index = [j for j in range(len(atoms)) if layers[j] in range(n[0], n[1])] constraint = FixAtoms(indices=index) atoms.set_constraint(constraint) return atoms
def create_matsudada(): atoms = bulk("Al", cubic=True, a=4.05) atoms = atoms*(4, 1, 4) # Construct Matsudada structure layer_100, _ = get_layers(atoms, (1, 0, 0)) indices = [atom.index for atom in atoms if layer_100[atom.index] == 0] layer_010, _ = get_layers(atoms, (0, 1, 0)) for indx in indices: if layer_010[indx]%2 == 0: atoms[indx].symbol = "Mg" else: atoms[indx].symbol = "Si" fname = "data/mgsi_matsudada.xyz" write(fname, atoms) print("Structure written to {}".format(fname))
def closestPackLabeling( atoms ): """ Gives each atom a label A,B or C depending on which layer they are in """ atomLabels = [] layerIndx, dist = geometry.get_layers( atoms, (1,1,1), tolerance=0.8 ) print (layerIndx,dist) exit()
def mgsi(interface): atoms = bulk('Al', cubic=True) mgsi = atoms.copy() miller = (0, 0, 1) if interface == SILICON or interface == MAGNESIUM: miller = (1, 0, 0) tags, levels = get_layers(mgsi, miller) for i, atom in enumerate(mgsi): if interface == SILICON or interface == ALTERNATING: if tags[i] % 2 == 0: atom.symbol = 'Mg' else: atom.symbol = 'Si' elif interface == MAGNESIUM: if tags[i] % 2 == 0: atom.symbol = 'Si' else: atom.symbol = 'Mg' # Tag all atoms to -1 for atom in mgsi: atom.tag = -1 mgsi = mgsi * (4, 4, 4) matrix = atoms.copy() for atom in matrix: atom.tag = -1 matrix = matrix * (4, 4, 4) active_area = atoms.copy() active_area = active_area * (4, 4, 4) tags, layers = get_layers(active_area, (1, 0, 0)) for t, atom in zip(tags, active_area): atom.tag = t # Stack togeter atoms = stack(mgsi, active_area, axis=0) atoms = stack(atoms, matrix, axis=0) atoms = wrap_and_sort_by_position(atoms) write(f"data/mgsi_active_matrix_{interface}_interface.xyz", atoms)
def get_top_struct(atoms): atoms_lst = [] layers = geometry.get_layers(atoms, (0, 0, 1), tolerance=0.1) lays = list(set(layers[0])) for atom in atoms: if get_layer(atoms, atom) in lays[-5:]: atoms_lst.append(atom) new_atoms = Atoms(atoms_lst) new_atoms.set_cell(np.add(atoms.get_cell(), [0, 0, 0.])) new_atoms.center(axis=2) new_atoms.set_pbc([True, True, True]) return new_atoms
def get_cubic_nano_particle(layer=0): from ase.cluster.cubic import FaceCenteredCubic from ase.geometry import get_layers surfaces = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] layers = cubic_np_layer[layer] lc = 4.05 atoms = FaceCenteredCubic('Si', surfaces, layers, latticeconstant=lc) tags, array = get_layers(atoms, (1, 0, 0)) for t, atom in zip(tags, atoms): if t % 2 == 0: atom.symbol = "Mg" print(atoms.get_chemical_formula()) return atoms
def get_layer_frame(structure, miller_index): layer_indexes, layer_distances = get_layers(structure, miller_index) repeating_layer_distances = [] for i in range(len(layer_indexes)): layer_distance = layer_distances[layer_indexes[i]] repeating_layer_distances.append(layer_distance) repeating_layer_distances = np.array(repeating_layer_distances) layer_dict = {'layer_index': layer_indexes, 'layer_distance': repeating_layer_distances} layer_frame = pd.DataFrame(data=layer_dict) layer_frame.index.name = "structure_index" return layer_frame
def gen_midpoints(slab: Atoms, cutoff_dist: float = 3.5, heights: Iterable[float] = None, **kw): """ :param slab: :param cutoff_dist: :param heights: :param kw: :return: """ # default heights h = heights or [(0, 0, i) for i in (0, 2, 1.8, 1.5, 1.3)] tags, layer_pos = get_layers(slab, (0, 0, 1), 0.3) surface_atoms = slab[tags == max(tags)] # n_atoms = len(surface_atoms) atoms = surface_atoms.repeat([3, 3, 1]) n_points = len(atoms) # TODO: check if atoms moved atoms.wrap(center=(1 / 6, 1 / 6, 0)) mem = set() def memo(pos: Tuple[float, float]) -> bool: label = f'{pos[0]:.2f} {pos[1]:.2f}' res = label in mem if not res: mem.add(label) return res for i, pos in enumerate(surface_atoms.positions): memo(pos) yield (f'1_{i}', pos + h[1]) for i in range(2, len(h)): ix = 0 for indices in combinations(range(n_points), i): if not valid_comb(indices, surface_atoms, atoms, cutoff_dist): continue positions = atoms.positions[list(indices)] # TODO: if i == 2: mean = np.mean(positions, axis=0) else: mean = get_incenter(positions[0], positions[1], positions[2]) mean += h[i] if not in_cell(surface_atoms, position=mean) or memo(tuple(mean)): continue yield (f'{i}_{ix}', mean) ix += 1
def get_nanoparticle(): from ase.cluster.cubic import FaceCenteredCubic from ase.geometry import get_layers surfaces = [(1, 0, 0), (1, 1, 0), (1, 1, 1)] layers = [10, 13, 9] lc = 4.05 atoms = FaceCenteredCubic('Si', surfaces, layers, latticeconstant=lc) tags, array = get_layers(atoms, (1, 0, 0)) for t, atom in zip(tags, atoms): if t % 2 == 0: atom.symbol = "Mg" print(atoms.get_chemical_formula()) atoms.rotate(90, 'z', rotate_cell=True) return atoms
def create_linear_elastic(): atoms = bulk("Al", cubic=True, a=4.05) atoms = atoms*(4, 1, 4) # Construct structure predicted by linear elasticity layer_101, _ = get_layers(atoms, (1, 0, 1)) mg_indices = [atom.index for atom in atoms if layer_101[atom.index] == 6] si_indices = [atom.index for atom in atoms if layer_101[atom.index] == 7] for indx in mg_indices: atoms[indx].symbol = "Mg" for indx in si_indices: atoms[indx].symbol = "Si" fname = "data/mgsi_linear_elastic.xyz" write(fname, atoms) print("Structure written to {}".format(fname))
def main(): a = 4.05 atoms = bulk('Al', cubic=True, a=a) * (N, N, N) tags, _ = get_layers(atoms, (0, 0, 1)) pos = atoms.get_positions().copy() center = np.mean(pos, axis=0) + np.array([a / 4, a / 4, 0.0]) pos -= center radii = np.sqrt(np.sum(pos[:, :2]**2, axis=1)) indices = np.argsort(radii) num_per_layer = 5 all_indices = [] for layer in range(2, 12): symbol = 'Mg' if layer % 2 == 0 else 'Si' num_inserted = 0 for i in indices: if tags[i] == layer and num_inserted < num_per_layer: atoms[i].symbol = symbol num_inserted += 1 all_indices.append(i) # Extract all items in the 2 layers above indices_si = [] indices_mg = [] num_si = 4 num_mg = 5 for layer in range(12, 14): num_extracted = 0 num_to_extract = num_mg if layer % 2 == 0 else num_si for i in indices: if tags[i] == layer and num_extracted < num_to_extract: if layer % 2 == 0: indices_mg.append(i) else: indices_si.append(i) num_extracted += 1 print(len(indices_mg), len(indices_si)) traj = TrajectoryWriter("data/specialMgSiClusters.traj") for symbMg in product(['Al', 'Mg'], repeat=len(indices_mg)): for symbSi in product(['Al', 'Si'], repeat=len(indices_si)): atoms_cpy = atoms.copy() for i in range(len(indices_mg)): atoms_cpy[indices_mg[i]].symbol = symbMg[i] for j in range(len(indices_si)): atoms_cpy[indices_si[j]].symbol = symbSi[j] traj.write(atoms_cpy)
def auto_layers(atoms, miller=(0, 0, 1)): """Returns two arrays describing which layer each atom belongs to and the distance between the layers and origo. Assumes the tolerance corresponds to the average atomic radii of the slab. Parameters ---------- atoms : object The atoms object must have the following keys in atoms.subsets: 'slab_atoms' : list indices of atoms belonging to the slab """ radii = [get_radius(z) for z in atoms.numbers[atoms.subsets['slab_atoms']]] radius = np.average(radii) / 2. lz, li = get_layers(atoms, miller=miller, tolerance=radius) return lz, li
def insert_np(size, atoms): cluster = bulk("Al", cubic=True) * (size, size, size) tags, array = get_layers(cluster, (1, 0, 0)) for atom in cluster: if atom.index % 2 == 0: atom.symbol = "Si" else: atom.symbol = "Mg" tree = KDTree(atoms.get_positions()) symbols = [a.symbol for a in atoms] for atom in cluster: closest = tree.query(atom.position)[1] symbols[closest] = atom.symbol atoms.get_calculator().set_symbols(symbols) return atoms
def center_at_origin(atoms: Atoms) -> Atoms: """ Returns a copy of the atoms object centered at the lowest atoms in the z axis :param atoms: Atoms object :return: Copy of the translated Atoms object """ ads = atoms.copy() if len(ads) == 1: ads[0].position = (0, 0, 0) else: tags, layer_pos = get_layers(ads, (0, 0, 1), 0.3) if sum(tags == 0) == 1: center = atoms[0].position else: lowest_atoms = ads[tags == 0] center = np.mean([a.position for a in lowest_atoms], axis=0) ads.translate(-center) return ads
def layers2ads_index(atoms, formula): """ Returns the indexes of atoms in layers exceeding the number of layers stored in the key value pair 'layers'. Parameters ---------- atoms : ase atoms object. atoms.info must be a dictionary containing the key 'key_value_pairs', which is expected to contain CatMAP standard adsorbate structure key value pairs. See the ase db to catmap module in catmap. the key value pair 'species' must be the chemical formula of the adsorbate and 'layers' must be an integer. """ composition = string2symbols(formula) n_ads = len(composition) natoms = len(atoms) radii = [get_radius(s) for s in composition] lz, li = get_layers(atoms, (0, 0, 1), tolerance=np.average(radii)) layers = int(atoms.info['key_value_pairs']['layers']) ads_atoms = [a.index for a in atoms if li[a.index] > layers - 1] ads_atoms = list(range(natoms - n_ads, natoms)) if len(ads_atoms) != len(composition): raise AssertionError("ads atoms identification by layers failed.") return ads_atoms
def test_update_method(self): if not available: self.skipTest(reason) atoms = bulk("Al", crystalstructure="sc", a=5.0) atoms *= (10, 10, 10) layers, dist = get_layers(atoms, (1, 0, 0)) for atom in atoms: if layers[atom.index] % 2 == 0: atom.symbol = "Al" else: atom.symbol = "Mg" k = 2.0 * np.pi / 10.0 updater = DiffractionUpdater(atoms=atoms, k_vector=[k, 0, 0], active_symbols=["Mg"], all_symbols=["Al", "Mg"]) # Remove all Al atoms for i in range(len(atoms)): if atoms[i].symbol == "Al": system_change = [(i, "Al", "Mg")] updater.update(system_change) # Reflection should be zero self.assertAlmostEqual(np.abs(updater.value), 0.0) # Insert Al atoms again for i in range(len(atoms)): if layers[i] % 2 == 0: system_change = [(i, "Mg", "Al")] updater.update(system_change) # Now value should be back to 1/2 self.assertAlmostEqual(np.abs(updater.value), 0.5)
def set_layer_info(atoms, miller=(0, 0, 1), tolerance=0.001): tags = get_layers(atoms, miller, tolerance) atoms.set_tags(tags[0])
def get_layer(atoms, atom): atomlayers = geometry.get_layers(atoms, (0, 0, 1), tolerance=0.01) return atomlayers[0][atom.index]
# Cut out slab of 5 Al(001) layers al001 = cut(al, nlayers=5) correct_pos = np.array([[0., 0., 0.], [0., 0.5, 0.2], [0.5, 0., 0.2], [0.5, 0.5, 0.], [0., 0., 0.4], [0., 0.5, 0.6], [0.5, 0., 0.6], [0.5, 0.5, 0.4], [0., 0., 0.8], [0.5, 0.5, 0.8]]) assert np.allclose(correct_pos, al001.get_scaled_positions()) # Check layers along 001 tags, levels = get_layers(al001, (0, 0, 1)) assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4]) assert np.allclose(levels, [0., 2.025, 4.05, 6.075, 8.1]) # Check layers along 101 tags, levels = get_layers(al001, (1, 0, 1)) assert np.allclose(tags, [0, 1, 5, 3, 2, 4, 8, 7, 6, 9]) assert np.allclose(levels, [0.000, 0.752, 1.504, 1.880, 2.256, 2.632, 3.008, 3.384, 4.136, 4.888], atol=0.001) # Check layers along 111 tags, levels = get_layers(al001, (1, 1, 1)) assert np.allclose(tags, [0, 2, 2, 4, 1, 5, 5, 6, 3, 7]) assert np.allclose(levels, [0.000, 1.102, 1.929, 2.205, 2.756, 3.031, 3.858, 4.960],
def surfaces_with_termination(lattice, indices, layers, vacuum=None, tol=1e-10, termination=None, return_all=False, verbose=False): """Create surface from a given lattice and Miller indices with a given termination Parameters ========== lattice: Atoms object or str Bulk lattice structure of alloy or pure metal. Note that the unit-cell must be the conventional cell - not the primitive cell. One can also give the chemical symbol as a string, in which case the correct bulk lattice will be generated automatically. indices: sequence of three int Surface normal in Miller indices (h,k,l). layers: int Number of equivalent layers of the slab. (not the same as the layers you choose from for terminations) vacuum: float Amount of vacuum added on both sides of the slab. termination: str the atoms you wish to be in the top layer. There may be many such terminations, this function returns all terminations with the same atomic composition. e.g. 'O' will return oxygen terminated surfaces. e.g.'TiO' will return surfaces terminated with layers containing both O and Ti Returns: return_surfs: List a list of surfaces that match the specifications given """ lats = translate_lattice(lattice, indices) return_surfs = [] check = [] check2 = [] for item in lats: too_similar = False surf = surface(item, indices, layers, vacuum=vacuum, tol=tol) surf.wrap(pbc=[True] * 3) # standardize slabs positions = surf.get_scaled_positions().flatten() for i, value in enumerate(positions): if value >= 1 - tol: # move things closer to zero within tol positions[i] -= 1 surf.set_scaled_positions(np.reshape(positions, (len(surf), 3))) #rep = find_z_layers(surf) z_layers, hs = get_layers(surf, (0, 0, 1)) # just z layers matter # get the indicies of the atoms in the highest layer top_layer = [ i for i, val in enumerate(z_layers == max(z_layers)) if val ] if termination is not None: comp = [surf.get_chemical_symbols()[a] for a in top_layer] term = string2symbols(termination) # list atoms in top layer and not in requested termination check = [a for a in comp if a not in term] # list of atoms in requested termination and not in top layer check2 = [a for a in term if a not in comp] if len(return_surfs) > 0: pos_diff = [ a.get_positions() - surf.get_positions() for a in return_surfs ] for i, su in enumerate(pos_diff): similarity_test = su.flatten() < tol * 1000 if similarity_test.all(): # checks if surface is too similar to another surface too_similar = True if too_similar: continue if return_all is True: pass elif check != [] or check2 != []: continue return_surfs.append(surf) return return_surfs
def detect_termination(atoms): """ Returns three lists, the first containing indices of bulk atoms and the second containing indices of atoms in the second outermost layer, and the last denotes atoms in the outermost layer or termination or the slab. Parameters ---------- atoms : ase atoms object. The atoms object must have the following keys in atoms.subsets: - 'ads_atoms' : list indices of atoms belonging to the adsorbate - 'slab_atoms' : list indices of atoms belonging to the slab """ max_coord = 0 try: cm = atoms.connectivity.copy() np.fill_diagonal(cm, 0) # List coordination numbers of slab atoms. coord = np.count_nonzero(cm[atoms.subsets['slab_atoms'], :], axis=1) coord -= 1 bulk = [] term = [] max_coord = np.max(coord) for j, c in enumerate(coord): a_s = atoms.subsets['slab_atoms'][j] if (c < max_coord and a_s not in atoms.constraints[0].get_indices()): term.append(a_s) else: bulk.append(a_s) subsurf = [] for a_b in bulk: for a_t in term: if cm[a_t, a_b] > 0: subsurf.append(a_b) subsurf = list(np.unique(subsurf)) try: sx, sy = atoms.info['key_value_pairs']['supercell'].split('x') sx = int(''.join(i for i in sx if i.isdigit())) sy = int(''.join(i for i in sy if i.isdigit())) if int(sx) * int(sy) != len(term): msg = str(len(term)) + ' termination atoms identified.' + \ 'size = ' + atoms.info['key_value_pairs']['supercell'] + \ ' id: ' + str(atoms.info['id']) warnings.warn(msg) except KeyError: pass if len(bulk) is 0 or len(term) is 0 or len(subsurf) is 0: # print(il, zl) # msg = 'dbid: ' + str(atoms.info['id']) raise AssertionError() except (AssertionError, IndexError): layers = int(atoms.info['key_value_pairs']['layers']) radii = [ get_radius(z) for z in atoms.numbers[atoms.subsets['slab_atoms']] ] radius = np.average(radii) il, zl = get_layers(atoms, (0, 0, 1), tolerance=radius) if len(zl) < layers: # msg = 'dbid: ' + str(atoms.info['id']) raise AssertionError() # bulk atoms includes subsurface atoms. bulk = [a.index for a in atoms if il[a.index] <= layers - 2] subsurf = [a.index for a in atoms if il[a.index] == layers - 2] term = [ a.index for a in atoms if il[a.index] >= layers - 1 and a.index not in atoms.subsets['ads_atoms'] ] if len(bulk) is 0 or len(term) is 0 or len(subsurf) is 0: print(bulk, term, subsurf) msg = 'Detect bulk/term.' if 'id' in atoms.info: msg += ' id: ' + str(atoms.info['id']) print(il, zl) raise AssertionError(msg) for a_s in atoms.subsets['site_atoms']: if a_s not in term: msg = 'site not in term.' if 'id' in atoms.info: msg += str(atoms.info['id']) warnings.warn(msg) break return bulk, term, subsurf
def test_geometry(): """Test the ase.geometry module and ase.build.cut() function.""" import numpy as np from ase.build import cut, bulk, fcc111 from ase.cell import Cell from ase.geometry import get_layers, wrap_positions from ase.spacegroup import crystal, get_spacegroup al = crystal('Al', [(0, 0, 0)], spacegroup=225, cellpar=4.05) # Cut out slab of 5 Al(001) layers al001 = cut(al, nlayers=5) correct_pos = np.array([[0., 0., 0.], [0., 0.5, 0.2], [0.5, 0., 0.2], [0.5, 0.5, 0.], [0., 0., 0.4], [0., 0.5, 0.6], [0.5, 0., 0.6], [0.5, 0.5, 0.4], [0., 0., 0.8], [0.5, 0.5, 0.8]]) assert np.allclose(correct_pos, al001.get_scaled_positions()) # Check layers along 001 tags, levels = get_layers(al001, (0, 0, 1)) assert np.allclose(tags, [0, 1, 1, 0, 2, 3, 3, 2, 4, 4]) assert np.allclose(levels, [0., 2.025, 4.05, 6.075, 8.1]) # Check layers along 101 tags, levels = get_layers(al001, (1, 0, 1)) assert np.allclose(tags, [0, 1, 5, 3, 2, 4, 8, 7, 6, 9]) assert np.allclose(levels, [0.000, 0.752, 1.504, 1.880, 2.256, 2.632, 3.008, 3.384, 4.136, 4.888], atol=0.001) # Check layers along 111 tags, levels = get_layers(al001, (1, 1, 1)) assert np.allclose(tags, [0, 2, 2, 4, 1, 5, 5, 6, 3, 7]) assert np.allclose(levels, [0.000, 1.102, 1.929, 2.205, 2.756, 3.031, 3.858, 4.960], atol=0.001) # Cut out slab of three Al(111) layers al111 = cut(al, (1, -1, 0), (0, 1, -1), nlayers=3) correct_pos = np.array([[0.5, 0., 0.], [0., 0.5, 0.], [0.5, 0.5, 0.], [0., 0., 0.], [1 / 6., 1 / 3., 1 / 3.], [1 / 6., 5 / 6., 1 / 3.], [2 / 3., 5 / 6., 1 / 3.], [2 / 3., 1 / 3., 1 / 3.], [1 / 3., 1 / 6., 2 / 3.], [5 / 6., 1 / 6., 2 / 3.], [5 / 6., 2 / 3., 2 / 3.], [1 / 3., 2 / 3., 2 / 3.]]) assert np.allclose(correct_pos, al111.get_scaled_positions()) # Cut out cell including all corner and edge atoms (non-periodic structure) al = cut(al, extend=1.1) correct_pos = np.array([[0., 0., 0.], [0., 2.025, 2.025], [2.025, 0., 2.025], [2.025, 2.025, 0.], [0., 0., 4.05], [2.025, 2.025, 4.05], [0., 4.05, 0.], [2.025, 4.05, 2.025], [0., 4.05, 4.05], [4.05, 0., 0.], [4.05, 2.025, 2.025], [4.05, 0., 4.05], [4.05, 4.05, 0.], [4.05, 4.05, 4.05]]) assert np.allclose(correct_pos, al.positions) # Create an Ag(111)/Si(111) interface ag = crystal(['Ag'], basis=[(0, 0, 0)], spacegroup=225, cellpar=4.09) si = crystal(['Si'], basis=[(0, 0, 0)], spacegroup=227, cellpar=5.43) try: assert get_spacegroup(ag).no == 225 assert get_spacegroup(si).no == 227 except ImportError: pass ag111 = cut(ag, a=(4, -4, 0), b=(4, 4, -8), nlayers=5) # noqa si111 = cut(si, a=(3, -3, 0), b=(3, 3, -6), nlayers=5) # noqa # # interface = stack(ag111, si111) # assert len(interface) == 1000 # assert np.allclose(interface.positions[::100], # [[ 4.08125 , -2.040625 , -2.040625 ], # [ 8.1625 , 6.121875 , -14.284375 ], # [ 10.211875 , 0.00875 , 2.049375 ], # [ 24.49041667, -4.07833333, -16.32208333], # [ 18.37145833, 14.29020833, -24.48166667], # [ 24.49916667, 12.25541667, -20.39458333], # [ 18.36854167, 16.32791667, -30.60645833], # [ 19.0575 , 0.01166667, 5.45333333], # [ 23.13388889, 6.80888889, 1.36722222], # [ 35.3825 , 5.45333333, -16.31333333]]) # # Test the wrap_positions function. positions = np.array([ [4.0725, -4.0725, -1.3575], [1.3575, -1.3575, -1.3575], [2.715, -2.715, 0.], [4.0725, 1.3575, -1.3575], [0., 0., 0.], [2.715, 2.715, 0.], [6.7875, -1.3575, -1.3575], [5.43, 0., 0.]]) cell = np.array([[5.43, 5.43, 0.0], [5.43, -5.43, 0.0], [0.00, 0.00, 40.0]]) positions += np.array([6.1, -0.1, 10.1]) result_positions = wrap_positions(positions=positions, cell=cell) correct_pos = np.array([ [4.7425, 1.2575, 8.7425], [7.4575, -1.4575, 8.7425], [3.385, 2.615, 10.1], [4.7425, -4.1725, 8.7425], [6.1, -0.1, 10.1], [3.385, -2.815, 10.1], [2.0275, -1.4575, 8.7425], [0.67, -0.1, 10.1]]) assert np.allclose(correct_pos, result_positions) positions = wrap_positions(positions, cell, pbc=[False, True, False]) correct_pos = np.array([ [4.7425, 1.2575, 8.7425], [7.4575, -1.4575, 8.7425], [3.385, 2.615, 10.1], [10.1725, 1.2575, 8.7425], [6.1, -0.1, 10.1], [8.815, 2.615, 10.1], [7.4575, 3.9725, 8.7425], [6.1, 5.33, 10.1]]) assert np.allclose(correct_pos, positions) # Test center away from values 0, 0.5 result_positions = wrap_positions(positions, cell, pbc=[True, True, False], center=0.2) correct_pos = [[4.7425, 1.2575, 8.7425], [2.0275, 3.9725, 8.7425], [3.385, 2.615, 10.1], [-0.6875, 1.2575, 8.7425], [6.1, -0.1, 10.1], [3.385, -2.815, 10.1], [2.0275, -1.4575, 8.7425], [0.67, -0.1, 10.1]] assert np.allclose(correct_pos, result_positions) # Test pretty_translation keyword positions = np.array([ [0, 0, 0], [0, 1, 1], [1, 0, 1], [1, 1, 0.]]) cell = np.diag([2, 2, 2]) result = wrap_positions(positions, cell, pbc=[True, True, True], pretty_translation=True) assert np.max(result) < 1 + 1E-10 assert np.min(result) > -1E-10 result = wrap_positions(positions - 5, cell, pbc=[True, True, True], pretty_translation=True) assert np.max(result) < 1 + 1E-10 result = wrap_positions(positions - 5, cell, pbc=[False, True, True], pretty_translation=True) assert np.max(result[:, 0]) < -3 assert np.max(result[:, 1:]) < 1 + 1E-10 # Get the correct crystal structure from a range of different cells def checkcell(cell, name): cell = Cell.ascell(cell) lat = cell.get_bravais_lattice() assert lat.name == name, (lat.name, name) checkcell(bulk('Al').cell, 'FCC') checkcell(bulk('Fe').cell, 'BCC') checkcell(bulk('Zn').cell, 'HEX') checkcell(fcc111('Au', size=(1, 1, 3), periodic=True).cell, 'HEX') checkcell([[1, 0, 0], [0, 1, 0], [0, 0, 1]], 'CUB') checkcell([[1, 0, 0], [0, 1, 0], [0, 0, 2]], 'TET') checkcell([[1, 0, 0], [0, 2, 0], [0, 0, 3]], 'ORC') checkcell([[1, 0, 0], [0, 2, 0], [0.5, 0, 3]], 'ORCC') checkcell([[1, 0, 0], [0, 2, 0], [0.501, 0, 3]], 'MCL') checkcell([[1, 0, 0], [0.5, 3**0.5 / 2, 0], [0, 0, 3]], 'HEX')
def set_tags(atoms, direction=(0, 0, 1)): tags, positions = get_layers(atoms, direction, 0.3) atoms.set_tags(tags)
def free_energy_vs_layered(T, mod): from cemc.mcmc import AdaptiveBiasReactionPathSampler from cemc.mcmc import ReactionCrdRangeConstraint from cemc.mcmc import DiffractionObserver from cemc.mcmc import MinimalEnergyPath from ase.geometry import get_layers atoms = get_atoms(cubic=True) atoms_cpy = atoms.copy() layers, dist = get_layers(atoms, (0, 1, 0)) for atom in atoms_cpy: if layers[atom.index] % 2 == 0: atom.symbol = "Mg" else: atom.symbol = "Si" symbols = [atom.symbol for atom in atoms_cpy] atoms.get_calculator().set_symbols(symbols) lamb = 4.05 k = 2.0 * np.pi / lamb workdir = "data/diffraction" # Have to perform only insert moves mc = Montecarlo(atoms, T) k_vec = [k, 0, 0] observer = DiffractionObserver(atoms=atoms, active_symbols=["Si"], all_symbols=["Mg", "Si"], k_vector=k_vec, name="reflection") if T < 250: mc.max_attempts = 5 nsteps = 100 * len(atoms) mep = MinimalEnergyPath(mc_obj=mc, observer=observer, value_name="reflection", relax_steps=nsteps, search_steps=nsteps, traj_file="{}/mep_diffract{}K.traj".format( workdir, T), max_reac_crd=1999.0) mep.run() mep.save(fname="{}/mep_mep_diffract_path{}.csv".format(workdir, T)) else: conc_cnst = ReactionCrdRangeConstraint(observer, value_name="reflection") conc_cnst.update_range([0, 0.5]) mc.add_constraint(conc_cnst) reac_path = AdaptiveBiasReactionPathSampler( mc_obj=mc, react_crd=[0.0, 0.5], observer=observer, n_bins=500, data_file="{}/layered_bias{}K.h5".format(workdir, T), mod_factor=mod, delete_db_if_exists=True, mpicomm=None, db_struct="{}/layered_bias_struct{}K.db".format(workdir, T), react_crd_name="reflection", ignore_equil_steps=False, smear=2000) reac_path.run() reac_path.save()