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)
def test_list_wrapping(self): # Ensure that at least certain properties handle automatic list # wrapping and are typed to do so. # See https://github.com/SynBioDex/pySBOL3/issues/301 sbol3.set_namespace('https://github.com/synbiodex/pysbol3') source_uri = 'https://example.org/source' derived_from_uri = 'https://example.org/derived_from' statute_mile = sbol3.OM_NS + 'mile-Statute' comp1_type = sbol3.SBO_DNA comp1_role = sbol3.SO_PROMOTER comp1_seq1 = sbol3.Sequence('seq1') comp1_model = sbol3.Model('model1', source=source_uri, language='https://example.org/language', framework='https://example.org/framework') comp1_attachment = sbol3.Attachment('att1', source=source_uri) comp1_measure = sbol3.Measure(value=26.2, unit=statute_mile) comp1_activity = sbol3.Activity('activity1') comp1 = sbol3.Component('comp1', types=comp1_type, sequences=comp1_seq1, roles=comp1_role, models=comp1_model, attachments=comp1_attachment, derived_from=derived_from_uri, measures=comp1_measure, generated_by=comp1_activity) self.assertEqual([comp1_type], comp1.types) self.assertEqual([comp1_seq1.identity], comp1.sequences) self.assertEqual([comp1_role], comp1.roles) self.assertEqual([comp1_model.identity], comp1.models) self.assertEqual([comp1_attachment.identity], comp1.attachments) self.assertEqual([derived_from_uri], comp1.derived_from) self.assertEqual([comp1_measure], comp1.measures) self.assertEqual([comp1_activity.identity], comp1.generated_by)
def test_full_constructor(self): identity = 'https://github.com/synbiodex/pysbol3/s1' elements = 'GCAT' encoding = sbol3.IUPAC_DNA_ENCODING attachments = ['https://github.com/synbiodex/pysbol3/attachment1'] name = None description = None derived_from = ['https://github.com/synbiodex/pysbol3/parent1'] generated_by = ['https://github.com/synbiodex/pysbol3/tool1'] m1 = sbol3.Measure(value=2.3, unit='meter') measures = [m1] s1 = sbol3.Sequence(identity=identity, elements=elements, encoding=encoding, attachments=attachments, name=name, description=description, derived_from=derived_from, generated_by=generated_by, measures=measures) self.assertEqual(identity, s1.identity) self.assertEqual(elements, s1.elements) self.assertEqual(encoding, s1.encoding) self.assertEqual(attachments, s1.attachments) self.assertEqual(name, s1.name) self.assertEqual(description, s1.description) self.assertEqual(derived_from, s1.derived_from) self.assertEqual(generated_by, s1.generated_by) self.assertEqual(measures, s1.measures)
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 test_measures_initial_value(self): # See https://github.com/SynBioDex/pySBOL3/issues/301 sbol3.set_namespace('https://github.com/synbiodex/pysbol3') metre = 'http://www.ontology-of-units-of-measure.org/resource/om-2/metre' one_metre = sbol3.Measure(1, unit=metre) two_metres = sbol3.Measure(2, unit=metre) # Test passing a list of measures c1 = sbol3.Component('c1', types=[sbol3.SBO_DNA], measures=[one_metre, two_metres]) self.assertListEqual([one_metre, two_metres], list(c1.measures)) # test passing a singleton measure three_metres = sbol3.Measure(3, unit=metre) c2 = sbol3.Component('c2', types=[sbol3.SBO_DNA], measures=three_metres) self.assertListEqual([three_metres], list(c2.measures))
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 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 test_list_wrapping(self): # Ensure that at least certain properties handle automatic list # wrapping and are typed to do so. # See https://github.com/SynBioDex/pySBOL3/issues/301 sbol3.set_namespace('https://github.com/synbiodex/pysbol3') unit_uri = 'http://www.ontology-of-units-of-measure.org/resource/om-2/gramPerLitre' # SBO:0000612 is from the SBOL 3.0.1 specification. test_type = 'https://identifiers.org/SBO:0000612' measure1 = sbol3.Measure(value=1.0, unit=unit_uri, types=test_type) self.assertEqual([test_type], measure1.types)
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 agent = sbol3.Agent("test_agent") # Execute the protocol # In order to get repeatable timings, we use ordinal time in the test # where each timepoint is one second after the previous time point ee = ExecutionEngine(use_ordinal_time=True) parameter_values = [ paml.ParameterValue( parameter=protocol.get_input("wavelength"), value=uml.LiteralIdentified( value=sbol3.Measure(100, tyto.OM.nanometer))) ] execution = ee.execute(protocol, agent, id="test_execution", parameter_values=parameter_values) # Get the SampleData objects and attach values # get_data() returns a dict of output parameter ids to SampleData objects dataset = execution.get_data() for k, v in dataset.data_vars.items(): for dimension in v.dims: new_data = [8] * len(dataset[k].data) dataset.update({k: (dimension, new_data)}) execution.set_data(dataset) 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_data_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_data_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_create(self): unit = 'https://sbolstandard.org/examples/millimolePerLitre' value = 0.1 types = ['https://identifiers.org/SBO:0000196', 'https://identifiers.org/SBO:0000197'] measure = sbol3.Measure(value, unit) measure.types = types self.assertIsNotNone(measure) self.assertIsInstance(measure, sbol3.Measure) self.assertEqual(value, measure.value) self.assertCountEqual(types, measure.types) self.assertEqual(unit, measure.unit)
def test_singleton_property_update_identity(self): sbol3.set_namespace('https://github.com/synbiodex/pysbol3') tl = sbol3.CustomTopLevel( 'foo', 'http://synbio.bbn.com/opil#MeasurementValue') tl.measure = sbol3.OwnedObject(tl, 'http://synbio.bbn.com/opil#measure', 0, 1) m = sbol3.Measure(10, 'liters') tl.measure = m expected = posixpath.join(tl.identity, 'Measure1') self.assertIsNotNone(tl.measure.identity) self.assertEqual(expected, tl.measure.identity)
def test_parse_class_name(self): sbol3.set_namespace('https://github.com/synbiodex/pysbol3') # Test parsing with # delimiter in type URI c = sbol3.Component('c', sbol3.SBO_DNA) class_name = parse_class_name(c.type_uri) self.assertEqual(class_name, 'Component') # Test parsing with # delimiter in type URI m = sbol3.Measure(0, 'dollars') class_name = parse_class_name(m.type_uri) self.assertEqual(class_name, 'Measure') # Test failure with invalid type URI with self.assertRaises(ValueError): parse_class_name('Component')
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
def test_round_trip2(self): # See https://github.com/SynBioDex/pySBOL3/issues/159 sbol3.set_namespace('https://github.com/synbiodex/pysbol3') doc1 = sbol3.Document() comp1 = sbol3.Component('comp1', sbol3.SBO_DNA) doc1.add(comp1) cd1 = sbol3.CombinatorialDerivation('cd1', comp1) self.assertEqual(comp1.identity, cd1.template) doc1.add(cd1) vf1 = sbol3.VariableFeature(cardinality=sbol3.SBOL_ONE, variable=sbol3.PYSBOL3_MISSING) hour = 'https://identifiers.org/ncit:C25529' m1 = sbol3.Measure(32, hour) vf1.variant_measures.append(m1) cd1.variable_features.append(vf1) self.assertTrue(vf1.identity.startswith(cd1.identity)) # Ensure that Measure m1 is valid. The bug tested here was that it # had been assigned an invalid displayId. report = m1.validate() self.assertEqual(0, len(report.errors))
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 _run_execution(self, doc, specializations): protocol = doc.find( "https://bbn.com/scratch/iGEM_LUDOX_OD_calibration_2018") ############################################# # Execution Configuration ee = ExecutionEngine(specializations=specializations) 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)
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)
# 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
# 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(
def test_create_protocol(self): protocol: paml.Protocol doc: sbol3.Document logger = logging.getLogger("transfer_map_protocol") logger.setLevel(logging.INFO) protocol, doc = initialize_protocol() # The aliquots will be the coordinates of the SampleArray and SampleMap objects num_aliquots = 4 aliquot_ids = list(range(num_aliquots)) # Make Components for the contents of the SampleArray reagent1 = sbol3.Component( 'ddH2Oa', 'https://identifiers.org/pubchem.substance:24901740') reagent2 = sbol3.Component( 'ddH2Ob', 'https://identifiers.org/pubchem.substance:24901740') reagents = [reagent1, reagent2] # TODO ContainerSpec without parameters will refer to a logical container of unspecified size and geometry source_spec = paml.ContainerSpec(name='abstractPlateRequirement1') target_spec = paml.ContainerSpec(name='abstractPlateRequirement2') # Arbitrary volume to use in specifying the reagents in the container. default_volume = sbol3.Measure(600, tyto.OM.microliter) # Creating the source SampleArray involves the following steps: # 1. Calling the EmptyContainer primitive with a defined specifcation # 2. Creating the SampleArray and referencing the specification. # (SBOLFactory needs the specification to have an identity, which only # happens in step 1.) # 3. Remove the EmptyContainer InputPin for "sample_array" # 4. Create a ValuePin for "sample_array" and add it to the input of the EmptyContainer call. # (This is a place where we can map a SampleArray to a container) # 1. create_source = protocol.primitive_step('EmptyContainer', specification=source_spec) # 2. # The SampleArray is a 2D |aliquot| x |reagent| array, where values are volumes. # The aliquot dimension uses aliquot_ids (specified above) as coordinates. # The reagent dimension uses reagents (specified above) as coordinates. # # Results in the DataArray representing contents: # # <xarray.DataArray (aliquot: 4, contents: 2)> # array([[600., 600.], # [600., 600.], # [600., 600.], # [600., 600.]]) # Coordinates: # * aliquot (aliquot) int64 0 1 2 3 # * contents (contents) <U30 'https://bbn.com/scratch/ddH2Oa' 'https://bbn.c... source_array = paml.SampleArray( name="source", container_type=source_spec, contents=json.dumps( xr.DataArray( [[default_volume.value for reagent in reagents] for id in aliquot_ids], dims=("aliquot", "contents"), coords={ "aliquot": aliquot_ids, "contents": [r.identity for r in reagents] }).to_dict())) # 3. sample_array_parameter = create_source.pin_parameter("sample_array") [old_input ] = [x for x in create_source.inputs if x.name == "sample_array"] create_source.inputs.remove(old_input) # 4. create_source.inputs.append( uml.ValuePin( name="sample_array", is_ordered=sample_array_parameter.property_value.is_ordered, is_unique=sample_array_parameter.property_value.is_unique, value=uml.literal(source_array))) # Similar to the source_array, above, we specify an analogous target_array # 1. create_target = protocol.primitive_step('EmptyContainer', specification=target_spec) # 2. target_array = paml.SampleArray( name="target", container_type=target_spec, contents=json.dumps( xr.DataArray( [[0.0 for reagent in reagents] for id in aliquot_ids], dims=("aliquot", "contents"), coords={ "aliquot": aliquot_ids, "contents": [r.identity for r in reagents] }).to_dict())) # 3. sample_array_parameter = create_target.pin_parameter("sample_array") [old_input ] = [x for x in create_target.inputs if x.name == "sample_array"] create_target.inputs.remove(old_input) # 4. create_target.inputs.append( uml.ValuePin( name="sample_array", is_ordered=sample_array_parameter.property_value.is_ordered, is_unique=sample_array_parameter.property_value.is_unique, value=uml.literal(target_array))) # plan_mapping is a 4D array of volumes for transfers: # (source_array, source_aliquot) --volume--> (target_array, target_aliquot) # # The plan_mapping from above is a single source_array and single target_array: # # <xarray.DataArray (source_array: 1, source_aliquot: 4, target_array: 1, # target_aliquot: 4)> # array([[[[10., 10., 10., 10.]], # [[10., 10., 10., 10.]], # [[10., 10., 10., 10.]], # [[10., 10., 10., 10.]]]]) # Coordinates: # * source_array (source_array) <U6 'source' # * source_aliquot (source_aliquot) int64 0 1 2 3 # * target_array (target_array) <U6 'target' # * target_aliquot (target_aliquot) int64 0 1 2 3 plan_mapping = json.dumps( xr.DataArray( [[[[10.0 for target_aliquot in aliquot_ids] for target_array in [target_array.name]] for source_aliquot in aliquot_ids] for source_array in [source_array.name]], dims=( "source_array", "source_aliquot", "target_array", "target_aliquot", ), coords={ "source_array": [source_array.name], "source_aliquot": aliquot_ids, "target_array": [target_array.name], "target_aliquot": aliquot_ids }).to_dict()) # The SampleMap specifies the sources and targets, along with the mappings. plan = paml.SampleMap(sources=[source_array], targets=[target_array], values=plan_mapping) # The outputs of the create_source and create_target calls will be identical # to the source_array and target_array. They will not be on the output pin # until execution, but the SampleMap needs to reference them. transfer_by_map = protocol.primitive_step( 'TransferByMap', source=create_source.output_pin('samples'), destination=create_target.output_pin('samples'), plan=plan) ######################################## # Validate and write the document agent = sbol3.Agent("test_agent") # Execute the protocol # In order to get repeatable timings, we use ordinal time in the test # where each timepoint is one second after the previous time point ee = ExecutionEngine(use_ordinal_time=True) parameter_values = [ # paml.ParameterValue(parameter=protocol.get_input("wavelength"), # value=uml.LiteralIdentified(value=sbol3.Measure(100, tyto.OM.nanometer))) ] execution = ee.execute(protocol, agent, id="test_execution", parameter_values=parameter_values) 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_data_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', 'sample_map_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_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')