def minimize(self, cluster: Cluster, *args, **kwargs) -> Cluster: """ Method to locally minimise a cluster of Lennard-Jones particles. Uses the L-BFGS-B method implemented within scipy.minimize. Attributes: coordinates: np.array(shape=(number_of_particles, 3), dtype=float) array of coordinates kwargs: Dict containing any other keyword arguments to be passed to the scipy optimizer molecules: list(int), optional, Returns ------- result_dict{'coordinates': optimised structure coordinates, 'energy': final energy of the cluster, 'success': True if successfully minimised} """ positions = list(cluster.get_particle_positions()) coordinates = positions[0].flatten() # args = {"sigma": self.sigma, "epsilon": self.epsilon4, "base_exp": 6} result = scipy.optimize.minimize( fun=self.get_energy, x0=coordinates, # , args=args, method='L-BFGS-B', jac=self.get_jacobian) positions = (result.x.reshape( (self.n_atoms, 3)), positions[1], positions[2]) cluster.set_particle_positions(positions) cluster.cost = result.fun return cluster
def minimize(self, cluster: Cluster, *args, **kwargs) -> Cluster: """ Method to locally minimise a cluster of Lennard-Jones particles. Uses the L-BFGS-B method implemented within scipy.minimize. Args: cluster: Cluster instance, required, cluster instance to be minimized kwargs: Dict containing any other keyword arguments to be passed to the scipy optimizer Returns: result_dict{'coordinates': optimised structure coordinates, 'energy': final energy of the cluster, 'success': True if successfully minimised} """ coords, ids, labels = cluster.get_particle_positions() coordinates = coords result = minimize(fun=self.get_energy, x0=coordinates.flatten(), method='L-BFGS-B', jac=self.get_jacobian, *args, **kwargs) if not result.success: print("Optimization failed") cluster.set_particle_positions( (result.x.reshape(coordinates.shape), ids, labels)) cluster.cost = result.fun return cluster
def align_clusters(c1: Cluster, c2: Cluster) -> None: """Employs the Kabsch algorithm to align two clusters. c1 and c2 will be modified in-place See: Kabsch, Wolfgang, (1976) "A solution of the best rotation to relate two sets of vectors", Acta Crystallographica 32:922 Args: c1, (Cluster): required c2, (Cluster): required Returns: None """ # Transform both clusters CoM to the origin. c1.center() c2.center() coords_mols_labels_c1 = c1.get_particle_positions() coords_c1 = coords_mols_labels_c1[0] coords_mols_labels_c2 = c2.get_particle_positions() coords_c2 = coords_mols_labels_c2[0] # Calculate covarience matrix A = np.dot(coords_c2.transpose(), coords_c1) # Single value decomp u, s, v = np.linalg.svd(A) if np.linalg.det(u) * np.linalg.det(v) + 1.0 < 1e-8: s[-1] = -s[-1] u[:, -1] = -u[:, -1] rot_mat = np.dot(u, v).transpose() new_coordinates = [] for c in coords_c2: new_coordinates.append(np.dot(c, rot_mat)) new_coordinates = np.array(new_coordinates) c2.set_particle_positions( (new_coordinates, coords_mols_labels_c2[1], coords_mols_labels_c2[2]))
def minimize(self, cluster: Cluster) -> Cluster: self.step_size = self.initial_step step = 0 _ = cluster.get_particle_positions() # n_coords = len(_[1]) self.coords = _[0] np.random.shuffle(self.coords) last_energy = self.pot.get_energy(cluster) converged = False while step <= self.max_steps and not converged: converged = True step += 1 for idx in range(len(self.coords)): self._take_step(self.coords[idx]) cluster.set_particle_positions((self.coords, _[1], _[2])) new_energy = self.pot.get_energy(cluster) # print(new_energy, last_energy) dE = new_energy - last_energy last_energy = new_energy self.update() if abs(dE) >= self.convergence_gradient: converged = False # print(f"Exiting in {step} steps") # for i in self.coords: # print("Cl " + " ".join([str(a) for a in i])) cluster.minimum = True return cluster
def minimize(self, cluster: Cluster, *args, **kwargs) -> Cluster: name = self.get_directory() raw_coordinates = cluster.get_particle_positions() coordinates = self.format_XYZ(raw_coordinates[0], raw_coordinates[2]) tag_dict = {"<XYZ>": coordinates, "<NAME>": name} run_dir = self.work_dir + name input_file_name = run_dir + "/input.nw" output_file_name = run_dir + "/output.out" self.insert_to_template(template=self.minimize_template, out_file=input_file_name, target_dict=tag_dict) commands = [ self.run_string, f"{input_file_name}", f"> {output_file_name}" ] commands = ' '.join(commands) self.log.info(f"Starting NWChem with commands: {commands}") nwchem_process = subprocess.Popen(commands, cwd=run_dir, shell=True) exit_code = nwchem_process.wait() if exit_code == 134: try: raise DFTExitedUnexpectedlyError( f"NWChem exited unexpectedly with exitcode: {exit_code}\n") except DFTExitedUnexpectedlyError as error: self.log.exception(error) raise if exit_code != 0: try: raise DFTExitedUnexpectedlyError( f"NWChem exited unexpectedly with exitcode: {exit_code}\n") except DFTExitedUnexpectedlyError as error: self.log.exception(error) raise else: self.log.info( f"NWChem exited successfully. Exit code: {exit_code}") output_parser = NWChemOutputParser(output_file_name) output_parser.parse() final_structure = output_parser.final_structure try: assert all(raw_coordinates[2]) == all(final_structure[0]) except AssertionError as error: self.log.exception( f"Atoms out of order! {raw_coordinates[2]} != {final_structure[0]}\n{error}" ) cluster.set_particle_positions( (final_structure[1], raw_coordinates[1], final_structure[0])) return cluster
def run_DeMonNano( self, cluster: Cluster, dir_name: str, optimize: bool = False): # TODO move minimize and energy to here """Common interface to DeMonNano""" if dir_name is not None: dir_name = os.path.abspath(dir_name) else: dir_name = os.path.abspath(self.get_directory()) inp_fname = dir_name + "/deMon.inp" out_fname = dir_name + "/deMon.out" shutil.copyfile("SCC-SLAKO", dir_name + "/SCC-SLAKO") shutil.copyfile("SLAKO", dir_name + "/SLAKO") coords, molecule_ids, atom_labels = cluster.get_particle_positions() Natoms = len(molecule_ids) xyz_formatted_coordinates = self.format_XYZ(coords, atom_labels) with work_dir(): os.chdir(dir_name) # Change into the scratch dir. tag_dict = {"<XYZ>": xyz_formatted_coordinates} if optimize: template = self.minimize_template else: template = self.energy_template self.insert_to_template(template=self.work_dir + template, out_file=inp_fname, target_dict=tag_dict) with open("error_file", "w") as ef: # self.run_string should just be the location of the deMonNano executable dftb_process = subprocess.Popen([self.run_string], cwd=dir_name, shell=True, stderr=ef, stdout=ef) exit_code = dftb_process.wait() # check exit code if exit_code != 0: try: raise DFTBError( f"DFTB+ exited unexpectedly with exitcode: {exit_code}\n" ) except DFTBError as error: self.log.exception(error) raise else: self.log.debug( f"DFTB+ exited successfully. Exit code: {exit_code}") if optimize: # noinspection PyTypeChecker parser = DeMonNanoParser(out_fname, natoms=Natoms, logger=self.log) result_dict = parser.parse_DeMonNano_output() new_coords = result_dict["coordinates"] cluster.set_particle_positions( (new_coords, molecule_ids, atom_labels)) else: # noinspection PyTypeChecker parser = DeMonNanoParser(out_fname, natoms=Natoms, geometry_opt=False, logger=self.log) result_dict = parser.parse_DeMonNano_output() energy = result_dict["energy"] cluster.cost = energy # os.chdir("..") # Come back out of the scratch dir. return cluster