{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Manipulating Atoms\nThis tutorial shows how to build and manipulate structures\nwith ASE.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Ag adatom on Ni slab\n\nWe will set up a one layer slab of four Ni atoms with one Ag adatom.\nDefine the slab atoms:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from math import sqrt\n\nfrom ase import Atoms\n\na = 3.55\natoms = Atoms(\n 'Ni4',\n cell=[sqrt(2) * a, sqrt(2) * a, 1.0, 90, 90, 120],\n pbc=(1, 1, 0),\n scaled_positions=[(0, 0, 0), (0.5, 0, 0), (0, 0.5, 0), (0.5, 0.5, 0)],\n)\natoms.center(vacuum=5.0, axis=2)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Have a look at the cell and positions of the atoms:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(atoms.cell)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(atoms.positions)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(atoms[0])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Visualizing a structure\n\nWrite the structure to a file and plot the whole system by bringing up the\n:mod:`ase.gui`:\n\n```python\nfrom ase.visualize import view\natoms.write('slab.xyz')\nview(atoms)\n```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, we can plot structures with Matplotlib.\nThroughout this tutorial, we will be using matplotlib to visualize the\nstructures. Note, however, that in practice using the view function\nor opening structures in the :mod:`ase gui `\ndirectly from the terminal with ``ase gui structure.xyz``\ngives an interactive view, which might be preferred.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\n\nfrom ase.visualize.plot import plot_atoms\n\nfig, ax = plt.subplots()\nplot_atoms(atoms, ax, rotation=('-80x,0y,0z'))\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note that we added the ``rotation`` argument,\nso that we can get a side view of the cell.\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Repeating a structure\n\nWithin the viewer (called :mod:`ase gui `) it is possible to repeat\nthe unit cell in all three directions\n(using the :menuselection:`Repeat --> View` window).\nFrom the command line, use ``ase gui -r 3,3,2 slab.xyz``.\n\nAlternatively, you can also do this directly in ase with the\nrepeat function of the Atoms object.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "atoms_repeated = atoms.repeat((3, 3, 2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This gives a repeated atoms object. We visualize it here\nagain in matplotlib.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\nplot_atoms(atoms_repeated, ax, rotation=('-80x,0y,0z'))\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Adding atoms\n\nWe now add an adatom in a three-fold site at a height of ``h=1.9`` \u00c5:\nTo generate the new positions of the adatom, we are using numpy.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n\nh = 1.9\nrelative = (1 / 6, 1 / 6, 0.5)\nabsolute = np.dot(relative, atoms.cell) + (0, 0, h)\natoms.append('Ag')\natoms.positions[-1] = absolute" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The structure now looks like this:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\nplot_atoms(atoms, ax, rotation=('-80x,0y,0z'))\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Interface building\n\nNow, we will make an interface with Ni(111) and water.\nFirst we need a layer of water. One layer of water is constructed in the\nfollowing\nscript and saved in the file ``water.traj``.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n\nfrom ase import Atoms\n\np = np.array(\n [\n [0.27802511, -0.07732213, 13.46649107],\n [0.91833251, -1.02565868, 13.41456626],\n [0.91865997, 0.87076761, 13.41228287],\n [1.85572027, 2.37336781, 13.56440907],\n [3.13987926, 2.3633134, 13.4327577],\n [1.77566079, 2.37150862, 14.66528237],\n [4.52240322, 2.35264513, 13.37435864],\n [5.16892729, 1.40357034, 13.42661052],\n [5.15567324, 3.30068395, 13.4305779],\n [6.10183518, -0.0738656, 13.27945071],\n [7.3856151, -0.07438536, 13.40814585],\n [6.01881192, -0.08627583, 12.1789428],\n ]\n)\nc = np.array([[8.490373, 0.0, 0.0], [0.0, 4.901919, 0.0], [0.0, 0.0, 26.93236]])\nwater = Atoms('4(OH2)', positions=p, cell=c, pbc=[1, 1, 0])\nwater.write('water.traj')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "With the atoms object saved as trajectory file,\nwe can also read the atoms from this file.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.io import read\n\nwater = read('water.traj')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualization\nLets take a look at the structure. For this, you can use view to open the\nASE gui as show above. Here, we are using matplotlib again.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\nplot_atoms(water, ax)\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and let's look at the unit cell.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "print(water.cell)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Creating a Ni slab\nWe will need a Ni(111) slab which matches the water as closely as possible.\nA 2x4 orthogonal fcc111 supercell should be good enough.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.build import fcc111\n\nslab = fcc111('Ni', size=[2, 4, 3], a=3.55, orthogonal=True)\n\nfig, ax = plt.subplots()\nplot_atoms(slab, ax)\nax.set_axis_off()\n\nprint(slab.cell)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Manipulating a Structure\nLooking at the two unit cells, we can see that they match with around 2\npercent difference, if we rotate one of the cells 90 degrees in the plane.\nLet's rotate the cell:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "water.cell = [water.cell[1, 1], water.cell[0, 0], 0.0]\n\nfig, ax = plt.subplots()\nplot_atoms(water, ax)\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's also :meth:`~ase.Atoms.rotate` the molecules:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "water.rotate(90, 'z', center=(0, 0, 0))\n\nfig, ax = plt.subplots()\nplot_atoms(water, ax)\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now we can wrap the atoms into the cell\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "water.wrap()\nfig, ax = plt.subplots()\nplot_atoms(water, ax)\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The :meth:`~ase.Atoms.wrap` method only works if periodic boundary\nconditions are enabled. We have a 2 percent lattice mismatch between Ni(111)\nand the water, so we scale the water in the plane to match the cell of the\nslab.\nThe argument *scale_atoms=True* indicates that the atomic positions should be\nscaled with the unit cell. The default is *scale_atoms=False* indicating that\nthe cartesian coordinates remain the same when the cell is changed.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "water.set_cell(slab.cell, scale_atoms=True)\nzmin = water.positions[:, 2].min()\nzmax = slab.positions[:, 2].max()\nwater.positions += (0, 0, zmax - zmin + 1.5)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding one Structure to the Other\nFinally we add the water onto the slab:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "interface = slab + water\ninterface.center(vacuum=6, axis=2)\ninterface.write('NiH2O.traj')\n\nfig, ax = plt.subplots()\nplot_atoms(interface, ax)\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Adding two atoms objects will take the positions from both and the cell and\nboundary conditions from the first.\n\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "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.13.7" } }, "nbformat": 4, "nbformat_minor": 0 }