def build_supercell_full_disorder(pstructure, scaling_matrix): """ get a supercell with all disordered sites inside, should be used to generate a certain config based on instruction :param pstructure: :param scaling_matrix: :return: """ scale_matrix = np.array(scaling_matrix, np.int16) if scale_matrix.shape != (3, 3): scale_matrix = np.array(scale_matrix * np.eye(3), np.int16) new_lattice = Lattice(np.dot(scale_matrix, pstructure._lattice.matrix)) f_lat = lattice_points_in_supercell(scale_matrix) c_lat = new_lattice.get_cartesian_coords(f_lat) new_sites = [] for site in pstructure.sites: icell = 0 for v in c_lat: site_properties = deepcopy(site.properties) site_properties['icell'] = icell s = PeriodicSite(site.species, site.coords + v, new_lattice, properties=site_properties, coords_are_cartesian=True, to_unit_cell=False) new_sites.append(s) icell += 1 new_charge = pstructure._charge * np.linalg.det( scale_matrix) if pstructure._charge else None return Structure.from_sites(new_sites, charge=new_charge), len(c_lat)
def __getitem__(self, frames): """ Gets a subset of the trajectory if a slice is given, if an int is given, return a structure Args: frames (int, slice): int or slice of trajectory to return Return: (Trajectory, Structure) Subset of trajectory """ if isinstance(frames, int) and frames < self.frac_coords.shape[0]: lattice = self.lattice if self.constant_lattice else self.lattice[frames] site_properties = self.site_properties[frames] if self.site_properties else None return Structure(Lattice(lattice), self.species, self.frac_coords[frames], site_properties=site_properties, to_unit_cell=True) if isinstance(frames, slice): frames = np.arange(frames.start, frames.stop, frames.step) elif not (isinstance(frames, list) or isinstance(frames, np.ndarray)): try: frames = np.asarray(frames) except: raise Exception('Given accessor is not of type int, slice, tuple, list, or array') if (isinstance(frames, list) or isinstance(frames, np.ndarray)) and \ (np.asarray([frames]) < self.frac_coords.shape[0]).all(): if self.constant_lattice: lattice = self.lattice else: lattice = self.lattice[frames, :] return Trajectory(lattice, self.species, self.frac_coords[frames, :], self.time_step, self.site_properties) else: warnings.warn('Some or all selected frames exceed trajectory length') return
def apply_transformation(self, structure): """ Returns a copy of structure with lattice parameters and sites scaled to the same degree as the relaxed_structure. Arg: structure (Structure): A structurally similar structure in regards to crystal and site positions. """ if self.species_map is None: match = StructureMatcher() s_map = match.get_best_electronegativity_anonymous_mapping(self.unrelaxed_structure, structure) else: s_map = self.species_map params = list(structure.lattice.abc) params.extend(structure.lattice.angles) new_lattice = Lattice.from_parameters(*(p * self.params_percent_change[i] for i, p in enumerate(params))) species, frac_coords = [], [] for site in self.relaxed_structure: species.append(s_map[site.specie]) frac_coords.append(site.frac_coords) return Structure(new_lattice, species, frac_coords)
def generate_Si_cluster(): from pymatgen.io.xyz import XYZ coords = [[0, 0, 0], [0.75, 0.5, 0.75]] lattice = Lattice.from_parameters(a=3.84, b=3.84, c=3.84, alpha=120, beta=90, gamma=60) struct = Structure(lattice, ['Si', 'Si'], coords) struct.make_supercell([2, 2, 2]) # Creating molecule for testing mol = Molecule.from_sites(struct) XYZ(mol).write_file(os.path.join(test_dir, "Si_cluster.xyz")) # Rorate the whole molecule mol_rotated = mol.copy() rotate(mol_rotated, seed=42) XYZ(mol_rotated).write_file(os.path.join(test_dir, "Si_cluster_rotated.xyz")) # Perturbing the atom positions mol_perturbed = mol.copy() perturb(mol_perturbed, 0.3, seed=42) XYZ(mol_perturbed).write_file(os.path.join(test_dir, "Si_cluster_perturbed.xyz")) # Permuting the order of the atoms mol_permuted = mol.copy() permute(mol_permuted, seed=42) XYZ(mol_permuted).write_file(os.path.join(test_dir, "Si_cluster_permuted.xyz")) # All-in-one mol2 = mol.copy() rotate(mol2, seed=42) perturb(mol2, 0.3, seed=42) permute(mol2, seed=42) XYZ(mol2).write_file(os.path.join(test_dir, "Si_cluster_2.xyz"))
def get_modeldimers_array(bones: [ModelBone], lattice: Lattice, maxfold=2): """ :param maxfold: translation vector in fc can be [h, h, h] where maxfold <= h <= maxfold :return: dimers_array, z x z x n array, dimers[i][j][k] is the dimer of omols[i], omols[j] with translation vector as transv_fcs[k] transv_fcs """ z = len(bones) transv_1d = list(range(-maxfold, maxfold + 1)) transv_fcs = np.array(np.meshgrid(transv_1d, transv_1d, transv_1d)).T.reshape(-1, 3) # symmetry dimers[i][j][transv_fcs[k]] = dimers[j][i][-transv_fcs[k]] dimers = np.empty((z, z, len(transv_fcs)), dtype=ModelBoneDimer) used_transv_fcs = transv_fcs for i in range(z): ref = deepcopy(bones[i]) for j in range(z): var = deepcopy(bones[j]) for k in range(len(used_transv_fcs)): transv = lattice.get_cartesian_coords(used_transv_fcs[k]) var_k: ModelBone = deepcopy(var) for h in range(len(var_k)): var_k.pts[h] += transv dimer_ijk = ModelBoneDimer(ref, var_k, label="{}_{}_{}".format(i, j, k)) dimers[i][j][k] = dimer_ijk return dimers, transv_fcs
def apply_transformation(self, structure): """ Returns a copy of structure with lattice parameters and sites scaled to the same degree as the relaxed_structure. Arg: structure (Structure): A structurally similar structure in regards to crystal and site positions. """ if self.species_map == None: match = StructureMatcher() s_map = \ match.get_best_electronegativity_anonymous_mapping(self.unrelaxed_structure, structure) else: s_map = self.species_map params = list(structure.lattice.abc) params.extend(structure.lattice.angles) new_lattice = Lattice.from_parameters(*[p*self.params_percent_change[i] \ for i, p in enumerate(params)]) species, frac_coords = [], [] for site in self.relaxed_structure: species.append(s_map[site.specie]) frac_coords.append(site.frac_coords) return Structure(new_lattice, species, frac_coords)
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 setUp(self): self.si = Element("Si") coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) self.lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) self.struct = Structure(self.lattice, [self.si, self.si], coords)
def test_properties(self): self.assertEquals(self.kpoint.frac_coords[0], 0.1) self.assertEquals(self.kpoint.frac_coords[1], 0.4) self.assertEquals(self.kpoint.frac_coords[2], -0.5) self.assertEquals(self.kpoint.a, 0.1) self.assertEquals(self.kpoint.b, 0.4) self.assertEquals(self.kpoint.c, -0.5) self.assertEquals(self.lattice, Lattice.cubic(10.0)) self.assertEquals(self.kpoint.cart_coords[0], 1.0) self.assertEquals(self.kpoint.cart_coords[1], 4.0) self.assertEquals(self.kpoint.cart_coords[2], -5.0) self.assertEqual(self.kpoint.label, "X")
def setUp(self): l = Lattice([[3.52,0.0,2.033], [1.174,3.32,2.033], \ [0.0,0.0,4.066]]) s_bulk = Structure(l, ['Ga', 'As'], \ [[0.0000, 0.0000, 0.0000], \ [0.2500, 0.2500, 0.2500]]) defect_site = PeriodicSite('As', [0.25, 0.25, 0.25], l) defect = Vacancy(s_bulk, defect_site, charge=1.) defect_entry = DefectEntry(defect, 0.) entries = [defect_entry] vbm = 0.2 band_gap = 1. dpd = DefectPhaseDiagram(entries, vbm, band_gap) self.dp = DefectPlotter(dpd)
def get_dist_and_trans(lattice: Lattice, fc1, fc2): """ get the shortest distance and corresponding translation vector between two frac coords :param lattice: pmg lattic obj :param fc1: :param fc2: :return: """ v, d2 = pbc_shortest_vectors(lattice, fc1, fc2, return_d2=True) if len(np.array(fc1).shape) == 1: fc = lattice.get_fractional_coords(v[0][0]) + fc1 - fc2 else: fc = np.zeros((len(fc1), len(fc2), 3)) for i in range(len(fc1)): for j in range(len(fc2)): fc_vector = np.dot(v[i][j], lattice.inv_matrix) fc[i][j] = fc_vector + fc1[i] - fc2[j] return np.sqrt(d2), fc
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_parameters(*s.lattice.parameters) self.lattice = new_lat
def __init__(self, cifstring: str): """ this can only handle one alternative configuration for the asymmetric unit one asymmetric unit == inv_conf + disg1 + disg2 disg1 = disunit_a + disunit_b + ... disg2 = disunit_a' + disunit_b' + ... DisorderPair_a = disunit_a + disunit_a' if the cif file contains previouly fitted occu and disg, we call it dis-0 and we use occu/disg info to get inv_conf, disg1, disg2 if there is no previously fitted info, we deal with the following situations: dis-1: set(self.tags) is ["", <non-word>, <word>, ...], EI<non-word> -- EI, note x17059.cif is dis-1 but it has hydrogens like H12A -- H12D, this can only be captured by previously fitted disg and occu, dis-2: set(self.tags) is ["", <non-word>, <word>, ...], EI<non-word> -- E'I' e.g. ALOVOO.cif nodis-0: no dup in self.eis, set(self.tags) is {""} nodis-1: dup in self.eis, set(self.tags) is ["", <word>, ...], this could be a dis as in ASIXEH weird: else for dis-1, dis-2, we fill the occu, disg fields in cifdata, so they can be coonverted to dis-0 Attributes: data[atomlabel] = [x, y, z, symbol, occu, disgrp] this will be used to write config cif file """ # prepare_data into cifdata self.classification = None self.cifstring = cifstring self.identifier, self.cifdata = get_pmg_dict(self.cifstring) self.cifdata['_atom_site_fract_x'] = [ braket2float(x) for x in self.cifdata['_atom_site_fract_x'] ] self.cifdata['_atom_site_fract_y'] = [ braket2float(x) for x in self.cifdata['_atom_site_fract_y'] ] self.cifdata['_atom_site_fract_z'] = [ braket2float(x) for x in self.cifdata['_atom_site_fract_z'] ] for i in range(len(self.cifdata['_atom_site_type_symbol'])): if self.cifdata['_atom_site_type_symbol'][i] == 'D': warnings.warn( 'D is considered as H in the ciffile _atom_site_type_symbol!' ) self.cifdata['_atom_site_type_symbol'][i] = 'H' # check cif file try: labels = self.cifdata['_atom_site_label'] except KeyError: raise CifFileError('no _atom_site_label field in the cifstring!') if len(labels) != len(set(labels)): warnings.warn( 'duplicate labels found in the cifstring, reassign labels!') for i in range(len(self.cifdata['_atom_site_label'])): label = AtomLabel(self.cifdata['_atom_site_label'][i]) self.cifdata['_atom_site_label'][i] = label.element + str( i) + label.tag # raise CifFileError('duplicate labels found in the cifstring!') # "global" info self.labels = [AtomLabel(lab) for lab in labels] self.eis = [al.ei for al in self.labels] self.tags = [al.tag for al in self.labels] self.latparams = [self.cifdata[k] for k in latt_labels] self.lattice = Lattice.from_parameters( *[braket2float(p) for p in self.latparams], True) if '_atom_site_disorder_group' in self.cifdata.keys(): self.was_fitted = True # deal with e.g. k06071 for i in range(len(self.cifdata['_atom_site_disorder_group'])): if self.cifdata['_atom_site_disorder_group'][i] != ".": dv = self.cifdata['_atom_site_disorder_group'][i] dv = abs(int(dv)) self.cifdata['_atom_site_disorder_group'][i] = dv disgs = self.cifdata['_atom_site_disorder_group'] disg_vals = list(set([disg for disg in disgs if disg != "."])) disg_vals.sort() for i in range(len(self.cifdata['_atom_site_disorder_group'])): for j in range(len(disg_vals)): dv = disg_vals[j] if self.cifdata['_atom_site_disorder_group'][i] == dv: self.cifdata['_atom_site_disorder_group'][i] = str(j + 1) break if '_atom_site_occupancy' not in self.cifdata.keys(): occus = [] for disg in self.cifdata['_atom_site_disorder_group']: if disg == '.': occus.append(1) else: if int(disg) & 1: occus.append(0.51) else: occus.append(0.49) self.cifdata['_atom_site_occupancy'] = occus else: self.was_fitted = False if self.was_fitted: self.cifdata['_atom_site_occupancy'] = [ braket2float(x) for x in self.cifdata['_atom_site_occupancy'] ] # self.cifdata['_atom_site_disorder_group'] = [braket2float(x) for x in # self.cifdata['_atom_site_disorder_group']] # prepare self.data to be used in parsing data = map( list, zip( self.cifdata['_atom_site_fract_x'], self.cifdata['_atom_site_fract_y'], self.cifdata['_atom_site_fract_z'], self.cifdata['_atom_site_type_symbol'], )) data = list(data) data = OrderedDict(zip(self.labels, data)) self.data = data for i in range(len(self.labels)): al = self.labels[i] if self.was_fitted: self.data[al].append(self.cifdata['_atom_site_occupancy'][i]) self.data[al].append( self.cifdata['_atom_site_disorder_group'][i]) else: self.data[al].append(None) self.data[al].append(None)
def __getitem__(self, frames): """ Gets a subset of the trajectory if a slice is given, if an int is given, return a structure Args: frames (int, slice): int or slice of trajectory to return Return: (Trajectory, Structure) Subset of trajectory """ # If trajectory is in displacement mode, return the displacements at that frame if self.coords_are_displacement: if isinstance(frames, int): if frames >= np.shape(self.frac_coords)[0]: raise ValueError( "Selected frame exceeds trajectory length") # For integer input, return the displacements at that timestep return self.frac_coords[frames] if isinstance(frames, slice): # For slice input, return a list of the displacements start, stop, step = frames.indices(len(self)) return [self.frac_coords[i] for i in range(start, stop, step)] if isinstance(frames, (list, np.ndarray)): # For list input, return a list of the displacements pruned_frames = [ i for i in frames if i < len(self) ] # Get rid of frames that exceed trajectory length if len(pruned_frames) < len(frames): warnings.warn( "Some or all selected frames exceed trajectory length") return [self.frac_coords[i] for i in pruned_frames] raise Exception( "Given accessor is not of type int, slice, list, or array") # If trajectory is in positions mode, return a structure for the given frame or trajectory for the given frames if isinstance(frames, int): if frames >= np.shape(self.frac_coords)[0]: raise ValueError("Selected frame exceeds trajectory length") # For integer input, return the structure at that timestep lattice = self.lattice if self.constant_lattice else self.lattice[ frames] site_properties = (self.site_properties[frames] if self.site_properties else None) site_properties = (self.site_properties[frames] if self.site_properties else None) return Structure( Lattice(lattice), self.species, self.frac_coords[frames], site_properties=site_properties, to_unit_cell=True, ) if isinstance(frames, slice): # For slice input, return a trajectory of the sliced time start, stop, step = frames.indices(len(self)) pruned_frames = range(start, stop, step) lattice = (self.lattice if self.constant_lattice else [self.lattice[i] for i in pruned_frames]) frac_coords = [self.frac_coords[i] for i in pruned_frames] if self.site_properties is not None: site_properties = [ self.site_properties[i] for i in pruned_frames ] else: site_properties = None if self.frame_properties is not None: frame_properties = {} for key, item in self.frame_properties.items(): frame_properties[key] = [item[i] for i in pruned_frames] else: frame_properties = None return Trajectory( lattice, self.species, frac_coords, time_step=self.time_step, site_properties=site_properties, frame_properties=frame_properties, constant_lattice=self.constant_lattice, coords_are_displacement=False, base_positions=self.base_positions, ) if isinstance(frames, (list, np.ndarray)): # For list input, return a trajectory of the specified times pruned_frames = [ i for i in frames if i < len(self) ] # Get rid of frames that exceed trajectory length if len(pruned_frames) < len(frames): warnings.warn( "Some or all selected frames exceed trajectory length") lattice = (self.lattice if self.constant_lattice else [self.lattice[i] for i in pruned_frames]) frac_coords = [self.frac_coords[i] for i in pruned_frames] if self.site_properties is not None: site_properties = [ self.site_properties[i] for i in pruned_frames ] else: site_properties = None if self.frame_properties is not None: frame_properties = {} for key, item in self.frame_properties.items(): frame_properties[key] = [item[i] for i in pruned_frames] else: frame_properties = None return Trajectory( lattice, self.species, frac_coords, time_step=self.time_step, site_properties=site_properties, frame_properties=frame_properties, constant_lattice=self.constant_lattice, coords_are_displacement=False, base_positions=self.base_positions, ) raise Exception( "Given accessor is not of type int, slice, tuple, list, or array")
crystal = Structure.from_file( filename=each_original_vasp) # vasp file read -> structure 정보 접근 for i in range( num_noised_files): # original vasp file로부터 noised vasp 파일을 횟수만큼 생성 # -------------------------- lattice 구조에 noise 부여하기 ----------------------------------------- lattice_shell = np.zeros( shape=crystal.lattice.matrix.shape) # 새로운 lattice matrix shell 생성 noised_lattice = gaussian_noise_level * np.random.normal( 0, 1, 9).reshape((3, 3)) # 기존 lattice matrix에 넣을 noise 생성 new_lattice_matrix = crystal.lattice.matrix + noised_lattice # 기존 lattice matrix에 noise 추가 noised_lattice = Lattice(new_lattice_matrix) # noise 추가된 새로운 lattice noised_crystal = Structure( noised_lattice, crystal.species, crystal.frac_coords) # unitcell에 noise 추가된 결정구조 # ----------------------------------------------------------------------------------------------- # --------------------------- 결정구조 안의 각 원자에 대해 좌표 noise 부여 ------------------------- for j in range( len(noised_crystal)): # For each atom in the crystal structure noised_coord = list(gaussian_noise_level * np.random.normal( 0, 1, 3).reshape(-1)) # internal coordinate noise noised_crystal[j] = noised_crystal[j].specie.name, noised_crystal[ j].frac_coords + noised_coord # 각 원자 좌표에 noise 추가 # ----------------------------------------------------------------------------------------------- # vasp file saved
def setUp(self): self.lattice = Lattice.cubic(10.0) self.kpoint = Kpoint([0.1, 0.4, -0.5], self.lattice, label = "X")
def get_orthogonal_grain(self): a, b, c = self.lattice.abc new_latt = Lattice.orthorhombic(a, b, c) return Structure(new_latt, self.species, self.frac_coords)