def test_simple_deserialization(valid_data): """Ensure that a deserialized Process Spec looks sane.""" process_spec: ProcessSpec = ProcessSpec.build(valid_data) assert process_spec.uids == {'id': valid_data['uids']['id']} assert process_spec.tags == ['baking::cakes', 'danger::low'] assert process_spec.parameters[0] == Parameter(name='oven temp', value=UniformReal( 195, 205, ''), origin='specified') assert process_spec.conditions == [] assert process_spec.template == \ ProcessTemplate('the template', uids={'id': valid_data['template']['uids']['id']}, parameters=[ [ParameterTemplate('oven temp template', bounds=RealBounds(175, 225, ''), uids={'id': valid_data['template']['parameters'][0][0]['uids']['id']}), RealBounds(175, 225, '')] ], description='a long description', allowed_labels=['a', 'b'], allowed_names=['a name']) assert process_spec.name == 'Process 1' assert process_spec.notes == 'make sure to use oven mitts' assert process_spec.file_links == [ FileLink('cake_recipe.txt', 'www.baking.com') ] assert process_spec.typ == 'process_spec' assert process_spec.audit_info == AuditInfo(**valid_data['audit_info'])
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"]))
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"
def test_deserialize(): """Round-trip serde should leave the object unchanged.""" condition = Condition(name="A condition", value=NominalReal(7, '')) parameter = Parameter(name="A parameter", value=NormalReal(mean=17, std=1, units='')) measurement = MeasurementRun(tags="A tag on a measurement", conditions=condition, parameters=parameter) copy_meas = GEMDJson().copy(measurement) assert(copy_meas.conditions[0].value == measurement.conditions[0].value) assert(copy_meas.parameters[0].value == measurement.parameters[0].value) assert(copy_meas.uids["auto"] == measurement.uids["auto"])
def test_recursive_foreach(): """Test that recursive_foreach() applies a method to every object.""" new_tag = "Extra tag" def func(base_ent): """Adds a specific tag to the object.""" base_ent.tags.extend([new_tag]) return param_template = ParameterTemplate("a param template", bounds=RealBounds(0, 100, '')) meas_template = MeasurementTemplate("Measurement template", parameters=[param_template]) parameter = Parameter(name="A parameter", value=NormalReal(mean=17, std=1, units='')) measurement = MeasurementSpec(name="name", parameters=parameter, template=meas_template) test_dict = {"foo": measurement} recursive_foreach(test_dict, func, apply_first=True) for ent in [param_template, meas_template, measurement]: assert new_tag in ent.tags
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
def ingest_material_run(data, material_spec=None, process_run=None): """Ingest material run with data, a material spec, and an originating process run.""" if isinstance(data, list): return [ingest_material_run(x, material_spec) for x in data] if not isinstance(data, dict): raise ValueError("This ingester operates on dict, but got {}".format(type(data))) material = MaterialRun() sample_id = data.get("sample_id") if sample_id: material.add_uid("given_sample_id", sample_id) tags = data.get("tags") if tags: material.tags = tags for experiment in data.get("experiments", []): measurement = MeasurementRun() for name in set(known_properties.keys()).intersection(experiment.keys()): prop = Property( name=name, template=known_properties[name], value=_parse_value(experiment[name]) ) measurement.properties.append(prop) for name in set(known_conditions.keys()).intersection(experiment.keys()): cond = Condition( name=name, template=known_conditions[name], value=_parse_value(experiment[name]) ) measurement.conditions.append(cond) for name in set(known_parameters.keys()).intersection(experiment.keys()): param = Parameter( name=name, template=known_parameters[name], value=_parse_value(experiment[name]) ) measurement.parameters.append(param) scan_id = experiment.get("scan_id") if scan_id: measurement.add_uid("given_scan_id", scan_id) tags = experiment.get("tags") if tags: measurement.tags = tags measurement.material = material if material_spec: material.material_spec = material_spec if process_run: material.process = process_run return material