예제 #1
0
파일: plot.py 프로젝트: fjarri/thesis
def spinecho_single_run_population(fname):
    with open(get_path(__file__, 'simulations/echo_wigner_single_run.json')) as f:
        wig = json.load(f)

    wig_t = numpy.array(wig['times'])

    wig_N1 = numpy.array(wig['N1'])
    wig_N2 = numpy.array(wig['N2'])

    fig = mplh.figure(width=0.5)
    s = fig.add_subplot(111,
        xlabel='$t$ (s)',
        ylabel='$N$')

    # Pure Wigner
    s.plot(wig_t, wig_N1, color=mplh.color.f.blue.main,
        linestyle='-', dashes=mplh.dash['-'])
    s.plot(wig_t, wig_N2, color=mplh.color.f.red.main,
        linestyle='--', dashes=mplh.dash['--'])

    s.set_xlim((0, 1.3))
    s.set_ylim((0, 55000. / 2))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    s.text(0.1, 2500, 'spin echo sequence')
    s.text(1., 4000, '$\\vert 1 \\rangle$')
    s.text(1., 13000, '$\\vert 2 \\rangle$')

    fig.text(0.01, 0.92, '(b)', fontweight='bold')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #2
0
파일: plot.py 프로젝트: fjarri/thesis
def spinecho_short(fname):
    with open(get_path(__file__, 'experimental/echo_experimental.json')) as f:
        vis_exp = json.load(f)
    with open(get_path(__file__, 'simulations/echo_gpe.json')) as f:
        gpe = json.load(f)
    with open(get_path(__file__, 'simulations/echo_wigner.json')) as f:
        wig = json.load(f)
    with open(get_path(__file__, 'simulations/echo_wigner_varied_pulse.json')) as f:
        wig_tech = json.load(f)

    t_exp = numpy.array(vis_exp['xarray'])
    v_exp = numpy.array(vis_exp['yarray'])
    v_exp_err = numpy.array(vis_exp['yerrors'])

    gpe_t = numpy.array(gpe['times'])
    gpe_v = numpy.array(gpe['visibility'])
    wig_t = numpy.array(wig['times'])
    wig_v = numpy.array(wig['visibility'])

    wig_tech_t = numpy.array(wig_tech['times'])
    wig_tech_v = numpy.array(wig_tech['est_visibility'])

    fig = mplh.figure(width=0.75)
    s = fig.add_subplot(111,
        xlabel='$t$ (s)',
        ylabel='$\\mathcal{V}$')
    s.errorbar(t_exp, v_exp, yerr=v_exp_err, color='k', linestyle='none',
        capsize=1.5)

    # GPE
    s.plot(gpe_t, gpe_v, color=mplh.color.f.green.main,
        linestyle=':', dashes=mplh.dash[':'])
    # Pure Wigner
    s.plot(wig_t, wig_v, color=mplh.color.f.red.main,
        linestyle='--', dashes=mplh.dash['--'])
    # Wigner + technical noise
    s.plot(wig_tech_t, wig_tech_v, color=mplh.color.f.blue.main,
        linestyle='-', dashes=mplh.dash['-'])

    s.set_xlim((0, 1.8))
    s.set_ylim((0, 1.))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    s.plot([0.05, 0.16], [0.23, 0.23], color=mplh.color.f.green.main, linestyle=':', dashes=mplh.dash[':'])
    s.text(0.18, 0.22, 'mean-field')
    s.plot([0.05, 0.16], [0.15, 0.15], color=mplh.color.f.red.main, linestyle='--', dashes=mplh.dash['--'])
    s.text(0.18, 0.14, 'Wigner')
    s.plot([0.05, 0.16], [0.07, 0.07], color=mplh.color.f.blue.main, linestyle='-', dashes=mplh.dash['-'])
    s.text(0.18, 0.06, 'Wigner + tech. noise')

    s.errorbar([0.88], [0.07], yerr=[0.02], color='k', linestyle='none', capsize=1.5)
    s.text(0.92, 0.06, 'experiment')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #3
0
파일: plot.py 프로젝트: fjarri/thesis
def illustration_noise(fname, t_ms):
    with open(get_path(__file__, 'ramsey_sim_phnoise_' + str(t_ms) + 'ms.json')) as f:
        meas = json.load(f)

    Pz = numpy.array(meas['Pz'])
    phis = numpy.array(meas['phis'])
    est_phase = numpy.array(meas['est_phase'])
    est_phnoise = numpy.array(meas['est_phnoise'])
    est_amp = numpy.array(meas['est_amp'])

    fig = mplh.figure(width=0.5)
    s = fig.add_subplot(111,
        xlabel='$\\phi$ (rad)',
        ylabel='$P_z$')

    phis_fit = numpy.linspace(0, numpy.pi * 2, 200)
    Pz_fit = est_amp * numpy.cos(phis_fit + est_phase)

    s.plot(phis_fit, Pz_fit, color=mplh.color.f.red.main,
        linestyle='-', dashes=mplh.dash['-'])
    s.scatter(phis, Pz, color='grey', s=1)

    s.text(numpy.pi / 2 - est_phase - est_phnoise - 0.5, 0.05, "$\\sigma")
    arrow_kwds = dict(
        shape="full",
        overhang=0, head_starts_at_zero=False, fill=False,
        length_includes_head=True)
    s.arrow(
        numpy.pi / 2 - est_phase - est_phnoise - 0.5, 0.0,
        0.45, 0.0,
        **arrow_kwds)
    s.arrow(
        numpy.pi / 2 - est_phase + est_phnoise + 0.5, 0.0,
        -0.45, 0.0,
        **arrow_kwds)

    s.set_xlim((0, 2 * numpy.pi))
    s.set_ylim((-1, 1))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    s.text(0.15, -0.85, '$t=' + str(t_ms) + '\\,\\mathrm{ms}$')

    fig.text(0.01, 0.92, '(a)' if t_ms == 20 else '(b)', fontweight='bold')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #4
0
파일: plot.py 프로젝트: fjarri/thesis
def _squeezing(fname, coupling, ens):

    with open(get_path(__file__, 'single_well_squeezing_wigner_' + ens + '_Na200.pickle')) as f:
        wigner = pickle.load(f)

    suffix = '_c' if coupling else '_nc'

    interaction = (100.4, 80.8, 95.5) if coupling else (100.4, 0., 100.4)
    tau_exact = numpy.linspace(1e-3, 120 if coupling else 20, 200)
    s_exact = get_exact_S(tau_exact, 200, *interaction)

    tau_wigner = wigner['tau' + suffix]
    s_wigner = wigner['s_pi2' + suffix]
    s_wigner_err = wigner['s_pi2' + suffix + '_err']

    s_exact = numpy.log10(s_exact) * 10
    tau_wigner, s_wigner_bot, s_wigner_top = mplh.crop_bounds(
        tau_wigner,
        numpy.log10(s_wigner - s_wigner_err) * 10,
        numpy.log10(s_wigner + s_wigner_err) * 10,
        (0, (120 if coupling else 20), -15, 1))

    fig = mplh.figure(width=0.5)
    s = fig.add_subplot(111,
        xlabel='$\\tau$',
        ylabel='$S_{\\theta + \\pi/2}$ (dB)')

    s.plot(tau_exact, s_exact, color="black")
    s.fill_between(
        tau_wigner, s_wigner_bot, s_wigner_top,
        facecolor=mplh.color.f.blue.lightest,
        linewidth=0)

    s.text(
        18 if coupling else 3,
        -1,
        "interaction on" if coupling else "interaction off")

    s.set_xlim((0, 120 if coupling else 20))
    s.set_ylim((-15, 1))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    fig.text(0.01, 0.92, '(b)' if coupling else '(a)', fontweight='bold')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #5
0
파일: plot.py 프로젝트: fjarri/thesis
def spinecho_noise(fname):
    with open(get_path(__file__, 'experimental/echo_phnoise_exp.json')) as f:
        exp = json.load(f)
    with open(get_path(__file__, 'simulations/echo_wigner.json')) as f:
        wig = json.load(f)
    with open(get_path(__file__, 'simulations/echo_wigner_varied_pulse.json')) as f:
        tech_wig = json.load(f)

    t_wig = numpy.array(wig['times'])
    ph_wig = numpy.array(wig['est_phnoises'])

    t_tech_wig = numpy.array(tech_wig['times'])
    ph_tech_wig = numpy.array(tech_wig['est_phnoises'])

    t_exp = numpy.array(exp['xarray'])
    ph_exp = numpy.array(exp['yarray'])
    ph_exp_errors = numpy.array(exp['yerrors'])

    fig = mplh.figure(width=0.75)
    s = fig.add_subplot(111,
        xlabel='$t$ (s)',
        ylabel='$\\mathcal{\\sigma}$ (rad)')

    s.errorbar(t_exp, ph_exp, yerr=ph_exp_errors, color='k', linestyle='none',
        capsize=1.5)
    s.plot(t_wig, ph_wig, color=mplh.color.f.red.main,
        linestyle='--', dashes=mplh.dash['--'])
    s.plot(t_tech_wig, ph_tech_wig, color=mplh.color.f.blue.main,
        linestyle='-', dashes=mplh.dash['-'])

    s.set_xlim((0, 1.6))
    s.set_ylim((0, 0.5))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    s.plot([0.03, 0.15], [0.43, 0.43], color=mplh.color.f.red.main, linestyle='--', dashes=mplh.dash['--'])
    s.text(0.18, 0.42, 'Wigner')
    s.plot([0.03, 0.15], [0.39, 0.39], color=mplh.color.f.blue.main, linestyle='-', dashes=mplh.dash['-'])
    s.text(0.18, 0.38, 'Wigner + tech. noise')
    s.errorbar([0.09], [0.35], yerr=[0.01], color='k', linestyle='none', capsize=1.5)
    s.text(0.18, 0.34, 'experiment')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #6
0
파일: plot.py 프로젝트: fjarri/thesis
def _two_comp_gs(fname, a12):
    with open(get_path(__file__, 'two_comp_gs.json'), 'rb') as f:
        data = json.load(f)

    zs = numpy.array(data[str(a12)]['xs']) / 1e-6 # to um
    n_z = numpy.array(data[str(a12)]['n_x']) * 1e-6 # to um^-1

    fig = mplh.figure()
    s = fig.add_subplot(111,
        xlabel='$z$ ($\\mu\\mathrm{m}$)',
        ylabel='$n_z$ ($\\mu\\mathrm{m}^{-1}$)')
    s.plot(zs, n_z[0], color=mplh.color.f.blue.main, linestyle='-')
    s.plot(zs, n_z[1], color=mplh.color.f.red.main, linestyle='--', dashes=mplh.dash['--'])

    s.text(
        11,
        2000. / 2500 * 1600 if a12 == 97. else 2000,
        '$a_{12}=' + str(a12) + '\\,r_B$')
    s.text(
        11,
        1700. / 2500 * 1600 if a12 == 97. else 1700,
        ('(miscible)' if a12 == 97. else '(immiscible)'))

    s.text(
        -28 if a12 == 97. else -26,
         1000. / 2500 * 1600 if a12 == 97. else 1000,
         '$\\vert 1 \\rangle$')
    s.text(
        -15 if a12 == 97. else -13,
         2000. / 2500 * 1600 if a12 == 97. else 2000,
         '$\\vert 2 \\rangle$')

    s.set_xlim(-40, 40)
    if a12 == 97.:
        s.set_ylim(0, 1600)
    else:
        s.set_ylim(0, 2500)

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    fig.text(0.01, 0.92, '(a)' if a12 == 97. else '(b)', fontweight='bold')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #7
0
파일: plot.py 프로젝트: fjarri/thesis
def _one_comp_gs(fname, N):
    with open(get_path(__file__, 'one_comp_gs.json'), 'rb') as f:
        data = json.load(f)

    zs = numpy.array(data[str(N)]['xs']) / 1e-6 # to um
    n_z = numpy.array(data[str(N)]['n_x']) * 1e-6 # to um^-1
    tf_n_z = numpy.array(data[str(N)]['tf_n_x']) * 1e-6 # to um^-1

    fig = mplh.figure()
    s = fig.add_subplot(111,
        xlabel='$z$ ($\\mu\\mathrm{m}$)',
        ylabel='$n_z$ ($\\mu\\mathrm{m}^{-1}$)')
    s.plot(zs, n_z[0], color=mplh.color.f.blue.main, linestyle='-')
    s.plot(zs, tf_n_z[0], color=mplh.color.f.red.main, linestyle='--', dashes=mplh.dash['--'])

    s.text(
        6 if N == 1000 else 15,
        2400 * 0.03 if N == 1000 else 2400,
        '$N=' + str(N) + '$')

    s.text(
        -14 if N == 1000 else -35,
        500 * 0.03 if N == 1000 else 500,
        'T-F')
    s.text(
        -7.5 if N == 1000 else -23,
        500 * 0.03 if N == 1000 else 500,
        'numerical')

    if N == 1000:
        s.set_xlim(-20, 20)
        s.set_ylim(0, 90)
    else:
        s.set_xlim(-50, 50)
        s.set_ylim(0, 3000)

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    fig.text(0.01, 0.92, '(b)' if N == 1000 else '(a)', fontweight='bold')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #8
0
파일: plot.py 프로젝트: fjarri/thesis
def spinecho_long(fname):
    with open(get_path(__file__, 'simulations/echo_gpe_long.json')) as f:
        gpe = json.load(f)
    with open(get_path(__file__, 'simulations/echo_wigner_long.json')) as f:
        wig = json.load(f)
    with open(get_path(__file__, 'simulations/echo_wigner_varied_pulse_long.json')) as f:
        wig_tech = json.load(f)

    t_gpe = numpy.array(gpe['times'])
    v_gpe = numpy.array(gpe['visibility'])
    t_wig = numpy.array(wig['times'])
    v_wig = numpy.array(wig['visibility'])
    t_tech_wig = numpy.array(wig_tech['times'])
    v_tech_wig = numpy.array(wig_tech['est_visibility'])

    fig = mplh.figure()
    s = fig.add_subplot(111,
        xlabel='$t$ (s)',
        ylabel='$\\mathcal{V}$')

    # GPE
    s.plot(t_gpe, v_gpe, color=mplh.color.f.green.main,
        linestyle=':', dashes=mplh.dash[':'])
    # Pure Wigner
    s.plot(t_wig, v_wig, color=mplh.color.f.red.main,
        linestyle='--', dashes=mplh.dash['--'])
    s.plot(t_tech_wig, v_tech_wig, color=mplh.color.f.blue.main,
        linestyle='-', dashes=mplh.dash['-'])

    s.set_xlim((0, 3.))
    s.set_ylim((0, 1.))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    s.text(0.2, 0.1, 'spin echo sequence')

    fig.text(0.01, 0.92, '(b)', fontweight='bold')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #9
0
파일: plot.py 프로젝트: fjarri/thesis
def ramsey_short(fname):
    with open(get_path(__file__, 'experimental/ramsey_experimental.json')) as f:
        vis_exp = json.load(f)
    with open(get_path(__file__, 'simulations/ramsey_gpe_no_losses.json')) as f:
        gpe_nl = json.load(f)
    with open(get_path(__file__, 'simulations/ramsey_gpe.json')) as f:
        gpe = json.load(f)
    with open(get_path(__file__, 'simulations/ramsey_wigner.json')) as f:
        wig = json.load(f)
    with open(get_path(__file__, 'simulations/ramsey_wigner_varied_pulse.json')) as f:
        wig_tech = json.load(f)

    t_exp = numpy.array(vis_exp['xarray'])
    v_exp = numpy.array(vis_exp['yarray'])
    v_exp_err = numpy.array(vis_exp['yerrors'])

    gpe_t = numpy.array(gpe['times'])
    gpe_v = numpy.array(gpe['visibility'])

    gpe_nl_t = numpy.array(gpe_nl['times'])
    gpe_nl_v = numpy.array(gpe_nl['visibility'])

    wig_t = numpy.array(wig['times'])
    wig_v = numpy.array(wig['visibility'])

    wig_tech_t = numpy.array(wig_tech['times'])
    wig_tech_v = numpy.array(wig_tech['est_visibility'])

    gpe_N1 = numpy.array(gpe['N1'])
    gpe_N2 = numpy.array(gpe['N2'])

    fig = mplh.figure(width=0.75)
    s = fig.add_subplot(111,
        xlabel='$t$ (s)',
        ylabel='$\\mathcal{V}$')
    s.errorbar(t_exp, v_exp, yerr=v_exp_err, color='k', linestyle='none',
        capsize=1.5)

    # Theoretical limit of visibility
    s.plot(wig_t, 2 * numpy.sqrt(gpe_N1 * gpe_N2) / (gpe_N1 + gpe_N2), color='grey',
        linestyle=':', dashes=mplh.dash[':'])

    # GPE
    s.plot(gpe_nl_t, gpe_nl_v, color=mplh.color.f.yellow.main,
        linestyle='-.', dashes=mplh.dash['-.'])
    s.plot(gpe_t, gpe_v, color=mplh.color.f.green.main,
        linestyle=':', dashes=mplh.dash[':'])
    # Pure Wigner
    s.plot(wig_t, wig_v, color=mplh.color.f.red.main,
        linestyle='--', dashes=mplh.dash['--'])
    # Wigner + technical noise
    s.plot(wig_tech_t, wig_tech_v, color=mplh.color.f.blue.main,
        linestyle='-', dashes=mplh.dash['-'])

    s.set_xlim((0, 1.3))
    s.set_ylim((0, 1.))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    s.plot([0.04, 0.12], [0.23, 0.23], color=mplh.color.f.green.main, linestyle=':', dashes=mplh.dash[':'])
    s.text(0.14, 0.22, 'mean-field')
    s.plot([0.04, 0.12], [0.15, 0.15], color=mplh.color.f.red.main, linestyle='--', dashes=mplh.dash['--'])
    s.text(0.14, 0.14, 'Wigner')
    s.plot([0.04, 0.12], [0.07, 0.07], color=mplh.color.f.blue.main, linestyle='-', dashes=mplh.dash['-'])
    s.text(0.14, 0.06, 'Wigner + tech. noise')

    s.plot([0.6, 0.68], [0.23, 0.23], color=mplh.color.f.yellow.main, linestyle='-.', dashes=mplh.dash['-.'])
    s.text(0.7, 0.22, 'mean-field, no losses')
    s.plot([0.6, 0.68], [0.15, 0.15], color='grey', linestyle=':', dashes=mplh.dash[':'])
    s.text(0.7, 0.14, 'visibility limit')
    s.errorbar([0.64], [0.07], yerr=[0.02], color='k', linestyle='none', capsize=1.5)
    s.text(0.7, 0.06, 'experiment')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #10
0
파일: plot.py 프로젝트: fjarri/thesis
def _squeezing_err(fname, coupling):

    suffix = '_c' if coupling else '_nc'

    colors = {
        '10k': mplh.color.f.blue,
        '1k': mplh.color.f.red,
        '100': mplh.color.f.green}
    dashes = {
        '10k': '-',
        '1k': '--'}

    fig = mplh.figure(width=0.5)
    s = fig.add_subplot(111,
        xlabel='$\\tau$',
        ylabel='Relative errors')

    for tr in ('1k', '10k'):

        with open(get_path(__file__, 'single_well_squeezing_wigner_' + tr + '_Na200.pickle')) as f:
            wigner = pickle.load(f)

        tau_wigner = wigner['tau' + suffix]
        s_wigner = wigner['s_pi2' + suffix]
        s_wigner_err = wigner['s_pi2' + suffix + '_err']

        interaction = (100.4, 80.8, 95.5) if coupling else (100.4, 0., 100.4)

        s_exact = get_exact_S(tau_wigner, 200, *interaction)
        s_exact[0] = 1.

        diff = numpy.abs(s_wigner - s_exact) / s_exact
        err = s_wigner_err / s_exact

        tau_diff, min_diff, max_diff = mplh.crop_bounds(
            tau_wigner, diff-err, diff+err, (0, (120 if coupling else 20), 0, 0.1))

        s.fill_between(tau_diff, min_diff, max_diff,
            facecolor=colors[tr].light,
            linewidth=0,
            alpha=0.5)
        s.plot(tau_wigner, diff, color=colors[tr].dark, dashes=mplh.dash[dashes[tr]])

    s.text(
        72 if coupling else 12,
        0.085,
        "interaction on" if coupling else "interaction off")

    if coupling:
        s.text(55, 0.06, '$20,000$')
        s.text(55, 0.052, 'trajectories')
        s.text(19, 0.02, '$200,000$')
        s.text(19, 0.012, 'trajectories')
    else:
        s.text(13, 0.06, '$20,000$')
        s.text(13, 0.052, 'trajectories')
        s.text(4, 0.02, '$200,000$')
        s.text(4, 0.012, 'trajectories')

    s.set_xlim((0, 120 if coupling else 20))
    s.set_ylim((0, 0.1))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    fig.text(0.01, 0.92, '(b)' if coupling else '(a)', fontweight='bold')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)
예제 #11
0
파일: plot.py 프로젝트: fjarri/thesis
def _squeezing_N_err(fname, coupling):

    suffix = '_c' if coupling else '_nc'

    fig = mplh.figure(width=0.5)
    s = fig.add_subplot(111,
        xlabel='$\\tau / \\tau_c(N)$',
        ylabel='Relative errors')

    colors = {
        20: mplh.color.f.blue,
        200: mplh.color.f.red,
        2000: mplh.color.f.green}
    dashes = {
        20: '-',
        200: '--',
        2000: ':'}

    for Na in (20, 200, 2000):

        with open(get_path(__file__, 'single_well_squeezing_wigner_10k_Na' + str(Na) + '.pickle')) as f:
            wigner = pickle.load(f)

        tau_wigner = wigner['tau' + suffix]
        s_wigner = wigner['s_pi2' + suffix]
        s_wigner_err = wigner['s_pi2' + suffix + '_err']

        interaction = (100.4, 80.8, 95.5) if coupling else (100.4, 0., 100.4)

        s_exact = get_exact_S(tau_wigner, Na, *interaction)
        s_exact[0] = 1.

        tau = tau_wigner / tau_wigner[-1]
        diff = numpy.abs(s_wigner - s_exact) / s_exact
        err = s_wigner_err / s_exact

        tau_diff, min_diff, max_diff = mplh.crop_bounds(tau, diff-err, diff+err, (0, 1., 0, 0.16))

        s.fill_between(tau_diff, min_diff, max_diff,
            facecolor=colors[Na].light,
            linewidth=0,
            alpha=0.5)
        s.plot(tau, diff, color=colors[Na].dark, dashes=mplh.dash[dashes[Na]])

    s.text(
        0.6,
        0.135,
        "interaction on" if coupling else "interaction off")

    if coupling:
        s.text(0.12, 0.12, '$N=20$')
        s.text(0.25, 0.062, '$N=200$')
        s.text(0.17, 0.03, '$N=2000$')
    else:
        s.text(0.1, 0.12, '$N=20$')
        s.text(0.27, 0.082, '$N=200$')
        s.text(0.16, 0.01, '$N=2000$')

    s.set_xlim((0, 1.))
    s.set_ylim((0, 0.16))

    s.set_aspect((5 ** 0.5 - 1) / 2 * mplh.aspect_modifier(s))

    fig.text(0.01, 0.92, '(b)' if coupling else '(a)', fontweight='bold')

    fig.tight_layout(pad=0.3)
    fig.savefig(fname)