API Reference


Full docstrings

Pourfecto.ActuatorType

abstract type Actuator end

Represents the mechnaism used to move liquid in a Piston.

Pourfecto.ChannelType

struct Channel capacity::Unitful.Volume end

A Channel defines the liquid handler's capacity for carrying liquid. This can be set independent of the piston.

Pourfecto.ConfigurationType
struct Configuration{I<:Instrument}
  head::Head{I}
  deck::Deck
  settings::InstrumentSettings
end

An instance of a liquid handler instrument with definitions for the head, deck, and other settings

See Also: Head, Deck, InstrumentSettings

Pourfecto.ConstrainedPositionType

struct ConstrainedPosition <: DeckPosition name::String labware::Set{Type{<:Labware}} slots::Tuple{Int,Int} aspirate::Bool dispense::Bool plotting_shape::String end

Represents a position that can hold a set of specified JLIMS.Labware` Types. Constrained positions have specified slots for allowable labware

Pourfecto.DeckType

Deck = AbstractArray{<:DeckPosition}

Alias for an array of DeckPosition objects

Pourfecto.EmptyPositionType

struct EmptyPosition <: DeckPosition name::String end

Represents an empty or inaccessible position on the deck.

Pourfecto.HeadType

struct Head{I<:Instrument} pistons::AbstractArray{<:Piston} channels::AbstractArray{Channel} mask::BitMatrix end

An instance of a head for a instrument I. Heads contains pistons that indpendently control aspiration and dispensation volumes and channels that access wells. Pistons and channels are usually arranged as an array. The head definition also includes a mask that defines which pistons are connected to which channels.


julia> abstract type ExSingleChannel <: Instrument end 

julia> piston = Piston{ContinuousActuator,SingleRepeater}((20u"µL",200u"µL"),(20u"µL",200u"µL"),1) 

julia> head = Head{ExSingleChannel}(
  [piston],
  [Channel(220u"µL")],
  trues(1,1)
)

See Also Piston, Channel

Pourfecto.InstrumentType

abstract type Instrument end

Abstract Supertype to define new instruments. Each new class of instrument should define a new abstract type

For example,

julia>  abstract type NewInstrument <: Instrument end 

We refer to instances of liquid handlers as a Configuration which are parameterized by the abstract instrument type

julia> Configuration{SingleChannel} 

See Also Configuration

Pourfecto.MaskType
struct Mask 
    head::Head
    labware::Labware
    asp::Function 
    disp::Function
    asp_positions::Tuple{Integer,Integer}
    disp_positions::Tuple{Integer,Integer}
end

Contains all of the mask related objects used to compute flows. We include separate entries for the aspirate and dispnese mask functions becuase they may be different for some liquid handlers.

Pourfecto.MultiRepeaterType

abstract type MultiRepeater <: RepeaterStyle end

MultiRepeater styles allow the piston to dispense a single aspiration in multiple shots

See Also: RepeaterStyle

Pourfecto.ParameterDictType
ParameterDict = Dict{Symbol,Any}

An Alias for Dict{Symbol,Any} that can be used to store keyword arguments for Pourfecto

Pourfecto.PistonType

struct Piston{A <: Actuator, R<: RepeaterStyle} minAsp::Unitful.Volume maxAsp::Unitful.Volume minDisp::Unitful.Volume maxDisp::Unitful.Volume deadPad::Real end

Pistons control how liquid moves in and out of the Head of a Liquid handler. They are parameterized by an Actuator and a [RepeaterSytle]. The fields of the piston define their maximum and minimum aspirating and dispensing volumes. The deadPad field is a multiplicative factor to account for error and miscalibration in the piston.

Pourfecto.PourcastType
Pourcast

A result container for the pourfecto workflow. Pourcast bundles the relevant source/target stocks and labware, the instrument configuration set used for scheduling, the parameters supplied to pourfecto, and the resulting model solution and objective value.

Pourfecto.PriorityDictType
PriorityDict = Dict{String,UInt64}

Dictionary type alias mapping reagent identifiers to a priority rank used during the Pourfecto planning stage.

Lower values represent higher priority (e.g., 1 is higher priority than 2). A common convention is to use typemax(UInt64) to indicate “lowest priority” or “effectively no priority preference”.

Examples

```jldoctest julia> PriorityDict( "sodium" => 1, "potassium" => 1, "ethanol" => 2, "water" => typemax(UInt64), ) Dict{String, UInt64} with 4 entries: "water" => 18446744073709551615 "ethanol" => 2 "sodium" => 1 "potassium" => 1

Pourfecto.RepeaterStyleType

abstract type RepeaterStyle end

Represents how a liquid handler handles repeated aspirations and dispenses. For example, a single channel pipette must dispense its entire aspirate in one shot, while other liquid handlers can fire repeated shots.

Pourfecto.SingleRepeaterType

abstract type SingleRepeater <: RepeaterStyle end

SingleRepeater styles require that when a piston dispenses, it dispenses all of its volume in a single shot

See Also: RepeaterStyle

Pourfecto.UnconstrainedPositionType

struct UnconstrainedPosition <: DeckPosition name::String end

Represents a position that can hold any number of any labware

Pourfecto.cobra_settingsConstant
cobra_settings

A dictionary with settings for the cobra

dictionary keys:

  • pause=true: pause over each well before dispensing. If false, the instrument moves continuously while dispensing.
  • predispenses=0: force the instrument to predispense a shot of liquid before starting the run.
  • cobra_path: the local path the cobra should look for protocol files on the machine running the instrument
  • washtime: set how long the wash system should flush the nozzles for
  • AspPad = 1.125: Force the cobra to overaspirate by a factor of AspPad. Pourfecto takes this into account when planning
  • AspDistnace = 1.5: set how many millimeters above the bottom of the labware the cobra should aspirate from.
Pourfecto.configurationsConstant
configurations

By default, Pourfecto provides an assortment of pre-defined instruments. Use keys(configurations) to see available instruments

Pourfecto.labwaresConstant
labwares

By default, Pourfecto provides an assortment of pre-defined labware. Use keys(labwares) to see available instruments

Pourfecto.objectivesConstant
objectives

Pourfecto provides an assortment of pre-defined objectives. Use keys(objectives) to see available options

Note that many non-defualt options turn the problem into an MILP in the scheudling phase, which can lead to long and unpredictable run times.

Pourfecto.add_stock!Method
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.
Pourfecto.compileMethod
compile(
    directory::AbstractString,
    pourcast::Pourcast;
    packing_method::Function = packing_greedy,
    kwargs...
) -> Nothing

Compile a Pourcast into one or more protocol folders on disk.

For each configuration in configs(pourcast), this function determines which source/target labware pairs must be simultaneously accessible (“slotted”) and uses packing_method to produce one or more feasible slotting layouts. For each layout, it extracts the corresponding well-to-well transfer submatrix for that configuration and writes instrument files into a protocol-specific subfolder.

Output layout

For each configuration c, files are written under:

directory/<config_type>/<protocol_name>/...

where <config_type> = string(get_config_type(configs(pourcast)[c])) and <protocol_name> is generated by random_adverb_verb_pairs.

Arguments

  • directory::AbstractString: Root directory where configuration/protocol subdirectories will be created.
  • pourcast::Pourcast: The plan to compile (provides labware, configurations, and flows).

Keyword Arguments

  • packing_method::Function = packing_greedy: Packing/slotting routine called as packing_method(slotting_pairs, config). It must return a collection of slotting dictionaries (one per generated protocol).
  • check_quality::Bool = true: Force the compiler to check the quality of solution before compiling using the solution_quality function.
  • kwargs...: Forwarded keyword arguments passed to slotting_requirements(...) and write_instrument_files(...) (e.g., a transfer threshold).

Side Effects

  • Creates directories using mkdir when needed.
  • Writes instrument/protocol files via write_instrument_files.

Algorithm (per configuration)

  1. Compute slotting_reqs = slotting_requirements(pourcast; kwargs...).
  2. Convert slotting_reqs[c] into a vector of (source::Labware, target::Labware) pairs that require co-slotting.
  3. Generate one or more slotting solutions: slotting_dicts = packing_method(slotting_pairs, configs(pourcast)[c]).
  4. For each solution p:
    • Determine slotted sources/targets from the dict keys.
    • Convert labware to well-index selectors using labware_indices(pourcast).
    • Slice the configuration transfer matrix: protocol_design = transfers_by_config(pourcast)[c][s_idxs, t_idxs].
    • Convert protocol_design to a DataFrame and write files.
Pourfecto.concentrationMethod
concentration(stock::JLIMS.Stock,ingredient::JLIMS.Ingredient)

Return the concentration of an ingredient in a stock using the preferred units for that ingredient and stock

Pourfecto.configsMethod
configs(p::Pourcast) -> Vector{<:Configuration}

Return the instrument configurations used by pourfecto to schedule the liquid handling operations

Pourfecto.df_to_labwareMethod
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.df_to_stockMethod
df_to_stock(df::DataFrame, units::DataFrame; kwargs...) -> Vector{Stock}

Parse a pair of DataFrame representations into a vector of Stocks.

This is a format-dispatching wrapper that supports two encodings:

  • "vc" (volume/concentration): tables include a "volume" column (in both df and units) and are parsed via vc_to_stock(df, units; kwargs...).
  • "q" (quantity): tables omit "volume" and are parsed via q_to_stock(df, units; kwargs...).

The function auto-detects the encoding by checking for the presence of the "volume" column in both df and units.

Arguments

  • df::DataFrame: Main stock table (one row per stock; columns depend on the chosen format).
  • units::DataFrame: Units/metadata table aligned with df (e.g., unit strings for numeric columns).

See also

stock_to_df

Pourfecto.flowsMethod
flows(p::Pourcast)

Return the flow decision variable(s) from a Pourcast solution.

This is an accessor that extracts the solver/model variable stored under the key :Q from the pourcast's model_solution

See also

transfers, get_pourfecto_solution_variable

Pourfecto.flows_by_configMethod
flows_by_config(pc::Pourcast) -> Vector{Matrix{Float64}}

Partition the full flow matrix into one flow matrix per configuration.

This function computes aspiration and dispense flow nodes across all configurations, then slices the global flow matrix flows(pc) into C matrices (one per configuration in configs(pc)). For configuration c, only entries corresponding to aspiration/dispense nodes whose configuration equals configs(pc)[c] are copied from the global flow matrix; all other entries remain zero.

Arguments

  • pc::Pourcast: A pour/transfer plan providing configurations, labware, and a global flow matrix via flows(pc).

Returns

  • Vector{Matrix{Float64}}: A length-C vector flws_by_config where C == length(configs(pc)). Each entry flws_by_config[c] is an A×D matrix (A == length(asp_nodes), D == length(disp_nodes)) whose nonzero entries represent flows for configuration c.
Pourfecto.generateMethod
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)
Pourfecto.labware_indicesMethod
labware_indices(pc::Pourcast) -> Tuple{Dict,Dict}

Construct dictionaries mapping each source/target labware item to the vector of indices it occupies in the flattened transfer indexing scheme.

This function expands source_labware(pc) and target_labware(pc) into “by-index” vectors where each labware object is repeated length(x) times, then computes, for each labware object x, the positions (via findall) at which it appears in that expanded vector. The result is two dictionaries:

  • src_dict: maps each source labware object to a vector of integer indices into the expanded source index space.
  • tgt_dict: maps each target labware object to a vector of integer indices into the expanded target index space.

These index vectors are intended to be used as selectors when indexing transfer data.

Arguments

  • pc::Pourcast: A pour/transfer plan providing source_labware(pc) and target_labware(pc).

Returns

  • (src_dict, tgt_dict): A 2-tuple of dictionaries:
    • src_dict::Dict{<:Any, <:AbstractVector{Int}} mapping each element of source_labware(pc) to a vector of indices.
    • tgt_dict::Dict{<:Any, <:AbstractVector{Int}} mapping each element of target_labware(pc) to a vector of indices.
Pourfecto.labware_to_dfFunction
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})

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

Pourfecto.model_solutionMethod
model_solution(p::Pourcast) -> Dict{Symbol,Any}

Return the raw JuMP model output from the pourfecto algorithm.

Pourfecto.paramsMethod
params(p::Pourcast) -> ParameterDict

Return the parameter dictionary used to store all metadata from each pourfecto run.

Pourfecto.planned_stocksMethod
planned_stocks(p::Pourcast) -> Vector{Stock}

Compute the planned target stocks from a Pourcast solution.

This function reconstructs each target stock as a mixture of the source stocks using the planned transfer volumes from the solution. Conceptually, for each target t:

planned[t] = Σₛ ( V[s,t] * source_stocks(p)[s] )

where V is the transfer-volume decision variable.

Details

  • Transfer volumes are rounded to a number of digits derived from params(p)[:min_vol_threshold] to reduce numerical noise: digits = -floor(log10(min_vol_threshold)).
  • Volumes are interpreted as microliters and converted to Unitful quantities via V .* u"µL".

See Also

target_stocks

Pourfecto.plot_flowMethod
plot_flow(a::AspNode, d::DispNode, vol::Real; kwargs...) -> Any

Create a visualization of a single flow from an aspiration node to a dispense node.

This helper plots the aspiration node a and dispense node d (via plot(a; kwargs...) and plot(d; kwargs...)) and combines them into a single figure with a title that includes the node configuration type and the requested volume (in µL).

Arguments

  • a::AspNode: Aspiration (source-side) flow node to visualize.
  • d::DispNode: Dispense (target-side) flow node to visualize.
  • vol::Real: Flow volume to display in the title (interpreted as µL).
Pourfecto.plot_flowsMethod
plot_flows(pc::Pourcast; threshold::Real = 1e-4) -> Vector

Generate per-flow plots for all flows in a solved Pourcast above a threshold.

This function:

  1. Builds aspiration and dispense node grids with compute_flow_nodes using the configs(pc) and the corresponding source/target labware.
  2. Reads the flow matrix via flows(pc).
  3. For each matrix entry (i, j) whose flow is >= threshold, generates a plot with plot_flow and collects the results.

Arguments

  • pc::Pourcast: A solved Pourcast

See also

flows, plot_flow

Pourfecto.plot_slottingMethod
plot_slotting(deck::Deck, slotting::SlottingDict;
              wrapwidth::Integer=20,
              titlefontsize::Integer=18,
              fontsize::Integer=14,
              plotsize=(1200,800)) -> Plots.Plot

Plot a deck with labware arranged according to slotting.

Assumptions about slotting (based on your other plotting code):

  • slotting[lw][1] is a Position object on the deck (e.g., ConstrainedPosition, StackPosition, ...).
  • slotting[lw][2] is an index (row,col) within that position's slot grid where the labware is placed.

Assumptions about deck:

  • deck is either a matrix-like container (2D) of positions, or a vector of positions.
Pourfecto.pourcast_to_jsonMethod
pourcast_to_json(p::Pourcast) -> String

Serialize a Pourcast to a JSON string.

This converter prepares Pourcast contents for JSON encoding by first converting stocks, labware, and configurations into table-like or dictionary-like representations, then assembling a single dictionary and encoding it with JSON.json.

Pourfecto.pourfectoMethod
    pourfecto(directory::AbstractString,
            source_labware::Vector{<:JLIMS.Labware},
            target_labware::Vector{<:JLIMS.Labware},
            configs::Union{Vector{<:AbstractString},Vector{<:Configuration}};
            kwargs...)

Run the Pourfecto algorithm in planning and scheduling mode and automatically compile the resulting Pourcast(@ref) in directory.

If the Pourcast fails a solution_quality check, pourfecto will produce an error message while saving the Pourcast and quality report to the directory

Arguments

  • directory: The output directory (will be made if it doesn't already exist)
  • source_labware: The JLIMS.Labware objects containing source stocks that pourfecto can use to create the targets
  • target_labware: The JLIMS.Labware objects containing target stocks pourfecto is trying to create using the sources
  • configs : The the Configuration objects or String identifiers for instrument configurations pourfecto can use to schedule the planned liquid transfers.

Keyword Arguments

  • objective = min_cost_flow: Set the scheduling objective for Pourfecto. See keys(objectives) for options.
  • priority = Dict{String, UInt64}(): a PriorityDict that specifies which reagents take precedence over others in a plan.
  • quiet = true: Supress the printout of the solver.
  • grb_timelimit = 30: Set the Gurobi solver's TimeLimit paramter.
  • grb_feasibility_tol = 1.0e-6: Set the Gurobi solver's FeasibilityTol parameter.
  • min_vol_threshold = 0.1: Set the minimum volume threshold in µL. Any transfer must be at least this large.
  • require_nonzero = true: If a target contains a reagent, require that some amount of that reagent is delivered, even if the optimal solution is none.
  • enforce_minimum_shot = false: Enforce the minimum shot volume constraints for each instrument. Caution turns the problem into an MILP.
  • slack_tol = 0.01: Set the tolerance of the slacks in the solution to stay with in a percentage of the optimal value. A value of 0.01 equates to a 1% tolerance.
  • config_costs = ones(length(configs)): Set the relative cost of using each configuration.
  • solution_tolerance = 0.01: Set the limit for the magnitude of any single slack. A value of 1 indicates that the slack can be as large as the largest target in the model.
Pourfecto.pourfectoMethod
    pourfecto(source_labware::Vector{<:JLIMS.Labware},
            target_labware::Vector{<:JLIMS.Labware},
            configs_string::Vector{<:AbstractString};kwargs...)

Run the Pourfecto algorithm in planning and scheduling mode.

Arguments

  • source_labware: The JLIMS.Labware objects containing source stocks that pourfecto can use to create the targets
  • target_labware: The JLIMS.Labware objects containing target stocks pourfecto is trying to create using the sources
  • configs_string : The String identifiers for instrument configurations pourfecto can use to schedule the planned liquid transfers.

Keyword Arguments

  • objective = min_cost_flow: Set the scheduling objective for Pourfecto. See keys(objectives) for options.
  • priority = Dict{String, UInt64}(): a PriorityDict that specifies which reagents take precedence over others in a plan.
  • quiet = true: Supress the printout of the solver.
  • grb_timelimit = 30: Set the Gurobi solver's TimeLimit paramter.
  • grb_feasibility_tol = 1.0e-6: Set the Gurobi solver's FeasibilityTol parameter.
  • min_vol_threshold = 0.1: Set the minimum volume threshold in µL. Any transfer must be at least this large.
  • require_nonzero = true: If a target contains a reagent, require that some amount of that reagent is delivered, even if the optimal solution is none.
  • enforce_minimum_shot = false: Enforce the minimum shot volume constraints for each instrument. Caution turns the problem into an MILP.
  • slack_tol = 0.01: Set the tolerance of the slacks in the solution to stay with in a percentage of the optimal value. A value of 0.01 equates to a 1% tolerance.
  • config_costs = ones(length(configs)): Set the relative cost of using each configuration.
  • solution_tolerance = 0.01: Set the limit for the magnitude of any single slack. A value of 1 indicates that the slack can be as large as the largest target in the model.
Pourfecto.pourfectoMethod
    pourfecto(source_labware::Vector{<:JLIMS.Labware},
            target_labware::Vector{<:JLIMS.Labware},
            configs::Vector{<:Configuration};
            kwargs...)

Run the Pourfecto algorithm in planning and scheduling mode

Arguments

  • source_labware: The JLIMS.Labware objects containing source stocks that pourfecto can use to create the targets
  • target_labware: The JLIMS.Labware objects containing target stocks pourfecto is trying to create using the sources
  • configs : The instrument configurations pourfecto can use to schedule the planned liquid transfers.

Keyword Arguments

  • objective = min_cost_flow: Set the scheduling objective for Pourfecto. See keys(objectives) for options.
  • priority = Dict{String, UInt64}(): a PriorityDict that specifies which reagents take precedence over others in a plan.
  • quiet = true: Supress the printout of the solver.
  • grb_timelimit = 30: Set the Gurobi solver's TimeLimit paramter.
  • grb_feasibility_tol = 1.0e-6: Set the Gurobi solver's FeasibilityTol parameter.
  • min_vol_threshold = 0.1: Set the minimum volume threshold in µL. Any transfer must be at least this large.
  • require_nonzero = true: If a target contains a reagent, require that some amount of that reagent is delivered, even if the optimal solution is none.
  • enforce_minimum_shot = false: Enforce the minimum shot volume constraints for each instrument. Caution turns the problem into an MILP.
  • slack_tol = 0.01: Set the tolerance of the slacks in the solution to stay with in a percentage of the optimal value. A value of 0.01 equates to a 1% tolerance.
  • config_costs = ones(length(configs)): Set the relative cost of using each configuration.
  • solution_tolerance = 0.01: Set the limit for the magnitude of any single slack. A value of 1 indicates that the slack can be as large as the largest target in the model.
Pourfecto.pourfectoMethod
    pourfecto(source_labware::Vector{<:JLIMS.Labware},
            target_labware::Vector{<:JLIMS.Labware};
            kwargs...)

Run the Pourfecto algorithm in planning mode using just the source and target labware

Arguments

  • source_labware: The JLIMS.Labware objects containing source stocks that pourfecto can use to create the targets
  • target_labware: The JLIMS.Labware objects containing target stocks pourfecto is trying to create using the sources

Keyword Arguments

  • objective = min_cost_flow: Set the scheduling objective for Pourfecto. See keys(objectives) for options.
  • priority = Dict{String, UInt64}(): a PriorityDict that specifies which reagents take precedence over others in a plan.
  • quiet = true: Supress the printout of the solver.
  • grb_timelimit = 30: Set the Gurobi solver's TimeLimit paramter.
  • grb_feasibility_tol = 1.0e-6: Set the Gurobi solver's FeasibilityTol parameter.
  • min_vol_threshold = 0.1: Set the minimum volume threshold in µL. Any transfer must be at least this large.
  • require_nonzero = true: If a target contains a reagent, require that some amount of that reagent is delivered, even if the optimal solution is none.
  • enforce_minimum_shot = false: Enforce the minimum shot volume constraints for each instrument. Caution turns the problem into an MILP.
  • slack_tol = 0.01: Set the tolerance of the slacks in the solution to stay with in a percentage of the optimal value. A value of 0.01 equates to a 1% tolerance.
  • config_costs = ones(length(configs)): Set the relative cost of using each configuration.
  • solution_tolerance = 0.01: Set the limit for the magnitude of any single slack. A value of 1 indicates that the slack can be as large as the largest target in the model.
Pourfecto.pourfectoMethod
    pourfecto(sources::Vector{<:JLIMS.Stock},
            targets::Vector{<:JLIMS.Stock};
            kwargs...)

Run the Pourfecto algorithm in planning mode using just the source and target stocks

Arguments

  • sources: The JLIMS.Stock objects that pourfecto can use to create the targets
  • targets: The JLIMS.Stock objects that pourfecto is trying to create using the sources

Keyword Arguments

  • objective = min_cost_flow: Set the scheduling objective for Pourfecto. See keys(objectives) for options.
  • priority = Dict{String, UInt64}(): a PriorityDict that specifies which reagents take precedence over others in a plan.
  • quiet = true: Supress the printout of the solver.
  • grb_timelimit = 30: Set the Gurobi solver's TimeLimit paramter.
  • grb_feasibility_tol = 1.0e-6: Set the Gurobi solver's FeasibilityTol parameter.
  • min_vol_threshold = 0.1: Set the minimum volume threshold in µL. Any transfer must be at least this large.
  • require_nonzero = true: If a target contains a reagent, require that some amount of that reagent is delivered, even if the optimal solution is none.
  • enforce_minimum_shot = false: Enforce the minimum shot volume constraints for each instrument. Caution turns the problem into an MILP.
  • slack_tol = 0.01: Set the tolerance of the slacks in the solution to stay with in a percentage of the optimal value. A value of 0.01 equates to a 1% tolerance.
  • config_costs = ones(length(configs)): Set the relative cost of using each configuration.
  • solution_tolerance = 0.01: Set the limit for the magnitude of any single slack. A value of 1 indicates that the slack can be as large as the largest target in the model.
Pourfecto.quantityMethod
quantity(stock::JLIMS.Stock,ingredient::JLIMS.Ingredient)

Return the quantity of an ingredient in a stock using the preferred units for that ingredient

Pourfecto.random_adverb_verb_pairsFunction
random_adverb_verb_pairs(adverbs, verbs, n; rng=Random.default_rng(), unique=true) -> Vector{String}

Generate n directory-safe random strings of the form adverb_verb by sampling from adverbs and verbs (assumed to be Julia vectors of strings).

Directory-safety rules applied:

  • lowercase
  • spaces and hyphens become _
  • all other non [a-z0-9_] characters removed
  • repeated _ collapsed; leading/trailing _ stripped

If unique=true, results are unique; throws if n exceeds the number of possible unique combinations after sanitization.

Pourfecto.reagent_to_stringMethod
reagent_to_string(chem::JLIMS.Chemical; chem_context::Vector{Module} = [JLIMS], kwargs...) -> String

Convert a JLIMS.Chemical into a stable string identifier.

This function attempts to return the registry key for chem when the chemical is registered in one of the modules listed in chem_context. This is useful because a registered chemical’s display name (JLIMS.name(chem)) is not necessarily the same string that chemparse expects to resolve it.

If chem is not found among the registered chemicals in chem_context, the function falls back to returning JLIMS.name(chem), which is sufficient to reconstruct “ad hoc” (unregistered) chemicals created on the fly.

See also: string_to_reagent

Pourfecto.sanitize_tokensMethod
sanitize_tokens(xs::AbstractVector{<:AbstractString};
                lowercase::Bool=true,
                replace_space_hyphen::Bool=true,
                allow_digits::Bool=true,
                keep_underscores::Bool=true,
                drop_empty::Bool=true,
                de_duplicate::Bool=true,
                sort_items::Bool=false) -> Vector{String}

Sanitize a vector of strings for directory-safe usage.

Rules (in order):

  1. strip
  2. (optional) lowercase
  3. (optional) replace whitespace and - with _
  4. remove disallowed characters (keeps only [a-z], optionally digits and _)
  5. collapse consecutive _
  6. trim leading/trailing _
  7. (optional) drop empty strings
  8. (optional) de-duplicate (stable)

Examples

```julia adverbss = sanitizetokens(adverbs) verbss = sanitizetokens(verbs)

directory-safe pairings

names = randomadverbverbpairs(adverbss, verbs_s, 50)

Pourfecto.slacksMethod
slacks(p::Pourcast)

Return the slack decision variable(s) from a Pourcast solution.

This is an accessor that extracts the solver/model variable stored under the key :slacks from the pourcast's model_solution

Pourfecto.slotting_requirementsMethod
slotting_requirements(pc::Pourcast; threshold::Real = 1e-4) -> Vector{BitMatrix}

Compute per-configuration source→target “slotting” requirements from a Pourcast plan by marking which source labware must be able to transfer to which target labware.

For each configuration c, this function returns a boolean matrix M = slotting_matrices[c] of size (S, T) where:

  • S == length(source_labware(pc)) (number of source labware items)
  • T == length(target_labware(pc)) (number of target labware items)
  • M[s, t] == true if any transfer value associated with transfers from sources[s] to targets[t] in configuration c exceeds threshold. Otherwise M[s, t] == false.

This is useful for determining which source/target pairs must be physically co-slotted/accessible in each configuration.

Keyword Arguments

  • threshold::Real = 1e-4: Minimum transfer magnitude considered “present”. Transfers > threshold trigger a true requirement.

Returns

  • Vector{BitMatrix}: A length-C vector of boolean matrices, one per configuration, where C == length(configs(pc)). Each matrix has shape (S, T).

Notes

  • The comparison is strictly greater than (>) threshold.
  • Requires the following Pourcast-related functions to be defined: transfers_by_config, configs, source_labware, target_labware, transfer_indices.

Examples

```julia reqs = slotting_requirements(pc)

Pourfecto.slottingdict_to_dfMethod
slottingdict_to_df(slotting::SlottingDict) -> DataFrame

Convert a SlottingDict describing labware placement into a DataFrame.

The input dictionary is expected to map each labware object s to a 2-tuple of the form (deck_position, slot), where:

  • deck_position is an object whose human-readable identifier is given by name(deck_position).
  • slot is a String or Integer identifying the slot within that deck position.

Arguments

  • slotting::SlottingDict: Dictionary of labware placement info.

Returns

  • DataFrame: A table with columns Position, Slot, LabwareID, and Name.
Pourfecto.source_labwareMethod
source_labware(p::Pourcast) -> Vector{<:JLIMS.Labware}

Return the source labware associated with p. The labware also provide the source stocks. They are stored separately because not all pourcasts have associated labware.

Pourfecto.source_stocksMethod
source_stocks(p::Pourcast) -> Vector{<:JLIMS.Stock}

Return the source (input) stocks associated with p.

Pourfecto.stock_to_dfFunction
stock_to_df(stocks::Vector{<:JLIMS.Stock}, format::AbstractString = "vc"; kwargs...) -> (DataFrame, DataFrame)

Convert a vector of Stocks into a pair of dataframes (df, units) suitable for serialization.

Two formats are supported:

  • format = "vc": volume/concentration representation (stock_to_vc).
  • format = "q": quantity representation (stock_to_q).

Arguments

  • stocks::Vector{<:JLIMS.Stock}: Stocks to convert.
  • format::AbstractString = "vc": Output encoding; must be one of ["vc", "q"].

See also

df_to_stock

Pourfecto.string_to_reagentMethod
string_to_reagent(str::AbstractString, chem_type::Type{<:Chemical};
                  chem_context::Vector{Module} = [JLIMS], kwargs...) -> Chemical

Convert a string into a reagent (Chemical) instance.

The function first attempts to parse str using JLIMS.chemparse, which may return a registered reagent/chemical object from the provided chem_context. If parsing fails, it emits a warning and falls back to constructing a new chemical of type chem_type using str as the identifier/name and missing for unknown properties.

Arguments

  • str::AbstractString: The reagent identifier to parse (e.g., a registered name, alias, or other parseable representation).
  • chem_type::Type{<:Chemical}: Concrete Chemical subtype to instantiate if str is not registered / cannot be parsed.

Keyword Arguments

  • chem_context::Vector{Module} = [JLIMS]: Modules to search for registered chemicals during parsing (forwarded to chemparse).

Returns

  • A Chemical object. If chemparse succeeds, the parsed/registered object is returned; otherwise, a new chem_type(str, missing, missing, missing) is returned.

Notes

This function is intentionally permissive: unknown reagents do not error, but are treated as chemicals with unspecified properties (missing), which may affect downstream calculations that require those properties.

See also: reagent_to_string

Pourfecto.target_labwareMethod
target_labware(p::Pourcast) -> Vector{<:JLIMS.Labware}

Return the target labware associated with p. The labware also provide the target stocks. They are stored separately because not all pourcasts have associated labware.

Pourfecto.target_stocksMethod
target_stocks(p::Pourcast) -> Vector{<:JLIMS.Stock}

Return the target (output) stocks associated with p.

Pourfecto.transfer_indicesMethod
transfer_indices(pc::Pourcast, source::Labware, target::Labware) -> Tuple{AbstractVector,AbstractVector}

Return the transfer index selectors for a given sourcetarget labware pair.

This function validates that source is present in source_labware(pc) and that target is present in target_labware(pc). If either is missing, it throws an ArgumentError. On success, it retrieves index selectors from labware_indices(pc) and returns the pair associated with the requested source and target.

Arguments

  • pc::Pourcast: A pour/transfer plan containing source/target labware lists and index mappings.
  • source::Labware: Source labware to look up.
  • target::Labware: Target labware to look up.

Returns

  • (src_inds, tgt_inds): A 2-tuple of vectors:

    • src_inds: index selector(s) associated with source
    • tgt_inds: index selector(s) associated with target

    These are intended to be used together for indexing transfer arrays/tensors.

Throws

  • ArgumentError: If source is not in source_labware(pc) or target is not in target_labware(pc).
Pourfecto.transfersMethod
transfers(p::Pourcast)

Return the transfer decision variable(s) from a Pourcast solution.

This is an accessor that extracts the solver/model variable stored under the key :V from the pourcast's model_solution.

See also

flows

Pourfecto.transfers_by_configMethod
transfers_by_config(pc::Pourcast) -> Vector{Matrix{Float64}}

Compute well-to-well transfer volumes per configuration.

This function aggregates the global flow matrix flows(pc) into per-configuration transfer matrices between source wells and target wells.

For each configuration c in configs(pc), it returns an S×T matrix trfs_by_config[c] where:

  • S is the total number of source wells across all source labware.
  • T is the total number of target wells across all target labware.
  • Entry trfs_by_config[c][s, t] equals the sum of all flows (from flows(pc)) along aspiration/dispense node pairs that:
    1. connect source well s to target well t (via Pourfecto.compute_flow_connections), and
    2. belong to configuration c (i.e., both nodes have .configuration == cons[c]).

Arguments

  • pc::Pourcast: A pour/transfer plan providing configurations, labware, well names, and a global flow matrix via flows(pc).

Returns

  • Vector{Matrix{Float64}}: A length-C vector where C == length(configs(pc)). Each element is an S×T dense matrix of aggregated transfer amounts.