def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): order_gen = b.baker(b.fixed_timer, kwargs={ 'count': self.config_dict["N"], 't': self.config_dict["t"]}, position_to_pass_through=(0, 3)) return _exp.move_capture(self, {}, order_gen=order_gen, func_list=func_list, save_mode=save_mode, number=1, delay=0)
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): print self.scope.stage.position end = b.baker(b.max_fifth_col, args=['IMAGE_ARR', self.scope, self.initial_position]) # Take measurements and move to position of maximum brightness. _exp.move_capture(self, {'x': self.config_dict['raster3d_n_step'], 'y': self.config_dict['raster3d_n_step'], 'z': self.config_dict['raster3d_n_step']}, func_list=func_list, save_mode=save_mode, end_func=end) print self.scope.stage.position
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): end = b.baker(b.max_fifth_col, args=['IMAGE_ARR', self.scope, self.initial_position]) # Take measurements and move to position of maximum brightness. # end_func is applied at the end of every set of (n, step). for n_step in self.config_dict['raster_n_step']: _exp.move_capture(self, {'x': [n_step], 'y': [n_step]}, func_list=func_list, save_mode=save_mode, end_func=end) print self.scope.stage.position
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): order_gen = b.baker(b.fixed_timer, kwargs={ 'count': self.config_dict["N"], 't': self.config_dict["t"] }, position_to_pass_through=(0, 3)) _exp.move_capture(self, {}, order_gen=order_gen, func_list=func_list, save_mode=save_mode, number=300, delay=0)
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final', axis='x'): """Operates on one axis at a time.""" # Get default values. step_pair = (self.config_dict["parabola_N"], self.config_dict["parabola_step"]) end = b.baker(b.move_to_parmax, args=['IMAGE_ARR', self.scope, axis]) _exp.move_capture(self, {axis: [step_pair]}, func_list=func_list, save_mode=save_mode, end_func=end)
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): end = b.baker(b.max_fifth_col, args=['IMAGE_ARR', self.scope, self.initial_position]) # Take measurements and move to position of maximum brightness. _exp.move_capture(self, { 'x': self.config_dict['raster_n_step'], 'y': self.config_dict['raster_n_step'] }, func_list=func_list, save_mode=save_mode, end_func=end) print self.scope.stage.position
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): """Default is to sleep for 10 minutes.""" # Do an initial alignment and then take that position as the initial # position. align = Align(self.scope, self.config_dict, group=self.gr) hillwalk = HillWalk(self.scope, self.config_dict, group=self.gr) sleep_times = self.config_dict['sleep_times'] drifts = [] for i in xrange(len(sleep_times)): if i == 0: align.run(func_list=func_list, save_mode=save_mode) else: hillwalk.run() pos = self.scope.stage.position t.sleep(sleep_times[i]) if i == 0: last_pos = pos drift = pos - last_pos last_pos = pos drifts.append([sleep_times[i], drift]) # Measure the position after it has drifted by working out how much # it needs to move by to re-centre it. self.gr.create_dataset('Drift', data=np.array(drifts))
def run(self, save_mode='save_final'): # At the end, move to the position of maximum brightness. end = b.baker(b.max_fifth_col, args=['IMAGE_ARR', self.scope, self.initial_position]) for n_step in self.config_dict['mmt_range']: # Allow the iteration to take place as many times as specified # in the scope_dict file. _exp.move_capture(self, {'z': [n_step]}, save_mode=save_mode, end_func=end) print self.scope.stage.position
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final', max_step=10, min_step=1, number=100, delay=0): raster = RasterXY(self.scope, self.config_dict, group=self.gr, group_name='KeepCentred') raster.run(func_list=func_list, save_mode=save_mode) hilly = HillWalk(self.scope, self.config_dict, group=self.gr) while True: try: hilly.run(max_step=max_step, min_step=min_step, number=number, delay=delay) except KeyboardInterrupt: break
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): raster = RasterXY(self.scope, self.config_dict, group=self.gr, group_name='KeepCentred') raster.run(func_list=func_list, save_mode=save_mode) # TODO Insert a better algorithm here for fine alignment! align_fine = HillWalk(self.scope, self.config_dict, group=self.gr) while True: try: align_fine.run() except KeyboardInterrupt: break
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): """Algorithm for alignment is to iterate the RasterXY procedure several times with decreasing width and increasing precision, and then using the parabola of brightness to try and find the maximum point by shifting slightly.""" raster_set = RasterXY(self.scope, self.config_dict, group=self.gr, raster_n_step=self.config_dict['n_steps']) # Take measurements and move to position of maximum brightness. All # arrays measurements will be taken before moving on. raster_set.run(func_list=func_list, save_mode=save_mode) hilly = HillWalk(self.scope, self.config_dict, group=self.gr) hilly.run()
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): """Algorithm for alignment is to iterate the RasterXY procedure several times with decreasing width and increasing precision, and then using the parabola of brightness to try and find the maximum point by shifting slightly.""" raster_set = RasterXY(self.scope, self.config_dict, group=self.gr, raster_n_step=self.config_dict['n_steps']) # Take measurements and move to position of maximum brightness. All # arrays measurements will be taken before moving on. raster_set.run(func_list=func_list, save_mode=save_mode) par = ParabolicMax(self.scope, self.config_dict, group=self.gr) for i in xrange(self.config_dict["parabola_iterations"]): for ax in ['x', 'y']: par.run(func_list=func_list, save_mode=save_mode, axis=ax)
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final'): for i in range(2): # TODO GENERALISE THE NUMBER if i == 0: raster_2d = RasterXY(self.scope, self.config_dict, group=self.gr, raster_n_step=[[39, 500]]) else: raster_2d = RasterXY(self.scope, self.config_dict, group=self.gr) raster_2d.run(func_list=func_list, save_mode=save_mode) along_z = AlongZ(self.scope, self.config_dict, group=self.gr, mmt_range=self.config_dict["mmt_range"]) along_z.run(save_mode=save_mode)
def run(self, func_list=b.baker(b.unchanged), save_mode='save_final', number=1000, delay=0.1, initial_align=False): """Default is to measure for 100s. See the config file for sleep times.""" # Do an initial alignment and then take that position as the initial # position. align = Align(self.scope, self.config_dict, group=self.gr) hill_walk = HillWalk(self.scope, self.config_dict, group=self.gr) timed_mmts = TimedMeasurements(self.scope, self.config_dict, group=self.gr, N=1, t=0) sleep_times = self.config_dict['sleep_times'] drifts = [] for i in xrange(len(sleep_times)): if i == 0 and initial_align: align.run(func_list=func_list, save_mode=save_mode) else: hill_walk.run() pos = self.scope.stage.position sleep_start = t.time() timed_list = [] while _exp.elapsed(sleep_start) < sleep_times[i]: timed_list.append(timed_mmts.run(save_mode=None)) self.gr.create_dataset('timed_run', data=np.array(timed_list), attrs={'number': number, 'delay': delay, 'sleep_time': sleep_times[i]}) if i == 0: last_pos = pos drift = pos - last_pos last_pos = pos drifts.append([sleep_times[i], drift[0], drift[1], drift[2]]) # Measure the position after it has drifted by working out how much # it needs to move by to re-centre it. self.gr.create_dataset('Drift', data=np.array(drifts), attrs={ 'number': number, 'delay': delay})
def move_capture(exp_obj, positions_dict, func_list=b.baker(b.unchanged), order_gen=b.baker(b.raster, position_to_pass_through=(0, 3)), save_mode='save_subset', end_func=b.baker(b.unchanged), valid_keys=('x', 'y', 'z'), number=100, delay=0): """Function to carry out a sequence of measurements as per iter_list, take an image at each position, post-process it and return a final result. :param exp_obj: The experiment object. :param positions_dict: A dictionary of lists of 2-tuples to indicate all positions where images should be taken: {'x': [(n_x1, step_x1), ...], 'y': [(n_y1, step_y1), ...], 'z': ...], where each key indicates the axis to move, 'n' is the number of times to step (resulting in n+1 images) and 'step' is the number of microsteps between each subsequent image. So {'x': [(3, 100)]} would move only the x-axis of the microscope, taking 4 images at x=-150, x=-50, x=50, x=150 microsteps relative to the initial position. Note that: - Not all keys need to be specified. - All lists must be the same length. - All measurements will be taken symmetrically about the initial position. The position of each tuple in the list is important. If we have {'x': [(1, 100), (0, 100)], 'y': [(2, 50), (3, 40)]}, then tuples of the same index from each list will be combined into an array. This means that for the 0th index of the list, for x we have the positions [-50, 50] and [0] and for y [-50, 0, 50] and [-60, -20, 20, 60] respectively. [-50, 50] and [-50, 0, 50] will be combined to get the resulting array of [[-50, -50], [-50, 0], [-50, 50], [50, -50], [50, 0], [50, 50]], and the latter two to get [[0, -60], [0, -20], [0, 20], [0, 60]]. These are all the positions the stage will move to (the format here is [x, y]), iterating through each array in the order given before applying end_func ONCE RIGHT AT THE END. To run after each array, run this function multiple times. If you prefer to take images once for all the 'x' and, separately, once for all the 'y', run this function twice, once for 'x', once for 'y'. :param func_list: The post-processing curried function list, created using the baker function in the _experiments.py module. :param order_gen: A generator object that determines the order to visit each position. Takes arguments (x_positions_array, y_positions_arry, z_positions_array, intial_position_vector). :param save_mode: How to save at the end of each iteration. - 'save_each': Every single measurement is saved: {'x': [(3, 100)]} would result in 4 post-processed results being saved, which is useful if the post-processed results are image arrays. - 'save_final': Every single measurement is made before the entire set of results, as an array, is saved along with their positions, in the format [[x-column], [y-column], [z-column], [measurements-column]]. This is good for the post-processed results that are single numerical value. - 'save_subset': Each array is measured before being saved (for example, in the description of iter_dict, there are two arrays being iterated through). - None: Data is not saved at all, but is returned. Might be useful if this is intermediate step. :param end_func: A curried function, which is executed on the array of final results. This can be useful to move to a position where the final measurement is maximised. Note: if save_mode is 'save_each', the results array will be empty so end_func must be None. :param valid_keys: A tuple of strings containing all keys that are valid ways to move. if rotational degrees of freedom are introduced, this can include theta, etc. :param number: The number of measurements to average over at each position. :param delay: The time delay between each individual measurement taken at a specific position.""" exp_obj.scope.sensor.ignore_saturation = False # Verify positions_dict format. The num_arrays is the number of arrays # of positions that will be measured in sequence. num_arrays = _verify_positions(positions_dict, valid_keys=valid_keys) # Get initial position, which may not be [0, 0, 0] if scope object # has been used for something else prior to this experiment. initial_position = exp_obj.scope.stage.position # A set of results to be collected if save_mode == 'save_final'. results = [] for i in xrange(num_arrays): # For the length of each list, combine every group of tuples in the # same position to get array of positions to move to. move_by = {} for key in valid_keys: try: (n, steps) = positions_dict[key][i] move_by[key] = np.linspace(-n / 2. * steps, n / 2. * steps, n + 1) except KeyError: # If key does not exist, then keep this axis fixed. move_by[key] = np.array([0]) # Generate array of positions to move to. pos = order_gen(move_by['x'], move_by['y'], move_by['z'], initial_position) try: # For each position in the range specified, take an image, apply # all the functions in func_list on it, then either save the # measurement if save_mode = 'save_final', or append the # calculation to a results file and save it all at the end. while True: results = read_move_save(exp_obj, pos, func_list, save_mode, number, delay, results) except (StopIteration, KeyboardInterrupt) as e: # Iterations finished - save the subset of results. if save_mode == 'save_subset' or (save_mode == 'save_final' and e is KeyboardInterrupt): save_results(exp_obj, results, number, delay, why_ended=str(e)) if e is KeyboardInterrupt: # Move to original position and exit program. print "Aborted, moving back to initial position. Exiting " \ "program." exp_obj.scope.stage.move_to_pos(initial_position) sys.exit() except b.NonZeroReading: # After reading a non-zero value stay at that current position, # save all results up to that point. print "Non-zero value read." except b.Saturation: # If the max value has been read, then the user must turn down # the gain. Once turned down, this entire move_sequence must be # re-measured. return move_capture(exp_obj, positions_dict, func_list, order_gen, save_mode, end_func, valid_keys, number, delay) exp_obj.scope.sensor.ignore_saturation = False if save_mode != 'save_each': if save_mode == 'save_final': save_results(exp_obj, results, number, delay, 'brightness_final') elif save_mode != 'save_subset' and save_mode is not None: raise ValueError('Invalid save mode.') # Process the result and return it. Remember end_func is unchanged # by default, which returns the array as-is. return end_func(np.array(results)) elif save_mode == 'save_each': if end_func is not None or end_func is not b.baker(b.unchanged): w.warn('end_func will not be used when the save_mode is ' '\'save_each\', as it is here.') else: raise ValueError('Invalid save_mode.')
def run(self, save_mode='save_final'): initial_pos = self.scope.stage.position self.scope.sensor.ignore_saturation = False xy_results = [] maxima_values = [] z_step = self.step_size[2] z_direction = 1 for i in range(3): while np.any(self.step_size[:2] > self.config_dict['min_step']): # Changed the algorithm to align well in x and y, then adjust z # slightly and see the difference for axis in [[1, 0, 0], [0, 1, 0]]: count = 0 # Track how many times that axis has been # measured this time. axis_index = axis.index(1) + 1 print 'axis', axis_index while True: try: results = [] current_pos = self.scope.stage.position positions = self.next_positions(axis, current_pos) gen = b.yield_pos(positions) func_list = b.baker(b.saturation_reached, args=['mmt-placeholder', self.scope.sensor]) count += 1 while True: results = _exp.read_move_save( self, gen, func_list, save_mode, self.number, self.delay, results) except b.Saturation: # If the measurement saturates, then take that set of # measurements again, with the new values of parameters # that change (step_size, number, delay). Don't ignore # saturation exceptions here, as this is fine # alignment! results = np.array(results) _exp.save_results(self, results, self.number, self.delay, why_ended='Saturation') self.scope.stage.move_to_pos(current_pos) continue except KeyboardInterrupt: if save_mode == 'save_subset' or save_mode == \ 'save_final': _exp.save_results( self, results, self.number, self.delay, why_ended=str(KeyboardInterrupt)) sys.exit() except StopIteration: # Iterations finished - save the subset of results. results = np.array(results) # Note only unsaturated results are saved in # xy_results, and that later, only results with the # lowest gain are used in calculation. xy_results.append(results) _exp.save_results( self, results, self.number, self.delay, why_ended=str(StopIteration)) if self.process_com(results, axis_index) or \ count >= 5: print "breaking" break self.scope.sensor.ignore_saturation = False self.step_size[axis_index - 1] /= 2 print "step size is now ", self.step_size # After aligning in x and y, note down the position, max brightness # and width of the peak. Note we get width from the set of previous # xy_results, by looking at where brightness exceeds the half # maximum. peak_position = self.scope.stage.position brightness = self.scope.sensor.average_n(self.number, t=self.delay) xy_results = np.array(xy_results) # We need to take results with the same value of gain, so separate # such that only the lowest values of gain (strongest signals) are # kept. lowest_gain = xy_results[np.where(xy_results[:, :, 8] == np.min( xy_results[:, :, 8]))] above_half_max = lowest_gain[np.where(lowest_gain[:, :, 4] >= 0.5 * brightness[0])] # Get a rough measure of the width of the xy region by just finding # the average of the range in each of x and y. width = np.mean((self.arr_range(above_half_max[:, :, 1]), self.arr_range(above_half_max[:, :, 2]))) # Append this to maxima values to store details of the brightness. maxima_values.append([peak_position[0], peak_position[1], peak_position[2], brightness[0], brightness[1], width]) print "maxima values", maxima_values try: # Ensure last reading is outside the range of the previous # readings and its error. if not (maxima_values[-2][3] - maxima_values[-2][4] <= maxima_values[-1][3] <= maxima_values[-2][3] + maxima_values[-2][4]): if maxima_values[-2][3] >= maxima_values[-1][3] and \ maxima_values[-2][5] <= maxima_values[-1][5]: # If the readings are getting dimmer and wider, change # z direction. z_direction *= -1 except IndexError: # Not enough readings to compare this. pass # Move in z by a step size that decreases slowly. Reset x and y # step sizes after each z motion. TODO Allow z step size to reduce. self.scope.stage.focus_rel(z_step * z_direction) print "moved to pos", self.scope.stage.position self.reset() _exp.save_results(self, np.array(maxima_values), self.number, self.delay, name='maxima_values', why_ended='complete')
def move_capture(exp_obj, positions_dict, func_list=b.baker(b.unchanged), order_gen=b.baker(b.raster, position_to_pass_through=(0, 3)), save_mode='save_subset', end_func=b.baker(b.unchanged), valid_keys=('x', 'y', 'z'), number=100, delay=0): """Function to carry out a sequence of measurements as per iter_list, take an image at each position, post-process it and return a final result. :param exp_obj: The experiment object. :param positions_dict: A dictionary of lists of 2-tuples to indicate all positions where images should be taken: {'x': [(n_x1, step_x1), ...], 'y': [(n_y1, step_y1), ...], 'z': ...], where each key indicates the axis to move, 'n' is the number of times to step (resulting in n+1 images) and 'step' is the number of microsteps between each subsequent image. So {'x': [(3, 100)]} would move only the x-axis of the microscope, taking 4 images at x=-150, x=-50, x=50, x=150 microsteps relative to the initial position. Note that: - Not all keys need to be specified. - All lists must be the same length. - All measurements will be taken symmetrically about the initial position. The position of each tuple in the list is important. If we have {'x': [(1, 100), (0, 100)], 'y': [(2, 50), (3, 40)]}, then tuples of the same index from each list will be combined into an array. This means that for the 0th index of the list, for x we have the positions [-50, 50] and [0] and for y [-50, 0, 50] and [-60, -20, 20, 60] respectively. [-50, 50] and [-50, 0, 50] will be combined to get the resulting array of [[-50, -50], [-50, 0], [-50, 50], [50, -50], [50, 0], [50, 50]], and the latter two to get [[0, -60], [0, -20], [0, 20], [0, 60]]. These are all the positions the stage will move to (the format here is [x, y]), iterating through each array in the order given. If you prefer to take images once for all the 'x' and, separately, once for all the 'y', run this function twice, once for 'x', once for 'y'. :param func_list: The post-processing curried function list, created using the baker function in the _experiments.py module. :param order_gen: A generator object that determines the order to visit each position. Takes arguments (x_positions_array, y_positions_arry, z_positions_array, intial_position_vector). :param save_mode: How to save at the end of each iteration. - 'save_each': Every single measurement is saved: {'x': [(3, 100)]} would result in 4 post-processed results being saved, which is useful if the post-processed results are image arrays. - 'save_final': Every single measurement is made before the entire set of results, as an array, is saved along with their positions, in the format [[x-column], [y-column], [z-column], [measurements-column]]. This is good for the post-processed results that are single numerical value. - 'save_subset': Each array is measured before being saved (for example, in the description of iter_dict, there are two arrays being iterated through). - None: Data is not saved at all, but is returned. Might be useful if this is intermediate step. :param end_func: A curried function, which is executed on the array of final results. This can be useful to move to a position where the final measurement is maximised. Note: if save_mode is 'save_each', the results array will be empty so end_func must be None. :param valid_keys: A tuple of strings containing all keys that are valid ways to move. if rotational degrees of freedom are introduced, this can include theta, etc. :param number: The number of measurements to average over at each position. :param delay: The time delay between each individual measurement taken at a specific position.""" b.ignore_saturation = False # Verify positions_dict format. The num_arrays is the number of arrays # of positions that will be measured in sequence. num_arrays = _verify_positions(positions_dict, valid_keys=valid_keys) # Get initial position, which may not be [0, 0, 0] if scope object # has been used for something else prior to this experiment. initial_position = exp_obj.scope.stage.position # A set of results to be collected if save_mode == 'save_final'. results = [] for i in xrange(num_arrays): # For the length of each list, combine every group of tuples in the # same position to get array of positions to move to. move_by = {} for key in valid_keys: try: (n, steps) = positions_dict[key][i] move_by[key] = np.linspace(-n / 2. * steps, n / 2. * steps, n + 1) except KeyError: # If key does not exist, then keep this axis fixed. move_by[key] = np.array([0]) print "move-by", move_by # Generate array of positions to move to. pos = order_gen(move_by['x'], move_by['y'], move_by['z'], initial_position) attrs = {'mmts_per_reading': number, 'delay_between': delay} try: # For each position in the range specified, take an image, apply # all the functions in func_list on it, then either save the # measurement if save_mode = 'save_final', or append the # calculation to a results file and save it all at the end. while True: next_pos = next(pos) # This returns StopIteration at end. exp_obj.scope.stage.move_to_pos(next_pos) # Mmt is returned as a tuple of (mean brightness, # error_in_mean). mmt = exp_obj.scope.sensor.average_n(number, t=delay) processed = mmt # Post-process. Converting to list first allows even single # functions to be understood. try: len(func_list) except TypeError: func_list = [func_list] for function in func_list: processed = function(processed) # Save this array in HDF5 file. if save_mode == 'save_each': exp_obj.gr.create_dataset('post_processed', attrs={ 'Position': exp_obj.scope.stage.position, 'mmts_per_reading': number, 'delay_between': delay }, data=processed) else: # The curried function and 'save_final' both use the # array of final results. reading = [ elapsed(exp_obj.scope.start), exp_obj.scope.stage.position[0], exp_obj.scope.stage.position[1], exp_obj.scope.stage.position[2], processed[0], processed[1] ] print reading results.append(reading) except StopIteration: # Iterations finished - save the subset of results and take the # next set. if save_mode == 'save_subset': # Save after every array of motions. results = np.array(results, dtype=np.float) exp_obj.gr.create_dataset('brightness_subset', data=results, attrs=attrs) except KeyboardInterrupt: print "Aborted, moving back to initial position." exp_obj.scope.stage.move_to_pos(initial_position) print "Exiting program." sys.exit() except b.NonZeroReading: # After reading a non-zero value stay at that current position, # save all results up to that point. print "Non-zero value read." except b.Saturation: # If the max value has been read, then the user must turn down # the gain. Once turned down, this entire move_sequence must be # re-measured. return move_capture(exp_obj, positions_dict, func_list, order_gen, save_mode, end_func, valid_keys, number, delay) results = np.array(results, dtype=np.float) b.ignore_saturation = False if save_mode != 'save_each': if save_mode == 'save_final': exp_obj.gr.create_dataset('brightness_final', data=results, attrs={ 'mmts_per_reading': number, 'delay_between': delay }) elif all(save_mode != valid_mode for valid_mode in ['save_subset', None]): raise ValueError('Invalid save mode.') # Process the result and return it. Remember end_func is unchanged # by default, which returns the array as-is. return end_func(results) elif save_mode == 'save_each': if end_func is not None or end_func is not b.baker(b.unchanged): w.warn('end_func will not be used when the save_mode is ' '\'save_each\', as it is here.') else: raise ValueError('Invalid save_mode.')