def do_symmetric_surface(test_dir, in_plane_supercell=[1,1], pert_pos=0.0): assert len(supercell) == 2 surf = ase.io.read(test_dir+"/surface.xyz", format="extxyz") surf *= list(in_plane_supercell) + [1] if pert_pos > 0.0: surf.rattle(pert_pos) bulk = rescale_to_relaxed_bulk(surf) bulk_Zs = bulk.get_atomic_numbers() evaluate(bulk) bulk_cell = bulk.get_cell() bulk_E = bulk.get_potential_energy() try: model.reset_config() except AttributeError: pass print("got relaxed bulk cell ", bulk_cell) print("got rescaled surf cell ", surf.get_cell()) # relax surface system tol = 1.0e-2 surf = relax_config(surf, relax_pos=True, relax_cell=False, tol=tol, save_traj=True, config_label="surface", from_base_model=True, save_config=True, try_restart=True) ase.io.write(os.path.join("..",run_root+"-relaxed.xyz"), surf, format='extxyz') # check stoichiometry and number of bulk cell energies to subtract surf_Zs = surf.get_atomic_numbers() Z0 = bulk_Zs[0] n_bulk_cells = float(sum(surf_Zs == Z0))/float(sum(bulk_Zs == Z0)) if len(set(bulk_Zs)) == 1: n_dmu = None else: n_dmu = {} for Z in set(bulk_Zs): # make sure types are JSON compatible n_dmu[int(Z)] = float(n_bulk_cells*sum(bulk_Zs == Z) - sum(surf_Zs == Z)) # calculate surface energy area = np.linalg.norm(np.cross(surf.get_cell()[0,:],surf.get_cell()[1,:])) print("got surface cell potential energy", surf.get_potential_energy()) print("got bulk potential energy",bulk_E*n_bulk_cells) print("got area",area) return { "bulk_struct_test" : surf.info["bulk_struct_test"], "Ef" : (surf.get_potential_energy() - bulk_E*n_bulk_cells)/(2.0*area), "dmu" : n_dmu, 'filename' : run_root+"-relaxed.xyz" }
def do_farthest_inequiv_pairs(test_dir, tol=1.0e-2): print("doing do_farthest_antisite_pairs") bulk_supercell = ase.io.read(os.path.join(test_dir, "bulk_supercell.xyz"), format="extxyz") print("got bulk_supercell ", len(bulk_supercell)) bulk = rescale_to_relaxed_bulk(bulk_supercell) # relax bulk supercell positions in case it's only approximate (as it must be for different models), but stick # to relaxed bulk's lattice constants as set by rescale_to_relaxed_bulk bulk_supercell = relax_config(bulk_supercell, relax_pos=True, relax_cell=False, tol=tol, save_traj=True, config_label="rescaled_bulk", from_base_model=True, save_config=True) ase.io.write(os.path.join("..", run_root + "-rescaled-bulk.xyz"), bulk_supercell, format='extxyz') print("got bulk primitive cell ", bulk.get_cell()) print("got rescaled bulk_supercell cell ", bulk_supercell.get_cell()) if 'arb_supercell' in bulk_supercell.info: print("making bulk supercell from", bulk_supercell.info['arb_supercell'].reshape((3, 3))) bulk_supersupercell = ase.build.make_supercell( bulk_supercell, bulk_supercell.info['arb_supercell'].reshape( (3, 3))) print("got supersupercell with ", len(bulk_supersupercell), "atoms, cell\n", bulk_supersupercell.get_cell()) bulk_supersupercell.info.update(bulk_supercell.info) bulk_supercell = bulk_supersupercell sym_data = spglib.get_symmetry_dataset(bulk_supercell, symprec=0.01) equiv_at = set([ tuple(iZ) for iZ in zip(sym_data["equivalent_atoms"], bulk_supercell.numbers) ]) # print("equiv_at", equiv_at) antisite_list = [] for i1, Z1 in equiv_at: for i2_proto, Z2 in equiv_at: if Z1 <= Z2: continue # print("check i", i1, i2_proto, "Z", Z1, Z2) i2s = np.where(sym_data["equivalent_atoms"] == i2_proto)[0] i2_dists = bulk_supercell.get_distances(i1, i2s, mic=True) farthest_ind = np.argmax(i2_dists) i2 = i2s[farthest_ind] antisite_list.append((i1, i2)) # print("antisite_list", antisite_list) ## evaluate(bulk_supercell) bulk_supercell_pe = bulk_supercell.get_potential_energy() properties = { "bulk_struct_test": bulk_supercell.info["bulk_struct_test"], "bulk_E_per_atom": bulk_supercell_pe / len(bulk_supercell), "defects": {} } for i1, i2 in antisite_list: (label, unrelaxed_filename, Ef0, relaxed_filename, Ef, Z1, Z2) = do_one_antisite_pair(bulk_supercell, bulk_supercell_pe, i1, i2, tol) properties["defects"][label] = { 'Ef0': Ef0, 'Ef': Ef, 'unrelaxed_filename': unrelaxed_filename, 'relaxed_filename': relaxed_filename, 'atom_inds': (int(i1), int(i2)), 'Zs': (int(Z1), int(Z2)) } print("returning properties", properties) return properties
def do_all_interstitials(test_dir, nn_cutoff=0.0, tol=1.0e-2): print("doing do_all_interstitials") bulk_supercell = ase.io.read(os.path.join(test_dir, "bulk_supercell.xyz"), format="extxyz") print("got bulk_supercell ", len(bulk_supercell)) bulk = rescale_to_relaxed_bulk(bulk_supercell) # relax bulk supercell positions in case it's only approximate (as it must be for different models), but stick # to relaxed bulk's lattice constants as set by rescale_to_relaxed_bulk bulk_supercell = relax_config(bulk_supercell, relax_pos=True, relax_cell=False, tol=tol, save_traj=True, config_label="relaxed_bulk", from_base_model=True, save_config=True) ase.io.write(os.path.join("..", run_root + "-rescaled-bulk.xyz"), bulk_supercell, format='extxyz') print("got bulk primitive cell ", bulk.get_cell()) print("got rescaled bulk_supercell cell ", bulk_supercell.get_cell()) try: # Cartesian 3-vector interstitial_pos_l = [ np.array([float(x) for x in bulk_supercell.info["interstitials"]]) ] if len(interstitial_pos_l) != 3: raise ValueError("not a 3-vector") except: interstitial_pos_type = bulk_supercell.info["interstitials"].split()[0] if interstitial_pos_type == "mean": neighbor_indices = [ int(i) for i in bulk_supercell.info["interstitials"].split()[1:] ] if len(neighbor_indices) < 2: raise ValueError( "interstitial position type mean, but {} < 2 indices". format(len(neighbor_indices))) interstitial_pos_l = [ np.sum(bulk_supercell.get_positions()[neighbor_indices], axis=0) / float(len(neighbor_indices)) ] elif interstitial_pos_type == "inequivalent": if 'arb_supercell' in bulk_supercell.info: print("making bulk supercell from", bulk_supercell.info['arb_supercell'].reshape((3, 3))) bulk_supersupercell = ase.build.make_supercell( bulk_supercell, bulk_supercell.info['arb_supercell'].reshape((3, 3))) print("got supersupercell with ", len(bulk_supersupercell), "atoms, cell\n", bulk_supersupercell.get_cell()) voids = find_voids(bulk_supercell) interstitial_pos_l = [(v[1], v[2], v[3]) for v in voids] bulk_supersupercell.info.update(bulk_supercell.info) bulk_supercell = bulk_supersupercell else: raise ValueError("Unknown interstitial position type in '" + bulk_supercell.info["interstitials"] + "'") evaluate(bulk_supercell) bulk_supercell_pe = bulk_supercell.get_potential_energy() properties = { "bulk_struct_test": bulk_supercell.info["bulk_struct_test"], "bulk_E_per_atom": bulk_supercell_pe / len(bulk_supercell), "defects": {} } Z_list = ([bulk_supercell.info["Zs"]] if isinstance( bulk_supercell.info["Zs"], (int, np.integer)) else bulk_supercell.info["Zs"]) for interstitial_Z in Z_list: try: relax_radial = bulk_supercell.info['relax_radial_{}'.format( interstitial_Z)] except: relax_radial = 0.0 try: relax_symm_break = bulk_supercell.info[ 'relax_symm_break_{}'.format(interstitial_Z)] except: relax_symm_break = 0.0 for interst_i, interst_pos in enumerate(interstitial_pos_l): label = f'Z_{interstitial_Z}_pos_{interst_i}' (unrelaxed_filename, Ef0, relaxed_filename, Ef, interstitial_i) = do_one_interstitial( bulk_supercell, bulk_supercell_pe, interstitial_Z, interst_pos, label, relax_radial, relax_symm_break, nn_cutoff, tol) properties["defects"][label] = { 'Ef0': Ef0, 'Ef': Ef, 'unrelaxed_filename': unrelaxed_filename, 'relaxed_filename': relaxed_filename, 'atom_ind': int(interstitial_i), 'Z': int(interstitial_Z), 'pos_index': interst_i } if len(set(bulk_supercell.get_atomic_numbers())) != 1: properties["defects"][label]['dmu'] = [-1, int(interstitial_Z)] return properties
def do_interstitial(test_dir, nn_cutoff=0.0, tol=1.0e-2): print("doing do_interstitial") bulk_supercell = ase.io.read(os.path.join(test_dir, "bulk_supercell.xyz"), format="extxyz") print("got bulk_supercell ", len(bulk_supercell)) bulk = rescale_to_relaxed_bulk(bulk_supercell) # relax bulk supercell positions in case it's only approximate (as it must be for different models), but stick # to relaxed bulk's lattice constants as set by rescale_to_relaxed_bulk bulk_supercell = relax_config(bulk_supercell, relax_pos=True, relax_cell=False, tol=tol, traj_file=None, config_label="relaxed_bulk", from_base_model=True, save_config=True) evaluate(bulk_supercell) bulk_supercell_pe = bulk_supercell.get_potential_energy() ase.io.write(os.path.join("..", run_root + "-rescaled-bulk.xyz"), bulk_supercell, format='extxyz') print("got bulk primitive cell ", bulk.get_cell()) print("got rescaled bulk_supercell cell ", bulk_supercell.get_cell()) properties = { "bulk_struct_test": bulk_supercell.info["bulk_struct_test"], "bulk_E_per_atom": bulk_supercell_pe / len(bulk_supercell), "defects": {} } try: # Cartesian 3-vector interstitial_pos = np.array( [float(x) for x in bulk_supercell.info["interstitial_position"]]) if len(interstitial_pos) != 3: raise ValueError("not a 3-vector") except: interstitial_pos_type = bulk_supercell.info[ "interstitial_position"].split()[0] if interstitial_pos_type == "mean": neighbor_indices = [ int(i) for i in bulk_supercell.info["interstitial_position"].split()[1:] ] if len(neighbor_indices) < 2: raise ValueError( "interstitial position type mean, but {} < 2 indices". format(len(neighbor_indices))) interstitial_pos = np.sum( bulk_supercell.get_positions()[neighbor_indices], axis=0) / float(len(neighbor_indices)) else: raise ValueError("Unknown interstitial position type in '" + bulk_supercell.info["interstitial_position"] + "'") if isinstance(bulk_supercell.info["Zs"], list): Z_list = bulk_supercell.info["Zs"] else: Z_list = [bulk_supercell.info["Zs"]] for interstitial_Z in Z_list: try: relax_radial = bulk_supercell.info['relax_radial_{}'.format( interstitial_Z)] except: relax_radial = 0.0 try: relax_symm_break = bulk_supercell.info[ 'relax_symm_break_{}'.format(interstitial_Z)] except: relax_symm_break = 0.0 (label, unrelaxed_filename, Ef0, relaxed_filename, Ef, interstitial_i) = do_one_interstitial( bulk_supercell, bulk_supercell_pe, interstitial_Z, interstitial_pos, relax_radial, relax_symm_break, nn_cutoff, tol) properties["defects"][label] = { 'Ef0': Ef0, 'Ef': Ef, 'unrelaxed_filename': unrelaxed_filename, 'relaxed_filename': relaxed_filename, 'atom_ind': int(interstitial_i), 'Z': int(interstitial_Z) } if len(set(bulk_supercell.get_atomic_numbers())) != 1: properties["defects"][label]['dmu'] = [-1, interstitial_Z] return properties
def do_all_vacancies(test_dir, nn_cutoff=0.0, tol=1.0e-2): print("doing do_all_vacancies") bulk_supercell = ase.io.read(os.path.join(test_dir,"bulk_supercell.xyz"), format="extxyz") print("got bulk_supercell ", len(bulk_supercell)) bulk = rescale_to_relaxed_bulk(bulk_supercell) # relax bulk supercell positions in case it's only approximate (as it must be for different models), but stick # to relaxed bulk's lattice constants as set by rescale_to_relaxed_bulk bulk_supercell = relax_config(bulk_supercell, relax_pos=True, relax_cell=False, tol=tol, save_traj=True, config_label="rescaled_bulk", from_base_model=True, save_config=True) ase.io.write(os.path.join("..",run_root+"-rescaled-bulk.xyz"), bulk_supercell, format='extxyz') print("got bulk primitive cell ", bulk.get_cell()) print("got rescaled bulk_supercell cell ", bulk_supercell.get_cell()) if bulk_supercell.info['vacancies'] == "inequivalent": sym_data = spglib.get_symmetry_dataset(bulk_supercell, symprec=0.01) prim_vacancy_list = np.unique(sym_data["equivalent_atoms"]) print("orig cell vacancy_list", prim_vacancy_list) if 'arb_supercell' in bulk_supercell.info: print("making bulk supercell from", bulk_supercell.info['arb_supercell'].reshape((3,3)) ) bulk_supersupercell = ase.build.make_supercell(bulk_supercell,bulk_supercell.info['arb_supercell'].reshape((3,3)) ) print("got supersupercell with ",len(bulk_supersupercell),"atoms, cell\n",bulk_supersupercell.get_cell()) vacancy_list = [] for i in prim_vacancy_list: p = bulk_supercell.get_positions()[i] dv = bulk_supersupercell.get_positions() - p dv_scaled = np.dot(dv, bulk_supersupercell.get_reciprocal_cell().T) dv -= np.dot(np.round(dv_scaled), bulk_supersupercell.get_cell()) i_closest = np.argmin(np.linalg.norm(dv, axis=1)) print("found closest in new cell", i_closest, "distance in orig cell lattice coords", np.dot((bulk_supersupercell.get_positions()[i_closest]-p), \ bulk_supercell.get_reciprocal_cell().T)) vacancy_list.append(i_closest) bulk_supersupercell.info.update(bulk_supercell.info) bulk_supercell = bulk_supersupercell else: vacancy_list = prim_vacancy_list print("final vacancy_list", vacancy_list) else: try: vacancy_list = [ int(i) for i in bulk_supercell.info['vacancies'] ] except: vacancy_list = [ int(bulk_supercell.info['vacancies']) ] evaluate(bulk_supercell) bulk_supercell_pe = bulk_supercell.get_potential_energy() properties = { "bulk_struct_test" : bulk_supercell.info["bulk_struct_test"], "bulk_E_per_atom" : bulk_supercell_pe / len(bulk_supercell), "defects" : {} } for vac_i in vacancy_list: # maybe set up a system to read these from xyz file? try: relax_radial = bulk_supercell.info['relax_radial_{}'.format(vac_i)] except: relax_radial = 0.0 try: relax_symm_break = bulk_supercell.info['relax_symm_break_{}'.format(vac_i)] except: relax_symm_break = 0.0 (label, unrelaxed_filename, Ef0, relaxed_filename, Ef, vac_Z, vac_pos) = do_one_vacancy(bulk_supercell, bulk_supercell_pe, vac_i, relax_radial, relax_symm_break, nn_cutoff, tol) properties["defects"][label] = { 'Ef0' : Ef0, 'Ef' : Ef, 'unrelaxed_filename' : unrelaxed_filename,'relaxed_filename' : relaxed_filename, 'atom_ind' : int(vac_i), 'Z' : int(vac_Z), 'vac_pos' : vac_pos.tolist()} if len(set(bulk_supercell.get_atomic_numbers())) > 1: properties["defects"][label]["dmu"] = [1, vac_Z] print("returning properties", properties) return properties