示例#1
0
    def test_jet_pt_string(self, logging_mixin):
        """ Test the jet pt string generation functions. Each bin (except for the last) is tested.

        The last pt bin is left for a separate test because it is printed differently
        (see ``test_jet_pt_string_for_last_pt_bin()`` for more).
        """
        pt_bins = []
        for i, (min, max) in enumerate(
                zip(self.jet_pt_bins[:-2], self.jet_pt_bins[1:-1])):
            pt_bins.append(
                analysis_objects.JetPtBin(bin=i,
                                          range=params.SelectedRange(min,
                                                                     max)))

        for pt_bin, expected_min, expected_max in zip(pt_bins,
                                                      self.jet_pt_bins[:-2],
                                                      self.jet_pt_bins[1:-1]):
            logger.debug(
                f"Checking bin {pt_bin}, {pt_bin.range}, {type(pt_bin)}")
            assert labels.jet_pt_range_string(
                pt_bin
            ) == r"$%(lower)s < p_{\text{T,jet}}^{\text{ch+ne}} < %(upper)s\:\mathrm{GeV/\mathit{c}}$" % {
                "lower": expected_min,
                "upper": expected_max
            }
示例#2
0
def post_projection_processing_for_2d_correlation(
        hist: Hist,
        normalization_factor: float,
        title_label: str,
        jet_pt: analysis_objects.JetPtBin,
        track_pt: analysis_objects.TrackPtBin,
        rebin_factors: Optional[Tuple[int, int]] = None) -> None:
    """ Basic post processing tasks for a new 2D correlation observable.

    Args:
        hist: Histogram to be post processed.
        normalization_factor: Factor by which the hist should be scaled.
        title_label: Histogram title label.
        jet_pt: Jet pt bin.
        track_pt: Track pt bin.
        rebin_factors: (x rebin factor, y rebin factor). Both values must be specified (can set to 1 if you
            don't want to rebin a particular axis). Default: None.
    Returns:
        None. The histogram is modified in place.
    """
    # If we specify a rebin factor, then rebin.
    if rebin_factors is not None:
        hist.Rebin2D(*rebin_factors)

    # Scale
    hist.Scale(1.0 / normalization_factor)

    # Set title, axis labels
    jet_pt_bins_title = labels.jet_pt_range_string(jet_pt)
    track_pt_bins_title = labels.track_pt_range_string(track_pt)
    hist.SetTitle(
        rf"{title_label}\:\mathrm{{with}}\:{jet_pt_bins_title} \mathrm{{,}} {track_pt_bins_title}"
    )
    hist.GetXaxis().SetTitle(r"$\Delta\varphi$")
    hist.GetYaxis().SetTitle(r"$\Delta\eta$")
示例#3
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)
示例#4
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)
示例#5
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_and_label_1d_signal_and_background_with_matplotlib_on_axis(ax: matplotlib.axes.Axes,
                                                                    jet_hadron: "correlations.Correlations",
                                                                    apply_correlation_scale_factor: bool = True) -> None:
    """ Plot and label the signal and background dominated hists on the given axis.

    This is a helper function so that we don't have to repeat code when we need to plot these hists.
    It can also be used in other modules.

    Args:
        ax: Axis on which the histograms should be plotted.
        jet_hadron: Correlations object from which the delta_phi hists should be retrieved.
        apply_correlation_scale_factor: Whether to scale the histogram by the correlation scale factor.
    Returns:
        None. The given axis is modified.
    """
    # Setup
    hists = jet_hadron.correlation_hists_delta_phi

    h_signal = histogram.Histogram1D.from_existing_hist(hists.signal_dominated.hist)
    if apply_correlation_scale_factor:
        h_signal *= jet_hadron.correlation_scale_factor
    ax.errorbar(
        h_signal.x, h_signal.y, yerr = h_signal.errors,
        label = hists.signal_dominated.type.display_str(), marker = "o", linestyle = "",
    )
    h_background = histogram.Histogram1D.from_existing_hist(hists.background_dominated.hist)
    if apply_correlation_scale_factor:
        h_background *= jet_hadron.correlation_scale_factor
    # Plot with opacity first
    background_plot = ax.errorbar(
        h_background.x, h_background.y, yerr = h_background.errors,
        marker = "o", linestyle = "", alpha = 0.5,
    )
    # Then restrict range and plot without opacity
    near_side = len(h_background.x) // 2
    ax.errorbar(
        h_background.x[:near_side], h_background.y[:near_side], yerr = h_background.errors[:near_side],
        label = hists.background_dominated.type.display_str(),
        marker = "o", linestyle = "", color = background_plot[0].get_color()
    )

    # Set labels.
    ax.set_xlabel(labels.make_valid_latex_string(hists.signal_dominated.hist.GetXaxis().GetTitle()))
    ax.set_ylabel(labels.make_valid_latex_string(hists.signal_dominated.hist.GetYaxis().GetTitle()))
    jet_pt_label = labels.jet_pt_range_string(jet_hadron.jet_pt)
    track_pt_label = labels.track_pt_range_string(jet_hadron.track_pt)
    ax.set_title(fr"Unsubtracted 1D ${hists.signal_dominated.axis.display_str()}$,"
                 f" {jet_hadron.reaction_plane_orientation.display_str()} event plane orient.,"
                 f" {jet_pt_label}, {track_pt_label}")
示例#7
0
    def test_jet_pt_string_for_last_pt_bin(self, logging_mixin):
        """ Test the jet pt string generation function for the last jet pt bin.

        In the case of the last pt bin, we only want to show the lower range.
        """
        pt_bin = len(self.jet_pt_bins) - 2
        jet_pt_bin = analysis_objects.JetPtBin(
            bin=pt_bin,
            range=params.SelectedRange(self.jet_pt_bins[pt_bin],
                                       self.jet_pt_bins[pt_bin + 1]))
        assert labels.jet_pt_range_string(
            jet_pt_bin
        ) == r"$%(lower)s < p_{\text{T,jet}}^{\text{ch+ne}}\:\mathrm{GeV/\mathit{c}}$" % {
            "lower": self.jet_pt_bins[-2]
        }
示例#8
0
def post_creation_processing_for_1d_correlations(
        hist: Hist, normalization_factor: float, rebin_factor: int,
        title_label: str, axis_label: str, jet_pt: analysis_objects.JetPtBin,
        track_pt: analysis_objects.TrackPtBin) -> None:
    """ Basic post processing tasks for a new 1D correlation observable. """
    # Rebin to decrease the fluctuations in the correlations
    # We don't scale by the rebin factor here because we will scale by bin width later.
    # Since we will handle it later, it doesn't make sense to try to preserve normalization here.
    hist.Rebin(rebin_factor)

    # Scale
    hist.Scale(1.0 / normalization_factor)

    # Set title, labels
    jet_pt_bins_title = labels.jet_pt_range_string(jet_pt)
    track_pt_bins_title = labels.track_pt_range_string(track_pt)
    # This won't look so good in ROOT, but that's just because their latex rendering is absolutely atrocious...
    hist.SetTitle(
        rf"{title_label} with {jet_pt_bins_title}, {track_pt_bins_title}")
    hist.GetXaxis().SetTitle(axis_label)
    hist.GetYaxis().SetTitle(fr"$\mathrm{{dN}}/\mathrm{{d}}{axis_label}$")
示例#9
0
def delta_eta_fit(analysis: "correlations.Correlations") -> None:
    """ Plot the delta eta correlations with the fit. """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))

    # Plot both the near side and the away side.
    for (attribute_name, correlation), (fit_attribute_name, fit_object) in \
            zip(analysis.correlation_hists_delta_eta, analysis.fit_objects_delta_eta):
        if attribute_name != fit_attribute_name:
            raise ValueError(
                "Issue extracting hist and pedestal fit object together."
                f"Correlation obj name: {attribute_name}, pedestal fit obj name: {fit_attribute_name}"
            )
        # Setup an individual hist
        h = histogram.Histogram1D.from_existing_hist(correlation.hist)
        label = correlation.type.display_str()

        # Determine the fit range so we can show it in the plot.
        # For example, -1.2 < h.x < -0.8
        negative_restricted_range = (
            (h.x < -1 * analysis.background_dominated_eta_region.min)
            & (h.x > -1 * analysis.background_dominated_eta_region.max))
        # For example, 0.8 < h.x < 1.2
        positive_restricted_range = (
            (h.x > analysis.background_dominated_eta_region.min)
            & (h.x < analysis.background_dominated_eta_region.max))
        restricted_range = negative_restricted_range | positive_restricted_range

        # First plot all of the data with opacity
        data_plot = ax.errorbar(
            h.x,
            h.y,
            yerr=h.errors,
            marker="o",
            linestyle="",
            alpha=0.5,
        )
        # Then plot again without opacity highlighting the fit range.
        ax.errorbar(h.x[restricted_range],
                    h.y[restricted_range],
                    yerr=h.errors[restricted_range],
                    label=label,
                    marker="o",
                    linestyle="",
                    color=data_plot[0].get_color())

        # Next, plot the pedestal following the same format
        # First plot the restricted values
        # We have to plot the fit data in two separate halves to prevent the lines
        # from being connected across the region where were don't fit.
        # Plot the left half
        pedestal_plot = ax.plot(
            h.x[negative_restricted_range],
            fit_object(h.x[negative_restricted_range],
                       **fit_object.fit_result.values_at_minimum),
            label="Pedestal",
        )
        # And then the right half
        ax.plot(
            h.x[positive_restricted_range],
            fit_object(h.x[positive_restricted_range],
                       **fit_object.fit_result.values_at_minimum),
            color=pedestal_plot[0].get_color(),
        )
        # Then plot the errors over the entire range.
        fit_values = fit_object(h.x, **fit_object.fit_result.values_at_minimum)
        fit_errors = fit_object.calculate_errors(h.x)
        ax.fill_between(
            h.x,
            fit_values - fit_errors,
            fit_values + fit_errors,
            facecolor=pedestal_plot[0].get_color(),
            alpha=0.7,
        )
        # Then plot over the entire range using a dashed line.
        ax.plot(
            h.x,
            fit_values,
            linestyle="--",
            color=pedestal_plot[0].get_color(),
        )

        # 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"Unsubtracted 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}_fit")
        # Reset for the next iteration of the loop
        ax.clear()

    # Final cleanup
    plt.close(fig)
示例#10
0
def plot_RP_fit(rp_fit: reaction_plane_fit.fit.ReactionPlaneFit,
                inclusive_analysis: "correlations.Correlations",
                ep_analyses: List[Tuple[Any, "correlations.Correlations"]],
                output_info: analysis_objects.PlottingOutputWrapper,
                output_name: str) -> None:
    """ Basic plot of the reaction plane fit.

    Args:
        rp_fit: Reaction plane fit object.
        inclusive_analysis: Inclusive analysis object. Mainly used for labeling.
        ep_analyses: Event plane dependent correlation analysis objects.
        output_info: Output information.
        output_name: Name of the output plot.
    Returns:
        None. The plot will be saved.
    """
    # Setup
    n_components = len(rp_fit.components)
    fig, axes = plt.subplots(2,
                             n_components,
                             sharey="row",
                             sharex=True,
                             gridspec_kw={"height_ratios": [3, 1]},
                             figsize=(3 * n_components, 6))
    flat_axes = axes.flatten()

    # Plot the fits on the upper panels.
    _plot_rp_fit_components(rp_fit=rp_fit,
                            ep_analyses=ep_analyses,
                            axes=flat_axes[:n_components])
    # Plot the residuals on the lower panels.
    _plot_rp_fit_residuals(rp_fit=rp_fit,
                           ep_analyses=ep_analyses,
                           axes=flat_axes[n_components:])

    # Define upper panel labels.
    # In-plane
    text = labels.track_pt_range_string(inclusive_analysis.track_pt)
    text += "\n" + labels.constituent_cuts()
    text += "\n" + labels.make_valid_latex_string(
        inclusive_analysis.leading_hadron_bias.display_str())
    _add_label_to_rpf_plot_axis(ax=flat_axes[0], label=text)
    # Mid-plane
    text = labels.make_valid_latex_string(
        inclusive_analysis.alice_label.display_str())
    text += "\n" + labels.system_label(
        energy=inclusive_analysis.collision_energy,
        system=inclusive_analysis.collision_system,
        activity=inclusive_analysis.event_activity)
    text += "\n" + labels.jet_pt_range_string(inclusive_analysis.jet_pt)
    text += "\n" + labels.jet_finding()
    _add_label_to_rpf_plot_axis(ax=flat_axes[1], label=text)
    # Out-of-plane
    #text = "Background: $0.8<|\Delta\eta|<1.2$"
    #text += "\nSignal + Background: $|\Delta\eta|<0.6$"
    #_add_label_to_rpf_plot_axis(ax = flat_axes[2], label = text)
    # Inclusive
    text = (r"\chi^{2}/\mathrm{NDF} = "
            f"{rp_fit.fit_result.minimum_val:.1f}/{rp_fit.fit_result.nDOF} = "
            f"{rp_fit.fit_result.minimum_val / rp_fit.fit_result.nDOF:.3f}")
    _add_label_to_rpf_plot_axis(ax=flat_axes[2],
                                label=labels.make_valid_latex_string(text))

    # Define lower panel labels.
    for ax in flat_axes[n_components:]:
        # Increase the frequency of major ticks to once every integer.
        ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=1.0))
        # Add axis labels
        ax.set_xlabel(
            labels.make_valid_latex_string(
                inclusive_analysis.correlation_hists_delta_phi.
                signal_dominated.axis.display_str()))
    # Improve the viewable range for the lower panels.
    # This value is somewhat arbitrarily selected, but seems to work well enough.
    flat_axes[n_components].set_ylim(-0.2, 0.2)

    # Specify shared y axis label
    # Delta phi correlations first
    flat_axes[0].set_ylabel(labels.delta_phi_axis_label())
    # Then label the residual
    flat_axes[n_components].set_ylabel("data - fit / fit")

    # Final adjustments
    fig.tight_layout()
    # Reduce spacing between subplots
    fig.subplots_adjust(hspace=0, wspace=0)
    # Save plot and cleanup
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)
示例#11
0
def rp_fit_subtracted(ep_analyses: List[Tuple[Any,
                                              "correlations.Correlations"]],
                      inclusive_analysis: "correlations.Correlations",
                      output_info: analysis_objects.PlottingOutputWrapper,
                      output_name: str) -> None:
    """ Basic plot of the reaction plane fit subtracted hists.

    Args:
        ep_analyses: Event plane dependent correlation analysis objects.
        inclusive_analysis: Inclusive analysis object. Mainly used for labeling.
        output_info: Output information.
        output_name: Name of the output plot.
    Returns:
        None. The plot will be saved.
    """
    # Setup
    n_components = len(ep_analyses)
    fig, axes = plt.subplots(
        1,
        n_components,
        sharey="row",
        sharex=True,
        #gridspec_kw = {"height_ratios": [3, 1]},
        figsize=(3 * n_components, 6))
    flat_axes = axes.flatten()

    # Plot the fits on the upper panels.
    _plot_rp_fit_subtracted(ep_analyses=ep_analyses,
                            axes=flat_axes[:n_components])

    # Define upper panel labels.
    # In-plane
    text = labels.track_pt_range_string(inclusive_analysis.track_pt)
    text += "\n" + labels.constituent_cuts()
    text += "\n" + labels.make_valid_latex_string(
        inclusive_analysis.leading_hadron_bias.display_str())
    _add_label_to_rpf_plot_axis(ax=flat_axes[0], label=text)
    # Mid-plane
    text = labels.make_valid_latex_string(
        inclusive_analysis.alice_label.display_str())
    text += "\n" + labels.system_label(
        energy=inclusive_analysis.collision_energy,
        system=inclusive_analysis.collision_system,
        activity=inclusive_analysis.event_activity)
    text += "\n" + labels.jet_pt_range_string(inclusive_analysis.jet_pt)
    text += "\n" + labels.jet_finding()
    _add_label_to_rpf_plot_axis(ax=flat_axes[1], label=text)
    # Out-of-plane
    #text = "Background: $0.8<|\Delta\eta|<1.2$"
    #text += "\nSignal + Background: $|\Delta\eta|<0.6$"
    #_add_label_to_rpf_plot_axis(ax = flat_axes[2], label = text)
    _add_label_to_rpf_plot_axis(ax=flat_axes[2],
                                label=labels.make_valid_latex_string(text))

    for ax in flat_axes:
        # Increase the frequency of major ticks to once every integer.
        ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=1.0))
        # Set label
        ax.set_xlabel(labels.make_valid_latex_string(r"\Delta\varphi"))

    flat_axes[0].set_ylabel(
        labels.make_valid_latex_string(labels.delta_phi_axis_label()))
    #jet_pt_label = labels.jet_pt_range_string(inclusive_analysis.jet_pt)
    #track_pt_label = labels.track_pt_range_string(inclusive_analysis.track_pt)
    #ax.set_title(fr"Subtracted 1D ${inclusive_analysis.correlation_hists_delta_phi_subtracted.signal_dominated.axis.display_str()}$,"
    #             f" {inclusive_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()
    # Reduce spacing between subplots
    fig.subplots_adjust(hspace=0, wspace=0)
    # Save plot and cleanup
    plot_base.save_plot(
        output_info, fig,
        f"jetH_delta_phi_{inclusive_analysis.identifier}_rp_subtracted")
    plt.close(fig)
示例#12
0
def plot_RPF_fit_regions(jet_hadron: "correlations.Correlations", filename: str) -> None:
    """ Plot showing highlighted RPF fit regions.

    Args:
        jet_hadron: Main analysis object.
        filename: Filename under which the hist should be saved.
    Returns:
        None
    """
    # Retrieve the hist to be plotted
    # Here we selected the corrected 2D correlation
    hist = jet_hadron.correlation_hists_2d.signal.hist.Clone(f"{jet_hadron.correlation_hists_2d.signal.name}_RPF_scaled")
    hist.Scale(jet_hadron.correlation_scale_factor)

    with sns.plotting_context(context = "notebook", font_scale = 1.5):
        # Perform the plotting
        # The best option for clarify of viewing seems to be to just replace the colors with the overlay colors.
        # mathematical_blending and screen_colors look similar and seem to be the next most promising option.
        (fig, ax) = highlight_RPF.plot_RPF_fit_regions(
            histogram.get_array_from_hist2D(hist),
            highlight_regions = define_highlight_regions(
                signal_dominated_eta_region = jet_hadron.signal_dominated_eta_region,
                background_dominated_eta_region = jet_hadron.background_dominated_eta_region,
                near_side_phi_region = jet_hadron.near_side_phi_region,
            ),
            use_color_overlay = False,
            use_color_screen = False,
            use_mathematical_blending = False,
        )

        # Add additional labeling
        # Axis
        # Needed to fix z axis rotation. See: https://stackoverflow.com/a/21921168
        ax.zaxis.set_rotate_label(False)
        ax.set_zlabel(r"$1/N_{\mathrm{trig}}\mathrm{d^{2}}N/\mathrm{d}\Delta\varphi\mathrm{d}\Delta\eta$", rotation=90)
        # Set the distance from axis to label in pixels.
        # This is not ideal, but clearly tight_layout doesn't work as well for 3D plots
        ax.xaxis.labelpad = 12
        # Visually, dEta looks closer
        ax.yaxis.labelpad = 15
        ax.zaxis.labelpad = 12
        # Overall
        alice_label = labels.make_valid_latex_string(jet_hadron.alice_label.display_str())
        system_label = labels.system_label(
            energy = jet_hadron.collision_energy,
            system = jet_hadron.collision_system,
            activity = jet_hadron.event_activity
        )
        jet_finding = labels.jet_finding()
        constituent_cuts = labels.constituent_cuts()
        leading_hadron = "$" + jet_hadron.leading_hadron_bias.display_str() + "$"
        jet_pt = labels.jet_pt_range_string(jet_hadron.jet_pt)
        assoc_pt = labels.track_pt_range_string(jet_hadron.track_pt)

        # Upper left side
        upper_left_text = ""
        upper_left_text += alice_label
        upper_left_text += "\n" + system_label
        upper_left_text += "\n" + jet_pt
        upper_left_text += "\n" + jet_finding

        # Upper right side
        upper_right_text = ""
        upper_right_text += leading_hadron
        upper_right_text += "\n" + constituent_cuts
        upper_right_text += "\n" + assoc_pt

        # Need a different text function since we have a 3D axis
        ax.text2D(0.01, 0.99, upper_left_text,
                  horizontalalignment = "left",
                  verticalalignment = "top",
                  multialignment = "left",
                  transform = ax.transAxes)
        ax.text2D(0.00, 0.00, upper_right_text,
                  horizontalalignment = "left",
                  verticalalignment = "bottom",
                  multialignment = "left",
                  transform = ax.transAxes)

        # Finish up
        plot_base.save_plot(jet_hadron.output_info, fig, filename)
        plt.close(fig)
示例#13
0
def plot_2d_correlations(jet_hadron: "correlations.Correlations") -> None:
    """ Plot the 2D correlations. """
    canvas = ROOT.TCanvas("canvas2D", "canvas2D")

    # Iterate over 2D hists
    for name, observable in jet_hadron.correlation_hists_2d:
        hist = observable.hist.Clone(f"{observable.name}_scaled")
        logger.debug(f"name: {name}, hist: {hist}")
        # We don't want to scale the mixed event hist because we already determined the normalization
        if "mixed" not in observable.type:
            hist.Scale(jet_hadron.correlation_scale_factor)

        # We don't need the title with all of the labeling
        hist.SetTitle("")

        # Draw plot
        hist.Draw("surf2")

        # Label axes
        hist.GetXaxis().CenterTitle(True)
        hist.GetXaxis().SetTitleSize(0.08)
        hist.GetXaxis().SetLabelSize(0.06)
        hist.GetYaxis().CenterTitle(True)
        hist.GetYaxis().SetTitleSize(0.08)
        # If I remove this, it looks worse, even though this is not supposed to do anything
        hist.GetYaxis().SetTitleOffset(1.2)
        hist.GetYaxis().SetLabelSize(0.06)
        hist.GetZaxis().CenterTitle(True)
        hist.GetZaxis().SetTitleSize(0.06)
        hist.GetZaxis().SetLabelSize(0.05)
        hist.GetZaxis().SetTitleOffset(0.8)
        canvas.SetLeftMargin(0.13)

        if "mixed" in observable.type:
            hist.GetZaxis().SetTitle(r"$a(\Delta\varphi,\Delta\eta)$")
            hist.GetZaxis().SetTitleOffset(0.9)
        else:
            z_title = r"$1/N_{\mathrm{trig}}\mathrm{d^{2}}N%(label)s/\mathrm{d}\Delta\varphi\mathrm{d}\Delta\eta$"
            if "signal" in observable.type:
                z_title = z_title % {"label": ""}
            else:
                z_title = z_title % {"label": r"_{\mathrm{raw}}"}
                # Decrease size so it doesn't overlap with the other labels
                hist.GetZaxis().SetTitleSize(0.05)

            hist.GetZaxis().SetTitle(z_title)

        # Add labels
        # PDF DOES NOT WORK HERE: https://root-forum.cern.ch/t/latex-sqrt-problem/17442/15
        # Instead, print to EPS and then convert to PDF
        alice_label = labels.make_valid_latex_string(jet_hadron.alice_label.display_str())
        system_label = labels.system_label(
            energy = jet_hadron.collision_energy,
            system = jet_hadron.collision_system,
            activity = jet_hadron.event_activity
        )
        jet_finding = labels.jet_finding()
        constituent_cuts = labels.constituent_cuts()
        leading_hadron = "$" + jet_hadron.leading_hadron_bias.display_str() + "$"
        jet_pt = labels.jet_pt_range_string(jet_hadron.jet_pt)
        assoc_pt = labels.track_pt_range_string(jet_hadron.track_pt)
        logger.debug(f"label: {alice_label}, system_label: {system_label}, constituent_cuts: {constituent_cuts}, leading_hadron: {leading_hadron}, jet_pt: {jet_pt}, assoc_pt: {assoc_pt}")

        tex = ROOT.TLatex()
        tex.SetTextSize(0.04)
        # Upper left side
        tex.DrawLatexNDC(.005, .96, labels.use_label_with_root(alice_label))
        tex.DrawLatexNDC(.005, .91, labels.use_label_with_root(system_label))
        tex.DrawLatexNDC(.005, .86, labels.use_label_with_root(jet_pt))
        tex.DrawLatexNDC(.005, .81, labels.use_label_with_root(jet_finding))

        # Upper right side
        tex.DrawLatexNDC(.67, .96, labels.use_label_with_root(assoc_pt))
        tex.DrawLatexNDC(.7275, .91, labels.use_label_with_root(leading_hadron))
        tex.DrawLatexNDC(.73, .86, labels.use_label_with_root(constituent_cuts))

        # Save plot
        plot_base.save_plot(jet_hadron.output_info, canvas, observable.name)

        # Draw as colz to view more precisely
        hist.Draw("colz")
        plot_base.save_plot(jet_hadron.output_info, canvas, observable.name + "_colz")

        canvas.Clear()
示例#14
0
def delta_eta_with_gaussian(analysis: "correlations.Correlations") -> None:
    """ Plot the subtracted delta eta near-side. """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))

    for (attribute_name, width_obj), (correlation_attribute_name, correlation) in \
            zip(analysis.widths_delta_eta, analysis.correlation_hists_delta_eta_subtracted):
        # Setup
        # Sanity check
        if attribute_name != correlation_attribute_name:
            raise ValueError(
                "Issue extracting width and hist together."
                f"Width obj name: {attribute_name}, hist obj name: {correlation_attribute_name}"
            )
        # Plot only the near side for now because the away-side doesn't have a gaussian shape
        if attribute_name == "away_side":
            continue

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

        # Plot the fit
        gauss = width_obj.fit_object(h.x,
                                     **width_obj.fit_result.values_at_minimum)
        fit_plot = ax.plot(
            h.x,
            gauss,
            label=
            fr"Gaussian fit: $\mu = $ {width_obj.mean:.2f}, $\sigma = $ {width_obj.width:.2f}",
        )
        # Fill in the error band.
        error = width_obj.fit_object.calculate_errors(x=h.x)
        ax.fill_between(
            h.x,
            gauss - error,
            gauss + error,
            facecolor=fit_plot[0].get_color(),
            alpha=0.5,
        )

        # 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}_width_{attribute_name}_fit")
        # Reset for the next iteration of the loop
        ax.clear()

    # Final cleanup.
    plt.close(fig)
示例#15
0
def _extracted_values(
    analyses: Mapping[Any, "correlations.Correlations"],
    selected_iterables: Mapping[str, Sequence[Any]],
    extract_value_func: Callable[["correlations.Correlations"],
                                 analysis_objects.ExtractedObservable],
    plot_labels: plot_base.PlotLabels,
    output_name: str,
    output_info: analysis_objects.PlottingOutputWrapper,
    projection_range_func: Optional[Callable[["correlations.Correlations"],
                                             str]] = None,
    extraction_range_func: Optional[Callable[["correlations.Correlations"],
                                             str]] = None
) -> None:
    """ Plot extracted values.

    Note:
        It's best to fully define the ``extract_value_func`` function even though it can often be easily accomplished
        with a lambda because only a full function definition can use explicit type checking. Since this function uses
        a variety of different sources for the data, this type checking is particularly helpful. So writing a full
        function with full typing is strongly preferred to ensure that we get it right.

    Args:
        analyses: Correlation analyses.
        selected_iterables: Iterables that were used in constructing the analysis objects. We use them to iterate
            over some iterators in a particular order (particularly the reaction plane orientation).
        extract_value_func: Function to retrieve the extracted value and error.
        plot_labels: Titles and axis labels for the plot.
        output_name: Base of name under which the plot will be stored.
        output_info: Information needed to determine where to store the plot.
        projection_range_func: Function which will provide the projection range of the extracted value given
            the inclusive object. Default: None.
        extraction_range_func: Function which will provide the extraction range of the extracted value given
            the inclusive object. Default: None.
    """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))
    # Specify plotting properties
    # color, marker, fill marker or not
    # NOTE: Fill marker is specified when plotting because of a matplotlib bug
    # NOTE: This depends on iterating over the EP orientation in the exact manner specified below.
    ep_plot_properties = {
        # black, diamond, no fill
        params.ReactionPlaneOrientation.inclusive: ("black", "D", "none"),
        # blue = "C0", square, fill
        params.ReactionPlaneOrientation.in_plane: ("tab:blue", "s", "full"),
        # green = "C2", triangle up, fill
        params.ReactionPlaneOrientation.mid_plane: ("tab:green", "^", "full"),
        # red = "C3", circle, fill
        params.ReactionPlaneOrientation.out_of_plane: ("tab:red", "o", "full"),
    }
    cyclers = []
    plot_property_values = list(ep_plot_properties.values())
    for i, prop in enumerate(["color", "marker", "fillstyle"]):
        cyclers.append(cycler(prop, [p[i] for p in plot_property_values]))
    # We skip the fillstyle because apparently it doesn't work with the cycler at the moment due to a bug...
    # They didn't implement their add operation to handle 0, so we have to give it the explicit start value.
    combined_cyclers = sum(cyclers[1:-1], cyclers[0])
    ax.set_prop_cycle(combined_cyclers)

    # Used for labeling purposes. The values that are used are identical for all analyses.
    inclusive_analysis: Optional["correlations.Correlations"] = None
    for displace_index, ep_orientation in enumerate(
            selected_iterables["reaction_plane_orientation"]):
        # Store the values to be plotted
        values: Dict[analysis_objects.PtBin,
                     analysis_objects.ExtractedObservable] = {}
        for key_index, analysis in \
                analysis_config.iterate_with_selected_objects(
                    analyses, reaction_plane_orientation = ep_orientation
                ):
            # Store each extracted value.
            values[analysis.track_pt] = extract_value_func(analysis)
            # These are both used for labeling purposes and are identical for all analyses that are iterated over.
            if ep_orientation == params.ReactionPlaneOrientation.inclusive and inclusive_analysis is None:
                inclusive_analysis = analysis

        # Plot the values
        bin_centers = np.array([k.bin_center for k in values])
        bin_centers = bin_centers + displace_index * 0.05
        ax.errorbar(
            bin_centers,
            [v.value for v in values.values()],
            yerr=[v.error for v in values.values()],
            label=ep_orientation.display_str(),
            linestyle="",
            fillstyle=ep_plot_properties[ep_orientation][2],
        )

    # Help out mypy...
    assert inclusive_analysis is not None

    # Labels.
    # General
    text = labels.make_valid_latex_string(
        inclusive_analysis.alice_label.display_str())
    text += "\n" + labels.system_label(
        energy=inclusive_analysis.collision_energy,
        system=inclusive_analysis.collision_system,
        activity=inclusive_analysis.event_activity)
    text += "\n" + labels.jet_pt_range_string(inclusive_analysis.jet_pt)
    text += "\n" + labels.jet_finding()
    text += "\n" + labels.constituent_cuts()
    text += "\n" + labels.make_valid_latex_string(
        inclusive_analysis.leading_hadron_bias.display_str())
    # Deal with projection range, extraction range string.
    additional_label = _proj_and_extract_range_label(
        inclusive_analysis=inclusive_analysis,
        projection_range_func=projection_range_func,
        extraction_range_func=extraction_range_func,
    )
    if additional_label:
        text += "\n" + additional_label
    # Finally, add the text to the axis.
    ax.text(0.97,
            0.97,
            text,
            horizontalalignment="right",
            verticalalignment="top",
            multialignment="right",
            transform=ax.transAxes)
    # Axes and titles
    ax.set_xlabel(
        labels.make_valid_latex_string(labels.track_pt_display_label()))
    # Apply any specified labels
    if plot_labels.title is not None:
        plot_labels.title = plot_labels.title + f" for {labels.jet_pt_range_string(inclusive_analysis.jet_pt)}"
    plot_labels.apply_labels(ax)
    ax.legend(loc="center right", frameon=False)

    # Final adjustments
    fig.tight_layout()
    # Save plot and cleanup
    plot_base.save_plot(
        output_info, fig,
        f"{output_name}_{inclusive_analysis.jet_pt_identifier}")
    plt.close(fig)
示例#16
0
def delta_phi_with_gaussians(analysis: "correlations.Correlations") -> None:
    """ Plot the subtracted delta phi correlation with gaussian fits to the near and away side. """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))
    correlation = analysis.correlation_hists_delta_phi_subtracted.signal_dominated
    h = correlation.hist

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

    # Plot the fit.
    for attribute_name, width_obj in analysis.widths_delta_phi:
        # Setup
        # Convert the attribute name to display better. Ex: "near_side" -> "Near side"
        attribute_display_name = attribute_name.replace("_", " ").capitalize()
        # We only want to plot the fit over the range that it was fit.
        restricted_range = (h.x > width_obj.fit_object.fit_range.min) & (
            h.x < width_obj.fit_object.fit_range.max)
        x = h.x[restricted_range]

        # Plot the fit
        gauss = width_obj.fit_object(x,
                                     **width_obj.fit_result.values_at_minimum)
        fit_plot = ax.plot(
            x,
            gauss,
            label=
            fr"{attribute_display_name} gaussian fit: $\mu = $ {width_obj.mean:.2f}"
            fr", $\sigma = $ {width_obj.width:.2f}",
        )
        # Fill in the error band.
        error = width_obj.fit_object.calculate_errors(x=x)
        ax.fill_between(
            x,
            gauss - error,
            gauss + error,
            facecolor=fit_plot[0].get_color(),
            alpha=0.5,
        )

        # This means that we extracted values from the RP fit. Let's also plot them for comparison
        if width_obj.fit_args != {}:
            args = dict(width_obj.fit_result.values_at_minimum)
            args.update({
                # Help out mypy...
                k: cast(float, v)
                for k, v in width_obj.fit_args.items() if "error_" not in k
            })
            rpf_gauss = width_obj.fit_object(x, **args)
            rp_fit_plot = ax.plot(
                x,
                rpf_gauss,
                label=
                fr"RPF {attribute_display_name} gaussian fit: $\mu = $ {width_obj.mean:.2f}"
                fr", $\sigma = $ {width_obj.fit_args['width']:.2f}",
            )
            # Fill in the error band.
            # NOTE: Strictly speaking, this error band isn't quite right (since it is dependent on the fit result
            # of the actual width fit), but I think it's fine for these purposes.
            error = width_obj.fit_object.calculate_errors(x=x)
            ax.fill_between(
                x,
                rpf_gauss - error,
                rpf_gauss + error,
                facecolor=rp_fit_plot[0].get_color(),
                alpha=0.5,
            )

    # Labels.
    ax.set_xlabel(
        labels.make_valid_latex_string(correlation.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 ${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_phi_{analysis.identifier}_width_signal_dominated_fit")
    plt.close(fig)