<commit_before>"""
This module contains the functions for plotting and comparing isotherms.
"""

import matplotlib.pyplot as plt
from cycler import cycler
from matplotlib import cm
from numpy import linspace

from ..utilities.exceptions import GraphingError
from ..utilities.exceptions import ParameterError
from ..utilities.string_utilities import convert_chemformula

# ! list of plot types
_PLOT_TYPES = ("isotherm", "property", "combined")

# ! list of branch types
_BRANCH_TYPES = ("ads", "des")


def plot_iso(isotherms,
             plot_type='isotherm', secondary_key=None,
             branch=_BRANCH_TYPES, logx=False, color=True,

             adsorbent_basis="mass",
             adsorbent_unit="g",
             loading_basis="molar",
             loading_unit="mmol",
             pressure_mode="absolute",
             pressure_unit="bar",

             x_range=(None, None),
             y1_range=(None, None),
             y2_range=(None, None),

             fig_title=None,
             legend_list=None,
             legend_bottom=False,

             save_path=None,
             **other_parameters):
    """
    Plots the isotherm(s) provided on a single graph.

    Parameters
    ----------
    isotherms : list
        An iterable of the isotherms to be plotted.
    plot_type : {'isotherm', 'property', 'combined'}
        The plot type, to display: isotherm, a property or a combination.
        The 'isotherm' graph type displays only isotherm data and is the standard.
        If other data is recorded in the isotherm object, such as enthalpy, it
        can be displayed at the same time as the isotherm or in a property v
        loading graph by selecting one of the other graph types.
    secondary_key : 'str'
        The key which has the column with the supplementary data to be plotted.
        This parameter is only required in the 'property' and 'combined' graphs.
    branch : list
        List with branches to disply, options: 'ads', 'des'.
    logx : bool
        Whether the graph x axis should be logarithmic.
    color : bool, optional
        Whether the graph should be coloured or grayscale. Grayscale graphs
        are usually preferred for publications or print media.

    adsorbent_basis : str, optional
        Whether the adsorption is read in terms of either 'per volume'
        or 'per mass'.
    adsorbent_unit : str, optional
        Unit of loading.
    loading_basis : str, optional
        Loading basis.
    loading_unit : str, optional
        Unit of loading.
    pressure_mode : str, optional
        The pressure mode, either absolute pressures or relative in
        the form of p/p0.
    pressure_unit : str, optional
        Unit of pressure.

    x_range : tuple
        Range for data on the x axis. eg: (0, 1). Is applied to each
        isotherm, in the unit/mode/basis requested.
    y1_range : tuple
        Range for data on the regular y axis. eg: (0, 1). Is applied to each
        isotherm, in the unit/mode/basis requested.
    y2_range : tuple
        Range for data on the secondary y axis. eg: (0, 1). Is applied to each
        isotherm, in the unit/mode/basis requested.

    legend_list : iterable
        The components of the isotherm which are displayed on the legend. For example
        pass ['sample_batch', 'adsorbate'] to have the legend labels display only these
        two components. Defaults to the sample name and adsorbate.
    fig_title : str
        Title of the graph. Defaults to type of graph.
    legend_bottom : bool
        Specify as True to have the legend at the bottom of the graph. Alternatively,
        when plotting a lot of data, the legend is best displayed separately to the
        side of the graph. In that case, set this to False.

    save_path : str, optional
        Whether to save the graph or not.
        If a path is provided, then that is where the graph will be saved.

    Other Parameters
    ----------------
    title_style : dict
        A dictionary that will be passed into the matplotlib set_title() function.

    label_style : dict
        A dictionary that will be passed into the matplotlib set_label() function.

    line_style : dict
        A dictionary that will be passed into the matplotlib plot() function.

    tick_style : dict
        A dictionary that will be passed into the matplotlib tick_params() function.

    legend_style : dict
        A dictionary that will be passed into the matplotlib legend() function.

    save_style : dict
        A dictionary that will be passed into the matplotlib savefig() function
        if the saving of the figure is selected.

    Returns
    -------
    fig : Matplotlib figure
        The figure object generated.
    axes1 : Matplotlib ax
        Ax object for primary graph.
    axes2 : Matplotlib ax
        Ax object for secondary graph.

    """
#######################################
#
# Initial checks
    #
    # Check for plot type validity
    if plot_type is None:
        raise ParameterError("Specify a plot type to graph"
                             " e.g. plot_type=\'isotherm\'")
    if plot_type not in _PLOT_TYPES:
        raise ParameterError("Plot type {0} not an option. Viable plot"
                             "types are {1}".format(plot_type, _PLOT_TYPES))
    if plot_type == 'property' or plot_type == 'combined':
        if secondary_key is None:
            raise ParameterError("No secondary_key parameter specified")
        if all(secondary_key not in isotherm.other_keys for isotherm in isotherms):
            raise GraphingError(
                "None of the isotherms supplied have {} data".format(secondary_key))

    # Store which branches will be displayed
    if branch is None:
        raise ParameterError("Specify a branch to display"
                             " e.g. branch=\'ads\'")
    if not [i for i in branch if i in _BRANCH_TYPES]:
        raise GraphingError("One of the supplied branch types is not valid."
                            "Viable types are {}".format(_BRANCH_TYPES))

    ads = False
    des = False
    if 'ads' in branch:
        ads = True
    if 'des' in branch:
        des = True

    # Create empty axes object
    axes2 = None

    # Add limits for data x and y
    range_pressure = None
    range_property = None
    range_loading = None

#######################################
#
# Settings and graph generation
    #
    # Generate the graph itself
    fig, axes = plt.subplots(1, 1, figsize=(8, 8))
    if plot_type == 'combined':
        axes2 = axes.twinx()

    # Build the name of the axes
    text_xaxis = r'Pressure'
    text_yaxis = r'Loading'
    if secondary_key == 'enthalpy':
        text_y2axis = r'Enthalpy of adsorption $(-kJ\/mol^{-1})$'
    else:
        text_y2axis = secondary_key
    if pressure_mode == "absolute":
        text_xaxis = text_xaxis + ' ($' + pressure_unit + '$)'
    elif pressure_mode == "relative":
        text_xaxis = "Relative " + text_xaxis
    text_yaxis = text_yaxis + \
        ' ($' + loading_unit + '/' + adsorbent_unit + '$)'

    # Set the colours of the graph
    cy1 = cycler('marker', ['o', 's'])
    cy2 = cycler('marker', ['v', '^'])
    cy4 = cycler('marker', ['v', '^', '<', '>'])

    if color:
        number_of_lines = 7
        if not isinstance(color, bool):
            number_of_lines = color

        colors = [cm.jet(x) for x in linspace(0, 1, number_of_lines)]
        if isinstance(color, list):
            colors = color

        polychrome_cy = cycler('color', colors)
        pc_iso = (cy1 * polychrome_cy)
        pc_prop = (cy2 * polychrome_cy)
    else:
        monochrome_cy = cycler('color', ['black', 'grey', 'silver'])
        linestyle_cy = cycler('linestyle', ['-', '--', ':', '-.'])
        pc_iso = (cy1 * linestyle_cy * monochrome_cy)
        pc_prop = (cy4 * linestyle_cy * monochrome_cy)

    # Set the colours and ranges for the axes
    if plot_type == 'isotherm':
        axes.set_prop_cycle(pc_iso)
        range_pressure = x_range
        range_loading = y1_range
        range_property = (None, None)
    elif plot_type == 'property':
        axes.set_prop_cycle(pc_prop)
        range_pressure = (None, None)
        range_loading = x_range
        range_property = y1_range
    elif plot_type == 'combined':
        axes.set_prop_cycle(pc_iso)
        axes2.set_prop_cycle(pc_prop)
        range_pressure = x_range
        range_loading = y1_range
        range_property = y2_range

    # Styles in the graph
    styles = dict(title_style=dict(horizontalalignment='center',
                                   fontsize=25, fontdict={'family': 'monospace'}),
                  label_style=dict(horizontalalignment='center',
                                   fontsize=20, fontdict={'family': 'monospace'}),
                  line_style=dict(linewidth=2, markersize=8),
                  tick_style=dict(labelsize=17),
                  legend_style=dict(handlelength=3, fontsize=15, loc='best'),
                  )

    styles.update(other_parameters)  # Update with any user provided styles

    # Put grid on plot
    axes.grid(True, zorder=5)

    # Graph title
    if fig_title is None:
        fig_title = plot_type.capitalize() + ' Graph'
    axes.set_title(fig_title, **styles['title_style'])

    # Graph legend builder
    def build_label(lbl_components, isotherm):
        """
        Builds a label for the legend depending on requested parameters
        """
        if lbl_components is None:<commit_msg>plot_iso now accepts single or multiple isotherms<commit_after>"""
This module contains the functions for plotting and comparing isotherms.
"""

import collections

import matplotlib.pyplot as plt
from cycler import cycler
from matplotlib import cm
from numpy import linspace

from ..utilities.exceptions import GraphingError
from ..utilities.exceptions import ParameterError
from ..utilities.string_utilities import convert_chemformula

# ! list of plot types
_PLOT_TYPES = ("isotherm", "property", "combined")

# ! list of branch types
_BRANCH_TYPES = ("ads", "des")


def plot_iso(isotherms,
             plot_type='isotherm', secondary_key=None,
             branch=_BRANCH_TYPES, logx=False, color=True,

             adsorbent_basis="mass",
             adsorbent_unit="g",
             loading_basis="molar",
             loading_unit="mmol",
             pressure_mode="absolute",
             pressure_unit="bar",

             x_range=(None, None),
             y1_range=(None, None),
             y2_range=(None, None),

             fig_title=None,
             legend_list=None,
             legend_bottom=False,

             save_path=None,
             **other_parameters):
    """
    Plots the isotherm(s) provided on a single graph.

    Parameters
    ----------
    isotherms : PointIsotherms or list of Pointisotherms
        An isotherm or iterable of isotherms to be plotted.
    plot_type : {'isotherm', 'property', 'combined'}
        The plot type, to display: isotherm, a property or a combination.
        The 'isotherm' graph type displays only isotherm data and is the standard.
        If other data is recorded in the isotherm object, such as enthalpy, it
        can be displayed at the same time as the isotherm or in a property v
        loading graph by selecting one of the other graph types.
    secondary_key : 'str'
        The key which has the column with the supplementary data to be plotted.
        This parameter is only required in the 'property' and 'combined' graphs.
    branch : list
        List with branches to disply, options: 'ads', 'des'.
    logx : bool
        Whether the graph x axis should be logarithmic.
    color : bool, optional
        Whether the graph should be coloured or grayscale. Grayscale graphs
        are usually preferred for publications or print media.

    adsorbent_basis : str, optional
        Whether the adsorption is read in terms of either 'per volume'
        or 'per mass'.
    adsorbent_unit : str, optional
        Unit of loading.
    loading_basis : str, optional
        Loading basis.
    loading_unit : str, optional
        Unit of loading.
    pressure_mode : str, optional
        The pressure mode, either absolute pressures or relative in
        the form of p/p0.
    pressure_unit : str, optional
        Unit of pressure.

    x_range : tuple
        Range for data on the x axis. eg: (0, 1). Is applied to each
        isotherm, in the unit/mode/basis requested.
    y1_range : tuple
        Range for data on the regular y axis. eg: (0, 1). Is applied to each
        isotherm, in the unit/mode/basis requested.
    y2_range : tuple
        Range for data on the secondary y axis. eg: (0, 1). Is applied to each
        isotherm, in the unit/mode/basis requested.

    legend_list : iterable
        The components of the isotherm which are displayed on the legend. For example
        pass ['sample_batch', 'adsorbate'] to have the legend labels display only these
        two components. Defaults to the sample name and adsorbate.
    fig_title : str
        Title of the graph. Defaults to type of graph.
    legend_bottom : bool
        Specify as True to have the legend at the bottom of the graph. Alternatively,
        when plotting a lot of data, the legend is best displayed separately to the
        side of the graph. In that case, set this to False.

    save_path : str, optional
        Whether to save the graph or not.
        If a path is provided, then that is where the graph will be saved.

    Other Parameters
    ----------------
    title_style : dict
        A dictionary that will be passed into the matplotlib set_title() function.

    label_style : dict
        A dictionary that will be passed into the matplotlib set_label() function.

    line_style : dict
        A dictionary that will be passed into the matplotlib plot() function.

    tick_style : dict
        A dictionary that will be passed into the matplotlib tick_params() function.

    legend_style : dict
        A dictionary that will be passed into the matplotlib legend() function.

    save_style : dict
        A dictionary that will be passed into the matplotlib savefig() function
        if the saving of the figure is selected.

    Returns
    -------
    fig : Matplotlib figure
        The figure object generated.
    axes1 : Matplotlib ax
        Ax object for primary graph.
    axes2 : Matplotlib ax
        Ax object for secondary graph.

    """
#######################################
#
# Initial checks
    # Make iterable if not already
    if not isinstance(isotherms, collections.Iterable):
        isotherms = [isotherms]

    # Check for plot type validity
    if plot_type is None:
        raise ParameterError("Specify a plot type to graph"
                             " e.g. plot_type=\'isotherm\'")
    if plot_type not in _PLOT_TYPES:
        raise ParameterError("Plot type {0} not an option. Viable plot"
                             "types are {1}".format(plot_type, _PLOT_TYPES))
    if plot_type == 'property' or plot_type == 'combined':
        if secondary_key is None:
            raise ParameterError("No secondary_key parameter specified")
        if all(secondary_key not in isotherm.other_keys for isotherm in isotherms):
            raise GraphingError(
                "None of the isotherms supplied have {} data".format(secondary_key))

    # Store which branches will be displayed
    if branch is None:
        raise ParameterError("Specify a branch to display"
                             " e.g. branch=\'ads\'")
    if not [i for i in branch if i in _BRANCH_TYPES]:
        raise GraphingError("One of the supplied branch types is not valid."
                            "Viable types are {}".format(_BRANCH_TYPES))

    ads = False
    des = False
    if 'ads' in branch:
        ads = True
    if 'des' in branch:
        des = True

    # Create empty axes object
    axes2 = None

    # Add limits for data x and y
    range_pressure = None
    range_property = None
    range_loading = None

#######################################
#
# Settings and graph generation
    #
    # Generate the graph itself
    fig, axes = plt.subplots(1, 1, figsize=(8, 8))
    if plot_type == 'combined':
        axes2 = axes.twinx()

    # Build the name of the axes
    text_xaxis = r'Pressure'
    text_yaxis = r'Loading'
    if secondary_key == 'enthalpy':
        text_y2axis = r'Enthalpy of adsorption $(-kJ\/mol^{-1})$'
    else:
        text_y2axis = secondary_key
    if pressure_mode == "absolute":
        text_xaxis = text_xaxis + ' ($' + pressure_unit + '$)'
    elif pressure_mode == "relative":
        text_xaxis = "Relative " + text_xaxis
    text_yaxis = text_yaxis + \
        ' ($' + loading_unit + '/' + adsorbent_unit + '$)'

    # Set the colours of the graph
    cy1 = cycler('marker', ['o', 's'])
    cy2 = cycler('marker', ['v', '^'])
    cy4 = cycler('marker', ['v', '^', '<', '>'])

    if color:
        number_of_lines = 7
        if not isinstance(color, bool):
            number_of_lines = color

        colors = [cm.jet(x) for x in linspace(0, 1, number_of_lines)]
        if isinstance(color, list):
            colors = color

        polychrome_cy = cycler('color', colors)
        pc_iso = (cy1 * polychrome_cy)
        pc_prop = (cy2 * polychrome_cy)
    else:
        monochrome_cy = cycler('color', ['black', 'grey', 'silver'])
        linestyle_cy = cycler('linestyle', ['-', '--', ':', '-.'])
        pc_iso = (cy1 * linestyle_cy * monochrome_cy)
        pc_prop = (cy4 * linestyle_cy * monochrome_cy)

    # Set the colours and ranges for the axes
    if plot_type == 'isotherm':
        axes.set_prop_cycle(pc_iso)
        range_pressure = x_range
        range_loading = y1_range
        range_property = (None, None)
    elif plot_type == 'property':
        axes.set_prop_cycle(pc_prop)
        range_pressure = (None, None)
        range_loading = x_range
        range_property = y1_range
    elif plot_type == 'combined':
        axes.set_prop_cycle(pc_iso)
        axes2.set_prop_cycle(pc_prop)
        range_pressure = x_range
        range_loading = y1_range
        range_property = y2_range

    # Styles in the graph
    styles = dict(title_style=dict(horizontalalignment='center',
                                   fontsize=25, fontdict={'family': 'monospace'}),
                  label_style=dict(horizontalalignment='center',
                                   fontsize=20, fontdict={'family': 'monospace'}),
                  line_style=dict(linewidth=2, markersize=8),
                  tick_style=dict(labelsize=17),
                  legend_style=dict(handlelength=3, fontsize=15, loc='best'),
                  save_style=dict(),
                  )

    styles.update(other_parameters)  # Update with any user provided styles

    # Put grid on plot
    axes.grid(True, zorder=5)

    # Graph title
    if fig_title is None:
        fig_title = plot_type.capitalize() + ' Graph'
    axes.set_title(fig_title, **styles['title_style'])

    # Graph legend builder
    def build_label(lbl_components, isotherm):
        """
        Builds a label for the legend depending on requested parameters
        """
        if lbl_components is None:
{'fraction_non_alphanumeric': 0.09165064685405518, 'fraction_numerical': 0.005299527199044007, 'mean_word_length': 2.320910973084886, 'pattern_counts': {'":': 4, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1442091', 'n_tokens_mistral': 5564, 'n_tokens_neox': 5341, 'n_words': 1916}