diff --git a/docs/resources/library/hamilton.md b/docs/resources/library/hamilton.md
index 75ea4ce2bd3..b2b70eaf8b4 100644
--- a/docs/resources/library/hamilton.md
+++ b/docs/resources/library/hamilton.md
@@ -20,6 +20,7 @@ Company history: [Hamilton Robotics history](https://www.hamiltoncompany.com/his
| - | - | - |
| 'PLT_CAR_L5AC_A00'
Part no.: 182090
[manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/182090)
Carrier for 5x 96 Deep Well Plates or for 5x 384 tip racks (e.g.384HEAD_384TIPS_50μl) (6T) |  | `hamilton_plate_carrier_L5_ac` |
| 'PLT_CAR_L5MD_A00'
Part no.: 182365/02
[manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/182365)
Carries five ANSI/SLAS footprint MTPs in landscape orientation. Occupies six tracks.|  | `PLT_CAR_L5MD_A00` |
+| 'PLT_CAR_L5PCR'
Part no.: 182070
[manufacturer website](https://www.hamiltoncompany.com/other-robotics/182070)
Carrier for 5x 96-well PCR plates in landscape orientation using 182531 inserts (6T). |  | `PLT_CAR_L5PCR` |
| 'PLT_CAR_P3AC'
Part no.: 182365/03
[manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/182365)
Hamilton Deepwell Plate Carrier for 3 Plates (Portrait, 6 tracks wide)|  | `PLT_CAR_P3AC` |
| 'PLT_CAR_L5_DWP'
Part no.: 93522-01/03
manufacturer website?
Hamilton Plate Carrier for 5 Plates (Landscape, 6 tracks wide). Plastic tabs. |  | `PLT_CAR_L5_DWP` |
@@ -68,6 +69,7 @@ Sometimes called "reagent carriers" in Hamilton jargon.
| Description | Image | PLR definition |
| - | - | - |
+| 'Hamilton_96_adapter_182531'
Part no.: 182531
PCR insert for the [182070 carrier](https://www.hamiltoncompany.com/other-robotics/182070). 110 × 75 × 15 mm, with a 12 × 8 hole grid at 9 mm spacing. Geometry and image derived from Hamilton's 3D model. |  | `Hamilton_96_adapter_182531` |
| 'Hamilton_96_adapter_188182'
Part no.: 188182
[manufacturer website](https://www.hamiltoncompany.com/automated-liquid-handling/other-robotics/188182) (<-non-functional link?)
Adapter for 96 well PCR plate, plunged. Does not have an ANSI/SLAS footprint -> requires assignment with specified location. |  | `Hamilton_96_adapter_188182` |
## Consumables
diff --git a/docs/resources/library/img/hamilton/Hamilton_96_adapter_182531.jpg b/docs/resources/library/img/hamilton/Hamilton_96_adapter_182531.jpg
new file mode 100644
index 00000000000..7127144e7bc
Binary files /dev/null and b/docs/resources/library/img/hamilton/Hamilton_96_adapter_182531.jpg differ
diff --git a/docs/resources/library/img/hamilton/PLT_CAR_L5PCR.jpg b/docs/resources/library/img/hamilton/PLT_CAR_L5PCR.jpg
new file mode 100644
index 00000000000..719718faf0a
Binary files /dev/null and b/docs/resources/library/img/hamilton/PLT_CAR_L5PCR.jpg differ
diff --git a/pylabrobot/resources/hamilton/plate_adapters.py b/pylabrobot/resources/hamilton/plate_adapters.py
index e60115f1bc7..af67887476f 100644
--- a/pylabrobot/resources/hamilton/plate_adapters.py
+++ b/pylabrobot/resources/hamilton/plate_adapters.py
@@ -1,8 +1,40 @@
-"""Hamilton magnetic racks"""
+"""Hamilton PCR plate adapters."""
from pylabrobot.resources.plate_adapter import PlateAdapter
+def Hamilton_96_adapter_182531(name: str) -> PlateAdapter:
+ """Hamilton 182531 PCR insert for the 182070 landscape carrier.
+
+ Geometry is derived from the five ``182531_RNO`` meshes in Hamilton's 3D model.
+ The 12 x 8 hole grid has 9 mm spacing; H1 is 5.5/6.0 mm from the left/front edges.
+ The mesh's top opening diameter is approximately 7.732 mm, and its cavity bottom
+ is 1.951 mm above the insert base, giving a 13.049 mm cavity depth.
+
+ ``dz`` represents the modeled cavity bottom. Automatic plate placement uses that
+ reference; it does not model contact between a particular PCR plate and the tapered
+ cavities. Supply an explicit plate location if its seating height differs.
+
+ Args:
+ name: The insert name.
+ """
+ hole_diameter = 7.732 # from the .x mesh's top opening vertices, rounded to 0.001 mm
+ return PlateAdapter(
+ name=name,
+ size_x=110.0, # from the .x mesh bounds
+ size_y=75.0, # from the .x mesh bounds
+ size_z=15.0, # from the .x mesh: 108.7 - 93.7
+ dx=5.5 - hole_diameter / 2, # from the .x hole centers relative to the insert's left edge
+ dy=6.0 - hole_diameter / 2, # from the .x hole centers relative to the insert's front edge
+ dz=1.951, # from the .x mesh: cavity bottom 95.651 - insert base 93.7
+ adapter_hole_size_x=hole_diameter,
+ adapter_hole_size_y=hole_diameter,
+ adapter_hole_dx=9.0, # from the .x hole grid
+ adapter_hole_dy=9.0, # from the .x hole grid
+ model="Hamilton_96_adapter_182531",
+ )
+
+
def Hamilton_96_adapter_188182(name: str) -> PlateAdapter:
"""Hamilton cat. no.: 188182
Adapter for 96 well PCR plate, plunged.
diff --git a/pylabrobot/resources/hamilton/plate_carriers.py b/pylabrobot/resources/hamilton/plate_carriers.py
index 1f9e43ea986..6246793b35e 100644
--- a/pylabrobot/resources/hamilton/plate_carriers.py
+++ b/pylabrobot/resources/hamilton/plate_carriers.py
@@ -6,6 +6,7 @@
PlateHolder,
create_homogeneous_resources,
)
+from pylabrobot.resources.hamilton.plate_adapters import Hamilton_96_adapter_182531
def PLT_CAR_L4_HHS_ALT_A00(name: str) -> PlateCarrier:
@@ -254,28 +255,79 @@ def PLT_CAR_L5MD_A00(name: str) -> PlateCarrier:
)
-def PLT_CAR_L5PCR(name: str) -> PlateCarrier:
- """ """
- return PlateCarrier(
+def PLT_CAR_L5PCR(name: str, *, with_adapters: bool = False) -> PlateCarrier:
+ """Hamilton 182070 carrier for five 96-well PCR plates in landscape orientation (6 tracks).
+
+ https://www.hamiltoncompany.com/other-robotics/182070
+
+ Geometry:
+ Hamilton's VENUS template specifies a 135 x 497 x 130 mm carrier envelope and
+ 96 mm site pitch. Its 130 mm height and clearance describe the envelope, not the insert
+ support surface. The 75 mm inserts have 21 mm clear gaps.
+
+ Insert locations are derived from the five 182531_RNO meshes in Hamilton's 3D model.
+ Coordinates are referenced to the main body's left/front edges and the rail underside,
+ excluding the projecting front latch. In the model's stored axes, the reference
+ is lateral X = -67, longitudinal Z = -244, and vertical Y = -12 mm. Subtracting
+ these from the first insert's minimum coordinates (-55, -229.4, 93.7 mm,
+ respectively) gives these carrier-relative locations:
+
+ Site | Insert X (mm) | Insert Y (mm) | Support Z (mm)
+ 0 | 12.00 | 14.60 | 105.70
+ 1 | 12.00 | 110.60 | 105.70
+ 2 | 12.00 | 206.60 | 105.70
+ 3 | 12.00 | 302.60 | 105.70
+ 4 | 12.00 | 398.60 | 105.70
+
+ Each site is a 110 x 75 mm support plane with no additional offset or sinking.
+ Each insert is 15 mm high, with H1 at 5.5/6.0 mm from its left/front edges and a
+ 9 x 9 mm hole grid. The first H1 center is therefore 17.5/20.6 mm from the carrier
+ body edges. STARlet track placement adds a 100 mm Z datum, giving insert bottoms
+ at Z = 205.7 mm and tops at Z = 220.7 mm.
+
+ The template also declares 127 x 86 mm plate sites at X = 5, Y = 9.5 + 96i,
+ Z = 109.2 mm. These are separate VENUS plate-placement references, not insert-base
+ coordinates, and are not added to the insert locations above. VENUS site IDs run
+ 5 through 1 from front to back; PyLabRobot indices run 0 through 4.
+
+ The insert model gives an approximately 7.732 mm top opening and 13.049 mm cavity
+ depth, with the cavity bottom 1.951 mm above the insert base. The adapter's dz uses
+ this modeled bottom. Automatic plate placement aligns the plate origin to that
+ reference; it does not calculate how a particular plate contacts the tapered
+ cavities. Supply an explicit plate location when its seating height differs.
+
+ Args:
+ name: The carrier name.
+ with_adapters: Mount five independent 182531 inserts. Defaults to empty sites.
+ """
+
+ support_height = 105.7 # from .x: insert base 93.7 minus rail underside -12.0
+ adapter_x = 12.0 # from .x: insert edge -55.0 minus body left edge -67.0
+ first_adapter_y = 14.6 # from .x: insert edge -229.4 minus body front edge -244.0
+ site_pitch = 96.0 # from spec: .tml site Y differences; also present in the .x model
+ carrier = PlateCarrier(
name=name,
- size_x=135.0,
- size_y=497.0,
- size_z=130.0,
+ size_x=135.0, # from spec: .tml Dim.Dx
+ size_y=497.0, # from spec: .tml Dim.Dy
+ size_z=130.0, # from spec: .tml Dim.Dz and Clearance
sites=create_homogeneous_resources(
klass=PlateHolder,
locations=[
- Coordinate(4.0, 8.5, 107.5),
- Coordinate(4.0, 104.5, 107.5),
- Coordinate(4.0, 200.5, 107.5),
- Coordinate(4.0, 296.5, 107.5),
- Coordinate(4.0, 392.5, 107.5),
+ Coordinate(adapter_x, first_adapter_y + i * site_pitch, support_height)
+ for i in range(5) # number of positions from spec
],
- resource_size_x=127.0,
- resource_size_y=86.0,
+ resource_size_x=110.0, # from the .x insert mesh bounds
+ resource_size_y=75.0, # from the .x insert mesh bounds
+ resource_size_z=0.0, # sites represent the adapter support plane
name_prefix=name,
+ pedestal_size_z=0.0, # adapters rest on the support plane without sinking
),
model="PLT_CAR_L5PCR",
)
+ if with_adapters:
+ for i in carrier.sites:
+ carrier[i] = Hamilton_96_adapter_182531(f"{name}_adapter_{i}")
+ return carrier
def PLT_CAR_L5PCR_A00(name: str) -> PlateCarrier: