SBOLInventory defines an SBOL 3 profile for describing a laboratory as a facility catalog and recording what happened there. The versioned, language-neutral Profile 0.2 specification is the interoperability contract, and this repository provides its pySBOL3 reference implementation:
a facility contains zones
zones locate assets and material lots
assets expose capabilities
workflows require capabilities
plans bind requirements to qualified assets
runs record material changes and evidence
The first, second, third, and sixth lines are represented directly by the profile. The fourth and fifth belong to a workflow and planning system: workflows stay portable, planning uses catalog facts, and reviewed plans preserve the selected identities. SBOLInventory does not turn an unordered RDF graph into a workflow language or prescribe how a consumer implements its compiler.
| Domain object | SBOL 3 representation | Meaning |
|---|---|---|
Facility |
custom TopLevel |
One governed laboratory site |
Zone |
custom TopLevel |
A room, work area, containment boundary, controlled environment, or storage area |
Asset |
custom TopLevel |
An installed instrument, container, workstation, storage unit, or independently reservable functional unit |
Capability |
owned custom Identified |
An operation one installed asset offers, with parameters, control mode, and qualification |
MaterialLot |
standard sbol:Implementation plus custody properties |
A physical sample or reagent lot whose built reference names its SBOL Component design |
| run | standard prov:Activity |
The assets and input lots used by one execution |
| output and evidence | sbol:Implementation, sbol:ExperimentalData, and sbol:Attachment |
Material derivations and files generated by a run |
The graph deliberately separates five relationships:
facilityidentifies a zone's governing facility. Assets and material lots derive membership through their location, or an asset's physical parent.parent_zonenests spatial or policy boundaries.located_ingives the direct location of an asset or material lot. The target may be a zone or a container asset.part_ofcomposes an asset from child assets. For example, a three-block thermocycler is one parent with three independently bindable block assets.establishes_zoneslinks an environmental asset to the controlled space it creates. An anaerobic chamber can therefore be in a room while a liquid handler is in the chamber interior.
No inverse location edges are serialized. Catalog queries derive containment from these authoritative edges.
A product specification is not an execution guarantee. Qualification belongs to a capability offering on a particular installed asset:
discovered → described → plannable → simulatable → executable → qualified
discovered: the asset or offering is known to exist.described: its relevant public or local metadata is represented.plannable: a planner has a checked parameter and capacity model for it.simulatable: planned behavior can be exercised without touching hardware.executable: a reviewed adapter can invoke the installed asset.qualified: the end-to-end path has been accepted for its intended laboratory use.
Control mode is independent of qualification. Unspecified, manual operation, reviewed file export, a vendor session, an API, SiLA 2, and OPC UA are all representable without implying that the compiler supports them. unspecified is preferable to guessing how a publicly listed instrument is operated.
examples/ebef_catalog.py is a public-data example based on Caltech's Ecology and Biosphere Engineering Facility equipment page. It models the basement microbiology and microscopy areas, the first-floor plant lab, anaerobic chamber interiors, composite ProFlex blocks, environmental chambers, and the publicly listed equipment and capabilities. Each parameter records its facility or manufacturer source; manufacturer specifications require verification against the installed equipment.
The example is intentionally not an operational inventory. It contains no serial numbers, addresses, booking state, calibration records, private layout, or claim that any workflow system can execute an instrument. Publicly supported facts begin at described.
python -m pip install -e '.[dev]'
python examples/ebef_catalog.py ebef.ttlfrom sbol_inventory import (
QUALIFICATION_DESCRIBED,
THERMAL_CYCLING,
find_qualified_assets,
)
matches = find_qualified_assets(
document,
THERMAL_CYCLING,
minimum_qualification=QUALIFICATION_DESCRIBED,
)The result contains three ProFlex block assets rather than one instrument with an integer block_count. That distinction matters because a plan binds work to independently schedulable capacity.
import sbol3
from sbol_inventory import (
CONTROL_VENDOR_SESSION,
INSTRUMENT,
LIQUID_HANDLING,
QUALIFICATION_DESCRIBED,
ROOM,
add_all,
make_asset,
make_capability,
make_document,
make_facility,
make_zone,
write_turtle,
)
ns = "https://example.org/my_lab/"
document = make_document()
facility = make_facility(ns + "facility", name="My laboratory")
room = make_zone(ns + "main_lab", facility=facility, kind=ROOM)
handler = make_asset(
ns + "liquid_handler_1",
kind=INSTRUMENT,
located_in=room,
capabilities=[
make_capability(
LIQUID_HANDLING,
qualification=QUALIFICATION_DESCRIBED,
control_mode=CONTROL_VENDOR_SESSION,
)
],
)
add_all(document, [facility, room, handler])
write_turtle(document, "facility.ttl")pySBOL3 display IDs use alphanumeric characters and underscores. A full identity such as https://example.org/my_lab/liquid_handler_1 is valid; a final path segment containing hyphens is not.
Physical biological and chemical materials remain standard SBOL Implementation objects. Designs may be used or generated as information by a run; physical custody and consumption apply to lots. A MaterialLot points to the corresponding Component through built.
For a transformation, create a new output lot and record its derivation from the input lots. record_run adds a standard PROV activity, records asset and material usages, links outputs and evidence with wasGeneratedBy, and can associate the run with the reviewed Plan and responsible Agent.
examples/material_run.py demonstrates these links with a versioned protocol, material lots, and evidence. Its records and file references are illustrative; the example executes no protocol.
record_material_derivation(output_lot, [input_lot])
run = record_run(
document,
ns + "run_2026_08_26",
assets=[handler],
input_materials=[input_lot],
input_components=[input_design],
output_components=[new_design],
output_materials=[output_lot],
evidence=[experimental_data],
plan=reviewed_plan,
executor=operator_or_control_plane,
)ExperimentalData can describe a Component and share its evidence files with it. Repository objects and links record completed transfers without performing network operations:
from sbol_inventory import (
ExperimentalDataDatabase,
MetadataDatabase,
link_evidence,
record_submission,
record_retrieval,
)
journal = ExperimentalDataDatabase(ns + "journal", name="Field Journal")
synbiohub = MetadataDatabase(ns + "synbiohub", name="SynBioHub")
add_all(document, [journal, synbiohub])
link_evidence(experimental_data, input_design)
record_submission(experimental_data, journal)
record_submission(input_design, synbiohub)
record_retrieval(input_design, synbiohub)The interoperability boundary is the serialized SBOL 3 profile, not the Python package API. A consuming workflow system remains responsible for:
- Checked workflow actions expose capability requirements.
- A native SBOL implementation parses the catalog and enforces the profile invariants.
- An explicit mapping connects compiler capability names to stable capability IRIs.
- Planning checks parameters, capacity, and other constraints before selecting concrete assets.
- A reviewed execution plan preserves its catalog version and requirement-to-asset bindings.
- Runtime dispatch follows the reviewed binding rather than selecting again from a changed catalog.
- Completion records actual asset use, material derivations, and evidence as SBOL/PROV.
find_qualified_assets is a convenience in the Python reference implementation. It returns catalog candidates, not a feasible plan. Constraint solving, scheduling, allocation, plan serialization, and dispatch remain consumer responsibilities.
A consumer written in another language should implement the profile over its native SBOL library and verify RDF interoperability against this implementation. For example, a Rust compiler can extend sbol-rs to preserve and validate the facility terms instead of embedding Python or treating this package as a planning service. Its adoption plan belongs with that compiler and sbol-rs, where the actual IR, validation, and execution boundaries can be designed together.
validate_document runs the procedural facility-profile validator and pySBOL3's core validator on a pySBOL3 Document. It rejects dangling facility, zone, asset, material-design, lineage, and run-usage references; cross-facility containment; location, composition, and lineage cycles; invalid container positions; double occupancy, including inactive occupants; malformed typed parameters or ontology IRIs; unknown qualification/control values; and ambiguous duplicate offerings on one asset. Every violation carries a stable sbolinv-* rule ID.
Profile validation is deliberately two-layered: SHACL Core shapes validate raw graph structure before an object library can normalize it, while validate_inventory_graph checks cross-object and graph invariants. A Full Validator claim also requires SBOL core validation. The implementation-neutral rule catalog, fixture manifest, and conformance protocol keep the pySBOL3 and future sbol-rs implementations aligned by rule ID and RDF graph meaning.
Turtle is the preferred human-readable form. RDF/XML is also supported:
write_turtle(document, "catalog.ttl")
write_rdfxml(document, "catalog.xml")
parsed = read_document("catalog.ttl")Custom-class builders are registered automatically so a round trip restores Facility, Zone, Asset, Capability, and PropertyValue Python types. A parsed standard Implementation is decorated as MaterialLot, but it participates in inventory validation only when it has a materialKind property; unrelated SBOL documents remain valid.
The 0.2 API is a breaking migration from pySBOL2. See MIGRATION.md.
For the profile rationale and implementation boundary, see architecture.md. For normative semantics, use the versioned specification.