Merge branch 'master' of https://github.com/simpeg/simpeg into cylClean

Conflicts:
	SimPEG/Survey.py
This commit is contained in:
rowanc1
2014-04-14 09:44:51 -07:00
12 changed files with 231 additions and 135 deletions
+1 -1
View File
@@ -61,7 +61,7 @@ class BaseObjFunction(object):
if self.debug: print 'Calling ObjFunction.startup' if self.debug: print 'Calling ObjFunction.startup'
if self.reg.mref is None: if self.reg.mref is None:
print 'Regularization has not set mref. SimPEG will set it to m0.' print 'Regularization has not set mref. SimPEG.ObjFunction will set it to m0.'
self.reg.mref = m0 self.reg.mref = m0
self.phi_d = np.nan self.phi_d = np.nan
+7 -9
View File
@@ -800,13 +800,15 @@ class NewtonRoot(object):
""" """
tol = 1.000e-06 tol = 1.000e-06
solveTol = 0 # Default direct solve.
maxIter = 20 maxIter = 20
stepDcr = 0.5 stepDcr = 0.5
maxLS = 30 maxLS = 30
comments = False comments = False
doLS = True doLS = True
Solver = Solver
solverOpts = {}
def __init__(self, **kwargs): def __init__(self, **kwargs):
Utils.setKwargs(self, **kwargs) Utils.setKwargs(self, **kwargs)
@@ -828,13 +830,9 @@ class NewtonRoot(object):
while True: while True:
r, J = fun(x, return_g=True) r, J = fun(x, return_g=True)
if self.solveTol == 0:
Jinv = Solver(J) Jinv = self.Solver(J, **self.solverOpts)
dh = - Jinv.solve(r) dh = - Jinv.solve(r)
else:
raise NotImplementedError('Iterative solve on NewtonRoot is not yet implemented.')
# M = @(x) tril(J)\(diag(J).*(triu(J)\x));
# [dh, ~] = bicgstab(J,-r,O.solveTol,500,M);
muLS = 1. muLS = 1.
LScnt = 1 LScnt = 1
@@ -862,7 +860,7 @@ class NewtonRoot(object):
if norm(rt) < self.tol: if norm(rt) < self.tol:
break break
if self.iter > self.maxIter: if self.iter > self.maxIter:
print 'NewtonRoot stopped by maxIters. norm: %4.4e' % norm(rt) print 'NewtonRoot stopped by maxIters (%d). norm: %4.4e' % (self.maxIter, norm(rt))
break break
return x return x
+14 -1
View File
@@ -56,7 +56,7 @@ class Parameter(object):
if (self.current is None or if (self.current is None or
not self.opt.iter == self.currentIter): not self.opt.iter == self.currentIter):
self.current = self.nextIter() self.current = self.nextIter()
self.currentIter = self.opt.iter self.currentIter = getattr(self.opt, 'iter', 0)
return self.current return self.current
def nextIter(self): def nextIter(self):
@@ -162,3 +162,16 @@ class BetaSchedule(BetaEstimate):
self.beta /= self.coolingFactor self.beta /= self.coolingFactor
return self.beta return self.beta
class UpdateReferenceModel(Parameter):
mref0 = None
def nextIter(self):
mref = getattr(self, 'm_prev', None)
if mref is None:
if self.debug: print 'UpdateReferenceModel is using mref0'
mref = self.mref0
self.m_prev = self.objFunc.m_current
return mref
+24 -66
View File
@@ -4,42 +4,14 @@ import Model
class BaseProblem(object): class BaseProblem(object):
""" """
Problem is the base class for all geophysical forward problems in SimPEG. Problem is the base class for all geophysical forward problems in SimPEG.
The problem is a partial differential equation of the form:
.. math::
c(m, u) = 0
Here, m is the model and u is the field (or fields).
Given the model, m, we can calculate the fields u(m),
however, the data we collect is a subset of the fields,
and can be defined by a linear projection, P.
.. math::
d_\\text{pred} = Pu(m)
We are interested in how changing the model transforms the data,
as such we can take write the Taylor expansion:
.. math::
Pu(m + hv) = Pu(m) + hP\\frac{\partial u(m)}{\partial m} v + \mathcal{O}(h^2 \left\| v \\right\| )
We can linearize and define the sensitivity matrix as:
.. math::
J = P\\frac{\partial u}{\partial m}
The sensitivity matrix, and it's transpose will be used in the inverse problem
to (locally) find how model parameters change the data, and optimize!
""" """
__metaclass__ = Utils.SimPEGMetaClass __metaclass__ = Utils.SimPEGMetaClass
counter = None #: A SimPEG.Utils.Counter object counter = None #: A SimPEG.Utils.Counter object
surveyPair = Survey.BaseSurvey surveyPair = Survey.BaseSurvey #: A SimPEG.Survey Class
modelPair = Model.BaseModel modelPair = Model.BaseModel #: A SimPEG.Model Class
def __init__(self, model, **kwargs): def __init__(self, model, **kwargs):
Utils.setKwargs(self, **kwargs) Utils.setKwargs(self, **kwargs)
@@ -49,7 +21,9 @@ class BaseProblem(object):
self.model = model self.model = model
@property @property
def mesh(self): return self.model.mesh def mesh(self):
"""SimPEG mesh that is associated with the model provided."""
return self.model.mesh
@property @property
def survey(self): def survey(self):
@@ -73,48 +47,33 @@ class BaseProblem(object):
self._survey = None self._survey = None
@property @property
def ispaired(self): return self.survey is not None def ispaired(self):
"""True if the problem is paired to a survey."""
return self.survey is not None
@Utils.timeIt @Utils.timeIt
def Jvec(self, m, v, u=None): def Jvec(self, m, v, u=None):
""" """
Effect of J(m) on a vector v.
:param numpy.array m: model :param numpy.array m: model
:param numpy.array v: vector to multiply :param numpy.array v: vector to multiply
:param numpy.array u: fields :param numpy.array u: fields
:rtype: numpy.array :rtype: numpy.array
:return: Jv :return: Jv
Working with the general PDE, c(m, u) = 0, where m is the model and u is the field,
the sensitivity is defined as:
.. math::
J = P\\frac{\partial u}{\partial m}
We can take the derivative of the PDE:
.. math::
\\nabla_m c(m, u) \delta m + \\nabla_u c(m, u) \delta u = 0
If the forward problem is invertible, then we can rearrange for du/dm:
.. math::
J = - P \left( \\nabla_u c(m, u) \\right)^{-1} \\nabla_m c(m, u)
This can often be computed given a vector (i.e. J(v)) rather than stored, as J is a large dense matrix.
""" """
raise NotImplementedError('J is not yet implemented.') raise NotImplementedError('J is not yet implemented.')
@Utils.timeIt @Utils.timeIt
def Jtvec(self, m, v, u=None): def Jtvec(self, m, v, u=None):
""" """
Effect of transpose of J(m) on a vector v.
:param numpy.array m: model :param numpy.array m: model
:param numpy.array v: vector to multiply :param numpy.array v: vector to multiply
:param numpy.array u: fields :param numpy.array u: fields
:rtype: numpy.array :rtype: numpy.array
:return: JTv :return: JTv
Effect of transpose of J on a vector v.
""" """
raise NotImplementedError('Jt is not yet implemented.') raise NotImplementedError('Jt is not yet implemented.')
@@ -122,29 +81,26 @@ class BaseProblem(object):
@Utils.timeIt @Utils.timeIt
def Jvec_approx(self, m, v, u=None): def Jvec_approx(self, m, v, u=None):
""" """
Approximate effect of J(m) on a vector v
:param numpy.array m: model :param numpy.array m: model
:param numpy.array v: vector to multiply :param numpy.array v: vector to multiply
:param numpy.array u: fields :param numpy.array u: fields
:rtype: numpy.array :rtype: numpy.array
:return: Jv :return: approxJv
Approximate effect of J on a vector v
""" """
return self.Jvec(m, v, u) return self.Jvec(m, v, u)
@Utils.timeIt @Utils.timeIt
def Jtvec_approx(self, m, v, u=None): def Jtvec_approx(self, m, v, u=None):
""" """
Approximate effect of transpose of J(m) on a vector v.
:param numpy.array m: model :param numpy.array m: model
:param numpy.array v: vector to multiply :param numpy.array v: vector to multiply
:param numpy.array u: fields :param numpy.array u: fields
:rtype: numpy.array :rtype: numpy.array
:return: JTv :return: JTv
Approximate transpose of J*v
""" """
return self.Jtvec(m, v, u) return self.Jtvec(m, v, u)
@@ -152,26 +108,28 @@ class BaseProblem(object):
""" """
The field given the model. The field given the model.
.. math:: :param numpy.array m: model
u(m) :rtype: numpy.array
:return: u, the fields
""" """
pass raise NotImplementedError('fields is not yet implemented.')
#TODO: Rename and refactor to createSyntheticData def createSyntheticSurvey(self, m, std=0.05, u=None, **survey_kwargs):
def createSyntheticSurvey(self, m, std=0.05, u=None, **geometry_kwargs):
""" """
Create synthetic survey given a model, and a standard deviation. Create synthetic survey given a model, and a standard deviation.
:param numpy.array m: geophysical model :param numpy.array m: geophysical model
:param numpy.array std: standard deviation :param numpy.array std: standard deviation
:param numpy.array u: fields for the given model (if pre-calculated)
:param numpy.array survey_kwargs: Keyword arguments for initiating the survey.
:rtype: SurveyObject :rtype: SurveyObject
:return: survey :return: survey
Returns the observed data with random Gaussian noise Returns the observed data with random Gaussian noise
and Wd which is the same size as data, and can be used to weight the inversion. and Wd which is the same size as data, and can be used to weight the inversion.
""" """
survey = self.surveyPair(mtrue=m, **geometry_kwargs) survey = self.surveyPair(mtrue=m, **survey_kwargs)
survey.pair(self) survey.pair(self)
survey.dtrue = survey.dpred(m, u=u) survey.dtrue = survey.dpred(m, u=u)
noise = std*abs(survey.dtrue)*np.random.randn(*survey.dtrue.shape) noise = std*abs(survey.dtrue)*np.random.randn(*survey.dtrue.shape)
+19 -12
View File
@@ -60,7 +60,8 @@ class BaseSurvey(object):
@Utils.count @Utils.count
@Utils.requires('prob') @Utils.requires('prob')
def dpred(self, m, u=None): def dpred(self, m, u=None):
""" """dpred(m, u=None)
Create the projected data from a model. Create the projected data from a model.
The field, u, (if provided) will be used for the predicted data The field, u, (if provided) will be used for the predicted data
instead of recalculating the fields (which may be expensive!). instead of recalculating the fields (which may be expensive!).
@@ -77,9 +78,9 @@ class BaseSurvey(object):
@Utils.count @Utils.count
def projectFields(self, u): def projectFields(self, u):
""" """projectFields(u)
This function projects the fields onto the data space.
This function projects the fields onto the data space.
.. math:: .. math::
@@ -89,22 +90,23 @@ class BaseSurvey(object):
@Utils.count @Utils.count
def projectFieldsDeriv(self, u): def projectFieldsDeriv(self, u):
""" """projectFieldsDeriv(u)
This function projects the fields onto the data space.
This function s the derivative of projects the fields onto the data space.
.. math:: .. math::
\\frac{\partial d_\\text{pred}}{\partial u} = \mathbf{P} \\frac{\partial d_\\text{pred}}{\partial u} = \mathbf{P}
""" """
return sp.identity(u.size) raise NotImplemented('projectFields is not yet implemented.')
@Utils.count @Utils.count
def residual(self, m, u=None): def residual(self, m, u=None):
""" """residual(m, u=None)
:param numpy.array m: geophysical model :param numpy.array m: geophysical model
:param numpy.array u: fields :param numpy.array u: fields
:rtype: float :rtype: numpy.array
:return: data residual :return: data residual
The data residual: The data residual:
@@ -129,6 +131,7 @@ class BaseSurvey(object):
""" """
if getattr(self,'_Wd',None) is None: if getattr(self,'_Wd',None) is None:
print 'SimPEG is making Survey.Wd to be norm of the data plus a floor.'
eps = np.linalg.norm(Utils.mkvc(self.dobs),2)*1e-5 eps = np.linalg.norm(Utils.mkvc(self.dobs),2)*1e-5
self._Wd = 1/(abs(self.dobs)*self.std+eps) self._Wd = 1/(abs(self.dobs)*self.std+eps)
return self._Wd return self._Wd
@@ -137,11 +140,12 @@ class BaseSurvey(object):
self._Wd = value self._Wd = value
def residualWeighted(self, m, u=None): def residualWeighted(self, m, u=None):
""" """residualWeighted(m, u=None)
:param numpy.array m: geophysical model :param numpy.array m: geophysical model
:param numpy.array u: fields :param numpy.array u: fields
:rtype: float :rtype: numpy.array
:return: data residual :return: weighted data residual
The weighted data residual: The weighted data residual:
@@ -149,7 +153,7 @@ class BaseSurvey(object):
\mu_\\text{data}^{\\text{weighted}} = \mathbf{W}_d(\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs}) \mu_\\text{data}^{\\text{weighted}} = \mathbf{W}_d(\mathbf{d}_\\text{pred} - \mathbf{d}_\\text{obs})
Where W_d is a covariance matrix that weights the data residual. Where \\\\(W_d\\\\) is a covariance matrix that weights the data residual.
""" """
return Utils.mkvc(self.Wd*self.residual(m, u=u)) return Utils.mkvc(self.Wd*self.residual(m, u=u))
@@ -205,7 +209,10 @@ class BaseRx(object):
def nD(self): def nD(self):
return self.locs.shape[0] return self.locs.shape[0]
<<<<<<< HEAD
=======
>>>>>>> dbd1334e0bf48dedc12f744841e71725a9d98d50
class BaseTx(object): class BaseTx(object):
"""SimPEG Transmitter Object""" """SimPEG Transmitter Object"""
+29 -12
View File
@@ -70,20 +70,37 @@ def getIndecesBlock(p0,p1,ccMesh):
# Return a tuple # Return a tuple
return ind return ind
def defineBlockConductivity(ccMesh,p0,p1,condVals): def defineBlock(ccMesh,p0,p1,vals=[0,1]):
""" """
Build a block with the conductivity specified by condVal. Returns an array. Build a block with the conductivity specified by condVal. Returns an array.
condVals[0] conductivity of the block vals[0] conductivity of the block
condVals[1] conductivity of the ground vals[1] conductivity of the ground
""" """
sigma = np.zeros(ccMesh.shape[0]) + condVals[1] sigma = np.zeros(ccMesh.shape[0]) + vals[1]
ind = getIndecesBlock(p0,p1,ccMesh) ind = getIndecesBlock(p0,p1,ccMesh)
sigma[ind] = condVals[0] sigma[ind] = vals[0]
return sigma return sigma
def defineTwoLayeredConductivity(ccMesh,depth,condVals): def defineElipse(ccMesh, center=[0,0,0], anisotropy=[1,1,1], slope=10., theta=0.):
G = ccMesh.copy()
dim = ccMesh.shape[1]
for i in range(dim):
G[:, i] = G[:,i] - center[i]
theta = -theta*np.pi/180
M = np.array([[np.cos(theta),-np.sin(theta),0],[np.sin(theta),np.cos(theta),0],[0,0,1.]])
M = M[:dim,:dim]
G = M.dot(G.T).T
for i in range(dim):
G[:, i] = G[:,i]/anisotropy[i]*2.
D = np.sqrt(np.sum(G**2,axis=1))
return -np.arctan((D-1)*slope)*(2./np.pi)/2.+0.5
def defineTwoLayers(ccMesh,depth,vals=[0,1]):
""" """
Define a two layered model. Depth of the first layer must be specified. Define a two layered model. Depth of the first layer must be specified.
CondVals vector with the conductivity values of the layers. Eg: CondVals vector with the conductivity values of the layers. Eg:
@@ -94,7 +111,7 @@ def defineTwoLayeredConductivity(ccMesh,depth,condVals):
0 depth zf 0 depth zf
1st layer 2nd layer 1st layer 2nd layer
""" """
sigma = np.zeros(ccMesh.shape[0]) + condVals[1] sigma = np.zeros(ccMesh.shape[0]) + vals[1]
dim = np.size(ccMesh[0,:]) dim = np.size(ccMesh[0,:])
@@ -116,7 +133,7 @@ def defineTwoLayeredConductivity(ccMesh,depth,condVals):
ind = getIndecesBlock(p0,p1,ccMesh) ind = getIndecesBlock(p0,p1,ccMesh)
sigma[ind] = condVals[0]; sigma[ind] = vals[0];
return sigma return sigma
@@ -230,9 +247,9 @@ if __name__ == '__main__':
p0 = np.array([0.5,0.5,0.5])[:testDim] p0 = np.array([0.5,0.5,0.5])[:testDim]
p1 = np.array([1.0,1.0,1.0])[:testDim] p1 = np.array([1.0,1.0,1.0])[:testDim]
condVals = np.array([100,1e-6]) vals = np.array([100,1e-6])
sigma = defineBlockConductivity(ccMesh,p0,p1,condVals) sigma = defineBlockConductivity(ccMesh,p0,p1,vals)
# Plot sigma model # Plot sigma model
print sigma.shape print sigma.shape
@@ -242,10 +259,10 @@ if __name__ == '__main__':
# ----------------------------------------- # -----------------------------------------
print('Testing the two layered model') print('Testing the two layered model')
condVals = np.array([100,1e-5]); vals = np.array([100,1e-5]);
depth = 1.0; depth = 1.0;
sigma = defineTwoLayeredConductivity(ccMesh,depth,condVals) sigma = defineTwoLayeredConductivity(ccMesh,depth,vals)
M.plotImage(sigma) M.plotImage(sigma)
print sigma print sigma
-30
View File
@@ -1,30 +0,0 @@
.. _api_Forward:
Model
=====
.. automodule:: SimPEG.Model
:show-inheritance:
:members:
:undoc-members:
:inherited-members:
Survey
======
.. automodule:: SimPEG.Survey
:show-inheritance:
:members:
:undoc-members:
:inherited-members:
Problem
=======
.. automodule:: SimPEG.Problem
:show-inheritance:
:members:
:undoc-members:
:inherited-members:
+7 -3
View File
@@ -39,6 +39,7 @@ the implementations.
axes[1].set_title('TreeMesh') axes[1].set_title('TreeMesh')
rM.plotGrid(ax=axes[2], **opts) rM.plotGrid(ax=axes[2], **opts)
axes[2].set_title('LogicallyRectMesh') axes[2].set_title('LogicallyRectMesh')
plt.show()
Variable Locations and Terminology Variable Locations and Terminology
@@ -58,10 +59,12 @@ of the TensorMesh.
:include-source: :include-source:
from SimPEG import Mesh, np from SimPEG import Mesh, np
import matplotlib.pyplot as plt
hx = np.r_[3,2,1,1,1,1,2,3] hx = np.r_[3,2,1,1,1,1,2,3]
hy = np.r_[3,1,1,3] hy = np.r_[3,1,1,3]
M = Mesh.TensorMesh([hx, hy]) M = Mesh.TensorMesh([hx, hy])
M.plotGrid(centers=True) M.plotGrid(centers=True)
plt.show()
In this simple mesh, the hx vector defines the widths of the cell In this simple mesh, the hx vector defines the widths of the cell
@@ -85,6 +88,7 @@ plotted above as red circles. Other terminology for this mesh are:
M.plotGrid(faces=True, nodes=True) M.plotGrid(faces=True, nodes=True)
plt.title('Cell faces in the x- and y-directions.') plt.title('Cell faces in the x- and y-directions.')
plt.legend(('Nodes', 'X-Faces', 'Y-Faces')) plt.legend(('Nodes', 'X-Faces', 'Y-Faces'))
plt.show()
Generally, the faces are used to discretize fluxes, quantities that Generally, the faces are used to discretize fluxes, quantities that
leave or enter the cells. As such, these fluxes have a direction to leave or enter the cells. As such, these fluxes have a direction to
@@ -102,7 +106,7 @@ and live on the edges(!) of the cell.
:include-source: :include-source:
from SimPEG import Mesh from SimPEG import Mesh
Mesh.TensorMesh([1,1,1]).plotGrid(faces=True, edges=True, centers=True) Mesh.TensorMesh([1,1,1]).plotGrid(faces=True, edges=True, centers=True, showIt=True)
How many of each? How many of each?
----------------- -----------------
@@ -159,7 +163,7 @@ vector grid size.
:include-source: :include-source:
from SimPEG import Mesh from SimPEG import Mesh
Mesh.TensorMesh([4,5]).plotGrid(faces=True) Mesh.TensorMesh([4,5]).plotGrid(faces=True, showIt=True)
Making Tensors Making Tensors
@@ -183,7 +187,7 @@ notation::
from SimPEG import Mesh, Utils from SimPEG import Mesh, Utils
h1 = (5, 10, 1.5), (20, 5), (3, 10) h1 = (5, 10, 1.5), (20, 5), (3, 10)
M = Mesh.TensorMesh(Utils.meshTensors(h1, h1)) M = Mesh.TensorMesh(Utils.meshTensors(h1, h1))
M.plotGrid() M.plotGrid(showIt=True)
Hopefully, you now know how to create TensorMesh objects in SimPEG, Hopefully, you now know how to create TensorMesh objects in SimPEG,
and by extension you are also familiar with how to create and use and by extension you are also familiar with how to create and use
+45
View File
@@ -0,0 +1,45 @@
.. _api_Model:
Model
*****
A SimPEG model operates on a vector and transforms it to another space.
We will use an example commonly applied in electromagnetics (EM) of the
log-conductivity model (:class:`SimPEG.Model.LogModel`).
Electrical conductivity varies over many orders of magnitude, so it is a common
technique when solving the inverse problem to parameterize and optimize in terms
of log conductivity. This makes sense not only because it ensures all conductivities
will be positive, but because this is fundamentally the space where conductivity
lives (i.e. it varies logarithmically). In SimPEG, we use the term Model to
describe how to get between these two spaces.
The API
=======
.. autoclass:: SimPEG.Model.BaseModel
:members:
:undoc-members:
.. autoclass:: SimPEG.Model.BaseNonLinearModel
:members:
:undoc-members:
.. autoclass:: SimPEG.Model.ComboModel
:members:
:undoc-members:
Common Models
=============
.. autoclass:: SimPEG.Model.LogModel
:members:
:undoc-members:
.. autoclass:: SimPEG.Model.Vertical1DModel
:members:
:undoc-members:
.. autoclass:: SimPEG.Model.Mesh2Mesh
:members:
:undoc-members:
+71
View File
@@ -0,0 +1,71 @@
.. _api_Problem:
Problem
*******
The problem is a partial differential equation of the form:
.. math::
c(m, u) = 0
Here, \\(m\\) is the model and u is the field (or fields).
Given the model, \\(m\\), we can calculate the fields \\(u(m)\\),
however, the data we collect is a subset of the fields,
and can be defined by a linear projection, \\(P\\).
.. math::
d_\text{pred} = P u(m)
For the inverse problem, we are interested in how changing the model transforms the data,
as such we can take write the Taylor expansion:
.. math::
Pu(m + hv) = Pu(m) + hP\frac{\partial u(m)}{\partial m} v + \mathcal{O}(h^2 \left\| v \right\| )
We can linearize and define the sensitivity matrix as:
.. math::
J = P\frac{\partial u}{\partial m}
The sensitivity matrix, and it's transpose will be used in the inverse problem
to (locally) find how model parameters change the data, and optimize!
Working with the general PDE, \\(c(m, u) = 0\\), where m is the model and u is the field,
the sensitivity is defined as:
.. math::
J = P\frac{\partial u}{\partial m}
We can take the derivative of the PDE:
.. math::
\nabla_m c(m, u) \partial m + \nabla_u c(m, u) \partial u = 0
If the forward problem is invertible, then we can rearrange for \\(\\frac{\\partial u}{\\partial m}\\):
.. math::
J = - P \left( \nabla_u c(m, u) \right)^{-1} \nabla_m c(m, u)
This can often be computed given a vector (i.e. \\(J(v)\\)) rather than stored, as \\(J\\) is a large dense matrix.
.. math::
u(m)
The API
=======
.. automodule:: SimPEG.Problem
:members:
:undoc-members:
+11
View File
@@ -0,0 +1,11 @@
.. _api_Survey:
Survey
======
.. automodule:: SimPEG.Survey
:show-inheritance:
:members:
:undoc-members:
:inherited-members:
+3 -1
View File
@@ -39,7 +39,9 @@ Forward Problems
.. toctree:: .. toctree::
:maxdepth: 2 :maxdepth: 2
api_Forward api_Model
api_Survey
api_Problem
Inversion Inversion
********* *********