{ "cells": [ { "cell_type": "markdown", "id": "title", "metadata": {}, "source": [ "# Scan specifications and planning\n", "\n", "A `ScanSpec` contains geometry, captures, and autofocus policy. `celigo.plan(spec)` compiles it offline; `await celigo.execute(plan)` performs the inspected operations." ] }, { "cell_type": "code", "execution_count": null, "id": "imports", "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "from pylabrobot.resources.corning.plates import cor_96_wellplate_360uL_Fb\n", "from pylabrobot.revvity import (\n", " Capture,\n", " Celigo,\n", " CeligoConfig,\n", " ScanEstimateModel,\n", " ScanRegion,\n", " ScanSpec,\n", ")" ] }, { "cell_type": "markdown", "id": "configure-note", "metadata": {}, "source": [ "## Configure without connecting\n", "\n", "Planning reads the installed calibration but does not connect to or move the instrument." ] }, { "cell_type": "code", "execution_count": null, "id": "configure", "metadata": {}, "outputs": [], "source": [ "config_root = Path(\"/path/to/Celigo/ConfigFiles\")\n", "config = CeligoConfig.from_install(str(config_root))\n", "celigo = Celigo(config=config)" ] }, { "cell_type": "markdown", "id": "coverage-note", "metadata": {}, "source": [ "## Cover physical bounds\n", "\n", "`full_coverage()` chooses the frame grid and groups it into coarse-stage blocks that fit the calibrated galvo reach." ] }, { "cell_type": "code", "execution_count": null, "id": "coverage", "metadata": {}, "outputs": [], "source": [ "region = ScanRegion.from_bounds_mm(left=5, top=5, right=122, bottom=81)\n", "coverage_spec = ScanSpec.full_coverage(\n", " region,\n", " channel=\"brightfield\",\n", " exposure_ms=1.0,\n", ")\n", "coverage_plan = celigo.plan(coverage_spec)\n", "print(coverage_plan)" ] }, { "cell_type": "code", "execution_count": null, "id": "inspect", "metadata": {}, "outputs": [], "source": [ "first_block = coverage_plan.blocks[0]\n", "first_frame = coverage_plan.frames[0]\n", "{\n", " \"block_stage_mm\": (first_block.stage_x_mm, first_block.stage_y_mm),\n", " \"block_shape\": first_block.block_shape,\n", " \"frame_sample_mm\": (\n", " first_frame.position.sample_x_mm,\n", " first_frame.position.sample_y_mm,\n", " ),\n", " \"channel\": first_frame.capture.channel,\n", "}" ] }, { "cell_type": "markdown", "id": "points-note", "metadata": {}, "source": [ "## Scan physical points\n", "\n", "Points need no enclosing bounds or labels. `block_shape=(columns, rows)` may be any positive shape within the calibrated per-stage reach." ] }, { "cell_type": "code", "execution_count": null, "id": "points", "metadata": {}, "outputs": [], "source": [ "point_spec = ScanSpec.points(\n", " [(25.0, 20.0), (63.5, 43.0), (102.0, 66.0)],\n", " block_shape=(2, 3),\n", " channel=\"brightfield\",\n", ")\n", "point_plan = celigo.plan(point_spec)\n", "[(block.center_x_mm, block.center_y_mm) for block in point_plan.blocks]" ] }, { "cell_type": "markdown", "id": "random-note", "metadata": {}, "source": [ "## Sample reproducible random blocks\n", "\n", "The seed fixes the chosen physical blocks. Non-overlapping blocks are the default." ] }, { "cell_type": "code", "execution_count": null, "id": "random", "metadata": {}, "outputs": [], "source": [ "random_spec = ScanSpec.random(\n", " region,\n", " count=10,\n", " block_shape=(4, 4),\n", " seed=42,\n", " channel=\"brightfield\",\n", ")\n", "random_plan = celigo.plan(random_spec)\n", "random_plan.stage_positions_mm" ] }, { "cell_type": "markdown", "id": "wells-note", "metadata": {}, "source": [ "## Scan named wells\n", "\n", "`ScanSpec.wells()` converts names once to labeled physical centers. The resulting specification does not retain the plate." ] }, { "cell_type": "code", "execution_count": null, "id": "wells", "metadata": {}, "outputs": [], "source": [ "plate = cor_96_wellplate_360uL_Fb(name=\"imaging_plate\")\n", "well_spec = ScanSpec.wells(\n", " plate,\n", " [\"A1\", \"B2\", \"C3\"],\n", " block_shape=(2, 3),\n", " channel=\"brightfield\",\n", " exposure_ms=1.0,\n", " gain=1.0,\n", " autofocus=\"image\",\n", ")\n", "well_plan = celigo.plan(well_spec)\n", "[(block.label, block.block_shape) for block in well_plan.blocks]" ] }, { "cell_type": "markdown", "id": "one-call-note", "metadata": {}, "source": [ "For the common single-capture case, skip the explicit specification:" ] }, { "cell_type": "code", "execution_count": null, "id": "one-call", "metadata": {}, "outputs": [], "source": [ "async def scan_a1_and_b2():\n", " return await celigo.scan_wells(\n", " plate,\n", " [\"A1\", \"B2\"],\n", " channel=\"brightfield\",\n", " block_shape=(1, 1),\n", " exposure_ms=1.0,\n", " gain=1.0,\n", " )" ] }, { "cell_type": "markdown", "id": "captures-note", "metadata": {}, "source": [ "## Plan multiple captures\n", "\n", "Use `Capture` when every position needs more than one channel. Capture settings become part of the plan, and the coarse stage moves once per block." ] }, { "cell_type": "code", "execution_count": null, "id": "captures", "metadata": {}, "outputs": [], "source": [ "multichannel_spec = ScanSpec.wells(\n", " plate,\n", " [\"A1\", \"B2\"],\n", " block_shape=(2, 3),\n", " captures=[\n", " Capture(channel=\"brightfield\", exposure_ms=1.0, gain=1.0),\n", " Capture(channel=\"green\", exposure_ms=20.0, gain=2.0),\n", " ],\n", " autofocus=\"image\",\n", ")\n", "multichannel_plan = celigo.plan(multichannel_spec)\n", "print(multichannel_plan)" ] }, { "cell_type": "markdown", "id": "estimates-note", "metadata": {}, "source": [ "## Use measured throughput\n", "\n", "Exposure times come from the captures. Supply only instrument overhead and storage assumptions." ] }, { "cell_type": "code", "execution_count": null, "id": "estimates", "metadata": {}, "outputs": [], "source": [ "estimate_model = ScanEstimateModel(\n", " seconds_per_frame=0.35,\n", " seconds_per_stage_position=2.0,\n", " seconds_per_autofocus=5.0,\n", " bytes_per_pixel=2,\n", ")\n", "estimated_plan = celigo.plan(multichannel_spec, estimate_model=estimate_model)\n", "estimated_plan.estimated_duration, estimated_plan.estimated_storage_bytes" ] }, { "cell_type": "markdown", "id": "execute-note", "metadata": {}, "source": [ "## Execute an inspected plan\n", "\n", "The guarded cell below is the only hardware operation in this notebook. `execute()` accepts no geometry or capture overrides." ] }, { "cell_type": "code", "execution_count": null, "id": "execute", "metadata": {}, "outputs": [], "source": [ "RUN_HARDWARE = False\n", "\n", "if RUN_HARDWARE:\n", " await celigo.setup()\n", " try:\n", " scan_result = await celigo.execute(multichannel_plan)\n", " finally:\n", " await celigo.stop()\n", "\n", " first_result = scan_result.frames[0]\n", " print(\n", " first_result.planned.block.label,\n", " first_result.planned.capture.channel,\n", " first_result.actual_stage_mm,\n", " first_result.actual_z_mm,\n", " )" ] } ], "metadata": { "kernelspec": { "display_name": ".venv (3.11.15)", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.11.15" } }, "nbformat": 4, "nbformat_minor": 5 }