def test_model_returns_complex(self): # This tests model failure with scalar complex. # Quantity class has other more thorough tests. A = Symbol('a', ['A'], ['A'], units='dimensionless', shape=1) B = Symbol('b', ['B'], ['B'], units='dimensionless', shape=1) get_config = { 'name': 'add_complex_value', # 'connections': [{'inputs': ['b'], 'outputs': ['a']}], 'equations': ['a = b + 1j'], # 'unit_map': {'a': "dimensionless", 'a': "dimensionless"} 'symbol_property_map': { "a": A, "b": B } } model = EquationModel(**get_config) out = model.evaluate({'b': Quantity(B, 5)}, allow_failure=True) self.assertFalse(out['successful']) self.assertEqual(out['message'], 'Evaluation returned invalid values (complex)') out = model.evaluate({'b': Quantity(B, 5j)}, allow_failure=True) self.assertTrue(out['successful']) self.assertTrue(np.isclose(out['a'].magnitude, 6j))
def test_unit_handling(self): """ Tests unit handling with a simple model that calculates the area of a rectangle as the product of two lengths. In this case the input lengths are provided in centimeters and meters. Tests whether the input units are properly coerced into canonical types. Tests whether the output units are properly set. Tests whether the model returns as predicted. Returns: None """ L = Symbol('l', ['L'], ['L'], units=[1.0, [['centimeter', 1.0]]], shape=[1]) A = Symbol('a', ['A'], ['A'], units=[1.0, [['centimeter', 2.0]]], shape=[1]) get_area_config = { 'name': 'area', # 'connections': [{'inputs': ['l1', 'l2'], 'outputs': ['a']}], 'equations': ['a = l1 * l2'], # 'unit_map': {'l1': "cm", "l2": "cm", 'a': "cm^2"} 'symbol_property_map': {"a": A, "l1": L, "l2": L} } model = EquationModel(**get_area_config) out = model.evaluate({'l1': Quantity(L, 1, 'meter'), 'l2': Quantity(L, 2)}, allow_failure=False) self.assertTrue(math.isclose(out['a'].magnitude, 200.0)) self.assertTrue(out['a'].units == A.units)
def test(self, inputs, outputs): """ Runs a test of the model to determine whether its operation is consistent with the specified inputs and outputs Args: inputs (dict): set of input names to values outputs (dict): set of output names to values Returns (bool): True if test succeeds """ evaluate_inputs = self.map_symbols_to_properties(inputs) evaluate_inputs = {s: Quantity(s, v, self.unit_map.get(s)) for s, v in evaluate_inputs.items()} evaluate_outputs = self.evaluate(evaluate_inputs, allow_failure=False) evaluate_outputs = self.map_properties_to_symbols(evaluate_outputs) errmsg = "{} model test failed on ".format(self.name) + "{}\n" errmsg += "{}(test data) = {}\n"#.format(k, known_output) errmsg += "{}(model output) = {}"#.format(k, plug_in_output) for k, known_output in outputs.items(): symbol = self.symbol_property_map[k] units = self.unit_map.get(k) known_quantity = Quantity(symbol, known_output, units) evaluate_output = evaluate_outputs[k] if known_quantity.is_pint or isinstance(known_quantity.value, list): if not np.allclose(known_quantity.value, evaluate_output.value): errmsg = errmsg.format("evaluate", k, evaluate_output, k, known_quantity) raise ModelEvaluationError(errmsg) elif known_quantity != evaluate_output: errmsg = errmsg.format("evaluate", k, evaluate_output, k, known_quantity) raise ModelEvaluationError(errmsg) return True
def test_add_default_quantities(self): material = Material(add_default_quantities=True) self.assertEqual( list(material['temperature'])[0], Quantity("temperature", 300)) self.assertEqual( list(material['relative_permeability'])[0], Quantity("relative_permeability", 1))
def test_from_weighted_mean(self): qlist = [ Quantity(self.custom_symbol, val) for val in np.arange(1, 2.01, 0.01) ] qagg = Quantity.from_weighted_mean(qlist) self.assertAlmostEqual(qagg.magnitude, 1.5) self.assertAlmostEqual(qagg.uncertainty, 0.2915475947422652)
def process_item(self, item): # Define quantities corresponding to materials doc fields # Attach quantities to materials item = MontyDecoder().process_decoded(item) logger.info("Populating material for %s", item['task_id']) material = Material() for mkey, property_name in self.materials_symbol_map.items(): value = get(item, mkey) if value: material.add_quantity(Quantity(property_name, value)) # Add custom things, e. g. computed entry computed_entry = get_entry(item) material.add_quantity(Quantity("computed_entry", computed_entry)) material.add_quantity( Quantity("external_identifier_mp", item['task_id'])) input_quantities = material.get_quantities() # Use graph to generate expanded quantity pool logger.info("Evaluating graph for %s", item['task_id']) graph = Graph() graph.remove_models({ "dimensionality_cheon": DEFAULT_MODEL_DICT['dimensionality_cheon'], "dimensionality_gorai": DEFAULT_MODEL_DICT['dimensionality_gorai'] }) new_material = graph.evaluate(material) # Format document and return logger.info("Creating doc for %s", item['task_id']) doc = {"inputs": [quantity.as_dict() for quantity in input_quantities]} for symbol, quantity in new_material.get_aggregated_quantities().items( ): all_qs = new_material._symbol_to_quantity[symbol] # Only add new quantities if len(all_qs) == 1 and list(all_qs)[0] in input_quantities: continue qs = [quantity.as_dict() for quantity in all_qs] sub_doc = { "quantities": qs, "mean": unumpy.nominal_values(quantity.value).tolist(), "std_dev": unumpy.std_devs(quantity.value).tolist(), "units": qs[0]['units'], "title": quantity._symbol_type.display_names[0] } doc[symbol.name] = sub_doc doc.update({ "task_id": item["task_id"], "pretty_formula": item["pretty_formula"] }) return jsanitize(doc, strict=True)
def test_properties(self): # Test units, magnitude q = Quantity("bulk_modulus", 100) self.assertEqual(q.units, "gigapascal") self.assertEqual(q.magnitude, 100) # Ensure non-pint values raise error with units, magnitude structure = PymatgenTest.get_structure('Si') q = Quantity("structure", structure) with self.assertRaises(ValueError): print(q.units) with self.assertRaises(ValueError): print(q.magnitude)
def setUp(self): # Create some test properties and a few base objects self.q1 = Quantity( DEFAULT_SYMBOLS['bulk_modulus'], ureg.Quantity.from_tuple([200, [['gigapascals', 1]]])) self.q2 = Quantity( DEFAULT_SYMBOLS['shear_modulus'], ureg.Quantity.from_tuple([100, [['gigapascals', 1]]])) self.q3 = Quantity( DEFAULT_SYMBOLS['bulk_modulus'], ureg.Quantity.from_tuple([300, [['gigapascals', 1]]])) self.material = Material() self.graph = Graph()
def generate_canonical_material(c_symbols): """ Generates a Material with appropriately attached Quantities. Args: c_symbols: (dict<str, Symbol>) dictionary of defined materials. Returns: (Material) material with properties loaded. """ q1 = Quantity(c_symbols['A'], 19) q2 = Quantity(c_symbols['A'], 23) m = Material() m.add_quantity(q1) m.add_quantity(q2) return m
def test_quantity_construction(self): # From custom symbol q = Quantity(self.custom_symbol, 5.0) self.assertEqual(q.value.magnitude, 5.0) self.assertIsInstance(q.value, ureg.Quantity) # From canonical symbol q = Quantity("bulk_modulus", 100) self.assertEqual(q.value.magnitude, 100) # From custom symbol with constraint with self.assertRaises(SymbolConstraintError): Quantity(self.constraint_symbol, -500) # From canonical symbol with constraint with self.assertRaises(SymbolConstraintError): Quantity("bulk_modulus", -500)
def evaluate(input_rows): quantities = [ Quantity(symbol_type=ROW_IDX_TO_SYMBOL_NAME[idx], value=ureg.parse_expression(row['Value'])) for idx, row in enumerate(input_rows) if row['Value'] ] material = Material() for quantity in quantities: material.add_quantity(quantity) graph = Graph() output_material = graph.evaluate(material) output_quantities = output_material.get_aggregated_quantities() print(output_quantities) output_rows = [{ 'Property': symbol.display_names[0], 'Value': str(quantity.value), 'Provenance': None } for symbol, quantity in output_quantities.items()] return output_rows
def iterate(self, nested_dict=None): """http://stackoverflow.com/questions/10756427/loop-through-all-nested-dictionary-values""" d = self if nested_dict is None else nested_dict if nested_dict is None: self.level = 0 for key in list(d.keys()): value = d[key] if isinstance(value, _Mapping): if value.get('@class') == 'Structure': from pymatgen import Structure yield key, Structure.from_dict(value) continue yield (self.level, key), None if value.get('@class') == 'Table': from mpcontribs.io.core.components.tdata import Table yield key, Table.from_dict(value) continue if Quantity is not None and value.get('@class') == 'Quantity': quantity = Quantity.from_dict(value) yield key, quantity continue self.level += 1 for inner_key, inner_value in self.iterate(nested_dict=value): yield inner_key, inner_value self.level -= 1 else: yield (self.level, key), value
def get_materials_for_mpids(self, mpids, filter_null_properties=True): """ Retrieve a list of Materials from the materials Project for a given list of Materials Project IDs. Args: mpids: a list of Materials Project IDs Returns: """ materials_properties = self.get_properties_for_mpids( mpids, filter_null_properties=filter_null_properties) materials = [] for material_properties in materials_properties: material = Material() for property_name, property_value in material_properties.items(): provenance = ProvenanceElement(source='Materials Project') quantity = Quantity(self.mapping[property_name], property_value, provenance=provenance) material.add_quantity(quantity) materials.append(material) return materials
def test_get_provenance_graph(self): g = Graph() qs = [ Quantity("bulk_modulus", 100), Quantity("shear_modulus", 50), Quantity("density", 8.96) ] mat = Material(qs) evaluated = g.evaluate(mat) # TODO: this should be tested more thoroughly out = list(evaluated['vickers_hardness'])[0] with tempfile.ScratchDir('.'): out.draw_provenance_graph("out.png") pgraph = out.get_provenance_graph() end = list(evaluated['vickers_hardness'])[0] shortest_lengths = nx.shortest_path_length(pgraph, qs[0]) self.assertEqual(shortest_lengths[end], 4)
def evaluate(input_rows, data, aggregate): quantities = [Quantity(symbol_type=ROW_IDX_TO_SYMBOL_NAME[idx], value=ureg.parse_expression(row['Editable Value'])) for idx, row in enumerate(input_rows) if row['Editable Value']] if data and len(data) > 0: quantities += json.loads(data, cls=MontyDecoder).values() if not quantities: raise PreventUpdate material = Material() for quantity in quantities: material.add_quantity(quantity) graph = Graph() output_material = graph.evaluate(material) if aggregate: output_quantities = output_material.get_aggregated_quantities().values() else: output_quantities = output_material.get_quantities() output_rows = [{ 'Property': quantity.symbol.display_names[0], 'Value': quantity.pretty_string(3) } for quantity in output_quantities] output_table = dt.DataTable(id='output-table', rows=output_rows, editable=False) # TODO: clean up input_quantity_names = [q.symbol.name for q in quantities] derived_quantity_names = set( [q.symbol.name for q in output_quantities]) - \ set(input_quantity_names) material_graph_data = graph_conversion( graph.get_networkx_graph(), nodes_to_highlight_green=input_quantity_names, nodes_to_highlight_yellow=list(derived_quantity_names)) options = AESTHETICS['global_options'] options['edges']['color'] = '#000000' output_graph = html.Div(GraphComponent( id='material-graph', graph=material_graph_data, options=options ), style={'width': '100%', 'height': '400px'}) return [ output_graph, html.Br(), output_table ]
def test_numpy_scalar_conversion(self): # From custom symbol q_int = Quantity(self.custom_symbol, np.int64(5)) q_float = Quantity(self.custom_symbol, np.float64(5.0)) q_complex = Quantity(self.custom_symbol, np.complex64(5.0 + 1.j)) self.assertTrue(isinstance(q_int.magnitude, int)) self.assertTrue(isinstance(q_float.magnitude, float)) self.assertTrue(isinstance(q_complex.magnitude, complex)) q_int_uncertainty = Quantity(self.custom_symbol, 5, uncertainty=np.int64(1)) q_float_uncertainty = Quantity(self.custom_symbol, 5.0, uncertainty=np.float64(1.0)) q_complex_uncertainty = Quantity(self.custom_symbol, 5.0 + 1j, uncertainty=np.complex64(1.0 + 0.1j)) self.assertTrue( isinstance(q_int_uncertainty.uncertainty.magnitude, int)) self.assertTrue( isinstance(q_float_uncertainty.uncertainty.magnitude, float)) self.assertTrue( isinstance(q_complex_uncertainty.uncertainty.magnitude, complex))
def add_default_quantities(self): """ Adds any default symbols which are not present in the graph Returns: None """ new_syms = set(DEFAULT_SYMBOL_VALUES.keys()) new_syms -= set(self._symbol_to_quantity.keys()) for sym in new_syms: quantity = Quantity.from_default(sym) warnings.warn("Adding default {} quantity with value {}".format( sym, quantity)) self.add_quantity(quantity)
def get_aggregated_quantities(self): """ Return mean values for all quantities for each symbol. Returns: (dict<Symbol, weighted_mean) mapping from a Symbol to an aggregated statistic. """ # TODO: proper weighting system, and more flexibility in object handling aggregated = {} for symbol, quantities in self._symbol_to_quantity.items(): if not symbol.category == 'object': aggregated[symbol] = Quantity.from_weighted_mean( list(quantities)) return aggregated
def aggregate_quantities(quantities, model_score_dict=None): """ Simple method for aggregating a set of quantities Args: quantities: model_score_dict: Returns: """ symbol = next(iter(quantities)).symbol if not all([q.symbol == symbol for q in quantities]): raise ValueError("Quantities passed to aggregate must be same symbol") weights = [get_weight(q, model_score_dict) for q in quantities] result_value = sum( [w * q.value for w, q in zip(weights, quantities)]) / sum(weights) return Quantity(symbol, result_value)
def test_model_returns_nan(self): # This tests model failure with scalar nan. # Quantity class has other more thorough tests. A = Symbol('a', ['A'], ['A'], units='dimensionless', shape=1) B = Symbol('b', ['B'], ['B'], units='dimensionless', shape=1) get_config = { 'name': 'equality', # 'connections': [{'inputs': ['b'], 'outputs': ['a']}], 'equations': ['a = b'], # 'unit_map': {'a': "dimensionless", 'a': "dimensionless"} 'symbol_property_map': {"a": A, "b": B} } model = EquationModel(**get_config) out = model.evaluate({'b': Quantity(B, float('nan'))}, allow_failure=True) self.assertFalse(out['successful']) self.assertEqual(out['message'], 'Evaluation returned invalid values (NaN)')
def test_super_evaluate(self): """ Tests the graph's composite material evaluation. """ mpr = MPRester() m1 = mpr.get_material_for_mpid("mp-13") # Temporary hack for problem with zero band-gap materials m1.remove_symbol("band_gap_pbe") m1.add_quantity(Quantity("band_gap", 0.0)) m2 = mpr.get_material_for_mpid("mp-24972") sm = CompositeMaterial([m1, m2]) g = Graph() sm = g.super_evaluate(sm, allow_model_failure=False) self.assertTrue('pilling_bedworth_ratio' in sm._symbol_to_quantity.keys(), "Super Evaluate failed to derive expected outputs.") self.assertTrue(len(sm._symbol_to_quantity['pilling_bedworth_ratio']) > 0, "Super Evaluate failed to derive expected outputs.")
def test_provenance(self): model4 = EquationModel(name="model4", equations=["D=B*C*11"], constraints=["G==0"]) symbols = GraphTest.generate_canonical_symbols() models = GraphTest.generate_canonical_models() models['model4'] = model4 del models['model6'] material = GraphTest.generate_canonical_material(symbols) g = Graph(symbol_types=symbols, models=models, composite_models=dict()) material_derived = g.evaluate(material) expected_quantities = [ Quantity(symbols['A'], 19), Quantity(symbols['A'], 23), Quantity(symbols['B'], 38), Quantity(symbols['B'], 46), Quantity(symbols['C'], 57), Quantity(symbols['C'], 69), Quantity(symbols['G'], 95), Quantity(symbols['G'], 115), Quantity(symbols['F'], 266), Quantity(symbols['F'], 322), Quantity(symbols['D'], 70395), Quantity(symbols['D'], 85215), Quantity(symbols['D'], 103155) ] for q in material_derived._symbol_to_quantity[symbols['A']]: self.assertTrue(q._provenance is None) for q in material_derived._symbol_to_quantity[symbols['B']]: if q.value == 38: self.assertTrue(q._provenance.model is models['model1'].name, "provenance improperly calculated") self.assertTrue(expected_quantities[0] in q._provenance.inputs, "provenance improperly calculated") else: self.assertTrue(q._provenance.model is models['model1'].name, "provenance improperly calculated") self.assertTrue(expected_quantities[1] in q._provenance.inputs, "provenance improperly calculated") for q in material_derived._symbol_to_quantity[symbols['C']]: if q.value == 57: self.assertTrue(q._provenance.model is models['model1'].name, "provenance improperly calculated") self.assertTrue(expected_quantities[0] in q._provenance.inputs, "provenance improperly calculated") else: self.assertTrue(q._provenance.model is models['model1'].name, "provenance improperly calculated") self.assertTrue(expected_quantities[1] in q._provenance.inputs, "provenance improperly calculated") for q in material_derived._symbol_to_quantity[symbols['G']]: if q.value == 95: self.assertTrue(q._provenance.model is models['model2'].name, "provenance improperly calculated") self.assertTrue(expected_quantities[0] in q._provenance.inputs, "provenance improperly calculated") else: self.assertTrue(q._provenance.model is models['model2'].name, "provenance improperly calculated") self.assertTrue(expected_quantities[1] in q._provenance.inputs, "provenance improperly calculated") for q in material_derived._symbol_to_quantity[symbols['D']]: if q.value == 70395: self.assertTrue(q._provenance.model is models['model5'].name, "provenance improperly calculated") self.assertTrue(expected_quantities[4] in q._provenance.inputs, "provenance improperly calculated") self.assertTrue(expected_quantities[6] in q._provenance.inputs, "provenance improperly calculated")
def test_evaluate_single_material_degenerate_property(self): """ Graph has one material on it: mat1 mat1 has trivial degenerate properties relative permittivity and relative permeability 2 experimental relative permittivity measurements 2 experimental relative permeability measurements Determines if TestGraph1 is correctly evaluated using the evaluate method. We expect 4 refractive_index properties to be calculated as the following: sqrt(3), sqrt(5), sqrt(6), sqrt(10) """ # Setup propnet = Graph() mat1 = Material() mat1.add_quantity(Quantity(DEFAULT_SYMBOLS['relative_permeability'], 1)) mat1.add_quantity(Quantity(DEFAULT_SYMBOLS['relative_permeability'], 2)) mat1.add_quantity(Quantity(DEFAULT_SYMBOLS['relative_permittivity'], 3)) mat1.add_quantity(Quantity(DEFAULT_SYMBOLS['relative_permittivity'], 5)) mat1_derived = propnet.evaluate(mat1) # Expected outputs s_outputs = [] s_outputs.append(Quantity('relative_permeability', 1)) s_outputs.append(Quantity('relative_permeability', 2)) s_outputs.append(Quantity('relative_permittivity', 3)) s_outputs.append(Quantity('relative_permittivity', 5)) s_outputs.append(Quantity('refractive_index', 3 ** 0.5)) s_outputs.append(Quantity('refractive_index', 5 ** 0.5)) s_outputs.append(Quantity('refractive_index', 6 ** 0.5)) s_outputs.append(Quantity('refractive_index', 10 ** 0.5)) st_outputs = [] st_outputs.append(DEFAULT_SYMBOLS['relative_permeability']) st_outputs.append(DEFAULT_SYMBOLS['relative_permittivity']) st_outputs.append(DEFAULT_SYMBOLS['refractive_index']) # Test for q_expected in s_outputs: q = None for q_derived in mat1_derived._symbol_to_quantity[q_expected.symbol]: if q_derived == q_expected: q = q_derived self.assertTrue(q is not None, "Quantity missing from evaluate.")
def test_evaluate_constraints_cyclic(self): """ Tests the evaluation algorithm on a cyclic graph involving constraints. The canonical graph and the canonical material are used for this test. """ model4 = EquationModel(name="model4", equations=["D=B*C*11"], constraints=["G==0"]) symbols = GraphTest.generate_canonical_symbols() models = GraphTest.generate_canonical_models() models['model4'] = model4 material = GraphTest.generate_canonical_material(symbols) g = Graph(symbol_types=symbols, models=models, composite_models=dict()) material_derived = g.evaluate(material) expected_quantities = [ Quantity(symbols['A'], 19), Quantity(symbols['A'], 23), Quantity(symbols['B'], 38), Quantity(symbols['B'], 46), Quantity(symbols['C'], 57), Quantity(symbols['C'], 69), Quantity(symbols['G'], 95), Quantity(symbols['G'], 115), Quantity(symbols['F'], 266), Quantity(symbols['F'], 322), Quantity(symbols['D'], 70395), Quantity(symbols['D'], 85215), Quantity(symbols['D'], 103155), ] self.assertTrue(material == GraphTest.generate_canonical_material(symbols), "evaluate() mutated the original material argument.") derived_quantities = material_derived.get_quantities() self.assertTrue(len(expected_quantities) == len(derived_quantities), "Evaluate did not correctly derive outputs.") for q in expected_quantities: self.assertTrue(q in material_derived._symbol_to_quantity[q.symbol], "Evaluate failed to derive all outputs.") self.assertTrue(q in derived_quantities)
def test_from_default(self): default = Quantity.from_default('temperature') self.assertEqual(default, Quantity('temperature', 300)) default = Quantity.from_default('relative_permeability') self.assertEqual(default, Quantity("relative_permeability", 1))
def test_evaluate_cyclic(self): """ Tests the evaluation algorithm on a cyclic graph. The canonical graph and the canonical material are used for this test. """ symbols = GraphTest.generate_canonical_symbols() models = GraphTest.generate_canonical_models() material = GraphTest.generate_canonical_material(symbols) g = Graph(symbol_types=symbols, models=models, composite_models=dict()) material_derived = g.evaluate(material) expected_quantities = [ # Starting Quantity(symbols['A'], 19), Quantity(symbols['A'], 23), # Derives -1 (M1) Quantity(symbols['B'], 38), Quantity(symbols['B'], 46), Quantity(symbols['C'], 57), Quantity(symbols['C'], 69), # Derives -2 (M3, M1) Quantity(symbols['F'], 266), Quantity(symbols['F'], 322), # Derives -2 (M4, M1) Quantity(symbols['D'], 23826), Quantity(symbols['D'], 28842), Quantity(symbols['D'], 34914), # Derives -1 (M2) Quantity(symbols['G'], 95), Quantity(symbols['G'], 115), # Derives -2 (M5, M1, M2) Quantity(symbols['D'], 70395), Quantity(symbols['D'], 85215), Quantity(symbols['D'], 103155), ] self.assertTrue(material == GraphTest.generate_canonical_material(symbols), "evaluate() mutated the original material argument.") derived_quantities = material_derived.get_quantities() self.assertTrue(len(expected_quantities) == len(derived_quantities), "Evaluate did not correctly derive outputs.") for q in expected_quantities: self.assertTrue(q in material_derived._symbol_to_quantity[q.symbol], "Evaluate failed to derive all outputs.") self.assertTrue(q in derived_quantities)
def super_evaluate(self, material, allow_model_failure=True): """ Given a SuperMaterial object as input, creates a new SuperMaterial object to include all derivable properties. Returns a reference to the new, augmented SuperMaterial object. Args: material (SuperMaterial): material for which properties will be expanded. Returns: (Material) reference to the newly derived material object. """ if not isinstance(material, CompositeMaterial): raise Exception("material provided is not a SuperMaterial: " + str(type(material))) # Evaluate material's sub-materials evaluated_materials = list() for m in material.materials: logger.debug("Evaluating sub-material: " + str(id(m))) if isinstance(m, CompositeMaterial): evaluated_materials.append(self.super_evaluate(m)) else: evaluated_materials.append(self.evaluate(m)) # Run all SuperModels in the graph on this SuperMaterial if # a material mapping can be established. Store any derived quantities. all_quantities = defaultdict(set) for (k, v) in material._symbol_to_quantity: all_quantities[k].add(v) to_return = CompositeMaterial(evaluated_materials) to_return._symbol_to_quantity = all_quantities logger.debug("Evaluating SuperMaterial") for model in self._composite_models.values(): logger.debug("\tEvaluating Model: " + model.name) # Establish material mappings for the given input set. mat_mappings = model.gen_material_mappings(to_return.materials) # Avoid ambiguous or impossible mappings, at least for now. if len(mat_mappings) != 1: continue mat_mapping = mat_mappings[0] # Go through input sets for property_input_sets in model.evaluation_list: logger.debug("\t\tGenerating input sets for: " + str(property_input_sets)) # Create a quantity pool from the appropriate materials. # Modify inputs for use in generate_input_sets temp_pool = defaultdict(set) combined_list = [] mat_list = [] symbol_list = [] for item in property_input_sets: combined_list.append(item) mat_list.append(CompositeModel.get_material(item)) symbol_list.append(CompositeModel.get_symbol(item)) for i in range(0, len(mat_list)): if mat_list[ i] == None: # Draw symbol from the CompositeMaterial mat = to_return else: mat = mat_mapping[mat_list[i]] for q in mat._symbol_to_quantity[symbol_list[i]]: temp_pool[combined_list[i]].add(q) input_sets = self.generate_input_sets(combined_list, temp_pool) for input_set in input_sets: logger.debug("\t\t\tEvaluating input set: " + str(input_set)) # Check if input_set can be evaluated -- input_set must pass the necessary model constraints if not model.check_constraints(input_set): logger.debug( "\t\t\tInput set failed -- did not pass model constraints." ) continue # Try to evaluate input_set: evaluate_set = dict(zip(combined_list, input_set)) output = model.evaluate(evaluate_set, allow_failure=allow_model_failure) success = output.pop('successful') if not success: logger.debug( "\t\t\tInput set failed -- did not produce a successful output." ) continue # input_set led to output from the Model -- add output to the SuperMaterial logger.debug("\t\t\tInput set produced successful output.") for symbol, quantity in output.items(): st = self._symbol_types.get(symbol) if not st: raise ValueError( "Symbol type {} not found".format(symbol)) q = Quantity(st, quantity) to_return._symbol_to_quantity[st].add(q) logger.debug("\t\t\tNew output: " + str(q)) # Evaluate the SuperMaterial's quantities and return the result. mappings = self.evaluate(to_return)._symbol_to_quantity to_return._symbol_to_quantity = mappings return to_return
def test_to(self): quantity = Quantity('band_gap', 3.0, 'eV') new = quantity.to('joules') self.assertEqual(new.magnitude, 4.80652959e-19) self.assertEqual(new.units, 'joule')
def test_pretty_string(self): quantity = Quantity('bulk_modulus', 100) self.assertEqual(quantity.pretty_string(3), "100 GPa")
def test_derive_quantities(self): # Simple one quantity test quantity = Quantity("band_gap", 3.2) graph = Graph() new, qpool = graph.derive_quantities([quantity]) new_mat = graph.evaluate(Material([quantity]))