{ "cells": [ { "cell_type": "markdown", "id": "34123934", "metadata": {}, "source": [ "# Vacancy Trajectory Analysis\n", "\n", "You can download source files to follow this tutorial from this [link](https://drive.google.com/file/d/1OV7nbabecWXPq6DxNCrkphOIG-InZ7my/view?usp=drivesdk) (552 MB).\n", "\n", "---\n", "Analyzing vacancy hopping in `VacHopPy` begins with the `Calculator()` factory function and the `Site` class.\n", "\n", "---\n", "## The `Calculator()` Factory Function\n", "\n", "The `Calculator()` function is the main entry point for `VacHopPy`'s analysis pipeline. It gathers all necessary inputs, performs initial setup, and returns a fully configured `CalculatorEnsemble` object, ready for analysis.\n", "\n", "To get started, you call this function as follows:\n", "\n", "```bash\n", "calc_ensemble = Calculator(path_traj, site, t_interval)\n", "```\n", "### Key Arguments\n", "\n", "* **`path_traj` (*str*)** \n", "\n", " Path to the trajectory data. This can be either a single HDF5 trajectory file or a directory containing a bundle of HDF5 files. `VacHopPy` will automatically discover and process all valid files in the specified directory.\n", "\n", "* **`site` (*Site*)**\n", "\n", " An instance of the `Site` class.\n", "\n", "* **`t_interval` (*float, Optional*)**\n", "\n", " The time interval in **picoseconds (ps)** used for averaging atomic positions to determine site occupation. To distinguish true hopping events from thermal vibrations, `VacHopPy` averages the atomic coordinates and forces over each `t_interval`. This averaged quantities are then used to assign an atom to a specific lattice site. Each interval corresponds to a single analysis step. If this argument is not provided, `Calculator()` will automatically search for an optimal `t_interval` for the analysis.\n", "\n", "### Return Value\n", "\n", "Calling the `Calculator()` function returns an instance of the `CalculatorEnsemble` class, which holds all the processed data and methods required for subsequent analysis steps.\n", "\n", "---\n", "## The `Site` Class\n", "\n", "The `Site` class is used to define the structural backbone of your material. It identifies all **lattice sites** from a vacancy-free reference structure and determines the possible **hopping paths** between them.\n", "\n", "You can create a `Site` object like this:\n", "\n", "```bash\n", "site = Site(path_structure, symbol)\n", "```\n", "\n", "### Key Arguments\n", "\n", "* **`path_structure` (*str*)**\n", "\n", " Path to a structure file of the perfect, vacancy-free material. Any format supported by the **Atomic Simulation Environment (ASE)** is compatible.\n", "\n", "* **`symbol` (*str*)**\n", "\n", " The chemical symbol of the diffusing species.\n", "\n", "----\n", "\n", "## Usage Example\n", "\n", "Navigate into the `Example6/` directory you downloaded. In this directory, you will find two files:\n", "\n", "* **`TRAJ_O.h5`**\n", "\n", " This is the HDF5 file containing the MD trajectory data. The example system is **rutile TiO₂** containing **two oxygen vacancies**, simulated at **2100 K**.\n", "\n", "* **`POSCAR_TiO2`**\n", "\n", " This file contains the crystal structure of the perfect, **vacancy-free** rutile rutile TiO₂ supercell.\n" ] }, { "cell_type": "code", "execution_count": 1, "id": "0e5533cd", "metadata": {}, "outputs": [], "source": [ "import os\n", "import numpy as np\n", "from vachoppy.utils import show_traj\n", "from vachoppy.core import Site, Calculator\n", "\n", "path_traj, path_structure = 'TRAJ_O.h5', 'POSCAR_TiO2'\n", "\n", "if not os.path.exists(path_traj): print(f\"{path_traj} not found.\")\n", "if not os.path.exists(path_structure): print(f\"{path_structure} not found.\")" ] }, { "cell_type": "markdown", "id": "09bbb32a", "metadata": {}, "source": [ "---\n", "### 1. How to inspect the HDF5 file\n", "\n", "You can inspect the contents of the HDF5 trajectory file using the `show_traj` method:" ] }, { "cell_type": "code", "execution_count": 2, "id": "2e4a8f69", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "==================================================\n", " Trajectory File: TRAJ_O.h5\n", "==================================================\n", "\n", "[Simulation Parameters]\n", " - Atomic Symbol: O\n", " - Number of Frames: 300000\n", " - Temperature: 2100.0 K\n", " - Time Step: 2.0 fs\n", "\n", "[Composition]\n", " - Counts: O: 46, Ti: 24\n", " - Total Atoms: 70\n", "\n", "[Lattice Vectors (Ang)]\n", " [ 9.29133, 0.00000, 0.00000]\n", " [ 0.00000, 9.29133, 0.00000]\n", " [ 0.00000, 0.00000, 8.90126]\n", "\n", "[Stored Datasets]\n", " - positions: Shape = (300000, 46, 3)\n", " - forces: Shape = (300000, 46, 3)\n", "==================================================\n" ] } ], "source": [ "show_traj(path_traj)" ] }, { "cell_type": "markdown", "id": "ebf6e7e6", "metadata": {}, "source": [ "From the output, the MD run was performed at 2100 K for 600 ps (300,000 iterations × 2.0 fs). The composition shows the system contains **two oxygen vacancies**." ] }, { "cell_type": "markdown", "id": "b1e6a22a", "metadata": {}, "source": [ "---\n", "### 2. Creating a `Site` Instance" ] }, { "cell_type": "code", "execution_count": 3, "id": "097068c5", "metadata": {}, "outputs": [], "source": [ "site = Site(path_structure, 'O')" ] }, { "cell_type": "markdown", "id": "3b36bb90", "metadata": {}, "source": [ "You can inspect the generated lattice information by calling the `.summary()` method.\n", "\n", "`VacHopPy` automatically identifies all possible hopping paths by employing the **Voronoi tessellation** technique. The maximum distance for this search can be adjusted using the `rmax` parameter, which defaults to **3.25 Å**." ] }, { "cell_type": "code", "execution_count": 5, "id": "4b0a5562", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\n", "====================================================================================================\n", " Structure File: POSCAR_TiO2\n", "====================================================================================================\n", "[Structure Information]\n", " - Structure Composition : Ti24 O48\n", " - Lattice Vectors (Ang) :\n", " [ 9.29133, 0.00000, 0.00000]\n", " [ 0.00000, 9.29133, 0.00000]\n", " [ 0.00000, 0.00000, 8.90126]\n", "\n", "[Hopping Path Information]\n", " - Diffusing Symbol : O\n", " - Inequivalent Sites : 1 found\n", " - Inequivalent Paths : 3 found (with Rmax = 3.25 Å)\n", "\n", "Name Init Site Final Site a (Å) z Initial Coord (Frac) Final Coord (Frac)\n", "------ ----------- ------------ ------- --- ------------------------ -------------------------\n", "A1 site1 site1 2.5625 1 [0.0975, 0.4025, 0.1667] [-0.0975, 0.5975, 0.1667]\n", "A2 site1 site1 2.8031 8 [0.0975, 0.4025, 0.1667] [0.3475, 0.3475, 0.0000]\n", "A3 site1 site1 2.9671 2 [0.0975, 0.4025, 0.1667] [0.0975, 0.4025, -0.1667]\n", "====================================================================================================\n", "\n" ] } ], "source": [ "site.summary()" ] }, { "cell_type": "markdown", "id": "6cbfafb0", "metadata": {}, "source": [ "---\n", "### 3. Creating the `CalculatorEnsemble` Instance\n", "\n", "While you can create a `CalculatorEnsemble` instance by calling its constructor directly, the recommended approach is to use the `Calculator()` factory function. This helper function offers a significant advantage: it can automatically search for an optimal `t_interval` if one is not provided, streamlining your analysis setup" ] }, { "cell_type": "code", "execution_count": 6, "id": "e9ac7ca9", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "====================================================================\n", " Automatic t_interval Estimation\n", "====================================================================\n", " Estimating from TRAJ_O.h5\n", " -> t_interval : 0.076 ps\n", "====================================================================\n", " Adjusting t_interval to the nearest multiple of dt\n", "====================================================================\n", " - dt : 0.0020 ps\n", " - Original t_interval : 0.0764 ps\n", " - Adjusted t_interval : 0.0760 ps (38 frames)\n", "====================================================================\n" ] } ], "source": [ "calc_ensemble = Calculator(path_traj, site)" ] }, { "cell_type": "markdown", "id": "021ee656", "metadata": {}, "source": [ "Now that the `calc_ensemble` object is prepared, you can kick off the main analysis by calling the `.calculate()` method.\n", "\n", "This powerful method handles the entire analysis pipeline for you, which automatically includes **identifying the trajectory of each vacancy** and **calculating hopping parameters**." ] }, { "cell_type": "code", "execution_count": 7, "id": "99f761f6", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "73ade4a805e74b958e8fb675938535fc", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Analyze Trajectory: 0%| | 0/1 [00:00\n", "\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "id": "e592f67a", "metadata": {}, "source": [ "---\n", "#### - Usage of `animate_occupation()`\n", "\n", "You can customize the output GIF file using `step_init`, `step_final`, `fps`, `dpi`, and etc." ] }, { "cell_type": "code", "execution_count": 48, "id": "c0625701", "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "ca79b1549fb442adaade481433642735", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Make Animation: 0%| | 0/200 [00:00\n", "\n", "\"Animation\"\n", "\n", "" ] }, { "cell_type": "markdown", "id": "16cafe7f", "metadata": {}, "source": [ "The `.animate_occupation()` method visualizes the dynamics of site occupancy for both atoms and vacancies using a color-coded scheme.\n", "\n", "Each arrow in the animation traces the movement of an atom of the corresponding color. To indicate the direction and recency of motion, these arrows gradually fade over time. The fading rate can be controlled with the `update_alpha` argument.\n", "\n", "##### Color and Symbol Legend\n", "\n", "* **Yellow Circles**: Represent vacancies.\n", "* **Other Colored Circles**: Represent the atoms.\n", "* **Orange Squares**: Represent transient vacancies.\n", "\n", "A transient vacancy is a temporary vacancy that was not present in the initial system configuration. It occurs when two or more atoms are assigned to the same lattice site, a phenomenon often associated with the **kick-out mechanism**.\n", "\n", "For instance, the animation below illustrates a kick-out event observed in this system, where a transient vacancy (the orange square) can be seen appearing at step 5516." ] }, { "cell_type": "markdown", "id": "acb394ed", "metadata": {}, "source": [ "
\n", "\n", "\"Kick-out\"\n", "\n", "
" ] }, { "cell_type": "markdown", "id": "7f778d91", "metadata": {}, "source": [ "The `.transient_vacancy` attribute is a dictionary mapping step numbers to a list of site indices where transient vacancies exist. For instance, `calc.transient_vacancy[5516]` returns the index of sites of transient vacancies at that step. This data is particularly useful for identifying diffusion mechanisms other than standard vacancy-mediated hopping." ] }, { "cell_type": "markdown", "id": "50296d6f", "metadata": {}, "source": [ "---\n", "#### - Usage of `animate_vacancy_trajectory()`\n", "\n", "The `animate_vacancy_trajectory()` method brings the static 3D plot to life, generating a fully interactive HTML-based video. This animation includes playback controls and a timeline slider, making it an excellent tool for observing the dynamic movement of vacancies over time.\n", "\n", "However, creating a video of the entire trajectory can be resource-intensive and result in very large file sizes. Therefore, it is highly recommended to focus on a specific range of interest using the `step_init` and `step_final` arguments." ] }, { "cell_type": "code", "execution_count": 16, "id": "62030b50", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Make Animation: 100%|##############################| 26/26 [00:00<00:00, 77.40it/s]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\n", "'trajectory_video.html' created.\n", "\n", "Execution Time: 0.626 seconds\n", "Peak RAM Usage: 0.035 GB\n" ] } ], "source": [ "calc.animate_vacancy_trajectory(vacancy_indices=[0, 1], step_init=5500, step_final=5525, unwrap=False)" ] }, { "cell_type": "markdown", "id": "5b0fab6b", "metadata": {}, "source": [ "Example output:\n", "
\n", "\n", "\n", "\n", "
" ] } ], "metadata": { "kernelspec": { "display_name": "vhp_ver3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.18" } }, "nbformat": 4, "nbformat_minor": 5 }