예제 #1
0
def test_complex_substitutions():
    """Make sure accounting works for realistic objects."""
    root = MaterialRun("root",
                       process=ProcessRun("root", spec=ProcessSpec("root")),
                       spec=MaterialSpec("root"))
    root.spec.process = root.process.spec
    input = MaterialRun("input",
                        process=ProcessRun("input", spec=ProcessSpec("input")),
                        spec=MaterialSpec("input"))
    input.spec.process = input.process.spec
    IngredientRun(process=root.process,
                  material=input,
                  spec=IngredientSpec("ingredient",
                                      process=root.process.spec,
                                      material=input.spec))
    param = ParameterTemplate("Param", bounds=RealBounds(-1, 1, "m"))
    root.process.spec.template = ProcessTemplate("Proc", parameters=[param])
    root.process.parameters.append(
        Parameter("Param", value=NormalReal(0, 1, 'm'), template=param))

    links = flatten(root, scope="test-scope")
    index = make_index(links)
    rebuild = substitute_objects(links, index, inplace=True)
    rebuilt_root = next(x for x in rebuild
                        if x.name == root.name and x.typ == root.typ)
    all_objs = recursive_flatmap(rebuilt_root,
                                 func=lambda x: [x],
                                 unidirectional=False)
    unique = [x for i, x in enumerate(all_objs) if i == all_objs.index(x)]
    assert not any(isinstance(x, LinkByUID) for x in unique), "All are objects"
    assert len(links) == len(unique), "Objects are missing"
예제 #2
0
def test_scope_control():
    """Serializing a nested object should be identical to individually serializing each piece."""
    input_material = MaterialSpec()
    process = ProcessSpec()
    IngredientSpec(material=input_material, process=process)
    material = MaterialSpec(process=process)

    # Verify the default scope is there
    default_json = GEMDJson()
    default_text = default_json.dumps(material)
    assert "auto" in default_text
    assert "custom" not in default_text

    # Clear out ids
    input_material.uids = {}
    process.uids = {}
    process.ingredients[0].uids = {}
    input_material.uids = {}
    material.uids = {}

    # Verify the default scope is there
    custom_json = GEMDJson(scope='custom')
    custom_text = custom_json.dumps(material)
    assert "auto" not in custom_text
    assert "custom" in custom_text
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
예제 #4
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"
예제 #5
0
def test_default_scope():
    """Test flatten exceptions around providing a scope."""
    ps_one = ProcessSpec(name="one")

    with pytest.raises(ValueError):
        flatten(ps_one)

    pr_one = ProcessRun(name="one", uids={'my': 'outer'}, spec=ps_one)

    with pytest.raises(ValueError):
        flatten(pr_one)

    ps_one.uids['my'] = 'id'
    assert len(flatten(pr_one)) == 2

    two = ProcessRun(name="two", spec=ProcessSpec(name="two"))
    assert len(flatten(two, scope="my")) == 2
예제 #6
0
def test_template_access():
    """A process run's template should be equal to its spec's template."""
    template = ProcessTemplate("process template", uids={'id': str(uuid4())})
    spec = ProcessSpec("A spec", uids={'id': str(uuid4())}, template=template)
    proc = ProcessRun("A run", uids={'id': str(uuid4())}, spec=spec)
    assert proc.template == template

    proc.spec = LinkByUID.from_entity(spec)
    assert proc.template is None
def test_dependencies():
    """Test that dependency lists make sense."""
    ps = ProcessSpec(name="ps")
    pr = ProcessRun(name="pr", spec=ps)
    ms = MaterialSpec(name="ms", process=ps)
    mr = MaterialRun(name="mr", spec=ms, process=pr)

    assert ps not in mr.all_dependencies()
    assert pr in mr.all_dependencies()
    assert ms in mr.all_dependencies()
예제 #8
0
def test_equality():
    """Test that equality check works as expected."""
    spec1 = ProcessSpec("A spec")
    spec2 = ProcessSpec("A spec", tags=["a tag"])

    assert spec1 != spec2
    spec3 = deepcopy(spec1)
    assert spec1 == spec3, "Copy somehow failed"
    IngredientSpec("An ingredient", process=spec3)
    assert spec1 != spec3

    spec4 = deepcopy(spec3)
    assert spec4 == spec3, "Copy somehow failed"
    spec4.ingredients[0].tags.append('A tag')
    assert spec4 != spec3

    spec5 = next(x for x in flatten(spec4, 'test-scope')
                 if isinstance(x, ProcessSpec))
    assert spec5 == spec4, "Flattening removes measurement references, but that's okay"
예제 #9
0
def make_node(name: str,
              *,
              process_name: str = None,
              process_template: ProcessTemplate = None,
              material_template: MaterialTemplate = None) -> MaterialRun:
    """
    Generate a material-process spec-run quadruple.

    Parameters
    ----------
    name: str
        Name of the MaterialRun and MaterialSpec.
    process_name: str
        Name of the ProcessRun and ProcessSpec.  Defaults to
        `process_template.name` if `process_template` is defined, else `name`.
    process_template: ProcessTemplate
        ProcessTemplate for the quadruple.
    material_template: MaterialTemplate
        MaterialTemplate for the quadruple.

    Returns
    --------
    MaterialRun
        A MaterialRun with linked processes, specs and templates

    """
    if process_name is None:
        if process_template is None:
            process_name = name
        else:
            process_name = process_template.name

    my_process_spec = ProcessSpec(
        name=process_name,
        template=process_template
    )

    my_process_run = ProcessRun(
        name=process_name,
        spec=my_process_spec
    )

    my_mat_spec = MaterialSpec(
        name=name,
        process=my_process_spec,
        template=material_template
    )

    my_mat_run = MaterialRun(
        name=name,
        process=my_process_run,
        spec=my_mat_spec
    )
    return my_mat_run
예제 #10
0
def test_substitute_equivalence():
    """PLA-6423: verify that substitutions match up."""
    spec = ProcessSpec(name="old spec", uids={'scope': 'spec'})
    run = ProcessRun(name="old run",
                     uids={'scope': 'run'},
                     spec=LinkByUID(id='spec', scope="scope"))

    # make a dictionary from ids to objects, to be used in substitute_objects
    gem_index = make_index([run, spec])
    substitute_objects(obj=run, index=gem_index, inplace=True)
    assert spec == run.spec
예제 #11
0
def test_flatten_bounds():
    """Test that flatten works when the objects contain other objects."""
    bounds = CategoricalBounds(categories=["foo", "bar"])
    template = ProcessTemplate(
        "spam",
        conditions=[(ConditionTemplate(name="eggs", bounds=bounds), bounds)]
    )
    spec = ProcessSpec(name="spec", template=template)

    flat = flatten(spec, 'test-scope')
    assert len(flat) == 2, "Expected 2 flattened objects"
예제 #12
0
def test_inplace_v_not():
    """Test that client can copy a dictionary in which keys are BaseEntity objects."""
    spec = ProcessSpec("A process spec", uids={'id': str(uuid4()), 'auto': str(uuid4())})
    run1 = ProcessRun("A process run", spec=spec, uids={'id': str(uuid4()), 'auto': str(uuid4())})
    run2 = ProcessRun("Another process run", spec=spec, uids={'id': str(uuid4())})
    process_dict = {spec: [run1, run2]}

    subbed = substitute_links(process_dict)
    assert subbed != process_dict  # This is true because the hashes change, even if objects equal
    substitute_links(process_dict, inplace=True)
    assert subbed == process_dict
예제 #13
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"
예제 #14
0
def test_flatten_empty_history():
    """Test that flatten works when the objects are empty and go through a whole history."""
    procured = ProcessSpec(name="procured")
    input = MaterialSpec(name="foo", process=procured)
    transform = ProcessSpec(name="transformed")
    ingredient = IngredientSpec(name="input", material=input, process=transform)

    procured_run = ProcessRun(name="procured", spec=procured)
    input_run = MaterialRun(name="foo", process=procured_run, spec=input)
    transform_run = ProcessRun(name="transformed", spec=transform)
    ingredient_run = IngredientRun(material=input_run, process=transform_run, spec=ingredient)

    assert len(flatten(procured)) == 1
    assert len(flatten(input)) == 1
    assert len(flatten(ingredient)) == 3
    assert len(flatten(transform)) == 3

    assert len(flatten(procured_run)) == 3
    assert len(flatten(input_run)) == 3
    assert len(flatten(ingredient_run)) == 7
    assert len(flatten(transform_run)) == 7
예제 #15
0
def test_repeated_objects():
    """Test that objects aren't double counted."""
    ct = ConditionTemplate(name="color",
                           bounds=CategoricalBounds(categories=["black", "white"]))
    pt = ProcessTemplate(name="painting", conditions=[ct])
    ps = ProcessSpec(name='painting',
                     template=pt,
                     conditions=Condition(name='Paint color',
                                          value=NominalCategorical("black"),
                                          template=ct
                                          )
                     )
    assert len(recursive_flatmap(ps, lambda x: [x])) == 3
예제 #16
0
def test_template_check_generator():
    """Verify that the generator throws exceptions."""
    spec1 = ProcessSpec("A spec")
    with pytest.raises(ValueError):  # Can't find class
        spec1._generate_template_check(validate=lambda x, y: True)

    with pytest.raises(ValueError):  # Can't find attribute
        spec1._generate_template_check(validate=ProcessSpec.name.fget)
예제 #17
0
 def _make_material(*, material_name, template, process_tmpl_name,
                    process_kwargs, **material_kwargs):
     """Convenience method to reuse material name in creating a material's arguments."""
     process_name = "{} {}".format(process_tmpl_name, material_name)
     return MaterialSpec(
         name=material_name,
         uids={DEMO_SCOPE: material_name.lower().replace(' ', '-')},
         template=template,
         process=ProcessSpec(
             name=process_name,
             uids={DEMO_SCOPE: process_name.lower().replace(' ', '-')},
             template=tmpl[process_tmpl_name],
             **process_kwargs),
         **material_kwargs)
예제 #18
0
def test_many_ingredients():
    """Test that ingredients remain connected to processes when round-robined through json."""
    proc = ProcessRun("foo", spec=ProcessSpec("sfoo"))
    expected = []
    for i in range(10):
        mat = MaterialRun(name=str(i), spec=MaterialSpec("s{}".format(i)))
        i_spec = IngredientSpec(name="i{}".format(i), material=mat.spec, process=proc.spec)
        IngredientRun(process=proc, material=mat, spec=i_spec)
        expected.append("i{}".format(i))

    reloaded = loads(dumps(proc))
    assert len(list(reloaded.ingredients)) == 10
    names = [x.name for x in reloaded.ingredients]
    assert sorted(names) == sorted(expected)
예제 #19
0
def test_process_spec():
    """Tests that the Process Spec/Run connection persists when serializing."""
    # Create the ProcessSpec
    condition1 = Condition(name="a condition on the process in general")
    spec = ProcessSpec("Spec", conditions=condition1)

    # Create the ProcessRun with a link to the ProcessSpec from above
    condition2 = Condition(
        name="a condition on this process run in particular")
    process = ProcessRun("Run", conditions=condition2, spec=spec)

    copy_process = loads(dumps(process))
    assert dumps(copy_process.spec) == dumps(spec), \
        "Process spec should be preserved through serialization"
예제 #20
0
def test_register_classes_override():
    """Test that register_classes overrides existing entries in the class index."""
    class MyProcessSpec(ProcessSpec):
        pass

    normal = GEMDJson()
    custom = GEMDJson()
    custom.register_classes({MyProcessSpec.typ: MyProcessSpec})

    obj = ProcessSpec(name="foo")
    assert not isinstance(normal.copy(obj), MyProcessSpec),\
        "Class registration bled across GEMDJson() objects"

    assert isinstance(custom.copy(obj), ProcessSpec),\
        "Custom GEMDJson didn't deserialize as MyProcessSpec"
예제 #21
0
def test_object_key_substitution():
    """Test that client can copy a dictionary in which keys are BaseEntity objects."""
    spec = ProcessSpec("A process spec", uids={'id': str(uuid4()), 'auto': str(uuid4())})
    run1 = ProcessRun("A process run", spec=spec, uids={'id': str(uuid4()), 'auto': str(uuid4())})
    run2 = ProcessRun("Another process run", spec=spec, uids={'id': str(uuid4())})
    process_dict = {spec: [run1, run2]}

    subbed = substitute_links(process_dict, scope='auto')
    for key, value in subbed.items():
        assert key == LinkByUID.from_entity(spec, scope='auto')
        assert LinkByUID.from_entity(run1, scope='auto') in value
        assert LinkByUID.from_entity(run2) in value

    reverse_process_dict = {run2: spec}
    subbed = substitute_links(reverse_process_dict, scope='auto')
    for key, value in subbed.items():
        assert key == LinkByUID.from_entity(run2)
        assert value == LinkByUID.from_entity(spec, scope='auto')
예제 #22
0
def test_equality():
    """Test that equality check works as expected."""
    spec = ProcessSpec("A spec", tags=["a tag"])
    run1 = ProcessRun("A process", spec=spec)

    run2 = deepcopy(run1)
    assert run1 == run2, "Copy somehow failed"
    IngredientRun(process=run2)
    assert run1 != run2

    run3 = deepcopy(run2)
    assert run3 == run2, "Copy somehow failed"
    run3.ingredients[0].tags.append('A tag')
    assert run3 != run2

    run4 = next(x for x in flatten(run3, 'test-scope')
                if isinstance(x, ProcessRun))
    assert run4 == run3, "Flattening removes measurement references, but that's okay"
def test_make_index():
    """Test functionality of make_index method."""
    ps1 = ProcessSpec(name="hello", uids={"test_scope": "test_value"})
    pr1 = ProcessRun(
        name="world",
        spec=LinkByUID(scope="test_scope", id="test_value"),
        uids={"test_scope": "another_test_value",
              "other_test": "also_valid"
              },
    )
    ms1 = MaterialSpec(
        name="material",
        process=LinkByUID(scope="test_scope", id="test_value"),
        uids={"second_scope": "this_is_an_id"},
    )
    mr1 = MaterialRun(
        name="material_run",
        spec=LinkByUID(scope="second_scope", id="this_is_an_id"),
        process=LinkByUID(scope="test_scope", id="another_test_value"),
    )

    pr2 = ProcessRun(
        name="goodbye",
        spec=LinkByUID.from_entity(ps1),
        uids={"test_scope": "the_other_value"},
    )
    mr2 = MaterialRun(
        name="cruel",
        spec=LinkByUID.from_entity(ms1),
        process=LinkByUID.from_entity(pr2),
    )

    gems = [ps1, pr1, ms1, mr1, pr2, mr2]
    gem_index = make_index(gems)
    for gem in gems:
        for scope in gem.uids:
            assert (scope, gem.uids[scope]) in gem_index
            assert gem_index[(scope, gem.uids[scope])] == gem

    # Make sure substitute_objects can consume the index
    subbed = substitute_objects(mr1, gem_index)
    assert subbed.spec.uids == ms1.uids
예제 #24
0
def test_signature():
    """Exercise various permutations of the substitute_links sig."""
    spec = ProcessSpec("A process spec", uids={'my': 'spec'})

    with pytest.warns(DeprecationWarning):
        run1 = ProcessRun("First process run", uids={'my': 'run1'}, spec=spec)
        assert isinstance(substitute_links(run1, native_uid='my').spec, LinkByUID)

    run2 = ProcessRun("Second process run", uids={'my': 'run2'}, spec=spec)
    assert isinstance(substitute_links(run2, scope='my').spec, LinkByUID)

    run3 = ProcessRun("Third process run", uids={'my': 'run3'}, spec=spec)
    assert isinstance(substitute_links(run3, 'my').spec, LinkByUID)

    with pytest.raises(ValueError):  # Test deprecated auto-population
        run4 = ProcessRun("Fourth process run", uids={'my': 'run4'}, spec=spec)
        assert isinstance(substitute_links(run4, 'other', allow_fallback=False).spec, LinkByUID)

    with pytest.warns(DeprecationWarning):
        with pytest.raises(ValueError):  # Test deprecated auto-population
            run5 = ProcessRun("Fifth process run", uids={'my': 'run4'}, spec=spec)
            assert isinstance(substitute_links(run5, scope="my", native_uid="my").spec, LinkByUID)
예제 #25
0
def test_equality():
    """Test that __eq__ and _cached_equals behave as expected."""
    from gemd.entity.object import ProcessSpec, IngredientSpec, MaterialSpec
    from gemd.entity.link_by_uid import LinkByUID

    one = ProcessSpec("Object", tags=["tags!"], uids={"scope": "id"})
    assert one == LinkByUID(scope="scope", id="id"), "Objects equal their links"
    assert one == ("scope", "id"), "Objects equal their equivalent tuples"
    assert one != ("scope", "id", "extra"), "But not if they are too long"
    assert one != ("epocs", "id"), "Or have the wrong scope"
    assert one != ("scope", "di"), "Or have the wrong id"

    junk = MaterialSpec("Object", tags=["tags!"], uids={"scope": "id"})
    assert one != junk, "Objects don't match across types"

    two = ProcessSpec("Object", tags=["tags!"], uids={"scope": "id"}, notes="Notes!")
    assert one != two, "Objects don't match unless the fields do"
    one.notes = "Notes!"
    assert one == two, "And then they will"

    # It ignores the ingredient length mismatch if the uids matched
    one_ing = IngredientSpec("Ingredient", process=one)
    assert one == two
    assert two == one

    # And a cycle is not a problem
    one_mat = MaterialSpec("Material", tags=["other tags!"], process=one)
    two_mat = MaterialSpec("Material", tags=["other tags!"], process=two)
    one_ing.material = two_mat
    IngredientSpec("Ingredient", process=two, material=one_mat)  # two_ing
    assert one == two
    assert two == one

    # Order doesn't matter for tags
    one.tags = ["One", "Two", "Three", "Four"]
    two.tags = ["Four", "One", "Three", "Two"]
    assert one == two
    assert two == one
예제 #26
0
def make_cake_spec(tmpl=None):
    """Define a recipe for making a cake."""
    ###############################################################################################
    # Templates
    if tmpl is None:
        tmpl = make_cake_templates()

    count = dict()

    def ingredient_kwargs(material):
        # Pulls the elements of a material that all ingredients consume out
        count[material.name] = count.get(material.name, 0) + 1
        return {
            "name":
            "{} input{}".format(material.name.replace('Abstract ', ''),
                                " (Again)" * (count[material.name] - 1)),
            "tags":
            list(material.tags),
            "material":
            material
        }

    ###############################################################################################
    # Objects
    cake = MaterialSpec(
        name="Abstract Cake",
        template=tmpl["Dessert"],
        process=ProcessSpec(
            name='Icing Cake, in General',
            template=tmpl["Icing"],
            tags=['spreading'],
            notes='The act of covering a baked output with frosting'),
        properties=[
            PropertyAndConditions(
                Property(name="Tastiness",
                         value=NominalInteger(5),
                         template=tmpl["Tastiness"],
                         origin="specified"))
        ],
        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(**ingredient_kwargs(frosting),
                   notes='Seems like a lot of frosting',
                   labels=['coating'],
                   process=cake.process,
                   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(**ingredient_kwargs(baked_cake),
                   labels=['substrate'],
                   process=cake.process)

    ########################
    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(**ingredient_kwargs(batter),
                   labels=['precursor'],
                   process=baked_cake.process)

    ########################
    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(**ingredient_kwargs(wetmix),
                   labels=['wet'],
                   process=batter.process)

    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(**ingredient_kwargs(drymix),
                   labels=['dry'],
                   process=batter.process,
                   absolute_quantity=NominalReal(nominal=3.052, units='cups'))

    ########################
    flour = MaterialSpec(
        name="Abstract Flour",
        template=tmpl["Nutritional Material"],
        properties=[
            PropertyAndConditions(
                property=Property(name="Nutritional Information",
                                  value=NominalComposition({
                                      "dietary-fiber":
                                      1,
                                      "sugars":
                                      1,
                                      "other-carbohydrate":
                                      20,
                                      "protein":
                                      4,
                                      "other":
                                      4
                                  }),
                                  template=tmpl["Nutritional Information"],
                                  origin="specified"),
                conditions=Condition(name="Serving Size",
                                     value=NominalReal(30, 'g'),
                                     template=tmpl["Sample Mass"],
                                     origin="specified"))
        ],
        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(
        **ingredient_kwargs(flour),
        labels=['dry'],
        process=drymix.process,
        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(
        **ingredient_kwargs(baking_powder),
        labels=['leavening', 'dry'],
        process=drymix.process,
        volume_fraction=NominalReal(nominal=0.0137, units='')  # 2 teaspoons
    )

    salt = MaterialSpec(name="Abstract Salt",
                        template=tmpl["Formulaic Material"],
                        process=ProcessSpec(name='Buying Salt, in General',
                                            template=tmpl["Procurement"],
                                            tags=['purchase::dry-goods'],
                                            notes='Purchasing salt'),
                        tags=[],
                        notes='Plain old NaCl',
                        properties=[
                            PropertyAndConditions(
                                Property(name='Formula',
                                         value=EmpiricalFormula("NaCl")))
                        ])
    IngredientSpec(
        **ingredient_kwargs(salt),
        labels=['dry', 'seasoning'],
        process=drymix.process,
        volume_fraction=NominalReal(nominal=0.0034, units='')  # 1/2 teaspoon
    )

    sugar = MaterialSpec(
        name="Abstract Sugar",
        template=tmpl["Formulaic Material"],
        process=ProcessSpec(name='Buying Sugar, in General',
                            template=tmpl["Procurement"],
                            tags=['purchase::dry-goods'],
                            notes='Purchasing all purpose flour'),
        tags=[],
        notes='Sugar',
        properties=[
            PropertyAndConditions(
                Property(name="Formula", value=EmpiricalFormula("C12H22O11"))),
            PropertyAndConditions(
                Property(name='SMILES',
                         value=Smiles(
                             "C(C1C(C(C(C(O1)OC2(C(C(C(O2)CO)O)O)CO)O)O)O)O"),
                         template=tmpl["Molecular Structure"]))
        ])
    IngredientSpec(**ingredient_kwargs(sugar),
                   labels=['wet', 'sweetener'],
                   process=wetmix.process,
                   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(**ingredient_kwargs(butter),
                   labels=['wet', 'shortening'],
                   process=wetmix.process,
                   absolute_quantity=NominalReal(nominal=1, units='cups'))
    IngredientSpec(
        **ingredient_kwargs(butter),
        labels=['shortening'],
        process=frosting.process,
        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(**ingredient_kwargs(eggs),
                   labels=['wet'],
                   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=
        'Vanilla Extract is mostly alcohol but the most important component '
        'is vanillin (see attached structure)',
        properties=[
            PropertyAndConditions(
                Property(
                    name='Component Structure',
                    value=InChI(
                        "InChI=1S/C8H8O3/c1-11-8-4-6(5-9)2-3-7(8)10/h2-5,10H,1H3"
                    ),
                    template=tmpl["Molecular Structure"]))
        ])
    IngredientSpec(**ingredient_kwargs(vanilla),
                   labels=['wet', 'flavoring'],
                   process=wetmix.process,
                   absolute_quantity=NominalReal(nominal=2, units='teaspoons'))
    IngredientSpec(
        **ingredient_kwargs(vanilla),
        labels=['flavoring'],
        process=frosting.process,
        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(**ingredient_kwargs(milk),
                   labels=['wet'],
                   process=batter.process,
                   absolute_quantity=NominalReal(nominal=1, units='cup'))
    IngredientSpec(
        **ingredient_kwargs(milk),
        labels=[],
        process=frosting.process,
        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(
        **ingredient_kwargs(chocolate),
        labels=['flavoring'],
        process=frosting.process,
        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(
        **ingredient_kwargs(powder_sugar),
        labels=['flavoring'],
        process=frosting.process,
        mass_fraction=NominalReal(nominal=0.6387,
                                  units='')  # 4 c @ 30 g/ 0.25 cups
    )

    # Crawl tree and annotate with uids; only add ids if there's nothing there
    recursive_foreach(
        cake, lambda obj: obj.uids or obj.add_uid(DEMO_SCOPE, obj.name))

    return cake
예제 #27
0
def test_recursive_flatmap_maintains_order():
    """Test flatmap preserves order in lists."""
    p1 = ProcessSpec(name="one")
    p2 = ProcessSpec(name="two")
    orig = [p1, p2]
    assert [x.name for x in orig] == recursive_flatmap(orig, lambda x: [x.name])
예제 #28
0
def test_invalid_assignment():
    """Invalid assignments to `spec` throw a TypeError."""
    with pytest.raises(TypeError):
        ProcessRun("name", spec=[ProcessSpec("spec")])
    with pytest.raises(TypeError):
        ProcessRun()  # Name is required
예제 #29
0
def test_invalid_assignment():
    """Omitting a name throws a TypeError."""
    with pytest.raises(TypeError):
        ProcessSpec()  # Name is required
def make_data_island(density,
                     bulk_modulus,
                     firing_temperature,
                     binders,
                     powders,
                     tag=None):
    """Helper function to create a relatively involved data island."""
    binder_specs = keymap(lambda x: MaterialSpec(name=x), binders)
    powder_specs = keymap(lambda x: MaterialSpec(name=x), powders)

    binder_runs = keymap(lambda x: MaterialRun(name=x.name, spec=x),
                         binder_specs)
    powder_runs = keymap(lambda x: MaterialRun(name=x.name, spec=x),
                         powder_specs)

    all_input_materials = keymap(lambda x: x.spec.name,
                                 merge(binder_runs, powder_runs))
    mixing_composition = Condition(
        name="composition", value=NominalComposition(all_input_materials))
    mixing_process = ProcessRun(name="Mixing",
                                tags=["mixing"],
                                conditions=[mixing_composition])
    binder_ingredients = []
    for run in binder_runs:
        binder_ingredients.append(
            IngredientRun(
                material=run,
                process=mixing_process,
                mass_fraction=NominalReal(binders[run.spec.name], ''),
            ))

    powder_ingredients = []
    for run in powder_runs:
        powder_ingredients.append(
            IngredientRun(
                material=run,
                process=mixing_process,
                mass_fraction=NominalReal(powders[run.spec.name], ''),
            ))

    green_sample = MaterialRun("Green", process=mixing_process)

    measured_firing_temperature = Condition(
        name="Firing Temperature",
        value=UniformReal(firing_temperature - 0.5, firing_temperature + 0.5,
                          'degC'),
        template=firing_temperature_template)

    specified_firing_setting = Parameter(name="Firing setting",
                                         value=DiscreteCategorical("hot"))
    firing_spec = ProcessSpec("Firing", template=firing_template)
    firing_process = ProcessRun(name=firing_spec.name,
                                conditions=[measured_firing_temperature],
                                parameters=[specified_firing_setting],
                                spec=firing_spec)
    IngredientRun(material=green_sample,
                  process=firing_process,
                  mass_fraction=NormalReal(1.0, 0.0, ''),
                  volume_fraction=NormalReal(1.0, 0.0, ''),
                  number_fraction=NormalReal(1.0, 0.0, ''))

    measured_density = Property(name="Density",
                                value=NominalReal(density, ''),
                                template=density_template)
    measured_modulus = Property(name="Bulk modulus",
                                value=NormalReal(bulk_modulus,
                                                 bulk_modulus / 100.0, ''))
    measurement_spec = MeasurementSpec("Mechanical Properties",
                                       template=measurement_template)
    measurement = MeasurementRun(
        measurement_spec.name,
        properties=[measured_density, measured_modulus],
        spec=measurement_spec)

    tags = [tag] if tag else []

    material_spec = MaterialSpec("Coupon", template=material_template)
    material_run = MaterialRun(material_spec.name,
                               process=firing_process,
                               tags=tags,
                               spec=material_spec)
    measurement.material = material_run
    return material_run