Ejemplo n.º 1
0
    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
Ejemplo n.º 2
0
    def evaluate(input_rows, data, aggregate):

        quantities = [
            QuantityFactory.create_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(sigfigs=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]
Ejemplo n.º 3
0
    def __init__(self,
                 name,
                 display_names=None,
                 display_symbols=None,
                 units=None,
                 shape=None,
                 object_type=None,
                 comment=None,
                 category='property',
                 constraint=None,
                 default_value=None,
                 is_builtin=False,
                 register=True,
                 overwrite_registry=True):
        """
        Instantiates Symbol object.

        Args:
            name (str): string ASCII identifying the property uniquely
                as an internal identifier.
            units (str, tuple): units of the property as a Quantity
                supported by the Pint package.  Can be supplied as a
                string (e. g. ``cm^2``) or a tuple for ``Quantity.from_tuple``
                (e. g. ``[1.0, [['centimeter', 1.0]]]``)
            display_names (`list` of `str`): list of strings giving possible
                human-readable names for the property.
            display_symbols (`list` of `str`): list of strings giving possible
                human-readable symbols for the property.
            shape (list, int): list giving the order of the tensor as the length,
                and number of dimensions as individual integers in the list.
                If an integer is provided, the symbol contains a vector. If ``shape=1``,
                the symbol contains a scalar.
            comment (str): any useful information on the property including
                its definitions and possible citations.
            category (str): 'property', for property of a material,
                            'condition', for other variables,
                            'object', for a value that is a python object.
            object_type (type, str): class or name of a class representing the object stored in
                these symbols.
            constraint (str): constraint associated with the symbol, must
                be a string expression (e. g. inequality) using the symbol
                name, e. g. ``bulk_modulus > 0``.
            default_value (any): default value for the symbol, e. g. 300 for
                temperature or 1 for magnetic permeability
            is_builtin (bool): True if the model is included with propnet
                by default. Not intended to be set explicitly by users
            register (bool): True if the model should be registered in the symbol registry
                upon instantiation
            overwrite_registry (bool): True if the value in the symbol registry should be
                overwritten if it exists. False will raise a KeyError if a symbol with the
                same name is already registered.

        """

        # TODO: not sure object should be distinguished
        # TODO: clean up for divergent logic
        if category not in ('property', 'condition', 'object'):
            raise ValueError('Unsupported category: {}'.format(category))

        if not name.isidentifier():
            raise ValueError(
                "The canonical name ({}) is not valid.".format(name))

        if not display_names:
            display_names = [name]

        self.object_type = None
        self._object_class = None
        self._object_module = None

        if category in ('property', 'condition'):

            if object_type is not None:
                raise ValueError(
                    "Cannot define an object type for a {}.".format(category))

            try:
                np.zeros(shape)
            except TypeError:
                raise TypeError(
                    "Shape provided for ({}) is invalid.".format(name))

            if units is None:
                units = 1 * ureg.dimensionless
            elif isinstance(units, six.string_types):
                units = 1 * ureg.parse_expression(units)
            elif isinstance(units, (tuple, list)):
                units = ureg.Quantity.from_tuple(units)
            else:
                raise TypeError("Cannot parse unit format: {}".format(units))

            units = units.units.format_babel()
        else:
            if units is not None:
                raise ValueError("Cannot define units for generic objects.")
            units = None  # ureg.parse_expression("")  # dimensionless

            if object_type:
                if isinstance(object_type, type):
                    self._object_module = object_type.__module__
                    self._object_class = object_type.__name__
                else:
                    # Do not try to import the module for security reasons.
                    # We don't want malicious modules to be automatically imported.
                    modclass = object_type.rsplit('.', 1)
                    if len(modclass) == 1:
                        self._object_module = 'builtins'
                        self._object_class = modclass[0]
                    else:
                        self._object_module, self._object_class = modclass

                if self._object_module == 'builtins':
                    self.object_type = self._object_class
                else:
                    self.object_type = ".".join(
                        [self._object_module, self._object_class])

        self.name = name
        self.category = category
        self._units = units
        self.display_names = display_names
        self.display_symbols = display_symbols
        # If a user enters [1] or [1, 1, ...] for shape, treat as a scalar
        if shape and np.size(np.zeros(shape=shape)) == 1:
            shape = 1
        # If a user enters a 0 dimension, throw an error
        if shape and np.size(np.zeros(shape=shape)) == 0:
            raise ValueError(
                "Symbol cannot have a shape with a 0-size dimension: {}".
                format(shape))
        self.shape = shape
        self.comment = comment
        self.default_value = default_value
        self._is_builtin = is_builtin

        # TODO: This should explicity deal with only numerical symbols
        #       because it uses sympy to evaluate them until we make
        #       a class to evaluate them using either sympy or a custom func
        # Note that symbol constraints are not constraint objects
        # at the moment because using them would result in a circular

        if constraint is not None:
            try:
                func = sp.lambdify(self.name, parse_expr(constraint))
                func(0)
            except Exception:
                raise ValueError(
                    "Constraint expression invalid: {}".format(constraint))

        self._constraint = constraint
        self._constraint_func = None

        if register:
            self.register(overwrite_registry=overwrite_registry)
Ejemplo n.º 4
0
    def __init__(self,
                 name,
                 display_names=None,
                 display_symbols=None,
                 units=None,
                 shape=None,
                 object_type=None,
                 comment=None,
                 category='property',
                 constraint=None):
        """
        Parses and validates a series of inputs into a PropertyMetadata
        tuple, a format that PropNet expects.

        Parameters correspond exactly with those of a PropertyMetadata tuple.

        Args:
            name (str): string ASCII identifying the property uniquely
                as an internal identifier.
            units (str or tuple): units of the property as a Quantity
                supported by the Pint package.  Can be supplied as a
                string (e. g. cm^2) or a tuple for Quantity.from_tuple
                (e. g. [1.0, [['centimeter', 1.0]]])
            display_names (list<str>): list of strings giving possible
                human-readable names for the property.
            display_symbols (list<str>): list of strings giving possible
                human-readable symbols for the property.
            shape (id): list giving the order of the tensor as the length,
                and number of dimensions as individual integers in the list.
            comment (str): any useful information on the property including
                its definitions and possible citations.
            category (str): 'property', for property of a material,
                            'condition', for other variables,
                            'object', for a value that is a python object.
            object_type (class): class representing the object stored in
                these symbols.
            constraint (str): constraint associated with the symbol, must
                be a string expression (e. g. inequality) using the symbol
                name, e. g. bulk_modulus > 0.
        """

        # TODO: not sure object should be distinguished
        # TODO: clean up for divergent logic
        if category not in ('property', 'condition', 'object'):
            raise ValueError('Unsupported category: {}'.format(category))

        if not name.isidentifier():
            raise ValueError(
                "The canonical name ({}) is not valid.".format(name))

        if not display_names:
            display_names = [name]

        if category in ('property', 'condition'):

            if object_type is not None:
                raise ValueError(
                    "Cannot define an object type for a {}.".format(category))

            try:
                np.zeros(shape)
            except TypeError:
                raise TypeError(
                    "Shape provided for ({}) is invalid.".format(id))

            logger.info(
                "Units parsed from a string format automatically, "
                "do these look correct? %s", units)
            if isinstance(units, six.string_types):
                units = 1 * ureg.parse_expression(units)
            else:
                units = ureg.Quantity.from_tuple(units)
        else:
            if units is not None:
                raise ValueError("Cannot define units for generic objects.")
            units = None  # ureg.parse_expression("")  # dimensionless

        self.name = name
        self.category = category
        self.units = units
        self.object_type = object_type
        self.display_names = display_names
        self.display_symbols = display_symbols
        self.shape = shape
        self.comment = comment

        # Note that symbol constraints are not constraint objects
        # at the moment because using them would result in a circular
        # dependence, this might be resolved with some reorganization
        if constraint:
            expr = parse_expr(constraint)
            self.constraint = sp.lambdify(self.name, expr)
        else:
            self.constraint = None
Ejemplo n.º 5
0
    def __init__(self, name, display_names=None, display_symbols=None,
                 units=None, shape=None, object_type=None, comment=None,
                 category='property', constraint=None, default_value=None):
        """
        Parses and validates a series of inputs into a PropertyMetadata
        tuple, a format that PropNet expects.

        Parameters correspond exactly with those of a PropertyMetadata tuple.

        Args:
            name (str): string ASCII identifying the property uniquely
                as an internal identifier.
            units (str or tuple): units of the property as a Quantity
                supported by the Pint package.  Can be supplied as a
                string (e. g. cm^2) or a tuple for Quantity.from_tuple
                (e. g. [1.0, [['centimeter', 1.0]]])
            display_names (list<str>): list of strings giving possible
                human-readable names for the property.
            display_symbols (list<str>): list of strings giving possible
                human-readable symbols for the property.
            shape (id): list giving the order of the tensor as the length,
                and number of dimensions as individual integers in the list.
            comment (str): any useful information on the property including
                its definitions and possible citations.
            category (str): 'property', for property of a material,
                            'condition', for other variables,
                            'object', for a value that is a python object.
            object_type (class): class representing the object stored in
                these symbols.
            constraint (str): constraint associated with the symbol, must
                be a string expression (e. g. inequality) using the symbol
                name, e. g. bulk_modulus > 0.
            default_value: default value for the symbol, e. g. 300 for
                temperature or 1 for magnetic permeability
        """

        # TODO: not sure object should be distinguished
        # TODO: clean up for divergent logic
        if category not in ('property', 'condition', 'object'):
            raise ValueError('Unsupported category: {}'.format(category))

        if not name.isidentifier():
            raise ValueError(
                "The canonical name ({}) is not valid.".format(name))

        if not display_names:
            display_names = [name]

        self.object_type = None
        self._object_class = None
        self._object_module = None

        if category in ('property', 'condition'):

            if object_type is not None:
                raise ValueError(
                    "Cannot define an object type for a {}.".format(category))

            try:
                np.zeros(shape)
            except TypeError:
                raise TypeError(
                    "Shape provided for ({}) is invalid.".format(name))

            if units is None:
                units = 'dimensionless'

            logger.info("Units parsed from a string format automatically, "
                        "do these look correct? %s", units)
            if isinstance(units, six.string_types):
                units = 1 * ureg.parse_expression(units)
            else:
                units = ureg.Quantity.from_tuple(units)
        else:
            if units is not None:
                raise ValueError("Cannot define units for generic objects.")
            units = None # ureg.parse_expression("")  # dimensionless

            if object_type:
                if isinstance(object_type, type):
                    self._object_module = object_type.__module__
                    self._object_class = object_type.__name__
                else:
                    # Do not try to import the module for security reasons.
                    # We don't want malicious modules to be automatically imported.
                    modclass = object_type.rsplit('.', 1)
                    if len(modclass) == 1:
                        self._object_module = 'builtins'
                        self._object_class = modclass[0]
                    else:
                        self._object_module, self._object_class = modclass

                if self._object_module == 'builtins':
                    self.object_type = self._object_class
                else:
                    self.object_type = ".".join([self._object_module,
                                                 self._object_class])

        self.name = name
        self.category = category
        self.units = units
        self.display_names = display_names
        self.display_symbols = display_symbols
        # If a user enters [1] or [1, 1, ...] for shape, treat as a scalar
        if shape and np.size(np.zeros(shape=shape)) == 1:
            shape = 1
        # If a user enters a 0 dimension, throw an error
        if shape and np.size(np.zeros(shape=shape)) == 0:
            raise ValueError("Symbol cannot have a shape with a 0-size dimension: {}".format(shape))
        self.shape = shape
        self.comment = comment
        self.default_value = default_value



        # TODO: This should explicity deal with only numerical symbols
        #       because it uses sympy to evaluate them until we make
        #       a class to evaluate them using either sympy or a custom func
        # Note that symbol constraints are not constraint objects
        # at the moment because using them would result in a circular
        # dependence, this might be resolved with some reorganization
        if constraint:
            expr = parse_expr(constraint)
            self.constraint = sp.lambdify(self.name, expr)
        else:
            self.constraint = None
Ejemplo n.º 6
0
    def evaluate(input_rows, data, aggregate):

        quantities = []

        for idx, row in enumerate(input_rows):
            if row['Editable Value']:
                try:
                    value = ureg.parse_expression(row['Editable Value'])
                    units = Registry("units").get(ROW_IDX_TO_SYMBOL_NAME[idx])
                    value.ito(units)
                except Exception:
                    # Someone put an invalid value in the table
                    # TODO: Make error known to the user
                    raise PreventUpdate
                q = QuantityFactory.create_quantity(
                    symbol_type=ROW_IDX_TO_SYMBOL_NAME[idx], value=value)
                quantities.append(q)

        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)

        output_material = graph_evaluator.evaluate(material, timeout=5)

        if aggregate:
            aggregated_quantities = output_material.get_aggregated_quantities()
            non_aggregatable_quantities = [
                v for v in output_material.get_quantities()
                if v.symbol not in aggregated_quantities
            ]
            output_quantities = list(
                aggregated_quantities.values()) + non_aggregatable_quantities
        else:
            output_quantities = output_material.get_quantities()

        output_rows = [{
            'Property': quantity.symbol.display_names[0],
            'Value': quantity.pretty_string(sigfigs=3)
        } for quantity in output_quantities]

        output_table = dt.DataTable(id='output-table',
                                    data=output_rows,
                                    columns=[{
                                        'id': val,
                                        'name': val
                                    } for val in ('Property', 'Value')],
                                    editable=False,
                                    **DATA_TABLE_STYLE)

        # TODO: clean up

        input_quantity_names = [q.symbol for q in quantities]
        derived_quantity_names = \
            set([q.symbol for q in output_quantities]) - \
            set(input_quantity_names)

        models_evaluated = set(
            output_q.provenance.model
            for output_q in output_material.get_quantities())
        models_evaluated = [
            Registry("models").get(m) for m in models_evaluated
            if Registry("models").get(m) is not None
        ]

        material_graph_data = graph_conversion(
            propnet_nx_graph,
            derivation_pathway={
                'inputs': input_quantity_names,
                'outputs': list(derived_quantity_names),
                'models': models_evaluated
            })

        output_graph = html.Div(children=[
            dcc.Checklist(id='material-graph-options',
                          options=[{
                              'label': 'Show models',
                              'value': 'show_models'
                          }, {
                              'label': 'Show properties',
                              'value': 'show_properties'
                          }],
                          value=['show_properties'],
                          labelStyle={'display': 'inline-block'}),
            Cytoscape(id='material-graph',
                      elements=material_graph_data,
                      stylesheet=GRAPH_STYLESHEET,
                      layout=GRAPH_LAYOUT_CONFIG,
                      **GRAPH_SETTINGS['full_view'])
        ])

        return [output_graph, html.Br(), output_table]