def plot_energy_error(diag_filename, output_filename):

    diag_file = open(diag_filename, 'r')
    lines = diag_file.readlines()

    time = get_column("TIME", diag_filename)
    energy = get_column("TOTAL ENERGY", diag_filename)

    rot_period = 0.0

    dir = os.path.dirname(diag_filename)

    inputs_filename = dir + '/' + wdmerger.get_inputs_filename(dir)
    rot_period = wdmerger.get_inputs_var(inputs_filename,
                                         "castro.rotational_period")

    if (rot_period > 0.0):
        time = time / rot_period
        xlabel = "Time / Rotational Period"
    else:
        xlabel = "Time (s)"
    ylabel = "Relative Change in Energy"

    plt.plot(time, abs((energy - energy[0]) / energy[0]), lw=4.0)
    plt.yscale('log')
    plt.xlabel(xlabel, fontsize=20)
    plt.ylabel(ylabel, fontsize=20)
    plt.ylim([1.0e-8, 1.0e0])
    plt.tick_params(labelsize=16)
    plt.tight_layout()

    plt.savefig(output_filename)
    wdmerger.insert_commits_into_eps(output_filename, diag_filename, 'diag')

    plt.close()
def plot_wd_distance(diag_filenames, output_filename, n_orbits=-1, labels=''):

    # If we got only a single diagnostic file,
    # create a list out of it for the coming loop.

    if (type(diag_filenames) not in [list, tuple]):
        diag_filenames = [ diag_filenames ]
        labels = [ labels ]

    # Cycle through markers for multiple curves on the same plot.

    markers = ['o', '+', '.', ',', '*']

    j = 0

    for diag_filename, label in zip(diag_filenames, labels):

        diag_file = open(diag_filename, 'r')

        time = get_column("TIME", diag_filename)
        dist = get_column("WD DISTANCE",diag_filename)

        # Normalize distance by initial distance.

        dist = dist / dist[0]

        # Normalize time by rotational period.

        rot_period = 0.0

        dir = os.path.dirname(diag_filename)

        inputs_filename = dir + '/' + wdmerger.get_inputs_filename(dir)
        rot_period = wdmerger.get_inputs_var(inputs_filename, "castro.rotational_period")

        if (rot_period > 0.0):
            time = time / rot_period
            xlabel = "Time / Rotational Period"
            if (n_orbits > 0):
                idx = np.where(time < n_orbits)
                time = time[idx]
                dist = dist[idx]
        else:
            xlabel = "Time (s)"
        ylabel = "WD Distance / Initial Distance"

        if (label is not ''):
            plot_label = label

        if (len(diag_filenames) > 1):
            marker = markers[j]
        else:
            marker = ''

        # Use a markevery stride because of how densely packed the star_diag.out data usually is.

        plt.plot(time, dist, marker = marker, ms = 12.0, markevery = 500, lw = 4.0, label=plot_label)

        j += 1

    plt.xlabel(xlabel, fontsize=20)
    plt.ylabel(ylabel, fontsize=20)

    plt.gca().get_yaxis().get_major_formatter().set_useOffset(False)

    # Use the 'best' location for the legend, since for a generic function like this
    # it is hard to know ahead of time where the legend ought to go.
    # The alpha value controls the transparency, since we may end up covering some data.

    if (label is not ''):
        plt.legend(loc='best', fancybox=True, framealpha=0.5)

    # The padding ensures that the lower-left ticks on the x- and y-axes don't overlap.

    plt.tick_params(labelsize=16, pad=10)

    # We have now increased the size of both the ticks and the axis labels,
    # which may have caused the latter to fall off the plot. Use tight_layout
    # to automatically adjust the plot to fix this.

    plt.tight_layout()

    # Save it into our designated file, which is usually EPS format.

    plt.savefig(output_filename)

    # Insert git commit hashes into this file from the various code sources.

    wdmerger.insert_commits_into_eps(output_filename, diag_filename, 'diag')

    plt.close()
def plot_gw_signal(diag_filename,
                   output_filename,
                   n_orbits=-1,
                   do_analytical=0):

    diag_file = open(diag_filename, 'r')

    time = get_column("TIME", diag_filename)
    hplus = get_column("h_+ (axis 3)", diag_filename)
    hcross = get_column("h_x (axis 3)", diag_filename)

    # Normalize time by rotational period.

    rot_period = 0.0

    dir = os.path.dirname(diag_filename)

    inputs_filename = dir + '/' + wdmerger.get_inputs_filename(dir)
    probin_filename = dir + '/probin'

    rot_period = wdmerger.get_inputs_var(inputs_filename,
                                         "castro.rotational_period")

    if (rot_period > 0.0):
        time = time / rot_period
        xlabel = "Time / Rotational Period"
        if (n_orbits > 0):
            idx = np.where(time < n_orbits)
            time = time[idx]
            hplus = hplus[idx]
            hcross = hcross[idx]
    else:
        xlabel = "Time (s)"

    ylabel = "Gravitational Wave Strain"

    markers = ['o', '+', '.', ',', '*']

    # Now work out the analytical result for a circular binary orbit, if desired.

    if (do_analytical == 1):

        # Get relevant CGS constants.

        G_const = wdmerger.get_castro_const('Gconst')
        M_solar = wdmerger.get_castro_const('M_solar')
        c_light = wdmerger.get_castro_const('c_light')
        parsec = wdmerger.get_castro_const('parsec')

        # Relevant variables from the probin file.

        mass_P = wdmerger.get_probin_var(probin_filename, 'mass_P') * M_solar
        mass_S = wdmerger.get_probin_var(probin_filename, 'mass_S') * M_solar

        gw_dist = wdmerger.get_probin_var(probin_filename, 'gw_dist')  # In kpc

        mu = mass_P * mass_S / (mass_P + mass_S)
        M_tot = mass_P + mass_S
        dist = gw_dist * 1.e3 * parsec
        omega = 2.0 * np.pi / rot_period
        coeff = -4 * G_const * mu / (c_light**4 * dist) * (G_const * M_tot *
                                                           omega)**(2.0 / 3.0)
        hplus_true = coeff * np.cos(2.0 * omega * rot_period * time)
        hcross_true = coeff * np.sin(2.0 * omega * rot_period * time)

        plt.plot(time, hplus_true, lw=4.0, ls='-')
        plt.plot(time, hcross_true, lw=4.0, ls='--')

        plt.plot(time,
                 hplus,
                 marker=markers[0],
                 ms=12.0,
                 markevery=200,
                 label=r"$\plus$" + " polarization")
        plt.plot(time,
                 hcross,
                 marker=markers[4],
                 ms=12.0,
                 markevery=200,
                 label=r"$\times$" + " polarization")

    else:

        plt.plot(time,
                 hplus,
                 lw=4.0,
                 ls='-',
                 label=r"$\plus$" + " polarization")
        plt.plot(time,
                 hcross,
                 lw=4.0,
                 ls='--',
                 label=r"$\times$" + " polarization")

    plt.xlabel(xlabel, fontsize=20)
    plt.ylabel(ylabel, fontsize=20)

    # Use the 'best' location for the legend, since for a generic function like this
    # it is hard to know ahead of time where the legend ought to go.
    # The alpha value controls the transparency, since we may end up covering some data.

    plt.legend(loc='best', fancybox=True)

    # The padding ensures that the lower-left ticks on the x- and y-axes don't overlap.

    plt.tick_params(labelsize=16, pad=10)

    # We have now increased the size of both the ticks and the axis labels,
    # which may have caused the latter to fall off the plot. Use tight_layout
    # to automatically adjust the plot to fix this.

    plt.tight_layout()

    # Save it into our designated file, which is usually EPS format.

    plt.savefig(output_filename)

    # Insert git commit hashes into this file from the various code sources.

    wdmerger.insert_commits_into_eps(output_filename, diag_filename, 'diag')

    plt.close()
def plot_wd_location(diag_filenames, output_filename):

    # If we got only a single diagnostic file,
    # create a list out of it for the coming loop.

    if (type(diag_filenames) not in [list, tuple]):
        diag_filenames = [ diag_filenames ]

    # Cycle through markers for multiple curves on the same plot.

    N = len(diag_filenames)

    # Determine the number of subplots and their layout.

    nRows = 1
    nCols = 1

    if (N == 2):
        nCols = 2
    elif (N == 3):
        nCols = 3
    elif (N == 4):
        nRows = 2
        nCols = 2

    if (N > 1):  
        fig, ax = plt.subplots(nRows, nCols)
    else:
        fig = plt.gcf()
        ax  = plt.gca()

    row = 0
    col = 0
    j   = 0

    labels = ['(a)', '(b)', '(c)', '(d)']

    for diag_filename in diag_filenames:

        diag_file = open(diag_filename, 'r')

        # x and y locations of each star

        xp   = get_column("PRIMARY X COM",diag_filename)
        yp   = get_column("PRIMARY Y COM",diag_filename)
        xs   = get_column("SECONDARY X COM",diag_filename)
        ys   = get_column("SECONDARY Y COM",diag_filename)

        # Width of domain, from inputs file.

        dir = os.path.dirname(diag_filename)

        inputs_filename = dir + '/' + wdmerger.get_inputs_filename(dir)

        problo = wdmerger.get_inputs_var(inputs_filename, "geometry.prob_lo")
        probhi = wdmerger.get_inputs_var(inputs_filename, "geometry.prob_hi")

        half_width = (probhi - problo) / 2.0

        # If we are in the rotating frame, we want to transform back to the inertial frame.

        do_rotation = wdmerger.get_inputs_var(inputs_filename, "castro.do_rotation")

        if (do_rotation == 1):
            time = get_column("TIME",diag_filename)
            rot_period = wdmerger.get_inputs_var(inputs_filename, "castro.rotational_period")

            theta = (2.0 * np.pi / rot_period) * time        

            # Rotate from rotating frame to inertial frame using rotation matrix

            xp_old = xp
            yp_old = yp

            xp = xp_old * np.cos(theta) - yp_old * np.sin(theta)
            yp = xp_old * np.sin(theta) + yp_old * np.cos(theta)

            xs_old = xs
            ys_old = ys

            xs = xs_old * np.cos(theta) - ys_old * np.sin(theta)
            ys = xs_old * np.sin(theta) + ys_old * np.cos(theta)

        # Normalize the locations by this domain size, so that they are in the range [-0.5, 0.5]

        xp = xp / half_width[0]
        yp = yp / half_width[1]
        xs = xs / half_width[0]
        ys = ys / half_width[1]

        xlabel = "x"
        ylabel = "y"

        if (N > 1):
            curr_ax = ax[row,col]
        else:
            curr_ax = ax

        # Determine the label and position of this subplot based on the number of inputs.

        curr_ax.plot(xp, yp, 'b--')
        curr_ax.plot(xs, ys, 'r')

        curr_ax.set_xlabel(xlabel, fontsize=20)
        curr_ax.set_ylabel(ylabel, fontsize=20)

        curr_ax.set_xlim([-0.55, 0.55])
        curr_ax.set_ylim([-0.55, 0.55])

        curr_ax.xaxis.set_ticks([-0.50, 0.0, 0.50])
        curr_ax.yaxis.set_ticks([-0.50, 0.0, 0.50])

        curr_ax.set_aspect('equal')

        # The padding ensures that the lower-left ticks on the x- and y-axes don't overlap.

        curr_ax.tick_params(labelsize=16, pad=10)

        # Give it a letter label, for multipanel figures.

        if (N > 1):

            curr_ax.annotate(labels[j], xy=(0.15, 0.85), xycoords='axes fraction', fontsize=16,
                             horizontalalignment='right', verticalalignment='bottom')

        col += 1
        
        if (col > nCols-1):
            col = 0
            row += 1

        j += 1

    # Tighten up figure layout.

    fig.tight_layout()

    # Save it into our designated file, which is usually EPS format.

    plt.savefig(output_filename)

    # Insert git commit hashes into this file from the various code sources.

    wdmerger.insert_commits_into_eps(output_filename, diag_filename, 'diag')

    plt.close()