Exemple #1
0
def test_material_spec():
    """Test that Process/Material Spec link survives serialization."""
    # Create a ProcessSpec
    proc_spec = ProcessSpec(name="a process spec", tags=["tag1", "tag2"])

    # Create MaterialSpec without a ProcessSpec
    prop = Property(
        name="The material is a solid",
        value=DiscreteCategorical(probabilities="solid")
    )
    mat_spec = MaterialSpec(name="a material spec", properties=PropertyAndConditions(prop))
    assert mat_spec.process is None, \
        "MaterialSpec should be initialized with no ProcessSpec, by default"

    # Assign a ProcessSpec to mat_spec, first ensuring that the type is enforced
    with pytest.raises(TypeError):
        mat_spec.process = 17
    mat_spec.process = proc_spec

    # Assert circular links
    assert dumps(proc_spec.output_material.process) == dumps(proc_spec), \
        "ProcessSpec should link to MaterialSpec that links back to itself"

    assert dumps(mat_spec.process.output_material) == dumps(mat_spec), \
        "MaterialSpec should link to ProcessSpec that links back to itself"

    # Make copies of both specs
    mat_spec_copy = loads(dumps(mat_spec))
    proc_spec_copy = loads(dumps(proc_spec))

    assert proc_spec_copy.output_material == mat_spec, \
        "Serialization should preserve link from ProcessSpec to MaterialSpec"

    assert mat_spec_copy.process == proc_spec, \
        "Serialization should preserve link from MaterialSpec to ProcessSpec"
def test_process_reassignment():
    """Test that a material can be assigned to a new process."""
    drying = ProcessSpec("drying")
    welding = ProcessSpec("welding")
    powder = MaterialSpec("Powder", process=welding)

    assert powder.process == welding
    assert welding.output_material == powder

    powder.process = drying
    assert powder.process == drying
    assert drying.output_material == powder
    assert welding.output_material is None
Exemple #3
0
def test_circular_crawl():
    """Test that make_instance can handle a circular set of linked objects."""
    proc = ProcessSpec("process name")
    mat = MaterialSpec("material name", process=proc)
    IngredientSpec(name="ingredient name", material=mat, process=proc)
    mat_run = make_instance(mat)
    assert mat_run == mat_run.process.ingredients[0].material
Exemple #4
0
def test_serialized_history():
    """Test the serialization of a complete material history."""
    # Create several runs and specs linked together
    buy_spec = LinkByUID("id", "pr723")
    cookie_dough_spec = MaterialSpec("cookie dough spec", process=buy_spec)
    buy_cookie_dough = ProcessRun("Buy cookie dough", uids={'id': '32283'}, spec=buy_spec)
    cookie_dough = MaterialRun("cookie dough", process=buy_cookie_dough, spec=cookie_dough_spec)
    bake = ProcessRun("bake cookie dough", conditions=[
        Condition("oven temp", origin='measured', value=NominalReal(357, 'degF'))])
    IngredientRun(material=cookie_dough,
                  process=bake, number_fraction=NominalReal(1, ''))
    cookie = MaterialRun("cookie", process=bake, tags=["chocolate chip", "drop"])
    MeasurementRun("taste", material=cookie, properties=[
        Property("taste", value=DiscreteCategorical("scrumptious"))])

    cookie_history = complete_material_history(cookie)
    # There are 7 entities in the serialized list: cookie dough (spec & run), buy cookie dough,
    # cookie dough ingredient, bake cookie dough, cookie, taste
    assert len(cookie_history) == 7
    for entity in cookie_history:
        assert len(entity['uids']) > 0, "Serializing material history should assign uids."

    # Check that the measurement points to the material
    taste_dict = next(x for x in cookie_history if x.get('type') == 'measurement_run')
    cookie_dict = next(x for x in cookie_history if x.get('name') == 'cookie')
    scope = taste_dict.get('material').get('scope')
    assert taste_dict.get('material').get('id') == cookie_dict.get('uids').get(scope)

    # Check that both the material spec and the process run point to the same process spec.
    # Because that spec was initially a LinkByUID, this also tests the methods ability to
    # serialize a LinkByUID.
    cookie_dough_spec_dict = next(x for x in cookie_history if x.get('type') == 'material_spec')
    buy_cookie_dough_dict = next(x for x in cookie_history if x.get('name') == 'Buy cookie dough')
    assert cookie_dough_spec_dict.get('process') == buy_spec.as_dict()
    assert buy_cookie_dough_dict.get('spec') == buy_spec.as_dict()
Exemple #5
0
def test_ingredient_spec():
    """Tests that a process can house an ingredient, and that pairing survives serialization."""
    # Create a ProcessSpec
    proc_spec = ProcessSpec(name="a process spec", tags=["tag1", "tag2"])
    IngredientSpec(name='Input', material=MaterialSpec(name='Raw'), process=proc_spec)

    # Make copies of both specs
    proc_spec_copy = loads(dumps(proc_spec))

    assert proc_spec_copy == proc_spec, "Full structure wasn't preserved across serialization"
 def __init__(self,
              name: str,
              uids: Optional[Dict[str, str]] = None,
              tags: Optional[List[str]] = None,
              notes: Optional[str] = None,
              process: Optional[TaurusProcessSpec] = None,
              properties: Optional[List[PropertyAndConditions]] = None,
              template: Optional[TaurusMaterialTemplate] = None,
              file_links: Optional[List[FileLink]] = None):
     DataConcepts.__init__(self, TaurusMaterialSpec.typ)
     TaurusMaterialSpec.__init__(self,
                                 name=name,
                                 uids=set_default_uid(uids),
                                 tags=tags,
                                 process=process,
                                 properties=properties,
                                 template=template,
                                 file_links=file_links,
                                 notes=notes)
Exemple #7
0
def test_make_instance():
    """Build up several linked objects and test their properties."""
    msr_spec = MeasurementSpec()
    assert isinstance(make_instance(msr_spec), MeasurementRun)

    mat_spec = MaterialSpec(name='Mat name')
    mat_spec.process = ProcessSpec(name='Pro name')
    IngredientSpec(name='Ing label', process=mat_spec.process)
    mat_spec.process.ingredients[0].material = MaterialSpec(name='Baby mat name')

    mat_run = make_instance(mat_spec)
    assert isinstance(mat_run, MaterialRun)
    assert isinstance(mat_run.process, ProcessRun)
    assert isinstance(mat_run.process.ingredients[0], IngredientRun)
    assert isinstance(mat_run.process.ingredients[0].material, MaterialRun)

    assert mat_run.process.spec == mat_run.spec.process
    ingredient = mat_run.process.ingredients[0]
    assert ingredient.spec == mat_run.spec.process.ingredients[0]
    assert ingredient.material.spec == mat_run.spec.process.ingredients[0].material
Exemple #8
0
def make_strehlow_objects(table=None):
    """Make a table with Strehlow & Cook data."""
    tmpl = make_templates()

    if table is None:
        table = import_table()

    # Specs
    msr_spec = MeasurementSpec(name='Band gap',
                               template=tmpl["Band gap measurement"])

    def real_mapper(prop):
        """Mapping methods for RealBounds."""
        if 'uncertainty' in prop['scalars'][0]:
            val = NormalReal(mean=float(prop['scalars'][0]['value']),
                             units=prop['units'],
                             std=float(prop['scalars'][0]['uncertainty']))
        else:
            val = NominalReal(nominal=float(prop['scalars'][0]['value']),
                              units=prop['units'])
        return val

    content_map = {
        RealBounds:
        real_mapper,
        CategoricalBounds:
        lambda prop: NominalCategorical(category=prop['scalars'][0]['value']),
        type(None):
        lambda bnd: 'Label'
    }

    datapoints = []
    compounds = dict()
    for row in table:
        formula = formula_clean(row['chemicalFormula'])
        spec = compounds.get(
            formula,
            MaterialSpec(name=formula_latex(formula),
                         template=tmpl["Chemical"],
                         process=ProcessSpec(
                             name="Sample preparation",
                             template=tmpl["Sample preparation"])))
        run = make_instance(spec)
        datapoints.append(run)

        if not spec.properties:
            spec.properties.append(
                PropertyAndConditions(property=Property(
                    name=spec.template.properties[0][0].name,
                    value=EmpiricalFormula(formula=formula),
                    template=spec.template.properties[0][0])))

        msr = make_instance(msr_spec)
        msr.material = run

        # 2 categories in the PIF need to be split to avoid repeat Attribute Templates in a Run
        name_map = {'Phase': 'Crystal system', 'Transition': 'Bands'}
        origin_map = {
            'EXPERIMENTAL': Origin.MEASURED,
            'COMPUTATIONAL': Origin.COMPUTED
        }
        seen = set(
        )  # Some conditions come in from multiple properties on the same object
        for prop in row['properties']:
            origin = origin_map.get(prop.get('dataType', None), Origin.UNKNOWN)
            if 'method' in prop:
                method = 'Method: ' + prop['method']['name']
            else:
                method = 'Method: unreported'
            for attr in [prop] + prop.get('conditions', []):
                if attr['name'] in seen:
                    # Early return if it's a repeat
                    continue
                seen.add(attr['name'])

                template = tmpl[attr['name']]
                # Figure out if we need to split this column
                if attr['name'] in name_map:
                    value = attr['scalars'][0]['value']
                    if value not in template.bounds.categories:
                        template = tmpl[name_map[attr['name']]]

                # Move into GEMD structure
                if type(template) == PropertyTemplate:
                    msr.properties.append(
                        Property(name=template.name,
                                 template=template,
                                 value=content_map[type(
                                     template.bounds)](attr),
                                 origin=origin,
                                 notes=method))
                elif type(template) == ConditionTemplate:
                    msr.conditions.append(
                        Condition(name=template.name,
                                  template=template,
                                  value=content_map[type(
                                      template.bounds)](attr),
                                  origin=origin,
                                  notes=method))

    return datapoints
def test_invalid_assignment():
    """Invalid assignments to `process` or `template` throw a TypeError."""
    with pytest.raises(TypeError):
        MaterialSpec("name", process=["Process 1", "Process 2"])
    with pytest.raises(TypeError):
        MaterialSpec("name", template=MaterialSpec("another spec"))
Exemple #10
0
def make_cake_spec():
    """Define a recipe for making a cake."""
    ###############################################################################################
    # Templates
    tmpl = make_cake_templates()

    ###############################################################################################
    # Objects
    cake = MaterialSpec(
        name="Abstract Cake",
        template=tmpl["Dessert"],
        process=ProcessSpec(
            name='Icing, in General',
            template=tmpl["Icing"],
            tags=[
                'spreading'
            ],
            notes='The act of covering a baked output with frosting'
        ),
        file_links=FileLink(
            filename="Becky's Butter Cake",
            url='https://www.landolakes.com/recipe/16730/becky-s-butter-cake/'
        ),
        tags=[
            'cake::butter cake',
            'dessert::baked::cake',
            'iced::chocolate'
        ],
        notes='Butter cake recipe reminiscent of the 1-2-3-4 cake that Grandma may have baked.'
    )

    ########################
    frosting = MaterialSpec(
        name="Abstract Frosting",
        template=tmpl["Dessert"],
        process=ProcessSpec(
            name='Mixing Frosting, in General',
            template=tmpl["Mixing"],
            tags=[
                'mixing'
            ],
            notes='Combining ingredients to make a sweet frosting'
        ),
        tags=[
            'frosting::chocolate',
            'topping::chocolate'
        ],
        notes='Chocolate frosting'
    )
    IngredientSpec(
        name="{} input".format(frosting.name),
        tags=list(frosting.tags),
        notes='Seems like a lot of frosting',
        labels=['coating'],
        process=cake.process,
        material=frosting,
        absolute_quantity=NominalReal(nominal=0.751, units='kg')
    )

    baked_cake = MaterialSpec(
        name="Abstract Baked Cake",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Baking, in General',
            template=tmpl["Baking in an oven"],
            tags=[
                'oven::baking'
            ],
            notes='Using heat to convert batter into a solid matrix'
        ),
        tags=[
        ],
        notes='The cakey part of the cake'
    )
    IngredientSpec(
        name="{} input".format(baked_cake.name),
        tags=list(baked_cake.tags),
        labels=['substrate'],
        process=cake.process,
        material=baked_cake
    )

    ########################
    batter = MaterialSpec(
        name="Abstract Batter",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Mixing Batter, in General',
            template=tmpl["Mixing"],
            tags=[
                'mixing'
            ],
            notes='Combining ingredients to make a baking feedstock'
        ),
        tags=[
        ],
        notes='The fluid that converts to cake with heat'
    )
    IngredientSpec(
        name="{} input".format(batter.name),
        tags=list(batter.tags),
        labels=['precursor'],
        process=baked_cake.process,
        material=batter
    )

    ########################
    wetmix = MaterialSpec(
        name="Abstract Wet Mix",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Mixing Wet, in General',
            template=tmpl["Mixing"],
            tags=[
                'mixing'
            ],
            notes='Combining wet ingredients to make a baking feedstock'
        ),
        tags=[
        ],
        notes='The wet fraction of a batter'
    )
    IngredientSpec(
        name="{} input".format(wetmix.name),
        tags=list(wetmix.tags),
        labels=['wet'],
        process=batter.process,
        material=wetmix
    )

    drymix = MaterialSpec(
        name="Abstract Dry Mix",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Mixing Dry, in General',
            template=tmpl["Mixing"],
            tags=[
                'mixing'
            ],
            notes='Combining dry ingredients to make a baking feedstock'
        ),
        tags=[
        ],
        notes='The dry fraction of a batter'
    )
    IngredientSpec(
        name="{} input".format(drymix.name),
        tags=list(drymix.tags),
        labels=['dry'],
        process=batter.process,
        material=drymix,
        absolute_quantity=NominalReal(nominal=3.052, units='cups')
    )

    ########################
    flour = MaterialSpec(
        name="Abstract Flour",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Flour, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::dry-goods'
            ],
            notes='Purchasing all purpose flour'
        ),
        tags=[
        ],
        notes='All-purpose flour'
    )
    IngredientSpec(
        name="{} input".format(flour.name),
        tags=list(flour.tags),
        labels=['dry'],
        process=drymix.process,
        material=flour,
        volume_fraction=NominalReal(nominal=0.9829, units='')  # 3 cups
    )

    baking_powder = MaterialSpec(
        name="Abstract Baking Powder",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Baking Powder, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::dry-goods'
            ],
            notes='Purchasing baking powder'
        ),
        tags=[
        ],
        notes='Leavening agent for cake'
    )
    IngredientSpec(
        name="{} input".format(baking_powder.name),
        tags=list(baking_powder.tags),
        labels=['leavening', 'dry'],
        process=drymix.process,
        material=baking_powder,
        volume_fraction=NominalReal(nominal=0.0137, units='')  # 2 teaspoons
    )

    salt = MaterialSpec(
        name="Abstract Salt",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Salt, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::dry-goods'
            ],
            notes='Purchasing salt'
        ),
        tags=[
        ],
        notes='Plain old NaCl'
    )
    IngredientSpec(
        name="{} input".format(salt.name),
        tags=list(salt.tags),
        labels=['dry', 'seasoning'],
        process=drymix.process,
        material=salt,
        volume_fraction=NominalReal(nominal=0.0034, units='')  # 1/2 teaspoon
    )

    sugar = MaterialSpec(
        name="Abstract Sugar",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Sugar, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::dry-goods'
            ],
            notes='Purchasing all purpose flour'
        ),
        tags=[
        ],
        notes='Sugar'
    )
    IngredientSpec(
        name="{} input".format(sugar.name),
        tags=list(sugar.tags),
        labels=['wet', 'sweetener'],
        process=wetmix.process,
        material=sugar,
        absolute_quantity=NominalReal(nominal=2, units='cups')
    )

    butter = MaterialSpec(
        name="Abstract Butter",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Butter, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::produce'
            ],
            notes='Purchasing butter'
        ),
        tags=[
        ],
        notes='Shortening for making rich, buttery baked goods'
    )
    IngredientSpec(
        name="{} input".format(butter.name),
        tags=list(butter.tags),
        labels=['wet', 'shortening'],
        process=wetmix.process,
        material=butter,
        absolute_quantity=NominalReal(nominal=1, units='cups')
    )
    IngredientSpec(
        name="{} input".format(butter.name),
        tags=list(butter.tags),
        labels=['shortening'],
        process=frosting.process,
        material=butter,
        mass_fraction=NominalReal(nominal=0.1434, units='')  # 1/2 c @ 0.911 g/cc
    )

    eggs = MaterialSpec(
        name="Abstract Eggs",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Eggs, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::produce'
            ],
            notes='Purchasing eggs'
        ),
        tags=[
        ],
        notes=''
    )
    IngredientSpec(
        name="{} input".format(eggs.name),
        tags=list(eggs.tags),
        labels=['wet'],
        process=wetmix.process,
        material=eggs,
        absolute_quantity=NominalReal(nominal=4, units='')
    )

    vanilla = MaterialSpec(
        name="Abstract Vanilla",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Vanilla, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::dry-goods'
            ],
            notes='Purchasing vanilla'
        ),
        tags=[
        ],
        notes=''
    )
    IngredientSpec(
        name="{} input".format(vanilla.name),
        tags=list(vanilla.tags),
        labels=['wet', 'flavoring'],
        process=wetmix.process,
        material=vanilla,
        absolute_quantity=NominalReal(nominal=2, units='teaspoons')
    )
    IngredientSpec(
        name="{} input".format(vanilla.name),
        tags=list(vanilla.tags),
        labels=['flavoring'],
        process=frosting.process,
        material=vanilla,
        mass_fraction=NominalReal(nominal=0.0231, units='')  # 2 tsp @ 0.879 g/cc
    )

    milk = MaterialSpec(
        name="Abstract Milk",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Milk, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::produce'
            ],
            notes='Purchasing milk'
        ),
        tags=[
        ],
        notes=''
    )
    IngredientSpec(
        name="{} input".format(milk.name),
        tags=list(milk.tags),
        labels=['wet'],
        process=batter.process,
        material=milk,
        absolute_quantity=NominalReal(nominal=1, units='cup')
    )
    IngredientSpec(
        name="{} input".format(milk.name),
        tags=list(milk.tags),
        labels=[],
        process=frosting.process,
        material=milk,
        mass_fraction=NominalReal(nominal=0.0816, units='')  # 1/4 c @ 1.037 g/cc
    )

    chocolate = MaterialSpec(
        name="Abstract Chocolate",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Chocolate, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::dry-goods'
            ],
            notes='Purchasing chocolate'
        ),
        tags=[
        ],
        notes=''
    )
    IngredientSpec(
        name="{} input".format(chocolate.name),
        tags=list(chocolate.tags),
        labels=['flavoring'],
        process=frosting.process,
        material=chocolate,
        mass_fraction=NominalReal(nominal=0.1132, units='')  # 3 oz.
    )

    powder_sugar = MaterialSpec(
        name="Abstract Powdered Sugar",
        template=tmpl["Generic Material"],
        process=ProcessSpec(
            name='Buying Powdered Sugar, in General',
            template=tmpl["Procurement"],
            tags=[
                'purchase::dry-goods'
            ],
            notes='Purchasing powdered sugar'
        ),
        tags=[
        ],
        notes='Granulated sugar mixed with corn starch'
    )
    IngredientSpec(
        name="{} input".format(powder_sugar.name),
        tags=list(powder_sugar.tags),
        labels=['flavoring'],
        process=frosting.process,
        material=powder_sugar,
        mass_fraction=NominalReal(nominal=0.6387, units='')  # 4 c @ 30 g/ 0.25 cups
    )
    return cake