Beispiel #1
0
def dataplot(comps,
             phases,
             conds,
             datasets,
             ax=None,
             plot_kwargs=None,
             tieline_plot_kwargs=None):
    """
    Plot datapoints corresponding to the components, phases, and conditions.


    Parameters
    ----------
    comps : list
        Names of components to consider in the calculation.
    phases : []
        Names of phases to consider in the calculation.
    conds : dict
        Maps StateVariables to values and/or iterables of values.
    datasets : PickleableTinyDB
    ax : matplotlib.Axes
        Default axes used if not specified.
    plot_kwargs : dict
        Additional keyword arguments to pass to the matplotlib plot function for points
    tieline_plot_kwargs : dict
        Additional keyword arguments to pass to the matplotlib plot function for tielines

    Returns
    -------
    matplotlib.Axes
        A plot of phase equilibria points as a figure

    Examples
    --------

    >>> from espei.datasets import load_datasets, recursive_glob
    >>> from espei.plot import dataplot
    >>> datasets = load_datasets(recursive_glob('.', '*.json'))
    >>> my_phases = ['BCC_A2', 'CUMG2', 'FCC_A1', 'LAVES_C15', 'LIQUID']
    >>> my_components = ['CU', 'MG' 'VA']
    >>> conditions = {v.P: 101325, v.T: (500, 1000, 10), v.X('MG'): (0, 1, 0.01)}
    >>> dataplot(my_components, my_phases, conditions, datasets)

    """
    indep_comps = [
        key for key, value in conds.items()
        if isinstance(key, v.Composition) and len(np.atleast_1d(value)) > 1
    ]
    indep_pots = [
        key for key, value in conds.items()
        if ((key == v.T) or (key == v.P)) and len(np.atleast_1d(value)) > 1
    ]
    plot_kwargs = plot_kwargs or {}
    phases = sorted(phases)

    # determine what the type of plot will be
    if len(indep_comps) == 1 and len(indep_pots) == 1:
        projection = None
    elif len(indep_comps) == 2 and len(indep_pots) == 0:
        projection = 'triangular'
    else:
        raise ValueError(
            'The eqplot projection is not defined and cannot be autodetected. There are {} independent compositions and {} indepedent potentials.'
            .format(len(indep_comps), len(indep_pots)))

    if projection is None:
        x = indep_comps[0].species
        y = indep_pots[0]
    elif projection == 'triangular':
        x = indep_comps[0].species
        y = indep_comps[1].species

    # set up plot if not done already
    if ax is None:
        ax = plt.gca(projection=projection)
        box = ax.get_position()
        ax.set_position([box.x0, box.y0, box.width * 0.8, box.height])
        ax.tick_params(axis='both', which='major', labelsize=14)
        ax.grid(True)
        plot_title = '-'.join([
            component.title() for component in sorted(comps)
            if component != 'VA'
        ])
        ax.set_title(plot_title, fontsize=20)
        ax.set_xlabel('X({})'.format(x), labelpad=15, fontsize=20)
        ax.set_xlim((0, 1))
        if projection is None:
            ax.set_ylabel(plot_mapping.get(str(y), y), fontsize=20)
        elif projection == 'triangular':
            ax.set_ylabel('X({})'.format(y), labelpad=15, fontsize=20)
            ax.set_ylim((0, 1))
            ax.yaxis.label.set_rotation(60)
            # Here we adjust the x coordinate of the ylabel.
            # We make it reasonably comparable to the position of the xlabel from the xaxis
            # As the figure size gets very large, the label approaches ~0.55 on the yaxis
            # 0.55*cos(60 deg)=0.275, so that is the xcoord we are approaching.
            ax.yaxis.label.set_va('baseline')
            fig_x_size = ax.figure.get_size_inches()[0]
            y_label_offset = 1 / fig_x_size
            ax.yaxis.set_label_coords(x=(0.275 - y_label_offset), y=0.5)

    output = 'ZPF'
    # TODO: used to include VA. Should this be added by default. Can't determine presence of VA in eq.
    # Techincally, VA should not be present in any phase equilibria.
    desired_data = datasets.search(
        (tinydb.where('output') == output) & (tinydb.where('components').test(
            lambda x: set(x).issubset(comps + ['VA'])))
        & (tinydb.where('phases').test(
            lambda x: len(set(phases).intersection(x)) > 0)))

    # get all the possible references from the data and create the bibliography map
    bib_reference_keys = sorted(
        list({entry['reference']
              for entry in desired_data}))
    symbol_map = bib_marker_map(bib_reference_keys)

    # The above handled the phases as in the equilibrium, but there may be
    # phases that are in the datasets but not in the equilibrium diagram that
    # we would like to plot point for (they need color maps).
    # To keep consistent colors with the equilibrium diagram, we will append
    # the new phases from the datasets to the existing phases in the equilibrium
    # calculation.
    data_phases = set()
    for entry in desired_data:
        data_phases.update(set(entry['phases']))
    new_phases = sorted(list(data_phases.difference(set(phases))))
    phases.extend(new_phases)
    legend_handles, phase_color_map = phase_legend(phases)

    if projection is None:
        # TODO: There are lot of ways this could break in multi-component situations

        # plot x vs. T
        y = 'T'

        # handle plotting kwargs
        scatter_kwargs = {'markersize': 6, 'markeredgewidth': 1}
        # raise warnings if any of the aliased versions of the default values are used
        possible_aliases = [('markersize', 'ms'), ('markeredgewidth', 'mew')]
        for actual_arg, aliased_arg in possible_aliases:
            if aliased_arg in plot_kwargs:
                warnings.warn(
                    "'{0}' passed as plotting keyword argument to dataplot, but the alias '{1}' is already set to '{2}'. Use the full version of the keyword argument '{1}' to override the default."
                    .format(aliased_arg, actual_arg,
                            scatter_kwargs.get(actual_arg)))
        scatter_kwargs.update(plot_kwargs)

        eq_dict = ravel_zpf_values(desired_data, [x])

        # two phase
        updated_tieline_plot_kwargs = {'linewidth': 1, 'color': 'k'}
        if tieline_plot_kwargs is not None:
            updated_tieline_plot_kwargs.update(tieline_plot_kwargs)
        for eq in eq_dict.get(2, []):  # list of things in equilibrium
            # plot the scatter points for the right phases
            x_points, y_points = [], []
            for phase_name, comp_dict, ref_key in eq:
                sym_ref = symbol_map[ref_key]
                x_val, y_val = comp_dict[x], comp_dict[y]
                if x_val is not None and y_val is not None:
                    ax.plot(x_val,
                            y_val,
                            label=sym_ref['formatted'],
                            fillstyle=sym_ref['markers']['fillstyle'],
                            marker=sym_ref['markers']['marker'],
                            linestyle='',
                            color=phase_color_map[phase_name],
                            **scatter_kwargs)
                x_points.append(x_val)
                y_points.append(y_val)

            # plot the tielines
            if all([
                    xx is not None and yy is not None
                    for xx, yy in zip(x_points, y_points)
            ]):
                ax.plot(x_points, y_points, **updated_tieline_plot_kwargs)

    elif projection == 'triangular':
        scatter_kwargs = {'markersize': 4, 'markeredgewidth': 0.4}
        # raise warnings if any of the aliased versions of the default values are used
        possible_aliases = [('markersize', 'ms'), ('markeredgewidth', 'mew')]
        for actual_arg, aliased_arg in possible_aliases:
            if aliased_arg in plot_kwargs:
                warnings.warn(
                    "'{0}' passed as plotting keyword argument to dataplot, but the alias '{1}' is already set to '{2}'. Use the full version of the keyword argument '{1}' to override the default."
                    .format(aliased_arg, actual_arg,
                            scatter_kwargs.get(actual_arg)))
        scatter_kwargs.update(plot_kwargs)

        eq_dict = ravel_zpf_values(desired_data, [x, y], {'T': conds[v.T]})

        # two phase
        updated_tieline_plot_kwargs = {'linewidth': 1, 'color': 'k'}
        if tieline_plot_kwargs is not None:
            updated_tieline_plot_kwargs.update(tieline_plot_kwargs)
        for eq in eq_dict.get(2, []):  # list of things in equilibrium
            # plot the scatter points for the right phases
            x_points, y_points = [], []
            for phase_name, comp_dict, ref_key in eq:
                sym_ref = symbol_map[ref_key]
                x_val, y_val = comp_dict[x], comp_dict[y]
                if x_val is not None and y_val is not None:
                    ax.plot(x_val,
                            y_val,
                            label=sym_ref['formatted'],
                            fillstyle=sym_ref['markers']['fillstyle'],
                            marker=sym_ref['markers']['marker'],
                            linestyle='',
                            color=phase_color_map[phase_name],
                            **scatter_kwargs)
                x_points.append(x_val)
                y_points.append(y_val)

            # plot the tielines
            if all([
                    xx is not None and yy is not None
                    for xx, yy in zip(x_points, y_points)
            ]):
                ax.plot(x_points, y_points, **updated_tieline_plot_kwargs)

        # three phase
        updated_tieline_plot_kwargs = {'linewidth': 1, 'color': 'r'}
        if tieline_plot_kwargs is not None:
            updated_tieline_plot_kwargs.update(tieline_plot_kwargs)
        for eq in eq_dict.get(3, []):  # list of things in equilibrium
            # plot the scatter points for the right phases
            x_points, y_points = [], []
            for phase_name, comp_dict, ref_key in eq:
                x_val, y_val = comp_dict[x], comp_dict[y]
                x_points.append(x_val)
                y_points.append(y_val)
            # Make sure the triangle completes
            x_points.append(x_points[0])
            y_points.append(y_points[0])
            # plot
            # check for None values
            if all([
                    xx is not None and yy is not None
                    for xx, yy in zip(x_points, y_points)
            ]):
                ax.plot(x_points, y_points, **updated_tieline_plot_kwargs)

    # now we will add the symbols for the references to the legend handles
    for ref_key in bib_reference_keys:
        mark = symbol_map[ref_key]['markers']
        # The legend marker edge width appears smaller than in the plot.
        # We will add this small hack to increase the width in the legend only.
        legend_kwargs = scatter_kwargs.copy()
        legend_kwargs['markeredgewidth'] = 1
        legend_kwargs['markersize'] = 6
        legend_handles.append(
            mlines.Line2D([], [],
                          linestyle='',
                          color='black',
                          markeredgecolor='black',
                          label=symbol_map[ref_key]['formatted'],
                          fillstyle=mark['fillstyle'],
                          marker=mark['marker'],
                          **legend_kwargs))

    # finally, add the completed legend
    ax.legend(handles=legend_handles,
              loc='center left',
              bbox_to_anchor=(1, 0.5))

    return ax
Beispiel #2
0
# load the experimental and DFT datasets
datasets = load_datasets(recursive_glob(DATASETS_DIR, '*.json'))
# phases = ['LIQUID', 'BCC_A2', 'FCC_A1']




desired_data = datasets.search((tinydb.where('output') == 'ZPF') &
                               (tinydb.where('components').test(lambda x: set(x).issubset(comps + ['VA']))) #&
                              # (tinydb.where('phases').test(lambda x: len(set(phases).intersection(x)) > 0)))
                              )



raveled_dict = ravel_zpf_values(desired_data, [independent_component])


bib_reference_keys = sorted(list({entry['reference'] for entry in desired_data}))
symbol_map = bib_marker_map(bib_reference_keys)
# map matplotlib string markers to strings of markers for Thermo-Calc's POST
dataplot_symbols = ['S'+str(i) for i in range(1, 18)]
dataplot_marker_map = dict(zip([v['markers']['marker'] for v in symbol_map.values()], dataplot_symbols))



equilibria_to_plot = raveled_dict.get(2, [])
equilibria_lines = []
for eq in equilibria_to_plot:
    x_points, y_points = [], []
    for phase_name, comp_dict, ref_key in eq: