def test_uid(self): class UidModel(properties.HasProperties): uid = properties.Uuid('my uuid') model = UidModel() assert isinstance(model.uid, uuid.UUID) with self.assertRaises(AttributeError): model.uid = uuid.uuid4() assert model.validate() model._backend['uid'] = 'hi' with self.assertRaises(ValueError): model.validate() json_uuid = uuid.uuid4() json_uuid_str = str(json_uuid) assert properties.Uuid.to_json(json_uuid) == json_uuid_str assert str(properties.Uuid.from_json(json_uuid_str)) == json_uuid_str assert properties.Uuid('').equal(uuid.UUID(int=0), uuid.UUID(int=0))
class UidModel(properties.HasProperties): uid = properties.Uuid('my uuid')
class BaseRx(properties.HasProperties): """SimPEG Receiver Object""" # TODO: write a validator that checks against mesh dimension in the # BaseSimulation # TODO: location locations = RxLocationArray("Locations of the receivers (nRx x nDim)", shape=("*", "*"), required=True) # TODO: project_grid? projGLoc = properties.StringChoice( "Projection grid location, default is CC", choices=["CC", "Fx", "Fy", "Fz", "Ex", "Ey", "Ez", "N"], default="CC", ) # TODO: store_projections storeProjections = properties.Bool( "Store calls to getP (organized by mesh)", default=True) _uid = properties.Uuid("unique ID for the receiver") _Ps = properties.Dictionary("dictonary for storing projections", ) def __init__(self, locations=None, **kwargs): super(BaseRx, self).__init__(**kwargs) if locations is not None: self.locations = locations rxType = kwargs.pop("rxType", None) if rxType is not None: warnings.warn( "BaseRx no longer has an rxType. Each rxType should instead " "be a different receiver class.") if getattr(self, "_Ps", None) is None: self._Ps = {} locs = deprecate_property(locations, "locs", new_name="locations", removal_version="0.15.0") @property def nD(self): """Number of data in the receiver.""" return self.locations.shape[0] def getP(self, mesh, projGLoc=None): """ Returns the projection matrices as a list for all components collected by the receivers. .. note:: Projection matrices are stored as a dictionary listed by meshes. """ if projGLoc is None: projGLoc = self.projGLoc if (mesh, projGLoc) in self._Ps: return self._Ps[(mesh, projGLoc)] P = mesh.getInterpolationMat(self.locations, projGLoc) if self.storeProjections: self._Ps[(mesh, projGLoc)] = P return P def eval(self, **kwargs): raise NotImplementedError( "the eval method for {} has not been implemented".format(self)) def evalDeriv(self, **kwargs): raise NotImplementedError( "the evalDeriv method for {} has not been implemented".format( self))
class BaseSrc(BaseSimPEG): """SimPEG Source Object""" location = SourceLocationArray("Location of the source [x, y, z] in 3D", shape=("*", ), required=False) receiver_list = properties.List("receiver list", properties.Instance( "a SimPEG receiver", BaseRx), default=[]) _uid = properties.Uuid("unique identifier for the source") loc = deprecate_property(location, "loc", new_name="location", removal_version="0.15.0") @properties.validator("receiver_list") def _receiver_list_validator(self, change): value = change["value"] assert len( set(value)) == len(value), "The receiver_list must be unique" self._rxOrder = dict() [self._rxOrder.setdefault(rx._uid, ii) for ii, rx in enumerate(value)] rxList = deprecate_property(receiver_list, "rxList", new_name="receiver_list", removal_version="0.15.0") def getReceiverIndex(self, receiver): if not isinstance(receiver, list): receiver = [receiver] for rx in receiver: if getattr(rx, "_uid", None) is None: raise KeyError("Source does not have a _uid: {0!s}".format( str(rx))) inds = list(map(lambda rx: self._rxOrder.get(rx._uid, None), receiver)) if None in inds: raise KeyError( "Some of the receiver specified are not in this survey. " "{0!s}".format(str(inds))) return inds @property def nD(self): """Number of data""" return self.vnD.sum() @property def vnD(self): """Vector number of data""" return np.array([rx.nD for rx in self.receiver_list]) def __init__(self, receiver_list=None, location=None, **kwargs): super(BaseSrc, self).__init__(**kwargs) if receiver_list is not None: self.receiver_list = receiver_list if location is not None: self.location = location
class Data(properties.HasProperties): """ Data storage. This class keeps track of observed data, relative error of those data and the noise floor. .. code:: python data = Data(survey, dobs=dobs, relative_error=relative, noise_floor=floor) or .. code:: python data = Data(survey, dobs=dobs, standard_deviation=standard_deviation) """ dobs = properties.Array( """ Vector of the observed data. The data can be set using the survey parameters: .. code:: python data = Data(survey) for src in survey.source_list: for rx in src.receiver_list: data[src, rx] = datum """, shape=("*", ), required=True, ) relative_error = UncertaintyArray( """ Relative error of the data. This can be set using an array of the same size as the data (e.g. if you want to assign a different relative error to each datum) or as a scalar if you would like to assign a the same relative error to all data. The standard_deviation is constructed as follows:: sqrt( (relative_error * np.abs(dobs))**2 + noise_floor**2 ) For example, if you set .. code:: python data = Data(survey, dobs=dobs) data.relative_error = 0.05 then the contribution to the standard_deviation is equal to .. code:: python data.relative_error * np.abs(data.dobs) """, shape=("*", ), ) noise_floor = UncertaintyArray( """ Noise floor of the data. This can be set using an array of the same size as the data (e.g. if you want to assign a different noise floor to each datum) or as a scalar if you would like to assign a the same noise floor to all data. The standard_deviation is constructed as follows:: sqrt( (relative_error * np.abs(dobs))**2 + noise_floor**2 ) For example, if you set .. code:: python data = Data(survey, dobs=dobs) data.noise_floor = 1e-10 then the contribution to the standard_deviation is equal to .. code:: python data.noise_floor """, shape=("*", ), ) survey = properties.Instance("a SimPEG survey object", BaseSurvey, required=True) _uid = properties.Uuid("unique ID for the data") ####################### # Instantiate the class ####################### def __init__( self, survey, dobs=None, relative_error=None, noise_floor=None, standard_deviation=None, ): super(Data, self).__init__() self.survey = survey # Observed data if dobs is None: dobs = np.nan * np.ones(survey.nD) # initialize data as nans self.dobs = dobs if relative_error is not None: self.relative_error = relative_error if noise_floor is not None: self.noise_floor = noise_floor if standard_deviation is not None: if relative_error is not None or noise_floor is not None: warnings.warn("Setting the standard_deviation overwrites the " "relative_error and noise_floor") self.standard_deviation = standard_deviation if (standard_deviation is None and relative_error is None and noise_floor is None): self.standard_deviation = 0.0 ####################### # Properties ####################### @property def standard_deviation(self): """ Data standard deviations. If a relative error and noise floor are provided, the standard_deviation is .. code:: python data.standard_deviation = np.sqrt( (data.relative_error*np.abs(data.dobs))**2 + data.noise_floor**2 ) otherwise, the standard_deviation can be set directly .. code:: python data.standard_deviation = 0.05 * np.absolute(self.dobs) + 1e-12 Note that setting the standard_deviation directly will clear the `relative_error` and set the value to the `noise_floor` property. """ if self.relative_error is None and self.noise_floor is None: raise Exception( "The relative_error and / or noise_floor must be set " "before asking for uncertainties. Alternatively, the " "standard_deviation can be set directly") uncert = np.zeros(self.nD) if self.relative_error is not None: uncert += np.array(self.relative_error * np.absolute(self.dobs))**2 if self.noise_floor is not None: uncert += np.array(self.noise_floor)**2 return np.sqrt(uncert) @standard_deviation.setter def standard_deviation(self, value): self.relative_error = np.zeros(self.nD) self.noise_floor = value @property def nD(self): return len(self.dobs) @property def shape(self): return self.dobs.shape ########################## # Observers and validators ########################## @properties.validator("dobs") def _dobs_validator(self, change): if self.survey.nD != len(change["value"]): raise ValueError( "{} must have the same length as the number of data. The " "provided input has len {}, while the survey expects " "survey.nD = {}".format(change["name"], len(change["value"]), self.survey.nD)) @properties.validator(["relative_error", "noise_floor"]) def _standard_deviation_validator(self, change): if isinstance(change["value"], float): change["value"] = change["value"] * np.ones(self.nD) self._dobs_validator(change) @property def index_dictionary(self): """ Dictionary of data indices by sources and receivers. To set data using survey parameters: .. code:: python data = Data(survey) for src in survey.source_list: for rx in src.receiver_list: index = data.index_dictionary[src][rx] data.dobs[index] = datum """ if getattr(self, "_index_dictionary", None) is None: if self.survey is None: raise Exception( "To set or get values by source-receiver pairs, a survey must " "first be set. `data.survey = survey`") # create an empty dict self._index_dictionary = {} # create an empty dict associated with each source for src in self.survey.source_list: self._index_dictionary[src] = {} # loop over sources and find the associated data indices indBot, indTop = 0, 0 for src in self.survey.source_list: for rx in src.receiver_list: indTop += rx.nD self._index_dictionary[src][rx] = np.arange(indBot, indTop) indBot += rx.nD return self._index_dictionary ########################## # Methods ########################## def __setitem__(self, key, value): index = self.index_dictionary[key[0]][key[1]] self.dobs[index] = mkvc(value) def __getitem__(self, key): index = self.index_dictionary[key[0]][key[1]] return self.dobs[index] def tovec(self): return self.dobs def fromvec(self, v): v = mkvc(v) self.dobs = v ########################## # Deprecated ########################## std = deprecate_property( relative_error, "std", new_name="relative_error", removal_version="0.16.0", future_warn=True, ) eps = deprecate_property( noise_floor, "eps", new_name="noise_floor", removal_version="0.16.0", future_warn=True, )
class UidModel(properties.HasProperties): """UidModel is a HasProperties object with uid""" _REGISTRY = OrderedDict() uid = properties.Uuid('Unique identifier', serializer=lambda val, **kwargs: None, deserializer=lambda val, **kwargs: None) date_created = properties.GettableProperty( 'Date project was created', default=datetime.datetime.utcnow, serializer=properties.DateTime.to_json, deserializer=lambda val, **kwargs: None) date_modified = properties.GettableProperty( 'Date project was modified', default=datetime.datetime.utcnow, serializer=properties.DateTime.to_json, deserializer=lambda val, **kwargs: None) @properties.observer(properties.everything) def _modify(self, _): """Update date_modified whenever anything changes""" self._backend['date_modified'] = datetime.datetime.utcnow() @properties.validator def _update_date_modified(self): """Update date_modified if any contained UidModel has been modified""" for val in self._backend.values(): if (isinstance(val, UidModel) and val.date_modified > self.date_modified): self._backend['date_modified'] = val.date_modified def serialize( self, include_class=True, registry=None, #pylint: disable=arguments-differ skip_validation=False, **kwargs): """Serialize nested UidModels to a flat dictionary with pointers""" if registry is None: if not skip_validation: self.validate() registry = dict() root = True else: root = False if str(self.uid) not in registry: registry.update({ str(self.uid): super(UidModel, self).serialize(include_class, registry=registry, **kwargs) }) if root: return registry return str(self.uid) @classmethod def deserialize(cls, uid, trusted=True, registry=None, **kwargs): #pylint: disable=arguments-differ """Deserialize nested UidModels from flat pointer dictionary""" if registry is None: raise ValueError('no registry provided') if uid not in registry: raise ValueError('uid not found: {}'.format(uid)) if not isinstance(registry[uid], UidModel): date_created = registry[uid]['date_created'] date_modified = registry[uid]['date_modified'] kwargs.update({'verbose': False}) new_model = super(UidModel, cls).deserialize(value=registry[uid], registry=registry, trusted=trusted, **kwargs) new_model._backend.update({ 'uid': properties.Uuid.from_json(uid), 'date_created': properties.DateTime.from_json(date_created), 'date_modified': properties.DateTime.from_json(date_modified) }) registry.update({uid: new_model}) return registry[uid]
class HasUid(properties.HasProperties): uid = properties.Uuid('uid')
class UidModel(properties.HasProperties): uid = properties.Uuid('unique id')