OTProto_Template Superclass Walkthrough¶
Overview¶
The OTProto_Template class is a superclass intended to be used in the development of OTProto templates. In BiomationScripter, Templates can be used to help create protocols which will be used often, but with different inputs or variables. For example, the Opentrons can be used to prepare transformations. The general steps for most heat shock transformations are the same: add DNA to competent cells, heat shock, and add media. However, transformation protocols will differ in aspects such as the DNA used and the volume of DNA, competent cells, and media. In this case, an OTProto Template could be set up for heat shock transformations where the main steps are encoded within the Template, but the exact transfer events vary based on user inputs.
Below is an example of how the OTProto_Template superclass can be used to write an OTProto Template. Templates should be added into their own file named <PROTOCOL>.py and placed within the BiomationScripter/OTProto/Templates directory.
Note
Once you've finished this walkthrough, it is recommended that you view the full documentation for information on additional methods not mentioned here.
Defining the Protocol¶
In this example, an OTProto Template for mixing coloured solutions is created. This exact requirements for this Template are listed below:
- The Template will take a list of coloured solutions
- In a destination plate, 2-colour mixtures will be prepared in equal amounts
- The user should be able to define the final volume of each mixture
- The user should be able to define if the mixtures are permuations or combinations
Setting Up¶
To begin, the BMS generic tools and OTProto tools are imported.
import BiomationScripter as BMS
from BiomationScripter import OTProto
We also need the math module, so we'll import that now too
import math
Next, the Template class can be defined. Templates are defined as a Python class, which extends the OTProto_Template superclass. Note that his class must be named Template:
class Template(OTProto.OTProto_Template):
def __init__(self):
pass # This is used to say "do nothing". It will be removed in the next step once we want our Template to do something.
Protocol_Template = Template() # This creates the protocol using our Template class. At the moment, our Template does nothing.
print(Protocol_Template)
<__main__.Template object at 0x0000017807835E80>
Default Arguments¶
The OTProto_Template superclass takes seven arguments: Protocol, Name, Metadata, Custom_Labware_Dir, Starting_20uL_Tip, Starting_300uL_Tip, and Starting_1000uL_Tip.
Protocol|opentrons.protocol_api.contexts.ProtocolContext: The protocol object which is used by the Opentrons API to encapsulate all information relating to the current protocolName|str: A readable name for the protocol to be created by the templateMetadata|dict{str: str}: This is metadata about the protocol - it should follow the best practices described hereCustom_Labware_Dir|str: Directory location pointing to where any custom labware definitions are storedStarting_20uL_Tip|str = "A1": The position of the starting tip in the first p20 tip boxStarting_300uL_Tip|str = "A1": The position of the starting tip in the first p300 tip boxStarting_1000uL_Tip|str = "A1": The position of the starting tip in the first p1000 tip box
These arguments get passed from our template to the superclass using the super() method, as shown below:
class Template(OTProto.OTProto_Template):
def __init__(
self,
**kwargs # This will make the superclass arguments available to our template as keyword arguments
):
super().__init__(**kwargs) # This passes the keyword arguments to the superclass
Now let's use these arguments in our Template class.
First we'll define the name of the protocol and the metadata:
Protocol_Name = "Colour Mixing Example"
metadata = {
'protocolName': Protocol_Name,
'author': 'Bradley Brown',
'author-email': 'b.bradley2@newcastle.ac.uk',
'user': '',
'user-email': '',
'source': 'BiomationScripter Examples - BMS v0.2.0.dev',
'apiLevel': '2.11',
'robotName': 'RobOT2' # This is the name of the OT2 you plan to run the protocol on
}
Next we'll define the opentrons.protocol_api.contexts.ProtocolContext object.
Note
In normal use, the opentrons.protocol_api.contexts.ProtocolContext object supplied to Protocol would be generated by the Opentrons during execution. For this walkthrough, we'll generate it ourselves.
from opentrons import simulate as OT2
protocol = OT2.get_protocol_api(metadata["apiLevel"])
protocol.home()
C:\Users\bradl\.opentrons\robot_settings.json not found. Loading defaults C:\Users\bradl\.opentrons\deck_calibration.json not found. Loading defaults
Finally we'll define the directory containing of our custom labware definitions. Note that this isn't needed for execution by the Opentrons; it is used when simulating on a separate device.
Custom_Labware_Directory = "../../../data/custom_labware"
We'll ignore the Starting Tip arguments for now as they aren't required.
The information defined above can be used to instantiate our Template class. We can then retrieve this information using the class' attributes:
Protocol_Template = Template(
Protocol = protocol,
Name = Protocol_Name,
Metadata = metadata,
Custom_Labware_Dir = Custom_Labware_Directory
)
print(Protocol_Template)
print("\nname:", Protocol_Template.name)
print("\nprotocol object:", Protocol_Template._protocol)
print("\nmetadata:", Protocol_Template.metadata)
print("\ncustom_labware_dir:", Protocol_Template.custom_labware_dir)
<__main__.Template object at 0x00000178078B9D60>
name: Colour Mixing Example
protocol object: <opentrons.protocol_api.protocol_context.ProtocolContext object at 0x0000017807894DF0>
metadata: {'protocolName': 'Colour Mixing Example', 'author': 'Bradley Brown', 'author-email': 'b.bradley2@newcastle.ac.uk', 'user': '', 'user-email': '', 'source': 'BiomationScripter Examples - BMS v0.2.0.dev', 'apiLevel': '2.11', 'robotName': 'RobOT2'}
custom_labware_dir: ../../../data/custom_labware
Template classes which extend the OTProto_Template superclass also have several other attributes generate during instantiation. These are shown below.
tip_types is a dictionary recording the pipette tip labware to be used by each of the supported pipette types. The dictionary keys are "P20", "p300", "P1000", which refer to the three gen2 single channel pipette types available. The default values for each of these keys are "opentrons_96_tiprack_20ul", "opentrons_96_tiprack_300ul", "opentrons_96_tiprack_1000ul" respectively
Protocol_Template.tip_types
{'p20': 'opentrons_96_tiprack_20ul',
'p300': 'opentrons_96_tiprack_300ul',
'p1000': 'opentrons_96_tiprack_1000ul'}
starting_tips is A dictionary recording the starting well location for each supported pipette type. The starting tip is assumed to be located in the first tip box loaded onto the deck which is associated with that pipette. The dictionary keys are "p20", "p300", "p1000", which refer to the three gen2 single channel pipette types supported by BiomationScripter.
Protocol_Template.starting_tips
{'p20': 'A1', 'p300': 'A1', 'p1000': 'A1'}
tips_needed is a dictionary which records how many of each tip type is required to complete the protocol generated by the template. The dictionary keys are the same as for tip_types
Protocol_Template.tips_needed
{'p20': 0, 'p300': 0, 'p1000': 0}
_pipettes is a dictionary which records the type of pipette mounted in each position of the Opentrons' pipette mount. The keys are "left", "right". The default values for each of these keys are "p20_single_gen2", "p300_single_gen2" respectively.
Protocol_Template._pipettes
{'left': 'p20_single_gen2', 'right': 'p300_single_gen2'}
Custom Arguments¶
For our Colour_Mixing Opentrons Template, based on the requirements defined above, we need six extra arguments. These are:
Source_Labware_Type: a string defining the Opentrons labware API name for the type of labware which will be used to store the coloured solutionsDestination_Labware_Type: a string defining the Opentrons labware API name for the type of labware which will be used to mix the coloured solutionsSource_Colours: a list of strings defining the names of the colour source solutionsSource_Colours_Aliquot_Volume: a float (in microlitres) stating the volume of the source colourant aliquotsFinal_Volume: the final volume, in microlitres, of the coloured mixturesPermutations: a boolean (True/False) defining whether the full permutations of colours should be mixed. We'll give this a default value ofFalse
These arguments are added to our Template class as shown below. We'll also add some attributes to store the values of these arguments.
class Template(OTProto.OTProto_Template):
def __init__(
self,
Source_Labware_Type,
Destination_Labware_Type,
Source_Colours,
Source_Colours_Aliquot_Volume,
Final_Volume,
Permutations = False,
**kwargs # This will make the superclass arguments available to our template as keyword arguments
):
super().__init__(**kwargs) # This passes the keyword arguments to the superclass
#######################
# User Defined Values #
#######################
self.source_labware_type = Source_Labware_Type
self.destination_labware_type = Destination_Labware_Type
self.source_colours = Source_Colours
self.source_colour_aliquot_volumes = Source_Colours_Aliquot_Volume
self.final_volume = Final_Volume # uL
self.permutations = Permutations
Below we check that everything is working as expected:
Protocol_Name = "Colour Mixing Example"
metadata = {
'protocolName': Protocol_Name,
'author': 'Bradley Brown',
'author-email': 'b.bradley2@newcastle.ac.uk',
'user': '',
'user-email': '',
'source': 'BiomationScripter Examples - BMS v0.2.0.dev',
'apiLevel': '2.11',
'robotName': 'RobOT2' # This is the name of the OT2 you plan to run the protocol on
}
Custom_Labware_Directory = "../../../data/custom_labware"
protocol = OT2.get_protocol_api(metadata["apiLevel"])
protocol.home()
C:\Users\bradl\.opentrons\robot_settings.json not found. Loading defaults C:\Users\bradl\.opentrons\deck_calibration.json not found. Loading defaults
Source_Labware_Type = "opentrons_24_aluminumblock_nest_1.5ml_snapcap"
Destination_Labware_Type = "biorad_96_wellplate_200ul_pcr"
Source_Colours = ["Red", "Blue", "Yellow"]
Source_Colours_Aliquot_Volume = 1000
Final_Volume = 100
Permutations = False
Protocol_Template = Template(
Source_Labware_Type = Source_Labware_Type,
Destination_Labware_Type = Destination_Labware_Type,
Source_Colours = Source_Colours,
Source_Colours_Aliquot_Volume = Source_Colours_Aliquot_Volume,
Final_Volume = Final_Volume,
Permutations = Permutations,
Protocol = protocol,
Name = Protocol_Name,
Metadata = metadata,
Custom_Labware_Dir = Custom_Labware_Directory
)
print(Protocol_Template)
print("\nname:", Protocol_Template.name)
print("\nprotocol object:", Protocol_Template._protocol)
print("\nmetadata:", Protocol_Template.metadata)
print("\ncustom_labware_dir:", Protocol_Template.custom_labware_dir)
print("\nsource_labware_type:", Protocol_Template.source_labware_type)
print("\ndestination_labware_type:", Protocol_Template.destination_labware_type)
print("\nsource_colours:", Protocol_Template.source_colours)
print("\nsource_colour_aliquot_volumes:", Protocol_Template.source_colour_aliquot_volumes)
print("\nfinal_volume:", Protocol_Template.final_volume)
print("\npermutations:", Protocol_Template.permutations)
<__main__.Template object at 0x0000017809908EE0>
name: Colour Mixing Example
protocol object: <opentrons.protocol_api.protocol_context.ProtocolContext object at 0x00000178098FF6A0>
metadata: {'protocolName': 'Colour Mixing Example', 'author': 'Bradley Brown', 'author-email': 'b.bradley2@newcastle.ac.uk', 'user': '', 'user-email': '', 'source': 'BiomationScripter Examples - BMS v0.2.0.dev', 'apiLevel': '2.11', 'robotName': 'RobOT2'}
custom_labware_dir: ../../../data/custom_labware
source_labware_type: opentrons_24_aluminumblock_nest_1.5ml_snapcap
destination_labware_type: biorad_96_wellplate_200ul_pcr
source_colours: ['Red', 'Blue', 'Yellow']
source_colour_aliquot_volumes: 1000
final_volume: 100
permutations: False
Adding Functionality¶
We now have the basis of our Template class, but it still doesn't really do anything. The next step is to add code which will set up the destination plate based on the basic functionality and user inputs.
For OTProto Templates, this code is contained within a run method. The first thing any OTProto template should do is to load the pipettes. This method is added to our class below, along with the load_pipettes method.
class Template(OTProto.OTProto_Template):
def __init__(
self,
Source_Labware_Type,
Destination_Labware_Type,
Source_Colours,
Source_Colours_Aliquot_Volume,
Final_Volume,
Permutations = False,
**kwargs # This will make the superclass arguments available to our template as keyword arguments
):
super().__init__(**kwargs) # This passes the keyword arguments to the superclass
#######################
# User Defined Values #
#######################
self.source_labware_type = Source_Labware_Type
self.destination_labware_type = Destination_Labware_Type
self.source_colours = Source_Colours
self.source_colour_aliquot_volumes = Source_Colours_Aliquot_Volume
self.final_volume = Final_Volume # uL
self.permutations = Permutations
def run(self):
self.load_pipettes()
Determine Mixtures¶
The first step in our protocol will be to determine the colour mixtures required. To do this, we'll use the code below:
# Set up an empty list in which the colour mixtures required will be added
Colour_Mixtures = []
# Iterate through the list of source colours provided to create the list of mixtures.
for colour_1 in self.source_colours:
for colour_2 in self.source_colours:
# Ignore situations where colour_1 and colour_2 are the same
if colour_1 == colour_2:
continue
# Unless permutations has been set to `True`, ignore situations where...
# ...the same colours have already been mixed, just in a different order
elif not self.permutations and [colour_2, colour_1] in Colour_Mixtures:
continue
else:
# Add the two colours to the list of mixtures to prepare
Colour_Mixtures.append([colour_1, colour_2])
# Here, we'll print to OUT all of the mixtures which will be prepared
for c in Colour_Mixtures:
print(c)
This code can then be added to our run method:
class Template(OTProto.OTProto_Template):
def __init__(
self,
Source_Labware_Type,
Destination_Labware_Type,
Source_Colours,
Source_Colours_Aliquot_Volume,
Final_Volume,
Permutations = False,
**kwargs # This will make the superclass arguments available to our template as keyword arguments
):
super().__init__(**kwargs) # This passes the keyword arguments to the superclass
#######################
# User Defined Values #
#######################
self.source_labware_type = Source_Labware_Type
self.destination_labware_type = Destination_Labware_Type
self.source_colours = Source_Colours
self.source_colour_aliquot_volumes = Source_Colours_Aliquot_Volume
self.final_volume = Final_Volume # uL
self.permutations = Permutations
def run(self):
self.load_pipettes()
# Set up an empty list in which the colour mixtures required will be added
Colour_Mixtures = []
# Iterate through the list of source colours provided to create the list of mixtures.
for colour_1 in self.source_colours:
for colour_2 in self.source_colours:
# Ignore situations where colour_1 and colour_2 are the same
if colour_1 == colour_2:
continue
# Unless permutations has been set to `True`, ignore situations where...
# ...the same colours have already been mixed, just in a different order
elif not self.permutations and [colour_2, colour_1] in Colour_Mixtures:
continue
else:
# Add the two colours to the list of mixtures to prepare
Colour_Mixtures.append([colour_1, colour_2])
# Here, we'll print to OUT all of the mixtures which will be prepared
for c in Colour_Mixtures:
print(c)
We can check the functionality of our code by calling the run method on the Protocol object
# When Permutations is True
Protocol_Name = "Colour Mixing Example"
metadata = {
'protocolName': Protocol_Name,
'author': 'Bradley Brown',
'author-email': 'b.bradley2@newcastle.ac.uk',
'user': '',
'user-email': '',
'source': 'BiomationScripter Examples - BMS v0.2.0.dev',
'apiLevel': '2.11',
'robotName': 'RobOT2' # This is the name of the OT2 you plan to run the protocol on
}
Custom_Labware_Directory = "../../../data/custom_labware"
protocol = OT2.get_protocol_api(metadata["apiLevel"])
protocol.home()
Source_Labware_Type = "opentrons_24_aluminumblock_nest_1.5ml_snapcap"
Destination_Labware_Type = "biorad_96_wellplate_200ul_pcr"
Source_Colours = ["Red", "Blue", "Yellow"]
Source_Colours_Aliquot_Volume = 1000
Final_Volume = 100
Permutations = True
Protocol_Template = Template(
Source_Labware_Type = Source_Labware_Type,
Destination_Labware_Type = Destination_Labware_Type,
Source_Colours = Source_Colours,
Source_Colours_Aliquot_Volume = Source_Colours_Aliquot_Volume,
Final_Volume = Final_Volume,
Permutations = Permutations,
Protocol = protocol,
Name = Protocol_Name,
Metadata = metadata,
Custom_Labware_Dir = Custom_Labware_Directory
)
Protocol_Template.run()
C:\Users\bradl\.opentrons\robot_settings.json not found. Loading defaults C:\Users\bradl\.opentrons\deck_calibration.json not found. Loading defaults
['Red', 'Blue'] ['Red', 'Yellow'] ['Blue', 'Red'] ['Blue', 'Yellow'] ['Yellow', 'Red'] ['Yellow', 'Blue']
# When Permutations is False
Protocol_Name = "Colour Mixing Example"
metadata = {
'protocolName': Protocol_Name,
'author': 'Bradley Brown',
'author-email': 'b.bradley2@newcastle.ac.uk',
'user': '',
'user-email': '',
'source': 'BiomationScripter Examples - BMS v0.2.0.dev',
'apiLevel': '2.11',
'robotName': 'RobOT2' # This is the name of the OT2 you plan to run the protocol on
}
Custom_Labware_Directory = "../../../data/custom_labware"
protocol = OT2.get_protocol_api(metadata["apiLevel"])
protocol.home()
Source_Labware_Type = "opentrons_24_aluminumblock_nest_1.5ml_snapcap"
Destination_Labware_Type = "biorad_96_wellplate_200ul_pcr"
Source_Colours = ["Red", "Blue", "Yellow"]
Source_Colours_Aliquot_Volume = 1000
Final_Volume = 100
Permutations = False
Protocol_Template = Template(
Source_Labware_Type = Source_Labware_Type,
Destination_Labware_Type = Destination_Labware_Type,
Source_Colours = Source_Colours,
Source_Colours_Aliquot_Volume = Source_Colours_Aliquot_Volume,
Final_Volume = Final_Volume,
Permutations = Permutations,
Protocol = protocol,
Name = Protocol_Name,
Metadata = metadata,
Custom_Labware_Dir = Custom_Labware_Directory
)
Protocol_Template.run()
C:\Users\bradl\.opentrons\robot_settings.json not found. Loading defaults C:\Users\bradl\.opentrons\deck_calibration.json not found. Loading defaults
['Red', 'Blue'] ['Red', 'Yellow'] ['Blue', 'Yellow']
Create Labware_Layout objects¶
We can use the BiomationScripter.Labware_Layout class to store information about the labware.
Note
If you're not familiar with the Labware_Layout class, read the documentation, or see it in use here and here.
First, we'll define the destination labware.
For the destination labware, we'll use the colour mixtures generated in the previous step to populate the destination labware with the intended content using the the code below:
##########################
# Set up labware layouts #
##########################
# Create the destination object
Destination_Labware_Layout = BMS.Labware_Layout(
Name = "Destination Labware",
Type = self.destination_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
destination_rows, destination_columns = OTProto.get_labware_format(
labware_api_name = Destination_Labware_Layout.type,
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware_Layout.define_format(destination_rows, destination_columns)
Destination_Labware_Layout.set_available_wells()
for colour_1, colour_2 in Colour_Mixtures:
# Get the next empty well
well = Destination_Labware_Layout.get_next_empty_well()
# Add the first colourant
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_1,
Volume = self.final_volume / 2 # uL
)
# And then the second
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_2,
Volume = self.final_volume / 2 # uL
)
# Can also label the well
Destination_Labware_Layout.add_well_label(
Well = well,
Label = "Mixture: {}-{}".format(colour_1, colour_2)
)
Destination_Labware_Layout.print()
Next, we'll define the the source labware, and determine how many of each source colour aliquot is required.
# Create the source object
Source_Labware_Layout = BMS.Labware_Layout(
Name = "Source Labware",
Type = self.source_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
source_rows, source_columns = OTProto.get_labware_format(
labware_api_name = Source_Labware_Layout.type,
)
Source_Labware_Layout.define_format(source_rows, source_columns)
Source_Labware_Layout.set_available_wells()
# For each colourant
for colourant in self.source_colours:
# Calculate the number of aliquots needed
aliquots_needed = BMS.Aliquot_Calculator(
Liquid = colourant,
Destination_Layouts = [Destination_Labware_Layout],
Aliquot_Volume = self.source_colour_aliquot_volumes,
Dead_Volume = 0
)
# Add that many aliquots to the source layout
for aliquot_n in range(0, aliquots_needed):
# Get the next empty well
well = Source_Labware_Layout.get_next_empty_well()
# Add content to the empty well
Source_Labware_Layout.add_content(
Well = well,
Reagent = colourant,
Volume = self.source_colour_aliquot_volumes # uL
)
Source_Labware_Layout.print()
Once again, this code gets added to the run method:
class Template(OTProto.OTProto_Template):
def __init__(
self,
Source_Labware_Type,
Destination_Labware_Type,
Source_Colours,
Source_Colours_Aliquot_Volume,
Final_Volume,
Permutations = False,
**kwargs # This will make the superclass arguments available to our template as keyword arguments
):
super().__init__(**kwargs) # This passes the keyword arguments to the superclass
#######################
# User Defined Values #
#######################
self.source_labware_type = Source_Labware_Type
self.destination_labware_type = Destination_Labware_Type
self.source_colours = Source_Colours
self.source_colour_aliquot_volumes = Source_Colours_Aliquot_Volume
self.final_volume = Final_Volume # uL
self.permutations = Permutations
def run(self):
self.load_pipettes()
# Set up an empty list in which the colour mixtures required will be added
Colour_Mixtures = []
# Iterate through the list of source colours provided to create the list of mixtures.
for colour_1 in self.source_colours:
for colour_2 in self.source_colours:
# Ignore situations where colour_1 and colour_2 are the same
if colour_1 == colour_2:
continue
# Unless permutations has been set to `True`, ignore situations where...
# ...the same colours have already been mixed, just in a different order
elif not self.permutations and [colour_2, colour_1] in Colour_Mixtures:
continue
else:
# Add the two colours to the list of mixtures to prepare
Colour_Mixtures.append([colour_1, colour_2])
# Here, we'll print to OUT all of the mixtures which will be prepared
for c in Colour_Mixtures:
print(c)
##########################
# Set up labware layouts #
##########################
# Create the destination object
Destination_Labware_Layout = BMS.Labware_Layout(
Name = "Destination Labware",
Type = self.destination_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
destination_rows, destination_columns = OTProto.get_labware_format(
labware_api_name = Destination_Labware_Layout.type,
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware_Layout.define_format(destination_rows, destination_columns)
Destination_Labware_Layout.set_available_wells()
for colour_1, colour_2 in Colour_Mixtures:
# Get the next empty well
well = Destination_Labware_Layout.get_next_empty_well()
# Add the first colourant
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_1,
Volume = self.final_volume / 2 # uL
)
# And then the second
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_2,
Volume = self.final_volume / 2 # uL
)
# Can also label the well
Destination_Labware_Layout.add_well_label(
Well = well,
Label = "Mixture: {}-{}".format(colour_1, colour_2)
)
Destination_Labware_Layout.print()
# Create the source object
Source_Labware_Layout = BMS.Labware_Layout(
Name = "Source Labware",
Type = self.source_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
source_rows, source_columns = OTProto.get_labware_format(
labware_api_name = Source_Labware_Layout.type,
)
Source_Labware_Layout.define_format(source_rows, source_columns)
Source_Labware_Layout.set_available_wells()
# For each colourant
for colourant in self.source_colours:
# Calculate the number of aliquots needed
aliquots_needed = BMS.Aliquot_Calculator(
Liquid = colourant,
Destination_Layouts = [Destination_Labware_Layout],
Aliquot_Volume = self.source_colour_aliquot_volumes,
Dead_Volume = 0
)
# Add that many aliquots to the source layout
for aliquot_n in range(0, aliquots_needed):
# Get the next empty well
well = Source_Labware_Layout.get_next_empty_well()
# Add content to the empty well
Source_Labware_Layout.add_content(
Well = well,
Reagent = colourant,
Volume = self.source_colour_aliquot_volumes # uL
)
Source_Labware_Layout.print()
We can then check the state of the source and destination layouts by running the protocol template
Protocol_Name = "Colour Mixing Example"
metadata = {
'protocolName': Protocol_Name,
'author': 'Bradley Brown',
'author-email': 'b.bradley2@newcastle.ac.uk',
'user': '',
'user-email': '',
'source': 'BiomationScripter Examples - BMS v0.2.0.dev',
'apiLevel': '2.11',
'robotName': 'RobOT2' # This is the name of the OT2 you plan to run the protocol on
}
Custom_Labware_Directory = "../../../data/custom_labware"
protocol = OT2.get_protocol_api(metadata["apiLevel"])
protocol.home()
Source_Labware_Type = "opentrons_24_aluminumblock_nest_1.5ml_snapcap"
Destination_Labware_Type = "biorad_96_wellplate_200ul_pcr"
Source_Colours = ["Red", "Blue", "Yellow"]
Source_Colours_Aliquot_Volume = 1000
Final_Volume = 100
Permutations = False
Protocol_Template = Template(
Source_Labware_Type = Source_Labware_Type,
Destination_Labware_Type = Destination_Labware_Type,
Source_Colours = Source_Colours,
Source_Colours_Aliquot_Volume = Source_Colours_Aliquot_Volume,
Final_Volume = Final_Volume,
Permutations = Permutations,
Protocol = protocol,
Name = Protocol_Name,
Metadata = metadata,
Custom_Labware_Dir = Custom_Labware_Directory
)
Protocol_Template.run()
C:\Users\bradl\.opentrons\robot_settings.json not found. Loading defaults C:\Users\bradl\.opentrons\deck_calibration.json not found. Loading defaults
['Red', 'Blue'] ['Red', 'Yellow'] ['Blue', 'Yellow'] Information for Destination Labware Plate Type: biorad_96_wellplate_200ul_pcr Well Volume(uL) Liquid Class Reagent A1 50.0 Unknown Red A1 50.0 Unknown Blue A2 50.0 Unknown Red A2 50.0 Unknown Yellow A3 50.0 Unknown Blue A3 50.0 Unknown Yellow Information for Source Labware Plate Type: opentrons_24_aluminumblock_nest_1.5ml_snapcap Well Volume(uL) Liquid Class Reagent A1 1000.0 Unknown Red A2 1000.0 Unknown Blue A3 1000.0 Unknown Yellow
Load the labware¶
The BiomationScripter.Labware_Layout class was used above to keep track of the state of each labware in the protocol. For Opentrons protocols, an opentrons.protocol_api.labware.Labware object is used by the API to represent the physical labware.
We can use the OTProto.load_labware_from_layout function to create the opentrons.protocol_api.labware.Labware object from our Labware_Layout objects
################
# Load labware #
################
Source_Labware = OTProto.load_labware_from_layout(
Protocol = protocol,
Labware_Layout = Source_Labware_Layout,
deck_position = OTProto.next_empty_slot(protocol),
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware = OTProto.load_labware_from_layout(
Protocol = protocol,
Labware_Layout = Destination_Labware_Layout,
deck_position = OTProto.next_empty_slot(protocol),
custom_labware_dir = self.custom_labware_dir
)
Once we've added the code to run, we can check its functionality
class Template(OTProto.OTProto_Template):
def __init__(
self,
Source_Labware_Type,
Destination_Labware_Type,
Source_Colours,
Source_Colours_Aliquot_Volume,
Final_Volume,
Permutations = False,
**kwargs # This will make the superclass arguments available to our template as keyword arguments
):
super().__init__(**kwargs) # This passes the keyword arguments to the superclass
#######################
# User Defined Values #
#######################
self.source_labware_type = Source_Labware_Type
self.destination_labware_type = Destination_Labware_Type
self.source_colours = Source_Colours
self.source_colour_aliquot_volumes = Source_Colours_Aliquot_Volume
self.final_volume = Final_Volume # uL
self.permutations = Permutations
def run(self):
self.load_pipettes()
# Set up an empty list in which the colour mixtures required will be added
Colour_Mixtures = []
# Iterate through the list of source colours provided to create the list of mixtures.
for colour_1 in self.source_colours:
for colour_2 in self.source_colours:
# Ignore situations where colour_1 and colour_2 are the same
if colour_1 == colour_2:
continue
# Unless permutations has been set to `True`, ignore situations where...
# ...the same colours have already been mixed, just in a different order
elif not self.permutations and [colour_2, colour_1] in Colour_Mixtures:
continue
else:
# Add the two colours to the list of mixtures to prepare
Colour_Mixtures.append([colour_1, colour_2])
# Here, we'll print to OUT all of the mixtures which will be prepared
for c in Colour_Mixtures:
print(c)
##########################
# Set up labware layouts #
##########################
# Create the destination object
Destination_Labware_Layout = BMS.Labware_Layout(
Name = "Destination Labware",
Type = self.destination_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
destination_rows, destination_columns = OTProto.get_labware_format(
labware_api_name = Destination_Labware_Layout.type,
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware_Layout.define_format(destination_rows, destination_columns)
Destination_Labware_Layout.set_available_wells()
for colour_1, colour_2 in Colour_Mixtures:
# Get the next empty well
well = Destination_Labware_Layout.get_next_empty_well()
# Add the first colourant
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_1,
Volume = self.final_volume / 2 # uL
)
# And then the second
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_2,
Volume = self.final_volume / 2 # uL
)
# Can also label the well
Destination_Labware_Layout.add_well_label(
Well = well,
Label = "Mixture: {}-{}".format(colour_1, colour_2)
)
Destination_Labware_Layout.print()
# Create the source object
Source_Labware_Layout = BMS.Labware_Layout(
Name = "Source Labware",
Type = self.source_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
source_rows, source_columns = OTProto.get_labware_format(
labware_api_name = Source_Labware_Layout.type,
)
Source_Labware_Layout.define_format(source_rows, source_columns)
Source_Labware_Layout.set_available_wells()
# For each colourant
for colourant in self.source_colours:
# Calculate the number of aliquots needed
aliquots_needed = BMS.Aliquot_Calculator(
Liquid = colourant,
Destination_Layouts = [Destination_Labware_Layout],
Aliquot_Volume = self.source_colour_aliquot_volumes,
Dead_Volume = 0
)
# Add that many aliquots to the source layout
for aliquot_n in range(0, aliquots_needed):
# Get the next empty well
well = Source_Labware_Layout.get_next_empty_well()
# Add content to the empty well
Source_Labware_Layout.add_content(
Well = well,
Reagent = colourant,
Volume = self.source_colour_aliquot_volumes # uL
)
Source_Labware_Layout.print()
################
# Load labware #
################
Source_Labware = OTProto.load_labware_from_layout(
Protocol = protocol,
Labware_Layout = Source_Labware_Layout,
deck_position = OTProto.next_empty_slot(protocol),
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware = OTProto.load_labware_from_layout(
Protocol = protocol,
Labware_Layout = Destination_Labware_Layout,
deck_position = OTProto.next_empty_slot(protocol),
custom_labware_dir = self.custom_labware_dir
)
Protocol_Name = "Colour Mixing Example"
metadata = {
'protocolName': Protocol_Name,
'author': 'Bradley Brown',
'author-email': 'b.bradley2@newcastle.ac.uk',
'user': '',
'user-email': '',
'source': 'BiomationScripter Examples - BMS v0.2.0.dev',
'apiLevel': '2.11',
'robotName': 'RobOT2' # This is the name of the OT2 you plan to run the protocol on
}
Custom_Labware_Directory = "../../../data/custom_labware"
protocol = OT2.get_protocol_api(metadata["apiLevel"])
protocol.home()
Source_Labware_Type = "opentrons_24_aluminumblock_nest_1.5ml_snapcap"
Destination_Labware_Type = "biorad_96_wellplate_200ul_pcr"
Source_Colours = ["Red", "Blue", "Yellow"]
Source_Colours_Aliquot_Volume = 1000
Final_Volume = 100
Permutations = False
Protocol_Template = Template(
Source_Labware_Type = Source_Labware_Type,
Destination_Labware_Type = Destination_Labware_Type,
Source_Colours = Source_Colours,
Source_Colours_Aliquot_Volume = Source_Colours_Aliquot_Volume,
Final_Volume = Final_Volume,
Permutations = Permutations,
Protocol = protocol,
Name = Protocol_Name,
Metadata = metadata,
Custom_Labware_Dir = Custom_Labware_Directory
)
C:\Users\bradl\.opentrons\robot_settings.json not found. Loading defaults C:\Users\bradl\.opentrons\deck_calibration.json not found. Loading defaults
We can observe how the state of the Opentrons' deck changes when labware is loaded:
# Before loading
for slot in Protocol_Template._protocol.deck:
print(slot, Protocol_Template._protocol.deck[slot])
1 None 2 None 3 None 4 None 5 None 6 None 7 None 8 None 9 None 10 None 11 None 12 Opentrons Fixed Trash on 12
Protocol_Template.run()
['Red', 'Blue'] ['Red', 'Yellow'] ['Blue', 'Yellow'] Information for Destination Labware Plate Type: biorad_96_wellplate_200ul_pcr Well Volume(uL) Liquid Class Reagent A1 50.0 Unknown Red A1 50.0 Unknown Blue A2 50.0 Unknown Red A2 50.0 Unknown Yellow A3 50.0 Unknown Blue A3 50.0 Unknown Yellow Information for Source Labware Plate Type: opentrons_24_aluminumblock_nest_1.5ml_snapcap Well Volume(uL) Liquid Class Reagent A1 1000.0 Unknown Red A2 1000.0 Unknown Blue A3 1000.0 Unknown Yellow
# After loading
for slot in Protocol_Template._protocol.deck:
print(slot, Protocol_Template._protocol.deck[slot])
1 <opentrons.protocols.context.protocol_api.labware.LabwareImplementation object at 0x0000017809949370> 2 <opentrons.protocols.context.protocol_api.labware.LabwareImplementation object at 0x00000178099474F0> 3 None 4 None 5 None 6 None 7 None 8 None 9 None 10 None 11 None 12 Opentrons Fixed Trash on 12
Transfer Events¶
The next step is to determine the transfer steps required in the protocol. For this example, we can create a list of transfer steps for each source colourant provided by the user.
To generate the transfer lists, we'll use the BiomationScripter.Get_Transfers_Required function.
###################
# Transfer Events #
###################
Transfers = {}
for colourant in self.source_colours:
Transfers[colourant] = BMS.Get_Transfers_Required(
Liquid = colourant,
Destination_Layouts = [Destination_Labware_Layout]
)
Loading Pipettes Tips¶
The final step before the liquid handling commands are generated is to determine how many pipette tips are required. This can be done using the calculate_and_add_tips method and add_tip_boxes_to_pipettes method
#####################
# Load Pipette Tips #
#####################
for colourant in Transfers:
transfer_volumes = Transfers[colourant][0]
self.calculate_and_add_tips(
Transfer_Volumes = transfer_volumes,
New_Tip = True
)
self.add_tip_boxes_to_pipettes()
We can also use the tip_racks_prompt method to let users know at runtime how many tips and tip boxes are required:
self.tip_racks_prompt()
Now let's add this code to the run method and check its functionality
class Template(OTProto.OTProto_Template):
def __init__(
self,
Source_Labware_Type,
Destination_Labware_Type,
Source_Colours,
Source_Colours_Aliquot_Volume,
Final_Volume,
Permutations = False,
**kwargs # This will make the superclass arguments available to our template as keyword arguments
):
super().__init__(**kwargs) # This passes the keyword arguments to the superclass
#######################
# User Defined Values #
#######################
self.source_labware_type = Source_Labware_Type
self.destination_labware_type = Destination_Labware_Type
self.source_colours = Source_Colours
self.source_colour_aliquot_volumes = Source_Colours_Aliquot_Volume
self.final_volume = Final_Volume # uL
self.permutations = Permutations
def run(self):
self.load_pipettes()
# Set up an empty list in which the colour mixtures required will be added
Colour_Mixtures = []
# Iterate through the list of source colours provided to create the list of mixtures.
for colour_1 in self.source_colours:
for colour_2 in self.source_colours:
# Ignore situations where colour_1 and colour_2 are the same
if colour_1 == colour_2:
continue
# Unless permutations has been set to `True`, ignore situations where...
# ...the same colours have already been mixed, just in a different order
elif not self.permutations and [colour_2, colour_1] in Colour_Mixtures:
continue
else:
# Add the two colours to the list of mixtures to prepare
Colour_Mixtures.append([colour_1, colour_2])
# Here, we'll print to OUT all of the mixtures which will be prepared
for c in Colour_Mixtures:
print(c)
##########################
# Set up labware layouts #
##########################
# Create the destination object
Destination_Labware_Layout = BMS.Labware_Layout(
Name = "Destination Labware",
Type = self.destination_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
destination_rows, destination_columns = OTProto.get_labware_format(
labware_api_name = Destination_Labware_Layout.type,
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware_Layout.define_format(destination_rows, destination_columns)
Destination_Labware_Layout.set_available_wells()
for colour_1, colour_2 in Colour_Mixtures:
# Get the next empty well
well = Destination_Labware_Layout.get_next_empty_well()
# Add the first colourant
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_1,
Volume = self.final_volume / 2 # uL
)
# And then the second
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_2,
Volume = self.final_volume / 2 # uL
)
# Can also label the well
Destination_Labware_Layout.add_well_label(
Well = well,
Label = "Mixture: {}-{}".format(colour_1, colour_2)
)
Destination_Labware_Layout.print()
# Create the source object
Source_Labware_Layout = BMS.Labware_Layout(
Name = "Source Labware",
Type = self.source_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
source_rows, source_columns = OTProto.get_labware_format(
labware_api_name = Source_Labware_Layout.type,
)
Source_Labware_Layout.define_format(source_rows, source_columns)
Source_Labware_Layout.set_available_wells()
# For each colourant
for colourant in self.source_colours:
# Calculate the number of aliquots needed
aliquots_needed = BMS.Aliquot_Calculator(
Liquid = colourant,
Destination_Layouts = [Destination_Labware_Layout],
Aliquot_Volume = self.source_colour_aliquot_volumes,
Dead_Volume = 0
)
# Add that many aliquots to the source layout
for aliquot_n in range(0, aliquots_needed):
# Get the next empty well
well = Source_Labware_Layout.get_next_empty_well()
# Add content to the empty well
Source_Labware_Layout.add_content(
Well = well,
Reagent = colourant,
Volume = self.source_colour_aliquot_volumes # uL
)
Source_Labware_Layout.print()
################
# Load labware #
################
Source_Labware = OTProto.load_labware_from_layout(
Protocol = protocol,
Labware_Layout = Source_Labware_Layout,
deck_position = OTProto.next_empty_slot(protocol),
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware = OTProto.load_labware_from_layout(
Protocol = protocol,
Labware_Layout = Destination_Labware_Layout,
deck_position = OTProto.next_empty_slot(protocol),
custom_labware_dir = self.custom_labware_dir
)
###################
# Transfer Events #
###################
Transfers = {}
for colourant in self.source_colours:
Transfers[colourant] = BMS.Get_Transfers_Required(
Liquid = colourant,
Destination_Layouts = [Destination_Labware_Layout]
)
#####################
# Load Pipette Tips #
#####################
for colourant in Transfers:
transfer_volumes = Transfers[colourant][0]
self.calculate_and_add_tips(
Transfer_Volumes = transfer_volumes,
New_Tip = True
)
self.add_tip_boxes_to_pipettes()
self.tip_racks_prompt()
Protocol_Name = "Colour Mixing Example"
metadata = {
'protocolName': Protocol_Name,
'author': 'Bradley Brown',
'author-email': 'b.bradley2@newcastle.ac.uk',
'user': '',
'user-email': '',
'source': 'BiomationScripter Examples - BMS v0.2.0.dev',
'apiLevel': '2.11',
'robotName': 'RobOT2' # This is the name of the OT2 you plan to run the protocol on
}
Custom_Labware_Directory = "../../../data/custom_labware"
protocol = OT2.get_protocol_api(metadata["apiLevel"])
protocol.home()
Source_Labware_Type = "opentrons_24_aluminumblock_nest_1.5ml_snapcap"
Destination_Labware_Type = "biorad_96_wellplate_200ul_pcr"
Source_Colours = ["Red", "Blue", "Yellow"]
Source_Colours_Aliquot_Volume = 1000
Final_Volume = 100
Permutations = False
Protocol_Template = Template(
Source_Labware_Type = Source_Labware_Type,
Destination_Labware_Type = Destination_Labware_Type,
Source_Colours = Source_Colours,
Source_Colours_Aliquot_Volume = Source_Colours_Aliquot_Volume,
Final_Volume = Final_Volume,
Permutations = Permutations,
Protocol = protocol,
Name = Protocol_Name,
Metadata = metadata,
Custom_Labware_Dir = Custom_Labware_Directory
)
Protocol_Template.run()
C:\Users\bradl\.opentrons\robot_settings.json not found. Loading defaults C:\Users\bradl\.opentrons\deck_calibration.json not found. Loading defaults
['Red', 'Blue'] ['Red', 'Yellow'] ['Blue', 'Yellow'] Information for Destination Labware Plate Type: biorad_96_wellplate_200ul_pcr Well Volume(uL) Liquid Class Reagent A1 50.0 Unknown Red A1 50.0 Unknown Blue A2 50.0 Unknown Red A2 50.0 Unknown Yellow A3 50.0 Unknown Blue A3 50.0 Unknown Yellow Information for Source Labware Plate Type: opentrons_24_aluminumblock_nest_1.5ml_snapcap Well Volume(uL) Liquid Class Reagent A1 1000.0 Unknown Red A2 1000.0 Unknown Blue A3 1000.0 Unknown Yellow
The .commands attribute of opentrons.protocol_api.contexts.ProtocolContext can be used to check the current liquid handling commands:
for line in Protocol_Template._protocol.commands():
print(line)
Pausing robot operation: This protocol uses 0 20 uL tip boxes Pausing robot operation: This protocol uses 1 300 uL tip boxes
So far, the only commands are those which let the user know how many tip boxes are needed
Liquid Handling¶
For this protocol template, the OTProto.dispense_from_aliquots function can be used to create the Opentrons liquid handling commands, as we are simply dispensing the different colourants from source colour aliquots to wells of a destination labware:
Note
The OTProto.dispense_from_aliquots function takes opentrons.protocol_api.labware.Well objects as the source and destination locations. Here, the OTProto.get_locations function is used to generate these objects.
###################
# Liquid Handling #
###################
for colourant in self.source_colours:
# Get the transfer volumes
transfer_volumes = Transfers[colourant][0]
# Get the destination locations
destination_locations = OTProto.get_locations(
Labware = Destination_Labware,
Wells = Transfers[colourant][1]
)
# Get the aliquot source locations
source_wells = Source_Labware_Layout.get_wells_containing_liquid(colourant)
source_locations = OTProto.get_locations(
Labware = Source_Labware,
Wells = source_wells
)
OTProto.dispense_from_aliquots(
Protocol = self._protocol,
Transfer_Volumes = transfer_volumes,
Aliquot_Source_Locations = source_locations,
Destinations = destination_locations,
Dead_Volume_Proportion = 1.0,
Aliquot_Volumes = self.source_colour_aliquot_volumes,
new_tip = True,
mix_after = (10, "transfer_volume")
)
class Template(OTProto.OTProto_Template):
def __init__(
self,
Source_Labware_Type,
Destination_Labware_Type,
Source_Colours,
Source_Colours_Aliquot_Volume,
Final_Volume,
Permutations = False,
**kwargs # This will make the superclass arguments available to our template as keyword arguments
):
super().__init__(**kwargs) # This passes the keyword arguments to the superclass
#######################
# User Defined Values #
#######################
self.source_labware_type = Source_Labware_Type
self.destination_labware_type = Destination_Labware_Type
self.source_colours = Source_Colours
self.source_colour_aliquot_volumes = Source_Colours_Aliquot_Volume
self.final_volume = Final_Volume # uL
self.permutations = Permutations
def run(self):
self.load_pipettes()
# Set up an empty list in which the colour mixtures required will be added
Colour_Mixtures = []
# Iterate through the list of source colours provided to create the list of mixtures.
for colour_1 in self.source_colours:
for colour_2 in self.source_colours:
# Ignore situations where colour_1 and colour_2 are the same
if colour_1 == colour_2:
continue
# Unless permutations has been set to `True`, ignore situations where...
# ...the same colours have already been mixed, just in a different order
elif not self.permutations and [colour_2, colour_1] in Colour_Mixtures:
continue
else:
# Add the two colours to the list of mixtures to prepare
Colour_Mixtures.append([colour_1, colour_2])
# Here, we'll print to OUT all of the mixtures which will be prepared
for c in Colour_Mixtures:
print(c)
##########################
# Set up labware layouts #
##########################
# Create the destination object
Destination_Labware_Layout = BMS.Labware_Layout(
Name = "Destination Labware",
Type = self.destination_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
destination_rows, destination_columns = OTProto.get_labware_format(
labware_api_name = Destination_Labware_Layout.type,
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware_Layout.define_format(destination_rows, destination_columns)
Destination_Labware_Layout.set_available_wells()
for colour_1, colour_2 in Colour_Mixtures:
# Get the next empty well
well = Destination_Labware_Layout.get_next_empty_well()
# Add the first colourant
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_1,
Volume = self.final_volume / 2 # uL
)
# And then the second
Destination_Labware_Layout.add_content(
Well = well,
Reagent = colour_2,
Volume = self.final_volume / 2 # uL
)
# Can also label the well
Destination_Labware_Layout.add_well_label(
Well = well,
Label = "Mixture: {}-{}".format(colour_1, colour_2)
)
Destination_Labware_Layout.print()
# Create the source object
Source_Labware_Layout = BMS.Labware_Layout(
Name = "Source Labware",
Type = self.source_labware_type
)
# Define the labware's format (i.e. number of rows and columns)
source_rows, source_columns = OTProto.get_labware_format(
labware_api_name = Source_Labware_Layout.type,
)
Source_Labware_Layout.define_format(source_rows, source_columns)
Source_Labware_Layout.set_available_wells()
# For each colourant
for colourant in self.source_colours:
# Calculate the number of aliquots needed
aliquots_needed = BMS.Aliquot_Calculator(
Liquid = colourant,
Destination_Layouts = [Destination_Labware_Layout],
Aliquot_Volume = self.source_colour_aliquot_volumes,
Dead_Volume = 0
)
# Add that many aliquots to the source layout
for aliquot_n in range(0, aliquots_needed):
# Get the next empty well
well = Source_Labware_Layout.get_next_empty_well()
# Add content to the empty well
Source_Labware_Layout.add_content(
Well = well,
Reagent = colourant,
Volume = self.source_colour_aliquot_volumes # uL
)
Source_Labware_Layout.print()
################
# Load labware #
################
Source_Labware = OTProto.load_labware_from_layout(
Protocol = protocol,
Labware_Layout = Source_Labware_Layout,
deck_position = OTProto.next_empty_slot(protocol),
custom_labware_dir = self.custom_labware_dir
)
Destination_Labware = OTProto.load_labware_from_layout(
Protocol = protocol,
Labware_Layout = Destination_Labware_Layout,
deck_position = OTProto.next_empty_slot(protocol),
custom_labware_dir = self.custom_labware_dir
)
###################
# Transfer Events #
###################
Transfers = {}
for colourant in self.source_colours:
Transfers[colourant] = BMS.Get_Transfers_Required(
Liquid = colourant,
Destination_Layouts = [Destination_Labware_Layout]
)
#####################
# Load Pipette Tips #
#####################
for colourant in Transfers:
transfer_volumes = Transfers[colourant][0]
self.calculate_and_add_tips(
Transfer_Volumes = transfer_volumes,
New_Tip = True
)
self.add_tip_boxes_to_pipettes()
self.tip_racks_prompt()
###################
# Liquid Handling #
###################
for colourant in self.source_colours:
# Get the transfer volumes
transfer_volumes = Transfers[colourant][0]
# Get the destination locations
destination_locations = OTProto.get_locations(
Labware = Destination_Labware,
Wells = Transfers[colourant][1]
)
# Get the aliquot source locations
source_wells = Source_Labware_Layout.get_wells_containing_liquid(colourant)
source_locations = OTProto.get_locations(
Labware = Source_Labware,
Wells = source_wells
)
OTProto.dispense_from_aliquots(
Protocol = self._protocol,
Transfer_Volumes = transfer_volumes,
Aliquot_Source_Locations = source_locations,
Destinations = destination_locations,
Dead_Volume_Proportion = 1.0,
Aliquot_Volumes = self.source_colour_aliquot_volumes,
new_tip = True,
mix_after = (10, "transfer_volume")
)
Simulation¶
Now we've coded the protocol template, we can check it works by simulating it.
Protocol_Name = "Colour Mixing Example"
metadata = {
'protocolName': Protocol_Name,
'author': 'Bradley Brown',
'author-email': 'b.bradley2@newcastle.ac.uk',
'user': '',
'user-email': '',
'source': 'BiomationScripter Examples - BMS v0.2.0.dev',
'apiLevel': '2.11',
'robotName': 'RobOT2' # This is the name of the OT2 you plan to run the protocol on
}
Custom_Labware_Directory = "../../../data/custom_labware"
protocol = OT2.get_protocol_api(metadata["apiLevel"])
protocol.home()
Source_Labware_Type = "opentrons_24_aluminumblock_nest_1.5ml_snapcap"
Destination_Labware_Type = "biorad_96_wellplate_200ul_pcr"
Source_Colours = ["Red", "Blue", "Yellow"]
Source_Colours_Aliquot_Volume = 1000
Final_Volume = 100
Permutations = False
Protocol_Template = Template(
Source_Labware_Type = Source_Labware_Type,
Destination_Labware_Type = Destination_Labware_Type,
Source_Colours = Source_Colours,
Source_Colours_Aliquot_Volume = Source_Colours_Aliquot_Volume,
Final_Volume = Final_Volume,
Permutations = Permutations,
Protocol = protocol,
Name = Protocol_Name,
Metadata = metadata,
Custom_Labware_Dir = Custom_Labware_Directory
)
Protocol_Template.run()
C:\Users\bradl\.opentrons\robot_settings.json not found. Loading defaults C:\Users\bradl\.opentrons\deck_calibration.json not found. Loading defaults
['Red', 'Blue'] ['Red', 'Yellow'] ['Blue', 'Yellow'] Information for Destination Labware Plate Type: biorad_96_wellplate_200ul_pcr Well Volume(uL) Liquid Class Reagent A1 50.0 Unknown Red A1 50.0 Unknown Blue A2 50.0 Unknown Red A2 50.0 Unknown Yellow A3 50.0 Unknown Blue A3 50.0 Unknown Yellow Information for Source Labware Plate Type: opentrons_24_aluminumblock_nest_1.5ml_snapcap Well Volume(uL) Liquid Class Reagent A1 1000.0 Unknown Red A2 1000.0 Unknown Blue A3 1000.0 Unknown Yellow
Once again, the .commands attribute of opentrons.protocol_api.contexts.ProtocolContext can be used to check the liquid handling commands:
for line in Protocol_Template._protocol.commands():
print(line)
Pausing robot operation: This protocol uses 0 20 uL tip boxes Pausing robot operation: This protocol uses 1 300 uL tip boxes Picking up tip from A1 of Opentrons 96 Tip Rack 300 µL on 3 Moving to A1 of Source Labware on 1 Aspirating 50.0 uL from A1 of Source Labware on 1 at 92.86 uL/sec Moving to A1 of Destination Labware on 2 Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Mixing 10 times with a volume of 50.0 ul Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Dropping tip into A1 of Opentrons Fixed Trash on 12 Picking up tip from B1 of Opentrons 96 Tip Rack 300 µL on 3 Moving to A1 of Source Labware on 1 Aspirating 50.0 uL from A1 of Source Labware on 1 at 92.86 uL/sec Moving to A2 of Destination Labware on 2 Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Mixing 10 times with a volume of 50.0 ul Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Dropping tip into A1 of Opentrons Fixed Trash on 12 Picking up tip from C1 of Opentrons 96 Tip Rack 300 µL on 3 Moving to A2 of Source Labware on 1 Aspirating 50.0 uL from A2 of Source Labware on 1 at 92.86 uL/sec Moving to A1 of Destination Labware on 2 Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Mixing 10 times with a volume of 50.0 ul Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A1 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A1 of Destination Labware on 2 at 92.86 uL/sec Dropping tip into A1 of Opentrons Fixed Trash on 12 Picking up tip from D1 of Opentrons 96 Tip Rack 300 µL on 3 Moving to A2 of Source Labware on 1 Aspirating 50.0 uL from A2 of Source Labware on 1 at 92.86 uL/sec Moving to A3 of Destination Labware on 2 Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Mixing 10 times with a volume of 50.0 ul Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Dropping tip into A1 of Opentrons Fixed Trash on 12 Picking up tip from E1 of Opentrons 96 Tip Rack 300 µL on 3 Moving to A3 of Source Labware on 1 Aspirating 50.0 uL from A3 of Source Labware on 1 at 92.86 uL/sec Moving to A2 of Destination Labware on 2 Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Mixing 10 times with a volume of 50.0 ul Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A2 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A2 of Destination Labware on 2 at 92.86 uL/sec Dropping tip into A1 of Opentrons Fixed Trash on 12 Picking up tip from F1 of Opentrons 96 Tip Rack 300 µL on 3 Moving to A3 of Source Labware on 1 Aspirating 50.0 uL from A3 of Source Labware on 1 at 92.86 uL/sec Moving to A3 of Destination Labware on 2 Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Mixing 10 times with a volume of 50.0 ul Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Aspirating 50.0 uL from A3 of Destination Labware on 2 at 92.86 uL/sec Dispensing 50.0 uL into A3 of Destination Labware on 2 at 92.86 uL/sec Dropping tip into A1 of Opentrons Fixed Trash on 12
Adding the Template to BiomationScripter¶
Custom made templates can be added to BiomationScripter. They must be contained within their own file named <PROTOCOL>.py (in this case Colour_Mixing.py) and placed within the BiomationScripter/OTProto/Templates/ directory.
Once the template has been tested, it can be uploaded to BiomationScripter for use by others. To do this, open a pull request on GitHub. Make sure you've also added to the documentation.