def test_invalid_constructor(): """Test types in constructor.""" with pytest.raises(ValueError): CompositionBounds(components="foo") with pytest.raises(ValueError): CompositionBounds(components={1, 2})
def test_components(): """Test components setter.""" assert CompositionBounds().components == set() assert CompositionBounds(components={"foo", "bar"}).components == { "foo", "bar" } assert CompositionBounds(components=["foo", "bar"]).components == { "foo", "bar" }
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_numpy(): """Test that ndarrays, Series work as well.""" assert len(array_like()) < 5 # In case we extend at some point if len(array_like()) > 2: # Test numpy import numpy as np np_bounds = CompositionBounds(components=np.array(["spam", "eggs"], dtype=object)) np_copy = loads(dumps(np_bounds)) assert np_copy == np_bounds if len(array_like()) > 3: # Test pandas import pandas as pd pd_bounds = CompositionBounds(components=pd.Series(["spam", "eggs"])) pd_copy = loads(dumps(pd_bounds)) assert pd_copy == pd_bounds
def make_templates(template_scope=DEMO_TEMPLATE_SCOPE): """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={template_scope: 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={template_scope: name}, tags=['citrine::demo::template::object'], **kw_args) return tmpl
def make_strehlow_table(compounds): """ Headers and content for the output of make_strehlow_objects. Note that this is supposed to be mimicking the transformation of a set of Material Histories into a Training Table, and as such we are missing the column definition component of the query that created this particular result set. :param compounds: a list of MaterialRun objects from the make_strehlow_objects method :return: """ # Stash templates in convenience variables for comp in compounds: if comp.spec.properties: chem_tmpl = comp.spec.properties[0].property.template break chem_mat_tmpl = compounds[0].spec.template tmpl = dict() properties = compounds[0].measurements[0].spec.template.properties conditions = compounds[0].measurements[0].spec.template.conditions parameters = compounds[0].measurements[0].spec.template.parameters for attr in (properties + conditions + parameters): tmpl[attr[0].name] = attr[0] # Consider how to specify relevant data pathing here output = {'headers': [], 'content': []} # "Chemical" is supposed to be the unifying characterization of all the root elements of the # Material Histories, but that can't be the spec name because the spec is Compound specific -- # that's where the chemical is defined output['headers'].append( {'name': [chem_mat_tmpl.name, "Display name" # It would be good to derive this from the structure somehow ], 'primitive': True } ) output['headers'].append( {'name': [chem_mat_tmpl.name, chem_tmpl.name ], 'primitive': False, 'bounds': CompositionBounds() } ) terms = ["Band gap", "Temperature derivative of band gap", "Temperature", "Color", "Lasing", "Cathodoluminescence", "Mechanical luminescence", "Photoluminescence", "Electroluminescence", "Thermoluminescence", "Transition", "Bands", "Electric field polarization", "Crystallinity", "Morphology", "Phase", 'Crystal system'] for term in terms: output['headers'].append( {'name': [chem_mat_tmpl.name, term ], 'primitive': False, 'bounds': tmpl[term].bounds } ) for comp in compounds: row = [comp.spec.name] x = list(filter(lambda y: y.name == chem_tmpl.name, comp.spec.properties)) if x: row.append(x[0].value) else: row.append(None) for term in terms: x = list(filter(lambda y: y.name == term, comp.measurements[0].properties + comp.measurements[0].conditions)) if x: row.append(x[0].value) else: row.append(None) output['content'].append(row) return output
def test_json(): """Test serialization (components is encoded as a list).""" bounds = CompositionBounds(components={"spam", "eggs"}) copy = loads(dumps(bounds)) assert copy == bounds
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"}) from gemd.entity.value import NominalComposition assert bounds.contains(NominalComposition({"spam": 0.2, "eggs": 0.8})) assert not bounds.contains(NominalComposition({"foo": 1.0}))