{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import scipy.sparse as sp\n", "from SimPEG import Mesh, Utils, Solver \n", "from scipy.constants import mu_0, epsilon_0\n", "import matplotlib.pyplot as plt\n", "\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Numerical simulation of the 1D Magnetotelluric (MT) problem" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Purpose\n", "\n", "With [SimPEG's](http://simpeg.xyz) mesh class, we discretize Maxwell's equations for a 1D magnetotelluric problem. We then solve for both electric and magnetic fields, and evaluate data at a receing location. There are some milestones to be accomplished:\n", "\n", "- Introduce differential operators and the terminology used in the SimPEG mesh class\n", "\n", "- Set up boundary conditions\n", "\n", "- Set up an linear system $\\mathbf{A}\\mathbf{u} = \\mathbf{rhs}$, compute the fields, $\\mathbf{u}$\n", "\n", "- Evaluate the data at a receiver location: apparent resistivity and phase\n", "\n", "- Recognize extensibility of this example to higher dimensions: 2D and 3D" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Physics: Maxwell's equations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The governing equations for electromagnetic problems are Maxwell's equations. Here, we show them in the frequency domain. For more background on Maxwell's equations, we recommend http://em.geosci.xyz and [Ward & Hohmann, 1988](http://library.seg.org/doi/abs/10.1190/1.9781560802631.ch4).\n", "\n", "$$\\nabla \\times \\mathbf{E} + \\imath\\omega \\mu \\mathbf{H} = 0 $$\n", "\n", "$$\\nabla \\times \\mathbf{H} - (\\sigma + \\imath \\omega \\epsilon) \\mathbf{E} = 0$$\n", "\n", "where\n", "\n", "- $\\mathbf{E}$ is the electric field (V/m)\n", "- $\\mathbf{H}$ is the magnetic field (A/m)\n", "- $\\omega = 2\\pi f$ is the angular frequency\n", "- $\\mu$ is the magnetic permeability, often taken to be that of free spase ($\\mu_0 = 4\\pi\\times 10^{-7}$ H/m)\n", "- $\\sigma$ is the electrical conductivity (S/m). \n", "- $\\epsilon$ is the dielectric permittivity, often taken to be that of free space ($\\epsilon = 8.85 \\times 10^{-12}$ F/m)\n", "\n", "For convienence, we will make the substitution: $\\hat{\\sigma} = \\sigma + \\imath \\omega \\epsilon$ and write Maxwell's equations as\n", "\n", "$$\\nabla \\times \\mathbf{E} + \\imath\\omega \\mu \\mathbf{H} = 0$$\n", "\n", "$$\\nabla \\times \\mathbf{H} - \\hat{\\sigma} \\mathbf{E} = 0$$\n", "\n", "The first equation is [Faraday's Law](http://em.geosci.xyz/content/maxwell1_fundamentals/formative_laws/faraday.html), and the second is [Ampere's Law](http://em.geosci.xyz/content/maxwell1_fundamentals/formative_laws/ampere_maxwell.html)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For the Magnetotelluric problem, we are interested in examining Maxwell's equations for a plane wave source. We consider a vertically propagating plane wave. For a 1D earth model, the fields and fluxes are defined by horizontal, orthogonal electric and magnetic fields, so we take\n", "\n", "$$\\mathbf{E} = E_x\\mathbf{\\hat{x}}$$\n", "$$\\mathbf{H} = H_y\\mathbf{\\hat{y}}$$\n", "\n", "\"plane_wave\"\n", "\n", "The coordinate system we use is right-handed, and $z$ is positive up. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, our governing equations simplify to scalar equations\n", "\n", "$$ \\frac{\\partial E_x}{\\partial z} + \\imath \\omega \\mu H_y = 0$$\n", "\n", "$$-\\frac{\\partial H_y}{\\partial z} + \\hat{\\sigma} E_x = 0$$\n", "\n", "with the boundary conditions:\n", "\n", "$$E_x (z=0) = 1$$\n", "\n", "$$E_x (z=-\\infty) = 0$$\n", "\n", "To solve the forward problem, the \n", "- **knowns** are: $\\omega$, $\\mu$, $\\hat{\\sigma}$, boundary conditions\n", "- **unknowns** are: $E_x$, $H_y$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Discretiation, the Short version. \n", "\n", "**TL;DR.** Here is the answer. \n", "If you want to see the full derivation, checkout The Gory Details \n", "\n", "\n", "We define physical properties at cell centers, and stagger the electric and magnetic fields\n", "\n", "- $\\sigma$, $\\mu$, $\\epsilon$ : cell centers\n", "- $E_x$: cell centers\n", "- $H_y$: faces\n", "\n", " \n", "\n", "and use a finite difference approach to define the operators, this gives us the discrete system of equations\n", "\n", "$$\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " \\mathbf{Grad} & \\imath \\omega \\mathbf{M}^{f}_{\\mu} \\\\[0.3em]\n", " \\mathbf{M}^{cc}_{\\hat{\\sigma}} & \\mathbf{Div} \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{A}}\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " \\mathbf{e_x} \\\\[0.3em]\n", " \\mathbf{h_y} \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{u}}\n", "=\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " - \\mathbf{B}\\mathbf{e_x}^{BC} \\\\[0.3em]\n", " \\boldsymbol{0} \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{rhs}}\n", "$$\n", "\n", "with \n", "\n", "- $\\mathbf{e_x}$: Discrete $E_x$, on cell centers $[\\text{nC} \\times 1]$\n", "\n", "- $\\mathbf{h_y}$: Dicrete $H_x$, on cell faces $[(\\text{nC}+1) \\times 1]$\n", "\n", "- $ \\mathbf{Grad}$: Discrete gradient operator $[\\text{nC} \\times (\\text{nC}+1)]$\n", "\n", "- $ \\mathbf{Div}$: Discrete divergence operator $[(\\text{nC}+1) \\times \\text{nC}]$\n", "\n", "- $\\mathbf{M}^{f}_{\\boldsymbol{\\mu}} = \\mathbf{diag}(\\mathbf{Av^{cc2f}} \\boldsymbol{\\mu})$ $[(\\text{nC}+1) \\times (\\text{nC}+1)]$\n", "\n", "- $\\mathbf{M}^{cc}_{\\boldsymbol{\\hat{\\sigma}}} = \\mathbf{diag}(\\boldsymbol{\\hat{\\sigma}})$ $[\\text{nC} \\times \\text{nC}]$\n", "\n", "- $\\mathbf{B} \\mathbf{e_x}^{BC}$ handles the boundary conditions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Designing a mesh\n", "\n", "When designing a mesh, we need to ensure that we can capture the physics (what should the thickness of the finest cells be?) and make sure that the boundary is far enough away so that the fields have decayed (how far do we need to go to approximate $\\infty$??). To address these, we look at the [skin depth equation](http://em.geosci.xyz/content/maxwell1_fundamentals/plane_waves_in_homogeneous_media/frequency/analytic_solution.html#attenuation-and-skin-depth), which tells us over what distance we expect electromagnetic fields to have decayed by a factor of $1/e$ in a conductive medium:\n", "\n", "$$\n", "\\delta = \\frac{500}{\\sqrt{\\sigma f}}\n", "$$\n", "\n", "- The finest cells capture the behaviour of the highest frequency near the surface\n", "- The mesh needs to extend far enough so that the fields at the lowest frequency have sufficiently decayed" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lets start by considering:\n", "- a half-space with conductivity $\\sigma = 10^{-2}$ S/m \n", "- a maximum frequency of 1000 Hz\n", "- a minimum frequency of 0.01 Hz" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "sigma_halfspace = 1e-2\n", "fmax, fmin = 1e3, 1e-2" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def skin_depth(sigma, f):\n", " return 500./np.sqrt(sigma*f)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The minimum skin depth is 158.1 m\n", "The maximum skin depth is 50000.0 m\n" ] } ], "source": [ "skin_depth_min = skin_depth(sigma_halfspace, fmax)\n", "skin_depth_max = skin_depth(sigma_halfspace, fmin)\n", "\n", "print(\"The minimum skin depth is {:2.1f} m\".format(skin_depth_min))\n", "print(\"The maximum skin depth is {:2.1f} m\".format(skin_depth_max))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To ensure that we are capturing the physics and have a sufficiently far boundary, we will choose\n", "- a minimum cell size of $\\delta_{\\text{min}}/4$\n", "- a padding distance that extends to $2 \\delta_{\\text{max}}$" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The smallest cell size should be 39.5 m\n", "The mesh should extend 1.0e+05 m\n" ] } ], "source": [ "print(\n", " \"The smallest cell size should be {:2.1f} m\".format(\n", " skin_depth_min / 4.\n", " )\n", ")\n", "print(\n", " \"The mesh should extend {:1.1e} m\".format(\n", " skin_depth_max * 2.\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Set up a mesh\n", "\n", "Here, we use the [SimPEG Mesh class](http://docs.simpeg.xyz) to set up the mesh, differential operators, and handy properties and methods that handle counting and plotting. " ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The mesh extends 1.2e+05m, is that far enough?\n" ] }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "cs = 39. # core cell size\n", "npad = 25 # number of padding cells\n", "ncz = 100 # number of core cells\n", "\n", "# define a tensor mesh\n", "hz = [(cs, npad, -1.3), (cs, ncz)] \n", "mesh = Mesh.TensorMesh([hz], x0='N') # put the origin at the surface \n", "\n", "# plot the mesh\n", "fig, ax = plt.subplots(1,1, figsize=(8, 3))\n", "mesh.plotGrid(centers=True, faces=True, ax=ax)\n", "ax.legend([\"centers\", \"faces\"])\n", "ax.invert_xaxis() # so that the surface is on our left hand side\n", "ax.set_xlabel('z (m)')\n", "ax.grid(which=\"both\", linewidth=0.5)\n", "\n", "print(\n", " \"The mesh extends {:1.1e}m, is that far enough?\".format(\n", " mesh.hx.sum()\n", " )\n", ")" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " ---- 1-D TensorMesh ---- \n", " x0: -122984.33\n", " nCx: 125\n", " hx: 27520.00, 21169.23, 16284.02, 12526.17, 9635.52, 7411.94, 5701.49, 4385.76, 3373.66, 2595.12, 1996.25, 1535.58, 1181.21, 908.63, 698.94, 537.65, 413.58, 318.13, 244.72, 188.25, 144.80, 111.39, 85.68, 65.91, 50.70, 100*39.00,\n" ] } ], "source": [ "print(mesh)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Assemble the discrete system of equations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#### Model parameters\n", "\n", "We start with a half space that has physical properties\n", "- $\\sigma = 10^{-2}$ S/m\n", "- $\\mu = \\mu_0$\n", "- $\\epsilon = \\epsilon_0$\n", "\n", "and define these on the mesh" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "There are 125 cell centers. \n", " sigma is 125 elements long, all cells have a value of 1.00e-02 S/m \n", " mu is 125 elements long, all cells have a value of 1.26e-06 H/m \n", " epsilon is 125 elements long, all cells have a value of 8.85e-12 F/m \n", "\n" ] } ], "source": [ "sigma = np.ones(mesh.nC)*sigma_halfspace # conductivity values for all cells\n", "mu = np.ones(mesh.nC)*mu_0 # magnetic permeability values for all cells\n", "epsilon = np.ones(mesh.nC)*epsilon_0 # dielectric constant values for all cells\n", "\n", "print(\n", " \"There are {:1.0f} cell centers. \\n\"\n", " \" sigma is {:1.0f} elements long, all cells have a value of {:1.2e} S/m \\n\"\n", " \" mu is {:1.0f} elements long, all cells have a value of {:1.2e} H/m \\n\"\n", " \" epsilon is {:1.0f} elements long, all cells have a value of {:1.2e} F/m \\n\".format(\n", " mesh.nC, \n", " len(sigma), sigma_halfspace,\n", " len(mu), mu_0,\n", " len(epsilon), epsilon_0\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We will pick a single frequency to work with for now\n", "- f = 1000 Hz" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "frequency = 1e3 # Frequency (Hz)\n", "omega = 2*np.pi*frequency # Angular frequency (rad/s)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Here, we will adopt the quasistatic assumption and ignore displacement current $(i \\epsilon \\omega)$. To explore the impacts of this assumption, uncomment the next second line" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "sigmahat = sigma # quasi-static assumption\n", "# sigmahat = sigma + 1j*epsilon*omega # includes displacement current" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The system we want to solve is\n", "$$\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " \\mathbf{Grad} & \\imath \\omega \\mathbf{M}^{f}_{\\mu} \\\\[0.3em]\n", " \\mathbf{M}^{cc}_{\\hat{\\sigma}} & \\mathbf{Div} \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{A}}\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " \\mathbf{e_x} \\\\[0.3em]\n", " \\mathbf{h_y} \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{u}}\n", "=\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " - \\mathbf{B}\\mathbf{e_x}^{BC} \\\\[0.3em]\n", " \\boldsymbol{0} \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{rhs}}\n", "$$\n", "\n", "so we need to construct each of the operators. For details, see: The Gory Details \n", "\n", "We start by laying our the piece and will then assemble the full matrix system. " ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# Grad \n", "mesh.setCellGradBC([['dirichlet', 'dirichlet']]) # Setup boundary conditions\n", "Grad = mesh.cellGrad # Gradient matrix\n", "\n", "# MfMu\n", "Mmu = Utils.sdiag(mesh.aveCC2F * mu) \n", "\n", "# Mccsigma\n", "Msighat = Utils.sdiag(sigmahat) \n", "\n", "# Div\n", "Div = mesh.faceDiv # Divergence matrix\n", "\n", "# Right Hand Side\n", "B = mesh.cellGradBC # a matrix for boundary conditions\n", "Exbc = np.r_[0., 1.] # boundary values for Ex" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "# Assemble the matrix\n", "\n", "# A-matrix\n", "A = sp.vstack([\n", " sp.hstack([Grad, 1j*omega*Mmu]), # Top row of A matrix\n", " sp.hstack((Msighat, Div)) # Bottom row of A matrix\n", "])\n", "\n", "# Right-hand side\n", "rhs = np.r_[\n", " -B*Exbc, \n", " np.zeros(mesh.nC)\n", "] " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now that we have all of the pieces, we can go ahead and solve the MT system" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 2.16 ms, sys: 1.82 ms, total: 3.99 ms\n", "Wall time: 2.99 ms\n" ] } ], "source": [ "%%time\n", "Ainv = Solver(A) # Factorize A matrix\n", "sol = Ainv*rhs # Solve A^-1 rhs = sol\n", "Ex = sol[:mesh.nC] # Extract Ex from solution vector u\n", "Hy = sol[mesh.nC:mesh.nC+mesh.nN] # Extract Hy from solution vector u" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Impedance, Apparent Resistivity, and Phase\n", "\n", "MT data are natural source data, meaning that the source is free! but we don't know its amplitude. To account for this, the data we examine are typically transfer functions that involve ratios of the electric and magnetic fields. For the 1D problem, the Impedance is simply given by\n", "\n", "$$\n", "Z_{xy} = - \\frac{E_x}{H_y}\n", "$$\n", "\n", "(The negative is because we have defined a coordinate system such that z is positive up) \n", "\n", "$Z_{xy}$ is a complex number, so we can look at real and imaginary components or amplitude and phase. \n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Impedance: 6.2e-01 + 6.4e-01i\n", "or in terms of Amplidude: 8.9e-01 and phase: 45.9 degrees\n" ] } ], "source": [ "Zxy = - 1./Hy[-1] # Impedance at the surface\n", "\n", "print(\"Impedance: {:1.1e} + {:1.1e}i\".format(Zxy.real, Zxy.imag))\n", "print(\"or in terms of Amplidude: {:1.1e} and phase: {:1.1f} degrees\".format(\n", " np.absolute(Zxy), np.rad2deg(np.arctan(Zxy.imag / Zxy.real)))\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Often is useful to translate the impedance to an apparent resistivity ($\\rho_a$) and phase. \n", "\n", "$$\n", "\\rho_a = \\frac{1}{\\mu_0\\omega} \\big|Z_{xy}\\big|^2\n", "$$\n", "\n", "$$\n", "\\phi = \\tan^{-1}\\left(\\frac{\\text{Im}(Z_{xy})}{\\text{Re}(Z_{xy})}\\right)\n", "$$\n", "\n", "For a half-space, we expect the apparent resistivity to equal the true resistivity, and the phase to be $45^\\circ$" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Apparent Resistivity: 100.0, Phase: 45.9\n" ] } ], "source": [ "app_res = abs(Zxy)**2 / (mu_0*omega)\n", "app_phase = np.rad2deg(np.arctan(Zxy.imag / Zxy.real))\n", "\n", "print(\n", " \"Apparent Resistivity: {:1.1f}, Phase: {:1.1f}\".format(\n", " app_res, app_phase\n", " )\n", ")" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Note that the apparent resistivity, 100.0 is the same as the true half-space 100.0\n" ] } ], "source": [ "print(\n", " \"Note that the apparent resistivity, {:1.1f} \"\n", " \"is the same as the true half-space {:1.1f}\".format(\n", " app_res,\n", " 1./sigma_halfspace \n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Put it all together\n", "\n", "Here, we define a function that performs an MT simulation so that we can readily compute the Magnetotelluric response at multiple frequencies and for a variety of models. We write this function to the file MTsimulation.py so that we can import it and use it in later notebooks. Uncomment the first three lines to write out the file again. " ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [], "source": [ "# %%writefile MTforward.py\n", "# import numpy as np\n", "# import scipy.sparse as sp\n", "# from scipy.constants import mu_0\n", "# from SimPEG import Utils, Solver\n", "\n", "\n", "def simulateMT(mesh, sigma, frequency, rtype=\"app_res\"):\n", " \"\"\"\n", " Compute apparent resistivity and phase at each frequency. \n", " Return apparent resistivity and phase for rtype=\"app_res\",\n", " or impedance for rtype=\"impedance\" \n", " \"\"\"\n", " \n", " # Angular frequency (rad/s)\n", " def omega(freq):\n", " return 2*np.pi*freq\n", " \n", " # make sure we are working with numpy arrays\n", " if type(frequency) is float:\n", " frequency = np.r_[frequency] # make it a list to loop over later if it is just a scalar\n", " elif type(frequency) is list: \n", " frequency = np.array(frequency)\n", " \n", " # Frequency independent pieces of the A matrix\n", " # Grad \n", " mesh.setCellGradBC([['dirichlet', 'dirichlet']]) # Setup boundary conditions\n", " Grad = mesh.cellGrad # Gradient matrix\n", "\n", " # MfMu\n", " mu = np.ones(mesh.nC)*mu_0 # magnetic permeability values for all cells\n", " Mmu = Utils.sdiag(mesh.aveCC2F * mu) \n", "\n", " # Mccsigma\n", " sigmahat = sigma # quasi-static assumption\n", " Msighat = Utils.sdiag(sigmahat) \n", "\n", " # Div\n", " Div = mesh.faceDiv # Divergence matrix\n", "\n", " # Right Hand Side\n", " B = mesh.cellGradBC # a matrix for boundary conditions\n", " Exbc = np.r_[0., 1.] # boundary values for Ex\n", " \n", " # Right-hand side\n", " rhs = np.r_[\n", " -B*Exbc, \n", " np.zeros(mesh.nC)\n", " ] \n", " \n", " # loop over frequencies \n", " Zxy = []\n", " for freq in frequency: \n", "\n", " # A-matrix\n", " A = sp.vstack([\n", " sp.hstack([Grad, 1j*omega(freq)*Mmu]), # Top row of A matrix\n", " sp.hstack((Msighat, Div)) # Bottom row of A matrix\n", " ])\n", " \n", " Ainv = Solver(A) # Factorize A matrix\n", " sol = Ainv*rhs # Solve A^-1 rhs = sol\n", " Ex = sol[:mesh.nC] # Extract Ex from solution vector u\n", " Hy = sol[mesh.nC:mesh.nC+mesh.nN] # Extract Hy from solution vector u\n", "\n", " Zxy.append(- 1./Hy[-1]) # Impedance at the surface\n", " \n", " # turn it into an array\n", " Zxy = np.array(Zxy)\n", "\n", " # return impedance or apparent resistivity and phase \n", " if rtype.lower() == \"impedance\":\n", " return Zxy\n", "\n", " elif rtype.lower() == \"app_res\":\n", " app_res = abs(Zxy)**2 / (mu_0*omega(frequency))\n", " app_phase = np.rad2deg(np.arctan(Zxy.imag / Zxy.real))\n", " return app_res, app_phase\n", " \n", " else:\n", " raise Exception(\"rtype must be 'impedance' or 'app_res', not {}\".format(rtype.lower()))" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 51 ms, sys: 4.05 ms, total: 55 ms\n", "Wall time: 52.5 ms\n" ] } ], "source": [ "%%time \n", "\n", "# Run the simulation over 25 frequencies from 1e-3 Hz to 100 Hz\n", "frequencies = np.logspace(-2, 3, 25)\n", "\n", "# for freq in frequencies:\n", "app_res, phase = simulateMT(mesh, sigma, frequencies)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For a half-space, the apparent resistivity should equal the true resistivity and the phase should be $45^\\circ$. How did we do??" ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(2, 1, figsize=(8, 3*2))\n", "\n", "# plot apparent resistivity\n", "ax[0].loglog(frequencies, app_res, lw=2)\n", "ax[0].set_ylim(1./sigma_halfspace*np.r_[0.1, 10])\n", "ax[0].set_ylabel(\"$ \\\\rho_a (\\Omega$-m)\", fontsize=14)\n", "\n", "# plot phase\n", "ax[1].semilogx(frequencies, phase, lw=2)\n", "ax[1].set_ylim(np.r_[0., 90.])\n", "ax[1].grid(which=\"both\", linewidth=0.4)\n", "\n", "ax[1].set_xlabel(\"frequency (Hz)\", fontsize=14)\n", "ax[1].set_ylabel(\"$\\phi (^\\circ)$\", fontsize=14)\n", "\n", "for a in ax:\n", " a.invert_xaxis() # highest frequencies see the near surface, lower frequencies see deeper\n", " a.set_xlabel(\"frequency (Hz)\", fontsize=14)\n", " a.grid(which=\"both\", linewidth=0.4)\n", "\n", "\n", "plt.tight_layout()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Discretization, the Gory Details. \n", "\n", "If you want to skip this section, we won't judge! " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To numerically solve Maxwell's equations, we need to first discretize them so they are represented on a mesh. We will take a finite difference approach for this example.\n", "\n", "Since we are solving for a 1D model, we will use a 1D mesh and leverage the Mesh class in SimPEG to build the operators (see http://docs.simpeg.xyz for docs). \n", "\n", "We show a very small mesh in the derivation so that it is meaningful to print out the matrices. When we go to solve, we will use a larger mesh." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "This mesh has 4 cells and 5 faces. Each cell is 1.0m wide\n" ] } ], "source": [ "cell_size = 1. # width of the cell in meters\n", "ncells = 4 # number of cells that make up our domain\n", "\n", "# define a Tensor Mesh\n", "dz = [(cell_size, ncells)]\n", "mesh = Mesh.TensorMesh([dz], x0='N')\n", "\n", "print(\n", " \"This mesh has {nC} cells and {nF} faces. \"\n", " \"Each cell is {h}m wide\".format(\n", " nC=mesh.nC, nF=mesh.nF, h=mesh.hx.min() # it is hx because SimPEG treats dimensions in the order (x, y, z), so if the mesh is 1D, we work with the first component\n", " )\n", ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "There are two places where we can discretize variables on a 1D mesh for the electromagnetic problem: cell centers and cell faces. " ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 21, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fig, ax = plt.subplots(1,1, figsize=(8,3))\n", "\n", "mesh.plotGrid(centers=True, faces=True, ax=ax)\n", "ax.invert_xaxis() # put the surface of the earth on the left. \n", "ax.set_xlabel('z (m)')\n", "ax.grid(which=\"both\", linewidth=0.4)\n", "plt.legend((\"centers\", \"faces\"))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To count, we will use \n", "$$\n", "i = 0, 1, 2, ..., N\n", "$$\n", "to denote cell centers, so faces are at $i \\pm 1/2$.\n", "\n", "To discretize our system of equations, we put the physical properties, $\\sigma$, $\\mu$, $\\epsilon$ at cell centers and stagger the electric and magnetic fields so that $E_x$ is on cell centers and $H_y$ is at cell faces.\n", "\n", "Our physical properties are described by the discrete vectors\n", "\n", "$$\n", "\\boldsymbol{\\sigma} = [\\sigma_0, \\sigma_1, \\sigma_2, ..., \\sigma_N]^\\top\n", "$$\n", "$$\n", "\\boldsymbol{\\mu} = [\\mu_0, \\mu_1, \\mu_2, ..., \\mu_N]^\\top\n", "$$\n", "$$\n", "\\boldsymbol{\\epsilon} = [\\epsilon_0, \\epsilon_1, \\epsilon_2, ..., \\epsilon_N]^\\top\n", "$$\n", "\n", "and \n", "$$\n", "\\boldsymbol{\\hat{\\sigma}} = \\boldsymbol{\\sigma} + \\imath \\omega \\boldsymbol{\\epsilon}\n", "$$\n", " \n", "is also defined at cell centers. \n", "\n", "Our fields are described by the discrete vectors\n", "$$\n", "\\mathbf{e_x} = [e_0, e_1, e_2, ..., e_N]^\\top\n", "$$\n", "\n", "$$\n", "\\mathbf{h_y} = [h_{-1/2}, h_{1/2}, h_{1+1/2}, h_{2+1/2}, ..., h_{N+1/2}]^\\top\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Discretizing Ampere's law\n", "\n", "Lets start by exmining Ampere's law (the second equation), \n", "\n", "$$-\\frac{\\partial H_y}{\\partial z} + \\hat{\\sigma} E_x = 0$$\n", "\n", "To approximate the derivative of $H_y$ (which is defined on faces) with respect to $z$, we use centered differences, so \n", "\n", "$$\n", "\\frac{\\partial H_y}{\\partial z} \\bigg\\rvert_i \\simeq \\frac{h_{i+1/2} - h_{i-1/2}}{\\Delta z_i}\n", "$$\n", "\n", "where $\\Delta z_i$ is the width of the cell, and the approximation of the derivative lands on the cell center. We repeat this operation for each cell in our mesh. You could do this in a for loop, but it is often benificial to work with the matrix form, so we will do that here. \n", "\n", "The differential operator matrix that takes the derivative of a variable defined on faces is the face divergence operator.\n", "\n", "$$\n", "\\frac{\\partial H_y}{\\partial z} \\simeq \\mathbf{Div} ~\\mathbf{h_y}\n", "$$" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[-1. 1. 0. 0. 0.]\n", " [ 0. -1. 1. 0. 0.]\n", " [ 0. 0. -1. 1. 0.]\n", " [ 0. 0. 0. -1. 1.]]\n" ] } ], "source": [ "Div = mesh.faceDiv\n", "print(Div.todense()) # operators are stored as sparse matrices in SimPEG" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since the physical properties $\\boldsymbol{\\hat{\\sigma}}$ is defined at cell centers which is in the same location as $E_x$, we can simply multiply them. In matrix form, we use a diagonal matrix, \n", "\n", "$$\\mathbf{M^{cc}_{\\boldsymbol{\\hat{\\sigma}}}} = \\mathbf{diag}(\\boldsymbol{\\hat{\\sigma}})$$ \n", "\n", "so the product is given by\n", "\n", "$$\n", "\\hat{\\sigma} E_x \\simeq \\mathbf{M^{cc}_{\\boldsymbol{\\hat{\\sigma}}}} ~\\mathbf{e_x}\n", "$$\n", "\n", "in the example that follows, we will assume that $\\sigma \\gg \\imath \\omega \\epsilon$, so $\\hat{\\sigma} \\simeq \\sigma$. In the more general implementation later on in the tutorial, we will use the full definition of $\\hat{\\sigma}$ " ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[0.1 0. 0. 0. ]\n", " [0. 0.1 0. 0. ]\n", " [0. 0. 0.1 0. ]\n", " [0. 0. 0. 0.1]]\n" ] } ], "source": [ "sigma = 1e-1 * np.ones(mesh.nC)\n", "Mcc_sigma = Utils.sdiag(sigma)\n", "print(Mcc_sigma.todense())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we have taken\n", "\n", "$$-\\frac{\\partial H_y}{\\partial z} + \\hat{\\sigma} E_x = 0$$\n", "\n", "and discretized to \n", "\n", "$$\n", "- \\mathbf{Div} ~ \\mathbf{h_y} + \\mathbf{M^{cc}_{\\boldsymbol{\\hat{\\sigma}}}} ~ \\mathbf{e_x} = \\mathbf{0}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Discretizing Faraday's Law\n", "\n", "Next, we examine Faraday's law:\n", "\n", "$$\n", "\\frac{\\partial E_x}{\\partial z} + \\imath \\omega \\mu H_y = 0\n", "$$\n", "\n", "Over one cell, the discrete approximation of the derivative of $E_x$ with respect to $z$ is\n", "\n", "$$\n", "\\frac{\\partial E_x}{\\partial z}\\bigg\\rvert_{i+1/2} = \\frac{e_{i+1} - e_{i}}{\\Delta z_{i+1/2}}\n", "$$\n", "\n", "where $\\Delta z_{1+1/2}$ is the distance (m) from the cell center $z_{i}$ to $z_{i+1}$. Notice here that we are going from cell centers to cell faces. So in this case we need to handle the boundary conditions, what do we do at \n", "the top and the bottom:\n", "$$\n", "\\frac{\\partial E_x}{\\partial z}\\bigg\\rvert_{-1/2}, \\quad\n", "\\frac{\\partial E_x}{\\partial z}\\bigg\\rvert_{nC+1/2} \n", "$$\n", "\n", "we somehow need to define \"ghost points\" $e_{-1}$ and $e_{N+1}$ so that we can solve \n", "\n", "$$\n", "\\frac{\\partial E_x}{\\partial z}\\bigg\\rvert_{-1/2} = \\frac{e_{0} - e_{-1}}{\\Delta z_{-1/2}}\n", "$$\n", "\n", "and\n", "\n", "$$\n", "\\frac{\\partial E_x}{\\partial z}\\bigg\\rvert_{N+1/2} = \\frac{e_{N+1} - e_{N}}{\\Delta z_{N+1/2}}\n", "$$\n", "\n", "#### Boundary Conditions\n", "\n", "Lets start with the bottom boundary - we know that MT fields and fluxes are diffusive and decay as they travel through conductive media, so if our boundary is sufficiently far away\n", "\n", "$$E_x (z=-\\infty) = 0$$\n", "\n", "Clearly we can't discretize to infinity... but we know approximately how quickly the fields decay, this is captured by the skin depth\n", "\n", "$$\n", "\\delta \\simeq \\frac{500}{\\sqrt{\\sigma f}}\n", "$$\n", "\n", "So as long as we define our mesh such that we are a few skin depths from the surface, then we can safely assume that the fields will have decayed to zero (dirichlet boundary condition). In our discrete world, this means that we want to enforce\n", "\n", "$$\n", "E_x \\big|_{nC+{1/2}} = 0\n", "$$\n", "\n", "The elements of $e$ are defined on cell centers and our boundary is a face, so we choose our ghost point $e_N$ such that when we average across the boundary, the average is 0, eg\n", "\n", "$$\n", "\\frac{1}{2} (e_{N-1} + e_{N}) = 0\n", "$$\n", "\n", "which means\n", "\n", "$$\n", "e_N = - e_{N-1}\n", "$$\n", "\n", "and our discrete approximation of the derivative at this boundary is\n", "\n", "$$\n", "\\frac{\\partial E_x}{\\partial z}\\bigg\\rvert_{N+1/2} = \\frac{e_{N+1} - e_{N}}{\\Delta z_{N+1/2}} = \\frac{-2 e_{N}}{\\Delta z_{N+1/2}}\n", "$$\n", "\n", "At the top boundary is where our incoming plane wave is, so we specify an electric field at the surface of \n", "\n", "$$E_x (z=0) = 1$$\n", "\n", "So this means we want to define our ghost point $e_{-1}$ such that\n", "\n", "$$\n", "\\frac{1}{2}(e_{-1} + e_0) = 1\n", "$$\n", "\n", "or \n", "\n", "$$\n", "(e_{-1}) = 2 - e_0\n", "$$\n", "\n", "and the derivative is \n", "\n", "$$\n", "\\frac{\\partial E_x}{\\partial z}\\bigg\\rvert_{-1/2} = \\frac{e_{0} - e_{-1}}{\\Delta z_{-1/2}} = \\underbrace{\\frac{2 e_{0}}{\\Delta z_{-1/2}}}_{\\text{due to dirichlet BC}} - \\underbrace{\\frac{2}{\\Delta z_{-1/2}}}_{\\text{due to non-homogeneous BC}}\n", "$$\n", "\n", "For conveienence, when we discretize, we first employ dirichlet boundary conditions on each boundary, and add the second term, due to a non-homogeneous boundary condition. \n", "\n", "The differential operator matrix that takes the derivative of a variable defined on faces is the cell gradient operator, so the discrete derivative of $E_x$ is given by\n", "$$\n", "\\frac{\\partial E_x}{\\partial z} \\simeq \\mathbf{Grad} ~ \\mathbf{e_x} + \\mathbf{B} ~ \\mathbf{e_x}^{BC}\n", "$$ \n", "\n", "where $\\mathbf{Grad}$ includes dirichlet boundary conditions, and $\\mathbf{B}$ is a $\\text{nC}\\times2$ matrix that accounts for the non-homogeneous boundary conditions" ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[ 2. 0. 0. 0.]\n", " [-1. 1. 0. 0.]\n", " [ 0. -1. 1. 0.]\n", " [ 0. 0. -1. 1.]\n", " [ 0. 0. 0. -2.]]\n" ] } ], "source": [ "# Grad matrix with dirichlet boundary conditions\n", "mesh.setCellGradBC([['dirichlet', 'dirichlet']]) # set the boundary conditions\n", "Grad = mesh.cellGrad\n", "print(Grad.todense())" ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[-2. 0.]\n", " [ 0. 0.]\n", " [ 0. 0.]\n", " [ 0. 0.]\n", " [ 0. 2.]]\n" ] } ], "source": [ "# deal with the boundary conditions\n", "ex_bc = np.r_[0., 1.] # bottom boundary, fields decay to zero, top is source\n", "B = mesh.cellGradBC # a matrix for boundary conditions\n", "print(B.todense())" ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[0. 0. 0. 0. 2.]\n" ] } ], "source": [ "# B * e_BC describes what we need to add to Grad e in order to addount for \n", "# the boundary conditions\n", "print(B*ex_bc)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The last piece we need to define is how to take the product $\\imath \\omega \\mu H_y$. $\\imath$ and $\\omega$ are scalars, so they are easy. The tricky part is $\\mu H_y$ since $\\mathbf{\\mu}$ is defined at cell centers (there are $\\text{nC}$ of them) and $\\mathbf{h}$ is at faces (there are $\\text{nC+1}$ of them). So to take this product, we will average the magnetic permeability to faces, and again stick it in a diagonal matrix \n", "\n", "$$\\mathbf{M^{f}_{\\mu}} = \\mathbf{diag}(\\mathbf{Av^{cc2f} \\mathbf{\\mu}})$$\n", "\n", "so the product is then\n", "\n", "$$\n", "\\imath\\omega\\mu H_y \\simeq \\imath\\omega\\mathbf{M^{f}_{\\mu}} ~\\mathbf{h_y}\n", "$$" ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1. 0. 0. 0. ]\n", " [0.5 0.5 0. 0. ]\n", " [0. 0.5 0.5 0. ]\n", " [0. 0. 0.5 0.5]\n", " [0. 0. 0. 1. ]]\n" ] } ], "source": [ "# Averaging matrix\n", "AvCC2F = mesh.aveCC2F\n", "print(AvCC2F.todense())" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[[1.25663706e-06 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", " 0.00000000e+00]\n", " [0.00000000e+00 1.25663706e-06 0.00000000e+00 0.00000000e+00\n", " 0.00000000e+00]\n", " [0.00000000e+00 0.00000000e+00 1.25663706e-06 0.00000000e+00\n", " 0.00000000e+00]\n", " [0.00000000e+00 0.00000000e+00 0.00000000e+00 1.25663706e-06\n", " 0.00000000e+00]\n", " [0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00\n", " 1.25663706e-06]]\n" ] } ], "source": [ "mu = mu_0*np.ones(mesh.nC)\n", "Mfmu = Utils.sdiag(AvCC2F * mu)\n", "print(Mfmu.todense())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "So we have taken Faraday's law\n", "\n", "$$\n", "\\frac{\\partial E_x}{\\partial z} + \\imath \\omega \\mu H_y = 0\n", "$$\n", "\n", "and arrived at the discrete system\n", "\n", "$$\n", "\\mathbf{Grad} ~ \\mathbf{e_x} + \\mathbf{B} ~ \\mathbf{e_x}^{BC} + \\imath\\omega \\mathbf{M^f_\\mu} \\mathbf{h_y} = 0\n", "$$\n", "\n", "since the boundary conditions are known, we can move them to the right hand side\n", "\n", "$$\n", "\\mathbf{Grad} ~ \\mathbf{e_x} + \\imath\\omega \\mathbf{M^f_\\mu} \\mathbf{h_y} = - \\mathbf{B} ~ \\mathbf{e_x}^{BC}\n", "$$" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Two equations, Two unknowns\n", "\n", "Our discrete Maxwell system is \n", "\n", "$$\n", "\\mathbf{Grad} ~ \\mathbf{e_x} + \\imath\\omega \\mathbf{M^f_\\mu} \\mathbf{h_y} = - \\mathbf{B} ~ \\mathbf{e_x}^{BC}\n", "$$\n", "\n", "$$\n", "- \\mathbf{Div} ~ \\mathbf{h_y} + \\mathbf{M^{cc}_{\\boldsymbol{\\hat{\\sigma}}}} ~ \\mathbf{e_x} = \\mathbf{0}\n", "$$\n", "\n", "For convienence, lets re-arrage... \n", "$$\n", "\\mathbf{Grad} ~ \\mathbf{e_x} + \\imath\\omega \\mathbf{M^f_\\mu} \\mathbf{h_y} = - \\mathbf{B} ~ \\mathbf{e_x}^{BC}\n", "$$\n", "\n", "$$\n", "\\mathbf{M^{cc}_{\\boldsymbol{\\hat{\\sigma}}}} ~ \\mathbf{e_x} - \\mathbf{Div} ~ \\mathbf{h_y} = \\mathbf{0}\n", "$$\n", "\n", "and assemble into a single matrix system\n", "\n", "$$\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " \\mathbf{Grad} & \\imath \\omega \\mathbf{M}^{f2cc}_{\\mu} \\\\[0.3em]\n", " \\mathbf{M}^{cc}_{\\hat{\\sigma}} & \\mathbf{Div} \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{A}}\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " \\mathbf{e}_x \\\\[0.3em]\n", " \\mathbf{h}_y \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{u}}\n", "=\n", "\\underbrace{\n", " \\begin{bmatrix}\n", " - \\mathbf{B}\\mathbf{E}_x^{BC} \\\\[0.3em]\n", " \\boldsymbol{0} \\\\[0.3em]\n", " \\end{bmatrix}\n", "}_{\\mathbf{rhs}}\n", "$$\n", "\n", "with \n", "\n", "- $\\mathbf{e}_x$: Discrete $E_x$ $[\\text{nC} \\times 1]$\n", "\n", "- $\\mathbf{e}_y$: Dicrete $H_x$ $[(\\text{nC}+1) \\times 1]$\n", "\n", "- $ \\mathbf{Grad}$: Discrete gradient operator with dirichlet boundary conditions $[\\text{nC} \\times (\\text{nC}+1)]$\n", "\n", "- $ \\mathbf{Div}$: Discrete divergence operator $[(\\text{nC}+1) \\times \\text{nC}]$\n", "\n", "- $\\mathbf{M}^{f}_{\\boldsymbol{\\mu}} = \\mathbf{diag}(\\mathbf{Av^{cc2f}} \\boldsymbol{\\mu})$ $[(\\text{nC}+1) \\times (\\text{nC}+1)]$\n", "\n", "- $\\mathbf{M}^{cc}_{\\boldsymbol{\\hat{\\sigma}}} = \\mathbf{diag}(\\boldsymbol{\\hat{\\sigma}})$ $[\\text{nC} \\times \\text{nC}]$\n", "\n", "- $\\mathbf{B} \\mathbf{e_x}^{BC}$ handles the boundary conditions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Now we have all of the pieces\n", "\n", "Here, lets create a larger mesh, and we can go ahead and asseble the system of equations to solve" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "sigma_halfspace = 1e-2\n", "freq = 1" ] }, { "cell_type": "code", "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The mesh extends 1.2e+05m, is that far enough? (should be at least 5.0e+03m away)\n" ] } ], "source": [ "cs = 39. # core cell size\n", "npad = 25 # number of padding cells\n", "ncz = 100 # number of core cells\n", "\n", "# define a tensor mesh\n", "hz = [(cs, npad, -1.3), (cs, ncz)] \n", "mesh = Mesh.TensorMesh([hz], x0='N') # put the origin at the surface \n", "\n", "print(\n", " \"The mesh extends {:1.1e}m, is that far enough? (should be at least {:1.1e}m away)\".format(\n", " mesh.hx.sum(),\n", " skin_depth(sigma_halfspace, freq)\n", " )\n", ")" ] }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ "# physical properties\n", "sigma = np.ones(mesh.nC)*sigma_halfspace # conductivity values for all cells\n", "mu = np.ones(mesh.nC)*mu_0 # magnetic permeability values for all cells\n", "epsilon = np.ones(mesh.nC)*epsilon_0 # dielectric constant values for all cells" ] }, { "cell_type": "code", "execution_count": 32, "metadata": {}, "outputs": [], "source": [ "# Grad \n", "mesh.setCellGradBC([['dirichlet', 'dirichlet']]) # Setup boundary conditions\n", "Grad = mesh.cellGrad # Gradient matrix\n", "\n", "# MfMu\n", "Mmu = Utils.sdiag(mesh.aveCC2F * mu) \n", "\n", "# Mccsigma\n", "Msighat = Utils.sdiag(sigmahat) \n", "\n", "# Div\n", "Div = mesh.faceDiv # Divergence matrix\n", "\n", "# Right Hand Side\n", "B = mesh.cellGradBC # a matrix for boundary conditions\n", "Exbc = np.r_[0., 1.] # boundary values for Ex" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "# Assemble the matrix\n", "\n", "# A-matrix\n", "A = sp.vstack([\n", " sp.hstack([Grad, 1j*omega*Mmu]), # Top row of A matrix\n", " sp.hstack((Msighat, Div)) # Bottom row of A matrix\n", "])\n", "\n", "# Right-hand side\n", "rhs = np.r_[\n", " -B*Exbc, \n", " np.zeros(mesh.nC)\n", "] " ] }, { "cell_type": "code", "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 1.96 ms, sys: 1.52 ms, total: 3.48 ms\n", "Wall time: 2.41 ms\n" ] } ], "source": [ "%%time\n", "Ainv = Solver(A) # Factorize A matrix\n", "sol = Ainv*rhs # Solve A^-1 rhs = sol\n", "Ex = sol[:mesh.nC] # Extract Ex from solution vector u\n", "Hy = sol[mesh.nC:mesh.nC+mesh.nN] # Extract Hy from solution vector u" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Impedance: 6.2e-01 + 6.4e-01i\n", "or in terms of Amplidude: 8.9e-01 and phase: 45.9 degrees\n" ] } ], "source": [ "Zxy = - 1./Hy[-1] # Impedance at the surface\n", "\n", "print(\"Impedance: {:1.1e} + {:1.1e}i\".format(Zxy.real, Zxy.imag))\n", "print(\n", " \"or in terms of Amplidude: {:1.1e} and phase: {:1.1f} degrees\".format(\n", " np.absolute(Zxy), np.rad2deg(np.arctan(Zxy.imag / Zxy.real))\n", " )\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.6" } }, "nbformat": 4, "nbformat_minor": 2 }