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
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
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_')
def test_surface(): "Bare minimum: energy_surf produces a result." energy_surf(DBF, ['AL', 'CR', 'NI'], ['L12_FCC'], T=1273., mode='numpy')
def test_surface(): energy_surf(DBF, ['AL', 'CR', 'NI'], ['L12_FCC'], T=1273, pdens=10, mode='numpy')