예제 #1
0
def _get_distribution_parameters(x, y, z, px, py, pz, q, n_slices, len_slice):
    a_x, b_x, g_x = bd.twiss_parameters(x, px, pz, py, w=q)
    a_y, b_y, g_y = bd.twiss_parameters(y, py, pz, px, w=q)
    ene = bd.mean_energy(px, py, pz, w=q)
    ene_sp = bd.relative_rms_energy_spread(px, py, pz, w=q)
    ene_sp_slice_params = bd.relative_rms_slice_energy_spread(
        z, px, py, pz, w=q, n_slices=n_slices, len_slice=len_slice)
    ene_sp_sl, sl_w, sl_e, ene_sp_sl_avg = ene_sp_slice_params
    em_x = bd.normalized_transverse_rms_emittance(x, px, py, pz, w=q)
    em_y = bd.normalized_transverse_rms_emittance(y, py, px, pz, w=q)
    emitt_x_slice_params = bd.normalized_transverse_rms_slice_emittance(
        z, x, px, py, pz, w=q, n_slices=n_slices, len_slice=len_slice)
    em_x_sl, sl_w, sl_e, em_x_sl_avg = emitt_x_slice_params
    emitt_y_slice_params = bd.normalized_transverse_rms_slice_emittance(
        z, y, py, px, pz, w=q, n_slices=n_slices, len_slice=len_slice)
    em_y_sl, sl_w, sl_e, em_y_sl_avg = emitt_y_slice_params
    s_z = bd.rms_length(z, w=q)
    s_x = bd.rms_size(x, w=q)
    s_y = bd.rms_size(y, w=q)
    x_avg = np.average(x, weights=q)
    y_avg = np.average(y, weights=q)
    px_avg = np.average(px, weights=q)
    py_avg = np.average(py, weights=q)
    theta_x = px_avg / ene
    theta_y = py_avg / ene
    i_peak = bd.peak_current(z, q, n_slices=n_slices, len_slice=len_slice)
    return (theta_x, theta_y, x_avg, y_avg, s_x, s_y, a_x, a_y, b_x, b_y, g_x,
            g_y, em_x, em_y, em_x_sl_avg, em_y_sl_avg, ene, ene_sp,
            ene_sp_sl_avg, i_peak, s_z)
예제 #2
0
def phase_space_overview(x, y, z, px, py, pz, q):
    em_x = bd.normalized_transverse_rms_emittance(x, px, w=q) * 1e6
    em_y = bd.normalized_transverse_rms_emittance(y, py, w=q) * 1e6
    a_x, b_x, g_x = bd.twiss_parameters(x, px, pz, w=q)
    a_y, b_y, g_y = bd.twiss_parameters(y, py, pz, w=q)
    s_x = bd.rms_size(x, w=q)
    s_y = bd.rms_size(y, w=q)
    em_l = bd.longitudinal_rms_emittance(z, px, py, pz, w=q) * 1e6
    dz = z - np.average(z, weights=q)
    s_z = bd.rms_length(z, w=q)
    s_g = bd.relative_rms_energy_spread(pz, py, pz, w=q)
    s_g_sl, w_sl, sl_ed, s_g_sl_av = bd.relative_rms_slice_energy_spread(
        z, px, py, pz, w=q, n_slices=10)
    c_prof, _ = bd.current_profile(z, q, n_slices=50)
    c_peak = max(abs(c_prof))/1e3  # kA
    # s_g_sl_c = s_g_sl[int(len(s_g_sl)/2)]

    # make plot
    plt.figure(figsize=(8, 3))
    with plt.rc_context(aptools_rc_params):
        # x - px
        ax_1 = plt.subplot(131)
        scatter_histogram(x*1e6, px)
        plt.xlabel("x [$\\mu m$]")
        plt.ylabel("$p_x \\ \\mathrm{[m_ec^2/e]}$")
        plt.text(0.1, 0.9, '$\\epsilon_{n,x} = $'
                 + '{}'.format(np.around(em_x, 3))
                 + '$\\ \\mathrm{\\pi \\ \\mu m \\ rad}$',
                 transform=ax_1.transAxes, fontsize=8)
        plt.text(0.1, 0.8, '$\\beta_{x} = $' + '{}'.format(np.around(b_x, 3))
                 + 'm', transform=ax_1.transAxes, fontsize=8)
        plt.text(0.1, 0.7, '$\\alpha_{x} = $' + '{}'.format(np.around(a_x, 3)),
                 transform=ax_1.transAxes, fontsize=8)
        plt.text(0.1, 0.6, '$\\sigma_{x} = $'
                 + '{}'.format(np.around(s_x*1e6, 3))
                 + '$\\ \\mathrm{\\mu m}$', transform=ax_1.transAxes,
                 fontsize=8)
        # y - py
        ax_2 = plt.subplot(132)
        scatter_histogram(y * 1e6, py)
        plt.xlabel("y [$\\mu m$]")
        plt.ylabel("$p_y \\ \\mathrm{[m_ec^2/e]}$")
        plt.text(0.1, 0.9, '$\\epsilon_{n,y} = $'
                 + '{}'.format(np.around(em_y, 3))
                 + '$\\ \\mathrm{\\pi \\ \\mu m \\ rad}$',
                 transform=ax_2.transAxes, fontsize=8)
        plt.text(0.1, 0.8, '$\\beta_{y} = $' + '{}'.format(np.around(b_y, 3))
                 + 'm', transform=ax_2.transAxes, fontsize=8)
        plt.text(0.1, 0.7, '$\\alpha_{y} = $' + '{}'.format(np.around(a_y, 3)),
                 transform=ax_2.transAxes, fontsize=8)
        plt.text(0.1, 0.6, '$\\sigma_{y} = $'
                 + '{}'.format(np.around(s_y*1e6, 3))
                 + '$\\ \\mathrm{\\mu m}$', transform=ax_2.transAxes,
                 fontsize=8)
        # z - pz
        ax_3 = plt.subplot(133)
        scatter_histogram(dz / ct.c * 1e15, pz)
        plt.xlabel("$\\Delta z$ [fs]")
        plt.ylabel("$p_z \\ \\mathrm{[m_ec^2/e]}$")
        plt.text(0.1, 0.9, '$\\epsilon_{L} = $'
                 + '{}'.format(np.around(em_l, 3))
                 + '$\\ \\mathrm{\\pi \\ \\mu m}$', transform=ax_3.transAxes,
                 fontsize=8)
        plt.text(0.1, 0.8, '$\\sigma_\\gamma/\\gamma=$'
                 + '{}'.format(np.around(s_g*1e2, 3)) + '$\\%$',
                 transform=ax_3.transAxes, fontsize=8)
        plt.text(0.1, 0.7, '$\\sigma^s_\\gamma/\\gamma=$'
                 + '{}'.format(np.around(s_g_sl_av*1e2, 3)) + '$\\%$',
                 transform=ax_3.transAxes, fontsize=8)
        plt.text(0.1, 0.6, '$\\sigma_z=$'
                 + '{}'.format(np.around(s_z/ct.c*1e15, 3)) + ' fs',
                 transform=ax_3.transAxes, fontsize=8)
        plt.text(0.1, 0.5, '$I_{peak}=$'
                 + '{}'.format(np.around(c_peak, 2)) + ' kA',
                 transform=ax_3.transAxes, fontsize=8)
        plt.tight_layout()
    plt.show()
예제 #3
0
def slice_analysis(x, y, z, px, py, pz, q, n_slices=50, len_slice=None,
                   ene_bins=50, left=0.125, right=0.875, top=0.98, bottom=0.13,
                   add_labels=False, include_twiss=False, fig=None,
                   rasterized_scatter=None, show=True):
    # analyze beam
    current_prof, z_edges = bd.current_profile(z, q, n_slices=n_slices,
                                               len_slice=len_slice)
    ene_spectrum, ene_spec_edgs = bd.energy_spectrum(px, py, pz, w=q,
                                                     bins=ene_bins)
    slice_ene, *_ = bd.energy_profile(
        z, px, py, pz, w=q, n_slices=n_slices, len_slice=len_slice)
    slice_ene_sp, *_ = bd.relative_rms_slice_energy_spread(
        z, px, py, pz, w=q, n_slices=n_slices, len_slice=len_slice)
    sl_tw, sl_w, *_ = bd.slice_twiss_parameters(
        z, x, px, pz, w=q, n_slices=n_slices, len_slice=len_slice)
    alpha_x, *_ = get_only_statistically_relevant_slices(
        sl_tw[0], sl_w, replace_with_nans=True)
    beta_x, *_ = get_only_statistically_relevant_slices(
        sl_tw[1], sl_w, replace_with_nans=True)
    sl_tw, *_ = bd.slice_twiss_parameters(
        z, y, py, pz, w=q, n_slices=n_slices, len_slice=len_slice)
    alpha_y, *_ = get_only_statistically_relevant_slices(
        sl_tw[0], sl_w, replace_with_nans=True)
    beta_y, *_ = get_only_statistically_relevant_slices(
        sl_tw[1], sl_w, replace_with_nans=True)
    slice_em_x, *_ = bd.normalized_transverse_rms_slice_emittance(
        z, x, px, w=q, n_slices=n_slices, len_slice=len_slice)
    slice_em_y, *_ = bd.normalized_transverse_rms_slice_emittance(
        z, y, py, w=q, n_slices=n_slices, len_slice=len_slice)
    s_z = bd.rms_length(z, w=q)
    len_fwhm = bd.fwhm_length(z, q, n_slices=n_slices, len_slice=len_slice)
    ene_sp_tot = bd.relative_rms_energy_spread(px, py, pz, w=q)

    # perform operations
    gamma = np.sqrt(1 + px**2 + py**2 + pz**2)
    ene = gamma * ct.m_e*ct.c**2/ct.e * 1e-9  # GeV
    z_center = np.average(z, weights=q)
    dz = z_edges[1] - z_edges[0]
    slice_z = (z_edges[1:] - dz/2 - z_center) * 1e6  # micron
    current_prof = np.abs(current_prof) * 1e-3  # kA
    peak_current = np.nanmax(current_prof)
    s_t = s_z * 1e15/ct.c
    len_fwhm *= 1e15/ct.c  # fs
    slice_ene *= ct.m_e*ct.c**2/ct.e * 1e-9  # GeV
    ene_spec_edgs = ene_spec_edgs[:-1] + (ene_spec_edgs[1]-ene_spec_edgs[0])/2
    ene_spec_edgs *= ct.m_e*ct.c**2/ct.e * 1e-9  # GeV
    slice_ene_sp *= 1e2  # %
    ene_sp_tot *= 1e2  # %
    slice_em_x *= 1e6  # micron
    slice_em_y *= 1e6  # micron
    max_beta = np.nanmax(beta_x)
    if max_beta <= 0.1:
        beta_units = 'mm'
        beta_x *= 1e3
        beta_y *= 1e3
    else:
        beta_units = 'm'
    max_ene = np.nanmax(ene)
    if max_ene <= 1:
        ene_units = 'MeV'
        ene *= 1e3
        ene_spec_edgs *= 1e3
    else:
        ene_units = 'GeV'
    ene_mean = np.average(ene, weights=q)

    # make plot
    if include_twiss:
        nrows = 3
        hr = [2.5, 1, 1]
        fh = 3.3
    else:
        nrows = 2
        hr = [2.5, 1]
        fh = 2.5
    if fig is None:
        fig = plt.figure(figsize=(4, fh))
    gs = gridspec.GridSpec(nrows, 2, height_ratios=hr,
                           width_ratios=[1, 0.02], hspace=0.1, wspace=0.05,
                           figure=fig, left=left, right=right,
                           top=top, bottom=bottom)
    leg_frac = 0.25  # space to reserve for legend

    with plt.rc_context(aptools_rc_params):
        ax_or = plt.subplot(gs[0])
        pscatt = scatter_histogram((z-z_center)*1e6, ene, bins=300,
                                   weights=np.abs(q)*1e15,
                                   rasterized=rasterized_scatter)
        plt.ylabel('Energy [{}]'.format(ene_units))
        plt.tick_params(axis='x', which='both', labelbottom=False)
        params_text = ('$\\langle E \\rangle = '
                       + '{:0.1f}$ {}\n'.format(ene_mean, ene_units)
                       + '$\\sigma_\\mathrm{E,rel}='
                       + '{:0.1f}$ %\n'.format(ene_sp_tot)
                       + '$I_\\mathrm{peak}='
                       + '{:0.1f}$ kA\n'.format(peak_current)
                       + '$\\sigma_t='
                       + '{:0.1f}$ fs'.format(s_t))
        plt.text(0.98, 0.95, params_text, transform=ax_or.transAxes,
                 fontsize=6, horizontalalignment='right',
                 verticalalignment='top')
        if add_labels:
            plt.text(0.03, 0.05, '(a)', transform=ax_or.transAxes, fontsize=6,
                     horizontalalignment='left', verticalalignment='bottom')

        xlim = list(plt.xlim())
        xlim[0] -= (xlim[1] - xlim[0])/8
        xlim[1] += (xlim[1] - xlim[0])/3
        plt.xlim(xlim)

        ylim = list(plt.ylim())
        ylim[0] -= (ylim[1] - ylim[0])/3
        plt.ylim(ylim)

        # current profile plot
        z_or = ax_or.get_zorder()
        pos = list(ax_or.get_position().bounds)
        pos[3] /= 5
        ax_or.patch.set_alpha(0)
        ax = fig.add_axes(pos)
        ax.set_zorder(z_or-1)
        plt.plot(slice_z, current_prof, c='k', lw=0.5, alpha=0.5)
        plt.fill_between(slice_z, current_prof, facecolor='tab:gray',
                         alpha=0.3)
        ax.spines['left'].set_position('zero')
        ax.spines['left'].set_color('tab:grey')
        ax.tick_params(axis='y', colors='tab:grey', labelsize=6,
                       direction="in", pad=-4)
        ax.spines['right'].set_color('none')
        ax.spines['top'].set_color('none')
        ax.yaxis.set_ticks_position('left')
        ax.xaxis.set_ticks_position('bottom')
        plt.tick_params(axis='x', which='both', labelbottom=False)
        for label in ax.yaxis.get_ticklabels():
            label.set_horizontalalignment('left')
            label.set_verticalalignment('bottom')
        plt.xlim(xlim)
        ylim_c = list(plt.ylim())
        ylim_c[0] = 0
        plt.ylim(ylim_c)
        plt.ylabel('I [kA]', color='tab:gray', fontsize=6)

        # energy profile plot
        pos = list(ax_or.get_position().bounds)
        pos[2] /= 8
        ax = fig.add_axes(pos)
        ax.set_zorder(z_or-1)
        plt.plot(ene_spectrum, ene_spec_edgs, c='k', lw=0.5, alpha=0.5)
        plt.fill_betweenx(ene_spec_edgs, ene_spectrum, facecolor='tab:gray',
                          alpha=0.3)
        plt.gca().axis('off')
        plt.ylim(ylim)
        xlim_e = list(plt.xlim())
        xlim_e[0] = 0
        plt.xlim(xlim_e)

        # colorbar
        ax = plt.subplot(gs[1])
        matplotlib.colorbar.Colorbar(ax, pscatt, label='Q [fC]')

        # slice parameters plot
        plt.subplot(gs[2])
        l1 = plt.plot(slice_z, slice_ene_sp, lw=1, c='tab:green',
                      label='$\\sigma_\\gamma/\\gamma$')
        plt.ylabel('$\\sigma_\\gamma/\\gamma$ [%]')
        if include_twiss:
            plt.tick_params(axis='x', which='both', labelbottom=False)
        else:
            plt.xlabel('$\\Delta z \\ [\\mathrm{\\mu m}]$')
        # make room for legend
        # ylim = list(plt.ylim())
        # ylim[1] += (ylim[1] - ylim[0]) * leg_frac
        plt.xlim(xlim)
        # plt.ylim(ylim)

        ax = plt.twinx()
        l2 = plt.plot(slice_z, slice_em_x, lw=1, c='tab:blue',
                      label='$\\epsilon_{n,x}$')
        l3 = plt.plot(slice_z, slice_em_y, lw=1, c='tab:orange',
                      label='$\\epsilon_{n,y}$')
        plt.ylabel('$\\epsilon_{n} \\ [\\mathrm{\\mu m}]$')
        # make room for legend
        # ylim = list(plt.ylim())
        # ylim[1] += (ylim[1] - ylim[0]) * leg_frac
        # plt.ylim(ylim)
        lines = l1 + l2 + l3
        labels = [line.get_label() for line in lines]
        plt.legend(lines, labels, fontsize=6, frameon=False,
                   loc='center right', borderaxespad=0.3)
        if add_labels:
            plt.text(0.03, 0.05, '(b)', transform=plt.gca().transAxes,
                     fontsize=6, horizontalalignment='left',
                     verticalalignment='bottom')

        if include_twiss:
            plt.subplot(gs[4])
            l1 = plt.plot(slice_z, beta_x, lw=1, c='tab:blue',
                          label='$\\beta_x$')
            l2 = plt.plot(slice_z, beta_y, lw=1, c='tab:orange',
                          label='$\\beta_y$')
            plt.xlabel('$\\Delta z \\ [\\mathrm{\\mu m}]$')
            plt.ylabel('$\\beta$ [{}]'.format(beta_units))
            # make room for legend
            ylim = list(plt.ylim())
            ylim[1] += (ylim[1] - ylim[0]) * leg_frac
            plt.ylim(ylim)
            plt.xlim(xlim)
            plt.twinx()
            l3 = plt.plot(slice_z, alpha_x, lw=1, c='tab:blue', ls='--',
                          label='$\\alpha_x$')
            l4 = plt.plot(slice_z, alpha_y, lw=1, c='tab:orange', ls='--',
                          label='$\\alpha_y$')
            lines = l1 + l2 + l3 + l4
            labels = [line.get_label() for line in lines]
            # make room for legend
            # ylim = list(plt.ylim())
            # ylim[1] += (ylim[1] - ylim[0]) * leg_frac
            # plt.ylim(ylim)
            plt.legend(lines, labels, fontsize=6, ncol=1, frameon=False,
                       loc='center right', borderaxespad=0.3,
                       labelspacing=0.20)
            if add_labels:
                plt.text(0.03, 0.05, '(c)', transform=plt.gca().transAxes,
                         fontsize=6, horizontalalignment='left',
                         verticalalignment='bottom')
            plt.ylabel('$\\alpha$')
    if show:
        plt.show()
예제 #4
0
def analyze_data(beam_list):
    print("Running data analysis...   ")
    l = len(beam_list)
    x_part = np.zeros(l)
    g_part = np.zeros(l)
    px_part = np.zeros(l)
    a_x = np.zeros(l)
    a_y = np.zeros(l)
    b_x = np.zeros(l)
    b_y = np.zeros(l)
    g_x = np.zeros(l)
    g_y = np.zeros(l)
    ene = np.zeros(l)
    dist = np.zeros(l)
    chirp = np.zeros(l)
    ene_sp = np.zeros(l)
    ene_sp_sl = np.zeros(l)
    emitt = np.zeros(l)
    em_x = np.zeros(l)
    em_y = np.zeros(l)
    em_sl_x = np.zeros(l)
    em_sl_y = np.zeros(l)
    emitt_3 = np.zeros(l)
    dx = np.zeros(l)
    sx = np.zeros(l)
    sy = np.zeros(l)
    x_centroid = np.zeros(l)
    y_centroid = np.zeros(l)
    px_centroid = np.zeros(l)
    py_centroid = np.zeros(l)
    disp_x = np.zeros(l)
    sz = np.zeros(l)
    for i, beam in enumerate(beam_list):
        dist[i] = beam.prop_distance
        a_x[i], b_x[i], g_x[i] = bd.twiss_parameters(beam.x,
                                                     beam.px,
                                                     beam.pz,
                                                     beam.py,
                                                     w=beam.q)
        a_y[i], b_y[i], g_y[i] = bd.twiss_parameters(beam.y,
                                                     beam.py,
                                                     beam.pz,
                                                     beam.px,
                                                     w=beam.q)
        ene[i] = bd.mean_energy(beam.px, beam.py, beam.pz, w=beam.q)
        ene_sp[i] = bd.relative_rms_energy_spread(beam.px,
                                                  beam.py,
                                                  beam.pz,
                                                  w=beam.q)
        enespls, sl_w, _ = bd.relative_rms_slice_energy_spread(
            beam.xi, beam.px, beam.py, beam.pz, w=beam.q, len_slice=0.1e-6)
        ene_sp_sl[i] = np.average(enespls, weights=sl_w)
        em_x[i] = bd.normalized_transverse_rms_emittance(beam.x,
                                                         beam.px,
                                                         beam.py,
                                                         beam.pz,
                                                         w=beam.q)
        em_y[i] = bd.normalized_transverse_rms_emittance(beam.y,
                                                         beam.py,
                                                         beam.px,
                                                         beam.pz,
                                                         w=beam.q)
        emsx, sl_w, _ = bd.normalized_transverse_rms_slice_emittance(
            beam.xi,
            beam.x,
            beam.px,
            beam.py,
            beam.pz,
            w=beam.q,
            len_slice=0.1e-6)
        em_sl_x[i] = np.average(emsx, weights=sl_w)
        emsy, sl_w, _ = bd.normalized_transverse_rms_slice_emittance(
            beam.xi,
            beam.y,
            beam.py,
            beam.px,
            beam.pz,
            w=beam.q,
            len_slice=0.1e-6)
        em_sl_y[i] = np.average(emsy, weights=sl_w)
        sz[i] = bd.rms_length(beam.xi, w=beam.q)
        sx[i] = bd.rms_size(beam.x, w=beam.q)
        sy[i] = bd.rms_size(beam.y, w=beam.q)
        x_centroid[i] = np.average(beam.x, weights=beam.q)
        y_centroid[i] = np.average(beam.y, weights=beam.q)
        px_centroid[i] = np.average(beam.px, weights=beam.q)
        py_centroid[i] = np.average(beam.py, weights=beam.q)
        disp_x[i] = bd.dispersion(beam.x, beam.px, beam.pz, beam.py, w=beam.q)
    plt.figure(1)
    plt.subplot(341)
    plt.semilogy(dist * 1e3, b_x * 1e3)
    plt.semilogy(dist * 1e3, b_y * 1e3)
    plt.xlabel("z [mm]")
    plt.ylabel("$\\beta_x$ [mm]")
    plt.subplot(342)
    plt.plot(dist * 1e3, a_x)
    plt.plot(dist * 1e3, a_y)
    plt.xlabel("z [mm]")
    plt.ylabel("$\\alpha_x$")
    plt.subplot(343)
    plt.plot(dist * 1e3, g_x)
    plt.plot(dist * 1e3, g_y)
    plt.xlabel("z [mm]")
    plt.ylabel("$\\gamma_x$")
    plt.subplot(344)
    plt.plot(dist * 1e3, ene)
    plt.xlabel("z [mm]")
    plt.ylabel("$\\gamma$")
    plt.subplot(345)
    plt.semilogy(dist * 1e3, ene_sp * 100)
    plt.semilogy(dist * 1e3, ene_sp_sl * 100)
    plt.xlabel("z [mm]")
    plt.ylabel("$\\frac{\\Delta \\gamma_z}{\\gamma}$ [%]")
    plt.subplot(346)
    plt.plot(dist * 1e3, em_x * 1e6)
    plt.plot(dist * 1e3, em_y * 1e6)
    plt.xlabel("z [mm]")
    plt.ylabel("$\\epsilon_{nx}$ [$\\mu$m]")
    plt.subplot(347)
    plt.plot(dist * 1e3, sx * 1e6)
    plt.plot(dist * 1e3, sy * 1e6)
    plt.xlabel("z [mm]")
    plt.ylabel("$\\sigma_{x,y}$ [$\\mu$m]")
    plt.subplot(348)
    plt.plot(dist * 1e3, sz / ct.c * 1e15)
    plt.xlabel("z [mm]")
    plt.ylabel("$\\sigma_z$ [fs]")
    plt.subplot(349)
    plt.plot(dist * 1e3, x_centroid * 1e6)
    plt.plot(dist * 1e3, y_centroid * 1e6)
    plt.xlabel("z [mm]")
    plt.ylabel("bunch centroid [$\\mu m$]")
    plt.subplot(3, 4, 10)
    plt.plot(dist * 1e3, px_centroid / ene * 1e6)
    plt.plot(dist * 1e3, py_centroid / ene * 1e6)
    plt.xlabel("z [mm]")
    plt.ylabel("pointing angle [$\\mu rad$]")
    plt.subplot(3, 4, 11)
    plt.plot(dist * 1e3, disp_x)
    plt.xlabel("z [mm]")
    plt.ylabel("$D_x$ [m]")
    plt.tight_layout()
    plt.show()
예제 #5
0
def lon_phase_space(x,
                    y,
                    z,
                    px,
                    py,
                    pz,
                    q,
                    n_slices=50,
                    len_slice=None,
                    ene_bins=50,
                    xlim=None,
                    ylim=None,
                    show_text=True,
                    x_proj=True,
                    y_proj=True,
                    cbar=True,
                    left=0.125,
                    right=0.875,
                    top=0.98,
                    bottom=0.13,
                    fig=None,
                    rasterized_scatter=None,
                    show=True):
    # analyze beam
    current_prof, z_edges = bd.current_profile(z,
                                               q,
                                               n_slices=n_slices,
                                               len_slice=len_slice)
    ene_spectrum, ene_spec_edgs = bd.energy_spectrum(px,
                                                     py,
                                                     pz,
                                                     w=q,
                                                     bins=ene_bins)
    s_z = bd.rms_length(z, w=q)
    len_fwhm = bd.fwhm_length(z, q, n_slices=n_slices, len_slice=len_slice)
    ene_sp_tot = bd.relative_rms_energy_spread(px, py, pz, w=q)

    # perform operations
    gamma = np.sqrt(1 + px**2 + py**2 + pz**2)
    ene = gamma * ct.m_e * ct.c**2 / ct.e * 1e-9  # GeV
    z_center = np.average(z, weights=q)
    dz = z_edges[1] - z_edges[0]
    slice_z = (z_edges[1:] - dz / 2 - z_center) * 1e6  # micron
    current_prof = np.abs(current_prof) * 1e-3  # kA
    peak_current = np.nanmax(current_prof)
    s_t = s_z * 1e15 / ct.c
    len_fwhm *= 1e15 / ct.c  # fs
    ene_spec_edgs = ene_spec_edgs[:-1] + (ene_spec_edgs[1] -
                                          ene_spec_edgs[0]) / 2
    ene_spec_edgs *= ct.m_e * ct.c**2 / ct.e * 1e-9  # GeV
    ene_sp_tot *= 1e2  # %
    max_ene = np.nanmax(ene)
    if max_ene <= 1:
        ene_units = 'MeV'
        ene *= 1e3
        ene_spec_edgs *= 1e3
    else:
        ene_units = 'GeV'
    ene_mean = np.average(ene, weights=q)

    # make plot
    if fig is None:
        fig = plt.figure(figsize=(4, 2.5))
    if cbar:
        gs = gridspec.GridSpec(1,
                               2,
                               width_ratios=[1, 0.02],
                               hspace=0.1,
                               wspace=0.05,
                               figure=fig,
                               left=left,
                               right=right,
                               top=top,
                               bottom=bottom)
    else:
        gs = gridspec.GridSpec(1,
                               1,
                               figure=fig,
                               left=left,
                               right=right,
                               top=top,
                               bottom=bottom)
    with plt.rc_context(aptools_rc_params):
        ax_or = plt.subplot(gs[0])
        pscatt = scatter_histogram((z - z_center) * 1e6,
                                   ene,
                                   bins=300,
                                   weights=np.abs(q) * 1e15,
                                   rasterized=rasterized_scatter)
        plt.xlabel('$\\Delta z \\ [\\mathrm{\\mu m}]$')
        plt.ylabel('Energy [{}]'.format(ene_units))
        if show_text:
            params_text = ('$\\langle E \\rangle = ' +
                           '{:0.1f}$ {}\n'.format(ene_mean, ene_units) +
                           '$\\sigma_\\mathrm{E,rel}=' +
                           '{:0.1f}$ %\n'.format(ene_sp_tot) +
                           '$I_\\mathrm{peak}=' +
                           '{:0.1f}$ kA\n'.format(peak_current) +
                           '$\\sigma_t=' + '{:0.1f}$ fs'.format(s_t))
            plt.text(0.98,
                     0.95,
                     params_text,
                     transform=ax_or.transAxes,
                     fontsize=6,
                     horizontalalignment='right',
                     verticalalignment='top')

        if xlim is not None:
            plt.xlim(xlim)
        else:
            xlim = list(plt.xlim())
            if y_proj:
                xlim[0] -= (xlim[1] - xlim[0]) / 8
            if show_text:
                xlim[1] += (xlim[1] - xlim[0]) / 3
            plt.xlim(xlim)
        if ylim is not None:
            plt.ylim(ylim)
        else:
            ylim = list(plt.ylim())
            if x_proj:
                ylim[0] -= (ylim[1] - ylim[0]) / 3
            plt.ylim(ylim)

        # current profile plot
        if x_proj:
            z_or = ax_or.get_zorder()
            pos = list(ax_or.get_position().bounds)
            pos[3] /= 5
            ax_or.patch.set_alpha(0)
            ax = fig.add_axes(pos)
            ax.set_zorder(z_or - 1)
            plt.plot(slice_z, current_prof, c='k', lw=0.5, alpha=0.5)
            plt.fill_between(slice_z,
                             current_prof,
                             facecolor='tab:gray',
                             alpha=0.3)
            ax.spines['left'].set_position('zero')
            ax.spines['left'].set_color('tab:grey')
            ax.tick_params(axis='y',
                           colors='tab:grey',
                           labelsize=6,
                           direction="in",
                           pad=-4)
            ax.spines['right'].set_color('none')
            ax.spines['top'].set_color('none')
            ax.yaxis.set_ticks_position('left')
            ax.xaxis.set_ticks_position('bottom')
            plt.tick_params(axis='x', which='both', labelbottom=False)
            for label in ax.yaxis.get_ticklabels():
                label.set_horizontalalignment('left')
                label.set_verticalalignment('bottom')
            plt.xlim(xlim)
            ylim_c = list(plt.ylim())
            ylim_c[0] = 0
            plt.ylim(ylim_c)
            plt.ylabel('I [kA]', color='tab:gray', fontsize=6)

        # energy profile plot
        if y_proj:
            z_or = ax_or.get_zorder()
            pos = list(ax_or.get_position().bounds)
            pos[2] /= 8
            ax_or.patch.set_alpha(0)
            ax = fig.add_axes(pos)
            ax.set_zorder(z_or - 1)
            plt.plot(ene_spectrum, ene_spec_edgs, c='k', lw=0.5, alpha=0.5)
            plt.fill_betweenx(ene_spec_edgs,
                              ene_spectrum,
                              facecolor='tab:gray',
                              alpha=0.3)
            plt.gca().axis('off')
            plt.ylim(ylim)
            xlim_e = list(plt.xlim())
            xlim_e[0] = 0
            plt.xlim(xlim_e)

        # colorbar
        if cbar:
            ax = plt.subplot(gs[1])
            matplotlib.colorbar.Colorbar(ax, pscatt, label='Q [fC]')

    if show:
        plt.show()