def __init__(self,
              *,
              uids: Optional[Dict[str, str]] = None,
              tags: Optional[List[str]] = None,
              notes: Optional[str] = None,
              material: Optional[GEMDMaterialRun] = None,
              process: Optional[GEMDProcessRun] = None,
              mass_fraction: Optional[ContinuousValue] = None,
              volume_fraction: Optional[ContinuousValue] = None,
              number_fraction: Optional[ContinuousValue] = None,
              absolute_quantity: Optional[ContinuousValue] = None,
              spec: Optional[GEMDIngredientSpec] = None,
              file_links: Optional[List[FileLink]] = None):
     if uids is None:
         uids = dict()
     DataConcepts.__init__(self, GEMDIngredientRun.typ)
     GEMDIngredientRun.__init__(self,
                                uids=uids,
                                tags=tags,
                                notes=notes,
                                material=material,
                                process=process,
                                mass_fraction=mass_fraction,
                                volume_fraction=volume_fraction,
                                number_fraction=number_fraction,
                                absolute_quantity=absolute_quantity,
                                spec=spec,
                                file_links=file_links)
예제 #2
0
def test_id_case_sensitivitiy():
    """Test that uids are case insensitive."""
    with pytest.raises(ValueError):
        IngredientRun(uids={'my_id': 'sample1', 'My_ID': 'sample2'})

    ingredient = IngredientRun(uids={'my_id': 'sample1'})
    assert ingredient.uids['my_id'] == 'sample1'
    assert ingredient.uids['MY_id'] == 'sample1'
예제 #3
0
def test_invalid_assignment():
    """Invalid assignments to `process` or `material` throw a TypeError."""
    with pytest.raises(TypeError):
        IngredientRun(material=RealBounds(0, 5.0, ''))
    with pytest.raises(TypeError):
        IngredientRun(process="process")
    with pytest.raises(TypeError):
        IngredientRun(spec=5)
def test_link_by_uid():
    """Test that linking works."""
    root = MaterialRun(name='root', process=ProcessRun(name='root proc'))
    leaf = MaterialRun(name='leaf', process=ProcessRun(name='leaf proc'))
    IngredientRun(process=root.process, material=leaf)
    IngredientRun(process=root.process, material=LinkByUID.from_entity(leaf))

    copy = loads(dumps(root))
    assert copy.process.ingredients[0].material == copy.process.ingredients[
        1].material
예제 #5
0
    def crawler(spec):
        from gemd.entity.object.measurement_spec import MeasurementSpec
        from gemd.entity.object.measurement_run import MeasurementRun
        from gemd.entity.object.material_spec import MaterialSpec
        from gemd.entity.object.material_run import MaterialRun
        from gemd.entity.object.ingredient_spec import IngredientSpec
        from gemd.entity.object.ingredient_run import IngredientRun
        from gemd.entity.object.process_spec import ProcessSpec
        from gemd.entity.object.process_run import ProcessRun

        if id(spec) in seen:
            return seen[id(spec)]

        if isinstance(spec, MeasurementSpec):
            seen[id(spec)] = MeasurementRun(name=spec.name, spec=spec)
        elif isinstance(spec, MaterialSpec):
            seen[id(spec)] = MaterialRun(name=spec.name, spec=spec)
            seen[id(spec)].process = crawler(
                spec.process) if spec.process else None
        elif isinstance(spec, IngredientSpec):
            seen[id(spec)] = IngredientRun(spec=spec)
            seen[id(spec)].material = crawler(
                spec.material) if spec.material else None
        elif isinstance(spec, ProcessSpec):
            seen[id(spec)] = ProcessRun(name=spec.name, spec=spec)
            for x in spec.ingredients:
                crawler(x).process = seen[id(spec)]
        else:
            raise TypeError(
                'Passed object is not a spec-like object({})'.format(
                    type(spec)))

        # Should we assume that the same MaterialSpec in different parts of the tree
        # yields the same MaterialRun?
        return seen[id(spec)]
예제 #6
0
def test_serialize():
    """Serializing a nested object should be identical to individually serializing each piece."""
    condition = Condition(name="A condition", value=NominalReal(7, ''))
    parameter = Parameter(name="A parameter",
                          value=NormalReal(mean=17, std=1, units=''))
    input_material = MaterialRun(tags="input")
    process = ProcessRun(tags="A tag on a process run")
    ingredient = IngredientRun(material=input_material, process=process)
    material = MaterialRun(tags=["A tag on a material"], process=process)
    measurement = MeasurementRun(tags="A tag on a measurement",
                                 conditions=condition,
                                 parameters=parameter,
                                 material=material)

    # serialize the root of the tree
    native_object = json.loads(dumps(measurement))
    # ingredients don't get serialized on the process
    assert (len(native_object["context"]) == 5)
    assert (native_object["object"]["type"] == LinkByUID.typ)

    # serialize all of the nodes
    native_batch = json.loads(
        dumps([material, process, measurement, ingredient]))
    assert (len(native_batch["context"]) == 5)
    assert (len(native_batch["object"]) == 4)
    assert (all(x["type"] == LinkByUID.typ for x in native_batch["object"]))
예제 #7
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()
예제 #8
0
def test_uid_deser():
    """Test that uids continue to be a CaseInsensitiveDict after deserialization."""
    material = MaterialRun("Input material", tags="input", uids={'Sample ID': '500-B'})
    ingredient = IngredientRun(material=material)
    ingredient_copy = loads(dumps(ingredient))
    assert isinstance(ingredient_copy.uids, CaseInsensitiveDict)
    assert ingredient_copy.material == material
    assert ingredient_copy.material.uids['sample id'] == material.uids['Sample ID']
def test_name_persistance():
    """Verify that a serialized IngredientRun doesn't lose its name."""
    from gemd.entity.object import IngredientSpec
    from gemd.entity.link_by_uid import LinkByUID
    from gemd.json import GEMDJson

    je = GEMDJson()

    ms_link = LinkByUID(scope='local', id='mat_spec')
    mr_link = LinkByUID(scope='local', id='mat_run')
    ps_link = LinkByUID(scope='local', id='pro_spec')
    pr_link = LinkByUID(scope='local', id='pro_run')
    spec = IngredientSpec(name='Ingred',
                          labels=['some', 'words'],
                          process=ps_link,
                          material=ms_link)
    run = IngredientRun(spec=spec, process=pr_link, material=mr_link)
    assert run.name == spec.name
    assert run.labels == spec.labels

    # Try changing them and make sure they change
    spec.name = 'Frank'
    spec.labels = ['other', 'words']
    assert run.name == spec.name
    assert run.labels == spec.labels

    run.spec = LinkByUID(scope='local', id='ing_spec')
    # Name and labels are now stashed but not stored
    assert run == je.copy(run)
    assert run.name == spec.name
    assert run.labels == spec.labels

    # Test that serialization doesn't get confused after a deser and set
    spec_too = IngredientSpec(name='Jorge',
                              labels=[],
                              process=ps_link,
                              material=ms_link)
    run.spec = spec_too
    assert run == je.copy(run)
    assert run.name == spec_too.name
    assert run.labels == spec_too.labels
예제 #10
0
def test_to_link():
    """Test that to_link behaves as expected."""
    obj = IngredientRun(uids={"Scope": "UID", "Second": "option"})
    assert isinstance(obj.to_link(), LinkByUID), "Returns a useful LinkByUID"
    assert LinkByUID(scope="Scope", id="UID") == obj.to_link("Scope"), "Correct choice of UID"

    with pytest.raises(ValueError):
        IngredientRun().to_link(), "to_link on an object w/o IDs is fatal"

    with pytest.raises(ValueError):
        obj.to_link("Third"), "to_link with a scope that an object lacks is fatal"

    assert obj.to_link(scope="Third", allow_fallback=True).scope in obj.uids, \
        "... unless allow_fallback is set"
예제 #11
0
def test_ingredient_run():
    """Tests that a process can house an ingredient, and that pairing survives serialization."""
    # Create a ProcessSpec
    proc_run = ProcessRun(name="a process spec", tags=["tag1", "tag2"])
    ingred_run = IngredientRun(material=MaterialRun(name='Raw'), process=proc_run)

    # Make copies of both specs
    proc_run_copy = loads(dumps(proc_run))

    assert proc_run_copy == proc_run, "Full structure wasn't preserved across serialization"

    assert 'process' in repr(ingred_run)
    assert 'ingredients' in repr(proc_run)
예제 #12
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)
def test_implicit_fields():
    """These test that users can't directly set names and labels."""
    name = 'name'
    labels = ['label', 'also']
    with pytest.raises(TypeError):
        IngredientRun(name=name)
    with pytest.raises(TypeError):
        IngredientRun(labels=labels)

    run = IngredientRun()
    with pytest.raises(AttributeError):
        run.name = name
    with pytest.raises(AttributeError):
        run.labels = labels
예제 #14
0
def test_ingredient_reassignment():
    """Check that an ingredient run can be re-assigned to a new process run."""
    boiling = ProcessRun("Boil potatoes")
    frying = ProcessRun("Fry potatoes")
    oil = IngredientRun(process=boiling)
    potatoes = IngredientRun(process=boiling)
    assert oil.process == boiling
    assert set(boiling.ingredients) == {oil, potatoes}
    assert frying.ingredients == []

    oil.process = frying
    assert oil.process == frying
    assert boiling.ingredients == [potatoes]
    assert frying.ingredients == [oil]

    potatoes.process = frying
    assert potatoes.process == frying
    assert boiling.ingredients == []
    assert set(frying.ingredients) == {oil, potatoes}
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