Locations & Operations
Location types, LocationKinds, occupancy, and the operations that change where things are and what state they are in. See the Locations and Movement & Occupancy manual chapters.
CHESSCore.location_kinds — Constant
const location_kinds::Dict{Symbol,LocationKind}Registry of every LocationKind defined with @location_kind, keyed by name. Kinds registered in any module are also added here, so this one table covers the whole session. Look up a single kind with @loc_str (loc"WP96") rather than indexing this directly.
CHESSCore.LocationKind — Type
struct LocationKindLocationKind is a named, interned, immutable value describing a kind of location or labware (e.g. :Room, :Incubator, :WP96, :AltemisBox, :Well200). It replaces minting a new concrete Julia type per kind — every Location/Labware/Well instance carries a reference to a shared LocationKind value rather than duplicating its descriptive data.
Fields:
name: the kind's unique identifier.categories: flat, ordered tags used for occupancy-rule resolution (no chaining/hierarchy).shape: grid shape (rows, cols) if this kind isLabware-like;nothingotherwise.socket: theLocationKindname filling each grid slot, ifshapeis set.capacity: well volume capacity if this kind isWell-like;nothingotherwise.vendor/catalog: descriptive product data, if applicable.default_parent_cost/default_child_cost: fallback occupancy costs used when no specific or category-basedoccupancy_costrule applies.actuatable_attributes: names ofAttributeKinds this kind is associated with as an instrument – descriptive only, not enforced; empty for non-instrument kinds.performable_operations: mutating operation functions (e.g.move_into!,transfer!,set_attribute!) this kind's instrument can perform on other locations; empty for non-instrument kinds.readable_types: names ofReadKinds this kind's instrument can produce; empty for non-instrument kinds.is_instrument: whether this kind is capability-bearing (seeis_capable) – independent of its structuralconcretetype, which is decided purely bycapacity/shape. A kind can be both capability-bearing and grid-shaped (e.g. a plate reader with a physical deck), in which case it concretizes asLabwarewith capability data layered on top.
See also: concretetype, @location_kind, @loc_str
CHESSCore.concretetype — Method
concretetype(k::LocationKind)Return the concrete Julia type (Well, Labware, or GenericLocation) that a LocationKind produces: Well if capacity is set, Labware if shape is set, GenericLocation otherwise. Purely structural – whether k is capability-bearing (is_instrument/is_capable) is an orthogonal fact, checkable on any concrete Location, not a 4th type.
CHESSCore.is_capable — Method
is_capable(k::LocationKind)
is_capable(x::Location)Return whether k/x is capability-bearing – i.e. can perform operations on other locations (see performable_operations) and/or produce reads (see readable_types), as opposed to a plain location whose state only ever changes via direct calls, never its own action. Independent of concretetype: a capability-bearing kind can be grid-shaped (Labware) or not (GenericLocation) alike.
CHESSCore.@loc_str — Macro
@loc_str(kind)String macro to recall a LocationKind registered with @location_kind by name, mirroring @spc_str/@org_str (and sharing their lookup machinery).
loc"WP96" is a pure lookup — it never allocates a new physical instance or touches the database. Actual instantiation stays an explicit call, e.g. generate_location(loc"WP96", "Plate 1").
Example:
julia> loc"WP96"
LocationKind(WP96)CHESSCore.@location_kind — Macro
@location_kind labsymb categories shape socket capacity vendor catalog [default_parent_cost] [default_child_cost]Define a new LocationKind and register it under labsymb, both as a const binding (so it can be used directly, e.g. WP96) and in the location_kinds registry (so it can be looked up by name, e.g. for database reconstruction).
Example:
julia> @location_kind WP96 [:Plate] (8,12) :Well200 nothing "Thermo" "123456"
WP96CHESSCore.GenericLocation — Type
mutable struct GenericLocation <: LocationThe concrete Location subtype for locations with mutable, heterogeneous membership over time (rooms, incubators, drawers, ...) — as opposed to Labware (fixed grid) or Well (terminal leaf). Every distinct kind (:Room, :Incubator, ...) is a LocationKind value carried in kind, not a distinct Julia type.
CHESSCore.Location — Type
abstract type Location endLocations represent the physical objects that make up the lab. Location objects use the AbstractTrees.jl interface to construct relational hierarchies.
The concrete subtypes are GenericLocation (mutable membership over time), Labware (fixed, permanent grid membership), and Well (terminal leaf holding a Stock). Every distinct kind of location/labware (:Room, :Incubator, :WP96, ...) is data (a LocationKind), not a distinct Julia type — see @location_kind.
AbstractTrees.children — Method
children(x::Location)The locations directly inside x. For a GenericLocation this is a Vector; for a Labware it is the fixed Matrix of slots; a Well has none.
Base.getindex — Method
getindex(loc::Location,name::String)Look up the unique direct child of loc named name. Throws ChildNotFoundError if no child matches, or AmbiguousChildNameError if more than one does — name is not guaranteed unique (see children_named for the non-throwing, plural alternative). There is deliberately no integer getindex for a generic Location: unlike Labware's fixed grid, children is a mutable Vector with no stable positional identity.
Base.parent — Method
parent(x::Location)The location directly containing x, or nothing if x is at the root of its tree.
CHESSCore.activate! — Method
activate!(x::Location;instrument::Union{Location,Nothing}=nothing)Set the is_active property of Location x to true
See also: is_active. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.actuatable_attributes — Method
actuatable_attributes(x::Location)Return the Set{Symbol} of AttributeKind names x is associated with — descriptive data carried on x's LocationKind, not enforced anywhere (reserved for a possible future planning/scheduling feature). Empty for a non-capable location.
CHESSCore.ancestors — Method
ancestors(x::Location;rev=false)return the parent location chain of location x in the order of most to least proximal.
Use the keyword arg rev=true to reverse the order from least proximal to most proximal
CHESSCore.assert_all_committed — Method
assert_all_committed(args...)Throw UncommittedLocationError if any Location among args is not committed to a database (see is_committed) — an operation that writes to the database needs every location involved to already have a location_id.
CHESSCore.attributes — Method
attributes(x::Location)Access the attributes property of a location.
CHESSCore.catalog — Method
catalog(x::Location)Access the catalog property of a location's LocationKind, if defined.
CHESSCore.children_named — Method
children_named(loc::Location,name::String)Return every direct child of loc whose name matches, as a Vector (possibly empty). name(x) is explicitly documented as non-unique, so unlike Base.getindex(::Location,::String) this never errors — use it when duplicate names are expected and you want to handle them yourself.
CHESSCore.deactivate! — Method
deactivate!(x::Location;instrument::Union{Location,Nothing}=nothing)Set the is_active property of Location x to false
See also: is_active. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.get_all_within — Method
get_all_within(loc::Location,typ::Type{<:Location})Find and return all locations of type typ within loc or loc's children
CHESSCore.is_active — Method
is_active(x::Location)Access the is_active property of a location
See also: activate!, deactivate!, toggle_activity!
CHESSCore.is_capable — Method
is_capable(x::Location)Return whether x is capability-bearing (see is_capable(::LocationKind)) – carried as data on x's LocationKind, independent of x's concrete Location subtype.
CHESSCore.is_committed — Method
is_committed(x::Location)Return true if x has a location_id — it has been committed to a database and is tracked there. Most Locations never need to be: this is the exceptional case, not the default one.
CHESSCore.is_locked — Method
is_locked(x::Location)Access the state of the is_locked property of a location. Locked locations cannot be moved from their current parent, but children of locked locations can be moved.
See also: unlock!,lock!,toggle_lock!.
CHESSCore.kind — Method
kind(x::Location)Access the LocationKind of a location.
CHESSCore.location_id — Method
location_id(x::Location)Access the location_id property of a location. nothing by default – every Location is a normal, complete in-memory object with or without one; a location_id only appears once x has been committed to a database (see is_committed).
CHESSCore.lock! — Method
lock!(x::Location;instrument::Union{Location,Nothing}=nothing)Change the state of the is_locked property of a location to true.
See also: is_locked. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.name — Method
name(x::Location)Access the name property of a location. The name of a location does not need to be unique and can be used for display purposes.
CHESSCore.performable_operations — Method
performable_operations(x::Location)Return the Set{Function} of operation functions (e.g. move_into!, transfer!, set_attribute!) that x can perform on other locations — data carried on x's LocationKind. Empty for a non-capable location.
CHESSCore.readable_types — Method
readable_types(x::Location)Return the Set{Symbol} of ReadKind names that x can produce — data carried on x's LocationKind. Empty for a non-capable location.
CHESSCore.reads — Method
reads(x::Location, kind::ReadKind)
reads(x::Location, name::Symbol)Return x's reads of a single ReadKind (by the kind itself, or by its name), sorted by read_time (reads with no recorded time sort first) – directly usable as a time series, regardless of the underlying collection's insertion order.
CHESSCore.reads — Method
reads(x::Location)Access the full reads collection of a location – every Read ever recorded for it (see record_read!), in whatever order they were recorded. Unlike attributes, this is never overwritten – many independent reads of the same ReadKind can coexist.
CHESSCore.shape — Method
shape(x::Location)Access the grid shape of a location's LocationKind, if defined. (0,0) otherwise.
CHESSCore.toggle_activity! — Method
toggle_activity!(x::Location;instrument::Union{Location,Nothing}=nothing)Switch the is_active property of Location x from its current state.
See also: is_active. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.toggle_lock! — Method
toggle_lock!(x::Location;instrument::Union{Location,Nothing}=nothing)Flip the state of the is_locked property of a location.
See also: is_locked. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.unlock! — Method
unlock!(x::Location;instrument::Union{Location,Nothing}=nothing)Change the state of the is_locked property of a location to false.
See also: is_locked. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.vendor — Method
vendor(x::Location)Access the vendor property of a location's LocationKind, if defined.
CHESSCore.occupancy_rules — Constant
const occupancy_rulesData table of explicit occupancy-cost rules, keyed by (parent_kind_name, child_kind_name). Prefer set_occupancy_cost! over mutating this directly.
CHESSCore.child_cost — Method
child_cost(x::Location) -> RationalThe fallback occupancy cost of x when it is the child: its LocationKind's default_child_cost. See parent_cost.
CHESSCore.occupancy — Method
occupancy(x::Location)return the fractional occupancy of location x.
Occupancies can range from 0 (empty) to 1 (fully occupied).
The occupancy is calculated by summing the occupancy_cost of each child in the location.
CHESSCore.occupancy_cost — Method
occupancy_cost(parent::Location,child::Location)Returns a Rational representing the fractional occupancy of child in parent.
Resolution precedence: an exact (parent kind, child kind) rule (see set_occupancy_cost!) wins outright; otherwise, a single matching category-based rule applies; if more than one category-based rule matches with no exact rule to disambiguate, throws AmbiguousOccupancyRuleError; otherwise falls back to max(parent_cost(parent),child_cost(child)).
CHESSCore.parent_cost — Method
parent_cost(x::Location) -> RationalThe fallback occupancy cost of x when it is the parent: its LocationKind's default_parent_cost. occupancy_cost uses the larger of this and the child's child_cost only when no exact or category-based rule matches the pair.
CHESSCore.set_occupancy_cost! — Method
set_occupancy_cost!(parent_kind::Symbol,child_kind::Symbol,cost::Rational)Register an occupancy-cost rule for a (parent_kind,child_kind) pair (or category pair — any of either kind's LocationKind categories). An exact kind-to-kind rule always takes precedence over a category-based one; if more than one category-based rule matches with no exact rule to break the tie, occupancy_cost throws AmbiguousOccupancyRuleError.
CHESSCore.Labware — Type
mutable struct Labware <: LocationThe concrete Location subtype for a manufactured physical item with a fixed, homogeneous grid of slots (kind.shape), populated once at creation by build_location and never restructured afterward — add_to!/remove! on a Labware throw FixedMembershipError. Every distinct model (:WP96, :AltemisBox, ...) is a LocationKind value carried in kind, not a distinct Julia type.
CHESSCore.childtype — Method
childtype(x::Labware)Return the concrete Location subtype (see concretetype) that fills each of x's grid slots, per x.kind.socket.
CHESSCore.occupancy — Method
occupancy(x::Labware)Always 1//1 – a Labware's children (kind.shape wells) are populated exactly once at construction and can never change (add_to!/remove! throw FixedMembershipError), so there is no "how full" question to answer the way there is for GenericLocation/Instrument. Mirrors occupancy(::Well) (Well.jl), which is fully-occupied for the same reason (fixed contents, no partial-membership state).
CHESSCore.wells — Method
wells(x::Labware)The grid of slots in x, the same Matrix as children. The name reads better when the slots are Wells.
CHESSCore.Well — Type
mutable struct Well <: LocationWells are special Location subtypes that contain a single Stock object.
A well's LocationKind (kind) determines its volume capacity in kind.capacity. kind is a required, explicit constructor argument (not derived from the parent), so a well's capacity is known immediately at construction, whether or not it has a parent yet.
Wells are only allowed to be located in Labware objects and cannot be moved from the labware — they are physically fused to it.
Base.empty! — Method
empty!(x::Well)Remove the stock contained in a well by setting it to Empty()
See also: Empty. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.cost — Method
cost(::Well)Access the cost property of a well
CHESSCore.deposit! — Function
deposit!(recipient::Well, stock::Stock, cost::Real=0)Add stock into recipient's current Stock, guarded by wellcapacity (throws WellCapacityError if the combined stock would exceed it). cost is a plain tracked number (e.g. a reagent cost) added to the well's running cost, apportioned proportionally whenever withdraw! pulls material back out. Defaults to 0 for deposits that don't track a cost.
CHESSCore.drain! — Method
drain!(x::Well)Remove all Species components from the Stock object contained in the well.
drain!leaves behind anyOrganisms *
See also: sterilize!,empty!. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.sterilize! — Method
sterilize!(x::Well)Remove any organisms from the Stock object contained in well x. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location).
CHESSCore.stock — Method
stock(::Well)Access the Stock property of a well
CHESSCore.wellcapacity — Method
wellcapacity(k::LocationKind)Return the capacity of a well LocationKind as a Unitful.Volume quantity, or nothing if k is not a well-shaped kind.
CHESSCore.wellcapacity — Method
wellcapacity(w::Well)Return the capacity of a well as a Unitful.Volume quantity
CHESSCore.withdraw! — Method
withdraw!(donor::Well, quant::Union{Unitful.Volume,Unitful.Mass}) -> (Stock, Real)Remove quant of donor's stock and return it along with its share of the well's cost.
The withdrawn Stock is a proportional slice of the well's contents: withdrawing a quarter of the total quantity takes a quarter of every reagent and organism. The well's cost is reduced by the same fraction, and that amount is returned as the second value. quant must have the same dimension (volume or mass) as the well's total quantity, otherwise an error is thrown.
CHESSCore._attributes_summary — Method
_attributes_summary(io::IO,x::Location)Print x's environment (own attributes overriding inherited ones, via environment) as a table, with an Own/inherited column distinguishing locally-set attributes from inherited ones. Prints nothing if the environment is empty.
CHESSCore._capabilities_summary — Method
_capabilities_summary(io::IO,x::Location)Print x's capability data (performable operations, actuatable attributes, readable types) if is_capable(x) – independent of x's concrete Location subtype. Prints nothing otherwise.
CHESSCore._children_summary — Method
_children_summary(io::IO,x::Location)Print a summary (count, occupancy, breakdown by child kind) of x's children – never enumerates every child. Prints nothing if x has no children.
CHESSCore._present_children — Method
_present_children(x::Location)Return x's children as a plain Vector{Location}, skipping any unassigned slots. GenericLocation stores children in a Vector (always fully assigned); Labware stores them in a Matrix that can contain unassigned slots before every socket is populated.
CHESSCore._reads_summary — Method
_reads_summary(io::IO,x::Location;nshow::Integer=3)Print x's reads grouped by ReadKind, most recent nshow per kind (with a "+N more" tail note if truncated). Prints nothing if x has no reads.
CHESSCore._build_location — Method
_build_location(kind::LocationKind,name::String,next_id::Function,child_namer::Vararg{Function})Recursively build a Location/Labware/Well object graph for kind, obtaining each node's location_id from next_id(name,kind). This is the shared, pure (no database calls) graph-construction logic behind both generate_location (real, DB-backed, in CHESSDatabase) and build_location (no location_id, in-memory only).
CHESSCore.build_location — Function
build_location(kind::LocationKind,name::String=string(UUIDs.uuid4()),child_namer::Vararg{Function}=plate_namer)Build a kind Location entirely in memory — no database calls are made, and location_id is nothing (see is_committed). This is the default, ordinary way to get a Location: a plain in-memory Julia object, nothing exceptional about it. Useful for reasoning about hypothetical arrangements without minting real database rows, and it's the building block CHESSCore's own tests and fixtures use throughout. A location built this way cannot be passed to upload/cache (CHESSDatabase) until it's committed — see UncommittedLocationError. To commit a subtree to a database, see commit_location! (CHESSDatabase); to strip a committed location back down to an uncommitted one (e.g. to merge subtrees from separate databases), see release_location (CHESSDatabase).
Previewing hypothetical arrangements: since nothing here is ever committed unless you choose to, this is already the preview mechanism for anything that doesn't exist yet — build it, mutate it freely (move_into!, drain!, lock!, ...), inspect it, and simply never call upload/cache if you decide not to keep it. There's no "restore" step, because nothing was ever committed. For previewing a change to an already-committed location instead, see reconstruct_location (CHESSDatabase).
See also: generate_location, reconstruct_location, commit_location!, release_location (all CHESSDatabase).
CHESSCore.plate_namer — Method
plate_namer(row,col)Return the microplate standard name for a row and col coordinate
Ex. platenamer(1,1) = "A1" , platenamer(8,12) = "H12"
CHESSCore.plot_well_heatmap! — Function
plot_well_heatmap!(plt, l::Labware; kwargs...)Overlay a heatmap of each well's stock quantity (in µL; Empty wells render as zero) onto plt.
Extension point: the actual implementation lives in CHESSCoreLabwarePlottingExt, loaded automatically once both LabwarePlotting and Plots are also using'd. CHESSCore itself has no hard Plots dependency.
CHESSCore._check_capability — Method
_check_capability(instrument::Union{Location,Nothing}, fun::Function)Shared gate for the four operations whose CHESS Database tables carry an InstrumentID (move_into!/transfer!/set_attribute!/record_read!): if instrument is given, it must have fun in its performable_operations, or this throws ArgumentError. A no-op if instrument is nothing (the default everywhere it's used) – this never mutates instrument and never changes the mutation logic of the operation itself, it only validates.
Checks only the coarse "can this instrument perform this operation at all" axis – actuatable_attributes/readable_types (the finer, per-argument axes) are deliberately not consulted here; they remain descriptive LocationKind data for a possible future planning/scheduling feature.
CHESSCore.add_to! — Method
add_to!(parent::Labware,child::Location)
add_to!(parent::Well,child::Location)Labware slots are fixed at creation (populated by build_location) and Wells never have children — both throw FixedMembershipError rather than allowing move_into! to mutate their (structurally fixed) membership.
CHESSCore.can_move_into — Method
can_move_into(parent::Location,child::Location)returns true if child can be moved into parent, throw an error if not
there are four errors that prevent movement
LockedLocationError:childis locked in its current parent.AlreadyLocatedInError:childis already located inparentOccupancyError:parenthas occupancy constraints that prevent it from containingchild. This could happen ifparentis full or ifchildis never meant to be able to fit insideparent.FixedMembershipError:childis aWell– wells are physically fused to their labware and can never be independently relocated, regardless of any occupancy rule (see thecan_move_into(::Location,child::Well)method below).
See also: move_into!
CHESSCore.can_move_into — Method
can_move_into(::Location,child::Well)A Well is physically fused to its Labware and can never be independently relocated – this always throws FixedMembershipError, regardless of any set_occupancy_cost! rule that might otherwise apply to the (parent kind, child kind) pair. Dispatches ahead of the generic can_move_into(::Location,::Location) for any Well child.
CHESSCore.move_into! — Function
move_into!(parent::Location,child::Location,lock::Bool=false;instrument=nothing)Move child into parent, if allowed.
if lock=true, then also lock the child after the movement. See _check_capability for instrument.
CHESSCore.remove! — Method
remove!(parent::Labware,child::Location)
remove!(parent::Well,child::Location)Mirrors add_to!'s Labware/Well overloads: slots are fixed at creation, so removing a child is just as structurally invalid as adding one – both throw FixedMembershipError.
CHESSCore.transfer! — Function
transfer!(donor::Well,recipient::Well,quantity::Union{Unitful.Volume,Unitful.Mass},configuration::String="";instrument=nothing)Remove quantity from donor and move it to recipient
transfer! mutates the donor and recipient in place. To preview this without mutating either well, see build_location (or CHESSDatabase's reconstruct_location). See _check_capability for instrument.
CHESSCore.set_attribute! — Method
set_attribute!(x::Location,attribute::Attribute; instrument=nothing)Set the value for a location's environmental attributes to attribute.
We use this method to ensure a proper pairing between the attribute type and the attribute in the dict. To preview this without mutating x, see build_location (or CHESSDatabase's reconstruct_location). See _check_capability for instrument.
CHESSCore.record_read! — Method
record_read!(loc::Location, read::Read; instrument=nothing)Append read to loc's reads collection. Does not call invalidate_environment! – reads never participate in environment. See _check_capability for instrument.
CHESSCore.observe! — Method
observe!(well::Well, component::StockComponent, quant; instrument=nothing)
observe!(loc::Location, attribute::Attribute; instrument=nothing)
observe!(child::Location, parent::Union{Location,Nothing}; instrument=nothing)
observe!(loc::Location, facet::Symbol, value; instrument=nothing)Declare one fact about loc as ground truth, whatever history predicted. Unlike the other operations, an observation makes no claim about how the state came about. It only states what the state is.
- Component:
componentis present inwellat exactlyquant, and every other component is unchanged.observe!(well, rgt"glucose", 2u"g"). A zeroquantasserts absence. Cost is unchanged. - Attribute:
loc's own attribute isattribute, as withset_attribute!. Amissingvalue meanslochas no local value and defers to its parent. - Parent:
childis located inparent(or nowhere, fornothing). This moveschildlikemove_into!but ignoreschild's lock. Occupancy rules still apply. - Scalar facets:
facetis one of:cost(aWell's cost),:lockedor:active.
See _check_capability for instrument.
CHESSCore.set_component — Method
set_component(st::Stock, component::StockComponent, quant)Return a copy of st in which component is present at exactly quant, leaving every other component unchanged. A zero quant removes the component. quant must have the dimension the component is tracked in: mass for a Solid, volume for a Liquid, and Biomass for an Organism.
See also: observe!
Base.:- — Method
-(c1::Stock,c2::Stock)Overload the subtraction operator to remove c2 from c1.
If the result contains a chemical or organism with a negative quantity, a MixingError will be thrown saying which one is causing the problem – this includes organism biomass, which (unlike the old presence-only model) can now be fully removed, e.g. c - c == Empty() once biomass cancels exactly.