Ejemplo n.º 1
0
 def dull_scan(mot, count, sig=None, sleep_time=0):
     if sig:
         thread = threading.Thread(target=sig_sequence, args=(sig, ))
         thread.start()
     for i in range(count):
         yield from checkpoint()
         try:
             yield from mv(mot, i)
         except:
             pass
         # make every step take 1s extra
         yield from sleep(sleep_time)
         yield from checkpoint()
         yield from create()
         yield from read(mot)
         yield from save()
         yield from checkpoint()
Ejemplo n.º 2
0
 def inner_xpcs():
     yield from bp.checkpoint()
     yield from bp.create()
     for det in detectors:
         # Start acquisition.
         yield from bp.trigger(det)
         # Read the UID that points to this dataset in progress.
         yield from bp.read(det)
     # Insert an 'Event' document into databroker. Now we can access the (partial) dataset.
     yield from bp.save()
     # *Now* wait for the detector to actual finish acquisition.
     yield from bp.wait()
Ejemplo n.º 3
0
def iterwalk(detectors,
             motors,
             goals,
             starts=None,
             first_steps=1,
             gradients=None,
             detector_fields='centroid_x',
             motor_fields='alpha',
             tolerances=20,
             system=None,
             averages=1,
             overshoot=0,
             max_walks=None,
             timeout=None,
             recovery_plan=None,
             filters=None):
    """
    Iteratively adjust a system of detectors and motors where each motor
    primarily affects the reading of a single detector but also affects the
    other axes parasitically.

    This is a Bluesky plan, but it does not have start run or end run
    decorators so it can be included inside of other plans. It yields a
    checkpoint message before adjusting the detector positions and before
    executing a walk substep.

    All list arguments that expect one entry per detector must be the same
    length as the detectors list. If any optional list arguments are provided
    as single values instead of as iterables, iterwalk will interpret it as
    "use this value for every detector".

    Parameters
    ----------
    detectors: list of objects
        These are your axes of motion, which must implement both the Bluesky
        reader interface and the setter interface. The set method must accept
        the "IN" and "OUT" arguments to move the device in and out. It is
        assumed that detectors earlier in the list block detectors later in the
        list.

    motors: list of objects
        These are your axes of motion, which must implement both the Bluesky
        reader interface and the setter interface.

    goals: list of numbers
        These are the scalar readbacks expected from the detectors at the
        desired positions.

    starts: list of numbers, optional
        If provided, these are the nominal positions to move the motors to
        before starting to align.

    first_steps: list of numbers, optional.
        This is how far the motors will be moved in an initial probe step as we
        gauge the detector's response. This argument is ignored if 'gradients'
        is provided.

    gradients: list of numbers, optional
        If provided, this is a guess at the ratio between motor move and change
        in detector readback. This will be used to make a very good first guess
        at the first step towards the goal pixel. This should be in units of
        detector/motor

    detector_fields: list of strings, optional
        For each detector, this is the field we're walking to.

    motor_fields: list of strings, optional
        For each motor, this is the field we're using along with the
        detector_field to build our linear regression. This should be the
        readback with nominally the same value as the position field.

    tolerances: list of numbers, optional
        If our detector readbacks are within these tolerances of the goal
        positions, we'll treat the goal as reached.

    system: list of readable objects, optional
        Other system parameters that we'd like to read during the walk.

    averages: list of numbers, optional
        For each detector, this is the number of shots to average before
        settling on a reading.

    overshoot: number, optional
        The percent to overshoot at each goal step. For these parasitic
        systems, over or undershooting can allow convergence to occur faster.
        An overshoot of 0 is no overshoot, an overshoot of 0.1 is a 10%
        overshoot, an overshoot of -0.2 is a 20% undershoot, etc.

    max_walks: int, optional
        The number of sets of walks to try before giving up on the alignment.
        E.g. if max_walks is 3, we'll move each motor/detector pair 3 times in
        series before giving up.

    timeout: number, optional
        The maximum time to allow for the alignment before aborting. The
        timeout is checked after each walk step.

    recovery_plan: plan, optional
        A backup plan to run when no there is no readback on the detectors.
        This plan should expect a complete description of the situation in the
        form of all arguments given to iterwalk, except for recovery_plan. Note
        that these arguments are listified before being passed to
        recovery_plan. It will also be passed 'index', which is the index in
        all of the list arguments that is currently active.
        Also note that there is no requirement to use all of the provided
        arguments. A good practice is to have recovery_plan accept and ignore
        **kwargs while explicitly including desired keywords to use.

    filters: list of dictionaries
        Each entry in this list should be a valid input to the filters argument
        in the lower functions, such as walk_to_pixel and measure_average.
    """
    num = len(detectors)

    # Listify most optional arguments
    goals = as_list(goals, num)
    starts = as_list(starts, num)
    first_steps = as_list(first_steps, num, float)
    gradients = as_list(gradients, num)
    detector_fields = as_list(detector_fields, num)
    motor_fields = as_list(motor_fields, num)
    tolerances = as_list(tolerances, num)
    system = as_list(system)
    averages = as_list(averages, num)
    filters = as_list(filters, num)

    logger.debug("iterwalk aligning %s to %s on %s", motors, goals, detectors)

    # Debug counters
    mirror_walks = 0
    yag_cycles = 0
    recoveries = 0
    # Set up end conditions
    n_steps = 0
    start_time = time.time()
    models = [None] * num
    finished = [False] * num
    done_pos = [0] * num
    while True:
        index = 0
        while index < num:
            try:
                # Before each walk, check the global timeout.
                if timeout is not None and time.time() - start_time > timeout:
                    raise RuntimeError("Iterwalk has timed out after %s s",
                                       time.time() - start_time)

                logger.debug("putting imager in")
                ok = (yield from prep_img_motors(index, detectors, timeout=15))
                yag_cycles += 1

                # Be loud if the yags fail to move! Operator should know!
                if not ok:
                    err = "Detector motion timed out!"
                    logger.error(err)
                    raise RuntimeError(err)

                # Choose a start position for the first move if it was given
                if n_steps == 0 and starts[index] is not None:
                    firstpos = starts[index]
                else:
                    firstpos = None

                # Give higher-level a chance to recover or suspend
                yield from checkpoint()

                # Set up the system to not include the redundant objects
                full_system = copy(system)
                try:
                    full_system.remove(motors[index])
                except ValueError:
                    pass
                try:
                    full_system.remove(detectors[index])
                except ValueError:
                    pass

                # Set flag for needing recovery before walking
                recover_pre_walk = True
                original_position = motors[index].position

                # Check if we're already done
                logger.debug("measure_average on det=%s, mot=%s, sys=%s",
                             detectors[index], motors[index], full_system)
                avgs = (yield from measure_average(
                    [detectors[index], motors[index]] + full_system,
                    num=averages[index],
                    filters=filters[index]))

                pos = avgs[field_prepend(detector_fields[index],
                                         detectors[index])]
                logger.debug("recieved %s from measure_average on %s", pos,
                             detectors[index])

                if abs(pos - goals[index]) < tolerances[index]:
                    logger.info("Beam was aligned on %s without a move",
                                detectors[index])
                    finished[index] = True
                    done_pos[index] = pos
                    if all(finished):
                        logger.debug("beam aligned on all yags")
                        break
                    # Increment index before restarting loop
                    index += 1
                    continue
                else:
                    # If any of the detectors were wrong, reset finished flags
                    logger.debug("reset alignment flags before move")
                    finished = [False] * num

                # Modify goal to use overshoot
                if index == 0:
                    goal = goals[index]
                else:
                    goal = (goals[index] - pos) * (1 + overshoot) + pos

                # Clear flag for needing recovery before walking
                recover_pre_walk = False

                # Core walk
                logger.info(('Starting walk from {} to {} on {} using {}'
                             ''.format(pos, goal, detectors[index].name,
                                       motors[index].name)))
                pos, models[index] = (yield from walk_to_pixel(
                    detectors[index],
                    motors[index],
                    goal,
                    filters=filters[index],
                    start=firstpos,
                    gradient=gradients[index],
                    target_fields=[
                        detector_fields[index], motor_fields[index]
                    ],
                    first_step=first_steps[index],
                    tolerance=tolerances[index],
                    system=full_system,
                    average=averages[index],
                    max_steps=10))
                if models[index]:
                    try:
                        gradients[index] = models[index].result.values['slope']
                        logger.debug(
                            "Found equation of ({}, {}) between "
                            "linear fit of {} to {}"
                            "".format(gradients[index],
                                      models[index].result.values['intercept'],
                                      motors[index].name,
                                      detectors[index].name))
                    except Exception as e:
                        logger.warning(e)
                        logger.warning("Unable to find gradient of "
                                       "linear fit of {} to {}"
                                       "".format(motors[index].name,
                                                 detectors[index].name))

                logger.debug("Walk reached pos %s on %s", pos,
                             detectors[index].name)
                mirror_walks += 1

                # Be loud if the walk fails to reach the pixel!
                if abs(pos - goal) > tolerances[index]:
                    err = "walk_to_pixel failed to reach the goal"
                    logger.error(err)
                    raise RuntimeError(err)

                finished[index] = True
                done_pos[index] = pos

                # Increment index before restarting loop
                index += 1
            except FilterCountError as err:
                if recovery_plan is None:
                    logger.error("No recovery plan, not attempting to recover")
                    raise

                # If we had to recover before walk_to_pixel,
                # get a fallback position for if the recovery fails
                if recover_pre_walk:
                    try:
                        fallback_pos = motors[index].nominal_position
                    except AttributeError:
                        fallback_pos = None
                    # Explicitly check again in case nominal_position is None
                    if fallback_pos is None:
                        fallback_pos = motors[index].position
                else:
                    # If we are recovering after walk_to_pixel, don't bother
                    # with the recovery plan. Just move back and adjust
                    # parameters.
                    logger.info(("Bad state reached during walk_to_pixel. "
                                 "Undoing walk_to_pixel..."))
                    yield from mv(motors[index], original_position)

                    # Reset the finished flag
                    finished = [False] * num

                    # Cut our step parameters in half, because they were
                    # probably too big
                    logger.info("Lowering initial step parameters...")
                    if gradients[index] is not None:
                        gradients[index] = gradients[index] * 2
                    if first_steps[index] is not None:
                        first_steps[index] = first_steps[index] / -2
                    continue

                ok = yield from recovery_plan(detectors=detectors,
                                              motors=motors,
                                              goals=goals,
                                              starts=starts,
                                              first_steps=first_steps,
                                              gradients=gradients,
                                              detector_fields=detector_fields,
                                              motor_fields=motor_fields,
                                              tolerances=tolerances,
                                              system=system,
                                              averages=averages,
                                              overshoot=overshoot,
                                              max_walks=max_walks,
                                              timeout=timeout,
                                              filters=filters,
                                              index=index)

                # Reset the finished tag because we moved something
                finished = [False] * num
                recoveries += 1

                # If recovery failed, move to nominal and switch to next device
                if not ok:
                    logger.info(("Recover failed, using fallback pos and "
                                 "trying next device alignment."))
                    yield from mv(motors[index], fallback_pos)
                    index += 1
                # Try again
                continue

        if all(finished):
            break

        # After each set of walks, check if we've exceeded max_walks
        n_steps += 1
        if max_walks is not None and n_steps > max_walks:
            logger.info("Iterwalk has reached the max_walks limit")
            break
    logger.info(("Finished in %.2fs after %s mirror walks, %s yag cycles, "
                 "and %s recoveries"),
                time.time() - start_time, mirror_walks, yag_cycles, recoveries)
    logger.info("Aligned to %s", done_pos)
    logger.info("Goals were %s", goals)
    logger.info("Deltas are %s", [d - g for g, d in zip(goals, done_pos)])
    logger.info("Mirror positions are %s", [m.position for m in motors])
Ejemplo n.º 4
0
def sleepy_scan():
    yield from checkpoint()
    yield from sleep(0.2)