def __init__(self, row, meta={}, subscript=None, prefix=''): self.row = row self.cell = [['{:.3f}'.format(a) for a in axis] for axis in row.cell] par = ['{:.3f}'.format(x) for x in cell_to_cellpar(row.cell)] self.lengths = par[:3] self.angles = par[3:] self.stress = row.get('stress') if self.stress is not None: self.stress = ', '.join('{0:.3f}'.format(s) for s in self.stress) self.formula = formula_metal(row.numbers) if subscript: self.formula = subscript.sub(r'<sub>\1</sub>', self.formula) kd = meta.get('key_descriptions', {}) create_layout = meta.get('layout') or default_layout self.layout = create_layout(row, kd, prefix) self.dipole = row.get('dipole') if self.dipole is not None: self.dipole = ', '.join('{0:.3f}'.format(d) for d in self.dipole) self.data = row.get('data') if self.data: self.data = ', '.join(self.data.keys()) self.constraints = row.get('constraints') if self.constraints: self.constraints = ', '.join(c.__class__.__name__ for c in self.constraints)
def generate_cif(structure, comment=None, symops=['+x,+y,+z']): parameters = cell_to_cellpar(structure.cell) cif_data = "# " + comment + "\n\n" if comment else '' cif_data += 'data_tilde_project\n' cif_data += '_cell_length_a ' + "%2.6f" % parameters[0] + "\n" cif_data += '_cell_length_b ' + "%2.6f" % parameters[1] + "\n" cif_data += '_cell_length_c ' + "%2.6f" % parameters[2] + "\n" cif_data += '_cell_angle_alpha ' + "%2.6f" % parameters[3] + "\n" cif_data += '_cell_angle_beta ' + "%2.6f" % parameters[4] + "\n" cif_data += '_cell_angle_gamma ' + "%2.6f" % parameters[5] + "\n" cif_data += "_symmetry_space_group_name_H-M 'P1'" + "\n" cif_data += 'loop_' + "\n" cif_data += '_symmetry_equiv_pos_as_xyz' + "\n" for i in symops: cif_data += i + "\n" cif_data += 'loop_' + "\n" cif_data += '_atom_site_type_symbol' + "\n" cif_data += '_atom_site_fract_x' + "\n" cif_data += '_atom_site_fract_y' + "\n" cif_data += '_atom_site_fract_z' + "\n" pos = structure.get_scaled_positions() for n, i in enumerate(structure): cif_data += "%s % 1.8f % 1.8f % 1.8f\n" % (i.symbol, pos[n][0], pos[n][1], pos[n][2]) return cif_data
def _ngl_write_structure(elements, positions, cell): """ Turns structure information into a NGLView-readable protein-database-formatted string. Args: elements (numpy.ndarray/list): Element symbol for each atom. positions (numpy.ndarray/list): Vector of Cartesian atom positions. cell (numpy.ndarray/list): Simulation cell Bravais matrix. Returns: (str): The PDB-formatted representation of the structure. """ from ase.geometry import cell_to_cellpar, cellpar_to_cell if cell is None or any(np.max(cell, axis=0) < 1e-2): # Define a dummy cell if it doesn't exist (eg. for clusters) max_pos = np.max(positions, axis=0) max_pos[np.abs(max_pos) < 1e-2] = 10 cell = np.eye(3) * max_pos cellpar = cell_to_cellpar(cell) exportedcell = cellpar_to_cell(cellpar) rotation = np.linalg.solve(cell, exportedcell) pdb_str = _ngl_write_cell(*cellpar) pdb_str += "MODEL 1\n" if rotation is not None: positions = np.array(positions).dot(rotation) for i, p in enumerate(positions): pdb_str += _ngl_write_atom(i, elements[i], *p) pdb_str += "ENDMDL \n" return pdb_str
def get_cell_parameters(self, atoms): cell = atoms.cell cell_parameters = {} parameters = cell_to_cellpar(cell) for i, param in enumerate(['a', 'b', 'c', 'alpha', 'beta', 'gamma']): cell_parameters.update({param: parameters[i]}) if np.all(self.free_atoms == 0): return cell_parameters dir_map = ['x', 'y', 'z'] relative_positions = np.dot(np.linalg.inv(cell.T), atoms.positions.T).T unique_atoms_w, indices = np.unique(self.atoms_wyckoffs, return_index=True) for ai in indices: if not self.free_atoms[ai]: continue aw = self.atoms_wyckoffs[ai] position = relative_positions[ai] w_position = self.get_parameter_from_position(position, aw[0]) for i, w in enumerate(w_position): if np.isclose(w, 0): continue w_p_name = dir_map[i] + aw cell_parameters.update({w_p_name: w}) return cell_parameters
def write_eon(fileobj, images): """Writes structure to EON reactant.con file Multiple snapshots are allowed.""" if isinstance(images, Atoms): atoms = images elif len(images) == 1: atoms = images[0] else: raise ValueError('Can only write one configuration to EON ' 'reactant.con file') out = [] out.append(atoms.info.get('comment', 'Generated by ASE')) out.append('0.0000 TIME') # ?? a, b, c, alpha, beta, gamma = cell_to_cellpar(atoms.cell) out.append('%-10.6f %-10.6f %-10.6f' % (a, b, c)) out.append('%-10.6f %-10.6f %-10.6f' % (gamma, beta, alpha)) out.append('0 0') # ?? out.append('0 0 0') # ?? symbols = atoms.get_chemical_symbols() massdict = dict(list(zip(symbols, atoms.get_masses()))) atomtypes = sorted(massdict.keys()) atommasses = [massdict[at] for at in atomtypes] natoms = [symbols.count(at) for at in atomtypes] ntypes = len(atomtypes) out.append(str(ntypes)) out.append(' '.join([str(n) for n in natoms])) out.append(' '.join([str(n) for n in atommasses])) atom_id = 0 for n in range(ntypes): fixed = np.array([False] * len(atoms)) out.append(atomtypes[n]) out.append('Coordinates of Component %d' % (n + 1)) indices = [i for i, at in enumerate(symbols) if at == atomtypes[n]] a = atoms[indices] coords = a.positions for c in a.constraints: if not isinstance(c, FixAtoms): warn( 'Only FixAtoms constraints are supported by con files. ' 'Dropping %r', c) continue if c.index.dtype.kind == 'b': fixed = np.array(c.index, dtype=int) else: fixed = np.zeros((natoms[n], ), dtype=int) for i in c.index: fixed[i] = 1 for xyz, fix in zip(coords, fixed): out.append('%22.17f %22.17f %22.17f %d %4d' % (tuple(xyz) + (fix, atom_id))) atom_id += 1 fileobj.write('\n'.join(out)) fileobj.write('\n')
def row2dct( row, key_descriptions: Dict[str, Tuple[str, str, str]] = {}) -> Dict[str, Any]: """Convert row to dict of things for printing or a web-page.""" from ase.db.core import float_to_time_string, now dct = {} atoms = Atoms(cell=row.cell, pbc=row.pbc) dct['size'] = kptdensity2monkhorstpack(atoms, kptdensity=1.8, even=False) dct['cell'] = [['{:.3f}'.format(a) for a in axis] for axis in row.cell] par = ['{:.3f}'.format(x) for x in cell_to_cellpar(row.cell)] dct['lengths'] = par[:3] dct['angles'] = par[3:] stress = row.get('stress') if stress is not None: dct['stress'] = ', '.join('{0:.3f}'.format(s) for s in stress) dct['formula'] = Formula(row.formula).format('abc') dipole = row.get('dipole') if dipole is not None: dct['dipole'] = ', '.join('{0:.3f}'.format(d) for d in dipole) data = row.get('data') if data: dct['data'] = ', '.join(data.keys()) constraints = row.get('constraints') if constraints: dct['constraints'] = ', '.join(c.__class__.__name__ for c in constraints) keys = ({'id', 'energy', 'fmax', 'smax', 'mass', 'age'} | set(key_descriptions) | set(row.key_value_pairs)) dct['table'] = [] for key in keys: if key == 'age': age = float_to_time_string(now() - row.ctime, True) dct['table'].append(('ctime', 'Age', age)) continue value = row.get(key) if value is not None: if isinstance(value, float): value = '{:.3f}'.format(value) elif not isinstance(value, str): value = str(value) desc, unit = key_descriptions.get(key, ['', '', ''])[1:] if unit: value += ' ' + unit dct['table'].append((key, desc, value)) return dct
def get_string(self, significant_figures=6, write_info=False): """ Returns a string to be written as a Res file. Args: significant_figures (int): No. of significant figures to output all quantities. Defaults to 6. write_info (bool): if True, format TITL line using key-value pairs from atoms.info in addition to attributes stored in Res object Returns: String representation of Res. """ # Title line if write_info: info = self.atoms.info.copy() for attribute in ['name', 'pressure', 'energy', 'spacegroup', 'times_found']: if getattr(self, attribute) and attribute not in info: info[attribute] = getattr(self, attribute) lines = ['TITL ' + ' '.join(['{0}={1}'.format(k, v) for (k, v) in info.items()])] else: lines = ['TITL ' + self.print_title()] # Cell abc_ang = cell_to_cellpar(self.atoms.get_cell()) fmt = '{{0:.{0}f}}'.format(significant_figures) cell = ' '.join([fmt.format(a) for a in abc_ang]) lines.append('CELL 1.0 ' + cell) # Latt lines.append('LATT -1') # Atoms symbols = self.atoms.get_chemical_symbols() species_types = [] for symbol in symbols: if symbol not in species_types: species_types.append(symbol) lines.append('SFAC ' + ' '.join(species_types)) fmt = '{{0}} {{1}} {{2:.{0}f}} {{3:.{0}f}} {{4:.{0}f}} 1.0' fmtstr = fmt.format(significant_figures) for symbol, coords in zip(symbols, self.atoms_.get_scaled_positions()): lines.append( fmtstr.format(symbol, species_types.index(symbol) + 1, coords[0], coords[1], coords[2])) lines.append('END') return '\n'.join(lines)
def diffMap(cif, Ro=1.1745, b=0.514, co=6., dx=0.4, dy=0.4, dz=0.4, mesh=None, anion=None): a = ase.io.read(cif) formula = ChemFormula(cif) counter_ion_label = max_electronegativity(formula) counter_ions = a[a.numbers == chem.element( counter_ion_label).atomic_number] counter_ion_positions = counter_ions.positions lat = np.linalg.norm(a.cell, axis=1) shape = tuple(int(round(i)) for i in lat / np.array([dx, dy, dz])) V = np.ones(shape) counter_ion_scaled_positions = counter_ions.get_scaled_positions().copy() connectivity = [ np.array(i) for i in itr.product([-1, 0, 1], repeat=len(V.shape)) ] counter_ion_scaled_positions = np.concatenate( [counter_ion_scaled_positions + con for con in connectivity]) ctIons = np.einsum('ij,kj->ik', a.cell.T, counter_ion_scaled_positions).T counter_ion_positions = counter_ions.positions for i in range(len(V.flatten())): ri = np.dot(a.cell.T, (2 * np.array(unrav(i, V.shape)) + 1.) / (2 * np.array([V.shape]).astype(float))[0]) cti = ctIons[np.all(((ctIons < ri + co) & (ctIons > ri - co)), axis=1)] # counter ions # put in & instaed of == ... Ri = np.linalg.norm(cti - ri, axis=1) V[unrav(i, V.shape)] = np.abs(np.sum(np.exp((Ro - Ri[Ri < co]) / b)) - 1) output_filename = cif.split('.')[0] + '.grd' with open(output_filename, "w") as savefile: savefile.write("Bond Valence Sum Difference\r") # Title from ase.geometry import cell_to_cellpar cellParams = cell_to_cellpar(a.cell) # get ABC alpha, beta, gamma savefile.write(" ".join([str(k) for k in cellParams]) + "\r") savefile.write(" ".join([str(k) for k in V.shape]) + "\r") for i in np.nditer(V.flatten()): savefile.write("%.6f " % (i)) # Write each valence difference value return V
def write_proteindatabank(fileobj, images, write_arrays=True): """Write images to PDB-file.""" if isinstance(fileobj, basestring): fileobj = paropen(fileobj, 'w') if hasattr(images, 'get_positions'): images = [images] rotation = None if images[0].get_pbc().any(): from ase.geometry import cell_to_cellpar, cellpar_to_cell currentcell = images[0].get_cell() cellpar = cell_to_cellpar(currentcell) exportedcell = cellpar_to_cell(cellpar) rotation = np.linalg.solve(currentcell, exportedcell) # ignoring Z-value, using P1 since we have all atoms defined explicitly format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n' fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2], cellpar[3], cellpar[4], cellpar[5])) # 1234567 123 6789012345678901 89 67 456789012345678901234567 890 format = ('ATOM %5d %4s MOL 1 %8.3f%8.3f%8.3f%6.2f%6.2f' ' %2s \n') # RasMol complains if the atom index exceeds 100000. There might # be a limit of 5 digit numbers in this field. MAXNUM = 100000 symbols = images[0].get_chemical_symbols() natoms = len(symbols) for n, atoms in enumerate(images): fileobj.write('MODEL ' + str(n + 1) + '\n') p = atoms.get_positions() occupancy = np.ones(len(atoms)) bfactor = np.zeros(len(atoms)) if write_arrays: if 'occupancy' in atoms.arrays: occupancy = atoms.get_array('occupancy') if 'bfactor' in atoms.arrays: bfactor = atoms.get_array('bfactor') if rotation is not None: p = p.dot(rotation) for a in range(natoms): x, y, z = p[a] occ = occupancy[a] bf = bfactor[a] fileobj.write( format % (a % MAXNUM, symbols[a], x, y, z, occ, bf, symbols[a].upper())) fileobj.write('ENDMDL\n')
def get_cell(ps): descr = ps.get_description().replace('\n', '\n%s' % (' ' * 8)) add_line('Poisson solver: %s' % descr, 8) if hasattr(ps, 'gd'): gd = ps.gd par = cell_to_cellpar(gd.cell_cv * Bohr) h_eff = gd.dv**(1.0 / 3.0) * Bohr l1 = '{:8d} x {:8d} x {:8d} points'.format(*gd.N_c) l2 = '{:8.2f} x {:8.2f} x {:8.2f} AA'.format(*par[:3]) l3 = 'Effective grid spacing dv^(1/3) = {:.4f}'.format(h_eff) add_line('Grid: %s' % l1, 8) add_line(' %s' % l2, 8) add_line(' %s' % l3, 8)
def __get_unitcell(self): """Wrapper to get the unit cell from different structure classes""" from javelin.unitcell import UnitCell try: # javelin structure return self.structure.unitcell except AttributeError: try: # diffpy structure return UnitCell(self.structure.lattice.abcABG()) except AttributeError: try: # ASE structure from ase.geometry import cell_to_cellpar return UnitCell(cell_to_cellpar(self.structure.cell)) except (ImportError, AttributeError) as e: print(e) raise ValueError("Unable to get unit cell from structure")
def set_substrate(atoms, a=None, b=None, c=None, angle_ab=90.0, all_angle_90=False, m=1, fix_volume=False): """ set atoms cellpars to fit the substrate cellpars. a,b , angle_ab are the the inplane cellpars of the substrate all_angle_90: if True,all the angles will be set to 90 m is the multiplier. a*m b*m fix_volume: whether to set c so that the volume is unchanged. Note that this is not always really the case if angles of a-b plane and c is changed. """ cellpars = cell_to_cellpar(atoms.get_cell()) print(a, b, c) a0, b0, c0 = cellpars[0:3] if a is not None: cellpars[0] = a * m if b is not None: cellpars[1] = b * m if c is not None: cellpars[2] = c * m if angle_ab is not None: cellpars[5] = angle_ab if all_angle_90: cellpars[3:] = [90, 90, 90] print(cellpars) if fix_volume: ab = cellpars[5] c = (a0 * b0 * c0 * math.sin(math.radians(ab)) / (a * b * m * m * math.sin(math.radians(angle_ab)))) cellpars[2] = c cell = cellpar_to_cell(cellpars) print(cell) spos = atoms.get_scaled_positions() atoms.set_cell(cell) atoms.set_scaled_positions(spos) return atoms
def print_cell(gd, pbc_c, log): log("""Unit cell: periodic x y z points spacing""") h_c = gd.get_grid_spacings() for c in range(3): log(' %d. axis: %s %10.6f %10.6f %10.6f %3d %8.4f' % ((c + 1, ['no ', 'yes'][int(pbc_c[c])]) + tuple(Bohr * gd.cell_cv[c]) + (gd.N_c[c], Bohr * h_c[c]))) par = cell_to_cellpar(gd.cell_cv * Bohr) log('\n Lengths: {:10.6f} {:10.6f} {:10.6f}'.format(*par[:3])) log(' Angles: {:10.6f} {:10.6f} {:10.6f}\n'.format(*par[3:])) h_eff = gd.dv**(1.0 / 3.0) * Bohr log('Effective grid spacing dv^(1/3) = {:.4f}'.format(h_eff)) log()
def write_jsv(f, atoms): """Writes JSV file.""" f.write('asymmetric_unit_cell\n') f.write('[cell]') for v in cell_to_cellpar(atoms.cell): f.write(' %g' % v) f.write('\n') f.write('[natom] %d\n' % len(atoms)) f.write('[nbond] 0\n') # FIXME f.write('[npoly] 0\n') # FIXME if 'spacegroup' in atoms.info: sg = Spacegroup(atoms.info['spacegroup']) f.write('[space_group] %d %d\n' % (sg.no, sg.setting)) else: f.write('[space_group] 1 1\n') f.write('[title] %s\n' % atoms.info.get('title', 'untitled')) f.write('\n') f.write('[atoms]\n') if 'labels' in atoms.info: labels = atoms.info['labels'] else: labels = [ '%s%d' % (s, i + 1) for i, s in enumerate(atoms.get_chemical_symbols()) ] numbers = atoms.get_atomic_numbers() scaled = atoms.get_scaled_positions() for l, n, p in zip(labels, numbers, scaled): f.write('%-4s %2d %9.6f %9.6f %9.6f\n' % (l, n, p[0], p[1], p[2])) f.write('Label AtomicNumber x y z (repeat natom times)\n') f.write('\n') f.write('[bonds]\n') f.write('\n') f.write('[poly]\n') f.write('\n')
def test_monoclinic(): """Test band structure from different variations of hexagonal cells.""" import numpy as np from ase import Atoms from ase.calculators.test import FreeElectrons from ase.geometry import (crystal_structure_from_cell, cell_to_cellpar, cellpar_to_cell) from ase.dft.kpoints import get_special_points mc1 = [[1, 0, 0], [0, 1, 0], [0, 0.2, 1]] par = cell_to_cellpar(mc1) mc2 = cellpar_to_cell(par) mc3 = [[1, 0, 0], [0, 1, 0], [-0.2, 0, 1]] mc4 = [[1, 0, 0], [-0.2, 1, 0], [0, 0, 1]] path = 'GYHCEM1AXH1' firsttime = True for cell in [mc1, mc2, mc3, mc4]: a = Atoms(cell=cell, pbc=True) a.cell *= 3 a.calc = FreeElectrons(nvalence=1, kpts={'path': path}) cs = crystal_structure_from_cell(a.cell) assert cs == 'monoclinic' r = a.cell.reciprocal() k = get_special_points(a.cell)['H'] print(np.dot(k, r)) a.get_potential_energy() bs = a.calc.band_structure() coords, labelcoords, labels = bs.get_labels() assert ''.join(labels) == path e_skn = bs.energies # bs.plot() if firsttime: coords1 = coords labelcoords1 = labelcoords e_skn1 = e_skn firsttime = False else: for d in [ coords - coords1, labelcoords - labelcoords1, e_skn - e_skn1 ]: print(abs(d).max()) assert abs(d).max() < 1e-13, d
def write_v_sim(filename, atoms): """Write V_Sim input file. Writes the atom positions and unit cell. """ from ase.geometry import cellpar_to_cell, cell_to_cellpar if isinstance(filename, str): f = open(filename) else: # Assume it's a file-like object f = filename # Convert the lattice vectors to triangular matrix by converting # to and from a set of lengths and angles cell = cellpar_to_cell(cell_to_cellpar(atoms.cell)) dxx = cell[0, 0] dyx, dyy = cell[1, 0:2] dzx, dzy, dzz = cell[2, 0:3] f.write('===== v_sim input file created using the' ' Atomic Simulation Environment (ASE) ====\n') f.write('{0} {1} {2}\n'.format(dxx, dyx, dyy)) f.write('{0} {1} {2}\n'.format(dzx, dzy, dzz)) # Use v_sim 3.5 keywords to indicate scaled positions, etc. f.write('#keyword: reduced\n') f.write('#keyword: angstroem\n') if np.alltrue(atoms.pbc): f.write('#keyword: periodic\n') elif not np.any(atoms.pbc): f.write('#keyword: freeBC\n') elif np.array_equiv(atoms.pbc, [True, False, True]): f.write('#keyword: surface\n') else: raise Exception( 'Only supported boundary conditions are full PBC,' ' no periodic boundary, and surface which is free in y direction' ' (i.e. Atoms.pbc = [True, False, True]).') # Add atoms (scaled positions) for position, symbol in zip(atoms.get_scaled_positions(), atoms.get_chemical_symbols()): f.write('{0} {1} {2} {3}\n'.format(position[0], position[1], position[2], symbol))
def write_v_sim(filename, atoms): """Write V_Sim input file. Writes the atom positions and unit cell. """ from ase.geometry import cellpar_to_cell, cell_to_cellpar if isinstance(filename, str): f = open(filename) else: # Assume it's a file-like object f = filename # Convert the lattice vectors to triangular matrix by converting # to and from a set of lengths and angles cell = cellpar_to_cell(cell_to_cellpar(atoms.cell)) dxx = cell[0, 0] dyx, dyy = cell[1, 0:2] dzx, dzy, dzz = cell[2, 0:3] f.write('===== v_sim input file created using the' ' Atomic Simulation Environment (ASE) ====\n') f.write('{0} {1} {2}\n'.format(dxx, dyx, dyy)) f.write('{0} {1} {2}\n'.format(dzx, dzy, dzz)) # Use v_sim 3.5 keywords to indicate scaled positions, etc. f.write('#keyword: reduced\n') f.write('#keyword: angstroem\n') if np.alltrue(atoms.pbc): f.write('#keyword: periodic\n') elif not np.any(atoms.pbc): f.write('#keyword: freeBC\n') elif np.array_equiv(atoms.pbc, [True, False, True]): f.write('#keyword: surface\n') else: raise Exception('Only supported boundary conditions are full PBC,' ' no periodic boundary, and surface which is free in y direction' ' (i.e. Atoms.pbc = [True, False, True]).') # Add atoms (scaled positions) for position, symbol in zip(atoms.get_scaled_positions(), atoms.get_chemical_symbols()): f.write('{0} {1} {2} {3}\n'.format( position[0], position[1], position[2], symbol))
def write_proteindatabank(fileobj, images): """Write images to PDB-file.""" if isinstance(fileobj, basestring): fileobj = paropen(fileobj, 'w') if hasattr(images, 'get_positions'): images = [images] rotation = None if images[0].get_pbc().any(): from ase.geometry import cell_to_cellpar, cellpar_to_cell currentcell = images[0].get_cell() cellpar = cell_to_cellpar(currentcell) exportedcell = cellpar_to_cell(cellpar) rotation = np.linalg.solve(currentcell, exportedcell) # ignoring Z-value, using P1 since we have all atoms defined explicitly format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n' fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2], cellpar[3], cellpar[4], cellpar[5])) # 1234567 123 6789012345678901 89 67 456789012345678901234567 890 format = ('ATOM %5d %4s MOL 1 %8.3f%8.3f%8.3f 1.00 0.00' ' %2s \n') # RasMol complains if the atom index exceeds 100000. There might # be a limit of 5 digit numbers in this field. MAXNUM = 100000 symbols = images[0].get_chemical_symbols() natoms = len(symbols) for n, atoms in enumerate(images): fileobj.write('MODEL ' + str(n + 1) + '\n') p = atoms.get_positions() if rotation is not None: p = p.dot(rotation) for a in range(natoms): x, y, z = p[a] fileobj.write(format % (a % MAXNUM, symbols[a], x, y, z, symbols[a].upper())) fileobj.write('ENDMDL\n')
def test(): myatoms = gen_STO() print(find_sym(myatoms)) #print myatoms.get_chemical_symbols() #natoms,d=ref_atoms_mag(myatoms) ##print ref_atoms_mag(myatoms)[0].get_chemical_symbols() #old_atoms=rev_ref_atoms(natoms,d) #print old_atoms.get_chemical_symbols() #print get_prim_atoms(myatoms) new_atoms = find_primitive_mag(myatoms) #print new_atoms.get_chemical_symbols() print(new_atoms.get_cell()) print(new_atoms.get_scaled_positions()) print(new_atoms.get_volume()) print(spglib.get_spacegroup(new_atoms)) #write('POSCAR',new_atoms,sort=True,vasp5=True) natoms = normalize(new_atoms) print(natoms.get_positions()) print(natoms.get_volume()) print(cell_to_cellpar(natoms.get_cell()))
def write_jsv(f, atoms): """Writes JSV file.""" f.write("asymmetric_unit_cell\n") f.write("[cell]") for v in cell_to_cellpar(atoms.cell): f.write(" %g" % v) f.write("\n") f.write("[natom] %d\n" % len(atoms)) f.write("[nbond] 0\n") # FIXME f.write("[npoly] 0\n") # FIXME if "spacegroup" in atoms.info: sg = Spacegroup(atoms.info["spacegroup"]) f.write("[space_group] %d %d\n" % (sg.no, sg.setting)) else: f.write("[space_group] 1 1\n") f.write("[title] %s\n" % atoms.info.get("title", "untitled")) f.write("\n") f.write("[atoms]\n") if "labels" in atoms.info: labels = atoms.info["labels"] else: labels = ["%s%d" % (s, i + 1) for i, s in enumerate(atoms.get_chemical_symbols())] numbers = atoms.get_atomic_numbers() scaled = atoms.get_scaled_positions() for l, n, p in zip(labels, numbers, scaled): f.write("%-4s %2d %9.6f %9.6f %9.6f\n" % (l, n, p[0], p[1], p[2])) f.write("Label AtomicNumber x y z (repeat natom times)\n") f.write("\n") f.write("[bonds]\n") f.write("\n") f.write("[poly]\n") f.write("\n")
def normalize(atoms, set_origin=False): """ set the most near 0 to 0 make cell -> cellpar ->cell make all the scaled postion between 0 and 1 """ newatoms = atoms.copy() newatoms = force_near_0(newatoms) if set_origin: positions = newatoms.get_positions() s_positions = sorted(positions, key=np.linalg.norm) newatoms.translate(-s_positions[0]) positions = newatoms.get_scaled_positions() newcell = cellpar_to_cell(cell_to_cellpar(newatoms.get_cell())) newatoms.set_cell(newcell) for i, pos in enumerate(positions): positions[i] = np.mod(pos, 1.0) newatoms.set_scaled_positions(positions) return newatoms
def get_lattice_type(self): cellpar = cell_to_cellpar(self.atoms.cell) abc = cellpar[:3] angles = cellpar[3:] min_lv = min(abc) if abc.ptp() < 0.01 * min_lv: if abs(angles - 90).max() < 1: return 'cubic' elif abs(angles - 60).max() < 1: return 'fcc' elif abs(angles - np.arccos(-1 / 3.) * 180 / np.pi).max < 1: return 'bcc' elif abs(angles - 90).max() < 1: if abs(abc[0] - abc[1]).min() < 0.01 * min_lv: return 'tetragonal' else: return 'orthorhombic' elif abs(abc[0] - abc[1]) < 0.01 * min_lv and \ abs(angles[2] - 120) < 1 and abs(angles[:2] - 90).max() < 1: return 'hexagonal' else: return 'not special'
def write_pdb(fileobj, images): """Write images to PDB-file. The format is assumed to follow the description given in http://www.wwpdb.org/documentation/format32/sect9.html.""" if isinstance(fileobj, str): fileobj = paropen(fileobj, 'w') if not isinstance(images, (list, tuple)): images = [images] if images[0].get_pbc().any(): from ase.geometry import cell_to_cellpar cellpar = cell_to_cellpar(images[0].get_cell()) # ignoring Z-value, using P1 since we have all atoms defined explicitly format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n' fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2], cellpar[3], cellpar[4], cellpar[5])) # 1234567 123 6789012345678901 89 67 456789012345678901234567 890 format = 'ATOM %5d %4s MOL 1 %8.3f%8.3f%8.3f 1.00 0.00 %2s \n' # RasMol complains if the atom index exceeds 100000. There might # be a limit of 5 digit numbers in this field. MAXNUM = 100000 symbols = images[0].get_chemical_symbols() natoms = len(symbols) for n, atoms in enumerate(images): fileobj.write('MODEL ' + str(n + 1) + '\n') p = atoms.get_positions() for a in range(natoms): x, y, z = p[a] fileobj.write( format % (a % MAXNUM, symbols[a], x, y, z, symbols[a].rjust(2))) fileobj.write('ENDMDL\n')
def write_proteindatabank(fileobj, images): """Write images to PDB-file. The format is assumed to follow the description given in http://www.wwpdb.org/documentation/format32/sect9.html.""" if isinstance(fileobj, str): fileobj = paropen(fileobj, 'w') if hasattr(images, 'get_positions'): images = [images] if images[0].get_pbc().any(): from ase.geometry import cell_to_cellpar cellpar = cell_to_cellpar(images[0].get_cell()) # ignoring Z-value, using P1 since we have all atoms defined explicitly format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n' fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2], cellpar[3], cellpar[4], cellpar[5])) # 1234567 123 6789012345678901 89 67 456789012345678901234567 890 format = ('ATOM %5d %4s MOL 1 %8.3f%8.3f%8.3f 1.00 0.00' ' %2s \n') # RasMol complains if the atom index exceeds 100000. There might # be a limit of 5 digit numbers in this field. MAXNUM = 100000 symbols = images[0].get_chemical_symbols() natoms = len(symbols) for n, atoms in enumerate(images): fileobj.write('MODEL ' + str(n + 1) + '\n') p = atoms.get_positions() for a in range(natoms): x, y, z = p[a] fileobj.write(format % (a % MAXNUM, symbols[a], x, y, z, symbols[a].rjust(2))) fileobj.write('ENDMDL\n')
def ase_to_eq_cif(ase_obj, supply_sg=True): """ From ASE object generate CIF with symmetry-equivalent atoms; mostly for debugging """ if supply_sg: sg_hm = getattr(ase_obj.info.get('spacegroup', object), 'symbol', 'P1') sg_n = getattr(ase_obj.info.get('spacegroup', object), 'no', 1) else: sg_hm, sg_n = 'P1', 1 parameters = cell_to_cellpar(ase_obj.cell) cif_data = 'data_tilde_labs\n' cif_data += '_cell_length_a ' + "%2.6f" % parameters[0] + "\n" cif_data += '_cell_length_b ' + "%2.6f" % parameters[1] + "\n" cif_data += '_cell_length_c ' + "%2.6f" % parameters[2] + "\n" cif_data += '_cell_angle_alpha ' + "%2.6f" % parameters[3] + "\n" cif_data += '_cell_angle_beta ' + "%2.6f" % parameters[4] + "\n" cif_data += '_cell_angle_gamma ' + "%2.6f" % parameters[5] + "\n" cif_data += "_symmetry_space_group_name_H-M '%s'" % sg_hm + "\n" cif_data += "_symmetry_Int_Tables_number %s" % sg_n + "\n" cif_data += 'loop_' + "\n" cif_data += ' _symmetry_equiv_pos_as_xyz' + "\n" cif_data += ' +x,+y,+z' + "\n" cif_data += 'loop_' + "\n" cif_data += ' _atom_site_type_symbol' + "\n" cif_data += ' _atom_site_fract_x' + "\n" cif_data += ' _atom_site_fract_y' + "\n" cif_data += ' _atom_site_fract_z' + "\n" pos = ase_obj.get_scaled_positions(wrap=False) for n, item in enumerate(ase_obj): cif_data += " {:3s} {: 6.5f} {: 6.5f} {: 6.5f}\n".format( item.symbol, pos[n][0], pos[n][1], pos[n][2]) return cif_data
def atomsToPDB(fileobj, atoms): ''' ASE idiotically truncates to 3dp when writing .pdb This is a simplification of ase write (note - code taken from ase/io/proteindatabank.py Keep as atoms rather than Crystal to fit with aseToRDKit stuff ''' from ase.geometry import cell_to_cellpar # THIS CANNOT BE READ BY RDKIT SO IS NOT MUCH USE !!! if isinstance(fileobj, basestring): fileobj = paropen(fileobj, 'w') cellpar = cell_to_cellpar(atoms.get_cell()) fileobj.write('CRYST1 ' + ' '.join(['%s' % x for x in cellpar]) + ' P 1\n') fileobj.write('MODEL 1\n') symbols = atoms.get_chemical_symbols() natoms = len(symbols) format = ('ATOM %s %s MOL 1 %s %s %s 1.00 0.00' ' %2s \n') p = atoms.get_positions() for a in range(natoms): x, y, z = p[a] fileobj.write(format % (a, symbols[a], x, y, z, symbols[a].upper())) fileobj.write('ENDMDL\n') return fileobj
def write_eon(fileobj, images): """Writes structure to EON reactant.con file Multiple snapshots are not allowed.""" if isinstance(fileobj, str): f = paropen(fileobj, 'w') else: f = fileobj if isinstance(images, Atoms): atoms = images elif len(images) == 1: atoms = images[0] else: raise ValueError('Can only write one configuration to EON ' 'reactant.con file') out = [] out.append(atoms.info.get('comment', 'Generated by ASE')) out.append('0.0000 TIME') # ?? a, b, c, alpha, beta, gamma = cell_to_cellpar(atoms.cell) out.append('%-10.6f %-10.6f %-10.6f' % (a, b, c)) out.append('%-10.6f %-10.6f %-10.6f' % (gamma, beta, alpha)) out.append('0 0') # ?? out.append('0 0 0') # ?? symbols = atoms.get_chemical_symbols() massdict = dict(list(zip(symbols, atoms.get_masses()))) atomtypes = sorted(massdict.keys()) atommasses = [massdict[at] for at in atomtypes] natoms = [symbols.count(at) for at in atomtypes] ntypes = len(atomtypes) out.append(str(ntypes)) out.append(' '.join([str(n) for n in natoms])) out.append(' '.join([str(n) for n in atommasses])) atom_id = 0 for n in range(ntypes): fixed = np.array([False] * len(atoms)) out.append(atomtypes[n]) out.append('Coordinates of Component %d' % (n + 1)) indices = [i for i, at in enumerate(symbols) if at == atomtypes[n]] a = atoms[indices] coords = a.positions for c in a.constraints: if not isinstance(c, FixAtoms): warn('Only FixAtoms constraints are supported by con files. ' 'Dropping %r', c) continue if c.index.dtype.kind == 'b': fixed = np.array(c.index, dtype=int) else: fixed = np.zeros((natoms[n], ), dtype=int) for i in c.index: fixed[i] = 1 for xyz, fix in zip(coords, fixed): out.append('%22.17f %22.17f %22.17f %d %4d' % (tuple(xyz) + (fix, atom_id))) atom_id += 1 f.write('\n'.join(out)) f.write('\n') if isinstance(fileobj, str): f.close()
# -v option if args.convergence: if calc.convergence: output_lines += str(calc.convergence) + "\n" if calc.tresholds: for i in range(len(calc.tresholds)): try: ncycles = calc.ncycles[i] except IndexError: ncycles = "" output_lines += "%1.2e" % calc.tresholds[i][0] + " "*2 + "%1.5f" % calc.tresholds[i][1] + " "*2 + "%1.4f" % calc.tresholds[i][2] + " "*2 + "%1.4f" % calc.tresholds[i][3] + " "*2 + "E=" + "%1.4f" % calc.tresholds[i][4] + " eV" + " "*2 + "(%s)" % ncycles + "\n" # -s option if args.structures: out = '' if len(calc.structures) > 1: out += str(cell_to_cellpar(calc.structures[0].cell)) + " V=%2.2f" % (abs(det(calc.structures[0].cell))) + ' -> ' out += str(cell_to_cellpar(calc.structures[-1].cell)) out += " V=%2.2f\n" % calc.info['dims'] for i in calc.structures[-1]: out += " %s %s %s %s\n" % (i.symbol, i.x, i.y, i.z) output_lines += out # -c option if args.cif: try: calc.structures[ args.cif ] except IndexError: output_lines += "Warning! Structure "+args.cif+" not found!" + "\n" else: N = args.cif if args.cif>0 else len(calc.structures) + 1 + args.cif comment = calc.info['formula'] + " extracted from " + task + " (structure N " + str(N) + ")" cif_file = os.path.realpath(os.path.abspath(DATA_DIR + os.sep + os.path.basename(task))) + '_' + str(args.cif) + '.cif' if write_cif(cif_file, calc.structures[ args.cif ], comment):
def get_special_points(lattice, cell, eps=1e-4): """Return dict of special points. The definitions are from a paper by Wahyu Setyawana and Stefano Curtarolo:: http://dx.doi.org/10.1016/j.commatsci.2010.05.010 lattice: str One of the following: cubic, fcc, bcc, orthorhombic, tetragonal, hexagonal or monoclinic. cell: 3x3 ndarray Unit cell. eps: float Tolerance for cell-check. """ lattice = lattice.lower() cellpar = cell_to_cellpar(cell=cell) abc = cellpar[:3] angles = cellpar[3:] / 180 * pi a, b, c = abc alpha, beta, gamma = angles # Check that the unit-cells are as in the Setyawana-Curtarolo paper: if lattice == 'cubic': assert abc.ptp() < eps and abs(angles - pi / 2).max() < eps elif lattice == 'fcc': assert abc.ptp() < eps and abs(angles - pi / 3).max() < eps elif lattice == 'bcc': angle = np.arccos(-1 / 3) assert abc.ptp() < eps and abs(angles - angle).max() < eps elif lattice == 'tetragonal': assert abs(a - b) < eps and abs(angles - pi / 2).max() < eps elif lattice == 'orthorhombic': assert abs(angles - pi / 2).max() < eps elif lattice == 'hexagonal': assert abs(a - b) < eps assert abs(gamma - pi / 3 * 2) < eps assert abs(angles[:2] - pi / 2).max() < eps elif lattice == 'monoclinic': assert c >= a and c >= b assert alpha < pi / 2 and abs(angles[1:] - pi / 2).max() < eps if lattice != 'monoclinic': return special_points[lattice] # Here, we need the cell: eta = (1 - b * cos(alpha) / c) / (2 * sin(alpha)**2) nu = 1 / 2 - eta * c * cos(alpha) / b return { 'G': [0, 0, 0], 'A': [1 / 2, 1 / 2, 0], 'C': [0, 1 / 2, 1 / 2], 'D': [1 / 2, 0, 1 / 2], 'D1': [1 / 2, 0, -1 / 2], 'E': [1 / 2, 1 / 2, 1 / 2], 'H': [0, eta, 1 - nu], 'H1': [0, 1 - eta, nu], 'H2': [0, eta, -nu], 'M': [1 / 2, eta, 1 - nu], 'M1': [1 / 2, 1 - eta, nu], 'M2': [1 / 2, eta, -nu], 'X': [0, 1 / 2, 0], 'Y': [0, 0, 1 / 2], 'Y1': [0, 0, -1 / 2], 'Z': [1 / 2, 0, 0] }
def get_special_points(lattice, cell, eps=2e-4): """Return dict of special points. The definitions are from a paper by Wahyu Setyawana and Stefano Curtarolo:: http://dx.doi.org/10.1016/j.commatsci.2010.05.010 lattice: str One of the following: cubic, fcc, bcc, orthorhombic, tetragonal, hexagonal or monoclinic. cell: 3x3 ndarray Unit cell. eps: float Tolerance for cell-check. """ lattice = lattice.lower() cellpar = cell_to_cellpar(cell=cell) abc = cellpar[:3] angles = cellpar[3:] / 180 * pi a, b, c = abc alpha, beta, gamma = angles # Check that the unit-cells are as in the Setyawana-Curtarolo paper: if lattice == 'cubic': assert abc.ptp() < eps and abs(angles - pi / 2).max() < eps elif lattice == 'fcc': assert abc.ptp() < eps and abs(angles - pi / 3).max() < eps elif lattice == 'bcc': angle = np.arccos(-1 / 3) assert abc.ptp() < eps and abs(angles - angle).max() < eps elif lattice == 'tetragonal': assert abs(a - b) < eps and abs(angles - pi / 2).max() < eps elif lattice == 'orthorhombic': assert abs(angles - pi / 2).max() < eps elif lattice == 'hexagonal': assert abs(a - b) < eps assert abs(gamma - pi / 3 * 2) < eps assert abs(angles[:2] - pi / 2).max() < eps elif lattice == 'monoclinic': assert c >= a and c >= b assert alpha < pi / 2 and abs(angles[1:] - pi / 2).max() < eps elif lattice == 'rhombohedral type 1': assert abc.ptp() < eps and angles.ptp() < eps assert abs(alpha) < pi / 2 if lattice == 'monoclinic': # Here, we need the cell: eta = (1 - b * cos(alpha) / c) / (2 * sin(alpha)**2) nu = 1 / 2 - eta * c * cos(alpha) / b return {'G': [0, 0, 0], 'A': [1 / 2, 1 / 2, 0], 'C': [0, 1 / 2, 1 / 2], 'D': [1 / 2, 0, 1 / 2], 'D1': [1 / 2, 0, -1 / 2], 'E': [1 / 2, 1 / 2, 1 / 2], 'H': [0, eta, 1 - nu], 'H1': [0, 1 - eta, nu], 'H2': [0, eta, -nu], 'M': [1 / 2, eta, 1 - nu], 'M1': [1 / 2, 1 - eta, nu], 'M2': [1 / 2, eta, -nu], 'X': [0, 1 / 2, 0], 'Y': [0, 0, 1 / 2], 'Y1': [0, 0, -1 / 2], 'Z': [1 / 2, 0, 0]} elif lattice == 'rhombohedral type 1': eta = (1 + 4 * np.cos(alpha)) / (2 + 4 * np.cos(alpha)) nu = 3 / 4 - eta / 2 return {'G': [0, 0, 0], 'B': [eta, 1 / 2, 1 - eta], 'B1': [1 / 2, 1 - eta, eta - 1], 'F': [1 / 2, 1 / 2, 0], 'L': [1 / 2, 0, 0], 'L1': [0, 0, - 1 / 2], 'P': [eta, nu, nu], 'P1': [1 - nu, 1 - nu, 1 - eta], 'P2': [nu, nu, eta - 1], 'Q': [1 - nu, nu, 0], 'X': [nu, 0, -nu], 'Z': [0.5, 0.5, 0.5]} else: return special_points[lattice]
def write(self, atoms=None, frame=None, arrays=None, time=None): """ Write the atoms to the file. If the atoms argument is not given, the atoms object specified when creating the trajectory object is used. """ self._open() self._call_observers(self.pre_observers) if atoms is None: atoms = self.atoms if hasattr(atoms, 'interpolate'): # seems to be a NEB neb = atoms assert not neb.parallel try: neb.get_energies_and_forces(all=True) except AttributeError: pass for image in neb.images: self.write(image) return if not self.has_header: self._define_file_structure(atoms) else: if len(atoms) != self.n_atoms: raise ValueError('Bad number of atoms!') if frame is None: i = self.frame else: i = frame # Number can be per file variable numbers = self._get_variable(self._numbers_var) if numbers.dimensions[0] == self._frame_dim: numbers[i] = atoms.get_atomic_numbers() else: if np.any(numbers != atoms.get_atomic_numbers()): raise ValueError('Atomic numbers do not match!') self._get_variable(self._positions_var)[i] = atoms.get_positions() if atoms.has('momenta'): self._add_velocities() self._get_variable(self._velocities_var)[i] = \ atoms.get_momenta() / atoms.get_masses().reshape(-1, 1) a, b, c, alpha, beta, gamma = cell_to_cellpar(atoms.get_cell()) cell_lengths = np.array([a, b, c]) * atoms.pbc self._get_variable(self._cell_lengths_var)[i] = cell_lengths self._get_variable(self._cell_angles_var)[i] = [alpha, beta, gamma] if arrays is not None: for array in arrays: data = atoms.get_array(array) if array in self.extra_per_file_vars: # This field exists but is per file data. Check that the # data remains consistent. if np.any(self._get_variable(array) != data): raise ValueError('Trying to write Atoms object with ' 'incompatible data for the {0} ' 'array.'.format(array)) else: self._add_array(atoms, array, data.dtype, data.shape) self._get_variable(array)[i] = data if time is not None: self._add_time() self._get_variable(self._time_var)[i] = time self.sync() self._call_observers(self.post_observers) self.frame += 1 self._close()
def classify(self, calc, symprec=None): ''' Reasons on normalization, invokes hierarchy API and prepares calc for saving NB: this is the PUBLIC method @returns tilde_obj, error ''' error = None symbols = calc.structures[-1].get_chemical_symbols() calc.info['formula'] = self.formula(symbols) calc.info['cellpar'] = cell_to_cellpar( calc.structures[-1].cell).tolist() if calc.info['input']: try: calc.info['input'] = str(calc.info['input'], errors='ignore') except: pass # applying filter: todo if (calc.info['finished'] == 0x1 and self.settings['skip_unfinished']) or \ (not calc.info['energy'] and self.settings['skip_notenergy']): return None, 'data do not satisfy the active filter' # naive elements extraction fragments = re.findall(r'([A-Z][a-z]?)(\d*[?:.\d+]*)?', calc.info['formula']) for i in fragments: if i[0] == 'X': continue calc.info['elements'].append(i[0]) calc.info['contents'].append(int( i[1])) if i[1] else calc.info['contents'].append(1) # extend hierarchy with modules for C_obj in self.Classifiers: try: calc = C_obj['classify'](calc) except: exc_type, exc_value, exc_tb = sys.exc_info() error = "Fatal error during classification:\n %s" % "".join( traceback.format_exception(exc_type, exc_value, exc_tb)) return None, error # chemical ratios if not len(calc.info['standard']): if len(calc.info['elements']) == 1: calc.info['expanded'] = 1 if not calc.info['expanded']: calc.info['expanded'] = reduce(gcd, calc.info['contents']) for n, i in enumerate( [x // calc.info['expanded'] for x in calc.info['contents']]): if i == 1: calc.info['standard'] += calc.info['elements'][n] else: calc.info['standard'] += calc.info['elements'][n] + str(i) if not calc.info['expanded']: del calc.info['expanded'] calc.info['nelem'] = len(calc.info['elements']) if calc.info['nelem'] > 13: calc.info['nelem'] = 13 calc.info['natom'] = len(symbols) # periodicity if calc.info['periodicity'] == 0: calc.info['periodicity'] = 0x4 elif calc.info['periodicity'] == -1: calc.info['periodicity'] = 0x5 # general calculation type reasoning if (calc.structures[-1].get_initial_charges() != 0).sum(): calc.info['calctypes'].append( 0x4) # numpy count_nonzero implementation if (calc.structures[-1].get_initial_magnetic_moments() != 0).sum(): calc.info['calctypes'].append(0x5) if calc.phonons['modes']: calc.info['calctypes'].append(0x6) if calc.phonons['ph_k_degeneracy']: calc.info['calctypes'].append(0x7) if calc.phonons['dielectric_tensor']: calc.info['calctypes'].append(0x8) # CRYSTAL-only! if len(calc.tresholds) > 1: calc.info['calctypes'].append(0x3) calc.info['optgeom'] = True if calc.electrons['dos'] or calc.electrons['bands']: calc.info['calctypes'].append(0x2) if calc.info['energy']: calc.info['calctypes'].append(0x1) calc.info['spin'] = 0x2 if calc.info['spin'] else 0x1 # TODO: standardize if 'vac' in calc.info: if 'X' in symbols: calc.info['techs'].append('vacancy defect: ghost') else: calc.info['techs'].append('vacancy defect: void space') calc.info['lata'] = round(calc.info['cellpar'][0], 3) calc.info['latb'] = round(calc.info['cellpar'][1], 3) calc.info['latc'] = round(calc.info['cellpar'][2], 3) calc.info['latalpha'] = round(calc.info['cellpar'][3], 2) calc.info['latbeta'] = round(calc.info['cellpar'][4], 2) calc.info['latgamma'] = round(calc.info['cellpar'][5], 2) # invoke symmetry finder found = SymmetryHandler(calc, symprec) if found.error: return None, found.error calc.info['sg'] = found.i calc.info['ng'] = found.n calc.info['symmetry'] = found.symmetry calc.info['spg'] = "%s — %s" % (found.n, found.i) calc.info['pg'] = found.pg calc.info['dg'] = found.dg # phonons if calc.phonons['dfp_magnitude']: calc.info['dfp_magnitude'] = round(calc.phonons['dfp_magnitude'], 3) if calc.phonons['dfp_disps']: calc.info['dfp_disps'] = len(calc.phonons['dfp_disps']) if calc.phonons['modes']: calc.info['n_ph_k'] = len( calc.phonons['ph_k_degeneracy'] ) if calc.phonons['ph_k_degeneracy'] else 1 #calc.info['rgkmax'] = calc.electrons['rgkmax'] # LAPW # electronic properties reasoning by bands if calc.electrons['bands']: if calc.electrons['bands'].is_conductor(): calc.info['etype'] = 0x2 calc.info['bandgap'] = 0.0 calc.info['bandgaptype'] = 0x1 else: try: gap, is_direct = calc.electrons['bands'].get_bandgap() except ElectronStructureError as e: calc.electrons['bands'] = None calc.warning(e.value) else: calc.info['etype'] = 0x1 calc.info['bandgap'] = round(gap, 2) calc.info['bandgaptype'] = 0x2 if is_direct else 0x3 # electronic properties reasoning by DOS if calc.electrons['dos']: try: gap = round(calc.electrons['dos'].get_bandgap(), 2) except ElectronStructureError as e: calc.electrons['dos'] = None calc.warning(e.value) else: if calc.electrons['bands']: # check coincidence if abs(calc.info['bandgap'] - gap) > 0.2: calc.warning( 'Bans gaps in DOS and bands data differ considerably! The latter will be considered.' ) else: calc.info['bandgap'] = gap if gap: calc.info['etype'] = 0x1 else: calc.info['etype'] = 0x2 calc.info['bandgaptype'] = 0x1 # TODO: beware to add something new to an existing item! # TODO2: unknown or absent? for entity in self.hierarchy: if entity['creates_topic'] and not entity[ 'optional'] and not calc.info.get(entity['source']): if entity['enumerated']: calc.info[entity['source']] = [ 0x0 ] if entity['multiple'] else 0x0 else: calc.info[entity['source']] = [ 'none' ] if entity['multiple'] else 'none' calc.benchmark() # this call must be at the very end of parsing return calc, error
def save(self, calc, session): ''' Saves tilde_obj into the database NB: this is the PUBLIC method @returns checksum, error ''' checksum = calc.get_checksum() try: existing_calc = session.query(model.Calculation).filter( model.Calculation.checksum == checksum).one() except NoResultFound: pass else: del calc return None, "This calculation already exists!" if not calc.download_size: for f in calc.related_files: calc.download_size += os.stat(f).st_size ormcalc = model.Calculation(checksum=checksum) if calc._calcset: ormcalc.meta_data = model.Metadata( chemical_formula=calc.info['standard'], download_size=calc.download_size) for child in session.query(model.Calculation).filter( model.Calculation.checksum.in_(calc._calcset)).all(): ormcalc.children.append(child) ormcalc.siblings_count = len(ormcalc.children) ormcalc.nested_depth = calc._nested_depth else: # prepare phonon data for saving # this is actually a dict to list conversion TODO re-structure this if calc.phonons['modes']: phonons_json = [] for bzpoint, frqset in calc.phonons['modes'].items(): # re-orientate eigenvectors for i in range(0, len(calc.phonons['ph_eigvecs'][bzpoint])): for j in range( 0, len(calc.phonons['ph_eigvecs'][bzpoint][i]) // 3): eigv = array([ calc.phonons['ph_eigvecs'][bzpoint][i][j * 3], calc.phonons['ph_eigvecs'][bzpoint][i][j * 3 + 1], calc.phonons['ph_eigvecs'][bzpoint][i][j * 3 + 2] ]) R = dot(eigv, calc.structures[-1].cell).tolist() calc.phonons['ph_eigvecs'][bzpoint][i][ j * 3], calc.phonons['ph_eigvecs'][bzpoint][i][ j * 3 + 1], calc.phonons['ph_eigvecs'][bzpoint][i][ j * 3 + 2] = [round(x, 3) for x in R] try: irreps = calc.phonons['irreps'][bzpoint] except KeyError: empty = [] for i in range(len(frqset)): empty.append('') irreps = empty phonons_json.append({ 'bzpoint': bzpoint, 'freqs': frqset, 'irreps': irreps, 'ph_eigvecs': calc.phonons['ph_eigvecs'][bzpoint] }) if bzpoint == '0 0 0': phonons_json[-1]['ir_active'] = calc.phonons[ 'ir_active'] phonons_json[-1]['raman_active'] = calc.phonons[ 'raman_active'] if calc.phonons['ph_k_degeneracy']: phonons_json[-1]['ph_k_degeneracy'] = calc.phonons[ 'ph_k_degeneracy'][bzpoint] ormcalc.phonons = model.Phonons() ormcalc.spectra.append( model.Spectra(kind=model.Spectra.PHONON, eigenvalues=json.dumps(phonons_json))) # prepare electron data for saving TODO re-structure this for i in ['dos', 'bands']: # projected? if calc.electrons[i]: calc.electrons[i] = calc.electrons[i].todict() if calc.electrons['dos'] or calc.electrons['bands']: ormcalc.electrons = model.Electrons(gap=calc.info['bandgap']) if 'bandgaptype' in calc.info: ormcalc.electrons.is_direct = 1 if calc.info[ 'bandgaptype'] == 'direct' else -1 ormcalc.spectra.append( model.Spectra( kind=model.Spectra.ELECTRON, dos=json.dumps(calc.electrons['dos']), bands=json.dumps(calc.electrons['bands']), projected=json.dumps(calc.electrons['projected']), eigenvalues=json.dumps(calc.electrons['eigvals']))) # construct ORM for other props calc.related_files = list(map(virtualize_path, calc.related_files)) ormcalc.meta_data = model.Metadata( location=calc.info['location'], finished=calc.info['finished'], raw_input=calc.info['input'], modeling_time=calc.info['duration'], chemical_formula=html_formula(calc.info['standard']), download_size=calc.download_size, filenames=json.dumps(calc.related_files)) codefamily = model.Codefamily.as_unique( session, content=calc.info['framework']) codeversion = model.Codeversion.as_unique( session, content=calc.info['prog']) codeversion.instances.append(ormcalc.meta_data) codefamily.versions.append(codeversion) pot = model.Pottype.as_unique(session, name=calc.info['H']) pot.instances.append(ormcalc) ormcalc.recipinteg = model.Recipinteg( kgrid=calc.info['k'], kshift=calc.info['kshift'], smearing=calc.info['smear'], smeartype=calc.info['smeartype']) ormcalc.basis = model.Basis( kind=calc.info['ansatz'], content=json.dumps(calc.electrons['basis_set']) if calc.electrons['basis_set'] else None) ormcalc.energy = model.Energy(convergence=json.dumps( calc.convergence), total=calc.info['energy']) ormcalc.spacegroup = model.Spacegroup(n=calc.info['ng']) ormcalc.struct_ratios = model.Struct_ratios( chemical_formula=calc.info['standard'], formula_units=calc.info['expanded'], nelem=calc.info['nelem'], dimensions=calc.info['dims']) if len(calc.tresholds) > 1: ormcalc.struct_optimisation = model.Struct_optimisation( tresholds=json.dumps(calc.tresholds), ncycles=json.dumps(calc.ncycles)) for n, ase_repr in enumerate(calc.structures): is_final = True if n == len(calc.structures) - 1 else False struct = model.Structure(step=n, final=is_final) s = cell_to_cellpar(ase_repr.cell) struct.lattice = model.Lattice(a=s[0], b=s[1], c=s[2], alpha=s[3], beta=s[4], gamma=s[5], a11=ase_repr.cell[0][0], a12=ase_repr.cell[0][1], a13=ase_repr.cell[0][2], a21=ase_repr.cell[1][0], a22=ase_repr.cell[1][1], a23=ase_repr.cell[1][2], a31=ase_repr.cell[2][0], a32=ase_repr.cell[2][1], a33=ase_repr.cell[2][2]) #rmts = ase_repr.get_array('rmts') if 'rmts' in ase_repr.arrays else [None for j in range(len(ase_repr))] charges = ase_repr.get_array( 'charges') if 'charges' in ase_repr.arrays else [ None for j in range(len(ase_repr)) ] magmoms = ase_repr.get_array( 'magmoms') if 'magmoms' in ase_repr.arrays else [ None for j in range(len(ase_repr)) ] for n, i in enumerate(ase_repr): struct.atoms.append( model.Atom(number=chemical_symbols.index(i.symbol), x=i.x, y=i.y, z=i.z, charge=charges[n], magmom=magmoms[n])) ormcalc.structures.append(struct) # TODO Forces ormcalc.uigrid = model.Grid(info=json.dumps(calc.info)) # tags ORM uitopics = [] for entity in self.hierarchy: if not entity['creates_topic']: continue if entity['multiple'] or calc._calcset: for item in calc.info.get(entity['source'], []): uitopics.append(model.topic(cid=entity['cid'], topic=item)) else: topic = calc.info.get(entity['source']) if topic or not entity['optional']: uitopics.append(model.topic(cid=entity['cid'], topic=topic)) uitopics = [ model.Topic.as_unique(session, cid=x.cid, topic="%s" % x.topic) for x in uitopics ] ormcalc.uitopics.extend(uitopics) if calc._calcset: session.add(ormcalc) else: session.add_all([codefamily, codeversion, pot, ormcalc]) session.commit() del calc, ormcalc return checksum, None
def __init__(self, tilde_obj, accuracy=None): self.i = None self.n = None self.symmetry = None self.pg = None self.dg = None SymmetryFinder.__init__(self, accuracy) SymmetryFinder.get_spacegroup(self, tilde_obj) # Data below are taken from Table 2.3 of the book # Robert A. Evarestov, Quantum Chemistry of Solids, # LCAO Treatment of Crystals and Nanostructures, 2nd Edition, # Springer, 2012, http://dx.doi.org/10.1007/978-3-642-30356-2 # NB 7 crystal systems != 7 lattice systems # space group to crystal system conversion if 195 <= self.n <= 230: self.symmetry = 'cubic' elif 168 <= self.n <= 194: self.symmetry = 'hexagonal' elif 143 <= self.n <= 167: self.symmetry = 'trigonal' elif 75 <= self.n <= 142: self.symmetry = 'tetragonal' elif 16 <= self.n <= 74: self.symmetry = 'orthorhombic' elif 3 <= self.n <= 15: self.symmetry = 'monoclinic' elif 1 <= self.n <= 2: self.symmetry = 'triclinic' # space group to point group conversion if 221 <= self.n <= 230: self.pg = 'O<sub>h</sub>' elif 215 <= self.n <= 220: self.pg = 'T<sub>d</sub>' elif 207 <= self.n <= 214: self.pg = 'O' elif 200 <= self.n <= 206: self.pg = 'T<sub>h</sub>' elif 195 <= self.n <= 199: self.pg = 'T' elif 191 <= self.n <= 194: self.pg = 'D<sub>6h</sub>' elif 187 <= self.n <= 190: self.pg = 'D<sub>3h</sub>' elif 183 <= self.n <= 186: self.pg = 'C<sub>6v</sub>' elif 177 <= self.n <= 182: self.pg = 'D<sub>6</sub>' elif 175 <= self.n <= 176: self.pg = 'C<sub>6h</sub>' elif self.n == 174: self.pg = 'C<sub>3h</sub>' elif 168 <= self.n <= 173: self.pg = 'C<sub>6</sub>' elif 162 <= self.n <= 167: self.pg = 'D<sub>3d</sub>' elif 156 <= self.n <= 161: self.pg = 'C<sub>3v</sub>' elif 149 <= self.n <= 155: self.pg = 'D<sub>3</sub>' elif 147 <= self.n <= 148: self.pg = 'C<sub>3i</sub>' elif 143 <= self.n <= 146: self.pg = 'C<sub>3</sub>' elif 123 <= self.n <= 142: self.pg = 'D<sub>4h</sub>' elif 111 <= self.n <= 122: self.pg = 'D<sub>2d</sub>' elif 99 <= self.n <= 110: self.pg = 'C<sub>4v</sub>' elif 89 <= self.n <= 98: self.pg = 'D<sub>4</sub>' elif 83 <= self.n <= 88: self.pg = 'C<sub>4h</sub>' elif 81 <= self.n <= 82: self.pg = 'S<sub>4</sub>' elif 75 <= self.n <= 80: self.pg = 'C<sub>4</sub>' elif 47 <= self.n <= 74: self.pg = 'D<sub>2h</sub>' elif 25 <= self.n <= 46: self.pg = 'C<sub>2v</sub>' elif 16 <= self.n <= 24: self.pg = 'D<sub>2</sub>' elif 10 <= self.n <= 15: self.pg = 'C<sub>2h</sub>' elif 6 <= self.n <= 9: self.pg = 'C<sub>s</sub>' elif 3 <= self.n <= 5: self.pg = 'C<sub>2</sub>' elif self.n == 2: self.pg = 'C<sub>i</sub>' elif self.n == 1: self.pg = 'C<sub>1</sub>' # space group to layer group conversion if tilde_obj.structures[-1].periodicity == 2: if self.n in [25, 26, 28, 51]: tilde_obj.warning('Warning! Diperiodical group setting is undefined!') DIPERIODIC_MAPPING = {3:8, 4:9, 5:10, 6:11, 7:12, 8:13, 10:14, 11:15, 12:16, 13:17, 14:18, 16:19, 17:20, 18:21, 21:22, 25:23, 25:24, 26:25, 26:26, 27:27, 28:28, 28:29, 29:30, 30:31, 31:32, 32:33, 35:34, 38:35, 39:36, 47:37, 49:38, 50:39, 51:40, 51:41, 53:42, 54:43, 55:44, 57:45, 59:46, 65:47, 67:48, 75:49, 81:50, 83:51, 85:52, 89:53, 90:54, 99:55, 100:56, 111:57, 113:58, 115:59, 117:60, 123:61, 125:62, 127:63, 129:64, 143:65, 147:66, 149:67, 150:68, 156:69, 157:70, 162:71, 164:72, 168:73, 174:74, 175:75, 177:76, 183:77, 187:78, 189:79, 191:80} cellpar = cell_to_cellpar( tilde_obj.structures[-1].cell ).tolist() if cellpar[3] != 90 or cellpar[4] != 90 or cellpar[5] != 90: DIPERIODIC_MAPPING.update({1:1, 2:2, 3:3, 6:4, 7:5, 10:6, 13:7}) try: self.dg = DIPERIODIC_MAPPING[self.n] except KeyError: tilde_obj.warning('No diperiodical group found because rotational axes inconsistent with 2d translations!') else: if 65 <= self.dg <= 80: self.symmetry = '2d-hexagonal' elif 49 <= self.dg <= 64: self.symmetry = '2d-square' elif 8 <= self.dg <= 48: self.symmetry = '2d-rectangular' elif 1 <= self.dg <= 7: self.symmetry = '2d-oblique'
def save(self, calc, session): ''' Saves tilde_obj into the database NB: this is the PUBLIC method @returns checksum, error ''' checksum = calc.get_checksum() try: existing_calc = session.query(model.Calculation).filter(model.Calculation.checksum == checksum).one() except NoResultFound: pass else: del calc return None, "This calculation already exists!" if not calc.download_size: for f in calc.related_files: calc.download_size += os.stat(f).st_size ormcalc = model.Calculation(checksum = checksum) if calc._calcset: ormcalc.meta_data = model.Metadata(chemical_formula = calc.info['standard'], download_size = calc.download_size) for child in session.query(model.Calculation).filter(model.Calculation.checksum.in_(calc._calcset)).all(): ormcalc.children.append(child) ormcalc.siblings_count = len(ormcalc.children) ormcalc.nested_depth = calc._nested_depth else: # prepare phonon data for saving # this is actually a dict to list conversion TODO re-structure this if calc.phonons['modes']: phonons_json = [] for bzpoint, frqset in calc.phonons['modes'].items(): # re-orientate eigenvectors for i in range(0, len(calc.phonons['ph_eigvecs'][bzpoint])): for j in range(0, len(calc.phonons['ph_eigvecs'][bzpoint][i])//3): eigv = array([calc.phonons['ph_eigvecs'][bzpoint][i][j*3], calc.phonons['ph_eigvecs'][bzpoint][i][j*3+1], calc.phonons['ph_eigvecs'][bzpoint][i][j*3+2]]) R = dot( eigv, calc.structures[-1].cell ).tolist() calc.phonons['ph_eigvecs'][bzpoint][i][j*3], calc.phonons['ph_eigvecs'][bzpoint][i][j*3+1], calc.phonons['ph_eigvecs'][bzpoint][i][j*3+2] = [round(x, 3) for x in R] try: irreps = calc.phonons['irreps'][bzpoint] except KeyError: empty = [] for i in range(len(frqset)): empty.append('') irreps = empty phonons_json.append({ 'bzpoint':bzpoint, 'freqs':frqset, 'irreps':irreps, 'ph_eigvecs':calc.phonons['ph_eigvecs'][bzpoint] }) if bzpoint == '0 0 0': phonons_json[-1]['ir_active'] = calc.phonons['ir_active'] phonons_json[-1]['raman_active'] = calc.phonons['raman_active'] if calc.phonons['ph_k_degeneracy']: phonons_json[-1]['ph_k_degeneracy'] = calc.phonons['ph_k_degeneracy'][bzpoint] ormcalc.phonons = model.Phonons() ormcalc.spectra.append( model.Spectra(kind = model.Spectra.PHONON, eigenvalues = json.dumps(phonons_json)) ) # prepare electron data for saving TODO re-structure this for task in ['dos', 'bands']: # projected? if calc.electrons[task]: calc.electrons[task] = calc.electrons[task].todict() if calc.electrons['dos'] or calc.electrons['bands']: ormcalc.electrons = model.Electrons(gap = calc.info['bandgap']) if 'bandgaptype' in calc.info: ormcalc.electrons.is_direct = 1 if calc.info['bandgaptype'] == 'direct' else -1 ormcalc.spectra.append(model.Spectra( kind = model.Spectra.ELECTRON, dos = json.dumps(calc.electrons['dos']), bands = json.dumps(calc.electrons['bands']), projected = json.dumps(calc.electrons['projected']), eigenvalues = json.dumps(calc.electrons['eigvals']) )) # construct ORM for other props calc.related_files = list(map(virtualize_path, calc.related_files)) ormcalc.meta_data = model.Metadata(location = calc.info['location'], finished = calc.info['finished'], raw_input = calc.info['input'], modeling_time = calc.info['duration'], chemical_formula = html_formula(calc.info['standard']), download_size = calc.download_size, filenames = json.dumps(calc.related_files)) codefamily = model.Codefamily.as_unique(session, content = calc.info['framework']) codeversion = model.Codeversion.as_unique(session, content = calc.info['prog']) codeversion.instances.append( ormcalc.meta_data ) codefamily.versions.append( codeversion ) pot = model.Pottype.as_unique(session, name = calc.info['H']) pot.instances.append(ormcalc) ormcalc.recipinteg = model.Recipinteg(kgrid = calc.info['k'], kshift = calc.info['kshift'], smearing = calc.info['smear'], smeartype = calc.info['smeartype']) ormcalc.basis = model.Basis(kind = calc.info['ansatz'], content = json.dumps(calc.electrons['basis_set']) if calc.electrons['basis_set'] else None) ormcalc.energy = model.Energy(convergence = json.dumps(calc.convergence), total = calc.info['energy']) ormcalc.spacegroup = model.Spacegroup(n=calc.info['ng']) ormcalc.struct_ratios = model.Struct_ratios(chemical_formula=calc.info['standard'], formula_units=calc.info['expanded'], nelem=calc.info['nelem'], dimensions=calc.info['dims']) if len(calc.tresholds) > 1: ormcalc.struct_optimisation = model.Struct_optimisation(tresholds=json.dumps(calc.tresholds), ncycles=json.dumps(calc.ncycles)) for n, ase_repr in enumerate(calc.structures): is_final = True if n == len(calc.structures)-1 else False struct = model.Structure(step = n, final = is_final) s = cell_to_cellpar(ase_repr.cell) struct.lattice = model.Lattice(a=s[0], b=s[1], c=s[2], alpha=s[3], beta=s[4], gamma=s[5], a11=ase_repr.cell[0][0], a12=ase_repr.cell[0][1], a13=ase_repr.cell[0][2], a21=ase_repr.cell[1][0], a22=ase_repr.cell[1][1], a23=ase_repr.cell[1][2], a31=ase_repr.cell[2][0], a32=ase_repr.cell[2][1], a33=ase_repr.cell[2][2]) #rmts = ase_repr.get_array('rmts') if 'rmts' in ase_repr.arrays else [None for j in range(len(ase_repr))] charges = ase_repr.get_array('charges') if 'charges' in ase_repr.arrays else [None for j in range(len(ase_repr))] magmoms = ase_repr.get_array('magmoms') if 'magmoms' in ase_repr.arrays else [None for j in range(len(ase_repr))] for n, i in enumerate(ase_repr): struct.atoms.append( model.Atom( number=chemical_symbols.index(i.symbol), x=i.x, y=i.y, z=i.z, charge=charges[n], magmom=magmoms[n] ) ) ormcalc.structures.append(struct) # TODO Forces ormcalc.uigrid = model.Grid(info=json.dumps(calc.info)) # tags ORM uitopics = [] for entity in self.hierarchy: if not entity['creates_topic']: continue if entity['multiple'] or calc._calcset: for item in calc.info.get( entity['source'], [] ): uitopics.append( model.topic(cid=entity['cid'], topic=item) ) else: topic = calc.info.get(entity['source']) if topic or not entity['optional']: uitopics.append( model.topic(cid=entity['cid'], topic=topic) ) uitopics = [model.Topic.as_unique(session, cid=x.cid, topic="%s" % x.topic) for x in uitopics] ormcalc.uitopics.extend(uitopics) if calc._calcset: session.add(ormcalc) else: session.add_all([codefamily, codeversion, pot, ormcalc]) session.commit() del calc, ormcalc return checksum, None
def get_cellinfo(cell, lattice=None, eps=2e-4): from ase.build.tools import niggli_reduce_cell rcell, M = niggli_reduce_cell(cell) latt = crystal_structure_from_cell(rcell, niggli_reduce=False) if lattice: assert latt == lattice.lower(), latt if latt == 'monoclinic': # Transform From Niggli to Setyawana-Curtarolo cell: a, b, c, alpha, beta, gamma = cell_to_cellpar(rcell, radians=True) if abs(beta - np.pi / 2) > eps: T = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1]]) scell = np.dot(T, rcell) elif abs(gamma - np.pi / 2) > eps: T = np.array([[0, 0, 1], [1, 0, 0], [0, -1, 0]]) else: raise ValueError('You are using a badly oriented ' + 'monoclinic unit cell. Please choose one with ' + 'either beta or gamma != pi/2') scell = np.dot(np.dot(T, rcell), T.T) a, b, c, alpha, beta, gamma = cell_to_cellpar(scell, radians=True) assert alpha < np.pi / 2, 'Your monoclinic angle has to be < pi / 2' M = np.dot(M, T.T) eta = (1 - b * cos(alpha) / c) / (2 * sin(alpha)**2) nu = 1 / 2 - eta * c * cos(alpha) / b points = { 'G': [0, 0, 0], 'A': [1 / 2, 1 / 2, 0], 'C': [0, 1 / 2, 1 / 2], 'D': [1 / 2, 0, 1 / 2], 'D1': [1 / 2, 0, -1 / 2], 'E': [1 / 2, 1 / 2, 1 / 2], 'H': [0, eta, 1 - nu], 'H1': [0, 1 - eta, nu], 'H2': [0, eta, -nu], 'M': [1 / 2, eta, 1 - nu], 'M1': [1 / 2, 1 - eta, nu], 'M2': [1 / 2, eta, -nu], 'X': [0, 1 / 2, 0], 'Y': [0, 0, 1 / 2], 'Y1': [0, 0, -1 / 2], 'Z': [1 / 2, 0, 0] } elif latt == 'rhombohedral type 1': a, b, c, alpha, beta, gamma = cell_to_cellpar(cell=cell, radians=True) eta = (1 + 4 * np.cos(alpha)) / (2 + 4 * np.cos(alpha)) nu = 3 / 4 - eta / 2 points = { 'G': [0, 0, 0], 'B': [eta, 1 / 2, 1 - eta], 'B1': [1 / 2, 1 - eta, eta - 1], 'F': [1 / 2, 1 / 2, 0], 'L': [1 / 2, 0, 0], 'L1': [0, 0, -1 / 2], 'P': [eta, nu, nu], 'P1': [1 - nu, 1 - nu, 1 - eta], 'P2': [nu, nu, eta - 1], 'Q': [1 - nu, nu, 0], 'X': [nu, 0, -nu], 'Z': [0.5, 0.5, 0.5] } else: points = ibz_points[latt] myspecial_points = {label: np.dot(M, kpt) for label, kpt in points.items()} return CellInfo(rcell=rcell, lattice=latt, special_points=myspecial_points)
def classify(self, calc, symprec=None): ''' Reasons on normalization, invokes hierarchy API and prepares calc for saving NB: this is the PUBLIC method @returns tilde_obj, error ''' error = None symbols = calc.structures[-1].get_chemical_symbols() calc.info['formula'] = self.formula(symbols) calc.info['cellpar'] = cell_to_cellpar(calc.structures[-1].cell).tolist() if calc.info['input']: try: calc.info['input'] = str(calc.info['input'], errors='ignore') except: pass # applying filter: todo if (calc.info['finished'] == 0x1 and self.settings['skip_unfinished']) or \ (not calc.info['energy'] and self.settings['skip_notenergy']): return None, 'data do not satisfy the active filter' # naive elements extraction fragments = re.findall(r'([A-Z][a-z]?)(\d*[?:.\d+]*)?', calc.info['formula']) for fragment in fragments: if fragment[0] == 'X': continue calc.info['elements'].append(fragment[0]) calc.info['contents'].append(int(fragment[1])) if fragment[1] else calc.info['contents'].append(1) # extend hierarchy with modules for C_obj in self.Classifiers: try: calc = C_obj['classify'](calc) except: exc_type, exc_value, exc_tb = sys.exc_info() error = "Fatal error during classification:\n %s" % "".join(traceback.format_exception( exc_type, exc_value, exc_tb )) return None, error # chemical ratios if not len(calc.info['standard']): if len(calc.info['elements']) == 1: calc.info['expanded'] = 1 if not calc.info['expanded']: calc.info['expanded'] = reduce(gcd, calc.info['contents']) for n, i in enumerate([x//calc.info['expanded'] for x in calc.info['contents']]): if i == 1: calc.info['standard'] += calc.info['elements'][n] else: calc.info['standard'] += calc.info['elements'][n] + str(i) if not calc.info['expanded']: del calc.info['expanded'] calc.info['nelem'] = len(calc.info['elements']) if calc.info['nelem'] > 13: calc.info['nelem'] = 13 calc.info['natom'] = len(symbols) # periodicity if calc.info['periodicity'] == 0: calc.info['periodicity'] = 0x4 elif calc.info['periodicity'] == -1: calc.info['periodicity'] = 0x5 # general calculation type reasoning if (calc.structures[-1].get_initial_charges() != 0).sum(): calc.info['calctypes'].append(0x4) # numpy count_nonzero implementation if (calc.structures[-1].get_initial_magnetic_moments() != 0).sum(): calc.info['calctypes'].append(0x5) if calc.phonons['modes']: calc.info['calctypes'].append(0x6) if calc.phonons['ph_k_degeneracy']: calc.info['calctypes'].append(0x7) if calc.phonons['dielectric_tensor']: calc.info['calctypes'].append(0x8) # CRYSTAL-only! if len(calc.tresholds) > 1: calc.info['calctypes'].append(0x3) calc.info['optgeom'] = True if calc.electrons['dos'] or calc.electrons['bands']: calc.info['calctypes'].append(0x2) if calc.info['energy']: calc.info['calctypes'].append(0x1) calc.info['spin'] = 0x2 if calc.info['spin'] else 0x1 # TODO: standardize if 'vac' in calc.info: if 'X' in symbols: calc.info['techs'].append('vacancy defect: ghost') else: calc.info['techs'].append('vacancy defect: void space') calc.info['lata'] = round(calc.info['cellpar'][0], 3) calc.info['latb'] = round(calc.info['cellpar'][1], 3) calc.info['latc'] = round(calc.info['cellpar'][2], 3) calc.info['latalpha'] = round(calc.info['cellpar'][3], 2) calc.info['latbeta'] = round(calc.info['cellpar'][4], 2) calc.info['latgamma'] = round(calc.info['cellpar'][5], 2) # invoke symmetry finder found = SymmetryHandler(calc, symprec) if found.error: return None, found.error calc.info['sg'] = found.i calc.info['ng'] = found.n calc.info['symmetry'] = found.symmetry calc.info['spg'] = "%s — %s" % (found.n, found.i) calc.info['pg'] = found.pg calc.info['dg'] = found.dg # phonons if calc.phonons['dfp_magnitude']: calc.info['dfp_magnitude'] = round(calc.phonons['dfp_magnitude'], 3) if calc.phonons['dfp_disps']: calc.info['dfp_disps'] = len(calc.phonons['dfp_disps']) if calc.phonons['modes']: calc.info['n_ph_k'] = len(calc.phonons['ph_k_degeneracy']) if calc.phonons['ph_k_degeneracy'] else 1 #calc.info['rgkmax'] = calc.electrons['rgkmax'] # LAPW # electronic properties reasoning by bands if calc.electrons['bands']: if calc.electrons['bands'].is_conductor(): calc.info['etype'] = 0x2 calc.info['bandgap'] = 0.0 calc.info['bandgaptype'] = 0x1 else: try: gap, is_direct = calc.electrons['bands'].get_bandgap() except ElectronStructureError as e: calc.electrons['bands'] = None calc.warning(e.value) else: calc.info['etype'] = 0x1 calc.info['bandgap'] = round(gap, 2) calc.info['bandgaptype'] = 0x2 if is_direct else 0x3 # electronic properties reasoning by DOS if calc.electrons['dos']: try: gap = round(calc.electrons['dos'].get_bandgap(), 2) except ElectronStructureError as e: calc.electrons['dos'] = None calc.warning(e.value) else: if calc.electrons['bands']: # check coincidence if abs(calc.info['bandgap'] - gap) > 0.2: calc.warning('Bans gaps in DOS and bands data differ considerably! The latter will be considered.') else: calc.info['bandgap'] = gap if gap: calc.info['etype'] = 0x1 else: calc.info['etype'] = 0x2 calc.info['bandgaptype'] = 0x1 # TODO: beware to add something new to an existing item! # TODO2: unknown or absent? for entity in self.hierarchy: if entity['creates_topic'] and not entity['optional'] and not calc.info.get(entity['source']): if entity['enumerated']: calc.info[ entity['source'] ] = [0x0] if entity['multiple'] else 0x0 else: calc.info[ entity['source'] ] = ['none'] if entity['multiple'] else 'none' calc.benchmark() # this call must be at the very end of parsing return calc, error
def diffMap2(input_filename, Ro, b, co, dx, dy, dz, output_filename, anion='O', anion_number=8): # Read in the CIF file a = ase.io.read("{}".format(input_filename)) # Take only the anion positions and unit cell vectors a = ase.Atoms([anion for i in range(len(a[a.numbers == anion_number]))], cell=a.cell, positions=a.positions[a.numbers == anion_number], pbc=True) # Make a mesh over the resolution defined by dx,dy,dz x, y, z = np.mgrid[0:1 + dx:dx, 0:1 + dy:dy, 0:1 + dz:dz] r_scaled = np.stack([x, y, z]) # Cartesian coordinates of each voxel # Transform the unit cell to access each voxel by real lengths r = np.dot(r_scaled.reshape((3, r_scaled.shape[1]**3)).T, a.cell).T.reshape(r_scaled.shape) # Make an empty array to calculate the Valence over V = np.ones(x.shape) # Define oxygen positions O = a.get_positions() # Add coordinates of all anions in adjacent cells # (important if the interaction length is larger than the unit cell) permutations = [[-1, 0, 1] for i in range(3)] for i in itr.product(*permutations): if i != (0, 0, 0): shift = a.cell[0] * i[0] + a.cell[1] * i[1] + a.cell[2] * i[2] a.translate(shift) O = np.concatenate((O, a.get_positions()), axis=0) a.translate(-shift) # optionally, time this section because it is the time critical step start_time = time.time() # Iterate through each volume element in the unit cell lens = [range(r.shape[1]), range(r.shape[2]), range(r.shape[3])] for i in itr.product(*lens): # define upper and lower bound for anion coordinates as deffined by the cuttoff radii (co) top = r[:, i[0], i[1], i[2]] + co bottom = r[:, i[0], i[1], i[2]] - co # Store all oxygen within a box around the sphere defined by the cuttoff radii O2 = O[np.all(((O < top) == (O > bottom)), axis=1)] # Calculate the distance to each anion in the box Ri = np.array([ np.sqrt((r[0, i[0], i[1], i[2]] - O2[k, 0])**2 + # This way worked faster (r[1, i[0], i[1], i[2]] - O2[k, 1])**2 + # than with linalg.norm() (r[2, i[0], i[1], i[2]] - O2[k, 2])**2) for k in range(len(O2[:, 0])) ]) # Calculate the valence sum and apply the cutoff V[i[0], i[1], i[2]] = np.abs(np.sum(np.exp((Ro - Ri[Ri < co]) / b)) - 1) # Save the information with open('{0}.grd'.format(output_filename), "w") as savefile: savefile.write("Bond Valence Sum Difference\r") # Title from ase.geometry import cell_to_cellpar cellParams = cell_to_cellpar(a.cell) # get ABC alpha, beta, gamma savefile.write(" ".join([str(k) for k in cellParams]) + "\r") savefile.write(" ".join([str(k) for k in V.shape]) + "\r") for i in np.nditer(V.flatten()): savefile.write("%.6f " % (i)) # Write each valence difference value #print "Total time taken = %.4f s" % (time.time()-start_time) return V
def fix_cell(cell,eps=5e-3): cellpar=cell_to_cellpar(cell) for i in [3,4,5]: if abs(cellpar[i]/90.0*np.pi/2-np.pi/2)<eps: cellpar[i]=90.0 return cellpar_to_cell(cellpar)
def split_layer(atoms, thr=0.03, direction=2, sort=True, return_pos=False): """ split atoms into layers Parameters: thr: max distance in direction from atoms in same layer. direction: 0 | 1| 2 sort: whether to sort the layers by posiontion. return_pos: whether to return the positions of each atom. Returns: A list of symnum lists, each is the symnum of a layer. [['Ni1','O2'],['']] """ for i in [0, 1, 2]: if i != direction: if atoms.get_cell()[direction][i] > 0.5: raise NotImplementedError( "cellparameters should be orthgonal in the direction") atoms = force_near_0(atoms) z = cell_to_cellpar(atoms.get_cell())[direction] positions = [pos[direction] for pos in atoms.get_positions()] def is_near(pos1, pos2): if abs(pos1 - pos2) % z < thr or z - abs(pos1 - pos2) % z < thr: return True else: return False symdict = symbol_number(atoms) layer_indexes = [] layer_poses = [] layer_symnums = [] for i, pos in enumerate(positions): got_layer = False if layer_indexes == []: layer_indexes.append([i]) layer_poses.append([pos]) layer_symnums.append([list(symdict.keys())[i]]) continue for ind, layer_ind_pos in enumerate(zip(layer_indexes, layer_poses)): lp = np.average(layer_ind_pos[1]) #print "ind_pos",layer_ind_pos[1] if is_near(pos, lp): print("got: %s" % ind, pos, lp) got_layer = True index = ind break if got_layer: layer_indexes[index].append(i) layer_poses[index].append(pos) layer_symnums[index].append(list(symdict.keys())[i]) else: layer_indexes.append([i]) layer_poses.append([pos]) layer_symnums.append([list(symdict.keys())[i]]) if sort: sort_i = sorted(list(range(len(layer_poses))), key=layer_poses.__getitem__) layer_symnums = [layer_symnums[i] for i in sort_i] layer_poses = [layer_poses[i] for i in sort_i] if return_pos: return layer_symnums, layer_poses else: return layer_symnums
def valence_map(input_filename, anion, anion_number, Ro, b, co, dx, dy, dz, output_filename): # Read in CIF file a = ase.io.read("{}.cif".format(input_filename)) # a = ase.Atoms([anion for i in range(len(a[a.numbers == anion_number]))], cell=a.cell, positions=a.positions[a.numbers == anion_number], pbc=True) x, y, z = np.mgrid[0:1 + dx:dx, 0:1 + dy:dy, 0:1 + dz:dz] # Make mesh # Here we will take the mesh and transform it to fit the unit cell in real space r = np.stack([x, y, z]) # Gives the cartesian coordinates of each voxel lens = [range(r.shape[2]), range(r.shape[3])] # List to iterate over all y,z columns for j in itr.product(*lens): r[:, :, j[0], j[1]] = np.dot(r[:, :, j[0], j[1]].T, a.cell).T # Transform to coordinates of unit cell # make an empty array to hold values of the Valence difference V = np.ones(x.shape) O = a.get_positions()[a.numbers == 8] #print O.shape # coordinates of all anions in the unit cell # Add coordinates of all anions in adjacent cells permutations = [[-1, 0, 1] for i in range(3)] for i in itr.product(*permutations): if i != (0, 0, 0): shift = a.cell[0] * i[0] + a.cell[1] * i[1] + a.cell[2] * i[2] a.translate(shift) O = np.concatenate((O, a.get_positions()), axis=0) a.translate(-shift) start_time = time.time( ) # We're going to time this section because it's the long part # Iterate through each volume element (the index of which is contained in lens) lens = [range(r.shape[1]), range(r.shape[2]), range(r.shape[3])] for i in itr.product(*lens): # iterate through each voxel in the unit cell top = r[:, i[0], i[1], i[2]] + co # define upper and lower bound for anion coordinates bottom = r[:, i[0], i[1], i[2]] - co # that fall within the cutoff radii of the voxel # Apply cuttoff 'box' on anions to reduce the number of distance calculations O2 = O[np.all(((O < top) == (O > bottom)), axis=1)] # Contains anions in cutoff box # Calculate the distance to each anion in the box Ri = np.array([ np.sqrt((r[0, i[0], i[1], i[2]] - O2[k, 0])**2 + # This way worked faster (r[1, i[0], i[1], i[2]] - O2[k, 1])**2 + # than with linalg.norm() (r[2, i[0], i[1], i[2]] - O2[k, 2])**2) for k in range(len(O2[:, 0])) ]) # Calculate the valence sum and apply the cutoff V[i[0], i[1], i[2]] = np.sum(np.exp((Ro - Ri[Ri < co]) / b)) savefile = open('{0}.grd'.format(output_filename), "w") # outputfile savefile.write("Bond Valence Sum Difference\r") # Title from ase.geometry import cell_to_cellpar cellParams = cell_to_cellpar(a.cell) # get ABC alpha, beta, gamma savefile.write(" ".join([str(k) for k in cellParams]) + "\r") savefile.write(" ".join([str(k) for k in V.shape]) + "\r") for i in np.nditer(V.flatten()): savefile.write("%.6f " % (i)) # Write each valence difference value savefile.close() return r, V