Launcher Architecture

Experiment Flow Diagram

digraph experiment_flow {
    rankdir=LR;
    node [shape=box, style=filled, fillcolor=lightgray];

    ParamFile [label="Parameter File (JSON)", shape=note, fillcolor=lightyellow];
    RigConfigFile [label="Rig Config (TOML)", shape=note, fillcolor=lightyellow];
    RuntimePrompt [label="Runtime Prompts", shape=note, fillcolor=lightyellow];
    Launcher [label="Launcher\n(flow orchestrator)", shape=box, fillcolor=lightblue, style="filled,bold"];
    PrePipeline [label="Pre-Acquisition Pipeline\n(modules)"];
    Acquisition [label="Acquisition Subprocess\n(Bonsai, MATLAB, Python, etc.)"];
    PostPipeline [label="Post-Acquisition Pipeline\n(modules)"];
    SessionFolder [label="Session Folder\n(all logs, metadata, data)", shape=folder, fillcolor=yellow];

    ParamFile -> Launcher;
    RigConfigFile -> Launcher;
    RuntimePrompt -> Launcher;
    Launcher -> PrePipeline;
    PrePipeline -> SessionFolder;
    Launcher -> Acquisition;
    Acquisition -> SessionFolder;
    Launcher -> PostPipeline;
    PostPipeline -> SessionFolder;
    Launcher -> SessionFolder; // <-- Launcher writes directly to session folder
}

Note

The launcher manages the entire experiment flow: merging parameters, prompting for missing info, running pre-acquisition modules, launching the acquisition subprocess, and running post-acquisition modules. All steps, including the launcher itself, write to the session folder.

System Architecture Diagram

digraph launcher_architecture {
    rankdir=LR;
    node [shape=box, style=filled, fillcolor=lightgray];

    # Core entities
    ParamFile [label="Parameter File (JSON)", shape=note, fillcolor=lightyellow];
    BaseLauncher [label="BaseLauncher\n(core orchestration)", fillcolor=lightblue, style="filled,bold"];
    InterfaceAdapter [label="Interface Adapter\n(Bonsai / MATLAB / Python / Custom)", fillcolor=white, style="filled"];
PreModules [label="Pre-Acquisition Modules\n(mouse weight prompts, etc.)"];
    PostModules [label="Post-Acquisition Modules\n(session_creator, notes, etc.)"];
    AcquisitionProc [label="Acquisition Process\n(external executable)"];

    # Utilities
    ParamUtils [label="param_utils.py\n(load + prompts)"];
    RigConfig [label="rig_config.py\n(rig placeholders)"];
    GitManager [label="git_manager.py\n(optional repo mgmt)"];
    Logging [label="logging utils\n(session + centralized)"];

    # Relationships
    ParamFile -> BaseLauncher;
    BaseLauncher -> ParamUtils;
    ParamUtils -> RigConfig;
    BaseLauncher -> PreModules;
    BaseLauncher -> InterfaceAdapter;
    InterfaceAdapter -> AcquisitionProc [label="subprocess spawn"];
    BaseLauncher -> PostModules;
    BaseLauncher -> GitManager;
    BaseLauncher -> Logging;
    PreModules -> AcquisitionProc [style=dashed, label="optional signals"];
    PostModules -> AcquisitionProc [style=dashed, label="optional logs"];

    # Pre and post remain independent
    PreModules [group=pre];
    PostModules [group=post];
}

Note

A single BaseLauncher orchestrates experiments. Lightweight interface adapters handle only subprocess creation; extend or override _create_process for custom environments.

Launcher Flow Overview

  1. Parameter File Input: - The user provides a parameter file (JSON) to the launcher.

  2. Rig Config Merge: - The launcher loads the rig config (TOML) and merges it with the parameter file (parameters override rig config).

  3. Runtime Prompts: - The launcher interactively prompts for any missing required values, which override both param file and rig config.

  4. Pre-Acquisition Pipeline: - The launcher runs each pre-acquisition module in order, passing the merged parameter file. Each module can read/write to the session folder.

  5. Acquisition Subprocess: - The launcher starts the main experiment process (Bonsai, MATLAB, Python, etc.) with the merged parameters. The subprocess writes data and logs to the session folder.

  6. Post-Acquisition Pipeline: - After acquisition, the launcher runs each post-acquisition module in order, again passing the merged parameter file. These modules can generate session files, enhance metadata, and write results to the session folder.

All steps (launcher, pre-acquisition modules, acquisition subprocess, post-acquisition modules) write logs, metadata, and data to the session folder.

Repository Folder Structure

  • src/openscope_experimental_launcher/: Main package source code

    • launchers/: Core BaseLauncher and optional custom subclasses

    • interfaces/: Lightweight interface adapters for spawning external processes

    • pre_acquisition/: Modular pre-acquisition pipeline modules (mouse weight prompts, etc.)

    • post_acquisition/: Modular post-acquisition pipeline modules (session creation, notes, enhancement, etc.)

    • utils/: Shared utilities (configuration, git, logging, etc.)

  • params/: Example and project-specific parameter files (JSON)

  • tests/: Unit and integration tests for all core logic and modules

  • docs/: Documentation sources (reStructuredText, Sphinx, and build scripts)

  • run_launcher.py: CLI entry point for running experiments with a parameter file

  • run_module.py: CLI entry point for running any pipeline module directly

  • setup.py, pyproject.toml: Packaging and build configuration

  • README.md, LICENSE, etc.: Project metadata and top-level documentation

Philosophy: Modular Pre- and Post-Acquisition

The OpenScope launcher is designed for maximum flexibility and reproducibility. All experiment-specific logic (e.g., mouse weight prompts, experiment notes, data enhancement) is handled by modular pipeline modules, not the launcher core. This ensures:

  • Generic launchers for each language/software (Bonsai, MATLAB, Python)

  • All pre- and post-acquisition steps are modular and easily extended

  • Parameter files define which modules run for each experiment

  • Symmetry: Pre- and post-acquisition are handled identically, via ordered module lists

How Modules Are Inserted

To add a pre- or post-acquisition step, simply add the module name to the appropriate list in your parameter file:

{
   "pre_acquisition_pipeline": ["mouse_weight_pre_prompt"],
   "post_acquisition_pipeline": ["mouse_weight_post_prompt", "experiment_notes_post_prompt"],
   ...
}

Each module is a Python file in src/openscope_experimental_launcher/pre_acquisition/ or post_acquisition/, and must accept a param_file argument and return 0 for success, 1 for failure.

For more, see the Pre-Acquisition Modules and Post-Acquisition Modules sections of Pre- and Post-Acquisition Modules, and the Contributing to OpenScope Experimental Launcher guide.