def plot_RPF_regions(input_file: str, hist_name: str, output_prefix: str = ".", printing_extensions: Optional[List[str]] = None) -> None:
    """ Main entry point for stand-alone highlight plotting functionality.

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

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

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

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

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

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

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

        # Save and finish up
        # The figure will be saved at ``output_prefix/output_path.printing_extension``
        output_wrapper = analysis_objects.PlottingOutputWrapper(output_prefix = output_prefix, printing_extensions = printing_extensions)
        plot_base.save_plot(output_wrapper, fig, output_name = "highlightRPFRegions")
        plt.close(fig)
示例#2
0
    def setup(self) -> bool:
        """ Setup the analysis and the PYTHIA 6 generator.

        The main task is to setup the efficiency histograms.

        Usually, we'd like the generator to set itself up, but ``TPythia{6,8}`` is a singleton,
        so we need to call set up immediately before we run the particular analysis.
        """
        # Setup the efficiency histograms
        # Distribution to sample for determining which efficiency hist to use.
        # Based on year 14 ZDC rates (plot from Dan).
        # 0 = 0-33 kHz -> 1/9
        # 1 = 33-66 kHz -> 5/9
        # 2 = 66-100 kHz -> 3/9
        self.efficiency_sampling = [0, 1, 1, 1, 1, 1, 2, 2, 2]

        # Retrieve the efficiency histograms
        hists = histogram.get_histograms_in_file(
            filename="inputData/AuAu/200/y14_efficiency_dca1.root")

        centrality_index_map = {
            # 0-10% in most analyses.
            # 0 = 0-5%, 1 = 5-10%
            params.EventActivity.central:
            list(range(0, 2)),
            # 20-50% in Joel's STAR analysis.
            # 4 = 20-25%, 5 = 25-30%, 6 = 30-35%, 7 = 35-40%, 8 = 40-45%, 9 = 45-50%
            params.EventActivity.semi_central:
            list(range(4, 10)),
        }

        for interation_rate_index in range(3):
            centrality_hists = []
            for centrality_index in centrality_index_map[self.event_activity]:
                h_root = hists[
                    f"efficiency_lumi_{interation_rate_index}_cent_{centrality_index}"]
                _, _, h_temp = histogram.get_array_from_hist2D(
                    h_root, set_zero_to_NaN=False)
                centrality_hists.append(h_temp)

            # Average the efficiency over the centrality bins.
            final_efficiency_hist = sum(centrality_hists) / len(
                centrality_hists)
            self.efficiency_hists.append(final_efficiency_hist)

            if interation_rate_index == 0:
                # h_root was set from the last iteration, so we take advantage of it.
                # Take advantage of the last iteration
                self.efficiency_pt_bin_edges = histogram.get_bin_edges_from_axis(
                    h_root.GetXaxis())
                self.efficiency_eta_bin_edges = histogram.get_bin_edges_from_axis(
                    h_root.GetYaxis())

        # Complete setup of PYTHIA6 if necessary.
        if self.generator.initialized is False:
            self.generator.setup()

        return True
def plot_2D_efficiency_data(efficiency_hist: Hist, centrality_range: params.SelectedRange, output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Plot the 2D efficiency data

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

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

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

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

    # Cleanup
    plt.close(fig)
示例#4
0
    def test_get_array_from_hist2D(self, logging_mixin, use_bin_edges, set_zero_to_NaN, test_root_hists):
        """ Test getting numpy arrays from a 2D hist. """
        hist = test_root_hists.hist2D
        x, y, hist_array = histogram.get_array_from_hist2D(hist = hist, set_zero_to_NaN = set_zero_to_NaN, return_bin_edges = use_bin_edges)

        # Determine expected values
        if use_bin_edges:
            epsilon = 1e-9
            x_bin_edges = np.empty(hist.GetXaxis().GetNbins() + 1)
            x_bin_edges[:-1] = [hist.GetXaxis().GetBinLowEdge(i) for i in range(1, hist.GetXaxis().GetNbins() + 1)]
            x_bin_edges[-1] = hist.GetXaxis().GetBinUpEdge(hist.GetXaxis().GetNbins())
            y_bin_edges = np.empty(hist.GetYaxis().GetNbins() + 1)
            y_bin_edges[:-1] = [hist.GetYaxis().GetBinLowEdge(i) for i in range(1, hist.GetYaxis().GetNbins() + 1)]
            y_bin_edges[-1] = hist.GetYaxis().GetBinUpEdge(hist.GetYaxis().GetNbins())
            x_mesh = np.arange(np.amin(x_bin_edges), np.amax(x_bin_edges) + epsilon, hist.GetXaxis().GetBinWidth(1))
            y_mesh = np.arange(np.amin(y_bin_edges), np.amax(y_bin_edges) + epsilon, hist.GetYaxis().GetBinWidth(1))
        else:
            x_mesh = np.array([hist.GetXaxis().GetBinCenter(i) for i in range(1, hist.GetXaxis().GetNbins() + 1)])
            y_mesh = np.array([hist.GetYaxis().GetBinCenter(i) for i in range(1, hist.GetYaxis().GetNbins() + 1)])
        x_range = x_mesh
        y_range = y_mesh
        expected_x, expected_y = np.meshgrid(x_range, y_range)
        expected_hist_array = np.array([
            hist.GetBinContent(x, y)
            for x in range(1, hist.GetXaxis().GetNbins() + 1)
            for y in range(1, hist.GetYaxis().GetNbins() + 1)], dtype=np.float32
        ).reshape(hist.GetYaxis().GetNbins(), hist.GetXaxis().GetNbins())
        if set_zero_to_NaN:
            expected_hist_array[expected_hist_array == 0] = np.nan

        assert np.allclose(x, expected_x)
        assert np.allclose(y, expected_y)
        assert np.allclose(hist_array, expected_hist_array, equal_nan = True)

        # Check particular values for good measure.
        assert np.isclose(hist_array[1][0], 1.0)
        if set_zero_to_NaN:
            assert np.isnan(hist_array[0][1])
        else:
            assert np.isclose(hist_array[0][1], 0.0)
    def _plot_2D_hists(self, fig: matplotlib.figure.Figure,
                       ax: matplotlib.axes.Axes) -> None:
        """ Plot a 2D hist using matplotlib.

        Note:
            This is is restricted to plotting only one hist (because how would you plot multiple
            2D hists on one canvas)?

        Args:
            fig: Figure from matplotlib
            ax: Axis from matplotlib.
        Raises:
            ValueError: If more than one histogram is stored in the ``hists``, which doesn't make sense for
                2D hist, which doesn't make sense for 2D hists.
        """
        if len(self.hists) > 1:
            raise ValueError(
                self.hists,
                "Too many hists are included for a 2D hist. Should only be one!"
            )

        # We can do a few different types of plots:
        #   - Surface plot
        #   - Image plot (like colz), created using:
        #       - imshow() by default
        #       - pcolormesh() as an option
        # NOTE: seaborn heatmap is not really as flexible here, so we'll handle it manually instead.
        #       Since the style is already applied, there isn't really anything lost

        # Retrieve the array corresponding to the data
        # NOTE: The convention for the array is arr[x_index][y_index]. Apparently this matches up with
        #       numpy axis conventions (?). We will have to transpose the data when we go to plot it.
        #       This is the same convention as used in root_numpy.
        X, Y, hist_array = histogram.get_array_from_hist2D(
            hist=self._first_hist(),
            set_zero_to_NaN=True,
            return_bin_edges=not self.surface)

        # Define and fill kwargs
        kwargs = {}

        # Set the z axis normalization via this particular function
        # Will be called when creating the arguments
        normalizationFunction = matplotlib.colors.Normalize
        if self.logz:
            normalizationFunction = matplotlib.colors.LogNorm
        # Evaluate
        kwargs["norm"] = normalizationFunction(vmin=np.nanmin(hist_array),
                                               vmax=np.nanmax(hist_array))
        logger.debug("min: {}, max: {}".format(np.nanmin(hist_array),
                                               np.nanmax(hist_array)))
        # Colormap is the default from sns.heatmap
        kwargs["cmap"] = plot_base.prepare_colormap(sns.cm.rocket)
        # Label is included so we could use a legend if we want
        kwargs["label"] = self._first_hist().GetTitle()

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

        if self.surface:
            logger.debug("Plotting surface")
            # Need to retrieve a special 3D axis for the surface plot
            ax = plt.axes(projection="3d")

            # For the surface plot, we want to specify (X,Y) as bin centers. Edges of surfaces
            # will be at these points.
            # NOTE: There are n-1 faces for n points, so not every value will be represented by a face.
            #       However, the location of the points at the bin centers is still correct!
            # Create the plot
            axFromPlot = ax.plot_surface(X, Y, hist_array.T, **kwargs)
        else:
            # Assume an image plot if we don't explicitly select surface
            logger.debug("Plotting as an image")

            # pcolormesh is somewhat like a hist in that (X,Y) should define bin edges. So we need (X,Y)
            # to define lower bin edges, with the last element in the array corresponding to the lower
            # edge of the next (n+1) bin (which is the same as the upper edge of the nth bin).
            #
            # imshow also takes advantage of these limits to determine the extent, but only in a limited
            # way, as it just takes the min and max of each range.
            #
            # Plot with either imshow or pcolormesh
            # Anecdotally, I think pcolormesh is a bit more flexible, but imshow seems to be much faster.
            # According to https://stackoverflow.com/a/21169703, either one can be fine with the proper
            # options. So the plan is to stick with imshow unless pcolormesh is needed for some reason
            if self.use_pcolor_mesh:
                logger.debug("Plotting with pcolormesh")

                axFromPlot = plt.pcolormesh(X, Y, hist_array.T, **kwargs)
            else:
                logger.debug("Plotting with imshow ")

                # This imshow is quite similar to rplt.imshow (it is located in
                # ``plotting.root2matplotlib.imshow``), which can be seen here:
                # https://github.com/rootpy/rootpy/blob/master/rootpy/plotting/root2matplotlib.py#L743
                # However, we don't copy it directly because we want to set the 0s to nan so they won't
                # be plotted.
                extent = [np.amin(X), np.amax(X), np.amin(Y), np.amax(Y)]

                #logger.debug(f"Extent: {extent}, binEdges[1]: {binEdges[1]}")
                axFromPlot = plt.imshow(hist_array.T,
                                        extent=extent,
                                        interpolation="nearest",
                                        aspect="auto",
                                        origin="lower",
                                        **kwargs)

        # Draw the colorbar based on the drawn axis above.
        label = self.z_label if self.z_label else self._first_hist().GetZaxis(
        ).GetTitle()
        # NOTE: The mappable argument must be the separate axes, not the overall one
        # 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(axFromPlot, label=label)
def _plot_response_matrix_with_matplotlib(
        name: str, x_label: str, y_label: str, output_name: str, hist: Hist,
        plot_errors_hist: bool,
        output_info: analysis_objects.PlottingOutputWrapper) -> None:
    """ Underlying function to actually plot a response matrix with matplotlib.

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

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

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

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

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

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

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

    # Save and cleanup
    output_name += "_mpl"
    plot_base.save_plot(output_info, fig, output_name)
    plt.close(fig)
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)
示例#8
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)}")