diff --git a/SimPEG/PropMaps.py b/SimPEG/PropMaps.py index 2a8e7ffc..d4fcb85d 100644 --- a/SimPEG/PropMaps.py +++ b/SimPEG/PropMaps.py @@ -4,7 +4,6 @@ class Property(object): name = '' doc = '' - # mappingPair = None defaultVal = None defaultInvProp = False @@ -14,11 +13,23 @@ class Property(object): self.doc = doc Utils.setKwargs(self, **kwargs) + @property + def propertyLink(self): + "Can be something like: ('sigma', Maps.ReciprocalMap)" + return getattr(self, '_propertyLink', None) + @propertyLink.setter + def propertyLink(self, value): + assert type(value) is tuple and len(value) == 2 and type(value[0]) is str and issubclass(value[1], Maps.IdentityMap), 'Use format: ("%s", Maps.ReciprocalMap)'%self.name + self._propertyLink = value + def _getMapProperty(self): prop = self def fget(self): return getattr(self, '_%sMap'%prop.name, None) def fset(self, val): + if prop.propertyLink is not None: + linkName, linkMap = prop.propertyLink + assert getattr(self, '%sMap'%linkName, None) is None, 'Cannot set both sides of a linked property.' # TODO: Check if the mapping can be correct setattr(self, '_%sMap'%prop.name, val) return property(fget=fget, fset=fset, doc=prop.doc) @@ -35,19 +46,33 @@ class Property(object): prop = self def fget(self): mapping = getattr(self, '%sMap'%prop.name) - if mapping is None: + if mapping is None and prop.propertyLink is None: return prop.defaultVal - m = getattr(self, '%sModel'%prop.name) + + if mapping is None and prop.propertyLink is not None: + linkName, linkMapClass = prop.propertyLink + linkMap = linkMapClass(None) + m = getattr(self, '%s'%linkName) + return linkMap * m + + m = getattr(self, '%sModel'%prop.name) return mapping * m return property(fget=fget) def _getModelDerivProperty(self): prop = self def fget(self): - m = getattr(self, '%sModel'%prop.name) mapping = getattr(self, '%sMap'%prop.name) - if mapping is None: + if mapping is None and prop.propertyLink is None: return None + + if mapping is None and prop.propertyLink is not None: + linkName, linkMapClass = prop.propertyLink + linkMap = linkMapClass(None) * getattr(self, '%sMap'%linkName) + m = getattr(self, '%s'%linkName) + return linkMap.deriv( m ) + + m = getattr(self, '%sModel'%prop.name) return mapping.deriv( m ) return property(fget=fget) diff --git a/SimPEG/Tests/test_PropMaps.py b/SimPEG/Tests/test_PropMaps.py index b869502b..bb5788bc 100644 --- a/SimPEG/Tests/test_PropMaps.py +++ b/SimPEG/Tests/test_PropMaps.py @@ -5,7 +5,12 @@ from scipy.constants import mu_0 class MyPropMap(Maps.PropMap): sigma = Maps.Property("Electrical Conductivity", defaultInvProp=True) - mu = Maps.Property("Electrical Conductivity", defaultVal=mu_0) + mu = Maps.Property("Mu", defaultVal=mu_0) + +class MyReciprocalPropMap(Maps.PropMap): + sigma = Maps.Property("Electrical Conductivity", defaultInvProp=True, propertyLink=('rho', Maps.ReciprocalMap)) + rho = Maps.Property("Electrical Resistivity", propertyLink=('sigma', Maps.ReciprocalMap)) + mu = Maps.Property("Mu", defaultVal=mu_0) class TestPropMaps(unittest.TestCase): @@ -30,15 +35,15 @@ class TestPropMaps(unittest.TestCase): assert PM.muMap is None assert PM.muIndex is None - m = PM(np.r_[1,2,3]) + m = PM(np.r_[1.,2,3]) assert m.mu == mu_0 assert m.muModel is None assert m.muMap is None assert m.muDeriv is None - assert np.all(m.sigmaModel == np.r_[1,2,3]) + assert np.all(m.sigmaModel == np.r_[1.,2,3]) assert m.sigmaMap is expMap - assert np.all(m.sigma == np.exp(np.r_[1,2,3])) + assert np.all(m.sigma == np.exp(np.r_[1.,2,3])) assert m.sigmaDeriv is not None assert m.nP == 3 @@ -47,7 +52,7 @@ class TestPropMaps(unittest.TestCase): expMap = Maps.ExpMap(Mesh.TensorMesh((3,))) PM = MyPropMap({'maps':[('sigma', expMap)], 'slices':{'sigma':[2,1,0]}}) assert PM.sigmaIndex == [2,1,0] - m = PM(np.r_[1,2,3]) + m = PM(np.r_[1.,2,3]) assert np.all(m.sigmaModel == np.r_[3,2,1]) assert np.all(m.sigma == np.exp(np.r_[3,2,1])) @@ -57,14 +62,14 @@ class TestPropMaps(unittest.TestCase): iMap = Maps.IdentityMap(m) PM = MyPropMap([('sigma', expMap), ('mu', iMap)]) - pm = PM(np.r_[1,2,3,4,5,6]) + pm = PM(np.r_[1.,2,3,4,5,6]) assert pm.nP == 6 - assert np.all(pm.sigmaModel == [1,2,3]) - assert np.all(pm.sigma == np.exp([1,2,3])) - assert np.all(pm.muModel == [4,5,6]) - assert np.all(pm.mu == [4,5,6]) + assert np.all(pm.sigmaModel == [1.,2,3]) + assert np.all(pm.sigma == np.exp([1.,2,3])) + assert np.all(pm.muModel == [4.,5,6]) + assert np.all(pm.mu == [4.,5,6]) def test_multiMapCompressed(self): @@ -73,7 +78,7 @@ class TestPropMaps(unittest.TestCase): iMap = Maps.IdentityMap(m) PM = MyPropMap({'maps':[('sigma', expMap), ('mu', iMap)],'slices':{'mu':[0,1,2]}}) - pm = PM(np.r_[1,2,3]) + pm = PM(np.r_[1,2.,3]) assert pm.nP == 3 @@ -82,6 +87,37 @@ class TestPropMaps(unittest.TestCase): assert np.all(pm.muModel == [1,2,3]) assert np.all(pm.mu == [1,2,3]) + def test_Links(self): + m = Mesh.TensorMesh((3,)) + expMap = Maps.ExpMap(m) + iMap = Maps.IdentityMap(m) + PM = MyReciprocalPropMap([('sigma', iMap)]) + pm = PM(np.r_[1,2.,3]) + # print pm.sigma + # print pm.sigmaMap + assert np.all(pm.sigma == [1,2,3]) + assert np.all(pm.rho == 1./np.r_[1,2,3]) + assert pm.sigmaMap is iMap + assert pm.rhoMap is None + assert pm.sigmaDeriv is not None + assert pm.rhoDeriv is not None + + PM = MyReciprocalPropMap([('rho', iMap)]) + pm = PM(np.r_[1,2.,3]) + # print pm.sigma + # print pm.sigmaMap + assert np.all(pm.sigma == 1./np.r_[1,2,3]) + assert np.all(pm.rho == [1,2,3]) + assert pm.sigmaMap is None + assert pm.rhoMap is iMap + assert pm.sigmaDeriv is not None + assert pm.rhoDeriv is not None + + self.assertRaises(AssertionError, MyReciprocalPropMap, [('rho', iMap), ('sigma', iMap)]) + self.assertRaises(AssertionError, MyReciprocalPropMap, [('sigma', iMap), ('rho', iMap)]) + + MyReciprocalPropMap([('sigma', iMap), ('mu', iMap)]) # This should be fine + if __name__ == '__main__': unittest.main()