From 105a856ffdd46428cfc07207a6746a6dcda7925f Mon Sep 17 00:00:00 2001 From: Camillo Moschner Date: Mon, 18 May 2026 10:25:30 +0100 Subject: [PATCH] Fix NEST/AGenBio trough reservoir well geometry Definition bugs surfaced by the 3D geometry preview (PR #1042); these are corrections to the resource definitions themselves, scoped to one PR. - nest_1_troughplate_185000uL_Vb: well_size_x/well_size_y were transposed vs the plate body (127.76 x 85.48), so the single well was rotated 90 deg and overhung the trough; swapped to match the body and the working nest_8/nest_12 convention. - nest_1/nest_8/nest_12_troughplate_*: add cross_section_type=RECTANGLE (defaulted to CIRCLE, so rectangular troughs were modelled/rendered round). - AGenBio_1_troughplate_{190000,100000}uL_Fl: extend well size_z so the cavity reaches the plate rim; pin max_volume to the catalogued spec (190/100 mL) so the now-taller box does not overstate capacity; add the missing compute_volume_from_height to the 100000 def (mirrors the 190000 sibling). - Add concise one-line docstrings + `cat. no.:` provenance to all five. No public API change; geometry and volume models are now correct. ruff/format/mypy/pytest(resources)/docs(-W) all green. Co-Authored-By: Claude Opus 4.7 (1M context) --- pylabrobot/resources/agenbio/plates.py | 27 ++++++++++++++++---------- pylabrobot/resources/nest/plates.py | 26 ++++++++++++++++++++----- 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/pylabrobot/resources/agenbio/plates.py b/pylabrobot/resources/agenbio/plates.py index dd663b87eeb..aa932e09575 100644 --- a/pylabrobot/resources/agenbio/plates.py +++ b/pylabrobot/resources/agenbio/plates.py @@ -136,10 +136,10 @@ def AGenBio_1_wellplate_Fl(name: str, lid: Optional[Lid] = None) -> Plate: def AGenBio_1_troughplate_190000uL_Fl(name: str, lid: Optional[Lid] = None) -> Plate: - """ - AGenBio Catalog No. RES-190-F - - Material: Polypropylene - - Max. volume: 190 mL + """AGenBio single-well reagent reservoir, 190 mL, flat-bottom (ANSI/SLAS footprint). + + cat. no.: RES-190-F + Material: polypropylene """ INNER_WELL_WIDTH = 107.2 # measured INNER_WELL_HEIGHT = 70.9 # measured @@ -147,7 +147,8 @@ def AGenBio_1_troughplate_190000uL_Fl(name: str, lid: Optional[Lid] = None) -> P well_kwargs = { "size_x": INNER_WELL_WIDTH, # measured "size_y": INNER_WELL_HEIGHT, # measured - "size_z": 24.76, # measured to bottom of well + "size_z": 44.2 - 5.88, # well cavity reaches the plate top (plate size_z 44.2 - dz 5.88) + "max_volume": 190_000, # spec rating; size_z reaches the rim so the box volume would overstate capacity "bottom_type": WellBottomType.FLAT, "cross_section_type": CrossSectionType.RECTANGLE, "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( @@ -185,10 +186,10 @@ def AGenBio_1_troughplate_190000uL_Fl(name: str, lid: Optional[Lid] = None) -> P def AGenBio_1_troughplate_100000uL_Fl(name: str, lid: Optional[Lid] = None) -> Plate: - """ - AGenBio Catalog No. RES-100-F - - Material: Polypropylene - - Max. volume: 100 mL + """AGenBio single-well reagent reservoir, 100 mL, flat-bottom (ANSI/SLAS footprint). + + cat. no.: RES-100-F + Material: polypropylene """ INNER_WELL_WIDTH = 107.2 # measured INNER_WELL_HEIGHT = 70.9 # measured @@ -196,7 +197,8 @@ def AGenBio_1_troughplate_100000uL_Fl(name: str, lid: Optional[Lid] = None) -> P well_kwargs = { "size_x": INNER_WELL_WIDTH, # measured "size_y": INNER_WELL_HEIGHT, # measured - "size_z": 13, # measured to bottom of well + "size_z": 31.4 - 5.88, # well cavity reaches the plate top (plate size_z 31.4 - dz 5.88) + "max_volume": 100_000, # spec rating; size_z reaches the rim so the box volume would overstate capacity "bottom_type": WellBottomType.FLAT, "cross_section_type": CrossSectionType.RECTANGLE, "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( @@ -204,6 +206,11 @@ def AGenBio_1_troughplate_100000uL_Fl(name: str, lid: Optional[Lid] = None) -> P INNER_WELL_HEIGHT, INNER_WELL_WIDTH, ), + "compute_volume_from_height": lambda liquid_height: compute_volume_from_height_rectangle( + liquid_height, + INNER_WELL_HEIGHT, + INNER_WELL_WIDTH, + ), "material_z_thickness": 1, } diff --git a/pylabrobot/resources/nest/plates.py b/pylabrobot/resources/nest/plates.py index ce9ccb5e1c8..e674a196312 100644 --- a/pylabrobot/resources/nest/plates.py +++ b/pylabrobot/resources/nest/plates.py @@ -54,16 +54,22 @@ def nest_1_troughplate_195000uL_Vb(name: str) -> Plate: def nest_1_troughplate_185000uL_Vb(name: str) -> Plate: - """part no 360104. 384 tiny holes, but one container.""" + """NEST single-trough reagent reservoir plate, 185 mL, V-bottom (ANSI/SLAS footprint). + + cat. no.: 360104 + + 384 tiny holes feed one shared container. + """ real_well_d = (85.48 - 8.99 * 2) / 15 # 4.5. in the drawing it says 2.4 which is wrong - well_size_y = 127.76 - (12.13 - real_well_d / 2) * 2 # from datasheet - well_size_x = 85.48 - (8.99 - real_well_d / 2) * 2 # from datasheet + well_size_x = 127.76 - (12.13 - real_well_d / 2) * 2 # from datasheet + well_size_y = 85.48 - (8.99 - real_well_d / 2) * 2 # from datasheet well_kwargs = { "size_x": well_size_x, "size_y": well_size_y, "size_z": 26.85, # from datasheet "bottom_type": WellBottomType.V, + "cross_section_type": CrossSectionType.RECTANGLE, # an approximation: the trapezoid at the bottom is not fully defined in the datasheet "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( liquid_volume=liquid_volume, well_length=well_size_x, well_width=well_size_y @@ -96,7 +102,12 @@ def nest_1_troughplate_185000uL_Vb(name: str) -> Plate: def nest_8_troughplate_22000uL_Vb(name: str) -> Plate: - """part no 360101. not validated""" + """NEST 8-trough reagent reservoir plate, 22 mL, V-bottom (ANSI/SLAS footprint). + + cat. no.: 360101 + + Not validated. + """ well_size_x = 107.5 # from datasheet well_size_y = 8.2 # from datasheet well_kwargs = { @@ -104,6 +115,7 @@ def nest_8_troughplate_22000uL_Vb(name: str) -> Plate: "size_y": well_size_y, "size_z": 26.85, # from datasheet "bottom_type": WellBottomType.V, + "cross_section_type": CrossSectionType.RECTANGLE, # an approximation: the trapezoid at the bottom is not fully defined in the datasheet "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( liquid_volume=liquid_volume, well_length=well_size_x, well_width=well_size_y @@ -136,7 +148,10 @@ def nest_8_troughplate_22000uL_Vb(name: str) -> Plate: def nest_12_troughplate_15000uL_Vb(name: str) -> Plate: - """part no 360102.""" + """NEST 12-trough reagent reservoir plate, 15 mL, V-bottom (ANSI/SLAS footprint). + + cat. no.: 360102 + """ well_size_x = 8.2 # from datasheet well_size_y = 71.2 # from datasheet well_kwargs = { @@ -144,6 +159,7 @@ def nest_12_troughplate_15000uL_Vb(name: str) -> Plate: "size_y": well_size_y, "size_z": 26.85, # from datasheet "bottom_type": WellBottomType.V, + "cross_section_type": CrossSectionType.RECTANGLE, # an approximation: the trapezoid at the bottom is not fully defined in the datasheet "compute_height_from_volume": lambda liquid_volume: compute_height_from_volume_rectangle( liquid_volume=liquid_volume, well_length=well_size_x, well_width=well_size_y