Exemplo n.º 1
0
def rho_centrality(rho_hist: Hist,
                   output_info: analysis_objects.PlottingOutputWrapper,
                   includes_constituent_cut: bool = True) -> None:
    """ Plot rho as a function of centrality vs jet pt.

    Args:
        rho_hist: Rho centrality dependent hist.
        output_info: Output information.
        includes_constituent_cut: True if the plot was produced using the constituent cut.
    Returns:
        None. The figure is plotted.
    """
    # Setup
    import ROOT
    canvas = ROOT.TCanvas("c", "c")
    canvas.SetRightMargin(0.15)
    # Set labels
    rho_hist.SetTitle("")
    rho_hist.Draw("colz")
    # Keep the range more meaningful.
    rho_hist.GetYaxis().SetRangeUser(0, 50)
    # Draw a profile of the mean
    rho_hist_profile = rho_hist.ProfileX(f"{rho_hist.GetName()}_profile")
    rho_hist_profile.SetMarkerStyle(ROOT.kFullCircle)
    rho_hist_profile.SetMarkerColor(ROOT.kRed)
    rho_hist_profile.SetMarkerSize(1.4)
    rho_hist_profile.Draw("same")

    # Finally, save and cleanup
    output_name = "rho_background"
    if includes_constituent_cut:
        output_name += "_3GeVConstituents"
    plot_base.save_plot(output_info, canvas, output_name)
Exemplo n.º 2
0
def trigger_jets_EP(
        ep_analyses: List[Tuple[Any, "correlations.Correlations"]],
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot jets triggers as a function of event plane orientation.

    Args:
        ep_analyses: Event plane dependent analyses.
        output_info: Output information.
    Returns:
        None. The triggers are plotted.
    """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))

    # Plot
    for key_index, analysis in ep_analyses:
        h = histogram.Histogram1D.from_existing_hist(
            analysis.number_of_triggers_observable.hist)
        # Scale by the bin width
        h *= 1.0 / h.bin_widths[0]
        ax.errorbar(
            h.x,
            h.y,
            xerr=h.bin_widths / 2,
            yerr=h.errors,
            marker="o",
            linestyle="None",
            label=
            fr"{analysis.reaction_plane_orientation.display_str()}: $N_{{\text{{trig}}}} = {analysis.number_of_triggers:g}$",
        )
        ax.set_xlim(0, 100)

    ax.text(
        0.025,
        0.025,
        r"$N_{\text{trig}}$ restricted to " +
        labels.jet_pt_range_string(analysis.jet_pt),
        transform=ax.transAxes,
        horizontalalignment="left",
        verticalalignment="bottom",
        multialignment="left",
    )

    # Final presentation settings
    ax.set_xlabel(
        labels.make_valid_latex_string(
            fr"{labels.jet_pt_display_label()}\:({labels.momentum_units_label_gev()})"
        ))
    ax.set_ylabel(
        labels.make_valid_latex_string(
            fr"d\text{{n}}/d{labels.jet_pt_display_label()}\:({labels.momentum_units_label_gev()}^{{-1}})"
        ))
    ax.set_yscale("log")
    ax.legend(frameon=False, loc="upper right")
    fig.tight_layout()

    # Finally, save and cleanup
    output_name = f"trigger_jet_spectra_EP"
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)
Exemplo n.º 3
0
def z_vertex(raw_z_vertex: List[Hist], z_vertices: Dict[params.EventActivity,
                                                        Hist],
             output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the z_vertex distribution for a list of centralities.

    Args:
        raw_z_vertex: Raw z_vertex. Each entry should have the same content.
        z_vertices: Centrality selected z_vertex distributions.
        output_info: Output information.
    Returns:
        None. The z_vertex are plotted.
    """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))

    # First plot the first raw distribution (we don't need the others).
    h = histogram.Histogram1D.from_existing_hist(raw_z_vertex[0])
    ax.plot(h.x, h.y, label=r"Raw $z_{\text{vtx}}$")

    # Then plot the selected ones
    for event_activity, hist in z_vertices.items():
        h = histogram.Histogram1D.from_existing_hist(hist)
        ax.plot(h.x, h.y, label=event_activity.display_str())

    # Final presentation settings
    ax.set_yscale("log")
    ax.legend(frameon=False, loc="upper right")
    fig.tight_layout()

    # Finally, save and cleanup
    plot_base.save_plot(output_info, fig, "z_vertex")
    plt.close(fig)
Exemplo n.º 4
0
def matched_jet_energy_scale(
        plot_labels: plot_base.PlotLabels, output_name: str,
        output_info: analysis_objects.PlottingOutputWrapper,
        obj: "response_matrix.ResponseMatrixBase") -> None:
    # Setup
    canvas = ROOT.TCanvas("canvas", "canvas")
    canvas.SetLogz(True)
    hist = obj.matched_jet_pt_difference
    logger.debug(f"hist: {hist}")

    # Plot the histogram
    plot_labels.apply_labels(hist)
    hist.Draw("colz")

    # Axis ranges
    hist.GetXaxis().SetRangeUser(0, 150)
    # Scale Z axis. Otherwise, we won't see much.
    min_val = ctypes.c_double(0)
    max_val = ctypes.c_double(0)
    hist.GetMinimumAndMaximum(min_val, max_val)
    # * 1.1 to put it slightly above the max value
    # min_val doesn't work here, because there are some entries at 0
    hist.GetZaxis().SetRangeUser(10e-7, max_val.value * 1.1)

    # Save
    plot_base.save_plot(output_info, canvas, output_name)
Exemplo n.º 5
0
def plot_RPF_regions(input_file: str, hist_name: str, output_prefix: str = ".", printing_extensions: Optional[List[str]] = None) -> None:
    """ Main entry point for stand-alone highlight plotting functionality.

    If this is being used as a library, call ``plot_RPF_fit_regions(...)`` directly instead.

    Args:
        input_file (str): Path to the input file.
        hist_name (str): Name of the histogram to be highlighted.
        output_prefix (str): Directory where the output file should be stored. Default: "."
        printing_extensions (list): Printing extensions to be used. Default: None, which corresponds
            to printing to ``.pdf``.
    Returns:
        None.
    """
    # Argument validation
    if printing_extensions is None:
        printing_extensions = [".pdf"]

    # Basic setup
    # Create logger
    logging.basicConfig(level=logging.DEBUG)
    # Quiet down the matplotlib logging
    logging.getLogger("matplotlib").setLevel(logging.INFO)

    # Retrieve hist
    with histogram.RootOpen(filename = input_file, mode = "READ") as f:
        hist = f.Get(hist_name)
        hist.SetDirectory(0)

    # Increase the size of the fonts, etc
    with sns.plotting_context(context = "notebook", font_scale = 1.5):
        # See the possible arguments to highlight_region_of_surface(...)
        # For example, to turn on the color overlay, it would be:
        #highlight_args = {"use_color_screen" : True}
        highlight_args: Dict[str, bool] = {}
        # Call plotting functions
        fig, ax = plot_RPF_fit_regions(
            histogram.get_array_from_hist2D(hist),
            highlight_regions = define_highlight_regions(),
            colormap = "ROOT_kBird", view_angle = (35, 225),
            **highlight_args,
        )

        # Modify axis labels
        # Set the distance from axis to label in pixels.
        # Having to set this by hand isn't ideal, but tight_layout doesn't work as well for 3D plots.
        ax.xaxis.labelpad = 12
        # Visually, delta eta looks closer
        ax.yaxis.labelpad = 15

        # If desired, add additional labeling here.
        # Probably want to use, for examxple, `ax.text2D(0.1, 0.9, "label text", transform = ax.transAxes)`
        # This would place the text in the upper left corner (it's like NDC)

        # Save and finish up
        # The figure will be saved at ``output_prefix/output_path.printing_extension``
        output_wrapper = analysis_objects.PlottingOutputWrapper(output_prefix = output_prefix, printing_extensions = printing_extensions)
        plot_base.save_plot(output_wrapper, fig, output_name = "highlightRPFRegions")
        plt.close(fig)
def plot_residual(residual: np.ndarray, pts: List[float], etas: List[float],
                  period: str, centrality_bin: int, centrality_ranges: Dict[int, params.SelectedRange],
                  output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the residual between the data and the parametrization.

    Args:
        residual: Calculated residual.
        pts: Pt values where the residual was evaluated.
        etas: Eta values where the residual was evaluated.
        period: Name of the data taking period.
        centrality_bin: Centrality bin.
        centraliy_ranges: Map of centrality bins to ranges.
        output_info: Output info for saving figures.
    Returns:
        None.
    """
    fig, ax = plt.subplots(figsize = (8, 6))
    im = ax.imshow(
        residual.T, extent = [np.nanmin(pts), np.nanmax(pts), np.nanmin(etas), np.nanmax(etas)],
        interpolation = "nearest", aspect = "auto", origin = "lower",
        # An even normalization is better for the colorscheme.
        # NOTE: This causes clipping at the lowest pt values, but I don't think this is a big problem.
        norm = matplotlib.colors.Normalize(
            #vmin = np.nanmin(residuals[centrality_bin]), vmax = np.nanmax(residuals[centrality_bin])
            vmin = -40, vmax = 40
        ),
        # This is a good diverging color scheme when it's centered at 0.
        cmap = "RdBu",
    )

    # Add the colorbar
    color_bar = fig.colorbar(im, ax = ax)
    color_bar.set_label(r"(data - fit)/fit (\%)")

    # Labels
    ax.set_xlabel(fr"${labels.pt_display_label()}\:({labels.momentum_units_label_gev()})$")
    ax.set_ylabel(r"$\eta$")
    title = f"{period} tracking efficiency residuals"
    if system != params.CollisionSystem.pp:
        centrality_range = centrality_ranges[centrality_bin]
        title += rf", ${centrality_range.min} \textendash {centrality_range.max}\%$"
    ax.set_title(title, size = 16)

    # Final adjustments
    fig.tight_layout()
    name = f"efficiency_residuals_{period}"
    if system != params.CollisionSystem.pp:
        centrality_range = centrality_ranges[centrality_bin]
        name += f"_centrality_{centrality_range.min}_{centrality_range.max}"
    plot_base.save_plot(output_info, fig, name)

    # Cleanup
    plt.close(fig)
def plot_1D_pt_efficiency(efficiency: Hist, PublicUtils: T_PublicUtils, efficiency_period: Any,
                          centrality_bin: int, centrality_range: params.SelectedRange,
                          output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot 1D pt efficiency.

    Args:
        efficiency: Pt efficiency hist.
        PublicUtils: Jet-H public utils class.
        efficiency_period: Data taking period in the efficiency enum.
        centrality_bin: int
        centrality_range: Centrality range.
        output_info: Output info for saving figures.
    Returns:
        None.
    """
    # 1D efficiency as a function of pt
    logger.debug(f"max efficiency_1D: {efficiency.GetMaximum()}")
    h = histogram.Histogram1D.from_existing_hist(efficiency)
    fig, ax = plt.subplots(figsize = (8, 6))
    ax.errorbar(
        h.x, h.y, yerr = h.errors,
        label = "${labels.pt_display_label()}$",
        color = "black", marker = ".", linestyle = "",
    )

    # Efficiency function
    parametrization = []
    for x in h.x:
        parametrization.append(PublicUtils.LHC15oPtEfficiency(x, centrality_bin))
    ax.plot(
        h.x, parametrization,
        label = "${labels.pt_display_label()}$ param.",
        color = "red",
    )

    # Ensure that it's on a consistent axis
    ax.set_ylim(0.6, 1)

    # Labels
    ax.set_xlabel(fr"${labels.pt_display_label()}\:({labels.momentum_units_label_gev()})$")
    ax.set_ylabel(r"Efficiency")
    title = f"{period} ${labels.pt_display_label()}$ tracking efficiency"
    if system != params.CollisionSystem.pp:
        title += rf", ${centrality_range.min} \textendash {centrality_range.max}\%$"
    ax.set_title(title, size = 16)

    # Final adjustments
    fig.tight_layout()
    name = f"efficiency_pt_{period}"
    if system != params.CollisionSystem.pp:
        name += f"_centrality_{centrality_range.min}_{centrality_range.max}"
    plot_base.save_plot(output_info, fig, name)
Exemplo n.º 8
0
def delta_eta_unsubtracted(hists: "correlations.CorrelationHistogramsDeltaEta",
                           correlation_scale_factor: float,
                           jet_pt: analysis_objects.JetPtBin, track_pt: analysis_objects.TrackPtBin,
                           reaction_plane_orientation: params.ReactionPlaneOrientation,
                           identifier: str, output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot 1D delta eta correlations on a single plot.

    Args:
        hists: Unsubtracted delta eta histograms.
        correlation_scale_factor: Overall correlation scale factor.
        jet_pt: Jet pt bin.
        track_pt: Track pt bin.
        reaction_plane_orientation: Reaction plane orientation.
        identifier: Analysis identifier string. Usually contains jet pt, track pt, and other information.
        output_info: Standard information needed to store the output.
    Returns:
        None.
    """
    # Setup
    fig, ax = plt.subplots(figsize = (8, 6))

    # Plot NS, AS
    h_near_side = histogram.Histogram1D.from_existing_hist(hists.near_side.hist)
    h_near_side *= correlation_scale_factor
    ax.errorbar(
        h_near_side.x, h_near_side.y, yerr = h_near_side.errors,
        label = hists.near_side.type.display_str(), marker = "o", linestyle = "",
    )
    h_away_side = histogram.Histogram1D.from_existing_hist(hists.away_side.hist)
    h_away_side *= correlation_scale_factor
    ax.errorbar(
        h_away_side.x, h_away_side.y, yerr = h_away_side.errors,
        label = hists.away_side.type.display_str(), marker = "o", linestyle = "",
    )

    # Set labels.
    ax.set_xlabel(labels.make_valid_latex_string(hists.near_side.hist.GetXaxis().GetTitle()))
    ax.set_ylabel(labels.make_valid_latex_string(hists.near_side.hist.GetYaxis().GetTitle()))
    ax.set_title(fr"Unsubtracted 1D ${hists.near_side.axis.display_str()}$,"
                 f" {reaction_plane_orientation.display_str()} event plane orient.,"
                 f" {labels.jet_pt_range_string(jet_pt)}, {labels.track_pt_range_string(track_pt)}")

    # Labeling
    ax.legend(loc = "upper right")

    # Final adjustments
    fig.tight_layout()

    # Save and cleanup
    output_name = f"jetH_delta_eta_{identifier}_near_away_side_comparison"
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)
Exemplo n.º 9
0
def delta_eta_fit_subtracted(analysis: "correlations.Correlations") -> None:
    """ Plot the subtracted delta eta near-side and away-side. """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))

    # Plot both the near side and the away side.
    attribute_names = ["near_side", "away_side"]
    for attribute_name in attribute_names:
        # Setup an individual hist
        correlation = getattr(analysis.correlation_hists_delta_eta_subtracted,
                              attribute_name)
        h = correlation.hist

        # Plot the data
        ax.errorbar(
            h.x,
            h.y,
            yerr=h.errors,
            marker="o",
            linestyle="",
            label=f"Subtracted {correlation.type.display_str()}",
        )

        # Add horizontal line at 0 for comparison.
        ax.axhline(y=0, color="black", linestyle="dashed", zorder=1)

        # Labels.
        ax.set_xlabel(
            labels.make_valid_latex_string(correlation.axis.display_str()))
        ax.set_ylabel(
            labels.make_valid_latex_string(labels.delta_eta_axis_label()))
        jet_pt_label = labels.jet_pt_range_string(analysis.jet_pt)
        track_pt_label = labels.track_pt_range_string(analysis.track_pt)
        ax.set_title(
            fr"Subtracted 1D ${correlation.axis.display_str()}$,"
            f" {analysis.reaction_plane_orientation.display_str()} event plane orient.,"
            f" {jet_pt_label}, {track_pt_label}")
        ax.legend(loc="upper right")

        # Final adjustments
        fig.tight_layout()
        # Save plot and cleanup
        plot_base.save_plot(
            analysis.output_info, fig,
            f"jetH_delta_eta_{analysis.identifier}_{attribute_name}_subtracted"
        )
        # Reset for the next iteration of the loop
        ax.clear()

    # Final cleanup
    plt.close(fig)
Exemplo n.º 10
0
    def finalize(self, n_events_accepted: int) -> None:
        """ Finalize the analysis. """
        # Sanity check
        assert len(self.events) == n_events_accepted

        logger.info(
            f"number of accepted events: {len(self.events)}, jets in tree: {len(self.jets)}"
        )
        # Finally, convert to a proper numpy array. It's only converted here because it's not efficient to expand
        # existing numpy arrays.
        self.jets = np.array(self.jets, dtype=DTYPE_JETS)
        self.events = np.array(self.events, dtype=DTYPE_EVENT_PROPERTIES)

        # And save out the tree so we don't have to calculate it again later.
        self.save_tree(arr=self.jets, output_name="jets")
        self.save_tree(arr=self.events, output_name="event_properties")

        # Create the response matrix and plot it as a cross check.
        # Create histogram
        h, x_edges, y_edges = np.histogram2d(self.jets["det_pT"],
                                             self.jets["part_pT"],
                                             bins=(60, 60),
                                             range=((0, 60), (0, 60)))
        # Plot
        import matplotlib
        import matplotlib.pyplot as plt
        # Fix normalization
        h[h == 0] = np.nan
        fig, ax = plt.subplots(figsize=(8, 6))
        resp = ax.imshow(
            h,
            extent=(x_edges[0], x_edges[-1], y_edges[0], y_edges[-1]),
            interpolation="nearest",
            aspect="auto",
            origin="lower",
            norm=matplotlib.colors.Normalize(vmin=np.nanmin(h),
                                             vmax=np.nanmax(h)),
        )
        fig.colorbar(resp)

        # Final labeling and presentation
        ax.set_xlabel(
            labels.make_valid_latex_string(labels.jet_pt_display_label("det")))
        ax.set_ylabel(
            labels.make_valid_latex_string(
                labels.jet_pt_display_label("part")))
        fig.tight_layout()
        fig.subplots_adjust(hspace=0, wspace=0, right=0.99)
        plot_base.save_plot(self.output_info, fig, f"response")

        plt.close(fig)
Exemplo n.º 11
0
def _plot_all_1d_correlations_with_matplotlib(
        jet_hadron: "correlations.Correlations") -> None:
    """ Plot all 1D correlations in a very basic way with matplotlib.

    Note:
        We don't want to scale the histogram any further here because it's already been fully scaled!
    """
    # Not ideal, but it's much more convenient to import it here. Importing it at the top
    # of the file would cause an import loop.
    from jet_hadron.analysis import correlations
    fig, ax = plt.subplots()
    for correlations_groups in [
            jet_hadron.correlation_hists_delta_phi,
            jet_hadron.correlation_hists_delta_eta
    ]:
        # Help out mypy...
        assert isinstance(correlations_groups,
                          (correlations.CorrelationHistogramsDeltaPhi,
                           correlations.CorrelationHistogramsDeltaEta))
        for _, observable in correlations_groups:
            # Draw the 1D histogram.
            h = histogram.Histogram1D.from_existing_hist(observable.hist)
            ax.errorbar(
                h.x,
                h.y,
                yerr=h.errors,
                label=observable.hist.GetName(),
                marker="o",
                linestyle="",
            )
            # Set labels.
            ax.set_xlabel(
                labels.make_valid_latex_string(
                    observable.hist.GetXaxis().GetTitle()))
            ax.set_ylabel(
                labels.make_valid_latex_string(
                    observable.hist.GetYaxis().GetTitle()))
            ax.set_title(
                labels.make_valid_latex_string(observable.hist.GetTitle()))

            # Final adjustments
            fig.tight_layout()

            # Save and cleanup
            output_name = observable.hist.GetName() + "_mpl"
            plot_base.save_plot(jet_hadron.output_info, fig, output_name)
            ax.clear()

    # Cleanup
    plt.close(fig)
Exemplo n.º 12
0
    def plot(self,
             obj: "generic_tasks.PlotTaskHists",
             output_name: str = "") -> None:
        # Ensure that the output directory is available.
        path = os.path.join(obj.output_prefix, os.path.dirname(output_name))
        if not os.path.exists(path):
            os.makedirs(path)

        # Make any pre draw adjustments.
        self._pre_draw_options()

        # Make the plots
        fig, ax = plt.subplots(figsize=(8, 6))

        # Draw the hist
        import ROOT
        if isinstance(self._first_hist(), ROOT.TH2):
            self._plot_2D_hists(fig=fig, ax=ax)
        elif isinstance(self._first_hist(), ROOT.TH1):
            self._plot_1D_hists(fig=fig, ax=ax)
        else:
            raise ValueError(
                f"Histogram must be 1D or 2D. Type provided: {type(self._first_hist())}"
            )

        # Apply the options
        # Need to apply these here because rplt messes with them!
        self._apply_hist_settings(ax)

        # Apply post drawing options
        self._post_draw_options(ax)
        self._add_text_labels(ax, obj)

        # Final plotting options
        plt.tight_layout()

        # Determine the final output path
        # Use ``self.output_name`` for the end of the output name if it's set. If it's not set, then we just
        # use the plot configuration key name passed in via ``output_name``
        final_output_name = output_name
        if self.output_name:
            final_output_name = os.path.join(
                os.path.dirname(final_output_name), self.output_name)
        logger.debug(
            f"final_output_name: {final_output_name}, self.output_name: {self.output_name}"
        )

        # Save and close the figure
        plot_base.save_plot(obj.output_info, fig, final_output_name)
        plt.close(fig)
Exemplo n.º 13
0
def fit_subtracted_signal_dominated(
        analysis: "correlations.Correlations") -> None:
    """ Plot the subtracted signal dominated hist. """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))
    hists = analysis.correlation_hists_delta_phi_subtracted
    h = hists.signal_dominated.hist

    # Plot the subtracted hist
    ax.errorbar(
        h.x,
        h.y,
        yerr=h.errors,
        label=f"Subtracted {hists.signal_dominated.type.display_str()}",
        marker="o",
        linestyle="",
    )
    # Plot the background uncertainty separately.
    background_error = analysis.fit_object.calculate_background_function_errors(
        h.x)
    ax.fill_between(
        h.x,
        h.y - background_error,
        h.y + background_error,
        label="RP background uncertainty",
        color=plot_base.AnalysisColors.fit,
    )
    # Line for comparison
    ax.axhline(y=0, color="black", linestyle="dashed", zorder=1)

    # Labels.
    ax.set_xlabel(
        labels.make_valid_latex_string(
            hists.signal_dominated.axis.display_str()))
    ax.set_ylabel(labels.make_valid_latex_string(
        labels.delta_phi_axis_label()))
    jet_pt_label = labels.jet_pt_range_string(analysis.jet_pt)
    track_pt_label = labels.track_pt_range_string(analysis.track_pt)
    ax.set_title(
        fr"Subtracted 1D ${hists.signal_dominated.axis.display_str()}$,"
        f" {analysis.reaction_plane_orientation.display_str()} event plane orient.,"
        f" {jet_pt_label}, {track_pt_label}")
    ax.legend(loc="upper right", frameon=False)

    # Final adjustments
    fig.tight_layout()
    # Save plot and cleanup
    plot_base.save_plot(analysis.output_info, fig,
                        f"jetH_delta_phi_{analysis.identifier}_subtracted")
    plt.close(fig)
def plot_tracking_efficiency_parametrization(efficiency: np.ndarray, centrality_range: params.SelectedRange,
                                             period: str, system: params.CollisionSystem,
                                             output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the given tracking efficiencies parametrization.

    Args:
        efficiency: Calculated tracking efficiencies.
        centrality_range: Associated centrality range.
        period: Data taking period.
        system: Collision system.
        output_info: Output info for saving figures.
    Returns:
        None.
    """
    # Get the parameters
    pt_values, eta_values, n_cent_bins, centrality_ranges = generate_parameters(system)

    logger.debug(fr"Plotting efficiencies for {centrality_range.min}--{centrality_range.max}%")
    fig, ax = plt.subplots(figsize = (8, 6))
    im = ax.imshow(
        efficiency.T,
        extent = [np.min(pt_values), np.max(pt_values), np.min(eta_values), np.max(eta_values)],
        interpolation = "nearest", aspect = "auto", origin = "lower",
        norm = matplotlib.colors.Normalize(vmin = 0.5, vmax = 1), cmap = "viridis",
    )

    # Add the colorbar
    fig.colorbar(im, ax = ax)

    # Labels
    ax.set_xlabel(fr"${labels.pt_display_label()}\:({labels.momentum_units_label_gev()})$")
    ax.set_ylabel(r"$\eta$")
    title = f"{period} tracking efficiency parametrization"
    if system != params.CollisionSystem.pp:
        title += rf", ${centrality_range.min} \textendash {centrality_range.max}\%$"
    ax.set_title(title, size = 16)

    # Final adjustments
    fig.tight_layout()
    name = f"efficiency_{period}"
    if system != params.CollisionSystem.pp:
        name += f"_centrality_parametrization_{centrality_range.min}_{centrality_range.max}"
    plot_base.save_plot(output_info, fig, name)

    # Cleanup
    plt.close(fig)
Exemplo n.º 15
0
def _plot_1d_signal_and_background_with_ROOT(
        jet_hadron: "correlations.Correlations", output_name: str) -> None:
    """ Plot 1D signal and background hists on a single plot with ROOT. """
    # Setup
    canvas = ROOT.TCanvas("canvas1D", "canvas1D")
    hists = jet_hadron.correlation_hists_delta_phi

    # Plot
    hists.signal_dominated.hist.SetLineColor(ROOT.kBlack)
    hists.signal_dominated.hist.SetMarkerColor(ROOT.kBlack)
    hists.signal_dominated.hist.Draw("")
    hists.background_dominated.hist.SetLineColor(ROOT.kBlue)
    hists.background_dominated.hist.SetMarkerColor(ROOT.kBlue)
    hists.background_dominated.hist.Draw("same")

    # Save
    output_name += "_ROOT"
    plot_base.save_plot(jet_hadron.output_info, canvas, output_name)
Exemplo n.º 16
0
def _plot_fit_parameter_vs_assoc_pt(
        fit_objects: FitObjects, parameter: ParameterInfo,
        reference_data: ReferenceData,
        selected_analysis_options: params.SelectedAnalysisOptions,
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Implementation to plot the fit parameters vs associated track pt.  """
    fig, ax = plt.subplots(figsize=(8, 6))

    # Extract the parameter values from each fit object.
    bin_centers = np.zeros(len(fit_objects))
    parameter_values = np.zeros(len(fit_objects))
    parameter_values_errors = np.zeros(len(fit_objects))
    for i, (key_index, fit_object) in enumerate(fit_objects.items()):
        bin_centers[i] = key_index.track_pt_bin.bin_center
        parameter_values[i] = fit_object.fit_result.values_at_minimum[
            parameter.name]
        parameter_values_errors[
            i] = fit_object.fit_result.errors_on_parameters[parameter.name]

    # Plot the particular parameter.
    ax.errorbar(
        bin_centers,
        parameter_values,
        yerr=parameter_values_errors,
        marker="o",
        linestyle="",
        label=parameter.labels.title,
    )

    if parameter.plot_reference_data_func:
        parameter.plot_reference_data_func(
            reference_data,
            ax,
            selected_analysis_options,
        )

    # Labeling
    parameter.labels.apply_labels(ax)
    ax.legend(loc="upper left", frameon=False)
    # Final adjustments
    fig.tight_layout()
    # Save plot and cleanup
    plot_base.save_plot(output_info, fig, parameter.output_name)
    plt.close(fig)
def plot_2D_efficiency_data(efficiency_hist: Hist, centrality_range: params.SelectedRange, output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the 2D efficiency data

    Args:
        efficiency_hist: Efficiecny histogram.
        centrality_range: Centrality range.
        output_info: Output info for saving figures.
    Returns:
        None.
    """
    X, Y, efficiency_data = histogram.get_array_from_hist2D(hist = efficiency_hist)
    logger.debug(f"efficiency data min: {np.nanmin(efficiency_data)}, max: {np.nanmax(efficiency_data)}")
    fig, ax = plt.subplots(figsize = (8, 6))
    im = ax.imshow(
        efficiency_data.T, extent = [np.nanmin(X), np.nanmax(X), np.nanmin(Y), np.nanmax(Y)],
        interpolation = "nearest", aspect = "auto", origin = "lower",
        norm = matplotlib.colors.Normalize(
            vmin = np.nanmin(efficiency_data), vmax = np.nanmax(efficiency_data)
            #vmin = 0.5, vmax = 1,
        ),
        cmap = "viridis",
    )

    # Add the colorbar
    color_bar = fig.colorbar(im, ax = ax)
    color_bar.set_label("Efficiency")

    # Labels
    ax.set_xlabel(fr"${labels.pt_display_label()}\:({labels.momentum_units_label_gev()})$")
    ax.set_ylabel(r"$\eta$")
    title = f"{period} tracking efficiency data"
    if system != params.CollisionSystem.pp:
        title += rf", ${centrality_range.min} \textendash {centrality_range.max}\%$"
    ax.set_title(title, size = 16)

    # Final adjustments
    fig.tight_layout()
    name = f"efficiency_{period}"
    if system != params.CollisionSystem.pp:
        name += f"_centrality_{centrality_range.min}_{centrality_range.max}"
    plot_base.save_plot(output_info, fig, name)

    # Cleanup
    plt.close(fig)
    def _example_plots(self) -> None:
        """ Produce some images of example collisions. """
        logger.info("Creating example interaction region plots")
        # Setup
        c = ROOT.TCanvas("c", "c")

        # Iterate over the min and max impact parameters, plotting an example interaction from each.
        for _, impact_parameter in self.impact_parameters[self.selected_analysis_options.event_activity]:
            logger.debug(f"impact_parameter: {impact_parameter}")
            # Setup
            self.analysis.glauber.SetBmin(impact_parameter)
            self.analysis.glauber.SetBmax(impact_parameter)
            # Generate
            self.analysis.event_loop(
                n_events = 1, progress_manager = self._progress_manager
            )
            # Draw
            self.analysis.glauber.Draw()
            plot_base.save_plot(self.output_info, c, f"example_collision_b_{impact_parameter}")
Exemplo n.º 19
0
def signal_dominated_with_background_function(
        analysis: "correlations.Correlations") -> None:
    """ Plot the signal dominated hist with the background function. """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))

    # Plot signal and background dominated hists
    plot_correlations.plot_and_label_1d_signal_and_background_with_matplotlib_on_axis(
        ax=ax, jet_hadron=analysis)

    # Plot background function
    # First we retrieve the signal dominated histogram to get reference x values and bin edges.
    h = histogram.Histogram1D.from_existing_hist(
        analysis.correlation_hists_delta_phi.signal_dominated.hist)
    background = histogram.Histogram1D(
        bin_edges=h.bin_edges,
        y=analysis.fit_object.evaluate_background(h.x),
        errors_squared=analysis.fit_object.
        calculate_background_function_errors(h.x)**2,
    )
    background_plot = ax.plot(background.x,
                              background.y,
                              label="Background function")
    ax.fill_between(
        background.x,
        background.y - background.errors,
        background.y + background.errors,
        facecolor=background_plot[0].get_color(),
        alpha=0.9,
    )

    # Labeling
    ax.legend(loc="upper right")

    # Final adjustments
    fig.tight_layout()
    # Save plot and cleanup
    plot_base.save_plot(
        analysis.output_info, fig,
        f"jetH_delta_phi_{analysis.identifier}_signal_background_function_comparison"
    )
    plt.close(fig)
Exemplo n.º 20
0
def _plot_1d_signal_and_background_with_matplotlib(jet_hadron: "correlations.Correlations", output_name: str) -> None:
    """ Plot 1D signal and background hists on a single plot with matplotlib. """
    # Setup
    fig, ax = plt.subplots(figsize = (8, 6))

    # Perform the actual plot
    plot_and_label_1d_signal_and_background_with_matplotlib_on_axis(
        ax = ax, jet_hadron = jet_hadron, apply_correlation_scale_factor = True
    )

    # Labeling
    ax.legend(loc = "upper right")

    # Final adjustments
    fig.tight_layout()

    # Save and cleanup
    output_name += "_mpl"
    plot_base.save_plot(jet_hadron.output_info, fig, output_name)
    plt.close(fig)
Exemplo n.º 21
0
def plot_particle_level_spectra_agreement(
        difference: Hist, absolute_value_of_difference: Hist,
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the agreement of the particle level spectra between the inclusive and sum of all EP orientations.

    Args:
        difference: Hist of the sum of the EP orientations spectra minus the inclusive spectra.
        absolute_value_of_difference: Same as the difference hist, but having taken the absolute value.
            This allows us to plot the difference on a log scale (which is useful if the differences
            are small).
        output_info: Output information.
    Returns:
        None.
    """
    # Setup
    output_name = "difference_of_sum_EP_orientations_vs_inclusive"
    canvas = ROOT.TCanvas("canvas", "canvas")
    # Labeling
    x_label = labels.use_label_with_root(
        fr"{labels.jet_pt_display_label(upper_label = 'part')}\:({labels.momentum_units_label_gev()})"
    )
    y_label = r"\mathrm{d}N/\mathrm{d}p_{\mathrm{T}}"

    # Apply settings to hists
    for h in [difference, absolute_value_of_difference]:
        # Labeling
        h.GetXaxis().SetTitle(x_label)
        h.GetYaxis().SetTitle(y_label)
        # Center axis title
        h.GetXaxis().CenterTitle(True)
        h.GetYaxis().CenterTitle(True)

    # Draw and save the difference histogram.
    difference.Draw()
    plot_base.save_plot(output_info, canvas, output_name)

    # Draw and save the absolute value of the difference histogram.
    absolute_value_of_difference.Draw()
    canvas.SetLogy(True)
    output_name += "_abs"
    plot_base.save_plot(output_info, canvas, output_name)
Exemplo n.º 22
0
def _plot_all_1d_correlations_with_ROOT(jet_hadron: "correlations.Correlations") -> None:
    """ Plot all 1D correlations in a very basic way with ROOT.

    Note:
        We scaled with the correlation scale factor before plotting to ensure that the stored
        plots are always scaled properly.
    """
    # Not ideal, but it's much more convenient to import it here. Importing it at the top
    # of the file would cause an import loop.
    from jet_hadron.analysis import correlations
    canvas = ROOT.TCanvas("canvas1D", "canvas1D")
    for correlations_groups in [jet_hadron.correlation_hists_delta_phi, jet_hadron.correlation_hists_delta_eta]:
        # Help out mypy...
        assert isinstance(correlations_groups, (correlations.CorrelationHistogramsDeltaPhi, correlations.CorrelationHistogramsDeltaEta))
        for name, observable in correlations_groups:
            # Draw the 1D histogram.
            hist = observable.hist.Clone(f"{name}_scaled")
            hist.Scale(jet_hadron.correlation_scale_factor)
            hist.Draw("")
            output_name = observable.hist.GetName() + "_ROOT"
            plot_base.save_plot(jet_hadron.output_info, canvas, output_name)
Exemplo n.º 23
0
def _plot_response_matrix_with_ROOT(
        name: str, x_label: str, y_label: str, output_name: str, hist: Hist,
        plot_errors_hist: bool,
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Underlying function to actually plot a response matrix with ROOT.

    Args:
        name: Name of the histogram.
        x_label: X axis label.
        y_label: Y axis label.
        output_name: Output name of the histogram.
        hist: The response matrix related 2D hist.
        errors_hist: True if the hist is the response matrix errors hist.
        output_info: Output information.
    Returns:
        None
    """
    # Setup
    canvas = ROOT.TCanvas("canvas", "canvas")
    canvas.SetLogz(True)

    # Plot the histogram
    hist.SetTitle(name)
    hist.GetXaxis().SetTitle(labels.use_label_with_root(x_label))
    hist.GetYaxis().SetTitle(labels.use_label_with_root(y_label))
    hist.Draw("colz")

    # Set the final axis ranges.
    # Z axis
    min_val = ctypes.c_double(0)
    max_val = ctypes.c_double(0)
    hist.GetMinimumAndMaximum(min_val, max_val)
    # * 1.1 to put it slightly above the max value
    # min_val doesn't work here, because there are some entries at 0
    hist.GetZaxis().SetRangeUser(10e-7, max_val.value * 1.1)

    # Save
    output_name += "_ROOT"
    plot_base.save_plot(output_info, canvas, output_name)
Exemplo n.º 24
0
def comparison_1d(output_info: analysis_objects.PlottingOutputWrapper,
                  our_hist: histogram.Histogram1D,
                  their_hist: histogram.Histogram1D,
                  ratio: histogram.Histogram1D, title: str, x_label: str,
                  y_label: str, output_name: str) -> None:
    """ Compare our hist and their hist. """
    fig, ax = plt.subplots(2,
                           1,
                           sharex=True,
                           gridspec_kw={"height_ratios": [3, 1]},
                           figsize=(8, 6))

    # Plot data
    ax[0].errorbar(our_hist.x,
                   our_hist.y,
                   yerr=our_hist.errors,
                   label="Our hist")
    ax[0].errorbar(their_hist.x,
                   their_hist.y,
                   yerr=their_hist.errors,
                   label="Their hist")
    # Plot ratio
    ax[1].errorbar(ratio.x, ratio.y, yerr=ratio.errors, label="Theirs/ours")

    # Set plot properties
    ax[0].set_title(title)
    ax[0].set_ylabel(r"$\mathrm{d}N/\mathrm{d}\varphi$")
    ax[0].legend(loc="best")
    ax[1].set_xlabel(r"$\Delta\varphi$")
    ax[1].set_ylabel("Theirs/ours")

    # Final adjustments
    fig.tight_layout()
    # Reduce spacing between subplots
    fig.subplots_adjust(hspace=0, wspace=0.05)

    # Save and cleanup
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)
Exemplo n.º 25
0
def event_cut_stats(
        cut_stats: Dict[str, Hist], event_activity: params.EventActivity,
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the event cut stats.

    Args:
        cut_stats: Collections of cut stats to plot.
        event_activity: Event activity of the stats info.
        output_info: Output information.
    Returns:
        None. The centralities are plotted.
    """
    # Setup
    import ROOT
    canvas = ROOT.TCanvas("canvas", "canvas")
    # Legend
    legend = ROOT.TLegend(0.1, 0.35, 0.3, 0.6)
    # Increase text size
    legend.SetTextSize(0.03)
    # Remove the legend border
    legend.SetBorderSize(0)
    # Make the legend transparent
    legend.SetFillStyle(0)

    # Plot
    colors = [ROOT.kBlack, ROOT.kRed, ROOT.kBlue]
    for (name, hist), color in zip(cut_stats.items(), colors):
        hist.SetLineColor(color)
        hist.Draw("same")

        legend.AddEntry(hist, name)

    # Final presentation settings
    legend.Draw("same")

    # Finally, save and cleanup
    plot_base.save_plot(output_info, canvas,
                        f"event_cuts_{str(event_activity)}")
Exemplo n.º 26
0
def _plot_all_1d_correlations_with_ROOT(
        jet_hadron: "correlations.Correlations") -> None:
    """ Plot all 1D correlations in a very basic way with ROOT.

    Note:
        We don't want to scale the histogram any further here because it's already been fully scaled!
    """
    # Not ideal, but it's much more convenient to import it here. Importing it at the top
    # of the file would cause an import loop.
    from jet_hadron.analysis import correlations
    canvas = ROOT.TCanvas("canvas1D", "canvas1D")
    for correlations_groups in [
            jet_hadron.correlation_hists_delta_phi,
            jet_hadron.correlation_hists_delta_eta
    ]:
        # Help out mypy...
        assert isinstance(correlations_groups,
                          (correlations.CorrelationHistogramsDeltaPhi,
                           correlations.CorrelationHistogramsDeltaEta))
        for _, observable in correlations_groups:
            # Draw the 1D histogram.
            observable.hist.Draw("")
            output_name = observable.hist.GetName() + "_ROOT"
            plot_base.save_plot(jet_hadron.output_info, canvas, output_name)
Exemplo n.º 27
0
def test_save_plot(logging_mixin, setup_save_tests, use_canvas):
    """ Test the wrapper for saving a matplotlib plot. """
    (obj, figure, canvas, expected_filenames) = setup_save_tests
    filename = "filename"
    args = {
        "obj": obj,
        "figure": canvas if use_canvas else figure,
        "output_name": filename,
        "pdf_with_ROOT": True,
    }
    filenames = plot_base.save_plot(**args)

    # Check the result
    if use_canvas:
        canvas.SaveAs.assert_called()
    else:
        figure.savefig.assert_called()
    assert filenames == [
        name.format(filename=filename) for name in expected_filenames
    ]
Exemplo n.º 28
0
def _plot_response_spectra_with_ROOT(
        plot_labels: plot_base.PlotLabels, output_name: str,
        merged_analysis: analysis_objects.JetHBase, pt_hard_analyses: Analyses,
        hist_attribute_name: str, colors: Sequence[Tuple[float, float,
                                                         float]]) -> None:
    """ Plot 1D response spectra with ROOT.

    Args:
        plot_labels: Labels for the plot.
        output_name: Name under which the plot should be stored.
        merged_analysis: Full merged together analysis object.
        pt_hard_analyses: Pt hard dependent analysis objects to be plotted.
        hist_attribute_name: Name of the attribute under which the histogram is stored.
        colors: List of colors to be used for plotting the pt hard spectra.
    """
    # Setup
    canvas = ROOT.TCanvas("canvas", "canvas")
    canvas.SetLogy(True)
    # Legend
    legend = ROOT.TLegend(0.37, 0.55, 0.9, 0.9)
    legend.SetHeader(r"p_{\mathrm{T}}\:\mathrm{bins}", "C")
    # Increase text size
    legend.SetTextSize(0.025)
    # Use two columns because we have a lot of entries.
    legend.SetNColumns(2)
    # Remove the legend border
    legend.SetBorderSize(0)
    # Make the legend transparent
    legend.SetFillStyle(0)

    # First, we plot the merged analysis. This is the sum of the various pt hard bin contributions.
    merged_hist = utils.recursive_getattr(merged_analysis, hist_attribute_name)
    # Apply axis labels (which must be set on the hist)
    plot_labels.apply_labels(merged_hist)
    # Style the merged hist to ensure that it is possible to see the points
    merged_hist.SetMarkerStyle(ROOT.kFullCircle)
    merged_hist.SetMarkerSize(1)
    merged_hist.SetMarkerColor(ROOT.kBlack)
    merged_hist.SetLineColor(ROOT.kBlack)
    # Ensure that the max is never beyond 300 for better presentation.
    max_limit = merged_hist.GetXaxis().GetXmax()
    if max_limit > 300:
        max_limit = 300
    merged_hist.GetXaxis().SetRangeUser(0, max_limit)

    # Label and draw
    legend.AddEntry(merged_hist, "Merged")
    merged_hist.Draw("same")

    # Now, we plot the pt hard dependent hists
    for i, ((key_index, analysis),
            color) in enumerate(zip(pt_hard_analyses.items(), colors)):
        # Setup
        color = ROOT.TColor.GetColor(*color)
        # Determine the proper label.
        label = labels.pt_range_string(
            pt_bin=key_index.pt_hard_bin,
            lower_label="T",
            upper_label="hard",
            only_show_lower_value_for_last_bin=True,
        )

        # Retrieve and style the hist
        hist = utils.recursive_getattr(analysis, hist_attribute_name)
        hist.SetMarkerStyle(ROOT.kFullCircle + i)
        hist.SetMarkerSize(1)
        hist.SetMarkerColor(color)
        hist.SetLineColor(color)

        # Label and draw
        legend.AddEntry(hist, labels.use_label_with_root(label))
        hist.Draw("same")

    # Final presentation settings
    legend.Draw()

    # Save and cleanup
    output_name += "_ROOT"
    plot_base.save_plot(merged_analysis.output_info, canvas, output_name)
Exemplo n.º 29
0
def _plot_response_spectra_with_matplotlib(
        plot_labels: plot_base.PlotLabels, output_name: str,
        merged_analysis: analysis_objects.JetHBase, pt_hard_analyses: Analyses,
        hist_attribute_name: str, colors: Sequence[Tuple[float, float,
                                                         float]]) -> None:
    """ Plot 1D response spectra with matplotlib.

    Args:
        plot_labels: Labels for the plot.
        output_name: Name under which the plot should be stored.
        merged_analysis: Full merged together analysis object.
        pt_hard_analyses: Pt hard dependent analysis objects to be plotted.
        hist_attribute_name: Name of the attribute under which the histogram is stored.
        colors: List of colors to be used for plotting the pt hard spectra.
    """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))
    plot_labels.apply_labels(ax)

    # First, we plot the merged analysis. This is the sum of the various pt hard bin contributions.
    merged_hist = utils.recursive_getattr(merged_analysis, hist_attribute_name)
    merged_hist = histogram.Histogram1D.from_existing_hist(merged_hist)
    ax.errorbar(
        merged_hist.x,
        merged_hist.y,
        yerr=merged_hist.errors,
        label="Merged",
        color="black",
        marker=".",
        linestyle="",
    )

    # Now, we plot the pt hard dependent hists
    for (key_index, analysis), color in zip(pt_hard_analyses.items(), colors):
        # Determine the proper label.
        label = labels.pt_range_string(
            pt_bin=key_index.pt_hard_bin,
            lower_label="T",
            upper_label="hard",
            only_show_lower_value_for_last_bin=True,
        )

        # Plot the histogram.
        hist = utils.recursive_getattr(analysis, hist_attribute_name)
        h = histogram.Histogram1D.from_existing_hist(hist)
        ax.errorbar(
            h.x,
            h.y,
            yerr=h.errors,
            label=label,
            color=color,
            marker=".",
            linestyle="",
        )

    # Final presentation settings
    # Ensure that the max is never beyond 300 for better presentation.
    max_limit = np.max(merged_hist.x)
    if max_limit > 300:
        max_limit = 300
    ax.set_xlim(0, max_limit)
    ax.set_yscale("log")
    ax.legend(loc="best", frameon=False)
    fig.tight_layout()

    # Save and cleanup
    output_name += "_mpl"
    plot_base.save_plot(merged_analysis.output_info, fig, output_name)
    plt.close(fig)
Exemplo n.º 30
0
def _plot_response_matrix_with_matplotlib(
        name: str, x_label: str, y_label: str, output_name: str, hist: Hist,
        plot_errors_hist: bool,
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Underlying function to actually plot a response matrix with matplotlib.

    Args:
        name: Name of the histogram.
        x_label: X axis label.
        y_label: Y axis label.
        output_name: Output name of the histogram.
        hist: The response matrix related 2D hist.
        errors_hist: True if the hist is the response matrix errors hist.
        output_info: Output information.
    Returns:
        None
    """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))

    # Convert the histogram
    X, Y, hist_array = histogram.get_array_from_hist2D(
        hist=hist,
        set_zero_to_NaN=True,
        return_bin_edges=True,
    )

    # Determine and fill args
    kwargs = {}
    # Create a log z axis heat map.
    kwargs["norm"] = matplotlib.colors.LogNorm(vmin=np.nanmin(hist_array),
                                               vmax=np.nanmax(hist_array))
    logger.debug(f"min: {np.nanmin(hist_array)}, max: {np.nanmax(hist_array)}")
    # The colormap that we use is the default from sns.heatmap
    kwargs["cmap"] = plot_base.prepare_colormap(sns.cm.rocket)
    # Label is included so we could use a legend if we want
    kwargs["label"] = name

    logger.debug("kwargs: {}".format(kwargs))

    # Determine the edges
    extent = [np.amin(X), np.amax(X), np.amin(Y), np.amax(Y)]
    # Finally, create the plot
    ax_from_imshow = ax.imshow(hist_array.T,
                               extent=extent,
                               interpolation="nearest",
                               aspect="auto",
                               origin="lower",
                               **kwargs)

    # Add colorbar
    # It needs to be defined on the figure because it is stored in a separate axis.
    fig.colorbar(ax_from_imshow, ax=ax)

    # Final styling
    ax.set_title(name)
    ax.set_xlabel(x_label)
    ax.set_ylabel(y_label)
    fig.tight_layout()

    # Save and cleanup
    output_name += "_mpl"
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)