Beispiel #1
0
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
Beispiel #2
0
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)
Beispiel #3
0
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)
Beispiel #4
0
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
Beispiel #5
0
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