def test3(self): l = Lattice.cubic(100) s1 = PeriodicSite('Li', [99.5, 0, 1], l, coords_are_cartesian=True) s2 = PeriodicSite('Sn', [99.5, 1, 0], l, coords_are_cartesian=True) s3 = PeriodicSite('Li', [0.5, 0, 0], l, coords_are_cartesian=True) s = Structure.from_sites([s1, s2, s3]) coords = np.array(s.cart_coords)[:, None, :] cd = CoordData(coords, s) gtc = GaussianTripletColumns(rrange=[1, 10], c=0.5) f = gtc.make(cd) self.assertAlmostEqual(f[0, 0], 0) self.assertAlmostEqual(f[1, 8], -0.41849511) self.assertAlmostEqual(f[2, 8], 0.83699023) self.assertAlmostEqual(f[3, 32], -0.83699023)
def _make_sites(self): """ """ self.sites = [] self.coords = [] for raw in self.atoms: proc = [ s.strip() for s in (raw[:6], raw[6:11], raw[12:16], raw[16], raw[17:20], raw[21], raw[22:26], raw[26], raw[30:38], raw[38:46], raw[46:54], raw[54:60], raw[60:66], raw[72:76], raw[76:78], raw[78:]) ] coord = np.dot(self.cart_to_lat, np.array( proc[8:11], dtype=float)) + self.translate props = dict(serial=proc[1], name=proc[2], altloc=proc[3], residue=proc[4], chain=proc[5], sequence=proc[6], segment=proc[13]) self.coords.append(coord) self.sites.append( PeriodicSite(Composition({proc[-2]: float(proc[11])}), coord, self.lattice, coords_are_cartesian=True, properties=props)) return
def make_supercell(self, scaling_matrix): """ Create a supercell. Very similar to pymatgen's Structure.make_supercell However, we need to make sure that all fractional coordinates that equal to 1 will become 0 and the lattice are redefined so that x_c = [0, 0, c] Args: scaling_matrix (3x3 matrix): The scaling matrix to make supercell. """ s = self * scaling_matrix for i, site in enumerate(s): f_coords = np.mod(site.frac_coords, 1) # The following for loop is probably not necessary. But I will leave # it here for now. for j, v in enumerate(f_coords): if abs(v - 1) < 1e-6: f_coords[j] = 0 s[i] = PeriodicSite(site.specie, f_coords, site.lattice, properties=site.properties) self._sites = s.sites self._lattice = s.lattice new_lat = Lattice.from_lengths_and_angles( *s.lattice.lengths_and_angles) self.modify_lattice(new_lat)
def periodic_muon_site(self): """The periodic single muon site""" structure = self.pristine_structure_supercell.copy() mupos = self.relax_muon_site.frac_coords return PeriodicSite("H", mupos, structure.lattice, coords_are_cartesian=False)
def get_sitej(self, site_index, image_index): """ Assuming there is some value in the connectivity array at indices (1, 3, 12). sitei can be obtained directly from the input structure (structure[1]). sitej can be obtained by passing 3, 12 to this function Args: site_index (int): index of the site (3 in the example) image_index (int): index of the image (12 in the example) """ atoms_n_occu = self.s[site_index].species lattice = self.s.lattice coords = self.s[site_index].frac_coords + self.offsets[image_index] return PeriodicSite(atoms_n_occu, coords, lattice)
def __init__(self, supercell_matrix, cluster_expansion): """ Args: supercell matrix: array describing the supercell, e.g. [[1,0,0],[0,1,0],[0,0,1]] cluster_expansion: ClusterExpansion object """ self.basis = cluster_expansion.basis self.supercell_matrix = np.array(supercell_matrix) self.prim_to_supercell = np.linalg.inv(self.supercell_matrix) self.cluster_expansion = cluster_expansion self.supercell = cluster_expansion.structure.copy() self.supercell.make_supercell(self.supercell_matrix) self.size = int(round(np.abs(np.linalg.det(self.supercell_matrix)))) self.bits = get_bits(self.supercell) self.nbits = np.array([len(b)-1 for b in self.bits]) self.fcoords = np.array(self.supercell.frac_coords) self._generate_mappings() if self.cluster_expansion.use_ewald: #lazily generate the difficult ewald parts self.ewald_inds = [] ewald_sites = [] for bits, s in zip(self.bits, self.supercell): inds = np.zeros(max(self.nbits) + 1) - 1 for i, b in enumerate(bits): if b == 'Vacancy': #inds.append(-1) continue inds[i] = len(ewald_sites) ewald_sites.append(PeriodicSite(b, s.frac_coords, s.lattice)) self.ewald_inds.append(inds) self.ewald_inds = np.array(self.ewald_inds, dtype=np.int) self._ewald_structure = Structure.from_sites(ewald_sites) self._ewald_matrix = None self._partial_ems = None self._all_ewalds = None self._range = np.arange(len(self.nbits)) else: self._all_ewalds = np.zeros((0, 0, 0), dtype=np.float) self.ewald_inds = np.zeros((0, 0), dtype=np.int)
def periodic_muon_site(self): """get muon position as a PeriodicSite Parameters ---------- positions : nd.array numpy.array of fractional position of host atoms (including muon) Returns: mu_positions : PeriodicSite Periodic muon site positions """ structure = self.structure_sc.copy() positions = self.positions mu_positions = list() for mu_i, mu in enumerate(self.positions): mu_positions.append( PeriodicSite("H", [mu_p for mu_p in positions[mu_i][-1]], structure.lattice, coords_are_cartesian=False)) return mu_positions
def test_symmetrized_structure(self): t = OrderDisorderedStructureTransformation(symmetrized_structures=True) c = [] sp = [] c.append([0.5, 0.5, 0.5]) sp.append('Si4+') c.append([0.45, 0.45, 0.45]) sp.append({"Si4+": 0.5}) c.append([0.56, 0.56, 0.56]) sp.append({"Si4+": 0.5}) c.append([0.25, 0.75, 0.75]) sp.append({"Si4+": 0.5}) c.append([0.75, 0.25, 0.25]) sp.append({"Si4+": 0.5}) l = Lattice.cubic(5) s = Structure(l, sp, c) test_site = PeriodicSite("Si4+", c[2], l) s = SymmetrizedStructure(s, 'not_real', [0, 1, 1, 2, 2]) output = t.apply_transformation(s) self.assertTrue(test_site in output.sites)
def periodic_muon_equivalent_site(self): """get muon position as a PeriodicSite Parameters ---------- positions : numpy.ndarray 3D muon positions (including muon) Returns: PeriodicSite of muon positions """ #frac_coords = self.ase_muon_find_equiv() positions = self.muon_equivalent_sites structure = self.pristine_structure_supercell.copy() periodic_sites = list() for mu, i in enumerate(positions): periodic_sites.append( PeriodicSite("H", [mu_i for mu_i in positions[mu]], structure.lattice, coords_are_cartesian=False)) return periodic_sites
def delete_bt_layer(self, bt, tol=0.25, axis=2): """ Delete bottom or top layer of the structure. Args: bt (str): Specify whether it's a top or bottom layer delete. "b" means bottom layer and "t" means top layer. tol (float), Angstrom: Tolerance factor to determine whether two atoms are at the same plane. Default to 0.25 axis (int): The direction of top and bottom layers. 0: x, 1: y, 2: z """ if bt == "t": l1, l2 = (-1, -2) else: l1, l2 = (0, 1) l = self.lattice.abc[axis] layers = self.sort_sites_in_layers(tol=tol, axis=axis) l_dist = abs(layers[l1][0].coords[axis] - layers[l2][0].coords[axis]) l_vector = [1, 1] l_vector.insert(axis, (l - l_dist) / l) new_lat = Lattice(self.lattice.matrix * np.array(l_vector)[:, None]) layers.pop(l1) sites = reduce(lambda x, y: np.concatenate((x, y), axis=0), layers) new_sites = [] l_dist = 0 if bt == "t" else l_dist l_vector = [0, 0] l_vector.insert(axis, l_dist) for i in sites: new_sites.append( PeriodicSite(i.specie, i.coords - l_vector, new_lat, coords_are_cartesian=True)) self._sites = new_sites self._lattice = new_lat
def test2(self): l = Lattice.cubic(100) s1 = PeriodicSite('Li', [99.5, 0, 1.1], l, coords_are_cartesian=True) s2 = PeriodicSite('Sn', [99.5, 1, 0], l, coords_are_cartesian=True) s3 = PeriodicSite('Li', [0.5, 0, 0], l, coords_are_cartesian=True) s4 = PeriodicSite('Li', [59.5, 0, 1.1], l, coords_are_cartesian=True) s5 = PeriodicSite('Sn', [59.5, 1, 0], l, coords_are_cartesian=True) s6 = PeriodicSite('Li', [60.5, 0, 0], l, coords_are_cartesian=True) s = Structure.from_sites([s1, s2, s3, s4, s5, s6]) coords = np.array(s.cart_coords)[:, None, :] cd = CoordData(coords, s) gtc = GaussianTripletColumns(rrange=np.arange(1, 1.1, 1), c=0.5) f = gtc.make(cd) self.assertAlmostEqual(f[9, 0], 0) self.assertAlmostEqual(f[10, 1], -0.36030969) self.assertAlmostEqual(f[11, 1], 0.79268132) self.assertAlmostEqual(f[12, 4], -0.64481206) self.assertAlmostEqual(f[13, 4], 1.36543144) self.assertAlmostEqual(np.sum(f), 0)
def structure_from_occu(self, occu): sites = [] for b, o, s in zip(self.bits, occu, self.supercell): if b[o] != 'Vacancy': sites.append(PeriodicSite(b[o], s.frac_coords, self.supercell.lattice)) return Structure.from_sites(sites)
def generate_supercells( bulk_structure, grid_size = 4, supercell = (2, 2, 2), method = 'grid', positions=None ): """ This is a wrapper to various methods to generate different interstitial impurities. Parameters ---------- bulk_structure : Structure object A unitcell (magnetic or not) structure in form of Pymatgen Structure object grid_size : int A grid size in all lattice directions to generate uniform grid for interstitial impurities implemented by Pietro Bonfa. Default=4. supercell : tuple, numpy.ndarray Supercell size in a, b and c direction. Default = (2, 2, 2) method : str The method to generate interstial impurities. Default='grid' positions : None A numpy.ndarray of muon positions if `method="manual"`. Returns ------- structure_list : list A list of supercell structure containing interstitial impurities """ #magnetic_structure = CollinearMagneticStructureAnalyzer(bulk_structure, make_primitive=False) #if magnetic_structure.is_magnetic: # #spin_bulk_structure = magnetic_structure.get_structure_with_spin() # spin_bulk_structure = # spin_bulk_structure.make_supercell(supercell) site_properties_preservation = bulk_structure.copy() site_properties_preservation.make_supercell(supercell) structure_list = [] ## Manually set ? if method == 'manual': if positions is None: raise ValueError('Position must be specified in manual mode!') r = [] for position in positions: defect_site = PeriodicSite("H", position, bulk_structure.lattice, coords_are_cartesian=False) r.append(Interstitial(bulk_structure, defect_site, charge=0.)) elif method == 'tess': r = [] for position in tess_interstitials(bulk_structure): defect_site = PeriodicSite("H", position, bulk_structure.lattice, coords_are_cartesian=False) r.append(Interstitial(bulk_structure, defect_site, charge=0.)) elif method == 'infit': r = list(InterstitialGenerator( bulk_structure, 'H')) elif method == 'grid': r = [] for position in generate_uniform_grid(bulk_structure, grid_size): defect_site = PeriodicSite("H", position, bulk_structure.lattice, coords_are_cartesian=False) r.append(Interstitial(bulk_structure, defect_site, charge=0.)) else: r = list(VoronoiInterstitialGenerator( bulk_structure, 'H')) for i,v in enumerate(r): struct=v.generate_defect_structure(supercell=supercell) ## Preserve magnetic structure #if (magnetic_structure.is_magnetic): # spins = np.zeros(struct.num_sites) # # TODO: check non collinear... # for site1 in spin_bulk_structure: # for i, site2 in enumerate(struct): # if np.allclose(site1.coords, site2.coords, atol = site2.position_atol): # spins[i] = site1.specie.spin # break # # struct.add_spin_by_site(spins) for site1 in site_properties_preservation: for i, site2 in enumerate(struct): if np.allclose(site1.coords, site2.coords, atol = site2.position_atol): #spins[i] = site1.specie.spin struct[i].properties = site1.properties break # Remove symmetry if method != 'manual': # Here we assume, manual muon site are perturb. # Performs a random perturbation of the sites in the structure # to break symmetries. A distance of 1e-4 Angs. surely does nothing # However we can perturb the muon site with a random vector with # 0.1 Angs. distance struct.perturb(distance=0.0001) struct.translate_sites(-1, 0.1 * np.random.random(3), frac_coords=False) if struct.is_valid(): structure_list.append(struct) return structure_list
def get_structure_graph_scene( self, origin=(0, 0, 0), draw_image_atoms=True, bonded_sites_outside_unit_cell=True, hide_incomplete_bonds=False, explicitly_calculate_polyhedra_hull=False, ) -> Scene: primitives = defaultdict(list) sites_to_draw = self._get_sites_to_draw( draw_image_atoms=draw_image_atoms, bonded_sites_outside_unit_cell=bonded_sites_outside_unit_cell, ) for (idx, jimage) in sites_to_draw: site = self.structure[idx] if jimage != (0, 0, 0): connected_sites = self.get_connected_sites(idx, jimage=jimage) site = PeriodicSite( site.species, np.add(site.frac_coords, jimage), site.lattice, properties=site.properties, ) else: connected_sites = self.get_connected_sites(idx) true_number_of_connected_sites = len(connected_sites) connected_sites_being_drawn = [ cs for cs in connected_sites if (cs.index, cs.jimage) in sites_to_draw ] number_of_connected_sites_drawn = len(connected_sites_being_drawn) all_connected_sites_present = ( true_number_of_connected_sites == number_of_connected_sites_drawn) if hide_incomplete_bonds: # only draw bonds if the destination site is also being drawn connected_sites = connected_sites_being_drawn site_scene = site.get_scene( connected_sites=connected_sites, all_connected_sites_present=all_connected_sites_present, origin=origin, explicitly_calculate_polyhedra_hull= explicitly_calculate_polyhedra_hull, ) for scene in site_scene.contents: primitives[scene.name] += scene.contents # we are here ... # select polyhedra # split by atom type at center # see if any intersect, if yes split further # order sets, with each choice, go to add second set etc if don't intersect # they intersect if centre atom forms vertex of another atom (caveat: centre atom may not actually be inside polyhedra! not checking for this, add todo) # def _set_intersects() ->bool: # def _split_set() ->List: (by type, then..?) # def _order_sets()... pick 1, ask can add 2? etc primitives["unit_cell"].append( self.structure.lattice.get_scene(origin=origin)) return Scene( name=self.structure.composition.reduced_formula, contents=[Scene(name=k, contents=v) for k, v in primitives.items()], )
def get_structure_graph_scene( self, origin=None, draw_image_atoms=True, bonded_sites_outside_unit_cell=True, hide_incomplete_edges=False, incomplete_edge_length_scale=0.3, color_edges_by_edge_weight=True, edge_weight_color_scale="coolwarm", explicitly_calculate_polyhedra_hull=False, legend: Optional[Legend] = None, group_by_symmetry: bool = True, ) -> Scene: origin = origin or list( -self.structure.lattice.get_cartesian_coords([0.5, 0.5, 0.5])) legend = legend or Legend(self.structure) primitives = defaultdict(list) sites_to_draw = self._get_sites_to_draw( draw_image_atoms=draw_image_atoms, bonded_sites_outside_unit_cell=bonded_sites_outside_unit_cell, ) color_edges = False if color_edges_by_edge_weight: weights = [e[2].get("weight") for e in self.graph.edges(data=True)] weights = np.array([w for w in weights if w]) if any(weights): cmap = get_cmap(edge_weight_color_scale) # try to keep color scheme symmetric around 0 weight_max = max([abs(min(weights)), max(weights)]) weight_min = -weight_max def get_weight_color(weight): if not weight: weight = 0 x = (weight - weight_min) / (weight_max - weight_min) return "#{:02x}{:02x}{:02x}".format( *[int(c * 255) for c in cmap(x)[0:3]]) color_edges = True idx_to_wyckoff = {} if group_by_symmetry: sga = SpacegroupAnalyzer(self.structure) struct_sym = sga.get_symmetrized_structure() for equiv_idxs, wyckoff in zip(struct_sym.equivalent_indices, struct_sym.wyckoff_symbols): for idx in equiv_idxs: idx_to_wyckoff[idx] = wyckoff for (idx, jimage) in sites_to_draw: site = self.structure[idx] if jimage != (0, 0, 0): connected_sites = self.get_connected_sites(idx, jimage=jimage) site = PeriodicSite( site.species, np.add(site.frac_coords, jimage), site.lattice, properties=site.properties, ) else: connected_sites = self.get_connected_sites(idx) connected_sites = [ cs for cs in connected_sites if (cs.index, cs.jimage) in sites_to_draw ] connected_sites_not_drawn = [ cs for cs in connected_sites if (cs.index, cs.jimage) not in sites_to_draw ] if color_edges: connected_sites_colors = [ get_weight_color(cs.weight) for cs in connected_sites ] connected_sites_not_drawn_colors = [ get_weight_color(cs.weight) for cs in connected_sites_not_drawn ] else: connected_sites_colors = None connected_sites_not_drawn_colors = None site_scene = site.get_scene( connected_sites=connected_sites, connected_sites_not_drawn=connected_sites_not_drawn, hide_incomplete_edges=hide_incomplete_edges, incomplete_edge_length_scale=incomplete_edge_length_scale, connected_sites_colors=connected_sites_colors, connected_sites_not_drawn_colors=connected_sites_not_drawn_colors, explicitly_calculate_polyhedra_hull= explicitly_calculate_polyhedra_hull, legend=legend, ) for scene in site_scene.contents: if group_by_symmetry and scene.name == "atoms" and idx in idx_to_wyckoff: # will rename to e.g. atoms_N_4e scene.name = f"atoms_{site_scene.name}_{idx_to_wyckoff[idx]}" # this is a proof-of-concept to demonstrate hover labels, could create label # automatically from site properties instead scene.contents[ 0].tooltip = f"{site_scene.name} ({idx_to_wyckoff[idx]})" primitives[scene.name] += scene.contents primitives["unit_cell"].append(self.structure.lattice.get_scene()) return Scene( name="StructureGraph", origin=origin, contents=[ Scene(name=k, contents=v, origin=origin) for k, v in primitives.items() ], )
def get_structure_graph_scene( self, origin=None, draw_image_atoms=True, bonded_sites_outside_unit_cell=True, hide_incomplete_edges=False, incomplete_edge_length_scale=0.3, color_edges_by_edge_weight=True, edge_weight_color_scale="coolwarm", explicitly_calculate_polyhedra_hull=False, legend: Optional[Legend] = None, ) -> Scene: origin = origin or list( -self.structure.lattice.get_cartesian_coords([0.5, 0.5, 0.5])) legend = legend or Legend(self.structure) primitives = defaultdict(list) sites_to_draw = self._get_sites_to_draw( draw_image_atoms=draw_image_atoms, bonded_sites_outside_unit_cell=bonded_sites_outside_unit_cell, ) color_edges = False if color_edges_by_edge_weight: weights = [e[2].get("weight") for e in self.graph.edges(data=True)] weights = np.array([w for w in weights if w]) if any(weights): cmap = get_cmap(edge_weight_color_scale) # try to keep color scheme symmetric around 0 weight_max = max([abs(min(weights)), max(weights)]) weight_min = -weight_max def get_weight_color(weight): if not weight: weight = 0 x = (weight - weight_min) / (weight_max - weight_min) return "#{:02x}{:02x}{:02x}".format( *[int(c * 255) for c in cmap(x)[0:3]]) color_edges = True for (idx, jimage) in sites_to_draw: site = self.structure[idx] if jimage != (0, 0, 0): connected_sites = self.get_connected_sites(idx, jimage=jimage) site = PeriodicSite( site.species, np.add(site.frac_coords, jimage), site.lattice, properties=site.properties, ) else: connected_sites = self.get_connected_sites(idx) connected_sites = [ cs for cs in connected_sites if (cs.index, cs.jimage) in sites_to_draw ] connected_sites_not_drawn = [ cs for cs in connected_sites if (cs.index, cs.jimage) not in sites_to_draw ] if color_edges: connected_sites_colors = [ get_weight_color(cs.weight) for cs in connected_sites ] connected_sites_not_drawn_colors = [ get_weight_color(cs.weight) for cs in connected_sites_not_drawn ] else: connected_sites_colors = None connected_sites_not_drawn_colors = None site_scene = site.get_scene( connected_sites=connected_sites, connected_sites_not_drawn=connected_sites_not_drawn, hide_incomplete_edges=hide_incomplete_edges, incomplete_edge_length_scale=incomplete_edge_length_scale, connected_sites_colors=connected_sites_colors, connected_sites_not_drawn_colors=connected_sites_not_drawn_colors, explicitly_calculate_polyhedra_hull= explicitly_calculate_polyhedra_hull, legend=legend, ) for scene in site_scene.contents: primitives[scene.name] += scene.contents primitives["unit_cell"].append(self.structure.lattice.get_scene()) return Scene( name=self.structure.composition.reduced_formula, origin=origin, contents=[ Scene(name=k, contents=v, origin=origin) for k, v in primitives.items() ], )
def trialstep(self, spch_config, energy_now): e0 = energy_now structure = spch_config.structure.copy() structure_Osite = spch_config.structure.copy() structure_Osite.remove_species(["Pt", "Zr"]) base_Osite = spch_config.Osite_struct.copy() # base_Osite.append("He", [0,0,0]) # base_Osite.append("He", [0.4,0.5,0]) # base_Osite.append("He", [0.3, 0.2, 0.4]) # Osite.append("He", [0,0,0]) # Osite.append("He", [0.4,0.5,0]) # Osite.append("He", [0.3, 0.2, 0.4]) # Figure out where the vacancies are by comparing with base_structure # filled_sites = self.matcher_site.get_mapping(base_Osite, # Osite) # if not filled_sites.any(): # print("something went wrong at matching") # sys.exit(1) # vac_struct = base_Osite # vac_struct.remove_sites(filled_sites) # Make sure the correct number of vacancies have been detected # assert len(vac_struct)==int(spch_config.N_Ovac*len(spch_config.Osite_struct)) # choose one vacancy and one O atom randomly and flip # Osites = structure.indices_from_symbol("O") # O_flip = rand.choice(Osites) # del structure[O_flip] # choose one O atom and move it Osites = structure.indices_from_symbol("O") O_flip = rand.choice(Osites) del structure[O_flip] ntrial = 0 while True: ntrial += 1 Omove_candidate = PeriodicSite( "O", [ rand.random(), rand.random(), rand.uniform(spch_config.ZrO2boxZ[0], spch_config.ZrO2boxZ[1]), ], structure[0].lattice, ) okflag = True for site in structure: if site.distance_and_image(Omove_candidate)[0] < 1.5: okflag = False break if okflag: break print("It took ", ntrial, " trials to find space for O") print(Omove_candidate.frac_coords) structure.append( "O", Omove_candidate.frac_coords, properties={"velocities": [0, 0, 0]} ) # print(vac_flip.frac_coords) # structure.append("O", vac_candidate.frac_coords,properties={"velocities":[0]}) # structure.to(fmt="poscar", filename="POSCAR.1") # Backup structure of previous step structure0 = spch_config.structure spch_config.structure = structure # do vasp calculation on structure e1 = self.energy(spch_config) # return old spch structure spch_config.structure = structure0 # Simply pass new structure in dconfig to be used by newconfig(): dconfig = structure dE = e1 - e0 return dconfig, dE
def get_structure_graph_scene( self, origin=(0, 0, 0), draw_image_atoms=True, bonded_sites_outside_unit_cell=True, hide_incomplete_edges=False, incomplete_edge_length_scale=0.3, color_edges_by_edge_weight=True, edge_weight_color_scale="coolwarm", explicitly_calculate_polyhedra_hull=False, ) -> Scene: primitives = defaultdict(list) sites_to_draw = self._get_sites_to_draw( draw_image_atoms=draw_image_atoms, bonded_sites_outside_unit_cell=bonded_sites_outside_unit_cell, ) color_edges = False if color_edges_by_edge_weight: weights = [e[2].get("weight") for e in self.graph.edges(data=True)] weights = np.array([w for w in weights if w]) if any(weights): cmap = get_cmap(edge_weight_color_scale) # try to keep color scheme symmetric around 0 weight_max = max([abs(min(weights)), max(weights)]) weight_min = -weight_max def get_weight_color(weight): if not weight: weight = 0 x = (weight - weight_min) / (weight_max - weight_min) return "#{:02x}{:02x}{:02x}".format( *[int(c * 255) for c in cmap(x)[0:3]]) color_edges = True for (idx, jimage) in sites_to_draw: site = self.structure[idx] if jimage != (0, 0, 0): connected_sites = self.get_connected_sites(idx, jimage=jimage) site = PeriodicSite( site.species, np.add(site.frac_coords, jimage), site.lattice, properties=site.properties, ) else: connected_sites = self.get_connected_sites(idx) connected_sites = [ cs for cs in connected_sites if (cs.index, cs.jimage) in sites_to_draw ] connected_sites_not_drawn = [ cs for cs in connected_sites if (cs.index, cs.jimage) not in sites_to_draw ] if color_edges: connected_sites_colors = [ get_weight_color(cs.weight) for cs in connected_sites ] connected_sites_not_drawn_colors = [ get_weight_color(cs.weight) for cs in connected_sites_not_drawn ] else: connected_sites_colors = None connected_sites_not_drawn_colors = None site_scene = site.get_scene( connected_sites=connected_sites, connected_sites_not_drawn=connected_sites_not_drawn, hide_incomplete_edges=hide_incomplete_edges, incomplete_edge_length_scale=incomplete_edge_length_scale, connected_sites_colors=connected_sites_colors, connected_sites_not_drawn_colors=connected_sites_not_drawn_colors, origin=origin, explicitly_calculate_polyhedra_hull= explicitly_calculate_polyhedra_hull, ) for scene in site_scene.contents: primitives[scene.name] += scene.contents # we are here ... # select polyhedra # split by atom type at center # see if any intersect, if yes split further # order sets, with each choice, go to add second set etc if don't intersect # they intersect if centre atom forms vertex of another atom (caveat: centre atom may not actually be inside polyhedra! not checking for this, add todo) # def _set_intersects() ->bool: # def _split_set() ->List: (by type, then..?) # def _order_sets()... pick 1, ask can add 2? etc primitives["unit_cell"].append( self.structure.lattice.get_scene(origin=origin)) return Scene( name=self.structure.composition.reduced_formula, contents=[Scene(name=k, contents=v) for k, v in primitives.items()], )
structure = Poscar.from_file('POSCAR_unit').structure default_inputs = DefaultInputs(structure) incar_settings = default_inputs.get_incar_default(xc='PBE') incar_settings['LVTOT'] = '.TRUE.' kpoints = Kpoints.gamma_automatic(kpts=(2, 2, 2)) supercell_size = 3 structure.make_supercell(supercell_size) structure_pure = structure.copy() for el in charge_states_dict: structure = structure_pure.copy() for s in structure.sites: if s.species_string == 'Na': defect_site = PeriodicSite(el, s.frac_coords, s.lattice) break substitution = Substitution(structure, defect_site) defect_site_coords = defect_site.frac_coords structure = substitution.generate_defect_structure() structure.get_sorted_structure() for charge in charge_states_dict[el]: path = os.path.join(os.getcwd(), f'{el}-substitution', f'Charged{charge}') default_inputs = DefaultInputs(structure) potcar_symbols = [] for s in default_inputs.potcar_symbols:
def get_all_neighbors_and_image(structure, r, include_index=False): """ Modified from `pymatgen <http://pymatgen.org/_modules/pymatgen/core/structure.html#IStructure.get_all_neighbors>`_ to return image (used for mapping to supercell), and to use the f2py wrapped OpenMP dist subroutine to get the distances (smaller memory footprint and faster than numpy). Get neighbours for each atom in the unit cell, out to a distance r Returns a list of list of neighbors for each site in structure. Use this method if you are planning on looping over all sites in the crystal. If you only want neighbors for a particular site, use the method get_neighbors as it may not have to build such a large supercell However if you are looping over all sites in the crystal, this method is more efficient since it only performs one pass over a large enough supercell to contain all possible atoms out to a distance r. The return type is a [(site, dist) ...] since most of the time, subsequent processing requires the distance. Args: - r (float): Radius of sphere. - include_index (bool): Whether to include the non-supercell site - in the returned data Returns: - A list of a list of nearest neighbors for each site, i.e., [[(site, dist, index, image) ...], ..]. Index only supplied if include_index = True. The index is the index of the site in the original (non-supercell) structure. This is needed for ewaldmatrix by keeping track of which sites contribute to the ewald sum. """ recp_len = np.array(structure.lattice.reciprocal_lattice.abc) maxr = np.ceil((r + 0.15) * recp_len / (2 * math.pi)) nmin = np.floor(np.min(structure.frac_coords, axis=0)) - maxr nmax = np.ceil(np.max(structure.frac_coords, axis=0)) + maxr all_ranges = [np.arange(x, y) for x, y in zip(nmin, nmax)] latt = structure._lattice neighbors = [list() for i in range(len(structure._sites))] all_fcoords = np.mod(structure.frac_coords, 1) coords_in_cell = latt.get_cartesian_coords(all_fcoords) site_coords = structure.cart_coords indices = np.arange(len(structure)) for image in itertools.product(*all_ranges): coords = latt.get_cartesian_coords(image) + coords_in_cell all_dists = dist.dist(coords, site_coords, len(coords)) all_within_r = np.bitwise_and(all_dists <= r, all_dists > 1e-8) for (j, d, within_r) in zip(indices, all_dists, all_within_r): nnsite = PeriodicSite( structure[j].specie, coords[j], latt, properties=structure[j].properties, coords_are_cartesian=True, ) for i in indices[within_r]: item = (nnsite, d[i], j, image) if include_index else (nnsite, d[i]) neighbors[i].append(item) return neighbors