Skip to content

Commit

Permalink
Feature: Major changes to Container class functions (Merge pull request
Browse files Browse the repository at this point in the history
#33 from ekwan/feature_dilute)

Heavy refactor of the Container class functions create_solution() and dilute() along with added and removed functions for Container, Recipe, and Unit classes. See pull request for more details.
  • Loading branch information
jlw387 authored Sep 6, 2024
2 parents 327942d + 1118a67 commit be40d83
Show file tree
Hide file tree
Showing 13 changed files with 1,336 additions and 1,095 deletions.
2 changes: 1 addition & 1 deletion docs/source/users_guide/working_with_containers.rst
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ Diluting Stock Solutions

In the previous examples, we made a solution by dissolving a solid into a liquid. You can also create a solution by diluting part of a stock solution::

salt_water1M = Container.create_solution(name='salt water (1 M)', solute=salt, solvent=water, concentration='1 M', quantity='100 mL')
salt_water1M = Container.create_solution(name='salt water (1 M)', solute=salt, solvent=water, concentration='1 M', total_quantity='100 mL')

salt_water1M, salt_water500mM = Container.create_solution_from(name='salt water (0.5 M)', source=salt_water1M, solute=salt,
solvent=water, concentration='0.5 M', quantity='10 mL')
Expand Down
137 changes: 137 additions & 0 deletions proposals/dilute.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
Dilute
""""""

Currently ``Container.dilute(solute, concentration, solvent, name)`` consumes all of the source container.

We use create_solution_from to use some of the source container to create a new container with a different concentration.

I propose we combine these into a new ``dilute()`` function.

This is an example of dilute::

salt_water = Container.create_solution(solute=salt, solvent=water, concentration='1 mol/L', total_quantity='100 mL')
salt_water = salt_water.dilute(solute=salt, solvent=water, concentration='0.5 M')

>>> salt_water
+-------------------------+------------+-----------+------------+-----+
| Solution of NaCl in H2O | Volume | Mass | Moles | U |
+-------------------------+------------+-----------+------------+-----+
| Maximum Volume | ∞ | - | - | - |
| NaCl | 5.844 mL | 5.844 g | 100.0 mmol | - |
| H2O | 194.156 mL | 194.156 g | 10.777 mol | - |
| Total | 200.0 mL | 200.0 g | 10.877 mol | 0 U |
+-------------------------+------------+-----------+------------+-----+

>>> salt_water.get_concentration(solute=salt, units='M')
0.5 M


This is an example of create_solution_from::

salt_water1M = Container.create_solution(name='salt water (1 M)', solute=salt, solvent=water, concentration='1 M', quantity='100 mL')

salt_water1M, salt_water500mM = Container.create_solution_from(name='salt water (0.5 M)', source=salt_water1M, solute=salt,
solvent=water, concentration='0.5 M', quantity='10 mL')


>>> salt_water1M
+------------------+-----------+----------+-----------+-----+
| salt water (1 M) | Volume | Mass | Moles | U |
+------------------+-----------+----------+-----------+-----+
| Maximum Volume | ∞ | - | - | - |
| NaCl | 5.552 mL | 5.552 g | 95.0 mmol | - |
| H2O | 89.448 mL | 89.448 g | 4.965 mol | - |
| Total | 95.0 mL | 95.0 g | 5.06 mol | 0 U |
+------------------+-----------+----------+-----------+-----+

>>> salt_water500mM
+--------------------+----------+----------+--------------+-----+
| salt water (0.5 M) | Volume | Mass | Moles | U |
+--------------------+----------+----------+--------------+-----+
| Maximum Volume | ∞ | - | - | - |
| H2O | 9.708 mL | 9.708 g | 538.865 mmol | - |
| NaCl | 292.0 uL | 292.2 mg | 5.0 mmol | - |
| Total | 10.0 mL | 10.0 g | 543.865 mmol | 0 U |
+--------------------+----------+----------+--------------+-----+

5 mL of the 100mL of 1M solution was used to create a 10 mL 0.5M solution, leaving 95 mL of the 1M solution.

This is an example of create_soution_from using a container for the solvent::

water_stock = Container(name='water stock', initial_contents=[(water, '100 mL')])
salt_water1M = Container.create_solution(name='salt water (1 M)', solute=salt, solvent=water, concentration='1 M', total_quantity='100 mL')

salt_water1M, water_stock, salt_water500mM = Container.create_solution_from(name='salt water (0.5 M)', source=salt_water1M,
solute=salt, solvent=water_stock,
concentration='0.5 M', quantity='10 mL')

>>> salt_water1M
+------------------+-----------+----------+-----------+-----+
| salt water (1 M) | Volume | Mass | Moles | U |
+------------------+-----------+----------+-----------+-----+
| Maximum Volume | ∞ | - | - | - |
| NaCl | 5.552 mL | 5.552 g | 95.0 mmol | - |
| H2O | 89.448 mL | 89.448 g | 4.965 mol | - |
| Total | 95.0 mL | 95.0 g | 5.06 mol | 0 U |
+------------------+-----------+----------+-----------+-----+

>>> water_stock
+----------------+---------+--------+-----------+-----+
| water stock | Volume | Mass | Moles | U |
+----------------+---------+--------+-----------+-----+
| Maximum Volume | ∞ | - | - | - |
| H2O | 95.0 mL | 95.0 g | 5.273 mol | - |
| Total | 95.0 mL | 95.0 g | 5.273 mol | 0 U |
+----------------+---------+--------+-----------+-----+

>>> salt_water500mM
+--------------------+----------+----------+--------------+-----+
| salt water (0.5 M) | Volume | Mass | Moles | U |
+--------------------+----------+----------+--------------+-----+
| Maximum Volume | ∞ | - | - | - |
| NaCl | 292.0 uL | 292.2 mg | 5.0 mmol | - |
| H2O | 9.708 mL | 9.708 g | 538.865 mmol | - |
| Total | 10.0 mL | 10.0 g | 543.865 mmol | 0 U |
+--------------------+----------+----------+--------------+-----+

5 mL of the 1 M salt water and 5mL of the water stock were combined to make the new solution.

I propose the following:

- ``dilute()`` should be a method of the container class.
- ``dilute()`` should take the following arguments:

- solute
- solvent
- concentration
- name
- quantity (optional)
- If quantity is not provided, the container will be diluted to the maximum volume.
- If quantity is provided, the container will be diluted to the specified quantity.
- If the quantity is greater than the maximum volume, an exception will be raised.
- The function will return an (new) updated version of the original container and the new container.
- If a container is used as the solvent, the remainder of the solvent will be returned.

The full dilute example will now be::

salt_water = Container.create_solution(solute=salt, solvent=water, concentration='1 mol/L', total_quantity='100 mL')
_, salt_water = salt_water.dilute(solute=salt, solvent=water, concentration='0.5 M')

The partial dilute example will now be::

salt_water1M = Container.create_solution(name='salt water (1 M)', solute=salt, solvent=water, concentration='1 M', total_quantity='100 mL')
salt_water1M, salt_water500mM = salt_water1M.dilute(solute=salt, solvent=water, concentration='0.5 M', quantity='10 mL')

The example with a container as the solvent will now be::

water_stock = Container(name='water stock', initial_contents=[(water, '100 mL')])
salt_water1M = Container.create_solution(name='salt water (1 M)', solute=salt, solvent=water, concentration='1 M', total_quantity='100 mL')

salt_water1M, water_stock, salt_water500mM = salt_water1M.dilute(solute=salt, solvent=water_stock, concentration='0.5 M', quantity='10 mL')

A full dilute example with a container as the solvent::

water_stock = Container(name='water stock', initial_contents=[(water, '100 mL')])
salt_water1M = Container.create_solution(name='salt water (1 M)', solute=salt, solvent=water, concentration='1 M', total_quantity='100 mL')

_, water_stock, salt_water1M = salt_water1M.dilute(solute=salt, solvent=water_stock, concentration='0.5 M')
4 changes: 4 additions & 0 deletions pyplate/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,15 @@ def __init__(self):
assert self.moles_storage_unit[-3:] == 'mol'
self.moles_display_unit = yaml_config['moles_display_unit']
assert self.moles_display_unit[-3:] == 'mol'

self.volume_storage_unit = yaml_config['volume_storage_unit']
assert self.volume_storage_unit[-1] == 'L'
self.volume_display_unit = yaml_config['volume_display_unit']
assert self.volume_display_unit[-1] == 'L'

self.mass_display_unit = yaml_config['mass_display_unit']
assert self.mass_display_unit[-1] == 'g'

self.concentration_display_unit = yaml_config['concentration_display_unit']
# we can't use Unit to do a full check of the unit, so we just do a cursory check
assert ('/' in self.concentration_display_unit or self.concentration_display_unit[-1] == 'm' or
Expand Down
Loading

0 comments on commit be40d83

Please sign in to comment.