Labware

Creating Labware from Tables

To plan executable liquid handling workflows, Pourfecto needs information about how Stocks are contained in physical labware. Just like for Stock objects, Pourfecto uses JLIMS to create Labware objects.

Pourfecto can create populated labware objects from stock tables augmented with labware metadata. This is useful when source plates, destination plates, tubes, reservoirs, or other labware are described in CSV files, spreadsheets, or DataFrames.

Pourfecto.df_to_labwareFunction
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: Labware type code used by Pourfecto.generate(labware_code, name). Valid codes are keys(labwares).
  • 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.
  • Parses a Stock per row from the stock columns using df_to_stock.

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

Pourfecto.labware_to_dfFunction
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(typeof(lw)) for the labware containing the well
  • name: JLIMS.name(lw) labware instance name
  • well: JLIMS.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

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})


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
labwareLabware type code used by Pourfecto.generate(labware, name)
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],
)

Here, the columns labware, name, and well describe where each stock is located. The remaining columns describe the stock in that well.


Labware codes

The labware column should contain a labware code recognized by Pourfecto.

keys(labwares)

returns the available labware codes.

When parsing a table, Pourfecto calls:

Pourfecto.generate(labware_code, name)

for each unique (labware, name) pair.

For example, rows with:

labware = "WP96"
name = "source_plate"

will be placed on the same generated labware 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)

Because the stock data includes a volume column, Pourfecto interprets the stock portion as the "vc" volume/concentration format.

The result is a vector of labware objects. In this example, the vector contains a single 96-well plate named "source_plate" with stocks deposited into 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

Internally, df_to_labware performs the following 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

In addition to building labware from tables with df_to_labware, you can create labware objects directly in Julia with generate.

This is useful for examples, tests, notebooks, and workflows where you want to programmatically construct source or target labware.

Pourfecto.generateFunction
generate(T::AbstractString, name::AbstractString)

Generate a labware instance from a registered labware code.

T is a short identifier (index code) used to look up a labware template in the global labwares registry. If the code is present, this method delegates to the underlying generator:

generate(labwares[T], name)

Examples

lw = generate("WP96", "plate_1")   # code must exist in keys(labwares)

Adding stocks to labware

Pourfecto provides a convenience function for depositing a Stock into a specific well of a Labware object.

Pourfecto.add_stock!Function
add_stock!(lw::JLIMS.Labware, stock::JLIMS.Stock, row::Integer, col::Integer) -> JLIMS.Labware

Deposit stock into the well at (row, col) in lw.

This is a convenience wrapper around JLIMS.deposit! for manually filling labware. If the selected well is not empty, a warning is emitted and the function still attempts to deposit the stock.

Arguments

  • lw::JLIMS.Labware: Labware object to modify.
  • stock::JLIMS.Stock: Stock to deposit.
  • row::Integer: Row index of the target well.
  • col::Integer: Column index of the target well.

Returns

  • JLIMS.Labware: The modified labware object lw.

This function selects the well at (row, col), deposits the given stock into that well, and returns the modified labware object.

For example:

using JLIMS
using Unitful
using Pourfecto

plate = generate("DeepWP96", "source_plate")

water_stock = 1u"mL" * chem"water"

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

The returned object is the same labware object, modified in place:


Behavior when the well is not empty

If the selected well already contains a stock, add_stock! emits a warning and still attempts to deposit the new stock:

add_stock!(plate, another_stock, 1, 1)

This may combine with or otherwise modify the existing well contents depending on the behavior of JLIMS.deposit!.

Warning

add_stock! does not prevent depositing into non-empty wells. It warns, then proceeds.


Example: manually fill a source plate

using JLIMS
using Unitful
using Pourfecto

source_plate = generate("DeepWP96", "source_plate")

stock_a = 1u"mL" * chem"water"
stock_b = 900u"µL" * chem"water" + 100u"µL" * chem"ethanol"

add_stock!(source_plate, stock_a, 1, 1)  # A1
add_stock!(source_plate, stock_b, 1, 2)  # A2