API Reference¶
Deck¶
- class biohit_pipettor_plus.deck_structure.Deck(range_x: tuple[int, int], range_y: tuple[int, int], deck_id: str, range_z: float = 500)[source]¶
Bases:
SerializableRepresents the Deck of a Pipettor. The Deck can contain multiple slots, each of which can hold multiple Labware objects stacked vertically.
- range_z¶
Maximum vertical range of the deck (mm). This is the total Z-axis travel from top (pipettor home) to bottom (deck surface).
- Type:
float, optional
- add_labware(labware: Labware, slot_id: str, min_z: float)[source]¶
Add a Labware to a specific Slot at a specific Z position.
- Parameters:
- Raises:
TypeError – If the object is not a Labware instance.
ValueError – If the slot does not exist, labware ID already exists, or labware does not fit in the slot.
- add_slots(slots: list[Slot])[source]¶
Add a Slot to the Deck after validating range and overlap.
- Parameters:
slots (list of slots) – The slot to add to the deck.
- Raises:
ValueError – If slot ID already exists, is out of deck range, or overlaps an existing slot.
- get_slot_for_labware(labware_id: str) str | None[source]¶
Find the slot ID containing the given labware_id. :param labware_id: The ID of the labware to locate. :type labware_id: str
- Returns:
The slot ID if found, else None.
- Return type:
Optional[str]
- remove_labware(labware_id: str) Labware[source]¶
Remove a Labware from its Slot and the Deck. Only the topmost labware in a slot’s stack can be removed. Returns the Labware object for reuse or deletion.
Slot¶
- class biohit_pipettor_plus.deck_structure.Slot(range_x: tuple[float, float], range_y: tuple[float, float], range_z: float, slot_id: str)[source]¶
Bases:
SerializableRepresents a single slot on a Deck. A slot can hold multiple Labware objects stacked vertically with specified Z-ranges
- labware_stack¶
Dictionary mapping Labware IDs to a list containing the Labware object and its Z-range (min_z, max_z) within the slot.
Labware¶
- class biohit_pipettor_plus.deck_structure.Labware(size_x: float, size_y: float, size_z: float, offset: tuple[float, float] = (0.0, 0.0), labware_id: str | None = None, position: tuple[float, float] | None = None, can_be_stacked_upon: bool = False)[source]¶
Bases:
SerializableBase class for all labware objects with automatic subclass registry.
Initialize a Labware instance.
- Parameters:
size_x (float) – Width of the labware in millimeters.
size_y (float) – Depth of the labware in millimeters.
size_z (float) – Height of the labware in millimeters.
labware_id (str, optional) – Unique ID for the labware. If None, a UUID will be generated.
position (tuple[float, float], optional) – (x, y) position coordinates of the labware in millimeters. If None, position is not set.
- each_tip_needs_separate_item() bool[source]¶
For multichannel operation, does each tip need to access a separate item?
- Returns:
True: Each tip needs its own item (e.g., Plate - small wells) False: All tips can share one item (e.g., ReservoirHolder - large reservoirs)
- Return type:
- to_dict() dict[source]¶
Serialize the Labware instance to a dictionary.
- Returns:
dictionary representation of the labware.
- Return type:
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str][source]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], row: int, consecutive_rows: int = 1) None[source]¶
Validate column and row, raising ValueError if invalid.
Convenience method for when you want to raise an error immediately.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str][source]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')[source]¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
Plate¶
- class biohit_pipettor_plus.deck_structure.Plate(size_x: float, size_y: float, size_z: float, wells_x: int, wells_y: int, well: Well, add_height: float = -3, remove_height: float = -10, offset: tuple[float, float] = (0, 0), x_spacing: float | None = None, y_spacing: float | None = None, labware_id: str | None = None, position: tuple[float, float] | None = None, can_be_stacked_upon: bool = False)[source]¶
Bases:
LabwareInitialize a Plate instance.
- Parameters:
size_x (float) – Width of the plate.
size_y (float) – Depth of the plate.
size_z (float) – Height of the plate.
wells_x (int) – Number of wells in X direction.
wells_y (int) – Number of wells in Y direction.
add_height (float) – Height above the well bottom used when adding liquid (in mm).
remove_height (float) – Height above the well bottom used when removing liquid (in mm).
well (Well) – Template well to use for all wells in the plate.
offset (tuple[float, float], optional) – Offset of the plate.
x_spacing (float, optional) – Distance along x-axis between hooks in millimeters.
y_spacing (float, optional) – Distance along y-axis between hooks in millimeters.
labware_id (str, optional) – Unique ID for the plate.
position (tuple[float, float], optional) – (x, y) position coordinates of the plate in millimeters.
- each_tip_needs_separate_item() bool¶
For multichannel operation, does each tip need to access a separate item?
- Returns:
True: Each tip needs its own item (e.g., Plate - small wells) False: All tips can share one item (e.g., ReservoirHolder - large reservoirs)
- Return type:
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], row: int, consecutive_rows: int = 1) None¶
Validate column and row, raising ValueError if invalid.
Convenience method for when you want to raise an error immediately.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
Well¶
- class biohit_pipettor_plus.deck_structure.Well(size_x: float, size_y: float, size_z: float, offset: tuple[float, float] = (0, 0), position: tuple[float, float] | None = None, can_be_stacked_upon: bool = False, labware_id: str | None = None, row: int | None = None, column: int | None = None, content: dict | None = None, capacity: float = 1000, shape: Literal['rectangular', 'circular', 'conical', 'u_bottom'] | None = None)[source]¶
Bases:
LabwareRepresents a single Well, extending Labware with additional parameters for liquid handling.
Initialize a Well instance.
- Parameters:
size_x (float) – Width of the well in millimeters.
size_y (float) – Depth of the well in millimeters.
size_z (float) – Height of the well in millimeters.
position (tuple[float, float], optional) – (x, y) position coordinates of the well in millimeters.
labware_id (str, optional) – Unique identifier for this well. If None, a UUID will be generated.
row (int, optional.) – row inside of plate
column (int, optional) – column inside of plate
content (dict, optional) – Dictionary mapping content types to volumes (µL). Example: {“PBS”: 150, “water”: 100}
capacity (float, optional) – Maximum volume the well can hold (µL). Default is Default_well_capacity
- add_content(content_type: str, volume: float) None[source]¶
Add content to the well with intelligent mixing logic.
When adding content to a well: - Same content type: volumes are combined - Different content type: tracked separately (but physically mixed)
Note: Once liquids are mixed in a well, they cannot be separated. Removal is always proportional from all content types.
- Parameters:
- Raises:
ValueError – If adding volume would exceed capacity or volume is negative
- each_tip_needs_separate_item() bool¶
For multichannel operation, does each tip need to access a separate item?
- Returns:
True: Each tip needs its own item (e.g., Plate - small wells) False: All tips can share one item (e.g., ReservoirHolder - large reservoirs)
- Return type:
- get_available_volume() float[source]¶
Get the remaining capacity available in the well.
- Returns:
Available volume in µL
- Return type:
- get_content_info() dict[source]¶
Get current content information.
- Returns:
Dictionary with detailed content information
- Return type:
- get_content_summary() str[source]¶
Get a human-readable summary of well content.
- Returns:
Summary string like “PBS: 150.0µL, water: 100.0µL” or “empty”
- Return type:
- get_total_volume() float[source]¶
Get total volume of all content in the well.
- Returns:
Total volume in µL
- Return type:
- remove_content(volume: float, return_dict: bool = False) dict[str, float] | None[source]¶
Remove content from the well proportionally.
When content is removed from a well, it’s removed proportionally from all content types since they are mixed together.
- Parameters:
- Returns:
If return_dict is True, returns dictionary mapping content types to removed volumes. Otherwise, returns None.
- Return type:
- Raises:
ValueError – If trying to remove more volume than available or volume is negative
- to_dict() dict[source]¶
Serialize the Well to a dictionary for JSON export.
- Returns:
dictionary containing all well attributes.
- Return type:
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], row: int, consecutive_rows: int = 1) None¶
Validate column and row, raising ValueError if invalid.
Convenience method for when you want to raise an error immediately.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
PipetteHolder¶
- class biohit_pipettor_plus.deck_structure.PipetteHolder(size_x: float, size_y: float, size_z: float, holders_across_x: int, holders_across_y: int, individual_holder: IndividualPipetteHolder, add_height: float = 0, remove_height: float = 0, offset: tuple[float, float] = (0, 0), x_spacing: float | None = None, y_spacing: float | None = None, labware_id: str | None = None, position: tuple[float, float] | None = None, can_be_stacked_upon: bool = False)[source]¶
Bases:
LabwareInitialize a PipetteHolder instance.
- Parameters:
size_x (float) – Width of the pipette holder in millimeters.
size_y (float) – Depth of the pipette holder in millimeters.
size_z (float) – Height of the pipette holder in millimeters.
holders_across_x (int) – Number of individual holder positions across X-axis.
holders_across_y (int) – Number of individual holder positions across Y-axis.
individual_holder (IndividualPipetteHolder) – Template for individual holder positions. If provided, copies will be created for each position in the grid.
labware_id (str, optional) – Unique ID for the pipette holder.
add_height (float) – Height above the well bottom used when adding liquid (in mm).
remove_height (float) – Height above the well bottom used when removing liquid (in mm).
x_spacing (float, optional) – Distance along x-axis between hooks in millimeters.
y_spacing (float, optional) – Distance along y-axis between hooks in millimeters.
position (tuple[float, float], optional) – (x, y) position coordinates of the pipette holder in millimeters. If None, position is not set.
- check_col_start_row_multi(col: int, start_row: int) str[source]¶
Check the occupancy status of 8 consecutive positions starting from (col, start_row).
- Parameters:
- Returns:
Status of the 8 consecutive positions: - “FULLY_OCCUPIED”: All 8 positions exist and are occupied - “FULLY_AVAILABLE”: All 8 positions exist and are empty - “MIXED”: All 8 positions exist but have mixed occupancy - “INVALID”: One or more positions don’t exist (out of bounds)
- Return type:
- each_tip_needs_separate_item() bool¶
For multichannel operation, does each tip need to access a separate item?
- Returns:
True: Each tip needs its own item (e.g., Plate - small wells) False: All tips can share one item (e.g., ReservoirHolder - large reservoirs)
- Return type:
- get_all_children() list[IndividualPipetteHolder][source]¶
- get_available_holder_multi() list[tuple[int, int]][source]¶
Get all columns with starting rows where 8 consecutive available positions exist. No holder is reused - blocks are non-overlapping.
- get_available_holders() list[IndividualPipetteHolder][source]¶
Get all available (unoccupied) holder positions.
- Returns:
List of available holders.
- Return type:
- get_holder_at(column: int, row: int) IndividualPipetteHolder | None[source]¶
Get the individual holder at a specific position.
- Parameters:
- Returns:
The holder at the position, or None if not found
- Return type:
Optional[IndividualPipetteHolder]
- get_individual_holders() dict[tuple[int, int], IndividualPipetteHolder][source]¶
Get all individual holder positions.
- get_occupied_holder_multi() list[tuple[int, int]][source]¶
Get all columns with starting rows where 8 consecutive occupied positions exist. No holder is reused - blocks are non-overlapping.
- get_occupied_holders() list[IndividualPipetteHolder][source]¶
Get all occupied holder positions.
- Returns:
List of occupied holders.
- Return type:
- place_consecutive_pipettes_multi(columns: list[int], row: int = 0) None[source]¶
Place pipettes in consecutive positions within specified columns for multichannel pipettor.
- Parameters:
- Raises:
ValueError – If any column index is out of range, row out of range, or if any position in the specified columns is already occupied.
- place_pipette_at(column: int, row: int) None[source]¶
Place a pipette at a specific position.
- Parameters:
- Raises:
ValueError – If position is out of range, no holder exists, or position is occupied.
- remove_consecutive_pipettes_multi(columns: list[int], row: int = 0) None[source]¶
Remove pipettes from consecutive positions within specified columns for multichannel pipettor.
- Parameters:
- Raises:
ValueError – If any column index is out of range, row out of range, or if any position in the specified columns is already empty.
- remove_pipette_at(column: int, row: int) None[source]¶
Remove a pipette from a specific position.
- Parameters:
- Raises:
ValueError – If position is out of range, no holder exists, or position is empty.
- to_dict() dict[source]¶
Serialize the PipetteHolder instance to a dictionary.
- Returns:
Dictionary representation of the pipette holder.
- Return type:
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], row: int, consecutive_rows: int = 1) None¶
Validate column and row, raising ValueError if invalid.
Convenience method for when you want to raise an error immediately.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
IndividualPipetteHolder¶
- class biohit_pipettor_plus.deck_structure.IndividualPipetteHolder(size_x: float, size_y: float, size_z: float, offset: tuple[float, float] = (0, 0), is_occupied: bool = True, row: int | None = None, column: int | None = None, labware_id: str | None = None, can_be_stacked_upon: bool = False, position: tuple[float, float] | None = None)[source]¶
Bases:
LabwareRepresents an individual pipette holder position within a PipetteHolder. Tracks occupancy status and pipette type.
Initialize an IndividualPipetteHolder instance.
- Parameters:
size_x (float) – Width of the individual holder position in millimeters.
size_y (float) – Depth of the individual holder position in millimeters.
size_z (float) – Height of the individual holder position in millimeters.
is_occupied (bool, optional) – Whether a pipette is currently stored here. Default is False.
labware_id (str, optional) – Unique identifier for this holder position. If None, a UUID will be generated.
position (tuple[float, float], optional) – (x, y) absolute position coordinates in millimeters. If None, position is not set.
- each_tip_needs_separate_item() bool¶
For multichannel operation, does each tip need to access a separate item?
- Returns:
True: Each tip needs its own item (e.g., Plate - small wells) False: All tips can share one item (e.g., ReservoirHolder - large reservoirs)
- Return type:
- get_content_info() dict[source]¶
Standardized content info for Pipette Holders. Matches the return signature of Plate/Reservoir wells.
- is_available() bool[source]¶
Check if this holder position is available for placing a pipette. Returns bool.True if available (not occupied), False otherwise.
- place_pipette() None[source]¶
Mark this holder position as occupied (pipette placed). Raises ValueError If the holder position is already occupied.
- remove_pipette() None[source]¶
Mark this holder position as available (pipette removed).
- Raises:
ValueError – If the holder position is already empty.
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], row: int, consecutive_rows: int = 1) None¶
Validate column and row, raising ValueError if invalid.
Convenience method for when you want to raise an error immediately.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
ReservoirHolder¶
- class biohit_pipettor_plus.deck_structure.ReservoirHolder(size_x: float, size_y: float, size_z: float, hooks_across_x: int, hooks_across_y: int, reservoir_template: Reservoir | None = None, remove_height: float = -45, add_height: float = 0, offset: tuple[float, float] = (0, 0), labware_id: str | None = None, position: tuple[float, float] | None = None, can_be_stacked_upon: bool = False, x_spacing: float | None = None, y_spacing: float | None = None, each_tip_needs_separate_item=False)[source]¶
Bases:
LabwareInitialize a ReservoirHolder instance that can hold multiple reservoirs.
- Parameters:
size_x (float) – Width of the ReservoirHolder in millimeters.
size_y (float) – Depth of the ReservoirHolder in millimeters.
size_z (float) – Height of the ReservoirHolder in millimeters.
hooks_across_x (int) – Number of hooks along X-axis.
hooks_across_y (int) – Number of hooks along Y-axis (rows of hooks).
add_height (float) – relative height at which liquid is dispensed
remove_height (float) – relative height at which liquid is aspirated
reservoir_template (reservoir) – example or individual reservoir that will be placed across all hooks.
labware_id (str, optional) – Unique ID for the holder.
position (tuple[float, float], optional) – (x, y) position coordinates of the ReservoirHolder in millimeters. If None, position is not set.
x_spacing (float, optional) – Distance along x-axis between hooks in millimeters.
y_spacing (float, optional) – Distance along y-axis between hooks in millimeters.
each_tip_needs_separate_item (bool, optional) – If True, each pipette tip needs its own reservoir. If False, all tips can access the same reservoir (default: False).
- add_content(hook_id: int, content: str, volume: float) None[source]¶
Add content to a reservoir at a specific hook.
- Parameters:
- Raises:
ValueError – If no reservoir at hook or volume exceeds capacity
- each_tip_needs_separate_item() bool[source]¶
For multichannel operation, does each tip need to access a separate item?
- Returns:
True: Each tip needs its own item (e.g., Plate - small wells) False: All tips can share one item (e.g., ReservoirHolder - large reservoirs)
- Return type:
- get_hook_to_reservoir_map() dict[int, Reservoir | None][source]¶
Return the complete hook to reservoir mapping.
- get_reservoirs_by_content_type(content_type: str) list[Reservoir][source]¶
Get all unique reservoirs that contain a specific content type.
- get_waste_reservoirs() list[Reservoir][source]¶
Get all unique reservoirs that contain ‘waste’ in any content type.
- hook_id_to_position(hook_id: int) tuple[int, int][source]¶
Convert hook_id to (col, row) position.
- Parameters:
hook_id (int) – Hook ID (1-indexed, 1 to total_hooks)
- Returns:
(col, row) where col is 0 to hooks_across_x-1, row is 0 to hooks_across_y-1
- Return type:
Example
For hooks_across_x=3, hooks_across_y=2:
hook_id: 1 2 3 4 5 6 layout: [1 2 3] <- row 0 [4 5 6] <- row 1
- place_reservoir(hook_ids: list[int], reservoir: Reservoir) None[source]¶
Place a single reservoir on specific hooks.
- Parameters:
- Raises:
ValueError – If hook_ids are invalid, don’t form a rectangle, already occupied, or reservoir dimensions incompatible.
- place_reservoirs(reservoir_template: Reservoir) None[source]¶
allocate duplicate reservoir to all available hooks, unless specific hook id specified
If hook_ids is specified, the reservoir will be placed there, given that position is empty Otherwise, calculates required hooks based on dimensions and allocates automatically.
- Raises:
ValueError – If a specified hook_id is occupied, insufficient space, or reservoir parameters are invalid.
- remove_content(hook_id: int, volume: float, return_dict: bool = False) dict[str, float] | None[source]¶
Remove content from a reservoir at a specific hook.
- Parameters:
- Returns:
If return_dict is True, returns dictionary mapping content types to removed volumes. Otherwise, returns None.
- Return type:
- Raises:
ValueError – If no reservoir at hook or insufficient volume
- remove_reservoir(hook_id: int) Reservoir[source]¶
Remove a reservoir from the holder.
- Parameters:
hook_id (int) – Any hook ID occupied by the reservoir to remove
- Returns:
The removed reservoir
- Return type:
- Raises:
ValueError – If no reservoir at the specified hook
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], row: int, consecutive_rows: int = 1) None¶
Validate column and row, raising ValueError if invalid.
Convenience method for when you want to raise an error immediately.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
Reservoir¶
- class biohit_pipettor_plus.deck_structure.Reservoir(size_x: float, size_y: float, size_z: float, offset: tuple[float, float] = (0, 0), labware_id: str | None = None, position: tuple[float, float] | None = None, can_be_stacked_upon: bool = False, capacity: float = 30000, content: dict | None = None, hook_ids: list[int] | None = None, row: int | None = None, column: int | None = None, shape: Literal['rectangular', 'circular', 'conical', 'u_bottom'] | None = None)[source]¶
Bases:
LabwareInitialize a Reservoir instance. These are containers that store the medium to be filled in and removed from well.
- Parameters:
size_x (float) – Width of the reservoir in millimeters.
size_y (float) – Depth of the reservoir in millimeters.
size_z (float) – Height of the reservoir in millimeters.
capacity (float, optional) – Maximum amount of liquid that reservoir can hold. Default is Default_Reservoir_Capacity.
content (dict, optional) – Dictionary mapping content types to volumes (µL). Example: {“PBS”: 25000, “water”: 5000}
hook_ids (list[int], optional) – List of hook locations on ReservoirHolder where this reservoir is going to be placed.
labware_id (str, optional) – Unique ID for the reservoir.
position (tuple[float, float], optional) – (x, y) position coordinates of the reservoir in millimeters. If None, position is not set.
- add_content(content_type: str, volume: float) None[source]¶
Add content to the reservoir with intelligent mixing logic.
When adding content to a reservoir: - Same content type: volumes are combined - Different content type: tracked separately (but physically mixed)
Note: Once liquids are mixed in a reservoir, they cannot be separated. Removal is always proportional from all content types.
- Parameters:
- Raises:
ValueError – If adding volume would exceed capacity or volume is negative
- each_tip_needs_separate_item() bool¶
For multichannel operation, does each tip need to access a separate item?
- Returns:
True: Each tip needs its own item (e.g., Plate - small wells) False: All tips can share one item (e.g., ReservoirHolder - large reservoirs)
- Return type:
- get_available_volume() float[source]¶
Get the remaining capacity available in the reservoir.
- Returns:
Available volume in µL
- Return type:
- get_content_info() dict[source]¶
Get current content information.
- Returns:
Dictionary with detailed content information
- Return type:
- get_content_summary() str[source]¶
Get a human-readable summary of reservoir content.
- Returns:
Summary string like “PBS: 25000µL, water: 5000µL” or “empty”
- Return type:
- get_total_volume() float[source]¶
Get total volume of all content in the reservoir.
- Returns:
Total volume in µL
- Return type:
- has_content_type(content_type: str) bool[source]¶
- Check if reservoir contains specific content type.l
True if content type is present
- is_waste_reservoir() bool[source]¶
Check if this is a waste reservoir.
- Returns:
True if any content type contains “waste” (case-insensitive)
- Return type:
- remove_content(volume: float, return_dict: bool = False) dict[str, float] | None[source]¶
Remove content from the reservoir proportionally.
When content is removed from a reservoir, it’s removed proportionally from all content types since they are mixed together.
- Parameters:
- Returns:
If return_dict is True, returns dictionary mapping content types to removed volumes. Otherwise, returns None.
- Return type:
- Raises:
ValueError – If trying to remove more volume than available or volume is negative
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], row: int, consecutive_rows: int = 1) None¶
Validate column and row, raising ValueError if invalid.
Convenience method for when you want to raise an error immediately.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
TipDropzone¶
- class biohit_pipettor_plus.deck_structure.TipDropzone(size_x: float, size_y: float, size_z: float, can_be_stacked_upon: bool = False, offset: tuple[float, float] = (0, 0), drop_height_relative: float = 20, position: tuple[float, float] | None = None, labware_id: str | None = None)[source]¶
Bases:
LabwareRepresents a Tip Dropzone labware with relative drop position and height.
Initialize a TipDropzone instance.
- Parameters:
size_x (float) – Width of the drop zone in millimeters.
size_y (float) – Depth of the drop zone in millimeters.
size_z (float) – Height of the drop zone in millimeters.
labware_id (str, optional) – Unique ID for the dropzone object.
position (tuple[float, float], optional) – (x, y) position coordinates of the tip dropzone in millimeters. If None, position is not set.
drop_height_relative (float, optional) – Height from which tips are dropped relative to the labware. Default is 20.
- each_tip_needs_separate_item() bool¶
For multichannel operation, does each tip need to access a separate item?
- Returns:
True: Each tip needs its own item (e.g., Plate - small wells) False: All tips can share one item (e.g., ReservoirHolder - large reservoirs)
- Return type:
- restore_state_snapshot(snapshot: dict) None[source]¶
No-op - TipDropzone has no mutable state to restore
- to_dict() dict[source]¶
Serialize the TipDropzone instance to a dictionary, extending the base Labware fields.
- Returns:
dictionary representation of the tip dropzone.
- Return type:
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], row: int, consecutive_rows: int = 1) None¶
Validate column and row, raising ValueError if invalid.
Convenience method for when you want to raise an error immediately.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
Stack¶
- class biohit_pipettor_plus.deck_structure.Stack(size_x: float, size_y: float, size_z: float, offset: tuple[float, float] = (0.0, 0.0), labware_id: str | None = None, position: tuple[float, float] | None = None, can_be_stacked_upon: bool = True)[source]¶
Bases:
LabwareA labware that serves as a platform for stacking other labware on top. Has no functional elements itself - purely structural.
- can_be_stacked_upon¶
Whether other labware can be placed on top of this stack (default True)
- Type:
Initialize a Stack instance.
- each_tip_needs_separate_item() bool[source]¶
Stack has no functional items.
- Returns:
False - Stack is purely structural
- Return type:
- to_dict() dict[source]¶
Serialize the Stack to a dictionary.
- Returns:
Dictionary containing all stack attributes.
- Return type:
- validate_col_row(columns: list[int], row: int, consecutive_rows: int = 1) tuple[bool, str]¶
Validate column indices and row range for grid-based labware operations.
- Parameters:
- Returns:
(is_valid, error_message) - is_valid: True if validation passes, False otherwise - error_message: Empty string if valid, error description if invalid
- Return type:
- validate_col_row_or_raise(columns: list[int], start_row: int, consecutive_rows: int = 1) None[source]¶
Stack has no positions to validate - always raises.
- validate_multichannel_compatible(item_size_y: float) tuple[bool, str]¶
Validate if an item is large enough for multichannel operation.
- static validate_positive_dimensions(size_x: float, size_y: float, size_z: float, labware_type: str = 'Labware')¶
Validate that dimensions are positive
- registry: dict[str, type] = {'IndividualPipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.individualpipetteholder.IndividualPipetteHolder'>, 'PipetteHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.pipetteholder.PipetteHolder'>, 'Plate': <class 'biohit_pipettor_plus.deck_structure.labware_classes.plate.Plate'>, 'Reservoir': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoir.Reservoir'>, 'ReservoirHolder': <class 'biohit_pipettor_plus.deck_structure.labware_classes.reservoirHolder.ReservoirHolder'>, 'Stack': <class 'biohit_pipettor_plus.deck_structure.labware_classes.stack.Stack'>, 'TipDropzone': <class 'biohit_pipettor_plus.deck_structure.labware_classes.tipdropzone.TipDropzone'>, 'Well': <class 'biohit_pipettor_plus.deck_structure.labware_classes.well.Well'>}¶
PipettorPlus¶
- class biohit_pipettor_plus.pipettor_plus.pipettor_plus.PipettorPlus(tip_volume: Literal[200, 1000], *, multichannel: bool, initialize: bool = True, deck: Deck, tip_length: float | None = None, mock_pipettor=False)[source]¶
Bases:
objectInterface to the Biohit Robo pipettor with deck/slot/labware structure
- Parameters:
tip_volume (Literal[200, 1000]) – The tip volume (must be 1000 if multichannel is True)
multichannel (bool) – If True, it is assumed the device uses a multichannel pipet
initialize (bool) – If True, the device will be initialized
deck (Deck) – The deck containing slots and labware
tip_length (float) – The tip length in mm
mock_pipettor (bool) – If True, mock_pipettor will be used instead of Biohit Robo pipettor
- add_content(content_type: str, volume: float, tip_index: int | None = None) None[source]¶
Add content to specific tip(s).
- add_medium(source: ReservoirHolder, source_col_row: tuple[int, int], volume_per_well: float, destination: Plate, dest_col_row: List[tuple[int, int]], mix_volume: float = 0) None[source]¶
Transfer medium from a reservoir to destination plate column(s). Works for both single-channel and multichannel pipettors.
- Parameters:
source (ReservoirHolder) – ReservoirHolder containing the medium to transfer
source_col_row (tuple[int, int]) – (Column, Row) position of reservoir.
volume_per_well (float) – Volume (µL) to dispense into each destination well
destination (Plate) – Target plate to receive the medium
dest_col_row (List[tuple[int, int]]) – List of (Column, Row) positions in destination plate. Row is start row for multichannel pipettor.
mix_volume (float, optional) – Volume (µL) to use for mixing after dispensing. 0 = no mixing (default)
- discard_tips(tip_dropzone: Labware) None[source]¶
Discard tips to a TipDropzone.
- Parameters:
tip_dropzone (labware) – TipDropzone labware
- find_labware_by_type(labware_type: str) list[Labware][source]¶
Find a labware instance by its type name (case-sensitive).
- Parameters:
labware_type (str) – The class name of the labware to find (e.g., “Plate”, “ReservoirHolder”, “PipetteHolder”) Case-sensitive.
Labwares (Returns list of) – Any Labware instance of the specified type, placed in deck and slot.
ValueError (Raises) – If no labware of the specified type is found in the deck and placed in a slot.
- measure_foc(seconds: int, platename: str | None = None, bat_script_path: str | None = None)[source]¶
Wait for specified seconds, then run FOC measurement script.
- pick_multi_tips(pipette_holder: PipetteHolder, list_col_row: List[tuple[int, int]] | None = None) List[tuple[int, int]][source]¶
Pick tips from a PipetteHolder using multichannel pipettor.
For multichannel, 8 consecutive tips are picked vertically.
- Parameters:
pipette_holder (PipetteHolder) – PipetteHolder labware containing tips
list_col_row (List[tuple[int, int]], optional) – List of (column, start_row) grid indices to try. start_row indicates where the first pipettor in multichannel would be positioned. If None, automatically finds all grid locations with 8 consecutive occupied tips.
- Returns:
Actual (column, start_row) position where tips were picked
- Return type:
- Raises:
ValueError – If not a multichannel pipettor or no tips available
RuntimeError – If failed to pick tips from any specified grid location
- pick_single_tip(pipette_holder: PipetteHolder, list_col_row: List[tuple[int, int]] | None = None) List[tuple[int, int]][source]¶
Pick a single tip from a PipetteHolder using single-channel pipettor.
- pick_tips(pipette_holder: PipetteHolder, list_col_row: List[tuple[int, int]] | None = None) List[tuple[int, int]][source]¶
Pick tips from a PipetteHolder.
- Parameters:
pipette_holder (PipetteHolder) – PipetteHolder labware containing tips
list_col_row (List[tuple[int, int]], optional) – List of (column, row) grid indices to try. If None, automatically finds occupied grid locations.
- Returns:
Actual (column, row) positions where tips were picked
- Return type:
- Raises:
ValueError – If pipettor already has tips or pipette holder not found
- remove_content(volume: float, tip_index: int | None = None) None[source]¶
Remove content from tip(s) proportionally.
- remove_medium(source: Plate, source_col_row: List[tuple[int, int]], volume_per_well: float, destination: ReservoirHolder, destination_col_row: tuple[int, int]) None[source]¶
Remove medium from plate wells to a destination reservoir. Works for both single-channel and multichannel pipettors.
- Parameters:
source (Plate) – Plate to remove medium from
source_col_row (List[tuple[int, int]]) – List of (Column, Row) positions to remove from. Row is start row for multichannel pipettor.
volume_per_well (float) – Volume (µL) to remove from each well
destination (ReservoirHolder) – ReservoirHolder to receive the liquid
destination_col_row (tuple[int, int]) – (Column, Row) position of destination reservoir.
- replace_tips(pipette_holder: PipetteHolder, pick_pipette_holder: PipetteHolder | None = None, return_list_col_row: List[tuple[int, int]] | None = None, pick_list_col_row: List[tuple[int, int]] | None = None) dict[source]¶
- return_multi_tips(pipette_holder: PipetteHolder, list_col_row: List[tuple[int, int]] | None = None) List[tuple[int, int]][source]¶
Return tips to a PipetteHolder using multichannel pipettor.
For multichannel, 8 consecutive tips are returned vertically.
- Parameters:
pipette_holder (PipetteHolder) – PipetteHolder labware to return tips to
list_col_row (List[tuple[int, int]], optional) – List of (column, start_row) grid indices to try. start_row indicates where the first pipettor in multichannel would be positioned. If None, automatically finds all grid locations with 8 consecutive empty positions.
- Raises:
ValueError – If not a multichannel pipettor or no tips to return
RuntimeError – If failed to return tips to any specified grid location
- return_single_tip(pipette_holder: PipetteHolder, list_col_row: List[tuple[int, int]] | None = None) List[tuple[int, int]][source]¶
Return a single tip to a PipetteHolder using single-channel pipettor.
- return_tips(pipette_holder: PipetteHolder, list_col_row: List[tuple[int, int]] | None = None) List[tuple[int, int]][source]¶
return tips to PipetteHolder.
- spit(destination: Labware, dest_col_row: tuple[int, int], volume: float) None[source]¶
Dispense from tips to a destination labware.
Works with any labware type. If labware supports content tracking, content is tracked. If not, only physical movement is performed.
- suck(source: Labware, source_col_row: tuple[int, int], volume: float) None[source]¶
Aspirate from a source labware.
Works with any labware type. If labware supports content tracking, content is tracked. If not, only physical movement is performed.
- transfer_plate_to_plate(source: Plate, source_col_row: List[tuple[int, int]], destination: Plate, dest_col_row: List[tuple[int, int]], volume_per_well: float, mix_volume: float = 0) None[source]¶
Transfer liquid from source plate to destination plate (one-to-one mapping). Works for both single-channel and multichannel pipettors.
Each source position is transferred to the corresponding destination position. source_col_row[i] → dest_col_row[i]
- Parameters:
source (Plate) – Source plate
source_col_row (List[tuple[int, int]]) – List of source (Column, Row) positions. Row is start row for multichannel pipettor.
destination (Plate) – Destination plate
dest_col_row (List[tuple[int, int]]) – List of destination (Column, Row) positions. Must be same length as source_col_row.
volume_per_well (float) – Volume (µL) to transfer from each well
- Raises:
ValueError – If source and destination lists have different lengths