Liquid Classes
See Liquid Classes for the conceptual model — what a liquid class encapsulates, how the Hamilton and Bravo parameter models differ, and why liquid classes are dataclasses. This guide covers the procedural API for instantiating, browsing, and subclassing predefined classes.
Using Predefined Classes
Hamilton ships a library of named liquid classes. List available classes by printing the enum, then instantiate by name.
from unitelabs.labware.hamilton import LiquidClass
print(LiquidClass)
# HamiltonTip_300_Water_DispenseJet_Empty
# HamiltonTip_300_Water_DispenseJet_Part
# HamiltonTip_300_Water_DispenseSurface_Empty
# HamiltonTip_300_Water_DispenseSurface_Part
liquid_class = LiquidClass.HamiltonTip_300_Water_DispenseJet_Empty()
# HamiltonTip_300_Water_DispenseJet_Empty [Water (100%)]
# aspirate_flow_rate: 100
# dispense_flow_rate: 180
# ...
Any parameter can be overridden after instantiation:
liquid_class.aspirate_flow_rate = 200
The BravoLiquidClasses factory provides access to predefined classes. Use find() to search by tip type or volume.
from unitelabs.labware.agilent import BravoLiquidClasses
from unitelabs.labware.agilent.tips import AgilentTip_250
liquid_classes = BravoLiquidClasses()
# Access by name
lc = liquid_classes.OQ_96LT_water_highVol
# Find matching classes for a given tip and volume
results = liquid_classes.find(tip=AgilentTip_250, volume=100)
Available predefined classes (96LT head):
| Liquid Class | Tip | Volume Range | Description |
|---|---|---|---|
OQ_96LT_water_lowVol | AgilentTip_200 | 0–50 µL | Water, low volume |
OQ_96LT_water_highVol | AgilentTip_250 | 51–250 µL | Water, high volume |
When no liquid class is passed to aspirate or dispense, the SDK auto-selects based on the mounted tip type and requested volume.
Liquid Class Parameters
Hamilton
Key parameters on a Hamilton liquid class:
| Parameter | Description |
|---|---|
aspirate_flow_rate | Plunger speed during aspiration (µL/s) |
aspirate_mix_flow_rate | Plunger speed during mixing aspiration (µL/s) |
aspirate_transport_air_volume | Air drawn after aspiration to prevent dripping (µL) |
aspirate_blowout_air_volume | Pre-conditioning blowout air (µL) |
aspirate_swap_speed | Retract speed after aspiration (mm/s) |
aspirate_settling_time | Dwell time in liquid after aspiration (s) |
aspirate_over_aspirate_volume | Pre-wetting extra volume (µL) |
dispense_mode | Jet empty / jet part / surface empty / surface part |
dispense_flow_rate | Plunger speed during dispense (µL/s) |
dispense_stop_flow_rate | Flow rate at end of dispense step (µL/s) |
dispense_stop_back_volume | Air re-aspirated immediately after dispense (µL) |
curve | Volume correction map: {target_µL: corrected_µL, ...} |
Bravo
Key parameters on a Bravo liquid class:
| Parameter | Default | Description |
|---|---|---|
aspirate_velocity | 5.0 mm/s | Plunger speed during aspiration |
aspirate_acceleration | 10.0 mm/s² | Plunger acceleration |
aspirate_velocity_into_wells | 50.0 mm/s | Z descent velocity |
aspirate_post_delay_ms | 250 ms | Dwell after aspiration |
dispense_velocity | 5.0 mm/s | Plunger speed during dispense |
dispense_acceleration | 10.0 mm/s² | Plunger acceleration |
dispense_post_delay_ms | 250 ms | Dwell after dispense |
coefficients | 0.0, 1.0 | Polynomial volume correction: corrected = c₀ + c₁·v + c₂·v² + ... |
Creating a Custom Liquid Class
Subclass HamiltonLiquidClass and override the fields you want to change. All other fields inherit their default values.
import dataclasses
from unitelabs.labware import Decimal, Ingredient, Liquid, Mixture, PredefinedLiquids
from unitelabs.labware.hamilton import DispenseMode, HamiltonLiquidClass, StandardTip
@dataclasses.dataclass
class EthanolJetEmpty(HamiltonLiquidClass):
liquid: Mixture = dataclasses.field(
default_factory=lambda: Mixture([Ingredient(PredefinedLiquids.ETHANOL, 1)])
)
tip: type = HamiltonTip_300
aspirate_flow_rate: Decimal = Decimal(default="80")
aspirate_settling_time: Decimal = Decimal(default="1.5")
dispense_mode: int = DispenseMode.JET_EMPTY
dispense_flow_rate: Decimal = Decimal(default="150")
curve: dict[float, float] = dataclasses.field(
default_factory=lambda: {
0.0: 0.0,
50.0: 52.1,
100.0: 103.8,
200.0: 207.0,
300.0: 311.2,
}
)
Subclass BravoLiquidClass and override fields as needed. Provide coefficients directly or fit them from calibration data.
With direct coefficients:
import dataclasses
import decimal
from unitelabs.labware.agilent.liquids.bravo_liquid_class import BravoLiquidClass
from unitelabs.labware.agilent.tips import LT250Tip, AgilentTip
from unitelabs.labware.liquids import Liquid, Mixture, PredefinedLiquids
from unitelabs.labware.math import Decimal
def _ethanol_mixture() -> Mixture:
return Mixture({PredefinedLiquids.ETHANOL: 1})
@dataclasses.dataclass
class EthanolLiquidClass(BravoLiquidClass):
liquid: Mixture = dataclasses.field(default_factory=_ethanol_mixture)
tip: type[AgilentTip] = AgilentTip_250
min_volume: Decimal = dataclasses.field(default=Decimal(default="0"))
max_volume: Decimal = dataclasses.field(default=Decimal(default="250"))
coefficients: list[decimal.Decimal] = dataclasses.field(
default_factory=lambda: [decimal.Decimal("0.05"), decimal.Decimal("1.02")]
)
aspirate_velocity: Decimal = dataclasses.field(default=Decimal(default="35.0"))
dispense_velocity: Decimal = dataclasses.field(default=Decimal(default="40.0"))
With calibration curve (SDK fits the polynomial automatically):
@dataclasses.dataclass
class CalibratedEthanol(BravoLiquidClass):
tip: type[AgilentTip] = AgilentTip_250
coefficients: list[decimal.Decimal] | int = 2 # fit a 2nd-order polynomial
curve: dict[float, float] | None = dataclasses.field(
default_factory=lambda: {
0: 0.0, 50: 51.8, 100: 102.5, 150: 153.4, 200: 204.6, 250: 255.9
}
)
For full parameter details see Bravo Liquid Classes.