id4_common.utils.experiment_utils#

Experiment session setup.

Public API#

Module Contents#

id4_common.utils.experiment_utils.logger#
id4_common.utils.experiment_utils.PERSIST_FILENAME = '.polar_experiment.yml'#
id4_common.utils.experiment_utils.RESET_SCAN_ID_NOOP = -1#
class id4_common.utils.experiment_utils.ExperimentClass(*, prompt: Callable[[str], str] = input, printer: Callable[Ellipsis, None] = print)#

Drives interactive experiment setup, server/path resolution, and optional Data Management (DM) integration.

The class accepts prompt and printer callables so it can be driven deterministically from tests; the module-level singleton experiment uses input() and print().

esaf#

Cached ESAF record (or "dev" for development runs).

Type:

dict | str | None

proposal#

Cached proposal record (or "dev").

Type:

dict | str | None

server#

"data management" or "dserv".

Type:

str | None

base_experiment_path#

Server root + run + experiment_name.

Type:

Path | None

windows_base_experiment_path#

Windows-mounted equivalent (only set when running on dserv).

Type:

Path | None

experiment_name#
Type:

str | None

sample#
Type:

str | None

file_base_name#
Type:

str | None

spec_file#
Type:

str | None

data_management#

Cached DM experiment record when server == "data management".

Type:

dict | None

start_daq#

If True, setup_dm_daq will start the DAQ. Off by default because the DAQ start changes file permissions and can prevent new files being written (TODO 2025-07-15).

Type:

bool

start_daq: bool = False#
esaf: dict | str | None = None#
proposal: dict | str | None = None#
server: str | None = None#
base_experiment_path: pathlib.Path | None = None#
windows_base_experiment_path: pathlib.Path | None = None#
experiment_name: str | None = None#
data_management: dict | None = None#
sample: str | None = None#
file_base_name: str | None = None#
spec_file: str | None = None#
dm_experiment = None#
property experiment_path: pathlib.Path#

base_experiment_path / sample.

Type:

Linux-side experiment folder

property windows_experiment_path: pathlib.Path | None#

Windows-mounted experiment folder, or None when DM owns the data.

esaf_input(esaf_id: int | str | None = None) None#

Resolve the ESAF; accepts "dev" to skip DM lookup.

proposal_input(proposal_id: int | str | None = None) None#

Resolve the proposal; accepts "dev" to skip DM lookup.

sample_input(sample: str | None = None) None#

Set the sample name (default "DefaultSample").

base_name_input(base_name: str | None = None) None#

Set the file base name.

server_input(server: str | None = None) None#

Pick the data server: "data management" or "dserv".

experiment_name_input(experiment_name: str | None = None) None#

Set (or prompt for) the DM experiment name.

scan_number_input(reset_scan_id: int | None = RESET_SCAN_ID_NOOP) None#

Set RE.md["scan_id"] to the last completed scan number.

Semantics: - reset_scan_id is a non-negative int → write that value.

Next scan = value + 1.

  • -1 (default sentinel) → leave RE.md["scan_id"] alone. Used by load_from_bluesky.

  • None → interactive prompt (default no).

  • any other int → log a warning and leave it alone.

dm_experiment_setup(experiment_name: str) bool#

Configure (or look up + reuse) the DM experiment.

Returns False if the user wants to pick a different name. On any DM error, demotes to server="dserv" and returns True so the caller continues without DM.

setup_dm_daq() None#

Optionally start the DM voyager DAQ for this experiment.

Disabled by default (start_daq=False) because starting the DAQ changes file permissions and prevents new files being written (TODO 2025-07-15). Enable per-class instance via experiment.start_daq = True.

Wrapped in try/except so a transient DM failure here does not abort the whole setup — we log a warning and demote to no-DM mode.

setup_path() None#

Make sure the experiment folder exists; chdir into it.

start_specwriter() None#

Open a new SPEC file for the current sample.

save_params_to_yaml() None#

Write a minimal snapshot of current state to the experiment dir.

Best-effort — any write error is logged and swallowed so failure here never aborts a setup.

load_params_from_yaml(path: str | pathlib.Path) dict | None#

Load a previously-saved snapshot. Returns the parsed dict or None.

resume(path: str | pathlib.Path | None = None) None#

Restore an experiment after a Bluesky restart.

With no path, restores singleton state from RE.md (the PersistentDict bluesky already loaded at startup). With an explicit path, loads the snapshot YAML at that location and populates both singleton state and RE.md from it.

Does NOT contact DM either way.

load_from_bluesky(scan_id: int = -1, reset_scan_id: int = RESET_SCAN_ID_NOOP, useRE: bool = False) None#

Restore experiment state from a previous Bluesky run document.

setup(esaf_id: int | str | None = None, proposal_id: int | str | None = None, base_name: str | None = None, sample: str | None = None, server: str | None = None, experiment_name: str | None = None, reset_scan_id: int | None = RESET_SCAN_ID_NOOP) None#

Run the full experiment setup.

DM availability is auto-detected. When DM is unreachable, the ESAF/proposal prompts are skipped, server is forced to "dserv", and metadata is stamped as "dev". To force bypass even when DM is up, pass server="dserv" or use esaf_id="dev"/proposal_id="dev".

change_sample(sample_name: str | None = None, base_name: str | None = None, reset_scan_id: int | None = RESET_SCAN_ID_NOOP) None#

Switch sample, refresh paths, and start a new SPEC file.

id4_common.utils.experiment_utils.experiment#
id4_common.utils.experiment_utils.experiment_setup(esaf_id: int | str | None = None, proposal_id: int | str | None = None, base_name: str | None = None, sample: str | None = None, server: str | None = None, experiment_name: str | None = None, reset_scan_id: int | None = RESET_SCAN_ID_NOOP) None#

Run the full experiment setup (delegates to experiment.setup).

id4_common.utils.experiment_utils.experiment_change_sample(sample_name: str | None = None, base_name: str | None = None, reset_scan_id: int | None = RESET_SCAN_ID_NOOP) None#

Switch sample (delegates to experiment.change_sample).

id4_common.utils.experiment_utils.experiment_load_from_scan(scan_id: int = -1, reset_scan_id: int = RESET_SCAN_ID_NOOP) None#

Restore experiment state from a previous Bluesky run.

id4_common.utils.experiment_utils.experiment_resume(path: str | pathlib.Path | None = None) None#

Restore experiment state after a Bluesky restart.

With no argument, restores from RE.md (the PersistentDict bluesky already loaded at startup). With an explicit path, loads the YAML snapshot at that location instead.