janosh/pymatviz

Changing the edge color of atoms

Asif-Iqbal-Bhatti opened this issue · 5 comments

Using from pymatviz import plot_structure_2d, we can plot atoms, but is there a way to change the edge or face color of the atoms using the attributes of plot_structure_2d()? In the source code, Wedge() is already defined with edge color. Further, there is no option for xlabel or ylabel when I set them?

janosh commented

Bear in mind that the edge drawing functionality in plot_structure_2d() is still experimental.

if show_bonds:
warnings.warn(
"Warning: the show_bonds feature of plot_structure_2d() is experimental. "
"Issues and PRs with improvements welcome.",
category=ExperimentalWarning,
)

Even though it's not well tested, I think the functionality you're asking about is already there. Have a look at the doc string. Specifically colors, show_bonds and bond_kwargs.

colors (dict[str, str | list[float]], optional): Map from element symbols to
colors, either a named color (str) or rgb(a) values like (0.2, 0.3, 0.6).
Defaults to JMol colors.
scale (float, optional): Scaling of the plotted atoms and lines. Defaults to 1.
show_unit_cell (bool, optional): Whether to draw unit cell. Defaults to True.
show_bonds (bool | NearNeighbors, optional): Whether to draw bonds. If True, use
pymatgen.analysis.local_env.CrystalNN to infer the structure's connectivity.
If False, don't draw bonds. If a subclass of
pymatgen.analysis.local_env.NearNeighbors, use that to determine
connectivity. Options include VoronoiNN, MinimumDistanceNN, OpenBabelNN,
CovalentBondNN, dtc. Defaults to True.
site_labels (bool | dict[str, str | float] | list[str | float]): How to annotate
lattice sites. If True, labels are element symbols. If a dict, should map
element symbols to labels. If a list, must be same length as the number of
sites in the crystal. Defaults to True.
label_kwargs (dict, optional): Keyword arguments for matplotlib.text.Text like
{"fontsize": 14}. Defaults to None.
bond_kwargs (dict, optional): Keyword arguments for the matplotlib.path.Path
class used to draw chemical bonds. Allowed are edgecolor, facecolor, color,
linewidth, linestyle, antialiased, hatch, fill, capstyle, joinstyle.
Defaults to None.

Here's a code snippet showing how to use them taken from _generate_assets.py:

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matminer.datasets import load_dataset

df_phonons = load_dataset("matbench_phonons")

df_phonons[["spg_symbol", "spg_num"]] = [
    struct.get_space_group_info() for struct in tqdm(df_phonons.structure)
]

n_rows, n_cols = 3, 4
fig, axs = plt.subplots(n_rows, n_cols, figsize=(3 * n_cols, 3 * n_rows))
title = f"{len(axs.flat)} Matbench Phonons Structures"
fig.suptitle(title, fontweight="bold", fontsize=20)

for row, ax in zip(df_phonons.itertuples(), axs.flat):
    idx, struct, *_, spg_num = row
    plot_structure_2d(
        struct,
        ax=ax,
        show_bonds=True,
        bond_kwargs=dict(facecolor="gray", linewidth=2, linestyle="dotted", alpha=0.5),
    )
    sub_title = f"{idx + 1}. {struct.formula} ({spg_num})"
    ax.set_title(sub_title, fontweight="bold")

matbench-phonons-structures-2d

I'm not sure what you mean by x/y-label on a structure. Does the subplot title above each structure in the figure above serve your purpose?

Thank you for reply. I will look into that.

As for set_xlabel when I try:
ax[0].axhline(y=a/2, color='black', linestyle='--', linewidth=0.2)
ax[0].axvline(x=b/2, color='black', linestyle='--', linewidth=0.2)
ax[0].set_xlabel("x-axis [$\mathrm{\AA}$]")
ax[0].set_ylabel("y-axis [$\mathrm{\AA}$]")

It does not show on the plot. The plot is empty although I can see axhline.

janosh commented

That's because of

ax.axis("off")

We can add a kwarg

axis: Literal[
  "on", "off", "equal", "scaled", "tight", "auto", "image", "square"
] = "off",

to disable turning off the plot's axes. Then you'd get sth like this. Is that what you're after?

matbench-phonons-structures-2d

Yes that is what I want. The reason for this is that when looking at the crystal along the [111] (bcc screw dislocations), the distortion of the surface atoms should be clearly visible on a 2D graph. We can measure how much there is deviation from the unrelax position after doping with alloy elements. In this way we can highlight the atoms and change the radius to see distortion. SO I have to add this
axis: Literal[
"on", "off", "equal", "scaled", "tight", "auto", "image", "square"
] = "off",

plot_structure_2d(struct, ax[0], rotation='0x,0y,0z',
atomic_radii={'H':0.05, 'Fe':0.2},
colors={'H':'k','Fe':'b'},
show_unit_cell=True, site_labels=False,
label_kwargs={"fontsize": 4,},
)

Again thank you for this. I must say this function is very useful if our workflow involves automation.

janosh commented

Thanks for giving some context.

If you're looking to visualize structure relaxations, this could also be relevant for you: materialsproject/crystaltoolkit#323

CrystalToolkit has the advantage of rendering structures in 3d. For automation purposes, you can take any browser tool like Puppeteer to take snapshots. I'm not sure about this but I think CrystalToolkit might even have snapshot functionality built in.