def fit_colecole_with_se(self, eta_cc=0.8, tau_cc=0.003, c_cc=0.6): def ColeColeSeigel(f, sigmaInf, eta, tau, c): w = 2 * np.pi * f return sigmaInf * (1 - eta / (1 + (1j * w * tau)**c)) # Step1: Fit Cole-Cole with Stretched Exponential function time = np.logspace(-6, np.log10(0.01), 41) wt, tbase, omega_int = DigFilter.setFrequency(time) frequency = omega_int / (2 * np.pi) # Cole-Cole parameters siginf = 1. self.eta_cc = eta_cc self.tau_cc = tau_cc self.c_cc = c_cc sigma = ColeColeSeigel(frequency, siginf, eta_cc, tau_cc, c_cc) sigTCole = DigFilter.transFiltImpulse(sigma, wt, tbase, omega_int, time, tol=1e-12) wires = Maps.Wires(('eta', 1), ('tau', 1), ('c', 1)) taumap = Maps.ExpMap(nP=1) * wires.tau survey = SESurvey() dtrue = -sigTCole survey.dobs = dtrue m1D = Mesh.TensorMesh([np.ones(3)]) prob = SEInvImpulseProblem(m1D, etaMap=wires.eta, tauMap=taumap, cMap=wires.c) update_sens = Directives.UpdateSensitivityWeights() prob.time = time prob.pair(survey) m0 = np.r_[eta_cc, np.log(tau_cc), c_cc] perc = 0.05 dmisfitpeta = DataMisfit.l2_DataMisfit(survey) dmisfitpeta.W = 1 / (abs(survey.dobs) * perc) reg = regularization.Simple(m1D) opt = Optimization.ProjectedGNCG(maxIter=10) invProb = InvProblem.BaseInvProblem(dmisfitpeta, reg, opt) # Create an inversion object target = Directives.TargetMisfit() invProb.beta = 0. inv = Inversion.BaseInversion(invProb, directiveList=[target]) reg.mref = 0. * m0 prob.counter = opt.counter = Utils.Counter() opt.LSshorten = 0.5 opt.remember('xc') opt.tolX = 1e-20 opt.tolF = 1e-20 opt.tolG = 1e-20 opt.eps = 1e-20 mopt = inv.run(m0) return mopt
def solve(self): # Tikhonov Inversion #################### # Initial model values m0 = np.median(self.ln_sigback) * np.ones(self.mapping.nP) m0 += np.random.randn(m0.size) # Misfit functional dmis = DataMisfit.l2_DataMisfit(self.survey.simpeg_survey) # Regularization functional regT = Regularization.Simple(self.mesh, alpha_s=10.0, alpha_x=10.0, alpha_y=10.0, alpha_z=10.0, indActive=self.actind) # Personal preference for this solver with a Jacobi preconditioner opt = Optimization.ProjectedGNCG(maxIter=8, tolX=1, maxIterCG=30) #opt = Optimization.ProjectedGradient(maxIter=100, tolX=1e-2, # maxIterLS=20, maxIterCG=30, tolCG=1e-4) opt.printers.append(Optimization.IterationPrinters.iterationLS) #print(opt.printersLS) # Optimization class keeps value of 'xc'. Seems to be solution for the model parameters opt.remember('xc') invProb = InvProblem.BaseInvProblem(dmis, regT, opt) # Options for the inversion algorithm in particular selection of Beta weight for regularization. # How to choose initial estimate for beta beta = Directives.BetaEstimate_ByEig(beta0_ratio=1.) Target = Directives.TargetMisfit() # Beta changing algorithm. betaSched = Directives.BetaSchedule(coolingFactor=5., coolingRate=2) # Change model weights, seems sensitivity of conductivity ?? Not sure. updateSensW = Directives.UpdateSensitivityWeights(threshold=1e-3) # Use Jacobi preconditioner ( the only available). update_Jacobi = Directives.UpdatePreconditioner() inv = Inversion.BaseInversion(invProb, directiveList=[ beta, Target, betaSched, updateSensW, update_Jacobi ]) self.minv = inv.run(m0)
invProb = InvProblem.BaseInvProblem(dmis, reg, opt, beta=beta * 10.) # Here is where the norms are applied IRLS = Directives.Update_IRLS(f_min_change=1e-4, maxIRLSiter=20, minGNiter=1, beta_tol=0.5, coolingRate=1, coolEps_q=True, betaSearch=False) # Special directive specific to the mag amplitude problem. The sensitivity # weights are update between each iteration. ProjSpherical = Directives.ProjectSphericalBounds() update_SensWeight = Directives.UpdateSensitivityWeights() update_Jacobi = Directives.UpdatePreconditioner() inv = Inversion.BaseInversion( invProb, directiveList=[ProjSpherical, IRLS, update_SensWeight, update_Jacobi]) mrec_MVI_S = inv.run(mstart) ############################################################# # Final Plot # ---------- # # Let's compare the smooth and compact model # #
# Personal preference for this solver with a Jacobi preconditioner opt = Optimization.ProjectedGNCG(maxIter=20, lower=-10, upper=10, maxIterLS=20, maxIterCG=30, tolCG=1e-4) opt.remember('xc') invProb = InvProblem.BaseInvProblem(dmis, regT, opt) beta = Directives.BetaEstimate_ByEig(beta0_ratio=1.) Target = Directives.TargetMisfit() betaSched = Directives.BetaSchedule(coolingFactor=5., coolingRate=2) updateSensW = Directives.UpdateSensitivityWeights(threshold=1e-3) update_Jacobi = Directives.UpdatePreconditioner() inv = Inversion.BaseInversion( invProb, directiveList=[beta, Target, betaSched, updateSensW, update_Jacobi]) minv = inv.run(m0) # Final Plot ############ fig, ax = plt.subplots(1, 2, figsize=(12, 5)) ax = Utils.mkvc(ax) cyl0v = getCylinderPoints(x0, z0, r0)
def setUp(self): np.random.seed(0) H0 = (50000., 90., 0.) # The magnetization is set along a different # direction (induced + remanence) M = np.array([45., 90.]) # Create grid of points for topography # Lets create a simple Gaussian topo # and set the active cells [xx, yy] = np.meshgrid( np.linspace(-200, 200, 50), np.linspace(-200, 200, 50) ) b = 100 A = 50 zz = A*np.exp(-0.5*((xx/b)**2. + (yy/b)**2.)) # We would usually load a topofile topo = np.c_[Utils.mkvc(xx), Utils.mkvc(yy), Utils.mkvc(zz)] # Create and array of observation points xr = np.linspace(-100., 100., 20) yr = np.linspace(-100., 100., 20) X, Y = np.meshgrid(xr, yr) Z = A*np.exp(-0.5*((X/b)**2. + (Y/b)**2.)) + 5 # Create a MAGsurvey xyzLoc = np.c_[Utils.mkvc(X.T), Utils.mkvc(Y.T), Utils.mkvc(Z.T)] rxLoc = PF.BaseMag.RxObs(xyzLoc) srcField = PF.BaseMag.SrcField([rxLoc], param=H0) survey = PF.BaseMag.LinearSurvey(srcField) # Create a mesh h = [5, 5, 5] padDist = np.ones((3, 2)) * 100 nCpad = [2, 4, 2] # Get extent of points limx = np.r_[topo[:, 0].max(), topo[:, 0].min()] limy = np.r_[topo[:, 1].max(), topo[:, 1].min()] limz = np.r_[topo[:, 2].max(), topo[:, 2].min()] # Get center of the mesh midX = np.mean(limx) midY = np.mean(limy) midZ = np.mean(limz) nCx = int(limx[0]-limx[1]) / h[0] nCy = int(limy[0]-limy[1]) / h[1] nCz = int(limz[0]-limz[1]+int(np.min(np.r_[nCx, nCy])/3)) / h[2] # Figure out full extent required from input extent = np.max(np.r_[nCx * h[0] + padDist[0, :].sum(), nCy * h[1] + padDist[1, :].sum(), nCz * h[2] + padDist[2, :].sum()]) maxLevel = int(np.log2(extent/h[0]))+1 # Number of cells at the small octree level nCx, nCy, nCz = 2**(maxLevel), 2**(maxLevel), 2**(maxLevel) # Define the mesh and origin # For now cubic cells mesh = Mesh.TreeMesh([np.ones(nCx)*h[0], np.ones(nCx)*h[1], np.ones(nCx)*h[2]]) # Set origin mesh.x0 = np.r_[ -nCx*h[0]/2.+midX, -nCy*h[1]/2.+midY, -nCz*h[2]/2.+midZ ] # Refine the mesh around topography # Get extent of points F = NearestNDInterpolator(topo[:, :2], topo[:, 2]) zOffset = 0 # Cycle through the first 3 octree levels for ii in range(3): dx = mesh.hx.min()*2**ii nCx = int((limx[0]-limx[1]) / dx) nCy = int((limy[0]-limy[1]) / dx) # Create a grid at the octree level in xy CCx, CCy = np.meshgrid( np.linspace(limx[1], limx[0], nCx), np.linspace(limy[1], limy[0], nCy) ) z = F(mkvc(CCx), mkvc(CCy)) # level means number of layers in current OcTree level for level in range(int(nCpad[ii])): mesh.insert_cells( np.c_[ mkvc(CCx), mkvc(CCy), z-zOffset ], np.ones_like(z)*maxLevel-ii, finalize=False ) zOffset += dx mesh.finalize() self.mesh = mesh # Define an active cells from topo actv = Utils.surface2ind_topo(mesh, topo) nC = int(actv.sum()) model = np.zeros((mesh.nC, 3)) # Convert the inclination declination to vector in Cartesian M_xyz = Utils.matutils.dip_azimuth2cartesian(M[0], M[1]) # Get the indicies of the magnetized block ind = Utils.ModelBuilder.getIndicesBlock( np.r_[-20, -20, -10], np.r_[20, 20, 25], mesh.gridCC, )[0] # Assign magnetization values model[ind, :] = np.kron( np.ones((ind.shape[0], 1)), M_xyz*0.05 ) # Remove air cells self.model = model[actv, :] # Create active map to go from reduce set to full self.actvMap = Maps.InjectActiveCells(mesh, actv, np.nan) # Creat reduced identity map idenMap = Maps.IdentityMap(nP=nC*3) # Create the forward model operator prob = PF.Magnetics.MagneticIntegral( mesh, chiMap=idenMap, actInd=actv, modelType='vector' ) # Pair the survey and problem survey.pair(prob) # Compute some data and add some random noise data = prob.fields(Utils.mkvc(self.model)) std = 5 # nT data += np.random.randn(len(data))*std wd = np.ones(len(data))*std # Assigne data and uncertainties to the survey survey.dobs = data survey.std = wd # Create an projection matrix for plotting later actvPlot = Maps.InjectActiveCells(mesh, actv, np.nan) # Create sensitivity weights from our linear forward operator rxLoc = survey.srcField.rxList[0].locs # This Mapping connects the regularizations for the three-component # vector model wires = Maps.Wires(('p', nC), ('s', nC), ('t', nC)) # Create sensitivity weights from our linear forward operator # so that all cells get equal chance to contribute to the solution wr = np.sum(prob.G**2., axis=0)**0.5 wr = (wr/np.max(wr)) # Create three regularization for the different components # of magnetization reg_p = Regularization.Sparse(mesh, indActive=actv, mapping=wires.p) reg_p.mref = np.zeros(3*nC) reg_p.cell_weights = (wires.p * wr) reg_s = Regularization.Sparse(mesh, indActive=actv, mapping=wires.s) reg_s.mref = np.zeros(3*nC) reg_s.cell_weights = (wires.s * wr) reg_t = Regularization.Sparse(mesh, indActive=actv, mapping=wires.t) reg_t.mref = np.zeros(3*nC) reg_t.cell_weights = (wires.t * wr) reg = reg_p + reg_s + reg_t reg.mref = np.zeros(3*nC) # Data misfit function dmis = DataMisfit.l2_DataMisfit(survey) dmis.W = 1./survey.std # Add directives to the inversion opt = Optimization.ProjectedGNCG(maxIter=30, lower=-10, upper=10., maxIterLS=20, maxIterCG=20, tolCG=1e-4) invProb = InvProblem.BaseInvProblem(dmis, reg, opt) # A list of directive to control the inverson betaest = Directives.BetaEstimate_ByEig() # Here is where the norms are applied # Use pick a treshold parameter empirically based on the distribution of # model parameters IRLS = Directives.Update_IRLS( f_min_change=1e-3, maxIRLSiter=0, beta_tol=5e-1 ) # Pre-conditioner update_Jacobi = Directives.UpdatePreconditioner() inv = Inversion.BaseInversion(invProb, directiveList=[IRLS, update_Jacobi, betaest]) # Run the inversion m0 = np.ones(3*nC) * 1e-4 # Starting model mrec_MVIC = inv.run(m0) self.mstart = Utils.matutils.cartesian2spherical(mrec_MVIC.reshape((nC, 3), order='F')) beta = invProb.beta dmis.prob.coordinate_system = 'spherical' dmis.prob.model = self.mstart # Create a block diagonal regularization wires = Maps.Wires(('amp', nC), ('theta', nC), ('phi', nC)) # Create a Combo Regularization # Regularize the amplitude of the vectors reg_a = Regularization.Sparse(mesh, indActive=actv, mapping=wires.amp) reg_a.norms = np.c_[0., 0., 0., 0.] # Sparse on the model and its gradients reg_a.mref = np.zeros(3*nC) # Regularize the vertical angle of the vectors reg_t = Regularization.Sparse(mesh, indActive=actv, mapping=wires.theta) reg_t.alpha_s = 0. # No reference angle reg_t.space = 'spherical' reg_t.norms = np.c_[2., 0., 0., 0.] # Only norm on gradients used # Regularize the horizontal angle of the vectors reg_p = Regularization.Sparse(mesh, indActive=actv, mapping=wires.phi) reg_p.alpha_s = 0. # No reference angle reg_p.space = 'spherical' reg_p.norms = np.c_[2., 0., 0., 0.] # Only norm on gradients used reg = reg_a + reg_t + reg_p reg.mref = np.zeros(3*nC) Lbound = np.kron(np.asarray([0, -np.inf, -np.inf]), np.ones(nC)) Ubound = np.kron(np.asarray([10, np.inf, np.inf]), np.ones(nC)) # Add directives to the inversion opt = Optimization.ProjectedGNCG(maxIter=20, lower=Lbound, upper=Ubound, maxIterLS=20, maxIterCG=30, tolCG=1e-3, stepOffBoundsFact=1e-3, ) opt.approxHinv = None invProb = InvProblem.BaseInvProblem(dmis, reg, opt, beta=beta*10.) # Here is where the norms are applied IRLS = Directives.Update_IRLS(f_min_change=1e-4, maxIRLSiter=20, minGNiter=1, beta_tol=0.5, coolingRate=1, coolEps_q=True, betaSearch=False) # Special directive specific to the mag amplitude problem. The sensitivity # weights are update between each iteration. ProjSpherical = Directives.ProjectSphericalBounds() update_SensWeight = Directives.UpdateSensitivityWeights() update_Jacobi = Directives.UpdatePreconditioner() self.inv = Inversion.BaseInversion( invProb, directiveList=[ ProjSpherical, IRLS, update_SensWeight, update_Jacobi ] )
def run(plotIt=True, survey_type="dipole-dipole", rho_background=1e3, rho_block=1e2, block_x0=100, block_dx=10, block_y0=-10, block_dy=5): np.random.seed(1) # Initiate I/O class for DC IO = DC.IO() # Obtain ABMN locations xmin, xmax = 0., 200. ymin, ymax = 0., 0. zmin, zmax = 0, 0 endl = np.array([[xmin, ymin, zmin], [xmax, ymax, zmax]]) # Generate DC survey object survey = DC.Utils.gen_DCIPsurvey(endl, survey_type=survey_type, dim=2, a=10, b=10, n=10) survey.getABMN_locations() survey = IO.from_ambn_locations_to_survey(survey.a_locations, survey.b_locations, survey.m_locations, survey.n_locations, survey_type, data_dc_type='volt') # Obtain 2D TensorMesh mesh, actind = IO.set_mesh() # Flat topography actind = Utils.surface2ind_topo(mesh, np.c_[mesh.vectorCCx, mesh.vectorCCx * 0.]) survey.drapeTopo(mesh, actind, option="top") # Use Exponential Map: m = log(rho) actmap = Maps.InjectActiveCells(mesh, indActive=actind, valInactive=np.log(1e8)) parametric_block = Maps.ParametricBlock(mesh, slopeFact=1e2) mapping = Maps.ExpMap(mesh) * parametric_block # Set true model # val_background,val_block, block_x0, block_dx, block_y0, block_dy mtrue = np.r_[np.log(1e3), np.log(10), 100, 10, -20, 10] # Set initial model m0 = np.r_[np.log(rho_background), np.log(rho_block), block_x0, block_dx, block_y0, block_dy] rho = mapping * mtrue rho0 = mapping * m0 # Show the true conductivity model fig = plt.figure(figsize=(12, 3)) ax = plt.subplot(111) temp = rho.copy() temp[~actind] = np.nan out = mesh.plotImage(temp, grid=False, ax=ax, gridOpts={'alpha': 0.2}, clim=(10, 1000), pcolorOpts={ "cmap": "viridis", "norm": colors.LogNorm() }) ax.plot(survey.electrode_locations[:, 0], survey.electrode_locations[:, 1], 'k.') ax.set_xlim(IO.grids[:, 0].min(), IO.grids[:, 0].max()) ax.set_ylim(-IO.grids[:, 1].max(), IO.grids[:, 1].min()) cb = plt.colorbar(out[0]) cb.set_label("Resistivity (ohm-m)") ax.set_aspect('equal') ax.set_title("True resistivity model") plt.show() # Show the true conductivity model fig = plt.figure(figsize=(12, 3)) ax = plt.subplot(111) temp = rho0.copy() temp[~actind] = np.nan out = mesh.plotImage(temp, grid=False, ax=ax, gridOpts={'alpha': 0.2}, clim=(10, 1000), pcolorOpts={ "cmap": "viridis", "norm": colors.LogNorm() }) ax.plot(survey.electrode_locations[:, 0], survey.electrode_locations[:, 1], 'k.') ax.set_xlim(IO.grids[:, 0].min(), IO.grids[:, 0].max()) ax.set_ylim(-IO.grids[:, 1].max(), IO.grids[:, 1].min()) cb = plt.colorbar(out[0]) cb.set_label("Resistivity (ohm-m)") ax.set_aspect('equal') ax.set_title("Initial resistivity model") plt.show() # Generate 2.5D DC problem # "N" means potential is defined at nodes prb = DC.Problem2D_N(mesh, rhoMap=mapping, storeJ=True, Solver=Solver) # Pair problem with survey try: prb.pair(survey) except: survey.unpair() prb.pair(survey) # Make synthetic DC data with 5% Gaussian noise dtrue = survey.makeSyntheticData(mtrue, std=0.05, force=True) # Show apparent resisitivty pseudo-section IO.plotPseudoSection(data=survey.dobs / IO.G, data_type='apparent_resistivity') # Show apparent resisitivty histogram fig = plt.figure() out = hist(survey.dobs / IO.G, bins=20) plt.show() # Set uncertainty # floor eps = 10**(-3.2) # percentage std = 0.05 dmisfit = DataMisfit.l2_DataMisfit(survey) uncert = abs(survey.dobs) * std + eps dmisfit.W = 1. / uncert # Map for a regularization mesh_1d = Mesh.TensorMesh([parametric_block.nP]) # Related to inversion reg = Regularization.Simple(mesh_1d, alpha_x=0.) opt = Optimization.InexactGaussNewton(maxIter=10) invProb = InvProblem.BaseInvProblem(dmisfit, reg, opt) beta = Directives.BetaSchedule(coolingFactor=5, coolingRate=2) betaest = Directives.BetaEstimate_ByEig(beta0_ratio=1e0) target = Directives.TargetMisfit() updateSensW = Directives.UpdateSensitivityWeights() update_Jacobi = Directives.UpdatePreconditioner() invProb.beta = 0. inv = Inversion.BaseInversion(invProb, directiveList=[target]) prb.counter = opt.counter = Utils.Counter() opt.LSshorten = 0.5 opt.remember('xc') # Run inversion mopt = inv.run(m0) # Convert obtained inversion model to resistivity # rho = M(m), where M(.) is a mapping rho_est = mapping * mopt rho_true = rho.copy() # show recovered conductivity vmin, vmax = rho.min(), rho.max() fig, ax = plt.subplots(2, 1, figsize=(20, 6)) out1 = mesh.plotImage(rho_true, clim=(10, 1000), pcolorOpts={ "cmap": "viridis", "norm": colors.LogNorm() }, ax=ax[0]) out2 = mesh.plotImage(rho_est, clim=(10, 1000), pcolorOpts={ "cmap": "viridis", "norm": colors.LogNorm() }, ax=ax[1]) out = [out1, out2] for i in range(2): ax[i].plot(survey.electrode_locations[:, 0], survey.electrode_locations[:, 1], 'kv') ax[i].set_xlim(IO.grids[:, 0].min(), IO.grids[:, 0].max()) ax[i].set_ylim(-IO.grids[:, 1].max(), IO.grids[:, 1].min()) cb = plt.colorbar(out[i][0], ax=ax[i]) cb.set_label("Resistivity ($\Omega$m)") ax[i].set_xlabel("Northing (m)") ax[i].set_ylabel("Elevation (m)") ax[i].set_aspect('equal') ax[0].set_title("True resistivity model") ax[1].set_title("Recovered resistivity model") plt.tight_layout() plt.show()
def setUp(self): # We will assume a vertical inducing field H0 = (50000., 90., 0.) # The magnetization is set along a different direction (induced + remanence) M = np.array([90., 0.]) # Block with an effective susceptibility chi_e = 0.05 # Create grid of points for topography # Lets create a simple Gaussian topo and set the active cells [xx, yy] = np.meshgrid(np.linspace(-200, 200, 50), np.linspace(-200, 200, 50)) b = 100 A = 50 zz = A * np.exp(-0.5 * ((xx / b)**2. + (yy / b)**2.)) topo = np.c_[Utils.mkvc(xx), Utils.mkvc(yy), Utils.mkvc(zz)] # Create and array of observation points xr = np.linspace(-100., 100., 20) yr = np.linspace(-100., 100., 20) X, Y = np.meshgrid(xr, yr) Z = A * np.exp(-0.5 * ((X / b)**2. + (Y / b)**2.)) + 5 # Create a MAGsurvey rxLoc = np.c_[Utils.mkvc(X.T), Utils.mkvc(Y.T), Utils.mkvc(Z.T)] Rx = PF.BaseMag.RxObs(rxLoc) srcField = PF.BaseMag.SrcField([Rx], param=H0) survey = PF.BaseMag.LinearSurvey(srcField) # Create a mesh h = [5, 5, 5] padDist = np.ones((3, 2)) * 100 nCpad = [4, 4, 2] # Get extent of points limx = np.r_[topo[:, 0].max(), topo[:, 0].min()] limy = np.r_[topo[:, 1].max(), topo[:, 1].min()] limz = np.r_[topo[:, 2].max(), topo[:, 2].min()] # Get center of the mesh midX = np.mean(limx) midY = np.mean(limy) midZ = np.mean(limz) nCx = int(limx[0] - limx[1]) / h[0] nCy = int(limy[0] - limy[1]) / h[1] nCz = int(limz[0] - limz[1] + int(np.min(np.r_[nCx, nCy]) / 3)) / h[2] # Figure out full extent required from input extent = np.max(np.r_[nCx * h[0] + padDist[0, :].sum(), nCy * h[1] + padDist[1, :].sum(), nCz * h[2] + padDist[2, :].sum()]) maxLevel = int(np.log2(extent / h[0])) + 1 # Number of cells at the small octree level # For now equal in 3D nCx, nCy, nCz = 2**(maxLevel), 2**(maxLevel), 2**(maxLevel) # Define the mesh and origin mesh = Mesh.TreeMesh( [np.ones(nCx) * h[0], np.ones(nCx) * h[1], np.ones(nCx) * h[2]]) # Set origin mesh.x0 = np.r_[-nCx * h[0] / 2. + midX, -nCy * h[1] / 2. + midY, -nCz * h[2] / 2. + midZ] # Refine the mesh around topography # Get extent of points F = NearestNDInterpolator(topo[:, :2], topo[:, 2]) zOffset = 0 # Cycle through the first 3 octree levels for ii in range(3): dx = mesh.hx.min() * 2**ii nCx = int((limx[0] - limx[1]) / dx) nCy = int((limy[0] - limy[1]) / dx) # Create a grid at the octree level in xy CCx, CCy = np.meshgrid(np.linspace(limx[1], limx[0], nCx), np.linspace(limy[1], limy[0], nCy)) z = F(mkvc(CCx), mkvc(CCy)) # level means number of layers in current OcTree level for level in range(int(nCpad[ii])): mesh.insert_cells(np.c_[mkvc(CCx), mkvc(CCy), z - zOffset], np.ones_like(z) * maxLevel - ii, finalize=False) zOffset += dx mesh.finalize() # Define an active cells from topo actv = Utils.surface2ind_topo(mesh, topo) nC = int(actv.sum()) # Convert the inclination declination to vector in Cartesian M_xyz = Utils.matutils.dip_azimuth2cartesian( np.ones(nC) * M[0], np.ones(nC) * M[1]) # Get the indicies of the magnetized block ind = Utils.ModelBuilder.getIndicesBlock( np.r_[-20, -20, -10], np.r_[20, 20, 25], mesh.gridCC, )[0] # Assign magnetization value, inducing field strength will # be applied in by the :class:`SimPEG.PF.Magnetics` problem model = np.zeros(mesh.nC) model[ind] = chi_e # Remove air cells self.model = model[actv] # Create active map to go from reduce set to full self.actvPlot = Maps.InjectActiveCells(mesh, actv, np.nan) # Creat reduced identity map idenMap = Maps.IdentityMap(nP=nC) # Create the forward model operator prob = PF.Magnetics.MagneticIntegral(mesh, M=M_xyz, chiMap=idenMap, actInd=actv) # Pair the survey and problem survey.pair(prob) # Compute some data and add some random noise data = prob.fields(self.model) # Split the data in components nD = rxLoc.shape[0] std = 5 # nT data += np.random.randn(nD) * std wd = np.ones(nD) * std # Assigne data and uncertainties to the survey survey.dobs = data survey.std = wd ###################################################################### # Equivalent Source # Get the active cells for equivalent source is the top only surf = Utils.modelutils.surface_layer_index(mesh, topo) # Get the layer of cells directyl below topo nC = np.count_nonzero(surf) # Number of active cells # Create active map to go from reduce set to full surfMap = Maps.InjectActiveCells(mesh, surf, np.nan) # Create identity map idenMap = Maps.IdentityMap(nP=nC) # Create static map prob = PF.Magnetics.MagneticIntegral(mesh, chiMap=idenMap, actInd=surf, parallelized=False, equiSourceLayer=True) prob.solverOpts['accuracyTol'] = 1e-4 # Pair the survey and problem if survey.ispaired: survey.unpair() survey.pair(prob) # Create a regularization function, in this case l2l2 reg = Regularization.Sparse(mesh, indActive=surf, mapping=Maps.IdentityMap(nP=nC), scaledIRLS=False) reg.mref = np.zeros(nC) # Specify how the optimization will proceed, # set susceptibility bounds to inf opt = Optimization.ProjectedGNCG(maxIter=20, lower=-np.inf, upper=np.inf, maxIterLS=20, maxIterCG=20, tolCG=1e-3) # Define misfit function (obs-calc) dmis = DataMisfit.l2_DataMisfit(survey) dmis.W = 1. / survey.std # Create the default L2 inverse problem from the above objects invProb = InvProblem.BaseInvProblem(dmis, reg, opt) # Specify how the initial beta is found betaest = Directives.BetaEstimate_ByEig() # Target misfit to stop the inversion, # try to fit as much as possible of the signal, # we don't want to lose anything IRLS = Directives.Update_IRLS(f_min_change=1e-3, minGNiter=1, beta_tol=1e-1) update_Jacobi = Directives.UpdatePreconditioner() # Put all the parts together inv = Inversion.BaseInversion( invProb, directiveList=[betaest, IRLS, update_Jacobi]) # Run the equivalent source inversion mstart = np.ones(nC) * 1e-4 mrec = inv.run(mstart) # Won't store the sensitivity and output 'xyz' data. prob.forwardOnly = True prob.rx_type = 'xyz' prob._G = None prob.modelType = 'amplitude' prob.model = mrec pred = prob.fields(mrec) bx = pred[:nD] by = pred[nD:2 * nD] bz = pred[2 * nD:] bAmp = (bx**2. + by**2. + bz**2.)**0.5 # AMPLITUDE INVERSION # Create active map to go from reduce space to full actvMap = Maps.InjectActiveCells(mesh, actv, -100) nC = int(actv.sum()) # Create identity map idenMap = Maps.IdentityMap(nP=nC) self.mstart = np.ones(nC) * 1e-4 # Create the forward model operator prob = PF.Magnetics.MagneticIntegral(mesh, chiMap=idenMap, actInd=actv, modelType='amplitude', rx_type='xyz') prob.model = self.mstart # Change the survey to xyz components surveyAmp = PF.BaseMag.LinearSurvey(survey.srcField) # Pair the survey and problem surveyAmp.pair(prob) # Create a regularization function, in this case l2l2 wr = np.sum(prob.G**2., axis=0)**0.5 wr = (wr / np.max(wr)) # Re-set the observations to |B| surveyAmp.dobs = bAmp surveyAmp.std = wd # Create a sparse regularization reg = Regularization.Sparse(mesh, indActive=actv, mapping=idenMap) reg.norms = np.c_[0, 0, 0, 0] reg.mref = np.zeros(nC) reg.cell_weights = wr # Data misfit function dmis = DataMisfit.l2_DataMisfit(surveyAmp) dmis.W = 1. / surveyAmp.std # Add directives to the inversion opt = Optimization.ProjectedGNCG(maxIter=30, lower=0., upper=1., maxIterLS=20, maxIterCG=20, tolCG=1e-3) invProb = InvProblem.BaseInvProblem(dmis, reg, opt) # Here is the list of directives betaest = Directives.BetaEstimate_ByEig() # Specify the sparse norms IRLS = Directives.Update_IRLS(f_min_change=1e-3, minGNiter=1, coolingRate=1, betaSearch=False) # The sensitivity weights are update between each iteration. update_SensWeight = Directives.UpdateSensitivityWeights() update_Jacobi = Directives.UpdatePreconditioner(threshold=1 - 3) # Put all together self.inv = Inversion.BaseInversion( invProb, directiveList=[betaest, IRLS, update_SensWeight, update_Jacobi]) self.mesh = mesh
def run_inversion( m0, survey, actind, mesh, std, eps, maxIter=15, beta0_ratio=1e0, coolingFactor=5, coolingRate=2, upper=np.inf, lower=-np.inf, use_sensitivity_weight=False, alpha_s=1e-4, alpha_x=1., alpha_y=1., alpha_z=1., ): """ Run IP inversion """ dmisfit = DataMisfit.l2_DataMisfit(survey) uncert = abs(survey.dobs) * std + eps dmisfit.W = 1./uncert # Map for a regularization regmap = Maps.IdentityMap(nP=int(actind.sum())) # Related to inversion if use_sensitivity_weight: reg = Regularization.Sparse( mesh, indActive=actind, mapping=regmap ) reg.alpha_s = alpha_s reg.alpha_x = alpha_x reg.alpha_y = alpha_y reg.alpha_z = alpha_z else: reg = Regularization.Sparse( mesh, indActive=actind, mapping=regmap, cell_weights=mesh.vol[actind] ) reg.alpha_s = alpha_s reg.alpha_x = alpha_x reg.alpha_y = alpha_y reg.alpha_z = alpha_z opt = Optimization.ProjectedGNCG(maxIter=maxIter, upper=upper, lower=lower) invProb = InvProblem.BaseInvProblem(dmisfit, reg, opt) beta = Directives.BetaSchedule( coolingFactor=coolingFactor, coolingRate=coolingRate ) betaest = Directives.BetaEstimate_ByEig(beta0_ratio=beta0_ratio) target = Directives.TargetMisfit() # Need to have basice saving function if use_sensitivity_weight: updateSensW = Directives.UpdateSensitivityWeights() update_Jacobi = Directives.UpdatePreconditioner() directiveList = [ beta, betaest, target, update_Jacobi ] else: directiveList = [ beta, betaest, target ] inv = Inversion.BaseInversion( invProb, directiveList=directiveList ) opt.LSshorten = 0.5 opt.remember('xc') # Run inversion mopt = inv.run(m0) return mopt, invProb.dpred
def run(plotIt=True, survey_type="dipole-dipole"): np.random.seed(1) # Initiate I/O class for DC IO = DC.IO() # Obtain ABMN locations xmin, xmax = 0., 200. ymin, ymax = 0., 0. zmin, zmax = 0, 0 endl = np.array([[xmin, ymin, zmin], [xmax, ymax, zmax]]) # Generate DC survey object survey = DC.Utils.gen_DCIPsurvey(endl, survey_type=survey_type, dim=2, a=10, b=10, n=10) survey.getABMN_locations() survey = IO.from_ambn_locations_to_survey( survey.a_locations, survey.b_locations, survey.m_locations, survey.n_locations, survey_type, data_dc_type='volt' ) # Obtain 2D TensorMesh mesh, actind = IO.set_mesh() topo, mesh1D = DC.Utils.genTopography(mesh, -10, 0, its=100) actind = Utils.surface2ind_topo(mesh, np.c_[mesh1D.vectorCCx, topo]) survey.drapeTopo(mesh, actind, option="top") # Build a conductivity model blk_inds_c = Utils.ModelBuilder.getIndicesSphere( np.r_[60., -25.], 12.5, mesh.gridCC ) blk_inds_r = Utils.ModelBuilder.getIndicesSphere( np.r_[140., -25.], 12.5, mesh.gridCC ) layer_inds = mesh.gridCC[:, 1] > -5. sigma = np.ones(mesh.nC)*1./100. sigma[blk_inds_c] = 1./10. sigma[blk_inds_r] = 1./1000. sigma[~actind] = 1./1e8 rho = 1./sigma # Show the true conductivity model if plotIt: fig = plt.figure(figsize=(12, 3)) ax = plt.subplot(111) temp = rho.copy() temp[~actind] = np.nan out = mesh.plotImage( temp, grid=True, ax=ax, gridOpts={'alpha': 0.2}, clim=(10, 1000), pcolorOpts={"cmap": "viridis", "norm": colors.LogNorm()} ) ax.plot( survey.electrode_locations[:, 0], survey.electrode_locations[:, 1], 'k.' ) ax.set_xlim(IO.grids[:, 0].min(), IO.grids[:, 0].max()) ax.set_ylim(-IO.grids[:, 1].max(), IO.grids[:, 1].min()) cb = plt.colorbar(out[0]) cb.set_label("Resistivity (ohm-m)") ax.set_aspect('equal') plt.show() # Use Exponential Map: m = log(rho) actmap = Maps.InjectActiveCells( mesh, indActive=actind, valInactive=np.log(1e8) ) mapping = Maps.ExpMap(mesh) * actmap # Generate mtrue mtrue = np.log(rho[actind]) # Generate 2.5D DC problem # "N" means potential is defined at nodes prb = DC.Problem2D_N( mesh, rhoMap=mapping, storeJ=True, Solver=Solver ) # Pair problem with survey try: prb.pair(survey) except: survey.unpair() prb.pair(survey) geometric_factor = survey.set_geometric_factor( data_type="apparent_resistivity", survey_type='dipole-dipole', space_type='half-space' ) # Make synthetic DC data with 5% Gaussian noise dtrue = survey.makeSyntheticData(mtrue, std=0.05, force=True) IO.data_dc = dtrue # Show apparent resisitivty pseudo-section if plotIt: IO.plotPseudoSection( data=survey.dobs, data_type='apparent_resistivity' ) # Show apparent resisitivty histogram if plotIt: fig = plt.figure() out = hist(survey.dobs, bins=20) plt.xlabel("Apparent Resisitivty ($\Omega$m)") plt.show() # Set initial model based upon histogram m0 = np.ones(actmap.nP)*np.log(100.) # Set uncertainty # floor (10 ohm-m) eps = 1. # percentage std = 0.05 dmisfit = DataMisfit.l2_DataMisfit(survey) uncert = abs(survey.dobs) * std + eps dmisfit.W = 1./uncert # Map for a regularization regmap = Maps.IdentityMap(nP=int(actind.sum())) # Related to inversion reg = Regularization.Sparse(mesh, indActive=actind, mapping=regmap) opt = Optimization.InexactGaussNewton(maxIter=15) invProb = InvProblem.BaseInvProblem(dmisfit, reg, opt) beta = Directives.BetaSchedule(coolingFactor=5, coolingRate=2) betaest = Directives.BetaEstimate_ByEig(beta0_ratio=1e0) target = Directives.TargetMisfit() updateSensW = Directives.UpdateSensitivityWeights() update_Jacobi = Directives.UpdatePreconditioner() inv = Inversion.BaseInversion( invProb, directiveList=[ beta, betaest, target, updateSensW, update_Jacobi ] ) prb.counter = opt.counter = Utils.Counter() opt.LSshorten = 0.5 opt.remember('xc') # Run inversion mopt = inv.run(m0) # Get diag(JtJ) mask_inds = np.ones(mesh.nC, dtype=bool) jtj = np.sqrt(updateSensW.JtJdiag[0]) jtj /= jtj.max() temp = np.ones_like(jtj, dtype=bool) temp[jtj > 0.005] = False mask_inds[actind] = temp actind_final = np.logical_and(actind, ~mask_inds) jtj_cc = np.ones(mesh.nC)*np.nan jtj_cc[actind] = jtj # Show the sensitivity if plotIt: fig = plt.figure(figsize=(12, 3)) ax = plt.subplot(111) temp = rho.copy() temp[~actind] = np.nan out = mesh.plotImage( jtj_cc, grid=True, ax=ax, gridOpts={'alpha': 0.2}, clim=(0.005, 0.5), pcolorOpts={"cmap": "viridis", "norm": colors.LogNorm()} ) ax.plot( survey.electrode_locations[:, 0], survey.electrode_locations[:, 1], 'k.' ) ax.set_xlim(IO.grids[:, 0].min(), IO.grids[:, 0].max()) ax.set_ylim(-IO.grids[:, 1].max(), IO.grids[:, 1].min()) cb = plt.colorbar(out[0]) cb.set_label("Sensitivity") ax.set_aspect('equal') plt.show() # Convert obtained inversion model to resistivity # rho = M(m), where M(.) is a mapping rho_est = mapping*mopt rho_est[~actind_final] = np.nan rho_true = rho.copy() rho_true[~actind_final] = np.nan # show recovered conductivity if plotIt: vmin, vmax = rho.min(), rho.max() fig, ax = plt.subplots(2, 1, figsize=(20, 6)) out1 = mesh.plotImage( rho_true, clim=(10, 1000), pcolorOpts={"cmap": "viridis", "norm": colors.LogNorm()}, ax=ax[0] ) out2 = mesh.plotImage( rho_est, clim=(10, 1000), pcolorOpts={"cmap": "viridis", "norm": colors.LogNorm()}, ax=ax[1] ) out = [out1, out2] for i in range(2): ax[i].plot( survey.electrode_locations[:, 0], survey.electrode_locations[:, 1], 'kv' ) ax[i].set_xlim(IO.grids[:, 0].min(), IO.grids[:, 0].max()) ax[i].set_ylim(-IO.grids[:, 1].max(), IO.grids[:, 1].min()) cb = plt.colorbar(out[i][0], ax=ax[i]) cb.set_label("Resistivity ($\Omega$m)") ax[i].set_xlabel("Northing (m)") ax[i].set_ylabel("Elevation (m)") ax[i].set_aspect('equal') plt.tight_layout() plt.show()
def setUp(self): # We will assume a vertical inducing field H0 = (50000., 90., 0.) # The magnetization is set along a different direction (induced + remanence) M = np.array([90., 0.]) # Block with an effective susceptibility chi_e = 0.05 # Create grid of points for topography # Lets create a simple Gaussian topo and set the active cells [xx, yy] = np.meshgrid(np.linspace(-200, 200, 50), np.linspace(-200, 200, 50)) b = 100 A = 50 zz = A * np.exp(-0.5 * ((xx / b)**2. + (yy / b)**2.)) topo = np.c_[Utils.mkvc(xx), Utils.mkvc(yy), Utils.mkvc(zz)] # Create and array of observation points xr = np.linspace(-100., 100., 20) yr = np.linspace(-100., 100., 20) X, Y = np.meshgrid(xr, yr) Z = A * np.exp(-0.5 * ((X / b)**2. + (Y / b)**2.)) + 5 # Create a MAGsurvey xyzLoc = np.c_[Utils.mkvc(X.T), Utils.mkvc(Y.T), Utils.mkvc(Z.T)] Rx = PF.BaseMag.RxObs(xyzLoc) srcField = PF.BaseMag.SrcField([Rx], param=H0) survey = PF.BaseMag.LinearSurvey(srcField) # Create a mesh h = [5, 5, 5] padDist = np.ones((3, 2)) * 100 nCpad = [4, 4, 2] self.mesh = meshutils.mesh_builder_xyz( xyzLoc, h, padding_distance=padDist, mesh_type='TREE', ) self.mesh = meshutils.refine_tree_xyz( self.mesh, topo, method='surface', octree_levels=nCpad, octree_levels_padding=nCpad, finalize=True, ) # Define an active cells from topo actv = Utils.surface2ind_topo(self.mesh, topo) nC = int(actv.sum()) # Convert the inclination declination to vector in Cartesian M_xyz = Utils.matutils.dip_azimuth2cartesian( np.ones(nC) * M[0], np.ones(nC) * M[1]) # Get the indicies of the magnetized block ind = Utils.ModelBuilder.getIndicesBlock( np.r_[-20, -20, -15], np.r_[20, 20, 20], self.mesh.gridCC, )[0] # Assign magnetization value, inducing field strength will # be applied in by the :class:`SimPEG.PF.Magnetics` problem model = np.zeros(self.mesh.nC) model[ind] = chi_e # Remove air cells self.model = model[actv] # Create active map to go from reduce set to full self.actvPlot = Maps.InjectActiveCells(self.mesh, actv, np.nan) # Creat reduced identity map idenMap = Maps.IdentityMap(nP=nC) # Create the forward model operator prob = PF.Magnetics.MagneticIntegral(self.mesh, M=M_xyz, chiMap=idenMap, actInd=actv) # Pair the survey and problem survey.pair(prob) # Compute some data and add some random noise data = prob.fields(self.model) # Split the data in components nD = xyzLoc.shape[0] std = 5 # nT data += np.random.randn(nD) * std wd = np.ones(nD) * std # Assigne data and uncertainties to the survey survey.dobs = data survey.std = wd ###################################################################### # Equivalent Source # Get the active cells for equivalent source is the top only surf = Utils.modelutils.surface_layer_index(self.mesh, topo) # Get the layer of cells directyl below topo nC = np.count_nonzero(surf) # Number of active cells # Create active map to go from reduce set to full surfMap = Maps.InjectActiveCells(self.mesh, surf, np.nan) # Create identity map idenMap = Maps.IdentityMap(nP=nC) # Create static map prob = PF.Magnetics.MagneticIntegral(self.mesh, chiMap=idenMap, actInd=surf, parallelized=False, equiSourceLayer=True) prob.solverOpts['accuracyTol'] = 1e-4 # Pair the survey and problem if survey.ispaired: survey.unpair() survey.pair(prob) # Create a regularization function, in this case l2l2 reg = Regularization.Sparse(self.mesh, indActive=surf, mapping=Maps.IdentityMap(nP=nC), scaledIRLS=False) reg.mref = np.zeros(nC) # Specify how the optimization will proceed, # set susceptibility bounds to inf opt = Optimization.ProjectedGNCG(maxIter=20, lower=-np.inf, upper=np.inf, maxIterLS=20, maxIterCG=20, tolCG=1e-3) # Define misfit function (obs-calc) dmis = DataMisfit.l2_DataMisfit(survey) dmis.W = 1. / survey.std # Create the default L2 inverse problem from the above objects invProb = InvProblem.BaseInvProblem(dmis, reg, opt) # Specify how the initial beta is found betaest = Directives.BetaEstimate_ByEig() # Target misfit to stop the inversion, # try to fit as much as possible of the signal, # we don't want to lose anything IRLS = Directives.Update_IRLS(f_min_change=1e-3, minGNiter=1, beta_tol=1e-1) update_Jacobi = Directives.UpdatePreconditioner() # Put all the parts together inv = Inversion.BaseInversion( invProb, directiveList=[betaest, IRLS, update_Jacobi]) # Run the equivalent source inversion mstart = np.ones(nC) * 1e-4 mrec = inv.run(mstart) # Won't store the sensitivity and output 'xyz' data. prob.forwardOnly = True prob.rx_type = 'xyz' prob._G = None prob.modelType = 'amplitude' prob.model = mrec pred = prob.fields(mrec) bx = pred[:nD] by = pred[nD:2 * nD] bz = pred[2 * nD:] bAmp = (bx**2. + by**2. + bz**2.)**0.5 # AMPLITUDE INVERSION # Create active map to go from reduce space to full actvMap = Maps.InjectActiveCells(self.mesh, actv, -100) nC = int(actv.sum()) # Create identity map idenMap = Maps.IdentityMap(nP=nC) self.mstart = np.ones(nC) * 1e-4 # Create the forward model operator prob = PF.Magnetics.MagneticIntegral(self.mesh, chiMap=idenMap, actInd=actv, modelType='amplitude', rx_type='xyz') prob.model = self.mstart # Change the survey to xyz components surveyAmp = PF.BaseMag.LinearSurvey(survey.srcField) # Pair the survey and problem surveyAmp.pair(prob) # Create a regularization function, in this case l2l2 wr = np.sum(prob.G**2., axis=0)**0.5 wr /= np.max(wr) # Re-set the observations to |B| surveyAmp.dobs = bAmp surveyAmp.std = wd # Create a sparse regularization reg = Regularization.Sparse(self.mesh, indActive=actv, mapping=idenMap) reg.norms = np.c_[0, 0, 0, 0] reg.mref = np.zeros(nC) reg.cell_weights = wr # Data misfit function dmis = DataMisfit.l2_DataMisfit(surveyAmp) dmis.W = 1. / surveyAmp.std # Add directives to the inversion opt = Optimization.ProjectedGNCG(maxIter=30, lower=0., upper=1., maxIterLS=20, maxIterCG=20, tolCG=1e-3) invProb = InvProblem.BaseInvProblem(dmis, reg, opt) # Here is the list of directives betaest = Directives.BetaEstimate_ByEig() # Specify the sparse norms IRLS = Directives.Update_IRLS(f_min_change=1e-3, minGNiter=1, coolingRate=1, betaSearch=False) # The sensitivity weights are update between each iteration. update_SensWeight = Directives.UpdateSensitivityWeights() update_Jacobi = Directives.UpdatePreconditioner() # Put all together self.inv = Inversion.BaseInversion( invProb, directiveList=[betaest, IRLS, update_SensWeight, update_Jacobi])