Save/Load a Deck
The rationale for saving deck layouts — audit trail, reproducibility, version control — is covered in Deck. This guide covers the procedural API.
Saving a Deck
Save to a JSON file
Call save() on any deck object. It writes an indented JSON file at the path you specify, accepting either a string or a pathlib.Path.
from unitelabs.liquid_handling.hamilton import MicrolabSTAR
hamilton = MicrolabSTAR(name="Microlab STAR")
await hamilton.initialize()
# ... add carriers and labware ...
hamilton.deck.save("deck_layouts/my_deck.json")
The resulting JSON file captures the full deck configuration: every carrier, its track position, and all nested labware with their identifiers and state. The file is human-readable and safe to commit to version control.
What the JSON looks like
A saved deck file has this structure:
{
"description": "STARlet deck for Plate Painter Pro",
"carriers": [
{
"id": "tip_carrier",
"model": "TIP_CAR_480_A00",
"track": 15,
"labware": {
"0": {
"model": "HamiltonTipRack_300",
"id": "tip_rack",
"filled_with": "HamiltonTip_300_Filter"
}
}
},
{
"id": "reservoir_carrier",
"model": "PLT_CAR_L5AC_A00",
"track": 11,
"labware": {
"0": { "model": "StandardTrough", "id": "reservoir_ice", "volume_ul": 200000 },
"1": { "model": "StandardTrough", "id": "reservoir_orange", "volume_ul": 200000 }
}
},
{
"id": "plate_carrier",
"model": "PLT_CAR_L5AC_A00",
"track": 7,
"labware": {
"0": { "model": "Standard96Plate", "id": "wellplate" }
}
}
]
}
Export to a Python script
If you prefer a deck definition that is readable as code rather than a data file, you can represent the same layout as a Python script that builds it programmatically. This is also the format shown in Building a Deck.
from unitelabs.liquid_handling.hamilton import HamiltonDeck
from unitelabs.labware.hamilton import (
TIP_CAR_480_A00,
PLT_CAR_L5AC_A00,
HamiltonTipRack_300,
HamiltonTip_300_Filter,
StandardTrough,
Standard96Plate,
)
deck = HamiltonDeck()
tip_carrier = TIP_CAR_480_A00(identifier="tip_carrier")
tip_carrier[0] = HamiltonTipRack_300(identifier="tip_rack", filled_with=HamiltonTip_300_Filter)
deck.add(tip_carrier, track=15)
reservoir_carrier = PLT_CAR_L5AC_A00(identifier="reservoir_carrier")
reservoir_carrier[0] = StandardTrough(identifier="reservoir_ice", volume_ul=200_000)
reservoir_carrier[1] = StandardTrough(identifier="reservoir_orange", volume_ul=200_000)
deck.add(reservoir_carrier, track=11)
plate_carrier = PLT_CAR_L5AC_A00(identifier="plate_carrier")
plate_carrier[0] = Standard96Plate(identifier="wellplate")
deck.add(plate_carrier, track=7)
The Python script form is useful when you want deck changes to produce meaningful diffs in code review. The JSON form is more convenient for loading at runtime. Both work well together: use the Python script as the authoritative source and regenerate the JSON from it when needed.
Loading a Deck
Load into a new deck object
Use HamiltonDeck.load() to create a fully configured deck object from a saved JSON file. This is useful when you want to inspect or manipulate the deck before attaching it to a liquid handler.
from unitelabs.liquid_handling.hamilton.modules import HamiltonDeck
deck = HamiltonDeck.load("deck_layouts/my_deck.json")
print(deck.summary())
Load in-place into a liquid handler
Use lh.deck.load() to load a deck configuration directly into an already-configured liquid handler. This is the most common pattern in workflow scripts: configure the instrument, load the saved deck layout, then initialize.
from unitelabs.liquid_handling.hamilton import MicrolabSTARMock
hamilton = MicrolabSTARMock(name="Microlab STAR")
await hamilton.configure()
hamilton.deck.load("deck_layouts/my_deck.json")
await hamilton.initialize()
print(hamilton.deck.summary())
lh.deck.load() after configure() but before initialize(). This ensures the instrument initializes with the correct layout already applied.Reconstructing from a dictionary
If you have a deck serialized as a Python dictionary (for example, fetched from a database or returned by an API) use HamiltonDeck.from_json():
from unitelabs.liquid_handling.hamilton.modules import HamiltonDeck
# deck_dict could come from a database, an API response, etc.
deck = HamiltonDeck.from_json(deck_dict)
To get the dictionary from a live deck, use deck.to_json():
deck_dict = hamilton.deck.to_json()
Bundling Deck Configs with Workflow Code
For production workflows, keep your deck JSON files inside your workflow package rather than relying on filesystem paths that may differ between machines. A simple helper resolves the path relative to the package itself.
Project layout:
my_workflow/
├── __init__.py
├── library/
│ ├── __init__.py ← path helper lives here
│ └── standard_deck.json ← deck config bundled with the code
├── phase_01_initialization.py
└── phase_02_run.py
my_workflow/library/__init__.py:
from pathlib import Path
_LIBRARY_DIR = Path(__file__).parent
def get_library_path(filename: str) -> str:
path = _LIBRARY_DIR / filename
if not path.exists():
raise FileNotFoundError(f"Library file not found: {path}")
return str(path)
my_workflow/phase_01_initialization.py:
from unitelabs.liquid_handling.hamilton import MicrolabSTARMock
from my_workflow.library import get_library_path
async def initialize_workcell() -> MicrolabSTARMock:
hamilton = MicrolabSTARMock(name="Microlab STAR")
await hamilton.configure()
deck_config_path = get_library_path("standard_deck.json")
hamilton.deck.load(deck_config_path)
await hamilton.initialize()
return hamilton
Quick Reference
| Method | What it does |
|---|---|
deck.save("path.json") | Write the current deck to a JSON file |
deck.to_json() | Return the deck as a Python dictionary |
HamiltonDeck.load("path.json") | Create a new deck object from a JSON file |
HamiltonDeck.from_json(dict) | Create a new deck object from a dictionary |
lh.deck.load("path.json") | Load a deck configuration in-place into a liquid handler |
Next Steps
- Building a Deck: define a deck layout from scratch
- Basic Pipetting: attach a deck to a liquid handler and run operations