diff --git a/other/materials_designer/specific_examples/Introduction.ipynb b/other/materials_designer/specific_examples/Introduction.ipynb index 2bd1bfc59..7082fa0a7 100644 --- a/other/materials_designer/specific_examples/Introduction.ipynb +++ b/other/materials_designer/specific_examples/Introduction.ipynb @@ -31,6 +31,7 @@ "| `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", "| `C-2D-INT-C` | Interface Commensurate Lattice | [Twisted Commensurate MoS₂ Bilayer](interface_bilayer_twisted_commensurate_lattices_molybdenum_disulfide.ipynb) | [Twisted MoS₂ Bilayer Band Structure](interface_bilayer_twisted_commensurate_lattices_molybdenum_disulfide_SIMULATION.ipynb) | [[8]](#ref8) |\n", + "| `C-2D-INT-C` | Interface Commensurate Lattice | [Twisted Commensurate h-BN Bilayer](interface_bilayer_twisted_commensurate_lattices_boron_nitride.ipynb) | [Twisted h-BN Bilayer Flat Bands](interface_bilayer_twisted_commensurate_lattices_boron_nitride_SIMULATION.ipynb) | [[9]](#ref9) |\n", "| `C-2D-MLT` | Multi-Layer | *To be added* | — | — |\n", "| `C-0D-INT-R` | Nanoribbons Interface Rotation | [Twisted BN Nanoribbon Bilayer](interface_bilayer_twisted_nanoribbons_boron_nitride.ipynb) | *To be added* | [[9]](#ref9) |\n", "| `D-3D-AMO` | Amorphous | *To be added* | — | — |\n", diff --git a/other/materials_designer/specific_examples/interface_bilayer_twisted_commensurate_lattices_boron_nitride.ipynb b/other/materials_designer/specific_examples/interface_bilayer_twisted_commensurate_lattices_boron_nitride.ipynb new file mode 100644 index 000000000..1b9547df3 --- /dev/null +++ b/other/materials_designer/specific_examples/interface_bilayer_twisted_commensurate_lattices_boron_nitride.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Twisted bilayer Boron Nitride at 7.34° on a commensurate lattice.\n", + "\n", + "## 0. Introduction.\n", + "\n", + "This notebook demonstrates how to generate a twisted interface between two materials using commensurate lattices. The example uses hexagonal boron nitride (h-BN) as both the film and substrate materials and builds the periodic moiré cells of configurations α and β that the paper computes, at 7.34° (244 atoms) by default. The notebook uses the `create_interface_commensurate` function which first creates a slab from the material and then performs commensurate lattice matching to find valid supercells for the target twist angle. The algorithm searches for the supercell matrices that give the target twist angle. The generated interfaces are visualized and analyzed to determine the number of atoms and the interlayer distance.\n", + "\n", + "> **Lede Xian, Dante M. Kennes, Nicolas Tancogne-Dejean, Massimo Altarelli, and Angel Rubio**\n", + "> Multiflat Bands and Strong Correlations in Twisted Bilayer Boron Nitride: Doping-Induced Correlated Insulator and Superconductor. Nano Letters, 19, 4934. 2019.\n", + "> [https://doi.org/10.1021/acs.nanolett.9b00986](https://doi.org/10.1021/acs.nanolett.9b00986)\n", + "\n", + "The twisted h-BN bilayer is shown in the following Figure 1 from the article: configurations α (a) and β (b), and the width of the top valence flat bands against twist angle (g).\n", + "\n", + "\"Twisted" + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## 1. Prepare the Environment\n", + "### 1.1. Configurations from the article\n", + "\n", + "The twist angles and the interlayer distance below are the ones of the article's band-width series: flat layers,\n", + "3.23 Å apart, the optimized interlayer distance of the untwisted bilayer. The article's two configurations, α and β,\n", + "share one moiré cell and differ in twist by 60°: in α the B-over-B and N-over-N regions sit at different sites of the\n", + "cell, in β at the same site.\n", + "\n", + "The names below are how the [band structure notebook](interface_bilayer_twisted_commensurate_lattices_boron_nitride_SIMULATION.ipynb)\n", + "finds the materials created here, so changing one means changing it there too." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "# Uncomment a line to build that configuration as well: α is twisted by 60° + θ, β by θ. The angles are those of\n", + "# Xian et al. 2019 Fig. 1g; smaller angles have larger cells (6.01°: 364 atoms, 5.08°: 508, 3.89°: 868, 2.64°: 1876).\n", + "INTERFACE_PARAMETERS = [\n", + " {\"name\": \"h-BN bilayer 7.34deg alpha d3.23\", \"angle\": 60 + 7.34},\n", + " {\"name\": \"h-BN bilayer 7.34deg beta d3.23\", \"angle\": 7.34},\n", + " # {\"name\": \"h-BN bilayer 6.01deg alpha d3.23\", \"angle\": 60 + 6.01},\n", + " # {\"name\": \"h-BN bilayer 6.01deg beta d3.23\", \"angle\": 6.01},\n", + " # {\"name\": \"h-BN bilayer 5.08deg alpha d3.23\", \"angle\": 60 + 5.08},\n", + " # {\"name\": \"h-BN bilayer 5.08deg beta d3.23\", \"angle\": 5.08},\n", + " # {\"name\": \"h-BN bilayer 3.89deg alpha d3.23\", \"angle\": 60 + 3.89},\n", + " # {\"name\": \"h-BN bilayer 3.89deg beta d3.23\", \"angle\": 3.89},\n", + " # {\"name\": \"h-BN bilayer 2.64deg alpha d3.23\", \"angle\": 60 + 2.64},\n", + " # {\"name\": \"h-BN bilayer 2.64deg beta d3.23\", \"angle\": 2.64},\n", + "]\n", + "INTERFACE_DISTANCE = 3.23 # Å, the article's\n", + "MONOLAYER_NAME = \"h-BN monolayer\" # the Standata monolayer, saved as is (Fig. 1d)" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "### 1.2. Set interface creation and visualization parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "VACUUM = 20.0 # Å of empty space above the top layer; paper: not stated\n", + "\n", + "# Visualization parameters\n", + "SHOW_INTERMEDIATE_STEPS = True\n", + "VISUALIZE_REPETITIONS = [3, 3, 1]" + ] + }, + { + "cell_type": "markdown", + "id": "5", + "metadata": {}, + "source": [ + "### 1.3. Install packages\n", + "The step executes only in Pyodide environment. For other environments, the packages should be installed via `pip install` (see [README](../../README.ipynb))." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.packages import install_packages\n", + "\n", + "await install_packages(\"made|specific_examples\")" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "### 1.4. Get input material\n", + "We'll use the h-BN monolayer from Standata.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.material import Material\n", + "from mat3ra.standata.materials import Materials\n", + "from mat3ra.notebooks_utils.ipython.entity.material.visualize import visualize_materials\n", + "\n", + "# paper: a = 2.512 Å; ours Standata's h-BN monolayer as is\n", + "material = Material.create(Materials.get_by_name_and_categories(\"BN\", \"2D\"))\n", + "print(f\"{material.name}: {material.basis.number_of_atoms} atoms, a = {material.lattice.a:.3f} Å\")\n", + "\n", + "if SHOW_INTERMEDIATE_STEPS:\n", + " visualize_materials(material, repetitions=VISUALIZE_REPETITIONS)\n", + " visualize_materials(material, repetitions=VISUALIZE_REPETITIONS, rotation=\"-90x\")" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "## 2. Generate the Twisted Interfaces" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.tools.analyze.other import get_average_interlayer_distance\n", + "from mat3ra.made.tools.convert import to_ase\n", + "from mat3ra.made.tools.convert.interface_parts_enum import InterfacePartsEnum\n", + "from mat3ra.made.tools.helpers import create_interface_commensurate\n", + "\n", + "interfaces = []\n", + "for parameters in INTERFACE_PARAMETERS:\n", + " interface = create_interface_commensurate(\n", + " material=material,\n", + " target_angle=parameters[\"angle\"],\n", + " gap=INTERFACE_DISTANCE,\n", + " # the builder adds the gap above the top layer as well\n", + " vacuum=VACUUM - INTERFACE_DISTANCE,\n", + " )\n", + " # Naming the hexagonal lattice is what lets the symbolic K point of the band path resolve.\n", + " interface.lattice.type = \"HEX\"\n", + " interface.name = parameters[\"name\"]\n", + " interfaces.append(interface)\n", + "\n", + " interlayer_distance = get_average_interlayer_distance(\n", + " interface, InterfacePartsEnum.SUBSTRATE.value, InterfacePartsEnum.FILM.value)\n", + " print(f\"{interface.name}: {interface.basis.number_of_atoms} atoms ({to_ase(interface).get_chemical_formula()}), \"\n", + " f\"a = {interface.lattice.a:.3f} Å, gamma = {interface.lattice.gamma:.1f}°, \"\n", + " f\"interlayer distance {interlayer_distance:.3f} Å, target twist {parameters['angle']:.2f}°\")" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "## 3. Preview the materials" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "titled_interfaces = [{\"material\": interface, \"title\": interface.name} for interface in interfaces]\n", + "visualize_materials(titled_interfaces, repetitions=VISUALIZE_REPETITIONS)\n", + "visualize_materials(titled_interfaces, repetitions=VISUALIZE_REPETITIONS, rotation=\"-90x\")" + ] + }, + { + "cell_type": "markdown", + "id": "13", + "metadata": {}, + "source": [ + "## 4. Save the materials\n", + "\n", + "Each material is written to the `uploads` folder under its name, which is how the\n", + "[band structure notebook](interface_bilayer_twisted_commensurate_lattices_boron_nitride_SIMULATION.ipynb)\n", + "picks it up." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.io import download_content_to_file\n", + "from mat3ra.notebooks_utils.material import set_materials\n", + "\n", + "material.name = MONOLAYER_NAME\n", + "materials_to_save = [*interfaces, material]\n", + "set_materials(materials_to_save)\n", + "for material_to_save in materials_to_save:\n", + " download_content_to_file(material_to_save.to_json(), f\"{material_to_save.name}.json\")" + ] + }, + { + "cell_type": "markdown", + "id": "15", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "[1] L. Xian, D. M. Kennes, N. Tancogne-Dejean, M. Altarelli and A. Rubio, \"Multiflat Bands and Strong Correlations in Twisted Bilayer Boron Nitride: Doping-Induced Correlated Insulator and Superconductor\", Nano Lett. 19, 4934 (2019). https://doi.org/10.1021/acs.nanolett.9b00986" + ] + } + ], + "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": 5 +} diff --git a/other/materials_designer/specific_examples/interface_bilayer_twisted_commensurate_lattices_boron_nitride_SIMULATION.ipynb b/other/materials_designer/specific_examples/interface_bilayer_twisted_commensurate_lattices_boron_nitride_SIMULATION.ipynb new file mode 100644 index 000000000..f492a9a22 --- /dev/null +++ b/other/materials_designer/specific_examples/interface_bilayer_twisted_commensurate_lattices_boron_nitride_SIMULATION.ipynb @@ -0,0 +1,712 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Flat Valence Bands of Twisted Bilayer h-BN\n", + "\n", + "> **Lede Xian, Dante M. Kennes, Nicolas Tancogne-Dejean, Massimo Altarelli, and Angel Rubio**\n", + "> **Multiflat Bands and Strong Correlations in Twisted Bilayer Boron Nitride: Doping-Induced Correlated Insulator and Superconductor**\n", + "> Nano Letters 19, 4934 (2019)\n", + "> [DOI: 10.1021/acs.nanolett.9b00986](https://doi.org/10.1021/acs.nanolett.9b00986)\n", + "\n", + "Calculate the band structures of the h-BN monolayer and the twisted h-BN bilayers created in the\n", + "[structure notebook](interface_bilayer_twisted_commensurate_lattices_boron_nitride.ipynb), using Quantum ESPRESSO and the\n", + "band structure workflow from Standata, read off the band gap of the monolayer (Fig. 1d) and the width of the top set\n", + "of flat valence bands of the bilayers.\n", + "\n", + "The article's result is that twisting bilayer h-BN splits its top valence bands into isolated flat sets whose width\n", + "shrinks with the twist angle, from 101.4 meV at 7.34° to 1.6 meV at 2.64° (Fig. 1g).\n", + "\n", + "

Usage

\n", + "\n", + "1. Create the materials in the [structure notebook](interface_bilayer_twisted_commensurate_lattices_boron_nitride.ipynb), which saves them to the `uploads` folder under the names used in cell 1.2 below.\n", + "1. Set the materials and the calculation parameters in cells 1.2 and 1.3 (or use the default values).\n", + "1. Click \"Run\" > \"Run All\" to run all cells.\n", + "1. Wait for the jobs to complete.\n", + "1. Scroll down to view the results; section 8 prints the comparison with the paper.\n", + "\n", + "## Summary\n", + "\n", + "1. Set up the environment and parameters: install packages (JupyterLite only) and configure the materials, workflow, model, compute resources and jobs.\n", + "1. Authenticate and initialize API client: authenticate via browser, initialize the client, then select account and project.\n", + "1. Load the materials by name from the `uploads` folder, print their provenance and save them to the platform.\n", + "1. Configure the workflow: select the application and the model, load the band structure workflow from Standata, and set the k-grid of the self-consistent run, the k-path of the bands and the cutoffs.\n", + "1. Configure compute: get the list of clusters and create a compute configuration.\n", + "1. Run one job per material, one after another, re-using a job that already ran under the same workflow name.\n", + "1. Retrieve results: the band structures, the width of the top valence flat bands and their separation from the bands below, the bands near the valence-band top, and the band gap of the monolayer.\n", + "1. Compare with Xian et al. (2019)." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## 1. Set up the environment and parameters\n", + "### 1.1. Install packages (JupyterLite)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.packages import install_packages\n", + "\n", + "await install_packages(\"made|api_examples\")" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "### 1.2. Set parameters and configurations for the workflow and jobs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "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", + "# paper: Γ–K–M, number of points not stated; ours evenly spaced (|ΓK| = 2|KM|), 7 points for the moiré cells\n", + "KPATH = [\n", + " {\"point\": \"Γ\", \"steps\": 4},\n", + " {\"point\": \"K\", \"steps\": 2},\n", + " {\"point\": \"M\", \"steps\": 1},\n", + "]\n", + "# paper Fig. 1d: Γ–K–M, number of points not stated; ours 61, evenly spaced\n", + "KPATH_MONOLAYER = [\n", + " {\"point\": \"Γ\", \"steps\": 40},\n", + " {\"point\": \"K\", \"steps\": 20},\n", + " {\"point\": \"M\", \"steps\": 1},\n", + "]\n", + "# Names as saved by the structure notebook, each with the k-grid of its self-consistent run, the path of its bands and\n", + "# the paper's value: the monolayer's LDA band gap (eV, SI p. 10) or the width of the bilayer's top valence flat set\n", + "# (meV, configuration not stated; 7.34° and 2.64°: text p. 4936; 6.01, 5.08 and 3.89°: read off Fig. 1g). The twisted\n", + "# cells use the paper's Γ-only self-consistent run. One job per material; every angle below 7.34° exceeds one 4 h\n", + "# single-node job.\n", + "MATERIALS = {\n", + " # paper: k-grid not stated; ours a multiple of 3, which keeps K on the mesh\n", + " \"h-BN monolayer\": {\"kgrid\": [12, 12, 1], \"kpath\": KPATH_MONOLAYER, \"paper_band_gap\": 4.7},\n", + " \"h-BN bilayer 7.34deg alpha d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 101.4},\n", + " # \"h-BN bilayer 7.34deg beta d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 101.4},\n", + " # \"h-BN bilayer 6.01deg alpha d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 54.9},\n", + " # \"h-BN bilayer 6.01deg beta d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 54.9},\n", + " # \"h-BN bilayer 5.08deg alpha d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 31.6},\n", + " # \"h-BN bilayer 5.08deg beta d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 31.6},\n", + " # \"h-BN bilayer 3.89deg alpha d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 11.7},\n", + " # \"h-BN bilayer 3.89deg beta d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 11.7},\n", + " # \"h-BN bilayer 2.64deg alpha d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 1.6},\n", + " # \"h-BN bilayer 2.64deg beta d3.23\": {\"kgrid\": [1, 1, 1], \"kpath\": KPATH, \"paper_flat_band_width\": 1.6},\n", + "}\n", + "\n", + "WORKFLOW_SEARCH_TERM = \"band_structure.json\"\n", + "MY_WORKFLOW_NAME = \"Band Structure\"\n", + "APPLICATION_NAME = \"espresso\"\n", + "\n", + "CLUSTER_NAME = None # specify full or partial name i.e. \"cluster-001\" to select\n", + "QUEUE_NAME = QueueName.OR\n", + "PPN = 16 # OR allows 16 cores per job\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", + "id": "5", + "metadata": {}, + "source": [ + "### 1.3. Set the DFT parameters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6", + "metadata": {}, + "outputs": [], + "source": [ + "MODEL_SUBTYPE = \"lda\"\n", + "FUNCTIONAL = \"pz\"\n", + "PSEUDOPOTENTIAL_TYPE = \"us\" # GBRV\n", + "ECUTWFC = 40 # Ry\n", + "ECUTRHO = 200 # Ry; paper: VASP PAW, 400 eV\n", + "\n", + "MODEL_TAG = f\"{FUNCTIONAL}-{PSEUDOPOTENTIAL_TYPE} {ECUTWFC}-{ECUTRHO}Ry\"\n", + "\n", + "SCF_UNIT = \"pw_scf\"\n", + "BANDS_UNIT = \"pw_bands\"\n", + "\n", + "PLOT_ENERGY_RANGE = (-0.5, 0.1) # eV, relative to the top of the flat bands" + ] + }, + { + "cell_type": "markdown", + "id": "7", + "metadata": {}, + "source": [ + "## 2. Authenticate and initialize API client\n", + "### 2.1. Authenticate\n", + "Authenticate in the browser and have credentials stored in environment variable \"OIDC_ACCESS_TOKEN\"." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.auth import authenticate\n", + "\n", + "await authenticate()" + ] + }, + { + "cell_type": "markdown", + "id": "9", + "metadata": {}, + "source": [ + "### 2.2. Initialize API client" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "10", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.api_client import APIClient\n", + "\n", + "client = APIClient.authenticate()\n", + "client" + ] + }, + { + "cell_type": "markdown", + "id": "11", + "metadata": {}, + "source": [ + "### 2.3. Select account" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12", + "metadata": {}, + "outputs": [], + "source": [ + "client.list_accounts()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "13", + "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", + "id": "14", + "metadata": {}, + "source": [ + "### 2.4. Select project" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "15", + "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", + "id": "16", + "metadata": {}, + "source": [ + "## 3. Load the materials\n", + "### 3.1. Load from the uploads folder and print provenance\n", + "\n", + "The structures, their geometry and their lattice type all come from the structure notebook; this one only loads them\n", + "by name." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.core.entity.material.api import load_material\n", + "\n", + "materials = {}\n", + "for name in MATERIALS:\n", + " material = load_material(client, FOLDER, name, ACCOUNT_ID)\n", + " materials[name] = material\n", + " print(f\"{name}: {material.basis.number_of_atoms} atoms, a = {material.lattice.a:.3f} Å, \"\n", + " f\"gamma = {material.lattice.gamma:.3f}°, lattice {material.lattice.type}\")" + ] + }, + { + "cell_type": "markdown", + "id": "18", + "metadata": {}, + "source": [ + "### 3.2. Preview the materials" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "19", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.ipython.entity.material.visualize import visualize_materials\n", + "\n", + "visualize_materials([{\"material\": material, \"title\": name} for name, material in materials.items()],\n", + " viewer=\"wave\")" + ] + }, + { + "cell_type": "markdown", + "id": "20", + "metadata": {}, + "source": [ + "### 3.3. Save the materials to the platform" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "21", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.made.material import Material\n", + "from mat3ra.notebooks_utils.core.entity.material.api import get_or_create_material\n", + "\n", + "saved_materials = {}\n", + "for name, material in materials.items():\n", + " material.basis.set_labels_from_list([])\n", + " saved_materials[name] = Material.create(get_or_create_material(client, material, ACCOUNT_ID))" + ] + }, + { + "cell_type": "markdown", + "id": "22", + "metadata": {}, + "source": [ + "## 4. Configure the workflow\n", + "### 4.1. Select application and model" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "23", + "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", + "id": "24", + "metadata": {}, + "source": [ + "### 4.2. Configure the workflow\n", + "\n", + "One workflow per material: the self-consistent run on the k-grid set alongside its name in 1.2, then the bands along\n", + "the path set beside it, both with the cutoffs of 1.3. Each workflow is named after its material, `MODEL_TAG`, its\n", + "k-grid and the number of points on its path; section 6 finds a job that already ran by that name.\n", + "\n", + "The structure is not relaxed, as in the article's band-width series." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "25", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.standata.workflows import WorkflowStandata\n", + "from mat3ra.wode.workflows import Workflow\n", + "from mat3ra.wode.context.providers import PlanewaveCutoffsContextProvider, PointsGridDataProvider, \\\n", + " PointsPathDataProvider\n", + "\n", + "from copy import deepcopy\n", + "\n", + "workflow_config = WorkflowStandata.filter_by_application(app.name).get_by_name_first_match(WORKFLOW_SEARCH_TERM)\n", + "cutoffs_context = PlanewaveCutoffsContextProvider(\n", + " wavefunction=ECUTWFC, density=ECUTRHO, isEdited=True).get_context_item_data()\n", + "\n", + "workflows = {}\n", + "for name, material in materials.items():\n", + " workflow = Workflow.create(deepcopy(workflow_config))\n", + " kpath = MATERIALS[name][\"kpath\"]\n", + " workflow.name = (f\"{MY_WORKFLOW_NAME} {name} {MODEL_TAG} k{MATERIALS[name]['kgrid'][0]} \"\n", + " f\"p{sum(point['steps'] for point in kpath)}\")\n", + " subworkflow = workflow.subworkflows[0]\n", + " subworkflow.model = model\n", + " grid_context = PointsGridDataProvider(material=material, dimensions=MATERIALS[name][\"kgrid\"],\n", + " isEdited=True).get_context_item_data()\n", + " path_context = PointsPathDataProvider(path=kpath, isEdited=True).get_context_item_data()\n", + "\n", + " for unit_name, contexts in [(SCF_UNIT, [grid_context, cutoffs_context]),\n", + " (BANDS_UNIT, [path_context, cutoffs_context])]:\n", + " unit = subworkflow.get_unit_by_name(name=unit_name)\n", + " for context in contexts:\n", + " unit.add_context(context)\n", + " subworkflow.set_unit(unit)\n", + " workflows[name] = workflow\n", + " print(workflow.name)" + ] + }, + { + "cell_type": "markdown", + "id": "26", + "metadata": {}, + "source": [ + "### 4.3. Preview the workflow" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "27", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.ipython.entity.workflow.visualize import visualize_workflow\n", + "\n", + "visualize_workflow(next(iter(workflows.values())))" + ] + }, + { + "cell_type": "markdown", + "id": "28", + "metadata": {}, + "source": [ + "### 4.4. Save the workflows to the collection" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "29", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.core.entity.workflow.api import get_or_create_workflow\n", + "\n", + "for name, workflow in workflows.items():\n", + " saved = Workflow.create(get_or_create_workflow(client, workflow, ACCOUNT_ID))\n", + " print(f\"{name}: workflow {saved.id}\")" + ] + }, + { + "cell_type": "markdown", + "id": "30", + "metadata": {}, + "source": [ + "## 5. Create the compute configuration\n", + "### 5.1. Get list of clusters" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31", + "metadata": {}, + "outputs": [], + "source": [ + "clusters = client.clusters.list()\n", + "print(f\"Available clusters: {[c['hostname'] for c in clusters]}\")" + ] + }, + { + "cell_type": "markdown", + "id": "32", + "metadata": {}, + "source": [ + "### 5.2. Create the compute configuration for the jobs" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "33", + "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", + "id": "34", + "metadata": {}, + "source": [ + "## 6. Run the jobs one at a time\n", + "\n", + "A job is found by its material and its workflow name, which carries the model tag, and re-used if it exists;\n", + "otherwise it is created and submitted. The jobs run one after another." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.core.entity.job.api import find_job_for_material\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", + "job_ids = {}\n", + "for name, workflow in workflows.items():\n", + " job = find_job_for_material(\n", + " client, saved_materials[name].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_materials[name]], 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\"✅ {name}: submitted job {job['_id']}\")\n", + " else:\n", + " print(f\"♻️ {name}: reusing job {job['_id']}\")\n", + " await wait_for_jobs_to_finish_async(client.jobs, [job[\"_id\"]], poll_interval=POLL_INTERVAL)\n", + " job_ids[name] = job[\"_id\"]" + ] + }, + { + "cell_type": "markdown", + "id": "36", + "metadata": {}, + "source": [ + "## 7. Retrieve the results\n", + "### 7.1. Band structure\n", + "\n", + "The path is Γ–K–M of each cell, as in the article: the monolayer's own Brillouin zone, and for the moiré cells their\n", + "small zone, onto which the bands of the two layers are folded." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37", + "metadata": {}, + "outputs": [], + "source": [ + "from mat3ra.notebooks_utils.core.entity.property.api import get_properties_for_job\n", + "from mat3ra.notebooks_utils.ipython.entity.property.visualize import visualize_properties\n", + "\n", + "band_structures = {}\n", + "for name, job_id in job_ids.items():\n", + " band_structures[name] = get_properties_for_job(client, job_id, property_name=\"band_structure\")[0]\n", + " visualize_properties(band_structures[name], title=f\"Band Structure: {name}\",\n", + " extra_config={\"material\": materials[name].to_dict()})" + ] + }, + { + "cell_type": "markdown", + "id": "38", + "metadata": {}, + "source": [ + "### 7.2. Top valence flat bands of the bilayers\n", + "\n", + "The flat set starts at the highest occupied band and takes in each band below whose maximum along the path reaches\n", + "the set's minimum. Its width is the spread of the set over the points of `KPATH`, and its separation is the distance\n", + "from the set's minimum down to the maximum of the next band. The article does not say whether its width is taken along\n", + "the path or over the whole Brillouin zone." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "39", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "bands_by_material = {name: np.array(band_structure[\"yDataSeries\"]) # band × path point, eV\n", + " for name, band_structure in band_structures.items()}\n", + "# GBRV valence electrons: B 3, N 5\n", + "number_of_occupied_bands = {name: 2 * material.basis.number_of_atoms for name, material in materials.items()}\n", + "\n", + "flat_bands = {}\n", + "for name in [name for name, settings in MATERIALS.items() if \"paper_flat_band_width\" in settings]:\n", + " bands, occupied = bands_by_material[name], number_of_occupied_bands[name]\n", + " flat_set_bottom = occupied - 1\n", + " while bands[flat_set_bottom - 1].max() >= bands[flat_set_bottom:occupied].min():\n", + " flat_set_bottom -= 1\n", + " flat_set = bands[flat_set_bottom:occupied]\n", + " flat_bands[name] = {\n", + " \"bands\": bands,\n", + " \"flat_set\": flat_set,\n", + " \"width\": 1000 * (flat_set.max() - flat_set.min()),\n", + " \"separation\": 1000 * (flat_set.min() - bands[flat_set_bottom - 1].max()),\n", + " }" + ] + }, + { + "cell_type": "markdown", + "id": "40", + "metadata": {}, + "source": [ + "### 7.3. Bands near the valence-band top\n", + "\n", + "Energies relative to the top of the flat set." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "41", + "metadata": {}, + "outputs": [], + "source": [ + "from matplotlib import pyplot as plt\n", + "from mat3ra.notebooks_utils.plot import display_matplotlib_figure\n", + "\n", + "for name, result in flat_bands.items():\n", + " kpath = MATERIALS[name][\"kpath\"]\n", + " figure, axes = plt.subplots()\n", + " axes.plot((result[\"bands\"] - result[\"flat_set\"].max()).T, color=\"black\", marker=\".\")\n", + " axes.set_xticks(np.cumsum([0] + [point[\"steps\"] for point in kpath[:-1]]), [point[\"point\"] for point in kpath])\n", + " axes.set_ylim(*PLOT_ENERGY_RANGE)\n", + " axes.set_ylabel(\"E − E(top of the flat bands) (eV)\")\n", + " axes.set_title(f\"Bands near the valence-band top: {name}\")\n", + " display_matplotlib_figure(figure)" + ] + }, + { + "cell_type": "markdown", + "id": "42", + "metadata": {}, + "source": [ + "## 8. Compare with Xian et al. (2019)\n", + "\n", + "The article's values, set in `MATERIALS`, beside ours: the width of the flat set for the bilayers (the article gives no\n", + "separation), and the band gap over the points of its path for the monolayer.\n", + "\n", + "Settings that differ from the article's: GBRV ultrasoft pseudopotentials at 40/200 Ry against VASP PAW at 400 eV, and\n", + "Standata's lattice constant of 2.509 Å against 2.512 Å." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "43", + "metadata": {}, + "outputs": [], + "source": [ + "for name, result in flat_bands.items():\n", + " print(f\"{name}: top valence flat set ({len(result['flat_set'])} bands), width along Γ–K–M: \"\n", + " f\"{result['width']:.1f} meV · Xian et al. 2019: {MATERIALS[name]['paper_flat_band_width']} meV\")\n", + " print(f\"{name}: separation from the bands below: {result['separation']:.1f} meV\")\n", + "\n", + "for name in [name for name, settings in MATERIALS.items() if \"paper_band_gap\" in settings]:\n", + " bands, occupied = bands_by_material[name], number_of_occupied_bands[name]\n", + " band_gap = bands[occupied].min() - bands[occupied - 1].max()\n", + " print(f\"{name}: band gap along Γ–K–M: {band_gap:.2f} eV · Xian et al. 2019: {MATERIALS[name]['paper_band_gap']} eV\")" + ] + }, + { + "cell_type": "markdown", + "id": "44", + "metadata": {}, + "source": [ + "## References\n", + "\n", + "[1] L. Xian, D. M. Kennes, N. Tancogne-Dejean, M. Altarelli and A. Rubio, \"Multiflat Bands and Strong Correlations in Twisted Bilayer Boron Nitride: Doping-Induced Correlated Insulator and Superconductor\", Nano Lett. 19, 4934 (2019). https://doi.org/10.1021/acs.nanolett.9b00986\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": 5 +} diff --git a/other/materials_designer/specific_examples/interface_bilayer_twisted_nanoribbons_boron_nitride.ipynb b/other/materials_designer/specific_examples/interface_bilayer_twisted_nanoribbons_boron_nitride.ipynb index a34081ed7..a9c46fd11 100644 --- a/other/materials_designer/specific_examples/interface_bilayer_twisted_nanoribbons_boron_nitride.ipynb +++ b/other/materials_designer/specific_examples/interface_bilayer_twisted_nanoribbons_boron_nitride.ipynb @@ -13,7 +13,7 @@ "\n", "> **Lede Xian, Dante M. Kennes, Nicolas Tancogne-Dejean, Massimo Altarelli, and Angel Rubio**,\n", "> \"Multiflat Bands and Strong Correlations in Twisted Bilayer Boron Nitride: Doping-Induced Correlated Insulator and Superconductor\"\n", - "> Phys. Rev. Lett. 125, 086402 – Published 20 August 2020\n", + "> Nano Lett. 19, 4934–4940 (2019)\n", "> [DOI: 10.1021/acs.nanolett.9b00986](https://pubs.acs.org/doi/10.1021/acs.nanolett.9b00986)\n", "\n", "We will reproduce the structure shown in Figure 1(a,b) of the paper, which shows a twisted bilayer h-BN with specific stacking regions.\n", @@ -22,7 +22,7 @@ "1. Hexagonal boron nitride bilayer structure\n", "2. Twisted configurations with 2 specific twist angles\n", "\n", - "The image shows the twisted bilayer h-BN structure with a twist angle of 2.64° (a) and 62.64° (b).\n", + "The image shows the twisted bilayer h-BN structure with a twist angle of 62.64° (a, configuration α) and 2.64° (b, configuration β).\n", "\n", "\"Twisted" ]