diff --git a/config.yml b/config.yml index 7b88d0f05..5c9cca38b 100644 --- a/config.yml +++ b/config.yml @@ -66,7 +66,7 @@ notebooks: - ipywidgets - tabulate==0.9.0 - mat3ra-utils - - mat3ra-api-client>=2026.10.8.post0 + - emfs:/drive/packages/mat3ra_api_client-2026.11.4.post1.dev7+gb5f25f7-py3-none-any.whl - name: dataframe packages_pyodide: - jinja2 @@ -79,7 +79,7 @@ notebooks: - emfs:/drive/packages/pydantic-2.7.1-py3-none-any.whl - mat3ra-standata - mat3ra-ide - - mat3ra-api-client>=2026.10.8.post0 + - emfs:/drive/packages/mat3ra_api_client-2026.11.4.post1.dev7+gb5f25f7-py3-none-any.whl - requests - mat3ra-ade - mat3ra-mode diff --git a/other/materials_designer/specific_examples/Introduction.ipynb b/other/materials_designer/specific_examples/Introduction.ipynb index 898458bb1..1582835c0 100644 --- a/other/materials_designer/specific_examples/Introduction.ipynb +++ b/other/materials_designer/specific_examples/Introduction.ipynb @@ -35,6 +35,7 @@ "| `D-3D-AMO` | Amorphous | *To be added* | — | — |\n", "| `D-2D-ADA` | Adatom | [Pt Adatom/Island on MoS₂](defect_point_adatom_island_molybdenum_disulfide_platinum.ipynb) | *To be added* | [[10]](#ref10) |\n", "| `D-2D-ADA` | Adatom | [Adatom on Graphene](defect_surface_adatom_graphene.ipynb) | *To be added* | [[11]](#ref11) |\n", + "| `D-2D-ADA` | Adatom | [Ti Adatom on a 5×5 TiN Island](defect_point_adatom_island_titanium_nitride.ipynb) | [Ti Adatom Descent on a 5×5 TiN Island (MACE and DFT)](defect_point_adatom_island_titanium_nitride_SIMULATION.ipynb) | [[14]](#ref14) |\n", "| `D-2D-GBP` | Grain Boundary Planar | [Grain Boundary in 3D FCC Cu](defect_planar_grain_boundary_3d_fcc_metals_copper.ipynb) | *To be added* | [[13]](#ref13) |\n", "| `D-2D-ISL` | Island | [TiN Island on Surface](defect_surface_island_titanium_nitride.ipynb) | *To be added* | [[14]](#ref14) |\n", "| `D-2D-TER` | Terrace | [Step/Terrace on Pt Surface](defect_surface_step_platinum.ipynb) | *To be added* | [[15]](#ref15) |\n", diff --git a/other/materials_designer/specific_examples/defect_point_adatom_island_titanium_nitride.ipynb b/other/materials_designer/specific_examples/defect_point_adatom_island_titanium_nitride.ipynb new file mode 100644 index 000000000..02fb97193 --- /dev/null +++ b/other/materials_designer/specific_examples/defect_point_adatom_island_titanium_nitride.ipynb @@ -0,0 +1,325 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Ti Adatom on a 5×5 TiN Island on TiN(001)\n", + "\n", + "## 0. Introduction\n", + "\n", + "This tutorial builds the cell of the Ti adatom descent study in the publication:\n", + "\n", + "> **D. G. Sangiovanni, A. B. Mei, D. Edström, L. Hultman, V. Chirita, I. Petrov, and J. E. Greene**, \"Effects of surface vibrations on interlayer mass transport: Ab initio molecular dynamics investigation of Ti adatom descent pathways and rates from TiN/TiN(001) islands\", Physical Review B 97, 035406 (2018). [DOI: 10.1103/PhysRevB.97.035406](https://doi.org/10.1103/PhysRevB.97.035406)\n", + "\n", + "The cell (Sec. II.A, Fig. 1b) is a 5×5-atom TiN island, one plane high with Ti corners, on a 12×12×3-atom\n", + "TiN(001) slab with 15.3 Å of vacuum: 457 atoms. A Ti adatom is added at each of the sites a, c and i of Fig. 6.\n", + "\n", + "![Fig. 1(b)](https://github.com/Exabyte-io/documentation/raw/98b0e6b61a7007a4a53e41d894fb56c891ccaaa0/images/tutorials/materials/defects/defect-point-adatom-island-titanium-nitride/0-figure-1b-from-manuscript.webp)" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## 1. Prepare the Environment\n", + "### 1.1. Set up defect parameters\n", + "\n", + "Each entry of `ADATOM_SITES` places one adatom relative to the N atom with the smaller x in the island's\n", + "edge row at the largest y, in units of the Ti–N site spacing d = a/2, with Δz = 0 the island plane; to add a\n", + "site, add an entry under a new letter to both `ADATOM_SITES` and `ADATOM_MATERIAL_NAMES`.\n", + "\n", + "`CELL_SIZE` picks the cell: `\"12x12x3\"`, the manuscript's (12×12 sites, 3 slab planes, 457 atoms), or `\"10x10x2\"`\n", + "(10×10 sites, 2 slab planes, 225 atoms). With the bottom slab plane frozen, MACE gives E(c) − E(a) = 0.315 and 0.311 eV,\n", + "E(i) − E(a) = −2.247 and −2.109 eV, and E_ads(a) = −3.015 and −3.067 eV on the two." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "MATERIAL_NAME = \"Titanium Nitride\"\n", + "MILLER_INDICES = (0, 0, 1)\n", + "# Thickness counts conventional layers of two (001) planes; bottom planes are removed after the island is added.\n", + "SLAB_THICKNESS = 2\n", + "VACUUM = 15.3 # Angstrom\n", + "NUMBER_OF_ADDED_LAYERS = 0.5\n", + "\n", + "# Cell sizes as (xy supercell, island box minimum and maximum in crystal coordinates, bottom planes removed);\n", + "# x and y minima one site apart put Ti atoms at the island corners\n", + "CELL_SIZES = {\n", + " \"12x12x3\": ([[6, 0], [0, 6]], [0.3233, 0.24, 0], [0.7233, 0.64, 1], 1),\n", + " \"10x10x2\": ([[5, 0], [0, 5]], [0.29, 0.19, 0], [0.75, 0.65, 1], 2),\n", + "}\n", + "CELL_SIZE = \"12x12x3\"\n", + "XY_SUPERCELL_MATRIX, ISLAND_MIN_COORDINATE, ISLAND_MAX_COORDINATE, PLANES_REMOVED = CELL_SIZES[CELL_SIZE]\n", + "\n", + "ADATOM_ELEMENT = \"Ti\"\n", + "# Adatom sites as (Δx, Δy, Δz) from the island's edge N atom, in units of the Ti–N site spacing;\n", + "# Δz = 0 is the island plane. Fig. 6's b and d–h are NEB images between these sites, not positions.\n", + "ADATOM_SITES = {\n", + " \"a\": (0.5, -0.5, 1), # fourfold hollow on the island beside the edge\n", + " \"c\": (0, 0, 1), # atop the edge N\n", + " \"i\": (0, 1, 0), # atop the terrace N in front of the edge\n", + "}\n", + "\n", + "ISLAND_MATERIAL_NAME = f\"TiN(001) {CELL_SIZE} island 5x5\"\n", + "ADATOM_MATERIAL_NAMES = {\n", + " \"a\": f\"{ISLAND_MATERIAL_NAME} + Ti a (FFH island)\",\n", + " \"c\": f\"{ISLAND_MATERIAL_NAME} + Ti c (atop-N edge)\",\n", + " \"i\": f\"{ISLAND_MATERIAL_NAME} + Ti i (atop-N terrace)\",\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "### 1.2. Install Packages\n", + "The step executes only in Pyodide environment. For other environments, the packages should be installed via `pip install` (see [README](../../README.ipynb))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.packages import install_packages\n", + "\n", + "await install_packages(\"made|specific_examples\")" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "### 1.3. Load input material" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.material import Material\n", + "from mat3ra.standata.materials import Materials\n", + "\n", + "material = Material.create(Materials.get_by_name_first_match(MATERIAL_NAME))" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "### 1.4. Create and preview Slab" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.tools.helpers import create_slab\n", + "from mat3ra.notebooks_utils.ipython.entity.material.visualize import visualize_materials\n", + "\n", + "slab = create_slab(\n", + " crystal=material,\n", + " termination_top=None, # Use default termination\n", + " miller_indices=MILLER_INDICES,\n", + " number_of_layers=SLAB_THICKNESS,\n", + " vacuum=VACUUM,\n", + " use_orthogonal_c=True,\n", + " xy_supercell_matrix=XY_SUPERCELL_MATRIX\n", + ")\n", + "\n", + "visualize_materials(slab)" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "## 2. Create the Island" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from mat3ra.made.tools.analyze.other import get_atom_indices_by_layer\n", + "from mat3ra.made.tools.convert import to_ase\n", + "from mat3ra.made.tools.entities.coordinate import BoxCoordinateCondition\n", + "from mat3ra.made.tools.helpers import create_island\n", + "from mat3ra.made.tools.modify import filter_by_ids, remove_vacuum\n", + "\n", + "coordinate_condition = BoxCoordinateCondition(min_coordinate=ISLAND_MIN_COORDINATE, max_coordinate=ISLAND_MAX_COORDINATE)\n", + "\n", + "slab_with_island = create_island(\n", + " slab=slab,\n", + " condition=coordinate_condition,\n", + " number_of_added_layers=NUMBER_OF_ADDED_LAYERS,\n", + " use_cartesian_coordinates=False,\n", + ")\n", + "for _ in range(PLANES_REMOVED):\n", + " bottom_plane = get_atom_indices_by_layer(slab_with_island)[0]\n", + " slab_with_island = filter_by_ids(slab_with_island, ids=bottom_plane, invert=True, reset_ids=True)\n", + "slab_with_island = remove_vacuum(slab_with_island, fixed_padding=VACUUM)\n", + "slab_with_island.name = ISLAND_MATERIAL_NAME\n", + "\n", + "atoms = to_ase(slab_with_island)\n", + "symbols = np.array(atoms.get_chemical_symbols())\n", + "planes = get_atom_indices_by_layer(slab_with_island)\n", + "island = planes[-1]\n", + "island_x, island_y = atoms.positions[island, 0], atoms.positions[island, 1]\n", + "is_corner = ((np.isclose(island_x, island_x.min()) | np.isclose(island_x, island_x.max()))\n", + " & (np.isclose(island_y, island_y.min()) | np.isclose(island_y, island_y.max())))\n", + "print(f\"{slab_with_island.name}: {len(atoms)} atoms ({sum(symbols == 'Ti')} Ti, {sum(symbols == 'N')} N) \"\n", + " f\"in {len(planes)} planes, c = {atoms.cell[2, 2]:.3f} Å\")\n", + "print(f\"Island: {len(island)} atoms, corners {symbols[island][is_corner].tolist()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## 3. Add the Adatom at the Sites of Fig. 6\n", + "\n", + "The paper gives no coordinates for the sites; they follow from the lattice and are measured on the island\n", + "built above, from the offsets in `ADATOM_SITES`. Measured from the edge N with the smaller x, a lies between\n", + "that N and the edge-centre Ti, the paper's FFH_edge; i is the epitaxial site that extends the island." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.tools.helpers import create_defect_point_interstitial\n", + "\n", + "site_spacing = material.lattice.a / 2\n", + "edge_y = island_y.max()\n", + "edge_nitrogen_x = min(atoms.positions[index, 0] for index in island\n", + " if symbols[index] == \"N\" and np.isclose(atoms.positions[index, 1], edge_y))\n", + "island_z = atoms.positions[island, 2].mean()\n", + "\n", + "adatom_sites = {\n", + " site: [edge_nitrogen_x + offset_x * site_spacing, edge_y + offset_y * site_spacing, island_z + offset_z * site_spacing]\n", + " for site, (offset_x, offset_y, offset_z) in ADATOM_SITES.items()\n", + "}\n", + "\n", + "materials_with_adatom = []\n", + "for site, coordinate in adatom_sites.items():\n", + " material_with_adatom = create_defect_point_interstitial(\n", + " slab_with_island, coordinate, ADATOM_ELEMENT, \"exact_coordinate\", use_cartesian_coordinates=True\n", + " )\n", + " material_with_adatom.name = ADATOM_MATERIAL_NAMES[site]\n", + " materials_with_adatom.append(material_with_adatom)\n", + "\n", + " atoms_with_adatom = to_ase(material_with_adatom)\n", + " adatom = len(atoms_with_adatom) - 1\n", + " distances = np.sort(atoms_with_adatom.get_distances(adatom, range(adatom), mic=True))[:3]\n", + " print(f\"{material_with_adatom.name}: {len(atoms_with_adatom)} atoms, \"\n", + " f\"adatom's three shortest distances {np.round(distances, 3)} Å\")" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "## 4. Visualize the Result(s)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "visualize_materials([{\"material\": material_to_show, \"title\": material_to_show.name, \"rotation\": \"-45x\"}\n", + " for material_to_show in [slab_with_island, *materials_with_adatom]])" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "## 5. Pass data to the outside runtime" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.io import download_content_to_file\n", + "from mat3ra.notebooks_utils.material import set_materials\n", + "\n", + "materials = [slab_with_island, *materials_with_adatom]\n", + "set_materials(materials)\n", + "for material_to_save in materials:\n", + " download_content_to_file(material_to_save.to_json(), f\"{material_to_save.name}.json\")" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "[1] D. G. Sangiovanni, A. B. Mei, D. Edström, L. Hultman, V. Chirita, I. Petrov, J. E. Greene, \"Effects of surface\n", + "vibrations on interlayer mass transport: Ab initio molecular dynamics investigation of Ti adatom descent pathways\n", + "and rates from TiN/TiN(001) islands\", Phys. Rev. B 97, 035406 (2018).\n", + "[DOI: 10.1103/PhysRevB.97.035406](https://doi.org/10.1103/PhysRevB.97.035406)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/other/materials_designer/specific_examples/defect_point_adatom_island_titanium_nitride_SIMULATION.ipynb b/other/materials_designer/specific_examples/defect_point_adatom_island_titanium_nitride_SIMULATION.ipynb new file mode 100644 index 000000000..2a49c229c --- /dev/null +++ b/other/materials_designer/specific_examples/defect_point_adatom_island_titanium_nitride_SIMULATION.ipynb @@ -0,0 +1,808 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Ti Adatom Descent on a 5×5 TiN Island (MACE and DFT)\n", + "\n", + "> **D. G. Sangiovanni, A. B. Mei, D. Edström, L. Hultman, V. Chirita, I. Petrov, and J. E. Greene**,\n", + "> \"Effects of surface vibrations on interlayer mass transport: Ab initio molecular dynamics investigation of Ti\n", + "> adatom descent pathways and rates from TiN/TiN(001) islands\", Physical Review B 97, 035406 (2018).\n", + "> [DOI: 10.1103/PhysRevB.97.035406](https://doi.org/10.1103/PhysRevB.97.035406)\n", + "\n", + "Relaxes a Ti adatom at three sites of the paper's Fig. 6 on the cell created in the\n", + "[structure notebook](defect_point_adatom_island_titanium_nitride.ipynb): **a**, the fourfold hollow on the island\n", + "next to its edge; **c**, atop the edge N atom; **i**, atop the terrace N atom in front of the edge, the site\n", + "that extends the island. The energies of c and i relative to a, and the adsorption energy at a, are compared\n", + "with the paper's. The paper computed them with DFT (GGA); here every relaxation uses the\n", + "[MACE-MP-0](https://github.com/ACEsuit/mace) machine-learned force field with the D3 dispersion correction.\n", + "DFT (PBE) relaxations on the platform, started from the MACE-relaxed structures, give the energies printed beside MACE's.\n", + "\n", + "The four relaxations are MACE runs on 457 and 458 atoms in the manuscript's cell, meant to be executed natively.\n", + "\n", + "

Usage

\n", + "\n", + "1. Create the materials in the [structure notebook](defect_point_adatom_island_titanium_nitride.ipynb) with the same `CELL_SIZE`, which saves the island cell and the three adatom cells to the `uploads` folder.\n", + "1. Set the material names and parameters in cells 1.2-1.5 (or use the defaults).\n", + "1. Run natively: `pip install \".[all]\" mace-torch torch-dftd`. In JupyterLite the install and the calculator run, but the first force call on the manuscript's cell exceeds the browser's memory (a 0.76 GB tensor), so the relaxations run natively; the browser also has no torch-dftd, so D3 is off there.\n", + "1. Click \"Run\" > \"Run All\" to run all cells.\n", + "1. Wait for the DFT jobs to finish.\n", + "1. Scroll down to view the results.\n", + "\n", + "## Summary\n", + "\n", + "1. Set up the environment and parameters: install packages (JupyterLite only), material names, force-field, relaxation, DFT and compute parameters.\n", + "1. Load the materials by name from the `uploads` folder.\n", + "1. Build the MACE calculator.\n", + "1. Relax the island cell; measure the island contraction and the surface ripple.\n", + "1. Relax the adatom at a, c and i.\n", + "1. Compute the energy of an isolated Ti atom.\n", + "1. Run DFT on the platform: a fixed-cell relaxation on the four slab structures, and a total energy on the isolated Ti atom, which has nothing to relax.\n", + "1. Compare with the paper: energies relative to a, the adsorption energy at a, and Fig. 6." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## 1. Set up the environment and parameters\n", + "### 1.1. Install packages (JupyterLite)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.calculators.mlff import get_mlff_install_profiles\n", + "from mat3ra.notebooks_utils.packages import install_packages\n", + "\n", + "await install_packages(get_mlff_install_profiles(\"mace\"))\n", + "\n", + "from mat3ra.notebooks_utils.pyodide.packages.patches import apply_all_patches\n", + "\n", + "apply_all_patches(\"mace\")" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "### 1.2. Material names" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "# Names saved by defect_point_adatom_island_titanium_nitride.ipynb, with the CELL_SIZE set there.\n", + "CELL_SIZE = \"12x12x3\"\n", + "ISLAND_MATERIAL_NAME = f\"TiN(001) {CELL_SIZE} island 5x5\"\n", + "ADATOM_MATERIAL_NAMES = {\n", + " \"a\": f\"{ISLAND_MATERIAL_NAME} + Ti a (FFH island)\",\n", + " \"c\": f\"{ISLAND_MATERIAL_NAME} + Ti c (atop-N edge)\",\n", + " \"i\": f\"{ISLAND_MATERIAL_NAME} + Ti i (atop-N terrace)\",\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "### 1.3. Parameters\n", + "\n", + "The DFT settings below — `MAXIMUM_IONIC_STEPS`, `TIME_LIMIT` and the queue — are sized for the `\"10x10x2\"`\n", + "cell: to run them, set `CELL_SIZE = \"10x10x2\"` in both notebooks and list the structures to run in\n", + "`DFT_MATERIAL_LABELS`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "ORGANIZATION_NAME = None\n", + "FOLDER = \"./uploads\"\n", + "\n", + "MACE_MODEL_FAMILY = \"MACE-MP-0\"\n", + "MACE_MODEL = \"large\"\n", + "MACE_DISPERSION = True\n", + "MACE_DEFAULT_DTYPE = \"float64\"\n", + "MACE_DEVICE = \"cpu\"\n", + "\n", + "FMAX = 0.02 # eV/Å\n", + "MAX_STEPS = 1000\n", + "FROZEN_SLAB_PLANES = 1 # bottom slab planes held fixed in the MACE and DFT relaxations\n", + "\n", + "# One platform job per structure: a fixed-cell relaxation on the MACE-relaxed slab structures, and a total\n", + "# energy on the isolated Ti atom, which has nothing to relax; an empty list skips the platform jobs.\n", + "# The structures can be added one at a time, each after the previous job has finished.\n", + "DFT_MATERIAL_LABELS = [\n", + " # \"slab_with_island\",\n", + " # \"a\",\n", + " # \"c\",\n", + " # \"i\",\n", + " # \"isolated_adatom\",\n", + "]" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "### 1.4. DFT model parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "# manuscript: VASP, every structure relaxed with DFT; here the DFT relaxation starts from the MACE-relaxed structure\n", + "APPLICATION_NAME = \"espresso\"\n", + "RELAXATION_SEARCH_TERM = \"fixed_cell_relaxation.json\"\n", + "TOTAL_ENERGY_SEARCH_TERM = \"total_energy.json\" # the isolated atom: one SCF, nothing to relax\n", + "RELAX_UNIT = \"pw_relax\"\n", + "SCF_UNIT = \"pw_scf\"\n", + "# manuscript: GGA of Perdew, Burke and Wang (PW91); for PW91 add \"input_dft\": \"pw91\" to DFT_SYSTEM_SETTINGS\n", + "FUNCTIONAL = \"pbe\"\n", + "PSEUDOPOTENTIAL_TYPE = \"us\" # manuscript: PAW; ours GBRV ultrasoft\n", + "ECUTWFC = 40 # Ry; manuscript: 400 eV for the static calculations, Sec. II.B\n", + "ECUTRHO = 200 # Ry\n", + "DFT_KGRID = [1, 1, 1] # manuscript: Γ only\n", + "DFT_SYSTEM_SETTINGS = {\"smearing\": \"mv\", \"degauss\": 0.01} # Ry\n", + "MAXIMUM_IONIC_STEPS = 5 # per job, so that it ends within TIME_LIMIT\n", + "DAVIDSON_SUBSPACE_DIMENSION = 2 # half the default 4: less memory per process\n", + "DFT_CONTROL_SETTINGS = {\"nstep\": MAXIMUM_IONIC_STEPS}\n", + "# conv_thr 1e-4 Ry ≈ 1.4 meV on the total energy; local-TF mixing for a slab with vacuum\n", + "DFT_ELECTRONS_SETTINGS = {\"conv_thr\": 1e-4, \"mixing_mode\": \"local-TF\", \"mixing_beta\": 0.2,\n", + " \"diago_david_ndim\": DAVIDSON_SUBSPACE_DIMENSION}\n", + "DFT_SPIN_SETTINGS_BY_LABEL = {\"isolated_adatom\": {\"nspin\": 2, \"tot_magnetization\": 2}} # two unpaired Ti d electrons" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "### 1.5. Compute parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.ide.compute import QueueName\n", + "\n", + "CLUSTER_NAME = \"001\"\n", + "QUEUE_NAME = QueueName.OF\n", + "PPN = 60 # OF's maxPPN: the job is billed for the whole node\n", + "TIME_LIMIT = \"08:00:00\"\n", + "POLL_INTERVAL = 600 # seconds" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## 2. Load the materials" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.material import load_materials_from_folder\n", + "\n", + "materials_by_name = {material.name: material for material in load_materials_from_folder(FOLDER)}\n", + "slab_with_island = materials_by_name[ISLAND_MATERIAL_NAME]\n", + "materials_with_adatom = {site: materials_by_name[name] for site, name in ADATOM_MATERIAL_NAMES.items()}\n", + "\n", + "for material in [slab_with_island, *materials_with_adatom.values()]:\n", + " print(f\"{material.name}: {material.basis.number_of_atoms} atoms\")" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "## 3. Build the MACE calculator" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "import importlib.util\n", + "\n", + "from mat3ra.notebooks_utils.calculators.mlff import create_mlff_calculator\n", + "\n", + "dispersion_available = importlib.util.find_spec(\"torch_dftd\") is not None\n", + "dispersion_active = MACE_DISPERSION and dispersion_available\n", + "if MACE_DISPERSION and not dispersion_available:\n", + " print(\"torch-dftd is not available here: MACE runs WITHOUT the D3 dispersion correction.\")\n", + "\n", + "calculator = create_mlff_calculator(\n", + " \"mace\",\n", + " {\n", + " \"family\": MACE_MODEL_FAMILY,\n", + " \"model\": MACE_MODEL,\n", + " \"dispersion\": dispersion_active,\n", + " \"default_dtype\": MACE_DEFAULT_DTYPE,\n", + " \"device\": MACE_DEVICE,\n", + " },\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "## 4. Relax the island cell\n", + "### 4.1. Relax\n", + "\n", + "The bottom slab plane is held fixed, as in the paper's AIMD runs (Sec. II.A); every other atom is free." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "16", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from mat3ra.made.tools.analyze.other import get_atom_indices_by_layer\n", + "from mat3ra.made.tools.calculate import calculate_total_energy\n", + "from mat3ra.made.tools.convert import to_ase\n", + "from mat3ra.notebooks_utils.workflows.relaxation import relax_material\n", + "\n", + "frozen_atom_indices = [index for plane in get_atom_indices_by_layer(slab_with_island)[:FROZEN_SLAB_PLANES]\n", + " for index in plane]\n", + "slab_with_island_relaxed = relax_material(slab_with_island, calculator, fmax=FMAX, max_steps=MAX_STEPS,\n", + " fixed_atom_indices=frozen_atom_indices)\n", + "energy_slab_with_island = calculate_total_energy(slab_with_island_relaxed, calculator)\n", + "print(f\"E(slab + island) = {energy_slab_with_island:.3f} eV\")" + ] + }, + { + "cell_type": "markdown", + "id": "17", + "metadata": {}, + "source": [ + "### 4.2. Island contraction and surface ripple\n", + "\n", + "The island's Ti–Ti spacing along its diagonals and Ti–N spacing along its medians, against the same spacings as\n", + "built on the bulk lattice; the ripple is the height of the N atoms above the Ti atoms in the exposed top plane of\n", + "the terrace (Fig. 2 of the paper)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "18", + "metadata": {}, + "outputs": [], + "source": [ + "built, relaxed = to_ase(slab_with_island), to_ase(slab_with_island_relaxed)\n", + "symbols = np.array(built.get_chemical_symbols())\n", + "planes = get_atom_indices_by_layer(slab_with_island)\n", + "island, terrace = np.array(planes[-1]), np.array(planes[-2])\n", + "\n", + "island_x, island_y = built.positions[island, 0], built.positions[island, 1]\n", + "x_values = (island_x.min(), (island_x.min() + island_x.max()) / 2, island_x.max())\n", + "y_values = (island_y.min(), (island_y.min() + island_y.max()) / 2, island_y.max())\n", + "grid = {(column, row): island[np.argmin(np.hypot(island_x - x, island_y - y))]\n", + " for column, x in enumerate(x_values) for row, y in enumerate(y_values)}\n", + "island_rows = {\n", + " \"Ti–Ti along diagonals\": [(grid[0, 0], grid[2, 2]), (grid[0, 2], grid[2, 0])],\n", + " \"Ti–N along medians\": [(grid[0, 1], grid[2, 1]), (grid[1, 0], grid[1, 2])],\n", + "}\n", + "spacings_per_row = np.sqrt(len(island)) - 1\n", + "\n", + "island_contraction = {}\n", + "for label, pairs in island_rows.items():\n", + " as_built, after = (np.mean([atoms.get_distance(*pair) for pair in pairs]) / spacings_per_row\n", + " for atoms in (built, relaxed))\n", + " island_contraction[label] = 100 * (after / as_built - 1)\n", + " print(f\"{label}: {after:.3f} Å, {island_contraction[label]:+.1f} % against {as_built:.3f} Å as built\")\n", + "\n", + "island_xy = built.positions[island, :2]\n", + "exposed_terrace = [index for index in terrace\n", + " if not np.isclose(np.linalg.norm(island_xy - built.positions[index, :2], axis=1), 0).any()]\n", + "ripple = (np.mean([relaxed.positions[index, 2] for index in exposed_terrace if symbols[index] == \"N\"])\n", + " - np.mean([relaxed.positions[index, 2] for index in exposed_terrace if symbols[index] == \"Ti\"]))\n", + "print(f\"Ripple of the exposed terrace ({len(exposed_terrace)} atoms): {ripple:.3f} Å\")" + ] + }, + { + "cell_type": "markdown", + "id": "19", + "metadata": {}, + "source": [ + "## 5. Relax the adatom at a, c and i\n", + "\n", + "Each relaxation starts from the site placed in the structure notebook; the adatom's displacement from that site\n", + "shows whether it stayed there." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "20", + "metadata": {}, + "outputs": [], + "source": [ + "energies = {}\n", + "relaxed_materials_with_adatom = {}\n", + "for site, material in materials_with_adatom.items():\n", + " material_relaxed = relax_material(material, calculator, fmax=FMAX, max_steps=MAX_STEPS,\n", + " fixed_atom_indices=frozen_atom_indices)\n", + " relaxed_materials_with_adatom[site] = material_relaxed\n", + " energies[site] = calculate_total_energy(material_relaxed, calculator)\n", + "\n", + " placed, final = to_ase(material), to_ase(material_relaxed)\n", + " adatom = len(final) - 1\n", + " displacement = np.linalg.norm(final.positions[adatom] - placed.positions[adatom])\n", + " distances = np.sort(final.get_distances(adatom, range(adatom), mic=True))[:3]\n", + " print(f\"{site}: E = {energies[site]:.3f} eV, adatom moved {displacement:.2f} Å from its site, \"\n", + " f\"three shortest distances {np.round(distances, 3)} Å\")" + ] + }, + { + "cell_type": "markdown", + "id": "21", + "metadata": {}, + "source": [ + "### 5.1. Visualize each site as built and relaxed\n", + "\n", + "Side view along the island edge: the island is on the left, the terrace on the right." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.ipython.entity.material.visualize import visualize_materials\n", + "\n", + "visualize_materials([{\"material\": materials[site], \"title\": f\"{site} — {state}\", \"rotation\": \"-90x,-90y\"}\n", + " for state, materials in ((\"as built\", materials_with_adatom), (\"relaxed\", relaxed_materials_with_adatom))\n", + " for site in materials])" + ] + }, + { + "cell_type": "markdown", + "id": "23", + "metadata": {}, + "source": [ + "## 6. Isolated Ti atom\n", + "\n", + "The reference of the adsorption energy, Eq. (4) of the paper: one Ti atom at the centre of an otherwise\n", + "empty cell the size of the island cell, same calculator. This energy is the model's prediction for a single\n", + "atom, outside its training data (periodic crystals); the comparison below prints the paper's −2.275 eV beside it." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "24", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.esse.models.materials_category_components.entities.core.zero_dimensional.atom import AtomSchema\n", + "from mat3ra.made.tools.build_components.entities.core.zero_dimensional.atom.builder import AtomAtCoordinateBuilder\n", + "from mat3ra.made.tools.build_components.entities.core.zero_dimensional.atom.configuration import AtomAtCoordinateConfiguration\n", + "\n", + "adatom_element = materials_with_adatom[\"a\"].basis.elements.values[-1]\n", + "isolated_adatom = AtomAtCoordinateBuilder().get_material(AtomAtCoordinateConfiguration(\n", + " crystal=slab_with_island, element=AtomSchema(chemical_element=adatom_element), coordinate=[0.5, 0.5, 0.5]))\n", + "energy_isolated_adatom = calculate_total_energy(isolated_adatom, calculator)\n", + "print(f\"E({adatom_element}, isolated) = {energy_isolated_adatom:.3f} eV\")" + ] + }, + { + "cell_type": "markdown", + "id": "25", + "metadata": {}, + "source": [ + "## 7. DFT on the platform\n", + "\n", + "One job per structure: a fixed-cell relaxation on the four slab structures, starting from the island cell and the\n", + "adatom at a, c and i as MACE relaxed them, with the same bottom slab plane fixed; and a total energy on the\n", + "isolated Ti atom, which has nothing to relax, spin-polarized. A finished or running job of the same workflow on\n", + "the same structure is reused.\n", + "\n", + "### 7.1. Authenticate" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "26", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.auth import authenticate\n", + "\n", + "await authenticate()" + ] + }, + { + "cell_type": "markdown", + "id": "27", + "metadata": {}, + "source": [ + "### 7.2. Initialize the API client" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "28", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.api_client import APIClient\n", + "\n", + "client = APIClient.authenticate()" + ] + }, + { + "cell_type": "markdown", + "id": "29", + "metadata": {}, + "source": [ + "### 7.3. Select the account" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "30", + "metadata": {}, + "outputs": [], + "source": [ + "selected_account = client.my_account\n", + "\n", + "if ORGANIZATION_NAME:\n", + " selected_account = client.get_account(name=ORGANIZATION_NAME)\n", + "\n", + "ACCOUNT_ID = selected_account.id\n", + "print(f\"Selected account ID: {ACCOUNT_ID}, name: {selected_account.name}\")" + ] + }, + { + "cell_type": "markdown", + "id": "31", + "metadata": {}, + "source": [ + "### 7.4. Select the project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "32", + "metadata": {}, + "outputs": [], + "source": [ + "projects = client.projects.list({\"isDefault\": True, \"owner._id\": ACCOUNT_ID})\n", + "project_id = projects[0][\"_id\"]\n", + "print(f\"Using project: {projects[0]['name']} ({project_id})\")" + ] + }, + { + "cell_type": "markdown", + "id": "33", + "metadata": {}, + "source": [ + "### 7.5. Save the structures to the platform" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "34", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.code.array_with_ids import ArrayWithIds\n", + "from mat3ra.notebooks_utils.core.entity.material.api import get_or_create_material\n", + "\n", + "dft_materials = {label: material.clone() for label, material in\n", + " {\"slab_with_island\": slab_with_island_relaxed, **relaxed_materials_with_adatom}.items()}\n", + "for material in dft_materials.values():\n", + " material.name = f\"{material.name} (MACE-relaxed)\"\n", + " material.basis.constraints = ArrayWithIds.from_values(\n", + " [[index not in frozen_atom_indices] * 3 for index in range(material.basis.number_of_atoms)])\n", + "dft_materials[\"isolated_adatom\"] = isolated_adatom.clone()\n", + "dft_materials[\"isolated_adatom\"].name = f\"{adatom_element} atom {CELL_SIZE}\"\n", + "saved_materials = {label: get_or_create_material(client, dft_materials[label], ACCOUNT_ID) for label in DFT_MATERIAL_LABELS}\n", + "saved_materials = {label: client.materials.update(saved[\"_id\"], dft_materials[label].to_dict())\n", + " for label, saved in saved_materials.items()}" + ] + }, + { + "cell_type": "markdown", + "id": "35", + "metadata": {}, + "source": [ + "### 7.6. Set the DFT model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "36", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.mode import ModelFactory\n", + "from mat3ra.standata.model_tree import ModelTreeStandata\n", + "\n", + "model_config = ModelTreeStandata.get_model_by_parameters(type=\"dft\", subtype=\"gga\", functional=FUNCTIONAL)\n", + "model_config[\"method\"] = {\"type\": \"pseudopotential\", \"subtype\": PSEUDOPOTENTIAL_TYPE}\n", + "model = ModelFactory.create(model_config)" + ] + }, + { + "cell_type": "markdown", + "id": "37", + "metadata": {}, + "source": [ + "### 7.7. Create the compute configuration" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "38", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.ide.compute import Compute\n", + "\n", + "cluster = next(cluster for cluster in client.clusters.list() if not CLUSTER_NAME or CLUSTER_NAME in cluster[\"hostname\"])\n", + "compute = Compute(cluster=cluster, queue=QUEUE_NAME, ppn=PPN, timeLimit=TIME_LIMIT)\n", + "print(f\"Using cluster: {compute.cluster.hostname}, queue: {QUEUE_NAME}, ppn: {PPN}, time limit: {TIME_LIMIT}\")" + ] + }, + { + "cell_type": "markdown", + "id": "39", + "metadata": {}, + "source": [ + "### 7.8. Create and submit one job per structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "40", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.api.job import submit_jobs\n", + "from mat3ra.notebooks_utils.core.entity.job.api import find_job_for_material\n", + "from mat3ra.notebooks_utils.job import create_job\n", + "from mat3ra.notebooks_utils.workflow import apply_planewave_cutoffs, apply_scf_kgrid, patch_workflow_qe_input\n", + "from mat3ra.standata.workflows import WorkflowStandata\n", + "from mat3ra.wode.workflows import Workflow\n", + "\n", + "RELAXATION_WORKFLOW = (RELAXATION_SEARCH_TERM, RELAX_UNIT, DFT_CONTROL_SETTINGS)\n", + "WORKFLOW_BY_LABEL = {\"isolated_adatom\": (TOTAL_ENERGY_SEARCH_TERM, SCF_UNIT, {})}\n", + "dft_job_ids, new_job_ids = {}, []\n", + "for label, material in saved_materials.items():\n", + " search_term, unit_name, control = WORKFLOW_BY_LABEL.get(label, RELAXATION_WORKFLOW)\n", + " workflow = Workflow.create(\n", + " WorkflowStandata.filter_by_application(APPLICATION_NAME).get_by_name_first_match(search_term))\n", + " workflow.subworkflows[0].model = model\n", + " apply_planewave_cutoffs(workflow, ECUTWFC, ECUTRHO, unit_name=unit_name)\n", + " apply_scf_kgrid(workflow, DFT_KGRID, material=dft_materials[label], unit_name=unit_name)\n", + " system = {**DFT_SYSTEM_SETTINGS, **DFT_SPIN_SETTINGS_BY_LABEL.get(label, {})}\n", + " patch_workflow_qe_input(\n", + " workflow, {\"control\": control, \"system\": system, \"electrons\": DFT_ELECTRONS_SETTINGS}, [unit_name]\n", + " )\n", + " job = find_job_for_material(client, material[\"_id\"], workflow.name, ACCOUNT_ID,\n", + " statuses=(\"submitted\", \"queued\", \"active\", \"finished\"), kgrid=DFT_KGRID,\n", + " unit_name=unit_name)\n", + " if job is None:\n", + " job = create_job(api_client=client, materials=[material], workflow=workflow, project_id=project_id,\n", + " owner_id=ACCOUNT_ID, compute=compute.to_dict(), prefix=f\"{workflow.name} {material['name']}\")\n", + " new_job_ids.append(job[\"_id\"])\n", + " dft_job_ids[label] = job[\"_id\"]\n", + " print(f\"{label}: job {job['_id']}, {job['status']}\")\n", + "submit_jobs(client.jobs, new_job_ids)" + ] + }, + { + "cell_type": "markdown", + "id": "41", + "metadata": {}, + "source": [ + "### 7.9. Wait for the jobs to finish" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "42", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.api.job import wait_for_jobs_to_finish_async\n", + "\n", + "if dft_job_ids:\n", + " await wait_for_jobs_to_finish_async(client.jobs, list(dft_job_ids.values()), poll_interval=POLL_INTERVAL)" + ] + }, + { + "cell_type": "markdown", + "id": "43", + "metadata": {}, + "source": [ + "### 7.10. Read the final energies" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "44", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.core.entity.property.api import get_properties_for_job\n", + "\n", + "dft_energies = {label: get_properties_for_job(client, job_id, \"total_energy\")[0][\"value\"]\n", + " for label, job_id in dft_job_ids.items()}" + ] + }, + { + "cell_type": "markdown", + "id": "45", + "metadata": {}, + "source": [ + "## 8. Compare with the paper\n", + "### 8.1. Energies relative to a, adsorption energy at a, island relaxation\n", + "\n", + "The paper's energies of c and i are read off Fig. 6 (±0.02 and ±0.05 eV); the adsorption energy at a, the\n", + "island contraction and the ripple are from Sec. III.A, the isolated Ti atom energy it uses from Sec. II.B.\n", + "Quantum ESPRESSO's absolute energy for the free Ti atom is not on the manuscript's scale, so the DFT column\n", + "leaves E_Ti out; it cancels in E_ads(a), where it is used.\n", + "The island contraction and the ripple are measured on the MACE relaxation only." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "46", + "metadata": {}, + "outputs": [], + "source": [ + "# Fig. 6 upper panel, read off; b = 0.10 eV also Sec. III.C.1\n", + "PAPER_FIG6 = {\"a\": 0.0, \"b\": 0.10, \"c\": -0.15, \"d\": -0.20, \"e\": -0.25, \"f\": -0.50, \"g\": -1.13, \"h\": -2.25, \"i\": -2.65}\n", + "PAPER_ADSORPTION_ENERGY = -2.81 # Sec. III.A, FFH_edge on the 5×5 island\n", + "PAPER_ISOLATED_ADATOM_ENERGY = -2.275 # Sec. II.B, Eq. (4), from ref. [68]\n", + "PAPER_ISLAND_CONTRACTION = {\"Ti–Ti along diagonals\": -6.8, \"Ti–N along medians\": -5.2} # Sec. III.A, p. 10\n", + "PAPER_RIPPLE = 0.19 # Sec. III.A, Fig. 2b\n", + "\n", + "relative_energies = {site: energy - energies[\"a\"] for site, energy in energies.items()}\n", + "mace_energies = {\"slab_with_island\": energy_slab_with_island, **energies, \"isolated_adatom\": energy_isolated_adatom}\n", + "dft_energies_or_nan = {label: dft_energies.get(label, np.nan) for label in mace_energies}\n", + "energies_by_method = {\"MACE\": mace_energies, \"DFT\": dft_energies_or_nan}\n", + "comparison = {\n", + " \"E(c) - E(a), eV\": [energy[\"c\"] - energy[\"a\"] for energy in energies_by_method.values()] + [PAPER_FIG6[\"c\"]],\n", + " \"E(i) - E(a), eV\": [energy[\"i\"] - energy[\"a\"] for energy in energies_by_method.values()] + [PAPER_FIG6[\"i\"]],\n", + " \"E_ads(a), eV\": [energy[\"a\"] - energy[\"slab_with_island\"] - energy[\"isolated_adatom\"]\n", + " for energy in energies_by_method.values()] + [PAPER_ADSORPTION_ENERGY],\n", + " \"E_Ti, isolated, eV\": [energy_isolated_adatom, np.nan, PAPER_ISOLATED_ADATOM_ENERGY],\n", + " **{f\"{label}, %\": [island_contraction[label], np.nan, PAPER_ISLAND_CONTRACTION[label]] for label in island_contraction},\n", + " \"Ripple, Å\": [ripple, np.nan, PAPER_RIPPLE],\n", + "}\n", + "print(f\"{'':<28}{'MACE':>10}{'DFT':>10}{'manuscript':>12}\")\n", + "for label, (mace, dft, manuscript) in comparison.items():\n", + " dft_cell = \"—\" if np.isnan(dft) else f\"{dft:.3f}\"\n", + " print(f\"{label:<28}{mace:>10.3f}{dft_cell:>10}{manuscript:>12}\")" + ] + }, + { + "cell_type": "markdown", + "id": "47", + "metadata": {}, + "source": [ + "### 8.2. Fig. 6" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48", + "metadata": {}, + "outputs": [], + "source": [ + "import plotly.graph_objects as go\n", + "\n", + "dft_sites = [site for site in relative_energies if site in dft_energies] if \"a\" in dft_energies else []\n", + "\n", + "figure = go.Figure()\n", + "figure.add_trace(go.Scatter(x=list(PAPER_FIG6), y=list(PAPER_FIG6.values()), mode=\"lines+markers\",\n", + " name=\"Sangiovanni et al. (2018)\"))\n", + "figure.add_trace(go.Scatter(x=list(relative_energies), y=list(relative_energies.values()), mode=\"markers\",\n", + " marker={\"size\": 12}, name=\"this notebook (MACE-MP-0)\"))\n", + "if dft_sites:\n", + " figure.add_trace(go.Scatter(x=dft_sites, y=[dft_energies[site] - dft_energies[\"a\"] for site in dft_sites],\n", + " mode=\"markers\", marker={\"size\": 14, \"symbol\": \"diamond-open\"},\n", + " name=\"this notebook (DFT)\"))\n", + "figure.update_layout(title=\"Ti adatom descent from the 5×5 island edge (Fig. 6)\", xaxis_title=\"state\",\n", + " yaxis_title=\"energy relative to a (eV)\")\n", + "figure.show()" + ] + }, + { + "cell_type": "markdown", + "id": "49", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "[1] D. G. Sangiovanni, A. B. Mei, D. Edström, L. Hultman, V. Chirita, I. Petrov, J. E. Greene, \"Effects of surface\n", + "vibrations on interlayer mass transport: Ab initio molecular dynamics investigation of Ti adatom descent pathways\n", + "and rates from TiN/TiN(001) islands\", Phys. Rev. B 97, 035406 (2018).\n", + "[DOI: 10.1103/PhysRevB.97.035406](https://doi.org/10.1103/PhysRevB.97.035406)\n", + "\n", + "[2] MACE-MP-0 foundation models: https://github.com/ACEsuit/mace" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/packages/mat3ra_api_client-2026.11.4.post1.dev7+gb5f25f7-py3-none-any.whl b/packages/mat3ra_api_client-2026.11.4.post1.dev7+gb5f25f7-py3-none-any.whl new file mode 100644 index 000000000..e655f953a --- /dev/null +++ b/packages/mat3ra_api_client-2026.11.4.post1.dev7+gb5f25f7-py3-none-any.whl @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e0999d8a1f3a41775a85d8c1d86ba509c00ab0f7663fc3f890927cafa779728 +size 28984 diff --git a/pyproject.toml b/pyproject.toml index 2840c8d43..a501d7e6f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ utils_standata = [ "mat3ra-standata", ] api = [ - "mat3ra-api-client>=2026.10.8.post0", + "mat3ra-api-client @ git+https://github.com/mat3ra/api-client@b5f25f755db5c40d7580662affa16ac642a8d67c", ] materials = [ # ase >=3.25.0 is required for supercell to be generated,