class LammpsInteractive(LammpsBase, GenericInteractive): def __init__(self, project, job_name): super(LammpsInteractive, self).__init__(project, job_name) self._check_opened = False self._interactive_run_command = None self._interactive_grand_canonical = True if "stress" in self.interactive_output_functions.keys(): del self.interactive_output_functions["stress"] @property def structure(self): return GenericInteractive.structure.fget(self) @structure.setter def structure(self, structure): self._prism = UnfoldingPrism(structure.cell) GenericInteractive.structure.fset(self, structure) def get_structure(self, iteration_step=-1, wrap_atoms=True): return GenericInteractive.get_structure(self, iteration_step=iteration_step, wrap_atoms=wrap_atoms) def _interactive_lib_command(self, command): self._logger.debug("Lammps library: " + command) self._interactive_library.command(command) def interactive_positions_getter(self): positions = np.reshape( np.array(self._interactive_library.gather_atoms("x", 1, 3)), (len(self.structure), 3), ) if np.matrix.trace(self._prism.R) != 3: positions = np.matmul(positions, self._prism.R.T) return positions.tolist() def interactive_positions_setter(self, positions): if np.matrix.trace(self._prism.R) != 3: positions = np.array(positions).reshape(-1, 3) positions = np.matmul(positions, self._prism.R) positions = np.array(positions).flatten() if self.server.run_mode.interactive and self.server.cores == 1: self._interactive_library.scatter_atoms( "x", 1, 3, (len(positions) * c_double)(*positions)) else: self._interactive_library.scatter_atoms("x", 1, 3, positions) self._interactive_lib_command("change_box all remap") def interactive_cells_getter(self): cc = np.array([ [self._interactive_library.get_thermo("lx"), 0, 0], [ self._interactive_library.get_thermo("xy"), self._interactive_library.get_thermo("ly"), 0, ], [ self._interactive_library.get_thermo("xz"), self._interactive_library.get_thermo("yz"), self._interactive_library.get_thermo("lz"), ], ]) return self._prism.unfold_cell(cc) def interactive_cells_setter(self, cell): self._prism = UnfoldingPrism(cell) lx, ly, lz, xy, xz, yz = self._prism.get_lammps_prism() if np.matrix.trace(self._prism.R) != 3: warnings.warn( "Warning: setting upper trangular matrix might slow down the calculation" ) is_skewed = self._prism.is_skewed() is_scaled = self.structure._is_scaled if is_skewed and is_scaled: self._interactive_lib_command( "change_box all x final 0 %f y final 0 %f z final 0 %f \ xy final %f xz final %f yz final %f triclinic remap units box" % (lx, ly, lz, xy, xz, yz)) elif is_skewed and not is_scaled: self._interactive_lib_command( "change_box all x final 0 %f y final 0 %f z final 0 %f \ xy final %f xz final %f yz final %f triclinic units box" % (lx, ly, lz, xy, xz, yz)) elif not is_skewed and is_scaled: self._interactive_lib_command( "change_box all x final 0 %f y final 0 %f z final 0 %f remap units box" % (lx, ly, lz)) else: # is neither skewed nor scaled self._interactive_lib_command( "change_box all x final 0 %f y final 0 %f z final 0 %f units box" % (lx, ly, lz)) def interactive_volume_getter(self): return self._interactive_library.get_thermo("vol") def interactive_forces_getter(self): ff = np.reshape( np.array(self._interactive_library.gather_atoms("f", 1, 3)), (len(self.structure), 3), ) if np.matrix.trace(self._prism.R) != 3: ff = np.matmul(ff, self._prism.R.T) return ff.tolist() def interactive_execute(self): self._interactive_lib_command(self._interactive_run_command) def _interactive_lammps_input(self): del self.input.control["dump___1"] del self.input.control["dump_modify___1"] for key, value in zip(self.input.control.dataset["Parameter"], self.input.control.dataset["Value"]): if key in [ "read_data", "units", "dimension", "boundary", "atom_style", "atom_modify", "include", "run", "minimize", ]: continue else: self._interactive_lib_command(" ".join( key.split(self.input.control.multi_word_separator)) + " " + str(value)) def _interactive_set_potential(self): potential_lst = [] if self.input.potential.files is not None: for potential in self.input.potential.files: if not os.path.exists(potential): raise ValueError("Potential not found: ", potential) potential_lst.append([potential.split("/")[-1], potential]) style_full = self.input.control["atom_style"] == "full" for line in self.input.potential.get_string_lst(): for potential in potential_lst: if ' ' + potential[0] in line: line = line.replace(' ' + potential[0], ' ' + potential[1]) # Don't write the kspace_style or pair style commands if the atom style is "full" if not (style_full and ("kspace" in line or "pair" in line)): self._interactive_lib_command(line.split("\n")[0]) if len(potential_lst) == 0: self._interactive_lib_command(line.split("\n")[0]) if style_full: # Currently supports only water molecules. Please feel free to expand this self._interactive_water_setter() def _executable_activate_mpi(self): if (self.server.run_mode.interactive or self.server.run_mode.interactive_non_modal): pass else: super(LammpsInteractive, self)._executable_activate_mpi() def _reset_interactive_run_command(self): df = pd.DataFrame(self.input.control.dataset) self._interactive_run_command = " ".join(df.T[df.index[-1]].values) def interactive_initialize_interface(self): if self.server.run_mode.interactive and self.server.cores == 1: lammps = getattr(importlib.import_module("lammps"), "lammps") self._interactive_library = lammps(cmdargs=["-screen", "none"]) else: self._create_working_directory() self._interactive_library = LammpsLibrary( cores=self.server.cores, working_directory=self.working_directory) if not all(self.structure.pbc): self.input.control["boundary"] = " ".join( ["p" if coord else "f" for coord in self.structure.pbc]) self._reset_interactive_run_command() self.interactive_structure_setter(self.structure) def calc_minimize(self, e_tol=0.0, f_tol=1e-4, max_iter=100000, pressure=None, n_print=100, style='cg'): # Docstring set programmatically -- Please ensure that changes to signature or defaults stay consistent! if self.server.run_mode.interactive_non_modal: warnings.warn( "calc_minimize() is not implemented for the non modal interactive mode use calc_static()!" ) super(LammpsInteractive, self).calc_minimize( e_tol=e_tol, f_tol=f_tol, max_iter=max_iter, pressure=pressure, n_print=n_print, style=style, ) if self.interactive_is_activated() and ( self.server.run_mode.interactive or self.server.run_mode.interactive_non_modal): self.interactive_structure_setter(self.structure) calc_minimize.__doc__ = LammpsControl.calc_minimize.__doc__ def calc_md( self, temperature=None, pressure=None, n_ionic_steps=1000, time_step=1.0, n_print=100, temperature_damping_timescale=100.0, pressure_damping_timescale=1000.0, seed=None, tloop=None, initial_temperature=None, langevin=False, delta_temp=None, delta_press=None, ): super(LammpsInteractive, self).calc_md( temperature=temperature, pressure=pressure, n_ionic_steps=n_ionic_steps, time_step=time_step, n_print=n_print, temperature_damping_timescale=temperature_damping_timescale, pressure_damping_timescale=pressure_damping_timescale, seed=seed, tloop=tloop, initial_temperature=initial_temperature, langevin=langevin, delta_temp=delta_temp, delta_press=delta_press, ) if self.interactive_is_activated() and ( self.server.run_mode.interactive or self.server.run_mode.interactive_non_modal): self.interactive_structure_setter(self.structure) def run_if_interactive(self): if self._generic_input["calc_mode"] in ["md", "vcsgc"]: self.input.control["run"] = self._generic_input["n_print"] super(LammpsInteractive, self).run_if_interactive() self._reset_interactive_run_command() counter = 0 iteration_max = int(self._generic_input["n_ionic_steps"] / self._generic_input["n_print"]) while counter < iteration_max: self.interactive_execute() self.interactive_collect() counter += 1 else: super(LammpsInteractive, self).run_if_interactive() self.interactive_execute() self.interactive_collect() def run_if_interactive_non_modal(self): if not self._interactive_fetch_completed: print("Warning: interactive_fetch being effectuated") self.interactive_fetch() super(LammpsInteractive, self).run_if_interactive() self.interactive_execute() self._interactive_fetch_completed = False def interactive_fetch(self): if (self._interactive_fetch_completed and self.server.run_mode.interactive_non_modal): print("First run and then fetch") else: self.interactive_collect() self._logger.debug("interactive run - done") def interactive_structure_setter(self, structure): self._interactive_lib_command("clear") self._set_selective_dynamics() self._interactive_lib_command("units " + self.input.control["units"]) self._interactive_lib_command("dimension " + str(self.input.control["dimension"])) self._interactive_lib_command("boundary " + self.input.control["boundary"]) self._interactive_lib_command("atom_style " + self.input.control["atom_style"]) self._interactive_lib_command("atom_modify map array") self._prism = UnfoldingPrism(structure.cell) if np.matrix.trace(self._prism.R) != 3: warnings.warn( "Warning: setting upper trangular matrix might slow down the calculation" ) xhi, yhi, zhi, xy, xz, yz = self._prism.get_lammps_prism() if self._prism.is_skewed(): self._interactive_lib_command("region 1 prism" + " 0.0 " + str(xhi) + " 0.0 " + str(yhi) + " 0.0 " + str(zhi) + " " + str(xy) + " " + str(xz) + " " + str(yz) + " units box") else: self._interactive_lib_command("region 1 block" + " 0.0 " + str(xhi) + " 0.0 " + str(yhi) + " 0.0 " + str(zhi) + " units box") el_struct_lst = self.structure.get_species_symbols() el_obj_lst = self.structure.get_species_objects() el_eam_lst = self.input.potential.get_element_lst() if self.input.control["atom_style"] == "full": self._interactive_lib_command("create_box " + str(len(el_eam_lst)) + " 1 " + "bond/types 1 " + "angle/types 1 " + "extra/bond/per/atom 2 " + "extra/angle/per/atom 2 ") else: self._interactive_lib_command("create_box " + str(len(el_eam_lst)) + " 1") el_dict = {} for id_eam, el_eam in enumerate(el_eam_lst): if el_eam in el_struct_lst: id_el = list(el_struct_lst).index(el_eam) el = el_obj_lst[id_el] el_dict[el] = id_eam + 1 self._interactive_lib_command("mass {0:3d} {1:f}".format( id_eam + 1, el.AtomicMass)) else: self._interactive_lib_command("mass {0:3d} {1:f}".format( id_eam + 1, 1.00)) self._interactive_lib_command("create_atoms 1 random " + str(len(structure)) + " 12345 1") positions = structure.positions.flatten() if np.matrix.trace(self._prism.R) != 3: positions = np.array(positions).reshape(-1, 3) positions = np.matmul(positions, self._prism.R) positions = positions.flatten() elem_all = np.array( [el_dict[el] for el in structure.get_chemical_elements()]) if self.server.run_mode.interactive and self.server.cores == 1: self._interactive_library.scatter_atoms( "x", 1, 3, (len(positions) * c_double)(*positions)) self._interactive_library.scatter_atoms( "type", 0, 1, (len(elem_all) * c_int)(*elem_all)) else: self._interactive_library.scatter_atoms("x", 1, 3, positions) self._interactive_library.scatter_atoms("type", 0, 1, elem_all) self._interactive_lib_command("change_box all remap") # if self.input.control['atom_style'] == "full": # Do not scatter or manipulate when you have water/ use atom_style full in your system # self._interactive_water_setter() self._interactive_lammps_input() self._interactive_set_potential() def _interactive_water_setter(self): """ This function writes the bonds for water molecules present in the structure. It is assumed that only intact water molecules are present and the H atoms are within 1.3 $\AA$ of each O atom. Once the neighbor list is generated, the bonds and angles are created. This function needs to be generalized/extended to account for dissociated water. This function can also be used as an example to create bonds between other molecules. """ neighbors = self.structure.get_neighbors(cutoff=1.3) o_indices = self.structure.select_index("O") h_indices = self.structure.select_index("H") h1_indices = np.intersect1d( np.vstack(neighbors.indices[o_indices])[:, 0], h_indices) h2_indices = np.intersect1d( np.vstack(neighbors.indices[o_indices])[:, 1], h_indices) o_ind_str = (np.array2string(o_indices + 1).replace("[", "").replace( "]", "").strip()) h1_ind_str = (np.array2string(h1_indices + 1).replace("[", "").replace( "]", "").strip()) h2_ind_str = (np.array2string(h2_indices + 1).replace("[", "").replace( "]", "").strip()) group_o = "group Oatoms id {}".format(o_ind_str).replace(" ", " ") group_h1 = "group H1atoms id {}".format(h1_ind_str).replace(" ", " ") group_h2 = "group H2atoms id {}".format(h2_ind_str).replace(" ", " ") self._interactive_lib_command(group_o) self._interactive_lib_command(group_h1) self._interactive_lib_command(group_h2) # A dummy pair style that does not have any Coulombic interactions needs to be initialized to create the bonds self._interactive_lib_command("pair_style lj/cut 2.5") self._interactive_lib_command("pair_coeff * * 0.0 0.0") self._interactive_lib_command( "create_bonds many Oatoms H1atoms 1 0.7 1.4") self._interactive_lib_command( "create_bonds many Oatoms H2atoms 1 0.7 1.4") for i, o_ind in enumerate(o_indices): self._interactive_lib_command( "create_bonds single/angle 1 {} {} {}".format( int(h1_indices[i]) + 1, int(o_ind) + 1, int(h2_indices[i]) + 1)) # Now the actual pair styles are written self._interactive_lib_command("pair_style " + self.input.potential["pair_style"]) values = np.array(self.input.potential._dataset["Value"]) pair_val = values[[ "pair_coeff" in val for val in self.input.potential._dataset["Parameter"] ]] for val in pair_val: self._interactive_lib_command("pair_coeff " + val) self._interactive_lib_command("kspace_style " + self.input.potential["kspace_style"]) def from_hdf(self, hdf=None, group_name=None): """ Recreates instance from the hdf5 file Args: hdf (str): Path to the hdf5 file group_name (str): Name of the group which contains the object """ super(LammpsInteractive, self).from_hdf(hdf=hdf, group_name=group_name) self.species_from_hdf() def collect_output(self): if (self.server.run_mode.interactive or self.server.run_mode.interactive_non_modal): pass else: super(LammpsInteractive, self).collect_output() def update_potential(self): self._interactive_lib_command(self.potential.Config[0][0]) self._interactive_lib_command(self.potential.Config[0][1]) def interactive_indices_getter(self): lammps_indices = np.array( self._interactive_library.gather_atoms("type", 0, 1)) indices = self.remap_indices(lammps_indices) return indices.tolist() def interactive_indices_setter(self, indices): el_struct_lst = self._structure_current.get_species_symbols() el_obj_lst = self._structure_current.get_species_objects() el_eam_lst = self.input.potential.get_element_lst() el_dict = {} for id_eam, el_eam in enumerate(el_eam_lst): if el_eam in el_struct_lst: id_el = list(el_struct_lst).index(el_eam) el = el_obj_lst[id_el] el_dict[el] = id_eam + 1 elem_all = np.array( [el_dict[self._structure_current.species[el]] for el in indices]) if self.server.run_mode.interactive and self.server.cores == 1: self._interactive_library.scatter_atoms( "type", 0, 1, (len(elem_all) * c_int)(*elem_all)) else: self._interactive_library.scatter_atoms("type", 0, 1, elem_all) def interactive_energy_pot_getter(self): return self._interactive_library.get_thermo("pe") def interactive_energy_tot_getter(self): return self._interactive_library.get_thermo("etotal") def interactive_steps_getter(self): return self._interactive_library.get_thermo("step") def interactive_temperatures_getter(self): return self._interactive_library.get_thermo("temp") def interactive_stress_getter(self): """ This gives back an Nx3x3 array of stress/atom defined in http://lammps.sandia.gov/doc/compute_stress_atom.html Keep in mind that it is stress*volume in eV. Further discussion can be found on the website above. Returns: numpy.array: Nx3x3 np array of stress/atom """ if not "stress" in self.interactive_cache.keys(): self._interactive_lib_command("compute st all stress/atom NULL") self._interactive_lib_command("run 0") self.interactive_cache["stress"] = [] id_lst = self._interactive_library.extract_atom("id", 0) id_lst = np.array([id_lst[i] for i in range(len(self.structure))]) - 1 id_lst = np.arange(len(id_lst))[np.argsort(id_lst)] ind = np.array([0, 3, 4, 3, 1, 5, 4, 5, 2]) ss = self._interactive_library.extract_compute("st", 1, 2) ss = np.array([ ss[i][j] for i in range(len(self.structure)) for j in range(6) ]).reshape(-1, 6)[id_lst] ss = ss[:, ind].reshape( len(self.structure), 3, 3) / constants.eV * constants.bar * constants.angstrom**3 if np.matrix.trace(self._prism.R) != 3: ss = np.einsum('ij,njk->nik', self._prism.R, ss) ss = np.einsum('nij,kj->nik', ss, self._prism.R) return ss def interactive_pressures_getter(self): pp = np.array([ [ self._interactive_library.get_thermo("pxx"), self._interactive_library.get_thermo("pxy"), self._interactive_library.get_thermo("pxz"), ], [ self._interactive_library.get_thermo("pxy"), self._interactive_library.get_thermo("pyy"), self._interactive_library.get_thermo("pyz"), ], [ self._interactive_library.get_thermo("pxz"), self._interactive_library.get_thermo("pyz"), self._interactive_library.get_thermo("pzz"), ], ]) if np.matrix.trace(self._prism.R) != 3: pp = np.dot(np.dot(self._prism.R, pp), self._prism.R.T) return pp / 10000 # bar -> GPa def interactive_close(self): if self.interactive_is_activated(): self._interactive_library.close() with self.project_hdf5.open("output") as h5: if "interactive" in h5.list_groups(): for key in h5["interactive"].list_nodes(): h5["generic/" + key] = h5["interactive/" + key] super(LammpsInteractive, self).interactive_close()
def collect_dump_file(self, file_name="dump.out", cwd=None): """ general purpose routine to extract static from a lammps dump file Args: file_name: cwd: Returns: """ file_name = self.job_file_name(file_name=file_name, cwd=cwd) tag_dict = {"ITEM: TIMESTEP": {"arg": "0", "rows": 1, "h5": "time"}, # "ITEM: NUMBER OF ATOMS": {"arg": "0", # "rows": 1, # "h5": "number_of_atoms"}, "ITEM: BOX BOUNDS": {"arg": "0", "rows": 3, "h5": "cells", "func": to_amat}, "ITEM: ATOMS": {"arg": ":,:", "rows": len(self.structure), "splitArg": True} } h5_dict = {"id": "id", "type": "type", "xsu": "coord_xs", "ysu": "coord_ys", "zsu": "coord_zs", "f_ave[1]": "coord_xs", "f_ave[2]": "coord_ys", "f_ave[3]": "coord_zs", "fx": "force_x", "fy": "force_y", "fz": "force_z", } lammps_dict = None lf = Logstatus() lf.extract_file(file_name=file_name, tag_dict=tag_dict, h5_dict=h5_dict, key_dict=lammps_dict) lf.combine_xyz('force_x', 'force_y', 'force_z', 'forces') lf.combine_xyz('coord_xs', 'coord_ys', 'coord_zs', 'positions') prism = UnfoldingPrism(self.structure.cell, digits=15) rel_positions = list() for ind, (pos, forc, cel) in enumerate( zip(lf.status_dict["positions"], lf.status_dict["forces"], lf.status_dict["cells"])): cell = cel[1] positions = pos[1] forces = forc[1] # rotation matrix from lammps(unfolded) cell to original cell rotation_lammps2orig = np.linalg.inv(prism.R) # convert from scaled positions to absolute in lammps cell positions = np.array([np.dot(cell.T, r) for r in positions]) # rotate positions from lammps to original positions_atoms = np.array([np.dot(np.array(r), rotation_lammps2orig) for r in positions]) # rotate forces from lammps to original cell forces_atoms = np.array([np.dot(np.array(f), rotation_lammps2orig) for f in forces]) # unfold cell cell = prism.unfold_cell(cell) # rotate cell from unfolded lammps to original cell_atoms = np.array([np.dot(np.array(f), rotation_lammps2orig) for f in cell]) lf.status_dict["positions"][ind][1] = positions_atoms rel_positions.append(np.dot(positions_atoms, np.linalg.inv(cell_atoms))) lf.status_dict["forces"][ind][1] = forces_atoms lf.status_dict["cells"][ind][1] = cell_atoms del lf.status_dict['id'] del lf.status_dict['type'] unwrapped_rel_pos = unwrap_coordinates(positions=np.array(rel_positions), is_relative=True) unwrapped_pos = list() # print(np.shape(unwrapped_rel_pos)) for i, cell in enumerate(lf.status_dict["cells"]): unwrapped_pos.append(np.dot(np.array(unwrapped_rel_pos[i]), cell[1])) lf.status_dict["unwrapped_positions"] = list() for pos in unwrapped_pos: lf.status_dict["unwrapped_positions"].append([[0], pos]) with self.project_hdf5.open("output/generic") as hdf_output: lf.to_hdf(hdf_output) return lf