Compare commits

..
29 Commits
Author SHA1 Message Date
GudniRos 2fcdabf3d5 Fixed directive to save iteration dictionary. 2016-06-09 12:39:25 -07:00
GudniRos b965c96242 Updating examples by running python __init__.py 2016-06-07 13:48:29 -07:00
GudniRos f06ba238f8 Fixing Examples/__init__.py after merge conflict 2016-06-07 13:14:36 -07:00
GudniRos 11408a3788 Fixing an FDEM import to be more explicit 2016-06-07 11:52:29 -07:00
GudniRos 358e2f96af Pull request #328 on github.
Fixing MT namespace to NSEM.
2016-06-07 10:54:16 -07:00
GudniRos cf5181016f Made MT_1d_analytic_nLayer_Earth.run have default n layers of 3.
Trying to fix example testing.
2016-06-07 09:45:02 -07:00
GudniRos d1f7d0c37a Adding ipywidgets to the conda install list for travis 2016-06-01 21:54:19 -07:00
GudniRos 50c4a6caf8 Refactor and updated tests 2016-06-01 20:53:12 -07:00
GudniRos b3029b697b Adjust settings for the MT1D inversion example 2016-06-01 12:00:52 -07:00
GudniRos 8d4581e92f Cleaned MT_1D inverison example 2016-06-01 11:55:12 -07:00
Gudni Karl Rosenkjaer 0a44796d4c Merge pull request #329 from simpeg/em/dev
Em/dev into mt/dev
2016-06-01 11:17:19 -07:00
GudniRos 8117ee3b06 Fixing MT_1D example, runs but inversion results should be better. 2016-06-01 10:22:18 -07:00
GudniRos fb7f5a53d4 Working on examples 2016-06-01 01:05:21 -07:00
GudniRos f34314bba2 Fixed tests and Jvec 2016-05-31 22:33:57 -07:00
GudniRos 7087632701 Fixing name space, updating utils 2016-05-31 22:06:47 -07:00
GudniRos 6165e619ed Moving Problem1D/3D folders to NSEM file
Looking at making Jvec more general.
2016-05-10 16:07:45 -07:00
GudniRos 1ab67b5790 Updated MT-->NSEM for all the classes.
Changed Vertical1DMap --> SurjectVertical1D
2016-05-10 14:41:20 -07:00
Gudni Karl Rosenkjaer a96e9e08d7 Merge pull request #316 from simpeg/em/dev
Em/dev into mt/NSEMrefact
2016-05-10 14:09:13 -07:00
GudniRos b1569e5734 Changes to MT1D analytic example 2016-05-10 14:00:48 -07:00
GudniRos 22f0a742e7 Fixed UBC mesh read in function 2016-05-02 05:16:56 -07:00
GudniRos 18357a11da Fixing analytic function 2016-04-15 14:48:58 -07:00
GudniRos abc5d72725 Merge branch 'em/dev' into mt/dev 2016-04-15 13:29:39 -07:00
GudniRos 6482b94cf1 Merge branch 'master' into mt/dev 2016-04-05 10:26:03 -07:00
GudniRos 009806f2ba Merge remote-tracking branch 'origin/dev' into mt/dev 2016-03-29 16:52:01 -07:00
GudniRos 127c51974f Fixed errors in analytic solution 2016-03-10 08:10:21 -08:00
GudniRos e96945b991 Indexing 2016-03-09 14:30:39 -08:00
GudniRos fc444a345f Fixing dtypes in the MT1Danalytic 2016-03-09 14:24:15 -08:00
GudniRos 9a739d8380 Merge branch 'mt/dev' of https://github.com/simpeg/simpeg into mt/dev 2016-03-09 13:27:50 -08:00
GudniRos 664a7bb484 Updated plotting functions for MT 2016-03-09 13:26:50 -08:00
179 changed files with 3438 additions and 4011 deletions
+1 -1
View File
@@ -1,4 +1,4 @@
[bumpversion] [bumpversion]
current_version = 0.1.12 current_version = 0.1.10
files = setup.py SimPEG/__init__.py docs/conf.py files = setup.py SimPEG/__init__.py docs/conf.py
-2
View File
@@ -39,5 +39,3 @@ nosetests.xml
*.sublime-workspace *.sublime-workspace
docs/_build/ docs/_build/
Makefile Makefile
docs/warnings.txt
.DS_Store
+4 -26
View File
@@ -24,25 +24,18 @@ env:
- TEST_DIR=tests/examples - TEST_DIR=tests/examples
- TEST_DIR=tests/em/fdem/inverse/adjoint - TEST_DIR=tests/em/fdem/inverse/adjoint
- TEST_DIR=tests/em/fdem/forward - TEST_DIR=tests/em/fdem/forward
- TEST_DIR=tests/docs;
GAE_PYTHONPATH=${HOME}/.cache/google_appengine;
PATH=$PATH:${HOME}/google-cloud-sdk/bin;
PYTHONPATH=${PYTHONPATH}:${GAE_PYTHONPATH};
CLOUDSDK_CORE_DISABLE_PROMPTS=1
# Setup anaconda # Setup anaconda
before_install: before_install:
# Install packages - if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-3.8.3-Linux-x86_64.sh -O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-3.8.3-Linux-x86_64.sh -O miniconda.sh; fi
- if [ ${TRAVIS_PYTHON_VERSION:0:1} == "2" ]; then wget http://repo.continuum.io/miniconda/Miniconda-3.8.3-Linux-x86_64.sh
-O miniconda.sh; else wget http://repo.continuum.io/miniconda/Miniconda3-3.8.3-Linux-x86_64.sh
-O miniconda.sh; fi
- chmod +x miniconda.sh - chmod +x miniconda.sh
- ./miniconda.sh -b - ./miniconda.sh -b
- export PATH=/home/travis/anaconda/bin:/home/travis/miniconda/bin:$PATH - export PATH=/home/travis/anaconda/bin:/home/travis/miniconda/bin:$PATH
- conda update --yes conda - conda update --yes conda
# Install packages
install: install:
- conda install --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib cython ipython nose vtk sphinx - conda install --yes pip python=$TRAVIS_PYTHON_VERSION numpy scipy matplotlib cython ipython ipywidgets nose vtk
- pip install nose-cov python-coveralls - pip install nose-cov python-coveralls
- git clone https://github.com/rowanc1/pymatsolver.git - git clone https://github.com/rowanc1/pymatsolver.git
@@ -53,26 +46,11 @@ install:
# Run test # Run test
script: script:
# test docs
- nosetests $TEST_DIR --with-cov --cov SimPEG --cov-config .coveragerc -v -s - nosetests $TEST_DIR --with-cov --cov SimPEG --cov-config .coveragerc -v -s
# Calculate coverage # Calculate coverage
after_success: after_success:
- bash <(curl -s https://codecov.io/bash) - coveralls --config_file .coveragerc
- if [ "$TRAVIS_BRANCH" = "master" -a "$TRAVIS_PULL_REQUEST" = "false" ]; then
if [ ${TEST_DIR} == "tests/docs" ]; then
python scripts/fetch_gae_sdk.py $(dirname "${GAE_PYTHONPATH}");
openssl aes-256-cbc -K $encrypted_93066031461c_key -iv $encrypted_93066031461c_iv
-in docs/credentials.tar.gz.enc -out credentials.tar.gz -d ;
if [ ! -d ${HOME}/google-cloud-sdk ]; then curl https://sdk.cloud.google.com | bash; fi ;
tar -xzf credentials.tar.gz ;
gcloud auth activate-service-account --key-file client-secret.json ;
gcloud config set project simpegdocs;
gcloud -q components update gae-python;
gcloud -q preview app deploy ./docs/app.yaml --version ${TRAVIS_COMMIT} --promote;
fi;
fi
notifications: notifications:
email: email:
+6 -13
View File
@@ -1,4 +1,4 @@
.. image:: https://raw.github.com/simpeg/simpeg/master/docs/images/simpeg-logo.png .. image:: https://raw.github.com/simpeg/simpeg/master/docs/simpeg-logo.png
:alt: SimPEG Logo :alt: SimPEG Logo
====== ======
@@ -21,21 +21,14 @@ SimPEG
:target: https://travis-ci.org/simpeg/simpeg :target: https://travis-ci.org/simpeg/simpeg
:alt: Travis CI build status :alt: Travis CI build status
.. image:: https://img.shields.io/coveralls/simpeg/simpeg.svg
:target: https://coveralls.io/r/simpeg/simpeg?branch=master
:alt: Coverage status
.. image:: http://img.shields.io/badge/GITTER-JOIN_CHAT-brightgreen.svg?style=flat-square .. image:: http://img.shields.io/badge/GITTER-JOIN_CHAT-brightgreen.svg?style=flat-square
:alt: gitter chat room at https://gitter.im/simpeg/simpeg :alt: gitter chat room at https://gitter.im/simpeg/simpeg
:target: https://gitter.im/simpeg/simpeg :target: https://gitter.im/simpeg/simpeg
.. image:: https://codecov.io/gh/simpeg/simpeg/branch/master/graph/badge.svg
   :target: https://codecov.io/gh/simpeg/simpeg
.. image:: https://www.quantifiedcode.com/api/v1/project/933aa3decf444538aa432c8817169b6d/badge.svg
:target: https://www.quantifiedcode.com/app/project/933aa3decf444538aa432c8817169b6d
:alt: Code issues
.. image:: https://api.codacy.com/project/badge/Grade/4fc959a5294a418fa21fc7bc3b3aa078
:target: https://www.codacy.com/app/lindseyheagy/simpeg?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=simpeg/simpeg&amp;utm_campaign=Badge_Grade
:alt: codacy
Simulation and Parameter Estimation in Geophysics - A python package for simulation and gradient based parameter estimation in the context of geophysical applications. Simulation and Parameter Estimation in Geophysics - A python package for simulation and gradient based parameter estimation in the context of geophysical applications.
The vision is to create a package for finite volume simulation with applications to geophysical imaging and subsurface flow. To enable the understanding of the many different components, this package has the following features: The vision is to create a package for finite volume simulation with applications to geophysical imaging and subsurface flow. To enable the understanding of the many different components, this package has the following features:
+2 -2
View File
@@ -162,8 +162,8 @@ class ProblemDC_CC(Problem.BaseProblem):
""" """
Makes the matrix A(m) for the DC resistivity problem. Makes the matrix A(m) for the DC resistivity problem.
:param numpy.ndarray m: model :param numpy.array m: model
:rtype: scipy.sparse.csc_matrix :rtype: scipy.csc_matrix
:return: A(m) :return: A(m)
.. math:: .. math::
+1 -1
View File
@@ -71,7 +71,7 @@ class ProblemIP(Problem.BaseProblem):
Makes the matrix A(m) for the DC resistivity problem. Makes the matrix A(m) for the DC resistivity problem.
:param numpy.array m: model :param numpy.array m: model
:rtype: scipy.sparse.csc_matrix :rtype: scipy.csc_matrix
:return: A(m) :return: A(m)
.. math:: .. math::
+7 -7
View File
@@ -476,7 +476,7 @@ def writeUBC_DCobs(fileName, DCsurvey, dim, surveyType, iptype = 0):
fid.write('! ' + surveyType + ' FORMAT\n') fid.write('! ' + surveyType + ' FORMAT\n')
if iptype!=0: if iptype!=0:
fid.write('IPTYPE={0:d}\n'.format(iptype)) fid.write('IPTYPE=%i\n'%iptype)
else: else:
fid.write('! ' + stype + ' FORMAT\n') fid.write('! ' + stype + ' FORMAT\n')
@@ -512,7 +512,7 @@ def writeUBC_DCobs(fileName, DCsurvey, dim, surveyType, iptype = 0):
if surveyType == 'SURFACE': if surveyType == 'SURFACE':
fid.writelines("{0:f} ".format(ii) for ii in mkvc(tx[0,:])) fid.writelines("%f " % ii for ii in mkvc(tx[0,:]))
M = M[:,0] M = M[:,0]
N = N[:,0] N = N[:,0]
@@ -521,7 +521,7 @@ def writeUBC_DCobs(fileName, DCsurvey, dim, surveyType, iptype = 0):
# Flip sign for z-elevation to depth # Flip sign for z-elevation to depth
tx[2::2,:] = -tx[2::2,:] tx[2::2,:] = -tx[2::2,:]
fid.writelines("{0:e} ".format(ii) for ii in mkvc(tx[::2,:])) fid.writelines("%e " % ii for ii in mkvc(tx[::2,:]))
M = M[:,0::2] M = M[:,0::2]
N = N[:,0::2] N = N[:,0::2]
@@ -529,22 +529,22 @@ def writeUBC_DCobs(fileName, DCsurvey, dim, surveyType, iptype = 0):
M[:,1::2] = -M[:,1::2] M[:,1::2] = -M[:,1::2]
N[:,1::2] = -N[:,1::2] N[:,1::2] = -N[:,1::2]
fid.write('{0:d}\n'.format(nD)) fid.write('%i\n'% nD)
np.savetxt(fid, np.c_[ M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%f',delimiter=' ',newline='\n') np.savetxt(fid, np.c_[ M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%f',delimiter=' ',newline='\n')
if dim=='3D': if dim=='3D':
if surveyType == 'SURFACE': if surveyType == 'SURFACE':
fid.writelines("{0:e} ".format(ii) for ii in mkvc(tx[0:2,:])) fid.writelines("%e " % ii for ii in mkvc(tx[0:2,:]))
M = M[:,0:2] M = M[:,0:2]
N = N[:,0:2] N = N[:,0:2]
if surveyType == 'GENERAL': if surveyType == 'GENERAL':
fid.writelines("{0:e} ".format(ii) for ii in mkvc(tx[0:3,:])) fid.writelines("%e " % ii for ii in mkvc(tx[0:3,:]))
fid.write('{0:d}\n'.format(nD)) fid.write('%i\n'% nD)
np.savetxt(fid, np.c_[ M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%e',delimiter=' ',newline='\n') np.savetxt(fid, np.c_[ M, N , DCsurvey.dobs[count:count+nD], DCsurvey.std[count:count+nD] ], fmt='%e',delimiter=' ',newline='\n')
fid.write('\n') fid.write('\n')
+50 -72
View File
@@ -15,7 +15,7 @@ class InversionDirective(object):
@inversion.setter @inversion.setter
def inversion(self, i): def inversion(self, i):
if getattr(self,'_inversion',None) is not None: if getattr(self,'_inversion',None) is not None:
print 'Warning: InversionDirective {0!s} has switched to a new inversion.'.format(self.__name__) print 'Warning: InversionDirective %s has switched to a new inversion.' % self.__name__
self._inversion = i self._inversion = i
@property @property
@@ -47,7 +47,7 @@ class DirectiveList(object):
def __init__(self, *directives, **kwargs): def __init__(self, *directives, **kwargs):
self.dList = [] self.dList = []
for d in directives: for d in directives:
assert isinstance(d, InversionDirective), 'All directives must be InversionDirectives not {0!s}'.format(d.__name__) assert isinstance(d, InversionDirective), 'All directives must be InversionDirectives not %s' % d.__name__
self.dList.append(d) self.dList.append(d)
Utils.setKwargs(self, **kwargs) Utils.setKwargs(self, **kwargs)
@@ -68,7 +68,7 @@ class DirectiveList(object):
def inversion(self, i): def inversion(self, i):
if self.inversion is i: return if self.inversion is i: return
if getattr(self,'_inversion',None) is not None: if getattr(self,'_inversion',None) is not None:
print 'Warning: {0!s} has switched to a new inversion.'.format(self.__name__) print 'Warning: %s has switched to a new inversion.' % self.__name__
for d in self.dList: for d in self.dList:
d.inversion = i d.inversion = i
self._inversion = i self._inversion = i
@@ -79,7 +79,7 @@ class DirectiveList(object):
return return
directives = ['initialize', 'endIter', 'finish'] directives = ['initialize', 'endIter', 'finish']
assert ruleType in directives, 'Directive type must be in ["{0!s}"]'.format('", "'.join(directives)) assert ruleType in directives, 'Directive type must be in ["%s"]' % '", "'.join(directives)
for r in self.dList: for r in self.dList:
getattr(r, ruleType)() getattr(r, ruleType)()
@@ -141,7 +141,7 @@ class BetaSchedule(InversionDirective):
def endIter(self): def endIter(self):
if self.opt.iter > 0 and self.opt.iter % self.coolingRate == 0: if self.opt.iter > 0 and self.opt.iter % self.coolingRate == 0:
if self.debug: print 'BetaSchedule is cooling Beta. Iteration: {0:d}'.format(self.opt.iter) if self.debug: print 'BetaSchedule is cooling Beta. Iteration: %d' % self.opt.iter
self.invProb.beta /= self.coolingFactor self.invProb.beta /= self.coolingFactor
@@ -167,7 +167,7 @@ class TargetMisfit(InversionDirective):
class SaveEveryIteration(InversionDirective): class _SaveEveryIteration(InversionDirective):
@property @property
def name(self): def name(self):
if getattr(self, '_name', None) is None: if getattr(self, '_name', None) is None:
@@ -181,79 +181,71 @@ class SaveEveryIteration(InversionDirective):
def fileName(self): def fileName(self):
if getattr(self, '_fileName', None) is None: if getattr(self, '_fileName', None) is None:
from datetime import datetime from datetime import datetime
self._fileName = '{0!s}-{1!s}'.format(self.name, datetime.now().strftime('%Y-%m-%d-%H-%M')) self._fileName = '%s-%s'%(self.name, datetime.now().strftime('%Y-%m-%d-%H-%M'))
return self._fileName return self._fileName
@fileName.setter @fileName.setter
def fileName(self, value): def fileName(self, value):
self._fileName = value self._fileName = value
class SaveModelEveryIteration(SaveEveryIteration): class SaveModelEveryIteration(_SaveEveryIteration):
"""SaveModelEveryIteration""" """SaveModelEveryIteration"""
def initialize(self): def initialize(self):
print "SimPEG.SaveModelEveryIteration will save your models as: '###-{0!s}.npy'".format(self.fileName) print "SimPEG.SaveModelEveryIteration will save your models as: '###-%s.npy'"%self.fileName
def endIter(self): def endIter(self):
np.save('{0:03d}-{1!s}'.format(self.opt.iter, self.fileName), self.opt.xc) np.save('%03d-%s' % (self.opt.iter, self.fileName), self.opt.xc)
class SaveOutputEveryIteration(SaveEveryIteration): class SaveOutputEveryIteration(_SaveEveryIteration):
"""SaveModelEveryIteration""" """SaveModelEveryIteration"""
def initialize(self): def initialize(self):
print "SimPEG.SaveOutputEveryIteration will save your inversion progress as: '###-{0!s}.txt'".format(self.fileName) print "SimPEG.SaveOutputEveryIteration will save your inversion progress as: '###-%s.txt'"%self.fileName
f = open(self.fileName+'.txt', 'w') f = open(self.fileName+'.txt', 'w')
f.write(" # beta phi_d phi_m f\n") f.write(" # beta phi_d phi_m f\n")
f.close() f.close()
def endIter(self): def endIter(self):
f = open(self.fileName+'.txt', 'a') f = open(self.fileName+'.txt', 'a')
f.write(' {0:3d} {1:1.4e} {2:1.4e} {3:1.4e} {4:1.4e}\n'.format(self.opt.iter, self.invProb.beta, self.invProb.phi_d, self.invProb.phi_m, self.opt.f)) f.write(' %3d %1.4e %1.4e %1.4e %1.4e\n'%(self.opt.iter, self.invProb.beta, self.invProb.phi_d, self.invProb.phi_m, self.opt.f))
f.close() f.close()
class SaveOutputDictEveryIteration(SaveEveryIteration): class SaveOutputDictEveryIteration(_SaveEveryIteration):
"""SaveOutputDictEveryIteration""" """
Saves inversion parameters at every iteraion.
"""
def initialize(self): def initialize(self):
print "SimPEG.SaveOutputDictEveryIteration will save your inversion progress as dictionary: '###-{0!s}.npz'".format(self.fileName) print "SimPEG.SaveOutputDictEveryIteration will save your inversion progress as dictionary: '###-%s.npz'"%self.fileName
def endIter(self): def endIter(self):
# Save the data.
ms = self.reg.Ws * ( self.reg.mapping * (self.invProb.curModel - self.reg.mref) )
phi_ms = 0.5*ms.dot(ms)
if self.reg.mrefInSmooth == True:
mref = self.reg.mref
else:
mref = 0
mx = self.reg.Wx * ( self.reg.mapping * (self.invProb.curModel - mref) )
phi_mx = 0.5 * mx.dot(mx)
if self.prob.mesh.dim >= 2:
my = self.reg.Wy * ( self.reg.mapping * (self.invProb.curModel - mref) )
phi_my = 0.5 * my.dot(my)
else:
phi_my = 'NaN'
if self.prob.mesh.dim==3:
mz = self.reg.Wz * ( self.reg.mapping * (self.invProb.curModel - mref) )
phi_mz = 0.5 * mz.dot(mz)
else:
phi_mz = 'NaN'
# Initialize the output dict
outDict = {}
# Save the data.
outDict['iter'] = self.opt.iter
outDict['beta'] = self.invProb.beta
outDict['phi_d'] = self.invProb.phi_d
outDict['phi_ms'] = self.reg._evalSmall(self.invProb.curModel)
outDict['phi_mx'] = self.reg._evalSmoothx(self.invProb.curModel)
outDict['phi_my'] = self.reg._evalSmoothy(self.invProb.curModel) if self.prob.mesh.dim >= 2 else 'NaN'
outDict['phi_mz'] = self.reg._evalSmoothz(self.invProb.curModel) if self.prob.mesh.dim==3 else 'NaN'
outDict['f'] = self.opt.f
outDict['m'] = self.invProb.curModel
outDict['dpred'] = self.invProb.dpred
# Save the file as a npz # Save the file as a npz
np.savez('{:03d}-{:s}'.format(self.opt.iter,self.fileName), iter=self.opt.iter, beta=self.invProb.beta, phi_d=self.invProb.phi_d, phi_m=self.invProb.phi_m, phi_ms=phi_ms, phi_mx=phi_mx, phi_my=phi_my, phi_mz=phi_mz,f=self.opt.f, m=self.invProb.curModel,dpred=self.invProb.dpred) np.savez('{:03d}-{:s}'.format(self.opt.iter,self.fileName), outDict)
# 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.invProb.m_current
# return mref
class Update_IRLS(InversionDirective): class Update_IRLS(InversionDirective):
eps_min = None eps_min = None
eps = None eps_p = None
eps_q = None
norms = [2.,2.,2.,2.] norms = [2.,2.,2.,2.]
factor = None factor = None
gamma = None gamma = None
@@ -262,7 +254,6 @@ class Update_IRLS(InversionDirective):
f_old = None f_old = None
f_min_change = 1e-2 f_min_change = 1e-2
beta_tol = 5e-2 beta_tol = 5e-2
prctile = 95
# Solving parameter for IRLS (mode:2) # Solving parameter for IRLS (mode:2)
IRLSiter = 0 IRLSiter = 0
@@ -297,22 +288,9 @@ class Update_IRLS(InversionDirective):
print "Convergence with smooth l2-norm regularization: Start IRLS steps..." print "Convergence with smooth l2-norm regularization: Start IRLS steps..."
self.mode = 2 self.mode = 2
print self.eps_p, self.eps_q, self.norms
# Either use the supplied epsilon, or fix base on distribution of self.reg.eps_p = self.eps_p
# model values self.reg.eps_q = self.eps_q
if getattr(self, 'reg.eps', None) is None:
self.reg.eps_p = np.percentile(np.abs(self.invProb.curModel),self.prctile)
else:
self.reg.eps_p = self.eps[0]
if getattr(self, 'reg.eps', None) is None:
self.reg.eps_q = np.percentile(np.abs(self.reg.regmesh.cellDiffxStencil*(self.reg.mapping * self.invProb.curModel)),self.prctile)
else:
self.reg.eps_q = self.eps[1]
print "L[p qx qy qz]-norm : " + str(self.reg.norms)
print "eps_p: " + str(self.reg.eps_p) + " eps_q: " + str(self.reg.eps_q)
self.reg.norms = self.norms self.reg.norms = self.norms
self.coolingFactor = 1. self.coolingFactor = 1.
self.coolingRate = 1 self.coolingRate = 1
@@ -328,7 +306,7 @@ class Update_IRLS(InversionDirective):
# Beta Schedule # Beta Schedule
if self.opt.iter > 0 and self.opt.iter % self.coolingRate == 0: if self.opt.iter > 0 and self.opt.iter % self.coolingRate == 0:
if self.debug: print 'BetaSchedule is cooling Beta. Iteration: {0:d}'.format(self.opt.iter) if self.debug: print 'BetaSchedule is cooling Beta. Iteration: %d' % self.opt.iter
self.invProb.beta /= self.coolingFactor self.invProb.beta /= self.coolingFactor
@@ -340,11 +318,11 @@ class Update_IRLS(InversionDirective):
phim_new = self.reg.eval(self.invProb.curModel) phim_new = self.reg.eval(self.invProb.curModel)
self.f_change = np.abs(self.f_old - phim_new) / self.f_old self.f_change = np.abs(self.f_old - phim_new) / self.f_old
print "Regularization decrease: {0:6.3e}".format((self.f_change)) print "Regularization decrease: %6.3e" % (self.f_change)
# Check for maximum number of IRLS cycles # Check for maximum number of IRLS cycles
if self.IRLSiter == self.maxIRLSiter: if self.IRLSiter == self.maxIRLSiter:
print "Reach maximum number of IRLS cycles: {0:d}".format(self.maxIRLSiter) print "Reach maximum number of IRLS cycles: %i" % self.maxIRLSiter
self.opt.stopNextIteration = True self.opt.stopNextIteration = True
return return
@@ -356,14 +334,14 @@ class Update_IRLS(InversionDirective):
else: else:
self.f_old = phim_new self.f_old = phim_new
# # Cool the threshold parameter if required # Cool the threshold parameter if required
# if getattr(self, 'factor', None) is not None: if getattr(self, 'factor', None) is not None:
# eps = self.reg.eps / self.factor eps = self.reg.eps / self.factor
#
# if getattr(self, 'eps_min', None) is not None: if getattr(self, 'eps_min', None) is not None:
# self.reg.eps = np.max([self.eps_min,eps]) self.reg.eps = np.max([self.eps_min,eps])
# else: else:
# self.reg.eps = eps self.reg.eps = eps
# Get phi_m at the end of current iteration # Get phi_m at the end of current iteration
self.phi_m_last = self.invProb.phi_m_last self.phi_m_last = self.invProb.phi_m_last
-302
View File
@@ -1,302 +0,0 @@
from __future__ import division
import numpy as np
from scipy.constants import mu_0, pi, epsilon_0
from scipy.special import erf
from SimPEG import Utils
omega = lambda f: 2.*np.pi*f
# TODO:
# r = lambda dx, dy, dz: np.sqrt( dx**2. + dy**2. + dz**2.)
# k = lambda f, mu, epsilon, sig: np.sqrt( omega(f)**2. *mu*epsilon -1j*omega(f)*mu*sig )
def E_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=0., epsr=1.):
"""
Computing Analytic Electric fields from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
mu = mu_0*(1+kappa)
epsilon = epsilon_0*epsr
sig_hat = sig + 1j*omega(f)*epsilon
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
# Check
if XYZ.shape[0] > 1 & f.shape[0] > 1:
raise Exception("I/O type error: For multiple field locations only a single frequency can be specified.")
dx = XYZ[:,0]-srcLoc[0]
dy = XYZ[:,1]-srcLoc[1]
dz = XYZ[:,2]-srcLoc[2]
r = np.sqrt( dx**2. + dy**2. + dz**2.)
# k = np.sqrt( -1j*2.*np.pi*f*mu*sig )
k = np.sqrt( omega(f)**2. *mu*epsilon -1j*omega(f)*mu*sig )
front = current * length / (4.*np.pi*sig_hat* r**3) * np.exp(-1j*k*r)
mid = -k**2 * r**2 + 3*1j*k*r + 3
if orientation.upper() == 'X':
Ex = front*((dx**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
Ey = front*(dx*dy / r**2)*mid
Ez = front*(dx*dz / r**2)*mid
return Ex, Ey, Ez
elif orientation.upper() == 'Y':
# x--> y, y--> z, z-->x
Ey = front*((dy**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
Ez = front*(dy*dz / r**2)*mid
Ex = front*(dy*dx / r**2)*mid
return Ex, Ey, Ez
elif orientation.upper() == 'Z':
# x --> z, y --> x, z --> y
Ez = front*((dz**2 / r**2)*mid + (k**2 * r**2 -1j*k*r-1.))
Ex = front*(dz*dx / r**2)*mid
Ey = front*(dz*dy / r**2)*mid
return Ex, Ey, Ez
def E_galvanic_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=1., epsr=1.):
"""
Computing Galvanic portion of Electric fields from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
mu = mu_0*(1+kappa)
epsilon = epsilon_0*epsr
sig_hat = sig + 1j*omega(f)*epsilon
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
# Check
if XYZ.shape[0] > 1 & f.shape[0] > 1:
raise Exception("I/O type error: For multiple field locations only a single frequency can be specified.")
dx = XYZ[:,0]-srcLoc[0]
dy = XYZ[:,1]-srcLoc[1]
dz = XYZ[:,2]-srcLoc[2]
r = np.sqrt( dx**2. + dy**2. + dz**2.)
# k = np.sqrt( -1j*2.*np.pi*f*mu*sig )
k = np.sqrt( omega(f)**2. *mu*epsilon -1j*omega(f)*mu*sig )
front = current * length / (4.*np.pi*sig_hat* r**3) * np.exp(-1j*k*r)
mid = -k**2 * r**2 + 3*1j*k*r + 3
if orientation.upper() == 'X':
Ex_galvanic = front*((dx**2 / r**2)*mid + (-1j*k*r-1.))
Ey_galvanic = front*(dx*dy / r**2)*mid
Ez_galvanic = front*(dx*dz / r**2)*mid
return Ex_galvanic, Ey_galvanic, Ez_galvanic
elif orientation.upper() == 'Y':
# x--> y, y--> z, z-->x
Ey_galvanic = front*((dy**2 / r**2)*mid + (-1j*k*r-1.))
Ez_galvanic = front*(dy*dz / r**2)*mid
Ex_galvanic = front*(dy*dx / r**2)*mid
return Ex_galvanic, Ey_galvanic, Ez_galvanic
elif orientation.upper() == 'Z':
# x --> z, y --> x, z --> y
Ez_galvanic = front*((dz**2 / r**2)*mid + (-1j*k*r-1.))
Ex_galvanic = front*(dz*dx / r**2)*mid
Ey_galvanic = front*(dz*dy / r**2)*mid
return Ex_galvanic, Ey_galvanic, Ez_galvanic
def E_inductive_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=1., epsr=1.):
"""
Computing Inductive portion of Electric fields from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
mu = mu_0*(1+kappa)
epsilon = epsilon_0*epsr
sig_hat = sig + 1j*omega(f)*epsilon
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
# Check
if XYZ.shape[0] > 1 & f.shape[0] > 1:
raise Exception("I/O type error: For multiple field locations only a single frequency can be specified.")
dx = XYZ[:,0]-srcLoc[0]
dy = XYZ[:,1]-srcLoc[1]
dz = XYZ[:,2]-srcLoc[2]
r = np.sqrt( dx**2. + dy**2. + dz**2.)
# k = np.sqrt( -1j*2.*np.pi*f*mu*sig )
k = np.sqrt( omega(f)**2. *mu*epsilon -1j*omega(f)*mu*sig )
front = current * length / (4.*np.pi*sig_hat* r**3) * np.exp(-1j*k*r)
if orientation.upper() == 'X':
Ex_inductive = front*(k**2 * r**2)
Ey_inductive = np.zeros_like(Ex_inductive)
Ez_inductive = np.zeros_like(Ex_inductive)
return Ex_inductive, Ey_inductive, Ez_inductive
elif orientation.upper() == 'Y':
# x--> y, y--> z, z-->x
Ey_inductive = front*(k**2 * r**2)
Ez_inductive = np.zeros_like(Ey_inductive)
Ex_inductive = np.zeros_like(Ey_inductive)
return Ex_inductive, Ey_inductive, Ez_inductive
elif orientation.upper() == 'Z':
# x --> z, y --> x, z --> y
Ez_inductive = front*(k**2 * r**2)
Ex_inductive = np.zeros_like(Ez_inductive)
Ey_inductive = np.zeros_like(Ez_inductive)
return Ex_inductive, Ey_inductive, Ez_inductive
def J_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=1., epsr=1.):
"""
Computing Current densities from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
Ex, Ey, Ez = E_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=current, length=length, orientation=orientation, kappa=kappa, epsr=epsr)
Jx = sig*Ex
Jy = sig*Ey
Jz = sig*Ez
return Jx, Jy, Jz
def J_galvanic_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=1., epsr=1.):
"""
Computing Galvanic portion of Current densities from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
Ex_galvanic, Ey_galvanic, Ez_galvanic = E_galvanic_from_ElectricDipoleWholeSpaced(XYZ, srcLoc, sig, f, current=current, length=length, orientation=orientation, kappa=kappa, epsr=epsr)
Jx_galvanic = sig*Ex_galvanic
Jy_galvanic = sig*Ey_galvanic
Jz_galvanic = sig*Ez_galvanic
return Jx_galvanic, Jy_galvanic, Jz_galvanic
def J_inductive_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=1., epsr=1.):
"""
Computing Inductive portion of Current densities from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
Ex_inductive, Ey_inductive, Ez_inductive = E_inductive_from_ElectricDipoleWholeSpaced(XYZ, srcLoc, sig, f, current=current, length=length, orientation=orientation, kappa=kappa, epsr=epsr)
Jx_inductive = sig*Ex_inductive
Jy_inductive = sig*Ey_inductive
Jz_inductive = sig*Ez_inductive
return Jx_inductive, Jy_inductive, Jz_inductive
def H_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=1., epsr=1.):
"""
Computing Magnetic fields from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
mu = mu_0*(1+kappa)
epsilon = epsilon_0*epsr
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
# Check
if XYZ.shape[0] > 1 & f.shape[0] > 1:
raise Exception("I/O type error: For multiple field locations only a single frequency can be specified.")
dx = XYZ[:,0]-srcLoc[0]
dy = XYZ[:,1]-srcLoc[1]
dz = XYZ[:,2]-srcLoc[2]
r = np.sqrt( dx**2. + dy**2. + dz**2.)
# k = np.sqrt( -1j*2.*np.pi*f*mu*sig )
k = np.sqrt( omega(f)**2. *mu*epsilon -1j*omega(f)*mu*sig )
front = current * length / (4.*np.pi* r**2) * (-1j*k*r + 1) * np.exp(-1j*k*r)
if orientation.upper() == 'X':
Hy = front*(-dz / r)
Hz = front*(dy / r)
Hx = np.zeros_like(Hy)
return Hx, Hy, Hz
elif orientation.upper() == 'Y':
Hx = front*(dz / r)
Hz = front*(-dx / r)
Hy = np.zeros_like(Hx)
return Hx, Hy, Hz
elif orientation.upper() == 'Z':
Hx = front*(-dy / r)
Hy = front*(dx / r)
Hz = np.zeros_like(Hx)
return Hx, Hy, Hz
def B_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=1., epsr=1.):
"""
Computing Magnetic flux densites from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
Hx, Hy, Hz = H_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=current, length=length, orientation=orientation, kappa=kappa, epsr=epsr)
Bx = mu*Hx
By = mu*Hy
Bz = mu*Hz
return Bx, By, Bz
def A_from_ElectricDipoleWholeSpace(XYZ, srcLoc, sig, f, current=1., length=1., orientation='X', kappa=1., epsr=1.):
"""
Computing Electric vector potentials from Electrical Dipole in a Wholespace
TODO:
Add description of parameters
"""
mu = mu_0*(1+kappa)
epsilon = epsilon_0*epsr
XYZ = Utils.asArray_N_x_Dim(XYZ, 3)
# Check
if XYZ.shape[0] > 1 & f.shape[0] > 1:
raise Exception("I/O type error: For multiple field locations only a single frequency can be specified.")
dx = XYZ[:,0]-srcLoc[0]
dy = XYZ[:,1]-srcLoc[1]
dz = XYZ[:,2]-srcLoc[2]
r = np.sqrt( dx**2. + dy**2. + dz**2.)
k = np.sqrt( omega(f)**2. *mu*epsilon -1j*omega(f)*mu*sig )
front = current * length / (4.*np.pi*r)
if orientation.upper() == 'X':
Ax = front*np.exp(-1j*k*r)
Ay = np.zeros_like(Ax)
Az = np.zeros_like(Ax)
return Ax, Ay, Az
elif orientation.upper() == 'Y':
Ay = front*np.exp(-1j*k*r)
Ax = np.zeros_like(Ay)
Az = np.zeros_like(Ay)
return Ax, Ay, Az
elif orientation.upper() == 'Z':
Az = front*np.exp(-1j*k*r)
Ax = np.zeros_like(Ay)
Ay = np.zeros_like(Ay)
return Ax, Ay, Az
-1
View File
@@ -2,4 +2,3 @@ from TDEM import hzAnalyticDipoleT
from FDEM import hzAnalyticDipoleF from FDEM import hzAnalyticDipoleF
from FDEMcasing import * from FDEMcasing import *
from DC import DCAnalyticHalf, DCAnalyticSphere from DC import DCAnalyticHalf, DCAnalyticSphere
from FDEMDipolarfields import *
+3 -4
View File
@@ -20,10 +20,10 @@ class BaseEMProblem(Problem.BaseProblem):
Problem.BaseProblem.__init__(self, mesh, **kwargs) Problem.BaseProblem.__init__(self, mesh, **kwargs)
surveyPair = Survey.BaseSurvey #: The survey to pair with. surveyPair = Survey.BaseSurvey
dataPair = Survey.Data #: The data to pair with. dataPair = Survey.Data
PropMap = EMPropMap #: The property mapping PropMap = EMPropMap
Solver = SimpegSolver Solver = SimpegSolver
solverOpts = {} solverOpts = {}
@@ -217,7 +217,6 @@ class BaseEMSurvey(Survey.BaseSurvey):
def eval(self, f): def eval(self, f):
""" """
Project fields to receiver locations Project fields to receiver locations
:param Fields u: fields object :param Fields u: fields object
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: data :return: data
+38 -26
View File
@@ -6,11 +6,11 @@ from SimPEG.EM.Utils import omega
from SimPEG.Utils import Zero, Identity, sdiag from SimPEG.Utils import Zero, Identity, sdiag
class FieldsFDEM(SimPEG.Problem.Fields): class Fields(SimPEG.Problem.Fields):
""" """
Fancy Field Storage for a FDEM survey. Only one field type is stored for Fancy Field Storage for a FDEM survey. Only one field type is stored for
each problem, the rest are computed. The fields object acts like an array and is indexed by each problem, the rest are computed. The fields obejct acts like an array and is indexed by
.. code-block:: python .. code-block:: python
@@ -42,7 +42,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
:return: total electric field :return: total electric field
""" """
if getattr(self, '_ePrimary', None) is None or getattr(self, '_eSecondary', None) is None: if getattr(self, '_ePrimary', None) is None or getattr(self, '_eSecondary', None) is None:
raise NotImplementedError ('Getting e from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting e from %s is not implemented' %self.knownFields.keys()[0])
return self._ePrimary(solution,srcList) + self._eSecondary(solution,srcList) return self._ePrimary(solution,srcList) + self._eSecondary(solution,srcList)
@@ -56,7 +56,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
:return: total magnetic flux density :return: total magnetic flux density
""" """
if getattr(self, '_bPrimary', None) is None or getattr(self, '_bSecondary', None) is None: if getattr(self, '_bPrimary', None) is None or getattr(self, '_bSecondary', None) is None:
raise NotImplementedError ('Getting b from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting b from %s is not implemented' %self.knownFields.keys()[0])
return self._bPrimary(solution, srcList) + self._bSecondary(solution, srcList) return self._bPrimary(solution, srcList) + self._bSecondary(solution, srcList)
@@ -70,7 +70,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
:return: total magnetic field :return: total magnetic field
""" """
if getattr(self, '_hPrimary', None) is None or getattr(self, '_hSecondary', None) is None: if getattr(self, '_hPrimary', None) is None or getattr(self, '_hSecondary', None) is None:
raise NotImplementedError ('Getting h from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting h from %s is not implemented' %self.knownFields.keys()[0])
return self._hPrimary(solution, srcList) + self._hSecondary(solution, srcList) return self._hPrimary(solution, srcList) + self._hSecondary(solution, srcList)
@@ -84,7 +84,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
:return: total current density :return: total current density
""" """
if getattr(self, '_jPrimary', None) is None or getattr(self, '_jSecondary', None) is None: if getattr(self, '_jPrimary', None) is None or getattr(self, '_jSecondary', None) is None:
raise NotImplementedError ('Getting j from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting j from %s is not implemented' %self.knownFields.keys()[0])
return self._jPrimary(solution, srcList) + self._jSecondary(solution, srcList) return self._jPrimary(solution, srcList) + self._jSecondary(solution, srcList)
@@ -92,7 +92,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
""" """
Total derivative of e with respect to the inversion model. Returns :math:`d\mathbf{e}/d\mathbf{m}` for forward and (:math:`d\mathbf{e}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint Total derivative of e with respect to the inversion model. Returns :math:`d\mathbf{e}/d\mathbf{m}` for forward and (:math:`d\mathbf{e}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: source :param Src src: sorce
:param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint) :param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint)
:param numpy.ndarray v: vector to take sensitivity product with :param numpy.ndarray v: vector to take sensitivity product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
@@ -100,7 +100,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
:return: derivative times a vector (or tuple for adjoint) :return: derivative times a vector (or tuple for adjoint)
""" """
if getattr(self, '_eDeriv_u', None) is None or getattr(self, '_eDeriv_m', None) is None: if getattr(self, '_eDeriv_u', None) is None or getattr(self, '_eDeriv_m', None) is None:
raise NotImplementedError ('Getting eDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting eDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._eDeriv_u(src, v, adjoint), self._eDeriv_m(src, v, adjoint) return self._eDeriv_u(src, v, adjoint), self._eDeriv_m(src, v, adjoint)
@@ -110,7 +110,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
""" """
Total derivative of b with respect to the inversion model. Returns :math:`d\mathbf{b}/d\mathbf{m}` for forward and (:math:`d\mathbf{b}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint Total derivative of b with respect to the inversion model. Returns :math:`d\mathbf{b}/d\mathbf{m}` for forward and (:math:`d\mathbf{b}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: source :param Src src: sorce
:param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint) :param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint)
:param numpy.ndarray v: vector to take sensitivity product with :param numpy.ndarray v: vector to take sensitivity product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
@@ -118,7 +118,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
:return: derivative times a vector (or tuple for adjoint) :return: derivative times a vector (or tuple for adjoint)
""" """
if getattr(self, '_bDeriv_u', None) is None or getattr(self, '_bDeriv_m', None) is None: if getattr(self, '_bDeriv_u', None) is None or getattr(self, '_bDeriv_m', None) is None:
raise NotImplementedError ('Getting bDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting bDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._bDeriv_u(src, v, adjoint), self._bDeriv_m(src, v, adjoint) return self._bDeriv_u(src, v, adjoint), self._bDeriv_m(src, v, adjoint)
@@ -128,7 +128,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
""" """
Total derivative of h with respect to the inversion model. Returns :math:`d\mathbf{h}/d\mathbf{m}` for forward and (:math:`d\mathbf{h}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint Total derivative of h with respect to the inversion model. Returns :math:`d\mathbf{h}/d\mathbf{m}` for forward and (:math:`d\mathbf{h}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: source :param Src src: sorce
:param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint) :param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint)
:param numpy.ndarray v: vector to take sensitivity product with :param numpy.ndarray v: vector to take sensitivity product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
@@ -136,7 +136,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
:return: derivative times a vector (or tuple for adjoint) :return: derivative times a vector (or tuple for adjoint)
""" """
if getattr(self, '_hDeriv_u', None) is None or getattr(self, '_hDeriv_m', None) is None: if getattr(self, '_hDeriv_u', None) is None or getattr(self, '_hDeriv_m', None) is None:
raise NotImplementedError ('Getting hDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting hDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._hDeriv_u(src, v, adjoint), self._hDeriv_m(src, v, adjoint) return self._hDeriv_u(src, v, adjoint), self._hDeriv_m(src, v, adjoint)
@@ -146,7 +146,7 @@ class FieldsFDEM(SimPEG.Problem.Fields):
""" """
Total derivative of j with respect to the inversion model. Returns :math:`d\mathbf{j}/d\mathbf{m}` for forward and (:math:`d\mathbf{j}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint Total derivative of j with respect to the inversion model. Returns :math:`d\mathbf{j}/d\mathbf{m}` for forward and (:math:`d\mathbf{j}/d\mathbf{u}`, :math:`d\mathb{u}/d\mathbf{m}`) for the adjoint
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: source :param Src src: sorce
:param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint) :param numpy.ndarray du_dm_v: derivative of the solution vector with respect to the model times a vector (is None for adjoint)
:param numpy.ndarray v: vector to take sensitivity product with :param numpy.ndarray v: vector to take sensitivity product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
@@ -154,18 +154,18 @@ class FieldsFDEM(SimPEG.Problem.Fields):
:return: derivative times a vector (or tuple for adjoint) :return: derivative times a vector (or tuple for adjoint)
""" """
if getattr(self, '_jDeriv_u', None) is None or getattr(self, '_jDeriv_m', None) is None: if getattr(self, '_jDeriv_u', None) is None or getattr(self, '_jDeriv_m', None) is None:
raise NotImplementedError ('Getting jDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting jDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._jDeriv_u(src, v, adjoint), self._jDeriv_m(src, v, adjoint) return self._jDeriv_u(src, v, adjoint), self._jDeriv_m(src, v, adjoint)
return np.array(self._jDeriv_u(src, du_dm_v, adjoint) + self._jDeriv_m(src, v, adjoint), dtype = complex) return np.array(self._jDeriv_u(src, du_dm_v, adjoint) + self._jDeriv_m(src, v, adjoint), dtype = complex)
class Fields3D_e(FieldsFDEM): class Fields3D_e(Fields):
""" """
Fields object for Problem3D_e. Fields object for Problem3D_e.
:param BaseMesh mesh: mesh :param Mesh mesh: mesh
:param SimPEG.EM.FDEM.SurveyFDEM.Survey survey: survey :param Survey survey: survey
""" """
knownFields = {'eSolution':'E'} knownFields = {'eSolution':'E'}
@@ -180,6 +180,9 @@ class Fields3D_e(FieldsFDEM):
'h' : ['eSolution','CCV','_h'], 'h' : ['eSolution','CCV','_h'],
} }
def __init__(self, mesh, survey, **kwargs):
Fields.__init__(self, mesh, survey, **kwargs)
def startup(self): def startup(self):
self.prob = self.survey.prob self.prob = self.survey.prob
self._edgeCurl = self.survey.prob.mesh.edgeCurl self._edgeCurl = self.survey.prob.mesh.edgeCurl
@@ -423,12 +426,12 @@ class Fields3D_e(FieldsFDEM):
class Fields3D_b(FieldsFDEM): class Fields3D_b(Fields):
""" """
Fields object for Problem3D_b. Fields object for Problem3D_b.
:param BaseMesh mesh: mesh :param Mesh mesh: mesh
:param SimPEG.EM.FDEM.SurveyFDEM.Survey survey: survey :param Survey survey: survey
""" """
knownFields = {'bSolution':'F'} knownFields = {'bSolution':'F'}
@@ -443,6 +446,9 @@ class Fields3D_b(FieldsFDEM):
'h' : ['bSolution','CCV','_h'], 'h' : ['bSolution','CCV','_h'],
} }
def __init__(self,mesh,survey,**kwargs):
Fields.__init__(self,mesh,survey,**kwargs)
def startup(self): def startup(self):
self.prob = self.survey.prob self.prob = self.survey.prob
self._edgeCurl = self.survey.prob.mesh.edgeCurl self._edgeCurl = self.survey.prob.mesh.edgeCurl
@@ -687,12 +693,12 @@ class Fields3D_b(FieldsFDEM):
return Zero() return Zero()
class Fields3D_j(FieldsFDEM): class Fields3D_j(Fields):
""" """
Fields object for Problem3D_j. Fields object for Problem3D_j.
:param BaseMesh mesh: mesh :param Mesh mesh: mesh
:param SimPEG.EM.FDEM.SurveyFDEM.Survey survey: survey :param Survey survey: survey
""" """
knownFields = {'jSolution':'F'} knownFields = {'jSolution':'F'}
@@ -707,6 +713,9 @@ class Fields3D_j(FieldsFDEM):
'b' : ['jSolution','CCV','_b'], 'b' : ['jSolution','CCV','_b'],
} }
def __init__(self,mesh,survey,**kwargs):
Fields.__init__(self,mesh,survey,**kwargs)
def startup(self): def startup(self):
self.prob = self.survey.prob self.prob = self.survey.prob
self._edgeCurl = self.survey.prob.mesh.edgeCurl self._edgeCurl = self.survey.prob.mesh.edgeCurl
@@ -979,12 +988,12 @@ class Fields3D_j(FieldsFDEM):
return 1./(1j * omega(src.freq)) * VI * (self._aveE2CCV * ( s_mDeriv(v) - self._edgeCurl.T * ( self._MfRhoDeriv(jSolution) * v ) ) ) return 1./(1j * omega(src.freq)) * VI * (self._aveE2CCV * ( s_mDeriv(v) - self._edgeCurl.T * ( self._MfRhoDeriv(jSolution) * v ) ) )
class Fields3D_h(FieldsFDEM): class Fields3D_h(Fields):
""" """
Fields object for Problem3D_h. Fields object for Problem3D_h.
:param BaseMesh mesh: mesh :param Mesh mesh: mesh
:param SimPEG.EM.FDEM.SurveyFDEM.Survey survey: survey :param Survey survey: survey
""" """
knownFields = {'hSolution':'E'} knownFields = {'hSolution':'E'}
@@ -999,6 +1008,9 @@ class Fields3D_h(FieldsFDEM):
'b' : ['hSolution','CCV','_b'], 'b' : ['hSolution','CCV','_b'],
} }
def __init__(self,mesh,survey,**kwargs):
Fields.__init__(self,mesh,survey,**kwargs)
def startup(self): def startup(self):
self.prob = self.survey.prob self.prob = self.survey.prob
self._edgeCurl = self.survey.prob.mesh.edgeCurl self._edgeCurl = self.survey.prob.mesh.edgeCurl
+16 -21
View File
@@ -1,7 +1,7 @@
from SimPEG import Problem, Utils, np, sp, Solver as SimpegSolver from SimPEG import Problem, Utils, np, sp, Solver as SimpegSolver
from scipy.constants import mu_0 from scipy.constants import mu_0
from SurveyFDEM import Survey as SurveyFDEM from SurveyFDEM import Survey as SurveyFDEM
from FieldsFDEM import FieldsFDEM, Fields3D_e, Fields3D_b, Fields3D_h, Fields3D_j from FieldsFDEM import Fields, Fields3D_e, Fields3D_b, Fields3D_h, Fields3D_j
from SimPEG.EM.Base import BaseEMProblem from SimPEG.EM.Base import BaseEMProblem
from SimPEG.EM.Utils import omega from SimPEG.EM.Utils import omega
@@ -31,11 +31,10 @@ class BaseFDEMProblem(BaseEMProblem):
if using the H-J formulation (:code:`Problem3D_j` or :code:`Problem3D_h`). Note that here, :math:`\mathbf{s_m}` is an integrated quantity. if using the H-J formulation (:code:`Problem3D_j` or :code:`Problem3D_h`). Note that here, :math:`\mathbf{s_m}` is an integrated quantity.
The problem performs the elimination so that we are solving the system for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or \\\(\\\mathbf{h}\\\) The problem performs the elimination so that we are solving the system for \\\(\\\mathbf{e},\\\mathbf{b},\\\mathbf{j} \\\) or \\\(\\\mathbf{h}\\\)
""" """
surveyPair = SurveyFDEM surveyPair = SurveyFDEM
fieldsPair = FieldsFDEM fieldsPair = Fields
def fields(self, m): def fields(self, m):
""" """
@@ -65,7 +64,7 @@ class BaseFDEMProblem(BaseEMProblem):
:param numpy.array m: inversion model (nP,) :param numpy.array m: inversion model (nP,)
:param numpy.array v: vector which we take sensitivity product with (nP,) :param numpy.array v: vector which we take sensitivity product with (nP,)
:param SimPEG.EM.FDEM.FieldsFDEM.FieldsFDEM u: fields object :param SimPEG.EM.FDEM.Fields u: fields object
:rtype numpy.array: :rtype numpy.array:
:return: Jv (ndata,) :return: Jv (ndata,)
""" """
@@ -100,7 +99,7 @@ class BaseFDEMProblem(BaseEMProblem):
:param numpy.array m: inversion model (nP,) :param numpy.array m: inversion model (nP,)
:param numpy.array v: vector which we take adjoint product with (nP,) :param numpy.array v: vector which we take adjoint product with (nP,)
:param SimPEG.EM.FDEM.FieldsFDEM.FieldsFDEM u: fields object :param SimPEG.EM.FDEM.Fields u: fields object
:rtype numpy.array: :rtype numpy.array:
:return: Jv (ndata,) :return: Jv (ndata,)
""" """
@@ -154,8 +153,8 @@ class BaseFDEMProblem(BaseEMProblem):
Evaluates the sources for a given frequency and puts them in matrix form Evaluates the sources for a given frequency and puts them in matrix form
:param float freq: Frequency :param float freq: Frequency
:rtype: tuple :rtype: (numpy.ndarray, numpy.ndarray)
:return: (s_m, s_e) (nE or nF, nSrc) :return: s_m, s_e (nE or nF, nSrc)
""" """
Srcs = self.survey.getSrcByFreq(freq) Srcs = self.survey.getSrcByFreq(freq)
if self._formulation is 'EB': if self._formulation is 'EB':
@@ -195,7 +194,7 @@ class Problem3D_e(BaseFDEMProblem):
which we solve for :math:`\mathbf{e}`. which we solve for :math:`\mathbf{e}`.
:param SimPEG.Mesh.BaseMesh.BaseMesh mesh: mesh :param SimPEG.Mesh mesh: mesh
""" """
_solutionType = 'eSolution' _solutionType = 'eSolution'
@@ -270,7 +269,7 @@ class Problem3D_e(BaseFDEMProblem):
Derivative of the right hand side with respect to the model Derivative of the right hand side with respect to the model
:param float freq: frequency :param float freq: frequency
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: FDEM source :param SimPEG.EM.FDEM.Src src: FDEM source
:param numpy.ndarray v: vector to take product with :param numpy.ndarray v: vector to take product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
:rtype: numpy.ndarray :rtype: numpy.ndarray
@@ -306,7 +305,7 @@ class Problem3D_b(BaseFDEMProblem):
.. note :: .. note ::
The inverse problem will not work with full anisotropy The inverse problem will not work with full anisotropy
:param SimPEG.Mesh.BaseMesh.BaseMesh mesh: mesh :param SimPEG.Mesh mesh: mesh
""" """
_solutionType = 'bSolution' _solutionType = 'bSolution'
@@ -401,7 +400,7 @@ class Problem3D_b(BaseFDEMProblem):
Derivative of the right hand side with respect to the model Derivative of the right hand side with respect to the model
:param float freq: frequency :param float freq: frequency
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: FDEM source :param SimPEG.EM.FDEM.Src src: FDEM source
:param numpy.ndarray v: vector to take product with :param numpy.ndarray v: vector to take product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
:rtype: numpy.ndarray :rtype: numpy.ndarray
@@ -445,7 +444,6 @@ class Problem3D_j(BaseFDEMProblem):
\mathbf{h} = \\frac{1}{i \omega} \mathbf{M_{\mu}^e}^{-1} \\left(-\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{j} + \mathbf{M^e} \mathbf{s_m} \\right) \mathbf{h} = \\frac{1}{i \omega} \mathbf{M_{\mu}^e}^{-1} \\left(-\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{j} + \mathbf{M^e} \mathbf{s_m} \\right)
and solve for \\\(\\\mathbf{j}\\\) using and solve for \\\(\\\mathbf{j}\\\) using
.. math :: .. math ::
@@ -455,7 +453,7 @@ class Problem3D_j(BaseFDEMProblem):
.. note:: .. note::
This implementation does not yet work with full anisotropy!! This implementation does not yet work with full anisotropy!!
:param SimPEG.Mesh.BaseMesh.BaseMesh mesh: mesh :param SimPEG.Mesh mesh: mesh
""" """
_solutionType = 'jSolution' _solutionType = 'jSolution'
@@ -531,8 +529,8 @@ class Problem3D_j(BaseFDEMProblem):
\mathbf{RHS} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1}\mathbf{s_m} -i\omega \mathbf{s_e} \mathbf{RHS} = \mathbf{C} \mathbf{M_{\mu}^e}^{-1}\mathbf{s_m} -i\omega \mathbf{s_e}
:param float freq: Frequency :param float freq: Frequency
:rtype: numpy.ndarray :rtype: numpy.ndarray (nE, nSrc)
:return: RHS (nE, nSrc) :return: RHS
""" """
s_m, s_e = self.getSourceTerm(freq) s_m, s_e = self.getSourceTerm(freq)
@@ -551,7 +549,7 @@ class Problem3D_j(BaseFDEMProblem):
Derivative of the right hand side with respect to the model Derivative of the right hand side with respect to the model
:param float freq: frequency :param float freq: frequency
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: FDEM source :param SimPEG.EM.FDEM.Src src: FDEM source
:param numpy.ndarray v: vector to take product with :param numpy.ndarray v: vector to take product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
:rtype: numpy.ndarray :rtype: numpy.ndarray
@@ -593,7 +591,7 @@ class Problem3D_h(BaseFDEMProblem):
\\left(\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}\\right) \mathbf{h} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{s_e} \\left(\mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}\\right) \mathbf{h} = \mathbf{M^e} \mathbf{s_m} + \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{s_e}
:param SimPEG.Mesh.BaseMesh.BaseMesh mesh: mesh :param SimPEG.Mesh mesh: mesh
""" """
_solutionType = 'hSolution' _solutionType = 'hSolution'
@@ -610,11 +608,9 @@ class Problem3D_h(BaseFDEMProblem):
.. math:: .. math::
\mathbf{A} = \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e} \mathbf{A} = \mathbf{C}^{\\top} \mathbf{M_{\\rho}^f} \mathbf{C} + i \omega \mathbf{M_{\mu}^e}
:param float freq: Frequency :param float freq: Frequency
:rtype: scipy.sparse.csr_matrix :rtype: scipy.sparse.csr_matrix
:return: A :return: A
""" """
MeMu = self.MeMu MeMu = self.MeMu
@@ -657,7 +653,6 @@ class Problem3D_h(BaseFDEMProblem):
:param float freq: Frequency :param float freq: Frequency
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: RHS (nE, nSrc) :return: RHS (nE, nSrc)
""" """
s_m, s_e = self.getSourceTerm(freq) s_m, s_e = self.getSourceTerm(freq)
@@ -671,7 +666,7 @@ class Problem3D_h(BaseFDEMProblem):
Derivative of the right hand side with respect to the model Derivative of the right hand side with respect to the model
:param float freq: frequency :param float freq: frequency
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: FDEM source :param SimPEG.EM.FDEM.Src src: FDEM source
:param numpy.ndarray v: vector to take product with :param numpy.ndarray v: vector to take product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
:rtype: numpy.ndarray :rtype: numpy.ndarray
+7 -7
View File
@@ -11,8 +11,8 @@ class BaseRx(SimPEG.Survey.BaseRx):
""" """
def __init__(self, locs, orientation=None, component=None): def __init__(self, locs, orientation=None, component=None):
assert(orientation in ['x','y','z']), "Orientation {0!s} not known. Orientation must be in 'x', 'y', 'z'. Arbitrary orientations have not yet been implemented.".format(orientation) assert(orientation in ['x','y','z']), "Orientation %s not known. Orientation must be in 'x', 'y', 'z'. Arbitrary orientations have not yet been implemented."%orientation
assert(component in ['real', 'imag']), "'component' must be 'real' or 'imag', not {0!s}".format(component) assert(component in ['real', 'imag']), "'component' must be 'real' or 'imag', not %s"%component
self.projComp = orientation self.projComp = orientation
self.component = component self.component = component
@@ -25,10 +25,10 @@ class BaseRx(SimPEG.Survey.BaseRx):
def eval(self, src, mesh, f): def eval(self, src, mesh, f):
""" """
Project fields to receivers to get data. Project fields to recievers to get data.
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: FDEM source :param Source src: FDEM source
:param BaseMesh mesh: mesh used :param Mesh mesh: mesh used
:param Fields f: fields object :param Fields f: fields object
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: fields projected to recievers :return: fields projected to recievers
@@ -44,8 +44,8 @@ class BaseRx(SimPEG.Survey.BaseRx):
""" """
Derivative of projected fields with respect to the inversion model times a vector. Derivative of projected fields with respect to the inversion model times a vector.
:param SimPEG.EM.FDEM.SrcFDEM.BaseSrc src: FDEM source :param Source src: FDEM source
:param BaseMesh mesh: mesh used :param Mesh mesh: mesh used
:param Fields f: fields object :param Fields f: fields object
:param numpy.ndarray v: vector to multiply :param numpy.ndarray v: vector to multiply
:rtype: numpy.ndarray :rtype: numpy.ndarray
+28 -28
View File
@@ -23,8 +23,8 @@ class BaseSrc(Survey.BaseSrc):
- :math:`s_m` : magnetic source term - :math:`s_m` : magnetic source term
- :math:`s_e` : electric source term - :math:`s_e` : electric source term
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: tuple :rtype: (numpy.ndarray, numpy.ndarray)
:return: tuple with magnetic source term and electric source term :return: tuple with magnetic source term and electric source term
""" """
s_m = self.s_m(prob) s_m = self.s_m(prob)
@@ -37,10 +37,10 @@ class BaseSrc(Survey.BaseSrc):
- :code:`s_mDeriv` : derivative of the magnetic source term - :code:`s_mDeriv` : derivative of the magnetic source term
- :code:`s_eDeriv` : derivative of the electric source term - :code:`s_eDeriv` : derivative of the electric source term
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:param numpy.ndarray v: vector to take product with :param numpy.ndarray v: vector to take product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
:rtype: tuple :rtype: (numpy.ndarray, numpy.ndarray)
:return: tuple with magnetic source term and electric source term derivatives times a vector :return: tuple with magnetic source term and electric source term derivatives times a vector
""" """
if v is not None: if v is not None:
@@ -52,7 +52,7 @@ class BaseSrc(Survey.BaseSrc):
""" """
Primary magnetic flux density Primary magnetic flux density
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic flux density :return: primary magnetic flux density
""" """
@@ -64,7 +64,7 @@ class BaseSrc(Survey.BaseSrc):
""" """
Primary magnetic field Primary magnetic field
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -76,7 +76,7 @@ class BaseSrc(Survey.BaseSrc):
""" """
Primary electric field Primary electric field
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary electric field :return: primary electric field
""" """
@@ -88,7 +88,7 @@ class BaseSrc(Survey.BaseSrc):
""" """
Primary current density Primary current density
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary current density :return: primary current density
""" """
@@ -100,7 +100,7 @@ class BaseSrc(Survey.BaseSrc):
""" """
Magnetic source term Magnetic source term
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: magnetic source term on mesh :return: magnetic source term on mesh
""" """
@@ -110,7 +110,7 @@ class BaseSrc(Survey.BaseSrc):
""" """
Electric source term Electric source term
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: electric source term on mesh :return: electric source term on mesh
""" """
@@ -120,7 +120,7 @@ class BaseSrc(Survey.BaseSrc):
""" """
Derivative of magnetic source term with respect to the inversion model Derivative of magnetic source term with respect to the inversion model
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:param numpy.ndarray v: vector to take product with :param numpy.ndarray v: vector to take product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
:rtype: numpy.ndarray :rtype: numpy.ndarray
@@ -133,7 +133,7 @@ class BaseSrc(Survey.BaseSrc):
""" """
Derivative of electric source term with respect to the inversion model Derivative of electric source term with respect to the inversion model
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:param numpy.ndarray v: vector to take product with :param numpy.ndarray v: vector to take product with
:param bool adjoint: adjoint? :param bool adjoint: adjoint?
:rtype: numpy.ndarray :rtype: numpy.ndarray
@@ -162,7 +162,7 @@ class RawVec_e(BaseSrc):
""" """
Electric source term Electric source term
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: electric source term on mesh :return: electric source term on mesh
""" """
@@ -191,7 +191,7 @@ class RawVec_m(BaseSrc):
""" """
Magnetic source term Magnetic source term
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: magnetic source term on mesh :return: magnetic source term on mesh
""" """
@@ -220,7 +220,7 @@ class RawVec(BaseSrc):
""" """
Magnetic source term Magnetic source term
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: magnetic source term on mesh :return: magnetic source term on mesh
""" """
@@ -232,7 +232,7 @@ class RawVec(BaseSrc):
""" """
Electric source term Electric source term
:param BaseFDEMProblem prob: FDEM Problem :param Problem prob: FDEM Problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: electric source term on mesh :return: electric source term on mesh
""" """
@@ -301,7 +301,7 @@ class MagDipole(BaseSrc):
""" """
The primary magnetic flux density from a magnetic vector potential The primary magnetic flux density from a magnetic vector potential
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -339,7 +339,7 @@ class MagDipole(BaseSrc):
""" """
The primary magnetic field from a magnetic vector potential The primary magnetic field from a magnetic vector potential
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -350,7 +350,7 @@ class MagDipole(BaseSrc):
""" """
The magnetic source term The magnetic source term
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -364,7 +364,7 @@ class MagDipole(BaseSrc):
""" """
The electric source term The electric source term
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -416,7 +416,7 @@ class MagDipole_Bfield(BaseSrc):
""" """
The primary magnetic flux density from the analytic solution for magnetic fields from a dipole The primary magnetic flux density from the analytic solution for magnetic fields from a dipole
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -455,7 +455,7 @@ class MagDipole_Bfield(BaseSrc):
""" """
The primary magnetic field from a magnetic vector potential The primary magnetic field from a magnetic vector potential
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -466,7 +466,7 @@ class MagDipole_Bfield(BaseSrc):
""" """
The magnetic source term The magnetic source term
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -479,7 +479,7 @@ class MagDipole_Bfield(BaseSrc):
""" """
The electric source term The electric source term
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -530,7 +530,7 @@ class CircularLoop(BaseSrc):
""" """
The primary magnetic flux density from a magnetic vector potential The primary magnetic flux density from a magnetic vector potential
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -567,7 +567,7 @@ class CircularLoop(BaseSrc):
""" """
The primary magnetic field from a magnetic vector potential The primary magnetic field from a magnetic vector potential
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -578,7 +578,7 @@ class CircularLoop(BaseSrc):
""" """
The magnetic source term The magnetic source term
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
@@ -591,7 +591,7 @@ class CircularLoop(BaseSrc):
""" """
The electric source term The electric source term
:param BaseFDEMProblem prob: FDEM problem :param Problem prob: FDEM problem
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: primary magnetic field :return: primary magnetic field
""" """
+3 -3
View File
@@ -9,7 +9,7 @@ class Fields(SimPEG.Problem.Fields):
def _phiDeriv(self, src, du_dm_v, v, adjoint=False): def _phiDeriv(self, src, du_dm_v, v, adjoint=False):
if getattr(self, '_phiDeriv_u', None) is None or getattr(self, '_phiDeriv_m', None) is None: if getattr(self, '_phiDeriv_u', None) is None or getattr(self, '_phiDeriv_m', None) is None:
raise NotImplementedError ('Getting phiDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting phiDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._phiDeriv_u(src, v, adjoint=adjoint), self._phiDeriv_m(src, v, adjoint=adjoint) return self._phiDeriv_u(src, v, adjoint=adjoint), self._phiDeriv_m(src, v, adjoint=adjoint)
@@ -18,7 +18,7 @@ class Fields(SimPEG.Problem.Fields):
def _eDeriv(self, src, du_dm_v, v, adjoint=False): def _eDeriv(self, src, du_dm_v, v, adjoint=False):
if getattr(self, '_eDeriv_u', None) is None or getattr(self, '_eDeriv_m', None) is None: if getattr(self, '_eDeriv_u', None) is None or getattr(self, '_eDeriv_m', None) is None:
raise NotImplementedError ('Getting eDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting eDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._eDeriv_u(src, v, adjoint), self._eDeriv_m(src, v, adjoint) return self._eDeriv_u(src, v, adjoint), self._eDeriv_m(src, v, adjoint)
@@ -26,7 +26,7 @@ class Fields(SimPEG.Problem.Fields):
def _jDeriv(self, src, du_dm_v, v, adjoint=False): def _jDeriv(self, src, du_dm_v, v, adjoint=False):
if getattr(self, '_jDeriv_u', None) is None or getattr(self, '_jDeriv_m', None) is None: if getattr(self, '_jDeriv_u', None) is None or getattr(self, '_jDeriv_m', None) is None:
raise NotImplementedError ('Getting jDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting jDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._jDeriv_u(src, v, adjoint), self._jDeriv_m(src, v, adjoint) return self._jDeriv_u(src, v, adjoint), self._jDeriv_m(src, v, adjoint)
+3 -3
View File
@@ -32,7 +32,7 @@ class Fields_ky(SimPEG.Problem.TimeFields):
def _phiDeriv(self,kyInd, src, du_dm_v, v, adjoint=False): def _phiDeriv(self,kyInd, src, du_dm_v, v, adjoint=False):
if getattr(self, '_phiDeriv_u', None) is None or getattr(self, '_phiDeriv_m', None) is None: if getattr(self, '_phiDeriv_u', None) is None or getattr(self, '_phiDeriv_m', None) is None:
raise NotImplementedError ('Getting phiDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting phiDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._phiDeriv_u(kyInd, src, v, adjoint=adjoint), self._phiDeriv_m(kyInd, src, v, adjoint=adjoint) return self._phiDeriv_u(kyInd, src, v, adjoint=adjoint), self._phiDeriv_m(kyInd, src, v, adjoint=adjoint)
@@ -41,7 +41,7 @@ class Fields_ky(SimPEG.Problem.TimeFields):
def _eDeriv(self,kyInd, src, du_dm_v, v, adjoint=False): def _eDeriv(self,kyInd, src, du_dm_v, v, adjoint=False):
if getattr(self, '_eDeriv_u', None) is None or getattr(self, '_eDeriv_m', None) is None: if getattr(self, '_eDeriv_u', None) is None or getattr(self, '_eDeriv_m', None) is None:
raise NotImplementedError ('Getting eDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting eDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._eDeriv_u(kyInd, src, v, adjoint), self._eDeriv_m(kyInd, src, v, adjoint) return self._eDeriv_u(kyInd, src, v, adjoint), self._eDeriv_m(kyInd, src, v, adjoint)
@@ -49,7 +49,7 @@ class Fields_ky(SimPEG.Problem.TimeFields):
def _jDeriv(self,kyInd, src, du_dm_v, v, adjoint=False): def _jDeriv(self,kyInd, src, du_dm_v, v, adjoint=False):
if getattr(self, '_jDeriv_u', None) is None or getattr(self, '_jDeriv_m', None) is None: if getattr(self, '_jDeriv_u', None) is None or getattr(self, '_jDeriv_m', None) is None:
raise NotImplementedError ('Getting jDerivs from {0!s} is not implemented'.format(self.knownFields.keys()[0])) raise NotImplementedError ('Getting jDerivs from %s is not implemented' %self.knownFields.keys()[0])
if adjoint: if adjoint:
return self._jDeriv_u(kyInd, src, v, adjoint), self._jDeriv_m(kyInd, src, v, adjoint) return self._jDeriv_u(kyInd, src, v, adjoint), self._jDeriv_m(kyInd, src, v, adjoint)
+2 -2
View File
@@ -46,7 +46,7 @@ class BaseDCProblem(BaseEMProblem):
du_dm_v = self.Ainv * ( - dA_dm_v + dRHS_dm_v ) du_dm_v = self.Ainv * ( - dA_dm_v + dRHS_dm_v )
for rx in src.rxList: for rx in src.rxList:
df_dmFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_dmFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False) df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False)
Jv[src, rx] = rx.evalDeriv(src, self.mesh, f, df_dm_v) Jv[src, rx] = rx.evalDeriv(src, self.mesh, f, df_dm_v)
return Utils.mkvc(Jv) return Utils.mkvc(Jv)
@@ -69,7 +69,7 @@ class BaseDCProblem(BaseEMProblem):
u_src = f[src, self._solutionType] u_src = f[src, self._solutionType]
for rx in src.rxList: for rx in src.rxList:
PTv = rx.evalDeriv(src, self.mesh, f, v[src, rx], adjoint=True) # wrt f, need possibility wrt m PTv = rx.evalDeriv(src, self.mesh, f, v[src, rx], adjoint=True) # wrt f, need possibility wrt m
df_duTFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_duTFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True) df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True)
ATinvdf_duT = self.Ainv * df_duT ATinvdf_duT = self.Ainv * df_duT
+2 -2
View File
@@ -60,7 +60,7 @@ class BaseDCProblem_2D(BaseEMProblem):
dRHS_dm_v = self.getRHSDeriv(ky, src, v) dRHS_dm_v = self.getRHSDeriv(ky, src, v)
du_dm_v = self.Ainv[iky] * ( - dA_dm_v + dRHS_dm_v ) du_dm_v = self.Ainv[iky] * ( - dA_dm_v + dRHS_dm_v )
for rx in src.rxList: for rx in src.rxList:
df_dmFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_dmFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_dm_v = df_dmFun(iky, src, du_dm_v, v, adjoint=False) df_dm_v = df_dmFun(iky, src, du_dm_v, v, adjoint=False)
# Trapezoidal intergration # Trapezoidal intergration
Jv1_temp = 1./np.pi*rx.evalDeriv(ky, src, self.mesh, f, df_dm_v) Jv1_temp = 1./np.pi*rx.evalDeriv(ky, src, self.mesh, f, df_dm_v)
@@ -101,7 +101,7 @@ class BaseDCProblem_2D(BaseEMProblem):
ky = self.kys[iky] ky = self.kys[iky]
AT = self.getA(ky) AT = self.getA(ky)
PTv = rx.evalDeriv(ky, src, self.mesh, f, v[src, rx], adjoint=True) # wrt f, need possibility wrt m PTv = rx.evalDeriv(ky, src, self.mesh, f, v[src, rx], adjoint=True) # wrt f, need possibility wrt m
df_duTFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_duTFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_duT, df_dmT = df_duTFun(iky, src, None, PTv, adjoint=True) df_duT, df_dmT = df_duTFun(iky, src, None, PTv, adjoint=True)
ATinvdf_duT = self.Ainv[iky] * df_duT ATinvdf_duT = self.Ainv[iky] * df_duT
+2 -2
View File
@@ -56,7 +56,7 @@ class BaseIPProblem(BaseEMProblem):
du_dm_v = self.Ainv * ( - dA_dm_v + dRHS_dm_v ) du_dm_v = self.Ainv * ( - dA_dm_v + dRHS_dm_v )
for rx in src.rxList: for rx in src.rxList:
df_dmFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_dmFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False) df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False)
Jv[src, rx] = rx.evalDeriv(src, self.mesh, f, df_dm_v) Jv[src, rx] = rx.evalDeriv(src, self.mesh, f, df_dm_v)
# Conductivity (d u / d log sigma) # Conductivity (d u / d log sigma)
@@ -83,7 +83,7 @@ class BaseIPProblem(BaseEMProblem):
u_src = f[src, self._solutionType] u_src = f[src, self._solutionType]
for rx in src.rxList: for rx in src.rxList:
PTv = rx.evalDeriv(src, self.mesh, f, v[src, rx], adjoint=True) # wrt f, need possibility wrt m PTv = rx.evalDeriv(src, self.mesh, f, v[src, rx], adjoint=True) # wrt f, need possibility wrt m
df_duTFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_duTFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True) df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True)
ATinvdf_duT = self.Ainv * df_duT ATinvdf_duT = self.Ainv * df_duT
dA_dmT = self.getADeriv(u_src, ATinvdf_duT, adjoint=True) dA_dmT = self.getADeriv(u_src, ATinvdf_duT, adjoint=True)
+3 -3
View File
@@ -83,7 +83,7 @@ class BaseSIPProblem(BaseEMProblem):
for rx in src.rxList: for rx in src.rxList:
timeindex = rx.getTimeP(self.survey.times) timeindex = rx.getTimeP(self.survey.times)
if timeindex[tind]: if timeindex[tind]:
df_dmFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_dmFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False) df_dm_v = df_dmFun(src, du_dm_v, v, adjoint=False)
Jv[src, rx, t] = rx.evalDeriv(src, self.mesh, f, df_dm_v) Jv[src, rx, t] = rx.evalDeriv(src, self.mesh, f, df_dm_v)
@@ -122,7 +122,7 @@ class BaseSIPProblem(BaseEMProblem):
for rx in src.rxList: for rx in src.rxList:
timeindex = rx.getTimeP(self.survey.times) timeindex = rx.getTimeP(self.survey.times)
if timeindex[tind]: if timeindex[tind]:
df_dmFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_dmFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_dm_v0 = df_dmFun(src, du_dm_v0, v0, adjoint=False) df_dm_v0 = df_dmFun(src, du_dm_v0, v0, adjoint=False)
df_dm_v1 = df_dmFun(src, du_dm_v1, v1, adjoint=False) df_dm_v1 = df_dmFun(src, du_dm_v1, v1, adjoint=False)
Jv[src, rx, t] = rx.evalDeriv(src, self.mesh, f, df_dm_v0) Jv[src, rx, t] = rx.evalDeriv(src, self.mesh, f, df_dm_v0)
@@ -153,7 +153,7 @@ class BaseSIPProblem(BaseEMProblem):
timeindex = rx.getTimeP(self.survey.times) timeindex = rx.getTimeP(self.survey.times)
if timeindex[tind]: if timeindex[tind]:
PTv = rx.evalDeriv(src, self.mesh, f, v[src, rx, t], adjoint=True) # wrt f, need possibility wrt m PTv = rx.evalDeriv(src, self.mesh, f, v[src, rx, t], adjoint=True) # wrt f, need possibility wrt m
df_duTFun = getattr(f, '_{0!s}Deriv'.format(rx.projField), None) df_duTFun = getattr(f, '_%sDeriv'%rx.projField, None)
df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True) df_duT, df_dmT = df_duTFun(src, None, PTv, adjoint=True)
ATinvdf_duT = self.Ainv * df_duT ATinvdf_duT = self.Ainv * df_duT
dA_dmT = self.getADeriv(u_src, ATinvdf_duT, adjoint=True) dA_dmT = self.getADeriv(u_src, ATinvdf_duT, adjoint=True)
+13 -13
View File
@@ -47,7 +47,7 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
self.waveformType = "GENERAL" self.waveformType = "GENERAL"
def fields(self, m): def fields(self, m):
if self.verbose: print '{0!s}\nCalculating fields(m)\n{1!s}'.format('*'*50, '*'*50) if self.verbose: print '%s\nCalculating fields(m)\n%s'%('*'*50,'*'*50)
self.curModel = m self.curModel = m
# Create a fields storage object # Create a fields storage object
F = self._FieldsForward_pair(self.mesh, self.survey) F = self._FieldsForward_pair(self.mesh, self.survey)
@@ -55,7 +55,7 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
# Set the initial conditions # Set the initial conditions
F[src,:,0] = src.getInitialFields(self.mesh) F[src,:,0] = src.getInitialFields(self.mesh)
F = self.forward(m, self.getRHS, F=F) F = self.forward(m, self.getRHS, F=F)
if self.verbose: print '{0!s}\nDone calculating fields(m)\n{1!s}'.format('*'*50, '*'*50) if self.verbose: print '%s\nDone calculating fields(m)\n%s'%('*'*50,'*'*50)
return F return F
def forward(self, m, RHS, F=None): def forward(self, m, RHS, F=None):
@@ -70,11 +70,11 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
if Ainv is not None: if Ainv is not None:
Ainv.clean() Ainv.clean()
A = self.getA(tInd) A = self.getA(tInd)
if self.verbose: print 'Factoring... (dt = {0:e})'.format(dt) if self.verbose: print 'Factoring... (dt = %e)'%dt
Ainv = self.Solver(A, **self.solverOpts) Ainv = self.Solver(A, **self.solverOpts)
if self.verbose: print 'Done' if self.verbose: print 'Done'
rhs = RHS(tInd, F) rhs = RHS(tInd, F)
if self.verbose: print ' Solving... (tInd = {0:d})'.format(tInd) if self.verbose: print ' Solving... (tInd = %d)'%tInd
sol = Ainv * rhs sol = Ainv * rhs
if self.verbose: print ' Done...' if self.verbose: print ' Done...'
if sol.ndim == 1: if sol.ndim == 1:
@@ -95,11 +95,11 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
if Ainv is not None: if Ainv is not None:
Ainv.clean() Ainv.clean()
A = self.getA(tInd) A = self.getA(tInd)
if self.verbose: print 'Factoring (Adjoint)... (dt = {0:e})'.format(dt) if self.verbose: print 'Factoring (Adjoint)... (dt = %e)'%dt
Ainv = self.Solver(A, **self.solverOpts) Ainv = self.Solver(A, **self.solverOpts)
if self.verbose: print 'Done' if self.verbose: print 'Done'
rhs = RHS(tInd, F) rhs = RHS(tInd, F)
if self.verbose: print ' Solving (Adjoint)... (tInd = {0:d})'.format(tInd) if self.verbose: print ' Solving (Adjoint)... (tInd = %d)'%tInd
sol = Ainv * rhs sol = Ainv * rhs
if self.verbose: print ' Done...' if self.verbose: print ' Done...'
if sol.ndim == 1: if sol.ndim == 1:
@@ -112,7 +112,7 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
""" """
:param numpy.array m: Conductivity model :param numpy.array m: Conductivity model
:param numpy.ndarray v: vector (model object) :param numpy.ndarray v: vector (model object)
:param FieldsTDEM f: Fields resulting from m :param simpegEM.TDEM.FieldsTDEM f: Fields resulting from m
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: w (data object) :return: w (data object)
@@ -123,21 +123,21 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
* Compute \\\(\\\\vec{w} = -\\\mathbf{Q} \\\\vec{y}\\\) * Compute \\\(\\\\vec{w} = -\\\mathbf{Q} \\\\vec{y}\\\)
""" """
if self.verbose: print '{0!s}\nCalculating J(v)\n{1!s}'.format('*'*50, '*'*50) if self.verbose: print '%s\nCalculating J(v)\n%s'%('*'*50,'*'*50)
self.curModel = m self.curModel = m
if f is None: if f is None:
f = self.fields(m) f = self.fields(m)
p = self.Gvec(m, v, f) p = self.Gvec(m, v, f)
y = self.solveAh(m, p) y = self.solveAh(m, p)
Jv = self.survey.evalDeriv(f, v=y) Jv = self.survey.evalDeriv(f, v=y)
if self.verbose: print '{0!s}\nDone calculating J(v)\n{1!s}'.format('*'*50, '*'*50) if self.verbose: print '%s\nDone calculating J(v)\n%s'%('*'*50,'*'*50)
return - mkvc(Jv) return - mkvc(Jv)
def Jtvec(self, m, v, f=None): def Jtvec(self, m, v, f=None):
""" """
:param numpy.array m: Conductivity model :param numpy.array m: Conductivity model
:param numpy.ndarray v: vector (or a :class:`SimPEG.Survey.Data` object) :param numpy.ndarray,SimPEG.Survey.Data v: vector (data object)
:param FieldsTDEM u: Fields resulting from m :param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: w (model object) :return: w (model object)
@@ -148,7 +148,7 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
* Compute \\\(\\\\vec{w} = -\\\mathbf{G}^\\\\top y\\\) * Compute \\\(\\\\vec{w} = -\\\mathbf{G}^\\\\top y\\\)
""" """
if self.verbose: print '{0!s}\nCalculating J^T(v)\n{1!s}'.format('*'*50, '*'*50) if self.verbose: print '%s\nCalculating J^T(v)\n%s'%('*'*50,'*'*50)
self.curModel = m self.curModel = m
if f is None: if f is None:
f = self.fields(m) f = self.fields(m)
@@ -159,6 +159,6 @@ class BaseTDEMProblem(BaseTimeProblem, BaseEMProblem):
p = self.survey.evalDeriv(f, v=v, adjoint=True) p = self.survey.evalDeriv(f, v=v, adjoint=True)
y = self.solveAht(m, p) y = self.solveAht(m, p)
w = self.Gtvec(m, y, f) w = self.Gtvec(m, y, f)
if self.verbose: print '{0!s}\nDone calculating J^T(v)\n{1!s}'.format('*'*50, '*'*50) if self.verbose: print '%s\nDone calculating J^T(v)\n%s'%('*'*50,'*'*50)
return - mkvc(w) return - mkvc(w)
+13 -13
View File
@@ -87,8 +87,8 @@ class ProblemTDEM_b(BaseTDEMProblem):
""" """
:param numpy.array m: Conductivity model :param numpy.array m: Conductivity model
:param numpy.array vec: vector (like a model) :param numpy.array vec: vector (like a model)
:param FieldsTDEM u: Fields resulting from m :param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
:rtype: FieldsTDEM :rtype: simpegEM.TDEM.FieldsTDEM
:return: f :return: f
Multiply G by a vector Multiply G by a vector
@@ -125,9 +125,9 @@ class ProblemTDEM_b(BaseTDEMProblem):
""" """
:param numpy.array m: Conductivity model :param numpy.array m: Conductivity model
:param numpy.array vec: vector (like a fields) :param numpy.array vec: vector (like a fields)
:param FieldsTDEM u: Fields resulting from m :param simpegEM.TDEM.FieldsTDEM u: Fields resulting from m
:rtype: numpy.ndarray :rtype: np.ndarray (like a model)
:return: p (like a model) :return: p
Multiply G.T by a vector Multiply G.T by a vector
""" """
@@ -153,8 +153,8 @@ class ProblemTDEM_b(BaseTDEMProblem):
def solveAh(self, m, p): def solveAh(self, m, p):
""" """
:param numpy.array m: Conductivity model :param numpy.array m: Conductivity model
:param FieldsTDEM p: Fields object :param simpegEM.TDEM.FieldsTDEM p: Fields object
:rtype: FieldsTDEM :rtype: simpegEM.TDEM.FieldsTDEM
:return: y :return: y
Solve the block-matrix system \\\(\\\hat{A} \\\hat{y} = \\\hat{p}\\\): Solve the block-matrix system \\\(\\\hat{A} \\\hat{y} = \\\hat{p}\\\):
@@ -200,8 +200,8 @@ class ProblemTDEM_b(BaseTDEMProblem):
def solveAht(self, m, p): def solveAht(self, m, p):
""" """
:param numpy.array m: Conductivity model :param numpy.array m: Conductivity model
:param FieldsTDEM p: Fields object :param simpegEM.TDEM.FieldsTDEM p: Fields object
:rtype: FieldsTDEM :rtype: simpegEM.TDEM.FieldsTDEM
:return: y :return: y
Solve the block-matrix system \\\(\\\hat{A}^\\\\top \\\hat{y} = \\\hat{p}\\\): Solve the block-matrix system \\\(\\\hat{A}^\\\\top \\\hat{y} = \\\hat{p}\\\):
@@ -270,8 +270,8 @@ class ProblemTDEM_b(BaseTDEMProblem):
def _AhVec(self, m, vec): def _AhVec(self, m, vec):
""" """
:param numpy.array m: Conductivity model :param numpy.array m: Conductivity model
:param FieldsTDEM vec: Fields object :param simpegEM.TDEM.FieldsTDEM vec: Fields object
:rtype: FieldsTDEM :rtype: simpegEM.TDEM.FieldsTDEM
:return: f :return: f
Multiply the matrix \\\(\\\hat{A}\\\) by a fields vector where Multiply the matrix \\\(\\\hat{A}\\\) by a fields vector where
@@ -315,8 +315,8 @@ class ProblemTDEM_b(BaseTDEMProblem):
def _AhtVec(self, m, vec): def _AhtVec(self, m, vec):
""" """
:param numpy.array m: Conductivity model :param numpy.array m: Conductivity model
:param FieldsTDEM vec: Fields object :param simpegEM.TDEM.FieldsTDEM vec: Fields object
:rtype: FieldsTDEM :rtype: simpegEM.TDEM.FieldsTDEM
:return: f :return: f
Multiply the matrix \\\(\\\hat{A}\\\) by a fields vector where Multiply the matrix \\\(\\\hat{A}\\\) by a fields vector where
+2 -2
View File
@@ -58,7 +58,7 @@ def getFDEMProblem(fdemType, comp, SrcList, freq, useMu=False, verbose=False):
Src.append(EM.FDEM.Src.RawVec([rx0], freq, mesh.getEdgeInnerProduct()*S_m, S_e)) Src.append(EM.FDEM.Src.RawVec([rx0], freq, mesh.getEdgeInnerProduct()*S_m, S_e))
if verbose: if verbose:
print ' Fetching {0!s} problem'.format((fdemType)) print ' Fetching %s problem' % (fdemType)
if fdemType == 'e': if fdemType == 'e':
survey = EM.FDEM.Survey(Src) survey = EM.FDEM.Survey(Src)
@@ -94,7 +94,7 @@ def crossCheckTest(SrcList, fdemType1, fdemType2, comp, addrandoms = False, useM
prb1 = getFDEMProblem(fdemType1, comp, SrcList, freq, useMu, verbose) prb1 = getFDEMProblem(fdemType1, comp, SrcList, freq, useMu, verbose)
mesh = prb1.mesh mesh = prb1.mesh
print 'Cross Checking Forward: {0!s}, {1!s} formulations - {2!s}'.format(fdemType1, fdemType2, comp) print 'Cross Checking Forward: %s, %s formulations - %s' % (fdemType1, fdemType2, comp)
logsig = np.log(np.ones(mesh.nC)*CONDUCTIVITY) logsig = np.log(np.ones(mesh.nC)*CONDUCTIVITY)
mu = np.ones(mesh.nC)*MU mu = np.ones(mesh.nC)*MU
+7 -7
View File
@@ -1,7 +1,7 @@
from SimPEG import * from SimPEG import *
import SimPEG.EM.Static.DC as DC import SimPEG.DCIP as DC
def run(plotIt=True): def run(plotIt=False):
cs = 25. cs = 25.
hx = [(cs,7, -1.3),(cs,21),(cs,7, 1.3)] hx = [(cs,7, -1.3),(cs,21),(cs,7, 1.3)]
hy = [(cs,7, -1.3),(cs,21),(cs,7, 1.3)] hy = [(cs,7, -1.3),(cs,21),(cs,7, 1.3)]
@@ -21,10 +21,10 @@ def run(plotIt=True):
# ax.plot(xyz_rxP[:,0],xyz_rxP[:,1], 'w.') # ax.plot(xyz_rxP[:,0],xyz_rxP[:,1], 'w.')
# ax.plot(xyz_rxN[:,0],xyz_rxN[:,1], 'r.', ms = 3) # ax.plot(xyz_rxN[:,0],xyz_rxN[:,1], 'r.', ms = 3)
rx = DC.Rx.Dipole(xyz_rxP, xyz_rxN) rx = DC.RxDipole(xyz_rxP, xyz_rxN)
src = DC.Src.Dipole([rx], np.r_[-200, 0, -12.5], np.r_[+200, 0, -12.5]) src = DC.SrcDipole([rx], [-200, 0, -12.5], [+200, 0, -12.5])
survey = DC.Survey([src]) survey = DC.SurveyDC([src])
problem = DC.Problem3D_CC(mesh) problem = DC.ProblemDC_CC(mesh)
problem.pair(survey) problem.pair(survey)
try: try:
from pymatsolver import MumpsSolver from pymatsolver import MumpsSolver
@@ -65,4 +65,4 @@ def run(plotIt=True):
if __name__ == '__main__': if __name__ == '__main__':
print run() print run(plotIt=True)
@@ -19,13 +19,10 @@ def run(plotIt=True):
Morrison Casing Model, and the results are used in a 2016 SEG abstract by Morrison Casing Model, and the results are used in a 2016 SEG abstract by
Yang et al. Yang et al.
.. code-block:: text - Schenkel, C.J., and H.F. Morrison, 1990, Effects of well casing on potential field measurements using downhole current sources: Geophysical prospecting, 38, 663-686.
Schenkel, C.J., and H.F. Morrison, 1990, Effects of well casing on potential field measurements using downhole current sources: Geophysical prospecting, 38, 663-686.
The model consists of: The model consists of:
- Air: Conductivity 1e-8 S/m, above z = 0 - Air: Conductivity 1e-8 S/m, above z = 0
- Background: conductivity 1e-2 S/m, below z = 0 - Background: conductivity 1e-2 S/m, below z = 0
- Casing: conductivity 1e6 S/m - Casing: conductivity 1e6 S/m
@@ -110,7 +107,7 @@ def run(plotIt=True):
# Mesh # Mesh
mesh = Mesh.CylMesh([hx,1.,hz], [0.,0.,-np.sum(hz[:npadzu+ncz-nza])]) mesh = Mesh.CylMesh([hx,1.,hz], [0.,0.,-np.sum(hz[:npadzu+ncz-nza])])
print 'Mesh Extent xmax: {0:f},: zmin: {1:f}, zmax: {2:f}'.format(mesh.vectorCCx.max(), mesh.vectorCCz.min(), mesh.vectorCCz.max()) print 'Mesh Extent xmax: %f,: zmin: %f, zmax: %f'%(mesh.vectorCCx.max(), mesh.vectorCCz.min(), mesh.vectorCCz.max())
print 'Number of cells', mesh.nC print 'Number of cells', mesh.nC
if plotIt is True: if plotIt is True:
@@ -218,7 +215,7 @@ def run(plotIt=True):
# ------------ Problem and Survey --------------- # ------------ Problem and Survey ---------------
survey = FDEM.Survey(sg_p + dg_p) survey = FDEM.Survey(sg_p + dg_p)
mapping = [('sigma', Maps.IdentityMap(mesh))] mapping = [('sigma', Maps.IdentityMap(mesh))]
problem = FDEM.Problem3D_h(mesh, mapping=mapping, Solver=solver) problem = FDEM.Problem3D_h(mesh, mapping=mapping)
problem.pair(survey) problem.pair(survey)
# ------------- Solve --------------------------- # ------------- Solve ---------------------------
+29 -7
View File
@@ -42,33 +42,55 @@ def run(N=100, plotIt=True):
survey = Survey.LinearSurvey() survey = Survey.LinearSurvey()
survey.pair(prob) survey.pair(prob)
survey.dobs = prob.fields(mtrue) + std_noise * np.random.randn(nk) survey.dobs = prob.fields(mtrue) + std_noise * np.random.randn(nk)
#survey.makeSyntheticData(mtrue, std=std_noise)
wd = np.ones(nk) * std_noise wd = np.ones(nk) * std_noise
#print survey.std[0]
#M = prob.mesh
# Distance weighting # Distance weighting
wr = np.sum(prob.G**2.,axis=0)**0.5 wr = np.sum(prob.G**2.,axis=0)**0.5
wr = ( wr/np.max(wr) ) wr = ( wr/np.max(wr) )
# reg = Regularization.Simple(mesh)
# reg.mref = mref
# reg.cell_weights = wr
#
dmis = DataMisfit.l2_DataMisfit(survey) dmis = DataMisfit.l2_DataMisfit(survey)
dmis.Wd = 1./wd dmis.Wd = 1./wd
#
# opt = Optimization.ProjectedGNCG(maxIter=20,lower=-2.,upper=2., maxIterCG= 10, tolCG = 1e-4)
# invProb = InvProblem.BaseInvProblem(dmis, reg, opt)
# invProb.curModel = m0
#
# beta = Directives.BetaSchedule(coolingFactor=2, coolingRate=1)
# target = Directives.TargetMisfit()
#
betaest = Directives.BetaEstimate_ByEig() betaest = Directives.BetaEstimate_ByEig()
# inv = Inversion.BaseInversion(invProb, directiveList=[beta, betaest, target])
#
#
# mrec = inv.run(m0)
# ml2 = mrec
# print "Final misfit:" + str(invProb.dmisfit.eval(mrec))
#
# # Switch regularization to sparse
# phim = invProb.phi_m_last
# phid = invProb.phi_d
reg = Regularization.Sparse(mesh) reg = Regularization.Sparse(mesh)
reg.mref = mref reg.mref = mref
reg.cell_weights = wr reg.cell_weights = wr
reg.mref = np.zeros(mesh.nC) reg.mref = np.zeros(mesh.nC)
eps_p = 5e-2
eps_q = 5e-2
norms = [0., 0., 2., 2.]
opt = Optimization.ProjectedGNCG(maxIter=100 ,lower=-2.,upper=2., maxIterLS = 20, maxIterCG= 10, tolCG = 1e-3) opt = Optimization.ProjectedGNCG(maxIter=100 ,lower=-2.,upper=2., maxIterLS = 20, maxIterCG= 10, tolCG = 1e-3)
invProb = InvProblem.BaseInvProblem(dmis, reg, opt) invProb = InvProblem.BaseInvProblem(dmis, reg, opt)
update_Jacobi = Directives.Update_lin_PreCond() update_Jacobi = Directives.Update_lin_PreCond()
IRLS = Directives.Update_IRLS( norms=norms, eps_p=eps_p, eps_q=eps_q)
# Set the IRLS directive, penalize the lowest 25 percentile of model values
# Start with an l2-l2, then switch to lp-norms
norms = [0., 0., 2., 2.]
IRLS = Directives.Update_IRLS( norms=norms, prctile = 25, maxIRLSiter = 15, minGNiter=3)
inv = Inversion.BaseInversion(invProb, directiveList=[IRLS,betaest,update_Jacobi]) inv = Inversion.BaseInversion(invProb, directiveList=[IRLS,betaest,update_Jacobi])
+30 -24
View File
@@ -1,13 +1,15 @@
import SimPEG as simpeg import SimPEG as simpeg
import numpy as np import numpy as np
import SimPEG.MT as MT from SimPEG import NSEM
from scipy.constants import mu_0 from scipy.constants import mu_0
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
np.random.seed(1983)
def run(plotIt=True): def run(plotIt=True):
""" """
MT: 1D: Inversion MT: 1D: Inversion
================= =======================
Forward model 1D MT data. Forward model 1D MT data.
Setup and run a MT 1D inversion. Setup and run a MT 1D inversion.
@@ -17,13 +19,13 @@ def run(plotIt=True):
## Setup the forward modeling ## Setup the forward modeling
# Setting up 1D mesh and conductivity models to forward model data. # Setting up 1D mesh and conductivity models to forward model data.
# Frequency # Frequency
nFreq = 31 nFreq = 26
freqs = np.logspace(3,-3,nFreq) freqs = np.logspace(2,-3,nFreq)
# Set mesh parameters # Set mesh parameters
ct = 20 ct = 10
air = simpeg.Utils.meshTensor([(ct,16,1.4)]) air = simpeg.Utils.meshTensor([(ct,25,1.4)])
core = np.concatenate( ( np.kron(simpeg.Utils.meshTensor([(ct,10,-1.3)]),np.ones((5,))) , simpeg.Utils.meshTensor([(ct,5)]) ) ) core = np.concatenate( ( np.kron(simpeg.Utils.meshTensor([(ct,10,-1.3)]),np.ones((5,))) , simpeg.Utils.meshTensor([(ct,5)]) ) )
bot = simpeg.Utils.meshTensor([(core[0],10,-1.4)]) bot = simpeg.Utils.meshTensor([(core[0],25,-1.4)])
x0 = -np.array([np.sum(np.concatenate((core,bot)))]) x0 = -np.array([np.sum(np.concatenate((core,bot)))])
# Make the model # Make the model
m1d = simpeg.Mesh.TensorMesh([np.concatenate((bot,core,air))], x0=x0) m1d = simpeg.Mesh.TensorMesh([np.concatenate((bot,core,air))], x0=x0)
@@ -33,7 +35,7 @@ def run(plotIt=True):
layer1 = (m1d.vectorCCx<-500.) & (m1d.vectorCCx>=-800.) layer1 = (m1d.vectorCCx<-500.) & (m1d.vectorCCx>=-800.)
layer2 = (m1d.vectorCCx<-3500.) & (m1d.vectorCCx>=-5000.) layer2 = (m1d.vectorCCx<-3500.) & (m1d.vectorCCx>=-5000.)
# Set the conductivity values # Set the conductivity values
sig_half = 2e-3 sig_half = 1e-2
sig_air = 1e-8 sig_air = 1e-8
sig_layer1 = .2 sig_layer1 = .2
sig_layer2 = .2 sig_layer2 = .2
@@ -57,31 +59,31 @@ def run(plotIt=True):
# Receivers # Receivers
rxList = [] rxList = []
for rxType in ['z1dr','z1di']: for rxType in ['z1dr','z1di']:
rxList.append(MT.Rx(simpeg.mkvc(np.array([0.0]),2).T,rxType)) rxList.append(NSEM.Rx(simpeg.mkvc(np.array([-0.5]),2).T,rxType))
# Source list # Source list
srcList =[] srcList =[]
for freq in freqs: for freq in freqs:
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq)) srcList.append(NSEM.SrcNSEM.polxy_1Dprimary(rxList,freq))
# Make the survey # Make the survey
survey = MT.Survey(srcList) survey = NSEM.Survey(srcList)
survey.mtrue = m_true survey.mtrue = m_true
## Set the problem ## Set the problem
problem = MT.Problem1D.eForm_psField(m1d,sigmaPrimary=sigma_0,mapping=mappingExpAct) problem = NSEM.Problem1D_ePrimSec(m1d,sigmaPrimary=sigma_0,mapping=mappingExpAct)
problem.pair(survey) problem.pair(survey)
## Forward model data ## Forward model data
# Project the data # Project the data
survey.dtrue = survey.dpred(m_true) survey.dtrue = survey.dpred(m_true)
survey.dobs = survey.dtrue + 0.025*abs(survey.dtrue)*np.random.randn(*survey.dtrue.shape) survey.dobs = survey.dtrue + 0.01*abs(survey.dtrue)*np.random.randn(*survey.dtrue.shape)
if plotIt: if plotIt:
fig = MT.Utils.dataUtils.plotMT1DModelData(problem, [m_0]) fig = NSEM.Utils.dataUtils.plotMT1DModelData(problem,[])
fig.suptitle('Target - smooth true') fig.suptitle('Target - smooth true')
# Assign uncertainties # Assign uncertainties
std = 0.05 # 5% std std = 0.025 # 5% std
survey.std = np.abs(survey.dobs*std) survey.std = np.abs(survey.dobs*std)
# Assign the data weight # Assign the data weight
Wd = 1./survey.std Wd = 1./survey.std
@@ -90,30 +92,33 @@ def run(plotIt=True):
# Define a counter # Define a counter
C = simpeg.Utils.Counter() C = simpeg.Utils.Counter()
# Set the optimization # Set the optimization
opt = simpeg.Optimization.InexactGaussNewton(maxIter = 30) opt = simpeg.Optimization.ProjectedGNCG(maxIter = 25)
opt.counter = C opt.counter = C
opt.LSshorten = 0.5 opt.lower = np.log(1e-4)
opt.upper = np.log(5)
opt.LSshorten = 0.1
opt.remember('xc') opt.remember('xc')
# Data misfit # Data misfit
dmis = simpeg.DataMisfit.l2_DataMisfit(survey) dmis = simpeg.DataMisfit.l2_DataMisfit(survey)
dmis.Wd = Wd dmis.Wd = Wd
# Regularization - with a regularization mesh # Regularization - with a regularization mesh
regMesh = simpeg.Mesh.TensorMesh([m1d.hx[problem.mapping.sigmaMap.maps[-1].indActive]],m1d.x0) regMesh = simpeg.Mesh.TensorMesh([m1d.hx[active]],m1d.x0)
reg = simpeg.Regularization.Tikhonov(regMesh) reg = simpeg.Regularization.Tikhonov(regMesh)
reg.mrefInSmooth = True reg.mrefInSmooth = True
reg.alpha_s = 1e-7 reg.alpha_s = 1e-1
reg.alpha_x = 1. reg.alpha_x = 1.
# Inversion problem # Inversion problem
invProb = simpeg.InvProblem.BaseInvProblem(dmis, reg, opt) invProb = simpeg.InvProblem.BaseInvProblem(dmis, reg, opt)
invProb.counter = C invProb.counter = C
# Beta cooling # Beta cooling
beta = simpeg.Directives.BetaSchedule() beta = simpeg.Directives.BetaSchedule()
beta.coolingRate = 4 beta.coolingRate = 4.
betaest = simpeg.Directives.BetaEstimate_ByEig(beta0_ratio=0.75) beta.coolingFactor = 4.
betaest = simpeg.Directives.BetaEstimate_ByEig(beta0_ratio=1.)
betaest.beta0 = 1.
targmis = simpeg.Directives.TargetMisfit() targmis = simpeg.Directives.TargetMisfit()
targmis.target = survey.nD targmis.target = survey.nD
saveModel = simpeg.Directives.SaveModelEveryIteration()
saveModel.fileName = 'Inversion_TargMisEqnD_smoothTrue'
# Create an inversion object # Create an inversion object
inv = simpeg.Inversion.BaseInversion(invProb, directiveList=[beta,betaest,targmis]) inv = simpeg.Inversion.BaseInversion(invProb, directiveList=[beta,betaest,targmis])
@@ -121,8 +126,9 @@ def run(plotIt=True):
mopt = inv.run(m_0) mopt = inv.run(m_0)
if plotIt: if plotIt:
fig = MT.Utils.dataUtils.plotMT1DModelData(problem,[mopt]) fig = NSEM.Utils.dataUtils.plotMT1DModelData(problem,[mopt])
fig.suptitle('Target - smooth true') fig.suptitle('Target - smooth true')
fig.axes[0].set_ylim([-10000,500])
plt.show() plt.show()
if __name__ == '__main__': if __name__ == '__main__':
@@ -0,0 +1,428 @@
from scipy.constants import epsilon_0, mu_0
import matplotlib.pyplot as plt
import numpy as np
from ipywidgets import *
from SimPEG.EM.Utils import k, omega
"""
MT1D: n layered earth problem
*****************************
Author: Thibaut Astic
Contact: thast@eos.ubc.ca
Date: January 2016
This code compute the analytic response of a n-layered Earth to a plane wave (Magneto-Tellurics).
We start by looking at Maxwell's equations in the electric
field \\\(\\\mathbf{E}\\) and the magnetic flux
\\\(\\\mathbf{H}\\) to write the wave equations
\\(\\ \nabla ^2 \mathbf{E_x} + k^2 \mathbf{E_x} = 0 \\) &
\\(\\ \nabla ^2 \mathbf{H_y} + k^2 \mathbf{H_y} = 0 \\)
Then solving the equations in each layer "j" between z_{j-1} and z_j in the form of
\\(\\ E_{x,j} (z) = U_j e^{i k (z-z_{j-1})} + D_j e^{-i k (z-z_{j-1})} \\)
\\(\\ H_{y,j} (z) = \frac{1}{Z_j} (D_j e^{-i k (z-z_{j-1})} - U_j e^{i k (z-z_{j-1})}) \\)
With U and D the Up and Down components of the E-field.
The iteration from one layer to another is ensure by:
\\(\\ \left(\begin{matrix} E_{x,j} \\ H_{y,j} \end{matrix} \right) =
P_j T_j P^{-1}_J \left(\begin{matrix} E_{x,j+1} \\ H_{y,j+1} \end{matrix} \right) \\)
And the Boundary Condition is set for the E-field in the last layer, with no Up component (=0)
and only a down component (=1 then normalized by the highest amplitude to ensure numeric stability)
The layer 0 is assumed to be the air layer.
"""
#Define a frquency range for a survey
frange = lambda minfreq, maxfreq, step: np.logspace(minfreq,maxfreq,num = step, base = 10.)
#Functions to create random physical Perties for a n-layered earth
thick = lambda minthick, maxthick, nlayer: np.append(np.array([1.2*10.**5]),
np.ndarray.round(minthick + (maxthick-minthick)* np.random.rand(nlayer-1,1)
,decimals =1))
sig = lambda minsig, maxsig, nlayer: np.append(np.array([0.]),
np.ndarray.round(10.**minsig + (10.**maxsig-10.**minsig)* np.random.rand(nlayer,1)
,decimals=3))
mu = lambda minmu, maxmu, nlayer: np.append(np.array([1.]),
np.ndarray.round(minmu + (maxmu-minmu)* np.random.rand(nlayer,1)
,decimals=1))
eps = lambda mineps, maxeps, nlayer: np.append(np.array([1.]),
np.ndarray.round(mineps + (maxeps-mineps)* np.random.rand(nlayer,1)
,decimals=1))
#Evaluate Impedance Z of a layer
ImpZ = lambda f, mu, k: omega(f)*mu*mu_0/k
#Complex Cole-Cole Conductivity - EM utils
PCC= lambda siginf,m,t,c,f: siginf*(1.-(m/(1.+(1j*omega(f)*t)**c)))
#Converted thickness array into top of layer array
top = lambda thick: np.cumsum(thick)
#Propagation Matrix and theirs inverses
#matrix T for transition of Up and Down components accross a layer
T = lambda h,k: np.matrix([[np.exp(1j*k*h),0.],[0.,np.exp(-1j*k*h)]],dtype='complex_')
Tinv = lambda h,k: np.matrix([[np.exp(-1j*k*h),0.],[0.,np.exp(1j*k*h)]],dtype='complex_')
#transition of Up and Down components accross a layer
UD_Z = lambda UD,z,zj,k : T((z-zj),k)*UD
#matrix P relating Up and Down components with E and H fields
P = lambda z: np.matrix([[1.,1,],[-1./z,1./z]],dtype='complex_')
Pinv = lambda z: np.matrix([[1.,-z],[1.,z]],dtype='complex_')/2.
#Time Variation of E and H
E_ZT = lambda U,D,f,t : np.exp(1j*omega(f)*t)*(U+D)
H_ZT = lambda U,D,Z,f,t : (1./Z)*np.exp(1j*omega(f)*t)*(D-U)
#Plot the configuration of the problem
def PlotConfiguration(thick,sig,eps,mu,ax,widthg,z):
topn = top(thick)
widthn = np.arange(-widthg,widthg+widthg/10.,widthg/10.)
ax.set_ylim([z.min(),z.max()])
ax.set_xlim([-widthg,widthg])
ax.set_ylabel("Depth (m)", fontsize=16.)
ax.yaxis.tick_right()
ax.yaxis.set_label_position("right")
#define filling for the different layers
hatches=['/' , '+', 'x', '|' , '\\', '-' , 'o' , 'O' , '.' , '*' ]
#Write the physical properties of air
ax.annotate(("Air, $\sigma$ =%1.0f mS/m")%(sig[0]*10**(3)),
xy=(-widthg/2., -np.abs(z.max())/2.), xycoords='data',
xytext=(-widthg/2., -np.abs(z.max())/2.), textcoords='data',
fontsize=14.)
ax.annotate(("$\epsilon_r$= %1i")%(eps[0]),
xy=(-widthg/2., -np.abs(z.max())/3.), xycoords='data',
xytext=(-widthg/2., -np.abs(z.max())/3.), textcoords='data',
fontsize=14.)
ax.annotate(("$\mu_r$= %1i")%(mu[0]),
xy=(-widthg/2., -np.abs(z.max())/3.), xycoords='data',
xytext=(0, -np.abs(z.max())/3.), textcoords='data',
fontsize=14.)
#Write the physical properties of the differents layers up to the (n-1)-th and fill it with pattern
for i in range(1,len(topn)-1,1):
if topn[i] == topn[i+1]:
pass
else:
ax.annotate(("$\sigma$ =%3.3f mS/m")%(sig[i]*10**(3)),
xy=(0., (2.*topn[i]+topn[i+1])/3), xycoords='data',
xytext=(0., (2.*topn[i]+topn[i+1])/3), textcoords='data',
fontsize=14.)
ax.annotate(("$\epsilon_r$= %1i")%(eps[i]),
xy=(-widthg/1.1, (2.*topn[i]+topn[i+1])/3), xycoords='data',
xytext=(-widthg/1.1, (2.*topn[i]+topn[i+1])/3), textcoords='data',
fontsize=14.)
ax.annotate(("$\mu_r$= %1.2f")%(mu[i]),
xy=(-widthg/2., (2.*topn[i]+topn[i+1])/3), xycoords='data',
xytext=(-widthg/2., (2.*topn[i]+topn[i+1])/3), textcoords='data',
fontsize=14.)
ax.plot(widthn,topn[i]*np.ones_like(widthn),color='black')
ax.fill_between(widthn,topn[i],topn[i+1],alpha=0.3,color="none",edgecolor='black', hatch=hatches[(i-1)%10])
#Write the physical properties of the n-th layer and fill it with pattern
ax.plot(widthn,topn[-1]*np.ones_like(widthn),color='black')
ax.fill_between(widthn,topn[-1],z.max(),alpha=0.3,color="none",edgecolor='black', hatch=hatches[(len(topn)-2)%10])
ax.annotate(("$\sigma$ =%3.3f mS/m")%(sig[-1]*10**(3)),
xy=(0., (2.*topn[-1]+z.max())/3), xycoords='data',
xytext=(0., (2.*topn[-1]+z.max())/3), textcoords='data',
fontsize=14.)
ax.annotate(("$\epsilon_r$= %1i")%(eps[-1]),
xy=(-widthg/1.1, (2.*topn[-1]+z.max())/3), xycoords='data',
xytext=(-widthg/1.1, (2.*topn[-1]+z.max())/3), textcoords='data',
fontsize=14.)
ax.annotate(("$\mu_r$= %1.2f")%(mu[-1]),
xy=(-widthg/2., (2.*topn[-1]+z.max())/3), xycoords='data',
xytext=(-widthg/2., (2.*topn[-1]+z.max())/3), textcoords='data',
fontsize=14.)
#plot Trees!
ax.annotate("",
xy=(widthg/2., -1.*z.max()/5.), xycoords='data',
xytext=(widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.2,head_length=1.2',color='green',linewidth=2.)
)
ax.annotate("",
xy=(widthg/2., -3./4.*z.max()/5.), xycoords='data',
xytext=(widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.4,head_length=1.4',color='green',linewidth=2.)
)
ax.annotate("",
xy=(widthg/2., -1./2.*z.max()/5.), xycoords='data',
xytext=(widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.6,head_length=1.6',color='green',linewidth=2.)
)
ax.annotate("",
xy=(1.2*widthg/2., -1.*z.max()/5.), xycoords='data',
xytext=(1.2*widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.2,head_length=1.2',color='green',linewidth=2.)
)
ax.annotate("",
xy=(1.2*widthg/2., -3./4.*z.max()/5.), xycoords='data',
xytext=(1.2*widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.4,head_length=1.4',color='green',linewidth=2.)
)
ax.annotate("",
xy=(1.2*widthg/2., -1./2.*z.max()/5.), xycoords='data',
xytext=(1.2*widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.6,head_length=1.6',color='green',linewidth=2.)
)
ax.annotate("",
xy=(1.5*widthg/2., -1.*z.max()/5.), xycoords='data',
xytext=(1.5*widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.2,head_length=1.2',color='green',linewidth=2.)
)
ax.annotate("",
xy=(1.5*widthg/2., -3./4.*z.max()/5.), xycoords='data',
xytext=(1.5*widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.4,head_length=1.4',color='green',linewidth=2.)
)
ax.annotate("",
xy=(1.5*widthg/2., -1./2.*z.max()/5.), xycoords='data',
xytext=(1.5*widthg/2., 0.), textcoords='data',
arrowprops=dict(arrowstyle='->, head_width=1.6,head_length=1.6',color='green',linewidth=2.)
)
ax.invert_yaxis()
return ax
#Propagate Up and Down component for a certain frequency & evaluate E and H field
def Propagate(f,H,sig,chg,taux,c,mu,eps,n):
sigcm = np.zeros_like(sig,dtype='complex_')
for j in range(1,len(sig)):
sigcm[j]=PCC(sig[j],chg[j],taux[j],c[j],f)
K = k(f, sigcm, mu, eps)
Z = ImpZ(f,mu,K)
EH = np.matrix(np.zeros((2,n+1),dtype = 'complex_'),dtype = 'complex_')
UD = np.matrix(np.zeros((2,n+1),dtype = 'complex_'),dtype = 'complex_')
UD[1,-1] = 1.
for i in range(-2,-(n+2),-1):
UD[:,i] = Tinv(H[i+1],K[i])*Pinv(Z[i])*P(Z[i+1])*UD[:,i+1]
UD = UD/((np.abs(UD[0,:]+UD[1,:])).max())
for j in range(0,n+1):
EH[:,j] = np.matrix([[1.,1,],[-1./Z[j],1./Z[j]]])*UD[:,j]
return UD, EH, Z ,K
#Evaluate the apparent resistivity and phase for a frequency range
def appres(F,H,sig,chg,taux,c,mu,eps,n):
Res = np.zeros_like(F)
Phase = np.zeros_like(F)
App_ImpZ= np.zeros_like(F,dtype='complex_')
for i in range(0,len(F)):
UD,EH,Z ,K = Propagate(F[i],H,sig,chg,taux,c,mu,eps,n)
App_ImpZ[i] = EH[0,1]/EH[1,1]
Res[i] = np.abs(App_ImpZ[i])**2./(mu_0*omega(F[i]))
Phase[i] = np.angle(App_ImpZ[i], deg = True)
return Res,Phase
#Evaluate Up, Down components, E and H field, for a frequency range,
#a discretized depth range and a time range (use to calculate envelope)
def calculateEHzt(F,H,sig,chg,taux,c,mu,eps,n,zsample,tsample):
topc = top(H)
layer = np.zeros(len(zsample),dtype=np.int)-1
Exzt = np.matrix(np.zeros((len(zsample),len(tsample)),dtype = 'complex_'),dtype = 'complex_')
Hyzt = np.matrix(np.zeros((len(zsample),len(tsample)),dtype = 'complex_'),dtype = 'complex_')
Uz = np.matrix(np.zeros((len(zsample),len(tsample)),dtype = 'complex_'),dtype = 'complex_')
Dz = np.matrix(np.zeros((len(zsample),len(tsample)),dtype = 'complex_'),dtype = 'complex_')
UDaux = np.matrix(np.zeros((2,len(zsample)),dtype = 'complex_'),dtype = 'complex_')
for i in range(0,n+1,1):
layer = layer+(zsample>=topc[i])*1
for j in range(0,len(F)):
UD,EH,Z ,K = Propagate(F[j],H,sig,chg,taux,c,mu,eps,n)
for p in range(0,len(zsample)):
UDaux[:,p] = UD_Z(UD[:,layer[p]],zsample[p],topc[layer[p]],K[layer[p]])
for q in range(0,len(tsample)):
Exzt[p,q] = Exzt[p,q] + E_ZT(UDaux[0,p],UDaux[1,p],F[j],tsample[q])/len(F)
Hyzt[p,q] = Hyzt[p,q] + H_ZT(UDaux[0,p],UDaux[1,p],Z[layer[p]],F[j],tsample[q])/len(F)
Uz[p,q] = Uz[p,q] + UDaux[0,p]*np.exp(1j*omega(F[j])*tsample[q])/len(F)
Dz[p,q] = Dz[p,q] + UDaux[1,p]*np.exp(1j*omega(F[j])*tsample[q])/len(F)
return Exzt,Hyzt,Uz,Dz,UDaux,layer
#Function to Plot Apparent Resistivity and Phase
def PlotAppRes(F,H,sig,chg,taux,c,mu,eps,n,fenvelope,PlotEnvelope):
Res, Phase = appres(F,H,sig,chg,taux,c,mu,eps,n)
fig,ax = plt.subplots(1,2,figsize=(16,10))
ax[0].scatter(Res,F,color='black')
ax[0].set_xscale('Log')
ax[0].set_yscale('Log')
ax[0].set_xlim([10.**(np.log10(Res.min())-1.),10.**(np.log10(Res.max())+1.)])
ax[0].set_ylim([F.min(),F.max()])
ax[0].set_xlabel('Apparent Resistivity (Ohm*m)',fontsize=16.,color="black")
ax[0].set_ylabel('Frequency (Hz)',fontsize=16.)
ax[0].grid(which='major')
ax0 = ax[0].twiny()
ax0.set_xlim([0.,90.])
ax0.set_ylim([F.min(),F.max()])
ax0.scatter(Phase,F,color='purple')
ax0.set_xlabel('Phase (Degrees)',fontsize=16.,color="purple")
zc=np.arange(-(H[1:].max()+10)*n,(H[1:].max()+10)*n,10.)
ax[0].tick_params(labelsize=16)
ax[1].tick_params(labelsize=16)
ax0.tick_params(labelsize=16)
if PlotEnvelope:
widthn=np.logspace(np.log10(Res.min())-1., np.log10(Res.max())+1., num=100, endpoint=True, base=10.0)
fenvelope1n=np.ones(100)*fenvelope
ax[0].plot(widthn,fenvelope1n,linestyle='dashed',color='black')
tc=np.arange(0.,1./fenvelope,0.01/(fenvelope))
Exzt,Hyzt,Uz,Dz,UDaux,layer = calculateEHzt(np.array([fenvelope]),H,sig,chg,taux,c,mu,eps,n,zc,tc)
ax1=ax[1].twiny()
ax[1].tick_params(labelsize=16)
ax1.tick_params(labelsize=16)
ax[1].set_xlabel('Amplitude Electric Field E (V/m)',color='blue',fontsize=16)
ax1.set_xlabel('Amplitude Magnetic Field H (A/m)',color='red',fontsize=16)
ax[1].fill_betweenx(zc,np.squeeze(np.asarray(np.real(Exzt.min(axis=1)))),
np.squeeze(np.asarray(np.real(Exzt.max(axis=1)))),
color='blue', alpha=0.1)
ax1.fill_betweenx(zc,np.squeeze(np.asarray(np.real(Hyzt.min(axis=1)))),
np.squeeze(np.asarray(np.real(Hyzt.max(axis=1)))),
color='red', alpha=0.1)
ax[1] = PlotConfiguration(H,sig,eps,mu,ax[1],(1.5*np.abs(Exzt).max()),zc)
ax1.set_xlim([-1.5*np.abs(Hyzt).max(),1.5*np.abs(Hyzt).max()])
ax1.set_xlim([-1.5*np.abs(Hyzt).max(),1.5*np.abs(Hyzt).max()])
else:
print 'No envelop (if True, might be slow)'
ax[1] = PlotConfiguration(H,sig,eps,mu,ax[1],1.,zc)
ax[1].get_xaxis().set_ticks([])
plt.show()
#Interactive MT for Notebook
def PlotAppRes3LayersInteract(h1,h2,sigl1,sigl2,sigl3,mul1,mul2,mul3,epsl1,epsl2,epsl3,PlotEnvelope,F_Envelope):
frangn=frange(-5,5,100.)
sig3= np.array([0.,0.001,0.1, 0.001])
thick3 = np.array([120000.,50.,50.])
eps3=np.array([1.,1.,1.,1])
mu3=np.array([1.,1.,1.,1])
chg3=np.array([0.,0.1,0.,0.2])
chg3_0=np.array([0.,0.1,0.,0.])
taux3=np.array([0.,0.1,0.,0.1])
c3=np.array([1.,1.,1.,1.])
sig3[1]=sigl1
sig3[1]=10.**sig3[1]
sig3[2]=sigl2
sig3[2]=10.**sig3[2]
sig3[3]=sigl3
sig3[3]=10.**sig3[3]
mu3[1]=mul1
mu3[2]=mul2
mu3[3]=mul3
eps3[1]=epsl1
eps3[2]=epsl2
eps3[3]=epsl3
thick3[1]=h1
thick3[2]=h2
PlotAppRes(frangn,thick3,sig3,chg3_0,taux3,c3,mu3,eps3,3,F_Envelope,PlotEnvelope)
def run(n=3,plotIt=True):
# something to make a plot
F = frange(-5.,5.,20)
H = thick(50.,100.,n)
sign = sig(-5.,0.,n)
mun = mu(1.,2.,n)
epsn = eps(1.,9.,n)
chg = np.zeros_like(sign)
taux = np.zeros_like(sign)
c = np.zeros_like(sign)
Res, Phase = appres(F,H,sign,chg,taux,c,mun,epsn,n)
if plotIt:
PlotAppRes(F, H, sign, chg, taux, c, mun, epsn, n, fenvelope=1000., PlotEnvelope=True)
return Res, Phase
if __name__ == '__main__':
run()
+8 -7
View File
@@ -2,7 +2,7 @@
# Import # Import
import SimPEG as simpeg import SimPEG as simpeg
from SimPEG import MT from SimPEG import NSEM
import numpy as np import numpy as np
try: try:
from pymatsolver import MumpsSolver as Solver from pymatsolver import MumpsSolver as Solver
@@ -12,7 +12,7 @@ except:
def run(plotIt=True, nFreq=1): def run(plotIt=True, nFreq=1):
""" """
MT: 3D: Forward MT: 3D: Forward
=============== =======================
Forward model 3D MT data. Forward model 3D MT data.
@@ -37,24 +37,25 @@ def run(plotIt=True, nFreq=1):
for loc in rx_loc: for loc in rx_loc:
# NOTE: loc has to be a (1,3) np.ndarray otherwise errors accure # NOTE: loc has to be a (1,3) np.ndarray otherwise errors accure
for rxType in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']: for rxType in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']:
rxList.append(MT.Rx(simpeg.mkvc(loc,2).T,rxType)) rxList.append(NSEM.Rx(simpeg.mkvc(loc,2).T,rxType))
# Source list # Source list
srcList =[] srcList =[]
for freq in np.logspace(3,-3,nFreq): for freq in np.logspace(3,-3,nFreq):
srcList.append(MT.SrcMT.polxy_1Dprimary(rxList,freq)) srcList.append(NSEM.SrcNSEM.polxy_1Dprimary(rxList,freq))
# Survey MT # Survey MT
survey = MT.Survey(srcList) survey = NSEM.Survey(srcList)
## Setup the problem object ## Setup the problem object
problem = MT.Problem3D.eForm_ps(M, sigmaPrimary=sigBG, Solver=Solver) problem = NSEM.Problem3D_ePrimSec(M, sigmaPrimary=sigBG)
problem.pair(survey) problem.pair(survey)
problem.Solver = Solver
# Calculate the data # Calculate the data
fields = problem.fields(sig) fields = problem.fields(sig)
dataVec = survey.eval(fields) dataVec = survey.eval(fields)
# Make the data # Make the data
mtData = MT.Data(survey, dataVec) mtData = NSEM.Data(survey,dataVec)
# Add plots # Add plots
if plotIt: if plotIt:
pass pass
-62
View File
@@ -1,62 +0,0 @@
from SimPEG import Mesh, Maps, np
def run(plotIt=True):
"""
Maps: ComboMaps
===============
We will use an example where we want a 1D layered earth as
our model, but we want to map this to a 2D discretization to do our forward
modeling. We will also assume that we are working in log conductivity still,
so after the transformation we want to map to conductivity space.
To do this we will introduce the vertical 1D map (:class:`SimPEG.Maps.SurjectVertical1D`),
which does the first part of what we just described. The second part will be
done by the :class:`SimPEG.Maps.ExpMap` described above.
.. code-block:: python
:linenos:
M = Mesh.TensorMesh([7,5])
v1dMap = Maps.SurjectVertical1D(M)
expMap = Maps.ExpMap(M)
myMap = expMap * v1dMap
m = np.r_[0.2,1,0.1,2,2.9] # only 5 model parameters!
sig = myMap * m
If you noticed, it was pretty easy to combine maps. What is even cooler is
that the derivatives also are made for you (if everything goes right).
Just to be sure that the derivative is correct, you should always run the test
on the mapping that you create.
"""
M = Mesh.TensorMesh([7,5])
v1dMap = Maps.SurjectVertical1D(M)
expMap = Maps.ExpMap(M)
myMap = expMap * v1dMap
m = np.r_[0.2,1,0.1,2,2.9] # only 5 model parameters!
sig = myMap * m
if not plotIt: return
import matplotlib.pyplot as plt
figs, axs = plt.subplots(1,2)
axs[0].plot(m, M.vectorCCy, 'b-o')
axs[0].set_title('Model')
axs[0].set_ylabel('Depth, y')
axs[0].set_xlabel('Value, $m_i$')
axs[0].set_xlim(0,3)
axs[0].set_ylim(0,1)
clbar = plt.colorbar(M.plotImage(sig,ax=axs[1],grid=True,gridOpts=dict(color='grey'))[0])
axs[1].set_title('Physical Property')
axs[1].set_ylabel('Depth, y')
clbar.set_label('$\sigma = \exp(\mathbf{P}m)$')
plt.tight_layout()
plt.show()
if __name__ == '__main__':
run()
-41
View File
@@ -1,41 +0,0 @@
from SimPEG import Mesh, Maps, Utils
def run(plotIt=True):
"""
Maps: Mesh2Mesh
===============
This mapping allows you to go from one mesh to another.
"""
M = Mesh.TensorMesh([100,100])
h1 = Utils.meshTensor([(6,7,-1.5),(6,10),(6,7,1.5)])
h1 = h1/h1.sum()
M2 = Mesh.TensorMesh([h1,h1])
V = Utils.ModelBuilder.randomModel(M.vnC, seed=79, its=50)
v = Utils.mkvc(V)
modh = Maps.Mesh2Mesh([M,M2])
modH = Maps.Mesh2Mesh([M2,M])
H = modH * v
h = modh * H
if not plotIt: return
import matplotlib.pyplot as plt
ax = plt.subplot(131)
M.plotImage(v, ax=ax)
ax.set_title('Fine Mesh (Original)')
ax = plt.subplot(132)
M2.plotImage(H,clim=[0,1],ax=ax)
ax.set_title('Course Mesh')
ax = plt.subplot(133)
M.plotImage(h,clim=[0,1],ax=ax)
ax.set_title('Fine Mesh (Interpolated)')
plt.show()
if __name__ == '__main__':
run()
@@ -98,7 +98,7 @@ def run(plotIt=True, n=60):
ii = int(ii) ii = int(ii)
out = M.plotImage(PHIS[ii][1],ax=ax) out = M.plotImage(PHIS[ii][1],ax=ax)
ax.axis('off') ax.axis('off')
ax.set_title('Elapsed Time: {0:4.1f}'.format(PHIS[ii][0])) ax.set_title('Elapsed Time: %4.1f'%PHIS[ii][0])
plt.show() plt.show()
if __name__ == '__main__': if __name__ == '__main__':
+3 -3
View File
@@ -29,15 +29,15 @@ def run(plotIt=True, n=60):
axes[0].set_ylim([-1,17]) axes[0].set_ylim([-1,17])
for ii, loc in zip(range(M.nC),M.gridCC): for ii, loc in zip(range(M.nC),M.gridCC):
axes[0].text(loc[0]+0.2,loc[1],'{0:d}'.format(ii), color='r') axes[0].text(loc[0]+0.2,loc[1],'%d'%ii, color='r')
axes[0].plot(M.gridFx[:,0],M.gridFx[:,1], 'g>') axes[0].plot(M.gridFx[:,0],M.gridFx[:,1], 'g>')
for ii, loc in zip(range(M.nFx),M.gridFx): for ii, loc in zip(range(M.nFx),M.gridFx):
axes[0].text(loc[0]+0.2,loc[1],'{0:d}'.format(ii), color='g') axes[0].text(loc[0]+0.2,loc[1],'%d'%ii, color='g')
axes[0].plot(M.gridFy[:,0],M.gridFy[:,1], 'm^') axes[0].plot(M.gridFy[:,0],M.gridFy[:,1], 'm^')
for ii, loc in zip(range(M.nFy),M.gridFy): for ii, loc in zip(range(M.nFy),M.gridFy):
axes[0].text(loc[0]+0.2,loc[1]+0.2,'{0:d}'.format((ii+M.nFx)), color='m') axes[0].text(loc[0]+0.2,loc[1]+0.2,'%d'%(ii+M.nFx), color='m')
axes[1].spy(M.faceDiv) axes[1].spy(M.faceDiv)
axes[1].set_title('Face Divergence') axes[1].set_title('Face Divergence')
+7 -9
View File
@@ -2,12 +2,8 @@ from SimPEG import *
from SimPEG.Utils import surface2ind_topo from SimPEG.Utils import surface2ind_topo
def run(plotIt=True, nx=5, ny=5): def run(plotIt=False, nx = 5, ny = 5):
""" """
Utils: surface2ind_topo
=======================
Here we show how to use :code:`Utils.surface2ind_topo` to identify cells below Here we show how to use :code:`Utils.surface2ind_topo` to identify cells below
a topographic surface. a topographic surface.
@@ -17,25 +13,27 @@ def run(plotIt=True, nx=5, ny=5):
xtopo = np.linspace(mesh.gridN[:,0].min(), mesh.gridN[:,0].max()) xtopo = np.linspace(mesh.gridN[:,0].min(), mesh.gridN[:,0].max())
topo = 0.4*np.sin(xtopo*5) # define a topographic surface topo = 0.4*np.sin(xtopo*5) # define a topographic surface
Topo = np.hstack([Utils.mkvc(xtopo,2), Utils.mkvc(topo,2)]) #make it an array Topo = np.hstack([Utils.mkvc(xtopo,2),Utils.mkvc(topo,2)]) #make it an array
indcc = surface2ind_topo(mesh, Topo, 'CC') indcc = surface2ind_topo(mesh, Topo,'CC')
if plotIt: if plotIt:
from matplotlib.pylab import plt from matplotlib.pylab import plt
from scipy.interpolate import interp1d from scipy.interpolate import interp1d
fig, ax = plt.subplots(1,1, figsize=(6,6)) fig, ax = plt.subplots(1,1,figsize=(6,6))
mesh.plotGrid(ax=ax, nodes=True, centers=True) mesh.plotGrid(ax=ax, nodes=True, centers=True)
ax.plot(xtopo,topo,'k',linewidth=1) ax.plot(xtopo,topo,'k',linewidth=1)
# ax.plot(mesh.vectorNx, interp1d(xtopo,topo)(mesh.vectorNx),'--k',linewidth=3)
ax.plot(mesh.vectorCCx, interp1d(xtopo,topo)(mesh.vectorCCx),'--k',linewidth=3) ax.plot(mesh.vectorCCx, interp1d(xtopo,topo)(mesh.vectorCCx),'--k',linewidth=3)
aveN2CC = Utils.sdiag(mesh.aveN2CC.T.sum(1))*mesh.aveN2CC.T aveN2CC = Utils.sdiag(mesh.aveN2CC.T.sum(1))*mesh.aveN2CC.T
a = aveN2CC * indcc a = aveN2CC * indcc
a[a > 0] = 1. a[a > 0] = 1.
a[a < 0.25] = np.nan a[a < 0.25] = np.nan
a = a.reshape(mesh.vnN, order='F') a = a.reshape(mesh.vnN, order='F')
masked_array = np.ma.array(a, mask=np.isnan(a)) masked_array = np.ma.array(a, mask=np.isnan(a))
ax.pcolor(mesh.vectorNx,mesh.vectorNy,masked_array.T, cmap=plt.cm.gray, alpha=0.2) ax.pcolor(mesh.vectorNx,mesh.vectorNy,masked_array.T, cmap = plt.cm.gray,alpha=0.2)
plt.show() plt.show()
+25 -26
View File
@@ -1,30 +1,29 @@
# Run this file to add imports. # Run this file to add imports.
##### AUTOIMPORTS ##### ##### AUTOIMPORTS #####
import DC_Analytic_Dipole
import DC_Forward_PseudoSection
import EM_FDEM_1D_Inversion import EM_FDEM_1D_Inversion
import EM_FDEM_Analytic_MagDipoleWholespace import Mesh_QuadTree_Creation
import EM_Schenkel_Morrison_Casing
import EM_TDEM_1D_Inversion import EM_TDEM_1D_Inversion
import Mesh_QuadTree_FaceDiv
import Mesh_Tensor_Creation
import FLOW_Richards_1D_Celia1990 import FLOW_Richards_1D_Celia1990
import DC_Forward_PseudoSection
import Mesh_Operators_CahnHilliard
import Mesh_Basic_Types
import Inversion_IRLS import Inversion_IRLS
import Inversion_Linear import Inversion_Linear
import Maps_ComboMaps import EM_Schenkel_Morrison_Casing
import Maps_Mesh2Mesh
import Mesh_Basic_ForwardDC
import Mesh_Basic_PlotImage
import Mesh_Basic_Types
import Mesh_Operators_CahnHilliard
import Mesh_QuadTree_Creation
import Mesh_QuadTree_FaceDiv
import Mesh_QuadTree_HangingNodes
import Mesh_Tensor_Creation
import MT_1D_ForwardAndInversion
import MT_3D_Foward import MT_3D_Foward
import Mesh_Basic_ForwardDC
import MT_1D_ForwardAndInversion
import Utils_surface2ind_topo import Utils_surface2ind_topo
import MT_1D_analytic_nlayer_Earth
import EM_FDEM_Analytic_MagDipoleWholespace
import Mesh_Basic_PlotImage
import DC_Analytic_Dipole
import Mesh_QuadTree_HangingNodes
__examples__ = ["DC_Analytic_Dipole", "DC_Forward_PseudoSection", "EM_FDEM_1D_Inversion", "EM_FDEM_Analytic_MagDipoleWholespace", "EM_Schenkel_Morrison_Casing", "EM_TDEM_1D_Inversion", "FLOW_Richards_1D_Celia1990", "Inversion_IRLS", "Inversion_Linear", "Maps_ComboMaps", "Maps_Mesh2Mesh", "Mesh_Basic_ForwardDC", "Mesh_Basic_PlotImage", "Mesh_Basic_Types", "Mesh_Operators_CahnHilliard", "Mesh_QuadTree_Creation", "Mesh_QuadTree_FaceDiv", "Mesh_QuadTree_HangingNodes", "Mesh_Tensor_Creation", "MT_1D_ForwardAndInversion", "MT_3D_Foward", "Utils_surface2ind_topo"] __examples__ = ["EM_FDEM_1D_Inversion", "Mesh_QuadTree_Creation", "EM_TDEM_1D_Inversion", "Mesh_QuadTree_FaceDiv", "Mesh_Tensor_Creation", "FLOW_Richards_1D_Celia1990", "DC_Forward_PseudoSection", "Mesh_Operators_CahnHilliard", "Mesh_Basic_Types", "Inversion_IRLS", "Inversion_Linear", "EM_Schenkel_Morrison_Casing", "MT_3D_Foward", "Mesh_Basic_ForwardDC", "MT_1D_ForwardAndInversion", "Utils_surface2ind_topo", "MT_1D_analytic_nlayer_Earth", "EM_FDEM_Analytic_MagDipoleWholespace", "Mesh_Basic_PlotImage", "DC_Analytic_Dipole", "Mesh_QuadTree_HangingNodes"]
##### AUTOIMPORTS ##### ##### AUTOIMPORTS #####
@@ -40,7 +39,7 @@ if __name__ == '__main__':
# Create the examples dir in the docs folder. # Create the examples dir in the docs folder.
fName = os.path.realpath(__file__) fName = os.path.realpath(__file__)
docExamplesDir = os.path.sep.join(fName.split(os.path.sep)[:-3] + ['docs', 'content', 'examples']) docExamplesDir = os.path.sep.join(fName.split(os.path.sep)[:-3] + ['docs', 'examples'])
shutil.rmtree(docExamplesDir) shutil.rmtree(docExamplesDir)
os.makedirs(docExamplesDir) os.makedirs(docExamplesDir)
@@ -59,7 +58,7 @@ if __name__ == '__main__':
if line == "##### AUTOIMPORTS #####\n": if line == "##### AUTOIMPORTS #####\n":
inimports = not inimports inimports = not inimports
if inimports: if inimports:
out += '\n'.join(["import {0!s}".format(_) for _ in exfiles]) out += '\n'.join(["import %s"%_ for _ in exfiles])
out += '\n\n__examples__ = ["' + '", "'.join(exfiles)+ '"]\n' out += '\n\n__examples__ = ["' + '", "'.join(exfiles)+ '"]\n'
out += '\n##### AUTOIMPORTS #####\n' out += '\n##### AUTOIMPORTS #####\n'
f.close() f.close()
@@ -76,11 +75,11 @@ if __name__ == '__main__':
docstr = runFunction.__doc__ docstr = runFunction.__doc__
if docstr is None: if docstr is None:
doc = '{0!s}\n{1!s}'.format(name.replace('_',' '), '='*len(name)) doc = '%s\n%s'%(name.replace('_',' '),'='*len(name))
else: else:
doc = '\n'.join([_[8:].rstrip() for _ in docstr.split('\n')]) doc = '\n'.join([_[8:].rstrip() for _ in docstr.split('\n')])
out = """.. _examples_{0!s}: out = """.. _examples_%s:
.. --------------------------------- .. .. --------------------------------- ..
.. .. .. ..
@@ -90,21 +89,21 @@ if __name__ == '__main__':
.. .. .. ..
.. --------------------------------- .. .. --------------------------------- ..
{1!s} %s
.. plot:: .. plot::
from SimPEG import Examples from SimPEG import Examples
Examples.{2!s}.run() Examples.%s.run()
.. literalinclude:: ../../../SimPEG/Examples/{3!s}.py .. literalinclude:: ../../SimPEG/Examples/%s.py
:language: python :language: python
:linenos: :linenos:
""".format(name, doc, name, name) """%(name,doc,name,name)
rst = os.path.sep.join((filePath.split(os.path.sep)[:-3] + ['docs', 'content', 'examples', name + '.rst'])) rst = os.path.sep.join((filePath.split(os.path.sep)[:-3] + ['docs', 'examples', name + '.rst']))
print 'Creating: {0!s}.rst'.format(name) print 'Creating: %s.rst'%name
f = open(rst, 'w') f = open(rst, 'w')
f.write(out) f.write(out)
f.close() f.close()
+3 -3
View File
@@ -31,7 +31,7 @@ class NonLinearMap(object):
""" """
:param numpy.array u: fields :param numpy.array u: fields
:param numpy.array m: model :param numpy.array m: model
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: derivative of transformed model :return: derivative of transformed model
The *transform* changes the model into the physical property. The *transform* changes the model into the physical property.
@@ -44,7 +44,7 @@ class NonLinearMap(object):
""" """
:param numpy.array u: fields :param numpy.array u: fields
:param numpy.array m: model :param numpy.array m: model
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: derivative of transformed model :return: derivative of transformed model
The *transform* changes the model into the physical property. The *transform* changes the model into the physical property.
@@ -116,7 +116,7 @@ class RichardsMap(object):
ax.semilogx(self.k(h, m), h) ax.semilogx(self.k(h, m), h)
def _assertMatchesPair(self, pair): def _assertMatchesPair(self, pair):
assert isinstance(self, pair), "Mapping object must be an instance of a {0!s} class.".format((pair.__name__)) assert isinstance(self, pair), "Mapping object must be an instance of a %s class."%(pair.__name__)
+1 -1
View File
@@ -140,7 +140,7 @@ class RichardsProblem(Problem.BaseTimeProblem):
for ii, dt in enumerate(self.timeSteps): for ii, dt in enumerate(self.timeSteps):
bc = self.getBoundaryConditions(ii, u[ii]) bc = self.getBoundaryConditions(ii, u[ii])
u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, bc, return_g=return_g), u[ii]) u[ii+1] = self.rootFinder.root(lambda hn1m, return_g=True: self.getResidual(m, u[ii], hn1m, dt, bc, return_g=return_g), u[ii])
if self.debug: print "Solving Fields ({0:4d}/{1:d} - {2:3.1f}% Done) {3:d} Iterations, {4:4.2f} seconds".format(ii+1, self.nT, 100.0*(ii+1)/self.nT, self.rootFinder.iter, time.time() - tic) if self.debug: print "Solving Fields (%4d/%d - %3.1f%% Done) %d Iterations, %4.2f seconds"%(ii+1, self.nT, 100.0*(ii+1)/self.nT, self.rootFinder.iter, time.time() - tic)
return u return u
@Utils.timeIt @Utils.timeIt
+4 -4
View File
@@ -37,7 +37,7 @@ class Fields(object):
for f in self.knownFields: for f in self.knownFields:
loc =self.knownFields[f] loc =self.knownFields[f]
sz += np.array(self._storageShape(loc)).prod()*8.0/(1024**2) sz += np.array(self._storageShape(loc)).prod()*8.0/(1024**2)
return "{0:e} MB".format(sz) return "%e MB"%sz
def _storageShape(self, loc): def _storageShape(self, loc):
nSrc = self.survey.nSrc nSrc = self.survey.nSrc
@@ -84,12 +84,12 @@ class Fields(object):
return return
if accessType=='set' and name not in self.knownFields: if accessType=='set' and name not in self.knownFields:
if name in self.aliasFields: if name in self.aliasFields:
raise KeyError("Invalid field name ({0!s}) for setter, you can't set an aliased property".format(name)) raise KeyError("Invalid field name (%s) for setter, you can't set an aliased property"%name)
else: else:
raise KeyError('Invalid field name ({0!s}) for setter'.format(name)) raise KeyError('Invalid field name (%s) for setter'%name)
elif accessType=='get' and (name not in self.knownFields and name not in self.aliasFields): elif accessType=='get' and (name not in self.knownFields and name not in self.aliasFields):
raise KeyError('Invalid field name ({0!s}) for getter'.format(name)) raise KeyError('Invalid field name (%s) for getter'%name)
return name return name
def _indexAndNameFromKey(self, key, accessType): def _indexAndNameFromKey(self, key, accessType):
-132
View File
@@ -1,132 +0,0 @@
from SimPEG import SolverLU as SimpegSolver, PropMaps, Utils, mkvc, sp, np
from SimPEG.EM.FDEM.ProblemFDEM import BaseFDEMProblem
from SurveyMT import Survey, Data
from FieldsMT import BaseMTFields
class BaseMTProblem(BaseFDEMProblem):
"""
Base class for all Natural source problems.
"""
def __init__(self, mesh, **kwargs):
BaseFDEMProblem.__init__(self, mesh, **kwargs)
Utils.setKwargs(self, **kwargs)
# Set the default pairs of the problem
surveyPair = Survey
dataPair = Data
fieldsPair = BaseMTFields
# Set the solver
Solver = SimpegSolver
solverOpts = {}
verbose = False
# Notes:
# Use the forward and devs from BaseFDEMProblem
# Might need to add more stuff here.
## NEED to clean up the Jvec and Jtvec to use Zero and Identities for None components.
def Jvec(self, m, v, f=None):
"""
Function to calculate the data sensitivities dD/dm times a vector.
:param numpy.ndarray m (nC, 1) - conductive model
:param numpy.ndarray v (nC, 1) - random vector
:param MTfields object (optional) - MT fields object, if not given it is calculated
:rtype: MTdata object
:return: Data sensitivities wrt m
"""
# Calculate the fields
if f is None:
f= self.fields(m)
# Set current model
self.curModel = m
# Initiate the Jv object
Jv = self.dataPair(self.survey)
# Loop all the frequenies
for freq in self.survey.freqs:
dA_du = self.getA(freq) #
dA_duI = self.Solver(dA_du, **self.solverOpts)
for src in self.survey.getSrcByFreq(freq):
# We need fDeriv_m = df/du*du/dm + df/dm
# Construct du/dm, it requires a solve
# NOTE: need to account for the 2 polarizations in the derivatives.
f_src = f[src,:]
# dA_dm and dRHS_dm should be of size nE,2, so that we can multiply by dA_duI. The 2 columns are each of the polarizations.
dA_dm = self.getADeriv_m(freq, f_src, v) # Size: nE,2 (u_px,u_py) in the columns.
dRHS_dm = self.getRHSDeriv_m(freq, v) # Size: nE,2 (u_px,u_py) in the columns.
if dRHS_dm is None:
du_dm = dA_duI * ( -dA_dm )
else:
du_dm = dA_duI * ( -dA_dm + dRHS_dm )
# Calculate the projection derivatives
for rx in src.rxList:
# Get the projection derivative
# v should be of size 2*nE (for 2 polarizations)
PDeriv_u = lambda t: rx.evalDeriv(src, self.mesh, f, t) # wrt u, we don't have have PDeriv wrt m
Jv[src, rx] = PDeriv_u(mkvc(du_dm))
dA_duI.clean()
# Return the vectorized sensitivities
return mkvc(Jv)
def Jtvec(self, m, v, f=None):
"""
Function to calculate the transpose of the data sensitivities (dD/dm)^T times a vector.
:param numpy.ndarray m (nC, 1) - conductive model
:param numpy.ndarray v (nD, 1) - vector
:param MTfields object u (optional) - MT fields object, if not given it is calculated
:rtype: MTdata object
:return: Data sensitivities wrt m
"""
if f is None:
f = self.fields(m)
self.curModel = m
# Ensure v is a data object.
if not isinstance(v, self.dataPair):
v = self.dataPair(self.survey, v)
Jtv = np.zeros(m.size)
for freq in self.survey.freqs:
AT = self.getA(freq).T
ATinv = self.Solver(AT, **self.solverOpts)
for src in self.survey.getSrcByFreq(freq):
ftype = self._fieldType + 'Solution'
f_src = f[src, :]
for rx in src.rxList:
# Get the adjoint evalDeriv
# PTv needs to be nE,
PTv = rx.evalDeriv(src, self.mesh, f, mkvc(v[src, rx],2), adjoint=True) # wrt u, need possibility wrt m
# Get the
dA_duIT = ATinv * PTv
dA_dmT = self.getADeriv_m(freq, f_src, mkvc(dA_duIT), adjoint=True)
dRHS_dmT = self.getRHSDeriv_m(freq, mkvc(dA_duIT), adjoint=True)
# Make du_dmT
if dRHS_dmT is None:
du_dmT = -dA_dmT
else:
du_dmT = -dA_dmT + dRHS_dmT
# Select the correct component
# du_dmT needs to be of size nC,
real_or_imag = rx.projComp
if real_or_imag == 'real':
Jtv += du_dmT.real
elif real_or_imag == 'imag':
Jtv += -du_dmT.real
else:
raise Exception('Must be real or imag')
# Clean the factorization, clear memory.
ATinv.clean()
return Jtv
-291
View File
@@ -1,291 +0,0 @@
from SimPEG.EM.Utils import omega
from SimPEG import mkvc
from scipy.constants import mu_0
from SimPEG.MT.BaseMT import BaseMTProblem
from SimPEG.MT.SurveyMT import Survey, Data
from SimPEG.MT.FieldsMT import Fields1D_e
from SimPEG.MT.Utils.MT1Danalytic import getEHfields
import numpy as np
import multiprocessing, sys, time
class eForm_psField(BaseMTProblem):
"""
A MT problem soving a e formulation and primary/secondary fields decomposion.
By eliminating the magnetic flux density using
.. math ::
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} \\right)
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
.. math ::
\\left(\mathbf{C}^T \mathbf{M^e_{\mu^{-1}}} \mathbf{C} + i \omega \mathbf{M^f_\sigma}] \mathbf{e}_{s} =& i \omega \mathbf{M^f_{\delta \sigma}} \mathbf{e}_{p}
which we solve for \\\(\\\mathbf{e_s}\\\). The total field \\\mathbf{e}\\ = \\\mathbf{e_p}\\ + \\\mathbf{e_s}\\.
The primary field is estimated from a background model (commonly half space ).
"""
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
_fieldType = 'e_1d'
_eqLocs = 'EF'
_sigmaPrimary = None
def __init__(self, mesh, **kwargs):
BaseMTProblem.__init__(self, mesh, **kwargs)
self.fieldsPair = Fields1D_e
# self._sigmaPrimary = sigmaPrimary
@property
def MeMui(self):
"""
Edge inner product matrix
"""
if getattr(self, '_MeMui', None) is None:
self._MeMui = self.mesh.getEdgeInnerProduct(1.0/mu_0)
return self._MeMui
@property
def MfSigma(self):
"""
Edge inner product matrix
"""
if getattr(self, '_MfSigma', None) is None:
self._MfSigma = self.mesh.getFaceInnerProduct(self.curModel.sigma)
return self._MfSigma
@property
def sigmaPrimary(self):
"""
A background model, use for the calculation of the primary fields.
"""
return self._sigmaPrimary
@sigmaPrimary.setter
def sigmaPrimary(self, val):
# Note: TODO add logic for val, make sure it is the correct size.
self._sigmaPrimary = val
def getA(self, freq):
"""
Function to get the A matrix.
:param float freq: Frequency
:rtype: scipy.sparse.csr_matrix
:return: A
"""
# Note: need to use the code above since in the 1D problem I want
# e to live on Faces(nodes) and h on edges(cells). Might need to rethink this
# Possible that _fieldType and _eqLocs can fix this
MeMui = self.MeMui
MfSigma = self.MfSigma
C = self.mesh.nodalGrad
# Make A
A = C.T*MeMui*C + 1j*omega(freq)*MfSigma
# Either return full or only the inner part of A
return A
def getADeriv_m(self, freq, u, v, adjoint=False):
"""
The derivative of A wrt sigma
"""
dsig_dm = self.curModel.sigmaDeriv
MeMui = self.MeMui
#
u_src = u['e_1dSolution']
dMfSigma_dm = self.mesh.getFaceInnerProductDeriv(self.curModel.sigma)(u_src) * self.curModel.sigmaDeriv
if adjoint:
return 1j * omega(freq) * ( dMfSigma_dm.T * v )
# Note: output has to be nN/nF, not nC/nE.
# v should be nC
return 1j * omega(freq) * ( dMfSigma_dm * v )
def getRHS(self, freq):
"""
Function to return the right hand side for the system.
:param float freq: Frequency
:rtype: numpy.ndarray (nF, 1), numpy.ndarray (nF, 1)
:return: RHS for 1 polarizations, primary fields
"""
# Get sources for the frequncy(polarizations)
Src = self.survey.getSrcByFreq(freq)[0]
S_e = Src.S_e(self)
return -1j * omega(freq) * S_e
def getRHSDeriv_m(self, freq, v, adjoint=False):
"""
The derivative of the RHS wrt sigma
"""
Src = self.survey.getSrcByFreq(freq)[0]
S_eDeriv = Src.S_eDeriv_m(self, v, adjoint)
return -1j * omega(freq) * S_eDeriv
def fields(self, m):
'''
Function to calculate all the fields for the model m.
:param np.ndarray (nC,) m: Conductivity model
'''
# Set the current model
self.curModel = m
F = Fields1D_e(self.mesh, self.survey)
for freq in self.survey.freqs:
if self.verbose:
startTime = time.time()
print 'Starting work for {:.3e}'.format(freq)
sys.stdout.flush()
A = self.getA(freq)
rhs = self.getRHS(freq)
Ainv = self.Solver(A, **self.solverOpts)
e_s = Ainv * rhs
# Store the fields
Src = self.survey.getSrcByFreq(freq)[0]
# NOTE: only store the e_solution(secondary), all other components calculated in the fields object
F[Src, 'e_1dSolution'] = e_s[:,-1] # Only storing the yx polarization as 1d
# Note curl e = -iwb so b = -curl e /iw
# b = -( self.mesh.nodalGrad * e )/( 1j*omega(freq) )
# F[Src, 'b_1d'] = b[:,1]
if self.verbose:
print 'Ran for {:f} seconds'.format(time.time()-startTime)
sys.stdout.flush()
return F
# Note this is not fully functional.
# Missing:
# Fields class corresponding to the fields
# Update Jvec and Jtvec to include all the derivatives components
# Other things ...
class eForm_TotalField(BaseMTProblem):
"""
A MT problem solving a e formulation and a Total bondary domain decompostion.
Solves the equation:
Math:
"""
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
_fieldType = 'e'
_eqLocs = 'EF'
def __init__(self, mesh, **kwargs):
BaseMTProblem.__init__(self, mesh, **kwargs)
@property
def MeMui(self):
"""
Edge inner product matrix
"""
if getattr(self, '_MeMui', None) is None:
self._MeMui = self.mesh.getEdgeInnerProduct(1.0/mu_0)
return self._MeMui
@property
def MfSigma(self):
"""
Edge inner product matrix
"""
if getattr(self, '_MfSigma', None) is None:
self._MfSigma = self.mesh.getFaceInnerProduct(self.curModel.sigma)
return self._MfSigma
def getA(self, freq, full=False):
"""
Function to get the A matrix.
:param float freq: Frequency
:param logic full: Return full A or the inner part
:rtype: scipy.sparse.csr_matrix
:return: A
"""
MeMui = self.MeMui
MfSigma = self.MfSigma
# Note: need to use the code above since in the 1D problem I want
# e to live on Faces(nodes) and h on edges(cells). Might need to rethink this
# Possible that _fieldType and _eqLocs can fix this
# MeMui = self.MfMui
# MfSigma = self.MfSigma
C = self.mesh.nodalGrad
# Make A
A = C.T*MeMui*C + 1j*omega(freq)*MfSigma
# Either return full or only the inner part of A
if full:
return A
else:
return A[1:-1,1:-1]
def getADeriv_m(self, freq, u, v, adjoint=False):
raise NotImplementedError('getADeriv is not implemented')
def getRHS(self, freq):
"""
Function to return the right hand side for the system.
:param float freq: Frequency
:rtype: numpy.ndarray (nE, 2), numpy.ndarray (nE, 2)
:return: RHS for both polarizations, primary fields
"""
# Get sources for the frequency
# NOTE: Need to use the source information, doesn't really apply in 1D
src = self.survey.getSrcByFreq(freq)
# Get the full A
A = self.getA(freq,full=True)
# Define the outer part of the solution matrix
Aio = A[1:-1,[0,-1]]
Ed, Eu, Hd, Hu = getEHfields(self.mesh,self.curModel.sigma,freq,self.mesh.vectorNx)
Etot = (Ed + Eu)
sourceAmp = 1.0
Etot = ((Etot/Etot[-1])*sourceAmp) # Scale the fields to be equal to sourceAmp at the top
## Note: The analytic solution is derived with e^iwt
eBC = np.r_[Etot[0],Etot[-1]]
# The right hand side
return -Aio*eBC, eBC
def getRHSderiv_m(self, freq, backSigma, u, v, adjoint=False):
raise NotImplementedError('getRHSDeriv not implemented yet')
return None
def fields(self, m):
'''
Function to calculate all the fields for the model m.
:param np.ndarray (nC,) m: Conductivity model
:param np.ndarray (nC,) m_back: Background conductivity model
'''
self.curModel = m
# RHS, CalcFields = self.getRHS(freq,m_back), self.calcFields
F = Fields1D_e(self.mesh, self.survey)
for freq in self.survey.freqs:
if self.verbose:
startTime = time.time()
print 'Starting work for {:.3e}'.format(freq)
sys.stdout.flush()
A = self.getA(freq)
rhs, e_o = self.getRHS(freq)
Ainv = self.Solver(A, **self.solverOpts)
e_i = Ainv * rhs
e = mkvc(np.r_[e_o[0], e_i, e_o[1]],2)
# Store the fields
Src = self.survey.getSrcByFreq(freq)
# NOTE: only store e fields
F[Src, 'e_1dSolution'] = e[:,0]
if self.verbose:
print 'Ran for {:f} seconds'.format(time.time()-startTime)
sys.stdout.flush()
return F
-1
View File
@@ -1 +0,0 @@
from Probs import eForm_TotalField, eForm_psField
View File
-1
View File
@@ -1 +0,0 @@
pass
-138
View File
@@ -1,138 +0,0 @@
from SimPEG import Survey, Problem, Utils, Models, np, sp, mkvc, SolverLU as SimpegSolver
from SimPEG.EM.Utils import omega
from scipy.constants import mu_0
from SimPEG.MT.BaseMT import BaseMTProblem
from SimPEG.MT.SurveyMT import Survey, Data
from SimPEG.MT.FieldsMT import Fields3D_e
import multiprocessing, sys, time
class eForm_ps(BaseMTProblem):
"""
A MT problem solving a e formulation and a primary/secondary fields decompostion.
By eliminating the magnetic flux density using
.. math ::
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} \\right)
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
.. math ::
\\left(\mathbf{C}^T \mathbf{M^f_{\mu^{-1}}} \mathbf{C} + i \omega \mathbf{M^e_\sigma}] \mathbf{e}_{s} =& i \omega \mathbf{M^e_{\delta \sigma}} \mathbf{e}_{p}
which we solve for \\\(\\\mathbf{e_s}\\\). The total field \\\mathbf{e}\\ = \\\mathbf{e_p}\\ + \\\mathbf{e_s}\\.
The primary field is estimated from a background model (commonly as a 1D model).
"""
# From FDEMproblem: Used to project the fields. Currently not used for MTproblem.
_fieldType = 'e'
_eqLocs = 'FE'
fieldsPair = Fields3D_e
_sigmaPrimary = None
def __init__(self, mesh, **kwargs):
BaseMTProblem.__init__(self, mesh, **kwargs)
@property
def sigmaPrimary(self):
"""
A background model, use for the calculation of the primary fields.
"""
return self._sigmaPrimary
@sigmaPrimary.setter
def sigmaPrimary(self, val):
# Note: TODO add logic for val, make sure it is the correct size.
self._sigmaPrimary = val
def getA(self, freq):
"""
Function to get the A system.
:param float freq: Frequency
:rtype: scipy.sparse.csr_matrix
:return: A
"""
Mmui = self.MfMui
Msig = self.MeSigma
C = self.mesh.edgeCurl
return C.T*Mmui*C + 1j*omega(freq)*Msig
def getADeriv_m(self, freq, u, v, adjoint=False):
"""
Calculate the derivative of A wrt m.
"""
# This considers both polarizations and returns a nE,2 matrix for each polarization
if adjoint:
dMe_dsigV = sp.hstack(( self.MeSigmaDeriv( u['e_pxSolution'] ).T, self.MeSigmaDeriv(u['e_pySolution'] ).T ))*v
else:
# Need a nE,2 matrix to be returned
dMe_dsigV = np.hstack(( mkvc(self.MeSigmaDeriv( u['e_pxSolution'] )*v,2), mkvc( self.MeSigmaDeriv(u['e_pySolution'] )*v,2) ))
return 1j * omega(freq) * dMe_dsigV
def getRHS(self, freq):
"""
Function to return the right hand side for the system.
:param float freq: Frequency
:rtype: numpy.ndarray (nE, 2), numpy.ndarray (nE, 2)
:return: RHS for both polarizations, primary fields
"""
# Get sources for the frequncy(polarizations)
Src = self.survey.getSrcByFreq(freq)[0]
S_e = Src.S_e(self)
return -1j * omega(freq) * S_e
def getRHSDeriv_m(self, freq, v, adjoint=False):
"""
The derivative of the RHS with respect to sigma
"""
Src = self.survey.getSrcByFreq(freq)[0]
S_eDeriv = Src.S_eDeriv_m(self, v, adjoint)
return -1j * omega(freq) * S_eDeriv
def fields(self, m):
'''
Function to calculate all the fields for the model m.
:param np.ndarray (nC,) m: Conductivity model
'''
# Set the current model
self.curModel = m
F = Fields3D_e(self.mesh, self.survey)
for freq in self.survey.freqs:
if self.verbose:
startTime = time.time()
print 'Starting work for {:.3e}'.format(freq)
sys.stdout.flush()
A = self.getA(freq)
rhs = self.getRHS(freq)
# Solve the system
Ainv = self.Solver(A, **self.solverOpts)
e_s = Ainv * rhs
# Store the fields
Src = self.survey.getSrcByFreq(freq)[0]
# Store the fieldss
F[Src, 'e_pxSolution'] = e_s[:,0]
F[Src, 'e_pySolution'] = e_s[:,1]
# Note curl e = -iwb so b = -curl/iw
if self.verbose:
print 'Ran for {:f} seconds'.format(time.time()-startTime)
sys.stdout.flush()
Ainv.clean()
return F
-1
View File
@@ -1 +0,0 @@
from Probs import eForm_ps
-4
View File
@@ -1,4 +0,0 @@
from MT1Dsolutions import * # Add the names of the functions
from MT1Danalytic import *
from dataUtils import *
from ediFilesUtils import *
-46
View File
@@ -1,46 +0,0 @@
import SimPEG as simpeg, numpy as np
def homo1DModelSource(mesh,freq,m_back):
'''
Function that calculates and return background fields for a 3D mesh and model.
The calculuations use 1D field solution for a vertical slice throught model (south-western most column),
which is assigned at the fields everywhere for the respective polarizations.2
:param Simpeg mesh object mesh: Holds information on the discretization
:param float freq: The frequency to solve at
:param np.array m_back: Background model of conductivity to base the calculations on.
:rtype: numpy.ndarray (mesh.nE,2)
:return: eBG_bp, E fields for the background model at both polarizations.
'''
# import
from SimPEG.MT.Utils import get1DEfields
# Get a 1d solution for a halfspace background
mesh1d = simpeg.Mesh.TensorMesh([mesh.hz],np.array([mesh.x0[2]]))
# Note: Everything is using e^iwt
e0_1d = get1DEfields(mesh1d,mesh.r(m_back,'CC','CC','M')[0,0,:],freq)
# Setup x (east) polarization (_x)
ex_px = np.zeros(mesh.vnEx,dtype=complex)
ey_px = np.zeros((mesh.nEy,1),dtype=complex)
ez_px = np.zeros((mesh.nEz,1),dtype=complex)
# Assign the source to ex_x
for i in np.arange(mesh.vnEx[0]):
for j in np.arange(mesh.vnEx[1]):
ex_px[i,j,:] = -e0_1d
eBG_px = np.vstack((simpeg.Utils.mkvc(ex_px,2),ey_px,ez_px))
# Setup y (north) polarization (_py)
ex_py = np.zeros((mesh.nEx,1), dtype='complex128')
ey_py = np.zeros(mesh.vnEy, dtype='complex128')
ez_py = np.zeros((mesh.nEz,1), dtype='complex128')
# Assign the source to ey_py
for i in np.arange(mesh.vnEy[0]):
for j in np.arange(mesh.vnEy[1]):
ey_py[i,j,:] = e0_1d
# ey_py[1:-1,1:-1,1:-1] = 0
eBG_py = np.vstack((ex_py,simpeg.Utils.mkvc(ey_py,2),ez_py))
# Return the electric fields
eBG_bp = np.hstack((eBG_px,eBG_py))
return eBG_bp
-5
View File
@@ -1,5 +0,0 @@
import Utils
from SurveyMT import Rx, Survey, Data
from FieldsMT import Fields1D_e, Fields3D_e
import Problem1D, Problem2D, Problem3D
import SrcMT
+14 -16
View File
@@ -41,8 +41,8 @@ class IdentityMap(object):
If this is a meshless mapping (i.e. nP is defined independently) If this is a meshless mapping (i.e. nP is defined independently)
the shape will be the the shape (nP,nP). the shape will be the the shape (nP,nP).
:rtype: tuple :rtype: (int,int)
:return: shape of the operator as a tuple (int,int) :return: shape of the operator as a tuple
""" """
if self._nP is not None: if self._nP is not None:
return (self.nP, self.nP) return (self.nP, self.nP)
@@ -86,7 +86,7 @@ class IdentityMap(object):
The derivative of the transformation. The derivative of the transformation.
:param numpy.array m: model :param numpy.array m: model
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: derivative of transformed model :return: derivative of transformed model
""" """
@@ -101,7 +101,7 @@ class IdentityMap(object):
:return: passed the test? :return: passed the test?
""" """
print 'Testing {0!s}'.format(str(self)) print 'Testing %s' % str(self)
if m is None: if m is None:
m = abs(np.random.rand(self.nP)) m = abs(np.random.rand(self.nP))
if 'plotIt' not in kwargs: if 'plotIt' not in kwargs:
@@ -111,21 +111,21 @@ class IdentityMap(object):
def _assertMatchesPair(self, pair): def _assertMatchesPair(self, pair):
assert (isinstance(self, pair) or assert (isinstance(self, pair) or
isinstance(self, ComboMap) and isinstance(self.maps[0], pair) isinstance(self, ComboMap) and isinstance(self.maps[0], pair)
), "Mapping object must be an instance of a {0!s} class.".format((pair.__name__)) ), "Mapping object must be an instance of a %s class."%(pair.__name__)
def __mul__(self, val): def __mul__(self, val):
if isinstance(val, IdentityMap): if isinstance(val, IdentityMap):
if not (self.shape[1] == '*' or val.shape[0] == '*') and not self.shape[1] == val.shape[0]: if not (self.shape[1] == '*' or val.shape[0] == '*') and not self.shape[1] == val.shape[0]:
raise ValueError('Dimension mismatch in {0!s} and {1!s}.'.format(str(self), str(val))) raise ValueError('Dimension mismatch in %s and %s.' % (str(self), str(val)))
return ComboMap([self, val]) return ComboMap([self, val])
elif isinstance(val, np.ndarray): elif isinstance(val, np.ndarray):
if not self.shape[1] == '*' and not self.shape[1] == val.shape[0]: if not self.shape[1] == '*' and not self.shape[1] == val.shape[0]:
raise ValueError('Dimension mismatch in {0!s} and np.ndarray{1!s}.'.format(str(self), str(val.shape))) raise ValueError('Dimension mismatch in %s and np.ndarray%s.' % (str(self), str(val.shape)))
return self._transform(val) return self._transform(val)
raise Exception('Unrecognized data type to multiply. Try a map or a numpy.ndarray!') raise Exception('Unrecognized data type to multiply. Try a map or a numpy.ndarray!')
def __str__(self): def __str__(self):
return "{0!s}({1!s},{2!s})".format(self.__class__.__name__, self.shape[0], self.shape[1]) return "%s(%s,%s)" % (self.__class__.__name__, self.shape[0], self.shape[1])
class ComboMap(IdentityMap): class ComboMap(IdentityMap):
@@ -140,7 +140,7 @@ class ComboMap(IdentityMap):
if ii > 0 and not (self.shape[1] == '*' or m.shape[0] == '*') and not self.shape[1] == m.shape[0]: if ii > 0 and not (self.shape[1] == '*' or m.shape[0] == '*') and not self.shape[1] == m.shape[0]:
prev = self.maps[-1] prev = self.maps[-1]
errArgs = (prev.__class__.__name__, prev.shape[0], prev.shape[1], m.__class__.__name__, m.shape[0], m.shape[1]) errArgs = (prev.__class__.__name__, prev.shape[0], prev.shape[1], m.__class__.__name__, m.shape[0], m.shape[1])
raise ValueError('Dimension mismatch in map[{0!s}] ({1!s}, {2!s}) and map[{3!s}] ({4!s}, {5!s}).'.format(*errArgs)) raise ValueError('Dimension mismatch in map[%s] (%s, %s) and map[%s] (%s, %s).' % errArgs)
if isinstance(m, ComboMap): if isinstance(m, ComboMap):
self.maps += m.maps self.maps += m.maps
@@ -173,7 +173,7 @@ class ComboMap(IdentityMap):
return deriv return deriv
def __str__(self): def __str__(self):
return 'ComboMap[{0!s}]({1!s},{2!s})'.format(' * '.join([m.__str__() for m in self.maps]), self.shape[0], self.shape[1]) return 'ComboMap[%s](%s,%s)' % (' * '.join([m.__str__() for m in self.maps]), self.shape[0], self.shape[1])
class ExpMap(IdentityMap): class ExpMap(IdentityMap):
@@ -216,7 +216,7 @@ class ExpMap(IdentityMap):
def deriv(self, m): def deriv(self, m):
""" """
:param numpy.array m: model :param numpy.array m: model
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: derivative of transformed model :return: derivative of transformed model
The *transform* changes the model into the physical property. The *transform* changes the model into the physical property.
@@ -366,7 +366,7 @@ class SurjectVertical1D(IdentityMap):
def deriv(self, m): def deriv(self, m):
""" """
:param numpy.array m: model :param numpy.array m: model
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: derivative of transformed model :return: derivative of transformed model
""" """
repNum = self.mesh.vnC[:self.mesh.dim-1].prod() repNum = self.mesh.vnC[:self.mesh.dim-1].prod()
@@ -427,7 +427,7 @@ class Surject2Dto3D(IdentityMap):
def deriv(self, m): def deriv(self, m):
""" """
:param numpy.array m: model :param numpy.array m: model
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: derivative of transformed model :return: derivative of transformed model
""" """
inds = self * np.arange(self.nP) inds = self * np.arange(self.nP)
@@ -502,9 +502,7 @@ class InjectActiveCells(IdentityMap):
if Utils.isScalar(valInactive): if Utils.isScalar(valInactive):
self.valInactive = np.ones(self.nC)*float(valInactive) self.valInactive = np.ones(self.nC)*float(valInactive)
else: else:
self.valInactive = np.ones(self.nC) self.valInactive = valInactive.copy()
self.valInactive[self.indInactive] = valInactive.copy()
self.valInactive[self.indActive] = 0 self.valInactive[self.indActive] = 0
inds = np.nonzero(self.indActive)[0] inds = np.nonzero(self.indActive)[0]
+25 -27
View File
@@ -7,8 +7,8 @@ class BaseMesh(object):
BaseMesh does all the counting you don't want to do. BaseMesh does all the counting you don't want to do.
BaseMesh should be inherited by meshes with a regular structure. BaseMesh should be inherited by meshes with a regular structure.
:param numpy.array n: (or list) number of cells in each direction (dim, ) :param numpy.array,list n: number of cells in each direction (dim, )
:param numpy.array x0: (or list) Origin of the mesh (dim, ) :param numpy.array,list x0: Origin of the mesh (dim, )
""" """
@@ -34,8 +34,8 @@ class BaseMesh(object):
""" """
Origin of the mesh Origin of the mesh
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: x0, (dim, ) :return: x0
""" """
return self._x0 return self._x0
@@ -116,8 +116,8 @@ class BaseMesh(object):
""" """
Total number of edges in each direction Total number of edges in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: [nEx, nEy, nEz], (dim, ) :return: [nEx, nEy, nEz]
.. plot:: .. plot::
:include-source: :include-source:
@@ -173,8 +173,8 @@ class BaseMesh(object):
""" """
Total number of faces in each direction Total number of faces in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: [nFx, nFy, nFz], (dim, ) :return: [nFx, nFy, nFz]
.. plot:: .. plot::
:include-source: :include-source:
@@ -200,8 +200,8 @@ class BaseMesh(object):
""" """
Face Normals Face Normals
:rtype: numpy.array :rtype: numpy.array (sum(nF), dim)
:return: normals, (sum(nF), dim) :return: normals
""" """
if self.dim == 2: if self.dim == 2:
nX = np.c_[np.ones(self.nFx), np.zeros(self.nFx)] nX = np.c_[np.ones(self.nFx), np.zeros(self.nFx)]
@@ -218,8 +218,8 @@ class BaseMesh(object):
""" """
Edge Tangents Edge Tangents
:rtype: numpy.array :rtype: numpy.array (sum(nE), dim)
:return: normals, (sum(nE), dim) :return: normals
""" """
if self.dim == 2: if self.dim == 2:
tX = np.c_[np.ones(self.nEx), np.zeros(self.nEx)] tX = np.c_[np.ones(self.nEx), np.zeros(self.nEx)]
@@ -236,9 +236,8 @@ class BaseMesh(object):
Given a vector, fV, in cartesian coordinates, this will project it onto the mesh using the normals Given a vector, fV, in cartesian coordinates, this will project it onto the mesh using the normals
:param numpy.array fV: face vector with shape (nF, dim) :param numpy.array fV: face vector with shape (nF, dim)
:rtype: numpy.array :rtype: numpy.array with shape (nF, )
:return: projected face vector, (nF, ) :return: projected face vector
""" """
assert isinstance(fV, np.ndarray), 'fV must be an ndarray' assert isinstance(fV, np.ndarray), 'fV must be an ndarray'
assert len(fV.shape) == 2 and fV.shape[0] == self.nF and fV.shape[1] == self.dim, 'fV must be an ndarray of shape (nF x dim)' assert len(fV.shape) == 2 and fV.shape[0] == self.nF and fV.shape[1] == self.dim, 'fV must be an ndarray of shape (nF x dim)'
@@ -249,9 +248,8 @@ class BaseMesh(object):
Given a vector, eV, in cartesian coordinates, this will project it onto the mesh using the tangents Given a vector, eV, in cartesian coordinates, this will project it onto the mesh using the tangents
:param numpy.array eV: edge vector with shape (nE, dim) :param numpy.array eV: edge vector with shape (nE, dim)
:rtype: numpy.array :rtype: numpy.array with shape (nE, )
:return: projected edge vector, (nE, ) :return: projected edge vector
""" """
assert isinstance(eV, np.ndarray), 'eV must be an ndarray' assert isinstance(eV, np.ndarray), 'eV must be an ndarray'
assert len(eV.shape) == 2 and eV.shape[0] == self.nE and eV.shape[1] == self.dim, 'eV must be an ndarray of shape (nE x dim)' assert len(eV.shape) == 2 and eV.shape[0] == self.nE and eV.shape[1] == self.dim, 'eV must be an ndarray of shape (nE x dim)'
@@ -297,7 +295,7 @@ class BaseRectangularMesh(BaseMesh):
""" """
Total number of cells in each direction Total number of cells in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: [nCx, nCy, nCz] :return: [nCx, nCy, nCz]
""" """
return np.array([x for x in [self.nCx, self.nCy, self.nCz] if not x is None]) return np.array([x for x in [self.nCx, self.nCy, self.nCz] if not x is None])
@@ -337,7 +335,7 @@ class BaseRectangularMesh(BaseMesh):
""" """
Total number of nodes in each direction Total number of nodes in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: [nNx, nNy, nNz] :return: [nNx, nNy, nNz]
""" """
return np.array([x for x in [self.nNx, self.nNy, self.nNz] if not x is None]) return np.array([x for x in [self.nNx, self.nNy, self.nNz] if not x is None])
@@ -347,7 +345,7 @@ class BaseRectangularMesh(BaseMesh):
""" """
Number of x-edges in each direction Number of x-edges in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnEx :return: vnEx
""" """
return np.array([x for x in [self.nCx, self.nNy, self.nNz] if not x is None]) return np.array([x for x in [self.nCx, self.nNy, self.nNz] if not x is None])
@@ -357,7 +355,7 @@ class BaseRectangularMesh(BaseMesh):
""" """
Number of y-edges in each direction Number of y-edges in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnEy or None if dim < 2 :return: vnEy or None if dim < 2
""" """
return None if self.dim < 2 else np.array([x for x in [self.nNx, self.nCy, self.nNz] if not x is None]) return None if self.dim < 2 else np.array([x for x in [self.nNx, self.nCy, self.nNz] if not x is None])
@@ -367,7 +365,7 @@ class BaseRectangularMesh(BaseMesh):
""" """
Number of z-edges in each direction Number of z-edges in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnEz or None if dim < 3 :return: vnEz or None if dim < 3
""" """
return None if self.dim < 3 else np.array([x for x in [self.nNx, self.nNy, self.nCz] if not x is None]) return None if self.dim < 3 else np.array([x for x in [self.nNx, self.nNy, self.nCz] if not x is None])
@@ -377,7 +375,7 @@ class BaseRectangularMesh(BaseMesh):
""" """
Number of x-faces in each direction Number of x-faces in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnFx :return: vnFx
""" """
return np.array([x for x in [self.nNx, self.nCy, self.nCz] if not x is None]) return np.array([x for x in [self.nNx, self.nCy, self.nCz] if not x is None])
@@ -387,7 +385,7 @@ class BaseRectangularMesh(BaseMesh):
""" """
Number of y-faces in each direction Number of y-faces in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnFy or None if dim < 2 :return: vnFy or None if dim < 2
""" """
return None if self.dim < 2 else np.array([x for x in [self.nCx, self.nNy, self.nCz] if not x is None]) return None if self.dim < 2 else np.array([x for x in [self.nCx, self.nNy, self.nCz] if not x is None])
@@ -397,7 +395,7 @@ class BaseRectangularMesh(BaseMesh):
""" """
Number of z-faces in each direction Number of z-faces in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnFz or None if dim < 3 :return: vnFz or None if dim < 3
""" """
return None if self.dim < 3 else np.array([x for x in [self.nCx, self.nCy, self.nNz] if not x is None]) return None if self.dim < 3 else np.array([x for x in [self.nCx, self.nCy, self.nNz] if not x is None])
@@ -522,7 +520,7 @@ class BaseRectangularMesh(BaseMesh):
assert xType in outType, 'You cannot change type of components.' assert xType in outType, 'You cannot change type of components.'
if type(x) == list: if type(x) == list:
for i, xi in enumerate(x): for i, xi in enumerate(x):
assert isinstance(x, np.ndarray), "x[{0:d}] must be a numpy array".format(i) assert isinstance(x, np.ndarray), "x[%i] must be a numpy array" % i
assert xi.size == x[0].size, "Number of elements in list must not change." assert xi.size == x[0].size, "Number of elements in list must not change."
x_array = np.ones((x.size, len(x))) x_array = np.ones((x.size, len(x)))
+223 -255
View File
@@ -4,28 +4,14 @@ from DiffOperators import DiffOperators
from InnerProducts import InnerProducts from InnerProducts import InnerProducts
from View import CurvView from View import CurvView
# Some helper functions. # Some helper functions.
def length2D(x): length2D = lambda x: (x[:, 0]**2 + x[:, 1]**2)**0.5
return (x[:, 0]**2 + x[:, 1]**2)**0.5 length3D = lambda x: (x[:, 0]**2 + x[:, 1]**2 + x[:, 2]**2)**0.5
normalize2D = lambda x: x/np.kron(np.ones((1, 2)), Utils.mkvc(length2D(x), 2))
normalize3D = lambda x: x/np.kron(np.ones((1, 3)), Utils.mkvc(length3D(x), 2))
def length3D(x): class CurvilinearMesh(BaseRectangularMesh, DiffOperators, InnerProducts, CurvView):
return (x[:, 0]**2 + x[:, 1]**2 + x[:, 2]**2)**0.5
def normalize2D(x):
return x/np.kron(np.ones((1, 2)), Utils.mkvc(length2D(x), 2))
def normalize3D(x):
return x/np.kron(np.ones((1, 3)), Utils.mkvc(length3D(x), 2))
# Curvi Mesh
class CurvilinearMesh(BaseRectangularMesh, DiffOperators, InnerProducts,
CurvView):
""" """
CurvilinearMesh is a mesh class that deals with curvilinear meshes. CurvilinearMesh is a mesh class that deals with curvilinear meshes.
@@ -45,16 +31,12 @@ class CurvilinearMesh(BaseRectangularMesh, DiffOperators, InnerProducts,
_meshType = 'Curv' _meshType = 'Curv'
def __init__(self, nodes): def __init__(self, nodes):
assert type(nodes) == list, ("'nodes' variable must be a list of " assert type(nodes) == list, "'nodes' variable must be a list of np.ndarray"
"np.ndarray")
assert len(nodes) > 1, "len(node) must be greater than 1" assert len(nodes) > 1, "len(node) must be greater than 1"
for i, nodes_i in enumerate(nodes): for i, nodes_i in enumerate(nodes):
assert isinstance(nodes_i, np.ndarray), ("nodes[{0:d}] is not a" assert isinstance(nodes_i, np.ndarray), ("nodes[%i] is not a numpy array." % i)
"numpy array.".format(i)) assert nodes_i.shape == nodes[0].shape, ("nodes[%i] is not the same shape as nodes[0]" % i)
assert nodes_i.shape == nodes[0].shape, ("nodes[{0:d}] is not the "
"same shape as nodes[0]"
.format(i))
assert len(nodes[0].shape) == len(nodes), "Dimension mismatch" assert len(nodes[0].shape) == len(nodes), "Dimension mismatch"
assert len(nodes[0].shape) > 1, "Not worth using Curv for a 1D mesh." assert len(nodes[0].shape) > 1, "Not worth using Curv for a 1D mesh."
@@ -66,113 +48,121 @@ class CurvilinearMesh(BaseRectangularMesh, DiffOperators, InnerProducts,
for i, node_i in enumerate(nodes): for i, node_i in enumerate(nodes):
self._gridN[:, i] = Utils.mkvc(node_i.astype(float)) self._gridN[:, i] = Utils.mkvc(node_i.astype(float))
@property def gridCC():
def gridCC(self): doc = "Cell-centered grid."
"""
Cell-centered grid
"""
if getattr(self, '_gridCC', None) is None:
self._gridCC = np.concatenate([self.aveN2CC*self.gridN[:, i]
for i in range(self.dim)]).reshape(
(-1, self.dim), order='F')
return self._gridCC
@property def fget(self):
def gridN(self): if self._gridCC is None:
""" self._gridCC = np.concatenate([self.aveN2CC*self.gridN[:,i] for i in range(self.dim)]).reshape((-1,self.dim), order='F')
Nodal grid. return self._gridCC
""" return locals()
if getattr(self, '_gridN', None) is None: _gridCC = None # Store grid by default
raise Exception("Someone deleted this. I blame you.") gridCC = property(**gridCC())
return self._gridN
@property def gridN():
def gridFx(self): doc = "Nodal grid."
"""
Face staggered grid in the x direction.
"""
if getattr(self, '_gridFx', None) is None: def fget(self):
N = self.r(self.gridN, 'N', 'N', 'M') if self._gridN is None:
if self.dim == 2: raise Exception("Someone deleted this. I blame you.")
XY = [Utils.mkvc(0.5 * (n[:, :-1] + n[:, 1:])) for n in N] return self._gridN
self._gridFx = np.c_[XY[0], XY[1]] return locals()
elif self.dim == 3: _gridN = None # Store grid by default
XYZ = [Utils.mkvc(0.25 * (n[:, :-1, :-1] + n[:, :-1, 1:] + gridN = property(**gridN())
n[:, 1:, :-1] + n[:, 1:, 1:])) for n in N]
self._gridFx = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridFx
@property def gridFx():
def gridFy(self): doc = "Face staggered grid in the x direction."
"""
Face staggered grid in the y direction.
"""
if getattr(self, '_gridFy', None) is None: def fget(self):
N = self.r(self.gridN, 'N', 'N', 'M') if self._gridFx is None:
if self.dim == 2: N = self.r(self.gridN, 'N', 'N', 'M')
XY = [Utils.mkvc(0.5 * (n[:-1, :] + n[1:, :])) for n in N] if self.dim == 2:
self._gridFy = np.c_[XY[0], XY[1]] XY = [Utils.mkvc(0.5 * (n[:, :-1] + n[:, 1:])) for n in N]
elif self.dim == 3: self._gridFx = np.c_[XY[0], XY[1]]
XYZ = [Utils.mkvc(0.25 * (n[:-1, :, :-1] + n[:-1, :, 1:] + elif self.dim == 3:
n[1:, :, :-1] + n[1:, :, 1:])) for n in N] XYZ = [Utils.mkvc(0.25 * (n[:, :-1, :-1] + n[:, :-1, 1:] + n[:, 1:, :-1] + n[:, 1:, 1:])) for n in N]
self._gridFy = np.c_[XYZ[0], XYZ[1], XYZ[2]] self._gridFx = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridFy return self._gridFx
return locals()
_gridFx = None # Store grid by default
gridFx = property(**gridFx())
@property def gridFy():
def gridFz(self): doc = "Face staggered grid in the y direction."
"""
Face staggered grid in the y direction.
"""
if getattr(self, '_gridFz', None) is None: def fget(self):
N = self.r(self.gridN, 'N', 'N', 'M') if self._gridFy is None:
XYZ = [Utils.mkvc(0.25 * (n[:-1, :-1, :] + n[:-1, 1:, :] + N = self.r(self.gridN, 'N', 'N', 'M')
n[1:, :-1, :] + n[1:, 1:, :])) for n in N] if self.dim == 2:
self._gridFz = np.c_[XYZ[0], XYZ[1], XYZ[2]] XY = [Utils.mkvc(0.5 * (n[:-1, :] + n[1:, :])) for n in N]
return self._gridFz self._gridFy = np.c_[XY[0], XY[1]]
elif self.dim == 3:
XYZ = [Utils.mkvc(0.25 * (n[:-1, :, :-1] + n[:-1, :, 1:] + n[1:, :, :-1] + n[1:, :, 1:])) for n in N]
self._gridFy = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridFy
return locals()
_gridFy = None # Store grid by default
gridFy = property(**gridFy())
@property def gridFz():
def gridEx(self): doc = "Face staggered grid in the z direction."
"""
Edge staggered grid in the x direction.
"""
if getattr(self, '_gridEx', None) is None:
N = self.r(self.gridN, 'N', 'N', 'M')
if self.dim == 2:
XY = [Utils.mkvc(0.5 * (n[:-1, :] + n[1:, :])) for n in N]
self._gridEx = np.c_[XY[0], XY[1]]
elif self.dim == 3:
XYZ = [Utils.mkvc(0.5 * (n[:-1, :, :] + n[1:, :, :])) for n in N]
self._gridEx = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridEx
@property def fget(self):
def gridEy(self): if self._gridFz is None and self.dim == 3:
""" N = self.r(self.gridN, 'N', 'N', 'M')
Edge staggered grid in the y direction. XYZ = [Utils.mkvc(0.25 * (n[:-1, :-1, :] + n[:-1, 1:, :] + n[1:, :-1, :] + n[1:, 1:, :])) for n in N]
""" self._gridFz = np.c_[XYZ[0], XYZ[1], XYZ[2]]
if getattr(self, '_gridEy', None) is None: return self._gridFz
N = self.r(self.gridN, 'N', 'N', 'M') return locals()
if self.dim == 2: _gridFz = None # Store grid by default
XY = [Utils.mkvc(0.5 * (n[:, :-1] + n[:, 1:])) for n in N] gridFz = property(**gridFz())
self._gridEy = np.c_[XY[0], XY[1]]
elif self.dim == 3:
XYZ = [Utils.mkvc(0.5 * (n[:, :-1, :] + n[:, 1:, :])) for n in N]
self._gridEy = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridEy
@property def gridEx():
def gridEz(self): doc = "Edge staggered grid in the x direction."
"""
Edge staggered grid in the z direction. def fget(self):
""" if self._gridEx is None:
if getattr(self, '_gridEz', None) is None and self.dim == 3: N = self.r(self.gridN, 'N', 'N', 'M')
N = self.r(self.gridN, 'N', 'N', 'M') if self.dim == 2:
XYZ = [Utils.mkvc(0.5 * (n[:, :, :-1] + n[:, :, 1:])) for n in N] XY = [Utils.mkvc(0.5 * (n[:-1, :] + n[1:, :])) for n in N]
self._gridEz = np.c_[XYZ[0], XYZ[1], XYZ[2]] self._gridEx = np.c_[XY[0], XY[1]]
return self._gridEz elif self.dim == 3:
XYZ = [Utils.mkvc(0.5 * (n[:-1, :, :] + n[1:, :, :])) for n in N]
self._gridEx = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridEx
return locals()
_gridEx = None # Store grid by default
gridEx = property(**gridEx())
def gridEy():
doc = "Edge staggered grid in the y direction."
def fget(self):
if self._gridEy is None:
N = self.r(self.gridN, 'N', 'N', 'M')
if self.dim == 2:
XY = [Utils.mkvc(0.5 * (n[:, :-1] + n[:, 1:])) for n in N]
self._gridEy = np.c_[XY[0], XY[1]]
elif self.dim == 3:
XYZ = [Utils.mkvc(0.5 * (n[:, :-1, :] + n[:, 1:, :])) for n in N]
self._gridEy = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridEy
return locals()
_gridEy = None # Store grid by default
gridEy = property(**gridEy())
def gridEz():
doc = "Edge staggered grid in the z direction."
def fget(self):
if self._gridEz is None and self.dim == 3:
N = self.r(self.gridN, 'N', 'N', 'M')
XYZ = [Utils.mkvc(0.5 * (n[:, :, :-1] + n[:, :, 1:])) for n in N]
self._gridEz = np.c_[XYZ[0], XYZ[1], XYZ[2]]
return self._gridEz
return locals()
_gridEz = None # Store grid by default
gridEz = property(**gridEz())
# --------------- Geometries --------------------- # --------------- Geometries ---------------------
# #
@@ -204,94 +194,78 @@ class CurvilinearMesh(BaseRectangularMesh, DiffOperators, InnerProducts,
# | / | / # | / | /
# D -------------- C # D -------------- C
# node(i+1,j,k) node(i+1,j+1,k) # node(i+1,j,k) node(i+1,j+1,k)
def vol():
doc = "Construct cell volumes of the 3D model as 1d array."
@property def fget(self):
def vol(self): if(self._vol is None):
""" if self.dim == 2:
Construct cell volumes of the 3D model as 1d array A, B, C, D = Utils.indexCube('ABCD', self.vnC+1)
""" normal, area = Utils.faceInfo(np.c_[self.gridN, np.zeros((self.nN, 1))], A, B, C, D)
self._vol = area
elif self.dim == 3:
# Each polyhedron can be decomposed into 5 tetrahedrons
# However, this presents a choice so we may as well divide in two ways and average.
A, B, C, D, E, F, G, H = Utils.indexCube('ABCDEFGH', self.vnC+1)
if getattr(self, '_vol', None) is None: vol1 = (Utils.volTetra(self.gridN, A, B, D, E) + # cutted edge top
if self.dim == 2: Utils.volTetra(self.gridN, B, E, F, G) + # cutted edge top
A, B, C, D = Utils.indexCube('ABCD', self.vnC+1) Utils.volTetra(self.gridN, B, D, E, G) + # middle
normal, area = Utils.faceInfo(np.c_[self.gridN, np.zeros( Utils.volTetra(self.gridN, B, C, D, G) + # cutted edge bottom
(self.nN, 1))], A, B, C, D) Utils.volTetra(self.gridN, D, E, G, H)) # cutted edge bottom
self._vol = area
elif self.dim == 3:
# Each polyhedron can be decomposed into 5 tetrahedrons
# However, this presents a choice so we may as well divide in
# two ways and average.
A, B, C, D, E, F, G, H = Utils.indexCube('ABCDEFGH', self.vnC +
1)
vol1 = (Utils.volTetra(self.gridN, A, B, D, E) + # cutted edge top vol2 = (Utils.volTetra(self.gridN, A, F, B, C) + # cutted edge top
Utils.volTetra(self.gridN, B, E, F, G) + # cutted edge top Utils.volTetra(self.gridN, A, E, F, H) + # cutted edge top
Utils.volTetra(self.gridN, B, D, E, G) + # middle Utils.volTetra(self.gridN, A, H, F, C) + # middle
Utils.volTetra(self.gridN, B, C, D, G) + # cutted edge bottom Utils.volTetra(self.gridN, C, H, D, A) + # cutted edge bottom
Utils.volTetra(self.gridN, D, E, G, H)) # cutted edge bottom Utils.volTetra(self.gridN, C, G, H, F)) # cutted edge bottom
vol2 = (Utils.volTetra(self.gridN, A, F, B, C) + # cutted edge top self._vol = (vol1 + vol2)/2
Utils.volTetra(self.gridN, A, E, F, H) + # cutted edge top return self._vol
Utils.volTetra(self.gridN, A, H, F, C) + # middle return locals()
Utils.volTetra(self.gridN, C, H, D, A) + # cutted edge bottom _vol = None
Utils.volTetra(self.gridN, C, G, H, F)) # cutted edge bottom vol = property(**vol())
self._vol = (vol1 + vol2)/2 def area():
return self._vol doc = "Face areas."
@property def fget(self):
def area(self): if(self._area is None or self._normals is None):
if (getattr(self, '_area', None) is None or # Compute areas of cell faces
getattr(self, '_normals', None) is None): if(self.dim == 2):
# Compute areas of cell faces xy = self.gridN
if(self.dim == 2): A, B = Utils.indexCube('AB', self.vnC+1, np.array([self.nNx, self.nCy]))
xy = self.gridN edge1 = xy[B, :] - xy[A, :]
A, B = Utils.indexCube('AB', self.vnC+1, np.array([self.nNx, normal1 = np.c_[edge1[:, 1], -edge1[:, 0]]
self.nCy])) area1 = length2D(edge1)
edge1 = xy[B, :] - xy[A, :] A, D = Utils.indexCube('AD', self.vnC+1, np.array([self.nCx, self.nNy]))
normal1 = np.c_[edge1[:, 1], -edge1[:, 0]] # Note that we are doing A-D to make sure the normal points the right way.
area1 = length2D(edge1) # Think about it. Look at the picture. Normal points towards C iff you do this.
A, D = Utils.indexCube('AD', self.vnC+1, np.array([self.nCx, edge2 = xy[A, :] - xy[D, :]
self.nNy])) normal2 = np.c_[edge2[:, 1], -edge2[:, 0]]
# Note that we are doing A-D to make sure the normal points the area2 = length2D(edge2)
# right way. self._area = np.r_[Utils.mkvc(area1), Utils.mkvc(area2)]
# Think about it. Look at the picture. Normal points towards C self._normals = [normalize2D(normal1), normalize2D(normal2)]
# iff you do this. elif(self.dim == 3):
edge2 = xy[A, :] - xy[D, :]
normal2 = np.c_[edge2[:, 1], -edge2[:, 0]]
area2 = length2D(edge2)
self._area = np.r_[Utils.mkvc(area1), Utils.mkvc(area2)]
self._normals = [normalize2D(normal1), normalize2D(normal2)]
elif(self.dim == 3): A, E, F, B = Utils.indexCube('AEFB', self.vnC+1, np.array([self.nNx, self.nCy, self.nCz]))
normal1, area1 = Utils.faceInfo(self.gridN, A, E, F, B, average=False, normalizeNormals=False)
A, E, F, B = Utils.indexCube('AEFB', self.vnC+1, np.array( A, D, H, E = Utils.indexCube('ADHE', self.vnC+1, np.array([self.nCx, self.nNy, self.nCz]))
[self.nNx, self.nCy, self.nCz])) normal2, area2 = Utils.faceInfo(self.gridN, A, D, H, E, average=False, normalizeNormals=False)
normal1, area1 = Utils.faceInfo(self.gridN, A, E, F, B,
average=False,
normalizeNormals=False)
A, D, H, E = Utils.indexCube('ADHE', self.vnC+1, np.array( A, B, C, D = Utils.indexCube('ABCD', self.vnC+1, np.array([self.nCx, self.nCy, self.nNz]))
[self.nCx, self.nNy, self.nCz])) normal3, area3 = Utils.faceInfo(self.gridN, A, B, C, D, average=False, normalizeNormals=False)
normal2, area2 = Utils.faceInfo(self.gridN, A, D, H, E,
average=False,
normalizeNormals=False)
A, B, C, D = Utils.indexCube('ABCD', self.vnC+1, np.array( self._area = np.r_[Utils.mkvc(area1), Utils.mkvc(area2), Utils.mkvc(area3)]
[self.nCx, self.nCy, self.nNz])) self._normals = [normal1, normal2, normal3]
normal3, area3 = Utils.faceInfo(self.gridN, A, B, C, D, return self._area
average=False, return locals()
normalizeNormals=False) _area = None
area = property(**area())
self._area = np.r_[Utils.mkvc(area1), Utils.mkvc(area2), def normals():
Utils.mkvc(area3)] doc = """Face normals: calling this will average
self._normals = [normal1, normal2, normal3]
return self._area
@property
def normals(self):
"""
Face normals: calling this will average
the computed normals so that there is one the computed normals so that there is one
per face. This is especially relevant in per face. This is especially relevant in
3D, as there are up to 4 different normals 3D, as there are up to 4 different normals
@@ -302,64 +276,58 @@ class CurvilinearMesh(BaseRectangularMesh, DiffOperators, InnerProducts,
NyX, NyY, NyZ = M.r(M.normals, 'F', 'Fy', 'M') NyX, NyY, NyZ = M.r(M.normals, 'F', 'Fy', 'M')
""" """
if getattr(self, '_normals', None) is None: def fget(self):
self.area # calling .area will create the face normals if(self._normals is None):
if self.dim == 2: self.area # calling .area will create the face normals
return normalize2D(np.r_[self._normals[0], self._normals[1]]) if self.dim == 2:
elif self.dim == 3: return normalize2D(np.r_[self._normals[0], self._normals[1]])
normal1 = (self._normals[0][0] + self._normals[0][1] + self._normals[0][2] + self._normals[0][3])/4 elif self.dim == 3:
normal2 = (self._normals[1][0] + self._normals[1][1] + self._normals[1][2] + self._normals[1][3])/4 normal1 = (self._normals[0][0] + self._normals[0][1] + self._normals[0][2] + self._normals[0][3])/4
normal3 = (self._normals[2][0] + self._normals[2][1] + self._normals[2][2] + self._normals[2][3])/4 normal2 = (self._normals[1][0] + self._normals[1][1] + self._normals[1][2] + self._normals[1][3])/4
return normalize3D(np.r_[normal1, normal2, normal3]) normal3 = (self._normals[2][0] + self._normals[2][1] + self._normals[2][2] + self._normals[2][3])/4
return normalize3D(np.r_[normal1, normal2, normal3])
return locals()
_normals = None
normals = property(**normals())
@property def edge():
def edge(self): doc = "Edge legnths."
"""
Edge lengths def fget(self):
""" if(self._edge is None or self._tangents is None):
if getattr(self, '_edge', None) is None: if(self.dim == 2):
if(self.dim == 2): xy = self.gridN
xy = self.gridN A, D = Utils.indexCube('AD', self.vnC+1, np.array([self.nCx, self.nNy]))
A, D = Utils.indexCube('AD', self.vnC+1, np.array([self.nCx, edge1 = xy[D, :] - xy[A, :]
self.nNy])) A, B = Utils.indexCube('AB', self.vnC+1, np.array([self.nNx, self.nCy]))
edge1 = xy[D, :] - xy[A, :] edge2 = xy[B, :] - xy[A, :]
A, B = Utils.indexCube('AB', self.vnC+1, np.array([self.nNx, self._edge = np.r_[Utils.mkvc(length2D(edge1)), Utils.mkvc(length2D(edge2))]
self.nCy])) self._tangents = np.r_[edge1, edge2]/np.c_[self._edge, self._edge]
edge2 = xy[B, :] - xy[A, :] elif(self.dim == 3):
self._edge = np.r_[Utils.mkvc(length2D(edge1)), xyz = self.gridN
Utils.mkvc(length2D(edge2))] A, D = Utils.indexCube('AD', self.vnC+1, np.array([self.nCx, self.nNy, self.nNz]))
self._tangents = np.r_[edge1, edge2]/np.c_[self._edge, edge1 = xyz[D, :] - xyz[A, :]
self._edge] A, B = Utils.indexCube('AB', self.vnC+1, np.array([self.nNx, self.nCy, self.nNz]))
elif(self.dim == 3): edge2 = xyz[B, :] - xyz[A, :]
xyz = self.gridN A, E = Utils.indexCube('AE', self.vnC+1, np.array([self.nNx, self.nNy, self.nCz]))
A, D = Utils.indexCube('AD', self.vnC+1, np.array([self.nCx, edge3 = xyz[E, :] - xyz[A, :]
self.nNy, self._edge = np.r_[Utils.mkvc(length3D(edge1)), Utils.mkvc(length3D(edge2)), Utils.mkvc(length3D(edge3))]
self.nNz])) self._tangents = np.r_[edge1, edge2, edge3]/np.c_[self._edge, self._edge, self._edge]
edge1 = xyz[D, :] - xyz[A, :]
A, B = Utils.indexCube('AB', self.vnC+1, np.array([self.nNx,
self.nCy,
self.nNz]))
edge2 = xyz[B, :] - xyz[A, :]
A, E = Utils.indexCube('AE', self.vnC+1, np.array([self.nNx,
self.nNy,
self.nCz]))
edge3 = xyz[E, :] - xyz[A, :]
self._edge = np.r_[Utils.mkvc(length3D(edge1)),
Utils.mkvc(length3D(edge2)),
Utils.mkvc(length3D(edge3))]
self._tangents = (np.r_[edge1, edge2, edge3] /
np.c_[self._edge, self._edge, self._edge])
return self._edge return self._edge
return self._edge return locals()
_edge = None
edge = property(**edge())
@property def tangents():
def tangents(self): doc = "Edge tangents."
"""
Edge tangents def fget(self):
""" if(self._tangents is None):
if getattr(self, '_tangents', None) is None: self.edge # calling .edge will create the tangents
self.edge # calling .edge will create the tangents return self._tangents
return self._tangents return locals()
_tangents = None
tangents = property(**tangents())
+6 -6
View File
@@ -68,8 +68,8 @@ class CylMesh(BaseTensorMesh, BaseRectangularMesh, InnerProducts, CylView):
""" """
Number of x-faces in each direction Number of x-faces in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnFx, (dim, ) :return: vnFx
""" """
return self.vnC return self.vnC
@@ -78,8 +78,8 @@ class CylMesh(BaseTensorMesh, BaseRectangularMesh, InnerProducts, CylView):
""" """
Number of y-edges in each direction Number of y-edges in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnEy or None if dim < 2, (dim, ) :return: vnEy or None if dim < 2
""" """
nNx = self.nNx if self.isSymmetric else self.nNx - 1 nNx = self.nNx if self.isSymmetric else self.nNx - 1
return np.r_[nNx, self.nCy, self.nNz] return np.r_[nNx, self.nCy, self.nNz]
@@ -89,8 +89,8 @@ class CylMesh(BaseTensorMesh, BaseRectangularMesh, InnerProducts, CylView):
""" """
Number of z-edges in each direction Number of z-edges in each direction
:rtype: numpy.array :rtype: numpy.array (dim, )
:return: vnEz or None if nCy > 1, (dim, ) :return: vnEz or None if nCy > 1
""" """
if self.isSymmetric: if self.isSymmetric:
return np.r_[self.nNx, self.nNy, self.nCz] return np.r_[self.nNx, self.nNy, self.nCz]
+331 -394
View File
@@ -18,15 +18,13 @@ def checkBC(bc):
for bc_i in bc: for bc_i in bc:
assert type(bc_i) is str, "each bc must be a string" assert type(bc_i) is str, "each bc must be a string"
assert bc_i in ['dirichlet', 'neumann'], ("each bc must be either," assert bc_i in ['dirichlet', 'neumann'], "each bc must be either, 'dirichlet' or 'neumann'"
"'dirichlet' or 'neumann'")
return bc return bc
def ddxCellGrad(n, bc): def ddxCellGrad(n, bc):
""" """
Create 1D derivative operator from cell-centers to nodes this means we Create 1D derivative operator from cell-centers to nodes this means we go from n to n+1
go from n to n+1
For Cell-Centered **Dirichlet**, use a ghost point:: For Cell-Centered **Dirichlet**, use a ghost point::
@@ -54,8 +52,7 @@ def ddxCellGrad(n, bc):
""" """
bc = checkBC(bc) bc = checkBC(bc)
D = sp.spdiags((np.ones((n+1, 1))*[-1, 1]).T, [-1, 0], n+1, n, D = sp.spdiags((np.ones((n+1, 1))*[-1, 1]).T, [-1, 0], n+1, n, format="csr")
format="csr")
# Set the first side # Set the first side
if(bc[0] == 'dirichlet'): if(bc[0] == 'dirichlet'):
D[0, 0] = 2 D[0, 0] = 2
@@ -68,11 +65,10 @@ def ddxCellGrad(n, bc):
D[-1, -1] = 0 D[-1, -1] = 0
return D return D
def ddxCellGradBC(n, bc): def ddxCellGradBC(n, bc):
""" """
Create 1D derivative operator from cell-centers to nodes this means we
go from n to n+1 Create 1D derivative operator from cell-centers to nodes this means we go from n to n+1
For Cell-Centered **Dirichlet**, use a ghost point:: For Cell-Centered **Dirichlet**, use a ghost point::
@@ -103,7 +99,7 @@ def ddxCellGradBC(n, bc):
""" """
bc = checkBC(bc) bc = checkBC(bc)
ij = (np.array([0, n]), np.array([0, 1])) ij = (np.array([0, n]),np.array([0, 1]))
vals = np.zeros(2) vals = np.zeros(2)
# Set the first side # Set the first side
@@ -116,7 +112,7 @@ def ddxCellGradBC(n, bc):
vals[1] = 2 vals[1] = 2
elif(bc[1] == 'neumann'): elif(bc[1] == 'neumann'):
vals[1] = 0 vals[1] = 0
D = sp.csr_matrix((vals, ij), shape=(n+1, 2)) D = sp.csr_matrix((vals, ij), shape=(n+1,2))
return D return D
@@ -125,166 +121,175 @@ class DiffOperators(object):
Class creates the differential operators that you need! Class creates the differential operators that you need!
""" """
def __init__(self): def __init__(self):
raise Exception('DiffOperators is a base class providing differential' raise Exception('DiffOperators is a base class providing differential operators on meshes and cannot run on its own. Inherit to your favorite Mesh class.')
'operators on meshes and cannot run on its own.'
'Inherit to your favorite Mesh class.')
@property def faceDiv():
def faceDiv(self): doc = "Construct divergence operator (face-stg to cell-centres)."
"""
Construct divergence operator (face-stg to cell-centres). def fget(self):
""" if(self._faceDiv is None):
if getattr(self, '_faceDiv', None) is None: # The number of cell centers in each direction
n = self.vnC n = self.vnC
# Compute faceDivergence operator on faces # Compute faceDivergence operator on faces
if(self.dim == 1): if(self.dim == 1):
D = ddx(n[0]) D = ddx(n[0])
elif(self.dim == 2): elif(self.dim == 2):
D1 = sp.kron(speye(n[1]), ddx(n[0])) D1 = sp.kron(speye(n[1]), ddx(n[0]))
D2 = sp.kron(ddx(n[1]), speye(n[0])) D2 = sp.kron(ddx(n[1]), speye(n[0]))
D = sp.hstack((D1, D2), format="csr") D = sp.hstack((D1, D2), format="csr")
elif(self.dim == 3): elif(self.dim == 3):
D1 = kron3(speye(n[2]), speye(n[1]), ddx(n[0])) D1 = kron3(speye(n[2]), speye(n[1]), ddx(n[0]))
D2 = kron3(speye(n[2]), ddx(n[1]), speye(n[0])) D2 = kron3(speye(n[2]), ddx(n[1]), speye(n[0]))
D3 = kron3(ddx(n[2]), speye(n[1]), speye(n[0]))
D = sp.hstack((D1, D2, D3), format="csr")
# Compute areas of cell faces & volumes
S = self.area
V = self.vol
self._faceDiv = sdiag(1/V)*D*sdiag(S)
return self._faceDiv
return locals()
_faceDiv = None
faceDiv = property(**faceDiv())
def faceDivx():
doc = "Construct divergence operator in the x component (face-stg to cell-centres)."
def fget(self):
if(self._faceDivx is None):
# The number of cell centers in each direction
n = self.vnC
# Compute faceDivergence operator on faces
if(self.dim == 1):
D1 = ddx(n[0])
elif(self.dim == 2):
D1 = sp.kron(speye(n[1]), ddx(n[0]))
elif(self.dim == 3):
D1 = kron3(speye(n[2]), speye(n[1]), ddx(n[0]))
# Compute areas of cell faces & volumes
S = self.r(self.area, 'F', 'Fx', 'V')
V = self.vol
self._faceDivx = sdiag(1/V)*D1*sdiag(S)
return self._faceDivx
return locals()
_faceDivx = None
faceDivx = property(**faceDivx())
def faceDivy():
doc = "Construct divergence operator in the y component (face-stg to cell-centres)."
def fget(self):
if(self.dim < 2): return None
if(self._faceDivy is None):
# The number of cell centers in each direction
n = self.vnC
# Compute faceDivergence operator on faces
if(self.dim == 2):
D2 = sp.kron(ddx(n[1]), speye(n[0]))
elif(self.dim == 3):
D2 = kron3(speye(n[2]), ddx(n[1]), speye(n[0]))
# Compute areas of cell faces & volumes
S = self.r(self.area, 'F', 'Fy', 'V')
V = self.vol
self._faceDivy = sdiag(1/V)*D2*sdiag(S)
return self._faceDivy
return locals()
_faceDivy = None
faceDivy = property(**faceDivy())
def faceDivz():
doc = "Construct divergence operator in the z component (face-stg to cell-centres)."
def fget(self):
if(self.dim < 3): return None
if(self._faceDivz is None):
# The number of cell centers in each direction
n = self.vnC
# Compute faceDivergence operator on faces
D3 = kron3(ddx(n[2]), speye(n[1]), speye(n[0])) D3 = kron3(ddx(n[2]), speye(n[1]), speye(n[0]))
D = sp.hstack((D1, D2, D3), format="csr") # Compute areas of cell faces & volumes
# Compute areas of cell faces & volumes S = self.r(self.area, 'F', 'Fz', 'V')
S = self.area V = self.vol
V = self.vol self._faceDivz = sdiag(1/V)*D3*sdiag(S)
self._faceDiv = sdiag(1/V)*D*sdiag(S)
return self._faceDiv
@property return self._faceDivz
def faceDivx(self): return locals()
""" _faceDivz = None
Construct divergence operator in the x component (face-stg to faceDivz = property(**faceDivz())
cell-centres).
"""
if getattr(self, '_faceDivx', None) is None:
# The number of cell centers in each direction
n = self.vnC
# Compute faceDivergence operator on faces
if(self.dim == 1):
D1 = ddx(n[0])
elif(self.dim == 2):
D1 = sp.kron(speye(n[1]), ddx(n[0]))
elif(self.dim == 3):
D1 = kron3(speye(n[2]), speye(n[1]), ddx(n[0]))
# Compute areas of cell faces & volumes
S = self.r(self.area, 'F', 'Fx', 'V')
V = self.vol
self._faceDivx = sdiag(1/V)*D1*sdiag(S)
return self._faceDivx def nodalGrad():
doc = "Construct gradient operator (nodes to edges)."
@property def fget(self):
def faceDivy(self): if(self._nodalGrad is None):
if(self.dim < 2): # The number of cell centers in each direction
return None n = self.vnC
if getattr(self, '_faceDivy', None) is None: # Compute divergence operator on faces
# The number of cell centers in each direction if(self.dim == 1):
n = self.vnC G = ddx(n[0])
# Compute faceDivergence operator on faces elif(self.dim == 2):
if(self.dim == 2): D1 = sp.kron(speye(n[1]+1), ddx(n[0]))
D2 = sp.kron(ddx(n[1]), speye(n[0])) D2 = sp.kron(ddx(n[1]), speye(n[0]+1))
elif(self.dim == 3): G = sp.vstack((D1, D2), format="csr")
D2 = kron3(speye(n[2]), ddx(n[1]), speye(n[0])) elif(self.dim == 3):
# Compute areas of cell faces & volumes D1 = kron3(speye(n[2]+1), speye(n[1]+1), ddx(n[0]))
S = self.r(self.area, 'F', 'Fy', 'V') D2 = kron3(speye(n[2]+1), ddx(n[1]), speye(n[0]+1))
V = self.vol D3 = kron3(ddx(n[2]), speye(n[1]+1), speye(n[0]+1))
self._faceDivy = sdiag(1/V)*D2*sdiag(S) G = sp.vstack((D1, D2, D3), format="csr")
return self._faceDivy # Compute lengths of cell edges
L = self.edge
self._nodalGrad = sdiag(1/L)*G
return self._nodalGrad
return locals()
_nodalGrad = None
nodalGrad = property(**nodalGrad())
@property def nodalLaplacian():
def faceDivz(self): doc = "Construct laplacian operator (nodes to edges)."
"""
Construct divergence operator in the z component (face-stg to
cell-centres).
"""
if(self.dim < 3):
return None
if getattr(self, '_faceDivz', None) is None:
# The number of cell centers in each direction
n = self.vnC
# Compute faceDivergence operator on faces
D3 = kron3(ddx(n[2]), speye(n[1]), speye(n[0]))
# Compute areas of cell faces & volumes
S = self.r(self.area, 'F', 'Fz', 'V')
V = self.vol
self._faceDivz = sdiag(1/V)*D3*sdiag(S)
return self._faceDivz
@property def fget(self):
def nodalGrad(self): if(self._nodalLaplacian is None):
""" print 'Warning: Laplacian has not been tested rigorously.'
Construct gradient operator (nodes to edges). # The number of cell centers in each direction
""" n = self.vnC
if getattr(self, '_nodalGrad', None) is None: # Compute divergence operator on faces
# The number of cell centers in each direction if(self.dim == 1):
n = self.vnC D1 = sdiag(1./self.hx) * ddx(mesh.nCx)
# Compute divergence operator on faces L = - D1.T*D1
if(self.dim == 1): elif(self.dim == 2):
G = ddx(n[0]) D1 = sdiag(1./self.hx) * ddx(n[0])
elif(self.dim == 2): D2 = sdiag(1./self.hy) * ddx(n[1])
D1 = sp.kron(speye(n[1]+1), ddx(n[0])) L1 = sp.kron(speye(n[1]+1), - D1.T * D1)
D2 = sp.kron(ddx(n[1]), speye(n[0]+1)) L2 = sp.kron(- D2.T * D2, speye(n[0]+1))
G = sp.vstack((D1, D2), format="csr") L = L1 + L2
elif(self.dim == 3): elif(self.dim == 3):
D1 = kron3(speye(n[2]+1), speye(n[1]+1), ddx(n[0])) D1 = sdiag(1./self.hx) * ddx(n[0])
D2 = kron3(speye(n[2]+1), ddx(n[1]), speye(n[0]+1)) D2 = sdiag(1./self.hy) * ddx(n[1])
D3 = kron3(ddx(n[2]), speye(n[1]+1), speye(n[0]+1)) D3 = sdiag(1./self.hz) * ddx(n[2])
G = sp.vstack((D1, D2, D3), format="csr") L1 = kron3(speye(n[2]+1), speye(n[1]+1), - D1.T * D1)
# Compute lengths of cell edges L2 = kron3(speye(n[2]+1), - D2.T * D2, speye(n[0]+1))
L = self.edge L3 = kron3(- D3.T * D3, speye(n[1]+1), speye(n[0]+1))
self._nodalGrad = sdiag(1/L)*G L = L1 + L2 + L3
return self._nodalGrad self._nodalLaplacian = L
return self._nodalLaplacian
@property return locals()
def nodalLaplacian(self): _nodalLaplacian = None
""" nodalLaplacian = property(**nodalLaplacian())
Construct laplacian operator (nodes to edges).
"""
if getattr(self, '_nodalLaplacian', None) is None:
print 'Warning: Laplacian has not been tested rigorously.'
# The number of cell centers in each direction
n = self.vnC
# Compute divergence operator on faces
if(self.dim == 1):
D1 = sdiag(1./self.hx) * ddx(mesh.nCx)
L = - D1.T*D1
elif(self.dim == 2):
D1 = sdiag(1./self.hx) * ddx(n[0])
D2 = sdiag(1./self.hy) * ddx(n[1])
L1 = sp.kron(speye(n[1]+1), - D1.T * D1)
L2 = sp.kron(- D2.T * D2, speye(n[0]+1))
L = L1 + L2
elif(self.dim == 3):
D1 = sdiag(1./self.hx) * ddx(n[0])
D2 = sdiag(1./self.hy) * ddx(n[1])
D3 = sdiag(1./self.hz) * ddx(n[2])
L1 = kron3(speye(n[2]+1), speye(n[1]+1), - D1.T * D1)
L2 = kron3(speye(n[2]+1), - D2.T * D2, speye(n[0]+1))
L3 = kron3(- D3.T * D3, speye(n[1]+1), speye(n[0]+1))
L = L1 + L2 + L3
self._nodalLaplacian = L
return self._nodalLaplacian
def setCellGradBC(self, BC): def setCellGradBC(self, BC):
""" """
Function that sets the boundary conditions for cell-centred derivative Function that sets the boundary conditions for cell-centred derivative operators.
operators.
Examples:: Examples::
# Neumann in all directions
BC = 'neumann'
# 3D, Dirichlet in y Neumann else BC = 'neumann' # Neumann in all directions
BC = ['neumann', 'dirichlet', 'neumann'] BC = ['neumann', 'dirichlet', 'neumann'] # 3D, Dirichlet in y Neumann else
BC = [['neumann', 'dirichlet'], 'dirichlet', 'dirichlet'] # 3D, Neumann in x on bottom of domain,
# Dirichlet else
# 3D, Neumann in x on bottom of domain, Dirichlet else
BC = [['neumann', 'dirichlet'], 'dirichlet', 'dirichlet']
""" """
if(type(BC) is str): if(type(BC) is str):
BC = [BC]*self.dim BC = [BC]*self.dim
if(type(BC) is list): if(type(BC) is list):
@@ -318,69 +323,47 @@ class DiffOperators(object):
G = sp.vstack((G1, G2, G3), format="csr") G = sp.vstack((G1, G2, G3), format="csr")
return G return G
@property def cellGrad():
def cellGrad(self): doc = "The cell centered Gradient, takes you to cell faces."
"""
The cell centered Gradient, takes you to cell faces.
"""
if getattr(self, '_cellGrad', None) is None:
G = self._cellGradStencil()
S = self.area # Compute areas of cell faces & volumes
V = self.aveCC2F*self.vol # Average volume between adjacent cells
self._cellGrad = sdiag(S/V)*G
return self._cellGrad
@property def fget(self):
def cellGradBC(self): if(self._cellGrad is None):
""" G = self._cellGradStencil()
The cell centered Gradient boundary condition matrix # Compute areas of cell faces & volumes
""" S = self.area
if getattr(self, '_cellGradBC', None) is None: V = self.aveCC2F*self.vol # Average volume between adjacent cells
BC = self.setCellGradBC(self._cellGradBC_list) self._cellGrad = sdiag(S/V)*G
n = self.vnC return self._cellGrad
if(self.dim == 1): return locals()
G = ddxCellGradBC(n[0], BC[0]) _cellGrad = None
elif(self.dim == 2): cellGrad = property(**cellGrad())
G1 = sp.kron(speye(n[1]), ddxCellGradBC(n[0], BC[0]))
G2 = sp.kron(ddxCellGradBC(n[1], BC[1]), speye(n[0]))
G = sp.block_diag((G1, G2), format="csr")
elif(self.dim == 3):
G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGradBC(n[0], BC[0]))
G2 = kron3(speye(n[2]), ddxCellGradBC(n[1], BC[1]), speye(n[0]))
G3 = kron3(ddxCellGradBC(n[2], BC[2]), speye(n[1]), speye(n[0]))
G = sp.block_diag((G1, G2, G3), format="csr")
# Compute areas of cell faces & volumes
S = self.area
V = self.aveCC2F*self.vol # Average volume between adjacent cells
self._cellGradBC = sdiag(S/V)*G
return self._cellGradBC
# def cellGradBC(): def cellGradBC():
# doc = "The cell centered Gradient boundary condition matrix" doc = "The cell centered Gradient boundary condition matrix"
# def fget(self): def fget(self):
# if(self._cellGradBC is None): if(self._cellGradBC is None):
# BC = self.setCellGradBC(self._cellGradBC_list) BC = self.setCellGradBC(self._cellGradBC_list)
# n = self.vnC n = self.vnC
# if(self.dim == 1): if(self.dim == 1):
# G = ddxCellGradBC(n[0], BC[0]) G = ddxCellGradBC(n[0], BC[0])
# elif(self.dim == 2): elif(self.dim == 2):
# G1 = sp.kron(speye(n[1]), ddxCellGradBC(n[0], BC[0])) G1 = sp.kron(speye(n[1]), ddxCellGradBC(n[0], BC[0]))
# G2 = sp.kron(ddxCellGradBC(n[1], BC[1]), speye(n[0])) G2 = sp.kron(ddxCellGradBC(n[1], BC[1]), speye(n[0]))
# G = sp.block_diag((G1, G2), format="csr") G = sp.block_diag((G1, G2), format="csr")
# elif(self.dim == 3): elif(self.dim == 3):
# G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGradBC(n[0], BC[0])) G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGradBC(n[0], BC[0]))
# G2 = kron3(speye(n[2]), ddxCellGradBC(n[1], BC[1]), speye(n[0])) G2 = kron3(speye(n[2]), ddxCellGradBC(n[1], BC[1]), speye(n[0]))
# G3 = kron3(ddxCellGradBC(n[2], BC[2]), speye(n[1]), speye(n[0])) G3 = kron3(ddxCellGradBC(n[2], BC[2]), speye(n[1]), speye(n[0]))
# G = sp.block_diag((G1, G2, G3), format="csr") G = sp.block_diag((G1, G2, G3), format="csr")
# # Compute areas of cell faces & volumes # Compute areas of cell faces & volumes
# S = self.area S = self.area
# V = self.aveCC2F*self.vol # Average volume between adjacent cells V = self.aveCC2F*self.vol # Average volume between adjacent cells
# self._cellGradBC = sdiag(S/V)*G self._cellGradBC = sdiag(S/V)*G
# return self._cellGradBC return self._cellGradBC
# return locals() return locals()
# _cellGradBC = None _cellGradBC = None
# cellGradBC = property(**cellGradBC()) cellGradBC = property(**cellGradBC())
def _cellGradxStencil(self): def _cellGradxStencil(self):
BC = ['neumann', 'neumann'] BC = ['neumann', 'neumann']
@@ -393,19 +376,20 @@ class DiffOperators(object):
G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC)) G1 = kron3(speye(n[2]), speye(n[1]), ddxCellGrad(n[0], BC))
return G1 return G1
@property
def cellGradx(self): def cellGradx():
""" doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions."
Cell centered Gradient in the x dimension. Has neumann boundary
conditions. def fget(self):
""" if getattr(self, '_cellGradx', None) is None:
if getattr(self, '_cellGradx', None) is None: G1 = self._cellGradxStencil()
G1 = self._cellGradxStencil() # Compute areas of cell faces & volumes
# Compute areas of cell faces & volumes V = self.aveCC2F*self.vol
V = self.aveCC2F*self.vol L = self.r(self.area/V, 'F','Fx', 'V')
L = self.r(self.area/V, 'F','Fx', 'V') self._cellGradx = sdiag(L)*G1
self._cellGradx = sdiag(L)*G1 return self._cellGradx
return self._cellGradx return locals()
cellGradx = property(**cellGradx())
def _cellGradyStencil(self): def _cellGradyStencil(self):
if self.dim < 2: return None if self.dim < 2: return None
@@ -417,17 +401,19 @@ class DiffOperators(object):
G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC), speye(n[0])) G2 = kron3(speye(n[2]), ddxCellGrad(n[1], BC), speye(n[0]))
return G2 return G2
@property def cellGrady():
def cellGrady(self): doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions."
if self.dim < 2: def fget(self):
return None if self.dim < 2: return None
if getattr(self, '_cellGrady', None) is None: if getattr(self, '_cellGrady', None) is None:
G2 = self._cellGradyStencil() G2 = self._cellGradyStencil()
# Compute areas of cell faces & volumes # Compute areas of cell faces & volumes
V = self.aveCC2F*self.vol V = self.aveCC2F*self.vol
L = self.r(self.area/V, 'F', 'Fy', 'V') L = self.r(self.area/V, 'F','Fy', 'V')
self._cellGrady = sdiag(L)*G2 self._cellGrady = sdiag(L)*G2
return self._cellGrady return self._cellGrady
return locals()
cellGrady = property(**cellGrady())
def _cellGradzStencil(self): def _cellGradzStencil(self):
if self.dim < 3: return None if self.dim < 3: return None
@@ -436,61 +422,66 @@ class DiffOperators(object):
G3 = kron3(ddxCellGrad(n[2], BC), speye(n[1]), speye(n[0])) G3 = kron3(ddxCellGrad(n[2], BC), speye(n[1]), speye(n[0]))
return G3 return G3
@property def cellGradz():
def cellGradz(self): doc = "Cell centered Gradient in the x dimension. Has neumann boundary conditions."
""" def fget(self):
Cell centered Gradient in the x dimension. Has neumann boundary if self.dim < 3: return None
conditions. if getattr(self, '_cellGradz', None) is None:
""" G3 = self._cellGradzStencil()
if self.dim < 3: # Compute areas of cell faces & volumes
return None V = self.aveCC2F*self.vol
if getattr(self, '_cellGradz', None) is None: L = self.r(self.area/V, 'F','Fz', 'V')
G3 = self._cellGradzStencil() self._cellGradz = sdiag(L)*G3
# Compute areas of cell faces & volumes return self._cellGradz
V = self.aveCC2F*self.vol return locals()
L = self.r(self.area/V, 'F', 'Fz', 'V') cellGradz = property(**cellGradz())
self._cellGradz = sdiag(L)*G3
return self._cellGradz
@property def edgeCurl():
def edgeCurl(self): doc = "Construct the 3D curl operator."
"""
Construct the 3D curl operator.
"""
if getattr(self, '_edgeCurl', None) is None:
assert self.dim > 1, "Edge Curl only programed for 2 or 3D."
n = self.vnC # The number of cell centers in each direction def fget(self):
L = self.edge # Compute lengths of cell edges if(self._edgeCurl is None):
S = self.area # Compute areas of cell faces assert self.dim > 1, "Edge Curl only programed for 2 or 3D."
# The number of cell centers in each direction
n = self.vnC
# Compute divergence operator on faces # Compute lengths of cell edges
if self.dim == 2: L = self.edge
D21 = sp.kron(ddx(n[1]), speye(n[0])) # Compute areas of cell faces
D12 = sp.kron(speye(n[1]), ddx(n[0])) S = self.area
C = sp.hstack((-D21, D12), format="csr")
self._edgeCurl = C*sdiag(1/S)
elif self.dim == 3: # Compute divergence operator on faces
if self.dim == 2:
D32 = kron3(ddx(n[2]), speye(n[1]), speye(n[0]+1)) D21 = sp.kron(ddx(n[1]), speye(n[0]))
D23 = kron3(speye(n[2]), ddx(n[1]), speye(n[0]+1)) D12 = sp.kron(speye(n[1]), ddx(n[0]))
D31 = kron3(ddx(n[2]), speye(n[1]+1), speye(n[0])) C = sp.hstack((-D21, D12), format="csr")
D13 = kron3(speye(n[2]), speye(n[1]+1), ddx(n[0])) self._edgeCurl = C*sdiag(1/S)
D21 = kron3(speye(n[2]+1), ddx(n[1]), speye(n[0]))
D12 = kron3(speye(n[2]+1), speye(n[1]), ddx(n[0]))
O1 = spzeros(np.shape(D32)[0], np.shape(D31)[1]) elif self.dim == 3:
O2 = spzeros(np.shape(D31)[0], np.shape(D32)[1])
O3 = spzeros(np.shape(D21)[0], np.shape(D13)[1])
C = sp.vstack((sp.hstack((O1, -D32, D23)), D32 = kron3(ddx(n[2]), speye(n[1]), speye(n[0]+1))
sp.hstack((D31, O2, -D13)), D23 = kron3(speye(n[2]), ddx(n[1]), speye(n[0]+1))
sp.hstack((-D21, D12, O3))), format="csr") D31 = kron3(ddx(n[2]), speye(n[1]+1), speye(n[0]))
D13 = kron3(speye(n[2]), speye(n[1]+1), ddx(n[0]))
D21 = kron3(speye(n[2]+1), ddx(n[1]), speye(n[0]))
D12 = kron3(speye(n[2]+1), speye(n[1]), ddx(n[0]))
self._edgeCurl = sdiag(1/S)*(C*sdiag(L)) O1 = spzeros(np.shape(D32)[0], np.shape(D31)[1])
return self._edgeCurl O2 = spzeros(np.shape(D31)[0], np.shape(D32)[1])
O3 = spzeros(np.shape(D21)[0], np.shape(D13)[1])
C = sp.vstack((sp.hstack((O1, -D32, D23)),
sp.hstack((D31, O2, -D13)),
sp.hstack((-D21, D12, O3))), format="csr")
self._edgeCurl = sdiag(1/S)*(C*sdiag(L))
return self._edgeCurl
return locals()
_edgeCurl = None
edgeCurl = property(**edgeCurl())
def getBCProjWF(self, BC, discretization='CC'): def getBCProjWF(self, BC, discretization='CC'):
""" """
@@ -498,19 +489,16 @@ class DiffOperators(object):
The weak form boundary condition projection matrices. The weak form boundary condition projection matrices.
Examples:: Examples::
# Neumann in all directions
BC = 'neumann'
# 3D, Dirichlet in y Neumann else BC = 'neumann' # Neumann in all directions
BC = ['neumann', 'dirichlet', 'neumann'] BC = ['neumann', 'dirichlet', 'neumann'] # 3D, Dirichlet in y Neumann else
BC = [['neumann', 'dirichlet'], 'dirichlet', 'dirichlet'] # 3D, Neumann in x on bottom of domain,
# Dirichlet else
# 3D, Neumann in x on bottom of domain, Dirichlet else
BC = [['neumann', 'dirichlet'], 'dirichlet', 'dirichlet']
""" """
if discretization is not 'CC': if discretization is not 'CC':
raise NotImplementedError('Boundary conditions only implemented' raise NotImplementedError('Boundary conditions only implemented for CC discretization.')
'for CC discretization.')
if(type(BC) is str): if(type(BC) is str):
BC = [BC for _ in self.vnC] # Repeat the str self.dim times BC = [BC for _ in self.vnC] # Repeat the str self.dim times
@@ -522,34 +510,35 @@ class DiffOperators(object):
for i, bc_i in enumerate(BC): for i, bc_i in enumerate(BC):
BC[i] = checkBC(bc_i) BC[i] = checkBC(bc_i)
def projDirichlet(n, bc): def projDirichlet(n, bc):
bc = checkBC(bc) bc = checkBC(bc)
ij = ([0, n], [0, 1]) ij = ([0,n], [0,1])
vals = [0, 0] vals = [0,0]
if(bc[0] == 'dirichlet'): if(bc[0] == 'dirichlet'):
vals[0] = -1 vals[0] = -1
if(bc[1] == 'dirichlet'): if(bc[1] == 'dirichlet'):
vals[1] = 1 vals[1] = 1
return sp.csr_matrix((vals, ij), shape=(n+1, 2)) return sp.csr_matrix((vals, ij), shape=(n+1,2))
def projNeumannIn(n, bc): def projNeumannIn(n, bc):
bc = checkBC(bc) bc = checkBC(bc)
P = sp.identity(n+1).tocsr() P = sp.identity(n+1).tocsr()
if(bc[0] == 'neumann'): if(bc[0] == 'neumann'):
P = P[1:, :] P = P[1:,:]
if(bc[1] == 'neumann'): if(bc[1] == 'neumann'):
P = P[:-1, :] P = P[:-1,:]
return P return P
def projNeumannOut(n, bc): def projNeumannOut(n, bc):
bc = checkBC(bc) bc = checkBC(bc)
ij = ([0, 1], [0, n]) ij = ([0, 1],[0, n])
vals = [0,0] vals = [0,0]
if(bc[0] == 'neumann'): if(bc[0] == 'neumann'):
vals[0] = 1 vals[0] = 1
if(bc[1] == 'neumann'): if(bc[1] == 'neumann'):
vals[1] = 1 vals[1] = 1
return sp.csr_matrix((vals, ij), shape=(2, n+1)) return sp.csr_matrix((vals, ij), shape=(2,n+1))
n = self.vnC n = self.vnC
indF = self.faceBoundaryInd indF = self.faceBoundaryInd
@@ -561,7 +550,6 @@ class DiffOperators(object):
Pin = projNeumannIn(n[0], BC[0]) Pin = projNeumannIn(n[0], BC[0])
Pout = projNeumannOut(n[0], BC[0]) Pout = projNeumannOut(n[0], BC[0])
elif(self.dim == 2): elif(self.dim == 2):
Pbc1 = sp.kron(speye(n[1]), projDirichlet(n[0], BC[0])) Pbc1 = sp.kron(speye(n[1]), projDirichlet(n[0], BC[0]))
Pbc2 = sp.kron(projDirichlet(n[1], BC[1]), speye(n[0])) Pbc2 = sp.kron(projDirichlet(n[1], BC[1]), speye(n[0]))
@@ -576,14 +564,12 @@ class DiffOperators(object):
P1 = sp.kron(speye(n[1]), projNeumannOut(n[0], BC[0])) P1 = sp.kron(speye(n[1]), projNeumannOut(n[0], BC[0]))
P2 = sp.kron(projNeumannOut(n[1], BC[1]), speye(n[0])) P2 = sp.kron(projNeumannOut(n[1], BC[1]), speye(n[0]))
Pout = sp.block_diag((P1, P2), format="csr") Pout = sp.block_diag((P1, P2), format="csr")
elif(self.dim == 3): elif(self.dim == 3):
Pbc1 = kron3(speye(n[2]), speye(n[1]), projDirichlet(n[0], BC[0])) Pbc1 = kron3(speye(n[2]), speye(n[1]), projDirichlet(n[0], BC[0]))
Pbc2 = kron3(speye(n[2]), projDirichlet(n[1], BC[1]), speye(n[0])) Pbc2 = kron3(speye(n[2]), projDirichlet(n[1], BC[1]), speye(n[0]))
Pbc3 = kron3(projDirichlet(n[2], BC[2]), speye(n[1]), speye(n[0])) Pbc3 = kron3(projDirichlet(n[2], BC[2]), speye(n[1]), speye(n[0]))
Pbc = sp.block_diag((Pbc1, Pbc2, Pbc3), format="csr") Pbc = sp.block_diag((Pbc1, Pbc2, Pbc3), format="csr")
indF = np.r_[(indF[0] | indF[1]), (indF[2] | indF[3]), (indF[4] | indF = np.r_[(indF[0] | indF[1]), (indF[2] | indF[3]), (indF[4] | indF[5])]
indF[5])]
Pbc = Pbc*sdiag(self.area[indF]) Pbc = Pbc*sdiag(self.area[indF])
P1 = kron3(speye(n[2]), speye(n[1]), projNeumannIn(n[0], BC[0])) P1 = kron3(speye(n[2]), speye(n[1]), projNeumannIn(n[0], BC[0]))
@@ -600,36 +586,36 @@ class DiffOperators(object):
def getBCProjWF_simple(self, discretization='CC'): def getBCProjWF_simple(self, discretization='CC'):
""" """
The weak form boundary condition projection matrices The weak form boundary condition projection matrices
when mixed boundary condition is used when mixed boundary condition is used
""" """
if discretization is not 'CC': if discretization is not 'CC':
raise NotImplementedError('Boundary conditions only implemented' raise NotImplementedError('Boundary conditions only implemented for CC discretization.')
'for CC discretization.')
def projBC(n): def projBC(n):
ij = ([0, n], [0, 1]) ij = ([0,n], [0,1])
vals = [0, 0] vals = [0,0]
vals[0] = 1 vals[0] = 1
vals[1] = 1 vals[1] = 1
return sp.csr_matrix((vals, ij), shape=(n+1, 2)) return sp.csr_matrix((vals, ij), shape=(n+1,2))
def projDirichlet(n, bc): def projDirichlet(n, bc):
bc = checkBC(bc) bc = checkBC(bc)
ij = ([0, n], [0, 1]) ij = ([0,n], [0,1])
vals = [0, 0] vals = [0,0]
if(bc[0] == 'dirichlet'): if(bc[0] == 'dirichlet'):
vals[0] = -1 vals[0] = -1
if(bc[1] == 'dirichlet'): if(bc[1] == 'dirichlet'):
vals[1] = 1 vals[1] = 1
return sp.csr_matrix((vals, ij), shape=(n+1, 2)) return sp.csr_matrix((vals, ij), shape=(n+1,2))
BC = [['dirichlet', 'dirichlet'], ['dirichlet', 'dirichlet'], BC = [['dirichlet','dirichlet'],['dirichlet','dirichlet'],['dirichlet','dirichlet']]
['dirichlet', 'dirichlet']]
n = self.vnC n = self.vnC
indF = self.faceBoundaryInd indF = self.faceBoundaryInd
if(self.dim == 1): if(self.dim == 1):
Pbc = projDirichlet(n[0], BC[0]) Pbc = projDirichlet(n[0], BC[0])
B = projBC(n[0]) B = projBC(n[0])
@@ -667,11 +653,9 @@ class DiffOperators(object):
if(self.dim == 1): if(self.dim == 1):
return self.aveFx2CC return self.aveFx2CC
elif(self.dim == 2): elif(self.dim == 2):
return (0.5)*sp.hstack((self.aveFx2CC, self.aveFy2CC), return (0.5)*sp.hstack((self.aveFx2CC, self.aveFy2CC), format="csr")
format="csr")
elif(self.dim == 3): elif(self.dim == 3):
return (1./3.)*sp.hstack((self.aveFx2CC, self.aveFy2CC, return (1./3.)*sp.hstack((self.aveFx2CC, self.aveFy2CC, self.aveFz2CC), format="csr")
self.aveFz2CC), format="csr")
@property @property
def aveF2CCV(self): def aveF2CCV(self):
@@ -681,16 +665,11 @@ class DiffOperators(object):
elif(self.dim == 2): elif(self.dim == 2):
return sp.block_diag((self.aveFx2CC, self.aveFy2CC), format="csr") return sp.block_diag((self.aveFx2CC, self.aveFy2CC), format="csr")
elif(self.dim == 3): elif(self.dim == 3):
return sp.block_diag((self.aveFx2CC, self.aveFy2CC, self.aveFz2CC), return sp.block_diag((self.aveFx2CC, self.aveFy2CC, self.aveFz2CC), format="csr")
format="csr")
@property @property
def aveFx2CC(self): def aveFx2CC(self):
""" "Construct the averaging operator on cell faces in the x direction to cell centers."
Construct the averaging operator on cell faces in the x direction to
cell centers.
"""
if getattr(self, '_aveFx2CC', None) is None: if getattr(self, '_aveFx2CC', None) is None:
n = self.vnC n = self.vnC
if(self.dim == 1): if(self.dim == 1):
@@ -703,12 +682,8 @@ class DiffOperators(object):
@property @property
def aveFy2CC(self): def aveFy2CC(self):
""" "Construct the averaging operator on cell faces in the y direction to cell centers."
Construct the averaging operator on cell faces in the y direction to if self.dim < 2: return None
cell centers.
"""
if self.dim < 2:
return None
if getattr(self, '_aveFy2CC', None) is None: if getattr(self, '_aveFy2CC', None) is None:
n = self.vnC n = self.vnC
if(self.dim == 2): if(self.dim == 2):
@@ -719,10 +694,7 @@ class DiffOperators(object):
@property @property
def aveFz2CC(self): def aveFz2CC(self):
""" "Construct the averaging operator on cell faces in the z direction to cell centers."
Construct the averaging operator on cell faces in the z direction to
cell centers.
"""
if self.dim < 3: return None if self.dim < 3: return None
if getattr(self, '_aveFz2CC', None) is None: if getattr(self, '_aveFz2CC', None) is None:
n = self.vnC n = self.vnC
@@ -739,18 +711,12 @@ class DiffOperators(object):
if(self.dim == 1): if(self.dim == 1):
self._aveCC2F = avExtrap(n[0]) self._aveCC2F = avExtrap(n[0])
elif(self.dim == 2): elif(self.dim == 2):
self._aveCC2F = sp.vstack((sp.kron(speye(n[1]), self._aveCC2F = sp.vstack((sp.kron(speye(n[1]), avExtrap(n[0])),
avExtrap(n[0])), sp.kron(avExtrap(n[1]), speye(n[0]))), format="csr")
sp.kron(avExtrap(n[1]),
speye(n[0]))), format="csr")
elif(self.dim == 3): elif(self.dim == 3):
self._aveCC2F = sp.vstack((kron3(speye(n[2]), speye(n[1]), self._aveCC2F = sp.vstack((kron3(speye(n[2]), speye(n[1]), avExtrap(n[0])),
avExtrap(n[0])), kron3(speye(n[2]), avExtrap(n[1]), speye(n[0])),
kron3(speye(n[2]), avExtrap(n[1]), kron3(avExtrap(n[2]), speye(n[1]), speye(n[0]))), format="csr")
speye(n[0])),
kron3(avExtrap(n[2]), speye(n[1]),
speye(n[0]))),
format="csr")
return self._aveCC2F return self._aveCC2F
@property @property
@@ -761,8 +727,7 @@ class DiffOperators(object):
elif(self.dim == 2): elif(self.dim == 2):
return 0.5*sp.hstack((self.aveEx2CC, self.aveEy2CC), format="csr") return 0.5*sp.hstack((self.aveEx2CC, self.aveEy2CC), format="csr")
elif(self.dim == 3): elif(self.dim == 3):
return (1./3)*sp.hstack((self.aveEx2CC, self.aveEy2CC, return (1./3)*sp.hstack((self.aveEx2CC, self.aveEy2CC, self.aveEz2CC), format="csr")
self.aveEz2CC), format="csr")
@property @property
def aveE2CCV(self): def aveE2CCV(self):
@@ -772,15 +737,11 @@ class DiffOperators(object):
elif(self.dim == 2): elif(self.dim == 2):
return sp.block_diag((self.aveEx2CC, self.aveEy2CC), format="csr") return sp.block_diag((self.aveEx2CC, self.aveEy2CC), format="csr")
elif(self.dim == 3): elif(self.dim == 3):
return sp.block_diag((self.aveEx2CC, self.aveEy2CC, self.aveEz2CC), return sp.block_diag((self.aveEx2CC, self.aveEy2CC, self.aveEz2CC), format="csr")
format="csr")
@property @property
def aveEx2CC(self): def aveEx2CC(self):
""" "Construct the averaging operator on cell edges in the x direction to cell centers."
Construct the averaging operator on cell edges in the x direction to
cell centers.
"""
if getattr(self, '_aveEx2CC', None) is None: if getattr(self, '_aveEx2CC', None) is None:
# The number of cell centers in each direction # The number of cell centers in each direction
n = self.vnC n = self.vnC
@@ -794,12 +755,8 @@ class DiffOperators(object):
@property @property
def aveEy2CC(self): def aveEy2CC(self):
""" "Construct the averaging operator on cell edges in the y direction to cell centers."
Construct the averaging operator on cell edges in the y direction to if self.dim < 2: return None
cell centers.
"""
if self.dim < 2:
return None
if getattr(self, '_aveEy2CC', None) is None: if getattr(self, '_aveEy2CC', None) is None:
# The number of cell centers in each direction # The number of cell centers in each direction
n = self.vnC n = self.vnC
@@ -811,12 +768,8 @@ class DiffOperators(object):
@property @property
def aveEz2CC(self): def aveEz2CC(self):
""" "Construct the averaging operator on cell edges in the z direction to cell centers."
Construct the averaging operator on cell edges in the z direction to if self.dim < 3: return None
cell centers.
"""
if self.dim < 3:
return None
if getattr(self, '_aveEz2CC', None) is None: if getattr(self, '_aveEz2CC', None) is None:
# The number of cell centers in each direction # The number of cell centers in each direction
n = self.vnC n = self.vnC
@@ -840,10 +793,7 @@ class DiffOperators(object):
@property @property
def aveN2E(self): def aveN2E(self):
""" "Construct the averaging operator on cell nodes to cell edges, keeping each dimension separate."
Construct the averaging operator on cell nodes to cell edges, keeping
each dimension separate.
"""
if getattr(self, '_aveN2E', None) is None: if getattr(self, '_aveN2E', None) is None:
# The number of cell centers in each direction # The number of cell centers in each direction
@@ -852,24 +802,16 @@ class DiffOperators(object):
self._aveN2E = av(n[0]) self._aveN2E = av(n[0])
elif(self.dim == 2): elif(self.dim == 2):
self._aveN2E = sp.vstack((sp.kron(speye(n[1]+1), av(n[0])), self._aveN2E = sp.vstack((sp.kron(speye(n[1]+1), av(n[0])),
sp.kron(av(n[1]), speye(n[0]+1))), sp.kron(av(n[1]), speye(n[0]+1))), format="csr")
format="csr")
elif(self.dim == 3): elif(self.dim == 3):
self._aveN2E = sp.vstack((kron3(speye(n[2]+1), speye(n[1]+1), self._aveN2E = sp.vstack((kron3(speye(n[2]+1), speye(n[1]+1), av(n[0])),
av(n[0])), kron3(speye(n[2]+1), av(n[1]), speye(n[0]+1)),
kron3(speye(n[2]+1), av(n[1]), kron3(av(n[2]), speye(n[1]+1), speye(n[0]+1))), format="csr")
speye(n[0]+1)),
kron3(av(n[2]), speye(n[1]+1),
speye(n[0]+1))),
format="csr")
return self._aveN2E return self._aveN2E
@property @property
def aveN2F(self): def aveN2F(self):
""" "Construct the averaging operator on cell nodes to cell faces, keeping each dimension separate."
Construct the averaging operator on cell nodes to cell faces, keeping
each dimension separate.
"""
if getattr(self, '_aveN2F', None) is None: if getattr(self, '_aveN2F', None) is None:
# The number of cell centers in each direction # The number of cell centers in each direction
n = self.vnC n = self.vnC
@@ -877,14 +819,9 @@ class DiffOperators(object):
self._aveN2F = av(n[0]) self._aveN2F = av(n[0])
elif(self.dim == 2): elif(self.dim == 2):
self._aveN2F = sp.vstack((sp.kron(av(n[1]), speye(n[0]+1)), self._aveN2F = sp.vstack((sp.kron(av(n[1]), speye(n[0]+1)),
sp.kron(speye(n[1]+1), av(n[0]))), sp.kron(speye(n[1]+1), av(n[0]))), format="csr")
format="csr")
elif(self.dim == 3): elif(self.dim == 3):
self._aveN2F = sp.vstack((kron3(av(n[2]), av(n[1]), self._aveN2F = sp.vstack((kron3(av(n[2]), av(n[1]), speye(n[0]+1)),
speye(n[0]+1)), kron3(av(n[2]), speye(n[1]+1), av(n[0])),
kron3(av(n[2]), speye(n[1]+1), kron3(speye(n[2]+1), av(n[1]), av(n[0]))), format="csr")
av(n[0])),
kron3(speye(n[2]+1), av(n[1]),
av(n[0]))),
format="csr")
return self._aveN2F return self._aveN2F
+10 -9
View File
@@ -16,7 +16,7 @@ class InnerProducts(object):
:param bool invProp: inverts the material property :param bool invProp: inverts the material property
:param bool invMat: inverts the matrix :param bool invMat: inverts the matrix
:param bool doFast: do a faster implementation if available. :param bool doFast: do a faster implementation if available.
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: M, the inner product matrix (nF, nF) :return: M, the inner product matrix (nF, nF)
""" """
return self._getInnerProduct('F', prop=prop, invProp=invProp, invMat=invMat, doFast=doFast) return self._getInnerProduct('F', prop=prop, invProp=invProp, invMat=invMat, doFast=doFast)
@@ -27,7 +27,7 @@ class InnerProducts(object):
:param bool invProp: inverts the material property :param bool invProp: inverts the material property
:param bool invMat: inverts the matrix :param bool invMat: inverts the matrix
:param bool doFast: do a faster implementation if available. :param bool doFast: do a faster implementation if available.
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: M, the inner product matrix (nE, nE) :return: M, the inner product matrix (nE, nE)
""" """
return self._getInnerProduct('E', prop=prop, invProp=invProp, invMat=invMat, doFast=doFast) return self._getInnerProduct('E', prop=prop, invProp=invProp, invMat=invMat, doFast=doFast)
@@ -39,7 +39,7 @@ class InnerProducts(object):
:param bool invProp: inverts the material property :param bool invProp: inverts the material property
:param bool invMat: inverts the matrix :param bool invMat: inverts the matrix
:param bool doFast: do a faster implementation if available. :param bool doFast: do a faster implementation if available.
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: M, the inner product matrix (nE, nE) :return: M, the inner product matrix (nE, nE)
""" """
assert projType in ['F', 'E'], "projType must be 'F' for faces or 'E' for edges" assert projType in ['F', 'E'], "projType must be 'F' for faces or 'E' for edges"
@@ -115,12 +115,13 @@ class InnerProducts(object):
:param bool doFast: do a faster implementation if available. :param bool doFast: do a faster implementation if available.
:param bool invProp: inverts the material property :param bool invProp: inverts the material property
:param bool invMat: inverts the matrix :param bool invMat: inverts the matrix
:rtype: function
:return: dMdmu(u), the derivative of the inner product matrix (u) :return: dMdmu(u), the derivative of the inner product matrix (u)
Given u, dMdmu returns (nF, nC*nA) Given u, dMdmu returns (nF, nC*nA)
:param numpy.ndarray u: vector that multiplies dMdmu :param np.ndarray u: vector that multiplies dMdmu
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: dMdmu, the derivative of the inner product matrix for a certain u :return: dMdmu, the derivative of the inner product matrix for a certain u
""" """
return self._getInnerProductDeriv(prop, 'F', doFast=doFast, invProp=invProp, invMat=invMat) return self._getInnerProductDeriv(prop, 'F', doFast=doFast, invProp=invProp, invMat=invMat)
@@ -132,7 +133,7 @@ class InnerProducts(object):
:param bool doFast: do a faster implementation if available. :param bool doFast: do a faster implementation if available.
:param bool invProp: inverts the material property :param bool invProp: inverts the material property
:param bool invMat: inverts the matrix :param bool invMat: inverts the matrix
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: dMdm, the derivative of the inner product matrix (nE, nC*nA) :return: dMdm, the derivative of the inner product matrix (nE, nC*nA)
""" """
return self._getInnerProductDeriv(prop, 'E', doFast=doFast, invProp=invProp, invMat=invMat) return self._getInnerProductDeriv(prop, 'E', doFast=doFast, invProp=invProp, invMat=invMat)
@@ -144,7 +145,7 @@ class InnerProducts(object):
:param bool doFast: do a faster implementation if available. :param bool doFast: do a faster implementation if available.
:param bool invProp: inverts the material property :param bool invProp: inverts the material property
:param bool invMat: inverts the matrix :param bool invMat: inverts the matrix
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: dMdm, the derivative of the inner product matrix (nE, nC*nA) :return: dMdm, the derivative of the inner product matrix (nE, nC*nA)
""" """
fast = None fast = None
@@ -168,7 +169,7 @@ class InnerProducts(object):
:param numpy.array v: vector to multiply (required in the general implementation) :param numpy.array v: vector to multiply (required in the general implementation)
:param list P: list of projection matrices :param list P: list of projection matrices
:param str projType: 'F' for faces 'E' for edges :param str projType: 'F' for faces 'E' for edges
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: dMdm, the derivative of the inner product matrix (n, nC*nA) :return: dMdm, the derivative of the inner product matrix (n, nC*nA)
""" """
assert projType in ['F', 'E'], "projType must be 'F' for faces or 'E' for edges" assert projType in ['F', 'E'], "projType must be 'F' for faces or 'E' for edges"
@@ -421,7 +422,7 @@ class InnerProducts(object):
def _getEdgePx(M): def _getEdgePx(M):
"""Returns a function for creating projection matrices""" """Returns a function for creating projection matrices"""
def Px(xEdge): def Px(xEdge):
assert xEdge == 'eX0', 'xEdge = {0!s}, not eX0'.format(xEdge) assert xEdge == 'eX0', 'xEdge = %s, not eX0' % xEdge
return sp.identity(M.nC) return sp.identity(M.nC)
return Px return Px
+81 -25
View File
@@ -6,11 +6,13 @@ class TensorMeshIO(object):
@classmethod @classmethod
def readUBC(TensorMesh, fileName): def readUBC(TensorMesh, fileName):
""" """
Read UBC GIF 3D tensor mesh and generate 3D TensorMesh in SimPEG. Read UBC GIF 3DTensor mesh and generate 3D Tensor mesh in simpegTD
:param string fileName: path to the UBC GIF mesh file Input:
:rtype: TensorMesh :param fileName, path to the UBC GIF mesh file
:return: The tensor mesh for the fileName.
Output:
:param SimPEG TensorMesh object
""" """
# Interal function to read cell size lines for the UBC mesh files. # Interal function to read cell size lines for the UBC mesh files.
@@ -46,9 +48,11 @@ class TensorMeshIO(object):
Read VTK Rectilinear (vtr xml file) and return SimPEG Tensor mesh and model Read VTK Rectilinear (vtr xml file) and return SimPEG Tensor mesh and model
Input: Input:
:param string fileName: path to the vtr model file to read :param vtrFileName, path to the vtr model file to write to
:rtype: tuple
:return: (TensorMesh, modelDictionary) Output:
:return SimPEG TensorMesh object
:return SimPEG model dictionary
""" """
# Import # Import
@@ -98,8 +102,9 @@ class TensorMeshIO(object):
Makes and saves a VTK rectilinear file (vtr) for a simpeg Tensor mesh and model. Makes and saves a VTK rectilinear file (vtr) for a simpeg Tensor mesh and model.
Input: Input:
:param string fileName: path to the output vtk file :param str, path to the output vtk file
:param dict models: dictionary of numpy.array - Name('s) and array('s). Match number of cells :param mesh, SimPEG TensorMesh object - mesh to be transfer to VTK
:param models, dictionary of numpy.array - Name('s) and array('s). Match number of cells
""" """
# Import # Import
@@ -135,7 +140,6 @@ class TensorMeshIO(object):
vtkObj.GetCellData().AddArray(vtkDoubleArr) vtkObj.GetCellData().AddArray(vtkDoubleArr)
# Set the active scalar # Set the active scalar
vtkObj.GetCellData().SetActiveScalars(models.keys()[0]) vtkObj.GetCellData().SetActiveScalars(models.keys()[0])
# vtkObj.Update()
# Check the extension of the fileName # Check the extension of the fileName
ext = os.path.splitext(fileName)[1] ext = os.path.splitext(fileName)[1]
@@ -152,14 +156,61 @@ class TensorMeshIO(object):
vtrWriteFilter.SetFileName(fileName) vtrWriteFilter.SetFileName(fileName)
vtrWriteFilter.Update() vtrWriteFilter.Update()
def _toVTRObj(mesh,models=None):
"""
Makes and saves a VTK rectilinear file (vtr) for a simpeg Tensor mesh and model.
Input:
:param str, path to the output vtk file
:param mesh, SimPEG TensorMesh object - mesh to be transfer to VTK
:param models, dictionary of numpy.array - Name('s) and array('s). Match number of cells
"""
# Import
from vtk import vtkRectilinearGrid as rectGrid, VTK_VERSION
from vtk.util.numpy_support import numpy_to_vtk
# Deal with dimensionalities
if mesh.dim >= 1:
vX = mesh.vectorNx
xD = mesh.nNx
yD,zD = 1,1
vY, vZ = np.array([0,0])
if mesh.dim >= 2:
vY = mesh.vectorNy
yD = mesh.nNy
if mesh.dim == 3:
vZ = mesh.vectorNz
zD = mesh.nNz
# Use rectilinear VTK grid.
# Assign the spatial information.
vtkObj = rectGrid()
vtkObj.SetDimensions(xD,yD,zD)
vtkObj.SetXCoordinates(numpy_to_vtk(vX,deep=1))
vtkObj.SetYCoordinates(numpy_to_vtk(vY,deep=1))
vtkObj.SetZCoordinates(numpy_to_vtk(vZ,deep=1))
# Assign the model('s) to the object
if models is not None:
for item in models.iteritems():
# Convert numpy array
vtkDoubleArr = numpy_to_vtk(item[1],deep=1)
vtkDoubleArr.SetName(item[0])
vtkObj.GetCellData().AddArray(vtkDoubleArr)
# Set the active scalar
vtkObj.GetCellData().SetActiveScalars(models.keys()[0])
return vtkObj
def readModelUBC(mesh, fileName): def readModelUBC(mesh, fileName):
""" """
Read UBC 3DTensor mesh model and generate 3D Tensor mesh model in simpeg Read UBC 3DTensor mesh model and generate 3D Tensor mesh model in simpeg
:param string fileName: path to the UBC GIF mesh file to read Input:
:rtype: numpy.ndarray :param fileName, path to the UBC GIF mesh file to read
:return: model with TensorMesh ordered :param mesh, TensorMesh object, mesh that coresponds to the model
Output:
:return numpy array, model with TensorMesh ordered
""" """
f = open(fileName, 'r') f = open(fileName, 'r')
model = np.array(map(float, f.readlines())) model = np.array(map(float, f.readlines()))
@@ -175,7 +226,8 @@ class TensorMeshIO(object):
Writes a model associated with a SimPEG TensorMesh Writes a model associated with a SimPEG TensorMesh
to a UBC-GIF format model file. to a UBC-GIF format model file.
:param string fileName: File to write to :param str fileName: File to write to
:param simpeg.Mesh.TensorMesh mesh: The mesh
:param numpy.ndarray model: The model :param numpy.ndarray model: The model
""" """
@@ -192,17 +244,17 @@ class TensorMeshIO(object):
""" """
Writes a SimPEG TensorMesh to a UBC-GIF format mesh file. Writes a SimPEG TensorMesh to a UBC-GIF format mesh file.
:param string fileName: File to write to :param str fileName: File to write to
:param dict models: A dictionary of the models :param simpeg.Mesh.TensorMesh mesh: The mesh
""" """
assert mesh.dim == 3 assert mesh.dim == 3
s = '' s = ''
s += '{0:d} {1:d} {2:d}\n'.format(*tuple(mesh.vnC)) s += '%i %i %i\n' %tuple(mesh.vnC)
origin = mesh.x0 + np.array([0,0,mesh.hz.sum()]) # Have to it in the same operation or use mesh.x0.copy(), otherwise the mesh.x0 is updated. origin = mesh.x0 + np.array([0,0,mesh.hz.sum()]) # Have to it in the same operation or use mesh.x0.copy(), otherwise the mesh.x0 is updated.
origin.dtype = float origin.dtype = float
s += '{0:.2f} {1:.2f} {2:.2f}\n'.format(*tuple(origin)) s += '%.2f %.2f %.2f\n' %tuple(origin)
s += ('%.2f '*mesh.nCx+'\n')%tuple(mesh.hx) s += ('%.2f '*mesh.nCx+'\n')%tuple(mesh.hx)
s += ('%.2f '*mesh.nCy+'\n')%tuple(mesh.hy) s += ('%.2f '*mesh.nCy+'\n')%tuple(mesh.hy)
s += ('%.2f '*mesh.nCz+'\n')%tuple(mesh.hz[::-1]) s += ('%.2f '*mesh.nCz+'\n')%tuple(mesh.hz[::-1])
@@ -222,8 +274,9 @@ class TreeMeshIO(object):
""" """
Write UBC ocTree mesh and model files from a simpeg ocTree mesh and model. Write UBC ocTree mesh and model files from a simpeg ocTree mesh and model.
:param string fileName: File to write to :param str fileName: File to write to
:param dict models: The models in a dictionary, where the keys is the name of the of the model file :param simpeg.Mesh.TreeMesh mesh: The mesh
:param dictionary models: The models in a dictionary, where the keys is the name of the of the model file
""" """
# Calculate information to write in the file. # Calculate information to write in the file.
@@ -276,9 +329,10 @@ class TreeMeshIO(object):
Input: Input:
:param str meshFile: path to the UBC GIF OcTree mesh file to read :param str meshFile: path to the UBC GIF OcTree mesh file to read
:rtype: SimPEG.Mesh.TreeMesh
:return: The octree mesh
Output:
:return SimPEG.Mesh.TreeMesh mesh: The octree mesh
:return list of ndarray's: models as a list of numpy array's
""" """
## Read the file lines ## Read the file lines
@@ -324,9 +378,11 @@ class TreeMeshIO(object):
""" """
Read UBC OcTree model and get vector Read UBC OcTree model and get vector
:param string fileName: path to the UBC GIF model file to read Input:
:rtype: numpy.ndarray :param fileName, path to the UBC GIF model file to read
:return: OcTree model
Output:
:return numpy array, OcTree model
""" """
if type(fileName) is list: if type(fileName) is list:
+8 -8
View File
@@ -23,8 +23,8 @@ class BaseTensorMesh(BaseMesh):
h_i = self._unitDimensions[i] * np.ones(int(h_i))/int(h_i) h_i = self._unitDimensions[i] * np.ones(int(h_i))/int(h_i)
elif type(h_i) is list: elif type(h_i) is list:
h_i = Utils.meshTensor(h_i) h_i = Utils.meshTensor(h_i)
assert isinstance(h_i, np.ndarray), ("h[{0:d}] is not a numpy array.".format(i)) assert isinstance(h_i, np.ndarray), ("h[%i] is not a numpy array." % i)
assert len(h_i.shape) == 1, ("h[{0:d}] must be a 1D numpy array.".format(i)) assert len(h_i.shape) == 1, ("h[%i] must be a 1D numpy array." % i)
h[i] = h_i[:] # make a copy. h[i] = h_i[:] # make a copy.
x0 = np.zeros(len(h)) x0 = np.zeros(len(h))
@@ -41,7 +41,7 @@ class BaseTensorMesh(BaseMesh):
elif x_i == 'N': elif x_i == 'N':
x0[i] = -h_i.sum() x0[i] = -h_i.sum()
else: else:
raise Exception("x0[{0:d}] must be a scalar or '0' to be zero, 'C' to center, or 'N' to be negative.".format(i)) raise Exception("x0[%i] must be a scalar or '0' to be zero, 'C' to center, or 'N' to be negative." % i)
if isinstance(self, BaseRectangularMesh): if isinstance(self, BaseRectangularMesh):
BaseRectangularMesh.__init__(self, np.array([x.size for x in h]), x0) BaseRectangularMesh.__init__(self, np.array([x.size for x in h]), x0)
@@ -198,8 +198,8 @@ class BaseTensorMesh(BaseMesh):
Determines if a set of points are inside a mesh. Determines if a set of points are inside a mesh.
:param numpy.ndarray pts: Location of points to test :param numpy.ndarray pts: Location of points to test
:rtype numpy.ndarray: :rtype numpy.ndarray
:return: inside, numpy array of booleans :return inside, numpy array of booleans
""" """
pts = Utils.asArray_N_x_Dim(pts, self.dim) pts = Utils.asArray_N_x_Dim(pts, self.dim)
@@ -221,7 +221,7 @@ class BaseTensorMesh(BaseMesh):
:param numpy.ndarray loc: Location of points to interpolate to :param numpy.ndarray loc: Location of points to interpolate to
:param str locType: What to interpolate (see below) :param str locType: What to interpolate (see below)
:rtype: scipy.sparse.csr_matrix :rtype: scipy.sparse.csr.csr_matrix
:return: M, the interpolation matrix :return: M, the interpolation matrix
locType can be:: locType can be::
@@ -239,7 +239,7 @@ class BaseTensorMesh(BaseMesh):
'CCVz' -> z-component of vector field defined on cell centers 'CCVz' -> z-component of vector field defined on cell centers
""" """
if self._meshType == 'CYL' and self.isSymmetric and locType in ['Ex','Ez','Fy']: if self._meshType == 'CYL' and self.isSymmetric and locType in ['Ex','Ez','Fy']:
raise Exception('Symmetric CylMesh does not support {0!s} interpolation, as this variable does not exist.'.format(locType)) raise Exception('Symmetric CylMesh does not support %s interpolation, as this variable does not exist.' % locType)
loc = Utils.asArray_N_x_Dim(loc, self.dim) loc = Utils.asArray_N_x_Dim(loc, self.dim)
@@ -289,7 +289,7 @@ class BaseTensorMesh(BaseMesh):
:param bool returnP: returns the projection matrices :param bool returnP: returns the projection matrices
:param bool invProp: inverts the material property :param bool invProp: inverts the material property
:param bool invMat: inverts the matrix :param bool invMat: inverts the matrix
:rtype: scipy.sparse.csr_matrix :rtype: scipy.csr_matrix
:return: M, the inner product matrix (nF, nF) :return: M, the inner product matrix (nF, nF)
""" """
assert projType in ['F', 'E'], "projType must be 'F' for faces or 'E' for edges" assert projType in ['F', 'E'], "projType must be 'F' for faces or 'E' for edges"
+5 -5
View File
@@ -177,7 +177,7 @@ class TreeMesh(BaseTensorMesh, InnerProducts, TreeMeshIO):
return l return l
def __str__(self): def __str__(self):
outStr = ' ---- {0!s}TreeMesh ---- '.format(('Oc' if self.dim == 3 else 'Quad')) outStr = ' ---- %sTreeMesh ---- '%('Oc' if self.dim == 3 else 'Quad')
def printH(hx, outStr=''): def printH(hx, outStr=''):
i = -1 i = -1
while True: while True:
@@ -213,7 +213,7 @@ class TreeMesh(BaseTensorMesh, InnerProducts, TreeMeshIO):
outStr += printH(self.hy, outStr='\n hy:') outStr += printH(self.hy, outStr='\n hy:')
outStr += printH(self.hz, outStr='\n hz:') outStr += printH(self.hz, outStr='\n hz:')
outStr += '\n nC: {0:d}'.format(self.nC) outStr += '\n nC: {0:d}'.format(self.nC)
outStr += '\n Fill: {0:2.2f}%'.format((self.fill*100)) outStr += '\n Fill: %2.2f%%'%(self.fill*100)
return outStr return outStr
@property @property
@@ -1875,7 +1875,7 @@ class TreeMesh(BaseTensorMesh, InnerProducts, TreeMeshIO):
:param numpy.ndarray locs: Location of points to interpolate to :param numpy.ndarray locs: Location of points to interpolate to
:param str locType: What to interpolate (see below) :param str locType: What to interpolate (see below)
:rtype: scipy.sparse.csr_matrix :rtype: scipy.sparse.csr.csr_matrix
:return: M, the interpolation matrix :return: M, the interpolation matrix
locType can be:: locType can be::
@@ -2210,7 +2210,7 @@ class TreeMesh(BaseTensorMesh, InnerProducts, TreeMeshIO):
ax.set_xlabel('y' if normal == 'X' else 'x') ax.set_xlabel('y' if normal == 'X' else 'x')
ax.set_ylabel('y' if normal == 'Z' else 'z') ax.set_ylabel('y' if normal == 'Z' else 'z')
ax.set_title('Slice {0:d}, {1!s} = {2:4.2f}'.format(ind, normal, indLoc)) ax.set_title('Slice %d, %s = %4.2f' % (ind,normal,indLoc))
if grid: if grid:
_ = antiNormalInd _ = antiNormalInd
@@ -2240,7 +2240,7 @@ class TreeMesh(BaseTensorMesh, InnerProducts, TreeMeshIO):
if key < 0 : #Handle negative indices if key < 0 : #Handle negative indices
key += len( self ) key += len( self )
if key >= len( self ) : if key >= len( self ) :
raise IndexError, "The index ({0:d}) is out of range.".format(key) raise IndexError, "The index (%d) is out of range."%key
self._numberCells() # no-op if numbered self._numberCells() # no-op if numbered
index = self._i2cc[key] index = self._i2cc[key]
+8 -8
View File
@@ -171,7 +171,7 @@ class TensorView(object):
iz = ix + iy*nX iz = ix + iy*nX
if iz < self.nCz: if iz < self.nCz:
ax.text((ix+1)*(self.vectorNx[-1]-self.x0[0])-pad,(iy)*(self.vectorNy[-1]-self.x0[1])+pad, ax.text((ix+1)*(self.vectorNx[-1]-self.x0[0])-pad,(iy)*(self.vectorNy[-1]-self.x0[1])+pad,
'#{0:.0f}'.format(iz),color=annotationColor,verticalalignment='bottom',horizontalalignment='right',size='x-large') '#%i'%iz,color=annotationColor,verticalalignment='bottom',horizontalalignment='right',size='x-large')
ax.set_title(vType) ax.set_title(vType)
if showIt: plt.show() if showIt: plt.show()
@@ -221,10 +221,10 @@ class TensorView(object):
vTypeOpts = ['CC', 'CCv','N','F','E','Fx','Fy','Fz','E','Ex','Ey','Ez'] vTypeOpts = ['CC', 'CCv','N','F','E','Fx','Fy','Fz','E','Ex','Ey','Ez']
# Some user error checking # Some user error checking
assert vType in vTypeOpts, "vType must be in ['{0!s}']".format("','".join(vTypeOpts)) assert vType in vTypeOpts, "vType must be in ['%s']" % "','".join(vTypeOpts)
assert self.dim == 3, 'Must be a 3D mesh. Use plotImage.' assert self.dim == 3, 'Must be a 3D mesh. Use plotImage.'
assert view in viewOpts, "view must be in ['{0!s}']".format("','".join(viewOpts)) assert view in viewOpts, "view must be in ['%s']" % "','".join(viewOpts)
assert normal in normalOpts, "normal must be in ['{0!s}']".format("','".join(normalOpts)) assert normal in normalOpts, "normal must be in ['%s']" % "','".join(normalOpts)
assert type(grid) is bool, 'grid must be a boolean' assert type(grid) is bool, 'grid must be a boolean'
szSliceDim = getattr(self, 'nC'+normal.lower()) #: Size of the sliced dimension szSliceDim = getattr(self, 'nC'+normal.lower()) #: Size of the sliced dimension
@@ -295,7 +295,7 @@ class TensorView(object):
ax.set_xlabel('y' if normal == 'X' else 'x') ax.set_xlabel('y' if normal == 'X' else 'x')
ax.set_ylabel('y' if normal == 'Z' else 'z') ax.set_ylabel('y' if normal == 'Z' else 'z')
ax.set_title('Slice {0:.0f}'.format(ind)) ax.set_title('Slice %d' % ind)
return out return out
@@ -316,11 +316,11 @@ class TensorView(object):
vTypeOptsV = ['CCv','F','E'] vTypeOptsV = ['CCv','F','E']
vTypeOpts = vTypeOptsCC + vTypeOptsV vTypeOpts = vTypeOptsCC + vTypeOptsV
if view == 'vec': if view == 'vec':
assert vType in vTypeOptsV, "vType must be in ['{0!s}'] when view='vec'".format("','".join(vTypeOptsV)) assert vType in vTypeOptsV, "vType must be in ['%s'] when view='vec'" % "','".join(vTypeOptsV)
assert vType in vTypeOpts, "vType must be in ['{0!s}']".format("','".join(vTypeOpts)) assert vType in vTypeOpts, "vType must be in ['%s']" % "','".join(vTypeOpts)
viewOpts = ['real','imag','abs','vec'] viewOpts = ['real','imag','abs','vec']
assert view in viewOpts, "view must be in ['{0!s}']".format("','".join(viewOpts)) assert view in viewOpts, "view must be in ['%s']" % "','".join(viewOpts)
if ax is None: if ax is None:
@@ -4,18 +4,21 @@ import sys
from numpy.lib import recfunctions as recFunc from numpy.lib import recfunctions as recFunc
from SimPEG.EM.Utils import omega from SimPEG.EM.Utils import omega
############## ##############
### Fields ### ### Fields ###
############## ##############
class BaseMTFields(Problem.Fields): class BaseNSEMFields(Problem.Fields):
"""Field Storage for a MT survey.""" """Field Storage for a NSEM survey."""
knownFields = {} knownFields = {}
dtype = complex dtype = complex
###########
class Fields1D_e(BaseMTFields): # 1D Fields
###########
class Fields1D_ePrimSec(BaseNSEMFields):
""" """
Fields storage for the 1D MT solution. Fields storage for the 1D NSEM solution.
""" """
knownFields = {'e_1dSolution':'F'} knownFields = {'e_1dSolution':'F'}
aliasFields = { aliasFields = {
@@ -28,7 +31,119 @@ class Fields1D_e(BaseMTFields):
} }
def __init__(self,mesh,survey,**kwargs): def __init__(self,mesh,survey,**kwargs):
BaseMTFields.__init__(self,mesh,survey,**kwargs) BaseNSEMFields.__init__(self,mesh,survey,**kwargs)
def _ePrimary(self, eSolution, srcList):
ePrimary = np.zeros_like(eSolution)
for i, src in enumerate(srcList):
ep = src.ePrimary(self.survey.prob)
if ep is not None:
ePrimary[:,i] = ep[:,-1]
return ePrimary
def _eSecondary(self, eSolution, srcList):
return eSolution
def _e(self, eSolution, srcList):
return self._ePrimary(eSolution,srcList) + self._eSecondary(eSolution,srcList)
def _eDeriv_u(self, src, du_dm_v, adjoint = False):
return Utils.Identity()*du_dm_v
def _eDeriv_m(self, src, v, adjoint = False):
# assuming primary does not depend on the model
return Utils.Zero()
def _bPrimary(self, eSolution, srcList):
bPrimary = np.zeros([self.survey.mesh.nE,eSolution.shape[1]], dtype = complex)
for i, src in enumerate(srcList):
bp = src.bPrimary(self.survey.prob)
if bp is not None:
bPrimary[:,i] += bp[:,-1]
return bPrimary
def _bSecondary(self, eSolution, srcList):
C = self.mesh.nodalGrad
b = (C * eSolution)
for i, src in enumerate(srcList):
b[:,i] *= - 1./(1j*omega(src.freq))
# There is no magnetic source in the MT problem
# S_m, _ = src.eval(self.survey.prob)
# if S_m is not None:
# b[:,i] += 1./(1j*omega(src.freq)) * S_m
return b
def _b(self, eSolution, srcList):
return self._bPrimary(eSolution, srcList) + self._bSecondary(eSolution, srcList)
def _bSecondaryDeriv_u(self, src, v, adjoint = False):
C = self.mesh.nodalGrad
if adjoint:
return - 1./(1j*omega(src.freq)) * (C.T * v)
return - 1./(1j*omega(src.freq)) * (C * v)
def _bSecondaryDeriv_m(self, src, v, adjoint = False):
# Doesn't depend on m
# _, S_eDeriv = src.evalDeriv(self.survey.prob, adjoint)
# S_eDeriv = S_eDeriv(v)
# if S_eDeriv is not None:
# return 1./(1j * omega(src.freq)) * S_eDeriv
return None
def _bDeriv_u(self, src, v, adjoint=False):
# Primary does not depend on u
return self._bSecondaryDeriv_u(src, v, adjoint)
def _bDeriv_m(self, src, v, adjoint=False):
# Assuming the primary does not depend on the model
return self._bSecondaryDeriv_m(src, v, adjoint)
def _fDeriv_u(self, src, v, adjoint=False):
"""
Derivative of the fields object wrt u.
:param NSEMsrc src: NSEM source
:param numpy.ndarray v: random vector of f_sol.size
This function stacks the fields derivatives appropriately
return a vector of size (nreEle+nrbEle)
"""
de_du = v #Utils.spdiag(np.ones((self.nF,)))
db_du = self._bDeriv_u(src, v, adjoint)
# Return the stack
# This doesn't work...
return np.vstack((de_du,db_du))
def _fDeriv_m(self, src, v, adjoint=False):
"""
Derivative of the fields object wrt m.
This function stacks the fields derivatives appropriately
"""
return None
class Fields1D_eTotal(BaseNSEMFields):
"""
Fields storage for the 1D NSEM solution solved with for a total domain formulation.
Used in conjuction with Problem1D_eTotal.
"""
knownFields = {'e_1dSolution':'F'}
aliasFields = {
'e_1d' : ['e_1dSolution','F','_e'],
'e_1dPrimary' : ['e_1dSolution','F','_ePrimary'],
'e_1dSecondary' : ['e_1dSolution','F','_eSecondary'],
'b_1d' : ['e_1dSolution','E','_b'],
'b_1dPrimary' : ['e_1dSolution','E','_bPrimary'],
'b_1dSecondary' : ['e_1dSolution','E','_bSecondary']
}
def __init__(self,mesh,survey,**kwargs):
BaseNSEMFields.__init__(self,mesh,survey,**kwargs)
def _ePrimary(self, eSolution, srcList): def _ePrimary(self, eSolution, srcList):
ePrimary = np.zeros_like(eSolution) ePrimary = np.zeros_like(eSolution)
@@ -99,7 +214,7 @@ class Fields1D_e(BaseMTFields):
""" """
Derivative of the fields object wrt u. Derivative of the fields object wrt u.
:param MTsrc src: MT source :param NSEMsrc src: NSEM source
:param numpy.ndarray v: random vector of f_sol.size :param numpy.ndarray v: random vector of f_sol.size
This function stacks the fields derivatives appropriately This function stacks the fields derivatives appropriately
@@ -120,9 +235,18 @@ class Fields1D_e(BaseMTFields):
""" """
return None return None
class Fields3D_e(BaseMTFields):
###########
# 2D Fields
###########
###########
# 3D Fields
###########
class Fields3D_ePrimSec(BaseNSEMFields):
""" """
Fields storage for the 3D MT solution. Labels polarizations by px and py. Fields storage for the 3D NSEM solution. Labels polarizations by px and py.
:param SimPEG object mesh: The solution mesh :param SimPEG object mesh: The solution mesh
:param SimPEG object survey: A survey object :param SimPEG object survey: A survey object
@@ -147,7 +271,7 @@ class Fields3D_e(BaseMTFields):
} }
def __init__(self,mesh,survey,**kwargs): def __init__(self,mesh,survey,**kwargs):
BaseMTFields.__init__(self,mesh,survey,**kwargs) BaseNSEMFields.__init__(self,mesh,survey,**kwargs)
def _e_pxPrimary(self, e_pxSolution, srcList): def _e_pxPrimary(self, e_pxSolution, srcList):
e_pxPrimary = np.zeros_like(e_pxSolution) e_pxPrimary = np.zeros_like(e_pxSolution)
@@ -228,7 +352,7 @@ class Fields3D_e(BaseMTFields):
b = (C * e_pxSolution) b = (C * e_pxSolution)
for i, src in enumerate(srcList): for i, src in enumerate(srcList):
b[:,i] *= - 1./(1j*omega(src.freq)) b[:,i] *= - 1./(1j*omega(src.freq))
# There is no magnetic source in the MT problem # There is no magnetic source in the NSEM problem
# S_m, _ = src.eval(self.survey.prob) # S_m, _ = src.eval(self.survey.prob)
# if S_m is not None: # if S_m is not None:
# b[:,i] += 1./(1j*omega(src.freq)) * S_m # b[:,i] += 1./(1j*omega(src.freq)) * S_m
@@ -239,7 +363,7 @@ class Fields3D_e(BaseMTFields):
b = (C * e_pySolution) b = (C * e_pySolution)
for i, src in enumerate(srcList): for i, src in enumerate(srcList):
b[:,i] *= - 1./(1j*omega(src.freq)) b[:,i] *= - 1./(1j*omega(src.freq))
# There is no magnetic source in the MT problem # There is no magnetic source in the NSEM problem
# S_m, _ = src.eval(self.survey.prob) # S_m, _ = src.eval(self.survey.prob)
# if S_m is not None: # if S_m is not None:
# b[:,i] += 1./(1j*omega(src.freq)) * S_m # b[:,i] += 1./(1j*omega(src.freq)) * S_m
@@ -302,7 +426,7 @@ class Fields3D_e(BaseMTFields):
""" """
Derivative of the fields object wrt u. Derivative of the fields object wrt u.
:param MTsrc src: MT source :param NSEMsrc src: NSEM source
:param numpy.ndarray v: random vector of f_sol.size :param numpy.ndarray v: random vector of f_sol.size
This function stacks the fields derivatives appropriately This function stacks the fields derivatives appropriately
@@ -319,7 +443,7 @@ class Fields3D_e(BaseMTFields):
""" """
Derivative of the fields object wrt u. Derivative of the fields object wrt u.
:param MTsrc src: MT source :param NSEMsrc src: NSEM source
:param numpy.ndarray v: random vector of f_sol.size :param numpy.ndarray v: random vector of f_sol.size
This function stacks the fields derivatives appropriately This function stacks the fields derivatives appropriately
+560
View File
@@ -0,0 +1,560 @@
from SimPEG.EM.Utils.EMUtils import omega, mu_0
from SimPEG import SolverLU as SimpegSolver, PropMaps, Utils, mkvc, sp, np
from SimPEG.EM.FDEM.ProblemFDEM import BaseFDEMProblem
from SurveyNSEM import Survey, Data
from FieldsNSEM import BaseNSEMFields, Fields1D_ePrimSec, Fields3D_ePrimSec
from SimPEG.NSEM.Utils.MT1Danalytic import getEHfields
import time, sys
class BaseNSEMProblem(BaseFDEMProblem):
"""
Base class for all Natural source problems.
"""
def __init__(self, mesh, **kwargs):
BaseFDEMProblem.__init__(self, mesh, **kwargs)
Utils.setKwargs(self, **kwargs)
# Set the default pairs of the problem
surveyPair = Survey
dataPair = Data
fieldsPair = BaseNSEMFields
# Set the solver
Solver = SimpegSolver
solverOpts = {}
verbose = False
# Notes:
# Use the forward and devs from BaseFDEMProblem
# Might need to add more stuff here.
## NEED to clean up the Jvec and Jtvec to use Zero and Identities for None components.
def Jvec(self, m, v, f=None):
"""
Function to calculate the data sensitivities dD/dm times a vector.
:param numpy.ndarray m (nC, 1) - conductive model
:param numpy.ndarray v (nC, 1) - random vector
:param NSEMfields object (optional) - NSEM fields object, if not given it is calculated
:rtype: NSEMdata object
:return: Data sensitivities wrt m
"""
# Calculate the fields
if f is None:
f= self.fields(m)
# Set current model
self.curModel = m
# Initiate the Jv object
Jv = self.dataPair(self.survey)
# Loop all the frequenies
for freq in self.survey.freqs:
dA_du = self.getA(freq) #
dA_duI = self.Solver(dA_du, **self.solverOpts)
for src in self.survey.getSrcByFreq(freq):
# We need fDeriv_m = df/du*du/dm + df/dm
# Construct du/dm, it requires a solve
# NOTE: need to account for the 2 polarizations in the derivatives.
u_src = f[src,:] # u should be a vector by definition. Need to fix this...
# dA_dm and dRHS_dm should be of size nE,2, so that we can multiply by dA_duI. The 2 columns are each of the polarizations.
dA_dm = self.getADeriv_m(freq, u_src, v) # Size: nE,2 (u_px,u_py) in the columns.
dRHS_dm = self.getRHSDeriv_m(freq, v) # Size: nE,2 (u_px,u_py) in the columns.
if dRHS_dm is None:
du_dm = dA_duI * ( -dA_dm )
else:
du_dm = dA_duI * ( -dA_dm + dRHS_dm )
# Calculate the projection derivatives
for rx in src.rxList:
# Get the projection derivative
# v should be of size 2*nE (for 2 polarizations)
PDeriv_u = lambda t: rx.evalDeriv(src, self.mesh, f, t) # wrt u, we don't have have PDeriv wrt m
Jv[src, rx] = PDeriv_u(mkvc(du_dm))
dA_duI.clean()
# Return the vectorized sensitivities
return mkvc(Jv)
def Jtvec(self, m, v, f=None):
"""
Function to calculate the transpose of the data sensitivities (dD/dm)^T times a vector.
:param numpy.ndarray m (nC, 1) - conductive model
:param numpy.ndarray v (nD, 1) - vector
:param NSEMfields object f (optional) - NSEM fields object, if not given it is calculated
:rtype: NSEMdata object
:return: Data sensitivities wrt m
"""
if f is None:
f = self.fields(m)
self.curModel = m
# Ensure v is a data object.
if not isinstance(v, self.dataPair):
v = self.dataPair(self.survey, v)
Jtv = np.zeros(m.size)
for freq in self.survey.freqs:
AT = self.getA(freq).T
ATinv = self.Solver(AT, **self.solverOpts)
for src in self.survey.getSrcByFreq(freq):
ftype = self._solutionType
f_src = f[src, :] # Need to fix this...
for rx in src.rxList:
# Get the adjoint evalDeriv
# PTv needs to be nE,
PTv = rx.evalDeriv(src, self.mesh, f, mkvc(v[src, rx],2), adjoint=True) # wrt u, need possibility wrt m
# Get the
dA_duIT = ATinv * PTv
dA_dmT = self.getADeriv_m(freq, f_src, mkvc(dA_duIT), adjoint=True)
dRHS_dmT = self.getRHSDeriv_m(freq, mkvc(dA_duIT), adjoint=True)
# Make du_dmT
if dRHS_dmT is None:
du_dmT = -dA_dmT
else:
du_dmT = -dA_dmT + dRHS_dmT
# Select the correct component
# du_dmT needs to be of size nC,
real_or_imag = rx.projComp
if real_or_imag == 'real':
Jtv += du_dmT.real
elif real_or_imag == 'imag':
Jtv += -du_dmT.real
else:
raise Exception('Must be real or imag')
# Clean the factorization, clear memory.
ATinv.clean()
return Jtv
###################################
## 1D problems
###################################
class Problem1D_ePrimSec(BaseNSEMProblem):
"""
A NSEM problem soving a e formulation and primary/secondary fields decomposion.
By eliminating the magnetic flux density using
.. math ::
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} \\right)
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
.. math ::
\\left(\mathbf{C}^T \mathbf{M^e_{\mu^{-1}}} \mathbf{C} + i \omega \mathbf{M^f_\sigma}] \mathbf{e}_{s} =& i \omega \mathbf{M^f_{\delta \sigma}} \mathbf{e}_{p}
which we solve for \\\(\\\mathbf{e_s}\\\). The total field \\\mathbf{e}\\ = \\\mathbf{e_p}\\ + \\\mathbf{e_s}\\.
The primary field is estimated from a background model (commonly half space ).
"""
# From FDEMproblem: Used to project the fields. Currently not used for NSEMproblem.
_solutionType = 'e_1dSolution'
_formulation = 'EF'
fieldsPair = Fields1D_ePrimSec
# Initiate properties
_sigmaPrimary = None
def __init__(self, mesh, **kwargs):
BaseNSEMProblem.__init__(self, mesh, **kwargs)
# self._sigmaPrimary = sigmaPrimary
@property
def MeMui(self):
"""
Edge inner product matrix
"""
if getattr(self, '_MeMui', None) is None:
self._MeMui = self.mesh.getEdgeInnerProduct(1.0/mu_0)
return self._MeMui
@property
def MfSigma(self):
"""
Edge inner product matrix
"""
if getattr(self, '_MfSigma', None) is None:
self._MfSigma = self.mesh.getFaceInnerProduct(self.curModel.sigma)
return self._MfSigma
@property
def sigmaPrimary(self):
"""
A background model, use for the calculation of the primary fields.
"""
return self._sigmaPrimary
@sigmaPrimary.setter
def sigmaPrimary(self, val):
# Note: TODO add logic for val, make sure it is the correct size.
self._sigmaPrimary = val
def getA(self, freq):
"""
Function to get the A matrix.
:param float freq: Frequency
:rtype: scipy.sparse.csr_matrix
:return: A
"""
# Note: need to use the code above since in the 1D problem I want
# e to live on Faces(nodes) and h on edges(cells). Might need to rethink this
# Possible that _fieldType and _eqLocs can fix this
MeMui = self.MeMui
MfSigma = self.MfSigma
C = self.mesh.nodalGrad
# Make A
A = C.T*MeMui*C + 1j*omega(freq)*MfSigma
# Either return full or only the inner part of A
return A
def getADeriv_m(self, freq, u, v, adjoint=False):
"""
The derivative of A wrt sigma
"""
dsig_dm = self.curModel.sigmaDeriv
MeMui = self.MeMui
#
u_src = u['e_1dSolution']
dMfSigma_dm = self.mesh.getFaceInnerProductDeriv(self.curModel.sigma)(u_src) * self.curModel.sigmaDeriv
if adjoint:
return 1j * omega(freq) * ( dMfSigma_dm.T * v )
# Note: output has to be nN/nF, not nC/nE.
# v should be nC
return 1j * omega(freq) * ( dMfSigma_dm * v )
def getRHS(self, freq):
"""
Function to return the right hand side for the system.
:param float freq: Frequency
:rtype: numpy.ndarray (nF, 1), numpy.ndarray (nF, 1)
:return: RHS for 1 polarizations, primary fields
"""
# Get sources for the frequncy(polarizations)
Src = self.survey.getSrcByFreq(freq)[0]
S_e = Src.S_e(self)
return -1j * omega(freq) * S_e
def getRHSDeriv_m(self, freq, v, adjoint=False):
"""
The derivative of the RHS wrt sigma
"""
Src = self.survey.getSrcByFreq(freq)[0]
S_eDeriv = Src.S_eDeriv_m(self, v, adjoint)
return -1j * omega(freq) * S_eDeriv
def fields(self, m):
'''
Function to calculate all the fields for the model m.
:param np.ndarray (nC,) m: Conductivity model
'''
# Set the current model
self.curModel = m
# Make the fields object
F = self.fieldsPair(self.mesh, self.survey)
# Loop over the frequencies
for freq in self.survey.freqs:
if self.verbose:
startTime = time.time()
print 'Starting work for {:.3e}'.format(freq)
sys.stdout.flush()
A = self.getA(freq)
rhs = self.getRHS(freq)
Ainv = self.Solver(A, **self.solverOpts)
e_s = Ainv * rhs
# Store the fields
Src = self.survey.getSrcByFreq(freq)[0]
# NOTE: only store the e_solution(secondary), all other components calculated in the fields object
F[Src, 'e_1dSolution'] = e_s[:,-1] # Only storing the yx polarization as 1d
# Note curl e = -iwb so b = -curl e /iw
# b = -( self.mesh.nodalGrad * e )/( 1j*omega(freq) )
# F[Src, 'b_1d'] = b[:,1]
if self.verbose:
print 'Ran for {:f} seconds'.format(time.time()-startTime)
sys.stdout.flush()
return F
# Note this is not fully functional.
# Missing:
# Fields class corresponding to the fields
# Update Jvec and Jtvec to include all the derivatives components
# Other things ...
class Problem1D_eTotal(BaseNSEMProblem):
"""
A NSEM problem solving a e formulation and a Total bondary domain decompostion.
Solves the equation:
Math:
Have to do this...
Not implement correctly.......
"""
# From FDEMproblem: Used to project the fields. Currently not used for NSEMproblem.
_solutionType = 'e_1dSolution'
_formulation = 'EF'
# fieldsPair = Fields1D_eTotal
def __init__(self, mesh, **kwargs):
BaseNSEMProblem.__init__(self, mesh, **kwargs)
@property
def MeMui(self):
"""
Edge inner product matrix
"""
if getattr(self, '_MeMui', None) is None:
self._MeMui = self.mesh.getEdgeInnerProduct(1.0/mu_0)
return self._MeMui
@property
def MfSigma(self):
"""
Edge inner product matrix
"""
if getattr(self, '_MfSigma', None) is None:
self._MfSigma = self.mesh.getFaceInnerProduct(self.curModel.sigma)
return self._MfSigma
def getA(self, freq, full=False):
"""
Function to get the A matrix.
:param float freq: Frequency
:param logic full: Return full A or the inner part
:rtype: scipy.sparse.csr_matrix
:return: A
"""
MeMui = self.MeMui
MfSigma = self.MfSigma
# Note: need to use the code above since in the 1D problem I want
# e to live on Faces(nodes) and h on edges(cells). Might need to rethink this
# Possible that _fieldType and _eqLocs can fix this
# MeMui = self.MfMui
# MfSigma = self.MfSigma
C = self.mesh.nodalGrad
# Make A
A = C.T*MeMui*C + 1j*omega(freq)*MfSigma
# Either return full or only the inner part of A
if full:
return A
else:
return A[1:-1,1:-1]
def getADeriv_m(self, freq, u, v, adjoint=False):
raise NotImplementedError('getADeriv is not implemented')
def getRHS(self, freq):
"""
Function to return the right hand side for the system.
:param float freq: Frequency
:rtype: numpy.ndarray (nE, 2), numpy.ndarray (nE, 2)
:return: RHS for both polarizations, primary fields
"""
# Get sources for the frequency
# NOTE: Need to use the source information, doesn't really apply in 1D
src = self.survey.getSrcByFreq(freq)
# Get the full A
A = self.getA(freq,full=True)
# Define the outer part of the solution matrix
Aio = A[1:-1,[0,-1]]
Ed, Eu, Hd, Hu = getEHfields(self.mesh,self.curModel.sigma,freq,self.mesh.vectorNx)
Etot = (Ed + Eu)
sourceAmp = 1.0
Etot = ((Etot/Etot[-1])*sourceAmp) # Scale the fields to be equal to sourceAmp at the top
## Note: The analytic solution is derived with e^iwt
eBC = np.r_[Etot[0],Etot[-1]]
# The right hand side
return -Aio*eBC, eBC
def getRHSderiv_m(self, freq, backSigma, u, v, adjoint=False):
raise NotImplementedError('getRHSDeriv not implemented yet')
return None
def fields(self, m):
'''
Function to calculate all the fields for the model m.
:param np.ndarray (nC,) m: Conductivity model
:param np.ndarray (nC,) m_back: Background conductivity model
'''
self.curModel = m
# RHS, CalcFields = self.getRHS(freq,m_back), self.calcFields
F = Fields1D_eTotal(self.mesh, self.survey)
for freq in self.survey.freqs:
if self.verbose:
startTime = time.time()
print 'Starting work for {:.3e}'.format(freq)
sys.stdout.flush()
A = self.getA(freq)
rhs, e_o = self.getRHS(freq)
Ainv = self.Solver(A, **self.solverOpts)
e_i = Ainv * rhs
e = mkvc(np.r_[e_o[0], e_i, e_o[1]],2)
# Store the fields
Src = self.survey.getSrcByFreq(freq)
# NOTE: only store e fields
F[Src, 'e_1dSolution'] = e[:,0]
if self.verbose:
print 'Ran for {:f} seconds'.format(time.time()-startTime)
sys.stdout.flush()
return F
###################################
## 3D problems
###################################
class Problem3D_ePrimSec(BaseNSEMProblem):
"""
A NSEM problem solving a e formulation and a primary/secondary fields decompostion.
By eliminating the magnetic flux density using
.. math ::
\mathbf{b} = \\frac{1}{i \omega}\\left(-\mathbf{C} \mathbf{e} \\right)
we can write Maxwell's equations as a second order system in \\\(\\\mathbf{e}\\\) only:
.. math ::
\\left(\mathbf{C}^T \mathbf{M^f_{\mu^{-1}}} \mathbf{C} + i \omega \mathbf{M^e_\sigma}] \mathbf{e}_{s} =& i \omega \mathbf{M^e_{\delta \sigma}} \mathbf{e}_{p}
which we solve for \\\(\\\mathbf{e_s}\\\). The total field \\\mathbf{e}\\ = \\\mathbf{e_p}\\ + \\\mathbf{e_s}\\.
The primary field is estimated from a background model (commonly as a 1D model).
"""
# From FDEMproblem: Used to project the fields. Currently not used for NSEMproblem.
_solutionType = [ 'e_pxSolution', 'e_pySolution'] # Forces order on the object
_formulation = 'EB'
fieldsPair = Fields3D_ePrimSec
# Initiate properties
_sigmaPrimary = None
def __init__(self, mesh, **kwargs):
BaseNSEMProblem.__init__(self, mesh, **kwargs)
@property
def sigmaPrimary(self):
"""
A background model, use for the calculation of the primary fields.
"""
return self._sigmaPrimary
@sigmaPrimary.setter
def sigmaPrimary(self, val):
# Note: TODO add logic for val, make sure it is the correct size.
self._sigmaPrimary = val
def getA(self, freq):
"""
Function to get the A system.
:param float freq: Frequency
:rtype: scipy.sparse.csr_matrix
:return: A
"""
Mmui = self.MfMui
Msig = self.MeSigma
C = self.mesh.edgeCurl
return C.T*Mmui*C + 1j*omega(freq)*Msig
def getADeriv_m(self, freq, u, v, adjoint=False):
"""
Calculate the derivative of A wrt m.
"""
# Fix u to be a matrix nE,2
# This considers both polarizations and returns a nE,2 matrix for each polarization
if adjoint:
dMe_dsigV = sp.hstack(( self.MeSigmaDeriv( u['e_pxSolution'] ).T, self.MeSigmaDeriv(u['e_pySolution'] ).T ))*v
else:
# Need a nE,2 matrix to be returned
dMe_dsigV = np.hstack(( mkvc(self.MeSigmaDeriv( u['e_pxSolution'] )*v,2), mkvc( self.MeSigmaDeriv(u['e_pySolution'] )*v,2) ))
return 1j * omega(freq) * dMe_dsigV
def getRHS(self, freq):
"""
Function to return the right hand side for the system.
:param float freq: Frequency
:rtype: numpy.ndarray (nE, 2), numpy.ndarray (nE, 2)
:return: RHS for both polarizations, primary fields
"""
# Get sources for the frequncy(polarizations)
Src = self.survey.getSrcByFreq(freq)[0]
S_e = Src.S_e(self)
return -1j * omega(freq) * S_e
def getRHSDeriv_m(self, freq, v, adjoint=False):
"""
The derivative of the RHS with respect to sigma
"""
Src = self.survey.getSrcByFreq(freq)[0]
S_eDeriv = Src.S_eDeriv_m(self, v, adjoint)
return -1j * omega(freq) * S_eDeriv
def fields(self, m):
'''
Function to calculate all the fields for the model m.
:param np.ndarray (nC,) m: Conductivity model
'''
# Set the current model
self.curModel = m
F = self.fieldsPair(self.mesh, self.survey)
for freq in self.survey.freqs:
if self.verbose:
startTime = time.time()
print 'Starting work for {:.3e}'.format(freq)
sys.stdout.flush()
A = self.getA(freq)
rhs = self.getRHS(freq)
# Solve the system
Ainv = self.Solver(A, **self.solverOpts)
e_s = Ainv * rhs
# Store the fields
Src = self.survey.getSrcByFreq(freq)[0]
# Store the fields
# Use self._solutionType
F[Src, 'e_pxSolution'] = e_s[:,0]
F[Src, 'e_pySolution'] = e_s[:,1]
# Note curl e = -iwb so b = -curl/iw
if self.verbose:
print 'Ran for {:f} seconds'.format(time.time()-startTime)
sys.stdout.flush()
Ainv.clean()
return F
+11 -11
View File
@@ -11,9 +11,9 @@ import sys
### Sources ### ### Sources ###
################# #################
class BaseMTSrc(FDEMBaseSrc): class BaseNSEMSrc(FDEMBaseSrc):
''' '''
Sources for the MT problem. Sources for the NSEM problem.
Use the SimPEG BaseSrc, since the source fields share properties with the transmitters. Use the SimPEG BaseSrc, since the source fields share properties with the transmitters.
:param float freq: The frequency of the source :param float freq: The frequency of the source
@@ -29,28 +29,28 @@ class BaseMTSrc(FDEMBaseSrc):
FDEMBaseSrc.__init__(self, rxList) FDEMBaseSrc.__init__(self, rxList)
# 1D sources # 1D sources
class polxy_1DhomotD(BaseMTSrc): class polxy_1DhomotD(BaseNSEMSrc):
""" """
MT source for both polarizations (x and y) for the total Domain. NSEM source for both polarizations (x and y) for the total Domain.
It calculates fields calculated based on conditions on the boundary of the domain. It calculates fields calculated based on conditions on the boundary of the domain.
""" """
def __init__(self, rxList, freq): def __init__(self, rxList, freq):
BaseMTSrc.__init__(self, rxList, freq) BaseNSEMSrc.__init__(self, rxList, freq)
# TODO: need to add the primary fields calc and source terms into the problem. # TODO: need to add the primary fields calc and source terms into the problem.
# Need to implement such that it works for all dims. # Need to implement such that it works for all dims.
class polxy_1Dprimary(BaseMTSrc): class polxy_1Dprimary(BaseNSEMSrc):
""" """
MT source for both polarizations (x and y) given a 1D primary models. NSEM source for both polarizations (x and y) given a 1D primary models.
It assigns fields calculated from the 1D model as fields in the full space of the problem. It assigns fields calculated from the 1D model as fields in the full space of the problem.
""" """
def __init__(self, rxList, freq): def __init__(self, rxList, freq):
# assert mkvc(self.mesh.hz.shape,1) == mkvc(sigma1d.shape,1),'The number of values in the 1D background model does not match the number of vertical cells (hz).' # assert mkvc(self.mesh.hz.shape,1) == mkvc(sigma1d.shape,1),'The number of values in the 1D background model does not match the number of vertical cells (hz).'
self.sigma1d = None self.sigma1d = None
BaseMTSrc.__init__(self, rxList, freq) BaseNSEMSrc.__init__(self, rxList, freq)
# Hidden property of the ePrimary # Hidden property of the ePrimary
self._ePrimary = None self._ePrimary = None
@@ -128,15 +128,15 @@ class polxy_1Dprimary(BaseMTSrc):
# v should be nC size # v should be nC size
return MsigmaDeriv * v return MsigmaDeriv * v
class polxy_3Dprimary(BaseMTSrc): class polxy_3Dprimary(BaseNSEMSrc):
""" """
MT source for both polarizations (x and y) given a 3D primary model. It assigns fields calculated from the 1D model NSEM source for both polarizations (x and y) given a 3D primary model. It assigns fields calculated from the 1D model
as fields in the full space of the problem. as fields in the full space of the problem.
""" """
def __init__(self, rxList, freq): def __init__(self, rxList, freq):
# assert mkvc(self.mesh.hz.shape,1) == mkvc(sigma1d.shape,1),'The number of values in the 1D background model does not match the number of vertical cells (hz).' # assert mkvc(self.mesh.hz.shape,1) == mkvc(sigma1d.shape,1),'The number of values in the 1D background model does not match the number of vertical cells (hz).'
self.sigmaPrimary = None self.sigmaPrimary = None
BaseMTSrc.__init__(self, rxList, freq) BaseNSEMSrc.__init__(self, rxList, freq)
# Hidden property of the ePrimary # Hidden property of the ePrimary
self._ePrimary = None self._ePrimary = None
@@ -4,7 +4,7 @@ from SimPEG.EM.Utils import omega
from scipy.constants import mu_0 from scipy.constants import mu_0
from numpy.lib import recfunctions as recFunc from numpy.lib import recfunctions as recFunc
from Utils import rec2ndarr from Utils import rec2ndarr
import SrcMT import SrcNSEM
import sys import sys
################# #################
@@ -63,9 +63,9 @@ class Rx(SimPEGsurvey.BaseRx):
''' '''
Project the fields to natural source data. Project the fields to natural source data.
:param SrcMT src: The source of the fields to project :param SrcNSEM src: The source of the fields to project
:param SimPEG.Mesh mesh: :param SimPEG.Mesh mesh:
:param FieldsMT f: Natural source fields object to project :param FieldsNSEM f: Natural source fields object to project
''' '''
## NOTE: Assumes that e is on t ## NOTE: Assumes that e is on t
@@ -143,9 +143,9 @@ class Rx(SimPEGsurvey.BaseRx):
""" """
The derivative of the projection wrt u The derivative of the projection wrt u
:param MTsrc src: MT source :param NSEMsrc src: NSEM source
:param TensorMesh mesh: Mesh defining the topology of the problem :param TensorMesh mesh: Mesh defining the topology of the problem
:param MTfields f: MT fields object of the source :param NSEMfields f: NSEM fields object of the source
:param numpy.ndarray v: Random vector of size :param numpy.ndarray v: Random vector of size
""" """
@@ -390,12 +390,12 @@ class Rx(SimPEGsurvey.BaseRx):
################# #################
class Survey(SimPEGsurvey.BaseSurvey): class Survey(SimPEGsurvey.BaseSurvey):
""" """
Survey class for MT. Contains all the sources associated with the survey. Survey class for NSEM. Contains all the sources associated with the survey.
:param list srcList: List of sources associated with the survey :param list srcList: List of sources associated with the survey
""" """
srcPair = SrcMT.BaseMTSrc srcPair = SrcNSEM.BaseNSEMSrc
def __init__(self, srcList, **kwargs): def __init__(self, srcList, **kwargs):
# Sort these by frequency # Sort these by frequency
@@ -443,7 +443,7 @@ class Survey(SimPEGsurvey.BaseSurvey):
################# #################
class Data(SimPEGsurvey.Data): class Data(SimPEGsurvey.Data):
''' '''
Data class for MTdata. Stores the data vector indexed by the survey. Data class for NSEMdata. Stores the data vector indexed by the survey.
:param SimPEG survey object survey: :param SimPEG survey object survey:
:param v vector of the data in order matching of the survey :param v vector of the data in order matching of the survey
@@ -461,7 +461,7 @@ class Data(SimPEGsurvey.Data):
def toRecArray(self,returnType='RealImag'): def toRecArray(self,returnType='RealImag'):
''' '''
Function that returns a numpy.recarray for a SimpegMT impedance data object. Function that returns a numpy.recarray for a SimpegNSEM impedance data object.
:param str returnType: Switches between returning a rec array where the impedance is split to real and imaginary ('RealImag') or is a complex ('Complex') :param str returnType: Switches between returning a rec array where the impedance is split to real and imaginary ('RealImag') or is a complex ('Complex')
@@ -483,7 +483,7 @@ class Data(SimPEGsurvey.Data):
locs = np.hstack((np.array([[0.0]]),locs)) locs = np.hstack((np.array([[0.0]]),locs))
tArrRec = np.concatenate((src.freq*np.ones((locs.shape[0],1)),locs,np.nan*np.ones((locs.shape[0],12))),axis=1).view(dtRI) tArrRec = np.concatenate((src.freq*np.ones((locs.shape[0],1)),locs,np.nan*np.ones((locs.shape[0],12))),axis=1).view(dtRI)
# np.array([(src.freq,rx.locs[0,0],rx.locs[0,1],rx.locs[0,2],np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ) for rx in src.rxList],dtype=dtRI) # np.array([(src.freq,rx.locs[0,0],rx.locs[0,1],rx.locs[0,2],np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ,np.nan ) for rx in src.rxList],dtype=dtRI)
# Get the type and the value for the DataMT object as a list # Get the type and the value for the DataNSEM object as a list
typeList = [[rx.rxType.replace('z1d','zyx'),self[src,rx]] for rx in src.rxList] typeList = [[rx.rxType.replace('z1d','zyx'),self[src,rx]] for rx in src.rxList]
# Insert the values to the temp array # Insert the values to the temp array
for nr,(key,val) in enumerate(typeList): for nr,(key,val) in enumerate(typeList):
@@ -517,17 +517,17 @@ class Data(SimPEGsurvey.Data):
@classmethod @classmethod
def fromRecArray(cls, recArray, srcType='primary'): def fromRecArray(cls, recArray, srcType='primary'):
""" """
Class method that reads in a numpy record array to MTdata object. Class method that reads in a numpy record array to NSEMdata object.
Only imports the impedance data. Only imports the impedance data.
""" """
if srcType=='primary': if srcType=='primary':
src = SrcMT.polxy_1Dprimary src = SrcNSEM.polxy_1Dprimary
elif srcType=='total': elif srcType=='total':
src = SrcMT.polxy_1DhomotD src = SrcNSEM.polxy_1DhomotD
else: else:
raise NotImplementedError('{:s} is not a valid source type for MTdata') raise NotImplementedError('{:s} is not a valid source type for NSEMdata')
# Find all the frequencies in recArray # Find all the frequencies in recArray
uniFreq = np.unique(recArray['freq']) uniFreq = np.unique(recArray['freq'])
@@ -3,7 +3,7 @@
import numpy as np, SimPEG as simpeg import numpy as np, SimPEG as simpeg
from scipy.constants import mu_0, epsilon_0 as eps_0 from scipy.constants import mu_0, epsilon_0 as eps_0
def getEHfields(m1d,sigma,freq,zd,scaleUD=True): def getEHfields(m1d,sigma,freq,zd,scaleUD=True,scaleValue=1):
'''Analytic solution for MT 1D layered earth. Returns E and H fields. '''Analytic solution for MT 1D layered earth. Returns E and H fields.
:param SimPEG.mesh, object m1d: Mesh object with the 1D spatial information. :param SimPEG.mesh, object m1d: Mesh object with the 1D spatial information.
@@ -12,7 +12,7 @@ def getEHfields(m1d,sigma,freq,zd,scaleUD=True):
:param numpy array, vector zd: location to calculate EH fields at :param numpy array, vector zd: location to calculate EH fields at
:param bollean, scaleUD: scales the output to be 1 at the top, increases numeracal stability. :param bollean, scaleUD: scales the output to be 1 at the top, increases numeracal stability.
Assumes a halfspace with the same conductive as the last cell below. Assumes a halfspace with the same conductive as the deepest cell.
''' '''
# Note add an error check for the mesh and sigma are the same size. # Note add an error check for the mesh and sigma are the same size.
@@ -29,7 +29,7 @@ def getEHfields(m1d,sigma,freq,zd,scaleUD=True):
# Initiate the propagation matrix, in the order down up. # Initiate the propagation matrix, in the order down up.
UDp = np.zeros((2,m1d.nC+1),dtype=complex) UDp = np.zeros((2,m1d.nC+1),dtype=complex)
UDp[1,0] = 1. # Set the wave amplitude as 1 into the half-space at the bottom of the mesh UDp[1,0] = scaleValue # Set the wave amplitude as 1 into the half-space at the bottom of the mesh
# Loop over all the layers, starting at the bottom layer # Loop over all the layers, starting at the bottom layer
for lnr, h in enumerate(m1d.hx): # lnr-number of layer, h-thickness of the layer for lnr, h in enumerate(m1d.hx): # lnr-number of layer, h-thickness of the layer
# Calculate # Calculate
@@ -38,9 +38,9 @@ def getEHfields(m1d,sigma,freq,zd,scaleUD=True):
# Build the propagation matrix # Build the propagation matrix
# Convert fields to down/up going components in layer below current layer # Convert fields to down/up going components in layer below current layer
Pj1 = np.array([[1,1],[yp1,-yp1]]) Pj1 = np.array([[1,1],[yp1,-yp1]],dtype=complex)
# Convert fields to down/up going components in current layer # Convert fields to down/up going components in current layer
Pjinv = 1./2*np.array([[1,zp],[1,-zp]]) Pjinv = 1./2*np.array([[1,zp],[1,-zp]],dtype=complex)
# Propagate down and up components through the current layer # Propagate down and up components through the current layer
elamh = np.array([[np.exp(-1j*k[lnr+1]*h),0],[0,np.exp(1j*k[lnr+1]*h)]]) elamh = np.array([[np.exp(-1j*k[lnr+1]*h),0],[0,np.exp(1j*k[lnr+1]*h)]])
@@ -48,7 +48,14 @@ def getEHfields(m1d,sigma,freq,zd,scaleUD=True):
UDp[:,lnr+1] = elamh.dot(Pjinv.dot(Pj1)).dot(UDp[:,lnr]) UDp[:,lnr+1] = elamh.dot(Pjinv.dot(Pj1)).dot(UDp[:,lnr])
if scaleUD: if scaleUD:
UDp[:,lnr+1::-1] = UDp[:,lnr+1::-1]/UDp[1,lnr+1] # Scale the values such that 1 at the top
scaleVal = UDp[:,lnr+1::-1]/UDp[1,lnr+1]
if np.any(np.isnan(scaleVal)):
# If there is a nan (thickness very great), rebuild the move up cell
scaleVal = np.zeros_like(UDp[:,lnr+1::-1],dtype=complex)
scaleVal[1,0] = scaleValue
UDp[:,lnr+1::-1] = scaleVal
# Calculate the fields # Calculate the fields
Ed = np.empty((zd.size,),dtype=complex) Ed = np.empty((zd.size,),dtype=complex)
+5
View File
@@ -0,0 +1,5 @@
from MT1Dsolutions import get1DEfields # Add the names of the functions
from MT1Danalytic import getEHfields, getImpedance
from dataUtils import *
from ediFilesUtils import *
from testUtils import *
@@ -5,25 +5,25 @@ import numpy.lib.recfunctions as recFunc
from scipy.constants import mu_0 from scipy.constants import mu_0
from scipy import interpolate as sciint from scipy import interpolate as sciint
def getAppRes(MTdata): def getAppRes(NSEMdata):
# Make impedance # Make impedance
zList = [] zList = []
for src in MTdata.survey.srcList: for src in NSEMdata.survey.srcList:
zc = [src.freq] zc = [src.freq]
for rx in src.rxList: for rx in src.rxList:
if 'i' in rx.rxType: if 'i' in rx.rxType:
m=1j m=1j
else: else:
m = 1 m = 1
zc.append(m*MTdata[src,rx]) zc.append(m*NSEMdata[src,rx])
zList.append(zc) zList.append(zc)
return [appResPhs(zList[i][0],np.sum(zList[i][1:3])) for i in np.arange(len(zList))] return [appResPhs(zList[i][0],np.sum(zList[i][1:3])) for i in np.arange(len(zList))]
def rotateData(MTdata, rotAngle): def rotateData(NSEMdata,rotAngle):
''' '''
Function that rotates clockwist by rotAngle (- negative for a counter-clockwise rotation) Function that rotates clockwist by rotAngle (- negative for a counter-clockwise rotation)
''' '''
recData = MTdata.toRecArray('Complex') recData = NSEMdata.toRecArray('Complex')
impData = rec2ndarr(recData[['zxx','zxy','zyx','zyy']],complex) impData = rec2ndarr(recData[['zxx','zxy','zyx','zyy']],complex)
# Make the rotation matrix # Make the rotation matrix
# c,s,zxx,zxy,zyx,zyy = sympy.symbols('c,s,zxx,zxy,zyx,zyy') # c,s,zxx,zxy,zyx,zyy = sympy.symbols('c,s,zxx,zxy,zyx,zyy')
@@ -40,27 +40,27 @@ def rotateData(MTdata, rotAngle):
for nr,comp in enumerate(['zxx','zxy','zyx','zyy']): for nr,comp in enumerate(['zxx','zxy','zyx','zyy']):
outRec[comp] = rotData[:,nr] outRec[comp] = rotData[:,nr]
from SimPEG import MT from SimPEG import NSEM
return MT.Data.fromRecArray(outRec) return NSEM.Data.fromRecArray(outRec)
def appResPhs(freq, z): def appResPhs(freq,z):
app_res = ((1./(8e-7*np.pi**2))/freq)*np.abs(z)**2 app_res = ((1./(8e-7*np.pi**2))/freq)*np.abs(z)**2
app_phs = np.arctan2(z.imag,z.real)*(180/np.pi) app_phs = np.arctan2(z.imag,z.real)*(180/np.pi)
return app_res, app_phs return app_res, app_phs
def skindepth(rho, freq): def skindepth(rho,freq):
''' Function to calculate the skindepth of EM waves''' ''' Function to calculate the skindepth of EM waves'''
return np.sqrt( (rho*((1/(freq * mu_0 * np.pi ))))) return np.sqrt( (rho*((1/(freq * mu_0 * np.pi )))))
def rec2ndarr(x, dt=float): def rec2ndarr(x,dt=float):
return x.view((dt, len(x.dtype.names))) return x.view((dt, len(x.dtype.names)))
def makeAnalyticSolution(mesh, model, elev, freqs): def makeAnalyticSolution(mesh,model,elev,freqs):
from SimPEG import MT from SimPEG import NSEM
data1D = [] data1D = []
for freq in freqs: for freq in freqs:
anaEd, anaEu, anaHd, anaHu = MT.Utils.MT1Danalytic.getEHfields(mesh,model,freq,elev) anaEd, anaEu, anaHd, anaHu = NSEM.Utils.MT1Danalytic.getEHfields(mesh,model,freq,elev)
anaE = anaEd+anaEu anaE = anaEd+anaEu
anaH = anaHd+anaHu anaH = anaHd+anaHu
@@ -70,8 +70,8 @@ def makeAnalyticSolution(mesh, model, elev, freqs):
dataRec = np.array(data1D,dtype=[('freq',float),('x',float),('y',float),('z',float),('zyx',complex)]) dataRec = np.array(data1D,dtype=[('freq',float),('x',float),('y',float),('z',float),('zyx',complex)])
return dataRec return dataRec
def plotMT1DModelData(problem, models, symList=None): def plotMT1DModelData(problem,models,symList=None):
from SimPEG import MT from SimPEG import NSEM
# Setup the figure # Setup the figure
fontSize = 15 fontSize = 15
@@ -79,7 +79,7 @@ def plotMT1DModelData(problem, models, symList=None):
axM = fig.add_axes([0.075,.1,.25,.875]) axM = fig.add_axes([0.075,.1,.25,.875])
axM.set_xlabel('Resistivity [Ohm*m]',fontsize=fontSize) axM.set_xlabel('Resistivity [Ohm*m]',fontsize=fontSize)
axM.set_xlim(1e-1,1e5) axM.set_xlim(1e-1,1e5)
axM.set_ylim(-10000,5000) # axM.set_ylim(-10000,5000)
axM.set_ylabel('Depth [km]',fontsize=fontSize) axM.set_ylabel('Depth [km]',fontsize=fontSize)
axR = fig.add_axes([0.42,.575,.5,.4]) axR = fig.add_axes([0.42,.575,.5,.4])
axR.set_xscale('log') axR.set_xscale('log')
@@ -132,38 +132,94 @@ def plotMT1DModelData(problem, models, symList=None):
freq = simpeg.mkvc(data1D['freq'],2) freq = simpeg.mkvc(data1D['freq'],2)
res, phs = appResPhs(freq,allData) res, phs = appResPhs(freq,allData)
stdCol = 'gray' if False:
axRtw = axR.twinx() stdCol = 'gray'
axRtw.set_ylabel('Std of log10',color=stdCol) axRtw = axR.twinx()
[(t.set_color(stdCol), t.set_rotation(-45)) for t in axRtw.get_yticklabels()] axRtw.set_ylabel('Std of log10',color=stdCol)
axPtw = axP.twinx() [(t.set_color(stdCol), t.set_rotation(-45)) for t in axRtw.get_yticklabels()]
axPtw.set_ylabel('Std ',color=stdCol) axPtw = axP.twinx()
[t.set_color(stdCol) for t in axPtw.get_yticklabels()] axPtw.set_ylabel('Std ',color=stdCol)
axRtw.plot(freq, np.std(np.log10(res),1),'--',color=stdCol) [t.set_color(stdCol) for t in axPtw.get_yticklabels()]
axPtw.plot(freq, np.std(phs,1),'--',color=stdCol) axRtw.plot(freq, np.std(np.log10(res),1),'--',color=stdCol)
axPtw.plot(freq, np.std(phs,1),'--',color=stdCol)
# Fix labels and ticks # Fix labels and ticks
yMtick = [l/1000 for l in axM.get_yticks().tolist()] # yMtick = [l/1000 for l in axM.get_yticks().tolist()]
axM.set_yticklabels(yMtick) # axM.set_yticklabels(yMtick)
[ l.set_rotation(90) for l in axM.get_yticklabels()] [ l.set_rotation(90) for l in axM.get_yticklabels()]
[ l.set_rotation(90) for l in axR.get_yticklabels()] [ l.set_rotation(90) for l in axR.get_yticklabels()]
[(t.set_color(stdCol), t.set_rotation(-45)) for t in axRtw.get_yticklabels()] # [(t.set_color(stdCol), t.set_rotation(-45)) for t in axRtw.get_yticklabels()]
[t.set_color(stdCol) for t in axPtw.get_yticklabels()] # [t.set_color(stdCol) for t in axPtw.get_yticklabels()]
for ax in [axM,axR,axP]: for ax in [axM,axR,axP]:
ax.xaxis.set_tick_params(labelsize=fontSize) ax.xaxis.set_tick_params(labelsize=fontSize)
ax.yaxis.set_tick_params(labelsize=fontSize) ax.yaxis.set_tick_params(labelsize=fontSize)
return fig return fig
def plotImpAppRes(dataArrays,plotLoc,textStr=[]):
''' Plots amplitude impedance and phase'''
# fig = plt.figure(1,(7, 7))
import plotDataTypes as pDt
# axes = ImageGrid(fig, (0.05,0.05,0.875,0.875),nrows_ncols = (2, 2),axes_pad = 0.25,add_all=True,share_all=True,label_mode = "L")
# Make the figure and axes
fig,axT=plt.subplots(2,2,sharex=True)
axes = axT.ravel()
fig.set_size_inches((13.5,7.0))
fig.suptitle('{:s}\nStation at: {:.1f}x ; {:.1f}y'.format(textStr,plotLoc[0],plotLoc[1]))
# Have to deal with axes
# Set log
for ax in axes.ravel():
ax.set_xscale('log')
axes[0].invert_xaxis()
axes[0].set_yscale('log')
axes[2].set_yscale('log')
# Set labels
axes[2].set_xlabel('Frequency [Hz]')
axes[3].set_xlabel('Frequency [Hz]')
axes[0].set_ylabel('Apperent resistivity [Ohm m]')
axes[1].set_ylabel('Apperent phase [degrees]')
axes[1].set_ylim(-180,180)
axes[2].set_ylabel('Impedance amplitude [V/A]')
axes[3].set_ylim(-180,180)
axes[3].set_ylabel('Impedance angle [degrees]')
# Plot the data
for nr,dataArray in enumerate(dataArrays):
if nr==1:
parSym = '*'
else:
parSym = 's'
# app res
pDt.plotIsoStaImpedance(axes[0],plotLoc,dataArray,'zxy',par='res',pSym=parSym)
pDt.plotIsoStaImpedance(axes[0],plotLoc,dataArray,'zyx',par='res',pSym=parSym)
# app phs
pDt.plotIsoStaImpedance(axes[1],plotLoc,dataArray,'zxy',par='phs',pSym=parSym)
pDt.plotIsoStaImpedance(axes[1],plotLoc,dataArray,'zyx',par='phs',pSym=parSym)
# imp abs
pDt.plotIsoStaImpedance(axes[2],plotLoc,dataArray,'zxx',par='abs',pSym=parSym)
pDt.plotIsoStaImpedance(axes[2],plotLoc,dataArray,'zxy',par='abs',pSym=parSym)
pDt.plotIsoStaImpedance(axes[2],plotLoc,dataArray,'zyx',par='abs',pSym=parSym)
pDt.plotIsoStaImpedance(axes[2],plotLoc,dataArray,'zyy',par='abs',pSym=parSym)
# imp abs
pDt.plotIsoStaImpedance(axes[3],plotLoc,dataArray,'zxx',par='phs',pSym=parSym)
pDt.plotIsoStaImpedance(axes[3],plotLoc,dataArray,'zxy',par='phs',pSym=parSym)
pDt.plotIsoStaImpedance(axes[3],plotLoc,dataArray,'zyx',par='phs',pSym=parSym)
pDt.plotIsoStaImpedance(axes[3],plotLoc,dataArray,'zyy',par='phs',pSym=parSym)
return fig,axes
def printTime(): def printTime():
import time import time
print time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime()) print time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime())
def convert3Dto1Dobject(MTdata,rxType3D='zyx'): def convert3Dto1Dobject(NSEMdata,rxType3D='zyx'):
from SimPEG import MT from SimPEG import NSEM
# Find the unique locations # Find the unique locations
# Need to find the locations # Need to find the locations
recDataTemp = MTdata.toRecArray() recDataTemp = NSEMdata.toRecArray()
# Check if survey.std has been assigned. # Check if survey.std has been assigned.
## NEED TO: write this... ## NEED TO: write this...
# Calculte and add the DET of the tensor to the recArray # Calculte and add the DET of the tensor to the recArray
@@ -185,24 +241,24 @@ def convert3Dto1Dobject(MTdata,rxType3D='zyx'):
# Make the receiver list # Make the receiver list
rx1DList = [] rx1DList = []
for rxType in ['z1dr','z1di']: for rxType in ['z1dr','z1di']:
rx1DList.append(MT.Rx(simpeg.mkvc(loc,2).T,rxType)) rx1DList.append(NSEM.Rx(simpeg.mkvc(loc,2).T,rxType))
# Source list # Source list
locrecData = recData[np.sqrt(np.sum( (rec2ndarr(recData[['x','y','z']]).data - loc )**2,axis=1)) < 1e-5] locrecData = recData[np.sqrt(np.sum( (rec2ndarr(recData[['x','y','z']]).data - loc )**2,axis=1)) < 1e-5]
dat1DList = [] dat1DList = []
src1DList = [] src1DList = []
for freq in locrecData['freq']: for freq in locrecData['freq']:
src1DList.append(MT.SrcMT.src_polxy_1Dprimary(rx1DList,freq)) src1DList.append(NSEM.SrcNSEM.src_polxy_1Dprimary(rx1DList,freq))
for comp in ['r','i']: for comp in ['r','i']:
dat1DList.append( corr * locrecData[rxType3D+comp][locrecData['freq']== freq].data ) dat1DList.append( corr * locrecData[rxType3D+comp][locrecData['freq']== freq].data )
# Make the survey # Make the survey
sur1D = MT.Survey(src1DList) sur1D = NSEM.Survey(src1DList)
# Make the data # Make the data
dataVec = np.hstack(dat1DList) dataVec = np.hstack(dat1DList)
dat1D = MT.Data(sur1D,dataVec) dat1D = NSEM.Data(sur1D,dataVec)
sur1D.dobs = dataVec sur1D.dobs = dataVec
# Need to take MTdata.survey.std and split it as well. # Need to take NSEMdata.survey.std and split it as well.
std=0.05 std=0.05
sur1D.std = np.abs(sur1D.dobs*std) #+ 0.01*np.linalg.norm(sur1D.dobs) sur1D.std = np.abs(sur1D.dobs*std) #+ 0.01*np.linalg.norm(sur1D.dobs)
mtData1DList.append(dat1D) mtData1DList.append(dat1D)
@@ -210,29 +266,29 @@ def convert3Dto1Dobject(MTdata,rxType3D='zyx'):
# Return the the list of data. # Return the the list of data.
return mtData1DList return mtData1DList
def resampleMTdataAtFreq(MTdata,freqs): def resampleNSEMdataAtFreq(NSEMdata,freqs):
""" """
Function to resample MTdata at set of frequencies Function to resample NSEMdata at set of frequencies
""" """
from SimPEG import MT from SimPEG import NSEM
# Make a rec array # Make a rec array
MTrec = MTdata.toRecArray().data NSEMrec = NSEMdata.toRecArray().data
# Find unique locations # Find unique locations
uniLoc = np.unique(MTrec[['x','y','z']]) uniLoc = np.unique(NSEMrec[['x','y','z']])
uniFreq = MTdata.survey.freqs uniFreq = NSEMdata.survey.freqs
# Get the comps # Get the comps
dNames = MTrec.dtype dNames = NSEMrec.dtype
# Loop over all the locations and interpolate # Loop over all the locations and interpolate
for loc in uniLoc: for loc in uniLoc:
# Find the index of the station # Find the index of the station
ind = np.sqrt(np.sum((rec2ndarr(MTrec[['x','y','z']]) - rec2ndarr(loc))**2,axis=1)) < 1. # Find dist of 1 m accuracy ind = np.sqrt(np.sum((rec2ndarr(NSEMrec[['x','y','z']]) - rec2ndarr(loc))**2,axis=1)) < 1. # Find dist of 1 m accuracy
# Make a temporary recArray and interpolate all the components # Make a temporary recArray and interpolate all the components
tArrRec = np.concatenate((simpeg.mkvc(freqs,2),np.ones((len(freqs),1))*rec2ndarr(loc),np.nan*np.ones((len(freqs),12))),axis=1).view(dNames) tArrRec = np.concatenate((simpeg.mkvc(freqs,2),np.ones((len(freqs),1))*rec2ndarr(loc),np.nan*np.ones((len(freqs),12))),axis=1).view(dNames)
for comp in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']: for comp in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']:
int1d = sciint.interp1d(MTrec[ind]['freq'],MTrec[ind][comp],bounds_error=False) int1d = sciint.interp1d(NSEMrec[ind]['freq'],NSEMrec[ind][comp],bounds_error=False)
tArrRec[comp] = simpeg.mkvc(int1d(freqs),2) tArrRec[comp] = simpeg.mkvc(int1d(freqs),2)
# Join together # Join together
@@ -241,5 +297,5 @@ def resampleMTdataAtFreq(MTdata,freqs):
except NameError as e: except NameError as e:
outRecArr = tArrRec outRecArr = tArrRec
# Make the MTdata and return # Make the NSEMdata and return
return MT.Data.fromRecArray(outRecArr) return NSEM.Data.fromRecArray(outRecArr)
@@ -2,7 +2,7 @@
from SimPEG import mkvc from SimPEG import mkvc
from scipy.constants import mu_0 from scipy.constants import mu_0
from numpy.lib import recfunctions as recFunc from numpy.lib import recfunctions as recFunc
from SimPEG.MT.Utils.dataUtils import rec2ndarr from SimPEG.NSEM.Utils.dataUtils import rec2ndarr
# Import modules # Import modules
import numpy as np import numpy as np
@@ -12,7 +12,7 @@ def homo1DModelSource(mesh,freq,sigma_1d):
''' '''
# import # import
from SimPEG.MT.Utils import get1DEfields from SimPEG.NSEM.Utils import get1DEfields
# Get a 1d solution for a halfspace background # Get a 1d solution for a halfspace background
if mesh.dim == 1: if mesh.dim == 1:
mesh1d = mesh mesh1d = mesh
@@ -77,7 +77,7 @@ def analytic1DModelSource(mesh,freq,sigma_1d):
''' '''
# import # import
from SimPEG.MT.Utils import getEHfields from SimPEG.NSEM.Utils import getEHfields
# Get a 1d solution for a halfspace background # Get a 1d solution for a halfspace background
if mesh.dim == 1: if mesh.dim == 1:
mesh1d = mesh mesh1d = mesh
+198
View File
@@ -0,0 +1,198 @@
import unittest
import sys
from scipy.constants import mu_0
import SimPEG as simpeg
from SimPEG.Utils import meshTensor
import numpy as np
np.random.seed(1100)
# Define the tolerances
TOLr = 5e-2
TOLp = 5e-2
def getAppResPhs(NSEMdata):
# Make impedance
from SimPEG.NSEM.Utils import appResPhs
zList = []
for src in NSEMdata.survey.srcList:
zc = [src.freq]
for rx in src.rxList:
if 'i' in rx.rxType:
m=1j
else:
m = 1
zc.append(m*NSEMdata[src,rx])
zList.append(zc)
return [appResPhs(zList[i][0],np.sum(zList[i][1:3])) for i in np.arange(len(zList))]
def setup1DSurvey(sigmaHalf,tD=True,structure=False):
from SimPEG import NSEM
# Frequency
nFreq = 33
freqs = np.logspace(3,-3,nFreq)
# Make the mesh
ct = 5
air = meshTensor([(ct,25,1.3)])
# coreT0 = meshTensor([(ct,15,1.2)])
# coreT1 = np.kron(meshTensor([(coreT0[-1],15,1.3)]),np.ones((7,)))
core = np.concatenate( ( np.kron(meshTensor([(ct,15,-1.2)]),np.ones((10,))) , meshTensor([(ct,20)]) ) )
bot = meshTensor([(core[0],20,-1.3)])
x0 = -np.array([np.sum(np.concatenate((core,bot)))])
m1d = simpeg.Mesh.TensorMesh([np.concatenate((bot,core,air))], x0=x0)
# Make the model
sigma = np.zeros(m1d.nC) + sigmaHalf
sigma[m1d.gridCC > 0 ] = 1e-8
sigmaBack = sigma.copy()
# Add structure
if structure:
shallow = (m1d.gridCC < -200) * (m1d.gridCC > -600)
deep = (m1d.gridCC < -3000) * (m1d.gridCC > -5000)
sigma[shallow] = 1
sigma[deep] = 0.1
rxList = []
for rxType in ['z1dr','z1di']:
rxList.append(NSEM.Rx(simpeg.mkvc(np.array([0.0]),2).T,rxType))
# Source list
srcList =[]
if tD:
for freq in freqs:
srcList.append(NSEM.SrcNSEM.polxy_1DhomotD(rxList,freq))
else:
for freq in freqs:
srcList.append(NSEM.SrcNSEM.polxy_1Dprimary(rxList,freq))
survey = NSEM.Survey(srcList)
return survey, sigma, m1d
def setupSimpegNSEM_ePrimSec(inputSetup,comp='Imp',singleFreq=False,expMap=True):
from SimPEG import NSEM
M,freqs,sig,sigBG,rx_loc = inputSetup
# Make a receiver list
rxList = []
if comp == 'All':
for rxType in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi','tzxr','tzxi','tzyr','tzyi']:
rxList.append(NSEM.Rx(rx_loc,rxType))
elif comp == 'Imp':
for rxType in ['zxxr','zxxi','zxyr','zxyi','zyxr','zyxi','zyyr','zyyi']:
rxList.append(NSEM.Rx(rx_loc,rxType))
elif comp == 'Tip':
for rxType in ['tzxr','tzxi','tzyr','tzyi']:
rxList.append(NSEM.Rx(rx_loc,rxType))
else:
rxList.append(NSEM.Rx(rx_loc,comp))
# Source list
srcList =[]
if singleFreq:
srcList.append(NSEM.SrcNSEM.polxy_1Dprimary(rxList,singleFreq))
else:
for freq in freqs:
srcList.append(NSEM.SrcNSEM.polxy_1Dprimary(rxList,freq))
# Survey NSEM
survey = NSEM.Survey(srcList)
## Setup the problem object
sigma1d = M.r(sigBG,'CC','CC','M')[0,0,:]
if expMap:
problem = NSEM.Problem3D_ePrimSec(M,sigmaPrimary= np.log(sigma1d) )
problem.mapping = simpeg.Maps.ExpMap(problem.mesh)
problem.curModel = np.log(sig)
else:
problem = NSEM.Problem3D_ePrimSec(M,sigmaPrimary= sigma1d)
problem.curModel = sig
problem.pair(survey)
problem.verbose = False
try:
from pymatsolver import MumpsSolver
problem.Solver = MumpsSolver
except:
pass
return (survey, problem)
def getInputs():
"""
Function that returns Mesh, freqs, rx_loc, elev.
"""
# Make a mesh
# M = simpeg.Mesh.TensorMesh([[(100,5,-1.5),(100.,10),(100,5,1.5)],[(100,5,-1.5),(100.,10),(100,5,1.5)],[(100,5,1.6),(100.,10),(100,3,2)]], x0=['C','C',-3529.5360])
# M = simpeg.Mesh.TensorMesh([[(1000,6,-1.5),(1000.,6),(1000,6,1.5)],[(1000,6,-1.5),(1000.,2),(1000,6,1.5)],[(1000,6,-1.3),(1000.,6),(1000,6,1.3)]], x0=['C','C','C'])# Setup the model
M = simpeg.Mesh.TensorMesh([[(200,6,-1.5),(200.,4),(200,6,1.5)],[(200,6,-1.5),(200.,4),(200,6,1.5)],[(200,8,-1.5),(200.,8),(200,8,1.5)]], x0=['C','C','C'])# Setup the model
# Set the frequencies
freqs = np.logspace(1,-3,5)
elev = 0
## Setup the the survey object
# Receiver locations
rx_x, rx_y = np.meshgrid(np.arange(-350,350,200),np.arange(-350,350,200))
rx_loc = np.hstack((simpeg.Utils.mkvc(rx_x,2),simpeg.Utils.mkvc(rx_y,2),elev+np.zeros((np.prod(rx_x.shape),1))))
return M, freqs, rx_loc, elev
def random(conds):
''' Returns a halfspace model based on the inputs'''
M, freqs, rx_loc, elev = getInputs()
# Backround
sigBG = np.ones(M.nC)*conds
# Add randomness to the model (10% of the value).
sig = np.exp( np.log(sigBG) + np.random.randn(M.nC)*(conds)*1e-1 )
return (M, freqs, sig, sigBG, rx_loc)
def halfSpace(conds):
''' Returns a halfspace model based on the inputs'''
M, freqs, rx_loc, elev = getInputs()
# Model
ccM = M.gridCC
# conds = [1e-2]
groundInd = ccM[:,2] < elev
sig = np.zeros(M.nC) + 1e-8
sig[groundInd] = conds
# Set the background, not the same as the model
sigBG = np.zeros(M.nC) + 1e-8
sigBG[groundInd] = conds
return (M, freqs, sig, sigBG, rx_loc)
def blockInhalfSpace(conds):
''' Returns a halfspace model based on the inputs'''
M, freqs, rx_loc, elev = getInputs()
# Model
ccM = M.gridCC
# conds = [1e-2]
groundInd = ccM[:,2] < elev
sig = simpeg.Utils.ModelBuilder.defineBlock(M.gridCC,np.array([-1000,-1000,-1500]),np.array([1000,1000,-1000]),conds)
sig[~groundInd] = 1e-8
# Set the background, not the same as the model
sigBG = np.zeros(M.nC) + 1e-8
sigBG[groundInd] = conds[1]
return (M, freqs, sig, sigBG, rx_loc)
def twoLayer(conds):
''' Returns a 2 layer model based on the conductivity values given'''
M, freqs, rx_loc, elev = getInputs()
# Model
ccM = M.gridCC
groundInd = ccM[:,2] < elev
botInd = ccM[:,2] < -3000
sig = np.zeros(M.nC) + 1e-8
sig[groundInd] = conds[1]
sig[botInd] = conds[0]
# Set the background, not the same as the model
sigBG = np.zeros(M.nC) + 1e-8
sigBG[groundInd] = conds[1]
return (M, freqs, sig, sigBG, rx_loc)
+5
View File
@@ -0,0 +1,5 @@
import Utils
from SurveyNSEM import Rx, Survey, Data
from FieldsNSEM import Fields1D_ePrimSec, Fields3D_ePrimSec
from ProblemNSEM import Problem1D_ePrimSec, Problem3D_ePrimSec
import SrcNSEM
+8 -8
View File
@@ -121,7 +121,7 @@ class Minimize(object):
@callback.setter @callback.setter
def callback(self, value): def callback(self, value):
if self.callback is not None: if self.callback is not None:
print 'The callback on the {0!s} Optimization was replaced.'.format(self.__name__) print 'The callback on the %s Optimization was replaced.' % self.__name__
self._callback = value self._callback = value
@@ -131,7 +131,7 @@ class Minimize(object):
Minimizes the function (evalFunction) starting at the location x0. Minimizes the function (evalFunction) starting at the location x0.
:param callable evalFunction: function handle that evaluates: f, g, H = F(x) :param def evalFunction: function handle that evaluates: f, g, H = F(x)
:param numpy.ndarray x0: starting location :param numpy.ndarray x0: starting location
:rtype: numpy.ndarray :rtype: numpy.ndarray
:return: x, the last iterate of the optimization algorithm :return: x, the last iterate of the optimization algorithm
@@ -372,8 +372,8 @@ class Minimize(object):
Else, a modifySearchDirectionBreak call is preformed. Else, a modifySearchDirectionBreak call is preformed.
:param numpy.ndarray p: searchDirection :param numpy.ndarray p: searchDirection
:rtype: tuple :rtype: numpy.ndarray,bool
:return: (xt, passLS) numpy.ndarray, bool :return: (xt, passLS)
""" """
# Projected Armijo linesearch # Projected Armijo linesearch
self._LS_t = 1 self._LS_t = 1
@@ -408,8 +408,8 @@ class Minimize(object):
evalFunction returns a False indicating the break was not caught. evalFunction returns a False indicating the break was not caught.
:param numpy.ndarray p: searchDirection :param numpy.ndarray p: searchDirection
:rtype: tuple :rtype: numpy.ndarray,bool
:return: (xt, breakCaught) numpy.ndarray, bool :return: (xt, breakCaught)
""" """
self.printDone(inLS=True) self.printDone(inLS=True)
print 'The linesearch got broken. Boo.' print 'The linesearch got broken. Boo.'
@@ -855,7 +855,7 @@ class NewtonRoot(object):
if self.comments and self.doLS: print '\tLinesearch:\n' if self.comments and self.doLS: print '\tLinesearch:\n'
# Enter Linesearch # Enter Linesearch
while True and self.doLS: while True and self.doLS:
if self.comments: print '\t\tResid: {0:e}\n'.format(norm(rt)) if self.comments: print '\t\tResid: %e\n'%norm(rt)
if norm(rt) <= norm(r) or norm(rt) < self.tol: if norm(rt) <= norm(r) or norm(rt) < self.tol:
break break
@@ -873,7 +873,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 ({0:d}). norm: {1:4.4e}'.format(self.maxIter, norm(rt)) print 'NewtonRoot stopped by maxIters (%d). norm: %4.4e' % (self.maxIter, norm(rt))
break break
return x return x
+1 -1
View File
@@ -49,7 +49,7 @@ class BaseProblem(object):
def pair(self, d): def pair(self, d):
"""Bind a survey to this problem instance using pointers.""" """Bind a survey to this problem instance using pointers."""
assert isinstance(d, self.surveyPair), "Data object must be an instance of a {0!s} class.".format((self.surveyPair.__name__)) assert isinstance(d, self.surveyPair), "Data object must be an instance of a %s class."%(self.surveyPair.__name__)
if d.ispaired: if d.ispaired:
raise Exception("The survey object is already paired to a problem. Use survey.unpair()") raise Exception("The survey object is already paired to a problem. Use survey.unpair()")
self._survey = d self._survey = d
+29 -29
View File
@@ -19,85 +19,85 @@ class Property(object):
return getattr(self, '_propertyLink', None) return getattr(self, '_propertyLink', None)
@propertyLink.setter @propertyLink.setter
def propertyLink(self, value): 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: ("{0!s}", Maps.ReciprocalMap)'.format(self.name) 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 self._propertyLink = value
def _getMapProperty(self): def _getMapProperty(self):
prop = self prop = self
def fget(self): def fget(self):
return getattr(self, '_{0!s}Map'.format(prop.name), None) return getattr(self, '_%sMap'%prop.name, None)
def fset(self, val): def fset(self, val):
if prop.propertyLink is not None: if prop.propertyLink is not None:
linkName, linkMap = prop.propertyLink linkName, linkMap = prop.propertyLink
assert getattr(self, '{0!s}Map'.format(linkName), None) is None, 'Cannot set both sides of a linked property.' assert getattr(self, '%sMap'%linkName, None) is None, 'Cannot set both sides of a linked property.'
# TODO: Check if the mapping can be correct # TODO: Check if the mapping can be correct
setattr(self, '_{0!s}Map'.format(prop.name), val) setattr(self, '_%sMap'%prop.name, val)
return property(fget=fget, fset=fset, doc=prop.doc) return property(fget=fget, fset=fset, doc=prop.doc)
def _getIndexProperty(self): def _getIndexProperty(self):
prop = self prop = self
def fget(self): def fget(self):
return getattr(self, '_{0!s}Index'.format(prop.name), slice(None)) return getattr(self, '_%sIndex'%prop.name, slice(None))
def fset(self, val): def fset(self, val):
setattr(self, '_{0!s}Index'.format(prop.name), val) setattr(self, '_%sIndex'%prop.name, val)
return property(fget=fget, fset=fset, doc=prop.doc) return property(fget=fget, fset=fset, doc=prop.doc)
def _getProperty(self): def _getProperty(self):
prop = self prop = self
def fget(self): def fget(self):
mapping = getattr(self, '{0!s}Map'.format(prop.name)) mapping = getattr(self, '%sMap'%prop.name)
if mapping is None and prop.propertyLink is None: if mapping is None and prop.propertyLink is None:
return prop.defaultVal return prop.defaultVal
if mapping is None and prop.propertyLink is not None: if mapping is None and prop.propertyLink is not None:
linkName, linkMapClass = prop.propertyLink linkName, linkMapClass = prop.propertyLink
linkMap = linkMapClass(None) linkMap = linkMapClass(None)
if getattr(self, '{0!s}Map'.format(linkName), None) is None: if getattr(self, '%sMap'%linkName, None) is None:
return prop.defaultVal return prop.defaultVal
m = getattr(self, '{0!s}'.format(linkName)) m = getattr(self, '%s'%linkName)
return linkMap * m return linkMap * m
m = getattr(self, '{0!s}Model'.format(prop.name)) m = getattr(self, '%sModel'%prop.name)
return mapping * m return mapping * m
return property(fget=fget) return property(fget=fget)
def _getModelDerivProperty(self): def _getModelDerivProperty(self):
prop = self prop = self
def fget(self): def fget(self):
mapping = getattr(self, '{0!s}Map'.format(prop.name)) mapping = getattr(self, '%sMap'%prop.name)
if mapping is None and prop.propertyLink is None: if mapping is None and prop.propertyLink is None:
return None return None
if mapping is None and prop.propertyLink is not None: if mapping is None and prop.propertyLink is not None:
linkName, linkMapClass = prop.propertyLink linkName, linkMapClass = prop.propertyLink
linkedMap = getattr(self, '{0!s}Map'.format(linkName)) linkedMap = getattr(self, '%sMap'%linkName)
if linkedMap is None: if linkedMap is None:
return None return None
linkMap = linkMapClass(None) * linkedMap linkMap = linkMapClass(None) * linkedMap
m = getattr(self, '{0!s}Model'.format(linkName)) m = getattr(self, '%sModel'%linkName)
return linkMap.deriv( m ) return linkMap.deriv( m )
m = getattr(self, '{0!s}Model'.format(prop.name)) m = getattr(self, '%sModel'%prop.name)
return mapping.deriv( m ) return mapping.deriv( m )
return property(fget=fget) return property(fget=fget)
def _getModelProperty(self): def _getModelProperty(self):
prop = self prop = self
def fget(self): def fget(self):
mapping = getattr(self, '{0!s}Map'.format(prop.name)) mapping = getattr(self, '%sMap'%prop.name)
if mapping is None: if mapping is None:
return None return None
index = getattr(self.propMap, '{0!s}Index'.format(prop.name)) index = getattr(self.propMap, '%sIndex'%prop.name)
return self.vector[index] return self.vector[index]
return property(fget=fget) return property(fget=fget)
def _getModelProjProperty(self): def _getModelProjProperty(self):
prop = self prop = self
def fget(self): def fget(self):
mapping = getattr(self, '{0!s}Map'.format(prop.name)) mapping = getattr(self, '%sMap'%prop.name)
if mapping is None: if mapping is None:
return None return None
inds = getattr(self.propMap, '{0!s}Index'.format(prop.name)) inds = getattr(self.propMap, '%sIndex'%prop.name)
if type(inds) is slice: if type(inds) is slice:
inds = range(*inds.indices(self.nP)) inds = range(*inds.indices(self.nP))
nI, nP = len(inds),self.nP nI, nP = len(inds),self.nP
@@ -107,7 +107,7 @@ class Property(object):
def _getModelMapProperty(self): def _getModelMapProperty(self):
prop = self prop = self
def fget(self): def fget(self):
return getattr(self.propMap, '_{0!s}Map'.format(prop.name), None) return getattr(self.propMap, '_%sMap'%prop.name, None)
return property(fget=fget) return property(fget=fget)
@@ -123,7 +123,7 @@ class PropModel(object):
inds = [] inds = []
if getattr(self, '_nP', None) is None: if getattr(self, '_nP', None) is None:
for name in self.propMap._properties: for name in self.propMap._properties:
index = getattr(self.propMap, '{0!s}Index'.format(name), None) index = getattr(self.propMap, '%sIndex'%name, None)
if index is not None: if index is not None:
if type(index) is slice: if type(index) is slice:
inds += range(*index.indices(len(self.vector))) inds += range(*index.indices(len(self.vector)))
@@ -163,9 +163,9 @@ class _PropMapMetaClass(type):
if prop.defaultInvProp: if prop.defaultInvProp:
defaultInvProps += [p] defaultInvProps += [p]
if prop.propertyLink is not None: if prop.propertyLink is not None:
assert prop.propertyLink[0] in _properties, "You can only link to things that exist: '{0!s}' is trying to link to '{1!s}'".format(prop.name, prop.propertyLink[0]) assert prop.propertyLink[0] in _properties, "You can only link to things that exist: '%s' is trying to link to '%s'"%(prop.name, prop.propertyLink[0])
if len(defaultInvProps) > 1: if len(defaultInvProps) > 1:
raise Exception('You have more than one default inversion property: {0!s}'.format(defaultInvProps)) raise Exception('You have more than one default inversion property: %s' % defaultInvProps)
newClass = super(_PropMapMetaClass, cls).__new__(cls, name, bases, attrs) newClass = super(_PropMapMetaClass, cls).__new__(cls, name, bases, attrs)
@@ -187,7 +187,7 @@ class _PropMapMetaClass(type):
attrs[attr + 'Model'] = prop._getModelProperty() attrs[attr + 'Model'] = prop._getModelProperty()
attrs[attr + 'Deriv'] = prop._getModelDerivProperty() attrs[attr + 'Deriv'] = prop._getModelDerivProperty()
return type('PropModel', (PropModel, ), attrs) return type(name.replace('PropMap', 'PropModel'), (PropModel, ), attrs)
class PropMap(object): class PropMap(object):
@@ -223,7 +223,7 @@ class PropMap(object):
type(m[0]) is str and type(m[0]) is str and
m[0] in self._properties and m[0] in self._properties and
isinstance(m[1], Maps.IdentityMap) isinstance(m[1], Maps.IdentityMap)
for m in maps]), "Use signature: [{0!s}]".format((', '.join(["('{0!s}', {1!s}Map)".format(p, p) for p in self._properties]))) for m in maps]), "Use signature: [%s]" % (', '.join(["('%s', %sMap)"%(p,p) for p in self._properties]))
if slices is None: if slices is None:
slices = dict() slices = dict()
else: else:
@@ -236,8 +236,8 @@ class PropMap(object):
nP = 0 nP = 0
for name, mapping in maps: for name, mapping in maps:
setattr(self, '{0!s}Map'.format(name), mapping) setattr(self, '%sMap'%name, mapping)
setattr(self, '{0!s}Index'.format(name), slices.get(name, slice(nP, nP + mapping.nP))) setattr(self, '%sIndex'%name, slices.get(name, slice(nP, nP + mapping.nP)))
nP += mapping.nP nP += mapping.nP
self.nP = nP self.nP = nP
@@ -250,12 +250,12 @@ class PropMap(object):
def clearMaps(self): def clearMaps(self):
for name in self._properties: for name in self._properties:
setattr(self, '{0!s}Map'.format(name), None) setattr(self, '%sMap'%name, None)
setattr(self, '{0!s}Index'.format(name), None) setattr(self, '%sIndex'%name, None)
def __call__(self, vec): def __call__(self, vec):
return self.PropModel(self, vec) return self.PropModel(self, vec)
def __contains__(self, val): def __contains__(self, val):
activeMaps = [name for name in self._properties if getattr(self, '{0!s}Map'.format(name)) is not None] activeMaps = [name for name in self._properties if getattr(self, '%sMap'%name) is not None]
return val in activeMaps return val in activeMaps
+6 -6
View File
@@ -10,7 +10,7 @@ class RegularizationMesh(object):
are not necessarily true differential operators, but are constructed from are not necessarily true differential operators, but are constructed from
a SimPEG Mesh. a SimPEG Mesh.
:param BaseMesh mesh: problem mesh :param Mesh mesh: problem mesh
:param numpy.array indActive: bool array, size nC, that is True where we have active cells. Used to reduce the operators so we regularize only on active cells :param numpy.array indActive: bool array, size nC, that is True where we have active cells. Used to reduce the operators so we regularize only on active cells
""" """
@@ -383,8 +383,8 @@ class BaseRegularization(object):
:param numpy.array m: geophysical model :param numpy.array m: geophysical model
:param numpy.array v: vector to multiply :param numpy.array v: vector to multiply
:rtype: scipy.sparse.csr_matrix :rtype: scipy.sparse.csr_matrix or numpy.ndarray
:return: WtW, or if v is supplied WtW*v (numpy.ndarray) :return: WtW or WtW*v
The regularization is: The regularization is:
@@ -650,8 +650,8 @@ class Tikhonov(Simple):
Note if the key word argument `mrefInSmooth` is False, then mref is not Note if the key word argument `mrefInSmooth` is False, then mref is not
included in the smoothness contribution. included in the smoothness contribution.
:param BaseMesh mesh: SimPEG mesh :param Mesh mesh: SimPEG mesh
:param IdentityMap mapping: regularization mapping, takes the model from model space to the thing you want to regularize :param Maps mapping: regularization mapping, takes the model from model space to the thing you want to regularize
:param numpy.ndarray indActive: active cell indices for reducing the size of differential operators in the definition of a regularization mesh :param numpy.ndarray indActive: active cell indices for reducing the size of differential operators in the definition of a regularization mesh
:param bool mrefInSmooth: (default = False) put mref in the smoothness component? :param bool mrefInSmooth: (default = False) put mref in the smoothness component?
:param float alpha_s: (default 1e-6) smallness weight :param float alpha_s: (default 1e-6) smallness weight
@@ -671,7 +671,7 @@ class Tikhonov(Simple):
alpha_yy = Utils.dependentProperty('_alpha_yy', 0.0, ['_W', '_Wyy'], "Weight for the second derivative in the y direction") alpha_yy = Utils.dependentProperty('_alpha_yy', 0.0, ['_W', '_Wyy'], "Weight for the second derivative in the y direction")
alpha_zz = Utils.dependentProperty('_alpha_zz', 0.0, ['_W', '_Wzz'], "Weight for the second derivative in the z direction") alpha_zz = Utils.dependentProperty('_alpha_zz', 0.0, ['_W', '_Wzz'], "Weight for the second derivative in the z direction")
def __init__(self, mesh, mapping=None, indActive=None, **kwargs): def __init__(self, mesh, mapping=None, indActive = None, **kwargs):
BaseRegularization.__init__(self, mesh, mapping=mapping, indActive=indActive, **kwargs) BaseRegularization.__init__(self, mesh, mapping=mapping, indActive=indActive, **kwargs)
@property @property
+9 -8
View File
@@ -26,7 +26,7 @@ class BaseRx(object):
def rxType(self, value): def rxType(self, value):
known = self.knownRxTypes known = self.knownRxTypes
if known is not None: if known is not None:
assert value in known, "rxType must be in ['{0!s}']".format(("', '".join(known))) assert value in known, "rxType must be in ['%s']" % ("', '".join(known))
self._rxType = value self._rxType = value
@property @property
@@ -125,7 +125,7 @@ class BaseSrc(object):
def __init__(self, rxList, **kwargs): def __init__(self, rxList, **kwargs):
assert type(rxList) is list, 'rxList must be a list' assert type(rxList) is list, 'rxList must be a list'
for rx in rxList: for rx in rxList:
assert isinstance(rx, self.rxPair), 'rxList must be a {0!s}'.format(self.rxPair.__name__) assert isinstance(rx, self.rxPair), 'rxList must be a %s'%self.rxPair.__name__
assert len(set(rxList)) == len(rxList), 'The rxList must be unique' assert len(set(rxList)) == len(rxList), 'The rxList must be unique'
self.uid = str(uuid.uuid4()) self.uid = str(uuid.uuid4())
self.rxList = rxList self.rxList = rxList
@@ -227,7 +227,7 @@ class BaseSurvey(object):
@srcList.setter @srcList.setter
def srcList(self, value): def srcList(self, value):
assert type(value) is list, 'srcList must be a list' assert type(value) is list, 'srcList must be a list'
assert np.all([isinstance(src, self.srcPair) for src in value]), 'All sources must be instances of {0!s}'.format(self.srcPair.__name__) assert np.all([isinstance(src, self.srcPair) for src in value]), 'All sources must be instances of %s' % self.srcPair.__name__
assert len(set(value)) == len(value), 'The srcList must be unique' assert len(set(value)) == len(value), 'The srcList must be unique'
self._srcList = value self._srcList = value
self._sourceOrder = dict() self._sourceOrder = dict()
@@ -238,10 +238,10 @@ class BaseSurvey(object):
sources = [sources] sources = [sources]
for src in sources: for src in sources:
if getattr(src,'uid',None) is None: if getattr(src,'uid',None) is None:
raise KeyError('Source does not have a uid: {0!s}'.format(str(src))) raise KeyError('Source does not have a uid: %s'%str(src))
inds = map(lambda src: self._sourceOrder.get(src.uid, None), sources) inds = map(lambda src: self._sourceOrder.get(src.uid, None), sources)
if None in inds: if None in inds:
raise KeyError('Some of the sources specified are not in this survey. {0!s}'.format(str(inds))) raise KeyError('Some of the sources specified are not in this survey. %s'%str(inds))
return inds return inds
@property @property
@@ -263,7 +263,7 @@ class BaseSurvey(object):
def pair(self, p): def pair(self, p):
"""Bind a problem to this survey instance using pointers""" """Bind a problem to this survey instance using pointers"""
assert hasattr(p, 'surveyPair'), "Problem must have an attribute 'surveyPair'." assert hasattr(p, 'surveyPair'), "Problem must have an attribute 'surveyPair'."
assert isinstance(self, p.surveyPair), "Problem requires survey object must be an instance of a {0!s} class.".format((p.surveyPair.__name__)) assert isinstance(self, p.surveyPair), "Problem requires survey object must be an instance of a %s class."%(p.surveyPair.__name__)
if p.ispaired: if p.ispaired:
raise Exception("The problem object is already paired to a survey. Use prob.unpair()") raise Exception("The problem object is already paired to a survey. Use prob.unpair()")
self._prob = p self._prob = p
@@ -311,6 +311,7 @@ class BaseSurvey(object):
if f is None: f = self.prob.fields(m) if f is None: f = self.prob.fields(m)
return Utils.mkvc(self.eval(f)) return Utils.mkvc(self.eval(f))
@Utils.count @Utils.count
def eval(self, f): def eval(self, f):
"""eval(f) """eval(f)
@@ -321,7 +322,7 @@ class BaseSurvey(object):
d_\\text{pred} = \mathbf{P} f(m) d_\\text{pred} = \mathbf{P} f(m)
""" """
raise NotImplementedError('eval is not yet implemented.') raise NotImplemented('eval is not yet implemented.')
@Utils.count @Utils.count
def evalDeriv(self, f): def evalDeriv(self, f):
@@ -333,7 +334,7 @@ class BaseSurvey(object):
\\frac{\partial d_\\text{pred}}{\partial u} = \mathbf{P} \\frac{\partial d_\\text{pred}}{\partial u} = \mathbf{P}
""" """
raise NotImplementedError('eval is not yet implemented.') raise NotImplemented('eval is not yet implemented.')
@Utils.count @Utils.count
def residual(self, m, f=None): def residual(self, m, f=None):
+10 -9
View File
@@ -4,6 +4,7 @@ from SimPEG.Utils import mkvc, sdiag, diagEst
from SimPEG import Utils from SimPEG import Utils
from SimPEG.Mesh import TensorMesh, CurvilinearMesh, CylMesh from SimPEG.Mesh import TensorMesh, CurvilinearMesh, CylMesh
from SimPEG.Mesh.TreeMesh import TreeMesh as Tree from SimPEG.Mesh.TreeMesh import TreeMesh as Tree
import numpy as np
import scipy.sparse as sp import scipy.sparse as sp
import unittest import unittest
import inspect import inspect
@@ -199,10 +200,10 @@ class OrderTest(unittest.TestCase):
print '_____________________________________________' print '_____________________________________________'
print ' h | error | e(i-1)/e(i) | order' print ' h | error | e(i-1)/e(i) | order'
print '~~~~~~|~~~~~~~~~~~~~|~~~~~~~~~~~~~|~~~~~~~~~~' print '~~~~~~|~~~~~~~~~~~~~|~~~~~~~~~~~~~|~~~~~~~~~~'
print '{0:4d} | {1:8.2e} |'.format(nc, err) print '%4i | %8.2e |' % (nc, err)
else: else:
order.append(np.log(err/err_old)/np.log(max_h/max_h_old)) order.append(np.log(err/err_old)/np.log(max_h/max_h_old))
print '{0:4d} | {1:8.2e} | {2:6.4f} | {3:6.4f}'.format(nc, err, err_old/err, order[-1]) print '%4i | %8.2e | %6.4f | %6.4f' % (nc, err, err_old/err, order[-1])
err_old = err err_old = err
max_h_old = max_h max_h_old = max_h
print '---------------------------------------------' print '---------------------------------------------'
@@ -236,7 +237,7 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None, expectedOrder=2, tole
Compares error decay of 0th and 1st order Taylor approximation at point Compares error decay of 0th and 1st order Taylor approximation at point
x0 for a randomized search direction. x0 for a randomized search direction.
:param callable fctn: function handle :param lambda fctn: function handle
:param numpy.array x0: point at which to check derivative :param numpy.array x0: point at which to check derivative
:param int num: number of times to reduce step length, h :param int num: number of times to reduce step length, h
:param bool plotIt: if you would like to plot :param bool plotIt: if you would like to plot
@@ -257,8 +258,8 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None, expectedOrder=2, tole
Tests.checkDerivative(simplePass, np.random.randn(5)) Tests.checkDerivative(simplePass, np.random.randn(5))
""" """
print "{0!s} checkDerivative {1!s}".format('='*20, '='*20) print "%s checkDerivative %s" % ('='*20, '='*20)
print "iter h |ft-f0| |ft-f0-h*J0*dx| Order\n{0!s}".format(('-'*57)) print "iter h |ft-f0| |ft-f0-h*J0*dx| Order\n%s" % ('-'*57)
f0, J0 = fctn(x0) f0, J0 = fctn(x0)
@@ -289,7 +290,7 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None, expectedOrder=2, tole
order0 = np.log10(E0[:-1]/E0[1:]) order0 = np.log10(E0[:-1]/E0[1:])
order1 = np.log10(E1[:-1]/E1[1:]) order1 = np.log10(E1[:-1]/E1[1:])
print " {0:d} {1:1.2e} {2:1.3e} {3:1.3e} {4:1.3f}".format(i, h[i], E0[i], E1[i], np.nan if i == 0 else order1[i-1]) print " %d %1.2e %1.3e %1.3e %1.3f" % (i, h[i], E0[i], E1[i], np.nan if i == 0 else order1[i-1])
# Ensure we are about precision # Ensure we are about precision
order0 = order0[E0[1:] > eps] order0 = order0[E0[1:] > eps]
@@ -301,10 +302,10 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None, expectedOrder=2, tole
passTest = belowTol or correctOrder passTest = belowTol or correctOrder
if passTest: if passTest:
print "{0!s} PASS! {1!s}".format('='*25, '='*25) print "%s PASS! %s" % ('='*25, '='*25)
print happiness[np.random.randint(len(happiness))]+'\n' print happiness[np.random.randint(len(happiness))]+'\n'
else: else:
print "{0!s}\n{1!s} FAIL! {2!s}\n{3!s}".format('*'*57, '<'*25, '>'*25, '*'*57) print "%s\n%s FAIL! %s\n%s" % ('*'*57, '<'*25, '>'*25, '*'*57)
print sadness[np.random.randint(len(sadness))]+'\n' print sadness[np.random.randint(len(sadness))]+'\n'
@@ -313,7 +314,7 @@ def checkDerivative(fctn, x0, num=7, plotIt=True, dx=None, expectedOrder=2, tole
ax = ax or plt.subplot(111) ax = ax or plt.subplot(111)
ax.loglog(h, E0, 'b') ax.loglog(h, E0, 'b')
ax.loglog(h, E1, 'g--') ax.loglog(h, E1, 'g--')
ax.set_title('Check Derivative - {0!s}'.format(('PASSED :)' if passTest else 'FAILED :('))) ax.set_title('Check Derivative - %s' % ('PASSED :)' if passTest else 'FAILED :('))
ax.set_xlabel('h') ax.set_xlabel('h')
ax.set_ylabel('Error') ax.set_ylabel('Error')
leg = ax.legend(['$\mathcal{O}(h)$', '$\mathcal{O}(h^2)$'], loc='best', leg = ax.legend(['$\mathcal{O}(h)$', '$\mathcal{O}(h^2)$'], loc='best',
+13 -13
View File
@@ -7,11 +7,11 @@ def addBlock(gridCC, modelCC, p0, p1, blockProp):
""" """
Add a block to an exsisting cell centered model, modelCC Add a block to an exsisting cell centered model, modelCC
:param numpy.array gridCC: mesh.gridCC is the cell centered grid :param numpy.array, gridCC: mesh.gridCC is the cell centered grid
:param numpy.array modelCC: cell centered model :param numpy.array, modelCC: cell centered model
:param numpy.array p0: bottom, southwest corner of block :param numpy.array, p0: bottom, southwest corner of block
:param numpy.array p1: top, northeast corner of block :param numpy.array, p1: top, northeast corner of block
:blockProp float blockProp: property to assign to the model :blockProp float, blockProp: property to assign to the model
:return numpy.array, modelBlock: model with block :return numpy.array, modelBlock: model with block
""" """
@@ -147,7 +147,7 @@ def getIndicesSphere(center,radius,ccMesh):
if dimMesh == 1: if dimMesh == 1:
# Define the reference points # Define the reference points
ind = np.abs(center[0] - ccMesh[:,0]) < radius ind = np.abs(center[0] - ccMesh[:,0]) < radius
elif dimMesh == 2: elif dimMesh == 2:
@@ -222,14 +222,14 @@ def layeredModel(ccMesh, layerTops, layerValues):
:param numpy.array ccMesh: cell-centered mesh :param numpy.array ccMesh: cell-centered mesh
:param numpy.array layerTops: z-locations of the tops of each layer :param numpy.array layerTops: z-locations of the tops of each layer
:param numpy.array layerValue: values of the property to assign for each layer (starting at the top) :param numpy.array layerValue: values of the property to assign for each layer (starting at the top)
:rtype: numpy.array :rtype: numpy.array
:return: M, layered model on the mesh :return: M, layered model on the mesh
""" """
descending = np.linalg.norm(sorted(layerTops, reverse=True) - layerTops) < 1e-20 descending = np.linalg.norm(sorted(layerTops, reverse=True) - layerTops) < 1e-20
# TODO: put an error check to make sure that there is an ordering... needs to work with inf elts # TODO: put an error check to make sure that there is an ordering... needs to work with inf elts
# assert ascending or descending, "Layers must be listed in either ascending or descending order" # assert ascending or descending, "Layers must be listed in either ascending or descending order"
# start from bottom up # start from bottom up
@@ -253,10 +253,10 @@ def layeredModel(ccMesh, layerTops, layerValues):
model = np.zeros(ccMesh.shape[0]) model = np.zeros(ccMesh.shape[0])
for i, top in enumerate(layerTops): for i, top in enumerate(layerTops):
zind = z <= top zind = z <= top
model[zind] = layerValues[i] model[zind] = layerValues[i]
return model return model
@@ -265,9 +265,9 @@ def randomModel(shape, seed=None, anisotropy=None, its=100, bounds=None):
Create a random model by convolving a kernel with a Create a random model by convolving a kernel with a
uniformly distributed model. uniformly distributed model.
:param tuple shape: shape of the model. :param int,tuple shape: shape of the model.
:param int seed: pick which model to produce, prints the seed if you don't choose. :param int seed: pick which model to produce, prints the seed if you don't choose.
:param numpy.ndarray anisotropy: this is the (3 x n) blurring kernel that is used. :param numpy.ndarray,list anisotropy: this is the (3 x n) blurring kernel that is used.
:param int its: number of smoothing iterations :param int its: number of smoothing iterations
:param list bounds: bounds on the model, len(list) == 2 :param list bounds: bounds on the model, len(list) == 2
:rtype: numpy.ndarray :rtype: numpy.ndarray
+8 -8
View File
@@ -8,12 +8,12 @@ def _checkAccuracy(A, b, X, accuracyTol):
if nrm_b > 0: if nrm_b > 0:
nrm /= nrm_b nrm /= nrm_b
if nrm > accuracyTol: if nrm > accuracyTol:
msg = '### SolverWarning ###: Accuracy on solve is above tolerance: {0:e} > {1:e}'.format(nrm, accuracyTol) msg = '### SolverWarning ###: Accuracy on solve is above tolerance: %e > %e' % (nrm, accuracyTol)
print msg print msg
warnings.warn(msg, RuntimeWarning) warnings.warn(msg, RuntimeWarning)
def SolverWrapD(fun, factorize=True, checkAccuracy=True, accuracyTol=1e-6, name=None): def SolverWrapD(fun, factorize=True, checkAccuracy=True, accuracyTol=1e-6):
""" """
Wraps a direct Solver. Wraps a direct Solver.
@@ -72,11 +72,11 @@ def SolverWrapD(fun, factorize=True, checkAccuracy=True, accuracyTol=1e-6, name=
if factorize and hasattr(self.solver, 'clean'): if factorize and hasattr(self.solver, 'clean'):
return self.solver.clean() return self.solver.clean()
return type(name if name is not None else fun.__name__, (object,), {"__init__": __init__, "clean": clean, "__mul__": __mul__}) return type(fun.__name__+'_Wrapped', (object,), {"__init__": __init__, "clean": clean, "__mul__": __mul__})
def SolverWrapI(fun, checkAccuracy=True, accuracyTol=1e-5, name=None): def SolverWrapI(fun, checkAccuracy=True, accuracyTol=1e-5):
""" """
Wraps an iterative Solver. Wraps an iterative Solver.
@@ -128,13 +128,13 @@ def SolverWrapI(fun, checkAccuracy=True, accuracyTol=1e-5, name=None):
def clean(self): def clean(self):
pass pass
return type(name if name is not None else fun.__name__, (object,), {"__init__": __init__, "clean": clean, "__mul__": __mul__}) return type(fun.__name__+'_Wrapped', (object,), {"__init__": __init__, "clean": clean, "__mul__": __mul__})
from scipy.sparse import linalg from scipy.sparse import linalg
Solver = SolverWrapD(linalg.spsolve, factorize=False, name="Solver") Solver = SolverWrapD(linalg.spsolve, factorize=False)
SolverLU = SolverWrapD(linalg.splu, factorize=True, name="SolverLU") SolverLU = SolverWrapD(linalg.splu, factorize=True)
SolverCG = SolverWrapI(linalg.cg, name="SolverCG") SolverCG = SolverWrapI(linalg.cg)
class SolverDiag(object): class SolverDiag(object):
+15 -15
View File
@@ -32,7 +32,7 @@ def memProfileWrapper(towrap, *funNames):
if hasattr(towrap,f): if hasattr(towrap,f):
attrs[f] = profile(getattr(towrap,f)) attrs[f] = profile(getattr(towrap,f))
else: else:
print '{0!s} not found in {1!s} Class'.format(f, towrap.__name__) print '%s not found in %s Class' % (f, towrap.__name__)
return type(towrap.__name__ + 'MemProfileWrap', (towrap,), attrs) return type(towrap.__name__ + 'MemProfileWrap', (towrap,), attrs)
@@ -65,7 +65,7 @@ def setKwargs(obj, ignore=None, **kwargs):
if hasattr(obj, attr): if hasattr(obj, attr):
setattr(obj, attr, kwargs[attr]) setattr(obj, attr, kwargs[attr])
else: else:
raise Exception('{0!s} attr is not recognized'.format(attr)) raise Exception('%s attr is not recognized' % attr)
hook(obj,hook, silent=True) hook(obj,hook, silent=True)
hook(obj,setKwargs, silent=True) hook(obj,setKwargs, silent=True)
@@ -74,7 +74,7 @@ def printTitles(obj, printers, name='Print Titles', pad=''):
titles = '' titles = ''
widths = 0 widths = 0
for printer in printers: for printer in printers:
titles += ('{{:^{0:d}}}'.format(printer['width'])).format(printer['title']) + '' titles += ('{:^%i}'%printer['width']).format(printer['title']) + ''
widths += printer['width'] widths += printer['width']
print pad + "{0} {1} {0}".format('='*((widths-1-len(name))/2), name) print pad + "{0} {1} {0}".format('='*((widths-1-len(name))/2), name)
print pad + titles print pad + titles
@@ -83,7 +83,7 @@ def printTitles(obj, printers, name='Print Titles', pad=''):
def printLine(obj, printers, pad=''): def printLine(obj, printers, pad=''):
values = '' values = ''
for printer in printers: for printer in printers:
values += ('{{:^{0:d}}}'.format(printer['width'])).format(printer['format'] % printer['value'](obj)) values += ('{:^%i}'%printer['width']).format(printer['format'] % printer['value'](obj))
print pad + values print pad + values
def checkStoppers(obj, stoppers): def checkStoppers(obj, stoppers):
@@ -104,12 +104,12 @@ def checkStoppers(obj, stoppers):
return (len(optimal)>0 and all(optimal)) | (len(critical)>0 and any(critical)) return (len(optimal)>0 and all(optimal)) | (len(critical)>0 and any(critical))
def printStoppers(obj, stoppers, pad='', stop='STOP!', done='DONE!'): def printStoppers(obj, stoppers, pad='', stop='STOP!', done='DONE!'):
print pad + "{0!s}{1!s}{2!s}".format('-'*25, stop, '-'*25) print pad + "%s%s%s" % ('-'*25,stop,'-'*25)
for stopper in stoppers: for stopper in stoppers:
l = stopper['left'](obj) l = stopper['left'](obj)
r = stopper['right'](obj) r = stopper['right'](obj)
print pad + stopper['str'] % (l<=r,l,r) print pad + stopper['str'] % (l<=r,l,r)
print pad + "{0!s}{1!s}{2!s}".format('-'*25, done, '-'*25) print pad + "%s%s%s" % ('-'*25,done,'-'*25)
def callHooks(match, mainFirst=False): def callHooks(match, mainFirst=False):
""" """
@@ -144,14 +144,14 @@ def callHooks(match, mainFirst=False):
extra = """ extra = """
If you have things that also need to run in the method {0!s}, you can create a method:: If you have things that also need to run in the method %s, you can create a method::
def _{1!s}*(self, ... ): def _%s*(self, ... ):
pass pass
Where the * can be any string. If present, _{2!s}* will be called at the start of the default {3!s} call. Where the * can be any string. If present, _%s* will be called at the start of the default %s call.
You may also completely overwrite this function. You may also completely overwrite this function.
""".format(match, match, match, match) """ % (match, match, match, match)
doc = wrapper.__doc__ doc = wrapper.__doc__
wrapper.__doc__ = ('' if doc is None else doc) + extra wrapper.__doc__ = ('' if doc is None else doc) + extra
return wrapper return wrapper
@@ -186,7 +186,7 @@ def asArray_N_x_Dim(pts, dim):
elif len(pts.shape) == 1: elif len(pts.shape) == 1:
pts = pts[:,np.newaxis] pts = pts[:,np.newaxis]
assert pts.shape[1] == dim, "pts must be a column vector of shape (nPts, {0:d}) not ({1:d}, {2:d})".format(*((dim,)+pts.shape)) assert pts.shape[1] == dim, "pts must be a column vector of shape (nPts, %d) not (%d, %d)" % ((dim,)+pts.shape)
return pts return pts
@@ -207,17 +207,17 @@ def requires(var):
.. note:: .. note::
To use survey.{0!s}(), SimPEG requires that a problem be bound to the survey. To use survey.%s(), SimPEG requires that a problem be bound to the survey.
If a problem has not been bound, an Exception will be raised. If a problem has not been bound, an Exception will be raised.
To bind a problem to the Data object:: To bind a problem to the Data object::
survey.pair(myProblem) survey.pair(myProblem)
""".format(f.__name__) """ % f.__name__
else: else:
extra = """ extra = """
To use *{0!s}* method, SimPEG requires that the {1!s} be specified. To use *%s* method, SimPEG requires that the %s be specified.
""".format(f.__name__, var) """ % (f.__name__, var)
@wraps(f) @wraps(f)
def requiresVarWrapper(self,*args,**kwargs): def requiresVarWrapper(self,*args,**kwargs):
if getattr(self, var, None) is None: if getattr(self, var, None) is None:
+1 -1
View File
@@ -80,7 +80,7 @@ def indexCube(nodes, gridSize, n=None):
# Make sure that we choose from the possible nodes. # Make sure that we choose from the possible nodes.
possibleNodes = 'ABCD' if gridSize.size == 2 else 'ABCDEFGH' possibleNodes = 'ABCD' if gridSize.size == 2 else 'ABCDEFGH'
for node in nodes: for node in nodes:
assert node in possibleNodes, "Nodes must be chosen from: '{0!s}'".format(possibleNodes) assert node in possibleNodes, "Nodes must be chosen from: '%s'" % possibleNodes
dim = gridSize.size dim = gridSize.size
if n is None: if n is None:
n = gridSize - 1 n = gridSize - 1
+1 -1
View File
@@ -25,7 +25,7 @@ def interpmat(locs, x, y=None, z=None):
:param numpy.ndarray x: Tensor vector of 1st dimension of grid. :param numpy.ndarray x: Tensor vector of 1st dimension of grid.
:param numpy.ndarray y: Tensor vector of 2nd dimension of grid. None by default. :param numpy.ndarray y: Tensor vector of 2nd dimension of grid. None by default.
:param numpy.ndarray z: Tensor vector of 3rd dimension of grid. None by default. :param numpy.ndarray z: Tensor vector of 3rd dimension of grid. None by default.
:rtype: scipy.sparse.csr_matrix :rtype: scipy.sparse.csr.csr_matrix
:return: Interpolation matrix :return: Interpolation matrix
.. plot:: .. plot::
+7 -7
View File
@@ -27,7 +27,7 @@ def mkvc(x, numDims=1):
if isinstance(x, Zero): if isinstance(x, Zero):
return x return x
assert isinstance(x, np.ndarray), "Vector must be a numpy array" assert isinstance(x, np.ndarray), "Vector must be a numpy array"
if numDims == 1: if numDims == 1:
@@ -278,7 +278,7 @@ class TensorType(object):
else: else:
raise Exception('Unexpected shape of tensor') raise Exception('Unexpected shape of tensor')
def __str__(self): def __str__(self):
return 'TensorType[{0:d}]: {1!s}'.format(self._tt, self._tts) return 'TensorType[%i]: %s' % (self._tt, self._tts)
def __eq__(self, v): return self._tt == v def __eq__(self, v): return self._tt == v
def __le__(self, v): return self._tt <= v def __le__(self, v): return self._tt <= v
def __ge__(self, v): return self._tt >= v def __ge__(self, v): return self._tt >= v
@@ -355,9 +355,9 @@ def diagEst(matFun, n, k=None, approach='Probing'):
2. Ones : random +/- 1 entries 2. Ones : random +/- 1 entries
3. Random : random vectors 3. Random : random vectors
:param callable matFun: takes a (numpy.array) and multiplies it by a matrix to estimate the diagonal :param lambda (numpy.array) matFun: matrix to estimate the diagonal of
:param int n: size of the vector that should be used to compute matFun(v) :param int64 n: size of the vector that should be used to compute matFun(v)
:param int k: number of vectors to be used to estimate the diagonal :param int64 k: number of vectors to be used to estimate the diagonal
:param str approach: approach to be used for getting vectors :param str approach: approach to be used for getting vectors
:rtype: numpy.array :rtype: numpy.array
:return: est_diag(A) :return: est_diag(A)
@@ -422,9 +422,9 @@ class Zero(object):
def __ge__(self, v):return 0 >= v def __ge__(self, v):return 0 >= v
def __gt__(self, v):return 0 > v def __gt__(self, v):return 0 > v
@property @property
def transpose(self): return Zero() def transpose(self): return Zero()
@property @property
def T(self): return Zero() def T(self): return Zero()
+14 -18
View File
@@ -83,7 +83,7 @@ def closestPoints(mesh, pts, gridLoc='CC'):
""" """
Move a list of points to the closest points on a grid. Move a list of points to the closest points on a grid.
:param BaseMesh mesh: The mesh :param simpeg.Mesh.BaseMesh mesh: The mesh
:param numpy.ndarray pts: Points to move :param numpy.ndarray pts: Points to move
:param string gridLoc: ['CC', 'N', 'Fx', 'Fy', 'Fz', 'Ex', 'Ex', 'Ey', 'Ez'] :param string gridLoc: ['CC', 'N', 'Fx', 'Fy', 'Fz', 'Ex', 'Ex', 'Ey', 'Ez']
:rtype: numpy.ndarray :rtype: numpy.ndarray
@@ -104,20 +104,16 @@ def closestPoints(mesh, pts, gridLoc='CC'):
def ExtractCoreMesh(xyzlim, mesh, meshType='tensor'): def ExtractCoreMesh(xyzlim, mesh, meshType='tensor'):
""" """
Extracts Core Mesh from Global mesh Extracts Core Mesh from Global mesh
xyzlim: 2D array [ndim x 2]
:param numpy.ndarray xyzlim: 2D array [ndim x 2] mesh: SimPEG mesh
:param BaseMesh mesh: The mesh This function ouputs:
- actind: corresponding boolean index from global to core
This function ouputs:: - meshcore: core SimPEG mesh
Warning: 1D and 2D has not been tested
- actind: corresponding boolean index from global to core
- meshcore: core SimPEG mesh
Warning: 1D and 2D has not been tested
""" """
from SimPEG import Mesh from SimPEG import Mesh
if mesh.dim == 1: if mesh.dim ==1:
xyzlim = xyzlim.flatten() xyzlim = xyzlim.flatten()
xmin, xmax = xyzlim[0], xyzlim[1] xmin, xmax = xyzlim[0], xyzlim[1]
@@ -129,11 +125,11 @@ def ExtractCoreMesh(xyzlim, mesh, meshType='tensor'):
x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5] x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5]
meshCore = Mesh.TensorMesh([hx, hy], x0=x0) meshCore = Mesh.TensorMesh([hx, hy] ,x0=x0)
actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax) actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax)
elif mesh.dim == 2: elif mesh.dim ==2:
xmin, xmax = xyzlim[0,0], xyzlim[0,1] xmin, xmax = xyzlim[0,0], xyzlim[0,1]
ymin, ymax = xyzlim[1,0], xyzlim[1,1] ymin, ymax = xyzlim[1,0], xyzlim[1,1]
@@ -148,12 +144,12 @@ def ExtractCoreMesh(xyzlim, mesh, meshType='tensor'):
x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5] x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5]
meshCore = Mesh.TensorMesh([hx, hy], x0=x0) meshCore = Mesh.TensorMesh([hx, hy] ,x0=x0)
actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax) \ actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax) \
& (mesh.gridCC[:,1]>ymin) & (mesh.gridCC[:,1]<ymax) \ & (mesh.gridCC[:,1]>ymin) & (mesh.gridCC[:,1]<ymax) \
elif mesh.dim == 3: elif mesh.dim==3:
xmin, xmax = xyzlim[0,0], xyzlim[0,1] xmin, xmax = xyzlim[0,0], xyzlim[0,1]
ymin, ymax = xyzlim[1,0], xyzlim[1,1] ymin, ymax = xyzlim[1,0], xyzlim[1,1]
zmin, zmax = xyzlim[2,0], xyzlim[2,1] zmin, zmax = xyzlim[2,0], xyzlim[2,1]
@@ -172,7 +168,7 @@ def ExtractCoreMesh(xyzlim, mesh, meshType='tensor'):
x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5, zc[0]-hz[0]*0.5] x0 = [xc[0]-hx[0]*0.5, yc[0]-hy[0]*0.5, zc[0]-hz[0]*0.5]
meshCore = Mesh.TensorMesh([hx, hy, hz], x0=x0) meshCore = Mesh.TensorMesh([hx, hy, hz] ,x0=x0)
actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax) \ actind = (mesh.gridCC[:,0]>xmin) & (mesh.gridCC[:,0]<xmax) \
& (mesh.gridCC[:,1]>ymin) & (mesh.gridCC[:,1]<ymax) \ & (mesh.gridCC[:,1]>ymin) & (mesh.gridCC[:,1]<ymax) \
+2 -2
View File
@@ -26,7 +26,7 @@ def surface2ind_topo(mesh, topo, gridLoc='CC'):
gridTopo = Ftopo(XY).reshape(mesh.vnN[:2], order='F') gridTopo = Ftopo(XY).reshape(mesh.vnN[:2], order='F')
if mesh._meshType not in ['TENSOR', 'CYL', 'BASETENSOR']: if mesh._meshType not in ['TENSOR', 'CYL', 'BASETENSOR']:
raise NotImplementedError('Nodal surface2ind_topo not implemented for {0!s} mesh'.format(mesh._meshType)) raise NotImplementedError('Nodal surface2ind_topo not implemented for %s mesh'%mesh._meshType)
Nz = mesh.vectorNz[1:] # TODO: this will only work for tensor meshes Nz = mesh.vectorNz[1:] # TODO: this will only work for tensor meshes
actind = np.array([False]*mesh.nC).reshape(mesh.vnC, order='F') actind = np.array([False]*mesh.nC).reshape(mesh.vnC, order='F')
@@ -47,7 +47,7 @@ def surface2ind_topo(mesh, topo, gridLoc='CC'):
gridTopo = Ftopo(mesh.vectorNx) gridTopo = Ftopo(mesh.vectorNx)
if mesh._meshType not in ['TENSOR', 'CYL', 'BASETENSOR']: if mesh._meshType not in ['TENSOR', 'CYL', 'BASETENSOR']:
raise NotImplementedError('Nodal surface2ind_topo not implemented for {0!s} mesh'.format(mesh._meshType)) raise NotImplementedError('Nodal surface2ind_topo not implemented for %s mesh'%mesh._meshType)
Ny = mesh.vectorNy[1:] # TODO: this will only work for tensor meshes Ny = mesh.vectorNy[1:] # TODO: this will only work for tensor meshes
actind = np.array([False]*mesh.nC).reshape(mesh.vnC, order='F') actind = np.array([False]*mesh.nC).reshape(mesh.vnC, order='F')
+1 -1
View File
@@ -15,7 +15,7 @@ import Directives
import Inversion import Inversion
import Tests import Tests
__version__ = '0.1.12' __version__ = '0.1.10'
__author__ = 'Rowan Cockett' __author__ = 'Rowan Cockett'
__license__ = 'MIT' __license__ = 'MIT'
__copyright__ = 'Copyright 2014 Rowan Cockett' __copyright__ = 'Copyright 2014 Rowan Cockett'

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

+1 -1
View File
@@ -2,7 +2,7 @@
# #
# You can set these variables from the command line. # You can set these variables from the command line.
SPHINXOPTS = -n -w warnings.txt SPHINXOPTS =
SPHINXBUILD = sphinx-build SPHINXBUILD = sphinx-build
PAPER = PAPER =
BUILDDIR = _build BUILDDIR = _build

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

-22
View File
@@ -1,22 +0,0 @@
{# Import the theme's layout. #}
{% extends "!layout.html" %}
{% block extrahead %}
{{ super() }}
<meta name="description" content="Simulation and Parameter Estimation in Geophysics">
<meta name="author" content="SimPEG Developers">
<meta name="keywords" content="python, geophysics, inversion, electromagnetics, magnetotellurics, magnetics, gravity, DC, flow inverse problems, open source, finite volume">
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-45185336-1', 'auto');
ga('send', 'pageview');
</script>
{% endblock %}
+10 -26
View File
@@ -1,3 +1,5 @@
.. _api_DC:
.. math:: .. math::
\renewcommand{\div}{\nabla\cdot\,} \renewcommand{\div}{\nabla\cdot\,}
@@ -36,27 +38,10 @@
\renewcommand {\u} { {\vec u} } \renewcommand {\u} { {\vec u} }
\newcommand{\I}{\vec{I}} \newcommand{\I}{\vec{I}}
Direct Current Resistivity
**************************
`SimPEG.DCIP` uses SimPEG as the framework for the forward and inverse
direct current (DC) resistivity and induced polarization (IP) geophysical problems.
DC resistivity survey DC resistivity survey
===================== *********************
Electrical resistivity of subsurface materials is measured by causing an Electrical resistivity of subsurface materials is measured by causing an electrical current to flow in the earth between one pair of electrodes while the voltage across a second pair of electrodes is measured. The result is an "apparent" resistivity which is a value representing the weighted average resistivity over a volume of the earth. Variations in this measurement are caused by variations in the soil, rock, and pore fluid electrical resistivity. Surveys require contact with the ground, so they can be labour intensive. Results are sometimes interpreted directly, but more commonly, 1D, 2D or 3D models are estimated using inversion procedures (`GPG <http://www.eos.ubc.ca/courses/eosc350/content/>`_).
electrical current to flow in the earth between one pair of electrodes while
the voltage across a second pair of electrodes is measured. The result is an
"apparent" resistivity which is a value representing the weighted average
resistivity over a volume of the earth. Variations in this measurement are
caused by variations in the soil, rock, and pore fluid electrical resistivity.
Surveys require contact with the ground, so they can be labour intensive.
Results are sometimes interpreted directly, but more commonly, 1D, 2D or 3D
models are estimated using inversion procedures (`GPG
<http://gpg.geosci.xyz>`_).
Background Background
@@ -70,7 +55,7 @@ As direct current (DC) implies, in DC resistivity survey, we assume steady-state
\curl \e = 0 \curl \e = 0
Then by taking \\(\\div\\) of the first equation, we have Then by taking \\(\\curl\\) for the first equation, we have
.. math:: .. math::
@@ -152,14 +137,13 @@ Comparing to the analytic function:
.. plot:: .. plot::
from SimPEG import Examples import simpegDC as DC
Examples.DC_Analytic_Dipole.run(plotIt=True) DC.Examples.Verification.run(plotIt=True)
API
===
API for DC codes .. automodule:: simpegDC.BaseDC
================
.. automodule:: SimPEG.DCIP.BaseDC
:show-inheritance: :show-inheritance:
:members: :members:
:undoc-members: :undoc-members:
@@ -7,7 +7,7 @@ Examples
:maxdepth: 1 :maxdepth: 1
:glob: :glob:
../examples/* examples/*
External Notebooks External Notebooks
+19
View File
@@ -0,0 +1,19 @@
.. _api_FiniteVolume:
Finite Volume
*************
Any numerical implementation requires the discretization of continuous functions into discrete approximations. These approximations are typically organized in a mesh, which defines boundaries, locations, and connectivity. Of specific interest to geophysical simulations, we require that averaging, interpolation and differential operators be defined for any mesh. In SimPEG, we have implemented a staggered mimetic finite volume approach (`Hyman and Shashkov, 1999 <http://math.lanl.gov/~mac/papers/numerics/HS99B.pdf>`_). This approach requires the definitions of variables at either cell-centers, nodes, faces, or edges as seen in the figure below.
.. image:: images/finitevolrealestate.png
:width: 400 px
:alt: FiniteVolume
:align: center
.. toctree::
:maxdepth: 2
api_Mesh
api_DiffOps
api_InnerProducts
@@ -52,15 +52,13 @@ We can take the derivative of the PDE:
\nabla_m c(m, u) \partial m + \nabla_u c(m, u) \partial u = 0 \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 If the forward problem is invertible, then we can rearrange for \\(\\frac{\\partial u}{\\partial m}\\):
\\(\\frac{\\partial u}{\\partial m}\\):
.. math:: .. math::
J = - P \left( \nabla_u c(m, u) \right)^{-1} \nabla_m c(m, u) 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 This can often be computed given a vector (i.e. \\(J(v)\\)) rather than stored, as \\(J\\) is a large dense matrix.
stored, as \\(J\\) is a large dense matrix.
@@ -69,45 +67,13 @@ The API
Problem Problem
------- -------
.. automodule:: SimPEG.Problem
.. autoclass:: SimPEG.Problem.BaseProblem
:members:
:undoc-members:
.. autoclass:: SimPEG.Problem.BaseTimeProblem
:members:
:undoc-members:
Fields
------
.. autoclass:: SimPEG.Fields.Fields
:members:
:undoc-members:
.. autoclass:: SimPEG.Fields.TimeFields
:members: :members:
:undoc-members: :undoc-members:
Survey Survey
------ ------
.. automodule:: SimPEG.Survey
.. autoclass:: SimPEG.Survey.BaseSurvey
:members: :members:
:undoc-members: :undoc-members:
.. autoclass:: SimPEG.Survey.BaseSrc
:members:
:undoc-members:
.. autoclass:: SimPEG.Survey.BaseRx
:members:
:undoc-members:
.. autoclass:: SimPEG.Survey.BaseTimeRx
:members:
:undoc-members:
.. autoclass:: SimPEG.Survey.Data
:members:
:undoc-members:
@@ -4,10 +4,7 @@
Inner Products Inner Products
************** **************
By using the weak formulation of many of the PDEs in geophysical applications, By using the weak formulation of many of the PDEs in geophysical applications, we can rapidly develop discretizations. Much of this work, however, needs a good understanding of how to approximate inner products on our discretized meshes. We will define the inner product as:
we can rapidly develop discretizations. Much of this work, however, needs a
good understanding of how to approximate inner products on our discretized
meshes. We will define the inner product as:
.. math:: .. math::
@@ -17,15 +14,12 @@ where a and b are either scalars or vectors.
.. note:: .. note::
The InnerProducts class is a base class providing inner product matrices The InnerProducts class is a base class providing inner product matrices for meshes and cannot run on its own.
for meshes and cannot run on its own.
Example problem for DC resistivity Example problem for DC resistivity
---------------------------------- ----------------------------------
We will start with the formulation of the Direct Current (DC) resistivity problem in geophysics.
We will start with the formulation of the Direct Current (DC) resistivity
problem in geophysics.
.. math:: .. math::
@@ -34,13 +28,12 @@ problem in geophysics.
\nabla\cdot \vec{j} = q \nabla\cdot \vec{j} = q
In the following discretization, :math:`\sigma` and :math:`\phi` In the following discretization, \\\( \\sigma \\\) and \\\( \\phi \\\)
will be discretized on the cell-centers and the flux, :math:`\vec{j}`, will be discretized on the cell-centers and the flux, \\\(\\vec{j}\\\),
will be on the faces. We will use the weak formulation to discretize will be on the faces. We will use the weak formulation to discretize
the DC resistivity equation. the DC resistivity equation.
We can define in weak form by integrating with a general face function We can define in weak form by integrating with a general face function \\\(\\vec{f}\\\):
:math:`\vec{f}`:
.. math:: .. math::
@@ -68,16 +61,9 @@ We can then discretize for every cell:
.. note:: .. note::
We have discretized the dot product above, but remember that we do not We have discretized the dot product above, but remember that we do not really have a single vector \\\(\\mathbf{J}\\\), but approximations of \\\(\\vec{j}\\\) on each face of our cell. In 2D that means 2 approximations of \\\(\\mathbf{J}_x\\\) and 2 approximations of \\\(\\mathbf{J}_y\\\). In 3D we also have 2 approximations of \\\(\\mathbf{J}_z\\\).
really have a single vector :math:`\mathbf{J}`, but approximations of
:math:`\vec{j}` on each face of our cell. In 2D that means 2
approximations of :math:`\mathbf{J}_x` and 2 approximations of
:math:`\mathbf{J}_y`. In 3D we also have 2 approximations of
:math:`\mathbf{J}_z`.
Regardless of how we choose to approximate this dot product, we can represent Regardless of how we choose to approximate this dot product, we can represent this in vector form (again this is for every cell), and will generalize for the case of anisotropic (tensor) sigma.
this in vector form (again this is for every cell), and will generalize for
the case of anisotropic (tensor) sigma.
.. math:: .. math::
@@ -85,17 +71,14 @@ the case of anisotropic (tensor) sigma.
-\phi^{\top} v_{\text{cell}} \mathbf{D}_{\text{cell}} \mathbf{F}) -\phi^{\top} v_{\text{cell}} \mathbf{D}_{\text{cell}} \mathbf{F})
+ \text{BC} + \text{BC}
We multiply by square-root of volume on each side of the tensor conductivity We multiply by square-root of volume on each side of the tensor conductivity to keep symmetry in the system. Here \\\(\\mathbf{J}_c\\\) is the Cartesian \\\(\\mathbf{J}\\\) (on the faces that we choose to use in our approximation) and must be calculated differently depending on the mesh:
to keep symmetry in the system. Here :math:`\mathbf{J}_c` is the Cartesian
:math:`\mathbf{J}` (on the faces that we choose to use in our approximation)
and must be calculated differently depending on the mesh:
.. math:: .. math::
\mathbf{J}_c = \mathbf{Q}_{(i)}\mathbf{J}_\text{TENSOR} \\ \mathbf{J}_c = \mathbf{Q}_{(i)}\mathbf{J}_\text{TENSOR} \\
\mathbf{J}_c = \mathbf{N}_{(i)}^{-1}\mathbf{Q}_{(i)}\mathbf{J}_\text{Curv} \mathbf{J}_c = \mathbf{N}_{(i)}^{-1}\mathbf{Q}_{(i)}\mathbf{J}_\text{Curv}
Here the :math:`i` index refers to where we choose to approximate this integral, as discussed in the note above. Here the \\\(i\\\) index refers to where we choose to approximate this integral, as discussed in the note above.
We will approximate this integral by taking the fluxes clustered around every node of the cell, there are 8 combinations in 3D, and 4 in 2D. We will use a projection matrix :math:`\mathbf{Q}_{(i)}` to pick the appropriate fluxes. So, now that we have 8 approximations of this integral, we will just take the average. For the TensorMesh, this looks like: We will approximate this integral by taking the fluxes clustered around every node of the cell, there are 8 combinations in 3D, and 4 in 2D. We will use a projection matrix \\\( \\mathbf{Q}_{(i)} \\\) to pick the appropriate fluxes. So, now that we have 8 approximations of this integral, we will just take the average. For the TensorMesh, this looks like:
.. math:: .. math::
@@ -124,12 +107,10 @@ By defining the faceInnerProduct (8 combinations of fluxes in 3D, 4 in 2D, 2 in
\sum_{i=1}^{2^d} \sum_{i=1}^{2^d}
\mathbf{P}_{(i)}^{\top} \Sigma^{-1} \mathbf{P}_{(i)} \mathbf{P}_{(i)}^{\top} \Sigma^{-1} \mathbf{P}_{(i)}
Where :math:`d` is the dimension of the mesh. Where \\\(d\\\) is the dimension of the mesh.
The :math:`\mathbf{M}^f` is returned when given the input of :math:`\Sigma^{-1}`. The \\\( \\mathbf{M}^f \\\) is returned when given the input of \\\( \\Sigma^{-1} \\\).
Here each :math:`\mathbf{P} ~ \in ~ \mathbb{R}^{(d*nC, nF)}` is a combination Here each \\( \\mathbf{P} \\in \\mathbb{R}^{(d*nC, nF)} \\\) is a combination of the projection, volume, and any normalization to Cartesian coordinates (where the dot product is well defined):
of the projection, volume, and any normalization to Cartesian coordinates
(where the dot product is well defined):
.. math:: .. math::
@@ -148,10 +129,7 @@ If ``returnP=True`` is requested in any of these methods the projection matrices
# In 1D # In 1D
P = [P0, P1] P = [P0, P1]
The derivation for ``edgeInnerProducts`` is exactly the same, however, when we The derivation for ``edgeInnerProducts`` is exactly the same, however, when we approximate the integral using the fields around each node, the projection matrices look a bit different because we have 12 edges in 3D instead of just 6 faces. The interface to the code is exactly the same.
approximate the integral using the fields around each node, the projection
matrices look a bit different because we have 12 edges in 3D instead of just 6
faces. The interface to the code is exactly the same.
Defining Tensor Properties Defining Tensor Properties
@@ -159,8 +137,7 @@ Defining Tensor Properties
**For 3D:** **For 3D:**
Depending on the number of columns (either 1, 3, or 6) of mu, the material Depending on the number of columns (either 1, 3, or 6) of mu, the material property is interpreted as follows:
property is interpreted as follows:
.. math:: .. math::
@@ -211,16 +188,13 @@ Which is nice and easy to invert if necessary, however, in the fully anisotropic
Taking Derivatives Taking Derivatives
------------------ ------------------
We will take the derivative of the fully anisotropic tensor for a 3D mesh, the We will take the derivative of the fully anisotropic tensor for a 3D mesh, the other cases are easier and will not be discussed here. Let us start with one part of the sum which makes up \\\(\\mathbf{M}^f_\\Sigma\\\) and take the derivative when this is multiplied by some vector \\\(\\mathbf{v}\\\):
other cases are easier and will not be discussed here. Let us start with one
part of the sum which makes up :math:`\mathbf{M}^f_\Sigma` and take the
derivative when this is multiplied by some vector :math:`\mathbf{v}`:
.. math:: .. math::
\mathbf{P}^\top \boldsymbol{\Sigma} \mathbf{Pv} \mathbf{P}^\top \boldsymbol{\Sigma} \mathbf{Pv}
Here we will let :math:`\mathbf{Pv} = \mathbf{y}` and :math:`\mathbf{y}` will have the form: Here we will let \\\( \\mathbf{Pv} = \\mathbf{y} \\\) and \\\(\\mathbf{y}\\\) will have the form:
.. math:: .. math::
@@ -259,9 +233,7 @@ Here we will let :math:`\mathbf{Pv} = \mathbf{y}` and :math:`\mathbf{y}` will ha
\end{matrix} \end{matrix}
\right] \right]
Now it is easy to take the derivative with respect to any one of the Now it is easy to take the derivative with respect to any one of the parameters, for example, \\\(\\frac{\\partial}{\\partial\\boldsymbol{\\sigma}_1}\\\)
parameters, for example,
:math:`\frac{\partial}{\partial\boldsymbol{\sigma}_1}`
.. math:: .. math::
\frac{\partial}{\partial \boldsymbol{\sigma}_1}\left(\mathbf{P}^\top\Sigma\mathbf{y}\right) \frac{\partial}{\partial \boldsymbol{\sigma}_1}\left(\mathbf{P}^\top\Sigma\mathbf{y}\right)
@@ -275,8 +247,7 @@ parameters, for example,
\end{matrix} \end{matrix}
\right] \right]
Whereas :math:`\frac{\partial}{\partial\boldsymbol{\sigma}_4}`, for Whereas \\\(\\frac{\\partial}{\\partial\\boldsymbol{\\sigma}_4}\\\), for example, is:
example, is:
.. math:: .. math::
\frac{\partial}{\partial \boldsymbol{\sigma}_4}\left(\mathbf{P}^\top\Sigma\mathbf{y}\right) \frac{\partial}{\partial \boldsymbol{\sigma}_4}\left(\mathbf{P}^\top\Sigma\mathbf{y}\right)
@@ -290,12 +261,11 @@ example, is:
\end{matrix} \end{matrix}
\right] \right]
These are computed for each of the 8 projections, horizontally concatenated, These are computed for each of the 8 projections, horizontally concatenated, and returned.
and returned.
The API The API
------- -------
.. autoclass:: SimPEG.Mesh.InnerProducts.InnerProducts .. automodule:: SimPEG.Mesh.InnerProducts
:members: :members:
:undoc-members: :undoc-members:
@@ -3,7 +3,7 @@
InvProblem InvProblem
********** **********
.. autoclass:: SimPEG.InvProblem.BaseInvProblem .. automodule:: SimPEG.InvProblem
:show-inheritance: :show-inheritance:
:members: :members:
:undoc-members: :undoc-members:
@@ -12,7 +12,7 @@ InvProblem
Inversion Inversion
********* *********
.. autoclass:: SimPEG.Inversion.BaseInversion .. automodule:: SimPEG.Inversion
:show-inheritance: :show-inheritance:
:members: :members:
:undoc-members: :undoc-members:

Some files were not shown because too many files have changed in this diff Show More