{ "cells": [ { "cell_type": "markdown", "id": "348db862", "metadata": {}, "source": [ "# External Methods \n", "\n", "This tutorial demonstrates how to perform an ORCA calculation via OPI with external wrapper scripts to allow the use of methods not natively implemented in ORCA. As an example, we will run an optimization of the phenylalanine–serine dipeptide with PM7. Therefore, the respective wrapper must be downloaded (available via [GitHub](https://github.com/faccts/orca-external-tools)) and MOPAC has to be installed (also available via [GitHub](https://github.com/openmopac/mopac)). Besides geometry optimization, also other ORCA functionalities like GOAT or a NEB-TS are available in combination with wrapper scripts.\n", "\n", ":::{note}\n", "Besides MOPAC and PM7, you can use any program and method of your liking as long as you have the respective program installed and the wrapper available. If you create or use a custom wrapper script, please consider contributing on [GitHub](https://github.com/faccts/orca-external-tools) to make it accessible for everyone.\n", ":::\n", "\n", "In this notebook we will:\n", "1. Import the required python dependencies.\n", "2. Define a working directory.\n", "3. Set up the input structure.\n", "4. Define the calculation settings.\n", "5. Perform the ORCA calculation.\n", "6. Check for proper termination.\n", "7. Process the results." ] }, { "cell_type": "markdown", "id": "8cc75855", "metadata": {}, "source": [ "## Step 1: Import Dependencies\n", "\n", "The first thing to do for using OPI is to import the modules needed. We additionally import also the modules for visualization like `py3Dmol`. For this, it might be necessary to install `py3Dmol` into your OPI `venv` (e.g., by activating the `.venv` and using `pip install py3Dmol`)." ] }, { "cell_type": "code", "execution_count": 1, "id": "20705d64", "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "import shutil\n", "\n", "# > OPI imports for performing ORCA calculations and reading output\n", "from opi.core import Calculator\n", "from opi.output.core import Output\n", "from opi.input.simple_keywords import Opt\n", "from opi.input.structures.structure import Structure\n", "from opi.input.blocks import BlockMethod\n", "from opi.input.simple_keywords import ExternalTools\n", "\n", "# > for visualization of molecules\n", "import py3Dmol" ] }, { "cell_type": "markdown", "id": "48122235", "metadata": {}, "source": [ "## Step 2: Working Directory\n", "\n", "When working with OPI, it is always a good idea to define a subdirectory in which the actual ORCA calculations will take place. In this case, we set up a `RUN` directory:" ] }, { "cell_type": "code", "execution_count": 2, "id": "d2a07d41", "metadata": {}, "outputs": [], "source": [ "# > Calculation is performed in `RUN`\n", "working_dir = Path(\"RUN\")\n", "# > The `working_dir`is automatically (re-)created\n", "shutil.rmtree(working_dir, ignore_errors=True)\n", "working_dir.mkdir()" ] }, { "cell_type": "markdown", "id": "cd363d23", "metadata": {}, "source": [ "## Step 3: Setup the Input Structure\n", "\n", "We define the structure with Cartesian coordinates in angstrom and visualize it:\n" ] }, { "cell_type": "code", "execution_count": 3, "id": "11f3f621", "metadata": {}, "outputs": [ { "data": { "application/3dmoljs_load.v0": "
\n

3Dmol.js failed to load for some reason. Please check your browser console for error messages.

\n
\n", "text/html": [ "
\n", "

3Dmol.js failed to load for some reason. Please check your browser console for error messages.

\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# > Define cartesian coordinates in angstrom as python string \n", "xyz_data = \"\"\"\\\n", "43\n", "\n", " H -4.66498485894935 -0.74241492470540 0.69974381312263\n", " C -4.17671963772691 -0.41946813411513 1.62085663387098\n", " H -4.31556295019038 0.65835557933743 1.74617763893195\n", " H -4.64735205939740 -0.91420097769670 2.47504134616210\n", " C -2.69928997074851 -0.71449720892044 1.64944528147625\n", " O -1.98975107774701 -0.42928398115826 2.62017129759124\n", " N -2.17773611732287 -1.30327445426666 0.54436388078954\n", " H -2.78004021432631 -1.52785357956148 -0.23421300168168\n", " C -0.76472603182702 -1.64082193548350 0.46301524117174\n", " H -0.48934972945319 -2.31114326509938 1.28555265397361\n", " C -0.49680188103055 -2.34091857378744 -0.88594861326624\n", " H -1.12591070939924 -3.23791564349719 -0.92078590384981\n", " H -0.81348050791786 -1.67401763664883 -1.69607155624943\n", " C 0.95062385323134 -2.71378129189350 -1.06213996849627\n", " C 1.47509728895600 -3.83883958097587 -0.41893480660121\n", " C 1.80206493224889 -1.92339666967511 -1.83795922104143\n", " C 2.82283098211029 -4.16358701199868 -0.54351664024624\n", " C 3.15157817659875 -2.24582496944767 -1.96455890072444\n", " C 3.66615600696023 -3.36582314961534 -1.31551415939035\n", " H 0.82015336645497 -4.46361266116421 0.18316223338911\n", " H 1.40383614174953 -1.04827263842458 -2.34534914498881\n", " H 3.21532334050428 -5.04205196812944 -0.04017998407723\n", " H 3.80053282584475 -1.62233049211834 -2.57203001397934\n", " H 4.71717343897748 -3.61922096939729 -1.41415620606549\n", " C 0.13897619924998 -0.40963929818479 0.62323338935093\n", " O 1.18982465311154 -0.47050762380759 1.27051960118465\n", " N -0.26042356408273 0.69305102705598 -0.03880513029755\n", " H -1.17884625655101 0.71224550095703 -0.46453347384447\n", " C 0.46632507370965 1.95671966072207 0.00061241530592\n", " H 1.53922104685905 1.73383868295454 0.04807577903327\n", " C 0.17476690677966 2.75108272784102 -1.26783749196997\n", " H 0.72597721347984 3.69989315677944 -1.22599656255061\n", " H 0.52311351799454 2.18298120102531 -2.13543127758411\n", " O -1.22607123349587 2.96938238237637 -1.44869581637241\n", " H -1.49646216555062 3.50039758318850 -0.68132009170136\n", " C 0.06279389695701 2.75700018989079 1.25788607468049\n", " O -0.64234099967927 3.77463473172127 1.18777548552859\n", " N 0.51212557634628 2.25231891234389 2.41467071805516\n", " H 1.04291833952237 1.39006469799826 2.38265045795352\n", " C 0.12919661680901 2.81402640326745 3.70411596033363\n", " H 0.45784513235661 3.85472478200394 3.78674503246121\n", " H -0.95777954492060 2.78312669629151 3.83756598938075\n", " H 0.60317498350469 2.22685472401805 4.49159704123113\\n\n", "\"\"\"\n", "# > Visualize the input structure\n", "view = py3Dmol.view(width=400, height=400)\n", "view.addModel(xyz_data, 'xyz')\n", "view.setStyle({}, {'stick': {}, 'sphere': {'scale': 0.3}})\n", "view.zoomTo()\n", "view.show()\n", "\n", "# > Write the input structure to a file\n", "with open(working_dir / \"struc.xyz\",\"w\") as f:\n", " f.write(xyz_data)\n", "# > Read structure into object\n", "structure = Structure.from_xyz(working_dir / \"struc.xyz\")" ] }, { "cell_type": "markdown", "id": "9d129e7d", "metadata": {}, "source": [ "## Step 4: Define the Calculation Settings\n", "Next, we have to define the settings used in the ORCA optimization. This can be done by defining a calculator and adding simple keywords to it:" ] }, { "cell_type": "code", "execution_count": null, "id": "bd8a7d2e", "metadata": {}, "outputs": [], "source": [ "def setup_calc(basename : str, working_dir: Path, structure: Structure, ncores: int = 1) -> Calculator:\n", " # > Set up a Calculator object, the basename for the ORCA calculation is also set\n", " calc = Calculator(basename=basename, working_dir=working_dir)\n", " # > Assign structure to calculator\n", " calc.structure = structure\n", "\n", " # > List of simple keywords for ORCA\n", " sk_list = [\n", " ExternalTools.EXTOPT, # Set the extopt keyword for the ORCA input\n", " Opt.OPT # > Perform an optimization\n", " ]\n", "\n", " # > Use simple keywords in calculator\n", " calc.input.add_simple_keywords(*sk_list)\n", "\n", " # > Set the path to the wrapper script that should be used\n", " # > Without adjusting the path this notebook will not work!\n", " path_to_wrapper = \"/home/full/path/to/orca-external-tools/mopac.sh\"\n", " # Additional arguments for the wrapper script\n", " arguments = '\"--method PM7\"'\n", " \n", " # Add the block list to calculator\n", " calc.input.add_blocks(BlockMethod(ProgExt=path_to_wrapper,Ext_Params=arguments))\n", "\n", " # > Define number of CPUs for the calcualtion\n", " calc.input.ncores = ncores # > CPUs for this ORCA run (default: 1)\n", "\n", " return calc\n", "\n", "basename = \"pm7_opt\"\n", "calc = setup_calc(basename=basename, working_dir=working_dir, structure=structure)" ] }, { "cell_type": "markdown", "id": "0e186b08", "metadata": {}, "source": [ ":::{note}\n", "When defining `Ext_Params`, both types of quotes are required in a format like ('\"arguments\"').\n", ":::" ] }, { "cell_type": "markdown", "id": "ff9dc881", "metadata": {}, "source": [ "## Step 5: Perform the Calculation\n", "\n", "To perform the single-point calculation, we have to write the input in ORCA format first, and then run the job by using the `run()` function of the calculator class. The output of ORCA can be written to the calculator with its `get_output()` function." ] }, { "cell_type": "code", "execution_count": 5, "id": "a6cb12c7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Running ORCA calculation ... Done\n" ] } ], "source": [ "def run_calc(calc: Calculator) -> Output:\n", " # > Write the ORCA input file\n", " calc.write_input()\n", " # > Run the ORCA calculation\n", " print(\"Running ORCA calculation ...\", end=\"\")\n", " calc.run()\n", " print(\" Done\")\n", "\n", " # > Get the output object\n", " output = calc.get_output()\n", " \n", " return output\n", "\n", "output = run_calc(calc)" ] }, { "cell_type": "markdown", "id": "ba053236", "metadata": {}, "source": [ "## Step 6: Check for Proper Termination\n", "When performing automated computations, a check for proper termination should always be included. A simple check could look like:" ] }, { "cell_type": "code", "execution_count": 6, "id": "6002a6a6", "metadata": {}, "outputs": [], "source": [ "def check_and_parse_output(output: Output):\n", " # > Check for proper termination of ORCA\n", " status = output.terminated_normally()\n", " if not status:\n", " # > ORCA did not terminate normally\n", " raise RuntimeError(f\"ORCA did not terminate normally. Please check RUN/{basename}.out\")\n", " else:\n", " # > ORCA did terminate normally so we can parse the output\n", " # > We must switch off the processing of the gbw output file\n", " # as a gbw file is not written for extopt calculations\n", " output.parse(read_gbw_json=False)\n", " \n", "check_and_parse_output(output)" ] }, { "cell_type": "markdown", "id": "bbf32991", "metadata": {}, "source": [ "## Step 7: Access the Results\n", "After a successful calculation, we can access the resulting properties. They are generally available via the `output` object:" ] }, { "cell_type": "code", "execution_count": 7, "id": "b9368260", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Final energy in Hartree: -0.279731653465\n" ] }, { "data": { "application/3dmoljs_load.v0": "
\n

3Dmol.js failed to load for some reason. Please check your browser console for error messages.

\n
\n", "text/html": [ "
\n", "

3Dmol.js failed to load for some reason. Please check your browser console for error messages.

\n", "
\n", "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# > Get the PM7 energy of the final structure (last element in geometries)\n", "Etot = output.results_properties.geometries[-1].single_point_data.finalenergy\n", "\n", "# > Print total energy\n", "print(\"Final energy in Hartree: \",Etot)\n", "\n", "# > The final geometry after optimization is stored as the last element in geometries:\n", "coords = output.results_properties.geometries[-1].geometry.coordinates.cartesians\n", "# > Convert the coordinates from bohr to angstrom\n", "bohr_to_angstrom = 0.529177\n", "coords = [\n", " (atom, x * bohr_to_angstrom, y * bohr_to_angstrom, z * bohr_to_angstrom)\n", " for atom, x, y, z in coords\n", "]\n", "# > Make a simple string to be processed by py3Dmol\n", "xyz_str = f\"{len(coords)}\\n\\n\"\n", "for atom, x, y, z in coords:\n", " xyz_str += f\"{atom} {x:.6f} {y:.6f} {z:.6f}\\n\"\n", " \n", "# Visualize with py3Dmol\n", "view = py3Dmol.view(width=400, height=400)\n", "view.addModel(xyz_str, 'xyz')\n", "view.setStyle({}, {'stick': {}, 'sphere': {'scale': 0.3}})\n", "view.zoomTo()\n", "view.show()" ] }, { "cell_type": "markdown", "id": "f1791756", "metadata": {}, "source": [ "## Summary\n", "\n", "In this notebook we demonstrated how to perform an ORCA optimization with the ORCA Python Interface (OPI) and external methods via wrapper scripts." ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "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.12.3" } }, "nbformat": 4, "nbformat_minor": 5 }