def write_results(self): # Save results from the Optimizer to HDF5 file if requested h5_group = get_h5_group(self.h5_fn, self.h5_group_name) # Some attributes never change and are only set in the first cycle if self.cur_cycle == 0: h5_group.attrs["is_cos"] = self.is_cos try: atoms = self.geometry.images[0].atoms coord_size = self.geometry.images[0].coords.size except AttributeError: atoms = self.geometry.atoms coord_size = self.geometry.coords.size h5_group.attrs["atoms"] = atoms h5_group.attrs["coord_type"] = self.geometry.coord_type h5_group.attrs["coord_size"] = coord_size # Update changing attributes h5_group.attrs["cur_cycle"] = self.cur_cycle h5_group.attrs["is_converged"] = self.is_converged for key, shape in self.data_model.items(): value = getattr(self, key) # Don't try to set empty values, e.g. 'tangents' are only present # for COS methods. 'modified_forces' are only present for NCOptimizer. if not value: continue if len(shape) > 1: h5_group[key][self.cur_cycle, :len(value[-1])] = value[-1] else: h5_group[key][self.cur_cycle] = value[-1] h5_group.file.close()
def init_h5_group(self, atoms, max_cycles=None): if max_cycles is None: max_cycles = self.h5_cycles self.data_model = get_data_model(atoms, max_cycles) self.h5_group = get_h5_group(self.h5_fn, self.h5_group_name, self.data_model, reset=True)
def set_restart_info(self, restart_info): # Set restart information general to all optimizers self.last_cycle = restart_info["last_cycle"] + 1 must_resize = self.last_cycle >= self.max_cycles if must_resize: self.max_cycles += restart_info["max_cycles"] # Resize HDF5 if self.dump: h5_group = get_h5_group(self.h5_fn, self.h5_group_name) resize_h5_group(h5_group, self.max_cycles) h5_group.file.close() self.coords = [np.array(coords) for coords in restart_info["coords"]] self.energies = restart_info["energies"] self.forces = [np.array(forces) for forces in restart_info["forces"]] self.steps = [np.array(step) for step in restart_info["steps"]] # Set subclass specific information self._set_opt_restart_info(restart_info) # Propagate restart information downwards to the geometry self.geometry.set_restart_info(restart_info["geom_info"])
def __init__( self, geometry, thresh="gau_loose", max_step=0.04, max_cycles=50, rms_force=None, rms_force_only=False, align=False, dump=False, dump_restart=None, prefix="", reparam_thresh=1e-3, overachieve_factor=0.0, restart_info=None, check_coord_diffs=True, coord_diff_thresh=0.01, h5_fn="optimization.h5", h5_group_name="opt", ): assert thresh in self.CONV_THRESHS.keys() self.geometry = geometry self.thresh = thresh self.max_step = max_step self.rms_force_only = rms_force_only self.align = align self.dump = dump self.dump_restart = dump_restart self.prefix = f"{prefix}_" if prefix else prefix self.reparam_thresh = reparam_thresh self.overachieve_factor = float(overachieve_factor) self.check_coord_diffs = check_coord_diffs self.coord_diff_thresh = float(coord_diff_thresh) self.is_cos = issubclass(type(self.geometry), ChainOfStates) self.convergence = self.make_conv_dict(thresh, rms_force) for key, value in self.convergence.items(): setattr(self, key, value) # Setting some default values self.resetted = False self.max_cycles = max_cycles self.out_dir = os.getcwd() if self.is_cos: moving_image_num = len(self.geometry.moving_indices) print(f"Path with {moving_image_num} moving images.") self.out_dir = Path(self.out_dir) if not self.out_dir.exists(): os.mkdir(self.out_dir) self.h5_fn = self.out_dir / h5_fn self.h5_group_name = h5_group_name current_fn = "current_geometries.trj" if self.is_cos else "current_geometry.xyz" self.current_fn = self.get_path_for_fn(current_fn) final_fn = "final_geometries.trj" if self.is_cos else "final_geometry.xyz" self.final_fn = self.get_path_for_fn(final_fn) self.hei_trj_fn = self.get_path_for_fn("cos_hei.trj") try: os.remove(self.hei_trj_fn) except FileNotFoundError: pass self.logger = logging.getLogger("optimizer") # Setting some empty lists as default. The actual shape of the respective # entries is not considered, which gives us some flexibility. self.data_model = get_data_model(self.geometry, self.is_cos, self.max_cycles) for la in self.data_model.keys(): setattr(self, la, list()) if self.dump: out_trj_fn = self.get_path_for_fn("optimization.trj") self.out_trj_handle = open(out_trj_fn, "w") # Call with reset=True to delete remnants of previous calculations, unless # the optimizer was restarted. Given a previous optimization with, e.g. 30 # cycles and a second restarted optimization with 20 cycles the last 10 cycles # of the previous optimization would still be present. reset = restart_info is None h5_group = get_h5_group(self.h5_fn, self.h5_group_name, self.data_model, reset=reset) h5_group.file.close() if self.prefix: self.log(f"Created optimizer with prefix {self.prefix}") self.restarted = False self.last_cycle = 0 self.cur_cycle = 0 if restart_info is not None: if isinstance(restart_info, str): restart_info = yaml.load(restart_info, Loader=yaml.SafeLoader) self.set_restart_info(restart_info) self.restarted = True