Beispiel #1
0
def run(plotIt=True):
    """
        PF: Magnetics: Analytics
        ========================

        Comparing the magnetics field in Vancouver to Seoul

    """

    xr = np.linspace(-300, 300, 41)
    yr = np.linspace(-300, 300, 41)
    X, Y = np.meshgrid(xr, yr)
    Z = np.ones((np.size(xr), np.size(yr)))*150

    # Bz component in Korea
    inckr = -8. + 3./60
    deckr = 54. + 9./60
    btotkr = 50898.6
    Bokr = PF.MagAnalytics.IDTtoxyz(inckr, deckr, btotkr)

    bx, by, bz = PF.MagAnalytics.MagSphereAnaFunA(
        X, Y, Z, 100., 0., 0., 0., 0.01, Bokr, 'secondary'
    )
    Bzkr = np.reshape(bz, (np.size(xr), np.size(yr)), order='F')

    # Bz component in Canada
    incca = 16. + 49./60
    decca = 70. + 19./60
    btotca = 54692.1
    Boca = PF.MagAnalytics.IDTtoxyz(incca, decca, btotca)

    bx, by, bz = PF.MagAnalytics.MagSphereAnaFunA(
        X, Y, Z, 100., 0., 0., 0., 0.01, Boca, 'secondary'
    )
    Bzca = np.reshape(bz, (np.size(xr), np.size(yr)), order='F')

    if plotIt:
        import matplotlib.pyplot as plt
        from mpl_toolkits.axes_grid1 import make_axes_locatable
        fig = plt.figure(figsize=(14, 5))

        ax1 = plt.subplot(121)
        dat1 = plt.imshow(Bzkr, extent=[min(xr), max(xr), min(yr), max(yr)])
        divider = make_axes_locatable(ax1)
        cax1 = divider.append_axes("right", size="5%", pad=0.05)
        ax1.set_xlabel('East-West (m)')
        ax1.set_ylabel('South-North (m)')
        plt.colorbar(dat1, cax=cax1)
        ax1.set_title('$B_z$ field at Seoul, South Korea')

        ax2 = plt.subplot(122)
        dat2 = plt.imshow(Bzca, extent=[min(xr), max(xr), min(yr), max(yr)])
        divider = make_axes_locatable(ax2)
        cax2 = divider.append_axes("right", size="5%", pad=0.05)
        ax2.set_xlabel('East-West (m)')
        ax2.set_ylabel('South-North (m)')
        plt.colorbar(dat2, cax=cax2)
        ax2.set_title('$B_z$ field at Vancouver, Canada')
        plt.show()
Beispiel #2
0
    def magnetizationModel(self):
        """
            magnetization vector
        """

        if getattr(self, 'magfile', None) is None:

            M = Magnetics.dipazm_2_xyz(np.ones(self.nC) *
                                       self.survey.srcField.param[1],
                                       np.ones(self.nC) *
                                       self.survey.srcField.param[2])

        else:

            with open(self.basePath + self.magfile) as f:
                magmodel = f.read()

            magmodel = magmodel.splitlines()
            M = []

            for line in magmodel:
                M.append(map(float, line.split()))

            # Convert list to 2d array
            M = np.vstack(M)

            # Cycle through three components and permute from UBC to SimPEG
            for ii in range(3):
                m = np.reshape(M[:, ii],
                               (self.mesh.nCz, self.mesh.nCx, self.mesh.nCy),
                               order='F')

                m = m[::-1, :, :]
                m = np.transpose(m, (1, 2, 0))
                M[:, ii] = Utils.mkvc(m)

        self._M = M

        return self._M
Beispiel #3
0
def readUBC_DC3Dobs(fileName):
    
    from SimPEG import np
    """
        Read UBC GIF DCIP 3D observation file and generate arrays for tx-rx location
    
        Input:
        :param fileName, path to the UBC GIF 3D obs file
    
        Output:
        :param rx, tx, d, wd
        :return
        
        Created on Mon December 7th, 2015
    
        @author: dominiquef
    
    """
       
    # Load file
    obsfile = np.genfromtxt(fileName,delimiter=' \n',dtype=np.str,comments='!')
    
    # Pre-allocate
    Tx = []
    Rx = []
    d = []
    wd = []
    
    # Countdown for number of obs/tx
    count = 0
    for ii in range(obsfile.shape[0]):
        
        if not obsfile[ii]:
            continue
        
        # First line is transmitter with number of receivers
        if count==0:
    
            temp = (np.fromstring(obsfile[ii], dtype=float,sep=' ').T)
            count = int(temp[-1])
            temp = np.reshape(temp[0:-1],[2,3]).T
            
            Tx.append(temp)
            rx = []
            continue
        
        temp = np.fromstring(obsfile[ii], dtype=float,sep=' ')
        
            
        rx.append(temp)          
        
        count = count -1        
        
        # Reach the end of  
        if count == 0:
            temp = np.asarray(rx)
            Rx.append(temp[:,0:6])
            
            # Check for data + uncertainties
            if temp.shape[1]==8:
                d.append(temp[:,6])
                wd.append(temp[:,7])
                
            # Check for data only    
            elif temp.shape[1]==7:
                d.append(temp[:,6])
            
    return Tx, Rx, d, wd
Beispiel #4
0
indx = PF.BaseMag.gocad2vtk(topsurf, mesh, bcflag=False, inflag=True)
actv = np.zeros(mesh.nC)
actv[indx] = 1

model[actv == 0] = -100

Utils.meshutils.writeUBCTensorModel('VTKout.dat', mesh, model)

Utils.meshutils.writeUBCTensorMesh('Mesh_temp.msh', mesh)

start_time = tm.time()

d = PF.Magnetics.Intgrl_Fwr_Data(mesh, B, M, rxLoc, model, actv, 'tmi')

timer = (tm.time() - start_time)

#%% Plot data
plt.figure()
ax = plt.subplot()
plt.imshow(np.reshape(d, X.shape),
           interpolation="bicubic",
           extent=[xr.min(), xr.max(), yr.min(),
                   yr.max()],
           origin='lower')
plt.clim(0, 25)
plt.colorbar(fraction=0.02)
plt.contour(X, Y, np.reshape(d, X.shape), 10)
plt.scatter(X, Y, c=np.reshape(d, X.shape), s=20)

ax.set_title('Forward data')
Beispiel #5
0
def animate(ii):

    #for ii in range(1):
    removeFrame()
    # Grab current line and
    indx = np.where(lineID == ii)[0]

    srcLeft = []
    obs_l = []
    obs = []
    srcRight = []
    obs_r = []
    srcList = []
    # Split the obs file into left and right
    # Split the obs file into left and right
    for jj in range(len(indx)):

        # Grab corresponding data
        obs = np.hstack([obs, DCdobs2D.dobs[dataID == indx[jj]]])
        #std = dobs2D.std[dataID==indx[jj]]

        srcList.append(DCdobs2D.srcList[indx[jj]])

        Tx = DCdobs2D.srcList[indx[jj]].loc
        Rx = DCdobs2D.srcList[indx[jj]].rxList[0].locs

        # Create mid-point location
        Cmid = (Tx[0][0] + Tx[1][0]) / 2
        Pmid = (Rx[0][:, 0] + Rx[1][:, 0]) / 2

        ileft = Pmid < Cmid
        iright = Pmid >= Cmid

        temp = np.zeros(len(ileft))
        temp[ileft] = 1
        obs_l = np.hstack([obs_l, temp])

        temp = np.zeros(len(iright))
        temp[iright] = 1
        obs_r = np.hstack([obs_r, temp])

        if np.any(ileft):
            rx = DC.RxDipole(Rx[0][ileft, :], Rx[1][ileft, :])
            srcLeft.append(DC.SrcDipole([rx], Tx[0], Tx[1]))

            #std_l = np.hstack([std_l,std[ileft]])

        if np.any(iright):
            rx = DC.RxDipole(Rx[0][iright, :], Rx[1][iright, :])
            srcRight.append(DC.SrcDipole([rx], Tx[0], Tx[1]))

            #obs_r = np.hstack([obs_r,iright])
            #std_r = np.hstack([std_r,std[iright]])

    DC2D_full = DC.SurveyDC(srcList)
    DC2D_full.dobs = np.asarray(obs)
    DC2D_full.std = DC2D_full.dobs * 0.
    DC2D_full.std[obs_l ==
                  1] = np.abs(DC2D_full.dobs[obs_l == 1]) * 0.02 + 2e-5
    DC2D_full.std[obs_r ==
                  1] = np.abs(DC2D_full.dobs[obs_r == 1]) * 0.06 + 4e-5

    #    DC2D_l = DC.SurveyDC(srcLeft)
    #    DC2D_l.dobs = np.asarray(obs[obs_l==1])
    #    DC2D_l.std = np.abs(np.asarray(DC2D_l.dobs))*0.05 + 2e-5
    #
    #    DC2D_r = DC.SurveyDC(srcRight)
    #    DC2D_r.dobs = np.asarray(obs[obs_r==1])
    #    DC2D_r.std = np.abs(np.asarray(DC2D_r.dobs))*0.05 + 2e-5

    survey = DC2D_full

    # Export data file
    DC.writeUBC_DCobs(inv_dir + dsep + obsfile2d, survey, '2D', 'SIMPLE')

    # Write input file
    fid = open(inv_dir + dsep + inp_file, 'w')
    fid.write('OBS LOC_X %s \n' % obsfile2d)
    fid.write('MESH FILE %s \n' % mshfile2d)
    fid.write('CHIFACT 1 \n')
    fid.write('TOPO DEFAULT \n')
    fid.write('INIT_MOD VALUE %e\n' % ini_mod)
    fid.write('REF_MOD VALUE %e\n' % ref_mod)
    fid.write('ALPHA VALUE %f %f %F\n' % (1. / dx**4., 1, 1))
    fid.write('WEIGHT DEFAULT\n')
    fid.write('STORE_ALL_MODELS FALSE\n')
    fid.write('INVMODE CG\n')
    #fid.write('CG_PARAM 200 1e-4\n')
    fid.write('USE_MREF FALSE\n')
    #fid.write('BOUNDS VALUE 1e-4 1e+2\n')
    fid.close()

    os.chdir(inv_dir)
    os.system('dcinv2d ' + inp_file)

    #%% Load DC model and predicted data
    minv = DC.readUBC_DC2DModel(inv_dir + dsep + 'dcinv2d.con')
    minv = np.reshape(minv, (mesh2d.nCy, mesh2d.nCx))

    #%% Repeat for IP data
    indx = np.where(IPlineID == ii)[0]

    srcLeft = []
    obs_l = []
    std_l = []

    srcRight = []
    obs_r = []
    std_r = []

    obs_full = []
    std_full = []
    srcList = []

    # Split the obs file into left and right
    for jj in range(len(indx)):

        srcList.append(IPdobs2D.srcList[indx[jj]])
        # Grab corresponding data
        obs = IPdobs2D.dobs[IPdataID == indx[jj]]
        std = IPdobs2D.std[IPdataID == indx[jj]]

        obs_full = np.hstack([obs_full, obs])
        std_full = np.hstack([std_full, std])

        Tx = IPdobs2D.srcList[indx[jj]].loc
        Rx = IPdobs2D.srcList[indx[jj]].rxList[0].locs

        # Create mid-point location
        Cmid = (Tx[0][0] + Tx[1][0]) / 2
        Pmid = (Rx[0][:, 0] + Rx[1][:, 0]) / 2

        ileft = Pmid < Cmid
        iright = Pmid >= Cmid

        temp = np.zeros(len(ileft))
        temp[ileft] = 1
        obs_l = np.hstack([obs_l, temp])

        temp = np.zeros(len(iright))
        temp[iright] = 1
        obs_r = np.hstack([obs_r, temp])

        if np.any(ileft):
            rx = DC.RxDipole(Rx[0][ileft, :], Rx[1][ileft, :])
            srcLeft.append(DC.SrcDipole([rx], Tx[0], Tx[1]))

            #std_l = np.hstack([std_l,std[ileft]])

        if np.any(iright):
            rx = DC.RxDipole(Rx[0][iright, :], Rx[1][iright, :])
            srcRight.append(DC.SrcDipole([rx], Tx[0], Tx[1]))

    IP2D_full = DC.SurveyDC(srcList)
    IP2D_full.dobs = np.asarray(obs_full)
    IP2D_full.std = np.asarray(std_full)

    IP2D_l = DC.SurveyDC(srcLeft)
    IP2D_l.dobs = np.asarray(obs_full[obs_l == 1])
    #IP2D_l.std = np.abs(np.asarray(obs_l))*0.03 + 2e-2

    IP2D_r = DC.SurveyDC(srcRight)
    IP2D_r.dobs = np.asarray(obs_full[obs_r == 1])
    #IP2D_r.std = np.abs(np.asarray(obs_r))*0.03 + 1e-2

    id_lbe = int(IPsurvey.srcList[indx[jj]].loc[0][1])

    mesh3d = Mesh.TensorMesh([hx, np.ones(1) * 100., hz],
                             x0=(-np.sum(padx) + np.min(srcMat[0][:, 0]),
                                 id_lbe - 50,
                                 np.max(srcMat[0][0, 2]) - np.sum(hz)))
    Mesh.TensorMesh.writeUBC(mesh3d,
                             home_dir + dsep + 'Mesh' + str(id_lbe) + '.msh')
    global ax1, ax2, ax3, ax5, ax6, fig

    ax2 = plt.subplot(3, 2, 2)
    ph = DC.plot_pseudoSection(IP2D_r,
                               ax2,
                               stype='pdp',
                               dtype='volt',
                               colorbar=False)
    ax2.set_title('Observed P-DP', fontsize=10)
    plt.xlim([xmin, xmax])
    plt.ylim([zmin, zmax])
    plt.gca().set_aspect('equal', adjustable='box')
    ax2.set_xticklabels([])
    ax2.set_yticklabels([])

    ax1 = plt.subplot(3, 2, 1)
    DC.plot_pseudoSection(IP2D_l,
                          ax1,
                          stype='pdp',
                          dtype='volt',
                          clim=(ph[0].get_clim()[0], ph[0].get_clim()[1]),
                          colorbar=False)
    ax1.set_title('Observed DP-P', fontsize=10)
    plt.xlim([xmin, xmax])
    plt.ylim([zmin, zmax])
    plt.gca().set_aspect('equal', adjustable='box')
    ax1.set_xticklabels([])
    z = np.linspace(np.min(ph[2]), np.max(ph[2]), 5)
    z_label = np.linspace(20, 1, 5)
    ax1.set_yticks(map(int, z))
    ax1.set_yticklabels(map(str, map(int, z_label)), size=8)
    ax1.set_ylabel('n-spacing', fontsize=8)

    #%% Add labels
    bbox_props = dict(boxstyle="circle,pad=0.3", fc="r", ec="k", lw=1)
    ax2.text(0.00,
             1,
             'A',
             transform=ax2.transAxes,
             ha="left",
             va="center",
             size=6,
             bbox=bbox_props)

    bbox_props = dict(boxstyle="circle,pad=0.3", fc="y", ec="k", lw=1)
    ax2.text(0.1,
             1,
             'M',
             transform=ax2.transAxes,
             ha="left",
             va="center",
             size=6,
             bbox=bbox_props)

    bbox_props = dict(boxstyle="circle,pad=0.3", fc="g", ec="k", lw=1)
    ax2.text(0.2,
             1,
             'N',
             transform=ax2.transAxes,
             ha="left",
             va="center",
             size=6,
             bbox=bbox_props)

    bbox_props = dict(boxstyle="circle,pad=0.3", fc="g", ec="k", lw=1)
    ax1.text(0.00,
             1,
             'N',
             transform=ax1.transAxes,
             ha="left",
             va="center",
             size=6,
             bbox=bbox_props)

    bbox_props = dict(boxstyle="circle,pad=0.3", fc="y", ec="k", lw=1)
    ax1.text(0.1,
             1,
             'M',
             transform=ax1.transAxes,
             ha="left",
             va="center",
             size=6,
             bbox=bbox_props)

    bbox_props = dict(boxstyle="circle,pad=0.3", fc="r", ec="k", lw=1)
    ax1.text(0.2,
             1,
             'A',
             transform=ax1.transAxes,
             ha="left",
             va="center",
             size=6,
             bbox=bbox_props)

    survey = IP2D_full

    # Export data file
    DC.writeUBC_DCobs(inv_dir + dsep + ipfile2d,
                      survey,
                      '2D',
                      'SIMPLE',
                      iptype=1)

    fid = open(inv_dir + dsep + inp_file, 'w')
    fid.write('OBS LOC_X %s \n' % ipfile2d)
    fid.write('MESH FILE %s \n' % mshfile2d)
    fid.write('CHIFACT 4 \n')
    fid.write('COND FILE dcinv2d.con\n')
    fid.write('TOPO DEFAULT \n')
    fid.write('INIT_MOD VALUE %e\n' % ini_mod)
    fid.write('REF_MOD VALUE 0.0\n')
    fid.write('ALPHA VALUE %f %f %F\n' % (1. / dx**4., 1, 1))
    fid.write('WEIGHT DEFAULT\n')
    fid.write('STORE_ALL_MODELS FALSE\n')
    fid.write('INVMODE CG\n')
    #fid.write('CG_PARAM 200 1e-4\n')
    fid.write('USE_MREF FALSE\n')
    #fid.write('BOUNDS VALUE 1e-4 1e+2\n')
    fid.close()

    os.chdir(inv_dir)
    os.system('ipinv2d ' + inp_file)

    #%% Load model and predicted data
    minv = DC.readUBC_DC2DModel(inv_dir + dsep + 'ipinv2d.chg')
    minv = np.reshape(minv, (mesh2d.nCy, mesh2d.nCx))

    Mesh.TensorMesh.writeModelUBC(
        mesh3d, home_dir + dsep + 'Model' + str(id_lbe) + '.chg', minv.T)

    dpre = DC.readUBC_DC2Dpre(inv_dir + dsep + 'ipinv2d.pre')
    DCpre = dpre['DCsurvey']

    DCtemp = IP2D_l
    DCtemp.dobs = DCpre.dobs[obs_l == 1]

    ax5 = plt.subplot(3, 2, 3)
    DC.plot_pseudoSection(DCtemp,
                          ax5,
                          stype='pdp',
                          dtype='volt',
                          clim=(ph[0].get_clim()[0], ph[0].get_clim()[1]),
                          colorbar=False)
    ax5.set_title('Predicted', fontsize=10)
    plt.xlim([xmin, xmax])
    plt.ylim([zmin, zmax])
    plt.gca().set_aspect('equal', adjustable='box')
    ax5.set_xticklabels([])
    z = np.linspace(np.min(ph[2]), np.max(ph[2]), 5)
    z_label = np.linspace(20, 1, 5)
    ax5.set_yticks(map(int, z))
    ax5.set_yticklabels(map(str, map(int, z_label)), size=8)
    ax5.set_ylabel('n-spacing', fontsize=8)

    DCtemp = IP2D_r
    DCtemp.dobs = DCpre.dobs[obs_r == 1]

    ax6 = plt.subplot(3, 2, 4)
    DC.plot_pseudoSection(DCtemp,
                          ax6,
                          stype='pdp',
                          dtype='volt',
                          clim=(ph[0].get_clim()[0], ph[0].get_clim()[1]),
                          colorbar=False)
    ax6.set_title('Predicted', fontsize=10)
    plt.xlim([xmin, xmax])
    plt.ylim([zmin, zmax])
    plt.gca().set_aspect('equal', adjustable='box')
    ax6.set_xticklabels([])
    ax6.set_yticklabels([])

    pos = ax6.get_position()
    cbarax = fig.add_axes([
        pos.x0 + 0.325, pos.y0 + 0.2, pos.width * 0.1, pos.height * 0.5
    ])  ## the parameters are the specified position you set
    cb = fig.colorbar(ph[0],
                      cax=cbarax,
                      orientation="vertical",
                      ax=ax6,
                      ticks=np.linspace(ph[0].get_clim()[0],
                                        ph[0].get_clim()[1], 4),
                      format="$10^{%.1f}$")
    cb.set_label("App. Charg.", size=8)

    ax3 = plt.subplot(3, 1, 3)
    ax3.set_title('2-D Model (S/m)', fontsize=10)
    ax3.set_xticks(map(int, x))
    ax3.set_xticklabels(map(str, map(int, x)))
    ax3.set_xlabel('Easting (m)', fontsize=8)
    ax3.set_yticks(map(int, z))
    ax3.set_yticklabels(map(str, map(int, z)), rotation='vertical')
    ax3.set_ylabel('Depth (m)', fontsize=8)

    plt.xlim([xmin, xmax])
    plt.ylim([zmin / 2, zmax])
    plt.gca().set_aspect('equal', adjustable='box')

    ph2 = plt.pcolormesh(mesh2d.vectorNx,
                         mesh2d.vectorNy, (minv),
                         vmin=vmin,
                         vmax=vmax)
    plt.gca().tick_params(axis='both', which='major', labelsize=8)

    plt.draw()

    for ss in range(survey.nSrc):
        Tx = survey.srcList[ss].loc[0]
        plt.scatter(Tx[0], mesh2d.vectorNy[-1] + 10, s=10)

    pos = ax3.get_position()
    ax3.set_position([pos.x0 + 0.025, pos.y0, pos.width, pos.height])
    pos = ax3.get_position()
    cbarax = fig.add_axes([
        pos.x0 + 0.65, pos.y0 + 0.01, pos.width * 0.05, pos.height * 0.75
    ])  ## the parameters are the specified position you set
    cb = fig.colorbar(ph2,
                      cax=cbarax,
                      orientation="vertical",
                      ax=ax3,
                      ticks=np.linspace(vmin, vmax, 4),
                      format="%4.1f")
    cb.set_label("Chargeability", size=8)

    pos = ax1.get_position()
    ax1.set_position([pos.x0 + 0.03, pos.y0, pos.width, pos.height])

    pos = ax5.get_position()
    ax5.set_position([pos.x0 + 0.03, pos.y0, pos.width, pos.height])

    pos = ax2.get_position()
    ax2.set_position([pos.x0 - 0.03, pos.y0, pos.width, pos.height])

    pos = ax6.get_position()
    ax6.set_position([pos.x0 - 0.03, pos.y0, pos.width, pos.height])

    #%% Add the extra

    bbox_props = dict(boxstyle="rarrow,pad=0.3", fc="w", ec="k", lw=2)
    ax2.text(0.01, (float(ii) + 1.) / (len(uniqueID) + 2),
             'N: ' + str(id_lbe),
             transform=fig.transFigure,
             ha="left",
             va="center",
             size=8,
             bbox=bbox_props)

    mrk_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=2)
    ax2.text(0.01,
             0.9,
             'Line ID#',
             transform=fig.transFigure,
             ha="left",
             va="center",
             size=8,
             bbox=mrk_props)

    mrk_props = dict(boxstyle="square,pad=0.3", fc="b", ec="k", lw=2)

    for jj in range(len(uniqueID)):
        ax2.text(0.1, (float(jj) + 1.) / (len(uniqueID) + 2),
                 ".",
                 transform=fig.transFigure,
                 ha="right",
                 va="center",
                 size=8,
                 bbox=mrk_props)

    mrk_props = dict(boxstyle="square,pad=0.3", fc="r", ec="k", lw=2)

    ax2.text(0.1, (float(ii) + 1.) / (len(uniqueID) + 2),
             ".",
             transform=fig.transFigure,
             ha="right",
             va="center",
             size=8,
             bbox=mrk_props)
Beispiel #6
0
 
 # Create array of points for interpolating from 3D to 2D mesh
 xx = Tx[0][0,0] + mesh2d.vectorCCx * np.cos(azm)
 yy = Tx[0][1,0] + mesh2d.vectorCCx * np.sin(azm)
 zz = mesh2d.vectorCCy
 
 [XX,ZZ] = np.meshgrid(xx,zz)
 [YY,ZZ] = np.meshgrid(yy,zz)
 
 xyz2d = np.c_[mkvc(XX),mkvc(YY),mkvc(ZZ)]
 
 #plt.scatter(xx,yy,s=20,c='y')
 
 
 F = interpolation.NearestNDInterpolator(mesh.gridCC,model)
 m2D = np.reshape(F(xyz2d),[mesh2d.nCx,mesh2d.nCy]).T
 
  
 #==============================================================================
 # mesh2d = Mesh.TensorMesh([mesh.hx, mesh.hz], x0=(mesh.x0[0]-endl[0,0],mesh.x0[2]))
 # m3D = np.reshape(model, (mesh.nCz, mesh.nCy, mesh.nCx))
 # m2D = m3D[:,1,:]
 #==============================================================================
 
 plt.figure()
 axs = plt.subplot(2,1,1)
 
 plt.xlim([0,nc*dx])
 plt.ylim([mesh2d.vectorNy[-1]-dl_len/2,mesh2d.vectorNy[-1]])
 plt.gca().set_aspect('equal', adjustable='box')