{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "\n# ASE Introduction: Nitrogen on copper\n\nThis section gives a quick (and incomplete) overview of what ASE can do.\nYou can download the code shown in this tutorial (and others in the same style)\nas python scripts or jupyter notebooks at the bottom of this page.\n\nWe will calculate the adsorption energy of a nitrogen\nmolecule on a copper surface.\nThis is done by calculating the total\nenergy for the isolated slab and for the isolated molecule. The\nadsorbate is then added to the slab and relaxed, and the total energy\nfor this composite system is calculated. The adsorption energy is\nobtained as the sum of the isolated energies minus the energy of the\ncomposite system.\n\nYou will be able to see an image of the system after relaxation,\nlater in the \"Visualization\" section.\n\nPlease have a look at the following script:\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase import Atoms\nfrom ase.build import add_adsorbate, fcc111\nfrom ase.calculators.emt import EMT\nfrom ase.constraints import FixAtoms\nfrom ase.optimize import QuasiNewton\n\nh = 1.85\nd = 1.10\n\nslab = fcc111('Cu', size=(4, 4, 2), vacuum=10.0)\n\nslab.calc = EMT()\ne_slab = slab.get_potential_energy()\n\nmolecule = Atoms('2N', positions=[(0.0, 0.0, 0.0), (0.0, 0.0, d)])\nmolecule.calc = EMT()\ne_N2 = molecule.get_potential_energy()\n\nadd_adsorbate(slab, molecule, h, 'ontop')\nconstraint = FixAtoms(mask=[a.symbol != 'N' for a in slab])\nslab.set_constraint(constraint)\ndyn = QuasiNewton(slab, trajectory='N2Cu.traj')\ndyn.run(fmax=0.05)\n\nprint('Adsorption energy:', e_slab + e_N2 - slab.get_potential_energy())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Assuming you have ASE setup correctly (`download_and_install`)\nyou can copy it into a python file (e.g. N2Cu.py) and run the script::\n\n python N2Cu.py\n\nPlease read below what the script does.\n\n## Atoms\n\nThe :class:`~ase.Atoms` object is a collection of atoms. Here\nis how to define a N2 molecule by directly specifying the position of\ntwo nitrogen atoms\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "d = 1.10\nmolecule = Atoms('2N', positions=[(0.0, 0.0, 0.0), (0.0, 0.0, d)])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also build crystals using, for example, the lattice module\nwhich returns :class:`~ase.Atoms` objects corresponding to\ncommon crystal structures. Let us make a Cu (111) surface\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.build import fcc111\n\nslab = fcc111('Cu', size=(4, 4, 2), vacuum=10.0)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding calculator\n\nIn this overview we use the effective medium theory (EMT) calculator,\nas it is very fast and hence useful for getting started.\n\nWe can attach a calculator to the previously created\n:class:`~ase.Atoms` objects\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.calculators.emt import EMT\n\nslab.calc = EMT()\nmolecule.calc = EMT()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "and use it to calculate the total energies for the systems by using\nthe :meth:`~ase.Atoms.get_potential_energy` method from the\n:class:`~ase.Atoms` class\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "e_slab = slab.get_potential_energy()\ne_N2 = molecule.get_potential_energy()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Structure relaxation\n\nLet's use the :class:`~ase.optimize.QuasiNewton` minimizer to optimize the\nstructure of the N2 molecule adsorbed on the Cu surface. First add the\nadsorbate to the Cu slab, for example in the on-top position\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "h = 1.85\nadd_adsorbate(slab, molecule, h, 'ontop')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In order to speed up the relaxation, let us keep the Cu atoms fixed in\nthe slab by using :class:`~ase.constraints.FixAtoms` from the\n:mod:`~ase.constraints` module. Only the N2 molecule is then allowed\nto relax to the equilibrium structure\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.constraints import FixAtoms\n\nconstraint = FixAtoms(mask=[a.symbol != 'N' for a in slab])\nslab.set_constraint(constraint)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now attach the :class:`~ase.optimize.QuasiNewton` minimizer to the\nsystem and save the trajectory file. Run the minimizer with the\nconvergence criteria that the force on all atoms should be less than\nsome ``fmax``\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.optimize import QuasiNewton\n\ndyn = QuasiNewton(slab, trajectory='N2Cu.traj')\ndyn.run(fmax=0.05)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

Note

The general documentation on\n `structure optimizations ` contains\n information about different algorithms, saving the state of an optimizer\n and other functionality which should be considered when performing\n expensive relaxations.

\n\n## Input-output\n\nWriting the atomic positions to a file is done with the\n:func:`~ase.io.write` function\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.io import write\n\nwrite('slab.xyz', slab)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will write a file in the xyz-format. Possible formats are:\n\n======== ===========================\nformat description\n======== ===========================\n``xyz`` Simple xyz-format\n``cube`` Gaussian cube file\n``pdb`` Protein data bank file\n``traj`` ASE's own trajectory format\n``py`` Python script\n======== ===========================\n\nReading from a file is done like this\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.io import read\n\nslab_from_file = read('slab.xyz')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If the file contains several configurations, the default behavior of\nthe :func:`~ase.io.write` function is to return the last\nconfiguration. However, we can load a specific configuration by\ndoing\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "read('N2Cu.traj') # last configuration\nread('N2Cu.traj', -1) # same as above\nread('N2Cu.traj', 0) # first configuration" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualization\n\nThe simplest way to visualize the atoms is the :func:`~ase.visualize.view`\nfunction\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase.visualize import view\n\nview(slab)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This will pop up a :mod:`ase.gui` window. Alternative viewers can be used\nby specifying the optional keyword ``viewer=...`` - use one of\n'ase.gui', 'gopenmol', 'vmd', or 'rasmol'. (Note that these alternative\nviewers are not a part of ASE and will need to be installed by the user\nseparately.) The VMD viewer can take an optional ``data`` argument to\nshow 3D data\n\n\n```python\nview(slab, viewer='VMD', data=array)\n```\nIf you do not want a gui to open and plot this directly, you can do\nthis with plot_atoms in Matplotlib\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(slab_from_file, ax)\nax.set_axis_off()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Molecular dynamics\n\nLet us look at the nitrogen molecule as an example of molecular\ndynamics with the :class:`VelocityVerlet `\nalgorithm. We first create the :class:`VelocityVerlet\n` object giving it the molecule and the time\nstep for the integration of Newton's law. We then perform the dynamics\nby calling its :meth:`~ase.md.verlet.VelocityVerlet.run` method and\ngiving it the number of steps to take:\n\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ase import units\nfrom ase.md.verlet import VelocityVerlet\n\ndyn = VelocityVerlet(molecule, timestep=1.0 * units.fs)\nfor i in range(10):\n pot = molecule.get_potential_energy()\n kin = molecule.get_kinetic_energy()\n print('%2d: %.5f eV, %.5f eV, %.5f eV' % (i, pot + kin, pot, kin))\n dyn.run(steps=20)" ] } ], "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 }