def move_random(self, entry_id, factor=0.2, in_place=False, kind='move'): entry = self.get_entry(entry_id) pos = np.array(entry['structure']['positions']).reshape((-1, 3)) # Unit Vectors uv = pychemia.utils.mathematics.unit_vectors( 2 * np.random.rand(*pos.shape) - 1) new_pos = generic_serializer(pos + factor * uv) structure = pychemia.Structure(positions=new_pos, symbols=entry['structure']['symbols'], periodicity=False) if in_place: return self.pcdb.db.pychemia_entries.update_one( {'_id': entry_id}, {'$set': { 'structure': structure.to_dict, 'properties': {} }}) else: structure = pychemia.Structure( positions=new_pos, symbols=entry['structure']['symbols'], periodicity=False) return self.new_entry(structure, active=False)
def cross(self, ids): if len(ids) != 2: raise ValueError("Crossing only implemented between two clusters") entry0 = self.get_entry(ids[0]) entry1 = self.get_entry(ids[1]) pos0 = np.array(entry0['structure']['positions']).reshape((-1, 3)) pos1 = np.array(entry1['structure']['positions']).reshape((-1, 3)) cut = np.random.randint(1, len(pos0)) new_pos0 = np.concatenate((pos0[:cut], pos1[cut:])) new_pos1 = np.concatenate((pos1[:cut], pos0[cut:])) new_structure = pychemia.Structure( positions=new_pos0, symbols=entry0['structure']['symbols'], periodicity=False) entry_id = self.new_entry(structure=new_structure) new_structure = pychemia.Structure( positions=new_pos1, symbols=entry0['structure']['symbols'], periodicity=False) entry_jd = self.new_entry(structure=new_structure) return entry_id, entry_jd
def get_onion_layers(structure): """ Returns the different layers of a finite structure :param structure: :return: """ assert (not structure.is_periodic) layers = [] cur_st = structure.copy() morelayers = True while morelayers: pos = cur_st.positions if len(pos) <= 4: core = range(cur_st.natom) layers.append(core) break st = pychemia.Structure(positions=pos, symbols=len(pos)*['H'], periodicity=False) st.canonical_form() # print('The current volume is %7.3f' % st.volume) if st.volume < 0.1: core = range(cur_st.natom) layers.append(core) break try: voro = scipy.spatial.Voronoi(pos) surface = [i for i in range(cur_st.natom) if -1 in voro.regions[voro.point_region[i]]] except qhull.QhullError: surface = range(cur_st.natom) morelayers = False layers.append(surface) if not morelayers: break core = [i for i in range(cur_st.natom) if i not in surface] if len(core) == 0: break symbols = list(np.array(cur_st.symbols)[np.array(core)]) positions = cur_st.positions[np.array(core)] cur_st = pychemia.Structure(symbols=symbols, positions=positions, periodicity=False) new_layers = [layers[0]] included = list(layers[0]) acumulator = 0 for i in range(1, len(layers)): noincluded = [j for j in range(structure.natom) if j not in included] # print 'Layer: %3d Atoms on Surface: %3d Internal Atoms: %3d' % (i, # len(included) - acumulator, # len(noincluded)) acumulator = len(included) relabel = [noincluded[j] for j in layers[i]] included += relabel new_layers.append(relabel) return new_layers
def random_attaching(structure, seed, target_species, natom_crystal, radius=1.8, basetol=4.0): lys = pychemia.analysis.surface.get_onion_layers(structure) surface = lys[0] counter = 0 while True: if seed not in surface or counter > 0: print('Current Seed not in surface, searching a new seed') seed = find_new_seed(structure, surface, seed, natom_crystal) tol = basetol facets = [] while True: facets, mintol = get_facets(structure, surface, seed, distance_tolerance=tol) if len(facets) > 0: print('Possible Facets', facets) break elif mintol > 2 * basetol: return None, None, None, None else: tol = mintol print('No facets found, increasing tolerance to ', tol) counter = 0 while True: counter += 1 rnd = np.random.randint(len(facets)) facet_chosen = facets[rnd] print('Seed: %3d Number of facets: %3d Facet chosen: %s' % (seed, len(facets), facet_chosen)) center, uvector, atoms_facet = attach_to_facet(structure, facet_chosen) new_sts = {} good_pos = 0 for specie in target_species: cov_rad = pychemia.utils.periodic.covalent_radius(specie) vec = center + radius * cov_rad * uvector new_symbols = list(structure.symbols) + [specie] new_positions = np.concatenate((structure.positions, [vec])) dist_matrix = scipy.spatial.distance_matrix(new_positions, new_positions) identity = np.eye(len(new_positions), len(new_positions)) mindist = np.min((dist_matrix + 100 * identity).flatten()) if mindist > cov_rad: good_pos += 1 print('We have a minimal distance of', mindist) new_sts[specie] = pychemia.Structure(symbols=new_symbols, positions=new_positions, periodicity=False) if good_pos == len(target_species): print('Good position selected for all species') break else: print('No enough good positions: %d. One bad position, choosing a new facet' % good_pos) if counter > len(facets): break if good_pos == len(target_species): break return new_sts, facet_chosen, center, uvector
def run_one(a): """ Take one OQMD 'Entry' object, search all the calculations associated and take the best calculation in order to insert its data into the PyChemia Database :param a: OQMD Entry object :return: """ print('Entry: %6d Number of Calculations: %3d Energies: %s' % (a.id, a.calculation_set.count(), str([c.energy for c in a.calculation_set.all()]))) energy = 1E10 best_calculation = None for c in a.calculation_set.all(): if c.energy is not None and c.energy < energy: best_calculation = c energy = c.energy if best_calculation is None: return None, None cell = best_calculation.output.cell.T symbols = atomic_symbol(best_calculation.output.atomic_numbers) reduced = best_calculation.output.coords structure = pychemia.Structure(cell=cell, symbols=symbols, reduced=reduced) structure_id = best_calculation.output_id entry_id = a.id calculation_id = best_calculation.id energy_pa = best_calculation.energy_pa energy = best_calculation.energy band_gap = best_calculation.band_gap settings = best_calculation.settings try: spacegroup_number = best_calculation.output.spacegroup.number except ValueError: spacegroup_number = None symm = pychemia.crystal.CrystalSymmetry(structure) sym2 = symm.number(1E-2) properties = { 'oqmd': { 'structure_id': structure_id, 'entry_id': entry_id, 'calculation_id': calculation_id, 'energy_pa': energy_pa, 'energy': energy, 'band_gap': band_gap, 'settings': settings, 'spacegroup_number': spacegroup_number }, 'spacegroup_number': { 'value': sym2, 'symprec': 1E-2 } } return structure, properties
def run(self, nparal=4): for ifactor in np.arange(self.ini_factor, self.fin_factor + 0.9 * self.delta_factor, self.delta_factor): lattice = self.structure.lattice new_lengths = (ifactor - 1.0) * np.array(self.expansion) * lattice.lengths + lattice.lengths newlattice_params = tuple(np.concatenate(new_lengths, lattice.angles)) newlattice = pychemia.crystal.Lattice.from_parameters_to_cell(*newlattice_params) newst = pychemia.Structure(cell=newlattice.cell, symbols=self.structure.symbols, reduced=self.structure.reduced) tmpkp = pychemia.crystal.KPoints.optimized_grid(newst.lattice, kp_density=self.kp_density, force_odd=True) if ifactor < 1.0: print('\nCompresing cell to %7.3f percent' % (ifactor * 100)) print('-------------------\n') elif ifactor > 1.0: print('\nExpanding cell to %7.3f percent' % (ifactor * 100)) print('-------------------\n') else: print('\nOriginal cell') print('-------------\n') new_density = tmpkp.get_density_of_kpoints(newst.lattice) print('KP Density: target: %d new lattice: %d' % (self.kp_density, new_density)) print('KP Grid: target: %s new lattice: %s' % (self.kpoints.grid, tmpkp.grid)) if np.prod(tmpkp.grid) > np.prod(self.kpoints.grid): print('The new cell ask for a bigger grid, doing a new convergence...\n') self.cleaner() print('\nConvergence of K-Point Grid') print('---------------------------\n') ck = ConvergenceKPointGrid(newst, workdir=self.workdir + os.sep + 'KPCONV_' + str(ifactor), executable=self.executable, encut=self.encut, energy_tolerance=self.energy_tol, recover=True) ck.run(nparal=nparal) ck.save() ck.report() self.kpoints = ck.best_kpoints else: print('The current grid is still good') self.cleaner() relax = IonRelaxation(structure=newst, workdir=self.workdir + os.sep + 'RELAX_' + str(ifactor), kp_grid=self.kpoints.grid, encut=self.encut, relax_cell=False, target_forces=self.target_forces, waiting=False, executable=self.executable) relax.run(nparal=nparal) vo = pychemia.code.vasp.VaspOutput(self.workdir + '_' + str(ifactor) + '/OUTCAR') relst = relax.get_final_geometry() symm = pychemia.symm.StructureSymmetry(relst) self.output.append({'factor': ifactor, 'volume': newst.volume, 'density': newst.density, 'grid': list(self.kpoints.grid), 'output': vo.to_dict, 'spacegroup': symm.number()}) self.save()
def ase2pychemia(aseatoms): """ Converts an ase atoms object into a pychemia structure object """ cell = aseatoms.get_cell() an = aseatoms.get_atomic_numbers() symbols = pychemia.utils.periodic.atomic_symbol(an) positions = aseatoms.get_positions() return pychemia.Structure(cell=cell, positions=positions, symbols=symbols)
def test_gold(): """ Visualization of Gold FCC lattice : """ a = 4.05 b = a / 2 fcc = pychemia.Structure(symbols=['Au'], cell=[[0, b, b], [b, 0, b], [b, b, 0]], periodicity=True) assert (fcc.natom == 1) assert (fcc.is_periodic is True) assert (fcc.is_crystal is True) fig = fcc.plot() time.sleep(5) fig.remove() fig.stop()
def test_structure(self): """ Test (pychemia.core.structure) : """ a = 4.05 b = a / 2 fcc = pychemia.Structure(symbols=['Au'], cell=[[0, b, b], [b, 0, b], [b, b, 0]], periodicity=True) self.assertEqual(fcc.natom, 1) fcc_copy = fcc.copy() fcc_copy.canonical_form() self.assertTrue(abs(fcc.volume - fcc_copy.volume) < 1E-13) self.assertTrue( np.linalg.norm(fcc_copy.lattice.angles - fcc.lattice.angles) < 1E-10) spc = fcc.supercell((3, 3, 3)) self.assertEqual(spc.natom, 27)
def test_structure(): """ Tests (pychemia.core.structure) : """ pychemia.pcm_log.debug('Tests (pychemia.core.structure)') a = 4.05 b = a / 2 fcc = pychemia.Structure(symbols=['Au'], cell=[[0, b, b], [b, 0, b], [b, b, 0]], periodicity=True) assert (fcc.natom == 1) fcc_copy = fcc.copy() fcc_copy.canonical_form() assert (abs(fcc.volume - fcc_copy.volume) < 1E-13) assert (np.linalg.norm(fcc_copy.lattice.angles - fcc.lattice.angles) < 1E-10) spc = fcc.supercell((3, 3, 3)) assert (spc.natom == 27)
def run_one(a): """ Take one OQMD 'Entry' object, search all the calculations associated and take the best calculation in order to insert its data into the PyChemia Database :param a: OQMD Entry object :return: """ energy = 1E10 best_calculation = None if a.calculation_set.count() > 0: if 'standard' in a.calculations: best_calculation = a.calculations['standard'] calculation_name = 'standard' elif 'fine_relax' in a.calculations: best_calculation = a.calculations['fine_relax'] calculation_name = 'fine_relax' elif 'coarse_relax' in a.calculations: best_calculation = a.calculations['coarse_relax'] calculation_name = 'coarse_relax' elif 'static' in a.calculations: best_calculation = a.calculations['static'] calculation_name = 'static' elif 'relaxation' in a.calculations: best_calculation = a.calculations['relaxation'] calculation_name = 'relaxation' elif len(a.calculations) > 0: calculations = sorted(a.calculations.keys()) print('Calculations found: %s, using the last one' % calculations) best_calculation = a.calculations[calculations[-1]] calculation_name = calculations[-1] else: print('ERROR: Count > 0 and no calculation found') if best_calculation is not None: structure_name = None if best_calculation.output is not None: structure_used = best_calculation.output structure_id = best_calculation.output_id from_output = True elif best_calculation.input is not None: print( 'WARNING: No data was found from the output of the calculation, using input geometries and leaving energetics empty' ) structure_used = best_calculation.input structure_id = best_calculation.input_id from_output = False else: calculation_name = None if a.structures is not None and len(a.structures) > 0: struct_keys = sorted(a.structures.keys()) print( "WARNING: Calculation not found for %s. Structures found: %s using the first one " % (a, struct_keys)) structure_used = a.structures[struct_keys[0]] structure_id = None from_output = False structure_name = struct_keys[0] else: print("ERROR: No calculation and no structure found for %s" % a) return None, None cell = structure_used.cell.T symbols = atomic_symbol(structure_used.atomic_numbers) reduced = structure_used.coords structure = pychemia.Structure(cell=cell, symbols=symbols, reduced=reduced) entry_id = a.id if best_calculation is not None: calculation_id = best_calculation.id energy_pa = best_calculation.energy_pa energy = best_calculation.energy band_gap = best_calculation.band_gap settings = best_calculation.settings try: spacegroup_number = best_calculation.output.spacegroup.number except AttributeError: spacegroup_number = None else: calculation_id = None energy_pa = None energy = None band_gap = None settings = None spacegroup_number = None from_output = False try: symm = pychemia.crystal.CrystalSymmetry(structure) sym2 = symm.number(1E-2) except ValueError: sym2 = None properties = { 'oqmd': { 'structure_id': structure_id, 'entry_id': entry_id, 'calculation_id': calculation_id, 'energy_pa': energy_pa, 'energy': energy, 'band_gap': band_gap, 'settings': settings, 'from_output': from_output, 'calculation_name': calculation_name, 'structure_name': structure_name, 'spacegroup_number': spacegroup_number }, 'spacegroup_number': { 'value': sym2, 'symprec': 1E-2 } } return structure, properties
def rotate_along_indices(structure, h, k, l, layers, tol=1.e-5): cell = structure.cell a1 = np.array(cell[0]) a2 = np.array(cell[1]) a3 = np.array(cell[2]) rcell = structure.lattice.reciprocal().cell b1 = np.array(rcell[0]) b2 = np.array(rcell[1]) b3 = np.array(rcell[2]) # Solve equation pk + ql = 1 for p and q using extended_Euclidean algorithm v = ext_gcd(k, l) p = v[0] q = v[1] # print('p = ', p) # print('q = ', q) k1 = np.dot(p * (k * a1 - h * a2) + q * (l * a1 - h * a3), l * a2 - k * a3) k2 = np.dot(l * (k * a1 - h * a2) - k * (l * a1 - h * a3), l * a2 - k * a3) # print("\n\nk1 = ", k1) # print("k2 = ", k2) if abs(k2) > tol: c = -int(round(k1 / k2)) # print("c = -int(round(k1/k2)) = ", c) p, q = p + c * l, q - c * k # Calculate lattice vectors {v1, v2, v3} defining basis of the new cell v1 = p * (k * a1 - h * a2) + q * (l * a1 - h * a3) v2 = l * a2 - k * a3 n = p * k + q * l v = ext_gcd(n, h) a = v[0] b = v[1] v3 = b * a1 + a * p * a2 + a * q * a3 newbasis = np.array([v1, v2, v3]) # transformation = np.array([[p*k+q*l, -h*p, -h], [0, l, -k], [b, a*p, a*q]]) # inv_transformation = np.linalg.inv(transformation) symbols = [] positions = structure.positions + np.tile( np.dot(structure.cell.T, np.array([0, 0, 0])).reshape(1, 3), (structure.natom, 1)) reduced = np.linalg.solve(newbasis.T, positions.T).T for i in range(3): reduced[:, i] %= 1.0 symbols += structure.symbols surf = pychemia.Structure(symbols=symbols, reduced=reduced, cell=newbasis) if layers > 1: new_surf = surf.supercell((1, 1, layers)) cell = new_surf.cell # cell[2] = cell[2]+(layers+1)*cell[1]+(layers+1)*cell[0] surf = pychemia.Structure(symbols=new_surf.symbols, positions=new_surf.positions, cell=cell) # print '\n\n********* Lattice vectors of the original cell *********\n\n', cell # print '\n\n********* ATOMIC positions in the original cell **********\n', structure.positions # print '\nTotal number of atoms in cell = ', structure.natom # Now create the surface starting from the original structure # surf = structure.copy() # surf.set_cell(newbasis) # print '\n\n********* New basis of the surface cell *********\n\n', surf.cell # print '\n\n********* Atomic coordinates in the newbasis of surface cell *********\n\n', surf.positions # surf = surf.supercell((1, 1, layers)) # a1, a2, a3 = surf.cell # surf.set_cell([a1, a2, # np.cross(a1, a2) * np.dot(a3, np.cross(a1, a2)) / # np.linalg.norm(np.cross(a1, a2)) ** 2]) # Change unit cell to have the x-axis parallel with a surface vector # and z perpendicular to the surface: # a1, a2, a3 = surf.cell # surf.set_cell([(np.linalg.norm(a1), 0, 0), # (np.dot(a1, a2) / np.linalg.norm(a1), # np.sqrt(np.linalg.norm(a2) ** 2 - (np.dot(a1, a2) / np.linalg.norm(a1)) ** 2), 0), # (0, 0, np.linalg.norm(a3))]) # Move atoms into the unit cell: # scaled = surf.reduced # scaled[:, :2] %= 1 # surf.set_reduced(scaled) # surf.center(vacuum=vacuum, axis=2) return surf
def worker(self, path): """ From a given 'path', the worker will collect information from several files such as: fireball.in eigen.dat param.dat answer.bas output collected from the standard output of fireball.x After collecting data into dictionaries, the data is stored in a PyChemia database """ pcdb = get_database(self.db_settings) files = os.listdir(path) properties = {} geo = None if os.path.lexists(path+os.sep+'fireball.in'): properties['path'] = path score = 'input' try: invars = pychemia.code.fireball.read_fireball_in(path + os.sep + 'fireball.in') properties['input'] = invars except: print('Bad fireball.in on %s' % path) if os.path.exists(path+os.sep+'param.dat'): try: score += '+param' param = pychemia.code.fireball.read_param(path + os.sep + 'param.dat') properties['param_dat'] = param except: print('Bad param.dat') if os.path.lexists(path+os.sep+'eigen.dat'): try: score += '+eigen' eigen = pychemia.code.fireball.read_eigen(path + os.sep + 'eigen.dat') properties['eigen_dat'] = eigen except: print('bad eigen.dat') if os.path.lexists(path+os.sep+'answer.bas'): try: score += '+answer' geo = pychemia.code.fireball.read_geometry_bas(path + os.sep + 'answer.bas') periodic = False except: print('Bad answer.bas') for ioutput in self.output_names: if os.path.isfile(path + os.sep + ioutput): rf = open(path + os.sep + ioutput) line = rf.readline() rf.close() if "Welcome to FIREBALL" in line: try: output = pychemia.code.fireball.read_final_fireball_relax(path + os.sep + ioutput) properties['output'] = output break except: print('Bad output %s on %s' % (ioutput, path)) for ifile in files: if ifile[-3:] == 'lvs': try: cell = pychemia.code.fireball.read_lvs(path + os.sep + ifile) periodic = True lat = pychemia.crystral.Lattice(cell) if lat.volume > 1E7: print('Lattice too big, assuming non-periodic structure') periodic = False except: print('Bad %s' % ifile) if geo is not None: print('DB: %d entries, Path: %s' % (pcdb.entries.count(), path)) if periodic: st = pychemia.Structure(symbols=geo.symbols, positions=geo.positions, cell=cell) else: st = pychemia.Structure(symbols=geo.symbols, positions=geo.positions, periodicity=False) pcdb.insert(st, properties) return properties
def generator(args): elements = args['elements'] composition = args['composition'] spg = args['spg'] wyckoff_position = args['wyckoff_position'] generation_info = args['generation_information'] cs = crystal.random_crystal(int(spg), species=elements, numIons=composition, factor=1) if not cs.valid: return None if wyckoff_position in list( np.unique([ '%d%s' % (s.wp.multiplicity, s.wp.letter) for s in cs.wyckoff_sites ])): cell = cs.struct.lattice.matrix reduced = cs.struct.frac_coords symbols = [ele.value for ele in cs.struct.species] structure = pychemia.Structure(cell=cell, symbols=symbols, reduced=reduced) sym = pychemia.crystal.CrystalSymmetry(structure) properties = { 'pretty_formula': structure.get_composition().sorted_formula(sortby='electroneg'), 'elements': elements, 'generation_information': generation_info, 'requested_spg': spg, 'spacegroup': { 'number': sym.number(symprec=1e-3), 'symbol': sym.symbol(symprec=1e-3), 'spglib_wyckoffs': sym.get_symmetry_dataset()['wyckoffs'], 'wyckoffs': list( np.unique([ '%d%s' % (s.wp.multiplicity, s.wp.letter) for s in cs.wyckoff_sites ])), } } print( str(properties['spacegroup']['number']) + '-' + str(structure.nspecies) + '-' + properties['pretty_formula'] + '.vasp') if spg != sym.number(symprec=1e-3): print('++++++++++++++++++++++++++++++++++++++++++++') print('++++++++++++++++++++++++++++++++++++++++++++') print( 'The requested space group %d not equal to the generated space group %d' % (spg, sym.number(symprec=1e-3))) cs.print_all() print(cs.frac_coords.round(5)) print(structure.reduced) print("--------------------------------------------") print('elements', elements) print('compisotion', composition) print('spg', spg) return None return (structure, properties)
def read_poscar(path='POSCAR'): """ Load a POSCAR file and return a pychemia structure object :param path: (str) Filename of the POSCAR to read :return: """ 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: print("POSCAR path not found") return # Reading the POSCAR file rf = open(poscarfile, 'r') comment = rf.readline().strip() latconst = float(rf.readline()) newcell = _np.zeros((3, 3)) newcell[0, :] = latconst * _np.array([float(x) for x in rf.readline().split()]) newcell[1, :] = latconst * _np.array([float(x) for x in rf.readline().split()]) newcell[2, :] = latconst * _np.array([float(x) for x in rf.readline().split()]) line = rf.readline() species = None try: natom_per_species = _np.array([int(x) for x in line.split()]) # print 'Old Format' except ValueError: # print 'New format' species = [x for x in line.split()] line = rf.readline() natom_per_species = _np.array([int(x) for x in line.split()]) natom = _np.sum(natom_per_species) if species is None: if os.path.isfile(potcarfile): species = get_species(potcarfile) elif len(comment.split()) == len(natom_per_species): species = comment.split() else: print(""" ERROR: The POSCAR does not contain information about the species present on the structure You can set a consistent POTCAR along the POSCAR or modify your POSCAR by adding the atomic symbol on the sixth line of the file""") return None if not species: print('No information about species') raise ValueError() symbols = [] for i in range(len(natom_per_species)): numspe = natom_per_species[i] for j in range(numspe): 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 = _np.array(pos).reshape((-1, 3)) if kmode == 'Cartesian': return pychemia.Structure(cell=newcell, symbols=symbols, reduced=pos, comment=comment) else: return pychemia.Structure(cell=newcell, symbols=symbols, reduced=pos, comment=comment)
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 = pychemia.Structure(positions=new_positions, symbols=symbols, periodicity=False) lj = pychemia.code.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 = pychemia.Structure(positions=pos_orig, symbols=symbols, periodicity=False) lj = pychemia.code.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 = pychemia.Structure(positions=pos_dest, symbols=symbols, periodicity=False) lj = pychemia.code.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 = pychemia.analysis.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 = pychemia.Structure(positions=new_positions, symbols=st_orig.symbols, periodicity=False) lj = pychemia.code.LennardJones(new_structure) if lj.get_energy() < 0.0: print( 'Effective factor reduced to %7.3f, original factor %7.3f' % (reduc * factor, factor)) break reduc -= 0.05 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: print("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 = pychemia.Structure(positions=new_positions, symbols=st_orig.symbols, periodicity=False) # print 'Density of cluster', new_structure.density if in_place: return self.pcdb.update(entry_id, structure=new_structure, properties={}) else: return self.new_entry(new_structure, active=False)