diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..6a137d7 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright 2022 Smart Information Flow Technologies and Raytheon BBN + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/doc/generate_specification_content.py b/doc/generate_specification_content.py new file mode 100644 index 0000000..dc3cd57 --- /dev/null +++ b/doc/generate_specification_content.py @@ -0,0 +1,36 @@ +import glob + +from sbol_factory import UMLFactory +import os +from shutil import copy +from pathlib import Path + +print('Warning: this script is fragile and assumes that PAML and PAML-specification are sibling directories on Mac or Unix.') + +print('Loading UML') +uml_module = UMLFactory(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../uml/uml.ttl'), 'http://bioprotocols.org/uml#') +print('Generating UML specification materials') +uml_module.generate('uml_classes') + +print('Moving UML to specification folder') +copy('umlDataModel.tex', '../PAML-specification/umlDataModel.tex') +os.remove('umlDataModel.tex') +Path('../PAML-specification/uml_classes/').mkdir(parents=True, exist_ok=True) +for file in glob.glob('uml_classes/*'): + copy(file, '../PAML-specification/uml_classes/') + os.remove(file) + +print('Loading PAML') +paml_module = UMLFactory(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../paml/paml.ttl'), + 'http://bioprotocols.org/paml#') +print('Generating PAML specification materials') +paml_module.generate('paml_classes') + +print('Moving PAML to specification folder') +copy('pamlDataModel.tex', '../PAML-specification/pamlDataModel.tex') +os.remove('pamlDataModel.tex') +Path('../PAML-specification/paml_classes/').mkdir(parents=True, exist_ok=True) +for file in glob.glob('paml_classes/*'): + copy(file, '../PAML-specification/paml_classes/') + os.remove(file) +print('Update complete') diff --git a/examples/LUDOX_protocol.py b/examples/LUDOX_protocol.py new file mode 100644 index 0000000..f0d3229 --- /dev/null +++ b/examples/LUDOX_protocol.py @@ -0,0 +1,173 @@ +import json +import logging +import os +from typing import Tuple + +import rdflib as rdfl +import sbol3 +import tyto +from sbol3 import Document + +import paml + +logger: logging.Logger = logging.Logger("LUDOX_protocol") + +CONT_NS = rdfl.Namespace('https://sift.net/container-ontology/container-ontology#') +OM_NS = rdfl.Namespace('http://www.ontology-of-units-of-measure.org/resource/om-2/') + + +def prepare_document() -> Document: + logger.info('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + return doc + + +def import_paml_libraries() -> None: + logger.info('Importing libraries') + paml.import_library('liquid_handling') + logger.info('... Imported liquid handling') + paml.import_library('plate_handling') + logger.info('... Imported plate handling') + paml.import_library('spectrophotometry') + logger.info('... Imported spectrophotometry') + paml.import_library('sample_arrays') + logger.info('... Imported sample arrays') + + +DOCSTRING = \ + ''' +With this protocol you will use LUDOX CL-X (a 45% colloidal silica suspension) as a single point reference to +obtain a conversion factor to transform absorbance (OD600) data from your plate reader into a comparable +OD600 measurement as would be obtained in a spectrophotometer. This conversion is necessary because plate +reader measurements of absorbance are volume dependent; the depth of the fluid in the well defines the path +length of the light passing through the sample, which can vary slightly from well to well. In a standard +spectrophotometer, the path length is fixed and is defined by the width of the cuvette, which is constant. +Therefore this conversion calculation can transform OD600 measurements from a plate reader (i.e. absorbance +at 600 nm, the basic output of most instruments) into comparable OD600 measurements. The LUDOX solution +is only weakly scattering and so will give a low absorbance value. + ''' + + +def create_protocol() -> paml.Protocol: + logger.info('Creating protocol') + protocol: paml.Protocol = paml.Protocol('iGEM_LUDOX_OD_calibration_2018') + protocol.name = "iGEM 2018 LUDOX OD calibration protocol" + protocol.description = DOCSTRING + return protocol + + +def create_h2o() -> sbol3.Component: + ddh2o = sbol3.Component('ddH2O', 'https://identifiers.org/pubchem.substance:24901740') + ddh2o.name = 'Water, sterile-filtered, BioReagent, suitable for cell culture' # TODO get via tyto + return ddh2o + + +def create_ludox() -> sbol3.Component: + ludox = sbol3.Component('LUDOX', 'https://identifiers.org/pubchem.substance:24866361') + ludox.name = 'LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O' + return ludox + + +PLATE_SPECIFICATION = \ + """cont:ClearPlate and + cont:SLAS-4-2004 and + (cont:wellVolume some + ((om:hasUnit value om:microlitre) and + (om:hasNumericalValue only xsd:decimal[>= "200"^^xsd:decimal])))""" + +PREFIX_MAP = json.dumps({"cont": CONT_NS, "om": OM_NS}) + + +def create_plate(protocol: paml.Protocol): + # graph: rdfl.Graph = protocol._other_rdf + # plate_spec_uri = \ + # "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/container_requirement#RequiredPlate" + # graph.add((plate_spec_uri, CONT_NS.containerOntologyQuery, PLATE_SPECIFICATION)) + # plate_spec = sbol3.Identified(plate_spec_uri, + # "foo", name="RequiredPlate") + spec = paml.ContainerSpec(queryString=PLATE_SPECIFICATION, prefixMap=PREFIX_MAP, name='plateRequirement') + plate = protocol.primitive_step('EmptyContainer', + specification=spec) + plate.name = 'calibration plate' + return plate + + +def provision_h2o(protocol: paml.Protocol, plate, ddh2o) -> None: + c_ddh2o = protocol.primitive_step('PlateCoordinates', source=plate.output_pin('samples'), coordinates='A1:D1') + protocol.primitive_step('Provision', resource=ddh2o, destination=c_ddh2o.output_pin('samples'), + amount=sbol3.Measure(100, tyto.OM.microliter)) + + +def provision_ludox(protocol: paml.Protocol, plate, ludox) -> None: + c_ludox = protocol.primitive_step('PlateCoordinates', source=plate.output_pin('samples'), coordinates='A2:D2') + protocol.primitive_step('Provision', resource=ludox, destination=c_ludox.output_pin('samples'), + amount=sbol3.Measure(100, tyto.OM.microliter)) + + +def measure_absorbance(protocol: paml.Protocol, plate, wavelength_param): + c_measure = protocol.primitive_step('PlateCoordinates', source=plate.output_pin('samples'), coordinates='A1:D2') + return protocol.primitive_step( + 'MeasureAbsorbance', + samples=c_measure.output_pin('samples'), + wavelength=wavelength_param, + ) + + +def ludox_protocol() -> Tuple[paml.Protocol, Document]: + ############################################# + # set up the document + doc: Document = prepare_document() + + ############################################# + # Import the primitive libraries + import_paml_libraries() + + ############################################# + # Create the protocol + protocol: paml.Protocol = create_protocol() + doc.add(protocol) + + # create the materials to be provisioned + ddh2o = create_h2o() + doc.add(ddh2o) + + ludox = create_ludox() + doc.add(ludox) + + # add an optional parameter for specifying the wavelength + wavelength_param = protocol.input_value('wavelength', sbol3.OM_MEASURE, optional=True, + default_value=sbol3.Measure(600, tyto.OM.nanometer)) + + # actual steps of the protocol + # get a plate + plate = create_plate(protocol) + + # put ludox and water in selected wells + provision_h2o(protocol, plate, ddh2o) + provision_ludox(protocol, plate, ludox) + + # measure the absorbance + measure = measure_absorbance(protocol, plate, wavelength_param) + + output = protocol.designate_output('absorbance', sbol3.OM_MEASURE, + measure.output_pin('measurements')) + protocol.order(protocol.get_last_step(), output) + return protocol, doc + + +if __name__ == '__main__': + new_protocol: paml.Protocol + new_protocol, doc = ludox_protocol() + print('Validating and writing protocol') + v = doc.validate() + assert len(v) == 0, "".join(f'\n {e}' for e in v) + + rdf_filename = os.path.join(os.path.dirname(__file__), 'iGEM 2018 LUDOX OD calibration protocol.nt') + doc.write(rdf_filename, sbol3.SORTED_NTRIPLES) + print(f'Wrote file as {rdf_filename}') + + # render and view the dot + dot = new_protocol.to_dot() + dot.render(f'{new_protocol.name}.gv') + dot.view() diff --git a/examples/OT2TestGen.py b/examples/OT2TestGen.py new file mode 100644 index 0000000..d3c72ff --- /dev/null +++ b/examples/OT2TestGen.py @@ -0,0 +1,131 @@ +import os +import tempfile +import sbol3 +import paml +import tyto +import uml +import json +import rdflib as rdfl +from typing import Dict + +from paml.execution_engine import ExecutionEngine +from paml_check.paml_check import check_doc +from paml_convert.ot2.ot2_specialization import OT2Specialization + +# Dev Note: This is a test of the initial version of the OT2 specialization. Any specs shown here can be changed in the future. Use at your own risk. Here be dragons. + + +############################################# +# set up the document +print('Setting up document') +doc = sbol3.Document() +sbol3.set_namespace('https://bbn.com/scratch/') + +############################################# +# Import the primitive libraries +print('Importing libraries') +paml.import_library('liquid_handling') +print('... Imported liquid handling') +paml.import_library('plate_handling') +print('... Imported plate handling') +paml.import_library('spectrophotometry') +print('... Imported spectrophotometry') +paml.import_library('sample_arrays') +print('... Imported sample arrays') +paml.import_library('wait') +print('... Imported wait') + + +# Example of how to generate a template for a new protocol step + +#print(primitives["https://bioprotocols.org/paml/primitives/liquid_handling/Dispense"].template()) + +protocol = paml.Protocol('iGEM_LUDOX_OD_calibration_2018') +protocol.name = "iGEM 2018 LUDOX OD calibration protocol" +protocol.description = ''' +With this protocol you will use LUDOX CL-X (a 45% colloidal silica suspension) as a single point reference to +obtain a conversion factor to transform absorbance (OD600) data from your plate reader into a comparable +OD600 measurement as would be obtained in a spectrophotometer. This conversion is necessary because plate +reader measurements of absorbance are volume dependent; the depth of the fluid in the well defines the path +length of the light passing through the sample, which can vary slightly from well to well. In a standard +spectrophotometer, the path length is fixed and is defined by the width of the cuvette, which is constant. +Therefore this conversion calculation can transform OD600 measurements from a plate reader (i.e. absorbance +at 600 nm, the basic output of most instruments) into comparable OD600 measurements. The LUDOX solution +is only weakly scattering and so will give a low absorbance value. +''' +doc.add(protocol) + +# create the materials to be provisioned +CONT_NS = rdfl.Namespace('https://sift.net/container-ontology/container-ontology#') +OM_NS = rdfl.Namespace('http://www.ontology-of-units-of-measure.org/resource/om-2/') + +PREFIX_MAP = json.dumps({"cont": CONT_NS, "om": OM_NS}) + + +ddh2o = sbol3.Component('ddH2O', 'https://identifiers.org/pubchem.substance:24901740') +ddh2o.name = 'Water, sterile-filtered, BioReagent, suitable for cell culture' +ddh2o.OT2SpecificProps = sbol3.TextProperty(ddh2o,'',0,1) +#indicate where ddh2o is loaded, use JSON to set OT2 Specific parameters; might be cleaner and more pythonesque using a dictionary but this should do for now +#water is in well A1 of reservoir and declaring that the left pipette should be used when pipetting from this reservoir +#since no coordinates were issued its assumed to be loaded into well A1 +ddh2o.OT2SpecificProps = '{"deck":"1", "source":"reservoir", "type":"nest_12_reservoir_15ml"}' +doc.add(ddh2o) + +ludox = sbol3.Component('LUDOX', 'https://identifiers.org/pubchem.substance:24866361') +ludox.name = 'LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O' +ludox.OT2SpecificProps = sbol3.TextProperty(ludox,'',0,1) +#indicate where ludox is loaded, use JSON to set OT2 Specific parameters; might be cleaner and more pythonesque using a dictionary but this should do for now +#ludox is in well A2 of reservoir and declaring that the right pipette should be used when pipetting from this reservoir +#no need to redeclare source type as long as it was declared before +ludox.OT2SpecificProps = '{"coordinates":"A2", "source":"reservoir", "pipette":"right"}' +doc.add(ludox) + + +# actual steps of the protocol +# get a plate +platespec1 = paml.ContainerSpec(queryString="corning_48_wellplate_1.6ml_flat", prefixMap=PREFIX_MAP, name='plate1') +platespec1.OT2SpecificProps = sbol3.TextProperty(platespec1,"https://bioprotocols.org/paml/primitives/sample_arrays/EmptyContainer/OT2/Deck",0,1) +platespec1.OT2SpecificProps = '{"deck":"2"}' +plate1 = protocol.primitive_step('EmptyContainer', specification=platespec1) # declare a plate loaded in the second deck + +platespec2 = paml.ContainerSpec(queryString="corning_48_wellplate_1.6ml_flat", prefixMap=PREFIX_MAP, name='plate2') +platespec2.OT2SpecificProps = sbol3.TextProperty(platespec2,"https://bioprotocols.org/paml/primitives/sample_arrays/EmptyContainer/OT2/Deck",0,1) +platespec2.OT2SpecificProps = '{"deck":"3"}' +plate2 = protocol.primitive_step('EmptyContainer', specification=platespec2) # declare a plate loaded in the third deck + +# identify wells to use +c_ddh2o = protocol.primitive_step('PlateCoordinates', source=plate1.output_pin('samples'), coordinates="plate1['A1:D1']") +# put water in selected wells +provision_ddh2o = protocol.primitive_step('Provision', resource=ddh2o, destination=c_ddh2o.output_pin('samples'),amount=sbol3.Measure(80, tyto.OM.microliter)) +#identify wells to use +c_ludox = protocol.primitive_step('PlateCoordinates', source=plate1.output_pin('samples'), coordinates="plate1['A2:D2']") +# put ludox in selected wells +provision_ludox = protocol.primitive_step('Provision', resource=ludox, destination=c_ludox.output_pin('samples'),amount=sbol3.Measure(90, tyto.OM.microliter)) +protocol.primitive_step('WaitForTime', amount=sbol3.Measure(25, tyto.OM.second)) #Experimental: Errors out if you stick it in some places +# identify wells to use +c_ddh2o2 = protocol.primitive_step('PlateCoordinates', source=plate2.output_pin('samples'), coordinates="plate2['A1:D1']") +# put water in selected wells +provision_ddh2o2 = protocol.primitive_step('Provision', resource=ddh2o, destination=c_ddh2o2.output_pin('samples'),amount=sbol3.Measure(100, tyto.OM.microliter)) +#identify wells to use +c_ludox2 = protocol.primitive_step('PlateCoordinates', source=plate2.output_pin('samples'), coordinates="plate2['A2:D2']") +# put ludox in selected wells +provision_ludox2 = protocol.primitive_step('Provision', resource=ludox, destination=c_ludox2.output_pin('samples'),amount=sbol3.Measure(110, tyto.OM.microliter)) + + + + + +leftTiprackSettingJSON = '{"pipette":"p1000_single_gen2","tipracks":[{"id":"geb_96_tiprack_1000ul","deck":4},{"id":"geb_96_tiprack_1000ul","deck":5}]}' +rightTiprackSettingJSON = '{"pipette":"p20_single_gen2","tipracks":[{"id":"opentrons_96_tiprack_20ul","deck":6},{"id":"opentrons_96_tiprack_20ul","deck":7}]}' + + +filename="ludox_ot2.py" +agent = sbol3.Agent("test_agent") +ee = ExecutionEngine(specializations=[OT2Specialization("2.11",leftTiprackSettingJSON,rightTiprackSettingJSON)]) +parameter_values = [] +execution = ee.execute(protocol, agent, id="test_execution") +with open(filename, 'w') as f: + print(ee.specializations[0].script,file=f) +print(f"All done. Script dumped to {filename}.") + + diff --git a/examples/golden_gate_assembly.nt b/examples/golden_gate_assembly.nt new file mode 100644 index 0000000..b7e846c --- /dev/null +++ b/examples/golden_gate_assembly.nt @@ -0,0 +1,1167 @@ + + . + "ActivityParameterNode1" . + . + . + . + "ActivityParameterNode2" . + . + . + . + "ActivityParameterNode3" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "source" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "location" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "shakingFrequency" . + . + . + "Measure1" . + "5.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "duration" . + . + . + "Measure1" . + "60.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "temperature" . + . + . + . + . + . + . + . + "CallBehaviorAction10" . + . + . + . + . + . + "CallBehaviorAction1" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "source" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "destination" . + . + . + "true"^^ . + "true"^^ . + "InputPin3" . + "plan" . + . + . + "true"^^ . + "true"^^ . + "InputPin4" . + "dispenseVelocity" . + . + . + . + . + . + . + . + "CallBehaviorAction2" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "destination" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "dispenseVelocity" . + . + . + . + "LiteralReference1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "resource" . + . + . + "Measure1" . + "2.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "amount" . + . + . + . + . + . + . + . + "CallBehaviorAction3" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "destination" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "dispenseVelocity" . + . + . + . + "LiteralReference1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "resource" . + . + . + "Measure1" . + "1.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "amount" . + . + . + . + . + . + . + . + "CallBehaviorAction4" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "destination" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "dispenseVelocity" . + . + . + . + "LiteralReference1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "resource" . + . + . + "Measure1" . + "15.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "amount" . + . + . + . + . + . + . + . + "CallBehaviorAction5" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "location" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "type" . + . + . + . + . + . + "CallBehaviorAction6" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "location" . + . + . + "Measure1" . + "3.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "duration" . + . + . + "Measure1" . + "300.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "acceleration" . + . + . + . + . + . + . + "CallBehaviorAction7" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "location" . + . + . + . + . + "CallBehaviorAction8" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "location" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "shakingFrequency" . + . + . + "Measure1" . + "60.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "duration" . + . + . + "Measure1" . + "37.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "temperature" . + . + . + . + . + . + . + . + "CallBehaviorAction9" . + . + . + . + . + "ControlFlow10" . + . + . + . + . + "ControlFlow11" . + . + . + . + . + "ControlFlow1" . + . + . + . + . + "ControlFlow2" . + . + . + . + . + "ControlFlow3" . + . + . + . + . + "ControlFlow4" . + . + . + . + . + "ControlFlow5" . + . + . + . + . + "ControlFlow6" . + . + . + . + . + "ControlFlow7" . + . + . + . + . + "ControlFlow8" . + . + . + . + . + "ControlFlow9" . + . + . + "ForkNode1" . + . + . + "ForkNode2" . + . + . + "InitialNode1" . + . + . + . + . + "ObjectFlow10" . + . + . + . + . + "ObjectFlow11" . + . + . + . + . + "ObjectFlow12" . + . + . + . + . + "ObjectFlow13" . + . + . + . + . + "ObjectFlow14" . + . + . + . + . + "ObjectFlow1" . + . + . + . + . + "ObjectFlow2" . + . + . + . + . + "ObjectFlow3" . + . + . + . + . + "ObjectFlow4" . + . + . + . + . + "ObjectFlow5" . + . + . + . + . + "ObjectFlow6" . + . + . + . + . + "ObjectFlow7" . + . + . + . + . + "ObjectFlow8" . + . + . + . + . + "ObjectFlow9" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "source_samples" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "build_layout" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "constructs" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "\nThis protocol is for Golden Gate Assembly of pairs of DNA fragments into plasmids using the New England Biolabs\nGolden Gate Assembly Kit (BsaI-HFv2), product ID NEB #E1601.\nProtocol implements the specific case of two part assembly for the NEB-provided protocol: \nhttps://www.neb.com/protocols/2018/10/02/golden-gate-assembly-protocol-for-using-neb-golden-gate-assembly-mix-e1601\n" . + "GoldenGate_assembly" . + . + "Golden Gate Assembly" . + . + . + "NEB_GoldenGate_AssemblyMix" . + . + "NEB Golden Gate Assembly Mix" . + . + . + . + "NEB_GoldenGate_Buffer" . + . + "NEB T4 DNA Ligase Buffer (10X)" . + . + . + . + "nuclease_free_H2O" . + . + "Nuclease-free Water" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "resource" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "destination" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "amount" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "dispenseVelocity" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + . + . + . + . + "Place a measured amount (mass or volume) of a specified component into a location, where it may then be used in executing the protocol." . + "Provision" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "source" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "destination" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "plan" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "dispenseVelocity" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + . + . + . + . + "Move volumes from a collection of source samples to a collection of destination samples following a plan of value given for each location" . + "TransferByMap" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "location" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "duration" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "temperature" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "shakingFrequency" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + . + . + . + . + "Incubate a set of samples under specified conditions for a fixed period of time" . + "Incubate" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "location" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "type" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + . + . + "Seal a collection of samples fixing the seal using a user-selected method, in order to guarantee isolation from the external environment" . + "Seal" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "location" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "duration" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "acceleration" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + . + . + . + "Centrifuge a set of samples at a given acceleration for a given period of time" . + "Spin" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "location" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + . + "Unseal a sealed collection of samples to break their isolation from the external environment" . + "Unseal" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "source" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + . + . + "Create a new sample collection with identical parameters to the input collection" . + "DuplicateCollection" . + . + . + . diff --git a/examples/golden_gate_assembly.py b/examples/golden_gate_assembly.py new file mode 100644 index 0000000..7d80080 --- /dev/null +++ b/examples/golden_gate_assembly.py @@ -0,0 +1,111 @@ +import os +import tempfile +import unittest +import filecmp +import sbol3 +import paml +import tyto + + +# import paml_md +import uml + +############################################# +# set up the document +print('Setting up document') +doc = sbol3.Document() +sbol3.set_namespace('https://bbn.com/scratch/') + +############################################# +# Import the primitive libraries +print('Importing libraries') +paml.import_library('liquid_handling') +print('... Imported liquid handling') +paml.import_library('plate_handling') +print('... Imported plate handling') +paml.import_library('spectrophotometry') +print('... Imported spectrophotometry') +paml.import_library('sample_arrays') +print('... Imported sample arrays') + +############################################# +# Create the protocol +print('Creating protocol') +protocol = paml.Protocol('GoldenGate_assembly') +protocol.name = "Golden Gate Assembly" +protocol.description = ''' +This protocol is for Golden Gate Assembly of pairs of DNA fragments into plasmids using the New England Biolabs +Golden Gate Assembly Kit (BsaI-HFv2), product ID NEB #E1601. +Protocol implements the specific case of two part assembly for the NEB-provided protocol: +https://www.neb.com/protocols/2018/10/02/golden-gate-assembly-protocol-for-using-neb-golden-gate-assembly-mix-e1601 +''' +doc.add(protocol) + +# create the materials to be provisioned +nf_h2o = sbol3.Component('nuclease_free_H2O', 'https://identifiers.org/pubchem.compound:962') +nf_h2o.name = 'Nuclease-free Water' +doc.add(nf_h2o) + +gg_buf = sbol3.Component('NEB_GoldenGate_Buffer', tyto.SBO.functional_entity) +gg_buf.name = 'NEB T4 DNA Ligase Buffer (10X)' +gg_buf.derived_from.append('https://www.neb.com/products/e1601-neb-golden-gate-assembly-mix') +doc.add(gg_buf) + +gg_mix = sbol3.Component('NEB_GoldenGate_AssemblyMix', tyto.SBO.functional_entity) +gg_mix.name = 'NEB Golden Gate Assembly Mix' +gg_mix.derived_from.append('https://www.neb.com/products/e1601-neb-golden-gate-assembly-mix') +doc.add(gg_mix) + +# add an parameters for specifying the layout of the DNA source plate and build plate +dna_sources = protocol.input_value('source_samples', 'http://bioprotocols.org/paml#SampleCollection') +# TODO: add_input should be returning a usable ActivityNode! +dna_build_layout = protocol.input_value('build_layout', 'http://bioprotocols.org/paml#SampleData') + +# actual steps of the protocol +# get a plate space for building +build_wells = protocol.primitive_step('DuplicateCollection', source=dna_build_layout) + +# put DNA into the selected wells following the build plan +protocol.primitive_step('TransferByMap', source=dna_sources, destination=build_wells.output_pin('samples'), plan=dna_build_layout) + +# put buffer, assembly mix, and water into build wells too +protocol.primitive_step('Provision', resource=gg_buf, destination=build_wells.output_pin('samples'), + amount=sbol3.Measure(2, tyto.OM.microliter)) +protocol.primitive_step('Provision', resource=gg_mix, destination=build_wells.output_pin('samples'), + amount=sbol3.Measure(1, tyto.OM.microliter)) +protocol.primitive_step('Provision', resource=nf_h2o, destination=build_wells.output_pin('samples'), + amount=sbol3.Measure(15, tyto.OM.microliter)) + +# seal and spin to mix +protocol.primitive_step('Seal', location=build_wells.output_pin('samples')) # TODO: add type +protocol.primitive_step('Spin', acceleration=sbol3.Measure(300, "http://bioprotocols.org/temporary/unit/g"), # TODO: replace with OM-2 unit on resolution of https://github.com/HajoRijgersberg/OM/issues/54 + duration=sbol3.Measure(3, tyto.OM.minute)) +protocol.primitive_step('Unseal', location=build_wells.output_pin('samples')) + +# incubation steps +protocol.primitive_step('Incubate', location=build_wells.output_pin('samples'), + duration=sbol3.Measure(60, tyto.OM.minute), + temperature=sbol3.Measure(37, tyto.OM.get_uri_by_term('degree Celsius'))) # TODO: replace after resolution of https://github.com/SynBioDex/tyto/issues/29 +protocol.primitive_step('Incubate', location=build_wells.output_pin('samples'), + duration=sbol3.Measure(5, tyto.OM.minute), + temperature=sbol3.Measure(60, tyto.OM.get_uri_by_term('degree Celsius'))) # TODO: replace after resolution of https://github.com/SynBioDex/tyto/issues/29 + + +output = protocol.designate_output('constructs', 'http://bioprotocols.org/paml#SampleCollection', build_wells.output_pin('samples')) +protocol.order(protocol.get_last_step(), output) # don't return until all else is complete + + +######################################## +# Validate and write the document +print('Validating and writing protocol') +v = doc.validate() +assert len(v) == 0, "".join(f'\n {e}' for e in v) + +temp_name = os.path.join(tempfile.gettempdir(), 'golden_gate_assembly.nt') +doc.write(temp_name, sbol3.SORTED_NTRIPLES) +print(f'Wrote file as {temp_name}') + +# render and view the dot +dot = protocol.to_dot() +dot.render(f'{protocol.name}.gv') +dot.view() diff --git a/examples/growth_curve.py b/examples/growth_curve.py new file mode 100644 index 0000000..a493ced --- /dev/null +++ b/examples/growth_curve.py @@ -0,0 +1,271 @@ +import sbol3 +import paml +import tyto + +############################################# +# Helper functions + +# set up the document +doc = sbol3.Document() +sbol3.set_namespace('https://sd2e.org/PAML/') + +############################################# +# Import the primitive libraries +print('Importing libraries') +paml.import_library('liquid_handling') +paml.import_library('plate_handling') +paml.import_library('spectrophotometry') + +# this should really get pulled into a common library somewhere +rpm = sbol3.UnitDivision('rpm',name='rpm', symbol='rpm',label='revolutions per minute',numerator=tyto.OM.revolution,denominator=tyto.OM.minute) +doc.add(rpm) + + +############################################# +# Create the protocols + +print('Constructing measurement sub-protocols') +# This will be used 10 times generating "OD_Plate_1" .. "OD_Plate_9" + +split_and_measure = paml.Protocol('SplitAndMeasure', name="Split samples, dilute, and measure") +split_and_measure.description = ''' +Subprotocol to split a portion of each sample in a plate into another plate, diluting +with PBS, then measure OD and fluorescence from that other plate +''' +doc.add(split_and_measure) + +# plate for split-and-measure subroutine +od_plate = paml.Container(name='OD Plate', type=tyto.NCIT.Microplate, max_coordinate='H12') +split_and_measure.locations = {od_plate} + +# Inputs: collection of samples, pbs_source +samples = split_and_measure.add_input(name='samples', description='Samples to measure', type='http://bioprotocols.org/paml#LocatedSamples') +pbs_source = split_and_measure.add_input(name='pbs', description='Source for PBS', type='http://bioprotocols.org/paml#LocatedSamples') + +# subprotocol steps +s_p = split_and_measure.execute_primitive('Dispense', source=pbs_source, destination=od_plate, + amount=sbol3.Measure(90, tyto.OM.microliter)) +split_and_measure.add_flow(split_and_measure.initial(), s_p) # dispensing OD can be a first action +s_u = split_and_measure.execute_primitive('Unseal', location=samples) +split_and_measure.add_flow(split_and_measure.initial(), s_u) # unsealing the growth plate can be a first action +s_t = split_and_measure.execute_primitive('TransferInto', source=samples, destination=s_p.output_pin('samples'), + amount=sbol3.Measure(10, tyto.OM.microliter), + mixCycles=sbol3.Measure(10, tyto.OM.number)) +split_and_measure.add_flow(s_u, s_t) # transfer can't happen until growth plate is unsealed + +# add the measurements, in parallel +ready_to_measure = paml.Fork() +split_and_measure.activities.append(ready_to_measure) +split_and_measure.add_flow(s_t.output_pin('samples'), ready_to_measure) +measurement_complete = paml.Join() +split_and_measure.activities.append(measurement_complete) + +s_a = split_and_measure.execute_primitive('MeasureAbsorbance', samples=ready_to_measure, + wavelength=sbol3.Measure(600, tyto.OM.nanometer), + numFlashes=sbol3.Measure(25, tyto.OM.number)) +v_a = split_and_measure.add_output('absorbance', s_a.output_pin('measurements')) +split_and_measure.add_flow(v_a, measurement_complete) + +gains = {0.1, 0.2, 0.16} +for g in gains: + s_f = split_and_measure.execute_primitive('MeasureFluorescence', samples=ready_to_measure, + excitationWavelength=sbol3.Measure(488, tyto.OM.nanometer), + emissionBandpassWavelength=sbol3.Measure(530, tyto.OM.nanometer), + numFlashes=sbol3.Measure(25, tyto.OM.number), + gain=sbol3.Measure(g, tyto.OM.number)) + v_f = split_and_measure.add_output('fluorescence_'+str(g), s_f.output_pin('measurements')) + split_and_measure.add_flow(v_f, measurement_complete) + +s_c = split_and_measure.execute_primitive('Cover', location=od_plate) +split_and_measure.add_flow(measurement_complete, s_c) +split_and_measure.add_flow(s_c, split_and_measure.final()) + +s_s = split_and_measure.execute_primitive('Seal', location=samples, + type='http://autoprotocol.org/lids/breathable') # need to turn this into a proper ontology +split_and_measure.add_flow(measurement_complete, s_s) +split_and_measure.add_flow(s_s, split_and_measure.final()) + +print('Measurement sub-protocol construction complete') + + + +overnight_od_measure = paml.Protocol('OvernightODMeasure', name="Split samples and measure, without dilution") +overnight_od_measure.description = ''' +Subprotocol to split a portion of each sample in an unsealed plate into another plate, then measure OD and fluorescence from that other plate +''' +doc.add(overnight_od_measure) + +# plate for split-and-measure subroutine +od_plate = paml.Container(name='OD Plate', type=tyto.NCIT.Microplate, max_coordinate='H12') +overnight_od_measure.locations = {od_plate} + +# Input: collection of samples +samples = overnight_od_measure.add_input(name='samples', description='Samples to measure', type='http://bioprotocols.org/paml#LocatedSamples') + +# subprotocol steps +s_t = overnight_od_measure.execute_primitive('Transfer', source=samples, destination=od_plate, + amount=sbol3.Measure(200, tyto.OM.microliter)) +overnight_od_measure.add_flow(overnight_od_measure.initial(), s_t) # first action + +# add the measurements, in parallel +ready_to_measure = paml.Fork() +overnight_od_measure.activities.append(ready_to_measure) +overnight_od_measure.add_flow(s_t.output_pin('samples'), ready_to_measure) +measurement_complete = paml.Join() +overnight_od_measure.activities.append(measurement_complete) + +s_a = overnight_od_measure.execute_primitive('MeasureAbsorbance', samples=ready_to_measure, + wavelength=sbol3.Measure(600, tyto.OM.nanometer), + numFlashes=sbol3.Measure(25, tyto.OM.number)) +v_a = overnight_od_measure.add_output('absorbance', s_a.output_pin('measurements')) +overnight_od_measure.add_flow(v_a, measurement_complete) + +gains = {0.1, 0.2, 0.16} +for g in gains: + s_f = overnight_od_measure.execute_primitive('MeasureFluorescence', samples=ready_to_measure, + excitationWavelength=sbol3.Measure(488, tyto.OM.nanometer), + emissionBandpassWavelength=sbol3.Measure(530, tyto.OM.nanometer), + numFlashes=sbol3.Measure(25, tyto.OM.number), + gain=sbol3.Measure(g, tyto.OM.number)) + v_f = overnight_od_measure.add_output('fluorescence_'+str(g), s_f.output_pin('measurements')) + overnight_od_measure.add_flow(v_f, measurement_complete) + +s_c = overnight_od_measure.execute_primitive('Cover', location=od_plate) +overnight_od_measure.add_flow(measurement_complete, s_c) +overnight_od_measure.add_flow(s_c, overnight_od_measure.final()) + +overnight_od_measure.add_flow(measurement_complete, overnight_od_measure.final()) + +print('Overnight measurement sub-protocol construction complete') +############################################# +# Now the full protocol + +print('Making protocol') + +protocol = paml.Protocol('GrowthCurve', name = "SD2 Yeast growth curve protocol") +protocol.description = ''' +Protocol from SD2 Yeast States working group for studying growth curves: +Grow up cells and read with plate reader at n-hour intervals +''' +doc.add(protocol) + +# Create the materials to be provisioned +PBS = sbol3.Component('PBS', 'https://identifiers.org/pubchem.compound:24978514') +PBS.name = 'Phosphate-Buffered Saline' # I'd like to get this name from PubChem with tyto +doc.add(PBS) +# need to retrieve and convert this one +SC_media = sbol3.Component('SC_Media', 'TBD', name='Synthetic Complete Media') +doc.add(SC_media) +SC_plus_dox = sbol3.Component('SC_Media_plus_dox', 'TBD', name='Synthetic Complete Media plus 40nM Doxycycline') +doc.add(SC_plus_dox) +protocol.material += {PBS, SC_media, SC_plus_dox} + +## create the containers +# provisioning sources +pbs_source = paml.Container(name='PBS Source', type=tyto.NCIT.Bottle) +sc_source = paml.Container(name='SC Media + 40nM Doxycycline Source', type=tyto.NCIT.Bottle) +om_source = paml.Container(name='Overnight SC Media Source', type=tyto.NCIT.Bottle) +# plates for the general protocol +overnight_plate = paml.Container(name='Overnight Growth Plate', type=tyto.NCIT.Microplate, max_coordinate='H12') +overnight_od_plate = paml.Container(name='Overnight Growth Plate', type=tyto.NCIT.Microplate, max_coordinate='H12') +growth_plate = paml.Container(name='Growth Curve Plate', type=tyto.NCIT.Microplate, max_coordinate='H12') +protocol.locations = {pbs_source, sc_source, om_source, overnight_plate, growth_plate} + +# One input: a microplate full of strains +# TODO: change this to allow alternative places +strain_plate = protocol.add_input(name='strain_plate', description='Plate of strains to grow', type='http://bioprotocols.org/paml#LocatedSamples') +#input_plate = paml.Container(name='497943_4_UWBF_to_stratoes', type=tyto.NCIT.Microplate, max_coordinate='H12') + +print('Constructing protocol steps') + +# set up the sources +p_pbs = protocol.execute_primitive('Provision', resource=PBS, destination=pbs_source, + amount=sbol3.Measure(117760, tyto.OM.microliter)) +protocol.add_flow(protocol.initial(), p_pbs) # start with provisioning +p_om = protocol.execute_primitive('Provision', resource=SC_media, destination=om_source, + amount=sbol3.Measure(98, tyto.OM.milliliter)) +protocol.add_flow(protocol.initial(), p_om) # start with provisioning +p_scm = protocol.execute_primitive('Provision', resource=SC_plus_dox, destination=sc_source, + amount=sbol3.Measure(117200, tyto.OM.microliter)) +protocol.add_flow(protocol.initial(), p_scm) # start with provisioning + +# prep the overnight culture, then seal away the source plate again +s_d = protocol.execute_primitive('Dispense', source=p_om.output_pin('samples'), destination=overnight_plate, + amount=sbol3.Measure(500, tyto.OM.microliter)) +s_u = protocol.execute_primitive('Unseal', location=strain_plate) +s_t = protocol.execute_primitive('TransferInto', source=strain_plate, destination=s_d.output_pin('samples'), + amount=sbol3.Measure(5, tyto.OM.microliter), + mixCycles = sbol3.Measure(10, tyto.OM.number)) +s_s = protocol.execute_primitive('Seal', location=strain_plate, + type='http://autoprotocol.org/lids/breathable') # need to turn this into a proper ontology +protocol.add_flow(s_u, s_t) # transfer can't happen until strain plate is unsealed ... +protocol.add_flow(s_t, s_s) # ... and must complete before we re-seal it + +# run the overnight culture +overnight_samples = s_t.output_pin('samples') +s_s = protocol.execute_primitive('Seal', location=overnight_samples, + type='http://autoprotocol.org/lids/breathable') # need to turn this into a proper ontology +s_i = protocol.execute_primitive('Incubate', location=overnight_samples, + temperature=sbol3.Measure(30, tyto.OM.get_uri_by_term('degree Celsius')), + duration=sbol3.Measure(16, tyto.OM.hour), + shakingFrequency=sbol3.Measure(350, rpm.identity)) +protocol.add_flow(s_t, s_s) # sealing after transfer +protocol.add_flow(s_s, s_i) # incubation after sealing + +# Check the OD after running overnight; note that this is NOT the same measurement process as for the during-growth measurements +s_u = protocol.execute_primitive('Unseal', location=overnight_samples) # added because using the subprotocol leaves a sealed plate +protocol.add_flow(s_i, s_u) # growth plate after measurement +s_m = protocol.execute_subprotocol(overnight_od_measure, samples=overnight_samples) +protocol.add_flow(s_u, s_m) # measurement after incubation and unsealing + +# Set up the growth plate +s_d = protocol.execute_primitive('Dispense', source=p_scm.output_pin('samples'), destination=growth_plate, + amount=sbol3.Measure(700, tyto.OM.microliter)) +s_t = protocol.execute_primitive(doc.find('TransferInto'), source=overnight_samples, destination=s_d.output_pin('samples'), + amount=sbol3.Measure(2, tyto.OM.microliter), + mixCycles = sbol3.Measure(10, tyto.OM.number)) +s_s = protocol.execute_primitive('Seal', location=overnight_samples, + type='http://autoprotocol.org/lids/breathable') # need to turn this into a proper ontology +protocol.add_flow(s_u, s_t) # transfer can't happen until overnight plate is unsealed ... +protocol.add_flow(s_t, s_s) # ... and must complete before we re-seal it +protocol.add_flow(s_m, s_s) # ... as must its measurement + +# run the step-by-step culture +growth_samples = s_t.output_pin('samples') +last_round = None +# sample_hours = [1, 3, 6, 9, 12, 15, 18, 21, 24] # Original: modified to be friendly to human execution +sample_hours = [1, 3, 6, 9, 18, 21, 24] +for i in range(0,len(sample_hours)): + incubation_hours = sample_hours[i] - (sample_hours[i-1] if i>0 else 0) + s_i = protocol.execute_primitive('Incubate', location=growth_samples, + temperature=sbol3.Measure(30, tyto.OM.get_uri_by_term('degree Celsius')), + duration=sbol3.Measure(incubation_hours, tyto.OM.hour), + shakingFrequency=sbol3.Measure(350, rpm.identity)) + s_m = protocol.execute_subprotocol(split_and_measure, samples=growth_samples, pbs=p_pbs.output_pin('samples')) + if last_round: + protocol.add_flow(last_round, s_i) # measurement after incubation + protocol.add_flow(s_i, s_m) # measurement after incubation + last_round = s_m + +protocol.add_flow(last_round, protocol.final()) + +print('Protocol construction complete') + + +###################### +# Invocation of protocol on a plate:; + +# plate for invoking the protocol +#input_plate = paml.Container(name='497943_4_UWBF_to_stratoes', type=tyto.NCIT.Microplate, max_coordinate='H12') + + +print('Validating document') +for e in doc.validate().errors: print(e); +for w in doc.validate().warnings: print(w); + +print('Writing document') + +doc.write('test/testfiles/growth_curve.json','json-ld') +doc.write('test/testfiles/growth_curve.ttl','turtle') + +print('Complete') diff --git a/examples/ludox_ot2.py b/examples/ludox_ot2.py new file mode 100644 index 0000000..1ba1f32 --- /dev/null +++ b/examples/ludox_ot2.py @@ -0,0 +1,28 @@ +#Protocol Name:iGEM 2018 LUDOX OD calibration protocol + + +from opentrons import protocol_api + +metadata = {'apiLevel': '2.11'\} + +leftTiprack0 = protocol.load_labware('geb_96_tiprack_1000ul', 4) +leftTiprack1 = protocol.load_labware('geb_96_tiprack_1000ul', 5) +left = protocol.load_instrument('p1000_single_gen2', 'left', tip_rack=leftTiprack0,leftTiprack1) +rightTiprack0 = protocol.load_labware('opentrons_96_tiprack_20ul', 6) +rightTiprack1 = protocol.load_labware('opentrons_96_tiprack_20ul', 7) +right = protocol.load_instrument('p20_single_gen2', 'right', tip_rack=rightTiprack0,rightTiprack1) + + +#Protocol Materials +#[ddH2O](https://identifiers.org/pubchem.substance:24901740) +reservoir = protocol.load_labware('nest_12_reservoir_15ml', 1) +#[LUDOX](https://identifiers.org/pubchem.substance:24866361) + +#Steps +plate1 = protocol.load_labware('corning_48_wellplate_1.6ml_flat', 2) +plate2 = protocol.load_labware('corning_48_wellplate_1.6ml_flat', 3) +left.transfer(80.0,reservoir['A1'],plate1['A1:D1']) +right.transfer(90.0,reservoir['A2'],plate1['A2:D2']) +left.transfer(100.0,reservoir['A1'],plate2['A1:D1']) +right.transfer(110.0,reservoir['A2'],plate2['A2:D2']) + diff --git a/notebooks/Autoprotocol.ipynb b/notebooks/Autoprotocol.ipynb new file mode 100644 index 0000000..870d54f --- /dev/null +++ b/notebooks/Autoprotocol.ipynb @@ -0,0 +1,153 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "e7df9f1c", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import tempfile\n", + "import unittest\n", + "import filecmp\n", + "import sbol3\n", + "import paml\n", + "import tyto\n", + "import uml\n", + "import json\n", + "\n", + "from paml_convert.autoprotocol.autoprotocol_specialization import AutoprotocolSpecialization\n", + "from paml_convert.autoprotocol.strateos_api import StrateosAPI, StrateosConfig\n", + "from autoprotocol import container_type as ctype\n", + "from paml.execution_engine import ExecutionEngine\n", + "from container_api.client_api import matching_containers, strateos_id\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f6beec4f", + "metadata": {}, + "outputs": [], + "source": [ + "## Execute a PAML protocol, emitting an Autoprotocol protocol\n", + "\n", + "# Setup PAML protocol for execution\n", + "out_dir = \".\"\n", + "doc = sbol3.Document()\n", + "sbol3.set_namespace('https://bbn.com/scratch/')\n", + "protocol_file = os.path.join(os.getcwd(), \"../test/testfiles\", \"igem_ludox_test.nt\")\n", + "doc.read(protocol_file, 'nt')\n", + "protocol = doc.find(\"https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018\")\n", + "agent = sbol3.Agent(\"test_agent\")\n", + "\n", + "\n", + "# Setup Output location and Credentials for Strateos\n", + "out_dir = \"out\"\n", + "if not os.path.exists(out_dir):\n", + " os.mkdir(out_dir)\n", + "autoprotocol_output = os.path.join(out_dir, \"test_LUDOX_autoprotocol.json\")\n", + "secrets_file = os.path.join(os.getcwd(), \"../secrets/strateos_secrets.json\")\n", + "\n", + "\n", + "# Setup Autoprotocol specialization\n", + "resolutions = {\n", + " doc.find(\"https://bbn.com/scratch/LUDOX\") : \"rs1b6z2vgatkq7\",\n", + " doc.find(\"https://bbn.com/scratch/ddH2O\") : \"rs1c7pg8qs22dt\",\n", + " \"container_id\" : \"ct1g9qsg4wx6gcj\"\n", + "}\n", + "api = StrateosAPI(cfg=StrateosConfig.from_file(secrets_file))\n", + "autoprotocol_specialization = AutoprotocolSpecialization(autoprotocol_output, api, resolutions)\n", + "\n", + "\n", + "# Execute the protocol\n", + "ee = ExecutionEngine(specializations=[autoprotocol_specialization])\n", + "parameter_values = [paml.ParameterValue(parameter=protocol.get_input(\"wavelength\"), \n", + " value=uml.LiteralIdentified(value=sbol3.Measure(100, tyto.OM.nanometer)))]\n", + "execution = ee.execute(protocol, agent, id=\"test_execution\", parameter_values=parameter_values)\n", + "\n", + "# Return the instructions from the protocol\n", + "ee.specializations[0].protocol.instructions" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a847cae6", + "metadata": {}, + "outputs": [], + "source": [ + "# Display the Autoprotocol JSON\n", + "with open(autoprotocol_output, \"r\") as f:\n", + " autoprotocol_json = json.loads(f.read())\n", + "autoprotocol_json" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b114c5d1", + "metadata": {}, + "outputs": [], + "source": [ + "# Submit Autoprotocol object for execution at Strateos\n", + "\n", + "st = api.get_strateos_connection()\n", + "response = st.analyze_run(ee.specializations[0].protocol, test_mode=True)\n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a3bdb60", + "metadata": {}, + "outputs": [], + "source": [ + "#response = st.submit_run(ee.specializations[0].protocol, \n", + "# project_id=api.cfg.project_id,\n", + "# title=protocol.name,\n", + "# test_mode=True)\n", + "\n", + "#response" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6f99be08", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "execution": { + "allow_errors": false, + "timeout": 300 + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/README.md b/notebooks/README.md new file mode 100644 index 0000000..8f31395 --- /dev/null +++ b/notebooks/README.md @@ -0,0 +1,17 @@ +# PAML Notebooks + +The notebooks in this directory demonstrate several use cases for the PAML language, including: + +- [paml_demo.ipynb](paml_demo.ipynb): Demonstrates building a protocol from a list of PAML API instructions, visualizing the protocol, simulated protocol execution,and serialization to Markdown. +- [Autoprotocol.ipynb](Autoprotocol.ipynb): Demonstrate converting and submitting protocol from PAML to Autoprotocol for execution at Strateos +- [markdown.ipynb](markdown.ipynb): Convert protocol to Markdown. + +# Dependencies + +- [container_api](https://github.com/rpgoldman/container-ontology): Needed to resolve container specifications to a list of possible container types. Used by importing as follows: + ``` + from container_api.client_api import matching_containers, strateos_id + + possible_container_types = matching_containers(spec) + possible_short_names = [strateos_id(x) for x in possible_container_types] + ``` \ No newline at end of file diff --git a/notebooks/UItemplate.ipynb b/notebooks/UItemplate.ipynb new file mode 100644 index 0000000..94870fc --- /dev/null +++ b/notebooks/UItemplate.ipynb @@ -0,0 +1,182 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "745ffe8b", + "metadata": {}, + "outputs": [], + "source": [ + "# Prototype selecting primitive declarations with drop down\n", + "from IPython.display import clear_output, display_javascript, display, HTML\n", + "import ipywidgets as widgets\n", + "from ipywidgets import GridspecLayout\n", + "\n", + "import os\n", + "import tempfile\n", + "import unittest\n", + "import filecmp\n", + "import sbol3\n", + "import paml\n", + "import tyto\n", + "import uml\n", + "from paml.execution_engine import ExecutionEngine\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d717558f", + "metadata": {}, + "outputs": [], + "source": [ + "#############################################\n", + "# set up the document\n", + "print('Setting up document')\n", + "doc = sbol3.Document()\n", + "sbol3.set_namespace('https://bbn.com/scratch/')\n", + "\n", + "#############################################\n", + "# Import the primitive libraries\n", + "print('Importing libraries')\n", + "paml.import_library('liquid_handling')\n", + "print('... Imported liquid handling')\n", + "paml.import_library('plate_handling')\n", + "print('... Imported plate handling')\n", + "paml.import_library('spectrophotometry')\n", + "print('... Imported spectrophotometry')\n", + "paml.import_library('sample_arrays')\n", + "print('... Imported sample arrays')\n", + "\n", + "primitives = { primitive.identity : primitive \n", + " for lib, lib_doc in paml.loaded_libraries.items()\n", + " for primitive in lib_doc.objects }" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48d35e89", + "metadata": {}, + "outputs": [], + "source": [ + "hidden_html = HTML('''show.''')\n", + "\n", + "def make_and_exec_new_cell(text : str):\n", + " display_javascript(\"\"\"var t_cell = Jupyter.notebook.insert_cell_below()\n", + " t_cell.set_text('# @hidden\\\\n{}\\\\n{}');\n", + " var t_index = IPython.notebook.get_cells().indexOf(t_cell);\n", + " IPython.notebook.get_cell(t_index).render();\n", + " IPython.notebook.get_cell(t_index).execute();\n", + " Jupyter.notebook.execute_cells(t_index);\"\"\".format(display(hidden_html), text.replace('\\n','\\\\n')), raw=True)\n", + "\n", + "def make_new_cell(text : str):\n", + " display_javascript(\"\"\"var t_cell = Jupyter.notebook.insert_cell_below()\n", + " t_cell.set_text('{}');\n", + " var t_index = IPython.notebook.get_cells().indexOf(t_cell);\n", + " IPython.notebook.to_code(t_index);\n", + " IPython.notebook.get_cell(t_index).render();\n", + " Jupyter.notebook.execute_cells(t_index);\"\"\".format(text.replace('\\n','\\\\n').replace('\\'', \"\\\\'\")), raw=True)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28edddc2", + "metadata": {}, + "outputs": [], + "source": [ + "layout_hidden = widgets.Layout(visibility = 'hidden')\n", + "layout_visible = widgets.Layout(visibility = 'visible')\n", + "\n", + "def renderUI():\n", + " make_and_exec_new_cell(\"ui\")\n", + " \n", + "def new_protocol_event(b):\n", + " renderUI()\n", + " make_new_cell(f\"{paml.Protocol.template()}\")\n", + " \n", + "def create_primitive_event(b):\n", + " #renderUI()\n", + " #make_new_cell(primitive_template)\n", + " new_primitive_drop_down.layout = layout_visible\n", + " new_primitive_drop_down_ok.layout = layout_visible\n", + "\n", + "def new_primitive_event(b):\n", + " renderUI()\n", + " #print(primitive.template())\n", + " make_new_cell(primitive.template())\n", + " #make_new_cell(\"foo\")\n", + " #new_primitive_drop_down.layout = layout_visible\n", + " #new_primitive_drop_down_ok.layout = layout_visible\n", + "\n", + "\n", + "#protocol_template = f\"protocol\"\n", + "#primitive_template = f\"primitive\"\n", + "#primitives = [\"a\", \"b\", \"c\"]\n", + "primitive_keys = list(primitives.keys())\n", + "\n", + "new_protocol_button = widgets.Button(description=\"New Protocol\", \n", + " tooltip=\"Create a template for a new protocol.\")\n", + "new_primitive_button = widgets.Button(description=\"New Primitive\", \n", + " tooltip=\"Select a template for a new primitive.\")\n", + "new_primitive_drop_down = widgets.Dropdown(\n", + " options=primitive_keys,\n", + " value=primitive_keys[0],\n", + " description='Primitive:',\n", + " layout = layout_hidden\n", + ")\n", + "new_primitive_drop_down_ok = widgets.Button(description=\"Add\", \n", + " tooltip=\"Create a template for a new primitive.\",\n", + " layout=layout_hidden)\n", + "\n", + "new_protocol_button.on_click(new_protocol_event)\n", + "new_primitive_button.on_click(create_primitive_event)\n", + "new_primitive_drop_down_ok.on_click(new_primitive_event)\n", + "\n", + "ui = GridspecLayout(1, 4)\n", + "ui[0, 0] = new_protocol_button\n", + "ui[0, 1] = new_primitive_button\n", + "ui[0, 2] = new_primitive_drop_down\n", + "ui[0, 3] = new_primitive_drop_down_ok\n", + "ui" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/markdown.ipynb b/notebooks/markdown.ipynb new file mode 100644 index 0000000..9f203f4 --- /dev/null +++ b/notebooks/markdown.ipynb @@ -0,0 +1,98 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "cebc83e6", + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "import tempfile\n", + "import unittest\n", + "import filecmp\n", + "import sbol3\n", + "import paml\n", + "import tyto\n", + "import uml\n", + "import json\n", + "\n", + "from paml_convert.markdown.markdown_specialization import MarkdownSpecialization\n", + "\n", + "from paml.execution_engine import ExecutionEngine\n", + "from IPython.display import Markdown\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7b25c4e8", + "metadata": {}, + "outputs": [], + "source": [ + "## Execute a PAML protocol, emitting a Markdown protocol\n", + "\n", + "# Setup PAML protocol for execution\n", + "out_dir = \".\"\n", + "doc = sbol3.Document()\n", + "sbol3.set_namespace('https://bbn.com/scratch/')\n", + "protocol_file = os.path.join(os.getcwd(), \"../test/testfiles\", \"igem_ludox_test.nt\")\n", + "doc.read(protocol_file, 'nt')\n", + "protocol = doc.find(\"https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018\")\n", + "agent = sbol3.Agent(\"test_agent\")\n", + "\n", + "\n", + "# Setup Markdown Specialization\n", + "markdown_output = os.path.join(out_dir, \"test_LUDOX_markdown.md\")\n", + "markdown_specialization = MarkdownSpecialization(markdown_output)\n", + "\n", + "\n", + "# Execute the protocol\n", + "ee = ExecutionEngine(specializations=[markdown_specialization])\n", + "parameter_values = [\n", + " paml.ParameterValue(parameter=protocol.get_input(\"wavelength\"), \n", + " value=uml.LiteralIdentified(value=sbol3.Measure(100, tyto.OM.nanometer)))\n", + "]\n", + "execution = ee.execute(protocol, agent, id=\"test_execution\", parameter_values=parameter_values)\n", + "\n", + "Markdown(ee.specializations[0].markdown)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b212fa5e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "execution": { + "allow_errors": false, + "timeout": 300 + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/paml_demo.ipynb b/notebooks/paml_demo.ipynb new file mode 100644 index 0000000..a248d4d --- /dev/null +++ b/notebooks/paml_demo.ipynb @@ -0,0 +1,447 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "607f88bd", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "import os\n", + "import tempfile\n", + "import sbol3\n", + "import paml\n", + "import tyto\n", + "import uml\n", + "import json\n", + "import rdflib as rdfl\n", + "from IPython.display import Markdown\n", + "\n", + "\n", + "from paml.execution_engine import ExecutionEngine\n", + "from paml_check.paml_check import check_doc\n", + "from paml_convert.markdown.markdown_specialization import MarkdownSpecialization\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cfaa137", + "metadata": { + "slideshow": { + "slide_type": "skip" + } + }, + "outputs": [], + "source": [ + "#############################################\n", + "# set up the document\n", + "print('Setting up document')\n", + "doc = sbol3.Document()\n", + "sbol3.set_namespace('https://bbn.com/scratch/')\n", + "\n", + "#############################################\n", + "# Import the primitive libraries\n", + "print('Importing libraries')\n", + "paml.import_library('liquid_handling')\n", + "print('... Imported liquid handling')\n", + "paml.import_library('plate_handling')\n", + "print('... Imported plate handling')\n", + "paml.import_library('spectrophotometry')\n", + "print('... Imported spectrophotometry')\n", + "paml.import_library('sample_arrays')\n", + "print('... Imported sample arrays')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f4c171e", + "metadata": { + "slideshow": { + "slide_type": "subslide" + } + }, + "outputs": [], + "source": [ + "# Print descriptions of the primitives avaiable in each library\n", + "\n", + "dashes = \"-\" * 80\n", + "print(dashes)\n", + "primitives = {}\n", + "for lib, lib_doc in paml.loaded_libraries.items():\n", + " print(f\"{dashes}\\nlibrary: {lib}\")\n", + " for primitive in lib_doc.objects:\n", + " primitives[str(primitive.identity)] = primitive\n", + " print(primitive)\n", + " print(dashes)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "63137432", + "metadata": {}, + "outputs": [], + "source": [ + "# Example of how to generate a template for a new protocol step\n", + "\n", + "print(primitives[\"https://bioprotocols.org/paml/primitives/liquid_handling/Dispense\"].template())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6fe2221", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "protocol = paml.Protocol('iGEM_LUDOX_OD_calibration_2018')\n", + "protocol.name = \"iGEM 2018 LUDOX OD calibration protocol\"\n", + "protocol.description = '''\n", + "With this protocol you will use LUDOX CL-X (a 45% colloidal silica suspension) as a single point reference to\n", + "obtain a conversion factor to transform absorbance (OD600) data from your plate reader into a comparable\n", + "OD600 measurement as would be obtained in a spectrophotometer. This conversion is necessary because plate\n", + "reader measurements of absorbance are volume dependent; the depth of the fluid in the well defines the path\n", + "length of the light passing through the sample, which can vary slightly from well to well. In a standard\n", + "spectrophotometer, the path length is fixed and is defined by the width of the cuvette, which is constant.\n", + "Therefore this conversion calculation can transform OD600 measurements from a plate reader (i.e. absorbance\n", + "at 600 nm, the basic output of most instruments) into comparable OD600 measurements. The LUDOX solution\n", + "is only weakly scattering and so will give a low absorbance value.\n", + "'''\n", + "doc.add(protocol)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0fefac39", + "metadata": {}, + "outputs": [], + "source": [ + "# Add an optional parameter for specifying the wavelength\n", + "wavelength_param = protocol.input_value(\n", + " 'wavelength', sbol3.OM_MEASURE, optional=True,\n", + " default_value=sbol3.Measure(600, tyto.OM.nanometer))\n", + "\n", + "protocol.to_dot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be09367e", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# create the materials to be provisioned\n", + "ddh2o = sbol3.Component('ddH2O', 'https://identifiers.org/pubchem.substance:24901740')\n", + "ddh2o.name = 'Water, sterile-filtered, BioReagent, suitable for cell culture' \n", + "doc.add(ddh2o)\n", + "\n", + "ludox = sbol3.Component('LUDOX', 'https://identifiers.org/pubchem.substance:24866361')\n", + "ludox.name = 'LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O'\n", + "doc.add(ludox)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f036f677", + "metadata": {}, + "outputs": [], + "source": [ + "# specify the type of plate to use\n", + "\n", + "PLATE_SPECIFICATION = \\\n", + " \"\"\"cont:ClearPlate and \n", + " cont:SLAS-4-2004 and\n", + " (cont:wellVolume some \n", + " ((om:hasUnit value om:microlitre) and\n", + " (om:hasNumericalValue only xsd:decimal[>= \"200\"^^xsd:decimal])))\"\"\"\n", + "\n", + "CONT_NS = rdfl.Namespace('https://sift.net/container-ontology/container-ontology#')\n", + "OM_NS = rdfl.Namespace('http://www.ontology-of-units-of-measure.org/resource/om-2/')\n", + "\n", + "PREFIX_MAP = json.dumps({\"cont\": CONT_NS, \"om\": OM_NS})\n", + "\n", + "spec = paml.ContainerSpec(queryString=PLATE_SPECIFICATION, prefixMap=PREFIX_MAP, name='plateRequirement')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4217ac26", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# actual steps of the protocol\n", + "# get a plate\n", + "plate = protocol.primitive_step(\n", + " 'EmptyContainer', specification=spec)\n", + "\n", + "protocol.to_dot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e1a83b05", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# identify wells to use\n", + "c_ddh2o = protocol.primitive_step(\n", + " 'PlateCoordinates', source=plate.output_pin('samples'), coordinates='A1:D1')\n", + "\n", + "# put water in selected wells\n", + "provision_ddh2o = protocol.primitive_step(\n", + " 'Provision', resource=ddh2o, destination=c_ddh2o.output_pin('samples'),\n", + " amount=sbol3.Measure(100, tyto.OM.microliter))\n", + "\n", + "protocol.to_dot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34d9541e", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "#identify wells to use\n", + "c_ludox = protocol.primitive_step(\n", + " 'PlateCoordinates', source=plate.output_pin('samples'), coordinates='A2:D2')\n", + "\n", + "# put ludox in selected wells\n", + "provision_ludox = protocol.primitive_step(\n", + " 'Provision', resource=ludox, destination=c_ludox.output_pin('samples'),\n", + " amount=sbol3.Measure(100, tyto.OM.microliter))\n", + "\n", + "protocol.to_dot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a6022275", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# identify wells to use\n", + "c_measure = protocol.primitive_step(\n", + " 'PlateCoordinates', source=plate.output_pin('samples'), coordinates='A1:D2')\n", + "\n", + "# measure the absorbance\n", + "measure = protocol.primitive_step(\n", + " 'MeasureAbsorbance', samples=c_measure.output_pin('samples'))\n", + "\n", + "# link input parameter to measure primitive input\n", + "protocol.use_value(wavelength_param, measure.input_pin('wavelength'))\n", + "\n", + "protocol.to_dot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fd7bf51a", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# link measurement output to protocol output\n", + "output = protocol.designate_output(\n", + " 'absorbance', sbol3.OM_MEASURE, measure.output_pin('measurements'))\n", + "\n", + "# add control flow from last step to output\n", + "#protocol.order(protocol.get_last_step(), output)\n", + "\n", + "protocol.to_dot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c1d5f154", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import paml_time as pamlt\n", + "\n", + "# protocol starts at time 0\n", + "protocol_start_time = pamlt.startTime(protocol, 0, units=tyto.OM.hour)\n", + "\n", + "# Provisioning requires 60s\n", + "provision_ludox_duration = pamlt.duration(provision_ludox, 60, units=tyto.OM.second)\n", + "provision_ddh2o_duration = pamlt.duration(provision_ddh2o, 60, units=tyto.OM.second)\n", + "\n", + "# Measurement requires 2m\n", + "execute_measurement_duration = pamlt.duration(measure, 2, units=tyto.OM.minute)\n", + "\n", + "# Provisioning ddh2o precedes provisioning ludox by at most 60s\n", + "ddh2o_before_ludox_constraint = pamlt.precedes(provision_ddh2o, [0, 60], \n", + " provision_ludox, units=tyto.OM.second)\n", + "\n", + "# Coonstraints are combined via Boolean logic\n", + "time_constraints = pamlt.TimeConstraints(\"ludox_protocol_constraints\",\n", + " constraints=[pamlt.And([\n", + " protocol_start_time,\n", + " provision_ludox_duration,\n", + " provision_ddh2o_duration,\n", + " execute_measurement_duration,\n", + " ddh2o_before_ludox_constraint\n", + " ])],\n", + " protocols = [protocol]\n", + ")\n", + "\n", + "doc.add(time_constraints)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31b34a50", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Compute a Schedule for the protocol and get the constraint graph\n", + "schedule, graph = check_doc(doc)\n", + "schedule.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81f2d628", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Get minimum duration for the protocol (in seconds)\n", + "min_protocol_durations = graph.get_minimum_duration()\n", + "min_str = \"\\n\".join([f\"The minimum duration of {protocol} is {result['duration']}s \" \\\n", + " for protocol, result in min_protocol_durations.items()])\n", + "print(min_str) " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "067e1361", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Simulate Execution of the Protocol\n", + "\n", + "agent = sbol3.Agent(\"test_agent\")\n", + "ee = ExecutionEngine(specializations=[MarkdownSpecialization(\"test_LUDOX_markdown.md\")])\n", + "parameter_values = [\n", + " paml.ParameterValue(parameter=protocol.get_input(\"wavelength\"), \n", + " value=sbol3.Measure(100, tyto.OM.nanometer))\n", + "]\n", + "execution = ee.execute(protocol, agent, id=\"test_execution\", parameter_values=parameter_values)\n", + "execution.to_dot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be92c80d", + "metadata": {}, + "outputs": [], + "source": [ + "Markdown(ee.specializations[0].markdown)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a8dc4189", + "metadata": { + "tags": [ + "hide-cell" + ] + }, + "outputs": [], + "source": [ + "assert(True)" + ] + } + ], + "metadata": { + "celltoolbar": "Tags", + "execution": { + "allow_errors": false, + "timeout": 300 + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/paml/__init__.py b/paml/__init__.py new file mode 100644 index 0000000..1beea66 --- /dev/null +++ b/paml/__init__.py @@ -0,0 +1,378 @@ +import html +import os +import posixpath +from typing import Dict + +import graphviz +import tyto +from sbol_factory import SBOLFactory, UMLFactory +import sbol3 + +import uml # Note: looks unused, but is used in SBOLFactory + +# Load the ontology and create a Python module called paml_submodule +SBOLFactory('paml_submodule', + posixpath.join(os.path.dirname(os.path.realpath(__file__)), + 'paml.ttl'), + 'http://bioprotocols.org/paml#') + +# Import symbols into the top-level paml module +from paml_submodule import * +from paml.ui import * + +######################################### +# Kludge for getting parents and TopLevels - workaround for pySBOL3 issue #234 +# TODO: remove after resolution of https://github.com/SynBioDex/pySBOL3/issues/234 +def identified_get_parent(self): + if self.identity: + return self.document.find(self.identity.rsplit('/', 1)[0]) + else: + return None +sbol3.Identified.get_parent = identified_get_parent + + +def identified_get_toplevel(self): + if isinstance(self, sbol3.TopLevel): + return self + else: + parent = self.get_parent() + if parent: + return identified_get_toplevel(parent) + else: + return None +sbol3.Identified.get_toplevel = identified_get_toplevel + + +########################################### +# Define extension methods for Protocol + +def protocol_get_last_step(self): + return self.last_step if hasattr(self, 'last_step') else self.initial() +Protocol.get_last_step = protocol_get_last_step # Add to class via monkey patch + + +def protocol_execute_primitive(self, primitive, **input_pin_map): + """Create and add an execution of a Primitive to a Protocol + + :param primitive: Primitive to be invoked (object or string name) + :param input_pin_map: literal value or ActivityNode mapped to names of Behavior parameters + :return: CallBehaviorAction that invokes the Primitive + """ + + # Convenience converter: if given a string, use it to look up the primitive + if isinstance(primitive, str): + primitive = get_primitive(self.document, primitive) + return self.call_behavior(primitive, **input_pin_map) +Protocol.execute_primitive = protocol_execute_primitive # Add to class via monkey patch + + +def protocol_primitive_step(self, primitive: Primitive, **input_pin_map): + """Use a Primitive as an Action in a Protocol, automatically serialized to follow the last step added + + Note that this will not give a stable order if adding to a Protocol that has been deserialized, since + information about the order in which steps were created is not stored. + :param primitive: Primitive to be invoked (object or string name) + :param input_pin_map: literal value or ActivityNode mapped to names of Behavior parameters + :return: CallBehaviorAction that invokes the Primitive + """ + pe = self.execute_primitive(primitive, **input_pin_map) + self.order(self.get_last_step(), pe) + self.last_step = pe # update the last step + return pe +Protocol.primitive_step = protocol_primitive_step # Add to class via monkey patch + +############################################################################### +# +# Protocol class: execution related functions +# +############################################################################### + +def protocol_to_dot(self, legend=False): + def _gv_sanitize(id: str): + return html.escape(id.replace(":", "_")) + + def _legend(): + fontsize="10pt" + legend = graphviz.Digraph(name="cluster_Legend", + graph_attr={ + "label" : "Legend", + "shape" : "rectangle", + "color" : "black", + "rank" : "TB", + "fontsize" : fontsize + }) + legend.node("InitialNode_Legend", _attributes={'label': 'InitialNode', 'fontcolor' : "white", 'shape': 'circle', 'style': 'filled', 'fillcolor': 'black', "fontsize" : fontsize }) + #legend.node("CallBehaviorAction_Legend", _attributes=_type_attrs(uml.CallBehaviorAction())) + legend.node("FinalNode_Legend", _attributes={'label': 'FinalNode', 'fontcolor' : "white", 'shape': 'doublecircle', 'style': 'filled', 'fillcolor': 'black', "fontsize" : fontsize}) + legend.node("ForkNode_Legend", _attributes={'label': 'ForkNode', 'fontcolor' : "white", 'shape': 'rectangle', 'height': '0.02', 'style': 'filled', 'fillcolor': 'black', "fontsize" : fontsize}) + legend.node("MergeNode_Legend", _attributes={'label': 'MergeNode', 'shape': 'diamond', "fontsize" : fontsize}) + legend.node("ActivityParameterNode_Legend", _attributes={'label': "ActivityParameterNode", 'shape': 'rectangle', 'peripheries': '2', "fontsize" : fontsize}) + legend.node("CallBehaviorAction_Legend", _attributes={ + "label" : f'<
InputPin ValuePin: Value
CallBehaviorAction
OutputPin
>', + "shape" : "none", + "style": "rounded", "fontsize" : fontsize + }) + legend.node("a", _attributes={"style": "invis"}) + legend.node("b", _attributes={"style": "invis"}) + legend.node("c", _attributes={"style": "invis"}) + legend.node("d", _attributes={"style": "invis"}) + legend.edge("a", "b", label="uml.ControlFlow", _attributes={"color" : "blue", "fontsize" : fontsize}) + legend.edge("c", "d", label="uml.ObjectFlow", _attributes={"fontsize" : fontsize}) + legend.edge("InitialNode_Legend", "FinalNode_Legend", _attributes={"style" : "invis"}) + legend.edge("FinalNode_Legend", "ForkNode_Legend", _attributes={"style" : "invis"}) + legend.edge("ForkNode_Legend", "MergeNode_Legend", _attributes={"style": "invis"}) + legend.edge("MergeNode_Legend", "ActivityParameterNode_Legend", _attributes={"style": "invis"}) + legend.edge("ActivityParameterNode_Legend", "CallBehaviorAction_Legend", _attributes={"style": "invis"}) + legend.edge("CallBehaviorAction_Legend", "a", _attributes={"style": "invis"}) + legend.edge("b", "c", _attributes={"style": "invis"}) + return legend + + + def _label(object: sbol3.Identified): + truncated = _gv_sanitize(object.identity.replace(f'{self.identity}/', '')) + in_struct = truncated.replace('/',':') + return in_struct #_gv_sanitize(object.identity.replace(f'{self.identity}/', '')) + + def _inpin_str(pin: uml.InputPin) -> str: + if isinstance(pin, uml.ValuePin): + if isinstance(pin.value, uml.LiteralReference): + literal = pin.value.value.lookup() + else: + literal = pin.value.value + if isinstance(literal, sbol3.Measure): + # TODO: replace kludge with something nicer + if literal.unit.startswith('http://www.ontology-of-units-of-measure.org'): + unit = tyto.OM.get_term_by_uri(literal.unit) + else: + unit = literal.unit.rsplit('/',maxsplit=1)[1] + val_str = f'{literal.value} {unit}' + elif isinstance(literal, sbol3.Identified): + val_str = literal.name or literal.display_id + elif isinstance(literal, str) or isinstance(literal, int) or isinstance(literal, flow) or isinstance(literal, bool): + # FIXME: For longer strings, it would be better to left-justify than to center, but I have + # no great ideas about how to tell when that applies. + val_str = html.escape(str(literal)).lstrip('\n').replace('\n', '
') + else: + raise ValueError(f'Do not know how to render literal value {literal}') + return f'{pin.name}: {val_str}' + else: + return pin.name + + def _type_attrs(object: uml.ActivityNode) -> Dict[str,str]: + """Get an appropriate set of properties for rendering a GraphViz node. + Note that while these try to stay close to UML, the limits of GraphViz make us deviate in some cases + + :param object: object to be rendered + :return: dict of attribute/value pairs + """ + if isinstance(object, uml.InitialNode): + return {'label': '', 'shape': 'circle', 'style': 'filled', 'fillcolor': 'black' } + elif isinstance(object, uml.FinalNode): + return {'label': '', 'shape': 'doublecircle', 'style': 'filled', 'fillcolor': 'black'} + elif isinstance(object, uml.ForkNode) or isinstance(object, uml.JoinNode): + return {'label': '', 'shape': 'rectangle', 'height': '0.02', 'style': 'filled', 'fillcolor': 'black'} + elif isinstance(object, uml.MergeNode) or isinstance(object, uml.DecisionNode): + return {'label': '', 'shape': 'diamond'} + elif isinstance(object, uml.ObjectNode): + if isinstance(object, uml.ActivityParameterNode): + label = object.parameter.lookup().property_value.name + else: + raise ValueError(f'Do not know what GraphViz label to use for {object}') + return {'label': label, 'shape': 'rectangle', 'peripheries': '2'} + elif isinstance(object, uml.ExecutableNode): + if isinstance(object, uml.CallBehaviorAction): # render as an HMTL table with pins above/below call + port_row = ' {}
\n' + used_inputs = [o for o in object.inputs if isinstance(o,uml.ValuePin) or self.incoming_edges(o)] + in_ports = ' '.join(f'{_inpin_str(i)}' for i in used_inputs) + in_row = port_row.format(in_ports) if in_ports else '' + out_ports = ' '.join(f'{o.name}' for o in object.outputs) + out_row = port_row.format(out_ports) if out_ports else '' + + node_row = f' {object.behavior.lookup().display_id}\n' + table_template = '<\n{}{}{}
>' + label = table_template.format(in_row,node_row,out_row) + shape = 'none' + else: + raise ValueError(f'Do not know what GraphViz label to use for {object}') + return {'label': label, 'shape': shape, 'style': 'rounded'} + else: + raise ValueError(f'Do not know what GraphViz attributes to use for {object}') + + try: + parent = graphviz.Digraph(name='_root') + parent.attr(compound='true') + dot = graphviz.Digraph(name=f'cluster_{_gv_sanitize(self.identity)}', + graph_attr={ + 'label': self.name, + 'shape': 'box' + }) + if legend: + dot.subgraph(_legend()) + + for edge in self.edges: + src_id = _label(edge.source.lookup()) #edge.source.replace(":", "_") + dest_id = _label(edge.target.lookup()) #edge.target.replace(":", "_") + edge_id = _label(edge) #edge.identity.replace(":", "_") + if isinstance(edge.target.lookup(), uml.CallBehaviorAction): + dest_id = f'{dest_id}:node' + if isinstance(edge.source.lookup(), uml.CallBehaviorAction): + src_id = f'{src_id}:node' + + source = self.document.find(edge.source) + if isinstance(source, uml.Pin): + try: + src_activity = source.get_parent() + #dot.edge(_label(src_activity), src_id, label=f"{source.name}") + #src_activity = source.identity.rsplit('/', 1)[0] # Assumes pin is owned by activity + #dot.edge(src_activity.replace(":", "_"), src_id, label=f"{source.name}") + except Exception as e: + print(f"Cannot find source activity for {source.identity}") + target = self.document.find(edge.target) + if isinstance(target, uml.Pin): + try: + dest_activity = target.get_parent() + #dot.edge(dest_id, _label(dest_activity), label=f"{target.name}") + #dest_activity = target.identity.rsplit('/', 1)[0] # Assumes pin is owned by activity + #dot.edge(dest_id, dest_activity.replace(":", "_"), label=f"{target.name}") + except Exception as e: + print(f"Cannot find source activity for {source.identity}") + + #dot.node(src_id, label=_name_to_label(src_id)) + #dot.node(dest_id, label=_name_to_label(dest_id)) + #dot.node(edge_id, label=edge_id) + color = 'blue' if isinstance(edge, uml.ControlFlow) else 'black' + dot.edge(src_id, dest_id, color=color) + for node in self.nodes: + node_id = _label(node) + dot.node(node_id, **_type_attrs(node)) + + except Exception as e: + print(f"Cannot translate to graphviz: {e}") + parent.subgraph(dot) + return parent +Protocol.to_dot = protocol_to_dot + +def activity_edge_flow_get_target(self): + '''Find the target node of an edge flow + Parameters + ---------- + self + + Returns ActivityNode + ------- + + ''' + if self.edge: + target = self.document.find(self.document.find(self.edge).target) + else: # Tokens for pins do not have an edge connecting pin to activity + target = self.document.find(self.document.find(self.token_source).node).get_parent() + return target +ActivityEdgeFlow.get_target = activity_edge_flow_get_target + +# # Create and add an execution of a subprotocol to a protocol +# def protocol_execute_subprotocol(self, protocol: Protocol, **input_pin_map): +# # strip any activities in the pin map, which will be held for connecting via flows instead +# activity_inputs = {k: v for k, v in input_pin_map.items() if isinstance(v,Activity)} +# non_activity_inputs = {k: v for k, v in input_pin_map.items() if k not in activity_inputs} +# sub = make_CallBehaviorAction(protocol, **non_activity_inputs) +# self.activities.append(sub) +# # add flows for activities being connected implicitly +# for k,v in activity_inputs.items(): +# self.use_value(v, sub.input_pin(k)) +# return sub +# # Monkey patch: +# Protocol.execute_subprotocol = protocol_execute_subprotocol + +def primitive_str(self): + """ + Create a human readable string describing the Primitive + :param self: + :return: str + """ + def mark_optional(parameter): + return "(Optional) " if parameter.lower_value.value < 1 else "" + + input_parameter_strs = "\n\t".join([f"{parameter.property_value}{mark_optional(parameter.property_value)}" + for parameter in self.parameters + if parameter.property_value.direction == uml.PARAMETER_IN]) + input_str = f"Input Parameters:\n\t{input_parameter_strs}" if len(input_parameter_strs) > 0 else "" + output_parameter_strs = "\n\t".join([f"{parameter.property_value}{mark_optional(parameter.property_value)}" + for parameter in self.parameters + if parameter.property_value.direction == uml.PARAMETER_OUT]) + output_str = f"Output Parameters:\n\t{output_parameter_strs}" if len(output_parameter_strs) > 0 else "" + return f""" +Primitive: {self.identity} +{input_str} +{output_str} + """ +Primitive.__str__ = primitive_str + + +def behavior_execution_parameter_value_map(self): + """ + Return a dictionary mapping parameter names to value or (value, unit) + :param self: + :return: + """ + parameter_value_map = {} + + for pv in self.parameter_values: + name = pv.parameter.lookup().property_value.name + if isinstance(pv.value, uml.LiteralReference): + ref = pv.value.value.lookup() + value = ref.value if isinstance(ref, uml.LiteralSpecification) else ref + unit = ref.unit if isinstance(ref, uml.LiteralSpecification) and hasattr(ref, "unit") else None + else: + value = pv.value.value + unit = pv.value.unit if hasattr(pv.value, "unit") else None + + parameter_value_map[name] = {"parameter" : pv.parameter.lookup(), + "value" : (value, unit) if unit else value} + return parameter_value_map +BehaviorExecution.parameter_value_map = behavior_execution_parameter_value_map + + +######################################### +# Library handling +loaded_libraries = {} + + +def import_library(library: str, extension: str = 'ttl', nickname: str = None): + """Import a library of primitives and make it available for use in defining a protocol. + + Note that the actual contents of a library are added into a protocol document lazily, only as they're actually used + TODO: this needs to be generalized to a notion of load paths, to allow other than built-in libraries + + :param library: name of library file to load + :param extension: Format of library; defaults to ttl + :param nickname: Name to load the library under; defaults to library name + :return: Nothing + """ + if not nickname: + nickname = library + if not os.path.isfile(library): + library = posixpath.join(os.path.dirname(os.path.realpath(__file__)), f'lib/{library}.{extension}') + # read in the library and put the document in the library collection + lib = sbol3.Document() + lib.read(library, extension) + loaded_libraries[nickname] = lib + + +def get_primitive(doc: sbol3.Document, name: str): + """Get a Primitive for use in the protocol, either already in the document or imported from a linked library + + :param doc: Working document + :param name: Name of primitive, either displayId or full URI + :return: Primitive that has been found + """ + found = doc.find(name) + if not found: + found = {n: l.find(name) for (n, l) in loaded_libraries.items() if l.find(name)} + if len(found) >= 2: + raise ValueError(f'Ambiguous primitive: found "{name}" in multiple libraries: {found.keys()}') + if len(found) == 0: + raise ValueError(f'Could not find primitive "{name}" in any library') + found = next(iter(found.values())).copy(doc) + if not isinstance(found, Primitive): + raise ValueError(f'"{name}" should be a Primitive, but it resolves to a {type(found).__name__}') + return found diff --git a/paml/execution_engine.py b/paml/execution_engine.py new file mode 100644 index 0000000..1b9f3f0 --- /dev/null +++ b/paml/execution_engine.py @@ -0,0 +1,532 @@ +from abc import ABC +from typing import List +import uuid +import datetime + +import graphviz + +import paml +import uml +import sbol3 + +from paml_convert.behavior_specialization import BehaviorSpecialization, DefaultBehaviorSpecialization + + +class ExecutionEngine(ABC): + """Base class for implementing and recording a PAML executions. + This class can handle common UML activities and the propagation of tokens, but does not execute primitives. + It needs to be extended with specific implementations that have that capability. + """ + + def __init__(self, specializations: List[BehaviorSpecialization] = [DefaultBehaviorSpecialization()]): + self.exec_counter = 0 + self.variable_counter = 0 + self.specializations = specializations + + def next_id(self): + next = self.exec_counter + self.exec_counter += 1 + return next + + def next_variable(self): + variable = f"var_{self.variable_counter}" + self.variable_counter += 1 + return variable + + def execute(self, + protocol: paml.Protocol, + agent: sbol3.Agent, + parameter_values: List[paml.ParameterValue] = {}, + id: str = uuid.uuid4()) -> paml.ProtocolExecution: + """Execute the given protocol against the provided parameters + + Parameters + ---------- + protocol: Protocol to execute + agent: Agent that is executing this protocol + parameter_values: List of all input parameter values (if any) + id: display_id or URI to be used as the name of this execution; defaults to a UUID display_id + + Returns + ------- + ProtocolExecution containing a record of the execution + """ + + # Record in the document containing the protocol + doc = protocol.document + + # First, set up the record for the protocol and parameter values + ex = paml.ProtocolExecution(id, protocol=protocol) + doc.add(ex) + ex.start_time = str(datetime.datetime.now()) # TODO: remove str wrapper after sbol_factory #22 fixed + ex.association.append(sbol3.Association(agent=agent, plan=protocol)) + ex.parameter_values = parameter_values + + # Initialize specializations + for specialization in self.specializations: + specialization.initialize_protocol(ex) + specialization.on_begin() + + # Iteratively execute all unblocked activities until no more tokens can progress + tokens = [] # no tokens to start + ready = protocol.initiating_nodes() + while ready: + for node in ready: + tokens = self.execute_activity_node(ex, node, tokens) + ready = self.executable_activity_nodes(protocol, tokens, ex.parameter_values) + + # TODO: finish implementing + # TODO: ensure that only one token is allowed per edge + # TODO: think about infinite loops and how to abort + + # A Protocol has completed normally if all of its required output parameters have values + set_parameters = (p.parameter.lookup() for p in ex.parameter_values) + ex.completed_normally = all(p in set_parameters for p in protocol.get_required_outputs()) + + # aggregate consumed material records from all behaviors executed within, mark end time, and return + ex.aggregate_child_materials() + ex.end_time = str(datetime.datetime.now()) # TODO: remove str wrapper after sbol_factory #22 fixed + + # End specializations + for specialization in self.specializations: + specialization.on_end() + + return ex + + def executable_activity_nodes(self, protocol: paml.Protocol, tokens: List[paml.ActivityEdgeFlow], + parameter_values: List[paml.ParameterValue])\ + -> List[uml.ActivityNode]: + """Find all of the activity nodes that are ready to be run given the current set of tokens + Note that this will NOT identify activities with no in-flows: those are only set up as initiating nodes + + Parameters + ---------- + protocol: paml.Protocol being executed + tokens: set of ActivityEdgeFlow records that have not yet been consumed + + Returns + ------- + List of ActivityNodes that are ready to be run + """ + candidate_clusters = {} + for t in tokens: + target = t.get_target() + candidate_clusters[target] = candidate_clusters.get(target,[])+[t] + return [n for n,nt in candidate_clusters.items() + if self.enabled_activity_node(protocol, n, nt, parameter_values)] + + def enabled_activity_node(self, protocol: paml.Protocol, node: uml.ActivityNode, + tokens: List[paml.ActivityEdgeFlow], parameter_values: List[paml.ParameterValue]): + """Check whether all incoming edges have values defined by a token in tokens and that all value pin values are + defined. + + Parameters + ---------- + protocol: paml.Protocol being executed + node: node to be executed + tokens: current list of pending edge flows + + Returns + ------- + bool if node is enabled + """ + tokens_present = {node.document.find(t.edge) for t in tokens if t.edge}==protocol.incoming_edges(node) + if hasattr(node, "inputs"): + required_inputs = [node.input_pin(i.property_value.name) + for i in node.behavior.lookup().get_required_inputs()] + required_value_pins = {p for p in required_inputs if isinstance(p, uml.ValuePin)} + required_input_pins = {p for p in required_inputs if not isinstance(p, uml.ValuePin)} + pins_with_tokens = {t.token_source.lookup().node.lookup() for t in tokens if not t.edge} + parameter_names = {pv.parameter.lookup().property_value.name for pv in parameter_values} + pins_with_params = {p for p in required_input_pins if p.name in parameter_names} + satisfied_pins = set(list(pins_with_params) + list(pins_with_tokens)) + input_pins_satisfied = satisfied_pins == required_input_pins + value_pins_assigned = all({i.value for i in required_value_pins}) + return tokens_present and input_pins_satisfied and value_pins_assigned + else: + return tokens_present + + + + + def execute_activity_node(self, ex : paml.ProtocolExecution, node: uml.ActivityNode, + tokens: List[paml.ActivityEdgeFlow]) -> List[paml.ActivityEdgeFlow]: + """Execute a node in an activity, consuming the incoming flows and recording execution and outgoing flows + + Parameters + ---------- + ex: Current execution record + node: node to be executed + tokens: current list of pending edge flows + + Returns + ------- + updated list of pending edge flows + """ + # Extract the relevant set of incoming flow values + # TODO change to pointer lookup after pySBOL #237 + # inputs are tokens that are either connected to node via an edge, or + # are input pins connected to the node (implicitly) (i.e., the node owns the pin) + # and the node identity is a prefix of the pin identity. + inputs = [t for t in tokens if node == t.get_target()] +# if (hasattr(t, "edge") and t.edge and ex.document.find(t.edge).target == node.identity) or \ +# node.identity in ex.document.find(t.token_source).node ] + + # Create a record for the node execution + record = None + new_tokens = [] + + # Dispatch execution based on node type, collecting any object flows produced + # The dispatch methods are the ones that can (or must be) overridden for a particular execution environment + if isinstance(node, uml.InitialNode): + if len(inputs) != 0: + raise ValueError(f'Initial node must have zero inputs, but {node.identity} had {len(inputs)}') + record = paml.ActivityNodeExecution(node=node, incoming_flows=inputs) + ex.executions.append(record) + new_tokens = self.next_tokens(record, ex) + # put a control token on all outgoing edges + + elif isinstance(node, uml.FlowFinalNode): + record = paml.ActivityNodeExecution(node=node, incoming_flows=inputs) + ex.executions.append(record) + new_tokens = self.next_tokens(record, ex) + + elif isinstance(node, uml.ForkNode): + if len(inputs) != 1: + raise ValueError(f'Fork node must have precisely one input, but {node.identity} had {len(inputs)}') + record = paml.ActivityNodeExecution(node=node, incoming_flows=inputs) + ex.executions.append(record) + new_tokens = self.next_tokens(record, ex) + + # elif isinstance(node, uml.JoinNode): + # pass + # elif isinstance(node, uml.MergeNode): + # pass + # elif isinstance(node, uml.DecisionNode): + # pass + elif isinstance(node, uml.ActivityParameterNode): + record = paml.ActivityNodeExecution(node=node, incoming_flows=inputs) + ex.executions.append(record) + if node.parameter.lookup().property_value.direction == uml.PARAMETER_IN: + new_tokens = self.next_tokens(record, ex) + else: + [value] = [i.value.value for i in inputs if isinstance(i.edge.lookup(), uml.ObjectFlow)] + value = uml.LiteralReference(value=value) if isinstance(value, sbol3.Identified) else uml.literal(value) + ex.parameter_values += [paml.ParameterValue(parameter=node.parameter.lookup(), value=value)] + elif isinstance(node, uml.CallBehaviorAction): + record = paml.CallBehaviorExecution(node=node, incoming_flows=inputs) + + # Get the parameter values from input tokens for input pins + input_pin_values = {token.token_source.lookup().node.lookup().identity: + (uml.LiteralReference(value=token.value.value.lookup()) + if isinstance(token.value, uml.LiteralReference) + else uml.literal(token.value.value)) + for token in inputs if not token.edge} + # Get Input value pins + value_pin_values = {pin.identity: pin.value for pin in node.inputs if hasattr(pin, "value")} + # Convert References + value_pin_values = {k: (uml.LiteralReference(value=ex.document.find(v.value)) + if isinstance(v.value, sbol3.refobj_property.ReferencedURI) or + isinstance(v, uml.LiteralReference) + else uml.LiteralReference(value=v)) + for k, v in value_pin_values.items()} + pin_values = { **input_pin_values, **value_pin_values} # merge the dicts + + parameter_values = [paml.ParameterValue(parameter=node.pin_parameter(pin.name), + value=pin_values[pin.identity]) + for pin in node.inputs if pin.identity in pin_values] + parameter_values.sort(key=lambda x: ex.document.find(x.parameter).index) + call = paml.BehaviorExecution(f"execute_{self.next_id()}", + parameter_values=parameter_values, + completed_normally=True, + start_time=str(datetime.datetime.now()), # TODO: remove str wrapper after sbol_factory #22 fixed + end_time=str(datetime.datetime.now()), # TODO: remove str wrapper after sbol_factory #22 fixed + consumed_material=[]) # FIXME handle materials + record.call = call + + ex.document.add(call) + ex.executions.append(record) + new_tokens = self.next_tokens(record, ex) + + ## Add the output values to the call parameter-values + for token in new_tokens: + edge = token.edge.lookup() + if isinstance(edge, uml.ObjectFlow): + source = edge.source.lookup() + parameter = node.pin_parameter(source.name) + parameter_value = uml.LiteralReference(value=token.value) \ + if isinstance(token.value, sbol3.Identified) \ + else uml.literal(token.value) + pv = paml.ParameterValue(parameter=parameter, value=parameter_value) + call.parameter_values += [pv] + + elif isinstance(node, uml.Pin): + record = paml.ActivityNodeExecution(node=node, incoming_flows=inputs) + ex.executions.append(record) + new_tokens = self.next_pin_tokens(record, ex) + elif isinstance(node, uml.OrderedPropertyValue): + # node is an output parameter + out_param = node + [value] = [i.value for i in inputs] # Assume a single input for params + param_value = paml.ParameterValue(parameter=out_param, + value=uml.literal(value.value)) + ex.parameter_values.append(param_value) + else: + raise ValueError(f'Do not know how to execute node {node.identity} of type {node.type_uri}') + + if record: + for specialization in self.specializations: + specialization.process(record) + + # Send outgoing control flows + # Check that outgoing flows don't conflict with + # return updated token list + return [t for t in tokens if t not in inputs] + new_tokens + + def next_tokens(self, activity_node: paml.ActivityNodeExecution, ex: paml.ProtocolExecution): + protocol = ex.protocol.lookup() + out_edges = [e for e in protocol.edges if activity_node.node in e.source] + + if isinstance(activity_node.node.lookup(), uml.ForkNode): + [incoming_flow] = activity_node.incoming_flows + incoming_value = incoming_flow.lookup().value + value = incoming_value.value.lookup() \ + if isinstance(incoming_value, uml.LiteralReference)\ + else incoming_flow.lookup().value + edge_tokens = [paml.ActivityEdgeFlow(edge=edge, token_source=activity_node, + value=uml.LiteralReference(value=value)) + for edge in out_edges] + elif isinstance(activity_node.node.lookup(), uml.ActivityParameterNode): + [parameter_value] = [pv.value for pv in ex.parameter_values if pv.parameter == activity_node.node.lookup().parameter] + edge_tokens = [paml.ActivityEdgeFlow(edge=edge, token_source=activity_node, + value=uml.LiteralReference(value=parameter_value)) + for edge in out_edges] + else: + edge_tokens = [paml.ActivityEdgeFlow(edge=edge, token_source=activity_node, + value=self.get_value(activity_node, edge=edge)) + for edge in out_edges] + + # Save tokens in the protocol execution + ex.flows += edge_tokens + + # Assume that unlinked output pins are possible output parameters for the protocol + if isinstance(activity_node, paml.CallBehaviorExecution): + output_pins = activity_node.node.lookup().outputs + unlinked_output_pins = [p for p in output_pins if p not in {e.source.lookup() for e in out_edges}] + possible_output_parameter_values = [paml.ParameterValue(parameter=activity_node.node.lookup().pin_parameter(p.name), + value=self.get_value(activity_node)) + for p in unlinked_output_pins] + ex.parameter_values.extend(possible_output_parameter_values) + return edge_tokens + + def get_value(self, activity_node : paml.CallBehaviorExecution, edge: uml.ActivityEdge = None): + value = "" + if isinstance(edge, uml.ControlFlow): + value = "uml.ControlFlow" + elif isinstance(edge, uml.ObjectFlow): + parameter = activity_node.node.lookup().pin_parameter(edge.source.lookup().name).property_value + value = activity_node.compute_output(parameter) + + value = uml.literal(value) + + + return value + + def next_pin_tokens(self, activity_node: paml.ActivityNodeExecution, ex: paml.ProtocolExecution): + assert len(activity_node.incoming_flows) == 1 # One input per pin + incoming_flow = activity_node.incoming_flows[0].lookup() + refd_value = incoming_flow.value.value if isinstance(incoming_flow.value, uml.LiteralReference) else incoming_flow.value + pin_value = uml.LiteralReference(value=refd_value) + + tokens = [ paml.ActivityEdgeFlow(edge=None, token_source=activity_node, value=pin_value) ] + + # Save tokens in the protocol execution + ex.flows += tokens + return tokens + + + def execute_primitive(self, behavior: paml.Primitive, agent: sbol3.Agent, parameter_values: dict = {}, id: str = uuid.uuid4()) -> paml.BehaviorExecution: + pass + + +################################## +# Helper utility functions + +def sum_measures(measure_list): + """Add a list of measures and return a fresh measure + Note: requires that all have the same unit and types + + Parameters + ---------- + measure_list of SBOL Measure objects + + Returns + ------- + New Measure object with the sum of input measure amounts + """ + prototype = measure_list[0] + if not all(m.types == prototype.types and m.unit == prototype.unit for m in measure_list): + raise ValueError(f'Can only merge measures with identical units and types: {([m.value, m.unit, m.types] for m in measure_list)}') + total = sum(m.value for m in measure_list) + return sbol3.Measure(value=total, unit=prototype.unit, types=prototype.types) + + +def protocol_execution_aggregate_child_materials(self): + """Merge the consumed material from children, adding a fresh Material for each to this record. + + Parameters + ---------- + self: ProtocolExecution object + """ + child_materials = [e.call.consumed_material for e in self.executions + if isinstance(e, paml.CallBehaviorExecution) and + hasattr(e.call, "consumed_material")] + specifications = {m.specification for m in child_materials} + self.consumed_material = (paml.Material(s,sum_measures([m.amount for m in child_materials if m.specification==s])) + for s in specifications) +paml.ProtocolExecution.aggregate_child_materials = protocol_execution_aggregate_child_materials + + +def protocol_execution_to_dot(self): + """ + Create a dot graph that illustrates edge values appearing the execution of the protocol. + :param self: + :return: graphviz.Digraph + """ + dot = graphviz.Digraph(comment=self.protocol, + strict=True, + graph_attr={"rankdir": "TB", + "concentrate": "true"}, + node_attr={"ordering": "out"}) + def _make_object_edge(dot, incoming_flow, target, dest_parameter=None): + flow_source = incoming_flow.lookup().token_source.lookup() + source = incoming_flow.lookup().edge.lookup().source.lookup() + value = incoming_flow.lookup().value + if isinstance(value, uml.LiteralReference): + value = value.value.lookup() + else: + value = value.value + + if isinstance(source, uml.Pin): + src_parameter = source.get_parent().pin_parameter(source.name).property_value + src_var = src_parameter.name + else: + src_var = "" + + dest_var = dest_parameter.name if dest_parameter else "" + + source_id = source.dot_label(parent_identity=self.protocol) + if isinstance(source, uml.CallBehaviorAction): + source_id = f'{source_id}:node' + target_id = target.dot_label(parent_identity=self.protocol) + if isinstance(target, uml.CallBehaviorAction): + target_id = f'{target_id}:node' + + edge_label = f"{src_var}-[{value}]->{dest_var}" + attrs = {"color": "orange"} + dot.edge(source_id, target_id, edge_label, _attributes=attrs) + + + dot = graphviz.Digraph(name=f"cluster_{self.identity}", + graph_attr={ + "label": self.identity + }) + + # Protocol graph + protocol_graph = self.document.find(self.protocol).to_dot() + dot.subgraph(protocol_graph) + + + # Execution graph + for execution in self.executions: + exec_target = execution.node.lookup() + execution_label = "" + + # Make a self loop that includes the and start and end time. + if isinstance(execution, paml.CallBehaviorExecution): + execution_label += f"[{execution.call.lookup().start_time},\n {execution.call.lookup().end_time}]" + target_id = exec_target.dot_label(parent_identity=self.protocol) + target_id = f'{target_id}:node' + dot.edge(target_id, target_id, _attributes={"tailport": "w", + "headport": "w", + "taillabel": execution_label, + "color": "invis"}) + + for incoming_flow in execution.incoming_flows: + # Executable Nodes have incoming flow from their input pins and ControlFlows + flow_source = incoming_flow.lookup().token_source.lookup() + exec_source = flow_source.node.lookup() + edge_ref = incoming_flow.lookup().edge + if edge_ref and isinstance(edge_ref.lookup(), uml.ObjectFlow): + if isinstance(exec_target, uml.ActivityParameterNode): + # ActivityParameterNodes are ObjectNodes that have a parameter + _make_object_edge(dot, incoming_flow, exec_target, dest_parameter=exec_target.parameter.lookup()) + elif isinstance(exec_target, uml.ControlNode): + # This in an object flow into the node itself, which happens for ControlNodes + _make_object_edge(dot, incoming_flow, exec_target) + elif isinstance(exec_source, uml.Pin): + # This incoming_flow is from an input pin, and need the flow into the pin + into_pin_flow = flow_source.incoming_flows[0] + #source = src_to_pin_edge.source.lookup() + #target = src_to_pin_edge.target.lookup() + dest_parameter = exec_target.pin_parameter(exec_source.name).property_value + _make_object_edge(dot, into_pin_flow, into_pin_flow.lookup().edge.lookup().target.lookup(), dest_parameter=dest_parameter) + + return dot +paml.ProtocolExecution.to_dot = protocol_execution_to_dot + + +def call_behavior_execution_compute_output(self, parameter): + """ + Get parameter value from call behavior execution + :param self: + :param parameter: output parameter to define value + :return: value + """ + primitive = self.node.lookup().behavior.lookup() + call = self.call.lookup() + inputs = [x for x in call.parameter_values if x.parameter.lookup().property_value.direction == uml.PARAMETER_IN] + value = primitive.compute_output(inputs, parameter) + return value +paml.CallBehaviorExecution.compute_output = call_behavior_execution_compute_output + +def primitive_compute_output(self, inputs, parameter): + """ + Compute the value for parameter given the inputs. This default function will be overridden for specific primitives. + :param self: + :param inputs: list of paml.ParameterValue + :param parameter: Parameter needing value + :return: value + """ + if self.identity == 'https://bioprotocols.org/paml/primitives/sample_arrays/EmptyContainer' and \ + parameter.name == "samples" and \ + parameter.type == 'http://bioprotocols.org/paml#SampleArray': + # Make a SampleArray + for input in inputs: + i_parameter = input.parameter.lookup().property_value + value = input.value + if i_parameter.name == "specification": + spec = value + contents = "" + name = f"{parameter.name}" + sample_array = paml.SampleArray(name=name, + container_type=spec.value, + contents=contents) + return sample_array + elif self.identity == 'https://bioprotocols.org/paml/primitives/sample_arrays/PlateCoordinates' and \ + parameter.name == "samples" and \ + parameter.type == 'http://bioprotocols.org/paml#SampleCollection': + for input in inputs: + i_parameter = input.parameter.lookup().property_value + value = input.value + if i_parameter.name == "source": + source = value + elif i_parameter.name == "coordinates": + coordinates = value.value.lookup().value if isinstance(value, uml.LiteralReference) else value.value + + mask = paml.SampleMask(source=source, + mask=coordinates) + return mask + else: + return f"{parameter.name}" +paml.Primitive.compute_output = primitive_compute_output diff --git a/paml/lib/__init__.py b/paml/lib/__init__.py new file mode 100644 index 0000000..c50817e --- /dev/null +++ b/paml/lib/__init__.py @@ -0,0 +1 @@ +#from .library_type_inference import primitive_type_inference_functions diff --git a/paml/lib/autoprotocol_mappings.md b/paml/lib/autoprotocol_mappings.md new file mode 100644 index 0000000..d94bd46 --- /dev/null +++ b/paml/lib/autoprotocol_mappings.md @@ -0,0 +1,49 @@ +# Mapping from PAML libraries to Autoprotocol instructions + +## liquid_handling + +| autoprotocol | PAML | +| :--- | :--- | +| `provision` | `Provision` | +| `liquid_handle`, `mode=dispense` | `Dispense` | +| `liquid_handle` up one place, down another | `Transfer` | +| `liquid_handle` with a non-empty destination | `TransferInto` | +| `liquid_handle` up & down in one source | `PipetteMix` | + +Note: this assumes the internal Strateos operation `DISPENSE` is equivalent to `liquid_handle`, `mode=dispense` + +## spectrophotometry +This library handles plate readers and other spectrophotometers + +| autoprotocol | PAML | +| :--- | :--- | +| `spectrophotometry`, `mode=absorbance` | `MeasureAbsorbance` | +| `spectrophotometry`, `mode=fluorescence` | `MeasureFluorescence` | +| `spectrophotometry`, new mode |`MeasureFluorescenceSpectrum` | + +## plate_handling +| autoprotocol | PAML | +| :--- | :--- | +| `cover` | `Cover` | +| `incubate` | `Incubate` | +| `seal`,flexible mode | `Seal` | +| `seal`,`mode=thermal` | `AdhesiveSeal` | +| `seal`,`mode=adhesive` | `ThermalSeal` | +| `spin` | `Spin` | +| `uncover` | `Uncover` | +| `unseal` | `Unseal` | + +## Currently unmapped: + +- `acoustic_transfer` +- `flow_cytometry` +- `measure_mass` +- `measure_volume` +- `spectrophotometry`, `mode=luminescence` +- `spectrophotometry`, `mode=shake` + +# Operators at strateos not listed in autoprotocol +- `SUPPLY NEW CONTAINER` +- `DISPOSE CONTAINER` + +These can likely be handled implicitly based on container use and garbage collection. diff --git a/paml/lib/library_type_inference.py b/paml/lib/library_type_inference.py new file mode 100644 index 0000000..03525ce --- /dev/null +++ b/paml/lib/library_type_inference.py @@ -0,0 +1,112 @@ +# import sbol3 +# import paml +# import paml.type_inference +# +# +# # Pre-declare the ProtocolTyping class to avoid circularity with paml.type_inference +# class ProtocolTyping: +# pass +# +# primitive_type_inference_functions = {} # dictionary of identity : function for typing primitives +# +# +# # When there is no output, we don't need to do any inference +# def no_output_primitive_infer_typing(_, __: ProtocolTyping): +# pass +# +# +# ############################################# +# # Liquid handling primitives +# +# LIQUID_HANDLING_PREFIX = 'https://bioprotocols.org/paml/primitives/liquid_handling/' +# +# # TODO: add amount information into sample records +# +# +# def liquid_handling_provision_infer_typing(executable, typing: ProtocolTyping): +# resource = executable.input_pin('resource').input_type(typing) +# location = executable.input_pin('destination').input_type(typing) +# samples = paml.ReplicateSamples(specification=resource) +# samples.in_location.append(location) +# executable.output_pin('samples').assert_output_type(typing, samples) +# primitive_type_inference_functions[LIQUID_HANDLING_PREFIX+'Provision'] = liquid_handling_provision_infer_typing +# +# +# def liquid_handling_dispense_infer_typing(executable, typing: ProtocolTyping): +# source = executable.input_pin('source').input_type(typing) # Assumed singular replicate +# assert isinstance(source, paml.ReplicateSamples), ValueError('Dispense must come from a homogeneous source, but found '+str(source)) +# location = executable.input_pin('destination').input_type(typing).lookup() +# samples = paml.ReplicateSamples(specification=source.specification) # TODO: Fix the kludge here +# samples.in_location.append(location) +# executable.output_pin('samples').assert_output_type(typing, samples) +# primitive_type_inference_functions[LIQUID_HANDLING_PREFIX+'Dispense'] = liquid_handling_dispense_infer_typing +# +# +# def liquid_handling_transfer_infer_typing(executable, typing: ProtocolTyping): +# source = executable.input_pin('source').input_type(typing) +# destination = executable.input_pin('destination').input_type(typing) +# if isinstance(source, paml.ReplicateSamples): +# relocated = paml.ReplicateSamples(specification=source.specification) +# relocated.in_location.append(destination) +# elif isinstance(source, paml.LocatedSamples): +# relocated = paml.HeterogeneousSamples() +# kludge = paml.ReplicateSamples() # TODO: put something real here instead +# kludge.in_location.append(destination) +# relocated.replicate_samples.append(kludge) +# else: +# raise ValueError("Don't know how to infer type for Transfer with source of type "+str(type(source))) +# executable.output_pin('samples').assert_output_type(typing, relocated) +# primitive_type_inference_functions[LIQUID_HANDLING_PREFIX + 'Transfer'] = liquid_handling_transfer_infer_typing +# +# +# def liquid_handling_transferinto_infer_typing(executable, typing: ProtocolTyping): +# source = executable.input_pin('source').input_type(typing) +# destination = executable.input_pin('destination').input_type(typing) +# if isinstance(source, paml.ReplicateSamples) and isinstance(destination, paml.ReplicateSamples): +# contents = sbol3.Component(executable.display_id+'_contents', sbol3.SBO_FUNCTIONAL_ENTITY) # generic mixture +# mixture = paml.ReplicateSamples(specification=contents) +# mixture.in_location.append(destination) +# elif isinstance(source, paml.LocatedSamples) and isinstance(destination, paml.LocatedSamples): +# mixture = paml.HeterogeneousSamples() +# kludge = paml.ReplicateSamples() # TODO: put something real here instead +# kludge_loc = (destination.in_location if isinstance(destination, paml.ReplicateSamples) else destination.replicate_samples[0].in_location) +# kludge.in_location.append(kludge_loc[0]) +# mixture.replicate_samples.append(kludge) +# else: +# raise ValueError("Don't know how to infer type for TransferInto "+executable.identity+" with source and destination types "+str(type(source))+', '+str(type(destination))) +# executable.output_pin('samples').assert_output_type(typing, mixture) +# primitive_type_inference_functions[LIQUID_HANDLING_PREFIX + 'TransferInto'] = liquid_handling_transferinto_infer_typing +# +# +# primitive_type_inference_functions[LIQUID_HANDLING_PREFIX+'PipetteMix'] = no_output_primitive_infer_typing +# +# ############################################# +# # Plate handling primitives +# +# PLATE_HANDLING_PREFIX = 'https://bioprotocols.org/paml/primitives/plate_handling/' +# +# +# primitive_type_inference_functions[PLATE_HANDLING_PREFIX+'Cover'] = no_output_primitive_infer_typing +# primitive_type_inference_functions[PLATE_HANDLING_PREFIX+'Seal'] = no_output_primitive_infer_typing +# primitive_type_inference_functions[PLATE_HANDLING_PREFIX+'AdhesiveSeal'] = no_output_primitive_infer_typing +# primitive_type_inference_functions[PLATE_HANDLING_PREFIX+'ThermalSeal'] = no_output_primitive_infer_typing +# primitive_type_inference_functions[PLATE_HANDLING_PREFIX+'Uncover'] = no_output_primitive_infer_typing +# primitive_type_inference_functions[PLATE_HANDLING_PREFIX+'Unseal'] = no_output_primitive_infer_typing +# primitive_type_inference_functions[PLATE_HANDLING_PREFIX+'Incubate'] = no_output_primitive_infer_typing +# +# +# ############################################# +# # Spectrophotometry primitives +# +# SPECTROPHOTOMETRY = 'https://bioprotocols.org/paml/primitives/spectrophotometry/' +# +# +# def spectrophotometry_infer_typing(executable, typing: ProtocolTyping): +# samples = executable.input_pin('samples').input_type(typing) +# # TODO: figure out how to add appropriate metadata onto these +# data = paml.LocatedData() +# data.from_samples = samples +# executable.output_pin('measurements').assert_output_type(typing, data) +# primitive_type_inference_functions[SPECTROPHOTOMETRY+'MeasureAbsorbance'] = spectrophotometry_infer_typing +# primitive_type_inference_functions[SPECTROPHOTOMETRY+'MeasureFluorescence'] = spectrophotometry_infer_typing +# primitive_type_inference_functions[SPECTROPHOTOMETRY+'MeasureFluorescenceSpectrum'] = spectrophotometry_infer_typing diff --git a/paml/lib/liquid_handling.py b/paml/lib/liquid_handling.py new file mode 100644 index 0000000..e729592 --- /dev/null +++ b/paml/lib/liquid_handling.py @@ -0,0 +1,71 @@ +import sbol3 +import paml + +############################################# +# Set up the document +doc = sbol3.Document() +LIBRARY_NAME = 'liquid_handling' +sbol3.set_namespace('https://bioprotocols.org/paml/primitives/'+LIBRARY_NAME) + +############################################# +# Create the primitives +print('Making primitives for '+LIBRARY_NAME) + +p = paml.Primitive('Provision') +p.description = 'Place a measured amount (mass or volume) of a specified component into a location, where it may then be used in executing the protocol.' +p.add_input('resource', sbol3.SBOL_COMPONENT) +p.add_input('destination', 'http://bioprotocols.org/paml#SampleCollection') # TODO: change these URIs to constants on resolution of pySBOL issue #228 +p.add_input('amount', sbol3.OM_MEASURE) # Can be mass or volume +p.add_input('dispenseVelocity', sbol3.OM_MEASURE, True) +doc.add(p) + +p = paml.Primitive('Dispense') +p.description = 'Move a measured volume of liquid from one source sample to create samples at multiple destination locations' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('destination', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('amount', sbol3.OM_MEASURE) # Must be volume +p.add_input('dispenseVelocity', sbol3.OM_MEASURE, True) +doc.add(p) + +p = paml.Primitive('Transfer') +p.description = 'Move a measured volume taken from a collection of source samples to a location whose shape can contain them in a destination locations' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('destination', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('amount', sbol3.OM_MEASURE) # Must be volume +p.add_input('dispenseVelocity', sbol3.OM_MEASURE, True) +doc.add(p) + +p = paml.Primitive('TransferInto') +p.description = 'Mix a measured volume taken from an collection of source samples into a collection of destination samples whose shape can contain them' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('destination', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('amount', sbol3.OM_MEASURE) # Must be volume +p.add_input('mixCycles', sbol3.OM_MEASURE, True) +p.add_input('dispenseVelocity', sbol3.OM_MEASURE, True) +doc.add(p) + +p = paml.Primitive('TransferByMap') +p.description = 'Move volumes from a collection of source samples to a collection of destination samples following a plan of value given for each location' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('destination', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('plan', 'http://bioprotocols.org/paml#SampleData') # Must be a set of component/volume values +p.add_input('dispenseVelocity', sbol3.OM_MEASURE, True) +doc.add(p) + +p = paml.Primitive('PipetteMix') +p.description = 'Mix by cycling a measured volume of liquid in and out at an array of samples a fixed number of times' +p.add_input('samples', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('amount', sbol3.OM_MEASURE) # Must be volume +p.add_input('dispenseVelocity', sbol3.OM_MEASURE, True) +p.add_input('cycleCount', sbol3.OM_MEASURE, True) +doc.add(p) + +print('Library construction complete') + +print('Validating library') +for e in doc.validate().errors: print(e); +for w in doc.validate().warnings: print(w); + +filename = LIBRARY_NAME+'.ttl' +doc.write(filename,'turtle') +print('Library written as '+filename) diff --git a/paml/lib/liquid_handling.ttl b/paml/lib/liquid_handling.ttl new file mode 100644 index 0000000..e8ef674 --- /dev/null +++ b/paml/lib/liquid_handling.ttl @@ -0,0 +1,741 @@ +@prefix ns1: . +@prefix om: . +@prefix sbol: . +@prefix xsd: . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + ; + sbol:description "Move a measured volume of liquid from one source sample to create samples at multiple destination locations" ; + sbol:displayId "Dispense" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + ; + sbol:description "Mix by cycling a measured volume of liquid in and out at an array of samples a fixed number of times" ; + sbol:displayId "PipetteMix" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + ; + sbol:description "Place a measured amount (mass or volume) of a specified component into a location, where it may then be used in executing the protocol." ; + sbol:displayId "Provision" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + ; + sbol:description "Move a measured volume taken from a collection of source samples to a location whose shape can contain them in a destination locations" ; + sbol:displayId "Transfer" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + ; + sbol:description "Move volumes from a collection of source samples to a collection of destination samples following a plan of value given for each location" ; + sbol:displayId "TransferByMap" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + , + ; + sbol:description "Mix a measured volume taken from an collection of source samples into a collection of destination samples whose shape can contain them" ; + sbol:displayId "TransferInto" ; + sbol:hasNamespace . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "destination" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "amount" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "dispenseVelocity" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "amount" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "dispenseVelocity" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "cycleCount" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type sbol:Component ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "resource" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "destination" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "amount" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "dispenseVelocity" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "destination" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "amount" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "dispenseVelocity" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "destination" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "plan" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "dispenseVelocity" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "destination" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "amount" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "mixCycles" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 4 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue5" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "dispenseVelocity" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + diff --git a/paml/lib/plate_handling.py b/paml/lib/plate_handling.py new file mode 100644 index 0000000..e2fcca9 --- /dev/null +++ b/paml/lib/plate_handling.py @@ -0,0 +1,74 @@ +import sbol3 +import paml + +############################################# +# Set up the document +doc = sbol3.Document() +LIBRARY_NAME = 'plate_handling' +sbol3.set_namespace('https://bioprotocols.org/paml/primitives/'+LIBRARY_NAME) + + +############################################# +# Create the primitives +print('Making primitives for '+LIBRARY_NAME) + +# Note: plate handling primitives operate on whole arrays only, not just fragments +p = paml.Primitive('Cover') +p.description = 'Cover a set of samples to keep materials from entering or exiting' +p.add_input('location', 'http://bioprotocols.org/paml#SampleArray') +p.add_input('type', 'http://www.w3.org/2001/XMLSchema#anyURI') +doc.add(p) + +p = paml.Primitive('Seal') +p.description = 'Seal a collection of samples fixing the seal using a user-selected method, in order to guarantee isolation from the external environment' +p.add_input('location', 'http://bioprotocols.org/paml#SampleArray') +p.add_input('type', 'http://www.w3.org/2001/XMLSchema#anyURI') # e.g., breathable vs. non-breathable +doc.add(p) + +p = paml.Primitive('AdhesiveSeal') +p.description = 'Seal a collection of samples using adhesive to fix the seal, in order to guarantee isolation from the external environment' +p.add_input('location', 'http://bioprotocols.org/paml#SampleArray') +p.add_input('type', 'http://www.w3.org/2001/XMLSchema#anyURI') # e.g., breathable vs. non-breathable +doc.add(p) + +p = paml.Primitive('ThermalSeal') +p.description = 'Seal a collection of samples using heat to fix the seal, in order to guarantee isolation from the external environment' +p.add_input('location', 'http://bioprotocols.org/paml#SampleArray') +p.add_input('type', 'http://www.w3.org/2001/XMLSchema#anyURI') # e.g., breathable vs. non-breathable +p.add_input('temperature', sbol3.OM_MEASURE) +p.add_input('duration', sbol3.OM_MEASURE) # length of time to apply the sealing temperature in order to get the seal in place +doc.add(p) + +p = paml.Primitive('Uncover') +p.description = 'Uncover a collection of samples to allow materials to enter or exit' +p.add_input('location', 'http://bioprotocols.org/paml#SampleArray') +doc.add(p) + +p = paml.Primitive('Unseal') +p.description = 'Unseal a sealed collection of samples to break their isolation from the external environment' +p.add_input('location', 'http://bioprotocols.org/paml#SampleArray') +doc.add(p) + +p = paml.Primitive('Incubate') +p.description = 'Incubate a set of samples under specified conditions for a fixed period of time' +p.add_input('location', 'http://bioprotocols.org/paml#SampleArray') +p.add_input('duration', sbol3.OM_MEASURE) # time +p.add_input('temperature', sbol3.OM_MEASURE) # temperature +p.add_input('shakingFrequency', sbol3.OM_MEASURE, True) # Hertz or RPM?; in either case, defaults to zero +doc.add(p) + +p = paml.Primitive('Spin') +p.description = 'Centrifuge a set of samples at a given acceleration for a given period of time' +p.add_input('location', 'http://bioprotocols.org/paml#SampleArray') +p.add_input('duration', sbol3.OM_MEASURE) # time +p.add_input('acceleration', sbol3.OM_MEASURE) # acceleration +doc.add(p) + +print('Library construction complete') +print('Validating library') +for e in doc.validate().errors: print(e); +for w in doc.validate().warnings: print(w); + +filename = LIBRARY_NAME+'.ttl' +doc.write(filename,'turtle') +print('Library written as '+filename) diff --git a/paml/lib/plate_handling.ttl b/paml/lib/plate_handling.ttl new file mode 100644 index 0000000..814b7f6 --- /dev/null +++ b/paml/lib/plate_handling.ttl @@ -0,0 +1,585 @@ +@prefix ns1: . +@prefix om: . +@prefix sbol: . +@prefix xsd: . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + ; + sbol:description "Seal a collection of samples using adhesive to fix the seal, in order to guarantee isolation from the external environment" ; + sbol:displayId "AdhesiveSeal" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + ; + sbol:description "Cover a set of samples to keep materials from entering or exiting" ; + sbol:displayId "Cover" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + ; + sbol:description "Incubate a set of samples under specified conditions for a fixed period of time" ; + sbol:displayId "Incubate" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + ; + sbol:description "Seal a collection of samples fixing the seal using a user-selected method, in order to guarantee isolation from the external environment" ; + sbol:displayId "Seal" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + ; + sbol:description "Centrifuge a set of samples at a given acceleration for a given period of time" ; + sbol:displayId "Spin" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + ; + sbol:description "Seal a collection of samples using heat to fix the seal, in order to guarantee isolation from the external environment" ; + sbol:displayId "ThermalSeal" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter ; + sbol:description "Uncover a collection of samples to allow materials to enter or exit" ; + sbol:displayId "Uncover" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter ; + sbol:description "Unseal a sealed collection of samples to break their isolation from the external environment" ; + sbol:displayId "Unseal" ; + sbol:hasNamespace . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "location" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:anyURI ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "type" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "location" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:anyURI ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "type" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "location" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "duration" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "temperature" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "shakingFrequency" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "location" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:anyURI ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "type" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "location" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "duration" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "acceleration" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "location" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:anyURI ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "type" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "temperature" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "duration" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "location" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "location" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + diff --git a/paml/lib/rebuild_library.py b/paml/lib/rebuild_library.py new file mode 100644 index 0000000..3bfc6f3 --- /dev/null +++ b/paml/lib/rebuild_library.py @@ -0,0 +1,12 @@ +import glob +import os + +############# +# Run this file in order to rebuild the library when you change something in it + +os.chdir(os.path.dirname(os.path.realpath(__file__))) + +for file in glob.glob('*.py'): + if os.path.realpath(file) == os.path.realpath(__file__): + continue # don't rerun this file and start an infinite loop + exec(open(file).read()) diff --git a/paml/lib/sample_arrays.py b/paml/lib/sample_arrays.py new file mode 100644 index 0000000..38836df --- /dev/null +++ b/paml/lib/sample_arrays.py @@ -0,0 +1,63 @@ +import sbol3 +import paml + +############################################# +# Set up the document +doc = sbol3.Document() +LIBRARY_NAME = 'sample_arrays' +sbol3.set_namespace('https://bioprotocols.org/paml/primitives/'+LIBRARY_NAME) + + +############################################# +# Create the primitives +print('Making primitives for '+LIBRARY_NAME) + +p = paml.Primitive('EmptyContainer') +p.description = 'Allocate a sample array with size and type based on an empty container' +p.add_input('specification', sbol3.SBOL_IDENTIFIED) +p.add_output('samples', 'http://bioprotocols.org/paml#SampleArray') +doc.add(p) + +p = paml.Primitive('PlateCoordinates') +p.description = 'Select only the samples with specified row/column combination from a sample collection' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('coordinates', 'http://bioprotocols.org/uml#ValueSpecification') +p.add_output('samples', 'http://bioprotocols.org/paml#SampleCollection') +doc.add(p) + +p = paml.Primitive('Rows') +p.description = 'Select only the samples with specified rows from a sample collection' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('row', 'http://bioprotocols.org/uml#ValueSpecification') +p.add_output('samples', 'http://bioprotocols.org/paml#SampleCollection') +doc.add(p) + +p = paml.Primitive('Columns') +p.description = 'Select only the samples with specified columns from a sample collection' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('col', 'http://bioprotocols.org/uml#ValueSpecification') +p.add_output('samples', 'http://bioprotocols.org/paml#SampleCollection') +doc.add(p) + +p = paml.Primitive('ReplicateCollection') +p.description = 'Create a new sample collection containing a set of replicate slots for every sample in the input' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('replicates', 'http://bioprotocols.org/uml#ValueSpecification') +p.add_output('samples', 'http://bioprotocols.org/paml#SampleCollection') +doc.add(p) + +p = paml.Primitive('DuplicateCollection') +p.description = 'Create a new sample collection with identical parameters to the input collection' +p.add_input('source', 'http://bioprotocols.org/paml#SampleCollection') +p.add_output('samples', 'http://bioprotocols.org/paml#SampleCollection') +doc.add(p) + +print('Library construction complete') + +print('Validating library') +for e in doc.validate().errors: print(e); +for w in doc.validate().warnings: print(w); + +filename = LIBRARY_NAME+'.ttl' +doc.write(filename,'turtle') +print('Library written as '+filename) diff --git a/paml/lib/sample_arrays.ttl b/paml/lib/sample_arrays.ttl new file mode 100644 index 0000000..698c0f3 --- /dev/null +++ b/paml/lib/sample_arrays.ttl @@ -0,0 +1,488 @@ +@prefix ns1: . +@prefix sbol: . +@prefix xsd: . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + ; + sbol:description "Select only the samples with specified columns from a sample collection" ; + sbol:displayId "Columns" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + ; + sbol:description "Create a new sample collection with identical parameters to the input collection" ; + sbol:displayId "DuplicateCollection" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + ; + sbol:description "Allocate a sample array with size and type based on an empty container" ; + sbol:displayId "EmptyContainer" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + ; + sbol:description "Select only the samples with specified row/column combination from a sample collection" ; + sbol:displayId "PlateCoordinates" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + ; + sbol:description "Create a new sample collection containing a set of replicate slots for every sample in the input" ; + sbol:displayId "ReplicateCollection" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + ; + sbol:description "Select only the samples with specified rows from a sample collection" ; + sbol:displayId "Rows" ; + sbol:hasNamespace . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ns1:ValueSpecification ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "col" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type sbol:Identified ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "specification" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ns1:ValueSpecification ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "coordinates" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ns1:ValueSpecification ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "replicates" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "source" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ns1:ValueSpecification ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "row" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + diff --git a/paml/lib/spectrophotometry.py b/paml/lib/spectrophotometry.py new file mode 100644 index 0000000..94b00be --- /dev/null +++ b/paml/lib/spectrophotometry.py @@ -0,0 +1,53 @@ +import sbol3 +import paml + +############################################# +# Set up the document +doc = sbol3.Document() +LIBRARY_NAME = 'spectrophotometry' +sbol3.set_namespace('https://bioprotocols.org/paml/primitives/'+LIBRARY_NAME) + + +############################################# +# Create the primitives +print('Making primitives for '+LIBRARY_NAME) + +p = paml.Primitive('MeasureAbsorbance') +p.description = 'Measure absorbance at a given wavelength from a set of samples' +p.add_input('samples', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('wavelength', sbol3.OM_MEASURE) +p.add_input('numFlashes', 'http://www.w3.org/2001/XMLSchema#integer', True) +p.add_output('measurements', 'http://bioprotocols.org/paml#SampleData') +doc.add(p) + +p = paml.Primitive('MeasureFluorescence') +p.description = 'Measure fluorescence intensity from a set of samples stimulated by a given wavelength, with an optional bandpass or lowpass filter' +p.add_input('samples', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('excitationWavelength', sbol3.OM_MEASURE) +p.add_input('emissionBandpassWavelength', sbol3.OM_MEASURE, True) +p.add_input('emissionBandpassWidth', sbol3.OM_MEASURE, True) # measured in total range, e.g., 450nm wavelength, 50nm width = 425nm - 475nm +p.add_input('emissionLowpassCutoff', sbol3.OM_MEASURE, True) # e.g., 750LP +p.add_input('numFlashes', 'http://www.w3.org/2001/XMLSchema#integer', True) +p.add_input('gain', 'http://www.w3.org/2001/XMLSchema#double', True) +p.add_output('measurements', 'http://bioprotocols.org/paml#SampleData') +doc.add(p) + +p = paml.Primitive('MeasureFluorescenceSpectrum') +p.description = 'Measure fluorescence spectrum from a set of samples stimulated by a given wavelength' +p.add_input('samples', 'http://bioprotocols.org/paml#SampleCollection') +p.add_input('excitationWavelength', sbol3.OM_MEASURE) +p.add_input('numFlashes', 'http://www.w3.org/2001/XMLSchema#integer', True) +p.add_input('gain', 'http://www.w3.org/2001/XMLSchema#double', True) +p.add_output('measurements', 'http://bioprotocols.org/paml#SampleData') +doc.add(p) + +# Consider adding Measure[Color]Fluorescence as SubProtocols that hardwire standard wavelengths + +print('Library construction complete') +print('Validating library') +for e in doc.validate().errors: print(e); +for w in doc.validate().warnings: print(w); + +filename = LIBRARY_NAME+'.ttl' +doc.write(filename,'turtle') +print('Library written as '+filename) diff --git a/paml/lib/spectrophotometry.ttl b/paml/lib/spectrophotometry.ttl new file mode 100644 index 0000000..633339e --- /dev/null +++ b/paml/lib/spectrophotometry.ttl @@ -0,0 +1,499 @@ +@prefix ns1: . +@prefix om: . +@prefix sbol: . +@prefix xsd: . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + ; + sbol:description "Measure absorbance at a given wavelength from a set of samples" ; + sbol:displayId "MeasureAbsorbance" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + , + , + , + , + ; + sbol:description "Measure fluorescence intensity from a set of samples stimulated by a given wavelength, with an optional bandpass or lowpass filter" ; + sbol:displayId "MeasureFluorescence" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter , + , + , + , + ; + sbol:description "Measure fluorescence spectrum from a set of samples stimulated by a given wavelength" ; + sbol:displayId "MeasureFluorescenceSpectrum" ; + sbol:hasNamespace . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "wavelength" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:integer ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "numFlashes" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "measurements" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "excitationWavelength" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "emissionBandpassWavelength" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "emissionBandpassWidth" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 4 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue5" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "emissionLowpassCutoff" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 5 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue6" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:integer ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "numFlashes" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 6 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue7" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:double ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "gain" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 7 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue8" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "measurements" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "samples" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 1 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue2" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "excitationWavelength" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 2 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue3" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:integer ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "numFlashes" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 3 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue4" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:double ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "gain" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 0 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 4 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue5" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:out ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "measurements" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + diff --git a/paml/lib/wait.py b/paml/lib/wait.py new file mode 100644 index 0000000..7941979 --- /dev/null +++ b/paml/lib/wait.py @@ -0,0 +1,34 @@ +import sbol3 +import paml + +############################################# +# Set up the document +doc = sbol3.Document() +LIBRARY_NAME = 'wait' +sbol3.set_namespace('https://bioprotocols.org/paml/primitives/'+LIBRARY_NAME) + +############################################# +# Create the primitives +print('Making primitives for '+LIBRARY_NAME) + +p = paml.Primitive('WaitForTime') +p.description = 'Waits for a set amount of time.' +p.add_input('amount', sbol3.OM_MEASURE) +doc.add(p) + +p = paml.Primitive('WaitForTrue') +p.description = 'Waits for an expression to be true.' +p.add_input('expression', 'http://www.w3.org/2001/XMLSchema#boolean') +doc.add(p) + + + +print('Library construction complete') + +print('Validating library') +for e in doc.validate().errors: print(e); +for w in doc.validate().warnings: print(w); + +filename = LIBRARY_NAME+'.ttl' +doc.write(filename,'turtle') +print('Library written as '+filename) diff --git a/paml/lib/wait.ttl b/paml/lib/wait.ttl new file mode 100644 index 0000000..cb5fd29 --- /dev/null +++ b/paml/lib/wait.ttl @@ -0,0 +1,73 @@ +@prefix ns1: . +@prefix om: . +@prefix sbol: . +@prefix xsd: . + + a , + sbol:TopLevel ; + ns1:ownedParameter ; + sbol:description "Waits for a set amount of time." ; + sbol:displayId "WaitForTime" ; + sbol:hasNamespace . + + a , + sbol:TopLevel ; + ns1:ownedParameter ; + sbol:description "Waits for an expression to be true." ; + sbol:displayId "WaitForTrue" ; + sbol:hasNamespace . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type om:Measure ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "amount" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + + a ns1:OrderedPropertyValue, + sbol:Identified ; + ns1:indexValue 0 ; + ns1:propertyValue ; + sbol:displayId "OrderedPropertyValue1" . + + a ns1:Parameter, + sbol:Identified ; + ns1:direction ns1:in ; + ns1:isOrdered true ; + ns1:isUnique true ; + ns1:lowerValue ; + ns1:type xsd:boolean ; + ns1:upperValue ; + sbol:displayId "Parameter1" ; + sbol:name "expression" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger1" . + + a ns1:LiteralInteger, + sbol:Identified ; + ns1:integerValue 1 ; + sbol:displayId "LiteralInteger2" . + diff --git a/paml/paml.ttl b/paml/paml.ttl new file mode 100644 index 0000000..5100041 --- /dev/null +++ b/paml/paml.ttl @@ -0,0 +1,498 @@ +@prefix : . +@prefix om: . +@prefix owl: . +@prefix rdf: . +@prefix xsd: . +@prefix paml: . +@prefix prov: . +@prefix rdfs: . +@prefix sbol: . +@prefix uml: . +@prefix MathM: . +@base . + + rdf:type owl:Ontology ; + owl:imports , + , , + ; + rdfs:comment "Protocol Activity Modeling Languge (PAML) ontology." ; + owl:versionInfo "0.3" . + +################################################################# +# Annotation properties +################################################################# + +### http://www.w3.org/2002/07/owl#maxCardinality +owl:maxCardinality rdf:type owl:AnnotationProperty . +owl:minCardinality rdf:type owl:AnnotationProperty . + + +################################################################# +# Datatypes +################################################################# + +### http://www.w3.org/2001/XMLSchema#anySimpleType +xsd:anySimpleType rdf:type rdfs:Datatype . + + +################################################################# +# Protocol definitions, based on UML +################################################################# + +paml:Protocol rdf:type owl:Class ; + rdfs:comment '''A Protocol describes how to carry out some form of laboratory or research process. + For example, a Protocol could describe DNA miniprep, Golden-Gate assembly, a cell culture experiment. + At present this class adds no additional information over uml:Activity, but may in the future.''' ; + rdfs:subClassOf uml:Activity . + + +paml:Primitive rdf:type owl:Class ; + rdfs:comment '''A Primitive describes a library function that acts as a basic ``building block'' for a Protocol. + For example, a Primitive could describe pipetting, measuring absorbance in a plate reader, or centrifuging. + At present this class adds no additional information over uml:Behavior, but may in the future.''' ; + rdfs:subClassOf uml:Behavior . + + +################################################################# +# Execution record, bootstrapping from PROV-O +################################################################# + +paml:BehaviorExecution rdf:type owl:Class ; + rdfs:comment '''A BehaviorExecution is a record of how a Protocol, Primitive, or other uml:Behavior was carried out. + The execution of the behavior could be either real or simulated. + + In specifying a BehaviorExecution, the prov:type field inherited from prov:Activity is used to indicate the + uml:Behavior whose execution is being recorded. Precisely one value of prov:type MUST be a URI for a uml:Behavior. + The prov:startedAtTime and prov:endedAtTime fields SHOULD be used to record timing information as this becomes + available. + Finally, the entity carrying out the execution SHOULD be recorded as a prov:Agent indicated using a + prov:Association. + + Note that a BehaviorExecution can be used to record both the state of an in-progress execution as well as an + execution that has completed. As a BehaviorExecution proceeds, all values of its properties are monotonic, + i.e., they are only added to and never changed. + + TODO: need to changing completedNormally to allow indication of an in-progress BehaviorExecution + TODO: Is there a good ontology for agent roles in association?''' ; + rdfs:subClassOf prov:Activity , # note: prov:Activity here refers to the adapted version in SBOL + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:ParameterValue ; owl:onProperty paml:parameterValuePair ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom xsd:boolean ; owl:onProperty paml:completedNormally ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger] , + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:Material ; owl:onProperty paml:consumedMaterial ] . + +paml:parameterValuePair rdf:type owl:ObjectProperty ; + rdfs:comment '''The parameterValuePair property is used to record the value that was associated with each + uml:Parameter for the uml:Behavior when it was executed, by means of a ParameterValue object. + Any uml:Parameter that is not listed is assumed to have had no value assigned. Conversely, every non-optional + uml:Parameter for the uml:Behavior MUST have an associated parameter value. + Finally, note that this applies both to input uml:Parameter objects, whose value is set before execution begins, + and to output uml:Parameter objects, whose value is set by the time execution ends. + + TODO: are multiple values allowed, or do those need to be passed as list/set types? + ''' ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:BehaviorExecution ; + rdfs:range paml:ParameterValue ; + rdfs:label "parameter_values" . + +paml:completedNormally rdf:type owl:DatatypeProperty ; + rdfs:comment '''This boolean should be set to true if the Behavior completed normally and false if there + was some exception condition. At present, no further information is being encoded about exceptions, but this + is an extension that is anticipated for the future.''' ; + rdfs:domain paml:BehaviorExecution ; + rdfs:range xsd:boolean ; + rdfs:label "completed_normally" . + +paml:consumedMaterial rdf:type owl:ObjectProperty ; + rdfs:comment '''This property is used to record the noteworthy consumables used during the execution of the + Behavior. For example, a cell culture protocols will consume various reagents and samples of cells. Materials + with the same specification SHOULD be consolidated, such that the list of materials SHOULD NOT contain two + materials with the same specification. + + For example, consuming 5.0 mL of PBS and 2.0 mL of PBS should be recorded as consuming 7.0 mL of PBS. + Complex materials, however, MAY contain the same material more than once in their substructure. + For example, M9 media contains glucose, but it would not be necessary to consolidate the glucose in M9 media + with additional glucose that was added as a supplement, since that would change the definition of the media.''' ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:BehaviorExecution ; + rdfs:range paml:Material ; + rdfs:label "consumed_material" . + +paml:ParameterValue rdf:type owl:Class ; + rdfs:comment '''This class is used to represent the assignment of a value to a parameter in a BehaviorExecution + that records the execution of a uml:Behavior. This class is similar to prov:Usage, but instead of always + pointing to an object it uses an arbitrary literal (which might or might not be an object). An example would + be recording that a plate reader absorbance measurement was taken with its absorbance wavelength parameter set + to 600 nm''' ; + rdfs:subClassOf sbol:Identified, + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty paml:parameter ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:LiteralSpecification ; owl:onProperty paml:parameterValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +paml:parameter rdf:type owl:ObjectProperty ; + rdfs:comment '''This property points to the uml:Parameter associated with the value (e.g., wavelength for a + plate reader absorbance measurement behavior).''' ; + rdfs:domain paml:ParameterValue ; + rdfs:range uml:OrderedPropertyValue ; + rdfs:label "parameter" . + +paml:parameterValue rdf:type owl:ObjectProperty ; + rdfs:comment '''This property points to the literal value used for the parameter during execution (e.g., a + uml:LiteralIdentified for an om:Measure representing a 600 nm wavelength).''' ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:ParameterValue ; + rdfs:range uml:LiteralSpecification ; + rdfs:label "value" . + + +paml:Material rdf:type owl:Class ; + rdfs:comment '''An amount of material allocated for use during the execution of a behavior. + For example a Material might be used to specify 1 96-well flat-bottom microplate or 2.5 mL of 10 millimolar glucose. + + TODO: consider changing type of specification to allow non-TopLevel descriptions, such as a ContainerSpec or sbol:ExternallyDefined + TODO: consider adding a field to distinguish between expended vs. reusable materials.''' ; + rdfs:subClassOf sbol:Identified, + [ rdf:type owl:Restriction ; owl:allValuesFrom sbol:TopLevel ; owl:onProperty paml:specification ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom om:Measure ; owl:onProperty paml:amount ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +paml:specification rdf:type owl:ObjectProperty ; + rdfs:comment '''The specification property is used to indicate the type of material used. + For example a DNA sample would be described by an sbol:Component. + + TODO: add example for glucose and for 96-well plate''' ; + rdfs:domain paml:Material ; + rdfs:range sbol:TopLevel ; + rdfs:label "specification" . + +paml:amount rdf:type owl:ObjectProperty ; + rdfs:comment '''The amount property of a Material is used to indicate the quantity of material used. + For example, 2.5 mL (referring to a fluid) or 3 (with unit "number", referring to a group of microplates)''' ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:Material ; + rdfs:range om:Measure ; + rdfs:label "amount" . + +paml:ProtocolExecution rdf:type owl:Class ; + rdfs:comment '''A ProtocolExecution expands on the information in a BehaviorExecution by including records for + the nodes and edges defining the Protocol's behavior as a uml:Activity. Specifically, the execution property + is used to record each firing of a uml:ActivityNode and the flow property is used to record each time a token + moves along a uml:ActivityEdge. + Otherwise, a ProtocolExecution is used exactly the same way as its parent class BehaviorExecution. + + TODO: consider dropping the protocol field as redundant with use prov:type field in its parent''' ; + rdfs:subClassOf paml:BehaviorExecution , + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:Protocol ; owl:onProperty paml:protocol ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:ActivityNodeExecution ; owl:onProperty paml:execution ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:ActivityEdgeFlow ; owl:onProperty paml:flow ] . + +paml:protocol rdf:type owl:ObjectProperty ; + rdfs:comment '''This property appears to be redundant with the use of prov:type specified by BehaviorExecution, and is likely to be deleted''' ; + rdfs:domain paml:ProtocolExecution ; + rdfs:range paml:Protocol ; + rdfs:label "protocol" . + +paml:execution rdf:type owl:ObjectProperty ; + rdfs:comment '''Each instance of this property links to an ActivityNodeExecution that records one + firing of a uml:ActivityNode during the execution of its containing Protocol''' ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:ProtocolExecution ; + rdfs:range paml:ActivityNodeExecution ; + rdfs:label "executions" . + +paml:flow rdf:type owl:ObjectProperty ; + rdfs:comment '''Each instance of this property links to an ActivityEdgeFlow that records one movement of a UML + token along a uml:ActivityEdge during the execution of its containing Protocol''' ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:ProtocolExecution ; + rdfs:range paml:ActivityEdgeFlow ; + rdfs:label "flows" . + + +paml:ActivityEdgeFlow rdf:type owl:Class ; + rdfs:comment '''An ActivityEdgeFlow records one movement of a UML token along a uml:ActivityEdge during the + execution of its containing Protocol. If the edge is a uml:ObjectFlow, then the value MUST be set. + If the edge is a uml:ControlFlow, then the value MUST NOT be set. + + For instance, the ActivityEdgeFlow for a uml:ObjectFlow might record a measurement being sent to an output + uml:Parameter, while the ActivityEdgeFlow for a uml:ControlFlow might record a decision to proceed down a + particular branch from a uml:DecisionNode. + + Note that a uml:ActivityEdge might appear in multiple ActivityEdgeFlow records associated with a single + ProtocolExecution, e.g., due to a loop in the Protocol. It also might not appear in any, if the + uml:ActivityEdge is on a path not taken due to branching control flow. + + TODO: correct the cardinality: edgeValue is supposed to be optional, not edge + ''' ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ActivityEdge ; owl:onProperty paml:edge ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ; ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom sbol:Identified ; owl:onProperty paml:edgeValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ; ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:ActivityNodeExecution ; owl:onProperty paml:tokenSource ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ; ] . + +paml:edge rdf:type owl:ObjectProperty ; + rdfs:comment '''This property is used to indicate the uml:ActivityEdge down which the token moved.''' ; + rdfs:domain paml:ActivityEdgeFlow ; + rdfs:range uml:ActivityEdge ; + rdfs:label "edge" . + +paml:edgeValue rdf:type owl:ObjectProperty ; + rdfs:comment '''This property is used to indicate the value of a token that moved on a uml:ObjectFlow edge.''' ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:ActivityEdgeFlow ; + rdfs:range uml:LiteralSpecification ; + rdfs:label "value" . + +paml:tokenSource rdf:type owl:ObjectProperty ; + rdfs:comment '''This property is used to indicate the ActivityNodeExecution that produced the token.''' ; + rdfs:domain paml:ActivityEdgeFlow ; + rdfs:range paml:ActivityNodeExecution ; + rdfs:label "token_source" . + + +paml:ActivityNodeExecution rdf:type owl:Class ; + rdfs:comment '''An ActivityNodeExecution records one instance in which a uml:ActivityNode is executed during the + execution of its containing Protocol. + + For instance, the ActivityNodeExecution for a uml:CallBehaviorAction to measure absorbance on a plate reader + would set its node property to point to the uml:CallBehaviorAction and might have incomingFlow properties + indicating arrival of information about the samples to measure via a uml:ObjectFlow and the arrival a + of permission to begin via a uml:ControlFlow. + + Note that a uml:ActivityNode might appear in multiple ActivityNodeExecution records associated with a single + ProtocolExecution, e.g., due to a loop in the Protocol. It also might not appear in any, if the + uml:ActivityNode is on a path not taken due to branching control flow.''' ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ActivityNode ; owl:onProperty paml:node ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ; ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:ActivityEdgeFlow ; owl:onProperty paml:incomingFlow ] . + +paml:node rdf:type owl:ObjectProperty ; + rdfs:comment '''This property is used to indicate the uml:ActivityNode that has been execcuted.''' ; + rdfs:domain paml:ActivityNodeExecution ; + rdfs:range uml:ActivityNode ; + rdfs:label "node" . + +paml:incomingFlow rdf:type owl:ObjectProperty ; + rdfs:comment '''This property is used to indicate an ActivityEdgeFlow that delivered a token consumed during + the execution of the uml:ActivityNode.''' ; + rdfs:domain paml:ActivityNodeExecution ; + rdfs:range paml:ActivityEdgeFlow ; + rdfs:label "incoming_flows" . + + +paml:CallBehaviorExecution rdf:type owl:Class ; + rdfs:comment '''A CallBehaviorExecution extends ActivityNodeExecution by adding a pointer to a BehaviorExecution + record for the uml:Behavior that is being executed. + + For a primitive action (e.g., measuring absorbance on a plate reader), this is a plain BehaviorExecution, + while for calling a Protocol as a sub-routine (e.g., to run a stage of an Type IIS assembly), this would be a + ProtocolExecution.''' ; + rdfs:subClassOf paml:ActivityNodeExecution , + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:BehaviorExecution ; owl:onProperty paml:call ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ; ] . + +paml:call rdf:type owl:ObjectProperty ; + rdfs:comment '''This property indicates the BehaviorExecution record for the uml:Behavior that was called.''' ; + rdfs:domain paml:CallBehaviorExecution ; + rdfs:range paml:BehaviorExecution ; + rdfs:label "call" . + + + +################################################################# +# Sample collection data model +################################################################# + +paml:SampleCollection rdf:type owl:Class ; + rdfs:comment '''SampleCollection is the base class for describing the collections of physical materials that are + acted upon by a Protocol. For example, a SampleCollection might describe a set of 10 cell cultures growing in + 96-well plate cells, or a set of 6 streaked agar plates, or a single 500 mL flask filled with media. + + There are two types of SampleCollection. A SampleArray specifies an n-dimensional rectangular array of samples, + all stored in the same type of container. A SampleMask specifies a subset of a SampleCollection by means of an + array of Boolean values indicating whether each element is included or excluded from the subset. + + Note, however, that a SampleCollection is a logical object and not a physical object. Thus, while a + SampleCollection might describe a set of samples in 96-well plate wells, it does not necessarily identify + a particular 96-well plate or the location of those wells. In practice, these will be determined as a + result of the specific library calls made to generate SampleCollection objects, and may not be determined + until the protocol is actually run in a particular execution environment. + + This is important for increasing the flexibility with which a Protocol can be specified and applied. + Consider, for example, a cell culturing protocol that includes a step to measure sample absorbance on a plate + reader. Describing this step does not require knowing how the samples are laid out on the plate, and in many + cases is even acceptable to run on samples across multiple plates. This flexibility will allow the cell + culturing protocol to be applied for experiments with different numbers and arrangements of samples.''' ; + rdfs:subClassOf sbol:Identified . + + +paml:SampleArray rdf:type owl:Class ; + rdfs:comment '''A SampleArray specifies an n-dimensional rectangular array of samples, all stored in the same + type of container. For example, a SampleCollection might describe a set of 10 cell cultures growing in + 96-well plate cells, or a set of 6 streaked agar plates, or a single 500 mL flask filled with media. + + Wells may be full, in which case the contents property should contain a URI to a description of the sample, + or empty, in which case the contents should be null. + + Note that this is a logical array, and does not necessarily indicate the actual layout of the samples in space. + For example, a 2x4 array of samples in 96-well plate wells might end up being laid out as a 2x4 array in wells + A1 to B4 or as a 2x4 array in wells G5 to H8 or as an 8x1 column in wells A1 to H1, or even as eight wells + scattered arbitrarily around the plate according to an anti-bias quality control schema. + + This also allows for higher-dimensional arrays where each dimension represents an experimental factor. + For example, an experiment testing four factors with 3, 3, 4, and 5 values per factor, for a total of 180 + combinations, could be represented as a 4-dimensional sample array of 96-well plate wells, and then end up + laid out over two plates. + + TODO: need to decide on the format of the contents description.''' ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:containerType ; + owl:allValuesFrom xsd:anyURI ; # replace with container ontology, when available + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Type of container used for storing the samples. The size and dimension may not match that of the array: it is up to execution to lay out the array in one or more containers" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:contents ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of URI for specification or nulls" + ] . + +paml:containerType rdf:type owl:DatatypeProperty ; + rdfs:comment '''''' ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:anyURI ; + rdfs:label "container_type" . + +paml:contents rdf:type owl:DatatypeProperty ; + rdfs:comment '''Description of the contents. + TODO: need to decide whether this is a multi-valued property with associated array coordinates or a + single-valued property with an array value. + Currently set to string as a "dummy" value that can serialize anything.''' ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:string ; + rdfs:label "contents" . + +paml:SampleMask rdf:type owl:Class ; + rdfs:comment '''A SampleMask is a subset of a SampleCollection. The subset of samples to be included is defined + by an array of Boolean values, where true values indicate that a sample is included and false values indicate + that it is excluded. + + The dimensions of the mask MUST be identical to the dimensions of the source SampleCollection. For this purpose, + the dimensions of a masked subset are not reduced, but remain the same as the original SampleArray. This allows + masks to be composed, such that SampleMask(source=SampleMask(source=X,mask=mask1),mask=mask2) is equivalent to + SampleMask(source=X,mask=mask1 AND mask2). Note that this implies masks are commutative and idempotent.''' ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:source ; + owl:allValuesFrom paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:mask ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger + ] . + +paml:source rdf:type owl:ObjectProperty ; + rdfs:comment '''The source indicates the SampleCollection that is being subsetted via the mask''' ; + rdfs:domain paml:SampleMask ; + rdfs:range paml:SampleCollection ; + rdfs:label "source" . + +paml:mask rdf:type owl:DatatypeProperty ; + rdfs:comment '''The mask is an N-dimensional array of Booleans values, where each Boolean indicates whether the + sample at the corresponding location in the source is included in the subset. + + TODO: format of mask array needs to match the array format chosen for the SampleArray contents property''' ; + rdfs:domain paml:mask ; + rdfs:range xsd:string ; + rdfs:label "mask" . + + + + +paml:SampleData rdf:type owl:Class ; + rdfs:comment '''The SampleData class is used to associate a set of data with a collection of samples. + This is typically used to capture measurements, e.g., an array of absorbance measurements collected by + a plate reader. Using this data structure allows the values in a dataframe to be automatically linked to + the descriptions of the samples that the data describes, which is critical for data analysis. + + The dimensions of the sampleDataValues MUST equal the dimensions of the SampleCollection linked with fromSamples. + + TODO: the format of the data values needs to be compatible with the array format chosen for the + SampleArray contents property. In this case, however, we also need to consider how we want to support + multiple values for each sample (e.g., measurement of both fluorescence and absorbance in a plate reader), + as well as links to more complex data (e.g., results of flow cytometry or omics for each sample)''' ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; + owl:onProperty paml:sampleDataValues ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:fromSamples ; + owl:allValuesFrom paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] . + +paml:sampleDataValues rdf:type owl:ObjectProperty ; + rdfs:comment '''The sampleDataValues are an array of data values, one for each sample, format to be determined.'''; + rdfs:domain paml:SampleData ; + rdfs:range xsd:string ; # TODO: consider https://github.com/SynBioDex/sbol_factory/issues/7 + rdfs:label "values" . + +paml:fromSamples rdf:type owl:ObjectProperty ; + rdfs:comment '''The fromSamples property indicates the SampleCollection from which the data were collected.''' ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:SampleData ; + rdfs:range paml:SampleCollection ; + rdfs:label "from_samples" . + + +################################################################# +# Container specification +################################################################# +paml:queryString a owl:DatatypeProperty , + owl:FunctionalProperty ; + rdfs:domain paml:ContainerSpec ; + rdfs:range xsd:string ; + rdfs:label "queryString" ; + rdfs:comment "A query string, in OWL Manchester syntax, to be used to find matching containers in the ContainerSpec." . + + +paml:prefixMap a owl:DatatypeProperty , + owl:FunctionalProperty ; + rdfs:domain paml:ContainerSpec ; + rdfs:range xsd:string ; + rdfs:label "prefixMap" ; + rdfs:comment "A prefix map in JSON-LD format, to be applied to a queryString." . + + +paml:ContainerSpec a owl:Class ; + rdfs:comment '''A ContainerSpec is used to indicate the type of container to be used for a SampleArray, e.g., + a standard 96-well flat-bottom transparent plate. + + TODO: determine if we want to use this format or modify it in some way.''' ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; + owl:onProperty paml:queryString ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:queryString ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:prefixMap ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:prefixMap ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ] . diff --git a/paml/type_inference.py b/paml/type_inference.py new file mode 100644 index 0000000..73df564 --- /dev/null +++ b/paml/type_inference.py @@ -0,0 +1,159 @@ +# import paml +# from paml.lib.library_type_inference import primitive_type_inference_functions +# +# ############################## +# # Class for carrying a typing process +# +# +# class ProtocolTyping: +# def __init__(self): +# self.flow_values = {} # dictionary of paml.Flow : type value, includes subprotocols too +# self.typed_protocols = set() # protocol and subprotocols already evaluated or in process of evaluation +# self.cache = {} # kludge for accelerating inflow satisfaction computation +# +# def infer_typing(self, protocol : paml.Protocol): +# self.typed_protocols.add(protocol) +# pending_activities = set(protocol.activities) +# print('Building activity cache non-blocked') +# self.cache.update({a:a.input_flows() for a in pending_activities}) # speed kludge +# while pending_activities: +# print('Collecting non-blocked activities out of pending '+str(len(pending_activities))) +# non_blocked = {a for a in pending_activities if self.inflows_satisfied(a)} +# if not non_blocked: +# raise ValueError("Could not infer all flow types in "+protocol.identity+": circular dependency?") +# for activity in non_blocked: +# print('Inferring typing for '+activity.identity) +# activity.infer_typing(self) +# pending_activities -= non_blocked +# +# def inflows_satisfied(self, activity): +# #unsatisfied = {flow for flow in activity.input_flows() if flow not in self.flow_values.keys()} +# unsatisfied = {flow for flow in self.cache[activity] if flow not in self.flow_values.keys()} +# return len(unsatisfied) == 0 +# +# +# ############################# +# # Inference utilities +# +# def pin_input_type(self, typing: ProtocolTyping): +# try: +# return self.value +# except AttributeError: +# in_flows = self.input_flows() +# assert len(in_flows) == 1, \ +# ValueError("Expected one input flow for '" + self.get_parent().identity + "' but found " + len(in_flows)) +# return typing.flow_values[in_flows.pop()] +# paml.Pin.input_type = pin_input_type +# +# +# def pin_assert_output_type(self, typing: ProtocolTyping, value): +# out_flows = self.output_flows() +# # TODO: need to decide if this type of implicit fork is acceptable +# for f in out_flows: +# typing.flow_values[f] = value +# # assert len(out_flows) == 1, \ +# # ValueError("Expected one output flow for '" + self.get_parent().identity + "' but found " + str(len(out_flows))) +# # typing.flow_values[out_flows.pop()] = value +# paml.Pin.assert_output_type = pin_assert_output_type +# +# +# ############################# +# # Inference over Activities +# def initial_infer_typing(self, typing: ProtocolTyping): +# typing.flow_values.update({f: None for f in self.direct_output_flows()}) +# paml.Initial.infer_typing = initial_infer_typing +# +# +# def final_infer_typing(self, _: ProtocolTyping): +# assert len(self.direct_output_flows()) == 0 # should be no outputs +# paml.Final.infer_typing = final_infer_typing +# +# +# def fork_decision_infer_typing(self, typing: ProtocolTyping): +# assert len(self.direct_input_flows()) == 1 # should be precisely one input +# in_type = typing.flow_values[self.direct_input_flows().pop()] +# typing.flow_values.update({f: in_type for f in self.direct_output_flows()}) +# paml.Fork.infer_typing = fork_decision_infer_typing +# paml.Decision.infer_typing = fork_decision_infer_typing +# +# +# def join_infer_typing(self, typing: ProtocolTyping): +# #assert len(self.direct_output_flows()) == 1 # should be precisely one output +# value = join_values({typing.flow_values[f] for f in self.direct_input_flows()}) +# typing.flow_values.update({f: value for f in self.direct_output_flows()}) +# paml.Join.infer_typing = join_infer_typing +# +# # TODO: add type inference for Merge +# +# +# def primitiveexecutable_infer_typing(self, typing: ProtocolTyping): +# typing.flow_values.update({f: None for f in self.direct_output_flows()}) +# inference_function = primitive_type_inference_functions[self.instance_of.lookup().identity] +# inference_function(self, typing) +# paml.PrimitiveExecutable.infer_typing = primitiveexecutable_infer_typing +# +# # TODO: add type inference for SubProtocol +# def subprotocol_infer_typing(self: paml.SubProtocol, typing: ProtocolTyping): +# typing.flow_values.update({f: None for f in self.direct_output_flows()}) +# subprotocol = self.instance_of.lookup() +# if subprotocol not in typing.typed_protocols: +# # add types for inputs +# input_pin_flows = self.input_flows() - self.direct_input_flows() +# for f in input_pin_flows: +# typing.flow_values.update({subflow: typing.flow_values[f] for subflow in subprotocol.get_input(f.sink.lookup().name).activity.lookup().direct_output_flows()}) +# # run the actual inference +# typing.infer_typing(subprotocol) +# # pull values from outputs' inferred values +# output_pin_flows = self.output_flows() - self.direct_output_flows() +# for f in output_pin_flows: +# typing.flow_values.update({subflow: typing.flow_values[f] for subflow in subprotocol.get_output(f.source.lookup().name).activity.lookup().direct_input_flows()}) +# paml.SubProtocol.infer_typing = subprotocol_infer_typing +# +# +# def type_to_value(type_name: str, **kwargs): +# if type_name == 'http://bioprotocols.org/paml#LocatedSamples': +# return paml.LocatedSamples(**kwargs) +# elif type_name == 'http://bioprotocols.org/paml#LocatedData': +# return paml.LocatedData(**kwargs) +# else: +# ValueError("Don't know how to make dummy object for type "+type_name) +# +# +# def value_infer_typing(self: paml.Value, typing: ProtocolTyping): +# # assert len(self.direct_output_flows()) == 1 # should be precisely one output --- or maybe not. TODO: decide +# output_instance = (type_to_value(self.type, name=self.name) if self.type else None) +# # Don't overwrite values that are already written +# unset_values = {f for f in self.direct_output_flows() if f not in typing.flow_values.keys()} +# typing.flow_values.update({f: output_instance for f in unset_values}) +# paml.Value.infer_typing = value_infer_typing +# +# +# +# ################# +# # Join is a kludge for now +# # TODO: Make a more principled approach to inference of Join, which will also provide an architcture for Merge +# def join_locations(value_set): +# if not value_set: +# return paml.HeterogeneousSamples() +# next_value = value_set.pop() +# rest = join_locations(value_set) +# if isinstance(next_value, paml.ReplicateSamples): +# rest.replicate_samples.append(next_value) +# elif isinstance(next_value, paml.HeterogeneousSamples): +# for x in next_value.replicate_samples: +# rest.replicate_samples.append(x) +# else: +# raise ValueError("Don't know how to join locations for "+str(value_set)) +# return rest +# +# def join_values(value_set): +# if all(isinstance(x,paml.LocatedSamples) for x in value_set): +# return join_locations(value_set) +# elif all(x is None for x in value_set): +# return None +# # if we fall through to the end, then we didn't know how to infer +# raise ValueError("Don't know how to join values types for "+str(value_set)) +# +# +# +# diff --git a/paml/ui.py b/paml/ui.py new file mode 100644 index 0000000..925a64c --- /dev/null +++ b/paml/ui.py @@ -0,0 +1,23 @@ +from paml import Protocol, Primitive +import uml + +def protocol_template(): + """ + Create a template instantiation of a protocol. Used for populating UI elements. + :param + :return: str + """ + return f"protocol = paml.Protocol(\n\t\"Identity\",\n\tname=\"Name\",\n\tdescription=\"Description\")" +Protocol.template = protocol_template + +def primitive_template(self): + """ + Create a template instantiation of a primitive for writing a protocol. Used for populating UI elements. + :param self: + :return: str + """ + args = ",\n\t".join([f"{parameter.property_value.template()}" + for parameter in self.parameters + if parameter.property_value.direction == uml.PARAMETER_IN]) + return f"step = protocol.primitive_step(\n\t\'{self.display_id}\',\n\t{args}\n\t)" +Primitive.template = primitive_template diff --git a/paml_convert/__init__.py b/paml_convert/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/paml_convert/autoprotocol/__init__.py b/paml_convert/autoprotocol/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/paml_convert/autoprotocol/autoprotocol_specialization.py b/paml_convert/autoprotocol/autoprotocol_specialization.py new file mode 100644 index 0000000..cf63aff --- /dev/null +++ b/paml_convert/autoprotocol/autoprotocol_specialization.py @@ -0,0 +1,208 @@ +import json +from typing import Dict + +import sbol3 +import transcriptic + +import paml_convert.autoprotocol.plate_coordinates as pc +import tyto +import paml + +from autoprotocol.container import WellGroup +from autoprotocol.instruction import Provision, Spectrophotometry +from autoprotocol.protocol import Protocol +from autoprotocol.unit import Unit +from autoprotocol import container_type as ctype +from paml_convert.behavior_specialization import BehaviorSpecialization +from paml_convert.autoprotocol.strateos_api import StrateosAPI + +from container_api.client_api import matching_containers, strateos_id + + +import logging + +l = logging.getLogger(__file__) +l.setLevel(logging.ERROR) + +class AutoprotocolSpecialization(BehaviorSpecialization): + def __init__(self, out_path, api: StrateosAPI = None, resolutions: Dict[sbol3.Identified, str] = None) -> None: + super().__init__() + self.out_path = out_path + self.resolutions = resolutions + self.api = api + self.var_to_entity = {} + self.container_api_addl_conditions = "(cont:availableAt value )" + + + def _init_behavior_func_map(self) -> dict: + return { + "https://bioprotocols.org/paml/primitives/sample_arrays/EmptyContainer" : self.define_container, + "https://bioprotocols.org/paml/primitives/liquid_handling/Provision" : self.provision_container, + "https://bioprotocols.org/paml/primitives/sample_arrays/PlateCoordinates" : self.plate_coordinates, + "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance" : self.measure_absorbance, + } + + def on_begin(self): + protocol_name = self.execution.protocol.lookup().name + self.protocol = Protocol() + + def on_end(self): + with open(self.out_path, "w") as f: + json.dump(self.protocol.as_dict(), f, indent=2) + + def define_container(self, record: paml.ActivityNodeExecution): + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + spec = parameter_value_map["specification"]['value'] + samples_var = parameter_value_map["samples"]['value'] + + if "container_id" in self.resolutions: + container_id = self.resolutions["container_id"] + else: + container_type = self.get_container_type_from_spec(spec) + container_name = f"{self.execution.protocol.lookup().name} Container {samples_var}" + container_id = self.create_new_container(container_name, container_type) + + #container_id = tx.inventory("flat test")['results'][1]['id'] + #container_id = "ct1g9q3bndujat5" + tx = self.api.get_strateos_connection() # Make sure that connection is alive for making the container object + tx_container = transcriptic.Container(container_id) + container = self.protocol.ref(samples_var.name, id=tx_container.id, cont_type=tx_container.container_type, discard=True) + self.var_to_entity[samples_var] = container + + l.debug(f"define_container:") + l.debug(f" specification: {spec}") + l.debug(f" samples: {samples_var}") + + + #spec_term = UnresolvedTerm(None, samples_var, spec) + #self.unresolved_terms.append(spec_term) + + return results + + def get_container_type_from_spec(self, spec): + short_names = [v.shortname + for v in [getattr(ctype, x) for x in dir(ctype)] + if isinstance(v, ctype.ContainerType)] + try: + possible_container_types = self.resolve_container_spec(spec, + addl_conditions=self.container_api_addl_conditions) + possible_short_names = [strateos_id(x) for x in possible_container_types] + matching_short_names = [x for x in short_names if x in possible_short_names] + name_map = { + '96-ubottom-clear-tc': "96-flat", + '96-flat-clear-clear-tc': "96-flat" + } + mapped_names = [name_map[x] for x in matching_short_names] + return mapped_names[0] + # return matching_short_names[0] # FIXME need some error handling here + + except Exception as e: + l.warning(e) + container_type = "96-flat" + l.warning(f"Defaulting container to {container_type}") + return container_type + + + + def create_new_container(self, name, container_type): + container_spec = { + "name": name, + "cont_type": container_type, # resolve with spec here + "volume": "100:microliter", # FIXME where does this come from? + "properties": [ + { + "key": "concentration", + "value": "10:millimolar" + } + ] + } + container_ids = self.api.make_containers([container_spec]) + container_id = container_ids[name] + #tx = self.api.get_transcriptic_connection() + #container_id = tx.inventory("flat test")['results'][1]['id'] + #container_id = "ct1g9q3bndujat5" + return container_id + + # def provision_container(self, wells: WellGroup, amounts = None, volumes = None, informatics = None) -> Provision: + def provision_container(self, record: paml.ActivityNodeExecution) -> Provision: + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + destination = parameter_value_map["destination"]["value"] + dest_wells = self.var_to_entity[destination] + value = parameter_value_map["amount"]["value"].value + units = parameter_value_map["amount"]["value"].unit + units = tyto.OM.get_term_by_uri(units) + resource = parameter_value_map["resource"]["value"] + resource = self.resolutions[resource] + l.debug(f"provision_container:") + l.debug(f" destination: {destination}") + l.debug(f" amount: {value} {units}") + l.debug(f" resource: {resource}") + [step] = self.protocol.provision( + resource, + dest_wells, + amounts=Unit(value, units) + ) + #resource_term = UnresolvedTerm(step, "resource_id", resource) + #self.unresolved_terms.append(resource_term) + return results + + def plate_coordinates(self, record: paml.ActivityNodeExecution) -> WellGroup: + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + source = parameter_value_map["source"]["value"] + container = self.var_to_entity[source] + coords = parameter_value_map["coordinates"]["value"] + wells = pc.coordinate_rect_to_well_group(container, coords) + + self.var_to_entity[parameter_value_map['samples']["value"]] = wells + l.debug(f"plate_coordinates:") + l.debug(f" source: {source}") + l.debug(f" coordinates: {coords}") + #results[outputs['samples']] = ('samples', pc.coordinate_rect_to_well_group(source, coords)) + return results + + def measure_absorbance(self, record: paml.ActivityNodeExecution): + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + wl = parameter_value_map["wavelength"]["value"] + wl_units = tyto.OM.get_term_by_uri(wl.unit) + samples = parameter_value_map["samples"]["value"] + wells = self.var_to_entity[samples] + measurements = parameter_value_map["measurements"]["value"] + + # HACK extract contrainer from well group since we do not have it as input + container = wells[0].container + + l.debug(f"measure_absorbance:") + l.debug(f" container: {container}") + l.debug(f" samples: {samples}") + l.debug(f" wavelength: {wl.value} {wl_units}") + + self.protocol.spectrophotometry( + dataref=measurements, + obj=container, + groups=Spectrophotometry.builders.groups([ + Spectrophotometry.builders.group( + "absorbance", + Spectrophotometry.builders.absorbance_mode_params( + wells=wells, + wavelength=Unit(wl.value, wl_units), + num_flashes=None, + settle_time=None, + read_position=None, + position_z=None + ) + ) + ]) + ) + return results \ No newline at end of file diff --git a/paml_convert/autoprotocol/plate_coordinates.py b/paml_convert/autoprotocol/plate_coordinates.py new file mode 100644 index 0000000..dff4aaf --- /dev/null +++ b/paml_convert/autoprotocol/plate_coordinates.py @@ -0,0 +1,45 @@ +from autoprotocol.container import Container, WellGroup +from string import ascii_letters +import re + +def col2num(col: str): + """ + Get the index of the alpha column string. + - A -> 1 + - Z -> 26 + - AA -> 27 + - AZ -> 52 + - etc + """ + num = 0 + for c in col: + if c in ascii_letters: + num = num * 26 + (ord(c.upper()) - ord('A')) + 1 + else: + raise Exception(f"Invalid character: {c}") + return num + +def coordinate_to_row_col(coord: str): + m = re.match('^([a-zA-Z]+)([0-9]+)$', coord) + if m is None: + raise Exception(f"Invalid coordinate: {coord}") + # convert column to index and then adjust to zero-based indices + return (col2num(m.group(1)) - 1), (int(m.group(2)) - 1) + +def coordinate_rect_to_row_col_pairs(coords: str): + parts = coords.split(':') + if len(parts) != 2: + raise Exception(f"Invalid coordinates: {coords}") + fcol, frow = coordinate_to_row_col(parts[0]) + scol, srow = coordinate_to_row_col(parts[1]) + + indices = [] + for i in range(fcol, scol + 1): + for j in range(frow, srow + 1): + indices.append((i, j)) + return indices + +def coordinate_rect_to_well_group(container: Container, coordinates: str): + indices = coordinate_rect_to_row_col_pairs(coordinates) + wells = [container.well_from_coordinates(i,j) for i, j in indices] + return WellGroup(wells) diff --git a/paml_convert/autoprotocol/strateos_api.py b/paml_convert/autoprotocol/strateos_api.py new file mode 100644 index 0000000..ca0b840 --- /dev/null +++ b/paml_convert/autoprotocol/strateos_api.py @@ -0,0 +1,269 @@ +import json +import os +import requests +import time +import transcriptic + +from datetime import datetime +import requests +from requests_html import HTMLSession + + +import logging + +l = logging.getLogger(__file__) +l.setLevel(logging.ERROR) + +class StrateosException(Exception): + pass + +class StrateosEnvironmentException(Exception): + pass + + +class StrateosConfig(): + @property + def email(self) -> str: + return self._email + + @property + def token(self) -> str: + return self._token + + @property + def user_id(self) -> str: + return self._user_id + + @property + def organization_id(self) -> str: + return self._organization_id + + @property + def project_id(self) -> str: + return self._project_id + + def __init__(self, email: str, token: str, user_id: str, organization_id: str, project_id: str) -> None: + self._email = email + self._token = token + self._user_id = user_id + self._organization_id = organization_id + self._project_id = project_id + + def to_dict(self): + return { + "analytics": True, + "api_root": "https://secure.transcriptic.com", + "email": self.email, + "feature_groups": [], + "organization_id": self.organization_id, + "token": self.token, + "user_id": self.user_id + } + + @staticmethod + def from_file(cfg_file): + def get_file_else_error(cfg, var): + res = cfg[var] if var in cfg else None + if res is None: + raise StrateosEnvironmentException(f"Configuration variable '{var}' is unset") + return res + + with open(cfg_file, "r") as tx_cfg_file: + # Lab Configuration + tx_cfg = json.load(tx_cfg_file) + email = get_file_else_error(tx_cfg, "email") + token = get_file_else_error(tx_cfg, "token") + user = get_file_else_error(tx_cfg, "email") + org = get_file_else_error(tx_cfg, "organization_id") + project_id = get_file_else_error(tx_cfg, "project_id") + return StrateosConfig(email, token, user, org, project_id) + + @staticmethod + def from_environment(): + def get_env_else_error(var): + res = os.environ.get(var) + if res is None: + raise StrateosEnvironmentException(f"Environment variable '{var}' is unset") + return res + email = get_env_else_error("_TRANSCRIPTIC_EMAIL") + token = get_env_else_error("_TRANSCRIPTIC_TOKEN") + user = get_env_else_error("_TRANSCRIPTIC_USER_ID") + org = get_env_else_error("_TRANSCRIPTIC_ORGANIZATION_ID") + proj = get_env_else_error("_TRANSCRIPTIC_PROJECT_ID") + return StrateosConfig(email, token, user, org, proj) + +class StrateosProtocol(): + def __init__(self, protocol): + self.protocol = protocol + self.id = protocol["id"] + self.name = protocol["name"] + +class StrateosAPI(): + + @property + def protocol_make_containers(self) -> StrateosProtocol: + return self._protocol_make_containers + + def __init__(self, out_dir: str = "./", cfg: StrateosConfig = None) -> None: + self.out_dir = out_dir + if not os.path.exists(self.out_dir): + os.mkdir(self.out_dir) + + self.cfg = StrateosConfig.from_environment() if cfg is None else cfg + + self._protocol_name_map = {} + ps = self.query_all_protocols() + for p in ps: + tp = StrateosProtocol(p) + self._protocol_name_map[p["name"]] = tp + + self._protocol_make_containers = self._name_to_protocol("MakeContainers") + + def _name_to_protocol(self, name: str): + res = self._protocol_name_map.get(name) + if res is None: + raise StrateosException(f"Failed to find '{name}' in protocol name map") + return res + + def _build_headers(self): + return {"X-User-Email": self.cfg.email, # user-account-email + "X-User-Token": self.cfg.token, # Regular-mode API key + "Content-Type": "application/json", + "Accept": "application/json"} + + def _build_query_protocols(self): + return ("https://secure.transcriptic.com/{}/protocols.json".format(self.cfg.organization_id), + self._build_headers()) + + + def query_all_protocols(self): + """ + Get all protocols + """ + (url, headers) = self._build_query_protocols() + #l.debug(headers) + response = requests.get(url, headers=headers) + return json.loads(response.content) + + # TODO + def make_containers(self, containers, title = "make_containers", test_mode=True): + params = { + "parameters": { + "containers": containers + } + } + # TODO verify this request then enable sending + response = self.submit_to_strateos(self._protocol_make_containers, params, title) + + container_ids = {x['name'] : x['container_id'] for x in response['refs']} + + return container_ids + + def get_strateos_connection(self): + """Connect (without validation) to Strateos.com""" + try: + return transcriptic.Connection(**self.cfg.to_dict()) + except Exception: + raise + + + def submit_to_strateos(self, + protocol: StrateosProtocol, + params, + title, + test_mode=True): + """Submit to Strateos and record response""" + + launch_request_id = None + launch_protocol = None + + try: + conn = self.get_strateos_connection() + except Exception as exc: + raise StrateosException(exc) + + try: + launch_request = self._create_launch_request(params, title, test_mode=test_mode) + try: + launch_protocol = conn.launch_protocol(launch_request, protocol_id=protocol.id) + except Exception as exc: + raise StrateosException(exc) + launch_request_id = launch_protocol["id"] + except Exception as exc: + raise StrateosException(exc) + + # Delay needed because it takes Strateos a few seconds to + # complete launch_protocol() + time.sleep(30) + + request_response = {} + try: + req_title = "{}_{}_{}".format( + datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%ST%f'), + title, + protocol.name + ) + + # req_title = "{}-{}".format( + # robj.get_attr('name'), + # arrow.utcnow().format('YYYY-MM-DDThh:mm:ssTZD')) + # Retry submission up to timeout, with exponential backoff + request_response = self.__submit_launch_request( + conn, + launch_request_id, + protocol_id=protocol.id, + project_id=self.cfg.project_id, + title=req_title, + test_mode=test_mode) + return request_response + + except Exception as exc: + raise StrateosException(exc) + + def _create_launch_request(self, params, local_name, bsl=1, test_mode=True): + """Creates launch_request from input params""" + params_dict = dict() + params_dict["launch_request"] = params + params_dict["launch_request"]["bsl"] = bsl + params_dict["launch_request"]["test_mode"] = test_mode + + with open(os.path.join(self.out_dir, 'launch_request_{}.json'.format(local_name)), 'w') as lr: + json.dump(params_dict, lr, sort_keys=True, + indent=2, separators=(',', ': ')) + return json.dumps(params_dict) + + + #@retry(stop=stop_after_delay(70), wait=wait_exponential(multiplier=1, max=16)) + def __submit_launch_request(self, conn, launch_request_id, protocol_id=None, + project_id=None, title=None, test_mode=True): + try: + l.debug("Launching: launch_request_id = " + launch_request_id) + l.debug("Launching: protocol_id = " + protocol_id) + l.debug("Launching: project_id = " + project_id) + l.debug("Launching: title = " + title) + l.debug("Launching: test_mode = " + str(test_mode)) + lr = conn.submit_launch_request(launch_request_id, + protocol_id=protocol_id, + project_id=project_id, + title=title, + test_mode=test_mode) + return lr + except Exception as exc: + raise StrateosException(exc) + + def resolve_resource(self, resource): + types = resource.types + resolutions = {} + for type in types: + try: + session = HTMLSession() + response = session.get(type) + meta = response.html.find("meta") + properties = [x.attrs for x in response.html.find('meta') if "property" in x.attrs] + [title] = [x['content'] for x in properties if x['property'] == "og:title"] + except requests.exceptions.RequestException as e: + l.debug(e) + + conn = self.get_strateos_connection() + results = conn.resources(resource)['results'] + return results \ No newline at end of file diff --git a/paml_convert/behavior_specialization.py b/paml_convert/behavior_specialization.py new file mode 100644 index 0000000..70ab1b1 --- /dev/null +++ b/paml_convert/behavior_specialization.py @@ -0,0 +1,78 @@ +from abc import ABC, abstractmethod +from logging import error + +from container_api import matching_containers + +import paml +import uml + + +class BehaviorSpecializationException(Exception): + pass + +class ContainerAPIException(Exception): + pass + + +class BehaviorSpecialization(ABC): + """ + This abstract class defines an API for different conversions from PAML + to other formats, such as Markdown or Autoprotocol. + """ + + def __init__(self) -> None: + super().__init__() + self._behavior_func_map = self._init_behavior_func_map() + self.top_protocol = None + self.execution = None + + def initialize_protocol(self, execution: paml.ProtocolExecution): + self.execution = execution + + @abstractmethod + def _init_behavior_func_map(self) -> dict: + pass + + @abstractmethod + def on_begin(self): + pass + + @abstractmethod + def on_end(self): + pass + + def process(self, record): + node = record.node.lookup() + if not isinstance(node, uml.CallBehaviorAction): + return # raise BehaviorSpecializationException(f"Cannot handle node type: {type(node)}") + elif str(node.behavior) not in self._behavior_func_map: + raise BehaviorSpecializationException(f"Failed to find handler for behavior: {node.behavior}") + return self._behavior_func_map[str(node.behavior)](record) + + def resolve_container_spec(self, spec, addl_conditions=None): + try: + if addl_conditions: + possible_container_types = matching_containers(spec, addl_conditions=addl_conditions) + else: + possible_container_types = matching_containers(spec) + except: + raise ContainerAPIException(f"Cannot resolve specification {spec} with container ontology. Is the container server running and accessible?") + return possible_container_types + +class DefaultBehaviorSpecialization(BehaviorSpecialization): + def _init_behavior_func_map(self) -> dict: + return { + "https://bioprotocols.org/paml/primitives/sample_arrays/EmptyContainer" : self.handle, + "https://bioprotocols.org/paml/primitives/liquid_handling/Provision" : self.handle, + "https://bioprotocols.org/paml/primitives/sample_arrays/PlateCoordinates" : self.handle, + "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance" : self.handle, + } + + def handle(self, record): + pass + + def on_begin(self): + pass + + def on_end(self): + pass \ No newline at end of file diff --git a/paml_convert/markdown/__init__.py b/paml_convert/markdown/__init__.py new file mode 100644 index 0000000..ed03e1a --- /dev/null +++ b/paml_convert/markdown/__init__.py @@ -0,0 +1,2 @@ +from .protocol_to_markdown import MarkdownConverter +from .protocol_to_markdown import excel_to_numpy_range diff --git a/paml_convert/markdown/attic/__init__.py b/paml_convert/markdown/attic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/paml_convert/markdown/attic/excel_to_experimentdata.py b/paml_convert/markdown/attic/excel_to_experimentdata.py new file mode 100644 index 0000000..42b7432 --- /dev/null +++ b/paml_convert/markdown/attic/excel_to_experimentdata.py @@ -0,0 +1,74 @@ +import sbol3 +import paml +import openpyxl +import tyto +import uuid +import warnings + + +############################## +# Set up for conversion +print('Beginning conversion of Excel to SBOL') + +doc = paml.Document() + +wb = openpyxl.load_workbook(filename ='../../../test/testfiles/BBN_LUDOX_OD_calibration_2021-3.xlsx') +ws = wb['Data Reporting'] + +############################## +# Pull header materials from the Excel file +print('Transforming metadata to SBOL') +# pull the ID & metadata for the protocol from the comment +protocol_id = ws['D1'].comment.text +protocol_name = ws['D1'].value +person_executing = ws['C2'].value +date_executed = ws['C3'].value +date_reported = ws['C4'].value + +execution_id = str(uuid.uuid4()).replace('-','_') +execution = sbol3.Activity(protocol_id+'_Execution_'+execution_id) +execution.types.append(protocol_id) # This execution is of the protocol +doc.add(execution) + +experiment = sbol3.Experiment(protocol_id+'_Results_'+execution_id) +experiment.name = protocol_name+" Execution" +experiment.description = 'Executed by '+str(person_executing)+' on '+str(date_executed)+"; reported on "+str(date_reported) +experiment.generated_by.append(execution.identity) +doc.add(experiment) + +############################## +# find all cells below the header that have comments, and create an ExperimentalData, Implementation, and provenance for each +print('Transforming data to SBOL') +count = 0 +for row in ws.iter_rows(min_row=6): + for cell in row: + if cell.comment: # any cell with a comment should have data + count += 1 + data = cell.value + comment_ids = cell.comment.text.split(' ') + assert len(comment_ids)==2 # should be precisely two IDs + implementation_id = comment_ids[0]+"_Execution_"+execution_id + component_id = comment_ids[1] + if data is None: + warnings.warn('Expected value in cell '+cell.coordinate+' but found none') + else: + # find or make an implementation + implementation = doc.find(implementation_id) + if implementation is None: + implementation = sbol3.Implementation(implementation_id) + doc.add(implementation) + experiment.members.append(implementation) + implementation.derived_from.append(component_id) + else: + assert implementation.derived_from[0] == component_id + # stick the value on it as a measure + measure = sbol3.Measure(data,tyto.OM.get_uri_by_term('number')) + implementation.measures.append(measure) + +print('Processed '+str(count)+' data entries') +print(' Note: still need to add information about what the measure is quantifying, avoid collisions when same sample is measuremed multiple times') + +doc.write('BBN_LUDOX_execution_2021_3_report.json', 'json-ld') +doc.write('BBN_LUDOX_execution_2021_3_report.ttl', 'turtle') + +print('writing complete') diff --git a/paml_convert/markdown/markdown_primitives.py b/paml_convert/markdown/markdown_primitives.py new file mode 100644 index 0000000..4aebf6a --- /dev/null +++ b/paml_convert/markdown/markdown_primitives.py @@ -0,0 +1,130 @@ +# TODO: allow optionals to actually be optional + +# Pre-declare the MarkdownConverter class to avoid circularity with markdown.protocol_to_markdown +class MarkdownConverter: + pass + +primitive_to_markdown_functions = {} # dictionary of identity : function for primitives markdown conversion + +############################################# +# Liquid handling primitives + +LIQUID_HANDLING_PREFIX = 'https://bioprotocols.org/paml/primitives/liquid_handling/' + + +def liquid_handling_provision_to_markdown(executable, mdc: MarkdownConverter): + volume = executable.input_pin('amount').to_markdown(mdc) + resource = executable.input_pin('resource').to_markdown(mdc) + destination = executable.input_pin('destination').to_markdown(mdc) + return 'Pipette '+volume+' of '+resource+' into '+destination+'\n' +primitive_to_markdown_functions[LIQUID_HANDLING_PREFIX+'Provision'] = liquid_handling_provision_to_markdown + + +def liquid_handling_dispense_to_markdown(executable, mdc: MarkdownConverter): + volume = executable.input_pin('amount').to_markdown(mdc) + source = mdc.protocol_typing.flow_values[executable.input_pin('source').input_flows().pop()] + resource = mdc.document.find(source.specification).to_markdown(mdc) # Kludge due to document addition failures + source_location = mdc.document.find(source.in_location[0]).to_markdown(mdc) # TODO: generalize to support multi-locations + destination = executable.input_pin('destination').to_markdown(mdc) + return 'Pipette '+volume+' of '+resource+' from '+source_location+' into '+destination+'\n' +primitive_to_markdown_functions[LIQUID_HANDLING_PREFIX+'Dispense'] = liquid_handling_dispense_to_markdown + + +def liquid_handling_transfer_to_markdown(executable, mdc: MarkdownConverter): + volume = executable.input_pin('amount').to_markdown(mdc) + source = executable.input_pin('source').to_markdown(mdc) + destination = executable.input_pin('destination').to_markdown(mdc) + return 'Pipette '+volume+' from '+source+' to '+destination+'\n' +primitive_to_markdown_functions[LIQUID_HANDLING_PREFIX + 'Transfer'] = liquid_handling_transfer_to_markdown + + +def liquid_handling_transferinto_to_markdown(executable, mdc: MarkdownConverter): + volume = executable.input_pin('amount').to_markdown(mdc) + source = executable.input_pin('source').to_markdown(mdc) + destination = executable.input_pin('destination').to_markdown(mdc) + mix_cycles = executable.input_pin('mixCycles').to_markdown(mdc) # TODO: this should be optional, not required + return 'Pipette '+volume+' from '+source+' into '+destination+', mixing by pipetting up and down '+mix_cycles+' times at destination\n' +primitive_to_markdown_functions[LIQUID_HANDLING_PREFIX + 'TransferInto'] = liquid_handling_transferinto_to_markdown + + +def liquid_handling_pipettemix_to_markdown(executable, mdc: MarkdownConverter): + volume = executable.input_pin('amount').to_markdown(mdc) + samples = executable.input_pin('samples').to_markdown(mdc) + mix_cycles = executable.input_pin('mixCycles').to_markdown(mdc) # TODO: this should be optional, not required + return 'Mix '+samples+' by pipetting '+volume+' up and down '+mix_cycles+' times\n' +primitive_to_markdown_functions[LIQUID_HANDLING_PREFIX+'PipetteMix'] = liquid_handling_pipettemix_to_markdown + + +############################################# +# Plate handling primitives + +PLATE_HANDLING_PREFIX = 'https://bioprotocols.org/paml/primitives/plate_handling/' + +def plate_handling_cover_to_markdown(executable, mdc: MarkdownConverter): + location = executable.input_pin('location').to_markdown(mdc) + #type_uri = executable.input_pin('type').to_markdown(mdc) + #type = type_uri.split('/')[-1] # TODO: fix kludge: need a real ontology + #return 'Cover '+location+' with '+type+' cover\n' + return 'Cover '+location+'\n' +primitive_to_markdown_functions[PLATE_HANDLING_PREFIX+'Cover'] = plate_handling_cover_to_markdown + + +def plate_handling_seal_to_markdown(executable, mdc: MarkdownConverter): + location = executable.input_pin('location').to_markdown(mdc) + type_uri = executable.input_pin('type').to_markdown(mdc) + type = type_uri.split('/')[-1] # TODO: fix kludge: need a real ontology + return 'Seal '+location+' with '+type+' seal\n' +primitive_to_markdown_functions[PLATE_HANDLING_PREFIX+'Seal'] = plate_handling_seal_to_markdown + +# TODO: add the sealing sub-types +# primitive_to_markdown_functions[PLATE_HANDLING_PREFIX+'AdhesiveSeal'] = plate_handling_adhesive_seal_to_markdown +# primitive_to_markdown_functions[PLATE_HANDLING_PREFIX+'ThermalSeal'] = plate_handling_thermal_seal_to_markdown + +def plate_handling_uncover_to_markdown(executable, mdc: MarkdownConverter): + location = executable.input_pin('location').to_markdown(mdc) + return 'Remove cover from '+location+'\n' +primitive_to_markdown_functions[PLATE_HANDLING_PREFIX+'Uncover'] = plate_handling_uncover_to_markdown + + +def plate_handling_unseal_to_markdown(executable, mdc: MarkdownConverter): + location = executable.input_pin('location').to_markdown(mdc) + return 'Remove seal from '+location+'\n' +primitive_to_markdown_functions[PLATE_HANDLING_PREFIX+'Unseal'] = plate_handling_unseal_to_markdown + + +def plate_handling_incubate_to_markdown(executable, mdc: MarkdownConverter): + location = executable.input_pin('location').to_markdown(mdc) + duration = executable.input_pin('duration').to_markdown(mdc) + temperature = executable.input_pin('temperature').to_markdown(mdc) + shakingFrequency = executable.input_pin('shakingFrequency').to_markdown(mdc) # TODO: this should be optional + return 'Incubate '+location+' for '+duration+' at temperature '+temperature+', shaking at '+shakingFrequency+'\n' +primitive_to_markdown_functions[PLATE_HANDLING_PREFIX+'Incubate'] = plate_handling_incubate_to_markdown + + +############################################# +# Spectrophotometry primitives + +SPECTROPHOTOMETRY = 'https://bioprotocols.org/paml/primitives/spectrophotometry/' + + +def spectrophotometry_absorbance_to_markdown(executable, mdc: MarkdownConverter): + samples = executable.input_pin('samples').to_markdown(mdc) + wavelength = executable.input_pin('wavelength').to_markdown(mdc) + return 'Measure absorbance of '+samples+' at '+wavelength+'\n' +primitive_to_markdown_functions[SPECTROPHOTOMETRY+'MeasureAbsorbance'] = spectrophotometry_absorbance_to_markdown + + +def spectrophotometry_fluorescence_to_markdown(executable, mdc: MarkdownConverter): + samples = executable.input_pin('samples').to_markdown(mdc) + excitation = executable.input_pin('excitationWavelength').to_markdown(mdc) + # TODO: fix kludge: don't assume whether optionals are present + bp_wavelength = executable.input_pin('emissionBandpassWavelength').to_markdown(mdc) + #bp_width = executable.input_pin('emissionBandpassWidth').to_markdown(mdc) + emission = bp_wavelength #+' / '+bp_width + gain = executable.input_pin('gain').to_markdown(mdc) + return 'Measure fluorescence of '+samples+' at excitation '+excitation+' and emission '+emission+' with gain = '+gain+'\n' +primitive_to_markdown_functions[SPECTROPHOTOMETRY+'MeasureFluorescence'] = spectrophotometry_fluorescence_to_markdown + + +# TODO: add remaining primitives +# primitive_to_markdown_functions[SPECTROPHOTOMETRY+'MeasureFluorescenceSpectrum'] = spectrophotometry_to_markdown diff --git a/paml_convert/markdown/markdown_specialization.py b/paml_convert/markdown/markdown_specialization.py new file mode 100644 index 0000000..c86a9d8 --- /dev/null +++ b/paml_convert/markdown/markdown_specialization.py @@ -0,0 +1,185 @@ +import logging + +import sbol3 +import tyto + +import paml +import uml +from paml_convert.behavior_specialization import BehaviorSpecialization +from paml_convert.markdown import MarkdownConverter + +l = logging.getLogger(__file__) +l.setLevel(logging.ERROR) + +class MarkdownSpecialization(BehaviorSpecialization): + def __init__(self, out_path) -> None: + super().__init__() + self.out_path = out_path + self.markdown = "" + self.var_to_entity = {} + self.markdown_converter = None + self.markdown_steps = [] + self.doc = None + + def _init_behavior_func_map(self) -> dict: + return { + "https://bioprotocols.org/paml/primitives/sample_arrays/EmptyContainer": self.define_container, + "https://bioprotocols.org/paml/primitives/liquid_handling/Provision": self.provision_container, + "https://bioprotocols.org/paml/primitives/sample_arrays/PlateCoordinates": self.plate_coordinates, + "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance": self.measure_absorbance, + } + + def on_begin(self): + if self.execution: + protocol = self.execution.protocol.lookup() + self.markdown_converter = MarkdownConverter(protocol.document) + self.markdown += self.markdown_converter.markdown_header(protocol) + self.markdown += self._materials_markdown(protocol) + self.markdown += self._inputs_markdown(self.execution.parameter_values) + + def _inputs_markdown(self, parameter_values): + markdown = '\n\n## Protocol Inputs:\n' + for i in parameter_values: + parameter = i.parameter.lookup() + if parameter.property_value.direction == uml.PARAMETER_IN: + markdown += self._parameter_value_markdown(i) + return markdown + + def _outputs_markdown(self, parameter_values): + markdown = '\n\n## Protocol Outputs:\n' + for i in parameter_values: + parameter = i.parameter.lookup() + if parameter.property_value.direction == uml.PARAMETER_OUT: + markdown += self._parameter_value_markdown(i, True) + return markdown + + def _materials_markdown(self, protocol): + document_objects = protocol.document.objects + components = [x for x in protocol.document.objects if isinstance(x, sbol3.component.Component)] + materials = {x.name: x for x in components} + markdown = '\n\n## Protocol Materials:\n' + for name, material in materials.items(): + markdown += f"* [{name}]({material.types[0]})\n" + return markdown + + def _parameter_value_markdown(self, pv : paml.ParameterValue, is_output=False): + parameter = pv.parameter.lookup().property_value + value = pv.value.lookup().value if isinstance(pv.value, uml.LiteralReference) else pv.value.value + units = tyto.OM.get_term_by_uri(value.unit) if isinstance(value, sbol3.om_unit.Measure) else None + value = str(f"{value.value} {units}") if units else str(value) + if is_output: + return f"* `{parameter.name}`" + else: + return f"* `{parameter.name}` = {value}" + + def _steps_markdown(self): + markdown = '\n\n## Steps\n' + for i, step in enumerate(self.markdown_steps): + markdown += str(i + 1) + '. ' + step + '\n' + return markdown + + def on_end(self): + self.markdown += self._outputs_markdown(self.execution.parameter_values) + self.markdown_steps += [self.reporting_step()] + self.markdown += self._steps_markdown() + with open(self.out_path, "w") as f: + f.write(self.markdown) + + def reporting_step(self): + output_parameters = [] + for i in self.execution.parameter_values: + parameter = i.parameter.lookup() + value = i.value.value + if parameter.property_value.direction == uml.PARAMETER_OUT: + output_parameters.append(f"`{parameter.property_value.name}` from `{value}`") + output_parameters = ", ".join(output_parameters) + return f"Report values for {output_parameters}." + + def define_container(self, record: paml.ActivityNodeExecution): + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + spec = parameter_value_map["specification"]['value'] + samples_var = parameter_value_map["samples"]['value'] + + ## Define a container + + l.debug(f"define_container:") + l.debug(f" specification: {spec}") + l.debug(f" samples: {samples_var}") + + try: + possible_container_types = possible_container_types = self.resolve_container_spec(spec) + containers_str = ",".join([f"\n\t[{c.split('#')[1]}]({c})" for c in possible_container_types]) + self.markdown_steps += [f"Provision a container named `{samples_var.name}` such as: {containers_str}."] + except Exception as e: + l.warning(e) + self.markdown_steps += [f"Provision a container named `{samples_var.name}` meeting specification: {spec.queryString}."] + + return results + + # def provision_container(self, wells: WellGroup, amounts = None, volumes = None, informatics = None) -> Provision: + def provision_container(self, record: paml.ActivityNodeExecution): + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + destination = parameter_value_map["destination"]["value"] + #dest_wells = self.var_to_entity[destination] + value = parameter_value_map["amount"]["value"].value + units = parameter_value_map["amount"]["value"].unit + units = tyto.OM.get_term_by_uri(units) + resource = parameter_value_map["resource"]["value"] + #resource = self.resolutions[resource] + l.debug(f"provision_container:") + l.debug(f" destination: {destination}") + l.debug(f" amount: {value} {units}") + l.debug(f" resource: {resource}") + + resource_str = f"[{resource.name}]({resource.types[0]})" + destination_str = f"`{destination.source.lookup().value.lookup().value.name}({destination.mask})`" + self.markdown_steps += [f"Pipette {value} {units} of {resource_str} into {destination_str}."] + + + return results + + def plate_coordinates(self, record: paml.ActivityNodeExecution): + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + source = parameter_value_map["source"]["value"] + #container = self.var_to_entity[source] + coords = parameter_value_map["coordinates"]["value"] + + self.var_to_entity[parameter_value_map['samples']["value"]] = coords + l.debug(f"plate_coordinates:") + l.debug(f" source: {source}") + l.debug(f" coordinates: {coords}") + + return results + + def measure_absorbance(self, record: paml.ActivityNodeExecution): + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + wl = parameter_value_map["wavelength"]["value"] + wl_units = tyto.OM.get_term_by_uri(wl.unit) + samples = parameter_value_map["samples"]["value"] + #wells = self.var_to_entity[samples] + measurements = parameter_value_map["measurements"]["value"] + + # HACK extract contrainer from well group since we do not have it as input + container = None #wells[0].container + + l.debug(f"measure_absorbance:") + l.debug(f" container: {container}") + l.debug(f" samples: {samples}") + l.debug(f" wavelength: {wl.value} {wl_units}") + + # Add to markdown + + samples_str = f"`{samples.source.lookup().value.lookup().value.name}({samples.mask})`" + self.markdown_steps += [f'Make absorbance measurements (named `{measurements}`) of {samples_str} at {wl.value} {wl_units}.'] diff --git a/paml_convert/markdown/protocol_to_markdown.py b/paml_convert/markdown/protocol_to_markdown.py new file mode 100644 index 0000000..02858a5 --- /dev/null +++ b/paml_convert/markdown/protocol_to_markdown.py @@ -0,0 +1,426 @@ +import sbol3 +import paml +import uml +import paml.type_inference +import openpyxl +import numpy +from IPython.display import Markdown + + +########################################### +# Functions for reasoning about ranges + +# Transform an Excel-style range (col:row, inclusive, alpha-numeric) to numpy-style (row:col, start/stop, numeric) +def excel_to_numpy_range(excel_range): + bounds = openpyxl.utils.cell.range_boundaries(excel_range) + return [bounds[1]-1,bounds[0]-1,bounds[3],bounds[2]] + +def numpy_to_excel_range(top,left,bottom,right): + if top+1==bottom and left+1==right: # degenerate case of a single cell + return openpyxl.utils.cell.get_column_letter(left+1)+str(top+1) + else: + return openpyxl.utils.cell.get_column_letter(left+1)+str(top+1) + ":" + \ + openpyxl.utils.cell.get_column_letter(right) + str(bottom) + +def extract_range_from_top_left(region: numpy.ndarray): + # find the largest rectangular region starting at the first top-left zero + top = numpy.where(region)[0][0] + left = numpy.where(region)[1][0] + right = numpy.where(region[top,:])[0][-1]+1 + for bottom in range(top,region.shape[0]): + if not region[bottom,left:right].all(): + bottom -= 1 + break + bottom += 1 # adjust to stop coordinate + region[top:bottom, left:right] = False + return numpy_to_excel_range(top, left, bottom, right) + +def reduce_range_set(ranges): + assert len(ranges)>0, "Range set to reduce must have at least one element" + bounds = [max(openpyxl.utils.cell.range_boundaries(r)[i] for r in ranges) for i in range(2,4)] + region = numpy.zeros([bounds[1],bounds[0]],dtype=bool) # make an array of zeros + # mark each range in turn, ensuring that they don't overlap + for r in ranges: + nr = excel_to_numpy_range(r) + assert not (region[nr[0]:nr[2], nr[1]:nr[3]]).any(), ValueError("Found overlapping range in "+str(ranges)) + region[nr[0]:nr[2], nr[1]:nr[3]] = True + # pull chunks out until all zeros + reduced = set() + while region.any(): + reduced.add(extract_range_from_top_left(region)) + return reduced + + +############################################## +# Converter state object, to be carried along with "to_markdown" functions +# +# TODO: make this build PROV-O as it goes? +class MarkdownConverter(): + def __init__(self, document: sbol3.Document): + self.document = document + #self.protocol_typing = paml.type_inference.ProtocolTyping() + + def markdown_header(self, protocol): + header = '# ' + (protocol.display_id if (protocol.name is None) else protocol.name) + '\n' + header += '\n' + header += '## Description:\n' + ( + 'No description given' if protocol.description is None else protocol.description) + '\n' + return header + + # Entry-point for document conversion + # TODO: allow us to control the name of the output + def convert(self, execution, out=None): + # protocol argument can be either string, URI, or paml.Protocol + if not isinstance(execution, paml.ProtocolExecution): + execution = self.document.find(execution) + + # print('Inferring flow values') + # self.protocol_typing.infer_typing(protocol) + + print('Serializing activities') + serialized_noncontrol_activities = serialize_activities(execution) + + print('Writing markdown file') + markdown = write_markdown_file(execution, serialized_noncontrol_activities, self, out=out) + + print('Writing Excel file') + # write_excel_file(protocol, serialized_noncontrol_activities, self) + + print('Export complete') + return markdown + + + +# ############################################## +# # Direct conversion of individual objects to their markdown representations +# +# def list_to_markdown(l: list, mdc: MarkdownConverter): +# if len(l) == 0: +# return '' ## TODO: kludge: this shouldn't be happening +# this = l.pop().to_markdown(mdc) +# if len(l) == 0: +# return this +# if len(l) == 1: +# return this + ' and ' + list_to_markdown(l, mdc) +# else: +# return this + ', ' + list_to_markdown(l, mdc) +# +# def strlist_to_markdown(l: list, mdc: MarkdownConverter): +# if len(l) == 0: +# return '' ## TODO: kludge: this shouldn't be happening +# this = l.pop() +# if len(l) == 0: +# return this +# if len(l) == 1: +# return this + ' and ' + strlist_to_markdown(l, mdc) +# else: +# return this + ', ' + strlist_to_markdown(l, mdc) +# +# def measure_to_markdown(self: sbol3.Measure, mdc: MarkdownConverter): +# # TODO: convert large numbers to friendlier units +# unit = (mdc.document.find(self.unit).name if mdc.document.find(self.unit) else tyto.OM.get_term_by_uri(self.unit)) +# if unit=="number": # special case: don't need to say unit for pure numbers +# unit = '' +# return str(self.value) + ' ' + str(unit) +# sbol3.Measure.to_markdown = measure_to_markdown +# +# +# def component_to_markdown(self: sbol3.Component, mdc: MarkdownConverter): +# return '[' + (self.display_id if (self.name is None) else self.name) + ']('+self.types[0]+')' +# sbol3.Component.to_markdown = component_to_markdown +# +# +# def container_to_markdown(self: paml.Container, mdc: MarkdownConverter): +# return '[' + (self.display_id if (self.name is None) else self.name) + ']('+self.type+')' +# paml.Container.to_markdown = container_to_markdown +# +# +# def containercoodinates_to_markdown(self: paml.ContainerCoordinates, mdc: MarkdownConverter): +# return mdc.document.find(self.in_container).to_markdown(mdc) + ' ' + self.coordinates # TODO: figure out how to set document to enable changing doc.find to lookup +# paml.ContainerCoordinates.to_markdown = containercoodinates_to_markdown +# +# +# +# # Helper function for reducing coordinates shown for LocatedSamples +# def markdown_mergedlocations(location_list, mdc: MarkdownConverter): +# containers = [c for c in location_list if isinstance(c, paml.Container)] +# coords = {coord for coord in location_list if isinstance(coord, paml.ContainerCoordinates)} +# reduced = [] +# for c in {coord.in_container.lookup() for coord in coords}: +# ranges = reduce_range_set({l.coordinates for l in coords if l.in_container.lookup()==c}) +# for r in ranges: # BUG: should be a list comprehension, but in_container argument can't be set in constructor +# cc = paml.ContainerCoordinates(coordinates=r) +# cc.in_container = c +# reduced.append(cc) +# return list_to_markdown(containers+reduced, mdc) +# +# +# def locateddata_to_markdown(self: paml.LocatedData, mdc: MarkdownConverter): +# return self.from_samples.to_markdown(mdc) +# paml.LocatedData.to_markdown = locateddata_to_markdown +# +# def locatedsamples_to_markdown(self: paml.ReplicateSamples, _: MarkdownConverter): +# return self.name # TODO: can we do better than this kludge? +# paml.LocatedSamples.to_markdown = locatedsamples_to_markdown +# +# +# def replicatesamples_to_markdown(self: paml.ReplicateSamples, mdc: MarkdownConverter): +# return markdown_mergedlocations({mdc.document.find(x) for x in self.in_location}, mdc) +# paml.ReplicateSamples.to_markdown = replicatesamples_to_markdown +# +# +# def heterogeneoussamples_to_markdown(self: paml.HeterogeneousSamples, mdc: MarkdownConverter): +# return markdown_mergedlocations({mdc.document.find(loc) for rep in self.replicate_samples for loc in rep.in_location}, mdc) +# paml.HeterogeneousSamples.to_markdown = heterogeneoussamples_to_markdown +# +# +# def integerconstantpin_to_markdown(self: paml.IntegerConstantPin, mdc: MarkdownConverter): +# return str(self.value) +# paml.IntegerConstantPin.to_markdown = integerconstantpin_to_markdown +# +# def stringconstantpin_to_markdown(self: paml.StringConstantPin, mdc: MarkdownConverter): +# return str(self.value) +# paml.StringConstantPin.to_markdown = stringconstantpin_to_markdown +# +# +# def localvaluepin_to_markdown(self: paml.LocalValuePin, mdc: MarkdownConverter): +# return self.value.to_markdown(mdc) +# paml.LocalValuePin.to_markdown = localvaluepin_to_markdown +# +# +# def referencevaluepin_to_markdown(self: paml.ReferenceValuePin, mdc: MarkdownConverter): +# return self.value.lookup().to_markdown(mdc) +# paml.ReferenceValuePin.to_markdown = referencevaluepin_to_markdown +# +# # A non-constant pin needs to pull its value from the flow +# def pin_to_markdown(self: paml.Pin, mdc: MarkdownConverter): +# inflows = self.input_flows() +# assert len(inflows)==1, ValueError('Pin has more than one input flow: '+self.identity) +# value = mdc.protocol_typing.flow_values[inflows.pop()] +# return value.to_markdown(mdc) +# paml.Pin.to_markdown = pin_to_markdown +# +# ############################### +# # Base activities to markdown +# +# def primitiveexecutable_to_markdown(self: paml.PrimitiveExecutable, mdc: MarkdownConverter): +# stepwriter = primitive_to_markdown_functions[mdc.document.find(self.instance_of).identity] +# return stepwriter(self, mdc) +# paml.PrimitiveExecutable.to_markdown = primitiveexecutable_to_markdown +# +# def subcall_variable_to_markdown(pin,mdc): +# activity = pin.instance_of.lookup().activity.lookup() +# return pin.to_markdown(mdc) + " for " + (activity.description if activity.description else activity.name) +# +# def subprotocol_to_markdown(self: paml.SubProtocol, mdc: MarkdownConverter): +# protocol = self.instance_of.lookup() +# pname = (protocol.display_id if (protocol.name is None) else protocol.name) +# input_string = strlist_to_markdown([subcall_variable_to_markdown(pin,mdc) for pin in self.input], mdc) +# return 'Run protocol "'+pname+'" with inputs: '+input_string +# paml.SubProtocol.to_markdown = subprotocol_to_markdown +# +# +# def value_to_markdown(self: paml.Value, mdc: MarkdownConverter): +# if is_input_value(self): # This is an input value, used as a variable +# value = mdc.protocol_typing.flow_values[self.output_flows().pop()] +# return 'Protocol input: ' + value.to_markdown(mdc) +# else: # This is an output value, used for reporting +# value = mdc.protocol_typing.flow_values[self.input_flows().pop()] +# return 'Report values from ' + value.to_markdown(mdc) + '\n' +# paml.Value.to_markdown = value_to_markdown +# +# +############################# +# Other sorts of markdown functions + +def markdown_input(input : uml.Parameter, mdc: MarkdownConverter): + bullet = '* ' + str(input) + if input.description is not None: bullet += ': ' + input.description + bullet += '\n' + return bullet + + +# def markdown_material(component, mdc: MarkdownConverter): +# bullet = '* ' + component.to_markdown(mdc) +# if component.description is not None: bullet += ': ' + component.description +# bullet += '\n' +# return bullet +# +# +# def markdown_container_toplevel(container, mdc: MarkdownConverter): +# # TODO: generalize ontology option for type +# bullet = '* ' + container.to_markdown(mdc) + ' (['+tyto.NCIT.get_term_by_uri(container.type)+']('+container.type+'))' +# if container.description is not None: bullet += ': ' + container.description +# bullet += '\n' +# return bullet +# +# +# +# ############################## +# # Serialize order of steps +# +# def unpin_activity(protocol, activity): +# return activity.get_parent() if isinstance(activity, paml.Pin) else activity +# +# def is_input_value(x): +# return isinstance(x,paml.Value) and \ +# not({f for f in x.input_flows() if not isinstance(f.source.lookup(), paml.Initial)}) +# +def serialize_activities(execution: paml.ProtocolExecution): + serialized_activities = [] + + for execution in execution.executions: + if isinstance(execution, paml.CallBehaviorExecution): + execution_node = execution.node.lookup() + serialized_activities.append(execution_node) + + # assert isinstance(serialized_activities[0], paml.Initial) + # assert isinstance(serialized_activities[-1], paml.Final) + + # filter out control flow statements + serialized_noncontrol_activities = [x for x in serialized_activities if (not isinstance(x, uml.ControlNode)) and (not isinstance(x, uml.ObjectNode))] + serialized_noncontrol_activities.reverse() + return serialized_noncontrol_activities + +############################## +# Write to a markdown file + +def write_markdown_file(execution: paml.ProtocolExecution, serialized_noncontrol_activities, mdc: MarkdownConverter, out=None): + protocol = execution.protocol.lookup() + markdown = mdc.markdown_header(protocol) + + markdown += '\n\n## Protocol Inputs:\n' + for i in protocol.parameters: + if i.property_value.direction == uml.PARAMETER_IN: + markdown += markdown_input(i.property_value, mdc) + + markdown += '\n\n## Materials\n' + # for material in protocol.material: + # file.write(markdown_material(material.lookup(), mdc)) + + markdown += '\n\n## Containers\n' + # for container in (x for x in protocol.locations if isinstance(x, paml.Container)): + # file.write(markdown_container_toplevel(container, mdc)) + + markdown += '\n\n## Steps\n' + for step in range(len(serialized_noncontrol_activities)): + print('Writing step '+str(step)+": "+serialized_noncontrol_activities[step].identity) + #file.write('### Step ' + str(step + 1) + '\n' + serialized_noncontrol_activities[step].to_markdown(mdc) + '\n') + # file.write(str(step + 1) + '. ' + serialized_noncontrol_activities[step].to_markdown(mdc) + '\n') + markdown += str(step + 1) + '. ' + serialized_noncontrol_activities[step].behavior + '\n' + + markdown = Markdown(markdown) + + # make sure the file is fully written + if out: + with open(out, 'w') as file: + markdown.filename = out + file.write(markdown.data) + file.flush() + file.close() + + print('Finished writing markdown') + return markdown + +# ############################## +# # Write to an accompanying Excel file +# +# def excel_container_name(container): +# return (container.display_id if (container.name is None) else container.name) +# +# # def excel_write_container(ws, row_offset, container): +# # return '[' + (container.display_id if (container.name is None) else container.name) + ']('+container.type+')' +# +# def excel_write_containercoodinates(ws, row_offset, col_offset, coordinates, specification_URI): +# fixed_style = ws['A2'].fill +# entry_style = ws['C2'].fill +# +# # get the column letter +# col = openpyxl.utils.cell.get_column_letter(col_offset+1) +# # make the header +# ws[col+str(row_offset)] = excel_container_name(coordinates.in_container.lookup()) +# # write the materials +# block = openpyxl.utils.cell.range_boundaries(coordinates.coordinates) +# # use plate coordinates, which are the opposite of excel coordinates +# height = block[2]-block[0]+1 +# width = block[3]-block[1]+1 +# # write the plate coordinate frame +# for plate_row in range(height): +# coord = col+str(row_offset+plate_row+2) +# ws[coord] = openpyxl.utils.cell.get_column_letter(block[0]+plate_row) +# ws[coord].fill = copy(fixed_style) +# for plate_col in range(width): +# coord = openpyxl.utils.cell.get_column_letter(col_offset+plate_col+2)+str(row_offset+1) +# ws[coord] = str(block[1]+plate_col) +# ws[coord].fill = copy(fixed_style) +# # style the blanks +# for plate_row in range(height): +# for plate_col in range(width): +# coord = openpyxl.utils.cell.get_column_letter(col_offset+plate_col+2)+str(row_offset+plate_row+2) +# ws[coord].fill = copy(entry_style) +# ws[coord].alignment = openpyxl.styles.Alignment(horizontal="center") +# plate_coord = openpyxl.utils.cell.get_column_letter(block[0]+plate_row)+str(block[1]+plate_col) +# ws[coord].comment = openpyxl.comments.Comment(coordinates.in_container+"_"+plate_coord+" "+specification_URI, "PAML autogeneration, do not modify", height=24, width=1000) +# ws[coord].protection = openpyxl.styles.Protection(locked=False) +# +# return (height+2, width+1) +# +# def excel_write_location(ws, row_offset, col_offset, location, specification_URI): +# if isinstance(location, paml.ContainerCoordinates): +# return excel_write_containercoodinates(ws, row_offset, col_offset, location, specification_URI) +# # elif isinstance(location, paml.Container): +# # return excel_write_container(location) +# else: +# return str(location) +# +# def excel_write_mergedlocations(ws, row_offset, location_spec_list): +# col_offset = 0 +# block_height = 0 +# while location_spec_list: +# pair = location_spec_list.popitem() +# block = excel_write_location(ws, row_offset, col_offset, pair[0], pair[1]) +# col_offset += block[1] + 1 +# block_height = max(block_height,block[0]) +# return block_height +# +# # TODO: consolidate the Excel reporting squares, like we've already done for the protocol above +# def excel_write_flow_value(document, value, ws, row_offset): +# if isinstance(value, paml.LocatedData): +# value = value.from_samples # unwrap value +# if isinstance(value, paml.ReplicateSamples): +# return excel_write_mergedlocations(ws, row_offset, {x.lookup():value.specification for x in value.in_location}) +# elif isinstance(value, paml.HeterogeneousSamples): +# return excel_write_mergedlocations(ws, row_offset, {document.find(loc):rep.specification for rep in value.replicate_samples for loc in rep.in_location}) +# # if we fall through to here: +# return str(value) +# +# +# def write_excel_file(protocol, serialized_noncontrol_activities, mdc: MarkdownConverter): +# template_path = posixpath.join(os.path.dirname(os.path.realpath(__file__)),'template.xlsx') +# wb = openpyxl.load_workbook(filename=template_path) +# ws = wb.active # get the default worksheet +# +# # write header & metadata +# ws.title = "Data Reporting" +# ws.protection.enable() +# ws['D1'] = protocol.name +# ws['D1'].comment = openpyxl.comments.Comment(protocol.identity, "PAML autogeneration, do not modify", height=24, +# width=1000) +# for row in ws['C2:C4']: +# for cell in row: cell.protection = openpyxl.styles.Protection(locked=False) # unlock metadata locations +# header_style = ws['A1'].font +# row_offset = 7 # starting point for entries +# +# # write each value set, incrementing each time +# value_steps = (step for step in range(len(serialized_noncontrol_activities)) if +# isinstance(serialized_noncontrol_activities[step], paml.Value)) +# for step in value_steps: +# coord = 'A' + str(row_offset) +# ws[coord] = 'Report from Step ' + str(step + 1) +# ws[coord].font = copy(header_style) +# value_locations = mdc.protocol_typing.flow_values[serialized_noncontrol_activities[step].input_flows().pop()] +# block_height = excel_write_flow_value(mdc.document, value_locations, ws, row_offset + 1) +# row_offset += block_height + 2 +# +# wb.save(protocol.display_id + '.xlsx') +# diff --git a/paml_convert/markdown/template.xlsx b/paml_convert/markdown/template.xlsx new file mode 100644 index 0000000..7209ef9 Binary files /dev/null and b/paml_convert/markdown/template.xlsx differ diff --git a/paml_convert/ot2/__init__.py b/paml_convert/ot2/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/paml_convert/ot2/__init__.py @@ -0,0 +1 @@ + diff --git a/paml_convert/ot2/ot2_specialization.py b/paml_convert/ot2/ot2_specialization.py new file mode 100644 index 0000000..1665c82 --- /dev/null +++ b/paml_convert/ot2/ot2_specialization.py @@ -0,0 +1,205 @@ + +import json +from typing import Dict +import sbol3 +import tyto +import paml +from objexplore import explore +import rich +from paml_convert.behavior_specialization import BehaviorSpecialization + +# FIXME if container ontology is needed, then adapt for use here. +# from container_api.client_api import matching_containers, strateos_id + + +import logging + +l = logging.getLogger(__file__) +l.setLevel(logging.ERROR) + +class OT2Specialization(BehaviorSpecialization): + def __init__(self,apilevel,leftTipJson,rightTipJson,resolutions: Dict[sbol3.Identified,str] = None) -> None: + super().__init__() + self.resolutions = resolutions + self.var_to_entity = {} + self.script = "" + self.all_steps = [] + self.leftTipJson = leftTipJson + self.rightTipJson = rightTipJson + self.apilevel = apilevel + + # Needed for using container ontology + self.container_api_addl_conditions = "(cont:availableAt value )" + + + def _init_behavior_func_map(self) -> dict: + """ + This function redirects processing of each primitive to the functions + defined below. Adding additional mappings here is most likely required. + """ + return { + "https://bioprotocols.org/paml/primitives/sample_arrays/EmptyContainer" : self.define_container, + "https://bioprotocols.org/paml/primitives/liquid_handling/Provision" : self.provision_container, + "https://bioprotocols.org/paml/primitives/sample_arrays/PlateCoordinates" : self.plate_coordinates, + "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance" : self.measure_absorbance, + "https://bioprotocols.org/paml/primitives/wait/WaitForTime" : self.time_wait + } + + def on_begin(self): + + protocol = self.execution.protocol.lookup() + apilevel = self.apilevel + self.script += f"#Protocol Name:{protocol.name}\n\n\n" + self.script += "from opentrons import protocol_api\n\n" + self.script += "metadata = {'apiLevel': '"+apilevel+"'\}\n\n" + self.script += self._tipracks(json.loads(self.leftTipJson),json.loads(self.rightTipJson)) + self.script += self._materials(protocol) + + + def on_end(self): + self.script += self._steps() + + def _steps(self): + markdown = '\n#Steps\n' + for i, step in enumerate(self.all_steps): + # markdown += str(i + 1) + '. ' + step + '\n' + markdown += step + '\n' + return markdown + + def _tipracks(self, left,right): + leftTipID = left["pipette"] + rightTipID = right["pipette"] + tipCount=0 + tipList="" + markdown="" + for tiprack in left["tipracks"]: + tiprackID = tiprack["id"] + tiprackDeck = tiprack["deck"] + tipList += f"leftTiprack{tipCount}," + markdown += f"leftTiprack{tipCount} = protocol.load_labware('{tiprackID}', {tiprackDeck})\n" + tipCount+=1 + markdown += f"left = protocol.load_instrument('{leftTipID}', 'left', tip_rack={tipList[:-1]})\n" + tipCount=0 + tipList="" + for tiprack in right["tipracks"]: + tiprackID = tiprack["id"] + tiprackDeck = tiprack["deck"] + tipList += f"rightTiprack{tipCount}," + markdown += f"rightTiprack{tipCount} = protocol.load_labware('{tiprackID}', {tiprackDeck})\n" + tipCount+=1 + markdown += f"right = protocol.load_instrument('{rightTipID}', 'right', tip_rack={tipList[:-1]})\n" + return markdown + + def _materials(self, protocol): + document_objects = protocol.document.objects + components = [x for x in protocol.document.objects if isinstance(x, sbol3.component.Component)] + materials = {x.display_id: x for x in components} + markdown = '\n\n#Protocol Materials\n' + labware = set() + for name, material in materials.items(): + OT2Props = json.loads(material.OT2SpecificProps) + source = OT2Props["source"] + if source in labware: + markdown += f"#[{name}]({material.types[0]})\n" + else: + type = OT2Props["type"] + deck = OT2Props["deck"] + markdown += f"#[{name}]({material.types[0]})\n" + markdown += f"{source} = protocol.load_labware('{type}', {deck})\n" + labware.add(OT2Props["source"]) + + return markdown + + def _parameter_value_markdown(self, pv : paml.ParameterValue, is_output=False): + parameter = pv.parameter.lookup().property_value + value = pv.value.lookup().value if isinstance(pv.value, uml.LiteralReference) else pv.value.value + units = tyto.OM.get_term_by_uri(value.unit) if isinstance(value, sbol3.om_unit.Measure) else None + value = str(f"{value.value} {units}") if units else str(value) + if is_output: + return f"* `{parameter.name}`" + else: + return f"* `{parameter.name}` = {value}" + + def define_container(self, record: paml.ActivityNodeExecution): + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + spec = parameter_value_map["specification"]['value'] + samples_var = parameter_value_map["samples"]['value'] + + # FIXME the protocol var_to_entity mapping links variables created in + # the execution trace with real values assigned here, such as + # container below. This mapping can be used in later processing to + # reuse bindings. + # + # self.var_to_entity[samples_var] = container + + l.debug(f"define_container:") + l.debug(f" specification: {spec}") + l.debug(f" samples: {samples_var}") + OT2Props = json.loads(spec.OT2SpecificProps) + OT2Deck = OT2Props["deck"] + + self.all_steps += [f"{spec.name} = protocol.load_labware('{spec.queryString}', {OT2Deck})"] + + def time_wait(self, record: paml.ActivityNodeExecution): + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + value = parameter_value_map["amount"]["value"].value + units = parameter_value_map["amount"]["value"].unit + self.all_steps += [f"time.sleep(value)"] + + + def provision_container(self, record: paml.ActivityNodeExecution): + results = {} + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + destination = parameter_value_map["destination"]["value"] + value = parameter_value_map["amount"]["value"].value + units = parameter_value_map["amount"]["value"].unit + units = tyto.OM.get_term_by_uri(units) + resource = parameter_value_map["resource"]["value"] + l.debug(f"provision_container:") + l.debug(f" destination: {destination}") + l.debug(f" amount: {value} {units}") + l.debug(f" resource: {resource}") + + OT2Props = json.loads(resource.OT2SpecificProps) + OT2Source = OT2Props["source"] + try: + OT2Pipette=OT2Props["pipette"] + except: + OT2Pipette="left" + try: + OT2Coordinates=OT2Props["coordinates"] + except: + OT2Coordinates="A1" + destination_str = f"{destination.mask}" + self.all_steps += [f"{OT2Pipette}.transfer({value},{OT2Source}['{OT2Coordinates}'],{destination_str})"] + + def plate_coordinates(self, record: paml.ActivityNodeExecution): + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + source = parameter_value_map["source"]["value"] + coords = parameter_value_map["coordinates"]["value"] + + l.debug(f"plate_coordinates:") + l.debug(f" source: {source}") + l.debug(f" coordinates: {coords}") + + + def measure_absorbance(self, record: paml.ActivityNodeExecution): + call = record.call.lookup() + parameter_value_map = call.parameter_value_map() + + wl = parameter_value_map["wavelength"]["value"] + wl_units = tyto.OM.get_term_by_uri(wl.unit) + samples = parameter_value_map["samples"]["value"] + + l.debug(f"measure_absorbance:") + l.debug(f" samples: {samples}") + l.debug(f" wavelength: {wl.value} {wl_units}") + samples_str = f"`{samples.source.lookup().value.lookup().value.name}({samples.mask})`" + self.all_steps +=[f'protocol.comment(\'Make absorbance measurements (named `{measurements}`) of {samples_str} at {wl.value} {wl_units}.\')'] + diff --git a/paml_time/__init__.py b/paml_time/__init__.py new file mode 100644 index 0000000..d1d129b --- /dev/null +++ b/paml_time/__init__.py @@ -0,0 +1,102 @@ +import os +import posixpath +from sbol_factory import SBOLFactory, UMLFactory +import sbol3 +import uml # Note: looks unused, but is used in SBOLFactory +import paml_time as pamlt +import tyto + +# Import ontology +SBOLFactory("paml_time_submodule", + posixpath.join(os.path.dirname(os.path.realpath(__file__)), 'paml_time.ttl'), + 'http://bioprotocols.org/paml-time#') + +# Import submodule symbols into top-level module +from paml_time_submodule import * + + +# Helper functions +class MalformedInterval(Exception): + pass + +## Start and end time constraints + +def startTime(element, interval, units=tyto.OM.second): + return constrainTimePoint(element, interval, units=units, first=True) + +def endTime(element, interval, units=tyto.OM.second): + return constrainTimePoint(element, interval, units=units, first=False) + +def _getUMLInterval(interval, intervalType, units=tyto.OM.second): + if isinstance(interval, list) and len(interval) == 2: + min = interval[0] + max = interval[1] + elif isinstance(interval, int) or isinstance(interval, float): + min = interval + max = interval + else: + raise MalformedInterval(f"Cannot constrain time point with interval: {interval}") + + uml_interval = intervalType( + min=uml.TimeExpression(expr=pamlt.TimeMeasure(expr=sbol3.Measure(min, units))), + max=uml.TimeExpression(expr=pamlt.TimeMeasure(expr=sbol3.Measure(max, units))) + ) + return uml_interval + +def constrainTimePoint(element : uml.Behavior, interval, units=tyto.OM.second, first=True): + return timePointExpression(element, _getUMLInterval(interval, uml.TimeInterval, units=units), first=first) + +def timePointExpression(element : uml.Behavior, interval : uml.TimeInterval, first=True): + name = f"{element.identity}_start" if first else f"{element.identity}_end" + return uml.TimeConstraint(constrained_elements=[_referencedOrderedPropertyValue(0, element)], + specification=interval, + firstEvent=_orderedPropertyValue(0, uml.LiteralBoolean(value=first))) + +## Duration Constraints + +def duration(element : uml.Behavior, interval, units=tyto.OM.second): + return constrainDuation(element, interval, units=units) + +def constrainDuation(element : uml.Behavior, interval, units=tyto.OM.second): + return durationExpression(element, _getUMLInterval(interval, uml.DurationInterval, units=units)) + +def durationExpression(element : uml.Behavior, interval : uml.DurationInterval): + name = f"{element.identity}_duration" + return uml.DurationConstraint(constrained_elements=[_referencedOrderedPropertyValue(0, element)], + specification=interval) + +## Allen relations + +def binaryDuration(element1 : uml.Behavior, first1 : bool, + interval : uml.DurationInterval, + element2 : uml.Behavior, first2 : bool): + name1 = f"{element1.identity}_start" if first1 else f"{element1.identity}_end" + name2 = f"{element2.identity}_start" if first2 else f"{element2.identity}_end" + + ordered_elements = [_referencedOrderedPropertyValue(0, element1), + _referencedOrderedPropertyValue(1, element2)] + + return uml.DurationConstraint( + constrained_elements=ordered_elements, + specification=interval, + firstEvent=[_orderedPropertyValue(0, uml.LiteralBoolean(value=first1)), + _orderedPropertyValue(1, uml.LiteralBoolean(value=first2))] + ) + +def precedes(element1 : uml.Behavior, interval, element2 : uml.Behavior, units=tyto.OM.second): + return binaryDuration(element1, False, + _getUMLInterval(interval, uml.DurationInterval, units=units), + element2, True) + +def _orderedPropertyValue(i : int, value): + return uml.OrderedPropertyValue(index=i, property_value=value) + +def _referencedOrderedPropertyValue(i : int, value): + return pamlt.ReferencedOrderedPropertyValue(index=i, property_value=value) + +## Logical constraints + +def And(elements): + name = "and" #TODO use a more descriptive name + ordered_elements = [_orderedPropertyValue(i, e) for i, e in enumerate(elements)] + return AndConstraint(constrained_elements=ordered_elements) \ No newline at end of file diff --git a/paml_time/paml_time.ttl b/paml_time/paml_time.ttl new file mode 100644 index 0000000..8ce4a6a --- /dev/null +++ b/paml_time/paml_time.ttl @@ -0,0 +1,108 @@ +@prefix : . +@prefix om: . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix paml: . +@prefix pamlt: . +@prefix rdfs: . +@prefix sbol: . +@prefix uml: . +@prefix MathM: . +@prefix prov: . +@base . + + rdf:type owl:Ontology ; + owl:imports paml:, uml:, sbol: , om: ; + rdfs:comment "Time Extension for the Protocol Activity Modeling Languge (PAML) ontology." ; + owl:versionInfo "0.1" . + +################################################################# +# Annotation properties +################################################################# + +### http://www.w3.org/2002/07/owl#maxCardinality +owl:maxCardinality rdf:type owl:AnnotationProperty . +owl:minCardinality rdf:type owl:AnnotationProperty . + + +################################################################# +# Datatypes +################################################################# + +### http://www.w3.org/2001/XMLSchema#anySimpleType +xsd:anySimpleType rdf:type rdfs:Datatype . + +################################################################# +# Time properties +################################################################# + +pamlt:TimeMeasure rdf:type owl:Class ; + rdfs:subClassOf uml:ValueSpecification , + [ rdf:type owl:Restriction ; owl:allValuesFrom sbol:Measure ; owl:onProperty pamlt:measureValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +pamlt:measureValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain pamlt:TimeMeasure; + rdfs:range sbol:Measure ; + rdfs:label "expr" . + +pamlt:BooleanConstraint rdf:type owl:Class ; + rdfs:comment "Abstract class for boolean operators" ; + rdfs:subClassOf uml:Constraint , + [ rdf:type owl:Restriction ; owl:allValuesFrom pamlt:OwnedOrderedPropertyValue ; owl:onProperty pamlt:constrainedElement ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:specification ; owl:allValuesFrom uml:ValueSpecification ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:specification ; owl:minCardinality "0"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:specification ; owl:maxCardinality "0"^^xsd:nonNegativeInteger ] . + + +pamlt:constrainedElement rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain pamlt:BooleanConstraint ; + rdfs:range pamlt:OwnedOrderedPropertyValue ; # equivalent to uml:Element in this context + rdfs:label "constrained_elements" . + +pamlt:ReferencedOrderedPropertyValue rdf:type owl:Class ; + rdfs:subClassOf uml:OrderedPropertyValue , + [ rdf:type owl:Restriction ; owl:allValuesFrom xsd:integer ; owl:onProperty uml:indexValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom sbol:Identified ; owl:onProperty pamlt:referencedPropertyValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +pamlt:referencedPropertyValue rdf:type owl:ObjectProperty ; + #rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain pamlt:ReferencedOrderedPropertyValue ; + rdfs:range sbol:Identified ; + rdfs:label "property_value" . + +pamlt:AndConstraint rdf:type owl:Class ; + rdfs:subClassOf pamlt:BooleanConstraint . + +pamlt:OrConstraint rdf:type owl:Class ; + rdfs:subClassOf pamlt:BooleanConstraint . + +pamlt:XorConstraint rdf:type owl:Class ; + rdfs:subClassOf pamlt:BooleanConstraint . + +pamlt:Not rdf:type owl:Class ; + rdfs:subClassOf pamlt:BooleanConstraint , + [ rdf:type owl:Restriction ; owl:onClass sbol:Identified ; owl:onProperty uml:constrainedElement ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +pamlt:TimeConstraints rdf:type owl:Class ; + rdfs:subClassOf sbol:TopLevel , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:Constraint ; owl:onProperty pamlt:constraints ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom paml:Protocol ; owl:onProperty pamlt:protocols ] . + +pamlt:constraints rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain pamlt:TimeConstraints; + rdfs:range uml:Constraint ; + rdfs:label "constraints" . + +pamlt:protocols rdf:type owl:ObjectProperty ; + rdfs:domain pamlt:TimeConstraints; + rdfs:range paml:Protocol ; + rdfs:label "protocols" . diff --git a/secrets/strateos_secrets.json.sample b/secrets/strateos_secrets.json.sample new file mode 100644 index 0000000..d184054 --- /dev/null +++ b/secrets/strateos_secrets.json.sample @@ -0,0 +1,9 @@ +{ +"analytics": true, +"api_root": "https://secure.transcriptic.com", +"email": "Your Email", +"feature_groups": [], +"organization_id": "Your org id", +"token": "Your Token", +"user_id": "Your uid" +} \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..84cf892 --- /dev/null +++ b/setup.py @@ -0,0 +1,45 @@ +from setuptools import setup +import sys +import os +import subprocess + +test_deps = [ + 'nbmake', + 'pytest-xdist' +] +extras = { + 'test': test_deps, +} + +setup(name='pypaml', + description='Protocol Activity Modeling Language', + version='1.0a1', + license='MIT', + license_files=('LICENSE.txt'), + install_requires=[ + 'sbol3>=1.0b6', + 'sparqlwrapper>=1.8.5', + 'python-dateutil>=2.8.1', + 'sbol-factory==1.0a8', + 'requests', + 'graphviz', + 'tyto', + 'numpy', + 'openpyxl', + 'autoprotocol', + 'transcriptic', + 'requests_html', + "ipython", + "pre-commit", + "ipywidgets", + ], + tests_require=test_deps, + extras_require=extras, + packages=['paml', 'paml_convert', 'paml_convert.autoprotocol', 'paml_convert.markdown','paml_convert.ot2', 'paml.lib', 'paml_time', 'uml'], + package_data={'paml': ['paml.ttl', 'lib/*.ttl'], + 'paml_convert': ['markdown/template.xlsx'], + 'uml': ['uml.ttl'], + 'paml_time': ['paml_time.ttl']}, + + include_package_data=True, +) diff --git a/test/DISABLED_convert.py b/test/DISABLED_convert.py new file mode 100644 index 0000000..ad13a4e --- /dev/null +++ b/test/DISABLED_convert.py @@ -0,0 +1,93 @@ +import filecmp +import os +import tempfile + +import pytest + +import paml +import sbol3 +import tyto +import uml +import unittest + +from paml_convert.autoprotocol.autoprotocol_specialization import AutoprotocolSpecialization + +from paml_convert.autoprotocol.strateos_api import StrateosAPI, StrateosConfig +from paml.execution_engine import ExecutionEngine +from paml_convert.markdown.markdown_specialization import MarkdownSpecialization + + +class TestConvert(unittest.TestCase): + @pytest.mark.skip(reason="need to put strateos_secrets.json credentials on github first") + def test_igem_ludox(self): + + ############################################# + # Set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + + ############################################# + # Get the protocol + protocol_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "testfiles", "igem_ludox_test.nt") + doc.read(protocol_file, 'nt') + protocol = doc.find("https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018") + + ############################################# + # Autoprotocol and Strateos Configuration + autoprotocol_output = os.path.join(tempfile.gettempdir(), 'igem_ludox_autoprotocol.json') + secrets_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../secrets/strateos_secrets.json") + api = StrateosAPI(cfg=StrateosConfig.from_file(secrets_file)) + resolutions = { + doc.find("https://bbn.com/scratch/LUDOX"): "rs1b6z2vgatkq7", + doc.find("https://bbn.com/scratch/ddH2O"): "rs1c7pg8qs22dt", + "container_id": "ct1g9qsg4wx6gcj" + } + autoprotocol_specialization = AutoprotocolSpecialization(autoprotocol_output, api, resolutions) + + ############################################# + # Markdown Configuration + markdown_output = os.path.join(tempfile.gettempdir(), 'igem_ludox_markdown.md') + markdown_specialization = MarkdownSpecialization(markdown_output) + + ############################################# + # Execution Configuration + ee = ExecutionEngine(specializations=[autoprotocol_specialization, markdown_specialization]) + agent = sbol3.Agent("test_agent") + parameter_values = [ + paml.ParameterValue(parameter=protocol.get_input("wavelength"), + value=uml.LiteralIdentified(value=sbol3.Measure(100, tyto.OM.nanometer))) + ] + + ############################################# + # Execute Protocol and Convert + execution = ee.execute(protocol, agent, id="test_execution", parameter_values=parameter_values) + + + ############################################# + # Check outputs match + + # Check Markdown output + markdown_comparison_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'testfiles', 'igem_ludox_markdown.md') + # Uncomment next two lines to write the rubric file (Careful!) + # with open(markdown_comparison_file, "w") as out_file, open(markdown_output) as in_file: + # out_file.write(in_file.read()) + + print(f'Comparing against {markdown_comparison_file}') + assert filecmp.cmp(markdown_output, markdown_comparison_file), "Markdown files are not identical" + print('File identical with test file') + + # Check Autoprotocol output + autoprotocol_comparison_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), + 'testfiles', 'igem_ludox_autoprotocol.json') + # Uncomment next two lines to write the rubric file (Careful!) + # with open(autoprotocol_comparison_file, "w") as out_file, open(autoprotocol_output) as in_file: + # out_file.write(in_file.read()) + + print(f'Comparing against {autoprotocol_comparison_file}') + assert filecmp.cmp(autoprotocol_output, autoprotocol_comparison_file), "Autoprotocol files are not identical" + print('File identical with test file') + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/test/DISABLED_execution.py b/test/DISABLED_execution.py new file mode 100644 index 0000000..d7a9b5a --- /dev/null +++ b/test/DISABLED_execution.py @@ -0,0 +1,88 @@ +import os +import tempfile +import unittest +import filecmp +import sbol3 +import paml +import tyto +from paml.execution_engine import ExecutionEngine + +import logging +l = logging.getLogger(__file__) +l.setLevel(logging.ERROR) + + +class TestProtocolExecution(unittest.TestCase): + def _compare_execution_line(self, line): + """ + Return whether line satisfies a condition: + 1. Does not include a timepoint specification + :param line: + :return: + """ + return "http://www.w3.org/ns/prov#startedAtTime" not in line and \ + "http://www.w3.org/ns/prov#endedAtTime" not in line + + def _compare_execution_files(self, fn1, fn2): + with open(fn1, "r") as f1, open(fn2, "r") as f2: + f1 = filter(self._compare_execution_line, f1.readlines()) + f2 = filter(self._compare_execution_line, f2.readlines()) + mismatches = [(x, y) for x, y in zip(f1, f2) if x != y] + l.error(f"Executions in files: {fn1} and {fn2} have the following mismatches: \n{mismatches}") + return len(mismatches) == 0 + + def test_execute_protocol(self): + ############################################# + # set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + + protocol_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), "testfiles", "igem_ludox_test.nt") + doc.read(protocol_file, 'nt') + + protocol = doc.find("https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018") + agent = sbol3.Agent("test_agent") + + # print('Importing libraries') + # paml.import_library('liquid_handling') + # print('... Imported liquid handling') + + ee = ExecutionEngine() + parameter_values = [ + paml.ParameterValue(parameter=protocol.get_input("wavelength"), value=sbol3.Measure(100, tyto.OM.nanometer)) + ] + execution = ee.execute(protocol, agent, id="test_execution", parameter_values=parameter_values) + + #dot = execution.to_dot() + #dot.render(f'{protocol.name}.gv') + #dot.view() # uncomment to see it on your own screen + + ######################################## + # Validate and write the document + print('Validating and writing protocol') + v = doc.validate() + assert len(v) == 0, "".join(f'\n {e}' for e in v) + + #temp_name = os.path.join(tempfile.gettempdir(), 'igem_ludox_test_exec.nt') + #doc.write(temp_name, sbol3.SORTED_NTRIPLES) + #print(f'Wrote file as {temp_name}') + + #comparison_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'testfiles', 'igem_ludox_test_exec.nt') + #doc.write(comparison_file, sbol3.SORTED_NTRIPLES) + #print(f'Comparing against {comparison_file}') + #assert filecmp.cmp(temp_name, comparison_file), "Files are not identical" + #assert self._compare_execution_files(temp_name, comparison_file), "Files are not identical" + #print('File identical with test file') + + # def test_protocol_to_markdown(self): + # doc = sbol3.Document() + # doc.read('test/testfiles/igem_ludox_test.nt', 'nt') + # markdown.MarkdownConverter(doc).convert('iGEM_LUDOX_OD_calibration_2018') + + # Checking if files are identical needs to wait for increased stability + # assert filecmp.cmp('iGEM_LUDOX_OD_calibration_2018.md','test/testfiles/iGEM_LUDOX_OD_calibration_2018.md') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/owl_test_app/app/build.gradle b/test/owl_test_app/app/build.gradle new file mode 100644 index 0000000..b5f4073 --- /dev/null +++ b/test/owl_test_app/app/build.gradle @@ -0,0 +1,34 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java application project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/7.1.1/userguide/building_java_projects.html + */ + +plugins { + // Apply the application plugin to add support for building a CLI application in Java. + id 'application' +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use JUnit test framework. + testImplementation 'junit:junit:4.13.2' + + // This dependency is used by the application. + implementation 'com.google.guava:guava:30.1-jre' + + // owl-api + implementation 'net.sourceforge.owlapi:owlapi-distribution:4.5.4' + implementation 'net.sourceforge.owlapi:org.semanticweb.hermit:1.3.8.413' +} + +application { + // Define the main class for the application. + mainClass = 'owlet.App' +} diff --git a/test/owl_test_app/app/resources/paml-bad-restrictions.ttl b/test/owl_test_app/app/resources/paml-bad-restrictions.ttl new file mode 100644 index 0000000..149db0e --- /dev/null +++ b/test/owl_test_app/app/resources/paml-bad-restrictions.ttl @@ -0,0 +1,153 @@ +@prefix : . +@prefix om: . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix paml: . +@prefix rdfs: . +@prefix sbol: . +@prefix uml: . +@prefix MathM: . +@base . + + rdf:type owl:Ontology ; + owl:imports uml:, sbol: , om: ; + rdfs:comment "Protocol Activity Modeling Languge (PAML) ontology." ; + owl:versionInfo "0.3" . + +################################################################# +# Annotation properties +################################################################# + +### http://www.w3.org/2002/07/owl#maxCardinality +owl:maxCardinality rdf:type owl:AnnotationProperty . +owl:minCardinality rdf:type owl:AnnotationProperty . + + +################################################################# +# Datatypes +################################################################# + +### http://www.w3.org/2001/XMLSchema#anySimpleType +xsd:anySimpleType rdf:type rdfs:Datatype . + + +################################################################# +# Protocol definitions, based on UML +################################################################# + +paml:Protocol rdf:type owl:Class ; + rdfs:comment "A protocol is an activity in the laboratory" ; + rdfs:subClassOf uml:Activity . + + +paml:Primitive rdf:type owl:Class ; + rdfs:comment "A behavior that is a base-level library function" ; + rdfs:subClassOf uml:Behavior . + + +################################################################# +# Execution record, bootstrapping from PROV-O +################################################################# + +paml:Execution rdf:type owl:Class ; + rdfs:comment "An execution is a record of the values produced by the operation of a protocol, real or simulated" ; + rdfs:subClassOf sbol:TopLevel . + + +################################################################# +# Sample collection data model +################################################################# + +paml:SampleCollection rdf:type owl:Class ; + rdfs:comment "A sample collection is a group of samples" ; + rdfs:subClassOf sbol:Identified . + + +paml:SampleArray rdf:type owl:Class ; + rdfs:comment "A sample array is an n-dimensional rectangular group of samples, either with specified contents or empty, all stored in the same type of container" ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:containerType ; + owl:allValuesFrom xsd:anyURI ; # replace with container ontology, when available + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Type of container used for storing the samples. The size and dimension may not match that of the array: it is up to execution to lay out the array in one or more containers" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:contents ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of URI for specification or nulls" + ] . + +paml:containerType rdf:type owl:DatatypeProperty ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:anyURI ; + rdfs:label "container_type" . + +paml:contents rdf:type owl:DatatypeProperty ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:string ; + rdfs:label "contents" . + +paml:SampleMask rdf:type owl:Class ; + rdfs:comment "A sample mask is a subset of a SampleCollection, as defined by an array of boolean indiating whether each contents value is included or not" ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:source ; + owl:allValuesFrom paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Collection being subsetted via mask" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:mask ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of booleans indicating whether contents are included in mask or not" + ] . + +paml:source rdf:type owl:ObjectProperty ; + rdfs:domain paml:SampleMask ; + rdfs:range paml:SampleCollection ; + rdfs:label "source" . + +paml:mask rdf:type owl:DatatypeProperty ; + rdfs:domain paml:mask ; + rdfs:range xsd:string ; + rdfs:label "mask" . + + + + +paml:SampleData rdf:type owl:Class ; + rdfs:comment "Collection of samples with data at locations" ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; + owl:onProperty paml:sampleDataValues ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:fromSamples ; + owl:allValuesFrom paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] . + +paml:sampleDataValues rdf:type owl:ObjectProperty ; + rdfs:domain paml:SampleData ; + rdfs:range xsd:string ; # TODO: consider https://github.com/SynBioDex/sbol_factory/issues/7 + rdfs:label "values" . + +paml:fromSamples rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:SampleData ; + rdfs:range paml:SampleCollection ; + rdfs:label "from_samples" . + diff --git a/test/owl_test_app/app/resources/paml-bad.ttl b/test/owl_test_app/app/resources/paml-bad.ttl new file mode 100644 index 0000000..8cb19d0 --- /dev/null +++ b/test/owl_test_app/app/resources/paml-bad.ttl @@ -0,0 +1,155 @@ +# Old version of paml.ttl with malformed OWL +# https://github.com/SD2E/paml/blob/dbe533da0145d1586c74c3303c2f9a66147f7130/paml/paml.ttl +@prefix : . +@prefix om: . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix paml: . +@prefix rdfs: . +@prefix sbol: . +@prefix uml: . +@prefix MathM: . +@base . + + rdf:type owl:Ontology ; + owl:imports uml:, sbol: , om: ; + rdfs:comment "Protocol Activity Modeling Languge (PAML) ontology." ; + owl:versionInfo "0.3" . + +################################################################# +# Annotation properties +################################################################# + +### http://www.w3.org/2002/07/owl#maxCardinality +owl:maxCardinality rdf:type owl:AnnotationProperty . +owl:minCardinality rdf:type owl:AnnotationProperty . + + +################################################################# +# Datatypes +################################################################# + +### http://www.w3.org/2001/XMLSchema#anySimpleType +xsd:anySimpleType rdf:type rdfs:Datatype . + + +################################################################# +# Protocol definitions, based on UML +################################################################# + +paml:Protocol rdf:type owl:Class ; + rdfs:comment "A protocol is an activity in the laboratory" ; + rdfs:subClassOf uml:Activity . + + +paml:Primitive rdf:type owl:Class ; + rdfs:comment "A behavior that is a base-level library function" ; + rdfs:subClassOf uml:Behavior . + + +################################################################# +# Execution record, bootstrapping from PROV-O +################################################################# + +paml:Execution rdf:type owl:Class ; + rdfs:comment "An execution is a record of the values produced by the operation of a protocol, real or simulated" ; + rdfs:subClassOf sbol:TopLevel . + + +################################################################# +# Sample collection data model +################################################################# + +paml:SampleCollection rdf:type owl:Class ; + rdfs:comment "A sample collection is a group of samples" ; + rdfs:subClassOf sbol:Identified . + + +paml:SampleArray rdf:type owl:Class ; + rdfs:comment "A sample array is an n-dimensional rectangular group of samples, either with specified contents or empty, all stored in the same type of container" ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:containerType ; + owl:allValuesFrom xsd:anyURI ; # replace with container ontology, when available + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Type of container used for storing the samples. The size and dimension may not match that of the array: it is up to execution to lay out the array in one or more containers" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:contents ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of URI for specification or nulls" + ] . + +paml:containerType rdf:type owl:DatatypeProperty ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:anyURI ; + rdfs:label "container_type" . + +paml:contents rdf:type owl:DatatypeProperty ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:string ; + rdfs:label "contents" . + +paml:SampleMask rdf:type owl:Class ; + rdfs:comment "A sample mask is a subset of a SampleCollection, as defined by an array of boolean indiating whether each contents value is included or not" ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:source ; + owl:allValuesFrom paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Collection being subsetted via mask" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:mask ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of booleans indicating whether contents are included in mask or not" + ] . + +paml:source rdf:type owl:ObjectProperty ; + rdfs:domain paml:SampleMask ; + rdfs:range paml:SampleCollection ; + rdfs:label "source" . + +paml:mask rdf:type owl:DatatypeProperty ; + rdfs:domain paml:mask ; + rdfs:range xsd:string ; + rdfs:label "mask" . + + + + +paml:SampleData rdf:type owl:Class ; + rdfs:comment "Collection of samples with data at locations" ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; + owl:onProperty paml:sampleDataValues ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:fromSamples ; + owl:onClass paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] . + +paml:sampleDataValues rdf:type owl:ObjectProperty ; + rdfs:domain paml:SampleData ; + rdfs:range xsd:string ; + rdfs:label "values" . + +paml:fromSamples rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:SampleData ; + rdfs:range paml:SampleCollection ; + rdfs:label "from_samples" . + diff --git a/test/owl_test_app/app/resources/paml-good.ttl b/test/owl_test_app/app/resources/paml-good.ttl new file mode 100644 index 0000000..ff4ba62 --- /dev/null +++ b/test/owl_test_app/app/resources/paml-good.ttl @@ -0,0 +1,152 @@ +@prefix : . +@prefix om: . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix paml: . +@prefix rdfs: . +@prefix sbol: . +@prefix uml: . +@prefix MathM: . +@base . + + rdf:type owl:Ontology ; + owl:imports uml:, sbol: , om: ; + rdfs:comment "Protocol Activity Modeling Languge (PAML) ontology." ; + owl:versionInfo "0.3" . + +################################################################# +# Annotation properties +################################################################# + +### http://www.w3.org/2002/07/owl#maxCardinality +owl:maxCardinality rdf:type owl:AnnotationProperty . +owl:minCardinality rdf:type owl:AnnotationProperty . + + +################################################################# +# Datatypes +################################################################# + +### http://www.w3.org/2001/XMLSchema#anySimpleType +xsd:anySimpleType rdf:type rdfs:Datatype . + + +################################################################# +# Protocol definitions, based on UML +################################################################# + +paml:Protocol rdf:type owl:Class ; + rdfs:comment "A protocol is an activity in the laboratory" ; + rdfs:subClassOf uml:Activity . + + +paml:Primitive rdf:type owl:Class ; + rdfs:comment "A behavior that is a base-level library function" ; + rdfs:subClassOf uml:Behavior . + + +################################################################# +# Execution record, bootstrapping from PROV-O +################################################################# + +paml:Execution rdf:type owl:Class ; + rdfs:comment "An execution is a record of the values produced by the operation of a protocol, real or simulated" ; + rdfs:subClassOf sbol:TopLevel . + + +################################################################# +# Sample collection data model +################################################################# + +paml:SampleCollection rdf:type owl:Class ; + rdfs:comment "A sample collection is a group of samples" ; + rdfs:subClassOf sbol:Identified . + + +paml:SampleArray rdf:type owl:Class ; + rdfs:comment "A sample array is an n-dimensional rectangular group of samples, either with specified contents or empty, all stored in the same type of container" ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:containerType ; + owl:allValuesFrom xsd:anyURI ; # replace with container ontology, when available + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Type of container used for storing the samples. The size and dimension may not match that of the array: it is up to execution to lay out the array in one or more containers" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:contents ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of URI for specification or nulls" + ] . + +paml:containerType rdf:type owl:DatatypeProperty ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:anyURI ; + rdfs:label "container_type" . + +paml:contents rdf:type owl:DatatypeProperty ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:string ; + rdfs:label "contents" . + +paml:SampleMask rdf:type owl:Class ; + rdfs:comment "A sample mask is a subset of a SampleCollection, as defined by an array of boolean indiating whether each contents value is included or not" ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:source ; + owl:allValuesFrom paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Collection being subsetted via mask" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:mask ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of booleans indicating whether contents are included in mask or not" + ] . + +paml:source rdf:type owl:ObjectProperty ; + rdfs:domain paml:SampleMask ; + rdfs:range paml:SampleCollection ; + rdfs:label "source" . + +paml:mask rdf:type owl:DatatypeProperty ; + rdfs:domain paml:mask ; + rdfs:range xsd:string ; + rdfs:label "mask" . + + + + +paml:SampleData rdf:type owl:Class ; + rdfs:comment "Collection of samples with data at locations" ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; + owl:onProperty paml:sampleDataValues ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:fromSamples ; + owl:allValuesFrom paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] . + +paml:sampleDataValues rdf:type owl:ObjectProperty ; + rdfs:domain paml:SampleData ; + rdfs:range xsd:string ; # TODO: consider https://github.com/SynBioDex/sbol_factory/issues/7 + rdfs:label "values" . + +paml:fromSamples rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:SampleData ; + rdfs:range paml:SampleCollection ; + rdfs:label "from_samples" . diff --git a/test/owl_test_app/app/resources/paml-good.xml b/test/owl_test_app/app/resources/paml-good.xml new file mode 100644 index 0000000..bd62a3c --- /dev/null +++ b/test/owl_test_app/app/resources/paml-good.xml @@ -0,0 +1,149 @@ + + + + + + + A sample array is an n-dimensional rectangular group of samples, either with specified contents or empty, all stored in the same type of container + + + + + + + An execution is a record of the values produced by the operation of a protocol, real or simulated + + + + + + + + + from_samples + + + + + + values + + + + + A behavior that is a base-level library function + + + + 0.3 + + Protocol Activity Modeling Languge (PAML) ontology. + + + + + + + + mask + + + + + + + Collection of samples with data at locations + + + + A sample mask is a subset of a SampleCollection, as defined by an array of boolean indiating whether each contents value is included or not + + + + + + + + source + + + + + 1 + + + 1 + N-dimensional array of URI for specification or nulls + + + + + 1 + + Type of container used for storing the samples. The size and dimension may not match that of the array: it is up to execution to lay out the array in one or more containers + 1 + + + A protocol is an activity in the laboratory + + + + + + + + contents + + + + + + container_type + + + Collection being subsetted via mask + 1 + + 1 + + + + + + + A sample collection is a group of samples + + + + 1 + + 1 + N-dimensional array of booleans indicating whether contents are included in mask or not + + + + + + + 1 + + + + 1 + + + 1 + + + + 1 + + + + + diff --git a/test/owl_test_app/app/resources/university.owl b/test/owl_test_app/app/resources/university.owl new file mode 100644 index 0000000..cb74893 --- /dev/null +++ b/test/owl_test_app/app/resources/university.owl @@ -0,0 +1,478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + Laurea triennale in Ingegneria Informatica + + + + + + + + + 8 + 80 + Informatica + + + + + + + + + + true + Fulvio Corno + + + + + + + + + + + Luigi De Russis + + + + + + + + + Laurea magistrale in Ingegneria Informatica + + + + + + + + + 10 + 100 + Analisi matematica I + + + + + + + + + + Dottorato in Ingegneria Informatica e dei sistemi + + + + + + + + + Politecnico di Milano + + + + + + + + + + + + Politecnico di Torino + + + + + + + + + 20 + Semantic Web + + + + + + + + + + + + + + + + + + + + diff --git a/test/owl_test_app/app/src/main/java/owlet/App.java b/test/owl_test_app/app/src/main/java/owlet/App.java new file mode 100644 index 0000000..368c8bd --- /dev/null +++ b/test/owl_test_app/app/src/main/java/owlet/App.java @@ -0,0 +1,118 @@ +package owlet; + +import java.lang.Throwable; +import java.io.File; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.net.UnknownHostException; +import java.util.HashSet; +import java.util.Set; +import java.util.Map; +import java.util.logging.Logger; +import java.util.logging.Level; + +import org.semanticweb.HermiT.ReasonerFactory; +import org.semanticweb.owlapi.apibinding.OWLManager; +import org.semanticweb.owlapi.model.IRI; +import org.semanticweb.owlapi.model.OWLClass; +import org.semanticweb.owlapi.io.OWLParser; +import org.semanticweb.owlapi.model.OWLDataFactory; +import org.semanticweb.owlapi.model.OWLDataProperty; +import org.semanticweb.owlapi.model.OWLEntity; +import org.semanticweb.owlapi.model.OWLIndividual; +import org.semanticweb.owlapi.model.OWLNamedIndividual; +import org.semanticweb.owlapi.model.OWLOntology; +import org.semanticweb.owlapi.model.OWLOntologyLoaderConfiguration; +import org.semanticweb.owlapi.io.FileDocumentSource; +import org.semanticweb.owlapi.io.OWLParserException; +import org.semanticweb.owlapi.model.OWLOntologyCreationException; +import org.semanticweb.owlapi.io.OWLOntologyCreationIOException; +import org.semanticweb.owlapi.model.UnloadableImportException; +import org.semanticweb.owlapi.io.UnparsableOntologyException; +import org.semanticweb.owlapi.model.OWLOntologyManager; +import org.semanticweb.owlapi.reasoner.ConsoleProgressMonitor; +import org.semanticweb.owlapi.reasoner.InferenceType; +import org.semanticweb.owlapi.reasoner.NodeSet; +import org.semanticweb.owlapi.reasoner.OWLReasoner; +import org.semanticweb.owlapi.reasoner.OWLReasonerConfiguration; +import org.semanticweb.owlapi.reasoner.OWLReasonerFactory; +import org.semanticweb.owlapi.reasoner.SimpleConfiguration; +import org.semanticweb.owlapi.search.EntitySearcher; +import org.semanticweb.owlapi.util.DefaultPrefixManager; +import uk.ac.manchester.cs.owl.owlapi.OWLDataPropertyImpl; +import uk.ac.manchester.cs.owl.owlapi.OWLObjectPropertyImpl; + +/** + * Example of use of OWL-API 4.x and the HermiT reasoner. + * Realized with Gradle and Java 8. + * + * Semantic Web course, Politecnico di Torino, Italy + * + * @author Luigi De Russis, Politecnico di Torino, + * e-Lite group + * @version 1.0 (2017-02-22) + * + */ +public class App +{ + public boolean validateOntology(String ontologyPath, OWLOntologyLoaderConfiguration config) throws Exception { + Logger logger = Logger.getLogger(App.class.getName()); + logger.setLevel(Level.INFO); + logger.log(Level.INFO, "[OntologyValidator::validateOntology] BEGIN"); + boolean result = false; + try { + + OWLOntologyManager manager = OWLManager.createOWLOntologyManager(); + File file = new File(ontologyPath); + FileDocumentSource source = new FileDocumentSource(file); + OWLOntology localont = manager.loadOntologyFromOntologyDocument(source, config); + String ontologyUri = localont.getOntologyID().getOntologyIRI().toString(); + logger.log(Level.INFO, "[OntologyValidator::validateOntology] Loaded ontology with URI: " + ontologyUri); + IRI documentIRI = manager.getOntologyDocumentIRI(localont); + logger.log(Level.INFO, "[OntologyValidator::validateOntology] Artefact: " + documentIRI); + result = true; + + // get and configure a reasoner (HermiT) + OWLReasonerFactory reasonerFactory = new ReasonerFactory(); + ConsoleProgressMonitor progressMonitor = new ConsoleProgressMonitor(); + OWLReasonerConfiguration reasoner_config = new SimpleConfiguration(progressMonitor); + + // create the reasoner instance, classify and compute inferences + OWLReasoner reasoner = reasonerFactory.createReasoner(localont, reasoner_config); + if (!reasoner.isConsistent()) + return false; + } catch (OWLOntologyCreationIOException e) { + Throwable ioException = e.getCause(); + if (ioException instanceof FileNotFoundException) { + logger.log(Level.SEVERE, "[OntologyValidator::validateOntology] Could not load ontology. File not found: " + ioException.getMessage()); + } else if (ioException instanceof UnknownHostException) { + logger.log(Level.SEVERE, "[OntologyValidator::validateOntology] Could not load ontology. Unknown host: " + ioException.getMessage()); + } else { + logger.log(Level.SEVERE, "[OntologyValidator::validateOntology] Could not load ontology: " + ioException.getClass().getSimpleName() + " " + ioException.getMessage()); + } + throw e; + } catch (UnparsableOntologyException e) { + logger.log(Level.SEVERE, "[OntologyValidator::validateOntology] Could not parse the ontology: " + e.getMessage()); + Map exceptions = e.getExceptions(); + for (OWLParser parser : exceptions.keySet()) { + logger.log(Level.SEVERE, "Tried to parse the ontology with the " + parser.getClass().getSimpleName() + " parser"); + logger.log(Level.SEVERE, "Failed because: " + exceptions.get(parser).getMessage()); + } + throw e; + } catch (UnloadableImportException e) { + logger.log(Level.SEVERE, "[OntologyValidator::validateOntology] Could not load import: " + e.getImportsDeclaration()); + OWLOntologyCreationException cause = e.getOntologyCreationException(); + logger.log(Level.SEVERE, "Reason: " + cause.getMessage()); + throw e; + } catch (OWLOntologyCreationException e) { + logger.log(Level.SEVERE, "[OntologyValidator::validateOntology] Could not load ontology: " + e.getMessage()); + throw e; + } catch (Exception e) { + logger.log(Level.SEVERE, "[OntologyValidator::validateOntology] Ontology validation unsuccessful. Check ontology and parameters in input.", e); + throw e; + } + logger.log(Level.INFO, "[OntologyValidator::validateOntology] END"); + return result; + } + +} diff --git a/test/owl_test_app/app/src/main/java/owlet/university.owl b/test/owl_test_app/app/src/main/java/owlet/university.owl new file mode 100644 index 0000000..cb74893 --- /dev/null +++ b/test/owl_test_app/app/src/main/java/owlet/university.owl @@ -0,0 +1,478 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + 1 + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + 10 + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + Laurea triennale in Ingegneria Informatica + + + + + + + + + 8 + 80 + Informatica + + + + + + + + + + true + Fulvio Corno + + + + + + + + + + + Luigi De Russis + + + + + + + + + Laurea magistrale in Ingegneria Informatica + + + + + + + + + 10 + 100 + Analisi matematica I + + + + + + + + + + Dottorato in Ingegneria Informatica e dei sistemi + + + + + + + + + Politecnico di Milano + + + + + + + + + + + + Politecnico di Torino + + + + + + + + + 20 + Semantic Web + + + + + + + + + + + + + + + + + + + + diff --git a/test/owl_test_app/app/src/main/resources/paml-bad-onclass.ttl b/test/owl_test_app/app/src/main/resources/paml-bad-onclass.ttl new file mode 100644 index 0000000..98b09c6 --- /dev/null +++ b/test/owl_test_app/app/src/main/resources/paml-bad-onclass.ttl @@ -0,0 +1,153 @@ +@prefix : . +@prefix om: . +@prefix owl: . +@prefix rdf: . +@prefix xml: . +@prefix xsd: . +@prefix paml: . +@prefix rdfs: . +@prefix sbol: . +@prefix uml: . +@prefix MathM: . +@base . + + rdf:type owl:Ontology ; + owl:imports uml:, sbol: , om: ; + rdfs:comment "Protocol Activity Modeling Languge (PAML) ontology." ; + owl:versionInfo "0.3" . + +################################################################# +# Annotation properties +################################################################# + +### http://www.w3.org/2002/07/owl#maxCardinality +owl:maxCardinality rdf:type owl:AnnotationProperty . +owl:minCardinality rdf:type owl:AnnotationProperty . + + +################################################################# +# Datatypes +################################################################# + +### http://www.w3.org/2001/XMLSchema#anySimpleType +xsd:anySimpleType rdf:type rdfs:Datatype . + + +################################################################# +# Protocol definitions, based on UML +################################################################# + +paml:Protocol rdf:type owl:Class ; + rdfs:comment "A protocol is an activity in the laboratory" ; + rdfs:subClassOf uml:Activity . + + +paml:Primitive rdf:type owl:Class ; + rdfs:comment "A behavior that is a base-level library function" ; + rdfs:subClassOf uml:Behavior . + + +################################################################# +# Execution record, bootstrapping from PROV-O +################################################################# + +paml:Execution rdf:type owl:Class ; + rdfs:comment "An execution is a record of the values produced by the operation of a protocol, real or simulated" ; + rdfs:subClassOf sbol:TopLevel . + + +################################################################# +# Sample collection data model +################################################################# + +paml:SampleCollection rdf:type owl:Class ; + rdfs:comment "A sample collection is a group of samples" ; + rdfs:subClassOf sbol:Identified . + + +paml:SampleArray rdf:type owl:Class ; + rdfs:comment "A sample array is an n-dimensional rectangular group of samples, either with specified contents or empty, all stored in the same type of container" ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:containerType ; + owl:allValuesFrom xsd:anyURI ; # replace with container ontology, when available + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Type of container used for storing the samples. The size and dimension may not match that of the array: it is up to execution to lay out the array in one or more containers" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:contents ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of URI for specification or nulls" + ] . + +paml:containerType rdf:type owl:DatatypeProperty ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:anyURI ; + rdfs:label "container_type" . + +paml:contents rdf:type owl:DatatypeProperty ; + rdfs:domain paml:SampleArray ; + rdfs:range xsd:string ; + rdfs:label "contents" . + +paml:SampleMask rdf:type owl:Class ; + rdfs:comment "A sample mask is a subset of a SampleCollection, as defined by an array of boolean indiating whether each contents value is included or not" ; + rdfs:subClassOf paml:SampleCollection , + [ rdf:type owl:Restriction ; + owl:onProperty paml:source ; + owl:allValuesFrom paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "Collection being subsetted via mask" + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:mask ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + rdfs:comment "N-dimensional array of booleans indicating whether contents are included in mask or not" + ] . + +paml:source rdf:type owl:ObjectProperty ; + rdfs:domain paml:SampleMask ; + rdfs:range paml:SampleCollection ; + rdfs:label "source" . + +paml:mask rdf:type owl:DatatypeProperty ; + rdfs:domain paml:mask ; + rdfs:range xsd:string ; + rdfs:label "mask" . + + + + +paml:SampleData rdf:type owl:Class ; + rdfs:comment "Collection of samples with data at locations" ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; + owl:onProperty paml:sampleDataValues ; + owl:allValuesFrom xsd:string ; # need to replace this with an serializable array model + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] , + [ rdf:type owl:Restriction ; + owl:onProperty paml:fromSamples ; + owl:onClass paml:SampleCollection ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger ; + ] . + +paml:sampleDataValues rdf:type owl:ObjectProperty ; + rdfs:domain paml:SampleData ; + rdfs:range xsd:string ; + rdfs:label "values" . + +paml:fromSamples rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain paml:SampleData ; + rdfs:range paml:SampleCollection ; + rdfs:label "from_samples" . + diff --git a/test/owl_test_app/app/src/test/java/owlet/AppTest.java b/test/owl_test_app/app/src/test/java/owlet/AppTest.java new file mode 100644 index 0000000..e727e3b --- /dev/null +++ b/test/owl_test_app/app/src/test/java/owlet/AppTest.java @@ -0,0 +1,61 @@ +/* + * This Java source file was generated by the Gradle 'init' task. + */ +package owlet; + +import org.junit.Test; +import static org.junit.Assert.*; +import java.lang.Exception; +import java.io.File; +import java.io.IOException; +import org.semanticweb.HermiT.ReasonerFactory; +import org.semanticweb.owlapi.apibinding.OWLManager; +import org.semanticweb.owlapi.model.IRI; +import org.semanticweb.owlapi.model.OWLClass; +import org.semanticweb.owlapi.model.OWLDataFactory; +import org.semanticweb.owlapi.model.OWLDataProperty; +import org.semanticweb.owlapi.model.OWLEntity; +import org.semanticweb.owlapi.model.OWLIndividual; +import org.semanticweb.owlapi.model.OWLNamedIndividual; +import org.semanticweb.owlapi.model.OWLOntology; +import org.semanticweb.owlapi.model.OWLOntologyCreationException; +import org.semanticweb.owlapi.model.OWLOntologyManager; +import org.semanticweb.owlapi.reasoner.OWLReasoner; +import org.semanticweb.owlapi.reasoner.OWLReasonerConfiguration; +import org.semanticweb.owlapi.reasoner.OWLReasonerFactory; +import org.semanticweb.owlapi.reasoner.SimpleConfiguration; +import org.semanticweb.owlapi.reasoner.ConsoleProgressMonitor; +import org.semanticweb.owlapi.reasoner.structural.StructuralReasoner; +import org.semanticweb.owlapi.model.OWLOntologyLoaderConfiguration; +import org.semanticweb.owlapi.rdf.turtle.parser.TurtleOntologyParser; +import org.semanticweb.owlapi.model.OWLDocumentFormat; +import org.semanticweb.owlapi.io.OWLOntologyDocumentSource; +import org.semanticweb.owlapi.io.FileDocumentSource; + + + +public class AppTest { + + static App test_app = new App(); + + @Test public void test_valid() throws Exception { + + OWLOntologyLoaderConfiguration loader_config = new OWLOntologyLoaderConfiguration(); + loader_config = loader_config.addIgnoredImport(IRI.create("http://sbols.org/v3#")); + loader_config = loader_config.addIgnoredImport(IRI.create("http://bioprotocols.org/uml#")); + loader_config = loader_config.addIgnoredImport(IRI.create("http://www.ontology-of-units-of-measure.org/resource/om-2/")); + boolean status = test_app.validateOntology("resources/paml-good.ttl", loader_config); + assertTrue(status); + } + +// @Test public void test_invalid() throws Exception { +// +// OWLOntologyLoaderConfiguration loader_config = new OWLOntologyLoaderConfiguration(); +// loader_config = loader_config.addIgnoredImport(IRI.create("http://sbols.org/v3#")); +// loader_config = loader_config.addIgnoredImport(IRI.create("http://bioprotocols.org/uml#")); +// loader_config = loader_config.addIgnoredImport(IRI.create("http://www.ontology-of-units-of-measure.org/resource/om-2/")); +// boolean status = test_app.validateOntology("resources/paml-bad.ttl", loader_config); +// assertFalse(status); +// } + +} diff --git a/test/owl_test_app/gradle/wrapper/gradle-wrapper.jar b/test/owl_test_app/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..7454180 Binary files /dev/null and b/test/owl_test_app/gradle/wrapper/gradle-wrapper.jar differ diff --git a/test/owl_test_app/gradle/wrapper/gradle-wrapper.properties b/test/owl_test_app/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..05679dc --- /dev/null +++ b/test/owl_test_app/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/test/owl_test_app/gradlew b/test/owl_test_app/gradlew new file mode 100755 index 0000000..744e882 --- /dev/null +++ b/test/owl_test_app/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MSYS* | MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/test/owl_test_app/gradlew.bat b/test/owl_test_app/gradlew.bat new file mode 100644 index 0000000..107acd3 --- /dev/null +++ b/test/owl_test_app/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/test/owl_test_app/settings.gradle b/test/owl_test_app/settings.gradle new file mode 100644 index 0000000..ca15d48 --- /dev/null +++ b/test/owl_test_app/settings.gradle @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.1.1/userguide/multi_project_builds.html + */ + +rootProject.name = 'owlet' +include('app') diff --git a/test/test_LUDOX_protocol.py b/test/test_LUDOX_protocol.py new file mode 100644 index 0000000..ed80852 --- /dev/null +++ b/test/test_LUDOX_protocol.py @@ -0,0 +1,53 @@ +import filecmp +import logging +import os +import tempfile +import unittest +from importlib.machinery import SourceFileLoader +from importlib.util import spec_from_loader, module_from_spec + +import sbol3 + +import paml + +protocol_def_file = os.path.join(os.path.dirname(__file__), '../examples/LUDOX_protocol.py') + + +def load_ludox_protocol(protocol_filename): + loader = SourceFileLoader('ludox_protocol', protocol_filename) + spec = spec_from_loader(loader.name, loader) + module = module_from_spec(spec) + loader.exec_module(module) + return module + + +protocol_def = load_ludox_protocol(protocol_def_file) + + +class TestProtocolEndToEnd(unittest.TestCase): + def test_create_protocol(self): + protocol: paml.Protocol + doc: sbol3.Document + logger = logging.getLogger("LUDOX_protocol") + logger.setLevel(logging.INFO) + protocol, doc = protocol_def.ludox_protocol() + + ######################################## + # Validate and write the document + print('Validating and writing protocol') + v = doc.validate() + assert len(v) == 0, "".join(f'\n {e}' for e in v) + + temp_name = os.path.join(tempfile.gettempdir(), 'igem_ludox_test.nt') + doc.write(temp_name, sbol3.SORTED_NTRIPLES) + print(f'Wrote file as {temp_name}') + + comparison_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'testfiles', 'igem_ludox_test.nt') + # doc.write(comparison_file, sbol3.SORTED_NTRIPLES) + print(f'Comparing against {comparison_file}') + assert filecmp.cmp(temp_name, comparison_file), "Files are not identical" + print('File identical with test file') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_library_build.py b/test/test_library_build.py new file mode 100644 index 0000000..d1a4064 --- /dev/null +++ b/test/test_library_build.py @@ -0,0 +1,57 @@ +import tempfile +import os +import unittest +import filecmp +import sbol3 +import paml + + +class TestLibraryBuilding(unittest.TestCase): + def test_two_element_library(self): + ############################################# + # Set up the document + doc = sbol3.Document() + sbol3.set_namespace('https://example.org/test') + + ############################################# + # Create the primitives + print('Making primitives for test library') + + p = paml.Primitive('Provision') + p.description = 'Place a measured amount (mass or volume) of a specified component into a location.' + p.add_input('resource', sbol3.SBOL_COMPONENT) + p.add_input('destination', 'http://bioprotocols.org/paml#Location') + p.add_input('amount', sbol3.OM_MEASURE) # Can be mass or volume + p.add_input('dispenseVelocity', sbol3.OM_MEASURE, True) + p.add_output('samples', 'http://bioprotocols.org/paml#LocatedSamples') + doc.add(p) + + p = paml.Primitive('Transfer') + p.description = 'Move a measured volume taken from a collection of source samples to a location' + p.add_input('source', 'http://bioprotocols.org/paml#LocatedSamples') + p.add_input('destination', 'http://bioprotocols.org/paml#Location') + p.add_input('amount', sbol3.OM_MEASURE) # Must be volume + p.add_input('dispenseVelocity', sbol3.OM_MEASURE, True) + p.add_output('samples', 'http://bioprotocols.org/paml#LocatedSamples') + doc.add(p) + + print('Library construction complete') + + ######################################## + # Validate and write the document + print('Validating and writing protocol') + v = doc.validate() + assert len(v) == 0, "".join(f'\n {e}' for e in v) + + temp_name = os.path.join(tempfile.gettempdir(), 'mini_library.nt') + doc.write(temp_name, sbol3.SORTED_NTRIPLES) + print(f'Wrote file as {temp_name}') + + comparison_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'testfiles', 'mini_library.nt') + print(f'Comparing against {comparison_file}') + assert filecmp.cmp(temp_name, comparison_file), "Files are not identical" + print('File identical with test file') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_paml_utils.py b/test/test_paml_utils.py new file mode 100644 index 0000000..46e5592 --- /dev/null +++ b/test/test_paml_utils.py @@ -0,0 +1,25 @@ +import pytest +import unittest + +import paml_convert + + +class TestLocationUtilities(unittest.TestCase): + @pytest.mark.skip(reason="module 'paml_convert' has no submodule 'markdown'") + def test_range_reduction(self): + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A1'}) == {'A1'} + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A1', 'A2'}) == {'A1:A2'} + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A2', 'A4', 'A1', 'A5', 'A3'}) == {'A1:A5'} + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A2:B3', 'A4:D6', 'C2:D2', 'C3', 'D3'}) == {'A2:D6'} + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A1:C3', 'D1:F3', 'A4:C7'}) == {'A1:F3', 'A4:C7'} + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A1:C3', 'D1:F3', 'A4:C7', 'D4:F7'}) == {'A1:F7'} + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A1:C3', 'D1:F3', 'A4:C7', 'D4:D7'}) == {'A1:F3', 'A4:D7'} + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A1:C3', 'A4:C7'}) == {'A1:C7'} + assert paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A1:C3', 'D4:F7'}) == {'A1:C3', 'D4:F7'} + with pytest.raises(AssertionError): + paml_convert.markdown.protocol_to_markdown.reduce_range_set({'A1:C3', 'B3:F7'}) # overlapping ranges not allowed + with pytest.raises(AssertionError): + paml_convert.markdown.protocol_to_markdown.reduce_range_set({}) # must have at least one range + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_time.py b/test/test_time.py new file mode 100644 index 0000000..88b216a --- /dev/null +++ b/test/test_time.py @@ -0,0 +1,263 @@ +import filecmp +import os +import tempfile + +import sbol3 +import unittest +import tyto +import paml_time as pamlt +import paml +import uml +# from paml_check.paml_check import check_doc, get_minimum_duration + + +class TestTime(unittest.TestCase): + def test_single_behavior(self): + ############################################# + # set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + + + ############################################# + # Create the behavior and constraints + print('Creating Constraints') + + a = paml.Primitive("a") + # Constrain start time of a to [0, 10] + start_a = pamlt.startTime(a, [0, 10], units=tyto.OM.hour) + + # Constrain end time of a to [10, 15] + end_a = pamlt.endTime(a, [10, 15], units=tyto.OM.hour) + + # Constrain duration of a to [1, 5] + duration_a = pamlt.duration(a, [1, 5], units=tyto.OM.hour) + + constraint = pamlt.And([start_a, end_a, duration_a]) + time_constraints = pamlt.TimeConstraints("small_protocol_constraints", constraints=[constraint]) + + + doc.add(a) + doc.add(time_constraints) + ######################################## + # Validate and write the document + print('Validating and writing time') + v = doc.validate() + assert not v.errors and not v.warnings, "".join(str(e) for e in doc.validate().errors) + + # doc.write('difference.nt', 'sorted nt') + # doc.write('difference.ttl', 'turtle') + + def test_two_behaviors(self): + ############################################# + # set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + + + ############################################# + # Create the behavior and constraints + print('Creating Constraints') + + a = paml.Primitive("a") + b = paml.Primitive("b") + + # Constrain start of b to follow end of a by [10, 15] + follows_constraint = pamlt.precedes(a, [10, 15], b, units=tyto.OM.hour) + + doc.add(a) + # doc.add(follows_constraint) + ######################################## + # Validate and write the document + print('Validating and writing time') + v = doc.validate() + assert not v.errors and not v.warnings, "".join(str(e) for e in doc.validate().errors) + + # doc.write('timed_protocol.nt', 'sorted nt') + # doc.write('timed_protocol.ttl', 'turtle') + + def test_timed_small_protocol(self): + ############################################# + # set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + + + ############################################# + # Create the Protocol + print('Creating Protocol') + protocol = paml.Protocol('test_protocol') + + # Protocol starts at time zero + start = pamlt.startTime(protocol, 0, units=tyto.OM.hour) + + # Protocol lasts 10 - 15 hours + duration = pamlt.duration(protocol, [10, 15], units=tyto.OM.hour) + + time_constraints = pamlt.TimeConstraints("small_protocol_constraints", + constraints=pamlt.And([start, duration]), + protocols=[protocol]) + doc.add(protocol) + doc.add(time_constraints) + + + ######################################## + # Validate and write the document + print('Validating and writing time') + v = doc.validate() + assert not v.errors and not v.warnings, "".join(str(e) for e in doc.validate().errors) + + # doc.write('timed_protocol.nt', 'sorted nt') + # doc.write('timed_protocol.ttl', 'turtle') + + def test_create_timed_protocol(self): + ############################################# + # set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + + ############################################# + # Import the primitive libraries + print('Importing libraries') + paml.import_library('liquid_handling') + print('... Imported liquid handling') + paml.import_library('plate_handling') + print('... Imported plate handling') + paml.import_library('spectrophotometry') + print('... Imported spectrophotometry') + paml.import_library('sample_arrays') + print('... Imported sample arrays') + + ############################################# + # Create the protocol + print('Creating protocol') + protocol = paml.Protocol('iGEM_LUDOX_OD_calibration_2018') + protocol.name = "iGEM 2018 LUDOX OD calibration protocol" + protocol.description = ''' + With this protocol you will use LUDOX CL-X (a 45% colloidal silica suspension) as a single point reference to + obtain a conversion factor to transform absorbance (OD600) data from your plate reader into a comparable + OD600 measurement as would be obtained in a spectrophotometer. This conversion is necessary because plate + reader measurements of absorbance are volume dependent; the depth of the fluid in the well defines the path + length of the light passing through the sample, which can vary slightly from well to well. In a standard + spectrophotometer, the path length is fixed and is defined by the width of the cuvette, which is constant. + Therefore this conversion calculation can transform OD600 measurements from a plate reader (i.e. absorbance + at 600 nm, the basic output of most instruments) into comparable OD600 measurements. The LUDOX solution + is only weakly scattering and so will give a low absorbance value. + ''' + doc.add(protocol) + + # create the materials to be provisioned + ddh2o = sbol3.Component('ddH2O', 'https://identifiers.org/pubchem.substance:24901740') + ddh2o.name = 'Water, sterile-filtered, BioReagent, suitable for cell culture' # TODO get via tyto + doc.add(ddh2o) + + ludox = sbol3.Component('LUDOX', 'https://identifiers.org/pubchem.substance:24866361') + ludox.name = 'LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O' + doc.add(ludox) + + # actual steps of the protocol + # get a plate + plate = protocol.primitive_step('EmptyContainer', specification=tyto.NCIT.get_uri_by_term('Microplate')) # replace with container ontology + + # put ludox and water in selected wells + c_ddh2o = protocol.primitive_step('PlateCoordinates', source=plate, coordinates='A1:D1') + provision_ludox = protocol.primitive_step('Provision', resource=ludox, destination=c_ddh2o.output_pin('samples'), + amount=sbol3.Measure(100, tyto.OM.microliter)) + + c_ludox = protocol.primitive_step('PlateCoordinates', source=plate, coordinates='A2:D2') + provision_ddh2o = protocol.primitive_step('Provision', resource=ddh2o, destination=c_ludox.output_pin('samples'), + amount=sbol3.Measure(100, tyto.OM.microliter)) + + # measure the absorbance + c_measure = protocol.primitive_step('PlateCoordinates', source=plate, coordinates='A1:D2') + measure = protocol.primitive_step('MeasureAbsorbance', samples=c_measure, + wavelength=sbol3.Measure(600, tyto.OM.nanometer)) + + protocol.add_output('absorbance', measure.output_pin('measurements')) + + # Set protocol timepoints + + # protocol starts at time 0 + protocol_start_time = pamlt.startTime(protocol, 0, units=tyto.OM.hour) + provision_ludox_duration = pamlt.duration(provision_ludox, 60, units=tyto.OM.second) + provision_ddh2o_duration = pamlt.duration(provision_ddh2o, 60, units=tyto.OM.second) + execute_measurement_duration = pamlt.duration(measure, 60, units=tyto.OM.minute) + ludox_before_ddh2o_constraint = pamlt.precedes(provision_ludox, [10, 15], provision_ddh2o, units=tyto.OM.hour) + + time_constraints = pamlt.TimeConstraints("ludox_protocol_constraints", + constraints=[pamlt.And([ + protocol_start_time, + provision_ludox_duration, + provision_ddh2o_duration, + execute_measurement_duration, + ludox_before_ddh2o_constraint + ])], + protocols = [protocol] + ) + + doc.add(time_constraints) + + ######################################## + # Validate and write the document + print('Validating and writing protocol') + v = doc.validate() + assert not v.errors and not v.warnings, "".join(str(e) for e in doc.validate().errors) + + # assert check_doc(doc) # Is the protocol consistent? + # assert get_minimum_duration(doc) # What is the minimum duration for each protocol in doc + + temp_name = os.path.join(tempfile.gettempdir(), 'igem_ludox_time_test.nt') + doc.write(temp_name, sbol3.SORTED_NTRIPLES) + print(f'Wrote file as {temp_name}') + + comparison_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'testfiles', 'igem_ludox_time_test.nt') + doc.write(comparison_file, sbol3.SORTED_NTRIPLES) + print(f'Comparing against {comparison_file}') + assert filecmp.cmp(temp_name, comparison_file), "Files are not identical" + print('File identical with test file') + + + def test_expressions(self): + ############################################# + # set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + + + ############################################# + # Create the Expressions + print('Creating Protocol') + + # expression e1: 60s * duration(a1) + a1 = paml.Primitive("a1") + d1 = uml.Duration(observation=uml.DurationObservation(event=[a1])) + m1 = pamlt.TimeMeasure(expr=sbol3.Measure(60, tyto.OM.second)) + e1 = uml.Expression(symbol="*", is_ordered=False, operand=[m1, d1]) + #doc.add(e1) + + + # expression lt1: e1 < e2 + e2 = pamlt.TimeMeasure(expr=sbol3.Measure(120, tyto.OM.second)) + lt1 = uml.Expression(symbol="<", is_ordered=True, operand=[e1, e2]) + #doc.add(lt1) + + # c1: Not(lt1) + c1 = pamlt.Not(constrained_elements=lt1) + # doc.add(c1) + + ######################################## + # Validate and write the document + print('Validating and writing time') + v = doc.validate() + assert not v.errors and not v.warnings, "".join(str(e) for e in doc.validate().errors) + + # doc.write('timed_protocol.nt', 'sorted nt') + # doc.write('timed_protocol.ttl', 'turtle') + +if __name__ == '__main__': + unittest.main() diff --git a/test/test_uml.py b/test/test_uml.py new file mode 100644 index 0000000..b210e94 --- /dev/null +++ b/test/test_uml.py @@ -0,0 +1,41 @@ +import uml +import sbol3 +import pytest +import unittest +import tyto + + +class TestUML(unittest.TestCase): + def test_ordered_properties(self): + property = uml.LiteralNull() + ordered_property = uml.OrderedPropertyValue(index=0, property_value=property) + assert ordered_property.property_value == property and ordered_property.index == 0 + + def test_ordered_constraint(self): + property1 = uml.OrderedPropertyValue(index=0, property_value=uml.LiteralInteger(value=0)) + property2 = uml.OrderedPropertyValue(index=1, property_value=uml.LiteralInteger(value=2)) + constraint = uml.Constraint(constrained_elements=[property1, property2]) + assert property1 in constraint.constrained_elements and property2 in constraint.constrained_elements + + def test_ordered_behavior_parameters(self): + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + + parameter1 = uml.OrderedPropertyValue(index=0, property_value=uml.Parameter(direction="in", + default_value=uml.LiteralInteger(value=0), + is_unique=True, + is_ordered=True, + lower_value=uml.LiteralInteger(value=0), + upper_value=uml.LiteralInteger(value=10))) + parameter2 = uml.OrderedPropertyValue(index=1, property_value=uml.Parameter(direction="in", + default_value=uml.LiteralInteger(value=0), + is_unique=True, + is_ordered=True, + lower_value=uml.LiteralInteger(value=0), + upper_value=uml.LiteralInteger(value=10))) + behavior = uml.Behavior("b", parameters=[parameter1, parameter2]) + assert parameter1 in behavior.parameters and parameter2 in behavior.parameters + + doc.add(behavior) + v = doc.validate() + assert not v.errors and not v.warnings, "".join(str(e) for e in doc.validate().errors) diff --git a/test/test_validation.py b/test/test_validation.py new file mode 100644 index 0000000..a45e79a --- /dev/null +++ b/test/test_validation.py @@ -0,0 +1,67 @@ +import unittest +import sbol3 +import paml +import uml + + +class TestValidationErrorChecking(unittest.TestCase): + def test_activity_multiflow(self): + """Test whether validator can detect nondeterminism due to activity multiple outflows""" + # set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + paml.import_library('sample_arrays') + # Create the protocol + print('Creating protocol') + protocol = paml.Protocol('broken') + doc.add(protocol) + # get a plate + plate = protocol.primitive_step('EmptyContainer', specification='placeholder') + # use it in three places + s1 = protocol.primitive_step('PlateCoordinates', coordinates='A1:D1') + protocol.edges.append(uml.ObjectFlow(source=plate.output_pin('samples'),target=s1.input_pin('source'))) + s2 = protocol.primitive_step('PlateCoordinates', coordinates='A2:D2') + protocol.edges.append(uml.ObjectFlow(source=plate.output_pin('samples'),target=s2.input_pin('source'))) + s3 = protocol.primitive_step('PlateCoordinates', coordinates='A3:D3') + protocol.edges.append(uml.ObjectFlow(source=plate.output_pin('samples'),target=s3.input_pin('source'))) + # Validate the document, which should produce one error + print('Validating and writing protocol') + v = doc.validate() + assert len(v.errors) == 0, f'Expected zero errors, but found {len(v)}' + assert len(v.warnings) == 1, f'Expected precisely one warning, but found {len(v)}' + assert str(v.warnings[0]) == \ + 'https://bbn.com/scratch/broken/CallBehaviorAction1/OutputPin1: ' \ + 'ActivityNode has 3 outgoing edges: multi-edges can cause nondeterministic flow', \ + f'Unexpected warning content: {str(v.warnings[0])}' + + def test_activity_bad_inflows(self): + """Test whether validator can detect error due to excess or missing inflows""" + # set up the document + print('Setting up document') + doc = sbol3.Document() + sbol3.set_namespace('https://bbn.com/scratch/') + # Create the protocol + print('Creating protocol') + protocol = paml.Protocol('broken') + doc.add(protocol) + # call order backwards, to make an edge from the final to the initial + protocol.order(protocol.final(), protocol.initial()) + # access a parameter node and order it backwards too + p = uml.ActivityParameterNode() + protocol.nodes.append(p) + protocol.order(protocol.final(), p) + # Validate the document, which should produce two errors + print('Validating and writing protocol') + v = doc.validate() + assert len(v) == 3, f'Expected 3 validation issues, but found {len(v)}' + expected = [ + 'https://bbn.com/scratch/broken/ActivityParameterNode1: Too few values for property parameter. Expected 1, found 0', + 'https://bbn.com/scratch/broken/InitialNode1: InitialNode must have no incoming edges, but has 1', + 'https://bbn.com/scratch/broken/FlowFinalNode1: Node has no incoming edges, so cannot be executed' + ] + observed = [str(e) for e in v] + assert observed == expected, f'Unexpected error content: {observed}' + +if __name__ == '__main__': + unittest.main() diff --git a/test/testfiles/BBN_LUDOX_OD_calibration_2021-3.xlsx b/test/testfiles/BBN_LUDOX_OD_calibration_2021-3.xlsx new file mode 100644 index 0000000..1ac0160 Binary files /dev/null and b/test/testfiles/BBN_LUDOX_OD_calibration_2021-3.xlsx differ diff --git a/test/testfiles/BBN_LUDOX_execution_2021_3_report.json b/test/testfiles/BBN_LUDOX_execution_2021_3_report.json new file mode 100644 index 0000000..f7ed719 --- /dev/null +++ b/test/testfiles/BBN_LUDOX_execution_2021_3_report.json @@ -0,0 +1,241 @@ +{ + "@context": { + "@om": "http://www.ontology-of-units-of-measure.org/resource/om-2/", + "@prov": "http://www.w3.org/ns/prov#", + "@sbol": "http://sbols.org/v3#", + "@vocab": "https://sbolstandard.org/examples/" + }, + "@graph": [ + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_B1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasDerivedFrom": { + "@id": "https://bbn.com/scratch/LUDOX" + }, + "@sbol:displayId": "Container1_B1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:hasMeasure": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_B1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1" + }, + "@type": "@sbol:Implementation" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_C2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1", + "@om:hasNumericalValue": 0.077, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_B2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasDerivedFrom": { + "@id": "https://bbn.com/scratch/ddH2O" + }, + "@sbol:displayId": "Container1_B2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:hasMeasure": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_B2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1" + }, + "@type": "@sbol:Implementation" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_A2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1", + "@om:hasNumericalValue": 0.066, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018_Results_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasGeneratedBy": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + }, + "@sbol:description": "Executed by Miles Rogers on 2021-03-01 00:00:00; reported on 2021-03-01 00:00:00", + "@sbol:displayId": "iGEM_LUDOX_OD_calibration_2018_Results_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:member": [ + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_A1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_D2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_A2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_B2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_B1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_C1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_D1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_C2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" + } + ], + "@sbol:name": "iGEM 2018 LUDOX OD calibration protocol Execution", + "@type": "@sbol:Experiment" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_D1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1", + "@om:hasNumericalValue": 0.083, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_B2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1", + "@om:hasNumericalValue": 0.054, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_C2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasDerivedFrom": { + "@id": "https://bbn.com/scratch/ddH2O" + }, + "@sbol:displayId": "Container1_C2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:hasMeasure": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_C2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1" + }, + "@type": "@sbol:Implementation" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_B1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1", + "@om:hasNumericalValue": 0.108, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_C1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1", + "@om:hasNumericalValue": 0.119, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_D2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasDerivedFrom": { + "@id": "https://bbn.com/scratch/ddH2O" + }, + "@sbol:displayId": "Container1_D2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:hasMeasure": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_D2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1" + }, + "@type": "@sbol:Implementation" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_A1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasDerivedFrom": { + "@id": "https://bbn.com/scratch/LUDOX" + }, + "@sbol:displayId": "Container1_A1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:hasMeasure": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_A1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1" + }, + "@type": "@sbol:Implementation" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_A2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasDerivedFrom": { + "@id": "https://bbn.com/scratch/ddH2O" + }, + "@sbol:displayId": "Container1_A2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:hasMeasure": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_A2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1" + }, + "@type": "@sbol:Implementation" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_D1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasDerivedFrom": { + "@id": "https://bbn.com/scratch/LUDOX" + }, + "@sbol:displayId": "Container1_D1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:hasMeasure": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_D1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1" + }, + "@type": "@sbol:Implementation" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_A1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1", + "@om:hasNumericalValue": 0.11, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_C1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@prov:wasDerivedFrom": { + "@id": "https://bbn.com/scratch/LUDOX" + }, + "@sbol:displayId": "Container1_C1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:hasMeasure": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_C1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1" + }, + "@type": "@sbol:Implementation" + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018/Container1_D2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36/Measure1", + "@om:hasNumericalValue": 0.05, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:displayId": "iGEM_LUDOX_OD_calibration_2018_Execution_451cfa80_1af3_4d7d_beda_bac649826d36", + "@sbol:type": { + "@id": "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018" + }, + "@type": [ + "@prov:Activity", + "@sbol:TopLevel" + ] + } + ] +} \ No newline at end of file diff --git a/test/testfiles/BBN_LUDOX_execution_2021_3_report.ttl b/test/testfiles/BBN_LUDOX_execution_2021_3_report.ttl new file mode 100644 index 0000000..2309fbc --- /dev/null +++ b/test/testfiles/BBN_LUDOX_execution_2021_3_report.ttl @@ -0,0 +1,112 @@ +@prefix om: . +@prefix prov: . +@prefix sbol: . +@prefix xsd: . + + a sbol:Experiment ; + sbol:description "Executed by Miles Rogers on 2021-03-01 00:00:00; reported on 2021-03-01 00:00:00" ; + sbol:displayId "iGEM_LUDOX_OD_calibration_2018_Results_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:member , + , + , + , + , + , + , + ; + sbol:name "iGEM 2018 LUDOX OD calibration protocol Execution" ; + prov:wasGeneratedBy . + + a sbol:Implementation ; + sbol:displayId "Container1_A1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:hasMeasure ; + prov:wasDerivedFrom . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1.1e-01 ; + om:hasUnit om:Number . + + a sbol:Implementation ; + sbol:displayId "Container1_A2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:hasMeasure ; + prov:wasDerivedFrom . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 6.6e-02 ; + om:hasUnit om:Number . + + a sbol:Implementation ; + sbol:displayId "Container1_B1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:hasMeasure ; + prov:wasDerivedFrom . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1.08e-01 ; + om:hasUnit om:Number . + + a sbol:Implementation ; + sbol:displayId "Container1_B2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:hasMeasure ; + prov:wasDerivedFrom . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5.4e-02 ; + om:hasUnit om:Number . + + a sbol:Implementation ; + sbol:displayId "Container1_C1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:hasMeasure ; + prov:wasDerivedFrom . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1.19e-01 ; + om:hasUnit om:Number . + + a sbol:Implementation ; + sbol:displayId "Container1_C2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:hasMeasure ; + prov:wasDerivedFrom . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 7.7e-02 ; + om:hasUnit om:Number . + + a sbol:Implementation ; + sbol:displayId "Container1_D1_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:hasMeasure ; + prov:wasDerivedFrom . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 8.3e-02 ; + om:hasUnit om:Number . + + a sbol:Implementation ; + sbol:displayId "Container1_D2_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:hasMeasure ; + prov:wasDerivedFrom . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5e-02 ; + om:hasUnit om:Number . + + a sbol:TopLevel, + prov:Activity ; + sbol:displayId "iGEM_LUDOX_OD_calibration_2018_Execution_451cfa80_1af3_4d7d_beda_bac649826d36" ; + sbol:type . + diff --git a/test/testfiles/growth_curve.json b/test/testfiles/growth_curve.json new file mode 100644 index 0000000..ca67416 --- /dev/null +++ b/test/testfiles/growth_curve.json @@ -0,0 +1,8529 @@ +{ + "@context": { + "@om": "http://www.ontology-of-units-of-measure.org/resource/om-2/", + "@prov": "http://www.w3.org/ns/prov#", + "@sbol": "http://sbols.org/v3#", + "@vocab": "https://sbolstandard.org/examples/" + }, + "@graph": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "duration", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow27", + "@sbol:displayId": "Flow27", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow17", + "@sbol:displayId": "Flow17", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable10" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "amount", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow8", + "@sbol:displayId": "Flow8", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "dispenseVelocity", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 530.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification5", + "@sbol:displayId": "ProtocolPinSpecification5", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "http://bioprotocols.org/paml#ProtocolPinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value5" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "absorbance", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "location", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "dispenseVelocity", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow3", + "@sbol:displayId": "Flow3", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow7", + "@sbol:displayId": "Flow7", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin4" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 200.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2", + "@sbol:displayId": "SubProtocol2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#SubProtocol" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin6" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin5" + } + ], + "http://bioprotocols.org/paml#instanceOfSubProtocol": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure" + }, + "http://bioprotocols.org/paml#output": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin1" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7", + "@sbol:displayId": "PrimitiveExecutable7", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin4" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow24", + "@sbol:displayId": "Flow24", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable10" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "dispenseVelocity", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "dispenseVelocity", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "emissionBandpassWavelength", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow32", + "@sbol:displayId": "Flow32", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "amount", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow15", + "@sbol:displayId": "Flow15", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Fork1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow8", + "@sbol:displayId": "Flow8", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Join1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value2" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 350.0, + "@om:hasUnit": { + "@id": "https://sd2e.org/PAML/rpm" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow56", + "@sbol:displayId": "Flow56", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 530.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow20", + "@sbol:displayId": "Flow20", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value5" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto", + "@sbol:description": "Mix a measured volume taken from an array of source samples intto an identically shaped array of destination samples", + "@sbol:displayId": "TransferInto", + "@type": [ + "http://bioprotocols.org/paml#Primitive", + "@sbol:TopLevel" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification4" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification3" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification1" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification2" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification5" + } + ], + "http://bioprotocols.org/paml#output": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin4", + "@sbol:displayId": "LocalValuePin4", + "@sbol:name": "gain", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#double" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification7" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin4/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4", + "@sbol:displayId": "PrimitiveExecutable4", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/Pin2" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin4", + "@sbol:displayId": "LocalValuePin4", + "@sbol:name": "gain", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#double" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification7" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin4/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "excitationWavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 25.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow6", + "@sbol:displayId": "Flow6", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13", + "@sbol:displayId": "PrimitiveExecutable13", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13/StringConstantPin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "wavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow13", + "@sbol:displayId": "Flow13", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/Pin1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer", + "@sbol:description": "Move a measured volume taken from an array of source samples to an identically shaped array in a destination locations", + "@sbol:displayId": "Transfer", + "@type": [ + "http://bioprotocols.org/paml#Primitive", + "@sbol:TopLevel" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification2" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification4" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification1" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification3" + } + ], + "http://bioprotocols.org/paml#output": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow24", + "@sbol:displayId": "Flow24", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value6" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin4", + "@sbol:displayId": "LocalValuePin4", + "@sbol:name": "gain", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#double" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification7" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin4/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "measurements", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "numFlashes", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow61", + "@sbol:displayId": "Flow61", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value3", + "@sbol:displayId": "Value3", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Value" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin5", + "@sbol:displayId": "Pin5", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow29", + "@sbol:displayId": "Flow29", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "numFlashes", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification6" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow33", + "@sbol:displayId": "Flow33", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow16", + "@sbol:displayId": "Flow16", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Join1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "shakingFrequency", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow46", + "@sbol:displayId": "Flow46", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "emissionBandpassWidth", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 530.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/ReferenceValuePin2", + "@sbol:displayId": "ReferenceValuePin2", + "@sbol:name": "resource", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ReferenceValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@sbol:Component" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification1" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/SC_Media_plus_dox" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow17", + "@sbol:displayId": "Flow17", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value4" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "type", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#anyURI" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "temperature", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification4", + "@sbol:displayId": "PrimitivePinSpecification4", + "@sbol:name": "dispenseVelocity", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1", + "@sbol:displayId": "SubProtocol1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#SubProtocol" + ], + "http://bioprotocols.org/paml#input": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin5" + }, + "http://bioprotocols.org/paml#instanceOfSubProtocol": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure" + }, + "http://bioprotocols.org/paml#output": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin1" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow10", + "@sbol:displayId": "Flow10", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Fork1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "dispenseVelocity", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 3.0, + "@om:hasUnit": { + "@id": "@om:hour" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow59", + "@sbol:displayId": "Flow59", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "amount", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 117200.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow55", + "@sbol:displayId": "Flow55", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 530.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "source", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable5", + "@sbol:displayId": "PrimitiveExecutable5", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable5/Pin1" + }, + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow1", + "@sbol:displayId": "Flow1", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "excitationWavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin4/Measure1", + "@om:hasNumericalValue": 0.16, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin4", + "@sbol:displayId": "LocalValuePin4", + "@sbol:name": "gain", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#double" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification7" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin4/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow41", + "@sbol:displayId": "Flow41", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "shakingFrequency", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow11", + "@sbol:displayId": "Flow11", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/Pin2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Fork1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin5", + "@sbol:displayId": "Pin5", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification5", + "@sbol:displayId": "PrimitivePinSpecification5", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 488.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow34", + "@sbol:displayId": "Flow34", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification5", + "@sbol:displayId": "PrimitivePinSpecification5", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Final1", + "@sbol:displayId": "Final1", + "@type": [ + "http://bioprotocols.org/paml#Final", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "duration", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow14", + "@sbol:displayId": "Flow14", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value2", + "@sbol:displayId": "Value2", + "@type": [ + "http://bioprotocols.org/paml#Value", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification1", + "@sbol:displayId": "ProtocolPinSpecification1", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#ProtocolPinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "destination", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Fork1", + "@sbol:displayId": "Fork1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Fork" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable6/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#ReferenceValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover/PrimitivePinSpecification1" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Container1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow1", + "@sbol:displayId": "Flow1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Value1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container1", + "@sbol:displayId": "Container1", + "@sbol:name": "Growth Curve Plate", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Container" + ], + "http://bioprotocols.org/paml#containerMaxCoordinate": "H12", + "http://bioprotocols.org/paml#containerType": { + "@id": "https://identifiers.org/ncit:C43377" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "source", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin6", + "@sbol:displayId": "Pin6", + "@sbol:name": "pbs", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13/StringConstantPin1", + "@sbol:displayId": "StringConstantPin1", + "@sbol:name": "type", + "@type": [ + "http://bioprotocols.org/paml#StringConstantPin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#anyURI" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#pinStringConstant": "http://autoprotocol.org/lids/breathable" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "source", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/rpm", + "@om:hasDenominator": { + "@id": "@om:minute-Time" + }, + "@om:hasNumerator": { + "@id": "@om:revolution" + }, + "@om:label": "revolutions per minute", + "@om:symbol": "rpm", + "@sbol:displayId": "rpm", + "@sbol:name": "rpm", + "@type": [ + "@om:UnitDivision", + "@sbol:TopLevel" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin4/Measure1", + "@om:hasNumericalValue": 0.2, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 3.0, + "@om:hasUnit": { + "@id": "@om:hour" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow4", + "@sbol:displayId": "Flow4", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow5", + "@sbol:displayId": "Flow5", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "dispenseVelocity", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#ReferenceValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow13", + "@sbol:displayId": "Flow13", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value2", + "@sbol:description": "Source for PBS", + "@sbol:displayId": "Value2", + "@sbol:name": "pbs", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Value" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7", + "@sbol:displayId": "SubProtocol7", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#SubProtocol" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin6" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin5" + } + ], + "http://bioprotocols.org/paml#instanceOfSubProtocol": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure" + }, + "http://bioprotocols.org/paml#output": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin2" + } + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal", + "@sbol:description": "Seal a collection of samples fixing the seal using a user-selected method, in order to guarantee isolation from the external environment", + "@sbol:displayId": "Seal", + "@type": [ + "@sbol:TopLevel", + "http://bioprotocols.org/paml#Primitive" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification2" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification1" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container4", + "@sbol:displayId": "Container4", + "@sbol:name": "Overnight SC Media Source", + "@type": [ + "http://bioprotocols.org/paml#Container", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#containerType": { + "@id": "https://identifiers.org/ncit:C43169" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 30.0, + "@om:hasUnit": { + "@id": "@om:degreeCelsius" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "duration", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "temperature", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow30", + "@sbol:displayId": "Flow30", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Join1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/ReferenceValuePin2", + "@sbol:displayId": "ReferenceValuePin2", + "@sbol:name": "resource", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ReferenceValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@sbol:Component" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification1" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/PBS" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "emissionLowpassCutoff", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin4/Measure1", + "@om:hasNumericalValue": 0.1, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow30", + "@sbol:displayId": "Flow30", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin6", + "@sbol:displayId": "Pin6", + "@sbol:name": "pbs", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "emissionLowpassCutoff", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow22", + "@sbol:displayId": "Flow22", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable6" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow26", + "@sbol:displayId": "Flow26", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow5", + "@sbol:displayId": "Flow5", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/Pin2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Fork1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9", + "@sbol:displayId": "PrimitiveExecutable9", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/Pin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance", + "@sbol:description": "Measure absorbance at a given wavelength from a set of samples", + "@sbol:displayId": "MeasureAbsorbance", + "@type": [ + "http://bioprotocols.org/paml#Primitive", + "@sbol:TopLevel" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification3" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification2" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification1" + } + ], + "http://bioprotocols.org/paml#output": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "numFlashes", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification6" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow22", + "@sbol:displayId": "Flow22", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification3", + "@sbol:displayId": "ProtocolPinSpecification3", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#ProtocolPinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value3" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2", + "@sbol:displayId": "PrimitiveExecutable2", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/ReferenceValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/ReferenceValuePin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3", + "@sbol:displayId": "PrimitiveExecutable3", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/ReferenceValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/ReferenceValuePin2" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4", + "@sbol:displayId": "PrimitiveExecutable4", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/ReferenceValuePin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow26", + "@sbol:displayId": "Flow26", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Join1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value6" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense", + "@sbol:description": "Move a measured volume of liquid from one source sample to create samples at multiple destination locations", + "@sbol:displayId": "Dispense", + "@type": [ + "http://bioprotocols.org/paml#Primitive", + "@sbol:TopLevel" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification3" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification1" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification2" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification4" + } + ], + "http://bioprotocols.org/paml#output": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "mixCycles", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow9", + "@sbol:displayId": "Flow9", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Fork1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20", + "@sbol:displayId": "PrimitiveExecutable20", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 10.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin6", + "@sbol:displayId": "Pin6", + "@sbol:name": "pbs", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "type", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#anyURI" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 117760.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 10.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification3", + "@sbol:displayId": "PrimitivePinSpecification3", + "@sbol:name": "amount", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable10", + "@sbol:displayId": "PrimitiveExecutable10", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable10/Pin1" + }, + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow49", + "@sbol:displayId": "Flow49", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5", + "@sbol:displayId": "PrimitiveExecutable5", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin2" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "destination", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow18", + "@sbol:displayId": "Flow18", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow11", + "@sbol:displayId": "Flow11", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 500.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 600.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "source", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable6", + "@sbol:displayId": "PrimitiveExecutable6", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable6/ReferenceValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable6/Pin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 2.0, + "@om:hasUnit": { + "@id": "@om:hour" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "duration", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "emissionLowpassCutoff", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1", + "@sbol:displayId": "ProtocolPinSpecification1", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ProtocolPinSpecification" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "temperature", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Final1", + "@sbol:displayId": "Final1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Final" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 10.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow2", + "@sbol:displayId": "Flow2", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/ProtocolPinSpecification1", + "@sbol:displayId": "ProtocolPinSpecification1", + "@sbol:name": "strain_plate", + "@type": [ + "http://bioprotocols.org/paml#ProtocolPinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Initial1", + "@sbol:displayId": "Initial1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Initial" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "amount", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow14", + "@sbol:displayId": "Flow14", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value4" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow14", + "@sbol:displayId": "Flow14", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Join1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value3" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "absorbance", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "emissionBandpassWidth", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification4" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3", + "@sbol:displayId": "PrimitivePinSpecification3", + "@sbol:name": "temperature", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification4", + "@sbol:displayId": "PrimitivePinSpecification4", + "@sbol:name": "dispenseVelocity", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value5", + "@sbol:displayId": "Value5", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Value" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow35", + "@sbol:displayId": "Flow35", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow3", + "@sbol:displayId": "Flow3", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable7/StringConstantPin1", + "@sbol:displayId": "StringConstantPin1", + "@sbol:name": "type", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#StringConstantPin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#anyURI" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#pinStringConstant": "http://autoprotocol.org/lids/breathable" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value4", + "@sbol:displayId": "Value4", + "@type": [ + "http://bioprotocols.org/paml#Value", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 488.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "shakingFrequency", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification3", + "@sbol:displayId": "PrimitivePinSpecification3", + "@sbol:name": "numFlashes", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/SC_Media", + "@sbol:displayId": "SC_Media", + "@sbol:name": "Synthetic Complete Media", + "@sbol:type": { + "@id": "TBD" + }, + "@type": "@sbol:Component" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#ReferenceValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container2" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow45", + "@sbol:displayId": "Flow45", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow11", + "@sbol:displayId": "Flow11", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable7" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow13", + "@sbol:displayId": "Flow13", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Fork1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6", + "@sbol:displayId": "ProtocolPinSpecification6", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "http://bioprotocols.org/paml#ProtocolPinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value6" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow12", + "@sbol:displayId": "Flow12", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value3" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin4/Measure1", + "@om:hasNumericalValue": 0.16, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "amount", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow17", + "@sbol:displayId": "Flow17", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Fork1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4", + "@sbol:displayId": "ProtocolPinSpecification4", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#ProtocolPinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value4" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1", + "@sbol:displayId": "PrimitiveExecutable1", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/ReferenceValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/ReferenceValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/LocalValuePin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "absorbance", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5", + "@sbol:displayId": "ProtocolPinSpecification5", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "http://bioprotocols.org/paml#ProtocolPinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "source", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow28", + "@sbol:displayId": "Flow28", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable8" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin5", + "@sbol:displayId": "Pin5", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "emissionBandpassWidth", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 98.0, + "@om:hasUnit": { + "@id": "@om:millilitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable5/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal/PrimitivePinSpecification1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification4", + "@sbol:displayId": "PrimitivePinSpecification4", + "@sbol:name": "mixCycles", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification2", + "@sbol:displayId": "ProtocolPinSpecification2", + "@sbol:name": "absorbance", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ProtocolPinSpecification" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value2" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 25.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin6", + "@sbol:displayId": "Pin6", + "@sbol:name": "pbs", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow21", + "@sbol:displayId": "Flow21", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision", + "@sbol:description": "Place a measured amount (mass or volume) of a specified component into a location, where it may then be used in executing the protocol.", + "@sbol:displayId": "Provision", + "@type": [ + "@sbol:TopLevel", + "http://bioprotocols.org/paml#Primitive" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification3" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification2" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification4" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification1" + } + ], + "http://bioprotocols.org/paml#output": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19", + "@sbol:displayId": "PrimitiveExecutable19", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/Pin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification3", + "@sbol:displayId": "PrimitivePinSpecification3", + "@sbol:name": "emissionBandpassWavelength", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "absorbance", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "wavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate", + "@sbol:description": "Incubate a set of samples under specified conditions for a fixed period of time", + "@sbol:displayId": "Incubate", + "@type": [ + "@sbol:TopLevel", + "http://bioprotocols.org/paml#Primitive" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow44", + "@sbol:displayId": "Flow44", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow12", + "@sbol:displayId": "Flow12", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "emissionBandpassWavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal", + "@sbol:description": "Unseal a sealed collection of samples to break their isolation from the external environment", + "@sbol:displayId": "Unseal", + "@type": [ + "@sbol:TopLevel", + "http://bioprotocols.org/paml#Primitive" + ], + "http://bioprotocols.org/paml#input": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow2", + "@sbol:displayId": "Flow2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/Pin2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification3", + "@sbol:displayId": "PrimitivePinSpecification3", + "@sbol:name": "amount", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 25.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin5", + "@sbol:displayId": "Pin5", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow22", + "@sbol:displayId": "Flow22", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Join1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "emissionBandpassWavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow10", + "@sbol:displayId": "Flow10", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin5", + "@sbol:displayId": "Pin5", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable7/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification4", + "@sbol:displayId": "PrimitivePinSpecification4", + "@sbol:name": "dispenseVelocity", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow37", + "@sbol:displayId": "Flow37", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow31", + "@sbol:displayId": "Flow31", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin5", + "@sbol:displayId": "Pin5", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable6/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "type", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#anyURI" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover/PrimitivePinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow4", + "@sbol:displayId": "Flow4", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Fork1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure", + "@sbol:description": "\nSubprotocol to split a portion of each sample in a plate into another plate, diluting \nwith PBS, then measure OD and fluorescence from that other plate\n", + "@sbol:displayId": "SplitAndMeasure", + "@sbol:name": "Split samples, dilute, and measure", + "@type": [ + "http://bioprotocols.org/paml#Protocol", + "@sbol:TopLevel" + ], + "http://bioprotocols.org/paml#hasActivity": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Final1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable8" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Join1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Initial1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value6" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Fork1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value5" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1" + } + ], + "http://bioprotocols.org/paml#hasFlow": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow14" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow6" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow30" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow21" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow8" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow9" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow29" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow15" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow12" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow23" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow28" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow10" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow7" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow22" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow27" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow13" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow24" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow17" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow16" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow5" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow18" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow26" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow19" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow31" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow11" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow25" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow20" + } + ], + "http://bioprotocols.org/paml#hasInput": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2" + } + ], + "http://bioprotocols.org/paml#hasLocation": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Container1" + }, + "http://bioprotocols.org/paml#hasOutput": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15", + "@sbol:displayId": "PrimitiveExecutable15", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin4", + "@sbol:displayId": "LocalValuePin4", + "@sbol:name": "gain", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#double" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification7" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin4/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow7", + "@sbol:displayId": "Flow7", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 25.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow16", + "@sbol:displayId": "Flow16", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#ReferenceValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12", + "@sbol:displayId": "PrimitiveExecutable12", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin4" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "shakingFrequency", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "emissionLowpassCutoff", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Initial1", + "@sbol:displayId": "Initial1", + "@type": [ + "http://bioprotocols.org/paml#Initial", + "@sbol:Identified" + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "excitationWavelength", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 350.0, + "@om:hasUnit": { + "@id": "https://sd2e.org/PAML/rpm" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 488.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow5", + "@sbol:displayId": "Flow5", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable2/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 3.0, + "@om:hasUnit": { + "@id": "@om:hour" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "measurements", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification8" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2", + "@sbol:displayId": "PrimitiveExecutable2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/LocalValuePin2" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow2", + "@sbol:displayId": "Flow2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow60", + "@sbol:displayId": "Flow60", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "duration", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "absorbance", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8", + "@sbol:displayId": "SubProtocol8", + "@type": [ + "http://bioprotocols.org/paml#SubProtocol", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin6" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin5" + } + ], + "http://bioprotocols.org/paml#instanceOfSubProtocol": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure" + }, + "http://bioprotocols.org/paml#output": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin3" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "measurements", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification8" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "duration", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11", + "@sbol:displayId": "PrimitiveExecutable11", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/ReferenceValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/Pin2" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 30.0, + "@om:hasUnit": { + "@id": "@om:degreeCelsius" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "numFlashes", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification6" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification6", + "@sbol:displayId": "PrimitivePinSpecification6", + "@sbol:name": "numFlashes", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "measurements", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value1", + "@sbol:description": "Samples to measure", + "@sbol:displayId": "Value1", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Value" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value4", + "@sbol:displayId": "Value4", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Value" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow39", + "@sbol:displayId": "Flow39", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow6", + "@sbol:displayId": "Flow6", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable5/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 700.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "destination", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ReferenceValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Container1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification5", + "@sbol:displayId": "PrimitivePinSpecification5", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 90.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "emissionBandpassWavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow27", + "@sbol:displayId": "Flow27", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable8" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Join1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 3.0, + "@om:hasUnit": { + "@id": "@om:hour" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification8", + "@sbol:displayId": "PrimitivePinSpecification8", + "@sbol:name": "measurements", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedData" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow18", + "@sbol:displayId": "Flow18", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable7", + "@sbol:displayId": "PrimitiveExecutable7", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable7/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable7/StringConstantPin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification4", + "@sbol:displayId": "PrimitivePinSpecification4", + "@sbol:name": "emissionBandpassWidth", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "excitationWavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SC_Media_plus_dox", + "@sbol:displayId": "SC_Media_plus_dox", + "@sbol:name": "Synthetic Complete Media plus 40nM Doxycycline", + "@sbol:type": { + "@id": "TBD" + }, + "@type": "@sbol:Component" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin5", + "@sbol:displayId": "Pin5", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "emissionBandpassWavelength", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "duration", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow31", + "@sbol:displayId": "Flow31", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow21", + "@sbol:displayId": "Flow21", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Join1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow16", + "@sbol:displayId": "Flow16", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable10/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 2.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable10/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 350.0, + "@om:hasUnit": { + "@id": "https://sd2e.org/PAML/rpm" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6", + "@sbol:displayId": "PrimitiveExecutable6", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin2" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Fork1", + "@sbol:displayId": "Fork1", + "@type": [ + "http://bioprotocols.org/paml#Fork", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow51", + "@sbol:displayId": "Flow51", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 530.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow42", + "@sbol:displayId": "Flow42", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3", + "@sbol:displayId": "PrimitiveExecutable3", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin4" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin4" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2", + "@sbol:displayId": "ProtocolPinSpecification2", + "@sbol:name": "pbs", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ProtocolPinSpecification" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value2" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow25", + "@sbol:displayId": "Flow25", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable2/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14", + "@sbol:displayId": "PrimitiveExecutable14", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification5", + "@sbol:displayId": "PrimitivePinSpecification5", + "@sbol:name": "emissionLowpassCutoff", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value5", + "@sbol:displayId": "Value5", + "@type": [ + "http://bioprotocols.org/paml#Value", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "emissionBandpassWidth", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17", + "@sbol:displayId": "PrimitiveExecutable17", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin3" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "source", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification5", + "@sbol:displayId": "PrimitivePinSpecification5", + "@sbol:name": "dispenseVelocity", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container3", + "@sbol:displayId": "Container3", + "@sbol:name": "PBS Source", + "@type": [ + "http://bioprotocols.org/paml#Container", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#containerType": { + "@id": "https://identifiers.org/ncit:C43169" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 350.0, + "@om:hasUnit": { + "@id": "https://sd2e.org/PAML/rpm" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container5", + "@sbol:displayId": "Container5", + "@sbol:name": "Overnight Growth Plate", + "@type": [ + "http://bioprotocols.org/paml#Container", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#containerMaxCoordinate": "H12", + "http://bioprotocols.org/paml#containerType": { + "@id": "https://identifiers.org/ncit:C43377" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve", + "@sbol:description": "\nProtocol from SD2 Yeast States working group for studying growth curves:\nGrow up cells and read with plate reader at n-hour intervals\n", + "@sbol:displayId": "GrowthCurve", + "@sbol:name": "SD2 Yeast growth curve protocol", + "@type": [ + "@sbol:TopLevel", + "http://bioprotocols.org/paml#Protocol" + ], + "http://bioprotocols.org/paml#hasActivity": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable10" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Initial1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable5" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable7" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Value1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Final1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4" + } + ], + "http://bioprotocols.org/paml#hasFlow": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow25" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow11" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow53" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow38" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow20" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow9" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow41" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow30" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow60" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow44" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow51" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow13" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow37" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow61" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow31" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow45" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow14" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow26" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow8" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow47" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow28" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow56" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow24" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow18" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow59" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow48" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow16" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow21" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow19" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow43" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow7" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow12" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow34" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow39" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow15" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow36" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow35" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow55" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow33" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow6" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow58" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow46" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow40" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow22" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow52" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow23" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow27" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow32" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow29" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow5" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow42" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow50" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow54" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow49" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow57" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow10" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow17" + } + ], + "http://bioprotocols.org/paml#hasInput": { + "@id": "https://sd2e.org/PAML/GrowthCurve/ProtocolPinSpecification1" + }, + "http://bioprotocols.org/paml#hasLocation": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container5" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container2" + } + ], + "http://bioprotocols.org/paml#material": [ + { + "@id": "https://sd2e.org/PAML/SC_Media" + }, + { + "@id": "https://sd2e.org/PAML/SC_Media_plus_dox" + }, + { + "@id": "https://sd2e.org/PAML/PBS" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow9", + "@sbol:displayId": "Flow9", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable7/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 30.0, + "@om:hasUnit": { + "@id": "@om:degreeCelsius" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification3" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 25.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin5", + "@sbol:displayId": "Pin5", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4", + "@sbol:displayId": "SubProtocol4", + "@type": [ + "http://bioprotocols.org/paml#SubProtocol", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin6" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin5" + } + ], + "http://bioprotocols.org/paml#instanceOfSubProtocol": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure" + }, + "http://bioprotocols.org/paml#output": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin4" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Initial1", + "@sbol:displayId": "Initial1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Initial" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "dispenseVelocity", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 16.0, + "@om:hasUnit": { + "@id": "@om:hour" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "location", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "location", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable5/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 488.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "shakingFrequency", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "source", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "destination", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ReferenceValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Container1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#ReferenceValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container3" + } + }, + { + "@id": "https://sd2e.org/PAML/PBS", + "@sbol:displayId": "PBS", + "@sbol:name": "Phosphate-Buffered Saline", + "@sbol:type": { + "@id": "https://identifiers.org/pubchem.substance:24978514" + }, + "@type": "@sbol:Component" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification3", + "@sbol:displayId": "PrimitivePinSpecification3", + "@sbol:name": "amount", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8", + "@sbol:displayId": "PrimitiveExecutable8", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8/StringConstantPin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable8/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "location", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ReferenceValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover/PrimitivePinSpecification1" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Container1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow43", + "@sbol:displayId": "Flow43", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 350.0, + "@om:hasUnit": { + "@id": "https://sd2e.org/PAML/rpm" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 30.0, + "@om:hasUnit": { + "@id": "@om:degreeCelsius" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification6", + "@sbol:displayId": "PrimitivePinSpecification6", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8/StringConstantPin1", + "@sbol:displayId": "StringConstantPin1", + "@sbol:name": "type", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#StringConstantPin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#anyURI" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#pinStringConstant": "http://autoprotocol.org/lids/breathable" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow36", + "@sbol:displayId": "Flow36", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin6", + "@sbol:displayId": "Pin6", + "@sbol:name": "pbs", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Join1", + "@sbol:displayId": "Join1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Join" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3", + "@sbol:displayId": "PrimitiveExecutable3", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin2" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "mixCycles", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "temperature", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "shakingFrequency", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow7", + "@sbol:displayId": "Flow7", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin4" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "emissionBandpassWavelength", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow21", + "@sbol:displayId": "Flow21", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin4" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow15", + "@sbol:displayId": "Flow15", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value4" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "source", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable8/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "type", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#anyURI" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover/PrimitivePinSpecification2" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence", + "@sbol:description": "Measure fluorescence intensity from a set of samples stimulated by a given wavelength, with an optional bandpass or lowpass filter", + "@sbol:displayId": "MeasureFluorescence", + "@type": [ + "http://bioprotocols.org/paml#Primitive", + "@sbol:TopLevel" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification5" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification3" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification7" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification1" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification4" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification2" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification6" + } + ], + "http://bioprotocols.org/paml#output": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification8" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "temperature", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification4", + "@sbol:displayId": "PrimitivePinSpecification4", + "@sbol:name": "measurements", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedData" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin4", + "@sbol:displayId": "LocalValuePin4", + "@sbol:name": "gain", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#double" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification7" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin4/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow58", + "@sbol:displayId": "Flow58", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow3", + "@sbol:displayId": "Flow3", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value2" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow25", + "@sbol:displayId": "Flow25", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin6", + "@sbol:displayId": "Pin6", + "@sbol:name": "pbs", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow48", + "@sbol:displayId": "Flow48", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Join1", + "@sbol:displayId": "Join1", + "@type": [ + "http://bioprotocols.org/paml#Join", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "measurements", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification8" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 25.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "excitationWavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "amount", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin4/Measure1", + "@om:hasNumericalValue": 0.2, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "absorbance", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "excitationWavelength", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "amount", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5", + "@sbol:displayId": "PrimitiveExecutable5", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin4" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable8", + "@sbol:displayId": "PrimitiveExecutable8", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable8/ReferenceValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable8/Pin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow20", + "@sbol:displayId": "Flow20", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable11/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "mixCycles", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "numFlashes", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification6" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow52", + "@sbol:displayId": "Flow52", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow12", + "@sbol:displayId": "Flow12", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Join1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value3" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 1.0, + "@om:hasUnit": { + "@id": "@om:hour" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "shakingFrequency", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow23", + "@sbol:displayId": "Flow23", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Fork1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Final1", + "@sbol:displayId": "Final1", + "@type": [ + "http://bioprotocols.org/paml#Final", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow19", + "@sbol:displayId": "Flow19", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Fork1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "absorbance", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow23", + "@sbol:displayId": "Flow23", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable13/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "source", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification4", + "@sbol:displayId": "ProtocolPinSpecification4", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ProtocolPinSpecification" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow53", + "@sbol:displayId": "Flow53", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover", + "@sbol:description": "Cover a set of samples to keep materials from entering or exiting", + "@sbol:displayId": "Cover", + "@type": [ + "http://bioprotocols.org/paml#Primitive", + "@sbol:TopLevel" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover/PrimitivePinSpecification1" + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Cover/PrimitivePinSpecification2" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin6", + "@sbol:displayId": "Pin6", + "@sbol:name": "pbs", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 530.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow57", + "@sbol:displayId": "Flow57", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow1", + "@sbol:displayId": "Flow1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value3", + "@sbol:displayId": "Value3", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Value" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "emissionBandpassWidth", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1", + "@sbol:displayId": "PrimitiveExecutable1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/ReferenceValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/Pin3" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16", + "@sbol:displayId": "PrimitiveExecutable16", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/LocalValuePin2" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "absorbance", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "destination", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification2" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 350.0, + "@om:hasUnit": { + "@id": "https://sd2e.org/PAML/rpm" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value6", + "@sbol:displayId": "Value6", + "@type": [ + "http://bioprotocols.org/paml#Value", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow19", + "@sbol:displayId": "Flow19", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "dispenseVelocity", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value1", + "@sbol:description": "Samples to measure", + "@sbol:displayId": "Value1", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Value", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow9", + "@sbol:displayId": "Flow9", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable2" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "numFlashes", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification6" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "measurements", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification8" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "amount", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable4/ReferenceValuePin1", + "@sbol:displayId": "ReferenceValuePin1", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#ReferenceValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#Location" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification3", + "@sbol:displayId": "PrimitivePinSpecification3", + "@sbol:name": "amount", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "numFlashes", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable4/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Container1", + "@sbol:displayId": "Container1", + "@sbol:name": "OD Plate", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Container" + ], + "http://bioprotocols.org/paml#containerMaxCoordinate": "H12", + "http://bioprotocols.org/paml#containerType": { + "@id": "https://identifiers.org/ncit:C43377" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 30.0, + "@om:hasUnit": { + "@id": "@om:degreeCelsius" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol8/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "fluorescence_0.1", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Container1", + "@sbol:displayId": "Container1", + "@sbol:name": "OD Plate", + "@type": [ + "http://bioprotocols.org/paml#Container", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#containerMaxCoordinate": "H12", + "http://bioprotocols.org/paml#containerType": { + "@id": "https://identifiers.org/ncit:C43377" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow40", + "@sbol:displayId": "Flow40", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable16/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable2", + "@sbol:displayId": "PrimitiveExecutable2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable2/Pin1" + }, + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Unseal" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "duration", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow18", + "@sbol:displayId": "Flow18", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Join1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6", + "@sbol:displayId": "SubProtocol6", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#SubProtocol" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin5" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin6" + } + ], + "http://bioprotocols.org/paml#instanceOfSubProtocol": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure" + }, + "http://bioprotocols.org/paml#output": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin1" + } + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 9.0, + "@om:hasUnit": { + "@id": "@om:hour" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow20", + "@sbol:displayId": "Flow20", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Join1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable6/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 5.0, + "@om:hasUnit": { + "@id": "@om:microlitre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "destination", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "temperature", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18", + "@sbol:displayId": "PrimitiveExecutable18", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/Pin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification3", + "@sbol:displayId": "ProtocolPinSpecification3", + "@sbol:name": "absorbance", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ProtocolPinSpecification" + ], + "http://bioprotocols.org/paml#pinActivity": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value3" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure", + "@sbol:description": "\nSubprotocol to split a portion of each sample in an unsealed plate into another plate, then measure OD and fluorescence from that other plate\n", + "@sbol:displayId": "OvernightODMeasure", + "@sbol:name": "Split samples and measure, without dilution", + "@type": [ + "http://bioprotocols.org/paml#Protocol", + "@sbol:TopLevel" + ], + "http://bioprotocols.org/paml#hasActivity": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable6" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Initial1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Fork1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Final1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value4" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Join1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value5" + } + ], + "http://bioprotocols.org/paml#hasFlow": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow4" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow7" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow8" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow19" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow9" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow20" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow15" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow14" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow11" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow13" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow22" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow12" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow17" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow21" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow10" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow18" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow5" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow16" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow6" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow23" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow2" + } + ], + "http://bioprotocols.org/paml#hasInput": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification1" + }, + "http://bioprotocols.org/paml#hasLocation": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Container1" + }, + "http://bioprotocols.org/paml#hasOutput": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification5" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/ProtocolPinSpecification4" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable2/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 600.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4", + "@sbol:displayId": "PrimitivePinSpecification4", + "@sbol:name": "shakingFrequency", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "numFlashes", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#integer" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification6" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "measurements", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification8" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow4", + "@sbol:displayId": "Flow4", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Initial1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "measurements", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification8" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow54", + "@sbol:displayId": "Flow54", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable19" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "fluorescence_0.16", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "amount", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow50", + "@sbol:displayId": "Flow50", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureAbsorbance/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "wavelength", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 350.0, + "@om:hasUnit": { + "@id": "https://sd2e.org/PAML/rpm" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable17/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "location", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Container2", + "@sbol:displayId": "Container2", + "@sbol:name": "SC Media + 40nM Doxycycline Source", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Container" + ], + "http://bioprotocols.org/paml#containerType": { + "@id": "https://identifiers.org/ncit:C43169" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 488.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9/StringConstantPin1", + "@sbol:displayId": "StringConstantPin1", + "@sbol:name": "type", + "@type": [ + "http://bioprotocols.org/paml#StringConstantPin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#anyURI" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#pinStringConstant": "http://autoprotocol.org/lids/breathable" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow15", + "@sbol:displayId": "Flow15", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable8" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/ReferenceValuePin2", + "@sbol:displayId": "ReferenceValuePin2", + "@sbol:name": "resource", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#ReferenceValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@sbol:Component" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification1" + }, + "http://bioprotocols.org/paml#referenceValue": { + "@id": "https://sd2e.org/PAML/SC_Media" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin4/Measure1", + "@om:hasNumericalValue": 0.1, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow28", + "@sbol:displayId": "Flow28", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol2/Pin5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow29", + "@sbol:displayId": "Flow29", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9/Pin1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Value1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow19", + "@sbol:displayId": "Flow19", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable10" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "dispenseVelocity", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1", + "@sbol:displayId": "PrimitiveExecutable1", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/ReferenceValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/Pin3" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/TransferInto/PrimitivePinSpecification6" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/LocalValuePin1", + "@sbol:displayId": "LocalValuePin1", + "@sbol:name": "amount", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Transfer/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable1/LocalValuePin1/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 30.0, + "@om:hasUnit": { + "@id": "@om:degreeCelsius" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin3", + "@sbol:displayId": "LocalValuePin3", + "@sbol:name": "shakingFrequency", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification4" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable15/LocalValuePin3/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5", + "@sbol:displayId": "SubProtocol5", + "@type": [ + "http://bioprotocols.org/paml#SubProtocol", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin6" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin5" + } + ], + "http://bioprotocols.org/paml#instanceOfSubProtocol": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure" + }, + "http://bioprotocols.org/paml#output": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol5/Pin2" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin4", + "@sbol:displayId": "Pin4", + "@sbol:name": "emissionBandpassWidth", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification4" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable14/LocalValuePin3/Measure1", + "@om:hasNumericalValue": 350.0, + "@om:hasUnit": { + "@id": "https://sd2e.org/PAML/rpm" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable7/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "emissionLowpassCutoff", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/Flow8", + "@sbol:displayId": "Flow8", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable3/Pin2" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 10.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "temperature", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable20/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Value1", + "@sbol:description": "Plate of strains to grow", + "@sbol:displayId": "Value1", + "@sbol:name": "strain_plate", + "@type": [ + "http://bioprotocols.org/paml#Value", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification7", + "@sbol:displayId": "PrimitivePinSpecification7", + "@sbol:name": "gain", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://www.w3.org/2001/XMLSchema#double" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9", + "@sbol:displayId": "PrimitiveExecutable9", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9/StringConstantPin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable9/Pin1" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Seal" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable1/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "source", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "http://bioprotocols.org/paml#LocatedSamples" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Dispense/PrimitivePinSpecification1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol7/Pin3", + "@sbol:displayId": "Pin3", + "@sbol:name": "fluorescence_0.2", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/ProtocolPinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable5/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 25.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow38", + "@sbol:displayId": "Flow38", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol4/Pin6" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable1/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 30.0, + "@om:hasUnit": { + "@id": "@om:degreeCelsius" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3", + "@sbol:displayId": "SubProtocol3", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#SubProtocol" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin5" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin6" + } + ], + "http://bioprotocols.org/paml#instanceOfSubProtocol": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure" + }, + "http://bioprotocols.org/paml#output": [ + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin1" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol3/Pin4" + } + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow23", + "@sbol:displayId": "Flow23", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Join1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "emissionLowpassCutoff", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification5" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6", + "@sbol:displayId": "PrimitiveExecutable6", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitiveExecutable" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin4" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin4" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable3/LocalValuePin1/Measure1", + "@om:hasNumericalValue": 25.0, + "@om:hasUnit": { + "@id": "@om:Number" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@sbol:Identified", + "@om:Measure" + ] + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 488.0, + "@om:hasUnit": { + "@id": "@om:nanometre" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow10", + "@sbol:displayId": "Flow10", + "@type": [ + "http://bioprotocols.org/paml#Flow", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value3" + } + }, + { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "excitationWavelength", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#LocalValuePin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence/PrimitivePinSpecification2" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/SplitAndMeasure/PrimitiveExecutable6/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4", + "@sbol:displayId": "PrimitiveExecutable4", + "@type": [ + "http://bioprotocols.org/paml#PrimitiveExecutable", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#input": [ + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin4" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin2" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin1" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin3" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin4" + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/LocalValuePin3" + } + ], + "http://bioprotocols.org/paml#instanceOfPrimitive": { + "@id": "https://bioprotocols.org/paml/primitives/spectrophotometry/MeasureFluorescence" + }, + "http://bioprotocols.org/paml#output": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/PrimitiveExecutable4/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable9/LocalValuePin2/Measure1", + "@om:hasNumericalValue": 30.0, + "@om:hasUnit": { + "@id": "@om:degreeCelsius" + }, + "@sbol:displayId": "Measure1", + "@type": [ + "@om:Measure", + "@sbol:Identified" + ] + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/Flow47", + "@sbol:displayId": "Flow47", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/GrowthCurve/SubProtocol6/Pin5" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable12/Pin1" + } + }, + { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Flow6", + "@sbol:displayId": "Flow6", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Flow" + ], + "http://bioprotocols.org/paml#sink": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Final1" + }, + "http://bioprotocols.org/paml#source": { + "@id": "https://sd2e.org/PAML/OvernightODMeasure/Value2" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable3/Pin1", + "@sbol:displayId": "Pin1", + "@sbol:name": "samples", + "@type": [ + "http://bioprotocols.org/paml#Pin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification5" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification1", + "@sbol:displayId": "PrimitivePinSpecification1", + "@sbol:name": "resource", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#PrimitivePinSpecification" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@sbol:Component" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin2", + "@sbol:displayId": "LocalValuePin2", + "@sbol:name": "temperature", + "@type": [ + "http://bioprotocols.org/paml#LocalValuePin", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification3" + }, + "http://bioprotocols.org/paml#localValue": { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable18/LocalValuePin2/Measure1" + } + }, + { + "@id": "https://sd2e.org/PAML/GrowthCurve/PrimitiveExecutable2/Pin2", + "@sbol:displayId": "Pin2", + "@sbol:name": "dispenseVelocity", + "@type": [ + "@sbol:Identified", + "http://bioprotocols.org/paml#Pin" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + }, + "http://bioprotocols.org/paml#instanceOfPinSpecification": { + "@id": "https://bioprotocols.org/paml/primitives/liquid_handling/Provision/PrimitivePinSpecification4" + } + }, + { + "@id": "https://bioprotocols.org/paml/primitives/plate_handling/Incubate/PrimitivePinSpecification2", + "@sbol:displayId": "PrimitivePinSpecification2", + "@sbol:name": "duration", + "@type": [ + "http://bioprotocols.org/paml#PrimitivePinSpecification", + "@sbol:Identified" + ], + "http://bioprotocols.org/paml#activityType": { + "@id": "@om:Measure" + } + } + ] +} \ No newline at end of file diff --git a/test/testfiles/growth_curve.ttl b/test/testfiles/growth_curve.ttl new file mode 100644 index 0000000..153a750 --- /dev/null +++ b/test/testfiles/growth_curve.ttl @@ -0,0 +1,3754 @@ +@prefix ns1: . +@prefix om: . +@prefix sbol: . +@prefix xsd: . + + a ns1:Protocol, + sbol:TopLevel ; + ns1:hasActivity , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + ns1:hasFlow , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + ns1:hasInput ; + ns1:hasLocation , + , + , + , + ; + ns1:material , + , + ; + sbol:description """ +Protocol from SD2 Yeast States working group for studying growth curves: +Grow up cells and read with plate reader at n-hour intervals +""" ; + sbol:displayId "GrowthCurve" ; + sbol:name "SD2 Yeast growth curve protocol" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + , + , + ; + ns1:output ; + sbol:description "Move a measured volume taken from an array of source samples to an identically shaped array in a destination locations" ; + sbol:displayId "Transfer" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow1" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow10" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow11" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow12" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow13" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow14" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow15" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow16" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow17" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow18" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow19" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow2" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow20" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow21" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow22" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow23" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow24" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow25" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow26" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow27" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow28" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow29" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow3" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow30" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow31" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow32" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow33" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow34" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow35" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow36" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow37" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow38" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow39" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow4" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow40" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow41" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow42" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow43" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow44" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow45" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow46" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow47" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow48" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow49" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow5" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow50" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow51" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow52" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow53" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow54" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow55" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow56" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow57" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow58" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow59" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow6" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow60" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow61" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow7" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow8" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow9" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1.1776e+05 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "dispenseVelocity" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:Location ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "destination" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType sbol:Component ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin2" ; + sbol:name "resource" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable11" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 7e+02 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "dispenseVelocity" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:Location ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "destination" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "mixCycles" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2e+00 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "dispenseVelocity" . + + a ns1:StringConstantPin, + sbol:Identified ; + ns1:activityType xsd:anyURI ; + ns1:instanceOfPinSpecification ; + ns1:pinStringConstant "http://autoprotocol.org/lids/breathable" ; + sbol:displayId "StringConstantPin1" ; + sbol:name "type" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "duration" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1e+00 ; + om:hasUnit om:hour . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "temperature" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+01 ; + om:hasUnit om:degreeCelsius . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "shakingFrequency" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3.5e+02 ; + om:hasUnit . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "duration" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2e+00 ; + om:hasUnit om:hour . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "temperature" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+01 ; + om:hasUnit om:degreeCelsius . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "shakingFrequency" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3.5e+02 ; + om:hasUnit . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "duration" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+00 ; + om:hasUnit om:hour . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "temperature" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+01 ; + om:hasUnit om:degreeCelsius . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "shakingFrequency" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3.5e+02 ; + om:hasUnit . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "duration" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+00 ; + om:hasUnit om:hour . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "temperature" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+01 ; + om:hasUnit om:degreeCelsius . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "shakingFrequency" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3.5e+02 ; + om:hasUnit . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "duration" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 9e+00 ; + om:hasUnit om:hour . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "temperature" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+01 ; + om:hasUnit om:degreeCelsius . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "shakingFrequency" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3.5e+02 ; + om:hasUnit . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "duration" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+00 ; + om:hasUnit om:hour . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "temperature" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+01 ; + om:hasUnit om:degreeCelsius . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "shakingFrequency" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3.5e+02 ; + om:hasUnit . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 9.8e+01 ; + om:hasUnit om:millilitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "dispenseVelocity" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:Location ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "destination" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType sbol:Component ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin2" ; + sbol:name "resource" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "duration" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+00 ; + om:hasUnit om:hour . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "temperature" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+01 ; + om:hasUnit om:degreeCelsius . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "shakingFrequency" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3.5e+02 ; + om:hasUnit . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1.172e+05 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "dispenseVelocity" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:Location ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "destination" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType sbol:Component ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin2" ; + sbol:name "resource" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable4" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5e+02 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "dispenseVelocity" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:Location ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "destination" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "mixCycles" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5e+00 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "dispenseVelocity" . + + a ns1:StringConstantPin, + sbol:Identified ; + ns1:activityType xsd:anyURI ; + ns1:instanceOfPinSpecification ; + ns1:pinStringConstant "http://autoprotocol.org/lids/breathable" ; + sbol:displayId "StringConstantPin1" ; + sbol:name "type" . + + a ns1:StringConstantPin, + sbol:Identified ; + ns1:activityType xsd:anyURI ; + ns1:instanceOfPinSpecification ; + ns1:pinStringConstant "http://autoprotocol.org/lids/breathable" ; + sbol:displayId "StringConstantPin1" ; + sbol:name "type" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "duration" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1.6e+01 ; + om:hasUnit om:hour . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "temperature" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3e+01 ; + om:hasUnit om:degreeCelsius . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "shakingFrequency" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 3.5e+02 ; + om:hasUnit . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification1" ; + sbol:name "strain_plate" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "absorbance" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "fluorescence_0.1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "fluorescence_0.2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "fluorescence_0.16" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "absorbance" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "fluorescence_0.1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "fluorescence_0.2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "fluorescence_0.16" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "absorbance" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "fluorescence_0.1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "fluorescence_0.2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "fluorescence_0.16" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "absorbance" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "fluorescence_0.1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "fluorescence_0.2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "fluorescence_0.16" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "absorbance" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "fluorescence_0.1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "fluorescence_0.2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "fluorescence_0.16" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "absorbance" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "fluorescence_0.1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "fluorescence_0.2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "fluorescence_0.16" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "absorbance" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "fluorescence_0.1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "fluorescence_0.2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "fluorescence_0.16" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "absorbance" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "fluorescence_0.1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "fluorescence_0.2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "fluorescence_0.16" . + + a ns1:Protocol, + sbol:TopLevel ; + ns1:hasActivity , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + ns1:hasFlow , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + ns1:hasInput ; + ns1:hasLocation ; + ns1:hasOutput , + , + , + ; + sbol:description """ +Subprotocol to split a portion of each sample in an unsealed plate into another plate, then measure OD and fluorescence from that other plate +""" ; + sbol:displayId "OvernightODMeasure" ; + sbol:name "Split samples and measure, without dilution" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow1" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow10" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow11" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow12" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow13" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow14" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow15" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow16" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow17" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow18" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow19" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow2" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow20" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow21" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow22" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow23" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow3" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow4" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow5" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow6" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow7" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow8" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow9" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2e+02 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "dispenseVelocity" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:Location ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "destination" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable2" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:integer ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "numFlashes" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2.5e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "wavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 6e+02 ; + om:hasUnit om:nanometre . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable3" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:integer ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "numFlashes" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2.5e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "excitationWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 4.88e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "emissionBandpassWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5.3e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:double ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin4" ; + sbol:name "gain" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1e-01 ; + om:hasUnit om:Number . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "emissionLowpassCutoff" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "emissionBandpassWidth" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable4" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:integer ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "numFlashes" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2.5e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "excitationWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 4.88e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "emissionBandpassWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5.3e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:double ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin4" ; + sbol:name "gain" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2e-01 ; + om:hasUnit om:Number . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "emissionLowpassCutoff" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "emissionBandpassWidth" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable5" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:integer ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "numFlashes" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2.5e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "excitationWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 4.88e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "emissionBandpassWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5.3e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:double ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin4" ; + sbol:name "gain" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1.6e-01 ; + om:hasUnit om:Number . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "emissionLowpassCutoff" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "emissionBandpassWidth" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType xsd:anyURI ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "type" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "location" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow1" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow10" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow11" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow12" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow13" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow14" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow15" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow16" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow17" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow18" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow19" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow2" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow20" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow21" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow22" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow23" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow24" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow25" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow26" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow27" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow28" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow29" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow3" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow30" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow31" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow4" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow5" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow6" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow7" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow8" . + + a ns1:Flow, + sbol:Identified ; + ns1:sink ; + ns1:source ; + sbol:displayId "Flow9" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 9e+01 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "dispenseVelocity" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:Location ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "destination" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "mixCycles" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "amount" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1e+01 ; + om:hasUnit om:microlitre . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "dispenseVelocity" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable4" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:integer ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "numFlashes" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2.5e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "wavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 6e+02 ; + om:hasUnit om:nanometre . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable5" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:integer ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "numFlashes" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2.5e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "excitationWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 4.88e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "emissionBandpassWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5.3e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:double ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin4" ; + sbol:name "gain" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1e-01 ; + om:hasUnit om:Number . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "emissionLowpassCutoff" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "emissionBandpassWidth" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable6" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:integer ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "numFlashes" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2.5e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "excitationWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 4.88e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "emissionBandpassWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5.3e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:double ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin4" ; + sbol:name "gain" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2e-01 ; + om:hasUnit om:Number . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "emissionLowpassCutoff" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "emissionBandpassWidth" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable7" . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:integer ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin1" ; + sbol:name "numFlashes" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 2.5e+01 ; + om:hasUnit om:Number . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin2" ; + sbol:name "excitationWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 4.88e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin3" ; + sbol:name "emissionBandpassWavelength" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 5.3e+02 ; + om:hasUnit om:nanometre . + + a ns1:LocalValuePin, + sbol:Identified ; + ns1:activityType xsd:double ; + ns1:instanceOfPinSpecification ; + ns1:localValue ; + sbol:displayId "LocalValuePin4" ; + sbol:name "gain" . + + a sbol:Identified, + om:Measure ; + sbol:displayId "Measure1" ; + om:hasNumericalValue 1.6e-01 ; + om:hasUnit om:Number . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "emissionLowpassCutoff" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType om:Measure ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "emissionBandpassWidth" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType xsd:anyURI ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "type" . + + a ns1:ReferenceValuePin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + ns1:referenceValue ; + sbol:displayId "ReferenceValuePin1" ; + sbol:name "location" . + + a ns1:StringConstantPin, + sbol:Identified ; + ns1:activityType xsd:anyURI ; + ns1:instanceOfPinSpecification ; + ns1:pinStringConstant "http://autoprotocol.org/lids/breathable" ; + sbol:displayId "StringConstantPin1" ; + sbol:name "type" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "source" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:Location ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "destination" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification3" ; + sbol:name "amount" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification4" ; + sbol:name "dispenseVelocity" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification5" ; + sbol:name "samples" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + ; + sbol:description "Cover a set of samples to keep materials from entering or exiting" ; + sbol:displayId "Cover" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + , + ; + ns1:output ; + sbol:description "Measure absorbance at a given wavelength from a set of samples" ; + sbol:displayId "MeasureAbsorbance" . + + a ns1:Container, + sbol:Identified ; + ns1:containerMaxCoordinate "H12" ; + ns1:containerType ; + sbol:displayId "Container1" ; + sbol:name "Growth Curve Plate" . + + a ns1:Container, + sbol:Identified ; + ns1:containerType ; + sbol:displayId "Container2" ; + sbol:name "SC Media + 40nM Doxycycline Source" . + + a ns1:Container, + sbol:Identified ; + ns1:containerType ; + sbol:displayId "Container3" ; + sbol:name "PBS Source" . + + a ns1:Container, + sbol:Identified ; + ns1:containerType ; + sbol:displayId "Container4" ; + sbol:name "Overnight SC Media Source" . + + a ns1:Container, + sbol:Identified ; + ns1:containerMaxCoordinate "H12" ; + ns1:containerType ; + sbol:displayId "Container5" ; + sbol:name "Overnight Growth Plate" . + + a ns1:Final, + sbol:Identified ; + sbol:displayId "Final1" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable1" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "source" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "destination" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "source" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable14" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable2" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable3" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "source" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable5" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "destination" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "source" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable7" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin5" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin5" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin6" ; + sbol:name "pbs" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin5" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin6" ; + sbol:name "pbs" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin5" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin6" ; + sbol:name "pbs" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin5" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin6" ; + sbol:name "pbs" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin5" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin6" ; + sbol:name "pbs" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin5" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin6" ; + sbol:name "pbs" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin5" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin6" ; + sbol:name "pbs" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "source" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "samples" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification1" ; + sbol:name "samples" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification2" ; + sbol:name "absorbance" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification3" ; + sbol:name "fluorescence_0.1" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification4" ; + sbol:name "fluorescence_0.2" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification5" ; + sbol:name "fluorescence_0.16" . + + a sbol:Component ; + sbol:displayId "PBS" ; + sbol:name "Phosphate-Buffered Saline" ; + sbol:type . + + a sbol:Component ; + sbol:displayId "SC_Media" ; + sbol:name "Synthetic Complete Media" ; + sbol:type . + + a sbol:Component ; + sbol:displayId "SC_Media_plus_dox" ; + sbol:name "Synthetic Complete Media plus 40nM Doxycycline" ; + sbol:type . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable1" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "source" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable3" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "destination" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin4" ; + sbol:name "source" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin2" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin3" ; + sbol:name "samples" . + + a ns1:Pin, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "location" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + , + , + ; + ns1:output ; + sbol:description "Move a measured volume of liquid from one source sample to create samples at multiple destination locations" ; + sbol:displayId "Dispense" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + , + , + ; + ns1:output ; + sbol:description "Place a measured amount (mass or volume) of a specified component into a location, where it may then be used in executing the protocol." ; + sbol:displayId "Provision" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + , + , + , + ; + ns1:output ; + sbol:description "Mix a measured volume taken from an array of source samples intto an identically shaped array of destination samples" ; + sbol:displayId "TransferInto" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "location" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType xsd:anyURI ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "type" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input ; + sbol:description "Unseal a sealed collection of samples to break their isolation from the external environment" ; + sbol:displayId "Unseal" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "samples" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "wavelength" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType xsd:integer ; + sbol:displayId "PrimitivePinSpecification3" ; + sbol:name "numFlashes" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedData ; + sbol:displayId "PrimitivePinSpecification4" ; + sbol:name "measurements" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable12" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable13" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable15" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable16" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable17" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable18" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable19" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable20" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable8" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable9" . + + a ns1:SubProtocol, + sbol:Identified ; + ns1:input ; + ns1:instanceOfSubProtocol ; + ns1:output , + , + , + ; + sbol:displayId "SubProtocol1" . + + a ns1:SubProtocol, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfSubProtocol ; + ns1:output , + , + , + ; + sbol:displayId "SubProtocol2" . + + a ns1:SubProtocol, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfSubProtocol ; + ns1:output , + , + , + ; + sbol:displayId "SubProtocol3" . + + a ns1:SubProtocol, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfSubProtocol ; + ns1:output , + , + , + ; + sbol:displayId "SubProtocol4" . + + a ns1:SubProtocol, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfSubProtocol ; + ns1:output , + , + , + ; + sbol:displayId "SubProtocol5" . + + a ns1:SubProtocol, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfSubProtocol ; + ns1:output , + , + , + ; + sbol:displayId "SubProtocol6" . + + a ns1:SubProtocol, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfSubProtocol ; + ns1:output , + , + , + ; + sbol:displayId "SubProtocol7" . + + a ns1:SubProtocol, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfSubProtocol ; + ns1:output , + , + , + ; + sbol:displayId "SubProtocol8" . + + a ns1:Container, + sbol:Identified ; + ns1:containerMaxCoordinate "H12" ; + ns1:containerType ; + sbol:displayId "Container1" ; + sbol:name "OD Plate" . + + a ns1:Initial, + sbol:Identified ; + sbol:displayId "Initial1" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable6" . + + a ns1:Container, + sbol:Identified ; + ns1:containerMaxCoordinate "H12" ; + ns1:containerType ; + sbol:displayId "Container1" ; + sbol:name "OD Plate" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable2" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable8" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable9" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "source" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:Location ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "destination" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification3" ; + sbol:name "amount" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification4" ; + sbol:name "dispenseVelocity" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification5" ; + sbol:name "samples" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType sbol:Component ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "resource" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:Location ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "destination" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification3" ; + sbol:name "amount" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification4" ; + sbol:name "dispenseVelocity" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification5" ; + sbol:name "samples" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "source" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "destination" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification3" ; + sbol:name "amount" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification4" ; + sbol:name "mixCycles" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification5" ; + sbol:name "dispenseVelocity" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification6" ; + sbol:name "samples" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + ; + sbol:description "Seal a collection of samples fixing the seal using a user-selected method, in order to guarantee isolation from the external environment" ; + sbol:displayId "Seal" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "location" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input ; + ns1:instanceOfPrimitive ; + sbol:displayId "PrimitiveExecutable10" . + + a ns1:PrimitiveExecutable, + sbol:Identified ; + ns1:input , + , + , + , + ; + ns1:instanceOfPrimitive ; + ns1:output ; + sbol:displayId "PrimitiveExecutable6" . + + a ns1:Value, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:description "Samples to measure" ; + sbol:displayId "Value1" ; + sbol:name "samples" . + + a ns1:Value, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:description "Source for PBS" ; + sbol:displayId "Value2" ; + sbol:name "pbs" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "location" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType xsd:anyURI ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "type" . + + a ns1:Initial, + sbol:Identified ; + sbol:displayId "Initial1" . + + a ns1:Value, + sbol:Identified ; + sbol:displayId "Value2" . + + a ns1:Value, + sbol:Identified ; + sbol:displayId "Value3" . + + a ns1:Value, + sbol:Identified ; + sbol:displayId "Value4" . + + a ns1:Value, + sbol:Identified ; + sbol:displayId "Value5" . + + a ns1:Initial, + sbol:Identified ; + sbol:displayId "Initial1" . + + a ns1:Value, + sbol:Identified ; + sbol:displayId "Value3" . + + a ns1:Value, + sbol:Identified ; + sbol:displayId "Value4" . + + a ns1:Value, + sbol:Identified ; + sbol:displayId "Value5" . + + a ns1:Value, + sbol:Identified ; + sbol:displayId "Value6" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + , + , + , + , + , + ; + ns1:output ; + sbol:description "Measure fluorescence intensity from a set of samples stimulated by a given wavelength, with an optional bandpass or lowpass filter" ; + sbol:displayId "MeasureFluorescence" . + + a ns1:Value, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:description "Plate of strains to grow" ; + sbol:displayId "Value1" ; + sbol:name "strain_plate" . + + a ns1:Fork, + sbol:Identified ; + sbol:displayId "Fork1" . + + a ns1:Fork, + sbol:Identified ; + sbol:displayId "Fork1" . + + a ns1:Value, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:description "Samples to measure" ; + sbol:displayId "Value1" ; + sbol:name "samples" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "samples" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "excitationWavelength" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification3" ; + sbol:name "emissionBandpassWavelength" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification4" ; + sbol:name "emissionBandpassWidth" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification5" ; + sbol:name "emissionLowpassCutoff" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType xsd:integer ; + sbol:displayId "PrimitivePinSpecification6" ; + sbol:name "numFlashes" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType xsd:double ; + sbol:displayId "PrimitivePinSpecification7" ; + sbol:name "gain" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedData ; + sbol:displayId "PrimitivePinSpecification8" ; + sbol:name "measurements" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:Final, + sbol:Identified ; + sbol:displayId "Final1" . + + a ns1:Join, + sbol:Identified ; + sbol:displayId "Join1" . + + a ns1:Protocol, + sbol:TopLevel ; + ns1:hasActivity , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + ns1:hasFlow , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + ; + ns1:hasInput , + ; + ns1:hasLocation ; + ns1:hasOutput , + , + , + ; + sbol:description """ +Subprotocol to split a portion of each sample in a plate into another plate, diluting +with PBS, then measure OD and fluorescence from that other plate +""" ; + sbol:displayId "SplitAndMeasure" ; + sbol:name "Split samples, dilute, and measure" . + + a ns1:Final, + sbol:Identified ; + sbol:displayId "Final1" . + + a ns1:Join, + sbol:Identified ; + sbol:displayId "Join1" . + + a ns1:Primitive, + sbol:TopLevel ; + ns1:input , + , + , + ; + sbol:description "Incubate a set of samples under specified conditions for a fixed period of time" ; + sbol:displayId "Incubate" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification1" ; + sbol:name "samples" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification2" ; + sbol:name "pbs" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification3" ; + sbol:name "absorbance" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification4" ; + sbol:name "fluorescence_0.1" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification5" ; + sbol:name "fluorescence_0.2" . + + a ns1:ProtocolPinSpecification, + sbol:Identified ; + ns1:pinActivity ; + sbol:displayId "ProtocolPinSpecification6" ; + sbol:name "fluorescence_0.16" . + + a sbol:TopLevel, + om:UnitDivision ; + sbol:displayId "rpm" ; + sbol:name "rpm" ; + om:hasDenominator om:minute-Time ; + om:hasNumerator om:revolution ; + om:label "revolutions per minute" ; + om:symbol "rpm" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType ns1:LocatedSamples ; + sbol:displayId "PrimitivePinSpecification1" ; + sbol:name "location" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification2" ; + sbol:name "duration" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification3" ; + sbol:name "temperature" . + + a ns1:PrimitivePinSpecification, + sbol:Identified ; + ns1:activityType om:Measure ; + sbol:displayId "PrimitivePinSpecification4" ; + sbol:name "shakingFrequency" . + + a ns1:Pin, + sbol:Identified ; + ns1:instanceOfPinSpecification ; + sbol:displayId "Pin1" ; + sbol:name "samples" . + diff --git a/test/testfiles/iGEM_LUDOX_OD_calibration_2018.md b/test/testfiles/iGEM_LUDOX_OD_calibration_2018.md new file mode 100644 index 0000000..0f3ea06 --- /dev/null +++ b/test/testfiles/iGEM_LUDOX_OD_calibration_2018.md @@ -0,0 +1,34 @@ +# iGEM 2018 LUDOX OD calibration protocol + +## Description: + +With this protocol you will use LUDOX CL-X (a 45% colloidal silica suspension) as a single point reference to +obtain a conversion factor to transform absorbance (OD600) data from your plate reader into a comparable +OD600 measurement as would be obtained in a spectrophotometer. This conversion is necessary because plate +reader measurements of absorbance are volume dependent; the depth of the fluid in the well defines the path +length of the light passing through the sample, which can vary slightly from well to well. In a standard +spectrophotometer, the path length is fixed and is defined by the width of the cuvette, which is constant. +Therefore this conversion calculation can transform OD600 measurements from a plate reader (i.e. absorbance +at 600 nm, the basic output of most instruments) into comparable OD600 measurements. The LUDOX solution +is only weakly scattering and so will give a low absorbance value. + + + +## Materials +* [Water, sterile-filtered, BioReagent, suitable for cell culture](https://identifiers.org/pubchem.substance:24901740) +* [LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O](https://identifiers.org/pubchem.substance:24866361) +* [Microplate](https://identifiers.org/ncit:C43377) + +## Steps +### Step 1 +Pipette 100.0 microliter of [LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O](https://identifiers.org/pubchem.substance:24866361) into [Microplate](https://identifiers.org/ncit:C43377) A1:D1 + +### Step 2 +Pipette 100.0 microliter of [Water, sterile-filtered, BioReagent, suitable for cell culture](https://identifiers.org/pubchem.substance:24901740) into [Microplate](https://identifiers.org/ncit:C43377) A2:D2 + +### Step 3 +Measure absorbance of [Microplate](https://identifiers.org/ncit:C43377) A2:D2 and [Microplate](https://identifiers.org/ncit:C43377) A1:D1 at 600.0 nanometre + +### Step 4 +Report values from [Microplate](https://identifiers.org/ncit:C43377) A2:D2 and [Microplate](https://identifiers.org/ncit:C43377) A1:D1 + diff --git a/test/testfiles/iGEM_LUDOX_OD_calibration_2018.xlsx b/test/testfiles/iGEM_LUDOX_OD_calibration_2018.xlsx new file mode 100644 index 0000000..b97db8f Binary files /dev/null and b/test/testfiles/iGEM_LUDOX_OD_calibration_2018.xlsx differ diff --git a/test/testfiles/igem_ludox_autoprotocol.json b/test/testfiles/igem_ludox_autoprotocol.json new file mode 100644 index 0000000..c804efa --- /dev/null +++ b/test/testfiles/igem_ludox_autoprotocol.json @@ -0,0 +1,81 @@ +{ + "instructions": [ + { + "op": "provision", + "resource_id": "rs1c7pg8qs22dt", + "measurement_mode": "volume", + "to": [ + { + "well": "samples/0", + "volume": "100:microliter" + }, + { + "well": "samples/12", + "volume": "100:microliter" + }, + { + "well": "samples/24", + "volume": "100:microliter" + }, + { + "well": "samples/36", + "volume": "100:microliter" + } + ] + }, + { + "op": "provision", + "resource_id": "rs1b6z2vgatkq7", + "measurement_mode": "volume", + "to": [ + { + "well": "samples/1", + "volume": "100:microliter" + }, + { + "well": "samples/13", + "volume": "100:microliter" + }, + { + "well": "samples/25", + "volume": "100:microliter" + }, + { + "well": "samples/37", + "volume": "100:microliter" + } + ] + }, + { + "op": "spectrophotometry", + "dataref": "measurements", + "object": "samples", + "groups": [ + { + "mode": "absorbance", + "mode_params": { + "wells": [ + "samples/0", + "samples/1", + "samples/12", + "samples/13", + "samples/24", + "samples/25", + "samples/36", + "samples/37" + ], + "wavelength": [ + "100:nanometer" + ] + } + } + ] + } + ], + "refs": { + "samples": { + "id": "ct1g9qsg4wx6gcj", + "discard": true + } + } +} \ No newline at end of file diff --git a/test/testfiles/igem_ludox_markdown.md b/test/testfiles/igem_ludox_markdown.md new file mode 100644 index 0000000..95a64b0 --- /dev/null +++ b/test/testfiles/igem_ludox_markdown.md @@ -0,0 +1,37 @@ +# iGEM 2018 LUDOX OD calibration protocol + +## Description: + +With this protocol you will use LUDOX CL-X (a 45% colloidal silica suspension) as a single point reference to +obtain a conversion factor to transform absorbance (OD600) data from your plate reader into a comparable +OD600 measurement as would be obtained in a spectrophotometer. This conversion is necessary because plate +reader measurements of absorbance are volume dependent; the depth of the fluid in the well defines the path +length of the light passing through the sample, which can vary slightly from well to well. In a standard +spectrophotometer, the path length is fixed and is defined by the width of the cuvette, which is constant. +Therefore this conversion calculation can transform OD600 measurements from a plate reader (i.e. absorbance +at 600 nm, the basic output of most instruments) into comparable OD600 measurements. The LUDOX solution +is only weakly scattering and so will give a low absorbance value. + + + +## Protocol Materials: +* [LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O](https://identifiers.org/pubchem.substance:24866361) +* [Water, sterile-filtered, BioReagent, suitable for cell culture](https://identifiers.org/pubchem.substance:24901740) + + +## Protocol Inputs: +* `wavelength` = 100.0 nanometer + +## Protocol Outputs: +* `absorbance` + +## Steps +1. Provision a container named `samples` meeting specification: cont:ClearPlate and + cont:SLAS-4-2004 and + (cont:wellVolume some + ((om:hasUnit value om:microlitre) and + (om:hasNumericalValue only xsd:decimal[>= "200"^^xsd:decimal]))). +2. Pipette 100.0 microliter of [Water, sterile-filtered, BioReagent, suitable for cell culture](https://identifiers.org/pubchem.substance:24901740) into `samples(A1:D1)`. +3. Pipette 100.0 microliter of [LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O](https://identifiers.org/pubchem.substance:24866361) into `samples(A2:D2)`. +4. Make absorbance measurements (named `measurements`) of `samples(A1:D2)` at 100.0 nanometer. +5. Report values for `absorbance` from `measurements`. diff --git a/test/testfiles/igem_ludox_test.nt b/test/testfiles/igem_ludox_test.nt new file mode 100644 index 0000000..9370d29 --- /dev/null +++ b/test/testfiles/igem_ludox_test.nt @@ -0,0 +1,776 @@ + + "LUDOX" . + . + "LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O" . + . + . + "ddH2O" . + . + "Water, sterile-filtered, BioReagent, suitable for cell culture" . + . + . + . + "ActivityParameterNode1" . + . + . + . + "ActivityParameterNode2" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "{\"cont\": \"https://sift.net/container-ontology/container-ontology#\", \"om\": \"http://www.ontology-of-units-of-measure.org/resource/om-2/\"}" . + "cont:ClearPlate and \n cont:SLAS-4-2004 and\n (cont:wellVolume some \n ((om:hasUnit value om:microlitre) and\n (om:hasNumericalValue only xsd:decimal[>= \"200\"^^xsd:decimal])))" . + "ContainerSpec1" . + "plateRequirement" . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "specification" . + . + . + . + . + . + "CallBehaviorAction1" . + "calibration plate" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "source" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "A1:D1" . + "LiteralString1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "coordinates" . + . + . + . + . + . + . + "CallBehaviorAction2" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "destination" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "dispenseVelocity" . + . + . + . + "LiteralReference1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "resource" . + . + . + "Measure1" . + "100.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "amount" . + . + . + . + . + . + . + . + "CallBehaviorAction3" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "source" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "A2:D2" . + "LiteralString1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "coordinates" . + . + . + . + . + . + . + "CallBehaviorAction4" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "destination" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "dispenseVelocity" . + . + . + . + "LiteralReference1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "resource" . + . + . + "Measure1" . + "100.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "amount" . + . + . + . + . + . + . + . + "CallBehaviorAction5" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "source" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "A1:D2" . + "LiteralString1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "coordinates" . + . + . + . + . + . + . + "CallBehaviorAction6" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "samples" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "wavelength" . + . + . + "true"^^ . + "true"^^ . + "InputPin3" . + "numFlashes" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "measurements" . + . + . + . + . + . + . + . + "CallBehaviorAction7" . + . + . + . + . + "ControlFlow1" . + . + . + . + . + "ControlFlow2" . + . + . + . + . + "ControlFlow3" . + . + . + . + . + "ControlFlow4" . + . + . + . + . + "ControlFlow5" . + . + . + . + . + "ControlFlow6" . + . + . + . + . + "ControlFlow7" . + . + . + . + . + "ControlFlow8" . + . + . + "ForkNode1" . + . + . + "InitialNode1" . + . + . + . + . + "ObjectFlow1" . + . + . + . + . + "ObjectFlow2" . + . + . + . + . + "ObjectFlow3" . + . + . + . + . + "ObjectFlow4" . + . + . + . + . + "ObjectFlow5" . + . + . + . + . + "ObjectFlow6" . + . + . + . + . + "ObjectFlow7" . + . + . + . + . + "ObjectFlow8" . + . + . + . + . + "ObjectFlow9" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + "Measure1" . + "600.0"^^ . + . + . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "wavelength" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "absorbance" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "\nWith this protocol you will use LUDOX CL-X (a 45% colloidal silica suspension) as a single point reference to\nobtain a conversion factor to transform absorbance (OD600) data from your plate reader into a comparable\nOD600 measurement as would be obtained in a spectrophotometer. This conversion is necessary because plate\nreader measurements of absorbance are volume dependent; the depth of the fluid in the well defines the path\nlength of the light passing through the sample, which can vary slightly from well to well. In a standard\nspectrophotometer, the path length is fixed and is defined by the width of the cuvette, which is constant.\nTherefore this conversion calculation can transform OD600 measurements from a plate reader (i.e. absorbance\nat 600 nm, the basic output of most instruments) into comparable OD600 measurements. The LUDOX solution\nis only weakly scattering and so will give a low absorbance value.\n " . + "iGEM_LUDOX_OD_calibration_2018" . + . + "iGEM 2018 LUDOX OD calibration protocol" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "resource" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "destination" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "amount" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "dispenseVelocity" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + . + . + . + . + "Place a measured amount (mass or volume) of a specified component into a location, where it may then be used in executing the protocol." . + "Provision" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "specification" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + . + . + "Allocate a sample array with size and type based on an empty container" . + "EmptyContainer" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "source" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "coordinates" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + . + . + . + "Select only the samples with specified row/column combination from a sample collection" . + "PlateCoordinates" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "wavelength" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "numFlashes" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "measurements" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + . + . + . + . + "Measure absorbance at a given wavelength from a set of samples" . + "MeasureAbsorbance" . + . + . + . diff --git a/test/testfiles/igem_ludox_test_exec.nt b/test/testfiles/igem_ludox_test_exec.nt new file mode 100644 index 0000000..1a9d6a5 --- /dev/null +++ b/test/testfiles/igem_ludox_test_exec.nt @@ -0,0 +1,1459 @@ + + "LUDOX" . + . + "LUDOX(R) CL-X colloidal silica, 45 wt. % suspension in H2O" . + . + . + "ddH2O" . + . + "Water, sterile-filtered, BioReagent, suitable for cell culture" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue1" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue2" . + . + . + "true"^^ . + . + . + "execute_0" . + . + . + . + "2021-10-19T22:55:54.933996"^^ . + "2021-10-19T22:55:54.933987"^^ . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue1" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue2" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue3" . + . + . + "true"^^ . + . + . + . + "execute_1" . + . + . + . + "2021-10-19T22:55:55.027540"^^ . + "2021-10-19T22:55:55.027527"^^ . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue1" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue2" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue3" . + . + . + "true"^^ . + . + . + . + "execute_2" . + . + . + . + "2021-10-19T22:55:55.095751"^^ . + "2021-10-19T22:55:55.095737"^^ . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue1" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue2" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue3" . + . + . + "true"^^ . + . + . + . + "execute_3" . + . + . + . + "2021-10-19T22:55:55.124890"^^ . + "2021-10-19T22:55:55.124877"^^ . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue1" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue2" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue3" . + . + . + "true"^^ . + . + . + . + "execute_4" . + . + . + . + "2021-10-19T22:55:55.181820"^^ . + "2021-10-19T22:55:55.181808"^^ . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue1" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue2" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue3" . + . + . + "true"^^ . + . + . + . + "execute_5" . + . + . + . + "2021-10-19T22:55:55.205874"^^ . + "2021-10-19T22:55:55.205866"^^ . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue1" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue2" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ParameterValue3" . + . + . + "true"^^ . + . + . + . + "execute_6" . + . + . + . + "2021-10-19T22:55:55.251151"^^ . + "2021-10-19T22:55:55.251137"^^ . + . + "ActivityParameterNode1" . + . + . + . + "ActivityParameterNode2" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "{\"cont\": \"https://sift.net/container-ontology/container-ontology#\", \"om\": \"http://www.ontology-of-units-of-measure.org/resource/om-2/\"}" . + "cont:ClearPlate and \n cont:SLAS-4-2004 and\n (cont:wellVolume some \n ((om:hasUnit value om:microlitre) and\n (om:hasNumericalValue only xsd:decimal[>= \"200\"^^xsd:decimal])))" . + "ContainerSpec1" . + "plateRequirement" . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "specification" . + . + . + . + . + . + "CallBehaviorAction1" . + "calibration plate" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "source" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "A1:D1" . + "LiteralString1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "coordinates" . + . + . + . + . + . + . + "CallBehaviorAction2" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "destination" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "dispenseVelocity" . + . + . + . + "LiteralReference1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "resource" . + . + . + "Measure1" . + "100.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "amount" . + . + . + . + . + . + . + . + "CallBehaviorAction3" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "source" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "A2:D2" . + "LiteralString1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "coordinates" . + . + . + . + . + . + . + "CallBehaviorAction4" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "destination" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "dispenseVelocity" . + . + . + . + "LiteralReference1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "resource" . + . + . + "Measure1" . + "100.0"^^ . + . + . + . + . + "LiteralIdentified1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin2" . + "amount" . + . + . + . + . + . + . + . + "CallBehaviorAction5" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "source" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "samples" . + . + . + "A1:D2" . + "LiteralString1" . + . + . + "true"^^ . + "true"^^ . + . + "ValuePin1" . + "coordinates" . + . + . + . + . + . + . + "CallBehaviorAction6" . + . + . + "true"^^ . + "true"^^ . + "InputPin1" . + "samples" . + . + . + "true"^^ . + "true"^^ . + "InputPin2" . + "wavelength" . + . + . + "true"^^ . + "true"^^ . + "InputPin3" . + "numFlashes" . + . + . + "true"^^ . + "true"^^ . + "OutputPin1" . + "measurements" . + . + . + . + . + . + . + . + "CallBehaviorAction7" . + . + . + . + . + "ControlFlow1" . + . + . + . + . + "ControlFlow2" . + . + . + . + . + "ControlFlow3" . + . + . + . + . + "ControlFlow4" . + . + . + . + . + "ControlFlow5" . + . + . + . + . + "ControlFlow6" . + . + . + . + . + "ControlFlow7" . + . + . + . + . + "ControlFlow8" . + . + . + "ForkNode1" . + . + . + "InitialNode1" . + . + . + . + . + "ObjectFlow1" . + . + . + . + . + "ObjectFlow2" . + . + . + . + . + "ObjectFlow3" . + . + . + . + . + "ObjectFlow4" . + . + . + . + . + "ObjectFlow5" . + . + . + . + . + "ObjectFlow6" . + . + . + . + . + "ObjectFlow7" . + . + . + . + . + "ObjectFlow8" . + . + . + . + . + "ObjectFlow9" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + "Measure1" . + "600.0"^^ . + . + . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "wavelength" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "absorbance" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "\nWith this protocol you will use LUDOX CL-X (a 45% colloidal silica suspension) as a single point reference to\nobtain a conversion factor to transform absorbance (OD600) data from your plate reader into a comparable\nOD600 measurement as would be obtained in a spectrophotometer. This conversion is necessary because plate\nreader measurements of absorbance are volume dependent; the depth of the fluid in the well defines the path\nlength of the light passing through the sample, which can vary slightly from well to well. In a standard\nspectrophotometer, the path length is fixed and is defined by the width of the cuvette, which is constant.\nTherefore this conversion calculation can transform OD600 measurements from a plate reader (i.e. absorbance\nat 600 nm, the basic output of most instruments) into comparable OD600 measurements. The LUDOX solution\nis only weakly scattering and so will give a low absorbance value.\n " . + "iGEM_LUDOX_OD_calibration_2018" . + . + "iGEM 2018 LUDOX OD calibration protocol" . + . + . + "uml.ControlFlow" . + "LiteralString1" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ActivityEdgeFlow10" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ActivityEdgeFlow11" . + . + . + "A1:D1" . + . + "SampleMask1" . + . + . + . + "LiteralIdentified1" . + . + . + . + . + . + "ActivityEdgeFlow12" . + . + . + "uml.ControlFlow" . + "LiteralString1" . + . + . + . + . + . + "ActivityEdgeFlow13" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ActivityEdgeFlow14" . + . + . + "uml.ControlFlow" . + "LiteralString1" . + . + . + . + . + . + "ActivityEdgeFlow15" . + . + . + "A2:D2" . + . + "SampleMask1" . + . + . + . + "LiteralIdentified1" . + . + . + . + . + . + "ActivityEdgeFlow16" . + . + . + "uml.ControlFlow" . + "LiteralString1" . + . + . + . + . + . + "ActivityEdgeFlow17" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ActivityEdgeFlow18" . + . + . + "uml.ControlFlow" . + "LiteralString1" . + . + . + . + . + . + "ActivityEdgeFlow19" . + . + . + . + . + . + "ActivityEdgeFlow1" . + . + . + . + "LiteralReference1" . + . + . + "A1:D2" . + . + "SampleMask1" . + . + . + . + "LiteralIdentified1" . + . + . + . + . + . + "ActivityEdgeFlow20" . + . + . + "uml.ControlFlow" . + "LiteralString1" . + . + . + . + . + . + "ActivityEdgeFlow21" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ActivityEdgeFlow22" . + . + . + "uml.ControlFlow" . + "LiteralString1" . + . + . + . + . + . + "ActivityEdgeFlow23" . + . + . + "measurements" . + "LiteralString1" . + . + . + . + . + . + "ActivityEdgeFlow24" . + . + . + . + . + . + "ActivityEdgeFlow2" . + . + . + . + "" . + "SampleArray1" . + "samples" . + . + . + . + "LiteralIdentified1" . + . + . + . + . + . + "ActivityEdgeFlow3" . + . + . + "uml.ControlFlow" . + "LiteralString1" . + . + . + . + . + . + "ActivityEdgeFlow4" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ActivityEdgeFlow5" . + . + . + . + "LiteralReference1" . + . + . + . + . + . + "ActivityEdgeFlow6" . + . + . + . + "LiteralReference1" . + . + . + . + . + . + "ActivityEdgeFlow7" . + . + . + . + "LiteralReference1" . + . + . + . + . + . + "ActivityEdgeFlow8" . + . + . + . + "LiteralReference1" . + . + . + . + . + "ActivityEdgeFlow9" . + . + . + . + . + "ActivityNodeExecution10" . + . + . + . + . + . + "ActivityNodeExecution11" . + . + . + . + "ActivityNodeExecution1" . + . + . + . + "ActivityNodeExecution2" . + . + . + . + . + "ActivityNodeExecution3" . + . + . + . + . + "ActivityNodeExecution4" . + . + . + . + . + "ActivityNodeExecution5" . + . + . + . + . + "ActivityNodeExecution6" . + . + . + . + . + "ActivityNodeExecution7" . + . + . + . + . + "ActivityNodeExecution8" . + . + . + . + . + "ActivityNodeExecution9" . + . + . + "Association1" . + . + . + . + . + . + . + . + "CallBehaviorExecution1" . + . + . + . + . + . + . + "CallBehaviorExecution2" . + . + . + . + . + . + . + "CallBehaviorExecution3" . + . + . + . + . + . + . + "CallBehaviorExecution4" . + . + . + . + . + . + . + "CallBehaviorExecution5" . + . + . + . + . + . + . + "CallBehaviorExecution6" . + . + . + . + . + . + . + . + "CallBehaviorExecution7" . + . + . + "Measure1" . + "100.0"^^ . + . + . + . + . + . + "ParameterValue1" . + . + . + "measurements" . + "LiteralString1" . + . + . + . + . + "ParameterValue2" . + . + . + "true"^^ . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + . + "test_execution" . + . + . + . + "2021-10-19T22:55:55.269758"^^ . + . + "2021-10-19T22:55:54.920973"^^ . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "resource" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "destination" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "amount" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "dispenseVelocity" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + . + . + . + . + "Place a measured amount (mass or volume) of a specified component into a location, where it may then be used in executing the protocol." . + "Provision" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "specification" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + . + . + "Allocate a sample array with size and type based on an empty container" . + "EmptyContainer" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "source" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "coordinates" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + . + . + . + "Select only the samples with specified row/column combination from a sample collection" . + "PlateCoordinates" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "wavelength" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "numFlashes" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "measurements" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + . + . + . + . + "Measure absorbance at a given wavelength from a set of samples" . + "MeasureAbsorbance" . + . + . + . diff --git a/test/testfiles/mini_library.nt b/test/testfiles/mini_library.nt new file mode 100644 index 0000000..18fe340 --- /dev/null +++ b/test/testfiles/mini_library.nt @@ -0,0 +1,251 @@ + + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "resource" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "destination" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "amount" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "dispenseVelocity" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "4"^^ . + . + "OrderedPropertyValue5" . + . + . + . + . + . + . + . + "Place a measured amount (mass or volume) of a specified component into a location." . + "Provision" . + . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "source" . + . + . + "0"^^ . + . + "OrderedPropertyValue1" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "destination" . + . + . + "1"^^ . + . + "OrderedPropertyValue2" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "amount" . + . + . + "2"^^ . + . + "OrderedPropertyValue3" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "0"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "dispenseVelocity" . + . + . + "3"^^ . + . + "OrderedPropertyValue4" . + . + . + "1"^^ . + "LiteralInteger1" . + . + . + "1"^^ . + "LiteralInteger2" . + . + . + . + "true"^^ . + "true"^^ . + . + . + . + "Parameter1" . + "samples" . + . + . + "4"^^ . + . + "OrderedPropertyValue5" . + . + . + . + . + . + . + . + "Move a measured volume taken from a collection of source samples to a location" . + "Transfer" . + . + . + . diff --git a/uml/__init__.py b/uml/__init__.py new file mode 100644 index 0000000..1c413f8 --- /dev/null +++ b/uml/__init__.py @@ -0,0 +1,564 @@ +import os +import posixpath +from collections import Counter +from typing import List, Set, Iterable +from sbol_factory import SBOLFactory, UMLFactory +import sbol3 + +# Load ontology and create uml submodule +SBOLFactory('uml_submodule', + posixpath.join(os.path.dirname(os.path.realpath(__file__)), + 'uml.ttl'), + 'http://bioprotocols.org/uml#') + +# Import submodule symbols into top-level uml module +from uml_submodule import * +from .uml_graphviz import * + +# Workaround for pySBOL3 issue #231: should be applied to every iteration on a collection of SBOL objects +# TODO: delete after resolution of pySBOL3 issue #231 +def id_sort(i: iter): + sortable = list(i) + sortable.sort(key=lambda x: x.identity if isinstance(x, sbol3.Identified) else x) + return sortable + + +########################################### +# Define extension methods for ValueSpecification + +# TODO: move constants into ontology after resolution of https://github.com/SynBioDex/sbol_factory/issues/14 +PARAMETER_IN = 'http://bioprotocols.org/uml#in' +PARAMETER_OUT = 'http://bioprotocols.org/uml#out' + + +def literal(value, reference: bool = False) -> LiteralSpecification: + """Construct a UML LiteralSpecification based on the value of the literal passed + + Parameters + ---------- + value: the value to embed as a literal + reference: if true, use a reference for a non-TopLevel SBOL rather than embedding as a child object + + Returns + ------- + LiteralSpecification of the appropriate type for the value + """ + if isinstance(value, LiteralSpecification): + return literal(value.value, reference) # if it's a literal, unwrap and rebuild + elif value is None: + return LiteralNull() + elif isinstance(value, str): + return LiteralString(value=value) + elif isinstance(value, int): + return LiteralInteger(value=value) + elif isinstance(value, bool): + return LiteralBoolean(value=value) + elif isinstance(value, float): + return LiteralReal(value=value) + elif isinstance(value, sbol3.TopLevel) or (reference and isinstance(value, sbol3.Identified)): + return LiteralReference(value=value) + elif isinstance(value, sbol3.Identified): + return LiteralIdentified(value=value) + else: + raise ValueError(f'Don\'t know how to make literal from {type(value)} "{value}"') + + +########################################### +# Define extension methods for Behavior + +def behavior_add_parameter(self, name: str, param_type: str, direction: str, optional: bool = False, + default_value: ValueSpecification = None) -> OrderedPropertyValue: + """Add a Parameter for this Behavior; usually not called directly + + Note: Current assumption is that cardinality is either [0..1] or 1 + :param name: name of the parameter, which will also be used for pins + :param param_type: URI specifying the type of object that is expected for this parameter + :param direction: should be in or out + :param optional: True if the Parameter is optional; default is False + :param default_value: must be specified if Parameter is optional + :return: Parameter that has been added + """ + param = Parameter(name=name, type=param_type, direction=direction, is_ordered=True, is_unique=True) + ordered_param = OrderedPropertyValue(index=len(self.parameters), property_value=param) + print(param, ordered_param) + print(ordered_param.__class__.__mro__) + self.parameters.append(ordered_param) + param.upper_value = literal(1) # all parameters are assumed to have cardinality [0..1] or 1 for now + if optional: + param.lower_value = literal(0) + else: + param.lower_value = literal(1) + if default_value: + param.default_value = default_value + return ordered_param +Behavior.add_parameter = behavior_add_parameter # Add to class via monkey patch + + +def behavior_add_input(self, name: str, param_type: str, optional: bool = False, + default_value: ValueSpecification = None) -> OrderedPropertyValue: + """Add an input Parameter for this Behavior + + Note: Current assumption is that cardinality is either [0..1] or 1 + + :param name: name of the parameter, which will also be used for pins + :param param_type: URI specifying the type of object that is expected for this parameter + :param optional: True if the Parameter is optional; default is False + :param default_value: default value for this parameter + :return: Parameter that has been added + """ + return self.add_parameter(name, param_type, PARAMETER_IN, optional, default_value) +Behavior.add_input = behavior_add_input # Add to class via monkey patch + + +def behavior_add_output(self, name, param_type) -> OrderedPropertyValue: + """Add an output Parameter for this Behavior + + :param name: name of the parameter, which will also be used for pins + :param param_type: URI specifying the type of object that is expected for this parameter + :return: Parameter that has been added + """ + return self.add_parameter(name, param_type, PARAMETER_OUT) +Behavior.add_output = behavior_add_output # Add to class via monkey patch + + +def behavior_get_inputs(self) -> Iterable[Parameter]: + """Return all Parameters of type input for this Behavior + + Note: assumes that type is all either in or out + Returns + ------- + Iterator over Parameters + """ + return (p for p in self.parameters if p.property_value.direction == PARAMETER_IN) +Behavior.get_inputs = behavior_get_inputs # Add to class via monkey patch + + +def behavior_get_input(self, name) -> Parameter: + """Return a specific input Parameter for this Behavior + + Note: assumes that type is all either in or out + Returns + ------- + Parameter, or Value error + """ + print(p for p in self.get_inputs()) + found = [p for p in self.get_inputs() if p.property_value.name == name] + if len(found) == 0: + raise ValueError(f'Behavior {self.identity} has no input parameter named {name}') + elif len(found) > 1: + raise ValueError(f'Behavior {self.identity} has multiple input parameters named {name}') + else: + return found[0] +Behavior.get_input = behavior_get_input # Add to class via monkey patch + + +def behavior_get_required_inputs(self): + """Return all required Parameters of type input for this Behavior + + Note: assumes that type is all either in or out + Returns + ------- + Iterator over Parameters + """ + return (p for p in self.get_inputs() if p.property_value.lower_value.value > 0) +Behavior.get_required_inputs = behavior_get_required_inputs # Add to class via monkey patch + + +def behavior_get_outputs(self): + """Return all Parameters of type output for this Behavior + + Note: assumes that type is all either in or out + Returns + ------- + Iterator over Parameters + """ + return (p for p in self.parameters if p.property_value.direction == PARAMETER_OUT) +Behavior.get_outputs = behavior_get_outputs # Add to class via monkey patch + + +def behavior_get_output(self, name) -> Parameter: + """Return a specific input Parameter for this Behavior + + Note: assumes that type is all either in or out + Returns + ------- + Parameter, or Value error + """ + found = [p for p in self.get_outputs() if p.name == name] + if len(found) == 0: + raise ValueError(f'Behavior {self.identity} has no output parameter named {name}') + elif len(found) > 1: + raise ValueError(f'Behavior {self.identity} has multiple output parameters named {name}') + else: + return found[0] +Behavior.get_output = behavior_get_output # Add to class via monkey patch + + +def behavior_get_required_outputs(self): + """Return all required Parameters of type output for this Behavior + + Note: assumes that type is all either in or out + Returns + ------- + Iterator over Parameters + """ + return (p for p in self.get_outputs() if p.property_value.lower_value.value > 0) +Behavior.get_required_outputs = behavior_get_required_outputs # Add to class via monkey patch + + +########################################### +# Define extension methods for ActivityNode + +def activitynode_unpin(self: ActivityNode) -> ActivityNode: + """Find the root node for an ActivityNode: either itself if a Pin, otherwise the owning Action + + Parameters + ---------- + self: ActivityNode + + Returns + ------- + self if not a Pin, otherwise the owning Action + """ + if isinstance(self,Pin): + action = self.get_parent() + if not isinstance(action,Action): + raise ValueError(f'Parent of {self.identity} should be Action, but found {type(action)} instead') + return action + else: + return self +ActivityNode.unpin = activitynode_unpin # Add to class via monkey patch + +########################################### +# Define extension methods for CallBehaviorAction + +def call_behavior_action_input_pin(self, pin_name: str): + """Find an input pin on the action with the specified name + + :param pin_name: + :return: Pin with specified name + """ + pin_set = {x for x in self.inputs if x.name == pin_name} + if len(pin_set) == 0: + raise ValueError(f'Could not find input pin named {pin_name}') + if len(pin_set) > 1: + raise ValueError(f'Found more than one input pin named {pin_name}') + return pin_set.pop() +CallBehaviorAction.input_pin = call_behavior_action_input_pin # Add to class via monkey patch + + +def call_behavior_action_output_pin(self, pin_name: str): + """Find an output pin on the action with the specified name + + :param pin_name: + :return: Pin with specified name + """ + pin_set = {x for x in self.outputs if x.name == pin_name} + if len(pin_set) == 0: + raise ValueError(f'Could not find output pin named {pin_name}') + if len(pin_set) > 1: + raise ValueError(f'Found more than one output pin named {pin_name}') + return pin_set.pop() +CallBehaviorAction.output_pin = call_behavior_action_output_pin # Add to class via monkey patch + +def call_behavior_action_pin_parameter(self, pin_name: str): + """Find the behavior parameter corresponding to the pin + + :param pin_name: + :return: Parameter with specified name + """ + try: + pin = self.input_pin(pin_name) + except: + try: + pin = self.output_pin(pin_name) + except: + raise ValueError(f'Could not find pin named {pin_name}') + behavior = self.behavior.lookup() + [parameter] = [p for p in behavior.parameters if p.property_value.name == pin_name] + return parameter +CallBehaviorAction.pin_parameter = call_behavior_action_pin_parameter # Add to class via monkey patch + +def add_call_behavior_action(parent: Activity, behavior: Behavior, **input_pin_literals): + """Create a call to a Behavior and add it to an Activity + + :param parent: Activity to which the behavior is being added + :param behavior: Behavior to be called + :param input_pin_literals: map of literal values to be assigned to specific pins + :return: newly constructed + """ + # first, make sure that all of the keyword arguments are in the inputs of the behavior + unmatched_keys = [key for key in input_pin_literals.keys() if key not in (i.property_value.name for i in behavior.get_inputs())] + if unmatched_keys: + raise ValueError(f'Specification for "{behavior.display_id}" does not have inputs: {unmatched_keys}') + + # create action + action = CallBehaviorAction(behavior=behavior) + parent.nodes.append(action) + + # Instantiate input pins + for i in id_sort(behavior.get_inputs()): + if i.property_value.name in input_pin_literals: + value = input_pin_literals[i.property_value.name] + # TODO: type check relationship between value and parameter type specification + action.inputs.append(ValuePin(name=i.property_value.name, is_ordered=i.property_value.is_ordered, + is_unique=i.property_value.is_unique, value=literal(value))) + else: # if not a constant, then just a generic InputPin + action.inputs.append(InputPin(name=i.property_value.name, is_ordered=i.property_value.is_ordered, + is_unique=i.property_value.is_unique)) + + # Instantiate output pins + for o in id_sort(behavior.get_outputs()): + action.outputs.append(OutputPin(name=o.property_value.name, is_ordered=o.property_value.is_ordered, + is_unique=o.property_value.is_unique)) + + return action + + +########################################### +# Define extension methods for Activity + +def activity_initial(self): + """Find or create an initial node in an Activity. + + Note that while UML allows multiple initial nodes, use of this routine assumes a single one is sufficient. + :return: InitialNode for Activity + """ + + initial = [a for a in self.nodes if isinstance(a, InitialNode)] + if not initial: + self.nodes.append(InitialNode()) + return self.initial() + elif len(initial) == 1: + return initial[0] + else: + raise ValueError(f'Activity "{self.display_id}" assumed to have one initial node, but found {len(initial)}') +Activity.initial = activity_initial # Add to class via monkey patch + + +def activity_final(self): + """Find or create a final node in a Activity + + Note that while UML allows multiple final nodes, use of this routine assumes a single is sufficient. + :return: FinalNode for Activity + """ + final = [a for a in self.nodes if isinstance(a, FinalNode)] + if not final: + self.nodes.append(FlowFinalNode()) + return self.final() + elif len(final) == 1: + return final[0] + else: + raise ValueError(f'Activity "{self.display_id}" assumed to have one initial node, but found {len(initial)}') +Activity.final = activity_final # Add to class via monkey patch + + +def activity_input_value(self, name: str, param_type: str, optional: bool = False, + default_value: ValueSpecification = None) -> ActivityParameterNode: + """Add an input, then return the ActivityParameterNode that refers to that input + + :param self: Activity + :param name: Name of the input + :param param_type: type of value expected for the input + :param optional: True if the Parameter is optional; default is False + :param default_value: if the input is optional, a default value must be set + :return: ActivityParameterNode associated with the input + """ + parameter = self.add_input(name=name, param_type=param_type, optional=optional, default_value=default_value) + node = ActivityParameterNode(parameter=parameter) + self.nodes.append(node) + return node +Activity.input_value = activity_input_value # Add to class via monkey patch + + +def activity_designate_output(self, name: str, param_type: str, source: ActivityNode) -> ActivityParameterNode: + """Add an output, connect it to an ActivityParameterNode, and get its value from the designated node + + :param self: Activity + :param name: Name of the output + :param param_type: type of value expected for the output + :param source: ActivityNode whose ObjectValue output should be routed to the source + :return: ActivityParameterNode associated with the output + """ + parameter = self.add_output(name=name, param_type=param_type) + node = ActivityParameterNode(parameter=parameter) + self.nodes.append(node) + self.use_value(source, node) + return node +Activity.designate_output = activity_designate_output # Add to class via monkey patch + + +def activity_initiating_nodes(self) -> List[ActivityNode]: + """Find all InitialNode and ActivityParameterNode activities. + These should be the only activities with no in-flow, which can thus initiate execution. + + Parameters + ---------- + self: Activity + + Returns + ------- + List of ActivityNodes able to initiate execution + """ + return [n for n in self.nodes if isinstance(n, InitialNode) or + (isinstance(n, ActivityParameterNode) and n.parameter and n.parameter.lookup().property_value.direction == PARAMETER_IN)] +Activity.initiating_nodes = activity_initiating_nodes # Add to class via monkey patch + + +def activity_incoming_edges(self, node: ActivityNode) -> Set[ActivityEdge]: + """Find the edges that have the designated node as a target + + Parameters + ---------- + node: target for edges + + Returns + ------- + Set of ActivityEdges with node as a target + """ + return {e for e in self.edges if e.target == node.identity} # TODO: change to pointer lookup after pySBOL #237 +Activity.incoming_edges = activity_incoming_edges # Add to class via monkey patch + + +def activity_outgoing_edges(self, node: ActivityNode) -> Set[ActivityEdge]: + """Find the edges that have the designated node as a source + + Parameters + ---------- + node: target for edges + + Returns + ------- + Set of ActivityEdges with node as a source + """ + return {e for e in self.edges if e.source == node.identity} # TODO: change to pointer lookup after pySBOL #237 +Activity.outgoing_edges = activity_outgoing_edges # Add to class via monkey patch + + +def activity_deconflict_objectflow_sources(self, source: ActivityNode) -> ActivityNode: + '''Avoid nondeterminism in ObjectFlows by injecting ForkNode objects where necessary + + Parameters + ---------- + self: Activity + source: node to take a value from, directly or indirectly + + Returns + ------- + A source to attach to; either the original or an intervening ForkNode + ''' + # Use original if it's one of the node types that supports multiple dispatch + if isinstance(source, ForkNode) or isinstance(source, DecisionNode): + return source + # Otherwise, find out what targets currently attach: + current_outflows = [e for e in self.edges if e.source.lookup() is source] + # Use original if nothing is attached to it + if len(current_outflows) == 0: + #print(f'No prior use of {source.identity}, connecting directly') + return source + # If the flow goes to a single ForkNode, connect to that ForkNode + elif len(current_outflows) == 1 and isinstance(current_outflows[0].target.lookup(),ForkNode): + #print(f'Found an existing fork from {source.identity}, reusing') + return current_outflows[0].target.lookup() + # Otherwise, inject a ForkNode and connect all current flows to that instead + else: + #print(f'Found no existing fork from {source.identity}, injecting one') + fork = ForkNode() + self.nodes.append(fork) + self.edges.append(ObjectFlow(source=source, target=fork)) + for f in current_outflows: + f.source = fork # change over the existing flows + return fork +Activity.deconflict_objectflow_sources = activity_deconflict_objectflow_sources + + +def activity_call_behavior(self, behavior: Behavior, **input_pin_map): + """Call a Behavior as an Action in an Activity + + :param behavior: Activity to be invoked (object or name) + :param input_pin_map: literal value or ActivityNode mapped to names of Behavior parameters + :return: CallBehaviorAction that invokes the Behavior + """ + + # Any ActivityNode in the pin map will be withheld for connecting via object flows instead + activity_inputs = {k: v for k, v in input_pin_map.items() if isinstance(v, ActivityNode)} + non_activity_inputs = {k: v for k, v in input_pin_map.items() if k not in activity_inputs} + cba = add_call_behavior_action(self, behavior, **non_activity_inputs) + # add flows for activities being connected implicitly + for name, source in id_sort(activity_inputs.items()): + self.use_value(source, cba.input_pin(name)) + return cba +Activity.call_behavior = activity_call_behavior # Add to class via monkey patch + + +def activity_order(self, source: ActivityNode, target: ActivityNode): + """Add a ControlFlow between the designated source and target nodes in an Activity + + :param source: ActivityNode that is the source of the control flow + :param target: ActivityNode that is the target of the control flow + :return: ControlFlow created between source and target + """ + if source not in self.nodes: + raise ValueError(f'Source node {source.identity} is not a member of activity {self.identity}') + if target not in self.nodes: + raise ValueError(f'Target node {target.identity} is not a member of activity {self.identity}') + flow = ControlFlow(source=source, target=target) + self.edges.append(flow) + return flow +Activity.order = activity_order # Add to class via monkey patch + + +def activity_use_value(self, source: ActivityNode, target: ActivityNode) -> ObjectFlow: + """Add an ObjectFlow transferring a value between the designated source and target nodes in an Activity + + Typically, these activities will be either Action Pins or ActivityParameterNodes serving as input or output + :param source: ActivityNode that is the source of the value + :param target: ActivityNode that receives the value + :return: ObjectFlow created between source and target + """ + if source.get_toplevel() is not self: # check via toplevel, because pins are not directly in the node list + raise ValueError(f'Source node {source.identity} is not a member of activity {self.identity}') + if target.get_toplevel() is not self: + raise ValueError(f'Target node {target.identity} is not a member of activity {self.identity}') + source = self.deconflict_objectflow_sources(source) + flow = ObjectFlow(source=source, target=target) + self.edges.append(flow) + return flow +Activity.use_value = activity_use_value # Add to class via monkey patch + + +def activity_validate(self, report: sbol3.ValidationReport = None) -> sbol3.ValidationReport: + '''Checks to see if the activity has any undesirable non-deterministic edges + Parameters + ---------- + self + report + + Returns + ------- + + ''' + report = super(Activity, self).validate(report) + + # Check for objects with multiple outgoing ObjectFlow edges that are not of type ForkNode or DecisionNode + source_counts = Counter([e.source.lookup() for e in self.edges if isinstance(e,ObjectFlow)]) + multi_targets = {n: c for n, c in source_counts.items() if c>1 and not (isinstance(n,ForkNode) or isinstance(n,DecisionNode))} + for n, c in multi_targets.items(): + report.addWarning(n.identity, None, f'ActivityNode has {c} outgoing edges: multi-edges can cause nondeterministic flow') + + # Check that incoming flow counts obey constraints: + target_counts = Counter([e.target.lookup().unpin() for e in self.edges + if isinstance(e.target.lookup(), ActivityNode) ]) + # No InitialNode should have an incoming flow (though an ActivityParameterNode may) + initial_with_inflow = {n: c for n, c in target_counts.items() if isinstance(n,InitialNode)} + for n, c in initial_with_inflow.items(): + report.addError(n.identity, None, f'InitialNode must have no incoming edges, but has {c}') + # No node besides initiating nodes (InitialNode or ActivityParameterNode) should have no incoming flows + missing_inflow = set(self.nodes) - {n for n, c in target_counts.items()} - set(self.initiating_nodes()) + for n in missing_inflow: + report.addWarning(n.identity, None, f'Node has no incoming edges, so cannot be executed') + + return report +Activity.validate = activity_validate + +# TODO: add a check for loops that can obtain too many or too few values diff --git a/uml/uml-flattened.ttl b/uml/uml-flattened.ttl new file mode 100644 index 0000000..540f217 --- /dev/null +++ b/uml/uml-flattened.ttl @@ -0,0 +1,397 @@ +@prefix : . +@prefix uml: . +@prefix rdf: . +@prefix owl: . +@prefix xsd: . +@prefix sbol: . +@prefix rdfs: . +@prefix om: . +@base . +<> rdf:type owl:Ontology ; + owl:imports sbol: , + om: ; + rdfs:comment "Unified Modeling Languge (UML) subset, translated to an SBOL factory ontology." ; + owl:versionInfo "1.0-alpha1" . +owl:maxCardinality rdf:type owl:AnnotationProperty . +owl:minCardinality rdf:type owl:AnnotationProperty . +sbol:directlyComprises rdf:type owl:AnnotationProperty . +xsd:anySimpleType rdf:type rdfs:Datatype . +uml:type rdf:type owl:DatatypeProperty ; + rdfs:domain sbol:Identified ; + rdfs:range xsd:anyURI ; + rdfs:label "type" . +uml:isOrdered rdf:type owl:DatatypeProperty ; + rdfs:domain sbol:Identified ; + rdfs:range xsd:boolean ; + rdfs:label "is_ordered" . +uml:isUnique rdf:type owl:DatatypeProperty ; + rdfs:domain sbol:Identified ; + rdfs:range xsd:boolean ; + rdfs:label "is_unique" . +uml:lowerValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain sbol:Identified ; + rdfs:range uml:ValueSpecification ; + rdfs:label "lower_value" . +uml:upperValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain sbol:Identified ; + rdfs:range uml:ValueSpecification ; + rdfs:label "upper_value" . +uml:ValueSpecification rdf:type owl:Class ; + rdfs:subClassOf sbol:Identified . +_:Bgenid0 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:anyURI ; + owl:onProperty uml:type ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:LiteralSpecification rdf:type owl:Class ; + rdfs:subClassOf uml:ValueSpecification . +uml:LiteralNull rdf:type owl:Class ; + rdfs:subClassOf uml:LiteralSpecification . +uml:LiteralString rdf:type owl:Class ; + rdfs:subClassOf uml:LiteralSpecification . +_:Bgenid1 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:string ; + owl:onProperty uml:stringValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:LiteralString rdfs:subClassOf _:Bgenid1 . +uml:stringValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:LiteralString ; + rdfs:range xsd:string ; + rdfs:label "value" . +uml:LiteralInteger rdf:type owl:Class ; + rdfs:subClassOf uml:LiteralSpecification . +_:Bgenid2 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:integer ; + owl:onProperty uml:integerValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:LiteralInteger rdfs:subClassOf _:Bgenid2 . +uml:integerValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:LiteralInteger ; + rdfs:range xsd:integer ; + rdfs:label "value" . +uml:LiteralBoolean rdf:type owl:Class ; + rdfs:subClassOf uml:LiteralSpecification . +_:Bgenid3 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:boolean ; + owl:onProperty uml:booleanValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:LiteralBoolean rdfs:subClassOf _:Bgenid3 . +uml:booleanValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:LiteralBoolean ; + rdfs:range xsd:boolean ; + rdfs:label "value" . +uml:LiteralReal rdf:type owl:Class ; + rdfs:subClassOf uml:LiteralSpecification . +_:Bgenid4 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:float ; + owl:onProperty uml:realValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:LiteralReal rdfs:subClassOf _:Bgenid4 . +uml:realValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:LiteralReal ; + rdfs:range xsd:float ; + rdfs:label "value" . +uml:LiteralIdentified rdf:type owl:Class ; + rdfs:subClassOf uml:LiteralSpecification . +_:Bgenid5 rdf:type owl:Restriction ; + owl:onClass sbol:Identified ; + owl:onProperty uml:identifiedValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:LiteralIdentified rdfs:subClassOf _:Bgenid5 . +uml:identifiedValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:LiteralIdentified ; + rdfs:range sbol:Identified ; + rdfs:label "value" . +uml:LiteralReference rdf:type owl:Class ; + rdfs:subClassOf uml:LiteralSpecification . +_:Bgenid6 rdf:type owl:Restriction ; + owl:onClass sbol:Identified ; + owl:onProperty uml:referenceValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:LiteralReference rdfs:subClassOf _:Bgenid6 . +uml:referenceValue rdf:type owl:ObjectProperty ; + rdfs:domain uml:LiteralReference ; + rdfs:range sbol:Identified ; + rdfs:label "value" . +uml:Constraint rdf:type owl:Class ; + rdfs:subClassOf sbol:Identified . +_:Bgenid7 rdf:type owl:Restriction ; + owl:onClass sbol:Identified ; + owl:onProperty uml:constrainedElement . +uml:Constraint rdfs:subClassOf _:Bgenid7 . +_:Bgenid8 rdf:type owl:Restriction ; + owl:onClass uml:ValueSpecification ; + owl:onProperty uml:specification ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Constraint rdfs:subClassOf _:Bgenid8 . +uml:constrainedElement rdf:type owl:ObjectProperty ; + rdfs:domain uml:Constraint ; + rdfs:range sbol:Identified ; + rdfs:label "constrained_elements" . +uml:specification rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Constraint ; + rdfs:range uml:ValueSpecification ; + rdfs:label "specification" . +uml:Parameter rdf:type owl:Class ; + rdfs:subClassOf sbol:Identified . +_:Bgenid9 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:anyURI ; + owl:onProperty uml:direction ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Parameter rdfs:subClassOf _:Bgenid9 . +_:Bgenid10 rdf:type owl:Restriction ; + owl:onClass uml:ValueSpecification ; + owl:onProperty uml:defaultValue ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Parameter rdfs:subClassOf _:Bgenid10 . +_:Bgenid11 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:anyURI ; + owl:onProperty uml:type ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Parameter rdfs:subClassOf _:Bgenid11 . +_:Bgenid12 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:boolean ; + owl:onProperty uml:isOrdered ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Parameter rdfs:subClassOf _:Bgenid12 . +_:Bgenid13 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:boolean ; + owl:onProperty uml:isUnique ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Parameter rdfs:subClassOf _:Bgenid13 . +_:Bgenid14 rdf:type owl:Restriction ; + owl:onClass uml:ValueSpecification ; + owl:onProperty uml:lowerValue ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Parameter rdfs:subClassOf _:Bgenid14 . +_:Bgenid15 rdf:type owl:Restriction ; + owl:onClass uml:ValueSpecification ; + owl:onProperty uml:upperValue ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Parameter rdfs:subClassOf _:Bgenid15 . +uml:direction rdf:type owl:DatatypeProperty ; + rdfs:domain uml:Parameter ; + rdfs:range xsd:anyURI ; + rdfs:label "direction" . +uml:defaultValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Parameter ; + rdfs:range uml:ValueSpecification ; + rdfs:label "default_value" . +uml:Behavior rdf:type owl:Class ; + rdfs:subClassOf sbol:TopLevel . +_:Bgenid16 rdf:type owl:Restriction ; + owl:onClass uml:Constraint ; + owl:onProperty uml:precondition . +uml:Behavior rdfs:subClassOf _:Bgenid16 . +_:Bgenid17 rdf:type owl:Restriction ; + owl:onClass uml:Constraint ; + owl:onProperty uml:postcondition . +uml:Behavior rdfs:subClassOf _:Bgenid17 . +_:Bgenid18 rdf:type owl:Restriction ; + owl:onClass uml:Parameter ; + owl:onProperty uml:ownedParameter . +uml:Behavior rdfs:subClassOf _:Bgenid18 . +uml:precondition rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Behavior ; + rdfs:range uml:Constraint ; + rdfs:label "preconditions" . +uml:postcondition rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Behavior ; + rdfs:range uml:Constraint ; + rdfs:label "postconditions" . +uml:ownedParameter rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Behavior ; + rdfs:range uml:Parameter ; + rdfs:label "parameters" . +uml:Activity rdf:type owl:Class ; + rdfs:subClassOf uml:Behavior . +_:Bgenid19 rdf:type owl:Restriction ; + owl:onClass uml:ActivityNode ; + owl:onProperty uml:node . +uml:Activity rdfs:subClassOf _:Bgenid19 . +_:Bgenid20 rdf:type owl:Restriction ; + owl:onClass uml:ActivityEdge ; + owl:onProperty uml:edge . +uml:Activity rdfs:subClassOf _:Bgenid20 . +uml:node rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Activity ; + rdfs:range uml:ActivityNode ; + rdfs:label "nodes" . +uml:edge rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Activity ; + rdfs:range uml:ActivityEdge ; + rdfs:label "edges" . +uml:ActivityNode rdf:type owl:Class ; + rdfs:subClassOf sbol:Identified . +uml:ControlNode rdf:type owl:Class ; + rdfs:subClassOf uml:ActivityNode . +uml:InitialNode rdf:type owl:Class ; + rdfs:subClassOf uml:ControlNode . +uml:FinalNode rdf:type owl:Class ; + rdfs:subClassOf uml:ControlNode . +uml:FlowFinalNode rdf:type owl:Class ; + rdfs:subClassOf uml:FinalNode . +uml:ForkNode rdf:type owl:Class ; + rdfs:subClassOf uml:ControlNode . +uml:JoinNode rdf:type owl:Class ; + rdfs:subClassOf uml:ControlNode . +uml:MergeNode rdf:type owl:Class ; + rdfs:subClassOf uml:ControlNode . +uml:DecisionNode rdf:type owl:Class ; + rdfs:subClassOf uml:ControlNode . +_:Bgenid21 rdf:type owl:Restriction ; + owl:onClass uml:Behavior ; + owl:onProperty uml:decisionInput ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:DecisionNode rdfs:subClassOf _:Bgenid21 . +_:Bgenid22 rdf:type owl:Restriction ; + owl:onClass uml:ObjectFlow ; + owl:onProperty uml:decisionInputFlow ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:DecisionNode rdfs:subClassOf _:Bgenid22 . +uml:decisionInput rdf:type owl:ObjectProperty ; + rdfs:domain uml:DecisionNode ; + rdfs:range uml:Behavior ; + rdfs:label "decision_input" . +uml:decisionInputFlow rdf:type owl:ObjectProperty ; + rdfs:domain uml:DecisionNode ; + rdfs:range uml:ObjectFlow ; + rdfs:label "decision_input_flow" . +uml:ObjectNode rdf:type owl:Class ; + rdfs:subClassOf uml:ActivityNode . +_:Bgenid23 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:anyURI ; + owl:onProperty uml:type ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:ActivityParameterNode rdf:type owl:Class ; + rdfs:subClassOf uml:ObjectNode . +_:Bgenid24 rdf:type owl:Restriction ; + owl:onClass uml:Parameter ; + owl:onProperty uml:parameter ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:parameter rdf:type owl:ObjectProperty ; + rdfs:domain uml:ActivityParameterNode ; + rdfs:range uml:Parameter ; + rdfs:label "parameter" . +uml:ExecutableNode rdf:type owl:Class ; + rdfs:subClassOf uml:ActivityNode . +uml:Action rdf:type owl:Class ; + rdfs:subClassOf uml:ExecutableNode . +_:Bgenid25 rdf:type owl:Restriction ; + owl:onClass uml:InputPin ; + owl:onProperty uml:input . +uml:Action rdfs:subClassOf _:Bgenid25 . +_:Bgenid26 rdf:type owl:Restriction ; + owl:onClass uml:OutputPin ; + owl:onProperty uml:output . +uml:Action rdfs:subClassOf _:Bgenid26 . +uml:input rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Action ; + rdfs:range uml:InputPin ; + rdfs:label "inputs" . +uml:output rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Action ; + rdfs:range uml:OutputPin ; + rdfs:label "outputs" . +uml:InvocationAction rdf:type owl:Class ; + rdfs:subClassOf uml:Action . +uml:CallAction rdf:type owl:Class ; + rdfs:subClassOf uml:InvocationAction . +uml:CallBehaviorAction rdf:type owl:Class ; + rdfs:subClassOf uml:CallAction . +_:Bgenid27 rdf:type owl:Restriction ; + owl:onClass uml:Behavior ; + owl:onProperty uml:behavior . +uml:behavior rdf:type owl:ObjectProperty ; + rdfs:domain uml:CallBehaviorAction ; + rdfs:range uml:Behavior ; + rdfs:label "behavior" . +uml:Pin rdf:type owl:Class ; + rdfs:subClassOf uml:ObjectNode . +_:Bgenid28 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:boolean ; + owl:onProperty uml:isOrdered ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Pin rdfs:subClassOf _:Bgenid28 . +_:Bgenid29 rdf:type owl:Restriction ; + owl:allValuesFrom xsd:boolean ; + owl:onProperty uml:isUnique ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Pin rdfs:subClassOf _:Bgenid29 . +_:Bgenid30 rdf:type owl:Restriction ; + owl:onClass uml:ValueSpecification ; + owl:onProperty uml:lowerValue ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Pin rdfs:subClassOf _:Bgenid30 . +_:Bgenid31 rdf:type owl:Restriction ; + owl:onClass uml:ValueSpecification ; + owl:onProperty uml:upperValue ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:Pin rdfs:subClassOf _:Bgenid31 . +uml:InputPin rdf:type owl:Class ; + rdfs:subClassOf uml:Pin . +uml:ValuePin rdf:type owl:Class ; + rdfs:subClassOf uml:InputPin . +_:Bgenid32 rdf:type owl:Restriction ; + owl:onClass uml:ValueSpecification ; + owl:onProperty uml:value ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:ValuePin rdfs:subClassOf _:Bgenid32 . +uml:value rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:ValuePin ; + rdfs:range uml:ValueSpecification ; + rdfs:label "value" . +uml:OutputPin rdf:type owl:Class ; + rdfs:subClassOf uml:Pin . +uml:ActivityEdge rdf:type owl:Class ; + rdfs:subClassOf sbol:Identified . +_:Bgenid33 rdf:type owl:Restriction ; + owl:onClass uml:ActivityNode ; + owl:onProperty uml:source ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:ActivityEdge rdfs:subClassOf _:Bgenid33 . +_:Bgenid34 rdf:type owl:Restriction ; + owl:onClass uml:ActivityNode ; + owl:onProperty uml:target ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; + owl:maxCardinality "1"^^xsd:nonNegativeInteger . +uml:ActivityEdge rdfs:subClassOf _:Bgenid34 . +uml:source rdf:type owl:ObjectProperty ; + rdfs:domain uml:ActivityEdge ; + rdfs:range uml:ActivityNode ; + rdfs:label "source" . +uml:target rdf:type owl:ObjectProperty ; + rdfs:domain uml:ActivityEdge ; + rdfs:range uml:ActivityNode ; + rdfs:label "target" . +uml:ControlFlow rdf:type owl:Class ; + rdfs:subClassOf uml:ActivityEdge . +uml:ObjectFlow rdf:type owl:Class ; + rdfs:subClassOf uml:ActivityEdge . diff --git a/uml/uml.ttl b/uml/uml.ttl new file mode 100644 index 0000000..71602c3 --- /dev/null +++ b/uml/uml.ttl @@ -0,0 +1,693 @@ +@prefix : . +@prefix om: . +@prefix owl: . +@prefix rdf: . +@prefix xsd: . +@prefix rdfs: . +@prefix sbol: . +@prefix uml: . +@base . + + rdf:type owl:Ontology ; + owl:imports , ; + rdfs:comment "Unified Modeling Languge (UML) subset, translated to an SBOL factory ontology." ; + owl:versionInfo "1.0-alpha1" . + +################################################################# +# Annotation properties +################################################################# + +### http://www.w3.org/2002/07/owl#maxCardinality +# owl:maxCardinality rdf:type owl:AnnotationProperty . +# owl:minCardinality rdf:type owl:AnnotationProperty . + +################################################################# +# Object properties +################################################################# + +sbol:directlyComprises rdf:type owl:ObjectProperty . # should be SBOL - sbol_factory issue #10 + +################################################################# +# Datatypes +################################################################# + +### http://www.w3.org/2001/XMLSchema#anySimpleType +xsd:anySimpleType rdf:type rdfs:Datatype . +#sbol:Identified rdf:type owl:Class . +#sbol:TopLevel rdf:type owl:Class . + + +################################################################# +# Behaviors +################################################################# + +# Properties for TypedElement +uml:type rdf:type owl:DatatypeProperty ; + rdfs:comment "Specifies a set of Type instances constraining the allowed values. See UML 2.5.1 specification section 7.5." ; + rdfs:domain sbol:Identified ; # should probably actually be a union + rdfs:range xsd:anyURI ; + rdfs:label "type" . + +# Properties for MultiplicityElement +uml:isOrdered rdf:type owl:DatatypeProperty ; + rdfs:comment "For MultiplicityElement abstract class; UML 2.5.1 specification section 7.5" ; + rdfs:domain sbol:Identified ; # should probably actually be a union + rdfs:range xsd:boolean ; + rdfs:label "is_ordered" . + +uml:isUnique rdf:type owl:DatatypeProperty ; + rdfs:comment "For MultiplicityElement abstract class; UML 2.5.1 specification section 7.5" ; + rdfs:domain sbol:Identified ; # should probably actually be a union + rdfs:range xsd:boolean ; + rdfs:label "is_unique" . + +uml:lowerValue rdf:type owl:ObjectProperty ; + rdfs:comment "For MultiplicityElement abstract class; UML 2.5.1 specification section 7.5" ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain sbol:Identified ; # should probably actually be a union + rdfs:range uml:ValueSpecification ; + rdfs:label "lower_value" . + +uml:upperValue rdf:type owl:ObjectProperty ; + rdfs:comment "For MultiplicityElement abstract class; UML 2.5.1 specification section 7.5" ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain sbol:Identified ; # should probably actually be a union + rdfs:range uml:ValueSpecification ; + rdfs:label "upper_value" . + + +uml:ValueSpecification rdf:type owl:Class ; + rdfs:comment "A ValueSpecification is the specification of a (possibly empty) set of values. See UML 2.5.1 specification section 8." ; + rdfs:subClassOf sbol:Identified , + # Properties inherited from TypedElement: + [ rdf:type owl:Restriction ; owl:onProperty uml:type ; owl:allValuesFrom xsd:anyURI ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:type ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:LiteralSpecification rdf:type owl:Class ; + rdfs:comment "A LiteralSpecification identifies a literal constant being modeled. See UML 2.5.1 specification section 8.2." ; + rdfs:subClassOf uml:ValueSpecification . + # Omitted subclass: LiteralUnlimitedNatural + +uml:LiteralNull rdf:type owl:Class ; + rdfs:comment "A LiteralNull specifies the lack of a value. See UML 2.5.1 specification section 8.2." ; + rdfs:subClassOf uml:LiteralSpecification . + +uml:LiteralString rdf:type owl:Class ; + rdfs:comment "A LiteralSpecification identifies a literal constant being modeled. See UML 2.5.1 specification section 8.2." ; + rdfs:subClassOf uml:LiteralSpecification , + [ rdf:type owl:Restriction ; owl:onProperty uml:stringValue ; owl:allValuesFrom xsd:string ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:stringValue ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:stringValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:stringValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:LiteralString ; + rdfs:range xsd:string ; + rdfs:comment "The specified String value." ; + rdfs:label "value" . + +uml:LiteralInteger rdf:type owl:Class ; + rdfs:comment "A LiteralInteger is a specification of an Integer value. See UML 2.5.1 specification section 8.2." ; + rdfs:subClassOf uml:LiteralSpecification , + [ rdf:type owl:Restriction ; owl:onProperty uml:integerValue ; owl:allValuesFrom xsd:integer ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:integerValue ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:integerValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:integerValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:LiteralInteger ; + rdfs:range xsd:integer ; + rdfs:comment "The specified Integer value." ; + rdfs:label "value" . + +uml:LiteralBoolean rdf:type owl:Class ; + rdfs:comment "A LiteralBoolean is a specification of a Boolean value. See UML 2.5.1 specification section 8.2." ; + rdfs:subClassOf uml:LiteralSpecification , + [ rdf:type owl:Restriction ; owl:onProperty uml:booleanValue ; owl:allValuesFrom xsd:boolean ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:booleanValue ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:booleanValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:booleanValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:LiteralBoolean ; + rdfs:range xsd:boolean ; + rdfs:comment "The specified Boolean value." ; + rdfs:label "value" . + +uml:LiteralReal rdf:type owl:Class ; + rdfs:comment "A LiteralReal is a specification of a Real value. See UML 2.5.1 specification section 8.2." ; + rdfs:subClassOf uml:LiteralSpecification , + [ rdf:type owl:Restriction ; owl:onProperty uml:realValue ; owl:allValuesFrom xsd:float ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:realValue ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:realValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:realValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:LiteralReal ; + rdfs:range xsd:float ; + rdfs:comment "The specified Real value." ; + rdfs:label "value" . + +uml:LiteralIdentified rdf:type owl:Class ; + rdfs:comment "A LiteralIdentified is used for linking SBOL objects as a child object to UML objects." ; + rdfs:subClassOf uml:LiteralSpecification , + [ rdf:type owl:Restriction ; owl:onProperty uml:identifiedValue ; owl:allValuesFrom sbol:Identified ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:identifiedValue ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:identifiedValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:identifiedValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:LiteralIdentified ; + rdfs:range sbol:Identified ; + rdfs:comment "The embedded SBOL object" ; + rdfs:label "value" . + +uml:LiteralReference rdf:type owl:Class ; + rdfs:comment "A LiteralReference is used for embedding SBOL objects as a reference." ; + rdfs:subClassOf uml:LiteralSpecification , + [ rdf:type owl:Restriction ; owl:allValuesFrom sbol:Identified ; owl:onProperty uml:referenceValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:referenceValue rdf:type owl:ObjectProperty ; + rdfs:domain uml:LiteralReference ; + rdfs:range sbol:Identified ; + rdfs:comment "The referenced SBOL object." ; + rdfs:label "value" . + +uml:Expression rdf:type owl:Class ; + rdfs:comment "An Expression represents a node in an expression tree, which may be non-terminal or terminal. It defines a symbol, and has a possibly empty sequence of operands that are ValueSpecifications. See UML 2.5.1 specification section 8.6.5.1." ; + rdfs:subClassOf uml:ValueSpecification , + [ rdf:type owl:Restriction ; owl:allValuesFrom xsd:string ; owl:onProperty uml:symbolValue ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; owl:allValuesFrom xsd:boolean ; owl:onProperty uml:isOrdered ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty uml:operandValue ] . + +uml:symbolValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:Expression ; + rdfs:range xsd:anyURI ; + rdfs:comment "The symbol associated with this node in the expression tree." ; + rdfs:label "symbol" . + +uml:operandValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Expression ; + rdfs:range uml:OrderedPropertyValue ; + rdfs:comment "Specifies a sequence of operand ValueSpecifications." ; + rdfs:label "operand" . + +uml:TimeExpression rdf:type owl:Class ; + rdfs:comment "A TimeExpression is a ValueSpecification that represents a time value." ; + rdfs:subClassOf uml:ValueSpecification , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ValueSpecification ; owl:onProperty uml:exprValue ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:Observation ; owl:onProperty uml:observationValue ] . + +uml:Duration rdf:type owl:Class ; + rdfs:comment "A Duration is a ValueSpecification that specifies the temporal distance between two time instants." ; + rdfs:subClassOf uml:ValueSpecification , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ValueSpecification ; owl:onProperty uml:exprValue ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:Observation ; owl:onProperty uml:observationValue ] . + +uml:exprValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:TimeExpression, + uml:Duration; + rdfs:range uml:ValueSpecification ; + rdfs:comment "A ValueSpecification that evaluates to the value of the Duration." ; + rdfs:label "expr" . + +uml:observationValue rdf:type owl:ObjectProperty ; + rdfs:domain uml:TimeExpression, + uml:Duration; + rdfs:range uml:Observation ; + rdfs:comment "Refers to the Observations that are involved in the computation of the Duration value." ; + rdfs:label "observation" . + +uml:Observation rdf:type owl:Class ; + rdfs:subClassOf sbol:Identified ; + rdfs:comment "Observation specifies a value determined by observing an event or events that occur relative to other model entities." . + # Inherited from PackageableElement + +uml:TimeObservation rdf:type owl:Class ; + rdfs:comment "A TimeObservation is a reference to a time instant during an execution. It points out which entity in the model to observe and whether the observation is when this entity is entered or when it is exited." ; + rdfs:subClassOf uml:Observation , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty uml:firstEventValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; owl:allValuesFrom sbol:Identified ; owl:onProperty uml:timeObservationValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:DurationObservation rdf:type owl:Class ; + rdfs:comment "A DurationObservation is a reference to a duration during an execution. It points out the entities in the model to observe and whether the observations are when these entities are entered or exited." ; + rdfs:subClassOf uml:Observation , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty uml:firstEventValue ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; owl:maxCardinality "2"^^xsd:nonNegativeInteger ], + # FIXME events must be ordered + [ rdf:type owl:Restriction ; owl:allValuesFrom sbol:Identified ; owl:onProperty uml:durationObservationValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "2"^^xsd:nonNegativeInteger] . + +uml:firstEventValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:TimeObservation, + uml:DurationObservation, + uml:TimeConstraint, + uml:DurationConstraint ; + rdfs:range uml:OrderedPropertyValue ; + rdfs:comment "The value of firstEvent[i] is related to event[i] (where i is 1 or 2). If firstEvent[i] is true, then the correspondingobservation event is the first time instant the execution enters event[i]. If firstEvent[i] is false, then the corresponding observation event is the time instant the execution exits event[i]." ; + rdfs:label "firstEvent" . + +uml:timeObservationValue rdf:type owl:ObjectProperty ; + rdfs:comment "The TimeObservation is determined by the entering or exiting of the event during execution." ; + rdfs:domain uml:TimeObservation ; + rdfs:range sbol:Identified ; # FIXME Should be NamedElement + rdfs:label "event" . + +uml:durationObservationValue rdf:type owl:ObjectProperty ; + rdfs:domain uml:DurationObservation ; + rdfs:range sbol:Identified ; # FIXME Should be NamedElement + rdfs:comment "The DurationObservation is determined as the duration between the entering or exiting of a single event during execution, or the entering/exiting of one event and the entering/exiting of a second." ; + rdfs:label "event" . + +uml:Interval rdf:type owl:Class ; + rdfs:subClassOf uml:ValueSpecification ; + rdfs:comment "An Interval defines the range between two ValueSpecifications." . + +uml:TimeInterval rdf:type owl:Class ; + rdfs:subClassOf uml:Interval , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ValueSpecification ; owl:onProperty uml:minTimeValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ValueSpecification ; owl:onProperty uml:maxTimeValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] ; + rdfs:comment "A TimeInterval defines the range between two TimeExpressions." . + +uml:minTimeValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:TimeInterval ; + rdfs:range uml:TimeExpression ; + rdfs:comment "Refers to the TimeExpression denoting the minimum value of the range." ; + rdfs:label "min" . + +uml:maxTimeValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:TimeInterval ; + rdfs:range uml:TimeExpression ; + rdfs:comment "Refers to the TimeExpression denoting the maximum value of the range." ; + rdfs:label "max" . + +uml:DurationInterval rdf:type owl:Class ; + rdfs:subClassOf uml:Interval , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ValueSpecification ; owl:onProperty uml:minDurationValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ValueSpecification ; owl:onProperty uml:maxDurationValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] ; + rdfs:comment "A DurationInterval defines the range between two Durations." . + +uml:minDurationValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:DurationInterval ; + rdfs:range uml:Duration ; + rdfs:comment "Refers to the Duration denoting the minimum value of the range." ; + rdfs:label "min" . + +uml:maxDurationValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:DurationInterval ; + rdfs:range uml:Duration ; + rdfs:comment "Refers to the Duration denoting the maximum value of the range." ; + rdfs:label "max" . + +uml:Constraint rdf:type owl:Class ; + rdfs:comment "A Constraint is a condition or restriction expressed in natural language text or in a machine readable language. See UML 2.5.1 specification section 7.6." ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty uml:constrainedElement ] . + +uml:constrainedElement rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Constraint ; + rdfs:range uml:OrderedPropertyValue ; # equivalent to uml:Element in this context + rdfs:comment "The OrderedPropertyValue referenced by this Constraint." ; + rdfs:label "constrained_elements" . + +uml:IntervalConstraint rdf:type owl:Class ; + rdfs:subClassOf uml:Constraint ; + rdfs:comment "An IntervalConstraint is a Constraint that is specified by an Interval." . + +uml:TimeConstraint rdf:type owl:Class ; + rdfs:subClassOf uml:IntervalConstraint , + [ rdf:type owl:Restriction ; owl:onClass uml:TimeInterval ; owl:onProperty uml:timeSpecification ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty uml:firstEventValue ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] ; + rdfs:comment "A TimeConstraint is a Constraint that refers to a TimeInterval." . + +uml:timeSpecification rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:TimeConstraint ; + rdfs:range uml:TimeInterval ; + rdfs:comment "TheTimeInterval constraining the duration." ; + rdfs:label "specification" . + +uml:DurationConstraint rdf:type owl:Class ; + rdfs:subClassOf uml:IntervalConstraint , + [ rdf:type owl:Restriction ; owl:onClass uml:DurationInterval ; owl:onProperty uml:durationSpecification ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty uml:firstEventValue ; + owl:minCardinality "0"^^xsd:nonNegativeInteger ; owl:maxCardinality "2"^^xsd:nonNegativeInteger ] ; + rdfs:comment "A DurationConstraint is a Constraint that refers to a DurationInterval." . + +uml:durationSpecification rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:DurationConstraint ; + rdfs:range uml:DurationInterval ; + rdfs:comment "The DurationInterval constraining the duration." ; + rdfs:label "specification" . + + +uml:Parameter rdf:type owl:Class ; + rdfs:comment "A Parameter is a specification of an argument used to pass information into or out of an invocation of a Behavior. See UML 2.5.1 specification section 9.4." ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; owl:onProperty uml:direction ; owl:allValuesFrom xsd:anyURI ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:direction ; owl:minCardinality "1"^^xsd:nonNegativeInteger ], + [ rdf:type owl:Restriction ; owl:onProperty uml:direction ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:defaultValue ; owl:allValuesFrom uml:ValueSpecification ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:defaultValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + # omitted: /default, effect, isException, isStream + # Properties inherited from MultiplicityElement and TypedElement: + [ rdf:type owl:Restriction ; owl:onProperty uml:type ; owl:allValuesFrom xsd:anyURI ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:type ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isOrdered ; owl:allValuesFrom xsd:boolean ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isOrdered ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isOrdered ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isUnique ; owl:allValuesFrom xsd:boolean ], + [ rdf:type owl:Restriction ; owl:onProperty uml:isUnique ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isUnique ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:lowerValue ; owl:allValuesFrom uml:ValueSpecification ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:lowerValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:upperValue ; owl:allValuesFrom uml:ValueSpecification ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:upperValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + # omitted: /lower, /upper + +uml:direction rdf:type owl:DatatypeProperty ; + rdfs:domain uml:Parameter ; + rdfs:range xsd:anyURI ; # should really be an enumeration + rdfs:comment "Indicates whether a parameter is being sent into or out of a behavioral element." ; + rdfs:label "direction" . + +uml:defaultValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Parameter ; + rdfs:range uml:ValueSpecification ; + rdfs:comment "A ValueSpecification that represents a value to be used when no argument is supplied for the Parameter." ; + rdfs:label "default_value" . + + + +uml:Behavior rdf:type owl:Class ; + rdfs:comment "Behavior is an abstract specification of how a state changes over time. This specification may be a prospective definition of a protocol or a capture of an execution trace. See UML 2.5.1 specification section 13.2." ; + rdfs:subClassOf sbol:TopLevel , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:Constraint ; owl:onProperty uml:precondition ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:Constraint ; owl:onProperty uml:postcondition ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty uml:ownedParameter ] . + # omitted: context, specification, redefinedBehavior, ownedParameterSet + # omitted subclasses: OpaqueBehavior, FunctionBehavior + +#uml:isReentrant rdf:type owl:DatatypeProperty ; +# rdfs:domain uml:Behavior ; +# rdfs:range xsd:boolean ; +# rdfs:label "isReentrant" . + +uml:precondition rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Behavior ; + rdfs:range uml:Constraint ; + rdfs:comment "An optional set of Constraints specifying what must be fulfilled before the Behavior is invoked." ; + rdfs:label "preconditions" . + +uml:postcondition rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Behavior ; + rdfs:range uml:Constraint ; + rdfs:comment "An optional set of Constraints specifying what is fulfilled after the execution of the Behavior is completed, if its precondition was fulfilled before its invocation." ; + rdfs:label "postconditions" . + +uml:ownedParameter rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Behavior ; + rdfs:range uml:OrderedPropertyValue ; + rdfs:comment " a list of Parameters to the Behavior which describes the order and type of arguments that can be given when the Behavior is invoked and of the values which will be returned when the Behavior completes its execution." ; + rdfs:label "parameters" . + +uml:OrderedPropertyValue rdf:type owl:Class ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; owl:allValuesFrom xsd:integer ; owl:onProperty uml:indexValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom sbol:Identified ; owl:onProperty uml:propertyValue ; + owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:indexValue rdf:type owl:DatatypeProperty ; + rdfs:domain uml:OrderedPropertyValue ; + rdfs:range xsd:integer ; + rdfs:label "index" . + +uml:propertyValue rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:OrderedPropertyValue ; + rdfs:range sbol:Identified ; + rdfs:label "property_value" . + + +uml:Activity rdf:type owl:Class ; + rdfs:comment "An Activity coordinates and groups steps in a protocol or workflow. See UML 2.5.1 specification section 15." ; + rdfs:subClassOf uml:Behavior , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ActivityNode ; owl:onProperty uml:node ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:ActivityEdge ; owl:onProperty uml:edge ] . + # omitted: isReadOnly, isSingleExecution, variable + +uml:node rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Activity ; + rdfs:range uml:ActivityNode ; + rdfs:comment "ActivityNodes coordinated by the Activity." ; + rdfs:label "nodes" . + +uml:edge rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Activity ; + rdfs:range uml:ActivityEdge ; + rdfs:comment "ActivityEdges expressing flow between the nodes of the Activity." ; + rdfs:label "edges" . + + + +uml:ActivityNode rdf:type owl:Class ; + rdfs:comment "ActivityNode is an abstract class for points in the flow of an Activity connected by ActivityEdges. See UML 2.5.1 specification section 15.2." ; + rdfs:subClassOf sbol:Identified . + # omitted: redefinedNode + +uml:ControlNode rdf:type owl:Class ; + rdfs:comment "A ControlNode is a kind of ActivityNode used to manage the flow of tokens between other nodes in an Activity. It can manage branching and merging of workflows and the implementation of logic for flow control. See UML 2.5.1 specification section 15.3." ; + rdfs:subClassOf uml:ActivityNode . + +uml:InitialNode rdf:type owl:Class ; + rdfs:comment "An InitialNode acts as a starting point for executing an Activity. An Activity may have more than one InitialNodes that start multiple concurrent control flows. See UML 2.5.1 specification section 15.3." ; + rdfs:subClassOf uml:ControlNode . + +uml:FinalNode rdf:type owl:Class ; + rdfs:comment "A FinalNode is a ControlNode at which a flow in an Activity stops. A FinalNode shall not have outgoing ActivityEdges. See UML 2.5.1 specification section 15.3." ; + rdfs:subClassOf uml:ControlNode . + # omitted subclass: ActivityFinalNode + +uml:FlowFinalNode rdf:type owl:Class ; + rdfs:comment "A FlowFinalNode is a FinalNode that terminates a flow. All tokens accepted by a FlowFinalNode are destroyed. This has no effect on other flows in the Activity. See UML 2.5.1 specification section 15.3." ; + rdfs:subClassOf uml:FinalNode . + +uml:ForkNode rdf:type owl:Class ; + rdfs:comment "A ForkNode is a ControlNode that splits a flow into multiple concurrent flows. A ForkNode shall have exactly one incoming ActivityEdge, though it may have multiple outgoing ActivityEdges. See UML 2.5.1 specification section 15.3." ; + rdfs:subClassOf uml:ControlNode . + +uml:JoinNode rdf:type owl:Class ; + rdfs:comment "A JoinNode is a ControlNode that synchronizes multiple flows. A JoinNode shall have exactly one outgoing ActivityEdge but may have multiple incoming ActivityEdges. See UML 2.5.1 specification section 15.3." ; + rdfs:subClassOf uml:ControlNode . + # omitted: isCombineDuplicate, joinSpec + +uml:MergeNode rdf:type owl:Class ; + rdfs:comment "A MergeNode is a control node that brings together multiple flows without synchronization. A MergeNode shall have exactly one outgoing ActivityEdge but may have multiple incoming ActivityEdges. See UML 2.5.1 specification section 15.3." ; + rdfs:subClassOf uml:ControlNode . + +uml:DecisionNode rdf:type owl:Class ; + rdfs:comment "A DecisionNode is a ControlNode that chooses between outgoing flows. A DecisionNode shall have at least one and at most two incoming ActivityEdges, and at least one outgoing ActivityEdge. See UML 2.5.1 specification section 15.3." ; + rdfs:subClassOf uml:ControlNode , + [ rdf:type owl:Restriction ; owl:onProperty uml:decisionInput ; owl:allValuesFrom uml:Behavior ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:decisionInput ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:decisionInputFlow ; owl:allValuesFrom uml:ObjectFlow ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:decisionInputFlow ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:decisionInput rdf:type owl:ObjectProperty ; + rdfs:domain uml:DecisionNode ; + rdfs:range uml:Behavior ; + rdfs:comment "A Behavior that is executed to provide an input to guard ValueSpecifications on ActivityEdges outgoing from the DecisionNode." ; + rdfs:label "decision_input" . + +uml:decisionInputFlow rdf:type owl:ObjectProperty ; + rdfs:domain uml:DecisionNode ; + rdfs:range uml:ObjectFlow ; + rdfs:comment "An additional ActivityEdge incoming to the DecisionNode that provides a decision input value for the guards ValueSpecifications on ActivityEdges outgoing from the DecisionNode." ; + rdfs:label "decision_input_flow" . + + +uml:ObjectNode rdf:type owl:Class ; + rdfs:comment "An ObjectNode is a kind of ActivityNode used to hold value-containing object tokens during the course of the execution of an Activity. See UML 2.5.1 specification section 15.4." ; + rdfs:subClassOf uml:ActivityNode , + # omitted subtype: CentralBufferNode, DataStoreNode + # omitted: isControlType, ordering, selection, upperBound, inState + # Property inherited from TypedElement: + [ rdf:type owl:Restriction ; owl:onProperty uml:type ; owl:allValuesFrom xsd:anyURI ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:type ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + + +uml:ActivityParameterNode rdf:type owl:Class ; + rdfs:comment "An ActivityParameterNode is an ObjectNode for accepting values from the input Parameters or providing values to the output Parameters of an Activity. UML 2.5.1 specification section 15.4." ; + rdfs:subClassOf uml:ObjectNode , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OrderedPropertyValue ; owl:onProperty uml:parameter ] , + [ rdf:type owl:Restriction ; owl:minCardinality "1"^^xsd:nonNegativeInteger ; owl:onProperty uml:parameter ] , + [ rdf:type owl:Restriction ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ; owl:onProperty uml:parameter ] . + + +uml:parameter rdf:type owl:ObjectProperty ; + rdfs:domain uml:ActivityParameterNode ; + rdfs:range uml:OrderedPropertyValue ; + rdfs:comment "The Parameter for which the ActivityParameterNode will be accepting or providing values." ; + rdfs:label "parameter" . + + + +uml:ExecutableNode rdf:type owl:Class ; + rdfs:comment "An ExecutableNode is an abstract class for ActivityNodes whose execution may be controlled using ControlFlows and to which ExceptionHandlers may be attached. See UML 2.5.1 specification section 15.5." ; + rdfs:subClassOf uml:ActivityNode . + # omitted: handler + +uml:Action rdf:type owl:Class ; + rdfs:comment "An Action is the fundamental unit of executable functionality. The execution of an Action represents some transformation or processing in the modeled system. Actions provide the ExecutableNodes within Activities and may also be used within Interactions. See UML 2.5.1 specification section 16." ; + rdfs:subClassOf uml:ExecutableNode , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:InputPin ; owl:onProperty uml:input ] , + [ rdf:type owl:Restriction ; owl:allValuesFrom uml:OutputPin ; owl:onProperty uml:output ] . + # omitted: /context, localPrecondition, localPostcondition + +uml:input rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Action ; + rdfs:range uml:InputPin ; + rdfs:comment "The ordered set of InputPins representing the inputs to the Action." ; + rdfs:label "inputs" . + +uml:output rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:Action ; + rdfs:range uml:OutputPin ; + rdfs:comment "The ordered set of OutputPins representing outputs from the Action." ; + rdfs:label "outputs" . + +# Nearly all UML actions are omitted. Only those required are added here + +uml:InvocationAction rdf:type owl:Class ; + rdfs:comment "UML 2.5.1 specification section 16.3" ; + rdfs:subClassOf uml:Action . + # aliased: argument --> input + # omitted: onPort + # omitted subclass: SendObjectAction, SendSignalAction, BroadcastSignalAction + +uml:CallAction rdf:type owl:Class ; + rdfs:comment "A CallAction is an abstract class for Actions that invoke a Behavior with given argument values and (if the invocation is synchronous) receive reply values. See UML 2.5.1 specification section 16.3." ; + rdfs:subClassOf uml:InvocationAction . + # aliased: result --> output + # omitted: isSynchronous + # omitted subclass: CallOperationAction, StartObjectBehaviorAction + +uml:CallBehaviorAction rdf:type owl:Class ; + rdfs:comment "A CallBehaviorAction is a CallAction that invokes a Behavior directly. The argument values of the CallBehaviorAction are passed on the input Parameters of the invoked Behavior. UML 2.5.1 specification section 16.3" ; + rdfs:subClassOf uml:CallAction , + [ rdf:type owl:Restriction ; owl:onProperty uml:behavior ; owl:allValuesFrom uml:Behavior ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:behavior ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:behavior ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:behavior rdf:type owl:ObjectProperty ; + rdfs:domain uml:CallBehaviorAction ; + rdfs:range uml:Behavior ; + rdfs:comment "The Behavior being invoked."; + rdfs:label "behavior" . + + +uml:Pin rdf:type owl:Class ; + rdfs:comment "A Pin is an ObjectNode and MultiplicityElement that provides input values to an Action or accepts output values from an Action. See UML 2.5.1 specification section 16.2." ; + rdfs:subClassOf uml:ObjectNode , + [ rdf:type owl:Restriction ; owl:onProperty uml:isOrdered ; owl:allValuesFrom xsd:boolean ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isOrdered ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isOrdered ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isUnique ; owl:allValuesFrom xsd:boolean ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isUnique ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:isUnique ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:lowerValue ; owl:allValuesFrom uml:ValueSpecification ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:lowerValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:upperValue ; owl:allValuesFrom uml:ValueSpecification ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:upperValue ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + + +uml:InputPin rdf:type owl:Class ; + rdfs:comment "An InputPin is a Pin that holds input values to be consumed by an Action. See UML 2.5.1 specification section 16.2." ; + rdfs:subClassOf uml:Pin . + # omitted subclass: ActionInputPin + +uml:ValuePin rdf:type owl:Class ; + rdfs:comment "A ValuePin is an InputPin that provides a value by evaluating a ValueSpecification. See UML 2.5.1 specification section 16.2." ; + rdfs:subClassOf uml:InputPin , + [ rdf:type owl:Restriction ; owl:onProperty uml:value ; owl:allValuesFrom uml:ValueSpecification ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:value ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:value ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + +uml:value rdf:type owl:ObjectProperty ; + rdfs:subPropertyOf sbol:directlyComprises ; + rdfs:domain uml:ValuePin ; + rdfs:range uml:ValueSpecification ; + rdfs:comment "The ValueSpecification that is evaluated to obtain the value that the ValuePin will provide." ; + rdfs:label "value" . + + +uml:OutputPin rdf:type owl:Class ; + rdfs:comment "An OutputPin is a Pin that holds output values produced by an Action. See UML 2.5.1 specification section 16.2." ; + rdfs:subClassOf uml:Pin . + + +uml:ActivityEdge rdf:type owl:Class ; + rdfs:comment "An ActivityEdge is an abstract class for directed connections between two ActivityNodes. See UML 2.5.1 specification section 15.2." ; + rdfs:subClassOf sbol:Identified , + [ rdf:type owl:Restriction ; owl:onProperty uml:source ; owl:allValuesFrom uml:ActivityNode ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:source ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:source ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:target ; owl:allValuesFrom uml:ActivityNode ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:target ; owl:minCardinality "1"^^xsd:nonNegativeInteger ] , + [ rdf:type owl:Restriction ; owl:onProperty uml:target ; owl:maxCardinality "1"^^xsd:nonNegativeInteger ] . + # omitted: redefinedEdge, guard, weight + # Note that guard will need to be added to allow DecisionNode + +uml:source rdf:type owl:ObjectProperty ; + rdfs:domain uml:ActivityEdge ; + rdfs:range uml:ActivityNode ; + rdfs:comment "The ActivityNode from which tokens are taken when they traverse the ActivityEdge." ; + rdfs:label "source" . + +uml:target rdf:type owl:ObjectProperty ; + rdfs:domain uml:ActivityEdge ; + rdfs:range uml:ActivityNode ; + rdfs:comment "The ActivityNode to which tokens are put when they traverse the ActivityEdge." ; + rdfs:label "target" . + + + +uml:ControlFlow rdf:type owl:Class ; + rdfs:comment "A ControlFlow is an ActivityEdge traversed by control tokens or object tokens of control type, which are use to control the execution of ExecutableNodes. See UML 2.5.1 specification section 15.2." ; + rdfs:subClassOf uml:ActivityEdge . + +uml:ObjectFlow rdf:type owl:Class ; + rdfs:comment "An ObjectFlow is an ActivityEdge that is traversed by object tokens that may hold values. Object flows also support multicast/receive, token selection from object nodes, and transformation of tokens. See UML 2.5.1 specification section 15.2." ; + rdfs:subClassOf uml:ActivityEdge . + # omitted: isMulticast, isMultireceive, transformation, selection + + + diff --git a/uml/uml_graphviz.py b/uml/uml_graphviz.py new file mode 100644 index 0000000..d7bd309 --- /dev/null +++ b/uml/uml_graphviz.py @@ -0,0 +1,147 @@ +from uml import * +import tyto +import sbol3 + +def activity_node_dot_attrs(self): + return {'label': '', 'shape': 'circle'} +ActivityNode.dot_attrs = activity_node_dot_attrs # Add to class via monkey patch + +def initial_node_dot_attrs(self): + return {'label': '', 'shape': 'circle', 'style': 'filled', 'fillcolor': 'black'} +InitialNode.dot_attrs = initial_node_dot_attrs # Add to class via monkey patch + +def final_node_dot_attrs(self): + return {'label': '', 'shape': 'doublecircle', 'style': 'filled', 'fillcolor': 'black'} +FinalNode.dot_attrs = final_node_dot_attrs # Add to class via monkey patch + +def join_node_dot_attrs(self): + return {'label': '', 'shape': 'rectangle', 'height': '0.02', 'style': 'filled', 'fillcolor': 'black'} +JoinNode.dot_attrs = join_node_dot_attrs # Add to class via monkey patch + +def fork_node_dot_attrs(self): + return {'label': '', 'shape': 'rectangle', 'height': '0.02', 'style': 'filled', 'fillcolor': 'black'} +ForkNode.dot_attrs = fork_node_dot_attrs # Add to class via monkey patch + +def merge_node_dot_attrs(self): + return {'label': '', 'shape': 'diamond'} +MergeNode.dot_attrs = merge_node_dot_attrs # Add to class via monkey patch + +def decision_node_dot_attrs(self): + return {'label': '', 'shape': 'diamond'} +DecisionNode.dot_attrs = decision_node_dot_attrs # Add to class via monkey patch + +def object_node_dot_attrs(self): + raise ValueError(f'Do not know what GraphViz label to use for {self}') +ObjectNode.dot_attrs = object_node_dot_attrs # Add to class via monkey patch + +def input_pin_dot_attrs(self): + return {} +InputPin.dot_attrs = input_pin_dot_attrs # Add to class via monkey patch + + +def activity_parameter_node_dot_attrs(self): + label = self.parameter.lookup().name + return {'label': label, 'shape': 'rectangle', 'peripheries': '2'} +ActivityParameterNode.dot_attrs = activity_parameter_node_dot_attrs # Add to class via monkey patch + +def executable_node_dot_attrs(self): + raise ValueError(f'Do not know what GraphViz label to use for {self}') +ExecutableNode.dot_attrs = executable_node_dot_attrs # Add to class via monkey patch + +def call_behavior_action_node_dot_attrs(self): + port_row = ' {}
\n' + in_ports = ' '.join(f'{i.dot_node_name()}' for i in self.inputs) + in_row = port_row.format(in_ports) if in_ports else '' + out_ports = ' '.join(f'{o.name}' for o in self.outputs) + out_row = port_row.format(out_ports) if out_ports else '' + + node_row = f' {self.behavior.lookup().display_id}\n' + table_template = '<\n{}{}{}
>' + label = table_template.format(in_row, node_row, out_row) + shape = 'none' + return {'label': label, 'shape': shape, 'style': 'rounded'} +CallBehaviorAction.dot_attrs = call_behavior_action_node_dot_attrs # Add to class via monkey patch + + +def input_pin_dot_node_name(self): + return self.name +InputPin.dot_node_name = input_pin_dot_node_name # Add to class via monkey patch + +def value_pin_dot_node_name(self): + literal_str = self.value.dot_value() + return f'{self.name}: {literal_str}' +ValuePin.dot_node_name = value_pin_dot_node_name # Add to class via monkey patch + +def literal_string_dot_value(self): + return self.value +LiteralString.dot_value = literal_string_dot_value # Add to class via monkey patch + +def literal_integer_dot_value(self): + return str(self.value) +LiteralInteger.dot_value = literal_integer_dot_value # Add to class via monkey patch + +def literal_boolean_dot_value(self): + return str(self.value) +LiteralBoolean.dot_value = literal_boolean_dot_value # Add to class via monkey patch + +def literal_real_dot_value(self): + return str(self.value) +LiteralReal.dot_value = literal_real_dot_value # Add to class via monkey patch + +def literal_identified_dot_value(self): + literal = self.value + if isinstance(literal, sbol3.Measure): + # TODO: replace kludge with something nicer + if literal.unit.startswith('http://www.ontology-of-units-of-measure.org'): + unit = tyto.OM.get_term_by_uri(literal.unit) + else: + unit = literal.unit.rsplit('/', maxsplit=1)[1] + val_str = f'{literal.value} {unit}' + else: + val_str = literal.name or literal.display_id + return val_str +LiteralIdentified.dot_value = literal_identified_dot_value # Add to class via monkey patch + +def literal_reference_dot_value(self): + literal = self.value.lookup() + if isinstance(literal, sbol3.Measure): + # TODO: replace kludge with something nicer + if literal.unit.startswith('http://www.ontology-of-units-of-measure.org'): + unit = tyto.OM.get_term_by_uri(literal.unit) + else: + unit = literal.unit.rsplit('/', maxsplit=1)[1] + val_str = f'{literal.value} {unit}' + else: + val_str = literal.name or literal.display_id + return val_str +LiteralReference.dot_value = literal_reference_dot_value # Add to class via monkey patch + + +def _gv_sanitize(id: str): + return id.replace(":", "_") + +def identified_dot_label(self, parent_identity=None): + truncated = _gv_sanitize(self.identity.replace(f'{parent_identity}/', '')) + in_struct = truncated.replace('/', ':') + return in_struct +sbol3.Identified.dot_label = identified_dot_label # Add to class via monkey patch + +def parameter_str(self): + """ + Create a human readable string for a parameter. + :param self: + :return: str + """ + default_value_str = f"= {self.default_value}" if self.default_value else "" + return f"""{self.name}: {self.type} {default_value_str}""" +Parameter.__str__ = parameter_str + +def parameter_template(self): + """ + Create a template for a parameter. Used for populating UI elements. + :param self: + :return: str + """ + default_value_str = f"= {self.default_value}" if self.default_value else "" + return f"""{self.name}=\'{default_value_str}\'""" +Parameter.template = parameter_template \ No newline at end of file