Skip to content

plot

get_title_from_configuration(dataframe_data, ions=False, crossing_type=None, display_LHC_version=True, display_energy=True, display_bunch_index=True, display_CC_crossing=True, display_bunch_intensity=True, display_beta=True, display_crossing_IP_1=True, display_crossing_IP_2=True, display_crossing_IP_5=True, display_crossing_IP_8=True, display_bunch_length=True, display_polarity_IP_2_8=True, display_emittance=True, display_chromaticity=True, display_octupole_intensity=True, display_coupling=True, display_filling_scheme=True, display_horizontal_tune=None, display_vertical_tune=None, display_tune=True, display_luminosity_1=True, display_luminosity_2=True, display_luminosity_5=True, display_luminosity_8=True, display_PU_1=True, display_PU_2=True, display_PU_5=True, display_PU_8=True, display_number_of_turns=False)

Generates a title string from the configuration data.

Parameters:

Name Type Description Default
dataframe_data DataFrame

The dataframe containing configuration data.

required
ions bool

Whether the beam is composed of ions. Defaults to False.

False
crossing_type str

The type of crossing: 'vh' or 'hv'. Defaults to None, meaning it will try to be inferred from the optics file name. Back to 'hv' if not found.

None
display_betx_bety bool

Whether to display the beta functions. Defaults to True.

required
display_LHC_version bool

Whether to display the LHC version. Defaults to True.

True
display_energy bool

Whether to display the energy. Defaults to True.

True
display_bunch_index bool

Whether to display the bunch index. Defaults to True.

True
display_CC_crossing bool

Whether to display the CC crossing. Defaults to True.

True
display_bunch_intensity bool

Whether to display the bunch intensity. Defaults to True.

True
display_beta bool

Whether to display the beta function. Defaults to True.

True
display_crossing_IP_1 bool

Whether to display the crossing at IP1. Defaults to True.

True
display_crossing_IP_2 bool

Whether to display the crossing at IP2. Defaults to True.

True
display_crossing_IP_5 bool

Whether to display the crossing at IP5. Defaults to True.

True
display_crossing_IP_8 bool

Whether to display the crossing at IP8. Defaults to True.

True
display_bunch_length bool

Whether to display the bunch length. Defaults to True.

True
display_polarity_IP_2_8 bool

Whether to display the polarity at IP2 and IP8. Defaults to True.

True
display_emittance bool

Whether to display the emittance. Defaults to True.

True
display_chromaticity bool

Whether to display the chromaticity. Defaults to True.

True
display_octupole_intensity bool

Whether to display the octupole intensity. Defaults to True.

True
display_coupling bool

Whether to display the coupling. Defaults to True.

True
display_filling_scheme bool

Whether to display the filling scheme. Defaults to True.

True
display_horizontal_tune bool

Whether to display the horizontal tune. Defaults to None. Takes precedence over display_tune.

None
display_vertical_tune bool

Whether to display the vertical tune. Defaults to None. Takes precedence over display_tune.

None
display_tune bool

Whether to display the tune. Defaults to True.

True
display_luminosity_1 bool

Whether to display the luminosity at IP1. Defaults to True.

True
display_luminosity_2 bool

Whether to display the luminosity at IP2. Defaults to True.

True
display_luminosity_5 bool

Whether to display the luminosity at IP5. Defaults to True.

True
display_luminosity_8 bool

Whether to display the luminosity at IP8. Defaults to True.

True
display_PU_1 bool

Whether to display the PU at IP1. Defaults to True.

True
display_PU_2 bool

Whether to display the PU at IP2. Defaults to True.

True
display_PU_5 bool

Whether to display the PU at IP5. Defaults to True.

True
display_PU_8 bool

Whether to display the PU at IP8. Defaults to True.

True
display_number_of_turns bool

Whether to display the number of turns. Defaults to False.

False

Returns:

Name Type Description
str str

The generated title string.

Source code in study_da/plot/build_title.py
def get_title_from_configuration(
    dataframe_data: pd.DataFrame,
    ions: bool = False,
    crossing_type: Optional[str] = None,
    display_LHC_version: bool = True,
    display_energy: bool = True,
    display_bunch_index: bool = True,
    display_CC_crossing: bool = True,
    display_bunch_intensity: bool = True,
    display_beta: bool = True,
    display_crossing_IP_1: bool = True,
    display_crossing_IP_2: bool = True,
    display_crossing_IP_5: bool = True,
    display_crossing_IP_8: bool = True,
    display_bunch_length: bool = True,
    display_polarity_IP_2_8: bool = True,
    display_emittance: bool = True,
    display_chromaticity: bool = True,
    display_octupole_intensity: bool = True,
    display_coupling: bool = True,
    display_filling_scheme: bool = True,
    display_horizontal_tune: Optional[bool] = None,
    display_vertical_tune: Optional[bool] = None,
    display_tune: bool = True,
    display_luminosity_1: bool = True,
    display_luminosity_2: bool = True,
    display_luminosity_5: bool = True,
    display_luminosity_8: bool = True,
    display_PU_1: bool = True,
    display_PU_2: bool = True,
    display_PU_5: bool = True,
    display_PU_8: bool = True,
    display_number_of_turns=False,
) -> str:
    """
    Generates a title string from the configuration data.

    Args:
        dataframe_data (pd.DataFrame): The dataframe containing configuration data.
        ions (bool, optional): Whether the beam is composed of ions. Defaults to False.
        crossing_type (str, optional): The type of crossing: 'vh' or 'hv'. Defaults to None, meaning
            it will try to be inferred from the optics file name. Back to 'hv' if not found.
        display_betx_bety (bool, optional): Whether to display the beta functions. Defaults to True.
        display_LHC_version (bool, optional): Whether to display the LHC version. Defaults to True.
        display_energy (bool, optional): Whether to display the energy. Defaults to True.
        display_bunch_index (bool, optional): Whether to display the bunch index. Defaults to True.
        display_CC_crossing (bool, optional): Whether to display the CC crossing. Defaults to True.
        display_bunch_intensity (bool, optional): Whether to display the bunch intensity. Defaults
            to True.
        display_beta (bool, optional): Whether to display the beta function. Defaults to True.
        display_crossing_IP_1 (bool, optional): Whether to display the crossing at IP1. Defaults to
            True.
        display_crossing_IP_2 (bool, optional): Whether to display the crossing at IP2. Defaults to
            True.
        display_crossing_IP_5 (bool, optional): Whether to display the crossing at IP5. Defaults to
            True.
        display_crossing_IP_8 (bool, optional): Whether to display the crossing at IP8. Defaults to
            True.
        display_bunch_length (bool, optional): Whether to display the bunch length. Defaults to
            True.
        display_polarity_IP_2_8 (bool, optional): Whether to display the polarity at IP2 and IP8.
            Defaults to True.
        display_emittance (bool, optional): Whether to display the emittance. Defaults to True.
        display_chromaticity (bool, optional): Whether to display the chromaticity.
            Defaults to True.
        display_octupole_intensity (bool, optional): Whether to display the octupole intensity.
            Defaults to True.
        display_coupling (bool, optional): Whether to display the coupling. Defaults to True.
        display_filling_scheme (bool, optional): Whether to display the filling scheme. Defaults to
            True.
        display_horizontal_tune (bool, optional): Whether to display the horizontal tune. Defaults to
            None. Takes precedence over display_tune.
        display_vertical_tune (bool, optional): Whether to display the vertical tune. Defaults to
            None. Takes precedence over display_tune.
        display_tune (bool, optional): Whether to display the tune. Defaults to True.
        display_luminosity_1 (bool, optional): Whether to display the luminosity at IP1. Defaults to
            True.
        display_luminosity_2 (bool, optional): Whether to display the luminosity at IP2. Defaults to
            True.
        display_luminosity_5 (bool, optional): Whether to display the luminosity at IP5. Defaults to
            True.
        display_luminosity_8 (bool, optional): Whether to display the luminosity at IP8. Defaults to
            True.
        display_PU_1 (bool, optional): Whether to display the PU at IP1. Defaults to True.
        display_PU_2 (bool, optional): Whether to display the PU at IP2. Defaults to True.
        display_PU_5 (bool, optional): Whether to display the PU at IP5. Defaults to True.
        display_PU_8 (bool, optional): Whether to display the PU at IP8. Defaults to True.
        display_number_of_turns (bool, optional): Whether to display the number of turns. Defaults to
            False.

    Returns:
        str: The generated title string.
    """

    # Warn about tune definition
    if (
        display_horizontal_tune is not None or display_vertical_tune is not None
    ) and not display_tune:
        logging.warning(
            "You have defined display_horizontal_tune or display_vertical_tune, but not "
            "display_tune. The horizontal and/or vertical tunes will still be displayed."
        )

    # Find out what is the crossing type
    if crossing_type is None:
        crossing_type = get_crossing_type(dataframe_data)

    # Collect all the information to display
    LHC_version_str = get_LHC_version_str(dataframe_data, ions)
    energy_str = get_energy_str(dataframe_data, ions)
    bunch_index_str = get_bunch_index_str(dataframe_data)
    CC_crossing_str = get_CC_crossing_str(dataframe_data)
    bunch_intensity_str = get_bunch_intensity_str(dataframe_data)
    beta_str = get_beta_str(dataframe_data)
    xing_IP1_str, xing_IP5_str = get_crossing_IP_1_5_str(dataframe_data, crossing_type)
    xing_IP2_str, xing_IP8_str = get_crossing_IP_2_8_str(dataframe_data)
    bunch_length_str = get_bunch_length_str(dataframe_data)
    polarity_str = get_polarity_IP_2_8_str(dataframe_data)
    emittance_str = get_normalized_emittance_str(dataframe_data)
    chromaticity_str = get_chromaticity_str(dataframe_data)
    octupole_intensity_str = get_octupole_intensity_str(dataframe_data)
    coupling_str = get_linear_coupling_str(dataframe_data)
    filling_scheme_str = get_filling_scheme_str(dataframe_data)
    tune_str = get_tune_str(dataframe_data, display_horizontal_tune, display_vertical_tune)
    n_turns_str = get_number_of_turns_str(dataframe_data)

    # Collect luminosity and PU strings at each IP
    dic_lumi_PU_str = {
        "with_beam_beam": {"lumi": {}, "PU": {}},
        "without_beam_beam": {"lumi": {}, "PU": {}},
    }
    for beam_beam in ["with_beam_beam", "without_beam_beam"]:
        for ip in [1, 2, 5, 8]:
            dic_lumi_PU_str[beam_beam]["lumi"][ip] = get_luminosity_at_ip_str(
                dataframe_data, ip, beam_beam=True
            )
            dic_lumi_PU_str[beam_beam]["PU"][ip] = get_PU_at_IP_str(
                dataframe_data, ip, beam_beam=True
            )

    def test_if_empty_and_add_period(string: str) -> str:
        """
        Test if a string is empty and add a period if not.

        Args:
            string (str): The string to test.

        Returns:
            str: The string with a period if not empty.
        """
        return f"{string}. " if string != "" else ""

    # Make the final title (order is the same as in the past)
    title = ""
    if display_LHC_version:
        title += test_if_empty_and_add_period(LHC_version_str)
    if display_energy:
        title += test_if_empty_and_add_period(energy_str)
    if display_CC_crossing:
        title += test_if_empty_and_add_period(CC_crossing_str)
    if display_bunch_intensity:
        title += test_if_empty_and_add_period(bunch_intensity_str)
    # Jump to the next line
    title += "\n"
    if display_luminosity_1:
        title += test_if_empty_and_add_period(dic_lumi_PU_str["with_beam_beam"]["lumi"][1])
    if display_PU_1:
        title += test_if_empty_and_add_period(dic_lumi_PU_str["with_beam_beam"]["PU"][1])
    if display_luminosity_5:
        title += test_if_empty_and_add_period(dic_lumi_PU_str["with_beam_beam"]["lumi"][5])
    if display_PU_5:
        title += test_if_empty_and_add_period(dic_lumi_PU_str["with_beam_beam"]["PU"][5])
    # Jump to the next line
    title += "\n"
    if display_luminosity_2:
        title += test_if_empty_and_add_period(dic_lumi_PU_str["with_beam_beam"]["lumi"][2])
    if display_PU_2:
        title += test_if_empty_and_add_period(dic_lumi_PU_str["with_beam_beam"]["PU"][2])
    if display_luminosity_8:
        title += test_if_empty_and_add_period(dic_lumi_PU_str["with_beam_beam"]["lumi"][8])
    if display_PU_8:
        title += test_if_empty_and_add_period(dic_lumi_PU_str["with_beam_beam"]["PU"][8])
    # Jump to the next line
    title += "\n"
    if display_beta:
        title += test_if_empty_and_add_period(beta_str)
    if display_polarity_IP_2_8:
        title += test_if_empty_and_add_period(polarity_str)
    if display_bunch_length:
        title += test_if_empty_and_add_period(bunch_length_str)
    # Jump to the next line
    title += "\n"
    if display_crossing_IP_1:
        title += test_if_empty_and_add_period(xing_IP1_str)
    if display_crossing_IP_5:
        title += test_if_empty_and_add_period(xing_IP5_str)
    if display_crossing_IP_2:
        title += test_if_empty_and_add_period(xing_IP2_str)
    if display_crossing_IP_8:
        title += test_if_empty_and_add_period(xing_IP8_str)

    # Jump to the next line
    title += "\n"
    if display_emittance:
        title += test_if_empty_and_add_period(emittance_str)
    if display_chromaticity:
        title += test_if_empty_and_add_period(chromaticity_str)
    if display_octupole_intensity:
        title += test_if_empty_and_add_period(octupole_intensity_str)
    if display_coupling:
        title += test_if_empty_and_add_period(coupling_str)
    if display_tune:
        title += test_if_empty_and_add_period(tune_str)
    # Jump to the next line
    title += "\n"
    if display_filling_scheme:
        title += test_if_empty_and_add_period(filling_scheme_str)
    if display_bunch_index:
        title += test_if_empty_and_add_period(bunch_index_str)
    # Jump to the next line
    if display_number_of_turns:
        title += "\n"
        title += test_if_empty_and_add_period(n_turns_str)

    # Filter final title for empty lines
    title = "\n".join([line for line in title.split("\n") if line.strip() != ""])

    return title

plot_3D(dataframe_data, x_variable, y_variable, z_variable, color_variable, xlabel=None, ylabel=None, z_label=None, title='', vmin=4.5, vmax=7.5, surface_count=30, opacity=0.2, figsize=(1000, 1000), colormap='RdBu', colorbar_title_text='Minimum DA (σ)', display_colormap=False, output_path='output.png', output_path_html='output.html', display_plot=True, dark_theme=False)

Plots a 3D volume rendering from the given dataframe.

Parameters:

Name Type Description Default
dataframe_data DataFrame

The dataframe containing the data to plot.

required
x_variable str

The variable to plot on the x-axis.

required
y_variable str

The variable to plot on the y-axis.

required
z_variable str

The variable to plot on the z-axis.

required
color_variable str

The variable to use for the color scale.

required
xlabel Optional[str]

The label for the x-axis. Defaults to None.

None
ylabel Optional[str]

The label for the y-axis. Defaults to None.

None
z_label Optional[str]

The label for the z-axis. Defaults to None.

None
title str

The title of the plot. Defaults to "".

''
vmin float

The minimum value for the color scale. Defaults to 4.5.

4.5
vmax float

The maximum value for the color scale. Defaults to 7.5.

7.5
surface_count int

The number of surfaces for volume rendering. Defaults to 30.

30
opacity float

The opacity of the volume rendering. Defaults to 0.2.

0.2
figsize tuple[float, float]

The size of the figure. Defaults to (1000, 1000).

(1000, 1000)
colormap str

The colormap to use. Defaults to "RdBu".

'RdBu'
colorbar_title_text str

The label for the colorbar. Defaults to "Minimum DA (σ)".

'Minimum DA (σ)'
display_colormap bool

Whether to display the colormap. Defaults to False.

False
output_path str

The path to save the plot image. Defaults to "output.png".

'output.png'
output_path_html str

The path to save the plot HTML. Defaults to "output.html".

'output.html'
display_plot bool

Whether to display the plot. Defaults to True.

True
dark_theme bool

Whether to use a dark theme. Defaults to False.

False

Returns:

Type Description
Any

go.Figure: The plotly figure object.

Source code in study_da/plot/plot_study.py
def plot_3D(
    dataframe_data: pd.DataFrame,
    x_variable: str,
    y_variable: str,
    z_variable: str,
    color_variable: str,
    xlabel: Optional[str] = None,
    ylabel: Optional[str] = None,
    z_label: Optional[str] = None,
    title: str = "",
    vmin: float = 4.5,
    vmax: float = 7.5,
    surface_count: int = 30,
    opacity: float = 0.2,
    figsize: tuple[float, float] = (1000, 1000),
    colormap: str = "RdBu",
    colorbar_title_text: str = "Minimum DA (σ)",
    display_colormap: bool = False,
    output_path: str = "output.png",
    output_path_html: str = "output.html",
    display_plot: bool = True,
    dark_theme: bool = False,
) -> Any:
    """
    Plots a 3D volume rendering from the given dataframe.

    Args:
        dataframe_data (pd.DataFrame): The dataframe containing the data to plot.
        x_variable (str): The variable to plot on the x-axis.
        y_variable (str): The variable to plot on the y-axis.
        z_variable (str): The variable to plot on the z-axis.
        color_variable (str): The variable to use for the color scale.
        xlabel (Optional[str], optional): The label for the x-axis. Defaults to None.
        ylabel (Optional[str], optional): The label for the y-axis. Defaults to None.
        z_label (Optional[str], optional): The label for the z-axis. Defaults to None.
        title (str, optional): The title of the plot. Defaults to "".
        vmin (float, optional): The minimum value for the color scale. Defaults to 4.5.
        vmax (float, optional): The maximum value for the color scale. Defaults to 7.5.
        surface_count (int, optional): The number of surfaces for volume rendering. Defaults to 30.
        opacity (float, optional): The opacity of the volume rendering. Defaults to 0.2.
        figsize (tuple[float, float], optional): The size of the figure. Defaults to (1000, 1000).
        colormap (str, optional): The colormap to use. Defaults to "RdBu".
        colorbar_title_text (str, optional): The label for the colorbar. Defaults to "Minimum DA (σ)".
        display_colormap (bool, optional): Whether to display the colormap. Defaults to False.
        output_path (str, optional): The path to save the plot image. Defaults to "output.png".
        output_path_html (str, optional): The path to save the plot HTML. Defaults to "output.html".
        display_plot (bool, optional): Whether to display the plot. Defaults to True.
        dark_theme (bool, optional): Whether to use a dark theme. Defaults to False.

    Returns:
        go.Figure: The plotly figure object.
    """
    # Check if plotly is installed
    try:
        import plotly.graph_objects as go
    except ImportError as e:
        raise ImportError("Please install plotly to use this function") from e

    X = np.array(dataframe_data[x_variable])
    Y = np.array(dataframe_data[y_variable])
    Z = np.array(dataframe_data[z_variable])
    values = np.array(dataframe_data[color_variable])
    fig = go.Figure(
        data=go.Volume(
            x=X.flatten(),
            y=Y.flatten(),
            z=Z.flatten(),
            value=values.flatten(),
            isomin=vmin,
            isomax=vmax,
            opacity=opacity,  # needs to be small to see through all surfaces
            surface_count=surface_count,  # needs to be a large number for good volume rendering
            colorscale=colormap,
            colorbar_title_text=colorbar_title_text,
        )
    )

    fig.update_layout(
        scene_xaxis_title_text=xlabel,
        scene_yaxis_title_text=ylabel,
        scene_zaxis_title_text=z_label,
        title=title,
    )

    # Get a good initial view, dezoomed
    fig.update_layout(scene_camera=dict(eye=dict(x=1.5, y=1.5, z=1.5)))

    # Center the title
    fig.update_layout(title_x=0.5, title_y=0.9, title_xanchor="center", title_yanchor="top")

    # Specify the width and height of the figure
    fig.update_layout(width=figsize[0], height=figsize[1])

    # Remove margins and padding
    fig.update_layout(margin=dict(l=0, r=0, b=0, t=0))

    # Display the colormap
    if not display_colormap:
        fig.update_layout(coloraxis_showscale=False)
        fig.update_traces(showscale=False)
    else:
        # Make colorbar smaller
        fig.update_layout(coloraxis_colorbar=dict(thickness=10, len=0.5))

    # Set the theme
    if dark_theme:
        fig.update_layout(template="plotly_dark")

    # Display/save/return the figure
    if output_path is not None:
        fig.write_image(output_path)

    if output_path_html is not None:
        fig.write_html(output_path_html)

    if display_plot:
        fig.show()

    return fig

plot_heatmap(dataframe_data, horizontal_variable, vertical_variable, color_variable, link=None, position_qr='top-right', plot_contours=True, xlabel=None, ylabel=None, tick_interval=2, round_xticks=None, round_yticks=None, symmetric_missing=False, mask_lower_triangle=False, mask_upper_triangle=False, plot_diagonal_lines=True, shift_diagonal_lines=1, xaxis_ticks_on_top=True, title='', vmin=4.5, vmax=7.5, k_masking=-1, green_contour=6.0, min_level_contours=1, max_level_contours=15, delta_levels_contours=0.5, figsize=None, label_cbar='Minimum DA (' + '$\\sigma$' + ')', colormap='coolwarm_r', style='ggplot', output_path='output.png', display_plot=True, latex_fonts=True, vectorize=False, fill_missing_value_with=None, dpi=300)

Plots a heatmap from the given dataframe.

Parameters:

Name Type Description Default
dataframe_data DataFrame

The dataframe containing the data to plot.

required
horizontal_variable str

The variable to plot on the horizontal axis.

required
vertical_variable str

The variable to plot on the vertical axis.

required
color_variable str

The variable to use for the color scale.

required
link Optional[str]

A link to encode in a QR code. Defaults to None.

None
plot_contours bool

Whether to plot contours. Defaults to True.

True
xlabel Optional[str]

The label for the x-axis. Defaults to None.

None
ylabel Optional[str]

The label for the y-axis. Defaults to None.

None
tick_interval int

The interval for the ticks. Defaults to 2.

2
round_xticks Optional[int]

The number of decimal places to round the x-ticks to. Defaults to None.

None
round_yticks Optional[int]

The number of decimal places to round the y-ticks to. Defaults to None.

None
symmetric_missing bool

Whether to make the matrix symmetric by replacing the lower triangle with the upper triangle. Defaults to False.

False
mask_lower_triangle bool

Whether to mask the lower triangle. Defaults to False.

False
mask_upper_triangle bool

Whether to mask the upper triangle. Defaults to False.

False
plot_diagonal_lines bool

Whether to plot diagonal lines. Defaults to True.

True
shift_diagonal_lines int

The shift for the diagonal lines. Defaults to 1.

1
xaxis_ticks_on_top bool

Whether to place the x-axis ticks on top. Defaults to True.

True
title str

The title of the plot. Defaults to "".

''
vmin float

The minimum value for the color scale. Defaults to 4.5.

4.5
vmax float

The maximum value for the color scale. Defaults to 7.5.

7.5
k_masking int

The k parameter for masking. Defaults to -1.

-1
green_contour Optional[float]

The value for the green contour line. Defaults to 6.0.

6.0
min_level_contours float

The minimum level for the contours. Defaults to 1.

1
max_level_contours float

The maximum level for the contours. Defaults to 15.

15
delta_levels_contours float

The delta between contour levels. Defaults to 0.5.

0.5
figsize Optional[tuple[float, float]]

The size of the figure. Defaults to None.

None
label_cbar str

The label for the colorbar. Defaults to "Minimum DA ($\sigma$)".

'Minimum DA (' + '$\\sigma$' + ')'
colormap str

The colormap to use. Defaults to "coolwarm_r".

'coolwarm_r'
style str

The style to use for the plot. Defaults to "ggplot".

'ggplot'
output_path str

The path to save the plot. Defaults to "output.pdf".

'output.png'
display_plot bool

Whether to display the plot. Defaults to True.

True
latex_fonts bool

Whether to use LaTeX fonts. Defaults to True.

True
vectorize bool

Whether to vectorize the plot. Defaults to False.

False
fill_missing_value_with Optional[str | float]

The value to fill missing values with. Can be a number or 'interpolate'. Defaults to None.

None
dpi int

The DPI for the plot. Defaults to 300.

300

Returns:

Type Description
tuple[Figure, Axes]

tuple[plt.Figure, plt.Axes]: The figure and axes of the plot.

Source code in study_da/plot/plot_study.py
def plot_heatmap(
    dataframe_data: pd.DataFrame,
    horizontal_variable: str,
    vertical_variable: str,
    color_variable: str,
    link: Optional[str] = None,
    position_qr: Optional[str] = "top-right",
    plot_contours: bool = True,
    xlabel: Optional[str] = None,
    ylabel: Optional[str] = None,
    tick_interval: int = 2,
    round_xticks: Optional[int] = None,
    round_yticks: Optional[int] = None,
    symmetric_missing: bool = False,
    mask_lower_triangle: bool = False,
    mask_upper_triangle: bool = False,
    plot_diagonal_lines: bool = True,
    shift_diagonal_lines: int = 1,
    xaxis_ticks_on_top: bool = True,
    title: str = "",
    vmin: float = 4.5,
    vmax: float = 7.5,
    k_masking: int = -1,
    green_contour: Optional[float] = 6.0,
    min_level_contours: float = 1,
    max_level_contours: float = 15,
    delta_levels_contours: float = 0.5,
    figsize: Optional[tuple[float, float]] = None,
    label_cbar: str = "Minimum DA (" + r"$\sigma$" + ")",
    colormap: str = "coolwarm_r",
    style: str = "ggplot",
    output_path: str = "output.png",
    display_plot: bool = True,
    latex_fonts: bool = True,
    vectorize: bool = False,
    fill_missing_value_with: Optional[str | float] = None,
    dpi=300,
) -> tuple[plt.Figure, plt.Axes]:
    """
    Plots a heatmap from the given dataframe.

    Args:
        dataframe_data (pd.DataFrame): The dataframe containing the data to plot.
        horizontal_variable (str): The variable to plot on the horizontal axis.
        vertical_variable (str): The variable to plot on the vertical axis.
        color_variable (str): The variable to use for the color scale.
        link (Optional[str], optional): A link to encode in a QR code. Defaults to None.
        plot_contours (bool, optional): Whether to plot contours. Defaults to True.
        xlabel (Optional[str], optional): The label for the x-axis. Defaults to None.
        ylabel (Optional[str], optional): The label for the y-axis. Defaults to None.
        tick_interval (int, optional): The interval for the ticks. Defaults to 2.
        round_xticks (Optional[int], optional): The number of decimal places to round the x-ticks to.
            Defaults to None.
        round_yticks (Optional[int], optional): The number of decimal places to round the y-ticks to.
            Defaults to None.
        symmetric_missing (bool, optional): Whether to make the matrix symmetric by replacing the
            lower triangle with the upper triangle. Defaults to False.
        mask_lower_triangle (bool, optional): Whether to mask the lower triangle. Defaults to False.
        mask_upper_triangle (bool, optional): Whether to mask the upper triangle. Defaults to False.
        plot_diagonal_lines (bool, optional): Whether to plot diagonal lines. Defaults to True.
        shift_diagonal_lines (int, optional): The shift for the diagonal lines. Defaults to 1.
        xaxis_ticks_on_top (bool, optional): Whether to place the x-axis ticks on top. Defaults to True.
        title (str, optional): The title of the plot. Defaults to "".
        vmin (float, optional): The minimum value for the color scale. Defaults to 4.5.
        vmax (float, optional): The maximum value for the color scale. Defaults to 7.5.
        k_masking (int, optional): The k parameter for masking. Defaults to -1.
        green_contour (Optional[float], optional): The value for the green contour line. Defaults to 6.0.
        min_level_contours (float, optional): The minimum level for the contours. Defaults to 1.
        max_level_contours (float, optional): The maximum level for the contours. Defaults to 15.
        delta_levels_contours (float, optional): The delta between contour levels. Defaults to 0.5.
        figsize (Optional[tuple[float, float]], optional): The size of the figure. Defaults to None.
        label_cbar (str, optional): The label for the colorbar. Defaults to "Minimum DA ($\sigma$)".
        colormap (str, optional): The colormap to use. Defaults to "coolwarm_r".
        style (str, optional): The style to use for the plot. Defaults to "ggplot".
        output_path (str, optional): The path to save the plot. Defaults to "output.pdf".
        display_plot (bool, optional): Whether to display the plot. Defaults to True.
        latex_fonts (bool, optional): Whether to use LaTeX fonts. Defaults to True.
        vectorize (bool, optional): Whether to vectorize the plot. Defaults to False.
        fill_missing_value_with (Optional[str | float], optional): The value to fill missing values
            with. Can be a number or 'interpolate'. Defaults to None.
        dpi (int, optional): The DPI for the plot. Defaults to 300.

    Returns:
        tuple[plt.Figure, plt.Axes]: The figure and axes of the plot.
    """
    # Use the requested style
    _set_style(style, latex_fonts, vectorize)

    # Get the dataframe to plot
    df_to_plot = dataframe_data.pivot(
        index=vertical_variable, columns=horizontal_variable, values=color_variable
    )

    # Get numpy array from dataframe
    data_array = df_to_plot.to_numpy(dtype=float)

    # Replace NaNs with a value if requested
    if fill_missing_value_with is not None:
        if isinstance(fill_missing_value_with, (int, float)):
            data_array[np.isnan(data_array)] = fill_missing_value_with
        elif fill_missing_value_with == "interpolate":
            # Interpolate missing values with griddata
            x = np.arange(data_array.shape[1])
            y = np.arange(data_array.shape[0])
            xx, yy = np.meshgrid(x, y)
            x = xx[~np.isnan(data_array)]
            y = yy[~np.isnan(data_array)]
            z = data_array[~np.isnan(data_array)]
            data_array = griddata((x, y), z, (xx, yy), method="cubic")

    # Mask the lower or upper triangle (checks are done in the function)
    data_array_masked, mask_main_array = _mask(
        mask_lower_triangle, mask_upper_triangle, data_array, k_masking
    )

    # Define colormap and set NaNs to white
    cmap = matplotlib.colormaps.get_cmap(colormap)
    cmap.set_bad("w")

    # Build heatmap, with inverted y axis
    fig, ax = plt.subplots()
    if figsize is not None:
        fig.set_size_inches(figsize)
    im = ax.imshow(data_array_masked, cmap=cmap, vmin=vmin, vmax=vmax)
    ax.invert_yaxis()

    # Add text annotations
    ax = _add_text_annotation(df_to_plot, data_array, ax, vmin, vmax)

    # Smooth data for contours
    mx = _smooth(data_array, symmetric_missing)

    # Plot contours if requested
    if plot_contours:
        ax = _add_contours(
            ax,
            data_array,
            mx,
            green_contour,
            min_level_contours,
            max_level_contours,
            delta_levels_contours,
            mask_main_array,
        )

    if plot_diagonal_lines:
        # Diagonal lines must be plotted after the contour lines, because of bug in matplotlib
        # Shift might need to be adjusted
        ax = _add_diagonal_lines(ax, shift=shift_diagonal_lines)

    # Define title and axis labels
    ax.set_title(
        title,
        fontsize=10,
    )

    # Set axis labels
    ax = _set_labels(
        ax,
        df_to_plot,
        data_array,
        horizontal_variable,
        vertical_variable,
        xlabel,
        ylabel,
        xaxis_ticks_on_top,
        tick_interval,
        round_xticks,
        round_yticks,
    )

    # Create colorbar
    cbar = ax.figure.colorbar(im, ax=ax, fraction=0.026, pad=0.04)
    cbar.ax.set_ylabel(label_cbar, rotation=90, va="bottom", labelpad=15)

    # Remove potential grid
    plt.grid(visible=None)

    # Add QR code with a link to the topright side (a bit experimental, might need adjustments)
    if link is not None:
        fig = add_QR_code(fig, link, position_qr)

    # Save and potentially display the plot
    if output_path is not None:
        if output_path.endswith(".pdf") and not vectorize:
            raise ValueError("Please set vectorize=True to save as PDF")
        elif not output_path.endswith(".pdf") and vectorize:
            raise ValueError("Please set vectorize=False to save as PNG or JPG")
        plt.savefig(output_path, bbox_inches="tight", dpi=dpi)

    if display_plot:
        plt.show()
    return fig, ax