def test_template_validations(caplog): """Make sure template validations and level controls behave as expected.""" msr_tmpl = MeasurementTemplate( name="Measurement Template", properties=[PropertyTemplate("Name", bounds=RealBounds(0, 1, ""))], conditions=[ConditionTemplate("Name", bounds=RealBounds(0, 1, ""))], parameters=[ParameterTemplate("Name", bounds=RealBounds(0, 1, ""))], ) msr_spec = MeasurementSpec("Measurement Spec", template=msr_tmpl) msr_run = MeasurementRun("MeasurementRun", spec=msr_spec) with validation_level(WarningLevel.IGNORE): msr_run.properties.append(Property("Name", value=NominalReal(-1, ""))) msr_run.conditions.append(Condition("Name", value=NominalReal(-1, ""))) msr_run.parameters.append(Parameter("Name", value=NominalReal(-1, ""))) assert len(caplog.records) == 0, "Logging records wasn't empty" with validation_level(WarningLevel.WARNING): msr_run.properties.append(Property("Name", value=NominalReal(-1, ""))) assert len( caplog.records) == 1, "WARNING didn't warn on invalid Property." msr_run.conditions.append(Condition("Name", value=NominalReal(-1, ""))) assert len( caplog.records) == 2, "WARNING didn't warn on invalid Condition." msr_run.parameters.append(Parameter("Name", value=NominalReal(-1, ""))) assert len( caplog.records) == 3, "WARNING didn't warn on invalid Parameter." with validation_level(WarningLevel.FATAL): with pytest.raises(ValueError): msr_run.properties.append( Property("Name", value=NominalReal(-1, ""))) with pytest.raises(ValueError): msr_run.conditions.append( Condition("Name", value=NominalReal(-1, ""))) with pytest.raises(ValueError): msr_run.parameters.append( Parameter("Name", value=NominalReal(-1, "")))
def test_attributes(): """Exercise permutations of attributes, bounds and values.""" prop_tmpl = PropertyTemplate(name="Property", bounds=RealBounds(0, 10, "m")) cond_tmpl = ConditionTemplate(name="Condition", bounds=CategoricalBounds(["a", "b", "c"])) param_tmpl = ParameterTemplate(name="Parameter", bounds=CompositionBounds( EmpiricalFormula.all_elements())) mol_tmpl = PropertyTemplate(name="Molecule", bounds=MolecularStructureBounds()) int_tmpl = ConditionTemplate(name="Integer", bounds=IntegerBounds(0, 10)) msr = add_measurement(make_node('Material'), name='Measurement', attributes=[ make_attribute(prop_tmpl, 5), make_attribute(cond_tmpl, 'a'), make_attribute(param_tmpl, 'SiC'), make_attribute(mol_tmpl, 'InChI=1S/CSi/c1-2'), make_attribute(mol_tmpl, '[C-]#[Si+]'), make_attribute(int_tmpl, 5) ]) assert msr.properties[0].value.nominal == 5 assert msr.conditions[0].value.category == 'a' assert msr.parameters[0].value.formula == 'SiC' assert msr.properties[1].value.inchi == 'InChI=1S/CSi/c1-2' assert msr.properties[2].value.smiles == '[C-]#[Si+]' assert msr.conditions[1].value.nominal == 5
def _to_bounds(self) -> RealBounds: """ Return the smallest bounds object that is consistent with the Value. Returns ------- RealBounds The minimally consistent :class:`bounds <gemd.entity.bounds.real_bounds.RealBounds>`. """ return RealBounds(lower_bound=self.lower_bound, upper_bound=self.upper_bound, default_units=self.units)
def test_valid_template_conversions(): expected = [ (PropertyTemplate(name="density", bounds=RealBounds(lower_bound=0, upper_bound=100, default_units="g/cm^3")), density_desc), (ConditionTemplate( name="speed", bounds=CategoricalBounds(categories=["low", "high"])), CategoricalDescriptor(key="speed", categories=["low", "high"])), (ParameterTemplate(name="solvent", bounds=MolecularStructureBounds()), MolecularStructureDescriptor(key="solvent")), (PropertyTemplate(name="formula", bounds=CompositionBounds( components=EmpiricalFormula.all_elements())), ChemicalFormulaDescriptor(key="formula")) ] for tmpl, desc in expected: assert template_to_descriptor(tmpl) == desc
def test_contains(): """Test that bounds know if a Value is contained within it.""" bounds = RealBounds(1, 3, 'm') assert bounds.contains(NormalReal(300, 10, 'cm')._to_bounds()) assert not bounds.contains(NormalReal(5, 0.1, 'm')._to_bounds())
def fake_project(): """Fake project that serves templates from template collection's list_all method.""" templates = [ PropertyTemplate("density", bounds=RealBounds(lower_bound=0, upper_bound=100, default_units="g / cm^3"), uids={"my_scope": "density"}), ConditionTemplate("volume", bounds=IntegerBounds(lower_bound=0, upper_bound=11), uids={"my_scope": "volume"}), ParameterTemplate( "speed", bounds=CategoricalBounds(categories=["slow", "fast"]), uids={}) ] class FakePropertyTemplateCollection(PropertyTemplateCollection): def __init__(self): pass def list_all(self, forward: bool = True, per_page: int = 100) -> Iterator[PropertyTemplate]: return iter( [x for x in templates if isinstance(x, PropertyTemplate)]) class FakeConditionTemplateCollection(ConditionTemplateCollection): def __init__(self): pass def list_all(self, forward: bool = True, per_page: int = 100) -> Iterator[ConditionTemplate]: return iter( [x for x in templates if isinstance(x, ConditionTemplate)]) class FakeParameterTemplateCollection(ParameterTemplateCollection): def __init__(self): pass def list_all(self, forward: bool = True, per_page: int = 100) -> Iterator[ParameterTemplate]: return iter( [x for x in templates if isinstance(x, ParameterTemplate)]) class FakeProject(Project): def __init__(self): pass @property def property_templates(self) -> PropertyTemplateCollection: return FakePropertyTemplateCollection() @property def condition_templates(self) -> ConditionTemplateCollection: return FakeConditionTemplateCollection() @property def parameter_templates(self) -> ParameterTemplateCollection: return FakeParameterTemplateCollection() return FakeProject()
def make_cake_templates(): """Define all templates independently, as in the wild this will be an independent operation.""" tmpl = dict() # Attributes tmpl["Mixer speed setting"] = ParameterTemplate( name="Mixer speed setting", description="What speed setting to use on the mixer", bounds=IntegerBounds(0, 10)) tmpl['Cooking time'] = ConditionTemplate( name="Cooking time", description="The time elapsed during a cooking process", bounds=RealBounds(0, 7 * 24.0, "hr")) tmpl["Oven temperature setting"] = ParameterTemplate( name="Oven temperature setting", description="Where the knob points", bounds=RealBounds(0, 2000.0, "K")) tmpl["Oven temperature"] = ConditionTemplate( name="Oven temperature", description="Actual temperature measured by the thermocouple", bounds=RealBounds(0, 2000.0, "K")) tmpl["Toothpick test"] = PropertyTemplate( name="Toothpick test", description="Results of inserting a toothpick to check doneness", bounds=CategoricalBounds(["wet", "crumbs", "completely clean"])) tmpl["Color"] = PropertyTemplate( name="Baked color", description="Visual observation of the color of a baked good", bounds=CategoricalBounds( ["Pale", "Golden brown", "Deep brown", "Black"])) tmpl["Tastiness"] = PropertyTemplate( name="Tastiness", description="Yumminess on a fairly arbitrary scale", bounds=IntegerBounds(lower_bound=1, upper_bound=10)) tmpl["Nutritional Information"] = PropertyTemplate( name="Nutritional Information", description= "FDA Nutrition Facts, mass basis. Please be attentive to g vs. mg. " "`other-carbohydrate` and `other-fat` are the total values minus the " "broken-out quantities. Other is the difference between the total and the " "serving size.", bounds=CompositionBounds(components=[ 'other', 'saturated-fat', 'trans-fat', 'other-fat', 'cholesterol', 'sodium', 'dietary-fiber', 'sugars', 'other-carbohydrate', 'protein', 'vitamin-d', 'calcium', 'iron', 'potassium' ])) tmpl["Sample Mass"] = ConditionTemplate( name="Sample Mass", description= "Sample size in mass units, to go along with FDA Nutrition Facts", bounds=RealBounds(1.e-3, 1.e4, "g")) tmpl["Expected Sample Mass"] = ParameterTemplate( name="Expected Sample Mass", description= "Specified sample size in mass units, to go along with FDA Nutrition Facts", bounds=RealBounds(1.e-3, 1.e4, "g")) tmpl["Chemical Formula"] = PropertyTemplate( name="Chemical Formula", description="The chemical formula of a material", bounds=CompositionBounds(components=EmpiricalFormula.all_elements())) tmpl["Molecular Structure"] = PropertyTemplate( name="Molecular Structure", description="The molecular structure of the material", bounds=MolecularStructureBounds()) # Objects tmpl["Procuring"] = ProcessTemplate( name="Procuring", description="Buyin' stuff", allowed_names=[] # Takes no ingredients by definition ) tmpl["Baking"] = ProcessTemplate( name="Baking", description='Using heat to promote chemical reactions in a material', allowed_names=['batter'], allowed_labels=['precursor'], conditions=[(tmpl["Oven temperature"], RealBounds(0, 700, "degF"))], parameters=[(tmpl["Oven temperature setting"], RealBounds(100, 550, "degF"))]) tmpl["Icing"] = ProcessTemplate( name="Icing", description='Applying a coating to a substrate', allowed_labels=['coating', 'substrate']) tmpl["Mixing"] = ProcessTemplate( name="Mixing", description='Physically combining ingredients', allowed_labels=[ 'wet', 'dry', 'leavening', 'seasoning', 'sweetener', 'shortening', 'flavoring' ], parameters=[tmpl["Mixer speed setting"]]) tmpl["Generic Material"] = MaterialTemplate(name="Generic") tmpl["Nutritional Material"] = MaterialTemplate( name="Nutritional Material", description="A material with FDA Nutrition Facts attached", properties=[tmpl["Nutritional Information"]]) tmpl["Formulaic Material"] = MaterialTemplate( name="Formulaic Material", description="A material with chemical characterization", properties=[tmpl["Chemical Formula"], tmpl["Molecular Structure"]]) tmpl["Baked Good"] = MaterialTemplate( name="Baked Good", properties=[tmpl["Toothpick test"], tmpl["Color"]]) tmpl["Dessert"] = MaterialTemplate(name="Dessert", properties=[tmpl["Tastiness"]]) tmpl["Doneness"] = MeasurementTemplate( name="Doneness test", description= "An ensemble of tests to determine the doneness of a baked good", properties=[tmpl["Toothpick test"], tmpl["Color"]]) tmpl["Taste test"] = MeasurementTemplate(name="Taste test", properties=[tmpl["Tastiness"]]) tmpl["Nutritional Analysis"] = MeasurementTemplate( name="Nutritional Analysis", properties=[tmpl["Nutritional Information"]], conditions=[tmpl["Sample Mass"]], parameters=[tmpl["Expected Sample Mass"]]) tmpl["Elemental Analysis"] = MeasurementTemplate( name="Elemental Analysis", properties=[tmpl["Chemical Formula"]], conditions=[tmpl["Sample Mass"]], parameters=[tmpl["Expected Sample Mass"]]) for key in tmpl: tmpl[key].add_uid(TEMPLATE_SCOPE, key.lower().replace(' ', '-')) return tmpl
def test_contains(): """Test that bounds know if a Value is contained within it.""" bounds = RealBounds(1, 3, 'm') assert bounds.contains(UniformReal(100, 200, 'cm')._to_bounds()) assert not bounds.contains(UniformReal(3, 5, 'm')._to_bounds()) assert not bounds.contains(UniformReal(1, 3, '')._to_bounds())
def test_build(): """Test builder routines.""" mix_tmpl = ProcessTemplate(name="Mixing") term_tmpl = MaterialTemplate(name="Terminal") procure_tmpl = ProcessTemplate(name="Procure") raw_tmpl = MaterialTemplate(name="Raw") prop_tmpl = PropertyTemplate(name="Property", bounds=RealBounds(0, 10, "m")) cond_tmpl = ConditionTemplate(name="Condition", bounds=CategoricalBounds(["a", "b", "c"])) param_tmpl = ParameterTemplate(name="Parameter", bounds=CompositionBounds( EmpiricalFormula.all_elements())) mol_tmpl = PropertyTemplate(name="Molecule", bounds=MolecularStructureBounds()) int_tmpl = ConditionTemplate(name="Integer", bounds=IntegerBounds(0, 10)) bad_tmpl = PropertyTemplate(name="Bad", bounds=UnsupportedBounds()) root = make_node(name="Root", material_template=term_tmpl, process_template=mix_tmpl) assert root.template == term_tmpl, "Object didn't link correctly." assert root.process.template == mix_tmpl, "Object didn't link correctly." one = make_node("One", material_template=raw_tmpl, process_template=procure_tmpl) add_edge(output_material=root, input_material=one) add_edge(output_material=root, input_material=make_node("Two")) assert len( root.process.ingredients) == 2, "Ingredient count didn't line up." # Attribute tests with pytest.raises(ValueError): add_attribute(one.spec, cond_tmpl, "b") # No property yet # Create a property-and-condition on the mat spec add_attribute(one.spec, prop_tmpl, 1) assert len( one.spec.properties) == 1, "Material spec didn't get a property." assert one.spec.properties[ 0].property.template == prop_tmpl, "Wrong linking on property." assert one.spec.properties[ 0].property.value.units == "meter", "Wrong units on property." assert one.spec.properties[ 0].property.value.nominal == 1, "Wrong value on property." add_attribute(one.spec, cond_tmpl, "b") assert len( one.spec.properties[0].conditions) == 1, "Wrong location on condition." assert one.spec.properties[0].conditions[0].template == cond_tmpl, \ "Wrong linking on condition." assert one.spec.properties[0].conditions[ 0].value.category == "b", "Wrong value on condition." with pytest.raises(ValueError): add_attribute(one.spec, param_tmpl, "H2O") # Mat Specs don't support parameters # Create a second property-and-condition on the mat spec add_attribute(one.spec, mol_tmpl, "C") assert len(one.spec.properties) == 2, "Second property added." add_attribute(one.spec, int_tmpl, 5) assert len(one.spec.properties[-1].conditions ) == 1, "Second property has a condition." # Attach a measurement msr_tmpl = MeasurementTemplate(name="Measure!", properties=[prop_tmpl]) msr = add_measurement(material=root, template=msr_tmpl) assert len(root.measurements) == 1, "Measurement was added to root." add_attribute(msr, cond_tmpl, "c") # Order shouldn't matter anymore assert len(msr.conditions) == 1, "Condition wasn't added to measurement." add_attribute(msr, prop_tmpl, 5) assert len(msr.properties) == 1, "Property wasn't added to measurement." add_attribute(msr, param_tmpl, "CH4") assert len(msr.parameters) == 1, "Parameter wasn't added to measurement." assert msr.parameters[ 0].template == param_tmpl, "Wrong linking on parameter." assert msr.parameters[ 0].value.formula == "CH4", "Wrong value on parameter." # Test failed builds with pytest.raises(ValueError): add_measurement(material=root) assert len(root.measurements ) == 1, "Failed measurement build still added an object." with pytest.raises(ValueError): add_attribute(msr, bad_tmpl, "Word") assert len( msr.properties) == 1, "Failed attribute build still added an object." with pytest.raises(ValueError): add_attribute(root.process, prop_tmpl, 9) assert len(root.process.conditions ) == 0, "Failed attribute build still added an object." assert len(root.process.parameters ) == 0, "Failed attribute build still added an object."