def test_replace_outoflimits(self): ''' Must leave current image unchanged if it tries to replace an non existing image''' chain.replace_image( self.p_state, idx_image=5 ) # replace 0th with 5th (not exist) self.assertEqual( system.get_index( self.p_state ), 0 ) # active is 0th self.assertEqual( chain.get_noi( self.p_state ), 1 ) # total 1 image chain.replace_image( self.p_state, idx_image=-5 ) # replace 0th with -5th (not exist) self.assertEqual( system.get_index( self.p_state ), 0 ) # active is 0th self.assertEqual( chain.get_noi( self.p_state ), 1 ) # total 1 image
def test_delete_outoflimits(self): chain.insert_image_before( self.p_state ) # active is 1st chain.insert_image_before( self.p_state ) # active is 2nd self.assertEqual( system.get_index( self.p_state ), 2 ) # active is 2nd self.assertEqual( chain.get_noi( self.p_state ), 3 ) # total 3 images # test the deletion of a non existing image with positive idx chain.delete_image( self.p_state, idx_image=5 ) # delete -5th (not exist) self.assertEqual( chain.get_noi( self.p_state ), 3 ) # total 3 images # test the deletion of a non existing image with negative idx chain.delete_image( self.p_state, idx_image=-5 ) # delete -5th (not exist) self.assertEqual( chain.get_noi( self.p_state ), 2 ) # total 2 images self.assertEqual( system.get_index( self.p_state ), 1 ) # active is 1st
def helper(p_state, node): node.log(" collecting...") # Make sure the number of images matches our current simulation state chain.image_to_clipboard(p_state) noi = io.n_images_in_file(p_state, node.chain_file) chain.set_length(p_state, noi) io.chain_read(p_state, node.chain_file) noi = chain.get_noi(p_state) i = 0 while i < noi: # First we check if this images is within any of the ranges covered by the children is_child = False for idx_c, (i1, i2) in enumerate(node.child_indices): if i >= i1 and i <= i2: is_child = True idx_child = idx_c break # If the current idx is covered by a child node, we open up another level of recursion, else we append the image if is_child: helper(p_state, node.children[idx_child]) # After collecting the child we advance the current iteration idx, so that we continue one past the last child index i = node.child_indices[idx_child][1] + 1 # We also need to read the chain file again to return to our previous state chain.image_to_clipboard(p_state) chain.set_length(p_state, noi) io.chain_read(p_state, node.chain_file) else: io.image_append(p_state, output_file, idx_image=i) i += 1
def spawn_children(self, p_state): """If intermediate minima are present, relaxes them and creates child nodes""" from spirit import chain, simulation if not self.allow_split: return if len(self.intermediate_minima) == 0: return if self._converged: return self.log("Spawning children - splitting chain") self.log("Relaxing intermediate minima") for idx_minimum in self.intermediate_minima: simulation.start(p_state, simulation.METHOD_LLG, self.solver_llg, idx_image=idx_minimum) # Instantiate the GNEB nodes noi = chain.get_noi(p_state) # Creates a list of all indices that would be start/end points of new chains idx_list = [0, *self.intermediate_minima, noi - 1] # From the previous list, creates a list of pairs of start/end points idx_pairs = [(idx_list[i], idx_list[i + 1]) for i in range(len(idx_list) - 1)] # First create all the instances of GNEB nodes for i1, i2 in idx_pairs: self.add_child(i1, i2, p_state)
def test_pop_back(self): ''' Must make the image with next smaller index active when the active is poped''' chain.insert_image_before( self.p_state ) # add before active self.assertEqual( system.get_index( self.p_state ), 1 ) # active is 1st chain.pop_back( self.p_state ) # delete the last (active) self.assertEqual( system.get_index( self.p_state ), 0 ) # active is 0th self.assertEqual( chain.get_noi( self.p_state ), 1 ) # total 1 image
def prepare_moving_endpoints(self, idx_mid=-1): from spirit import chain, io, state, parameters self.log("Preparing for moving endpoints") self.target_noi = 3 self.moving_endpoints = True self.image_types = [[1, parameters.gneb.IMAGE_CLIMBING]] with state.State(self.input_file) as p_state: self._prepare_state(p_state) self.update_energy_path(p_state) noi = chain.get_noi(p_state) if idx_mid < 0: E = chain.get_energy(p_state) idx_mid = np.argmax(E) self.log("idx_mid = {}".format(idx_mid)) if (idx_mid >= 1 and idx_mid < noi - 1): for i in range(idx_mid - 1): chain.delete_image(p_state, idx_image=0) for i in range(noi - idx_mid - 2): chain.pop_back(p_state) self.update_energy_path(p_state) self.save_chain(p_state)
def mark_climbing_image(p_state, gnw, ax): """Helper function that marks the climbing image in a plot.""" import numpy as np image_types = np.array([ gneb.get_climbing_falling(p_state, i) for i in range(chain.get_noi(p_state)) ]) idx_climbing_list = np.array(range( chain.get_noi(p_state)))[image_types == gneb.IMAGE_CLIMBING] if (len(idx_climbing_list) == 0): return idx_climbing = idx_climbing_list[0] E0 = gnw.current_energy_path.total_energy[-1] ax.plot(gnw.current_energy_path.reaction_coordinate[idx_climbing], gnw.current_energy_path.total_energy[idx_climbing] - E0, marker="^", color="red")
def chain_append_from_file(p_state, filename): # TODO: chain_read with insert_idx seems to be broken raise NotImplementedError() from spirit import io, chain noi_file = io.n_images_in_file(p_state, filename) noi = chain.get_noi(p_state) chain.image_to_clipboard(p_state) chain.set_length(p_state, noi + noi_file) io.chain_read(p_state, filename, insert_idx=noi) chain.update_data(p_state)
def increase_noi(self, p_state=None): """Increases the noi by (roughly) a factor of two until the number of images is at least as large as target_noi""" from spirit import state, chain, transition, io with state.State(self.input_file) as p_state: self._prepare_state(p_state) self.noi = chain.get_noi(p_state) if (self.noi < self.target_noi): self.log( "Too few images ({}). Inserting additional interpolated images" .format(self.noi)) while (self.noi < self.target_noi): transition.homogeneous_insert_interpolated(p_state, 1) self.noi = chain.get_noi(p_state) self.log("Number of images = {}".format(self.noi)) self.save_chain(p_state)
def chain_rebalance(self, p_state, tol=0.25): """Tries to rebalance the chain while keeping the number of images constant. The closer tol is to zero, the more aggressive the rebalancing.""" import numpy as np from spirit import chain, transition from spirit.parameters import gneb noi = chain.get_noi(p_state) idx_max = np.argmax(self.current_energy_path.total_energy) delta_Rx = [ self.current_energy_path.reaction_coordinate[i + 1] - self.current_energy_path.reaction_coordinate[i] for i in range(noi - 1) ] delta_Rx_2 = [ self.current_energy_path.reaction_coordinate[i + 2] - self.current_energy_path.reaction_coordinate[i] for i in range(noi - 2) ] max_delta_Rx = np.max(delta_Rx) min_delta_Rx2 = np.min(delta_Rx_2) if max_delta_Rx > (1 + np.abs(tol)) * min_delta_Rx2: self.log("Rebalancing chain") self.log(" Max. Delta Rx {}, Min. Delta Rx2 {}".format( max_delta_Rx, min_delta_Rx2)) idx = np.argmax(delta_Rx) idx_2 = np.argmin(delta_Rx_2) self.log(" Inserting after idx {}, deleting idx {}".format( idx, idx_2 + 1)) # Delete the image that was too densely spaced. this will shift all indices greater than idx_2+1 chain.delete_image(p_state, idx_image=idx_2 + 1) # Correct the index if necessary if (idx > idx_2 + 1) < idx: idx -= 1 chain.insert_image_after(p_state, idx) transition.homogeneous(p_state, idx, idx + 2)
def chain_write_between(p_state, filename, idx_start, idx_stop, fileformat=None): """Writes the chain between idx_start and idx_stop to a file. Includes the endpoints!""" from spirit import io, chain if fileformat is None: fileformat = io.FILEFORMAT_OVF_TEXT noi = chain.get_noi(p_state) if (idx_start > idx_stop or idx_start < 0 or idx_stop >= noi): raise Exception("Error in idx_start and/or idx_stop") io.image_write(p_state, filename, idx_image=idx_start, fileformat=fileformat) for i in range(idx_start + 1, idx_stop + 1): io.image_append(p_state, filename, idx_image=i, fileformat=fileformat)
def prepare_dimer(self, idx_left, idx_right=None): from spirit import chain, io, state, parameters if idx_right is None: idx_right = idx_left + 1 self.log("Preparing for dimer endpoints") self.target_noi = 2 self.moving_endpoints = True self.translating_endpoints = True self.image_types = [] with state.State(self.input_file) as p_state: self._prepare_state(p_state) self.update_energy_path(p_state) noi = chain.get_noi(p_state) self.log("idx_left = {}, idx_right = {}".format( idx_left, idx_right)) # Delete all images to the right of idx right for i in range(noi - idx_right - 1): chain.pop_back(p_state) # Delete all images to the left of idx_left for i in range(idx_left): chain.delete_image(p_state, idx_image=0) # Delete images between idx_left and idx_right for i in range(idx_right - idx_left - 1): chain.delete_image(p_state, idx_image=1) self.update_energy_path(p_state) self.save_chain(p_state)
def test_push_back(self): ''' Must add one image and leave active's index unchanged ''' chain.push_back( self.p_state ) # add after all self.assertEqual( chain.get_noi( self.p_state ), 2 ) # total 2 images self.assertEqual( system.get_index( self.p_state ), 0 ) # active is 0th
def test_insert_before(self): ''' Must reindex (increment by one) active image ''' chain.insert_image_before( self.p_state ) # add before active self.assertEqual( chain.get_noi( self.p_state ), 2 ) # total 2 images self.assertEqual( system.get_index( self.p_state ), 1 ) # active is 1st
def test_insert_after(self): ''' Must leave the index of active image the same ''' chain.insert_image_after( self.p_state ) # add after active self.assertEqual( chain.get_noi( self.p_state ), 2 ) # total 2 images self.assertEqual( system.get_index( self.p_state ), 0 ) # active is 0th
def test_NOI(self): ''' NOI -> Number of images in that chain ''' self.assertEqual( chain.get_noi( self.p_state ), 1 ) # total 1 image
def tearDown(self): ''' clean the p_state ''' noi = chain.get_noi( self.p_state ) for i in range(noi-1): chain.pop_back( self.p_state ) self.assertEqual( chain.get_noi( self.p_state ), 1 )
def test_remove_smallest_index_active(self): '''' Must set the active to the image with the smallest index left''' chain.insert_image_after( self.p_state ) # active is 0th chain.delete_image( self.p_state ) # delete 0th self.assertEqual( chain.get_noi( self.p_state ), 1 ) # total 1 image self.assertEqual( system.get_index( self.p_state ), 0 ) # active is 0th
def test_delete_trivial(self): ''' Must NOT delete the image of a chain with only one image''' chain.delete_image( self.p_state ) # delete 0th self.assertEqual( chain.get_noi( self.p_state ), 1 ) # total 1 image