Beispiel #1
0
    def __init__(self,
                 symbol_type,
                 value,
                 units=None,
                 tags=None,
                 provenance=None,
                 uncertainty=None):
        """
        Parses inputs for constructing a Property object.

        Args:
            symbol_type (Symbol or str): pointer to an existing Symbol
                object in default_symbols or string giving the name
                of a SymbolType object identifies the type of data
                stored in the property.
            value (id): value of the property.
            units: (None): units associated with the quantity's value
            tags (list<str>): list of strings storing metadata from
                Quantity evaluation.
            provenance (ProvenanceElement): provenance associated with the
                object (e. g. inputs, model, see ProvenanceElement)
        """
        # Invoke default symbol if symbol is a string
        if isinstance(symbol_type, str):
            if symbol_type not in DEFAULT_SYMBOLS.keys():
                raise ValueError(
                    "Quantity type {} not recognized".format(symbol_type))
            symbol_type = DEFAULT_SYMBOLS[symbol_type]

        # Set default units if not supplied
        units = units or symbol_type.units

        # Invoke pint quantity if supplied or input is float/int
        if isinstance(value, (float, int, list, np.ndarray)):
            self._value = ureg.Quantity(value, units)
        elif isinstance(value, ureg.Quantity):
            self._value = value.to(units)
        else:
            self._value = value

        if isinstance(uncertainty, (float, int, list, np.ndarray)):
            self._uncertainty = ureg.Quantity(uncertainty, units)
        elif isinstance(uncertainty, ureg.Quantity):
            self._uncertainty = uncertainty.to(units)
        else:
            self._uncertainty = uncertainty

        # TODO: Symbol-level constraints are hacked together atm,
        #       constraints as a whole need to be refactored and
        #       put into a separate module
        if symbol_type.constraint is not None:
            if not symbol_type.constraint(
                    **{symbol_type.name: self.magnitude}):
                raise SymbolConstraintError(
                    "Quantity with {} value does not satisfy {}".format(
                        value, symbol_type.constraint))

        self._symbol_type = symbol_type
        self._tags = tags
        self._provenance = provenance
Beispiel #2
0
    def _get_elastic_data(entry, prop=None):
        """
        Extracts compliance and stiffness tensor from AFLOW AEL tensor file. Returns
        tensors in Voigt notation.

        Args:
            entry (aflow.entries.Entry): entry containing the tensor data as a string
                or JSON dict in the "AEL_elastic_tensor_json" field
            prop (`str`): optional, specifies which tensor to return. Possible values:
                `compliance`, `stiffness`, None (default, returns both)

        Returns:
            `dict` or `pint.Quantity`: If `prop` was specified, contains a pint Quantity
                representing the tensor. If `prop=None`, contains a dict of pint Quantity
                objects, keyed by tensor name `compliance_tensor_voigt` and
                `elastic_tensor_voigt` (stiffness tensor)
        """
        data_in = entry.raw.get('AEL_elastic_tensor_json')

        if isinstance(data_in, str):
            import json
            data_in = json.loads(data_in)

        if data_in is None:
            c_tensor = None
            s_tensor = None
        else:
            units = data_in['units']

            c_tensor_in = data_in['elastic_compliance_tensor']
            s_tensor_in = data_in['elastic_stiffness_tensor']

            c_idx = [idx for idx in ['s_'+str(i)+str(j) for i in range(1, 7) for j in range(1, 7)]]
            s_idx = [idx for idx in ['c_'+str(i)+str(j) for i in range(1, 7) for j in range(1, 7)]]

            c_tensor = np.reshape([c_tensor_in[idx] for idx in c_idx], (6, 6))
            s_tensor = np.reshape([s_tensor_in[idx] for idx in s_idx], (6, 6))

            c_tensor = ureg.Quantity(c_tensor, units).to(AflowAdapter.unit_map['compliance_tensor_voigt'])
            s_tensor = ureg.Quantity(s_tensor, units).to(AflowAdapter.unit_map['elastic_tensor_voigt'])

        if prop == 'compliance':
            return c_tensor
        elif prop == 'stiffness':
            return s_tensor
        return {
            'compliance_tensor_voigt': c_tensor,
            'elastic_tensor_voigt': s_tensor
        }
Beispiel #3
0
    def __init__(self, symbol_type, value, tags,
                 provenance=None):
        """
        Parses inputs for constructing a Property object.

        Args:
            symbol_type (SymbolType): pointer to an existing PropertyMetadata object or String giving
                    the name of a SymbolType object, identifies the type of data stored
                    in the property.
            value (id): value of the property.
            tags (list<str>): list of strings storing metadata from Symbol evaluation.
            provenance (id): time of creation of the object.
        """

        # TODO: move Symbol + SymbolType to separate files to remove circular import
        from propnet.symbols import DEFAULT_SYMBOL_TYPES
        if isinstance(symbol_type, str):
            if symbol_type not in DEFAULT_SYMBOL_TYPES.keys():
                raise ValueError("Symbol type {} not recognized".format(symbol_type))
            symbol_type = DEFAULT_SYMBOL_TYPES[symbol_type]
        
        if type(value) == float or type(value) == int:
            value = ureg.Quantity(value, symbol_type.units)
        elif type(value) == ureg.Quantity:
            value = value.to(symbol_type.units)

        self._symbol_type = symbol_type
        self._value = value
        self._tags = tags
        self._provenance = provenance
Beispiel #4
0
    def uncertainty(self):
        """
        Returns uncertainty as pint Quantity

        Returns: (pint.Quantity) uncertainty as pint object or None if does not exist
        """

        return ureg.Quantity(self._uncertainty, self._units) if self._uncertainty else None
Beispiel #5
0
    def process_item(self, item):
        quantities = []
        material = item.copy()

        containers = [c + '.quantities' for c in self.props
                      if pydash.get(material, c)] + ['inputs']

        for container in containers:
            for q in pydash.get(material, container) or []:
                this_q = q.copy()
                this_q['material_key'] = material['task_id']
                prov_inputs = pydash.get(this_q, 'provenance.inputs')
                if prov_inputs:
                    new_prov_inputs = [qq['internal_id'] for qq in prov_inputs]
                else:
                    new_prov_inputs = None
                pydash.set_(this_q, 'provenance.inputs', new_prov_inputs)
                quantities.append(this_q)

            pydash.set_(material, container,
                        [q['internal_id']
                         for q in pydash.get(material, container) or []])

            if container != 'inputs':
                prop = container.split(".")[0]
                units = Registry("units").get(prop)
                if units != pydash.get(material, [prop, 'units']):
                    pq_mean = ureg.Quantity(material[prop]['mean'],
                                            material[prop]['units']).to(units)
                    pq_std = ureg.Quantity(material[prop]['std'],
                                           material[prop]['units']).to(units)
                    material[prop]['mean'] = pq_mean.magnitude
                    material[prop]['std'] = pq_std.magnitude
                    material[prop]['units'] = pq_mean.units.format_babel()
            
        for q in quantities:
            units = Registry("units").get(q['symbol_type'])
            if q['units'] != units:
                pq = ureg.Quantity(q['value'], q['units']).to(units)
                q['value'] = pq.magnitude
                q['units'] = pq.units.format_babel()

        return quantities, material
Beispiel #6
0
    def evaluate(self, symbol_values):
        """
        Given a set of symbol_values, performs error checking to see if the input symbol_values represents a valid input
        set based on the self.connections() method. If so, it returns a dictionary representing the value of plug_in
        applied to the inputs. The dictionary contains a "successful" key representing if plug_in was successful.

        Args:
            symbol_values (dict<str,float>): Mapping from string symbol to float value, giving inputs.
        Returns:
            (dict<str,float>), mapping from string symbol to float value giving result of applying the model to the
                               given inputs. Additionally contains a "successful" key -> bool pair.
        """

        # strip units from input
        for symbol in symbol_values:
            if type(symbol_values[symbol]) == ureg.Quantity:
                symbol_values[symbol] = float(symbol_values[symbol].to(
                    self.unit_mapping[symbol]).magnitude)

        available_symbols = set(symbol_values.keys())

        # check we support this combination of inputs
        available_inputs = [
            len(set(possible_input_symbols) - available_symbols) == 0
            for possible_input_symbols in self.input_symbols
        ]
        if not any(available_inputs):
            return {
                'successful':
                False,
                'message':
                "The {} model cannot generate any outputs for these inputs: {}"
                .format(self.name, available_symbols)
            }
        try:
            # evaluate is allowed to fail
            out = self.plug_in(symbol_values)
            out['successful'] = True
        except Exception as e:
            return {'successful': False, 'message': str(e)}

        # add units to output
        for key in out:
            if key == 'successful':
                continue
            out[key] = ureg.Quantity(out[key], self.unit_mapping[key])

        return out
Beispiel #7
0
    def validate_evaluate_wrapper(self, symbols_and_values_in, symbol_out):

        # check we support this combination of inputs/outputs
        inputs = set(symbols_and_values_in.keys())
        if symbol_out not in self.connections:
            logger.error("The {} model does not support this output ({})."
                         "".format(self.name, symbol_out))
        else:
            if not self.connections[symbol_out].issubset(inputs):
                logger.error("The {} model does not support the output {} "
                             "for this combination of inputs ({}).".format(self.name,
                                                                           symbol_out,
                                                                           inputs))

        # check our units
        # TODO: make this more robust, check outputs too
        for symbol, value in symbols_and_values_in.items():
            if not isinstance(value, ureg.Quantity):
                logger.warn("Units are not defined for your {}, "
                            "using assumed units from property definition.".format(symbol))
                unit = getattr(self, 'unit_mapping')[symbol]
                symbols_and_values_in[symbol] = ureg.Quantity(value, unit)

        return func(self, symbols_and_values_in, symbol_out)
Beispiel #8
0
    def to_quantity(
        symbol: Union[str, Symbol], to_coerce: Union[float, np.ndarray,
                                                     ureg.Quantity, "Quantity"]
    ) -> "Quantity":
        """
        Converts the argument into a Quantity object based on its type:
        - float -> given default units (as a pint object) and wrapped in a Quantity object
        - numpy.ndarray array -> given default units (pint) and wrapped in a Quantity object
        - ureg.Quantity -> simply wrapped in a Quantity object
        - Quantity -> immediately returned without modification
        - Any other python object -> simply wrapped in a Quantity object

        TODO: Have a python object convert into an ObjectQuantity object or similar.

        Args:
            to_coerce: item to be converted into a Quantity object
        Returns:
            (Quantity) item that has been converted into a Quantity object
        """
        # If a quantity is passed in, return the quantity.
        if isinstance(to_coerce, Quantity):
            return to_coerce

        # Else
        # Convert the symbol to a Symbol if necessary.
        if isinstance(symbol, str):
            symbol = DEFAULT_SYMBOLS.get(symbol)
            if symbol is None:
                raise Exception(
                    "Attempted to create a quantity for an unrecognized symbol: "
                    + str(symbol))
        # Return the correct Quantity - warn if units are assumed.
        if isinstance(to_coerce, float) or isinstance(to_coerce, np.ndarray):
            return Quantity(symbol, ureg.Quantity(to_coerce, symbol.units))

        return Quantity(symbol, to_coerce)
Beispiel #9
0
    def __init__(self, symbol_type, value, units=None, tags=None,
                 provenance=None, uncertainty=None):
        """
        Instantiates an instance of the NumQuantity class.

        Args:
            symbol_type: (Symbol or str) the type of information that is represented
                by the associated value.  If a string, assigns a symbol from
                the default symbols that has that string name
            value: (int, float, complex, np.integer, np.floating, np.complexfloating,
                list, np.ndarray, pint.Quantity) the value of the property
            units: (str, tuple, list) desired units of the quantity. If value is a
                pint.Quantity, the value will be converted to these units. Input can
                be any acceptable unit format for pint.Quantity.
            tags: (list<str>) tags associated with the quantity, typically
                related to its origin, e. g. "DFT" or "ML" or "experiment"
            provenance: (ProvenanceElement) provenance associated with the
                object. See BaseQuantity.__init__() for more info.
            uncertainty: (int, float, complex, np.integer, np.floating, np.complexfloating,
                list, np.ndarray, pint.Quantity, tuple, NumQuantity) uncertainty
                associated with the value stored in the same units. pint.Quantity,
                tuple, and NumQuantity types will be converted to the units
                specified in 'units'. Other types will be assumed to be in the
                specified units.

        """
        # TODO: Test value on the shape dictated by symbol
        if isinstance(symbol_type, str):
            symbol_type = BaseQuantity.get_symbol_from_string(symbol_type)

        # Set default units if not supplied
        if not units:
            if symbol_type.units is None:
                raise ValueError("No units specified as keyword and no "
                                 "units provided by symbol for NumQuantity")
            logger.info("WARNING: No units supplied, assuming default units from symbol.")

        units = units or symbol_type.units

        if isinstance(value, self._ACCEPTABLE_DTYPES):
            value_in = ureg.Quantity(value.item(), units)
        elif isinstance(value, ureg.Quantity):
            value_in = value.to(units)
        elif self.is_acceptable_type(value):
            value_in = ureg.Quantity(value, units)
        else:
            raise TypeError('Invalid type passed to constructor for value:'
                            ' {}'.format(type(value)))

        super(NumQuantity, self).__init__(symbol_type, value_in,
                                          tags=tags, provenance=provenance)

        if uncertainty is not None:
            if isinstance(uncertainty, self._ACCEPTABLE_DTYPES):
                self._uncertainty = ureg.Quantity(uncertainty.item(), units)
            elif isinstance(uncertainty, ureg.Quantity):
                self._uncertainty = uncertainty.to(units)
            elif isinstance(uncertainty, NumQuantity):
                self._uncertainty = uncertainty._value.to(units)
            elif isinstance(uncertainty, tuple):
                self._uncertainty = ureg.Quantity.from_tuple(uncertainty).to(units)
            elif self.is_acceptable_type(uncertainty):
                self._uncertainty = ureg.Quantity(uncertainty, units)
            else:
                raise TypeError('Invalid type passed to constructor for uncertainty:'
                                ' {}'.format(type(uncertainty)))
        else:
            self._uncertainty = None

        # TODO: Symbol-level constraints are hacked together atm,
        #       constraints as a whole need to be refactored and
        #       put into a separate module. They also are only
        #       available for numerical symbols because it uses
        #       sympy to evaluate the constraints. Would be better
        #       to make some class for symbol and/or model constraints

        symbol_constraint = symbol_type.constraint
        if symbol_constraint is not None:
            if not symbol_constraint(**{symbol_type.name: self.magnitude}):
                raise SymbolConstraintError(
                    "NumQuantity with {} value does not satisfy {}".format(
                        value, symbol_constraint))
Beispiel #10
0
    def test_complex_and_imaginary_checking(self):
        A = Symbol('a', ['A'], ['A'], units='dimensionless', shape=1)
        B = Symbol('b', ['B'], ['B'], units='dimensionless', shape=[2, 2])
        # TODO: Revisit this when splitting quantity class into non-numerical and numerical
        C = Symbol('c', ['C'], ['C'], category='object', shape=1)

        real_float_scalar = Quantity(A, 1.0)
        real_float_non_scalar = Quantity(B, [[1.0, 1.0], [1.0, 1.0]])

        complex_scalar = Quantity(A, complex(1 + 1j))
        complex_non_scalar = Quantity(
            B, [[complex(1.0), complex(1.j)], [complex(1.j),
                                               complex(1.0)]])

        complex_scalar_zero_imaginary = Quantity(A, complex(1.0))
        complex_non_scalar_zero_imaginary = Quantity(
            B, [[complex(1.0), complex(1.0)], [complex(1.0),
                                               complex(1.0)]])

        complex_scalar_appx_zero_imaginary = Quantity(A, complex(1.0 + 1e-10j))
        complex_non_scalar_appx_zero_imaginary = Quantity(
            B, [[complex(1.0), complex(1.0 + 1e-10j)],
                [complex(1.0 + 1e-10j), complex(1.0)]])

        non_numerical = Quantity(C, 'test')

        # Test is_complex_type() with...
        # ...Quantity objects
        self.assertFalse(Quantity.is_complex_type(real_float_scalar))
        self.assertFalse(Quantity.is_complex_type(real_float_non_scalar))
        self.assertTrue(Quantity.is_complex_type(complex_scalar))
        self.assertTrue(Quantity.is_complex_type(complex_non_scalar))
        self.assertTrue(
            Quantity.is_complex_type(complex_scalar_zero_imaginary))
        self.assertTrue(
            Quantity.is_complex_type(complex_non_scalar_zero_imaginary))
        self.assertTrue(
            Quantity.is_complex_type(complex_scalar_appx_zero_imaginary))
        self.assertTrue(
            Quantity.is_complex_type(complex_non_scalar_appx_zero_imaginary))
        self.assertFalse(Quantity.is_complex_type(non_numerical))

        # ...primitive types
        self.assertFalse(Quantity.is_complex_type(1))
        self.assertFalse(Quantity.is_complex_type(1.))
        self.assertTrue(Quantity.is_complex_type(1j))
        self.assertFalse(Quantity.is_complex_type('test'))

        # ...np.array types
        self.assertFalse(Quantity.is_complex_type(np.array([1])))
        self.assertFalse(Quantity.is_complex_type(np.array([1.])))
        self.assertTrue(Quantity.is_complex_type(np.array([1j])))
        self.assertFalse(Quantity.is_complex_type(np.array(['test'])))

        # ...ureg Quantity objects
        self.assertFalse(Quantity.is_complex_type(ureg.Quantity(1)))
        self.assertFalse(Quantity.is_complex_type(ureg.Quantity(1.)))
        self.assertTrue(Quantity.is_complex_type(ureg.Quantity(1j)))
        self.assertFalse(Quantity.is_complex_type(ureg.Quantity([1])))
        self.assertFalse(Quantity.is_complex_type(ureg.Quantity([1.])))
        self.assertTrue(Quantity.is_complex_type(ureg.Quantity([1j])))

        # Check member functions
        self.assertFalse(real_float_scalar.contains_complex_type())
        self.assertFalse(real_float_scalar.contains_imaginary_value())
        self.assertFalse(real_float_non_scalar.contains_complex_type())
        self.assertFalse(real_float_non_scalar.contains_imaginary_value())

        self.assertTrue(complex_scalar.contains_complex_type())
        self.assertTrue(complex_scalar.contains_imaginary_value())
        self.assertTrue(complex_non_scalar.contains_complex_type())
        self.assertTrue(complex_non_scalar.contains_imaginary_value())

        self.assertTrue(complex_scalar_zero_imaginary.contains_complex_type())
        self.assertFalse(
            complex_scalar_zero_imaginary.contains_imaginary_value())
        self.assertTrue(
            complex_non_scalar_zero_imaginary.contains_complex_type())
        self.assertFalse(
            complex_non_scalar_zero_imaginary.contains_imaginary_value())

        self.assertTrue(
            complex_scalar_appx_zero_imaginary.contains_complex_type())
        self.assertFalse(
            complex_scalar_appx_zero_imaginary.contains_imaginary_value())
        self.assertTrue(
            complex_non_scalar_appx_zero_imaginary.contains_complex_type())
        self.assertFalse(
            complex_non_scalar_appx_zero_imaginary.contains_imaginary_value())

        self.assertFalse(non_numerical.contains_complex_type())
        self.assertFalse(non_numerical.contains_imaginary_value())
Beispiel #11
0
    def get_items(self):
        """
        Collects scalar data from propnet and MP databases, aggregates it by property, and creates
        a generator to iterate over all pairs of properties, including pairing of the same property
        with itself for sanity check, and correlation functions.

        Returns: (generator) a generator providing a dictionary with the data for correlation:
            {'x_data': (list<float>) data for independent property (x-axis),
             'x_name': (str) name of independent property,
             'y_data': (list<float>) data for dependent property (y-axis),
             'y_name': (str) name of dependent property,
             'func': (tuple<str, function>) name and function handle for correlation function
             }

        """
        data = defaultdict(dict)

        propnet_data = self.propnet_store.query(
            criteria={},
            properties=[p + '.mean' for p in self.propnet_props] +
            [p + '.units' for p in self.propnet_props] + ['task_id', 'inputs'])

        for material in propnet_data:
            mpid = material['task_id']
            for prop, values in material.items():
                if prop in self.propnet_props:
                    data[mpid][prop] = ureg.Quantity(values['mean'],
                                                     values['units'])
                elif prop == 'inputs':
                    input_d = defaultdict(list)
                    for q in values:
                        if q['symbol_type'] in self.propnet_props:
                            this_q = ureg.Quantity(q['value'], q['units'])
                            input_d[q['symbol_type']].append(this_q)
                    repeated_keys = set(input_d.keys()).intersection(
                        set(data[mpid].keys()))
                    if repeated_keys:
                        logger.warning(
                            'Repeated key(s) from inputs: {}'.format(
                                repeated_keys))
                    data[mpid].update(
                        {k: sum(v) / len(v)
                         for k, v in input_d.items()})

        # TODO: Add these symbols to propnet so we don't have to bring them in explicitly?

        mp_data = self.mp_store.query(criteria={},
                                      properties=self.mp_query_props +
                                      ['task_id'])

        for material in mp_data:
            mpid = material['task_id']
            for prop, value in material.items():
                if isinstance(value, dict):
                    for sub_prop, sub_value in value.items():
                        if prop + '.' + sub_prop in self.mp_query_props and sub_value is not None:
                            data[mpid][sub_prop] = sub_value
                elif prop in self.mp_query_props and value is not None:
                    data[mpid][prop] = value

        # product() produces all possible combinations of properties
        for prop_x, prop_y in product(self.propnet_props + self.mp_props,
                                      repeat=2):
            x = []
            y = []
            for props_data in data.values():
                if prop_x in props_data.keys() and prop_y in props_data.keys():
                    x.append(props_data[prop_x])
                    y.append(props_data[prop_y])

            # MP data does not have units listed in database, so will be floats. propnet
            # data may not have the same units as the MP data, so is stored as pint
            # quantities. Here, the quantities are coerced into the units of MP data
            # as stored in symbols and coverts them to floats.
            if x and any(isinstance(v, ureg.Quantity) for v in x):
                x_float = [
                    xx.to(DEFAULT_SYMBOLS[prop_x].units).magnitude
                    if isinstance(xx, ureg.Quantity) else xx for xx in x
                ]
            else:
                x_float = x
            if y and any(isinstance(v, ureg.Quantity) for v in y):
                y_float = [
                    yy.to(DEFAULT_SYMBOLS[prop_y].units).magnitude
                    if isinstance(yy, ureg.Quantity) else yy for yy in y
                ]
            else:
                y_float = y

            for name, func in self._funcs.items():
                data_dict = {
                    'x_data': x_float,
                    'x_name': prop_x,
                    'y_data': y_float,
                    'y_name': prop_y,
                    'func': (name, func)
                }
                yield data_dict
Beispiel #12
0
    def __init__(self,
                 symbol_type,
                 value,
                 units=None,
                 tags=None,
                 provenance=None,
                 uncertainty=None,
                 **kwargs):
        """
        Parses inputs for constructing a Property object.

        Args:
            symbol_type (Symbol or str): pointer to an existing Symbol
                object in default_symbols or string giving the name
                of a SymbolType object identifies the type of data
                stored in the property.
            value (id): value of the property.
            units: (None): units associated with the quantity's value
            tags (list<str>): list of strings storing metadata from
                Quantity evaluation.
            provenance (ProvenanceElement): provenance associated with the
                object (e. g. inputs, model, see ProvenanceElement)
        """
        # Invoke default symbol if symbol is a string
        if isinstance(symbol_type, str):
            if symbol_type not in DEFAULT_SYMBOLS.keys():
                raise ValueError(
                    "Quantity type {} not recognized".format(symbol_type))
            symbol_type = DEFAULT_SYMBOLS[symbol_type]

        # Set default units if not supplied
        units = units or symbol_type.units

        # Hid this keyword in kwargs so users don't see it
        # in function code completion display
        if 'internal_id' in kwargs.keys():
            self._internal_id = kwargs['internal_id']
        else:
            self._internal_id = uuid.uuid4().hex

        # Invoke pint quantity if supplied or input is float/int

        if isinstance(value, (np.floating, np.integer, np.complexfloating)):
            self._value = ureg.Quantity(np.asscalar(value), units)
        elif isinstance(value, (float, int, list, complex, np.ndarray)):
            self._value = ureg.Quantity(value, units)
        elif isinstance(value, ureg.Quantity):
            self._value = value.to(units)
        elif isinstance(value, Quantity):
            # TODO: This situation needs a deep copy function
            self._value = value._value
            self._internal_id = value._internal_id
        else:
            self._value = value

        if isinstance(uncertainty,
                      (np.floating, np.integer, np.complexfloating)):
            self._uncertainty = ureg.Quantity(np.asscalar(uncertainty), units)
        elif isinstance(uncertainty, (float, int, list, complex, np.ndarray)):
            self._uncertainty = ureg.Quantity(uncertainty, units)
        elif isinstance(uncertainty, ureg.Quantity):
            self._uncertainty = uncertainty.to(units)
        else:
            self._uncertainty = uncertainty

        # TODO: Symbol-level constraints are hacked together atm,
        #       constraints as a whole need to be refactored and
        #       put into a separate module
        if symbol_type.constraint is not None:
            if not symbol_type.constraint(
                    **{symbol_type.name: self.magnitude}):
                raise SymbolConstraintError(
                    "Quantity with {} value does not satisfy {}".format(
                        value, symbol_type.constraint))

        self._symbol_type = symbol_type
        self._tags = tags
        self._provenance = provenance

        if self._provenance is not None:
            if isinstance(self._provenance.source, dict):
                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
                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")
            elif self._provenance.source is None:
                self._provenance.source = {
                    "source": "propnet",
                    "source_key": self._internal_id,
                    "date_created":
                    datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                }
Beispiel #13
0
    def get_data_from_full_db(self, prop_x, prop_y):
        """
        Collects scalar data from full propnet database, aggregates it by property,
        and samples it if desired.

        Args:
            prop_x (str): name of property x
            prop_y (str): name of property y

        Returns:
            dict: dictionary of data keyed by property name

        """

        # Get all materials which have both properties in the inputs or outputs
        criteria = {
            '$and': [{
                '$or': [{
                    'inputs.symbol_type': prop_x
                }, {
                    prop_x: {
                        '$exists': True
                    }
                }]
            }, {
                '$or': [{
                    'inputs.symbol_type': prop_y
                }, {
                    prop_y: {
                        '$exists': True
                    }
                }]
            }]
        }
        properties = [prop_x + '.quantities', prop_y + '.quantities', 'inputs']

        if self.sample_size is None:
            pn_data = self.propnet_store.query(criteria=criteria,
                                               properties=properties)
        else:
            pipeline = [
                {
                    '$match': criteria
                },
                {
                    '$sample': {
                        'size': self.sample_size
                    }
                },
                {
                    '$project': {p: True
                                 for p in properties}
                },
            ]
            pn_data = self.propnet_store.collection.aggregate(
                pipeline, allowDiskUse=True)

        x_unit = Registry("units")[prop_x]
        y_unit = Registry("units")[prop_y]
        data = defaultdict(list)
        for material in pn_data:
            # Collect all data with units for this material
            # and calculate the mean, convert units, store magnitude of mean
            if prop_x == prop_y:
                # This is to avoid duplicating the work and the data
                props = (prop_x, )
                units = (x_unit, )
            else:
                props = (prop_x, prop_y)
                units = (x_unit, y_unit)
            for prop, unit in zip(props, units):
                qs = [
                    ureg.Quantity(q['value'], q['units'])
                    for q in material['inputs'] if q['symbol_type'] == prop
                ]
                if prop in material:
                    qs.extend([
                        ureg.Quantity(q['value'], q['units'])
                        for q in material[prop]['quantities']
                    ])

                if len(qs) == 0:
                    raise ValueError("Query for property {} gave no results"
                                     "".format(prop))
                prop_mean = sum(qs) / len(qs)
                data[prop].append(prop_mean.to(unit).magnitude)

        return data