Skip to content

Commit

Permalink
cleaning up STEM widgets
Browse files Browse the repository at this point in the history
  • Loading branch information
gvarnavi committed Jul 29, 2024
1 parent 2100aa0 commit b11ed24
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 225 deletions.
35 changes: 7 additions & 28 deletions 09_STEM.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,19 @@ The incident probe wavefunction is easiest to define in Fourier space
```{math}
:label: eq:fourier_probe
\begin{align}
\Psi_0(\bm{k}) = A(\bm{k}) \mathrm{e}^{-i\chi(\bm{k})},
\end{align}
\Psi_0(\bm{k}) = A(\bm{k}) \mathrm{e}^{-i\chi(\bm{k})},
```

where the amplitude, {math}`A(\bm{k})`, is the aperture function and the phase, {math}`\chi(\bm{k})`, is the aberration function introduced in section [%s](). The aperture is usually a disk with a radius given by the cutoff semiangle

```{math}
\begin{align}
\begin{aligned}
A(\bm{k}) =
\begin{cases}
1, & \lambda k = \alpha < \alpha_{cutoff} \\
0, & \text{otherwise} .
\end{cases}
\end{align}
\end{aligned}
```

While the above definition is typical for most STEM experiments, the initial wavefunction can be defined using any other complex function and other initial conditions may be required for simulating more exotic experiments such phase plate STEM.
Expand All @@ -41,37 +39,20 @@ The probe is transferred to the specimen using an inverse Fourier transform and,
```{math}
:label: eq:realspace_probe
\begin{align}
\psi_0(\bm{r}, \bm{r}_p) = \mathcal{F}_{\bm{q}}^{-1} \left[\mathrm{e^{-i\chi(\bm{k}) - 2 \pi i \bm{k}\cdot \bm{r}_p }} A(\bm{k}) \right] ,
\end{align}
```

where, {math}`\bm{r}_p`, is a specified probe position in relative to the atomic coordinates.

In [](#fig_probe) , we present an interactive visualization for exploring the relationship between size and shape of the incident probe and some parameters of the aperture and aberration functions.



```{figure} #app:probe_size
:name: fig_probe
:placeholder: ./static/probe_size.png
**Left** The Fourier space initial wavefunction in equation [](#eq:fourier_probe). **Middle** The real space probe at the specimen in equation [](#eq:realspace_probe). **Right** The real space intensity. Start by considering the effect of changing just the aperture and energy. In real space, the probe forms the diffraction-limited Airy disk pattern; increasing the aperture or energy decreases the radius of the pattern. Consider the differences between the STEM and SEM probes. Next, add defocus. In Fourier space, the phase starts oscillating radially with a linearly decreasing period, the resulting probe grows and its radial intensity profile may have multiple peaks and valleys. Now try to decrease the aperture to include only the inner slowly varying part of the phase; the result is a smaller, more well-behaved probe. Add some spherical aberration and try to compensate by adding some defocus to flatten the phase inside the aperture resulting in a better probe (uncorrected STEM at Scherzer). Lastly, make a large probe and observe the diffraction fringes from self-interaction(boundary artifacts). This is the issue that should be avoided by increasing the size of the unit cell, as described in section.
```


Because STEM simulations can require long computation times, we often try to reduce the size of the simulation cell as much as possible. However, we must be careful to use a simulation cell large enough to hold the STEM probe. In particular, we need to consider the size of the probe throughout the full simulation cell volume. For an empty cell, the probe will have a maximum size at either the entrance or exit surface, depending on the probe defocus.

Use the drop down menu in [](#fig_probe), to see what the probe looks like when it has wrap-around artifacts. Try modifying the aberrations and notice as the probe gets bigger the error gets worse. Note that this example only considers an empty cell volume. Atoms inside the simulation volume will scatter the electron beam, with heavier elements scattering electrons to higher angles. We recommend positioning individual STEM probes at the positions where the highest scattering is expected (for example directly on or adjacent to the thickest atomic columns), and carefully checking the dimensions of the probe at the exit surface. This is especially important for PRISM simulations, as the cropping box around the STEM probe can be significantly smaller than the full cell dimensions for high PRISM interpolation factors {cite:t}`ophus_fast_2017`.

(imaging)=
### Imaging

The initial wavefunction is passed through the specimen potential according to the multislice algorithm. The exit wavefunction, {math}`\psi_t(\bm{r}, \bm{r}_p)`, is then diffracted to the detector plane using another Fourier transform

```{math}
\begin{align*}
\Psi_t(\bm{k}, \bm{r}_p) = \mathcal{F}_{\bm{r}} [\psi_t(\bm{r}, \bm{r}_p)] .
\end{align*}
\Psi_t(\bm{k}, \bm{r}_p) = \mathcal{F}_{\bm{r}} [\psi_t(\bm{r}, \bm{r}_p)] .
```

The square modulus, {math}`|\Psi_t(\bm{k}, \bm{r}_p)|^2` is the diffraction pattern for the probe position {math}`\bm{r}_p`.
Expand All @@ -81,22 +62,20 @@ Every STEM mode requires calculating a diffraction pattern for each shifted init
In bright-field (BF) and annular-dark-field (ADF) STEM the detector integrates the CBED pattern on a region in the diffraction plane. This is equivalent to multiplying with a detector function

```{math}
\begin{align*}
g(\bm{r}_p) = \int\int D(\bm{k})|\Psi_t(\bm{k}, \bm{r}_p)|^2 ~d^2 \bm{k}
\end{align*}
g(\bm{r}_p) = \int\int D(\bm{k})|\Psi_t(\bm{k}, \bm{r}_p)|^2 ~d^2 \bm{k}
```


where

```{math}
\begin{align*}
\begin{aligned}
D(\bm{k}) =
\begin{cases}
1, & \alpha_a < \lambda k < \alpha_b \\
0, & \text{otherwise}
\end{cases}
\end{align*}
\end{aligned}
```


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,28 @@
"source": [
"---\n",
"title: Interactive plot for contrast in TEM\n",
"authors: [Stephanie Ribet]\n",
"authors: [Stephanie Ribet, Georgios Varnavides]\n",
"date: 2023/08/14\n",
"---\n",
"\n",
"\n",
"Updated by Georgios Varnavides, 2024/07/15\n",
"\n",
"[cif file](https://drive.google.com/file/d/161gO_iC7cHCBtCUlAAPrg9Q3SOPWARK0/view?usp=sharing)\n"
]
},
{
"cell_type": "code",
"execution_count": 10,
"execution_count": 1,
"id": "190f4206-c55e-4de4-af84-ccf696b59fcf",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[########################################] | 100% Completed | 404.91 ms\n"
"[########################################] | 100% Completed | 506.21 ms\n"
]
},
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "53394bc217004a3e867bb409bf557125",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Canvas(footer_visible=False, header_visible=False, layout=Layout(height='255px', width='675px')…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#| label: app:tem_contrast\n",
"\n",
"%matplotlib widget\n",
"\n",
"import numpy as np\n",
Expand Down Expand Up @@ -377,15 +358,39 @@
" VBox([phase_shift,zernike_radius])\n",
" ]),\n",
" ],\n",
")\n",
"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "f75e937c-034c-45bc-944b-f5d1517a6410",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "dc09b49ee6a148b79aedc4e890c3434b",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Canvas(footer_visible=False, header_visible=False, layout=Layout(height='255px', width='675px')…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#| label: app:tem_contrast\n",
"display(widget);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f75e937c-034c-45bc-944b-f5d1517a6410",
"id": "767dcd0c-217d-49a6-a7da-20ec3dc9241f",
"metadata": {},
"outputs": [],
"source": []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@
"title: Interactive plot for probe size\n",
"authors: [Stephanie Ribet, Colin Ophus]\n",
"date: 2023/08/14\n",
"---\n",
"\n",
"Updated by Georgios Varnavides, 2024/07/15"
"---"
]
},
{
Expand All @@ -23,7 +21,7 @@
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "332dd9f076e44e71bb5fd83a28326407",
"model_id": "11111dc902f8499d9fae19a0aea1f3c7",
"version_major": 2,
"version_minor": 0
},
Expand All @@ -41,21 +39,27 @@
"%matplotlib widget\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from colorspacious import cspace_convert\n",
"from IPython.display import display\n",
"from ipywidgets import HBox, VBox, widgets, Dropdown, Label, Layout, IntSlider, FloatSlider,FloatLogSlider\n",
"import abtem\n",
"\n",
"# Copied from py4DSTEM directly\n",
"def Complex2RGB(complex_data, vmin=None, vmax = None, hue_start = 0, invert=False):\n",
"def Complex2RGB(complex_data, vmin=None, vmax=None, power=None, chroma_boost=1):\n",
" \"\"\"\n",
" complex_data (array): complex array to plot\n",
" vmin (float) : minimum absolute value \n",
" vmax (float) : maximum absolute value \n",
" hue_start (float) : rotational offset for colormap (degrees)\n",
" inverse (bool) : if True, uses light color scheme\n",
" vmin (float) : minimum absolute value\n",
" vmax (float) : maximum absolute value\n",
" power (float) : power to raise amplitude to\n",
" chroma_boost (float): boosts chroma for higher-contrast (~1-2.5)\n",
" \"\"\"\n",
" amp = np.abs(complex_data)\n",
" if np.isclose(np.max(amp),np.min(amp)):\n",
" phase = np.angle(complex_data)\n",
"\n",
" if power is not None:\n",
" amp = amp**power\n",
"\n",
" if np.isclose(np.max(amp), np.min(amp)):\n",
" if vmin is None:\n",
" vmin = 0\n",
" if vmax is None:\n",
Expand All @@ -75,15 +79,16 @@
"\n",
" amp = np.where(amp < vmin, vmin, amp)\n",
" amp = np.where(amp > vmax, vmax, amp)\n",
" amp = ((amp - vmin) / vmax).clip(1e-16, 1)\n",
"\n",
" phase = np.angle(complex_data) + np.deg2rad(hue_start)\n",
" amp /= np.max(amp)\n",
" rgb = np.zeros(phase.shape +(3,))\n",
" rgb[...,0] = 0.5*(np.sin(phase)+1)*amp\n",
" rgb[...,1] = 0.5*(np.sin(phase+np.pi/2)+1)*amp\n",
" rgb[...,2] = 0.5*(-np.sin(phase)+1)*amp\n",
" \n",
" return 1-rgb if invert else rgb\n",
" J = amp * 61.5 # Note we restrict luminance to the monotonic chroma cutoff\n",
" C = np.minimum(chroma_boost * 98 * J / 123, 110)\n",
" h = np.rad2deg(phase) + 180\n",
"\n",
" JCh = np.stack((J, C, h), axis=-1)\n",
" rgb = cspace_convert(JCh, \"JCh\", \"sRGB1\").clip(0, 1)\n",
"\n",
" return rgb\n",
"\n",
"\n",
"# widget figure generation\n",
Expand Down Expand Up @@ -165,7 +170,6 @@
" probe_R = Complex2RGB(probe_R_vals, vmax = 1, vmin = 0)\n",
" probe_R_int = np.abs(probe_R_vals)**2\n",
" probe_R_int /= np.max(probe_R_int)\n",
" probe_R_int **= 0.25\n",
" \n",
" im0.set_data(probe_Q)\n",
" im1.set_data(probe_R)\n",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,10 @@
"source": [
"---\n",
"title: Interactive STEM detector angles\n",
"authors: [Stephanie Ribet]\n",
"authors: [Stephanie Ribet, Georgios Varnavides]\n",
"date: 2023/08/14\n",
"---\n",
"\n",
"\n",
"Updated by Georgios Varnavides, 2024/07/15\n",
"\n",
"TODO: re run simulations with frozen phonons \n",
"\n",
"Data [here](https://drive.google.com/file/d/1O0sAsL0oX7n3VsejrLr9LNLZNAysWI-v/view?usp=sharing)"
]
},
Expand All @@ -24,25 +19,8 @@
"execution_count": 1,
"id": "8efdd632-53df-4a08-ab7a-ae959a8bfbd2",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "0fa7b6d1fd794520a0f1b11567f100c4",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Canvas(footer_visible=False, header_visible=False, layout=Layout(height='255px', width='675px')…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"outputs": [],
"source": [
"#| label: app:stem_detectors\n",
"\n",
"%matplotlib widget\n",
"\n",
"import numpy as np\n",
Expand Down Expand Up @@ -239,15 +217,39 @@
" dropdown\n",
" ]),\n",
" ],\n",
")\n",
"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "1a15d388-5640-4c28-ae40-4b2cfbacfee7",
"metadata": {},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "3b3876696b0e4a3b85723cbbe04c3210",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"VBox(children=(Canvas(footer_visible=False, header_visible=False, layout=Layout(height='255px', width='675px')…"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"#| label: app:stem_detectors\n",
"display(widget);"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1a15d388-5640-4c28-ae40-4b2cfbacfee7",
"id": "351a792d-3339-4312-832f-f98055674c5c",
"metadata": {},
"outputs": [],
"source": []
Expand All @@ -269,7 +271,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
"version": "3.11.9"
}
},
"nbformat": 4,
Expand Down
Loading

0 comments on commit b11ed24

Please sign in to comment.