def evaluate(input_rows): quantities = [ Quantity(symbol_type=ROW_IDX_TO_SYMBOL_NAME[idx], value=ureg.parse_expression(row['Value'])) for idx, row in enumerate(input_rows) if row['Value'] ] material = Material() for quantity in quantities: material.add_quantity(quantity) graph = Graph() output_material = graph.evaluate(material) output_quantities = output_material.get_aggregated_quantities() print(output_quantities) output_rows = [{ 'Property': symbol.display_names[0], 'Value': str(quantity.value), 'Provenance': None } for symbol, quantity in output_quantities.items()] return output_rows
def 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]
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)
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
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
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]