Source code for pyhdtoolkit.plotting.sbs.phase

"""
.. _plotting-sbs-phase:

Segment-by-Segment Phase
------------------------

Functions to plot phase values of Segment-by-Segment results.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import matplotlib.pyplot as plt
import tfs

from loguru import logger
from matplotlib.legend import _get_legend_handles_labels

from pyhdtoolkit.plotting.utils import find_ip_s_from_segment_start

if TYPE_CHECKING:
    from matplotlib.axes import Axes
    from matplotlib.figure import Figure


[docs] def plot_phase_segment_one_beam( phase_x: tfs.TfsDataFrame, phase_y: tfs.TfsDataFrame, model: tfs.TfsDataFrame = None, ip: int | None = None, **kwargs, ) -> Figure: """ .. versionadded:: 0.19.0 Plots the propagated measured phase and the propagated corrected phase for the given IP segment, for both planes for a given beam. Optionally highlights the IP location in the segment. One can find an example use of this function in the :ref:`segment-by-segment plotting <demo-sbs-plotting>` example gallery. Parameters ---------- phase_x : tfs.TfsDataFrame A `~tfs.TfsDataFrame` of the segment-by-segment phase result for the horizontal plane in the given segment. phase_y : tfs.TfsDataFrame A `~tfs.TfsDataFrame` of the segment-by-segment phase result for the vertical plane in the given segment. model : tfs.TfsDataFrame, optional A `~tfs.TfsDataFrame` of the model used in the analysis. If given, the IP location in the segment will be highlighted by a vertical grey line. ip : int, optional The IP number of the segment. Used for the label of the vertical grey line. Requires to have provided the model dataframe. **kwargs Keyword arguments will be transmitted to the figure creation call to `~matplotlib.pyplot.subplots`. If ``bbox_to_anchor`` is found, it will be used to position the legend across the whole figure space. Returns ------- matplotlib.figure.Figure The `~matplotlib.figure.Figure` on which the plot is created. Example ------- .. code-block:: python fig = plot_phase_segment_one_beam( sbs_phasex, sbs_phasey, model=b2_model_tfs, ip=5, figsize=(8, 8) ) """ logger.debug("Plotting the phase for both planes over the segment.") # legend_bbox_to_anchor = kwargs.pop("bbox_to_anchor", (0.535, 0.97)) figure, (ax1, ax2) = plt.subplots(2, 1, **kwargs) plot_phase_segment(ax1, segment_df=phase_x, model_df=model, plane="x", ip=ip) plot_phase_segment(ax2, segment_df=phase_y, model_df=model, plane="y", ip=ip) ax1.legend(bbox_to_anchor=(0, 1.02, 1, 0.2), loc="lower left", ncol=4) ax2.set_xlabel(r"$\mathrm{S\ [m]}$") plt.tight_layout() return figure
[docs] def plot_phase_segment_both_beams( b1_phase_x: tfs.TfsDataFrame, b1_phase_y: tfs.TfsDataFrame, b2_phase_x: tfs.TfsDataFrame, b2_phase_y: tfs.TfsDataFrame, b1_model: tfs.TfsDataFrame = None, b2_model: tfs.TfsDataFrame = None, ip: int | None = None, **kwargs, ) -> Figure: """ .. versionadded:: 0.19.0 Plots the propagated measured phase and the propagated corrected phase for the given IP segment, for both planes and both beams. Optionally highlights the IP location in the segment. One can find an example use of this function in the :ref:`segment-by-segment plotting <demo-sbs-plotting>` example gallery. Parameters ---------- b1_phase_x : tfs.TfsDataFrame A `~tfs.TfsDataFrame` of the segment-by-segment phase result for the horizontal plane in the given segment, for Beam 1. b1_phase_y : tfs.TfsDataFrame A `~tfs.TfsDataFrame` of the segment-by-segment phase result for the vertical plane in the given segment, for Beam 1. b2_phase_x : tfs.TfsDataFrame A `~tfs.TfsDataFrame` of the segment-by-segment phase result for the horizontal plane in the given segment, for Beam 2. b2_phase_x : tfs.TfsDataFrame A `~tfs.TfsDataFrame` of the segment-by-segment phase result for the vertical plane in the given segment, for Beam 2. b1_model : tfs.TfsDataFrame, optional A `~tfs.TfsDataFrame` of the Beam 1 model used in the analysis. If given, then the IP location in the segment will be highlighted by a vertical grey line. b2_model : tfs.TfsDataFrame, optional A `~tfs.TfsDataFrame` of the Beam 2 model used in the analysis. If given, then the IP location in the segment will be highlighted by a vertical grey line. ip : int, optional The IP number of the segment. Used for the label of the vertical grey line. Requires to have provided the model dataframe. Returns ------- matplotlib.figure.Figure The `~matplotlib.figure.Figure` on which the plot is created. Example ------- .. code-block:: python fig = plot_phase_segment_both_beams( phasex_b1_tfs, phasey_b1_tfs, phasex_b2_tfs, phasey_b2_tfs, b1_model_tfs, b2_model_tfs, ip=1, figsize=(18, 9), bbox_to_anchor=(0.535, 0.94), ) """ logger.debug("Plotting the phase for both planes over the segment.") legend_bbox_to_anchor = kwargs.pop("bbox_to_anchor", (0.535, 0.97)) figure, ((b1x, b2x), (b1y, b2y)) = plt.subplots(2, 2, sharex=True, sharey="row", **kwargs) # Plotting B1 data plot_phase_segment(b1x, segment_df=b1_phase_x, model_df=b1_model, plane="x", ip=ip) plot_phase_segment(b1y, segment_df=b1_phase_y, model_df=b1_model, plane="y", ip=ip) # Plotting B2 data plot_phase_segment(b2x, segment_df=b2_phase_x, model_df=b2_model, plane="x", ip=ip) plot_phase_segment(b2y, segment_df=b2_phase_y, model_df=b2_model, plane="y", ip=ip) # X axis labels b1y.set_xlabel(r"$\mathrm{S\ [m]}$") b2y.set_xlabel(r"$\mathrm{S\ [m]}$") # Erasing Y labels of B2 (on the right) b2x.set_ylabel(None) b2y.set_ylabel(None) # Suptitles b1x.set_title(r"$\mathrm{Beam\ 1}$", y=1.01) b2x.set_title(r"$\mathrm{Beam\ 2}$", y=1.01) figure.legend(*_get_legend_handles_labels([b1x]), ncol=2, bbox_to_anchor=legend_bbox_to_anchor, loc="lower center") plt.tight_layout() return figure
# ----- Workhorse ----- #
[docs] def plot_phase_segment( ax: Axes = None, segment_df: tfs.TfsDataFrame = None, model_df: tfs.TfsDataFrame = None, plane: str = "x", ip: int | None = None, ) -> None: """ .. versionadded:: 0.19.0 Plots a the phase for a given plane over the segment, optionally highlighting the IP location. One can find an example use of this function in the :ref:`segment-by-segment plotting <demo-sbs-plotting>` example gallery. Parameters ---------- ax : matplotlib.axes.Axes, optional The `~matplotlib.axes.Axes` to plot on. Will get the current axis if no `~matplotlib.axes.Axes` is given. segment_df : tfs.TfsDataFrame A `~tfs.TfsDataFrame` of the segment-by-segment coupling result for the given segment. model_df : tfs.TfsDataFrame, optional A `~tfs.TfsDataFrame` of the model used in the analysis. If given, then the IP location in the segment will be determined from the two dataframes and will be highlighted in the plot by a vertical grey line. plane : str The plane the data is is for in the provided *segment_df*. Will be used for the ylabel. Should be either "x" or "y", case-insensitive. ip : int, optional The IP number of the segment. Used for the label of the vertical grey line. Requires to have provided the model dataframe. Example ------- .. code-block:: python plot_phase_segment(ax, segment_df, b1_model_tfs, plane="x", ip=1) """ if plane.upper() not in ("X", "Y"): logger.error("The provided plane is invalid, should be either 'x' or 'y', case-insensitively.") msg = "Invalid 'plane' argument." raise ValueError(msg) plane = plane.upper() logger.debug(f"Plotting phase for plane {plane.upper()} over the segment") ax = plt.gca() if ax is None else ax ax.set_ylabel(r"$\mathrm{\Delta \phi_{" + plane + "}}$") ax.errorbar( segment_df.S, segment_df[f"PROPPHASE{plane}"], yerr=segment_df[f"ERRPROPPHASE{plane}"], fmt="o-", markersize=3, capsize=2, color="C0", label="Measurement", ) ax.errorbar( segment_df.S, segment_df[f"CORPHASE{plane}"], yerr=segment_df[f"ERRCORPHASE{plane}"], fmt="o-", markersize=3, capsize=2, color="C1", label="Correction", ) if model_df is not None and isinstance(model_df, tfs.TfsDataFrame): # If model dataframe is given, find S location of IP and highlight it logger.debug("Plotting the IP location in the segment.") ips = find_ip_s_from_segment_start(segment_df=segment_df, model_df=model_df, ip=ip) ax.axvline(ips, ls="--", color="grey")