Example #1
0
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
Example #3
0
    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
Example #5
0
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()
Example #7
0
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."