def __init__(self, absolute_path=None): super().__init__(absolute_path) self.tags.add("lmpdat") self._multiple_dihedrals = False self.atoms = Atoms([]) self._xyz_lo_hi = None # [xlo, xhi, ylo, yhi, zlo, zhi] if self.absolute_path is not None: self.read_lmpdat()
def __init__(self, absolute_path=None): """Initiates an Xyz object. If absolute_path is not None, reads the xyz file.""" super().__init__(absolute_path) self.tags.add("xyz") self.atoms = Atoms([]) if self.absolute_path is not None: self.read_xyz()
def read_vasp(filename, symbols=None): file = open(filename) lines = file.readlines() line1 = [x for x in lines[0].split()] if is_exist_symbols(line1): symbols = line1 scale = float(lines[1]) cell = [] for i in range(2, 5): cell.append([float(x) for x in lines[i].split()[:3]]) cell = np.array(cell) * scale try: num_atoms = np.array([int(x) for x in lines[5].split()]) line_at = 6 except ValueError: symbols = [x for x in lines[5].split()] num_atoms = np.array([int(x) for x in lines[6].split()]) line_at = 7 expaned_symbols = expand_symbols(num_atoms, symbols) if lines[line_at][0].lower() == 's': line_at += 1 is_scaled = True if (lines[line_at][0].lower() == 'c' or lines[line_at][0].lower() == 'k'): is_scaled = False line_at += 1 positions = [] for i in range(line_at, line_at + num_atoms.sum()): positions.append([float(x) for x in lines[i].split()[:3]]) if is_scaled: atoms = Atoms(symbols=expaned_symbols, cell=cell, scaled_positions=positions) else: atoms = Atoms(symbols=expaned_symbols, cell=cell, positions=positions) return atoms
def __init__(self, absolute_path=None, ignore_types=None): # ignore_types is for internal use super().__init__(absolute_path=absolute_path) self.number_of_particles = None self.has_velocity = True self.atoms = Atoms([]) self.entry_count = None self.auxiliary = [] if ignore_types is None: self.ignore_types = [] else: self.ignore_types = ignore_types if self.absolute_path is not None: self.read()
def parse_scf(outputfile): 'Obtain material system information' unit_lvs = [] unit_rlvs = [] mass = [] symbol = [] position = [] file = open(outputfile, 'r') lines = file.readlines() for index, line in enumerate(lines): if 'bravais-lattice index' in line: ibrav = int(line.split()[3]) if 'lattice parameter' in line: lattice_constant = float(line.split()[4]) if 'number of atoms/cell' in line: natom = int(line.split()[4]) if 'number of atomic types' in line: ntype = int(line.split()[5]) if 'crystal axes' in line: for i in range(0, 3): unit_lvs.append( [float(f) for f in lines[index + 1 + i].split()[3:6]]) #unit_lvs = unit_lvs * lattice_constant if 'reciprocal axes:' in line: for i in range(0, 3): unit_rlvs.append( [float(f) for f in lines[index + 1 + i].split()[3:6]]) #unit_rlvs = unit_rlvs * 2 * math.pi / lattice_constant if 'mass' in line: for i in range(0, ntype): symbol.append(lines[index + 1 + i].split()[0]) mass.append(float(lines[index + 1 + i].split()[2])) if 'positions' in line: for i in range(0, natom): position.append( [float(f) for f in lines[index + 1 + i].split()[6:9]]) unitcell = Atoms(ntype, symbol, mass, natom, position, unit_lvs) unitcell.print_info() return unitcell
def findsym(self): def show_symmetry(symmetry): for i in range(symmetry['rotations'].shape[0]): print(" --------------- %4d ---------------" % (i + 1)) rot = symmetry['rotations'][i] trans = symmetry['translations'][i] print(" rotation:") for x in rot: print(" [%2d %2d %2d]" % (x[0], x[1], x[2])) print(" translation:") print(" (%8.5f %8.5f %8.5f)" % (trans[0], trans[1], trans[2])) atom_in = Atoms(symbols=self.symbols_in, cell=list(self.lattice), scaled_positions=self.positions_in, pbc=True) print("[get_spacegroup]") print(" Spacegroup of " + self.seedname + " is %s." % spglib.get_spacegroup(atom_in)) self.symmetry = spglib.get_symmetry(atom_in) self.nsymm = self.symmetry['rotations'].shape[0] show_symmetry(self.symmetry)
import sys from pyspglib import spglib import numpy as np try: from atoms import Atoms except ImportError: print "Atoms class is necessary." print "You can use atoms.py in the test/ directory." sys.exit(1) silicon = Atoms(symbols=['Si'] * 8, cell=[(4, 0, 0), (0, 4, 0), (0, 0, 4)], scaled_positions=[(0, 0, 0), (0, 0.5, 0.5), (0.5, 0, 0.5), (0.5, 0.5, 0), (0.25, 0.25, 0.25), (0.25, 0.75, 0.75), (0.75, 0.25, 0.75), (0.75, 0.75, 0.25)], pbc=True) silicon_prim = Atoms(symbols=['Si'] * 2, cell=[(0, 2, 2), (2, 0, 2), (2, 2, 0)], scaled_positions=[(0, 0, 0), (0.25, 0.25, 0.25)], pbc=True) rutile = Atoms(symbols=['Si'] * 2 + ['O'] * 4, cell=[(4, 0, 0), (0, 4, 0), (0, 0, 3)], scaled_positions=[(0, 0, 0), (0.5, 0.5, 0.5), (0.3, 0.3, 0.0), (0.7, 0.7, 0.0), (0.2, 0.8, 0.5), (0.8, 0.2, 0.5)], pbc=True)
for vec, axis in zip(lattice, ("a", "b", "c")): print("%s %10.5f %10.5f %10.5f" % (tuple(axis,) + tuple(vec))) def show_cell(lattice, positions, numbers): show_lattice(lattice) print("Atomic points:") for p, s in zip(positions, numbers): print("%2d %10.5f %10.5f %10.5f" % ((s,) + tuple(p))) silicon_ase = Atoms(symbols=['Si'] * 8, cell=[(4, 0, 0), (0, 4, 0), (0, 0, 4)], scaled_positions=[(0, 0, 0), (0, 0.5, 0.5), (0.5, 0, 0.5), (0.5, 0.5, 0), (0.25, 0.25, 0.25), (0.25, 0.75, 0.75), (0.75, 0.25, 0.75), (0.75, 0.75, 0.25)], pbc=True) silicon = ([(4, 0, 0), (0, 4, 0), (0, 0, 4)], [(0, 0, 0), (0, 0.5, 0.5), (0.5, 0, 0.5), (0.5, 0.5, 0), (0.25, 0.25, 0.25),
class Xyz(File): """Class for xyz files.""" def __init__(self, absolute_path=None): """Initiates an Xyz object. If absolute_path is not None, reads the xyz file.""" super().__init__(absolute_path) self.tags.add("xyz") self.atoms = Atoms([]) if self.absolute_path is not None: self.read_xyz() def read_xyz(self): """ Reads info from xyz file, whose path was given as absolute_path when the object was instantiated. Raises ------ ValueError If the xyz file has extra lines between atoms. If a Lattice is found that has the wrong number of parameters. TypeError If non-numeric values are found for an atom's position. """ self._check_read() lines = iter(self.content) line = next(lines) n_atoms = 0 while True: try: n_atoms = int(line.split()[0]) except TypeError: line = next(lines) continue else: break lattice_indexes = self._find("Lattice") must_invent_cell = False if len(lattice_indexes) > 0: lattice_index = lattice_indexes[0] lattice = find_between(self.content[lattice_index], '"', '"') try: xx, xy, xz, yx, yy, yz, zx, zy, zz = tuple(lattice.split()) except ValueError: raise ValueError("Lattice in xyz file is bad") self.atoms.cell = [[xx, xy, xz], [yx, yy, yz], [zx, zy, zz]] else: # if no "Lattice" is found, atoms.cell is invented below lattice_index = 0 must_invent_cell = True for line in self.content[lattice_index + 1:]: if not line[0].isalpha(): continue try: s, x, y, z = tuple(line.split()) except ValueError: continue # raise ValueError("xyz file has a bad format, avoid extra lines") try: xyz = [float(x), float(y), float(z)] except TypeError: raise TypeError("bad positions in file: {} {} {}".format( x, y, z)) atom = Atom(atom_type=s, position=xyz) self.atoms.add_atom(atom) if len(self.atoms) != n_atoms: print("WARNING: {} atoms declared in file, {} atoms found in file". format(n_atoms, len(self.atoms))) if must_invent_cell: gap = 10 # angstroms min_x = min(atom.position[0] for atom in self.atoms) max_x = max(atom.position[0] for atom in self.atoms) min_y = min(atom.position[1] for atom in self.atoms) max_y = max(atom.position[1] for atom in self.atoms) min_z = min(atom.position[2] for atom in self.atoms) max_z = max(atom.position[2] for atom in self.atoms) xx = max_x - min_x + gap yy = max_y - min_y + gap zz = max_z - min_z + gap self.atoms.cell = [[xx, 0, 0], [0, yy, 0], [0, 0, zz]] def write_xyz(self, filename, real_types=False, with_classification=False): """ Writes xyz file with info present in the Xyz object. Parameters ---------- filename : str Path to output xyz file. real_types : bool, optional If real types should be used instead of types (e.g. C instead of C2). Standard is False. with_classification : bool, optional If atom classification is wanted as a comment after every line. Standard is False. """ with open(filename, "w") as F: F.write(str(len(self.atoms))) F.write('\n') if self.atoms.cell is not None: F.write('Lattice="') F.write(' '.join([str(p) for p in self.atoms.cell[0]]) + ' ') F.write(' '.join([str(p) for p in self.atoms.cell[1]]) + ' ') F.write(' '.join([str(p) for p in self.atoms.cell[2]]) + '"' + '\n') else: F.write('\n') if not with_classification: for atom in self.atoms: F.write(atom.__str__(real_type=real_types) + '\n') else: for atom in self.atoms: F.write( atom.__str__(real_type=real_types) + '\t# ' + atom.classification + '\n') def write_simple_cif(self, filename, struct_name, comment=None, centralize=True): """ Writes a simple cif file. Meant to be used for 1D structures. Parameters ---------- filename : str Path to output cif file. struct_name : str Name of the structure, to be included in the file info. comment : str, optional Comment to the structure, to be included in the file info. Standard comment is 'simple cif for 1D nanostructure'. centralize : bool, optional If the atomic positions should be centralized in the cell. Standard is True. Notes ----- This is a very simple cif writing for theoretical structures. It's not meant to be used for complete crystallography information. """ self._check_read() if not self.atoms.atoms: self.read_xyz() # arguments checking struct_name = struct_name.replace(" ", "") if comment is None: comment = "simple cif for 1D nanostructure" with open(filename, "w") as F: F.write("data_" + struct_name + "\n\n") F.write("_publ_section_comment" + "\n" + ";" + "\n" + comment + "\n" + ";" + "\n\n") cell_x = self.atoms.cell[0][0] cell_y = self.atoms.cell[1][1] cell_z = self.atoms.cell[2][2] F.write("_cell_length_a " + str(round(cell_x, 4)) + "(0)" + "\n") F.write("_cell_length_b " + str(round(cell_y, 4)) + "(0)" + "\n") F.write("_cell_length_c " + str(round(cell_z, 4)) + "(0)" + "\n") F.write("_cell_angle_alpha 90.0000(0)" + "\n") F.write("_cell_angle_beta 90.0000(0)" + "\n") F.write("_cell_angle_gamma 90.0000(0)" + "\n\n") F.write("_symmetry_space_group_name_H-M 'P 1'" + "\n") F.write("_symmetry_Int_Tables_number 1" + "\n") F.write("_symmetry_cell_setting triclinic" + "\n\n") F.write("loop_" + "\n") F.write("_atom_site_label" + "\n") F.write("_atom_site_type_symbol" + "\n") F.write("_atom_site_occupancy" + "\n") F.write("_atom_site_fract_x" + "\n") F.write("_atom_site_fract_y" + "\n") F.write("_atom_site_fract_z" + "\n") if centralize: self.atoms.translate_to_cell_center() counter = dict() for atom in self.atoms: atom_type = str(atom.type) if atom_type in counter.keys(): counter[atom_type] += 1 else: counter[atom_type] = 1 F.write(atom_type + str(counter[atom_type]) + " " + atom.type.real + " 1.0000 " + str(round(atom.position[0] / cell_x, 4)) + " " + str(round(atom.position[1] / cell_y, 4)) + " " + str(round(atom.position[2] / cell_z, 4)) + "\n")
class Cfg(File): """Class for LAMMPS cfg files.""" # usual line: "mass type x y z *etc" where len(x,y,z,*etc) == entry_count def __init__(self, absolute_path=None, ignore_types=None): # ignore_types is for internal use super().__init__(absolute_path=absolute_path) self.number_of_particles = None self.has_velocity = True self.atoms = Atoms([]) self.entry_count = None self.auxiliary = [] if ignore_types is None: self.ignore_types = [] else: self.ignore_types = ignore_types if self.absolute_path is not None: self.read() def read(self): index_start = self._find("Number of particles")[0] index_auxiliary = None index_atoms = None self.number_of_particles = int(self.content[index_start].split()[-1]) h0_11, h0_12, h0_13, h0_21, h0_22, h0_23, h0_31, h0_32, h0_33 = 0, 0, 0, 0, 0, 0, 0, 0, 0 if len(self._find(".NO_VELOCITY.")) == 1: self.has_velocity = False if len(self._find("A = 1 Angstrom")) == 0: print( "WARNING: distance units in cfg file may not be in Angstroms!") for (index, line) in enumerate(self.content[index_start:], index_start): if line.startswith("H0"): if "H0(1,1)" in line: h0_11 = float(line.split()[-2]) elif "H0(1,2)" in line: h0_12 = float(line.split()[-2]) elif "H0(1,3)" in line: h0_13 = float(line.split()[-2]) elif "H0(2,1)" in line: h0_21 = float(line.split()[-2]) elif "H0(2,2)" in line: h0_22 = float(line.split()[-2]) elif "H0(2,3)" in line: h0_23 = float(line.split()[-2]) elif "H0(3,1)" in line: h0_31 = float(line.split()[-2]) elif "H0(3,2)" in line: h0_32 = float(line.split()[-2]) elif "H0(3,3)" in line: h0_33 = float(line.split()[-2]) elif "entry_count" in line: self.entry_count = int(line.split()[-1]) n_standard_args = 6 if self.has_velocity else 3 for _ in range(self.entry_count - n_standard_args): self.auxiliary.append(None) index_auxiliary = index + 1 break else: continue self.atoms.cell = [[h0_11, h0_12, h0_13], [h0_21, h0_22, h0_23], [h0_31, h0_32, h0_33]] for (index, line) in enumerate(self.content[index_auxiliary:], index_auxiliary): if line.startswith("auxiliary"): i = int(find_between(line, "[", "]")) _, __, auxiliary = tuple(line.split()) self.auxiliary[i] = auxiliary else: index_atoms = index break for i in range(index_atoms, len(self.content), 3): typ = clear_end(self.content[i + 1], [" ", "\n", "\t"]) if typ in self.ignore_types: continue mass = float(self.content[i + 0]) etc = dict() if self.has_velocity: raise TypeError("cfg with velocities! not implemented yet!") else: x, y, z, *args = tuple(self.content[i + 2].split()) atom = Atom(atom_type=typ, position=[float(x), float(y), float(z)]) atom.position = np.matmul(self.atoms.cell, atom.position) atom.type.mass = mass for (index, arg) in enumerate(args): etc[self.auxiliary[index]] = arg # string atom.etc = etc self.atoms.add_atom(atom) if self.number_of_particles != len( self.atoms) and not self.ignore_types: print("WARNING: file says {} atoms, but only {} were found".format( self.number_of_particles, len(self.atoms))) def write_xyz(self, path, real_types=False): xyz = Xyz() xyz.atoms = self.atoms xyz.write_xyz(path, real_types=real_types)
class LmpDat(File): """Class for LAMMPS Data files. Contains topological information for input. May contain force field parameters.""" def __init__(self, absolute_path=None): super().__init__(absolute_path) self.tags.add("lmpdat") self._multiple_dihedrals = False self.atoms = Atoms([]) self._xyz_lo_hi = None # [xlo, xhi, ylo, yhi, zlo, zhi] if self.absolute_path is not None: self.read_lmpdat() def read_lmpdat(self): pass def get_xyz(self, xyz_file: str): """ Gets info from xyz file, and lattice if there is any. Parameters ---------- xyz_file : str Path to xyz file. """ # makes a dummy Xyz object _xyz = Xyz(xyz_file) # uses the dummy Xyz object to get info from xyz file if len(self.atoms) > 0: print("WARNING: erasing current {} atoms".format(len(self.atoms))) self.atoms = _xyz.atoms print("Reading {} atoms from xyz file".format(len(self.atoms))) # deals with the cell self.cell_to_lo_hi() def cell_to_lo_hi(self): xlo = min(atom.position[0] for atom in self.atoms) ylo = min(atom.position[1] for atom in self.atoms) zlo = min(atom.position[2] for atom in self.atoms) extra_spacing = 5 # angstroms xhi = max(atom.position[0] for atom in self.atoms) + extra_spacing yhi = max(atom.position[1] for atom in self.atoms) + extra_spacing zhi = max(atom.position[2] for atom in self.atoms) + extra_spacing try: # if self.atoms.cell is not None xhi = xlo + self.atoms.cell[0][0] yhi = ylo + self.atoms.cell[1][1] zhi = zlo + self.atoms.cell[2][2] except TypeError: # if self.atoms.cell is None print("WARNING: no Lattice found in xyz file; " "if the system is periodic, this is BAD") finally: self._xyz_lo_hi = [xlo, xhi, ylo, yhi, zlo, zhi] def get_params(self, params_file: str): """ Gets info from parameters file (force field parameters). Parameters ---------- params_file : str Path to parameters file. Raises ------ TypeError If any parameter in the file isn't a number. Notes ----- The expected file format isn't native to LAMMPS. Instead, it can be written by e.g. a CharmmGeneral object. The file should look like this: " Atom Types C1 -0.18 0.06 3.59923 0.01 3.38542 C2 0.0 0.06 3.59923 0.01 3.38542 Bond Types C1:C1 195.0 1.53 C1:C2 195.0 1.53 Angle Types C1:C2:C1 58.0 109.5 11.16 2.561 C1:C1:C2 35.0 111.4 22.53 2.179 Dihedral Types C1:C2:C1:C1 0.14 3 0 1.0 Improper Types # none " """ if not self.atoms.bonds: print("WARNING: topology not computed yet") # makes a dummy generic File for parameters _params_file = File(params_file) atom_types_index = _params_file._find("Atom Types") bond_types_index = _params_file._find("Bond Types") angle_types_index = _params_file._find("Angle Types") dihedral_types_index = _params_file._find("Dihedral Types") improper_types_index = _params_file._find("Improper Types") # each of the titles above must appear exactly once # note that the area below the title may be left empty for lis in [ atom_types_index, bond_types_index, angle_types_index, dihedral_types_index, improper_types_index ]: if (len(lis) > 1) or (len(lis) == 0): print("WARNING: bad params file") atom_types_index = atom_types_index[0] bond_types_index = bond_types_index[0] angle_types_index = angle_types_index[0] dihedral_types_index = dihedral_types_index[0] improper_types_index = improper_types_index[0] # checks if there are multiple dihedrals self._multiple_dihedrals = False types = [] for line in _params_file.content[dihedral_types_index + 1:improper_types_index]: try: if line[0].isalpha(): typ, *etc = tuple(line.split()) if typ in types: self._multiple_dihedrals = True types.append(typ) except IndexError: continue # Atom Types index = 1 for line in _params_file.content[atom_types_index + 1:bond_types_index]: try: if line[0].isalpha(): typ, *charge_and_params = tuple(line.split()) if typ not in AtomType.instances_dict.keys(): AtomType(typ) # instantiates it charge, *params = charge_and_params params_nums = [] # just takes strings into the right numeric types # if the str "1" becomes the float 1.0, LAMMPS may complain for param in params: try: if "." in param: param_num = float(param) else: param_num = int(param) except TypeError: raise TypeError( "all Atom Type parameters must be numbers, " "at least one is not") except ValueError: # if param=None param_num = None params_nums.append(param_num) AtomType.instances_dict[typ].index = index AtomType.instances_dict[typ].charge = charge AtomType.instances_dict[typ].params = params_nums # list index += 1 except IndexError: continue # Bond Types index = 1 for line in _params_file.content[bond_types_index + 1:angle_types_index]: try: if line[0].isalpha(): typ, *params = tuple(line.split()) if typ not in BondType.instances_dict.keys(): BondType(typ) # instantiates it params_nums = [] # just takes strings into the right numeric types # if the str "1" becomes the float 1.0, LAMMPS may complain for param in params: try: if "." in param: param_num = float(param) else: param_num = int(param) except TypeError: raise TypeError( "all Bond Type parameters must be numbers, " "at least one is not") except ValueError: # if param is None param_num = None params_nums.append(param_num) BondType.instances_dict[typ].index = index BondType.instances_dict[typ].params = params_nums # list index += 1 except IndexError: continue # Angle Types index = 1 for line in _params_file.content[angle_types_index + 1:dihedral_types_index]: try: if line[0].isalpha(): typ, *params = tuple(line.split()) if typ not in AngleType.instances_dict.keys(): AngleType(typ) # instantiates it params_nums = [] # just takes strings into the right numeric types # if the str "1" becomes the float 1.0, LAMMPS may complain for param in params: try: if "." in param: param_num = float(param) else: param_num = int(param) except TypeError: raise TypeError( "all Angle Type parameters must be numbers, " "at least one is not") except ValueError: # if param=None param_num = None params_nums.append(param_num) AngleType.instances_dict[typ].index = index AngleType.instances_dict[typ].params = params_nums # list index += 1 except IndexError: continue # Dihedral Types index = 1 for line in _params_file.content[dihedral_types_index + 1:improper_types_index]: try: if line[0].isalpha(): typ, *params = tuple(line.split()) if typ not in DihedralType.instances_dict.keys(): DihedralType(typ) # instantiates it params_nums = [] # just takes strings into the right numeric types # if the str "1" becomes the float 1.0, LAMMPS may complain for param in params: try: if "." in param: param_num = float(param) else: param_num = int(param) except TypeError: raise TypeError( "all Dihedral Type parameters must be numbers, " "at least one is not") except ValueError: # if param=None param_num = None params_nums.append(param_num) if not self._multiple_dihedrals: DihedralType.instances_dict[typ].index = index DihedralType.instances_dict[ typ].params = params_nums # list else: # if self._multiple_dihedrals try: DihedralType.instances_dict[typ].index.append( index) DihedralType.instances_dict[typ].params.append( params_nums) except AttributeError: # first params of each dihedral DihedralType.instances_dict[typ].index = [] DihedralType.instances_dict[typ].params = [] DihedralType.instances_dict[typ].index.append( index) DihedralType.instances_dict[typ].params.append( params_nums) index += 1 except IndexError: continue # Improper Types index = 1 for line in _params_file.content[improper_types_index + 1:]: try: if line[0].isalpha(): typ, *params = tuple(line.split()) if typ not in ImproperType.instances_dict.keys(): ImproperType(typ) params_nums = [] # just takes strings into the right numeric types # if the str "1" becomes the float 1.0, LAMMPS may complain for param in params: try: if "." in param: param_num = float(param) else: param_num = int(param) except TypeError: raise TypeError( "all Improper Type parameters must be numbers, " "at least one is not") except ValueError: # if param=None param_num = None params_nums.append(param_num) ImproperType.instances_dict[typ].index = index ImproperType.instances_dict[ typ].params = params_nums # list index += 1 except IndexError: continue # moves charge from AtomType to each Atom self._set_charges() # re-indexes types leaving extra types (in .par but not in .xyz) behind self.atoms.re_index_types() # GAMBIARRA # but should not be a problem if .par is right self.delete_impropers_without_parameters() def delete_impropers_without_parameters(self): impropers = self.atoms.impropers[:] i = 1 for improper in impropers: if improper.type.params is None: self.atoms.impropers.remove(improper) else: improper.index = i i += 1 improper_types = self.atoms.improper_types[:] i = 1 for improper_type in improper_types: if improper_type.params is None: self.atoms.improper_types.remove(improper_type) else: improper_type.index = i i += 1 def _set_charges(self): for atom in self.atoms: atom.charge = atom.type.charge def multiply_dihedral_coeffs(self): """ Turns every dihedral in n dihedrals, where n is the number of terms in its series (e.g. a Fourier Series). Returns ------- n_dihedrals : int Total number of dihedrals in self.atoms, computed after the multiplication. Notes ----- The CHARMM parameters are given in this format. LAMMPS also has a nice sinusoidal form for dihedrals, making this format well suited. """ if not self._multiple_dihedrals: raise TypeError("not self._multiple_dihedrals") # this means there's an internal problem for dihedral in self.atoms.dihedrals: try: dihedral.index = dihedral.type.index[:] # meaningless numbers, only the list's len matters except TypeError: raise TypeError("bad dihedral type {}, check .par file".format( str(dihedral.type))) index = 1 for dihedral in self.atoms.dihedrals: for i in range(len(dihedral.index)): dihedral.index[i] = index index += 1 n_dihedrals = index - 1 return n_dihedrals def write_lmpdat(self, filename, atom_style="full", comments=False): """ Writes LAMMPS Data file with info present in this object i.e. topology and force field parameters previously read. Parameters ---------- filename : str Path for output LAMMPS Data file. CARE: Will erase any existing file with this name. atom_style : {'full', ...}, optional Wanted atom_style output format. comments : bool, optional If comments are wanted in the output file, containing redundant info about atom types. Notes ----- See LAMMPS documentation on atom_style: https://lammps.sandia.gov/doc/atom_style.html Examples -------- Example for combining xyz file and par file to make lmpdat file: >> xyz = "/home/lasim/mydir/NAME.xyz" >> par = "/home/lasim/mydir/NAME.par" >> from files.lmp import LmpDat >> lmp = LmpDat() >> lmp.get_xyz(xyz) >> lmp.atoms.compute_topology() # finds bonds, angles, etc >> lmp.get_params(par) # AFTER computing topology >> lmp.write_lmpdat("/home/lasim/mydir/NAME.lmp") """ # sets flags according to atom_style if atom_style == "full": atoms, bonds, angles, dihedrals, impropers = True, True, True, True, True molecule, charge = True, True parameters = True elif atom_style == "atomic": atoms, bonds, angles, dihedrals, impropers = True, False, False, False, False molecule, charge = False, False parameters = False elif atom_style == "charge": atoms, bonds, angles, dihedrals, impropers = True, False, False, False, False molecule, charge = False, True parameters = False else: # to be expanded as needed raise TypeError("bad atom_style") number_of_dihedrals = len(self.atoms.dihedrals) if self._multiple_dihedrals: number_of_dihedrals = self.multiply_dihedral_coeffs() with open(filename, "w") as F: # HEADERS F.write("LAMMPS data file\n\n") if atoms: F.write(str(len(self.atoms.atoms)) + " atoms \n") if bonds: F.write(str(len(self.atoms.bonds)) + " bonds \n") if angles: F.write(str(len(self.atoms.angles)) + " angles \n") if dihedrals: F.write(str(number_of_dihedrals) + " dihedrals \n") if impropers: F.write(str(len(self.atoms.impropers)) + " impropers \n\n") if atoms: F.write(str(len(self.atoms.atom_types)) + " atom types \n") if bonds: F.write(str(len(self.atoms.bond_types)) + " bond types \n") if angles: F.write(str(len(self.atoms.angle_types)) + " angle types \n") if dihedrals and not self._multiple_dihedrals: F.write( str(len(self.atoms.dihedral_types)) + " dihedral types \n") elif dihedrals and self._multiple_dihedrals: F.write( str( sum( len(typ.index) for typ in self.atoms.dihedral_types)) + " dihedral types \n") if impropers: F.write( str(len(self.atoms.improper_types)) + " improper types \n\n") F.write( str(self._xyz_lo_hi[0]) + " " + str(self._xyz_lo_hi[1]) + " " + "xlo xhi \n") F.write( str(self._xyz_lo_hi[2]) + " " + str(self._xyz_lo_hi[3]) + " " + "ylo yhi \n") F.write( str(self._xyz_lo_hi[4]) + " " + str(self._xyz_lo_hi[5]) + " " + "zlo zhi \n\n") F.write("Masses \n\n") for typ in self.atoms.atom_types: F.write( str(typ.index) + " " + str(typ.mass) + " # " + str(typ) + "\n") # STRUCTURES if atoms and self.atoms.atoms: molecule_flag = False F.write("\nAtoms \n\n") if molecule and charge: for atom in self.atoms.atoms: if atom.molecule.index is None: atom.molecule.index = 1 molecule_flag = True F.write( str(atom.index) + " " + str(atom.molecule.index) + " " + str(atom.type.index) + " " + str(atom.charge) + " " + " ".join(str(pos) for pos in atom.position) + " # " + str(atom.type) + "\n") elif charge: for atom in self.atoms.atoms: if atom.charge is None: atom.charge = 0.0 F.write( str(atom.index) + " " + str(atom.type.index) + " " + str(atom.charge) + " " + " ".join(str(pos) for pos in atom.position) + " # " + str(atom.type) + "\n") else: for atom in self.atoms.atoms: F.write( str(atom.index) + " " + str(atom.type.index) + " " + " ".join(str(pos) for pos in atom.position) + " # " + str(atom.type) + "\n") if molecule_flag: print("WARNING: single molecule assumed!") if bonds and self.atoms.bonds: F.write("\nBonds \n\n") for bond in self.atoms.bonds: if comments: output = (str(bond.index) + " " + str(bond.type.index) + " " + " ".join( str(atom.index) for atom in bond.atoms) + " # " + str(bond.type) + "\n") else: output = ( str(bond.index) + " " + str(bond.type.index) + " " + " ".join(str(atom.index) for atom in bond.atoms) + "\n") F.write(output) if angles and self.atoms.angles: F.write("\nAngles \n\n") for angle in self.atoms.angles: if comments: output = ( str(angle.index) + " " + str(angle.type.index) + " " + " ".join(str(atom.index) for atom in angle.atoms) + " # " + str(angle.type) + "\n") else: output = ( str(angle.index) + " " + str(angle.type.index) + " " + " ".join(str(atom.index) for atom in angle.atoms) + "\n") F.write(output) if dihedrals and self.atoms.dihedrals: F.write("\nDihedrals \n\n") for dihedral in self.atoms.dihedrals: if not self._multiple_dihedrals: if comments: output = (str(dihedral.index) + " " + str(dihedral.type.index) + " " + " ".join( str(atom.index) for atom in dihedral.atoms) + " # " + str(dihedral.type) + "\n") else: output = (str(dihedral.index) + " " + str(dihedral.type.index) + " " + " ".join( str(atom.index) for atom in dihedral.atoms) + "\n") F.write(output) else: # if self._multiple_dihedrals for i in range(len(dihedral.index)): if comments: output = (str(dihedral.index[i]) + " " + str(dihedral.type.index[i]) + " " + " ".join( str(atom.index) for atom in dihedral.atoms) + " # " + str(dihedral.type) + "\n") else: output = (str(dihedral.index[i]) + " " + str(dihedral.type.index[i]) + " " + " ".join( str(atom.index) for atom in dihedral.atoms) + "\n") F.write(output) if impropers and self.atoms.impropers: F.write("\nImpropers \n\n") for improper in self.atoms.impropers: if comments: output = (str(improper.index) + " " + str(improper.type.index) + " " + " ".join( str(atom.index) for atom in improper.atoms) + " # " + str(improper.type) + "\n") else: output = (str(improper.index) + " " + str(improper.type.index) + " " + " ".join( str(atom.index) for atom in improper.atoms) + "\n") F.write(output) # TYPES if parameters and atoms and self.atoms.atoms: F.write("\nPair Coeffs \n\n") for atom_type in self.atoms.atom_types: F.write( str(atom_type.index) + " " + " ".join(str(param) for param in atom_type.params) + " # " + str(atom_type) + "\n") if bonds and self.atoms.bonds: F.write("\nBond Coeffs \n\n") for bond_type in self.atoms.bond_types: F.write( str(bond_type.index) + " " + " ".join(str(param) for param in bond_type.params) + " # " + str(bond_type) + "\n") if angles and self.atoms.angles: F.write("\nAngle Coeffs \n\n") for angle_type in self.atoms.angle_types: F.write( str(angle_type.index) + " " + " ".join(str(param) for param in angle_type.params) + " # " + str(angle_type) + "\n") if dihedrals and self.atoms.dihedrals: F.write("\nDihedral Coeffs \n\n") for dihedral_type in self.atoms.dihedral_types: if not self._multiple_dihedrals: F.write( str(dihedral_type.index) + " " + " ".join( str(param) for param in dihedral_type.params) + " # " + str(dihedral_type) + "\n") else: # if self._multiple_dihedrals for i in range(len(dihedral_type.index)): F.write( str(dihedral_type.index[i]) + " " + " ".join( str(param) for param in dihedral_type.params[i]) + " # " + str(dihedral_type) + "\n") if impropers and self.atoms.impropers: F.write("\nImproper Coeffs \n\n") for improper_type in self.atoms.improper_types: try: F.write( str(improper_type.index) + " " + " ".join( str(param) for param in improper_type.params) + " # " + str(improper_type) + "\n") except TypeError: print( "WARNING: improper {} without parameters!".format( str(improper_type)))
def get_rot_trans(myposwan): Amat = myposwan.Amat natom_pos = myposwan.natom_pos atoms_frac = myposwan.atoms_frac atoms_symbol= myposwan.atoms_symbol natom_wan = myposwan.natom_wan atom_num = myposwan.atom_num crystal_ase = Atoms(symbols=atoms_symbol,cell=Amat.T, scaled_positions=atoms_frac.T,pbc=True) symmetry = spglib.get_symmetry(crystal_ase) ptgrp = spglib.get_pointgroup(symmetry['rotations']) nsymm = symmetry['rotations'].shape[0] rotmat = symmetry['rotations'] rottrn = symmetry['translations'] rmat0 = np.array(rotmat[0], dtype = np.float64) # get the number of ptrans nptrans = 0 for i in range(nsymm): rmati = np.array(rotmat[i], dtype = np.float64) diff = abs(rmati-rmat0).sum() #print "i ", i+1, "diff", diff if diff < 1.0E-9: nptrans = nptrans + 1 # get the number of distinct rotaions nrot = nsymm/nptrans print "--------------abstrac of symmetry------------------" print("Spacegroup of this cell is %s." % spglib.get_spacegroup(crystal_ase)) print('Point group: ',ptgrp[0]) print "number of total symmetry operations: ", nsymm print "number of distnct rot ", nrot print "nptrans per rotation", nptrans print "######### For details, see ./mysymmop.dat ########" print "" # check whether rotmat are put as typeI # still need to be tested. err=""" Opps, It seems that in spglib, the rotaions_matrix is not put as ----typeI----- rot1/ptran_11 rot2/ptran_21 ... rotn/ptran_n1 ... ... ... rot1/ptran_1m rot2/ptran_2m ... rotn/ptran_nm If put as above, I want to trans it as in vasp/OUTCAR ----typeII----- rot1/ptran_11 rot1/ptran_12 ... rot1/ptran_1m ... ... ... rotn/ptran_n1 rotn/ptran_n2 ... rotn/ptran_nm """ for isym in range(nrot): rmat0 = np.array(rotmat[isym], dtype = np.float64) diff = 0.0 for iptr in range(nptrans): numb = isym + iptr*nrot rmatt = np.array(rotmat[numb], dtype = np.float64) diff = diff + abs(rmatt-rmat0).sum() if diff>1.0E-5: print err if diff>1.0E-5: sys.exit(0) # To change code less, we use type I and set nptrans=1 nptrans = 1 wann_atom_rotmap=np.zeros((nsymm,nptrans,natom_wan), dtype=np.int32) symop = [] f = open('mysymmop.dat', 'w') for isym in range(nsymm): print>>f, (" --------------- %4d ---------------" % (isym + 1)) rot = symmetry['rotations'][isym] trans = symmetry['translations'][isym] print>>f, (" rotation:") for x in rot: print>>f, (" %2d %2d %2d " % (x[0], x[1], x[2])) print>>f, (" translation:") print>>f, (" %8.5f %8.5f %8.5f " % (trans[0], trans[1], trans[2])) print>>f, (" rotmap:") gtrans = trans ptrans = [np.zeros((3),dtype=np.float64)] # rot map for POSCAR rotmap = [] for iatom in range(natom_pos): atoms_frac_new = np.dot(rotmat[isym],atoms_frac[:,iatom]) + rottrn[isym] # find equiv loc as in original cell for jatom in range(natom_pos): frac_diff = atoms_frac[:,jatom]-atoms_frac_new # determine the equivalent between two atoms before and after space group operation. if abs(frac_diff-np.array([round(i) for i in frac_diff],dtype=np.float64)).sum() < 1.0E-3: print>>f, " ", iatom+1, "->", jatom+1 rotmap.append([iatom+1,jatom+1]) # rot map for wann.in. here for iatom in range(natom_wan): target = rotmap[ atom_num[iatom]-1 ][1] wann_atom_rotmap[isym,0,iatom]=atom_num.index(target) symop.append((rotmat[isym],gtrans,rotmap,ptrans)) print>>f, "\n" print>>f, "\n" print>>f,"rotations as for atoms in wannier projection" # rot map of atoms in wannier projection for isymm in range(nsymm): print >>f, "isymm", isymm+1, "---------" for iptr in range(nptrans): print>>f, "trans", 0, gtrans for ii in range(natom_wan): print>>f, "OUTCAR atom rot map:", atom_num[ii], "->", symop[isymm][2][atom_num[ii]-1][1], "=====>", "wannier atom rot map:", ii+1, "->", wann_atom_rotmap[isymm,iptr,ii]+1 f.close() return nsymm, nptrans, symop, wann_atom_rotmap
def wRMSD(atom_1, atom_2, ali): # filter non-aligned atoms atom_1, atom_2 = filter (atom_1, atom_2, ali) #print('wRMSD()| pA len: %d pB len: %d' % (len(atom_1.xyz), len(atom_2.xyz))) pro = [atom_1, atom_2] mmin = min(pro[0].count, pro[1].count) ### STEP 1 ### for p in pro: # resize p.count = mmin p.xyz = np.resize(p.xyz, (mmin, 3)) # cut off extra atoms (shh) p.centerOrigin() # move centroid to Origin ### STEP 2 ### # average Structure avgS = Atoms(np.zeros(pro[0].xyz.shape)) # initialize all 0s for p in pro: avgS.xyz += p.xyz avgS.xyz = avgS.xyz/len(pro) # average w = 1 # weights not used yet weight = np.array([1.0]*p.count) # calculate the Standard Deviation sd = np.float64(0) for p in pro: for atom in range(len(p.xyz)): n = np.linalg.norm(p.xyz[atom] - avgS.xyz[atom]) sd += w * n * n # e is epsilon aka reaaaally small number eps = 1.0*np.power(10.0,-5.0) ### STEP 3 ### while True: for p in pro: # Ri = from Horn's method # Si = Ri * Si r,t = horn(p.xyz, avgS.xyz, weight) #p.xyz = p.xyz*r + t temp = np.outer(np.ones(avgS.xyz.shape[0]), t) p.xyz = np.dot(p.xyz,r.T) + temp ### STEP 4 ### # average Structure avgS.xyz = avgS.xyz*0 # initialize all 0s for p in pro: avgS.xyz += p.xyz avgS.xyz = avgS.xyz/len(pro) # average # calculate the Standard Deviation sdNew = np.float64(0) for p in pro: for atom in range(len(p.xyz)): n = np.linalg.norm(p.xyz[atom] - avgS.xyz[atom]) sdNew += w * n * n ### STEP 5 ### if (sd - sdNew) < eps: break # algorithm terminates else: sd = sdNew wrmsd = math.sqrt(sd / len(p.xyz)) return wrmsd