def read_lammps_dump(fileobj, index=-1, order=True): """Method which reads a LAMMPS dump file. order: Order the particles according to their id. Might be faster to switch it off. """ if isinstance(fileobj, str): f = paropen(fileobj) else: f = fileobj # load everything into memory lines = f.readlines() natoms = 0 images = [] while len(lines) > natoms: line = lines.pop(0) if 'ITEM: TIMESTEP' in line: n_atoms = 0 lo = [] hi = [] tilt = [] id = [] types = [] positions = [] scaled_positions = [] velocities = [] forces = [] quaternions = [] if 'ITEM: NUMBER OF ATOMS' in line: line = lines.pop(0) natoms = int(line.split()[0]) if 'ITEM: BOX BOUNDS' in line: # save labels behind "ITEM: BOX BOUNDS" in # triclinic case (>=lammps-7Jul09) tilt_items = line.split()[3:] for i in range(3): line = lines.pop(0) fields = line.split() lo.append(float(fields[0])) hi.append(float(fields[1])) if (len(fields) >= 3): tilt.append(float(fields[2])) # determine cell tilt (triclinic case!) if (len(tilt) >= 3): # for >=lammps-7Jul09 use labels behind # "ITEM: BOX BOUNDS" to assign tilt (vector) elements ... if (len(tilt_items) >= 3): xy = tilt[tilt_items.index('xy')] xz = tilt[tilt_items.index('xz')] yz = tilt[tilt_items.index('yz')] # ... otherwise assume default order in 3rd column # (if the latter was present) else: xy = tilt[0] xz = tilt[1] yz = tilt[2] else: xy = xz = yz = 0 xhilo = (hi[0] - lo[0]) - (xy**2)**0.5 - (xz**2)**0.5 yhilo = (hi[1] - lo[1]) - (yz**2)**0.5 zhilo = (hi[2] - lo[2]) if xy < 0: if xz < 0: celldispx = lo[0] - xy - xz else: celldispx = lo[0] - xy else: celldispx = lo[0] celldispy = lo[1] celldispz = lo[2] cell = [[xhilo, 0, 0], [xy, yhilo, 0], [xz, yz, zhilo]] celldisp = [[celldispx, celldispy, celldispz]] def add_quantity(fields, var, labels): for label in labels: if label not in atom_attributes: return var.append( [float(fields[atom_attributes[label]]) for label in labels]) if 'ITEM: ATOMS' in line: # (reliably) identify values by labels behind # "ITEM: ATOMS" - requires >=lammps-7Jul09 # create corresponding index dictionary before # iterating over atoms to (hopefully) speed up lookups... atom_attributes = {} for (i, x) in enumerate(line.split()[2:]): atom_attributes[x] = i for n in range(natoms): line = lines.pop(0) fields = line.split() id.append(int(fields[atom_attributes['id']])) types.append(int(fields[atom_attributes['type']])) add_quantity(fields, positions, ['x', 'y', 'z']) add_quantity(fields, scaled_positions, ['xs', 'ys', 'zs']) add_quantity(fields, velocities, ['vx', 'vy', 'vz']) add_quantity(fields, forces, ['fx', 'fy', 'fz']) add_quantity(fields, quaternions, ['c_q[1]', 'c_q[2]', 'c_q[3]', 'c_q[4]']) if order: def reorder(inlist): if not len(inlist): return inlist outlist = [None] * len(id) for i, v in zip(id, inlist): outlist[i - 1] = v return outlist types = reorder(types) positions = reorder(positions) scaled_positions = reorder(scaled_positions) velocities = reorder(velocities) forces = reorder(forces) quaternions = reorder(quaternions) if len(quaternions): images.append( Quaternions(symbols=types, positions=positions, cell=cell, celldisp=celldisp, quaternions=quaternions)) elif len(positions): images.append( Atoms(symbols=types, positions=positions, celldisp=celldisp, cell=cell)) elif len(scaled_positions): images.append( Atoms(symbols=types, scaled_positions=scaled_positions, celldisp=celldisp, cell=cell)) if len(velocities): images[-1].set_velocities(velocities) if len(forces): calculator = SinglePointCalculator(0.0, forces, None, None, images[-1]) images[-1].set_calculator(calculator) return images[index]
def lammps_data_to_ase_atoms( data, colnames, cell, celldisp, pbc=False, atomsobj=Atoms, order=True, specorder=None, prismobj=None, units="metal", ): """Extract positions and other per-atom parameters and create Atoms :param data: per atom data :param colnames: index for data :param cell: cell dimensions :param celldisp: origin shift :param pbc: periodic boundaries :param atomsobj: function to create ase-Atoms object :param order: sort atoms by id. Might be faster to turn off :param specorder: list of species to map lammps types to ase-species (usually .dump files to not contain type to species mapping) :param prismobj: Coordinate transformation between lammps and ase :type prismobj: Prism :param units: lammps units for unit transformation between lammps and ase :returns: Atoms object :rtype: Atoms """ # data array of doubles ids = data[:, colnames.index("id")].astype(int) types = data[:, colnames.index("type")].astype(int) if order: sort_order = np.argsort(ids) ids = ids[sort_order] data = data[sort_order, :] types = types[sort_order] # reconstruct types from given specorder if specorder: types = [specorder[t - 1] for t in types] def get_quantity(labels, quantity=None): try: cols = [colnames.index(label) for label in labels] if quantity: return convert(data[:, cols], quantity, units, "ASE") return data[:, cols] except ValueError: return None # slice data block into columns # + perform necessary conversions to ASE units positions = get_quantity(["x", "y", "z"], "distance") scaled_positions = get_quantity(["xs", "ys", "zs"]) velocities = get_quantity(["vx", "vy", "vz"], "velocity") charges = get_quantity(["q"], "charge") forces = get_quantity(["fx", "fy", "fz"], "force") # !TODO: how need quaternions be converted? quaternions = get_quantity(["c_q[1]", "c_q[2]", "c_q[3]", "c_q[4]"]) # convert cell cell = convert(cell, "distance", units, "ASE") celldisp = convert(celldisp, "distance", units, "ASE") if prismobj: celldisp = prismobj.vector_to_ase(celldisp) cell = prismobj.update_cell(cell) if quaternions: out_atoms = Quaternions( symbols=types, positions=positions, cell=cell, celldisp=celldisp, pbc=pbc, quaternions=quaternions, ) elif positions is not None: # reverse coordinations transform to lammps system # (for all vectors = pos, vel, force) if prismobj: positions = prismobj.vector_to_ase(positions, wrap=True) out_atoms = atomsobj( symbols=types, positions=positions, pbc=pbc, celldisp=celldisp, cell=cell ) elif scaled_positions is not None: out_atoms = atomsobj( symbols=types, scaled_positions=scaled_positions, pbc=pbc, celldisp=celldisp, cell=cell, ) if velocities is not None: if prismobj: velocities = prismobj.vector_to_ase(velocities) out_atoms.set_velocities(velocities) if charges is not None: out_atoms.set_initial_charges(charges) if forces is not None: if prismobj: forces = prismobj.vector_to_ase(forces) # !TODO: use another calculator if available (or move forces # to atoms.property) (other problem: synchronizing # parallel runs) calculator = SinglePointCalculator(out_atoms, energy=0.0, forces=forces) out_atoms.calc = calculator # process the extra columns of fixes, variables and computes # that can be dumped, add as additional arrays to atoms object for colname in colnames: # determine if it is a compute or fix (but not the quaternian) if (colname.startswith('f_') or colname.startswith('v_') or (colname.startswith('c_') and not colname.startswith('c_q['))): out_atoms.new_array(colname, get_quantity([colname]), dtype='float') return out_atoms
def read_a_loop(f): ## Time step line = f.readline() assert 'ITEM: TIMESTEP' in line lo = [] hi = [] tilt = [] id = [] types = [] positions = [] element = [] ## ssrokyz pe = [] ## ssrokyz scaled_positions = [] velocities = [] forces = [] quaternions = [] # Read out timestep line = f.readline() ## Number of atoms line = f.readline() assert 'ITEM: NUMBER OF ATOMS' in line line = f.readline() natoms = int(line.split()[0]) ## Box bounds line = f.readline() assert 'ITEM: BOX BOUNDS' in line # save labels behind "ITEM: BOX BOUNDS" in # triclinic case (>=lammps-7Jul09) tilt_items = line.split()[3:] for i in range(3): line = f.readline() fields = line.split() lo.append(float(fields[0])) hi.append(float(fields[1])) if (len(fields) >= 3): tilt.append(float(fields[2])) # determine cell tilt (triclinic case!) if (len(tilt) >= 3): # for >=lammps-7Jul09 use labels behind # "ITEM: BOX BOUNDS" to assign tilt (vector) elements ... if (len(tilt_items) >= 3): xy = tilt[tilt_items.index('xy')] xz = tilt[tilt_items.index('xz')] yz = tilt[tilt_items.index('yz')] # ... otherwise assume default order in 3rd column # (if the latter was present) else: xy = tilt[0] xz = tilt[1] yz = tilt[2] else: xy = xz = yz = 0 xhilo = (hi[0] - lo[0]) - (xy**2)**0.5 - (xz**2)**0.5 yhilo = (hi[1] - lo[1]) - (yz**2)**0.5 zhilo = (hi[2] - lo[2]) if xy < 0: if xz < 0: celldispx = lo[0] - xy - xz else: celldispx = lo[0] - xy else: celldispx = lo[0] celldispy = lo[1] celldispz = lo[2] cell = [[xhilo, 0, 0], [xy, yhilo, 0], [xz, yz, zhilo]] celldisp = [[celldispx, celldispy, celldispz]] line = f.readline() assert 'ITEM: ATOMS' in line # (reliably) identify values by labels behind # "ITEM: ATOMS" - requires >=lammps-7Jul09 # create corresponding index dictionary before # iterating over atoms to (hopefully) speed up lookups... atom_attributes = {} for (i, x) in enumerate(line.split()[2:]): atom_attributes[x] = i for n in range(natoms): line = f.readline() fields = line.split() id.append(int(fields[atom_attributes['id']])) types.append(int(fields[atom_attributes['type']])) element.append(str(fields[atom_attributes['element']])) ## ssrokyz pe.append(float(fields[atom_attributes['c_1']])) ## ssrokyz add_quantity(fields, positions, ['x', 'y', 'z'], atom_attributes) add_quantity(fields, scaled_positions, ['xs', 'ys', 'zs'], atom_attributes) add_quantity(fields, velocities, ['vx', 'vy', 'vz'], atom_attributes) add_quantity(fields, forces, ['fx', 'fy', 'fz'], atom_attributes) add_quantity(fields, quaternions, ['c_q[1]', 'c_q[2]', 'c_q[3]', 'c_q[4]'], atom_attributes) if order: types = reorder(types, id) element = reorder(element, id) ## ssrokyz pe = reorder(pe, id) ## ssrokyz positions = reorder(positions, id) scaled_positions = reorder(scaled_positions, id) velocities = reorder( np.array(velocities) / units.fs / 1e3, id ) ## ssrokyz ## lammps metal unit: Ang./picosec ## units.fs *1e3 = units.ps forces = reorder(forces, id) quaternions = reorder(quaternions, id) ## Make 'Atoms' object if len(quaternions): atoms = Quaternions( symbols=element, positions=positions, cell=cell, celldisp=celldisp, quaternions=quaternions, ) elif len(positions): atoms = atomsobj( symbols=element, positions=positions, celldisp=celldisp, cell=cell, ) elif len(scaled_positions): atoms = atomsobj( symbols=element, scaled_positions=scaled_positions, celldisp=celldisp, cell=cell, ) if len(velocities): atoms.set_velocities(velocities) if len(forces): calculator = SinglePointCalculator(atoms, energy=0.0, forces=forces) atoms.set_calculator(calculator) if len(pe): atoms._calc.results['atomic_energies'] = pe return atoms
def read_lammps_dump(fileobj, index=-1): """Method which reads a LAMMPS dump file.""" if isinstance(fileobj, str): f = paropen(fileobj) else: f = fileobj # load everything into memory lines = f.readlines() natoms = 0 images = [] while len(lines) > natoms: line = lines.pop(0) if 'ITEM: TIMESTEP' in line: n_atoms = 0 lo = [] ; hi = [] ; tilt = [] id = [] ; type = [] positions = [] velocities = [] forces = [] quaternions = [] if 'ITEM: NUMBER OF ATOMS' in line: line = lines.pop(0) natoms = int(line.split()[0]) if 'ITEM: BOX BOUNDS' in line: # save labels behind "ITEM: BOX BOUNDS" in triclinic case (>=lammps-7Jul09) tilt_items = line.split()[3:] for i in range(3): line = lines.pop(0) fields = line.split() lo.append(float(fields[0])) hi.append(float(fields[1])) if (len(fields) >= 3): tilt.append(float(fields[2])) # determine cell tilt (triclinic case!) if (len(tilt) >= 3): # for >=lammps-7Jul09 use labels behind "ITEM: BOX BOUNDS" to assign tilt (vector) elements ... if (len(tilt_items) >= 3): xy = tilt[tilt_items.index('xy')] xz = tilt[tilt_items.index('xz')] yz = tilt[tilt_items.index('yz')] # ... otherwise assume default order in 3rd column (if the latter was present) else: xy = tilt[0] xz = tilt[1] yz = tilt[2] else: xy = xz = yz = 0 xhilo = (hi[0] - lo[0]) - xy - xz yhilo = (hi[1] - lo[1]) - yz zhilo = (hi[2] - lo[2]) cell = [[xhilo,0,0],[xy,yhilo,0],[xz,yz,zhilo]] def add_quantity(fields, var, labels): for label in labels: if label not in atom_attributes: return var.append([float(fields[atom_attributes[label]]) for label in labels]) if 'ITEM: ATOMS' in line: # (reliably) identify values by labels behind "ITEM: ATOMS" - requires >=lammps-7Jul09 # create corresponding index dictionary before iterating over atoms to (hopefully) speed up lookups... atom_attributes = {} for (i, x) in enumerate(line.split()[2:]): atom_attributes[x] = i for n in range(natoms): line = lines.pop(0) fields = line.split() id.append( int(fields[atom_attributes['id']]) ) type.append( int(fields[atom_attributes['type']]) ) add_quantity(fields, positions, ['x', 'y', 'z']) add_quantity(fields, velocities, ['vx', 'vy', 'vz']) add_quantity(fields, forces, ['fx', 'fy', 'fz']) add_quantity(fields, quaternions, ['c_q[1]', 'c_q[2]', 'c_q[3]', 'c_q[4]']) if len(quaternions): images.append(Quaternions(symbols=type, positions=positions, cell=cell, quaternions=quaternions)) else: images.append(Atoms(symbols=type, positions=positions, cell=cell)) return images[index]
def lammps_data_to_ase_atoms( data, colnames, cell, celldisp, pbc=False, atomsobj=Atoms, order=True, specorder=None, prismobj=None, units="metal", ): """Extract positions and other per-atom parameters and create Atoms :param data: per atom data :param colnames: index for data :param cell: cell dimensions :param celldisp: origin shift :param pbc: periodic boundaries :param atomsobj: function to create ase-Atoms object :param order: sort atoms by id. Might be faster to turn off. Disregarded in case `id` column is not given in file. :param specorder: list of species to map lammps types to ase-species (usually .dump files to not contain type to species mapping) :param prismobj: Coordinate transformation between lammps and ase :type prismobj: Prism :param units: lammps units for unit transformation between lammps and ase :returns: Atoms object :rtype: Atoms """ # read IDs if given and order if needed if "id" in colnames: ids = data[:, colnames.index("id")].astype(int) if order: sort_order = np.argsort(ids) data = data[sort_order, :] # determine the elements if "element" in colnames: # priority to elements written in file elements = data[:, colnames.index("element")] elif "type" in colnames: # fall back to `types` otherwise elements = data[:, colnames.index("type")].astype(int) # reconstruct types from given specorder if specorder: elements = [specorder[t - 1] for t in elements] else: # todo: what if specorder give but no types? # in principle the masses could work for atoms, but that needs # lots of cases and new code I guess raise ValueError("Cannot determine atom types form LAMMPS dump file") def get_quantity(labels, quantity=None): try: cols = [colnames.index(label) for label in labels] if quantity: return convert(data[:, cols].astype(float), quantity, units, "ASE") return data[:, cols].astype(float) except ValueError: return None # Positions positions = None scaled_positions = None if "x" in colnames: # doc: x, y, z = unscaled atom coordinates positions = get_quantity(["x", "y", "z"], "distance") elif "xs" in colnames: # doc: xs,ys,zs = scaled atom coordinates scaled_positions = get_quantity(["xs", "ys", "zs"]) elif "xu" in colnames: # doc: xu,yu,zu = unwrapped atom coordinates positions = get_quantity(["xu", "yu", "zu"], "distance") elif "xsu" in colnames: # xsu,ysu,zsu = scaled unwrapped atom coordinates scaled_positions = get_quantity(["xsu", "ysu", "zsu"]) else: raise ValueError("No atomic positions found in LAMMPS output") velocities = get_quantity(["vx", "vy", "vz"], "velocity") charges = get_quantity(["q"], "charge") forces = get_quantity(["fx", "fy", "fz"], "force") # !TODO: how need quaternions be converted? quaternions = get_quantity(["c_q[1]", "c_q[2]", "c_q[3]", "c_q[4]"]) # convert cell cell = convert(cell, "distance", units, "ASE") celldisp = convert(celldisp, "distance", units, "ASE") if prismobj: celldisp = prismobj.vector_to_ase(celldisp) cell = prismobj.update_cell(cell) if quaternions: out_atoms = Quaternions( symbols=elements, positions=positions, cell=cell, celldisp=celldisp, pbc=pbc, quaternions=quaternions, ) elif positions is not None: # reverse coordinations transform to lammps system # (for all vectors = pos, vel, force) if prismobj: positions = prismobj.vector_to_ase(positions, wrap=True) out_atoms = atomsobj(symbols=elements, positions=positions, pbc=pbc, celldisp=celldisp, cell=cell) elif scaled_positions is not None: out_atoms = atomsobj( symbols=elements, scaled_positions=scaled_positions, pbc=pbc, celldisp=celldisp, cell=cell, ) if velocities is not None: if prismobj: velocities = prismobj.vector_to_ase(velocities) out_atoms.set_velocities(velocities) if charges is not None: out_atoms.set_initial_charges(charges) if forces is not None: if prismobj: forces = prismobj.vector_to_ase(forces) # !TODO: use another calculator if available (or move forces # to atoms.property) (other problem: synchronizing # parallel runs) calculator = SinglePointCalculator(out_atoms, energy=0.0, forces=forces) out_atoms.calc = calculator # process the extra columns of fixes, variables and computes # that can be dumped, add as additional arrays to atoms object for colname in colnames: # determine if it is a compute or fix (but not the quaternian) if (colname.startswith('f_') or colname.startswith('v_') or (colname.startswith('c_') and not colname.startswith('c_q['))): out_atoms.new_array(colname, get_quantity([colname]), dtype='float') return out_atoms