Beispiel #1
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)
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)
Beispiel #3
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)
Beispiel #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)
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)
Beispiel #6
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}")
Beispiel #8
0
def delta_eta_plot_projection_range_string(
        inclusive_analysis: "correlations.Correlations") -> str:
    """ Provides a string that describes the delta phi projection range for delta eta plots. """
    # The limit is almost certainly a multiple of pi, so we try to express it more naturally
    # as a value like pi/2 or 3*pi/2
    value = _find_pi_coefficient(
        value=inclusive_analysis.near_side_phi_region.max)
    return labels.make_valid_latex_string(fr"$|\Delta\varphi|<{value}$")
Beispiel #9
0
    def near_side_extraction_range(
            analysis: "correlations.Correlations") -> str:
        """ Helper function to provide the yield extraction range.

        We want to extract the value from the hist itself in case the binning is off (it shouldn't be).
        """
        # Setup
        min_value, max_value = _find_extraction_range_min_and_max_in_hist(
            h=analysis.correlation_hists_delta_eta_subtracted.near_side.hist,
            extraction_range=analysis.yields_delta_eta.near_side.
            extraction_range,
        )
        # Due to floating point rounding issues, we need to apply our rounding trick here.
        return labels.make_valid_latex_string(
            r"\text{Yield extraction range:}\:"
            fr" |\Delta\eta| < {int(round(max_value * 10)) / 10}")
Beispiel #10
0
    def near_side_extraction_range(
            analysis: "correlations.Correlations") -> str:
        """ Helper function to provide the yield extraction range.

        We want to extract the value from the hist itself in case the binning is off (it shouldn't be).
        """
        # Setup
        min_value, max_value = _find_extraction_range_min_and_max_in_hist(
            h=analysis.correlation_hists_delta_phi_subtracted.signal_dominated.
            hist,
            extraction_range=analysis.yields_delta_phi.near_side.
            extraction_range,
        )
        return labels.make_valid_latex_string(
            r"\text{Yield extraction range:}\:|\Delta\varphi| < " +
            f"{_find_pi_coefficient(max_value)}")
Beispiel #11
0
def delta_phi_away_side_yields(
        analyses: Mapping[Any, "correlations.Correlations"],
        selected_iterables: Mapping[str, Sequence[Any]],
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the delta phi away-side yields. """
    def away_side_yields(
        analysis: "correlations.Correlations"
    ) -> analysis_objects.ExtractedObservable:
        """ Helper function to provide the ExtractedObservable. """
        return analysis.yields_delta_phi.away_side.value

    def away_side_extraction_range(
            analysis: "correlations.Correlations") -> str:
        """ Helper function to provide the yield extraction range.

        We want to extract the value from the hist itself in case the binning is off (it shouldn't be).
        """
        # Setup
        min_value, max_value = _find_extraction_range_min_and_max_in_hist(
            h=analysis.correlation_hists_delta_phi_subtracted.signal_dominated.
            hist,
            extraction_range=analysis.yields_delta_phi.away_side.
            extraction_range,
        )
        return labels.make_valid_latex_string(
            r"\text{Yield extraction range:}\:"
            fr" {_find_pi_coefficient(min_value)}"
            r" < |\Delta\varphi| <"
            fr" {_find_pi_coefficient(max_value)}")

    _extracted_values(
        analyses=analyses,
        selected_iterables=selected_iterables,
        extract_value_func=away_side_yields,
        plot_labels=plot_base.PlotLabels(
            y_label=labels.make_valid_latex_string(
                fr"\mathrm{{d}}N/\mathrm{{d}}{labels.pt_display_label()} ({labels.momentum_units_label_gev()})^{{-1}}",
            ),
            title="Away-side yield",
        ),
        output_name="yields_delta_phi_away_side",
        output_info=output_info,
        projection_range_func=delta_phi_plot_projection_range_string,
        extraction_range_func=away_side_extraction_range,
    )
Beispiel #12
0
def delta_eta_near_side_yields(
        analyses: Mapping[Any, "correlations.Correlations"],
        selected_iterables: Mapping[str, Sequence[Any]],
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the delta eta near-side yields. """
    def near_side_widths(
        analysis: "correlations.Correlations"
    ) -> analysis_objects.ExtractedObservable:
        """ Helper function to provide the ExtractedObservable. """
        return analysis.yields_delta_eta.near_side.value

    def near_side_extraction_range(
            analysis: "correlations.Correlations") -> str:
        """ Helper function to provide the yield extraction range.

        We want to extract the value from the hist itself in case the binning is off (it shouldn't be).
        """
        # Setup
        min_value, max_value = _find_extraction_range_min_and_max_in_hist(
            h=analysis.correlation_hists_delta_eta_subtracted.near_side.hist,
            extraction_range=analysis.yields_delta_eta.near_side.
            extraction_range,
        )
        # Due to floating point rounding issues, we need to apply our rounding trick here.
        return labels.make_valid_latex_string(
            r"\text{Yield extraction range:}\:"
            fr" |\Delta\eta| < {int(round(max_value * 10)) / 10}")

    _extracted_values(
        analyses=analyses,
        selected_iterables=selected_iterables,
        extract_value_func=near_side_widths,
        plot_labels=plot_base.PlotLabels(
            y_label=labels.make_valid_latex_string(
                fr"\mathrm{{d}}N/\mathrm{{d}}{labels.pt_display_label()} ({labels.momentum_units_label_gev()})^{{-1}}",
            ),
            title="Near-side yield",
        ),
        output_name="yields_delta_eta_near_side",
        output_info=output_info,
        projection_range_func=delta_eta_plot_projection_range_string,
        extraction_range_func=near_side_extraction_range,
    )
Beispiel #13
0
def _reference_v2_a_data(
        reference_data: ReferenceData, ax: matplotlib.axes.Axes,
        selected_analysis_options: params.SelectedAnalysisOptions) -> None:
    # Determine the centrality key (because they don't map cleanly onto our centrality ranges)
    reference_data_centrality_map = {
        params.EventActivity.central: "0-5",
        params.EventActivity.semi_central: "30-40",
    }
    centrality_label = reference_data_centrality_map[
        selected_analysis_options.event_activity]
    # Retrieve the data
    hist = reference_data["ptDependent"][centrality_label]

    # Plot and label it on the existing axis.
    ax.errorbar(
        hist.x,
        hist.y,
        yerr=hist.errors,
        marker="o",
        linestyle="",
        label=fr"ALICE {centrality_label}\% " +
        labels.make_valid_latex_string("v_{2}") + r"\{2, $| \Delta\eta |>1$\}",
    )
Beispiel #14
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)
Beispiel #15
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)
Beispiel #16
0
def _matrix_values(free_parameters: Sequence[str], matrix: Dict[Tuple[str,
                                                                      str],
                                                                float],
                   output_info: analysis_objects.PlottingOutputWrapper,
                   output_name: str) -> None:
    """ Plot the RP fit covariance matrix.

    Code substantially improved by using the information at
    https://matplotlib.org/gallery/images_contours_and_fields/image_annotated_heatmap.html

    Args:
        free_parameters: Names of the free parameters used in the fit (which will be included
            in the plot).
        matrix: Matrix values to plot. Should be either the correlation matrix or the covariance
            matrix.
        output_info: Output information.
        output_name: Name of the output plot.
    Returns:
        None. The covariance matrix is saved.
    """
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))
    # Move x labels to top to follow convention
    ax.tick_params(top=True, bottom=False, labeltop=True, labelbottom=False)
    # Create a map from the labels to valid LaTeX for improved presentation
    improved_labeling_map = {
        "ns_amplitude": "A_{NS}",
        "as_amplitude": "A_{AS}",
        "ns_sigma": r"\sigma_{NS}",
        "as_sigma": r"\sigma_{AS}",
        "BG": r"\text{Signal background}",
        "v1": "v_{1}",
        "v2_t": "v_{2}^{t}",
        "v2_a": "v_{2}^{a}",
        "v3": "v_{3}^{2}",
        "v4_t": "v_{4}^{t}",
        "v4_a": "v_{4}^{a}",
        "B": r"\text{RPF Background}",
    }
    # Ensure that the strings are valid LaTeX
    improved_labeling_map = {
        k: labels.make_valid_latex_string(v)
        for k, v in improved_labeling_map.items()
    }
    # Fixed parameters aren't in the covariance matrix.
    number_of_parameters = len(free_parameters)

    # Put the values into a form that can actually be plotted. Note that this assumes a square matrix.
    matrix_values = np.array(list(matrix.values()))
    matrix_values = matrix_values.reshape(number_of_parameters,
                                          number_of_parameters)

    # Plot the matrix
    im = ax.imshow(matrix_values, cmap="viridis")

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

    # Axis labeling
    parameter_labels = [b for a, b in list(matrix)[:number_of_parameters]]
    parameter_labels = [improved_labeling_map[l] for l in parameter_labels]
    # Show all axis ticks and then label them
    # The first step of settings the yticks is required according to the matplotlib docs
    ax.set_xticks(range(number_of_parameters))
    ax.set_yticks(range(number_of_parameters))
    # Also rotate the x-axis labels so they are all readable.
    ax.set_xticklabels(parameter_labels,
                       rotation=-30,
                       rotation_mode="anchor",
                       horizontalalignment="right")
    ax.set_yticklabels(parameter_labels)

    # Turn spines off and create white grid
    for edge, spine in ax.spines.items():
        spine.set_visible(False)
    # Use minor ticks to put a white border between each value
    ax.set_xticks(np.arange(number_of_parameters + 1) - .5, minor=True)
    ax.set_yticks(np.arange(number_of_parameters + 1) - .5, minor=True)
    ax.grid(which="minor", color="w", linestyle='-', linewidth=3)
    ax.tick_params(which="minor", bottom=False, left=False)
    # Have outward ticks just for this plot. They seem to look better here
    ax.tick_params(direction="out")

    # Label values in each element
    threshold_for_changing_label_colors = im.norm(np.max(matrix_values)) / 2
    text_colors = ["white", "black"]
    for i in range(number_of_parameters):
        for j in range(number_of_parameters):
            color = text_colors[im.norm(matrix_values[i, j]) >
                                threshold_for_changing_label_colors]
            im.axes.text(j,
                         i,
                         f"{matrix_values[i, j]:.1f}",
                         horizontalalignment="center",
                         verticalalignment="center",
                         color=color)

    # Final adjustments
    fig.tight_layout()
    # Save plot and cleanup
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)
Beispiel #17
0
def fit_parameters_vs_assoc_pt(
        fit_objects: FitObjects,
        selected_analysis_options: params.SelectedAnalysisOptions,
        reference_data_path: str,
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the extracted fit parameters.

    Args:
        fit_objects: Fit objects whose parameters will be plotted.
        selected_analysis_options: Selected analysis options to be used for labeling.
        reference_data_path: Path to the reference data.
        output_info: Output information.
    """
    # Extract data from the reference dataset. We extract it here to save file IO.
    reference_data_path = reference_data_path.format(
        **dict(selected_analysis_options),
        #collision_energy = selected_analysis_options.collision_energy
    )
    try:
        reference_data: Mapping[str, Mapping[str, histogram.Histogram1D]] = {}
        with open(reference_data_path, "r") as f:
            y = yaml.yaml(modules_to_register=[histogram])
            reference_data = y.load(f)
    except IOError:
        # We will just work with the empty reference data.
        pass

    pt_assoc_label = labels.make_valid_latex_string(
        f"{labels.track_pt_display_label()} ({labels.momentum_units_label_gev()})"
    )
    prefix = "fit_parameter"
    parameters = [
        ParameterInfo(
            name="v2_t",
            output_name=f"{prefix}_v2t",
            labels=plot_base.PlotLabels(title=r"Jet $v_{2}$",
                                        x_label=pt_assoc_label),
        ),
        ParameterInfo(
            name="v2_a",
            output_name=f"{prefix}_v2a",
            labels=plot_base.PlotLabels(title=r"Associated hadron $v_{2}$",
                                        x_label=pt_assoc_label),
            plot_reference_data_func=_reference_v2_a_data,
        ),
        ParameterInfo(
            name="v3",
            output_name=f"{prefix}_v3",
            labels=plot_base.PlotLabels(title=r"$v_{3}$",
                                        x_label=pt_assoc_label),
        ),
        ParameterInfo(
            name="v4_t",
            output_name=f"{prefix}_v4t",
            labels=plot_base.PlotLabels(title=r"Jet $v_{4}$",
                                        x_label=pt_assoc_label),
        ),
        ParameterInfo(
            name="v4_a",
            output_name=f"{prefix}_v4a",
            labels=plot_base.PlotLabels(title=r"Associated hadron $v_{4}$",
                                        x_label=pt_assoc_label),
        ),
        ParameterInfo(
            name="BG",
            output_name=f"{prefix}_background",
            labels=plot_base.PlotLabels(title=r"Effective RPF background",
                                        x_label=pt_assoc_label),
        ),
    ]

    # Plot the signal parameters (but only if they exist).
    fit_obj = next(iter(fit_objects.values()))
    if "ns_amplitude" in fit_obj.fit_result.parameters:
        parameters.append(
            ParameterInfo(
                name="ns_amplitude",
                output_name=f"{prefix}_ns_amplitude",
                labels=plot_base.PlotLabels(
                    title=r"Near side gaussian amplitude",
                    x_label=pt_assoc_label),
            ))
    if "ns_sigma" in fit_obj.fit_result.parameters:
        parameters.append(
            ParameterInfo(
                name="ns_sigma",
                output_name=f"{prefix}_ns_sigma",
                labels=plot_base.PlotLabels(
                    title=r"Near side $\sigma$",
                    x_label=pt_assoc_label,
                    y_label=r"$\sigma_{\text{ns}}$",
                ),
            ))
    if "as_amplitude" in fit_obj.fit_result.parameters:
        parameters.append(
            ParameterInfo(
                name="as_amplitude",
                output_name=f"{prefix}_as_amplitude",
                labels=plot_base.PlotLabels(
                    title=r"Away side gaussian amplitude",
                    x_label=pt_assoc_label),
            ))
    if "as_sigma" in fit_obj.fit_result.parameters:
        parameters.append(
            ParameterInfo(
                name="as_sigma",
                output_name=f"{prefix}_as_sigma",
                labels=plot_base.PlotLabels(title=r"Away side $\sigma$",
                                            x_label=pt_assoc_label,
                                            y_label=r"$\sigma_{\text{as}}$"),
            ))

    for parameter in parameters:
        _plot_fit_parameter_vs_assoc_pt(
            fit_objects=fit_objects,
            parameter=parameter,
            reference_data=reference_data,
            selected_analysis_options=selected_analysis_options,
            output_info=output_info)
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)
Beispiel #19
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)
Beispiel #20
0
def delta_phi_plot_projection_range_string(
        inclusive_analysis: "correlations.Correlations") -> str:
    """ Provides a string that describes the delta eta projection range for delta phi plots. """
    return labels.make_valid_latex_string(
        fr"$|\Delta\eta|<{inclusive_analysis.signal_dominated_eta_region.max}$"
    )
Beispiel #21
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)
Beispiel #22
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)
def plot_particle_level_spectra(
        ep_analyses_iter: Iterator[Tuple[Any,
                                         "response_matrix.ResponseMatrix"]],
        output_info: analysis_objects.PlottingOutputWrapper,
        plot_with_ROOT: bool = False) -> None:
    """ Plot the particle level spectra associated with the response.

    Args:
        ep_analyses: The event plane dependent final response matrices.
        output_info: Output information.
        plot_with_ROOT: True if the plot should be done via ROOT. Default: False
    Returns:
        None. The spectra are plotted and saved.
    """
    # Pull out the dict because we need to grab individual analyses for some labeling information, which doesn't
    # play well with generators (the generator will be exhausted).
    ep_analyses = dict(ep_analyses_iter)

    # Determine the general and plot labels
    # First, we need some variables to define the general labels, so we retrieve the inclusive analysis.
    # All of the parameters retrieved here are shared by all analyses.
    inclusive = next(iter(ep_analyses.values()))
    # Then we define some additional helper variables
    particle_level_spectra_bin = inclusive.task_config[
        "particle_level_spectra"]["particle_level_spectra_bin"]
    embedded_additional_label = inclusive.event_activity.display_str()

    # General labels
    general_labels = {
        "alice_and_collision_energy":
        rf"{inclusive.alice_label.display_str()}\:{inclusive.collision_energy.display_str()}",
        "collision_system_and_event_activity":
        rf"{inclusive.collision_system.display_str(embedded_additional_label = embedded_additional_label)}",
        "detector_pt_range":
        labels.pt_range_string(
            particle_level_spectra_bin,
            lower_label="T,jet",
            upper_label="det",
        ),
        "constituent_cuts":
        labels.constituent_cuts(additional_label="det"),
        "leading_hadron_bias":
        inclusive.leading_hadron_bias.display_str(additional_label="det"),
        "jet_finding":
        labels.jet_finding(),
    }
    # Ensure that each line is a valid latex line.
    # The heuristic is roughly that full statements (such as jet_finding) are already wrapped in "$",
    # while partial statements, such as the leading hadron bias, event activity, etc are not wrapped in "$".
    # This is due to the potential for such "$" to interfere with including those partial statements in other
    # statements. As an example, it would be impossible to use the ``embedded_additional_label`` above if the
    # ``event_activity`` included "$".
    for k, v in general_labels.items():
        general_labels[k] = labels.make_valid_latex_string(v)

    # Plot labels
    y_label = r"\mathrm{d}N/\mathrm{d}p_{\mathrm{T}}"
    if inclusive.task_config["particle_level_spectra"]["normalize_by_n_jets"]:
        y_label = r"(1/N_{\mathrm{jets}})" + y_label
        y_label = y_label
    if inclusive.task_config["particle_level_spectra"][
            "normalize_at_selected_jet_pt_bin"]:
        y_label = r"\mathrm{Arb. Units}"
    plot_labels = plot_base.PlotLabels(
        title="",
        x_label=
        fr"${labels.jet_pt_display_label(upper_label = 'part')}\:({labels.momentum_units_label_gev()})$",
        y_label=labels.make_valid_latex_string(y_label),
    )

    # Finally, we collect our arguments for the plotting functions.
    kwargs: Dict[str, Any] = {
        "ep_analyses": ep_analyses,
        "output_name": "particle_level_spectra",
        "output_info": output_info,
        "general_labels": general_labels,
        "plot_labels": plot_labels,
    }

    if plot_with_ROOT:
        _plot_particle_level_spectra_with_ROOT(**kwargs)
    else:
        _plot_particle_level_spectra_with_matplotlib(**kwargs)
Beispiel #24
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)
def test_make_valid_latex_string(logging_mixin, value: str, expected: str):
    """ Test for making a string into a valid latex string. """
    assert labels.make_valid_latex_string(value) == expected
Beispiel #26
0
def track_eta_phi(hist: Hist, event_activity: params.EventActivity,
                  output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot track eta phi.

    Also include an annotation of the EMCal eta, phi location.

    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
    fig, ax = plt.subplots(figsize=(8, 6))
    x, y, hist_array = histogram.get_array_from_hist2D(
        hist,
        set_zero_to_NaN=True,
        return_bin_edges=True,
    )

    plot = ax.imshow(
        hist_array.T,
        extent=[np.amin(x), np.amax(x),
                np.amin(y), np.amax(y)],
        interpolation="nearest",
        aspect="auto",
        origin="lower",
        # Intentionally stretch the scale near the top of the range to emphasise the inefficiency.
        norm=matplotlib.colors.Normalize(vmin=np.nanmax(hist_array) * 0.8,
                                         vmax=np.nanmax(hist_array) * 0.9),
        cmap="viridis",
    )
    # Draw the colorbar based on the drawn axis above.
    # NOTE: This can cause the warning:
    #       '''
    #       matplotlib/colors.py:1031: RuntimeWarning: invalid value encountered in less_equal
    #           mask |= resdat <= 0"
    #       '''
    #       The warning is due to the nan we introduced above. It can safely be ignored
    #       See: https://stackoverflow.com/a/34955622
    #       (Could suppress, but I don't feel it's necessary at the moment)
    fig.colorbar(plot)

    # Draw EMCal boundaries
    r = matplotlib.patches.Rectangle(
        xy=(-0.7, 80 * np.pi / 180),
        width=0.7 + 0.7,
        height=(187 - 80) * np.pi / 180,
        facecolor="none",
        edgecolor="tab:red",
        linewidth=1.5,
        label="EMCal",
    )
    ax.add_patch(r)

    # Final presentation settings
    ax.set_xlabel(labels.make_valid_latex_string(r"\eta"))
    ax.set_ylabel(labels.make_valid_latex_string(r"\varphi"))
    ax.legend(loc="upper right")
    fig.tight_layout()

    # Finally, save and cleanup
    output_name = f"track_eta_phi_{str(event_activity)}"
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)

    # Check the phi 1D projection
    track_phi = hist.ProjectionY()
    # Make it easier to view
    track_phi.Rebin(8)
    import ROOT
    canvas = ROOT.TCanvas("c", "c")
    # Labeling
    track_phi.SetTitle("")
    track_phi.GetXaxis().SetTitle(
        labels.use_label_with_root(labels.make_valid_latex_string(r"\varphi")))
    track_phi.GetYaxis().SetTitle("Counts")
    track_phi.Draw()
    # Draw lines corresponding to the EMCal
    line_min = ROOT.TLine(80 * np.pi / 180, track_phi.GetMinimum(),
                          80 * np.pi / 180, track_phi.GetMaximum())
    line_max = ROOT.TLine(187 * np.pi / 180, track_phi.GetMinimum(),
                          187 * np.pi / 180, track_phi.GetMaximum())
    for l in [line_min, line_max]:
        l.SetLineColor(ROOT.kRed)
        l.Draw("same")

    # Save the plot
    plot_base.save_plot(output_info, canvas,
                        f"track_phi_{str(event_activity)}")
def compare_STAR_and_ALICE(
        star_final_response_task: "response_matrix.ResponseMatrixBase",
        alice_particle_level_spectra: Dict[params.CollisionEnergy, Hist],
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    # Setup
    fig, ax = plt.subplots(figsize=(8, 6))

    # First, plot the STAR points
    star_centrality_map = {
        params.EventActivity.semi_central: r"20 \textendash 50 \%",
    }
    star_hist = histogram.Histogram1D.from_existing_hist(
        star_final_response_task.particle_level_spectra)
    star_label = f"STAR ${star_final_response_task.collision_energy.display_str()}$ hard-core jets"
    star_label += "\n" + f"PYTHIA with ${star_centrality_map[star_final_response_task.event_activity]}$ ${star_final_response_task.collision_system.display_str()}$ det. conditions"
    ax.errorbar(
        star_hist.x,
        star_hist.y,
        xerr=(star_hist.bin_edges[1:] - star_hist.bin_edges[:-1]) / 2,
        yerr=star_hist.errors,
        label=star_label,
        color="blue",
        marker="s",
        linestyle="",
    )

    # Convert and plot hist
    # Markers are for 2.76, 5.02 TeV
    markers = ["s", "o"]
    for (collision_energy,
         part_level_hist), marker in zip(alice_particle_level_spectra.items(),
                                         markers):
        alice_label = f"ALICE ${collision_energy.display_str()}$ biased jets"
        alice_label += "\n" + f"${params.CollisionSystem.embedPythia.display_str(embedded_additional_label = star_final_response_task.event_activity.display_str())}$"
        h = histogram.Histogram1D.from_existing_hist(part_level_hist)
        ax.errorbar(
            h.x,
            h.y,
            xerr=(h.bin_edges[1:] - h.bin_edges[:-1]) / 2,
            yerr=h.errors,
            label=alice_label,
            color="red",
            marker=marker,
            linestyle="",
        )

    # Label axes
    y_label = r"\text{d}N/\text{d}p_{\text{T}}"
    if star_final_response_task.task_config["particle_level_spectra"][
            "normalize_by_n_jets"]:
        y_label = r"(1/N_{\text{jets}})" + y_label
        y_label = y_label
    if star_final_response_task.task_config["particle_level_spectra"][
            "normalize_at_selected_jet_pt_bin"]:
        y_label = r"\text{Arb. Units}"
    plot_labels = plot_base.PlotLabels(
        title="",
        x_label=
        fr"${labels.jet_pt_display_label(upper_label = 'part')}\:({labels.momentum_units_label_gev()})$",
        y_label=labels.make_valid_latex_string(y_label),
    )
    # Apply labels individually so we can increase the font size...
    ax.set_xlabel(plot_labels.x_label, fontsize=16)
    ax.set_ylabel(plot_labels.y_label, fontsize=16)
    ax.set_title("")
    # Final presentation settings
    # Axis ticks
    ax.xaxis.set_major_locator(matplotlib.ticker.MultipleLocator(base=10))
    ax.xaxis.set_minor_locator(matplotlib.ticker.MultipleLocator(base=2))
    tick_shared_args = {
        "axis": "both",
        "bottom": True,
        "left": True,
    }
    ax.tick_params(
        which="major",
        # Size of the axis mark labels
        labelsize=15,
        length=8,
        **tick_shared_args,
    )
    ax.tick_params(
        which="minor",
        length=4,
        **tick_shared_args,
    )
    # Limits
    ax.set_xlim(
        0, star_final_response_task.task_config["particle_level_spectra"]
        ["particle_level_max_pt"])
    # Unfortunately, MPL doesn't calculate restricted log limits very nicely, so we
    # we have to set the values by hand.
    # We grab the value from the last analysis object - the value will be the same for all of them.
    y_limits = star_final_response_task.task_config["particle_level_spectra"][
        "y_limits"]
    ax.set_ylim(y_limits[0], y_limits[1])
    ax.set_yscale("log")
    # Legend
    ax.legend(
        # Here, we specify the location of the upper right corner of the legend box.
        loc="upper right",
        bbox_to_anchor=(0.99, 0.99),
        borderaxespad=0,
        frameon=True,
        fontsize=15,
    )
    ax.text(0.99,
            0.75,
            s="Inclusive event plane orientation",
            horizontalalignment="right",
            verticalalignment="top",
            multialignment="right",
            fontsize=15,
            transform=ax.transAxes)
    fig.tight_layout()

    # Finally, save and cleanup
    output_name = "particle_level_comparison_STAR_ALICE"
    output_name += "_mpl"
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)
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()