Vessel: Lesson
Overview:
In this lesson, we will be going through a class that is vital to the operation of all of our benches, the vessel class.
The source code for this can be found here: chemistrylab/chem_algorithms/vessel.py
. The vessel class as it is named is
meant to simulate the use of any given you might find in a chemistry lad, such as a beaker or an extraction vessel.
Here we will be going through the important concepts, functions and attributes that make up the vessel class so that you
can easily use it when designing your own reactions.
If you want a more detailed look into each function of the vessel I suggest you go to our documentation on the data structure.
The Vessel class serves as any container you might find in a lab, a beaker, a dripper, etc. The vessel class simulates and allows for any action that you might want to perform within a lab, such as draining contents, storing gasses from a reaction, performing reactions, mix, pour, etc. This is performed using an event queue, which we will look at later in this lesson. First an overview of some of the important variables that make up the vessel class:
Important Variables |
Structure |
Description |
---|---|---|
material_dict |
{str(material): material, …} |
a dictionary holding all the material inside this vessel |
solvents |
[str(material), …] |
A list of solute names |
solute_dict |
{str(solute): array[len(solvents)] , …} |
dictionary that represents the solution |
An example vessel:
from chemistrylab import vessel,material
from chemistrylab.util import Visualization
from IPython.display import display,clear_output,HTML
from copy import deepcopy
Visualization.use_mpl_light(size=1)
v=vessel.Vessel("A")
H2O = material.H2O(mol=1)
Na,Cl = material.NaCl().dissolve().keys()
Na.mol=Cl.mol=1
C6H14 = material.C6H14(mol=1.0)
ether=material.DiEthylEther(mol=0.5)
dodecane = material.Dodecane(mol=2)
v.material_dict={str(Na):Na,str(Cl):Cl,str(C6H14):C6H14,str(H2O):H2O,str(dodecane):dodecane}
v.validate_solvents()
v.validate_solutes()
display(v.get_material_dataframe(), v.get_solute_dataframe())
Amount | Phase | Solute | Solvent | |
---|---|---|---|---|
Na | 1.000000 | l | True | False |
Cl | 1.000000 | l | True | False |
C6H14 | 1.000000 | l | False | True |
H2O | 1.000000 | l | False | True |
dodecane | 2.000000 | l | True | False |
C6H14 | H2O | |
---|---|---|
Na | 0.500000 | 0.500000 |
Cl | 0.500000 | 0.500000 |
dodecane | 1.000000 | 1.000000 |
To briefly describe above, the material_dict describes the materials contained within a vessel and the quantity of that material. The material dict is a dictionary of (material.name, material instance) pairs. As for the solute dict, it represents how much each solute is dissolved in each solvent. Above we can see that each solute is dissolved in 50% water and 50% oil.
Next we will look at some of the important functions that we will need to use with the vessel class:
Important functions |
Description |
---|---|
push_event_to_queue() |
used to pass event into the vessel |
validate_solvents() |
Call when manually updating the material dict in order to update the solvent list |
validate_solutes() |
Call when manually updating the material dict in order to update the solute_dict |
From the list above, the most important function is push_event_to_queue(). The rest of the functions are generally handeled in the backend.
Event Functions
Function Name |
Description |
---|---|
‘pour by volume’ |
Pour from self vessel to target vessel by certain volume |
‘pour by percent’ |
Pour a fraction of all contents in one vessel into another |
‘drain by pixel |
Drain from self vessel to target vessel by certain pixel |
‘mix’ |
Shake the vessel or let it settle |
‘update_layer’ |
Update self vessel’s layer representation |
‘change heat’ |
Add or remove heat from the vessel, |
‘heat contact’ |
Connect the vessel to a reservoir for heat transfer, |
v2,v3 = deepcopy(v),deepcopy(v)
v.label,v2.label,v3.label = "Fully Mixed","Partially Mixed", "Settled"
v.push_event_to_queue([vessel.Event('mix',[-1],None)],0)
v2.push_event_to_queue([vessel.Event('mix',[0.02],None)])
v3.push_event_to_queue([vessel.Event('mix',[0.5],None)])
Visualization.matplotVisualizer.display_vessels([v,v2,v3],["layers"])
display(
v.get_solute_dataframe(),
v2.get_solute_dataframe(),
v3.get_solute_dataframe())
C6H14 | H2O | |
---|---|---|
Na | 0.802160 | 0.197840 |
Cl | 0.802160 | 0.197840 |
dodecane | 1.604319 | 0.395681 |
C6H14 | H2O | |
---|---|---|
Na | 0.756367 | 0.243633 |
Cl | 0.756367 | 0.243633 |
dodecane | 1.649199 | 0.350801 |
C6H14 | H2O | |
---|---|---|
Na | 0.674302 | 0.325698 |
Cl | 0.674302 | 0.325698 |
dodecane | 1.999999 | 0.000001 |
Customizing the event queue
You can add custom events to the Vessel class by registering them with Vessel.register(f: Callable, f_id: str)
. Functions must be of the following form:
f(vessel: Vessel, dt: float, other_vessel: Optional[Vessel], *args)
This will correspond to an Event of the form
Event(f_id, args, other_vessel)
Below is an example of how to register a custom function:
from chemistrylab.chem_algorithms.vessel import Vessel,Event
from chemistrylab.chem_algorithms.material import Material,H2O
from typing import NamedTuple, Tuple, Callable, Optional
def add_material(vessel: Vessel, dt: float, other_vessel: Optional[Vessel], material: Material):
"""
Custom method to add a material to the vessel's material dict
Args:
- vessel (Vessel): The vessel you want to put the material in
- dt (unused): How much time has passed
- other_vessel (unused): A second affected vessel
- material (Material): The material to add
"""
assert other_vessel is None
key = str(material)
materials=vessel.material_dict
if key in materials:
materials[key].mol+=material.mol
else:
# Create a new material with the same class and number of mols
materials[key] = material.ration(1)
# Rebuild the vessel's solute dict if new solvents have been added
vessel.validate_solvents()
vessel.validate_solutes()
return vessel._handle_overflow()
Vessel.register(add_material,"add material")
v = Vessel("Test Vessel")
event = Event("add material",(H2O(mol=1),),None)
v.push_event_to_queue([event])
display(v.get_material_dataframe())
The Workflow
Agent choose action from the action space of an environment.
The environment does the calculation and update and generate events.
At the end of each action, if the action affect a vessel, use push_event_to_queue() to push the event into the vessel, if no event generated, call the function with events=None.
With push_event_to_queue() called, events are pushed into the vessel.
_update_materials is automatically called and perform events in the events_queue.
Each event has a corresponding event function, it first update properties of the vessel, then loop over the materials inside the vessel by calling the corresponding event functions of each material.
The materials’ event function will return feedback by calling the push_event_to_queue(), which contains feedback and unfinished event
The returned feedback is added to the _feedback_queue
The the _merge_event_queue() is called on _feedback_queue, which merge the events in the feedback_queue to generate a merged_queue and add default event into it, then empty the _feedback_queue
Then the merged_queue will be executed and new feedback are collected and added to _feedback_queue, which will be executed with the next action.