Metadata-Version: 2.4
Name: pyhte
Version: 0.1.2
Summary: A python package for high-throughput experiments and automation.
Author-email: Jan Haase <j.haase@rub.de>
License-Expression: GPL-3.0-only
Project-URL: documentation, https://pyhte.readthedocs.io
Project-URL: repository, https://gitlab.ruhr-uni-bochum.de/cheminfolab/pyhte
Project-URL: issues, https://gitlab.ruhr-uni-bochum.de/cheminfolab/pyhte/-/work_items
Project-URL: changelog, https://gitlab.ruhr-uni-bochum.de/cheminfolab/pyhte/-/blob/development/CHANGELOG.md
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sqlalchemy>=2.0
Provides-Extra: chem
Requires-Dist: rdkit; extra == "chem"
Dynamic: license-file

# PyHTE

A python package for high-throughput experiments and laboratory automation.

## Features

- **Compositions** of solvent volumes and solute amounts, with mixed solvents and dissolved solids
- **Labware**: containers and plates with capacities and dead volumes
- **Plans** that are simulated before dispensing, and **designs** that create them, e.g. titrations
- **Devices**: protocols for the UK Robotics D2 dispenser, measurements from the JASCO V-750 spectrophotometer
- **Records** of what was actually dispensed, and the state derived from them
- **Storage** of plates, plans, runs and measurements in an SQLite (or other SQL) database

## Installation

```bash
python3 -m pip install pyhte
```

The package is published as `pyhte` and imported as `hte`.

Optional RDKit support (molecule objects on substances):

```bash
python3 -m pip install "pyhte[chem]"
```
Development tools (including `pytest`, `ruff` and `pyright`, and `sphinx`) from a clone of the local package:
```bash
python3 -m pip install -e .[chem] --group dev
```

## Usage

Volumes are given in `L`, amounts in `mol`, concentrations in `mol/L`, masses in `g` and molar masses in `g/mol`.
The factors in `hte.units` convert from and to these base units: multiply to enter a value (`250*uL`), divide to read
one (`state[well].volume/uL`).

Containers and plates describe the labware only. What they contain is a `LabState`: an immutable snapshot that maps
containers to their `Composition`. A `Plan` starts from an initial state and never modifies it; `plan.simulate()`
derives the final state and checks every transfer on the way.

```python
from hte import Composition, Container, LabState, Plate, PlateType, Substance, TitrationDesign, TitrationSeries
from hte.units import mL, mM

water, dmso = Substance("water"), Substance("DMSO")
analyte, titrant = Substance("analyte"), Substance("titrant")

analyte_stock = Container(capacity=10*mL, label="analyte")
titrant_stock = Container(capacity=10*mL, label="titrant")
solvent = Container(capacity=50*mL, label="solvent")
plate = Plate.create(PlateType.well_24())

initial = LabState.create(
    {
        analyte_stock: Composition.solution(dmso, 5*mL, {analyte: 10*mM}),
        titrant_stock: Composition.solution(water, 5*mL, {titrant: 20*mM}),
        solvent: Composition.solution(water, 30*mL),
    },
    plates=[plate],  # registers the (empty) wells
)
design = TitrationDesign(
    series=TitrationSeries(
        analyte_concentration=[1*mM] * plate.size,
        titrant_concentration=[i * 4*mM / (plate.size - 1) for i in range(plate.size)],
    ),
    analyte=analyte_stock,
    titrant=titrant_stock,
    solvent=solvent,
    volume=1*mL,
    plate=plate,
)

plan = design.create_plan(initial)
final = plan.simulate()  # raises at the first transfer that is not possible

well = plate["B3"]
print(final[well].concentration(titrant)/mM)  # 1.391...
print(final[well].solvent_fractions)  # 10 % DMSO, 90 % water
print(initial[well].is_empty)  # True: the initial state is unchanged
```

The [documentation](docs/index.md) covers dispensing with the D2, recording runs, importing measurements from the
V-750 and saving to a database; see the [tutorial](docs/tutorials/titration.ipynb) for a complete walk-through.

Invalid input and violated domain rules raise `hte.exceptions.ValidationError`, a subclass of both `HTEError` and
`ValueError`.
