{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": []
   },
   "outputs": [],
   "source": [
    "%reset -f\n",
    "import numpy as np                    # matrices & etc\n",
    "from matplotlib import pyplot as plt  # general plotting \n",
    "from matplotlib import patches        # plotting shapes\n",
    "from datetime import date             # date and time arithmetic\n",
    "from math import exp, pi, sin, cos, tan, sqrt, floor, log10, nan   # math functions\n",
    "import scipy.linalg as la             # linear algebra functions\n",
    "import os                             # operating system\n",
    "import matplotlib.cm as cm\n",
    "from matplotlib.colors import ListedColormap\n",
    "import scipy.signal as sg\n",
    "import scipy.stats as st\n",
    "import scipy.sparse.linalg as las\n",
    "import matplotlib\n",
    "import scipy.interpolate as interp    # linear algebra functions\n",
    "\n",
    "# function to make a numpy N-by-1 column vector\n",
    "# c=eda_cvec(v1, v2, ...) from a list of several\n",
    "# array-like entities v1, v2, including a number\n",
    "# a list of numbers, a tuple of numbers, an N-by-0 np array\n",
    "# and a N-by-1 np array. The function\n",
    "# also insures that, if v is an np array, that\n",
    "# c is a copy, as contrasted to a view, of it\n",
    "# It promotes integers to floats, and integers\n",
    "# and floats to complex, by context.\n",
    "# This version concatenates many argments,\n",
    "# whereas c=eda_cvec1(v1) takes just one argiment.\n",
    "# I recommend always using eda_cvec(v1, v2, ...)\n",
    "def gda_cvec(*argv):\n",
    "    t = int;\n",
    "    Nt = 0;\n",
    "    for a in argv:\n",
    "        v = gda_cvec1(a);\n",
    "        N,M = np.shape(v);\n",
    "        Nt = Nt + N;\n",
    "        if( N==0 ):\n",
    "            continue; # skip vector of zero length\n",
    "        if (t==int) and isinstance(v[0,0],float):\n",
    "            t=float;\n",
    "        elif isinstance(v[0,0],complex):\n",
    "            t=complex;\n",
    "    w = np.zeros((Nt,1),dtype=t);\n",
    "    Nt = 0;\n",
    "    for a in argv:\n",
    "        v = gda_cvec1(a);\n",
    "        N,M = np.shape(v);\n",
    "        w[Nt:Nt+N,0:1] = v;  # patch 20230418 was #w[Nt:Nt+N,0] = v[0:N,0];\n",
    "        Nt = Nt + N;\n",
    "    return w;\n",
    "\n",
    "# function to make a numpy N-by-1 column vector\n",
    "# c=gda_cvec1(v) from entity v that is array-like,\n",
    "# including a number, a list of numbers, a tuple\n",
    "# of numbers, an N-by-0 np array and a N-by1 np array.\n",
    "# It promotes integers to floats, and integers\n",
    "# and floats to complex, by context. The function\n",
    "# also insures that, if v is an np array, that\n",
    "# c is a copy, as contrasted to a view, of it.\n",
    "# This version takes just one input argment.\n",
    "# whereas c=gda_cvec(v1,v2,...) concatenates\n",
    "# many argiments.\n",
    "def gda_cvec1(v):\n",
    "    if isinstance(v, int) or isinstance(v, np.int32):\n",
    "        w = np.zeros((1,1),dtype=int);\n",
    "        w[0,0] = v;\n",
    "        return w;\n",
    "    elif isinstance(v, float):\n",
    "        w = np.zeros((1,1),dtype=float);\n",
    "        w[0,0] = v;\n",
    "        return w;\n",
    "    elif isinstance(v, complex):\n",
    "        w = np.zeros((1,1),dtype=complex);\n",
    "        w[0,0] = v;\n",
    "        return w;\n",
    "    elif isinstance(v, np.ndarray):\n",
    "        s = np.shape(v);\n",
    "        if len(s) == 1:\n",
    "            return np.copy(np.reshape(v,(s[0],1)));\n",
    "        else:\n",
    "            [r,c]=s;\n",
    "            if( c==1 ):\n",
    "                return(np.copy(v));\n",
    "            elif(r==1):\n",
    "                return(np.copy(v.T));\n",
    "            else:\n",
    "                raise TypeError(\"gda_cvec: %d by %d ndarray not allowed\" % (r, c));\n",
    "    elif isinstance(v, list):\n",
    "        r = len(v);\n",
    "        t = int;\n",
    "        for vi in v:\n",
    "            if isinstance(vi,int) or isinstance(vi, np.int32): #patch v->vi 20230418\n",
    "                pass;\n",
    "            elif isinstance(vi,float):\n",
    "                t=float;\n",
    "            elif isinstance(vi,complex):\n",
    "                t=complex;\n",
    "                break;\n",
    "            else:\n",
    "                raise TypeError(\"gda_cvec: list contains unsupported type %s\" % type(vi));\n",
    "        w = np.zeros((r,1),dtype=t);\n",
    "        w[:,0] = np.array(v); # patch v -> np.array(v)\n",
    "        return w;\n",
    "    elif isinstance(v, tuple):\n",
    "        r = len(v);\n",
    "        t = int;\n",
    "        for vi in v:\n",
    "            if isinstance(vi,int) or isinstance(vi, np.int32): #patch v->vi 20230418\n",
    "                pass;\n",
    "            elif isinstance(vi,float):\n",
    "                t=float;\n",
    "            elif isinstance(vi,complex):\n",
    "                t=complex;\n",
    "                break;\n",
    "            else:\n",
    "                raise TypeError(\"gda_cvec: tuple contains unsupported type %s\" % type(vi));\n",
    "        w = np.zeros((r,1),dtype=t);\n",
    "        w[:,0] = np.array(list(v)); # patch v -> np.array(list(v));\n",
    "        return w;\n",
    "    else:\n",
    "        raise TypeError(\"gda_cvec: %s not supported\" % type(v));\n",
    "\n",
    "        \n",
    "# gda_draw function makes a \"pictorial matrix equation\"\n",
    "# arguments are vectors, matrices and strings\n",
    "# which are plotted in the order that the appear\n",
    "# except that strings starting with 'title ' are plotted\n",
    "# under the subseqeunt matrix or vector\n",
    "# always returns a status of 1\n",
    "def gda_draw(*argv):\n",
    "    DOCOLOR=True;\n",
    "    if( DOCOLOR ):\n",
    "        bwcmap = matplotlib.colormaps['jet'];\n",
    "    else:\n",
    "        bw = np.zeros((256,4));\n",
    "        v = 0.9*(256 - np.linspace( 0, 255, 256 ))/255;\n",
    "        bw[:,0] = v;\n",
    "        bw[:,1] = v;\n",
    "        bw[:,2] = v;\n",
    "        bw[:,3] = np.ones(256);\n",
    "        bwcmap = ListedColormap(bw);\n",
    "    # size of plot\n",
    "    W = 16;\n",
    "    H = 4;\n",
    "    fig1 = plt.figure(1);\n",
    "    # figsize width and height in inches\n",
    "    fig1.set_size_inches(W,H);\n",
    "    ax1 = plt.subplot(1,1,1);\n",
    "    plt.axis([0, W, -H/2, H/2]);\n",
    "    plt.axis('off');\n",
    "    LM = W/6;    # matrix width and heoght\n",
    "    LV = W/40;   # vector width\n",
    "    FS = 0.12;    # character width\n",
    "    TO = 0.4;    # title vertical offset\n",
    "    SP = 0.2;    # space between objects\n",
    "    LS = 0.2;    # leading space\n",
    "    p = LS; # starting x-position\n",
    "    istitle=0; # flags presence of a title\n",
    "    for a in argv:\n",
    "        if isinstance(a,np.ndarray):\n",
    "            sh = np.shape(a);\n",
    "            if len(sh) == 1:  # conversion to nx1 array\n",
    "                n = sh[0];\n",
    "                m = 1;\n",
    "                ap = a;\n",
    "                a = np.zeros((n,1));\n",
    "                a[:,0] = ap;\n",
    "            else:\n",
    "                n = sh[0];\n",
    "                m = sh[1];\n",
    "            if m==1:\n",
    "                pold=p;\n",
    "                left=p;\n",
    "                right=p+LV;\n",
    "                bottom=-LM/2;\n",
    "                top=LM/2;\n",
    "                plt.imshow( a, cmap=bwcmap, vmin=np.min(a), vmax=np.max(a), extent=(left,right,bottom,top) );\n",
    "                p = p+LV;\n",
    "                pm = (p+pold)/2;\n",
    "                if istitle:\n",
    "                    plt.text(pm,-(LM/2)-TO,titlestr,horizontalalignment='center');\n",
    "                    istitle=0;\n",
    "                p = p+SP;\n",
    "            else:\n",
    "                pold=p;\n",
    "                left=p;\n",
    "                right=p+LM;\n",
    "                bottom=-LM/2;\n",
    "                top=LM/2;\n",
    "                plt.imshow( a, cmap=bwcmap, vmin=np.min(a), vmax=np.max(a), extent=(left,right,bottom,top) );\n",
    "                p = p+LM;\n",
    "                pm = (p+pold)/2;\n",
    "                if istitle:\n",
    "                    plt.text(pm,-(LM/2)-TO,titlestr,horizontalalignment='center');\n",
    "                    istitle=0;\n",
    "                p = p+SP;\n",
    "        elif isinstance(a,str):\n",
    "            ns = len(a);\n",
    "            istitle=0;\n",
    "            if( ns>=6 ):\n",
    "                if 'title ' in a[0:6]:\n",
    "                    istitle=1;\n",
    "                    titlestr=a[6:];\n",
    "            if( istitle != 1):\n",
    "                plt.text(p,0,a);\n",
    "                p = p + ns*FS + SP;\n",
    "    plt.show();\n",
    "    return 1;\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "done!\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 65088 characters]",
      "text/plain": [
       "<Figure size 800x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "groupno = 2; # group number\n",
    "\n",
    "# plate flexure, known bathymetry b(x)\n",
    "gamma = 6.67e-11; # gravitational constant N⋅m2/kg2\n",
    "g=9.81; # acceleration of gravity\n",
    "rho1 = 1000.0; # water density kg/m3\n",
    "rho2 = 2500.0; # crust density kg/m3\n",
    "rho3 = 3200.0; # mantle density kg/m3\n",
    "D = 5.0e21; # flexural rigidity N-m\n",
    "h = 100.0*1000.0; # observation level (m)\n",
    "T = 100.0*1000.0; # plate thickness (m)\n",
    "PLOTASD = 0; # plot amplitude spectral density on 1, don't on 0\n",
    "fscale = 7e-5; # plotting scale for gravity\n",
    "\n",
    "# standard set-up of position x and wavenumber kx-axis\n",
    "Nx=4096;\n",
    "Nkx=int(Nx/2+1);\n",
    "Dx=0.5*1000.0;\n",
    "x=gda_cvec(Dx*np.linspace(0,Nx-1,Nx));\n",
    "xmin = 0.0;\n",
    "xmax = Dx*(Nx-1);\n",
    "ixmid = int(floor(Nx/2));\n",
    "xmid = x[ixmid,0];\n",
    "kxmax=2.0*pi/(2.0*Dx);\n",
    "Dkx=kxmax/(Nx/2);\n",
    "kxp = gda_cvec( Dkx*np.linspace(0,Nkx-1,Nkx));\n",
    "kxn = -np.flipud(kxp[1:Nkx-1,0:1]);\n",
    "kx = gda_cvec(kxp,kxn);\n",
    "kxabs = np.abs(kx);\n",
    "\n",
    "D = np.genfromtxt(\"Group_%d_grav.txt\" % groupno, delimiter='\\t');\n",
    "fobs = D[0:Nx,1:2];\n",
    "fobsabsmax = np.max(np.abs(fobs));\n",
    "\n",
    "Npts = 51;\n",
    "Nc = int(Npts/2);\n",
    "xp = gda_cvec( np.linspace(xmin,xmax,Npts) );\n",
    "bp = np.zeros((Npts,1));\n",
    "bp[Nc,0]=1000.0;\n",
    "\n",
    "METHOD=0;\n",
    "# get current backend\n",
    "if( METHOD==1):\n",
    "    bkend = matplotlib.get_backend();\n",
    "    \n",
    "#  use interactive plotting\n",
    "if( METHOD==1 ):\n",
    "    matplotlib.use('Qt5Agg');\n",
    "else:\n",
    "    %matplotlib qt5\n",
    "\n",
    "fig1 = plt.figure(1,figsize=(8,8));\n",
    "ax1 = plt.subplot(2,1,1);\n",
    "ax2 = plt.subplot(2,1,2);\n",
    "\n",
    "# interpolated load\n",
    "cs = interp.CubicSpline(xp.ravel(), bp.ravel(), bc_type='natural', extrapolate=True);\n",
    "b = gda_cvec(cs(x));\n",
    "bt = np.fft.fft( b, axis=0 );\n",
    "\n",
    "# flexure\n",
    "wt = np.divide( -g*(rho2-rho1)*bt, D*np.power(kx,4) + g*(rho3-rho2) );\n",
    "wtabsmax = np.max(np.abs(wt));\n",
    "w = np.real(np.fft.ifft(wt, axis=0));\n",
    "wabsmax = np.max(np.abs(w));\n",
    "\n",
    "# gravity anomaly\n",
    "e1 = np.exp(-kxabs*h);\n",
    "e2 = np.exp(-kxabs*(T+h));\n",
    "q = np.reciprocal( D*np.power(kx,4) + g*(rho3-rho2) );\n",
    "term1 = -2*pi*gamma*(rho2-rho1)*np.multiply(bt,e1);\n",
    "term2 = -2*pi*gamma*(rho3-rho2)*np.multiply(wt,e2);\n",
    "ft = term1 + term2\n",
    "ftabsmax = np.max(np.abs(ft));\n",
    "f = np.real(np.fft.ifft(ft, axis=0));\n",
    "fabsmax = np.max(np.abs(f));\n",
    "\n",
    "# plot load\n",
    "ax1.cla();\n",
    "ax1.axis( [ 0.0/1000.0, xmax/1000.0, -1.1*2000, 1.1*2000] );\n",
    "ax1.invert_yaxis();\n",
    "ax1.plot( xp/1000.0, bp, 'ro', lw=3);\n",
    "ax1.plot( x/1000.0, b, 'k-', lw=2);\n",
    "ax1.set_xlabel('x (km)');\n",
    "ax1.set_ylabel('b(x) (m)');\n",
    "ax1.set_title('click to update load, click right of left axis terminates');\n",
    "\n",
    "# plot gravity\n",
    "ax2.cla();\n",
    "ax2.axis( [ 0.0/1000.0, xmax/1000.0, -1.1*fobsabsmax, 1.1*fobsabsmax] );\n",
    "ax2.plot( gda_cvec(xmin,xmax)/1000.0, gda_cvec(0.0,0.0), 'k:', lw=1);\n",
    "ax2.plot( x/1000.0, fobs, 'k-', lw=4);\n",
    "ax2.plot( x/1000.0, f, 'r-', lw=2);\n",
    "ax2.set_xlabel('x (km)');\n",
    "ax2.set_ylabel('f(x) (N)');\n",
    "\n",
    "NITER = 1000;\n",
    "for myiter in range(NITER):\n",
    "    \n",
    "    # mouse click to update load\n",
    "    p = fig1.ginput(1);  # returns list of (x,y) tupples\n",
    "    if( len(p)==0 ):\n",
    "        continue;\n",
    "    xc = 1000*p[0][0];\n",
    "    bc = p[0][1];\n",
    "    xedge = xmin+(xmax-xmin)/20.0;\n",
    "    if( xc < xedge ):\n",
    "        print('done!');\n",
    "        break;\n",
    "    ixc = np.argmin(np.abs(xp-xc));\n",
    "    bp[ixc] = bc;\n",
    "    \n",
    "    # interpolated load\n",
    "    cs = interp.CubicSpline(xp.ravel(), bp.ravel(), bc_type='natural', extrapolate=True);\n",
    "    b = gda_cvec(cs(x));\n",
    "    bt = np.fft.fft( b, axis=0 );\n",
    "\n",
    "    \n",
    "    # flexure\n",
    "    wt = np.divide( -g*(rho2-rho1)*bt, D*np.power(kx,4) + g*(rho3-rho2) );\n",
    "    wtabsmax = np.max(np.abs(wt));\n",
    "    w = np.real(np.fft.ifft(wt, axis=0));\n",
    "    wabsmax = np.max(np.abs(w));\n",
    "\n",
    "    # gravity anomaly\n",
    "    e1 = np.exp(-kxabs*h);\n",
    "    e2 = np.exp(-kxabs*(T+h));\n",
    "    q = np.reciprocal( D*np.power(kx,4) + g*(rho3-rho2) );\n",
    "    term1 = -2*pi*gamma*(rho2-rho1)*np.multiply(bt,e1);\n",
    "    term2 = -2*pi*gamma*(rho3-rho2)*np.multiply(wt,e2);\n",
    "    ft = term1 + term2\n",
    "    ftabsmax = np.max(np.abs(ft));\n",
    "    f = np.real(np.fft.ifft(ft, axis=0));\n",
    "    fabsmax = np.max(np.abs(f));\n",
    "\n",
    "    # plot bathymetry\n",
    "    ax1.cla();\n",
    "    ax1.axis( [ 0.0/1000.0, xmax/1000.0, -1.1*2000, 1.1*2000] );\n",
    "    ax1.invert_yaxis();\n",
    "    ax1.plot( xp/1000.0, bp, 'ro', lw=3);\n",
    "    ax1.plot( x/1000.0, b, 'k-', lw=2);\n",
    "    ax1.set_xlabel('x (km)');\n",
    "    ax1.set_ylabel('b(x) (m)');\n",
    "    ax1.set_title('click to update load, click right of left axis terminates');\n",
    "\n",
    "    # plot gravity\n",
    "    ax2.cla();\n",
    "    ax2.axis( [ 0.0/1000.0, xmax/1000.0, -1.1*fscale, 1.1*fscale] );\n",
    "    ax2.plot( gda_cvec(xmin,xmax)/1000.0, gda_cvec(0.0,0.0), 'k:', lw=1);\n",
    "    ax2.plot( x/1000.0, fobs, 'k-', lw=4);\n",
    "    ax2.plot( x/1000.0, f, 'r-', lw=2);\n",
    "    ax2.set_xlabel('x (km)');\n",
    "    ax2.set_ylabel('f(x) (N)');\n",
    "    plt.draw();  # must draw to update plot\n",
    "    \n",
    "plt.close(fig1);\n",
    "\n",
    "# switch back to standard plotting\n",
    "if( METHOD==1 ):\n",
    "    matplotlib.use(bkend);\n",
    "else:\n",
    "    %matplotlib inline\n",
    "\n",
    "fig2 = plt.figure(1,figsize=(8,8));\n",
    "ax1 = plt.subplot(2,1,1);\n",
    "ax2 = plt.subplot(2,1,2);\n",
    "    \n",
    "# plot bathymetry\n",
    "ax1.cla();\n",
    "ax1.axis( [ 0.0/1000.0, xmax/1000.0, -1.1*4000, 1.1*4000] );\n",
    "ax1.invert_yaxis();\n",
    "ax1.plot( xp/1000.0, bp, 'ro', lw=3);\n",
    "ax1.plot( x/1000.0, b, 'k-', lw=2);\n",
    "ax1.set_xlabel('x (km)');\n",
    "ax1.set_ylabel('bathymetry b(x) (m)');\n",
    "\n",
    "# plot gravity\n",
    "ax2.cla();\n",
    "ax2.axis( [ 0.0/1000.0, xmax/1000.0, -1.1*fobsabsmax, 1.1*fobsabsmax] );\n",
    "ax2.plot( gda_cvec(xmin,xmax)/1000.0, gda_cvec(0.0,0.0), 'k:', lw=1);\n",
    "ax2.plot( x/1000.0, fobs, 'k-', lw=4);\n",
    "ax2.plot( x/1000.0, f, 'r-', lw=2);\n",
    "ax2.set_xlabel('x (km)');\n",
    "ax2.set_ylabel('gravity f(x) (N)');\n",
    "\n",
    "plt.show();\n",
    "\n",
    "np.savetxt(\"Group_%d_load_est.txt\" % groupno,np.concatenate((x,b),axis=1),delimiter=\"\\t\");\n",
    "np.savetxt(\"Group_%d_grav_pre.txt\" % groupno,np.concatenate((x,f),axis=1),delimiter=\"\\t\");\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
