def test_autocorrelation_windowBigJump(): #The empty sets are ignored (no intermittency) input_ids = [{1}, {1}, {1}, set(), set(), {1}, {1}, {1}, set(), set(), {1}, {1}, {1}] tau_timeseries, sp_timeseries, sp_timeseries_data = autocorrelation( input_ids, tau_max=2, window_step=5) assert_almost_equal(sp_timeseries, [1, 1, 1])
def test_autocorrelation_windowBigJump_absence(): # In the last frame the molecules are absent input_ids = [{1}, {1}, {1}, set(), set(), {1}, {1}, {1}, set(), set(), {1}, set(), set()] tau_timeseries, sp_timeseries, sp_timeseries_data = autocorrelation( input_ids, tau_max=2, window_step=5) assert_almost_equal(sp_timeseries, [1, 2 / 3., 2 / 3.])
def test_autocorrelation_intermittency2_windowBigJump(): # The intermittency corrects the last frame input_ids = [{1}, {1}, {1}, set(), set(), {1}, {1}, {1}, set(), set(), {1}, set(), set(), {1}] corrected = correct_intermittency(input_ids, intermittency=2) tau_timeseries, sp_timeseries, sp_timeseries_data = autocorrelation( corrected, tau_max=2, window_step=5) assert_almost_equal(sp_timeseries, [1, 1, 1])
def test_autocorrelation_intermittency1_many(): input_ids = [{1}, set(), {1}, set(), {1}, set(), {1}, set(), {1}, set(), {1}, set(), {1}, set(), {1}] corrected = correct_intermittency(input_ids, intermittency=1) tau_timeseries, sp_timeseries, sp_timeseries_data = autocorrelation( corrected, tau_max=14, window_step=5) assert_almost_equal(sp_timeseries, [1] * 15)
def test_autocorrelation_intermittency1_windowJump_intermittencyAll(): """ Step leads to skipping frames if (tau_max + 1) + (intermittency * 2) < step. No frames should be skipped so intermittency should be applied to all. """ input_ids = [{2, 3}, { 3, }, {2, 3}, { 3, }, { 2, }, { 3, }, {2, 3}, { 3, }, {2, 3}, {2, 3}] corrected = correct_intermittency(input_ids, intermittency=1) tau_timeseries, sp_timeseries, sp_timeseries_data = autocorrelation( corrected, tau_max=2, window_step=5) assert all((x == {2, 3} for x in corrected)) assert_almost_equal(sp_timeseries, [1, 1, 1])
def test_autocorrelation_definedTaus(): input_ids = [{9, 8, 7}, {8, 7, 6}, {7, 6, 5}, {6, 5, 4}, {5, 4, 3}, {4, 3, 2}, {3, 2, 1}] tau_timeseries, sp_timeseries, sp_timeseries_data = autocorrelation( input_ids, tau_max=3) assert_almost_equal(sp_timeseries, [1, 2 / 3., 1 / 3., 0])
def test_autocorrelation_alwaysPresent(): input = [{1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}, {1, 2}] tau_timeseries, sp_timeseries, sp_timeseries_data = autocorrelation( input, tau_max=3) assert all(np.equal(sp_timeseries, 1))
def run(self, tau_max=20, start=None, stop=None, step=None, residues=False, intermittency=0, verbose=False): """ Computes and returns the Survival Probability (SP) timeseries Parameters ---------- start : int, optional Zero-based index of the first frame to be analysed, Default: None (first frame). stop : int, optional Zero-based index of the last frame to be analysed (exclusive), Default: None (last frame). step : int, optional Jump every `step`-th frame. This is compatible but independant of the taus used, and it is good to consider using the `step` equal to `tau_max` to remove the overlap. Note that `step` and `tau_max` work consistently with intermittency. Default: None (use every frame). tau_max : int, optional Survival probability is calculated for the range 1 <= `tau` <= `tau_max`. residues : Boolean, optional If true, the analysis will be carried out on the residues (.resids) rather than on atom (.ids). A single atom is sufficient to classify the residue as within the distance. intermittency : int, optional The maximum number of consecutive frames for which an atom can leave but be counted as present if it returns at the next frame. An intermittency of `0` is equivalent to a continuous survival probability, which does not allow for the leaving and returning of atoms. For example, for `intermittency=2`, any given atom may leave a region of interest for up to two consecutive frames yet be treated as being present at all frames. The default is continuous (0). verbose : Boolean, optional Print the progress to the console. Returns ------- tau_timeseries : list tau from 1 to `tau_max`. Saved in the field tau_timeseries. sp_timeseries : list survival probability for each value of `tau`. Saved in the field sp_timeseries. sp_timeseries_data: list raw datapoints from which the average is taken (sp_timeseries). Time dependancy and distribution can be extracted. .. versionchanged:: 1.0.0 To math other analysis methods, the `stop` keyword is now exclusive rather than inclusive. """ start, stop, step = self.universe.trajectory.check_slice_indices( start, stop, step) if tau_max > (stop - start): raise ValueError("Too few frames selected for given tau_max.") # preload the frames (atom IDs) to a list of sets self._selected_ids = [] # fixme - to parallise: the section should be rewritten so that this loop only creates a list of indices, # on which the parallel _single_frame can be applied. # skip frames that will not be used in order to improve performance # because AtomGroup.select_atoms is the most expensive part of this calculation # Example: step 5 and tau 2: LLLSS LLLSS, ... where L = Load, and S = Skip # Intermittency means that we have to load the extra frames to know if the atom is actually missing. # Say step=5 and tau=1, intermittency=0: LLSSS LLSSS # Say step=5 and tau=1, intermittency=1: LLLSL LLLSL frame_loaded_counter = 0 # only for the first window (frames before t are not used) frames_per_window = tau_max + 1 + intermittency # This number will apply after the first windows was loaded frames_per_window_subsequent = (tau_max + 1) + (2 * intermittency) num_frames_to_skip = max(step - frames_per_window_subsequent, 0) frame_no = start while frame_no < stop: # we have already added 1 to stop, therefore < if num_frames_to_skip != 0 and frame_loaded_counter == frames_per_window: logger.info("Skipping the next %d frames:", num_frames_to_skip) frame_no += num_frames_to_skip frame_loaded_counter = 0 # Correct the number of frames to be loaded after the first window (which starts at t=0, and # intermittency does not apply to the frames before) frames_per_window = frames_per_window_subsequent continue # update the frame number self.universe.trajectory[frame_no] logger.info("Loading frame: %d", self.universe.trajectory.frame) atoms = self.universe.select_atoms(self.selection) # SP of residues or of atoms ids = atoms.residues.resids if residues else atoms.ids self._selected_ids.append(set(ids)) frame_no += 1 frame_loaded_counter += 1 # adjust for the frames that were not loaded (step>tau_max + 1), # and for extra frames that were loaded (intermittency) window_jump = step - num_frames_to_skip self._intermittent_selected_ids = correct_intermittency( self._selected_ids, intermittency=intermittency) tau_timeseries, sp_timeseries, sp_timeseries_data = autocorrelation( self._intermittent_selected_ids, tau_max, window_jump) # warn the user if the NaN are found if all(np.isnan(sp_timeseries[1:])): logger.warning( 'NaN Error: Most likely data was not found. Check your atom selections. ' ) # user can investigate the distribution and sample size self.sp_timeseries_data = sp_timeseries_data self.tau_timeseries = tau_timeseries self.sp_timeseries = sp_timeseries return self