Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/resources/library/diy/grindbio.md
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you happen to have a resource definition for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Pioreactor is implemented as a skirted 1×1 Plate (PioreactorPlate) with a single circular well (A1) so it can be assigned to existing PlateHolders (e.g. Hamilton_MFX_plateholder_DWP_metal_tapped_10mm_3dprint).

Code: pylabrobot/resources/pioreactor/plates.py

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe we should make PlateHolder accept other resources then. It does not really make sense to index A1 into what is just a single container. I could also imagine Bioreactor (Resource) > Container as a child if people switch out the container frequently

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also in this case I was specifically asking about the adapter "This ANSI-compatible adapter allows the Pioreactor to be placed on the deck."

Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,8 @@ GrindBio created a 3D printed part for one of the Hamilton modules (cat.-no. 188
| Description | Image | PLR definition |
| - | - | - |
| 'Hamilton_MFX_plateholder_DWP_metal_tapped_10mm_3dprint'<br>3D printed supports accept Hamilton MFX DWP Module (cat.-no. 188042 / 188042-00)<br>[OnShape link to part](https://cad.onshape.com/documents/87b79aea22945656e1849b61/w/1d28384d184c23a6551facf8/e/3313021cc0b2fe3c5e005547) <br> Read more about assembly [here](https://labautomation.io/t/adapters-for-hamilton-carrier-188039/6561)| ![](../img/grindbio/3d-supports-for-Hamilton-module.jpeg) | `Hamilton_MFX_plateholder_DWP_metal_tapped_10mm_3dprint` |

## Pioreactor Plate Adapter
This ANSI-compatible adapter allows the Pioreactor to be placed on the deck.
https://cad.onshape.com/documents/cae54894e48b6624a361b53a/w/b1eaa767347c60ba70d74e31/e/cd40414a831dd21eb4ae1235

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions docs/resources/library/pioreactor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Pioreactor

Pioreactor is a modular, open-source benchtop bioreactor system designed for running many small, parallel microbial cultivations with tight control of key parameters (e.g., stirring, aeration, temperature, and dosing) and easy integration into automation workflows. In PyLabRobot, Pioreactor labware definitions let you reference Pioreactor vessels in deck layouts and liquid-handling protocols.

## Bioreactors

| Description | Image | PLR definition |
|-|-|-|
| `pioreactor_20ml` | ![](img/pioreactor/pioreactor_20mL.png) | `pioreactor_20ml` |
1 change: 1 addition & 0 deletions pylabrobot/resources/pioreactor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .plates import *
70 changes: 70 additions & 0 deletions pylabrobot/resources/pioreactor/plates.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why a Plate rather than a single Container instance?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose a skirted 1×1 Plate to stay within PLR’s existing deck/labware model. PlateHolders already expect Plates, collision/geometry logic is mature, and well addressing (A1) fits naturally.

While physically it’s a single vessel, modeling it as a Plate avoids adding special-case handling for a Container-like resource on the deck.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from typing import Optional

from pylabrobot.resources import Lid, Plate
from pylabrobot.resources.utils import create_ordered_items_2d
from pylabrobot.resources.well import CrossSectionType, Well, WellBottomType


def pioreactor_20ml(name: str, lid: Optional[Lid] = None) -> Plate:
"""
Pioreactor 20mL Vessel: https://pioreactor.com/products/pioreactor-20ml
Modeled as a 1x1 skirted plate

Geometry (mm):
- Outer footprint (on holder): 127.74 x 85.40
- Total height (plate Z): 126.5
- Central vial (A1): inner Ø 23.5, depth 57.0
"""

# --- Outer dimensions (measured) ---
OUTER_X = 127.74
OUTER_Y = 85.40
OUTER_Z = 126.5 # overall height used for collision checks

# --- Well (vial) geometry (spec/measured) ---
WELL_DIAMETER = 23.5
WELL_DEPTH = 57.0
MATERIAL_Z_THICKNESS = 1.0 # plastic between top surface and cavity start

# Center the single circular well
dx = (OUTER_X - WELL_DIAMETER) / 2.0
dy = (OUTER_Y - WELL_DIAMETER) / 2.0

# Distance from plate top to top of cavity
# dz = OUTER_Z - WELL_DEPTH - MATERIAL_Z_THICKNESS
# dz = 126.5-57 -1 =68.5 # tip crashes into bottom
dz = 76 # measured

# Cylinder area for volume/height conversions
cross_section_area = 3.14 * (WELL_DIAMETER / 2.0) ** 2

well_kwargs = {
"size_x": WELL_DIAMETER, # for CIRCLE, size_x == size_y == diameter
"size_y": WELL_DIAMETER,
"size_z": WELL_DEPTH,
"bottom_type": WellBottomType.FLAT,
"cross_section_type": CrossSectionType.CIRCLE,
"compute_height_from_volume": lambda v: v / cross_section_area,
"compute_volume_from_height": lambda h: h * cross_section_area,
"material_z_thickness": MATERIAL_Z_THICKNESS,
}

return Plate(
name=name,
size_x=OUTER_X,
size_y=OUTER_Y,
size_z=OUTER_Z,
lid=lid,
model="PioreactorPlate",
ordered_items=create_ordered_items_2d(
Well,
num_items_x=1,
num_items_y=1,
dx=dx,
dy=dy,
dz=dz,
item_dx=WELL_DIAMETER,
item_dy=WELL_DIAMETER,
**well_kwargs,
),
)
Loading