{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "
\n", "
\n", "
\n", "
Computational Seismology
\n", "
Discontinuous Galerkin Method - 1D Elastic Wave Equation, Heterogeneous case
\n", "
\n", "
\n", "
" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

\n", " \n", "\n", "\n", "

\n", "\n", "\n", "---\n", "\n", "This notebook is part of the supplementary material \n", "to [Computational Seismology: A Practical Introduction](https://global.oup.com/academic/product/computational-seismology-9780198717416?cc=de&lang=en&#), \n", "Oxford University Press, 2016.\n", "\n", "\n", "##### Authors:\n", "* Stephanie Wollherr ([@swollherr](https://github.com/swollherr))\n", "* David Vargas ([@dvargas](https://github.com/davofis))\n", "* Heiner Igel ([@heinerigel](https://github.com/heinerigel))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Basic Equations\n", "\n", "The source-free elastic wave equation in 1D reads \n", "\n", "\\begin{align}\n", "\\partial_t \\sigma - \\mu \\partial_x v & = 0 \\\\ \n", "\\partial_t v - \\frac{1}{\\rho} \\partial_x \\sigma & = 0\n", "\\end{align}\n", "\n", "with $\\rho$ the density and $\\mu$ the shear modulus. This equation in matrix-vector notation follows\n", "\n", "\\begin{equation}\n", "\\partial_t \\mathbf{Q} + \\mathbf{A} \\partial_x \\mathbf{Q} = 0\n", "\\end{equation}\n", "\n", "where $\\mathbf{Q} = (\\sigma, v)$ is the vector of unknowns and the matrix $\\mathbf{A}$ contains the parameters $\\rho$ and $\\mu$. We seek to solve the linear advection equation as a hyperbolic equation $\\partial_t u + \\mu \\ \\partial_x u=0$. A series of steps need to be done:\n", "\n", "1) The weak form of the equation is derived by multiplying both sides by an arbitrary test function. \n", "\n", "2) Apply the stress Free Boundary Condition after integration by parts\n", "\n", "3) We approximate the unknown field $\\mathbf{Q}(x,t)$ by a sum over space-dependent basis functions $\\ell_i$ weighted by time-dependent coefficients $\\mathbf{Q}(x_i,t)$, as we did in the spectral elements method. As interpolating functions we choose the Lagrange polynomials and use $\\xi$ as the space variable representing the elemental domain:\n", "\n", "\\begin{equation}\n", "\\mathbf{Q}(\\xi,t) \\ = \\ \\sum_{i=1}^{N_p} \\mathbf{Q}(\\xi_i,t) \\ell_i(\\xi) \\qquad with \\qquad \\ell_i^{(N)} (\\xi) \\ := \\ \\prod_{j = 1, \\ j \\neq i}^{N+1} \\frac{\\xi - \\xi_j}{\\xi_i-\\xi_j}, \\quad i,j = 1, 2, \\dotsc , N + 1 \n", "\\end{equation}\n", "\n", "4) The continuous weak form is written as a system of linear equations by considering the approximated displacement field. Finally, the semi-discrete scheme can be written in matrix-vector form as\n", "\n", "\\begin{equation}\n", "\\mathbf{M}\\partial_t \\mathbf{Q} = \\mathbf{A}\\mathbf{K}\\mathbf{Q} - \\mathbf{Flux}\n", "\\end{equation}\n", "\n", "5) Time extrapolation is done after applying a standard 1st order finite-difference approximation to the time derivative, we call it the Euler scheme.\n", "\n", "\\begin{equation}\n", "\\mathbf{Q}^{t+1} \\approx \\mathbf{Q}^{t} + dt\\mathbf{M}^{-1}(\\mathbf{A}\\mathbf{K}\\mathbf{Q} - \\mathbf{Flux})\n", "\\end{equation}\n", "\n", "This notebook implements both Euler and Runge-Kutta schemes for solving the free source version of the elastic wave equation in a homogeneous media. To keep the problem simple, we use as spatial initial condition a Gauss function with half-width $\\sigma$\n", "\n", "\\begin{equation}\n", "Q(x,t=0) = e^{-1/\\sigma^2 (x - x_{o})^2}\n", "\\end{equation}" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "code_folding": [ 0 ] }, "outputs": [], "source": [ "# Import all necessary libraries, this is a configuration step for the exercise.\n", "# Please run it before the simulation code!\n", "import numpy as np\n", "import matplotlib\n", "# Show Plot in The Notebook\n", "matplotlib.use(\"nbagg\")\n", "import matplotlib.pyplot as plt\n", "\n", "from gll import gll\n", "from lagrange1st import lagrange1st\n", "from flux_hetero import flux" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1. Initialization of setup" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# Initialization of setup\n", "# --------------------------------------------------------------------------\n", "tmax = 2.5 # Length of seismogram [s]\n", "xmax = 10000 # Length of domain [m]\n", "vs0 = 2500 # Advection velocity\n", "rho0 = 2500 # Density [kg/m^3]\n", "mu0 = rho0*vs0**2 # shear modulus\n", "N = 2 # Order of Lagrange polynomials\n", "ne = 200 # Number of elements\n", "sig = 100 # width of Gaussian initial condition\n", "x0 = 4000 # x locartion of Gauss\n", "eps = 0.2 # Courant criterion\n", "iplot = 20 # Plotting frequency\n", "imethod = 'RK' # 'Euler', 'RK'\n", "\n", "nx = ne*N + 1\n", "dx = xmax/(nx-1) # space increment\n", "#--------------------------------------------------------------------\n", "\n", "# Initialization of GLL points integration weights\n", "[xi,w] = gll(N) # xi, N+1 coordinates [-1 1] of GLL points\n", " # w Integration weights at GLL locations\n", "# Space domain\n", "le = xmax/ne # Length of elements, here equidistent\n", "ng = ne*N + 1\n", "\n", "# Vector with GLL points\n", "k = 0\n", "xg = np.zeros((N+1)*ne)\n", "for i in range(0, ne):\n", " for j in range(0, N+1):\n", " k += 1\n", " xg[k-1] = i*le + .5*(xi[j] + 1)*le\n", "\n", "x = np.reshape(xg, (N+1, ne), order='F').T\n", "\n", "# Calculation of time step acoording to Courant criterion\n", "dxmin = np.min(np.diff(xg[1:N+1]))\n", "dt = eps*dxmin/vs0 # Global time step\n", "nt = int(np.floor(tmax/dt))\n", "\n", "# Mapping - Jacobian\n", "J = le/2 # Jacobian\n", "Ji = 1/J # Inverse Jacobian\n", "\n", "# 1st derivative of Lagrange polynomials\n", "l1d = lagrange1st(N)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2. Elemental Mass and Stiffness matrices\n", "\n", "The mass and the stiffness matrix are calculated prior time extrapolation, so they are pre-calculated and stored at the beginning of the code. \n", "\n", "The integrals defined in the mass and stiffness matrices are computed using a numerical quadrature, in this cases the GLL quadrature that uses the GLL points and their corresponding weights to approximate the integrals. Hence,\n", "\n", "\\begin{equation}\n", "M_{ij}^k=\\int_{-1}^1 \\ell_i^k(\\xi) \\ell_j^k(\\xi) \\ J \\ d\\xi = \\sum_{m=1}^{N_p} w_m \\ \\ell_i^k (x_m) \\ell_j^k(x_m)\\ J =\\sum_{m=1}^{N_p} w_m \\delta_{im}\\ \\delta_{jm} \\ J= \\begin{cases} w_i \\ J \\ \\ \\text{ if } i=j \\\\ 0 \\ \\ \\ \\ \\ \\ \\ \\text{ if } i \\neq j\\end{cases}\n", "\\end{equation}\n", "\n", "that is a **diagonal mass matrix!**. Subsequently, the stiffness matrices is given as \n", " \n", "\\begin{equation} \n", "K_{i,j}= \\int_{-1}^1 \\ell_i^k(\\xi) \\cdot \\partial _x \\ell_j^k(\\xi) \\ d\\xi= \\sum_{m=1}^{N_p} w_m \\ \\ell_i^k(x_m)\\cdot \\partial_x \\ell_j^k(x_m)= \\sum_{m=1}^{N_p} w_m \\delta_{im}\\cdot \\partial_x\\ell_j^k(x_m)= w_i \\cdot \\partial_x \\ell_j^k(x_i) \n", "\\end{equation}\n", "\n", "The Lagrange polynomials and their properties have been already used, they determine the integration weights $w_i$ that are returned by the python method \"gll\". Additionally, the fist derivatives of such basis, $\\partial_x \\ell_j^k(x_i)$, are needed, the python method \"Lagrange1st\" returns them." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Initialization of system matrices\n", "# -----------------------------------------------------------------\n", "# Elemental Mass matrix\n", "M = np.zeros((N+1, N+1))\n", "for i in range(0, N+1):\n", " M[i, i] = w[i] * J\n", "\n", "# Inverse matrix of M (M is diagonal!)\n", "Minv = np.identity(N+1)\n", "for i in range(0, N+1):\n", " Minv[i,i] = 1. / M[i,i]\n", "\n", "# Elemental Stiffness Matrix\n", "K = np.zeros((N+1, N+1))\n", "for i in range(0, N+1):\n", " for j in range(0, N+1):\n", " K[i,j] = w[j] * l1d[i,j] # NxN matrix for every element\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3. Flux Matrices\n", "\n", "The main difference in the heterogeneous case with respect the homogeneous one is found in the definition of fluxes. As in the case of finite volumes when we solve the 1D elastic wave equation, we allow the coefficients of matrix A to vary inside the element.\n", "\n", "\\begin{equation} \n", "\\mathbf{A}=\n", " \\begin{pmatrix}\n", " 0 & -\\mu_i \\\\\n", " -1/\\rho_i & 0 \n", " \\end{pmatrix}\n", "\\end{equation}\n", "\n", "Now we need to diagonalize $\\mathbf{A}$. Introducing the seismic impedance $Z_i = \\rho_i c_i$, we have\n", " \n", "\\begin{equation}\n", "\\mathbf{A} = \\mathbf{R}^{-1}\\mathbf{\\Lambda}\\mathbf{R}\n", "\\qquad\\text{,}\\qquad\n", "\\mathbf{\\Lambda}=\n", " \\begin{pmatrix}\n", " -c_i & 0 \\\\\n", " 0 & c_i \n", " \\end{pmatrix}\n", "\\qquad\\text{,}\\qquad\n", "\\mathbf{R} = \n", " \\begin{pmatrix}\n", " Z_i & -Z_i \\\\\n", " 1 & 1 \n", " \\end{pmatrix}\n", "\\qquad\\text{and}\\qquad\n", "\\mathbf{R}^{-1} = \\frac{1}{2Z_i}\n", " \\begin{pmatrix}\n", " 1 & Z_i \\\\\n", " -1 & Z_i \n", " \\end{pmatrix}\n", "\\end{equation}\n", "\n", "We decompose the solution into right propagating $\\mathbf{\\Lambda}^{+}$ and left propagating eigenvalues $\\mathbf{\\Lambda}^{-}$ where\n", "\n", "\\begin{equation}\n", "\\mathbf{\\Lambda}^{+}=\n", " \\begin{pmatrix}\n", " -c_i & 0 \\\\\n", " 0 & 0 \n", " \\end{pmatrix}\n", "\\qquad\\text{,}\\qquad\n", "\\mathbf{\\Lambda}^{-}=\n", " \\begin{pmatrix}\n", " 0 & 0 \\\\\n", " 0 & c_i \n", " \\end{pmatrix}\n", "\\qquad\\text{and}\\qquad\n", "\\mathbf{A}^{\\pm} = \\mathbf{R}^{-1}\\mathbf{\\Lambda}^{\\pm}\\mathbf{R}\n", "\\end{equation}\n", "\n", "This strategy allows us to formulate the Flux term in the discontinuous Galerkin method. The following cell initializes all flux related matrices" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "lines_to_end_of_cell_marker": 2 }, "outputs": [], "source": [ "# Inialize Flux relates matrices\n", "# ---------------------------------------------------------------\n", "\n", "# initialize heterogeneous A\n", "Ap = np.zeros((ne,2,2))\n", "Am = np.zeros((ne,2,2))\n", "Z = np.zeros(ne)\n", "rho = np.zeros(ne)\n", "mu = np.zeros(ne)\n", "\n", "# initialize c, rho, mu, and Z\n", "rho = rho + rho0\n", "rho[int(ne/2):ne] = .25 * rho[int(ne/2):ne] # Introduce discontinuity \n", "mu = mu + mu0\n", "c = np.sqrt(mu/rho)\n", "Z = rho * c\n", "\n", "# Initialize flux matrices\n", "for i in range(1,ne-1):\n", " # Left side positive direction\n", " R = np.array([[Z[i], -Z[i]], [1, 1]])\n", " Lp = np.array([[0, 0], [0, c[i]]])\n", " Ap[i,:,:] = R @ Lp @ np.linalg.inv(R)\n", "\n", " # Right side negative direction\n", " R = np.array([[Z[i], -Z[i]], [1, 1]])\n", " Lm = np.array([[-c[i], 0 ], [0, 0]])\n", " Am[i,:,:] = R @ Lm @ np.linalg.inv(R)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 4. Discontinuous Galerkin Solution\n", "\n", "The principal characteristic of the discontinuous Galerkin Method is the communication between the element neighbors using a flux term, in general it is given \n", "\n", "\\begin{equation}\n", "\\mathbf{Flux} = \\int_{\\partial D_k} \\mathbf{A}\\mathbf{Q}\\ell_j(\\xi)\\mathbf{n}d\\xi\n", "\\end{equation}\n", "\n", "this term leads to four flux contributions for left and right sides of the elements\n", "\n", "\\begin{equation}\n", "\\mathbf{Flux} = -\\mathbf{A}_{k}^{-}\\mathbf{Q}_{l}^{k}\\mathbf{F}^{l} + \\mathbf{A}_{k}^{+}\\mathbf{Q}_{r}^{k}\\mathbf{F}^{r} - \\mathbf{A}_{k}^{+}\\mathbf{Q}_{r}^{k-1}\\mathbf{F}^{l} + \\mathbf{A}_{k}^{-}\\mathbf{Q}_{l}^{k+1}\\mathbf{F}^{r}\n", "\\end{equation}\n", "\n", "\n", "\n", "Last but not least, we have to solve our semi-discrete scheme that we derived above using an appropriate time extrapolation, in the code below we implemented two different time extrapolation schemes:\n", "\n", "1) **Euler scheme** \n", "\n", "\\begin{equation}\n", "\\mathbf{Q}^{t+1} \\approx \\mathbf{Q}^{t} + dt\\mathbf{M}^{-1}(\\mathbf{A}\\mathbf{K}\\mathbf{Q} - \\mathbf{Flux})\n", "\\end{equation}\n", "\n", "2) Second-order **Runge-Kutta method** (also called predictor-corrector scheme) \n", "\n", "\\begin{eqnarray*} \n", "k_1 &=& f(t_i, y_i) \\\\\n", "k_2 &=& f(t_i + dt, y_i + dt k_1) \\\\\n", "& & \\\\\n", "y_{i+1} &=& y_i + \\frac{dt}{2} (k_1 + k_2)\n", "\\end{eqnarray*}\n", "\n", "with $f$ that corresponds with $\\mathbf{M}^{-1}(\\mathbf{A}\\mathbf{K}\\mathbf{Q} - \\mathbf{Flux})$" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "code_folding": [ 72 ] }, "outputs": [ { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "window.mpl = {};\n", "\n", "\n", "mpl.get_websocket_type = function() {\n", " if (typeof(WebSocket) !== 'undefined') {\n", " return WebSocket;\n", " } else if (typeof(MozWebSocket) !== 'undefined') {\n", " return MozWebSocket;\n", " } else {\n", " alert('Your browser does not have WebSocket support. ' +\n", " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", " 'Firefox 4 and 5 are also supported but you ' +\n", " 'have to enable WebSockets in about:config.');\n", " };\n", "}\n", "\n", "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", " this.id = figure_id;\n", "\n", " this.ws = websocket;\n", "\n", " this.supports_binary = (this.ws.binaryType != undefined);\n", "\n", " if (!this.supports_binary) {\n", " var warnings = document.getElementById(\"mpl-warnings\");\n", " if (warnings) {\n", " warnings.style.display = 'block';\n", " warnings.textContent = (\n", " \"This browser does not support binary websocket messages. \" +\n", " \"Performance may be slow.\");\n", " }\n", " }\n", "\n", " this.imageObj = new Image();\n", "\n", " this.context = undefined;\n", " this.message = undefined;\n", " this.canvas = undefined;\n", " this.rubberband_canvas = undefined;\n", " this.rubberband_context = undefined;\n", " this.format_dropdown = undefined;\n", "\n", " this.image_mode = 'full';\n", "\n", " this.root = $(' ');\n", " this._root_extra_style(this.root)\n", " this.root.attr('style', 'display: inline-block');\n", "\n", "$(parent_element).append(this.root);\n", "\n", " this._init_header(this);\n", " this._init_canvas(this);\n", " this._init_toolbar(this);\n", "\n", " var fig = this;\n", "\n", " this.waiting = false;\n", "\n", " this.ws.onopen = function () {\n", " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", " fig.send_message(\"send_image_mode\", {});\n", " if (mpl.ratio != 1) {\n", " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", " }\n", " fig.send_message(\"refresh\", {});\n", " }\n", "\n", " this.imageObj.onload = function() {\n", " if (fig.image_mode == 'full') {\n", " // Full images could contain transparency (where diff images\n", " // almost always do), so we need to clear the canvas so that\n", " // there is no ghosting.\n", " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", " }\n", " fig.context.drawImage(fig.imageObj, 0, 0);\n", " };\n", "\n", " this.imageObj.onunload = function() {\n", " fig.ws.close();\n", " }\n", "\n", " this.ws.onmessage = this._make_on_message_function(this);\n", "\n", " this.ondownload = ondownload;\n", "}\n", "\n", "mpl.figure.prototype._init_header = function() {\n", " var titlebar = $(\n", " ' ');\n", " var titletext =$(\n", " '
');\n", " titlebar.append(titletext)\n", " this.root.append(titlebar);\n", " this.header = titletext;\n", "}\n", "\n", "\n", "\n", "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", "\n", "}\n", "\n", "\n", "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", "\n", "}\n", "\n", "mpl.figure.prototype._init_canvas = function() {\n", " var fig = this;\n", "\n", " var canvas_div = $(' ');\n", "\n", " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", "\n", " function canvas_keyboard_event(event) {\n", " return fig.key_event(event, event['data']);\n", " }\n", "\n", " canvas_div.keydown('key_press', canvas_keyboard_event);\n", " canvas_div.keyup('key_release', canvas_keyboard_event);\n", " this.canvas_div = canvas_div\n", " this._canvas_extra_style(canvas_div)\n", " this.root.append(canvas_div);\n", "\n", " var canvas =$('');\n", " canvas.addClass('mpl-canvas');\n", " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", "\n", " this.canvas = canvas;\n", " this.context = canvas.getContext(\"2d\");\n", "\n", " var backingStore = this.context.backingStorePixelRatio ||\n", "\tthis.context.webkitBackingStorePixelRatio ||\n", "\tthis.context.mozBackingStorePixelRatio ||\n", "\tthis.context.msBackingStorePixelRatio ||\n", "\tthis.context.oBackingStorePixelRatio ||\n", "\tthis.context.backingStorePixelRatio || 1;\n", "\n", " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", "\n", " var rubberband = $('');\n", " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", "\n", " var pass_mouse_events = true;\n", "\n", " canvas_div.resizable({\n", " start: function(event, ui) {\n", " pass_mouse_events = false;\n", " },\n", " resize: function(event, ui) {\n", " fig.request_resize(ui.size.width, ui.size.height);\n", " },\n", " stop: function(event, ui) {\n", " pass_mouse_events = true;\n", " fig.request_resize(ui.size.width, ui.size.height);\n", " },\n", " });\n", "\n", " function mouse_event_fn(event) {\n", " if (pass_mouse_events)\n", " return fig.mouse_event(event, event['data']);\n", " }\n", "\n", " rubberband.mousedown('button_press', mouse_event_fn);\n", " rubberband.mouseup('button_release', mouse_event_fn);\n", " // Throttle sequential mouse events to 1 every 20ms.\n", " rubberband.mousemove('motion_notify', mouse_event_fn);\n", "\n", " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", "\n", " canvas_div.on(\"wheel\", function (event) {\n", " event = event.originalEvent;\n", " event['data'] = 'scroll'\n", " if (event.deltaY < 0) {\n", " event.step = 1;\n", " } else {\n", " event.step = -1;\n", " }\n", " mouse_event_fn(event);\n", " });\n", "\n", " canvas_div.append(canvas);\n", " canvas_div.append(rubberband);\n", "\n", " this.rubberband = rubberband;\n", " this.rubberband_canvas = rubberband;\n", " this.rubberband_context = rubberband.getContext(\"2d\");\n", " this.rubberband_context.strokeStyle = \"#000000\";\n", "\n", " this._resize_canvas = function(width, height) {\n", " // Keep the size of the canvas, canvas container, and rubber band\n", " // canvas in synch.\n", " canvas_div.css('width', width)\n", " canvas_div.css('height', height)\n", "\n", " canvas.attr('width', width * mpl.ratio);\n", " canvas.attr('height', height * mpl.ratio);\n", " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", "\n", " rubberband.attr('width', width);\n", " rubberband.attr('height', height);\n", " }\n", "\n", " // Set the figure to an initial 600x600px, this will subsequently be updated\n", " // upon first draw.\n", " this._resize_canvas(600, 600);\n", "\n", " // Disable right mouse context menu.\n", "$(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", " return false;\n", " });\n", "\n", " function set_focus () {\n", " canvas.focus();\n", " canvas_div.focus();\n", " }\n", "\n", " window.setTimeout(set_focus, 100);\n", "}\n", "\n", "mpl.figure.prototype._init_toolbar = function() {\n", " var fig = this;\n", "\n", " var nav_element = $(' ');\n", " nav_element.attr('style', 'width: 100%');\n", " this.root.append(nav_element);\n", "\n", " // Define a callback function for later on.\n", " function toolbar_event(event) {\n", " return fig.toolbar_button_onclick(event['data']);\n", " }\n", " function toolbar_mouse_event(event) {\n", " return fig.toolbar_button_onmouseover(event['data']);\n", " }\n", "\n", " for(var toolbar_ind in mpl.toolbar_items) {\n", " var name = mpl.toolbar_items[toolbar_ind];\n", " var tooltip = mpl.toolbar_items[toolbar_ind];\n", " var image = mpl.toolbar_items[toolbar_ind];\n", " var method_name = mpl.toolbar_items[toolbar_ind];\n", "\n", " if (!name) {\n", " // put a spacer in here.\n", " continue;\n", " }\n", " var button =$('