Пример #1
0
 def test_add_default_quantities(self):
     material = Material(add_default_quantities=True)
     self.assertEqual(list(material['temperature'])[0],
                      QuantityFactory.create_quantity("temperature", 300,
                                                      provenance=ProvenanceElement(model='default')))
     self.assertEqual(list(material['relative_permeability'])[0],
                      QuantityFactory.create_quantity("relative_permeability", 1,
                      provenance=ProvenanceElement(model='default')))
Пример #2
0
 def test_get_weight(self):
     q1 = Quantity("band_gap", 3.2)
     wt = get_weight(q1)
     self.assertEqual(wt, 1)
     p2 = ProvenanceElement(model="model_2", inputs=[q1])
     q2 = Quantity("refractive_index", 4, provenance=p2)
     wt2 = get_weight(q2, {"model_2": 0.5})
     self.assertEqual(wt2, 0.5)
     p3 = ProvenanceElement(model="model_3", inputs=[q2])
     q3 = Quantity("bulk_modulus", 100, provenance=p3)
     wt3 = get_weight(q3, {"model_3": 0.25, "model_2": 0.5})
     self.assertEqual(wt3, 0.125)
Пример #3
0
    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
Пример #4
0
    def __init__(self, symbol_type, value, tags=None, provenance=None):
        """
        Parses inputs for constructing a BaseQuantity object.

        Args:
            symbol_type (Symbol or str): pointer to a Symbol
                object in DEFAULT_SYMBOLS or string giving the name
                of a Symbol object. Identifies the type of data
                stored in the quantity.
            value (id): value of the quantity.
            tags (list<str>): list of strings storing metadata from
                evaluation.
            provenance (ProvenanceElement): provenance associated with the
                object (e. g. inputs, model, see ProvenanceElement). If not specified,
                a default object will be created. All objects will receive
                the time created and the internal ID as fields 'source.date_created'
                and 'source.source_key', respectively, if the fields are not already
                written.
        """

        if not isinstance(symbol_type, Symbol):
            symbol_type = self.get_symbol_from_string(symbol_type)

        if provenance and not isinstance(provenance, ProvenanceElement):
            raise TypeError("Expected ProvenanceElement for provenance. "
                            "Instead received: {}".format(type(provenance)))

        self._value = value
        self._symbol_type = symbol_type
        self._tags = []
        if tags:
            if isinstance(tags, str):
                tags = [tags]
            self._tags.extend(tags)
        self._provenance = provenance
        self._internal_id = uuid.uuid4().hex

        if self._provenance is not None:
            if not isinstance(self._provenance.source, dict):
                self._provenance.source = {"source": self._provenance.source}

            if 'date_created' not in self._provenance.source.keys() or \
                    self._provenance.source['date_created'] in (None, ""):
                self._provenance.source['date_created'] = datetime.now(
                ).strftime("%Y-%m-%d %H:%M:%S")

            if 'source_key' not in self._provenance.source.keys() or \
                    self._provenance.source['source_key'] in (None, ""):
                self._provenance.source['source_key'] = self._internal_id
        else:
            self._provenance = ProvenanceElement(
                source={
                    "source": None,
                    "source_key": self._internal_id,
                    "date_created": datetime.now().strftime(
                        "%Y-%m-%d %H:%M:%S")
                })
Пример #5
0
    def from_weighted_mean(cls, quantities):
        """
        Function to invoke weighted mean quantity from other
        quantities

        Args:
            quantities ([NumQuantity]): list of quantities of the same type

        Returns: (NumQuantity) a quantity containing the weighted mean and
            standard deviation.
        """

        if not all(isinstance(q, cls) for q in quantities):
            raise ValueError(
                "Weighted mean cannot be applied to non-NumQuantity objects")

        input_symbol = quantities[0].symbol
        if not all(input_symbol == q.symbol for q in quantities):
            raise ValueError("Can only calculate a weighted mean if "
                             "all quantities refer to the same symbol.")

        # TODO: an actual weighted mean; just a simple mean at present
        # TODO: support propagation of uncertainties (this will only work
        # once at present)

        # # TODO: test this with units, not magnitudes ... remember units
        # # may not be canonical units(?)
        # if isinstance(quantities[0].value, list):
        #     # hack to get arrays working for now
        #     vals = [q.value for q in quantities]
        # else:
        #     vals = [q.value.magnitude for q in quantities]
        vals = [q.value for q in quantities]

        # Explicit formulas for mean / standard dev for pint support
        new_value = sum(vals) / len(vals)
        std_dev = (sum([(v - new_value)**2
                        for v in vals]) / len(vals))**(1 / 2)

        # Accumulate provenance and tags for new quantities
        new_tags = set()
        new_provenance = ProvenanceElement(model='aggregation', inputs=[])
        for quantity in quantities:
            if quantity.tags:
                for tag in quantity.tags:
                    new_tags.add(tag)
            new_provenance.inputs.append(quantity)

        return cls(symbol_type=input_symbol,
                   value=new_value,
                   tags=list(new_tags),
                   provenance=new_provenance,
                   uncertainty=std_dev)
Пример #6
0
    def from_default(symbol):
        """
        Method to invoke a default quantity from a symbol name

        Args:
            symbol (Symbol or str): symbol or string corresponding to
                the symbol name

        Returns:
            BaseQuantity corresponding to default quantity from default
        """
        val = Registry("symbol_values").get(symbol)
        if val is None:
            raise ValueError("No default value for {}".format(symbol))
        prov = ProvenanceElement(model='default')
        return QuantityFactory.create_quantity(symbol, val, provenance=prov)
Пример #7
0
    def from_default(cls, symbol):
        """
        Class method to invoke a default quantity from a symbol name

        Args:
            symbol (Symbol or str): symbol or string corresponding to
                the symbol name

        Returns:
            Quantity corresponding to default quantity from default
        """
        val = DEFAULT_SYMBOL_VALUES.get(symbol)
        if val is None:
            raise ValueError("No default value for {}".format(symbol))
        prov = ProvenanceElement(model='default', inputs=[])
        return cls(symbol, val, provenance=prov)
Пример #8
0
    def to_provenance_element(self, lookup=None):
        """
        Converts the current object to a ProvenanceElement object, looking up missing input
        values if needed.

        Args:
            lookup: (dict or function) lookup container for missing provenance input information

        Returns: (ProvenanceElement) reconstructed provenance object

        """
        if self._inputs:
            inputs = [v.to_quantity(lookup=lookup) for v in self._inputs]
        else:
            inputs = None
        return ProvenanceElement(model=self.model,
                                 inputs=inputs,
                                 source=self.source)
Пример #9
0
    def get_materials_for_mpids(self, mpids, filter_null_values=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_quantities = self.get_quantities_for_mpids(
            mpids,
            filter_null_values=filter_null_values,
            include_date_created=True)
        materials = []

        for material_quantities in materials_quantities:
            material = Material()
            try:
                date_created = material_quantities.pop('created_at')
            except KeyError:
                date_created = None
            for symbol_name, value in material_quantities.items():
                provenance = ProvenanceElement(
                    source={
                        'source': 'Materials Project',
                        'source_key': material_quantities.get(
                            'material_id', None),
                        'date_created': date_created
                    })
                quantity = QuantityFactory.create_quantity(
                    self.mapping[symbol_name],
                    value,
                    units=Registry("units").get(self.mapping[symbol_name],
                                                None),
                    provenance=provenance)
                material.add_quantity(quantity)
            materials.append(material)

        return materials
Пример #10
0
    def transform_properties_to_material(self, material_data):
        """
        Produces a propnet Material object from a dictionary of AFLOW materials data.

        Args:
            material_data (dict): AFLOW materials data, keyed by AFLOW keyword, as
                Python native types, not as strings as they are stored in AFLOW.

        Returns:
            propnet.core.materials.Material: propnet material containing the AFLOW data
        """
        qs = []
        auid = material_data.get('auid')
        date_created = material_data.get('aflowlib_date')
        if date_created:
            date, tz = date_created.rsplit("GMT", 1)
            tz = "GMT{:+05d}".format(int(tz) * 100)
            date_object = datetime.strptime(date + tz, "%Y%m%d_%H:%M:%S_%Z%z")
            date_created = date_object.strftime("%Y-%m-%d %H:%M:%S")

        for prop, value in material_data.items():
            if value is not None and prop in self.mapping:
                provenance = ProvenanceElement(
                    source={'source': 'AFLOW',
                            'source_key': auid,
                            'date_created': date_created}
                )

                if prop in self.transform_func:
                    value = self.transform_func[prop](value)
                if value is None:
                    continue
                q = QuantityFactory.create_quantity(
                    self.mapping.get(prop),
                    value,
                    units=self.unit_map.get(prop), provenance=provenance
                )
                qs.append(q)

        return Material(qs)
Пример #11
0
    def process(self, item):
        if self.graph_parallel and not self.allow_child_process and \
                current_process().name != "MainProcess":
            logger.warning(
                "It appears derive_quantities() is running "
                "in a child process, possibly in a parallelized "
                "Runner.\nThis is not recommended and will deteriorate "
                "performance.")
        # 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()

        if 'created_at' in item.keys():
            date_created = item['created_at']
        else:
            date_created = None

        provenance = ProvenanceElement(
            source={
                "source": self.source_name,
                "source_key": item['task_id'],
                "date_created": date_created
            })

        for mkey, property_name in self.materials_symbol_map.items():
            value = pydash.get(item, mkey)
            if value:
                material.add_quantity(
                    QuantityFactory.create_quantity(
                        property_name,
                        value,
                        units=Registry("units").get(property_name, None),
                        provenance=provenance))

        # Add custom things, e. g. computed entry
        computed_entry = get_entry(item)
        if computed_entry:
            material.add_quantity(
                QuantityFactory.create_quantity("computed_entry",
                                                computed_entry,
                                                provenance=provenance))
        else:
            logger.info("Unable to create computed entry for {}".format(
                item['task_id']))
        material.add_quantity(
            QuantityFactory.create_quantity("external_identifier_mp",
                                            item['task_id'],
                                            provenance=provenance))

        input_quantities = material.symbol_quantities_dict

        # Use graph to generate expanded quantity pool
        logger.info("Evaluating graph for %s", item['task_id'])

        new_material = self._graph_evaluator.evaluate(
            material, timeout=self.graph_timeout)

        # Format document and return
        logger.info("Creating doc for %s", item['task_id'])
        # Gives the initial inputs that were used to derive properties of a
        # certain material.

        doc = {
            "inputs": [
                StorageQuantity.from_quantity(q)
                for q in chain.from_iterable(input_quantities.values())
            ]
        }

        for symbol, quantities in new_material.symbol_quantities_dict.items():
            # If no new quantities of a given symbol were derived (i.e. if the initial
            # input quantity/ies is/are the only one/s listed in the new material) then don't add
            # that quantity to the propnet entry document as a derived quantity.
            if len(quantities) == len(input_quantities[symbol]):
                continue
            sub_doc = {}
            try:
                # Write out all quantities as dicts including the
                # internal ID for provenance tracing
                qs = [
                    jsanitize(StorageQuantity.from_quantity(q), strict=True)
                    for q in quantities
                ]
            except AttributeError as ex:
                # Check to see if this is an error caused by an object
                # that is not JSON serializable
                msg = ex.args[0]
                if "object has no attribute 'as_dict'" in msg:
                    # Write error to db and logger
                    errmsg = "Quantity of Symbol '{}' is not ".format(symbol.name) + \
                        "JSON serializable. Cannot write quantities to database!"
                    logger.error(errmsg)
                    sub_doc['error'] = errmsg
                    qs = []
                else:
                    # If not, re-raise the error
                    raise ex
            sub_doc['quantities'] = qs
            doc[symbol.name] = sub_doc

        aggregated_quantities = new_material.get_aggregated_quantities()

        for symbol, quantity in aggregated_quantities.items():
            if symbol.name not in doc:
                # No new quantities were derived
                continue
            # Store mean and std dev for aggregated quantities
            sub_doc = {
                "mean": unumpy.nominal_values(quantity.value).tolist(),
                "std_dev": unumpy.std_devs(quantity.value).tolist(),
                "units":
                quantity.units.format_babel() if quantity.units else None,
                "title": quantity.symbol.display_names[0]
            }
            # Symbol Name -> Sub_Document, listing all Quantities of that type.
            doc[symbol.name].update(sub_doc)

        doc.update({
            "task_id": item["task_id"],
            "pretty_formula": item.get("pretty_formula"),
            "deprecated": item.get("deprecated", False)
        })

        if self.include_sandboxed:
            doc.update({'sbxn': item.get("sbxn", [])})

        return jsanitize(doc, strict=True)
Пример #12
0
    def setUp(self):
        # Inspiration was taken from the GraphTest class
        # I tried to construct the dictionaries for comparison
        # without writing out every one explicity by reusing
        # information where it was applicable.
        # If this is too unreadable, can change to writing it
        # out explicity in a JSON file and importing it. Would
        # still need to replace some fields dynamically.
        symbols = StorageTest.generate_symbols()

        self.custom_syms_as_dicts = {
            k: {'@module': 'propnet.core.symbols',
                '@class': 'Symbol',
                'name': k,
                'display_names': [k],
                'display_symbols': [k],
                'units': (1, ()),
                'shape': 1,
                'object_type': None,
                'comment': None,
                'category': 'property',
                'constraint': None,
                'default_value': None} for k in ['A', 'B', 'C']
        }
        self.custom_syms_as_dicts['C'].update(
            {"units": None,
             "shape": None,
             "object_type": "str",
             "category": "object"})

        self.custom_symbols_json = copy.deepcopy(self.custom_syms_as_dicts)
        for k in ['A', 'B']:
            self.custom_symbols_json[k]['units'] = [1, []]

        a = [QuantityFactory.create_quantity(symbols['A'], 19),
             QuantityFactory.create_quantity(symbols['A'], 23)]
        b = [QuantityFactory.create_quantity(symbols['B'], 38,
                                             provenance=ProvenanceElement(model='model1',
                                                                          inputs=[a[0]])),
             QuantityFactory.create_quantity(symbols['B'], 46,
                                             provenance=ProvenanceElement(model='model1',
                                                                          inputs=[a[1]]))]
        self.quantities_custom_symbol = {"A": a,
                                         "B": b}

        self.sq_custom_sym_as_dicts = {
            k: [{'@module': 'propnet.dbtools.storage',
                 '@class': 'StorageQuantity',
                 'internal_id': vv._internal_id,
                 'data_type': 'NumQuantity',
                 'symbol_type': symbols[k],
                 'value': vv.magnitude,
                 'units': 'dimensionless',
                 'provenance': ProvenanceStore.from_provenance_element(vv.provenance),
                 'tags': [],
                 'uncertainty': None} for vv in v] for k, v in self.quantities_custom_symbol.items()
        }

        provenances_json = {
            "A": [{'@module': 'propnet.dbtools.storage',
                   '@class': 'ProvenanceStore',
                   'model': None,
                   'inputs': None,
                   'source': aa.provenance.source} for aa in a]}
        provenances_json['B'] = [
            {'@module': 'propnet.dbtools.storage',
             '@class': 'ProvenanceStore',
             'model': 'model1',
             'inputs': [{'@module': 'propnet.dbtools.storage',
                         '@class': 'ProvenanceStoreQuantity',
                         'data_type': 'NumQuantity',
                         'symbol_type': self.custom_symbols_json['A'],
                         'internal_id': q.provenance.inputs[0]._internal_id,
                         'tags': [],
                         'provenance': p}],
             'source': q.provenance.source} for q, p in zip(b, provenances_json['A'])]

        self.sq_custom_sym_json = copy.deepcopy(self.sq_custom_sym_as_dicts)
        for sym in ['A', 'B']:
            for q, p in zip(self.sq_custom_sym_json[sym], provenances_json[sym]):
                q['symbol_type'] = self.custom_symbols_json[sym]
                q['provenance'] = p

        band_gaps = [QuantityFactory.create_quantity('band_gap', 3.3, 'eV'),
                     QuantityFactory.create_quantity('band_gap', 2.1, 'eV')]

        bg_ri_model = DEFAULT_MODEL_DICT['band_gap_refractive_index_moss']
        refractive_indices = [bg_ri_model.evaluate({"Eg": bg}).pop('refractive_index') for bg in band_gaps]

        self.quantities_canonical_symbol = {"band_gaps": band_gaps,
                                            "refractive_indices": refractive_indices}

        self.sq_canonical_sym_as_dicts_no_value = copy.deepcopy(self.sq_custom_sym_as_dicts)
        self.sq_canonical_sym_as_dicts_no_value['band_gaps'] = self.sq_canonical_sym_as_dicts_no_value.pop('A')
        self.sq_canonical_sym_as_dicts_no_value['refractive_indices'] = self.sq_canonical_sym_as_dicts_no_value.pop('B')

        for d, sq in zip(self.sq_canonical_sym_as_dicts_no_value['band_gaps'], band_gaps):
            d.update({
                "internal_id": sq._internal_id,
                "symbol_type": "band_gap",
                "units": "electron_volt",
                "provenance": ProvenanceStore.from_provenance_element(sq.provenance)
            })
            d.pop('value')

        for d, sq in zip(self.sq_canonical_sym_as_dicts_no_value['refractive_indices'], refractive_indices):
            d.update({
                "internal_id": sq._internal_id,
                "symbol_type": "refractive_index",
                "units": "dimensionless",
                "provenance": ProvenanceStore.from_provenance_element(sq.provenance)
            })
            d.pop('value')

        self.sq_canonical_sym_values = {"band_gaps": [3.3, 2.1],
                                        "refractive_indices": [2.316340583741216, 2.593439239956374]}

        provenances_json['band_gaps'] = [
            {'@module': 'propnet.dbtools.storage',
             '@class': 'ProvenanceStore',
             'model': None,
             'inputs': None,
             'source': bg.provenance.source}
            for bg in band_gaps
        ]

        provenances_json['refractive_indices'] = [{
            '@module': 'propnet.dbtools.storage',
            '@class': 'ProvenanceStore',
            'model': 'band_gap_refractive_index_moss',
            'inputs': [{'@module': 'propnet.dbtools.storage',
                        '@class': 'ProvenanceStoreQuantity',
                        'data_type': 'NumQuantity',
                        'symbol_type': 'band_gap',
                        'internal_id': bg._internal_id,
                        'tags': [],
                        'provenance': pj}],
            'source': ri.provenance.source}
            for bg, pj, ri in zip(band_gaps,
                                  provenances_json['band_gaps'],
                                  refractive_indices)
        ]

        self.sq_canonical_sym_json_no_value = copy.deepcopy(self.sq_canonical_sym_as_dicts_no_value)

        for sym in ["band_gaps", "refractive_indices"]:
            for q, p in zip(self.sq_canonical_sym_json_no_value[sym], provenances_json[sym]):
                q['provenance'] = p

        self.quantity_with_uncertainty = NumQuantity.from_weighted_mean(b)
        self.sq_with_uncertainty_as_dict_no_numbers = {
            '@module': 'propnet.dbtools.storage',
            '@class': 'StorageQuantity',
            'internal_id': self.quantity_with_uncertainty._internal_id,
            'data_type': 'NumQuantity',
            'symbol_type': symbols['B'],
            'units': 'dimensionless',
            'provenance': ProvenanceStore.from_provenance_element(
                self.quantity_with_uncertainty.provenance),
            'tags': []}

        provenances_json = {
            '@module': 'propnet.dbtools.storage',
            '@class': 'ProvenanceStore',
            'model': 'aggregation',
            'inputs': [
                {'@module': 'propnet.dbtools.storage',
                 '@class': 'ProvenanceStoreQuantity',
                 'data_type': 'NumQuantity',
                 'symbol_type': self.custom_symbols_json['B'],
                 'internal_id': b['internal_id'],
                 'tags': [],
                 'provenance': b['provenance']}
                for b in self.sq_custom_sym_json['B']],
            'source': self.quantity_with_uncertainty.provenance.source
        }

        self.sq_with_uncertainty_json_no_numbers = copy.deepcopy(self.sq_with_uncertainty_as_dict_no_numbers)
        self.sq_with_uncertainty_json_no_numbers.update({"symbol_type": self.custom_symbols_json['B'],
                                                         "provenance": provenances_json})
        self.sq_with_uncertainty_numbers = {"value": 42.0,
                                            "uncertainty": 4.0}

        obj_symbol = symbols['C']
        self.object_quantity = QuantityFactory.create_quantity(obj_symbol, "Test string")
        self.sq_object_as_dict = copy.deepcopy(self.sq_custom_sym_as_dicts['A'][0])
        self.sq_object_as_dict.update({
            "data_type": "ObjQuantity",
            "symbol_type": symbols['C'],
            "internal_id": self.object_quantity._internal_id,
            "value": "Test string",
            "units": None,
            "provenance": ProvenanceStore.from_provenance_element(self.object_quantity.provenance)
        })
        self.sq_object_json = copy.deepcopy(self.sq_object_as_dict)
        self.sq_object_json.update(
            {"symbol_type": self.custom_syms_as_dicts['C'],
             "provenance": {'@module': 'propnet.dbtools.storage',
                            '@class': 'ProvenanceStore',
                            'model': None,
                            'inputs': None,
                            'source': self.object_quantity.provenance.source}}
        )

        # This setting allows dict differences to be shown in full
        self.maxDiff = None
Пример #13
0
    def test_as_dict_from_dict(self):
        q = Quantity(self.custom_symbol, 5, tags='experimental', uncertainty=1)
        d = q.as_dict()
        d_storage = q.as_dict(for_storage=True)
        d_storage_omit = q.as_dict(for_storage=True, omit_value=True)
        self.assertEqual(
            d, {
                "@module": "propnet.core.quantity",
                "@class": "Quantity",
                "value": 5,
                "units": "dimensionless",
                "provenance": None,
                "symbol_type": self.custom_symbol.name
            })

        self.assertEqual(
            d_storage, {
                "@module": "propnet.core.quantity",
                "@class": "Quantity",
                "value": 5,
                "units": "dimensionless",
                "provenance": None,
                "internal_id": q._internal_id,
                "symbol_type": self.custom_symbol.name
            })

        self.assertEqual(
            d_storage_omit, {
                "@module": "propnet.core.quantity",
                "@class": "Quantity",
                "value": None,
                "units": None,
                "provenance": None,
                "internal_id": q._internal_id,
                "symbol_type": self.custom_symbol.name
            })

        q = Quantity(self.custom_symbol,
                     5,
                     tags='experimental',
                     uncertainty=1,
                     provenance=ProvenanceElement())
        d = q.as_dict()
        d_storage = q.as_dict(for_storage=True)
        self.assertEqual(
            d, {
                "@module": "propnet.core.quantity",
                "@class": "Quantity",
                "value": 5,
                "units": "dimensionless",
                "provenance": q._provenance,
                "symbol_type": self.custom_symbol.name
            })

        # Need more tests for provenance as_dict() method
        self.assertEqual(
            d_storage, {
                "@module": "propnet.core.quantity",
                "@class": "Quantity",
                "value": 5,
                "units": "dimensionless",
                "provenance": q._provenance.as_dict(),
                "internal_id": q._internal_id,
                "symbol_type": self.custom_symbol.name
            })

        self.assertIsInstance(d_storage['provenance'], dict)
Пример #14
0
    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()

        if 'created_at' in item.keys():
            date_created = item['created_at']
        else:
            date_created = ""

        provenance = ProvenanceElement(
            source={
                "source": self.source_name,
                "source_key": item['task_id'],
                "date_created": date_created
            })

        for mkey, property_name in self.materials_symbol_map.items():
            value = get(item, mkey)
            if value:
                material.add_quantity(
                    QuantityFactory.create_quantity(property_name,
                                                    value,
                                                    provenance=provenance))

        # Add custom things, e. g. computed entry
        computed_entry = get_entry(item)
        material.add_quantity(
            QuantityFactory.create_quantity("computed_entry",
                                            computed_entry,
                                            provenance=provenance))
        material.add_quantity(
            QuantityFactory.create_quantity("external_identifier_mp",
                                            item['task_id'],
                                            provenance=provenance))

        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'])
        # Gives the initial inputs that were used to derive properties of a
        # certain material.

        doc = {
            "inputs":
            [StorageQuantity.from_quantity(q) for q 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
            # TODO: Condition insufficiently general.
            #       Can end up with initial quantities added as "new quantities"
            if len(all_qs) == 1 and list(all_qs)[0] in input_quantities:
                continue
            # Write out all quantities as dicts including the
            # internal ID for provenance tracing
            qs = [StorageQuantity.from_quantity(q).as_dict() for q in all_qs]
            # THE listing of all Quantities of a given symbol.
            sub_doc = {
                "quantities": qs,
                "mean": unumpy.nominal_values(quantity.value).tolist(),
                "std_dev": unumpy.std_devs(quantity.value).tolist(),
                "units":
                quantity.units.format_babel() if quantity.units else None,
                "title": quantity._symbol_type.display_names[0]
            }
            # Symbol Name -> Sub_Document, listing all Quantities of that type.
            doc[symbol.name] = sub_doc

        doc.update({
            "task_id": item["task_id"],
            "pretty_formula": item["pretty_formula"]
        })
        return jsanitize(doc, strict=True)
Пример #15
0
    def evaluate(self, symbol_quantity_dict, allow_failure=True):
        """
        Given a set of property_values, performs error checking to see
        if the corresponding input symbol_values represents a valid
        input set based on the self.connections() method. If so, returns
        a dictionary representing the value of plug_in applied to the
        input_symbols. The dictionary contains a "successful" key
        representing if plug_in was successful.

        The key distinction between evaluate and plug_in is properties
        in properties out vs. symbols in symbols out.  In addition,
        evaluate also handles any requisite unit_mapping

        Args:
            symbol_quantity_dict ({property_name: Quantity}): a mapping of
                symbol names to quantities to be substituted
            allow_failure (bool): whether or not to catch
                errors in model evaluation

        Returns:
            dictionary of output properties with associated values
            generated from the input, along with "successful" if the
            substitution succeeds
        """
        # Remap symbols and units if symbol map isn't none
        symbol_quantity_dict = self.map_properties_to_symbols(
            symbol_quantity_dict)

        # TODO: Is it really necessary to strip these?
        # TODO: maybe this only applies to pymodels or things with objects?
        # strip units from input and keep for reassignment
        symbol_value_dict = {}
        for symbol, quantity in symbol_quantity_dict.items():
            # If unit map convert and then scrub units
            if self.unit_map.get(symbol):
                quantity = quantity.to(self.unit_map[symbol])
                symbol_value_dict[symbol] = quantity.magnitude
            # Otherwise use values
            else:
                symbol_value_dict[symbol] = quantity.value
        # Plug in and check constraints
        try:
            out = self.plug_in(symbol_value_dict)
        except Exception as err:
            if allow_failure:
                return {
                    "successful": False,
                    "message": "{} evaluation failed: {}".format(self, err)
                }
            else:
                raise err
        if not self.check_constraints({**symbol_value_dict, **out}):
            return {
                "successful": False,
                "message": "Constraints not satisfied"
            }

        provenance = ProvenanceElement(model=self.name,
                                       inputs=list(
                                           symbol_quantity_dict.values()))
        out = self.map_symbols_to_properties(out)
        for symbol, value in out.items():
            try:
                quantity = Quantity(symbol,
                                    value,
                                    self.unit_map.get(symbol),
                                    provenance=provenance)
            except SymbolConstraintError as err:
                if allow_failure:
                    errmsg = "{} symbol constraint failed: {}".format(
                        self, err)
                    return {"successful": False, "message": errmsg}
                else:
                    raise err

            if quantity.contains_nan_value():
                return {
                    "successful": False,
                    "message": "Evaluation returned invalid values (NaN)"
                }
            out[symbol] = quantity

        out['successful'] = True
        return out
Пример #16
0
    def evaluate(self, symbol_quantity_dict_in, allow_failure=True):
        """
        Given a set of property_values, performs error checking to see
        if the corresponding input symbol_values represents a valid
        input set based on the self.connections() method. If so, returns
        a dictionary representing the value of plug_in applied to the
        input_symbols. The dictionary contains a "successful" key
        representing if plug_in was successful.

        The key distinction between evaluate and plug_in is properties
        in properties out vs. symbols in symbols out.  In addition,
        evaluate also handles any requisite unit_mapping

        Args:
            symbol_quantity_dict ({property_name: Quantity}): a mapping of
                symbol names to quantities to be substituted
            allow_failure (bool): whether or not to catch
                errors in model evaluation

        Returns:
            dictionary of output properties with associated values
            generated from the input, along with "successful" if the
            substitution succeeds
        """
        # Remap symbols and units if symbol map isn't none

        symbol_quantity_dict = self.map_properties_to_symbols(
            symbol_quantity_dict_in)

        input_symbol_quantity_dict = {k: v for k, v in symbol_quantity_dict.items()
                                      if not (k in self.constraint_symbols
                                              and k not in self.all_input_symbols)}

        for (k, v) in symbol_quantity_dict.items():
            # replacing = self.symbol_property_map.get(k, k)
            replacing = self.symbol_property_map.get(k)
            # to_quantity() returns original object if it's already a BaseQuantity
            # unlike Quantity() which will return a deep copy
            symbol_quantity_dict[k] = QuantityFactory.to_quantity(replacing, v)

        # TODO: Is it really necessary to strip these?
        # TODO: maybe this only applies to pymodels or things with objects?
        # strip units from input and keep for reassignment
        symbol_value_dict = {}

        for symbol, quantity in symbol_quantity_dict.items():
            # If unit map convert and then scrub units
            if self.unit_map.get(symbol):
                quantity = quantity.to(self.unit_map[symbol])
                symbol_value_dict[symbol] = quantity.magnitude
            # Otherwise use values
            else:
                symbol_value_dict[symbol] = quantity.value

        contains_complex_input = any(NumQuantity.is_complex_type(v) for v in symbol_value_dict.values())
        input_symbol_value_dict = {k: symbol_value_dict[k] for k in input_symbol_quantity_dict.keys()}

        # Plug in and check constraints
        try:
            with PrintToLogger():
                out = self.plug_in(input_symbol_value_dict)
        except Exception as err:
            if allow_failure:
                return {"successful": False,
                        "message": "{} evaluation failed: {}".format(self, err)}
            else:
                raise err
        if not self.check_constraints({**symbol_value_dict, **out}):
            return {"successful": False,
                    "message": "Constraints not satisfied"}

        provenance = ProvenanceElement(
            model=self.name, inputs=list(input_symbol_quantity_dict.values()),
            source="propnet")

        out = self.map_symbols_to_properties(out)
        unit_map_as_properties = self.map_symbols_to_properties(self.unit_map)
        for symbol, value in out.items():
            try:
                quantity = QuantityFactory.create_quantity(
                    symbol, value, unit_map_as_properties.get(symbol),
                    provenance=provenance)
            except SymbolConstraintError as err:
                if allow_failure:
                    errmsg = "{} symbol constraint failed: {}".format(self, err)
                    return {"successful": False,
                            "message": errmsg}
                else:
                    raise err

            if quantity.contains_nan_value():
                return {"successful": False,
                        "message": "Evaluation returned invalid values (NaN)"}
            # TODO: Update when we figure out how we're going to handle complex quantities
            # Model evaluation will fail if complex values are returned when no complex input was given
            # Can surely handle this more gracefully, or assume that the users will apply constraints
            if quantity.contains_imaginary_value() and not contains_complex_input:
                return {"successful": False,
                        "message": "Evaluation returned invalid values (complex)"}

            out[symbol] = quantity

        out['successful'] = True
        return out