mixsol

Pipetting planner for efficient combinatorial mixing of solutions.

https://github.com/rekumar/mixsol

Science Score: 44.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
    Found CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (9.4%) to scientific vocabulary
Last synced: 7 months ago · JSON representation ·

Repository

Pipetting planner for efficient combinatorial mixing of solutions.

Basic Info
  • Host: GitHub
  • Owner: rekumar
  • License: gpl-3.0
  • Language: Jupyter Notebook
  • Default Branch: main
  • Homepage:
  • Size: 24.4 MB
Statistics
  • Stars: 3
  • Watchers: 1
  • Forks: 0
  • Open Issues: 1
  • Releases: 7
Created over 4 years ago · Last pushed 9 months ago
Metadata Files
Readme License Citation

README.md

Binder PyPI version

MixSol

pip install mixsol

Pipetting planner for efficient combinatorial mixing of solutions. Often we want to interpolate a few stock solutions into many target mixtures. If some of these mixtures require only a tiny amount of a stock solution, the minimum volume for our pipette may limit our ability to make this solution without a serial dilution. Mixsol searches for mixing sequences that use only other target solutions as stepping stones to reach these difficult mixtures, minimizing waste.

Mixsol also has the ability to calculate the masses of solid reagents needed to make a target solution. Finally, measured amounts of solid reagents can be input to calculate the actual solution we have made.

Happy mixing!

Interpolating 4 stock solutions into 40 target solutions on an OpenTrons liquid handler!

Examples

Solution Mixing

Solutions are defined with the Solution class. Solutes and solvents are both defined by either dicts or their formula, which follows the (name1)(amount1)_(name2)(amount2)_..._(name)(amount) format. The names do not have to correspond to elements, so you can use placeholders for units that will be mixed. Parentheses can be used to simplify formulae as well: A2_B2_C == (A_B)2_C. An alias can be provided for the solution to simplify later analysis.

``` import mixsol as mx

stocksolutions = [ mx.Solution( solutes='FAPbI3', solvents='DMF9DMSO1', molarity=1, alias='FAPI' ), mx.Solution( solutes={ "MA": 1, "Pb": 1, "I": 3, }, solvents={ "DMF": 9, "DMSO": 1, }, molarity=1, alias='MAPI' ), ] ```

This process goes for both stock and target solutions.

You can manually generate your target solutions and place them in a list like so:

targets = [] for a in np.linspace(0, 0.8, 5): targets.append(mx.Solution( solutes={ "FA": a, "MA": 1-a, "Pb": 1, "I": 3, }, solvents="DMF9_DMSO1", molarity=1, alias=f'FA_{a:.3f}' ))

Or, if you want to mix in equal steps between two (or more!) endpoint solutions, you can use the interpolate function to generate a mesh of Solution obects. The following code block is nearly equivalent to the one above (it will interpolate all the way from 0-1 instead of 0-0.8, and it won't generate alias values).

target_mesh = mx.interpolate( solutions=stock_solutions, #this should be a list of 2 or more Solution's steps=5 #number of divisions. In this example, steps=5 will mix the input Solution's in 20% increments )

Stock and target solutions go into a Mixer object

sm = Mixer( stock_solutions = stock_solutions, targets = { t:60 #Solution:volume dictionary for t in targets }) which is then solved with constraints sm.solve( min_volume=20, #minimum volume for a single liquid transfer max_inputs = 3 #maximum number of solutions (stock or other target) that can be mixed to form one target )

The results can be displayed in two ways: - plain text output of liquid transfers, in order. use of the alias term really simplifies this output sm.print() ===== Stock Prep ===== 120.00 of FAPI 180.00 of MAPI ====== Mixing ===== Distribute FAPI: 54.00 to FA_0.600 36.00 to FA_0.400 30.00 to FA_0.800 Distribute MAPI: 60.00 to FA_0.000 36.00 to FA_0.600 54.00 to FA_0.400 30.00 to FA_0.200 Distribute FA_0.600: 30.00 to FA_0.800 Distribute FA_0.400: 30.00 to FA_0.200

  • a graph of solution transfers. This is harder to use in practice, but can give an overview of the mixing path. fig, ax = plt.subplots(figsize=(6,6)) sm.plot(ax=ax) Example Mixer.plot()

Note that the units of volume here are arbitrary. Using SI units for small volumes might cause numerical issues when solving a mixture strategy (eg you should use 10 microliters instead of 1e-5 liters).

Solution Preparation

Mixsol aids in determining the mass of solid reagents needed to form target solutions. We can also check the actual solution formed from recorded reagent masses. Here, the units do matter, and you should stick to SI units (mass in grams, volume in liters).

We define solid reagents with the Powder class. This requires at least a chemical formula delimited by underscores, similar to the Solution definition earlier. If this formula is a proper chemical formula of elements, the molar mass is calculated automatically. If not, you can pass the molar mass directly. The calculate_molar_mass function can be used for convenience. alias does the same thing it did for Solution.

``` from mixsol import Powder, calculatemolarmass, Weigher

powders = [ Powder('CsI'), Powder('PbI2'), Powder('PbBr2'), Powder('PbCl2'), Powder( formula='MAI', molarmass=calculatemolarmass('CH6NI'), alias='MAI', ), Powder( formula='FAI', molarmass = calculatemolarmass('CH5N2I'), alias='FAI', ) ] ```

The list of available Powders is fed into a Weigher object

weigher = Weigher( powders=powders ) which can then be used to determine powder amounts for a given volume of a target Solution

``` target=Solution( solutes='Cs0.05FA0.8MA0.15PbI2.4Br0.45Cl0.15', solvents='DMF9_DMSO1', molarity=1 )

answer = weigher.getweights( target, volume=1e-3, #in L ) print(answer) #masses of each powder, in grams {'CsI': 0.012990496098, 'PbI2': 0.322706258, 'PbBr2': 0.082576575, 'Pb_Cl2': 0.020857935, 'MAI': 0.02384543385, 'FAI': 0.1375746568} ```

Finally, we can also generate a Solution object by inputting a {powder:mass} dictionary into Weigher. We will just use the answer from before, but this can be manually input. result = weigher.weights_to_solution( weights=answer, volume=1e-3, solvents='DMF9_DMSO1', ) print(result) 2.4M Cs0.0208_I_MA0.0625_FA0.333_Br0.188_Cl0.0625_Pb0.417 in DMF9_DMSO1 The molarity of the output will by default be determined by the largest component amount. This can be a bit silly. Passing a component or a numeric value to molarity can be used to manually set the molarity. Note that this does not affect the solution itself, just the relative values of the formula units and the overall molarity.

result2 = weigher.weights_to_solution( weights=answer, volume=1e-3, #in L solvents='DMF9_DMSO1', norm='Pb', #normalize the formula+molarity such that Pb=1 ) print(result2) #result is a Solution object 1.0M Cs0.05_I2.4_Pb_MA0.15_Br0.45_Cl0.15_FA0.8 in DMF9_DMSO1

Solution objects can be compared - even if their molarity/formulae are apparently different, they will show as equal if the effective molarity of each component is within 0.01% between the solutions.

result == result2 True

Read the full documentation here.

Owner

  • Name: Rishi E Kumar
  • Login: rekumar
  • Kind: user
  • Location: Honolulu, HI
  • Company: Oceanit

Citation (CITATION.cff)

abstract: "A software tool to plan volume transfers for liquid handlers."
authors:
- affiliation: UC San Diego
  family-names: Rishi E Kumar
cff-version: 1.2.0
date-released: '2024-01-29'
doi: 10.5281/zenodo.10581827
license:
- cc-by-4.0
repository-code: https://github.com/rekumar/mixsol/tree/v0.5.1
title: 'rekumar/mixsol: Release for Zenodo Citation'
type: software
version: v0.5.1

GitHub Events

Total
  • Release event: 1
  • Push event: 2
  • Pull request event: 2
  • Create event: 1
Last Year
  • Release event: 1
  • Push event: 2
  • Pull request event: 2
  • Create event: 1

Committers

Last synced: about 3 years ago

All Time
  • Total Commits: 47
  • Total Committers: 2
  • Avg Commits per committer: 23.5
  • Development Distribution Score (DDS): 0.255
Top Committers
Name Email Commits
Rishi E Kumar 4****r@u****m 35
Rishi Kumar r****2@g****m 12

Issues and Pull Requests

Last synced: 8 months ago

All Time
  • Total issues: 1
  • Total pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Total issue authors: 1
  • Total pull request authors: 0
  • Average comments per issue: 0.0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • rekumar (1)
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels

Packages

  • Total packages: 1
  • Total downloads:
    • pypi 80 last-month
  • Total dependent packages: 0
  • Total dependent repositories: 1
  • Total versions: 9
  • Total maintainers: 1
pypi.org: mixsol

Planning tool for combinatorial solution mixing. Reach target solutions from mixes of starting solutions, constrained by minimum pipetting volumes. Also aids in computing amounts of powdered reagents required to form solutions with target solutes + molarities.

  • Versions: 9
  • Dependent Packages: 0
  • Dependent Repositories: 1
  • Downloads: 80 Last month
Rankings
Dependent packages count: 10.1%
Dependent repos count: 21.6%
Average: 23.3%
Downloads: 27.1%
Stargazers count: 27.8%
Forks count: 29.8%
Maintainers (1)
Last synced: 8 months ago

Dependencies

.github/workflows/release.yml actions
  • actions/checkout v4 composite
  • actions/create-release v1 composite
  • astral-sh/setup-uv v3 composite
  • pypa/gh-action-pypi-publish release/v1 composite
.github/workflows/test.yml actions
  • actions/checkout v4 composite
  • astral-sh/setup-uv v3 composite
pyproject.toml pypi
  • matplotlib *
  • molmass *
  • numpy *
  • scipy >=1.2.3
uv.lock pypi
  • appnope 0.1.4
  • asttokens 3.0.0
  • cffi 1.17.1
  • colorama 0.4.6
  • comm 0.2.2
  • contourpy 1.3.2
  • coverage 7.9.2
  • cycler 0.12.1
  • debugpy 1.8.15
  • decorator 5.2.1
  • executing 2.2.0
  • fonttools 4.59.0
  • iniconfig 2.1.0
  • ipykernel 6.29.5
  • ipython 9.4.0
  • ipython-pygments-lexers 1.1.1
  • jedi 0.19.2
  • jupyter-client 8.6.3
  • jupyter-core 5.8.1
  • kiwisolver 1.4.8
  • matplotlib 3.10.3
  • matplotlib-inline 0.1.7
  • mixsol 1.0.0
  • molmass 2025.4.14
  • nest-asyncio 1.6.0
  • numpy 2.3.1
  • packaging 25.0
  • pandas 2.3.1
  • parso 0.8.4
  • pexpect 4.9.0
  • pillow 11.3.0
  • platformdirs 4.3.8
  • pluggy 1.6.0
  • prompt-toolkit 3.0.51
  • psutil 7.0.0
  • ptyprocess 0.7.0
  • pure-eval 0.2.3
  • pycparser 2.22
  • pygments 2.19.2
  • pyparsing 3.2.3
  • pytest 8.4.1
  • pytest-cov 6.2.1
  • python-dateutil 2.9.0.post0
  • pytz 2025.2
  • pywin32 311
  • pyzmq 27.0.0
  • scipy 1.16.0
  • seaborn 0.13.2
  • six 1.17.0
  • stack-data 0.6.3
  • tomli 2.2.1
  • tornado 6.5.1
  • traitlets 5.14.3
  • typing-extensions 4.14.1
  • tzdata 2025.2
  • wcwidth 0.2.13