diff --git a/README.md b/README.md
index 58c482a8..6580420c 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@ Below, we list the contents of this repository, in roughly the order that a user
| Folder | Notebook | Description |
| ------------------|-----------------------------------------| ----------- |
+
| [Examples/System](examples/system/) | [Get Authentication Params](examples/system/get_authentication_params.ipynb) | Demonstrates how to programatically find your user ID and access token, which is to [authenticate](https://docs.mat3ra.com/rest-api/authentication/) for many portions of the Mat3ra API.
| [Examples/Workflow](examples/workflow/) | [Get Workflows](examples/workflow/get_workflows.ipynb) | Walks through how to [query](https://docs.mat3ra.com/rest-api/query-structure/) the Mat3ra API to programatically search for workflows. In this example, we search for workflows that calculate the total energy of a material.
| [Examples/Workflow](examples/workflow/) | [Quantum Espresso Workflow and Job](examples/workflow/qe_scf_calculation.ipynb) | Create Quantum Espresso workflow starting from QE input file; create and submit job; after the job is finished, download output file, and finally perform postprocessing analysis.
diff --git a/other/materials_designer/BN.json b/other/materials_designer/BN.json
new file mode 100644
index 00000000..133be860
--- /dev/null
+++ b/other/materials_designer/BN.json
@@ -0,0 +1 @@
+{"lattice":{"a":2.508995,"b":2.508995,"c":7.71,"alpha":90,"beta":90,"gamma":120,"units":{"length":"angstrom","angle":"degree"},"type":"HEX","vectors":{"a":[2.508995,0,0],"b":[-1.254498,2.172854,0],"c":[0,0,7.71],"alat":1,"units":"angstrom"}},"basis":{"elements":[{"id":0,"value":"B"},{"id":1,"value":"N"}],"coordinates":[{"id":0,"value":[0,0,0]},{"id":1,"value":[0.333333,0.666667,0]}],"units":"crystal","cell":[[2.50899515,0,1.5363164397618668e-16],[-1.2544975749999998,2.1728535378719482,1.5363164397618668e-16],[0,0,7.71]],"constraints":[]},"name":"h-BN (mp-984)","isNonPeriodic":false,"external":{"id":"mp-984","source":"materials project","doi":"10.17188/1281942","url":"https://next-gen.materialsproject.org/materials/mp-984/","origin":true}}
\ No newline at end of file
diff --git a/other/materials_designer/Gr.json b/other/materials_designer/Gr.json
new file mode 100644
index 00000000..7fa97ba9
--- /dev/null
+++ b/other/materials_designer/Gr.json
@@ -0,0 +1 @@
+{"lattice":{"a":2.467291,"b":2.467291,"c":20,"alpha":90,"beta":90,"gamma":120,"units":{"length":"angstrom","angle":"degree"},"type":"HEX","vectors":{"a":[2.467291,0,0],"b":[-1.233645,2.136737,0],"c":[0,0,20],"alat":1,"units":"angstrom"}},"basis":{"elements":[{"id":0,"value":"C"},{"id":1,"value":"C"}],"coordinates":[{"id":0,"value":[0,0,0]},{"id":1,"value":[0.333333,0.666667,0]}],"units":"crystal","cell":[[2.467291,0,1.5107800128575361e-16],[-1.2336454999999997,2.1367366845287115,1.5107800128575361e-16],[0,0,20]],"constraints":[]},"name":"Graphene (mp-1040425)","isNonPeriodic":false,"external":{"id":"mp-1040425","source":"materials project","doi":"10.17188/1405723","url":"https://next-gen.materialsproject.org/materials/mp-1040425/","origin":true},"isUpdated":true}
\ No newline at end of file
diff --git a/other/materials_designer/Ni.json b/other/materials_designer/Ni.json
new file mode 100644
index 00000000..ade14823
--- /dev/null
+++ b/other/materials_designer/Ni.json
@@ -0,0 +1 @@
+{"name":"Ni (mp-23)","basis":{"elements":[{"id":0,"value":"Ni"}],"coordinates":[{"id":0,"value":[0,0,0]}],"units":"crystal","cell":[[2.130422493309719,0,1.2300000000000002],[0.7101408311032398,2.008581589082206,1.2300000000000002],[0,0,2.46]],"constraints":[{"id":0,"value":[true,true,true]}]},"lattice":{"a":2.46,"b":2.46,"c":2.46,"alpha":60,"beta":60,"gamma":60,"units":{"length":"angstrom","angle":"degree"},"type":"FCC","vectors":{"a":[2.130422,0,1.23],"b":[0.7101408,2.008582,1.23],"c":[0,0,2.46],"alat":1,"units":"angstrom"}},"isNonPeriodic":false,"external":{"id":"mp-224","source":"materials project","doi":"10.17188/1199153","url":"https://next-gen.materialsproject.org/materials/mp-23/","origin":true},"isUpdated":true}
\ No newline at end of file
diff --git a/other/materials_designer/calculator_pickle.py b/other/materials_designer/calculator_pickle.py
new file mode 100644
index 00000000..36ab649d
--- /dev/null
+++ b/other/materials_designer/calculator_pickle.py
@@ -0,0 +1,15 @@
+import matgl
+from matgl.ext.ase import M3GNetCalculator
+from matgl.ext.ase import M3GNetCalculator
+
+import cloudpickle
+
+pot = matgl.load_model("M3GNet-MP-2021.2.8-PES")
+
+# Save the calculator to pickle for use in the Pyodide environment
+calculator = M3GNetCalculator(pot)
+print(calculator)
+
+with open("m3gnet_calculator_3.11_32.pkl", "wb") as f:
+ cloudpickle.dump(calculator, f, protocol=3)
+
\ No newline at end of file
diff --git a/other/materials_designer/create_interface_with_relaxation_alignn.ipynb b/other/materials_designer/create_interface_with_relaxation_alignn.ipynb
new file mode 100644
index 00000000..a3d68efa
--- /dev/null
+++ b/other/materials_designer/create_interface_with_relaxation_alignn.ipynb
@@ -0,0 +1,2165 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Create an interface between two materials with minimal strain\n",
+ "\n",
+ "Use Zur and McGill superlattices matching [algorithm](https://doi.org/10.1063/1.3330840) to create interfaces between two materials using the Pymatgen [implementation](https://pymatgen.org/pymatgen.analysis.interfaces.html#pymatgen.analysis.interfaces.zsl).\n",
+ "\n",
+ "
Usage
\n",
+ "\n",
+ "0. Make sure to select Input Materials\n",
+ "1. Execute \"Run first: ...\" cell below to load Input Materials into the current kernel\n",
+ "2. Set Input Parameters (e.g. `MILLER_INDICES`, `THICKNESS`, `MAX_AREA`) below or use the default values\n",
+ "3. Click \"Run\" > \"Run All\" to run all cells\n",
+ "4. Wait for the run to complete (depending on the area, it can take 1-2 min or more). Scroll down to view cell results.\n",
+ "5. Review the strain plot and modify its parameters as needed\n",
+ "\n",
+ "## Methodology\n",
+ "\n",
+ "The following happens in the script below:\n",
+ "\n",
+ "1. Create slabs for each input material. The materials data is passed in from and back to the web application according to this description (TBA).\n",
+ " We assume that two input materials are either in bulk form (e.g. Ni crystal) or layered (e.g. graphene). \n",
+ " \n",
+ " We construct the interface along the Z-axis. The material corresponding to the bottom of the interface is referred to as the \"**substrate**\", and the top - as the \"**layer**\". \n",
+ "\n",
+ "2. Perform strain matching on the slabs to extract the supercell dimensions. The algorithm has a set of parameters, such as the maximum area considered, that can be configured by editing the cells below.\n",
+ "\n",
+ "3. When the strain matching is finished, the interface with the lowest strain (and the smallest number of atoms) is selected. We create the corresponding supercells and place them at a specified distance from each other (note no shift is performed currently).\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Run first: load input materials in current kernel
\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:02.172653Z",
+ "start_time": "2024-02-23T06:54:02.117478Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import json\n",
+ "materials_in = []\n",
+ "for file in [\"Ni.json\", \"Gr.json\"]:\n",
+ " with open(file, \"r\") as f:\n",
+ " data = f.read()\n",
+ " materials_in.append(json.loads(data))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1. Set Input Parameters\n",
+ "\n",
+ "### 1.1. Select Substrate and Layer from Input Materials"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:02.183063Z",
+ "start_time": "2024-02-23T06:54:02.177507Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "SUBSTRATE_PARAMETERS = {\n",
+ " \"MATERIAL_INDEX\": 0, # the index of the material in the materials_in list\n",
+ " \"MILLER_INDICES\": (1, 1, 1), # the miller indices of the interfacial plane\n",
+ " \"THICKNESS\": 3, # in layers\n",
+ "}\n",
+ "\n",
+ "LAYER_PARAMETERS = {\n",
+ " \"MATERIAL_INDEX\": 1, # the index of the material in the materials_in list\n",
+ " \"MILLER_INDICES\": (0, 0, 1), # the miller indices of the interfacial plane\n",
+ " \"THICKNESS\": 1, # in layers\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1.2. Set Interface Parameters\n",
+ "\n",
+ "The distance between layer and substrate and maximum area to consider when matching.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:02.194678Z",
+ "start_time": "2024-02-23T06:54:02.185489Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "INTERFACE_PARAMETERS = {\n",
+ " \"DISTANCE_Z\": 3.0, # in Angstroms\n",
+ " \"MAX_AREA\": 50, # in Angstroms^2\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1.3. Set Algorithm Parameters"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 20,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:02.195298Z",
+ "start_time": "2024-02-23T06:54:02.190351Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "ZSL_PARAMETERS = {\n",
+ " \"MAX_AREA\": INTERFACE_PARAMETERS[\"MAX_AREA\"], # The area to consider in Angstrom^2\n",
+ " \"MAX_AREA_TOL\": 0.09, # The area within this tolerance is considered equal\n",
+ " \"MAX_LENGTH_TOL\": 0.03, # supercell lattice vectors lengths within this tolerance are considered equal\n",
+ " \"MAX_ANGLE_TOL\": 0.01, # supercell lattice angles within this tolerance are considered equal\n",
+ " \"STRAIN_TOL\": 10e-6, # strains within this tolerance are considered equal\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 2. Install Packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 21,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:04.004961Z",
+ "start_time": "2024-02-23T06:54:02.198579Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Python 3.11.7\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Package Version\n",
+ "-------------------------- -----------------\n",
+ "accelerate 0.27.2\n",
+ "aiohttp 3.9.3\n",
+ "aiosignal 1.3.1\n",
+ "alignn 2024.2.4\n",
+ "anyio 4.3.0\n",
+ "appnope 0.1.4\n",
+ "argon2-cffi 23.1.0\n",
+ "argon2-cffi-bindings 21.2.0\n",
+ "arrow 1.3.0\n",
+ "ase 3.22.1\n",
+ "asttokens 2.4.1\n",
+ "async-lru 2.0.4\n",
+ "attrs 23.2.0\n",
+ "Babel 2.14.0\n",
+ "beautifulsoup4 4.12.3\n",
+ "bleach 6.1.0\n",
+ "certifi 2024.2.2\n",
+ "cffi 1.16.0\n",
+ "charset-normalizer 3.3.2\n",
+ "click 8.1.7\n",
+ "colorama 0.4.6\n",
+ "comm 0.2.1\n",
+ "contourpy 1.2.0\n",
+ "cycler 0.12.1\n",
+ "debugpy 1.8.1\n",
+ "decorator 5.1.1\n",
+ "defusedxml 0.7.1\n",
+ "dgl 2.0.0\n",
+ "executing 2.0.1\n",
+ "fastjsonschema 2.19.1\n",
+ "filelock 3.13.1\n",
+ "flake8 7.0.0\n",
+ "fonttools 4.49.0\n",
+ "fqdn 1.5.1\n",
+ "frozenlist 1.4.1\n",
+ "fsspec 2024.2.0\n",
+ "future 1.0.0\n",
+ "ghp-import 2.1.0\n",
+ "h11 0.14.0\n",
+ "httpcore 1.0.4\n",
+ "httpx 0.27.0\n",
+ "huggingface-hub 0.20.3\n",
+ "idna 3.6\n",
+ "install 1.3.5\n",
+ "ipykernel 6.29.2\n",
+ "ipython 8.22.1\n",
+ "ipywidgets 8.1.2\n",
+ "isoduration 20.11.0\n",
+ "jarvis-tools 2023.12.12\n",
+ "jedi 0.19.1\n",
+ "Jinja2 3.1.3\n",
+ "joblib 1.3.2\n",
+ "json5 0.9.17\n",
+ "jsonpointer 2.4\n",
+ "jsonschema 4.21.1\n",
+ "jsonschema-specifications 2023.12.1\n",
+ "jupyter_client 8.6.0\n",
+ "jupyter_core 5.7.1\n",
+ "jupyter-events 0.9.0\n",
+ "jupyter-lsp 2.2.2\n",
+ "jupyter_server 2.12.5\n",
+ "jupyter_server_terminals 0.5.2\n",
+ "jupyterlab 4.1.2\n",
+ "jupyterlab_pygments 0.3.0\n",
+ "jupyterlab_server 2.25.3\n",
+ "jupyterlab_widgets 3.0.10\n",
+ "kiwisolver 1.4.5\n",
+ "latexcodec 2.0.1\n",
+ "lightning-utilities 0.10.1\n",
+ "Markdown 3.5.2\n",
+ "MarkupSafe 2.1.5\n",
+ "matgl 0.9.2\n",
+ "matplotlib 3.8.3\n",
+ "matplotlib-inline 0.1.6\n",
+ "mccabe 0.7.0\n",
+ "mergedeep 1.3.4\n",
+ "mistune 3.0.2\n",
+ "mkdocs 1.5.3\n",
+ "mkdocs-material 9.5.10\n",
+ "mkdocs-material-extensions 1.3.1\n",
+ "monty 2024.2.2\n",
+ "mpmath 1.3.0\n",
+ "multidict 6.0.5\n",
+ "nbclient 0.9.0\n",
+ "nbconvert 7.16.1\n",
+ "nbformat 5.9.2\n",
+ "nest-asyncio 1.6.0\n",
+ "networkx 3.2.1\n",
+ "nglview 3.1.1\n",
+ "notebook 7.1.0\n",
+ "notebook_shim 0.2.4\n",
+ "numpy 1.26.4\n",
+ "overrides 7.7.0\n",
+ "packaging 23.2\n",
+ "paginate 0.5.6\n",
+ "palettable 3.3.3\n",
+ "pandas 2.2.1\n",
+ "pandocfilters 1.5.1\n",
+ "parso 0.8.3\n",
+ "pathspec 0.12.1\n",
+ "pexpect 4.9.0\n",
+ "pillow 10.2.0\n",
+ "pip 23.3.1\n",
+ "platformdirs 4.2.0\n",
+ "plotly 5.19.0\n",
+ "prometheus_client 0.20.0\n",
+ "prompt-toolkit 3.0.43\n",
+ "psutil 5.9.8\n",
+ "ptyprocess 0.7.0\n",
+ "pure-eval 0.2.2\n",
+ "pybtex 0.24.0\n",
+ "pycodestyle 2.11.1\n",
+ "pycparser 2.21\n",
+ "pydantic 1.8.1\n",
+ "pydocstyle 6.3.0\n",
+ "pyflakes 3.2.0\n",
+ "Pygments 2.17.2\n",
+ "pymatgen 2024.2.8\n",
+ "pymdown-extensions 10.7\n",
+ "pyparsing 2.4.7\n",
+ "PyQt5 5.15.10\n",
+ "PyQt5-Qt5 5.15.12\n",
+ "PyQt5-sip 12.13.0\n",
+ "python-dateutil 2.8.2\n",
+ "python-json-logger 2.0.7\n",
+ "pytorch-ignite 0.5.0.dev20240223\n",
+ "pytorch-lightning 2.2.0.post0\n",
+ "pytz 2024.1\n",
+ "PyYAML 6.0.1\n",
+ "pyyaml_env_tag 0.1\n",
+ "pyzmq 25.1.2\n",
+ "referencing 0.33.0\n",
+ "regex 2023.12.25\n",
+ "requests 2.31.0\n",
+ "rfc3339-validator 0.1.4\n",
+ "rfc3986-validator 0.1.1\n",
+ "rpds-py 0.18.0\n",
+ "ruamel.yaml 0.18.6\n",
+ "ruamel.yaml.clib 0.2.8\n",
+ "safetensors 0.4.2\n",
+ "scikit-learn 1.4.1.post1\n",
+ "scipy 1.12.0\n",
+ "Send2Trash 1.8.2\n",
+ "setuptools 69.0.2\n",
+ "six 1.16.0\n",
+ "sniffio 1.3.0\n",
+ "snowballstemmer 2.2.0\n",
+ "soupsieve 2.5\n",
+ "spglib 2.0.2\n",
+ "stack-data 0.6.3\n",
+ "sympy 1.12\n",
+ "tabulate 0.9.0\n",
+ "tenacity 8.2.3\n",
+ "terminado 0.18.0\n",
+ "threadpoolctl 3.3.0\n",
+ "tinycss2 1.2.1\n",
+ "toolz 0.12.1\n",
+ "torch 2.0.0\n",
+ "torchdata 0.7.1\n",
+ "torchmetrics 1.3.1\n",
+ "tornado 6.4\n",
+ "tqdm 4.66.2\n",
+ "traitlets 5.14.1\n",
+ "types-python-dateutil 2.8.19.20240106\n",
+ "typing_extensions 4.9.0\n",
+ "tzdata 2024.1\n",
+ "uncertainties 3.1.7\n",
+ "uri-template 1.3.0\n",
+ "urllib3 2.2.1\n",
+ "watchdog 4.0.0\n",
+ "wcwidth 0.2.13\n",
+ "webcolors 1.13\n",
+ "webencodings 0.5.1\n",
+ "websocket-client 1.7.0\n",
+ "widgetsnbextension 4.0.10\n",
+ "xmltodict 0.13.0\n",
+ "yarl 1.9.4\n",
+ "\n",
+ "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip is available: \u001b[0m\u001b[31;49m23.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\u001b[0m\n",
+ "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n"
+ ]
+ }
+ ],
+ "source": [
+ "!python --version\n",
+ "!pip list\n",
+ "# !pip install pymatgen==2024.2.8 ase==3.22.1 nbformat==5.9.2 ipykernel==6.29.2 matgl==0.9.2 nglview==3.1.1 alignn==2024.2.4 pip install PyQt5==5.15.10"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 3. Create interfaces\n",
+ "\n",
+ "### 3.1. Extract Interfaces and Terminations\n",
+ "\n",
+ "Extract all possible layer/substrate supercell combinations within the maximum area including different terminations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:09.335794Z",
+ "start_time": "2024-02-23T06:54:04.013364Z"
+ },
+ "tags": [],
+ "trusted": true
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Full Formula (Ni1)\n",
+ "Reduced Formula: Ni\n",
+ "abc : 2.460000 2.460000 2.460000\n",
+ "angles: 60.000004 59.999994 60.000003\n",
+ "pbc : True True True\n",
+ "Sites (1)\n",
+ " # SP a b c\n",
+ "--- ---- --- --- ---\n",
+ " 0 Ni 0 0 0 \n",
+ "\n",
+ "Full Formula (C2)\n",
+ "Reduced Formula: C\n",
+ "abc : 2.467291 2.467291 20.000000\n",
+ "angles: 90.000000 90.000000 119.999986\n",
+ "pbc : True True True\n",
+ "Sites (2)\n",
+ " # SP a b c\n",
+ "--- ---- -------- -------- ---\n",
+ " 0 C 0 0 0\n",
+ " 1 C 0.333333 0.666667 0 \n",
+ "\n",
+ "Creating interfaces...\n"
+ ]
+ }
+ ],
+ "source": [
+ "from src.pymatgen_coherent_interface_builder import CoherentInterfaceBuilder, ZSLGenerator\n",
+ "from src.utils import to_pymatgen\n",
+ "\n",
+ "if \"materials_in\" in globals():\n",
+ " pymatgen_materials = [to_pymatgen(item) for item in materials_in]\n",
+ "for material in pymatgen_materials:\n",
+ " print(material, \"\\n\")\n",
+ "\n",
+ "\n",
+ "def create_interfaces(settings):\n",
+ " print(\"Creating interfaces...\")\n",
+ " zsl = ZSLGenerator(\n",
+ " max_area_ratio_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA_TOL\"],\n",
+ " max_area=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA\"],\n",
+ " max_length_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_LENGTH_TOL\"],\n",
+ " max_angle_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_ANGLE_TOL\"],\n",
+ " )\n",
+ "\n",
+ " cib = CoherentInterfaceBuilder(\n",
+ " substrate_structure=pymatgen_materials[settings[\"SUBSTRATE_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
+ " film_structure=pymatgen_materials[settings[\"LAYER_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
+ " substrate_miller=settings[\"SUBSTRATE_PARAMETERS\"][\"MILLER_INDICES\"],\n",
+ " film_miller=settings[\"LAYER_PARAMETERS\"][\"MILLER_INDICES\"],\n",
+ " zslgen=zsl,\n",
+ " strain_tol=settings[\"ZSL_PARAMETERS\"][\"STRAIN_TOL\"],\n",
+ " )\n",
+ "\n",
+ " # Find terminations\n",
+ " cib._find_terminations()\n",
+ " terminations = cib.terminations\n",
+ "\n",
+ " # Create interfaces for each termination\n",
+ " interfaces = {}\n",
+ " for termination in terminations:\n",
+ " interfaces[termination] = []\n",
+ " for interface in cib.get_interfaces(\n",
+ " termination,\n",
+ " gap=settings[\"INTERFACE_PARAMETERS\"][\"DISTANCE_Z\"],\n",
+ " film_thickness=settings[\"LAYER_PARAMETERS\"][\"THICKNESS\"],\n",
+ " substrate_thickness=settings[\"SUBSTRATE_PARAMETERS\"][\"THICKNESS\"],\n",
+ " in_layers=True,\n",
+ " ):\n",
+ " # Wrap atoms to unit cell\n",
+ " interface[\"interface\"].make_supercell((1,1,1), to_unit_cell=True)\n",
+ " interfaces[termination].append(interface)\n",
+ " return interfaces, terminations\n",
+ "\n",
+ "\n",
+ "interfaces, terminations = create_interfaces(\n",
+ " settings={\n",
+ " \"SUBSTRATE_PARAMETERS\": SUBSTRATE_PARAMETERS,\n",
+ " \"LAYER_PARAMETERS\": LAYER_PARAMETERS,\n",
+ " \"ZSL_PARAMETERS\": ZSL_PARAMETERS,\n",
+ " \"INTERFACE_PARAMETERS\": INTERFACE_PARAMETERS,\n",
+ " }\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 3.2. Print out the interfaces and terminations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 23,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:09.342541Z",
+ "start_time": "2024-02-23T06:54:09.337841Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Found 1 terminations\n",
+ "Found 233 interfaces for ('C_P6/mmm_2', 'Ni_R-3m_1') termination\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f'Found {len(terminations)} terminations')\n",
+ "for termination in terminations:\n",
+ " print(f\"Found {len(interfaces[termination])} interfaces for\", termination, \"termination\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 4. Sort interfaces by strain\n",
+ "\n",
+ "### 4.1. Sort all interfaces"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:09.349850Z",
+ "start_time": "2024-02-23T06:54:09.344766Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Could be \"strain\", \"von_mises_strain\", \"mean_abs_strain\"\n",
+ "strain_mode = \"mean_abs_strain\"\n",
+ "\n",
+ "# Sort interfaces by the specified strain mode and number of sites\n",
+ "def sort_interfaces(interfaces, terminations):\n",
+ " sorted_interfaces = {}\n",
+ " for termination in terminations:\n",
+ " sorted_interfaces[termination] = sorted(\n",
+ " interfaces[termination], key=lambda x: (x[strain_mode], x[\"interface\"].num_sites)\n",
+ " )\n",
+ " return sorted_interfaces\n",
+ "\n",
+ "\n",
+ "sorted_interfaces = sort_interfaces(interfaces, terminations)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 4.2. Print out interfaces with lowest strain for each termination"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:09.352241Z",
+ "start_time": "2024-02-23T06:54:09.349349Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Interface with lowest strain for termination ('C_P6/mmm_2', 'Ni_R-3m_1') (index 0):\n",
+ " strain: 0.06600000000000002 %\n",
+ " number of atoms: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "for termination in terminations:\n",
+ " print(f\"Interface with lowest strain for termination {termination} (index 0):\")\n",
+ " first_interface = interfaces[termination][0]\n",
+ " print(\" strain:\", first_interface[strain_mode] * 100, \"%\")\n",
+ " print(\" number of atoms:\", first_interface[\"interface\"].num_sites)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 5. Plot the results\n",
+ "\n",
+ "Plot the number of atoms vs strain. Adjust the parameters as needed.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:09.692367Z",
+ "start_time": "2024-02-23T06:54:09.362250Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.plotly.v1+json": {
+ "config": {
+ "plotlyServerURL": "https://plot.ly"
+ },
+ "data": [
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 0",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 0
Strain: 0.07%
Atoms: 5"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 5
+ ]
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 1-9",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 1-9
Strain: 0.07%
Atoms: 10"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 10
+ ]
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 10-19",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 10-19
Strain: 0.07%
Atoms: 15"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 15
+ ]
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 20-38",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 20-38
Strain: 0.07%
Atoms: 20"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 20
+ ]
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 39-56",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 39-56
Strain: 0.07%
Atoms: 25"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 25
+ ]
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 57-110",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 57-110
Strain: 0.07%
Atoms: 30"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 30
+ ]
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 111-132",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 111-132
Strain: 0.07%
Atoms: 35"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 35
+ ]
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 133-177",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 133-177
Strain: 0.07%
Atoms: 40"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 40
+ ]
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 178-232",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 178-232
Strain: 0.07%
Atoms: 45"
+ ],
+ "type": "scatter",
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 45
+ ]
+ }
+ ],
+ "layout": {
+ "height": 600,
+ "hovermode": "closest",
+ "legend": {
+ "title": {
+ "text": "Interfaces Index Range"
+ }
+ },
+ "template": {
+ "data": {
+ "bar": [
+ {
+ "error_x": {
+ "color": "#2a3f5f"
+ },
+ "error_y": {
+ "color": "#2a3f5f"
+ },
+ "marker": {
+ "line": {
+ "color": "#E5ECF6",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "bar"
+ }
+ ],
+ "barpolar": [
+ {
+ "marker": {
+ "line": {
+ "color": "#E5ECF6",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "barpolar"
+ }
+ ],
+ "carpet": [
+ {
+ "aaxis": {
+ "endlinecolor": "#2a3f5f",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "minorgridcolor": "white",
+ "startlinecolor": "#2a3f5f"
+ },
+ "baxis": {
+ "endlinecolor": "#2a3f5f",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "minorgridcolor": "white",
+ "startlinecolor": "#2a3f5f"
+ },
+ "type": "carpet"
+ }
+ ],
+ "choropleth": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "choropleth"
+ }
+ ],
+ "contour": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "contour"
+ }
+ ],
+ "contourcarpet": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "contourcarpet"
+ }
+ ],
+ "heatmap": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "heatmap"
+ }
+ ],
+ "heatmapgl": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "heatmapgl"
+ }
+ ],
+ "histogram": [
+ {
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "histogram"
+ }
+ ],
+ "histogram2d": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "histogram2d"
+ }
+ ],
+ "histogram2dcontour": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "histogram2dcontour"
+ }
+ ],
+ "mesh3d": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "type": "mesh3d"
+ }
+ ],
+ "parcoords": [
+ {
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "parcoords"
+ }
+ ],
+ "pie": [
+ {
+ "automargin": true,
+ "type": "pie"
+ }
+ ],
+ "scatter": [
+ {
+ "fillpattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ },
+ "type": "scatter"
+ }
+ ],
+ "scatter3d": [
+ {
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatter3d"
+ }
+ ],
+ "scattercarpet": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattercarpet"
+ }
+ ],
+ "scattergeo": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattergeo"
+ }
+ ],
+ "scattergl": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattergl"
+ }
+ ],
+ "scattermapbox": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scattermapbox"
+ }
+ ],
+ "scatterpolar": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterpolar"
+ }
+ ],
+ "scatterpolargl": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterpolargl"
+ }
+ ],
+ "scatterternary": [
+ {
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "type": "scatterternary"
+ }
+ ],
+ "surface": [
+ {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "type": "surface"
+ }
+ ],
+ "table": [
+ {
+ "cells": {
+ "fill": {
+ "color": "#EBF0F8"
+ },
+ "line": {
+ "color": "white"
+ }
+ },
+ "header": {
+ "fill": {
+ "color": "#C8D4E3"
+ },
+ "line": {
+ "color": "white"
+ }
+ },
+ "type": "table"
+ }
+ ]
+ },
+ "layout": {
+ "annotationdefaults": {
+ "arrowcolor": "#2a3f5f",
+ "arrowhead": 0,
+ "arrowwidth": 1
+ },
+ "autotypenumbers": "strict",
+ "coloraxis": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "colorscale": {
+ "diverging": [
+ [
+ 0,
+ "#8e0152"
+ ],
+ [
+ 0.1,
+ "#c51b7d"
+ ],
+ [
+ 0.2,
+ "#de77ae"
+ ],
+ [
+ 0.3,
+ "#f1b6da"
+ ],
+ [
+ 0.4,
+ "#fde0ef"
+ ],
+ [
+ 0.5,
+ "#f7f7f7"
+ ],
+ [
+ 0.6,
+ "#e6f5d0"
+ ],
+ [
+ 0.7,
+ "#b8e186"
+ ],
+ [
+ 0.8,
+ "#7fbc41"
+ ],
+ [
+ 0.9,
+ "#4d9221"
+ ],
+ [
+ 1,
+ "#276419"
+ ]
+ ],
+ "sequential": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ],
+ "sequentialminus": [
+ [
+ 0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1,
+ "#f0f921"
+ ]
+ ]
+ },
+ "colorway": [
+ "#636efa",
+ "#EF553B",
+ "#00cc96",
+ "#ab63fa",
+ "#FFA15A",
+ "#19d3f3",
+ "#FF6692",
+ "#B6E880",
+ "#FF97FF",
+ "#FECB52"
+ ],
+ "font": {
+ "color": "#2a3f5f"
+ },
+ "geo": {
+ "bgcolor": "white",
+ "lakecolor": "white",
+ "landcolor": "#E5ECF6",
+ "showlakes": true,
+ "showland": true,
+ "subunitcolor": "white"
+ },
+ "hoverlabel": {
+ "align": "left"
+ },
+ "hovermode": "closest",
+ "mapbox": {
+ "style": "light"
+ },
+ "paper_bgcolor": "white",
+ "plot_bgcolor": "#E5ECF6",
+ "polar": {
+ "angularaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "bgcolor": "#E5ECF6",
+ "radialaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ }
+ },
+ "scene": {
+ "xaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ },
+ "yaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ },
+ "zaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "gridwidth": 2,
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white"
+ }
+ },
+ "shapedefaults": {
+ "line": {
+ "color": "#2a3f5f"
+ }
+ },
+ "ternary": {
+ "aaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "baxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "bgcolor": "#E5ECF6",
+ "caxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ }
+ },
+ "title": {
+ "x": 0.05
+ },
+ "xaxis": {
+ "automargin": true,
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "white",
+ "zerolinewidth": 2
+ },
+ "yaxis": {
+ "automargin": true,
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "white",
+ "zerolinewidth": 2
+ }
+ }
+ },
+ "xaxis": {
+ "title": {
+ "text": "Strain (%)"
+ },
+ "type": "log"
+ },
+ "yaxis": {
+ "title": {
+ "text": "Number of atoms"
+ },
+ "type": "log"
+ }
+ }
+ }
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Termination 0: ('C_P6/mmm_2', 'Ni_R-3m_1')\n"
+ ]
+ }
+ ],
+ "source": [
+ "import plotly.graph_objs as go\n",
+ "from collections import defaultdict\n",
+ "\n",
+ "PLOT_SETTINGS = {\n",
+ " \"HEIGHT\": 600,\n",
+ " \"X_SCALE\": \"log\", # or linear\n",
+ " \"Y_SCALE\": \"log\", # or linear\n",
+ "}\n",
+ "\n",
+ "\n",
+ "def plot_strain_vs_atoms(sorted_interfaces, terminations, settings):\n",
+ " # Create a mapping from termination to its index\n",
+ " termination_to_index = {termination: i for i, termination in enumerate(terminations)}\n",
+ "\n",
+ " grouped_interfaces = defaultdict(list)\n",
+ " for termination, interfaces in sorted_interfaces.items():\n",
+ " for index, interface_data in enumerate(interfaces):\n",
+ " strain_percentage = interface_data[\"mean_abs_strain\"] * 100\n",
+ " num_sites = interface_data[\"interface\"].num_sites\n",
+ " key = (strain_percentage, num_sites)\n",
+ " grouped_interfaces[key].append((index, termination))\n",
+ "\n",
+ " data = []\n",
+ " for (strain, num_sites), indices_and_terminations in grouped_interfaces.items():\n",
+ " termination_indices = defaultdict(list)\n",
+ " for index, termination in indices_and_terminations:\n",
+ " termination_indices[termination].append(index)\n",
+ " all_indices = [index for indices in termination_indices.values() for index in indices]\n",
+ " index_range = f\"{min(all_indices)}-{max(all_indices)}\" if len(all_indices) > 1 else str(min(all_indices))\n",
+ "\n",
+ " hover_text = \"
-----
\".join(\n",
+ " f\"Termination: {termination}
Termination index: {termination_to_index[termination]}
Interfaces Index Range: {index_range}
Strain: {strain:.2f}%
Atoms: {num_sites}\"\n",
+ " for termination, indices in termination_indices.items()\n",
+ " )\n",
+ " trace = go.Scatter(\n",
+ " x=[strain],\n",
+ " y=[num_sites],\n",
+ " text=[hover_text],\n",
+ " mode=\"markers\",\n",
+ " hoverinfo=\"text\",\n",
+ " name=f\"Indices: {index_range}\",\n",
+ " )\n",
+ " data.append(trace)\n",
+ "\n",
+ " layout = go.Layout(\n",
+ " xaxis=dict(title=\"Strain (%)\", type=settings[\"X_SCALE\"]),\n",
+ " yaxis=dict(title=\"Number of atoms\", type=settings[\"Y_SCALE\"]),\n",
+ " hovermode=\"closest\",\n",
+ " height=settings[\"HEIGHT\"],\n",
+ " legend_title_text=\"Interfaces Index Range\",\n",
+ " )\n",
+ " fig = go.Figure(data=data, layout=layout)\n",
+ " fig.show()\n",
+ "\n",
+ "\n",
+ "\n",
+ "plot_strain_vs_atoms(sorted_interfaces, terminations, PLOT_SETTINGS)\n",
+ "\n",
+ "for i, termination in enumerate(terminations):\n",
+ " print(f\"Termination {i}:\", termination)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 6. Select the interface to relax\n",
+ "\n",
+ "### 6.1. Select the interface with the desired termination and strain\n",
+ "\n",
+ "The data in `sorted_interfaces` now contains an object with the following structure:\n",
+ "\n",
+ "```json\n",
+ "{\n",
+ " \"('C_P6/mmm_2', 'Si_R-3m_1')\": [\n",
+ " { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 0...},\n",
+ " { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 1...},\n",
+ " ...\n",
+ " ],\n",
+ " \"\": [\n",
+ " { ...interface for 'termination at index 1' at index 0...},\n",
+ " { ...interface for 'termination at index 1' at index 1...},\n",
+ " ...\n",
+ " ]\n",
+ "}\n",
+ "```\n",
+ "\n",
+ "Select the index for termination first, and for it - the index in the list of corresponding interfaces sorted by strain (index 0 has minimum strain)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:09.693236Z",
+ "start_time": "2024-02-23T06:54:09.688382Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "termination_index = 0\n",
+ "interface_index = 0\n",
+ "\n",
+ "termination = terminations[termination_index]\n",
+ "\n",
+ "interface = sorted_interfaces[termination][interface_index][\"interface\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 7. Apply relaxation\n",
+ "### 7.1. Apply relaxation to the selected interface"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:55:22.364557Z",
+ "start_time": "2024-02-23T06:55:11.082886Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "dir_path /Users/vb/code/mat3ra/api-examples/.venv-3.11/lib/python3.11/site-packages/alignn/ff/alignnff_wt10\n",
+ "model_path /Users/vb/code/mat3ra/api-examples/.venv-3.11/lib/python3.11/site-packages/alignn/ff/alignnff_wt10\n"
+ ]
+ },
+ {
+ "data": {
+ "application/vnd.jupyter.widget-view+json": {
+ "model_id": "ecd2719341094c3daf2bfc1fb837544a",
+ "version_major": 2,
+ "version_minor": 0
+ },
+ "text/plain": [
+ "FigureWidget({\n",
+ " 'data': [{'mode': 'lines+markers',\n",
+ " 'name': 'Energy',\n",
+ " 'type': 'scatter',\n",
+ " 'uid': 'c770d60f-b401-459d-adda-da6789b57e20',\n",
+ " 'x': [],\n",
+ " 'y': []}],\n",
+ " 'layout': {'template': '...',\n",
+ " 'title': {'text': 'Real-time Optimization Progress'},\n",
+ " 'xaxis': {'anchor': 'y', 'domain': [0.0, 1.0], 'title': {'text': 'Step'}},\n",
+ " 'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0], 'title': {'text': 'Energy (eV)'}}}\n",
+ "})"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " Step Time Energy fmax\n",
+ "BFGS: 0 09:15:13 -17.939891 0.1364\n",
+ "Step: 0, Energy: -17.9399 eV\n",
+ "BFGS: 1 09:15:14 -17.944651 0.1350\n",
+ "Step: 1, Energy: -17.9447 eV\n",
+ "BFGS: 2 09:15:14 -18.104429 0.0183\n",
+ "Step: 2, Energy: -18.1044 eV\n",
+ "BFGS: 3 09:15:14 -18.104575 0.0183\n",
+ "Step: 3, Energy: -18.1046 eV\n",
+ "BFGS: 4 09:15:15 -18.104699 0.0182\n",
+ "Step: 4, Energy: -18.1047 eV\n",
+ "BFGS: 5 09:15:15 -18.104762 0.0182\n",
+ "Step: 5, Energy: -18.1048 eV\n",
+ "BFGS: 6 09:15:15 -18.105327 0.0178\n",
+ "Step: 6, Energy: -18.1053 eV\n",
+ "Original structure:\n",
+ " Ni C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143268 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni C \n",
+ " 3 2\n",
+ "Cartesian\n",
+ " 1.2300003829518742 0.7101417146926911 0.0000000000000001\n",
+ " 0.0000010237800151 0.0000011496383034 2.0085815523361399\n",
+ " 2.4600007659037484 1.4202834293853821 4.0171631046722771\n",
+ " 0.0000000000000000 0.0000000000000000 7.0171631046722744\n",
+ " 1.2299984704321629 0.7101402381262073 7.0171631046722753\n",
+ "\n",
+ "\n",
+ "Relaxed structure:\n",
+ " Ni C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143268 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni C \n",
+ " 3 2\n",
+ "Cartesian\n",
+ " 1.2300027025647622 0.7101431378908720 0.1948863120965003\n",
+ " 0.0000122416025937 0.0000077319999213 1.8379527731385130\n",
+ " 2.4657495023575300 1.4236022556285031 4.0614048789710475\n",
+ " -0.0108452190571857 -0.0062635946350369 6.9907350485416675\n",
+ " 1.2221636434393375 0.7056186683932514 6.9790081204315761\n",
+ "\n",
+ "The final energy is -18.105 eV.\n"
+ ]
+ }
+ ],
+ "source": [
+ "import plotly.graph_objs as go\n",
+ "from IPython.display import display\n",
+ "from plotly.subplots import make_subplots\n",
+ "from src.utils import poscar_to_ase, ase_to_poscar, ase_to_pymatgen, pymatgen_to_ase\n",
+ "from ase.optimize import BFGS\n",
+ "from alignn.ff.ff import AlignnAtomwiseCalculator as Calculator, default_path\n",
+ "\n",
+ "model_path = default_path()\n",
+ "calc = Calculator(path=model_path)\n",
+ "\n",
+ "# relax the interface\n",
+ "ase_interface = poscar_to_ase(interface.to(fmt=\"poscar\"))\n",
+ "ase_interface.set_calculator(calc)\n",
+ "\n",
+ "dyn = BFGS(ase_interface)\n",
+ "\n",
+ "# Initialize empty lists to store steps and energies\n",
+ "steps = []\n",
+ "energies = []\n",
+ "\n",
+ "# Create a plotly figure widget\n",
+ "fig = make_subplots(rows=1, cols=1, specs=[[{\"type\": \"scatter\"}]])\n",
+ "scatter = go.Scatter(x=[], y=[], mode='lines+markers', name='Energy')\n",
+ "fig.add_trace(scatter)\n",
+ "fig.update_layout(title_text='Real-time Optimization Progress', xaxis_title='Step', yaxis_title='Energy (eV)')\n",
+ "\n",
+ "# Display figure widget\n",
+ "f = go.FigureWidget(fig)\n",
+ "display(f)\n",
+ "\n",
+ "# Define a callback function to update the plot at each step\n",
+ "def plotly_callback():\n",
+ " step = dyn.nsteps\n",
+ " energy = ase_interface.get_total_energy()\n",
+ "\n",
+ " # Add the new step and energy to the lists\n",
+ " steps.append(step)\n",
+ " energies.append(energy)\n",
+ "\n",
+ " print(f\"Step: {step}, Energy: {energy:.4f} eV\")\n",
+ "\n",
+ " # Update the figure with the new data\n",
+ " with f.batch_update():\n",
+ " f.data[0].x = steps\n",
+ " f.data[0].y = energies\n",
+ "\n",
+ "\n",
+ "\n",
+ "dyn.attach(plotly_callback, interval=1)\n",
+ "dyn.run(fmax=0.018)\n",
+ "\n",
+ "# extract results\n",
+ "ase_original_interface = pymatgen_to_ase(interface)\n",
+ "ase_final_interface = ase_interface\n",
+ "relaxed_energy = ase_interface.get_total_energy()\n",
+ "\n",
+ "# print out the final relaxed structure and energy\n",
+ "print('Original structure:\\n', ase_to_poscar(ase_original_interface))\n",
+ "print('\\nRelaxed structure:\\n', ase_to_poscar(ase_final_interface))\n",
+ "print(f\"The final energy is {float(relaxed_energy):.3f} eV.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 7.2. View structure before and after relaxation"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:16.486110Z",
+ "start_time": "2024-02-23T06:54:16.296274Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "\n",
+ "
\n",
+ "
Ni3C2 - original
\n",
+ "

\n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
Ni3C2 - relaxed
\n",
+ "

\n",
+ "
\n",
+ "
"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import base64\n",
+ "from ase.io import write\n",
+ "from ase.build import make_supercell\n",
+ "from IPython.display import HTML\n",
+ "import io\n",
+ "\n",
+ "def visualize_material_base64(material, title: str, rotation: str = '0x', number_of_repetitions: int = 3):\n",
+ " \"\"\"\n",
+ " Returns an HTML string with a Base64-encoded image for visualization,\n",
+ " including the name of the file, positioned horizontally.\n",
+ " \"\"\"\n",
+ " # Set the number of unit cell repetition for the structure\n",
+ " n = number_of_repetitions\n",
+ " material_repeat = make_supercell(material, [[n,0,0],[0,n,0],[0,0,1]])\n",
+ " text = f\"{material.symbols} - {title}\"\n",
+ "\n",
+ " # Write image to a buffer to display in HTML\n",
+ " buf = io.BytesIO()\n",
+ " write(buf, material_repeat, format='png', rotation=rotation)\n",
+ " buf.seek(0)\n",
+ " img_str = base64.b64encode(buf.read()).decode('utf-8')\n",
+ " html_str = f'''\n",
+ " \n",
+ "
{text}
\n",
+ "

\n",
+ "
\n",
+ " '''\n",
+ " return html_str\n",
+ "\n",
+ "html_original = visualize_material_base64(ase_original_interface, \"original\", \"-90x\")\n",
+ "html_relaxed = visualize_material_base64(ase_final_interface, \"relaxed\", \"-90x\")\n",
+ "\n",
+ "# Display the interfaces before and after relaxation\n",
+ "html_content = f'{html_original}{html_relaxed}
'\n",
+ "display(HTML(html_content))\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 7.3. Calculate energy energy using matgl M3GNet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:57:56.887702Z",
+ "start_time": "2024-02-23T06:57:56.631462Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "5.240839288055053\n",
+ "5\n",
+ "3\n",
+ "2\n",
+ "Relaxed interface energy: -18.1053 eV\n",
+ "Original Substrate energy: -2.6920 eV\n",
+ "Original Layer energy: -15.4182 eV\n",
+ "Original Delta: 0.0049 eV\n",
+ "Original Delta per area: 0.0009 eV/Ang^2\n",
+ "Relaxed Substrate energy: -2.6684 eV\n",
+ "Relaxed Layer energy: -15.4182 eV\n",
+ "Relaxed Delta: -0.0187 eV\n",
+ "Relaxed Delta per area: -0.0036 eV/Ang^2\n",
+ "Effective relaxed Delta per area: -0.9862 eV/Ang^2\n",
+ "Ni C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143268 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni C \n",
+ " 3 2\n",
+ "Cartesian\n",
+ " 1.2300027025647622 0.7101431378908720 0.1948863120965003\n",
+ " 0.0000122416025937 0.0000077319999213 1.8379527731385130\n",
+ " 2.4657495023575300 1.4236022556285031 4.0614048789710475\n",
+ " -0.0108452190571857 -0.0062635946350369 6.9907350485416675\n",
+ " 1.2221636434393375 0.7056186683932514 6.9790081204315761\n",
+ "\n",
+ "Ni \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143268 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni \n",
+ " 3\n",
+ "Cartesian\n",
+ " 1.2300027025647622 0.7101431378908720 0.1948863120965003\n",
+ " 0.0000122416025937 0.0000077319999213 1.8379527731385130\n",
+ " 2.4657495023575300 1.4236022556285031 4.0614048789710475\n",
+ "\n",
+ " C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143268 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " C \n",
+ " 2\n",
+ "Cartesian\n",
+ " -0.0108452190571857 -0.0062635946350369 6.9907350485416675\n",
+ " 1.2221636434393375 0.7056186683932514 6.9790081204315761\n",
+ "\n",
+ "Ni \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143268 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni \n",
+ " 3\n",
+ "Cartesian\n",
+ " 1.2300003829518742 0.7101417146926911 0.0000000000000001\n",
+ " 0.0000010237800151 0.0000011496383034 2.0085815523361399\n",
+ " 2.4600007659037484 1.4202834293853821 4.0171631046722771\n",
+ "\n",
+ " C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143268 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " C \n",
+ " 2\n",
+ "Cartesian\n",
+ " 0.0000000000000000 0.0000000000000000 7.0171631046722744\n",
+ " 1.2299984704321629 0.7101402381262073 7.0171631046722753\n",
+ "\n"
+ ]
+ }
+ ],
+ "source": [
+ "nickel_relaxed_interface = ase_final_interface.copy()\n",
+ "graphene_relaxed_interface = ase_final_interface.copy()\n",
+ "nickel_original_interface = ase_original_interface.copy()\n",
+ "graphene_original_interface = ase_original_interface.copy()\n",
+ "\n",
+ "del nickel_relaxed_interface[[atom.index for atom in nickel_relaxed_interface if atom.symbol != 'Ni']]\n",
+ "del graphene_relaxed_interface[[atom.index for atom in graphene_relaxed_interface if atom.symbol != 'C']]\n",
+ "del nickel_original_interface[[atom.index for atom in nickel_original_interface if atom.symbol != 'Ni']]\n",
+ "del graphene_original_interface[[atom.index for atom in graphene_original_interface if atom.symbol != 'C']]\n",
+ "\n",
+ "#set EMT calculator\n",
+ "nickel_relaxed_interface.set_calculator(calc)\n",
+ "graphene_relaxed_interface.set_calculator(calc)\n",
+ "nickel_original_interface.set_calculator(calc)\n",
+ "graphene_original_interface.set_calculator(calc)\n",
+ "\n",
+ "original_substrate_energy = nickel_original_interface.get_total_energy()\n",
+ "original_layer_energy = graphene_original_interface.get_total_energy()\n",
+ "relaxed_substrate_energy = nickel_relaxed_interface.get_total_energy()\n",
+ "relaxed_layer_energy = graphene_relaxed_interface.get_total_energy()\n",
+ "\n",
+ "delta_original = relaxed_energy - original_substrate_energy - original_layer_energy\n",
+ "delta_relaxed = relaxed_energy - relaxed_substrate_energy - relaxed_layer_energy\n",
+ "\n",
+ "# calculate area of interface\n",
+ "area = ase_original_interface.get_volume() / ase_original_interface.cell[2, 2]\n",
+ "print(area)\n",
+ "\n",
+ "effective_delta_relaxed = (relaxed_energy * ase_final_interface.get_global_number_of_atoms() -\n",
+ " nickel_relaxed_interface.get_global_number_of_atoms() * relaxed_substrate_energy -\n",
+ " graphene_relaxed_interface.get_global_number_of_atoms() * relaxed_layer_energy)/(2 * area * ase_final_interface.get_global_number_of_atoms())\n",
+ "print(ase_final_interface.get_global_number_of_atoms())\n",
+ "print(nickel_relaxed_interface.get_global_number_of_atoms())\n",
+ "print(graphene_relaxed_interface.get_global_number_of_atoms())\n",
+ "# print(f\"Original interface energy: {ase_original_interface.get_total_energy():.4f} eV\")\n",
+ "print(f\"Relaxed interface energy: {relaxed_energy:.4f} eV\")\n",
+ "print(f\"Original Substrate energy: {original_substrate_energy:.4f} eV\")\n",
+ "print(f\"Original Layer energy: {original_layer_energy:.4f} eV\")\n",
+ "print(f\"Original Delta: {delta_original:.4f} eV\")\n",
+ "print(f\"Original Delta per area: {delta_original / area:.4f} eV/Ang^2\")\n",
+ "print(f\"Relaxed Substrate energy: {relaxed_substrate_energy:.4f} eV\")\n",
+ "print(f\"Relaxed Layer energy: {relaxed_layer_energy:.4f} eV\")\n",
+ "print(f\"Relaxed Delta: {delta_relaxed:.4f} eV\")\n",
+ "print(f\"Relaxed Delta per area: {delta_relaxed / area:.4f} eV/Ang^2\")\n",
+ "print(f\"Effective relaxed Delta per area: {effective_delta_relaxed:.4f} eV/Ang^2\")\n",
+ "\n",
+ "\n",
+ "print(ase_to_poscar(ase_final_interface))\n",
+ "print(ase_to_poscar(nickel_relaxed_interface))\n",
+ "print(ase_to_poscar(graphene_relaxed_interface))\n",
+ "print(ase_to_poscar(nickel_original_interface))\n",
+ "print(ase_to_poscar(graphene_original_interface))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "start_time": "2024-02-23T06:54:17.113780Z"
+ }
+ },
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:54:17.124980Z",
+ "start_time": "2024-02-23T06:54:17.116350Z"
+ },
+ "collapsed": false
+ },
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {
+ "collapsed": false
+ },
+ "source": [
+ "## References\n",
+ "\n",
+ "[1] ASE alignn calculator relaxation example: https://github.com/usnistgov/alignn?tab=readme-ov-file#alignnff \n",
+ "[2] Plotly 3D molecular visualization: https://dash.plotly.com/dash-bio/molecule3dviewer"
+ ]
+ }
+ ],
+ "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.11.7"
+ },
+ "widgets": {
+ "application/vnd.jupyter.widget-state+json": {
+ "state": {},
+ "version_major": 2,
+ "version_minor": 0
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/other/materials_designer/create_interface_with_relaxation_ase_emt.ipynb b/other/materials_designer/create_interface_with_relaxation_ase_emt.ipynb
new file mode 100644
index 00000000..eb4dd729
--- /dev/null
+++ b/other/materials_designer/create_interface_with_relaxation_ase_emt.ipynb
@@ -0,0 +1,1906 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Create an interface between two materials with minimal strain\n",
+ "\n",
+ "Use Zur and McGill superlattices matching [algorithm](https://doi.org/10.1063/1.3330840) to create interfaces between two materials using the Pymatgen [implementation](https://pymatgen.org/pymatgen.analysis.interfaces.html#pymatgen.analysis.interfaces.zsl).\n",
+ "\n",
+ "Usage
\n",
+ "\n",
+ "0. Make sure to select Input Materials\n",
+ "1. Execute \"Run first: ...\" cell below to load Input Materials into the current kernel\n",
+ "2. Set Input Parameters (e.g. `MILLER_INDICES`, `THICKNESS`, `MAX_AREA`) below or use the default values\n",
+ "3. Click \"Run\" > \"Run All\" to run all cells\n",
+ "4. Wait for the run to complete (depending on the area, it can take 1-2 min or more). Scroll down to view cell results.\n",
+ "5. Review the strain plot and modify its parameters as needed\n",
+ "\n",
+ "## Methodology\n",
+ "\n",
+ "The following happens in the script below:\n",
+ "\n",
+ "1. Create slabs for each input material. The materials data is passed in from and back to the web application according to this description (TBA).\n",
+ " We assume that two input materials are either in bulk form (e.g. Ni crystal) or layered (e.g. graphene). \n",
+ " \n",
+ " We construct the interface along the Z-axis. The material corresponding to the bottom of the interface is referred to as the \"**substrate**\", and the top - as the \"**layer**\". \n",
+ "\n",
+ "2. Perform strain matching on the slabs to extract the supercell dimensions. The algorithm has a set of parameters, such as the maximum area considered, that can be configured by editing the cells below.\n",
+ "\n",
+ "3. When the strain matching is finished, the interface with the lowest strain (and the smallest number of atoms) is selected. We create the corresponding supercells and place them at a specified distance from each other (note no shift is performed currently).\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Run first: load input materials in current kernel
\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 24,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:32.139897Z",
+ "start_time": "2024-02-23T06:59:32.011540Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import json\n",
+ "materials_in = []\n",
+ "for file in [\"Ni.json\", \"Gr.json\"]:\n",
+ " with open(file, \"r\") as f:\n",
+ " data = f.read()\n",
+ " materials_in.append(json.loads(data))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1. Set Input Parameters\n",
+ "\n",
+ "### 1.1. Select Substrate and Layer from Input Materials"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:32.182902Z",
+ "start_time": "2024-02-23T06:59:32.143075Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "SUBSTRATE_PARAMETERS = {\n",
+ " \"MATERIAL_INDEX\": 0, # the index of the material in the materials_in list\n",
+ " \"MILLER_INDICES\": (1, 1, 1), # the miller indices of the interfacial plane\n",
+ " \"THICKNESS\": 3, # in layers\n",
+ "}\n",
+ "\n",
+ "LAYER_PARAMETERS = {\n",
+ " \"MATERIAL_INDEX\": 1, # the index of the material in the materials_in list\n",
+ " \"MILLER_INDICES\": (0, 0, 1), # the miller indices of the interfacial plane\n",
+ " \"THICKNESS\": 1, # in layers\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1.2. Set Interface Parameters\n",
+ "\n",
+ "The distance between layer and substrate and maximum area to consider when matching.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 26,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:32.260407Z",
+ "start_time": "2024-02-23T06:59:32.186142Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "INTERFACE_PARAMETERS = {\n",
+ " \"DISTANCE_Z\": 3.0, # in Angstroms\n",
+ " \"MAX_AREA\": 50, # in Angstroms^2\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1.3. Set Algorithm Parameters"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 27,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:32.325278Z",
+ "start_time": "2024-02-23T06:59:32.251721Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "ZSL_PARAMETERS = {\n",
+ " \"MAX_AREA\": INTERFACE_PARAMETERS[\"MAX_AREA\"], # The area to consider in Angstrom^2\n",
+ " \"MAX_AREA_TOL\": 0.09, # The area within this tolerance is considered equal\n",
+ " \"MAX_LENGTH_TOL\": 0.03, # supercell lattice vectors lengths within this tolerance are considered equal\n",
+ " \"MAX_ANGLE_TOL\": 0.01, # supercell lattice angles within this tolerance are considered equal\n",
+ " \"STRAIN_TOL\": 10e-6, # strains within this tolerance are considered equal\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 2. Install Packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 28,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:32.377580Z",
+ "start_time": "2024-02-23T06:59:32.318494Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# from jupyterlite.utils import install_packages\n",
+ "\n",
+ "# await install_packages(\"create_interface_with_min_strain_zsl.ipynb\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 3. Create interfaces\n",
+ "\n",
+ "### 3.1. Extract Interfaces and Terminations\n",
+ "\n",
+ "Extract all possible layer/substrate supercell combinations within the maximum area including different terminations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 29,
+ "metadata": {
+ "tags": [],
+ "trusted": true,
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.530093Z",
+ "start_time": "2024-02-23T06:59:32.371609Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Full Formula (Ni1)\n",
+ "Reduced Formula: Ni\n",
+ "abc : 2.460000 2.460000 2.460000\n",
+ "angles: 60.000004 59.999994 60.000003\n",
+ "pbc : True True True\n",
+ "Sites (1)\n",
+ " # SP a b c\n",
+ "--- ---- --- --- ---\n",
+ " 0 Ni 0 0 0 \n",
+ "\n",
+ "Full Formula (C2)\n",
+ "Reduced Formula: C\n",
+ "abc : 2.467291 2.467291 20.000000\n",
+ "angles: 90.000000 90.000000 119.999986\n",
+ "pbc : True True True\n",
+ "Sites (2)\n",
+ " # SP a b c\n",
+ "--- ---- -------- -------- ---\n",
+ " 0 C 0 0 0\n",
+ " 1 C 0.333333 0.666667 0 \n",
+ "\n",
+ "Creating interfaces...\n"
+ ]
+ }
+ ],
+ "source": [
+ "from src.pymatgen_coherent_interface_builder import CoherentInterfaceBuilder, ZSLGenerator\n",
+ "from src.utils import to_pymatgen\n",
+ "\n",
+ "if \"materials_in\" in globals():\n",
+ " pymatgen_materials = [to_pymatgen(item) for item in materials_in]\n",
+ "for material in pymatgen_materials:\n",
+ " print(material, \"\\n\")\n",
+ "\n",
+ "\n",
+ "def create_interfaces(settings):\n",
+ " print(\"Creating interfaces...\")\n",
+ " zsl = ZSLGenerator(\n",
+ " max_area_ratio_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA_TOL\"],\n",
+ " max_area=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA\"],\n",
+ " max_length_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_LENGTH_TOL\"],\n",
+ " max_angle_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_ANGLE_TOL\"],\n",
+ " )\n",
+ "\n",
+ " cib = CoherentInterfaceBuilder(\n",
+ " substrate_structure=pymatgen_materials[settings[\"SUBSTRATE_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
+ " film_structure=pymatgen_materials[settings[\"LAYER_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
+ " substrate_miller=settings[\"SUBSTRATE_PARAMETERS\"][\"MILLER_INDICES\"],\n",
+ " film_miller=settings[\"LAYER_PARAMETERS\"][\"MILLER_INDICES\"],\n",
+ " zslgen=zsl,\n",
+ " strain_tol=settings[\"ZSL_PARAMETERS\"][\"STRAIN_TOL\"],\n",
+ " )\n",
+ "\n",
+ " # Find terminations\n",
+ " cib._find_terminations()\n",
+ " terminations = cib.terminations\n",
+ "\n",
+ " # Create interfaces for each termination\n",
+ " interfaces = {}\n",
+ " for termination in terminations:\n",
+ " interfaces[termination] = []\n",
+ " for interface in cib.get_interfaces(\n",
+ " termination,\n",
+ " gap=settings[\"INTERFACE_PARAMETERS\"][\"DISTANCE_Z\"],\n",
+ " film_thickness=settings[\"LAYER_PARAMETERS\"][\"THICKNESS\"],\n",
+ " substrate_thickness=settings[\"SUBSTRATE_PARAMETERS\"][\"THICKNESS\"],\n",
+ " in_layers=True,\n",
+ " ):\n",
+ " # Wrap atoms to unit cell\n",
+ " interface[\"interface\"].make_supercell((1,1,1), to_unit_cell=True)\n",
+ " interfaces[termination].append(interface)\n",
+ " return interfaces, terminations\n",
+ "\n",
+ "\n",
+ "interfaces, terminations = create_interfaces(\n",
+ " settings={\n",
+ " \"SUBSTRATE_PARAMETERS\": SUBSTRATE_PARAMETERS,\n",
+ " \"LAYER_PARAMETERS\": LAYER_PARAMETERS,\n",
+ " \"ZSL_PARAMETERS\": ZSL_PARAMETERS,\n",
+ " \"INTERFACE_PARAMETERS\": INTERFACE_PARAMETERS,\n",
+ " }\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 3.2. Print out the interfaces and terminations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 30,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.539509Z",
+ "start_time": "2024-02-23T06:59:34.533076Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Found 1 terminations\n",
+ "Found 233 interfaces for ('C_P6/mmm_2', 'Ni_R-3m_1') termination\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f'Found {len(terminations)} terminations')\n",
+ "for termination in terminations:\n",
+ " print(f\"Found {len(interfaces[termination])} interfaces for\", termination, \"termination\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 4. Sort interfaces by strain\n",
+ "\n",
+ "### 4.1. Sort all interfaces"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 31,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.552722Z",
+ "start_time": "2024-02-23T06:59:34.549963Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Could be \"strain\", \"von_mises_strain\", \"mean_abs_strain\"\n",
+ "strain_mode = \"mean_abs_strain\"\n",
+ "\n",
+ "# Sort interfaces by the specified strain mode and number of sites\n",
+ "def sort_interfaces(interfaces, terminations):\n",
+ " sorted_interfaces = {}\n",
+ " for termination in terminations:\n",
+ " sorted_interfaces[termination] = sorted(\n",
+ " interfaces[termination], key=lambda x: (x[strain_mode], x[\"interface\"].num_sites)\n",
+ " )\n",
+ " return sorted_interfaces\n",
+ "\n",
+ "\n",
+ "sorted_interfaces = sort_interfaces(interfaces, terminations)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 4.2. Print out interfaces with lowest strain for each termination"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 32,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.603677Z",
+ "start_time": "2024-02-23T06:59:34.556662Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Interface with lowest strain for termination ('C_P6/mmm_2', 'Ni_R-3m_1') (index 0):\n",
+ " strain: 0.06600000000000002 %\n",
+ " number of atoms: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "for termination in terminations:\n",
+ " print(f\"Interface with lowest strain for termination {termination} (index 0):\")\n",
+ " first_interface = interfaces[termination][0]\n",
+ " print(\" strain:\", first_interface[strain_mode] * 100, \"%\")\n",
+ " print(\" number of atoms:\", first_interface[\"interface\"].num_sites)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 5. Plot the results\n",
+ "\n",
+ "Plot the number of atoms vs strain. Adjust the parameters as needed.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 33,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.605359Z",
+ "start_time": "2024-02-23T06:59:34.573908Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/vnd.plotly.v1+json": {
+ "data": [
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 0",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 0
Strain: 0.07%
Atoms: 5"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 5
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 1-9",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 1-9
Strain: 0.07%
Atoms: 10"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 10
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 10-19",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 10-19
Strain: 0.07%
Atoms: 15"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 15
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 20-38",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 20-38
Strain: 0.07%
Atoms: 20"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 20
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 39-56",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 39-56
Strain: 0.07%
Atoms: 25"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 25
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 57-110",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 57-110
Strain: 0.07%
Atoms: 30"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 30
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 111-132",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 111-132
Strain: 0.07%
Atoms: 35"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 35
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 133-177",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 133-177
Strain: 0.07%
Atoms: 40"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 40
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 178-232",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 178-232
Strain: 0.07%
Atoms: 45"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 45
+ ],
+ "type": "scatter"
+ }
+ ],
+ "layout": {
+ "height": 600,
+ "hovermode": "closest",
+ "legend": {
+ "title": {
+ "text": "Interfaces Index Range"
+ }
+ },
+ "xaxis": {
+ "title": {
+ "text": "Strain (%)"
+ },
+ "type": "log"
+ },
+ "yaxis": {
+ "title": {
+ "text": "Number of atoms"
+ },
+ "type": "log"
+ },
+ "template": {
+ "data": {
+ "histogram2dcontour": [
+ {
+ "type": "histogram2dcontour",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "choropleth": [
+ {
+ "type": "choropleth",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ ],
+ "histogram2d": [
+ {
+ "type": "histogram2d",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "heatmap": [
+ {
+ "type": "heatmap",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "heatmapgl": [
+ {
+ "type": "heatmapgl",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "contourcarpet": [
+ {
+ "type": "contourcarpet",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ ],
+ "contour": [
+ {
+ "type": "contour",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "surface": [
+ {
+ "type": "surface",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "mesh3d": [
+ {
+ "type": "mesh3d",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ ],
+ "scatter": [
+ {
+ "fillpattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ },
+ "type": "scatter"
+ }
+ ],
+ "parcoords": [
+ {
+ "type": "parcoords",
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scatterpolargl": [
+ {
+ "type": "scatterpolargl",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "bar": [
+ {
+ "error_x": {
+ "color": "#2a3f5f"
+ },
+ "error_y": {
+ "color": "#2a3f5f"
+ },
+ "marker": {
+ "line": {
+ "color": "#E5ECF6",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "bar"
+ }
+ ],
+ "scattergeo": [
+ {
+ "type": "scattergeo",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scatterpolar": [
+ {
+ "type": "scatterpolar",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "histogram": [
+ {
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "histogram"
+ }
+ ],
+ "scattergl": [
+ {
+ "type": "scattergl",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scatter3d": [
+ {
+ "type": "scatter3d",
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scattermapbox": [
+ {
+ "type": "scattermapbox",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scatterternary": [
+ {
+ "type": "scatterternary",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scattercarpet": [
+ {
+ "type": "scattercarpet",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "carpet": [
+ {
+ "aaxis": {
+ "endlinecolor": "#2a3f5f",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "minorgridcolor": "white",
+ "startlinecolor": "#2a3f5f"
+ },
+ "baxis": {
+ "endlinecolor": "#2a3f5f",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "minorgridcolor": "white",
+ "startlinecolor": "#2a3f5f"
+ },
+ "type": "carpet"
+ }
+ ],
+ "table": [
+ {
+ "cells": {
+ "fill": {
+ "color": "#EBF0F8"
+ },
+ "line": {
+ "color": "white"
+ }
+ },
+ "header": {
+ "fill": {
+ "color": "#C8D4E3"
+ },
+ "line": {
+ "color": "white"
+ }
+ },
+ "type": "table"
+ }
+ ],
+ "barpolar": [
+ {
+ "marker": {
+ "line": {
+ "color": "#E5ECF6",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "barpolar"
+ }
+ ],
+ "pie": [
+ {
+ "automargin": true,
+ "type": "pie"
+ }
+ ]
+ },
+ "layout": {
+ "autotypenumbers": "strict",
+ "colorway": [
+ "#636efa",
+ "#EF553B",
+ "#00cc96",
+ "#ab63fa",
+ "#FFA15A",
+ "#19d3f3",
+ "#FF6692",
+ "#B6E880",
+ "#FF97FF",
+ "#FECB52"
+ ],
+ "font": {
+ "color": "#2a3f5f"
+ },
+ "hovermode": "closest",
+ "hoverlabel": {
+ "align": "left"
+ },
+ "paper_bgcolor": "white",
+ "plot_bgcolor": "#E5ECF6",
+ "polar": {
+ "bgcolor": "#E5ECF6",
+ "angularaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "radialaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ }
+ },
+ "ternary": {
+ "bgcolor": "#E5ECF6",
+ "aaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "baxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ },
+ "caxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": ""
+ }
+ },
+ "coloraxis": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "colorscale": {
+ "sequential": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ],
+ "sequentialminus": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ],
+ "diverging": [
+ [
+ 0,
+ "#8e0152"
+ ],
+ [
+ 0.1,
+ "#c51b7d"
+ ],
+ [
+ 0.2,
+ "#de77ae"
+ ],
+ [
+ 0.3,
+ "#f1b6da"
+ ],
+ [
+ 0.4,
+ "#fde0ef"
+ ],
+ [
+ 0.5,
+ "#f7f7f7"
+ ],
+ [
+ 0.6,
+ "#e6f5d0"
+ ],
+ [
+ 0.7,
+ "#b8e186"
+ ],
+ [
+ 0.8,
+ "#7fbc41"
+ ],
+ [
+ 0.9,
+ "#4d9221"
+ ],
+ [
+ 1,
+ "#276419"
+ ]
+ ]
+ },
+ "xaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "white",
+ "automargin": true,
+ "zerolinewidth": 2
+ },
+ "yaxis": {
+ "gridcolor": "white",
+ "linecolor": "white",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "white",
+ "automargin": true,
+ "zerolinewidth": 2
+ },
+ "scene": {
+ "xaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white",
+ "gridwidth": 2
+ },
+ "yaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white",
+ "gridwidth": 2
+ },
+ "zaxis": {
+ "backgroundcolor": "#E5ECF6",
+ "gridcolor": "white",
+ "linecolor": "white",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "white",
+ "gridwidth": 2
+ }
+ },
+ "shapedefaults": {
+ "line": {
+ "color": "#2a3f5f"
+ }
+ },
+ "annotationdefaults": {
+ "arrowcolor": "#2a3f5f",
+ "arrowhead": 0,
+ "arrowwidth": 1
+ },
+ "geo": {
+ "bgcolor": "white",
+ "landcolor": "#E5ECF6",
+ "subunitcolor": "white",
+ "showland": true,
+ "showlakes": true,
+ "lakecolor": "white"
+ },
+ "title": {
+ "x": 0.05
+ },
+ "mapbox": {
+ "style": "light"
+ }
+ }
+ }
+ },
+ "config": {
+ "plotlyServerURL": "https://plot.ly"
+ }
+ },
+ "text/html": ""
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Termination 0: ('C_P6/mmm_2', 'Ni_R-3m_1')\n"
+ ]
+ }
+ ],
+ "source": [
+ "import plotly.graph_objs as go\n",
+ "from collections import defaultdict\n",
+ "\n",
+ "PLOT_SETTINGS = {\n",
+ " \"HEIGHT\": 600,\n",
+ " \"X_SCALE\": \"log\", # or linear\n",
+ " \"Y_SCALE\": \"log\", # or linear\n",
+ "}\n",
+ "\n",
+ "\n",
+ "def plot_strain_vs_atoms(sorted_interfaces, terminations, settings):\n",
+ " # Create a mapping from termination to its index\n",
+ " termination_to_index = {termination: i for i, termination in enumerate(terminations)}\n",
+ "\n",
+ " grouped_interfaces = defaultdict(list)\n",
+ " for termination, interfaces in sorted_interfaces.items():\n",
+ " for index, interface_data in enumerate(interfaces):\n",
+ " strain_percentage = interface_data[\"mean_abs_strain\"] * 100\n",
+ " num_sites = interface_data[\"interface\"].num_sites\n",
+ " key = (strain_percentage, num_sites)\n",
+ " grouped_interfaces[key].append((index, termination))\n",
+ " \n",
+ " data = []\n",
+ " for (strain, num_sites), indices_and_terminations in grouped_interfaces.items():\n",
+ " termination_indices = defaultdict(list)\n",
+ " for index, termination in indices_and_terminations:\n",
+ " termination_indices[termination].append(index)\n",
+ " all_indices = [index for indices in termination_indices.values() for index in indices]\n",
+ " index_range = f\"{min(all_indices)}-{max(all_indices)}\" if len(all_indices) > 1 else str(min(all_indices))\n",
+ " \n",
+ " hover_text = \"
-----
\".join(\n",
+ " f\"Termination: {termination}
Termination index: {termination_to_index[termination]}
Interfaces Index Range: {index_range}
Strain: {strain:.2f}%
Atoms: {num_sites}\"\n",
+ " for termination, indices in termination_indices.items()\n",
+ " )\n",
+ " trace = go.Scatter(\n",
+ " x=[strain],\n",
+ " y=[num_sites],\n",
+ " text=[hover_text],\n",
+ " mode=\"markers\",\n",
+ " hoverinfo=\"text\",\n",
+ " name=f\"Indices: {index_range}\",\n",
+ " )\n",
+ " data.append(trace)\n",
+ "\n",
+ " layout = go.Layout(\n",
+ " xaxis=dict(title=\"Strain (%)\", type=settings[\"X_SCALE\"]),\n",
+ " yaxis=dict(title=\"Number of atoms\", type=settings[\"Y_SCALE\"]),\n",
+ " hovermode=\"closest\",\n",
+ " height=settings[\"HEIGHT\"],\n",
+ " legend_title_text=\"Interfaces Index Range\",\n",
+ " )\n",
+ " fig = go.Figure(data=data, layout=layout)\n",
+ " fig.show()\n",
+ "\n",
+ "\n",
+ "\n",
+ "plot_strain_vs_atoms(sorted_interfaces, terminations, PLOT_SETTINGS)\n",
+ "\n",
+ "for i, termination in enumerate(terminations):\n",
+ " print(f\"Termination {i}:\", termination)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 6. Select the interface to relax\n",
+ "\n",
+ "### 6.1. Select the interface with the desired termination and strain\n",
+ "\n",
+ "The data in `sorted_interfaces` now contains an object with the following structure:\n",
+ "\n",
+ "```json\n",
+ "{\n",
+ " \"('C_P6/mmm_2', 'Si_R-3m_1')\": [\n",
+ " { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 0...},\n",
+ " { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 1...},\n",
+ " ...\n",
+ " ],\n",
+ " \"\": [\n",
+ " { ...interface for 'termination at index 1' at index 0...},\n",
+ " { ...interface for 'termination at index 1' at index 1...},\n",
+ " ...\n",
+ " ]\n",
+ "}\n",
+ "```\n",
+ "\n",
+ "Select the index for termination first, and for it - the index in the list of corresponding interfaces sorted by strain (index 0 has minimum strain)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 34,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.606026Z",
+ "start_time": "2024-02-23T06:59:34.582078Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "termination_index = 0\n",
+ "interface_index = 0\n",
+ "\n",
+ "termination = terminations[termination_index]\n",
+ "\n",
+ "interface = sorted_interfaces[termination][interface_index][\"interface\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 7. Apply relaxation\n",
+ "### 7.1. Apply relaxation to the selected interface"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 35,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.870087Z",
+ "start_time": "2024-02-23T06:59:34.592671Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ " Step Time Energy fmax\n",
+ "BFGS: 0 22:59:34 1.423498 0.2830\n",
+ "BFGS: 1 22:59:34 1.422027 0.2575\n",
+ "BFGS: 2 22:59:34 1.410513 0.2052\n",
+ "BFGS: 3 22:59:34 1.408686 0.2119\n",
+ "BFGS: 4 22:59:34 1.404357 0.2224\n",
+ "BFGS: 5 22:59:34 1.396633 0.3918\n",
+ "BFGS: 6 22:59:34 1.359630 0.7796\n",
+ "BFGS: 7 22:59:34 1.248944 1.0053\n",
+ "BFGS: 8 22:59:34 1.171894 1.0624\n",
+ "BFGS: 9 22:59:34 1.142866 1.0505\n",
+ "BFGS: 10 22:59:34 1.095750 1.0480\n",
+ "BFGS: 11 22:59:34 0.909481 1.0819\n",
+ "BFGS: 12 22:59:34 0.845958 1.4513\n",
+ "BFGS: 13 22:59:34 0.759011 0.9676\n",
+ "BFGS: 14 22:59:34 0.731432 0.3462\n",
+ "BFGS: 15 22:59:34 0.723573 0.1376\n",
+ "BFGS: 16 22:59:34 0.722363 0.0433\n",
+ "BFGS: 17 22:59:34 0.722235 0.0062\n",
+ "Original structure:\n",
+ " Ni C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni C \n",
+ " 3 2\n",
+ "Cartesian\n",
+ " 1.2300003829518735 0.7101417146926911 0.0000000000000001\n",
+ " 0.0000010237800144 0.0000011496383034 2.0085815523361399\n",
+ " 2.4600007659037471 1.4202834293853821 4.0171631046722771\n",
+ " 0.0000000000000000 0.0000000000000000 7.0171631046722780\n",
+ " 1.2299984704321631 0.7101402381262073 7.0171631046722780\n",
+ "\n",
+ "\n",
+ "Relaxed structure:\n",
+ " Ni C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni C \n",
+ " 3 2\n",
+ "Cartesian\n",
+ " 1.2300031015085946 0.7101461929515854 0.5260421300230792\n",
+ " 0.0000006922939747 0.0000006009098648 2.5465564275909114\n",
+ " 2.4599985522195529 1.4202799717570684 4.5673026420532636\n",
+ " -0.0000007808382405 -0.0000007432361958 6.2106684704714068\n",
+ " 1.2299990778839116 0.7101405094602526 6.2095011962143465\n",
+ "\n",
+ "The final energy is 0.722 eV.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Per https://github.com/materialsvirtuallab/matgl/blob/main/examples/Relaxations%20and%20Simulations%20using%20the%20M3GNet%20Universal%20Potential.ipynb\n",
+ "from src.utils import poscar_to_ase, ase_to_poscar, ase_to_pymatgen, pymatgen_to_ase\n",
+ "from ase.optimize import BFGS\n",
+ "from ase.calculators.emt import EMT\n",
+ "\n",
+ "# relax the interface\n",
+ "ase_interface = poscar_to_ase(interface.to(fmt=\"poscar\"))\n",
+ "ase_interface.set_calculator(EMT())\n",
+ "dyn = BFGS(ase_interface)\n",
+ "dyn.run(fmax=0.01)\n",
+ "\n",
+ "# extract results\n",
+ "ase_original_interface = pymatgen_to_ase(interface)\n",
+ "ase_final_interface = ase_interface\n",
+ "relaxed_energy = ase_interface.get_total_energy()\n",
+ "\n",
+ "# print out the final relaxed structure and energy\n",
+ "print('Original structure:\\n', ase_to_poscar(ase_original_interface))\n",
+ "print('\\nRelaxed structure:\\n', ase_to_poscar(ase_final_interface))\n",
+ "print(f\"The final energy is {float(relaxed_energy):.3f} eV.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 7.2. View structure before and after relaxation"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 39,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T07:00:03.504373Z",
+ "start_time": "2024-02-23T07:00:02.936170Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": "",
+ "text/html": "\n
\n
Ni3C2 - original
\n

\n
\n \n
\n
Ni3C2 - relaxed
\n

\n
\n
"
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import base64\n",
+ "from ase.io import write\n",
+ "from ase.build import make_supercell\n",
+ "from IPython.display import HTML\n",
+ "import io\n",
+ "\n",
+ "def visualize_material_base64(material, title: str, rotation: str = '0x', number_of_repetitions: int = 3):\n",
+ " \"\"\"\n",
+ " Returns an HTML string with a Base64-encoded image for visualization,\n",
+ " including the name of the file, positioned horizontally.\n",
+ " \"\"\"\n",
+ " # Set the number of unit cell repetition for the structure\n",
+ " n = number_of_repetitions\n",
+ " material_repeat = make_supercell(material, [[n,0,0],[0,n,0],[0,0,1]])\n",
+ " text = f\"{material.symbols} - {title}\"\n",
+ " \n",
+ " # Write image to a buffer to display in HTML\n",
+ " buf = io.BytesIO()\n",
+ " write(buf, material_repeat, format='png', rotation=rotation)\n",
+ " buf.seek(0)\n",
+ " img_str = base64.b64encode(buf.read()).decode('utf-8')\n",
+ " html_str = f'''\n",
+ " \n",
+ "
{text}
\n",
+ "

\n",
+ "
\n",
+ " '''\n",
+ " return html_str\n",
+ "\n",
+ "html_original = visualize_material_base64(ase_original_interface, \"original\", \"-90x\")\n",
+ "html_relaxed = visualize_material_base64(ase_final_interface, \"relaxed\", \"-90x\")\n",
+ "\n",
+ "# Display the interfaces before and after relaxation\n",
+ "html_content = f'{html_original}{html_relaxed}
'\n",
+ "display(HTML(html_content))\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 7.3. Calculate energy energy using ASE EMT"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 37,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.937042Z",
+ "start_time": "2024-02-23T06:59:34.870485Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "5.240839288055053\n",
+ "5\n",
+ "3\n",
+ "2\n",
+ "Relaxed interface energy: 0.7222 eV\n",
+ "Original Substrate energy: 1.0828 eV\n",
+ "Original Layer energy: 0.4296 eV\n",
+ "Original Delta: -0.7901 eV\n",
+ "Original Delta per area: -0.1508 eV/Ang^2\n",
+ "Relaxed Substrate energy: 1.0817 eV\n",
+ "Relaxed Layer energy: 0.4296 eV\n",
+ "Relaxed Delta: -0.7890 eV\n",
+ "Relaxed Delta per area: -0.1506 eV/Ang^2\n",
+ "Effective relaxed Delta per area: -0.0094 eV/Ang^2\n",
+ "Ni C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni C \n",
+ " 3 2\n",
+ "Cartesian\n",
+ " 1.2300031015085946 0.7101461929515854 0.5260421300230792\n",
+ " 0.0000006922939747 0.0000006009098648 2.5465564275909114\n",
+ " 2.4599985522195529 1.4202799717570684 4.5673026420532636\n",
+ " -0.0000007808382405 -0.0000007432361958 6.2106684704714068\n",
+ " 1.2299990778839116 0.7101405094602526 6.2095011962143465\n",
+ "\n",
+ "Ni \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni \n",
+ " 3\n",
+ "Cartesian\n",
+ " 1.2300031015085946 0.7101461929515854 0.5260421300230792\n",
+ " 0.0000006922939747 0.0000006009098648 2.5465564275909114\n",
+ " 2.4599985522195529 1.4202799717570684 4.5673026420532636\n",
+ "\n",
+ " C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " C \n",
+ " 2\n",
+ "Cartesian\n",
+ " -0.0000007808382405 -0.0000007432361958 6.2106684704714068\n",
+ " 1.2299990778839116 0.7101405094602526 6.2095011962143465\n",
+ "\n",
+ "Ni \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni \n",
+ " 3\n",
+ "Cartesian\n",
+ " 1.2300003829518735 0.7101417146926911 0.0000000000000001\n",
+ " 0.0000010237800144 0.0000011496383034 2.0085815523361399\n",
+ " 2.4600007659037471 1.4202834293853821 4.0171631046722771\n",
+ "\n",
+ " C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " C \n",
+ " 2\n",
+ "Cartesian\n",
+ " 0.0000000000000000 0.0000000000000000 7.0171631046722780\n",
+ " 1.2299984704321631 0.7101402381262073 7.0171631046722780\n"
+ ]
+ }
+ ],
+ "source": [
+ "from ase import Atoms\n",
+ "nickel_relaxed_interface = ase_final_interface.copy()\n",
+ "graphene_relaxed_interface = ase_final_interface.copy()\n",
+ "nickel_original_interface = ase_original_interface.copy()\n",
+ "graphene_original_interface = ase_original_interface.copy()\n",
+ "\n",
+ "del nickel_relaxed_interface[[atom.index for atom in nickel_relaxed_interface if atom.symbol != 'Ni']]\n",
+ "del graphene_relaxed_interface[[atom.index for atom in graphene_relaxed_interface if atom.symbol != 'C']]\n",
+ "del nickel_original_interface[[atom.index for atom in nickel_original_interface if atom.symbol != 'Ni']]\n",
+ "del graphene_original_interface[[atom.index for atom in graphene_original_interface if atom.symbol != 'C']]\n",
+ "\n",
+ "#set EMT calculator\n",
+ "nickel_relaxed_interface.set_calculator(EMT())\n",
+ "graphene_relaxed_interface.set_calculator(EMT())\n",
+ "nickel_original_interface.set_calculator(EMT())\n",
+ "graphene_original_interface.set_calculator(EMT())\n",
+ "\n",
+ "original_substrate_energy = nickel_original_interface.get_total_energy()\n",
+ "original_layer_energy = graphene_original_interface.get_total_energy()\n",
+ "relaxed_substrate_energy = nickel_relaxed_interface.get_total_energy()\n",
+ "relaxed_layer_energy = graphene_relaxed_interface.get_total_energy()\n",
+ "\n",
+ "delta_original = relaxed_energy - original_substrate_energy - original_layer_energy\n",
+ "delta_relaxed = relaxed_energy - relaxed_substrate_energy - relaxed_layer_energy\n",
+ "\n",
+ "# calculate area of interface\n",
+ "area = ase_original_interface.get_volume() / ase_original_interface.cell[2, 2]\n",
+ "print(area)\n",
+ "\n",
+ "effective_delta_relaxed = (relaxed_energy * ase_final_interface.get_global_number_of_atoms() -\n",
+ " nickel_relaxed_interface.get_global_number_of_atoms() * relaxed_substrate_energy -\n",
+ " graphene_relaxed_interface.get_global_number_of_atoms() * relaxed_layer_energy)/(2 * area * ase_final_interface.get_global_number_of_atoms())\n",
+ "print(ase_final_interface.get_global_number_of_atoms())\n",
+ "print(nickel_relaxed_interface.get_global_number_of_atoms())\n",
+ "print(graphene_relaxed_interface.get_global_number_of_atoms())\n",
+ "# print(f\"Original interface energy: {ase_original_interface.get_total_energy():.4f} eV\")\n",
+ "print(f\"Relaxed interface energy: {relaxed_energy:.4f} eV\")\n",
+ "print(f\"Original Substrate energy: {original_substrate_energy:.4f} eV\")\n",
+ "print(f\"Original Layer energy: {original_layer_energy:.4f} eV\")\n",
+ "print(f\"Original Delta: {delta_original:.4f} eV\")\n",
+ "print(f\"Original Delta per area: {delta_original / area:.4f} eV/Ang^2\")\n",
+ "print(f\"Relaxed Substrate energy: {relaxed_substrate_energy:.4f} eV\")\n",
+ "print(f\"Relaxed Layer energy: {relaxed_layer_energy:.4f} eV\")\n",
+ "print(f\"Relaxed Delta: {delta_relaxed:.4f} eV\")\n",
+ "print(f\"Relaxed Delta per area: {delta_relaxed / area:.4f} eV/Ang^2\")\n",
+ "print(f\"Effective relaxed Delta per area: {effective_delta_relaxed:.4f} eV/Ang^2\")\n",
+ "\n",
+ "\n",
+ "print(ase_to_poscar(ase_final_interface))\n",
+ "print(ase_to_poscar(nickel_relaxed_interface))\n",
+ "print(ase_to_poscar(graphene_relaxed_interface))\n",
+ "print(ase_to_poscar(nickel_original_interface))\n",
+ "print(ase_to_poscar(graphene_original_interface))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 8. Pass relaxed interface to Materials Designer"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 38,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:34.994260Z",
+ "start_time": "2024-02-23T06:59:34.937542Z"
+ }
+ },
+ "outputs": [
+ {
+ "ename": "ImportError",
+ "evalue": "This module intended to be used in a Pyodide environment. Please install packages yourself using pip.",
+ "output_type": "error",
+ "traceback": [
+ "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m",
+ "\u001B[0;31mModuleNotFoundError\u001B[0m Traceback (most recent call last)",
+ "File \u001B[0;32m~/code/GREEN/api-examples/other/materials_designer/jupyterlite/utils.py:6\u001B[0m\n\u001B[1;32m 5\u001B[0m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[0;32m----> 6\u001B[0m \u001B[38;5;28;01mimport\u001B[39;00m \u001B[38;5;21;01mmicropip\u001B[39;00m\n\u001B[1;32m 7\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mImportError\u001B[39;00m:\n",
+ "\u001B[0;31mModuleNotFoundError\u001B[0m: No module named 'micropip'",
+ "\nDuring handling of the above exception, another exception occurred:\n",
+ "\u001B[0;31mImportError\u001B[0m Traceback (most recent call last)",
+ "Cell \u001B[0;32mIn[38], line 1\u001B[0m\n\u001B[0;32m----> 1\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mjupyterlite\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mutils\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m set_data\n\u001B[1;32m 2\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01msrc\u001B[39;00m\u001B[38;5;21;01m.\u001B[39;00m\u001B[38;5;21;01mutils\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m from_pymatgen\n\u001B[1;32m 4\u001B[0m esse_interface \u001B[38;5;241m=\u001B[39m from_pymatgen(ase_to_pymatgen(ase_final_interface))\n",
+ "File \u001B[0;32m~/code/GREEN/api-examples/other/materials_designer/jupyterlite/utils.py:8\u001B[0m\n\u001B[1;32m 6\u001B[0m \u001B[38;5;28;01mimport\u001B[39;00m \u001B[38;5;21;01mmicropip\u001B[39;00m\n\u001B[1;32m 7\u001B[0m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mImportError\u001B[39;00m:\n\u001B[0;32m----> 8\u001B[0m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mImportError\u001B[39;00m(\n\u001B[1;32m 9\u001B[0m \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mThis module intended to be used in a Pyodide environment. Please install packages yourself using pip.\u001B[39m\u001B[38;5;124m\"\u001B[39m\n\u001B[1;32m 10\u001B[0m )\n\u001B[1;32m 12\u001B[0m \u001B[38;5;28;01masync\u001B[39;00m \u001B[38;5;28;01mdef\u001B[39;00m \u001B[38;5;21minstall_package\u001B[39m(pkg, verbose\u001B[38;5;241m=\u001B[39m\u001B[38;5;28;01mTrue\u001B[39;00m):\n\u001B[1;32m 13\u001B[0m \u001B[38;5;250m \u001B[39m\u001B[38;5;124;03m\"\"\"\u001B[39;00m\n\u001B[1;32m 14\u001B[0m \u001B[38;5;124;03m Installs a package in a Pyodide environment.\u001B[39;00m\n\u001B[1;32m 15\u001B[0m \u001B[38;5;124;03m Args:\u001B[39;00m\n\u001B[0;32m (...)\u001B[0m\n\u001B[1;32m 20\u001B[0m \u001B[38;5;124;03m None\u001B[39;00m\n\u001B[1;32m 21\u001B[0m \u001B[38;5;124;03m \"\"\"\u001B[39;00m\n",
+ "\u001B[0;31mImportError\u001B[0m: This module intended to be used in a Pyodide environment. Please install packages yourself using pip."
+ ]
+ }
+ ],
+ "source": [
+ "from jupyterlite.utils import set_data\n",
+ "from src.utils import from_pymatgen\n",
+ "\n",
+ "esse_interface = from_pymatgen(ase_to_pymatgen(ase_final_interface))\n",
+ "set_data(\"materials\", [esse_interface] )"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:59:35.003262Z",
+ "start_time": "2024-02-23T06:59:34.998238Z"
+ }
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "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.10.12"
+ },
+ "widgets": {
+ "application/vnd.jupyter.widget-state+json": {
+ "state": {},
+ "version_major": 2,
+ "version_minor": 0
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/other/materials_designer/create_interface_with_relaxation_ase_lj.ipynb b/other/materials_designer/create_interface_with_relaxation_ase_lj.ipynb
new file mode 100644
index 00000000..11e06637
--- /dev/null
+++ b/other/materials_designer/create_interface_with_relaxation_ase_lj.ipynb
@@ -0,0 +1,581 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Create an interface between two materials with minimal strain\n",
+ "\n",
+ "Use Zur and McGill superlattices matching [algorithm](https://doi.org/10.1063/1.3330840) to create interfaces between two materials using the Pymatgen [implementation](https://pymatgen.org/pymatgen.analysis.interfaces.html#pymatgen.analysis.interfaces.zsl).\n",
+ "\n",
+ "Usage
\n",
+ "\n",
+ "0. Make sure to select Input Materials\n",
+ "1. Execute \"Run first: ...\" cell below to load Input Materials into the current kernel\n",
+ "2. Set Input Parameters (e.g. `MILLER_INDICES`, `THICKNESS`, `MAX_AREA`) below or use the default values\n",
+ "3. Click \"Run\" > \"Run All\" to run all cells\n",
+ "4. Wait for the run to complete (depending on the area, it can take 1-2 min or more). Scroll down to view cell results.\n",
+ "5. Review the strain plot and modify its parameters as needed\n",
+ "\n",
+ "## Methodology\n",
+ "\n",
+ "The following happens in the script below:\n",
+ "\n",
+ "1. Create slabs for each input material. The materials data is passed in from and back to the web application according to this description (TBA).\n",
+ " We assume that two input materials are either in bulk form (e.g. Ni crystal) or layered (e.g. graphene). \n",
+ " \n",
+ " We construct the interface along the Z-axis. The material corresponding to the bottom of the interface is referred to as the \"**substrate**\", and the top - as the \"**layer**\". \n",
+ "\n",
+ "2. Perform strain matching on the slabs to extract the supercell dimensions. The algorithm has a set of parameters, such as the maximum area considered, that can be configured by editing the cells below.\n",
+ "\n",
+ "3. When the strain matching is finished, the interface with the lowest strain (and the smallest number of atoms) is selected. We create the corresponding supercells and place them at a specified distance from each other (note no shift is performed currently).\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Run first: load input materials in current kernel
\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import json\n",
+ "materials_in = []\n",
+ "for file in [\"Ni.json\", \"Gr.json\"]:\n",
+ " with open(file, \"r\") as f:\n",
+ " data = f.read()\n",
+ " materials_in.append(json.loads(data))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1. Set Input Parameters\n",
+ "\n",
+ "### 1.1. Select Substrate and Layer from Input Materials"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "SUBSTRATE_PARAMETERS = {\n",
+ " \"MATERIAL_INDEX\": 0, # the index of the material in the materials_in list\n",
+ " \"MILLER_INDICES\": (1, 1, 2), # the miller indices of the interfacial plane\n",
+ " \"THICKNESS\": 3, # in layers\n",
+ "}\n",
+ "\n",
+ "LAYER_PARAMETERS = {\n",
+ " \"MATERIAL_INDEX\": 1, # the index of the material in the materials_in list\n",
+ " \"MILLER_INDICES\": (0, 0, 1), # the miller indices of the interfacial plane\n",
+ " \"THICKNESS\": 1, # in layers\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1.2. Set Interface Parameters\n",
+ "\n",
+ "The distance between layer and substrate and maximum area to consider when matching.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "INTERFACE_PARAMETERS = {\n",
+ " \"DISTANCE_Z\": 3.0, # in Angstroms\n",
+ " \"MAX_AREA\": 50, # in Angstroms^2\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1.3. Set Algorithm Parameters"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ZSL_PARAMETERS = {\n",
+ " \"MAX_AREA\": INTERFACE_PARAMETERS[\"MAX_AREA\"], # The area to consider in Angstrom^2\n",
+ " \"MAX_AREA_TOL\": 0.09, # The area within this tolerance is considered equal\n",
+ " \"MAX_LENGTH_TOL\": 0.03, # supercell lattice vectors lengths within this tolerance are considered equal\n",
+ " \"MAX_ANGLE_TOL\": 0.01, # supercell lattice angles within this tolerance are considered equal\n",
+ " \"STRAIN_TOL\": 10e-6, # strains within this tolerance are considered equal\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 2. Install Packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from jupyterlite.utils import install_packages\n",
+ "\n",
+ "await install_packages(\"create_interface_with_min_strain_zsl.ipynb\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 3. Create interfaces\n",
+ "\n",
+ "### 3.1. Extract Interfaces and Terminations\n",
+ "\n",
+ "Extract all possible layer/substrate supercell combinations within the maximum area including different terminations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {
+ "tags": [],
+ "trusted": true
+ },
+ "outputs": [],
+ "source": [
+ "from src.pymatgen_coherent_interface_builder import CoherentInterfaceBuilder, ZSLGenerator\n",
+ "from src.utils import to_pymatgen\n",
+ "\n",
+ "if \"materials_in\" in globals():\n",
+ " pymatgen_materials = [to_pymatgen(item) for item in materials_in]\n",
+ "for material in pymatgen_materials:\n",
+ " print(material, \"\\n\")\n",
+ "\n",
+ "\n",
+ "def create_interfaces(settings):\n",
+ " print(\"Creating interfaces...\")\n",
+ " zsl = ZSLGenerator(\n",
+ " max_area_ratio_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA_TOL\"],\n",
+ " max_area=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA\"],\n",
+ " max_length_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_LENGTH_TOL\"],\n",
+ " max_angle_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_ANGLE_TOL\"],\n",
+ " )\n",
+ "\n",
+ " cib = CoherentInterfaceBuilder(\n",
+ " substrate_structure=pymatgen_materials[settings[\"SUBSTRATE_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
+ " film_structure=pymatgen_materials[settings[\"LAYER_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
+ " substrate_miller=settings[\"SUBSTRATE_PARAMETERS\"][\"MILLER_INDICES\"],\n",
+ " film_miller=settings[\"LAYER_PARAMETERS\"][\"MILLER_INDICES\"],\n",
+ " zslgen=zsl,\n",
+ " strain_tol=settings[\"ZSL_PARAMETERS\"][\"STRAIN_TOL\"],\n",
+ " )\n",
+ "\n",
+ " # Find terminations\n",
+ " cib._find_terminations()\n",
+ " terminations = cib.terminations\n",
+ "\n",
+ " # Create interfaces for each termination\n",
+ " interfaces = {}\n",
+ " for termination in terminations:\n",
+ " interfaces[termination] = []\n",
+ " for interface in cib.get_interfaces(\n",
+ " termination,\n",
+ " gap=settings[\"INTERFACE_PARAMETERS\"][\"DISTANCE_Z\"],\n",
+ " film_thickness=settings[\"LAYER_PARAMETERS\"][\"THICKNESS\"],\n",
+ " substrate_thickness=settings[\"SUBSTRATE_PARAMETERS\"][\"THICKNESS\"],\n",
+ " in_layers=True,\n",
+ " ):\n",
+ " # Wrap atoms to unit cell\n",
+ " interface[\"interface\"].make_supercell((1,1,1), to_unit_cell=True)\n",
+ " interfaces[termination].append(interface)\n",
+ " return interfaces, terminations\n",
+ "\n",
+ "\n",
+ "interfaces, terminations = create_interfaces(\n",
+ " settings={\n",
+ " \"SUBSTRATE_PARAMETERS\": SUBSTRATE_PARAMETERS,\n",
+ " \"LAYER_PARAMETERS\": LAYER_PARAMETERS,\n",
+ " \"ZSL_PARAMETERS\": ZSL_PARAMETERS,\n",
+ " \"INTERFACE_PARAMETERS\": INTERFACE_PARAMETERS,\n",
+ " }\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 3.2. Print out the interfaces and terminations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(f'Found {len(terminations)} terminations')\n",
+ "for termination in terminations:\n",
+ " print(f\"Found {len(interfaces[termination])} interfaces for\", termination, \"termination\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 4. Sort interfaces by strain\n",
+ "\n",
+ "### 4.1. Sort all interfaces"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Could be \"strain\", \"von_mises_strain\", \"mean_abs_strain\"\n",
+ "strain_mode = \"mean_abs_strain\"\n",
+ "\n",
+ "# Sort interfaces by the specified strain mode and number of sites\n",
+ "def sort_interfaces(interfaces, terminations):\n",
+ " sorted_interfaces = {}\n",
+ " for termination in terminations:\n",
+ " sorted_interfaces[termination] = sorted(\n",
+ " interfaces[termination], key=lambda x: (x[strain_mode], x[\"interface\"].num_sites)\n",
+ " )\n",
+ " return sorted_interfaces\n",
+ "\n",
+ "\n",
+ "sorted_interfaces = sort_interfaces(interfaces, terminations)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 4.2. Print out interfaces with lowest strain for each termination"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "for termination in terminations:\n",
+ " print(f\"Interface with lowest strain for termination {termination} (index 0):\")\n",
+ " first_interface = interfaces[termination][0]\n",
+ " print(\" strain:\", first_interface[strain_mode] * 100, \"%\")\n",
+ " print(\" number of atoms:\", first_interface[\"interface\"].num_sites)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 5. Plot the results\n",
+ "\n",
+ "Plot the number of atoms vs strain. Adjust the parameters as needed.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import plotly.graph_objs as go\n",
+ "from collections import defaultdict\n",
+ "\n",
+ "PLOT_SETTINGS = {\n",
+ " \"HEIGHT\": 600,\n",
+ " \"X_SCALE\": \"log\", # or linear\n",
+ " \"Y_SCALE\": \"log\", # or linear\n",
+ "}\n",
+ "\n",
+ "\n",
+ "def plot_strain_vs_atoms(sorted_interfaces, terminations, settings):\n",
+ " # Create a mapping from termination to its index\n",
+ " termination_to_index = {termination: i for i, termination in enumerate(terminations)}\n",
+ "\n",
+ " grouped_interfaces = defaultdict(list)\n",
+ " for termination, interfaces in sorted_interfaces.items():\n",
+ " for index, interface_data in enumerate(interfaces):\n",
+ " strain_percentage = interface_data[\"mean_abs_strain\"] * 100\n",
+ " num_sites = interface_data[\"interface\"].num_sites\n",
+ " key = (strain_percentage, num_sites)\n",
+ " grouped_interfaces[key].append((index, termination))\n",
+ " \n",
+ " data = []\n",
+ " for (strain, num_sites), indices_and_terminations in grouped_interfaces.items():\n",
+ " termination_indices = defaultdict(list)\n",
+ " for index, termination in indices_and_terminations:\n",
+ " termination_indices[termination].append(index)\n",
+ " all_indices = [index for indices in termination_indices.values() for index in indices]\n",
+ " index_range = f\"{min(all_indices)}-{max(all_indices)}\" if len(all_indices) > 1 else str(min(all_indices))\n",
+ " \n",
+ " hover_text = \"
-----
\".join(\n",
+ " f\"Termination: {termination}
Termination index: {termination_to_index[termination]}
Interfaces Index Range: {index_range}
Strain: {strain:.2f}%
Atoms: {num_sites}\"\n",
+ " for termination, indices in termination_indices.items()\n",
+ " )\n",
+ " trace = go.Scatter(\n",
+ " x=[strain],\n",
+ " y=[num_sites],\n",
+ " text=[hover_text],\n",
+ " mode=\"markers\",\n",
+ " hoverinfo=\"text\",\n",
+ " name=f\"Indices: {index_range}\",\n",
+ " )\n",
+ " data.append(trace)\n",
+ "\n",
+ " layout = go.Layout(\n",
+ " xaxis=dict(title=\"Strain (%)\", type=settings[\"X_SCALE\"]),\n",
+ " yaxis=dict(title=\"Number of atoms\", type=settings[\"Y_SCALE\"]),\n",
+ " hovermode=\"closest\",\n",
+ " height=settings[\"HEIGHT\"],\n",
+ " legend_title_text=\"Interfaces Index Range\",\n",
+ " )\n",
+ " fig = go.Figure(data=data, layout=layout)\n",
+ " fig.show()\n",
+ "\n",
+ "\n",
+ "\n",
+ "plot_strain_vs_atoms(sorted_interfaces, terminations, PLOT_SETTINGS)\n",
+ "\n",
+ "for i, termination in enumerate(terminations):\n",
+ " print(f\"Termination {i}:\", termination)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 6. Select the interface to relax\n",
+ "\n",
+ "### 6.1. Select the interface with the desired termination and strain\n",
+ "\n",
+ "The data in `sorted_interfaces` now contains an object with the following structure:\n",
+ "\n",
+ "```json\n",
+ "{\n",
+ " \"('C_P6/mmm_2', 'Si_R-3m_1')\": [\n",
+ " { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 0...},\n",
+ " { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 1...},\n",
+ " ...\n",
+ " ],\n",
+ " \"\": [\n",
+ " { ...interface for 'termination at index 1' at index 0...},\n",
+ " { ...interface for 'termination at index 1' at index 1...},\n",
+ " ...\n",
+ " ]\n",
+ "}\n",
+ "```\n",
+ "\n",
+ "Select the index for termination first, and for it - the index in the list of corresponding interfaces sorted by strain (index 0 has minimum strain)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "termination_index = 0\n",
+ "interface_index = 0\n",
+ "\n",
+ "termination = terminations[termination_index]\n",
+ "\n",
+ "interface = sorted_interfaces[termination][interface_index][\"interface\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 7. Apply relaxation\n",
+ "### 7.1. Apply relaxation to the selected interface"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Per https://github.com/materialsvirtuallab/matgl/blob/main/examples/Relaxations%20and%20Simulations%20using%20the%20M3GNet%20Universal%20Potential.ipynb\n",
+ "from src.utils import poscar_to_ase, ase_to_poscar, ase_to_pymatgen, pymatgen_to_ase\n",
+ "from ase.optimize import BFGS\n",
+ "from ase.calculators.lj import LennardJones as LJ\n",
+ "\n",
+ "# relax the interface\n",
+ "ase_interface = poscar_to_ase(interface.to(fmt=\"poscar\"))\n",
+ "ase_interface.set_calculator(LJ())\n",
+ "dyn = BFGS(ase_interface)\n",
+ "dyn.run(fmax=0.01)\n",
+ "\n",
+ "# extract results\n",
+ "ase_original_interface = pymatgen_to_ase(interface)\n",
+ "ase_relaxed_interface = ase_interface\n",
+ "relaxed_energy = ase_interface.get_total_energy()\n",
+ "\n",
+ "# print out the final relaxed structure and energy\n",
+ "print('Original structure:\\n', ase_to_poscar(ase_original_interface))\n",
+ "print('\\nRelaxed structure:\\n', ase_to_poscar(ase_relaxed_interface))\n",
+ "print(f\"The final energy is {float(relaxed_energy):.3f} eV.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 7.2. View structure before and after relaxation"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ase.visualize import view\n",
+ "\n",
+ "# view(ase_original_interface, viewer=\"x3d\")\n",
+ "# view(ase_relaxed_interface, viewer=\"x3d\")\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 7.3. Calculate energy energy using ASE LJ"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from ase import Atoms\n",
+ "nickel_relaxed_interface = ase_relaxed_interface.copy()\n",
+ "graphene_relaxed_interface = ase_relaxed_interface.copy()\n",
+ "nickel_original_interface = ase_original_interface.copy()\n",
+ "graphene_original_interface = ase_original_interface.copy()\n",
+ "\n",
+ "del nickel_relaxed_interface[[atom.index for atom in nickel_relaxed_interface if atom.symbol != 'Ni']]\n",
+ "del graphene_relaxed_interface[[atom.index for atom in graphene_relaxed_interface if atom.symbol != 'C']]\n",
+ "del nickel_original_interface[[atom.index for atom in nickel_original_interface if atom.symbol != 'Ni']]\n",
+ "del graphene_original_interface[[atom.index for atom in graphene_original_interface if atom.symbol != 'C']]\n",
+ "\n",
+ "#set LJ calculator\n",
+ "nickel_relaxed_interface.set_calculator(LJ())\n",
+ "graphene_relaxed_interface.set_calculator(LJ())\n",
+ "nickel_original_interface.set_calculator(LJ())\n",
+ "graphene_original_interface.set_calculator(LJ())\n",
+ "\n",
+ "original_substrate_energy = nickel_original_interface.get_total_energy()\n",
+ "original_layer_energy = graphene_original_interface.get_total_energy()\n",
+ "relaxed_substrate_energy = nickel_relaxed_interface.get_total_energy()\n",
+ "relaxed_layer_energy = graphene_relaxed_interface.get_total_energy()\n",
+ "\n",
+ "delta_original = relaxed_energy - original_substrate_energy - original_layer_energy\n",
+ "delta_relaxed = relaxed_energy - relaxed_substrate_energy - relaxed_layer_energy\n",
+ "\n",
+ "# calculate area of interface\n",
+ "area = ase_original_interface.get_volume() / ase_original_interface.cell[2, 2]\n",
+ "print('Interface area:', area, 'Ang^2')\n",
+ "print()\n",
+ "effective_delta_relaxed = (relaxed_energy*ase_relaxed_interface.get_global_number_of_atoms() - \n",
+ " nickel_relaxed_interface.get_global_number_of_atoms()*relaxed_substrate_energy - \n",
+ " graphene_relaxed_interface.get_global_number_of_atoms()*relaxed_layer_energy)/(2*area*ase_relaxed_interface.get_global_number_of_atoms())\n",
+ "\n",
+ "print(f\"Relaxed interface energy: {relaxed_energy:.4f} eV\")\n",
+ "print(f\"Original Substrate energy: {original_substrate_energy:.4f} eV\")\n",
+ "print(f\"Original Layer energy: {original_layer_energy:.4f} eV\")\n",
+ "print(f\"Original Delta: {delta_original:.4f} eV\")\n",
+ "print(f\"Original Delta per area: {delta_original / area:.4f} eV/Ang^2\")\n",
+ "print()\n",
+ "\n",
+ "print(f\"Relaxed Substrate energy: {relaxed_substrate_energy:.4f} eV\")\n",
+ "print(f\"Relaxed Layer energy: {relaxed_layer_energy:.4f} eV\")\n",
+ "print(f\"Relaxed Delta: {delta_relaxed:.4f} eV\")\n",
+ "print(f\"Relaxed Delta per area: {delta_relaxed / area:.4f} eV/Ang^2\")\n",
+ "print(f\"Effective relaxed Delta per area: {effective_delta_relaxed:.4f} eV/Ang^2\")\n",
+ "print()\n",
+ "\n",
+ "print(ase_to_poscar(ase_relaxed_interface))\n",
+ "print(ase_to_poscar(nickel_relaxed_interface))\n",
+ "print(ase_to_poscar(graphene_relaxed_interface))\n",
+ "print(ase_to_poscar(nickel_original_interface))\n",
+ "print(ase_to_poscar(graphene_original_interface))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 8. Pass relaxed interface to Materials Designer"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from jupyterlite.utils import set_data\n",
+ "from src.utils import from_pymatgen\n",
+ "\n",
+ "esse_interface = from_pymatgen(ase_to_pymatgen(ase_relaxed_interface))\n",
+ "set_data(\"materials\", [esse_interface] )"
+ ]
+ }
+ ],
+ "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.10.12"
+ },
+ "widgets": {
+ "application/vnd.jupyter.widget-state+json": {
+ "state": {},
+ "version_major": 2,
+ "version_minor": 0
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/other/materials_designer/create_interface_with_relaxation_matgl_m3gnet.ipynb b/other/materials_designer/create_interface_with_relaxation_matgl_m3gnet.ipynb
new file mode 100644
index 00000000..83b7291e
--- /dev/null
+++ b/other/materials_designer/create_interface_with_relaxation_matgl_m3gnet.ipynb
@@ -0,0 +1,2100 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Create an interface between two materials with minimal strain\n",
+ "\n",
+ "Use Zur and McGill superlattices matching [algorithm](https://doi.org/10.1063/1.3330840) to create interfaces between two materials using the Pymatgen [implementation](https://pymatgen.org/pymatgen.analysis.interfaces.html#pymatgen.analysis.interfaces.zsl).\n",
+ "\n",
+ "Usage
\n",
+ "\n",
+ "0. Make sure to select Input Materials\n",
+ "1. Execute \"Run first: ...\" cell below to load Input Materials into the current kernel\n",
+ "2. Set Input Parameters (e.g. `MILLER_INDICES`, `THICKNESS`, `MAX_AREA`) below or use the default values\n",
+ "3. Click \"Run\" > \"Run All\" to run all cells\n",
+ "4. Wait for the run to complete (depending on the area, it can take 1-2 min or more). Scroll down to view cell results.\n",
+ "5. Review the strain plot and modify its parameters as needed\n",
+ "\n",
+ "## Methodology\n",
+ "\n",
+ "The following happens in the script below:\n",
+ "\n",
+ "1. Create slabs for each input material. The materials data is passed in from and back to the web application according to this description (TBA).\n",
+ " We assume that two input materials are either in bulk form (e.g. Ni crystal) or layered (e.g. graphene). \n",
+ " \n",
+ " We construct the interface along the Z-axis. The material corresponding to the bottom of the interface is referred to as the \"**substrate**\", and the top - as the \"**layer**\". \n",
+ "\n",
+ "2. Perform strain matching on the slabs to extract the supercell dimensions. The algorithm has a set of parameters, such as the maximum area considered, that can be configured by editing the cells below.\n",
+ "\n",
+ "3. When the strain matching is finished, the interface with the lowest strain (and the smallest number of atoms) is selected. We create the corresponding supercells and place them at a specified distance from each other (note no shift is performed currently).\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Run first: load input materials in current kernel
\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:32.336033Z",
+ "start_time": "2024-02-23T06:46:32.328297Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "import json\n",
+ "materials_in = []\n",
+ "for file in [\"Ni.json\", \"Gr.json\"]:\n",
+ " with open(file, \"r\") as f:\n",
+ " data = f.read()\n",
+ " materials_in.append(json.loads(data))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 1. Set Input Parameters\n",
+ "\n",
+ "### 1.1. Select Substrate and Layer from Input Materials"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:32.344401Z",
+ "start_time": "2024-02-23T06:46:32.337621Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "SUBSTRATE_PARAMETERS = {\n",
+ " \"MATERIAL_INDEX\": 0, # the index of the material in the materials_in list\n",
+ " \"MILLER_INDICES\": (1, 1, 1), # the miller indices of the interfacial plane\n",
+ " \"THICKNESS\": 3, # in layers\n",
+ "}\n",
+ "\n",
+ "LAYER_PARAMETERS = {\n",
+ " \"MATERIAL_INDEX\": 1, # the index of the material in the materials_in list\n",
+ " \"MILLER_INDICES\": (0, 0, 1), # the miller indices of the interfacial plane\n",
+ " \"THICKNESS\": 1, # in layers\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1.2. Set Interface Parameters\n",
+ "\n",
+ "The distance between layer and substrate and maximum area to consider when matching.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:32.345092Z",
+ "start_time": "2024-02-23T06:46:32.341171Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "INTERFACE_PARAMETERS = {\n",
+ " \"DISTANCE_Z\": 3.0, # in Angstroms\n",
+ " \"MAX_AREA\": 50, # in Angstroms^2\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 1.3. Set Algorithm Parameters"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:32.352573Z",
+ "start_time": "2024-02-23T06:46:32.345998Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "ZSL_PARAMETERS = {\n",
+ " \"MAX_AREA\": INTERFACE_PARAMETERS[\"MAX_AREA\"], # The area to consider in Angstrom^2\n",
+ " \"MAX_AREA_TOL\": 0.09, # The area within this tolerance is considered equal\n",
+ " \"MAX_LENGTH_TOL\": 0.03, # supercell lattice vectors lengths within this tolerance are considered equal\n",
+ " \"MAX_ANGLE_TOL\": 0.01, # supercell lattice angles within this tolerance are considered equal\n",
+ " \"STRAIN_TOL\": 10e-6, # strains within this tolerance are considered equal\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 2. Install Packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:34.337926Z",
+ "start_time": "2024-02-23T06:46:32.350155Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Python 3.11.2\r\n",
+ "Package Version\r\n",
+ "-------------------------- -----------------\r\n",
+ "accelerate 0.27.2\r\n",
+ "aiohttp 3.9.3\r\n",
+ "aiosignal 1.3.1\r\n",
+ "alignn 2024.2.4\r\n",
+ "anyio 4.3.0\r\n",
+ "appnope 0.1.4\r\n",
+ "argon2-cffi 23.1.0\r\n",
+ "argon2-cffi-bindings 21.2.0\r\n",
+ "arrow 1.3.0\r\n",
+ "ase 3.22.1\r\n",
+ "asttokens 2.4.1\r\n",
+ "async-lru 2.0.4\r\n",
+ "attrs 23.2.0\r\n",
+ "Babel 2.14.0\r\n",
+ "beautifulsoup4 4.12.3\r\n",
+ "bleach 6.1.0\r\n",
+ "build 1.0.3\r\n",
+ "certifi 2024.2.2\r\n",
+ "cffi 1.16.0\r\n",
+ "cfgv 3.4.0\r\n",
+ "charset-normalizer 3.3.2\r\n",
+ "click 8.1.7\r\n",
+ "colorama 0.4.6\r\n",
+ "comm 0.2.1\r\n",
+ "contourpy 1.2.0\r\n",
+ "cycler 0.12.1\r\n",
+ "debugpy 1.8.1\r\n",
+ "decorator 5.1.1\r\n",
+ "defusedxml 0.7.1\r\n",
+ "dgl 2.0.0\r\n",
+ "distlib 0.3.8\r\n",
+ "executing 2.0.1\r\n",
+ "fastjsonschema 2.19.1\r\n",
+ "filelock 3.13.1\r\n",
+ "flake8 7.0.0\r\n",
+ "fonttools 4.49.0\r\n",
+ "fqdn 1.5.1\r\n",
+ "frozenlist 1.4.1\r\n",
+ "fsspec 2024.2.0\r\n",
+ "future 1.0.0\r\n",
+ "ghp-import 2.1.0\r\n",
+ "h11 0.14.0\r\n",
+ "httpcore 1.0.4\r\n",
+ "httpx 0.27.0\r\n",
+ "huggingface-hub 0.20.3\r\n",
+ "identify 2.5.35\r\n",
+ "idna 3.6\r\n",
+ "ipykernel 6.29.2\r\n",
+ "ipython 8.22.1\r\n",
+ "ipywidgets 8.1.2\r\n",
+ "isoduration 20.11.0\r\n",
+ "jarvis-tools 2023.12.12\r\n",
+ "jedi 0.19.1\r\n",
+ "Jinja2 3.1.3\r\n",
+ "joblib 1.3.2\r\n",
+ "json5 0.9.17\r\n",
+ "jsonpointer 2.4\r\n",
+ "jsonschema 4.21.1\r\n",
+ "jsonschema-specifications 2023.12.1\r\n",
+ "jupyter 1.0.0\r\n",
+ "jupyter_client 8.6.0\r\n",
+ "jupyter-console 6.6.3\r\n",
+ "jupyter_core 5.7.1\r\n",
+ "jupyter-events 0.9.0\r\n",
+ "jupyter-lsp 2.2.2\r\n",
+ "jupyter_server 2.12.5\r\n",
+ "jupyter_server_terminals 0.5.2\r\n",
+ "jupyterlab 4.1.2\r\n",
+ "jupyterlab_pygments 0.3.0\r\n",
+ "jupyterlab_server 2.25.3\r\n",
+ "jupyterlab_widgets 3.0.10\r\n",
+ "kiwisolver 1.4.5\r\n",
+ "latexcodec 2.0.1\r\n",
+ "lightning-utilities 0.10.1\r\n",
+ "Markdown 3.5.2\r\n",
+ "MarkupSafe 2.1.5\r\n",
+ "mat3ra-api-examples 0.1.0\r\n",
+ "matgl 0.9.2\r\n",
+ "matplotlib 3.8.3\r\n",
+ "matplotlib-inline 0.1.6\r\n",
+ "mccabe 0.7.0\r\n",
+ "mergedeep 1.3.4\r\n",
+ "mistune 3.0.2\r\n",
+ "mkdocs 1.5.3\r\n",
+ "mkdocs-material 9.5.10\r\n",
+ "mkdocs-material-extensions 1.3.1\r\n",
+ "monty 2024.2.2\r\n",
+ "mpmath 1.3.0\r\n",
+ "multidict 6.0.5\r\n",
+ "nbclient 0.9.0\r\n",
+ "nbconvert 7.16.1\r\n",
+ "nbformat 5.9.2\r\n",
+ "nest-asyncio 1.6.0\r\n",
+ "networkx 3.2.1\r\n",
+ "nglview 3.1.1\r\n",
+ "nodeenv 1.8.0\r\n",
+ "notebook 7.1.0\r\n",
+ "notebook_shim 0.2.4\r\n",
+ "numpy 1.26.4\r\n",
+ "overrides 7.7.0\r\n",
+ "packaging 23.2\r\n",
+ "paginate 0.5.6\r\n",
+ "palettable 3.3.3\r\n",
+ "pandas 2.2.0\r\n",
+ "pandocfilters 1.5.1\r\n",
+ "parso 0.8.3\r\n",
+ "pathspec 0.12.1\r\n",
+ "pexpect 4.9.0\r\n",
+ "pillow 10.2.0\r\n",
+ "pip 23.3.2\r\n",
+ "pip-tools 7.4.0\r\n",
+ "platformdirs 4.2.0\r\n",
+ "plotly 5.19.0\r\n",
+ "pre-commit 3.6.2\r\n",
+ "prometheus_client 0.20.0\r\n",
+ "prompt-toolkit 3.0.43\r\n",
+ "psutil 5.9.8\r\n",
+ "ptyprocess 0.7.0\r\n",
+ "pure-eval 0.2.2\r\n",
+ "pybtex 0.24.0\r\n",
+ "pycodestyle 2.11.1\r\n",
+ "pycparser 2.21\r\n",
+ "pydantic 1.8.1\r\n",
+ "pydocstyle 6.3.0\r\n",
+ "pyflakes 3.2.0\r\n",
+ "Pygments 2.17.2\r\n",
+ "pymatgen 2024.2.8\r\n",
+ "pymdown-extensions 10.7\r\n",
+ "pyparsing 2.4.7\r\n",
+ "pyproject_hooks 1.0.0\r\n",
+ "PyQt5 5.15.10\r\n",
+ "PyQt5-Qt5 5.15.12\r\n",
+ "PyQt5-sip 12.13.0\r\n",
+ "python-dateutil 2.8.2\r\n",
+ "python-json-logger 2.0.7\r\n",
+ "pytorch-ignite 0.5.0.dev20240223\r\n",
+ "pytorch-lightning 2.2.0.post0\r\n",
+ "pytz 2024.1\r\n",
+ "PyYAML 6.0.1\r\n",
+ "pyyaml_env_tag 0.1\r\n",
+ "pyzmq 25.1.2\r\n",
+ "qtconsole 5.5.1\r\n",
+ "QtPy 2.4.1\r\n",
+ "referencing 0.33.0\r\n",
+ "regex 2023.12.25\r\n",
+ "requests 2.31.0\r\n",
+ "rfc3339-validator 0.1.4\r\n",
+ "rfc3986-validator 0.1.1\r\n",
+ "rpds-py 0.18.0\r\n",
+ "ruamel.yaml 0.18.6\r\n",
+ "ruamel.yaml.clib 0.2.8\r\n",
+ "safetensors 0.4.2\r\n",
+ "scikit-learn 1.4.1.post1\r\n",
+ "scipy 1.12.0\r\n",
+ "Send2Trash 1.8.2\r\n",
+ "setuptools 69.0.3\r\n",
+ "six 1.16.0\r\n",
+ "sniffio 1.3.0\r\n",
+ "snowballstemmer 2.2.0\r\n",
+ "soupsieve 2.5\r\n",
+ "spglib 2.0.2\r\n",
+ "stack-data 0.6.3\r\n",
+ "sympy 1.12\r\n",
+ "tabulate 0.9.0\r\n",
+ "tenacity 8.2.3\r\n",
+ "terminado 0.18.0\r\n",
+ "threadpoolctl 3.3.0\r\n",
+ "tinycss2 1.2.1\r\n",
+ "toolz 0.12.1\r\n",
+ "torch 2.0.0\r\n",
+ "torchdata 0.7.1\r\n",
+ "torchmetrics 1.3.1\r\n",
+ "tornado 6.4\r\n",
+ "tqdm 4.66.2\r\n",
+ "traitlets 5.14.1\r\n",
+ "types-python-dateutil 2.8.19.20240106\r\n",
+ "typing_extensions 4.9.0\r\n",
+ "tzdata 2024.1\r\n",
+ "uncertainties 3.1.7\r\n",
+ "uri-template 1.3.0\r\n",
+ "urllib3 2.2.1\r\n",
+ "virtualenv 20.25.1\r\n",
+ "watchdog 4.0.0\r\n",
+ "wcwidth 0.2.13\r\n",
+ "webcolors 1.13\r\n",
+ "webencodings 0.5.1\r\n",
+ "websocket-client 1.7.0\r\n",
+ "wheel 0.42.0\r\n",
+ "widgetsnbextension 4.0.10\r\n",
+ "xmltodict 0.13.0\r\n",
+ "yarl 1.9.4\r\n",
+ "\r\n",
+ "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.3.2\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.0\u001B[0m\r\n",
+ "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpip install --upgrade pip\u001B[0m\r\n"
+ ]
+ }
+ ],
+ "source": [
+ "!python --version\n",
+ "!pip list\n",
+ "#!pip install pymatgen==2024.2.8 ase==3.22.1 nbformat==5.9.2 ipykernel==6.29.2 matgl==0.9.2 nglview==3.1.1"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 3. Create interfaces\n",
+ "\n",
+ "### 3.1. Extract Interfaces and Terminations\n",
+ "\n",
+ "Extract all possible layer/substrate supercell combinations within the maximum area including different terminations."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {
+ "tags": [],
+ "trusted": true,
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:40.318365Z",
+ "start_time": "2024-02-23T06:46:34.344521Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Full Formula (Ni1)\n",
+ "Reduced Formula: Ni\n",
+ "abc : 2.460000 2.460000 2.460000\n",
+ "angles: 60.000004 59.999994 60.000003\n",
+ "pbc : True True True\n",
+ "Sites (1)\n",
+ " # SP a b c\n",
+ "--- ---- --- --- ---\n",
+ " 0 Ni 0 0 0 \n",
+ "\n",
+ "Full Formula (C2)\n",
+ "Reduced Formula: C\n",
+ "abc : 2.467291 2.467291 20.000000\n",
+ "angles: 90.000000 90.000000 119.999986\n",
+ "pbc : True True True\n",
+ "Sites (2)\n",
+ " # SP a b c\n",
+ "--- ---- -------- -------- ---\n",
+ " 0 C 0 0 0\n",
+ " 1 C 0.333333 0.666667 0 \n",
+ "\n",
+ "Creating interfaces...\n"
+ ]
+ }
+ ],
+ "source": [
+ "from src.pymatgen_coherent_interface_builder import CoherentInterfaceBuilder, ZSLGenerator\n",
+ "from src.utils import to_pymatgen\n",
+ "\n",
+ "if \"materials_in\" in globals():\n",
+ " pymatgen_materials = [to_pymatgen(item) for item in materials_in]\n",
+ "for material in pymatgen_materials:\n",
+ " print(material, \"\\n\")\n",
+ "\n",
+ "\n",
+ "def create_interfaces(settings):\n",
+ " print(\"Creating interfaces...\")\n",
+ " zsl = ZSLGenerator(\n",
+ " max_area_ratio_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA_TOL\"],\n",
+ " max_area=settings[\"ZSL_PARAMETERS\"][\"MAX_AREA\"],\n",
+ " max_length_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_LENGTH_TOL\"],\n",
+ " max_angle_tol=settings[\"ZSL_PARAMETERS\"][\"MAX_ANGLE_TOL\"],\n",
+ " )\n",
+ "\n",
+ " cib = CoherentInterfaceBuilder(\n",
+ " substrate_structure=pymatgen_materials[settings[\"SUBSTRATE_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
+ " film_structure=pymatgen_materials[settings[\"LAYER_PARAMETERS\"][\"MATERIAL_INDEX\"]],\n",
+ " substrate_miller=settings[\"SUBSTRATE_PARAMETERS\"][\"MILLER_INDICES\"],\n",
+ " film_miller=settings[\"LAYER_PARAMETERS\"][\"MILLER_INDICES\"],\n",
+ " zslgen=zsl,\n",
+ " strain_tol=settings[\"ZSL_PARAMETERS\"][\"STRAIN_TOL\"],\n",
+ " )\n",
+ "\n",
+ " # Find terminations\n",
+ " cib._find_terminations()\n",
+ " terminations = cib.terminations\n",
+ "\n",
+ " # Create interfaces for each termination\n",
+ " interfaces = {}\n",
+ " for termination in terminations:\n",
+ " interfaces[termination] = []\n",
+ " for interface in cib.get_interfaces(\n",
+ " termination,\n",
+ " gap=settings[\"INTERFACE_PARAMETERS\"][\"DISTANCE_Z\"],\n",
+ " film_thickness=settings[\"LAYER_PARAMETERS\"][\"THICKNESS\"],\n",
+ " substrate_thickness=settings[\"SUBSTRATE_PARAMETERS\"][\"THICKNESS\"],\n",
+ " in_layers=True,\n",
+ " ):\n",
+ " # Wrap atoms to unit cell\n",
+ " interface[\"interface\"].make_supercell((1,1,1), to_unit_cell=True)\n",
+ " interfaces[termination].append(interface)\n",
+ " return interfaces, terminations\n",
+ "\n",
+ "\n",
+ "interfaces, terminations = create_interfaces(\n",
+ " settings={\n",
+ " \"SUBSTRATE_PARAMETERS\": SUBSTRATE_PARAMETERS,\n",
+ " \"LAYER_PARAMETERS\": LAYER_PARAMETERS,\n",
+ " \"ZSL_PARAMETERS\": ZSL_PARAMETERS,\n",
+ " \"INTERFACE_PARAMETERS\": INTERFACE_PARAMETERS,\n",
+ " }\n",
+ ")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 3.2. Print out the interfaces and terminations"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:40.325949Z",
+ "start_time": "2024-02-23T06:46:40.320976Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Found 1 terminations\n",
+ "Found 233 interfaces for ('C_P6/mmm_2', 'Ni_R-3m_1') termination\n"
+ ]
+ }
+ ],
+ "source": [
+ "print(f'Found {len(terminations)} terminations')\n",
+ "for termination in terminations:\n",
+ " print(f\"Found {len(interfaces[termination])} interfaces for\", termination, \"termination\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 4. Sort interfaces by strain\n",
+ "\n",
+ "### 4.1. Sort all interfaces"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:40.333235Z",
+ "start_time": "2024-02-23T06:46:40.325006Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "# Could be \"strain\", \"von_mises_strain\", \"mean_abs_strain\"\n",
+ "strain_mode = \"mean_abs_strain\"\n",
+ "\n",
+ "# Sort interfaces by the specified strain mode and number of sites\n",
+ "def sort_interfaces(interfaces, terminations):\n",
+ " sorted_interfaces = {}\n",
+ " for termination in terminations:\n",
+ " sorted_interfaces[termination] = sorted(\n",
+ " interfaces[termination], key=lambda x: (x[strain_mode], x[\"interface\"].num_sites)\n",
+ " )\n",
+ " return sorted_interfaces\n",
+ "\n",
+ "\n",
+ "sorted_interfaces = sort_interfaces(interfaces, terminations)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 4.2. Print out interfaces with lowest strain for each termination"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:40.340116Z",
+ "start_time": "2024-02-23T06:46:40.333147Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Interface with lowest strain for termination ('C_P6/mmm_2', 'Ni_R-3m_1') (index 0):\n",
+ " strain: 0.06600000000000002 %\n",
+ " number of atoms: 5\n"
+ ]
+ }
+ ],
+ "source": [
+ "for termination in terminations:\n",
+ " print(f\"Interface with lowest strain for termination {termination} (index 0):\")\n",
+ " first_interface = interfaces[termination][0]\n",
+ " print(\" strain:\", first_interface[strain_mode] * 100, \"%\")\n",
+ " print(\" number of atoms:\", first_interface[\"interface\"].num_sites)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 5. Plot the results\n",
+ "\n",
+ "Plot the number of atoms vs strain. Adjust the parameters as needed.\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:40.733358Z",
+ "start_time": "2024-02-23T06:46:40.345460Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/html": " \n "
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "application/vnd.plotly.v1+json": {
+ "data": [
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 0",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 0
Strain: 0.07%
Atoms: 5"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 5
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 1-9",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 1-9
Strain: 0.07%
Atoms: 10"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 10
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 10-19",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 10-19
Strain: 0.07%
Atoms: 15"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 15
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 20-38",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 20-38
Strain: 0.07%
Atoms: 20"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 20
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 39-56",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 39-56
Strain: 0.07%
Atoms: 25"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 25
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 57-110",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 57-110
Strain: 0.07%
Atoms: 30"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 30
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 111-132",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 111-132
Strain: 0.07%
Atoms: 35"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 35
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 133-177",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 133-177
Strain: 0.07%
Atoms: 40"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 40
+ ],
+ "type": "scatter"
+ },
+ {
+ "hoverinfo": "text",
+ "mode": "markers",
+ "name": "Indices: 178-232",
+ "text": [
+ "Termination: ('C_P6/mmm_2', 'Ni_R-3m_1')
Termination index: 0
Interfaces Index Range: 178-232
Strain: 0.07%
Atoms: 45"
+ ],
+ "x": [
+ 0.06600000000000002
+ ],
+ "y": [
+ 45
+ ],
+ "type": "scatter"
+ }
+ ],
+ "layout": {
+ "height": 600,
+ "hovermode": "closest",
+ "legend": {
+ "title": {
+ "text": "Interfaces Index Range"
+ }
+ },
+ "xaxis": {
+ "title": {
+ "text": "Strain (%)"
+ },
+ "type": "log"
+ },
+ "yaxis": {
+ "title": {
+ "text": "Number of atoms"
+ },
+ "type": "log"
+ },
+ "template": {
+ "data": {
+ "histogram2dcontour": [
+ {
+ "type": "histogram2dcontour",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "choropleth": [
+ {
+ "type": "choropleth",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ ],
+ "histogram2d": [
+ {
+ "type": "histogram2d",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "heatmap": [
+ {
+ "type": "heatmap",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "heatmapgl": [
+ {
+ "type": "heatmapgl",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "contourcarpet": [
+ {
+ "type": "contourcarpet",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ ],
+ "contour": [
+ {
+ "type": "contour",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "surface": [
+ {
+ "type": "surface",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ },
+ "colorscale": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ]
+ }
+ ],
+ "mesh3d": [
+ {
+ "type": "mesh3d",
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ ],
+ "scatter": [
+ {
+ "marker": {
+ "line": {
+ "color": "#283442"
+ }
+ },
+ "type": "scatter"
+ }
+ ],
+ "parcoords": [
+ {
+ "type": "parcoords",
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scatterpolargl": [
+ {
+ "type": "scatterpolargl",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "bar": [
+ {
+ "error_x": {
+ "color": "#f2f5fa"
+ },
+ "error_y": {
+ "color": "#f2f5fa"
+ },
+ "marker": {
+ "line": {
+ "color": "rgb(17,17,17)",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "bar"
+ }
+ ],
+ "scattergeo": [
+ {
+ "type": "scattergeo",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scatterpolar": [
+ {
+ "type": "scatterpolar",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "histogram": [
+ {
+ "marker": {
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "histogram"
+ }
+ ],
+ "scattergl": [
+ {
+ "marker": {
+ "line": {
+ "color": "#283442"
+ }
+ },
+ "type": "scattergl"
+ }
+ ],
+ "scatter3d": [
+ {
+ "type": "scatter3d",
+ "line": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scattermapbox": [
+ {
+ "type": "scattermapbox",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scatterternary": [
+ {
+ "type": "scatterternary",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "scattercarpet": [
+ {
+ "type": "scattercarpet",
+ "marker": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ }
+ }
+ ],
+ "carpet": [
+ {
+ "aaxis": {
+ "endlinecolor": "#A2B1C6",
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "minorgridcolor": "#506784",
+ "startlinecolor": "#A2B1C6"
+ },
+ "baxis": {
+ "endlinecolor": "#A2B1C6",
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "minorgridcolor": "#506784",
+ "startlinecolor": "#A2B1C6"
+ },
+ "type": "carpet"
+ }
+ ],
+ "table": [
+ {
+ "cells": {
+ "fill": {
+ "color": "#506784"
+ },
+ "line": {
+ "color": "rgb(17,17,17)"
+ }
+ },
+ "header": {
+ "fill": {
+ "color": "#2a3f5f"
+ },
+ "line": {
+ "color": "rgb(17,17,17)"
+ }
+ },
+ "type": "table"
+ }
+ ],
+ "barpolar": [
+ {
+ "marker": {
+ "line": {
+ "color": "rgb(17,17,17)",
+ "width": 0.5
+ },
+ "pattern": {
+ "fillmode": "overlay",
+ "size": 10,
+ "solidity": 0.2
+ }
+ },
+ "type": "barpolar"
+ }
+ ],
+ "pie": [
+ {
+ "automargin": true,
+ "type": "pie"
+ }
+ ]
+ },
+ "layout": {
+ "autotypenumbers": "strict",
+ "colorway": [
+ "#636efa",
+ "#EF553B",
+ "#00cc96",
+ "#ab63fa",
+ "#FFA15A",
+ "#19d3f3",
+ "#FF6692",
+ "#B6E880",
+ "#FF97FF",
+ "#FECB52"
+ ],
+ "font": {
+ "color": "#f2f5fa"
+ },
+ "hovermode": "closest",
+ "hoverlabel": {
+ "align": "left"
+ },
+ "paper_bgcolor": "rgb(17,17,17)",
+ "plot_bgcolor": "rgb(17,17,17)",
+ "polar": {
+ "bgcolor": "rgb(17,17,17)",
+ "angularaxis": {
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "ticks": ""
+ },
+ "radialaxis": {
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "ticks": ""
+ }
+ },
+ "ternary": {
+ "bgcolor": "rgb(17,17,17)",
+ "aaxis": {
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "ticks": ""
+ },
+ "baxis": {
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "ticks": ""
+ },
+ "caxis": {
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "ticks": ""
+ }
+ },
+ "coloraxis": {
+ "colorbar": {
+ "outlinewidth": 0,
+ "ticks": ""
+ }
+ },
+ "colorscale": {
+ "sequential": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ],
+ "sequentialminus": [
+ [
+ 0.0,
+ "#0d0887"
+ ],
+ [
+ 0.1111111111111111,
+ "#46039f"
+ ],
+ [
+ 0.2222222222222222,
+ "#7201a8"
+ ],
+ [
+ 0.3333333333333333,
+ "#9c179e"
+ ],
+ [
+ 0.4444444444444444,
+ "#bd3786"
+ ],
+ [
+ 0.5555555555555556,
+ "#d8576b"
+ ],
+ [
+ 0.6666666666666666,
+ "#ed7953"
+ ],
+ [
+ 0.7777777777777778,
+ "#fb9f3a"
+ ],
+ [
+ 0.8888888888888888,
+ "#fdca26"
+ ],
+ [
+ 1.0,
+ "#f0f921"
+ ]
+ ],
+ "diverging": [
+ [
+ 0,
+ "#8e0152"
+ ],
+ [
+ 0.1,
+ "#c51b7d"
+ ],
+ [
+ 0.2,
+ "#de77ae"
+ ],
+ [
+ 0.3,
+ "#f1b6da"
+ ],
+ [
+ 0.4,
+ "#fde0ef"
+ ],
+ [
+ 0.5,
+ "#f7f7f7"
+ ],
+ [
+ 0.6,
+ "#e6f5d0"
+ ],
+ [
+ 0.7,
+ "#b8e186"
+ ],
+ [
+ 0.8,
+ "#7fbc41"
+ ],
+ [
+ 0.9,
+ "#4d9221"
+ ],
+ [
+ 1,
+ "#276419"
+ ]
+ ]
+ },
+ "xaxis": {
+ "gridcolor": "#283442",
+ "linecolor": "#506784",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "#283442",
+ "automargin": true,
+ "zerolinewidth": 2
+ },
+ "yaxis": {
+ "gridcolor": "#283442",
+ "linecolor": "#506784",
+ "ticks": "",
+ "title": {
+ "standoff": 15
+ },
+ "zerolinecolor": "#283442",
+ "automargin": true,
+ "zerolinewidth": 2
+ },
+ "scene": {
+ "xaxis": {
+ "backgroundcolor": "rgb(17,17,17)",
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "#C8D4E3",
+ "gridwidth": 2
+ },
+ "yaxis": {
+ "backgroundcolor": "rgb(17,17,17)",
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "#C8D4E3",
+ "gridwidth": 2
+ },
+ "zaxis": {
+ "backgroundcolor": "rgb(17,17,17)",
+ "gridcolor": "#506784",
+ "linecolor": "#506784",
+ "showbackground": true,
+ "ticks": "",
+ "zerolinecolor": "#C8D4E3",
+ "gridwidth": 2
+ }
+ },
+ "shapedefaults": {
+ "line": {
+ "color": "#f2f5fa"
+ }
+ },
+ "annotationdefaults": {
+ "arrowcolor": "#f2f5fa",
+ "arrowhead": 0,
+ "arrowwidth": 1
+ },
+ "geo": {
+ "bgcolor": "rgb(17,17,17)",
+ "landcolor": "rgb(17,17,17)",
+ "subunitcolor": "#506784",
+ "showland": true,
+ "showlakes": true,
+ "lakecolor": "rgb(17,17,17)"
+ },
+ "title": {
+ "x": 0.05
+ },
+ "updatemenudefaults": {
+ "bgcolor": "#506784",
+ "borderwidth": 0
+ },
+ "sliderdefaults": {
+ "bgcolor": "#C8D4E3",
+ "borderwidth": 1,
+ "bordercolor": "rgb(17,17,17)",
+ "tickwidth": 0
+ },
+ "mapbox": {
+ "style": "dark"
+ }
+ }
+ }
+ },
+ "config": {
+ "plotlyServerURL": "https://plot.ly"
+ }
+ },
+ "text/html": ""
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Termination 0: ('C_P6/mmm_2', 'Ni_R-3m_1')\n"
+ ]
+ }
+ ],
+ "source": [
+ "import plotly.graph_objs as go\n",
+ "from collections import defaultdict\n",
+ "\n",
+ "PLOT_SETTINGS = {\n",
+ " \"HEIGHT\": 600,\n",
+ " \"X_SCALE\": \"log\", # or linear\n",
+ " \"Y_SCALE\": \"log\", # or linear\n",
+ "}\n",
+ "\n",
+ "\n",
+ "def plot_strain_vs_atoms(sorted_interfaces, terminations, settings):\n",
+ " # Create a mapping from termination to its index\n",
+ " termination_to_index = {termination: i for i, termination in enumerate(terminations)}\n",
+ "\n",
+ " grouped_interfaces = defaultdict(list)\n",
+ " for termination, interfaces in sorted_interfaces.items():\n",
+ " for index, interface_data in enumerate(interfaces):\n",
+ " strain_percentage = interface_data[\"mean_abs_strain\"] * 100\n",
+ " num_sites = interface_data[\"interface\"].num_sites\n",
+ " key = (strain_percentage, num_sites)\n",
+ " grouped_interfaces[key].append((index, termination))\n",
+ " \n",
+ " data = []\n",
+ " for (strain, num_sites), indices_and_terminations in grouped_interfaces.items():\n",
+ " termination_indices = defaultdict(list)\n",
+ " for index, termination in indices_and_terminations:\n",
+ " termination_indices[termination].append(index)\n",
+ " all_indices = [index for indices in termination_indices.values() for index in indices]\n",
+ " index_range = f\"{min(all_indices)}-{max(all_indices)}\" if len(all_indices) > 1 else str(min(all_indices))\n",
+ " \n",
+ " hover_text = \"
-----
\".join(\n",
+ " f\"Termination: {termination}
Termination index: {termination_to_index[termination]}
Interfaces Index Range: {index_range}
Strain: {strain:.2f}%
Atoms: {num_sites}\"\n",
+ " for termination, indices in termination_indices.items()\n",
+ " )\n",
+ " trace = go.Scatter(\n",
+ " x=[strain],\n",
+ " y=[num_sites],\n",
+ " text=[hover_text],\n",
+ " mode=\"markers\",\n",
+ " hoverinfo=\"text\",\n",
+ " name=f\"Indices: {index_range}\",\n",
+ " )\n",
+ " data.append(trace)\n",
+ "\n",
+ " layout = go.Layout(\n",
+ " xaxis=dict(title=\"Strain (%)\", type=settings[\"X_SCALE\"]),\n",
+ " yaxis=dict(title=\"Number of atoms\", type=settings[\"Y_SCALE\"]),\n",
+ " hovermode=\"closest\",\n",
+ " height=settings[\"HEIGHT\"],\n",
+ " legend_title_text=\"Interfaces Index Range\",\n",
+ " )\n",
+ " fig = go.Figure(data=data, layout=layout)\n",
+ " fig.show()\n",
+ "\n",
+ "\n",
+ "\n",
+ "plot_strain_vs_atoms(sorted_interfaces, terminations, PLOT_SETTINGS)\n",
+ "\n",
+ "for i, termination in enumerate(terminations):\n",
+ " print(f\"Termination {i}:\", termination)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 6. Select the interface to relax\n",
+ "\n",
+ "### 6.1. Select the interface with the desired termination and strain\n",
+ "\n",
+ "The data in `sorted_interfaces` now contains an object with the following structure:\n",
+ "\n",
+ "```json\n",
+ "{\n",
+ " \"('C_P6/mmm_2', 'Si_R-3m_1')\": [\n",
+ " { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 0...},\n",
+ " { ...interface for ('C_P6/mmm_2', 'Si_R-3m_1') at index 1...},\n",
+ " ...\n",
+ " ],\n",
+ " \"\": [\n",
+ " { ...interface for 'termination at index 1' at index 0...},\n",
+ " { ...interface for 'termination at index 1' at index 1...},\n",
+ " ...\n",
+ " ]\n",
+ "}\n",
+ "```\n",
+ "\n",
+ "Select the index for termination first, and for it - the index in the list of corresponding interfaces sorted by strain (index 0 has minimum strain)."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:40.733819Z",
+ "start_time": "2024-02-23T06:46:40.727365Z"
+ }
+ },
+ "outputs": [],
+ "source": [
+ "termination_index = 0\n",
+ "interface_index = 0\n",
+ "\n",
+ "termination = terminations[termination_index]\n",
+ "\n",
+ "interface = sorted_interfaces[termination][interface_index][\"interface\"]"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "## 7. Apply relaxation\n",
+ "### 7.1. Apply relaxation to the selected interface"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 17,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T07:03:26.118896Z",
+ "start_time": "2024-02-23T07:03:22.015685Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Users/mat3ra/code/GREEN/api-examples/.venv/lib/python3.11/site-packages/matgl/apps/pes.py:60: UserWarning:\n",
+ "\n",
+ "To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n",
+ "\n",
+ "/Users/mat3ra/code/GREEN/api-examples/.venv/lib/python3.11/site-packages/matgl/apps/pes.py:66: UserWarning:\n",
+ "\n",
+ "To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n",
+ "\n",
+ "/Users/mat3ra/code/GREEN/api-examples/.venv/lib/python3.11/site-packages/matgl/apps/pes.py:67: UserWarning:\n",
+ "\n",
+ "To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n",
+ "\n",
+ "/Users/mat3ra/code/GREEN/api-examples/.venv/lib/python3.11/site-packages/matgl/layers/_basis.py:121: UserWarning:\n",
+ "\n",
+ "To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n",
+ "\n"
+ ]
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "Ni3 C2\n",
+ "1.0\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ "Ni C\n",
+ "3 2\n",
+ "direct\n",
+ " 0.3333334309041891 0.3333336930861107 0.0000000000000000 Ni\n",
+ " 0.0000001463562833 0.0000005396291662 0.0743446506413096 Ni\n",
+ " 0.6666668618083782 0.6666673861722214 0.1486893012826191 Ni\n",
+ " 0.0000000000000000 0.0000000000000000 0.2597298272022775 C\n",
+ " 0.3333330000000001 0.3333330000000000 0.2597298272022775 C\n",
+ "\n",
+ "Ni3 C2\n",
+ "1.0\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ "Ni C\n",
+ "3 2\n",
+ "direct\n",
+ " 0.3333340134487732 0.3333330807328738 -0.0170757790468935 Ni\n",
+ " 0.0000006233571625 0.0000010932091160 0.0579943255656671 Ni\n",
+ " 0.6666659312513874 0.6666678563754861 0.1329532574037621 Ni\n",
+ " -0.0000348881345262 -0.0000347332523320 0.2843109022203070 C\n",
+ " 0.3333679848948666 0.3333671611812928 0.2843109029243810 C\n",
+ "\n",
+ "The final energy is -34.621 eV.\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Per https://github.com/materialsvirtuallab/matgl/blob/main/examples/Relaxations%20and%20Simulations%20using%20the%20M3GNet%20Universal%20Potential.ipynb\n",
+ "from src.utils import pymatgen_to_ase\n",
+ "\n",
+ "import matgl\n",
+ "\n",
+ "from pymatgen.io.ase import AseAtomsAdaptor\n",
+ "from matgl.ext.ase import M3GNetCalculator, Relaxer\n",
+ "from matgl.apps.pes import Potential\n",
+ "ase_adaptor = AseAtomsAdaptor()\n",
+ "\n",
+ "pot = matgl.load_model(\"M3GNet-MP-2021.2.8-PES\")\n",
+ "Calculator = M3GNetCalculator(pot)\n",
+ "\n",
+ "def get_energy(structure, pot: Potential):\n",
+ " atoms = ase_adaptor.get_atoms(structure)\n",
+ " calc = M3GNetCalculator(pot)\n",
+ " atoms.set_calculator(calc)\n",
+ " return float(atoms.get_potential_energy())\n",
+ "\n",
+ "\n",
+ "relaxer = Relaxer(potential=pot, relax_cell=False)\n",
+ "relax_results = relaxer.relax(interface, fmax=0.01)\n",
+ "\n",
+ "# extract results\n",
+ "relaxed_interface = relax_results[\"final_structure\"]\n",
+ "relaxed_energy = relax_results[\"trajectory\"].energies[-1]\n",
+ "\n",
+ "ase_original_interface = pymatgen_to_ase(interface)\n",
+ "ase_final_interface = pymatgen_to_ase(relaxed_interface)\n",
+ "\n",
+ "# print out the final relaxed structure and energy\n",
+ "print(interface.to(fmt=\"poscar\"))\n",
+ "print(relaxed_interface.to(fmt=\"poscar\"))\n",
+ "\n",
+ "print(f\"The final energy is {float(relaxed_energy):.3f} eV.\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 7.2. View structure before and after relaxation"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 18,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T07:03:28.832592Z",
+ "start_time": "2024-02-23T07:03:28.627358Z"
+ }
+ },
+ "outputs": [
+ {
+ "data": {
+ "text/plain": "",
+ "text/html": "\n
\n
Ni3C2 - original
\n

\n
\n \n
\n
Ni3C2 - relaxed
\n

\n
\n
"
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "import base64\n",
+ "from ase.io import write\n",
+ "from ase.build import make_supercell\n",
+ "from IPython.display import HTML\n",
+ "import io\n",
+ "\n",
+ "def visualize_material_base64(material, title: str, rotation: str = '0x', number_of_repetitions: int = 3):\n",
+ " \"\"\"\n",
+ " Returns an HTML string with a Base64-encoded image for visualization,\n",
+ " including the name of the file, positioned horizontally.\n",
+ " \"\"\"\n",
+ " # Set the number of unit cell repetition for the structure\n",
+ " n = number_of_repetitions\n",
+ " material_repeat = make_supercell(material, [[n,0,0],[0,n,0],[0,0,1]])\n",
+ " text = f\"{material.symbols} - {title}\"\n",
+ " \n",
+ " # Write image to a buffer to display in HTML\n",
+ " buf = io.BytesIO()\n",
+ " write(buf, material_repeat, format='png', rotation=rotation)\n",
+ " buf.seek(0)\n",
+ " img_str = base64.b64encode(buf.read()).decode('utf-8')\n",
+ " html_str = f'''\n",
+ " \n",
+ "
{text}
\n",
+ "

\n",
+ "
\n",
+ " '''\n",
+ " return html_str\n",
+ "\n",
+ "html_original = visualize_material_base64(ase_original_interface, \"original\", \"-90x\")\n",
+ "html_relaxed = visualize_material_base64(ase_final_interface, \"relaxed\", \"-90x\")\n",
+ "\n",
+ "# Display the interfaces before and after relaxation\n",
+ "html_content = f'{html_original}{html_relaxed}
'\n",
+ "display(HTML(html_content))\n",
+ "\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "### 7.3. Calculate energy energy using matgl M3GNet"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 19,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T07:03:30.691565Z",
+ "start_time": "2024-02-23T07:03:30.534663Z"
+ }
+ },
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "5.240839288055053\n",
+ "5\n",
+ "3\n",
+ "2\n",
+ "Relaxed interface energy: -34.6208 eV\n",
+ "Original Substrate energy: -16.3230 eV\n",
+ "Original Layer energy: -18.2532 eV\n",
+ "Original Delta: -0.0446 eV\n",
+ "Original Delta per area: -0.0085 eV/Ang^2\n",
+ "Relaxed Substrate energy: -16.3256 eV\n",
+ "Relaxed Layer energy: -18.2532 eV\n",
+ "Relaxed Delta: -0.0420 eV\n",
+ "Relaxed Delta per area: -0.0080 eV/Ang^2\n",
+ "Effective relaxed Delta per area: -1.6719 eV/Ang^2\n",
+ "Ni C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni C \n",
+ " 3 2\n",
+ "Cartesian\n",
+ " 1.2300010628171087 0.7101404101213661 -0.4613391076492668\n",
+ " 0.0000028781050507 0.0000023289976749 1.5668421529530936\n",
+ " 2.4599990550837409 1.4202844311171587 3.5920198405749182\n",
+ " -0.0001285466800217 -0.0000739965142423 7.6812740177225667\n",
+ " 1.2301265514954720 0.7102130158872388 7.6812740367446484\n",
+ "\n",
+ "Ni \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni \n",
+ " 3\n",
+ "Cartesian\n",
+ " 1.2300010628171087 0.7101404101213661 -0.4613391076492668\n",
+ " 0.0000028781050507 0.0000023289976749 1.5668421529530936\n",
+ " 2.4599990550837409 1.4202844311171587 3.5920198405749182\n",
+ "\n",
+ " C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " C \n",
+ " 2\n",
+ "Cartesian\n",
+ " -0.0001285466800217 -0.0000739965142423 7.6812740177225667\n",
+ " 1.2301265514954720 0.7102130158872388 7.6812740367446484\n",
+ "\n",
+ "Ni \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " Ni \n",
+ " 3\n",
+ "Cartesian\n",
+ " 1.2300003829518735 0.7101417146926911 0.0000000000000001\n",
+ " 0.0000010237800144 0.0000011496383034 2.0085815523361399\n",
+ " 2.4600007659037471 1.4202834293853821 4.0171631046722771\n",
+ "\n",
+ " C \n",
+ " 1.0000000000000000\n",
+ " 2.4599995727812636 0.0000000000000000 0.0000000000000002\n",
+ " 1.2299995285143266 2.1304228448014668 0.0000000000000002\n",
+ " 0.0000000000000000 0.0000000000000000 27.0171631046722780\n",
+ " C \n",
+ " 2\n",
+ "Cartesian\n",
+ " 0.0000000000000000 0.0000000000000000 7.0171631046722780\n",
+ " 1.2299984704321631 0.7101402381262073 7.0171631046722780\n"
+ ]
+ }
+ ],
+ "source": [
+ "from src.utils import ase_to_poscar\n",
+ "from ase import Atoms\n",
+ "nickel_relaxed_interface = ase_final_interface.copy()\n",
+ "graphene_relaxed_interface = ase_final_interface.copy()\n",
+ "nickel_original_interface = ase_original_interface.copy()\n",
+ "graphene_original_interface = ase_original_interface.copy()\n",
+ "\n",
+ "del nickel_relaxed_interface[[atom.index for atom in nickel_relaxed_interface if atom.symbol != 'Ni']]\n",
+ "del graphene_relaxed_interface[[atom.index for atom in graphene_relaxed_interface if atom.symbol != 'C']]\n",
+ "del nickel_original_interface[[atom.index for atom in nickel_original_interface if atom.symbol != 'Ni']]\n",
+ "del graphene_original_interface[[atom.index for atom in graphene_original_interface if atom.symbol != 'C']]\n",
+ "\n",
+ "#set EMT calculator\n",
+ "nickel_relaxed_interface.set_calculator(Calculator)\n",
+ "graphene_relaxed_interface.set_calculator(Calculator)\n",
+ "nickel_original_interface.set_calculator(Calculator)\n",
+ "graphene_original_interface.set_calculator(Calculator)\n",
+ "\n",
+ "original_substrate_energy = nickel_original_interface.get_total_energy()\n",
+ "original_layer_energy = graphene_original_interface.get_total_energy()\n",
+ "relaxed_substrate_energy = nickel_relaxed_interface.get_total_energy()\n",
+ "relaxed_layer_energy = graphene_relaxed_interface.get_total_energy()\n",
+ "\n",
+ "delta_original = relaxed_energy - original_substrate_energy - original_layer_energy\n",
+ "delta_relaxed = relaxed_energy - relaxed_substrate_energy - relaxed_layer_energy\n",
+ "\n",
+ "# calculate area of interface\n",
+ "area = ase_original_interface.get_volume() / ase_original_interface.cell[2, 2]\n",
+ "print(area)\n",
+ "\n",
+ "effective_delta_relaxed = (relaxed_energy * ase_final_interface.get_global_number_of_atoms() -\n",
+ " nickel_relaxed_interface.get_global_number_of_atoms() * relaxed_substrate_energy -\n",
+ " graphene_relaxed_interface.get_global_number_of_atoms() * relaxed_layer_energy)/(2 * area * ase_final_interface.get_global_number_of_atoms())\n",
+ "print(ase_final_interface.get_global_number_of_atoms())\n",
+ "print(nickel_relaxed_interface.get_global_number_of_atoms())\n",
+ "print(graphene_relaxed_interface.get_global_number_of_atoms())\n",
+ "# print(f\"Original interface energy: {ase_original_interface.get_total_energy():.4f} eV\")\n",
+ "print(f\"Relaxed interface energy: {relaxed_energy:.4f} eV\")\n",
+ "print(f\"Original Substrate energy: {original_substrate_energy:.4f} eV\")\n",
+ "print(f\"Original Layer energy: {original_layer_energy:.4f} eV\")\n",
+ "print(f\"Original Delta: {delta_original:.4f} eV\")\n",
+ "print(f\"Original Delta per area: {delta_original / area:.4f} eV/Ang^2\")\n",
+ "print(f\"Relaxed Substrate energy: {relaxed_substrate_energy:.4f} eV\")\n",
+ "print(f\"Relaxed Layer energy: {relaxed_layer_energy:.4f} eV\")\n",
+ "print(f\"Relaxed Delta: {delta_relaxed:.4f} eV\")\n",
+ "print(f\"Relaxed Delta per area: {delta_relaxed / area:.4f} eV/Ang^2\")\n",
+ "print(f\"Effective relaxed Delta per area: {effective_delta_relaxed:.4f} eV/Ang^2\")\n",
+ "\n",
+ "\n",
+ "print(ase_to_poscar(ase_final_interface))\n",
+ "print(ase_to_poscar(nickel_relaxed_interface))\n",
+ "print(ase_to_poscar(graphene_relaxed_interface))\n",
+ "print(ase_to_poscar(nickel_original_interface))\n",
+ "print(ase_to_poscar(graphene_original_interface))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {
+ "ExecuteTime": {
+ "end_time": "2024-02-23T06:46:46.857335Z",
+ "start_time": "2024-02-23T06:46:46.853894Z"
+ }
+ },
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "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.10.12"
+ },
+ "widgets": {
+ "application/vnd.jupyter.widget-state+json": {
+ "state": {},
+ "version_major": 2,
+ "version_minor": 0
+ }
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/other/materials_designer/pickling.ipynb b/other/materials_designer/pickling.ipynb
new file mode 100644
index 00000000..1d222be7
--- /dev/null
+++ b/other/materials_designer/pickling.ipynb
@@ -0,0 +1,127 @@
+{
+ "cells": [
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "b'ccopy_reg\\n_reconstructor\\np0\\n(ccloudpickle.cloudpickle\\n_make_skeleton_class\\np1\\n(c__builtin__\\ntype\\np2\\nVTest\\np3\\n(c__builtin__\\nobject\\np4\\ntp5\\n(dp6\\nV__module__\\np7\\nV__main__\\np8\\nsV9c9d2d434b1f404c97232f342818c56a\\np9\\nNtp10\\nRp11\\nccloudpickle.cloudpickle\\n_class_setstate\\np12\\ng11\\n((dp13\\ng7\\ng8\\nsV__init__\\np14\\nccloudpickle.cloudpickle\\n_make_function\\np15\\n(ccloudpickle.cloudpickle\\n_builtin_type\\np16\\n(VCodeType\\np17\\ntp18\\nRp19\\n(I1\\nI0\\nI0\\nI3\\nI1\\nI15\\nc_codecs\\nencode\\np20\\n(V\\x97\\\\u0000d\\\\u0000S\\\\u0000\\np21\\nVlatin1\\np22\\ntp23\\nRp24\\n(Ntp25\\n(t(Vself\\np26\\nVargs\\np27\\nVkwargs\\np28\\ntp29\\nV/var/folders/p2/dwnyx6w903d1s5rxs9br261m0000gn/T/ipykernel_9439/1462118015.py\\np30\\ng14\\nVTest.__init__\\np31\\nI3\\ng20\\n(V\\x80\\\\u0000\\xd8\\x08\\x0b\\x88\\x03\\np32\\ng22\\ntp33\\nRp34\\nc__builtin__\\nbytes\\np35\\n(tRp36\\n(t(ttp37\\nRp38\\n(dp39\\nV__package__\\np40\\nNsV__name__\\np41\\ng8\\nsNNNtp42\\nRp43\\nccloudpickle.cloudpickle\\n_function_setstate\\np44\\ng43\\n((dp45\\n(dp46\\ng41\\ng14\\nsV__qualname__\\np47\\ng31\\nsV__annotations__\\np48\\n(dp49\\nsV__kwdefaults__\\np50\\nNsV__defaults__\\np51\\nNsg7\\ng8\\nsV__doc__\\np52\\nNsV__closure__\\np53\\nNsV_cloudpickle_submodules\\np54\\n(lp55\\nsV__globals__\\np56\\n(dp57\\nstp58\\n\\x86R0sVget\\np59\\ng15\\n(g19\\n(I2\\nI0\\nI0\\nI2\\nI1\\nI3\\ng20\\n(V\\x97\\\\u0000|\\x01S\\\\u0000\\np60\\ng22\\ntp61\\nRp62\\ng25\\n(t(g26\\nVvalue\\np63\\ntp64\\ng30\\ng59\\nVTest.get\\np65\\nI5\\ng20\\n(V\\x80\\\\u0000\\xd8\\x0f\\x14\\x88\\x0c\\np66\\ng22\\ntp67\\nRp68\\ng36\\n(t(ttp69\\nRp70\\ng39\\nNNNtp71\\nRp72\\ng44\\ng72\\n((dp73\\n(dp74\\ng41\\ng59\\nsg47\\ng65\\nsg48\\n(dp75\\nsg50\\nNsg51\\n(I10\\ntp76\\nsg7\\ng8\\nsg52\\nNsg53\\nNsg54\\n(lp77\\nsg56\\n(dp78\\nstp79\\n\\x86R0sg52\\nNsV__slotnames__\\np80\\n(lp81\\ns(dp82\\ntp83\\n\\x86R0g4\\nNtp84\\nRp85\\n.'"
+ ]
+ },
+ "execution_count": 3,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\u001b[0;31mSignature:\u001b[0m \u001b[0mpickle\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdumps\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mobj\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mprotocol\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbuffer_callback\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+ "\u001b[0;31mDocstring:\u001b[0m\n",
+ "Serialize obj as a string of bytes allocated in memory\n",
+ "\n",
+ "protocol defaults to cloudpickle.DEFAULT_PROTOCOL which is an alias to\n",
+ "pickle.HIGHEST_PROTOCOL. This setting favors maximum communication\n",
+ "speed between processes running the same Python version.\n",
+ "\n",
+ "Set protocol=pickle.DEFAULT_PROTOCOL instead if you need to ensure\n",
+ "compatibility with older versions of Python (although this is not always\n",
+ "guaranteed to work because cloudpickle relies on some internal\n",
+ "implementation details that can change from one Python version to the\n",
+ "next).\n",
+ "\u001b[0;31mFile:\u001b[0m ~/code/green/api-examples/.venv-3.11/lib/python3.11/site-packages/cloudpickle/cloudpickle.py\n",
+ "\u001b[0;31mType:\u001b[0m function"
+ ]
+ }
+ ],
+ "source": [
+ "import cloudpickle as pickle\n",
+ "\n",
+ "class Test:\n",
+ " def __init__(self, *args, **kwargs):\n",
+ " ...\n",
+ " def get(self, value=10):\n",
+ " return value\n",
+ "\n",
+ "test_obj = Test()\n",
+ "\n",
+ "with open(\"test-3.11.pkl\", \"wb\") as f:\n",
+ " pickle.dump(test_obj, f)\n"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "\n"
+ ]
+ },
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/Users/mat3ra/code/green/api-examples/.venv-3.11/lib/python3.11/site-packages/matgl/apps/pes.py:60: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n",
+ " self.element_refs = AtomRef(property_offset=torch.tensor(element_refs, dtype=matgl.float_th))\n",
+ "/Users/mat3ra/code/green/api-examples/.venv-3.11/lib/python3.11/site-packages/matgl/apps/pes.py:66: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n",
+ " self.register_buffer(\"data_mean\", torch.tensor(data_mean, dtype=matgl.float_th))\n",
+ "/Users/mat3ra/code/green/api-examples/.venv-3.11/lib/python3.11/site-packages/matgl/apps/pes.py:67: UserWarning: To copy construct from a tensor, it is recommended to use sourceTensor.clone().detach() or sourceTensor.clone().detach().requires_grad_(True), rather than torch.tensor(sourceTensor).\n",
+ " self.register_buffer(\"data_std\", torch.tensor(data_std, dtype=matgl.float_th))\n"
+ ]
+ }
+ ],
+ "source": [
+ "import matgl\n",
+ "from matgl.ext.ase import M3GNetCalculator\n",
+ "from matgl.ext.ase import M3GNetCalculator\n",
+ "\n",
+ "import cloudpickle\n",
+ "\n",
+ "pot = matgl.load_model(\"M3GNet-MP-2021.2.8-PES\")\n",
+ "\n",
+ "# Save the calculator to pickle for use in the Pyodide environment\n",
+ "calculator = M3GNetCalculator(pot)\n",
+ "print(calculator)\n",
+ "\n",
+ "# with open(\"m3gnet_calculator_3.11_32.pkl\", \"wb\") as f:\n",
+ "# cloudpickle.dump(calculator, f, protocol=3)\n",
+ " "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": ".venv-3.11",
+ "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.11.3"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/other/materials_designer/src/utils.py b/other/materials_designer/src/utils.py
index 89b971f6..85d3da5a 100644
--- a/other/materials_designer/src/utils.py
+++ b/other/materials_designer/src/utils.py
@@ -104,3 +104,17 @@ def ase_to_poscar(atoms: ase_Atoms):
output.close()
return content
+
+def ase_to_pymatgen(atoms: ase_Atoms):
+ poscar = ase_to_poscar(atoms)
+ structure = Structure.from_str(poscar, fmt="poscar")
+
+ return structure
+
+def pymatgen_to_ase(structure: Structure):
+ poscar = structure.to(fmt="poscar")
+ atoms = poscar_to_ase(poscar)
+
+ return atoms
+
+
diff --git a/other/materials_designer/test-cloud.pkl b/other/materials_designer/test-cloud.pkl
new file mode 100644
index 00000000..b24f0e02
Binary files /dev/null and b/other/materials_designer/test-cloud.pkl differ