Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions examples/cloning_workflow.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
"""Compile all cloning stages from explicit synthetic inputs.

The arbitrary sequences, quantities, and parameters exercise the compiler;
this example does not supply validated laboratory methods.
"""

import argparse
from dataclasses import replace
from decimal import Decimal

import lab
from examples.provenance_build import NS
from examples.provenance_build import inputs as assembly_inputs
from lab import uL
from lab.experiments import cloning
from lab.experiments.cloning.methods import (
ExternalPreparationMethod,
PlatingMethod,
Reagent,
TransformationMethod,
)
from lab.experiments.cloning.planning import BuildRequest, CountTarget
from lab.experiments.cloning.systems import (
CloningSystem,
ExternalPreparationRecipe,
PlatingRecipe,
TransformationRecipe,
)
from lab.inventory import CountedStock, Inventory, MaterialForm, Stock
from lab.operations import Hold
from lab.provenance import Component, Document, EvidenceState, Implementation
from lab.provenance.vocabulary import SMALL_MOLECULE
from lab.targets import LiquidHandler, Manual


def inputs(count=2):
_, inputs = assembly_inputs()
doc = Document.from_snapshot(inputs["document"])
vector = doc.get(NS + "/vector", Component)
target = doc.get(NS + "/target", Component)
cells = Component(identity=NS + "/cells", types=("https://example.org/cell",))
strain = Component(identity=NS + "/strain", types=("https://example.org/cell",))
broth = Component(identity=NS + "/broth", types=(SMALL_MOLECULE,))
substrate = Component(identity=NS + "/substrate", types=(SMALL_MOLECULE,))
doc.add(cells, strain, broth, substrate)
stocks = [s for s in inputs["inventory"].stocks if s.design != vector.ref]
for design, form in ((cells, MaterialForm.COMPETENT_CELLS), (broth, MaterialForm.REAGENT)):
material = Implementation(
identity=design.identity + "/material",
derived_from=(design.ref,),
evidence_state=EvidenceState.RECORDED,
)
doc.add(material)
stocks.append(
Stock(
identity=design.identity + "/stock",
design=design.ref,
implementation=material.ref,
form=form,
quantity=20 * uL,
)
)
material = Implementation(
identity=NS + "/received_stab",
derived_from=(vector.ref,),
evidence_state=EvidenceState.RECORDED,
)
doc.add(material)
stocks.append(
CountedStock(
identity=NS + "/stab_stock",
design=vector.ref,
implementation=material.ref,
form=MaterialForm.BACTERIAL_STAB,
count=1,
)
)
preparation = ExternalPreparationMethod(
identity=NS + "/preparation_method",
procedure=NS + "/sop",
instructions=(
"Apply the supplied test procedure and record the resulting material and quantity."
),
source_count=1,
output_volume_ul=Decimal(10),
)
transformation = TransformationMethod(
identity=NS + "/transformation_method",
cell_volume_ul=Decimal(2),
dna_volume_ul=Decimal(1),
recovery=Reagent(component=broth.ref, volume_ul=Decimal(2)),
profile=(Hold(Decimal(25), Decimal(1)),),
recovery_profile=(Hold(Decimal(25), Decimal(1)),),
output_volume_ul=Decimal(5),
cell_mix_volume_ul=Decimal(1),
cell_mix_cycles=1,
dna_mix_cycles=1,
initial_celsius=Decimal(25),
)
plating = PlatingMethod(
identity=NS + "/plating_method",
substrate=substrate.ref,
diluent=broth.ref,
transfer_volume_ul=Decimal(1),
dilution_factors=(Decimal(2), Decimal(3)),
spot_volume_ul=Decimal(1),
spot_height_mm=Decimal(2),
mix_volume_ul=Decimal(1),
mix_cycles=1,
)
system = CloningSystem(
identity=NS + "/complete_system",
recipes=(
*inputs["system"].recipes,
ExternalPreparationRecipe(
identity=NS + "/preparation_recipe",
product=vector.ref,
source=vector.ref,
source_form=MaterialForm.BACTERIAL_STAB,
output_form=MaterialForm.DNA,
method=preparation.identity,
),
TransformationRecipe(
identity=NS + "/transformation_recipe",
product=strain.ref,
chassis=cells.ref,
plasmids=(target.ref,),
method=transformation.identity,
),
PlatingRecipe(
identity=NS + "/plating_recipe", product=strain.ref, method=plating.identity
),
),
)
return BuildRequest(
identity=NS + "/integrated_request", targets=(CountTarget(design=strain.ref, count=count),)
), {
**inputs,
"document": doc.freeze(),
"inventory": Inventory(identity=NS + "/integrated_inventory", stocks=tuple(stocks)),
"system": system,
"methods": replace(
inputs["methods"],
transformations=(transformation,),
platings=(plating,),
preparations=(preparation,),
),
}


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--target", choices=("manual", "ot2", "flex", "star"), default="manual")
parser.add_argument("--out")
args = parser.parse_args()
request, supplied = inputs()
plan = cloning.plan(request, **supplied)
plan.require_ready()
experiment = cloning.build(plan)
target = Manual() if args.target == "manual" else LiquidHandler(args.target)
compilation = lab.compile(experiment, target)
print(compilation.write(args.out or f"build/cloning/{args.target}"))
for stage in compilation.stages:
print(f"{stage.protocol.name}: {stage.target.name}")


if __name__ == "__main__":
main()
120 changes: 120 additions & 0 deletions examples/provenance_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""Compile a synthetic software fixture from SBOL designs through robot artifacts.

The short DNA strings and arbitrary method numbers demonstrate the compiler.
They are not a laboratory cloning recipe.
"""

import argparse
from decimal import Decimal

import lab
from lab import uL
from lab.experiments import cloning
from lab.experiments.cloning.methods import AssemblyMethod, CloningMethods, Reagent
from lab.experiments.cloning.planning import BuildRequest, BuildTarget
from lab.experiments.cloning.sequences import CIRCULAR, LINEAR
from lab.experiments.cloning.systems import AssemblyRecipe, CloningSystem, FragmentSelection
from lab.inventory import Inventory, MaterialForm, Stock
from lab.operations import Hold
from lab.provenance import Component, Document, EvidenceState, Implementation, Sequence
from lab.provenance.vocabulary import DNA, IUPAC_DNA, SMALL_MOLECULE
from lab.targets import LiquidHandler, Manual

NS = "https://example.org/lab_demo"


def inputs():
volume, stock_volume = "3", "10"
doc = Document(namespace=NS)
vector_sequence = Sequence(
identity=doc.iri("vector_sequence"), elements="AAAAGAATTCTTTT", encoding=IUPAC_DNA
)

insert_sequence = Sequence(
identity=doc.iri("insert_sequence"), elements="GGGAATTCCCCGAATTCGG", encoding=IUPAC_DNA
)
vector = Component(
identity=doc.iri("vector"), types=(DNA, CIRCULAR), sequences=(vector_sequence.ref,)
)
insert = Component(
identity=doc.iri("insert"), types=(DNA, LINEAR), sequences=(insert_sequence.ref,)
)
target = Component(identity=doc.iri("target"), types=(DNA, CIRCULAR))
buffer = Component(identity=doc.iri("buffer"), types=(SMALL_MOLECULE,))
water = Component(identity=doc.iri("water"), types=(SMALL_MOLECULE,))
doc.add(vector_sequence, insert_sequence, vector, insert, target, buffer, water)
stocks = []
for design, form in (
(vector, MaterialForm.DNA),
(insert, MaterialForm.DNA),
(buffer, MaterialForm.REAGENT),
(water, MaterialForm.REAGENT),
):
implementation = Implementation(
identity=design.identity + "/implementation",
derived_from=(design.ref,),
evidence_state=EvidenceState.RECORDED,
)
doc.add(implementation)
stocks.append(
Stock(
identity=design.identity + "/stock",
design=design.ref,
implementation=implementation.ref,
form=form,
quantity=Decimal(stock_volume) * uL,
)
)
recipe = AssemblyRecipe(
identity=doc.iri("recipe"),
product=target.ref,
enzyme="EcoRI",
fragments=(
FragmentSelection(component=vector.ref, left_cut=5, right_cut=5),
FragmentSelection(component=insert.ref, left_cut=3, right_cut=12),
),
method=doc.iri("method"),
)
method = AssemblyMethod(
identity=recipe.method,
enzyme="EcoRI",
dna_volume_ul=Decimal(1),
reaction_volume_ul=Decimal(5),
output_volume_ul=Decimal(5),
reagents=(Reagent(component=buffer.ref, volume_ul=Decimal(1)),),
diluent=water.ref,
profile=(Hold(Decimal(25), Decimal(1)),),
cycles=1,
mix_volume_ul=Decimal(2),
mix_cycles=1,
)
return (
BuildRequest(
identity=doc.iri("request"),
targets=(BuildTarget(design=target.ref, volume_ul=Decimal(volume)),),
),
dict(
document=doc.freeze(),
inventory=Inventory(identity=doc.iri("inventory"), stocks=tuple(stocks)),
system=CloningSystem(identity=doc.iri("system"), recipes=(recipe,)),
methods=CloningMethods(assemblies=(method,)),
),
)


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--target", choices=("manual", "ot2", "flex", "star"), default="manual")
parser.add_argument("--out", default=None)
args = parser.parse_args()
request, supplied = inputs()
planned = cloning.plan(request, **supplied)
planned.require_ready()
experiment = cloning.build(planned)
target = Manual() if args.target == "manual" else LiquidHandler(args.target)
compilation = lab.compile(experiment, target)
print(compilation.write(args.out or f"build/provenance/{args.target}"))


if __name__ == "__main__":
main()
2 changes: 2 additions & 0 deletions src/lab/experiments/cloning/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
containers and sites; compilation lowers it for one handler.
"""

from lab.experiments.cloning.build import build
from lab.experiments.cloning.decks import assembly_deck, plating_deck, transformation_deck
from lab.experiments.cloning.methods import (
AssemblyMethod,
Expand Down Expand Up @@ -74,6 +75,7 @@
"PlatingMethod",
"PlatingRecipe",
"Reagent",
"build",
"plan",
"AssemblyLayout",
"AssemblyReaction",
Expand Down
Loading
Loading