libSBOL  2.3.3
Biosystem Design

See Full Code Example for full example code.

Connecting Module Inputs & Outputs

LibSBOL can help synthetic biologists design modular systems of biochemical interactions. Following is an example of such a circuit adapted from Synthesizing AND gate genetic circuits based on CRISPR-Cas9 for identification of bladder cancer cells. This circuit consists of a NAND gate coupled to a NOT gate. The NAND gate turns off in the presence of specific tumor markers and tissue markers, thus its behavior is targeted to specific tissue and tumor sites in the human body. When the NAND gate is shut off, the downstream NOT gate is turned on, thus the coupled effect of the two modules will activate the apoptotic (self-destruct) protein hBAX. Thus, the entire system behaves as a targeted tumor-killer.

CRISPRTumorKiller.png

A representation of this system in SBOL can be programmatically constructed. Starting at the highest level of abstraction, a tumor-killer circuit will be constructed from the NAND and NOT gates. At this stage the modules are black boxes without any internal specifications.

ModuleDefinition& NandGate = *new ModuleDefinition("NandGate");
ModuleDefinition& NotGate = *new ModuleDefinition("NotGate");
ModuleDefinition& TumorKiller = *new ModuleDefinition("TumorKiller");
doc.add < ModuleDefinition > (NandGate);
doc.add < ModuleDefinition > (NotGate);
doc.add < ModuleDefinition > (TumorKiller);
TumorKiller.assemble({ &NotGate, &NandGate });

In order to connect these black boxes, their inputs and outputs must be defined. The following process defines an interface for the modules, while their internal workings still remain abstract. First, configure the NAND gate:

ComponentDefinition& TissueMarker = *new ComponentDefinition("TissueMarker", BIOPAX_PROTEIN);
ComponentDefinition& TumorMarker = *new ComponentDefinition("TumorMarker", BIOPAX_RNA);
ComponentDefinition& LacI = *new ComponentDefinition("LacI", BIOPAX_PROTEIN);
doc.add < ComponentDefinition > ({ &TissueMarker, &TumorMarker, &LacI });
FunctionalComponent& nand_input1 = NandGate.setInput(TissueMarker);
FunctionalComponent& nand_input2 = NandGate.setInput(TumorMarker);
FunctionalComponent& nand_output = NandGate.setOutput(LacI);

Next configure the NOT gate. Note that LacI's ComponentDefinition was already defined above.

ComponentDefinition& HBax = *new ComponentDefinition("HBax", BIOPAX_PROTEIN);
doc.add < ComponentDefinition > (HBax);
FunctionalComponent& not_input = NotGate.setInput(LacI);
FunctionalComponent& not_output = NandGate.setOutput(HBax);

Finally, these two modules can be connected with a metaphorical, yet very satisfying, "click"."

nand_output.connect(not_input);

Mechanistic Modeling of Biochemical Interactions

The preceding example demonstrated how black-box modules may be connected in terms of their inputs and outputs. In order to enable computer-aided engineering (model-based design and simulation of biochemical systems), libSBOL also supports detailed description of biochemical interactions and pathways. The SBOL standard was designed to be closely interoperable with SBML (Systems Biology Markup Language) so that information about structural components can be associated with mathematical descriptions of their behavior. Let's now look at defining modules at a deeper level of scrutiny.

CRISPRTemplate.png

Above is a representation of a template module for CRISPR gene-editing that consists of three biochemical interactions. As described previously (see template_design), template designs may be re-used in many different contexts. For example, a synthetic biologist may want to substitute many different sequences for a guideRNA depending on the actual downstream target. In this example, each component has a specifed functional role while detailed information about its sequence is to be filled in at a later time. Starting at a high level we define the template module and its three member interactions using Systems Biology Ontology (SBO) terms. In the first interaction, Cas9 protein and a guideRNA form a complex. This activated complex targets the promoter cognate to the guideRNA and disables gene expression. The third interaction is expression of the target gene which is normally on by default, but switched off in the presence of activated complex.

// Initialize the template CRISPR module
ModuleDefinition& CRISPRTemplate = *new ModuleDefintion("CRISPRTemplate");
ComponentDefinition& Cas9 = *new ComponentDefinition("Cas9", BIOPAX_PROTEIN);
ComponentDefinition& GuideRNA = *new ComponentDefinition("GuideRNA", BIOPAX_RNA);
ComponentDefinition& Cas9GuideRNAComplex = *new ComponentDefinition("Cas9GuideRNAComplex", BIOPAX_COMPLEX);
ComponentDefinition& TargetPromoter = *new ComponentDefinition("TargetPromoter", BIOPAX_DNA);
ComponentDefinition& TargetGene = *new ComponentDefinition("TargetGene", BIOPAX_DNA);
ComponentDefinition& TargetProtein = *new ComponentDefinition("TargetProtein", BIOPAX_PROTEIN);
doc.add < ModuleDefinition > (CRISPRTemplate);
doc.add < ComponentDefinition > ({ &Cas9, &GuideRNA, &Cas9GuideRNAComplex, &TargetPromoter, &TargetGene, &TargetProtein });
// Define the internal interactions in the module
Interaction& complex_formation = CRISPRTemplate.interactions.create("complex_formation");
Interaction& gene_repression = CRISPRTemplate.interactions.create("gene_repression");
Interaction& gene_production = CRISPRTemplate.interactions.create("gene_production");
complex_formation.types.add(SBO_NONCOVALENT_BINDING);
gene_repression.types.add(SBO_INHIBITION);
gene_production.types.add(SBO_GENETIC_PRODUCTION);

Now let's break the Interactions down into their member species, starting with the reaction for complex formation. Note the use of SBO terms to define the role of each species in the reaction.

// Complex formation A + B -> AB
Participation& A = complex_formation.participations.create("A");
Participation& B = complex_formation.participations.create("B");
Participation& AB = complex_formation.participations.create("AB");
A.roles.add(SBO_REACTANT);
B.roles.add(SBO_REACTANT);
AB.roles.add(SBO_PRODUCT);

The components which participate in an interaction must be assigned:

// Assign a ComponentDefinition to the reactants and products
Cas9.participate(A);
GuideRNA.participate(B);
Cas9GuideRNAComplex.participate(AB);

Similarly, the repression and production interactions are defined:

// Gene repression
Participation& repressor = complex_formation.participations.create("repressor");
Participation& repression_target = complex_formation.participations.create("repression_target");
repressor.roles.add(SBO_INHIBITOR);
repression_target.roles.add(SBO_BINDING_SITE);
// Gene production
Participation& promoter = gene_production.participations.create("promoter");
Participation& gene = gene_production.participations.create("gene");
Participation& gene_product = gene_production.participations.create("gene_product");
promoter.roles.add(SBO_PROMOTER);
gene.roles.add(SBO_GENE);
gene_product.roles.add(SBO_PRODUCT);
Cas9GuideRNAComplex.participate(repressor);
TargetPromoter.participate(repression_target);
TargetPromoter.participate(promoter);
TargetGene.participate(gene);
TargetProtein.participate(gene_product);

Completing a Template Design Using Component Overrides

A complete design can be realized from a template design by overriding components. In the CRISPR template above, the lacI-gRNA and LacI components override the generic guideRNA and gene product. The dashed lines here have a different meaning than they did in the tumor-killer example. There the dashed lines indicate that a module's output component is identical to another module's input component. Here the dashed lines indicate that a template component is being substituted with the LacI protein in this specific design variant. Thus it is possible to make many possible variant designs from a template design. To create a variant from a template design, nest the template module inside the variant module. Then mask the inner module's component with the outer module's component.

ModuleDefinition& CRISPRTemplate = *new ModuleDefinition("CRISPRTemplate");
ModuleDefinition& CRISPRVariant = *new ModuleDefinition("CRISPRVariant");
doc.add<ModuleDefinition>({ &CRISPRTemplate, &CRISPRVariant });
CRISPRVariant.assemble({ &CRISPRTemplate });
FunctionalComponent& template_output = CRISPRTemplate.setOutput(TargetProtein);
FunctionalComponent& variant_output = CRISPRTemplate.setOutput(LacI);
variant_output.mask(template_output);

Full Code Example

#include "sbol.h"
#include <iostream>
#include <vector>
using namespace std;
using namespace sbol;
int main()
{
setHomespace("http://sys-bio.org");
Document& doc = *new Document();
ModuleDefinition& NandGate = *new ModuleDefinition("NandGate");
ModuleDefinition& NotGate = *new ModuleDefinition("NotGate");
ModuleDefinition& TumorKiller = *new ModuleDefinition("TumorKiller");
doc.add < ModuleDefinition > (NandGate);
doc.add < ModuleDefinition > (NotGate);
doc.add < ModuleDefinition > (TumorKiller);
TumorKiller.assemble({ &NotGate, &NandGate });
// Configure the Nand Gate
ComponentDefinition& TissueMarker = *new ComponentDefinition("TissueMarker", BIOPAX_PROTEIN);
ComponentDefinition& TumorMarker = *new ComponentDefinition("TumorMarker", BIOPAX_RNA);
doc.add < ComponentDefinition > ({ &TissueMarker, &TumorMarker, &LacI });
FunctionalComponent& nand_input1 = NandGate.setInput(TissueMarker);
FunctionalComponent& nand_input2 = NandGate.setInput(TumorMarker);
FunctionalComponent& nand_output = NandGate.setOutput(LacI);
// Configure the Not Gate
doc.add < ComponentDefinition > (HBax);
FunctionalComponent& not_input = NotGate.setInput(LacI);
FunctionalComponent& not_output = NandGate.setOutput(HBax);
nand_output.connect(not_input);
// Define internal components in the NAND gate
ComponentDefinition& GuideRNA = *new ComponentDefinition("GuideRNA", BIOPAX_RNA);
ComponentDefinition& Cas9GuideRNAComplex = *new ComponentDefinition("Cas9GuideRNAComplex", BIOPAX_COMPLEX);
ComponentDefinition& TargetPromoter = *new ComponentDefinition("TargetPromoter", BIOPAX_DNA);
ComponentDefinition& TargetGene = *new ComponentDefinition("TargetGene", BIOPAX_DNA);
ComponentDefinition& TargetProtein = *new ComponentDefinition("TargetProtein", BIOPAX_PROTEIN);
doc.add< ComponentDefinition > ({ &Cas9, &GuideRNA, &Cas9GuideRNAComplex, &TargetPromoter, &TargetGene, &TargetProtein });
Interaction& complex_formation = NandGate.interactions.create("complex_formation");
Interaction& gene_repression = NandGate.interactions.create("gene_repression");
Interaction& gene_production = NandGate.interactions.create("gene_production");
complex_formation.types.add(SBO_NONCOVALENT_BINDING);
gene_repression.types.add(SBO_INHIBITION);
gene_production.types.add(SBO_GENETIC_PRODUCTION);
// Complex formation A + B -> AB
Participation& A = complex_formation.participations.create("A");
Participation& B = complex_formation.participations.create("B");
Participation& AB = complex_formation.participations.create("AB");
Participation& repressor = complex_formation.participations.create("repressor");
Participation& repression_target = complex_formation.participations.create("repression_target");
repressor.roles.add(SBO_INHIBITOR);
repression_target.roles.add(SBO_PROMOTER);
// Gene production
Participation& promoter = gene_production.participations.create("promoter");
Participation& gene = gene_production.participations.create("gene");
Participation& gene_product = gene_production.participations.create("gene_product");
gene_product.roles.add(SBO_PRODUCT);
Cas9.participate(A);
GuideRNA.participate(B);
Cas9GuideRNAComplex.participate(AB);
Cas9GuideRNAComplex.participate(repressor);
TargetPromoter.participate(repression_target);
TargetPromoter.participate(promoter);
TargetGene.participate(gene);
TargetProtein.participate(gene_product);
doc.write("module_example.xml");
}