예제 #1
0
def binplot(dbf, comps, phases, x_variable, low_temp, high_temp,
            steps=None, ax=None, **kwargs):
    """
    Calculate the binary isobaric phase diagram for the given temperature
    range.

    Parameters
    ----------
    dbf : Database
        Thermodynamic database containing the relevant parameters.
    comps : list
        Names of components to consider in the calculation.
    phases : list
        Names of phases to consider in the calculation.
    x_variable : string
        Name of the x-axis variable to plot, e.g., 'X(FE)'
    low_temp : float
        Lower bound of temperature to calculate.
    high_temp : float
        Upper bound of temperature to calculate.
    steps : int, optional
        Number of temperature steps to take between `low_temp` and `high_temp`.
    ax : Matplotlib Axes object, optional
    pdens : int, optional
        Number of points to sample per sublattice, per degree of freedom.
    ast : ['numpy', 'numexpr'], optional
        Specify how we should construct the callable for the energy.

    Returns
    -------
    A phase diagram as a figure.

    Examples
    --------
    None yet.
    """
    assert high_temp > low_temp
    tie_lines = []
    tie_line_colors = []
    tie_line_widths = []
    tsteps = steps or int((high_temp-low_temp) / 10) # Take 10 K steps by def.
    temps = np.array(np.linspace(low_temp, high_temp, num=tsteps),
                     dtype=np.float64)

    # Convert all phase names to uppercase
    phases = [phase.upper() for phase in phases]

    try:
        pdens = kwargs.pop('pdens')
    except KeyError:
        pdens = 1000 # points per d.o.f

    # Calculate energy surface at each temperature
    full_df = energy_surf(dbf, comps, phases, T=temps, pdens=pdens,
                          **kwargs)
    # Select only the P, T, etc., of interest
    full_df = full_df.groupby('T', sort=False)
    for temp, hull_frame in full_df:
        # Calculate the convex hull for the desired points
        hull_points = hull_frame[[x_variable, 'GM']].values
        hull = None
        try:
            hull = scipy.spatial.ConvexHull(
                hull_points, qhull_options='QJ'
            )
            del hull_points
        except RuntimeError:
            print('temperature: '+str(temp))
            raise
        # keep track of tie line orientations
        # this is for invariant reaction detection
        tieline_normals = []
        current_tielines = []

        # this was factored out of the loop based on profiling
        coordinates = hull_frame.iloc[np.asarray(hull.simplices).ravel()].values
        # Reshape coordinates into rank 3 ndarray of simplex coordinates
        # Each point is ordered as: Energy, Phase Name, Coordinates
        coordinates.shape = (len(hull.simplices), len(hull.simplices[0]),
                             len(coordinates[0]))
        columns = list(hull_frame.columns)

        for coords, equ in \
            zip(coordinates, hull.equations):
            if equ[-2] > -1e-6:
                # simplex oriented 'upwards' in energy direction
                # must not be part of the energy surface
                continue
            #distances = scipy.spatial.distance.pdist(coords)
            #simplex_edges = \
            #    np.asarray(list(itertools.combinations(simplex, 2)))
            #phase_edge_list = hull_frame['Phase'].values[simplex_edges]
            #edge_phases_match = \
            ##    np.array(phase_edge_list[:, 0] == phase_edge_list[:, 1])
            #new_lines = simplex_edges
            # Check if this is a two phase region
            first_endpoint = coords[0]
            second_endpoint = coords[1]
            phases_match = first_endpoint[columns.index('Phase')] == \
                second_endpoint[columns.index('Phase')]

            if phases_match:
                # Is this a miscibility gap region?
                # Check that the average of the tieline energy is less than
                # the energy calculated at the midpoint
                # If not, this is a single-phase region and should be dropped
                #phase_name = first_endpoint[columns.index('Phase')]
                #input_cols = [x for x in hull_frame.columns if phase_name in x]
                #midpoint = np.mean([first_endpoint.ix[input_cols].values, \
                #    second_endpoint.ix[input_cols].values], axis=0)
                # chebyshev distance returns maximum difference between any
                # dimension
                #pxd = scipy.spatial.distance.chebyshev(
                #    first_endpoint.ix[input_cols].values, \
                #    second_endpoint.ix[input_cols].values)
                pxd = scipy.spatial.distance.chebyshev(
                    first_endpoint[columns.index(x_variable)], \
                    second_endpoint[columns.index(x_variable)])
                if pxd < 0.03:
                    continue
                # energy at midpoint
                #midpoint_nrg = nrg[phase_name](*midpoint)
                # average energy of endpoints
                #average_nrg = (0.5 * (first_endpoint.ix['GM'] + \
                #    second_endpoint.ix['GM']))
                #if average_nrg >= midpoint_nrg:
                #    # not a true tieline, drop this simplex
                #    continue
            # if we get here, this is a miscibility gap region or a
            # two-phase region
            current_tielines.append(
                [[first_endpoint[columns.index(x_variable)], temp, \
                        first_endpoint[columns.index('Phase')]], \
                    [second_endpoint[columns.index(x_variable)], temp, \
                        second_endpoint[columns.index('Phase')]]]
                )
            tieline_norm = equ[:-1]
            tieline_normals.append(tieline_norm)

            # enumerate all normals but the one we just added
            for idx, normal in enumerate(tieline_normals[:-1]):
                continue
                dihedral = np.dot(normal, tieline_norm)
                if dihedral > (1.0 - 1e-11):
                    # nearly coplanar: we are near a 3-phase boundary
                    # red for an invariant
                    tie_lines.append(current_tielines[-1])
                    tie_lines.append(current_tielines[idx])
                    # prevent double counting
                    #del current_tielines[idx]
                    #del current_tielines[-1]
                    tie_line_colors.append([1, 0, 0, 1])
                    tie_line_widths.append(2)
                    tie_line_colors.append([1, 0, 0, 1])
                    tie_line_widths.append(2)

            for line in current_tielines:
                # Green for a tie line
                tie_lines.append(line)
                tie_line_colors.append([0, 1, 0, 1])
                tie_line_widths.append(0.5)

    tie_lines = np.asarray(tie_lines)

    if ax is None:
        ax = plt.gca()
    ax = _binplot_setup(ax, phases, tie_lines, tie_line_colors, tie_line_widths)
    plot_title = '-'.join([x.title() for x in sorted(comps) if x != 'VA'])
    ax.set_title(plot_title, fontsize=20)
    ax.set_xlim([-0.01, 1.01])
    ax.set_ylim([low_temp, high_temp])
    ax.set_xlabel(x_variable, labelpad=15, fontsize=20)
    ax.set_ylabel("Temperature (K)", fontsize=20)
    return ax
예제 #2
0
def binplot(dbf,
            comps,
            phases,
            x_variable,
            low_temp,
            high_temp,
            steps=None,
            ax=None,
            **kwargs):
    """
    Calculate the binary isobaric phase diagram for the given temperature
    range.

    Parameters
    ----------
    dbf : Database
        Thermodynamic database containing the relevant parameters.
    comps : list
        Names of components to consider in the calculation.
    phases : list
        Names of phases to consider in the calculation.
    x_variable : string
        Name of the x-axis variable to plot, e.g., 'X(FE)'
    low_temp : float
        Lower bound of temperature to calculate.
    high_temp : float
        Upper bound of temperature to calculate.
    steps : int, optional
        Number of temperature steps to take between `low_temp` and `high_temp`.
    ax : Matplotlib Axes object, optional
    pdens : int, optional
        Number of points to sample per sublattice, per degree of freedom.
    ast : ['numpy', 'numexpr'], optional
        Specify how we should construct the callable for the energy.

    Returns
    -------
    A phase diagram as a figure.

    Examples
    --------
    None yet.
    """
    assert high_temp > low_temp
    tie_lines = []
    tie_line_colors = []
    tie_line_widths = []
    tsteps = steps or int(
        (high_temp - low_temp) / 10)  # Take 10 K steps by def.
    temps = np.array(np.linspace(low_temp, high_temp, num=tsteps),
                     dtype=np.float64)

    # Convert all phase names to uppercase
    phases = [phase.upper() for phase in phases]

    try:
        pdens = kwargs.pop('pdens')
    except KeyError:
        pdens = 1000  # points per d.o.f

    # Calculate energy surface at each temperature
    full_df = energy_surf(dbf, comps, phases, T=temps, pdens=pdens, **kwargs)
    # Select only the P, T, etc., of interest
    full_df = full_df.groupby('T', sort=False)
    for temp, hull_frame in full_df:
        # Calculate the convex hull for the desired points
        hull_points = hull_frame[[x_variable, 'GM']].values
        hull = None
        try:
            hull = scipy.spatial.ConvexHull(hull_points, qhull_options='QJ')
            del hull_points
        except RuntimeError:
            print('temperature: ' + str(temp))
            raise
        # keep track of tie line orientations
        # this is for invariant reaction detection
        tieline_normals = []
        current_tielines = []

        # this was factored out of the loop based on profiling
        coordinates = hull_frame.iloc[np.asarray(
            hull.simplices).ravel()].values
        # Reshape coordinates into rank 3 ndarray of simplex coordinates
        # Each point is ordered as: Energy, Phase Name, Coordinates
        coordinates.shape = (len(hull.simplices), len(hull.simplices[0]),
                             len(coordinates[0]))
        columns = list(hull_frame.columns)

        for coords, equ in \
            zip(coordinates, hull.equations):
            if equ[-2] > -1e-6:
                # simplex oriented 'upwards' in energy direction
                # must not be part of the energy surface
                continue
            #distances = scipy.spatial.distance.pdist(coords)
            #simplex_edges = \
            #    np.asarray(list(itertools.combinations(simplex, 2)))
            #phase_edge_list = hull_frame['Phase'].values[simplex_edges]
            #edge_phases_match = \
            ##    np.array(phase_edge_list[:, 0] == phase_edge_list[:, 1])
            #new_lines = simplex_edges
            # Check if this is a two phase region
            first_endpoint = coords[0]
            second_endpoint = coords[1]
            phases_match = first_endpoint[columns.index('Phase')] == \
                second_endpoint[columns.index('Phase')]

            if phases_match:
                # Is this a miscibility gap region?
                # Check that the average of the tieline energy is less than
                # the energy calculated at the midpoint
                # If not, this is a single-phase region and should be dropped
                #phase_name = first_endpoint[columns.index('Phase')]
                #input_cols = [x for x in hull_frame.columns if phase_name in x]
                #midpoint = np.mean([first_endpoint.ix[input_cols].values, \
                #    second_endpoint.ix[input_cols].values], axis=0)
                # chebyshev distance returns maximum difference between any
                # dimension
                #pxd = scipy.spatial.distance.chebyshev(
                #    first_endpoint.ix[input_cols].values, \
                #    second_endpoint.ix[input_cols].values)
                pxd = scipy.spatial.distance.chebyshev(
                    first_endpoint[columns.index(x_variable)], \
                    second_endpoint[columns.index(x_variable)])
                if pxd < 0.03:
                    continue
                # energy at midpoint
                #midpoint_nrg = nrg[phase_name](*midpoint)
                # average energy of endpoints
                #average_nrg = (0.5 * (first_endpoint.ix['GM'] + \
                #    second_endpoint.ix['GM']))
                #if average_nrg >= midpoint_nrg:
                #    # not a true tieline, drop this simplex
                #    continue
            # if we get here, this is a miscibility gap region or a
            # two-phase region
            current_tielines.append(
                [[first_endpoint[columns.index(x_variable)], temp, \
                        first_endpoint[columns.index('Phase')]], \
                    [second_endpoint[columns.index(x_variable)], temp, \
                        second_endpoint[columns.index('Phase')]]]
                )
            tieline_norm = equ[:-1]
            tieline_normals.append(tieline_norm)

            # enumerate all normals but the one we just added
            for idx, normal in enumerate(tieline_normals[:-1]):
                continue
                dihedral = np.dot(normal, tieline_norm)
                if dihedral > (1.0 - 1e-11):
                    # nearly coplanar: we are near a 3-phase boundary
                    # red for an invariant
                    tie_lines.append(current_tielines[-1])
                    tie_lines.append(current_tielines[idx])
                    # prevent double counting
                    #del current_tielines[idx]
                    #del current_tielines[-1]
                    tie_line_colors.append([1, 0, 0, 1])
                    tie_line_widths.append(2)
                    tie_line_colors.append([1, 0, 0, 1])
                    tie_line_widths.append(2)

            for line in current_tielines:
                # Green for a tie line
                tie_lines.append(line)
                tie_line_colors.append([0, 1, 0, 1])
                tie_line_widths.append(0.5)

    tie_lines = np.asarray(tie_lines)

    if ax is None:
        ax = plt.gca()
    ax = _binplot_setup(ax, phases, tie_lines, tie_line_colors,
                        tie_line_widths)
    plot_title = '-'.join([x.title() for x in sorted(comps) if x != 'VA'])
    ax.set_title(plot_title, fontsize=20)
    ax.set_xlim([-0.01, 1.01])
    ax.set_ylim([low_temp, high_temp])
    ax.set_xlabel(x_variable, labelpad=15, fontsize=20)
    ax.set_ylabel("Temperature (K)", fontsize=20)
    return ax
예제 #3
0
def test_unknown_model_attribute():
    "Sampling an unknown model attribute raises exception."
    energy_surf(DBF, ['AL', 'CR', 'NI'], ['L12_FCC'],
                T=1400.0, output='_fail_')
예제 #4
0
def test_surface():
    "Bare minimum: energy_surf produces a result."
    energy_surf(DBF, ['AL', 'CR', 'NI'], ['L12_FCC'],
                T=1273., mode='numpy')
예제 #5
0
def test_surface():
    energy_surf(DBF, ['AL', 'CR', 'NI'], ['L12_FCC'],
                T=1273, pdens=10, mode='numpy')
예제 #6
0
def test_unknown_model_attribute():
    "Sampling an unknown model attribute raises exception."
    energy_surf(DBF, ['AL', 'CR', 'NI'], ['L12_FCC'],
                T=1400.0,
                output='_fail_')
예제 #7
0
def test_surface():
    "Bare minimum: energy_surf produces a result."
    energy_surf(DBF, ['AL', 'CR', 'NI'], ['L12_FCC'], T=1273., mode='numpy')