def test_contains_incompatible_units(): """Make sure contains returns false when the units don't match.""" dim = RealBounds(lower_bound=0, upper_bound=100, default_units="m") dim2 = RealBounds(lower_bound=0, upper_bound=100, default_units="kJ") dim3 = RealBounds(lower_bound=0, upper_bound=100, default_units='') assert not dim.contains(dim2) assert not dim.contains(dim3)
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'
def test_object_template_serde(): """Test serde of an object template.""" length_template = PropertyTemplate("Length", RealBounds(2.0, 3.5, 'cm')) sub_bounds = RealBounds(2.5, 3.0, 'cm') color_template = PropertyTemplate("Color", CategoricalBounds(["red", "green", "blue"])) # Properties are a mixture of property templates and [template, bounds], pairs block_template = MaterialTemplate("Block", properties=[[length_template, sub_bounds], color_template]) copy_template = MaterialTemplate.build(block_template.dump()) assert copy_template == block_template # Tests below exercise similar code, but for measurement and process templates pressure_template = ConditionTemplate("pressure", RealBounds(0.1, 0.11, 'MPa')) index_template = ParameterTemplate("index", IntegerBounds(2, 10)) meas_template = MeasurementTemplate("A measurement of length", properties=[length_template], conditions=[pressure_template], description="Description", parameters=[index_template], tags=["foo"]) assert MeasurementTemplate.build(meas_template.dump()) == meas_template proc_template = ProcessTemplate("Make an object", parameters=[index_template], conditions=[pressure_template], allowed_labels=["Label"], allowed_names=["first sample", "second sample"]) assert ProcessTemplate.build(proc_template.dump()) == proc_template # Check that serde still works if the template is a LinkByUID proc_template.conditions[0][0] = LinkByUID('id', pressure_template.uids['id']) assert ProcessTemplate.build(proc_template.dump()) == proc_template
def test_bounds_mismatch(): """Test that a mismatch between the attribute and given bounds throws a ValueError.""" attribute_bounds = RealBounds(0, 100, '') object_bounds = RealBounds(200, 300, '') cond_template = ConditionTemplate("a condition", bounds=attribute_bounds) with pytest.raises(ValueError): ProcessTemplate("a process template", conditions=[[cond_template, object_bounds]])
def make_cake_templates(): """Define all templates independently, as in the wild this will be an independent operation.""" tmpl = dict() # Attributes 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["Tastiness"] = PropertyTemplate( name="Tastiness", description="Yumminess on a fairly arbitrary scale", bounds=IntegerBounds(lower_bound=1, upper_bound=10) ) # Objects tmpl["Baking in an oven"] = ProcessTemplate( name="Baking in an oven", description='Using heat to promote chemical reactions in a material', allowed_labels=['precursor'], conditions=[(tmpl["Oven temperature"], RealBounds(0, 700, "degF"))], parameters=[(tmpl["Oven temperature setting"], RealBounds(100, 550, "degF"))] ) tmpl["Taste test"] = MeasurementTemplate( name="Taste test", properties=[tmpl["Tastiness"]] ) tmpl["Dessert"] = MaterialTemplate( name="Dessert", properties=[tmpl["Tastiness"]] ) tmpl["Generic Material"] = MaterialTemplate(name="Generic") 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']) tmpl["Procurement"] = ProcessTemplate(name="Procurement", description="Buyin' stuff") return tmpl
def test_template_assignment(): """Test that an object and its attributes can both be assigned templates.""" humidity_template = ConditionTemplate("Humidity", RealBounds(0.5, 0.75, "")) template = ProcessTemplate( "Dry", conditions=[[humidity_template, RealBounds(0.5, 0.65, "")]]) ProcessSpec("Dry a polymer", template=template, conditions=[ Condition("Humidity", value=NominalReal(0.6, ""), template=humidity_template) ])
def test_constructor_error(): """Test that invalid real bounds cannot be constructed.""" with pytest.raises(ValueError): RealBounds() with pytest.raises(ValueError): RealBounds(0, float("inf"), "meter") with pytest.raises(ValueError): RealBounds(None, 10, '') with pytest.raises(ValueError): RealBounds(0, 100) with pytest.raises(ValueError): RealBounds(100, 0, "m")
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_contains(): """Test basic contains logic.""" bounds = CompositionBounds(components={"spam", "eggs"}) assert bounds.contains(CompositionBounds(components={"spam"})) assert not bounds.contains(CompositionBounds(components={"foo"})) assert not bounds.contains(RealBounds(0.0, 2.0, '')) assert not bounds.contains(None) with pytest.raises(TypeError): bounds.contains({"spam"})
def test_contains(): """Test basic contains logic.""" bounds = CategoricalBounds(categories={"spam", "eggs"}) assert bounds.contains(CategoricalBounds(categories={"spam"})) assert not bounds.contains(CategoricalBounds(categories={"spam", "foo"})) assert not bounds.contains(RealBounds(0.0, 2.0, '')) assert not bounds.contains(None) with pytest.raises(TypeError): bounds.contains({"spam", "eggs"})
def test_type_mismatch(): """Test that incompatible types cannot be matched against RealBounds.""" bounds = RealBounds(0, 1, default_units="meters") assert not bounds.contains(IntegerBounds(0, 1)) assert not bounds.contains(None) with pytest.raises(TypeError): bounds.contains([.33, .66])
def test_condition_template(): """Test creation and serde of condition templates.""" bounds = RealBounds(2.5, 10.0, default_units='cm') template = ConditionTemplate("Chamber width", bounds=bounds, description="width of chamber") assert template.uids is not None # uids should be added automatically # Take template through a serde cycle and ensure that it is unchanged assert ConditionTemplate.build(template.dump()) == template # A more complicated cycle that goes through both taurus and citrine-python serde. assert ConditionTemplate.build(loads(dumps( template.dump())).as_dict()) == template
def test_attribute_serde(): """An attribute with a link to an attribute template should be copy-able.""" prop_tmpl = PropertyTemplate(name='prop_tmpl', bounds=RealBounds(0, 2, 'm') ) prop = Property(name='prop', template=prop_tmpl, value=NominalReal(1, 'm') ) meas_spec = MeasurementSpec("a spec") meas = MeasurementRun("a measurement", spec=meas_spec, properties=[prop]) assert loads(dumps(prop)) == prop assert loads(dumps(meas)) == meas assert isinstance(prop.template, PropertyTemplate)
def test_object_template_validation(): """Test that attribute templates are validated against given bounds.""" length_template = PropertyTemplate("Length", RealBounds(2.0, 3.5, 'cm')) dial_template = ConditionTemplate("dial", IntegerBounds(0, 5)) color_template = ParameterTemplate( "Color", CategoricalBounds(["red", "green", "blue"])) with pytest.raises(ValueError): MaterialTemplate( "Block", properties=[[length_template, RealBounds(3.0, 4.0, 'cm')]]) with pytest.raises(ValueError): ProcessTemplate( "a process", conditions=[[color_template, CategoricalBounds(["zz"])]]) with pytest.raises(ValueError): MeasurementTemplate("A measurement", parameters=[[dial_template, IntegerBounds(-3, -1)]])
def test_simple_deserialization(valid_data, attribute_list): for (key, Attribute, AttributeTemplate) in attribute_list: valid_data['type'] = key valid_data['template']['type'] = key + '_template' attribute: Attribute = Attribute.build(valid_data) assert attribute.name == 'mass' assert attribute.notes == 'This is a note' assert attribute.value == NominalReal(5.0, units='gram') assert attribute.template == \ AttributeTemplate('mass', uids={'id': valid_data['template']['uids']['id']}, description='mass of object', bounds=RealBounds(0.0, 20.0, 'gram') ) assert attribute.origin == 'measured' assert attribute.file_links == [] assert attribute.typ == key
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 test_recursive_foreach(): """Test that recursive foreach will actually walk through a material history.""" mat_run = MaterialRun("foo") process_run = ProcessRun("bar") IngredientRun(process=process_run, material=mat_run) output = MaterialRun(process=process_run) # property templates are trickier than templates because they are referenced in attributes template = PropertyTemplate("prop", bounds=RealBounds(0, 1, "")) prop = Property("prop", value=NominalReal(1.0, ""), template=template) MeasurementRun("check", material=output, properties=prop) types = [] recursive_foreach(output, lambda x: types.append(x.typ)) expected = [ "ingredient_run", "material_run", "material_run", "process_run", "measurement_run", "property_template" ] assert sorted(types) == sorted(expected)
}, "<Material spec 'foo'>"), (MaterialTemplate, { 'name': 'foo' }, "<Material template 'foo'>"), (MeasurementRun, { 'name': 'foo' }, "<Measurement run 'foo'>"), (MeasurementSpec, { 'name': 'foo' }, "<Measurement spec 'foo'>"), (MeasurementTemplate, { 'name': 'foo' }, "<Measurement template 'foo'>"), (ParameterTemplate, { 'name': 'foo', 'bounds': RealBounds(0, 1, '') }, "<Parameter template 'foo'>"), (ProcessRun, { 'name': 'foo' }, "<Process run 'foo'>"), (ProcessSpec, { 'name': 'foo' }, "<Process spec 'foo'>"), (ProcessTemplate, { 'name': 'foo' }, "<Process template 'foo'>"), (PropertyTemplate, { 'name': 'foo', 'bounds': RealBounds(0, 1, '') }, "<Property template 'foo'>"), (ConditionTemplate, {
def make_templates(): """Build all templates needed for the table.""" tmpl = dict() # Attribute Templates attribute_feed = { "Formula": [ PropertyTemplate, CompositionBounds(components=EmpiricalFormula.all_elements()) ], "Crystallinity": [ ConditionTemplate, CategoricalBounds( ['Amorphous', 'Polycrystalline', 'Single crystalline']) ], "Color": [ PropertyTemplate, CategoricalBounds([ 'Amber', 'Black', 'Blue', 'Bluish', 'Bronze', 'Brown', 'Brown-Black', 'Copper-Red', 'Dark Brown', 'Dark Gray', 'Dark Green', 'Dark Red', 'Gray', 'Light Gray', 'Ocher', 'Orange', 'Orange-Red', 'Pale Yellow', 'Red', 'Red-Yellow', 'Violet', 'White', 'Yellow', 'Yellow-Orange', 'Yellow-White' ]) ], "Band gap": [ PropertyTemplate, RealBounds(lower_bound=0.001, upper_bound=100, default_units='eV') ], "Temperature": [ ConditionTemplate, RealBounds(lower_bound=1, upper_bound=1000, default_units='K') ], "Temperature derivative of band gap": [ PropertyTemplate, RealBounds(lower_bound=-0.01, upper_bound=0.01, default_units='eV/K') ], "Lasing": [PropertyTemplate, CategoricalBounds(['True', 'False'])], "Cathodoluminescence": [PropertyTemplate, CategoricalBounds(['True', 'False'])], "Mechanical luminescence": [PropertyTemplate, CategoricalBounds(['True', 'False'])], "Photoluminescence": [PropertyTemplate, CategoricalBounds(['True', 'False'])], "Electroluminescence": [PropertyTemplate, CategoricalBounds(['True', 'False'])], "Thermoluminescence": [PropertyTemplate, CategoricalBounds(['True', 'False'])], "Morphology": [ConditionTemplate, CategoricalBounds(['Thin film', 'Bulk'])], "Electric field polarization": [ ConditionTemplate, CategoricalBounds([ 'Parallel to A axis', 'Parallel to B axis', 'Parallel to C axis', 'Perpendicular to B axis', 'Perpendicular to C axis' ]) ], "Phase": [ ConditionTemplate, CategoricalBounds([ 'A', 'B', 'B1', 'B2', 'Fused quartz', 'Natural diamond', 'Rutile', 'Sapphire', 'Synthetic quartz' ]) ], "Crystal system": [ ConditionTemplate, CategoricalBounds([ 'Cubic', 'Hexagonal', 'Orthorhombic', 'Tetragonal', 'Trigonal' ]) ], "Transition": [ ConditionTemplate, CategoricalBounds(['Direct', 'Excitonic', 'Indirect']) ], "Bands": [ ConditionTemplate, CategoricalBounds([ 'G1 to X1', 'G15 to G1', 'G15 to X1', 'G25 to G1', 'G25 to G12', 'G25 to G15', 'G6 to G8', 'G8 to G6+', 'L6+ to L6-' ]) ] } for (name, (typ, bounds)) in attribute_feed.items(): assert name not in tmpl tmpl[name] = typ(name=name, bounds=bounds, uids={DEMO_SCOPE + '-template': name}, tags=['citrine::demo::template::attribute']) # Object Templates object_feed = { "Sample preparation": [ProcessTemplate, dict()], "Chemical": [MaterialTemplate, { "properties": [tmpl["Formula"]] }], "Band gap measurement": [ MeasurementTemplate, { "properties": [ tmpl["Band gap"], tmpl["Temperature derivative of band gap"], tmpl["Color"], tmpl["Lasing"], tmpl["Cathodoluminescence"], tmpl["Mechanical luminescence"], tmpl["Photoluminescence"], tmpl["Electroluminescence"], tmpl["Thermoluminescence"] ], "conditions": [ tmpl["Temperature"], tmpl["Crystallinity"], tmpl["Morphology"], tmpl["Electric field polarization"], tmpl["Phase"], tmpl["Crystal system"], tmpl["Transition"], tmpl["Bands"] ] } ], } for (name, (typ, kw_args)) in object_feed.items(): assert name not in tmpl tmpl[name] = typ(name=name, uids={DEMO_SCOPE + '-template': name}, tags=['citrine::demo::template::object'], **kw_args) return tmpl
"""For entities that hve quantities.""" from taurus.entity.bounds.real_bounds import RealBounds from taurus.entity.value.continuous_value import ContinuousValue fraction_bounds = RealBounds(lower_bound=0.0, upper_bound=1.0, default_units='') class HasQuantities(object): """Mixin-trait that includes the mass, volume, number fraction, and absolute quantity.""" def __init__(self, mass_fraction=None, volume_fraction=None, number_fraction=None, absolute_quantity=None): self._mass_fraction = None self.mass_fraction = mass_fraction self._volume_fraction = None self.volume_fraction = volume_fraction self._number_fraction = None self.number_fraction = number_fraction self._absolute_quantity = None self.absolute_quantity = absolute_quantity @property def mass_fraction(self): """Get mass fraction.""" return self._mass_fraction @mass_fraction.setter
def test_json(): """Test that json serialization round robins to the identity.""" template = PropertyTemplate(name="foo", bounds=RealBounds(0, 1, "")) copy = loads(dumps(template)) assert copy == template
from taurus.entity.object import MeasurementRun, MaterialRun, ProcessRun, ProcessSpec,\ MeasurementSpec, MaterialSpec from taurus.entity.template.condition_template import ConditionTemplate from taurus.entity.template.material_template import MaterialTemplate from taurus.entity.template.measurement_template import MeasurementTemplate from taurus.entity.template.process_template import ProcessTemplate from taurus.entity.template.property_template import PropertyTemplate from taurus.entity.value.discrete_categorical import DiscreteCategorical from taurus.entity.value.nominal_composition import NominalComposition from taurus.entity.value.nominal_real import NominalReal from taurus.entity.value.normal_real import NormalReal from taurus.entity.value.uniform_real import UniformReal density_template = PropertyTemplate( name="Density", bounds=RealBounds(lower_bound=0, upper_bound=1.0e9, default_units='') ) firing_temperature_template = ConditionTemplate( name="Firing Temperature", bounds=RealBounds(lower_bound=0, upper_bound=1.0e9, default_units='degC') ) measurement_template = MeasurementTemplate(properties=density_template) firing_template = ProcessTemplate( name="Firing in a kiln", conditions=(firing_temperature_template, RealBounds(lower_bound=500, upper_bound=1000, default_units='degC')) ) material_template = MaterialTemplate( name="Some ceramic thing", properties=density_template
def test_incompatible_types(): """Make sure that incompatible types aren't contained or validated.""" int_bounds = IntegerBounds(0, 1) assert not int_bounds.contains(RealBounds(0.0, 1.0, ''))
from taurus.entity.attribute.parameter import Parameter from taurus.entity.attribute.property import Property from taurus.entity.bounds.categorical_bounds import CategoricalBounds from taurus.entity.bounds.real_bounds import RealBounds from taurus.entity.object import MaterialRun, MeasurementRun from taurus.entity.template.condition_template import ConditionTemplate from taurus.entity.template.parameter_template import ParameterTemplate from taurus.entity.template.property_template import PropertyTemplate from taurus.entity.value.discrete_categorical import DiscreteCategorical from taurus.entity.value.nominal_real import NominalReal from taurus.entity.value.normal_real import NormalReal known_properties = { "density": PropertyTemplate( name="density", bounds=RealBounds(lower_bound=0.0, upper_bound=1000.0, default_units='g / cm^3') ), "kinematic viscosity": PropertyTemplate( name="kinematic viscosity", bounds=RealBounds(lower_bound=0.0, upper_bound=10.0**40, default_units="m^2 / s") ) } known_conditions = { "temperature": ConditionTemplate( name="temperature", bounds=RealBounds(lower_bound=0.0, upper_bound=1000.0, default_units='K') ) } known_parameters = {
def test_contains_no_units(): """Make sure contains handles boundsless values.""" dim = RealBounds(lower_bound=0, upper_bound=100, default_units="") dim2 = RealBounds(lower_bound=0, upper_bound=100, default_units="") assert dim.contains(dim2)
def test_contains(): """Make sure unit conversions are applied to bounds for contains.""" dim = RealBounds(lower_bound=0, upper_bound=100, default_units="degC") dim2 = RealBounds(lower_bound=33, upper_bound=200, default_units="degF") assert dim.contains(dim2)