From 9f40db43919e1819a9cf568fc5be718a1435dca7 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 22 Oct 2024 16:06:08 -0700 Subject: [PATCH 1/8] deprecate _L and _P in resources --- pylabrobot/resources/azenta/plates.py | 8 +- pylabrobot/resources/corning_axygen/plates.py | 8 +- pylabrobot/resources/corning_costar/plates.py | 18 +- pylabrobot/resources/eppendorf/plates.py | 8 +- pylabrobot/resources/ml_star/tip_racks.py | 161 +++++++++++++----- pylabrobot/resources/porvair/plates.py | 8 +- pylabrobot/resources/thermo_fisher/plates.py | 15 +- 7 files changed, 165 insertions(+), 61 deletions(-) diff --git a/pylabrobot/resources/azenta/plates.py b/pylabrobot/resources/azenta/plates.py index 3030351747..b85a64963a 100644 --- a/pylabrobot/resources/azenta/plates.py +++ b/pylabrobot/resources/azenta/plates.py @@ -89,9 +89,13 @@ def Azenta4titudeFrameStar_96_wellplate_200ul_Vb(name: str, with_lid: bool = Fal #: Azenta4titudeFrameStar_96_wellplate_Vb_L def Azenta4titudeFrameStar_96_wellplate_200ul_Vb_L(name: str, with_lid: bool = False) -> Plate: - return Azenta4titudeFrameStar_96_wellplate_200ul_Vb(name=name, with_lid=with_lid) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Azenta4titudeFrameStar_96_wellplate_200ul_Vb instead.") #: Azenta4titudeFrameStar_96_wellplate_Vb_P def Azenta4titudeFrameStar_96_wellplate_200ul_Vb_P(name: str, with_lid: bool = False) -> Plate: - return Azenta4titudeFrameStar_96_wellplate_200ul_Vb(name=name, with_lid=with_lid).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. For portrait, use " + "Azenta4titudeFrameStar_96_wellplate_200ul_Vb().rotated(z=90) instead.") diff --git a/pylabrobot/resources/corning_axygen/plates.py b/pylabrobot/resources/corning_axygen/plates.py index c3cee09308..404e3cd87d 100644 --- a/pylabrobot/resources/corning_axygen/plates.py +++ b/pylabrobot/resources/corning_axygen/plates.py @@ -62,9 +62,13 @@ def Axy_24_DW_10ML(name: str, with_lid: bool = False) -> Plate: #: Axy_24_DW_10ML_L def Axy_24_DW_10ML_L(name: str, with_lid: bool = False) -> Plate: - return Axy_24_DW_10ML(name=name, with_lid=with_lid) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Axy_24_DW_10ML instead.") #: Axy_24_DW_10ML_P def Axy_24_DW_10ML_P(name: str, with_lid: bool = False) -> Plate: - return Axy_24_DW_10ML(name=name, with_lid=with_lid).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Axy_24_DW_10ML().rotated(z=90) instead.") diff --git a/pylabrobot/resources/corning_costar/plates.py b/pylabrobot/resources/corning_costar/plates.py index 704147a7dc..7b27180334 100644 --- a/pylabrobot/resources/corning_costar/plates.py +++ b/pylabrobot/resources/corning_costar/plates.py @@ -88,11 +88,15 @@ def Cos_6_wellplate_16800ul_Fb(name: str, with_lid: bool = True) -> Plate: def Cos_6_wellplate_16800ul_Fb_L(name: str, with_lid: bool = True) -> Plate: - return Cos_6_wellplate_16800ul_Fb(name=name, with_lid=with_lid) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Cos_6_wellplate_16800ul_Fb instead.") def Cos_6_wellplate_16800ul_Fb_P(name: str, with_lid: bool = True) -> Plate: - return Cos_6_wellplate_16800ul_Fb(name=name, with_lid=with_lid).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Cos_6_wellplate_16800ul_Fb().rotated(z=90) instead.") # # # # # # # # # # Cos_96_wellplate_2mL_Vb # # # # # # # # # # @@ -182,13 +186,15 @@ def Cos_96_wellplate_2mL_Vb(name: str, with_lid: bool = False) -> Plate: def Cos_96_wellplate_2mL_Vb_L(name: str, with_lid: bool = False) -> Plate: - """Cos_96_wellplate_2mL_Vb""" - return Cos_96_wellplate_2mL_Vb(name=name, with_lid=with_lid) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Cos_96_wellplate_2mL_Vb instead.") def Cos_96_wellplate_2mL_Vb_P(name: str, with_lid: bool = False) -> Plate: - """Cos_96_wellplate_2mL_Vb""" - return Cos_96_wellplate_2mL_Vb(name=name, with_lid=with_lid).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Cos_96_wellplate_2mL_Vb().rotated(z=90) instead.") # # # # # # # # # # Cor_96_wellplate_360ul_Fb # # # # # # # # # # diff --git a/pylabrobot/resources/eppendorf/plates.py b/pylabrobot/resources/eppendorf/plates.py index 1e4e2b1fa7..7bdbf6b431 100644 --- a/pylabrobot/resources/eppendorf/plates.py +++ b/pylabrobot/resources/eppendorf/plates.py @@ -102,8 +102,12 @@ def Eppendorf_96_wellplate_250ul_Vb(name: str, with_lid: bool = False) -> Plate: def Eppendorf_96_wellplate_250ul_Vb_L(name: str, with_lid: bool = False) -> Plate: - return Eppendorf_96_wellplate_250ul_Vb(name=name, with_lid=with_lid) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Eppendorf_96_wellplate_250ul_Vb instead.") def Eppendorf_96_wellplate_250ul_Vb_P(name: str, with_lid: bool = False) -> Plate: - return Eppendorf_96_wellplate_250ul_Vb(name=name, with_lid=with_lid).rotated(90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Eppendorf_96_wellplate_250ul_Vb.rotated(90) instead.") diff --git a/pylabrobot/resources/ml_star/tip_racks.py b/pylabrobot/resources/ml_star/tip_racks.py index ab407e20c4..fb6938a529 100644 --- a/pylabrobot/resources/ml_star/tip_racks.py +++ b/pylabrobot/resources/ml_star/tip_racks.py @@ -1,5 +1,3 @@ -"""ML Star tips""" - from pylabrobot.resources.utils import create_ordered_items_2d from pylabrobot.resources.tip_rack import ( TipSpot, @@ -23,7 +21,7 @@ ) -def FourmlTF_L(name: str, with_tips: bool = True) -> TipRack: +def FourmlTF(name: str, with_tips: bool = True) -> TipRack: """Tip Rack 24x 4ml Tip with Filter landscape oriented""" return TipRack( name=name, @@ -48,12 +46,19 @@ def FourmlTF_L(name: str, with_tips: bool = True) -> TipRack: ) +def FourmlTF_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "FourmlTF instead.") + + def FourmlTF_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack 24x 4ml Tip with Filter portrait oriented""" - return FourmlTF_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "FourmlTF().rotated(z=90) instead.") -def FivemlT_L(name: str, with_tips: bool = True) -> TipRack: +def FivemlT(name: str, with_tips: bool = True) -> TipRack: """Tip Rack 24x 5ml Tip landscape oriented""" return TipRack( name=name, @@ -78,12 +83,19 @@ def FivemlT_L(name: str, with_tips: bool = True) -> TipRack: ) +def FivemlT_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "FivemlT instead.") + + def FivemlT_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack 24x 5ml Tip portrait oriented""" - return FivemlT_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "FivemlT().rotated(z=90) instead.") -def HTF_L(name: str, with_tips: bool = True) -> TipRack: +def HTF(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 1000ul High Volume Tip with filter""" return TipRack( name=name, @@ -108,6 +120,18 @@ def HTF_L(name: str, with_tips: bool = True) -> TipRack: ) +def HTF_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "HTF instead.") + + +def HTF_P(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "HTF().rotated(z=90) instead.") + + def HTF_L_WIDE(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 1000ul High Volume Tip with filter""" return TipRack( @@ -158,12 +182,7 @@ def HTF_L_ULTRAWIDE(name: str, with_tips: bool = True) -> TipRack: ) -def HTF_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 1000ul High Volume Tip with filter (portrait)""" - return HTF_L(name=name, with_tips=with_tips).rotated(z=90) - - -def HT_L(name: str, with_tips: bool = True) -> TipRack: +def HT(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 1000ul High Volume Tip""" return TipRack( name=name, @@ -188,12 +207,19 @@ def HT_L(name: str, with_tips: bool = True) -> TipRack: ) +def HT_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "HT instead.") + + def HT_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 1000ul High Volume Tip (portrait)""" - return HT_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "HT().rotated(z=90) instead.") -def LTF_L(name: str, with_tips: bool = True) -> TipRack: +def LTF(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 10ul Low Volume Tip with filter""" return TipRack( name=name, @@ -218,12 +244,19 @@ def LTF_L(name: str, with_tips: bool = True) -> TipRack: ) +def LTF_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "LTF instead.") + + def LTF_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 10ul Low Volume Tip with filter (portrait)""" - return LTF_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "LTF().rotated(z=90) instead.") -def LT_L(name: str, with_tips: bool = True) -> TipRack: +def LT(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 10ul Low Volume Tip""" return TipRack( name=name, @@ -248,12 +281,19 @@ def LT_L(name: str, with_tips: bool = True) -> TipRack: ) +def LT_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "LT instead.") + + def LT_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 10ul Low Volume Tip (portrait)""" - return LT_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "LT().rotated(z=90) instead.") -def STF_L(name: str, with_tips: bool = True) -> TipRack: +def STF(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 300ul Standard Volume Tip with filter""" return TipRack( name=name, @@ -278,12 +318,19 @@ def STF_L(name: str, with_tips: bool = True) -> TipRack: ) +def STF_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "STF instead.") + + def STF_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 300ul Standard Volume Tip with filter (portrait)""" - return STF_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "STF().rotated(z=90) instead.") -def STF_Slim_L(name: str, with_tips: bool = True) -> TipRack: +def STF_Slim(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 300ul Slim Standard Volume Tip with filter""" return TipRack( name=name, @@ -308,12 +355,19 @@ def STF_Slim_L(name: str, with_tips: bool = True) -> TipRack: ) +def STF_Slim_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "STF_Slim instead.") + + def STF_Slim_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 300ul Slim Standard Volume Tip with filter (portrait)""" - return STF_Slim_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "STF_Slim().rotated(z=90) instead.") -def ST_L(name: str, with_tips: bool = True) -> TipRack: +def ST(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 300ul Standard Volume Tip""" return TipRack( name=name, @@ -338,12 +392,19 @@ def ST_L(name: str, with_tips: bool = True) -> TipRack: ) +def ST_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "ST instead.") + + def ST_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 300ul Standard Volume Tip (portrait)""" - return ST_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "ST().rotated(z=90) instead.") -def TIP_50ul_w_filter_L(name: str, with_tips: bool = True) -> TipRack: +def TIP_50ul_w_filter(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 50ul Tip with filter""" return TipRack( name=name, @@ -368,12 +429,19 @@ def TIP_50ul_w_filter_L(name: str, with_tips: bool = True) -> TipRack: ) +def TIP_50ul_w_filter_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "TIP_50ul_w_filter instead.") + + def TIP_50ul_w_filter_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 50ul Tip with filter (portrait)""" - return TIP_50ul_w_filter_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "TIP_50ul_w_filter().rotated(z=90) instead.") -def TIP_50ul_L(name: str, with_tips: bool = True) -> TipRack: +def TIP_50ul(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 50ul Tip""" return TipRack( name=name, @@ -398,9 +466,16 @@ def TIP_50ul_L(name: str, with_tips: bool = True) -> TipRack: ) +def TIP_50ul_L(name: str, with_tips: bool = True) -> TipRack: + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "TIP_50ul instead.") + + def TIP_50ul_P(name: str, with_tips: bool = True) -> TipRack: - """Tip Rack with 96 50ul Tip (portrait)""" - return TIP_50ul_L(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "TIP_50ul().rotated(z=90) instead.") def Hamilton_96_tiprack_50ul_NTR(name: str, with_tips: bool = True) -> NestedTipRack: @@ -431,10 +506,12 @@ def Hamilton_96_tiprack_50ul_NTR(name: str, with_tips: bool = True) -> NestedTip def Hamilton_96_tiprack_50ul_NTR_L(name: str, with_tips: bool = True) -> NestedTipRack: - """Nested Tip Rack with 96 50ul Tip (landscape, i.e. default)""" - return Hamilton_96_tiprack_50ul_NTR(name=name, with_tips=with_tips) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Hamilton_96_tiprack_50ul_NTR instead.") def Hamilton_96_tiprack_50ul_NTR_P(name: str, with_tips: bool = True) -> NestedTipRack: - """Nested Tip Rack with 96 50ul Tip (portrait)""" - return Hamilton_96_tiprack_50ul_NTR(name=name, with_tips=with_tips).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Hamilton_96_tiprack_50ul_NTR().rotated(z=90) instead.") diff --git a/pylabrobot/resources/porvair/plates.py b/pylabrobot/resources/porvair/plates.py index 06046699c8..6ac29ec8fd 100644 --- a/pylabrobot/resources/porvair/plates.py +++ b/pylabrobot/resources/porvair/plates.py @@ -93,12 +93,16 @@ def Porvair_6_reservoir_47ml_Vb(name: str, with_lid: bool = False) -> Plate: #: Porvair_6_reservoir_47ml_Vb_L def Porvair_6_reservoir_47ml_Vb_L(name: str, with_lid: bool = False) -> Plate: - return Porvair_6_reservoir_47ml_Vb(name=name, with_lid=with_lid) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Porvair_6_reservoir_47ml_Vb instead.") #: Porvair_6_reservoir_47ml_Vb_P def Porvair_6_reservoir_47ml_Vb_P(name: str, with_lid: bool = False) -> Plate: - return Porvair_6_reservoir_47ml_Vb(name=name, with_lid=with_lid).rotated(z=90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Porvair_6_reservoir_47ml_Vb.rotated(90) instead.") def Porvair_24_wellplate_Vb(name: str, lid: Optional[Lid] = None) -> Plate: diff --git a/pylabrobot/resources/thermo_fisher/plates.py b/pylabrobot/resources/thermo_fisher/plates.py index 82d7190527..db4d2b9e24 100644 --- a/pylabrobot/resources/thermo_fisher/plates.py +++ b/pylabrobot/resources/thermo_fisher/plates.py @@ -119,11 +119,14 @@ def Thermo_TS_96_wellplate_1200ul_Rb(name: str, with_lid: bool = False) -> Plate def Thermo_TS_96_wellplate_1200ul_Rb_L(name: str, with_lid: bool = False) -> Plate: - return Thermo_TS_96_wellplate_1200ul_Rb(name=name, with_lid=with_lid) - + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Thermo_TS_96_wellplate_1200ul_Rb instead.") def Thermo_TS_96_wellplate_1200ul_Rb_P(name: str, with_lid: bool = False) -> Plate: - return Thermo_TS_96_wellplate_1200ul_Rb(name=name, with_lid=with_lid).rotated(90) + # https://github.com/PyLabRobot/pylabrobot/issues/252 + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Thermo_TS_96_wellplate_1200ul_Rb().rotated(z=90) instead.") # # # # # # # # # # Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate # # # # # # # # # # @@ -226,8 +229,10 @@ def Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate(name: str, with_lid: bool = Fals def Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate_L(name: str, with_lid: bool = False) -> Plate: - return Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate(name=name, with_lid=with_lid) + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate instead.") def Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate_P(name: str, with_lid: bool = False) -> Plate: - return Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate(name=name, with_lid=with_lid).rotated(90) + raise NotImplementedError("_L and _P definitions are deprecated. Use " + "Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate.rotated(90) instead.") From 2751c018946938f3cf2db293f4983bf84ac325e2 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 22 Oct 2024 16:09:07 -0700 Subject: [PATCH 2/8] format --- pylabrobot/resources/azenta/plates.py | 12 ++- pylabrobot/resources/corning_axygen/plates.py | 8 +- pylabrobot/resources/corning_costar/plates.py | 21 ++-- pylabrobot/resources/eppendorf/plates.py | 11 +- pylabrobot/resources/ml_star/tip_racks.py | 101 +++++++++--------- pylabrobot/resources/porvair/plates.py | 10 +- pylabrobot/resources/thermo_fisher/plates.py | 24 +++-- 7 files changed, 107 insertions(+), 80 deletions(-) diff --git a/pylabrobot/resources/azenta/plates.py b/pylabrobot/resources/azenta/plates.py index b85a64963a..a83a63e019 100644 --- a/pylabrobot/resources/azenta/plates.py +++ b/pylabrobot/resources/azenta/plates.py @@ -90,12 +90,16 @@ def Azenta4titudeFrameStar_96_wellplate_200ul_Vb(name: str, with_lid: bool = Fal #: Azenta4titudeFrameStar_96_wellplate_Vb_L def Azenta4titudeFrameStar_96_wellplate_200ul_Vb_L(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Azenta4titudeFrameStar_96_wellplate_200ul_Vb instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " + "Azenta4titudeFrameStar_96_wellplate_200ul_Vb instead." + ) #: Azenta4titudeFrameStar_96_wellplate_Vb_P def Azenta4titudeFrameStar_96_wellplate_200ul_Vb_P(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. For portrait, use " - "Azenta4titudeFrameStar_96_wellplate_200ul_Vb().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. For portrait, use " + "Azenta4titudeFrameStar_96_wellplate_200ul_Vb().rotated(z=90) instead." + ) diff --git a/pylabrobot/resources/corning_axygen/plates.py b/pylabrobot/resources/corning_axygen/plates.py index 404e3cd87d..184dabf77d 100644 --- a/pylabrobot/resources/corning_axygen/plates.py +++ b/pylabrobot/resources/corning_axygen/plates.py @@ -63,12 +63,12 @@ def Axy_24_DW_10ML(name: str, with_lid: bool = False) -> Plate: #: Axy_24_DW_10ML_L def Axy_24_DW_10ML_L(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Axy_24_DW_10ML instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "Axy_24_DW_10ML instead.") #: Axy_24_DW_10ML_P def Axy_24_DW_10ML_P(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Axy_24_DW_10ML().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Axy_24_DW_10ML().rotated(z=90) instead." + ) diff --git a/pylabrobot/resources/corning_costar/plates.py b/pylabrobot/resources/corning_costar/plates.py index 7b27180334..4884a7b5c9 100644 --- a/pylabrobot/resources/corning_costar/plates.py +++ b/pylabrobot/resources/corning_costar/plates.py @@ -89,14 +89,17 @@ def Cos_6_wellplate_16800ul_Fb(name: str, with_lid: bool = True) -> Plate: def Cos_6_wellplate_16800ul_Fb_L(name: str, with_lid: bool = True) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Cos_6_wellplate_16800ul_Fb instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Cos_6_wellplate_16800ul_Fb instead." + ) def Cos_6_wellplate_16800ul_Fb_P(name: str, with_lid: bool = True) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Cos_6_wellplate_16800ul_Fb().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " + "Cos_6_wellplate_16800ul_Fb().rotated(z=90) instead." + ) # # # # # # # # # # Cos_96_wellplate_2mL_Vb # # # # # # # # # # @@ -187,14 +190,16 @@ def Cos_96_wellplate_2mL_Vb(name: str, with_lid: bool = False) -> Plate: def Cos_96_wellplate_2mL_Vb_L(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Cos_96_wellplate_2mL_Vb instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Cos_96_wellplate_2mL_Vb instead." + ) def Cos_96_wellplate_2mL_Vb_P(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Cos_96_wellplate_2mL_Vb().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Cos_96_wellplate_2mL_Vb().rotated(z=90) instead." + ) # # # # # # # # # # Cor_96_wellplate_360ul_Fb # # # # # # # # # # diff --git a/pylabrobot/resources/eppendorf/plates.py b/pylabrobot/resources/eppendorf/plates.py index 7bdbf6b431..c70f320606 100644 --- a/pylabrobot/resources/eppendorf/plates.py +++ b/pylabrobot/resources/eppendorf/plates.py @@ -103,11 +103,14 @@ def Eppendorf_96_wellplate_250ul_Vb(name: str, with_lid: bool = False) -> Plate: def Eppendorf_96_wellplate_250ul_Vb_L(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Eppendorf_96_wellplate_250ul_Vb instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Eppendorf_96_wellplate_250ul_Vb instead." + ) def Eppendorf_96_wellplate_250ul_Vb_P(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Eppendorf_96_wellplate_250ul_Vb.rotated(90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " + "Eppendorf_96_wellplate_250ul_Vb.rotated(90) instead." + ) diff --git a/pylabrobot/resources/ml_star/tip_racks.py b/pylabrobot/resources/ml_star/tip_racks.py index fb6938a529..58ae250a33 100644 --- a/pylabrobot/resources/ml_star/tip_racks.py +++ b/pylabrobot/resources/ml_star/tip_racks.py @@ -48,14 +48,14 @@ def FourmlTF(name: str, with_tips: bool = True) -> TipRack: def FourmlTF_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "FourmlTF instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "FourmlTF instead.") def FourmlTF_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "FourmlTF().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "FourmlTF().rotated(z=90) instead." + ) def FivemlT(name: str, with_tips: bool = True) -> TipRack: @@ -85,14 +85,14 @@ def FivemlT(name: str, with_tips: bool = True) -> TipRack: def FivemlT_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "FivemlT instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "FivemlT instead.") def FivemlT_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "FivemlT().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "FivemlT().rotated(z=90) instead." + ) def HTF(name: str, with_tips: bool = True) -> TipRack: @@ -122,14 +122,14 @@ def HTF(name: str, with_tips: bool = True) -> TipRack: def HTF_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "HTF instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "HTF instead.") def HTF_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "HTF().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "HTF().rotated(z=90) instead." + ) def HTF_L_WIDE(name: str, with_tips: bool = True) -> TipRack: @@ -209,14 +209,14 @@ def HT(name: str, with_tips: bool = True) -> TipRack: def HT_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "HT instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "HT instead.") def HT_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "HT().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "HT().rotated(z=90) instead." + ) def LTF(name: str, with_tips: bool = True) -> TipRack: @@ -246,14 +246,14 @@ def LTF(name: str, with_tips: bool = True) -> TipRack: def LTF_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "LTF instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "LTF instead.") def LTF_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "LTF().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "LTF().rotated(z=90) instead." + ) def LT(name: str, with_tips: bool = True) -> TipRack: @@ -283,14 +283,14 @@ def LT(name: str, with_tips: bool = True) -> TipRack: def LT_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "LT instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "LT instead.") def LT_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "LT().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "LT().rotated(z=90) instead." + ) def STF(name: str, with_tips: bool = True) -> TipRack: @@ -320,14 +320,14 @@ def STF(name: str, with_tips: bool = True) -> TipRack: def STF_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "STF instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "STF instead.") def STF_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "STF().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "STF().rotated(z=90) instead." + ) def STF_Slim(name: str, with_tips: bool = True) -> TipRack: @@ -357,14 +357,14 @@ def STF_Slim(name: str, with_tips: bool = True) -> TipRack: def STF_Slim_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "STF_Slim instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "STF_Slim instead.") def STF_Slim_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "STF_Slim().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "STF_Slim().rotated(z=90) instead." + ) def ST(name: str, with_tips: bool = True) -> TipRack: @@ -394,14 +394,14 @@ def ST(name: str, with_tips: bool = True) -> TipRack: def ST_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "ST instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "ST instead.") def ST_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "ST().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "ST().rotated(z=90) instead." + ) def TIP_50ul_w_filter(name: str, with_tips: bool = True) -> TipRack: @@ -431,14 +431,16 @@ def TIP_50ul_w_filter(name: str, with_tips: bool = True) -> TipRack: def TIP_50ul_w_filter_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "TIP_50ul_w_filter instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "TIP_50ul_w_filter instead." + ) def TIP_50ul_w_filter_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "TIP_50ul_w_filter().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "TIP_50ul_w_filter().rotated(z=90) instead." + ) def TIP_50ul(name: str, with_tips: bool = True) -> TipRack: @@ -468,14 +470,14 @@ def TIP_50ul(name: str, with_tips: bool = True) -> TipRack: def TIP_50ul_L(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "TIP_50ul instead.") + raise NotImplementedError("_L and _P definitions are deprecated. Use " "TIP_50ul instead.") def TIP_50ul_P(name: str, with_tips: bool = True) -> TipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "TIP_50ul().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "TIP_50ul().rotated(z=90) instead." + ) def Hamilton_96_tiprack_50ul_NTR(name: str, with_tips: bool = True) -> NestedTipRack: @@ -507,11 +509,14 @@ def Hamilton_96_tiprack_50ul_NTR(name: str, with_tips: bool = True) -> NestedTip def Hamilton_96_tiprack_50ul_NTR_L(name: str, with_tips: bool = True) -> NestedTipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Hamilton_96_tiprack_50ul_NTR instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Hamilton_96_tiprack_50ul_NTR instead." + ) def Hamilton_96_tiprack_50ul_NTR_P(name: str, with_tips: bool = True) -> NestedTipRack: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Hamilton_96_tiprack_50ul_NTR().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " + "Hamilton_96_tiprack_50ul_NTR().rotated(z=90) instead." + ) diff --git a/pylabrobot/resources/porvair/plates.py b/pylabrobot/resources/porvair/plates.py index 6ac29ec8fd..7d3e37c2c5 100644 --- a/pylabrobot/resources/porvair/plates.py +++ b/pylabrobot/resources/porvair/plates.py @@ -94,15 +94,17 @@ def Porvair_6_reservoir_47ml_Vb(name: str, with_lid: bool = False) -> Plate: #: Porvair_6_reservoir_47ml_Vb_L def Porvair_6_reservoir_47ml_Vb_L(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Porvair_6_reservoir_47ml_Vb instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Porvair_6_reservoir_47ml_Vb instead." + ) #: Porvair_6_reservoir_47ml_Vb_P def Porvair_6_reservoir_47ml_Vb_P(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Porvair_6_reservoir_47ml_Vb.rotated(90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Porvair_6_reservoir_47ml_Vb.rotated(90) instead." + ) def Porvair_24_wellplate_Vb(name: str, lid: Optional[Lid] = None) -> Plate: diff --git a/pylabrobot/resources/thermo_fisher/plates.py b/pylabrobot/resources/thermo_fisher/plates.py index db4d2b9e24..9453d1a3ea 100644 --- a/pylabrobot/resources/thermo_fisher/plates.py +++ b/pylabrobot/resources/thermo_fisher/plates.py @@ -120,13 +120,17 @@ def Thermo_TS_96_wellplate_1200ul_Rb(name: str, with_lid: bool = False) -> Plate def Thermo_TS_96_wellplate_1200ul_Rb_L(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Thermo_TS_96_wellplate_1200ul_Rb instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " "Thermo_TS_96_wellplate_1200ul_Rb instead." + ) + def Thermo_TS_96_wellplate_1200ul_Rb_P(name: str, with_lid: bool = False) -> Plate: # https://github.com/PyLabRobot/pylabrobot/issues/252 - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Thermo_TS_96_wellplate_1200ul_Rb().rotated(z=90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " + "Thermo_TS_96_wellplate_1200ul_Rb().rotated(z=90) instead." + ) # # # # # # # # # # Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate # # # # # # # # # # @@ -229,10 +233,14 @@ def Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate(name: str, with_lid: bool = Fals def Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate_L(name: str, with_lid: bool = False) -> Plate: - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " + "Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate instead." + ) def Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate_P(name: str, with_lid: bool = False) -> Plate: - raise NotImplementedError("_L and _P definitions are deprecated. Use " - "Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate.rotated(90) instead.") + raise NotImplementedError( + "_L and _P definitions are deprecated. Use " + "Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate.rotated(90) instead." + ) From 878395e9492888b408092dfd7bf43a46e0d01580 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 22 Oct 2024 16:10:38 -0700 Subject: [PATCH 3/8] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e8ca53105..87a35d3799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - `ThermoScientific_96_DWP_1200ul_Rd` in favor of `Thermo_TS_96_wellplate_1200ul_Rb` (https://github.com/PyLabRobot/pylabrobot/pull/215) - `Azenta4titudeFrameStar_96_wellplate_skirted` in favor of `Azenta4titudeFrameStar_96_wellplate_200ul_Vb` (https://github.com/PyLabRobot/pylabrobot/pull/205/) - `Cos_96_DWP_2mL_Vb` in favor of `Cos_96_wellplate_2mL_Vb (https://github.com/PyLabRobot/pylabrobot/pull/205/)` +- Resource definitions with `_L` and `_P`, it is easy enough to use the stem and `.rotated(z=90)` for `_P` (https://github.com/PyLabRobot/pylabrobot/pull/288) ### Fixed From 52b6411d0873540c8806a921b8ac5921291ea347 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 22 Oct 2024 16:18:07 -0700 Subject: [PATCH 4/8] tests and cleanup --- CHANGELOG.md | 4 +- docs/user_guide/basic.ipynb | 844 +++++----- docs/user_guide/moving-channels-around.ipynb | 384 ++--- docs/user_guide/using-the-visualizer.ipynb | 1376 ++++++++--------- docs/user_guide/using-trackers.ipynb | 8 +- .../backends/chatterbox_tests.py | 4 +- .../backends/hamilton/STAR_tests.py | 14 +- .../backends/hamilton/vantage_tests.py | 8 +- .../liquid_handling/backends/http_tests.py | 4 +- .../backends/serializing_backend_tests.py | 4 +- .../liquid_handling/liquid_handler_tests.py | 24 +- pylabrobot/resources/carrier.py | 4 +- .../resources/hamilton/hamilton_deck_tests.py | 10 +- .../resources/hamilton/hamilton_decks.py | 2 +- pylabrobot/resources/ml_star/tip_racks.py | 32 +- pylabrobot/resources/tecan/tecan_decks.py | 12 +- .../server/liquid_handling_api_tests.py | 4 +- 17 files changed, 1367 insertions(+), 1371 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87a35d3799..115963aae4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -59,14 +59,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - `Thermo_TS_96_wellplate_1200ul_Rb` (https://github.com/PyLabRobot/pylabrobot/pull/215) - `Thermo_AB_96_wellplate_300ul_Vb_EnduraPlate` (https://github.com/PyLabRobot/pylabrobot/pull/215) - `adapter_hole_size_z` and `plate_z_offset` parameters to `PlateAdapter` (https://github.com/PyLabRobot/pylabrobot/pull/215) -- `wide_high_volume_tip_with_filter` and `HTF_L_WIDE` (https://github.com/PyLabRobot/pylabrobot/pull/222) +- `wide_high_volume_tip_with_filter` and `HTF_WIDE` (https://github.com/PyLabRobot/pylabrobot/pull/222) - Serialize code cells and closures (https://github.com/PyLabRobot/pylabrobot/pull/220) - `Container.get_anchor()` now supports `"cavity_bottom"` as an argument for `z` (https://github.com/PyLabRobot/pylabrobot/pull/205/) - `pylabrobot.resources.utils.query` for basic querying (https://github.com/PyLabRobot/pylabrobot/commit/4a07f6a32a9a33d0370eb9c29015567c98aea002) - `HamiltonLiquidHandler.allow_firmware_planning` to allow STAR/Vantage to plan complex liquid handling operations automatically (may break hardware agnosticity unexpectedly) (https://github.com/PyLabRobot/pylabrobot/pull/224) - `size_z` and `nesting_z_height` for `Cor_96_wellplate_360ul_Fb_Lid` (https://github.com/PyLabRobot/pylabrobot/pull/226) - `NestedTipRack` (https://github.com/PyLabRobot/pylabrobot/pull/228) -- `HTF_L_ULTRAWIDE`, `ultrawide_high_volume_tip_with_filter` (https://github.com/PyLabRobot/pylabrobot/pull/229/) +- `HTF_ULTRAWIDE`, `ultrawide_high_volume_tip_with_filter` (https://github.com/PyLabRobot/pylabrobot/pull/229/) - `get_absolute_size_x`, `get_absolute_size_y`, `get_absolute_size_z` for `Resource` (https://github.com/PyLabRobot/pylabrobot/pull/235) - `Cytation5Backend` for plate reading on BioTek Cytation 5 (https://github.com/PyLabRobot/pylabrobot/pull/238) - imaging (https://github.com/PyLabRobot/pylabrobot/pull/277) diff --git a/docs/user_guide/basic.ipynb b/docs/user_guide/basic.ipynb index dcde4fd2fe..2e5b614ec5 100644 --- a/docs/user_guide/basic.ipynb +++ b/docs/user_guide/basic.ipynb @@ -1,426 +1,426 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Getting started with liquid handling on a Hamilton STAR(let)\n", - "\n", - "In this notebook, you will learn how to use PyLabRobot to move water from one range of wells to another.\n", - "\n", - "**Note: before running this notebook, you should have**:\n", - "\n", - "- Installed PyLabRobot and the USB driver as described in [the installation guide](/user_guide/installation).\n", - "- Connected the Hamilton to your computer using the USB cable.\n", - "\n", - "Video of what this code does:\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setting up a connection with the robot\n", - "\n", - "Start by importing the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` class, which will serve as a front end for all liquid handling operations.\n", - "\n", - "Backends serve as communicators between `LiquidHandler`s and the actual hardware. Since we are using a Hamilton STAR, we also import the {class}`~pylabrobot.liquid_handling.backends.STAR` backend." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import STAR" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In addition, import the {class}`~pylabrobot.resources.hamilton.STARLetDeck`, which represents the deck of the Hamilton STAR." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources.hamilton import STARLetDeck" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a new liquid handler using `STAR` as its backend." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "backend = STAR()\n", - "lh = LiquidHandler(backend=backend, deck=STARLetDeck())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final step is to open communication with the robot. This is done using the {func}`~pylabrobot.liquid_handling.LiquidHandler.setup` method." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "await lh.setup()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating the deck layout\n", - "\n", - "Now that we have a `LiquidHandler` instance, we can define the deck layout.\n", - "\n", - "The layout in this tutorial will contain five sets of standard volume tips with filter, 1 set of 96 1mL wells, and tip and plate carriers on which these resources are positioned.\n", - "\n", - "Start by importing the relevant objects and variables from the PyLabRobot package. This notebook uses the following resources:\n", - "\n", - "- {class}`~pylabrobot.resources.ml_star.tip_carriers.TIP_CAR_480_A00` tip carrier\n", - "- {class}`~pylabrobot.resources.ml_star.plate_carriers.PLT_CAR_L5AC_A00` plate carrier\n", - "- {class}`~pylabrobot.resources.corning_costar.plates.Cor_96_wellplate_360ul_Fb` wells\n", - "- {class}`~pylabrobot.resources.ml_star.tip_racks.HTF_L` tips" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cor_96_wellplate_360ul_Fb,\n", - " HTF_L,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then create a tip carrier named `tip carrier`, which will contain tip rack at all 5 positions. These positions can be accessed using `tip_car[x]`, and are 0 indexed." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name=\"tip carrier\")\n", - "tip_car[0] = HTF_L(name=\"tips_01\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use {func}`~pylabrobot.resources.abstract.assign_child_resources` to assign the tip carrier to the deck of the liquid handler. All resources contained by this carrier will be assigned automatically.\n", - "\n", - "In the `rails` parameter, we can pass the location of the tip carrier. The locations of the tips will automatically be calculated." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "lh.deck.assign_child_resource(tip_car, rails=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Repeat this for the plates." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name=\"plate carrier\")\n", - "plt_car[0] = Cor_96_wellplate_360ul_Fb(name=\"plate_01\")" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "lh.deck.assign_child_resource(plt_car, rails=15)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's look at a summary of the deck layout using {func}`~pylabrobot.liquid_handling.LiquidHandler.summary`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rail Resource Type Coordinates (mm)\n", - "===============================================================================================\n", - "(3) ├── tip carrier TipCarrier (145.000, 063.000, 100.000)\n", - " │ ├── tips_01 TipRack (162.900, 145.800, 131.450)\n", - " │ ├── \n", - " │ ├── \n", - " │ ├── \n", - " │ ├── \n", - " │\n", - "(15) ├── plate carrier PlateCarrier (415.000, 063.000, 100.000)\n", - " │ ├── plate_01 Plate (433.000, 146.000, 187.150)\n", - " │ ├── \n", - " │ ├── \n", - " │ ├── \n", - " │ ├── \n", - " │\n", - "(32) ├── trash Trash (800.000, 190.600, 137.100)\n", - "\n" - ] - } - ], - "source": [ - "lh.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Picking up tips\n", - "\n", - "Picking up tips is as easy as querying the tips from the tiprack." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TTid0004tt01tf1tl0871tv12500tg3tu0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TTid0004er00/00\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TPid0005xp01629 01629 01629 00000&yp1458 1368 1278 0000&tm1 1 1 0&tt01tp2244tz2164th2450td0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TPid0005er00/00\n" - ] - } - ], - "source": [ - "tiprack = lh.deck.get_resource(\"tips_01\")\n", - "await lh.pick_up_tips(tiprack[\"A1:C1\"])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Aspirating and dispensing" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Aspirating and dispensing work similarly to picking up tips: where you use booleans to specify which tips to pick up, with aspiration and dispensing you use floats to specify the volume to aspirate or dispense in $\\mu L$.\n", - "\n", - "The cells below move liquid from wells `'A1:C1'` to `'D1:F1'` using channels 1, 2, and 3 using the {func}`~pylabrobot.liquid_handling.LiquidHandler.aspirate` and {func}`~pylabrobot.liquid_handling.LiquidHandler.dispense` methods." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0ASid0006at0&tm1 1 1 0&xp04330 04330 04330 00000&yp1460 1370 1280 0000&th2450te2450lp1931 1931 1931&ch000 000 000&zl1881 1881 1881&po0100 0100 0100&zu0032 0032 0032&zr06180 06180 06180&zx1831 1831 1831&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&av01072 00551 02110&as1000 1000 1000&ta000 000 000&ba0000 0000 0000&oa000 000 000&lm0 0 0&ll1 1 1&lv1 1 1&zo000 000 000&ld00 00 00&de0020 0020 0020&wt10 10 10&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms1000 1000 1000&mh0000 0000 0000&gi000 000 000&gj0gk0lk0 0 0&ik0000 0000 0000&sd0500 0500 0500&se0500 0500 0500&sz0300 0300 0300&io0000 0000 0000&il00000 00000 00000&in0000 0000 0000&\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0ASid0006er00/00\n" - ] - } - ], - "source": [ - "plate = lh.deck.get_resource(\"plate_01\")\n", - "await lh.aspirate(plate[\"A1:C1\"], vols=[100.0, 50.0, 200.0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After the liquid has been aspirated, dispense it in the wells below. Note that while we specify different wells, we are still using the same channels. This is needed because only these channels contain liquid, of course." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0DSid0007dm2 2 2&tm1 1 1 0&xp04330 04330 04330 00000&yp1190 1100 1010 0000&zx1871 1871 1871&lp2321 2321 2321&zl1881 1881 1881&po0100 0100 0100&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&zu0032 0032 0032&zr06180 06180 06180&th2450te2450dv01072 00551 02110&ds1200 1200 1200&ss0050 0050 0050&rv000 000 000&ta000 000 000&ba0000 0000 0000&lm0 0 0&dj00zo000 000 000&ll1 1 1&lv1 1 1&de0020 0020 0020&wt00 00 00&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms0010 0010 0010&mh0000 0000 0000&gi000 000 000&gj0gk0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0DSid0007er00/00\n" - ] - } - ], - "source": [ - "await lh.dispense(plate[\"D1:F1\"], vols=[100.0, 50.0, 200.0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's move the liquid back to the original wells." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0ASid0008at0&tm1 1 1 0&xp04330 04330 04330 00000&yp1190 1100 1010 0000&th2450te2450lp1931 1931 1931&ch000 000 000&zl1881 1881 1881&po0100 0100 0100&zu0032 0032 0032&zr06180 06180 06180&zx1831 1831 1831&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&av01072 00551 02110&as1000 1000 1000&ta000 000 000&ba0000 0000 0000&oa000 000 000&lm0 0 0&ll1 1 1&lv1 1 1&zo000 000 000&ld00 00 00&de0020 0020 0020&wt10 10 10&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms1000 1000 1000&mh0000 0000 0000&gi000 000 000&gj0gk0lk0 0 0&ik0000 0000 0000&sd0500 0500 0500&se0500 0500 0500&sz0300 0300 0300&io0000 0000 0000&il00000 00000 00000&in0000 0000 0000&\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0ASid0008er00/00\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0DSid0009dm2 2 2&tm1 1 1 0&xp04330 04330 04330 00000&yp1460 1370 1280 0000&zx1871 1871 1871&lp2321 2321 2321&zl1881 1881 1881&po0100 0100 0100&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&zu0032 0032 0032&zr06180 06180 06180&th2450te2450dv01072 00551 02110&ds1200 1200 1200&ss0050 0050 0050&rv000 000 000&ta000 000 000&ba0000 0000 0000&lm0 0 0&dj00zo000 000 000&ll1 1 1&lv1 1 1&de0020 0020 0020&wt00 00 00&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms0010 0010 0010&mh0000 0000 0000&gi000 000 000&gj0gk0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0DSid0009er00/00\n" - ] - } - ], - "source": [ - "await lh.aspirate(plate[\"D1:F1\"], vols=[100.0, 50.0, 200.0])\n", - "await lh.dispense(plate[\"A1:C1\"], vols=[100.0, 50.0, 200.0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dropping tips\n", - "\n", - "Finally, you can drop tips anywhere on the deck by using the {func}`~pylabrobot.liquid_handling.LiquidHandler.discard_tips` method." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TRid0010xp01629 01629 01629 00000&yp1458 1368 1278 0000&tm1 1 1 0&tt01tp1314tz1414th2450ti0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TRid0010er00/00kz381 356 365 000 000 000 000 000vz303 360 368 000 000 000 000 000\n" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Getting started with liquid handling on a Hamilton STAR(let)\n", + "\n", + "In this notebook, you will learn how to use PyLabRobot to move water from one range of wells to another.\n", + "\n", + "**Note: before running this notebook, you should have**:\n", + "\n", + "- Installed PyLabRobot and the USB driver as described in [the installation guide](/user_guide/installation).\n", + "- Connected the Hamilton to your computer using the USB cable.\n", + "\n", + "Video of what this code does:\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting up a connection with the robot\n", + "\n", + "Start by importing the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` class, which will serve as a front end for all liquid handling operations.\n", + "\n", + "Backends serve as communicators between `LiquidHandler`s and the actual hardware. Since we are using a Hamilton STAR, we also import the {class}`~pylabrobot.liquid_handling.backends.STAR` backend." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import STAR" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition, import the {class}`~pylabrobot.resources.hamilton.STARLetDeck`, which represents the deck of the Hamilton STAR." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources.hamilton import STARLetDeck" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a new liquid handler using `STAR` as its backend." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "backend = STAR()\n", + "lh = LiquidHandler(backend=backend, deck=STARLetDeck())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The final step is to open communication with the robot. This is done using the {func}`~pylabrobot.liquid_handling.LiquidHandler.setup` method." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "await lh.setup()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating the deck layout\n", + "\n", + "Now that we have a `LiquidHandler` instance, we can define the deck layout.\n", + "\n", + "The layout in this tutorial will contain five sets of standard volume tips with filter, 1 set of 96 1mL wells, and tip and plate carriers on which these resources are positioned.\n", + "\n", + "Start by importing the relevant objects and variables from the PyLabRobot package. This notebook uses the following resources:\n", + "\n", + "- {class}`~pylabrobot.resources.ml_star.tip_carriers.TIP_CAR_480_A00` tip carrier\n", + "- {class}`~pylabrobot.resources.ml_star.plate_carriers.PLT_CAR_L5AC_A00` plate carrier\n", + "- {class}`~pylabrobot.resources.corning_costar.plates.Cor_96_wellplate_360ul_Fb` wells\n", + "- {class}`~pylabrobot.resources.ml_star.tip_racks.HTF` tips" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cor_96_wellplate_360ul_Fb,\n", + " HTF,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then create a tip carrier named `tip carrier`, which will contain tip rack at all 5 positions. These positions can be accessed using `tip_car[x]`, and are 0 indexed." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name=\"tip carrier\")\n", + "tip_car[0] = HTF(name=\"tips_01\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use {func}`~pylabrobot.resources.abstract.assign_child_resources` to assign the tip carrier to the deck of the liquid handler. All resources contained by this carrier will be assigned automatically.\n", + "\n", + "In the `rails` parameter, we can pass the location of the tip carrier. The locations of the tips will automatically be calculated." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "lh.deck.assign_child_resource(tip_car, rails=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Repeat this for the plates." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name=\"plate carrier\")\n", + "plt_car[0] = Cor_96_wellplate_360ul_Fb(name=\"plate_01\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "lh.deck.assign_child_resource(plt_car, rails=15)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's look at a summary of the deck layout using {func}`~pylabrobot.liquid_handling.LiquidHandler.summary`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rail Resource Type Coordinates (mm)\n", + "===============================================================================================\n", + "(3) ├── tip carrier TipCarrier (145.000, 063.000, 100.000)\n", + " │ ├── tips_01 TipRack (162.900, 145.800, 131.450)\n", + " │ ├── \n", + " │ ├── \n", + " │ ├── \n", + " │ ├── \n", + " │\n", + "(15) ├── plate carrier PlateCarrier (415.000, 063.000, 100.000)\n", + " │ ├── plate_01 Plate (433.000, 146.000, 187.150)\n", + " │ ├── \n", + " │ ├── \n", + " │ ├── \n", + " │ ├── \n", + " │\n", + "(32) ├── trash Trash (800.000, 190.600, 137.100)\n", + "\n" + ] + } + ], + "source": [ + "lh.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Picking up tips\n", + "\n", + "Picking up tips is as easy as querying the tips from the tiprack." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TTid0004tt01tf1tl0871tv12500tg3tu0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TTid0004er00/00\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TPid0005xp01629 01629 01629 00000&yp1458 1368 1278 0000&tm1 1 1 0&tt01tp2244tz2164th2450td0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TPid0005er00/00\n" + ] + } + ], + "source": [ + "tiprack = lh.deck.get_resource(\"tips_01\")\n", + "await lh.pick_up_tips(tiprack[\"A1:C1\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aspirating and dispensing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aspirating and dispensing work similarly to picking up tips: where you use booleans to specify which tips to pick up, with aspiration and dispensing you use floats to specify the volume to aspirate or dispense in $\\mu L$.\n", + "\n", + "The cells below move liquid from wells `'A1:C1'` to `'D1:F1'` using channels 1, 2, and 3 using the {func}`~pylabrobot.liquid_handling.LiquidHandler.aspirate` and {func}`~pylabrobot.liquid_handling.LiquidHandler.dispense` methods." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0ASid0006at0&tm1 1 1 0&xp04330 04330 04330 00000&yp1460 1370 1280 0000&th2450te2450lp1931 1931 1931&ch000 000 000&zl1881 1881 1881&po0100 0100 0100&zu0032 0032 0032&zr06180 06180 06180&zx1831 1831 1831&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&av01072 00551 02110&as1000 1000 1000&ta000 000 000&ba0000 0000 0000&oa000 000 000&lm0 0 0&ll1 1 1&lv1 1 1&zo000 000 000&ld00 00 00&de0020 0020 0020&wt10 10 10&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms1000 1000 1000&mh0000 0000 0000&gi000 000 000&gj0gk0lk0 0 0&ik0000 0000 0000&sd0500 0500 0500&se0500 0500 0500&sz0300 0300 0300&io0000 0000 0000&il00000 00000 00000&in0000 0000 0000&\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0ASid0006er00/00\n" + ] + } + ], + "source": [ + "plate = lh.deck.get_resource(\"plate_01\")\n", + "await lh.aspirate(plate[\"A1:C1\"], vols=[100.0, 50.0, 200.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After the liquid has been aspirated, dispense it in the wells below. Note that while we specify different wells, we are still using the same channels. This is needed because only these channels contain liquid, of course." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0DSid0007dm2 2 2&tm1 1 1 0&xp04330 04330 04330 00000&yp1190 1100 1010 0000&zx1871 1871 1871&lp2321 2321 2321&zl1881 1881 1881&po0100 0100 0100&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&zu0032 0032 0032&zr06180 06180 06180&th2450te2450dv01072 00551 02110&ds1200 1200 1200&ss0050 0050 0050&rv000 000 000&ta000 000 000&ba0000 0000 0000&lm0 0 0&dj00zo000 000 000&ll1 1 1&lv1 1 1&de0020 0020 0020&wt00 00 00&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms0010 0010 0010&mh0000 0000 0000&gi000 000 000&gj0gk0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0DSid0007er00/00\n" + ] + } + ], + "source": [ + "await lh.dispense(plate[\"D1:F1\"], vols=[100.0, 50.0, 200.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's move the liquid back to the original wells." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0ASid0008at0&tm1 1 1 0&xp04330 04330 04330 00000&yp1190 1100 1010 0000&th2450te2450lp1931 1931 1931&ch000 000 000&zl1881 1881 1881&po0100 0100 0100&zu0032 0032 0032&zr06180 06180 06180&zx1831 1831 1831&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&av01072 00551 02110&as1000 1000 1000&ta000 000 000&ba0000 0000 0000&oa000 000 000&lm0 0 0&ll1 1 1&lv1 1 1&zo000 000 000&ld00 00 00&de0020 0020 0020&wt10 10 10&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms1000 1000 1000&mh0000 0000 0000&gi000 000 000&gj0gk0lk0 0 0&ik0000 0000 0000&sd0500 0500 0500&se0500 0500 0500&sz0300 0300 0300&io0000 0000 0000&il00000 00000 00000&in0000 0000 0000&\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0ASid0008er00/00\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0DSid0009dm2 2 2&tm1 1 1 0&xp04330 04330 04330 00000&yp1460 1370 1280 0000&zx1871 1871 1871&lp2321 2321 2321&zl1881 1881 1881&po0100 0100 0100&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&zu0032 0032 0032&zr06180 06180 06180&th2450te2450dv01072 00551 02110&ds1200 1200 1200&ss0050 0050 0050&rv000 000 000&ta000 000 000&ba0000 0000 0000&lm0 0 0&dj00zo000 000 000&ll1 1 1&lv1 1 1&de0020 0020 0020&wt00 00 00&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms0010 0010 0010&mh0000 0000 0000&gi000 000 000&gj0gk0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0DSid0009er00/00\n" + ] + } + ], + "source": [ + "await lh.aspirate(plate[\"D1:F1\"], vols=[100.0, 50.0, 200.0])\n", + "await lh.dispense(plate[\"A1:C1\"], vols=[100.0, 50.0, 200.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dropping tips\n", + "\n", + "Finally, you can drop tips anywhere on the deck by using the {func}`~pylabrobot.liquid_handling.LiquidHandler.discard_tips` method." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TRid0010xp01629 01629 01629 00000&yp1458 1368 1278 0000&tm1 1 1 0&tt01tp1314tz1414th2450ti0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TRid0010er00/00kz381 356 365 000 000 000 000 000vz303 360 368 000 000 000 000 000\n" + ] + } + ], + "source": [ + "await lh.drop_tips(tiprack[\"A1:C1\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Closing connection to USB device.\n" + ] + } + ], + "source": [ + "await lh.stop()" + ] } - ], - "source": [ - "await lh.drop_tips(tiprack[\"A1:C1\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:root:Closing connection to USB device.\n" - ] + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.9.13 ('env': venv)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" + }, + "vscode": { + "interpreter": { + "hash": "bf274dfc1b974177267b6b8fba8543eeb0bb4c5d64c637dde420829b05625268" + } } - ], - "source": [ - "await lh.stop()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.13 ('env': venv)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" }, - "vscode": { - "interpreter": { - "hash": "bf274dfc1b974177267b6b8fba8543eeb0bb4c5d64c637dde420829b05625268" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/docs/user_guide/moving-channels-around.ipynb b/docs/user_guide/moving-channels-around.ipynb index dec7ca2866..8cc7f61a57 100644 --- a/docs/user_guide/moving-channels-around.ipynb +++ b/docs/user_guide/moving-channels-around.ipynb @@ -1,194 +1,194 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Manually moving channels around\n", - "\n", - "![star supported](https://img.shields.io/badge/STAR-supported-blue)\n", - "![Vantage supported](https://img.shields.io/badge/Vantage-supported-blue)\n", - "![OT supported](https://img.shields.io/badge/OT-supported-blue)\n", - "![EVO not tested](https://img.shields.io/badge/EVO-not%20tested-orange)\n", - "\n", - "With PLR, you can easily move individual channels around. This is useful for calibrating labware locations, calibrating labware sizes, and other things.\n", - "\n", - "```{warning}\n", - "Be very careful about collisions! Move channels to a safe z height before traversing.\n", - "```\n", - "\n", - "```{note}\n", - "With Hamilton robots, when a tip is mounted, the z location will refer to the point of the pipetting tip. With no tip mounted, it will refer to the bottom of the channel.\n", - "```" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Manually moving channels around\n", + "\n", + "![star supported](https://img.shields.io/badge/STAR-supported-blue)\n", + "![Vantage supported](https://img.shields.io/badge/Vantage-supported-blue)\n", + "![OT supported](https://img.shields.io/badge/OT-supported-blue)\n", + "![EVO not tested](https://img.shields.io/badge/EVO-not%20tested-orange)\n", + "\n", + "With PLR, you can easily move individual channels around. This is useful for calibrating labware locations, calibrating labware sizes, and other things.\n", + "\n", + "```{warning}\n", + "Be very careful about collisions! Move channels to a safe z height before traversing.\n", + "```\n", + "\n", + "```{note}\n", + "With Hamilton robots, when a tip is mounted, the z location will refer to the point of the pipetting tip. With no tip mounted, it will refer to the bottom of the channel.\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example: Hamilton STAR\n", + "\n", + "Here, we'll use a Hamilton STAR as an example. For other robots, simply change the deck layout, makign sure that you have at least a tip rack to use." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler, STAR\n", + "from pylabrobot.resources import STARDeck, TIP_CAR_480_A00, HTF\n", + "\n", + "lh = LiquidHandler(backend=STAR(), deck=STARDeck())\n", + "await lh.setup()\n", + "\n", + "# assign a tip rack\n", + "tip_carrier = TIP_CAR_480_A00(name=\"tip_carrier\")\n", + "tip_carrier[0] = tip_rack = HTF(name=\"tip_rack\")\n", + "lh.deck.assign_child_resource(tip_carrier, rails=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Moving channels\n", + "\n", + "All positions are in mm. The movements are to absolute positions. The origin will be near the left front bottom of the deck, but it differs between robots.\n", + "\n", + "* x: left (low) to right (high)\n", + "* y: front (low) to back (high)\n", + "* z: bottom (low) to top (high)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "channel = 1 # the channel to use\n", + "\n", + "# start by picking up a single tip\n", + "await lh.pick_up_tips(tip_rack[\"A1\"], use_channels=[channel])\n", + "\n", + "# prepare for manual operation\n", + "# this will space the other channels to safe positions\n", + "await lh.prepare_for_manual_channel_operation(channel)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since the channnel will be above the tip rack, it should be safe to move up. We perform a quick check to make sure the z_safe is at least above the resources we know about." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "z_safe = 240 # WARNING: this might NOT be safe for your setup\n", + "\n", + "if z_safe <= lh.deck.get_heighest_known_point():\n", + " raise ValueError(f\"z_safe position is not safe, it is lower than the highest known point: {lh.deck.get_heighest_known_point()}\")\n", + "\n", + "await lh.move_channel_z(channel, z_safe)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{warning}\n", + "The z position in the code above should be safe for most setups, but we can't guarantee that it will be safe for all setups. Move to a z position that is above all your labware before moving in the xy plane.\n", + "```\n", + "\n", + "When the z position of the bottom of the tip is above the labware, you can move the channel around in the xy plane." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# move the channel around\n", + "await lh.move_channel_x(channel, 100)\n", + "await lh.move_channel_y(channel, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After reaching a spot where the channel can move down, you can use `move_channel_z` again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "await lh.move_channel_z(channel, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before returning the tip to the tip rack, make sure to move the channel to a safe z position again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "await lh.move_channel_z(channel, z_safe)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can run the code above as often as you like. When you're done, you can return the channel to the tip rack." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "await lh.return_tips()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.2" + } }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example: Hamilton STAR\n", - "\n", - "Here, we'll use a Hamilton STAR as an example. For other robots, simply change the deck layout, makign sure that you have at least a tip rack to use." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler, STAR\n", - "from pylabrobot.resources import STARDeck, TIP_CAR_480_A00, HTF_L\n", - "\n", - "lh = LiquidHandler(backend=STAR(), deck=STARDeck())\n", - "await lh.setup()\n", - "\n", - "# assign a tip rack\n", - "tip_carrier = TIP_CAR_480_A00(name=\"tip_carrier\")\n", - "tip_carrier[0] = tip_rack = HTF_L(name=\"tip_rack\")\n", - "lh.deck.assign_child_resource(tip_carrier, rails=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Moving channels\n", - "\n", - "All positions are in mm. The movements are to absolute positions. The origin will be near the left front bottom of the deck, but it differs between robots.\n", - "\n", - "* x: left (low) to right (high)\n", - "* y: front (low) to back (high)\n", - "* z: bottom (low) to top (high)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "channel = 1 # the channel to use\n", - "\n", - "# start by picking up a single tip\n", - "await lh.pick_up_tips(tip_rack[\"A1\"], use_channels=[channel])\n", - "\n", - "# prepare for manual operation\n", - "# this will space the other channels to safe positions\n", - "await lh.prepare_for_manual_channel_operation(channel)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since the channnel will be above the tip rack, it should be safe to move up. We perform a quick check to make sure the z_safe is at least above the resources we know about." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "z_safe = 240 # WARNING: this might NOT be safe for your setup\n", - "\n", - "if z_safe <= lh.deck.get_heighest_known_point():\n", - " raise ValueError(f\"z_safe position is not safe, it is lower than the highest known point: {lh.deck.get_heighest_known_point()}\")\n", - "\n", - "await lh.move_channel_z(channel, z_safe)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```{warning}\n", - "The z position in the code above should be safe for most setups, but we can't guarantee that it will be safe for all setups. Move to a z position that is above all your labware before moving in the xy plane.\n", - "```\n", - "\n", - "When the z position of the bottom of the tip is above the labware, you can move the channel around in the xy plane." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# move the channel around\n", - "await lh.move_channel_x(channel, 100)\n", - "await lh.move_channel_y(channel, 100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After reaching a spot where the channel can move down, you can use `move_channel_z` again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "await lh.move_channel_z(channel, 100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before returning the tip to the tip rack, make sure to move the channel to a safe z position again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "await lh.move_channel_z(channel, z_safe)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can run the code above as often as you like. When you're done, you can return the channel to the tip rack." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "await lh.return_tips()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "env", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.2" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} + "nbformat": 4, + "nbformat_minor": 2 +} \ No newline at end of file diff --git a/docs/user_guide/using-the-visualizer.ipynb b/docs/user_guide/using-the-visualizer.ipynb index ec35050db4..d83462abd1 100644 --- a/docs/user_guide/using-the-visualizer.ipynb +++ b/docs/user_guide/using-the-visualizer.ipynb @@ -1,692 +1,692 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "c3c71913", - "metadata": {}, - "source": [ - "# Using the Visualizer\n", - "\n", - "The Visualizer is a tool that allows you to visualize the a Resource (like LiquidHandler) including its state to easily see what is going on, for example when executing a protocol on a robot or when developing a new protocol.\n", - "\n", - "When using a backend that does not require access to a physical robot, such as the {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend`, the Visualizer can be used to simulate a robot's behavior. Of course, you may also use the Visualizer when working with a real robot to see what is happening in the PLR resource and state trackers." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "d6cdca52", - "metadata": {}, - "source": [ - "## Setting up a connection with the robot\n", - "\n", - "As described in the [basic liquid handling tutorial](basic), we will use the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` class to control the robot. This time, however, instead of using the Hamilton {class}`~pylabrobot.liquid_handling.backends.hamilton.STAR.STAR` backend, we are using the software-only {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend` backend. This means that liquid handling will work exactly the same, but commands are simply printed out to the console instead of being sent to a physical robot. We are still using the same deck." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "7c5a2629", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import ChatterboxBackend\n", - "from pylabrobot.visualizer.visualizer import Visualizer" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "272520d3", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources.hamilton import STARLetDeck" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "2e280caa", - "metadata": {}, - "outputs": [], - "source": [ - "lh = LiquidHandler(backend=ChatterboxBackend(), deck=STARLetDeck())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "30dcf4a1", - "metadata": {}, - "source": [ - "Calling {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.setup` will print out \"Setting up the robot\" and also that two resources were assigned: the deck and the trash. Other than that, the chatter box backend has no setup to do." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "1419f2b3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Setting up the robot.\n", - "Resource deck was assigned to the robot.\n", - "Resource trash was assigned to the robot.\n", - "Resource trash_core96 was assigned to the robot.\n" - ] - } - ], - "source": [ - "await lh.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "871184ce", - "metadata": {}, - "source": [ - "Next, we will create a {class}`~pylabrobot.visualizer.visualizer.Visualizer` object. The Visualizer expects a Resource, and we will pass the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` object to it. This will allow us to visualize the robot's state and actions." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "165c9de4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Websocket server started at http://127.0.0.1:2121\n", - "File server started at http://127.0.0.1:1337 . Open this URL in your browser.\n" - ] - } - ], - "source": [ - "vis = Visualizer(resource=lh)\n", - "await vis.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "3002429b", - "metadata": {}, - "source": [ - "![The empty simulator](./img/visualizer/empty.png)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "050ccefd", - "metadata": {}, - "source": [ - "## Build the deck layout: Assigning plates and tips\n", - "\n", - "When resources are assigned to the root resource of the Visualizer, in this case `lh`, they will automatically appear in the visualization." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "c8c464c8", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cor_96_wellplate_360ul_Fb,\n", - " HTF_L\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b14e3628", - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "140872be", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Resource tip carrier was assigned to the robot.\n" - ] - } - ], - "source": [ - "lh.deck.assign_child_resource(tip_car, rails=15)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "13cbc612", - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", - "plt_car[0] = plate_1 = Cor_96_wellplate_360ul_Fb(name='plate_01')\n", - "plt_car[1] = plate_2 = Cor_96_wellplate_360ul_Fb(name='plate_02')\n", - "plt_car[2] = plate_3 = Cor_96_wellplate_360ul_Fb(name='plate_03')" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "d618ec6a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Resource plate carrier was assigned to the robot.\n" - ] - } - ], - "source": [ - "lh.deck.assign_child_resource(plt_car, rails=8)" - ] - }, - { - "cell_type": "markdown", - "id": "21835f31", - "metadata": {}, - "source": [ - "![The simulator after the resources have been assigned](./img/visualizer/assignment.png)" - ] - }, - { - "cell_type": "markdown", - "id": "68a9721b", - "metadata": {}, - "source": [ - "### Configuring the state of the deck\n", - "\n", - "As with every PyLabRobot script, you have the option of updating the state of the deck before you actually start your method. This will allow PyLabRobot to keep track of what is going on, enabling features like {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.return_tips` and catching errors (like missed tips) before a command would be executed on the robot. With the visualizer, this state has the additional effect of updating the visualization." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "daf99520", - "metadata": {}, - "source": [ - "### Tips\n", - "\n", - "Let's use {func}`~pylabrobot.resources.tip_rack.fill` to place tips at all spots in the tip rack in location `0`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "ca3152d0", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "tip_rack1.fill()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "26f036c6", - "metadata": {}, - "source": [ - "\n", - "You can precisely control the presence of tips using {func}`~pylabrobot.resources.tip_rack.set_tip_state`. This function allows you to set whether there is a tip in each {class}`~pylabrobot.resources.tip_rack.TipSpot`." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "8f574b8d", - "metadata": {}, - "outputs": [], - "source": [ - "tip_rack4 = lh.deck.get_resource(\"tips_04\")\n", - "tip_rack4.set_tip_state([[True]*6 + [False]*6]*8)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "8e1ed1e6", - "metadata": {}, - "outputs": [], - "source": [ - "tip_rack3.set_tip_state([[True, False]*6]*8)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "890272f1", - "metadata": {}, - "outputs": [], - "source": [ - "tip_rack2.set_tip_state([[True, True, False, False]*3]*8)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "594d8016", - "metadata": {}, - "source": [ - "### Liquids\n", - "\n", - "Adding liquid to wells works similarly. You can use {func}`~pylabrobot.resources.plate.set_well_liquids` to set the liquid in each well of a plate. Each liquid is represented by a tuple where the first element corresponds to the type of liquid and the second to the volume in uL. Here, `None` is used to designate an unknown liquid." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "5b76aac2", - "metadata": {}, - "outputs": [], - "source": [ - "plate_1_liquids = [[(None, 500)]]*96\n", - "plate_1.set_well_liquids(plate_1_liquids)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "400208c7", - "metadata": {}, - "outputs": [], - "source": [ - "plate_2_liquids = [[(None, 100)], [(None, 500)]]*(96//2)\n", - "plate_2.set_well_liquids(plate_2_liquids)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "66281459", - "metadata": {}, - "source": [ - "In the visualizer, you can see that the opacity of the well is proportional to how full the well is relative to its maximum volume." - ] - }, - { - "cell_type": "markdown", - "id": "66a27cfc", - "metadata": {}, - "source": [ - "![Simulator after the tips have been placed and the volumes adjusted](./img/visualizer/resources.png)" - ] - }, - { - "cell_type": "markdown", - "id": "27af93d8", - "metadata": {}, - "source": [ - "## Liquid handling\n", - "\n", - "Once the layout is complete, you can run the same commands as described in the [basic liquid handling tutorial](basic).\n", - "\n", - "It is important that both tip tracking and volume tracking are enabled globally, so that the visualizer can keep track of the state of the tips and the volumes of the liquids." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "ce58298d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(None, None)" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from pylabrobot.resources import set_tip_tracking, set_volume_tracking\n", - "set_tip_tracking(True), set_volume_tracking(True)" - ] - }, - { - "cell_type": "markdown", - "id": "21c3f2cd", - "metadata": {}, - "source": [ - "### Picking up tips\n", - "\n", - "Note that since we are using the {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend`, we just print out \"Picking up tips\" instead of actually performing an operation. The visualizer will show the tips being picked up." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "f97eadd4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Picking up tips [Pickup(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_1_1, location=(016.200, 059.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_2_2, location=(025.200, 050.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_3_3, location=(034.200, 041.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" - ] - } - ], - "source": [ - "await lh.pick_up_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "bf46e476", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dropping tips [Drop(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_1_1, location=(016.200, 059.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_2_2, location=(025.200, 050.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_3_3, location=(034.200, 041.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" - ] - } - ], - "source": [ - "await lh.drop_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])" - ] - }, - { - "cell_type": "markdown", - "id": "5c6948b2", - "metadata": {}, - "source": [ - "### Aspirating and dispensing" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "947977c7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Picking up tips [Pickup(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" - ] - } - ], - "source": [ - "await lh.pick_up_tips(tip_rack1[\"A1\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "c25a147f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Aspirating [Aspiration(resource=Well(name=plate_01_well_1_0, location=(019.870, 070.770, 003.030), size_x=6.86, size_y=6.86, size_z=10.67, category=well), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK), volume=200.0, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[(None, 200.0)])].\n" - ] - } - ], - "source": [ - "await lh.aspirate(plate_1[\"A2\"], vols=[200])" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "e86428ea", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dispensing [Dispense(resource=Well(name=plate_02_well_0_0, location=(010.870, 070.770, 003.030), size_x=6.86, size_y=6.86, size_z=10.67, category=well), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK), volume=200.0, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[(None, 200.0)])].\n" - ] - } - ], - "source": [ - "await lh.dispense(plate_2[\"A1\"], vols=[200])" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "70117e04", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dropping tips [Drop(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" - ] - } - ], - "source": [ - "await lh.return_tips()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "8a8b3d59", - "metadata": {}, - "source": [ - "### Aspirating using CoRe 96\n", - "\n", - "The CoRe 96 head supports liquid handling operations for 96 channels at once. Here's how to use:\n", - "\n", - "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.pick_up_tips96` for picking up 96 tips;\n", - "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.aspirate96` for aspirating liquid from an entire plate at once;\n", - "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.dispense96` for dispensing liquid to an entire plate at once;\n", - "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.drop_tips96` for dropping tips to the tip rack.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "b8c5706d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Picking up tips from tips_01.\n" - ] - } - ], - "source": [ - "await lh.pick_up_tips96(tip_rack1)" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "c09144c9", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Aspirating 100 from Plate(name=plate_01, size_x=127.0, size_y=86.0, size_z=42.0, location=(000.000, 000.000, 000.000)).\n" - ] - } - ], - "source": [ - "await lh.aspirate96(plate_1, volume=100)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "2ba711bb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dispensing 100 to Plate(name=plate_03, size_x=127.0, size_y=86.0, size_z=42.0, location=(000.000, 000.000, 000.000)).\n" - ] - } - ], - "source": [ - "await lh.dispense96(plate_3, volume=100)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "6d205ea7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dropping tips to tips_01.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "c3c71913", + "metadata": {}, + "source": [ + "# Using the Visualizer\n", + "\n", + "The Visualizer is a tool that allows you to visualize the a Resource (like LiquidHandler) including its state to easily see what is going on, for example when executing a protocol on a robot or when developing a new protocol.\n", + "\n", + "When using a backend that does not require access to a physical robot, such as the {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend`, the Visualizer can be used to simulate a robot's behavior. Of course, you may also use the Visualizer when working with a real robot to see what is happening in the PLR resource and state trackers." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d6cdca52", + "metadata": {}, + "source": [ + "## Setting up a connection with the robot\n", + "\n", + "As described in the [basic liquid handling tutorial](basic), we will use the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` class to control the robot. This time, however, instead of using the Hamilton {class}`~pylabrobot.liquid_handling.backends.hamilton.STAR.STAR` backend, we are using the software-only {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend` backend. This means that liquid handling will work exactly the same, but commands are simply printed out to the console instead of being sent to a physical robot. We are still using the same deck." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7c5a2629", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterboxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "272520d3", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources.hamilton import STARLetDeck" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2e280caa", + "metadata": {}, + "outputs": [], + "source": [ + "lh = LiquidHandler(backend=ChatterboxBackend(), deck=STARLetDeck())" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "30dcf4a1", + "metadata": {}, + "source": [ + "Calling {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.setup` will print out \"Setting up the robot\" and also that two resources were assigned: the deck and the trash. Other than that, the chatter box backend has no setup to do." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1419f2b3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Setting up the robot.\n", + "Resource deck was assigned to the robot.\n", + "Resource trash was assigned to the robot.\n", + "Resource trash_core96 was assigned to the robot.\n" + ] + } + ], + "source": [ + "await lh.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "871184ce", + "metadata": {}, + "source": [ + "Next, we will create a {class}`~pylabrobot.visualizer.visualizer.Visualizer` object. The Visualizer expects a Resource, and we will pass the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` object to it. This will allow us to visualize the robot's state and actions." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "165c9de4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Websocket server started at http://127.0.0.1:2121\n", + "File server started at http://127.0.0.1:1337 . Open this URL in your browser.\n" + ] + } + ], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "3002429b", + "metadata": {}, + "source": [ + "![The empty simulator](./img/visualizer/empty.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "050ccefd", + "metadata": {}, + "source": [ + "## Build the deck layout: Assigning plates and tips\n", + "\n", + "When resources are assigned to the root resource of the Visualizer, in this case `lh`, they will automatically appear in the visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c8c464c8", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cor_96_wellplate_360ul_Fb,\n", + " HTF\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b14e3628", + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", + "tip_car[0] = tip_rack1 = HTF(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF(name='tips_05', with_tips=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "140872be", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource tip carrier was assigned to the robot.\n" + ] + } + ], + "source": [ + "lh.deck.assign_child_resource(tip_car, rails=15)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "13cbc612", + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", + "plt_car[0] = plate_1 = Cor_96_wellplate_360ul_Fb(name='plate_01')\n", + "plt_car[1] = plate_2 = Cor_96_wellplate_360ul_Fb(name='plate_02')\n", + "plt_car[2] = plate_3 = Cor_96_wellplate_360ul_Fb(name='plate_03')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d618ec6a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource plate carrier was assigned to the robot.\n" + ] + } + ], + "source": [ + "lh.deck.assign_child_resource(plt_car, rails=8)" + ] + }, + { + "cell_type": "markdown", + "id": "21835f31", + "metadata": {}, + "source": [ + "![The simulator after the resources have been assigned](./img/visualizer/assignment.png)" + ] + }, + { + "cell_type": "markdown", + "id": "68a9721b", + "metadata": {}, + "source": [ + "### Configuring the state of the deck\n", + "\n", + "As with every PyLabRobot script, you have the option of updating the state of the deck before you actually start your method. This will allow PyLabRobot to keep track of what is going on, enabling features like {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.return_tips` and catching errors (like missed tips) before a command would be executed on the robot. With the visualizer, this state has the additional effect of updating the visualization." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "daf99520", + "metadata": {}, + "source": [ + "### Tips\n", + "\n", + "Let's use {func}`~pylabrobot.resources.tip_rack.fill` to place tips at all spots in the tip rack in location `0`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "ca3152d0", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "tip_rack1.fill()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "26f036c6", + "metadata": {}, + "source": [ + "\n", + "You can precisely control the presence of tips using {func}`~pylabrobot.resources.tip_rack.set_tip_state`. This function allows you to set whether there is a tip in each {class}`~pylabrobot.resources.tip_rack.TipSpot`." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8f574b8d", + "metadata": {}, + "outputs": [], + "source": [ + "tip_rack4 = lh.deck.get_resource(\"tips_04\")\n", + "tip_rack4.set_tip_state([[True]*6 + [False]*6]*8)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "8e1ed1e6", + "metadata": {}, + "outputs": [], + "source": [ + "tip_rack3.set_tip_state([[True, False]*6]*8)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "890272f1", + "metadata": {}, + "outputs": [], + "source": [ + "tip_rack2.set_tip_state([[True, True, False, False]*3]*8)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "594d8016", + "metadata": {}, + "source": [ + "### Liquids\n", + "\n", + "Adding liquid to wells works similarly. You can use {func}`~pylabrobot.resources.plate.set_well_liquids` to set the liquid in each well of a plate. Each liquid is represented by a tuple where the first element corresponds to the type of liquid and the second to the volume in uL. Here, `None` is used to designate an unknown liquid." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "5b76aac2", + "metadata": {}, + "outputs": [], + "source": [ + "plate_1_liquids = [[(None, 500)]]*96\n", + "plate_1.set_well_liquids(plate_1_liquids)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "400208c7", + "metadata": {}, + "outputs": [], + "source": [ + "plate_2_liquids = [[(None, 100)], [(None, 500)]]*(96//2)\n", + "plate_2.set_well_liquids(plate_2_liquids)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "66281459", + "metadata": {}, + "source": [ + "In the visualizer, you can see that the opacity of the well is proportional to how full the well is relative to its maximum volume." + ] + }, + { + "cell_type": "markdown", + "id": "66a27cfc", + "metadata": {}, + "source": [ + "![Simulator after the tips have been placed and the volumes adjusted](./img/visualizer/resources.png)" + ] + }, + { + "cell_type": "markdown", + "id": "27af93d8", + "metadata": {}, + "source": [ + "## Liquid handling\n", + "\n", + "Once the layout is complete, you can run the same commands as described in the [basic liquid handling tutorial](basic).\n", + "\n", + "It is important that both tip tracking and volume tracking are enabled globally, so that the visualizer can keep track of the state of the tips and the volumes of the liquids." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ce58298d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(None, None)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from pylabrobot.resources import set_tip_tracking, set_volume_tracking\n", + "set_tip_tracking(True), set_volume_tracking(True)" + ] + }, + { + "cell_type": "markdown", + "id": "21c3f2cd", + "metadata": {}, + "source": [ + "### Picking up tips\n", + "\n", + "Note that since we are using the {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend`, we just print out \"Picking up tips\" instead of actually performing an operation. The visualizer will show the tips being picked up." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "f97eadd4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_1_1, location=(016.200, 059.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_2_2, location=(025.200, 050.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_3_3, location=(034.200, 041.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "bf46e476", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_1_1, location=(016.200, 059.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_2_2, location=(025.200, 050.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_3_3, location=(034.200, 041.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" + ] + } + ], + "source": [ + "await lh.drop_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])" + ] + }, + { + "cell_type": "markdown", + "id": "5c6948b2", + "metadata": {}, + "source": [ + "### Aspirating and dispensing" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "947977c7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack1[\"A1\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "c25a147f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating [Aspiration(resource=Well(name=plate_01_well_1_0, location=(019.870, 070.770, 003.030), size_x=6.86, size_y=6.86, size_z=10.67, category=well), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK), volume=200.0, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[(None, 200.0)])].\n" + ] + } + ], + "source": [ + "await lh.aspirate(plate_1[\"A2\"], vols=[200])" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e86428ea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dispensing [Dispense(resource=Well(name=plate_02_well_0_0, location=(010.870, 070.770, 003.030), size_x=6.86, size_y=6.86, size_z=10.67, category=well), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK), volume=200.0, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[(None, 200.0)])].\n" + ] + } + ], + "source": [ + "await lh.dispense(plate_2[\"A1\"], vols=[200])" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "70117e04", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" + ] + } + ], + "source": [ + "await lh.return_tips()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8a8b3d59", + "metadata": {}, + "source": [ + "### Aspirating using CoRe 96\n", + "\n", + "The CoRe 96 head supports liquid handling operations for 96 channels at once. Here's how to use:\n", + "\n", + "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.pick_up_tips96` for picking up 96 tips;\n", + "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.aspirate96` for aspirating liquid from an entire plate at once;\n", + "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.dispense96` for dispensing liquid to an entire plate at once;\n", + "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.drop_tips96` for dropping tips to the tip rack.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "b8c5706d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips from tips_01.\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips96(tip_rack1)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "c09144c9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating 100 from Plate(name=plate_01, size_x=127.0, size_y=86.0, size_z=42.0, location=(000.000, 000.000, 000.000)).\n" + ] + } + ], + "source": [ + "await lh.aspirate96(plate_1, volume=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "2ba711bb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dispensing 100 to Plate(name=plate_03, size_x=127.0, size_y=86.0, size_z=42.0, location=(000.000, 000.000, 000.000)).\n" + ] + } + ], + "source": [ + "await lh.dispense96(plate_3, volume=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "6d205ea7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips to tips_01.\n" + ] + } + ], + "source": [ + "await lh.drop_tips96(tip_rack1)" + ] + }, + { + "cell_type": "markdown", + "id": "9cc77505", + "metadata": {}, + "source": [ + "![The simulator after the liquid handling operations completed](./img/visualizer/after_lh.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e49020a5", + "metadata": {}, + "source": [ + "## Shutting down\n", + "\n", + "When you're done, you can stop the visualizer by calling {func}`~pylabrobot.visualizer.visualizer.Visualizer.stop`. This will stop the visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "44a61431", + "metadata": {}, + "outputs": [], + "source": [ + "await vis.stop()" + ] } - ], - "source": [ - "await lh.drop_tips96(tip_rack1)" - ] - }, - { - "cell_type": "markdown", - "id": "9cc77505", - "metadata": {}, - "source": [ - "![The simulator after the liquid handling operations completed](./img/visualizer/after_lh.png)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "e49020a5", - "metadata": {}, - "source": [ - "## Shutting down\n", - "\n", - "When you're done, you can stop the visualizer by calling {func}`~pylabrobot.visualizer.visualizer.Visualizer.stop`. This will stop the visualization." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "44a61431", - "metadata": {}, - "outputs": [], - "source": [ - "await vis.stop()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + }, + "vscode": { + "interpreter": { + "hash": "bf274dfc1b974177267b6b8fba8543eeb0bb4c5d64c637dde420829b05625268" + } } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false }, - "vscode": { - "interpreter": { - "hash": "bf274dfc1b974177267b6b8fba8543eeb0bb4c5d64c637dde420829b05625268" - } - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/docs/user_guide/using-trackers.ipynb b/docs/user_guide/using-trackers.ipynb index 6f993dce10..8784965a73 100644 --- a/docs/user_guide/using-trackers.ipynb +++ b/docs/user_guide/using-trackers.ipynb @@ -31,7 +31,7 @@ "from pylabrobot.liquid_handling.backends.chatterbox_backend import ChatterboxBackend\n", "from pylabrobot.resources import (\n", " TIP_CAR_480_A00,\n", - " HTF_L,\n", + " HTF,\n", " PLT_CAR_L5AC_A00,\n", " Cor_96_wellplate_360ul_Fbate_360ul_Fb,\n", " set_tip_tracking,\n", @@ -105,7 +105,7 @@ "metadata": {}, "outputs": [], "source": [ - "tip_carrier[0] = tip_rack = HTF_L(name=\"tip rack\")" + "tip_carrier[0] = tip_rack = HTF(name=\"tip rack\")" ] }, { @@ -142,7 +142,7 @@ "metadata": {}, "outputs": [], "source": [ - "tip_carrier[1] = empty_tip_rack = HTF_L(name=\"empty tip rack\", with_tips=False)" + "tip_carrier[1] = empty_tip_rack = HTF(name=\"empty tip rack\", with_tips=False)" ] }, { @@ -996,4 +996,4 @@ }, "nbformat": 4, "nbformat_minor": 2 -} +} \ No newline at end of file diff --git a/pylabrobot/liquid_handling/backends/chatterbox_tests.py b/pylabrobot/liquid_handling/backends/chatterbox_tests.py index 203ab1f600..5862773a0d 100644 --- a/pylabrobot/liquid_handling/backends/chatterbox_tests.py +++ b/pylabrobot/liquid_handling/backends/chatterbox_tests.py @@ -6,7 +6,7 @@ ) from pylabrobot.resources import ( Cor_96_wellplate_360ul_Fb, - HTF_L, + HTF, Coordinate, ) from pylabrobot.resources.hamilton import STARLetDeck @@ -19,7 +19,7 @@ def setUp(self) -> None: self.deck = STARLetDeck() self.backend = LiquidHandlerChatterboxBackend(num_channels=8) self.lh = LiquidHandler(self.backend, deck=self.deck) - self.tip_rack = HTF_L(name="tip_rack") + self.tip_rack = HTF(name="tip_rack") self.deck.assign_child_resource(self.tip_rack, rails=3) self.plate = Cor_96_wellplate_360ul_Fb(name="plate") self.deck.assign_child_resource(self.plate, rails=9) diff --git a/pylabrobot/liquid_handling/backends/hamilton/STAR_tests.py b/pylabrobot/liquid_handling/backends/hamilton/STAR_tests.py index 288ac907f6..5a2e4f5fd9 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/STAR_tests.py +++ b/pylabrobot/liquid_handling/backends/hamilton/STAR_tests.py @@ -17,13 +17,13 @@ TIP_CAR_480_A00, TIP_CAR_288_C00, PLT_CAR_L5AC_A00, - HT_P, - HTF_L, + HT, + HTF, Cor_96_wellplate_360ul_Fb, no_volume_tracking, ) from pylabrobot.resources.hamilton import STARLetDeck -from pylabrobot.resources.ml_star import STF_L +from pylabrobot.resources.ml_star import STF from tests.usb import MockDev, MockEndpoint @@ -221,8 +221,8 @@ async def asyncSetUp(self): self.lh = LiquidHandler(self.mockSTAR, deck=self.deck) self.tip_car = TIP_CAR_480_A00(name="tip carrier") - self.tip_car[1] = self.tip_rack = STF_L(name="tip_rack_01") - self.tip_car[2] = self.tip_rack2 = HTF_L(name="tip_rack_02") + self.tip_car[1] = self.tip_rack = STF(name="tip_rack_01") + self.tip_car[2] = self.tip_rack2 = HTF(name="tip_rack_02") self.deck.assign_child_resource(self.tip_car, rails=1) self.plt_car = PLT_CAR_L5AC_A00(name="plate carrier") @@ -900,12 +900,10 @@ async def test_discard_tips(self): ) async def test_portrait_tip_rack_handling(self): - # Test with an alternative setup. - deck = STARLetDeck() lh = LiquidHandler(self.mockSTAR, deck=deck) tip_car = TIP_CAR_288_C00(name="tip carrier") - tip_car[0] = tr = HT_P(name="tips_01") + tip_car[0] = tr = HT(name="tips_01").rotated(z=90) assert tr.rotation.z == 90 assert tr.location == Coordinate(82.6, 0, 0) deck.assign_child_resource(tip_car, rails=2) diff --git a/pylabrobot/liquid_handling/backends/hamilton/vantage_tests.py b/pylabrobot/liquid_handling/backends/hamilton/vantage_tests.py index 1f323867d7..8d32c12c9d 100644 --- a/pylabrobot/liquid_handling/backends/hamilton/vantage_tests.py +++ b/pylabrobot/liquid_handling/backends/hamilton/vantage_tests.py @@ -6,8 +6,8 @@ Coordinate, TIP_CAR_480_A00, PLT_CAR_L5AC_A00, - HT_L, - LT_L, + HT, + LT, Cor_96_wellplate_360ul_Fb, ) from pylabrobot.resources.hamilton import VantageDeck @@ -244,8 +244,8 @@ async def asyncSetUp(self): self.lh = LiquidHandler(self.mockVantage, deck=self.deck) self.tip_car = TIP_CAR_480_A00(name="tip carrier") - self.tip_car[0] = self.tip_rack = HT_L(name="tip_rack_01") - self.tip_car[1] = self.small_tip_rack = LT_L(name="tip_rack_02") + self.tip_car[0] = self.tip_rack = HT(name="tip_rack_01") + self.tip_car[1] = self.small_tip_rack = LT(name="tip_rack_02") self.deck.assign_child_resource(self.tip_car, rails=18) self.plt_car = PLT_CAR_L5AC_A00(name="plate carrier") diff --git a/pylabrobot/liquid_handling/backends/http_tests.py b/pylabrobot/liquid_handling/backends/http_tests.py index 248b5fb10c..31fc4e6a81 100644 --- a/pylabrobot/liquid_handling/backends/http_tests.py +++ b/pylabrobot/liquid_handling/backends/http_tests.py @@ -9,7 +9,7 @@ from pylabrobot.resources import ( PLT_CAR_L5AC_A00, TIP_CAR_480_A00, - HTF_L, + HTF, Cor_96_wellplate_360ul_Fb, no_tip_tracking, no_volume_tracking, @@ -74,7 +74,7 @@ async def asyncSetUp(self) -> None: # type: ignore self.deck = STARLetDeck() self.tip_carrier = TIP_CAR_480_A00(name="tip_carrier") - self.tip_carrier[0] = self.tip_rack = HTF_L(name="tiprack") + self.tip_carrier[0] = self.tip_rack = HTF(name="tiprack") self.plate_carrier = PLT_CAR_L5AC_A00(name="plate_carrier") self.plate_carrier[0] = self.plate = Cor_96_wellplate_360ul_Fb(name="plate") self.deck.assign_child_resource(self.tip_carrier, rails=3) diff --git a/pylabrobot/liquid_handling/backends/serializing_backend_tests.py b/pylabrobot/liquid_handling/backends/serializing_backend_tests.py index 7502b950d8..7124df35a6 100644 --- a/pylabrobot/liquid_handling/backends/serializing_backend_tests.py +++ b/pylabrobot/liquid_handling/backends/serializing_backend_tests.py @@ -9,7 +9,7 @@ TIP_CAR_480_A00, PLT_CAR_L5AC_A00, Cor_96_wellplate_360ul_Fb, - STF_L, + STF, Coordinate, no_tip_tracking, no_volume_tracking, @@ -27,7 +27,7 @@ async def asyncSetUp(self) -> None: await self.lh.setup() self.tip_car = TIP_CAR_480_A00(name="tip carrier") - self.tip_car[0] = self.tip_rack = STF_L(name="tip_rack_01") + self.tip_car[0] = self.tip_rack = STF(name="tip_rack_01") self.deck.assign_child_resource(self.tip_car, rails=1) self.plt_car = PLT_CAR_L5AC_A00(name="plate carrier") diff --git a/pylabrobot/liquid_handling/liquid_handler_tests.py b/pylabrobot/liquid_handling/liquid_handler_tests.py index 5647c3f860..d2ade413b7 100644 --- a/pylabrobot/liquid_handling/liquid_handler_tests.py +++ b/pylabrobot/liquid_handling/liquid_handler_tests.py @@ -45,7 +45,7 @@ ResourceNotFoundError, ) from pylabrobot.resources.hamilton import STARLetDeck -from pylabrobot.resources.ml_star import STF_L, HTF_L +from pylabrobot.resources.ml_star import STF, HTF from .standard import ( GripDirection, Pickup, @@ -102,9 +102,9 @@ def setUp(self): def test_resource_assignment(self): tip_car = TIP_CAR_480_A00(name="tip_carrier") - tip_car[0] = STF_L(name="tip_rack_01") - tip_car[1] = STF_L(name="tip_rack_02") - tip_car[3] = HTF_L("tip_rack_04") + tip_car[0] = STF(name="tip_rack_01") + tip_car[1] = STF(name="tip_rack_02") + tip_car[3] = HTF("tip_rack_04") plt_car = PLT_CAR_L5AC_A00(name="plate carrier") plt_car[0] = Cor_96_wellplate_360ul_Fb(name="aspiration plate") @@ -136,7 +136,7 @@ def test_resource_assignment(self): def test_get_resource(self): tip_car = TIP_CAR_480_A00(name="tip_carrier") - tip_car[0] = STF_L(name="tip_rack_01") + tip_car[0] = STF(name="tip_rack_01") plt_car = PLT_CAR_L5AC_A00(name="plate carrier") plt_car[0] = Cor_96_wellplate_360ul_Fb(name="aspiration plate") self.deck.assign_child_resource(tip_car, rails=1) @@ -159,8 +159,8 @@ def test_get_resource(self): def test_subcoordinates(self): tip_car = TIP_CAR_480_A00(name="tip_carrier") - tip_car[0] = STF_L(name="tip_rack_01") - tip_car[3] = HTF_L(name="tip_rack_04") + tip_car[0] = STF(name="tip_rack_01") + tip_car[3] = HTF(name="tip_rack_04") plt_car = PLT_CAR_L5AC_A00(name="plate carrier") plt_car[0] = Cor_96_wellplate_360ul_Fb(name="aspiration plate") plt_car[2] = Cor_96_wellplate_360ul_Fb(name="dispense plate") @@ -208,7 +208,7 @@ def test_illegal_subresource_assignment_before(self): # Test assigning subresource with the same name as another resource in another carrier. This # should raise an ValueError when the carrier is assigned to the liquid handler. tip_car = TIP_CAR_480_A00(name="tip_carrier") - tip_car[0] = STF_L(name="sub") + tip_car[0] = STF(name="sub") plt_car = PLT_CAR_L5AC_A00(name="plate carrier") plt_car[0] = Cor_96_wellplate_360ul_Fb(name="sub") self.deck.assign_child_resource(tip_car, rails=1) @@ -219,7 +219,7 @@ def test_illegal_subresource_assignment_after(self): # Test assigning subresource with the same name as another resource in another carrier, after # the carrier has been assigned. This should raise an error. tip_car = TIP_CAR_480_A00(name="tip_carrier") - tip_car[0] = STF_L(name="sub") + tip_car[0] = STF(name="sub") plt_car = PLT_CAR_L5AC_A00(name="plate carrier") plt_car[0] = Cor_96_wellplate_360ul_Fb(name="ok") self.deck.assign_child_resource(tip_car, rails=1) @@ -450,7 +450,7 @@ async def asyncSetUp(self): self.deck = STARLetDeck() self.lh = LiquidHandler(backend=self.backend, deck=self.deck) - self.tip_rack = STF_L(name="tip_rack") + self.tip_rack = STF(name="tip_rack") self.plate = Cor_96_wellplate_360ul_Fb(name="plate") self.deck.assign_child_resource(self.tip_rack, location=Coordinate(0, 0, 0)) self.deck.assign_child_resource(self.plate, location=Coordinate(100, 100, 0)) @@ -1017,7 +1017,7 @@ async def asyncSetUp(self): self.backend = backends.SaverBackend(num_channels=8) self.deck = STARLetDeck() self.lh = LiquidHandler(backend=self.backend, deck=self.deck) - self.tip_rack = STF_L(name="tip_rack") + self.tip_rack = STF(name="tip_rack") self.plate = Cor_96_wellplate_360ul_Fb(name="plate") self.deck.assign_child_resource(self.tip_rack, location=Coordinate(0, 0, 0)) self.deck.assign_child_resource(self.plate, location=Coordinate(100, 100, 0)) @@ -1060,7 +1060,7 @@ async def asyncSetUp(self): self.backend = backends.SaverBackend(num_channels=8) self.deck = STARLetDeck() self.lh = LiquidHandler(backend=self.backend, deck=self.deck) - self.tip_rack = STF_L(name="tip_rack") + self.tip_rack = STF(name="tip_rack") self.plate = Cor_96_wellplate_360ul_Fb(name="plate") self.deck.assign_child_resource(self.tip_rack, location=Coordinate(0, 0, 0)) self.deck.assign_child_resource(self.plate, location=Coordinate(100, 100, 0)) diff --git a/pylabrobot/resources/carrier.py b/pylabrobot/resources/carrier.py index 7cd0096abd..107495ac73 100644 --- a/pylabrobot/resources/carrier.py +++ b/pylabrobot/resources/carrier.py @@ -70,13 +70,13 @@ class Carrier(Resource, Generic[S]): Creating a `TipCarrier` and assigning one set of tips at location 0 (the bottom): >>> tip_car = TIP_CAR_480_A00(name='tip carrier') - >>> tip_car[0] = STF_L(name='tips_1') + >>> tip_car[0] = STF(name='tips_1') Getting the tips: >>> tip_car[0] - STF_L(name='tips_1') + STF(name='tips_1') Deleting the tips: diff --git a/pylabrobot/resources/hamilton/hamilton_deck_tests.py b/pylabrobot/resources/hamilton/hamilton_deck_tests.py index 9830cceed0..fe506c3c60 100644 --- a/pylabrobot/resources/hamilton/hamilton_deck_tests.py +++ b/pylabrobot/resources/hamilton/hamilton_deck_tests.py @@ -9,8 +9,8 @@ ) from pylabrobot.resources.hamilton import STARLetDeck from pylabrobot.resources.ml_star import ( - STF_L, - HTF_L, + STF, + HTF, TIP_CAR_480_A00, PLT_CAR_L5AC_A00, ) @@ -24,9 +24,9 @@ def build_layout(self): deck = STARLetDeck() tip_car = TIP_CAR_480_A00(name="tip_carrier") - tip_car[0] = STF_L(name="tip_rack_01") - tip_car[1] = STF_L(name="tip_rack_02") - tip_car[3] = HTF_L(name="tip_rack_04") + tip_car[0] = STF(name="tip_rack_01") + tip_car[1] = STF(name="tip_rack_02") + tip_car[3] = HTF(name="tip_rack_04") plt_car = PLT_CAR_L5AC_A00(name="plate carrier") plt_car[0] = Cor_96_wellplate_360ul_Fb(name="aspiration plate") diff --git a/pylabrobot/resources/hamilton/hamilton_decks.py b/pylabrobot/resources/hamilton/hamilton_decks.py index 2e2b5384d3..d45453fa3f 100644 --- a/pylabrobot/resources/hamilton/hamilton_decks.py +++ b/pylabrobot/resources/hamilton/hamilton_decks.py @@ -207,7 +207,7 @@ def summary(self) -> str: Rail Resource Type Coordinates (mm) ============================================================================================= (1) ├── tip_car TIP_CAR_480_A00 (x: 100.000, y: 240.800, z: 164.450) - │ ├── tip_rack_01 STF_L (x: 117.900, y: 240.000, z: 100.000) + │ ├── tip_rack_01 STF (x: 117.900, y: 240.000, z: 100.000) """ if len(self.get_all_resources()) == 0: diff --git a/pylabrobot/resources/ml_star/tip_racks.py b/pylabrobot/resources/ml_star/tip_racks.py index 58ae250a33..21a98e9461 100644 --- a/pylabrobot/resources/ml_star/tip_racks.py +++ b/pylabrobot/resources/ml_star/tip_racks.py @@ -28,7 +28,7 @@ def FourmlTF(name: str, with_tips: bool = True) -> TipRack: size_x=122.4, size_y=82.6, size_z=7.0, - model="FourmlTF_L", + model="FourmlTF", ordered_items=create_ordered_items_2d( TipSpot, num_items_x=6, @@ -65,7 +65,7 @@ def FivemlT(name: str, with_tips: bool = True) -> TipRack: size_x=122.4, size_y=82.6, size_z=7.0, - model="FivemlT_L", + model="FivemlT", ordered_items=create_ordered_items_2d( TipSpot, num_items_x=6, @@ -102,7 +102,7 @@ def HTF(name: str, with_tips: bool = True) -> TipRack: size_x=122.4, size_y=82.6, size_z=20.0, - model="HTF_L", + model="HTF", ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, @@ -132,14 +132,14 @@ def HTF_P(name: str, with_tips: bool = True) -> TipRack: ) -def HTF_L_WIDE(name: str, with_tips: bool = True) -> TipRack: +def HTF_WIDE(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 1000ul High Volume Tip with filter""" return TipRack( name=name, size_x=122.4, size_y=82.6, size_z=20.0, - model=HTF_L_WIDE.__name__, + model=HTF_WIDE.__name__, ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, @@ -157,14 +157,18 @@ def HTF_L_WIDE(name: str, with_tips: bool = True) -> TipRack: ) -def HTF_L_ULTRAWIDE(name: str, with_tips: bool = True) -> TipRack: +def HTF_L_WIDE(name: str, with_tips: bool = True) -> TipRack: + raise NotImplementedError("_L and _P definitions are deprecated. Use " "HTF_WIDE instead.") + + +def HTF_ULTRAWIDE(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 1000ul High Volume Tip with filter""" return TipRack( name=name, size_x=122.4, size_y=82.6, size_z=20.0, - model=HTF_L_ULTRAWIDE.__name__, + model=HTF_ULTRAWIDE.__name__, ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, @@ -182,6 +186,10 @@ def HTF_L_ULTRAWIDE(name: str, with_tips: bool = True) -> TipRack: ) +def HTF_L_ULTRAWIDE(name: str, with_tips: bool = True) -> TipRack: + raise NotImplementedError("_L and _P definitions are deprecated. Use " "HTF_ULTRAWIDE instead.") + + def HT(name: str, with_tips: bool = True) -> TipRack: """Tip Rack with 96 1000ul High Volume Tip""" return TipRack( @@ -189,7 +197,7 @@ def HT(name: str, with_tips: bool = True) -> TipRack: size_x=122.4, size_y=82.6, size_z=20.0, - model="HT_L", + model="HT", ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, @@ -226,7 +234,7 @@ def LTF(name: str, with_tips: bool = True) -> TipRack: size_x=122.4, size_y=82.6, size_z=20.0, - model="LTF_L", + model="LTF", ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, @@ -263,7 +271,7 @@ def LT(name: str, with_tips: bool = True) -> TipRack: size_x=122.4, size_y=82.6, size_z=20.0, - model="LT_L", + model="LT", ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, @@ -300,7 +308,7 @@ def STF(name: str, with_tips: bool = True) -> TipRack: size_x=122.4, size_y=82.6, size_z=20.0, - model="STF_L", + model="STF", ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, @@ -374,7 +382,7 @@ def ST(name: str, with_tips: bool = True) -> TipRack: size_x=122.4, size_y=82.6, size_z=20.0, - model="ST_L", + model="ST", ordered_items=create_ordered_items_2d( TipSpot, num_items_x=12, diff --git a/pylabrobot/resources/tecan/tecan_decks.py b/pylabrobot/resources/tecan/tecan_decks.py index 7b6681cfdc..48b7071609 100644 --- a/pylabrobot/resources/tecan/tecan_decks.py +++ b/pylabrobot/resources/tecan/tecan_decks.py @@ -147,17 +147,7 @@ def _rails_for_x_coordinate(self, x: float): return round((x + _RAILS_WIDTH - 101) / _RAILS_WIDTH) + 1 def summary(self) -> str: - """Return a summary of the deck. - - Example: - Printing a summary of the deck layout: - - >>> print(deck.summary()) - Rail Resource Type Coordinates (mm) - ============================================================================================== - (1) ├── tip_car TIP_CAR_480_A00 (x: 100.000, y: 240.800, z: 164.450) - │ ├── tip_rack_01 STF_L (x: 117.900, y: 240.000, z: 100.000) - """ + """Return a summary of the deck.""" if len(self.get_all_resources()) == 0: raise ValueError( diff --git a/pylabrobot/server/liquid_handling_api_tests.py b/pylabrobot/server/liquid_handling_api_tests.py index 6e703cc44f..59cfbfc934 100644 --- a/pylabrobot/server/liquid_handling_api_tests.py +++ b/pylabrobot/server/liquid_handling_api_tests.py @@ -12,7 +12,7 @@ from pylabrobot.resources import ( Plate, TipRack, - HTF_L, + HTF, Cor_96_wellplate_360ul_Fb, TIP_CAR_480_A00, PLT_CAR_L5AC_A00, @@ -26,7 +26,7 @@ def build_layout() -> HamiltonDeck: # copied from liquid_handler_tests.py, can we make this shared? tip_car = TIP_CAR_480_A00(name="tip_carrier") - tip_car[0] = HTF_L(name="tip_rack_01") + tip_car[0] = HTF(name="tip_rack_01") plt_car = PLT_CAR_L5AC_A00(name="plate_carrier") plt_car[0] = plate = Cor_96_wellplate_360ul_Fb(name="aspiration plate") From 4e2de2e9215c487b587d33860c11c2d5c72a5864 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 22 Oct 2024 17:15:29 -0700 Subject: [PATCH 5/8] forgot this --- pylabrobot/gui/gui.py | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/pylabrobot/gui/gui.py b/pylabrobot/gui/gui.py index c0d0c9e1ef..1191785f83 100644 --- a/pylabrobot/gui/gui.py +++ b/pylabrobot/gui/gui.py @@ -50,58 +50,58 @@ def list_resources(): return jsonify( plates=[ "Cos_1536_10ul", - "Cos_1536_10ul_L", + "Cos_1536_10ul", "Cos_1536_10ul_P", "Cos_384_DW", - "Cos_384_DW_L", + "Cos_384_DW", "Cos_384_DW_P", "Cos_384_PCR", - "Cos_384_PCR_L", + "Cos_384_PCR", "Cos_384_PCR_P", "Cos_384_Sq", - "Cos_384_Sq_L", + "Cos_384_Sq", "Cos_384_Sq_P", "Cos_384_Sq_Rd", - "Cos_384_Sq_Rd_L", + "Cos_384_Sq_Rd", "Cos_384_Sq_Rd_P", "Cos_96_DW_1mL", - "Cos_96_DW_1mL_L", + "Cos_96_DW_1mL", "Cos_96_DW_1mL_P", "Cos_96_DW_2mL", - "Cos_96_DW_2mL_L", + "Cos_96_DW_2mL", "Cos_96_DW_2mL_P", "Cos_96_DW_500ul", - "Cos_96_DW_500ul_L", + "Cos_96_DW_500ul", "Cos_96_DW_500ul_P", "Cor_96_wellplate_360ul_Fb", - "Cor_96_wellplate_360ul_Fb_L", + "Cor_96_wellplate_360ul_Fb", "Cor_96_wellplate_360ul_Fb_P", "Cos_96_FL", "Cos_96_Filter", - "Cos_96_Filter_L", + "Cos_96_Filter", "Cos_96_Filter_P", - "Cos_96_Fl_L", + "Cos_96_Fl", "Cos_96_Fl_P", "Cos_96_HalfArea", - "Cos_96_HalfArea_L", + "Cos_96_HalfArea", "Cos_96_HalfArea_P", "Cos_96_PCR", - "Cos_96_PCR_L", + "Cos_96_PCR", "Cos_96_PCR_P", "Cos_96_ProtCryst", - "Cos_96_ProtCryst_L", + "Cos_96_ProtCryst", "Cos_96_ProtCryst_P", "Cos_96_Rd", - "Cos_96_Rd_L", + "Cos_96_Rd", "Cos_96_Rd_P", "Cos_96_SpecOps", - "Cos_96_SpecOps_L", + "Cos_96_SpecOps", "Cos_96_SpecOps_P", "Cos_96_UV", - "Cos_96_UV_L", + "Cos_96_UV", "Cos_96_UV_P", "Cos_96_Vb", - "Cos_96_Vb_L", + "Cos_96_Vb", "Cos_96_Vb_P", ], plate_carriers=[ @@ -197,21 +197,21 @@ def list_resources(): "TIP_CAR_NTR_A00", ], tip_racks=[ - "FourmlTF_L", + "FourmlTF", "FourmlTF_P", - "FivemlT_L", + "FivemlT", "FivemlT_P", - "HTF_L", + "HTF", "HTF_P", - "HT_L", + "HT", "HT_P", - "LTF_L", + "LTF", "LTF_P", - "LT_L", + "LT", "LT_P", - "STF_L", + "STF", "STF_P", - "ST_L", + "ST", "ST_P", ], ) From bd8ed8a55ccc507df82599e01bb4ae044feffab7 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 22 Oct 2024 17:19:56 -0700 Subject: [PATCH 6/8] this nb --- docs/user_guide/moving-channels-around.ipynb | 384 +++++++++---------- 1 file changed, 192 insertions(+), 192 deletions(-) diff --git a/docs/user_guide/moving-channels-around.ipynb b/docs/user_guide/moving-channels-around.ipynb index 8cc7f61a57..e6189abbb3 100644 --- a/docs/user_guide/moving-channels-around.ipynb +++ b/docs/user_guide/moving-channels-around.ipynb @@ -1,194 +1,194 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Manually moving channels around\n", - "\n", - "![star supported](https://img.shields.io/badge/STAR-supported-blue)\n", - "![Vantage supported](https://img.shields.io/badge/Vantage-supported-blue)\n", - "![OT supported](https://img.shields.io/badge/OT-supported-blue)\n", - "![EVO not tested](https://img.shields.io/badge/EVO-not%20tested-orange)\n", - "\n", - "With PLR, you can easily move individual channels around. This is useful for calibrating labware locations, calibrating labware sizes, and other things.\n", - "\n", - "```{warning}\n", - "Be very careful about collisions! Move channels to a safe z height before traversing.\n", - "```\n", - "\n", - "```{note}\n", - "With Hamilton robots, when a tip is mounted, the z location will refer to the point of the pipetting tip. With no tip mounted, it will refer to the bottom of the channel.\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Example: Hamilton STAR\n", - "\n", - "Here, we'll use a Hamilton STAR as an example. For other robots, simply change the deck layout, makign sure that you have at least a tip rack to use." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler, STAR\n", - "from pylabrobot.resources import STARDeck, TIP_CAR_480_A00, HTF\n", - "\n", - "lh = LiquidHandler(backend=STAR(), deck=STARDeck())\n", - "await lh.setup()\n", - "\n", - "# assign a tip rack\n", - "tip_carrier = TIP_CAR_480_A00(name=\"tip_carrier\")\n", - "tip_carrier[0] = tip_rack = HTF(name=\"tip_rack\")\n", - "lh.deck.assign_child_resource(tip_carrier, rails=0)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Moving channels\n", - "\n", - "All positions are in mm. The movements are to absolute positions. The origin will be near the left front bottom of the deck, but it differs between robots.\n", - "\n", - "* x: left (low) to right (high)\n", - "* y: front (low) to back (high)\n", - "* z: bottom (low) to top (high)" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "channel = 1 # the channel to use\n", - "\n", - "# start by picking up a single tip\n", - "await lh.pick_up_tips(tip_rack[\"A1\"], use_channels=[channel])\n", - "\n", - "# prepare for manual operation\n", - "# this will space the other channels to safe positions\n", - "await lh.prepare_for_manual_channel_operation(channel)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Since the channnel will be above the tip rack, it should be safe to move up. We perform a quick check to make sure the z_safe is at least above the resources we know about." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "z_safe = 240 # WARNING: this might NOT be safe for your setup\n", - "\n", - "if z_safe <= lh.deck.get_heighest_known_point():\n", - " raise ValueError(f\"z_safe position is not safe, it is lower than the highest known point: {lh.deck.get_heighest_known_point()}\")\n", - "\n", - "await lh.move_channel_z(channel, z_safe)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "```{warning}\n", - "The z position in the code above should be safe for most setups, but we can't guarantee that it will be safe for all setups. Move to a z position that is above all your labware before moving in the xy plane.\n", - "```\n", - "\n", - "When the z position of the bottom of the tip is above the labware, you can move the channel around in the xy plane." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "# move the channel around\n", - "await lh.move_channel_x(channel, 100)\n", - "await lh.move_channel_y(channel, 100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After reaching a spot where the channel can move down, you can use `move_channel_z` again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "await lh.move_channel_z(channel, 100)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Before returning the tip to the tip rack, make sure to move the channel to a safe z position again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "await lh.move_channel_z(channel, z_safe)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You can run the code above as often as you like. When you're done, you can return the channel to the tip rack." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [], - "source": [ - "await lh.return_tips()" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "env", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.2" - } + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Manually moving channels around\n", + "\n", + "![star supported](https://img.shields.io/badge/STAR-supported-blue)\n", + "![Vantage supported](https://img.shields.io/badge/Vantage-supported-blue)\n", + "![OT supported](https://img.shields.io/badge/OT-supported-blue)\n", + "![EVO not tested](https://img.shields.io/badge/EVO-not%20tested-orange)\n", + "\n", + "With PLR, you can easily move individual channels around. This is useful for calibrating labware locations, calibrating labware sizes, and other things.\n", + "\n", + "```{warning}\n", + "Be very careful about collisions! Move channels to a safe z height before traversing.\n", + "```\n", + "\n", + "```{note}\n", + "With Hamilton robots, when a tip is mounted, the z location will refer to the point of the pipetting tip. With no tip mounted, it will refer to the bottom of the channel.\n", + "```" + ] }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Example: Hamilton STAR\n", + "\n", + "Here, we'll use a Hamilton STAR as an example. For other robots, simply change the deck layout, makign sure that you have at least a tip rack to use." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler, STAR\n", + "from pylabrobot.resources import STARDeck, TIP_CAR_480_A00, HTF\n", + "\n", + "lh = LiquidHandler(backend=STAR(), deck=STARDeck())\n", + "await lh.setup()\n", + "\n", + "# assign a tip rack\n", + "tip_carrier = TIP_CAR_480_A00(name=\"tip_carrier\")\n", + "tip_carrier[0] = tip_rack = HTF(name=\"tip_rack\")\n", + "lh.deck.assign_child_resource(tip_carrier, rails=0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Moving channels\n", + "\n", + "All positions are in mm. The movements are to absolute positions. The origin will be near the left front bottom of the deck, but it differs between robots.\n", + "\n", + "* x: left (low) to right (high)\n", + "* y: front (low) to back (high)\n", + "* z: bottom (low) to top (high)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "channel = 1 # the channel to use\n", + "\n", + "# start by picking up a single tip\n", + "await lh.pick_up_tips(tip_rack[\"A1\"], use_channels=[channel])\n", + "\n", + "# prepare for manual operation\n", + "# this will space the other channels to safe positions\n", + "await lh.prepare_for_manual_channel_operation(channel)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since the channnel will be above the tip rack, it should be safe to move up. We perform a quick check to make sure the z_safe is at least above the resources we know about." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "z_safe = 240 # WARNING: this might NOT be safe for your setup\n", + "\n", + "if z_safe <= lh.deck.get_heighest_known_point():\n", + " raise ValueError(f\"z_safe position is not safe, it is lower than the highest known point: {lh.deck.get_heighest_known_point()}\")\n", + "\n", + "await lh.move_channel_z(channel, z_safe)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```{warning}\n", + "The z position in the code above should be safe for most setups, but we can't guarantee that it will be safe for all setups. Move to a z position that is above all your labware before moving in the xy plane.\n", + "```\n", + "\n", + "When the z position of the bottom of the tip is above the labware, you can move the channel around in the xy plane." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# move the channel around\n", + "await lh.move_channel_x(channel, 100)\n", + "await lh.move_channel_y(channel, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After reaching a spot where the channel can move down, you can use `move_channel_z` again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "await lh.move_channel_z(channel, 100)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Before returning the tip to the tip rack, make sure to move the channel to a safe z position again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "await lh.move_channel_z(channel, z_safe)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can run the code above as often as you like. When you're done, you can return the channel to the tip rack." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "await lh.return_tips()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "env", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 6b71973518be4cab5b1149680f78cf75ce7a2c4f Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 22 Oct 2024 17:21:44 -0700 Subject: [PATCH 7/8] this nb --- docs/user_guide/using-the-visualizer.ipynb | 1376 ++++++++++---------- 1 file changed, 688 insertions(+), 688 deletions(-) diff --git a/docs/user_guide/using-the-visualizer.ipynb b/docs/user_guide/using-the-visualizer.ipynb index d83462abd1..ec35050db4 100644 --- a/docs/user_guide/using-the-visualizer.ipynb +++ b/docs/user_guide/using-the-visualizer.ipynb @@ -1,692 +1,692 @@ { - "cells": [ - { - "cell_type": "markdown", - "id": "c3c71913", - "metadata": {}, - "source": [ - "# Using the Visualizer\n", - "\n", - "The Visualizer is a tool that allows you to visualize the a Resource (like LiquidHandler) including its state to easily see what is going on, for example when executing a protocol on a robot or when developing a new protocol.\n", - "\n", - "When using a backend that does not require access to a physical robot, such as the {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend`, the Visualizer can be used to simulate a robot's behavior. Of course, you may also use the Visualizer when working with a real robot to see what is happening in the PLR resource and state trackers." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "d6cdca52", - "metadata": {}, - "source": [ - "## Setting up a connection with the robot\n", - "\n", - "As described in the [basic liquid handling tutorial](basic), we will use the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` class to control the robot. This time, however, instead of using the Hamilton {class}`~pylabrobot.liquid_handling.backends.hamilton.STAR.STAR` backend, we are using the software-only {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend` backend. This means that liquid handling will work exactly the same, but commands are simply printed out to the console instead of being sent to a physical robot. We are still using the same deck." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "7c5a2629", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import ChatterboxBackend\n", - "from pylabrobot.visualizer.visualizer import Visualizer" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "272520d3", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources.hamilton import STARLetDeck" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "2e280caa", - "metadata": {}, - "outputs": [], - "source": [ - "lh = LiquidHandler(backend=ChatterboxBackend(), deck=STARLetDeck())" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "30dcf4a1", - "metadata": {}, - "source": [ - "Calling {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.setup` will print out \"Setting up the robot\" and also that two resources were assigned: the deck and the trash. Other than that, the chatter box backend has no setup to do." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "1419f2b3", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Setting up the robot.\n", - "Resource deck was assigned to the robot.\n", - "Resource trash was assigned to the robot.\n", - "Resource trash_core96 was assigned to the robot.\n" - ] - } - ], - "source": [ - "await lh.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "871184ce", - "metadata": {}, - "source": [ - "Next, we will create a {class}`~pylabrobot.visualizer.visualizer.Visualizer` object. The Visualizer expects a Resource, and we will pass the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` object to it. This will allow us to visualize the robot's state and actions." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "165c9de4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Websocket server started at http://127.0.0.1:2121\n", - "File server started at http://127.0.0.1:1337 . Open this URL in your browser.\n" - ] - } - ], - "source": [ - "vis = Visualizer(resource=lh)\n", - "await vis.setup()" - ] - }, - { - "cell_type": "markdown", - "id": "3002429b", - "metadata": {}, - "source": [ - "![The empty simulator](./img/visualizer/empty.png)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "050ccefd", - "metadata": {}, - "source": [ - "## Build the deck layout: Assigning plates and tips\n", - "\n", - "When resources are assigned to the root resource of the Visualizer, in this case `lh`, they will automatically appear in the visualization." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "c8c464c8", - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cor_96_wellplate_360ul_Fb,\n", - " HTF\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "b14e3628", - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF(name='tips_05', with_tips=False)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "140872be", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Resource tip carrier was assigned to the robot.\n" - ] - } - ], - "source": [ - "lh.deck.assign_child_resource(tip_car, rails=15)" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "13cbc612", - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", - "plt_car[0] = plate_1 = Cor_96_wellplate_360ul_Fb(name='plate_01')\n", - "plt_car[1] = plate_2 = Cor_96_wellplate_360ul_Fb(name='plate_02')\n", - "plt_car[2] = plate_3 = Cor_96_wellplate_360ul_Fb(name='plate_03')" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "d618ec6a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Resource plate carrier was assigned to the robot.\n" - ] - } - ], - "source": [ - "lh.deck.assign_child_resource(plt_car, rails=8)" - ] - }, - { - "cell_type": "markdown", - "id": "21835f31", - "metadata": {}, - "source": [ - "![The simulator after the resources have been assigned](./img/visualizer/assignment.png)" - ] - }, - { - "cell_type": "markdown", - "id": "68a9721b", - "metadata": {}, - "source": [ - "### Configuring the state of the deck\n", - "\n", - "As with every PyLabRobot script, you have the option of updating the state of the deck before you actually start your method. This will allow PyLabRobot to keep track of what is going on, enabling features like {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.return_tips` and catching errors (like missed tips) before a command would be executed on the robot. With the visualizer, this state has the additional effect of updating the visualization." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "daf99520", - "metadata": {}, - "source": [ - "### Tips\n", - "\n", - "Let's use {func}`~pylabrobot.resources.tip_rack.fill` to place tips at all spots in the tip rack in location `0`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "ca3152d0", - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "tip_rack1.fill()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "26f036c6", - "metadata": {}, - "source": [ - "\n", - "You can precisely control the presence of tips using {func}`~pylabrobot.resources.tip_rack.set_tip_state`. This function allows you to set whether there is a tip in each {class}`~pylabrobot.resources.tip_rack.TipSpot`." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "8f574b8d", - "metadata": {}, - "outputs": [], - "source": [ - "tip_rack4 = lh.deck.get_resource(\"tips_04\")\n", - "tip_rack4.set_tip_state([[True]*6 + [False]*6]*8)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "8e1ed1e6", - "metadata": {}, - "outputs": [], - "source": [ - "tip_rack3.set_tip_state([[True, False]*6]*8)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "890272f1", - "metadata": {}, - "outputs": [], - "source": [ - "tip_rack2.set_tip_state([[True, True, False, False]*3]*8)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "594d8016", - "metadata": {}, - "source": [ - "### Liquids\n", - "\n", - "Adding liquid to wells works similarly. You can use {func}`~pylabrobot.resources.plate.set_well_liquids` to set the liquid in each well of a plate. Each liquid is represented by a tuple where the first element corresponds to the type of liquid and the second to the volume in uL. Here, `None` is used to designate an unknown liquid." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "5b76aac2", - "metadata": {}, - "outputs": [], - "source": [ - "plate_1_liquids = [[(None, 500)]]*96\n", - "plate_1.set_well_liquids(plate_1_liquids)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "400208c7", - "metadata": {}, - "outputs": [], - "source": [ - "plate_2_liquids = [[(None, 100)], [(None, 500)]]*(96//2)\n", - "plate_2.set_well_liquids(plate_2_liquids)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "66281459", - "metadata": {}, - "source": [ - "In the visualizer, you can see that the opacity of the well is proportional to how full the well is relative to its maximum volume." - ] - }, - { - "cell_type": "markdown", - "id": "66a27cfc", - "metadata": {}, - "source": [ - "![Simulator after the tips have been placed and the volumes adjusted](./img/visualizer/resources.png)" - ] - }, - { - "cell_type": "markdown", - "id": "27af93d8", - "metadata": {}, - "source": [ - "## Liquid handling\n", - "\n", - "Once the layout is complete, you can run the same commands as described in the [basic liquid handling tutorial](basic).\n", - "\n", - "It is important that both tip tracking and volume tracking are enabled globally, so that the visualizer can keep track of the state of the tips and the volumes of the liquids." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "ce58298d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "(None, None)" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from pylabrobot.resources import set_tip_tracking, set_volume_tracking\n", - "set_tip_tracking(True), set_volume_tracking(True)" - ] - }, - { - "cell_type": "markdown", - "id": "21c3f2cd", - "metadata": {}, - "source": [ - "### Picking up tips\n", - "\n", - "Note that since we are using the {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend`, we just print out \"Picking up tips\" instead of actually performing an operation. The visualizer will show the tips being picked up." - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "f97eadd4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Picking up tips [Pickup(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_1_1, location=(016.200, 059.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_2_2, location=(025.200, 050.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_3_3, location=(034.200, 041.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" - ] - } - ], - "source": [ - "await lh.pick_up_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "bf46e476", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dropping tips [Drop(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_1_1, location=(016.200, 059.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_2_2, location=(025.200, 050.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_3_3, location=(034.200, 041.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" - ] - } - ], - "source": [ - "await lh.drop_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])" - ] - }, - { - "cell_type": "markdown", - "id": "5c6948b2", - "metadata": {}, - "source": [ - "### Aspirating and dispensing" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "947977c7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Picking up tips [Pickup(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" - ] - } - ], - "source": [ - "await lh.pick_up_tips(tip_rack1[\"A1\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "c25a147f", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Aspirating [Aspiration(resource=Well(name=plate_01_well_1_0, location=(019.870, 070.770, 003.030), size_x=6.86, size_y=6.86, size_z=10.67, category=well), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK), volume=200.0, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[(None, 200.0)])].\n" - ] - } - ], - "source": [ - "await lh.aspirate(plate_1[\"A2\"], vols=[200])" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "e86428ea", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dispensing [Dispense(resource=Well(name=plate_02_well_0_0, location=(010.870, 070.770, 003.030), size_x=6.86, size_y=6.86, size_z=10.67, category=well), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK), volume=200.0, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[(None, 200.0)])].\n" - ] - } - ], - "source": [ - "await lh.dispense(plate_2[\"A1\"], vols=[200])" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "70117e04", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dropping tips [Drop(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" - ] - } - ], - "source": [ - "await lh.return_tips()" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "8a8b3d59", - "metadata": {}, - "source": [ - "### Aspirating using CoRe 96\n", - "\n", - "The CoRe 96 head supports liquid handling operations for 96 channels at once. Here's how to use:\n", - "\n", - "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.pick_up_tips96` for picking up 96 tips;\n", - "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.aspirate96` for aspirating liquid from an entire plate at once;\n", - "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.dispense96` for dispensing liquid to an entire plate at once;\n", - "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.drop_tips96` for dropping tips to the tip rack.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 24, - "id": "b8c5706d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Picking up tips from tips_01.\n" - ] - } - ], - "source": [ - "await lh.pick_up_tips96(tip_rack1)" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "c09144c9", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Aspirating 100 from Plate(name=plate_01, size_x=127.0, size_y=86.0, size_z=42.0, location=(000.000, 000.000, 000.000)).\n" - ] - } - ], - "source": [ - "await lh.aspirate96(plate_1, volume=100)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "2ba711bb", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dispensing 100 to Plate(name=plate_03, size_x=127.0, size_y=86.0, size_z=42.0, location=(000.000, 000.000, 000.000)).\n" - ] - } - ], - "source": [ - "await lh.dispense96(plate_3, volume=100)" - ] - }, - { - "cell_type": "code", - "execution_count": 27, - "id": "6d205ea7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Dropping tips to tips_01.\n" - ] - } - ], - "source": [ - "await lh.drop_tips96(tip_rack1)" - ] - }, - { - "cell_type": "markdown", - "id": "9cc77505", - "metadata": {}, - "source": [ - "![The simulator after the liquid handling operations completed](./img/visualizer/after_lh.png)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "id": "e49020a5", - "metadata": {}, - "source": [ - "## Shutting down\n", - "\n", - "When you're done, you can stop the visualizer by calling {func}`~pylabrobot.visualizer.visualizer.Visualizer.stop`. This will stop the visualization." - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "44a61431", - "metadata": {}, - "outputs": [], - "source": [ - "await vis.stop()" - ] + "cells": [ + { + "cell_type": "markdown", + "id": "c3c71913", + "metadata": {}, + "source": [ + "# Using the Visualizer\n", + "\n", + "The Visualizer is a tool that allows you to visualize the a Resource (like LiquidHandler) including its state to easily see what is going on, for example when executing a protocol on a robot or when developing a new protocol.\n", + "\n", + "When using a backend that does not require access to a physical robot, such as the {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend`, the Visualizer can be used to simulate a robot's behavior. Of course, you may also use the Visualizer when working with a real robot to see what is happening in the PLR resource and state trackers." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d6cdca52", + "metadata": {}, + "source": [ + "## Setting up a connection with the robot\n", + "\n", + "As described in the [basic liquid handling tutorial](basic), we will use the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` class to control the robot. This time, however, instead of using the Hamilton {class}`~pylabrobot.liquid_handling.backends.hamilton.STAR.STAR` backend, we are using the software-only {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend` backend. This means that liquid handling will work exactly the same, but commands are simply printed out to the console instead of being sent to a physical robot. We are still using the same deck." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7c5a2629", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import ChatterboxBackend\n", + "from pylabrobot.visualizer.visualizer import Visualizer" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "272520d3", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources.hamilton import STARLetDeck" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2e280caa", + "metadata": {}, + "outputs": [], + "source": [ + "lh = LiquidHandler(backend=ChatterboxBackend(), deck=STARLetDeck())" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "30dcf4a1", + "metadata": {}, + "source": [ + "Calling {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.setup` will print out \"Setting up the robot\" and also that two resources were assigned: the deck and the trash. Other than that, the chatter box backend has no setup to do." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "1419f2b3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Setting up the robot.\n", + "Resource deck was assigned to the robot.\n", + "Resource trash was assigned to the robot.\n", + "Resource trash_core96 was assigned to the robot.\n" + ] } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.3" - }, - "toc": { - "base_numbering": 1, - "nav_menu": {}, - "number_sections": true, - "sideBar": true, - "skip_h1_title": false, - "title_cell": "Table of Contents", - "title_sidebar": "Contents", - "toc_cell": false, - "toc_position": {}, - "toc_section_display": true, - "toc_window_display": false - }, - "varInspector": { - "cols": { - "lenName": 16, - "lenType": 16, - "lenVar": 40 - }, - "kernels_config": { - "python": { - "delete_cmd_postfix": "", - "delete_cmd_prefix": "del ", - "library": "var_list.py", - "varRefreshCmd": "print(var_dic_list())" - }, - "r": { - "delete_cmd_postfix": ") ", - "delete_cmd_prefix": "rm(", - "library": "var_list.r", - "varRefreshCmd": "cat(var_dic_list()) " - } - }, - "types_to_exclude": [ - "module", - "function", - "builtin_function_or_method", - "instance", - "_Feature" - ], - "window_display": false - }, - "vscode": { - "interpreter": { - "hash": "bf274dfc1b974177267b6b8fba8543eeb0bb4c5d64c637dde420829b05625268" - } + ], + "source": [ + "await lh.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "871184ce", + "metadata": {}, + "source": [ + "Next, we will create a {class}`~pylabrobot.visualizer.visualizer.Visualizer` object. The Visualizer expects a Resource, and we will pass the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` object to it. This will allow us to visualize the robot's state and actions." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "165c9de4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Websocket server started at http://127.0.0.1:2121\n", + "File server started at http://127.0.0.1:1337 . Open this URL in your browser.\n" + ] + } + ], + "source": [ + "vis = Visualizer(resource=lh)\n", + "await vis.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "3002429b", + "metadata": {}, + "source": [ + "![The empty simulator](./img/visualizer/empty.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "050ccefd", + "metadata": {}, + "source": [ + "## Build the deck layout: Assigning plates and tips\n", + "\n", + "When resources are assigned to the root resource of the Visualizer, in this case `lh`, they will automatically appear in the visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c8c464c8", + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cor_96_wellplate_360ul_Fb,\n", + " HTF_L\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "b14e3628", + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", + "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "140872be", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource tip carrier was assigned to the robot.\n" + ] + } + ], + "source": [ + "lh.deck.assign_child_resource(tip_car, rails=15)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "13cbc612", + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name='plate carrier')\n", + "plt_car[0] = plate_1 = Cor_96_wellplate_360ul_Fb(name='plate_01')\n", + "plt_car[1] = plate_2 = Cor_96_wellplate_360ul_Fb(name='plate_02')\n", + "plt_car[2] = plate_3 = Cor_96_wellplate_360ul_Fb(name='plate_03')" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d618ec6a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Resource plate carrier was assigned to the robot.\n" + ] + } + ], + "source": [ + "lh.deck.assign_child_resource(plt_car, rails=8)" + ] + }, + { + "cell_type": "markdown", + "id": "21835f31", + "metadata": {}, + "source": [ + "![The simulator after the resources have been assigned](./img/visualizer/assignment.png)" + ] + }, + { + "cell_type": "markdown", + "id": "68a9721b", + "metadata": {}, + "source": [ + "### Configuring the state of the deck\n", + "\n", + "As with every PyLabRobot script, you have the option of updating the state of the deck before you actually start your method. This will allow PyLabRobot to keep track of what is going on, enabling features like {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.return_tips` and catching errors (like missed tips) before a command would be executed on the robot. With the visualizer, this state has the additional effect of updating the visualization." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "daf99520", + "metadata": {}, + "source": [ + "### Tips\n", + "\n", + "Let's use {func}`~pylabrobot.resources.tip_rack.fill` to place tips at all spots in the tip rack in location `0`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "ca3152d0", + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "tip_rack1.fill()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "26f036c6", + "metadata": {}, + "source": [ + "\n", + "You can precisely control the presence of tips using {func}`~pylabrobot.resources.tip_rack.set_tip_state`. This function allows you to set whether there is a tip in each {class}`~pylabrobot.resources.tip_rack.TipSpot`." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "8f574b8d", + "metadata": {}, + "outputs": [], + "source": [ + "tip_rack4 = lh.deck.get_resource(\"tips_04\")\n", + "tip_rack4.set_tip_state([[True]*6 + [False]*6]*8)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "8e1ed1e6", + "metadata": {}, + "outputs": [], + "source": [ + "tip_rack3.set_tip_state([[True, False]*6]*8)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "890272f1", + "metadata": {}, + "outputs": [], + "source": [ + "tip_rack2.set_tip_state([[True, True, False, False]*3]*8)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "594d8016", + "metadata": {}, + "source": [ + "### Liquids\n", + "\n", + "Adding liquid to wells works similarly. You can use {func}`~pylabrobot.resources.plate.set_well_liquids` to set the liquid in each well of a plate. Each liquid is represented by a tuple where the first element corresponds to the type of liquid and the second to the volume in uL. Here, `None` is used to designate an unknown liquid." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "5b76aac2", + "metadata": {}, + "outputs": [], + "source": [ + "plate_1_liquids = [[(None, 500)]]*96\n", + "plate_1.set_well_liquids(plate_1_liquids)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "400208c7", + "metadata": {}, + "outputs": [], + "source": [ + "plate_2_liquids = [[(None, 100)], [(None, 500)]]*(96//2)\n", + "plate_2.set_well_liquids(plate_2_liquids)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "66281459", + "metadata": {}, + "source": [ + "In the visualizer, you can see that the opacity of the well is proportional to how full the well is relative to its maximum volume." + ] + }, + { + "cell_type": "markdown", + "id": "66a27cfc", + "metadata": {}, + "source": [ + "![Simulator after the tips have been placed and the volumes adjusted](./img/visualizer/resources.png)" + ] + }, + { + "cell_type": "markdown", + "id": "27af93d8", + "metadata": {}, + "source": [ + "## Liquid handling\n", + "\n", + "Once the layout is complete, you can run the same commands as described in the [basic liquid handling tutorial](basic).\n", + "\n", + "It is important that both tip tracking and volume tracking are enabled globally, so that the visualizer can keep track of the state of the tips and the volumes of the liquids." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ce58298d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(None, None)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from pylabrobot.resources import set_tip_tracking, set_volume_tracking\n", + "set_tip_tracking(True), set_volume_tracking(True)" + ] + }, + { + "cell_type": "markdown", + "id": "21c3f2cd", + "metadata": {}, + "source": [ + "### Picking up tips\n", + "\n", + "Note that since we are using the {class}`~pylabrobot.liquid_handling.backends.chatterbox.ChatterboxBackend`, we just print out \"Picking up tips\" instead of actually performing an operation. The visualizer will show the tips being picked up." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "f97eadd4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_1_1, location=(016.200, 059.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_2_2, location=(025.200, 050.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Pickup(resource=TipSpot(name=tips_01_tipspot_3_3, location=(034.200, 041.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "bf46e476", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_1_1, location=(016.200, 059.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_2_2, location=(025.200, 050.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK)), Drop(resource=TipSpot(name=tips_01_tipspot_3_3, location=(034.200, 041.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" + ] + } + ], + "source": [ + "await lh.drop_tips(tip_rack1[\"A1\", \"B2\", \"C3\", \"D4\"])" + ] + }, + { + "cell_type": "markdown", + "id": "5c6948b2", + "metadata": {}, + "source": [ + "### Aspirating and dispensing" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "947977c7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips [Pickup(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips(tip_rack1[\"A1\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "c25a147f", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating [Aspiration(resource=Well(name=plate_01_well_1_0, location=(019.870, 070.770, 003.030), size_x=6.86, size_y=6.86, size_z=10.67, category=well), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK), volume=200.0, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[(None, 200.0)])].\n" + ] + } + ], + "source": [ + "await lh.aspirate(plate_1[\"A2\"], vols=[200])" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e86428ea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dispensing [Dispense(resource=Well(name=plate_02_well_0_0, location=(010.870, 070.770, 003.030), size_x=6.86, size_y=6.86, size_z=10.67, category=well), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK), volume=200.0, flow_rate=None, liquid_height=None, blow_out_air_volume=None, liquids=[(None, 200.0)])].\n" + ] + } + ], + "source": [ + "await lh.dispense(plate_2[\"A1\"], vols=[200])" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "70117e04", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips [Drop(resource=TipSpot(name=tips_01_tipspot_0_0, location=(007.200, 068.300, -83.500), size_x=9.0, size_y=9.0, size_z=0, category=tip_spot), offset=Coordinate(x=0, y=0, z=0), tip=HamiltonTip(HIGH_VOLUME, has_filter=True, maximal_volume=1065, fitting_depth=8, total_tip_length=95.1, pickup_method=OUT_OF_RACK))].\n" + ] + } + ], + "source": [ + "await lh.return_tips()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8a8b3d59", + "metadata": {}, + "source": [ + "### Aspirating using CoRe 96\n", + "\n", + "The CoRe 96 head supports liquid handling operations for 96 channels at once. Here's how to use:\n", + "\n", + "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.pick_up_tips96` for picking up 96 tips;\n", + "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.aspirate96` for aspirating liquid from an entire plate at once;\n", + "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.dispense96` for dispensing liquid to an entire plate at once;\n", + "- {func}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler.drop_tips96` for dropping tips to the tip rack.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "b8c5706d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Picking up tips from tips_01.\n" + ] + } + ], + "source": [ + "await lh.pick_up_tips96(tip_rack1)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "c09144c9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Aspirating 100 from Plate(name=plate_01, size_x=127.0, size_y=86.0, size_z=42.0, location=(000.000, 000.000, 000.000)).\n" + ] + } + ], + "source": [ + "await lh.aspirate96(plate_1, volume=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "2ba711bb", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dispensing 100 to Plate(name=plate_03, size_x=127.0, size_y=86.0, size_z=42.0, location=(000.000, 000.000, 000.000)).\n" + ] + } + ], + "source": [ + "await lh.dispense96(plate_3, volume=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "6d205ea7", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dropping tips to tips_01.\n" + ] + } + ], + "source": [ + "await lh.drop_tips96(tip_rack1)" + ] + }, + { + "cell_type": "markdown", + "id": "9cc77505", + "metadata": {}, + "source": [ + "![The simulator after the liquid handling operations completed](./img/visualizer/after_lh.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "e49020a5", + "metadata": {}, + "source": [ + "## Shutting down\n", + "\n", + "When you're done, you can stop the visualizer by calling {func}`~pylabrobot.visualizer.visualizer.Visualizer.stop`. This will stop the visualization." + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "44a61431", + "metadata": {}, + "outputs": [], + "source": [ + "await vis.stop()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.3" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false }, - "nbformat": 4, - "nbformat_minor": 5 -} \ No newline at end of file + "vscode": { + "interpreter": { + "hash": "bf274dfc1b974177267b6b8fba8543eeb0bb4c5d64c637dde420829b05625268" + } + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 593ee1a86a7a223eb4f3df3e1aa020b127648bb1 Mon Sep 17 00:00:00 2001 From: Rick Wierenga Date: Tue, 22 Oct 2024 17:23:15 -0700 Subject: [PATCH 8/8] ugh --- docs/user_guide/basic.ipynb | 844 ++++++++++----------- docs/user_guide/using-the-visualizer.ipynb | 12 +- 2 files changed, 428 insertions(+), 428 deletions(-) diff --git a/docs/user_guide/basic.ipynb b/docs/user_guide/basic.ipynb index 2e5b614ec5..5d9b6e7a53 100644 --- a/docs/user_guide/basic.ipynb +++ b/docs/user_guide/basic.ipynb @@ -1,426 +1,426 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Getting started with liquid handling on a Hamilton STAR(let)\n", - "\n", - "In this notebook, you will learn how to use PyLabRobot to move water from one range of wells to another.\n", - "\n", - "**Note: before running this notebook, you should have**:\n", - "\n", - "- Installed PyLabRobot and the USB driver as described in [the installation guide](/user_guide/installation).\n", - "- Connected the Hamilton to your computer using the USB cable.\n", - "\n", - "Video of what this code does:\n", - "\n", - "" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Setting up a connection with the robot\n", - "\n", - "Start by importing the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` class, which will serve as a front end for all liquid handling operations.\n", - "\n", - "Backends serve as communicators between `LiquidHandler`s and the actual hardware. Since we are using a Hamilton STAR, we also import the {class}`~pylabrobot.liquid_handling.backends.STAR` backend." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [], - "source": [ - "%load_ext autoreload\n", - "%autoreload 2" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.liquid_handling import LiquidHandler\n", - "from pylabrobot.liquid_handling.backends import STAR" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In addition, import the {class}`~pylabrobot.resources.hamilton.STARLetDeck`, which represents the deck of the Hamilton STAR." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources.hamilton import STARLetDeck" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Create a new liquid handler using `STAR` as its backend." - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [], - "source": [ - "backend = STAR()\n", - "lh = LiquidHandler(backend=backend, deck=STARLetDeck())" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The final step is to open communication with the robot. This is done using the {func}`~pylabrobot.liquid_handling.LiquidHandler.setup` method." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "scrolled": false - }, - "outputs": [], - "source": [ - "await lh.setup()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Creating the deck layout\n", - "\n", - "Now that we have a `LiquidHandler` instance, we can define the deck layout.\n", - "\n", - "The layout in this tutorial will contain five sets of standard volume tips with filter, 1 set of 96 1mL wells, and tip and plate carriers on which these resources are positioned.\n", - "\n", - "Start by importing the relevant objects and variables from the PyLabRobot package. This notebook uses the following resources:\n", - "\n", - "- {class}`~pylabrobot.resources.ml_star.tip_carriers.TIP_CAR_480_A00` tip carrier\n", - "- {class}`~pylabrobot.resources.ml_star.plate_carriers.PLT_CAR_L5AC_A00` plate carrier\n", - "- {class}`~pylabrobot.resources.corning_costar.plates.Cor_96_wellplate_360ul_Fb` wells\n", - "- {class}`~pylabrobot.resources.ml_star.tip_racks.HTF` tips" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "from pylabrobot.resources import (\n", - " TIP_CAR_480_A00,\n", - " PLT_CAR_L5AC_A00,\n", - " Cor_96_wellplate_360ul_Fb,\n", - " HTF,\n", - ")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Then create a tip carrier named `tip carrier`, which will contain tip rack at all 5 positions. These positions can be accessed using `tip_car[x]`, and are 0 indexed." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "tip_car = TIP_CAR_480_A00(name=\"tip carrier\")\n", - "tip_car[0] = HTF(name=\"tips_01\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use {func}`~pylabrobot.resources.abstract.assign_child_resources` to assign the tip carrier to the deck of the liquid handler. All resources contained by this carrier will be assigned automatically.\n", - "\n", - "In the `rails` parameter, we can pass the location of the tip carrier. The locations of the tips will automatically be calculated." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "lh.deck.assign_child_resource(tip_car, rails=3)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Repeat this for the plates." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "plt_car = PLT_CAR_L5AC_A00(name=\"plate carrier\")\n", - "plt_car[0] = Cor_96_wellplate_360ul_Fb(name=\"plate_01\")" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "lh.deck.assign_child_resource(plt_car, rails=15)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's look at a summary of the deck layout using {func}`~pylabrobot.liquid_handling.LiquidHandler.summary`." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Rail Resource Type Coordinates (mm)\n", - "===============================================================================================\n", - "(3) ├── tip carrier TipCarrier (145.000, 063.000, 100.000)\n", - " │ ├── tips_01 TipRack (162.900, 145.800, 131.450)\n", - " │ ├── \n", - " │ ├── \n", - " │ ├── \n", - " │ ├── \n", - " │\n", - "(15) ├── plate carrier PlateCarrier (415.000, 063.000, 100.000)\n", - " │ ├── plate_01 Plate (433.000, 146.000, 187.150)\n", - " │ ├── \n", - " │ ├── \n", - " │ ├── \n", - " │ ├── \n", - " │\n", - "(32) ├── trash Trash (800.000, 190.600, 137.100)\n", - "\n" - ] - } - ], - "source": [ - "lh.summary()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Picking up tips\n", - "\n", - "Picking up tips is as easy as querying the tips from the tiprack." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TTid0004tt01tf1tl0871tv12500tg3tu0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TTid0004er00/00\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TPid0005xp01629 01629 01629 00000&yp1458 1368 1278 0000&tm1 1 1 0&tt01tp2244tz2164th2450td0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TPid0005er00/00\n" - ] - } - ], - "source": [ - "tiprack = lh.deck.get_resource(\"tips_01\")\n", - "await lh.pick_up_tips(tiprack[\"A1:C1\"])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Aspirating and dispensing" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Aspirating and dispensing work similarly to picking up tips: where you use booleans to specify which tips to pick up, with aspiration and dispensing you use floats to specify the volume to aspirate or dispense in $\\mu L$.\n", - "\n", - "The cells below move liquid from wells `'A1:C1'` to `'D1:F1'` using channels 1, 2, and 3 using the {func}`~pylabrobot.liquid_handling.LiquidHandler.aspirate` and {func}`~pylabrobot.liquid_handling.LiquidHandler.dispense` methods." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0ASid0006at0&tm1 1 1 0&xp04330 04330 04330 00000&yp1460 1370 1280 0000&th2450te2450lp1931 1931 1931&ch000 000 000&zl1881 1881 1881&po0100 0100 0100&zu0032 0032 0032&zr06180 06180 06180&zx1831 1831 1831&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&av01072 00551 02110&as1000 1000 1000&ta000 000 000&ba0000 0000 0000&oa000 000 000&lm0 0 0&ll1 1 1&lv1 1 1&zo000 000 000&ld00 00 00&de0020 0020 0020&wt10 10 10&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms1000 1000 1000&mh0000 0000 0000&gi000 000 000&gj0gk0lk0 0 0&ik0000 0000 0000&sd0500 0500 0500&se0500 0500 0500&sz0300 0300 0300&io0000 0000 0000&il00000 00000 00000&in0000 0000 0000&\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0ASid0006er00/00\n" - ] - } - ], - "source": [ - "plate = lh.deck.get_resource(\"plate_01\")\n", - "await lh.aspirate(plate[\"A1:C1\"], vols=[100.0, 50.0, 200.0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "After the liquid has been aspirated, dispense it in the wells below. Note that while we specify different wells, we are still using the same channels. This is needed because only these channels contain liquid, of course." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0DSid0007dm2 2 2&tm1 1 1 0&xp04330 04330 04330 00000&yp1190 1100 1010 0000&zx1871 1871 1871&lp2321 2321 2321&zl1881 1881 1881&po0100 0100 0100&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&zu0032 0032 0032&zr06180 06180 06180&th2450te2450dv01072 00551 02110&ds1200 1200 1200&ss0050 0050 0050&rv000 000 000&ta000 000 000&ba0000 0000 0000&lm0 0 0&dj00zo000 000 000&ll1 1 1&lv1 1 1&de0020 0020 0020&wt00 00 00&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms0010 0010 0010&mh0000 0000 0000&gi000 000 000&gj0gk0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0DSid0007er00/00\n" - ] - } - ], - "source": [ - "await lh.dispense(plate[\"D1:F1\"], vols=[100.0, 50.0, 200.0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Let's move the liquid back to the original wells." - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0ASid0008at0&tm1 1 1 0&xp04330 04330 04330 00000&yp1190 1100 1010 0000&th2450te2450lp1931 1931 1931&ch000 000 000&zl1881 1881 1881&po0100 0100 0100&zu0032 0032 0032&zr06180 06180 06180&zx1831 1831 1831&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&av01072 00551 02110&as1000 1000 1000&ta000 000 000&ba0000 0000 0000&oa000 000 000&lm0 0 0&ll1 1 1&lv1 1 1&zo000 000 000&ld00 00 00&de0020 0020 0020&wt10 10 10&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms1000 1000 1000&mh0000 0000 0000&gi000 000 000&gj0gk0lk0 0 0&ik0000 0000 0000&sd0500 0500 0500&se0500 0500 0500&sz0300 0300 0300&io0000 0000 0000&il00000 00000 00000&in0000 0000 0000&\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0ASid0008er00/00\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0DSid0009dm2 2 2&tm1 1 1 0&xp04330 04330 04330 00000&yp1460 1370 1280 0000&zx1871 1871 1871&lp2321 2321 2321&zl1881 1881 1881&po0100 0100 0100&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&zu0032 0032 0032&zr06180 06180 06180&th2450te2450dv01072 00551 02110&ds1200 1200 1200&ss0050 0050 0050&rv000 000 000&ta000 000 000&ba0000 0000 0000&lm0 0 0&dj00zo000 000 000&ll1 1 1&lv1 1 1&de0020 0020 0020&wt00 00 00&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms0010 0010 0010&mh0000 0000 0000&gi000 000 000&gj0gk0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0DSid0009er00/00\n" - ] - } - ], - "source": [ - "await lh.aspirate(plate[\"D1:F1\"], vols=[100.0, 50.0, 200.0])\n", - "await lh.dispense(plate[\"A1:C1\"], vols=[100.0, 50.0, 200.0])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Dropping tips\n", - "\n", - "Finally, you can drop tips anywhere on the deck by using the {func}`~pylabrobot.liquid_handling.LiquidHandler.discard_tips` method." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TRid0010xp01629 01629 01629 00000&yp1458 1368 1278 0000&tm1 1 1 0&tt01tp1314tz1414th2450ti0\n", - "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TRid0010er00/00kz381 356 365 000 000 000 000 000vz303 360 368 000 000 000 000 000\n" - ] - } - ], - "source": [ - "await lh.drop_tips(tiprack[\"A1:C1\"])" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "WARNING:root:Closing connection to USB device.\n" - ] - } - ], - "source": [ - "await lh.stop()" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Getting started with liquid handling on a Hamilton STAR(let)\n", + "\n", + "In this notebook, you will learn how to use PyLabRobot to move water from one range of wells to another.\n", + "\n", + "**Note: before running this notebook, you should have**:\n", + "\n", + "- Installed PyLabRobot and the USB driver as described in [the installation guide](/user_guide/installation).\n", + "- Connected the Hamilton to your computer using the USB cable.\n", + "\n", + "Video of what this code does:\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setting up a connection with the robot\n", + "\n", + "Start by importing the {class}`~pylabrobot.liquid_handling.liquid_handler.LiquidHandler` class, which will serve as a front end for all liquid handling operations.\n", + "\n", + "Backends serve as communicators between `LiquidHandler`s and the actual hardware. Since we are using a Hamilton STAR, we also import the {class}`~pylabrobot.liquid_handling.backends.STAR` backend." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.liquid_handling import LiquidHandler\n", + "from pylabrobot.liquid_handling.backends import STAR" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition, import the {class}`~pylabrobot.resources.hamilton.STARLetDeck`, which represents the deck of the Hamilton STAR." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources.hamilton import STARLetDeck" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create a new liquid handler using `STAR` as its backend." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "backend = STAR()\n", + "lh = LiquidHandler(backend=backend, deck=STARLetDeck())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The final step is to open communication with the robot. This is done using the {func}`~pylabrobot.liquid_handling.LiquidHandler.setup` method." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "await lh.setup()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Creating the deck layout\n", + "\n", + "Now that we have a `LiquidHandler` instance, we can define the deck layout.\n", + "\n", + "The layout in this tutorial will contain five sets of standard volume tips with filter, 1 set of 96 1mL wells, and tip and plate carriers on which these resources are positioned.\n", + "\n", + "Start by importing the relevant objects and variables from the PyLabRobot package. This notebook uses the following resources:\n", + "\n", + "- {class}`~pylabrobot.resources.ml_star.tip_carriers.TIP_CAR_480_A00` tip carrier\n", + "- {class}`~pylabrobot.resources.ml_star.plate_carriers.PLT_CAR_L5AC_A00` plate carrier\n", + "- {class}`~pylabrobot.resources.corning_costar.plates.Cor_96_wellplate_360ul_Fb` wells\n", + "- {class}`~pylabrobot.resources.ml_star.tip_racks.HTF` tips" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "from pylabrobot.resources import (\n", + " TIP_CAR_480_A00,\n", + " PLT_CAR_L5AC_A00,\n", + " Cor_96_wellplate_360ul_Fb,\n", + " HTF,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Then create a tip carrier named `tip carrier`, which will contain tip rack at all 5 positions. These positions can be accessed using `tip_car[x]`, and are 0 indexed." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "tip_car = TIP_CAR_480_A00(name=\"tip carrier\")\n", + "tip_car[0] = HTF(name=\"tips_01\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use {func}`~pylabrobot.resources.abstract.assign_child_resources` to assign the tip carrier to the deck of the liquid handler. All resources contained by this carrier will be assigned automatically.\n", + "\n", + "In the `rails` parameter, we can pass the location of the tip carrier. The locations of the tips will automatically be calculated." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "lh.deck.assign_child_resource(tip_car, rails=3)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Repeat this for the plates." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "plt_car = PLT_CAR_L5AC_A00(name=\"plate carrier\")\n", + "plt_car[0] = Cor_96_wellplate_360ul_Fb(name=\"plate_01\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "lh.deck.assign_child_resource(plt_car, rails=15)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's look at a summary of the deck layout using {func}`~pylabrobot.liquid_handling.LiquidHandler.summary`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rail Resource Type Coordinates (mm)\n", + "===============================================================================================\n", + "(3) ├── tip carrier TipCarrier (145.000, 063.000, 100.000)\n", + " │ ├── tips_01 TipRack (162.900, 145.800, 131.450)\n", + " │ ├── \n", + " │ ├── \n", + " │ ├── \n", + " │ ├── \n", + " │\n", + "(15) ├── plate carrier PlateCarrier (415.000, 063.000, 100.000)\n", + " │ ├── plate_01 Plate (433.000, 146.000, 187.150)\n", + " │ ├── \n", + " │ ├── \n", + " │ ├── \n", + " │ ├── \n", + " │\n", + "(32) ├── trash Trash (800.000, 190.600, 137.100)\n", + "\n" + ] } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.9.13 ('env': venv)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.8" - }, - "vscode": { - "interpreter": { - "hash": "bf274dfc1b974177267b6b8fba8543eeb0bb4c5d64c637dde420829b05625268" - } + ], + "source": [ + "lh.summary()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Picking up tips\n", + "\n", + "Picking up tips is as easy as querying the tips from the tiprack." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TTid0004tt01tf1tl0871tv12500tg3tu0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TTid0004er00/00\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TPid0005xp01629 01629 01629 00000&yp1458 1368 1278 0000&tm1 1 1 0&tt01tp2244tz2164th2450td0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TPid0005er00/00\n" + ] + } + ], + "source": [ + "tiprack = lh.deck.get_resource(\"tips_01\")\n", + "await lh.pick_up_tips(tiprack[\"A1:C1\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Aspirating and dispensing" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Aspirating and dispensing work similarly to picking up tips: where you use booleans to specify which tips to pick up, with aspiration and dispensing you use floats to specify the volume to aspirate or dispense in $\\mu L$.\n", + "\n", + "The cells below move liquid from wells `'A1:C1'` to `'D1:F1'` using channels 1, 2, and 3 using the {func}`~pylabrobot.liquid_handling.LiquidHandler.aspirate` and {func}`~pylabrobot.liquid_handling.LiquidHandler.dispense` methods." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0ASid0006at0&tm1 1 1 0&xp04330 04330 04330 00000&yp1460 1370 1280 0000&th2450te2450lp1931 1931 1931&ch000 000 000&zl1881 1881 1881&po0100 0100 0100&zu0032 0032 0032&zr06180 06180 06180&zx1831 1831 1831&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&av01072 00551 02110&as1000 1000 1000&ta000 000 000&ba0000 0000 0000&oa000 000 000&lm0 0 0&ll1 1 1&lv1 1 1&zo000 000 000&ld00 00 00&de0020 0020 0020&wt10 10 10&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms1000 1000 1000&mh0000 0000 0000&gi000 000 000&gj0gk0lk0 0 0&ik0000 0000 0000&sd0500 0500 0500&se0500 0500 0500&sz0300 0300 0300&io0000 0000 0000&il00000 00000 00000&in0000 0000 0000&\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0ASid0006er00/00\n" + ] } + ], + "source": [ + "plate = lh.deck.get_resource(\"plate_01\")\n", + "await lh.aspirate(plate[\"A1:C1\"], vols=[100.0, 50.0, 200.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After the liquid has been aspirated, dispense it in the wells below. Note that while we specify different wells, we are still using the same channels. This is needed because only these channels contain liquid, of course." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0DSid0007dm2 2 2&tm1 1 1 0&xp04330 04330 04330 00000&yp1190 1100 1010 0000&zx1871 1871 1871&lp2321 2321 2321&zl1881 1881 1881&po0100 0100 0100&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&zu0032 0032 0032&zr06180 06180 06180&th2450te2450dv01072 00551 02110&ds1200 1200 1200&ss0050 0050 0050&rv000 000 000&ta000 000 000&ba0000 0000 0000&lm0 0 0&dj00zo000 000 000&ll1 1 1&lv1 1 1&de0020 0020 0020&wt00 00 00&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms0010 0010 0010&mh0000 0000 0000&gi000 000 000&gj0gk0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0DSid0007er00/00\n" + ] + } + ], + "source": [ + "await lh.dispense(plate[\"D1:F1\"], vols=[100.0, 50.0, 200.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's move the liquid back to the original wells." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0ASid0008at0&tm1 1 1 0&xp04330 04330 04330 00000&yp1190 1100 1010 0000&th2450te2450lp1931 1931 1931&ch000 000 000&zl1881 1881 1881&po0100 0100 0100&zu0032 0032 0032&zr06180 06180 06180&zx1831 1831 1831&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&av01072 00551 02110&as1000 1000 1000&ta000 000 000&ba0000 0000 0000&oa000 000 000&lm0 0 0&ll1 1 1&lv1 1 1&zo000 000 000&ld00 00 00&de0020 0020 0020&wt10 10 10&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms1000 1000 1000&mh0000 0000 0000&gi000 000 000&gj0gk0lk0 0 0&ik0000 0000 0000&sd0500 0500 0500&se0500 0500 0500&sz0300 0300 0300&io0000 0000 0000&il00000 00000 00000&in0000 0000 0000&\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0ASid0008er00/00\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0DSid0009dm2 2 2&tm1 1 1 0&xp04330 04330 04330 00000&yp1460 1370 1280 0000&zx1871 1871 1871&lp2321 2321 2321&zl1881 1881 1881&po0100 0100 0100&ip0000 0000 0000&it0 0 0&fp0000 0000 0000&zu0032 0032 0032&zr06180 06180 06180&th2450te2450dv01072 00551 02110&ds1200 1200 1200&ss0050 0050 0050&rv000 000 000&ta000 000 000&ba0000 0000 0000&lm0 0 0&dj00zo000 000 000&ll1 1 1&lv1 1 1&de0020 0020 0020&wt00 00 00&mv00000 00000 00000&mc00 00 00&mp000 000 000&ms0010 0010 0010&mh0000 0000 0000&gi000 000 000&gj0gk0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0DSid0009er00/00\n" + ] + } + ], + "source": [ + "await lh.aspirate(plate[\"D1:F1\"], vols=[100.0, 50.0, 200.0])\n", + "await lh.dispense(plate[\"A1:C1\"], vols=[100.0, 50.0, 200.0])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dropping tips\n", + "\n", + "Finally, you can drop tips anywhere on the deck by using the {func}`~pylabrobot.liquid_handling.LiquidHandler.discard_tips` method." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Sent command: C0TRid0010xp01629 01629 01629 00000&yp1458 1368 1278 0000&tm1 1 1 0&tt01tp1314tz1414th2450ti0\n", + "INFO:pylabrobot.liquid_handling.backends.hamilton.STAR:Received response: C0TRid0010er00/00kz381 356 365 000 000 000 000 000vz303 360 368 000 000 000 000 000\n" + ] + } + ], + "source": [ + "await lh.drop_tips(tiprack[\"A1:C1\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:root:Closing connection to USB device.\n" + ] + } + ], + "source": [ + "await lh.stop()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3.9.13 ('env': venv)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.8" }, - "nbformat": 4, - "nbformat_minor": 2 -} \ No newline at end of file + "vscode": { + "interpreter": { + "hash": "bf274dfc1b974177267b6b8fba8543eeb0bb4c5d64c637dde420829b05625268" + } + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/docs/user_guide/using-the-visualizer.ipynb b/docs/user_guide/using-the-visualizer.ipynb index ec35050db4..6158a7eeb8 100644 --- a/docs/user_guide/using-the-visualizer.ipynb +++ b/docs/user_guide/using-the-visualizer.ipynb @@ -143,7 +143,7 @@ " TIP_CAR_480_A00,\n", " PLT_CAR_L5AC_A00,\n", " Cor_96_wellplate_360ul_Fb,\n", - " HTF_L\n", + " HTF\n", ")" ] }, @@ -155,11 +155,11 @@ "outputs": [], "source": [ "tip_car = TIP_CAR_480_A00(name='tip carrier')\n", - "tip_car[0] = tip_rack1 = HTF_L(name='tips_01', with_tips=False)\n", - "tip_car[1] = tip_rack2 = HTF_L(name='tips_02', with_tips=False)\n", - "tip_car[2] = tip_rack3 = HTF_L(name='tips_03', with_tips=False)\n", - "tip_car[3] = tip_rack4 = HTF_L(name='tips_04', with_tips=False)\n", - "tip_car[4] = tip_rack5 = HTF_L(name='tips_05', with_tips=False)" + "tip_car[0] = tip_rack1 = HTF(name='tips_01', with_tips=False)\n", + "tip_car[1] = tip_rack2 = HTF(name='tips_02', with_tips=False)\n", + "tip_car[2] = tip_rack3 = HTF(name='tips_03', with_tips=False)\n", + "tip_car[3] = tip_rack4 = HTF(name='tips_04', with_tips=False)\n", + "tip_car[4] = tip_rack5 = HTF(name='tips_05', with_tips=False)" ] }, {