def __init__(self, mass_spec, N, scan_param_changepoints, isolation_window, mz_tol, rt_tol, min_ms1_intensity, n_purity_scans=None, purity_shift=None, purity_threshold=0): super().__init__(mass_spec) self.last_ms1_scan = None self.N = np.array(N) if scan_param_changepoints is not None: self.scan_param_changepoints = np.array([0] + scan_param_changepoints) else: self.scan_param_changepoints = np.array([0]) self.isolation_window = np.array( isolation_window ) # the isolation window (in Dalton) to select a precursor ion self.mz_tol = np.array( mz_tol ) # the m/z window (ppm) to prevent the same precursor ion to be fragmented again self.rt_tol = np.array( rt_tol ) # the rt window to prevent the same precursor ion to be fragmented again self.min_ms1_intensity = min_ms1_intensity # minimum ms1 intensity to fragment self.n_purity_scans = n_purity_scans self.purity_shift = purity_shift self.purity_threshold = purity_threshold # make sure the input are all correct assert len(self.N) == len(self.scan_param_changepoints) == len( self.isolation_window) == len(self.mz_tol) == len(self.rt_tol) if self.purity_threshold != 0: assert all(self.n_purity_scans < np.array(self.N)) mass_spec.reset() current_N, current_rt_tol, idx = self._get_current_N_DEW() mass_spec.current_N = current_N mass_spec.current_DEW = current_rt_tol default_scan = ScanParameters() default_scan.set(ScanParameters.MS_LEVEL, 1) default_scan.set(ScanParameters.ISOLATION_WINDOWS, [[DEFAULT_MS1_SCAN_WINDOW]]) mass_spec.set_repeating_scan(default_scan) # register new event handlers under this controller mass_spec.register(IndependentMassSpectrometer.MS_SCAN_ARRIVED, self.handle_scan) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_OPENING, self.handle_acquisition_open) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_CLOSING, self.handle_acquisition_closing)
def _process_scan(self, scan, queue_size): # if there's a previous ms1 scan to process new_tasks = [] if self.last_ms1_scan is not None: rt = self.last_ms1_scan.rt # then get the last ms1 scan, select bin walls and create scan locations mzs = self.last_ms1_scan.mzs default_range = [ DEFAULT_MS1_SCAN_WINDOW ] # TODO: this should maybe come from somewhere else? locations = DiaWindows(mzs, default_range, self.dia_design, self.window_type, self.kaufmann_design, self.extra_bins, self.num_windows).locations logger.debug('Window locations {}'.format(locations)) for i in range( len(locations) ): # define isolation window around the selected precursor ions isolation_windows = locations[i] dda_scan_params = ScanParameters() dda_scan_params.set(ScanParameters.MS_LEVEL, 2) dda_scan_params.set(ScanParameters.ISOLATION_WINDOWS, isolation_windows) new_tasks.append(dda_scan_params ) # push this dda scan to the mass spec queue # set this ms1 scan as has been processed self.last_ms1_scan = None return new_tasks
def __init__(self, mass_spec, dia_design, window_type, kaufmann_design, extra_bins, num_windows=None): super().__init__(mass_spec) self.last_ms1_scan = None self.dia_design = dia_design self.window_type = window_type self.kaufmann_design = kaufmann_design self.extra_bins = extra_bins self.num_windows = num_windows mass_spec.reset() default_scan = ScanParameters() default_scan.set(ScanParameters.MS_LEVEL, 1) default_scan.set(ScanParameters.ISOLATION_WINDOWS, [[DEFAULT_MS1_SCAN_WINDOW]]) mass_spec.set_repeating_scan(default_scan) mass_spec.register(IndependentMassSpectrometer.MS_SCAN_ARRIVED, self.handle_scan) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_OPENING, self.handle_acquisition_open) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_CLOSING, self.handle_acquisition_closing)
def __init__(self, mass_spec, N, isolation_window, mz_tol, rt_tol, min_ms1_intensity): super().__init__(mass_spec) self.last_ms1_scan = None self.N = N self.isolation_window = isolation_window # the isolation window (in Dalton) to select a precursor ion self.mz_tol = mz_tol # the m/z window (ppm) to prevent the same precursor ion to be fragmented again self.rt_tol = rt_tol # the rt window to prevent the same precursor ion to be fragmented again self.min_ms1_intensity = min_ms1_intensity # minimum ms1 intensity to fragment mass_spec.reset() mass_spec.current_N = N mass_spec.current_DEW = rt_tol default_scan = ScanParameters() default_scan.set(ScanParameters.MS_LEVEL, 1) default_scan.set(ScanParameters.ISOLATION_WINDOWS, [[DEFAULT_MS1_SCAN_WINDOW]]) mass_spec.set_repeating_scan(default_scan) # register new event handlers under this controller mass_spec.register(IndependentMassSpectrometer.MS_SCAN_ARRIVED, self.handle_scan) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_OPENING, self.handle_acquisition_open) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_CLOSING, self.handle_acquisition_closing)
def __init__(self, mass_spec, controller, min_time, max_time, progress_bar=True, out_dir=None, out_file=None): """ Initialises a synchronous environment to run the mass spec and controller :param mass_spec: An instance of Mass Spec object :param controller: An instance of Controller object :param min_time: start time :param max_time: end time :param progress_bar: True if a progress bar is to be shown """ self.scan_channel = [] self.task_channel = [] self.mass_spec = mass_spec self.controller = controller self.min_time = min_time self.max_time = max_time self.progress_bar = progress_bar self.default_scan_params = ScanParameters() self.default_scan_params.set(ScanParameters.MS_LEVEL, 1) self.default_scan_params.set(ScanParameters.ISOLATION_WINDOWS, [[DEFAULT_MS1_SCAN_WINDOW]]) self.default_scan_params.set(ScanParameters.ISOLATION_WIDTH, DEFAULT_ISOLATION_WIDTH) self.default_scan_params.set(ScanParameters.COLLISION_ENERGY, DEFAULT_COLLISION_ENERGY) self.default_scan_params.set(ScanParameters.POLARITY, self.mass_spec.ionisation_mode) self.default_scan_params.set(ScanParameters.FIRST_MASS, DEFAULT_MS1_SCAN_WINDOW[0]) self.default_scan_params.set(ScanParameters.LAST_MASS, DEFAULT_MS1_SCAN_WINDOW[1]) self.out_dir = out_dir self.out_file = out_file
def run(self, schedule_file, progress_bar=True): self.schedule = pd.read_csv(schedule_file) for idx, row in self.schedule.iterrows(): target_mass = row.targetMass target_time = row.targetTime if np.isnan(target_mass): ms_level = 1 isolation_windows = [[(0, 1000)]] precursor = None else: ms_level = 2 mz_lower = target_mass - self.isolation_window mz_upper = target_mass + self.isolation_window isolation_windows = [[(mz_lower, mz_upper)]] precursor_charge = +1 if (self.mass_spec.ionisation_mode == POSITIVE) else -1 scan_id = 0 precursor = Precursor(precursor_mz=target_mass, precursor_intensity=0, precursor_charge=precursor_charge, precursor_scan_id=scan_id) dda_scan_params = ScanParameters() dda_scan_params.set(ScanParameters.MS_LEVEL, ms_level) dda_scan_params.set(ScanParameters.ISOLATION_WINDOWS, isolation_windows) dda_scan_params.set(ScanParameters.TIME, target_time) if precursor: dda_scan_params.set(ScanParameters.PRECURSOR, precursor) self.mass_spec.add_task( dda_scan_params) # push this scan to the mass spec queue if progress_bar: with tqdm(total=target_time, initial=0) as pbar: self.mass_spec.run(self.schedule, pbar=pbar) else: self.mass_spec.run(self.schedule)
def __init__(self, mass_spec, isolation_window, mz_tol, min_ms1_intensity, min_roi_intensity, min_roi_length, score_method, N=None, rt_tol=math.inf, score_params=None): super().__init__(mass_spec) self.last_ms1_scan = None self.N = N self.isolation_window = isolation_window # the isolation window (in Dalton) to select a precursor ion self.mz_tol = mz_tol # the m/z window (ppm) to prevent the same precursor ion to be fragmented again self.rt_tol = rt_tol # the rt window to prevent the same precursor ion to be fragmented again self.min_ms1_intensity = min_ms1_intensity # minimum ms1 intensity to fragment # ROI stuff self.min_roi_intensity = min_roi_intensity self.mz_units = 'Da' self.min_roi_length = min_roi_length # Score stuff self.score_params = score_params self.score_method = score_method # Create ROI self.live_roi = [] self.dead_roi = [] self.junk_roi = [] self.live_roi_fragmented = [] self.live_roi_last_rt = [] # last fragmentation time of ROI mass_spec.reset() mass_spec.current_N = N mass_spec.current_DEW = rt_tol mass_spec.use_exclusion_list = False default_scan = ScanParameters() default_scan.set(ScanParameters.MS_LEVEL, 1) default_scan.set(ScanParameters.ISOLATION_WINDOWS, [[DEFAULT_MS1_SCAN_WINDOW]]) mass_spec.set_repeating_scan(default_scan) # register new event handlers under this controller mass_spec.register(IndependentMassSpectrometer.MS_SCAN_ARRIVED, self.handle_scan) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_OPENING, self.handle_acquisition_open) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_CLOSING, self.handle_acquisition_closing)
def __init__(self, mass_spec): super().__init__(mass_spec) default_scan = ScanParameters() default_scan.set(ScanParameters.MS_LEVEL, 1) default_scan.set(ScanParameters.ISOLATION_WINDOWS, [[DEFAULT_MS1_SCAN_WINDOW]]) mass_spec.reset() mass_spec.current_N = 0 mass_spec.current_DEW = 0 mass_spec.set_repeating_scan(default_scan) mass_spec.register(IndependentMassSpectrometer.MS_SCAN_ARRIVED, self.handle_scan) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_OPENING, self.handle_acquisition_open) mass_spec.register( IndependentMassSpectrometer.ACQUISITION_STREAM_CLOSING, self.handle_acquisition_closing)
class Environment(object): def __init__(self, mass_spec, controller, min_time, max_time, progress_bar=True, out_dir=None, out_file=None): """ Initialises a synchronous environment to run the mass spec and controller :param mass_spec: An instance of Mass Spec object :param controller: An instance of Controller object :param min_time: start time :param max_time: end time :param progress_bar: True if a progress bar is to be shown """ self.scan_channel = [] self.task_channel = [] self.mass_spec = mass_spec self.controller = controller self.min_time = min_time self.max_time = max_time self.progress_bar = progress_bar self.default_scan_params = ScanParameters() self.default_scan_params.set(ScanParameters.MS_LEVEL, 1) self.default_scan_params.set(ScanParameters.ISOLATION_WINDOWS, [[DEFAULT_MS1_SCAN_WINDOW]]) self.default_scan_params.set(ScanParameters.ISOLATION_WIDTH, DEFAULT_ISOLATION_WIDTH) self.default_scan_params.set(ScanParameters.COLLISION_ENERGY, DEFAULT_COLLISION_ENERGY) self.default_scan_params.set(ScanParameters.POLARITY, self.mass_spec.ionisation_mode) self.default_scan_params.set(ScanParameters.FIRST_MASS, DEFAULT_MS1_SCAN_WINDOW[0]) self.default_scan_params.set(ScanParameters.LAST_MASS, DEFAULT_MS1_SCAN_WINDOW[1]) self.out_dir = out_dir self.out_file = out_file def run(self): """ Runs the mass spec and controller :return: None """ # reset mass spec and set some initial values for each run self.mass_spec.reset() self.controller.reset() self._set_initial_values() # register event handlers from the controller self.mass_spec.register_event( IndependentMassSpectrometer.MS_SCAN_ARRIVED, self.add_scan) self.mass_spec.register_event( IndependentMassSpectrometer.ACQUISITION_STREAM_OPENING, self.controller.handle_acquisition_open) self.mass_spec.register_event( IndependentMassSpectrometer.ACQUISITION_STREAM_CLOSING, self.controller.handle_acquisition_closing) self.mass_spec.register_event( IndependentMassSpectrometer.STATE_CHANGED, self.controller.handle_state_changed) # run mass spec bar = tqdm(total=self.max_time - self.min_time, initial=0) if self.progress_bar else None self.mass_spec.fire_event( IndependentMassSpectrometer.ACQUISITION_STREAM_OPENING) try: # perform one step of mass spec up to max_time while self.mass_spec.time < self.max_time: # controller._process_scan() is called here immediately when a scan is produced within a step scan = self.mass_spec.step() # update controller internal states AFTER a scan has been generated and handled self.controller.update_state_after_scan(scan) # increment progress bar self._update_progress_bar(bar, scan) except Exception as e: raise e finally: self.mass_spec.close() self.close_progress_bar(bar) self.write_mzML(self.out_dir, self.out_file) def _update_progress_bar(self, pbar, scan): """ Updates progress bar based on elapsed time :param elapsed: Elapsed time to increment the progress bar :param pbar: progress bar object :param scan: the newly generated scan :return: None """ if pbar is not None: N, DEW = self._get_N_DEW(self.mass_spec.time) if N is not None and DEW is not None: msg = '(%.3fs) ms_level=%d N=%d DEW=%d' % ( self.mass_spec.time, scan.ms_level, N, DEW) else: msg = '(%.3fs) ms_level=%d' % (self.mass_spec.time, scan.ms_level) if pbar.n + scan.scan_duration < pbar.total: pbar.update(scan.scan_duration) pbar.set_description(msg) def close_progress_bar(self, bar): if bar is not None: try: bar.close() except Exception as e: logger.warning('Failed to close progress bar: %s' % str(e)) pass def add_scan(self, scan): """ Adds a newly generated scan. In this case, immediately we process it in the controller without saving the scan. :param scan: A newly generated scan :return: None """ self.scan_channel.append(scan) scan = self.scan_channel.pop(0) queue_size = len(self.mass_spec.get_processing_queue()) tasks = self.controller.handle_scan(scan, queue_size) self.add_tasks(tasks) def add_tasks(self, scan_params): """ Stores new tasks from the controller. In this case, immediately we pass the new tasks to the mass spec. :param scan_params: new tasks :return: None """ self.task_channel.extend(scan_params) while len(self.task_channel) > 0: new_task = self.task_channel.pop(0) self.mass_spec.add_to_processing_queue(new_task) def write_mzML(self, out_dir, out_file): """ Writes mzML to output file :param out_dir: output directory :param out_file: output filename :return: None """ if out_file is None: # if no filename provided, just quits return else: if out_dir is None: # no out_dir, use only out_file mzml_filename = Path(out_file) else: # both our_dir and out_file are provided mzml_filename = Path(out_dir, out_file) logger.debug('Writing mzML file to %s' % mzml_filename) try: precursor_information = self.controller.precursor_information except AttributeError: precursor_information = None writer = MzmlWriter('my_analysis', self.controller.scans, precursor_information) writer.write_mzML(mzml_filename) logger.debug('mzML file successfully written!') def _set_initial_values(self): """ Sets initial environment, mass spec start time, default scan parameters and other values :return: None """ self.controller.set_environment(self) self.mass_spec.set_environment(self) self.mass_spec.time = self.min_time N, DEW = self._get_N_DEW(self.mass_spec.time) if N is not None: self.mass_spec.current_N = N if DEW is not None: self.mass_spec.current_DEW = DEW def get_default_scan_params(self): """ Gets the default method scan parameters. Now it's set to do MS1 scan only. :return: the default scan parameters """ return self.default_scan_params def _get_N_DEW(self, time): """ Gets the current N and DEW depending on which controller type it is :return: The current N and DEW values, None otherwise """ if isinstance(self.controller, PurityController): current_N, current_rt_tol, idx = self.controller._get_current_N_DEW( time) return current_N, current_rt_tol elif isinstance(self.controller, TopNController): return self.controller.N, self.controller.rt_tols else: return None, None
def _get_dda_scan_param(self, mz, intensity, isolation_width, mz_tol, rt_tol, collision_energy): dda_scan_params = ScanParameters() dda_scan_params.set(ScanParameters.MS_LEVEL, 2) # create precursor object, assume it's all singly charged precursor_charge = +1 if (self.environment.mass_spec.ionisation_mode == POSITIVE) else -1 precursor = Precursor(precursor_mz=mz, precursor_intensity=intensity, precursor_charge=precursor_charge, precursor_scan_id=self.last_ms1_scan.scan_id) dda_scan_params.set(ScanParameters.PRECURSOR_MZ, precursor) # set the full-width isolation width, in Da dda_scan_params.set(ScanParameters.ISOLATION_WIDTH, isolation_width) # define dynamic exclusion parameters dda_scan_params.set(ScanParameters.DYNAMIC_EXCLUSION_MZ_TOL, mz_tol) dda_scan_params.set(ScanParameters.DYNAMIC_EXCLUSION_RT_TOL, rt_tol) # define other fragmentation parameters dda_scan_params.set(ScanParameters.COLLISION_ENERGY, collision_energy) dda_scan_params.set(ScanParameters.POLARITY, self.environment.mass_spec.ionisation_mode) dda_scan_params.set(ScanParameters.FIRST_MASS, DEFAULT_MSN_SCAN_WINDOW[0]) dda_scan_params.set(ScanParameters.LAST_MASS, DEFAULT_MSN_SCAN_WINDOW[1]) return dda_scan_params
def _update_parameters(self, scan): # if there's a previous ms1 scan to process if self.last_ms1_scan is not None: self.current_roi_mzs = [roi.get_mean_mz() for roi in self.live_roi] self.current_roi_intensities = [ roi.get_max_intensity() for roi in self.live_roi ] rt = self.last_ms1_scan.rt # loop over points in decreasing score scores = self.get_scores(self.score_method, self.score_params) idx = np.argsort(scores)[::-1] for i in idx: mz = self.current_roi_mzs[i] intensity = self.current_roi_intensities[i] # stopping criteria is done based on the scores if scores[i] <= 0: self.logger.debug( 'Time %f Top-%d ions have been selected' % (self.mass_spec.time, self.N)) break # updated fragmented list and times if self.live_roi_fragmented[i]: continue self.live_roi_fragmented[i] = True self.live_roi_last_rt[ i] = rt # TODO: May want to update this to use the time of the MS2 scan # send a new ms2 scan parameter to the mass spec dda_scan_params = ScanParameters() dda_scan_params.set(ScanParameters.MS_LEVEL, 2) # create precursor object, assume it's all singly charged precursor_charge = +1 if (self.mass_spec.ionisation_mode == POSITIVE) else -1 precursor = Precursor( precursor_mz=mz, precursor_intensity=intensity, precursor_charge=precursor_charge, precursor_scan_id=self.last_ms1_scan.scan_id) mz_lower = mz - self.isolation_window # Da mz_upper = mz + self.isolation_window # Da isolation_windows = [[(mz_lower, mz_upper)]] dda_scan_params.set(ScanParameters.ISOLATION_WINDOWS, isolation_windows) dda_scan_params.set(ScanParameters.PRECURSOR, precursor) # save dynamic exclusion parameters too dda_scan_params.set(ScanParameters.DYNAMIC_EXCLUSION_MZ_TOL, self.mz_tol) dda_scan_params.set(ScanParameters.DYNAMIC_EXCLUSION_RT_TOL, self.rt_tol) # push this dda scan parameter to the mass spec queue self.mass_spec.add_task(dda_scan_params) for param in self.mass_spec.get_task_queue(): precursor = param.get(ScanParameters.PRECURSOR) if precursor is not None: self.logger.debug('- %s' % str(precursor)) # set this ms1 scan as has been processed self.last_ms1_scan = None
def _update_parameters(self, scan): # if there's a previous ms1 scan to process if self.last_ms1_scan is not None: mzs = self.last_ms1_scan.mzs intensities = self.last_ms1_scan.intensities rt = self.last_ms1_scan.rt # set up current scan parameters current_N, current_rt_tol, idx = self._get_current_N_DEW() current_isolation_window = self.isolation_window[idx] current_mz_tol = self.mz_tol[idx] # calculate purities purities = [] for mz_idx in range(len(self.last_ms1_scan.mzs)): nearby_mzs_idx = np.where( abs(self.last_ms1_scan.mzs - self.last_ms1_scan.mzs[mz_idx] ) < current_isolation_window) if len(nearby_mzs_idx[0]) == 1: purities.append(1) else: total_intensity = sum( self.last_ms1_scan.intensities[nearby_mzs_idx]) purities.append(self.last_ms1_scan.intensities[mz_idx] / total_intensity) # loop over points in decreasing intensity fragmented_count = 0 idx = np.argsort(intensities)[::-1] for i in idx: mz = mzs[i] intensity = intensities[i] purity = purities[i] # stopping criteria is after we've fragmented N ions or we found ion < min_intensity if fragmented_count >= current_N: self.logger.debug( 'Time %f Top-%d ions have been selected' % (self.mass_spec.time, current_N)) break if intensity < self.min_ms1_intensity: self.logger.debug( 'Time %f Minimum intensity threshold %f reached at %f, %d' % (self.mass_spec.time, self.min_ms1_intensity, intensity, fragmented_count)) break # skip ion in the dynamic exclusion list of the mass spec if self.mass_spec.is_excluded(mz, rt): continue if purity < self.purity_threshold: purity_shift_amounts = [ self.purity_shift * (i - (self.n_purity_scans - 1) / 2) for i in range(self.n_purity_scans) ] for purity_idx in range(self.n_purity_scans): # send a new ms2 scan parameter to the mass spec dda_scan_params = ScanParameters() dda_scan_params.set(ScanParameters.MS_LEVEL, 2) dda_scan_params.set(ScanParameters.N, current_N) # create precursor object, assume it's all singly charged precursor_charge = +1 if ( self.mass_spec.ionisation_mode == POSITIVE) else -1 precursor = Precursor( precursor_mz=mz, precursor_intensity=intensity, precursor_charge=precursor_charge, precursor_scan_id=self.last_ms1_scan.scan_id) mz_lower = mz + purity_shift_amounts[ purity_idx] - current_isolation_window # Da mz_upper = mz + purity_shift_amounts[ purity_idx] + current_isolation_window # Da isolation_windows = [[(mz_lower, mz_upper)]] dda_scan_params.set(ScanParameters.ISOLATION_WINDOWS, isolation_windows) dda_scan_params.set(ScanParameters.PRECURSOR, precursor) # save dynamic exclusion parameters too dda_scan_params.set( ScanParameters.DYNAMIC_EXCLUSION_MZ_TOL, current_mz_tol) dda_scan_params.set( ScanParameters.DYNAMIC_EXCLUSION_RT_TOL, current_rt_tol) # push this dda scan parameter to the mass spec queue self.mass_spec.add_task(dda_scan_params) fragmented_count += 1 # need to work out what we want to do here else: # send a new ms2 scan parameter to the mass spec dda_scan_params = ScanParameters() dda_scan_params.set(ScanParameters.MS_LEVEL, 2) dda_scan_params.set(ScanParameters.N, current_N) # create precursor object, assume it's all singly charged precursor_charge = +1 if (self.mass_spec.ionisation_mode == POSITIVE) else -1 precursor = Precursor( precursor_mz=mz, precursor_intensity=intensity, precursor_charge=precursor_charge, precursor_scan_id=self.last_ms1_scan.scan_id) mz_lower = mz - current_isolation_window # Da mz_upper = mz + current_isolation_window # Da isolation_windows = [[(mz_lower, mz_upper)]] dda_scan_params.set(ScanParameters.ISOLATION_WINDOWS, isolation_windows) dda_scan_params.set(ScanParameters.PRECURSOR, precursor) # save dynamic exclusion parameters too dda_scan_params.set( ScanParameters.DYNAMIC_EXCLUSION_MZ_TOL, current_mz_tol) dda_scan_params.set( ScanParameters.DYNAMIC_EXCLUSION_RT_TOL, current_rt_tol) # push this dda scan parameter to the mass spec queue self.mass_spec.add_task(dda_scan_params) fragmented_count += 1 for param in self.mass_spec.get_task_queue(): precursor = param.get(ScanParameters.PRECURSOR) if precursor is not None: self.logger.debug('- %s' % str(precursor)) # set this ms1 scan as has been processed self.last_ms1_scan = None
def _update_parameters(self, scan): # if there's a previous ms1 scan to process if self.last_ms1_scan is not None: mzs = self.last_ms1_scan.mzs intensities = self.last_ms1_scan.intensities rt = self.last_ms1_scan.rt # loop over points in decreasing intensity fragmented_count = 0 idx = np.argsort(intensities)[::-1] for i in idx: mz = mzs[i] intensity = intensities[i] # stopping criteria is after we've fragmented N ions or we found ion < min_intensity if fragmented_count >= self.N: self.logger.debug( 'Time %f Top-%d ions have been selected' % (self.mass_spec.time, self.N)) break if intensity < self.min_ms1_intensity: self.logger.debug( 'Time %f Minimum intensity threshold %f reached at %f, %d' % (self.mass_spec.time, self.min_ms1_intensity, intensity, fragmented_count)) break # skip ion in the dynamic exclusion list of the mass spec if self.mass_spec.is_excluded(mz, rt): continue # send a new ms2 scan parameter to the mass spec dda_scan_params = ScanParameters() dda_scan_params.set(ScanParameters.MS_LEVEL, 2) # create precursor object, assume it's all singly charged precursor_charge = +1 if (self.mass_spec.ionisation_mode == POSITIVE) else -1 precursor = Precursor( precursor_mz=mz, precursor_intensity=intensity, precursor_charge=precursor_charge, precursor_scan_id=self.last_ms1_scan.scan_id) mz_lower = mz - self.isolation_window # Da mz_upper = mz + self.isolation_window # Da isolation_windows = [[(mz_lower, mz_upper)]] dda_scan_params.set(ScanParameters.ISOLATION_WINDOWS, isolation_windows) dda_scan_params.set(ScanParameters.PRECURSOR, precursor) # save dynamic exclusion parameters too dda_scan_params.set(ScanParameters.DYNAMIC_EXCLUSION_MZ_TOL, self.mz_tol) dda_scan_params.set(ScanParameters.DYNAMIC_EXCLUSION_RT_TOL, self.rt_tol) # push this dda scan parameter to the mass spec queue self.mass_spec.add_task(dda_scan_params) fragmented_count += 1 for param in self.mass_spec.get_task_queue(): precursor = param.get(ScanParameters.PRECURSOR) if precursor is not None: self.logger.debug('- %s' % str(precursor)) # set this ms1 scan as has been processed self.last_ms1_scan = None