Labware

Creating Labware from Tables

To plan liquid handling workflows, Pourfecto needs to know how Stocks are held in physical labware. As for stocks, Pourfecto uses CHESSCore to create Labware objects.

Pourfecto creates populated labware from stock tables with added labware columns. This suits source plates, destination plates, tubes, reservoirs, and other labware described in CSV files, spreadsheets, or DataFrames.

CHESSCore.df_to_labware — Function
df_to_labware(df::DataFrame, units::DataFrame; kwargs...) -> Vector{Labware}

Reconstruct labware (and deposited well contents) from a dataframe representation.

df_to_labware expects labware metadata (labware, name, and well) plus the stock columns needed to reconstruct well contents. Stock content may be encoded in any stock dataframe format supported by df_to_stock.

Required columns in df

df must include the following columns:

  • labware: LocationKind name registered in location_kinds (e.g. "WP96"), used to look up the kind and call build_location. Valid codes are keys(location_kinds).
  • name: Name/identifier of the labware instance. Multiple rows may share the same name (and labware) to indicate multiple wells on the same physical item.
  • well: Well identifier in row/column form (e.g. "A1", "H2", "B12")

All remaining columns are treated as stock data and are passed to df_to_stock.

Behavior

  • Creates one labware instance per unique (labware, name) pair, via build_location(location_kinds[Symbol(labware)], name) – these are uncommitted (no database connection needed, location_id is nothing); commit the result yourself (e.g. via commit_location! in CHESSDatabase) if you want it tracked.
  • Parses a Stock per row from the stock columns using df_to_stock.
  • Looks up each row's well by name (lw[well]), not by position.

Arguments

  • df::DataFrame: Labware + stock data (one row per filled well).
  • units::DataFrame: Units/metadata table for the stock columns (passed through to df_to_stock).

See also

labware_to_df, df_to_stock

source
CHESSCore.labware_to_df — Function
labware_to_df(lws::Vector{<:Labware}, format::AbstractString = "vc"; kwargs...) -> (DataFrame, DataFrame)

Convert labware into a dataframe representation suitable for serialization

The output contains one row per non-empty well and includes three labware descriptor columns plus the stock columns:

  • labware: string(kind(lw).name) – the registered LocationKind name for the labware containing the well (not typeof(lw); every Labware shares one concrete type, so the model/kind lives in kind(lw), not the Julia type).
  • name: CHESSCore.name(lw) labware instance name
  • well: CHESSCore.name(w) well identifier (e.g., "A1")

Stock content is converted using stock_to_df in the requested format. The returned units dataframe corresponds to the stock columns (not the labware/name/well columns).

Arguments

  • lws::Vector{<:Labware}: Labware objects to export.
  • format::AbstractString = "vc": Stock encoding passed to stock_to_df.

See also

df_to_labware, stock_to_df

source
labware_to_df(lw::Labware, format::AbstractString = "vc"; kwargs...) -> (DataFrame, DataFrame)

Convenience method for exporting a single Labware object. Equivalent to labware_to_df([lw], format; kwargs...).

See also: labware_to_df(::Vector{<:Labware})

source

Overview

A labware table is a stock table with three additional columns:

  • labware
  • name
  • well

All other columns are interpreted as stock data and passed to df_to_stock.

labware = df_to_labware(df, units)

The reverse operation is:

df, units = labware_to_df(labware)

Required columns

The input dataframe must include:

ColumnDescription
labwareName of a LocationKind registered in location_kinds, such as WP96. The plate kinds used on this page come from CHESSLabConstants, which is loaded with CHESS. See Registering Lab Constants.
nameName of the labware instance
wellWell identifier, such as "A1", "B12", or "H2"

For example:

using DataFrames 

DataFrame(
    labware = ["WP96","WP96","WP96"],
    name = ["source_plate", "source_plate", "source_plate"],
    well = ["A1", "A2", "A3"],
    volume = [1000, 1000, 1000],
    water = [1.0, 1.0, 1.0],
)

The columns labware, name, and well give the location of each stock. The remaining columns describe the stock in that well.

Labware codes

The labware column holds the name of a LocationKind that CHESSCore knows. The available names are the keys of location_kinds:

keys(location_kinds)

For each unique (labware, name) pair, Pourfecto builds one labware object with build_location(location_kinds[Symbol(labware_code)], name). Rows with the same labware code and name, such as "WP96" and "source_plate", are placed on the same object.

Example: create labware from a volume/concentration table

The following example creates a source plate with three filled wells.

using DataFrames
using Pourfecto

df = DataFrame(
    labware = ["WP96","WP96","WP96"],
    name = ["source_plate", "source_plate", "source_plate"],
    well = ["A1", "A2", "A3"],
    volume = [1000, 1000, 500],
    water = [100, 80, 0],
    ethanol = [0, 20, 100],
)

units = DataFrame(
    volume = ["µL"],
    water = ["percent"],
    ethanol = ["percent"],
)

source_labware = df_to_labware(df, units)

The stock data includes a volume column, so the stock portion is read as the "vc" volume/concentration format.

The result is a vector of labware objects. Here it holds one 96-well plate named "source_plate" with stocks in wells A1, A2, and A3.

Example: multiple labware objects in one table

A single dataframe can describe multiple pieces of labware.

df = DataFrame(
    labware = ["WP96","WP96","WP96","WP96"],
    name = ["source_plate_1", "source_plate_1", "source_plate_2", "source_plate_2"],
    well = ["A1", "A2", "A1", "A2"],
    volume = [1000, 1000, 500, 500],
    water = [100, 50, 0, 25],
    ethanol = [0.0, 50, 100, 75],
)

units = DataFrame(
    volume = ["µL"],
    water = ["percent"],
    ethanol = ["percent"],
)

lws = df_to_labware(df, units)

This creates two labware objects:

  • source_plate_1
  • source_plate_2

Rows with the same (labware, name) pair are placed on the same labware object.

How df_to_labware works

df_to_labware performs these steps:

  1. Splits the table into labware metadata columns and stock columns.
  2. Creates one labware object for each unique (labware, name) pair.
  3. Parses the stock columns using df_to_stock.
  4. Converts each well label, such as "A1", into a well index.
  5. Deposits each parsed stock into the corresponding well.

Only the stock columns should appear in the units dataframe. The units dataframe should not include labware, name, or well.

Exporting labware to dataframes

Use labware_to_df to convert labware objects back into table form.

df, units = labware_to_df(source_labware)

By default, this exports stock contents using the "vc" format.

df, units = labware_to_df(source_labware, "vc")

To export using quantity format:

df, units = labware_to_df(source_labware, "q")

The output dataframe contains one row per non-empty well.

Creating Labware Manually

Labware can also be built and filled directly with CHESSCore's build_location, location_kinds, and add_stock!. This suits examples, tests, and notebooks where a dataframe is unnecessary:

using Pourfecto, CHESSCore, Unitful

plate = build_location(location_kinds[:DeepWP96], "source_plate")
water_stock = 1u"mL" * string_to_component("water", Liquid)

add_stock!(plate, water_stock, 1, 1)  # adds stock to row 1, column 1; well A1

CHESSCore's Locations page describes location kinds, wells, and deposit!, which add_stock! uses.

Note

Some Pourfecto examples set .stock directly, as in children(plate)[row, col].stock = ..., or add to it, as in well.stock += ..., instead of calling add_stock! or deposit!. This shortcut skips the well-capacity check of deposit!. It is appropriate when building a target composition that is known to fit. Use deposit! for labware whose existing contents are not known.