# `find-mfs`: Accurate mass ➜ Molecular Formulae

[![CI](https://github.com/mhagar/find-mfs/actions/workflows/ci.yml/badge.svg)](https://github.com/mhagar/find-mfs/actions/workflows/ci.yml)
[![PyPI version](https://img.shields.io/pypi/v/find-mfs)](https://pypi.org/project/find-mfs/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)

`find-mfs` is a simple Python package for finding 
molecular formulae candidates which fit some given mass (+/- an error window).
It implements Böcker & Lipták's algorithm for efficient formula finding, as 
implemented in SIRIUS. 

`find-mfs` also implements other methods 
for filtering the MF candidate lists:
- **Octet rule**
- **Ring/double bond equivalents (RDBE's)**
- **Predicted isotope envelopes**, generated using  using Łącki and Startek's algorithm
  as implemented in `IsoSpecPy`

## Motivation:
I needed to perform mass decomposition and, shockingly, I could not find a Python library for it 
(despite being a routine process). `find-mfs` is intended to be used by anyone looking to incorporate
molecular formula finding into their Python project.

## Installation
```commandline
pip install find-mfs
```

## Example Usage:

```python
# For simple queries, one can use this convenience function
from find_mfs import find_chnops

find_chnops(
    mass=613.2391,         # Novobiocin [M+H]+ ion; C31H37N2O11+
    charge=1,              # Charge should be specified - electron mass matters
    error_ppm=5.0,         # Can also specify error_da instead
                           # --- FORMULA FILTERS ----
    check_octet=True,      # Candidates must obey the octet rule
    filter_rdbe=(0, 20),   # Candidates must have 0 to 20 ring/double-bond equivalents
    max_counts='C*H*N*O*P0S2'      # Element constraints: unlimited C/H/N/O,
                                   # No phosphorous atoms, up to two sulfurs.
)
```
Output:
```
FormulaSearchResults(query_mass=613.2391, n_results=38)

Formula                   Error (ppm)     Error (Da)      RDBE
----------------------------------------------------------------------
[C6H25N30O4S]+                     -0.12       0.000073       9.5
[C31H37N2O11]+                      0.14       0.000086      14.5
[C14H29N24OS2]+                     0.18       0.000110      12.5
[C16H41N10O11S2]+                   0.20       0.000121       1.5
[C29H33N12S2]+                     -0.64       0.000392      19.5
... and 33 more
```

```python
# If processing many masses, it's better to instantiate a FormulaFinder object
from find_mfs import FormulaFinder

finder = FormulaFinder()
finder.find_formulae(
    mass=613.2391,         # Novobiocin [M+H]+ ion; C31H37N2O11+
    charge=1,              
    error_ppm=5.0,         
    # ... etc
)
```

### Jupyter Notebook:
See [this Jupyter notebook](docs/basic_usage.ipynb) for more thorough examples/demonstrations

---
**If you use this package, make sure to cite:**
- [Böcker & Lipták, 2007](https://link.springer.com/article/10.1007/s00453-007-0162-8) - this package uses their algorithm for formula finding...
    - ...as implemented in SIRIUS: [Böcker et. al., 2008](https://academic.oup.com/bioinformatics/article/25/2/218/218950)
- [Łącki, Valkenborg & Startek 2020](https://pubs.acs.org/doi/10.1021/acs.analchem.0c00959) - this package uses IsoSpecPy to quickly simulate isotope envelopes
- [Gohlke, 2025](https://zenodo.org/records/17059777) - this package uses `molmass`, which provides very convenient methods for handling chemical formulae


## Contributing

Contributions are welcome. Here's a list of features I feel should be implemented eventually:
- Statistics-based isotope envelope fitting
- Fragmentation constraints
- Element ratio constraints
- GUI app

## License

This project is distributed under the GPL-3 license.
