diff --git a/other/materials_designer/specific_examples/Introduction.ipynb b/other/materials_designer/specific_examples/Introduction.ipynb index 71075fbc4..705d11224 100644 --- a/other/materials_designer/specific_examples/Introduction.ipynb +++ b/other/materials_designer/specific_examples/Introduction.ipynb @@ -25,7 +25,7 @@ "| `C-2D-HST` | Heterostack | [Si/SiO₂/HfO₂/TiN Heterostructure](heterostructure_silicon_silicon_dioxide_hafnium_dioxide_titanium_nitride.ipynb) | *To be added* | [[3]](#ref3) |\n", "| `C-2D-INT-S` | Interface Simple | *To be added* | — | — |\n", "| `C-2D-INT-Z` | Interface ZSL | [BN/Graphene 2D–2D Interface](interface_2d_2d_boron_nitride_graphene.ipynb) | *To be added* | [[4]](#ref4) |\n", - "| `C-2D-INT-Z` | Interface ZSL | [Graphene/SiO₂ 2D–3D Interface](interface_2d_3d_graphene_silicon_dioxide.ipynb) | *To be added* | [[5]](#ref5) |\n", + "| `C-2D-INT-Z` | Interface ZSL | [Graphene/SiO₂ 2D–3D Interface](interface_2d_3d_graphene_silicon_dioxide.ipynb) | [Doping and Gap at the Dirac Point](interface_2d_3d_graphene_silicon_dioxide_SIMULATION.ipynb) | [[5]](#ref5) |\n", "| `C-2D-INT-Z` | Interface ZSL | [Cu/Cristobalite 3D–3D Interface](interface_3d_3d_copper_cristobalite.ipynb) | *To be added* | [[6]](#ref6) |\n", "| `C-2D-INT-Z` | Interface ZSL | [Graphene/Ni Interface Film XY Position Optimization](optimization_interface_film_xy_position_graphene_nickel.ipynb) | [Gr/Ni(111) Registry and Work of Adhesion](optimization_interface_film_xy_position_graphene_nickel_SIMULATION.ipynb) | [[7]](#ref7) |\n", "| `C-2D-INT-T` | Interface Twisted | *To be added* | — | — |\n", diff --git a/other/materials_designer/specific_examples/interface_2d_3d_graphene_silicon_dioxide.ipynb b/other/materials_designer/specific_examples/interface_2d_3d_graphene_silicon_dioxide.ipynb index 99b913da3..69546b8a4 100644 --- a/other/materials_designer/specific_examples/interface_2d_3d_graphene_silicon_dioxide.ipynb +++ b/other/materials_designer/specific_examples/interface_2d_3d_graphene_silicon_dioxide.ipynb @@ -18,7 +18,7 @@ "\n", "\n", "\n", - "Replicating the materials from the manuscript, FIG. 1. (b):\n", + "Replicating the metastable geometry of Kang et al. Sec. III: graphene 2.58 Å above the O-terminated surface, shifted from the C-over-O registry of Fig. 1(b):\n", "\n", "\n" ] @@ -45,12 +45,16 @@ "FILM_VACUUM = 0.0 # in angstroms\n", "\n", "SUBSTRATE_MILLER_INDICES = (0, 0, 1)\n", - "SUBSTRATE_THICKNESS = 7 # in atomic layers (for 14 bilayers -- from manuscript)\n", + "SUBSTRATE_THICKNESS = 5 # conventional cells along c: 15 Si planes; the manuscript has 14 bilayers\n", "SUBSTRATE_TERMINATION_FORMULA = None # if None, the first termination will be used\n", "SUBSTRATE_VACUUM = 0.0 # in angstroms\n", "\n", "INTERFACE_DISTANCE = 2.58 # Gap between substrate and film, in Angstrom -- from manuscript\n", - "INTERFACE_VACUUM = 20.0 # Vacuum over film, in Angstrom -- from manuscript\n", + "# The slab is centered, so 17.5 gives about 20 Å between periodic images, 10 Å on each side, as in the manuscript\n", + "INTERFACE_VACUUM = 17.5 # in Angstrom\n", + "# Å, in-plane; the closest a rigid shift gets to the manuscript's metastable registry (Sec. III)\n", + "# 3.5 prints each surface O's distance to the nearest C (0.354 / 1.095 Å)\n", + "REGISTRY_SHIFT = [-1.011, -0.725, 0.0]\n", "\n", "# Whether to convert materials to conventional cells before creating slabs.\n", "USE_CONVENTIONAL_CELL = True\n", @@ -337,6 +341,62 @@ ")\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3.5. Shift graphene to the metastable registry\n", + "The ZSL match leaves the registry undefined; as built, both surface O atoms sit near a C atom. The film is shifted in-plane so that one surface O sits near a C atom and the other near a hexagon centre, the registry of the manuscript's metastable geometry (Sec. III).\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from mat3ra.made.tools.modify import interface_displace_part\n", + "\n", + "interface = interface_displace_part(interface, displacement=REGISTRY_SHIFT, use_cartesian_coordinates=True)\n", + "\n", + "interface_in_cartesian = interface.clone()\n", + "interface_in_cartesian.to_cartesian()\n", + "coordinates = np.array(interface_in_cartesian.basis.coordinates.values)\n", + "elements = np.array(interface.basis.elements.values)\n", + "cell_xy = np.array(interface.lattice.vector_arrays)[:2, :2]\n", + "carbons = coordinates[elements == \"C\"]\n", + "oxygens = coordinates[elements == \"O\"]\n", + "for oxygen in oxygens[np.argsort(-oxygens[:, 2])[:2]]:\n", + " distances = [np.linalg.norm(oxygen[:2] - carbon[:2] - i * cell_xy[0] - j * cell_xy[1])\n", + " for carbon in carbons for i in (-1, 0, 1) for j in (-1, 0, 1)]\n", + " print(f\"surface O at z = {oxygen[2]:.3f} Å: nearest C in the plane {min(distances):.3f} Å\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3.6. Set the cell to the standard hexagonal setting and center the slab\n", + "The ZSL interface cell comes out with γ = 60°. The cell is put in the 120° hexagonal setting (same atoms) and typed HEX, so the symbolic K point of the band path in [interface_2d_3d_graphene_silicon_dioxide_SIMULATION.ipynb](interface_2d_3d_graphene_silicon_dioxide_SIMULATION.ipynb) is graphene's K. The slab is then centered along z, so that no atom sits at z = 0, where a relaxation would wrap it to the top of the cell.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.tools.helpers import create_supercell\n", + "from mat3ra.made.tools.modify import translate_to_center\n", + "\n", + "interface = create_supercell(interface, supercell_matrix=[[1, 0, 0], [-1, 1, 0], [0, 0, 1]])\n", + "interface = translate_to_center(interface, axes=[\"z\"])\n", + "interface.lattice.type = \"HEX\"\n", + "print(f\"{interface.basis.number_of_atoms} atoms, a = {interface.lattice.a:.4f} Å, \"\n", + " f\"gamma = {interface.lattice.gamma:.1f}°\")" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -367,7 +427,8 @@ "metadata": {}, "outputs": [], "source": [ - "from mat3ra.notebooks_utils.material import set_materials, download_content_to_file\n", + "from mat3ra.notebooks_utils.io import download_content_to_file\n", + "from mat3ra.notebooks_utils.material import set_materials\n", "\n", "set_materials(interface)\n", "download_content_to_file(interface.to_json(), f\"{interface.name}.json\")" diff --git a/other/materials_designer/specific_examples/interface_2d_3d_graphene_silicon_dioxide_SIMULATION.ipynb b/other/materials_designer/specific_examples/interface_2d_3d_graphene_silicon_dioxide_SIMULATION.ipynb new file mode 100644 index 000000000..5aabf84f8 --- /dev/null +++ b/other/materials_designer/specific_examples/interface_2d_3d_graphene_silicon_dioxide_SIMULATION.ipynb @@ -0,0 +1,594 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Doping and Gap at the Dirac Point of Graphene on SiO2\n", + "\n", + "> **Yong-Ju Kang, Joongoo Kang, and K. J. Chang**, \"Electronic structure of graphene and doping\n", + "> effect on SiO2\", Physical Review B 78, 115404 (2008).\n", + "> [DOI:10.1103/PhysRevB.78.115404](https://doi.org/10.1103/PhysRevB.78.115404)\n", + "\n", + "Computes the band structure of the graphene on O-terminated α-quartz(0001) interface created in the\n", + "[structure notebook](interface_2d_3d_graphene_silicon_dioxide.ipynb), then reads the Dirac point's\n", + "position relative to the Fermi level and the gap at K, and compares both with Kang et al. (2008)\n", + "Sec. III and Fig. 3(a).\n", + "\n", + "

Usage

\n", + "\n", + "1. Create the interface in the [structure notebook](interface_2d_3d_graphene_silicon_dioxide.ipynb), which saves it to the `uploads` folder under the name used in cell 1.2 below.\n", + "1. Set the material name and parameters in cells 1.2-1.4, or use the defaults; `RELAX = True` relaxes the interface (fixed cell) in the same job, before the band structure.\n", + "1. Click \"Run\" > \"Run All\" to run all cells.\n", + "1. Wait for the job to complete.\n", + "1. Scroll down to view the results; the last section prints the comparison with the manuscript.\n", + "\n", + "## Summary\n", + "\n", + "1. Set up the environment and parameters: install packages (JupyterLite only) and configure the material name, model and compute parameters.\n", + "1. Authenticate and initialize API client: authenticate via browser, initialize the client, then select account and project.\n", + "1. Load the interface by name from the `uploads` folder, print its provenance.\n", + "1. Configure the DFT model and k-grid.\n", + "1. Configure compute: get the list of clusters and create a compute configuration.\n", + "1. Configure the Band Structure workflow, with a relaxation in front of it if `RELAX`, and run the job, re-using a job that already ran under the same workflow name.\n", + "1. Retrieve the band structure, the Fermi level and the Dirac point's position and gap at K.\n", + "1. Compare with Kang et al. (2008).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 1. Set up the environment and parameters\n", + "### 1.1. Install packages (JupyterLite)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.packages import install_packages\n", + "\n", + "await install_packages(\"made|specific_examples|api_examples\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1.2. Material name" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Name saved by interface_2d_3d_graphene_silicon_dioxide.ipynb.\n", + "INTERFACE_NAME = \"C(001)-O2Si(001), Interface, Strain 1.875pct\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1.3. Parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from datetime import datetime\n", + "from mat3ra.ide.compute import QueueName\n", + "\n", + "ORGANIZATION_NAME = None # set to your organization name (full or partial); otherwise, your default one is used\n", + "FOLDER = \"./uploads\"\n", + "\n", + "BAND_STRUCTURE_WORKFLOW_SEARCH_TERM = \"band_structure.json\"\n", + "MY_WORKFLOW_NAME = \"Band Structure\"\n", + "APPLICATION_NAME = \"espresso\"\n", + "\n", + "# NOTE: False reads the band structure of the structure as built: E_D - E_F +1.171 eV, gap at K\n", + "# 0.062 eV, about 1 h on OR/16. True relaxes all atoms at fixed cell to 0.03 eV/Å (Kang et al.\n", + "# Sec. II) before the band structure, in the same job; on OR/16 it did 5 BFGS steps in the 4 h\n", + "# TIME_LIMIT with the force still falling (job 25yp4K2SMNJgJMmBy).\n", + "RELAX = False\n", + "\n", + "CLUSTER_NAME = \"001\" # specify full or partial name i.e. \"cluster-001\" to select\n", + "QUEUE_NAME = QueueName.OR\n", + "PPN = 16 # queue OR on cluster-001 allows at most 16 cores per node\n", + "TIME_LIMIT = \"04:00:00\"\n", + "\n", + "timestamp = datetime.now().strftime(\"%Y-%m-%d %H:%M\")\n", + "POLL_INTERVAL = 60 # seconds" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 1.4. DFT model parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "MODEL_SUBTYPE = \"lda\"\n", + "FUNCTIONAL = \"pz\" # Kang et al. 2008 use LDA\n", + "PSEUDOPOTENTIAL_TYPE = \"us\" # GBRV ultrasoft, the only LDA family the platform publishes for Si, O and C\n", + "GBRV_VALENCE = {\"Si\": 4, \"O\": 6, \"C\": 4} # valence electrons per atom of the GBRV pseudopotentials\n", + "ECUTWFC = 40 # Ry, GBRV's recommended wavefunction cutoff\n", + "ECUTRHO = 200 # Ry, GBRV's recommended charge-density cutoff\n", + "\n", + "KPOINT_DENSITY = 4 # gives 6 x 6 x 1 on the 1x1 quartz cell, Kang et al. Sec. II\n", + "SMEARING_SETTINGS = {\"degauss\": 0.01} # Ry; the doped graphene has no gap at E_F\n", + "KPATH_STEPS = 20\n", + "MODEL_TAG = (f\"{FUNCTIONAL}-{PSEUDOPOTENTIAL_TYPE} {ECUTWFC}-{ECUTRHO}Ry k{KPOINT_DENSITY} \"\n", + " f\"p{KPATH_STEPS} g{SMEARING_SETTINGS['degauss']}\")\n", + "\n", + "SCF_UNIT = \"pw_scf\"\n", + "BANDS_UNIT = \"pw_bands\"\n", + "RELAX_UNIT = \"pw_vc-relax\"\n", + "# Fixed cell, 0.03 eV/Å, Kang et al. 2008 Sec. II\n", + "RELAXATION_SETTINGS = {\"calculation\": \"relax\", \"forc_conv_thr\": 1.17e-3, \"nstep\": 100}\n", + "WORKFLOW_TAG = MODEL_TAG + (f\" relax f{RELAXATION_SETTINGS['forc_conv_thr']}\" if RELAX else \"\")\n", + "\n", + "KPATH = [\n", + " {\"point\": \"K\", \"steps\": KPATH_STEPS},\n", + " {\"point\": \"Γ\", \"steps\": KPATH_STEPS},\n", + " {\"point\": \"M\", \"steps\": KPATH_STEPS},\n", + " {\"point\": \"K\", \"steps\": 1},\n", + "]\n", + "K_INDEX = 0 # KPATH starts at K, so the first point of the band structure's path is K" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 2. Authenticate and initialize API client\n", + "### 2.1. Authenticate" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.auth import authenticate\n", + "\n", + "await authenticate()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2.2. Initialize API client" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.api_client import APIClient\n", + "\n", + "client = APIClient.authenticate()\n", + "client" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2.3. Select account" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "client.list_accounts()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "selected_account = client.my_account\n", + "\n", + "if ORGANIZATION_NAME:\n", + " selected_account = client.get_account(name=ORGANIZATION_NAME)\n", + "\n", + "ACCOUNT_ID = selected_account.id\n", + "print(f\"✅ Selected account ID: {ACCOUNT_ID}, name: {selected_account.name}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 2.4. Select project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "projects = client.projects.list({\"isDefault\": True, \"owner._id\": ACCOUNT_ID})\n", + "project_id = projects[0][\"_id\"]\n", + "print(f\"✅ Using project: {projects[0]['name']} ({project_id})\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 3. Load the material\n", + "### 3.1. Load from the uploads folder and print provenance\n", + "\n", + "The structure, its registry and its 120° hexagonal cell come from the structure notebook; this one\n", + "only loads it by name." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "from collections import Counter\n", + "from mat3ra.notebooks_utils.core.entity.material.api import load_material\n", + "\n", + "interface = load_material(client, FOLDER, INTERFACE_NAME, ACCOUNT_ID)\n", + "interface.basis.set_labels_from_list([])\n", + "\n", + "composition = Counter(interface.basis.elements.values)\n", + "electron_count = sum(GBRV_VALENCE[element] * count for element, count in composition.items())\n", + "number_of_occupied_bands = electron_count // 2\n", + "\n", + "interface_in_cartesian = interface.clone()\n", + "interface_in_cartesian.to_cartesian()\n", + "z_coordinates = np.array(interface_in_cartesian.basis.coordinates.values)[:, 2]\n", + "is_carbon = np.array(interface.basis.elements.values) == \"C\"\n", + "interlayer_distance = z_coordinates[is_carbon].min() - z_coordinates[~is_carbon].max()\n", + "print(f\"{INTERFACE_NAME}: {dict(composition)}, {interface.basis.number_of_atoms} atoms, \"\n", + " f\"gamma = {interface.lattice.gamma:.3f}°, interlayer distance = {interlayer_distance:.3f} Å, \"\n", + " f\"{electron_count} valence electrons, {number_of_occupied_bands} occupied bands\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 3.2. Preview the material" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.ipython.entity.material.visualize import visualize_materials\n", + "\n", + "visualize_materials([{\"material\": interface, \"title\": INTERFACE_NAME}], viewer=\"wave\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 4. Configure the DFT model and k-grid\n", + "### 4.1. DFT model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.standata.applications import ApplicationStandata\n", + "from mat3ra.standata.model_tree import ModelTreeStandata\n", + "from mat3ra.ade.application import Application\n", + "from mat3ra.mode import ModelFactory\n", + "\n", + "app_config = ApplicationStandata.get_by_name_first_match(APPLICATION_NAME)\n", + "app = Application(**app_config)\n", + "\n", + "model_config = ModelTreeStandata.get_model_by_parameters(type=\"dft\", subtype=MODEL_SUBTYPE, functional=FUNCTIONAL)\n", + "model_config[\"method\"] = {\"type\": \"pseudopotential\", \"subtype\": PSEUDOPOTENTIAL_TYPE}\n", + "model = ModelFactory.create(model_config)\n", + "print(f\"Using application: {app.name}, model: {MODEL_TAG}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 4.2. k-grid" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.workflow import kgrid_from_density\n", + "\n", + "kgrid = kgrid_from_density(interface, KPOINT_DENSITY, periodic_dims=(0, 1))\n", + "print(f\"k-grid {kgrid}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 5. Create the compute configuration\n", + "### 5.1. Select cluster" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "clusters = client.clusters.list()\n", + "print(f\"Available clusters: {[c['hostname'] for c in clusters]}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 5.2. Create the compute configuration for the jobs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.ide.compute import Compute\n", + "\n", + "if CLUSTER_NAME:\n", + " cluster = next((c for c in clusters if CLUSTER_NAME in c[\"hostname\"]), None)\n", + " if cluster is None:\n", + " raise ValueError(f\"Cluster '{CLUSTER_NAME}' not found. Available: {[c['hostname'] for c in clusters]}\")\n", + "else:\n", + " cluster = clusters[0]\n", + "compute = Compute(cluster=cluster, queue=QUEUE_NAME, ppn=PPN, timeLimit=TIME_LIMIT)\n", + "print(f\"Using cluster: {compute.cluster.hostname}, queue: {QUEUE_NAME}, ppn: {PPN}, \"\n", + " f\"time limit: {TIME_LIMIT}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 6. Band structure job\n", + "### 6.1. Configure the workflow\n", + "\n", + "If `RELAX`, `add_relaxation()` puts a relaxation in front of the band structure in the same job; the platform runs the band structure on the relaxed structure. The relaxation is set to a fixed cell, with the same k-mesh as the SCF." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.standata.workflows import WorkflowStandata\n", + "from mat3ra.wode.workflows import Workflow\n", + "from mat3ra.wode.context.providers import PointsPathDataProvider\n", + "from mat3ra.notebooks_utils.workflow import apply_planewave_cutoffs, apply_scf_kgrid, patch_workflow_qe_input\n", + "from mat3ra.notebooks_utils.ipython.entity.workflow.visualize import visualize_workflow\n", + "\n", + "workflow = Workflow.create(\n", + " WorkflowStandata.filter_by_application(app.name).get_by_name_first_match(BAND_STRUCTURE_WORKFLOW_SEARCH_TERM)\n", + ")\n", + "workflow.name = f\"{MY_WORKFLOW_NAME} {INTERFACE_NAME} {WORKFLOW_TAG}\"\n", + "if RELAX:\n", + " workflow.add_relaxation()\n", + "for subworkflow in workflow.subworkflows:\n", + " subworkflow.model = model\n", + "\n", + "for unit_name in [RELAX_UNIT, SCF_UNIT, BANDS_UNIT]:\n", + " apply_planewave_cutoffs(workflow, ECUTWFC, ECUTRHO, unit_name=unit_name)\n", + " patch_workflow_qe_input(workflow, {\"system\": SMEARING_SETTINGS}, [unit_name])\n", + "for unit_name in [RELAX_UNIT, SCF_UNIT]:\n", + " apply_scf_kgrid(workflow, kgrid, material=interface, unit_name=unit_name)\n", + "patch_workflow_qe_input(workflow, {\"control\": RELAXATION_SETTINGS}, [RELAX_UNIT])\n", + "bands_subworkflow = workflow.subworkflows[-1]\n", + "bands_unit = bands_subworkflow.get_unit_by_name(name=BANDS_UNIT)\n", + "bands_unit.add_context(PointsPathDataProvider(path=KPATH, isEdited=True).get_context_item_data())\n", + "bands_subworkflow.set_unit(bands_unit)\n", + "\n", + "visualize_workflow(workflow)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 6.2. Save the material and run the job\n", + "\n", + "A job is found by its material and its workflow name, which carries the regime, and re-used if it exists; otherwise it is created and submitted." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.core.entity.job.api import find_job_for_material\n", + "from mat3ra.notebooks_utils.core.entity.material.api import get_or_create_material\n", + "from mat3ra.notebooks_utils.core.entity.property.api import get_properties_for_job\n", + "from mat3ra.notebooks_utils.job import create_job\n", + "from mat3ra.notebooks_utils.api.job import submit_jobs, wait_for_jobs_to_finish_async\n", + "\n", + "saved_interface = get_or_create_material(client, interface, ACCOUNT_ID)\n", + "\n", + "job = find_job_for_material(\n", + " client, saved_interface[\"_id\"], workflow.name, ACCOUNT_ID,\n", + " statuses=(\"submitted\", \"queued\", \"active\", \"finished\"),\n", + ")\n", + "if job is None:\n", + " job = create_job(\n", + " api_client=client, materials=[saved_interface], workflow=workflow, project_id=project_id,\n", + " owner_id=ACCOUNT_ID, compute=compute.to_dict(), prefix=f\"{workflow.name} {timestamp}\",\n", + " )\n", + " submit_jobs(client.jobs, [job[\"_id\"]])\n", + " print(f\"✅ Submitted job {job['_id']}\")\n", + "else:\n", + " print(f\"♻️ Reusing job {job['_id']}\")\n", + "await wait_for_jobs_to_finish_async(client.jobs, [job[\"_id\"]], poll_interval=POLL_INTERVAL)\n", + "job_id = job[\"_id\"]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 7. Retrieve the results\n", + "### 7.1. Band structure" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.ipython.entity.property.visualize import visualize_properties\n", + "\n", + "band_structure = get_properties_for_job(client, job_id, property_name=\"band_structure\")\n", + "visualize_properties(band_structure, title=\"Band Structure\",\n", + " extra_config={\"material\": interface.to_dict()})\n", + "if RELAX:\n", + " total_force = get_properties_for_job(client, job_id, \"total_force\")[0]\n", + " print(f\"Residual force after relaxation: {total_force['value']:.4f} {total_force['units']}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### 7.2. Dirac point and gap at K\n", + "\n", + "With `number_of_occupied_bands` doubly occupied bands, the two Dirac bands at K are bands\n", + "`number_of_occupied_bands - 1` and `number_of_occupied_bands` (0-based) for neutral graphene. If a\n", + "surface O state sits below the Dirac point, as in p-doped graphene, they are `number_of_occupied_bands`\n", + "and `number_of_occupied_bands + 1`. The pair with the smaller splitting at K is the Dirac pair; the\n", + "Dirac point is its midpoint and the gap its splitting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "bands = np.array(band_structure[0][\"yDataSeries\"])\n", + "fermi_energy = band_structure[0][\"fermiEnergy\"]\n", + "energies_at_k = bands[:, K_INDEX]\n", + "first, last = number_of_occupied_bands - 2, number_of_occupied_bands + 2\n", + "print(f\"Bands at K relative to E_F (eV), 0-based indices {first}..{last}:\")\n", + "for index in range(first, last + 1):\n", + " print(f\" {index}: {energies_at_k[index] - fermi_energy:+.4f}\")\n", + "\n", + "candidates = [(number_of_occupied_bands - 1, number_of_occupied_bands),\n", + " (number_of_occupied_bands, number_of_occupied_bands + 1)]\n", + "below, above = min(candidates, key=lambda pair: energies_at_k[pair[1]] - energies_at_k[pair[0]])\n", + "gap = energies_at_k[above] - energies_at_k[below]\n", + "dirac_minus_fermi = (energies_at_k[above] + energies_at_k[below]) / 2 - fermi_energy\n", + "print(f\"Dirac pair: bands {below} and {above}\")\n", + "print(f\"E_F = {fermi_energy:.4f} eV, E_D - E_F = {dirac_minus_fermi:+.4f} eV, gap at K = {1000 * gap:.1f} meV\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## 8. Compare with Kang et al. (2008)\n", + "\n", + "Settings that differ from the manuscript: GBRV ultrasoft LDA pseudopotentials against VASP at 396 eV;\n", + "the standata quartz cell is +2.3 % on a against the manuscript's 4.91 Å; the substrate is 5\n", + "conventional quartz cells (15 Si planes) where the manuscript has 14 bilayers; the back surface is the bare cut, not\n", + "H-passivated; the default run is unrelaxed." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "KANG_DIRAC_MINUS_FERMI = 1.28 # eV, midpoint of the two Dirac bands at K on Fig. 3(a): +1.21 and +1.35 eV\n", + "KANG_GAP = 0.13 # eV, Sec. III text\n", + "\n", + "print(f\"Regime: {'relaxed interface' if RELAX else 'unrelaxed SCF'}\")\n", + "print(f\"{'':<14}{'this notebook':>16}{'Kang et al. (2008)':>22}\")\n", + "print(f\"{'Doping':<14}{'p-type' if dirac_minus_fermi > 0 else 'n-type':>16}{'p-type':>22}\")\n", + "print(f\"{'E_D - E_F':<14}{dirac_minus_fermi:>13.3f} eV{KANG_DIRAC_MINUS_FERMI:>19.3f} eV\")\n", + "print(f\"{'Gap at K':<14}{gap:>13.3f} eV{KANG_GAP:>19.3f} eV\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "[1] Y.-J. Kang, J. Kang and K. J. Chang, \"Electronic structure of graphene and doping effect on SiO2\", Phys. Rev. B 78, 115404 (2008). https://doi.org/10.1103/PhysRevB.78.115404\n", + "\n", + "[2] K. F. Garrity, J. W. Bennett, K. M. Rabe and D. Vanderbilt, \"Pseudopotentials for high-throughput DFT calculations\", Comput. Mater. Sci. 81, 446 (2014). https://doi.org/10.1016/j.commatsci.2013.08.053" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.2" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}