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
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 }
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
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
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
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
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)
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)
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))
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())
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
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") }
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