def insert_take_dark(msg): nonlocal need_dark qualified_dark_uid = _validate_dark(expire_time=glbl["dk_window"]) area_det = xpd_configuration["area_det"] if (not need_dark) and (not qualified_dark_uid): need_dark = True if need_dark and ( not qualified_dark_uid) and msg.command == "open_run" and ( "dark_frame" not in msg.kwargs): # We are about to start a new 'run' (e.g., a count or a scan). # Insert a dark frame run first. need_dark = False # Annoying detail: the detector was probably already staged. # Unstage it (if it wasn't staged, nothing will happen) and # then take_dark() and then re-stage it. return ( bpp.pchain( bps.unstage(area_det), take_dark(), bps.stage(area_det), bpp.single_gen(msg), open_shutter_stub(), ), None, ) elif msg.command == "open_run" and "dark_frame" not in msg.kwargs: return ( bpp.pchain(bpp.single_gen(msg), open_shutter_stub()), None, ) else: # do nothing if (not need_dark) return None, None
def test_monitors(hw): det = hw.det rand = hw.rand rand2 = hw.rand2 # no-op D = SupplementalData() original = list(count([det])) processed = list(D(count([det]))) assert len(processed) == len(original) # one monitor D.monitors.append(rand) original = list(count([det])) processed = list(D(count([det]))) assert len(processed) == 2 + len(original) # two monitors D.monitors.append(rand2) processed = list(D(count([det]))) assert len(processed) == 4 + len(original) # two monitors applied a plan with consecutive runs original = list(list(count([det])) + list(count([det]))) processed = list(D(pchain(count([det]), count([det])))) assert len(processed) == 8 + len(original)
def test_mutator(msg): nonlocal _mut_active if _mut_active: _mut_active = False return (pchain(echo_plan(num=2, command=cmd2), single_message_gen(msg)), bad_tail()) return None, None
def test_mutator(msg): nonlocal _mut_active if _mut_active: _mut_active = False return (pchain(echo_plan(num=pre_count, command=pre_cmd), single_message_gen(msg)), echo_plan(num=post_count, command=post_cmd)) return None, None
def test_get_events_filtering_field(db, RE, hw): RE.subscribe(db.insert) uid, = RE(count([hw.det], num=7)) h = db[uid] assert len(list(db.get_events(h, fields=['det']))) == 7 assert len(list(h.documents(fields=['det']))) == 7 + 3 uids = RE(pchain(count([hw.det1], num=7), count([hw.det2], num=3))) headers = db[uids] assert len(list(db.get_events(headers, fields=['det1']))) == 7 assert len(list(db.get_events(headers, fields=['det2']))) == 3
def fast_shutter_wrapper(plan): update_metadata() if USE_FAST_SHUTTER: plan = bpp.pchain( bps.abs_set(fast_shutter.output, FastShutter.OPEN_SHUTTER, settle_time=FastShutter.SETTLE_TIME), plan) plan = bpp.finalize_wrapper( plan, bps.abs_set(fast_shutter.output, FastShutter.CLOSE_SHUTTER, settle_time=FastShutter.SETTLE_TIME)) return (yield from plan)
def test_baseline(hw): # one baseline detector D = SupplementalData(baseline=[hw.det2]) original = list(count([hw.det])) processed = list(D(count([hw.det]))) # should add 2X (trigger, wait, create, read, save) assert len(processed) == 10 + len(original) # two baseline detectors D.baseline.append(hw.det3) processed = list(D(count([hw.det]))) # should add 2X (trigger, triger, wait, create, read, read, save) assert len(processed) == 14 + len(original) # two baseline detectors applied to a plan with two consecutive runs original = list(list(count([hw.det])) + list(count([hw.det]))) processed = list(D(pchain(count([hw.det]), count([hw.det])))) assert len(processed) == 28 + len(original)
def test_flyers(hw): # one flyer D = SupplementalData(flyers=[hw.flyer1]) original = list(count([hw.det])) processed = list(D(count([hw.det]))) # should add kickoff, wait, complete, wait, collect assert len(processed) == 5 + len(original) # two flyers D.flyers.append(hw.flyer2) processed = list(D(count([hw.det]))) # should add 2 * (kickoff, complete, collect) + 2 * (wait) assert len(processed) == 8 + len(original) # two flyers applied to a plan with two consecutive runs original = list(list(count([hw.det])) + list(count([hw.det]))) processed = list(D(pchain(count([hw.det]), count([hw.det])))) assert len(processed) == 16 + len(original)
def test_uid_list_multiple_headers(db, RE, hw): RE.subscribe(db.insert) uids = RE(pchain(count([hw.det]), count([hw.det]))) headers = db[uids] assert uids == tuple([h['start']['uid'] for h in headers])
pchain( bp.scan([hw.noisy_det], hw.motor, 0, 10, 10), bp.grid_scan([hw.ab_det], hw.motor, 0, 10, 10, hw.motor2, 0, 10, 10, True, per_step=one_nd_step), bp.spiral([hw.ab_det], hw.motor, hw.motor2, 0, 0, 10, 10, 1, 10, per_step=one_nd_step), bp.grid_scan([hw.direct_img], hw.motor, 0, 10, 10, hw.motor2, 0, 10, 10, True, per_step=one_nd_step), ))
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)
def test_get_events_multiple_headers(db, RE, hw): RE.subscribe(db.insert) headers = db[RE(pchain(count([hw.det]), count([hw.det])))] assert len(list(db.get_events(headers))) == 2
def _per_step(detectors, step, pos_cache, take_reading=my_take_reading): yield from bps.sleep(delay) sts = yield from bps.one_nd_step(detectors, step, pos_cache, take_reading=take_reading) return sts return _per_step # Mandy's samples per_step7 = gen_per_step(60 * 5) plan42 = pchain( configure_area_det(pe1c, 60 * 5, 0.2), bp.list_scan([pe1c], cs700, [300, 433, 465], per_step=per_step7), configure_area_det(pe1c, 60, 0.2), bp.grid_scan([pe1c], cs700, 465, 300, 34, per_step=my_per_step)) plan43 = pchain(configure_area_det(pe1c, 60 * 5, 0.2), bp.list_scan([pe1c], cs700, [300, 433], per_step=per_step7), bps.mv(cs700, 300)) plan44 = pchain( configure_area_det(pe1c, 60 * 5, 0.2), bp.list_scan([pe1c], cs700, [300], per_step=my_per_step), configure_area_det(pe1c, 60, 0.2), bp.grid_scan([pe1c], cs700, 300, 455, 32, per_step=my_per_step), configure_area_det(pe1c, 60 * 5, 0.2), bp.list_scan([pe1c], cs700, [455], per_step=per_step7), configure_area_det(pe1c, 60, 0.2), bp.grid_scan([pe1c], cs700, 455, 300, 32, per_step=my_per_step), )
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)
pchain( bp.scan([hw.noisy_det], hw.motor, 0, 10, 10), bp.grid_scan( [hw.ab_det], hw.motor, 0, 5, 5, hw.motor2, 0, 5, 5, True, per_step=one_nd_step, ), bp.grid_scan( [hw.ab_det], hw.motor, 0, 10, 10, hw.motor2, 0, 10, 10, True, per_step=one_nd_step, ), bp.spiral( [hw.ab_det], hw.motor, hw.motor2, 0, 0, 10, 10, 1, 10, per_step=one_nd_step, ), bp.grid_scan( [rand_img], hw.motor, 0, 10, 10, hw.motor2, 0, 10, 10, True, per_step=one_nd_step, ), )