-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvial_classes_solubility_test.py
225 lines (186 loc) · 8.74 KB
/
vial_classes_solubility_test.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# All mass in mg
# All molar mass in g/mol
# All volumes in mL
# All molarities in mols/L
# All molar quantities in mmols
import os
import pandas as Pandas
from opentrons import protocol_api
from opentrons import types
metadata = {
"apiLevel": "2.16",
"protocolName":"Solubility Testing w/ Classes",
"author":"Otto Beall",
"description":"Evaluate the solubility of different mixtures by making a molarity gradient & seeing what dissolves",
}
requirements = {"robotType": "OT-2",}
class Vial:
'''
A class that tracks the contents and location of a vial.
'''
def __init__(self,solute_mmols:dict,solvent_vols:dict,labware,location,max_volume:float,complete = False):
'''
Initializes a new Vial object
Parameters
----------
solute_mmoles : dict
A dictionary mapping each ion ('H+' , 'Pb2+' , 'Cl-' ...) to a quantity in mmols
For clarity use IUPAC chemical names or common abbreviations (eg. MA for Methylammonium) followed by the charge ('+','2+','-','2-'...etc)
solvent_vols : dict
A dictionary mapping each solvent ('H2O' , 'DMF', 'DMSO' ...) to a volume in mL
labware
An opentrons labware object representing the labware that the vial is contained in
location
An integer or string representing the vial's position within the labware.
A numerical index (0,1,2...) or letter-number pair ('A1','A2','B1'...) is acceptable.
max_volume : float
Maximum volume of the vial in mL.
complete : bool, defaults to False
A boolean representing whether the Vial is complete (no more ingredients will be added to it).
)
'''
self.solute_mmols = solute_mmols
self.solvent_vols = solvent_vols
self.labware = labware
self.location = location
self.max_volume = max_volume
self.complete = complete
if self.get_volume()>max_volume:
raise Exception("Volume in vial exceeds maximum")
if(complete):
self.complete_volume = self.get_volume()
if self.complete_volume <= 0:
raise Exception("Vial cannot be complete without addition of liquid.")
self.fraction_left = 1
else:
self.complete_volume = None
self.fraction_left = None
def add_liquid(self,other_solution,volume,pipette,new_tip='always'):
'''
Adds liquid from another vial
Parameters
----------
other_solution
Vial object representing the source of the added liquid.
volume
Volume of other solution to be added to the vial in mL.
'''
#Only proceeds if this Vial has NOT been marked as complete and the other Vial has been marked as complete.
if(self.is_complete()):
raise Exception("Attempted to add to a solution that was marked as complete.")
elif(other_solution.is_complete()):
#Reduces the remaining fraction of the other solution.
other_solution.extract(volume)
volume_fraction = volume / other_solution.get_complete_volume()
for ion in other_solution.get_complete_solute_mmols():
if ion in self.solute_mmols:
self.solute_mmols[ion] += other_solution.get_complete_solute_mmols()[ion]*volume_fraction
else:
self.solute_mmols[ion] = other_solution.get_complete_solute_mmols()[ion]*volume_fraction
for solvent in other_solution.get_complete_solvent_vols():
if solvent in self.solvent_vols:
self.solvent_vols[solvent] += other_solution.get_complete_solvent_vols()[solvent]*volume_fraction
else:
self.solvent_vols[solvent] = other_solution.get_complete_solvent_vols()[solvent]*volume_fraction
if(self.get_volume() > self.max_volume):
raise Exception("Volume in vial exceeds maximum.")
pipette.transfer(volume*1000,other_solution.get_labware().wells(other_solution.get_location()),self.labware.wells(self.location),new_tip=new_tip)
else:
raise Exception("Attempted to extract from a solution that was not complete. (Use is_complete() to complete filling a vial)")
def get_volume(self):
'''
Gets the current volume contained in the vial.
'''
output = 0
for solvent in self.solvent_vols:
output += self.solvent_vols[solvent]
return output
def get_complete_volume(self):
'''
Gets the total volume of the Vial when it was first completed.
'''
if(self.is_complete):
return self.complete_volume
else:
raise Exception("Attempted to get the complete volume of a vial not marked as complete.")
def extract(self,volume):
'''
Decreases the fraction of the total solution remaining in the vial.
'''
if volume < self.complete_volume * self.fraction_left:
self.fraction_left -= volume/self.complete_volume
else:
raise Exception(f"Attempted to extract greater than remaining volume in vial. Total volume: {self.complete_volume} mL. Remaining Fraction: {self.fraction_left}")
def get_molarities(self):
'''
Returns a dictionary of molarities (mols/L) for each of the ions in the solution.
'''
volume = self.get_volume()
output = {}
for solute in self.solute_mmols:
output[solute] = self.solute_mmols[solute]/volume
return output
def complete(self):
'''
Changes the Vial's status to complete, indicating that nothing more will be added to it.
'''
self.complete_volume = self.get_volume()
if self.complete_volume <= 0:
raise Exception("Vial cannot be complete without addition of liquid.")
self.fraction_left = 1
self.complete = True
def get_complete_solute_mmols(self):
'''
Returns a dictionary mapping each ion to its quantity in the Vial when first completed.
'''
return self.solute_mmols
def get_complete_solvent_vols(self):
'''
Returns a dictionary mapping each solvent to its volume when the Vial was first completed.
'''
return self.solvent_vols
def is_complete(self):
'''
Returns a boolean indicating whether the Vial has been marked as complete.
'''
return self.complete
def get_labware(self):
'''
Returns the labware where the Vial is located.
'''
return self.labware
def get_location(self):
'''
Returns the Vial's location within its specified labware. A numerical index (0,1,2...) or letter-number pair ('A1','A2','B1'...) is acceptable.
'''
return self.location
def __str__(self):
output = "Vial containing the following chemicals:\n"
for solute in self.get_molarities():
output += f"\t{solute} : {str(self.get_molarities()[solute])} mols/L\n"
return output
def run(protocol: protocol_api.ProtocolContext,data=None) -> None:
tube_rack = protocol.load_labware('opentrons_6_tuberack_falcon_50ml_conical',location='9') # NOTE this is the location of the rack with acid in it, far back so less splash hazard
heater_shaker = protocol.load_module('heaterShakerModuleV1', location='4') # NOTE placed in spot 9 so less splash hazard
heater_shaker_plate = heater_shaker.load_labware('opentrons_24_aluminumblock_generic_2ml_screwcap')
heater_shaker.set_target_temperature(90)
heater_shaker.close_labware_latch()
tiprack = protocol.load_labware('opentrons_96_tiprack_1000ul',location='6')
pipette = protocol.load_instrument('p1000_single_gen2',mount="right",tip_racks=[tiprack])
acid_vol = 80
HCl_stock = Vial({'H+':9*acid_vol,'Cl-':9*acid_vol},{'H2O':acid_vol},tube_rack,0,acid_vol,complete=True)
HBr_stock = Vial({'H+':9*acid_vol,'Br-':9*acid_vol},{'H2O':acid_vol},tube_rack,1,acid_vol,complete=True)
HI_stock = Vial({'H+':9*acid_vol,'I-':9*acid_vol},{'H2O':acid_vol},tube_rack,2,acid_vol,complete=True)
solutions = []
molarities = [0.2,0.4,0.6,0.8,1]
max_volume = 4
powder_mmols = 4*min(molarities)
for i in range(0,5):
solutions.append(Vial({'Cs+':powder_mmols,'Ac-':powder_mmols},{},heater_shaker_plate,i,5))
solutions[0].add_liquid(HCl_stock,powder_mmols/molarities[i],pipette)
pipette.pick_up_tip()
for i in range(0,5):
solutions[i].add_liquid(HCl_stock,powder_mmols/molarities[i],pipette,new_tip='never')
pipette.drop_tip()
for i in range(0,5):
protocol.pause(solutions[i].__str__())