def filter_opt_count(det, target_count=100000, md={}): """ filter_opt_count OPtimize counts using filters Assumes mu=0.2, x = [0.89, 2.52, 3.83, 10.87] I = I_o \exp(-mu*x) Only takes one detector, since we are optimizing based on it alone target is mean+2std """ dc = DocumentCache() token = yield from bps.subscribe('all', dc) yield from bps.stage(det) md = {} yield from bps.open_run(md=md) # BlueskyRun object allows interaction with documents similar to db.v2, # but documents are in memory run = BlueskyRun(dc) yield from bps.trigger_and_read([det, filter1, filter2, filter3, filter4]) data = run.primary.read()['pilatus300k_image'] mean = data[-1].mean().values.item() # xarray.DataArray methods std = data[-1].std().values.item() # xarray.DataArray methods curr_counts = mean + 2 * std # gather filter information and solve filter_status = [ round(filter1.get() / 5), round(filter2.get() / 5), round(filter3.get() / 5), round(filter4.get() / 5) ] print(filter_status) filter_status = [not e for e in filter_status] new_filters = solve_filter_setup(filter_status, curr_counts, target_count) # invert again to reflect filter status new_filters = [not e for e in new_filters] print(new_filters) # set new filters and read. For some reason hangs on bps.mv when going high filter1.put(new_filters[0] * 4.9) filter2.put(new_filters[1] * 4.9) filter3.put(new_filters[2] * 4.9) filter4.put(new_filters[3] * 4.9) yield from bps.trigger_and_read([det, filter1, filter2, filter3, filter4]) # close out run yield from bps.close_run() yield from bps.unsubscribe(token) yield from bps.unstage(det)
def measure_average(detectors, num, delay=None, stream=None): """ Measure an average over a number of shots from a set of detectors Parameters ---------- detectors : list List of detectors to read num : int Number of shots to average together delay: iterable or scalar, optional Time delay between successive readings. See ``bluesky.count`` for more details stream : AverageStream, optional If a plan will call :func:`.measure_average` multiple times, a single ``AverageStream`` instance can be created and then passed in on each call. This allows other callbacks to subscribe to the averaged data stream. If no ``AverageStream`` is provided then one is created for the purpose of this function. Returns ------- averaged_event : dict A dictionary of all the measurements taken from the list of detectors averaged for ``num`` shots. The keys follow the same naming convention as that will appear in the event documents i.e "{name}_{field}" Notes ----- The returned average dictionary will only contain keys for 'number' or 'array' fields. Field types that can not be averaged such as 'string' will be ignored, do not expect them in the output. """ # Create a stream and subscribe if not given one if not stream: stream = AverageStream(num=num) yield from subscribe('all', stream) # Manually kick the LiveDispatcher to emit a start document because we # will not see the original one since this is subscribed after open_run stream.start({'uid': None}) # Ensure we sync our stream with request if using a prior one else: stream.num = num # Measure our detectors yield from stub_wrapper(count(detectors, num=num, delay=delay)) # Return the measured average as a dictionary for use in adaptive plans return stream.last_event
def golden_section_search(signal, motor, tolerance, limits, average=None): """ Use golden-section search to find the extrema of a signal The algorithm is fed the starting range in which the extrema is contained within and a tolerance in which we would like to know the position of the extrema. For the algorithm to succeed it is a requirement that the underlying distribution is unimodal. After beginning the scan, "probe" points will be chosen to help determine how to narrow the range which contains the extrema. These probes are chosen using the golden ratio so the scan will complete in a deterministic number of iterations based on the starting range and desired resolution. Parameters ---------- signal: ophyd.Signal Signal whose distribution we are investigating motor: ophyd.OphydObject Object that is ``set`` to probe different points of the distribution. tolerance: float The size of the range we would like to narrow the position of our extrema. Note that this is not the tolerance that we will know the "value" of the extrema, but instead the resolution we will be sure that it lies within on the "x" axis limits: tuple Starting bounds that we know the extrema lie within average : int, optional Option to average the signal we are reading to limit the affect of noise on our measurements Returns ------- bounds: tuple The range in which we have determined the extrema to lie within. """ # This is boiler plate code and should be packaged into a # pre-processor, for now we repeat it as to not subscribe numerous # streams average = average or 1 stream = AverageStream(num=average) yield from bps.subscribe('all', stream) stream.start({'uid': None}) # Measurement plan def measure_probe(position): # Move motor yield from bps.mv(motor, position) # Return measurement ret = yield from measure_average([signal, motor], average, stream=stream) logger.debug("Found a values of %r at %r", ret[signal.name], position) return ret[signal.name] # If we have already found what we are looking for stop the scan (a, b) = limits region_size = b - a if region_size <= tolerance: return (a, b) # Determine the number of steps to converge n = math.ceil(math.log(tolerance/region_size) / math.log(1/golden_ratio)) logger.debug("Beginning golden-section search, " "narrowing extrema location to %r " "will require %r steps", tolerance, n) # Place holders for probe values c = b - region_size/golden_ratio d = a + region_size/golden_ratio # Examine our new probe locations low_probe = yield from measure_probe(c) high_probe = yield from measure_probe(d) # Begin iteratively narrowing range for step in range(n - 1): logger.debug("Iteration %s: Extrema is between %s and %s", step + 1, a, b) if low_probe < high_probe: # Readjust region of interest b = d d = c high_probe = low_probe region_size /= golden_ratio # Calculate next probe c = b - region_size/golden_ratio # Measure next probe low_probe = yield from measure_probe(c) else: # Readjust region of interest a = c c = d low_probe = high_probe region_size /= golden_ratio # Calculate next probe d = a + region_size/golden_ratio # Measure next probe high_probe = yield from measure_probe(d) # Return the final banding region if low_probe < high_probe: final_region = (a, d) else: final_region = (c, b) logger.debug("Extrema determined to be within %r", final_region) return final_region
def plan_simultaneously(x_centroid, y_centroid, x, y, atol, x_target=None, y_target=None): """ This BlueSky plan aligns the laser's centroid with the x-ray's centroid. This plan implements 'walk_to_pixel' from the pswalker (a beam alignment module). The plan uses an iterative procedure to align any beam to a position on a screen, when two motors move the beam along the two axes. Liveplots are updated and show the paths taken to achieve alignment. Parameters ---------- x_centroid, y_centroid : These represent the x_centroid and y_centroid x, y: These respresnt the x_motor and y_motor x_target, y_target : int Target value on the x-axis and y-axis """ #Create a figure fig = plt.figure(figsize=(15, 10)) fig.subplots_adjust(hspace=0.3, wspace=0.4) #The first subplot, which plots the y_centroid vs x_centroid ax1 = fig.add_subplot(2, 2, 1) ax1.invert_yaxis() x_centroid_y_centroid = LivePlot(y_centroid.name, x_centroid.name, ax=ax1, marker='x', markersize=7, color='orange') #The second subplot, which plots the y_centroid and x_centroid with same x-axis (y_motor) ax2 = fig.add_subplot(2, 2, 3) ax2.set_ylabel(y_centroid.name, color='red') ax3 = ax2.twinx() # ax2.invert_yaxis() # ax3.invert_yaxis() ax3.set_ylabel(x_centroid.name, color='blue') y_plot_y_centroid = LivePlot(y_centroid.name, y.name, ax=ax2, marker='x', markersize=6, color='red') y_plot_x_centroid = LivePlot(x_centroid.name, y.name, ax=ax3, marker='o', markersize=6, color='blue') #The third subplot, which plots the y_centroid and x_centroid with same x-axis (x_motor) ax4 = fig.add_subplot(2, 2, 4) ax4.set_ylabel(y_centroid.name, color='green') ax5 = ax4.twinx() ax5.set_ylabel(x_centroid.name, color='purple') x_plot_y_centroid = LivePlot(y_centroid.name, x.name, ax=ax4, marker='x', markersize=6, color='green') x_plot_x_centroid = LivePlot(x_centroid.name, x.name, ax=ax5, marker='o', markersize=6, color='purple') #Subscribe the plots token_x_centroid_y_centroid = yield from subscribe('all', x_centroid_y_centroid) token_y_plot_x_centroid = yield from subscribe('all', y_plot_x_centroid) token_y_plot_y_centroid = yield from subscribe('all', y_plot_y_centroid) token_x_plot_x_centroid = yield from subscribe('all', x_plot_x_centroid) token_x_plot_y_centroid = yield from subscribe('all', x_plot_y_centroid) #Start a new run yield from open_run( md={ 'detectors': [(x_centroid.name), (y_centroid.name)], 'motors': [(x.name), (y.name)], 'hints': { 'dimensions': [(x.hints['fields'], 'primary'), (y.hints['fields'], 'primary')] } }) #Ask for the target values if x_target is None: x_target = int(input('Enter the x value: ')) if y_target is None: y_target = int(input('Enter the y value: ')) #Iteratively move until x_target and x-centroid are within a certain threshold of each other while True: if not np.isclose(x_target, x_centroid.get(), atol): yield from walk_to_pixel(x_centroid, x, x_target, first_step=0.1, target_fields=[x_centroid.name, x.name], tolerance=atol, average=5, system=[y, y_centroid]) elif not np.isclose(y_target, y_centroid.get(), atol): yield from walk_to_pixel(y_centroid, y, y_target, first_step=0.1, tolerance=atol, average=5, target_fields=[y_centroid.name, y.name], system=[x, x_centroid]) else: break # plt.show(block=True) #Close the run yield from close_run() #Unsubscribe the plots yield from unsubscribe(token_x_centroid_y_centroid) yield from unsubscribe(token_y_plot_x_centroid) yield from unsubscribe(token_y_plot_y_centroid) yield from unsubscribe(token_x_plot_x_centroid) yield from unsubscribe(token_x_plot_y_centroid)