예제 #1
0
    def test_reflectivity(self):
        """
        What happens when the input to the reflectivity is an array?
        :return:
        """
        i = 10637
        theta = 10.  # degrees
        rho = RpTestCase.w.block['Logs'].logs['den'].data
        vp = cnvrt(RpTestCase.w.block['Logs'].logs['ac'].data, 'us/ft', 'm/s')
        vs = cnvrt(RpTestCase.w.block['Logs'].logs['acs'].data, 'us/ft', 'm/s')

        func1 = rp.reflectivity(vp[i], vp[i + 1], vs[i], vs[i + 1], rho[i],
                                rho[i + 1])
        func1_2 = rp.reflectivity(vp[i + 1], vp[i + 2], vs[i + 1], vs[i + 2],
                                  rho[i + 1], rho[i + 2])
        func2 = rp.reflectivity(vp,
                                None,
                                vs,
                                None,
                                rho,
                                None,
                                along_wiggle=True)

        with self.subTest():
            print('Layer based refl. coeff. at i {} at {} deg.: {}'.format(
                i, theta, func1(theta)))
            print('Layer based refl. coeff. at i {} at {} deg.: {}'.format(
                i + 1, theta, func1_2(theta)))
            print('Wiggle based refl. coeff. at i {} at {} deg.: {}'.format(
                i, theta,
                func2(theta)[i:i + 2]))
            self.assertTrue(True)
예제 #2
0
def main():
    # Set up a test plot
    fig, [ax1, ax2] = plt.subplots(nrows=1, ncols=2)

    ref_functions = [
        rp.reflectivity(2800, 3000, 1350, 1500, 2.5, 2.4),
        rp.reflectivity(2900, 2800, 1400, 1300, 2.5, 2.4)
    ]

    for i, ax in enumerate([ax1, ax2]):
        xdata = np.linspace(0, 40, 50)
        ydata = ref_functions[i](xdata)
        yerror = np.full(ydata.shape, 0.002)

        plot(xdata,
             ydata,
             yerror=yerror,
             c=cnames[i],
             fig=fig,
             ax=ax,
             xtempl={
                 'full_name': 'X label',
                 'unit': '-',
                 'min': 0.,
                 'max': 1.
             },
             ytempl={
                 'full_name': 'Y label',
                 'unit': '-',
                 'min': 0.,
                 'max': 1.
             })

    # Handle the legends
    legends = []
    this_legend = ax1.legend(legends,
                             prop=FontProperties(size='smaller'),
                             scatterpoints=1,
                             markerscale=0.5,
                             loc=1)
    plt.show()
예제 #3
0
    def test_step(self):
        i = 5
        theta = 10.  # degrees
        x1 = np.linspace(1, 10, 10)
        x2 = np.linspace(2, 11, 10)
        x3 = np.linspace(3, 12, 10)
        d1 = rp.step(x1[i], x1[i + 1])
        d2 = rp.step(x1, None, along_wiggle=True)
        incept1 = rp.intercept(x1[i], x1[i + 1], x3[i], x3[i + 1])
        incept2 = rp.intercept(x1, None, x3, None, along_wiggle=True)
        grad1 = rp.gradient(x1[i], x1[i + 1], x2[i], x2[i + 1], x3[i],
                            x3[i + 1])
        grad2 = rp.gradient(x1, None, x2, None, x3, None, along_wiggle=True)
        func1 = rp.reflectivity(x1[i], x1[i + 1], x2[i], x2[i + 1], x3[i],
                                x3[i + 1])
        func2 = rp.reflectivity(x1,
                                None,
                                x2,
                                None,
                                x3,
                                None,
                                along_wiggle=True)

        with self.subTest():
            print('Layer based step at i {}: {}'.format(i, d1))
            print('Wiggle based step at i {}: {}'.format(i, d2[i]))
            print('Layer based intercept at i {}: {}'.format(i, incept1))
            print('Wiggle based intercept at i {}: {}'.format(i, incept2[i]))
            print('Layer based gradient at i {}: {}'.format(i, grad1))
            print('Wiggle based gradient at i {}: {}'.format(i, grad2[i]))
            print('Layer based refl. coeff. at i {} at {} deg.: {}'.format(
                i, theta, func1(theta)))
            print('Wiggle based refl. coeff. at i {} at {} deg.: {}'.format(
                i, theta,
                func2(theta)[i]))
            self.assertTrue(True)
예제 #4
0
def plot_one_interface(sums,
                       name1,
                       name2,
                       color,
                       fig_ig,
                       ax_ig,
                       fig_refl,
                       ax_refl,
                       n_samps=1000):

    # elastics_from_stats calculates the normally distributed variables, with correlations, given
    # the mean, std and correlation, using a multivariate function
    vp1, vs1, rho1 = elastics_from_stats(sums[name1], n_samps)
    vp2, vs2, rho2 = elastics_from_stats(sums[name2], n_samps)

    # Calculate statistics of the reflection coefficient, assuming 50 samples from 0 to 40 deg incidence angle
    theta = np.linspace(0, 40, 50)
    refs = np.full((n_samps, 50), np.nan)
    # calculate the reflectivity as a function of theta for all variations of the elastic properties
    for i, params in enumerate(zip(vp1, vp2, vs1, vs2, rho1, rho2)):
        refs[i, :] = rp.reflectivity(*params)(theta)

    refl_stds = np.std(refs, 0)

    # Calculate the mean reflectivity curve
    mean_refl = rp.reflectivity(
        sums[name1]['VpMean'],
        sums[name2]['VpMean'],
        sums[name1]['VsMean'],
        sums[name2]['VsMean'],
        sums[name1]['RhoMean'],
        sums[name2]['RhoMean'],
    )

    # plot the mean reflectivity curve together with the uncertainty
    mypr.plot(theta,
              mean_refl(theta),
              c=color,
              yerror=refl_stds,
              yerr_style='fill',
              fig=fig_refl,
              ax=ax_refl)

    intercept = rp.intercept(vp1, vp2, rho1, rho2)
    gradient = rp.gradient(vp1, vp2, vs1, vs2, rho1, rho2)

    #res = least_squares(
    #        mycf.residuals,
    #        [1.,1.],
    #        args=(intercept, gradient),
    #        kwargs={'target_function': straight_line}
    #)
    #print('{} on {}: WS = {:.4f}*I {:.4f} - G'.format(name1, name2, *res.x))
    #print(res.status)
    #print(res.message)
    #print(res.success)

    myxp.plot(intercept,
              gradient,
              cdata=color,
              fig=fig_ig,
              ax=ax_ig,
              edge_color=None,
              alpha=0.2)
    #x_new = np.linspace(-0.75, 0.75, 50)
    #ax_ig.plot(x_new, straight_line(x_new, *res.x), c=color, label='_nolegend_')

    # Do AVO classification
    c1 = len(gradient[(intercept > 0.) & (gradient > -4 * intercept) &
                      (gradient < 0.)])
    c2p = len(gradient[(intercept > 0.) & (gradient < -4 * intercept)])
    c2 = len(gradient[(intercept > -0.02) & (intercept < 0.) &
                      (gradient < 0.)])
    c3 = len(gradient[(intercept < -0.02) & (gradient < 0.)])
    c4 = len(gradient[(intercept < 0.) & (gradient > 0.)])
    rest = len(gradient[(intercept > 0.) & (gradient > 0.)])
    print(
        'Class I: {:.0f}% \nClass IIp: {:.0f}% \nClass II: {:.0f}% \nClass III: {:.0f}% \nClass IV: {:.0f}%'
        .format(100. * c1 / n_samps, 100. * c2p / n_samps, 100. * c2 / n_samps,
                100. * c3 / n_samps, 100. * c4 / n_samps))
    print('Rest:  {:.0f}%'.format(100. * rest / n_samps))
예제 #5
0
def plot_logs(well,
              log_table,
              wis,
              wi_name,
              templates,
              buffer=None,
              block_name=None,
              savefig=None,
              **kwargs):
    """
    Attempts to draw a plot similar to the "CPI plots", for one working interval with some buffer.
    :param well:
    :param log_table:
        dict
        Dictionary of log type: log name key: value pairs which decides which log to use when selecting which
        velocity / sonic and density logs. Other logs (e.g. 'Resistivity') are selected based on their presence
        E.G.
            log_table = {
               'P velocity': 'vp',
               'S velocity': 'vs',
               'Density': 'rhob'}
    :param buffer:
        float
        distance in meters
        Log is plotted from top of working interval - buffer to base of working interval + buffer
    :return:
    """
    log_table = small_log_table(log_table)
    if buffer is None:
        buffer = 50.
    if block_name is None:
        block_name = cw.def_lb_name

    time_step = kwargs.pop('time_step', 0.001)
    c_f = kwargs.pop('center_frequency', 30.)
    duration = kwargs.pop('duration', 0.512)
    scaling = kwargs.pop('scaling', 30.0)

    fig = plt.figure(figsize=(20, 10))
    fig.suptitle('{} interval in well {}'.format(wi_name, well.well))
    n_cols = 22  # subdivide plot in this number of equally wide columns
    l = 0.05
    w = (1 - l) / float(n_cols + 1)
    b = 0.05
    h = 0.8
    #l = 0.05; w = 0; b = 0.1; h = 0.8
    rel_pos = [1, 4, 5, 6, 9, 12, 15,
               18]  # Column number (starting with one) of subplot
    rel_widths = [
        _x - _y
        for _x, _y in zip(np.roll(rel_pos + [n_cols], -1)[:-1], rel_pos)
    ]
    ax_names = [
        'gr_ax', 'md_ax', 'twt_ax', 'res_ax', 'rho_ax', 'cpi_ax', 'ai_ax',
        'synt_ax'
    ]
    header_axes = {}
    for i in range(len(ax_names)):
        header_axes[ax_names[i]] = fig.add_subplot(
            2,
            n_cols,
            rel_pos[i],
            position=[
                l + (rel_pos[i] - 1) * w, h + 0.05, rel_widths[i] * w,
                1 - h - 0.1
            ])  #,
        #figure=fig)
    axes = {}
    for i in range(len(ax_names)):
        axes[ax_names[i]] = fig.add_subplot(
            2,
            n_cols,
            n_cols + rel_pos[i],
            position=[l + (rel_pos[i] - 1) * w, b, rel_widths[i] * w, h])  #,
        #figure=fig)

    #
    # Start plotting data
    #
    tb = well.block[block_name]  # this log block
    depth = tb.logs['depth'].data
    mask = np.ma.masked_inside(depth, wis[well.well][wi_name][0] - buffer,
                               wis[well.well][wi_name][1] + buffer).mask

    #
    # Gamma ray and Caliper
    try_these_log_types = ['Gamma ray', 'Caliper', 'Inclination']
    log_types = [
        x for x in try_these_log_types if (len(well.get_logs_of_type(x)) > 0)
    ]
    lognames = {
        ltype: well.get_logs_of_type(ltype)[0].name
        for ltype in log_types
    }
    limits = [[templates[x]['min'], templates[x]['max']] for x in log_types]
    styles = [{
        'lw': templates[x]['line width'],
        'color': templates[x]['line color'],
        'ls': templates[x]['line style']
    } for x in log_types]
    legends = [
        '{} [{}]'.format(lognames[x], templates[x]['unit']) for x in log_types
    ]

    xlims = axis_plot(axes['gr_ax'], depth[mask],
                      [tb.logs[lognames[xx]].data[mask] for xx in log_types],
                      limits, styles)
    header_plot(header_axes['gr_ax'], xlims, legends, styles)

    #for ax in [axes[x] for x in ax_names if x not in ['twt_ax', 'synt_ax']]:
    for ax in [axes[x] for x in ax_names if x not in ['twt_ax']]:
        ax.axhline(y=wis[well.well][wi_name][0], color='k', ls='--')
        ax.axhline(y=wis[well.well][wi_name][1], color='k', ls='--')

    #
    # MD
    header_plot(header_axes['md_ax'], None, None, None, title='MD [m]')
    annotate_plot(axes['md_ax'], depth[mask])

    #
    # TWT
    # Get the time-depth relation (time as a function of md)
    _x = None
    tdr = None
    if 'P velocity' in list(log_table.keys()):
        _x = log_table['P velocity']
    elif 'Sonic' in list(log_table.keys()):
        _x = log_table['Sonic']
    if _x is not None:
        tdr = well.time_to_depth(_x, templates=templates)
        if tdr is None:
            header_plot(header_axes['twt_ax'],
                        None,
                        None,
                        None,
                        title='TWT [s]\nLacking info')
            annotate_plot(axes['twt_ax'], None)
        else:
            header_plot(header_axes['twt_ax'],
                        None,
                        None,
                        None,
                        title='TWT [s]')
            annotate_plot(axes['twt_ax'], tdr[mask])
    else:
        header_plot(header_axes['twt_ax'],
                    None,
                    None,
                    None,
                    title='TWT [s]\nLacking info')
        annotate_plot(axes['twt_ax'], None)

    if tdr is not None:
        tops_twt = [
            tdr[find_nearest(depth, y)] for y in wis[well.well][wi_name]
        ]
        #print(tops_twt)
        #for ax in [axes[x] for x in ['twt_ax', 'synt_ax']]:
        for ax in [axes[x] for x in ['twt_ax']]:
            ax.axhline(y=tops_twt[0], color='k', ls='--')
            ax.axhline(y=tops_twt[1], color='k', ls='--')

    #
    # Resistivity
    log_types = ['Resistivity']
    lognames = {
        ltype: [x.name for x in well.get_logs_of_type(ltype)]
        for ltype in log_types
    }
    limits = [[templates[x]['min'], templates[x]['max']] for x in log_types]
    cls = ['r', 'b', 'k', 'g',
           'c']  # should not plot more than 5 lines in this plot!
    lws = [2, 1, 1, 1, 1]
    lss = ['-', '--', ':', '.-', '-']
    styles = [{
        'lw': lws[i],
        'color': cls[i],
        'ls': lss[i]
    } for i in range(len(lognames['Resistivity']))]
    legends = [
        '{} [{}]'.format(x, templates['Resistivity']['unit'])
        for x in lognames['Resistivity']
    ]

    xlims = axis_log_plot(
        axes['res_ax'],
        depth[mask], [tb.logs[x].data[mask] for x in lognames['Resistivity']],
        limits,
        styles,
        yticks=False)
    header_plot(header_axes['res_ax'], xlims * len(legends), legends, styles)

    #
    # Rho
    try_these_log_types = ['Density', 'Neutron density']
    log_types = [
        x for x in try_these_log_types if (len(well.get_logs_of_type(x)) > 0)
    ]
    lognames = {
        ltype: well.get_logs_of_type(ltype)[0].name
        for ltype in log_types
    }
    # Replace the density with the one selected by log_table
    lognames['Density'] = log_table['Density']
    limits = [[templates[x]['min'], templates[x]['max']] for x in log_types]
    styles = [{
        'lw': templates[x]['line width'],
        'color': templates[x]['line color'],
        'ls': templates[x]['line style']
    } for x in log_types]
    legends = [
        '{} [{}]'.format(lognames[x], templates[x]['unit']) for x in log_types
    ]

    for xx in log_types:
        print(lognames[xx])

    xlims = axis_plot(axes['rho_ax'],
                      depth[mask],
                      [tb.logs[lognames[xx]].data[mask] for xx in log_types],
                      limits,
                      styles,
                      yticks=False)
    header_plot(header_axes['rho_ax'], xlims, legends, styles)

    #
    # CPI
    try_these_log_types = ['Saturation', 'Porosity', 'Volume']
    log_types = [
        x for x in try_these_log_types if (len(well.get_logs_of_type(x)) > 0)
    ]
    lognames = {
        ltype: well.get_logs_of_type(ltype)[0].name
        for ltype in log_types
    }
    limits = [[templates[x]['min'], templates[x]['max']] for x in log_types]
    styles = [{
        'lw': templates[x]['line width'],
        'color': templates[x]['line color'],
        'ls': templates[x]['line style']
    } for x in log_types]
    legends = [
        '{} [{}]'.format(lognames[x], templates[x]['unit']) for x in log_types
    ]

    if len(log_types) == 0:
        header_plot(header_axes['cpi_ax'],
                    None,
                    None,
                    None,
                    title='CPI is lacking')
        axis_plot(axes['cpi_ax'], None, None, None, None)
    else:
        xlims = axis_plot(
            axes['cpi_ax'],
            depth[mask],
            [tb.logs[lognames[xx]].data[mask] for xx in log_types],
            limits,
            styles,
            yticks=False)
    header_plot(header_axes['cpi_ax'], xlims, legends, styles)

    #
    # AI
    tt = ''
    if 'Density' in list(log_table.keys()):
        tt += log_table['Density']
        if 'P velocity' in list(log_table.keys()):
            ai = tb.logs[log_table['Density']].data * tb.logs[
                log_table['P velocity']].data
            tt += '*{}'.format(log_table['P velocity'])
        elif 'Sonic' in list(log_table.keys()):
            ai = tb.logs[log_table['Density']].data / tb.logs[
                log_table['Sonic']].data
            tt += '/{}'.format(log_table['Sonic'])
        else:
            ai = None
    else:
        ai = None
    if ai is not None:
        #styles = [{'lw': 1, 'color': 'k', 'ls': '--'}]
        log_types = ['AI']
        lognames = {'AI': 'AI'}
        limits = [[templates[x]['min'], templates[x]['max']]
                  for x in log_types]
        styles = [{
            'lw': templates[x]['line width'],
            'color': templates[x]['line color'],
            'ls': templates[x]['line style']
        } for x in log_types]
        legends = [
            '{} [{}]'.format(lognames[x], templates[x]['unit'])
            for x in log_types
        ]
        # TODO
        # Now we blindly assumes AI is in m/s g/cm3 Make this more robust
        xlims = axis_plot(axes['ai_ax'],
                          depth[mask], [ai[mask] / 1000.],
                          limits,
                          styles,
                          yticks=False)
        #header_plot(header_axes['ai_ax'], xlims, ['AI ({})'.format(tt)], styles)
        header_plot(header_axes['ai_ax'], xlims, legends, styles)
    else:
        header_plot(header_axes['ai_ax'],
                    None,
                    None,
                    None,
                    title='AI is lacking')
        axis_plot(axes['ai_ax'], None, None, None, None)

    #
    # Wiggles
    #t = np.arange(tdr[mask][0], tdr[mask][-1], 0.004)  # A uniformly sampled array of time steps, from A to B
    t = np.arange(
        0., np.nanmax(tdr),
        time_step)  # A uniformly sampled array of time steps, from 0 to 3
    #print(len(t))
    if 'P velocity' in list(log_table.keys()):
        vp_t = np.interp(x=t, xp=tdr, fp=tb.logs[log_table['P velocity']].data)
    elif 'Sonic' in list(log_table.keys()):
        vp_t = np.interp(x=t, xp=tdr, fp=1. / tb.logs[log_table['Sonic']].data)
    else:
        vp_t = None
    #print(len(vp_t))
    if 'S velocity' in list(log_table.keys()):
        vs_t = np.interp(x=t, xp=tdr, fp=tb.logs[log_table['S velocity']].data)
    elif 'Shear sonic' in list(log_table.keys()):
        vs_t = np.interp(x=t,
                         xp=tdr,
                         fp=1. / tb.logs[log_table['Shear sonic']].data)
    else:
        vs_t = None
    if 'Density' in list(log_table.keys()):
        rho_t = np.interp(x=t, xp=tdr, fp=tb.logs[log_table['Density']].data)
    else:
        rho_t = None

    if (vp_t is not None) and (vs_t is not None) and (rho_t is not None):
        reff = rp.reflectivity(vp_t,
                               None,
                               vs_t,
                               None,
                               rho_t,
                               None,
                               along_wiggle=True)
    else:
        reff = None

    if reff is not None:
        #print(len(reff(10)))
        #tw, w = ricker(_f=c_f, _length=duration, _dt=time_step)
        w = tta.ricker(duration, time_step, c_f)
        #print(len(w))

        # Compute the depth-time relation
        dtr = np.array([depth[find_nearest(tdr, tt)] for tt in t])
        #print(np.nanmin(dtr), np.nanmax(dtr))
        # Translate the mask to the time variable
        t_mask = np.ma.masked_inside(t[:-1], np.nanmin(tdr[mask]),
                                     np.nanmax(tdr[mask])).mask
        #wiggle_plot(axes['synt_ax'], t[:-1][t_mask], wig[t_mask], 10)

        header_plot(
            header_axes['synt_ax'],
            None,
            None,
            None,
            title='Incidence angle\nRicker f={:.0f} Hz, l={:.3f} s'.format(
                c_f, duration))
        for inc_a in range(0, 35, 5):
            wig = np.convolve(w, np.nan_to_num(reff(inc_a)), mode='same')
            wiggle_plot(axes['synt_ax'],
                        dtr[:-1][t_mask],
                        wig[t_mask],
                        inc_a,
                        scaling=scaling)
    else:
        header_plot(header_axes['synt_ax'],
                    None,
                    None,
                    None,
                    title='Refl. coeff. lacking')
        wiggle_plot(axes['synt_ax'], None, None, None)

    if savefig is not None:
        fig.savefig(savefig)
    #fig.tight_layout()
    plt.show()
예제 #6
0
def twolayer(vp0, vs0, rho0, vp1, vs1, rho1, angels=None):
    #from bruges.reflection import shuey2
    #from bruges.filters import ricker
    from rp.rp_core import reflectivity, intercept, gradient
    if angels is None:
        angels = [5, 15, 30]

    n_samples = 500
    interface = int(n_samples / 2)
    ang = np.arange(31)
    wavelet = ricker(.25, 0.001, 25)

    model_ip, model_vpvs, rc0, rc1, rc2 = (np.zeros(n_samples)
                                           for _ in range(5))
    model_z = np.arange(n_samples)
    model_ip[:interface] = vp0 * rho0
    model_ip[interface:] = vp1 * rho1
    model_vpvs[:interface] = np.true_divide(vp0, vs0)
    model_vpvs[interface:] = np.true_divide(vp1, vs1)

    #avo = shuey2(vp0, vs0, rho0, vp1, vs1, rho1, ang)
    _avo = reflectivity(vp0, vp1, vs0, vs1, rho0, rho1, version='ShueyAkiRich')
    avo = _avo(ang)
    rc0[interface] = avo[angels[0]]
    rc1[interface] = avo[angels[1]]
    rc2[interface] = avo[angels[2]]
    synt0 = np.convolve(rc0, wavelet, mode='same')
    synt1 = np.convolve(rc1, wavelet, mode='same')
    synt2 = np.convolve(rc2, wavelet, mode='same')
    clip = np.max(np.abs([synt0, synt1, synt2]))
    clip += clip * .2

    ic = intercept(vp0, vp1, rho0, rho1)
    gr = gradient(vp0, vp1, vs0, vs1, rho0, rho1)

    opz0 = {'color': 'b', 'linewidth': 4}
    opz1 = {'color': 'k', 'linewidth': 2}
    opz2 = {'linewidth': 0, 'alpha': 0.5}
    opz3 = {'color': 'tab:red', 'linewidth': 0, 'markersize': 8, 'marker': 'o'}
    opz4 = {
        'color': 'tab:blue',
        'linewidth': 0,
        'markersize': 8,
        'marker': 'o'
    }

    f = plt.subplots(figsize=(12, 10))
    ax0 = plt.subplot2grid((2, 12), (0, 0), colspan=3)  # ip
    ax1 = plt.subplot2grid((2, 12), (0, 3), colspan=3)  # vp/vs
    ax2 = plt.subplot2grid((2, 12), (0, 6), colspan=2)  # synthetic @ 0 deg
    ax3 = plt.subplot2grid((2, 12), (0, 8), colspan=2)  # synthetic @ 30 deg
    ax35 = plt.subplot2grid((2, 12), (0, 10), colspan=2)  # synthetic @ 30 deg
    ax4 = plt.subplot2grid((2, 12), (1, 0), colspan=5)  # avo curve
    ax6 = plt.subplot2grid((2, 12), (1, 7), colspan=5)  # avo curve

    ax0.plot(model_ip, model_z, **opz0)
    ax0.set_xlabel('IP')
    ax0.locator_params(axis='x', nbins=2)

    ax1.plot(model_vpvs, model_z, **opz0)
    ax1.set_xlabel('VP/VS')
    ax1.locator_params(axis='x', nbins=2)

    ax2.plot(synt0, model_z, **opz1)
    ax2.plot(synt0[interface], model_z[interface], **opz3)
    ax2.fill_betweenx(model_z,
                      0,
                      synt0,
                      where=synt0 > 0,
                      facecolor='black',
                      **opz2)
    ax2.set_xlim(-clip, clip)
    ax2.set_xlabel('angle={:.0f}'.format(angels[0]))
    ax2.locator_params(axis='x', nbins=2)

    ax3.plot(synt1, model_z, **opz1)
    ax3.plot(synt1[interface], model_z[interface], **opz3)
    ax3.fill_betweenx(model_z,
                      0,
                      synt1,
                      where=synt1 > 0,
                      facecolor='black',
                      **opz2)
    ax3.set_xlim(-clip, clip)
    ax3.set_xlabel('angle={:.0f}'.format(angels[1]))
    ax3.locator_params(axis='x', nbins=2)

    ax35.plot(synt2, model_z, **opz1)
    ax35.plot(synt2[interface], model_z[interface], **opz3)
    ax35.fill_betweenx(model_z,
                       0,
                       synt2,
                       where=synt2 > 0,
                       facecolor='black',
                       **opz2)
    ax35.set_xlim(-clip, clip)
    ax35.set_xlabel('angle={:.0f}'.format(angels[2]))
    ax35.locator_params(axis='x', nbins=2)

    ax4.plot(ang, avo, **opz0)
    ax4.axhline(0, color='k', lw=2)
    ax4.set_xlabel('angle of incidence')
    ax4.tick_params(which='major', labelsize=8)

    ax5 = ax4.twinx()
    color = 'tab:red'
    ax5.plot(angels, [s[interface] for s in [synt0, synt1, synt2]], **opz3)
    ax5.set_ylabel('Seismic amplitude')
    ax5.tick_params(axis='y', labelcolor=color, labelsize=8)

    # Calculate intercept & gradient based on the three "angle stacks"1G
    res = least_squares(
        mycf.residuals, [1., 1.],
        args=(np.array(angels),
              np.array([s[interface] for s in [synt0, synt1, synt2]])),
        kwargs={'target_function': straight_line})
    print('amp = {:.4f}*theta + {:.4f}'.format(*res.x))
    print(res.status)
    print(res.message)
    print(res.success)
    ax5.plot(ang, straight_line(ang, *res.x), c='tab:red')

    res2 = least_squares(
        mycf.residuals, [1., 1.],
        args=(np.sin(np.array(angels) * np.pi / 180.)**2,
              np.array([s[interface] for s in [synt0, synt1, synt2]])),
        kwargs={'target_function': straight_line})
    print('amp = {:.4f}*theta + {:.4f}'.format(*res2.x))
    print(res2.status)
    print(res2.message)
    print(res2.success)

    ax6.plot(ic, gr, **opz4)
    ax6.plot(*res2.x[::-1], **opz3)
    ax6.set_xlabel('Intercept')
    ax6.set_ylabel('Gradient')

    for aa in [ax0, ax1, ax2, ax3, ax35]:
        aa.set_ylim(350, 150)
        aa.tick_params(which='major', labelsize=8)
        aa.set_yticklabels([])

    plt.subplots_adjust(wspace=.8, left=0.05, right=0.95)