def move(self, entry_id, entry_jd, factor=0.2, in_place=False): """ Moves entry_id in the direction of entry_jd If in_place is True the movement occurs on the same address as entry_id :param factor: :param entry_id: :param entry_jd: :param in_place: :return: """ structure_mobile = self.get_structure(entry_id) structure_target = self.get_structure(entry_jd) if structure_mobile.natom != structure_target.natom: # Moving structures with different number of atoms is only implemented for smaller structures moving # towards bigger ones by making a super-cell and only if their size is smaller that 'max_comp_mult' mult1 = structure_mobile.get_composition().gcd mult2 = structure_target.get_composition().gcd lcd = mult1 * mult2 / gcd(mult1, mult2) if lcd > self.max_comp_mult: # The resulting structure is bigger than the limit # cannot move if not in_place: return self.new_entry(structure_mobile) else: return entry_id # We will move structure1 in the direction of structure2 match = StructureMatch(structure_target, structure_mobile) match.match_size() match.match_shape() match.match_atoms() displacements = match.reduced_displacement() new_reduced = match.structure2.reduced + factor * displacements new_cell = match.structure2.cell new_symbols = match.structure2.symbols new_structure = Structure(reduced=new_reduced, symbols=new_symbols, cell=new_cell) if in_place: return self.set_structure(entry_id, new_structure) else: return self.new_entry(new_structure, active=False)
def __init__(self): Codes.__init__(self) self.workdir = None self.geometry = {} self.driver = {} self.hamiltonian = {} self.options = {} self.analysis = {} self.parser_options = {} self.slater_koster = [] self.structure = Structure() self.binary = 'dftb+' self.runner = None self.kpoints = None self.stdout_file = None self.output = None self.kp_density = None
def spglib_version(): from pychemia import Structure from . import CrystalSymmetry # Testing version of spglib st = Structure(symbols=['H']) symm = CrystalSymmetry(st) ret = spg.spglib.spg.dataset(symm.transposed, symm.reduced, symm.numbers, 1e-5, -1.0) if type(ret[3]) is list: HAS_SPGLIB = False version = "%d.%d.%d" % spg.get_version() print('SPGLIB current version is %s, please install spglib > 1.9' % version) else: HAS_SPGLIB = True return HAS_SPGLIB
def __init__(self, workdir='.'): if not os.path.lexists(workdir): os.mkdir(workdir) CodeRun.__init__(self, executable='dftb+', workdir=workdir) self.geometry = {} self.driver = {} self.hamiltonian = {} self.options = {} self.analysis = {} self.parser_options = {} self.slater_koster = [] self.structure = Structure() self.runner = None self.kpoints = None self.stdout_file = None self.output = None self.kp_density = None
def Al2O3(): return Structure(symbols=[ 'Al', 'Al', 'Al', 'Al', 'Al', 'Al', 'Al', 'Al', 'Al', 'Al', 'Al', 'Al', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O' ], cell=[[4.807634, 0.0, 0.0], [-2.403817, 4.163534, 0.0], [0.0, 0.0, 13.11727]], reduced=[[0.0, 0.0, 0.352185], [0.666667, 0.333333, 0.685518], [0.333333, 0.666667, 0.018852], [0.0, 0.0, 0.647815], [0.666667, 0.333333, 0.981148], [0.333333, 0.666667, 0.314482], [0.0, 0.0, 0.147815], [0.666667, 0.333333, 0.481148], [0.333333, 0.666667, 0.814482], [0.0, 0.0, 0.852185], [0.666667, 0.333333, 0.185518], [0.333333, 0.666667, 0.518852], [0.306167, 0.0, 0.25], [0.972834, 0.333333, 0.583333], [0.639501, 0.666667, 0.916667], [0.693833, 0.0, 0.75], [0.360499, 0.333333, 0.083333], [0.027166, 0.666667, 0.416667], [0.0, 0.306167, 0.25], [0.666667, 0.639501, 0.583333], [0.333333, 0.972834, 0.916667], [0.0, 0.693833, 0.75], [0.666667, 0.027166, 0.083333], [0.333333, 0.360499, 0.416667], [0.693833, 0.693833, 0.25], [0.360499, 0.027166, 0.583333], [0.027166, 0.360499, 0.916667], [0.306167, 0.306167, 0.75], [0.972834, 0.639501, 0.083333], [0.639501, 0.972834, 0.416667]], periodicity=True)
def movement_sweep(pos_orig, pos_dest, symbols, figname='figure.pdf'): import matplotlib.pyplot as plt fig, ax = plt.subplots(ncols=1, nrows=3, sharex=True, figsize=(11, 8.5)) plt.subplots_adjust(left=0.07, bottom=0.07, right=0.98, top=0.98, wspace=0.08, hspace=0.08) ee = [] ff = [] dd = [] delta = 2E-3 xx = np.arange(0.0, 1.0 + 0.9 * delta, delta) for f in xx: new_positions = direct_move(pos_orig, pos_dest, fraction=f) new_structure = Structure(positions=new_positions, symbols=symbols, periodicity=False) lj = LennardJones(new_structure) ee.append(lj.get_energy()) ff.append(np.max(lj.get_magnitude_forces())) # Distance Matrix dm = scipy.spatial.distance_matrix(new_positions, new_positions) # Min distance md = np.min( np.array(np.array(dm) + 100 * np.eye(len(pos_orig))).flatten()) dd.append(md) ax[0].plot(xx, ee) ax[0].set_ylim(min(ee), 0.1) ax[1].semilogy(xx, ff) ax[2].plot(xx, dd) st = Structure(positions=pos_orig, symbols=symbols, periodicity=False) lj = LennardJones(st) ax[0].plot(0, lj.get_energy(), 'ro') ax[1].semilogy(0, np.max(lj.get_magnitude_forces()), 'ro') dm = scipy.spatial.distance_matrix(lj.structure.positions, lj.structure.positions) md = np.min(np.array(np.array(dm) + 100 * np.eye(len(pos_orig))).flatten()) ax[2].plot(0, md, 'ro') st = Structure(positions=pos_dest, symbols=symbols, periodicity=False) lj = LennardJones(st) ax[0].plot(1, lj.get_energy(), 'ro') ax[1].semilogy(1, np.max(lj.get_magnitude_forces()), 'ro') dm = scipy.spatial.distance_matrix(lj.structure.positions, lj.structure.positions) md = np.min(np.array(np.array(dm) + 100 * np.eye(len(pos_orig)).flatten())) ax[2].plot(1, md, 'ro') ax[2].set_xlim(-0.01, 1.01) ax[0].set_ylabel('Energy') ax[1].set_ylabel('Max Force') ax[2].set_ylabel('Minimal inter atomic distance') plt.savefig(figname)
def move(self, entry_id, entry_jd, factor=0.2, in_place=False): st_orig = self.get_structure(entry_id) st_dest = self.get_structure(entry_jd) cm = ClusterMatch(st_orig, st_dest) cm.match() # pos_orig = np.array(entry_orig['structure']['positions']).reshape((-1, 3)) # pos_dest = np.array(entry_dest['structure']['positions']).reshape((-1, 3)) pos_orig = cm.structure1.positions pos_dest = cm.structure2.positions # Move to a position with negative energy reduc = 1 new_positions = np.array(pos_orig) while True: new_positions = rotation_move(pos_orig, pos_dest, fraction=reduc * factor) new_structure = Structure(positions=new_positions, symbols=st_orig.symbols, periodicity=False) lj = LennardJones(new_structure) if lj.get_energy() < 0.0: break reduc -= 0.05 pcm_log.debug( 'Effective factor will be reduced to %7.3f, original factor %7.3f' % (reduc * factor, factor)) if reduc <= 0.0: # print 'No movement effective' break # Avoid condition with atoms too close distance_matrix = scipy.spatial.distance_matrix( new_positions, new_positions) tmp = np.max(distance_matrix.flatten()) # print 'Scaling by', tmp minimal_distance = np.min( (distance_matrix + tmp * np.eye(len(new_positions))).flatten()) if minimal_distance < 1E-8: pcm_log.debug("Null distance between different atoms, no moving") new_positions = pos_orig if tmp > 5: # print 'Big scaling, better not to move' new_positions = pos_orig else: max_cov = np.max(covalent_radius(st_orig.symbols)) new_positions *= max_cov / minimal_distance new_structure = Structure(positions=new_positions, symbols=st_orig.symbols, periodicity=False) # print 'Density of cluster', new_structure.density if in_place: self.unset_properties(entry_id) return self.set_structure(entry_id, new_structure) else: return self.new_entry(new_structure, active=False)
def read_poscar(path='POSCAR'): """ Load a POSCAR file and return a pychemia structure object :param path: (str) Path to a POSCAR file or a directory where a file named 'POSCAR' is located :return: """ # The argument 'path' could refer to a POSCAR file or the directory where the file 'POSCAR' exists if os.path.isfile(path): poscarfile = path if os.path.dirname(path) != '': potcarfile = os.path.dirname(path) + os.sep + 'POTCAR' else: potcarfile = 'POTCAR' elif os.path.isdir(path) and os.path.isfile(path + os.sep + 'POSCAR'): poscarfile = path + os.sep + 'POSCAR' potcarfile = path + os.sep + 'POTCAR' else: raise ValueError("ERROR: No POSCAR file found on %s" % path) # Reading the POSCAR file rf = open(poscarfile, 'r') comment = rf.readline().strip() latconst = float(rf.readline().split()[0]) newcell = zeros((3, 3)) newcell[0, :] = latconst * array( [float(x) for x in rf.readline().split()[:3]]) newcell[1, :] = latconst * array( [float(x) for x in rf.readline().split()[:3]]) newcell[2, :] = latconst * array( [float(x) for x in rf.readline().split()[:3]]) line = rf.readline() species = None # This is the old format, the only way of knowing which species are refering is by # reading the POTCAR file natom_per_species = array([int(x) for x in line.split() if x.isdigit()]) # Check if the file is the new format, in such case this line contains the # atomic symbols of the atoms and the next line the number of atoms of each # species. The new format makes a POSCAR self-contained to create a structure if len(natom_per_species) == 0: species = [x for x in line.split() if x in atomic_symbols] line = rf.readline() natom_per_species = array( [int(x) for x in line.split() if x.isdigit()]) natom = sum(natom_per_species) if species is None: comment_species = [x for x in comment.split() if x in atomic_symbols] if os.path.isfile(potcarfile): species = get_species(potcarfile) elif len(comment_species) == len(natom_per_species): species = comment_species else: raise ValueError( "ERROR: The POSCAR does not have information about the species present on the structure\n" + "You can set a consistent POTCAR along the POSCAR or modify your POSCAR to the\n" + "new format by adding the atomic symbol(s) on the sixth line of the file" ) symbols = [] for i in range(len(natom_per_species)): for j in range(natom_per_species[i]): symbols.append(species[i]) mode = rf.readline() if mode[0].lower() in ['c', 'k']: kmode = 'Cartesian' else: kmode = 'Direct' pos = [] for i in range(natom): pos += [float(x) for x in rf.readline().split()[:3]] pos = array(pos).reshape((-1, 3)) if kmode == 'Cartesian': return Structure(cell=newcell, symbols=symbols, positions=pos, comment=comment) else: return Structure(cell=newcell, symbols=symbols, reduced=pos, comment=comment)
def get_simple_match(self): self.get_minimal_splitting() # grp1 and grp2 are tuples where the first value is the dimension of cut and the second one # is the second one is the index for the plane cut grp0 = self.ss_tags[0] grp1 = self.ss_tags[1] idim1 = grp0[0] idim2 = grp1[0] cp1 = grp0[1] cp2 = grp1[1] cut_value1 = self.cut_planes[0][idim1][cp1] cut_value2 = self.cut_planes[1][idim2][cp2] print('idim1:', idim1, 'cut_value1:', cut_value1) print('idim2:', idim2, 'cut_value2:', cut_value2) # Select one possible permutation that will make the second # structure being cutted along the same dimension as the first one permsel1 = None permsel2 = None for i in itertools.permutations(range(3), 3): if i[idim1] == idim2: permsel1 = i if i[idim2] == idim1: permsel2 = i if permsel1 is not None and permsel2 is not None: break print('Permutation Selected 1:', permsel1) print('Permutation Selected 2:', permsel2) newreduced1 = np.zeros((self.structures[0].natom, 3)) newreduced2 = np.zeros((self.structures[1].natom, 3)) symbols1 = self.structures[0].natom * ['U'] symbols2 = self.structures[1].natom * ['U'] nsites = self.structures[0].nsites index1 = 0 index2 = 0 for i in range(nsites): if i in self.split_sites[0][grp0]: newreduced1[index1] = self.structures[0].reduced[i, :] symbols1[index1] = self.structures[0].symbols[i] index1 += 1 else: pos = self.structures[0].reduced[i, permsel1] # print i,' - ', self.structures[0].reduced[i], '==>', pos newreduced2[index2] = pos symbols2[index2] = self.structures[0].symbols[i] index2 += 1 # print '1 Reduced\n', newreduced1 # print '2 Reduced\n', newreduced2 # print '1 Symbols:', symbols1 # print '2 Symbols:', symbols2 for i in range(nsites): if i in self.split_sites[1][grp1]: newreduced2[index2] = self.structures[1].reduced[i, :] symbols2[index2] = self.structures[1].symbols[i] index2 += 1 else: pos = self.structures[1].reduced[i, permsel1] # print i,' - ', self.structures[1].reduced[i], '==>', pos newreduced1[index1] = pos symbols1[index1] = self.structures[1].symbols[i] index1 += 1 cell1 = np.array(self.structures[0].cell) cell2 = np.array(self.structures[1].cell) # The Splitting is not checking for right stochiometry right now # Reverting the symbols to the original ones symbols1 = self.structures[0].symbols symbols2 = self.structures[1].symbols newst1 = Structure(symbols=symbols1, reduced=newreduced1, cell=cell1) newst2 = Structure(symbols=symbols2, reduced=newreduced2, cell=cell2) return newst1, newst2
def add_random(self, random_probability=0.3): """ Add one random structure to the population """ entry_id = None structure = Structure() if self.composition is None: raise ValueError('No composition associated to this population') factor = np.random.randint(self.min_comp_mult, self.max_comp_mult + 1) comp = self.composition.composition.copy() print(("Initail composition: %s" % comp)) print((Composition(comp))) print((Composition(comp).symbols)) for i in comp: comp[i] *= factor new_comp = Composition(comp) for i in range(len(new_comp.symbols)): if new_comp.symbols[i] == "Mg": new_comp.symbols[i] = "Ca" else: new_comp.symbols[i] = "Mg" print((new_comp.symbols)) print( "###############################################################") print(("New comp symbols= ", new_comp.symbols)) print( "###############################################################") while True: rnd = random.random() condition = { 'structure.nspecies': new_comp.nspecies, 'structure.natom': new_comp.natom } if self.pcdb_source is None: rnd = 0 if len(self.sources[factor]) == 0: rnd = 0 if self.pcdb_source is None or rnd < random_probability: pcm_log.debug('Random Structure') structure = Structure.random_cell(new_comp, method='stretching', stabilization_number=5, nparal=5, periodic=True) break else: pcm_log.debug('From source') entry_id = self.sources[factor][np.random.randint( 0, len(self.sources[factor]))] structure = self.pcdb_source.get_structure(entry_id) print(("chosen structure from database =", structure)) sym = CrystalSymmetry(structure) scale_factor = float( np.max(covalent_radius(new_comp.species)) / np.max(covalent_radius(structure.species))) reduce_scale = scale_factor**(1. / 3) # WIH msg = 'Mult: %d natom: %d From source: %s Spacegroup: %d Scaling: %7.3f' print((msg % (factor, structure.natom, structure.formula, sym.number(), scale_factor))) # structure.set_cell(np.dot(scale_factor * np.eye(3), structure.cell)) # WIH structure.set_cell( np.dot(reduce_scale * np.eye(3), structure.cell)) # WIH print(("symbols before change = ", structure.symbols)) structure.symbols = new_comp.symbols print(("symbols after change = ", structure.symbols)) self.sources[factor].remove(entry_id) break return self.new_entry(structure), entry_id
def get_new_structure(self, spglib_cell): from pychemia import Structure cell = spglib_cell[0] reduced = spglib_cell[1] symbols = [self.structure.species[x - 1] for x in spglib_cell[2]] return Structure(cell=cell, reduced=reduced, symbols=symbols)
def Cr(): return Structure(symbols=['Cr', 'Cr'], cell=2.812697, reduced=[[0.0, 0.0, 0.0], [0.5, 0.5, 0.5]], periodicity=True)