def update_bimet_result(metals, shape, num_atoms, diameter, n_metal1, CE, ordering, EE=None, nanop=None, allow_insert=True, ensure_ce_min=True): """ Takes raw data and inserts the BimetallicResults datatable - will update CE, EE, and ordering of an entry if necessary Returns: - BimetallicResults entry after successfully updating """ res = get_bimet_result(metals=metals, shape=shape, num_atoms=num_atoms, n_metal1=n_metal1) if res: # if ensure_ce_min, do not overwrite a minimum CE structure # with new data if ensure_ce_min and res.CE < CE: return res.CE = CE res.ordering = ordering res.EE = EE elif not allow_insert: return else: metal1, metal2 = db_utils.sort_2metals(metals) res = tbl.BimetallicResults( metal1=metal1, metal2=metal2, shape=shape, num_atoms=num_atoms, diameter=diameter, n_metal1=n_metal1, n_metal2=num_atoms - n_metal1, CE=CE, ordering=ordering, EE=EE) if nanop: nanop.bimetallic_results.append(res) session.add(nanop) # commit changes session.add(res) db_utils.commit_changes(session, raise_exception=True) return res
def get_bimet_result(metals=None, shape=None, num_atoms=None, num_shells=None, n_metal1=None, only_bimet=False, lim=None, return_query=False, custom_filter=None, return_list=False): """ Returns BimetallicResults entry that matches criteria - if no criteria given, all data (up to <lim> amount) returned Kargs: - metals (str || iterable): two metal elements - shape (str): shape of NP - num_atoms (int): number of atoms in NP - num_shells (int): number of shells used to build NP from structure_gen module - n_metal1 (int): number of metal1 atoms in NP - lim (int): max number of entries returned (default: None = no limit) - return_query (bool): if True, return query and not results - return_list (bool): if True, function always returns list, else will only return list if (# results) != 1 Returns: - (BimetallicResults)(s) if match is found else [] """ if not num_atoms and num_shells: if shape: num_atoms = get_shell2num(shape, num_shells) # if shape not defined, assume icosahedron shell->atoms mapping else: num_atoms = get_shell2num('icosahedron', num_shells) if only_bimet: only_bimet = db.and_(tbl.BimetallicResults.n_metal1 != 0, tbl.BimetallicResults.n_metal2 != 0) custom_filter = only_bimet # get sorted metals metal1, metal2 = db_utils.sort_2metals(metals) return get_entry(tbl.BimetallicResults, metal1=metal1, metal2=metal2, shape=shape, num_atoms=num_atoms, n_metal1=n_metal1, lim=lim, return_query=return_query, custom_filter=custom_filter, return_list=return_list)
def get_bimet_log(metals=None, shape=None, date=None, lim=None, return_query=False): """ Returns BimetallicLog entry that matches criteria - if no criteria given, all data (up to <lim> amount) returned Kargs: - metals (str || iterable): two metal elements - shape (str): shape of NP - date (datetime.datetime): GA batch run completion time - lim (int): max number of entries returned (default: None = no limit) - return_query (bool): if True, return query and not results Returns: - (BimetallicResults)(s) if match is found else (None) """ # get sorted metals metal1, metal2 = db_utils.sort_2metals(metals) return get_entry(tbl.BimetallicLog, metal1=metal1, metal2=metal2, shape=shape, date=date, lim=lim, return_query=return_query)
def build_srf_plot(metals, shape, T=None): """ Creates a 3D surface plot from NP SQL database - plots Size vs. Shape vs. Excess Energy (EE) - can also use configurational entropy of mixing to plot Size vs. Shape vs. delG = EE - T * delS(mix) Args: - metals (string || iterable): string(s) containing two metal elements - shape (string): shape of the NP KArgs: T (float): if temperature is given, plot delG(mix) (i.e. include configurational entropy) (Default: None) Returns: - (plt.figure): figure of 3D surface plot """ metal1, metal2 = db_utils.sort_2metals(metals) # build pd.DataFrame of all results that match criteria runs = session.query(tbl.BimetallicResults.diameter, (tbl.BimetallicResults.n_metal2 / db.cast(tbl.BimetallicResults.num_atoms, db.Float)) .label('comps'), tbl.BimetallicResults.num_atoms, tbl.BimetallicResults.EE) \ .filter(db.and_(tbl.BimetallicResults.metal1 == metal1, tbl.BimetallicResults.metal2 == metal2, tbl.BimetallicResults.shape == shape)) \ .statement df = pd.read_sql(runs, session.bind) # three parameters to plot size = df.num_atoms.values comps = df.comps.values ees = df.EE.values if T is not None: # k_b T [eV] = (25.7 mEV at 298 K) kt = 25.7E-3 * (T / 298.) del_s = comps * np.ma.log(comps).filled(0) + \ (1 - comps) * np.ma.log(1 - comps).filled(0) del_s *= -kt ees -= del_s # plots surface as heat map with warmer colors for larger EEs colormap = plt.get_cmap('coolwarm') normalize = matplotlib.colors.Normalize(vmin=ees.min(), vmax=-ees.min()) fig = plt.figure() ax = fig.add_subplot(111, projection='3d') try: ax.plot_trisurf(comps, size, ees, cmap=colormap, norm=normalize, alpha=0.5) except RuntimeError: # if not enough data to create surface, make a scatter plot instead ax.scatter3D(comps, size, ees, cmap=colormap, norm=normalize) # plot mins at each step add_legend = True for s in np.unique(size): sizes = np.where(size == s)[0] mini = np.where(ees == ees[sizes].min())[0][0] color = 'orange' if ees[sizes].min() == ees.min() else 'pink' ax.scatter3D(comps[mini], s, ees[mini], edgecolor='k', color=color, s=50) ax.set_xlabel('\n\n$X_{%s}$' % metal2) ax.set_ylabel('\n\n$\\rm N_{atoms}$') if T is not None: ax.set_zlabel('\n\n$\\rm \\Delta$G (eV)') ax.set_title('%iK\n%s %s %s' % (T, metal1, metal2, shape.title())) else: ax.set_zlabel('\n\nEE (eV)') ax.set_title('%s %s %s' % (metal1, metal2, shape.title())) return fig, ax
def build_new_structs_plot(metal_opts, shape_opts, pct=False, cutoff_date=None): """ Uses BimetallicLog to create 2D line plot of new structures found vs. datetime Args: - metal_opts (list): list of metal options - shape_opts (list): list of shape options Kargs: - pct (bool): if True, y-axis = % new structures else, y-axis = number of new structures - cutoff_date (Datetime.Datetime): if given, will filter out runs older than <cutoff_date> Returns: - (plt.Figure): 2D line plot object """ if isinstance(metal_opts, str): metal_opts = [metal_opts] if isinstance(shape_opts, str): shape_opts = [shape_opts] # if cutoff_date, create custom_filter if isinstance(cutoff_date, datetime.datetime): custom_filter = tbl.BimetallicLog.date >= cutoff_date else: custom_filter = None colors = ['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728', '#9467bd', '#8c564b', '#e377c2', '#7f7f7f', '#bcbd22', '#17becf'] fig, ax = plt.subplots() i = 0 tot_lines = len(metal_opts) * len(shape_opts) for j, m in enumerate(metal_opts): metal1, metal2 = db_utils.sort_2metals(m) for point, shape in zip(['o', 'x', '^', 's'], shape_opts): # use abbreviated names for shapes lbl_shape = shape.upper()[:3] # pd.DataFrame of bimetallic log data df = build_df(tbl.BimetallicLog, metals=m, shape=shape, custom_filter=custom_filter) x = df.date.values label = '%s%s - %s' % (metal1, metal2, lbl_shape) y = df.new_min_structs.values if pct: y = y / df.tot_structs.values[0] ax.plot(x, y, '%s-' % point, label=label, color=colors[j]) i += 1 if pct: ax.yaxis.set_major_formatter( matplotlib.ticker.FuncFormatter('{0:.0%}'.format)) ax.set_ylim(0, 1) ax.set_ylabel('Percent Count') else: ax.set_ylabel('Total Count') ax.legend(ncol=len(metal_opts)) ax.set_title('New Minimum Structures Found') ax.set_xlabel('Batch Run Date') # (month/day) x axis tick labels ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%m/%d')) fig.tight_layout() return fig