def test_tet_coord(self): coord = [0.5, 0.5, 0.5] coord = tet_coord(coord) self.assertTrue(np.allclose(coord, [1.0, 0.57735027, 0.40824829]))
def present(self, df=None, new_result_ids=None, all_result_ids=None, filename=None, save_hull_distance=False, finalize=False): """ Generate plots of convex hulls for each of the runs Args: df (DataFrame): dataframe with formation energies, compositions, ids new_result_ids ([]): list of new result ids (i. e. indexes in the updated dataframe) all_result_ids ([]): list of all result ids associated with the current run filename (str): filename to output, if None, no file output is produced Returns: (pyplot): plotter instance """ df = df if df is not None else self.df new_result_ids = new_result_ids if new_result_ids is not None \ else self.new_result_ids all_result_ids = all_result_ids if all_result_ids is not None \ else self.all_result_ids # TODO: consolidate duplicated code here # Generate all entries comps = df.loc[all_result_ids]['Composition'].dropna() system_elements = [] for comp in comps: system_elements += list(Composition(comp).as_dict().keys()) elems = set(system_elements) if len(elems) > 4: warnings.warn( "Number of elements too high for phase diagram plotting") return None ind_to_include = [] for ind in df.index: if set(Composition( df.loc[ind]['Composition']).as_dict().keys()).issubset( elems): ind_to_include.append(ind) _df = df.loc[ind_to_include] # Create computed entry column _df['entry'] = [ ComputedEntry( Composition(row['Composition']), row['delta_e'] * Composition( row['Composition']).num_atoms, # un-normalize the energy entry_id=index) for index, row in _df.iterrows() ] # Partition ids into sets of prior to CAMD run, from CAMD but prior to # current iteration, and new ids ids_prior_to_camd = list(set(_df.index) - set(all_result_ids)) ids_prior_to_run = list(set(all_result_ids) - set(new_result_ids)) # Create phase diagram based on everything prior to current run entries = list(_df.loc[ids_prior_to_run + ids_prior_to_camd]['entry']) # Filter for nans by checking if it's a computed entry entries = [ entry for entry in entries if isinstance(entry, ComputedEntry) ] pg_elements = [Element(el) for el in sorted(elems)] pd = PhaseDiagram(entries, elements=pg_elements) plotkwargs = { "markerfacecolor": "white", "markersize": 7, "linewidth": 2, } if finalize: plotkwargs.update({'linestyle': '--'}) else: plotkwargs.update({'linestyle': '-'}) plotter = PDPlotter(pd, **plotkwargs) getplotkwargs = {"label_stable": False} if finalize else {} plot = plotter.get_plot(**getplotkwargs) # Get valid results valid_results = [ new_result_id for new_result_id in new_result_ids if new_result_id in _df.index ] if finalize: # If finalize, we'll reset pd to all entries at this point # to measure stabilities wrt. the ultimate hull. pd = PhaseDiagram(_df['entry'].values, elements=pg_elements) plotter = PDPlotter( pd, **{ "markersize": 0, "linestyle": "-", "linewidth": 2 }) plot = plotter.get_plot(plt=plot) for entry in _df['entry'][valid_results]: decomp, e_hull = pd.get_decomp_and_e_above_hull( entry, allow_negative=True) if e_hull < self.hull_distance: color = 'g' marker = 'o' markeredgewidth = 1 else: color = 'r' marker = 'x' markeredgewidth = 1 # Get coords coords = [ entry.composition.get_atomic_fraction(el) for el in pd.elements ][1:] if pd.dim == 2: coords = coords + [pd.get_form_energy_per_atom(entry)] if pd.dim == 3: coords = triangular_coord(coords) elif pd.dim == 4: coords = tet_coord(coords) plot.plot(*coords, marker=marker, markeredgecolor=color, markerfacecolor="None", markersize=11, markeredgewidth=markeredgewidth) if filename is not None: plot.savefig(filename, dpi=70) plot.close() if filename is not None and save_hull_distance: if self.stabilities is None: print("ERROR: No stability information in analyzer.") return None with open(filename.split(".")[0] + '.json', 'w') as f: json.dump(self.stabilities, f)
def plot_hull(self, df, new_result_ids, filename=None, finalize=False): """ Generate plots of convex hulls for each of the runs Args: df (DataFrame): dataframe with formation energies and formulas new_result_ids ([]): list of new result ids (i. e. indexes in the updated dataframe) filename (str): filename to output, if None, no file output is produced finalize (bool): flag indicating whether to include all new results Returns: (pyplot): plotter instance """ # Generate all entries total_comp = Composition(df['Composition'].sum()) if len(total_comp) > 4: warnings.warn( "Number of elements too high for phase diagram plotting") return None filtered = filter_dataframe_by_composition(df, total_comp) filtered = filtered[['delta_e', 'Composition']] filtered = filtered.dropna() # Create computed entry column with un-normalized energies filtered["entry"] = [ ComputedEntry( Composition(row["Composition"]), row["delta_e"] * Composition(row["Composition"]).num_atoms, entry_id=index, ) for index, row in filtered.iterrows() ] ids_prior_to_run = list(set(filtered.index) - set(new_result_ids)) if not ids_prior_to_run: warnings.warn( "No prior data, prior phase diagram cannot be constructed") return None # Create phase diagram based on everything prior to current run entries = filtered.loc[ids_prior_to_run]["entry"].dropna() # Filter for nans by checking if it's a computed entry pg_elements = sorted(total_comp.keys()) pd = PhaseDiagram(entries, elements=pg_elements) plotkwargs = { "markerfacecolor": "white", "markersize": 7, "linewidth": 2, } if finalize: plotkwargs.update({"linestyle": "--"}) else: plotkwargs.update({"linestyle": "-"}) plotter = PDPlotter(pd, backend='matplotlib', **plotkwargs) getplotkwargs = {"label_stable": False} if finalize else {} plot = plotter.get_plot(**getplotkwargs) # Get valid results valid_results = [ new_result_id for new_result_id in new_result_ids if new_result_id in filtered.index ] if finalize: # If finalize, we'll reset pd to all entries at this point to # measure stabilities wrt. the ultimate hull. pd = PhaseDiagram(filtered["entry"].values, elements=pg_elements) plotter = PDPlotter(pd, backend="matplotlib", **{ "markersize": 0, "linestyle": "-", "linewidth": 2 }) plot = plotter.get_plot(plt=plot) for entry in filtered["entry"][valid_results]: decomp, e_hull = pd.get_decomp_and_e_above_hull( entry, allow_negative=True) if e_hull < self.hull_distance: color = "g" marker = "o" markeredgewidth = 1 else: color = "r" marker = "x" markeredgewidth = 1 # Get coords coords = [ entry.composition.get_atomic_fraction(el) for el in pd.elements ][1:] if pd.dim == 2: coords = coords + [pd.get_form_energy_per_atom(entry)] if pd.dim == 3: coords = triangular_coord(coords) elif pd.dim == 4: coords = tet_coord(coords) plot.plot(*coords, marker=marker, markeredgecolor=color, markerfacecolor="None", markersize=11, markeredgewidth=markeredgewidth) if filename is not None: plot.savefig(filename, dpi=70) plot.close()
def plot_quaternary_pd(pd): # different length but not at all qhull_data = pd.qhull_data qhull_data = np.delete(qhull_data, -1, axis=0) qhull_cord = np.vstack([tet_coord(qhull_data[i, 0:3]) for i in range(qhull_data.shape[0])]) facet_vertices = pd.facets qhull_stable = [qhull_cord[each] for each in facet_vertices] data = [] # plot dash segment lines between nodes line_list = [] for qhull in qhull_stable: polyhedron = qhull[:, 0:3] hull = ConvexHull(polyhedron) for simplex in hull.simplices: facet = polyhedron[simplex] lines = list(combinations(facet, 2)) lines = [np.array(l) for l in lines] for line in lines: duplicate_boolean = [ (line.round(3) == l.round(3)).all() for l in line_list] if not True in duplicate_boolean: line_list.append(line) for line in line_list: convex_lines = plotly_lines(line, 'dash') data.append(convex_lines) ####################################################### # If need to draw certain faces in solid lines, like the faces on the front, # ddd coordinations of the facet here. # note to comment them out at the first run. ####################################################### facet_front = [[[0.5, 0.8660254, 0.], [0.83333333, 0.28867513, 0.], [0., 0., 0]], [[0.83333333, 0.28867513, 0.], [1., 0., 0], [0., 0., 0]]] for facet in facet_front: lines_front = list(combinations(facet, 2)) lines_front = [np.array(l) for l in lines_front] for line in lines_front: convex_lines = plotly_lines(line, 'solid') data.append(convex_lines) ####################################################### facet_element = tieline_phases(pd, 'Li') qhull_element = np.array([qhull_cord[each] for each in facet_element]) for index, qhull in enumerate(qhull_element): polyhedron = qhull[:, 0:3] color = palette.Plotly[index % 10] convex_polyhedon = plotly_polyhedron(polyhedron, color=color) data.append(convex_polyhedon) # add node points nodes_index = np.array(facet_vertices).flatten() nodes_index = np.unique(nodes_index) # nodes_name = np.array([entries[each].name for each in nodes_index]) nodes_array = np.vstack([qhull_cord[each] for each in nodes_index]) scatter_vertices = dict( mode="markers", name='nodes', type="scatter3d", x=nodes_array[:, 0], y=nodes_array[:, 1], z=nodes_array[:, 2], # marker = dict(size=5, color="rgb(106, 90, 205)") marker=dict(size=6, color="rgb(50,50,50)") ) data.append(scatter_vertices) layout = dict( # title = '<b>Quaternary Phase Diagram</b>', title_x=0.5, scene=dict(xaxis=dict(zeroline=False, showticklabels=False, showgrid=False, showline=False, title={'text': ''}, visible=False), yaxis=dict(zeroline=False, showticklabels=False, showgrid=False, showline=False, title={'text': ''}, visible=False), # visible=False, showaxeslabels=False, zaxis=dict(zeroline=False, showticklabels=False, showgrid=False, showline=False, title={'text': ''}, visible=False), camera=dict(eye=dict(x=2.0, y=1.0, z=0.5)), ), showlegend=False, # width = 1000, # height = 1000, autosize=True # annotations = make_annotations(nodes_array, v_label), ) fig = dict(data=data, layout=layout) return fig