Example #1
0
def _collect_img(exposure,
                 dark_sub_bool,
                 sample_md,
                 tag,
                 RE_instance,
                 *,
                 calibrant=None,
                 detector=None):
    """helper function to collect image and return it"""
    # grab beamtime object linked to run_engine
    bto = RE_instance.beamtime
    plan = ScanPlan(bto, ct, exposure).factory()
    sample_md.update(bto)

    if tag == "calib":
        # instantiate Calibrant class
        calibrant_obj = Calibration(calibrant=calibrant).calibrant
        if calibrant_obj is None:
            raise xpdAcqException("Invalid calibrant")
        dSpacing = calibrant_obj.dSpacing
        if not dSpacing:
            raise xpdAcqException("empty dSpacing from calibrant")
        sample_md.update({"dSpacing": dSpacing, "detector": detector})
        plan = bpp.msg_mutator(plan, _inject_calibration_tag)

    # collect image
    uid = RE_instance(sample_md, plan)
    """
Example #2
0
def inject_metadata(plan: typing.Generator,
                    metadata: dict) -> typing.Generator:
    """Inject the metadata into a plan."""
    def _inject_metadata(msg: Msg):
        """Inject metadata in the start of the run."""
        if msg.command == "open_run":
            msg.kwargs.update(**metadata)
        return msg

    plan1 = bpp.msg_mutator(plan, _inject_metadata)
    return plan1
Example #3
0
def test_simple_replace():
    new_cmd = 'replaced'

    def change_command(msg):
        return msg._replace(command=new_cmd)

    num = 10
    msgs = EchoRE(msg_mutator(echo_plan(num=num), change_command))
    _verify_msg_seq(msgs,
                    m_len=num,
                    cmd_sq=[new_cmd] * num,
                    args_sq=[()] * num,
                    kwargs_sq=[{}] * num)
Example #4
0
def test_simple_replace():
    new_cmd = 'replaced'

    def change_command(msg):
        return msg._replace(command=new_cmd)

    num = 10
    msgs = EchoRE(msg_mutator(echo_plan(num=num),
                              change_command))
    _verify_msg_seq(msgs, m_len=num,
                    cmd_sq=[new_cmd]*num,
                    args_sq=[()]*num,
                    kwargs_sq=[{}]*num)
Example #5
0
    def inner():
        # Run plan (stripping open/close run messages)
        yield from msg_mutator(plan, block_run_control)

        # Yield result of Lorentz model
        logger.debug(model.result.fit_report())
        max_position = model.result.values['center']

        # Check that the estimated position is reasonable
        if not bounds[0] < max_position < bounds[1]:
            raise ValueError("Predicted maximum position of {} is outside the "
                             "bounds {}".format(max_position, bounds))
        # Order move to maximum position
        logger.debug("Travelling to maximum of Lorentz at %s", max_position)
        yield from abs_set(motor, model.result.values['center'], wait=True)
Example #6
0
def test_msg_mutator_skip():
    def skipper(msg):
        if msg.command == 'SKIP':
            return None
        return msg

    def skip_plan():
        for c in 'abcd':
            yield Msg(c, None)
            yield Msg('SKIP', None)

    pln = msg_mutator(skip_plan(), skipper)
    msgs = EchoRE(pln)
    _verify_msg_seq(msgs, m_len=4,
                    cmd_sq='abcd',
                    args_sq=[()]*4,
                    kwargs_sq=[{}]*4)
Example #7
0
def test_msg_mutator_skip():
    def skipper(msg):
        if msg.command == 'SKIP':
            return None
        return msg

    def skip_plan():
        for c in 'abcd':
            yield Msg(c, None)
            yield Msg('SKIP', None)

    pln = msg_mutator(skip_plan(), skipper)
    msgs = EchoRE(pln)
    _verify_msg_seq(msgs,
                    m_len=4,
                    cmd_sq='abcd',
                    args_sq=[()] * 4,
                    kwargs_sq=[{}] * 4)
Example #8
0
def _collect_img(exposure,
                 dark_sub_bool,
                 sample_md,
                 RE_instance,
                 *,
                 calibrant=None,
                 detector=None):
    """helper function to collect image and return it"""
    # grab beamtime object linked to run_engine
    bto = RE_instance.beamtime
    plan = ScanPlan(bto, ct, exposure).factory()
    sample_md.update(bto)
    dSpacing = find_dspacing(calibrant)
    sample_md.update({"dSpacing": dSpacing, "detector": detector})
    plan = bpp.msg_mutator(plan, _inject_calibration_tag)

    # collect image
    RE_instance(sample_md, plan)
Example #9
0
def _collect_img(exposure,
                 dark_sub_bool,
                 sample_md,
                 tag,
                 RE_instance,
                 *,
                 calibrant=None,
                 detector=None):
    """helper function to collect image and return it"""
    # grab beamtime object linked to run_engine
    bto = RE_instance.beamtime
    plan = ScanPlan(bto, ct, exposure).factory()
    sample_md.update(bto)

    if tag == "calib":
        # instantiate Calibrant class
        calibrant_obj = Calibration(calibrant=calibrant).calibrant
        if calibrant_obj is None:
            raise xpdAcqException("Invalid calibrant")
        dSpacing = calibrant_obj.dSpacing
        if not dSpacing:
            raise xpdAcqException("empty dSpacing from calibrant")
        sample_md.update({"dSpacing": dSpacing, "detector": detector})
        plan = bpp.msg_mutator(plan, _inject_calibration_tag)

    # collect image
    uid = RE_instance(sample_md, plan)
    # last one must be light
    db = xpd_configuration["db"]
    light_header = db[uid[-1]]
    dark_uid = light_header["start"].get("sc_dk_field_uid")
    dark_header = db[dark_uid]
    dark_img = dark_header.data(glbl["image_field"])
    dark_img = np.asarray(next(dark_img)).squeeze()
    img = light_header.data(glbl["image_field"])
    img = np.asarray(next(img)).squeeze()

    if dark_sub_bool:
        img -= dark_img
    # FIXME: filename template from xpdAn
    fn_template = "from_calib_func_{}.poni".format(_timestampstr(time.time()))

    return img, fn_template
Example #10
0
    def translate_to_plan(self, plan, sample):
        """Translate a plan input into a generator

        Parameters
        ----------
        sample : list of int or dict-like
            Sample metadata. If a beamtime object is linked,
            an integer will be interpreted as the index appears in the
            ``bt.list()`` method, corresponding metadata will be passed.
            A customized dict can also be passed as the sample
            metadata.
        plan : list of int or generator
            Scan plan. If a beamtime object is linked, an integer
            will be interpreted as the index appears in the
            ``bt.list()`` method, corresponding scan plan will be
            A generator or that yields ``Msg`` objects (or an iterable
            that returns such a generator) can also be passed.

        Returns
        -------
        plan : generator
            The generator of messages for the plan

        """
        if isinstance(plan, list):
            plan = [self.translate_to_plan(p, s) for p, s in zip(plan, sample)]
        # If a plan is given as a int, look in up in the global registry.
        else:
            if isinstance(plan, int):
                try:
                    plan = list(self.beamtime.scanplans.values())[plan]
                except IndexError:
                    print("WARNING: hmm, there is no scanplan with index `{}`"
                          ", please do `bt.list()` to check if it exists yet".
                          format(plan))
                    return
            # If the plan is an xpdAcq 'ScanPlan', make the actual plan.
            if isinstance(plan, ScanPlan):
                plan = plan.factory()
            mm = _sample_injector_factory(sample)
            plan = bpp.msg_mutator(plan, mm)
        return plan
Example #11
0
    def __call__(self,
                 sample,
                 plan,
                 subs=None,
                 *,
                 verify_write=False,
                 dark_strategy=periodic_dark,
                 robot=False,
                 **metadata_kw):
        """
        Execute a plan

        Any keyword arguments other than those listed below will
        be interpreted as metadata and recorded with the run.

        Parameters
        ----------
        sample : int or dict-like or list of int or dict-like
            Sample metadata. If a beamtime object is linked,
            an integer will be interpreted as the index appears in the
            ``bt.list()`` method, corresponding metadata will be passed.
            A customized dict can also be passed as the sample
            metadata.
        plan : int or generator or list of int or generator
            Scan plan. If a beamtime object is linked, an integer
            will be interpreted as the index appears in the
            ``bt.list()`` method, corresponding scan plan will be
            A generator or that yields ``Msg`` objects (or an iterable
            that returns such a generator) can also be passed.
        subs: callable, list, or dict, optional
            Temporary subscriptions (a.k.a. callbacks) to be used on
            this run. Default to None. For convenience, any of the
            following are accepted:

            * a callable, which will be subscribed to 'all'
            * a list of callables, which again will be subscribed to 'all'
            * a dictionary, mapping specific subscriptions to callables or
              lists of callables; valid keys are {'all', 'start', 'stop',
              'event', 'descriptor'}

        verify_write: bool, optional
            Double check if the data have been written into database.
            In general data is written in a lossless fashion at the
            NSLS-II. Therefore, False by default.
        dark_strategy: callable, optional.
            Protocol of taking dark frame during experiment. Default
            to the logic of matching dark frame and light frame with
            the sample exposure time and frame rate. Details can be
            found at ``http://xpdacq.github.io/xpdAcq/usb_Running.html#automated-dark-collection``
        robot: bool, optional
            If true run the scan as a robot scan, defaults to False
        metadata_kw:
            Extra keyword arguments for specifying metadata in the
            run time. If the extra metdata has the same key as the
            ``sample``, ``ValueError`` will be raised.

        Returns
        -------
        uids : list
            list of uids (i.e. RunStart Document uids) of run(s)
        """
        if self.md.get("robot", None) is not None:
            raise RuntimeError("Robot must be specified at call time, not in"
                               "global metadata")
        if robot:
            metadata_kw.update(robot=True)
        # The CustomizedRunEngine knows about a Beamtime object, and it
        # interprets integers for 'sample' as indexes into the Beamtime's
        # lists of Samples from all its Experiments.

        # Turn everything into lists
        sample, plan = self._normalize_sample_plan(sample, plan)
        # Turn ints into actual samples
        sample = self.translate_to_sample(sample)
        if robot:
            print("This is the current experimental plan:")
            print("Sample Name: Sample Position")
            for s, p in [
                (k, [o[1] for o in v])
                    for k, v in groupby(zip(sample, plan), key=lambda x: x[0])
            ]:
                print(
                    s["sample_name"],
                    ":",
                    self._beamtime.robot_info[s["sa_uid"]],
                )
                for pp in p:
                    # Check if this is a registered scanplan
                    if isinstance(pp, int):
                        print(
                            indent(
                                "{}".format(
                                    list(
                                        self.beamtime.scanplans.values())[pp]),
                                "\t",
                            ))
                    else:
                        print("This scan is not a registered scanplan so no "
                              "summary")
            ip = input("Is this ok? [y]/n")
            if ip.lower() == "n":
                return
        # Turn ints into generators
        plan = self.translate_to_plan(plan, sample)

        # Collect the plans by contiguous samples and chain them
        sample, plan = zip(
            *[(k, pchain(*[o[1] for o in v]))
              for k, v in groupby(zip(sample, plan), key=lambda x: x[0])])

        # Make the complete plan by chaining the chained plans
        total_plan = []
        for s, p in zip(sample, plan):
            if robot:
                # If robot scan inject the needed md into the sample
                s.update(self._beamtime.robot_info[s["sa_uid"]])
                total_plan.append(robot_wrapper(p, s))
            else:
                total_plan.append(p)
        plan = pchain(*total_plan)

        _subs = normalize_subs_input(subs)
        if verify_write:
            _subs.update({"stop": verify_files_saved})

        if self._beamtime and self._beamtime.get("bt_wavelength") is None:
            print("WARNING: there is no wavelength information in current"
                  "beamtime object, scan will keep going....")

        if glbl["shutter_control"]:
            # Alter the plan to incorporate dark frames.
            # only works if user allows shutter control
            if glbl["auto_dark"]:
                plan = dark_strategy(plan)
                plan = bpp.msg_mutator(plan, _inject_qualified_dark_frame_uid)
            # force to close shutter after scan
            plan = bpp.finalize_wrapper(
                plan,
                bps.abs_set(
                    xpd_configuration["shutter"],
                    XPD_SHUTTER_CONF["close"],
                    wait=True,
                ),
            )

        # Load calibration file
        if glbl["auto_load_calib"]:
            plan = bpp.msg_mutator(plan, _inject_calibration_md)
        # Insert xpdacq md version
        plan = bpp.msg_mutator(plan, _inject_xpdacq_md_version)
        # Insert analysis stage tag
        plan = bpp.msg_mutator(plan, _inject_analysis_stage)
        # Insert filter metadata
        plan = bpp.msg_mutator(plan, _inject_filter_positions)

        # Execute
        return super().__call__(plan, subs, **metadata_kw)
Example #12
0
def xpdacq_composer(beamtime: Beamtime,
                    sample: typing.Union[int, dict, typing.List[int]],
                    plan: typing.Union[int, Generator, typing.List[int],
                                       typing.Generator],
                    *,
                    robot: bool = False,
                    shutter_control: typing.Tuple[Device, typing.Any] = None,
                    dark_strategy: typing.Callable = None,
                    auto_load_calib: bool = False) -> typing.Generator:
    """Create a list of plans for an xpd experiment. Used in `~xpdacq.xpdacq.CumstomeizedRunEngine.__call__`.

    Parameters
    ----------
    beamtime :
        The beamtime object.

    sample :
        If a beamtime object is linked, an integer will be interpreted as the index appears in the ``bt.list()``
        method, corresponding metadata will be passed. A customized dict can also be passed as the sample
        metadata.

    plan :
        Scan plan. If a beamtime object is linked, an integer will be interpreted as the index appears in the
        ``bt.list()`` method, corresponding scan plan will be A generator or that yields ``Msg`` objects (or an
        iterable that returns such a generator) can also be passed.

    robot :
        If True, the plan is meant to be using robot.

    shutter_control :
        A tuple of the shutter device and its close state. The shutter will be closed after the whole scan.
        If None, shutter won't be controlled and no dark will be taken.

    dark_strategy :
        The strategy how to take the dark frame.

    auto_load_calib :
        If True, the calibration meta-data will be injected into the run. Else, do nothing.

    Returns
    -------
    grand_plan :
        The grand plan to be run by the RunEngine.
    """
    # check wavelength
    warn_wavelength(beamtime)
    # noramlize the sample and plan to two lists with the same length
    lst_sample, lst_plan = _normalize_sample_plan(sample, plan)
    # Turn ints into actual sample dictionary
    lst_metadata = [translate_to_sample(beamtime, s) for s in lst_sample]
    # Turn ints into bluesky generators
    lst_bp_plan = [translate_to_plan(beamtime, p) for p in lst_plan]
    # Make the complete plan by chaining the chained plans
    if robot:
        lst_bp_plan = gen_robot_plans(beamtime, lst_metadata, lst_bp_plan)
    # shutter control and dark
    if shutter_control:
        shutter, close_state = shutter_control
        # Alter the plan to incorporate dark frames.
        if dark_strategy:
            lst_bp_plan = [dark_strategy(p) for p in lst_bp_plan]
            lst_bp_plan = [
                bpp.msg_mutator(p, _inject_qualified_dark_frame_uid)
                for p in lst_bp_plan
            ]
        # force to close shutter after scan
        lst_bp_plan = [
            close_shutter_at_last(p, shutter, close_state) for p in lst_bp_plan
        ]
    # Load calibration file
    if auto_load_calib:
        lst_bp_plan = [
            bpp.msg_mutator(p, _inject_calibration_md) for p in lst_bp_plan
        ]
    # Insert xpdacq md version
    lst_bp_plan = [
        bpp.msg_mutator(p, _inject_xpdacq_md_version) for p in lst_bp_plan
    ]
    # Insert analysis stage tag
    lst_bp_plan = [
        bpp.msg_mutator(p, _inject_analysis_stage) for p in lst_bp_plan
    ]
    # Insert filter metadata
    lst_bp_plan = [
        bpp.msg_mutator(p, _inject_filter_positions) for p in lst_bp_plan
    ]
    # Inject the sample metadata
    lst_bp_plan = [
        inject_metadata(p, s) for p, s in zip(lst_bp_plan, lst_metadata)
    ]
    return pchain(*lst_bp_plan)